Beispiel #1
0
    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)
Beispiel #2
0
class PlayerList(QDialog):

    """QtSQL Model view of the players"""

    def __init__(self, parent):
        QDialog.__init__(self)
        self.parent = parent
        self._data = {}
        self.table = QTableWidget(self)
        self.table.horizontalHeader().setStretchLastSection(True)
        self.table.verticalHeader().setVisible(False)
        self.table.setEditTriggers(QTableWidget.NoEditTriggers)
        self.table.itemChanged.connect(self.itemChanged)
        self.updateTable()
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setStandardButtons(
            QDialogButtonBox.Close)  # Close has the Rejected role
        self.buttonBox.rejected.connect(self.accept)
        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.table)
        layout.addLayout(cmdLayout)
        self.setLayout(layout)
        decorateWindow(self, m18n("Players"))
        self.setObjectName('Players')

    def showEvent(self, dummyEvent):
        """adapt view to content"""
        StateSaver(self, self.table)

    @staticmethod
    def sortKey(text):
        """display order in Table"""
        if len(text) == 0:
            return 'zzzzzzzzzzzz'
        else:
            return text.upper()

    def updateTable(self, data=None, currentName=None):
        """fills self.table from DB"""
        self.table.itemChanged.disconnect(self.itemChanged)
        table = self.table
        table.clear()
        if data is None:
            data = dict(
                Query('select name, id from player where name not like "ROBOT %"').records)
        self._data = data
        table.setColumnCount(1)
        table.setRowCount(len(self._data))
        table.setHorizontalHeaderLabels([m18n("Player")])
        table.setSelectionBehavior(QTableWidget.SelectRows)
        table.setSelectionMode(QTableWidget.SingleSelection)
        selectedItem = None
        for row, name in enumerate(sorted(self._data, key=self.sortKey)):
            item = QTableWidgetItem(name)
            if selectedItem is None:
                selectedItem = item
            table.setItem(row, 0, item)
            if name == currentName:
                selectedItem = item
        if selectedItem:
            table.setCurrentItem(selectedItem)
            table.scrollToItem(selectedItem)
        self.table.itemChanged.connect(self.itemChanged)

    def itemChanged(self, item):
        """this must be new because editing is disabled for others"""
        currentName = unicode(item.text())
        if currentName in self._data:
            Sorry(m18n('Player %1 already exists', currentName))
            self.setFocus()
            del self._data[
                unicode(self.table.item(self.table.currentRow(), 0).text())]
            self.updateTable(currentName=currentName)
            return
        query = Query('insert into player(name) values(?)', (currentName, ))
        if query.failure:
            Sorry(
                m18n(
                    'Error while adding player %1: %2',
                    currentName,
                    query.failure.message))
        self.updateTable(currentName=currentName)

    def slotInsert(self):
        """insert a record"""
        self._data[''] = 0
        self.updateTable(data=self._data, currentName='')
        for row in range(len(self._data)):
            item = self.table.item(row, 0)
            if len(item.text()) == 0:
                self.table.editItem(item)

    def delete(self):
        """delete selected entries"""
        items = self.table.selectedItems()
        currentRow = self.table.currentRow()
        if len(items):
            name = unicode(items[0].text())
            playerId = self._data[name]
            query = Query(
                "select 1 from game where p0=? or p1=? or p2=? or p3=?",
                (playerId, ) * 4)
            if len(query.records):
                Sorry(
                    m18n('This player cannot be deleted. There are games associated with %1.', name))
                return
            Query("delete from player where name=?", (name,))
            self.updateTable()
        self.table.setCurrentCell(min(currentRow, len(self._data) - 1), 0)

    def keyPressEvent(self, event):
        """use insert/delete keys for insert/delete"""
        key = event.key()
        if key == Qt.Key_Insert:
            self.slotInsert()
        elif key == Qt.Key_Delete:
            self.delete()
        else:
            QDialog.keyPressEvent(self, event)
Beispiel #3
0
class PlayerList(QDialog):

    """QtSQL Model view of the players"""

    def __init__(self, parent):
        QDialog.__init__(self)
        self.parent = parent
        self._data = {}
        self.table = QTableWidget(self)
        self.table.horizontalHeader().setStretchLastSection(True)
        self.table.verticalHeader().setVisible(False)
        self.table.setEditTriggers(QTableWidget.NoEditTriggers)
        self.table.itemChanged.connect(self.itemChanged)
        self.updateTable()
        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setStandardButtons(
            QDialogButtonBox.Close)  # Close has the Rejected role
        self.buttonBox.rejected.connect(self.accept)
        self.newButton = self.buttonBox.addButton(
            i18nc('define a new player',
                  "&New"),
            QDialogButtonBox.ActionRole)
        self.newButton.setIcon(KIcon("document-new"))
        self.newButton.clicked.connect(self.slotInsert)
        self.deleteButton = self.buttonBox.addButton(
            i18n("&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.table)
        layout.addLayout(cmdLayout)
        self.setLayout(layout)
        decorateWindow(self, i18n("Players"))
        self.setObjectName('Players')

    def showEvent(self, dummyEvent):
        """adapt view to content"""
        StateSaver(self, self.table)

    @staticmethod
    def sortKey(text):
        """display order in Table"""
        if len(text) == 0:
            return 'zzzzzzzzzzzz'
        else:
            return text.upper()

    def updateTable(self, data=None, currentName=None):
        """fills self.table from DB"""
        self.table.itemChanged.disconnect(self.itemChanged)
        table = self.table
        table.clear()
        if data is None:
            data = dict(
                Query('select name, id from player where name not like "ROBOT %"').records)
        self._data = data
        table.setColumnCount(1)
        table.setRowCount(len(self._data))
        table.setHorizontalHeaderLabels([i18n("Player")])
        table.setSelectionBehavior(QTableWidget.SelectRows)
        table.setSelectionMode(QTableWidget.SingleSelection)
        selectedItem = None
        for row, name in enumerate(sorted(self._data, key=self.sortKey)):
            item = QTableWidgetItem(name)
            if selectedItem is None:
                selectedItem = item
            table.setItem(row, 0, item)
            if name == currentName:
                selectedItem = item
        if selectedItem:
            table.setCurrentItem(selectedItem)
            table.scrollToItem(selectedItem)
        self.table.itemChanged.connect(self.itemChanged)

    def itemChanged(self, item):
        """this must be new because editing is disabled for others"""
        currentName = item.text()
        if currentName in self._data:
            Sorry(i18n('Player %1 already exists', currentName))
            self.setFocus()
            del self._data[self.table.item(self.table.currentRow(), 0).text()]
            self.updateTable(currentName=currentName)
            return
        query = Query('insert into player(name) values(?)', (currentName, ))
        if query.failure:
            Sorry(
                i18n(
                    'Error while adding player %1: %2',
                    currentName,
                    query.failure.message))
        self.updateTable(currentName=currentName)

    def slotInsert(self):
        """insert a record"""
        self._data[''] = 0
        self.updateTable(data=self._data, currentName='')
        for row in range(len(self._data)):
            item = self.table.item(row, 0)
            if len(item.text()) == 0:
                self.table.editItem(item)

    def delete(self):
        """delete selected entries"""
        items = self.table.selectedItems()
        currentRow = self.table.currentRow()
        if len(items):
            name = items[0].text()
            playerId = self._data[name]
            query = Query(
                "select 1 from game where p0=? or p1=? or p2=? or p3=?",
                (playerId, ) * 4)
            if len(query.records):
                Sorry(
                    i18n('This player cannot be deleted. There are games associated with %1.', name))
                return
            Query("delete from player where name=?", (name,))
            self.updateTable()
        self.table.setCurrentCell(min(currentRow, len(self._data) - 1), 0)

    def keyPressEvent(self, event):
        """use insert/delete keys for insert/delete"""
        key = event.key()
        if key == Qt.Key_Insert:
            self.slotInsert()
        elif key == Qt.Key_Delete:
            self.delete()
        else:
            QDialog.keyPressEvent(self, event)
Beispiel #4
0
class Games(QDialog):

    """a dialog for selecting a game"""

    def __init__(self, parent=None):
        super(Games, self).__init__(parent)
        self.selectedGame = None
        self.onlyPending = True
        decorateWindow(self, i18nc('kajongg', 'Games'))
        self.setObjectName('Games')
        self.resize(700, 400)
        self.model = GamesModel()
        if Debug.modelTest:
            self.modelTest = ModelTest(self.model, self)

        self.view = MJTableView(self)
        self.view.setModel(self.model)
        self.selection = QItemSelectionModel(self.model, self.view)
        self.view.setSelectionModel(self.selection)
        self.view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.view.setSelectionMode(QAbstractItemView.SingleSelection)

        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel)
        self.newButton = self.buttonBox.addButton(
            i18nc('start a new game', "&New"), QDialogButtonBox.ActionRole)
        self.newButton.setIcon(KIcon("document-new"))
        self.newButton.clicked.connect(self.accept)
        self.loadButton = self.buttonBox.addButton(
            i18n("&Load"), QDialogButtonBox.AcceptRole)
        self.loadButton.clicked.connect(self.loadGame)
        self.loadButton.setIcon(KIcon("document-open"))
        self.deleteButton = self.buttonBox.addButton(
            i18n("&Delete"), QDialogButtonBox.ActionRole)
        self.deleteButton.setIcon(KIcon("edit-delete"))
        self.deleteButton.clicked.connect(self.delete)

        chkPending = QCheckBox(i18n("Show only pending games"), self)
        chkPending.setChecked(True)
        cmdLayout = QHBoxLayout()
        cmdLayout.addWidget(chkPending)
        cmdLayout.addWidget(self.buttonBox)

        layout = QVBoxLayout()
        layout.addWidget(self.view)
        layout.addLayout(cmdLayout)
        self.setLayout(layout)
        StateSaver(self)

        self.selection.selectionChanged.connect(self.selectionChanged)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)
        self.view.doubleClicked.connect(self.loadGame)
        chkPending.stateChanged.connect(self.pendingOrNot)

    def showEvent(self, dummyEvent):
        """only now get the data set. Not doing this in__init__ would eventually
        make it easier to subclass from some generic TableEditor class"""
        self.setQuery()
        self.view.initView()
        self.selectionChanged()

    def keyPressEvent(self, event):
        """use insert/delete keys for insert/delete"""
        key = event.key()
        if key == Qt.Key_Insert:
            self.newEntry()
            return
        if key == Qt.Key_Delete:
            self.delete()
            event.ignore()
            return
        QDialog.keyPressEvent(self, event)

    def selectionChanged(self):
        """update button states according to selection"""
        selectedRows = len(self.selection.selectedRows())
        self.loadButton.setEnabled(selectedRows == 1)
        self.deleteButton.setEnabled(selectedRows >= 1)

    def setQuery(self):
        """define the query depending on self.OnlyPending"""
        query = Query(
            "select g.id, g.starttime, "
            "p0.name||'///'||p1.name||'///'||p2.name||'///'||p3.name "
            "from game g, player p0,"
            "player p1, player p2, player p3 "
            "where seed is null"
            " and p0.id=g.p0 and p1.id=g.p1 "
            " and p2.id=g.p2 and p3.id=g.p3 "
            "%s"
            "and exists(select 1 from score where game=g.id)" %
            ("and g.endtime is null " if self.onlyPending else ""))
        self.model.setResultset(query.records)
        self.view.hideColumn(0)

    def __idxForGame(self, game):
        """returns the model index for game"""
        for row in range(self.model.rowCount()):
            idx = self.model.index(row, 0)
            if self.model.data(idx, 0) == game:
                return idx
        return self.model.index(0, 0)

    def __getSelectedGame(self):
        """returns the game id of the selected game"""
        rows = self.selection.selectedRows()
        if rows:
            return self.model.data(rows[0], 0)
        else:
            return 0

    def pendingOrNot(self, chosen):
        """do we want to see all games or only pending games?"""
        if self.onlyPending != chosen:
            self.onlyPending = chosen
            prevSelected = self.__getSelectedGame()
            self.setQuery()
            idx = self.__idxForGame(prevSelected)
            self.view.selectRow(idx.row())
        self.view.setFocus()

    def loadGame(self):
        """load a game"""
        self.selectedGame = self.__getSelectedGame()
        self.buttonBox.accepted.emit()

    def delete(self):
        """delete a game"""
        def answered(result, games):
            """question answered, result is True or False"""
            if result:
                for game in games:
                    Query("DELETE FROM score WHERE game = ?", (game, ))
                    Query("DELETE FROM game WHERE id = ?", (game, ))
                self.setQuery()  # just reload entire table
        allGames = self.view.selectionModel().selectedRows(0)
        deleteGames = list(x.data() for x in allGames)
        if len(deleteGames) == 0:
            # should never happen
            logException('delete: 0 rows selected')
        WarningYesNo(
            i18n(
                "Do you really want to delete <numid>%1</numid> games?<br>"
                "This will be final, you cannot cancel it with "
                "the cancel button",
                len(deleteGames))).addCallback(answered, deleteGames)
Beispiel #5
0
class Games(QDialog):

    """a dialog for selecting a game"""

    def __init__(self, parent=None):
        super(Games, self).__init__(parent)
        self.selectedGame = None
        self.onlyPending = True
        decorateWindow(self, m18nc('kajongg', 'Games'))
        self.setObjectName('Games')
        self.resize(700, 400)
        self.model = GamesModel()
        if Debug.modelTest:
            self.modelTest = ModelTest(self.model, self)

        self.view = MJTableView(self)
        self.view.setModel(self.model)
        self.selection = QItemSelectionModel(self.model, self.view)
        self.view.setSelectionModel(self.selection)
        self.view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.view.setSelectionMode(QAbstractItemView.SingleSelection)

        self.buttonBox = QDialogButtonBox(self)
        self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel)
        self.newButton = self.buttonBox.addButton(
            m18nc('start a new game', "&New"), QDialogButtonBox.ActionRole)
        self.newButton.setIcon(KIcon("document-new"))
        self.newButton.clicked.connect(self.accept)
        self.loadButton = self.buttonBox.addButton(
            m18n("&Load"), QDialogButtonBox.AcceptRole)
        self.loadButton.clicked.connect(self.loadGame)
        self.loadButton.setIcon(KIcon("document-open"))
        self.deleteButton = self.buttonBox.addButton(
            m18n("&Delete"), QDialogButtonBox.ActionRole)
        self.deleteButton.setIcon(KIcon("edit-delete"))
        self.deleteButton.clicked.connect(self.delete)

        chkPending = QCheckBox(m18n("Show only pending games"), self)
        chkPending.setChecked(True)
        cmdLayout = QHBoxLayout()
        cmdLayout.addWidget(chkPending)
        cmdLayout.addWidget(self.buttonBox)

        layout = QVBoxLayout()
        layout.addWidget(self.view)
        layout.addLayout(cmdLayout)
        self.setLayout(layout)
        StateSaver(self)

        self.selection.selectionChanged.connect(self.selectionChanged)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)
        self.view.doubleClicked.connect(self.loadGame)
        chkPending.stateChanged.connect(self.pendingOrNot)

    def showEvent(self, dummyEvent):
        """only now get the data set. Not doing this in__init__ would eventually
        make it easier to subclass from some generic TableEditor class"""
        self.setQuery()
        self.view.initView()
        self.selectionChanged()

    def keyPressEvent(self, event):
        """use insert/delete keys for insert/delete"""
        key = event.key()
        if key == Qt.Key_Insert:
            self.newEntry()
            return
        if key == Qt.Key_Delete:
            self.delete()
            event.ignore()
            return
        QDialog.keyPressEvent(self, event)

    def selectionChanged(self):
        """update button states according to selection"""
        selectedRows = len(self.selection.selectedRows())
        self.loadButton.setEnabled(selectedRows == 1)
        self.deleteButton.setEnabled(selectedRows >= 1)

    def setQuery(self):
        """define the query depending on self.OnlyPending"""
        query = Query(
            "select g.id, g.starttime, "
            "p0.name||'///'||p1.name||'///'||p2.name||'///'||p3.name "
            "from game g, player p0,"
            "player p1, player p2, player p3 "
            "where seed is null"
            " and p0.id=g.p0 and p1.id=g.p1 "
            " and p2.id=g.p2 and p3.id=g.p3 "
            "%s"
            "and exists(select 1 from score where game=g.id)" %
            ("and g.endtime is null " if self.onlyPending else ""))
        self.model.setResultset(query.records)
        self.view.hideColumn(0)

    def __idxForGame(self, game):
        """returns the model index for game"""
        for row in range(self.model.rowCount()):
            idx = self.model.index(row, 0)
            if variantValue(self.model.data(idx, 0)) == game:
                return idx
        return self.model.index(0, 0)

    def __getSelectedGame(self):
        """returns the game id of the selected game"""
        rows = self.selection.selectedRows()
        if rows:
            return variantValue(self.model.data(rows[0], 0))
        else:
            return 0

    def pendingOrNot(self, chosen):
        """do we want to see all games or only pending games?"""
        if self.onlyPending != chosen:
            self.onlyPending = chosen
            prevSelected = self.__getSelectedGame()
            self.setQuery()
            idx = self.__idxForGame(prevSelected)
            self.view.selectRow(idx.row())
        self.view.setFocus()

    def loadGame(self):
        """load a game"""
        self.selectedGame = self.__getSelectedGame()
        self.buttonBox.accepted.emit()

    def delete(self):
        """delete a game"""
        def answered(result, games):
            """question answered, result is True or False"""
            if result:
                for game in games:
                    Query("DELETE FROM score WHERE game = ?", (game, ))
                    Query("DELETE FROM game WHERE id = ?", (game, ))
                self.setQuery()  # just reload entire table
        allGames = self.view.selectionModel().selectedRows(0)
        deleteGames = list(variantValue(x.data()) for x in allGames)
        if len(deleteGames) == 0:
            # should never happen
            logException('delete: 0 rows selected')
        WarningYesNo(
            m18n(
                "Do you really want to delete <numid>%1</numid> games?<br>"
                "This will be final, you cannot cancel it with "
                "the cancel button",
                len(deleteGames))).addCallback(answered, deleteGames)
Beispiel #6
0
    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)