def __init__(self, dlg: QDialog) -> None: QDialog.__init__(self, dlg) self.addonsDlg = dlg self.mgr = dlg.mgr self.mw = self.mgr.mw self.ids: List[int] = [] self.form = aqt.forms.getaddons.Ui_Dialog() self.form.setupUi(self) b = self.form.buttonBox.addButton( tr(TR.ADDONS_BROWSE_ADDONS), QDialogButtonBox.ActionRole ) qconnect(b.clicked, self.onBrowse) disable_help_button(self) restoreGeom(self, "getaddons", adjustSize=True) self.exec_() saveGeom(self, "getaddons")
def loadAddons(self) -> None: for addon in self.all_addon_meta(): if not addon.enabled: continue if not addon.compatible(): continue self.dirty = True try: __import__(addon.dir_name) except: showWarning( tr( TR.ADDONS_FAILED_TO_LOAD, name=addon.human_name(), traceback=traceback.format_exc(), ))
def addGroup(self) -> None: name = getOnlyText(tr(TR.SCHEDULING_NEW_OPTIONS_GROUP_NAME)) if not name: return # first, save currently entered data to current conf self.saveConf() # then clone the conf id = self.mw.col.decks.add_config_returning_id(name, clone_from=self.conf) gui_hooks.deck_conf_did_add_config(self, self.deck, self.conf, name, id) # set the deck to the new conf self.deck["conf"] = id # then reload the conf list self.loadConfs()
def but(i: int, label: str) -> str: if i == default: extra = """id="defease" class="focus" """ else: extra = "" due = self._buttonTime(i) return """ <td align=center>%s<button %s title="%s" data-ease="%s" onclick='pycmd("ease%d");'>\ %s</button></td>""" % ( due, extra, tr(TR.ACTIONS_SHORTCUT_KEY, val=i), i, i, label, )
def onRename(self) -> None: idx = self.currentIdx f = self.model["flds"][idx] name = self._uniqueName(tr(TR.ACTIONS_NEW_NAME), self.currentIdx, f["name"]) if not name: return old_name = f["name"] self.change_tracker.mark_basic() self.mm.rename_field(self.model, f, name) gui_hooks.fields_did_rename_field(self, f, old_name) self.saveField() self.fillFields() self.form.fieldList.setCurrentRow(idx)
def __init__(self, dlg) -> None: QDialog.__init__(self, dlg) self.addonsDlg = dlg self.mgr = dlg.mgr self.mw = self.mgr.mw self.ids: List[int] = [] self.form = aqt.forms.getaddons.Ui_Dialog() self.form.setupUi(self) b = self.form.buttonBox.addButton(tr(TR.ADDONS_BROWSE_ADDONS), QDialogButtonBox.ActionRole) qconnect(b.clicked, self.onBrowse) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) # type: ignore restoreGeom(self, "getaddons", adjustSize=True) self.exec_() saveGeom(self, "getaddons")
def setupApkgImport(mw: AnkiQt, importer: AnkiPackageImporter) -> bool: base = os.path.basename(importer.file).lower() full = ((base == "collection.apkg") or re.match("backup-.*\\.apkg", base) or base.endswith(".colpkg")) if not full: # adding return True if not mw.restoringBackup and not askUser( tr(TR.IMPORTING_THIS_WILL_DELETE_YOUR_EXISTING_COLLECTION), msgfunc=QMessageBox.warning, defaultno=True, ): return False replaceWithApkg(mw, importer.file, mw.restoringBackup) return False
def postprocess(self, encode=True) -> None: self.encode = encode for c in processingChain: # print c if not self.encode and c[0] == "lame": continue try: cmd, env = _packagedCmd(c) ret = retryWait( subprocess.Popen(cmd, startupinfo=startup_info(), env=env)) except: ret = True finally: self.cleanup() if ret: raise Exception(tr(TR.MEDIA_ERROR_RUNNING, val=" ").join(cmd))
def _remove_tag(self, item: "aqt.browser.SidebarItem") -> None: old_name = item.full_name def do_remove(): self.mw.col.backend.clear_tag(old_name) self.col.tags.rename_tag(old_name, "") def on_done(fut: Future): self.mw.requireReset(reason=ResetReason.BrowserRemoveTags, context=self) self.browser.model.endReset() fut.result() self.browser.maybeRefreshSidebar() self.mw.checkpoint(tr(TR.ACTIONS_REMOVE_TAG)) self.browser.model.beginReset() self.mw.taskman.run_in_background(do_remove, on_done)
def _desc(self, deck: Dict[str, Any]) -> str: if deck["dyn"]: desc = tr(TR.STUDYING_THIS_IS_A_SPECIAL_DECK_FOR) desc += f" {tr(TR.STUDYING_CARDS_WILL_BE_AUTOMATICALLY_RETURNED_TO)}" desc += f" {tr(TR.STUDYING_DELETING_THIS_DECK_FROM_THE_DECK)}" else: desc = deck.get("desc", "") if deck.get("md", False): desc = self.mw.col.render_markdown(desc) if not desc: return "<p>" if deck["dyn"]: dyn = "dyn" else: dyn = "" return f'<div class="descfont descmid description {dyn}">{desc}</div>'
def _delete_deck(self, item: SidebarItem) -> None: did = item.id if self.mw.deckBrowser.ask_delete_deck(did): def do_delete() -> None: return self.mw.col.decks.rem(did, True) def on_done(fut: Future) -> None: self.mw.requireReset(reason=ResetReason.BrowserDeleteDeck, context=self) self.browser.search() self.browser.model.endReset() self.refresh() res = fut.result() # Required to check for errors self.mw.checkpoint(tr(TR.DECKS_DELETE_DECK)) self.browser.model.beginReset() self.mw.taskman.run_in_background(do_delete, on_done)
def prompt_to_update( parent: QWidget, mgr: AddonManager, client: HttpClient, ids: List[int], on_done: Callable[[List[DownloadLogEntry]], None], ) -> None: names = map(lambda x: mgr.addonName(str(x)), ids) if not askUser( tr(TR.ADDONS_THE_FOLLOWING_ADDONS_HAVE_UPDATES_AVAILABLE) + "\n\n" + "\n".join(names) ): # on_done is not called if the user cancels return download_addons(parent, mgr, ids, on_done, client)
def _renderDeckTree(self, top: DeckTreeNode) -> str: buf = """ <tr><th colspan=5 align=start>%s</th><th class=count>%s</th> <th class=count>%s</th><th class=optscol></th></tr>""" % ( _("Deck"), tr(TR.STATISTICS_DUE_COUNT), _("New"), ) buf += self._topLevelDragRow() ctx = RenderDeckNodeContext( current_deck_id=self.mw.col.conf["curDeck"]) for child in top.children: buf += self._render_deck_node(child, ctx) return buf
def onAddMedia(self) -> None: extension_filter = " ".join( f"*.{extension}" for extension in sorted(itertools.chain(pics, audio))) filter = f"{tr(TR.EDITING_MEDIA)} ({extension_filter})" def accept(file: str) -> None: self.addMedia(file) file = getFile( parent=self.widget, title=tr(TR.EDITING_ADD_MEDIA), cb=cast(Callable[[Any], None], accept), filter=filter, key="media", ) self.parentWindow.activateWindow()
def _remove_tags(self, _item: SidebarItem) -> None: tags = self._selected_tags() def do_remove() -> int: return self.col._backend.expunge_tags(" ".join(tags)) def on_done(fut: Future) -> None: self.mw.requireReset(reason=ResetReason.BrowserRemoveTags, context=self) self.browser.model.endReset() tooltip(tr(TR.BROWSING_NOTES_UPDATED, count=fut.result()), parent=self) self.refresh() self.mw.checkpoint(tr(TR.ACTIONS_REMOVE_TAG)) self.browser.model.beginReset() self.mw.taskman.with_progress(do_remove, on_done)
def _on_render_latex(self): self.progress_dialog = self.mw.progress.start() try: out = self.mw.col.media.render_all_latex( self._on_render_latex_progress) if self.progress_dialog.wantCancel: return finally: self.mw.progress.finish() self.progress_dialog = None if out is not None: nid, err = out self.mw.browser_search(SearchTerm(nid=nid)) showText(err, type="html") else: tooltip(tr(TR.MEDIA_CHECK_ALL_LATEX_RENDERED))
def _answerButtonList(self) -> Tuple[Tuple[int, str], ...]: button_count = self.mw.col.sched.answerButtons(self.card) if button_count == 2: buttons_tuple: Tuple[Tuple[int, str], ...] = ( (1, tr(TR.STUDYING_AGAIN)), (2, tr(TR.STUDYING_GOOD)), ) elif button_count == 3: buttons_tuple = ( (1, tr(TR.STUDYING_AGAIN)), (2, tr(TR.STUDYING_GOOD)), (3, tr(TR.STUDYING_EASY)), ) else: buttons_tuple = ( (1, tr(TR.STUDYING_AGAIN)), (2, tr(TR.STUDYING_HARD)), (3, tr(TR.STUDYING_GOOD)), (4, tr(TR.STUDYING_EASY)), ) buttons_tuple = gui_hooks.reviewer_will_init_answer_buttons( buttons_tuple, self, self.card) return buttons_tuple
def update_global(self) -> None: restart_required = False self.update_video_driver() newScale = self.form.uiScale.value() / 100 if newScale != self.mw.pm.uiScale(): self.mw.pm.setUiScale(newScale) restart_required = True if self.mw.pm.night_mode() != self.form.nightMode.isChecked(): self.mw.pm.set_night_mode(not self.mw.pm.night_mode()) restart_required = True if restart_required: showInfo(tr(TR.PREFERENCES_CHANGES_WILL_TAKE_EFFECT_WHEN_YOU)) self.updateOptions()
def add_simple( self, name: Union[str, TR], icon: Union[str, ColoredIcon], type: SidebarItemType, search_node: Optional[SearchNode], ) -> SidebarItem: "Add child sidebar item, and return it." if not isinstance(name, str): name = tr(name) item = SidebarItem( name=name, icon=icon, search_node=search_node, item_type=type, ) self.add_child(item) return item
def onConfig(self) -> None: addon = self.onlyOneSelected() if not addon: return # does add-on manage its own config? act = self.mgr.configAction(addon) if act: ret = act() if ret is not False: return conf = self.mgr.getConfig(addon) if conf is None: showInfo(tr(TR.ADDONS_ADDON_HAS_NO_CONFIGURATION)) return ConfigEditor(self, addon, conf)
def rename_saved_search(self, item: SidebarItem, new_name: str) -> None: old_name = item.name conf = self._get_saved_searches() try: filt = conf[old_name] except KeyError: return if new_name in conf and not askUser( tr(TR.BROWSING_CONFIRM_SAVED_SEARCH_OVERWRITE, name=new_name) ): return conf[new_name] = filt del conf[old_name] self._set_saved_searches(conf) self.refresh( lambda item: item.item_type == SidebarItemType.SAVED_SEARCH and item.name == new_name )
def __init__(self, mw: aqt.AnkiQt) -> None: QDialog.__init__(self, None, Qt.Window) mw.garbage_collect_on_dialog_finish(self) self.mw = mw self.form = aqt.forms.editcurrent.Ui_Dialog() self.form.setupUi(self) self.setWindowTitle(tr(TR.EDITING_EDIT_CURRENT)) disable_help_button(self) self.setMinimumHeight(400) self.setMinimumWidth(250) self.form.buttonBox.button(QDialogButtonBox.Close).setShortcut( QKeySequence("Ctrl+Return")) self.editor = aqt.editor.Editor(self.mw, self.form.fieldsArea, self) self.editor.card = self.mw.reviewer.card self.editor.set_note(self.mw.reviewer.card.note(), focusTo=0) restoreGeom(self, "editcurrent") gui_hooks.operation_did_execute.append(self.on_operation_did_execute) self.show()
def add_simple( self, name: Union[str, TR.V], icon: Union[str, ColoredIcon], type: SidebarItemType, on_click: Callable[[], None], ) -> SidebarItem: "Add child sidebar item, and return it." if not isinstance(name, str): name = tr(name) item = SidebarItem( name=name, icon=icon, on_click=on_click, item_type=type, ) self.add_child(item) return item
def onAddDeck(self) -> None: row = self.form.list.currentRow() if row < 0: default = self.form.filter.text() else: default = self.names[self.form.list.currentRow()] n = getOnlyText(tr(TR.DECKS_NEW_DECK_NAME), default=default) n = n.strip() if n: did = self.mw.col.decks.id(n) # deck name may not be the same as user input. ex: ", :: self.name = self.mw.col.decks.name(did) # make sure we clean up reset hook when manually exiting gui_hooks.state_did_reset.remove(self.onReset) if self.mw.state == "deckBrowser": self.mw.deckBrowser.refresh() gui_hooks.sidebar_should_refresh_decks() QDialog.accept(self)
def toggleEnabled(self, dir: str, enable: Optional[bool] = None) -> None: addon = self.addon_meta(dir) should_enable = enable if enable is not None else not addon.enabled if should_enable is True: conflicting = self._disableConflicting(dir) if conflicting: addons = ", ".join(self.addonName(f) for f in conflicting) showInfo( tr( TR.ADDONS_THE_FOLLOWING_ADDONS_ARE_INCOMPATIBLE_WITH, name=addon.human_name(), found=addons, ), textFormat="plain", ) addon.enabled = should_enable self.write_addon_meta(addon)
def _on_render_latex(self): self.progress_dialog = self.mw.progress.start() try: out = self.mw.col.media.render_all_latex(self._on_render_latex_progress) if self.progress_dialog.wantCancel: return finally: self.mw.progress.finish() self.progress_dialog = None if out is not None: nid, err = out browser = aqt.dialogs.open("Browser", self.mw) browser.form.searchEdit.lineEdit().setText("nid:%d" % nid) browser.onSearchActivated() showText(err, type="html") else: tooltip(tr(TR.MEDIA_CHECK_ALL_LATEX_RENDERED))
def _deck_tree(self, root: SidebarItem) -> None: icon = ":/icons/deck.svg" def render(root: SidebarItem, nodes: Iterable[DeckTreeNode], head: str = "") -> None: for node in nodes: def toggle_expand() -> Callable[[bool], None]: did = node.deck_id # pylint: disable=cell-var-from-loop return lambda _: self.mw.col.decks.collapseBrowser(did) item = SidebarItem( node.name, icon, self._filter_func(SearchNode(deck=head + node.name)), toggle_expand(), not node.collapsed, item_type=SidebarItemType.DECK, id=node.deck_id, full_name=head + node.name, ) root.add_child(item) newhead = f"{head + node.name}::" render(item, node.children, newhead) tree = self.col.decks.deck_tree() root = self._section_root( root=root, name=TR.BROWSING_SIDEBAR_DECKS, icon=icon, collapse_key=Config.Bool.COLLAPSE_DECKS, type=SidebarItemType.DECK_ROOT, ) root.on_click = self._filter_func(SearchNode(deck="*")) current = root.add_simple( name=tr(TR.BROWSING_CURRENT_DECK), icon=icon, type=SidebarItemType.DECK, on_click=self._filter_func(SearchNode(deck="current")), ) current.id = self.mw.col.decks.selected() render(root, tree.children)
def _tag_tree(self, root: SidebarItem) -> None: icon = ":/icons/tag.svg" def render(root: SidebarItem, nodes: Iterable[TagTreeNode], head: str = "") -> None: for node in nodes: def toggle_expand() -> Callable[[bool], None]: full_name = head + node.name # pylint: disable=cell-var-from-loop return lambda expanded: self.mw.col.tags.set_expanded( full_name, expanded) item = SidebarItem( node.name, icon, self._filter_func(SearchNode(tag=head + node.name)), toggle_expand(), node.expanded, item_type=SidebarItemType.TAG, full_name=head + node.name, ) root.add_child(item) newhead = f"{head + node.name}::" render(item, node.children, newhead) tree = self.col.tags.tree() root = self._section_root( root=root, name=TR.BROWSING_SIDEBAR_TAGS, icon=icon, collapse_key=Config.Bool.COLLAPSE_TAGS, type=SidebarItemType.TAG_ROOT, ) root.on_click = self._filter_func( SearchNode(negated=SearchNode(tag="none"))) root.add_simple( name=tr(TR.BROWSING_SIDEBAR_UNTAGGED), icon=icon, type=SidebarItemType.TAG_NONE, on_click=self._filter_func(SearchNode(tag="none")), ) render(root, tree.children)
def loadCollection(self) -> bool: try: return self._loadCollection() except Exception as e: showWarning( tr(TR.ERRORS_UNABLE_OPEN_COLLECTION) + "\n" + traceback.format_exc()) # clean up open collection if possible if self.col: try: self.col.close(save=False) except: pass self.col = None # return to profile manager self.hide() self.showProfileManager() return False
def on_full_sync_timer(mw: aqt.main.AnkiQt) -> None: progress = mw.col.latest_progress() if progress.kind != ProgressKind.FullSync: return assert isinstance(progress.val, FullSyncProgress) if progress.val.transferred == progress.val.total: label = tr(TR.SYNC_CHECKING) else: label = None mw.progress.update( value=progress.val.transferred, max=progress.val.total, process=False, label=label, ) if mw.progress.want_cancel(): mw.col.backend.abort_sync()