2012-12-21 08:51:59 +01:00
|
|
|
# -*- coding: utf-8 -*-
|
2019-02-05 04:59:03 +01:00
|
|
|
# Copyright: Ankitects Pty Ltd and contributors
|
2012-12-21 08:51:59 +01:00
|
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
|
2019-12-20 10:19:03 +01:00
|
|
|
import datetime
|
|
|
|
import time
|
2019-12-19 00:58:16 +01:00
|
|
|
|
2016-04-05 03:02:01 +02:00
|
|
|
import anki.lang
|
2012-12-21 08:51:59 +01:00
|
|
|
import aqt
|
2019-03-04 02:58:34 +01:00
|
|
|
from anki.lang import _
|
2019-12-20 10:19:03 +01:00
|
|
|
from aqt import AnkiQt
|
|
|
|
from aqt.qt import *
|
2020-02-15 08:48:35 +01:00
|
|
|
from aqt.utils import askUser, openHelp, showInfo, showWarning
|
2019-12-20 10:19:03 +01:00
|
|
|
|
2012-12-21 08:51:59 +01:00
|
|
|
|
|
|
|
class Preferences(QDialog):
|
2019-12-19 00:58:16 +01:00
|
|
|
def __init__(self, mw: AnkiQt):
|
2012-12-21 08:51:59 +01:00
|
|
|
QDialog.__init__(self, mw, Qt.Window)
|
|
|
|
self.mw = mw
|
|
|
|
self.prof = self.mw.pm.profile
|
|
|
|
self.form = aqt.forms.preferences.Ui_Preferences()
|
|
|
|
self.form.setupUi(self)
|
2014-06-03 09:24:47 +02:00
|
|
|
self.form.buttonBox.button(QDialogButtonBox.Help).setAutoDefault(False)
|
|
|
|
self.form.buttonBox.button(QDialogButtonBox.Close).setAutoDefault(False)
|
2016-05-31 10:51:40 +02:00
|
|
|
self.form.buttonBox.helpRequested.connect(lambda: openHelp("profileprefs"))
|
2017-09-10 07:15:12 +02:00
|
|
|
self.silentlyClose = True
|
2016-04-05 03:02:01 +02:00
|
|
|
self.setupLang()
|
2012-12-21 08:51:59 +01:00
|
|
|
self.setupCollection()
|
|
|
|
self.setupNetwork()
|
|
|
|
self.setupBackup()
|
|
|
|
self.setupOptions()
|
|
|
|
self.show()
|
|
|
|
|
|
|
|
def accept(self):
|
2015-04-26 12:28:32 +02:00
|
|
|
# avoid exception if main window is already closed
|
|
|
|
if not self.mw.col:
|
|
|
|
return
|
2012-12-21 08:51:59 +01:00
|
|
|
self.updateCollection()
|
|
|
|
self.updateNetwork()
|
|
|
|
self.updateBackup()
|
|
|
|
self.updateOptions()
|
|
|
|
self.mw.pm.save()
|
|
|
|
self.mw.reset()
|
|
|
|
self.done(0)
|
2017-09-10 07:15:12 +02:00
|
|
|
aqt.dialogs.markClosed("Preferences")
|
2012-12-21 08:51:59 +01:00
|
|
|
|
|
|
|
def reject(self):
|
|
|
|
self.accept()
|
|
|
|
|
2016-04-05 03:02:01 +02:00
|
|
|
# Language
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
def setupLang(self):
|
|
|
|
f = self.form
|
|
|
|
f.lang.addItems([x[0] for x in anki.lang.langs])
|
|
|
|
f.lang.setCurrentIndex(self.langIdx())
|
2016-05-31 10:51:40 +02:00
|
|
|
f.lang.currentIndexChanged.connect(self.onLangIdxChanged)
|
2016-04-05 03:02:01 +02:00
|
|
|
|
|
|
|
def langIdx(self):
|
|
|
|
codes = [x[1] for x in anki.lang.langs]
|
|
|
|
try:
|
|
|
|
return codes.index(anki.lang.getLang())
|
|
|
|
except:
|
2019-09-23 13:18:03 +02:00
|
|
|
return codes.index("en_US")
|
2016-04-05 03:02:01 +02:00
|
|
|
|
|
|
|
def onLangIdxChanged(self, idx):
|
|
|
|
code = anki.lang.langs[idx][1]
|
|
|
|
self.mw.pm.setLang(code)
|
|
|
|
showInfo(_("Please restart Anki to complete language change."), parent=self)
|
|
|
|
|
2012-12-21 08:51:59 +01:00
|
|
|
# Collection options
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
def setupCollection(self):
|
|
|
|
import anki.consts as c
|
2019-12-23 01:34:10 +01:00
|
|
|
|
2012-12-21 08:51:59 +01:00
|
|
|
f = self.form
|
|
|
|
qc = self.mw.col.conf
|
2018-01-20 06:32:52 +01:00
|
|
|
self._setupDayCutoff()
|
2018-08-30 05:39:04 +02:00
|
|
|
if isMac:
|
|
|
|
f.hwAccel.setVisible(False)
|
|
|
|
else:
|
|
|
|
f.hwAccel.setChecked(self.mw.pm.glMode() != "software")
|
2019-12-23 01:34:10 +01:00
|
|
|
f.lrnCutoff.setValue(qc["collapseTime"] / 60.0)
|
|
|
|
f.timeLimit.setValue(qc["timeLim"] / 60.0)
|
|
|
|
f.showEstimates.setChecked(qc["estTimes"])
|
|
|
|
f.showProgress.setChecked(qc["dueCounts"])
|
2016-05-12 06:45:35 +02:00
|
|
|
f.newSpread.addItems(list(c.newCardSchedulingLabels().values()))
|
2019-12-23 01:34:10 +01:00
|
|
|
f.newSpread.setCurrentIndex(qc["newSpread"])
|
2012-12-21 08:51:59 +01:00
|
|
|
f.useCurrent.setCurrentIndex(int(not qc.get("addToCur", True)))
|
2018-01-20 07:07:41 +01:00
|
|
|
f.dayLearnFirst.setChecked(qc.get("dayLearnFirst", False))
|
|
|
|
if self.mw.col.schedVer() != 2:
|
2018-01-22 02:24:45 +01:00
|
|
|
f.dayLearnFirst.setVisible(False)
|
2018-04-30 08:26:19 +02:00
|
|
|
else:
|
|
|
|
f.newSched.setChecked(True)
|
2018-01-20 06:32:52 +01:00
|
|
|
|
2012-12-21 08:51:59 +01:00
|
|
|
def updateCollection(self):
|
|
|
|
f = self.form
|
|
|
|
d = self.mw.col
|
2018-08-30 05:39:04 +02:00
|
|
|
|
|
|
|
if not isMac:
|
|
|
|
wasAccel = self.mw.pm.glMode() != "software"
|
|
|
|
wantAccel = f.hwAccel.isChecked()
|
|
|
|
if wasAccel != wantAccel:
|
|
|
|
if wantAccel:
|
|
|
|
self.mw.pm.setGlMode("auto")
|
|
|
|
else:
|
|
|
|
self.mw.pm.setGlMode("software")
|
|
|
|
showInfo(_("Changes will take effect when you restart Anki."))
|
|
|
|
|
2012-12-21 08:51:59 +01:00
|
|
|
qc = d.conf
|
2019-12-23 01:34:10 +01:00
|
|
|
qc["dueCounts"] = f.showProgress.isChecked()
|
|
|
|
qc["estTimes"] = f.showEstimates.isChecked()
|
|
|
|
qc["newSpread"] = f.newSpread.currentIndex()
|
|
|
|
qc["timeLim"] = f.timeLimit.value() * 60
|
|
|
|
qc["collapseTime"] = f.lrnCutoff.value() * 60
|
|
|
|
qc["addToCur"] = not f.useCurrent.currentIndex()
|
|
|
|
qc["dayLearnFirst"] = f.dayLearnFirst.isChecked()
|
2018-01-20 06:32:52 +01:00
|
|
|
self._updateDayCutoff()
|
2018-04-30 08:26:19 +02:00
|
|
|
self._updateSchedVer(f.newSched.isChecked())
|
2018-01-20 06:32:52 +01:00
|
|
|
d.setMod()
|
|
|
|
|
2018-04-30 08:26:19 +02:00
|
|
|
# Scheduler version
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
def _updateSchedVer(self, wantNew):
|
|
|
|
haveNew = self.mw.col.schedVer() == 2
|
|
|
|
|
|
|
|
# nothing to do?
|
|
|
|
if haveNew == wantNew:
|
|
|
|
return
|
|
|
|
|
2019-12-23 01:34:10 +01:00
|
|
|
if not askUser(
|
|
|
|
_(
|
|
|
|
"This will reset any cards in learning, clear filtered decks, and change the scheduler version. Proceed?"
|
|
|
|
)
|
|
|
|
):
|
2018-04-30 08:26:19 +02:00
|
|
|
return
|
|
|
|
|
2019-12-06 09:37:39 +01:00
|
|
|
if wantNew:
|
|
|
|
self.mw.col.changeSchedulerVer(2)
|
|
|
|
else:
|
|
|
|
self.mw.col.changeSchedulerVer(1)
|
2018-04-30 08:26:19 +02:00
|
|
|
|
2018-01-20 06:32:52 +01:00
|
|
|
# Day cutoff
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
def _setupDayCutoff(self):
|
|
|
|
if self.mw.col.schedVer() == 2:
|
|
|
|
self._setupDayCutoffV2()
|
|
|
|
else:
|
|
|
|
self._setupDayCutoffV1()
|
|
|
|
|
|
|
|
def _setupDayCutoffV1(self):
|
|
|
|
self.startDate = datetime.datetime.fromtimestamp(self.mw.col.crt)
|
|
|
|
self.form.dayOffset.setValue(self.startDate.hour)
|
|
|
|
|
|
|
|
def _setupDayCutoffV2(self):
|
|
|
|
self.form.dayOffset.setValue(self.mw.col.conf.get("rollover", 4))
|
|
|
|
|
|
|
|
def _updateDayCutoff(self):
|
|
|
|
if self.mw.col.schedVer() == 2:
|
|
|
|
self._updateDayCutoffV2()
|
|
|
|
else:
|
|
|
|
self._updateDayCutoffV1()
|
|
|
|
|
|
|
|
def _updateDayCutoffV1(self):
|
|
|
|
hrs = self.form.dayOffset.value()
|
2012-12-21 08:51:59 +01:00
|
|
|
old = self.startDate
|
2019-12-23 01:34:10 +01:00
|
|
|
date = datetime.datetime(old.year, old.month, old.day, hrs)
|
2018-01-20 06:32:52 +01:00
|
|
|
self.mw.col.crt = int(time.mktime(date.timetuple()))
|
|
|
|
|
|
|
|
def _updateDayCutoffV2(self):
|
2019-12-23 01:34:10 +01:00
|
|
|
self.mw.col.conf["rollover"] = self.form.dayOffset.value()
|
2012-12-21 08:51:59 +01:00
|
|
|
|
|
|
|
# Network
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
def setupNetwork(self):
|
2019-12-23 01:34:10 +01:00
|
|
|
self.form.syncOnProgramOpen.setChecked(self.prof["autoSync"])
|
|
|
|
self.form.syncMedia.setChecked(self.prof["syncMedia"])
|
|
|
|
if not self.prof["syncKey"]:
|
2012-12-21 08:51:59 +01:00
|
|
|
self._hideAuth()
|
|
|
|
else:
|
2019-12-23 01:34:10 +01:00
|
|
|
self.form.syncUser.setText(self.prof.get("syncUser", ""))
|
2016-05-31 10:51:40 +02:00
|
|
|
self.form.syncDeauth.clicked.connect(self.onSyncDeauth)
|
2012-12-21 08:51:59 +01:00
|
|
|
|
|
|
|
def _hideAuth(self):
|
2013-04-15 06:46:07 +02:00
|
|
|
self.form.syncDeauth.setVisible(False)
|
2013-10-03 23:09:36 +02:00
|
|
|
self.form.syncUser.setText("")
|
2019-12-23 01:34:10 +01:00
|
|
|
self.form.syncLabel.setText(
|
|
|
|
_(
|
|
|
|
"""\
|
2012-12-21 08:51:59 +01:00
|
|
|
<b>Synchronization</b><br>
|
2019-12-23 01:34:10 +01:00
|
|
|
Not currently enabled; click the sync button in the main window to enable."""
|
|
|
|
)
|
|
|
|
)
|
2012-12-21 08:51:59 +01:00
|
|
|
|
2020-02-15 08:48:35 +01:00
|
|
|
def onSyncDeauth(self) -> None:
|
|
|
|
if self.mw.media_syncer.is_syncing():
|
|
|
|
showWarning("Can't log out while sync in progress.")
|
|
|
|
return
|
2019-12-23 01:34:10 +01:00
|
|
|
self.prof["syncKey"] = None
|
2020-02-15 08:48:35 +01:00
|
|
|
self.mw.col.media.force_resync()
|
2012-12-21 08:51:59 +01:00
|
|
|
self._hideAuth()
|
|
|
|
|
|
|
|
def updateNetwork(self):
|
2019-12-23 01:34:10 +01:00
|
|
|
self.prof["autoSync"] = self.form.syncOnProgramOpen.isChecked()
|
|
|
|
self.prof["syncMedia"] = self.form.syncMedia.isChecked()
|
2013-05-14 08:23:50 +02:00
|
|
|
if self.form.fullSync.isChecked():
|
2014-11-03 08:28:12 +01:00
|
|
|
self.mw.col.modSchema(check=False)
|
2013-05-14 08:23:50 +02:00
|
|
|
self.mw.col.setMod()
|
2012-12-21 08:51:59 +01:00
|
|
|
|
|
|
|
# Backup
|
|
|
|
######################################################################
|
|
|
|
|
|
|
|
def setupBackup(self):
|
2019-12-23 01:34:10 +01:00
|
|
|
self.form.numBackups.setValue(self.prof["numBackups"])
|
2012-12-21 08:51:59 +01:00
|
|
|
|
|
|
|
def updateBackup(self):
|
2019-12-23 01:34:10 +01:00
|
|
|
self.prof["numBackups"] = self.form.numBackups.value()
|
2012-12-21 08:51:59 +01:00
|
|
|
|
|
|
|
# Basic & Advanced Options
|
|
|
|
######################################################################
|
|
|
|
|
2019-12-19 02:39:40 +01:00
|
|
|
def setupOptions(self):
|
2012-12-21 08:51:59 +01:00
|
|
|
self.form.pastePNG.setChecked(self.prof.get("pastePNG", False))
|
2019-12-23 01:34:10 +01:00
|
|
|
self.form.uiScale.setValue(self.mw.pm.uiScale() * 100)
|
2020-01-16 03:36:04 +01:00
|
|
|
self.form.pasteInvert.setChecked(self.prof.get("pasteInvert", False))
|
2020-01-21 12:00:17 +01:00
|
|
|
self.form.showPlayButtons.setChecked(self.prof.get("showPlayButtons", True))
|
2020-01-23 06:08:10 +01:00
|
|
|
self.form.nightMode.setChecked(self.mw.pm.night_mode())
|
2020-02-02 23:32:07 +01:00
|
|
|
self.form.interrupt_audio.setChecked(self.mw.pm.interrupt_audio())
|
2012-12-21 08:51:59 +01:00
|
|
|
|
2019-12-19 02:39:40 +01:00
|
|
|
def updateOptions(self):
|
2020-01-23 06:08:10 +01:00
|
|
|
restart_required = False
|
|
|
|
|
2019-12-23 01:34:10 +01:00
|
|
|
self.prof["pastePNG"] = self.form.pastePNG.isChecked()
|
2020-01-16 03:36:04 +01:00
|
|
|
self.prof["pasteInvert"] = self.form.pasteInvert.isChecked()
|
2019-12-23 01:34:10 +01:00
|
|
|
newScale = self.form.uiScale.value() / 100
|
2019-12-19 00:58:16 +01:00
|
|
|
if newScale != self.mw.pm.uiScale():
|
|
|
|
self.mw.pm.setUiScale(newScale)
|
2020-01-23 06:08:10 +01:00
|
|
|
restart_required = True
|
2020-01-21 12:00:17 +01:00
|
|
|
self.prof["showPlayButtons"] = self.form.showPlayButtons.isChecked()
|
2020-01-23 06:08:10 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2020-02-02 23:32:07 +01:00
|
|
|
self.mw.pm.set_interrupt_audio(self.form.interrupt_audio.isChecked())
|
|
|
|
|
2020-01-23 06:08:10 +01:00
|
|
|
if restart_required:
|
|
|
|
showInfo(_("Changes will take effect when you restart Anki."))
|