def rubify(editor: Editor): selected_text: str = editor.web.selectedText() if not selected_text: showInfo("""You must select some text first""", parent=editor.widget, title='Simple Mandarin Ruby') return try: editor.mw.app.setOverrideCursor(Qt.WaitCursor) global dict_has_loaded if not dict_has_loaded: from .data.custom_phrases_dict import phrases_dict pypinyin.load_phrases_dict(phrases_dict) dict_has_loaded = True pinyins = pypinyin.lazy_pinyin(selected_text, style=pypinyin.Style.TONE, errors=lambda chars: list(chars)) if len(pinyins) != len(selected_text): raise IndexError("""Generated pinyin list didn't match string length""") pairs = zip(selected_text, pinyins) output = pairs_to_html(pairs) # Uses document.execCommand('insertHTML', ...) in JS under the hood: editor.doPaste(output, internal=False, extended=True) finally: editor.mw.app.restoreOverrideCursor()
def add_styler_button(buttons: List[str], editor: Editor): editor._links['style'] = on_style buttons.append( editor._addButton( icon='', # TODO: Create some icon cmd='style', tip='Set style (Ctrl+S)'), )
def add_buttons(buttons: List[str], editor: Editor) -> List[str]: if config['soften'] == 'enabled': soften_button = editor.addButton( icon=ICON_SOFTEN_PATH, cmd="soften_text", func=lambda editor: soften(editor), tip=SOFTEN_TOOLTIP+' '+config['soften-shortcut'], keys=config['soften-shortcut'] ) buttons.append(soften_button) if config['contrast'] == 'enabled': contrast_button = editor.addButton( icon=ICON_CONTRAST_PATH, cmd="contrast_text", func=lambda editor: contrast(editor), tip=CONTRAST_TOOLTIP+' '+config['contrast-shortcut'], keys=config['contrast-shortcut'] ) buttons.append(contrast_button) if config['emphasize'] == 'enabled': emphasize_button = editor.addButton( icon=ICON_EMPHASIZE_PATH, cmd="emphasize_text", func=lambda editor: emphasize(editor), tip=EMPHASIZE_TOOLTIP+' '+config['emphasize-shortcut'], keys=config['emphasize-shortcut'] ) buttons.append(emphasize_button) return buttons
def add_editor_button(buttons: List[str], editor: Editor): editor._links["forvo_dl"] = on_editor_btn_click if os.path.isabs(os.path.join(asset_dir, "icon.png")): iconstr = editor.resourceToData(os.path.join(asset_dir, "icon.png")) else: iconstr = "/_anki/imgs/{}.png".format(os.path.join(asset_dir, "icon.png")) return buttons + [ "<div title=\"Hold down shift + click to select top audio\n\nCTRL+F to open window\nCTRL+SHIFT+F to select top audio\nCTRL+S to search for custom term\" style=\"float: right; margin: 0 3px\"><div style=\"display: flex; width: 50px; height: 25px; justify-content: center; align-items: center; padding: 0 5px; border-radius: 5px; background-color: #0094FF; color: #ffffff; font-size: 10px\" onclick=\"pycmd('forvo_dl');return false;\"><img style=\"height: 20px; width: 20px\" src=\"%s\"/></div></div>" % iconstr]
def init_dic(editor: Editor, *args, **kwargs) -> None: """ Get the last selected/default dictionary. :param editor: Anki editor window """ previous_dic = get_default_dictionary(mw) editor.dic = previous_dic
def init_ipa(editor: Editor, *args, **kwargs) -> None: """ Get the last selected/default IPA language. :param editor: Anki editor window """ previous_lang = get_default_lang(mw) editor.ipa_lang_alias = consts.LANGUAGES_MAP.get(previous_lang, "")
def on_setup_buttons(buttons: List[str], editor: Editor) -> List[str]: """ Add Addon button and Addon combobox to card editor. :param buttons: HTML codes of the editor buttons (e.g. for bold, italic, ...) :param editor: card editor object :return: updated list of buttons """ # add HTML button button = editor.addButton(ICON_PATH, "IPA", paste_ipa) buttons.append(button) # create list of language options previous_lang = get_default_lang(mw) options = [f"""<option>{previous_lang}</option>""" ] # first entry is the last selection options += [ f"""<option>{language}</option>""" for language in sorted(consts.LANGUAGES_MAP.keys(), key=str.lower) if language != previous_lang ] # add HTML combobox combo = select_elm.format("".join(options)) buttons.append(combo) return buttons
def add_preview_button(editor: Editor) -> None: preview_shortcut = "Ctrl+Shift+P" # TODO editor._links["preview"] = lambda _editor: self.onTogglePreview() editor.web.eval( "$editorToolbar.then(({ notetypeButtons }) => notetypeButtons.appendButton({ component: editorToolbar.PreviewButton, id: 'preview' }));" )
def munge_field(txt: str, editor: Editor): """Parse -lilypond field/lilypond tags in field before saving""" if editor.currentField is not None: fields: list[str] = _getfields(editor.note.model()) field: str = fields[editor.currentField] if field_match := FIELD_NAME_REGEXP.match(field): # LilyPond field template_name = field_match.group( FIELD_NAME_REGEXP.groupindex['template']) # Check to avoid compiling empty templates img_link = _img_link(template_name, _ly_from_html(txt)) if txt != "" else txt if (dest_field := field_match.group(FIELD_NAME_REGEXP.groupindex['field']) + TARGET_FIELD_NAME_SUFFIX)\ in fields: # Target field exists, populate it editor.note[dest_field] = img_link return txt else: # Substitute in-place if IMG_TAG_REGEXP.match(txt): # Field already contains rendered image return txt else: return img_link
def add_editor_button(cls, buttons: List[str], editor: Editor) -> None: b = editor.addButton( icon=os.path.join(ADDON_PATH, "icons", "edit.svg"), cmd="rename_media_files", func=cls.show_rename_dialog, tip="Rename media files referenced by note.", ) buttons.append(b)
def on_dictionary_select(editor: Editor, dic: str) -> None: """ Set new default dictionary. :param editor: Anki editor window :param dic: name of selected dictionary """ set_default_dictionary(mw, dic) editor.dic = dic
def paste_ipa(editor: Editor) -> None: """ Paste IPA transcription into the IPA field of the Anki editor. :param editor: Anki editor window """ lang_alias = editor.ipa_lang_alias note = editor.note # Get content of text field try: field_text = note[CONFIG["WORD_FIELD"]] except KeyError: showInfo(f"Field '{CONFIG['WORD_FIELD']}' doesn't exist.") return logging.debug(f"Field text: {field_text}") field_text = field_text.lower() if lang_alias == "english": ipa = get_english_ipa_transcription(field_text) else: # get word list from text field words = utils.get_words_from_field(field_text) logging.debug(f"Word list: {words}") # parse IPA transcription for every word in word list try: ipa = parse_ipa_transcription.transcript(words=words, language=lang_alias) except (urllib.error.HTTPError, IndexError): showInfo("IPA not found.") return logging.debug(f"IPA transcription string: {ipa}") # paste IPA transcription of every word in IPA transcription field try: note[CONFIG["IPA_FIELD"]] = ipa except KeyError: showInfo(f"Field '{CONFIG['IPA_FIELD']}' doesn't exist.") return # update editor editor.loadNote() editor.web.setFocus() editor.web.eval("focusField(%d);" % editor.currentField)
def on_ipa_language_select(editor: Editor, lang: str) -> None: """ Set new default IPA language. :param editor: Anki editor window :param lang: name of selected language """ alias = consts.LANGUAGES_MAP[lang] set_default_lang(mw, lang) editor.ipa_lang_alias = alias
def on_editor_did_init_buttons(buttons: List[str], editor: Editor): global config # pylint: disable=global-statement btn = editor.addButton( icon=os.path.join(addon_dir, "icon.svg"), cmd="armajor", func=open_dialog_in_editor, tip="مولد نظام المذكرات الصوتي للعربية", keys=config.get("shortcut", "Ctrl+Shift+K"), ) buttons.append(btn)
def add_preview_button(editor: Editor) -> None: preview_shortcut = "Ctrl+Shift+P" # TODO editor._links["preview"] = lambda _editor: self.onTogglePreview() editor.web.eval(f""" $editorToolbar.then(({{ addButton }}) => addButton(editorToolbar.labelButton({{ label: `{tr.actions_preview()}`, tooltip: `{tr.browsing_preview_selected_card(val=shortcut(preview_shortcut))}`, onClick: () => bridgeCommand("preview"), disables: false, }}), "notetype", -1)); """)
def __init__(self, mw): QDialog.__init__(self, None, Qt.Window) mw.setupDialogGC(self) self.mw = mw self.form = aqt.forms.editcurrent.Ui_Dialog() self.form.setupUi(self) self.setWindowTitle(_("Edit Current")) self.setMinimumHeight(400) self.setMinimumWidth(500) self.form.buttonBox.button(QDialogButtonBox.Close).setShortcut( QKeySequence("Ctrl+Return")) self.editor = Editor(self.mw, self.form.fieldsArea, self) self.editor.card = self.mw.reviewer.card self.editor.setNote(self.mw.reviewer.card.note(), focusTo=0) restoreGeom(self, "editcurrent") addHook("reset", self.onReset) self.mw.requireReset() self.show() # reset focus after open, taking care not to retain webview # pylint: disable=unnecessary-lambda self.mw.progress.timer(100, lambda: self.editor.web.setFocus(), False)
def add_buttons(buttons, editor: Editor): this_path = os.path.dirname(__file__) icon_path = os.path.abspath(os.path.join(this_path, 'resources', 'ruby.svg')) button = editor.addButton( # icon='qrc:///icons/anki.png', icon=icon_path, cmd='smr_rubify', func=rubify, tip="""Add pinyin ruby to selection""", # label='Ruby', ) buttons.append(button)
def on_setup_buttons(buttons: List[str], editor: Editor) -> List[str]: button = editor.addButton(ICON_PATH, "myad", paste_definitions) buttons.append(button) previous_dic = get_default_dictionary(mw) options = [f"""<option>{previous_dic}</option>""" ] # first entry is the last selection options += [ f"""<option>{dic}</option>""" for dic in dictionaries if dic != previous_dic ] combo = select_elm.format("".join(options)) buttons.append(combo) return buttons
def saveField(note: Note, fld: str, val: str) -> None: if fld == "Tags": # aqt.editor.Editor.saveTags tags = mw.col.tags.split(val) if note.tags == tags: return note.tags = tags elif fld not in note: raise FldNotFoundError(fld) else: # aqt.editor.Editor.onBridgeCmd txt = Editor.mungeHTML(editorwv.editor, val) if note[fld] == txt: return note[fld] = txt mw.checkpoint("Edit Field") note.flush()
def add_preview_button(leftbuttons: List[str], editor: Editor) -> None: preview_shortcut = "Ctrl+Shift+P" leftbuttons.insert( 0, editor.addButton( None, "preview", lambda _editor: self.onTogglePreview(), tr.browsing_preview_selected_card( val=shortcut(preview_shortcut), ), tr.actions_preview(), id="previewButton", keys=preview_shortcut, disables=False, rightside=False, toggleable=True, ), )
def mySave(nid, fld, val): note = mw.col.getNote(nid) # editor L257ff in on onBridgeCmd: # txt = urllib.parse.unquote(txt) # txt = unicodedata.normalize("NFC", txt) # txt = self.mungeHTML(txt) # # misbehaving apps may include a null byte in the text # txt = txt.replace("\x00", "") # # reverse the url quoting we added to get images to display # txt = self.mw.col.media.escapeImages(txt, unescape=True) txt = urllib.parse.unquote(val) txt = unicodedata.normalize("NFC", txt) txt = Editor.mungeHTML(None, txt) txt = txt.replace("\x00", "") txt = mw.col.media.escapeImages(txt, unescape=True) if note[fld] != txt: note[fld] = txt note.flush()
def saveField(note, fld, val): if fld == "Tags": tagsTxt = unicodedata.normalize("NFC", htmlToTextLine(val)) txt = mw.col.tags.canonify(mw.col.tags.split(tagsTxt)) field = note.tags else: # aqt.editor.Editor.onBridgeCmd txt = unicodedata.normalize("NFC", val) txt = Editor.mungeHTML(None, txt) txt = txt.replace("\x00", "") txt = mw.col.media.escapeImages(txt, unescape=True) field = note[fld] if field == txt: return if config['undo']: mw.checkpoint("Edit Field") if fld == "Tags": note.tags = txt else: note[fld] = txt note.flush()
def saveField(note, fld, val): if fld == "Tags": tagsTxt = unicodedata.normalize("NFC", htmlToTextLine(val)) txt = mw.col.tags.canonify(mw.col.tags.split(tagsTxt)) field = note.tags else: # https://github.com/dae/anki/blob/47eab46f05c8cc169393c785f4c3f49cf1d7cca8/aqt/editor.py#L257-L263 txt = urllib.parse.unquote(val) txt = unicodedata.normalize("NFC", txt) txt = Editor.mungeHTML(None, txt) txt = txt.replace("\x00", "") txt = mw.col.media.escapeImages(txt, unescape=True) field = note[fld] if field == txt: return config = mw.addonManager.getConfig(__name__) if config['undo']: mw.checkpoint("Edit Field") if fld == "Tags": note.tags = txt else: note[fld] = txt note.flush()
def paste_definitions(editor: Editor) -> None: note = editor.note try: word = note[CONFIG["WORD_FIELD"]] except KeyError: showdialog(f"Field '{CONFIG['WORD_FIELD']}' doesn't exist.") return logging.debug(f"Field text: {word}") word = word.lower() source_language = CONFIG["SOURCE_LANGUAGE"] target_language = CONFIG["TARGET_LANGUAGE"] abrv_source = CONFIG["LANG_SOURCE_ABRV"] abrv_target = CONFIG["LANG_TARGET_ABRV"] abrv_country = CONFIG["COUNTRY_TARGET_ABRV"] if editor.dic == 'cambridge': results = cambridge.search(word) if editor.dic == 'collins': results = collins.search(word) elif editor.dic == 'macmillan': results = macmillan.search(word) elif editor.dic == 'babla': results = babla.search(word, abrv_target, source_language, target_language) elif editor.dic == 'pons': results = pons.search(word, source_language, target_language) elif editor.dic == 'linguee': results = linguee.search(word, source_language, target_language) elif editor.dic == 'freedic': results = freedic.search(word, abrv_target, abrv_country) elif editor.dic == 'reverso': results = reverso.search(word, abrv_target, target_language) elif editor.dic == 'wordreference': results = wordreference.search(word, abrv_source, abrv_target) if len(results) == 0: showdialog(f"Word {word} not found.") elif results[0] == '' and results[1] == '' and results[ 2] == '' and results[3] == '': showdialog(f"Word {word} without results.") if len(results) >= 1: try: if results[0] != '': if note[editor.dic + CONFIG["TRANSLATIONS_FIELD"]] == '': note[editor.dic + CONFIG["TRANSLATIONS_FIELD"]] = f'{results[0]}' except KeyError: showdialog( f"Field '{editor.dic}{CONFIG['TRANSLATIONS_FIELD']}' doesn't exist." ) return if len(results) >= 2: try: if results[1] != '': note[editor.dic + CONFIG["DEFINITIONS_FIELD"]] = f'{results[1]}' except KeyError: showdialog( f"Field '{editor.dic}{CONFIG['DEFINITIONS_FIELD']}' doesn't exist." ) return if len(results) >= 3: try: if results[2] != '': note[editor.dic + CONFIG["IPA_FIELD"]] = f'{results[2]}' except KeyError: showdialog( f"Field '{editor.dic}{CONFIG['IPA_FIELD']}' doesn't exist.") return if len(results) >= 4: try: if results[3] != '': note[editor.dic + CONFIG["PHRASES_FIELD"]] = f'{results[3]}' except KeyError: showdialog( f"Field '{editor.dic}{CONFIG['PHRASES_FIELD']}' doesn't exist." ) return if len(results) >= 5: try: if results[4] != '': note[editor.dic + CONFIG["TRANSLATED_PHRASES_FIELD"]] = f'{results[4]}' except KeyError: showdialog( f"Field '{editor.dic}{CONFIG['TRANSLATED_PHRASES_FIELD']}' doesn't exist." ) return if len(results) >= 6: try: if results[5] != '': note[editor.dic + CONFIG["EXPRESSIONS_FIELD"]] = f'{results[5]}' except KeyError: showdialog( f"Field '{editor.dic}{CONFIG['EXPRESSIONS_FIELD']}' doesn't exist." ) return # update editor editor.loadNote() editor.web.setFocus() editor.web.eval("focusField(%d);" % editor.currentField)
class EditCurrent(QDialog): def __init__(self, mw): QDialog.__init__(self, None, Qt.Window) mw.setupDialogGC(self) self.mw = mw self.form = aqt.forms.editcurrent.Ui_Dialog() self.form.setupUi(self) self.setWindowTitle(_("Edit Current")) self.setMinimumHeight(400) self.setMinimumWidth(500) self.form.buttonBox.button(QDialogButtonBox.Close).setShortcut( QKeySequence("Ctrl+Return")) self.editor = Editor(self.mw, self.form.fieldsArea, self) self.editor.card = self.mw.reviewer.card self.editor.setNote(self.mw.reviewer.card.note(), focusTo=0) restoreGeom(self, "editcurrent") addHook("reset", self.onReset) self.mw.requireReset() self.show() # reset focus after open, taking care not to retain webview # pylint: disable=unnecessary-lambda self.mw.progress.timer(100, lambda: self.editor.web.setFocus(), False) def onReset(self): # lazy approach for now: throw away edits try: n = self.editor.note n.load() #reload in case the model changed except: # card's been deleted remHook("reset", self.onReset) self.editor.setNote(None) self.mw.reset() aqt.dialogs.markClosed("EditCurrent") self.close() return self.editor.setNote(n) def reopen(self, mw): tooltip("Please finish editing the existing card first.") self.onReset() def reject(self): self.saveAndClose() def saveAndClose(self): self.editor.saveNow(self._saveAndClose) def _saveAndClose(self): remHook("reset", self.onReset) r = self.mw.reviewer try: r.card.load() except: # card was removed by clayout pass else: self.mw.reviewer.cardQueue.append(self.mw.reviewer.card) self.editor.cleanup() self.mw.moveToState("review") saveGeom(self, "editcurrent") aqt.dialogs.markClosed("EditCurrent") QDialog.reject(self) def closeWithCallback(self, onsuccess): def callback(): self._saveAndClose() onsuccess() self.editor.saveNow(callback)
def insert_image_html(editor: Editor, image_filename: str): editor.doPaste(html=f'<img src="{image_filename}">', internal=True)
def add_pronunciation(editor: Editor, mode: Union[None, str] = None): if mode is None: modifiers = QApplication.keyboardModifiers() if modifiers == Qt.ShiftModifier: """Choose top pronunciation automatically when shift key is held down""" mode = "auto" deck_id = editor.card.did if editor.card is not None else editor.parentWindow.deckChooser.selectedId( ) if editor.note is not None: note_type_id = editor.note.mid elif editor.card is not None: note_type_id = editor.card.note().mid else: note_type_id = editor.mw.col.models.current()["id"] search_field = config.get_note_type_specific_config_object( "searchField", note_type_id) if search_field is None or search_field.value not in editor.note.keys(): d = FieldSelector(editor.parentWindow, editor.mw, note_type_id, "searchField", config) d.exec() search_field = handle_field_select(d, note_type_id, "searchField", editor) if search_field is None: return audio_field = config.get_note_type_specific_config_object( "audioField", note_type_id) if audio_field is None or audio_field.value not in editor.note.keys(): d = FieldSelector(editor.parentWindow, editor.mw, note_type_id, "audioField", config) d.exec() audio_field = handle_field_select(d, note_type_id, "audioField", editor) if audio_field is None: return search_field = search_field.value audio_field = audio_field.value if editor.note is None: showInfo( "Please enter a search term in the field '" + search_field + "'.", editor.widget) return if mode == 'input': query, suc = aqt.utils.getText("Please enter a custom search term:", editor.widget, title="Enter custom search term") if not suc: showWarning("Didn't get any text, please try again.", editor.widget) return elif editor.note is not None and search_field in editor.note.keys( ) and len(editor.note[search_field]) != 0: """If available, use the content of the defined search field as the query""" query = editor.note[search_field] else: showInfo( "Please enter a search term in the field '" + search_field + "'.", editor.widget) return query = BeautifulSoup(query, "html.parser").text if deck_id is not None: config_lang = config.get_deck_specific_config_object( "language", deck_id) if config_lang is None: d = LanguageSelector(editor.parentWindow, mw.col.decks.get(deck_id)["name"]) d.exec() if d.selected_lang is not None: config.set_deck_specific_config_object( ConfigObject(name="language", value=d.selected_lang, deck=deck_id, type=OptionType.LANG)) language = d.selected_lang else: showInfo( "Cancelled download because no language was selected.") return else: language = config_lang.value try: forvo = Forvo(query, language, editor.mw, config).load_search_query() if forvo is not None: results = forvo.get_pronunciations().pronunciations else: raise NoResultsException() except NoResultsException: showInfo("No results found! :(", editor.widget) return hidden_entries_amount = 0 if config.get_config_object("skipOggFallback").value: viable_entries = [p for p in results if not p.is_ogg] hidden_entries_amount = len(results) - len(viable_entries) if len(viable_entries) == 0: showInfo( f"No results found! :(\nThere are {hidden_entries_amount} entries which you chose to skip by deactivating .ogg fallback." ) return results = viable_entries if mode == "auto": def add_automatically(auto_results): """If shift key is held down""" auto_results.sort( key=lambda result: result.votes) # sort by votes top: Pronunciation = auto_results[ len(auto_results) - 1] # get most upvoted pronunciation top.download_pronunciation() # download that try: if config.get_config_object( "audioFieldAddMode").value == "append": """append""" editor.note.fields[get_field_id( audio_field, editor.note)] += "[sound:%s]" % top.audio elif config.get_config_object( "audioFieldAddMode").value == "replace": """replace""" editor.note.fields[get_field_id( audio_field, editor.note)] = "[sound:%s]" % top.audio else: """prepend""" editor.note.fields[get_field_id( audio_field, editor.note )] = "[sound:%s]" % top.audio + editor.note.fields[ get_field_id(audio_field, editor.note)] except FieldNotFoundException: showWarning( "Couldn't find field '%s' for adding the audio string. Please create a field with this name or change it in the config for the note type id %s" % (audio_field, str(note_type_id)), editor.widget) if config.get_config_object( "playAudioAfterSingleAddAutomaticSelection" ).value: # play audio if desired anki.sound.play(top.audio) def flush_field(): if not editor.addMode: # save editor.note.flush() editor.currentField = get_field_id(audio_field, editor.note) editor.loadNote( focusTo=get_field_id(audio_field, editor.note)) editor.saveNow(flush_field, keepFocus=True) editor.saveNow(functools.partial(add_automatically, results), keepFocus=False) else: dialog = AddSingle(editor.parentWindow, pronunciations=results, hidden_entries_amount=hidden_entries_amount) dialog.exec() Forvo.cleanup() if dialog.selected_pronunciation is not None: try: add_mode = config.get_config_object( "audioFieldAddMode").value if add_mode == "append": editor.note.fields[get_field_id( audio_field, editor.note )] += "[sound:%s]" % dialog.selected_pronunciation.audio elif add_mode == "prepend": editor.note.fields[ get_field_id(audio_field, editor.note)] = "[sound:%s]" % dialog.selected_pronunciation.audio + \ editor.note.fields[ get_field_id(audio_field, editor.note)] elif add_mode == "replace": editor.note.fields[get_field_id( audio_field, editor.note )] = "[sound:%s]" % dialog.selected_pronunciation.audio except FieldNotFoundException: showWarning( "Couldn't find field '%s' for adding the audio string. Please create a field with this name or change it in the config for the note type id %s" % (audio_field, str(note_type_id)), editor.widget) if not editor.addMode: editor.note.flush() editor.loadNote()
def on_editor_btn_click(editor: Editor, mode: Union[None, str] = None): editor.saveNow(lambda: add_pronunciation(editor, mode))