diff options
Diffstat (limited to '.local/share/Anki2/addons21/Background_and_gear/gui_updatemanager.py')
-rw-r--r-- | .local/share/Anki2/addons21/Background_and_gear/gui_updatemanager.py | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/.local/share/Anki2/addons21/Background_and_gear/gui_updatemanager.py b/.local/share/Anki2/addons21/Background_and_gear/gui_updatemanager.py new file mode 100644 index 0000000..1b55e56 --- /dev/null +++ b/.local/share/Anki2/addons21/Background_and_gear/gui_updatemanager.py @@ -0,0 +1,432 @@ +# -*- coding: utf-8 -*- +# Copyright: Lovac42 (much of this card heavily borrowed from the Dancing Baloney Add-on) +# Copyright: The AnKing +# Also thanks to ijgnord who helped on this +# Support: +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + + +import os + +from anki import version as anki_version # type: ignore +from aqt import mw +from aqt.qt import * +from aqt.utils import getFile, openFolder, openLink + +from .config import addon_path, getDefaultConfig, getUserOption, writeConfig +from .gui.forms import settings_dialog + +conf = getUserOption() + +USER_FILES_FOLDER = os.path.join(addon_path, "user_files") +RE_BG_IMG_EXT = "*.gif *.png *.apng *.jpg *.jpeg *.svg *.ico *.bmp" +ANKI_VERSION_TUPLE = tuple(int(i) for i in anki_version.split(".")) + + +def getMenu(parent, menuName): + menu = None + for a in parent.form.menubar.actions(): + if menuName == a.text(): + menu = a.menu() + break + if not menu: + menu = parent.form.menubar.addMenu(menuName) + return menu + + +class SettingsDialog(QDialog): + + def __init__(self, parent): + super().__init__(mw) + + mw.setupDialogGC(self) + self.mw = mw + self.parent = parent + self.setupDialog() + self.loadConfigData() + self.setupConnections() + + self.exec() + + def reject(self): + self.accept() + self.close() + + def accept(self): + QDialog.accept(self) + self.close() + + def setupDialog(self): + self.form = settings_dialog.Ui_Dialog() + self.form.setupUi(self) + + def setupConnections(self): + f = self.form + + # PushButtons ------------- + f.OkButton.clicked.connect(self.accept) + f.RestoreButton.clicked.connect(self.resetConfig) + + f.pushButton_randomize.clicked.connect(self.random) + f.pushButton_imageFolder.clicked.connect(lambda: openFolder(USER_FILES_FOLDER)) + f.pushButton_videoTutorial.clicked.connect(lambda _: self.openWeb("video")) + + f.toolButton_website.clicked.connect(lambda _: self.openWeb("anking")) + f.toolButton_youtube.clicked.connect(lambda _: self.openWeb("youtube")) + f.toolButton_patreon.clicked.connect(lambda _: self.openWeb("patreon")) + f.toolButton_instagram.clicked.connect(lambda _: self.openWeb("instagram")) + f.toolButton_facebook.clicked.connect(lambda _: self.openWeb("facebook")) + f.toolButton_palace.clicked.connect(lambda _: self.openWeb("palace")) + + # Color Pickers ------------- + controller = { + f.toolButton_color_main: (f.lineEdit_color_main,), + f.toolButton_color_top: (f.lineEdit_color_top,), + f.toolButton_color_bottom: (f.lineEdit_color_bottom,), + } + for btn, args in controller.items(): + btn.clicked.connect(lambda a="a", args=args: self.getColors(a, *args)) + + # File Buttons ----------------------- + controller = { + # Image Buttons ----------------------- + f.toolButton_background: (f.lineEdit_background,), + } + for btn, args in controller.items(): + # 'a' is used to get around an issue + # with pything binding + btn.clicked.connect(lambda a="a", args=args: self._getFile(a, *args)) + # File Buttons ----------------------- + controller = { + # Image Buttons ----------------------- + f.toolButton_gear: (f.lineEdit_gear,), + } + for btn, args in controller.items(): + # 'a' is used to get around an issue + # with pything binding + btn.clicked.connect(lambda a="a", args=args: self._getGearFile(a, *args)) + + # Checkboxes ---------------- + controller = { + f.checkBox_reviewer: ("Reviewer image",), + f.checkBox_toolbar: ("Toolbar image",), + f.checkBox_topbottom: ("Toolbar top/bottom",), + } + for cb, args in controller.items(): + cb.stateChanged.connect( + lambda cb=cb, args=args: self._updateCheckbox(cb, *args) + ) + + # Comboboxes --------------- + controller = { + f.comboBox_attachment: ("background-attachment",), + f.comboBox_position: ("background-position",), + f.comboBox_size: ("background-size",), + } + for cb, args in controller.items(): + t = cb.currentText() + cb.currentTextChanged.connect( + lambda t=t, args=args: self._updateComboBox(t, *args) + ) + + # Sliders -------------- + controller = { + f.Slider_main: ("background opacity main",), + f.Slider_review: ("background opacity review",), + } + for slider, args in controller.items(): + s = slider.value() + slider.valueChanged.connect( + lambda s=s, args=args: self._updateSliderLabel(s, *args) + ) + + # QDoubleSpinBox ------------ + f.scaleBox.valueChanged.connect(self._updateSpinBox) + + # LineEdits ------------- + a = f.lineEdit_background + t = a.text() + a.textChanged.connect( + lambda t=a.text(): self._updateLineEdit(t, "Image name for background") + ) + + a = f.lineEdit_gear + t = a.text() + a.textChanged.connect( + lambda t=a.text(): self._updateLineEdit(t, "Image name for gear") + ) + + a = f.lineEdit_color_main + t = a.text() + a.textChanged.connect( + lambda t=a.text(): self._updateLineEdit(t, "background-color main") + ) + + a = f.lineEdit_color_top + t = a.text() + a.textChanged.connect( + lambda t=a.text(): self._updateLineEdit(t, "background-color top") + ) + + a = f.lineEdit_color_bottom + t = a.text() + a.textChanged.connect( + lambda t=a.text(): self._updateLineEdit(t, "background-color bottom") + ) + + def loadConfigData(self): + f = self.form + + # Checkboxes ------------- + c = conf["Reviewer image"] + if f.checkBox_reviewer.isChecked() != c: + f.checkBox_reviewer.click() + + c = conf["Toolbar image"] + if f.checkBox_toolbar.isChecked() != c: + f.checkBox_toolbar.click() + + c = conf["Toolbar top/bottom"] + if f.checkBox_topbottom.isChecked() != c: + f.checkBox_topbottom.click() + + # Comboboxes ------------- + c = conf["background-attachment"] + f.comboBox_attachment.setCurrentText(c) + + c = conf["background-position"] + f.comboBox_position.setCurrentText(c) + + c = conf["background-size"] + f.comboBox_size.setCurrentText(c) + + # Sliders -------------- + c = float(conf["background opacity main"]) + f.Slider_main.setValue(int(c * 100)) + + c = float(conf["background opacity review"]) + f.Slider_review.setValue(int(c * 100)) + + # QDoubleSpinBox ------------------ + c = float(conf["background scale"]) + f.scaleBox.setValue(c) + + # LineEdits ------------- + t = conf["Image name for background"] + f.lineEdit_background.setText(t) + + t = conf["Image name for gear"] + f.lineEdit_gear.setText(t) + + t = conf["background-color main"] + f.lineEdit_color_main.setText(t) + + t = conf["background-color top"] + f.lineEdit_color_top.setText(t) + + t = conf["background-color bottom"] + f.lineEdit_color_bottom.setText(t) + + def _getFile(self, pad, lineEditor, ext=RE_BG_IMG_EXT): + def setWallpaper(path): + f = path.split("user_files/background/")[-1] + lineEditor.setText(f) + + f = getFile( + mw, + "Wallpaper", + cb=setWallpaper, + filter=ext, + dir=f"{addon_path}/user_files/background", + ) + + def _getGearFile(self, pad, lineEditor, ext=RE_BG_IMG_EXT): + def setWallpaper(path): + f = path.split("user_files/gear/")[-1] + lineEditor.setText(f) + + f = getFile( + mw, + "Gear icon", + cb=setWallpaper, + filter=ext, + dir=f"{addon_path}/user_files/gear", + ) + + def _updateCheckbox(self, cb, key): + n = -1 if cb == 2 else 1 + v = True if n == -1 else False + conf[key] = v + writeConfig(conf) + self._refresh() + + def _updateComboBox(self, text, key): + conf[key] = text + writeConfig(conf) + self._refresh() + + def _updateSliderLabel(self, val, key): + conf[key] = str(round(val / 100, 2)) + writeConfig(conf) + self._refresh() + + def _updateSpinBox(self): + f = self.form + n = round(f.scaleBox.value(), 2) + conf["background scale"] = str(n) + writeConfig(conf) + self._refresh() + + def _updateLineEdit(self, text, key): + conf[key] = text + writeConfig(conf) + self._refresh() + + def getColors(self, pad, lineEditor): + qcolor = QColorDialog.getColor() + if not qcolor.isValid(): + return + color = qcolor.name() + lineEditor.setText(color) + + def openWeb(self, site): + if site == "anking": + openLink("https://www.ankingmed.com") + elif site == "youtube": + openLink("https://www.youtube.com/theanking") + elif site == "patreon": + openLink("https://www.patreon.com/ankingmed") + elif site == "instagram": + openLink("https://instagram.com/ankingmed") + elif site == "facebook": + openLink("https://facebook.com/ankingmed") + elif site == "video": + openLink("https://youtu.be/5XAq0KpU3Jc") + elif site == "palace": + openLink( + "https://courses.ankipalace.com/?utm_source=anking_bg_add-on&utm_medium=anki_add-on&utm_campaign=mastery_course" + ) + + def random(self): + f = self.form + f.lineEdit_background.setText("random") + f.lineEdit_gear.setText("random") + self._refresh() + + def resetConfig(self): + global conf + conf = getDefaultConfig() + writeConfig(conf) + self._refresh() + self.close() + SettingsDialogExecute() + + def _refresh(self): + if ANKI_VERSION_TUPLE < (2, 1, 27): + mw.reset(True) + elif ANKI_VERSION_TUPLE < (2, 1, 45): + mw.reset(True) + mw.toolbar.draw() + else: + # this triggers the css update hooked to state_did_change in __init__.py + mw.moveToState("deckBrowser") + + # works around stylesheets getting cached + cmd = """ + (function(){ + var links = document.getElementsByTagName("link"); + for (var cl in links) + { + var link = links[cl]; + if (link.rel === "stylesheet") + link.href += "?v=" + Date.now().toString() + } + })() + """ + mw.deckBrowser.web.eval(cmd) + mw.toolbar.web.eval(cmd) + mw.bottomWeb.eval(cmd) + + +def SettingsDialogExecute(): + SettingsDialog(mw) + + +mw.addonManager.setConfigAction(__name__, SettingsDialogExecute) + + +######################################## + + +def create_get_help_submenu(parent: QMenu) -> QMenu: + submenu_name = "Get Anki Help" + menu_options = [ + ( + "Online Mastery Course", + "https://courses.ankipalace.com/?utm_source=anking_bg_add-on&utm_medium=anki_add-on&utm_campaign=mastery_course", + ), + ("Daily Q and A Support", "https://www.ankipalace.com/memberships"), + ("1-on-1 Tutoring", "https://www.ankipalace.com/tutoring"), + ] + submenu = QMenu(submenu_name, parent) + for name, url in menu_options: + act = QAction(name, mw) + act.triggered.connect(lambda _, u=url: openLink(u)) # type: ignore + submenu.addAction(act) + return submenu + + +def maybe_add_get_help_submenu(menu: QMenu) -> None: + """Adds 'Get Anki Help' submenu in 'Anking' menu if needed. + + The submenu is added if: + - The submenu does not exist in menu + - The submenu is an outdated version - existing is deleted + + With versioning and anking_get_help property, + future version can rename, hide, or change contents in the submenu + """ + submenu_property = "anking_get_help" + submenu_ver = 2 + for act in menu.actions(): + if act.property(submenu_property) or act.text() == "Get Anki Help": + ver = act.property("version") + if ver and ver >= submenu_ver: + return + submenu = create_get_help_submenu(menu) + menu.insertMenu(act, submenu) + menu.removeAction(act) + new_act = submenu.menuAction() + new_act.setProperty(submenu_property, True) + new_act.setProperty("version", submenu_ver) + return + else: + submenu = create_get_help_submenu(menu) + menu.addMenu(submenu) + new_act = submenu.menuAction() + new_act.setProperty(submenu_property, True) + new_act.setProperty("version", submenu_ver) + + +def get_anking_menu() -> QMenu: + """Return AnKing menu. If it doesn't exist, create one. Make sure its submenus are up to date.""" + menu_name = "&AnKing" + menubar = mw.form.menubar + for a in menubar.actions(): + if menu_name == a.text(): + menu = a.menu() + break + else: + menu = menubar.addMenu(menu_name) + maybe_add_get_help_submenu(menu) + return menu + + +######################################## + + +def setupMenu(): + menu = get_anking_menu() + a = QAction("Custom Background and Gear Icon", mw) + a.triggered.connect(SettingsDialogExecute) + menu.addAction(a) |