def answerCard_before(self, ease): checked = False if ease == 1: tags = self.mw.reviewer.card.note().stringTags() if self.card.template()["name"] == "Translation": if tags.find("pt") != -1: checked = True else: checked = False elif self.card.template()["name"] == "Forward": if tags.find("pf") != -1: checked = True else: checked = False elif self.card.template()["name"] == "Reverse": if tags.find("pb") != -1: checked = True else: checked = False if checked: self.state = None tooltip("Defered Card!") else: l = self._answerButtonList() a = [item for item in l if item[0] == ease] if len(a) > 0: tooltip(a[0][1])
def saveMedia(self): if self.checkCb.isChecked(): # ignore the media code if self.code not in Setup.config['codesIgnored']: Setup.config['codesIgnored'].append(self.code) Setup.saveConfigToDisk() tooltip('Current dictionary was ignored, click on reset to restore should you need it again') self.importRes = False self.close() return folder = self.pathEdit.text().strip() # remove the trailing / or \ if folder.endswith('/') or folder.endswith('\\'): folder = folder[:-1] fullPath = os.path.join(self.pathEdit.text(), self.filename) if not (os.path.exists(fullPath) and os.path.exists(folder)): msg = QMessageBox(self) msg.setText("Folder doesn't exist or doesn't contain the needed media, please select again") msg.setWindowTitle("Directory selected is not the right one") msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) msg.exec_() return Setup.config['addressMap'][self.code] = folder Setup.saveConfigToDisk() self.importRes = True tooltip('Media import completed') self.close()
def onDelete(): saveGeom(diag, "emptyCards") QDialog.accept(diag) self.checkpoint(_("Delete Empty")) self.col.remCards(cids) tooltip(ngettext("%d card deleted.", "%d cards deleted.", len(cids)) % len(cids)) self.reset()
def on_search_for_selection(web_view): sel_encode = selected_text_as_query(web_view).encode('utf8', 'ignore') #need to do this the long way around to avoid double % encoding url = QUrl.fromEncoded(SEARCH_URL % urllib.quote(sel_encode)) #openLink(SEARCH_URL + sel_encode) tooltip(_("Loading..."), period=1000) QDesktopServices.openUrl(url)
def __init__(self, app, profileManager, args): QMainWindow.__init__(self) self.state = "startup" aqt.mw = self self.app = app self.pm = profileManager # running 2.0 for the first time? if self.pm.meta['firstRun']: # load the new deck user profile self.pm.load(self.pm.profiles()[0]) self.pm.meta['firstRun'] = False self.pm.save() # init rest of app self.safeMode = self.app.queryKeyboardModifiers() & Qt.ShiftModifier try: self.setupUI() self.setupAddons() except: showInfo(_("Error during startup:\n%s") % traceback.format_exc()) sys.exit(1) # must call this after ui set up if self.safeMode: tooltip(_("Shift key was held down. Skipping automatic " "syncing and add-on loading.")) # were we given a file to import? if args and args[0]: self.onAppMsg(args[0]) # Load profile in a timer so we can let the window finish init and not # close on profile load error. self.progress.timer(10, self.setupProfile, False)
def accept(self): self.exporter.includeSched = ( self.frm.includeSched.isChecked()) self.exporter.includeMedia = ( self.frm.includeMedia.isChecked()) self.exporter.includeTags = ( self.frm.includeTags.isChecked()) if not self.frm.deck.currentIndex(): self.exporter.did = None else: name = self.decks[self.frm.deck.currentIndex()] self.exporter.did = self.col.decks.id(name) if (self.isApkg and self.exporter.includeSched and not self.exporter.did): verbatim = True # it's a verbatim apkg export, so place on desktop instead of # choosing file file = os.path.join(QDesktopServices.storageLocation( QDesktopServices.DesktopLocation), "collection.apkg") if os.path.exists(file): if not askUser( _("%s already exists on your desktop. Overwrite it?")% "collection.apkg"): return else: verbatim = False # Get deck name and remove invalid filename characters deck_name = self.decks[self.frm.deck.currentIndex()] deck_name = re.sub('[\\\\/?<>:*|"^]', '_', deck_name) filename = os.path.join(aqt.mw.pm.base, u'{0}{1}'.format(deck_name, self.exporter.ext)) while 1: file = getSaveFile(self, _("Export"), "export", self.exporter.key, self.exporter.ext, fname=filename) if not file: return if checkInvalidFilename(os.path.basename(file), dirsep=False): continue break self.hide() if file: self.mw.progress.start(immediate=True) try: f = open(file, "wb") f.close() except (OSError, IOError), e: showWarning(_("Couldn't save file: %s") % unicode(e)) else: os.unlink(file) self.exporter.exportInto(file) if verbatim: msg = _("A file called collection.apkg was saved on your desktop.") period = 5000 else: period = 3000 msg = ngettext("%d card exported.", "%d cards exported.", \ self.exporter.count) % self.exporter.count tooltip(msg, period=period) finally:
def _linkHandler(self, url): if url == "study": self.mw.col.startTimebox() self.mw.moveToState("review") if self.mw.state == "overview": tooltip(_("No cards are due yet.")) elif url == "anki": print "anki menu" elif url == "opts": self.mw.onDeckConf() elif url == "cram": deck = self.mw.col.decks.current() self.mw.onCram("'deck:%s'" % deck['name']) elif url == "refresh": self.mw.col.sched.rebuildDyn() self.mw.reset() elif url == "empty": self.mw.col.sched.emptyDyn(self.mw.col.decks.selected()) self.mw.reset() elif url == "decks": self.mw.moveToState("deckBrowser") elif url == "review": openLink(aqt.appShared+"info/%s?v=%s"%(self.sid, self.sidVer)) elif url == "studymore": self.onStudyMore() elif url.lower().startswith("http"): openLink(url)
def accept(self): file = getSaveFile( self, _("Export"), "export", self.exporter.key, self.exporter.ext) self.hide() if file: self.exporter.includeSched = ( self.frm.includeSched.isChecked()) self.exporter.includeMedia = ( self.frm.includeMedia.isChecked()) self.exporter.includeTags = ( self.frm.includeTags.isChecked()) if not self.frm.deck.currentIndex(): self.exporter.did = None else: name = self.decks[self.frm.deck.currentIndex()] self.exporter.did = self.col.decks.id(name) self.mw.progress.start(immediate=True) try: f = open(file, "wb") f.close() except (OSError, IOError), e: showWarning(_("Couldn't save file: %s") % unicode(e)) else: os.unlink(file) self.exporter.exportInto(file) tooltip(_("%d exported.") % self.exporter.count) finally:
def __init__(self, app, profileManager, opts, args): QMainWindow.__init__(self) self.state = "startup" self.opts = opts aqt.mw = self self.app = app self.pm = profileManager # init rest of app self.safeMode = self.app.queryKeyboardModifiers() & Qt.ShiftModifier try: self.setupUI() self.setupAddons() except: showInfo(_("Error during startup:\n%s") % traceback.format_exc()) sys.exit(1) # must call this after ui set up if self.safeMode: tooltip(_("Shift key was held down. Skipping automatic " "syncing and add-on loading.")) # were we given a file to import? if args and args[0]: self.onAppMsg(args[0]) # Load profile in a timer so we can let the window finish init and not # close on profile load error. if isWin: fn = self.setupProfileAfterWebviewsLoaded else: fn = self.setupProfile self.progress.timer(10, fn, False, requiresCollection=False)
def accept(self): QDialog.accept(self) # get codes try: ids = [int(n) for n in self.form.code.text().split()] except ValueError: showWarning(_("Invalid code.")) return errors = [] self.mw.progress.start(immediate=True) for n in ids: ret = download(self.mw, n) if ret[0] == "error": errors.append(_("Error downloading %(id)s: %(error)s") % dict(id=n, error=ret[1])) continue data, fname = ret self.mw.addonManager.install(data, fname) self.mw.progress.finish() if not errors: tooltip(_("Download successful. Please restart Anki."), period=3000) else: showWarning("\n".join(errors))
def historyRestore(self, mode, sorted_res, model): n = self.currentField field = model['flds'][n]['name'] last_val = {} keys = [] for nid in sorted_res[:100]: oldNote = self.note.col.getNote(nid) if field in oldNote: html = oldNote[field] else: try: html = oldNote.fields[n] except IndexError: pass if html.strip(): text = stripHTML(html) else: text = None if text and text not in last_val: keys.append(text) last_val[text] = html if not last_val: tooltip("No prior entries for this field found.") return False txt = "Set field to:" (text, ret) = myGetField(self.parentWindow, txt, keys, title="Field History") if not ret or not text.strip() or text not in last_val: return False self.note[field] = last_val[text]
def download_for_note(note=False, ask_user=False): """ Download audio for all fields. Download audio for all fields of the note passed in or the current note. When ask_user is true, show a dialog that lets the user modify these texts. """ card = None if not note: try: card = mw.reviewer.card note = card.note() except: return field_data = get_note_fields(note, get_empty=ask_user) if not field_data: # Complain before we show the empty dialog. tooltip(u'Nothing to download.') return language_code = get_language_code(card=card, note=note) if ask_user: try: field_data, language_code = update_data(field_data, language_code) except RuntimeError as rte: if 'cancel' in str(rte): # User canceled. No need for the "Nothing downloaded" # message. return else: # Don't know how to handle this after all raise do_download(note, field_data, language_code)
def addNote(self, data): "Takes model, deck, fields, tags. If deck is missing, use current deck." col = mw.col print "adding:", data # get model and deck model = col.models.byName(data["model"]) if not data["deck"]: deck_id = col.conf["curDeck"] else: deck_id = col.decks.id(data["deck"]) # make note note = notes.Note(col, model=model) note.model()["did"] = deck_id # you can specify fewer fields if you want, but not *more* if len(data["fields"]) > len(note.fields): raise Exception("received too many fields") for i, f in enumerate(data["fields"]): note.fields[i] = f if data["tags"]: for tag in data["tags"].split(): note.addTag(tag) # add note col.addNote(note) col.save() utils.tooltip("Note added.") return True
def get_preferred_valid_entries(editor, word): collegiate_url = "http://www.dictionaryapi.com/api/v1/references/collegiate/xml/" + \ urllib.parse.quote_plus(word) + "?key=" + MERRIAM_WEBSTER_API_KEY medical_url = "https://www.dictionaryapi.com/api/references/medical/v2/xml/" + \ urllib.parse.quote_plus(word) + "?key=" + MERRIAM_WEBSTER_MEDICAL_API_KEY all_collegiate_entries = get_entries_from_api(word, collegiate_url) all_medical_entries = get_entries_from_api(word, medical_url) potential_unified = set() if PREFERRED_DICTIONARY == "COLLEGIATE": entries = filter_entries_lower_and_potential(word, all_collegiate_entries) potential_unified |= entries.potential if not entries.valid: entries = filter_entries_lower_and_potential(word, all_medical_entries) potential_unified |= entries.potential else: entries = filter_entries_lower_and_potential(word, all_medical_entries) potential_unified |= entries.potential if not entries.valid: entries = filter_entries_lower_and_potential(word, all_collegiate_entries) potential_unified |= entries.potential if not entries.valid: potential = " Potential matches: " + ", ".join(potential_unified) tooltip("No entry found in Merriam-Webster dictionary for word '%s'.%s" % (word, potential if entries.potential else "")) _focus_zero_field(editor) return entries.valid
def convertNotes(self, nids): """Main note conversion method""" nids_by_nr = {} skipped = 0 (io_nids, filtered) = self.filterSelected(nids) for nid in io_nids: note = mw.col.getNote(nid) (uniq_id, note_nr) = self.getDataFromNamingScheme(note) if uniq_id == False: logging.debug("Skipping note that couldn't be parsed: %s", nid) skipped += 1 continue occl_tp = self.getOcclTypeAndNodes(note) occl_id = uniq_id + '-' + occl_tp if occl_id == self.occl_id_last: logging.debug( "Skipping note that we've just converted: %s", nid) continue self.occl_id_last = occl_id for nid in self.findByNoteId(uniq_id): note = mw.col.getNote(nid) (uniq_id, note_nr) = self.getDataFromNamingScheme(note) if uniq_id == False: logging.debug( "Skipping note that couldn't be parsed: %s", nid) skipped += 1 continue nids_by_nr[int(note_nr)] = nid self.idAndCorrelateNotes(nids_by_nr, occl_id) converted = len(io_nids) tooltip("<b>%i</b> notes updated, <b>%i</b> skipped" % (converted - skipped, filtered + skipped))
def on_lookup_wadoku_selection(): u"""Wrapper to look up the expression at Wadoku and catch value errors.""" try: # Empty list (or possibly 'None') means selection lookup_wadoku([]) except ValueError as ve: tooltip(str(ve))
def nextCard(self): if self.cardQueue: # undone/edited cards to show c = self.cardQueue.pop() c.startTimer() self.hadCardQueue = True else: if self.hadCardQueue: # the undone/edited cards may be sitting in the regular queue; # need to reset self.mw.col.reset() self.hadCardQueue = False c = self.mw.col.sched.getCard() self.card = c clearAudioQueue() if not c: self.mw.moveToState("overview") return if self._reps is None or self._reps % 100 == 0: # we recycle the webview periodically so webkit can free memory self._initWeb() else: self._showQuestion() elapsed = self.mw.col.timeboxReached() if elapsed: tooltip(_("%(cards)d cards studied in %(mins)s minutes.") % dict(cards=elapsed[1], mins=elapsed[0]/60)) self.mw.col.startTimebox()
def onCloze(self): # check that the model is set up for cloze deletion if not re.search("{{(.*:)*cloze:", self.note.model()["tmpls"][0]["qfmt"]): if self.addMode: tooltip(_("Warning, cloze deletions will not work until " "you switch the type at the top to Cloze.")) else: showInfo( _( """\ To make a cloze deletion on an existing note, you need to change it \ to a cloze type first, via Edit>Change Note Type.""" ) ) return # find the highest existing cloze highest = 0 for name, val in self.note.items(): m = re.findall("\{\{c(\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('{{c%d::', '}}');" % highest)
def generateNotes(self): """Generate new notes""" state = "default" self.uniq_id = str(uuid.uuid4()).replace("-", "") self.occl_id = '%s-%s' % (self.uniq_id, self.occl_tp) (svg_node, layer_node) = self._getMnodesAndSetIds() if not self.mnode_ids: tooltip("No cards to generate.<br>\ Are you sure you set your masks correctly?") return False self.new_svg = svg_node.toxml() # write changes to svg omask_path = self._saveMask(self.new_svg, self.occl_id, "O") qmasks = self._generateMaskSVGsFor("Q") amasks = self._generateMaskSVGsFor("A") image_path = mw.col.media.addFile(self.image_path) img = fname2img(image_path) mw.checkpoint("Adding Image Occlusion Cards") for nr, idx in enumerate(self.mnode_indexes): note_id = self.mnode_ids[idx] self._saveMaskAndReturnNote(omask_path, qmasks[nr], amasks[nr], img, note_id) tooltip("%s %s <b>added</b>" % self._cardS(len(qmasks)), parent=None) return state
def gui_add_QA_note(fnames_q, fnames_a, media_dir, tags, svg_fname, fname_original, header, footer, did): col = mw.col mm = col.models if not mm.byName(IMAGE_QA_MODEL_NAME): # first time addon is run add_image_QA_model(col) m = mm.byName(IMAGE_QA_MODEL_NAME) nrOfNotes = add_QA_note(col, fnames_q, fnames_a, tags, media_dir, svg_fname, fname_original, header, footer, did) rm_media_dir(media_dir) # removes the media and the directory # We must update the GUI so that the user knows that cards have # been added. When the GUI is updated, the number of new cards # changes, and it provides the feedback we want. # If we want more feedback, we can add a tooltip that tells the # user how many cards have been added. # The way to update the GUI will depend on the state # of the main window. There are four states (from what I understand): # - "review" # - "overview" # - "deckBrowser" # - "resetRequired" (we will treat this one like "deckBrowser) if mw.state == "review": mw.reviewer.show() elif mw.state == "overview": mw.overview.refresh() else: mw.deckBrowser.refresh() # this shows the browser even if the # main window is in state "resetRequired", which in my # opinion is a good thing utils.tooltip(notes_added_message(nrOfNotes))
def getNewImage(self, parent=None, noclip=False): """Get image from file selection or clipboard""" if noclip: clip = None else: clip = QApplication.clipboard() if clip and clip.mimeData().imageData(): handle, image_path = tempfile.mkstemp(suffix='.png') clip.image().save(image_path) clip.clear() if os.stat(image_path).st_size == 0: # workaround for a clipboard bug return self.getNewImage(noclip=True) else: return str(image_path) # retrieve last used image directory prev_image_dir = self.lconf["dir"] if not prev_image_dir or not os.path.isdir(prev_image_dir): prev_image_dir = IO_HOME image_path = QFileDialog.getOpenFileName(parent, "Select an Image", prev_image_dir, "Image Files (*.png *jpg *.jpeg *.gif)") if image_path: image_path = image_path[0] if not image_path: return None elif not os.path.isfile(image_path): tooltip("Invalid image file path") return False else: self.lconf["dir"] = os.path.dirname(image_path) return image_path
def nextCard(self): elapsed = self.mw.col.timeboxReached() if elapsed: part1 = ngettext("%d card studied in", "%d cards studied in", elapsed[1]) % elapsed[1] part2 = ngettext("%s minute.", "%s minutes.", elapsed[0]/60) % (elapsed[0]/60) tooltip("%s %s" % (part1, part2), period=5000) self.mw.col.startTimebox() if self.cardQueue: # undone/edited cards to show c = self.cardQueue.pop() c.startTimer() self.hadCardQueue = True else: if self.hadCardQueue: # the undone/edited cards may be sitting in the regular queue; # need to reset self.mw.col.reset() self.hadCardQueue = False c = self.mw.col.sched.getCard() self.card = c clearAudioQueue() if not c: self.mw.moveToState("overview") return if self._reps is None or self._reps % 100 == 0: # we recycle the webview periodically so webkit can free memory self._initWeb() else: self._showQuestion()
def accept(self): "On close, create notes from the contents of the poem editor." title = self.form.titleBox.text().strip() tags = self.mw.col.tags.canonify( self.mw.col.tags.split(self.form.tagsBox.text())) text = process_text(self.form.textBox.toPlainText().strip()) if not title.strip(): showWarning("You must enter a title for this poem.") return if self.mw.col.findNotes('"note:%s" "Title:%s"' % (lpcg_models.NAME, title)): showWarning("You already have a poem by that title in your " "database. Please check to see if you've already " "added it, or use a different name.") return if not text: showWarning("There's nothing to generate cards from! " "Please type a poem in the box, or use the " '"open file" button to import a text file.') return notes_generated = self._genNotes(title, tags, text) if notes_generated: super(LPCGDialog, self).accept() self.mw.reset() tooltip("%i notes added." % notes_generated)
def on_lookup_forvo_expression(): u"""Wrapper to look up the expression at Forvo and catch value errors.""" try: lookup_forvo(expression_fields) except ValueError as ve: tooltip(str(ve)) except AttributeError: tooltip(u'Error during lookup. (No note?)')
def setChildren(self): if not askUser(_("Set all decks below %s to this option group?") % self.deck["name"]): return for did in self.childDids: deck = self.mw.col.decks.get(did) deck["conf"] = self.deck["conf"] self.mw.col.decks.save(deck) tooltip(ngettext("%d deck updated.", "%d decks updated.", len(self.childDids)) % len(self.childDids))
def rescheduleRevReward(self, card, ease): """Increase ease factor as reward for straight""" dconf = self.col.decks.confForDid(card.did) if dconf.get('straitReward'): count = checkStrait(self, card, ease) if ease == 3 and count > 0 and count % STRAIT == STRAIT - 1: card.factor = max(1300, card.factor+REWARD) tooltip(str(count+1) + PRAISE + str(card.factor/10))
def on_lookup_wadoku_meaning(): u"""Wrapper to look up the expression at Wadoku and catch value errors.""" try: lookup_wadoku(meaning_fields) except ValueError as ve: tooltip(str(ve)) except AttributeError: tooltip(u'Error during lookup. (No note?)')
def do_download(note, field_data_list, language, hide_text=False): """ Download audio data. Go through the list of words and list of sites and download each word from each site. Then call a function that asks the user what to do. """ retrieved_entries = [] for field_data in field_data_list: if field_data.empty: continue for dloader in downloaders: # Use a public variable to set the language. dloader.language = language try: # Make it easer inside the downloader. If anything # goes wrong, don't catch, or raise whatever you want. dloader.download_files(field_data) except: # # Uncomment this raise while testing a new # # downloaders. Also comment out all the others in the # # downloaders list in downloaders.__init__ # raise continue retrieved_entries += dloader.downloads_list # Significantly changed the logic. Put all entries in one # list, do stuff with that list of DownloadEntries. try: retrieved_entries = review_entries(note, retrieved_entries, hide_text) # Now just the dialog, which sets the fields in the entries except ValueError as ve: tooltip(str(ve)) except RuntimeError as rte: if 'cancel' in str(rte): for entry in retrieved_entries: entry.action = Action.Delete else: raise for entry in retrieved_entries: entry.dispatch(note) if any(entry.action == Action.Add for entry in retrieved_entries): note.flush() # We have to do different things here, for download during # review, we should reload the card and replay. When we are in # the add dialog, we do a field update there. rnote = None try: rnote = mw.reviewer.card.note() except AttributeError: # Could not get the note of the reviewer's card. Probably # not reviewing at all. return if note == rnote: # The note we have is the one we were reviewing, so, # reload and replay mw.reviewer.card.load() mw.reviewer.replayAudio()
def _onHeight(self, qvar): if qvar is None: tooltip(_("Error connecting to local port. Retrying...")) from aqt import mw mw.progress.timer(2000, mw.reset, False) return height = math.ceil(qvar*self.zoomFactor()) self.setFixedHeight(height)
def onDelete(self): self.mw.checkpoint(_("Delete")) cnt = len(self.card.note().cards()) self.mw.col.remNotes([self.card.note().id]) self.mw.reset() tooltip(ngettext( "Note and its %d card deleted.", "Note and its %d cards deleted.", cnt) % cnt)
def bury_current_note(self) -> None: bury_notes( parent=self.mw, note_ids=[self.card.nid], ).success( lambda _: tooltip(tr.studying_note_buried())).run_in_background()
def bury_current_card(self) -> None: bury_cards( parent=self.mw, card_ids=[self.card.id], ).success( lambda _: tooltip(tr.studying_card_buried())).run_in_background()
def set_card_deck(*, parent: QWidget, card_ids: Sequence[CardId], deck_id: DeckId) -> CollectionOp[OpChangesWithCount]: return CollectionOp( parent, lambda col: col.set_deck(card_ids, deck_id)).success( lambda out: tooltip(tr.browsing_cards_updated(count=out.count), parent=parent))
def update_ICRP_sentences(): global log_message tooltip("Updating ICRP sentences... ") chinese_dictionary = read_cedict() cedict_keys = list(chinese_dictionary.keys()) # get all seen Chinese characters: ids = mw.col.findCards("note:Hanzi -is:suspended -is:new") known_hanzis = [] for card_id in ids: hanzi = mw.col.getCard(card_id).note()["hanzi"] known_hanzis.append(hanzi) # print(known_hanzis) # get all seen Chinese words: ids = mw.col.findCards("note:Word -is:suspended -is:new") known_words = [] for card_id in ids: word = mw.col.getCard(card_id).note()["hanzis"] known_words.append(word) # print(known_words) # iterate over the ICRP sentences: ids = mw.col.findCards("note:Sentence") for card_id in ids: table = "<table>\n" card = mw.col.getCard(card_id) note = card.note() # assigning vocabulary: sentence = note["Sentence"] print(sentence) while len(sentence) > 0: end_index = len(sentence) while end_index > 0: part = sentence[0:end_index] result = chinese_dictionary[part] if len(result) > 0: ktag = "" if end_index > 1: if part in known_words: ktag = " class=\"known\"" else: if part in known_hanzis: ktag = " class=\"known\"" for entry in result: table += "<tr{}>".format(ktag) if part != entry[0]: table += "<td class=\"hanzi small\">{}({})</td>".format( part, entry[0]) else: table += "<td class=\"hanzi small\">{}</td>".format( part) table += "<td class=\"pinyin\">{}</td>".format( entry[1]) table += "<td class=\"translation\">{}</td>".format( (" / ").join(entry[2])) table += "</tr>\n" end_index -= 1 sentence = sentence[1:] table += "</table>" note["Vocabulary"] = table note.flush() # creating {{Sentence with pinyin}}: sentence = note["Sentence"] sentence_with_pinyin = "" for character in sentence: pinyins = [] if character in known_hanzis: ktag = " known" else: ktag = "" result = chinese_dictionary[character] for entry in result: pinyins.append(entry[1]) color = list(set([x[-1] for x in pinyins])) if len(color) == 1: color_class = " color{}".format(color[0]) else: color_class = "" sentence_with_pinyin += "<div class=\"hanzi_with_pinyin{}\">".format( ktag) sentence_with_pinyin += "<div class=\"hanzi{}\">{}</div>".format( color_class, character) uniqe_pinyins = sorted(list(set([x.lower() for x in pinyins]))) pinyin_divs = ("").join([ "<div class=\"pinyin_div color{}\">{}</div>".format(x[-1], x) for x in uniqe_pinyins ]) sentence_with_pinyin += "<div class=\"pinyins_div\">{}</div></div>\n".format( pinyin_divs) note["Sentence with pinyin"] = sentence_with_pinyin note.flush() tooltip("Update complete!") return
def print_log_message(reviewer, ease=None): global log_message if len(log_message) > 0: tooltip(log_message) log_message = ""
def remove_all_from_queue(self): empty_priority_list() self.fill_list(self.t_view_left, [], with_nums = True) self.tabs.currentWidget().refresh() tooltip(f"Queue emptied.")
def reopen(self, mw): tooltip("Please finish editing the existing card first.") self.onReset()
def onCloze(self, _old): model = self.note.model() # If the model is set up for cloze deletion, then defer to Anki's implementation. if re.search('{{(.*:)*cloze:', model['tmpls'][0]['qfmt']): return _old(self) else: # Check if field is non-empty, in which case it can be clozed. if self.note.fields[self.currentField]: current_field_name = model["flds"][self.currentField]["name"] if current_field_name.endswith("Cloze"): content = self.note.fields[self.currentField] cloze_nums = get_cloze_nums(content) # Determine what cloze number the currently highlighted text should get. if cloze_nums: next_cloze_num = max(cloze_nums) # Unless we are reusing, then increment to the next greatest cloze number. if not self.mw.app.keyboardModifiers() & Qt.AltModifier: next_cloze_num += 1 else: next_cloze_num = 1 commands = ["wrap('((c{}::', '))')".format(next_cloze_num)] cloze_nums.add(next_cloze_num) cloze_field_update_commands, found_cloze_nums = \ update_cloze_fields(self, cloze_nums=cloze_nums, cloze_field_name=current_field_name, model=model) commands.extend(cloze_field_update_commands) missing_cloze_num = cloze_nums - found_cloze_nums self.web.eval(";".join(commands) + ";") if missing_cloze_num: tooltip("Not enough cloze fields. Missing: {}".format( ", ".join(current_field_name + str(n) for n in sorted(missing_cloze_num)))) else: tooltip("Cannot cloze unless field ends in name Cloze") else: # If the field is empty, then to be helpful we can check if it ends in Cloze and in that case # copy from another field without Cloze. For example, when ExpressionCloze is the current # field and it is empty, we will copy from the Expression field. current_field_name = model["flds"][self.currentField]["name"] if current_field_name.endswith("Cloze"): other_field_name = current_field_name[:-len("Cloze")] other_field_name_ord = next( (f["ord"] for f in model["flds"] if f["name"] == other_field_name), None) if other_field_name_ord is not None: content = self.note.fields[other_field_name_ord] self.web.eval("setFormat('inserthtml', {});".format( json.dumps(content))) else: tooltip( "Cannot populate empty field {} because other field {} was not found to copy from" .format(current_field_name, other_field_name)) else: tooltip( "Cannot populate empty field {} because name does not end in Cloze" .format(current_field_name))
def importFile(mw, file): importer = None done = False for i in importing.Importers: if done: break for mext in re.findall("[( ]?\*\.(.+?)[) ]", i[0]): if file.endswith("." + mext): importer = i[1] done = True break if not importer: # if no matches, assume TSV importer = importing.Importers[0][1] importer = importer(mw.col, file) # need to show import dialog? if importer.needMapper: # make sure we can load the file first mw.progress.start(immediate=True) try: importer.open() except UnicodeDecodeError: showUnicodeWarning() return except Exception as e: msg = repr(str(e)) if msg == "'unknownFormat'": if file.endswith(".anki2"): showWarning( _("""\ .anki2 files are not designed for importing. If you're trying to restore from a \ backup, please see the 'Backups' section of the user manual.""")) else: showWarning(_("Unknown file format.")) else: msg = _("Import failed. Debugging info:\n") msg += str(traceback.format_exc()) showText(msg) return finally: mw.progress.finish() diag = ImportDialog(mw, importer) else: # if it's an apkg/zip, first test it's a valid file if importer.__class__.__name__ == "AnkiPackageImporter": try: z = zipfile.ZipFile(importer.file) z.getinfo("collection.anki2") except: showWarning(invalidZipMsg()) return # we need to ask whether to import/replace if not setupApkgImport(mw, importer): return mw.progress.start(immediate=True) try: importer.run() except zipfile.BadZipfile: showWarning(invalidZipMsg()) except Exception as e: err = repr(str(e)) if "invalidFile" in err: msg = _("""\ Invalid file. Please restore from backup.""") showWarning(msg) elif "invalidTempFolder" in err: showWarning(mw.errorHandler.tempFolderMsg()) elif "readonly" in err: showWarning(_("""\ Unable to import from a read-only file.""")) else: msg = _("Import failed.\n") msg += str(traceback.format_exc()) showText(msg) else: log = "\n".join(importer.log) if "\n" not in log: tooltip(log) else: showText(log) finally: mw.progress.finish() mw.reset()
def quickAddCards(self, quickKeyModel): self.saveScrollPosition() hasSelection = 0 selectedText = '' #Copy text or html to clipboard if selected, else just use clipboard contents (user could hit Ctrl-C in a web browser instead) if (len(mw.web.selectedText()) > 0): hasSelection = 1 mw.web.triggerPageAction(QWebPage.Copy) clipboard = QApplication.clipboard() mimeData = clipboard.mimeData() selectedText = mimeData.html() #Highlight the text in the original document. This is only useful for cards with long texts like IRead2. Other card models will ignore. if (quickKeyModel.get('color', None) != None): runHook("highlightText", quickKeyModel['color'], quickKeyModel.get('colorText', 'false')) #Create new note with selected model and deck new_model = mw.col.models.byName(quickKeyModel['modelName']) new_note = notes.Note(mw.col, new_model) self.setField(new_note, quickKeyModel['fieldName'], selectedText) #Add tags and copy source fields from source card, if applicable if (mw.reviewer.card): card = mw.reviewer.card cur_note = card.note() tags = cur_note.stringTags() new_note.setTagsFromStr(tags) #sets tags for the note, but still have to set them in the editor if show dialog (see below) SOURCE_FIELD_NAME = 'Source' TITLE_FIELD_NAME = 'Title' self.setField(new_note, SOURCE_FIELD_NAME, self.getField(cur_note, SOURCE_FIELD_NAME)) self.setField(new_note, TITLE_FIELD_NAME, self.getField(cur_note, TITLE_FIELD_NAME)) #This is very specific to IRead2 Model and should be generalized or moved elsewhere IREAD_MODEL_NAME = 'IRead2' TEXT_FIELD_NAME = 'Text' SOURCE_FIELD_NAME = 'Source' DECK_FIELD_NAME = 'Anki Deck' MODEL_FIELD_NAME = 'Model' if (mw.reviewer.card.model()['name'] == IREAD_MODEL_NAME): for f in new_model['flds']: if (SOURCE_FIELD_NAME == f['name']): self.setField( new_note, SOURCE_FIELD_NAME, self.getField(cur_note, SOURCE_FIELD_NAME)) #if(quickKeyModel['modelName'] == IREAD_MODEL_NAME): # self.setField(new_note, SOURCE_FIELD_NAME, self.getField(cur_note, SOURCE_FIELD_NAME)) # self.setField(new_note, MODEL_FIELD_NAME, self.getField(cur_note, MODEL_FIELD_NAME)) # self.setField(new_note, DECK_FIELD_NAME, self.getField(cur_note, DECK_FIELD_NAME)) #If shortcut said NOT to show AddCards dialog, then skip it. if (quickKeyModel['showEditor'] == 0): if (hasSelection == 1): new_note.model()['did'] = mw.col.decks.byName( quickKeyModel['deckName'])['id'] ret = new_note.dupeOrEmpty() if ret == 1: showWarning(_("The first field is empty."), help="AddItems#AddError") return cards = mw.col.addNote(new_note) if not cards: showWarning(_("""\ The input you have provided would make an empty \ question on all cards."""), help="AddItems") return # stop anything playing clearAudioQueue() mw.col.autosave() tooltip(_("Added"), period=500) #Else show the add cards dialog else: self.acsCount += 1 if (quickKeyModel['showEditCurrent'] == 1): self.editCurrent = editcurrent.EditCurrent(mw) self.addCards = addcards.AddCards(mw) self.addCards.editor.setNote(new_note) if (new_note.stringTags() != None): self.addCards.editor.tags.setText( new_note.stringTags().strip()) #Not sure why doesn't get set automatically since note has associated tags, but ... self.addCards.modelChooser.models.setText( quickKeyModel['modelName']) self.addCards.deckChooser.deck.setText(quickKeyModel['deckName'])
def onEvent(self, evt, *args): pu = self.mw.progress.update if evt == "badAuth": tooltip( _("AnkiWeb ID or password was incorrect; please try again."), parent=self.mw) # blank the key so we prompt user again self.pm.profile['syncKey'] = None self.pm.save() elif evt == "corrupt": pass elif evt == "newKey": self.pm.profile['syncKey'] = args[0] self.pm.save() elif evt == "offline": tooltip(_("Syncing failed; internet offline.")) elif evt == "upbad": self._didFullUp = False self._checkFailed() elif evt == "sync": m = None t = args[0] if t == "login": m = _("Syncing...") elif t == "upload": self._didFullUp = True m = _("Uploading to AnkiWeb...") elif t == "download": m = _("Downloading from AnkiWeb...") elif t == "sanity": m = _("Checking...") elif t == "findMedia": m = _("Checking media...") elif t == "upgradeRequired": showText( _("""\ Please visit AnkiWeb, upgrade your deck, then try again.""")) if m: self.label = m self._updateLabel() elif evt == "syncMsg": self.label = args[0] self._updateLabel() elif evt == "error": self._didError = True showText(_("Syncing failed:\n%s") % self._rewriteError(args[0])) elif evt == "clockOff": self._clockOff() elif evt == "checkFailed": self._checkFailed() elif evt == "mediaSanity": showWarning( _("""\ A problem occurred while syncing media. Please use Tools>Check Media, then \ sync again to correct the issue.""")) elif evt == "noChanges": pass elif evt == "fullSync": self._confirmFullSync() elif evt == "send": # posted events not guaranteed to arrive in order self.sentBytes = max(self.sentBytes, int(args[0])) self._updateLabel() elif evt == "recv": self.recvBytes = max(self.recvBytes, int(args[0])) self._updateLabel()
def onRestoreDefaults(self) -> None: default_conf = self.mgr.addonConfigDefaults(self.addon) self.updateText(default_conf) tooltip(_("Restored defaults"), parent=self)
def onSuspend(self) -> None: self.mw.checkpoint(_("Suspend")) self.mw.col.sched.suspendCards([c.id for c in self.card.note().cards()]) tooltip(_("Note suspended.")) self.mw.reset()
def processNote(self, deckName): self.window.debug.appendPlainText("Processing Notes") deck = mw.col.decks.get(mw.col.decks.id(deckName)) # create custom model model = self.addCustomModel(deckName, mw.col) # assign custom model to new deck mw.col.decks.select(deck["id"]) mw.col.decks.get(deck)["mid"] = model["id"] mw.col.decks.save(deck) # assign new deck to custom model mw.col.models.setCurrent(model) mw.col.models.current()["did"] = deck["id"] mw.col.models.save(model) # start creating notes if self.new: for term in self.new: note = mw.col.newNote() note['term'] = term['term'] note['definition'] = term['definition'] note['uk'] = term['uk'] note['us'] = term['us'] if term['phrases'][0]: for index, phrase in enumerate(term['phrases']): note['phrase' + str(index)] = phrase note['phrase_explain' + str(index)] = term['phrases_explains'][index] note['pplaceHolder' + str(index)] = "Tap To View" if term['sentences'][0]: for index, sentence in enumerate(term['sentences']): note['sentence' + str(index)] = sentence note['sentence_explain' + str(index)] = term['sentences_explains'][index] note['splaceHolder' + str(index)] = "Tap To View" if term['image']: if self.window.downloadimage.isChecked(): note[ 'image'] = "<img src = 'Deck2Anki/{}.jpg'>".format( term['image']) else: note['image'] = "<img src ='{}' >".format( term['image']) mw.col.addNote(note) mw.col.fixIntegrity() mw.col.reset() mw.reset() # start deleting notes if self.deleted: for term in self.deleted: cardID = mw.col.findCards("term:" + term) deckID = mw.col.decks.id(deckName) for cid in cardID: nid = mw.col.db.scalar( "select nid from cards where id = ? and did = ?", cid, deckID) if nid is not None: mw.col.db.execute("delete from cards where id =?", cid) mw.col.db.execute("delete from notes where id =?", nid) mw.col.fixIntegrity() mw.col.reset() mw.reset() self.window.debug.appendPlainText("Notes processed") tooltip('Added : ' + str(len(self.new)) + '<br><br>Deleted : ' + str(len(self.deleted)), period=3000)
def accept(self): self.exporter.includeSched = (self.frm.includeSched.isChecked()) self.exporter.includeMedia = (self.frm.includeMedia.isChecked()) self.exporter.includeTags = (self.frm.includeTags.isChecked()) if not self.frm.deck.currentIndex(): self.exporter.did = None else: name = self.decks[self.frm.deck.currentIndex()] self.exporter.did = self.col.decks.id(name) if (self.isApkg and self.exporter.includeSched and not self.exporter.did): verbatim = True # it's a verbatim apkg export, so place on desktop instead of # choosing file; use homedir if no desktop usingHomedir = False file = os.path.join( QDesktopServices.storageLocation( QDesktopServices.DesktopLocation), "collection.apkg") if not os.path.exists(os.path.dirname(file)): usingHomedir = True file = os.path.join( QDesktopServices.storageLocation( QDesktopServices.HomeLocation), "collection.apkg") if os.path.exists(file): if usingHomedir: question = _( "%s already exists in your home directory. Overwrite it?" ) else: question = _( "%s already exists on your desktop. Overwrite it?") if not askUser(question % "collection.apkg"): return else: verbatim = False # Get deck name and remove invalid filename characters deck_name = self.decks[self.frm.deck.currentIndex()] deck_name = re.sub('[\\\\/?<>:*|"^]', '_', deck_name) filename = os.path.join( aqt.mw.pm.base, u'{0}{1}'.format(deck_name, self.exporter.ext)) while 1: file = getSaveFile(self, _("Export"), "export", self.exporter.key, self.exporter.ext, fname=filename) if not file: return if checkInvalidFilename(os.path.basename(file), dirsep=False): continue break self.hide() if file: self.mw.progress.start(immediate=True) try: f = open(file, "wb") f.close() except (OSError, IOError), e: showWarning(_("Couldn't save file: %s") % unicode(e)) else: os.unlink(file) exportedMedia = lambda cnt: self.mw.progress.update( label=ngettext("Exported %d media file", "Exported %d media files", cnt) % cnt) addHook("exportedMediaFiles", exportedMedia) self.exporter.exportInto(file) remHook("exportedMediaFiles", exportedMedia) if verbatim: if usingHomedir: msg = _( "A file called %s was saved in your home directory." ) else: msg = _("A file called %s was saved on your desktop.") msg = msg % "collection.apkg" period = 5000 else: period = 3000 if self.isTextNote: msg = ngettext( "%d note exported.", "%d notes exported.", self.exporter.count) % self.exporter.count else: msg = ngettext( "%d card exported.", "%d cards exported.", self.exporter.count) % self.exporter.count tooltip(msg, period=period) finally:
def onReplayRecorded(self): if not self._recordedAudio: return tooltip(_("You haven't recorded your voice yet.")) clearAudioQueue() play(self._recordedAudio)
def post( st ): # :: State -> State tooltip(_( 'Tagged {} notes containing morphemes in the selected db with "{}" '.format(st['noteCount'], st['tags']) ) ) return st
def onBuryNote(self): self.mw.checkpoint(_("Bury")) self.mw.col.sched.buryNote(self.card.nid) self.mw.reset() tooltip(_("Note buried."))
def answerCard_before(self, ease): l = self._answerButtonList() a = [item for item in l if item[0] == ease] if len(a) > 0: tooltip(a[0][1])
def onBuryCard(self): self.mw.checkpoint(_("Bury")) self.mw.col.sched.buryCards([self.card.id]) self.mw.reset() tooltip(_("Card buried."))
def onSuspendCard(self): self.mw.checkpoint(_("Suspend")) self.mw.col.sched.suspendCards([self.card.id]) tooltip(_("Card suspended.")) self.mw.reset()
def on_syncBtn_clicked(self): failedGenerator = (self.newWordListWidget.item(row).data(Qt.UserRole) is None for row in range(self.newWordListWidget.count())) if any(failedGenerator): if not askUser('存在未查询或失败的单词,确定要加入单词本吗?\n 你可以选择失败的单词点击 "查询按钮" 来重试。'): return currentConfig = self.getAndSaveCurrentConfig() model = getOrCreateModel(MODEL_NAME) getOrCreateModelCardTemplate(model, 'default') deck = getOrCreateDeck(self.deckComboBox.currentText()) logger.info('同步点击') audiosDownloadTasks = [] newWordCount = self.newWordListWidget.count() # 判断是否需要下载发音 if currentConfig['noPron']: logger.info('不下载发音') whichPron = None else: whichPron = 'AmEPron' if self.AmEPronRadioButton.isChecked() else 'BrEPron' logger.info(f'下载发音{whichPron}') added = 0 for row in range(newWordCount): wordItem = self.newWordListWidget.item(row) wordItemData = wordItem.data(Qt.UserRole) if wordItemData: addNoteToDeck(deck, model, currentConfig, wordItemData) added += 1 # 添加发音任务 if whichPron and wordItemData.get(whichPron): audiosDownloadTasks.append((f"{whichPron}_{wordItemData['term']}.mp3", wordItemData[whichPron],)) mw.reset() logger.info(f'发音下载任务:{audiosDownloadTasks}') if audiosDownloadTasks: self.syncBtn.setEnabled(False) self.progressBar.setValue(0) self.progressBar.setMaximum(len(audiosDownloadTasks)) if self.audioDownloadThread is not None: self.audioDownloadThread.requestInterruption() self.audioDownloadThread.quit() self.audioDownloadThread.wait() self.audioDownloadThread = QThread(self) self.audioDownloadThread.start() self.audioDownloadWorker = AudioDownloadWorker(audiosDownloadTasks) self.audioDownloadWorker.moveToThread(self.audioDownloadThread) self.audioDownloadWorker.tick.connect(lambda: self.progressBar.setValue(self.progressBar.value() + 1)) self.audioDownloadWorker.start.connect(self.audioDownloadWorker.run) self.audioDownloadWorker.done.connect(lambda: tooltip(f'发音下载完成')) self.audioDownloadWorker.done.connect(self.audioDownloadThread.quit) self.audioDownloadWorker.start.emit() self.newWordListWidget.clear() needToDeleteWordItems = [ self.needDeleteWordListWidget.item(row) for row in range(self.needDeleteWordListWidget.count()) if self.needDeleteWordListWidget.item(row).checkState() == Qt.Checked ] needToDeleteWords = [i.text() for i in needToDeleteWordItems] deleted = 0 if needToDeleteWords and askUser(f'确定要删除这些单词吗:{needToDeleteWords[:3]}...({len(needToDeleteWords)}个)', title='Dict2Anki', parent=self): needToDeleteWordNoteIds = getNotes(needToDeleteWords, currentConfig['deck']) mw.col.remNotes(needToDeleteWordNoteIds) deleted += 1 mw.col.reset() mw.reset() for item in needToDeleteWordItems: self.needDeleteWordListWidget.takeItem(self.needDeleteWordListWidget.row(item)) logger.info('删除完成') logger.info('完成') if not audiosDownloadTasks: tooltip(f'添加{added}个笔记\n删除{deleted}个笔记')
def notify_user(cnt, oldname, newfilename): s = f'Updated file location/reference in {cnt} note{"s" if cnt > 1 else ""}: <br> from {oldname} to {newfilename}' tooltip(s, period=6000)
def accept(self): self.exporter.includeSched = (self.frm.includeSched.isChecked()) self.exporter.includeMedia = (self.frm.includeMedia.isChecked()) self.exporter.includeTags = (self.frm.includeTags.isChecked()) self.exporter.includeHTML = (self.frm.includeHTML.isChecked()) if not self.frm.deck.currentIndex(): self.exporter.did = None else: name = self.decks[self.frm.deck.currentIndex()] self.exporter.did = self.col.decks.id(name) if self.isVerbatim: name = time.strftime("-%Y-%m-%d@%H-%M-%S", time.localtime(time.time())) deck_name = _("collection") + name else: # Get deck name and remove invalid filename characters deck_name = self.decks[self.frm.deck.currentIndex()] deck_name = re.sub('[\\\\/?<>:*|"^]', '_', deck_name) if not self.isVerbatim and self.isApkg and self.exporter.includeSched and self.col.schedVer( ) == 2: showInfo( "Please switch to the regular scheduler before exporting a single deck .apkg with scheduling." ) return filename = '{0}{1}'.format(deck_name, self.exporter.ext) while 1: file = getSaveFile(self, _("Export"), "export", self.exporter.key, self.exporter.ext, fname=filename) if not file: return if checkInvalidFilename(os.path.basename(file), dirsep=False): continue break self.hide() if file: self.mw.progress.start(immediate=True) try: f = open(file, "wb") f.close() except (OSError, IOError) as e: showWarning(_("Couldn't save file: %s") % str(e)) else: os.unlink(file) exportedMedia = lambda cnt: self.mw.progress.update( label=ngettext("Exported %d media file", "Exported %d media files", cnt) % cnt) addHook("exportedMediaFiles", exportedMedia) self.exporter.exportInto(file) remHook("exportedMediaFiles", exportedMedia) period = 3000 if self.isVerbatim: msg = _("Collection exported.") else: if self.isTextNote: msg = ngettext( "%d note exported.", "%d notes exported.", self.exporter.count) % self.exporter.count else: msg = ngettext( "%d card exported.", "%d cards exported.", self.exporter.count) % self.exporter.count tooltip(msg, period=period) finally: self.mw.progress.finish() QDialog.accept(self)
def onReplayRecorded(self) -> None: if not self._recordedAudio: tooltip(tr.studying_you_havent_recorded_your_voice_yet()) return av_player.play_file(self._recordedAudio)
def onReplayRecorded(self): if not self._recordedAudio: return tooltip(_("You haven't recorded your voice yet.")) av_player.play_file(self._recordedAudio)
def check_for_updates(self) -> None: tooltip(_("Checking...")) check_and_prompt_for_updates(self, self.mgr, self.after_downloading)
def after_downloading(self, log: List[DownloadLogEntry]) -> None: self.redrawAddons() if log: show_log_to_user(self, log) else: tooltip(_("No updates available."))
def onLeech(self, card: Card) -> None: # for now s = _("Card was a leech.") if card.queue < 0: s += " " + _("It has been suspended.") tooltip(s)
def suspend_current_note(self) -> None: suspend_note( parent=self.mw, note_ids=[self.card.nid], ).success(lambda _: tooltip(tr.studying_note_suspended()) ).run_in_background()