Python源码示例:anki.hooks.wrap()
示例1
def initializeLinks():
Overview._linkHandler = wrap(Overview._linkHandler, heatmapLinkHandler, "around")
DeckBrowser._linkHandler = wrap(
DeckBrowser._linkHandler, heatmapLinkHandler, "around"
)
DeckStats._linkHandler = heatmapLinkHandler
try:
from aqt.gui_hooks import browser_will_search
browser_will_search.append(on_browser_will_search)
except (ImportError, ModuleNotFoundError):
from anki.find import Finder
Finder.findRevlogEntries = findRevlogEntries
Finder.__init__ = wrap(Finder.__init__, addFinders, "after")
示例2
def _setupAddonMenus20(self):
from anki.hooks import wrap
from aqt.addons import AddonManager
from ..gui.dialog_configeditor import ConfigEditor
from ..consts import ADDON
from ..platform import PATH_ADDONS
def onEdit(mgr, file_path, _old):
entry_point = os.path.join(
PATH_ADDONS, ADDON.NAME + ".py")
if not file_path == entry_point:
return _old(mgr, file_path)
if self.conf_action:
self.conf_action()
elif "local" in self._config:
ConfigEditor(self, self.mw)
else:
return _old(mgr, file_path)
AddonManager.onEdit = wrap(AddonManager.onEdit, onEdit, "around")
# Local storage
######################################################################
示例3
def _ensureSaveBeforeConfigGUILoaded(self) -> None:
"""ugly workaround, drop as soon as possible"""
if checkVersion(ANKI.VERSION, "2.1.17"):
self._mw.addonManager.setConfigAction(
self._namespace, self._saveBeforeConfigLoaded
)
return
from anki.hooks import wrap
from aqt.addons import AddonsDialog
def wrappedOnConfig(addonsDialog: AddonsDialog, *args, **kwargs):
"""Save before config editor is invoked"""
addon = addonsDialog.onlyOneSelected()
if not addon or addon != self._namespace:
return
self.save()
AddonsDialog.onConfig = wrap(AddonsDialog.onConfig, wrappedOnConfig, "before")
示例4
def _setupAddonMenus20(self):
from anki.hooks import wrap
from aqt.addons import AddonManager
from ..gui.dialog_configeditor import ConfigEditor
from ..consts import ADDON
from ..platform import DIRECTORY_ADDONS
def onEdit(mgr, file_path, _old):
entry_point = os.path.join(
DIRECTORY_ADDONS, ADDON.NAME + ".py")
if not file_path == entry_point:
return _old(mgr, file_path)
if self.conf_action:
self.conf_action()
elif "local" in self._config:
ConfigEditor(self, self.mw)
else:
return _old(mgr, file_path)
AddonManager.onEdit = wrap(AddonManager.onEdit, onEdit, "around")
# Local storage
######################################################################
示例5
def onInsertCloze(self, _old):
"""Handles cloze-wraps when the add-on model is active"""
if not checkModel(self.note.model(), fields=False, notify=False):
return _old(self)
# find the highest existing cloze
highest = 0
for name, val in self.note.items():
m = re.findall("\[\[oc(\d+)::", val)
if m:
highest = max(highest, sorted([int(x) for x in m])[-1])
# reuse last?
if not self.mw.app.keyboardModifiers() & Qt.AltModifier:
highest += 1
# must start at 1
highest = max(1, highest)
self.web.eval("wrap('[[oc%d::', ']]');" % highest)
示例6
def initializeEditor():
# Editor widget
Editor.onCloze = wrap(Editor.onCloze, onInsertCloze, "around")
Editor.onOlClozeButton = onOlClozeButton
Editor.onOlOptionsButton = onOlOptionsButton
Editor.onInsertMultipleClozes = onInsertMultipleClozes
Editor.onRemoveClozes = onRemoveClozes
if ANKI20:
addHook("setupEditorButtons", onSetupEditorButtons20)
else:
addHook("setupEditorButtons", onSetupEditorButtons21)
# AddCard / EditCurrent windows
AddCards.addNote = wrap(AddCards.addNote, onAddNote, "around")
if ANKI20:
AddCards.addCards = wrap(AddCards.addCards, onAddCards, "around")
EditCurrent.onSave = wrap(EditCurrent.onSave, onEditCurrent, "around")
else:
# always use the methods that are fired on editor save:
AddCards._addCards = wrap(AddCards._addCards, onAddCards, "around")
EditCurrent._saveAndClose = wrap(EditCurrent._saveAndClose,
onEditCurrent, "around")
示例7
def load():
ruby.install()
chinese.install()
addHook('profileLoaded', load_menu)
addHook('profileLoaded', add_models)
addHook('loadNote', append_tone_styling)
addHook('unloadProfile', config.save)
addHook('unloadProfile', dictionary.conn.close)
addHook('unloadProfile', unload_menu)
CollectionStats.todayStats = wrap(
CollectionStats.todayStats, todayStats, 'around'
)
EditManager()
示例8
def patch_config():
from aqt.clayout import CardLayout
from .config import add_maobi_button
CardLayout.setupButtons = wrap(CardLayout.setupButtons, add_maobi_button)
示例9
def initializeViews():
try:
from aqt.gui_hooks import (
deck_browser_will_render_content,
overview_will_render_content,
)
deck_browser_will_render_content.append(on_deckbrowser_will_render_content)
overview_will_render_content.append(on_overview_will_render_content)
except (ImportError, ModuleNotFoundError):
Overview._body = ov_body
Overview._renderPage = overviewRenderPage
DeckBrowser._renderStats = wrap(
DeckBrowser._renderStats, deckbrowserRenderStats, "around"
)
# TODO: Submit Anki PR to add hook to CollectionStats.report
CollectionStats.dueGraph = wrap(
CollectionStats.dueGraph, collectionStatsDueGraph, "around"
)
DeckStats.__init__ = wrap(DeckStats.__init__, deckStatsInit21, "after")
DeckStats.reject = wrap(DeckStats.reject, deckStatsReject)
# Initially set up hotkey:
# TODO: Migrate to config.json storage, so that profile hook is not required
try:
from aqt.gui_hooks import profile_did_open
profile_did_open.append(initializeHotkey)
except (ImportError, ModuleNotFoundError):
addHook("profileLoaded", initializeHotkey)
# Update hotkey on config save:
addHook("config_saved_heatmap", initializeHotkey)
示例10
def customize_addcards():
AddCards.setupButtons = wrap(
AddCards.setupButtons, add_query_button, "before")
示例11
def profileLoaded():
"""Support for Advanced Previewer"""
try:
from advanced_previewer.previewer import Previewer
except ImportError:
return
Previewer.linkHandler = wrap(
Previewer.linkHandler, linkHandler, "around")
addHook("previewerMungeQA", linkInserter)
示例12
def onProfileLoaded():
"""
Apply modifications to Browser and Previewer
Needs to be run after all other add-ons have been loaded
"""
Browser.onTagMove = onTagMove
Browser.switchToSidebarItem = switchToSidebarItem
Browser.setFilter = wrap(Browser.setFilter, onSetFilter, "around")
Previewer.setupHotkeys = wrap(Previewer.setupHotkeys, onSetupHotkeys, "after")
示例13
def profileLoaded():
# add menu entry
mw.addonManager.rebuildAddonsMenu = wrap(mw.addonManager.rebuildAddonsMenu,
siblingMenu)
mw.addonManager.rebuildAddonsMenu()
# add scheduler
anki.sched.Scheduler._adjRevIvl = wrap(anki.sched.Scheduler._adjRevIvl,
siblingIvl, "around")
# --- Hooks: ---
示例14
def pasteWithoutLinebreaks(self):
"""Remove linebreaks from clipboard and paste"""
mime = self.web.mungeClip(mode=QClipboard.Clipboard)
html = mime.html()
text = mime.text()
if not(text or html):
return
if html:
content = re.sub(re_lb_open, " ", html)
content = re.sub(re_lb_close, "", content)
else:
if PRESERVE_PARAGRAPHS:
# remove EOL hyphens:
content = re.sub(r"-\n(?!\n)", "", text)
# skip ends of sentences:
content = re.sub(r"(?<!\.|\!|\:)\n(?!\n)", " ", content)
# strip extraneous linebreaks while rejoining with <br> tags
content = "<br />".join(i.strip() for i in content.split("\n"))
else:
content = content.replace("\n", " ")
self.web.eval("""
var pasteHTML = function(html) {
setFormat("inserthtml", html);
};
var filterHTML = function(html) {
// wrap it in <top> as we aren't allowed to change top level elements
var top = $.parseHTML("<ankitop>" + html + "</ankitop>")[0];
filterNode(top);
var outHtml = top.innerHTML;
// get rid of nbsp
outHtml = outHtml.replace(/ /ig, " ");
return outHtml;
};
pasteHTML(%s);
""" % json.dumps(content))
if TOOLTIP:
tooltip("Pasting without linebreaks", period=500)
示例15
def initializeReviewer():
if ANKI20:
Reviewer._keyHandler = wrap(
Reviewer._keyHandler, newKeyHandler20, "before")
else:
Reviewer._shortcutKeys = wrap(Reviewer._shortcutKeys, onShortcutKeys21,
"around")
示例16
def initializeScheduler():
for scheduler in SCHEDULERS:
scheduler._burySiblings = wrap(
scheduler._burySiblings, myBurySiblings, "around")
示例17
def __init__(cls, name, bases, attributes):
super().__init__(name, bases, attributes)
# singleton
cls.instance = None
old_creator = cls.__new__
cls.__new__ = singleton_creator(old_creator)
# additions and replacements
cls.additions = {}
cls.replacements = {}
target = attributes.get('target', None)
def callback_maker(wrapper):
def raw_new(*args, **kwargs):
return wrapper(cls.instance, *args, **kwargs)
return raw_new
for key, attr in attributes.items():
if key == 'init':
key = '__init__'
if hasattr(attr, 'wraps'):
if not target:
raise Exception(f'Asked to wrap "{key}" but target of {name} not defined')
original = getattr(target, key)
if type(original) is MethodType:
original = original.__func__
new = wrap(original, callback_maker(attr), attr.position)
# for classes, just add the new function, it will be bound later,
# but instances need some more work: we need to bind!
if not isclass(target):
new = MethodType(new, target)
cls.replacements[key] = new
if hasattr(attr, 'appends_in_night_mode'):
if not target:
raise Exception(f'Asked to replace "{key}" but target of {name} not defined')
cls.additions[key] = attr
if hasattr(attr, 'replaces_in_night_mode'):
if not target:
raise Exception(f'Asked to replace "{key}" but target of {name} not defined')
cls.replacements[key] = attr
# TODO: invoke and cache css?
if hasattr(attr, 'is_css'):
pass
示例18
def init_addon():
""" Executed once on Anki startup. """
global origEditorContextMenuEvt
gui_hooks.webview_did_receive_js_message.append(expanded_on_bridge_cmd)
#todo: Find out if there is a better moment to start index creation
state.db_file_existed = create_db_file_if_not_exists()
gui_hooks.profile_did_open.append(build_index)
gui_hooks.profile_did_open.append(insert_scripts)
gui_hooks.profile_did_open.append(lambda : recalculate_priority_queue(True))
if conf_or_def("searchOnTagEntry", True):
TagEdit.keyPressEvent = wrap(TagEdit.keyPressEvent, tag_edit_keypress, "around")
setup_tagedit_timer()
# add new notes to search index when adding
gui_hooks.add_cards_did_add_note.append(add_note_to_index)
# update notes in index when changed through the "Edit" button
EditDialog.saveAndClose = wrap(EditDialog.saveAndClose, editor_save_with_index_update, "around")
# register add-on's shortcuts
gui_hooks.editor_did_init_shortcuts.append(register_shortcuts)
# reset state after the add/edit dialog is opened
gui_hooks.editor_did_init_shortcuts.append(reset_state)
# activate nighmode if Anki's nightmode is active
gui_hooks.editor_did_init_shortcuts.append(activate_nightmode)
# add-on internal hooks
setup_hooks()
# add shortcuts
aqt.editor._html += """
<script>
document.addEventListener("keydown", function (e) {globalKeydown(e); }, false);
</script>"""
#this inserts all the javascript functions in scripts.js into the editor webview
aqt.editor._html += getScriptPlatformSpecific()
#when a note is loaded (i.e. the add cards dialog is opened), we have to insert our html for the search ui
gui_hooks.editor_did_load_note.append(on_load_note)
示例19
def customize_addcards():
"""
add button to addcards window
"""
def add_query_button(self):
'''
add a button in add card window
'''
bb = self.form.buttonBox
ar = QDialogButtonBox.ActionRole
# button
fastwqBtn = QPushButton(_("QUERY") + u" " + downArrow())
fastwqBtn.setShortcut(QKeySequence(my_shortcut))
fastwqBtn.setToolTip(_(u"Shortcut: %s") % shortcut(my_shortcut))
bb.addButton(fastwqBtn, ar)
# signal
def onQuery(e):
if isinstance(e, QMouseEvent):
if e.buttons() & Qt.LeftButton:
menu = QMenu(self)
menu.addAction(
_("ALL_FIELDS"),
lambda: query_from_editor_fields(self.editor),
QKeySequence(my_shortcut))
# default options
mid = self.editor.note.model()['id']
conf = config.get_maps(mid)
conf = {
'list': [conf],
'def': 0
} if isinstance(conf, list) else conf
maps_list = conf['list']
if len(maps_list) > 1:
menu.addSeparator()
for i, maps in enumerate(maps_list):
menu.addAction(
_OK_ICON if i == conf['def'] else _NULL_ICON,
_('CONFIG_INDEX') % (i + 1) if isinstance(
maps, list) else maps['name'],
lambda mid=mid, i=i: set_options_def(mid, i))
menu.addSeparator()
# end default options
menu.addAction(_("OPTIONS"), lambda: show_options(self, self.editor.note.model()['id']))
menu.exec_(
fastwqBtn.mapToGlobal(QPoint(0, fastwqBtn.height())))
else:
query_from_editor_fields(self.editor)
fastwqBtn.mousePressEvent = onQuery
fastwqBtn.clicked.connect(onQuery)
AddCards.setupButtons = wrap(AddCards.setupButtons, add_query_button,
"after")
示例20
def customize_addcards():
"""
add button to addcards window
"""
def add_query_button(self):
'''
add a button in add card window
'''
bb = self.form.buttonBox
ar = QDialogButtonBox.ActionRole
# button
fastwqBtn = QPushButton(_("QUERY") + u" " + downArrow())
fastwqBtn.setShortcut(QKeySequence(my_shortcut))
fastwqBtn.setToolTip(
_(u"Shortcut: %s") % shortcut(my_shortcut)
)
bb.addButton(fastwqBtn, ar)
# signal
def onQuery(e):
if isinstance(e, QMouseEvent):
if e.buttons() & Qt.LeftButton:
menu = QMenu(self)
menu.addAction(_("ALL_FIELDS"), lambda: query_from_editor_fields(self.editor), QKeySequence(my_shortcut))
# default options
mid = self.editor.note.model()['id']
conf = config.get_maps(mid)
conf = {'list': [conf], 'def': 0} if isinstance(conf, list) else conf
maps_list = conf['list']
if len(maps_list) > 1:
menu.addSeparator()
for i, maps in enumerate(maps_list):
menu.addAction(
_OK_ICON if i==conf['def'] else _NULL_ICON,
_('CONFIG_INDEX') % (i+1) if isinstance(maps, list) else maps['name'],
lambda mid=mid, i=i: set_options_def(mid, i)
)
menu.addSeparator()
# end default options
menu.addAction(_("OPTIONS"), lambda: show_options(self, self.editor.note.model()['id']))
menu.exec_(fastwqBtn.mapToGlobal(QPoint(0, fastwqBtn.height())))
else:
query_from_editor_fields(self.editor)
fastwqBtn.mousePressEvent = onQuery
fastwqBtn.clicked.connect(onQuery)
AddCards.setupButtons = wrap(
AddCards.setupButtons,
add_query_button,
"after"
)