File indexing completed on 2024-04-28 07:51:06
0001 # -*- coding: utf-8 -*- 0002 0003 """ 0004 Copyright (C) 2008-2016 Wolfgang Rohdewald <wolfgang@rohdewald.de> 0005 0006 SPDX-License-Identifier: GPL-2.0 0007 0008 """ 0009 0010 import datetime 0011 0012 from qt import Qt, QModelIndex 0013 from qt import QAbstractTableModel, QDialogButtonBox, QDialog 0014 from qt import QHBoxLayout, QVBoxLayout, QCheckBox 0015 from qt import QItemSelectionModel, QAbstractItemView 0016 0017 from dialogs import WarningYesNo 0018 from kde import KIcon 0019 from mi18n import i18n, i18nc 0020 from log import logException 0021 from query import Query 0022 from guiutil import MJTableView, decorateWindow 0023 from statesaver import StateSaver 0024 from common import Debug 0025 from modeltest import ModelTest 0026 0027 0028 class GamesModel(QAbstractTableModel): 0029 0030 """data for the list of games""" 0031 0032 def __init__(self): 0033 QAbstractTableModel.__init__(self) 0034 self._resultRows = [] 0035 0036 def columnCount(self, unusedParent=None): # pylint: disable=no-self-use 0037 """including the hidden col 0""" 0038 return 3 0039 0040 def rowCount(self, parent=None): 0041 """how many games""" 0042 if parent and parent.isValid(): 0043 # we have only top level items 0044 return 0 0045 return len(self._resultRows) 0046 0047 def setResultset(self, rows): 0048 """new data""" 0049 self.beginResetModel() 0050 try: 0051 self._resultRows = rows 0052 finally: 0053 self.endResetModel() 0054 0055 def index(self, row, column, parent=None): 0056 """helper""" 0057 if (row < 0 0058 or column < 0 0059 or row >= self.rowCount(parent) 0060 or column >= self.columnCount(parent)): 0061 # similar in tree.py 0062 return QModelIndex() 0063 return self.createIndex(row, column, 0) 0064 0065 def data(self, index, role=None): 0066 """get score table from view""" 0067 if role is None: 0068 role = Qt.DisplayRole 0069 if not (index.isValid() and role == Qt.DisplayRole): 0070 return None 0071 if role == Qt.DisplayRole: 0072 unformatted = str( 0073 self._resultRows[index.row()][index.column()]) # TODO: brauche ich str? 0074 if index.column() == 2: 0075 # we do not yet use this for listing remote games but if we do 0076 # this translation is needed for robot players 0077 names = [i18n(name) for name in unformatted.split('///')] 0078 return ', '.join(names) 0079 if index.column() == 1: 0080 dateVal = datetime.datetime.strptime( 0081 unformatted, '%Y-%m-%dT%H:%M:%S') 0082 return dateVal.strftime('%c') 0083 if index.column() == 0: 0084 return int(unformatted) 0085 return QAbstractTableModel.data(self, index, role) 0086 0087 def headerData(self, section, orientation, role): 0088 """for the two visible columns""" 0089 # pylint: disable=no-self-use 0090 if orientation == Qt.Horizontal and role == Qt.DisplayRole: 0091 return i18n('Players') if section == 2 else i18n('Started') 0092 return None 0093 0094 0095 class Games(QDialog): 0096 0097 """a dialog for selecting a game""" 0098 0099 def __init__(self, parent=None): 0100 super().__init__(parent) 0101 self.selectedGame = None 0102 self.onlyPending = True 0103 decorateWindow(self, i18nc("@title:window", "Games")) 0104 self.setObjectName('Games') 0105 self.resize(700, 400) 0106 self.model = GamesModel() 0107 if Debug.modelTest: 0108 self.modelTest = ModelTest(self.model, self) 0109 0110 self.view = MJTableView(self) 0111 self.view.setModel(self.model) 0112 self.selection = QItemSelectionModel(self.model, self.view) 0113 self.view.setSelectionModel(self.selection) 0114 self.view.setSelectionBehavior(QAbstractItemView.SelectRows) 0115 self.view.setSelectionMode(QAbstractItemView.SingleSelection) 0116 0117 self.buttonBox = QDialogButtonBox(self) 0118 self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel) 0119 self.newButton = self.buttonBox.addButton( 0120 i18nc('start a new game', "&New"), QDialogButtonBox.ActionRole) 0121 self.newButton.setIcon(KIcon("document-new")) 0122 self.newButton.clicked.connect(self.accept) 0123 self.loadButton = self.buttonBox.addButton( 0124 i18n("&Load"), QDialogButtonBox.AcceptRole) 0125 self.loadButton.clicked.connect(self.loadGame) 0126 self.loadButton.setIcon(KIcon("document-open")) 0127 self.deleteButton = self.buttonBox.addButton( 0128 i18n("&Delete"), QDialogButtonBox.ActionRole) 0129 self.deleteButton.setIcon(KIcon("edit-delete")) 0130 self.deleteButton.clicked.connect(self.delete) 0131 0132 chkPending = QCheckBox(i18n("Show only pending games"), self) 0133 chkPending.setChecked(True) 0134 cmdLayout = QHBoxLayout() 0135 cmdLayout.addWidget(chkPending) 0136 cmdLayout.addWidget(self.buttonBox) 0137 0138 layout = QVBoxLayout() 0139 layout.addWidget(self.view) 0140 layout.addLayout(cmdLayout) 0141 self.setLayout(layout) 0142 StateSaver(self) 0143 0144 self.selection.selectionChanged.connect(self.selectionChanged) 0145 self.buttonBox.accepted.connect(self.accept) 0146 self.buttonBox.rejected.connect(self.reject) 0147 self.view.doubleClicked.connect(self.loadGame) 0148 chkPending.stateChanged.connect(self.pendingOrNot) 0149 0150 def showEvent(self, unusedEvent): 0151 """only now get the data set. Not doing this in__init__ would eventually 0152 make it easier to subclass from some generic TableEditor class""" 0153 self.setQuery() 0154 self.view.initView() 0155 self.selectionChanged() 0156 0157 def keyPressEvent(self, event): 0158 """use insert/delete keys for insert/delete""" 0159 key = event.key() 0160 if key == Qt.Key_Insert: 0161 self.newEntry() 0162 return 0163 if key == Qt.Key_Delete: 0164 self.delete() 0165 event.ignore() 0166 return 0167 QDialog.keyPressEvent(self, event) 0168 0169 def selectionChanged(self): 0170 """update button states according to selection""" 0171 selectedRows = len(self.selection.selectedRows()) 0172 self.loadButton.setEnabled(selectedRows == 1) 0173 self.deleteButton.setEnabled(selectedRows >= 1) 0174 0175 def setQuery(self): 0176 """define the query depending on self.OnlyPending""" 0177 query = Query( 0178 "select g.id, g.starttime, " 0179 "p0.name||'///'||p1.name||'///'||p2.name||'///'||p3.name " 0180 "from game g, player p0," 0181 "player p1, player p2, player p3 " 0182 "where seed is null" 0183 " and p0.id=g.p0 and p1.id=g.p1 " 0184 " and p2.id=g.p2 and p3.id=g.p3 " 0185 "%s" 0186 "and exists(select 1 from score where game=g.id)" % 0187 ("and g.endtime is null " if self.onlyPending else "")) 0188 self.model.setResultset(query.records) 0189 self.view.hideColumn(0) 0190 0191 def __idxForGame(self, game): 0192 """return the model index for game""" 0193 for row in range(self.model.rowCount()): 0194 idx = self.model.index(row, 0) 0195 if self.model.data(idx, 0) == game: 0196 return idx 0197 return self.model.index(0, 0) 0198 0199 def __getSelectedGame(self): 0200 """return the game id of the selected game""" 0201 rows = self.selection.selectedRows() 0202 return self.model.data(rows[0], 0) if rows else 0 0203 0204 def pendingOrNot(self, chosen): 0205 """do we want to see all games or only pending games?""" 0206 if self.onlyPending != chosen: 0207 self.onlyPending = chosen 0208 prevSelected = self.__getSelectedGame() 0209 self.setQuery() 0210 idx = self.__idxForGame(prevSelected) 0211 self.view.selectRow(idx.row()) 0212 self.view.setFocus() 0213 0214 def loadGame(self): 0215 """load a game""" 0216 self.selectedGame = self.__getSelectedGame() 0217 self.buttonBox.accepted.emit() 0218 0219 def delete(self): 0220 """delete a game""" 0221 def answered(result, games): 0222 """question answered, result is True or False""" 0223 if result: 0224 for game in games: 0225 Query("DELETE FROM score WHERE game = ?", (game, )) 0226 Query("DELETE FROM game WHERE id = ?", (game, )) 0227 self.setQuery() # just reload entire table 0228 allGames = self.view.selectionModel().selectedRows(0) 0229 deleteGames = [x.data() for x in allGames] 0230 if not deleteGames: 0231 # should never happen 0232 logException('delete: 0 rows selected') 0233 WarningYesNo( 0234 i18n( 0235 "Do you really want to delete <numid>%1</numid> games?<br>" 0236 "This will be final, you cannot cancel it with " 0237 "the cancel button", 0238 len(deleteGames))).addCallback(answered, deleteGames)