def __init__(self, mw: aqt.AnkiQt) -> None: QObject.__init__(self) self.mw = mw.weakref() self._executor = ThreadPoolExecutor() self._closures: List[Closure] = [] self._closures_lock = Lock() qconnect(self._closures_pending, self._on_closures_pending)
def full_download(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None: def on_timer() -> None: on_full_sync_timer(mw) timer = QTimer(mw) qconnect(timer.timeout, on_timer) timer.start(150) def download() -> None: mw._close_for_full_download() mw.col.full_download(mw.pm.sync_auth()) def on_future_done(fut: Future) -> None: timer.stop() mw.col.reopen(after_full_sync=True) mw.reset() try: fut.result() except Exception as err: handle_sync_error(mw, err) mw.media_syncer.start() return on_done() mw.taskman.with_progress( download, on_future_done, label=tr(TR.SYNC_DOWNLOADING_FROM_ANKIWEB), )
def __init__(self, mw: aqt.main.AnkiQt, syncer: MediaSyncer, close_when_done: bool = False) -> None: super().__init__(mw) self.mw = mw self._syncer = syncer self._close_when_done = close_when_done self.form = aqt.forms.synclog.Ui_Dialog() self.form.setupUi(self) self.setWindowTitle(tr(TR.SYNC_MEDIA_LOG_TITLE)) disable_help_button(self) self.abort_button = QPushButton(tr(TR.SYNC_ABORT_BUTTON)) qconnect(self.abort_button.clicked, self._on_abort) self.abort_button.setAutoDefault(False) self.form.buttonBox.addButton(self.abort_button, QDialogButtonBox.ActionRole) self.abort_button.setHidden(not self._syncer.is_syncing()) gui_hooks.media_sync_did_progress.append(self._on_log_entry) gui_hooks.media_sync_did_start_or_stop.append(self._on_start_stop) self.form.plainTextEdit.setPlainText("\n".join( self._entry_to_text(x) for x in syncer.entries())) self.form.plainTextEdit.moveCursor(QTextCursor.End) self.show()
def full_upload(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None: mw.col.close_for_full_sync() def on_timer() -> None: on_full_sync_timer(mw) timer = QTimer(mw) qconnect(timer.timeout, on_timer) timer.start(150) def on_future_done(fut: Future) -> None: timer.stop() mw.col.reopen(after_full_sync=True) mw.reset() try: fut.result() except Exception as err: handle_sync_error(mw, err) return on_done() mw.media_syncer.start() return on_done() mw.taskman.with_progress( lambda: mw.col.full_upload(mw.pm.sync_auth()), on_future_done, label=tr.sync_uploading_to_ankiweb(), )
def full_upload(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None: mw.col.close_for_full_sync() def on_timer(): on_full_sync_timer(mw) timer = QTimer(mw) qconnect(timer.timeout, on_timer) timer.start(150) def on_future_done(fut): timer.stop() mw.col.reopen(after_full_sync=True) mw.reset() try: fut.result() except Exception as err: handle_sync_error(mw, err) return on_done() return on_done() mw.taskman.with_progress( lambda: mw.col.backend.full_upload(mw.pm.sync_auth()), on_future_done, label=tr(TR.SYNC_UPLOADING_TO_ANKIWEB), )
def __init__(self, mw: aqt.main.AnkiQt, report: EmptyCardsReport) -> None: super().__init__(mw) self.mw = mw.weakref() self.report = report self.form = aqt.forms.emptycards.Ui_Dialog() self.form.setupUi(self) restoreGeom(self, "emptycards") self.setWindowTitle(tr(TR.EMPTY_CARDS_WINDOW_TITLE)) disable_help_button(self) self.form.keep_notes.setText(tr( TR.EMPTY_CARDS_PRESERVE_NOTES_CHECKBOX)) self.form.webview.title = "empty cards" self.form.webview.set_bridge_command(self._on_note_link_clicked, self) gui_hooks.empty_cards_will_show(self) # make the note ids clickable html = re.sub( r"\[anki:nid:(\d+)\]", "<a href=# onclick=\"pycmd('nid:\\1'); return false\">\\1</a>: ", report.report, ) style = "<style>.allempty { color: red; }</style>" self.form.webview.stdHtml(style + html, context=self) def on_finished(code: Any) -> None: saveGeom(self, "emptycards") qconnect(self.finished, on_finished) self._delete_button = self.form.buttonBox.addButton( tr(TR.EMPTY_CARDS_DELETE_BUTTON), QDialogButtonBox.ActionRole) self._delete_button.setAutoDefault(False) self._delete_button.clicked.connect(self._on_delete)
def _create_gui(self): super()._create_gui() self._other_side = self.bbox.addButton("Other side", QDialogButtonBox.ActionRole) self._other_side.setAutoDefault(False) self._other_side.setShortcut(QKeySequence("Right")) self._other_side.setShortcut(QKeySequence("Left")) self._other_side.setToolTip(_("Shortcut key: Left or Right arrow")) qconnect(self._other_side.clicked, self._on_other_side)
def setupBrowserShortcuts(self): # self is browser cut = gc("shortcut: open window") if cut: cm = QShortcut(QKeySequence(cut), self) qconnect(cm.activated, lambda b=self: open_multiline_searchwindow(b)) view = getMenu(self, "&View") action = QAction(self) action.setText("Show search string in multi-line dialog") view.addAction(action) action.triggered.connect(lambda _, b=self: open_multiline_searchwindow(b))
def _create_gui(self): super()._create_gui() self._prev = self.bbox.addButton("<", QDialogButtonBox.ActionRole) self._prev.setAutoDefault(False) self._prev.setShortcut(QKeySequence("Left")) self._prev.setToolTip(_("Shortcut key: Left arrow")) self._next = self.bbox.addButton(">", QDialogButtonBox.ActionRole) self._next.setAutoDefault(True) self._next.setShortcut(QKeySequence("Right")) self._next.setToolTip(_("Shortcut key: Right arrow or Enter")) qconnect(self._prev.clicked, self._on_prev) qconnect(self._next.clicked, self._on_next)
def _create_gui(self) -> None: super()._create_gui() self._prev = self.bbox.addButton("<", QDialogButtonBox.ActionRole) self._prev.setAutoDefault(False) self._prev.setShortcut(QKeySequence("Left")) self._prev.setToolTip(tr.qt_misc_shortcut_key_left_arrow()) self._next = self.bbox.addButton(">", QDialogButtonBox.ActionRole) self._next.setAutoDefault(True) self._next.setShortcut(QKeySequence("Right")) self._next.setToolTip(tr.qt_misc_shortcut_key_right_arrow_or_enter()) qconnect(self._prev.clicked, self._on_prev) qconnect(self._next.clicked, self._on_next)
def _create_gui(self) -> None: self.setWindowTitle(tr.actions_preview()) self.close_shortcut = QShortcut(QKeySequence("Ctrl+Shift+P"), self) qconnect(self.close_shortcut.activated, self.close) qconnect(self.finished, self._on_finished) self.silentlyClose = True self.vbox = QVBoxLayout() self.vbox.setContentsMargins(0, 0, 0, 0) self._web = AnkiWebView(title="previewer") self.vbox.addWidget(self._web) self.bbox = QDialogButtonBox() self.bbox.setLayoutDirection(Qt.LayoutDirection.LeftToRight) self._replay = self.bbox.addButton( tr.actions_replay_audio(), QDialogButtonBox.ButtonRole.ActionRole) self._replay.setAutoDefault(False) self._replay.setShortcut(QKeySequence("R")) self._replay.setToolTip(tr.actions_shortcut_key(val="R")) qconnect(self._replay.clicked, self._on_replay_audio) both_sides_button = QCheckBox(tr.qt_misc_back_side_only()) both_sides_button.setShortcut(QKeySequence("B")) both_sides_button.setToolTip(tr.actions_shortcut_key(val="B")) self.bbox.addButton(both_sides_button, QDialogButtonBox.ButtonRole.ActionRole) self._show_both_sides = self.mw.col.get_config_bool( Config.Bool.PREVIEW_BOTH_SIDES) both_sides_button.setChecked(self._show_both_sides) qconnect(both_sides_button.toggled, self._on_show_both_sides) self.vbox.addWidget(self.bbox) self.setLayout(self.vbox) restoreGeom(self, "preview")
def addContextMenu(ev: QCloseEvent, name: str, dc_instance) -> None: ev.accept() frm = mw.debug_diag_form menu = frm.log.createStandardContextMenu(QCursor.pos()) menu.addSeparator() if name == "log": a = menu.addAction("Clear Log") a.setShortcuts(QKeySequence("ctrl+l")) qconnect(a.triggered, frm.log.clear) elif name == "text": a = menu.addAction("Clear Code") a.setShortcuts(QKeySequence("ctrl+shift+l")) qconnect(a.triggered, frm.text.clear) ## ADDITION hist_window = menu.addAction("Open History Window") cut = gc("debug console: shortcut open history window", "ctrl+i") if cut: hist_window.setShortcuts(QKeySequence(cut)) # doesn't work qconnect(hist_window.triggered, lambda _, d=dc_instance: history_helper(d)) save = menu.addAction("Save Current") cut = gc("debug console: shortcut save", "ctrl+s") if cut: save.setShortcuts(QKeySequence(cut)) # doesn't work qconnect(save.triggered, lambda _, d=dc_instance: save_current(d)) menu.exec(QCursor.pos())
def _create_gui(self) -> None: self.setWindowTitle(tr(TR.ACTIONS_PREVIEW)) self.close_shortcut = QShortcut(QKeySequence("Ctrl+Shift+P"), self) qconnect(self.close_shortcut.activated, self.close) qconnect(self.finished, self._on_finished) self.silentlyClose = True self.vbox = QVBoxLayout() self.vbox.setContentsMargins(0, 0, 0, 0) self._web = AnkiWebView(title="previewer") self.vbox.addWidget(self._web) self.bbox = QDialogButtonBox() self._replay = self.bbox.addButton(tr(TR.ACTIONS_REPLAY_AUDIO), QDialogButtonBox.ActionRole) self._replay.setAutoDefault(False) self._replay.setShortcut(QKeySequence("R")) self._replay.setToolTip(tr(TR.ACTIONS_SHORTCUT_KEY, val="R")) qconnect(self._replay.clicked, self._on_replay_audio) both_sides_button = QCheckBox(tr(TR.QT_MISC_BACK_SIDE_ONLY)) both_sides_button.setShortcut(QKeySequence("B")) both_sides_button.setToolTip(tr(TR.ACTIONS_SHORTCUT_KEY, val="B")) self.bbox.addButton(both_sides_button, QDialogButtonBox.ActionRole) self._show_both_sides = self.mw.col.get_config_bool( Config.Bool.PREVIEW_BOTH_SIDES) both_sides_button.setChecked(self._show_both_sides) qconnect(both_sides_button.toggled, self._on_show_both_sides) self.vbox.addWidget(self.bbox) self.setLayout(self.vbox) restoreGeom(self, "preview")
def _create_gui(self): super()._create_gui() self._prev = self.bbox.addButton("<", QDialogButtonBox.ActionRole) self._prev.setAutoDefault(False) self._prev.setShortcut(QKeySequence("Left")) self._prev.setToolTip(tr(TR.QT_MISC_SHORTCUT_KEY_LEFT_ARROW)) self._next = self.bbox.addButton(">", QDialogButtonBox.ActionRole) self._next.setAutoDefault(True) self._next.setShortcut(QKeySequence("Right")) self._next.setToolTip(tr(TR.QT_MISC_SHORTCUT_KEY_RIGHT_ARROW_OR_ENTER)) qconnect(self._prev.clicked, self._on_prev) qconnect(self._next.clicked, self._on_next)
def get_id_and_pass_from_user(mw: aqt.main.AnkiQt, username: str = "", password: str = "") -> tuple[str, str]: diag = QDialog(mw) diag.setWindowTitle("Anki") disable_help_button(diag) diag.setWindowModality(Qt.WindowModality.WindowModal) vbox = QVBoxLayout() info_label = QLabel( without_unicode_isolation( tr.sync_account_required( link="https://ankiweb.net/account/register"))) info_label.setOpenExternalLinks(True) info_label.setWordWrap(True) vbox.addWidget(info_label) vbox.addSpacing(20) g = QGridLayout() l1 = QLabel(tr.sync_ankiweb_id_label()) g.addWidget(l1, 0, 0) user = QLineEdit() user.setText(username) g.addWidget(user, 0, 1) l2 = QLabel(tr.sync_password_label()) g.addWidget(l2, 1, 0) passwd = QLineEdit() passwd.setText(password) passwd.setEchoMode(QLineEdit.EchoMode.Password) g.addWidget(passwd, 1, 1) vbox.addLayout(g) bb = QDialogButtonBox( QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel) # type: ignore bb.button(QDialogButtonBox.StandardButton.Ok).setAutoDefault(True) qconnect(bb.accepted, diag.accept) qconnect(bb.rejected, diag.reject) vbox.addWidget(bb) diag.setLayout(vbox) diag.show() accepted = diag.exec() if not accepted: return ("", "") return (user.text().strip(), passwd.text())
def get_id_and_pass_from_user(mw: aqt.main.AnkiQt, username="", password="") -> Tuple[str, str]: diag = QDialog(mw) diag.setWindowTitle("Anki") diag.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) # type: ignore diag.setWindowModality(Qt.WindowModal) vbox = QVBoxLayout() info_label = QLabel( without_unicode_isolation( tr(TR.SYNC_ACCOUNT_REQUIRED, link="https://ankiweb.net/account/register"))) info_label.setOpenExternalLinks(True) info_label.setWordWrap(True) vbox.addWidget(info_label) vbox.addSpacing(20) g = QGridLayout() l1 = QLabel(tr(TR.SYNC_ANKIWEB_ID_LABEL)) g.addWidget(l1, 0, 0) user = QLineEdit() user.setText(username) g.addWidget(user, 0, 1) l2 = QLabel(tr(TR.SYNC_PASSWORD_LABEL)) g.addWidget(l2, 1, 0) passwd = QLineEdit() passwd.setText(password) passwd.setEchoMode(QLineEdit.Password) g.addWidget(passwd, 1, 1) vbox.addLayout(g) bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) # type: ignore bb.button(QDialogButtonBox.Ok).setAutoDefault(True) qconnect(bb.accepted, diag.accept) qconnect(bb.rejected, diag.reject) vbox.addWidget(bb) diag.setLayout(vbox) diag.show() accepted = diag.exec_() if not accepted: return ("", "") return (user.text().strip(), passwd.text())
def sync_collection(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None: auth = mw.pm.sync_auth() assert auth def on_timer(): on_normal_sync_timer(mw) timer = QTimer(mw) qconnect(timer.timeout, on_timer) timer.start(150) def on_future_done(fut): mw.col.db.begin() timer.stop() try: out: SyncOutput = fut.result() except Exception as err: handle_sync_error(mw, err) return on_done() mw.pm.set_host_number(out.host_number) if out.server_message: showText(out.server_message) if out.required == out.NO_CHANGES: # all done return on_done() else: full_sync(mw, out, on_done) if not mw.col.basicCheck(): showWarning("Please use Tools>Check Database") return on_done() mw.col.save(trx=False) mw.taskman.with_progress( lambda: mw.col.backend.sync_collection(auth), on_future_done, label=tr(TR.SYNC_CHECKING), immediate=True, )
def _create_gui(self): self.setWindowTitle(_("Preview")) qconnect(self.finished, self._on_finished) self.silentlyClose = True self.vbox = QVBoxLayout() self.vbox.setContentsMargins(0, 0, 0, 0) self._web = AnkiWebView(title="previewer") self.vbox.addWidget(self._web) self.bbox = QDialogButtonBox() self._replay = self.bbox.addButton(_("Replay Audio"), QDialogButtonBox.ActionRole) self._replay.setAutoDefault(False) self._replay.setShortcut(QKeySequence("R")) self._replay.setToolTip(_("Shortcut key: %s" % "R")) qconnect(self._replay.clicked, self._on_replay_audio) both_sides_button = QCheckBox(_("Show Both Sides")) both_sides_button.setShortcut(QKeySequence("B")) both_sides_button.setToolTip(_("Shortcut key: %s" % "B")) self.bbox.addButton(both_sides_button, QDialogButtonBox.ActionRole) self._show_both_sides = self.mw.col.conf.get("previewBothSides", False) both_sides_button.setChecked(self._show_both_sides) qconnect(both_sides_button.toggled, self._on_show_both_sides) self.vbox.addWidget(self.bbox) self.setLayout(self.vbox) restoreGeom(self, "preview")
def __init__(self, mw: aqt.main.AnkiQt, report: EmptyCardsReport) -> None: super().__init__(mw) self.mw = mw.weakref() self.mw.garbage_collect_on_dialog_finish(self) self.report = report self.form = aqt.forms.emptycards.Ui_Dialog() self.form.setupUi(self) restoreGeom(self, "emptycards") self.setWindowTitle(tr.empty_cards_window_title()) disable_help_button(self) self.form.keep_notes.setText(tr.empty_cards_preserve_notes_checkbox()) self.form.webview.set_title("empty cards") self.form.webview.set_bridge_command(self._on_note_link_clicked, self) gui_hooks.empty_cards_will_show(self) # make the note ids clickable html = re.sub( r"\[anki:nid:(\d+)\]", "<a href=# onclick=\"pycmd('nid:\\1'); return false\">\\1</a>: ", report.report, ) style = "<style>.allempty { color: red; }</style>" self.form.webview.stdHtml(style + html, context=self) def on_finished(code: Any) -> None: self.form.webview.cleanup() self.form.webview = None saveGeom(self, "emptycards") qconnect(self.finished, on_finished) self._delete_button = self.form.buttonBox.addButton( tr.empty_cards_delete_button(), QDialogButtonBox.ButtonRole.ActionRole ) self._delete_button.setAutoDefault(False) qconnect(self._delete_button.clicked, self._on_delete)
def sync_collection(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None: auth = mw.pm.sync_auth() if not auth: raise Exception("expected auth") def on_timer() -> None: on_normal_sync_timer(mw) timer = QTimer(mw) qconnect(timer.timeout, on_timer) timer.start(150) def on_future_done(fut: Future) -> None: mw.col.db.begin() timer.stop() try: out: SyncOutput = fut.result() except Exception as err: handle_sync_error(mw, err) return on_done() mw.pm.set_host_number(out.host_number) if out.server_message: showText(out.server_message) if out.required == out.NO_CHANGES: # all done return on_done() else: full_sync(mw, out, on_done) mw.col.save(trx=False) mw.taskman.with_progress( lambda: mw.col.sync_collection(auth), on_future_done, label=tr.sync_checking(), immediate=True, )
def main_onProfileLoad(): gLogger.debug("__init__::onProfileLoad") """ This function is here, so addons are loaded *AFTER* the profile was chosen""" # ========================================================================== # ============= first things to do: set global variables! ================ # ========================================================================== tmpDir = os.path.join(mw.col.media.dir(),"..") if(config["Share tag data among decks"] == "YES"): # if we share, put it inside the head folder, so go one level up: tmpDir = os.path.join(tmpDir,"..") TSDataPersistent.PersistanceMgmt.static_PathToConfigFile = tmpDir global gSetupTagSelectorFinished if(not gSetupTagSelectorFinished): gLogger.debug("__init__:: do only once-stuff") gSetupTagSelectorFinished = True # ========================================================================== # ============= create menu items under Addons: =================== # ========================================================================== mw.TS_actionClearData = QAction("clear saved data", mw) mw.TS_actionClearData.triggered.connect(clearData) # create tagselector specific menu item tsMenu = QMenu(gAddonNamePrettyString, mw) a = tsMenu.addAction("clear saved data") qconnect(a.triggered, clearData) # insert it into GUI try: # note: 'try' because we don't want to crash if # anki internals for menues changed menu = mw.form.menuTools menu.insertMenu(mw.form.actionAdd_ons, tsMenu) except: pass
from aqt.qt import qconnect # from aqt.utils import showInfo from PyQt5 import QtWidgets # This function expects existing cards in the collection and the cards in the file to be imported each to contain a # "generated_*" tag. Existing cards with this tag that are *not* updated by the import are deleted. def put_import(): generated_tags = set() for cid in mw.col.find_cards(""): card = mw.col.getCard(cid) card_generated_tags = [ tag for tag in card.note().tags if tag.startswith("generated_") ] generated_tags.update(card_generated_tags) # showInfo(_("found tags: " + ",".join(generated_tags))) onImport(mw) query = " or ".join(["tag:" + tag for tag in generated_tags]) # showInfo(_("query: " + query)) cards_to_remove = [cid for cid in mw.col.find_cards(query)] # showInfo(_("to rem: " + ",".join([str(card) for card in cards_to_remove]))) mw.col.remove_notes_by_card(cards_to_remove) check_db(mw) actionPutImport = QtWidgets.QAction(mw) actionPutImport.setText(_("&Put Import")) actionPutImport.setObjectName("actionPutImport") qconnect(actionPutImport.triggered, put_import) mw.form.menuCol.addAction(actionPutImport)
def __init__(self) -> None: QObject.__init__(self) self._executor = ThreadPoolExecutor() self._closures: List[Closure] = [] self._closures_lock = Lock() qconnect(self._closures_pending, self._on_closures_pending)
def setup_editor(editor): if isinstance(editor.parentWindow, EditCurrent): qconnect(editor.tags.lostFocus, lambda: redraw_reviewer(editor.mw.reviewer))
def options_menu(menu, did): action = menu.addAction('Life Drain') menu.insertAction(menu.actions()[2], action) qt.qconnect(action.triggered, lambda b: action_deck_settings(did))