Esempio n. 1
0
def test_nextIvl():
    d = getEmptyCol()
    f = d.newNote()
    f["Front"] = "one"
    f["Back"] = "two"
    d.addNote(f)
    d.reset()
    conf = d.decks.confForDid(1)
    conf["new"]["delays"] = [0.5, 3, 10]
    conf["lapse"]["delays"] = [1, 5, 9]
    d.decks.save(conf)
    c = d.sched.getCard()
    # new cards
    ##################################################
    ni = d.sched.nextIvl
    assert ni(c, 1) == 30
    assert ni(c, 2) == 180
    assert ni(c, 3) == 4 * 86400
    d.sched.answerCard(c, 1)
    # cards in learning
    ##################################################
    assert ni(c, 1) == 30
    assert ni(c, 2) == 180
    assert ni(c, 3) == 4 * 86400
    d.sched.answerCard(c, 2)
    assert ni(c, 1) == 30
    assert ni(c, 2) == 600
    assert ni(c, 3) == 4 * 86400
    d.sched.answerCard(c, 2)
    # normal graduation is tomorrow
    assert ni(c, 2) == 1 * 86400
    assert ni(c, 3) == 4 * 86400
    # lapsed cards
    ##################################################
    c.type = CARD_TYPE_REV
    c.ivl = 100
    c.factor = STARTING_FACTOR
    assert ni(c, 1) == 60
    assert ni(c, 2) == 100 * 86400
    assert ni(c, 3) == 100 * 86400
    # review cards
    ##################################################
    c.queue = QUEUE_TYPE_REV
    c.ivl = 100
    c.factor = STARTING_FACTOR
    # failing it should put it at 60s
    assert ni(c, 1) == 60
    # or 1 day if relearn is false
    conf["lapse"]["delays"] = []
    d.decks.save(conf)
    assert ni(c, 1) == 1 * 86400
    # (* 100 1.2 86400)10368000.0
    assert ni(c, 2) == 10368000
    # (* 100 2.5 86400)21600000.0
    assert ni(c, 3) == 21600000
    # (* 100 2.5 1.3 86400)28080000.0
    assert ni(c, 4) == 28080000
    assert without_unicode_isolation(d.sched.nextIvlStr(c, 4)) == "10.8mo"
Esempio n. 2
0
 def _ensureProfile(self) -> None:
     "Create a new profile if none exists."
     self.create(_("User 1"))
     p = os.path.join(self.base, "README.txt")
     with open(p, "w", encoding="utf8") as file:
         file.write(
             without_unicode_isolation(
                 tr(TR.PROFILES_FOLDER_README,
                    link=appHelpSite + "#startupopts")))
Esempio n. 3
0
 def copy(self, notetype: NotetypeDict, add: bool = True) -> NotetypeDict:
     "Copy, save and return."
     cloned = copy.deepcopy(notetype)
     cloned["name"] = without_unicode_isolation(
         self.col.tr.notetypes_copy(val=cloned["name"]))
     cloned["id"] = 0
     if add:
         self.add(cloned)
     return cloned
Esempio n. 4
0
 def _ensureProfile(self) -> None:
     "Create a new profile if none exists."
     self.create(tr.profiles_user_1())
     p = os.path.join(self.base, "README.txt")
     with open(p, "w", encoding="utf8") as file:
         file.write(
             without_unicode_isolation(
                 tr.profiles_folder_readme(
                     link=f"{appHelpSite}files#startup-options", )) + "\n")
Esempio n. 5
0
 def copy(self, m: NoteType) -> NoteType:
     "Copy, save and return."
     m2 = copy.deepcopy(m)
     m2["name"] = without_unicode_isolation(
         self.col.tr(TR.NOTETYPES_COPY, val=m2["name"])
     )
     m2["id"] = 0
     self.add(m2)
     return m2
Esempio n. 6
0
 def copy(self, m: NotetypeDict, add: bool = True) -> NotetypeDict:
     "Copy, save and return."
     m2 = copy.deepcopy(m)
     m2["name"] = without_unicode_isolation(
         self.col.tr.notetypes_copy(val=m2["name"]))
     m2["id"] = 0
     if add:
         self.add(m2)
     return m2
Esempio n. 7
0
    def _load_deck(self) -> None:
        form = self.form
        deck = self.deck
        config = deck.config

        self.form.name.setText(deck.name)
        self.form.name.setPlaceholderText(deck.name)

        existing = deck.id != 0
        if existing:
            build_label = tr.actions_rebuild()
        else:
            build_label = tr.decks_build()
        self.form.buttonBox.button(
            QDialogButtonBox.StandardButton.Ok).setText(build_label)

        form.resched.setChecked(config.reschedule)
        self._onReschedToggled(0)

        term1: FilteredDeckConfig.SearchTerm = config.search_terms[0]
        form.search.setText(term1.search)
        form.order.setCurrentIndex(term1.order)
        form.limit.setValue(term1.limit)

        if self.col.sched_ver() == 1:
            if config.delays:
                form.steps.setText(self.listToUser(list(config.delays)))
                form.stepsOn.setChecked(True)
        else:
            form.steps.setVisible(False)
            form.stepsOn.setVisible(False)

        form.previewDelay.setValue(config.preview_delay)

        if len(config.search_terms) > 1:
            term2: FilteredDeckConfig.SearchTerm = config.search_terms[1]
            form.search_2.setText(term2.search)
            form.order_2.setCurrentIndex(term2.order)
            form.limit_2.setValue(term2.limit)
            show_second = existing
        else:
            show_second = False
            form.order_2.setCurrentIndex(5)
            form.limit_2.setValue(20)

        form.secondFilter.setChecked(show_second)
        form.filter2group.setVisible(show_second)

        self.set_custom_searches(self._desired_search_1,
                                 self._desired_search_2)

        self.setWindowTitle(
            without_unicode_isolation(
                tr.actions_options_for(val=self.deck.name)))

        gui_hooks.filtered_deck_dialog_did_load_deck(self, deck)
Esempio n. 8
0
 def _ensureProfile(self) -> None:
     "Create a new profile if none exists."
     self.create(tr(TR.PROFILES_USER_1))
     p = os.path.join(self.base, "README.txt")
     with open(p, "w", encoding="utf8") as file:
         file.write(
             without_unicode_isolation(
                 tr(
                     TR.PROFILES_FOLDER_README,
                     link=f"{appHelpSite}files?id=startup-options",
                 )))
Esempio n. 9
0
 def updateTitle(self) -> None:
     selected = self.table.len_selection()
     cur = self.table.len()
     tr_title = (
         tr.browsing_window_title_notes
         if self.table.is_notes_mode()
         else tr.browsing_window_title
     )
     self.setWindowTitle(
         without_unicode_isolation(tr_title(total=cur, selected=selected))
     )
Esempio n. 10
0
    def __init__(
        self,
        mw: AnkiQt,
        nt: NotetypeDict,
        parent: Optional[QWidget] = None,
        open_at: int = 0,
    ) -> None:
        QDialog.__init__(self, parent or mw)
        mw.garbage_collect_on_dialog_finish(self)
        self.mw = mw
        self.col = self.mw.col
        self.mm = self.mw.col.models
        self.model = nt
        self.mm._remove_from_cache(self.model["id"])
        self.change_tracker = ChangeTracker(self.mw)

        self.setWindowTitle(
            without_unicode_isolation(
                tr.fields_fields_for(val=self.model["name"])))

        if os.getenv("ANKI_EXPERIMENTAL_FIELDS_WEB"):
            form = aqt.forms.fields_web.Ui_Dialog()
            form.setupUi(self)

            self.webview = form.webview
            self.webview.set_title("fields")

            self.show()
            self.refresh()
            self.webview.set_bridge_command(self._on_bridge_cmd, self)
            self.activateWindow()
            return

        self.form = aqt.forms.fields.Ui_Dialog()
        self.form.setupUi(self)
        self.webview = None

        disable_help_button(self)
        self.form.buttonBox.button(
            QDialogButtonBox.StandardButton.Help).setAutoDefault(False)
        self.form.buttonBox.button(
            QDialogButtonBox.StandardButton.Cancel).setAutoDefault(False)
        self.form.buttonBox.button(
            QDialogButtonBox.StandardButton.Save).setAutoDefault(False)
        self.currentIdx: Optional[int] = None
        self.fillFields()
        self.setupSignals()
        self.form.fieldList.setDragDropMode(
            QAbstractItemView.DragDropMode.InternalMove)
        self.form.fieldList.dropEvent = self.onDrop  # type: ignore[assignment]
        self.form.fieldList.setCurrentRow(open_at)
        self.exec()
Esempio n. 11
0
 def __init__(
     self,
     mw: AnkiQt,
     note: Note,
     ord=0,
     parent: Optional[QWidget] = None,
     fill_empty: bool = False,
 ):
     QDialog.__init__(self, parent or mw, Qt.Window)
     mw.setupDialogGC(self)
     self.mw = aqt.mw
     self.note = note
     self.ord = ord
     self.col = self.mw.col.weakref()
     self.mm = self.mw.col.models
     self.model = note.model()
     self.templates = self.model["tmpls"]
     self.fill_empty_action_toggled = fill_empty
     self.night_mode_is_enabled = self.mw.pm.night_mode()
     self.mobile_emulation_enabled = False
     self.have_autoplayed = False
     self.mm._remove_from_cache(self.model["id"])
     self.mw.checkpoint(tr(TR.CARD_TEMPLATES_CARD_TYPES))
     self.change_tracker = ChangeTracker(self.mw)
     self.setupTopArea()
     self.setupMainArea()
     self.setupButtons()
     self.setupShortcuts()
     self.setWindowTitle(
         without_unicode_isolation(
             tr(TR.CARD_TEMPLATES_CARD_TYPES_FOR, val=self.model["name"])
         )
     )
     disable_help_button(self)
     v1 = QVBoxLayout()
     v1.addWidget(self.topArea)
     v1.addWidget(self.mainArea)
     v1.addLayout(self.buttons)
     v1.setContentsMargins(12, 12, 12, 12)
     self.setLayout(v1)
     gui_hooks.card_layout_will_show(self)
     self.redraw_everything()
     restoreGeom(self, "CardLayout")
     restoreSplitter(self.mainArea, "CardLayoutMainArea")
     self.setWindowModality(Qt.ApplicationModal)
     self.show()
     # take the focus away from the first input area when starting up,
     # as users tend to accidentally type into the template
     self.setFocus()
Esempio n. 12
0
def get_id_and_pass_from_user(mw: aqt.main.AnkiQt,
                              username="",
                              password="") -> Tuple[str, str]:
    diag = QDialog(mw)
    diag.setWindowTitle("Anki")
    diag.setWindowFlags(self.windowFlags()
                        & ~Qt.WindowContextHelpButtonHint)  # type: ignore
    diag.setWindowModality(Qt.WindowModal)
    vbox = QVBoxLayout()
    info_label = QLabel(
        without_unicode_isolation(
            tr(TR.SYNC_ACCOUNT_REQUIRED,
               link="https://ankiweb.net/account/register")))
    info_label.setOpenExternalLinks(True)
    info_label.setWordWrap(True)
    vbox.addWidget(info_label)
    vbox.addSpacing(20)
    g = QGridLayout()
    l1 = QLabel(tr(TR.SYNC_ANKIWEB_ID_LABEL))
    g.addWidget(l1, 0, 0)
    user = QLineEdit()
    user.setText(username)
    g.addWidget(user, 0, 1)
    l2 = QLabel(tr(TR.SYNC_PASSWORD_LABEL))
    g.addWidget(l2, 1, 0)
    passwd = QLineEdit()
    passwd.setText(password)
    passwd.setEchoMode(QLineEdit.Password)
    g.addWidget(passwd, 1, 1)
    vbox.addLayout(g)
    bb = QDialogButtonBox(QDialogButtonBox.Ok
                          | QDialogButtonBox.Cancel)  # type: ignore
    bb.button(QDialogButtonBox.Ok).setAutoDefault(True)
    qconnect(bb.accepted, diag.accept)
    qconnect(bb.rejected, diag.reject)
    vbox.addWidget(bb)
    diag.setLayout(vbox)
    diag.show()

    accepted = diag.exec_()
    if not accepted:
        return ("", "")
    return (user.text().strip(), passwd.text())
Esempio n. 13
0
File: sync.py Progetto: rye761/anki
def get_id_and_pass_from_user(mw: aqt.main.AnkiQt,
                              username: str = "",
                              password: str = "") -> tuple[str, str]:
    diag = QDialog(mw)
    diag.setWindowTitle("Anki")
    disable_help_button(diag)
    diag.setWindowModality(Qt.WindowModality.WindowModal)
    vbox = QVBoxLayout()
    info_label = QLabel(
        without_unicode_isolation(
            tr.sync_account_required(
                link="https://ankiweb.net/account/register")))
    info_label.setOpenExternalLinks(True)
    info_label.setWordWrap(True)
    vbox.addWidget(info_label)
    vbox.addSpacing(20)
    g = QGridLayout()
    l1 = QLabel(tr.sync_ankiweb_id_label())
    g.addWidget(l1, 0, 0)
    user = QLineEdit()
    user.setText(username)
    g.addWidget(user, 0, 1)
    l2 = QLabel(tr.sync_password_label())
    g.addWidget(l2, 1, 0)
    passwd = QLineEdit()
    passwd.setText(password)
    passwd.setEchoMode(QLineEdit.EchoMode.Password)
    g.addWidget(passwd, 1, 1)
    vbox.addLayout(g)
    bb = QDialogButtonBox(
        QDialogButtonBox.StandardButton.Ok
        | QDialogButtonBox.StandardButton.Cancel)  # type: ignore
    bb.button(QDialogButtonBox.StandardButton.Ok).setAutoDefault(True)
    qconnect(bb.accepted, diag.accept)
    qconnect(bb.rejected, diag.reject)
    vbox.addWidget(bb)
    diag.setLayout(vbox)
    diag.show()

    accepted = diag.exec()
    if not accepted:
        return ("", "")
    return (user.text().strip(), passwd.text())
Esempio n. 14
0
 def onAdvanced(self) -> None:
     nt = self.current_notetype()
     d = QDialog(self)
     disable_help_button(d)
     frm = aqt.forms.modelopts.Ui_Dialog()
     frm.setupUi(d)
     frm.latexsvg.setChecked(nt.get("latexsvg", False))
     frm.latexHeader.setText(nt["latexPre"])
     frm.latexFooter.setText(nt["latexPost"])
     d.setWindowTitle(
         without_unicode_isolation(tr.actions_options_for(val=nt["name"])))
     qconnect(frm.buttonBox.helpRequested, lambda: openHelp(HelpPage.LATEX))
     restoreGeom(d, "modelopts")
     gui_hooks.models_advanced_will_show(d)
     d.exec_()
     saveGeom(d, "modelopts")
     nt["latexsvg"] = frm.latexsvg.isChecked()
     nt["latexPre"] = str(frm.latexHeader.toPlainText())
     nt["latexPost"] = str(frm.latexFooter.toPlainText())
     self.saveAndRefresh(nt)
Esempio n. 15
0
 def __init__(self, dlg, addon, conf) -> None:
     super().__init__(dlg)
     self.addon = addon
     self.conf = conf
     self.mgr = dlg.mgr
     self.form = aqt.forms.addonconf.Ui_Dialog()
     self.form.setupUi(self)
     restore = self.form.buttonBox.button(QDialogButtonBox.RestoreDefaults)
     qconnect(restore.clicked, self.onRestoreDefaults)
     self.setupFonts()
     self.updateHelp()
     self.updateText(self.conf)
     restoreGeom(self, "addonconf")
     restoreSplitter(self.form.splitter, "addonconf")
     self.setWindowTitle(
         without_unicode_isolation(
             tr(
                 TR.ADDONS_CONFIG_WINDOW_TITLE,
                 name=self.mgr.addon_meta(addon).human_name(),
             )))
     self.show()
Esempio n. 16
0
    def __init__(self, mw, first=False, search="", deck=None):
        QDialog.__init__(self, mw)
        self.mw = mw
        self.deck = deck or self.mw.col.decks.current()
        self.search = search
        self.form = aqt.forms.dyndconf.Ui_Dialog()
        self.form.setupUi(self)
        if first:
            label = tr(TR.DECKS_BUILD)
        else:
            label = tr(TR.ACTIONS_REBUILD)
        self.ok = self.form.buttonBox.addButton(label,
                                                QDialogButtonBox.AcceptRole)
        self.mw.checkpoint(tr(TR.ACTIONS_OPTIONS))
        disable_help_button(self)
        self.setWindowModality(Qt.WindowModal)
        qconnect(self.form.buttonBox.helpRequested,
                 lambda: openHelp(HelpPage.FILTERED_DECK))
        self.setWindowTitle(
            without_unicode_isolation(
                tr(TR.ACTIONS_OPTIONS_FOR, val=self.deck["name"])))
        restoreGeom(self, "dyndeckconf")
        self.initialSetup()
        self.loadConf()
        if search:
            search = self.mw.col.build_search_string(
                search, SearchTerm(card_state=SearchTerm.CARD_STATE_DUE))
            self.form.search.setText(search)
            search_2 = self.mw.col.build_search_string(
                search, SearchTerm(card_state=SearchTerm.CARD_STATE_NEW))
            self.form.search_2.setText(search_2)
        self.form.search.selectAll()

        if self.mw.col.schedVer() == 1:
            self.form.secondFilter.setVisible(False)

        self.show()
        self.exec_()
        saveGeom(self, "dyndeckconf")
Esempio n. 17
0
    def _setup_ui(self) -> None:
        self.setWindowModality(Qt.ApplicationModal)
        self.mw.garbage_collect_on_dialog_finish(self)
        self.setMinimumWidth(400)
        disable_help_button(self)
        restoreGeom(self, self.TITLE)
        addCloseShortcut(self)

        self.web = AnkiWebView(title=self.TITLE)
        self.web.setVisible(False)
        self.web.load_ts_page("deckoptions")
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.web)
        self.setLayout(layout)

        self.web.eval(f"""const $deckOptions = anki.deckOptions(
            document.getElementById('main'), {self._deck["id"]});""")
        self.setWindowTitle(
            without_unicode_isolation(
                tr.actions_options_for(val=self._deck["name"])))
        gui_hooks.deck_options_did_load(self)
Esempio n. 18
0
 def onAdvanced(self) -> None:
     nt = self.current_notetype()
     d = QDialog(self)
     d.setWindowFlags(self.windowFlags()
                      & ~Qt.WindowContextHelpButtonHint)  # type: ignore
     frm = aqt.forms.modelopts.Ui_Dialog()
     frm.setupUi(d)
     frm.latexsvg.setChecked(nt.get("latexsvg", False))
     frm.latexHeader.setText(nt["latexPre"])
     frm.latexFooter.setText(nt["latexPost"])
     d.setWindowTitle(
         without_unicode_isolation(
             tr(TR.ACTIONS_OPTIONS_FOR, val=nt["name"])))
     qconnect(frm.buttonBox.helpRequested,
              lambda: openHelp("math?id=latex"))
     restoreGeom(d, "modelopts")
     gui_hooks.models_advanced_will_show(d)
     d.exec_()
     saveGeom(d, "modelopts")
     nt["latexsvg"] = frm.latexsvg.isChecked()
     nt["latexPre"] = str(frm.latexHeader.toPlainText())
     nt["latexPost"] = str(frm.latexFooter.toPlainText())
     self.saveAndRefresh(nt)
Esempio n. 19
0
def test_nextIvl():
    col = getEmptyCol()
    note = col.newNote()
    note["Front"] = "one"
    note["Back"] = "two"
    col.addNote(note)
    col.reset()
    conf = col.decks.config_dict_for_deck_id(1)
    conf["new"]["delays"] = [0.5, 3, 10]
    conf["lapse"]["delays"] = [1, 5, 9]
    col.decks.save(conf)
    c = col.sched.getCard()
    # new cards
    ##################################################
    ni = col.sched.nextIvl
    assert ni(c, 1) == 30
    assert ni(c, 2) == (30 + 180) // 2
    assert ni(c, 3) == 180
    assert ni(c, 4) == 4 * 86400
    col.sched.answerCard(c, 1)
    # cards in learning
    ##################################################
    assert ni(c, 1) == 30
    assert ni(c, 2) == (30 + 180) // 2
    assert ni(c, 3) == 180
    assert ni(c, 4) == 4 * 86400
    col.sched.answerCard(c, 3)
    assert ni(c, 1) == 30
    assert ni(c, 2) == (180 + 600) // 2
    assert ni(c, 3) == 600
    assert ni(c, 4) == 4 * 86400
    col.sched.answerCard(c, 3)
    # normal graduation is tomorrow
    assert ni(c, 3) == 1 * 86400
    assert ni(c, 4) == 4 * 86400
    # lapsed cards
    ##################################################
    c.type = CARD_TYPE_RELEARNING
    c.ivl = 100
    c.factor = STARTING_FACTOR
    c.flush()
    assert ni(c, 1) == 60
    assert ni(c, 3) == 100 * 86400
    assert ni(c, 4) == 101 * 86400
    # review cards
    ##################################################
    c.type = CARD_TYPE_REV
    c.queue = QUEUE_TYPE_REV
    c.ivl = 100
    c.factor = STARTING_FACTOR
    c.flush()
    # failing it should put it at 60s
    assert ni(c, 1) == 60
    # or 1 day if relearn is false
    conf["lapse"]["delays"] = []
    col.decks.save(conf)
    assert ni(c, 1) == 1 * 86400
    # (* 100 1.2 86400)10368000.0
    assert ni(c, 2) == 10368000
    # (* 100 2.5 86400)21600000.0
    assert ni(c, 3) == 21600000
    # (* 100 2.5 1.3 86400)28080000.0
    assert ni(c, 4) == 28080000
    assert without_unicode_isolation(col.sched.nextIvlStr(c, 4)) == "10.8mo"
Esempio n. 20
0
File: about.py Progetto: RumovZ/anki
def show(mw: aqt.AnkiQt) -> QDialog:
    dialog = ClosableQDialog(mw)
    disable_help_button(dialog)
    mw.garbage_collect_on_dialog_finish(dialog)
    abt = aqt.forms.about.Ui_About()
    abt.setupUi(dialog)

    # Copy debug info
    ######################################################################

    def addon_fmt(addmgr: AddonManager, addon: AddonMeta) -> str:
        if addon.installed_at:
            installed = time.strftime(
                "%Y-%m-%dT%H:%M", time.localtime(addon.installed_at)
            )
        else:
            installed = "0"
        if addon.provided_name:
            name = addon.provided_name
        else:
            name = "''"
        user = addmgr.getConfig(addon.dir_name)
        default = addmgr.addonConfigDefaults(addon.dir_name)
        if user == default:
            modified = "''"
        else:
            modified = "mod"
        return f"{name} ['{addon.dir_name}', {installed}, '{addon.human_version}', {modified}]"

    def onCopy() -> None:
        addmgr = mw.addonManager
        active = []
        activeids = []
        inactive = []
        for addon in addmgr.all_addon_meta():
            if addon.enabled:
                active.append(addon_fmt(addmgr, addon))
                if addon.ankiweb_id():
                    activeids.append(addon.dir_name)
            else:
                inactive.append(addon_fmt(addmgr, addon))
        newline = "\n"
        info = f"""
{supportText()}

===Add-ons (active)===
(add-on provided name [Add-on folder, installed at, version, is config changed])
{newline.join(sorted(active))}

===IDs of active AnkiWeb add-ons===
{" ".join(activeids)}

===Add-ons (inactive)===
(add-on provided name [Add-on folder, installed at, version, is config changed])
{newline.join(sorted(inactive))}
"""
        info = f"    {'    '.join(info.splitlines(True))}"
        QApplication.clipboard().setText(info)
        tooltip(tr.about_copied_to_clipboard(), parent=dialog)

    btn = QPushButton(tr.about_copy_debug_info())
    qconnect(btn.clicked, onCopy)
    abt.buttonBox.addButton(btn, QDialogButtonBox.ButtonRole.ActionRole)
    abt.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setFocus()

    # WebView contents
    ######################################################################
    abouttext = "<center><img src='/_anki/imgs/anki-logo-thin.png'></center>"
    abouttext += f"<p>{tr.about_anki_is_a_friendly_intelligent_spaced()}"
    abouttext += f"<p>{tr.about_anki_is_licensed_under_the_agpl3()}"
    abouttext += f"<p>{tr.about_version(val=versionWithBuild())}<br>"
    abouttext += ("Python %s Qt %s PyQt %s<br>") % (
        platform.python_version(),
        QT_VERSION_STR,
        PYQT_VERSION_STR,
    )
    abouttext += (
        without_unicode_isolation(tr.about_visit_website(val=aqt.appWebsite))
        + "</span>"
    )

    # automatically sorted; add new lines at the end
    allusers = sorted(
        (
            "Aaron Harsh",
            "Alex Fraser",
            "Andreas Klauer",
            "Andrew Wright",
            "Aristotelis P.",
            "Bernhard Ibertsberger",
            "C. van Rooyen",
            "Charlene Barina",
            "Christian Krause",
            "Christian Rusche",
            "Dave Druelinger",
            "David Smith",
            "Dmitry Mikheev",
            "Dotan Cohen",
            "Emilio Wuerges",
            "Emmanuel Jarri",
            "Frank Harper",
            "Gregor Skumavc",
            "Guillem Palau Salvà",
            "H. Mijail",
            "Henrik Enggaard Hansen",
            "Houssam Salem",
            "Ian Lewis",
            "Immanuel Asmus",
            "Iroiro",
            "Jarvik7",
            "Jin Eun-Deok",
            "Jo Nakashima",
            "Johanna Lindh",
            "Joseph Lorimer",
            "Julien Baley",
            "Jussi Määttä",
            "Kieran Clancy",
            "LaC",
            "Laurent Steffan",
            "Luca Ban",
            "Luciano Esposito",
            "Marco Giancotti",
            "Marcus Rubeus",
            "Mari Egami",
            "Mark Wilbur",
            "Matthew Duggan",
            "Matthew Holtz",
            "Meelis Vasser",
            "Michael Jürges",
            "Michael Keppler",
            "Michael Montague",
            "Michael Penkov",
            "Michal Čadil",
            "Morteza Salehi",
            "Nathanael Law",
            "Nguyễn Hào Khôi",
            "Nick Cook",
            "Niklas Laxström",
            "Norbert Nagold",
            "Ole Guldberg",
            "Pcsl88",
            "Petr Michalec",
            "Piotr Kubowicz",
            "Richard Colley",
            "Roland Sieker",
            "Samson Melamed",
            "Silja Ijas",
            "Snezana Lukic",
            "Soren Bjornstad",
            "Stefaan De Pooter",
            "Susanna Björverud",
            "Sylvain Durand",
            "Tacutu",
            "Timm Preetz",
            "Timo Paulssen",
            "Ursus",
            "Victor Suba",
            "Volker Jansen",
            "Volodymyr Goncharenko",
            "Xtru",
            "Ádám Szegi",
            "赵金鹏",
            "黃文龍",
            "David Bailey",
            "Arman High",
            "Arthur Milchior",
            "Rai (Michael Pokorny)",
            "AMBOSS MD Inc.",
            "Erez Volk",
            "Tobias Predel",
            "Thomas Kahn",
            "zjosua",
            "Ijgnd",
            "Evandro Coan",
            "Alan Du",
            "ANH",
            "Junseo Park",
            "Gustavo Costa",
            "余时行",
            "叶峻峣",
            "RumovZ",
            "学习骇客",
            "ready-research",
        )
    )

    abouttext += "<p>" + tr.about_written_by_damien_elmes_with_patches(
        cont=", ".join(allusers)
    )
    abouttext += f"<p>{tr.about_if_you_have_contributed_and_are()}"
    abouttext += f"<p>{tr.about_a_big_thanks_to_all_the()}"
    abt.label.setMinimumWidth(800)
    abt.label.setMinimumHeight(600)
    dialog.show()
    abt.label.stdHtml(abouttext, js=[])
    return dialog
Esempio n. 21
0
    def __init__(
        self,
        mw: AnkiQt,
        search: Optional[str] = None,
        search_2: Optional[str] = None,
        deck: Optional[Deck] = None,
    ) -> None:
        """If 'deck' is an existing filtered deck, load and modify its settings.
        Otherwise, build a new one and derive settings from the current deck.
        """

        QDialog.__init__(self, mw)
        self.mw = mw
        self.col = self.mw.col
        self.did: Optional[int] = None
        self.form = aqt.forms.dyndconf.Ui_Dialog()
        self.form.setupUi(self)
        self.mw.checkpoint(tr(TR.ACTIONS_OPTIONS))
        self.initialSetup()
        self.old_deck = self.col.decks.current()

        if deck and deck["dyn"]:
            # modify existing dyn deck
            label = tr(TR.ACTIONS_REBUILD)
            self.deck = deck
            self.loadConf()
        elif self.old_deck["dyn"]:
            # create new dyn deck from other dyn deck
            label = tr(TR.DECKS_BUILD)
            self.loadConf(deck=self.old_deck)
            self.new_dyn_deck()
        else:
            # create new dyn deck from regular deck
            label = tr(TR.DECKS_BUILD)
            self.new_dyn_deck()
            self.loadConf()
            self.set_default_searches(self.old_deck["name"])

        self.form.name.setText(self.deck["name"])
        self.form.name.setPlaceholderText(self.deck["name"])
        self.set_custom_searches(search, search_2)
        qconnect(self.form.search_button.clicked, self.on_search_button)
        qconnect(self.form.search_button_2.clicked, self.on_search_button_2)
        qconnect(self.form.hint_button.clicked, self.on_hint_button)
        blue = theme_manager.color(colors.LINK)
        grey = theme_manager.color(colors.DISABLED)
        self.setStyleSheet(f"""QPushButton[label] {{ padding: 0; border: 0 }}
            QPushButton[label]:hover {{ text-decoration: underline }}
            QPushButton[label="search"] {{ color: {blue} }}
            QPushButton[label="hint"] {{ color: {grey} }}""")
        disable_help_button(self)
        self.setWindowModality(Qt.WindowModal)
        qconnect(self.form.buttonBox.helpRequested,
                 lambda: openHelp(HelpPage.FILTERED_DECK))
        self.setWindowTitle(
            without_unicode_isolation(
                tr(TR.ACTIONS_OPTIONS_FOR, val=self.deck["name"])))
        self.form.buttonBox.button(QDialogButtonBox.Ok).setText(label)
        if self.col.schedVer() == 1:
            self.form.secondFilter.setVisible(False)
        restoreGeom(self, "dyndeckconf")

        self.show()
Esempio n. 22
0
def show(mw):
    dialog = ClosableQDialog(mw)
    dialog.setWindowFlags(self.windowFlags()
                          & ~Qt.WindowContextHelpButtonHint)  # type: ignore
    mw.setupDialogGC(dialog)
    abt = aqt.forms.about.Ui_About()
    abt.setupUi(dialog)

    # Copy debug info
    ######################################################################

    def addon_fmt(addmgr: AddonManager, addon: AddonMeta) -> str:
        if addon.installed_at:
            installed = time.strftime("%Y-%m-%dT%H:%M",
                                      time.localtime(addon.installed_at))
        else:
            installed = "0"
        if addon.provided_name:
            name = addon.provided_name
        else:
            name = "''"
        user = addmgr.getConfig(addon.dir_name)
        default = addmgr.addonConfigDefaults(addon.dir_name)
        if user == default:
            modified = "''"
        else:
            modified = "mod"
        return f"{name} ['{addon.dir_name}', {installed}, '{addon.human_version}', {modified}]"

    def onCopy():
        addmgr = mw.addonManager
        active = []
        activeids = []
        inactive = []
        for addon in addmgr.all_addon_meta():
            if addon.enabled:
                active.append(addon_fmt(addmgr, addon))
                if addon.ankiweb_id():
                    activeids.append(addon.dir_name)
            else:
                inactive.append(addon_fmt(addmgr, addon))
        newline = "\n"
        info = f"""
{supportText()}

===Add-ons (active)===
(add-on provided name [Add-on folder, installed at, version, is config changed])
{newline.join(sorted(active))}

===IDs of active AnkiWeb add-ons===
{" ".join(activeids)}

===Add-ons (inactive)===
(add-on provided name [Add-on folder, installed at, version, is config changed])
{newline.join(sorted(inactive))}
"""
        info = "    " + "    ".join(info.splitlines(True))
        QApplication.clipboard().setText(info)
        tooltip(tr(TR.ABOUT_COPIED_TO_CLIPBOARD), parent=dialog)

    btn = QPushButton(tr(TR.ABOUT_COPY_DEBUG_INFO))
    qconnect(btn.clicked, onCopy)
    abt.buttonBox.addButton(btn, QDialogButtonBox.ActionRole)
    abt.buttonBox.button(QDialogButtonBox.Ok).setFocus()

    # WebView contents
    ######################################################################
    abouttext = "<center><img src='/_anki/imgs/anki-logo-thin.png'></center>"
    abouttext += "<p>" + tr(TR.ABOUT_ANKI_IS_A_FRIENDLY_INTELLIGENT_SPACED)
    abouttext += "<p>" + tr(TR.ABOUT_ANKI_IS_LICENSED_UNDER_THE_AGPL3)
    abouttext += "<p>" + tr(TR.ABOUT_VERSION, val=versionWithBuild()) + "<br>"
    abouttext += ("Python %s Qt %s PyQt %s<br>") % (
        platform.python_version(),
        QT_VERSION_STR,
        PYQT_VERSION_STR,
    )
    abouttext += (without_unicode_isolation(
        tr(TR.ABOUT_VISIT_WEBSITE, val=aqt.appWebsite)) + "</span>")

    # automatically sorted; add new lines at the end
    allusers = sorted((
        "Aaron Harsh",
        "Alex Fraser",
        "Andreas Klauer",
        "Andrew Wright",
        "Aristotelis P.",
        "Bernhard Ibertsberger",
        "C. van Rooyen",
        "Charlene Barina",
        "Christian Krause",
        "Christian Rusche",
        "Dave Druelinger",
        "David Smith",
        "Dmitry Mikheev",
        "Dotan Cohen",
        "Emilio Wuerges",
        "Emmanuel Jarri",
        "Frank Harper",
        "Gregor Skumavc",
        "Guillem Palau Salvà",
        "H. Mijail",
        "Henrik Enggaard Hansen",
        "Houssam Salem",
        "Ian Lewis",
        "Immanuel Asmus",
        "Iroiro",
        "Jarvik7",
        "Jin Eun-Deok",
        "Jo Nakashima",
        "Johanna Lindh",
        "Joseph Lorimer",
        "Julien Baley",
        "Jussi Määttä",
        "Kieran Clancy",
        "LaC",
        "Laurent Steffan",
        "Luca Ban",
        "Luciano Esposito",
        "Marco Giancotti",
        "Marcus Rubeus",
        "Mari Egami",
        "Mark Wilbur",
        "Matthew Duggan",
        "Matthew Holtz",
        "Meelis Vasser",
        "Michael Jürges",
        "Michael Keppler",
        "Michael Montague",
        "Michael Penkov",
        "Michal Čadil",
        "Morteza Salehi",
        "Nathanael Law",
        "Nguyễn Hào Khôi",
        "Nick Cook",
        "Niklas Laxström",
        "Norbert Nagold",
        "Ole Guldberg",
        "Pcsl88",
        "Petr Michalec",
        "Piotr Kubowicz",
        "Richard Colley",
        "Roland Sieker",
        "Samson Melamed",
        "Silja Ijas",
        "Snezana Lukic",
        "Soren Bjornstad",
        "Stefaan De Pooter",
        "Susanna Björverud",
        "Sylvain Durand",
        "Tacutu",
        "Timm Preetz",
        "Timo Paulssen",
        "Ursus",
        "Victor Suba",
        "Volker Jansen",
        "Volodymyr Goncharenko",
        "Xtru",
        "Ádám Szegi",
        "赵金鹏",
        "黃文龍",
        "David Bailey",
        "Arman High",
        "Arthur Milchior",
        "Rai (Michael Pokorny)",
        "AMBOSS MD Inc.",
        "Erez Volk",
        "Tobias Predel",
        "Thomas Kahn",
        "zjosua",
        "Ijgnd",
        "Evandro Coan",
        "Alan Du",
        "ANH",
        "Junseo Park",
        "Gustavo Costa",
        "余时行",
        "叶峻峣",
    ))

    abouttext += "<p>" + tr(TR.ABOUT_WRITTEN_BY_DAMIEN_ELMES_WITH_PATCHES,
                            cont=", ".join(allusers))
    abouttext += "<p>" + tr(TR.ABOUT_IF_YOU_HAVE_CONTRIBUTED_AND_ARE)
    abouttext += "<p>" + tr(TR.ABOUT_A_BIG_THANKS_TO_ALL_THE)
    abt.label.setMinimumWidth(800)
    abt.label.setMinimumHeight(600)
    dialog.show()
    abt.label.stdHtml(abouttext, js=[])
    return dialog
Esempio n. 23
0
def test_latex():
    col = getEmptyCol()
    # change latex cmd to simulate broken build
    import anki.latex

    anki.latex.pngCommands[0][0] = "nolatex"
    # add a note with latex
    note = col.newNote()
    note["Front"] = "[latex]hello[/latex]"
    col.addNote(note)
    # but since latex couldn't run, there's nothing there
    assert len(os.listdir(col.media.dir())) == 0
    # check the error message
    msg = note.cards()[0].q()
    assert "executing nolatex" in without_unicode_isolation(msg)
    assert "installed" in msg
    # check if we have latex installed, and abort test if we don't
    if not shutil.which("latex") or not shutil.which("dvipng"):
        print("aborting test; latex or dvipng is not installed")
        return
    # fix path
    anki.latex.pngCommands[0][0] = "latex"
    # check media db should cause latex to be generated
    col.media.render_all_latex()
    assert len(os.listdir(col.media.dir())) == 1
    assert ".png" in note.cards()[0].q()
    # adding new notes should cause generation on question display
    note = col.newNote()
    note["Front"] = "[latex]world[/latex]"
    col.addNote(note)
    note.cards()[0].q()
    assert len(os.listdir(col.media.dir())) == 2
    # another note with the same media should reuse
    note = col.newNote()
    note["Front"] = " [latex]world[/latex]"
    col.addNote(note)
    assert len(os.listdir(col.media.dir())) == 2
    oldcard = note.cards()[0]
    assert ".png" in oldcard.q()
    # if we turn off building, then previous cards should work, but cards with
    # missing media will show a broken image
    anki.latex.build = False
    note = col.newNote()
    note["Front"] = "[latex]foo[/latex]"
    col.addNote(note)
    assert len(os.listdir(col.media.dir())) == 2
    assert ".png" in oldcard.q()
    # turn it on again so other test don't suffer
    anki.latex.build = True

    # bad commands
    (result, msg) = _test_includes_bad_command("\\write18")
    assert result, msg
    (result, msg) = _test_includes_bad_command("\\readline")
    assert result, msg
    (result, msg) = _test_includes_bad_command("\\input")
    assert result, msg
    (result, msg) = _test_includes_bad_command("\\include")
    assert result, msg
    (result, msg) = _test_includes_bad_command("\\catcode")
    assert result, msg
    (result, msg) = _test_includes_bad_command("\\openout")
    assert result, msg
    (result, msg) = _test_includes_bad_command("\\write")
    assert result, msg
    (result, msg) = _test_includes_bad_command("\\loop")
    assert result, msg
    (result, msg) = _test_includes_bad_command("\\def")
    assert result, msg
    (result, msg) = _test_includes_bad_command("\\shipout")
    assert result, msg

    # inserting commands beginning with a bad name should not raise an error
    (result, msg) = _test_includes_bad_command("\\defeq")
    assert not result, msg
    # normal commands should not either
    (result, msg) = _test_includes_bad_command("\\emph")
    assert not result, msg