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 __init__(self): """self.servers is a list of tuples containing server and last playername""" QDialog.__init__(self, None) self.setWindowTitle(m18n('Login') + ' - Kajongg') self.setupUi() localName = m18nc('kajongg name for local game server', Query.localServerName) self.servers = Query('select url,lastname from server order by lasttime desc').records servers = [m18nc('kajongg name for local game server', x[0]) for x in self.servers] # the first server combobox item should be default: either the last used server # or localName for autoPlay if localName not in servers: servers.append(localName) if 'kajongg.org' not in servers: servers.append('kajongg.org') demoHost = Options.host or localName if demoHost in servers: servers.remove(demoHost) # we want a unique list, it will be re-used for all following games servers.insert(0, demoHost) # in this process but they will not be autoPlay self.cbServer.addItems(servers) self.passwords = Query('select url, p.name, passwords.password from passwords, player p ' 'where passwords.player=p.id').records Players.load() self.cbServer.editTextChanged.connect(self.serverChanged) self.cbUser.editTextChanged.connect(self.userChanged) self.serverChanged() StateSaver(self)
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 contentStr(self): """make score readable for humans, i18n""" parts = [] if self.points: parts.append(m18nc('Kajongg', '%1 points', self.points)) if self.doubles: parts.append(m18nc('Kajongg', '%1 doubles', self.doubles)) if self.limits: parts.append(m18nc('Kajongg', '%1 limits', self.limits)) return ' '.join(parts)
def status(self): """a status string""" result = '' if self.suspendedAt: result = m18nc('table status', 'Suspended') result += ' ' + datetime.datetime.strptime( self.suspendedAt, '%Y-%m-%dT%H:%M:%S').strftime('%c').decode('utf-8') if self.running: result += ' ' + m18nc('table status', 'Running') return result or m18nc('table status', 'New')
def __init__(self, parent=None): super(Games, self).__init__(parent) self.selectedGame = None self.onlyPending = True self.setWindowTitle(m18nc('kajongg', 'Games') + ' - Kajongg') self.setObjectName('Games') self.resize(700, 400) self.model = GamesModel(self) 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 __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 setupUi(self): """create all Ui elements but do not fill them""" buttonBox = KDialogButtonBox(self) buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok) # Ubuntu 11.10 unity is a bit strange - without this, it sets focus on # the cancel button (which it shows on the left). I found no obvious # way to use setDefault and setAutoDefault for fixing this. buttonBox.button(QDialogButtonBox.Ok).setFocus(True) buttonBox.accepted.connect(self.accept) buttonBox.rejected.connect(self.reject) vbox = QVBoxLayout(self) self.grid = QFormLayout() self.cbServer = QComboBox() self.cbServer.setEditable(True) self.grid.addRow(m18n('Game server:'), self.cbServer) self.cbUser = QComboBox() self.cbUser.setEditable(True) self.grid.addRow(m18n('Username:'******'Password:'******'kajongg', 'Ruleset:'), self.cbRuleset) vbox.addLayout(self.grid) vbox.addWidget(buttonBox) pol = QSizePolicy() pol.setHorizontalPolicy(QSizePolicy.Expanding) self.cbUser.setSizePolicy(pol)
def setupUi(self): """create all Ui elements but do not fill them""" buttonBox = KDialogButtonBox(self) buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) # Ubuntu 11.10 unity is a bit strange - without this, it sets focus on # the cancel button (which it shows on the left). I found no obvious # way to use setDefault and setAutoDefault for fixing this. buttonBox.button(QDialogButtonBox.Ok).setFocus(True) buttonBox.accepted.connect(self.accept) buttonBox.rejected.connect(self.reject) vbox = QVBoxLayout(self) self.grid = QFormLayout() self.cbServer = QComboBox() self.cbServer.setEditable(True) self.grid.addRow(m18n('Game server:'), self.cbServer) self.cbUser = QComboBox() self.cbUser.setEditable(True) self.grid.addRow(m18n('Username:'******'Password:'******'kajongg', 'Ruleset:'), self.cbRuleset) vbox.addLayout(self.grid) vbox.addWidget(buttonBox) pol = QSizePolicy() pol.setHorizontalPolicy(QSizePolicy.Expanding) self.cbUser.setSizePolicy(pol)
def __navigateScoringGame(self, event): """keyboard navigation in a scoring game""" mod = event.modifiers() key = event.key() wind = chr(key % 128) moveCommands = m18nc('kajongg:keyboard commands for moving tiles to the players ' \ 'with wind ESWN or to the central tile selector (X)', 'ESWNX') tile = self.centralScene.focusItem().tile if wind in moveCommands: # translate i18n wind key to ESWN: wind = 'ESWNX'[moveCommands.index(wind)] self.__moveTile(tile, wind, mod & Qt.ShiftModifier) return True if key == Qt.Key_Tab and self.game: tabItems = [self.selectorBoard] tabItems.extend( list(p.handBoard for p in self.game.players if p.handBoard.tiles)) tabItems.append(tabItems[0]) currentBoard = tile.board if isinstance(tile, Tile) else None currIdx = 0 while tabItems[currIdx] != currentBoard and currIdx < len( tabItems) - 2: currIdx += 1 tabItems[currIdx + 1].hasFocus = True return True
def validateParameter(self): """check for validity""" if 'min' in self.options: minValue = self.parType(self.options['min']) if self.parameter < minValue: return m18nc('wrong value for rule', '%1: %2 is too small, minimal value is %3', m18n(self.name), self.parameter, minValue)
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 setupUi(self): """layout the window""" self.setWindowTitle(m18n('Customize rulesets') + ' - Kajongg') self.setObjectName('Rulesets') hlayout = QHBoxLayout(self) v1layout = QVBoxLayout() self.v1widget = QWidget() v1layout = QVBoxLayout(self.v1widget) v2layout = QVBoxLayout() hlayout.addWidget(self.v1widget) hlayout.addLayout(v2layout) for widget in [self.v1widget, hlayout, v1layout, v2layout]: widget.setContentsMargins(0, 0, 0, 0) hlayout.setStretchFactor(self.v1widget, 10) self.btnCopy = QPushButton() self.btnRemove = QPushButton() self.btnCompare = QPushButton() self.btnClose = QPushButton() self.rulesetView = RuleTreeView(m18nc('kajongg','Rule'), self.btnCopy, self.btnRemove, self.btnCompare) v1layout.addWidget(self.rulesetView) self.rulesetView.setWordWrap(True) self.rulesetView.setMouseTracking(True) spacerItem = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) v2layout.addWidget(self.btnCopy) v2layout.addWidget(self.btnRemove) v2layout.addWidget(self.btnCompare) self.btnCopy.clicked.connect(self.rulesetView.copyRow) self.btnRemove.clicked.connect(self.rulesetView.removeRow) self.btnCompare.clicked.connect(self.rulesetView.compareRow) self.btnClose.clicked.connect(self.hide) v2layout.addItem(spacerItem) v2layout.addWidget(self.btnClose) self.retranslateUi() StateSaver(self) self.show()
def popupMsg(self, msg): """shows a yellow message from player""" if msg != Message.NoClaim: self.speak(msg.name.lower()) yellow = self.front.message yellow.setText(' '.join([unicode(yellow.msg), m18nc('kajongg', msg.name)])) yellow.setVisible(True)
def formattedDiffs(self): """a list of tuples with 3 values: name, left, right""" formatted = [] leftRuleset = self.cbRuleset1.current rightRuleset = self.cbRuleset2.current assert rightRuleset, self.cbRuleset2.count() leftRuleset.load() rightRuleset.load() for rule1, rule2 in leftRuleset.diff(rightRuleset): name = m18n(rule1.name if rule1 else rule2.name) left = rule1.contentStr() if rule1 else m18nc("Kajongg-Rule", "not defined") right = rule2.contentStr() if rule2 else m18nc("Kajongg-Rule", "not defined") formatted.append((name, left, right)) if rule1 and rule2 and rule1.definition != rule2.definition: formatted.append(("", rule1.definition, rule2.definition)) return formatted
def setupUi(self): """setup UI elements""" self.viewLeft = ScoreViewLeft(self) self.viewRight = ScoreViewRight(self) self.viewRight.setHorizontalScrollBar(HorizontalScrollBar(self)) self.viewRight.setHorizontalScrollMode(QAbstractItemView.ScrollPerItem) self.viewRight.setFocusPolicy(Qt.NoFocus) self.viewRight.header().setClickable(False) self.viewRight.header().setMovable(False) self.viewRight.setSelectionMode(QAbstractItemView.NoSelection) windowLayout = QVBoxLayout(self) self.splitter = QSplitter(Qt.Vertical) self.splitter.setObjectName('ScoreTableSplitter') windowLayout.addWidget(self.splitter) scoreWidget = QWidget() self.scoreLayout = QHBoxLayout(scoreWidget) leftLayout = QVBoxLayout() leftLayout.addWidget(self.viewLeft) self.leftLayout = leftLayout self.scoreLayout.addLayout(leftLayout) self.scoreLayout.addWidget(self.viewRight) self.splitter.addWidget(scoreWidget) self.ruleTree = RuleTreeView(m18nc('kajongg','Used Rules')) self.splitter.addWidget(self.ruleTree) # this shows just one line for the ruleTree - so we just see the # name of the ruleset: self.splitter.setSizes(list([1000, 1]))
def validateDefinition(self, prevDefinition): """check for validity. If wrong, restore prevDefinition.""" payers = int(self.options.get('payers', 1)) payees = int(self.options.get('payees', 1)) if not 2 <= payers + payees <= 4: self.definition = prevDefinition logException(m18nc('%1 can be a sentence', '%4 have impossible values %2/%3 in rule "%1"', self.name, payers, payees, 'payers/payees'))
def popupMsg(self, msg): """shows a yellow message from player""" if msg != Message.NoClaim: self.speak(msg.name.lower()) yellow = self.front.message yellow.setText(' '.join( [unicode(yellow.msg), m18nc('kajongg', msg.name)])) yellow.setVisible(True)
def formattedDiffs(self): """a list of tuples with 3 values: name, left, right""" formatted = [] leftRuleset = self.cbRuleset1.current rightRuleset = self.cbRuleset2.current assert rightRuleset, self.cbRuleset2.count() leftRuleset.load() rightRuleset.load() for rule1, rule2 in leftRuleset.diff(rightRuleset): name = m18n(rule1.name if rule1 else rule2.name) left = rule1.contentStr() if rule1 else m18nc( 'Kajongg-Rule', 'not defined') right = rule2.contentStr() if rule2 else m18nc( 'Kajongg-Rule', 'not defined') formatted.append((name, left, right)) if rule1 and rule2 and rule1.definition != rule2.definition: formatted.append(('', rule1.definition, rule2.definition)) return formatted
def validateDefinition(self, prevDefinition): """check for validity. If wrong, restore prevDefinition.""" payers = int(self.options.get('payers', 1)) payees = int(self.options.get('payees', 1)) if not 2 <= payers + payees <= 4: self.definition = prevDefinition logException( m18nc('%1 can be a sentence', '%4 have impossible values %2/%3 in rule "%1"', self.name, payers, payees, 'payers/payees'))
def mapChar2Arrow(event): """maps the keys hjkl to arrows like in vi and konqueror""" key = event.key() if key in Board.arrows: return key charArrows = m18nc('kajongg:arrow keys hjkl like in konqueror', 'hjklHJKL') key = unicode(event.text()) if key and key in charArrows: key = Board.arrows[charArrows.index(key) % 4] return key
def __init__(self, parent, name): super(ConfigDialog, self).__init__(parent, QString(name), Preferences) self.pages = [ self.addPage(PlayConfigTab(self), m18nc('kajongg','Play'), "arrow-right"), self.addPage(TilesetSelector(self), m18n("Tiles"), "games-config-tiles"), self.addPage(BackgroundSelector(self), m18n("Backgrounds"), "games-config-background")] StateSaver(self)
def __init__(self, name): """name may be: - an integer: ruleset.id from the sql table - a list: the full ruleset specification (probably sent from the server) - a string: The hash value of a ruleset""" self.name = name self.rulesetId = 0 self.__hash = None self.allRules = [] self.__dirty = False # only the ruleset editor is supposed to make us dirty self.__loaded = False self.__filteredLists = {} self.description = None self.rawRules = None # used when we get the rules over the network self.splitRules = [] self.doublingMeldRules = [] self.doublingHandRules = [] self.meldRules = RuleList( 1, m18n('Meld Rules'), m18n( 'Meld rules are applied to single melds independent of the rest of the hand' )) self.handRules = RuleList( 2, m18n('Hand Rules'), m18n('Hand rules are applied to the entire hand, for all players')) self.winnerRules = RuleList( 3, m18n('Winner Rules'), m18n( 'Winner rules are applied to the entire hand but only for the winner' )) self.loserRules = RuleList( 33, m18n('Loser Rules'), m18n( 'Loser rules are applied to the entire hand but only for non-winners' )) self.mjRules = RuleList( 4, m18n('Mah Jongg Rules'), m18n('Only hands matching a Mah Jongg rule can win')) self.parameterRules = RuleList( 999, m18nc('kajongg', 'Options'), m18n('Here we have several special game related options')) self.penaltyRules = RuleList( 9999, m18n('Penalties'), m18n( """Penalties are applied manually by the user. They are only used for scoring games. When playing against the computer or over the net, Kajongg will never let you get into a situation where you have to pay a penalty""")) self.ruleLists = list([ self.meldRules, self.handRules, self.mjRules, self.winnerRules, self.loserRules, self.parameterRules, self.penaltyRules ]) # the order of ruleLists is the order in which the lists appear in the ruleset editor # if you ever want to remove an entry from ruleLists: make sure its listId is not reused or you get # in trouble when updating self._initRuleset()
def __init__(self, parent, name): super(ConfigDialog, self).__init__(parent, QString(name), Preferences) self.pages = [ self.addPage(PlayConfigTab(self), m18nc('kajongg', 'Play'), "arrow-right"), self.addPage(TilesetSelector(self), m18n("Tiles"), "games-config-tiles"), self.addPage(BackgroundSelector(self), m18n("Backgrounds"), "games-config-background") ] StateSaver(self)
def data(self, index, role=Qt.DisplayRole): """score table""" # pylint: disable=R0912,R0914 # pylint too many branches result = QVariant() if role == Qt.TextAlignmentRole: if index.column() == 0: result = QVariant(int(Qt.AlignHCenter | Qt.AlignVCenter)) else: result = QVariant(int(Qt.AlignLeft | Qt.AlignVCenter)) if index.isValid() and (0 <= index.row() < len(self.tables)): table = self.tables[index.row()] if role == Qt.DisplayRole and index.column() in (0, 1): result = QVariant(table.tableid) elif role == Qt.DisplayRole and index.column() == 2: players = [] zipped = zip(table.playerNames, table.playersOnline) for idx, pair in enumerate(zipped): name, online = pair[0], pair[1] if idx < len(zipped) - 1: name += ', ' palette = KApplication.palette() if online: color = palette.color(QPalette.Active, QPalette.WindowText).name() style = 'font-weight:normal;font-style:normal;color:%s' % color else: color = palette.color(QPalette.Disabled, QPalette.WindowText).name() style = 'font-weight:100;font-style:italic;color:%s' % color players.append('<nobr style="%s">' % style + name + '</nobr>') names = ''.join(players) result = QVariant(names) elif role == Qt.DisplayRole and index.column() == 3: status = table.status() if table.suspendedAt: dateVal = ' ' + datetime.datetime.strptime( table.suspendedAt, '%Y-%m-%dT%H:%M:%S').strftime('%c').decode('utf-8') status = 'Suspended' else: dateVal = '' result = QVariant(m18nc('table status', status) + dateVal) elif index.column() == 4: if role == Qt.DisplayRole: result = QVariant( m18n((table.myRuleset if table.myRuleset else table.ruleset).name)) elif role == Qt.ForegroundRole: palette = KApplication.palette() color = palette.windowText() if table.myRuleset else 'red' result = QVariant(QColor(color)) return result
def __init__(self, game): QWidget.__init__(self, None) self.game = None self.setWindowTitle(m18n('Scoring for this Hand') + ' - Kajongg') self.nameLabels = [None] * 4 self.spValues = [None] * 4 self.windLabels = [None] * 4 self.wonBoxes = [None] * 4 self.detailsLayout = [None] * 4 self.details = [None] * 4 self.__tilePixMaps = [] self.__meldPixMaps = [] grid = QGridLayout(self) pGrid = QGridLayout() grid.addLayout(pGrid, 0, 0, 2, 1) pGrid.addWidget(QLabel(m18nc('kajongg', "Player")), 0, 0) pGrid.addWidget(QLabel(m18nc('kajongg', "Wind")), 0, 1) pGrid.addWidget(QLabel(m18nc('kajongg', 'Score')), 0, 2) pGrid.addWidget(QLabel(m18n("Winner")), 0, 3) self.detailTabs = QTabWidget() self.detailTabs.setDocumentMode(True) pGrid.addWidget(self.detailTabs, 0, 4, 8, 1) for idx in range(4): self.setupUiForPlayer(pGrid, idx) self.draw = QCheckBox(m18nc('kajongg','Draw')) self.draw.clicked.connect(self.wonChanged) btnPenalties = QPushButton(m18n("&Penalties")) btnPenalties.clicked.connect(self.penalty) self.btnSave = QPushButton(m18n('&Save Hand')) self.btnSave.setEnabled(False) self.setupUILastTileMeld(pGrid) pGrid.setRowStretch(87, 10) pGrid.addWidget(self.draw, 7, 3) self.cbLastTile.currentIndexChanged.connect(self.slotLastTile) self.cbLastMeld.currentIndexChanged.connect(self.slotInputChanged) btnBox = QHBoxLayout() btnBox.addWidget(btnPenalties) btnBox.addWidget(self.btnSave) pGrid.addLayout(btnBox, 8, 4) StateSaver(self) self.refresh(game)
def penaltyChanged(self): """total has changed, update payments""" # normally value is only validated when leaving the field self.spPenalty.interpretText() offense = self.cbCrime.current penalty = self.spPenalty.value() payers = int(offense.options.get('payers', 1)) payees = int(offense.options.get('payees', 1)) payerAmount = -penalty // payers payeeAmount = penalty // payees for pList, amount, count in ((self.payers, payerAmount, payers), (self.payees, payeeAmount, payees)): for idx, player in enumerate(pList): player.setVisible(idx<count) player.lblPayment.setVisible(idx<count) if idx < count: if pList == self.payers: player.lblPayment.setText(m18nc('penalty dialog, appears behind paying player combobox', 'pays %1 points', -amount)) else: player.lblPayment.setText(m18nc('penalty dialog, appears behind profiting player combobox', 'gets %1 points', amount))
def _newKey(self, minus=False): """generate a new id and a new name if the name already exists""" newId = self.newId(minus=minus) newName = self.name if minus: copyNr = 1 while self.nameExists(newName): copyStr = ' ' + str(copyNr) if copyNr > 1 else '' newName = m18nc('Ruleset._newKey:%1 is empty or space plus number', 'Copy%1 of %2', copyStr, m18n(self.name)) copyNr += 1 return newId, newName
def loadData(self): """loads all data from the data base into a 2D matrix formatted like the wanted tree""" game = self.scoreTable.game data = [] records = Query( 'select player,rotated,notrotated,penalty,won,prevailing,wind,points,payments,balance,manualrules' ' from score where game=? order by player,hand', list([game.gameid])).records # pylint: disable=W0142 # pylint * magic data = list(tuple([player.localName, [HandResult(*x[1:]) for x in records \ if x[0] == player.nameid]]) for player in game.players) self.__findMinMaxChartPoints(data) parent = QModelIndex() groupIndex = self.index(self.rootItem.childCount(), 0, parent) groupNames = [m18nc('kajongg','Score'), m18nc('kajongg','Payments'), m18nc('kajongg','Balance'), m18nc('kajongg', 'Chart')] for idx, groupName in enumerate(groupNames): self.insertRows(idx, list([ScoreGroupItem(groupName)]), groupIndex) listIndex = self.index(idx, 0, groupIndex) for idx1, item in enumerate(data): self.insertRows(idx1, list([ScorePlayerItem(item)]), listIndex)
def _newKey(self, minus=False): """generate a new id and a new name if the name already exists""" newId = self.newId(minus=minus) newName = self.name if minus: copyNr = 1 while self.nameExists(newName): copyStr = ' ' + str(copyNr) if copyNr > 1 else '' newName = m18nc( 'Ruleset._newKey:%1 is empty or space plus number', 'Copy%1 of %2', copyStr, m18n(self.name)) copyNr += 1 return newId, newName
def data(self, index, role=Qt.DisplayRole): """score table""" # pylint: disable=R0912,R0914 # pylint too many branches result = QVariant() if role == Qt.TextAlignmentRole: if index.column() == 0: result = QVariant(int(Qt.AlignHCenter|Qt.AlignVCenter)) else: result = QVariant(int(Qt.AlignLeft|Qt.AlignVCenter)) if index.isValid() and (0 <= index.row() < len(self.tables)): table = self.tables[index.row()] if role == Qt.DisplayRole and index.column() == 1: result = QVariant(table.tableid) elif role == Qt.DisplayRole and index.column() == 0: if not table.status.startswith('Suspended'): result = QVariant(table.tableid) elif role == Qt.DisplayRole and index.column() == 2: players = [] zipped = zip(table.playerNames, table.playersOnline) for idx, pair in enumerate(zipped): name, online = pair[0], pair[1] if idx < len(zipped) - 1: name += ', ' palette = KApplication.palette() if online: color = palette.color(QPalette.Active, QPalette.WindowText).name() style = 'font-weight:normal;font-style:normal;color:%s' % color else: color = palette.color(QPalette.Disabled, QPalette.WindowText).name() style = 'font-weight:100;font-style:italic;color:%s' % color players.append('<nobr style="%s">' % style + name + '</nobr>') names = ''.join(players) result = QVariant(names) elif role == Qt.DisplayRole and index.column() == 3: status = table.status if status.startswith('Suspended'): dateVal = ' ' + datetime.datetime.strptime(status.replace('Suspended', ''), '%Y-%m-%dT%H:%M:%S').strftime('%c').decode('utf-8') status = 'Suspended' else: dateVal = '' result = QVariant(m18nc('table status', status) + dateVal) elif index.column() == 4: if role == Qt.DisplayRole: result = QVariant(m18n(table.ruleset.name)) elif role == Qt.ForegroundRole: palette = KApplication.palette() color = palette.windowText() if table.myRuleset else 'red' result = QVariant(QColor(color)) return result
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 __init__(self, game): super(ScoreTable, self).__init__(None) self.setObjectName('ScoreTable') self.game = None self.scoreModel = None self.scoreModelTest = None self.setWindowTitle(m18nc('kajongg', 'Scores') + ' - Kajongg') self.setAttribute(Qt.WA_AlwaysShowToolTips) self.setMouseTracking(True) self.__tableFields = ['prevailing', 'won', 'wind', 'points', 'payments', 'balance', 'hand', 'manualrules'] self.setupUi() self.refresh(game) StateSaver(self, self.splitter)
def headerData(self, section, orientation, role): """tell the view about the wanted headers""" if role == Qt.TextAlignmentRole: if orientation == Qt.Horizontal: return QVariant(int(Qt.AlignLeft | Qt.AlignVCenter)) if role != Qt.DisplayRole: return QVariant() if orientation == Qt.Horizontal: if section == 0: return QVariant(m18nc('Kajongg', 'Rule')) if section == 1: return QVariant(m18n(self.view.cbRuleset1.current.name)) if section == 2: return QVariant(m18n(self.view.cbRuleset2.current.name)) return QVariant()
def headerData(self, section, orientation, role): """tell the view about the wanted headers""" if role == Qt.TextAlignmentRole: if orientation == Qt.Horizontal: return QVariant(int(Qt.AlignLeft | Qt.AlignVCenter)) if role != Qt.DisplayRole: return QVariant() if orientation == Qt.Horizontal: if section == 0: return QVariant(m18nc("Kajongg", "Rule")) if section == 1: return QVariant(m18n(self.view.cbRuleset1.current.name)) if section == 2: return QVariant(m18n(self.view.cbRuleset2.current.name)) return QVariant()
def __init__(self): """self.servers is a list of tuples containing server and last playername""" QDialog.__init__(self, None) self.setWindowTitle(m18n('Login') + ' - Kajongg') self.setupUi() localName = m18nc('kajongg name for local game server', Query.localServerName) self.servers = Query( 'select url,lastname from server order by lasttime desc').records servers = [ m18nc('kajongg name for local game server', x[0]) for x in self.servers ] # the first server combobox item should be default: either the last used server # or localName for autoPlay if localName not in servers: servers.append(localName) if 'kajongg.org' not in servers: servers.append('kajongg.org') demoHost = Options.host or localName if demoHost in servers: servers.remove( demoHost ) # we want a unique list, it will be re-used for all following games servers.insert( 0, demoHost) # in this process but they will not be autoPlay self.cbServer.addItems(servers) self.passwords = Query( 'select url, p.name, passwords.password from passwords, player p ' 'where passwords.player=p.id').records Players.load() self.cbServer.editTextChanged.connect(self.serverChanged) self.cbUser.editTextChanged.connect(self.userChanged) self.serverChanged() StateSaver(self)
def headerData(self, section, orientation, role=Qt.DisplayRole): # pylint: disable=R0201 """show header""" if role == Qt.TextAlignmentRole: if orientation == Qt.Horizontal: if section in [3, 4]: return QVariant(int(Qt.AlignLeft)) else: return QVariant(int(Qt.AlignHCenter|Qt.AlignVCenter)) if role != Qt.DisplayRole: return QVariant() if orientation != Qt.Horizontal: return QVariant(int(section+1)) result = '' if section < 5: result = [m18n('Table'), '', m18n('Players'), m18nc('table status', 'Status'), m18n('Ruleset')][section] return QVariant(result)
def _loginFailed(self, failure): """login failed""" def answered(result): """user finally answered our question""" if result: return self.__adduser() else: return Failure(CancelledError) message = failure.getErrorMessage() if 'Wrong username' in message: if self.dlg.host == Query.localServerName: return answered(True) else: msg = m18nc('USER is not known on SERVER', '%1 is not known on %2, do you want to open an account?', self.dlg.username, self.dlg.host) return QuestionYesNo(msg).addCallback(answered) else: return self._loginReallyFailed(failure)
def headerData(self, section, orientation, role=Qt.DisplayRole): # pylint: disable=R0201 """show header""" if role == Qt.TextAlignmentRole: if orientation == Qt.Horizontal: if section in [3, 4]: return QVariant(int(Qt.AlignLeft)) else: return QVariant(int(Qt.AlignHCenter | Qt.AlignVCenter)) if role != Qt.DisplayRole: return QVariant() if orientation != Qt.Horizontal: return QVariant(int(section + 1)) result = '' if section < 5: result = [ m18n('Table'), '', m18n('Players'), m18nc('table status', 'Status'), m18n('Ruleset') ][section] return QVariant(result)
def _loginFailed(self, failure): """login failed""" def answered(result): """user finally answered our question""" if result: return self.__adduser() else: return Failure(CancelledError) message = failure.getErrorMessage() if 'Wrong username' in message: if self.dlg.host == Query.localServerName: return answered(True) else: msg = m18nc( 'USER is not known on SERVER', '%1 is not known on %2, do you want to open an account?', self.dlg.username, self.dlg.host) return QuestionYesNo(msg).addCallback(answered) else: return self._loginReallyFailed(failure)
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 __init__(self, name): """name may be: - an integer: ruleset.id from the sql table - a list: the full ruleset specification (probably sent from the server) - a string: The hash value of a ruleset""" self.name = name self.rulesetId = 0 self.__hash = None self.allRules = [] self.__dirty = False # only the ruleset editor is supposed to make us dirty self.__loaded = False self.__filteredLists = {} self.description = None self.rawRules = None # used when we get the rules over the network self.splitRules = [] self.doublingMeldRules = [] self.doublingHandRules = [] self.meldRules = RuleList(1, m18n('Meld Rules'), m18n('Meld rules are applied to single melds independent of the rest of the hand')) self.handRules = RuleList(2, m18n('Hand Rules'), m18n('Hand rules are applied to the entire hand, for all players')) self.winnerRules = RuleList(3, m18n('Winner Rules'), m18n('Winner rules are applied to the entire hand but only for the winner')) self.loserRules = RuleList(33, m18n('Loser Rules'), m18n('Loser rules are applied to the entire hand but only for non-winners')) self.mjRules = RuleList(4, m18n('Mah Jongg Rules'), m18n('Only hands matching a Mah Jongg rule can win')) self.parameterRules = RuleList(999, m18nc('kajongg','Options'), m18n('Here we have several special game related options')) self.penaltyRules = RuleList(9999, m18n('Penalties'), m18n( """Penalties are applied manually by the user. They are only used for scoring games. When playing against the computer or over the net, Kajongg will never let you get into a situation where you have to pay a penalty""")) self.ruleLists = list([self.meldRules, self.handRules, self.mjRules, self.winnerRules, self.loserRules, self.parameterRules, self.penaltyRules]) # the order of ruleLists is the order in which the lists appear in the ruleset editor # if you ever want to remove an entry from ruleLists: make sure its listId is not reused or you get # in trouble when updating self._initRuleset()
def __navigateScoringGame(self, event): """keyboard navigation in a scoring game""" mod = event.modifiers() key = event.key() wind = chr(key%128) moveCommands = m18nc('kajongg:keyboard commands for moving tiles to the players ' \ 'with wind ESWN or to the central tile selector (X)', 'ESWNX') tile = self.centralScene.focusItem().tile if wind in moveCommands: # translate i18n wind key to ESWN: wind = 'ESWNX'[moveCommands.index(wind)] self.__moveTile(tile, wind, mod &Qt.ShiftModifier) return True if key == Qt.Key_Tab and self.game: tabItems = [self.selectorBoard] tabItems.extend(list(p.handBoard for p in self.game.players if p.handBoard.tiles)) tabItems.append(tabItems[0]) currentBoard = tile.board if isinstance(tile, Tile) else None currIdx = 0 while tabItems[currIdx] != currentBoard and currIdx < len(tabItems) -2: currIdx += 1 tabItems[currIdx+1].hasFocus = True return True
def shortcuttedMeldName(meld): """convert int to speaking name with shortcut""" if meld == ALLMELDS or meld == REST or meld == 0: return '' parts = [] if SINGLE & meld: parts.append(m18nc('kajongg meld type','&single')) if PAIR & meld: parts.append(m18nc('kajongg meld type','&pair')) if CHOW & meld: parts.append(m18nc('kajongg meld type','&chow')) if PUNG & meld: parts.append(m18nc('kajongg meld type','p&ung')) if KONG & meld: parts.append(m18nc('kajongg meld type','k&ong')) if CLAIMEDKONG & meld: parts.append(m18nc('kajongg meld type','c&laimed kong')) return '|'.join(parts)
def setupUi(self): """layout the window""" self.setWindowTitle(m18n('Customize rulesets') + ' - Kajongg') self.setObjectName('Rulesets') hlayout = QHBoxLayout(self) v1layout = QVBoxLayout() self.v1widget = QWidget() v1layout = QVBoxLayout(self.v1widget) v2layout = QVBoxLayout() hlayout.addWidget(self.v1widget) hlayout.addLayout(v2layout) for widget in [self.v1widget, hlayout, v1layout, v2layout]: widget.setContentsMargins(0, 0, 0, 0) hlayout.setStretchFactor(self.v1widget, 10) self.btnCopy = QPushButton() self.btnRemove = QPushButton() self.btnCompare = QPushButton() self.btnClose = QPushButton() self.rulesetView = RuleTreeView(m18nc('kajongg', 'Rule'), self.btnCopy, self.btnRemove, self.btnCompare) v1layout.addWidget(self.rulesetView) self.rulesetView.setWordWrap(True) self.rulesetView.setMouseTracking(True) spacerItem = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) v2layout.addWidget(self.btnCopy) v2layout.addWidget(self.btnRemove) v2layout.addWidget(self.btnCompare) self.btnCopy.clicked.connect(self.rulesetView.copyRow) self.btnRemove.clicked.connect(self.rulesetView.removeRow) self.btnCompare.clicked.connect(self.rulesetView.compareRow) self.btnClose.clicked.connect(self.hide) v2layout.addItem(spacerItem) v2layout.addWidget(self.btnClose) self.retranslateUi() StateSaver(self) self.show()
def shortcuttedMeldName(meld): """convert int to speaking name with shortcut""" if meld == ALLMELDS or meld == REST or meld == 0: return '' parts = [] if SINGLE & meld: parts.append(m18nc('kajongg meld type', '&single')) if PAIR & meld: parts.append(m18nc('kajongg meld type', '&pair')) if CHOW & meld: parts.append(m18nc('kajongg meld type', '&chow')) if PUNG & meld: parts.append(m18nc('kajongg meld type', 'p&ung')) if KONG & meld: parts.append(m18nc('kajongg meld type', 'k&ong')) if CLAIMEDKONG & meld: parts.append(m18nc('kajongg meld type', 'c&laimed kong')) return '|'.join(parts)
def retranslateUi(self): """retranslate""" self.actionScoreGame.setText( m18nc('@action:inmenu', "&Score Manual Game")) self.actionScoreGame.setIconText( m18nc('@action:intoolbar', 'Manual Game')) self.actionScoreGame.setHelpText( m18nc('kajongg @info:tooltip', '&Score a manual game.')) self.actionPlayGame.setText(m18nc('@action:intoolbar', "&Play")) self.actionPlayGame.setPriority(QAction.LowPriority) self.actionPlayGame.setHelpText( m18nc('kajongg @info:tooltip', 'Start a new game.')) self.actionAbortGame.setText(m18nc('@action:inmenu', "&Abort Game")) self.actionAbortGame.setPriority(QAction.LowPriority) self.actionAbortGame.setHelpText( m18nc('kajongg @info:tooltip', 'Abort the current game.')) self.actionQuit.setText(m18nc('@action:inmenu', "&Quit Kajongg")) self.actionQuit.setPriority(QAction.LowPriority) self.actionPlayers.setText(m18nc('@action:intoolbar', "&Players")) self.actionPlayers.setHelpText( m18nc('kajongg @info:tooltip', 'define your players.')) self.actionRulesets.setText(m18nc('@action:intoolbar', "&Rulesets")) self.actionRulesets.setHelpText( m18nc('kajongg @info:tooltip', 'customize rulesets.')) self.actionAngle.setText( m18nc('@action:inmenu', "&Change Visual Angle")) self.actionAngle.setIconText(m18nc('@action:intoolbar', "Angle")) self.actionAngle.setHelpText( m18nc('kajongg @info:tooltip', "Change the visual appearance of the tiles.")) self.actionScoring.setText( m18nc('@action:inmenu', "&Show Scoring Editor")) self.actionScoring.setIconText(m18nc('@action:intoolbar', "&Scoring")) self.actionScoring.setHelpText( m18nc('kajongg @info:tooltip', "Show or hide the scoring editor for a manual game.")) self.actionScoreTable.setText( m18nc('kajongg @action:inmenu', "&Score Table")) self.actionScoreTable.setIconText( m18nc('kajongg @action:intoolbar', "&Scores")) self.actionScoreTable.setHelpText( m18nc('kajongg @info:tooltip', "Show or hide the score table for the current game.")) self.actionExplain.setText(m18nc('@action:inmenu', "&Explain Scores")) self.actionExplain.setIconText(m18nc('@action:intoolbar', "&Explain")) self.actionExplain.setHelpText( m18nc('kajongg @info:tooltip', 'Explain the scoring for all players in the current game.')) self.actionAutoPlay.setText(m18nc('@action:inmenu', "&Demo Mode")) self.actionAutoPlay.setPriority(QAction.LowPriority) self.actionAutoPlay.setHelpText( m18nc( 'kajongg @info:tooltip', 'Let the computer take over for you. Start a new local game if needed.' )) self.actionChat.setText(m18n("C&hat")) self.actionChat.setHelpText( m18nc('kajongg @info:tooltip', 'Chat with the other players.'))
def localName(self): """the localized name of this player""" return m18nc('kajongg, name of robot player, to be translated', self.name)
class Meld(object): """represents a meld. Can be empty. Many Meld methods will raise exceptions if the meld is empty. But we do not care, those methods are not supposed to be called on empty melds. Meld firstly holds the tile elements like 's1','s2','s3' but its attribute tiles can also hold references to the visual tile objects. The name of the tile element in the meld does not have to be identical with the name of the corresponding real tile while tiles are added or removed. See end of SelectorBoard.meldVariants().""" colorNames = { 'x': m18nc('kajongg', 'hidden'), 's': m18nc('kajongg', 'stone'), 'b': m18nc('kajongg', 'bamboo'), 'c': m18nc('kajongg', 'character'), 'w': m18nc('kajongg', 'wind'), 'd': m18nc('kajongg', 'dragon'), 'f': m18nc('kajongg', 'flower'), 'y': m18nc('kajongg', 'season') } valueNames = { 'y': m18nc('kajongg', 'tile'), 'b': m18nc('kajongg', 'white'), 'r': m18nc('kajongg', 'red'), 'g': m18nc('kajongg', 'green'), 'e': m18nc('kajongg', 'east'), 's': m18nc('kajongg', 'south'), 'w': m18nc('kajongg', 'west'), 'n': m18nc('kajongg', 'north'), 'O': m18nc('kajongg', 'own wind'), 'R': m18nc('kajongg', 'round wind') } for valNameIdx in range(1, 10): valueNames[str(valNameIdx)] = str(valNameIdx) @staticmethod def tileName(element): """returns translated name of a single tile""" return Meld.colorNames[element[0].lower()] + ' ' + Meld.valueNames[ element[1]] __hash__ = None def __init__(self, newContent=None): """init the meld: content can be either - a single string with 2 chars for every tile - a list containing such strings - another meld. Its tiles are not passed. - a list of Tile objects""" self.__pairs = Pairs() self.__valid = False self.meldType = None self.tiles = [] if isinstance(newContent, list) and newContent and hasattr( newContent[0], 'focusable'): self.joined = ''.join(x.element for x in newContent) self.tiles = newContent elif isinstance(newContent, Meld): self.joined = newContent.joined self.tiles = newContent.tiles elif isinstance(newContent, Tile): self.joined = newContent.element self.tiles = [newContent] else: self.joined = newContent def __len__(self): """how many tiles do we have?""" return len(self.tiles) if self.tiles else len(self.__pairs) def __getitem__(self, index): """Meld[x] returns Tile # x """ return self.tiles[index] def __setitem__(self, index, value): """sets a tile in the meld. Not currently used but pylint wants the container to be perfect""" raise Exception("Assigning a tile to a Meld is not supported") def __delitem__(self, index): """removes a tile from the meld. Not currently used but pylint wants the container to be perfect""" raise Exception("removing a tile from a Meld is not supported") def __eq__(self, other): return isinstance(other, Meld) and self.pairs == other.pairs def isValid(self): """is it valid?""" return self.__valid def __isChow(self): """expensive, but this is only computed once per meld""" if len(self.__pairs) == 3: starts = set(self.__pairs.startChars()) if len(starts) == 1: if starts & set('sbcSBC'): values = self.__pairs.values() if values[1] == values[0] + 1 and values[ 2] == values[0] + 2: return True return False @property def state(self): """meld state""" firsts = self.__pairs.startChars() if ''.join(firsts).islower(): return EXPOSED elif len(self) == 4 and firsts[1].isupper() and firsts[2].isupper(): return CONCEALED elif len(self) == 4: return EXPOSED else: return CONCEALED @state.setter def state(self, state): """meld state""" if state == EXPOSED: self.__pairs.toLower() if self.meldType == CLAIMEDKONG: self.__pairs.toUpper(3) elif state == CONCEALED: self.__pairs.toUpper() if len(self.__pairs) == 4: self.__pairs.toLower(0) self.__pairs.toLower(3) else: raise Exception('meld.setState: illegal state %d' % state) for idx, tile in enumerate(self.tiles): tile.element = self.__pairs[idx] def _getMeldType(self): """compute meld type""" # pylint: disable=R0912 # too many branches length = len(self.__pairs) if not length: return EMPTY assert self.__pairs[0][0].lower() in 'xdwsbcfy', self.__pairs if 'Xy' in self.__pairs: return REST elif length == 1: return SINGLE elif length == 2: result = PAIR elif length == 4: if self.__pairs.isUpper(): result = REST self.__valid = False elif self.__pairs.isLower(0, 3) and self.__pairs.isUpper(3): result = CLAIMEDKONG else: result = KONG elif self.__isChow(): result = CHOW elif length == 3: result = PUNG else: result = REST if result not in [CHOW, REST]: if len(set(x.lower() for x in self.__pairs)) > 1: result = REST return result def tileType(self): """return one of d w s b c f y""" return self.__pairs[0][0].lower() def isPair(self): """is this meld a pair?""" return self.meldType == PAIR def isChow(self): """is this meld a pair?""" return self.meldType == CHOW def isPung(self): """is this meld a pair?""" return self.meldType == PUNG def isKong(self): """is it a kong?""" return self.meldType in (KONG, CLAIMEDKONG) @property def pairs(self): """make them readonly""" return self.__pairs @property def joined(self): """content""" return ''.join(self.__pairs) @joined.setter def joined(self, newContent): """content""" assert not self.tiles self.__pairs = Pairs(newContent) self.__valid = True self.meldType = self._getMeldType() def expose(self, isClaiming): """expose this meld. For kungs, leave one or two concealed, showing how the kung was built""" assert self.__pairs.isUpper(), self.joined if len(self.__pairs) < 4: self.__pairs.toLower() else: if isClaiming: self.__pairs.toLower(0, 3) self.__pairs.toUpper(3) else: # concealed kong self.__pairs.toLower(0) self.__pairs.toUpper(1, 3) self.__pairs.toLower(3) self.meldType = self._getMeldType() def conceal(self): """conceal this meld again""" self.__pairs.toUpper() self.meldType = self._getMeldType() def __repr__(self): """the default representation""" return 'Meld(%s)' % self.joined
def retranslateUi(self): """translate to current language""" self.btnCopy.setText(m18n("Copy")) self.btnCompare.setText(m18nc('Kajongg ruleset comparer', 'Compare')) self.btnRemove.setText(m18n("Remove")) self.btnClose.setText(m18n('Close'))
def retranslateUi(self): """retranslate""" self.actionScoreGame.setText(m18nc('@action:inmenu', "&Score Manual Game")) self.actionScoreGame.setIconText(m18nc('@action:intoolbar', 'Manual Game')) self.actionScoreGame.setHelpText(m18nc('kajongg @info:tooltip', '&Score a manual game.')) self.actionPlayGame.setText(m18nc('@action:intoolbar', "&Play")) self.actionPlayGame.setPriority(QAction.LowPriority) self.actionPlayGame.setHelpText(m18nc('kajongg @info:tooltip', 'Start a new game.')) self.actionAbortGame.setText(m18nc('@action:inmenu', "&Abort Game")) self.actionAbortGame.setPriority(QAction.LowPriority) self.actionAbortGame.setHelpText(m18nc('kajongg @info:tooltip', 'Abort the current game.')) self.actionQuit.setText(m18nc('@action:inmenu', "&Quit Kajongg")) self.actionQuit.setPriority(QAction.LowPriority) self.actionPlayers.setText(m18nc('@action:intoolbar', "&Players")) self.actionPlayers.setHelpText(m18nc('kajongg @info:tooltip', 'define your players.')) self.actionRulesets.setText(m18nc('@action:intoolbar', "&Rulesets")) self.actionRulesets.setHelpText(m18nc('kajongg @info:tooltip', 'customize rulesets.')) self.actionAngle.setText(m18nc('@action:inmenu', "&Change Visual Angle")) self.actionAngle.setIconText(m18nc('@action:intoolbar', "Angle")) self.actionAngle.setHelpText(m18nc('kajongg @info:tooltip', "Change the visual appearance of the tiles.")) self.actionScoring.setText(m18nc('@action:inmenu', "&Show Scoring Editor")) self.actionScoring.setIconText(m18nc('@action:intoolbar', "&Scoring")) self.actionScoring.setHelpText(m18nc('kajongg @info:tooltip', "Show or hide the scoring editor for a manual game.")) self.actionScoreTable.setText(m18nc('kajongg @action:inmenu', "&Score Table")) self.actionScoreTable.setIconText(m18nc('kajongg @action:intoolbar', "&Scores")) self.actionScoreTable.setHelpText(m18nc('kajongg @info:tooltip', "Show or hide the score table for the current game.")) self.actionExplain.setText(m18nc('@action:inmenu', "&Explain Scores")) self.actionExplain.setIconText(m18nc('@action:intoolbar', "&Explain")) self.actionExplain.setHelpText(m18nc('kajongg @info:tooltip', 'Explain the scoring for all players in the current game.')) self.actionAutoPlay.setText(m18nc('@action:inmenu', "&Demo Mode")) self.actionAutoPlay.setPriority(QAction.LowPriority) self.actionAutoPlay.setHelpText(m18nc('kajongg @info:tooltip', 'Let the computer take over for you. Start a new local game if needed.')) self.actionChat.setText(m18n("C&hat")) self.actionChat.setHelpText(m18nc('kajongg @info:tooltip', 'Chat with the other players.'))
def __init__(self, name=None, shortcut=None): Message.__init__(self, name, shortcut) self.i18nName = m18nc('kajongg', self.name)
def buttonCaption(self): """localized, with a & for the shortcut""" i18nShortcut = m18nc('kajongg game dialog:Key for '+self.name, self.shortcut) return self.i18nName.replace(i18nShortcut, '&'+i18nShortcut, 1)