Example #1
0
def decorateWindow(window, name=None):
    """standard Kajongg window title and icon"""
    if name:
        window.setWindowTitle('{} – {}'.format(name, i18n('Kajongg')))
    else:
        window.setWindowTitle(i18n('Kajongg'))
    window.setWindowIcon(KIcon('kajongg'))
Example #2
0
 def showMoveHelper(self, visible=None):
     """show help text In empty HandBoards"""
     if visible is None:
         visible = not self.uiTiles
     if self.__moveHelper and not isAlive(self.__moveHelper):
         return
     if visible:
         if not self.__moveHelper:
             splitter = QGraphicsRectItem(self)
             hbCenter = self.rect().center()
             splitter.setRect(hbCenter.x() * 0.5, hbCenter.y(),
                              hbCenter.x() * 1, 1)
             helpItems = [splitter]
             for name, yFactor in [(i18n('Move Exposed Tiles Here'), 0.5),
                                   (i18n('Move Concealed Tiles Here'), 1.5)
                                   ]:
                 helper = QGraphicsSimpleTextItem(name, self)
                 helper.setScale(3)
                 nameRect = QRectF()
                 nameRect.setSize(
                     helper.mapToParent(
                         helper.boundingRect()).boundingRect().size())
                 center = QPointF(hbCenter)
                 center.setY(center.y() * yFactor)
                 helper.setPos(center - nameRect.center())
                 if sceneRotation(self) == 180:
                     rotateCenter(helper, 180)
                 helpItems.append(helper)
             self.__moveHelper = self.scene().createItemGroup(helpItems)
         self.__moveHelper.setVisible(True)
     else:
         if self.__moveHelper:
             self.__moveHelper.setVisible(False)
Example #3
0
 def __init__(self, swappers):
     QMessageBox.__init__(self)
     decorateWindow(self, i18n("Swap Seats"))
     self.setText(
         i18n("By the rules, %1 and %2 should now exchange their seats. ",
              swappers[0].name, swappers[1].name))
     self.yesAnswer = QPushButton(i18n("&Exchange"))
     self.addButton(self.yesAnswer, QMessageBox.YesRole)
     self.noAnswer = QPushButton(i18n("&Keep seat"))
     self.addButton(self.noAnswer, QMessageBox.NoRole)
Example #4
0
 def __computeMd5sum(self):
     """update md5sum file. If it changed, return True.
     If unchanged or no ogg files exist, remove archive and md5sum and return False.
     If ogg files exist but no archive, return True."""
     if self.__md5sum:
         # we already checked
         return
     md5FileName = os.path.join(self.directory, 'md5sum')
     archiveExists = os.path.exists(self.archiveName())
     ogg = self.oggFiles()
     if not ogg:
         removeIfExists(self.archiveName())
         removeIfExists(md5FileName)
         self.__md5sum = None
         logDebug('no ogg files in %s' % self)
         return
     md5sum = md5()
     for oggFile in ogg:
         md5sum.update(
             open(os.path.join(self.directory, oggFile), 'rb').read())
     # the md5 stamp goes into the old archive directory 'username'
     self.__md5sum = md5sum.hexdigest()
     existingMd5sum = self.savedmd5Sum()
     md5Name = self.md5FileName()
     if self.__md5sum != existingMd5sum:
         if Debug.sound:
             if not os.path.exists(md5Name):
                 logDebug('creating new %s' % md5Name)
             else:
                 logDebug('md5sum %s changed, rewriting %s with %s' %
                          (existingMd5sum, md5Name, self.__md5sum))
         try:
             open(md5Name, 'w').write('%s\n' % self.__md5sum)
         except BaseException as exception:
             logException('\n'.join([
                 i18n('cannot write <filename>%1</filename>: %2', md5Name,
                      str(exception)),
                 i18n(
                     'The voice files have changed, their checksum has changed.'
                 ),
                 i18n(
                     'Please reinstall kajongg or do, with sufficient permissions:'
                 ), 'cd {} ; cat *.ogg | md5sum > md5sum'.format(
                     self.directory)
             ]))
     if archiveExists:
         archiveIsOlder = os.path.getmtime(md5Name) > os.path.getmtime(
             self.archiveName())
         if self.__md5sum != existingMd5sum or archiveIsOlder:
             os.remove(self.archiveName())
Example #5
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()
Example #6
0
 def _saveScores(self):
     """save computed values to database,
     update score table and balance in status line"""
     scoretime = datetime.datetime.now().replace(microsecond=0).isoformat()
     logMessage = ''
     for player in self.players:
         if player.hand:
             manualrules = '||'.join(x.rule.name
                                     for x in player.hand.usedRules)
         else:
             manualrules = i18n('Score computed manually')
         Query(
             "INSERT INTO SCORE "
             "(game,hand,data,manualrules,player,scoretime,won,prevailing,"
             "wind,points,payments, balance,rotated,notrotated) "
             "VALUES(%d,%d,?,?,%d,'%s',%d,'%s','%s',%d,%d,%d,%d,%d)" %
             (self.gameid, self.handctr, player.nameid, scoretime,
              int(player == self.__winner), self.roundWind.char,
              player.wind, player.handTotal, player.payment, player.balance,
              self.rotated, self.notRotated),
             (player.hand.string, manualrules))
         logMessage += '{player:<12} {hand:>4} {total:>5} {won} | '.format(
             player=str(player)[:12],
             hand=player.handTotal,
             total=player.balance,
             won='WON' if player == self.winner else '   ')
         for usedRule in player.hand.usedRules:
             rule = usedRule.rule
             if rule.score.limits:
                 self.addCsvTag(rule.name.replace(' ', ''))
     if Debug.scores:
         self.debug(logMessage)
Example #7
0
 def remote_tableRemoved(self, tableid, message, *args):
     """update table list"""
     Client.remote_tableRemoved(self, tableid, message, *args)
     self.__updateTableList()
     if message:
         if self.name not in args or not message.endswith('has logged out'):
             logWarning(i18n(message, *args))
Example #8
0
 def pixmap(self, size):
     """returns a background pixmap or None for isPlain"""
     self.__pmap = QBrush()
     if not self.isPlain:
         width = size.width()
         height = size.height()
         if self.tiled:
             width = self.imageWidth
             height = self.imageHeight
         cachekey = '{name}W{width}H{height}'.format(name=self.name,
                                                     width=width,
                                                     height=height)
         self.__pmap = QPixmapCache.find(cachekey)
         if not self.__pmap:
             renderer = QSvgRenderer(self.graphicsPath)
             if not renderer.isValid():
                 logException(
                     i18n(
                         'file <filename>%1</filename> contains no valid SVG',
                         self.graphicsPath))
             self.__pmap = QPixmap(width, height)
             self.__pmap.fill(Qt.transparent)
             painter = QPainter(self.__pmap)
             renderer.render(painter)
             QPixmapCache.insert(cachekey, self.__pmap)
     return self.__pmap
Example #9
0
 def __getName(playerid):
     """get name for playerid
     """
     try:
         return Players.allNames[playerid]
     except KeyError:
         return i18n('Player %1 not known', playerid)
Example #10
0
    def __init__(self):
        SelectRuleset.__init__(self)
        Players.load()
        decorateWindow(self, i18n('Select four players'))
        self.names = None
        self.nameWidgets = []
        for idx, wind in enumerate(Wind.all4):
            cbName = QComboBox()
            cbName.manualSelect = False
            # increase width, we want to see the full window title
            cbName.setMinimumWidth(350)  # is this good for all platforms?
            cbName.addItems(list(Players.humanNames.values()))
            self.grid.addWidget(cbName, idx + 1, 1)
            self.nameWidgets.append(cbName)
            self.grid.addWidget(WindLabel(wind), idx + 1, 0)
            cbName.currentIndexChanged.connect(self.slotValidate)

        query = Query(
            "select p0,p1,p2,p3 from game where seed is null and game.id = (select max(id) from game)"
        )
        if len(query.records):
            with BlockSignals(self.nameWidgets):
                for cbName, playerId in zip(self.nameWidgets,
                                            query.records[0]):
                    try:
                        playerName = Players.humanNames[playerId]
                        playerIdx = cbName.findText(playerName)
                        if playerIdx >= 0:
                            cbName.setCurrentIndex(playerIdx)
                    except KeyError:
                        logError(
                            'database is inconsistent: player with id %d is in game but not in player'
                            % playerId)
        self.slotValidate()
Example #11
0
 def headerData(self, section, orientation, role=Qt.DisplayRole):  # pylint: disable=no-self-use
     """show header"""
     if role == Qt.TextAlignmentRole:
         if orientation == Qt.Horizontal:
             if section == 1:
                 return int(Qt.AlignRight)
             else:
                 return int(Qt.AlignLeft)
     if orientation != Qt.Horizontal:
         return int(section + 1)
     if role != Qt.DisplayRole:
         return
     result = ''
     if section < self.columnCount():
         result = [i18n('Time'), i18n('Player'), i18n('Message')][section]
     return result
Example #12
0
 def __init__(self, scene=None, table=None):
     super(ChatWindow, self).__init__(None)
     self.scene = scene
     self.table = table or scene.game.client.table
     self.table.chatWindow = self
     self.setObjectName('chatWindow')
     title = i18n('Chat on table %1 at %2', self.table.tableid,
                  self.table.client.connection.url)
     decorateWindow(self, title)
     self.messageView = ChatView()
     self.messageView.setModel(ChatModel())
     self.messageView.setFocusPolicy(Qt.NoFocus)
     self.messageView.setShowGrid(False)
     self.messageView.setWordWrap(False)
     self.messageView.setSelectionMode(QAbstractItemView.NoSelection)
     if Debug.modelTest:
         self.debugModelTest = ModelTest(self.messageView.model(),
                                         self.messageView)
     self.edit = QLineEdit()
     layout = QVBoxLayout()
     layout.addWidget(self.messageView)
     layout.addWidget(self.edit)
     self.setLayout(layout)
     self.edit.returnPressed.connect(self.sendLine)
     self.edit.setFocus()
     self.show()
     StateSaver(self)
Example #13
0
 def remote_abort(self, tableid, message: str, *args):
     """the server aborted this game"""
     if self.table and self.table.tableid == tableid:
         # translate Robot to Roboter:
         if self.game:
             args = self.game.players.translatePlayerNames(args)
         logWarning(i18n(message, *args))
         if self.game:
             self.game.close()
Example #14
0
 def remote_gameOver(self, tableid, message, *args):
     """the game is over"""
     def yes(dummy):
         """now that the user clicked the 'game over' prompt away, clean up"""
         if self.game:
             self.game.rotateWinds()
             self.game.close().addCallback(Internal.mainWindow.close)
     assert self.table and self.table.tableid == tableid
     if Internal.scene:
         # update the balances in the status bar:
         Internal.scene.mainWindow.updateGUI()
     Information(i18n(message, *args)).addCallback(yes)
Example #15
0
    def readyForGameStart(
            self, tableid, gameid, wantedGame, playerNames, shouldSave=True,
            gameClass=None):
        """playerNames are in wind order ESWN"""
        if gameClass is None:
            if Options.gui:
                gameClass = VisiblePlayingGame
            else:
                gameClass = PlayingGame

        def clientReady():
            """macro"""
            return Client.readyForGameStart(
                self, tableid, gameid, wantedGame, playerNames,
                shouldSave, gameClass)

        def answered(result):
            """callback, called after the client player said yes or no"""
            self.beginQuestion = None
            if self.connection and result:
                # still connected and yes, we are
                return clientReady()
            else:
                return Message.NoGameStart

        def cancelled(dummy):
            """the user does not want to start now. Back to table list"""
            if Debug.table:
                logDebug('%s: Readyforgamestart returns Message.NoGameStart for table %s' % (
                    self.name, self._tableById(tableid)))
            self.table = None
            self.beginQuestion = None
            if self.tableList:
                self.__updateTableList()
                self.tableList.show()
            return Message.NoGameStart
        if sum(not x[1].startswith('Robot ') for x in playerNames) == 1:
            # we play against 3 robots and we already told the server to start:
            # no need to ask again
            return clientReady()
        assert not self.table
        assert self.tables
        self.table = self._tableById(tableid)
        if not self.table:
            raise pb.Error(
                'client.readyForGameStart: tableid %d unknown' %
                tableid)
        msg = i18n(
            "The game on table <numid>%1</numid> can begin. Are you ready to play now?",
            tableid)
        self.beginQuestion = QuestionYesNo(msg, modal=False, caption=self.name).addCallback(
            answered).addErrback(cancelled)
        return self.beginQuestion
Example #16
0
 def readyForHandStart(self, playerNames, rotateWinds):
     """playerNames are in wind order ESWN. Never called for first hand."""
     def answered(dummy=None):
         """called after the client player said yes, I am ready"""
         if self.connection:
             return Client.readyForHandStart(self, playerNames, rotateWinds)
     if not self.connection:
         # disconnected meanwhile
         return
     if Options.gui:
         # update the balances in the status bar:
         Internal.mainWindow.updateGUI()
     assert not self.game.isFirstHand()
     return Information(i18n("Ready for next hand?"), modal=False).addCallback(answered)
Example #17
0
 def _endWallDangerous(self):
     """if end of living wall is reached, declare all invisible tiles
     as dangerous"""
     if len(self.wall.living) <= 5:
         allTiles = [
             x for x in defaultdict.keys(elements.occurrence)
             if not x.isBonus
         ]
         for tile in allTiles:
             assert isinstance(tile, Tile), tile
         # see https://www.logilab.org/ticket/23986
         invisibleTiles = set(x for x in allTiles
                              if x not in self.visibleTiles)
         msg = i18n('Short living wall: Tile is invisible, hence dangerous')
         self.dangerousTiles = list(x for x in self.dangerousTiles
                                    if x[1] != msg)
         self.dangerousTiles.append((invisibleTiles, msg))
Example #18
0
 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
Example #19
0
 def __init__(self, kongs, deferred):
     KDialogIgnoringEscape.__init__(self)
     decorateWindow(self)
     self.setButtons(0)
     self.kongs = kongs
     self.selectedKong = None
     self.deferred = deferred
     layout = QVBoxLayout()
     label = QLabel(i18n('Which kong do you want to declare?'))
     layout.addWidget(label)
     layout.setAlignment(label, Qt.AlignHCenter)
     self.buttons = []
     for kong in kongs:
         button = QRadioButton((kong[0].name()), self)
         self.buttons.append(button)
         layout.addWidget(button)
         button.toggled.connect(self.toggled)
     widget = QWidget(self)
     widget.setLayout(layout)
     self.setMainWidget(widget)
Example #20
0
def scoreGame():
    """show all games, select an existing game or create a new game"""
    Players.load()
    if len(Players.humanNames) < 4:
        logWarning(
            i18n(
                'Please define four players in <interface>Settings|Players</interface>'
            ))
        return
    gameSelector = Games(Internal.mainWindow)
    selected = None
    if not gameSelector.exec_():
        return
    selected = gameSelector.selectedGame
    gameSelector.close()
    if selected is not None:
        return ScoringGame.loadFromDB(selected)
    else:
        selectDialog = SelectPlayers()
        if not selectDialog.exec_():
            return
        return ScoringGame(list(zip(Wind.all4, selectDialog.names)),
                           selectDialog.cbRuleset.current)
Example #21
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)
Example #22
0
 def findOggBinary():
     """sets __oggBinary to exe name or an empty string"""
     if Sound.__oggBinary is None:
         if os.name == 'nt':
             Sound.__oggBinary = os.path.join('share', 'kajongg', 'voices',
                                              'oggdec.exe')
             msg = ''  # we bundle oggdec.exe with the kajongg installer, it must be there
         else:
             oggBinary = 'ogg123'
             msg = i18n(
                 'No voices will be heard because the program %1 is missing',
                 oggBinary)
             if which(oggBinary):
                 Sound.__oggBinary = oggBinary
             else:
                 Sound.__oggBinary = ''
                 Internal.Preferences.useSounds = False
                 # checks again at next reenable
                 if msg:
                     logWarning(msg)
         if Debug.sound:
             logDebug('ogg123 found:' + Sound.__oggBinary)
     return Sound.__oggBinary
Example #23
0
 def data(self, index, role=Qt.DisplayRole):
     """score table"""
     result = None
     if role == Qt.TextAlignmentRole:
         if index.column() == 1:
             return int(Qt.AlignRight)
         else:
             return int(Qt.AlignLeft)
     if index.isValid() and (0 <= index.row() < len(self.chatLines)):
         chatLine = self.chatLines[index.row()]
         if role == Qt.DisplayRole and index.column() == 0:
             local = chatLine.localtimestamp()
             result = '%02d:%02d:%02d' % (local.hour, local.minute,
                                          local.second)
         elif role == Qt.DisplayRole and index.column() == 1:
             result = chatLine.fromUser
         elif role == Qt.DisplayRole and index.column() == 2:
             result = i18n(chatLine.message)
         elif role == Qt.ForegroundRole and index.column() == 2:
             palette = Internal.app.palette()  # pylint: disable=no-member
             color = 'blue' if chatLine.isStatusMessage else palette.windowText(
             )
             result = QColor(color)
     return result
Example #24
0
 def __init__(self, chows, propose, deferred):
     KDialogIgnoringEscape.__init__(self)
     decorateWindow(self)
     self.setButtons(KDialog.NoButton)
     self.chows = chows
     self.selectedChow = None
     self.deferred = deferred
     layout = QVBoxLayout()
     label = QLabel(i18n('Which chow do you want to expose?'))
     layout.addWidget(label)
     layout.setAlignment(label, Qt.AlignHCenter)
     self.buttons = []
     for chow in chows:
         button = QRadioButton('{}-{}-{}'.format(*(x.value for x in chow)))
         self.buttons.append(button)
         layout.addWidget(button)
         layout.setAlignment(button, Qt.AlignHCenter)
         button.toggled.connect(self.toggled)
     widget = QWidget(self)
     widget.setLayout(layout)
     self.setMainWidget(widget)
     for idx, chow in enumerate(chows):
         if chow == propose:
             self.buttons[idx].setFocus()
Example #25
0
    def callServer(self, *args):
        """if we are online, call server"""
        if self.connection:
            if args[0] is None:
                args = args[1:]
            try:
                if Debug.traffic:
                    self.__logCallServer(*args)

                def callServerError(result):
                    """if serverDisconnected has been called meanwhile, just ignore msg about
                    connection lost in a non-clean fashion"""
                    if self.connection:
                        return result
                return self.connection.perspective.callRemote(*args).addErrback(callServerError)
            except pb.DeadReferenceError:
                logWarning(
                    i18n(
                        'The connection to the server %1 broke, please try again later.',
                        self.connection.url))
                self.remote_serverDisconnects()
                return succeed(None)
        else:
            return succeed(None)
Example #26
0
 def items(self, items):
     """combo box items"""
     self.clear()
     if items:
         for item in items:
             self.addItem(i18n(item.name), item)