update config.py

- move into an sqlite file so we don't have to worry about db corruption
  anymore, and can share the deck list among multiple instances
- remove some old options
This commit is contained in:
Damien Elmes 2011-03-14 00:21:59 +09:00
parent fbb7fe8a3e
commit c01c6cb79a
9 changed files with 128 additions and 195 deletions

View file

@ -123,7 +123,7 @@ def run():
if 'APPDATA' in os.environ: if 'APPDATA' in os.environ:
os.environ['HOME'] = os.environ['APPDATA'] os.environ['HOME'] = os.environ['APPDATA']
else: else:
os.environ['HOME'] = "c:\\anki" mustQuit = True
# make and check accessible # make and check accessible
try: try:
os.makedirs(os.path.expanduser("~/.anki")) os.makedirs(os.path.expanduser("~/.anki"))

View file

@ -22,10 +22,7 @@ class FocusButton(QPushButton):
class AddCards(QDialog): class AddCards(QDialog):
def __init__(self, parent): def __init__(self, parent):
if parent.config['standaloneWindows']: windParent = None
windParent = None
else:
windParent = parent
QDialog.__init__(self, windParent, Qt.Window) QDialog.__init__(self, windParent, Qt.Window)
self.parent = parent self.parent = parent
ui.utils.applyStyles(self) ui.utils.applyStyles(self)

View file

@ -342,10 +342,7 @@ class StatusDelegate(QItemDelegate):
class EditDeck(QMainWindow): class EditDeck(QMainWindow):
def __init__(self, parent): def __init__(self, parent):
if parent.config['standaloneWindows']: windParent = None
windParent = None
else:
windParent = parent
QMainWindow.__init__(self, windParent) QMainWindow.__init__(self, windParent)
applyStyles(self) applyStyles(self)
self.parent = parent self.parent = parent

View file

@ -3,132 +3,126 @@
# User configuration handling # User configuration handling
########################################################################## ##########################################################################
# The majority of the config is serialized into a string, both for easy access
# and backwards compatibility. A separate table keeps track of seen decks, so
# that multiple instances can update the recent decks list.
from PyQt4.QtGui import * import os, sys, time, random, cPickle
from PyQt4.QtCore import * from anki.db import DB
import os, sys, cPickle, locale, types, shutil, time, re, random
# compatability defaultConf = {
def unpickleWxFont(*args): 'confVer': 3,
pass # remove?
def pickleWxFont(*args): 'colourTimes': True,
pass 'deckBrowserNameLength': 30,
'deckBrowserOrder': 0,
'deckBrowserRefreshPeriod': 3600,
'factEditorAdvanced': False,
'showStudyScreen': True,
class Config(dict): 'recentDeckPaths': [],
'interfaceLang': "en",
configDbName = "config.db" 'autoplaySounds': True,
'checkForUpdates': True,
'created': time.time(),
'deleteMedia': False,
'documentDir': u"",
'dropboxPublicFolder': u"",
'editFontFamily': 'Arial',
'editFontSize': 12,
'editLineSize': 20,
'editorReverseOrder': False,
'iconSize': 32,
'id': random.randrange(0, 2**63),
'lastMsg': -1,
'loadLastDeck': False,
'mainWindowGeom': None,
'mainWindowState': None,
'mediaLocation': "",
'numBackups': 30,
'optimizeSmall': False,
'preserveKeyboard': True,
'proxyHost': '',
'proxyPass': '',
'proxyPort': 8080,
'proxyUser': '',
'qaDivider': True,
'recentColours': ["#000000", "#0000ff"],
'repeatQuestionAudio': True,
'scrollToAnswer': True,
'showCardTimer': True,
'showProgress': True,
'showTimer': True,
'showToolbar': True,
'showTrayIcon': False,
'splitQA': True,
'stripHTML': True,
'studyOptionsTab': 0,
'suppressEstimates': False,
'suppressUpdate': False,
'syncDisableWhenMoved': True,
'syncOnLoad': False,
'syncOnProgramOpen': True,
'syncPassword': "",
'syncUsername': "",
}
def __init__(self, configPath): class Config(object):
self.configPath = configPath configDbName = "ankiprefs.db"
if sys.platform == "win32":
if self.configPath.startswith("~"): def __init__(self, confDir):
# windows sucks self.confDir = confDir
self.configPath = "c:\\anki" self._conf = {}
elif sys.platform.startswith("darwin"): if sys.platform.startswith("darwin") and (
if self.configPath == os.path.expanduser("~/.anki"): self.confDir == os.path.expanduser("~/.anki")):
oldDb = self.getDbPath() self.confDir = os.path.expanduser(
self.configPath = os.path.expanduser(
"~/Library/Application Support/Anki") "~/Library/Application Support/Anki")
# upgrade? self._addAnkiDirs()
if (not os.path.exists(self.configPath) and
os.path.exists(oldDb)):
self.makeAnkiDir()
newDb = self.getDbPath()
shutil.copy2(oldDb, newDb)
self.makeAnkiDir()
self.load() self.load()
def defaults(self): # dict interface
fields = { def get(self, *args):
'addZeroSpace': False, return self._conf.get(*args)
'alternativeTheme': False, def __getitem__(self, key):
'autoplaySounds': True, return self._conf[key]
'checkForUpdates': True, def __setitem__(self, key, val):
'colourTimes': True, self._conf[key] = val
'created': time.time(), def __contains__(self, key):
'deckBrowserNameLength': 30, return self._conf.__contains__(key)
'deckBrowserOrder': 0,
'deckBrowserRefreshPeriod': 3600, # load/save
'deleteMedia': False, def load(self):
'documentDir': u"", path = self._dbPath()
'dropboxPublicFolder': u"", self.db = DB(path, level=None, text=str)
'editFontFamily': 'Arial', self.db.executescript("""
'editFontSize': 12, create table if not exists recentDecks (path not null);
'editLineSize': 20, create table if not exists config (conf text not null);
'editorReverseOrder': False, insert or ignore into config values ('');""")
'extraNewCards': 5, conf = self.db.scalar("select conf from config")
'factEditorAdvanced': False, if conf:
'forceLTR': False, self._conf.update(cPickle.loads(conf))
'iconSize': 32, else:
'id': random.randrange(0, 2**63), self._conf.update(defaultConf)
'interfaceLang': "", self._addDefaults()
'lastMsg': -1,
'loadLastDeck': False, def save(self):
'mainWindowGeom': None, self.db.execute("update config set conf = ?",
'mainWindowState': None, cPickle.dumps(self._conf))
# one of empty, 'dropbox', or path used as prefix self.db.commit()
'mediaLocation': "",
'mainWindowState': None, def _addDefaults(self):
'numBackups': 30, if self.get('confVer') >= defaultConf['confVer']:
'optimizeSmall': False, return
'preserveKeyboard': True, for (k,v) in defaultConf.items():
'preventEditUntilAnswer': False, if k not in self:
'proxyHost': '',
'proxyPass': '',
'proxyPort': 8080,
'proxyUser': '',
'qaDivider': True,
'randomizeOnCram': True,
'recentColours': ["#000000", "#0000ff"],
'recentDeckPaths': [],
'repeatQuestionAudio': True,
'saveAfterAdding': True,
'saveAfterAddingNum': 1,
'saveAfterAnswer': True,
'saveAfterAnswerNum': 10,
'saveOnClose': True,
'scrollToAnswer': True,
'showCardTimer': True,
'showFontPreview': False,
'showLastCardContent': False,
'showLastCardInterval': False,
'showProgress': True,
'showStudyScreen': True,
'showStudyStats': True,
'showTimer': True,
'showToolbar': True,
'showTrayIcon': False,
'sortIndex': 0,
'splitQA': True,
'standaloneWindows': True,
'stripHTML': True,
'studyOptionsScreen': 0,
'suppressEstimates': False,
'suppressUpdate': False,
'syncDisableWhenMoved': True,
'syncInMsgBox': False,
'syncOnLoad': False,
'syncOnProgramOpen': True,
'syncPassword': "",
'syncUsername': "",
}
# disable sync on deck load when upgrading
if not self.has_key("syncOnProgramOpen"):
self['syncOnLoad'] = False
self['syncOnClose'] = False
for (k,v) in fields.items():
if not self.has_key(k):
self[k] = v self[k] = v
if not self['interfaceLang']:
# guess interface and target languages
(lang, enc) = locale.getdefaultlocale()
self['interfaceLang'] = lang
def getDbPath(self): def _dbPath(self):
return os.path.join(self.configPath, self.configDbName) return os.path.join(self.confDir, self.configDbName)
def makeAnkiDir(self): def _addAnkiDirs(self):
base = self.configPath base = self.confDir
for x in (base, for x in (base,
os.path.join(base, "plugins"), os.path.join(base, "plugins"),
os.path.join(base, "backups")): os.path.join(base, "backups")):
@ -137,41 +131,9 @@ class Config(dict):
except: except:
pass pass
def save(self): def _importOldData(self):
path = self.getDbPath() # compatability
# write to a temp file def unpickleWxFont(*args):
from tempfile import mkstemp pass
(fd, tmpname) = mkstemp(dir=os.path.dirname(path)) def pickleWxFont(*args):
tmpfile = os.fdopen(fd, 'w')
cPickle.dump(dict(self), tmpfile)
tmpfile.close()
# the write was successful, delete config file (if exists) and rename
if os.path.exists(path):
os.unlink(path)
os.rename(tmpname, path)
def fixLang(self, lang):
if lang and lang not in ("pt_BR", "zh_CN", "zh_TW"):
lang = re.sub("(.*)_.*", "\\1", lang)
if not lang:
lang = "en"
return lang
def load(self):
base = self.configPath
db = self.getDbPath()
# load config
try:
f = open(db)
self.update(cPickle.load(f))
except:
# config file was corrupted previously
pass pass
self.defaults()
# fix old recent deck path list
for n in range(len(self['recentDeckPaths'])):
s = self['recentDeckPaths'][n]
if not isinstance(s, types.UnicodeType):
self['recentDeckPaths'][n] = unicode(s, sys.getfilesystemencoding())
# fix locale settings
self["interfaceLang"] = self.fixLang(self["interfaceLang"])

View file

@ -594,10 +594,7 @@ counts are %d %d %d
self.connect(self.mainWin.showAnswerButton, SIGNAL("clicked()"), self.connect(self.mainWin.showAnswerButton, SIGNAL("clicked()"),
lambda: self.moveToState("showAnswer")) lambda: self.moveToState("showAnswer"))
if sys.platform.startswith("win32"): if sys.platform.startswith("win32"):
if self.config['alternativeTheme']: self.mainWin.showAnswerButton.setFixedWidth(358)
self.mainWin.showAnswerButton.setFixedWidth(370)
else:
self.mainWin.showAnswerButton.setFixedWidth(358)
else: else:
self.mainWin.showAnswerButton.setFixedWidth(351) self.mainWin.showAnswerButton.setFixedWidth(351)
self.mainWin.showAnswerButton.setFixedHeight(41) self.mainWin.showAnswerButton.setFixedHeight(41)
@ -1309,9 +1306,6 @@ your deck."""))
focusButton = openButton focusButton = openButton
# more # more
moreButton = QPushButton(_("More")) moreButton = QPushButton(_("More"))
if sys.platform.startswith("win32") and \
self.config['alternativeTheme']:
moreButton.setFixedHeight(24)
moreMenu = QMenu() moreMenu = QMenu()
a = moreMenu.addAction(QIcon(":/icons/edit-undo.png"), a = moreMenu.addAction(QIcon(":/icons/edit-undo.png"),
_("Hide From List")) _("Hide From List"))
@ -1549,7 +1543,7 @@ not be touched.""") %
SIGNAL("clicked()"), self.onNewCategoriesClicked) SIGNAL("clicked()"), self.onNewCategoriesClicked)
self.connect(self.mainWin.revCategories, self.connect(self.mainWin.revCategories,
SIGNAL("clicked()"), self.onRevCategoriesClicked) SIGNAL("clicked()"), self.onRevCategoriesClicked)
self.mainWin.tabWidget.setCurrentIndex(self.config['studyOptionsScreen']) self.mainWin.tabWidget.setCurrentIndex(self.config['studyOptionsTab'])
def onNewCategoriesClicked(self): def onNewCategoriesClicked(self):
aqt.activetags.show(self, "new") aqt.activetags.show(self, "new")
@ -1706,13 +1700,6 @@ not be touched.""") %
<tr><td>%(ntod_header)s</td><td align=right><b>%(new)s</b></td></tr> <tr><td>%(ntod_header)s</td><td align=right><b>%(new)s</b></td></tr>
<tr><td>%(ntot_header)s</td><td align=right>%(newof)s</td></tr> <tr><td>%(ntot_header)s</td><td align=right>%(newof)s</td></tr>
</table>""") % h </table>""") % h
# if (not dyest and not dtoday) or not self.config['showStudyStats']:
# self.haveYesterday = False
# stats1 = ""
# else:
# self.haveYesterday = True
# stats1 = (
# "<td>%s</td><td>&nbsp;&nbsp;&nbsp;&nbsp;</td>" % stats1)
self.mainWin.optionsLabel.setText("""\ self.mainWin.optionsLabel.setText("""\
<p><table><tr> <p><table><tr>
%s %s
@ -1790,7 +1777,7 @@ learnt today")
self.deck.reset() self.deck.reset()
if not self.deck.finishScheduler: if not self.deck.finishScheduler:
self.deck.startTimebox() self.deck.startTimebox()
self.config['studyOptionsScreen'] = self.mainWin.tabWidget.currentIndex() self.config['studyOptionsTab'] = self.mainWin.tabWidget.currentIndex()
self.moveToState("getQuestion") self.moveToState("getQuestion")
def onStudyOptions(self): def onStudyOptions(self):
@ -2137,8 +2124,7 @@ it to your friends.
self.mainWin.retranslateUi(self) self.mainWin.retranslateUi(self)
anki.lang.setLang(self.config["interfaceLang"], local=False) anki.lang.setLang(self.config["interfaceLang"], local=False)
self.updateTitleBar() self.updateTitleBar()
if self.config['interfaceLang'] in ("he","ar","fa") and \ if self.config['interfaceLang'] in ("he","ar","fa"):
not self.config['forceLTR']:
self.app.setLayoutDirection(Qt.RightToLeft) self.app.setLayoutDirection(Qt.RightToLeft)
else: else:
self.app.setLayoutDirection(Qt.LeftToRight) self.app.setLayoutDirection(Qt.LeftToRight)
@ -2565,10 +2551,8 @@ This deck already exists on your computer. Overwrite the local copy?"""),
self.mainWin.actionSuspendCard.setEnabled(True) self.mainWin.actionSuspendCard.setEnabled(True)
self.mainWin.actionDelete.setEnabled(True) self.mainWin.actionDelete.setEnabled(True)
self.mainWin.actionBuryFact.setEnabled(True) self.mainWin.actionBuryFact.setEnabled(True)
enableEdits = (not self.config['preventEditUntilAnswer'] or self.mainWin.actionEditCurrent.setEnabled(True)
self.state != "getQuestion") self.mainWin.actionEditdeck.setEnabled(True)
self.mainWin.actionEditCurrent.setEnabled(enableEdits)
self.mainWin.actionEditdeck.setEnabled(enableEdits)
runHook("enableCardMenuItems") runHook("enableCardMenuItems")
def maybeEnableUndo(self): def maybeEnableUndo(self):
@ -2635,7 +2619,7 @@ This deck already exists on your computer. Overwrite the local copy?"""),
########################################################################## ##########################################################################
def pluginsFolder(self): def pluginsFolder(self):
dir = self.config.configPath dir = self.config.confDir
if sys.platform.startswith("win32"): if sys.platform.startswith("win32"):
dir = dir.encode(sys.getfilesystemencoding()) dir = dir.encode(sys.getfilesystemencoding())
return os.path.join(dir, "plugins") return os.path.join(dir, "plugins")
@ -2809,7 +2793,7 @@ to work with this version of Anki."""))
def setupSound(self): def setupSound(self):
anki.sound.noiseProfile = os.path.join( anki.sound.noiseProfile = os.path.join(
self.config.configPath, "noise.profile").\ self.config.confDir, "noise.profile").\
encode(sys.getfilesystemencoding()) encode(sys.getfilesystemencoding())
anki.sound.checkForNoiseProfile() anki.sound.checkForNoiseProfile()
if sys.platform.startswith("darwin"): if sys.platform.startswith("darwin"):

View file

@ -173,7 +173,7 @@ class Preferences(QDialog):
self.onOpenBackup) self.onOpenBackup)
def onOpenBackup(self): def onOpenBackup(self):
path = os.path.join(self.config.configPath, "backups") path = os.path.join(self.config.confDir, "backups")
if sys.platform == "win32": if sys.platform == "win32":
anki.utils.call(["explorer", path.encode( anki.utils.call(["explorer", path.encode(
sys.getfilesystemencoding())], sys.getfilesystemencoding())],
@ -213,8 +213,6 @@ class Preferences(QDialog):
self.dialog.showTimer.setChecked(self.config['showTimer']) self.dialog.showTimer.setChecked(self.config['showTimer'])
self.dialog.showDivider.setChecked(self.config['qaDivider']) self.dialog.showDivider.setChecked(self.config['qaDivider'])
self.dialog.splitQA.setChecked(self.config['splitQA']) self.dialog.splitQA.setChecked(self.config['splitQA'])
self.dialog.addZeroSpace.setChecked(self.config['addZeroSpace'])
self.dialog.alternativeTheme.setChecked(self.config['alternativeTheme'])
self.dialog.showProgress.setChecked(self.config['showProgress']) self.dialog.showProgress.setChecked(self.config['showProgress'])
self.dialog.openLastDeck.setChecked(self.config['loadLastDeck']) self.dialog.openLastDeck.setChecked(self.config['loadLastDeck'])
self.dialog.deckBrowserOrder.setChecked(self.config['deckBrowserOrder']) self.dialog.deckBrowserOrder.setChecked(self.config['deckBrowserOrder'])
@ -232,10 +230,7 @@ class Preferences(QDialog):
self.config['showStudyScreen'] = self.dialog.showStudyOptions.isChecked() self.config['showStudyScreen'] = self.dialog.showStudyOptions.isChecked()
self.config['qaDivider'] = self.dialog.showDivider.isChecked() self.config['qaDivider'] = self.dialog.showDivider.isChecked()
self.config['splitQA'] = self.dialog.splitQA.isChecked() self.config['splitQA'] = self.dialog.splitQA.isChecked()
self.config['addZeroSpace'] = self.dialog.addZeroSpace.isChecked()
self.config['alternativeTheme'] = self.dialog.alternativeTheme.isChecked()
self.config['showProgress'] = self.dialog.showProgress.isChecked() self.config['showProgress'] = self.dialog.showProgress.isChecked()
self.config['preventEditUntilAnswer'] = self.dialog.preventEdits.isChecked()
self.config['stripHTML'] = self.dialog.stripHTML.isChecked() self.config['stripHTML'] = self.dialog.stripHTML.isChecked()
self.config['autoplaySounds'] = self.dialog.autoplaySounds.isChecked() self.config['autoplaySounds'] = self.dialog.autoplaySounds.isChecked()
self.config['loadLastDeck'] = self.dialog.openLastDeck.isChecked() self.config['loadLastDeck'] = self.dialog.openLastDeck.isChecked()

View file

@ -34,6 +34,7 @@ class StatusView(object):
self.timer = None self.timer = None
self.timerFlashStart = 0 self.timerFlashStart = 0
self.thinkingTimer = QTimer(parent) self.thinkingTimer = QTimer(parent)
print "show timer"
self.thinkingTimer.start(1000) self.thinkingTimer.start(1000)
parent.connect(self.thinkingTimer, SIGNAL("timeout()"), parent.connect(self.thinkingTimer, SIGNAL("timeout()"),
self.drawTimer) self.drawTimer)

View file

@ -262,7 +262,7 @@ def mungeQA(deck, txt):
def applyStyles(widget): def applyStyles(widget):
try: try:
styleFile = open(os.path.join(aqt.mw.config.configPath, styleFile = open(os.path.join(aqt.mw.config.confDir,
"style.css")) "style.css"))
widget.setStyleSheet(styleFile.read()) widget.setStyleSheet(styleFile.read())
except (IOError, OSError): except (IOError, OSError):

View file

@ -254,9 +254,6 @@ class View(object):
def mungeQA(self, deck, txt): def mungeQA(self, deck, txt):
txt = mungeQA(deck, txt) txt = mungeQA(deck, txt)
# hack to fix thai presentation issues
if self.main.config['addZeroSpace']:
txt = txt.replace("</span>", "&#8203;</span>")
return txt return txt
def onLoadFinished(self, bool): def onLoadFinished(self, bool):