1

Updating preference system

Former-commit-id: a78921bc5ddfac052070580f12e000eda4b52200
This commit is contained in:
Alex Yatskov 2013-11-10 11:46:50 -08:00
parent eee3e9627a
commit dce854f966
5 changed files with 72 additions and 252 deletions

View File

@ -27,33 +27,33 @@
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="checkLoadRecentFile">
<property name="text">
<string>Load recent file on startup</string>
<string>Load most recent file</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="checkStripReadings">
<property name="text">
<string>Strip readings from loaded files</string>
<string>Strip readings from files</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="checkCheckForUpdates">
<property name="text">
<string>Check for updates on startup</string>
<string>Check for updates</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Maximum scan length</string>
<string>Text scan length</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="spinMaxScanLength">
<widget class="QSpinBox" name="spinScanLength">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>

View File

@ -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
}

View File

@ -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'))

View File

@ -16,245 +16,63 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
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()

View File

@ -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)