def initUi (self): self.window = Gtk.Window() self.window.set_border_width(12) self.window.set_icon_name("pychess") self.window.set_title("PyChess - Internet Chess Chat") self.window.connect("delete-event", lambda w,e: w.hide() or True) uistuff.keepWindowSize("chatwindow", self.window, defaultSize=(650,400)) dock = PyDockTop("icchat") dock.show() self.window.add(dock) leaf = dock.dock(self.viewspanel, CENTER, Gtk.Label(label="chat"), "chat") leaf.setDockable(False) self.channelspanel.connect('conversationAdded', self.onConversationAdded) self.channelspanel.connect('conversationRemoved', self.onConversationRemoved) self.channelspanel.connect('conversationSelected', self.onConversationSelected) leaf.dock(self.channelspanel, WEST, Gtk.Label(label=_("Conversations")), "conversations") leaf.dock(self.infopanel, EAST, Gtk.Label(label=_("Conversation info")), "info") for panel in self.panels: panel.show_all() panel.start()
def initUi(self): self.window = Gtk.Window() self.window.set_border_width(12) self.window.set_icon_name("pychess") self.window.set_title("PyChess - Internet Chess Chat") self.window.connect("delete-event", lambda w, e: w.hide() or True) uistuff.keepWindowSize("chatwindow", self.window, defaultSize=(650, 400)) dock = PyDockTop("icchat") dock.show() self.window.add(dock) leaf = dock.dock(self.viewspanel, CENTER, Gtk.Label(label="chat"), "chat") leaf.setDockable(False) self.channelspanel.connect('conversationAdded', self.onConversationAdded) self.channelspanel.connect('conversationRemoved', self.onConversationRemoved) self.channelspanel.connect('conversationSelected', self.onConversationSelected) leaf.dock(self.channelspanel, WEST, Gtk.Label(label=_("Conversations")), "conversations") leaf.dock(self.infopanel, EAST, Gtk.Label(label=_("Conversation info")), "info") for panel in self.panels: panel.show_all() panel.start()
class Games(GObject.GObject, Perspective): __gsignals__ = { 'gmwidg_created': (GObject.SignalFlags.RUN_FIRST, None, (object, )) } def __init__(self): GObject.GObject.__init__(self) Perspective.__init__(self, "games", _("Games")) self.notebooks = {} self.first_run = True self.gamewidgets = set() self.gmwidg_cids = {} self.board_cids = {} self.notify_cids = defaultdict(list) self.key2gmwidg = {} self.key2cid = {} self.dock = None self.dockAlign = None self.dockLocation = addUserConfigPrefix("pydock.xml") @asyncio.coroutine def generalStart(self, gamemodel, player0tup, player1tup, loaddata=None): """ The player tuples are: (The type af player in a System.const value, A callable creating the player, A list of arguments for the callable, A preliminary name for the player) If loaddata is specified, it should be a tuple of: (A text uri or fileobj, A Savers.something module with a load function capable of loading it, An int of the game in file you want to load, The position from where to start the game) """ log.debug("Games.generalStart: %s\n %s\n %s" % (gamemodel, player0tup, player1tup)) gmwidg = gamewidget.GameWidget(gamemodel, self) self.gamewidgets.add(gmwidg) self.gmwidg_cids[gmwidg] = gmwidg.connect("game_close_clicked", self.closeGame) # worker.publish((gmwidg,gamemodel)) self.attachGameWidget(gmwidg) game_nanny.nurseGame(gmwidg, gamemodel) log.debug("Games.generalStart: -> emit gmwidg_created: %s" % (gmwidg)) self.emit("gmwidg_created", gmwidg) log.debug("Games.generalStart: <- emit gmwidg_created: %s" % (gmwidg)) # Initing players def xxxset_name(none, player, key, alt): player.setName(conf.get(key, alt)) players = [] for i, playertup in enumerate((player0tup, player1tup)): type, func, args, prename = playertup if type != LOCAL: if type == ARTIFICIAL: player = yield from func(*args) else: player = func(*args) players.append(player) # if type == ARTIFICIAL: # def readyformoves (player, color): # gmwidg.setTabText(gmwidg.display_text)) # players[i].connect("readyForMoves", readyformoves, i) else: # Until PyChess has a proper profiles system, as discussed on the # issue tracker, we need to give human players special treatment player = func(gmwidg, *args) players.append(player) assert len(players) == 2 if player0tup[0] == ARTIFICIAL and player1tup[0] == ARTIFICIAL: def emit_action(board, action, player, param, gmwidg): if gmwidg.isInFront(): gamemodel.curplayer.emit("offer", Offer(action, param=param)) self.board_cids[gmwidg.board] = gmwidg.board.connect("action", emit_action, gmwidg) log.debug("Games.generalStart: -> gamemodel.setPlayers(): %s" % (gamemodel)) gamemodel.setPlayers(players) log.debug("Games.generalStart: <- gamemodel.setPlayers(): %s" % (gamemodel)) # Forward information from the engines for playertup, tagname in ((player0tup, "WhiteElo"), (player1tup, "BlackElo")): if playertup[0] == ARTIFICIAL: elo = playertup[2][0].get("elo") if elo: gamemodel.tags[tagname] = elo # Starting if loaddata: try: uri, loader, gameno, position = loaddata gamemodel.loadAndStart(uri, loader, gameno, position) if position != gamemodel.ply and position != -1: gmwidg.board.view.shown = position except LoadingError as e: d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.OK) d.set_markup(_("<big><b>Error loading game</big></b>")) d.format_secondary_text(", ".join(str(a) for a in e.args)) d.show() d.destroy() else: if gamemodel.variant.need_initial_board: for player in gamemodel.players: player.setOptionInitialBoard(gamemodel) log.debug("Games..generalStart: -> gamemodel.start(): %s" % (gamemodel)) gamemodel.start() log.debug("Games.generalStart: <- gamemodel.start(): %s" % (gamemodel)) log.debug("Games.generalStart: returning gmwidg=%s\n gamemodel=%s" % (gmwidg, gamemodel)) ################################################################################ # Saving # ################################################################################ def saveGame(self, game, position=None): if not game.isChanged(): return if game.uri and isWriteable(game.uri): self.saveGameSimple(game.uri, game, position=position) else: return self.saveGameAs(game, position=position) def saveGameSimple(self, uri, game, position=None): ending = os.path.splitext(uri)[1] if not ending: return saver = enddir[ending[1:]] game.save(uri, saver, append=False, position=position) def saveGamePGN(self, game): if conf.get("saveOwnGames") and not game.hasLocalPlayer(): return True filename = conf.get("autoSaveFormat") filename = filename.replace("#n1", game.tags["White"]) filename = filename.replace("#n2", game.tags["Black"]) year, month, day = parseDateTag(game.tags["Date"]) year = '' if year is None else str(year) month = '' if month is None else str(month) day = '' if day is None else str(day) filename = filename.replace("#y", "%s" % year) filename = filename.replace("#m", "%s" % month) filename = filename.replace("#d", "%s" % day) pgn_path = conf.get("autoSavePath") + "/" + filename + ".pgn" append = True try: if not os.path.isfile(pgn_path): # create new file with open(pgn_path, "w"): pass base_offset = os.path.getsize(pgn_path) # save to .sqlite database_path = os.path.splitext(pgn_path)[0] + '.sqlite' database.save(database_path, game, base_offset) # save to .scout from pychess.Savers.pgn import scoutfish_path if scoutfish_path is not None: pgn_text = pgn.save(StringIO(), game) tmp = tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", delete=False) pgnfile = tmp.name with tmp.file as f: f.write(pgn_text) # create new .scout from pgnfile we are importing args = [scoutfish_path, "make", pgnfile, "%s" % base_offset] output = subprocess.check_output(args, stderr=subprocess.STDOUT) # append it to our existing one if output.decode().find(u"Processing...done") > 0: old_scout = os.path.splitext(pgn_path)[0] + '.scout' new_scout = os.path.splitext(pgnfile)[0] + '.scout' with open(old_scout, "ab") as file1, open(new_scout, "rb") as file2: file1.write(file2.read()) # TODO: do we realy want to update .bin ? It can be huge/slow! # save to .pgn game.save(pgn_path, pgn, append) return True except IOError: return False def saveGameAs(self, game, position=None, export=False): savedialog, savecombo = get_save_dialog(export) # Keep running the dialog until the user has canceled it or made an error # free operation title = _("Save Game") if not export else _("Export position") savedialog.set_title(title) while True: savedialog.set_current_name( "%s %s %s" % (game.players[0], _("vs."), game.players[1])) res = savedialog.run() if res != Gtk.ResponseType.ACCEPT: break uri = savedialog.get_filename() ending = os.path.splitext(uri)[1] if ending.startswith("."): ending = ending[1:] append = False index = savecombo.get_active() if index == 0: if ending not in enddir: d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK) folder, file = os.path.split(uri) d.set_markup(_("<big><b>Unknown file type '%s'</b></big>") % ending) d.format_secondary_text(_( "Was unable to save '%(uri)s' as PyChess doesn't know the format '%(ending)s'.") % {'uri': uri, 'ending': ending}) d.run() d.destroy() continue else: saver = enddir[ending] else: format = exportformats[index] if export else saveformats[index] saver = format[2] if ending not in enddir or not saver == enddir[ending]: uri += ".%s" % saver.__ending__ if os.path.isfile(uri) and not os.access(uri, os.W_OK): d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK) d.set_markup(_("<big><b>Unable to save file '%s'</b></big>") % uri) d.format_secondary_text(_( "You don't have the necessary rights to save the file.\n\ Please ensure that you have given the right path and try again.")) d.run() d.destroy() continue if os.path.isfile(uri): d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.QUESTION) d.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, _("_Replace"), Gtk.ResponseType.ACCEPT) if saver.__append__: d.add_buttons(Gtk.STOCK_ADD, 1) d.set_title(_("File exists")) folder, file = os.path.split(uri) d.set_markup(_( "<big><b>A file named '%s' already exists. Would you like to replace it?</b></big>") % file) d.format_secondary_text(_( "The file already exists in '%s'. If you replace it, its content will be overwritten.") % folder) replaceRes = d.run() d.destroy() if replaceRes == 1: append = True elif replaceRes == Gtk.ResponseType.CANCEL: continue else: print(repr(uri)) try: game.save(uri, saver, append, position) except IOError as e: d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.ERROR) d.add_buttons(Gtk.STOCK_OK, Gtk.ResponseType.OK) d.set_title(_("Could not save the file")) d.set_markup(_( "<big><b>PyChess was not able to save the game</b></big>")) d.format_secondary_text(_("The error was: %s") % ", ".join( str(a) for a in e.args)) d.run() d.destroy() continue break savedialog.destroy() return res ################################################################################ # Closing # ################################################################################ def closeAllGames(self, gamewidgets): log.debug("Games.closeAllGames") response = None changedPairs = [(gmwidg, gmwidg.gamemodel) for gmwidg in gamewidgets if gmwidg.gamemodel.isChanged()] if len(changedPairs) == 0: response = Gtk.ResponseType.OK elif len(changedPairs) == 1: response = self.closeGame(changedPairs[0][0]) else: markup = "<big><b>" + ngettext("There is %d game with unsaved moves.", "There are %d games with unsaved moves.", len(changedPairs)) % len(changedPairs) + " " + \ _("Save moves before closing?") + "</b></big>" for gmwidg, game in changedPairs: if not gmwidg.gamemodel.isChanged(): response = Gtk.ResponseType.OK else: if conf.get("autoSave"): x = self.saveGamePGN(game) if x: response = Gtk.ResponseType.OK else: response = None markup = "<b><big>" + _("Unable to save to configured file. \ Save the games before closing?") + "</big></b>" break if response is None: widgets = GladeWidgets("saveGamesDialog.glade") dialog = widgets["saveGamesDialog"] heading = widgets["saveGamesDialogHeading"] saveLabel = widgets["saveGamesDialogSaveLabel"] treeview = widgets["saveGamesDialogTreeview"] heading.set_markup(markup) liststore = Gtk.ListStore(bool, str) treeview.set_model(liststore) renderer = Gtk.CellRendererToggle() renderer.props.activatable = True treeview.append_column(Gtk.TreeViewColumn("", renderer, active=0)) treeview.append_column(Gtk.TreeViewColumn("", Gtk.CellRendererText(), text=1)) for gmwidg, game in changedPairs: liststore.append((True, "%s %s %s" % (game.players[0], _("vs."), game.players[1]))) def callback(cell, path): if path: liststore[path][0] = not liststore[path][0] saves = len(tuple(row for row in liststore if row[0])) saveLabel.set_text(ngettext( "_Save %d document", "_Save %d documents", saves) % saves) saveLabel.set_use_underline(True) renderer.connect("toggled", callback) callback(None, None) while True: response = dialog.run() if response == Gtk.ResponseType.YES: for i in range(len(liststore) - 1, -1, -1): checked, name = liststore[i] if checked: cgmwidg, cgame = changedPairs[i] if self.saveGame(cgame) == Gtk.ResponseType.ACCEPT: liststore.remove(liststore.get_iter((i, ))) del changedPairs[i] if cgame.status in UNFINISHED_STATES: cgame.end(ABORTED, ABORTED_AGREEMENT) cgame.terminate() self.delGameWidget(cgmwidg) else: break else: break else: break dialog.destroy() if response not in (Gtk.ResponseType.DELETE_EVENT, Gtk.ResponseType.CANCEL): pairs = [(gmwidg, gmwidg.gamemodel) for gmwidg in gamewidgets] for gmwidg, game in pairs: if game.status in UNFINISHED_STATES: game.end(ABORTED, ABORTED_AGREEMENT) game.terminate() if gmwidg.notebookKey in self.key2gmwidg: self.delGameWidget(gmwidg) return response def closeGame(self, gmwidg): log.debug("Games.closeGame") response = None if not gmwidg.gamemodel.isChanged(): response = Gtk.ResponseType.OK else: markup = "<b><big>" + _("Save the current game before you close it?") + "</big></b>" if conf.get("autoSave"): x = self.saveGamePGN(gmwidg.gamemodel) if x: response = Gtk.ResponseType.OK else: markup = "<b><big>" + _("Unable to save to configured file. \ Save the current game before you close it?") + "</big></b>" if response is None: d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.WARNING) d.add_button(_("Close _without Saving"), Gtk.ResponseType.OK) d.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL) if gmwidg.gamemodel.uri: d.add_button(Gtk.STOCK_SAVE, Gtk.ResponseType.YES) else: d.add_button(Gtk.STOCK_SAVE_AS, Gtk.ResponseType.YES) gmwidg.bringToFront() d.set_markup(markup) d.format_secondary_text(_( "It is not possible later to continue the game,\nif you don't save it.")) response = d.run() d.destroy() if response == Gtk.ResponseType.YES: # Test if cancel was pressed in the save-file-dialog if self.saveGame(gmwidg.gamemodel) != Gtk.ResponseType.ACCEPT: response = Gtk.ResponseType.CANCEL if response not in (Gtk.ResponseType.DELETE_EVENT, Gtk.ResponseType.CANCEL): if gmwidg.gamemodel.status in UNFINISHED_STATES: gmwidg.gamemodel.end(ABORTED, ABORTED_AGREEMENT) gmwidg.disconnect(self.gmwidg_cids[gmwidg]) del self.gmwidg_cids[gmwidg] for cid in self.notify_cids[gmwidg]: conf.notify_remove(cid) del self.notify_cids[gmwidg] if gmwidg.board in self.board_cids: gmwidg.board.disconnect(self.board_cids[gmwidg.board]) del self.board_cids[gmwidg.board] self.delGameWidget(gmwidg) self.gamewidgets.remove(gmwidg) gmwidg.gamemodel.terminate() db_persp = perspective_manager.get_perspective("database") if len(self.gamewidgets) == 0: for widget in MENU_ITEMS: if widget in ("copy_pgn", "copy_fen") and db_persp.preview_panel is not None: continue gamewidget.getWidgets()[widget].set_property('sensitive', False) return response def delGameWidget(self, gmwidg): """ Remove the widget from the GUI after the game has been terminated """ log.debug("Games.delGameWidget: starting %s" % repr(gmwidg)) gmwidg.closed = True gmwidg.emit("closed") called_from_preferences = False window_list = Gtk.Window.list_toplevels() widgets = gamewidget.getWidgets() for window in window_list: if window.is_active() and window == widgets["preferences"]: called_from_preferences = True break pageNum = gmwidg.getPageNumber() headbook = self.getheadbook() if gmwidg.notebookKey in self.key2gmwidg: del self.key2gmwidg[gmwidg.notebookKey] if gmwidg.notebookKey in self.key2cid: headbook.disconnect(self.key2cid[gmwidg.notebookKey]) del self.key2cid[gmwidg.notebookKey] headbook.remove_page(pageNum) for notebook in self.notebooks.values(): notebook.remove_page(pageNum) if headbook.get_n_pages() == 1 and conf.get("hideTabs"): self.show_tabs(False) if headbook.get_n_pages() == 0: if not called_from_preferences: # If the last (but not the designGW) gmwidg was closed # and we are FICS-ing, present the FICS lounge perspective_manager.disable_perspective("games") gmwidg._del() def init_layout(self): perspective_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) perspective_manager.set_perspective_widget("games", perspective_widget) self.notebooks = {"board": new_notebook("board"), "buttons": new_notebook("buttons"), "messageArea": new_notebook("messageArea")} self.main_notebook = self.notebooks["board"] for panel in self.sidePanels: self.notebooks[panel_name(panel.__name__)] = new_notebook(panel_name(panel.__name__)) # Initing headbook align = gamewidget.createAlignment(4, 4, 0, 4) align.set_property("yscale", 0) headbook = Gtk.Notebook() headbook.set_name("headbook") headbook.set_scrollable(True) align.add(headbook) perspective_widget.pack_start(align, False, True, 0) self.show_tabs(not conf.get("hideTabs")) # Initing center centerVBox = Gtk.VBox() # The dock self.dock = PyDockTop("main", self) self.dockAlign = gamewidget.createAlignment(4, 4, 0, 4) self.dockAlign.add(self.dock) centerVBox.pack_start(self.dockAlign, True, True, 0) self.dockAlign.show() self.dock.show() self.docks["board"] = (Gtk.Label(label="Board"), self.notebooks["board"], None) for panel in self.sidePanels: self.docks[panel_name(panel.__name__)][1] = self.notebooks[panel_name(panel.__name__)] self.load_from_xml() # Default layout of side panels if not os.path.isfile(self.dockLocation): leaf = self.dock.dock(self.docks["board"][1], CENTER, Gtk.Label(label=self.docks["board"][0]), "board") self.docks["board"][1].show_all() leaf.setDockable(False) # S epanel = leaf.dock(self.docks["bookPanel"][1], SOUTH, self.docks["bookPanel"][0], "bookPanel") epanel.default_item_height = 45 epanel = epanel.dock(self.docks["engineOutputPanel"][1], CENTER, self.docks["engineOutputPanel"][0], "engineOutputPanel") # NE leaf = leaf.dock(self.docks["annotationPanel"][1], EAST, self.docks["annotationPanel"][0], "annotationPanel") leaf = leaf.dock(self.docks["historyPanel"][1], CENTER, self.docks["historyPanel"][0], "historyPanel") leaf = leaf.dock(self.docks["scorePanel"][1], CENTER, self.docks["scorePanel"][0], "scorePanel") # SE leaf = leaf.dock(self.docks["chatPanel"][1], SOUTH, self.docks["chatPanel"][0], "chatPanel") leaf = leaf.dock(self.docks["commentPanel"][1], CENTER, self.docks["commentPanel"][0], "commentPanel") def unrealize(dock, notebooks): # unhide the panel before saving so its configuration is saved correctly self.notebooks["board"].get_parent().get_parent().zoomDown() dock.saveToXML(self.dockLocation) dock._del() self.dock.connect("unrealize", unrealize, self.notebooks) hbox = Gtk.HBox() # Buttons self.notebooks["buttons"].set_border_width(4) hbox.pack_start(self.notebooks["buttons"], False, True, 0) # The message area # TODO: If you try to fix this first read issue #958 and 1018 align = gamewidget.createAlignment(0, 0, 0, 0) # sw = Gtk.ScrolledWindow() # port = Gtk.Viewport() # port.add(self.notebooks["messageArea"]) # sw.add(port) # align.add(sw) align.add(self.notebooks["messageArea"]) hbox.pack_start(align, True, True, 0) def ma_switch_page(notebook, gpointer, page_num): notebook.props.visible = notebook.get_nth_page(page_num).\ get_child().props.visible self.notebooks["messageArea"].connect("switch-page", ma_switch_page) centerVBox.pack_start(hbox, False, True, 0) perspective_widget.pack_start(centerVBox, True, True, 0) centerVBox.show_all() perspective_widget.show_all() perspective_manager.set_perspective_menuitems("games", self.menuitems) conf.notify_add("hideTabs", self.tabsCallback) # Connecting headbook to other notebooks def hb_switch_page(notebook, gpointer, page_num): for notebook in self.notebooks.values(): notebook.set_current_page(page_num) gmwidg = self.key2gmwidg[self.getheadbook().get_nth_page(page_num)] if isinstance(gmwidg.gamemodel, ICGameModel): primary = "primary " + str(gmwidg.gamemodel.ficsgame.gameno) gmwidg.gamemodel.connection.client.run_command(primary) headbook.connect("switch-page", hb_switch_page) if hasattr(headbook, "set_tab_reorderable"): def page_reordered(widget, child, new_num, headbook): old_num = self.notebooks["board"].page_num(self.key2gmwidg[child].boardvbox) if old_num == -1: log.error('Games and labels are out of sync!') else: for notebook in self.notebooks.values(): notebook.reorder_child( notebook.get_nth_page(old_num), new_num) headbook.connect("page-reordered", page_reordered, headbook) def getheadbook(self): if len(self.key2gmwidg) == 0: return None headbook = self.widget.get_children()[0].get_children()[0].get_child() # to help StoryText create widget description # headbook.get_tab_label_text = customGetTabLabelText return headbook def cur_gmwidg(self): if len(self.key2gmwidg) == 0: return None headbook = self.getheadbook() notebookKey = headbook.get_nth_page(headbook.get_current_page()) return self.key2gmwidg[notebookKey] def customGetTabLabelText(self, child): gmwidg = self.key2gmwidg[child] return gmwidg.display_text def zoomToBoard(self, view_zoomed): if not self.notebooks["board"].get_parent(): return if view_zoomed: self.notebooks["board"].get_parent().get_parent().zoomUp() else: self.notebooks["board"].get_parent().get_parent().zoomDown() def show_tabs(self, show): head = self.getheadbook() if head is None: return head.set_show_tabs(show) def tabsCallback(self, widget): head = self.getheadbook() if not head: return if head.get_n_pages() == 1: self.show_tabs(not conf.get("hideTabs")) def attachGameWidget(self, gmwidg): log.debug("attachGameWidget: %s" % gmwidg) if self.first_run: self.init_layout() self.first_run = False perspective_manager.activate_perspective("games") gmwidg.panels = [panel.Sidepanel().load(gmwidg) for panel in self.sidePanels] self.key2gmwidg[gmwidg.notebookKey] = gmwidg headbook = self.getheadbook() headbook.append_page(gmwidg.notebookKey, gmwidg.tabcontent) gmwidg.notebookKey.show_all() if hasattr(headbook, "set_tab_reorderable"): headbook.set_tab_reorderable(gmwidg.notebookKey, True) def callback(notebook, gpointer, page_num, gmwidg): if notebook.get_nth_page(page_num) == gmwidg.notebookKey: gmwidg.infront() if gmwidg.gamemodel.players and gmwidg.gamemodel.isObservationGame(): gmwidg.light_on_off(False) text = gmwidg.game_info_label.get_text() gmwidg.game_info_label.set_markup( '<span color="black" weight="bold">%s</span>' % text) self.key2cid[gmwidg.notebookKey] = headbook.connect_after("switch-page", callback, gmwidg) gmwidg.infront() align = gamewidget.createAlignment(0, 0, 0, 0) align.show() align.add(gmwidg.infobar) self.notebooks["messageArea"].append_page(align, None) self.notebooks["board"].append_page(gmwidg.boardvbox, None) gmwidg.boardvbox.show_all() for panel, instance in zip(self.sidePanels, gmwidg.panels): self.notebooks[panel_name(panel.__name__)].append_page(instance, None) instance.show_all() self.notebooks["buttons"].append_page(gmwidg.stat_hbox, None) gmwidg.stat_hbox.show_all() if headbook.get_n_pages() == 1: self.show_tabs(not conf.get("hideTabs")) else: # We should always show tabs if more than one exists self.show_tabs(True) headbook.set_current_page(-1) widgets = gamewidget.getWidgets() if headbook.get_n_pages() == 1 and not widgets["show_sidepanels"].get_active(): self.zoomToBoard(True)
class Database(GObject.GObject, Perspective): __gsignals__ = { 'chessfile_opened0': (GObject.SignalFlags.RUN_FIRST, None, (object, )), 'chessfile_opened': (GObject.SignalFlags.RUN_FIRST, None, (object, )), 'chessfile_closed': (GObject.SignalFlags.RUN_FIRST, None, ()), 'chessfile_imported': (GObject.SignalFlags.RUN_FIRST, None, (object, )), 'bookfile_created': (GObject.SignalFlags.RUN_FIRST, None, ()), } def __init__(self): GObject.GObject.__init__(self) Perspective.__init__(self, "database", _("Database")) self.widgets = gamewidget.getWidgets() self.first_run = True self.chessfile = None self.chessfiles = [] self.importer = None self.gamelists = [] self.filter_panels = [] self.opening_tree_panels = [] self.preview_panels = [] self.notebooks = {} self.page_dict = {} self.connect("chessfile_opened0", self.on_chessfile_opened0) self.dockLocation = addUserConfigPrefix("pydock-database.xml") @property def gamelist(self): if self.chessfile is None: return None else: return self.gamelists[self.chessfiles.index(self.chessfile)] @property def filter_panel(self): if self.chessfile is None: return None else: return self.filter_panels[self.chessfiles.index(self.chessfile)] @property def opening_tree_panel(self): if self.chessfile is None: return None else: return self.opening_tree_panels[self.chessfiles.index( self.chessfile)] @property def preview_panel(self): if self.chessfile is None: return None else: return self.preview_panels[self.chessfiles.index(self.chessfile)] def create_toolbuttons(self): self.import_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_CONVERT) self.import_button.set_tooltip_text(_("Import PGN file")) self.import_button.connect("clicked", self.on_import_clicked) self.save_as_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_SAVE_AS) self.save_as_button.set_tooltip_text(_("Save to PGN file as...")) self.save_as_button.connect("clicked", self.on_save_as_clicked) def init_layout(self): perspective_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) perspective_manager.set_perspective_widget("database", perspective_widget) self.notebooks = {"gamelist": new_notebook()} self.main_notebook = self.notebooks["gamelist"] for panel in self.sidePanels: self.notebooks[panel.__name__] = new_notebook(panel.__name__) self.spinner = Gtk.Spinner() self.spinner.set_size_request(50, 50) self.progressbar0 = Gtk.ProgressBar(show_text=True) self.progressbar = Gtk.ProgressBar(show_text=True) self.progress_dialog = Gtk.Dialog( "", mainwindow(), 0, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)) self.progress_dialog.set_deletable(False) self.progress_dialog.get_content_area().pack_start( self.spinner, True, True, 0) self.progress_dialog.get_content_area().pack_start( self.progressbar0, True, True, 0) self.progress_dialog.get_content_area().pack_start( self.progressbar, True, True, 0) self.progress_dialog.get_content_area().show_all() # Initing headbook align = createAlignment(4, 4, 0, 4) align.set_property("yscale", 0) self.headbook = Gtk.Notebook() self.headbook.set_name("headbook") self.headbook.set_scrollable(True) align.add(self.headbook) perspective_widget.pack_start(align, False, True, 0) self.headbook.connect_after("switch-page", self.on_switch_page) # The dock self.dock = PyDockTop("database", self) align = Gtk.Alignment() align.show() align.add(self.dock) self.dock.show() perspective_widget.pack_start(align, True, True, 0) self.docks["gamelist"] = (Gtk.Label(label="gamelist"), self.notebooks["gamelist"], None) for panel in self.sidePanels: self.docks[panel.__name__][1] = self.notebooks[panel.__name__] self.load_from_xml() # Default layout of side panels if not os.path.isfile(self.dockLocation): leaf = self.dock.dock(self.docks["gamelist"][1], CENTER, self.docks["gamelist"][0], "gamelist") leaf.setDockable(False) leaf = leaf.dock(self.docks["OpeningTreePanel"][1], EAST, self.docks["OpeningTreePanel"][0], "OpeningTreePanel") leaf = leaf.dock(self.docks["FilterPanel"][1], CENTER, self.docks["FilterPanel"][0], "FilterPanel") leaf.dock(self.docks["PreviewPanel"][1], SOUTH, self.docks["PreviewPanel"][0], "PreviewPanel") def unrealize(dock): dock.saveToXML(self.dockLocation) dock._del() self.dock.connect("unrealize", unrealize) self.dock.show_all() perspective_widget.show_all() perspective_manager.set_perspective_menuitems("database", self.menuitems) perspective_manager.set_perspective_toolbuttons( "database", [self.import_button, self.save_as_button]) def on_switch_page(self, notebook, page, page_num): if page in self.page_dict: self.chessfile = self.page_dict[page][0] i = self.chessfiles.index(self.chessfile) self.notebooks["gamelist"].set_current_page(i) self.notebooks["OpeningTreePanel"].set_current_page(i) self.notebooks["FilterPanel"].set_current_page(i) self.notebooks["PreviewPanel"].set_current_page(i) def set_sensitives(self, on): self.import_button.set_sensitive(on) self.widgets["import_chessfile"].set_sensitive(on) self.widgets["database_save_as"].set_sensitive(on) self.widgets["create_book"].set_sensitive(on) self.widgets["import_endgame_nl"].set_sensitive(on) self.widgets["import_twic"].set_sensitive(on) if on: gamewidget.getWidgets()["copy_pgn"].set_property('sensitive', on) gamewidget.getWidgets()["copy_fen"].set_property('sensitive', on) else: persp = perspective_manager.get_perspective("games") if persp.cur_gmwidg() is None: gamewidget.getWidgets()["copy_pgn"].set_property( 'sensitive', on) gamewidget.getWidgets()["copy_fen"].set_property( 'sensitive', on) def open_chessfile(self, filename): if self.first_run: self.init_layout() self.first_run = False perspective_manager.activate_perspective("database") self.progress_dialog.set_title(_("Open")) self.spinner.show() self.spinner.start() def opening(): if filename.endswith(".pgn"): GLib.idle_add(self.progressbar.show) GLib.idle_add(self.progressbar.set_text, _("Opening chessfile...")) chessfile = PGNFile(protoopen(filename), self.progressbar) self.importer = chessfile.init_tag_database() if self.importer is not None and self.importer.cancel: chessfile.tag_database.close() if os.path.isfile(chessfile.sqlite_path): os.remove(chessfile.sqlite_path) chessfile = None else: chessfile.init_scoutfish() chessfile.init_chess_db() elif filename.endswith(".epd"): self.importer = None chessfile = epd.load(protoopen(filename)) elif filename.endswith(".olv"): self.importer = None chessfile = olv.load(protoopen(filename, encoding="utf-8")) elif filename.endswith(".fen"): self.importer = None chessfile = fen.load(protoopen(filename)) else: self.importer = None chessfile = None GLib.idle_add(self.spinner.stop) GLib.idle_add(self.spinner.hide) GLib.idle_add(self.progress_dialog.hide) if chessfile is not None: self.chessfile = chessfile self.chessfiles.append(chessfile) GLib.idle_add(self.emit, "chessfile_opened0", chessfile) else: if self.chessfile is None: self.close(None) thread = threading.Thread(target=opening) thread.daemon = True thread.start() response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: if self.importer is not None: self.importer.do_cancel() self.progress_dialog.hide() def on_chessfile_opened0(self, persp, chessfile): page = Gtk.Alignment() tabcontent, close_button = self.get_tabcontent(chessfile) self.headbook.append_page(page, tabcontent) self.page_dict[page] = (chessfile, close_button) page.show_all() gamelist = GameList(self) self.gamelists.append(gamelist) opening_tree_panel = OpeningTreePanel(self) self.opening_tree_panels.append(opening_tree_panel) filter_panel = FilterPanel(self) self.filter_panels.append(filter_panel) preview_panel = PreviewPanel(self) self.preview_panels.append(preview_panel) self.notebooks["gamelist"].append_page(gamelist.box) self.notebooks["OpeningTreePanel"].append_page(opening_tree_panel.box) self.notebooks["FilterPanel"].append_page(filter_panel.box) self.notebooks["PreviewPanel"].append_page(preview_panel.box) self.headbook.set_current_page(self.headbook.get_n_pages() - 1) gamelist.load_games() opening_tree_panel.update_tree(load_games=False) self.set_sensitives(True) self.emit("chessfile_opened", chessfile) def close(self, close_button): for page in list(self.page_dict.keys()): if self.page_dict[page][1] == close_button: chessfile = self.page_dict[page][0] i = self.chessfiles.index(chessfile) self.notebooks["gamelist"].remove_page(i) self.notebooks["OpeningTreePanel"].remove_page(i) self.notebooks["FilterPanel"].remove_page(i) self.notebooks["PreviewPanel"].remove_page(i) del self.gamelists[i] del self.filter_panels[i] del self.chessfiles[i] chessfile.close() del self.page_dict[page] self.headbook.remove_page(self.headbook.page_num(page)) break if len(self.chessfiles) == 0: self.chessfile = None self.set_sensitives(False) perspective_manager.disable_perspective("database") self.emit("chessfile_closed") def on_import_endgame_nl(self): self.do_import(JvR) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_import_twic(self): LATEST = get_latest_twic() if LATEST is None: return html = "http://www.theweekinchess.com/html/twic%s.html" twic = [] pgn = "https://raw.githubusercontent.com/rozim/ChessData/master/Twic/fix-twic%s.pgn" # pgn = "/home/tamas/PGN/twic/twic%sg.zip" for i in range(210, 920): twic.append((html % i, pgn % i)) pgn = "http://www.theweekinchess.com/zips/twic%sg.zip" # pgn = "/home/tamas/PGN/twic/twic%sg.zip" for i in range(920, LATEST + 1): twic.append((html % i, pgn % i)) twic.append((html % LATEST, pgn % LATEST)) # import limited to latest twic .pgn for now twic = twic[-1:] self.do_import(twic) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_save_as_clicked(self, widget): dialog = Gtk.FileChooserDialog( _("Save as"), mainwindow(), Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT)) dialog.set_current_folder(os.path.expanduser("~")) response = dialog.run() if response == Gtk.ResponseType.ACCEPT: filename = dialog.get_filename() else: filename = None dialog.destroy() if filename is None: return self.progress_dialog.set_title(_("Save as")) def save_as(cancel_event): with open(filename, "w") as to_file: self.process_records(self.save_records, cancel_event, to_file) GLib.idle_add(self.progress_dialog.hide) cancel_event = threading.Event() loop = asyncio.get_event_loop() loop.run_in_executor(None, save_as, cancel_event) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: cancel_event.set() self.progress_dialog.hide() def save_records(self, records, to_file): f = self.chessfile.handle for i, rec in enumerate(records): offs = rec["Offset"] f.seek(offs) game = '' for line in f: if line.startswith('[Event "'): if game: break # Second one, start of next game else: game = line # First occurence elif game: game += line to_file.write(game) def on_import_clicked(self, widget): dialog = Gtk.FileChooserDialog( _("Open chess file"), mainwindow(), Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog.set_select_multiple(True) filter_text = Gtk.FileFilter() filter_text.set_name(".pgn") filter_text.add_pattern("*.pgn") filter_text.add_mime_type("application/x-chess-pgn") dialog.add_filter(filter_text) filter_text = Gtk.FileFilter() filter_text.set_name(".zip") filter_text.add_pattern("*.zip") filter_text.add_mime_type("application/zip") dialog.add_filter(filter_text) response = dialog.run() if response == Gtk.ResponseType.OK: filenames = dialog.get_filenames() else: filenames = None dialog.destroy() if filenames is not None: self.do_import(filenames) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() # @profile_me def importing(self, filenames): drop_indexes(self.chessfile.engine) self.importer = PgnImport(self.chessfile, append_pgn=True) self.importer.initialize() for i, filename in enumerate(filenames): if len(filenames) > 1: GLib.idle_add(self.progressbar0.set_fraction, i / float(len(filenames))) if self.importer.cancel: break if isinstance(filename, tuple): info_link, pgn_link = filename self.importer.do_import(pgn_link, info=info_link, progressbar=self.progressbar) else: self.importer.do_import(filename, progressbar=self.progressbar) GLib.idle_add(self.progressbar.set_text, _("Recreating indexes...")) # .sqlite create_indexes(self.chessfile.engine) # .scout self.chessfile.init_scoutfish() # .bin self.chessfile.init_chess_db() self.chessfile.set_tag_filter(None) self.chessfile.set_fen_filter(None) self.chessfile.set_scout_filter(None) GLib.idle_add(self.gamelist.load_games) GLib.idle_add(self.emit, "chessfile_imported", self.chessfile) GLib.idle_add(self.progressbar0.hide) GLib.idle_add(self.progress_dialog.hide) def do_import(self, filenames): self.progress_dialog.set_title(_("Import")) if len(filenames) > 1: self.progressbar0.show() self.progressbar.show() self.progressbar.set_text(_("Preparing to start import...")) thread = threading.Thread(target=self.importing, args=(filenames, )) thread.daemon = True thread.start() def process_records(self, callback, cancel_event, *args): counter = 0 records, plys = self.chessfile.get_records(FIRST_PAGE) callback(records, *args) GLib.idle_add(self.progressbar.set_text, _("%s games processed") % counter) while not cancel_event.is_set(): records, plys = self.chessfile.get_records(NEXT_PAGE) if records: callback(records, *args) counter += len(records) GLib.idle_add(self.progressbar.set_text, _("%s games processed") % counter) else: break def create_book(self, new_bin=None): if new_bin is None: dialog = Gtk.FileChooserDialog( _("Create New Polyglot Opening Book"), mainwindow(), Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_NEW, Gtk.ResponseType.ACCEPT)) dialog.set_current_folder(os.path.expanduser("~")) dialog.set_current_name("new_book.bin") response = dialog.run() if response == Gtk.ResponseType.ACCEPT: new_bin = dialog.get_filename() if not new_bin.endswith(".bin"): new_bin = "%s.bin" % new_bin dialog.destroy() if new_bin is None: return self.progress_dialog.set_title(_("Create Polyglot Book")) def creating_book(cancel_event): positions = {} self.process_records(self.feed_book, cancel_event, positions) if cancel_event.is_set(): return with open(new_bin, "wb") as to_file: GLib.idle_add(self.progressbar.set_text, _("Save")) for key, moves in sorted(positions.items(), key=lambda item: item[0]): # print(key, moves) for move in moves: to_file.write(pack(">QHHI", key, move, moves[move], 0)) GLib.idle_add(self.emit, "bookfile_created") GLib.idle_add(self.progress_dialog.hide) cancel_event = threading.Event() loop = asyncio.get_event_loop() loop.run_in_executor(None, creating_book, cancel_event) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: cancel_event.set() self.progress_dialog.hide() def feed_book(self, records, positions): for rec in records: model = GameModel() if rec["Result"] == DRAW: score = (1, 1) elif rec["Result"] == WHITEWON: score = (2, 0) elif rec["Result"] == BLACKWON: score = (0, 2) else: score = (0, 0) fenstr = rec["FEN"] variant = self.chessfile.get_variant(rec) if variant: model.variant = name2variant[variant] board = LBoard(model.variant.variant) else: model.variant = NormalBoard board = LBoard() if fenstr: try: board.applyFen(fenstr) except SyntaxError as err: continue else: board.applyFen(FEN_START) boards = [board] movetext = self.chessfile.get_movetext(rec) boards = self.chessfile.parse_movetext(movetext, boards[0], -1) for board in boards: if board.plyCount > BOOK_DEPTH: break move = board.lastMove if move is not None: poly_move = toPolyglot(board.prev, move) # move_str = "%s%s" % (reprCord[FCORD(move)], reprCord[TCORD(move)]) # print("%0.16x" % board.prev.hash, poly_move, board.prev.asFen(), move_str) if board.prev.hash in positions: if poly_move in positions[board.prev.hash]: positions[board.prev.hash][poly_move] += score[ board.prev.color] else: positions[board.prev.hash][poly_move] = score[ board.prev.color] else: # board.prev.asFen(), move_str, positions[board.prev.hash] = { poly_move: score[board.prev.color] } def create_database(self): dialog = Gtk.FileChooserDialog( _("Create New Pgn Database"), mainwindow(), Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_NEW, Gtk.ResponseType.ACCEPT)) dialog.set_current_folder(os.path.expanduser("~")) dialog.set_current_name("new.pgn") response = dialog.run() if response == Gtk.ResponseType.ACCEPT: new_pgn = dialog.get_filename() if not new_pgn.endswith(".pgn"): new_pgn = "%s.pgn" % new_pgn if not os.path.isfile(new_pgn): # create new file with open(new_pgn, "w"): pass self.open_chessfile(new_pgn) else: d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK) d.set_markup( _("<big><b>File '%s' already exists.</b></big>") % new_pgn) d.run() d.destroy() dialog.destroy() def get_tabcontent(self, chessfile): tabcontent = createAlignment(0, 0, 0, 0) hbox = Gtk.HBox() hbox.set_spacing(4) hbox.pack_start(createImage(pgn_icon), False, True, 0) close_button = Gtk.Button() close_button.set_property("can-focus", False) close_button.add(createImage(gtk_close)) close_button.set_relief(Gtk.ReliefStyle.NONE) close_button.set_size_request(20, 18) close_button.connect("clicked", self.close) hbox.pack_end(close_button, False, True, 0) name, ext = os.path.splitext(chessfile.path) basename = os.path.basename(name) info = "%s.%s" % (basename, ext[1:]) tooltip = _("%(path)s\ncontaining %(count)s games") % ( { "path": chessfile.path, "count": chessfile.count }) tabcontent.set_tooltip_text(tooltip) label = Gtk.Label(info) hbox.pack_start(label, False, True, 0) tabcontent.add(hbox) tabcontent.show_all() return tabcontent, close_button
class Database(GObject.GObject, Perspective): __gsignals__ = { 'chessfile_opened0': (GObject.SignalFlags.RUN_FIRST, None, (object, )), 'chessfile_opened': (GObject.SignalFlags.RUN_FIRST, None, (object, )), 'chessfile_closed': (GObject.SignalFlags.RUN_FIRST, None, ()), 'chessfile_imported': (GObject.SignalFlags.RUN_FIRST, None, (object, )), } def __init__(self): GObject.GObject.__init__(self) Perspective.__init__(self, "database", _("Database")) self.widgets = gamewidget.getWidgets() self.first_run = True self.chessfile = None self.chessfiles = [] self.importer = None self.gamelists = [] self.filter_panels = [] self.opening_tree_panels = [] self.preview_panels = [] self.notebooks = {} self.page_dict = {} self.connect("chessfile_opened0", self.on_chessfile_opened0) self.dockLocation = addUserConfigPrefix("pydock-database.xml") @property def gamelist(self): if self.chessfile is None: return None else: return self.gamelists[self.chessfiles.index(self.chessfile)] @property def filter_panel(self): if self.chessfile is None: return None else: return self.filter_panels[self.chessfiles.index(self.chessfile)] @property def opening_tree_panel(self): if self.chessfile is None: return None else: return self.opening_tree_panels[self.chessfiles.index( self.chessfile)] @property def preview_panel(self): if self.chessfile is None: return None else: return self.preview_panels[self.chessfiles.index(self.chessfile)] def create_toolbuttons(self): self.import_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_CONVERT) self.import_button.set_tooltip_text(_("Import PGN file")) self.import_button.connect("clicked", self.on_import_clicked) self.save_as_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_SAVE_AS) self.save_as_button.set_tooltip_text(_("Save to PGN file as...")) self.save_as_button.connect("clicked", self.on_save_as_clicked) def init_layout(self): perspective_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) perspective_manager.set_perspective_widget("database", perspective_widget) self.notebooks = {"gamelist": new_notebook()} self.main_notebook = self.notebooks["gamelist"] for panel in self.sidePanels: self.notebooks[panel.__name__] = new_notebook(panel.__name__) self.spinner = Gtk.Spinner() self.spinner.set_size_request(50, 50) self.progressbar0 = Gtk.ProgressBar(show_text=True) self.progressbar1 = Gtk.ProgressBar(show_text=True) self.progress_dialog = Gtk.Dialog( "", mainwindow(), 0, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)) self.progress_dialog.set_deletable(False) self.progress_dialog.get_content_area().pack_start( self.spinner, True, True, 0) self.progress_dialog.get_content_area().pack_start( self.progressbar0, True, True, 0) self.progress_dialog.get_content_area().pack_start( self.progressbar1, True, True, 0) self.progress_dialog.get_content_area().show_all() # Initing headbook align = createAlignment(4, 4, 0, 4) align.set_property("yscale", 0) self.headbook = Gtk.Notebook() self.headbook.set_name("headbook") self.headbook.set_scrollable(True) align.add(self.headbook) perspective_widget.pack_start(align, False, True, 0) self.headbook.connect_after("switch-page", self.on_switch_page) # The dock self.dock = PyDockTop("database", self) align = Gtk.Alignment() align.show() align.add(self.dock) self.dock.show() perspective_widget.pack_start(align, True, True, 0) self.docks["gamelist"] = (Gtk.Label(label="gamelist"), self.notebooks["gamelist"], None) for panel in self.sidePanels: self.docks[panel.__name__][1] = self.notebooks[panel.__name__] self.load_from_xml() # Default layout of side panels if not os.path.isfile(self.dockLocation): leaf = self.dock.dock(self.docks["gamelist"][1], CENTER, self.docks["gamelist"][0], "gamelist") leaf.setDockable(False) leaf = leaf.dock(self.docks["OpeningTreePanel"][1], EAST, self.docks["OpeningTreePanel"][0], "OpeningTreePanel") leaf = leaf.dock(self.docks["FilterPanel"][1], CENTER, self.docks["FilterPanel"][0], "FilterPanel") leaf.dock(self.docks["PreviewPanel"][1], SOUTH, self.docks["PreviewPanel"][0], "PreviewPanel") def unrealize(dock): dock.saveToXML(self.dockLocation) dock._del() self.dock.connect("unrealize", unrealize) self.dock.show_all() perspective_widget.show_all() perspective_manager.set_perspective_menuitems("database", self.menuitems) perspective_manager.set_perspective_toolbuttons( "database", [self.import_button, self.save_as_button]) def on_switch_page(self, notebook, page, page_num): if page in self.page_dict: self.chessfile = self.page_dict[page][0] i = self.chessfiles.index(self.chessfile) self.notebooks["gamelist"].set_current_page(i) self.notebooks["OpeningTreePanel"].set_current_page(i) self.notebooks["FilterPanel"].set_current_page(i) self.notebooks["PreviewPanel"].set_current_page(i) def set_sensitives(self, on): self.import_button.set_sensitive(on) self.widgets["import_chessfile"].set_sensitive(on) self.widgets["database_save_as"].set_sensitive(on) self.widgets["import_endgame_nl"].set_sensitive(on) self.widgets["import_twic"].set_sensitive(on) def open_chessfile(self, filename): if self.first_run: self.init_layout() self.first_run = False perspective_manager.activate_perspective("database") self.progress_dialog.set_title(_("Open")) self.progressbar0.hide() self.spinner.show() self.spinner.start() def opening(): if filename.endswith(".pgn"): GLib.idle_add(self.progressbar1.show) GLib.idle_add(self.progressbar1.set_text, _("Opening chessfile...")) chessfile = PGNFile(protoopen(filename), self.progressbar1) self.importer = PgnImport(chessfile) chessfile.init_tag_database(self.importer) if self.importer.cancel: chessfile.tag_database.close() if os.path.isfile(chessfile.sqlite_path): os.remove(chessfile.sqlite_path) chessfile = None else: chessfile.init_scoutfish() chessfile.init_chess_db() elif filename.endswith(".epd"): self.importer = None chessfile = epd.load(protoopen(filename)) elif filename.endswith(".fen"): self.importer = None chessfile = fen.load(protoopen(filename)) else: self.importer = None chessfile = None GLib.idle_add(self.spinner.stop) GLib.idle_add(self.progress_dialog.hide) if chessfile is not None: self.chessfile = chessfile self.chessfiles.append(chessfile) GLib.idle_add(self.emit, "chessfile_opened0", chessfile) else: if self.chessfile is None: self.close(None) thread = threading.Thread(target=opening) thread.daemon = True thread.start() response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: if self.importer is not None: self.importer.do_cancel() self.progress_dialog.hide() def on_chessfile_opened0(self, persp, chessfile): page = Gtk.Alignment() tabcontent, close_button = self.get_tabcontent(chessfile) self.headbook.append_page(page, tabcontent) self.page_dict[page] = (chessfile, close_button) page.show_all() gamelist = GameList(self) self.gamelists.append(gamelist) opening_tree_panel = OpeningTreePanel(self) self.opening_tree_panels.append(opening_tree_panel) filter_panel = FilterPanel(self) self.filter_panels.append(filter_panel) preview_panel = PreviewPanel(self) self.preview_panels.append(preview_panel) self.notebooks["gamelist"].append_page(gamelist.box) self.notebooks["OpeningTreePanel"].append_page(opening_tree_panel.box) self.notebooks["FilterPanel"].append_page(filter_panel.box) self.notebooks["PreviewPanel"].append_page(preview_panel.box) self.headbook.set_current_page(self.headbook.get_n_pages() - 1) gamelist.load_games() opening_tree_panel.update_tree(load_games=False) self.set_sensitives(True) self.emit("chessfile_opened", chessfile) def close(self, close_button): for page in list(self.page_dict.keys()): if self.page_dict[page][1] == close_button: chessfile = self.page_dict[page][0] i = self.chessfiles.index(chessfile) self.notebooks["gamelist"].remove_page(i) self.notebooks["OpeningTreePanel"].remove_page(i) self.notebooks["FilterPanel"].remove_page(i) self.notebooks["PreviewPanel"].remove_page(i) del self.gamelists[i] del self.filter_panels[i] del self.chessfiles[i] chessfile.close() del self.page_dict[page] self.headbook.remove_page(self.headbook.page_num(page)) break if len(self.chessfiles) == 0: self.set_sensitives(False) perspective_manager.disable_perspective("database") self.emit("chessfile_closed") def on_import_endgame_nl(self): self.do_import(JvR) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_import_twic(self): LATEST = get_latest_twic() if LATEST is None: return html = "http://www.theweekinchess.com/html/twic%s.html" twic = [] pgn = "https://raw.githubusercontent.com/rozim/ChessData/master/Twic/fix-twic%s.pgn" # pgn = "/home/tamas/PGN/twic/twic%sg.zip" for i in range(210, 920): twic.append((html % i, pgn % i)) pgn = "http://www.theweekinchess.com/zips/twic%sg.zip" # pgn = "/home/tamas/PGN/twic/twic%sg.zip" for i in range(920, LATEST + 1): twic.append((html % i, pgn % i)) twic.append((html % LATEST, pgn % LATEST)) # import limited to latest twic .pgn for now twic = twic[-1:] self.do_import(twic) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_save_as_clicked(self, widget): dialog = Gtk.FileChooserDialog( _("Save as"), mainwindow(), Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT)) dialog.set_current_folder(os.path.expanduser("~")) response = dialog.run() if response == Gtk.ResponseType.ACCEPT: filename = dialog.get_filename() else: filename = None dialog.destroy() if filename is not None: with open(filename, "w") as to_file: records, plys = self.chessfile.get_records(FIRST_PAGE) self.save_records(records, to_file) while True: records, plys = self.chessfile.get_records(NEXT_PAGE) if records: self.save_records(records, to_file) else: break def save_records(self, records, to_file): f = self.chessfile.handle for i, rec in enumerate(records): offs = rec["Offset"] f.seek(offs) game = '' for line in f: if line.startswith('[Event "'): if game: break # Second one, start of next game else: game = line # First occurence elif game: game += line to_file.write(game) def on_import_clicked(self, widget): dialog = Gtk.FileChooserDialog( _("Open chess file"), mainwindow(), Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog.set_select_multiple(True) filter_text = Gtk.FileFilter() filter_text.set_name(".pgn") filter_text.add_pattern("*.pgn") filter_text.add_mime_type("application/x-chess-pgn") dialog.add_filter(filter_text) filter_text = Gtk.FileFilter() filter_text.set_name(".zip") filter_text.add_pattern("*.zip") filter_text.add_mime_type("application/zip") dialog.add_filter(filter_text) response = dialog.run() if response == Gtk.ResponseType.OK: filenames = dialog.get_filenames() else: filenames = None dialog.destroy() if filenames is not None: self.do_import(filenames) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() # @profile_me def importing(self, filenames): drop_indexes(self.chessfile.engine) self.importer = PgnImport(self.chessfile, append_pgn=True) self.importer.initialize() for i, filename in enumerate(filenames): GLib.idle_add(self.progressbar0.set_fraction, i / float(len(filenames))) # GLib.idle_add(self.progressbar0.set_text, filename) if self.importer.cancel: break if isinstance(filename, tuple): info_link, pgn_link = filename self.importer.do_import(pgn_link, info=info_link, progressbar=self.progressbar1) else: self.importer.do_import(filename, progressbar=self.progressbar1) GLib.idle_add(self.progressbar1.set_text, _("Recreating indexes...")) # .sqlite create_indexes(self.chessfile.engine) # .scout self.chessfile.init_scoutfish() # .bin self.chessfile.init_chess_db() self.chessfile.set_tag_filter(None) self.chessfile.set_fen_filter(None) self.chessfile.set_scout_filter(None) GLib.idle_add(self.gamelist.load_games) GLib.idle_add(self.emit, "chessfile_imported", self.chessfile) GLib.idle_add(self.progress_dialog.hide) def do_import(self, filenames): self.progress_dialog.set_title(_("Import")) self.spinner.hide() if len(filenames) == 1: self.progressbar0.hide() else: self.progressbar0.show() self.progressbar1.show() self.progressbar1.set_text(_("Preparing to start import...")) thread = threading.Thread(target=self.importing, args=(filenames, )) thread.daemon = True thread.start() def create_database(self): dialog = Gtk.FileChooserDialog( _("Create New Pgn Database"), mainwindow(), Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_NEW, Gtk.ResponseType.ACCEPT)) dialog.set_current_folder(os.path.expanduser("~")) dialog.set_current_name("new.pgn") response = dialog.run() if response == Gtk.ResponseType.ACCEPT: new_pgn = dialog.get_filename() if not new_pgn.endswith(".pgn"): new_pgn = "%s.pgn" % new_pgn if not os.path.isfile(new_pgn): # create new file with open(new_pgn, "w"): pass self.open_chessfile(new_pgn) else: d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK) d.set_markup( _("<big><b>File '%s' already exists.</b></big>") % new_pgn) d.run() d.hide() print("%s allready exist." % new_pgn) dialog.destroy() def get_tabcontent(self, chessfile): tabcontent = createAlignment(0, 0, 0, 0) hbox = Gtk.HBox() hbox.set_spacing(4) hbox.pack_start(createImage(pgn_icon), False, True, 0) close_button = Gtk.Button() close_button.set_property("can-focus", False) close_button.add(createImage(gtk_close)) close_button.set_relief(Gtk.ReliefStyle.NONE) close_button.set_size_request(20, 18) close_button.connect("clicked", self.close) hbox.pack_end(close_button, False, True, 0) name, ext = os.path.splitext(chessfile.path) basename = os.path.basename(name) info = "%s.%s" % (basename, ext[1:]) tooltip = "%s\ncontaining %s games" % (chessfile.path, chessfile.count) tabcontent.set_tooltip_text(tooltip) label = Gtk.Label(info) hbox.pack_start(label, False, True, 0) tabcontent.add(hbox) tabcontent.show_all() return tabcontent, close_button
class Learn(GObject.GObject, Perspective): def __init__(self): GObject.GObject.__init__(self) Perspective.__init__(self, "learn", _("Learn")) self.always_on = True self.dockLocation = addUserConfigPrefix("pydock-learn.xml") self.first_run = True def create_toolbuttons(self): def on_exit_clicked(button): perspective_manager.disable_perspective("learn") self.exit_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_QUIT) self.exit_button.set_tooltip_text(_("Quit Learning")) self.exit_button.set_label("exit") self.exit_button.connect("clicked", on_exit_clicked) def init_layout(self): perspective_manager.set_perspective_toolbuttons("learn", (self.exit_button, )) perspective_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) perspective_manager.set_perspective_widget("learn", perspective_widget) perspective_manager.set_perspective_menuitems("learn", self.menuitems) self.notebooks = {"home": new_notebook()} self.main_notebook = self.notebooks["home"] for panel in self.sidePanels: self.notebooks[panel.__name__] = new_notebook(panel.__name__) self.dock = PyDockTop("learn", self) align = Gtk.Alignment() align.show() align.add(self.dock) self.dock.show() perspective_widget.pack_start(align, True, True, 0) self.notebooks = {"learnhome": new_notebook()} self.main_notebook = self.notebooks["learnhome"] for panel in self.sidePanels: self.notebooks[panel.__name__] = new_notebook(panel.__name__) self.docks["learnhome"] = (Gtk.Label(label="learnhome"), self.notebooks["learnhome"], None) for panel in self.sidePanels: self.docks[panel.__name__][1] = self.notebooks[panel.__name__] self.load_from_xml() # Default layout of side panels if not os.path.isfile(self.dockLocation): leaf0 = self.dock.dock(self.docks["learnhome"][1], CENTER, self.docks["learnhome"][0], "learnhome") leaf0.setDockable(False) leaf = leaf0.dock(self.docks["PuzzlesPanel"][1], WEST, self.docks["PuzzlesPanel"][0], "PuzzlesPanel") leaf.dock(self.docks["LessonsPanel"][1], SOUTH, self.docks["LessonsPanel"][0], "LessonsPanel") leaf = leaf0.dock(self.docks["LecturesPanel"][1], SOUTH, self.docks["LecturesPanel"][0], "LecturesPanel") leaf.dock(self.docks["EndgamesPanel"][1], SOUTH, self.docks["EndgamesPanel"][0], "EndgamesPanel") def unrealize(dock): dock.saveToXML(self.dockLocation) dock._del() self.dock.connect("unrealize", unrealize) self.dock.show_all() perspective_widget.show_all() log.debug("Learn.__init__: finished") def activate(self): if self.first_run: self.init_layout() self.first_run = False learn_home = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) box = Gtk.Box() self.tv = Gtk.TreeView() color = Gdk.RGBA() color.parse("lightblue") for i, col in enumerate((_("lichess"), _("wtharvey"), _("yacpdb"), _("lessons"))): renderer = Gtk.CellRendererProgress() renderer.set_orientation(Gtk.Orientation.VERTICAL) renderer.props.height = 100 renderer.props.inverted = True renderer.props.cell_background_rgba = color column = Gtk.TreeViewColumn(col, renderer, text=i * 2, value=i * 2 + 1) self.tv.append_column(column) self.store = Gtk.ListStore(str, int, str, int, str, int, str, int) self.update_progress(None, None, None) self.tv.set_model(self.store) self.tv.get_selection().set_mode(Gtk.SelectionMode.NONE) puzzles_solving_progress.connect("progress_updated", self.update_progress) lessons_solving_progress.connect("progress_updated", self.update_progress) box.pack_start(self.tv, False, False, 6) label = Gtk.Label(xpad=6, xalign=0) label.set_markup("<b>%s</b>" % _("Progress")) learn_home.pack_start(label, False, False, 6) learn_home.pack_start(box, False, False, 0) learn_home.show_all() if not self.first_run: self.notebooks["learnhome"].remove_page(-1) self.notebooks["learnhome"].append_page(learn_home) self.panels = [panel.Sidepanel().load(self) for panel in self.sidePanels] for panel, instance in zip(self.sidePanels, self.panels): if not self.first_run: self.notebooks[panel.__name__].remove_page(-1) self.notebooks[panel.__name__].append_page(instance) instance.show() perspective_manager.activate_perspective("learn") def update_progress(self, solving_progress, key, progress): self.store.clear() # Compute cumulative puzzles solving statistics solving_progress = puzzles_solving_progress.read_all() stat = [0, 0, 0, 0, 0, 0, 0, 0] for filename, progress in solving_progress.items(): if filename.startswith("lichess"): stat[0] += len(progress) stat[1] += progress.count(1) elif filename.startswith("mate_in"): stat[2] += len(progress) stat[3] += progress.count(1) elif filename.endswith(".olv"): stat[4] += len(progress) stat[5] += progress.count(1) # Compute cumulative lessons solving statistics solving_progress = lessons_solving_progress.read_all() for filename, progress in solving_progress.items(): stat[6] += len(progress) stat[7] += progress.count(1) stats = [] for i in range(4): percent = 0 if not stat[i * 2 + 1] else round((stat[i * 2 + 1] * 100.) / stat[i * 2]) stats.append("%s%%" % percent) stats.append(percent) self.store.append(stats)
class Database(GObject.GObject, Perspective): __gsignals__ = { 'chessfile_opened': (GObject.SignalFlags.RUN_FIRST, None, (object, )), 'chessfile_closed': (GObject.SignalFlags.RUN_FIRST, None, ()), 'chessfile_imported': (GObject.SignalFlags.RUN_FIRST, None, (object, )), 'chessfile_switched': (GObject.SignalFlags.RUN_FIRST, None, (object, )), } def __init__(self): GObject.GObject.__init__(self) Perspective.__init__(self, "database", _("Database")) self.gamelist = None def create_toolbuttons(self): self.import_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_CONVERT) self.import_button.set_tooltip_text(_("Import PGN file")) self.import_button.connect("clicked", self.on_import_clicked) self.close_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_CLOSE) self.close_button.set_tooltip_text(_("Close")) self.close_button.connect("clicked", self.close) def init_layout(self): perspective_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) perspective_manager.set_perspective_widget("database", perspective_widget) self.gamelist = GameList(database.load(None)) self.switcher_panel = SwitcherPanel(self.gamelist) self.opening_tree_panel = OpeningTreePanel(self.gamelist) self.filter_panel = FilterPanel(self.gamelist) self.preview_panel = PreviewPanel(self.gamelist) self.progressbar0 = Gtk.ProgressBar(show_text=True) self.progressbar = Gtk.ProgressBar(show_text=True) self.progress_dialog = Gtk.Dialog("Import", None, 0, ( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)) self.progress_dialog.get_content_area().pack_start(self.progressbar0, True, True, 0) self.progress_dialog.get_content_area().pack_start(self.progressbar, True, True, 0) self.progress_dialog.get_content_area().show_all() perspective = perspective_manager.get_perspective("database") self.dock = PyDockTop("database", perspective) align = Gtk.Alignment() align.show() align.add(self.dock) self.dock.show() perspective_widget.pack_start(align, True, True, 0) dockLocation = addUserConfigPrefix("pydock-database.xml") docks = { "gamelist": (Gtk.Label(label="gamelist"), self.gamelist.box), "switcher": (dock_panel_tab(_("Database switcher"), "", addDataPrefix("glade/panel_docker.svg")), self.switcher_panel.alignment), "openingtree": (dock_panel_tab(_("Opening tree"), "", addDataPrefix("glade/panel_docker.svg")), self.opening_tree_panel.box), "filter": (dock_panel_tab(_("Filter"), "", addDataPrefix("glade/panel_docker.svg")), self.filter_panel.box), "preview": (dock_panel_tab(_("Preview"), "", addDataPrefix("glade/panel_docker.svg")), self.preview_panel.box), } if os.path.isfile(dockLocation): try: self.dock.loadFromXML(dockLocation, docks) except Exception as e: stringio = StringIO() traceback.print_exc(file=stringio) error = stringio.getvalue() log.error("Dock loading error: %s\n%s" % (e, error)) msg_dia = Gtk.MessageDialog(None, type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.CLOSE) msg_dia.set_markup(_( "<b><big>PyChess was unable to load your panel settings</big></b>")) msg_dia.format_secondary_text(_( "Your panel settings have been reset. If this problem repeats, \ you should report it to the developers")) msg_dia.run() msg_dia.hide() os.remove(dockLocation) for title, panel in docks.values(): title.unparent() panel.unparent() if not os.path.isfile(dockLocation): leaf = self.dock.dock(docks["gamelist"][1], CENTER, docks["gamelist"][0], "gamelist") leaf.setDockable(False) leaf.dock(docks["switcher"][1], NORTH, docks["switcher"][0], "switcher") leaf = leaf.dock(docks["filter"][1], EAST, docks["filter"][0], "filter") leaf = leaf.dock(docks["openingtree"][1], SOUTH, docks["openingtree"][0], "openingtree") leaf.dock(docks["preview"][1], SOUTH, docks["preview"][0], "preview") def unrealize(dock): dock.saveToXML(dockLocation) dock._del() self.dock.connect("unrealize", unrealize) self.dock.show_all() perspective_widget.show_all() perspective_manager.set_perspective_toolbuttons("database", [self.import_button, self.close_button]) self.switcher_panel.connect("chessfile_switched", self.on_chessfile_switched) def open_chessfile(self, filename): if filename.endswith(".pdb"): chessfile = database.load(filename) elif filename.endswith(".pgn"): chessfile = pgn.load(protoopen(filename)) elif filename.endswith(".epd"): chessfile = epd.load(protoopen(filename)) elif filename.endswith(".fen"): chessfile = fen.load(protoopen(filename)) else: return if self.gamelist is None: self.init_layout() perspective_manager.activate_perspective("database") self.emit("chessfile_opened", chessfile) def close(self, widget): self.emit("chessfile_closed") def on_chessfile_switched(self, switcher, chessfile): self.emit("chessfile_switched", chessfile) def on_import_endgame_nl(self): self.do_import(JvR) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_import_twic(self): LATEST = get_latest_twic() if LATEST is None: return html = "http://www.theweekinchess.com/html/twic%s.html" twic = [] pgn = "https://raw.githubusercontent.com/rozim/ChessData/master/Twic/fix-twic%s.pgn" # pgn = "/home/tamas/PGN/twic/twic%sg.zip" for i in range(210, 920): twic.append((html % i, pgn % i)) pgn = "http://www.theweekinchess.com/zips/twic%sg.zip" # pgn = "/home/tamas/PGN/twic/twic%sg.zip" for i in range(920, LATEST + 1): twic.append((html % i, pgn % i)) twic.append((html % LATEST, pgn % LATEST)) # TODO: importing all twic .pgn files creates a huge (~8Gb) .pdb # and sqlite seems too slow to handle my current selects # until I find better solution import will be limited to latest twic .pgn twic = twic[-1:] self.do_import(twic) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_update_players(self): def importing(): self.importer = FIDEPlayersImport(self.gamelist.chessfile.engine) self.importer.import_players(progressbar=self.progressbar) GLib.idle_add(self.progress_dialog.hide) thread = threading.Thread(target=importing) thread.daemon = True thread.start() response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_import_clicked(self, widget): dialog = Gtk.FileChooserDialog( _("Open chess file"), None, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog.set_select_multiple(True) filter_text = Gtk.FileFilter() filter_text.set_name(".pgn") filter_text.add_pattern("*.pgn") filter_text.add_mime_type("application/x-chess-pgn") dialog.add_filter(filter_text) filter_text = Gtk.FileFilter() filter_text.set_name(".zip") filter_text.add_pattern("*.zip") filter_text.add_mime_type("application/zip") dialog.add_filter(filter_text) dialog = NestedFileChooserDialog(dialog) filenames = dialog.run() if filenames is not None: self.do_import(filenames) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def do_import(self, filenames): if len(filenames) == 1: self.progressbar0.hide() else: self.progressbar0.show() self.progressbar.set_text("Preparing to start import...") # @profile_me def importing(): drop_indexes(self.gamelist.chessfile.engine) self.importer = PgnImport(self.gamelist.chessfile.engine) for i, filename in enumerate(filenames): filename = unicode(filename) GLib.idle_add(self.progressbar0.set_fraction, i / float(len(filenames))) # GLib.idle_add(self.progressbar0.set_text, filename) if self.importer.cancel: break if isinstance(filename, tuple): info_link, pgn_link = filename self.importer.do_import(pgn_link, info=info_link, progressbar=self.progressbar) else: self.importer.do_import(filename, progressbar=self.progressbar) GLib.idle_add(self.progressbar.set_text, "Recreating indexes...") create_indexes(self.gamelist.chessfile.engine) self.gamelist.offset = 0 self.gamelist.chessfile.build_where_tags("") self.gamelist.chessfile.build_where_bitboards(0, 0) self.gamelist.chessfile.build_query() self.gamelist.chessfile.update_count() self.gamelist.chessfile.update_count_stats() GLib.idle_add(self.gamelist.load_games) GLib.idle_add(self.emit, "chessfile_imported", self.gamelist.chessfile) GLib.idle_add(self.progress_dialog.hide) thread = threading.Thread(target=importing) thread.daemon = True thread.start()
class ChatWindow(object): def __init__(self, widgets, connection): self.connection = connection self.window = None widgets["show_chat_button"].connect("clicked", self.showChat) connection.cm.connect("privateMessage", self.onPersonMessage) connection.connect("disconnected", self.onDisconnected) self.viewspanel = ViewsPanel(self.connection) self.channelspanel = ChannelsPanel(self.connection) self.infopanel = InfoPanel(self.connection) self.panels = [self.viewspanel, self.channelspanel, self.infopanel] self.viewspanel.connect('channel_content_Changed', self.channelspanel.channel_Highlight, id) @idle_add def onDisconnected(self, conn): if self.window: self.window.hide() def showChat(self, *widget): if not self.window: self.initUi() self.window.show_all() self.window.present() def initUi(self): self.window = Gtk.Window() self.window.set_border_width(12) self.window.set_icon_name("pychess") self.window.set_title("PyChess - Internet Chess Chat") self.window.connect_after("delete-event", lambda w, e: w.hide() or True) uistuff.keepWindowSize("chat", self.window, defaultSize=(650, 400)) self.dock = PyDockTop("icchat") self.dock.show() self.window.add(self.dock) leaf = self.dock.dock(self.viewspanel, CENTER, Gtk.Label(label="chat"), "chat") leaf.setDockable(False) self.channelspanel.connect('conversationAdded', self.onConversationAdded) self.channelspanel.connect('conversationRemoved', self.onConversationRemoved) self.channelspanel.connect('conversationSelected', self.onConversationSelected) leaf.dock(self.channelspanel, WEST, Gtk.Label(label=_("Conversations")), "conversations") leaf.dock(self.infopanel, EAST, Gtk.Label(label=_("Conversation info")), "info") for panel in self.panels: panel.show_all() panel.start() def onConversationAdded(self, panel, grp_id, text, grp_type): chatView = ChatView() plus_channel = '+channel ' + str(grp_id) self.connection.cm.connection.client.run_command(plus_channel) for panel in self.panels: panel.addItem(grp_id, text, grp_type, chatView) def onConversationRemoved(self, panel, grp_id): minus_channel = '-channel ' + str(grp_id) self.connection.cm.connection.client.run_command(minus_channel) for panel in self.panels: panel.removeItem(grp_id) def onConversationSelected(self, panel, grp_id): for panel in self.panels: panel.selectItem(grp_id) @idle_add def onPersonMessage(self, cm, name, title, isadmin, text): console_active = False for window in Gtk.Window.list_toplevels(): if window.is_active(): window_icon_name = window.get_icon_name() if window_icon_name is not None and "pychess" in window_icon_name: console_active = True break if self.connection.bm.isPlaying() or console_active: if not self.window: self.initUi() else: self.showChat() self.window.set_urgency_hint(True) self.initial_focus_id = self.window.connect( "focus-in-event", self.on_initial_focus_in) def on_initial_focus_in(self, widget, event): self.window.set_urgency_hint(False) self.window.disconnect(self.initial_focus_id) return False def openChatWithPlayer(self, name): self.showChat() self.window.get_window().raise_() cm = self.connection.cm self.onPersonMessage(cm, name, "", False, "") self.channelspanel.onPersonMessage(cm, name, "", False, "")
class Database(GObject.GObject, Perspective): __gsignals__ = { 'chessfile_opened0': (GObject.SignalFlags.RUN_FIRST, None, (object, )), 'chessfile_opened': (GObject.SignalFlags.RUN_FIRST, None, (object, )), 'chessfile_closed': (GObject.SignalFlags.RUN_FIRST, None, ()), 'chessfile_imported': (GObject.SignalFlags.RUN_FIRST, None, (object, )), } def __init__(self): GObject.GObject.__init__(self) Perspective.__init__(self, "database", _("Database")) self.widgets = gamewidget.getWidgets() self.chessfile = None self.chessfiles = [] self.gamelists = [] self.filter_panels = [] self.notebooks = {} self.connect("chessfile_opened0", self.on_chessfile_opened0) @property def gamelist(self): if self.chessfile is None: return None else: return self.gamelists[self.chessfiles.index(self.chessfile)] @property def filter_panel(self): if self.chessfile is None: return None else: return self.filter_panels[self.chessfiles.index(self.chessfile)] def create_toolbuttons(self): self.import_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_CONVERT) self.import_button.set_tooltip_text(_("Import PGN file")) self.import_button.connect("clicked", self.on_import_clicked) self.close_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_CLOSE) self.close_button.set_tooltip_text(_("Close")) self.close_button.connect("clicked", self.close) def init_layout(self): perspective_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) perspective_manager.set_perspective_widget("database", perspective_widget) self.switcher_panel = SwitcherPanel(self) self.notebooks["gamelist"] = new_notebook() self.notebooks["opening_tree"] = new_notebook() self.notebooks["filter"] = new_notebook() self.notebooks["preview"] = new_notebook() self.spinner = Gtk.Spinner() self.spinner.set_size_request(50, 50) self.progressbar0 = Gtk.ProgressBar(show_text=True) self.progressbar1 = Gtk.ProgressBar(show_text=True) mainwindow = gamewidget.getWidgets()["main_window"] self.progress_dialog = Gtk.Dialog("", mainwindow, 0, ( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)) self.progress_dialog.get_content_area().pack_start(self.spinner, True, True, 0) self.progress_dialog.get_content_area().pack_start(self.progressbar0, True, True, 0) self.progress_dialog.get_content_area().pack_start(self.progressbar1, True, True, 0) self.progress_dialog.get_content_area().show_all() self.dock = PyDockTop("database", self) align = Gtk.Alignment() align.show() align.add(self.dock) self.dock.show() perspective_widget.pack_start(align, True, True, 0) dockLocation = addUserConfigPrefix("pydock-database.xml") docks = { "gamelist": (Gtk.Label(label="gamelist"), self.notebooks["gamelist"]), "switcher": (dock_panel_tab(_("Databases"), "", addDataPrefix("glade/panel_database.svg")), self.switcher_panel.alignment), "openingtree": (dock_panel_tab(_("Openings"), "", addDataPrefix("glade/panel_book.svg")), self.notebooks["opening_tree"]), "filter": (dock_panel_tab(_("Filters"), "", addDataPrefix("glade/panel_filter.svg")), self.notebooks["filter"]), "preview": (dock_panel_tab(_("Preview"), "", addDataPrefix("glade/panel_games.svg")), self.notebooks["preview"]), } if os.path.isfile(dockLocation): try: self.dock.loadFromXML(dockLocation, docks) except Exception as e: stringio = StringIO() traceback.print_exc(file=stringio) error = stringio.getvalue() log.error("Dock loading error: %s\n%s" % (e, error)) msg_dia = Gtk.MessageDialog(None, type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.CLOSE) msg_dia.set_markup(_( "<b><big>PyChess was unable to load your panel settings</big></b>")) msg_dia.format_secondary_text(_( "Your panel settings have been reset. If this problem repeats, \ you should report it to the developers")) msg_dia.run() msg_dia.hide() os.remove(dockLocation) for title, panel in docks.values(): title.unparent() panel.unparent() if not os.path.isfile(dockLocation): leaf = self.dock.dock(docks["gamelist"][1], CENTER, docks["gamelist"][0], "gamelist") leaf.setDockable(False) leaf.dock(docks["switcher"][1], NORTH, docks["switcher"][0], "switcher") leaf = leaf.dock(docks["openingtree"][1], EAST, docks["openingtree"][0], "openingtree") leaf = leaf.dock(docks["filter"][1], CENTER, docks["filter"][0], "filter") leaf.dock(docks["preview"][1], SOUTH, docks["preview"][0], "preview") def unrealize(dock): dock.saveToXML(dockLocation) dock._del() self.dock.connect("unrealize", unrealize) self.dock.show_all() perspective_widget.show_all() perspective_manager.set_perspective_toolbuttons("database", [self.import_button, self.close_button]) self.switcher_panel.connect("chessfile_switched", self.on_chessfile_switched) def set_sensitives(self, on): self.import_button.set_sensitive(on) self.widgets["import_chessfile"].set_sensitive(on) self.widgets["import_endgame_nl"].set_sensitive(on) self.widgets["import_twic"].set_sensitive(on) def open_chessfile(self, filename): if self.chessfile is None: self.init_layout() perspective_manager.activate_perspective("database") self.progress_dialog.set_title(_("Open")) self.progressbar0.hide() self.progressbar1.show() self.progressbar1.set_text("Importing game headers...") self.spinner.show() self.spinner.start() def opening(): if filename.endswith(".pgn"): chessfile = pgn.load(protoopen(filename), self.progressbar1) elif filename.endswith(".epd"): chessfile = epd.load(protoopen(filename)) elif filename.endswith(".fen"): chessfile = fen.load(protoopen(filename)) else: chessfile = None GLib.idle_add(self.spinner.stop) GLib.idle_add(self.progress_dialog.hide) if chessfile is not None: self.chessfile = chessfile self.chessfiles.append(chessfile) GLib.idle_add(self.emit, "chessfile_opened0", chessfile) thread = threading.Thread(target=opening) thread.daemon = True thread.start() response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_chessfile_opened0(self, persp, chessfile): gamelist = GameList(self) self.gamelists.append(gamelist) opening_tree_panel = OpeningTreePanel(self) filter_panel = FilterPanel(self) self.filter_panels.append(filter_panel) preview_panel = PreviewPanel(self) self.notebooks["gamelist"].append_page(gamelist.box) self.notebooks["opening_tree"].append_page(opening_tree_panel.box) self.notebooks["filter"].append_page(filter_panel.box) self.notebooks["preview"].append_page(preview_panel.box) self.on_chessfile_switched(None, self.chessfile) gamelist.load_games() opening_tree_panel.update_tree(load_games=False) self.set_sensitives(True) self.emit("chessfile_opened", chessfile) def close(self, widget): i = self.chessfiles.index(self.chessfile) if self.chessfile.path is not None: self.notebooks["gamelist"].remove_page(i) self.notebooks["opening_tree"].remove_page(i) self.notebooks["filter"].remove_page(i) self.notebooks["preview"].remove_page(i) del self.gamelists[i] del self.filter_panels[i] del self.chessfiles[i] self.chessfile.close() if len(self.chessfiles) == 0: self.set_sensitives(False) perspective_manager.disable_perspective("database") self.emit("chessfile_closed") def on_chessfile_switched(self, switcher, chessfile): self.chessfile = chessfile i = self.chessfiles.index(chessfile) self.notebooks["gamelist"].set_current_page(i) self.notebooks["opening_tree"].set_current_page(i) self.notebooks["filter"].set_current_page(i) self.notebooks["preview"].set_current_page(i) def on_import_endgame_nl(self): self.do_import(JvR) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_import_twic(self): LATEST = get_latest_twic() if LATEST is None: return html = "http://www.theweekinchess.com/html/twic%s.html" twic = [] pgn = "https://raw.githubusercontent.com/rozim/ChessData/master/Twic/fix-twic%s.pgn" # pgn = "/home/tamas/PGN/twic/twic%sg.zip" for i in range(210, 920): twic.append((html % i, pgn % i)) pgn = "http://www.theweekinchess.com/zips/twic%sg.zip" # pgn = "/home/tamas/PGN/twic/twic%sg.zip" for i in range(920, LATEST + 1): twic.append((html % i, pgn % i)) twic.append((html % LATEST, pgn % LATEST)) # import limited to latest twic .pgn for now twic = twic[-1:] self.do_import(twic) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_import_clicked(self, widget): dialog = Gtk.FileChooserDialog( _("Open chess file"), None, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog.set_select_multiple(True) filter_text = Gtk.FileFilter() filter_text.set_name(".pgn") filter_text.add_pattern("*.pgn") filter_text.add_mime_type("application/x-chess-pgn") dialog.add_filter(filter_text) filter_text = Gtk.FileFilter() filter_text.set_name(".zip") filter_text.add_pattern("*.zip") filter_text.add_mime_type("application/zip") dialog.add_filter(filter_text) response = dialog.run() if response == Gtk.ResponseType.OK: filenames = dialog.get_filenames() else: filenames = None dialog.destroy() if filenames is not None: self.do_import(filenames) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def do_import(self, filenames): self.progress_dialog.set_title(_("Import")) self.spinner.hide() if len(filenames) == 1: self.progressbar0.hide() else: self.progressbar0.show() self.progressbar1.show() self.progressbar1.set_text("Preparing to start import...") # @profile_me def importing(): drop_indexes(self.chessfile.engine) self.importer = PgnImport(self.chessfile, append_pgn=True) for i, filename in enumerate(filenames): GLib.idle_add(self.progressbar0.set_fraction, i / float(len(filenames))) # GLib.idle_add(self.progressbar0.set_text, filename) if self.importer.cancel: break if isinstance(filename, tuple): info_link, pgn_link = filename self.importer.do_import(pgn_link, info=info_link, progressbar=self.progressbar1) else: self.importer.do_import(filename, progressbar=self.progressbar1) GLib.idle_add(self.progressbar1.set_text, "Recreating indexes...") # .sqlite create_indexes(self.chessfile.engine) # .scout self.chessfile.init_scoutfish() # .bin self.chessfile.init_chess_db() self.chessfile.set_tag_filter(None) self.chessfile.set_fen_filter(None) self.chessfile.set_scout_filter(None) GLib.idle_add(self.gamelist.load_games) GLib.idle_add(self.emit, "chessfile_imported", self.chessfile) GLib.idle_add(self.progress_dialog.hide) thread = threading.Thread(target=importing) thread.daemon = True thread.start() def create_database(self): dialog = Gtk.FileChooserDialog( _("Create New Pgn Database"), None, Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_NEW, Gtk.ResponseType.ACCEPT)) dialog.set_current_folder(os.path.expanduser("~")) dialog.set_current_name("new.pgn") response = dialog.run() if response == Gtk.ResponseType.ACCEPT: new_pgn = dialog.get_filename() if not new_pgn.endswith(".pgn"): new_pgn = "%s.pgn" % new_pgn if not os.path.isfile(new_pgn): # create new file with open(new_pgn, "w"): pass self.open_chessfile(new_pgn) else: d = Gtk.MessageDialog(type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK) d.set_markup(_("<big><b>File '%s' already exists.</b></big>") % new_pgn) d.run() d.hide() print("%s allready exist." % new_pgn) dialog.destroy()
class Database(GObject.GObject, Perspective): __gsignals__ = { 'chessfile_opened0': (GObject.SignalFlags.RUN_FIRST, None, (object, )), 'chessfile_opened': (GObject.SignalFlags.RUN_FIRST, None, (object, )), 'chessfile_closed': (GObject.SignalFlags.RUN_FIRST, None, ()), 'chessfile_imported': (GObject.SignalFlags.RUN_FIRST, None, (object, )), } def __init__(self): GObject.GObject.__init__(self) Perspective.__init__(self, "database", _("Database")) self.widgets = gamewidget.getWidgets() self.chessfile = None self.chessfiles = [] self.gamelists = [] self.filter_panels = [] self.notebooks = {} self.connect("chessfile_opened0", self.on_chessfile_opened0) @property def gamelist(self): if self.chessfile is None: return None else: return self.gamelists[self.chessfiles.index(self.chessfile)] @property def filter_panel(self): if self.chessfile is None: return None else: return self.filter_panels[self.chessfiles.index(self.chessfile)] def create_toolbuttons(self): self.import_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_CONVERT) self.import_button.set_tooltip_text(_("Import PGN file")) self.import_button.connect("clicked", self.on_import_clicked) self.save_as_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_SAVE_AS) self.save_as_button.set_tooltip_text(_("Save to PGN file as...")) self.save_as_button.connect("clicked", self.on_save_as_clicked) self.close_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_CLOSE) self.close_button.set_tooltip_text(_("Close")) self.close_button.connect("clicked", self.close) def init_layout(self): perspective_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) perspective_manager.set_perspective_widget("database", perspective_widget) self.switcher_panel = SwitcherPanel(self) self.notebooks["gamelist"] = new_notebook() self.notebooks["opening_tree"] = new_notebook() self.notebooks["filter"] = new_notebook() self.notebooks["preview"] = new_notebook() self.spinner = Gtk.Spinner() self.spinner.set_size_request(50, 50) self.progressbar0 = Gtk.ProgressBar(show_text=True) self.progressbar1 = Gtk.ProgressBar(show_text=True) self.progress_dialog = Gtk.Dialog( "", mainwindow(), 0, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)) self.progress_dialog.get_content_area().pack_start( self.spinner, True, True, 0) self.progress_dialog.get_content_area().pack_start( self.progressbar0, True, True, 0) self.progress_dialog.get_content_area().pack_start( self.progressbar1, True, True, 0) self.progress_dialog.get_content_area().show_all() self.dock = PyDockTop("database", self) align = Gtk.Alignment() align.show() align.add(self.dock) self.dock.show() perspective_widget.pack_start(align, True, True, 0) dockLocation = addUserConfigPrefix("pydock-database.xml") docks = { "gamelist": (Gtk.Label(label="gamelist"), self.notebooks["gamelist"]), "switcher": (dock_panel_tab(_("Databases"), "", addDataPrefix("glade/panel_database.svg")), self.switcher_panel.alignment), "openingtree": (dock_panel_tab(_("Openings"), "", addDataPrefix("glade/panel_book.svg")), self.notebooks["opening_tree"]), "filter": (dock_panel_tab(_("Filters"), "", addDataPrefix("glade/panel_filter.svg")), self.notebooks["filter"]), "preview": (dock_panel_tab(_("Preview"), "", addDataPrefix("glade/panel_games.svg")), self.notebooks["preview"]), } if os.path.isfile(dockLocation): try: self.dock.loadFromXML(dockLocation, docks) except Exception as e: stringio = StringIO() traceback.print_exc(file=stringio) error = stringio.getvalue() log.error("Dock loading error: %s\n%s" % (e, error)) msg_dia = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.CLOSE) msg_dia.set_markup( _("<b><big>PyChess was unable to load your panel settings</big></b>" )) msg_dia.format_secondary_text( _("Your panel settings have been reset. If this problem repeats, \ you should report it to the developers")) msg_dia.run() msg_dia.hide() os.remove(dockLocation) for title, panel in docks.values(): title.unparent() panel.unparent() if not os.path.isfile(dockLocation): leaf = self.dock.dock(docks["gamelist"][1], CENTER, docks["gamelist"][0], "gamelist") leaf.setDockable(False) leaf.dock(docks["switcher"][1], NORTH, docks["switcher"][0], "switcher") leaf = leaf.dock(docks["openingtree"][1], EAST, docks["openingtree"][0], "openingtree") leaf = leaf.dock(docks["filter"][1], CENTER, docks["filter"][0], "filter") leaf.dock(docks["preview"][1], SOUTH, docks["preview"][0], "preview") def unrealize(dock): dock.saveToXML(dockLocation) dock._del() self.dock.connect("unrealize", unrealize) self.dock.show_all() perspective_widget.show_all() perspective_manager.set_perspective_toolbuttons( "database", [self.import_button, self.save_as_button, self.close_button]) self.switcher_panel.connect("chessfile_switched", self.on_chessfile_switched) def set_sensitives(self, on): self.import_button.set_sensitive(on) self.widgets["import_chessfile"].set_sensitive(on) self.widgets["database_save_as"].set_sensitive(on) self.widgets["import_endgame_nl"].set_sensitive(on) self.widgets["import_twic"].set_sensitive(on) def open_chessfile(self, filename): if self.chessfile is None: self.init_layout() perspective_manager.activate_perspective("database") self.progress_dialog.set_title(_("Open")) self.progressbar0.hide() self.progressbar1.show() self.progressbar1.set_text("Importing game headers...") self.spinner.show() self.spinner.start() def opening(): if filename.endswith(".pgn"): chessfile = pgn.load(protoopen(filename), self.progressbar1) elif filename.endswith(".epd"): chessfile = epd.load(protoopen(filename)) elif filename.endswith(".fen"): chessfile = fen.load(protoopen(filename)) else: chessfile = None GLib.idle_add(self.spinner.stop) GLib.idle_add(self.progress_dialog.hide) if chessfile is not None: self.chessfile = chessfile self.chessfiles.append(chessfile) GLib.idle_add(self.emit, "chessfile_opened0", chessfile) thread = threading.Thread(target=opening) thread.daemon = True thread.start() response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_chessfile_opened0(self, persp, chessfile): gamelist = GameList(self) self.gamelists.append(gamelist) opening_tree_panel = OpeningTreePanel(self) filter_panel = FilterPanel(self) self.filter_panels.append(filter_panel) preview_panel = PreviewPanel(self) self.notebooks["gamelist"].append_page(gamelist.box) self.notebooks["opening_tree"].append_page(opening_tree_panel.box) self.notebooks["filter"].append_page(filter_panel.box) self.notebooks["preview"].append_page(preview_panel.box) self.on_chessfile_switched(None, self.chessfile) gamelist.load_games() opening_tree_panel.update_tree(load_games=False) self.set_sensitives(True) self.emit("chessfile_opened", chessfile) def close(self, widget): i = self.chessfiles.index(self.chessfile) if self.chessfile.path is not None: self.notebooks["gamelist"].remove_page(i) self.notebooks["opening_tree"].remove_page(i) self.notebooks["filter"].remove_page(i) self.notebooks["preview"].remove_page(i) del self.gamelists[i] del self.filter_panels[i] del self.chessfiles[i] self.chessfile.close() if len(self.chessfiles) == 0: self.set_sensitives(False) perspective_manager.disable_perspective("database") self.emit("chessfile_closed") def on_chessfile_switched(self, switcher, chessfile): self.chessfile = chessfile i = self.chessfiles.index(chessfile) self.notebooks["gamelist"].set_current_page(i) self.notebooks["opening_tree"].set_current_page(i) self.notebooks["filter"].set_current_page(i) self.notebooks["preview"].set_current_page(i) def on_import_endgame_nl(self): self.do_import(JvR) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_import_twic(self): LATEST = get_latest_twic() if LATEST is None: return html = "http://www.theweekinchess.com/html/twic%s.html" twic = [] pgn = "https://raw.githubusercontent.com/rozim/ChessData/master/Twic/fix-twic%s.pgn" # pgn = "/home/tamas/PGN/twic/twic%sg.zip" for i in range(210, 920): twic.append((html % i, pgn % i)) pgn = "http://www.theweekinchess.com/zips/twic%sg.zip" # pgn = "/home/tamas/PGN/twic/twic%sg.zip" for i in range(920, LATEST + 1): twic.append((html % i, pgn % i)) twic.append((html % LATEST, pgn % LATEST)) # import limited to latest twic .pgn for now twic = twic[-1:] self.do_import(twic) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_save_as_clicked(self, widget): dialog = Gtk.FileChooserDialog( "", mainwindow(), Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT)) dialog.set_current_folder(os.path.expanduser("~")) response = dialog.run() if response == Gtk.ResponseType.ACCEPT: filename = dialog.get_filename() else: filename = None dialog.destroy() if filename is not None: with open(filename, "w") as to_file: records, plys = self.chessfile.get_records(FIRST_PAGE) self.save_records(records, to_file) while True: records, plys = self.chessfile.get_records(NEXT_PAGE) if records: self.save_records(records, to_file) else: break def save_records(self, records, to_file): f = self.chessfile.handle for i, rec in enumerate(records): offs = rec["Offset"] f.seek(offs) game = '' for line in f: if line.startswith('[Event "'): if game: break # Second one, start of next game else: game = line # First occurence elif game: game += line to_file.write(game) def on_import_clicked(self, widget): dialog = Gtk.FileChooserDialog( _("Open chess file"), mainwindow(), Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog.set_select_multiple(True) filter_text = Gtk.FileFilter() filter_text.set_name(".pgn") filter_text.add_pattern("*.pgn") filter_text.add_mime_type("application/x-chess-pgn") dialog.add_filter(filter_text) filter_text = Gtk.FileFilter() filter_text.set_name(".zip") filter_text.add_pattern("*.zip") filter_text.add_mime_type("application/zip") dialog.add_filter(filter_text) response = dialog.run() if response == Gtk.ResponseType.OK: filenames = dialog.get_filenames() else: filenames = None dialog.destroy() if filenames is not None: self.do_import(filenames) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def do_import(self, filenames): self.progress_dialog.set_title(_("Import")) self.spinner.hide() if len(filenames) == 1: self.progressbar0.hide() else: self.progressbar0.show() self.progressbar1.show() self.progressbar1.set_text("Preparing to start import...") # @profile_me def importing(): drop_indexes(self.chessfile.engine) self.importer = PgnImport(self.chessfile, append_pgn=True) for i, filename in enumerate(filenames): GLib.idle_add(self.progressbar0.set_fraction, i / float(len(filenames))) # GLib.idle_add(self.progressbar0.set_text, filename) if self.importer.cancel: break if isinstance(filename, tuple): info_link, pgn_link = filename self.importer.do_import(pgn_link, info=info_link, progressbar=self.progressbar1) else: self.importer.do_import(filename, progressbar=self.progressbar1) GLib.idle_add(self.progressbar1.set_text, "Recreating indexes...") # .sqlite create_indexes(self.chessfile.engine) # .scout self.chessfile.init_scoutfish() # .bin self.chessfile.init_chess_db() self.chessfile.set_tag_filter(None) self.chessfile.set_fen_filter(None) self.chessfile.set_scout_filter(None) GLib.idle_add(self.gamelist.load_games) GLib.idle_add(self.emit, "chessfile_imported", self.chessfile) GLib.idle_add(self.progress_dialog.hide) thread = threading.Thread(target=importing) thread.daemon = True thread.start() def create_database(self): dialog = Gtk.FileChooserDialog( _("Create New Pgn Database"), mainwindow(), Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_NEW, Gtk.ResponseType.ACCEPT)) dialog.set_current_folder(os.path.expanduser("~")) dialog.set_current_name("new.pgn") response = dialog.run() if response == Gtk.ResponseType.ACCEPT: new_pgn = dialog.get_filename() if not new_pgn.endswith(".pgn"): new_pgn = "%s.pgn" % new_pgn if not os.path.isfile(new_pgn): # create new file with open(new_pgn, "w"): pass self.open_chessfile(new_pgn) else: d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK) d.set_markup( _("<big><b>File '%s' already exists.</b></big>") % new_pgn) d.run() d.hide() print("%s allready exist." % new_pgn) dialog.destroy()
class Learn(GObject.GObject, Perspective): def __init__(self): GObject.GObject.__init__(self) Perspective.__init__(self, "learn", _("Learn")) self.always_on = True self.dockLocation = addUserConfigPrefix("pydock-learn.xml") self.first_run = True def create_toolbuttons(self): def on_exit_clicked(button): perspective_manager.disable_perspective("learn") self.exit_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_QUIT) self.exit_button.set_tooltip_text(_("Quit Learning")) self.exit_button.set_label("exit") self.exit_button.connect("clicked", on_exit_clicked) def init_layout(self): perspective_manager.set_perspective_toolbuttons("learn", (self.exit_button, )) perspective_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) perspective_manager.set_perspective_widget("learn", perspective_widget) perspective_manager.set_perspective_menuitems("learn", self.menuitems) self.notebooks = {"home": new_notebook()} self.main_notebook = self.notebooks["home"] for panel in self.sidePanels: self.notebooks[panel.__name__] = new_notebook(panel.__name__) self.dock = PyDockTop("learn", self) align = Gtk.Alignment() align.show() align.add(self.dock) self.dock.show() perspective_widget.pack_start(align, True, True, 0) self.notebooks = {"learnhome": new_notebook()} self.main_notebook = self.notebooks["learnhome"] for panel in self.sidePanels: self.notebooks[panel.__name__] = new_notebook(panel.__name__) self.docks["learnhome"] = (Gtk.Label(label="learnhome"), self.notebooks["learnhome"], None) for panel in self.sidePanels: self.docks[panel.__name__][1] = self.notebooks[panel.__name__] self.load_from_xml() # Default layout of side panels if not os.path.isfile(self.dockLocation): leaf = self.dock.dock(self.docks["learnhome"][1], CENTER, self.docks["learnhome"][0], "learnhome") leaf.setDockable(False) leaf.dock(self.docks["PuzzlesPanel"][1], WEST, self.docks["PuzzlesPanel"][0], "PuzzlesPanel") leaf = leaf.dock(self.docks["LecturesPanel"][1], SOUTH, self.docks["LecturesPanel"][0], "LecturesPanel") leaf = leaf.dock(self.docks["LessonsPanel"][1], SOUTH, self.docks["LessonsPanel"][0], "LessonsPanel") leaf.dock(self.docks["EndgamesPanel"][1], SOUTH, self.docks["EndgamesPanel"][0], "EndgamesPanel") def unrealize(dock): dock.saveToXML(self.dockLocation) dock._del() self.dock.connect("unrealize", unrealize) self.dock.show_all() perspective_widget.show_all() log.debug("Learn.__init__: finished") def activate(self): if self.first_run: self.init_layout() self.first_run = False learn_home = Gtk.Box() learn_home.add(Gtk.Label("Practice! Practice! Practice!")) learn_home.show_all() if not self.first_run: self.notebooks["learnhome"].remove_page(-1) self.notebooks["learnhome"].append_page(learn_home) self.panels = [panel.Sidepanel().load(self) for panel in self.sidePanels] for panel, instance in zip(self.sidePanels, self.panels): if not self.first_run: self.notebooks[panel.__name__].remove_page(-1) self.notebooks[panel.__name__].append_page(instance) instance.show() perspective_manager.activate_perspective("learn")
class FICS(GObject.GObject, Perspective): __gsignals__ = { 'logout': (GObject.SignalFlags.RUN_FIRST, None, ()), 'autoLogout': (GObject.SignalFlags.RUN_FIRST, None, ()), } def __init__(self): log.debug("FICS.__init__: starting") GObject.GObject.__init__(self) Perspective.__init__(self, "fics", _("ICS")) self.dockLocation = addUserConfigPrefix("pydock-fics.xml") self.first_run = True def create_toolbuttons(self): def on_logoff_clicked(button): self.emit("logout") self.close() self.logoff_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_DISCONNECT) self.logoff_button.set_tooltip_text(_("Log Off")) self.logoff_button.set_label("logoff") self.logoff_button.connect("clicked", on_logoff_clicked) def on_minute_1_clicked(button): self.connection.client.run_command("1-minute") def on_minute_3_clicked(button): self.connection.client.run_command("3-minute") def on_minute_5_clicked(button): self.connection.client.run_command("5-minute") def on_minute_15_clicked(button): self.connection.client.run_command("15-minute") def on_minute_25_clicked(button): self.connection.client.run_command("25-minute") def on_chess960_clicked(button): self.connection.client.run_command("chess960") self.minute_1_button = Gtk.ToggleToolButton() self.minute_1_button.set_label("1") self.minute_1_button.set_tooltip_text(_("New game from 1-minute playing pool")) self.minute_1_button.connect("clicked", on_minute_1_clicked) self.minute_3_button = Gtk.ToggleToolButton() self.minute_3_button.set_label("3") self.minute_3_button.set_tooltip_text(_("New game from 3-minute playing pool")) self.minute_3_button.connect("clicked", on_minute_3_clicked) self.minute_5_button = Gtk.ToggleToolButton() self.minute_5_button.set_label("5") self.minute_5_button.set_tooltip_text(_("New game from 5-minute playing pool")) self.minute_5_button.connect("clicked", on_minute_5_clicked) self.minute_15_button = Gtk.ToggleToolButton() self.minute_15_button.set_label("15") self.minute_15_button.set_tooltip_text(_("New game from 15-minute playing pool")) self.minute_15_button.connect("clicked", on_minute_15_clicked) self.minute_25_button = Gtk.ToggleToolButton() self.minute_25_button.set_label("25") self.minute_25_button.set_tooltip_text(_("New game from 25-minute playing pool")) self.minute_25_button.connect("clicked", on_minute_25_clicked) self.chess960_button = Gtk.ToggleToolButton() self.chess960_button.set_label("960") self.chess960_button.set_tooltip_text(_("New game from Chess960 playing pool")) self.chess960_button.connect("clicked", on_chess960_clicked) def init_layout(self): perspective_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) perspective_manager.set_perspective_widget("fics", perspective_widget) perspective_manager.set_perspective_menuitems("fics", self.menuitems) self.infobar = InfoBarNotebook("fics_lounge_infobar") self.infobar.hide() perspective_widget.pack_start(self.infobar, False, False, 0) self.dock = PyDockTop("fics", self) align = Gtk.Alignment() align.show() align.add(self.dock) self.dock.show() perspective_widget.pack_start(align, True, True, 0) self.notebooks = {"ficshome": new_notebook()} self.main_notebook = self.notebooks["ficshome"] for panel in self.sidePanels: self.notebooks[panel_name(panel.__name__)] = new_notebook(panel_name(panel.__name__)) self.docks["ficshome"] = (Gtk.Label(label="ficshome"), self.notebooks["ficshome"], None) for panel in self.sidePanels: self.docks[panel_name(panel.__name__)][1] = self.notebooks[panel_name(panel.__name__)] self.load_from_xml() # Default layout of side panels if not os.path.isfile(self.dockLocation): leaf = self.dock.dock(self.docks["ficshome"][1], CENTER, self.docks["ficshome"][0], "ficshome") leaf.setDockable(False) console_leaf = leaf.dock(self.docks["ConsolePanel"][1], SOUTH, self.docks["ConsolePanel"][0], "ConsolePanel") console_leaf.dock(self.docks["NewsPanel"][1], CENTER, self.docks["NewsPanel"][0], "NewsPanel") seek_leaf = leaf.dock(self.docks["SeekListPanel"][1], WEST, self.docks["SeekListPanel"][0], "SeekListPanel") seek_leaf.dock(self.docks["SeekGraphPanel"][1], CENTER, self.docks["SeekGraphPanel"][0], "SeekGraphPanel") seek_leaf.dock(self.docks["PlayerListPanel"][1], CENTER, self.docks["PlayerListPanel"][0], "PlayerListPanel") seek_leaf.dock(self.docks["GameListPanel"][1], CENTER, self.docks["GameListPanel"][0], "GameListPanel") seek_leaf.dock(self.docks["ArchiveListPanel"][1], CENTER, self.docks["ArchiveListPanel"][0], "ArchiveListPanel") leaf = leaf.dock(self.docks["ChatPanel"][1], SOUTH, self.docks["ChatPanel"][0], "ChatPanel") # leaf.dock(self.docks["LecturesPanel"][1], CENTER, self.docks["LecturesPanel"][0], "LecturesPanel") def unrealize(dock): dock.saveToXML(self.dockLocation) dock._del() self.dock.connect("unrealize", unrealize) self.dock.show_all() perspective_widget.show_all() log.debug("FICS.__init__: finished") def open_lounge(self, connection, helperconn, host): if self.first_run: self.init_layout() self.connection = connection self.helperconn = helperconn self.host = host self.finger_sent = False self.messages = [] self.players = [] self.game_cids = {} self.widgets = uistuff.GladeWidgets("fics_lounge.glade") self.widgets["fics_lounge"].hide() fics_home = self.widgets["fics_home"] self.widgets["fics_lounge_content_hbox"].remove(fics_home) self.archive_list = self.widgets["archiveListContent"] self.widgets["fics_panels_notebook"].remove(self.archive_list) self.games_list = self.widgets["gamesListContent"] self.widgets["fics_panels_notebook"].remove(self.games_list) self.news_list = self.widgets["news"] self.widgets["fics_home"].remove(self.news_list) self.players_list = self.widgets["playersListContent"] self.widgets["fics_panels_notebook"].remove(self.players_list) self.seek_graph = self.widgets["seekGraphContent"] self.widgets["fics_panels_notebook"].remove(self.seek_graph) self.seek_list = self.widgets["seekListContent"] self.widgets["fics_panels_notebook"].remove(self.seek_list) self.seek_challenge = SeekChallengeSection(self) def on_autoLogout(alm): self.emit("autoLogout") self.close() self.connection.alm.connect("logOut", on_autoLogout) self.connection.connect("disconnected", lambda connection: self.close()) self.connection.connect("error", self.on_connection_error) if self.connection.isRegistred(): numtimes = conf.get("numberOfTimesLoggedInAsRegisteredUser") + 1 conf.set("numberOfTimesLoggedInAsRegisteredUser", numtimes) self.connection.em.connect("onCommandNotFound", lambda em, cmd: log.error( "Fics answered '%s': Command not found" % cmd)) self.connection.bm.connect("playGameCreated", self.onPlayGameCreated) self.connection.bm.connect("obsGameCreated", self.onObserveGameCreated) self.connection.bm.connect("exGameCreated", self.onObserveGameCreated) self.connection.fm.connect("fingeringFinished", self.onFinger) # the rest of these relay server messages to the lounge infobar self.connection.bm.connect("tooManySeeks", self.tooManySeeks) self.connection.bm.connect("nonoWhileExamine", self.nonoWhileExamine) self.connection.bm.connect("matchDeclined", self.matchDeclined) self.connection.bm.connect("player_on_censor", self.player_on_censor) self.connection.bm.connect("player_on_noplay", self.player_on_noplay) self.connection.bm.connect("req_not_fit_formula", self.req_not_fit_formula) self.connection.glm.connect("seek-updated", self.on_seek_updated) self.connection.glm.connect("our-seeks-removed", self.our_seeks_removed) self.connection.cm.connect("arrivalNotification", self.onArrivalNotification) self.connection.cm.connect("departedNotification", self.onDepartedNotification) def get_top_games(): if perspective_manager.current_perspective == self: self.connection.client.run_command("games *19") return True if self.connection.ICC: self.event_id = GLib.timeout_add_seconds(5, get_top_games) for user in self.connection.notify_users: user = self.connection.players.get(user) self.user_from_notify_list_is_present(user) self.userinfo = UserInfoSection(self.widgets, self.connection, self.host, self) if not self.first_run: self.notebooks["ficshome"].remove_page(-1) self.notebooks["ficshome"].append_page(fics_home) self.panels = [panel.Sidepanel().load(self.widgets, self.connection, self) for panel in self.sidePanels] for panel, instance in zip(self.sidePanels, self.panels): if not self.first_run: self.notebooks[panel_name(panel.__name__)].remove_page(-1) self.notebooks[panel_name(panel.__name__)].append_page(instance) instance.show() tool_buttons = [self.logoff_button, ] self.quick_seek_buttons = [] if self.connection.ICC: self.quick_seek_buttons = [self.minute_1_button, self.minute_3_button, self.minute_5_button, self.minute_15_button, self.minute_25_button, self.chess960_button] tool_buttons += self.quick_seek_buttons perspective_manager.set_perspective_toolbuttons("fics", tool_buttons) if self.first_run: self.first_run = False # After all panel is set up we can push initial messages out self.connection.com.onConsoleMessage("", self.connection.ini_messages) def show(self): perspective_manager.activate_perspective("fics") def present(self): perspective_manager.activate_perspective("fics") def on_connection_error(self, connection, error): log.warning("FICS.on_connection_error: %s" % repr(error)) self.close() def close(self): try: self.widgets = None except TypeError: pass except AttributeError: pass perspective_manager.disable_perspective("fics") def onPlayGameCreated(self, bm, ficsgame): log.debug("FICS.onPlayGameCreated: %s" % ficsgame) for message in self.messages: message.dismiss() del self.messages[:] if self.connection.ICC: for button in self.quick_seek_buttons: button.set_active(False) timemodel = TimeModel(ficsgame.minutes * 60, ficsgame.inc) gamemodel = ICGameModel(self.connection, ficsgame, timemodel) gamemodel.connect("game_started", self.onGameModelStarted, ficsgame) wplayer, bplayer = ficsgame.wplayer, ficsgame.bplayer # We start if wplayer.name.lower() == self.connection.getUsername().lower(): player0tup = (LOCAL, Human, (WHITE, wplayer.long_name(), wplayer.name, wplayer.getRatingForCurrentGame()), wplayer.long_name()) player1tup = (REMOTE, ICPlayer, ( gamemodel, bplayer.name, ficsgame.gameno, BLACK, bplayer.long_name(), bplayer.getRatingForCurrentGame()), bplayer.long_name()) # She starts else: player1tup = (LOCAL, Human, (BLACK, bplayer.long_name(), bplayer.name, bplayer.getRatingForCurrentGame()), bplayer.long_name()) player0tup = (REMOTE, ICPlayer, ( gamemodel, wplayer.name, ficsgame.gameno, WHITE, wplayer.long_name(), wplayer.getRatingForCurrentGame()), wplayer.long_name()) perspective = perspective_manager.get_perspective("games") if not ficsgame.board.fen: create_task(perspective.generalStart(gamemodel, player0tup, player1tup)) else: create_task(perspective.generalStart(gamemodel, player0tup, player1tup, ( StringIO(ficsgame.board.fen), fen, 0, -1))) def onGameModelStarted(self, gamemodel, ficsgame): self.connection.bm.onGameModelStarted(ficsgame.gameno) def onObserveGameCreated(self, bm, ficsgame): log.debug("FICS.onObserveGameCreated: %s" % ficsgame) timemodel = TimeModel(ficsgame.minutes * 60, ficsgame.inc) gamemodel = ICGameModel(self.connection, ficsgame, timemodel) gamemodel.connect("game_started", self.onGameModelStarted, ficsgame) # The players need to start listening for moves IN this method if they # want to be noticed of all moves the FICS server sends us from now on wplayer, bplayer = ficsgame.wplayer, ficsgame.bplayer player0tup = (REMOTE, ICPlayer, ( gamemodel, wplayer.name, ficsgame.gameno, WHITE, wplayer.long_name(), wplayer.getRatingForCurrentGame()), wplayer.long_name()) player1tup = (REMOTE, ICPlayer, ( gamemodel, bplayer.name, ficsgame.gameno, BLACK, bplayer.long_name(), bplayer.getRatingForCurrentGame()), bplayer.long_name()) perspective = perspective_manager.get_perspective("games") create_task(perspective.generalStart(gamemodel, player0tup, player1tup, ( StringIO(ficsgame.board.pgn), pgn, 0, -1))) if ficsgame.relation == IC_POS_OBSERVING_EXAMINATION: if 1: # int(self.connection.lvm.variablesBackup["kibitz"]) == 0: self.connection.cm.whisper(_( "You have to set kibitz on to see bot messages here.")) self.connection.fm.finger(bplayer.name) self.connection.fm.finger(wplayer.name) elif ficsgame.relation == IC_POS_EXAMINATING: gamemodel.examined = True if not self.connection.ICC: allob = 'allob ' + str(ficsgame.gameno) gamemodel.connection.client.run_command(allob) def onFinger(self, fm, finger): titles = finger.getTitles() if titles is not None: name = finger.getName() player = self.connection.players.get(name) for title in titles: player.titles.add(TITLES[title]) def tooManySeeks(self, bm): label = Gtk.Label(label=_( "You may only have 3 outstanding seeks at the same time. If you want \ to add a new seek you must clear your currently active seeks. Clear your seeks?")) label.set_width_chars(80) label.props.xalign = 0 label.set_line_wrap(True) def response_cb(infobar, response, message): if response == Gtk.ResponseType.YES: self.connection.client.run_command("unseek") message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.QUESTION, label, response_cb) message.add_button(InfoBarMessageButton(Gtk.STOCK_YES, Gtk.ResponseType.YES)) message.add_button(InfoBarMessageButton(Gtk.STOCK_NO, Gtk.ResponseType.NO)) self.messages.append(message) self.infobar.push_message(message) def nonoWhileExamine(self, bm): label = Gtk.Label(_("You can't touch this! You are examining a game.")) def response_cb(infobar, response, message): message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.INFO, label, response_cb) message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def matchDeclined(self, bm, player): text = _(" has declined your offer for a match") content = get_infobarmessage_content(player, text) def response_cb(infobar, response, message): message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.INFO, content, response_cb) message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def player_on_censor(self, bm, player): text = _(" is censoring you") content = get_infobarmessage_content(player, text) def response_cb(infobar, response, message): message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.INFO, content, response_cb) message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def player_on_noplay(self, bm, player): text = _(" noplay listing you") content = get_infobarmessage_content(player, text) def response_cb(infobar, response, message): message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.INFO, content, response_cb) message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def req_not_fit_formula(self, bm, player, formula): content = get_infobarmessage_content2( player, _(" uses a formula not fitting your match request:"), formula) def response_cb(infobar, response, message): message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.INFO, content, response_cb) message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def on_seek_updated(self, glm, message_text): if "manual accept" in message_text: message_text.replace("to manual accept", _("to manual accept")) elif "automatic accept" in message_text: message_text.replace("to automatic accept", _("to automatic accept")) if "rating range now" in message_text: message_text.replace("rating range now", _("rating range now")) label = Gtk.Label(label=_("Seek updated") + ": " + message_text) def response_cb(infobar, response, message): message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.INFO, label, response_cb) message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def our_seeks_removed(self, glm): label = Gtk.Label(label=_("Your seeks have been removed")) def response_cb(infobar, response, message): message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.INFO, label, response_cb) message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def _connect_to_player_changes(self, player): player.connect("ratings_changed", self._replace_notification_message, player) player.connect("notify::titles", self._replace_notification_message, None, player) def onArrivalNotification(self, cm, player): log.debug("%s" % player, extra={"task": (self.connection.username, "onArrivalNotification")}) self._add_notification_message(player, _(" has arrived"), chat=True, replace=True) if player not in self.players: self.players.append(player) self._connect_to_player_changes(player) def onDepartedNotification(self, cm, player): self._add_notification_message(player, _(" has departed"), replace=True) def user_from_notify_list_is_present(self, player): self._add_notification_message(player, _(" is present"), chat=True, replace=True) if player not in self.players: self.players.append(player) self._connect_to_player_changes(player) def _add_notification_message(self, player, text, chat=False, replace=False): if replace: for message in self.messages: if isinstance(message, PlayerNotificationMessage) and message.player == player: message.dismiss() content = get_infobarmessage_content(player, text) def response_cb(infobar, response, message): if response == 1: if player is None: return self.chat.openChatWithPlayer(player.name) if response == 2: if player is None: return self.connection.client.run_command("follow %s" % player.name) message.dismiss() # self.messages.remove(message) return False message = PlayerNotificationMessage(Gtk.MessageType.INFO, content, response_cb, player, text) if chat: message.add_button(InfoBarMessageButton(_("Chat"), 1)) message.add_button(InfoBarMessageButton(_("Follow"), 2)) message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def _replace_notification_message(self, obj, prop, rating_type, player): log.debug("%s %s" % (repr(obj), player), extra={"task": (self.connection.username, "_replace_notification_message")}) for message in self.messages: if isinstance(message, PlayerNotificationMessage) and \ message.player == player: message.update_content(get_infobarmessage_content( player, message.text)) return False
class Learn(GObject.GObject, Perspective): def __init__(self): GObject.GObject.__init__(self) Perspective.__init__(self, "learn", _("Learn")) self.always_on = True self.dockLocation = addUserConfigPrefix("pydock-learn.xml") self.first_run = True def create_toolbuttons(self): def on_exit_clicked(button): perspective_manager.disable_perspective("learn") self.exit_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_QUIT) self.exit_button.set_tooltip_text(_("Quit Learning")) self.exit_button.set_label("exit") self.exit_button.connect("clicked", on_exit_clicked) def init_layout(self): perspective_manager.set_perspective_toolbuttons( "learn", (self.exit_button, )) perspective_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) perspective_manager.set_perspective_widget("learn", perspective_widget) perspective_manager.set_perspective_menuitems("learn", self.menuitems) self.notebooks = {"home": new_notebook()} self.main_notebook = self.notebooks["home"] for panel in self.sidePanels: self.notebooks[panel.__name__] = new_notebook(panel.__name__) self.dock = PyDockTop("learn", self) align = Gtk.Alignment() align.show() align.add(self.dock) self.dock.show() perspective_widget.pack_start(align, True, True, 0) self.notebooks = {"learnhome": new_notebook()} self.main_notebook = self.notebooks["learnhome"] for panel in self.sidePanels: self.notebooks[panel.__name__] = new_notebook(panel.__name__) self.docks["learnhome"] = (Gtk.Label(label="learnhome"), self.notebooks["learnhome"], None) for panel in self.sidePanels: self.docks[panel.__name__][1] = self.notebooks[panel.__name__] self.load_from_xml() # Default layout of side panels if not os.path.isfile(self.dockLocation): leaf = self.dock.dock(self.docks["learnhome"][1], CENTER, self.docks["learnhome"][0], "learnhome") leaf.setDockable(False) leaf.dock(self.docks["PuzzlesPanel"][1], WEST, self.docks["PuzzlesPanel"][0], "PuzzlesPanel") leaf = leaf.dock(self.docks["LecturesPanel"][1], SOUTH, self.docks["LecturesPanel"][0], "LecturesPanel") leaf = leaf.dock(self.docks["LessonsPanel"][1], SOUTH, self.docks["LessonsPanel"][0], "LessonsPanel") leaf.dock(self.docks["EndgamesPanel"][1], SOUTH, self.docks["EndgamesPanel"][0], "EndgamesPanel") def unrealize(dock): dock.saveToXML(self.dockLocation) dock._del() self.dock.connect("unrealize", unrealize) self.dock.show_all() perspective_widget.show_all() log.debug("Learn.__init__: finished") def activate(self): if self.first_run: self.init_layout() self.first_run = False learn_home = Gtk.Box() learn_home.add(Gtk.Label("Practice! Practice! Practice!")) learn_home.show_all() if not self.first_run: self.notebooks["learnhome"].remove_page(-1) self.notebooks["learnhome"].append_page(learn_home) self.panels = [ panel.Sidepanel().load(self) for panel in self.sidePanels ] for panel, instance in zip(self.sidePanels, self.panels): if not self.first_run: self.notebooks[panel.__name__].remove_page(-1) self.notebooks[panel.__name__].append_page(instance) instance.show() perspective_manager.activate_perspective("learn")
class Games(GObject.GObject, Perspective): __gsignals__ = { 'gmwidg_created': (GObject.SignalFlags.RUN_FIRST, None, (object, )) } def __init__(self): GObject.GObject.__init__(self) Perspective.__init__(self, "games", _("Games")) self.notebooks = {} self.first_run = True self.gamewidgets = set() self.gmwidg_cids = {} self.board_cids = {} self.notify_cids = defaultdict(list) self.key2gmwidg = {} self.key2cid = {} self.dock = None self.dockAlign = None self.dockLocation = addUserConfigPrefix("pydock.xml") @asyncio.coroutine def generalStart(self, gamemodel, player0tup, player1tup, loaddata=None): """ The player tuples are: (The type af player in a System.const value, A callable creating the player, A list of arguments for the callable, A preliminary name for the player) If loaddata is specified, it should be a tuple of: (A text uri or fileobj, A Savers.something module with a load function capable of loading it, An int of the game in file you want to load, The position from where to start the game) """ log.debug("Games.generalStart: %s\n %s\n %s" % (gamemodel, player0tup, player1tup)) gmwidg = gamewidget.GameWidget(gamemodel, self) self.gamewidgets.add(gmwidg) self.gmwidg_cids[gmwidg] = gmwidg.connect("game_close_clicked", self.closeGame) # worker.publish((gmwidg,gamemodel)) self.attachGameWidget(gmwidg) game_nanny.nurseGame(gmwidg, gamemodel) log.debug("Games.generalStart: -> emit gmwidg_created: %s" % (gmwidg)) self.emit("gmwidg_created", gmwidg) log.debug("Games.generalStart: <- emit gmwidg_created: %s" % (gmwidg)) # Initing players def set_name(none, player, key, alt): player.setName(conf.get(key, alt)) players = [] for i, playertup in enumerate((player0tup, player1tup)): type, func, args, prename = playertup if type != LOCAL: if type == ARTIFICIAL: player = yield from func(*args) else: player = func(*args) players.append(player) # if type == ARTIFICIAL: # def readyformoves (player, color): # gmwidg.setTabText(gmwidg.display_text)) # players[i].connect("readyForMoves", readyformoves, i) else: # Until PyChess has a proper profiles system, as discussed on the # issue tracker, we need to give human players special treatment player = func(gmwidg, *args) players.append(player) if player0tup[0] == ARTIFICIAL and player1tup[0] == ARTIFICIAL: def emit_action(board, action, param, gmwidg): if gmwidg.isInFront(): gamemodel.curplayer.emit("offer", Offer(action, param=param)) self.board_cids[gmwidg.board] = gmwidg.board.connect("action", emit_action, gmwidg) log.debug("Games.generalStart: -> gamemodel.setPlayers(): %s" % (gamemodel)) gamemodel.setPlayers(players) log.debug("Games.generalStart: <- gamemodel.setPlayers(): %s" % (gamemodel)) # Starting if loaddata: try: uri, loader, gameno, position = loaddata gamemodel.loadAndStart(uri, loader, gameno, position) if position != gamemodel.ply: gmwidg.board.view.shown = position except LoadingError as e: d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.OK) d.set_markup(_("<big><b>Error loading game</big></b>")) d.format_secondary_text(", ".join(str(a) for a in e.args)) d.show() d.hide() else: if gamemodel.variant.need_initial_board: for player in gamemodel.players: player.setOptionInitialBoard(gamemodel) log.debug("Games..generalStart: -> gamemodel.start(): %s" % (gamemodel)) gamemodel.start() log.debug("Games.generalStart: <- gamemodel.start(): %s" % (gamemodel)) log.debug("Games.generalStart: returning gmwidg=%s\n gamemodel=%s" % (gmwidg, gamemodel)) ################################################################################ # Saving # ################################################################################ def saveGame(self, game, position=None): if not game.isChanged(): return if game.uri and isWriteable(game.uri): self.saveGameSimple(game.uri, game, position=position) else: return self.saveGameAs(game, position=position) def saveGameSimple(self, uri, game, position=None): ending = os.path.splitext(uri)[1] if not ending: return saver = enddir[ending[1:]] game.save(uri, saver, append=False, position=position) def saveGamePGN(self, game): if game.practice_game: return True if conf.get("saveOwnGames", False) and not game.hasLocalPlayer(): return True filename = conf.get("autoSaveFormat", "pychess") filename = filename.replace("#n1", game.tags["White"]) filename = filename.replace("#n2", game.tags["Black"]) filename = filename.replace("#y", "%s" % game.tags["Year"]) filename = filename.replace("#m", "%s" % game.tags["Month"]) filename = filename.replace("#d", "%s" % game.tags["Day"]) pgn_path = conf.get("autoSavePath", os.path.expanduser("~")) + \ "/" + filename + ".pgn" append = True try: if not os.path.isfile(pgn_path): # create new file with open(pgn_path, "w"): pass base_offset = os.path.getsize(pgn_path) # save to .sqlite database_path = os.path.splitext(pgn_path)[0] + '.sqlite' database.save(database_path, game, base_offset) # save to .scout from pychess.Savers.pgn import scoutfish_path if scoutfish_path is not None: pgn_text = pgn.save(StringIO(), game) tmp = tempfile.NamedTemporaryFile(mode="w", delete=False) pgnfile = tmp.name with tmp.file as f: f.write(pgn_text) # create new .scout from pgnfile we are importing args = [scoutfish_path, "make", pgnfile, "%s" % base_offset] output = subprocess.check_output(args, stderr=subprocess.STDOUT) # append it to our existing one if output.decode().find(u"Processing...done") > 0: old_scout = os.path.splitext(pgn_path)[0] + '.scout' new_scout = os.path.splitext(pgnfile)[0] + '.scout' with open(old_scout, "ab") as file1, open(new_scout, "rb") as file2: file1.write(file2.read()) # TODO: do we realy want to update .bin ? It can be huge/slow! # save to .pgn game.save(pgn_path, pgn, append) return True except IOError: return False def saveGameAs(self, game, position=None, export=False): savedialog, savecombo = get_save_dialog(export) # Keep running the dialog until the user has canceled it or made an error # free operation title = _("Save Game") if not export else _("Export position") savedialog.set_title(title) while True: savedialog.set_current_name( "%s %s %s" % (game.players[0], _("vs."), game.players[1])) res = savedialog.run() if res != Gtk.ResponseType.ACCEPT: break uri = savedialog.get_filename() ending = os.path.splitext(uri)[1] if ending.startswith("."): ending = ending[1:] append = False index = savecombo.get_active() if index == 0: if ending not in enddir: d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK) folder, file = os.path.split(uri) d.set_markup(_("<big><b>Unknown file type '%s'</b></big>") % ending) d.format_secondary_text(_( "Was unable to save '%(uri)s' as PyChess doesn't know the format '%(ending)s'.") % {'uri': uri, 'ending': ending}) d.run() d.hide() continue else: saver = enddir[ending] else: format = exportformats[index] if export else saveformats[index] saver = format[2] if ending not in enddir or not saver == enddir[ending]: uri += ".%s" % saver.__ending__ if os.path.isfile(uri) and not os.access(uri, os.W_OK): d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK) d.set_markup(_("<big><b>Unable to save file '%s'</b></big>") % uri) d.format_secondary_text(_( "You don't have the necessary rights to save the file.\n\ Please ensure that you have given the right path and try again.")) d.run() d.hide() continue if os.path.isfile(uri): d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.QUESTION) d.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, _("_Replace"), Gtk.ResponseType.ACCEPT) if saver.__append__: d.add_buttons(Gtk.STOCK_ADD, 1) d.set_title(_("File exists")) folder, file = os.path.split(uri) d.set_markup(_( "<big><b>A file named '%s' already exists. Would you like to replace it?</b></big>") % file) d.format_secondary_text(_( "The file already exists in '%s'. If you replace it, its content will be overwritten.") % folder) replaceRes = d.run() d.hide() if replaceRes == 1: append = True elif replaceRes == Gtk.ResponseType.CANCEL: continue else: print(repr(uri)) try: game.save(uri, saver, append, position) except IOError as e: d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.ERROR) d.add_buttons(Gtk.STOCK_OK, Gtk.ResponseType.OK) d.set_title(_("Could not save the file")) d.set_markup(_( "<big><b>PyChess was not able to save the game</b></big>")) d.format_secondary_text(_("The error was: %s") % ", ".join( str(a) for a in e.args)) d.run() d.hide() continue break savedialog.destroy() return res ################################################################################ # Closing # ################################################################################ def closeAllGames(self, gamewidgets): log.debug("Games.closeAllGames") response = None changedPairs = [(gmwidg, gmwidg.gamemodel) for gmwidg in gamewidgets if gmwidg.gamemodel.isChanged()] if len(changedPairs) == 0: response = Gtk.ResponseType.OK elif len(changedPairs) == 1: response = self.closeGame(changedPairs[0][0]) else: markup = "<big><b>" + ngettext("There is %d game with unsaved moves.", "There are %d games with unsaved moves.", len(changedPairs)) % len(changedPairs) + " " + \ _("Save moves before closing?") + "</b></big>" for gmwidg, game in changedPairs: if not gmwidg.gamemodel.isChanged(): response = Gtk.ResponseType.OK else: if conf.get("autoSave", False): x = self.saveGamePGN(game) if x: response = Gtk.ResponseType.OK else: response = None markup = "<b><big>" + _("Unable to save to configured file. \ Save the games before closing?") + "</big></b>" break if response is None: widgets = GladeWidgets("saveGamesDialog.glade") dialog = widgets["saveGamesDialog"] heading = widgets["saveGamesDialogHeading"] saveLabel = widgets["saveGamesDialogSaveLabel"] treeview = widgets["saveGamesDialogTreeview"] heading.set_markup(markup) liststore = Gtk.ListStore(bool, str) treeview.set_model(liststore) renderer = Gtk.CellRendererToggle() renderer.props.activatable = True treeview.append_column(Gtk.TreeViewColumn("", renderer, active=0)) treeview.append_column(Gtk.TreeViewColumn("", Gtk.CellRendererText(), text=1)) for gmwidg, game in changedPairs: liststore.append((True, "%s %s %s" % (game.players[0], _("vs."), game.players[1]))) def callback(cell, path): if path: liststore[path][0] = not liststore[path][0] saves = len(tuple(row for row in liststore if row[0])) saveLabel.set_text(ngettext( "_Save %d document", "_Save %d documents", saves) % saves) saveLabel.set_use_underline(True) renderer.connect("toggled", callback) callback(None, None) while True: response = dialog.run() if response == Gtk.ResponseType.YES: for i in range(len(liststore) - 1, -1, -1): checked, name = liststore[i] if checked: cgmwidg, cgame = changedPairs[i] if self.saveGame(cgame) == Gtk.ResponseType.ACCEPT: liststore.remove(liststore.get_iter((i, ))) del changedPairs[i] if cgame.status in UNFINISHED_STATES: cgame.end(ABORTED, ABORTED_AGREEMENT) cgame.terminate() self.delGameWidget(cgmwidg) else: break else: break else: break dialog.destroy() if response not in (Gtk.ResponseType.DELETE_EVENT, Gtk.ResponseType.CANCEL): pairs = [(gmwidg, gmwidg.gamemodel) for gmwidg in gamewidgets] for gmwidg, game in pairs: if game.status in UNFINISHED_STATES: game.end(ABORTED, ABORTED_AGREEMENT) game.terminate() if gmwidg.notebookKey in self.key2gmwidg: self.delGameWidget(gmwidg) return response def closeGame(self, gmwidg): log.debug("Games.closeGame") response = None if not gmwidg.gamemodel.isChanged(): response = Gtk.ResponseType.OK else: markup = "<b><big>" + _("Save the current game before you close it?") + "</big></b>" if conf.get("autoSave", False): x = self.saveGamePGN(gmwidg.gamemodel) if x: response = Gtk.ResponseType.OK else: markup = "<b><big>" + _("Unable to save to configured file. \ Save the current game before you close it?") + "</big></b>" if response is None: d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.WARNING) d.add_button(_("Close _without Saving"), Gtk.ResponseType.OK) d.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL) if gmwidg.gamemodel.uri: d.add_button(Gtk.STOCK_SAVE, Gtk.ResponseType.YES) else: d.add_button(Gtk.STOCK_SAVE_AS, Gtk.ResponseType.YES) gmwidg.bringToFront() d.set_markup(markup) d.format_secondary_text(_( "It is not possible later to continue the game,\nif you don't save it.")) response = d.run() d.destroy() if response == Gtk.ResponseType.YES: # Test if cancel was pressed in the save-file-dialog if self.saveGame(gmwidg.gamemodel) != Gtk.ResponseType.ACCEPT: response = Gtk.ResponseType.CANCEL if response not in (Gtk.ResponseType.DELETE_EVENT, Gtk.ResponseType.CANCEL): if gmwidg.gamemodel.status in UNFINISHED_STATES: gmwidg.gamemodel.end(ABORTED, ABORTED_AGREEMENT) gmwidg.disconnect(self.gmwidg_cids[gmwidg]) del self.gmwidg_cids[gmwidg] for cid in self.notify_cids[gmwidg]: conf.notify_remove(cid) del self.notify_cids[gmwidg] if gmwidg.board in self.board_cids: gmwidg.board.disconnect(self.board_cids[gmwidg.board]) del self.board_cids[gmwidg.board] self.delGameWidget(gmwidg) self.gamewidgets.remove(gmwidg) gmwidg.gamemodel.terminate() if len(self.gamewidgets) == 0: for widget in MENU_ITEMS: gamewidget.getWidgets()[widget].set_property('sensitive', False) return response def delGameWidget(self, gmwidg): """ Remove the widget from the GUI after the game has been terminated """ log.debug("Games.delGameWidget: starting %s" % repr(gmwidg)) gmwidg.closed = True gmwidg.emit("closed") called_from_preferences = False window_list = Gtk.Window.list_toplevels() widgets = gamewidget.getWidgets() for window in window_list: if window.is_active() and window == widgets["preferences"]: called_from_preferences = True break pageNum = gmwidg.getPageNumber() headbook = self.getheadbook() if gmwidg.notebookKey in self.key2gmwidg: del self.key2gmwidg[gmwidg.notebookKey] if gmwidg.notebookKey in self.key2cid: headbook.disconnect(self.key2cid[gmwidg.notebookKey]) del self.key2cid[gmwidg.notebookKey] headbook.remove_page(pageNum) for notebook in self.notebooks.values(): notebook.remove_page(pageNum) if headbook.get_n_pages() == 1 and conf.get("hideTabs", False): self.show_tabs(False) if headbook.get_n_pages() == 0: if not called_from_preferences: # If the last (but not the designGW) gmwidg was closed # and we are FICS-ing, present the FICS lounge perspective_manager.disable_perspective("games") gmwidg._del() def init_layout(self): perspective_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) perspective_manager.set_perspective_widget("games", perspective_widget) self.notebooks = {"board": new_notebook("board"), "buttons": new_notebook("buttons"), "messageArea": new_notebook("messageArea")} self.main_notebook = self.notebooks["board"] for panel in self.sidePanels: self.notebooks[panel.__name__] = new_notebook(panel.__name__) # Initing headbook align = gamewidget.createAlignment(4, 4, 0, 4) align.set_property("yscale", 0) headbook = Gtk.Notebook() headbook.set_name("headbook") headbook.set_scrollable(True) align.add(headbook) perspective_widget.pack_start(align, False, True, 0) self.show_tabs(not conf.get("hideTabs", False)) # Initing center centerVBox = Gtk.VBox() # The dock self.dock = PyDockTop("main", self) self.dockAlign = gamewidget.createAlignment(4, 4, 0, 4) self.dockAlign.add(self.dock) centerVBox.pack_start(self.dockAlign, True, True, 0) self.dockAlign.show() self.dock.show() self.docks["board"] = (Gtk.Label(label="Board"), self.notebooks["board"], None) for panel in self.sidePanels: self.docks[panel.__name__][1] = self.notebooks[panel.__name__] self.load_from_xml() # Default layout of side panels if not os.path.isfile(self.dockLocation): leaf = self.dock.dock(self.docks["board"][1], CENTER, Gtk.Label(label=self.docks["board"][0]), "board") self.docks["board"][1].show_all() leaf.setDockable(False) # S epanel = leaf.dock(self.docks["bookPanel"][1], SOUTH, self.docks["bookPanel"][0], "bookPanel") epanel.default_item_height = 45 epanel = epanel.dock(self.docks["engineOutputPanel"][1], CENTER, self.docks["engineOutputPanel"][0], "engineOutputPanel") # NE leaf = leaf.dock(self.docks["annotationPanel"][1], EAST, self.docks["annotationPanel"][0], "annotationPanel") leaf = leaf.dock(self.docks["historyPanel"][1], CENTER, self.docks["historyPanel"][0], "historyPanel") leaf = leaf.dock(self.docks["scorePanel"][1], CENTER, self.docks["scorePanel"][0], "scorePanel") # SE leaf = leaf.dock(self.docks["chatPanel"][1], SOUTH, self.docks["chatPanel"][0], "chatPanel") leaf = leaf.dock(self.docks["commentPanel"][1], CENTER, self.docks["commentPanel"][0], "commentPanel") def unrealize(dock, notebooks): # unhide the panel before saving so its configuration is saved correctly self.notebooks["board"].get_parent().get_parent().zoomDown() dock.saveToXML(self.dockLocation) dock._del() self.dock.connect("unrealize", unrealize, self.notebooks) hbox = Gtk.HBox() # Buttons self.notebooks["buttons"].set_border_width(4) hbox.pack_start(self.notebooks["buttons"], False, True, 0) # The message area # TODO: If you try to fix this first read issue #958 and 1018 align = gamewidget.createAlignment(0, 0, 0, 0) # sw = Gtk.ScrolledWindow() # port = Gtk.Viewport() # port.add(self.notebooks["messageArea"]) # sw.add(port) # align.add(sw) align.add(self.notebooks["messageArea"]) hbox.pack_start(align, True, True, 0) def ma_switch_page(notebook, gpointer, page_num): notebook.props.visible = notebook.get_nth_page(page_num).\ get_child().props.visible self.notebooks["messageArea"].connect("switch-page", ma_switch_page) centerVBox.pack_start(hbox, False, True, 0) perspective_widget.pack_start(centerVBox, True, True, 0) centerVBox.show_all() perspective_widget.show_all() perspective_manager.set_perspective_menuitems("games", self.menuitems) conf.notify_add("hideTabs", self.tabsCallback) # Connecting headbook to other notebooks def hb_switch_page(notebook, gpointer, page_num): for notebook in self.notebooks.values(): notebook.set_current_page(page_num) gmwidg = self.key2gmwidg[self.getheadbook().get_nth_page(page_num)] if isinstance(gmwidg.gamemodel, ICGameModel): primary = "primary " + str(gmwidg.gamemodel.ficsgame.gameno) gmwidg.gamemodel.connection.client.run_command(primary) headbook.connect("switch-page", hb_switch_page) if hasattr(headbook, "set_tab_reorderable"): def page_reordered(widget, child, new_num, headbook): old_num = self.notebooks["board"].page_num(self.key2gmwidg[child].boardvbox) if old_num == -1: log.error('Games and labels are out of sync!') else: for notebook in self.notebooks.values(): notebook.reorder_child( notebook.get_nth_page(old_num), new_num) headbook.connect("page-reordered", page_reordered, headbook) def getheadbook(self): if len(self.key2gmwidg) == 0: return None headbook = self.widget.get_children()[0].get_children()[0].get_child() # to help StoryText create widget description # headbook.get_tab_label_text = customGetTabLabelText return headbook def cur_gmwidg(self): if len(self.key2gmwidg) == 0: return None headbook = self.getheadbook() notebookKey = headbook.get_nth_page(headbook.get_current_page()) return self.key2gmwidg[notebookKey] def customGetTabLabelText(self, child): gmwidg = self.key2gmwidg[child] return gmwidg.display_text def zoomToBoard(self, view_zoomed): if not self.notebooks["board"].get_parent(): return if view_zoomed: self.notebooks["board"].get_parent().get_parent().zoomUp() else: self.notebooks["board"].get_parent().get_parent().zoomDown() def show_tabs(self, show): head = self.getheadbook() if head is None: return head.set_show_tabs(show) def tabsCallback(self, widget): head = self.getheadbook() if not head: return if head.get_n_pages() == 1: self.show_tabs(not conf.get("hideTabs", False)) def attachGameWidget(self, gmwidg): log.debug("attachGameWidget: %s" % gmwidg) if self.first_run: self.init_layout() self.first_run = False perspective_manager.activate_perspective("games") gmwidg.panels = [panel.Sidepanel().load(gmwidg) for panel in self.sidePanels] self.key2gmwidg[gmwidg.notebookKey] = gmwidg headbook = self.getheadbook() headbook.append_page(gmwidg.notebookKey, gmwidg.tabcontent) gmwidg.notebookKey.show_all() if hasattr(headbook, "set_tab_reorderable"): headbook.set_tab_reorderable(gmwidg.notebookKey, True) def callback(notebook, gpointer, page_num, gmwidg): if notebook.get_nth_page(page_num) == gmwidg.notebookKey: gmwidg.infront() if gmwidg.gamemodel.players and gmwidg.gamemodel.isObservationGame(): gmwidg.light_on_off(False) text = gmwidg.game_info_label.get_text() gmwidg.game_info_label.set_markup( '<span color="black" weight="bold">%s</span>' % text) self.key2cid[gmwidg.notebookKey] = headbook.connect_after("switch-page", callback, gmwidg) gmwidg.infront() align = gamewidget.createAlignment(0, 0, 0, 0) align.show() align.add(gmwidg.infobar) self.notebooks["messageArea"].append_page(align, None) self.notebooks["board"].append_page(gmwidg.boardvbox, None) gmwidg.boardvbox.show_all() for panel, instance in zip(self.sidePanels, gmwidg.panels): self.notebooks[panel.__name__].append_page(instance, None) instance.show_all() self.notebooks["buttons"].append_page(gmwidg.stat_hbox, None) gmwidg.stat_hbox.show_all() if headbook.get_n_pages() == 1: self.show_tabs(not conf.get("hideTabs", False)) else: # We should always show tabs if more than one exists self.show_tabs(True) headbook.set_current_page(-1) widgets = gamewidget.getWidgets() if headbook.get_n_pages() == 1 and not widgets["show_sidepanels"].get_active(): self.zoomToBoard(True)
class Database(GObject.GObject, Perspective): __gsignals__ = { 'chessfile_opened0': (GObject.SignalFlags.RUN_FIRST, None, (object, )), 'chessfile_opened': (GObject.SignalFlags.RUN_FIRST, None, (object, )), 'chessfile_closed': (GObject.SignalFlags.RUN_FIRST, None, ()), 'chessfile_imported': (GObject.SignalFlags.RUN_FIRST, None, (object, )), } def __init__(self): GObject.GObject.__init__(self) Perspective.__init__(self, "database", _("Database")) self.widgets = gamewidget.getWidgets() self.first_run = True self.chessfile = None self.chessfiles = [] self.importer = None self.gamelists = [] self.filter_panels = [] self.opening_tree_panels = [] self.preview_panels = [] self.notebooks = {} self.page_dict = {} self.connect("chessfile_opened0", self.on_chessfile_opened0) self.dockLocation = addUserConfigPrefix("pydock-database.xml") @property def gamelist(self): if self.chessfile is None: return None else: return self.gamelists[self.chessfiles.index(self.chessfile)] @property def filter_panel(self): if self.chessfile is None: return None else: return self.filter_panels[self.chessfiles.index(self.chessfile)] @property def opening_tree_panel(self): if self.chessfile is None: return None else: return self.opening_tree_panels[self.chessfiles.index(self.chessfile)] @property def preview_panel(self): if self.chessfile is None: return None else: return self.preview_panels[self.chessfiles.index(self.chessfile)] def create_toolbuttons(self): self.import_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_CONVERT) self.import_button.set_tooltip_text(_("Import PGN file")) self.import_button.connect("clicked", self.on_import_clicked) self.save_as_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_SAVE_AS) self.save_as_button.set_tooltip_text(_("Save to PGN file as...")) self.save_as_button.connect("clicked", self.on_save_as_clicked) def init_layout(self): perspective_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) perspective_manager.set_perspective_widget("database", perspective_widget) self.notebooks = {"gamelist": new_notebook()} self.main_notebook = self.notebooks["gamelist"] for panel in self.sidePanels: self.notebooks[panel.__name__] = new_notebook(panel.__name__) self.spinner = Gtk.Spinner() self.spinner.set_size_request(50, 50) self.progressbar0 = Gtk.ProgressBar(show_text=True) self.progressbar1 = Gtk.ProgressBar(show_text=True) self.progress_dialog = Gtk.Dialog("", mainwindow(), 0, ( Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)) self.progress_dialog.set_deletable(False) self.progress_dialog.get_content_area().pack_start(self.spinner, True, True, 0) self.progress_dialog.get_content_area().pack_start(self.progressbar0, True, True, 0) self.progress_dialog.get_content_area().pack_start(self.progressbar1, True, True, 0) self.progress_dialog.get_content_area().show_all() # Initing headbook align = createAlignment(4, 4, 0, 4) align.set_property("yscale", 0) self.headbook = Gtk.Notebook() self.headbook.set_name("headbook") self.headbook.set_scrollable(True) align.add(self.headbook) perspective_widget.pack_start(align, False, True, 0) self.headbook.connect_after("switch-page", self.on_switch_page) # The dock self.dock = PyDockTop("database", self) align = Gtk.Alignment() align.show() align.add(self.dock) self.dock.show() perspective_widget.pack_start(align, True, True, 0) self.docks["gamelist"] = (Gtk.Label(label="gamelist"), self.notebooks["gamelist"], None) for panel in self.sidePanels: self.docks[panel.__name__][1] = self.notebooks[panel.__name__] self.load_from_xml() # Default layout of side panels if not os.path.isfile(self.dockLocation): leaf = self.dock.dock(self.docks["gamelist"][1], CENTER, self.docks["gamelist"][0], "gamelist") leaf.setDockable(False) leaf = leaf.dock(self.docks["OpeningTreePanel"][1], EAST, self.docks["OpeningTreePanel"][0], "OpeningTreePanel") leaf = leaf.dock(self.docks["FilterPanel"][1], CENTER, self.docks["FilterPanel"][0], "FilterPanel") leaf.dock(self.docks["PreviewPanel"][1], SOUTH, self.docks["PreviewPanel"][0], "PreviewPanel") def unrealize(dock): dock.saveToXML(self.dockLocation) dock._del() self.dock.connect("unrealize", unrealize) self.dock.show_all() perspective_widget.show_all() perspective_manager.set_perspective_menuitems("database", self.menuitems) perspective_manager.set_perspective_toolbuttons("database", [ self.import_button, self.save_as_button]) def on_switch_page(self, notebook, page, page_num): if page in self.page_dict: self.chessfile = self.page_dict[page][0] i = self.chessfiles.index(self.chessfile) self.notebooks["gamelist"].set_current_page(i) self.notebooks["OpeningTreePanel"].set_current_page(i) self.notebooks["FilterPanel"].set_current_page(i) self.notebooks["PreviewPanel"].set_current_page(i) def set_sensitives(self, on): self.import_button.set_sensitive(on) self.widgets["import_chessfile"].set_sensitive(on) self.widgets["database_save_as"].set_sensitive(on) self.widgets["import_endgame_nl"].set_sensitive(on) self.widgets["import_twic"].set_sensitive(on) def open_chessfile(self, filename): if self.first_run: self.init_layout() self.first_run = False perspective_manager.activate_perspective("database") self.progress_dialog.set_title(_("Open")) self.progressbar0.hide() self.spinner.show() self.spinner.start() def opening(): if filename.endswith(".pgn"): GLib.idle_add(self.progressbar1.show) GLib.idle_add(self.progressbar1.set_text, _("Opening chessfile...")) chessfile = PGNFile(protoopen(filename), self.progressbar1) self.importer = chessfile.init_tag_database() if self.importer is not None and self.importer.cancel: chessfile.tag_database.close() if os.path.isfile(chessfile.sqlite_path): os.remove(chessfile.sqlite_path) chessfile = None else: chessfile.init_scoutfish() chessfile.init_chess_db() elif filename.endswith(".epd"): self.importer = None chessfile = epd.load(protoopen(filename)) elif filename.endswith(".olv"): self.importer = None chessfile = olv.load(protoopen(filename, encoding="utf-8")) elif filename.endswith(".fen"): self.importer = None chessfile = fen.load(protoopen(filename)) else: self.importer = None chessfile = None GLib.idle_add(self.spinner.stop) GLib.idle_add(self.progress_dialog.hide) if chessfile is not None: self.chessfile = chessfile self.chessfiles.append(chessfile) GLib.idle_add(self.emit, "chessfile_opened0", chessfile) else: if self.chessfile is None: self.close(None) thread = threading.Thread(target=opening) thread.daemon = True thread.start() response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: if self.importer is not None: self.importer.do_cancel() self.progress_dialog.hide() def on_chessfile_opened0(self, persp, chessfile): page = Gtk.Alignment() tabcontent, close_button = self.get_tabcontent(chessfile) self.headbook.append_page(page, tabcontent) self.page_dict[page] = (chessfile, close_button) page.show_all() gamelist = GameList(self) self.gamelists.append(gamelist) opening_tree_panel = OpeningTreePanel(self) self.opening_tree_panels.append(opening_tree_panel) filter_panel = FilterPanel(self) self.filter_panels.append(filter_panel) preview_panel = PreviewPanel(self) self.preview_panels.append(preview_panel) self.notebooks["gamelist"].append_page(gamelist.box) self.notebooks["OpeningTreePanel"].append_page(opening_tree_panel.box) self.notebooks["FilterPanel"].append_page(filter_panel.box) self.notebooks["PreviewPanel"].append_page(preview_panel.box) self.headbook.set_current_page(self.headbook.get_n_pages() - 1) gamelist.load_games() opening_tree_panel.update_tree(load_games=False) self.set_sensitives(True) self.emit("chessfile_opened", chessfile) def close(self, close_button): for page in list(self.page_dict.keys()): if self.page_dict[page][1] == close_button: chessfile = self.page_dict[page][0] i = self.chessfiles.index(chessfile) self.notebooks["gamelist"].remove_page(i) self.notebooks["OpeningTreePanel"].remove_page(i) self.notebooks["FilterPanel"].remove_page(i) self.notebooks["PreviewPanel"].remove_page(i) del self.gamelists[i] del self.filter_panels[i] del self.chessfiles[i] chessfile.close() del self.page_dict[page] self.headbook.remove_page(self.headbook.page_num(page)) break if len(self.chessfiles) == 0: self.set_sensitives(False) perspective_manager.disable_perspective("database") self.emit("chessfile_closed") def on_import_endgame_nl(self): self.do_import(JvR) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_import_twic(self): LATEST = get_latest_twic() if LATEST is None: return html = "http://www.theweekinchess.com/html/twic%s.html" twic = [] pgn = "https://raw.githubusercontent.com/rozim/ChessData/master/Twic/fix-twic%s.pgn" # pgn = "/home/tamas/PGN/twic/twic%sg.zip" for i in range(210, 920): twic.append((html % i, pgn % i)) pgn = "http://www.theweekinchess.com/zips/twic%sg.zip" # pgn = "/home/tamas/PGN/twic/twic%sg.zip" for i in range(920, LATEST + 1): twic.append((html % i, pgn % i)) twic.append((html % LATEST, pgn % LATEST)) # import limited to latest twic .pgn for now twic = twic[-1:] self.do_import(twic) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() def on_save_as_clicked(self, widget): dialog = Gtk.FileChooserDialog( _("Save as"), mainwindow(), Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT)) dialog.set_current_folder(os.path.expanduser("~")) response = dialog.run() if response == Gtk.ResponseType.ACCEPT: filename = dialog.get_filename() else: filename = None dialog.destroy() if filename is not None: with open(filename, "w") as to_file: records, plys = self.chessfile.get_records(FIRST_PAGE) self.save_records(records, to_file) while True: records, plys = self.chessfile.get_records(NEXT_PAGE) if records: self.save_records(records, to_file) else: break def save_records(self, records, to_file): f = self.chessfile.handle for i, rec in enumerate(records): offs = rec["Offset"] f.seek(offs) game = '' for line in f: if line.startswith('[Event "'): if game: break # Second one, start of next game else: game = line # First occurence elif game: game += line to_file.write(game) def on_import_clicked(self, widget): dialog = Gtk.FileChooserDialog( _("Open chess file"), mainwindow(), Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) dialog.set_select_multiple(True) filter_text = Gtk.FileFilter() filter_text.set_name(".pgn") filter_text.add_pattern("*.pgn") filter_text.add_mime_type("application/x-chess-pgn") dialog.add_filter(filter_text) filter_text = Gtk.FileFilter() filter_text.set_name(".zip") filter_text.add_pattern("*.zip") filter_text.add_mime_type("application/zip") dialog.add_filter(filter_text) response = dialog.run() if response == Gtk.ResponseType.OK: filenames = dialog.get_filenames() else: filenames = None dialog.destroy() if filenames is not None: self.do_import(filenames) response = self.progress_dialog.run() if response == Gtk.ResponseType.CANCEL: self.importer.do_cancel() self.progress_dialog.hide() # @profile_me def importing(self, filenames): drop_indexes(self.chessfile.engine) self.importer = PgnImport(self.chessfile, append_pgn=True) self.importer.initialize() for i, filename in enumerate(filenames): GLib.idle_add(self.progressbar0.set_fraction, i / float(len(filenames))) # GLib.idle_add(self.progressbar0.set_text, filename) if self.importer.cancel: break if isinstance(filename, tuple): info_link, pgn_link = filename self.importer.do_import(pgn_link, info=info_link, progressbar=self.progressbar1) else: self.importer.do_import(filename, progressbar=self.progressbar1) GLib.idle_add(self.progressbar1.set_text, _("Recreating indexes...")) # .sqlite create_indexes(self.chessfile.engine) # .scout self.chessfile.init_scoutfish() # .bin self.chessfile.init_chess_db() self.chessfile.set_tag_filter(None) self.chessfile.set_fen_filter(None) self.chessfile.set_scout_filter(None) GLib.idle_add(self.gamelist.load_games) GLib.idle_add(self.emit, "chessfile_imported", self.chessfile) GLib.idle_add(self.progress_dialog.hide) def do_import(self, filenames): self.progress_dialog.set_title(_("Import")) self.spinner.hide() if len(filenames) == 1: self.progressbar0.hide() else: self.progressbar0.show() self.progressbar1.show() self.progressbar1.set_text(_("Preparing to start import...")) thread = threading.Thread(target=self.importing, args=(filenames, )) thread.daemon = True thread.start() def create_database(self): dialog = Gtk.FileChooserDialog( _("Create New Pgn Database"), mainwindow(), Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_NEW, Gtk.ResponseType.ACCEPT)) dialog.set_current_folder(os.path.expanduser("~")) dialog.set_current_name("new.pgn") response = dialog.run() if response == Gtk.ResponseType.ACCEPT: new_pgn = dialog.get_filename() if not new_pgn.endswith(".pgn"): new_pgn = "%s.pgn" % new_pgn if not os.path.isfile(new_pgn): # create new file with open(new_pgn, "w"): pass self.open_chessfile(new_pgn) else: d = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK) d.set_markup(_("<big><b>File '%s' already exists.</b></big>") % new_pgn) d.run() d.hide() print("%s allready exist." % new_pgn) dialog.destroy() def get_tabcontent(self, chessfile): tabcontent = createAlignment(0, 0, 0, 0) hbox = Gtk.HBox() hbox.set_spacing(4) hbox.pack_start(createImage(pgn_icon), False, True, 0) close_button = Gtk.Button() close_button.set_property("can-focus", False) close_button.add(createImage(gtk_close)) close_button.set_relief(Gtk.ReliefStyle.NONE) close_button.set_size_request(20, 18) close_button.connect("clicked", self.close) hbox.pack_end(close_button, False, True, 0) name, ext = os.path.splitext(chessfile.path) basename = os.path.basename(name) info = "%s.%s" % (basename, ext[1:]) tooltip = "%s\ncontaining %s games" % (chessfile.path, chessfile.count) tabcontent.set_tooltip_text(tooltip) label = Gtk.Label(info) hbox.pack_start(label, False, True, 0) tabcontent.add(hbox) tabcontent.show_all() return tabcontent, close_button
class FICS(GObject.GObject, Perspective): __gsignals__ = { 'logout': (GObject.SignalFlags.RUN_FIRST, None, ()), 'autoLogout': (GObject.SignalFlags.RUN_FIRST, None, ()), } def __init__(self): log.debug("FICS.__init__: starting") GObject.GObject.__init__(self) Perspective.__init__(self, "fics", _("ICS")) self.dockLocation = addUserConfigPrefix("pydock-fics.xml") self.first_run = True def create_toolbuttons(self): def on_logoff_clicked(button): self.emit("logout") self.close() self.logoff_button = Gtk.ToolButton.new_from_stock( Gtk.STOCK_DISCONNECT) self.logoff_button.set_tooltip_text(_("Log Off")) self.logoff_button.set_label("logoff") self.logoff_button.connect("clicked", on_logoff_clicked) def on_minute_1_clicked(button): self.connection.client.run_command("1-minute") def on_minute_3_clicked(button): self.connection.client.run_command("3-minute") def on_minute_5_clicked(button): self.connection.client.run_command("5-minute") def on_minute_15_clicked(button): self.connection.client.run_command("15-minute") def on_minute_25_clicked(button): self.connection.client.run_command("25-minute") def on_chess960_clicked(button): self.connection.client.run_command("chess960") self.minute_1_button = Gtk.ToggleToolButton() self.minute_1_button.set_label("1") self.minute_1_button.set_tooltip_text( _("New game from 1-minute playing pool")) self.minute_1_button.connect("clicked", on_minute_1_clicked) self.minute_3_button = Gtk.ToggleToolButton() self.minute_3_button.set_label("3") self.minute_3_button.set_tooltip_text( _("New game from 3-minute playing pool")) self.minute_3_button.connect("clicked", on_minute_3_clicked) self.minute_5_button = Gtk.ToggleToolButton() self.minute_5_button.set_label("5") self.minute_5_button.set_tooltip_text( _("New game from 5-minute playing pool")) self.minute_5_button.connect("clicked", on_minute_5_clicked) self.minute_15_button = Gtk.ToggleToolButton() self.minute_15_button.set_label("15") self.minute_15_button.set_tooltip_text( _("New game from 15-minute playing pool")) self.minute_15_button.connect("clicked", on_minute_15_clicked) self.minute_25_button = Gtk.ToggleToolButton() self.minute_25_button.set_label("25") self.minute_25_button.set_tooltip_text( _("New game from 25-minute playing pool")) self.minute_25_button.connect("clicked", on_minute_25_clicked) self.chess960_button = Gtk.ToggleToolButton() self.chess960_button.set_label("960") self.chess960_button.set_tooltip_text( _("New game from Chess960 playing pool")) self.chess960_button.connect("clicked", on_chess960_clicked) def init_layout(self): perspective_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) perspective_manager.set_perspective_widget("fics", perspective_widget) perspective_manager.set_perspective_menuitems("fics", self.menuitems) self.infobar = InfoBarNotebook("fics_lounge_infobar") self.infobar.hide() perspective_widget.pack_start(self.infobar, False, False, 0) self.dock = PyDockTop("fics", self) align = Gtk.Alignment() align.show() align.add(self.dock) self.dock.show() perspective_widget.pack_start(align, True, True, 0) self.notebooks = {"ficshome": new_notebook()} self.main_notebook = self.notebooks["ficshome"] for panel in self.sidePanels: self.notebooks[panel.__name__] = new_notebook(panel.__name__) self.docks["ficshome"] = (Gtk.Label(label="ficshome"), self.notebooks["ficshome"], None) for panel in self.sidePanels: self.docks[panel.__name__][1] = self.notebooks[panel.__name__] self.load_from_xml() # Default layout of side panels if not os.path.isfile(self.dockLocation): leaf = self.dock.dock(self.docks["ficshome"][1], CENTER, self.docks["ficshome"][0], "ficshome") leaf.setDockable(False) console_leaf = leaf.dock(self.docks["ConsolePanel"][1], SOUTH, self.docks["ConsolePanel"][0], "ConsolePanel") console_leaf.dock(self.docks["NewsPanel"][1], CENTER, self.docks["NewsPanel"][0], "NewsPanel") seek_leaf = leaf.dock(self.docks["SeekListPanel"][1], WEST, self.docks["SeekListPanel"][0], "SeekListPanel") seek_leaf.dock(self.docks["SeekGraphPanel"][1], CENTER, self.docks["SeekGraphPanel"][0], "SeekGraphPanel") seek_leaf.dock(self.docks["PlayerListPanel"][1], CENTER, self.docks["PlayerListPanel"][0], "PlayerListPanel") seek_leaf.dock(self.docks["GameListPanel"][1], CENTER, self.docks["GameListPanel"][0], "GameListPanel") seek_leaf.dock(self.docks["ArchiveListPanel"][1], CENTER, self.docks["ArchiveListPanel"][0], "ArchiveListPanel") leaf = leaf.dock(self.docks["ChatPanel"][1], SOUTH, self.docks["ChatPanel"][0], "ChatPanel") # leaf.dock(self.docks["LecturesPanel"][1], CENTER, self.docks["LecturesPanel"][0], "LecturesPanel") def unrealize(dock): dock.saveToXML(self.dockLocation) dock._del() self.dock.connect("unrealize", unrealize) self.dock.show_all() perspective_widget.show_all() log.debug("FICS.__init__: finished") def open_lounge(self, connection, helperconn, host): if self.first_run: self.init_layout() self.connection = connection self.helperconn = helperconn self.host = host self.finger_sent = False self.messages = [] self.players = [] self.game_cids = {} self.widgets = uistuff.GladeWidgets("fics_lounge.glade") self.widgets["fics_lounge"].hide() fics_home = self.widgets["fics_home"] self.widgets["fics_lounge_content_hbox"].remove(fics_home) self.archive_list = self.widgets["archiveListContent"] self.widgets["fics_panels_notebook"].remove(self.archive_list) self.games_list = self.widgets["gamesListContent"] self.widgets["fics_panels_notebook"].remove(self.games_list) self.news_list = self.widgets["news"] self.widgets["fics_home"].remove(self.news_list) self.players_list = self.widgets["playersListContent"] self.widgets["fics_panels_notebook"].remove(self.players_list) self.seek_graph = self.widgets["seekGraphContent"] self.widgets["fics_panels_notebook"].remove(self.seek_graph) self.seek_list = self.widgets["seekListContent"] self.widgets["fics_panels_notebook"].remove(self.seek_list) self.seek_challenge = SeekChallengeSection(self) def on_autoLogout(alm): self.emit("autoLogout") self.close() self.connection.alm.connect("logOut", on_autoLogout) self.connection.connect("disconnected", lambda connection: self.close()) self.connection.connect("error", self.on_connection_error) if self.connection.isRegistred(): numtimes = conf.get("numberOfTimesLoggedInAsRegisteredUser", 0) + 1 conf.set("numberOfTimesLoggedInAsRegisteredUser", numtimes) self.connection.em.connect( "onCommandNotFound", lambda em, cmd: log.error( "Fics answered '%s': Command not found" % cmd)) self.connection.bm.connect("playGameCreated", self.onPlayGameCreated) self.connection.bm.connect("obsGameCreated", self.onObserveGameCreated) self.connection.bm.connect("exGameCreated", self.onObserveGameCreated) self.connection.fm.connect("fingeringFinished", self.onFinger) # the rest of these relay server messages to the lounge infobar self.connection.bm.connect("tooManySeeks", self.tooManySeeks) self.connection.bm.connect("nonoWhileExamine", self.nonoWhileExamine) self.connection.bm.connect("matchDeclined", self.matchDeclined) self.connection.bm.connect("player_on_censor", self.player_on_censor) self.connection.bm.connect("player_on_noplay", self.player_on_noplay) self.connection.bm.connect("req_not_fit_formula", self.req_not_fit_formula) self.connection.glm.connect("seek-updated", self.on_seek_updated) self.connection.glm.connect("our-seeks-removed", self.our_seeks_removed) self.connection.cm.connect("arrivalNotification", self.onArrivalNotification) self.connection.cm.connect("departedNotification", self.onDepartedNotification) def get_top_games(): if perspective_manager.current_perspective == self: self.connection.client.run_command("games *19") return True if self.connection.ICC: self.event_id = GLib.timeout_add_seconds(5, get_top_games) for user in self.connection.notify_users: user = self.connection.players.get(user) self.user_from_notify_list_is_present(user) self.userinfo = UserInfoSection(self.widgets, self.connection, self.host, self) if not self.first_run: self.notebooks["ficshome"].remove_page(-1) self.notebooks["ficshome"].append_page(fics_home) self.panels = [ panel.Sidepanel().load(self.widgets, self.connection, self) for panel in self.sidePanels ] for panel, instance in zip(self.sidePanels, self.panels): if not self.first_run: self.notebooks[panel.__name__].remove_page(-1) self.notebooks[panel.__name__].append_page(instance) instance.show() tool_buttons = [ self.logoff_button, ] self.quick_seek_buttons = [] if self.connection.ICC: self.quick_seek_buttons = [ self.minute_1_button, self.minute_3_button, self.minute_5_button, self.minute_15_button, self.minute_25_button, self.chess960_button ] tool_buttons += self.quick_seek_buttons perspective_manager.set_perspective_toolbuttons("fics", tool_buttons) if self.first_run: self.first_run = False # After all panel is set up we can push initial messages out self.connection.com.onConsoleMessage("", self.connection.ini_messages) def show(self): perspective_manager.activate_perspective("fics") def present(self): perspective_manager.activate_perspective("fics") def on_connection_error(self, connection, error): log.warning("FICS.on_connection_error: %s" % repr(error)) self.close() def close(self): try: self.widgets = None except TypeError: pass except AttributeError: pass perspective_manager.disable_perspective("fics") def onPlayGameCreated(self, bm, ficsgame): log.debug("FICS.onPlayGameCreated: %s" % ficsgame) for message in self.messages: message.dismiss() del self.messages[:] if self.connection.ICC: for button in self.quick_seek_buttons: button.set_active(False) timemodel = TimeModel(ficsgame.minutes * 60, ficsgame.inc) gamemodel = ICGameModel(self.connection, ficsgame, timemodel) gamemodel.connect("game_started", self.onGameModelStarted, ficsgame) wplayer, bplayer = ficsgame.wplayer, ficsgame.bplayer # We start if wplayer.name.lower() == self.connection.getUsername().lower(): player0tup = (LOCAL, Human, (WHITE, wplayer.long_name(), wplayer.name, wplayer.getRatingForCurrentGame()), wplayer.long_name()) player1tup = (REMOTE, ICPlayer, (gamemodel, bplayer.name, ficsgame.gameno, BLACK, bplayer.long_name(), bplayer.getRatingForCurrentGame()), bplayer.long_name()) # She starts else: player1tup = (LOCAL, Human, (BLACK, bplayer.long_name(), bplayer.name, bplayer.getRatingForCurrentGame()), bplayer.long_name()) player0tup = (REMOTE, ICPlayer, (gamemodel, wplayer.name, ficsgame.gameno, WHITE, wplayer.long_name(), wplayer.getRatingForCurrentGame()), wplayer.long_name()) perspective = perspective_manager.get_perspective("games") if not ficsgame.board.fen: asyncio. async (perspective.generalStart(gamemodel, player0tup, player1tup)) else: asyncio. async (perspective.generalStart( gamemodel, player0tup, player1tup, (StringIO(ficsgame.board.fen), fen, 0, -1))) def onGameModelStarted(self, gamemodel, ficsgame): self.connection.bm.onGameModelStarted(ficsgame.gameno) def onObserveGameCreated(self, bm, ficsgame): log.debug("FICS.onObserveGameCreated: %s" % ficsgame) timemodel = TimeModel(ficsgame.minutes * 60, ficsgame.inc) gamemodel = ICGameModel(self.connection, ficsgame, timemodel) gamemodel.connect("game_started", self.onGameModelStarted, ficsgame) # The players need to start listening for moves IN this method if they # want to be noticed of all moves the FICS server sends us from now on wplayer, bplayer = ficsgame.wplayer, ficsgame.bplayer player0tup = (REMOTE, ICPlayer, (gamemodel, wplayer.name, ficsgame.gameno, WHITE, wplayer.long_name(), wplayer.getRatingForCurrentGame()), wplayer.long_name()) player1tup = (REMOTE, ICPlayer, (gamemodel, bplayer.name, ficsgame.gameno, BLACK, bplayer.long_name(), bplayer.getRatingForCurrentGame()), bplayer.long_name()) perspective = perspective_manager.get_perspective("games") asyncio. async (perspective.generalStart( gamemodel, player0tup, player1tup, (StringIO(ficsgame.board.pgn), pgn, 0, -1))) if ficsgame.relation == IC_POS_OBSERVING_EXAMINATION: if 1: # int(self.connection.lvm.variablesBackup["kibitz"]) == 0: self.connection.cm.whisper( _("You have to set kibitz on to see bot messages here.")) self.connection.fm.finger(bplayer.name) self.connection.fm.finger(wplayer.name) elif ficsgame.relation == IC_POS_EXAMINATING: gamemodel.examined = True if not self.connection.ICC: allob = 'allob ' + str(ficsgame.gameno) gamemodel.connection.client.run_command(allob) def onFinger(self, fm, finger): titles = finger.getTitles() if titles is not None: name = finger.getName() player = self.connection.players.get(name) for title in titles: player.titles.add(TITLES[title]) def tooManySeeks(self, bm): label = Gtk.Label(label=_( "You may only have 3 outstanding seeks at the same time. If you want \ to add a new seek you must clear your currently active seeks. Clear your seeks?" )) label.set_width_chars(80) label.props.xalign = 0 label.set_line_wrap(True) def response_cb(infobar, response, message): if response == Gtk.ResponseType.YES: self.connection.client.run_command("unseek") message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.QUESTION, label, response_cb) message.add_button( InfoBarMessageButton(Gtk.STOCK_YES, Gtk.ResponseType.YES)) message.add_button( InfoBarMessageButton(Gtk.STOCK_NO, Gtk.ResponseType.NO)) self.messages.append(message) self.infobar.push_message(message) def nonoWhileExamine(self, bm): label = Gtk.Label(_("You can't touch this! You are examining a game.")) def response_cb(infobar, response, message): message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.INFO, label, response_cb) message.add_button( InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def matchDeclined(self, bm, player): text = _(" has declined your offer for a match") content = get_infobarmessage_content(player, text) def response_cb(infobar, response, message): message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.INFO, content, response_cb) message.add_button( InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def player_on_censor(self, bm, player): text = _(" is censoring you") content = get_infobarmessage_content(player, text) def response_cb(infobar, response, message): message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.INFO, content, response_cb) message.add_button( InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def player_on_noplay(self, bm, player): text = _(" noplay listing you") content = get_infobarmessage_content(player, text) def response_cb(infobar, response, message): message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.INFO, content, response_cb) message.add_button( InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def req_not_fit_formula(self, bm, player, formula): content = get_infobarmessage_content2( player, _(" uses a formula not fitting your match request:"), formula) def response_cb(infobar, response, message): message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.INFO, content, response_cb) message.add_button( InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def on_seek_updated(self, glm, message_text): if "manual accept" in message_text: message_text.replace("to manual accept", _("to manual accept")) elif "automatic accept" in message_text: message_text.replace("to automatic accept", _("to automatic accept")) if "rating range now" in message_text: message_text.replace("rating range now", _("rating range now")) label = Gtk.Label(label=_("Seek updated") + ": " + message_text) def response_cb(infobar, response, message): message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.INFO, label, response_cb) message.add_button( InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def our_seeks_removed(self, glm): label = Gtk.Label(label=_("Your seeks have been removed")) def response_cb(infobar, response, message): message.dismiss() return False message = InfoBarMessage(Gtk.MessageType.INFO, label, response_cb) message.add_button( InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def _connect_to_player_changes(self, player): player.connect("ratings_changed", self._replace_notification_message, player) player.connect("notify::titles", self._replace_notification_message, None, player) def onArrivalNotification(self, cm, player): log.debug("%s" % player, extra={ "task": (self.connection.username, "onArrivalNotification") }) self._add_notification_message(player, _(" has arrived"), chat=True, replace=True) if player not in self.players: self.players.append(player) self._connect_to_player_changes(player) def onDepartedNotification(self, cm, player): self._add_notification_message(player, _(" has departed"), replace=True) def user_from_notify_list_is_present(self, player): self._add_notification_message(player, _(" is present"), chat=True, replace=True) if player not in self.players: self.players.append(player) self._connect_to_player_changes(player) def _add_notification_message(self, player, text, chat=False, replace=False): if replace: for message in self.messages: if isinstance(message, PlayerNotificationMessage ) and message.player == player: message.dismiss() content = get_infobarmessage_content(player, text) def response_cb(infobar, response, message): if response == 1: if player is None: return self.chat.openChatWithPlayer(player.name) if response == 2: if player is None: return self.connection.client.run_command("follow %s" % player.name) message.dismiss() # self.messages.remove(message) return False message = PlayerNotificationMessage(Gtk.MessageType.INFO, content, response_cb, player, text) if chat: message.add_button(InfoBarMessageButton(_("Chat"), 1)) message.add_button(InfoBarMessageButton(_("Follow"), 2)) message.add_button( InfoBarMessageButton(Gtk.STOCK_CLOSE, Gtk.ResponseType.CANCEL)) self.messages.append(message) self.infobar.push_message(message) def _replace_notification_message(self, obj, prop, rating_type, player): log.debug("%s %s" % (repr(obj), player), extra={ "task": (self.connection.username, "_replace_notification_message") }) for message in self.messages: if isinstance(message, PlayerNotificationMessage) and \ message.player == player: message.update_content( get_infobarmessage_content(player, message.text)) return False