def _showQuestion(self): self._reps += 1 self.state = "question" self.typedAnswer = None c = self.card # grab the question and play audio if c.isEmpty(): q = _("""\ The front of this card is empty. Please run Tools>Empty Cards.""") else: q = c.q() if self.autoplay(c): playFromText(q) # render & update bottom q = self._mungeQA(q) q = runFilter("prepareQA", q, c, "reviewQuestion") bodyclass = bodyClass(self.mw.col, c) self.web.eval("_showQuestion(%s,'%s');" % (json.dumps(q), bodyclass)) self._drawFlag() self._drawMark() self._showAnswerButton() # if we have a type answer field, focus main web if self.typeCorrect: self.mw.web.setFocus() # user hook runHook('showQuestion')
def changeGroup(self): id = self.fact.id runHook("closeEditCurrent") browser = aqt.dialogs.open("Browser", self.mw) browser.form.searchEdit.setText("fid:%d" % id) browser.onSearch() browser.setGroup(True)
def __init__(self, mw): self.mw = mw f = self.mw.form; s = SIGNAL("triggered()") self.mw.connect(f.actionOpenPluginFolder, s, self.onOpenPluginFolder) self.mw.connect(f.actionEnableAllPlugins, s, self.onEnableAllPlugins) self.mw.connect(f.actionDisableAllPlugins, s, self.onDisableAllPlugins) if isWin: self.clearPluginCache() self.disableObsoletePlugins() plugdir = self.pluginsFolder() sys.path.insert(0, plugdir) plugins = self.enabledPlugins() plugins.sort() self.registeredPlugins = {} for plugin in plugins: try: nopy = plugin.replace(".py", "") __import__(nopy) except: print "Error in %s" % plugin traceback.print_exc() self.mw.disableCardMenuItems() self.rebuildPluginsMenu() # run the obsolete init hook try: runHook('init') except: showWarning( _("Broken plugin:\n\n%s") % unicode(traceback.format_exc(), "utf-8", "replace"))
def setupMenus(self): # actions self.connect(self.dialog.actionDelete, SIGNAL("triggered()"), self.deleteCards) self.connect(self.dialog.actionAddTag, SIGNAL("triggered()"), self.addTags) self.connect(self.dialog.actionDeleteTag, SIGNAL("triggered()"), self.deleteTags) self.connect(self.dialog.actionReschedule, SIGNAL("triggered()"), self.reschedule) self.connect(self.dialog.actionCram, SIGNAL("triggered()"), self.cram) self.connect(self.dialog.actionAddCards, SIGNAL("triggered()"), self.addCards) self.connect(self.dialog.actionChangeModel, SIGNAL("triggered()"), self.onChangeModel) self.connect(self.dialog.actionSuspend, SIGNAL("triggered()"), self.onSuspend) self.connect(self.dialog.actionUnsuspend, SIGNAL("triggered()"), self.onUnsuspend) # edit self.connect(self.dialog.actionFont, SIGNAL("triggered()"), self.onFont) self.connect(self.dialog.actionUndo, SIGNAL("triggered()"), self.onUndo) self.connect(self.dialog.actionRedo, SIGNAL("triggered()"), self.onRedo) self.connect(self.dialog.actionInvertSelection, SIGNAL("triggered()"), self.invertSelection) self.connect(self.dialog.actionReverseOrder, SIGNAL("triggered()"), self.reverseOrder) self.connect(self.dialog.actionSelectFacts, SIGNAL("triggered()"), self.selectFacts) self.connect(self.dialog.actionFindReplace, SIGNAL("triggered()"), self.onFindReplace) # jumps self.connect(self.dialog.actionFirstCard, SIGNAL("triggered()"), self.onFirstCard) self.connect(self.dialog.actionLastCard, SIGNAL("triggered()"), self.onLastCard) self.connect(self.dialog.actionPreviousCard, SIGNAL("triggered()"), self.onPreviousCard) self.connect(self.dialog.actionNextCard, SIGNAL("triggered()"), self.onNextCard) self.connect(self.dialog.actionFind, SIGNAL("triggered()"), self.onFind) self.connect(self.dialog.actionFact, SIGNAL("triggered()"), self.onFact) self.connect(self.dialog.actionTags, SIGNAL("triggered()"), self.onTags) self.connect(self.dialog.actionSort, SIGNAL("triggered()"), self.onSort) # help self.connect(self.dialog.actionGuide, SIGNAL("triggered()"), self.onHelp) runHook('editor.setupMenus', self)
def bridge(self, str): if not self.fact or not runHook: # shutdown return # focus lost or key/button pressed? if str.startswith("blur") or str.startswith("key"): (type, txt) = str.split(":", 1) self.fact.fields[self.currentField] = self.mungeHTML(txt) self.mw.requireReset() self.fact.flush() if type == "blur": if not self._keepButtons: self.disableButtons() runHook("editFocusLost", self.fact) else: runHook("editTimer", self.fact) self.checkValid() # focused into field? elif str.startswith("focus"): (type, num) = str.split(":", 1) self.enableButtons() self.currentField = int(num) # state buttons changed? elif str.startswith("state"): (cmd, txt) = str.split(":", 1) r = simplejson.loads(txt) self._buttons['text_bold'].setChecked(r['bold']) self._buttons['text_italic'].setChecked(r['italic']) self._buttons['text_under'].setChecked(r['under']) self._buttons['text_super'].setChecked(r['super']) self._buttons['text_sub'].setChecked(r['sub']) elif str.startswith("dupes"): self.showDupes() else: print str
def _showQuestion(self): self._reps += 1 self.state = "question" self.typedAnswer = None c = self.card # grab the question and play audio if c.isEmpty(): q = _("""\ The front of this card is empty. Please run Tools>Empty Cards.""") else: q = c.q() if self.autoplay(c): playFromText(q) # render & update bottom q = self._mungeQA(q) klass = "card card%d" % (c.ord+1) self.web.eval("_updateQA(%s, false, '%s');" % (json.dumps(q), klass)) self._toggleStar() if self._bottomReady: self._showAnswerButton() # if we have a type answer field, focus main web if self.typeCorrect: self.mw.web.setFocus() # user hook runHook('showQuestion')
def _checkLeech(self, card, conf): "Leech handler. True if card was a leech." lf = conf['leechFails'] if not lf: return # if over threshold or every half threshold reps after that if (card.lapses >= lf and (card.lapses-lf) % (max(lf/2, 1)) == 0): # add a leech tag f = card.note() f.addTag("leech") f.flush() # handle a = conf['leechAction'] if a == 0: # if it has an old due, remove it from cram/relearning if card.odue: card.due = card.odue if card.odid: card.did = card.odid card.odue = card.odid = 0 card.queue = -1 # notify UI runHook("leech", card) return True
def setupButtons(self): self._buttons = {} # button styles for mac self.plastiqueStyle = QStyleFactory.create("plastique") self.widget.setStyle(self.plastiqueStyle) # icons self.iconsBox = QHBoxLayout() if not isMac: self.iconsBox.setMargin(6) else: self.iconsBox.setMargin(0) self.iconsBox.setSpacing(0) self.outerLayout.addLayout(self.iconsBox) b = self._addButton b("fields", self.onFields, "", shortcut(_("Customize Fields")), size=False, text=_("Fields..."), native=True, canDisable=False) b("layout", self.onCardLayout, _("Ctrl+L"), shortcut(_("Customize Cards (Ctrl+L)")), size=False, text=_("Cards..."), native=True, canDisable=False) # align to right self.iconsBox.addItem(QSpacerItem(20,1, QSizePolicy.Expanding)) b("text_bold", self.toggleBold, _("Ctrl+B"), _("Bold text (Ctrl+B)"), check=True) b("text_italic", self.toggleItalic, _("Ctrl+I"), _("Italic text (Ctrl+I)"), check=True) b("text_under", self.toggleUnderline, _("Ctrl+U"), _("Underline text (Ctrl+U)"), check=True) b("text_super", self.toggleSuper, _("Ctrl+="), _("Superscript (Ctrl+=)"), check=True) b("text_sub", self.toggleSub, _("Ctrl+Shift+="), _("Subscript (Ctrl+Shift+=)"), check=True) b("text_clear", self.removeFormat, _("Ctrl+R"), _("Remove formatting (Ctrl+R)")) but = b("foreground", self.onForeground, _("F7"), text=" ") but.setToolTip(_("Set foreground colour (F7)")) self.setupForegroundButton(but) but = b("change_colour", self.onChangeCol, _("F8"), _("Change colour (F8)"), text=u"▾") but.setFixedWidth(12) but = b("cloze", self.onCloze, _("Ctrl+Shift+C"), _("Cloze deletion (Ctrl+Shift+C)"), text="[...]") but.setFixedWidth(24) s = self.clozeShortcut2 = QShortcut( QKeySequence(_("Ctrl+Alt+Shift+C")), self.parentWindow) s.connect(s, SIGNAL("activated()"), self.onCloze) # fixme: better image names b("mail-attachment", self.onAddMedia, _("F3"), _("Attach pictures/audio/video (F3)")) b("media-record", self.onRecSound, _("F5"), _("Record audio (F5)")) b("adv", self.onAdvanced, text=u"▾") s = QShortcut(QKeySequence("Ctrl+T, T"), self.widget) s.connect(s, SIGNAL("activated()"), self.insertLatex) s = QShortcut(QKeySequence("Ctrl+T, E"), self.widget) s.connect(s, SIGNAL("activated()"), self.insertLatexEqn) s = QShortcut(QKeySequence("Ctrl+T, M"), self.widget) s.connect(s, SIGNAL("activated()"), self.insertLatexMathEnv) s = QShortcut(QKeySequence("Ctrl+Shift+X"), self.widget) s.connect(s, SIGNAL("activated()"), self.onHtmlEdit) runHook("setupEditorButtons", self)
def id(self, name, create=True, type=None): "Add a deck with NAME. Reuse deck if already exists. Return id as int." if type is None: type = defaultDeck name = name.replace('"', '') name = unicodedata.normalize("NFC", name) for id, g in list(self.decks.items()): if unicodedata.normalize("NFC", g['name'].lower()) == name.lower(): return int(id) if not create: return None g = copy.deepcopy(type) if "::" in name: # not top level; ensure all parents exist name = self._ensureParents(name) g['name'] = name while 1: id = intTime(1000) if str(id) not in self.decks: break g['id'] = id self.decks[str(id)] = g self.save(g) self.maybeAddToActive() runHook("newDeck") return int(id)
def onModelChange(self, idx): model = self._models[idx] self.deck.conf['currentModelId'] = model.id self.updateTemplates() self._ignoreReset = True runHook("currentModelChanged") self._ignoreReset = False
def saveTags(self): if not self.note: return self.note.tags = self.mw.col.tags.split(self.tags.text()) if not self.addMode: self.note.flush() runHook("tagsUpdated", self.note)
def _showQuestion(self): self._reps += 1 self.state = "question" self.typedAnswer = None baseUrl = getBaseUrl(self.mw.col) + '__reviewer__.html' c = self.card # grab the question and play audio if c.isEmpty(): q = _("""\ The front of this card is empty. Please run Tools>Empty Cards.""") else: q = c.q() if self.autoplay(c): playFromText(q) # render & update bottom q = self._mungeQA(q) klass = "card card%d" % (c.ord+1) self.web.stdHtml(self._revHtml % q,self._styles(),klass, baseUrl=baseUrl, js=anki.js.jquery+anki.js.browserSel+self._revScript) self.web.eval("_updateQA(false, '%s');" % klass) self._toggleStar() if self._bottomReady: self._showAnswerButton() # if we have a type answer field, focus main web if self.typeCorrect: self.mw.web.setFocus() # user hook runHook('showQuestion')
def showContextMenu(self): opts = [ [_("Flag Card"), [ [_("Red Flag"), "Ctrl+1", lambda: self.setFlag(1)], [_("Purple Flag"), "Ctrl+2", lambda: self.setFlag(2)], [_("Green Flag"), "Ctrl+3", lambda: self.setFlag(3)], [_("Blue Flag"), "Ctrl+4", lambda: self.setFlag(4)], None, [_("Clear Flag"), "Ctrl+0", lambda: self.setFlag(0)], ]], [_("Mark Note"), "*", self.onMark], [_("Bury Card"), "-", self.onBuryCard], [_("Bury Note"), "=", self.onBuryNote], [_("Suspend Card"), "@", self.onSuspendCard], [_("Suspend Note"), "!", self.onSuspend], [_("Delete Note"), "Ctrl+Delete", self.onDelete], [_("Options"), "O", self.onOptions], None, [_("Replay Audio"), "R", self.replayAudio], [_("Record Own Voice"), "Shift+V", self.onRecordVoice], [_("Replay Own Voice"), "V", self.onReplayRecorded], ] m = QMenu(self.mw) self._addMenuItems(m, opts) runHook("Reviewer.contextMenuEvent",self,m) m.exec_(QCursor.pos())
def onModelChange(self): from aqt.studydeck import StudyDeck current = self.deck.models.current()["name"] # edit button edit = QPushButton(_("Manage")) self.connect(edit, SIGNAL("clicked()"), self.onEdit) def nameFunc(): return sorted(self.deck.models.allNames()) ret = StudyDeck( self.mw, names=nameFunc, accept=_("Choose"), title=_("Choose Note Type"), help="_notes", current=current, parent=self.widget, buttons=[edit], cancel=False, ) if not ret.name: return m = self.deck.models.byName(ret.name) self.deck.conf["curModel"] = m["id"] cdeck = self.deck.decks.current() cdeck["mid"] = m["id"] self.deck.decks.save(cdeck) runHook("currentModelChanged") self.mw.reset()
def showContextMenu(self): opts = [ [_("Mark Note"), "*", self.onMark], [_("Bury Card"), "-", self.onBuryCard], [_("Bury Note"), "=", self.onBuryNote], [_("Suspend Card"), "@", self.onSuspendCard], [_("Suspend Note"), "!", self.onSuspend], [_("Delete Note"), "Delete", self.onDelete], [_("Options"), "O", self.onOptions], None, [_("Replay Audio"), "R", self.replayAudio], [_("Record Own Voice"), "Shift+V", self.onRecordVoice], [_("Replay Own Voice"), "V", self.onReplayRecorded], ] m = QMenu(self.mw) for row in opts: if not row: m.addSeparator() continue label, scut, func = row a = m.addAction(label) a.setShortcut(QKeySequence(scut)) a.connect(a, SIGNAL("triggered()"), func) runHook("Reviewer.contextMenuEvent",self,m) m.exec_(QCursor.pos())
def unloadProfile(self, onsuccess): def callback(): self._unloadProfile() onsuccess() runHook("unloadProfile") self.unloadCollection(callback)
def flush(self): self.mod = intTime() self.usn = self.col.usn() # bug check if self.queue == 2 and self.odue \ and not self.col.decks.isDyn(self.did): runHook("odueInvalid") assert self.due < 4294967296 self.col.db.execute( """ insert or replace into cards values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", self.id, self.nid, self.did, self.ord, self.mod, self.usn, self.type, self.queue, self.due, self.ivl, self.factor, self.reps, self.lapses, self.left, self.odue, self.odid, self.flags, self.data) self.col.log(self)
def __init__(self, mw): # Override Browser __init_. We manually invoke the original after # we use our stub one. This is to work around the fact that super # needs to be called on Browser before its methods can be invoked, # which add-ons need to do in the hook. origInit = Browser.__init__ Browser.__init__ = self.newBrowserInit super(AdvancedBrowser, self).__init__(mw) # A list of columns to exclude when building the final column list. self.columnsToRemove = [] # CustomColumn objects maintained by this add-on. # {type -> CustomColumn} self.customTypes = {} # Let add-ons add or remove columns now. runHook("advBrowserLoaded", self) # Build the actual browser, which now has our state in it, # and restore constructor. origInit(self, mw) Browser.__init__ = origInit # Remove excluded columns after the browser is built. Doing it here # is mostly a compromise in complexity. The alternative is to # rewrite the order of the original __init__ method, which is # cumbersome and error-prone. self.__removeColumns()
def showContextMenu(self): opts = self._contextMenu() m = QMenu(self.mw) self._addMenuItems(m, opts) runHook("Reviewer.contextMenuEvent", self, m) m.exec_(QCursor.pos())
def event(self, evt): from anki.hooks import runHook if evt.type() == QEvent.FileOpen: runHook("macLoadEvent", unicode(evt.file())) return True return QApplication.event(self, evt)
def loadProfile(self): # show main window if self.pm.profile['mainWindowState']: restoreGeom(self, "mainWindow") restoreState(self, "mainWindow") else: self.resize(500, 400) # toolbar needs to be retranslated self.toolbar.draw() # show and raise window for osx self.show() self.activateWindow() self.raise_() # maybe sync (will load DB) self.onSync(auto=True) # import pending? if self.pendingImport: if self.pm.profile['key']: showInfo(_("""\ To import into a password protected profile, please open the profile before attempting to import.""")) else: import aqt.importing aqt.importing.importFile(self, self.pendingImport) self.pendingImport = None runHook("profileLoaded")
def onHistory(self): m = QMenu(self) for nid, txt in self.history: a = m.addAction(_("Edit %s") % txt) a.triggered.connect(lambda b, nid=nid: self.editHistory(nid)) runHook("AddCards.onHistory", self, m) m.exec_(self.historyButton.mapToGlobal(QPoint(0,0)))
def loadProfile(self): # show main window if self.pm.profile['mainWindowState']: restoreGeom(self, "mainWindow") restoreState(self, "mainWindow") # toolbar needs to be retranslated self.toolbar.draw() # titlebar self.setWindowTitle("Anki - " + self.pm.name) # show and raise window for osx self.show() self.activateWindow() self.raise_() # maybe sync (will load DB) if self.pendingImport and os.path.basename( self.pendingImport).startswith("backup-"): # skip sync when importing a backup self.loadCollection() else: self.onSync(auto=True) # import pending? if self.pendingImport: if self.pm.profile['key']: showInfo(_("""\ To import into a password protected profile, please open the profile before attempting to import.""")) else: self.handleImport(self.pendingImport) self.pendingImport = None runHook("profileLoaded")
def _colLoadingState(self, oldState): "Run once, when col is loaded." self.enableColMenuItems() # ensure cwd is set if media dir exists self.col.media.dir() runHook("colLoading", self.col) self.moveToState("overview")
def setupShortcuts(self): # if a third element is provided, enable shortcut even when no field selected cuts = [ ("Ctrl+L", self.onCardLayout, True), ("Ctrl+B", self.toggleBold), ("Ctrl+I", self.toggleItalic), ("Ctrl+U", self.toggleUnderline), ("Ctrl++", self.toggleSuper), ("Ctrl+=", self.toggleSub), ("Ctrl+R", self.removeFormat), ("F7", self.onForeground), ("F8", self.onChangeCol), ("Ctrl+Shift+C", self.onCloze), ("Ctrl+Shift+Alt+C", self.onCloze), ("F3", self.onAddMedia), ("F5", self.onRecSound), ("Ctrl+T, T", self.insertLatex), ("Ctrl+T, E", self.insertLatexEqn), ("Ctrl+T, M", self.insertLatexMathEnv), ("Ctrl+M, M", self.insertMathjaxInline), ("Ctrl+M, E", self.insertMathjaxBlock), ("Ctrl+M, C", self.insertMathjaxChemistry), ("Ctrl+Shift+X", self.onHtmlEdit), ("Ctrl+Shift+T", self.onFocusTags, True) ] runHook("setupEditorShortcuts", cuts, self) for row in cuts: if len(row) == 2: keys, fn = row fn = self._addFocusCheck(fn) else: keys, fn, _ = row QShortcut(QKeySequence(keys), self.widget, activated=fn)
def oncallback(arg): if not self.note: return self.setupForegroundButton() self.checkValid() if focusTo is not None: self.web.setFocus() runHook("loadNote", self)
def queueMplayer(path): ensureMplayerThreads() path = path.encode(sys.getfilesystemencoding()) mplayerCond.acquire() mplayerQueue.append(path) mplayerCond.notifyAll() mplayerCond.release() runHook("soundQueued")
def reset(self, guiOnly=False): "Called for non-trivial edits. Rebuilds queue and updates UI." if self.col: if not guiOnly: self.col.reset() runHook("reset") self.maybeEnableUndo() self.moveToState(self.state)
def contextMenuEvent(self, evt): if not self._canFocus: return m = QMenu(self) a = m.addAction(_("Copy")) a.triggered.connect(lambda: self.triggerPageAction(QWebEnginePage.Copy)) runHook("AnkiWebView.contextMenuEvent", self, m) m.popup(QCursor.pos())
def exporters(): exps = [ get_exporter_id(AnkiPackageExporter), get_exporter_id(TextNoteExporter), get_exporter_id(TextCardExporter), ] runHook("exportersList", exps) return exps
def recover(self, increment=True, value=None, damage=False): ''' Abstraction for recovering life, increments the bar if increment is True (default). ''' multiplier = 1 if not increment: multiplier = -1 if value is None: if damage and self._barInfo[self._currentDeck]['enableDamageValue']: multiplier = -1 value = self._barInfo[self._currentDeck]['damageValue'] else: value = self._barInfo[self._currentDeck]['recoverValue'] self._ankiProgressBar.incCurrentValue(multiplier * value) life = self._ankiProgressBar.getCurrentValue() self._barInfo[self._currentDeck]['currentValue'] = life if life == 0 and not self._gameOver: self._gameOver = True runHook('LifeDrain.gameOver') elif life > 0: self._gameOver = False
def onModelChange(self): """Open Choose Note Type window""" #Method called when we want to change the current model from aqt.studydeck import StudyDeck current = self.deck.models.current()['name'] # edit button edit = QPushButton(_("Manage"), clicked=self.onEdit) def nameFunc(): return sorted(self.deck.models.allNames()) ret = StudyDeck( self.mw, names=nameFunc, accept=_("Choose"), title=_("Choose Note Type"), help="_notes", current=current, parent=self.widget, buttons=[edit], cancel=True, geomKey="selectModel") if not ret.name: return m = self.deck.models.byName(ret.name) self.deck.conf['curModel'] = m['id'] cdeck = self.deck.decks.current() cdeck['mid'] = m['id'] self.deck.decks.save(cdeck) runHook("currentModelChanged") self.mw.reset()
def exportFiltered(self, z, path): # export into the anki2 file colfile = path.replace(".apkg", ".anki2") AnkiExporter.exportInto(self, colfile) z.write(colfile, "collection.anki2") # and media self.prepareMedia() media = {} for c, file in enumerate(self.mediaFiles): cStr = str(c) mpath = os.path.join(self.mediaDir, file) if os.path.exists(mpath): z.write(mpath, cStr, zipfile.ZIP_STORED) media[cStr] = file runHook("exportedMediaFiles", c) # tidy up intermediate files os.unlink(colfile) p = path.replace(".apkg", ".media.db2") if os.path.exists(p): os.unlink(p) os.chdir(self.mediaDir) shutil.rmtree(path.replace(".apkg", ".media")) return media
def id(self, name, create=True, type=defaultDeck): "Add a deck with NAME. Reuse deck if already exists. Return id as int." name = name.replace('"', '') for id, g in self.decks.items(): if g['name'].lower() == name.lower(): return int(id) if not create: return None g = copy.deepcopy(type) if "::" in name: # not top level; ensure all parents exist name = self._ensureParents(name) g['name'] = name while 1: id = intTime(1000) if str(id) not in self.decks: break g['id'] = id self.decks[str(id)] = g self.save(g) self.maybeAddToActive() runHook("newDeck") return int(id)
def cs_editor_setupShortcuts(self): # if a third element is provided, enable shortcut even when no field selected cuts = [ (config_scuts["editor card layout"], self.onCardLayout, True), (config_scuts["editor bold"], self.toggleBold), (config_scuts["editor italic"], self.toggleItalic), (config_scuts["editor underline"], self.toggleUnderline), (config_scuts["editor superscript"], self.toggleSuper), (config_scuts["editor subscript"], self.toggleSub), (config_scuts["editor remove format"], self.removeFormat), (config_scuts["editor foreground"], self.onForeground), (config_scuts["editor change col"], self.onChangeCol), (config_scuts["editor cloze"], self.onCloze), (config_scuts["editor cloze alt"], self.onAltCloze), (config_scuts["editor add media"], self.onAddMedia), (config_scuts["editor record sound"], self.onRecSound), (config_scuts["editor insert latex"], self.insertLatex), (config_scuts["editor insert latex equation"], self.insertLatexEqn), (config_scuts["editor insert latex math environment"], self.insertLatexMathEnv), (config_scuts["editor insert mathjax inline"], self.insertMathjaxInline), (config_scuts["editor insert mathjax block"], self.insertMathjaxBlock), (config_scuts["editor insert mathjax chemistry"], self.insertMathjaxChemistry), (config_scuts["editor html edit"], self.onHtmlEdit), (config_scuts["editor focus tags"], self.onFocusTags, True), (config_scuts["editor _extras"]["paste custom text"], self.customPaste) ] runHook("setupEditorShortcuts", cuts, self) for row in cuts: if len(row) == 2: keys, fn = row fn = self._addFocusCheck(fn) else: keys, fn, _ = row scut = QShortcut(QKeySequence(keys), self.widget, activated=fn)
def __init__(self, mw): # Override Browser __init_. We manually invoke the original after # we use our stub one. This is to work around the fact that super # needs to be called on Browser before its methods can be invoked, # which add-ons need to do in the hook. origInit = Browser.__init__ Browser.__init__ = self.newBrowserInit super(AdvancedBrowser, self).__init__(mw) # A list of columns to exclude when building the final column list. self.columnsToRemove = [] # CustomColumn objects maintained by this add-on. # {type -> CustomColumn} self.customTypes = {} # Let add-ons add or remove columns now. runHook("advBrowserLoaded", self) # Build the actual browser, which now has our state in it, # and restore constructor. origInit(self, mw) Browser.__init__ = origInit tn = QAction(('- Only show notes -'), self) tn.setShortcut(QKeySequence(config.getNoteModeShortcut())) self.addAction(tn) tn.triggered.connect(self.toggleUniqueNote) # Remove excluded columns after the browser is built. Doing it here # is mostly a compromise in complexity. The alternative is to # rewrite the order of the original __init__ method, which is # cumbersome and error-prone. self.__removeColumns() # Workaround for double-saving (see closeEvent) self.saveEvent = False
def onProfileLoadedHook(self) -> None: self._profile = self._profileManager.profile if AMBOSS_TOKEN not in self._profileManager.profile: runHook(AMBOSS_FIRSTRUN_HOOK) return if self.token: runHook(AMBOSS_LOGIN_HOOK) return runHook(AMBOSS_LOGOUT_HOOK)
def onTableViewContextMenu(self, pos): m = QMenu() a = m.addAction('Copy Selected cid') a.connect(a, SIGNAL("triggered()"), lambda s=self: QApplication.clipboard().setText(str(s.selectedCards()[0]))) a = m.addAction('Copy Selected nid') a.connect(a, SIGNAL("triggered()"), lambda s=self: QApplication.clipboard().setText(str(s.selectedNotes()[0]))) preset_search_menu = m.addMenu("Preset Searches") a = preset_search_menu.addAction('Review cards due soon with long interval') a.connect(a, SIGNAL("triggered()"), lambda b=self: set_search_text_to(b, "prop:due>5 prop:due<7 prop:ivl>=10 is:review")) a = preset_search_menu.addAction('Unseen cards') a.connect(a, SIGNAL("triggered()"), lambda b=self: set_search_text_to(b, "tag:ns*")) a = m.addAction('Set Columns to Min Size') a.connect(a, SIGNAL("triggered()"), lambda s=self: set_column_size_to_default(s)) runHook("Browser.tableViewContextMenuEvent", self, m) #m.popup(QCursor.pos()) m.exec_(QCursor.pos())
def setupShortcuts(self): cuts = [ ("Ctrl+L", self.onCardLayout), ("Ctrl+B", self.toggleBold), ("Ctrl+I", self.toggleItalic), ("Ctrl+U", self.toggleUnderline), ("Ctrl++", self.toggleSuper), ("Ctrl+=", self.toggleSub), ("Ctrl+R", self.removeFormat), ("F7", self.onForeground), ("F8", self.onChangeCol), ("Ctrl+Shift+C", self.onCloze), ("Ctrl+Shift+Alt+C", self.onCloze), ("F3", self.onAddMedia), ("F5", self.onRecSound), ("Ctrl+T, T", self.insertLatex), ("Ctrl+T, E", self.insertLatexEqn), ("Ctrl+T, M", self.insertLatexMathEnv), ("Ctrl+Shift+X", self.onHtmlEdit), ("Ctrl+Shift+T", self.onFocusTags) ] runHook("setupEditorShortcuts", cuts, self) for keys, fn in cuts: QShortcut(QKeySequence(keys), self.widget, activated=fn)
def reset(self, guiOnly=False): """Called for non-trivial edits. Rebuilds queue and updates UI. set Edit>undo change state (show the bottom bar, remove shortcut from last state) run hooks beforeStateChange and afterStateChange. By default they are empty. call cleanup of last state. call the hook "reset". It contains at least the onReset method from the current window if it is browser, (and its changeModel), editCurrent, addCard, studyDeck, modelChooser. Reset reinitialize those window without closing them. unless guiOnly: Deal with the fact that it's potentially a new day. Reset number of learning, review, new cards according to current decks empty queues. Set haveQueues to true. """ if self.col: if not guiOnly: self.col.reset() runHook("reset") self.maybeEnableUndo() self.moveToState(self.state)
def _checkLeech(self, card, conf): "Leech handler. True if card was a leech." lf = conf['leechFails'] if not lf: return # if over threshold or every half threshold reps after that if (card.lapses >= lf and (card.lapses - lf) % (max(lf // 2, 1)) == 0): # add a leech tag f = card.note() f.addTag("leech") f.flush() # handle a = conf['leechAction'] if a == 0: # if it has an old due, remove it from cram/relearning if card.odue: card.due = card.odue if card.odid: card.did = card.odid card.odue = card.odid = 0 card.queue = -1 # notify UI runHook("leech", card) return True
def showContextMenu(self): opts = [ [_("Mark Note"), "*", self.onMark], [_("Bury Note"), "-", self.onBuryNote], [_("Suspend Card"), "@", self.onSuspendCard], [_("Suspend Note"), "!", self.onSuspend], [_("Delete Note"), "Delete", self.onDelete], [_("Options"), "O", self.onOptions], None, [_("Replay Audio"), "R", self.replayAudio], [_("Record Own Voice"), "Shift+V", self.onRecordVoice], [_("Replay Own Voice"), "V", self.onReplayRecorded], ] m = QMenu(self.mw) for row in opts: if not row: m.addSeparator() continue label, scut, func = row a = m.addAction(label) a.setShortcut(QKeySequence(scut)) a.connect(a, SIGNAL("triggered()"), func) runHook("Reviewer.contextMenuEvent", self, m) m.exec_(QCursor.pos())
def setupShortcuts(self): # if a third element is provided, enable shortcut even when no field selected cuts = [("Ctrl+L", self.onCardLayout, True), ("Ctrl+B", self.toggleBold), ("Ctrl+I", self.toggleItalic), ("Ctrl+U", self.toggleUnderline), ("Ctrl++", self.toggleSuper), ("Ctrl+=", self.toggleSub), ("Ctrl+R", self.removeFormat), ("F7", self.onForeground), ("F8", self.onChangeCol), ("Ctrl+Shift+C", self.onCloze), ("Ctrl+Shift+Alt+C", self.onCloze), ("F3", self.onAddMedia), ("F5", self.onRecSound), ("Ctrl+T, T", self.insertLatex), ("Ctrl+T, E", self.insertLatexEqn), ("Ctrl+T, M", self.insertLatexMathEnv), ("Ctrl+M, M", self.insertMathjaxInline), ("Ctrl+M, E", self.insertMathjaxBlock), ("Ctrl+Shift+X", self.onHtmlEdit), ("Ctrl+Shift+T", self.onFocusTags, True)] runHook("setupEditorShortcuts", cuts, self) for row in cuts: if len(row) == 2: keys, fn = row fn = self._addFocusCheck(fn) else: keys, fn, _ = row QShortcut(QKeySequence(keys), self.widget, activated=fn)
def bridge(self, str): if not self.note or not runHook: # shutdown return # focus lost or key/button pressed? if str.startswith("blur") or str.startswith("key"): (type, txt) = str.split(":", 1) self.note.fields[self.currentField] = self.mungeHTML(txt) self.mw.requireReset() if not self.addMode: self.note.flush() if type == "blur": if not self._keepButtons: self.disableButtons() runHook("editFocusLost", self.note) else: runHook("editTimer", self.note) self.checkValid() # focused into field? elif str.startswith("focus"): (type, num) = str.split(":", 1) self.enableButtons() self.currentField = int(num) # state buttons changed? elif str.startswith("state"): (cmd, txt) = str.split(":", 1) r = simplejson.loads(txt) self._buttons['text_bold'].setChecked(r['bold']) self._buttons['text_italic'].setChecked(r['italic']) self._buttons['text_under'].setChecked(r['under']) self._buttons['text_super'].setChecked(r['super']) self._buttons['text_sub'].setChecked(r['sub']) elif str.startswith("dupes"): self.showDupes() else: print str
def loadProfile(self, onsuccess=None): self.maybeAutoSync() if not self.loadCollection(): return # show main window if self.pm.profile['mainWindowState']: restoreGeom(self, "mainWindow") restoreState(self, "mainWindow") # titlebar self.setWindowTitle(self.pm.name + " - Anki") # show and raise window for osx self.show() self.activateWindow() self.raise_() # import pending? if self.pendingImport: self.handleImport(self.pendingImport) self.pendingImport = None runHook("profileLoaded") if onsuccess: onsuccess()
def reschedule(self, card, ease): assert ease > 4 due = card.due #save days = self._parseDays(ease) if days == None: return self._parseWarning() elif days == '-0': return showInfo("PC LOAD A4!") elif days == '0': runHook('ReMemorize.forget', card) elif days > 0: runHook('ReMemorize.reschedule', card, days) elif days < 0: runHook('ReMemorize.changeDue', card, -days) else: return self._showTooltip(card, ease, due)
def setupButtons(self): self._buttons = {} # button styles for mac self.plastiqueStyle = QStyleFactory.create("plastique") if not self.plastiqueStyle: # plastique was removed in qt5 self.plastiqueStyle = QStyleFactory.create("fusion") self.widget.setStyle(self.plastiqueStyle) # icons self.iconsBox = QHBoxLayout() if not isMac: self.iconsBox.setMargin(6) else: self.iconsBox.setMargin(0) self.iconsBox.setSpacing(0) self.outerLayout.addLayout(self.iconsBox) b = self._addButton b("fields", self.onFields, "", shortcut(_("Customize Fields")), size=False, text=_("Fields..."), native=True, canDisable=False) self.iconsBox.addItem(QSpacerItem(6,1, QSizePolicy.Fixed)) b("layout", self.onCardLayout, _("Ctrl+L"), shortcut(_("Customize Cards (Ctrl+L)")), size=False, text=_("Cards..."), native=True, canDisable=False) # align to right self.iconsBox.addItem(QSpacerItem(20,1, QSizePolicy.Expanding)) b("text_bold", self.toggleBold, _("Ctrl+B"), _("Bold text (Ctrl+B)"), check=True) b("text_italic", self.toggleItalic, _("Ctrl+I"), _("Italic text (Ctrl+I)"), check=True) b("text_under", self.toggleUnderline, _("Ctrl+U"), _("Underline text (Ctrl+U)"), check=True) b("text_super", self.toggleSuper, _("Ctrl+Shift+="), _("Superscript (Ctrl+Shift+=)"), check=True) b("text_sub", self.toggleSub, _("Ctrl+="), _("Subscript (Ctrl+=)"), check=True) b("text_clear", self.removeFormat, _("Ctrl+R"), _("Remove formatting (Ctrl+R)")) but = b("foreground", self.onForeground, _("F7"), text=" ") but.setToolTip(_("Set foreground colour (F7)")) self.setupForegroundButton(but) but = b("change_colour", self.onChangeCol, _("F8"), _("Change colour (F8)"), text=u"▾") but.setFixedWidth(12) but = b("cloze", self.onCloze, _("Ctrl+Shift+C"), _("Cloze deletion (Ctrl+Shift+C)"), text="[...]") but.setFixedWidth(24) s = self.clozeShortcut2 = QShortcut( QKeySequence(_("Ctrl+Alt+Shift+C")), self.parentWindow) s.connect(s, SIGNAL("activated()"), self.onCloze) # fixme: better image names b("mail-attachment", self.onAddMedia, _("F3"), _("Attach pictures/audio/video (F3)")) b("media-record", self.onRecSound, _("F5"), _("Record audio (F5)")) b("adv", self.onAdvanced, text=u"▾") s = QShortcut(QKeySequence("Ctrl+T, T"), self.widget) s.connect(s, SIGNAL("activated()"), self.insertLatex) s = QShortcut(QKeySequence("Ctrl+T, E"), self.widget) s.connect(s, SIGNAL("activated()"), self.insertLatexEqn) s = QShortcut(QKeySequence("Ctrl+T, M"), self.widget) s.connect(s, SIGNAL("activated()"), self.insertLatexMathEnv) s = QShortcut(QKeySequence("Ctrl+Shift+X"), self.widget) s.connect(s, SIGNAL("activated()"), self.onHtmlEdit) # tags s = QShortcut(QKeySequence("Ctrl+Shift+T"), self.widget) s.connect(s, SIGNAL("activated()"), lambda: self.tags.setFocus()) runHook("setupEditorButtons", self)
def score_backlog(silent=False): if ah.settings.keep_log: ah.log.debug("Begin function") #Warn User that this can take some time warning = "Warning: Scoring backlog may take some time.\n\nMake sure Anki is synced across your devices before you do this. If you do this and you have unsynced reviews on another device, those reviews will not be counted towards Habitica points!\n\nWould you like to continue?" if not silent: if ah.settings.keep_log: ah.log.debug(warning.replace('\n', ' ')) cont = utils.askUser(warning) else: cont = True if ah.settings.keep_log: ah.log.debug("User chose to score backlog (or silent mode is on): %s" % cont) if not cont: if ah.settings.keep_log: ah.log.warning("End function returning: %s" % False) return False #Exit if not ready if not ready_or_not(): if ah.settings.keep_log: ah.log.warning("End function returning: %s" % False) return False #Check internet if down if not ah.settings.internet: ah.settings.internet = ah.habitica.test_internet() #If Internet is still down but class initialized if not ah.settings.internet and ah.settings.initialized: if not silent: ah.habitica.hrpg_showInfo( "Hmmm...\n\nI can't connect to Habitica. Perhaps your internet is down.\n\nI'll remember your points and try again later." ) if ah.settings.keep_log: ah.log.warning("No internet connection") if ah.settings.keep_log: ah.log.warning("End function returning: %s" % False) return False ah.habitica.grab_scorecounter('Anki Points') #Compare database to scored points if compare_score_to_db(): if ah.config[ah.settings.profile]['score'] < ah.settings.sched: if not silent: utils.showInfo("No backlog to score") if ah.settings.keep_log: ah.log.info("No backlog to score") if ah.settings.keep_log: ah.log.debug("End function returning: %s" % True) return True #OK, now we can score some points... p = 0 #point counter i = 0 #limit tries to 25 to prevent endless loop numScores = ah.config[ ah.settings.profile]['score'] // ah.settings.sched if ah.settings.keep_log: ah.log.debug("%s points to score" % numScores) progressLabel = "Scoring %s point%s to Habitica" % ( numScores, "" if numScores == 0 else "s") mw.progress.start(max=numScores, label=progressLabel) while i <= 2 * numScores and ah.config[ah.settings.profile][ 'score'] >= ah.settings.sched and ah.settings.internet: try: ah.habitica.silent_earn_points("Anki Points") ah.config[ah.settings.profile]['score'] -= ah.settings.sched i += 1 p += 1 mw.progress.update() except: i += 1 mw.progress.finish() if not silent: utils.showInfo("%s point%s scored on Habitica" % (p, "" if p == 1 else "s")) if ah.settings.keep_log: ah.log.info("%s point%s scored on Habitica" % (p, "" if p == 1 else "s")) # if ah.settings.debug: utils.showInfo("New scorecount: %s" % ah.habitica.hnote['Anki Points']['scorecount']) if ah.settings.keep_log: ah.log.info("New scorecount: %s" % ah.habitica.hnote['Anki Points']['scorecount']) if ah.settings.keep_log: ah.log.info("New config score: %s" % ah.config[ah.settings.profile]['score']) ah.habitica.hnote['Anki Points'][ 'scorecount'] = AnkiHabitica.habitica_class.Habitica.offline_scorecount = 0 ah.habitica.hnote['Anki Points'][ 'scoresincedate'] = AnkiHabitica.habitica_class.Habitica.offline_sincedate = AnkiHabitica.db_helper.latest_review_time( ) ah.config[ah.settings.profile]['score'] = ah.habitica.hnote[ 'Anki Points']['scoresincedate'] ah.habitica.post_scorecounter('Anki Points') runHook("HabiticaAfterScore") save_stats(None, None) if ah.settings.keep_log: ah.log.debug("End function")
def cleanup(self): runHook("reviewCleanup")
def run_move_to_state_hook(state, *args): u"""Run a hook whenever we have changed the state.""" runHook("movedToState", state)
def setStateShortcuts(self, shortcuts: List[Tuple[str, Callable]]) -> None: runHook(self.state + "StateShortcuts", shortcuts) self.stateShortcuts = self.applyShortcuts(shortcuts)
def setStateShortcuts(self, shortcuts): runHook(self.state + "StateShortcuts", shortcuts) self.stateShortcuts = self.applyShortcuts(shortcuts)
def noteChanged(self, nid): "Called when a card or note is edited (but not deleted)." runHook("noteChanged", nid)
def _updateConfig(self, config): self.config = nestedUpdate(self.config, config) runHook(self.addonName + '.configUpdated')
def focusLost(self, field): runHook('fact.focusLost', self, field)
def setStateShortcuts(self, shortcuts: List[Tuple[str, Callable]]) -> None: gui_hooks.state_shortcuts_will_change(self.state, shortcuts) # legacy hook runHook(self.state + "StateShortcuts", shortcuts) self.stateShortcuts = self.applyShortcuts(shortcuts)
def timesUp(): global death_toll death_toll-=1 if not death_toll: sys.exit() runHook('LifeDrain.recover',True,HP_RECOVER)
def reset(): global gameover gameover = False remHook('showQuestion', showMsg) runHook('LifeDrain.recover', True, 9999)
def contextMenuEvent(self, evt): m = QMenu(self) a = m.addAction(_("Copy")) a.triggered.connect(self.onCopy) runHook("AnkiWebView.contextMenuEvent", self, m) m.popup(QCursor.pos())
def onTreeMenu(self, pos): try: #stop timer for, auto update overview summary deck, during right clicks self.timer.stop() except: pass index = self.indexAt(pos) if not index.isValid(): return item = index.internalPointer() if not item: return try: #quick patch for addon compatibility item.type except AttributeError: item.type = None m = QMenu(self) if not isinstance(item.type, str) or item.type == "sys": # 2.1 does not patch _systemTagTree. # So I am using this to readjust item.type item.type = "sys" #TODO: Rewrite menu for each type + modifier keys elif mw.app.keyboardModifiers() == Qt.ShiftModifier: if item.type != "group": if item.type == "tag": act = m.addAction("Create Filtered Tag*") act.triggered.connect(lambda: self._onTreeCramTags(index)) m.addSeparator() #TODO: add support for custom study from deck list act = m.addAction("Mark/Unmark Item*") act.triggered.connect(lambda: self._onTreeMark(index)) if item.type in ("deck", "tag"): act = m.addAction("Pin Item*") act.triggered.connect(lambda: self._onTreePin(index)) if item.type in ("pin", "fav"): ico = self.getConf('Blitzkrieg.icon_fav', True) act = m.addAction("Show icon for paths") act.setCheckable(True) act.setChecked(ico) act.triggered.connect(lambda: self._toggleIconOption(item)) act = m.addAction("Refresh") act.triggered.connect(self.refresh) if item.type == "group": if item.fullname in ("tag", "deck", "model"): sort = self.getConf('Blitzkrieg.sort_' + item.fullname, False) act = m.addAction("Sort by A-a-B-b") act.setCheckable(True) act.setChecked(sort) act.triggered.connect(lambda: self._toggleSortOption(item)) if item.fullname in ("tag", "model"): ico = self.getConf('Blitzkrieg.icon_' + item.fullname, True) act = m.addAction("Show icon for paths") act.setCheckable(True) act.setChecked(ico) act.triggered.connect(lambda: self._toggleIconOption(item)) if item.fullname == "deck": up = self.getConf('Blitzkrieg.updateOV', False) act = m.addAction("Auto Update Overview") act.setCheckable(True) act.setChecked(up) act.triggered.connect(self._toggleMWUpdate) elif item.fullname == "tag": sa = self.getConf('Blitzkrieg.showAllTags', True) act = m.addAction("Auto Show Subtags") act.setCheckable(True) act.setChecked(sa) act.triggered.connect(self._toggleShowSubtags) if len(item.children): m.addSeparator() act = m.addAction("Collapse All*") act.triggered.connect(lambda: self.expandAllChildren(index)) act = m.addAction("Expand All*") act.triggered.connect( lambda: self.expandAllChildren(index, True)) elif item.type == "group": if item.fullname == "tag": act = m.addAction("Refresh") act.triggered.connect(self.refresh) elif item.fullname == "deck": act = m.addAction("Add Deck") act.triggered.connect(self._onTreeDeckAdd) act = m.addAction("Empty All Filters") act.triggered.connect(self.onEmptyAll) act = m.addAction("Rebuild All Filters") act.triggered.connect(self.onRebuildAll) elif item.fullname == "model": act = m.addAction("Manage Model") act.triggered.connect(self.onManageModel) m.addSeparator() act = m.addAction("Find...") act.triggered.connect(lambda: self.findRecursive(index)) act = m.addAction("Collapse All*") act.triggered.connect(lambda: self.expandAllChildren(index)) act = m.addAction("Expand All*") act.triggered.connect(lambda: self.expandAllChildren(index, True)) elif len(self.selectedIndexes()) > 1: #Multi sel items for itm in self.MENU_ITEMS[item.type]: if itm[0] < 0: m.addSeparator() elif itm[0] == 2 or (itm[0] == 3 and self.hasValue(item)): act = m.addAction(itm[1]) act.triggered.connect( lambda b, item=item, itm=itm: self._onTreeItemAction( item, itm[2], itm[3])) else: #Single selected itms for itm in self.MENU_ITEMS[item.type]: if itm[0] < 0: m.addSeparator() elif itm[0] in (0, 2) or self.hasValue(item): act = m.addAction(itm[1]) act.triggered.connect( lambda b, item=item, itm=itm: self._onTreeItemAction( item, itm[2], itm[3])) runHook("Blitzkrieg.treeMenu", self, item, m) if not m.isEmpty(): m.popup(QCursor.pos())