52642d693b
I do not recall anyone reporting that it worked better than the Qt implementation for them, and the lack of recent wheels on PyPI is a pain. We can always add it back in the future if enough people come out of the woodwork to report they were using it.
297 lines
11 KiB
Python
297 lines
11 KiB
Python
# Copyright: Ankitects Pty Ltd and contributors
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
from typing import Any, cast
|
|
|
|
import anki.lang
|
|
import aqt
|
|
from anki.collection import OpChanges
|
|
from anki.consts import newCardSchedulingLabels
|
|
from aqt import AnkiQt
|
|
from aqt.operations.collection import set_preferences
|
|
from aqt.profiles import VideoDriver
|
|
from aqt.qt import *
|
|
from aqt.utils import HelpPage, disable_help_button, openHelp, showInfo, showWarning, tr
|
|
|
|
|
|
class Preferences(QDialog):
|
|
def __init__(self, mw: AnkiQt) -> None:
|
|
QDialog.__init__(self, mw, Qt.WindowType.Window)
|
|
self.mw = mw
|
|
self.prof = self.mw.pm.profile
|
|
self.form = aqt.forms.preferences.Ui_Preferences()
|
|
self.form.setupUi(self)
|
|
disable_help_button(self)
|
|
self.form.buttonBox.button(QDialogButtonBox.StandardButton.Help).setAutoDefault(
|
|
False
|
|
)
|
|
self.form.buttonBox.button(
|
|
QDialogButtonBox.StandardButton.Close
|
|
).setAutoDefault(False)
|
|
qconnect(
|
|
self.form.buttonBox.helpRequested, lambda: openHelp(HelpPage.PREFERENCES)
|
|
)
|
|
self.silentlyClose = True
|
|
self.setup_collection()
|
|
self.setup_profile()
|
|
self.setup_global()
|
|
self.show()
|
|
|
|
def accept(self) -> None:
|
|
# avoid exception if main window is already closed
|
|
if not self.mw.col:
|
|
return
|
|
|
|
def after_collection_update() -> None:
|
|
self.update_profile()
|
|
self.update_global()
|
|
self.mw.pm.save()
|
|
self.done(0)
|
|
aqt.dialogs.markClosed("Preferences")
|
|
|
|
self.update_collection(after_collection_update)
|
|
|
|
def reject(self) -> None:
|
|
self.accept()
|
|
|
|
# Preferences stored in the collection
|
|
######################################################################
|
|
|
|
def setup_collection(self) -> None:
|
|
self.prefs = self.mw.col.get_preferences()
|
|
|
|
form = self.form
|
|
|
|
scheduling = self.prefs.scheduling
|
|
|
|
version = scheduling.scheduler_version
|
|
form.dayLearnFirst.setVisible(version == 2)
|
|
form.legacy_timezone.setVisible(version >= 2)
|
|
form.newSpread.setVisible(version < 3)
|
|
form.sched2021.setVisible(version >= 2)
|
|
|
|
form.lrnCutoff.setValue(int(scheduling.learn_ahead_secs / 60.0))
|
|
form.newSpread.addItems(list(newCardSchedulingLabels(self.mw.col).values()))
|
|
form.newSpread.setCurrentIndex(scheduling.new_review_mix)
|
|
form.dayLearnFirst.setChecked(scheduling.day_learn_first)
|
|
form.dayOffset.setValue(scheduling.rollover)
|
|
form.legacy_timezone.setChecked(not scheduling.new_timezone)
|
|
form.sched2021.setChecked(version == 3)
|
|
|
|
reviewing = self.prefs.reviewing
|
|
form.timeLimit.setValue(int(reviewing.time_limit_secs / 60.0))
|
|
form.showEstimates.setChecked(reviewing.show_intervals_on_buttons)
|
|
form.showProgress.setChecked(reviewing.show_remaining_due_counts)
|
|
form.showPlayButtons.setChecked(not reviewing.hide_audio_play_buttons)
|
|
form.interrupt_audio.setChecked(reviewing.interrupt_audio_when_answering)
|
|
|
|
editing = self.prefs.editing
|
|
form.useCurrent.setCurrentIndex(
|
|
0 if editing.adding_defaults_to_current_deck else 1
|
|
)
|
|
form.paste_strips_formatting.setChecked(editing.paste_strips_formatting)
|
|
form.pastePNG.setChecked(editing.paste_images_as_png)
|
|
form.default_search_text.setText(editing.default_search_text)
|
|
|
|
def update_collection(self, on_done: Callable[[], None]) -> None:
|
|
form = self.form
|
|
|
|
scheduling = self.prefs.scheduling
|
|
scheduling.new_review_mix = cast(Any, form.newSpread.currentIndex())
|
|
scheduling.learn_ahead_secs = form.lrnCutoff.value() * 60
|
|
scheduling.day_learn_first = form.dayLearnFirst.isChecked()
|
|
scheduling.rollover = form.dayOffset.value()
|
|
scheduling.new_timezone = not form.legacy_timezone.isChecked()
|
|
|
|
reviewing = self.prefs.reviewing
|
|
reviewing.show_remaining_due_counts = form.showProgress.isChecked()
|
|
reviewing.show_intervals_on_buttons = form.showEstimates.isChecked()
|
|
reviewing.time_limit_secs = form.timeLimit.value() * 60
|
|
reviewing.hide_audio_play_buttons = not self.form.showPlayButtons.isChecked()
|
|
reviewing.interrupt_audio_when_answering = self.form.interrupt_audio.isChecked()
|
|
|
|
editing = self.prefs.editing
|
|
editing.adding_defaults_to_current_deck = not form.useCurrent.currentIndex()
|
|
editing.paste_images_as_png = self.form.pastePNG.isChecked()
|
|
editing.paste_strips_formatting = self.form.paste_strips_formatting.isChecked()
|
|
editing.default_search_text = self.form.default_search_text.text()
|
|
|
|
def after_prefs_update(changes: OpChanges) -> None:
|
|
self.mw.apply_collection_options()
|
|
if scheduling.scheduler_version > 1:
|
|
want_v3 = form.sched2021.isChecked()
|
|
if self.mw.col.v3_scheduler() != want_v3:
|
|
self.mw.col.set_v3_scheduler(want_v3)
|
|
|
|
on_done()
|
|
|
|
set_preferences(parent=self, preferences=self.prefs).success(
|
|
after_prefs_update
|
|
).run_in_background()
|
|
|
|
# Preferences stored in the profile
|
|
######################################################################
|
|
|
|
def setup_profile(self) -> None:
|
|
"Setup options stored in the user profile."
|
|
self.setup_network()
|
|
self.setup_backup()
|
|
|
|
def update_profile(self) -> None:
|
|
self.update_network()
|
|
self.update_backup()
|
|
|
|
# Profile: network
|
|
######################################################################
|
|
|
|
def setup_network(self) -> None:
|
|
self.form.media_log.setText(tr.sync_media_log_button())
|
|
qconnect(self.form.media_log.clicked, self.on_media_log)
|
|
self.form.syncOnProgramOpen.setChecked(self.prof["autoSync"])
|
|
self.form.syncMedia.setChecked(self.prof["syncMedia"])
|
|
self.form.autoSyncMedia.setChecked(self.mw.pm.auto_sync_media_minutes() != 0)
|
|
if not self.prof["syncKey"]:
|
|
self._hide_sync_auth_settings()
|
|
else:
|
|
self.form.syncUser.setText(self.prof.get("syncUser", ""))
|
|
qconnect(self.form.syncDeauth.clicked, self.sync_logout)
|
|
self.form.syncDeauth.setText(tr.sync_log_out_button())
|
|
|
|
def on_media_log(self) -> None:
|
|
self.mw.media_syncer.show_sync_log()
|
|
|
|
def _hide_sync_auth_settings(self) -> None:
|
|
self.form.syncDeauth.setVisible(False)
|
|
self.form.syncUser.setText("")
|
|
self.form.syncLabel.setText(
|
|
tr.preferences_synchronizationnot_currently_enabled_click_the_sync()
|
|
)
|
|
|
|
def sync_logout(self) -> None:
|
|
if self.mw.media_syncer.is_syncing():
|
|
showWarning("Can't log out while sync in progress.")
|
|
return
|
|
self.prof["syncKey"] = None
|
|
self.mw.col.media.force_resync()
|
|
self._hide_sync_auth_settings()
|
|
|
|
def update_network(self) -> None:
|
|
self.prof["autoSync"] = self.form.syncOnProgramOpen.isChecked()
|
|
self.prof["syncMedia"] = self.form.syncMedia.isChecked()
|
|
self.mw.pm.set_auto_sync_media_minutes(
|
|
self.form.autoSyncMedia.isChecked() and 15 or 0
|
|
)
|
|
if self.form.fullSync.isChecked():
|
|
self.mw.col.mod_schema(check=False)
|
|
|
|
# Profile: backup
|
|
######################################################################
|
|
|
|
def setup_backup(self) -> None:
|
|
self.form.numBackups.setValue(self.prof["numBackups"])
|
|
|
|
def update_backup(self) -> None:
|
|
self.prof["numBackups"] = self.form.numBackups.value()
|
|
|
|
# Global preferences
|
|
######################################################################
|
|
|
|
def setup_global(self) -> None:
|
|
"Setup options global to all profiles."
|
|
self.form.uiScale.setValue(int(self.mw.pm.uiScale() * 100))
|
|
self.form.nightMode.setChecked(self.mw.pm.night_mode())
|
|
|
|
self.setup_language()
|
|
self.setup_video_driver()
|
|
|
|
self.setupOptions()
|
|
|
|
def update_global(self) -> None:
|
|
restart_required = False
|
|
|
|
self.update_video_driver()
|
|
|
|
newScale = self.form.uiScale.value() / 100
|
|
if newScale != self.mw.pm.uiScale():
|
|
self.mw.pm.setUiScale(newScale)
|
|
restart_required = True
|
|
|
|
if self.mw.pm.night_mode() != self.form.nightMode.isChecked():
|
|
self.mw.pm.set_night_mode(not self.mw.pm.night_mode())
|
|
restart_required = True
|
|
|
|
if restart_required:
|
|
showInfo(tr.preferences_changes_will_take_effect_when_you())
|
|
|
|
self.updateOptions()
|
|
|
|
# legacy - one of Henrik's add-ons is currently wrapping them
|
|
|
|
def setupOptions(self) -> None:
|
|
pass
|
|
|
|
def updateOptions(self) -> None:
|
|
pass
|
|
|
|
# Global: language
|
|
######################################################################
|
|
|
|
def setup_language(self) -> None:
|
|
f = self.form
|
|
f.lang.addItems([x[0] for x in anki.lang.langs])
|
|
f.lang.setCurrentIndex(self.current_lang_index())
|
|
qconnect(f.lang.currentIndexChanged, self.on_language_index_changed)
|
|
|
|
def current_lang_index(self) -> int:
|
|
codes = [x[1] for x in anki.lang.langs]
|
|
lang = anki.lang.currentLang
|
|
if lang in anki.lang.compatMap:
|
|
lang = anki.lang.compatMap[lang]
|
|
else:
|
|
lang = lang.replace("-", "_")
|
|
try:
|
|
return codes.index(lang)
|
|
except:
|
|
return codes.index("en_US")
|
|
|
|
def on_language_index_changed(self, idx: int) -> None:
|
|
code = anki.lang.langs[idx][1]
|
|
self.mw.pm.setLang(code)
|
|
showInfo(tr.preferences_please_restart_anki_to_complete_language(), parent=self)
|
|
|
|
# Global: video driver
|
|
######################################################################
|
|
|
|
def setup_video_driver(self) -> None:
|
|
self.video_drivers = VideoDriver.all_for_platform()
|
|
names = [
|
|
tr.preferences_video_driver(driver=video_driver_name_for_platform(d))
|
|
for d in self.video_drivers
|
|
]
|
|
self.form.video_driver.addItems(names)
|
|
self.form.video_driver.setCurrentIndex(
|
|
self.video_drivers.index(self.mw.pm.video_driver())
|
|
)
|
|
self.form.video_driver.setVisible(qtmajor == 5)
|
|
|
|
def update_video_driver(self) -> None:
|
|
new_driver = self.video_drivers[self.form.video_driver.currentIndex()]
|
|
if new_driver != self.mw.pm.video_driver():
|
|
self.mw.pm.set_video_driver(new_driver)
|
|
showInfo(tr.preferences_changes_will_take_effect_when_you())
|
|
|
|
|
|
def video_driver_name_for_platform(driver: VideoDriver) -> str:
|
|
if driver == VideoDriver.ANGLE:
|
|
return tr.preferences_video_driver_angle()
|
|
elif driver == VideoDriver.Software:
|
|
if isMac:
|
|
return tr.preferences_video_driver_software_mac()
|
|
else:
|
|
return tr.preferences_video_driver_software_other()
|
|
else:
|
|
if isMac:
|
|
return tr.preferences_video_driver_opengl_mac()
|
|
else:
|
|
return tr.preferences_video_driver_opengl_other()
|