Exemple #1
0
 def notifyAction(self, client, move):
     if client.beginQuestion or client.game:
         Sorry(i18n('%1 is not ready to start the game', move.player.name))
     if client.beginQuestion:
         client.beginQuestion.cancel()
     elif client.game:
         return client.game.close()
Exemple #2
0
 def tableChanged(self, table):
     """update table list"""
     oldTable, newTable = Client.tableChanged(self, table)
     if oldTable and oldTable == self.table:
         # this happens if a table has more than one human player and
         # one of them leaves the table. In that case, the other players
         # need this code.
         self.table = newTable
         if len(newTable.playerNames) == 3:
             # only tell about the first player leaving, because the
             # others will then automatically leave too
             for name in oldTable.playerNames:
                 if name != self.name and not newTable.isOnline(name):
                     def sorried(dummy):
                         """user ack"""
                         game = self.game
                         if game:
                             self.game = None
                             return game.close()
                     if self.beginQuestion:
                         self.beginQuestion.cancel()
                     Sorry(i18n('Player %1 has left the table', name)).addCallback(
                         sorried).addCallback(self.showTableList)
                     break
     self.__updateTableList()
Exemple #3
0
 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)
Exemple #4
0
 def selectButton(self, button=None):
     """select default answer. button may also be of type Message."""
     if self.answered:
         # sometimes we get this event twice
         return
     if button is None:
         button = self.focusWidget()
     if isinstance(button, Message):
         assert any(x.message == button for x in self.buttons)
         answer = button
     else:
         answer = button.message
     if not self.client.game.myself.sayable[answer]:
         self.proposeAction().setFocus() # go back to default action
         self.sorry = Sorry(i18n('You cannot say %1', answer.i18nName))
         return
     self.timer.stop()
     self.answered = True
     if self.sorry:
         self.sorry.cancel()
     self.sorry = None
     Internal.scene.clientDialog = None
     self.deferred.callback(answer)
Exemple #5
0
 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)
Exemple #6
0
 def setData(self, index, value, role=Qt.EditRole):
     """change data in the model"""
     # pylint:  disable=too-many-branches
     if not index.isValid():
         return False
     try:
         dirty = False
         column = index.column()
         item = index.internalPointer()
         ruleset = item.ruleset()
         content = item.rawContent
         if role == Qt.EditRole:
             if isinstance(content, Ruleset) and column == 0:
                 oldName = content.name
                 content.rename(english(value))
                 dirty = oldName != content.name
             elif isinstance(content, RuleBase):
                 dirty, message = self.__setRuleData(column, content, value)
                 if message:
                     Sorry(message)
                     return False
             else:
                 return False
         elif role == Qt.CheckStateRole:
             if isinstance(content, BoolRule) and column == 1:
                 if not isinstance(ruleset, PredefinedRuleset):
                     newValue = value == Qt.Checked
                     if content.parameter != newValue:
                         dirty = True
                         content.parameter = newValue
             else:
                 return False
         if dirty:
             if isinstance(content, RuleBase):
                 ruleset.updateRule(content)
             self.dataChanged.emit(index, index)
         return True
     except BaseException:
         return False
Exemple #7
0
 def selectButton(self, button=None):
     """select default answer. button may also be of type Message."""
     if self.answered:
         # sometimes we get this event twice
         return
     if button is None:
         button = self.focusWidget()
     if isinstance(button, Message):
         assert any(x.message == button for x in self.buttons)
         answer = button
     else:
         answer = button.message
     if not self.client.game.myself.sayable[answer]:
         self.proposeAction().setFocus() # go back to default action
         self.sorry = Sorry(m18n('You cannot say %1', answer.i18nName))
         return
     self.timer.stop()
     self.answered = True
     if self.sorry:
         self.sorry.cancel()
     self.sorry = None
     Internal.scene.clientDialog = None
     self.deferred.callback(answer)
Exemple #8
0
def logMessage(msg, prio, showDialog, showStack=False, withGamePrefix=True):
    """writes info message to log and to stdout"""
    # pylint: disable=R0912
    if isinstance(msg, Exception):
        msg = __exceptionToString(msg)
    msg = str(msg)
    msg = translateServerMessage(msg)
    __logUnicodeMessage(prio, __enrichMessage(msg, withGamePrefix))
    if showStack:
        if showStack is True:
            lower = 2
        else:
            lower = -showStack - 3
        for line in traceback.format_stack()[lower:-3]:
            if 'logException' not in line:
                __logUnicodeMessage(prio, '  ' + line.strip())
    if int(Debug.callers):
        __logUnicodeMessage(prio, callers(int(Debug.callers)))
    if showDialog and not Internal.isServer:
        if prio == logging.INFO:
            return Information(msg)
        else:
            return Sorry(msg, always=True)
    return NoPrompt(msg)
Exemple #9
0
class ClientDialog(QDialog):

    """a simple popup dialog for asking the player what he wants to do"""

    def __init__(self, client, parent=None):
        QDialog.__init__(self, parent)
        decorateWindow(self, m18n('Choose'))
        self.setObjectName('ClientDialog')
        self.client = client
        self.layout = QGridLayout(self)
        self.progressBar = QProgressBar()
        self.timer = QTimer()
        if not client.game.autoPlay:
            self.timer.timeout.connect(self.timeout)
        self.deferred = None
        self.buttons = []
        self.setWindowFlags(Qt.SubWindow | Qt.WindowStaysOnTopHint)
        self.setModal(False)
        self.btnHeight = 0
        self.answered = False
        self.move = None
        self.sorry = None

    def keyPressEvent(self, event):
        """ESC selects default answer"""
        if not self.client.game or self.client.game.autoPlay:
            return
        if event.key() in [Qt.Key_Escape, Qt.Key_Space]:
            self.selectButton()
            event.accept()
        else:
            for btn in self.buttons:
                if str(event.text()).upper() == btn.message.shortcut:
                    self.selectButton(btn)
                    event.accept()
                    return
            QDialog.keyPressEvent(self, event)

    def __declareButton(self, message):
        """define a button"""
        maySay = self.client.game.myself.sayable[message]
        if Internal.Preferences.showOnlyPossibleActions and not maySay:
            return
        btn = DlgButton(message, self)
        btn.setAutoDefault(True)
        btn.clicked.connect(self.selectedAnswer)
        self.buttons.append(btn)

    def focusTileChanged(self):
        """update icon and tooltip for the discard button"""
        if not self.client.game:
            return
        for button in self.buttons:
            button.decorate(self.client.game.myself.handBoard.focusTile)
        for uiTile in self.client.game.myself.handBoard.lowerHalfTiles():
            txt = []
            for button in self.buttons:
                _, _, tileTxt = button.message.toolTip(button, uiTile.tile)
                if tileTxt:
                    txt.append(tileTxt)
            uiTile.setToolTip('<br><br>'.join(txt))
        if self.client.game.activePlayer == self.client.game.myself:
            Internal.scene.handSelectorChanged(
                self.client.game.myself.handBoard)

    def checkTiles(self):
        """does the logical state match the displayed tiles?"""
        for player in self.client.game.players:
            player.handBoard.checkTiles()

    def messages(self):
        """a list of all messages returned by the declared buttons"""
        return list(x.message for x in self.buttons)

    def proposeAction(self):
        """either intelligently or first button by default. May also
        focus a proposed tile depending on the action."""
        result = self.buttons[0]
        game = self.client.game
        if game.autoPlay or Internal.Preferences.propose:
            answer, parameter = game.myself.intelligence.selectAnswer(
                self.messages())
            result = [x for x in self.buttons if x.message == answer][0]
            result.setFocus()
            if answer in [Message.Discard, Message.OriginalCall]:
                for uiTile in game.myself.handBoard.uiTiles:
                    if uiTile.tile is parameter:
                        game.myself.handBoard.focusTile = uiTile
        return result

    def askHuman(self, move, answers, deferred):
        """make buttons specified by answers visible. The first answer is default.
        The default button only appears with blue border when this dialog has
        focus but we always want it to be recognizable. Hence setBackgroundRole."""
        self.move = move
        self.deferred = deferred
        for answer in answers:
            self.__declareButton(answer)
        self.focusTileChanged()
        self.show()
        self.checkTiles()
        game = self.client.game
        myTurn = game.activePlayer == game.myself
        prefButton = self.proposeAction()
        if game.autoPlay:
            self.selectButton(prefButton)
            return
        prefButton.setFocus()

        self.progressBar.setVisible(not myTurn)
        if not myTurn:
            msecs = 50
            self.progressBar.setMinimum(0)
            self.progressBar.setMaximum(
                game.ruleset.claimTimeout * 1000 // msecs)
            self.progressBar.reset()
            self.timer.start(msecs)

    def placeInField(self):
        """place the dialog at bottom or to the right depending on space."""
        mainWindow = Internal.scene.mainWindow
        cwi = mainWindow.centralWidget()
        view = mainWindow.centralView
        geometry = self.geometry()
        if not self.btnHeight:
            self.btnHeight = self.buttons[0].height()
        vertical = view.width() > view.height() * 1.2
        if vertical:
            height = (len(self.buttons) + 1) * self.btnHeight * 1.2
            width = (cwi.width() - cwi.height()) // 2
            geometry.setX(cwi.width() - width)
            geometry.setY(min(cwi.height() // 3, cwi.height() - height))
        else:
            handBoard = self.client.game.myself.handBoard
            if not handBoard:
                # we are in the progress of logging out
                return
            hbLeftTop = view.mapFromScene(
                handBoard.mapToScene(handBoard.rect().topLeft()))
            hbRightBottom = view.mapFromScene(
                handBoard.mapToScene(handBoard.rect().bottomRight()))
            width = hbRightBottom.x() - hbLeftTop.x()
            height = self.btnHeight
            geometry.setY(cwi.height() - height)
            geometry.setX(hbLeftTop.x())
        for idx, btn in enumerate(self.buttons + [self.progressBar]):
            self.layout.addWidget(
                btn,
                idx +
                1 if vertical else 0,
                idx +
                1 if not vertical else 0)
        idx = len(self.buttons) + 2
        spacer = QSpacerItem(
            20,
            20,
            QSizePolicy.Expanding,
            QSizePolicy.Expanding)
        self.layout.addItem(
            spacer,
            idx if vertical else 0,
            idx if not vertical else 0)

        geometry.setWidth(width)
        geometry.setHeight(height)
        self.setGeometry(geometry)

    def showEvent(self, dummyEvent):
        """try to place the dialog such that it does not cover interesting information"""
        self.placeInField()

    def timeout(self):
        """the progressboard wants an update"""
        pBar = self.progressBar
        if isAlive(pBar):
            pBar.setValue(pBar.value() + 1)
            pBar.setVisible(True)
            if pBar.value() == pBar.maximum():
                # timeout: we always return the original default answer, not
                # the one with focus
                self.selectButton()
                pBar.setVisible(False)

    def selectButton(self, button=None):
        """select default answer. button may also be of type Message."""
        if self.answered:
            # sometimes we get this event twice
            return
        if button is None:
            button = self.focusWidget()
        if isinstance(button, Message):
            assert any(x.message == button for x in self.buttons)
            answer = button
        else:
            answer = button.message
        if not self.client.game.myself.sayable[answer]:
            self.proposeAction().setFocus() # go back to default action
            self.sorry = Sorry(m18n('You cannot say %1', answer.i18nName))
            return
        self.timer.stop()
        self.answered = True
        if self.sorry:
            self.sorry.cancel()
        self.sorry = None
        Internal.scene.clientDialog = None
        self.deferred.callback(answer)

    def selectedAnswer(self, dummyChecked):
        """the user clicked one of the buttons"""
        game = self.client.game
        if game and not game.autoPlay:
            self.selectButton(self.sender())
Exemple #10
0
class ClientDialog(QDialog):

    """a simple popup dialog for asking the player what he wants to do"""

    def __init__(self, client, parent=None):
        QDialog.__init__(self, parent)
        decorateWindow(self, i18n('Choose'))
        self.setObjectName('ClientDialog')
        self.client = client
        self.layout = QGridLayout(self)
        self.progressBar = QProgressBar()
        self.progressBar.setMinimumHeight(25)
        self.timer = QTimer()
        if not client.game.autoPlay:
            self.timer.timeout.connect(self.timeout)
        self.deferred = None
        self.buttons = []
        self.setWindowFlags(Qt.SubWindow | Qt.WindowStaysOnTopHint)
        self.setModal(False)
        self.btnHeight = 0
        self.answered = False
        self.move = None
        self.sorry = None

    def keyPressEvent(self, event):
        """ESC selects default answer"""
        if not self.client.game or self.client.game.autoPlay:
            return
        if event.key() in [Qt.Key_Escape, Qt.Key_Space]:
            self.selectButton()
            event.accept()
        else:
            for btn in self.buttons:
                if str(event.text()).upper() == btn.message.shortcut:
                    self.selectButton(btn)
                    event.accept()
                    return
            QDialog.keyPressEvent(self, event)

    def __declareButton(self, message):
        """define a button"""
        maySay = self.client.game.myself.sayable[message]
        if Internal.Preferences.showOnlyPossibleActions and not maySay:
            return
        btn = DlgButton(message, self)
        btn.setAutoDefault(True)
        btn.clicked.connect(self.selectedAnswer)
        self.buttons.append(btn)

    def focusTileChanged(self):
        """update icon and tooltip for the discard button"""
        if not self.client.game:
            return
        for button in self.buttons:
            button.setMeaning(self.client.game.myself.handBoard.focusTile)
        for uiTile in self.client.game.myself.handBoard.lowerHalfTiles():
            txt = []
            for button in self.buttons:
                _, _, tileTxt = button.message.toolTip(button, uiTile.tile)
                if tileTxt:
                    txt.append(tileTxt)
            uiTile.setToolTip('<br><br>'.join(txt))
        if self.client.game.activePlayer == self.client.game.myself:
            Internal.scene.handSelectorChanged(
                self.client.game.myself.handBoard)

    def checkTiles(self):
        """does the logical state match the displayed tiles?"""
        for player in self.client.game.players:
            player.handBoard.checkTiles()

    def messages(self):
        """a list of all messages returned by the declared buttons"""
        return list(x.message for x in self.buttons)

    def proposeAction(self):
        """either intelligently or first button by default. May also
        focus a proposed tile depending on the action."""
        result = self.buttons[0]
        game = self.client.game
        if game.autoPlay or Internal.Preferences.propose:
            answer, parameter = game.myself.intelligence.selectAnswer(
                self.messages())
            result = [x for x in self.buttons if x.message == answer][0]
            result.setFocus()
            if answer in [Message.Discard, Message.OriginalCall]:
                for uiTile in game.myself.handBoard.uiTiles:
                    if uiTile.tile is parameter:
                        game.myself.handBoard.focusTile = uiTile
        return result

    def askHuman(self, move, answers, deferred):
        """make buttons specified by answers visible. The first answer is default.
        The default button only appears with blue border when this dialog has
        focus but we always want it to be recognizable. Hence setBackgroundRole."""
        self.move = move
        self.deferred = deferred
        for answer in answers:
            self.__declareButton(answer)
        self.focusTileChanged()
        self.show()
        self.checkTiles()
        game = self.client.game
        myTurn = game.activePlayer == game.myself
        prefButton = self.proposeAction()
        if game.autoPlay:
            self.selectButton(prefButton)
            return
        prefButton.setFocus()

        self.progressBar.setVisible(not myTurn)
        if not myTurn:
            msecs = 50
            self.progressBar.setMinimum(0)
            self.progressBar.setMaximum(
                game.ruleset.claimTimeout * 1000 // msecs)
            self.progressBar.reset()
            self.timer.start(msecs)

    def placeInField(self):
        """place the dialog at bottom or to the right depending on space."""
        mainWindow = Internal.scene.mainWindow
        cwi = mainWindow.centralWidget()
        view = mainWindow.centralView
        geometry = self.geometry()
        if not self.btnHeight:
            self.btnHeight = self.buttons[0].height()
        vertical = view.width() > view.height() * 1.2
        if vertical:
            height = (len(self.buttons) + 1) * self.btnHeight * 1.2
            width = (cwi.width() - cwi.height()) // 2
            geometry.setX(cwi.width() - width)
            geometry.setY(min(cwi.height() // 3, cwi.height() - height))
        else:
            handBoard = self.client.game.myself.handBoard
            if not handBoard:
                # we are in the progress of logging out
                return
            hbLeftTop = view.mapFromScene(
                handBoard.mapToScene(handBoard.rect().topLeft()))
            hbRightBottom = view.mapFromScene(
                handBoard.mapToScene(handBoard.rect().bottomRight()))
            width = hbRightBottom.x() - hbLeftTop.x()
            height = self.btnHeight
            geometry.setY(cwi.height() - height)
            geometry.setX(hbLeftTop.x())
        for idx, btn in enumerate(self.buttons + [self.progressBar]):
            self.layout.addWidget(
                btn,
                idx +
                1 if vertical else 0,
                idx +
                1 if not vertical else 0)
        idx = len(self.buttons) + 2
        spacer = QSpacerItem(
            20,
            20,
            QSizePolicy.Expanding,
            QSizePolicy.Expanding)
        self.layout.addItem(
            spacer,
            idx if vertical else 0,
            idx if not vertical else 0)

        geometry.setWidth(width)
        geometry.setHeight(height)
        self.setGeometry(geometry)

    def showEvent(self, dummyEvent):
        """try to place the dialog such that it does not cover interesting information"""
        self.placeInField()

    def timeout(self):
        """the progressboard wants an update"""
        pBar = self.progressBar
        if isAlive(pBar):
            pBar.setValue(pBar.value() + 1)
            pBar.setVisible(True)
            if pBar.value() == pBar.maximum():
                # timeout: we always return the original default answer, not
                # the one with focus
                self.selectButton()
                pBar.setVisible(False)

    def selectButton(self, button=None):
        """select default answer. button may also be of type Message."""
        if self.answered:
            # sometimes we get this event twice
            return
        if button is None:
            button = self.focusWidget()
        if isinstance(button, Message):
            assert any(x.message == button for x in self.buttons)
            answer = button
        else:
            answer = button.message
        if not self.client.game.myself.sayable[answer]:
            self.proposeAction().setFocus() # go back to default action
            self.sorry = Sorry(i18n('You cannot say %1', answer.i18nName))
            return
        self.timer.stop()
        self.answered = True
        if self.sorry:
            self.sorry.cancel()
        self.sorry = None
        Internal.scene.clientDialog = None
        self.deferred.callback(answer)

    def selectedAnswer(self, dummyChecked):
        """the user clicked one of the buttons"""
        game = self.client.game
        if game and not game.autoPlay:
            self.selectButton(self.sender())