From dce854f966f946cba1a5aa2386b21a0050c9f4a0 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 10 Nov 2013 11:46:50 -0800 Subject: [PATCH] Updating preference system Former-commit-id: a78921bc5ddfac052070580f12e000eda4b52200 --- ui/preferences.ui | 10 +- yomi_base/defaults.json | 10 +- yomi_base/japanese/__init__.py | 2 +- yomi_base/preference_data.py | 248 +++++---------------------------- yomi_base/reader.py | 54 +++---- 5 files changed, 72 insertions(+), 252 deletions(-) diff --git a/ui/preferences.ui b/ui/preferences.ui index 1162b2d..ea7209b 100644 --- a/ui/preferences.ui +++ b/ui/preferences.ui @@ -27,33 +27,33 @@ - Load recent file on startup + Load most recent file - Strip readings from loaded files + Strip readings from files - Check for updates on startup + Check for updates - Maximum scan length + Text scan length - + 0 diff --git a/yomi_base/defaults.json b/yomi_base/defaults.json index b02cfd6..1dd6b4d 100644 --- a/yomi_base/defaults.json +++ b/yomi_base/defaults.json @@ -1,13 +1,13 @@ { + "bgColor": 4294967295, "checkForUpdates": true, - "colorBg": 4294967295, - "colorFg": 4278190080, - "decks": [], - "fantFamily": "Arial", + "fontFamily": "Arial", + "fgColor": 4278190080, "fontSize": 12, "loadRecentFile": true, "recentFiles": [], "scanLength": 16, "stripReadings": false, + "tags": [], "wordWrap": false -} \ No newline at end of file +} diff --git a/yomi_base/japanese/__init__.py b/yomi_base/japanese/__init__.py index 25f19ee..77ab2a1 100644 --- a/yomi_base/japanese/__init__.py +++ b/yomi_base/japanese/__init__.py @@ -23,7 +23,7 @@ import translate def initLanguage(): - directory = os.path.split(__file__)[0] + directory = os.path.dirname(__file__) return translate.Translator( deinflect.Deinflector(os.path.join(directory, 'deinflect.json')), dictionary.Dictionary(os.path.join(directory, 'dictionary.db')) diff --git a/yomi_base/preference_data.py b/yomi_base/preference_data.py index 2626e7a..30872b6 100644 --- a/yomi_base/preference_data.py +++ b/yomi_base/preference_data.py @@ -16,245 +16,63 @@ # along with this program. If not, see . -from PyQt4 import QtCore, QtGui -from xml.dom import minidom +import codecs +import json +import operator import os -class Preferences: +class Preferences(object): def __init__(self): - self.clear() + self.filename = os.path.expanduser('~/.yomichan.json') + self.defaults = os.path.join(os.path.dirname(__file__), 'defaults.json') + self.settings = dict() - def clear(self): - self.generalFindUpdates = True - self.generalReadingsStrip = False - self.generalRecentLoad = True - self.generalRecentFiles = list() - self.generalRecentMax = 10 - - self.uiContentFontFamily = 'Arial' - self.uiContentFontSize = 12 - self.uiContentColorFg = QtGui.QColor(QtCore.Qt.black).rgba() - self.uiContentColorBg = QtGui.QColor(QtCore.Qt.white).rgba() - self.uiContentWordWrap = False - - self.uiReaderState = None - self.uiReaderPosition = None - self.uiReaderSize = None - - self.searchScanMax = 16 - self.searchResultMax = 32 - self.searchGroupByExp = True - - self.ankiFields = dict() - self.ankiTags = list() - self.ankiDeck = unicode() - self.ankiModel = unicode() + def __getitem__(self, name): + return self.settings.get(name) - def load(self, filename=None): - if not filename: - filename = self.defaultFilename() + def __setitem__(self, name, value): + self.settings[name] = value + + + def load(self): + with codecs.open(self.defaults, 'rb', 'utf-8') as fp: + self.settings = json.load(fp) try: - doc = minidom.parse(filename) - except: - return False - - root = doc.documentElement - if root.nodeName != 'yomichan': - return False - - self.clear() - - for general in root.getElementsByTagName('general'): - self.generalRecentLoad = self.readAttrBool(general, 'recentLoad', self.generalRecentLoad) - self.generalReadingsStrip = self.readAttrBool(general, 'readingsStrip', self.generalReadingsStrip) - self.generalFindUpdates = self.readAttrBool(general, 'findUpdates', self.generalFindUpdates) - - for recent in general.getElementsByTagName('recent'): - path = self.readAttrStr(recent, 'path') - position = self.readAttrInt(recent, 'position') - if path and os.path.isfile(path): - self.generalRecentFiles.append((path, position)) - - for ui in root.getElementsByTagName('ui'): - for content in ui.getElementsByTagName('content'): - self.uiContentFontFamily = self.readAttrStr(content, 'fontFamily', self.uiContentFontFamily) - self.uiContentFontSize = self.readAttrInt(content, 'fontSize', self.uiContentFontSize) - self.uiContentColorFg = self.readAttrInt(content, 'colorFg', self.uiContentColorFg) - self.uiContentColorBg = self.readAttrInt(content, 'colorBg', self.uiContentColorBg) - self.uiContentWordWrap = self.readAttrBool(content, 'wordWrap', self.uiContentWordWrap) - - for reader in ui.getElementsByTagName('reader'): - self.uiReaderState = self.readAttrStr(reader, 'state', self.uiReaderState) - self.uiReaderPosition = self.readAttrIntTuple(reader, 'position', self.uiReaderPosition) - self.uiReaderSize = self.readAttrIntTuple(reader, 'size', self.uiReaderSize) - - for search in root.getElementsByTagName('search'): - self.searchScanMax = self.readAttrInt(search, 'scanMax', self.searchScanMax) - self.searchResultMax = self.readAttrInt(search, 'resultMax', self.searchResultMax) - self.searchGroupByExp = self.readAttrBool(search, 'groupByExp', self.searchGroupByExp) - - for anki in root.getElementsByTagName('anki'): - self.ankiDeck = self.readAttrStr(anki, 'deck', unicode()) - self.ankiModel = self.readAttrStr(anki, 'model', unicode()) - - for tag in anki.getElementsByTagName('tag'): - value = self.readAttrStr(tag, 'value', unicode()) - self.ankiTags.append(value) - - for model in anki.getElementsByTagName('model'): - for field in model.getElementsByTagName('field'): - name = self.readAttrStr(field, 'name') - value = self.readAttrStr(field, 'value') - if name and value: - self.ankiFields[name] = value - - return True + if os.path.exists(self.filename): + with codecs.open(self.filename, 'rb', 'utf-8') as fp: + self.settings.update(json.load(fp)) + except ValueError: + pass - def save(self, filename=None): - if not filename: - filename = self.defaultFilename() - - directory = os.path.split(filename)[0] - if not os.path.isdir(directory): - try: - os.makedirs(directory) - except OSError: - return False - - doc = minidom.Document() - root = doc.createElement('yomichan') - doc.appendChild(root) - - general = doc.createElement('general') - root.appendChild(general) - self.writeAttrBool(general, 'recentLoad', self.generalRecentLoad) - self.writeAttrBool(general, 'readingsStrip', self.generalReadingsStrip) - self.writeAttrBool(general, 'findUpdates', self.generalFindUpdates) - for path, position in self.generalRecentFiles: - recent = doc.createElement('recent') - general.appendChild(recent) - self.writeAttrStr(recent, 'path', path) - self.writeAttrInt(recent, 'position', position) - - ui = doc.createElement('ui') - root.appendChild(ui) - - content = doc.createElement('content') - ui.appendChild(content) - self.writeAttrStr(content, 'fontFamily', self.uiContentFontFamily) - self.writeAttrInt(content, 'fontSize', self.uiContentFontSize) - self.writeAttrInt(content, 'colorFg', self.uiContentColorFg) - self.writeAttrInt(content, 'colorBg', self.uiContentColorBg) - self.writeAttrBool(content, 'wordWrap', self.uiContentWordWrap) - - reader = doc.createElement('reader') - ui.appendChild(reader) - self.writeAttrStr(reader, 'state', self.uiReaderState) - self.writeAttrIntTuple(reader, 'position', self.uiReaderPosition) - self.writeAttrIntTuple(reader, 'size', self.uiReaderSize) - - search = doc.createElement('search') - root.appendChild(search) - self.writeAttrInt(search, 'scanMax', self.searchScanMax) - self.writeAttrInt(search, 'resultMax', self.searchResultMax) - self.writeAttrBool(search, 'groupByExp', self.searchGroupByExp) - - anki = doc.createElement('anki') - root.appendChild(anki) - self.writeAttrStr(anki, 'deck', self.ankiDeck) - self.writeAttrStr(anki, 'model', self.ankiModel) - - for value in self.ankiTags: - tag = doc.createElement('tag') - anki.appendChild(tag) - self.writeAttrStr(tag, 'value', value) - - if self.ankiFields: - model = doc.createElement('model') - anki.appendChild(model) - for name, value in self.ankiFields.items(): - field = doc.createElement('field') - model.appendChild(field) - self.writeAttrStr(field, 'name', name) - self.writeAttrStr(field, 'value', value) - - try: - with open(filename, 'w') as fp: - fp.write(doc.toprettyxml(encoding='utf-8')) - except IOError: - return False - - return True - - - def defaultFilename(self): - return os.path.expanduser('~/.yomichan/preferences.xml') + def save(self): + with codecs.open(self.filename, 'wb', 'utf-8') as fp: + json.dump(self.settings, fp, indent=4, sort_keys=True) def filePosition(self, filename): - results = filter(lambda x: x[0] == filename, self.generalRecentFiles) - return results[0][1] if results else 0 + matches = filter(lambda f: f['path'] != filename, self['recentFiles']) + return 0 if len(matches) == 0 else matches[0]['position'] def recentFiles(self): - return map(lambda x: x[0], self.generalRecentFiles) + return map(operator.itemgetter('path'), self['recentFiles']) def updateFactTags(self, tags): - if tags in self.ankiTags: - self.ankiTags.remove(tags) - self.ankiTags.insert(0, tags) + if tags in self['tags']: + self['tags'].remove(tags) + self['tags'].insert(0, tags) def updateRecentFile(self, filename, position): - self.generalRecentFiles = filter(lambda x: x[0] != filename, self.generalRecentFiles) - self.generalRecentFiles.insert(0, (filename, position)) - self.generalRecentFiles = self.generalRecentFiles[:self.generalRecentMax] + self['recentFiles'] = filter(lambda f: f['path'] != filename, self['recentFiles']) + self['recentFiles'].insert(0, {'path': filename, 'position': position}) def clearRecentFiles(self): - self.generalRecentFiles = list() - - - def readAttrStr(self, node, name, default=None): - return node.getAttribute(name) or default - - - def readAttrBool(self, node, name, default=False): - value = self.readAttrStr(node, name) - return value.lower() == 'true' if value else default - - - def readAttrInt(self, node, name, default=0): - value = self.readAttrStr(node, name) - return int(value) if value else default - - - def readAttrIntTuple(self, node, name, default=None): - value = self.readAttrStr(node, name) - return tuple([int(v) for v in value.split(',')]) if value else default - - - def writeAttrStr(self, node, name, value): - if value != None: - node.setAttribute(name, value) - - - def writeAttrBool(self, node, name, value): - if value != None: - node.setAttribute(name, ['false', 'true'][bool(value)]) - - - def writeAttrInt(self, node, name, value): - if value != None: - node.setAttribute(name, str(value)) - - - def writeAttrIntTuple(self, node, name, value): - if value != None: - node.setAttribute(name, ','.join([str(v) for v in value])) + self['recentFiles'] = list() diff --git a/yomi_base/reader.py b/yomi_base/reader.py index e0c82c2..59bf2ac 100644 --- a/yomi_base/reader.py +++ b/yomi_base/reader.py @@ -61,9 +61,9 @@ class MainWindowReader(QtGui.QMainWindow, reader_ui.Ui_MainWindowReader): if filename: self.openFile(filename) - elif self.preferences.generalRecentLoad: + elif self.preferences['loadRecentFile']: filenames = self.preferences.recentFiles() - if filenames: + if len(filenames) > 0: self.openFile(filenames[0]) self.actionOpen.triggered.connect(self.onActionOpen) @@ -88,40 +88,40 @@ class MainWindowReader(QtGui.QMainWindow, reader_ui.Ui_MainWindowReader): self.dockAnki.visibilityChanged.connect(self.onVisibilityChanged) self.updateFinder.updateResult.connect(self.onUpdaterSearchResult) - if self.preferences.generalFindUpdates: + if self.preferences['checkForUpdates']: self.updateFinder.start() def applyPreferences(self): - if self.preferences.uiReaderState is not None: - self.restoreState(QtCore.QByteArray.fromBase64(self.preferences.uiReaderState)) - if self.preferences.uiReaderPosition is not None: - self.move(QtCore.QPoint(*self.preferences.uiReaderPosition)) - if self.preferences.uiReaderSize is not None: - self.resize(QtCore.QSize(*self.preferences.uiReaderSize)) + if self.preferences['windowState'] is not None: + self.restoreState(QtCore.QByteArray.fromBase64(self.preferences['windowState'])) + if self.preferences['windowPosition'] is not None: + self.move(QtCore.QPoint(*self.preferences['windowPosition'])) + if self.preferences['windowSize'] is not None: + self.resize(QtCore.QSize(*self.preferences['windowSize'])) - self.comboTags.addItems(self.preferences.ankiTags) + self.comboTags.addItems(self.preferences['tags']) self.applyPreferencesContent() def applyPreferencesContent(self): palette = self.textContent.palette() - palette.setColor(QtGui.QPalette.Base, QtGui.QColor(self.preferences.uiContentColorBg)) - palette.setColor(QtGui.QPalette.Text, QtGui.QColor(self.preferences.uiContentColorFg)) + palette.setColor(QtGui.QPalette.Base, QtGui.QColor(self.preferences['bgColor'])) + palette.setColor(QtGui.QPalette.Text, QtGui.QColor(self.preferences['fgColor'])) self.textContent.setPalette(palette) font = self.textContent.font() - font.setFamily(self.preferences.uiContentFontFamily) - font.setPointSize(self.preferences.uiContentFontSize + self.zoom) - self.textContent.setLineWrapMode(self.preferences.uiContentWordWrap) + font.setFamily(self.preferences['fontFamily']) + font.setPointSize(self.preferences['fontSize'] + self.zoom) + self.textContent.setLineWrapMode(self.preferences['wordWrap']) self.textContent.setFont(font) - self.actionToggleWrap.setChecked(self.preferences.uiContentWordWrap) + self.actionToggleWrap.setChecked(self.preferences['wordWrap']) def closeEvent(self, event): self.closeFile() - self.preferences.uiReaderState = self.saveState().toBase64() + self.preferences['windowState'] = str(self.saveState().toBase64()) self.preferences.save() if self.anki is not None: @@ -148,11 +148,11 @@ class MainWindowReader(QtGui.QMainWindow, reader_ui.Ui_MainWindowReader): def moveEvent(self, event): - self.preferences.uiReaderPosition = (event.pos().x(), event.pos().y()) + self.preferences['windowPosition'] = event.pos().x(), event.pos().y() def resizeEvent(self, event): - self.preferences.uiReaderSize = (event.size().width(), event.size().height()) + self.preferences['windowSize'] = event.size().width(), event.size().height() def onActionOpen(self): @@ -219,7 +219,7 @@ class MainWindowReader(QtGui.QMainWindow, reader_ui.Ui_MainWindowReader): def onActionToggleWrap(self, wrap): mode = QtGui.QPlainTextEdit.WidgetWidth if wrap else QtGui.QPlainTextEdit.NoWrap - self.preferences.uiContentWordWrap = wrap + self.preferences['wordWrap'] = wrap self.textContent.setLineWrapMode(wrap) @@ -328,7 +328,7 @@ class MainWindowReader(QtGui.QMainWindow, reader_ui.Ui_MainWindowReader): self.updateRecentFiles() content, encoding = reader_util.decodeContent(content) - if self.preferences.generalReadingsStrip: + if self.preferences['stripReadings']: content = reader_util.stripContentReadings(content) self.textContent.setPlainText(content) @@ -339,7 +339,7 @@ class MainWindowReader(QtGui.QMainWindow, reader_ui.Ui_MainWindowReader): self.textContent.centerCursor() self.setStatus(u'Loaded file {0}'.format(filename)) - self.setWindowTitle(u'Yomichan - {0} ({1})'.format(os.path.split(filename)[1], encoding)) + self.setWindowTitle(u'Yomichan - {0} ({1})'.format(os.path.basename(filename), encoding)) def openFileByExtension(self, filename): @@ -433,7 +433,7 @@ class MainWindowReader(QtGui.QMainWindow, reader_ui.Ui_MainWindowReader): if self.anki is None: return False - fields = reader_util.replaceMarkupInFields(self.preferences.ankiFields, markup) + fields = reader_util.replaceMarkupInFields(self.preferences['tags'], markup) tagsSplit = reader_util.splitTags(unicode(self.comboTags.currentText())) tagsJoined = ' '.join(tagsSplit) @@ -444,6 +444,7 @@ class MainWindowReader(QtGui.QMainWindow, reader_ui.Ui_MainWindowReader): self.comboTags.insertItem(0, tagsJoined) self.preferences.updateFactTags(tagsJoined) + #FIXME factId = self.anki.addNote(self.preferences.ankiDeck, self.preferences.ankiModel, fields, tagsSplit) if factId is None: return False @@ -463,6 +464,7 @@ class MainWindowReader(QtGui.QMainWindow, reader_ui.Ui_MainWindowReader): def ankiIsFactValid(self, markup): + #FIXME if self.anki is not None: fields = reader_util.replaceMarkupInFields(self.preferences.ankiFields, markup) return self.anki.canAddNote(self.preferences.ankiDeck, self.preferences.ankiModel, fields) @@ -479,7 +481,7 @@ class MainWindowReader(QtGui.QMainWindow, reader_ui.Ui_MainWindowReader): def updateSampleFromPosition(self): samplePosStart = self.state.scanPosition - samplePosEnd = self.state.scanPosition + self.preferences.searchScanMax + samplePosEnd = self.state.scanPosition + self.preferences['scanLength'] cursor = self.textContent.textCursor() content = unicode(self.textContent.toPlainText()) @@ -544,11 +546,11 @@ class MainWindowReader(QtGui.QMainWindow, reader_ui.Ui_MainWindowReader): self.menuOpenRecent.clear() filenames = self.preferences.recentFiles() - if not filenames: + if len(filenames) == 0: return for filename in filenames: - self.menuOpenRecent.addAction(filename, lambda fn=filename: self.openFile(fn)) + self.menuOpenRecent.addAction(filename, lambda f=filename: self.openFile(f)) self.menuOpenRecent.addSeparator() self.menuOpenRecent.addAction('Clear file history', self.clearRecentFiles)