', allowedTags)
- self.labelTags.setText('Allowed tags are {0}'.format(', '.join(allowedTags)))
-
- self.updateAnkiFields()
-
-
- def updateSampleText(self):
- palette = self.textSample.palette()
- palette.setColor(QtGui.QPalette.Base, QtGui.QColor(self.preferences['bgColor']))
- palette.setColor(QtGui.QPalette.Text, QtGui.QColor(self.preferences['fgColor']))
- self.textSample.setPalette(palette)
-
- font = self.textSample.font()
- font.setFamily(self.preferences['fontFamily'])
- font.setPointSize(self.preferences['fontSize'])
- self.textSample.setFont(font)
-
-
- def setAnkiFields(self, fields, fieldsPrefs):
- if fields is None:
- fields = []
-
- self.tableFields.blockSignals(True)
- self.tableFields.setRowCount(len(fields))
-
- for i, name in enumerate(fields):
- columns = []
-
- itemName = QtGui.QTableWidgetItem(name)
- itemName.setFlags(QtCore.Qt.ItemIsSelectable)
- columns.append(itemName)
-
- itemValue = QtGui.QTableWidgetItem(fieldsPrefs.get(name, u''))
- columns.append(itemValue)
-
- for j, column in enumerate(columns):
- self.tableFields.setItem(i, j, column)
-
- self.tableFields.blockSignals(False)
-
-
- def ankiFields(self):
- result = {}
-
- for i in range(0, self.tableFields.rowCount()):
- itemName = unicode(self.tableFields.item(i, 0).text())
- itemValue = unicode(self.tableFields.item(i, 1).text())
- result[itemName] = itemValue
-
- return result
-
-
- def onAccept(self):
- self.dialogToData()
-
-
- def onButtonColorFgClicked(self):
- color, ok = QtGui.QColorDialog.getRgba(self.preferences['fgColor'], self)
- if ok:
- self.preferences['fgColor'] = color
- self.updateSampleText()
-
-
- def onButtonColorBgClicked(self):
- color, ok = QtGui.QColorDialog.getRgba(self.preferences['bgColor'], self)
- if ok:
- self.preferences['bgColor'] = color
- self.updateSampleText()
-
-
- def onFontFamilyChanged(self, font):
- self.preferences['fontFamily'] = unicode(font.family())
- self.updateSampleText()
-
-
- def onFontSizeChanged(self, size):
- self.preferences['fontSize'] = size
- self.updateSampleText()
-
-
- def onModelChanged(self, index):
- self.updateAnkiFields()
- self.dialogToProfile()
-
-
- def onDeckChanged(self, index):
- self.dialogToProfile()
-
-
- def onFieldsChanged(self, item):
- self.dialogToProfile()
-
-
- def onProfileChanged(self, data):
- self.profileToDialog()
-
-
- def updateAnkiFields(self):
- modelName = self.comboBoxModel.currentText()
- fieldNames = self.anki.modelFieldNames(modelName) or []
-
- profile, name = self.activeProfile()
- fields = {} if profile is None else profile['fields']
-
- self.setAnkiFields(fieldNames, fields)
-
-
- def activeProfile(self):
- name = 'vocab' if self.radioButtonVocab.isChecked() else 'kanji'
- return self.profiles.get(name), name
-
-
- def setActiveProfile(self, profile):
- name = 'vocab' if self.radioButtonVocab.isChecked() else 'kanji'
- self.profiles[name] = profile
diff --git a/yomi_base/reader.py b/yomi_base/reader.py
deleted file mode 100644
index 36fdd10..0000000
--- a/yomi_base/reader.py
+++ /dev/null
@@ -1,604 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (C) 2013 Alex Yatskov
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-
-from PyQt4 import QtGui, QtCore
-import about
-import constants
-import gen.reader_ui
-import os
-import preferences
-import reader_util
-import updates
-
-
-class MainWindowReader(QtGui.QMainWindow, gen.reader_ui.Ui_MainWindowReader):
- class State:
- def __init__(self):
- self.filename = u''
- self.searchText = u''
- self.kanjiDefs = []
- self.vocabDefs = []
- self.scanPosition = 0
- self.searchPosition = 0
-
-
- def __init__(self, parent, preferences, language, filename=None, anki=None, closed=None):
- QtGui.QMainWindow.__init__(self, parent)
- self.setupUi(self)
-
- self.textContent.mouseMoveEvent = self.onContentMouseMove
- self.textContent.mousePressEvent = self.onContentMousePress
- self.dockAnki.setEnabled(anki is not None)
-
- self.facts = []
- self.anki = anki
- self.closed = closed
- self.language = language
- self.preferences = preferences
- self.state = self.State()
- self.updates = updates.UpdateFinder()
- self.zoom = 0
-
- self.applyPreferences()
- self.updateRecentFiles()
- self.updateVocabDefs()
- self.updateKanjiDefs()
-
- if filename is not None:
- self.openFile(filename)
- elif self.preferences['rememberTextContent']:
- self.textContent.setPlainText(self.preferences['textContent'])
- elif self.preferences['loadRecentFile']:
- filenames = self.preferences.recentFiles()
- if len(filenames) > 0 and os.path.isfile(filenames[0]):
- self.openFile(filenames[0])
-
- self.actionAbout.triggered.connect(self.onActionAbout)
- self.actionFind.triggered.connect(self.onActionFind)
- self.actionFindNext.triggered.connect(self.onActionFindNext)
- self.actionHomepage.triggered.connect(self.onActionHomepage)
- self.actionKindleDeck.triggered.connect(self.onActionKindleDeck)
- self.actionWordList.triggered.connect(self.onActionWordList)
- self.actionOpen.triggered.connect(self.onActionOpen)
- self.actionPreferences.triggered.connect(self.onActionPreferences)
- self.actionToggleWrap.toggled.connect(self.onActionToggleWrap)
- self.actionZoomIn.triggered.connect(self.onActionZoomIn)
- self.actionZoomOut.triggered.connect(self.onActionZoomOut)
- self.actionZoomReset.triggered.connect(self.onActionZoomReset)
- self.dockAnki.visibilityChanged.connect(self.onVisibilityChanged)
- self.dockKanji.visibilityChanged.connect(self.onVisibilityChanged)
- self.dockVocab.visibilityChanged.connect(self.onVisibilityChanged)
- self.listDefinitions.itemDoubleClicked.connect(self.onDefinitionDoubleClicked)
- self.textKanjiDefs.anchorClicked.connect(self.onKanjiDefsAnchorClicked)
- self.textKanjiSearch.returnPressed.connect(self.onKanjiDefSearchReturn)
- self.textVocabDefs.anchorClicked.connect(self.onVocabDefsAnchorClicked)
- self.textVocabSearch.returnPressed.connect(self.onVocabDefSearchReturn)
- self.updates.updateResult.connect(self.onUpdaterSearchResult)
-
- if self.preferences['checkForUpdates']:
- self.updates.start()
-
-
- def applyPreferences(self):
- 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['tags'])
- self.applyPreferencesContent()
-
- if self.preferences['firstRun']:
- QtGui.QMessageBox.information(
- self,
- 'Yomichan',
- 'This may be the first time you are running Yomichan.\n' \
- 'Please take some time to configure this extension.'
- )
-
- self.onActionPreferences()
-
-
- def applyPreferencesContent(self):
- palette = self.textContent.palette()
- palette.setColor(QtGui.QPalette.Base, QtGui.QColor(self.preferences['bgColor']))
- palette.setColor(QtGui.QPalette.Text, QtGui.QColor(self.preferences['fgColor']))
- self.textContent.setPalette(palette)
-
- self.textContent.setReadOnly(not self.preferences['allowEditing'])
- self.textContent.setAttribute(QtCore.Qt.WA_InputMethodEnabled)
-
- font = self.textContent.font()
- font.setFamily(self.preferences['fontFamily'])
- font.setPointSize(self.preferences['fontSize'] + self.zoom)
- self.textContent.setLineWrapMode(QtGui.QPlainTextEdit.WidgetWidth if self.preferences['wordWrap'] else QtGui.QPlainTextEdit.NoWrap)
- self.textContent.setFont(font)
-
- self.actionToggleWrap.setChecked(self.preferences['wordWrap'])
-
-
- def closeEvent(self, event):
- self.closeFile()
- self.preferences['windowState'] = str(self.saveState().toBase64())
- self.preferences.save()
-
- if self.anki is not None:
- self.anki.stopEditing()
-
- if self.closed is not None:
- self.closed()
-
-
- def keyPressEvent(self, event):
- if event.key() == QtCore.Qt.Key_Shift:
- self.updateSampleFromPosition()
- elif ord('0') <= event.key() <= ord('9'):
- index = event.key() - ord('0') - 1
- if index < 0:
- index = 9
- if event.modifiers() & QtCore.Qt.ShiftModifier:
- if event.modifiers() & QtCore.Qt.ControlModifier:
- self.executeKanjiCommand('addKanji', index)
- else:
- if event.modifiers() & QtCore.Qt.ControlModifier:
- self.executeVocabCommand('addVocabExp', index)
- if event.modifiers() & QtCore.Qt.AltModifier:
- self.executeVocabCommand('addVocabReading', index)
- elif event.key() == ord('[') and self.state.scanPosition > 0:
- self.state.scanPosition -= 1
- self.updateSampleFromPosition()
- elif event.key() == ord(']') and self.state.scanPosition < len(self.textContent.toPlainText()) - 1:
- self.state.scanPosition += 1
- self.updateSampleFromPosition()
-
-
- def dragEnterEvent(self, event):
- if event.mimeData().hasUrls():
- event.acceptProposedAction()
-
-
- def dropEvent(self, event):
- url = event.mimeData().urls()[0]
- self.openFile(url.toLocalFile())
-
-
- def moveEvent(self, event):
- self.preferences['windowPosition'] = event.pos().x(), event.pos().y()
-
-
- def resizeEvent(self, event):
- self.preferences['windowSize'] = event.size().width(), event.size().height()
-
-
- def onActionOpen(self):
- filename = QtGui.QFileDialog.getOpenFileName(
- parent=self,
- caption='Select a file to open',
- filter='Text files (*.txt);;All files (*.*)'
- )
- if filename:
- self.openFile(filename)
-
-
- def onActionKindleDeck(self):
- filename = QtGui.QFileDialog.getOpenFileName(
- parent=self,
- caption='Select a Kindle deck to import',
- filter='Deck files (*.db)'
- )
- if filename:
- words = reader_util.extractKindleDeck(filename)
- self.importWordList(words)
-
-
- def onActionWordList(self):
- filename = QtGui.QFileDialog.getOpenFileName(
- parent=self,
- caption='Select a word list file to import',
- filter='Text files (*.txt);;All files (*.*)'
- )
- if filename:
- words = reader_util.extractWordList(filename)
- self.importWordList(words)
-
-
- def onActionPreferences(self):
- dialog = preferences.DialogPreferences(self, self.preferences, self.anki)
- if dialog.exec_() == QtGui.QDialog.Accepted:
- self.applyPreferencesContent()
-
-
- def onActionAbout(self):
- dialog = about.DialogAbout(self)
- dialog.exec_()
-
-
- def onActionZoomIn(self):
- font = self.textContent.font()
- if font.pointSize() < 72:
- font.setPointSize(font.pointSize() + 1)
- self.textContent.setFont(font)
- self.zoom += 1
-
-
- def onActionZoomOut(self):
- font = self.textContent.font()
- if font.pointSize() > 1:
- font.setPointSize(font.pointSize() - 1)
- self.textContent.setFont(font)
- self.zoom -= 1
-
-
- def onActionZoomReset(self):
- if self.zoom:
- font = self.textContent.font()
- font.setPointSize(font.pointSize() - self.zoom)
- self.textContent.setFont(font)
- self.zoom = 0
-
-
- def onActionFind(self):
- searchText = self.state.searchText
-
- cursor = self.textContent.textCursor()
- if cursor.hasSelection():
- searchText = cursor.selectedText()
-
- searchText, ok = QtGui.QInputDialog.getText(self, 'Find', 'Search text:', text=searchText)
- if searchText and ok:
- self.findText(searchText)
-
-
- def onActionFindNext(self):
- if self.state.searchText:
- self.findText(self.state.searchText)
-
-
- def onActionToggleWrap(self, wrap):
- self.preferences['wordWrap'] = wrap
- mode = QtGui.QPlainTextEdit.WidgetWidth if self.preferences['wordWrap'] else QtGui.QPlainTextEdit.NoWrap
- self.textContent.setLineWrapMode(mode)
-
-
- def onActionHomepage(self):
- url = QtCore.QUrl('https://foosoft.net/projects/yomichan')
- QtGui.QDesktopServices().openUrl(url)
-
-
- def onVocabDefsAnchorClicked(self, url):
- command, index = unicode(url.toString()).split(':')
- self.executeVocabCommand(command, int(index))
-
-
- def onKanjiDefsAnchorClicked(self, url):
- command, index = unicode(url.toString()).split(':')
- self.executeKanjiCommand(command, int(index))
-
-
- def onVocabDefSearchReturn(self):
- text = unicode(self.textVocabSearch.text())
- self.state.vocabDefs, length = self.language.findTerm(text, True)
- self.updateVocabDefs()
- if self.dockKanji.isVisible():
- self.state.kanjiDefs = self.language.findKanji(text)
- self.updateKanjiDefs()
-
-
- def onKanjiDefSearchReturn(self):
- text = unicode(self.textKanjiSearch.text())
- self.state.kanjiDefs = self.language.findKanji(text)
- self.updateKanjiDefs()
-
-
- def onDefinitionDoubleClicked(self, item):
- if self.anki is not None:
- row = self.listDefinitions.row(item)
- self.anki.browseNote(self.facts[row])
-
-
- def onVisibilityChanged(self, visible):
- self.actionToggleAnki.setChecked(self.dockAnki.isVisible())
- self.actionToggleVocab.setChecked(self.dockVocab.isVisible())
- self.actionToggleKanji.setChecked(self.dockKanji.isVisible())
-
-
- def onUpdaterSearchResult(self, versions):
- if versions['latest'] > constants.c['appVersion']:
- dialog = updates.DialogUpdates(self, versions)
- dialog.exec_()
-
-
- def onContentMouseMove(self, event):
- QtGui.QPlainTextEdit.mouseMoveEvent(self.textContent, event)
- self.updateSampleMouseEvent(event)
-
-
- def onContentMousePress(self, event):
- QtGui.QPlainTextEdit.mousePressEvent(self.textContent, event)
- self.updateSampleMouseEvent(event)
-
-
- def openFile(self, filename):
- try:
- filename = unicode(filename)
- with open(filename) as fp:
- content = fp.read()
- except IOError:
- self.setStatus(u'Failed to load file {0}'.format(filename))
- QtGui.QMessageBox.critical(self, 'Yomichan', 'Cannot open file for read')
- return
-
- self.closeFile()
-
- self.state.filename = filename
- self.state.scanPosition = self.preferences.filePosition(filename)
- if self.state.scanPosition > len(content):
- self.state.scanPosition = 0
-
- self.updateRecentFile()
- self.updateRecentFiles()
-
- content, encoding = reader_util.decodeContent(content)
- if self.preferences['stripReadings']:
- content = reader_util.stripReadings(content)
-
- self.textContent.setPlainText(content)
- if self.state.scanPosition > 0:
- cursor = self.textContent.textCursor()
- cursor.setPosition(self.state.scanPosition)
- self.textContent.setTextCursor(cursor)
- self.textContent.centerCursor()
-
- self.setStatus(u'Loaded file {0}'.format(filename))
- self.setWindowTitle(u'Yomichan - {0} ({1})'.format(os.path.basename(filename), encoding))
-
-
- def closeFile(self):
- if self.preferences['rememberTextContent']:
- self.preferences['textContent'] = unicode(self.textContent.toPlainText())
-
- self.setWindowTitle('Yomichan')
- self.textContent.setPlainText(u'')
- self.updateRecentFile(False)
- self.state = self.State()
-
-
- def findText(self, text):
- content = unicode(self.textContent.toPlainText())
- index = content.find(unicode(text), self.state.searchPosition)
-
- if index == -1:
- wrap = self.state.searchPosition != 0
- self.state.searchPosition = 0
- if wrap:
- self.findText(text)
- else:
- QtGui.QMessageBox.information(self, 'Yomichan', 'Search text not found')
- else:
- self.state.searchPosition = index + len(text)
- cursor = self.textContent.textCursor()
- cursor.setPosition(index, QtGui.QTextCursor.MoveAnchor)
- cursor.setPosition(self.state.searchPosition, QtGui.QTextCursor.KeepAnchor)
- self.textContent.setTextCursor(cursor)
-
- self.state.searchText = text
-
-
- def ankiAddFact(self, profile, markup):
- if markup is None:
- return False
-
- if self.anki is None:
- return False
-
- profile = self.preferences['profiles'].get(profile)
- if profile is None:
- return False
-
- fields = reader_util.formatFields(profile['fields'], markup)
- tagsSplit = reader_util.splitTags(unicode(self.comboTags.currentText()))
- tagsJoined = ' '.join(tagsSplit)
-
- tagIndex = self.comboTags.findText(tagsJoined)
- if tagIndex > 0:
- self.comboTags.removeItem(tagIndex)
- if tagIndex != 0:
- self.comboTags.insertItem(0, tagsJoined)
- self.preferences.updateFactTags(tagsJoined)
-
- factId = self.anki.addNote(profile['deck'], profile['model'], fields, tagsSplit, None)
- if factId is None:
- return False
-
- self.facts.append(factId)
- self.listDefinitions.addItem(markup['summary'])
- self.listDefinitions.setCurrentRow(self.listDefinitions.count() - 1)
- self.setStatus(u'Added fact {0}; {1} new fact(s) total'.format(markup['summary'], len(self.facts)))
-
- self.updateVocabDefs(scroll=True)
- self.updateKanjiDefs(scroll=True)
- return True
-
-
- def ankiIsFactValid(self, profile, markup):
- if markup is None:
- return False
-
- if self.anki is None:
- return False
-
- profile = self.preferences['profiles'].get(profile)
- if profile is None:
- return False
-
- fields = reader_util.formatFields(profile['fields'], markup)
- return self.anki.canAddNote(profile['deck'], profile['model'], fields)
-
-
- def executeVocabCommand(self, command, index):
- if index >= len(self.state.vocabDefs):
- return
-
- definition = self.state.vocabDefs[index]
- if command == 'addVocabExp':
- markup = reader_util.markupVocabExp(definition)
- self.ankiAddFact('vocab', markup)
- if command == 'addVocabReading':
- markup = reader_util.markupVocabReading(definition)
- self.ankiAddFact('vocab', markup)
- elif command == 'copyVocabDef':
- reader_util.copyVocabDef(definition)
-
-
- def executeKanjiCommand(self, command, index):
- if index >= len(self.state.kanjiDefs):
- return
-
- definition = self.state.kanjiDefs[index]
- if command == 'addKanji':
- markup = reader_util.markupKanji(definition)
- self.ankiAddFact('kanji', markup)
- elif command == 'copyKanjiDef':
- reader_util.copyKanjiDef(definition)
-
-
- def updateSampleMouseEvent(self, event):
- cursor = self.textContent.cursorForPosition(event.pos())
- self.state.scanPosition = cursor.position()
- if event.buttons() & QtCore.Qt.MidButton or event.modifiers() & QtCore.Qt.ShiftModifier:
- self.updateSampleFromPosition()
-
-
- def updateSampleFromPosition(self):
- samplePosStart = self.state.scanPosition
- samplePosEnd = self.state.scanPosition + self.preferences['scanLength']
-
- content = unicode(self.textContent.toPlainText())
- contentSample = content[samplePosStart:samplePosEnd]
- contentSampleFlat = contentSample.replace(u'\n', u'')
-
- cursor = self.textContent.textCursor()
-
- if len(contentSampleFlat) == 0:
- cursor.clearSelection()
- self.textContent.setTextCursor(cursor)
- return
-
- lengthMatched = 0
- if self.dockVocab.isVisible():
- self.state.vocabDefs, lengthMatched = self.language.findTerm(contentSampleFlat)
- sentence = reader_util.findSentence(content, samplePosStart)
- for definition in self.state.vocabDefs:
- definition['sentence'] = sentence
- self.updateVocabDefs()
-
- if self.dockKanji.isVisible():
- if lengthMatched == 0:
- self.state.kanjiDefs = self.language.findKanji(contentSampleFlat[0])
- if len(self.state.kanjiDefs) > 0:
- lengthMatched = 1
- else:
- self.state.kanjiDefs = self.language.findKanji(contentSampleFlat[:lengthMatched])
- self.updateKanjiDefs()
-
- lengthSelect = 0
- for c in contentSample:
- if lengthMatched <= 0:
- break
- lengthSelect += 1
- if c != u'\n':
- lengthMatched -= 1
-
- cursor.setPosition(samplePosStart, QtGui.QTextCursor.MoveAnchor)
- cursor.setPosition(samplePosStart + lengthSelect, QtGui.QTextCursor.KeepAnchor)
- self.textContent.setTextCursor(cursor)
-
-
- def clearRecentFiles(self):
- self.preferences.clearRecentFiles()
- self.updateRecentFiles()
-
-
- def updateRecentFiles(self):
- self.menuOpenRecent.clear()
-
- filenames = self.preferences.recentFiles()
- if len(filenames) == 0:
- return
-
- for filename in filenames:
- self.menuOpenRecent.addAction(filename, lambda f=filename: self.openFile(f))
-
- self.menuOpenRecent.addSeparator()
- self.menuOpenRecent.addAction('Clear file history', self.clearRecentFiles)
-
-
- def updateRecentFile(self, addIfNeeded=True):
- if self.state.filename:
- if addIfNeeded or self.state.filename in self.preferences.recentFiles():
- self.preferences.updateRecentFile(self.state.filename, self.state.scanPosition)
-
-
- def updateDefs(self, defs, builder, control, **options):
- scrollbar = control.verticalScrollBar()
- position = scrollbar.sliderPosition()
-
- html = builder(defs, self.ankiIsFactValid)
- control.setHtml(html)
-
- if options.get('scroll', False):
- scrollbar.setSliderPosition(position)
-
-
- def updateVocabDefs(self, **options):
- self.updateDefs(
- self.state.vocabDefs,
- reader_util.buildVocabDefs,
- self.textVocabDefs,
- **options
- )
-
-
- def updateKanjiDefs(self, **options):
- self.updateDefs(
- self.state.kanjiDefs,
- reader_util.buildKanjiDefs,
- self.textKanjiDefs,
- **options
- )
-
-
- def importWordList(self, words):
- self.state.vocabDefs = []
- self.state.kanjiDefs = []
-
- for word in words:
- if self.dockVocab.isVisible():
- self.state.vocabDefs += self.language.dictionary.findTerm(word)
-
- if self.dockKanji.isVisible():
- self.state.kanjiDefs += self.language.findKanji(word)
-
- self.updateVocabDefs(scroll=True)
- self.updateKanjiDefs(scroll=True)
-
-
- def setStatus(self, status):
- self.statusBar.showMessage(status)
diff --git a/yomi_base/reader_util.py b/yomi_base/reader_util.py
deleted file mode 100644
index 0ff4fd9..0000000
--- a/yomi_base/reader_util.py
+++ /dev/null
@@ -1,293 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (C) 2013 Alex Yatskov
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-
-from PyQt4 import QtGui
-import re
-import codecs
-import sqlite3
-
-
-def decodeContent(content):
- encodings = ['utf-8', 'shift_jis', 'euc-jp', 'utf-16']
- errors = {}
-
- for encoding in encodings:
- try:
- return content.decode(encoding), encoding
- except UnicodeDecodeError, e:
- errors[encoding] = e[2]
-
- encoding = sorted(errors, key=errors.get, reverse=True)[0]
- return content.decode(encoding, 'replace'), encoding
-
-
-def stripReadings(content):
- return re.sub(u'《[^》]+》', u'', content)
-
-
-def findSentence(content, position):
- quotesFwd = {u'「': u'」', u'『': u'』', u"'": u"'", u'"': u'"'}
- quotesBwd = {u'」': u'「', u'』': u'『', u"'": u"'", u'"': u'"'}
- terminators = u'。..??!!'
-
- quoteStack = []
-
- start = 0
- for i in xrange(position, start, -1):
- c = content[i]
-
- if not quoteStack and (c in terminators or c in quotesFwd or c == '\n'):
- start = i + 1
- break
-
- if quoteStack and c == quoteStack[0]:
- quoteStack.pop()
- elif c in quotesBwd:
- quoteStack.insert(0, quotesBwd[c])
-
- quoteStack = []
-
- end = len(content)
- for i in xrange(position, end):
- c = content[i]
-
- if not quoteStack:
- if c in terminators:
- end = i + 1
- break
- elif c in quotesBwd:
- end = i
- break
-
- if quoteStack and c == quoteStack[0]:
- quoteStack.pop()
- elif c in quotesFwd:
- quoteStack.insert(0, quotesFwd[c])
-
- return content[start:end].strip()
-
-
-def formatFields(fields, markup):
- result = {}
- for field, value in fields.items():
- try:
- result[field] = value.format(**markup)
- except KeyError:
- pass
- except ValueError:
- pass
-
- return result
-
-
-def splitTags(tags):
- return filter(lambda tag: tag.strip(), re.split('[;,\s]', tags))
-
-
-def markupVocabExp(definition):
- if definition['reading']:
- summary = u'{expression} [{reading}]'.format(**definition)
- else:
- summary = u'{expression}'.format(**definition)
-
- return {
- 'expression': definition['expression'],
- 'reading': definition['reading'] or u'',
- 'glossary': '; '.join(definition['glossary']),
- 'sentence': definition.get('sentence'),
- 'summary': summary
- }
-
-
-def markupVocabReading(definition):
- if definition['reading']:
- return {
- 'expression': definition['reading'],
- 'reading': u'',
- 'glossary': '; '.join(definition['glossary']),
- 'sentence': definition.get('sentence'),
- 'summary': definition['reading']
- }
-
-
-def copyVocabDef(definition):
- glossary = '; '.join(definition['glossary'])
- if definition['reading']:
- result = u'{0}\t{1}\t{2}\n'.format(definition['expression'], definition['reading'], glossary)
- else:
- result = u'{0}\t{1}\n'.format(definition['expression'], glossary)
-
- QtGui.QApplication.clipboard().setText(result)
-
-
-def markupKanji(definition):
- return {
- 'character': definition['character'],
- 'onyomi': ', '.join(definition['onyomi']),
- 'kunyomi': ', '.join(definition['kunyomi']),
- 'glossary': ', '.join(definition['glossary']),
- 'summary': definition['character']
- }
-
-
-def copyKanjiDef(definition):
- result = u'{0}\t{1}\t{2}\t{3}'.format(
- definition['character'],
- ', '.join(definition['kunyomi']),
- ', '.join(definition['onyomi']),
- ', '.join(definition['glossary'])
- )
-
- QtGui.QApplication.clipboard().setText(result)
-
-
-def buildDefHeader():
- palette = QtGui.QApplication.palette()
- toolTipBg = palette.color(QtGui.QPalette.Window).name()
- toolTipFg = palette.color(QtGui.QPalette.WindowText).name()
-
- return u'''
- '''.format(toolTipBg, toolTipFg)
-
-
-def buildDefFooter():
- return ''
-
-
-def buildEmpty():
- return u'''
- No definitions to display.
- Mouse over text with the middle mouse button or shift key pressed to search.
- You can also also input terms in the search box below.'''
-
-
-def buildVocabDef(definition, index, query):
- reading = u''
- if definition['reading']:
- reading = u'[{0}]
'.format(definition['reading'])
-
- rules = u''
- if definition.get('rules'):
- rules = ' < '.join(definition['rules'])
- rules = '({0})
'.format(rules)
-
- links = ''.format(index)
- if query is not None:
- if query('vocab', markupVocabExp(definition)):
- links += ''.format(index)
- if query('vocab', markupVocabReading(definition)):
- links += ''.format(index)
-
- glossary = u'
'
- for g in definition['glossary']:
- glossary += u'- {0}
'.format(g)
- glossary += u'
'
-
- expression = u''
- if 'P' in definition['tags']:
- expression += u'{}'.format(definition['expression'])
- else:
- expression += definition['expression']
- expression += u''
-
- html = u'''
- {links}
- {expression}
- {reading}
- {rules}
- {glossary}
-
'''.format(
- links = links,
- expression = expression,
- reading = reading,
- glossary = glossary,
- rules = rules
- )
-
- return html
-
-
-def buildVocabDefs(definitions, query):
- html = buildDefHeader()
- if len(definitions) > 0:
- for i, definition in enumerate(definitions):
- html += buildVocabDef(definition, i, query)
- else:
- html += buildEmpty()
-
- return html + buildDefFooter()
-
-
-def buildKanjiDef(definition, index, query):
- links = ''.format(index)
- if query is not None and query('kanji', markupKanji(definition)):
- links += ''.format(index)
-
- readings = ', '.join(definition['kunyomi'] + definition['onyomi'])
- glossary = ', '.join(definition['glossary'])
-
- html = u'''
- {links}
- {expression}
- [{reading}]
- {glossary}
-
'''.format(
- links = links,
- expression = definition['character'],
- reading = readings,
- glossary = glossary
- )
-
- return html
-
-
-def buildKanjiDefs(definitions, query):
- html = buildDefHeader()
-
- if len(definitions) > 0:
- for i, definition in enumerate(definitions):
- html += buildKanjiDef(definition, i, query)
- else:
- html += buildEmpty()
-
- return html + buildDefFooter()
-
-
-def extractKindleDeck(filename):
- words = []
-
- try:
- with sqlite3.connect(unicode(filename)) as db:
- for row in db.execute('select word from WORDS'):
- words.append(row[0])
- except sqlite3.OperationalError:
- pass
-
- return words
-
-
-def extractWordList(filename):
- words = []
-
- with codecs.open(unicode(filename), 'rb', 'utf-8') as fp:
- words = re.split('[;,\s]', fp.read())
-
- return filter(None, words)
diff --git a/yomi_base/updates.py b/yomi_base/updates.py
deleted file mode 100644
index 57a6970..0000000
--- a/yomi_base/updates.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (C) 2013 Alex Yatskov
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-
-from PyQt4 import QtCore, QtGui
-import constants
-import gen.updates_ui
-import json
-import urllib2
-
-
-class DialogUpdates(QtGui.QDialog, gen.updates_ui.Ui_DialogUpdates):
- def __init__(self, parent, versions):
- QtGui.QDialog.__init__(self, parent)
- self.setupUi(self)
-
- self.updateHtml(versions)
- self.labelUpdates.setText(
- unicode(self.labelUpdates.text()).format(
- constants.c['appVersion'],
- versions['latest']
- )
- )
-
-
- def updateHtml(self, versions):
- html = ''
-
- for update in versions['updates']:
- version = update.get('version')
- if version > constants.c['appVersion']:
- html += 'Version {0}'.format(version)
- html += ''
- for feature in update['features']:
- html += '- {0}
'.format(feature)
- html += '
'
-
- self.textBrowser.setHtml(html)
-
-
-class UpdateFinder(QtCore.QThread):
- updateResult = QtCore.pyqtSignal(dict)
-
- def run(self):
- latest = constants.c['appVersion']
- updates = []
-
- try:
- fp = urllib2.urlopen('https://foosoft.net/projects/yomichan/dl/updates.json')
- updates = json.loads(fp.read())
- fp.close()
-
- for update in updates:
- latest = max(latest, update.get('version'))
- except:
- pass
- finally:
- self.updateResult.emit({'latest': latest, 'updates': updates})
diff --git a/yomichan.py b/yomichan.py
deleted file mode 100755
index 8df5bbd..0000000
--- a/yomichan.py
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
-
-# Copyright (C) 2013 Alex Yatskov
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-
-from PyQt4 import QtGui
-from yomi_base import japanese
-from yomi_base.anki_connect import AnkiConnect
-from yomi_base.preference_data import Preferences
-from yomi_base.reader import MainWindowReader
-import sys
-
-
-class Yomichan:
- def __init__(self):
- self.language = japanese.initLanguage()
-
- self.preferences = Preferences()
- self.preferences.load()
-
-
-class YomichanPlugin(Yomichan):
- def __init__(self):
- Yomichan.__init__(self)
-
- self.toolIconVisible = False
- self.window = None
- self.anki = anki_bridge.Anki()
- self.parent = self.anki.window()
- self.ankiConnect = AnkiConnect(self.anki, self.preferences)
-
- separator = QtGui.QAction(self.parent)
- separator.setSeparator(True)
- self.anki.addUiAction(separator)
-
- action = QtGui.QAction(QtGui.QIcon(':/img/img/icon_logo_32.png'), '&Yomichan...', self.parent)
- action.setIconVisibleInMenu(True)
- action.setShortcut('Ctrl+Y')
- action.triggered.connect(self.onShowRequest)
- self.anki.addUiAction(action)
-
-
- def onShowRequest(self):
- if self.window:
- self.window.setVisible(True)
- self.window.activateWindow()
- else:
- self.window = MainWindowReader(
- self.parent,
- self.preferences,
- self.language,
- None,
- self.anki,
- self.onWindowClose
- )
- self.window.show()
-
-
- def onWindowClose(self):
- self.window = None
-
-
-class YomichanStandalone(Yomichan):
- def __init__(self):
- Yomichan.__init__(self)
-
- self.application = QtGui.QApplication(sys.argv)
- self.window = MainWindowReader(
- None,
- self.preferences,
- self.language,
- filename=sys.argv[1] if len(sys.argv) >= 2 else None
- )
-
- self.window.show()
- self.application.exec_()
-
-
-if __name__ == '__main__':
- instance = YomichanStandalone()
-else:
- from yomi_base import anki_bridge
- instance = YomichanPlugin()