def gen_qt_hr(): vbox = QVBoxLayout() hr = QFrame() hr.setAutoFillBackground(True) hr.setFrameShape(QFrame.HLine) hr.setStyleSheet("QFrame { background-color: #0060bf; color: #0060bf; }") hr.setFixedHeight(2) vbox.addWidget(hr) vbox.addSpacing(4) return vbox
def displaygrid(self, config, units): self.generate(config, units) self.win = QDialog(mw) self.wv = KanjiGridWebView() vl = QVBoxLayout() vl.setContentsMargins(0, 0, 0, 0) vl.addWidget(self.wv) self.wv.stdHtml(self.html) hl = QHBoxLayout() vl.addLayout(hl) sh = QPushButton("Save HTML", clicked=lambda: self.savehtml(config)) hl.addWidget(sh) sp = QPushButton("Save Image", clicked=self.savepng) hl.addWidget(sp) bb = QPushButton("Close", clicked=self.win.reject) hl.addWidget(bb) self.win.setLayout(vl) self.win.resize(500, 400) self.timepoint("Window complete") return 0
class Previewer(QDialog): _last_state = None _card_changed = False _last_render: Union[int, float] = 0 _timer = None _show_both_sides = False def __init__(self, parent: QWidget, mw: AnkiQt, on_close: Callable[[], None]): super().__init__(None, Qt.Window) self._open = True self._parent = parent self._close_callback = on_close self.mw = mw icon = QIcon() icon.addPixmap(QPixmap(":/icons/anki.png"), QIcon.Normal, QIcon.Off) self.setWindowIcon(icon) def card(self) -> Optional[Card]: raise NotImplementedError def card_changed(self) -> bool: raise NotImplementedError def open(self): self._state = "question" self._last_state = None self._create_gui() self._setup_web_view() self.render_card() self.show() 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(_("Back Side Only")) 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 _on_finished(self, ok): saveGeom(self, "preview") self.mw.progress.timer(100, self._on_close, False) def _on_replay_audio(self): if self._state == "question": replay_audio(self.card(), True) elif self._state == "answer": replay_audio(self.card(), False) def close(self): self._on_close() super().close() def _on_close(self): self._open = False self._close_callback() def _setup_web_view(self): jsinc = [ "jquery.js", "browsersel.js", "mathjax/conf.js", "mathjax/MathJax.js", "reviewer.js", ] self._web.stdHtml( self.mw.reviewer.revHtml(), css=["reviewer.css"], js=jsinc, context=self, ) self._web.set_bridge_command(self._on_bridge_cmd, self) def _on_bridge_cmd(self, cmd: str) -> Any: if cmd.startswith("play:"): play_clicked_audio(cmd, self.card()) def render_card(self): self.cancel_timer() # Keep track of whether render() has ever been called # with cardChanged=True since the last successful render self._card_changed |= self.card_changed() # avoid rendering in quick succession elap_ms = int((time.time() - self._last_render) * 1000) delay = 300 if elap_ms < delay: self._timer = self.mw.progress.timer(delay - elap_ms, self._render_scheduled, False) else: self._render_scheduled() def cancel_timer(self): if self._timer: self._timer.stop() self._timer = None def _render_scheduled(self) -> None: self.cancel_timer() self._last_render = time.time() if not self._open: return c = self.card() func = "_showQuestion" if not c: txt = _("(please select 1 card)") bodyclass = "" self._last_state = None else: if self._show_both_sides: self._state = "answer" elif self._card_changed: self._state = "question" currentState = self._state_and_mod() if currentState == self._last_state: # nothing has changed, avoid refreshing return # need to force reload even if answer txt = c.q(reload=True) if self._state == "answer": func = "_showAnswer" txt = c.a() txt = re.sub(r"\[\[type:[^]]+\]\]", "", txt) bodyclass = theme_manager.body_classes_for_card_ord(c.ord) if c.autoplay(): if self._show_both_sides: # if we're showing both sides at once, remove any audio # from the answer that's appeared on the question already question_audio = c.question_av_tags() only_on_answer_audio = [ x for x in c.answer_av_tags() if x not in question_audio ] audio = question_audio + only_on_answer_audio elif self._state == "question": audio = c.question_av_tags() else: audio = c.answer_av_tags() av_player.play_tags(audio) else: av_player.clear_queue_and_maybe_interrupt() txt = self.mw.prepare_card_text_for_display(txt) txt = gui_hooks.card_will_show( txt, c, "preview" + self._state.capitalize()) self._last_state = self._state_and_mod() self._web.eval("{}({},'{}');".format(func, json.dumps(txt), bodyclass)) self._card_changed = False def _on_show_both_sides(self, toggle): self._show_both_sides = toggle self.mw.col.conf["previewBothSides"] = toggle self.mw.col.setMod() if self._state == "answer" and not toggle: self._state = "question" self.render_card() def _state_and_mod(self): c = self.card() n = c.note() n.load() return (self._state, c.id, n.mod) def state(self) -> str: return self._state
class Hyperlink(QDialog): def __init__(self, editor, parent_window, selected_text, selected_is_url=False): QDialog.__init__(self, parent_window, Qt.Window) self.editor_instance = editor self.parent_window = parent_window self.selected_text = selected_text self.selected_is_url = selected_is_url self.setWindowTitle("Anki: Create a hyperlink") self.resize(DIALOG_SIZE_X, DIALOG_SIZE_Y) restoreGeom(self, "318752047__add_hyperlink") self.pb_ok = QPushButton("&OK", self) self.pb_ok.setEnabled(False) self.pb_ok.clicked.connect( lambda: self.insert_anchor(url_edit.text(), urltext_edit.text())) self.pb_cancel = QPushButton("&Cancel", self) self.pb_cancel.clicked.connect(self.reject) url_label = QLabel("Link to:") url_edit = QLineEdit() url_edit.setPlaceholderText("URL") url_edit.textChanged.connect(lambda: self.enable_ok_button( self.pb_ok, url_edit.text(), urltext_edit.text())) urltext_label = QLabel("Text to display:") urltext_edit = QLineEdit() urltext_edit.setPlaceholderText("Text") urltext_edit.textChanged.connect(lambda: self.enable_ok_button( self.pb_ok, url_edit.text(), urltext_edit.text())) self.button_bar = QHBoxLayout() self.button_bar.addStretch(1) self.button_bar.addWidget(self.pb_cancel) self.button_bar.addWidget(self.pb_ok) self.dialog_vbox = QVBoxLayout() self.dialog_vbox.addWidget(url_label) self.dialog_vbox.addWidget(url_edit) self.dialog_vbox.addWidget(urltext_label) self.dialog_vbox.addWidget(urltext_edit) self.dialog_vbox.addLayout(self.button_bar) self.setLayout(self.dialog_vbox) # if user already selected text, put it in urltext_edit if self.selected_text: if self.selected_is_url: url_edit.setText(self.selected_text) urltext_edit.setFocus() else: urltext_edit.setText(self.selected_text) url_edit.setFocus() @staticmethod def enable_ok_button(button, url, text): if url and text: button.setEnabled(True) else: button.setEnabled(False) @staticmethod def combine_to_hyperlink(url, text): # Create a hyperlink string, where `url` is the hyperlink reference # and `text` the content of the tag. text = escape_html_chars(text) if gc("remove whitespace from beginning and end of urls"): url = url.strip() if gc("encode_illegal_characters_in_links"): url = some_percent_encoding(url) out = "<a href=\"{0}\">{1}</a>".format(url, text) return out def reject(self): saveGeom(self, "318752047__add_hyperlink") QDialog.reject(self) def insert_anchor(self, url, text): # Inserts a HTML anchor `<a>` into the text field. self.replacement = self.combine_to_hyperlink(url, text) self.url = url self.text = text saveGeom(self, "318752047__add_hyperlink") self.accept()
class ImportDialog(QDialog): def __init__(self, mw, importer): QDialog.__init__(self, mw, Qt.Window) self.mw = mw self.importer = importer self.frm = aqt.forms.importing.Ui_ImportDialog() self.frm.setupUi(self) self.frm.buttonBox.button(QDialogButtonBox.Help).clicked.connect( self.helpRequested) self.setupMappingFrame() self.setupOptions() self.modelChanged() self.frm.autoDetect.setVisible(self.importer.needDelimiter) addHook("currentModelChanged", self.modelChanged) self.frm.autoDetect.clicked.connect(self.onDelimiter) self.updateDelimiterButtonText() self.frm.allowHTML.setChecked(self.mw.pm.profile.get( 'allowHTML', True)) self.frm.importMode.setCurrentIndex( self.mw.pm.profile.get('importMode', 1)) # import button b = QPushButton(_("Import")) self.frm.buttonBox.addButton(b, QDialogButtonBox.AcceptRole) self.exec_() def setupOptions(self): self.model = self.mw.col.models.current() self.modelChooser = aqt.modelchooser.ModelChooser(self.mw, self.frm.modelArea, label=False) self.deck = aqt.deckchooser.DeckChooser(self.mw, self.frm.deckArea, label=False) def modelChanged(self): self.importer.model = self.mw.col.models.current() self.importer.initMapping() self.showMapping() if self.mw.col.conf.get("addToCur", True): did = self.mw.col.conf['curDeck'] if self.mw.col.decks.isDyn(did): did = 1 else: did = self.importer.model['did'] #self.deck.setText(self.mw.col.decks.name(did)) def onDelimiter(self): str = getOnlyText(_("""\ By default, Anki will detect the character between fields, such as a tab, comma, and so on. If Anki is detecting the character incorrectly, you can enter it here. Use \\t to represent tab."""), self, help="importing") or "\t" str = str.replace("\\t", "\t") if len(str) > 1: showWarning( _("Multi-character separators are not supported. " "Please enter one character only.")) return self.hideMapping() def updateDelim(): self.importer.delimiter = str self.importer.updateDelimiter() self.showMapping(hook=updateDelim) self.updateDelimiterButtonText() def updateDelimiterButtonText(self): if not self.importer.needDelimiter: return if self.importer.delimiter: d = self.importer.delimiter else: d = self.importer.dialect.delimiter if d == "\t": d = _("Tab") elif d == ",": d = _("Comma") elif d == " ": d = _("Space") elif d == ";": d = _("Semicolon") elif d == ":": d = _("Colon") else: d = repr(d) txt = _("Fields separated by: %s") % d self.frm.autoDetect.setText(txt) def accept(self): self.importer.mapping = self.mapping if not self.importer.mappingOk(): showWarning(_("The first field of the note type must be mapped.")) return self.importer.importMode = self.frm.importMode.currentIndex() self.mw.pm.profile['importMode'] = self.importer.importMode self.importer.allowHTML = self.frm.allowHTML.isChecked() self.mw.pm.profile['allowHTML'] = self.importer.allowHTML did = self.deck.selectedId() if did != self.importer.model['did']: self.importer.model['did'] = did self.mw.col.models.save(self.importer.model) self.mw.col.decks.select(did) self.mw.progress.start(immediate=True) self.mw.checkpoint(_("Import")) try: self.importer.run() except UnicodeDecodeError: showUnicodeWarning() return except Exception as e: msg = _("Import failed.\n") err = repr(str(e)) if "1-character string" in err: msg += err elif "invalidTempFolder" in err: msg += self.mw.errorHandler.tempFolderMsg() else: msg += str(traceback.format_exc(), "ascii", "replace") showText(msg) return finally: self.mw.progress.finish() txt = _("Importing complete.") + "\n" if self.importer.log: txt += "\n".join(self.importer.log) self.close() showText(txt) self.mw.reset() def setupMappingFrame(self): # qt seems to have a bug with adding/removing from a grid, so we add # to a separate object and add/remove that instead self.frame = QFrame(self.frm.mappingArea) self.frm.mappingArea.setWidget(self.frame) self.mapbox = QVBoxLayout(self.frame) self.mapbox.setContentsMargins(0, 0, 0, 0) self.mapwidget = None def hideMapping(self): self.frm.mappingGroup.hide() def showMapping(self, keepMapping=False, hook=None): if hook: hook() if not keepMapping: self.mapping = self.importer.mapping self.frm.mappingGroup.show() assert self.importer.fields() # set up the mapping grid if self.mapwidget: self.mapbox.removeWidget(self.mapwidget) self.mapwidget.deleteLater() self.mapwidget = QWidget() self.mapbox.addWidget(self.mapwidget) self.grid = QGridLayout(self.mapwidget) self.mapwidget.setLayout(self.grid) self.grid.setContentsMargins(3, 3, 3, 3) self.grid.setSpacing(6) fields = self.importer.fields() for num in range(len(self.mapping)): text = _("Field <b>%d</b> of file is:") % (num + 1) self.grid.addWidget(QLabel(text), num, 0) if self.mapping[num] == "_tags": text = _("mapped to <b>Tags</b>") elif self.mapping[num]: text = _("mapped to <b>%s</b>") % self.mapping[num] else: text = _("<ignored>") self.grid.addWidget(QLabel(text), num, 1) button = QPushButton(_("Change")) self.grid.addWidget(button, num, 2) button.clicked.connect( lambda _, s=self, n=num: s.changeMappingNum(n)) def changeMappingNum(self, n): f = ChangeMap(self.mw, self.importer.model, self.mapping[n]).getField() try: # make sure we don't have it twice index = self.mapping.index(f) self.mapping[index] = None except ValueError: pass self.mapping[n] = f if getattr(self.importer, "delimiter", False): self.savedDelimiter = self.importer.delimiter def updateDelim(): self.importer.delimiter = self.savedDelimiter self.showMapping(hook=updateDelim, keepMapping=True) else: self.showMapping(keepMapping=True) def reject(self): self.modelChooser.cleanup() self.deck.cleanup() remHook("currentModelChanged", self.modelChanged) QDialog.reject(self) def helpRequested(self): openHelp("importing")
class Previewer(QDialog): _last_state: Optional[LastStateAndMod] = None _card_changed = False _last_render: Union[int, float] = 0 _timer: Optional[QTimer] = None _show_both_sides = False def __init__(self, parent: QWidget, mw: AnkiQt, on_close: Callable[[], None]) -> None: super().__init__(None, Qt.Window) self._open = True self._parent = parent self._close_callback = on_close self.mw = mw icon = QIcon() icon.addPixmap(QPixmap(":/icons/anki.png"), QIcon.Normal, QIcon.Off) disable_help_button(self) self.setWindowIcon(icon) def card(self) -> Optional[Card]: raise NotImplementedError def card_changed(self) -> bool: raise NotImplementedError def open(self) -> None: self._state = "question" self._last_state = None self._create_gui() self._setup_web_view() self.render_card() self.show() 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 _on_finished(self, ok: int) -> None: saveGeom(self, "preview") self.mw.progress.timer(100, self._on_close, False) def _on_replay_audio(self) -> None: if self._state == "question": replay_audio(self.card(), True) elif self._state == "answer": replay_audio(self.card(), False) def close(self) -> None: self._on_close() super().close() def _on_close(self) -> None: self._open = False self._close_callback() def _setup_web_view(self) -> None: jsinc = [ "js/vendor/jquery.min.js", "js/vendor/css_browser_selector.min.js", "js/mathjax.js", "js/vendor/mathjax/tex-chtml.js", "js/reviewer.js", ] self._web.stdHtml( self.mw.reviewer.revHtml(), css=["css/reviewer.css"], js=jsinc, context=self, ) self._web.set_bridge_command(self._on_bridge_cmd, self) def _on_bridge_cmd(self, cmd: str) -> Any: if cmd.startswith("play:"): play_clicked_audio(cmd, self.card()) def render_card(self) -> None: self.cancel_timer() # Keep track of whether render() has ever been called # with cardChanged=True since the last successful render self._card_changed |= self.card_changed() # avoid rendering in quick succession elap_ms = int((time.time() - self._last_render) * 1000) delay = 300 if elap_ms < delay: self._timer = self.mw.progress.timer(delay - elap_ms, self._render_scheduled, False) else: self._render_scheduled() def cancel_timer(self) -> None: if self._timer: self._timer.stop() self._timer = None def _render_scheduled(self) -> None: self.cancel_timer() self._last_render = time.time() if not self._open: return c = self.card() func = "_showQuestion" if not c: txt = tr(TR.QT_MISC_PLEASE_SELECT_1_CARD) bodyclass = "" self._last_state = None else: if self._show_both_sides: self._state = "answer" elif self._card_changed: self._state = "question" currentState = self._state_and_mod() if currentState == self._last_state: # nothing has changed, avoid refreshing return # need to force reload even if answer txt = c.q(reload=True) if self._state == "answer": func = "_showAnswer" txt = c.a() txt = re.sub(r"\[\[type:[^]]+\]\]", "", txt) bodyclass = theme_manager.body_classes_for_card_ord(c.ord) if c.autoplay(): AnkiWebView.setPlaybackRequiresGesture(False) if self._show_both_sides: # if we're showing both sides at once, remove any audio # from the answer that's appeared on the question already question_audio = c.question_av_tags() only_on_answer_audio = [ x for x in c.answer_av_tags() if x not in question_audio ] audio = question_audio + only_on_answer_audio elif self._state == "question": audio = c.question_av_tags() else: audio = c.answer_av_tags() av_player.play_tags(audio) else: AnkiWebView.setPlaybackRequiresGesture(True) av_player.clear_queue_and_maybe_interrupt() txt = self.mw.prepare_card_text_for_display(txt) txt = gui_hooks.card_will_show( txt, c, f"preview{self._state.capitalize()}") self._last_state = self._state_and_mod() self._web.eval(f"{func}({json.dumps(txt)},'{bodyclass}');") self._card_changed = False def _on_show_both_sides(self, toggle: bool) -> None: self._show_both_sides = toggle self.mw.col.set_config_bool(Config.Bool.PREVIEW_BOTH_SIDES, toggle) if self._state == "answer" and not toggle: self._state = "question" self.render_card() def _state_and_mod(self) -> Tuple[str, int, int]: c = self.card() n = c.note() n.load() return (self._state, c.id, n.mod) def state(self) -> str: return self._state
class Hyperlink(QDialog): def __init__(self, editor, parent_window, selected_visible_text, selected_is_url=False): QDialog.__init__(self, parent_window, Qt.Window) self.editor_instance = editor self.parent_window = parent_window self.visible_text = selected_visible_text self.selected_is_url = selected_is_url self.setWindowTitle("Anki: Create a hyperlink") self.resize(500, 200) restoreGeom(self, "318752047__add_hyperlink") self.pb_ok = QPushButton("&OK", self) self.pb_ok.setEnabled(False) self.pb_ok.clicked.connect(self.store_hyperlink_and_close) self.pb_cancel = QPushButton("&Cancel", self) self.pb_cancel.clicked.connect(self.reject) self.url_label = QLabel("Link to:") self.url_edit = QLineEdit() self.url_edit.setPlaceholderText("URL") self.url_edit.textChanged.connect(self.maybe_enable_ok_button) self.text_label = QLabel("Text to display:") self.text_edit = QLineEdit() self.text_edit.setPlaceholderText("Text") self.text_edit.textChanged.connect(self.maybe_enable_ok_button) self.button_bar = QHBoxLayout() self.button_bar.addStretch(1) self.button_bar.addWidget(self.pb_cancel) self.button_bar.addWidget(self.pb_ok) self.dialog_vbox = QVBoxLayout() self.dialog_vbox.addWidget(self.url_label) self.dialog_vbox.addWidget(self.url_edit) self.dialog_vbox.addWidget(self.text_label) self.dialog_vbox.addWidget(self.text_edit) self.dialog_vbox.addLayout(self.button_bar) self.setLayout(self.dialog_vbox) # if user already selected text, put it in self.text_edit if self.visible_text: if self.selected_is_url: self.url_edit.setText(self.visible_text) self.text_edit.setFocus() else: self.text_edit.setText(self.visible_text) self.url_edit.setFocus() def maybe_enable_ok_button(self): if self.url_edit.text() and self.text_edit.text(): self.pb_ok.setEnabled(True) else: self.pb_ok.setEnabled(False) def reject(self): saveGeom(self, "318752047__add_hyperlink") QDialog.reject(self) def store_hyperlink_and_close(self): self.url = self.url_edit.text() self.text = self.text_edit.text() self.replacement = combine_to_hyperlink(self.url, self.text) saveGeom(self, "318752047__add_hyperlink") self.accept()
def downloadMedia(url, editor): # Local file : just read the file content if url.startswith("file://"): try: url = url[7:] # On windows, paths tend to be prefixed by file:/// # rather than file://, so we remove redundant slash. if re.match(r'^/[A-Za-z]:\\', url): url = url[1:] # on mac else if url.startswith('//'): url = url[1:] return open(url, 'rb').read() except OSError: pass app = editor.mw.app # Show download dialog d = QDialog(editor.parentWindow) d.setWindowTitle("Downloading media (0.0%)") d.setWindowModality(Qt.WindowModal) vbox = QVBoxLayout() label = QLabel(url) label.setWordWrap(True) vbox.addWidget(label) d.setLayout(vbox) d.show() # Download chunk by chunk for progress bar try: response = urllib2.urlopen(url) totSize = int(response.info().getheader('Content-Length').strip()) currentRead = 0 chunk_size = 16384 chunks = [] while True: chunk = response.read(chunk_size) currentRead += len(chunk) if not chunk: break d.setWindowTitle( "Downloading media (%.1f%%)" % (currentRead * 100.0 / totSize) ) app.processEvents() chunks.append(chunk) return ''.join(chunks) except urllib2.URLError: return None finally: d.close() del d
def setupUI(self): mainLayout = QVBoxLayout() self.setLayout(mainLayout) # add widgets to set height and width widthLabel = QLabel('width') heightLabel = QLabel('height') self.widthEdit = QLineEdit(self) self.widthValidate = self.validate_label() self.heightValidate = self.validate_label() self.widthEdit.textEdited.connect(lambda i, v=self.widthValidate: self.onchange(i, v)) self.heightEdit = QLineEdit(self) self.heightEdit.textEdited.connect(lambda i, v=self.heightValidate: self.onchange(i, v)) self.attr2qt["width"] = self.widthEdit self.attr2qt["height"] = self.heightEdit wLayout = QHBoxLayout() wLayout.addWidget(widthLabel) wLayout.addWidget(self.widthEdit) hLayout = QHBoxLayout() hLayout.addWidget(heightLabel) hLayout.addWidget(self.heightEdit) sizeInputLayout = QHBoxLayout() sizeInputLayout.addLayout(wLayout) sizeInputLayout.addLayout(hLayout) labelLayout = QHBoxLayout() labelLayout.addWidget(self.widthValidate) labelLayout.addWidget(self.heightValidate) sizeLayout = QVBoxLayout() sizeLayout.addLayout(sizeInputLayout) sizeLayout.addLayout(labelLayout) # add final layout to main layout mainLayout.addLayout(sizeLayout) mainLayout.addWidget(self.hLine()) # add min- sizes and max- sizes if self.config["min-size"]: minWidthLabel = QLabel("min-width") minHeightLabel = QLabel("min-height") self.minWidthEdit = QLineEdit(self) self.minHeightEdit = QLineEdit(self) minLayout = QHBoxLayout() minLayout.addWidget(minWidthLabel) minLayout.addWidget(self.minWidthEdit) minLayout.addWidget(minHeightLabel) minLayout.addWidget(self.minHeightEdit) self.minWidthEdit.textEdited.connect(lambda i, v=self.widthValidate: self.onchange(i, v)) self.minHeightEdit.textEdited.connect(lambda i, v=self.heightValidate: self.onchange(i, v)) self.attr2qt["min-width"] = self.minWidthEdit self.attr2qt["min-height"] = self.minHeightEdit mainLayout.addLayout(minLayout) mainLayout.addWidget(self.hLine()) if self.config["max-size"]: maxWidthLabel = QLabel("max-width") maxHeightLabel = QLabel("max-height") self.maxWidthEdit = QLineEdit(self) self.maxHeightEdit = QLineEdit(self) maxLayout = QHBoxLayout() maxLayout.addWidget(maxWidthLabel) maxLayout.addWidget(self.maxWidthEdit) maxLayout.addWidget(maxHeightLabel) maxLayout.addWidget(self.maxHeightEdit) self.maxWidthEdit.textEdited.connect(lambda i, v=self.widthValidate: self.onchange(i, v)) self.maxHeightEdit.textEdited.connect(lambda i, v=self.heightValidate: self.onchange(i, v)) self.attr2qt["max-width"] = self.maxWidthEdit self.attr2qt["max-height"] = self.maxHeightEdit mainLayout.addLayout(maxLayout) mainLayout.addWidget(self.hLine()) # add widgets to show original width, height owidthLabel = QLabel('original width') oheightLabel = QLabel('original height') self.originalWidth = QLineEdit(self) self.originalHeight = QLineEdit(self) self.disableLineEdit(self.originalWidth) self.disableLineEdit(self.originalHeight) sizeLayout2 = QHBoxLayout() sizeLayout2.addWidget(owidthLabel) sizeLayout2.addWidget(self.originalWidth) sizeLayout2.addWidget(oheightLabel) sizeLayout2.addWidget(self.originalHeight) mainLayout.addLayout(sizeLayout2) # add Image Occlusion related buttons if self.is_occl: mainLayout.addWidget(self.hLine()) occlLabel = QLabel("Image Occlusion") occlLabel.setStyleSheet("QLabel {font-weight : bold;}") mainLayout.addWidget(occlLabel) occlAllNote = QCheckBox("Apply to all notes") self.occlAllNote = occlAllNote occlLayout = QHBoxLayout() occlLayout.addWidget(occlAllNote) self.attr2qt["Apply to all notes"] = self.occlAllNote if self.curr_fld in self.main.all_occl_flds: occlAllFld = QCheckBox("Apply to all fields") self.occlAllFld = occlAllFld occlLayout.addWidget(occlAllFld) self.attr2qt["Apply to all fields"] = self.occlAllFld mainLayout.addLayout(occlLayout) # add buttons okButton = QPushButton("OK") okButton.clicked.connect(self.clicked_ok) okButton.setDefault(True) okButton.setShortcut("Ctrl+Return") cancelButton = QPushButton("Cancel") cancelButton.clicked.connect(self.clicked_cancel) resetButton = QPushButton("Default") resetButton.clicked.connect(self.clicked_defaults) btnLayout = QHBoxLayout() btnLayout.addStretch(1) btnLayout.addWidget(okButton) btnLayout.addWidget(cancelButton) btnLayout.addWidget(resetButton) mainLayout.addLayout(btnLayout) # center the window self.move(QDesktopWidget().availableGeometry().center() - self.frameGeometry().center()) self.setWindowTitle('Style Editor') self.show()
class Previewer(QDialog): _last_state: LastStateAndMod | None = None _card_changed = False _last_render: int | float = 0 _timer: QTimer | None = None _show_both_sides = False def __init__( self, parent: QWidget, mw: AnkiQt, on_close: Callable[[], None] ) -> None: super().__init__(None, Qt.WindowType.Window) mw.garbage_collect_on_dialog_finish(self) self._open = True self._parent = parent self._close_callback = on_close self.mw = mw disable_help_button(self) setWindowIcon(self) def card(self) -> Card | None: raise NotImplementedError def card_changed(self) -> bool: raise NotImplementedError def open(self) -> None: self._state = "question" self._last_state = None self._create_gui() self._setup_web_view() self.render_card() self.show() 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 _on_finished(self, ok: int) -> None: saveGeom(self, "preview") self.mw.progress.timer(100, self._on_close, False) def _on_replay_audio(self) -> None: if self._state == "question": replay_audio(self.card(), True) elif self._state == "answer": replay_audio(self.card(), False) def _on_close(self) -> None: self._open = False self._close_callback() self._web.cleanup() self._web = None def _setup_web_view(self) -> None: self._web.stdHtml( self.mw.reviewer.revHtml(), css=["css/reviewer.css"], js=[ "js/mathjax.js", "js/vendor/mathjax/tex-chtml.js", "js/reviewer.js", ], context=self, ) self._web.set_bridge_command(self._on_bridge_cmd, self) def _on_bridge_cmd(self, cmd: str) -> Any: if cmd.startswith("play:"): play_clicked_audio(cmd, self.card()) def _update_flag_and_mark_icons(self, card: Card | None) -> None: if card: flag = card.user_flag() marked = card.note(reload=True).has_tag(MARKED_TAG) else: flag = 0 marked = False self._web.eval(f"_drawFlag({flag}); _drawMark({json.dumps(marked)});") def render_card(self) -> None: self.cancel_timer() # Keep track of whether render() has ever been called # with cardChanged=True since the last successful render self._card_changed |= self.card_changed() # avoid rendering in quick succession elap_ms = int((time.time() - self._last_render) * 1000) delay = 300 if elap_ms < delay: self._timer = self.mw.progress.timer( delay - elap_ms, self._render_scheduled, False ) else: self._render_scheduled() def cancel_timer(self) -> None: if self._timer: self._timer.stop() self._timer = None def _render_scheduled(self) -> None: self.cancel_timer() self._last_render = time.time() if not self._open: return c = self.card() self._update_flag_and_mark_icons(c) func = "_showQuestion" ans_txt = "" if not c: txt = tr.qt_misc_please_select_1_card() bodyclass = "" self._last_state = None else: if self._show_both_sides: self._state = "answer" elif self._card_changed: self._state = "question" currentState = self._state_and_mod() if currentState == self._last_state: # nothing has changed, avoid refreshing return # need to force reload even if answer txt = c.question(reload=True) ans_txt = c.answer() if self._state == "answer": func = "_showAnswer" txt = ans_txt txt = re.sub(r"\[\[type:[^]]+\]\]", "", txt) bodyclass = theme_manager.body_classes_for_card_ord(c.ord) if c.autoplay(): self._web.setPlaybackRequiresGesture(False) if self._show_both_sides: # if we're showing both sides at once, remove any audio # from the answer that's appeared on the question already question_audio = c.question_av_tags() only_on_answer_audio = [ x for x in c.answer_av_tags() if x not in question_audio ] audio = question_audio + only_on_answer_audio elif self._state == "question": audio = c.question_av_tags() else: audio = c.answer_av_tags() av_player.play_tags(audio) else: self._web.setPlaybackRequiresGesture(True) av_player.clear_queue_and_maybe_interrupt() txt = self.mw.prepare_card_text_for_display(txt) txt = gui_hooks.card_will_show(txt, c, f"preview{self._state.capitalize()}") self._last_state = self._state_and_mod() js: str if self._state == "question": ans_txt = self.mw.col.media.escape_media_filenames(ans_txt) js = f"{func}({json.dumps(txt)}, {json.dumps(ans_txt)}, '{bodyclass}');" else: js = f"{func}({json.dumps(txt)}, '{bodyclass}');" self._web.eval(js) self._card_changed = False def _on_show_both_sides(self, toggle: bool) -> None: self._show_both_sides = toggle self.mw.col.set_config_bool(Config.Bool.PREVIEW_BOTH_SIDES, toggle) if self._state == "answer" and not toggle: self._state = "question" self.render_card() def _state_and_mod(self) -> tuple[str, int, int]: c = self.card() n = c.note() n.load() return (self._state, c.id, n.mod) def state(self) -> str: return self._state
def setup(self): addonconfig = mw.addonManager.getConfig(__name__) config = types.SimpleNamespace(**addonconfig['defaults']) if addonconfig.get("_debug_time", False): self.timepoint = lambda c: print("%s: %0.3f" % (c, time.time()-self.time)) else: self.timepoint = lambda _: None config.did = mw.col.conf['curDeck'] swin = QDialog(mw) vl = QVBoxLayout() fl = QHBoxLayout() deckcb = QComboBox() deckcb.addItems(sorted(mw.col.decks.allNames())) deckcb.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) fl.addWidget(QLabel("Deck: ")) deckcb.setCurrentText(mw.col.decks.get(config.did)['name']) def change_did(deckname): config.did = mw.col.decks.byName(deckname)['id'] deckcb.currentTextChanged.connect(change_did) fl.addWidget(deckcb) vl.addLayout(fl) frm = QGroupBox("Settings") vl.addWidget(frm) il = QVBoxLayout() fl = QHBoxLayout() field = QLineEdit() field.setPlaceholderText("e.g. \"kanji\" or \"sentence-kanji\" (default: \"%s\")" % config.pattern) il.addWidget(QLabel("Pattern or Field names to search for (case insensitive):")) fl.addWidget(field) liter = QCheckBox("Match exactly") liter.setChecked(config.literal) fl.addWidget(liter) il.addLayout(fl) stint = QSpinBox() stint.setRange(1, 65536) stint.setValue(config.interval) il.addWidget(QLabel("Card interval considered strong:")) il.addWidget(stint) ttcol = QSpinBox() ttcol.setRange(1, 99) ttcol.setValue(config.thin) il.addWidget(QLabel("Number of Columns:")) coll = QHBoxLayout() coll.addWidget(QLabel("In-app:")) coll.addWidget(ttcol) wtcol = QSpinBox() wtcol.setRange(1, 99) wtcol.setValue(config.wide) coll.addWidget(QLabel("Exported:")) coll.addWidget(wtcol) itcol = QCheckBox("Don't care") itcol.setChecked(addonconfig['defaults'].get("autothinwide", False)) def disableEnableColumnSettings(state): ttcol.setEnabled(state != Qt.Checked) wtcol.setEnabled(state != Qt.Checked) itcol.stateChanged.connect(disableEnableColumnSettings) disableEnableColumnSettings(itcol.checkState()) coll.addWidget(itcol) il.addLayout(coll) groupby = QComboBox() groupby.addItems([ *("None, sorted by " + x.pretty_value() for x in SortOrder), *(x.name for x in data.groups), ]) groupby.setCurrentIndex(config.groupby) il.addWidget(QLabel("Group by:")) il.addWidget(groupby) shnew = QCheckBox("Show units not yet seen") shnew.setChecked(config.unseen) il.addWidget(shnew) toolt = QCheckBox("Show informational tooltips") toolt.setChecked(config.tooltips) il.addWidget(toolt) frm.setLayout(il) hl = QHBoxLayout() vl.addLayout(hl) gen = QPushButton("Generate", clicked=swin.accept) hl.addWidget(gen) cls = QPushButton("Close", clicked=swin.reject) hl.addWidget(cls) swin.setLayout(vl) swin.setTabOrder(gen, cls) swin.setTabOrder(cls, field) swin.setTabOrder(field, liter) swin.setTabOrder(liter, stint) swin.setTabOrder(stint, ttcol) swin.setTabOrder(ttcol, wtcol) swin.setTabOrder(wtcol, groupby) swin.setTabOrder(groupby, shnew) swin.setTabOrder(shnew, toolt) swin.resize(500, 400) if swin.exec_(): mw.progress.start(immediate=True) if len(field.text().strip()) != 0: config.pattern = field.text().lower() config.pattern = config.pattern.split() config.literal = liter.isChecked() config.interval = stint.value() config.thin = ttcol.value() config.wide = wtcol.value() config.groupby = groupby.currentIndex() config.unseen = shnew.isChecked() config.tooltips = toolt.isChecked() config.autothinwide = itcol.isChecked() self.makegrid(config) mw.progress.finish() self.win.show()
def setup_evernote(self): global icoEvernoteWeb global imgEvernoteWeb global elements global evernote_query_last_updated global evernote_pagination_current_page_spinner def update_checkbox(setting): if setting is DECKS.EVERNOTE_NOTEBOOK_INTEGRATION and not elements[DECKS.BASE].text(): return if setting.get.startswith(QUERY.get): update_evernote_query_visibilities() setting.save(elements[setting].isChecked()) # mw.col.conf[setting] = if setting is QUERY.USE_TAGS: update_evernote_query_visibilities() if setting is QUERY.LAST_UPDATED.USE: evernote_query_last_updated_value_set_visibilities() def create_checkbox(setting, label=" ", default_value=False, is_fixed_size=False, fixed_width=None): if isinstance(label, bool): default_value = label label = " " checkbox = QCheckBox(label, self) sval = setting.fetch() if not isinstance(sval, bool): sval = default_value checkbox.setChecked(sval) # noinspection PyUnresolvedReferences checkbox.stateChanged.connect(lambda: update_checkbox(setting)) if is_fixed_size or fixed_width: checkbox.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) if fixed_width: checkbox.setFixedWidth(fixed_width) elements[setting] = checkbox return checkbox def create_checked_checkbox(*a, **kw): kw['default_value'] = True return create_checkbox(*a, **kw) def update_text(setting, text): text = text.strip() setting.save(text) if setting is DECKS.BASE: update_anki_deck_visibilities() if setting.get.startswith(QUERY.get): if text: use_key = getattr(QUERY, 'USE_' + setting.label.name) elements[use_key].setChecked(True) evernote_query_text_changed() if setting is QUERY.SEARCH_TERMS: update_evernote_query_visibilities() def create_textbox(setting, default_value=""): textbox = QLineEdit() textbox.setText(setting.fetch(default_value)) textbox.connect(textbox, SIGNAL("textEdited(QString)"), lambda text: update_text(setting, text)) elements[setting] = textbox return textbox def add_query_row(setting, is_checked=False, **kw): try: default_value = setting.val except: default_value = '' row_label = ' '.join(x.capitalize() for x in setting.replace('_', ' ').split()) hbox = QHBoxLayout() hbox.addWidget(create_checkbox(getattr(QUERY, 'USE_' + setting), default_value=is_checked, **kw)) hbox.addWidget(create_textbox(getattr(QUERY, setting), default_value)) form.addRow(row_label, hbox) def gen_qt_hr(): vbox = QVBoxLayout() hr = QFrame() hr.setAutoFillBackground(True) hr.setFrameShape(QFrame.HLine) hr.setStyleSheet("QFrame { background-color: #0060bf; color: #0060bf; }") hr.setFixedHeight(2) vbox.addWidget(hr) vbox.addSpacing(4) return vbox # Begin setup_evernote() widget = QWidget() layout = QVBoxLayout() elements = {} rm_log_path('Dicts\\') evernote_query_last_updated = DictCaseInsensitive() ########################## QUERY ########################## ##################### QUERY: TEXTBOXES #################### group = QGroupBox("EVERNOTE SEARCH OPTIONS:") group.setStyleSheet('QGroupBox{ font-size: 10px; font-weight: bold; color: rgb(105, 170, 53);}') form = QFormLayout() form.addRow(gen_qt_hr()) # Show Generated Evernote Query Button button_show_generated_evernote_query = QPushButton(icoEvernoteWeb, "Show Full Query", self) button_show_generated_evernote_query.setAutoDefault(False) button_show_generated_evernote_query.connect(button_show_generated_evernote_query, SIGNAL("clicked()"), handle_show_generated_evernote_query) # Add Form Row for Match Any Terms hbox = QHBoxLayout() hbox.addWidget(create_checked_checkbox(QUERY.ANY, " Match Any Terms", is_fixed_size=True)) hbox.addWidget(button_show_generated_evernote_query) form.addRow("<b>Search Parameters:</b>", hbox) # Add Form Rows for Evernote Query Textboxes for el in QUERY_TEXTBOXES: add_query_row(el, 'TAGS' in el) ################### QUERY: LAST UPDATED ################### # Evernote Query: Last Updated Type evernote_query_last_updated.type = QComboBox() evernote_query_last_updated.type.setStyleSheet(' QComboBox { color: rgb(45, 79, 201); font-weight: bold; } ') evernote_query_last_updated.type.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) evernote_query_last_updated.type.addItems([u"Δ Day", u"Δ Week", u"Δ Month", u"Δ Year", "Date", "+ Time"]) evernote_query_last_updated.type.setCurrentIndex(QUERY.LAST_UPDATED.TYPE.fetch(EvernoteQueryLocationType.RelativeDay)) evernote_query_last_updated.type.activated.connect(update_evernote_query_last_updated_type) # Evernote Query: Last Updated Type: Relative Date evernote_query_last_updated.value.relative.spinner = EvernoteQueryLocationValueQSpinBox() evernote_query_last_updated.value.relative.spinner.setVisible(False) evernote_query_last_updated.value.relative.spinner.setStyleSheet( " QSpinBox, EvernoteQueryLocationValueQSpinBox { font-weight: bold; color: rgb(173, 0, 0); } ") evernote_query_last_updated.value.relative.spinner.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) evernote_query_last_updated.value.relative.spinner.connect(evernote_query_last_updated.value.relative.spinner, SIGNAL("valueChanged(int)"), update_evernote_query_last_updated_value_relative_spinner) # Evernote Query: Last Updated Type: Absolute Date evernote_query_last_updated.value.absolute.date = QDateEdit() evernote_query_last_updated.value.absolute.date.setDisplayFormat('M/d/yy') evernote_query_last_updated.value.absolute.date.setCalendarPopup(True) evernote_query_last_updated.value.absolute.date.setVisible(False) evernote_query_last_updated.value.absolute.date.setStyleSheet( "QDateEdit { font-weight: bold; color: rgb(173, 0, 0); } ") evernote_query_last_updated.value.absolute.date.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) evernote_query_last_updated.value.absolute.date.connect(evernote_query_last_updated.value.absolute.date, SIGNAL("dateChanged(QDate)"), update_evernote_query_last_updated_value_absolute_date) # Evernote Query: Last Updated Type: Absolute DateTime evernote_query_last_updated.value.absolute.datetime = QDateTimeEdit() evernote_query_last_updated.value.absolute.datetime.setDisplayFormat('M/d/yy h:mm AP') evernote_query_last_updated.value.absolute.datetime.setCalendarPopup(True) evernote_query_last_updated.value.absolute.datetime.setVisible(False) evernote_query_last_updated.value.absolute.datetime.setStyleSheet( "QDateTimeEdit { font-weight: bold; color: rgb(173, 0, 0); } ") evernote_query_last_updated.value.absolute.datetime.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) evernote_query_last_updated.value.absolute.datetime.connect(evernote_query_last_updated.value.absolute.datetime, SIGNAL("dateTimeChanged(QDateTime)"), update_evernote_query_last_updated_value_absolute_datetime) # Evernote Query: Last Updated Type: Absolute Time evernote_query_last_updated.value.absolute.time = QTimeEdit() evernote_query_last_updated.value.absolute.time.setDisplayFormat('h:mm AP') evernote_query_last_updated.value.absolute.time.setVisible(False) evernote_query_last_updated.value.absolute.time.setStyleSheet( "QTimeEdit { font-weight: bold; color: rgb(143, 0, 30); } ") evernote_query_last_updated.value.absolute.time.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) evernote_query_last_updated.value.absolute.time.connect(evernote_query_last_updated.value.absolute.time, SIGNAL("timeChanged(QTime)"), update_evernote_query_last_updated_value_absolute_time) # Create HBox for Separated Date & Time hbox_datetime = QHBoxLayout() hbox_datetime.addWidget(evernote_query_last_updated.value.absolute.date) hbox_datetime.addWidget(evernote_query_last_updated.value.absolute.time) # Evernote Query: Last Updated Type evernote_query_last_updated.value.stacked_layout = QStackedLayout() evernote_query_last_updated.value.stacked_layout.addWidget(evernote_query_last_updated.value.relative.spinner) evernote_query_last_updated.value.stacked_layout.addItem(hbox_datetime) # Add Form Row for Evernote Query: Last Updated hbox = QHBoxLayout() label = QLabel("Last Updated: ") label.setMinimumWidth(SETTINGS.FORM.LABEL_MINIMUM_WIDTH.val) hbox.addWidget(create_checkbox(QUERY.LAST_UPDATED.USE, is_fixed_size=True)) hbox.addWidget(evernote_query_last_updated.type) hbox.addWidget(evernote_query_last_updated.value.relative.spinner) hbox.addWidget(evernote_query_last_updated.value.absolute.date) hbox.addWidget(evernote_query_last_updated.value.absolute.time) form.addRow(label, hbox) # Add Horizontal Row Separator form.addRow(gen_qt_hr()) ############################ PAGINATION ########################## # Evernote Pagination: Current Page evernote_pagination_current_page_spinner = QSpinBox() evernote_pagination_current_page_spinner.setStyleSheet("QSpinBox { font-weight: bold; color: rgb(173, 0, 0); } ") evernote_pagination_current_page_spinner.setPrefix("PAGE: ") evernote_pagination_current_page_spinner.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) evernote_pagination_current_page_spinner.setValue(EVERNOTE.PAGINATION_CURRENT_PAGE.fetch(1)) evernote_pagination_current_page_spinner.connect(evernote_pagination_current_page_spinner, SIGNAL("valueChanged(int)"), update_evernote_pagination_current_page_spinner) # Evernote Pagination: Automation hbox = QHBoxLayout() hbox.addWidget(create_checked_checkbox(EVERNOTE.AUTO_PAGING, " Automate", fixed_width=105)) hbox.addWidget(evernote_pagination_current_page_spinner) # Add Form Row for Evernote Pagination form.addRow("<b>Pagination:</b>", hbox) # Add Query Form to Group Box group.setLayout(form) # Add Query Group Box to Main Layout layout.addWidget(group) ########################## DECK ########################## # Setup Group Box and Form group = QGroupBox("ANKI NOTE OPTIONS:") group.setStyleSheet('QGroupBox{ font-size: 10px; font-weight: bold; color: rgb(105, 170, 53);}') form = QFormLayout() # Add Horizontal Row Separator form.addRow(gen_qt_hr()) # Add Form Row for Default Anki Deck hbox = QHBoxLayout() hbox.insertSpacing(0, 33) hbox.addWidget(create_textbox(DECKS.BASE, DECKS.BASE_DEFAULT_VALUE)) label_deck = QLabel("<b>Anki Deck:</b>") label_deck.setMinimumWidth(SETTINGS.FORM.LABEL_MINIMUM_WIDTH.val) form.addRow(label_deck, hbox) # Add Form Row for Evernote Notebook Integration label_deck = QLabel("Evernote Notebook:") label_deck.setMinimumWidth(SETTINGS.FORM.LABEL_MINIMUM_WIDTH.val) form.addRow("", create_checked_checkbox(DECKS.EVERNOTE_NOTEBOOK_INTEGRATION, " Append Evernote Notebook")) # Add Horizontal Row Separator form.addRow(gen_qt_hr()) ############################ TAGS ########################## # Add Form Row for Evernote Tag Options label = QLabel("<b>Evernote Tags:</b>") label.setMinimumWidth(SETTINGS.FORM.LABEL_MINIMUM_WIDTH.val) # Tags: Save To Anki Note form.addRow(label, create_checkbox(TAGS.KEEP_TAGS, " Save To Anki Note", TAGS.KEEP_TAGS_DEFAULT_VALUE)) hbox = QHBoxLayout() hbox.insertSpacing(0, 33) hbox.addWidget(create_textbox(TAGS.TO_DELETE)) # Tags: Tags To Delete form.addRow("Tags to Delete:", hbox) form.addRow(" ", create_checkbox(TAGS.DELETE_EVERNOTE_QUERY_TAGS, " Also Delete Search Tags")) # Add Horizontal Row Separator form.addRow(gen_qt_hr()) ############################ NOTE UPDATING ########################## # Note Update Method update_existing_notes = QComboBox() update_existing_notes.setStyleSheet( ' QComboBox { color: #3b679e; font-weight: bold; } QComboBoxItem { color: #A40F2D; font-weight: bold; } ') update_existing_notes.addItems(["Ignore Existing Notes", "Update In-Place", "Delete and Re-Add"]) sval = ANKI.UPDATE_EXISTING_NOTES.fetch() if not isinstance(sval, int): sval = ANKI.UPDATE_EXISTING_NOTES.val update_existing_notes.setCurrentIndex(sval) update_existing_notes.activated.connect(update_update_existing_notes) # Add Form Row for Note Update Method hbox = QHBoxLayout() hbox.insertSpacing(0, 33) hbox.addWidget(update_existing_notes) form.addRow("<b>Note Updating:</b>", hbox) # Add Note Update Method Form to Group Box group.setLayout(form) # Add Note Update Method Group Box to Main Layout layout.addWidget(group) ######################### UPDATE VISIBILITIES ####################### # Update Visibilities of Anki Deck Options update_anki_deck_visibilities() # Update Visibilities of Query Options evernote_query_text_changed() update_evernote_query_visibilities() ######################## ADD TO SETTINGS PANEL ###################### # Vertical Spacer vertical_spacer = QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Expanding) layout.addItem(vertical_spacer) # Parent Widget widget.setLayout(layout) # New Tab self.form.tabWidget.addTab(widget, "Anknotes")
def setup(self): addonconfig = mw.addonManager.getConfig(__name__) config = types.SimpleNamespace(**addonconfig['defaults']) if addonconfig.get("_debug_time", False): self.timepoint = lambda c: print("%s: %0.3f" % (c, time.time() - self.time)) else: self.timepoint = lambda _: None config.did = mw.col.conf['curDeck'] swin = QDialog(mw) vl = QVBoxLayout() fl = QHBoxLayout() deckcb = QComboBox() deckcb.addItems(sorted(mw.col.decks.allNames())) deckcb.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) fl.addWidget(QLabel("Deck: ")) deckcb.setCurrentText(mw.col.decks.get(config.did)['name']) def change_did(deckname): config.did = mw.col.decks.byName(deckname)['id'] deckcb.currentTextChanged.connect(change_did) fl.addWidget(deckcb) vl.addLayout(fl) frm = QGroupBox("Settings") vl.addWidget(frm) il = QVBoxLayout() fl = QHBoxLayout() field = QLineEdit() field.setPlaceholderText( "e.g. \"kanji\" or \"sentence-kanji\" (default: \"%s\")" % config.pattern) il.addWidget( QLabel("Pattern or Field names to search for (case insensitive):")) fl.addWidget(field) liter = QCheckBox("Match exactly") liter.setChecked(config.literal) fl.addWidget(liter) il.addLayout(fl) stint = QSpinBox() stint.setRange(1, 65536) stint.setValue(config.interval) il.addWidget(QLabel("Card interval considered strong:")) il.addWidget(stint) ttcol = QSpinBox() ttcol.setRange(1, 99) ttcol.setValue(config.thin) il.addWidget(QLabel("Number of Columns:")) coll = QHBoxLayout() coll.addWidget(QLabel("In-app:")) coll.addWidget(ttcol) wtcol = QSpinBox() wtcol.setRange(1, 99) wtcol.setValue(config.wide) coll.addWidget(QLabel("Exported:")) coll.addWidget(wtcol) itcol = QCheckBox("Don't care") itcol.setChecked(addonconfig['defaults'].get("autothinwide", False)) def disableEnableColumnSettings(state): ttcol.setEnabled(state != Qt.Checked) wtcol.setEnabled(state != Qt.Checked) itcol.stateChanged.connect(disableEnableColumnSettings) disableEnableColumnSettings(itcol.checkState()) coll.addWidget(itcol) il.addLayout(coll) groupby = QComboBox() groupby.addItems([ *("None, sorted by " + x.pretty_value() for x in SortOrder), *(x.name for x in data.groups), ]) groupby.setCurrentIndex(config.groupby) il.addWidget(QLabel("Group by:")) il.addWidget(groupby) shnew = QCheckBox("Show units not yet seen") shnew.setChecked(config.unseen) il.addWidget(shnew) toolt = QCheckBox("Show informational tooltips") toolt.setChecked(config.tooltips) il.addWidget(toolt) frm.setLayout(il) hl = QHBoxLayout() vl.addLayout(hl) gen = QPushButton("Generate", clicked=swin.accept) hl.addWidget(gen) cls = QPushButton("Close", clicked=swin.reject) hl.addWidget(cls) swin.setLayout(vl) swin.setTabOrder(gen, cls) swin.setTabOrder(cls, field) swin.setTabOrder(field, liter) swin.setTabOrder(liter, stint) swin.setTabOrder(stint, ttcol) swin.setTabOrder(ttcol, wtcol) swin.setTabOrder(wtcol, groupby) swin.setTabOrder(groupby, shnew) swin.setTabOrder(shnew, toolt) swin.resize(500, 400) if swin.exec_(): mw.progress.start(immediate=True) if len(field.text().strip()) != 0: config.pattern = field.text().lower() config.pattern = config.pattern.split() config.literal = liter.isChecked() config.interval = stint.value() config.thin = ttcol.value() config.wide = wtcol.value() config.groupby = groupby.currentIndex() config.unseen = shnew.isChecked() config.tooltips = toolt.isChecked() config.autothinwide = itcol.isChecked() self.makegrid(config) mw.progress.finish() self.win.show()