class TableList(QWidget): """a widget for viewing, joining, leaving tables""" # pylint: disable=R0902 # pylint we have more than 10 attributes def __init__(self, client): super(TableList, self).__init__(None) self.autoStarted = False self.client = client self.setObjectName('TableList') self.resize(700, 400) self.view = MJTableView(self) self.differ = None self.debugModelTest = None self.requestedNewTable = False self.view.setItemDelegateForColumn(2, RichTextColumnDelegate(self.view)) buttonBox = QDialogButtonBox(self) self.newButton = buttonBox.addButton(m18nc('allocate a new table', "&New"), QDialogButtonBox.ActionRole) self.newButton.setIcon(KIcon("document-new")) self.newButton.setToolTip(m18n("Allocate a new table")) self.newButton.clicked.connect(self.client.newTable) self.joinButton = buttonBox.addButton(m18n("&Join"), QDialogButtonBox.AcceptRole) self.joinButton.clicked.connect(client.joinTable) self.joinButton.setIcon(KIcon("list-add-user")) self.joinButton.setToolTip(m18n("Join a table")) self.leaveButton = buttonBox.addButton(m18n("&Leave"), QDialogButtonBox.AcceptRole) self.leaveButton.clicked.connect(self.leaveTable) self.leaveButton.setIcon(KIcon("list-remove-user")) self.leaveButton.setToolTip(m18n("Leave a table")) self.compareButton = buttonBox.addButton(m18nc('Kajongg-Ruleset','Compare'), QDialogButtonBox.AcceptRole) self.compareButton.clicked.connect(self.compareRuleset) self.compareButton.setIcon(KIcon("preferences-plugin-script")) self.compareButton.setToolTip(m18n('Compare the rules of this table with my own rulesets')) self.chatButton = buttonBox.addButton(m18n('&Chat'), QDialogButtonBox.AcceptRole) self.chatButton.setIcon(KIcon("call-start")) self.chatButton.clicked.connect(self.chat) self.startButton = buttonBox.addButton(m18n('&Start'), QDialogButtonBox.AcceptRole) self.startButton.clicked.connect(self.startGame) self.startButton.setIcon(KIcon("arrow-right")) self.startButton.setToolTip(m18n("Start playing on a table. Empty seats will be taken by robot players.")) cmdLayout = QHBoxLayout() cmdLayout.addWidget(buttonBox) layout = QVBoxLayout() layout.addWidget(self.view) layout.addLayout(cmdLayout) self.setLayout(layout) self.view.doubleClicked.connect(client.joinTable) StateSaver(self, self.view.horizontalHeader()) self.updateButtonsForTable(None) def hideEvent(self, dummyEvent): # pylint: disable=R0201 """table window hides""" field = Internal.field field.startingGame = False model = self.view.model() if model: for table in model.tables: if table.chatWindow: table.chatWindow.hide() table.chatWindow = None if not field.game or field.game.client != self.client: # do we still need this connection? self.client.logout() def chat(self): """chat. Only generate ChatWindow after the message has successfully been sent to the server. Because the server might have gone away.""" def initChat(_): """now that we were able to send the message to the server instantiate the chat window""" table.chatWindow = ChatWindow(table) table.chatWindow.receiveLine(msg) table = self.selectedTable() if not table.chatWindow: line = m18nE('opens a chat window') msg = ChatMessage(table.tableid, table.client.name, line, isStatusMessage=True) table.client.sendChat(msg).addCallback(initChat).addErrback(self.client.tableError) elif table.chatWindow.isVisible(): table.chatWindow.hide() else: table.chatWindow.show() def show(self): """prepare the view and show it""" if self.client.hasLocalServer(): title = m18n('Local Games with Ruleset %1', self.client.ruleset.name) else: title = m18n('Tables at %1', self.client.connection.url) self.setWindowTitle(' - '.join([self.client.name, title, 'Kajongg'])) self.view.hideColumn(1) tableCount = self.view.model().rowCount(None) if self.view.model() else 0 self.view.showColumn(0) self.view.showColumn(2) self.view.showColumn(4) if tableCount or not self.client.hasLocalServer(): QWidget.show(self) if self.client.hasLocalServer(): self.view.hideColumn(0) self.view.hideColumn(2) self.view.hideColumn(4) def selectTable(self, idx): """select table by idx""" self.view.selectRow(idx) self.updateButtonsForTable(self.selectedTable()) def updateButtonsForTable(self, table): """update button status for the currently selected table""" hasTable = bool(table) suspended = hasTable and bool(table.suspendedAt) running = hasTable and table.running suspendedLocalGame = suspended and table.gameid and self.client.hasLocalServer() self.joinButton.setEnabled(hasTable and not running and not table.isOnline(self.client.name) and (self.client.name in table.playerNames) == suspended) self.leaveButton.setVisible(not (suspendedLocalGame)) self.compareButton.setVisible(not (suspendedLocalGame)) self.startButton.setVisible(not suspended) if suspendedLocalGame: self.newButton.setToolTip(m18n("Start a new game")) self.joinButton.setText(m18nc('resuming a local suspended game', '&Resume')) self.joinButton.setToolTip(m18n("Resume the selected suspended game")) else: self.newButton.setToolTip(m18n("Allocate a new table")) self.joinButton.setText(m18n('&Join')) self.joinButton.setToolTip(m18n("Join a table")) self.leaveButton.setEnabled(hasTable and not running and not self.joinButton.isEnabled()) self.startButton.setEnabled(not running and not suspendedLocalGame and hasTable \ and self.client.name == table.playerNames[0]) self.compareButton.setEnabled(hasTable and table.myRuleset is None) self.chatButton.setVisible(not self.client.hasLocalServer()) self.chatButton.setEnabled(not running and hasTable and self.client.name in table.playerNames and sum(x.startswith('Robot ') for x in table.playerNames) < 3) if self.chatButton.isEnabled(): self.chatButton.setToolTip(m18n("Chat with others on this table")) else: self.chatButton.setToolTip(m18n("For chatting with others on this table, please first take a seat")) def selectionChanged(self, selected, dummyDeselected): """update button states according to selection""" if selected.indexes(): self.selectTable(selected.indexes()[0].row()) def selectedTable(self): """returns the selected table""" if self.view.selectionModel(): index = self.view.selectionModel().currentIndex() if index.isValid() and self.view.model(): return self.view.model().tables[index.row()] def compareRuleset(self): """compare the ruleset of this table against ours""" table = self.selectedTable() self.differ = RulesetDiffer(table.ruleset, Ruleset.availableRulesets()) self.differ.show() def startGame(self): """start playing at the selected table""" table = self.selectedTable() self.startButton.setEnabled(False) self.client.callServer('startGame', table.tableid).addErrback(self.client.tableError) def leaveTable(self): """leave a table""" table = self.selectedTable() self.client.callServer('leaveTable', table.tableid).addErrback(self.client.tableError) def __keepChatWindows(self, tables): """copy chatWindows from the old table list which will be thrown away""" if self.view.model(): chatWindows = dict((x.tableid, x.chatWindow) for x in self.view.model().tables) unusedWindows = set(x.chatWindow for x in self.view.model().tables) for table in tables: table.chatWindow = chatWindows.get(table.tableid, None) unusedWindows -= set([table.chatWindow]) for unusedWindow in unusedWindows: if unusedWindow: unusedWindow.hide() def __preselectTableId(self, tables): """which table should be preselected? If we just requested a new table: select first new table. Only in the rare case that two clients request a new table at the same moment, this could put the focus on the wrong table. Ignore that for now. else if we had one selected: select that again else: select first table""" if self.requestedNewTable: self.requestedNewTable = False model = self.view.model() if model: oldIds = set(x.tableid for x in model.tables) newIds = sorted(list(set(x.tableid for x in tables) - oldIds)) if newIds: return newIds[0] if self.selectedTable(): return self.selectedTable().tableid return 0 def loadTables(self, tables): """build and use a model around the tables. Show all new tables (no gameid given yet) and all suspended tables that also exist locally. In theory all suspended games should exist locally but there might have been bugs or somebody might have removed the local database like when reinstalling linux""" if not Internal.field: return if Debug.traffic: for table in tables: if table.gameid and not table.gameExistsLocally(): logDebug('%s does not exist locally' % table) tables = [x for x in tables if not x.gameid or x.gameExistsLocally()] tables.sort(key=lambda x: x.tableid) preselectTableId = self.__preselectTableId(tables) self.__keepChatWindows(tables) model = TablesModel(tables) self.view.setModel(model) if Debug.modelTest: self.debugModelTest = ModelTest(model, self.view) selection = QItemSelectionModel(model, self.view) self.view.initView() self.view.setSelectionModel(selection) self.view.setSelectionBehavior(QAbstractItemView.SelectRows) self.view.setSelectionMode(QAbstractItemView.SingleSelection) selection.selectionChanged.connect(self.selectionChanged) if len(tables) == 1: self.selectTable(0) self.startButton.setFocus() elif not tables: self.newButton.setFocus() else: _ = [x for x in tables if x.tableid >= preselectTableId] self.selectTable(tables.index(_[0]) if _ else 0) self.updateButtonsForTable(self.selectedTable()) self.view.setFocus()
class PlayerList(QDialog): """QtSQL Model view of the players""" def __init__(self, parent): QDialog.__init__(self) self.parent = parent self.model = QSqlTableModel(self, DBHandle.default) self.model.setEditStrategy(QSqlTableModel.OnManualSubmit) self.model.setTable("player") self.model.setSort(1, 0) self.model.setHeaderData(1, Qt.Horizontal, QVariant(m18nc("Player", "Name"))) self.model.setFilter('name not like "ROBOT %" and name not like "Robot %"') self.view = MJTableView(self) self.view.verticalHeader().show() self.view.setModel(self.model) self.view.hideColumn(0) self.buttonBox = QDialogButtonBox() self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.newButton = self.buttonBox.addButton(m18nc('define a new player', "&New"), QDialogButtonBox.ActionRole) self.newButton.setIcon(KIcon("document-new")) self.newButton.clicked.connect(self.slotInsert) self.deleteButton = self.buttonBox.addButton(m18n("&Delete"), QDialogButtonBox.ActionRole) self.deleteButton.setIcon(KIcon("edit-delete")) self.deleteButton.clicked.connect(self.delete) cmdLayout = QHBoxLayout() cmdLayout.addWidget(self.buttonBox) layout = QVBoxLayout() layout.addWidget(self.view) layout.addLayout(cmdLayout) self.setLayout(layout) self.setWindowTitle(m18n("Players") + ' - Kajongg') self.setObjectName('Players') def showEvent(self, dummyEvent): """adapt view to content""" if not self.model.select(): logError("PlayerList: select failed") sys.exit(1) self.view.initView() StateSaver(self, self.view.horizontalHeader()) if not self.view.isColumnHidden(2): # we loaded a kajonggrc written by an older kajongg version where this table # still had more columns. This should happen only once. self.view.hideColumn(2) self.view.hideColumn(3) def accept(self): """commit all modifications""" self.view.selectRow(0) # if ALT-O is entered while editing a new row, this is one way # to end editing and to pass the new value to the model if not self.model.submitAll(): Sorry(m18n('Cannot save this. Possibly the name already exists. <br><br>' \ 'Message from database:<br><br><message>%1</message>', self.model.lastError().text())) return QDialog.accept(self) def slotInsert(self): """insert a record""" self.model.insertRow(self.model.rowCount()) self.view.selectRow(self.model.rowCount()-1) def delete(self): """delete selected entries""" sel = self.view.selectionModel() maxDel = self.view.currentIndex().row() - 1 for idx in sel.selectedIndexes(): if idx.column() != 1: continue # sqlite3 does not enforce referential integrity. # we could add a trigger to sqlite3 but if it raises an exception # it will be thrown away silently. # if anybody knows how to propagate sqlite3 exceptions via QtSql # into python please tell me (wrohdewald) player = self.model.createIndex(idx.row(), 0).data().toInt()[0] # no query preparation, we don't expect lots of data if Query("select 1 from game where p0==%d or p1==%d or p2==%d or p3==%d" % \ (player, player, player, player)).records: Sorry(m18n('This player cannot be deleted. There are games associated with %1.', idx.data().toString())) else: self.model.removeRow(idx.row()) maxDel = max(maxDel, idx.row()) self.view.selectRow(maxDel+1) def keyPressEvent(self, event): """use insert/delete keys for insert/delete""" key = event.key() if key == Qt.Key_Insert: self.slotInsert() return QDialog.keyPressEvent(self, event)
class TableList(QWidget): """a widget for viewing, joining, leaving tables""" # pylint: disable=too-many-instance-attributes def __init__(self, client): super(TableList, self).__init__(None) self.autoStarted = False self.client = client self.setObjectName('TableList') self.resize(700, 400) self.view = MJTableView(self) self.differ = None self.debugModelTest = None self.requestedNewTable = False self.view.setItemDelegateForColumn(2, RichTextColumnDelegate(self.view)) buttonBox = QDialogButtonBox(self) self.newButton = buttonBox.addButton( i18nc('allocate a new table', "&New"), QDialogButtonBox.ActionRole) self.newButton.setIcon(KIcon("document-new")) self.newButton.setToolTip(i18n("Allocate a new table")) self.newButton.clicked.connect(self.client.newTable) self.joinButton = buttonBox.addButton(i18n("&Join"), QDialogButtonBox.AcceptRole) self.joinButton.clicked.connect(client.joinTable) self.joinButton.setIcon(KIcon("list-add-user")) self.joinButton.setToolTip(i18n("Join a table")) self.leaveButton = buttonBox.addButton(i18n("&Leave"), QDialogButtonBox.AcceptRole) self.leaveButton.clicked.connect(self.leaveTable) self.leaveButton.setIcon(KIcon("list-remove-user")) self.leaveButton.setToolTip(i18n("Leave a table")) self.compareButton = buttonBox.addButton( i18nc('Kajongg-Ruleset', 'Compare'), QDialogButtonBox.AcceptRole) self.compareButton.clicked.connect(self.compareRuleset) self.compareButton.setIcon(KIcon("preferences-plugin-script")) self.compareButton.setToolTip( i18n('Compare the rules of this table with my own rulesets')) self.chatButton = buttonBox.addButton(i18n('&Chat'), QDialogButtonBox.AcceptRole) self.chatButton.setIcon(KIcon("call-start")) self.chatButton.clicked.connect(self.chat) self.startButton = buttonBox.addButton(i18n('&Start'), QDialogButtonBox.AcceptRole) self.startButton.clicked.connect(self.startGame) self.startButton.setIcon(KIcon("arrow-right")) self.startButton.setToolTip( i18n("Start playing on a table. " "Empty seats will be taken by robot players.")) cmdLayout = QHBoxLayout() cmdLayout.addWidget(buttonBox) layout = QVBoxLayout() layout.addWidget(self.view) layout.addLayout(cmdLayout) self.setLayout(layout) self.view.doubleClicked.connect(client.joinTable) StateSaver(self, self.view.horizontalHeader()) self.updateButtonsForTable(None) def hideEvent(self, dummyEvent): # pylint: disable=no-self-use """table window hides""" scene = Internal.scene if scene: scene.startingGame = False model = self.view.model() if model: for table in model.tables: if table.chatWindow: table.chatWindow.hide() table.chatWindow = None if scene: if not scene.game or scene.game.client != self.client: # do we still need this connection? self.client.logout() def chat(self): """chat. Only generate ChatWindow after the message has successfully been sent to the server. Because the server might have gone away.""" def initChat(_): """now that we were able to send the message to the server instantiate the chat window""" table.chatWindow = ChatWindow(table=table) table.chatWindow.receiveLine(msg) table = self.selectedTable() if not table.chatWindow: line = i18nE('opens a chat window') msg = ChatMessage(table.tableid, table.client.name, line, isStatusMessage=True) table.client.sendChat(msg).addCallback(initChat).addErrback( self.client.tableError) elif table.chatWindow.isVisible(): table.chatWindow.hide() else: table.chatWindow.show() def show(self): """prepare the view and show it""" if self.client.hasLocalServer(): title = i18n('Local Games with Ruleset %1', self.client.ruleset.name) else: title = i18n('Tables at %1', self.client.connection.url) decorateWindow(self, ' - '.join([self.client.name, title])) self.view.hideColumn(1) tableCount = self.view.model().rowCount( None) if self.view.model() else 0 self.view.showColumn(0) self.view.showColumn(2) self.view.showColumn(4) if tableCount or not self.client.hasLocalServer(): QWidget.show(self) if self.client.hasLocalServer(): self.view.hideColumn(0) self.view.hideColumn(2) self.view.hideColumn(4) def selectTable(self, idx): """select table by idx""" self.view.selectRow(idx) self.updateButtonsForTable(self.selectedTable()) def updateButtonsForTable(self, table): """update button status for the currently selected table""" hasTable = bool(table) suspended = hasTable and bool(table.suspendedAt) running = hasTable and table.running suspendedLocalGame = (suspended and table.gameid and self.client.hasLocalServer()) self.joinButton.setEnabled( hasTable and not running and not table.isOnline(self.client.name) and (self.client.name in table.playerNames) == suspended) self.leaveButton.setVisible(not suspendedLocalGame) self.compareButton.setVisible(not suspendedLocalGame) self.startButton.setVisible(not suspended) if suspendedLocalGame: self.newButton.setToolTip(i18n("Start a new game")) self.joinButton.setText( i18nc('resuming a local suspended game', '&Resume')) self.joinButton.setToolTip( i18n("Resume the selected suspended game")) else: self.newButton.setToolTip(i18n("Allocate a new table")) self.joinButton.setText(i18n('&Join')) self.joinButton.setToolTip(i18n("Join a table")) self.leaveButton.setEnabled(hasTable and not running and not self.joinButton.isEnabled()) self.startButton.setEnabled( not running and not suspendedLocalGame and hasTable and self.client.name == table.playerNames[0]) self.compareButton.setEnabled(hasTable and table.myRuleset is None) self.chatButton.setVisible(not self.client.hasLocalServer()) self.chatButton.setEnabled( not running and hasTable and self.client.name in table.playerNames and sum(x.startswith('Robot ') for x in table.playerNames) < 3) if self.chatButton.isEnabled(): self.chatButton.setToolTip(i18n("Chat with others on this table")) else: self.chatButton.setToolTip( i18n("For chatting with others on this table, " "please first take a seat")) def selectionChanged(self, selected, dummyDeselected): """update button states according to selection""" if selected.indexes(): self.selectTable(selected.indexes()[0].row()) def selectedTable(self): """returns the selected table""" if self.view.selectionModel(): index = self.view.selectionModel().currentIndex() if index.isValid() and self.view.model(): return self.view.model().tables[index.row()] def compareRuleset(self): """compare the ruleset of this table against ours""" table = self.selectedTable() self.differ = RulesetDiffer(table.ruleset, Ruleset.availableRulesets()) self.differ.show() def startGame(self): """start playing at the selected table""" table = self.selectedTable() self.startButton.setEnabled(False) self.client.callServer('startGame', table.tableid).addErrback( self.client.tableError) def leaveTable(self): """leave a table""" table = self.selectedTable() self.client.callServer('leaveTable', table.tableid).addErrback( self.client.tableError) def __keepChatWindows(self, tables): """copy chatWindows from the old table list which will be thrown away""" if self.view.model(): chatWindows = dict( (x.tableid, x.chatWindow) for x in self.view.model().tables) unusedWindows = set(x.chatWindow for x in self.view.model().tables) for table in tables: table.chatWindow = chatWindows.get(table.tableid, None) unusedWindows -= set([table.chatWindow]) for unusedWindow in unusedWindows: if unusedWindow: unusedWindow.hide() def __preselectTableId(self, tables): """which table should be preselected? If we just requested a new table: select first new table. Only in the rare case that two clients request a new table at the same moment, this could put the focus on the wrong table. Ignore that for now. else if we had one selected: select that again else: select first table""" if self.requestedNewTable: self.requestedNewTable = False model = self.view.model() if model: oldIds = set(x.tableid for x in model.tables) newIds = sorted(list(set(x.tableid for x in tables) - oldIds)) if newIds: return newIds[0] if self.selectedTable(): return self.selectedTable().tableid return 0 def loadTables(self, tables): """build and use a model around the tables. Show all new tables (no gameid given yet) and all suspended tables that also exist locally. In theory all suspended games should exist locally but there might have been bugs or somebody might have removed the local database like when reinstalling linux""" if not Internal.scene: return if Debug.table: for table in tables: if table.gameid and not table.gameExistsLocally(): logDebug('Table %s does not exist locally' % table) tables = [x for x in tables if not x.gameid or x.gameExistsLocally()] tables.sort(key=lambda x: x.tableid) preselectTableId = self.__preselectTableId(tables) self.__keepChatWindows(tables) model = TablesModel(tables) self.view.setModel(model) if Debug.modelTest: self.debugModelTest = ModelTest(model, self.view) selection = QItemSelectionModel(model, self.view) self.view.initView() self.view.setSelectionModel(selection) self.view.setSelectionBehavior(QAbstractItemView.SelectRows) self.view.setSelectionMode(QAbstractItemView.SingleSelection) selection.selectionChanged.connect(self.selectionChanged) if len(tables) == 1: self.selectTable(0) self.startButton.setFocus() elif not tables: self.newButton.setFocus() else: _ = [x for x in tables if x.tableid >= preselectTableId] self.selectTable(tables.index(_[0]) if _ else 0) self.updateButtonsForTable(self.selectedTable()) self.view.setFocus()
class PlayerList(QDialog): """QtSQL Model view of the players""" def __init__(self, parent): QDialog.__init__(self) self.parent = parent self.model = QSqlTableModel(self, DBHandle.default) self.model.setEditStrategy(QSqlTableModel.OnManualSubmit) self.model.setTable("player") self.model.setSort(1, 0) self.model.setHeaderData(1, Qt.Horizontal, QVariant(m18nc("Player", "Name"))) self.model.setFilter( 'name not like "ROBOT %" and name not like "Robot %"') self.view = MJTableView(self) self.view.verticalHeader().show() self.view.setModel(self.model) self.view.hideColumn(0) self.buttonBox = QDialogButtonBox() self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.newButton = self.buttonBox.addButton( m18nc('define a new player', "&New"), QDialogButtonBox.ActionRole) self.newButton.setIcon(KIcon("document-new")) self.newButton.clicked.connect(self.slotInsert) self.deleteButton = self.buttonBox.addButton( m18n("&Delete"), QDialogButtonBox.ActionRole) self.deleteButton.setIcon(KIcon("edit-delete")) self.deleteButton.clicked.connect(self.delete) cmdLayout = QHBoxLayout() cmdLayout.addWidget(self.buttonBox) layout = QVBoxLayout() layout.addWidget(self.view) layout.addLayout(cmdLayout) self.setLayout(layout) self.setWindowTitle(m18n("Players") + ' - Kajongg') self.setObjectName('Players') def showEvent(self, dummyEvent): """adapt view to content""" if not self.model.select(): logError("PlayerList: select failed") sys.exit(1) self.view.initView() StateSaver(self, self.view.horizontalHeader()) if not self.view.isColumnHidden(2): # we loaded a kajonggrc written by an older kajongg version where this table # still had more columns. This should happen only once. self.view.hideColumn(2) self.view.hideColumn(3) def accept(self): """commit all modifications""" self.view.selectRow( 0) # if ALT-O is entered while editing a new row, this is one way # to end editing and to pass the new value to the model if not self.model.submitAll(): Sorry(m18n('Cannot save this. Possibly the name already exists. <br><br>' \ 'Message from database:<br><br><message>%1</message>', self.model.lastError().text())) return QDialog.accept(self) def slotInsert(self): """insert a record""" self.model.insertRow(self.model.rowCount()) self.view.selectRow(self.model.rowCount() - 1) def delete(self): """delete selected entries""" sel = self.view.selectionModel() maxDel = self.view.currentIndex().row() - 1 for idx in sel.selectedIndexes(): if idx.column() != 1: continue # sqlite3 does not enforce referential integrity. # we could add a trigger to sqlite3 but if it raises an exception # it will be thrown away silently. # if anybody knows how to propagate sqlite3 exceptions via QtSql # into python please tell me (wrohdewald) player = self.model.createIndex(idx.row(), 0).data().toInt()[0] # no query preparation, we don't expect lots of data if Query("select 1 from game where p0==%d or p1==%d or p2==%d or p3==%d" % \ (player, player, player, player)).records: Sorry( m18n( 'This player cannot be deleted. There are games associated with %1.', idx.data().toString())) else: self.model.removeRow(idx.row()) maxDel = max(maxDel, idx.row()) self.view.selectRow(maxDel + 1) def keyPressEvent(self, event): """use insert/delete keys for insert/delete""" key = event.key() if key == Qt.Key_Insert: self.slotInsert() return QDialog.keyPressEvent(self, event)