commit
4007dbbb28
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2018 sthoo <sth201807@gmail.com>
|
||||
#
|
||||
@ -19,6 +19,7 @@
|
||||
|
||||
import ssl
|
||||
import sys
|
||||
|
||||
from anki.hooks import addHook
|
||||
from anki.utils import isMac
|
||||
|
||||
@ -28,6 +29,7 @@ if isMac:
|
||||
|
||||
############## other config here ##################
|
||||
shortcut = ('Ctrl+Alt' if isMac else 'Ctrl') + '+Q'
|
||||
|
||||
###################################################
|
||||
|
||||
|
||||
@ -42,7 +44,6 @@ def start_here():
|
||||
fastwq.browser_menu()
|
||||
fastwq.context_menu()
|
||||
fastwq.customize_addcards()
|
||||
# if fastwq.config.auto_update:
|
||||
# fastwq.check_updates(True)
|
||||
|
||||
addHook("profileLoaded", start_here)
|
||||
|
||||
addHook("profileLoaded", start_here)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2018 sthoo <sth201807@gmail.com>
|
||||
#
|
||||
@ -18,24 +18,23 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from operator import itemgetter
|
||||
|
||||
from anki.hooks import addHook, remHook, wrap
|
||||
from aqt import mw
|
||||
from aqt.qt import *
|
||||
from anki.hooks import addHook, wrap, remHook
|
||||
from aqt.addcards import AddCards
|
||||
from aqt.utils import showInfo, shortcut, downArrow
|
||||
from .gui import show_options, show_about_dialog #, check_updates
|
||||
from .query import query_from_browser, query_from_editor_fields
|
||||
from .context import config, APP_ICON, Config
|
||||
from aqt.qt import *
|
||||
from aqt.utils import downArrow, shortcut, showInfo
|
||||
|
||||
from .context import APP_ICON, Config, config
|
||||
from .gui import show_about_dialog, show_options # , check_updates
|
||||
from .lang import _
|
||||
from .query import query_from_browser, query_from_editor_fields
|
||||
from .service import service_pool
|
||||
from .utils import get_icon
|
||||
|
||||
|
||||
__all__ = [
|
||||
'browser_menu', 'customize_addcards',
|
||||
'config_menu', 'context_menu'
|
||||
] # 'check_updates',
|
||||
|
||||
'browser_menu', 'customize_addcards', 'config_menu', 'context_menu'
|
||||
] # 'check_updates',
|
||||
|
||||
have_setup = False
|
||||
my_shortcut = ''
|
||||
@ -52,18 +51,23 @@ def set_options_def(mid, i):
|
||||
data = dict()
|
||||
data[mid] = conf
|
||||
config.update(data)
|
||||
|
||||
|
||||
# end set_options_def
|
||||
|
||||
|
||||
def browser_menu():
|
||||
"""
|
||||
add add-on's menu to browser window
|
||||
"""
|
||||
|
||||
def on_setup_menus(browser):
|
||||
"""
|
||||
on browser setupMenus was called
|
||||
"""
|
||||
# main menu
|
||||
menu = browser.form.menubar.addMenu("FastWQ")
|
||||
|
||||
# menu gen
|
||||
def init_fastwq_menu():
|
||||
try:
|
||||
@ -78,6 +82,7 @@ def browser_menu():
|
||||
menu.addAction(action)
|
||||
# Options
|
||||
action = QAction(_('OPTIONS'), browser)
|
||||
|
||||
def _show_options():
|
||||
model_id = -1
|
||||
for note_id in browser.selectedNotes():
|
||||
@ -85,26 +90,31 @@ def browser_menu():
|
||||
model_id = note.model()['id']
|
||||
break
|
||||
show_options(browser, model_id)
|
||||
|
||||
action.triggered.connect(_show_options)
|
||||
menu.addAction(action)
|
||||
|
||||
# Default configs
|
||||
menu.addSeparator()
|
||||
b = False
|
||||
for m in sorted(browser.mw.col.models.all(), key=itemgetter("name")):
|
||||
for m in sorted(
|
||||
browser.mw.col.models.all(), key=itemgetter("name")):
|
||||
conf = config.get_maps(m['id'])
|
||||
conf = {'list': [conf], 'def': 0} if isinstance(conf, list) else conf
|
||||
conf = {
|
||||
'list': [conf],
|
||||
'def': 0
|
||||
} if isinstance(conf, list) else conf
|
||||
maps_list = conf['list']
|
||||
if len(maps_list) > 1:
|
||||
submenu = menu.addMenu(m['name'])
|
||||
for i, maps in enumerate(maps_list):
|
||||
submenu.addAction(
|
||||
_OK_ICON if i==conf['def'] else _NULL_ICON,
|
||||
_('CONFIG_INDEX') % (i+1) if isinstance(maps, list) else maps['name'],
|
||||
lambda mid=m['id'], i=i: set_options_def(mid, i)
|
||||
)
|
||||
_OK_ICON if i == conf['def'] else _NULL_ICON,
|
||||
_('CONFIG_INDEX') % (i + 1) if isinstance(
|
||||
maps, list) else maps['name'],
|
||||
lambda mid=m['id'], i=i: set_options_def(mid, i))
|
||||
b = True
|
||||
if b:
|
||||
if b:
|
||||
menu.addSeparator()
|
||||
|
||||
# # check update
|
||||
@ -120,6 +130,7 @@ def browser_menu():
|
||||
# end init_fastwq_menu
|
||||
init_fastwq_menu()
|
||||
addHook('config.update', init_fastwq_menu)
|
||||
|
||||
addHook('browser.setupMenus', on_setup_menus)
|
||||
|
||||
|
||||
@ -127,6 +138,7 @@ def customize_addcards():
|
||||
"""
|
||||
add button to addcards window
|
||||
"""
|
||||
|
||||
def add_query_button(self):
|
||||
'''
|
||||
add a button in add card window
|
||||
@ -136,44 +148,47 @@ def customize_addcards():
|
||||
# button
|
||||
fastwqBtn = QPushButton(_("QUERY") + u" " + downArrow())
|
||||
fastwqBtn.setShortcut(QKeySequence(my_shortcut))
|
||||
fastwqBtn.setToolTip(
|
||||
_(u"Shortcut: %s") % shortcut(my_shortcut)
|
||||
)
|
||||
fastwqBtn.setToolTip(_(u"Shortcut: %s") % shortcut(my_shortcut))
|
||||
bb.addButton(fastwqBtn, ar)
|
||||
|
||||
# signal
|
||||
def onQuery(e):
|
||||
if isinstance(e, QMouseEvent):
|
||||
if e.buttons() & Qt.LeftButton:
|
||||
menu = QMenu(self)
|
||||
menu.addAction(_("ALL_FIELDS"), lambda: query_from_editor_fields(self.editor), QKeySequence(my_shortcut))
|
||||
menu.addAction(
|
||||
_("ALL_FIELDS"),
|
||||
lambda: query_from_editor_fields(self.editor),
|
||||
QKeySequence(my_shortcut))
|
||||
# default options
|
||||
mid = self.editor.note.model()['id']
|
||||
conf = config.get_maps(mid)
|
||||
conf = {'list': [conf], 'def': 0} if isinstance(conf, list) else conf
|
||||
conf = {
|
||||
'list': [conf],
|
||||
'def': 0
|
||||
} if isinstance(conf, list) else conf
|
||||
maps_list = conf['list']
|
||||
if len(maps_list) > 1:
|
||||
menu.addSeparator()
|
||||
for i, maps in enumerate(maps_list):
|
||||
menu.addAction(
|
||||
_OK_ICON if i==conf['def'] else _NULL_ICON,
|
||||
_('CONFIG_INDEX') % (i+1) if isinstance(maps, list) else maps['name'],
|
||||
lambda mid=mid, i=i: set_options_def(mid, i)
|
||||
)
|
||||
_OK_ICON if i == conf['def'] else _NULL_ICON,
|
||||
_('CONFIG_INDEX') % (i + 1) if isinstance(
|
||||
maps, list) else maps['name'],
|
||||
lambda mid=mid, i=i: set_options_def(mid, i))
|
||||
menu.addSeparator()
|
||||
# end default options
|
||||
menu.addAction(_("OPTIONS"), lambda: show_options(self, self.editor.note.model()['id']))
|
||||
menu.exec_(fastwqBtn.mapToGlobal(QPoint(0, fastwqBtn.height())))
|
||||
menu.exec_(
|
||||
fastwqBtn.mapToGlobal(QPoint(0, fastwqBtn.height())))
|
||||
else:
|
||||
query_from_editor_fields(self.editor)
|
||||
|
||||
fastwqBtn.mousePressEvent = onQuery
|
||||
fastwqBtn.clicked.connect(onQuery)
|
||||
|
||||
AddCards.setupButtons = wrap(
|
||||
AddCards.setupButtons,
|
||||
add_query_button,
|
||||
"after"
|
||||
)
|
||||
|
||||
AddCards.setupButtons = wrap(AddCards.setupButtons, add_query_button,
|
||||
"after")
|
||||
|
||||
|
||||
def config_menu():
|
||||
@ -187,6 +202,7 @@ def config_menu():
|
||||
|
||||
def context_menu():
|
||||
'''mouse right click menu'''
|
||||
|
||||
def on_setup_menus(web_view, menu):
|
||||
"""
|
||||
add context menu to webview
|
||||
@ -217,32 +233,30 @@ def context_menu():
|
||||
name = s.title + ' :-> ' + s.fields[dict_fld_ord]
|
||||
if name not in names:
|
||||
names.append(name)
|
||||
curr_flds.append({
|
||||
'name': name,
|
||||
'def': i
|
||||
})
|
||||
curr_flds.append({'name': name, 'def': i})
|
||||
service_pool.put(s)
|
||||
|
||||
submenu = menu.addMenu(_('QUERY'))
|
||||
submenu.addAction(_('ALL_FIELDS'), lambda: query_from_editor_fields(web_view.editor), QKeySequence(my_shortcut))
|
||||
submenu.addAction(
|
||||
_('ALL_FIELDS'), lambda: query_from_editor_fields(web_view.editor),
|
||||
QKeySequence(my_shortcut))
|
||||
if len(curr_flds) > 0:
|
||||
# quer hook method
|
||||
def query_from_editor_hook(i):
|
||||
conf = config.get_maps(current_model_id)
|
||||
maps_old_def = 0 if isinstance(conf, list) else conf.get('def', 0)
|
||||
maps_old_def = 0 if isinstance(conf, list) else conf.get(
|
||||
'def', 0)
|
||||
set_options_def(current_model_id, i)
|
||||
query_from_editor_fields(
|
||||
web_view.editor,
|
||||
fields=[web_view.editor.currentField]
|
||||
)
|
||||
web_view.editor, fields=[web_view.editor.currentField])
|
||||
set_options_def(current_model_id, maps_old_def)
|
||||
|
||||
# sub menu
|
||||
#flds_menu = submenu.addMenu(_('CURRENT_FIELDS'))
|
||||
# flds_menu = submenu.addMenu(_('CURRENT_FIELDS'))
|
||||
submenu.addSeparator()
|
||||
for c in curr_flds:
|
||||
submenu.addAction(c['name'],
|
||||
lambda i=c['def']: query_from_editor_hook(i)
|
||||
)
|
||||
submenu.addAction(
|
||||
c['name'], lambda i=c['def']: query_from_editor_hook(i))
|
||||
submenu.addSeparator()
|
||||
submenu.addAction(_("OPTIONS"), lambda: show_options(web_view, web_view.editor.note.model()['id']))
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2018 sthoo <sth201807@gmail.com>
|
||||
#
|
||||
@ -17,13 +17,12 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from .lang import _
|
||||
|
||||
|
||||
__all__ = ['VERSION', 'Endpoint', 'Template']
|
||||
|
||||
VERSION = 'v1.3.5'
|
||||
VERSION = 'v2.0.0b'
|
||||
|
||||
|
||||
class Endpoint:
|
||||
repository = u'https://github.com/sth2018/FastWordQuery'
|
||||
@ -36,10 +35,13 @@ class Endpoint:
|
||||
|
||||
class Template:
|
||||
tmpl_about = u'''<b>{t0}</b><br/>{version}<br/><b>{t1}</b><br/>
|
||||
<a href="{url}">{url}</a><br/><b>{t2}</b><br/>
|
||||
<a href="{feedback0}">{feedback0}</a><br/>
|
||||
<a href="mailto:{feedback1}">{feedback1}</a>'''.format(
|
||||
t0=_('VERSION'), version=VERSION,
|
||||
t1=_('REPOSITORY'), url=Endpoint.repository,
|
||||
t2=_('FEEDBACK'), feedback0=Endpoint.feedback_issue, feedback1=Endpoint.feedback_mail)
|
||||
miss_css = u'MDX dictonary <b>{dict}</b> misses css file <b>{css}</b>. <br/>Please choose the file.'
|
||||
<a href="{url}">{url}</a><br/><b>{t2}</b><br/>
|
||||
<a href="{feedback0}">{feedback0}</a><br/>
|
||||
<a href="mailto:{feedback1}">{feedback1}</a>'''.format(
|
||||
t0=_('VERSION'),
|
||||
version=VERSION,
|
||||
t1=_('REPOSITORY'),
|
||||
url=Endpoint.repository,
|
||||
t2=_('FEEDBACK'),
|
||||
feedback0=Endpoint.feedback_issue,
|
||||
feedback1=Endpoint.feedback_mail)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2018 sthoo <sth201807@gmail.com>
|
||||
#
|
||||
@ -19,25 +19,24 @@
|
||||
|
||||
import json
|
||||
import os
|
||||
from aqt import mw
|
||||
|
||||
from anki.hooks import runHook
|
||||
from aqt import mw
|
||||
|
||||
from .constants import VERSION
|
||||
from .utils import get_icon
|
||||
|
||||
|
||||
__all__ = ['APP_ICON', 'config']
|
||||
|
||||
|
||||
APP_ICON = get_icon('wqicon.png') #Addon Icon
|
||||
APP_ICON = get_icon('wqicon.png') # Addon Icon
|
||||
|
||||
|
||||
class Config(object):
|
||||
|
||||
"""
|
||||
Addon Config
|
||||
"""
|
||||
|
||||
_CONFIG_FILENAME = 'fastwqcfg.json' #Config File Path
|
||||
_CONFIG_FILENAME = 'fastwqcfg.json' # Config File Path
|
||||
|
||||
def __init__(self, window):
|
||||
self.path = u'_' + self._CONFIG_FILENAME
|
||||
@ -55,10 +54,12 @@ class Config(object):
|
||||
Update && Save
|
||||
"""
|
||||
data['version'] = VERSION
|
||||
data['%s_last' % self.pmname] = data.get('last_model', self.last_model_id)
|
||||
data['%s_last' % self.pmname] = data.get('last_model',
|
||||
self.last_model_id)
|
||||
self.data.update(data)
|
||||
with open(self.path, 'w', encoding='utf-8') as f:
|
||||
json.dump(self.data, f, indent=4, sort_keys=True, ensure_ascii=False)
|
||||
json.dump(
|
||||
self.data, f, indent=4, sort_keys=True, ensure_ascii=False)
|
||||
f.close()
|
||||
runHook('config.update')
|
||||
|
||||
@ -69,13 +70,15 @@ class Config(object):
|
||||
if self.data:
|
||||
return self.data
|
||||
try:
|
||||
path = self.path if os.path.exists(self.path) else u'.' + self._CONFIG_FILENAME
|
||||
path = self.path if os.path.exists(
|
||||
self.path) else u'.' + self._CONFIG_FILENAME
|
||||
with open(path, 'r', encoding="utf-8") as f:
|
||||
self.data = json.load(f)
|
||||
f.close()
|
||||
if not os.path.exists(self.path):
|
||||
self.update(self.data)
|
||||
except:
|
||||
except Exception as e:
|
||||
print('can not find config file', e)
|
||||
self.data = dict()
|
||||
|
||||
def get_maps(self, model_id):
|
||||
@ -107,7 +110,7 @@ class Config(object):
|
||||
@property
|
||||
def force_update(self):
|
||||
return self.data.get('force_update', False)
|
||||
|
||||
|
||||
@property
|
||||
def ignore_mdx_wordcase(self):
|
||||
return self.data.get('ignore_mdx_wordcase', False)
|
||||
@ -122,7 +125,7 @@ class Config(object):
|
||||
@property
|
||||
def last_folder(self):
|
||||
"""
|
||||
last file dialog open path
|
||||
last file dialog open path
|
||||
"""
|
||||
return self.data.get('last_folder', '')
|
||||
|
||||
@ -131,15 +134,13 @@ class Config(object):
|
||||
'''ignore accents of field in querying'''
|
||||
return self.data.get('ignore_accents', False)
|
||||
|
||||
@property
|
||||
def auto_update(self):
|
||||
'''auto check new version'''
|
||||
return self.data.get('auto_update', True)
|
||||
|
||||
@property
|
||||
def cloze_str(self):
|
||||
'''cloze formater string'''
|
||||
return self.data.get('cloze_str', '{{c1::%s}}')
|
||||
tmpstr = self.data.get('cloze_str', '{{c1::%s}}')
|
||||
if len(tmpstr.split('%s')) != 2:
|
||||
tmpstr = '{{c1::%s}}'
|
||||
return tmpstr
|
||||
|
||||
|
||||
config = Config(mw)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
from .common import *
|
||||
from .progress import *
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2018 sthoo <sth201807@gmail.com>
|
||||
#
|
||||
@ -18,10 +18,11 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
|
||||
from anki.utils import isMac
|
||||
from aqt.qt import *
|
||||
from ..context import APP_ICON
|
||||
|
||||
from ..context import APP_ICON
|
||||
|
||||
__all__ = ['Dialog', 'WIDGET_SIZE']
|
||||
|
||||
@ -37,20 +38,15 @@ class Dialog(QDialog):
|
||||
return value of the _ui() method, and sets a default title.
|
||||
'''
|
||||
|
||||
self._title = title
|
||||
self._title = title if "FastWQ" in title else "FastWQ - " + title
|
||||
self._parent = parent
|
||||
super(Dialog, self).__init__(parent)
|
||||
|
||||
self.setModal(True)
|
||||
self.setWindowFlags(
|
||||
self.windowFlags() &
|
||||
~Qt.WindowContextHelpButtonHint
|
||||
)
|
||||
self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
|
||||
self.setWindowIcon(APP_ICON)
|
||||
self.setWindowTitle(
|
||||
title if "FastWQ" in title
|
||||
else "FastWQ - " + title
|
||||
)
|
||||
self.setWindowTitle(self._title)
|
||||
# 2 & 3 & mac compatible
|
||||
if isMac and sys.hexversion >= 0x03000000:
|
||||
QApplication.setStyle('Fusion')
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2018 sthoo <sth201807@gmail.com>
|
||||
#
|
||||
@ -18,38 +18,22 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import types
|
||||
|
||||
from aqt import mw
|
||||
from aqt.qt import *
|
||||
from aqt.utils import showInfo
|
||||
from .options import OptionsDialog
|
||||
from .foldermanager import FoldersManageDialog
|
||||
from .dictmanager import DictManageDialog
|
||||
from ..libs import ankihub
|
||||
|
||||
from ..constants import Template
|
||||
from ..context import config
|
||||
from ..lang import _
|
||||
from ..constants import Endpoint, Template
|
||||
from ..service import service_manager, service_pool
|
||||
from .dictmanager import DictManageDialog
|
||||
from .foldermanager import FoldersManageDialog
|
||||
from .options import OptionsDialog
|
||||
|
||||
__all__ = ['show_options', 'show_fm_dialog', 'show_about_dialog']
|
||||
|
||||
|
||||
__all__ = ['show_options', 'show_fm_dialog', 'show_about_dialog'] #'check_updates',
|
||||
|
||||
|
||||
# def check_updates(background=False, parent=None):
|
||||
# '''check add-on last version'''
|
||||
# try:
|
||||
# parent = mw if parent is None else parent
|
||||
# state = ankihub.update([Endpoint.check_version], Endpoint.version, background, parent)
|
||||
# if not background:
|
||||
# if state == 0:
|
||||
# showInfo(_('LATEST_VERSION'))
|
||||
# elif state == -1:
|
||||
# showInfo(_('CHECK_FAILURE'))
|
||||
# except:
|
||||
# if not background:
|
||||
# showInfo(_('CHECK_FAILURE'))
|
||||
|
||||
|
||||
def show_fm_dialog(browser = None):
|
||||
def show_fm_dialog(browser=None):
|
||||
'''open dictionary folder manager window'''
|
||||
parent = mw if browser is None else browser
|
||||
fm_dialog = FoldersManageDialog(parent, u'Dictionary Folder Manager')
|
||||
@ -64,7 +48,7 @@ def show_fm_dialog(browser = None):
|
||||
show_options(browser)
|
||||
|
||||
|
||||
def show_dm_dialog(browser = None):
|
||||
def show_dm_dialog(browser=None):
|
||||
parent = mw if browser is None else browser
|
||||
dm_dialog = DictManageDialog(parent, u'Dictionary Manager')
|
||||
dm_dialog.activateWindow()
|
||||
@ -78,7 +62,7 @@ def show_dm_dialog(browser = None):
|
||||
show_options(browser)
|
||||
|
||||
|
||||
def show_options(browser = None, model_id = -1, callback = None, *args, **kwargs):
|
||||
def show_options(browser=None, model_id=-1, callback=None, *args, **kwargs):
|
||||
'''open options window'''
|
||||
parent = mw if browser is None else browser
|
||||
config.read()
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2018 sthoo <sth201807@gmail.com>
|
||||
#
|
||||
@ -19,19 +19,20 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
from aqt.qt import *
|
||||
|
||||
from aqt.forms.editaddon import Ui_Dialog
|
||||
from .base import Dialog, WIDGET_SIZE
|
||||
from ..service import service_manager, service_pool
|
||||
from aqt.qt import *
|
||||
|
||||
from ..context import config
|
||||
from ..lang import _, _sl
|
||||
from ..service import service_manager, service_pool
|
||||
from ..utils import get_icon
|
||||
from .base import WIDGET_SIZE, Dialog
|
||||
|
||||
# 2x3 compatible
|
||||
if sys.hexversion >= 0x03000000:
|
||||
unicode = str
|
||||
|
||||
|
||||
__all__ = ['DictManageDialog']
|
||||
|
||||
|
||||
@ -45,14 +46,14 @@ class DictManageDialog(Dialog):
|
||||
self.main_layout = QVBoxLayout()
|
||||
self.setLayout(self.main_layout)
|
||||
self._options = list()
|
||||
btnbox = QDialogButtonBox(QDialogButtonBox.Ok, Qt.Horizontal , self)
|
||||
btnbox = QDialogButtonBox(QDialogButtonBox.Ok, Qt.Horizontal, self)
|
||||
btnbox.accepted.connect(self.accept)
|
||||
# add dicts mapping
|
||||
self.dicts_layout = QGridLayout()
|
||||
self.main_layout.addLayout(self.dicts_layout)
|
||||
self.main_layout.addWidget(btnbox)
|
||||
self.build()
|
||||
|
||||
|
||||
def build(self):
|
||||
''' '''
|
||||
# labels
|
||||
@ -80,10 +81,14 @@ class DictManageDialog(Dialog):
|
||||
services = service_manager.local_custom_services + service_manager.web_services
|
||||
for clazz in services:
|
||||
dicts.append({
|
||||
'title': clazz.__title__,
|
||||
'unique': clazz.__unique__,
|
||||
'path': clazz.__path__,
|
||||
'enabled': confs.get(clazz.__unique__, dict()).get('enabled', True)
|
||||
'title':
|
||||
clazz.__title__,
|
||||
'unique':
|
||||
clazz.__unique__,
|
||||
'path':
|
||||
clazz.__path__,
|
||||
'enabled':
|
||||
confs.get(clazz.__unique__, dict()).get('enabled', True)
|
||||
})
|
||||
# add dict
|
||||
for i, d in enumerate(dicts):
|
||||
@ -102,7 +107,7 @@ class DictManageDialog(Dialog):
|
||||
)
|
||||
# button
|
||||
check_btn = QCheckBox(title)
|
||||
check_btn.setMinimumSize(WIDGET_SIZE.map_dict_width*1.5, 0)
|
||||
check_btn.setMinimumSize(WIDGET_SIZE.map_dict_width * 1.5, 0)
|
||||
check_btn.setEnabled(True)
|
||||
check_btn.setChecked(enabled)
|
||||
edit_btn = QToolButton(self)
|
||||
@ -114,9 +119,9 @@ class DictManageDialog(Dialog):
|
||||
self.dicts_layout.addWidget(check_btn, i + 1, 0)
|
||||
self.dicts_layout.addWidget(edit_btn, i + 1, 1)
|
||||
self._options.append({
|
||||
'unique': unique,
|
||||
'check_btn': check_btn,
|
||||
'edit_btn': edit_btn,
|
||||
'unique': unique,
|
||||
'check_btn': check_btn,
|
||||
'edit_btn': edit_btn,
|
||||
})
|
||||
|
||||
def enabled_all_update(self):
|
||||
@ -126,7 +131,7 @@ class DictManageDialog(Dialog):
|
||||
b = False
|
||||
break
|
||||
self.enabled_all_check_btn.setChecked(b)
|
||||
|
||||
|
||||
def enabled_all_changed(self):
|
||||
b = self.enabled_all_check_btn.isChecked()
|
||||
for row in self._options:
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2018 sthoo <sth201807@gmail.com>
|
||||
#
|
||||
@ -18,10 +18,10 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from aqt.qt import *
|
||||
from .base import Dialog, WIDGET_SIZE
|
||||
|
||||
from ..context import config
|
||||
from ..lang import _, _sl
|
||||
|
||||
from .base import WIDGET_SIZE, Dialog
|
||||
|
||||
__all__ = ['FoldersManageDialog']
|
||||
|
||||
@ -33,7 +33,7 @@ class FoldersManageDialog(Dialog):
|
||||
|
||||
def __init__(self, parent, title=u'Dictionary Folder Manager'):
|
||||
super(FoldersManageDialog, self).__init__(parent, title)
|
||||
#self._dict_paths = []
|
||||
# self._dict_paths = []
|
||||
self.build()
|
||||
|
||||
def build(self):
|
||||
@ -65,10 +65,9 @@ class FoldersManageDialog(Dialog):
|
||||
def add_folder(self):
|
||||
dir_ = QFileDialog.getExistingDirectory(
|
||||
self,
|
||||
caption=u"Select Folder",
|
||||
directory=config.last_folder,
|
||||
options=QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks
|
||||
)
|
||||
caption=u"Select Folder",
|
||||
directory=config.last_folder,
|
||||
options=QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)
|
||||
if dir_:
|
||||
self.folders_lst.addItem(dir_)
|
||||
config.update({'last_folder': dir_})
|
||||
@ -80,8 +79,10 @@ class FoldersManageDialog(Dialog):
|
||||
@property
|
||||
def dirs(self):
|
||||
'''dictionary folders list'''
|
||||
return [self.folders_lst.item(i).text()
|
||||
for i in range(self.folders_lst.count())]
|
||||
return [
|
||||
self.folders_lst.item(i).text()
|
||||
for i in range(self.folders_lst.count())
|
||||
]
|
||||
|
||||
def accept(self):
|
||||
'''ok button clicked'''
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2018 sthoo <sth201807@gmail.com>
|
||||
#
|
||||
@ -18,22 +18,23 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
|
||||
import anki
|
||||
import aqt
|
||||
import aqt.models
|
||||
import sip
|
||||
from anki.utils import isMac
|
||||
from aqt import mw
|
||||
from aqt.qt import *
|
||||
from aqt.studydeck import StudyDeck
|
||||
from anki.utils import isMac
|
||||
from .base import Dialog, WIDGET_SIZE
|
||||
from .setting import SettingDialog
|
||||
|
||||
from ..constants import Endpoint
|
||||
from ..context import config
|
||||
from ..lang import _, _sl
|
||||
from ..service import service_manager, service_pool
|
||||
from ..utils import get_model_byId, get_icon
|
||||
from ..constants import Endpoint
|
||||
|
||||
from ..utils import get_icon, get_model_byId
|
||||
from .base import WIDGET_SIZE, Dialog
|
||||
from .setting import SettingDialog
|
||||
|
||||
__all__ = ['OptionsDialog']
|
||||
|
||||
@ -44,16 +45,13 @@ class OptionsDialog(Dialog):
|
||||
setting query dictionary and fileds
|
||||
'''
|
||||
|
||||
__slot__ = [
|
||||
'begore_build',
|
||||
'after_build'
|
||||
]
|
||||
__slot__ = ['begore_build', 'after_build']
|
||||
_signal = pyqtSignal(str)
|
||||
|
||||
_NULL_ICON = get_icon('null.png')
|
||||
_OK_ICON = get_icon('ok.png')
|
||||
|
||||
def __init__(self, parent, title=u'Options', model_id = -1):
|
||||
def __init__(self, parent, title=u'Options', model_id=-1):
|
||||
super(OptionsDialog, self).__init__(parent, title)
|
||||
self._signal.connect(self._before_build)
|
||||
self._signal.connect(self._after_build)
|
||||
@ -61,32 +59,34 @@ class OptionsDialog(Dialog):
|
||||
self.main_layout = QVBoxLayout()
|
||||
self.loading_label = QLabel(_('INITLIZING_DICT'))
|
||||
self.main_layout.addWidget(self.loading_label, 0, Qt.AlignCenter)
|
||||
#self.loading_layout.addLayout(models_layout)
|
||||
# self.loading_layout.addLayout(models_layout)
|
||||
self.setLayout(self.main_layout)
|
||||
#initlize properties
|
||||
# initlize properties
|
||||
self.model_id = model_id if model_id != -1 else config.last_model_id
|
||||
self.current_model = None
|
||||
self.tabs = []
|
||||
self.dict_services = None
|
||||
# size and signal
|
||||
self.resize(WIDGET_SIZE.dialog_width, 4 * WIDGET_SIZE.map_max_height + WIDGET_SIZE.dialog_height_margin)
|
||||
self.resize(
|
||||
WIDGET_SIZE.dialog_width,
|
||||
4 * WIDGET_SIZE.map_max_height + WIDGET_SIZE.dialog_height_margin)
|
||||
self._signal.emit('before_build')
|
||||
|
||||
|
||||
def _before_build(self, s):
|
||||
if s != 'before_build':
|
||||
return
|
||||
# dict service list
|
||||
dicts = config.dicts
|
||||
self.dict_services = {
|
||||
'local': [], #本地词典
|
||||
'web': [] #网络词典
|
||||
'local': [], # 本地词典
|
||||
'web': [] # 网络词典
|
||||
}
|
||||
for clazz in service_manager.local_services:
|
||||
if dicts.get(clazz.__unique__, dict()).get('enabled', True):
|
||||
service = service_pool.get(clazz.__unique__)
|
||||
if service and service.support:
|
||||
self.dict_services['local'].append({
|
||||
'title': service.title,
|
||||
'title': service.title,
|
||||
'unique': service.unique
|
||||
})
|
||||
service_pool.put(service)
|
||||
@ -95,7 +95,7 @@ class OptionsDialog(Dialog):
|
||||
service = service_pool.get(clazz.__unique__)
|
||||
if service and service.support:
|
||||
self.dict_services['web'].append({
|
||||
'title': service.title,
|
||||
'title': service.title,
|
||||
'unique': service.unique
|
||||
})
|
||||
service_pool.put(service)
|
||||
@ -121,13 +121,11 @@ class OptionsDialog(Dialog):
|
||||
# tabs
|
||||
self.tab_widget = QTabWidget()
|
||||
self.tab_widget.setTabBar(CTabBar())
|
||||
self.tab_widget.setStyleSheet(
|
||||
"""
|
||||
self.tab_widget.setStyleSheet("""
|
||||
QTabWidget::pane { /* The tab widget frame */
|
||||
border: 1px solid #c3c3c3;
|
||||
}
|
||||
"""
|
||||
)
|
||||
""")
|
||||
tab_corner = QWidget()
|
||||
tab_corner_layout = QHBoxLayout()
|
||||
tab_corner_layout.setSpacing(1)
|
||||
@ -160,8 +158,7 @@ class OptionsDialog(Dialog):
|
||||
# chk_update_btn = QPushButton(_('UPDATE'))
|
||||
# chk_update_btn.clicked.connect(self.check_updates)
|
||||
home_label = QLabel(
|
||||
'<a href="{url}">User Guide</a>'.format(url=Endpoint.user_guide)
|
||||
)
|
||||
'<a href="{url}">User Guide</a>'.format(url=Endpoint.user_guide))
|
||||
home_label.setOpenExternalLinks(True)
|
||||
# buttons
|
||||
btnbox = QDialogButtonBox(QDialogButtonBox.Ok, Qt.Horizontal, self)
|
||||
@ -171,7 +168,7 @@ class OptionsDialog(Dialog):
|
||||
bottom_layout.addWidget(about_btn)
|
||||
bottom_layout.addWidget(home_label)
|
||||
bottom_layout.addWidget(btnbox)
|
||||
#self.setLayout(self.main_layout)
|
||||
# self.setLayout(self.main_layout)
|
||||
self.main_layout.addLayout(bottom_layout)
|
||||
# init from saved data
|
||||
self.current_model = None
|
||||
@ -179,7 +176,8 @@ class OptionsDialog(Dialog):
|
||||
self.current_model = get_model_byId(mw.col.models, self.model_id)
|
||||
if self.current_model:
|
||||
self.models_button.setText(
|
||||
u'%s [%s]' % (_('CHOOSE_NOTE_TYPES'), self.current_model['name']))
|
||||
u'%s [%s]' % (_('CHOOSE_NOTE_TYPES'),
|
||||
self.current_model['name']))
|
||||
# build fields -- dicts layout
|
||||
self.build_tabs_layout()
|
||||
|
||||
@ -198,7 +196,7 @@ class OptionsDialog(Dialog):
|
||||
'''open folder manager dialog'''
|
||||
self.accept()
|
||||
self.setResult(1001)
|
||||
|
||||
|
||||
def show_dm_dialog(self):
|
||||
'''open dictionary manager dialog'''
|
||||
self.accept()
|
||||
@ -225,13 +223,18 @@ class OptionsDialog(Dialog):
|
||||
'''
|
||||
build dictionary、fields etc
|
||||
'''
|
||||
try: self.tab_widget.currentChanged.disconnect()
|
||||
except Exception: pass
|
||||
try:
|
||||
self.tab_widget.currentChanged.disconnect()
|
||||
except Exception:
|
||||
pass
|
||||
while len(self.tabs) > 0:
|
||||
self.removeTab(0, True)
|
||||
# tabs
|
||||
conf = config.get_maps(self.current_model['id'])
|
||||
maps_list = {'list': [conf], 'def': 0} if isinstance(conf, list) else conf
|
||||
maps_list = {
|
||||
'list': [conf],
|
||||
'def': 0
|
||||
} if isinstance(conf, list) else conf
|
||||
for maps in maps_list['list']:
|
||||
self.addTab(maps, False)
|
||||
self.tab_widget.currentChanged.connect(self.changedTab)
|
||||
@ -240,28 +243,21 @@ class OptionsDialog(Dialog):
|
||||
self.tab_widget.setCurrentIndex(maps_list['def'])
|
||||
# size
|
||||
self.resize(
|
||||
WIDGET_SIZE.dialog_width,
|
||||
min(max(3, len(self.current_model['flds'])+1), 14) * WIDGET_SIZE.map_max_height + WIDGET_SIZE.dialog_height_margin
|
||||
)
|
||||
WIDGET_SIZE.dialog_width,
|
||||
min(max(3,
|
||||
len(self.current_model['flds']) + 1), 14) *
|
||||
WIDGET_SIZE.map_max_height + WIDGET_SIZE.dialog_height_margin)
|
||||
self.save()
|
||||
|
||||
|
||||
def addTab(self, maps=None, forcus=True):
|
||||
i = len(self.tabs)
|
||||
if isinstance(maps, list):
|
||||
maps = {
|
||||
'fields': maps,
|
||||
'name': _('CONFIG_INDEX') % (i+1)
|
||||
}
|
||||
tab = TabContent(
|
||||
self.current_model,
|
||||
maps['fields'] if maps else None,
|
||||
self.dict_services
|
||||
)
|
||||
maps = {'fields': maps, 'name': _('CONFIG_INDEX') % (i + 1)}
|
||||
tab = TabContent(self.current_model, maps['fields'] if maps else None,
|
||||
self.dict_services)
|
||||
self.tabs.append(tab)
|
||||
self.tab_widget.addTab(
|
||||
tab,
|
||||
maps['name'] if maps else _('CONFIG_INDEX') % (i+1)
|
||||
)
|
||||
tab, maps['name'] if maps else _('CONFIG_INDEX') % (i + 1))
|
||||
if forcus:
|
||||
self.tab_widget.setCurrentIndex(i)
|
||||
|
||||
@ -273,7 +269,7 @@ class OptionsDialog(Dialog):
|
||||
del self.tabs[i]
|
||||
self.tab_widget.removeTab(i)
|
||||
tab.destroy()
|
||||
#for k in range(0, len(self.tabs)):
|
||||
# for k in range(0, len(self.tabs)):
|
||||
# self.tab_widget.setTabText(k, _('CONFIG_INDEX') % (k+1))
|
||||
|
||||
def changedTab(self, i):
|
||||
@ -289,12 +285,18 @@ class OptionsDialog(Dialog):
|
||||
'''
|
||||
show choose note type window
|
||||
'''
|
||||
edit = QPushButton(anki.lang._("Manage"),
|
||||
clicked=lambda: aqt.models.Models(mw, self))
|
||||
ret = StudyDeck(mw, names=lambda: sorted(mw.col.models.allNames()),
|
||||
accept=anki.lang._("Choose"), title=anki.lang._("Choose Note Type"),
|
||||
help="_notes", parent=self, buttons=[edit],
|
||||
cancel=True, geomKey="selectModel")
|
||||
edit = QPushButton(
|
||||
anki.lang._("Manage"), clicked=lambda: aqt.models.Models(mw, self))
|
||||
ret = StudyDeck(
|
||||
mw,
|
||||
names=lambda: sorted(mw.col.models.allNames()),
|
||||
accept=anki.lang._("Choose"),
|
||||
title=anki.lang._("Choose Note Type"),
|
||||
help="_notes",
|
||||
parent=self,
|
||||
buttons=[edit],
|
||||
cancel=True,
|
||||
geomKey="selectModel")
|
||||
if ret.name:
|
||||
model = mw.col.models.byName(ret.name)
|
||||
self.models_button.setText(
|
||||
@ -306,18 +308,17 @@ class OptionsDialog(Dialog):
|
||||
if not self.current_model:
|
||||
return
|
||||
data = dict()
|
||||
maps_list = {
|
||||
'list': [],
|
||||
'def': self.tab_widget.currentIndex()
|
||||
}
|
||||
maps_list = {'list': [], 'def': self.tab_widget.currentIndex()}
|
||||
for i, tab in enumerate(self.tabs):
|
||||
maps_list['list'].append({
|
||||
'fields': tab.data,
|
||||
'name': self.tab_widget.tabBar().tabText(i)
|
||||
'fields':
|
||||
tab.data,
|
||||
'name':
|
||||
self.tab_widget.tabBar().tabText(i)
|
||||
})
|
||||
current_model_id = str(self.current_model['id'])
|
||||
data[current_model_id] = maps_list
|
||||
data['last_model'] = self.current_model['id']
|
||||
data['last_model'] = self.current_model['id']
|
||||
config.update(data)
|
||||
|
||||
|
||||
@ -339,7 +340,7 @@ class TabContent(QScrollArea):
|
||||
self.setWidgetResizable(True)
|
||||
self.setWidget(dicts)
|
||||
self.dicts_layout = dicts.layout()
|
||||
#self.dicts_layout.setSizeConstraint(QLayout.SetFixedSize)
|
||||
# self.dicts_layout.setSizeConstraint(QLayout.SetFixedSize)
|
||||
|
||||
def build_layout(self):
|
||||
'''
|
||||
@ -352,7 +353,7 @@ class TabContent(QScrollArea):
|
||||
self._last_checkeds = None
|
||||
self._was_built = True
|
||||
|
||||
model = self._model
|
||||
model = self._model
|
||||
maps = self._conf
|
||||
|
||||
# labels
|
||||
@ -372,7 +373,8 @@ class TabContent(QScrollArea):
|
||||
self.ignore_all_check_btn.setEnabled(True)
|
||||
self.ignore_all_check_btn.setChecked(True)
|
||||
self.dicts_layout.addWidget(self.ignore_all_check_btn, 0, 1)
|
||||
self.ignore_all_check_btn.clicked.connect(self.ignore_all_check_changed)
|
||||
self.ignore_all_check_btn.clicked.connect(
|
||||
self.ignore_all_check_changed)
|
||||
|
||||
# Skip valued all
|
||||
self.skip_all_check_btn = QCheckBox(_('SELECT_ALL'))
|
||||
@ -381,7 +383,7 @@ class TabContent(QScrollArea):
|
||||
self.skip_all_check_btn.setChecked(True)
|
||||
self.dicts_layout.addWidget(self.skip_all_check_btn, 0, 4)
|
||||
self.skip_all_check_btn.clicked.connect(self.skip_all_check_changed)
|
||||
|
||||
|
||||
# dict & fields
|
||||
self.radio_group = QButtonGroup()
|
||||
for i, fld in enumerate(model['flds']):
|
||||
@ -389,16 +391,19 @@ class TabContent(QScrollArea):
|
||||
name = fld['name']
|
||||
if maps:
|
||||
for j, each in enumerate(maps):
|
||||
if each.get('fld_ord', -1) == ord or each.get('fld_name', '') == name:
|
||||
if each.get('fld_ord', -1) == ord or each.get(
|
||||
'fld_name', '') == name:
|
||||
each['fld_name'] = name
|
||||
each['fld_ord'] = ord
|
||||
self.add_dict_layout(j, **each)
|
||||
break
|
||||
else:
|
||||
self.add_dict_layout(i, fld_name=name, fld_ord=ord, word_checked=i==0)
|
||||
self.add_dict_layout(
|
||||
i, fld_name=name, fld_ord=ord, word_checked=i == 0)
|
||||
else:
|
||||
self.add_dict_layout(i, fld_name=name, fld_ord=ord, word_checked=i==0)
|
||||
|
||||
self.add_dict_layout(
|
||||
i, fld_name=name, fld_ord=ord, word_checked=i == 0)
|
||||
|
||||
# update
|
||||
self.ignore_all_update()
|
||||
self.skip_all_update()
|
||||
@ -410,21 +415,21 @@ class TabContent(QScrollArea):
|
||||
word_checked = kwargs.get('word_checked', False)
|
||||
|
||||
fld_name, fld_ord = (
|
||||
kwargs.get('fld_name', ''), #笔记类型的字段名
|
||||
kwargs.get('fld_ord', ''), #笔记类型的字段编号
|
||||
kwargs.get('fld_name', ''), # 笔记类型的字段名
|
||||
kwargs.get('fld_ord', ''), # 笔记类型的字段编号
|
||||
)
|
||||
|
||||
dict_name, dict_unique, dict_fld_name, dict_fld_ord = (
|
||||
kwargs.get('dict_name', ''), #字典名
|
||||
kwargs.get('dict_unique', ''), #字典ID
|
||||
kwargs.get('dict_fld_name', ''), #对应字典的字段名
|
||||
kwargs.get('dcit_fld_ord', 0) #对应字典的字段编号
|
||||
kwargs.get('dict_name', ''), # 字典名
|
||||
kwargs.get('dict_unique', ''), # 字典ID
|
||||
kwargs.get('dict_fld_name', ''), # 对应字典的字段名
|
||||
kwargs.get('dcit_fld_ord', 0) # 对应字典的字段编号
|
||||
)
|
||||
|
||||
ignore, skip, cloze = (
|
||||
kwargs.get('ignore', True), #忽略标志
|
||||
kwargs.get('skip_valued', True), #略过有值项标志
|
||||
kwargs.get('cloze_word', False), #单词填空
|
||||
kwargs.get('ignore', True), # 忽略标志
|
||||
kwargs.get('skip_valued', True), # 略过有值项标志
|
||||
kwargs.get('cloze_word', False), # 单词填空
|
||||
)
|
||||
|
||||
# check
|
||||
@ -436,26 +441,23 @@ class TabContent(QScrollArea):
|
||||
# dict combox
|
||||
dict_combo = QComboBox()
|
||||
dict_combo.setMinimumSize(WIDGET_SIZE.map_dict_width, 0)
|
||||
dict_combo.setMaximumSize(
|
||||
WIDGET_SIZE.map_dict_width,
|
||||
WIDGET_SIZE.map_max_height
|
||||
)
|
||||
dict_combo.setFocusPolicy(
|
||||
Qt.TabFocus | Qt.ClickFocus | Qt.StrongFocus | Qt.WheelFocus
|
||||
)
|
||||
ignore = not self.fill_dict_combo_options(dict_combo, dict_unique, self._services) or ignore
|
||||
dict_combo.setMaximumSize(WIDGET_SIZE.map_dict_width,
|
||||
WIDGET_SIZE.map_max_height)
|
||||
dict_combo.setFocusPolicy(Qt.TabFocus | Qt.ClickFocus | Qt.StrongFocus
|
||||
| Qt.WheelFocus)
|
||||
ignore = not self.fill_dict_combo_options(dict_combo, dict_unique,
|
||||
self._services) or ignore
|
||||
dict_unique = dict_combo.itemData(dict_combo.currentIndex())
|
||||
dict_combo.setEnabled(not word_checked and not ignore)
|
||||
# field combox
|
||||
field_combo = QComboBox()
|
||||
field_combo.setMinimumSize(WIDGET_SIZE.map_field_width, 0)
|
||||
field_combo.setMaximumSize(
|
||||
WIDGET_SIZE.map_field_width,
|
||||
WIDGET_SIZE.map_max_height
|
||||
)
|
||||
field_combo.setMaximumSize(WIDGET_SIZE.map_field_width,
|
||||
WIDGET_SIZE.map_max_height)
|
||||
field_combo.setEnabled(not word_checked and not ignore)
|
||||
self.fill_field_combo_options(field_combo, dict_name, dict_unique, dict_fld_name, dict_fld_ord)
|
||||
|
||||
self.fill_field_combo_options(field_combo, dict_name, dict_unique,
|
||||
dict_fld_name, dict_fld_ord)
|
||||
|
||||
# ignore
|
||||
ignore_check_btn = QCheckBox(_("NOT_DICT_FIELD"))
|
||||
ignore_check_btn.setEnabled(not word_checked)
|
||||
@ -489,13 +491,14 @@ class TabContent(QScrollArea):
|
||||
cloze_check_btn.setEnabled(not word_checked and not ignore)
|
||||
if word_checked:
|
||||
self._last_checkeds = [
|
||||
ignore_check_btn, dict_combo,
|
||||
field_combo, skip_check_btn
|
||||
ignore_check_btn, dict_combo, field_combo, skip_check_btn
|
||||
]
|
||||
|
||||
word_check_btn.clicked.connect(radio_btn_checked)
|
||||
if word_checked:
|
||||
if word_checked:
|
||||
self._last_checkeds = None
|
||||
radio_btn_checked()
|
||||
|
||||
# ignor
|
||||
def ignore_check_changed():
|
||||
word_checked = word_check_btn.isChecked()
|
||||
@ -504,35 +507,38 @@ class TabContent(QScrollArea):
|
||||
field_combo.setEnabled(not word_checked and not ignore)
|
||||
skip_check_btn.setEnabled(not word_checked and not ignore)
|
||||
cloze_check_btn.setEnabled(not word_checked and not ignore)
|
||||
|
||||
ignore_check_btn.stateChanged.connect(ignore_check_changed)
|
||||
ignore_check_btn.clicked.connect(self.ignore_all_update)
|
||||
# skip
|
||||
skip_check_btn.clicked.connect(self.skip_all_update)
|
||||
|
||||
# dict
|
||||
def dict_combo_changed(index):
|
||||
'''dict combo box index changed'''
|
||||
self.fill_field_combo_options(
|
||||
field_combo,
|
||||
dict_combo.currentText(),
|
||||
dict_combo.itemData(index),
|
||||
field_combo.currentText(),
|
||||
field_combo.itemData(field_combo.currentIndex())
|
||||
)
|
||||
field_combo, dict_combo.currentText(),
|
||||
dict_combo.itemData(index), field_combo.currentText(),
|
||||
field_combo.itemData(field_combo.currentIndex()))
|
||||
|
||||
dict_combo.currentIndexChanged.connect(dict_combo_changed)
|
||||
|
||||
|
||||
self.dicts_layout.addWidget(word_check_btn, i + 1, 0)
|
||||
self.dicts_layout.addWidget(ignore_check_btn, i + 1, 1)
|
||||
self.dicts_layout.addWidget(dict_combo, i + 1, 2)
|
||||
self.dicts_layout.addWidget(field_combo, i + 1, 3)
|
||||
self.dicts_layout.addWidget(skip_check_btn, i + 1, 4)
|
||||
self.dicts_layout.addWidget(cloze_check_btn, i + 1, 5)
|
||||
|
||||
|
||||
self._options.append({
|
||||
'model': {'fld_name': fld_name, 'fld_ord': fld_ord},
|
||||
'word_check_btn': word_check_btn,
|
||||
'dict_combo': dict_combo,
|
||||
'field_combo': field_combo,
|
||||
'ignore_check_btn': ignore_check_btn,
|
||||
'model': {
|
||||
'fld_name': fld_name,
|
||||
'fld_ord': fld_ord
|
||||
},
|
||||
'word_check_btn': word_check_btn,
|
||||
'dict_combo': dict_combo,
|
||||
'field_combo': field_combo,
|
||||
'ignore_check_btn': ignore_check_btn,
|
||||
'skip_check_btn': skip_check_btn,
|
||||
'cloze_check_btn': cloze_check_btn
|
||||
})
|
||||
@ -548,7 +554,7 @@ class TabContent(QScrollArea):
|
||||
# hr
|
||||
if len(services['local']) > 0:
|
||||
dict_combo.insertSeparator(dict_combo.count())
|
||||
|
||||
|
||||
# web dict service
|
||||
for service in services['web']:
|
||||
dict_combo.addItem(service['title'], userData=service['unique'])
|
||||
@ -561,15 +567,18 @@ class TabContent(QScrollArea):
|
||||
dict_combo.setCurrentIndex(i)
|
||||
return True
|
||||
return False
|
||||
|
||||
return set_dict_combo_index()
|
||||
|
||||
def fill_field_combo_options(self, field_combo, dict_combo_text, dict_combo_itemdata, dict_fld_name, dict_fld_ord):
|
||||
def fill_field_combo_options(self, field_combo, dict_combo_text,
|
||||
dict_combo_itemdata, dict_fld_name,
|
||||
dict_fld_ord):
|
||||
'''setup field combobox'''
|
||||
field_combo.clear()
|
||||
field_combo.setEditable(False)
|
||||
#if dict_combo_text in _sl('NOT_DICT_FIELD'):
|
||||
# if dict_combo_text in _sl('NOT_DICT_FIELD'):
|
||||
# field_combo.setEnabled(False)
|
||||
#el
|
||||
# el
|
||||
if dict_combo_text in _sl('MDX_SERVER'):
|
||||
text = dict_fld_name if dict_fld_name else 'http://'
|
||||
field_combo.setEditable(True)
|
||||
@ -594,16 +603,26 @@ class TabContent(QScrollArea):
|
||||
maps = []
|
||||
for row in self._options:
|
||||
maps.append({
|
||||
'fld_name': row['model']['fld_name'],
|
||||
'fld_ord': row['model']['fld_ord'],
|
||||
'word_checked': row['word_check_btn'].isChecked(),
|
||||
'dict_name': row['dict_combo'].currentText().strip(),
|
||||
'dict_unique': row['dict_combo'].itemData(row['dict_combo'].currentIndex()),
|
||||
'dict_fld_name': row['field_combo'].currentText().strip(),
|
||||
'dict_fld_ord': row['field_combo'].itemData(row['field_combo'].currentIndex()),
|
||||
'ignore': row['ignore_check_btn'].isChecked(),
|
||||
'skip_valued': row['skip_check_btn'].isChecked(),
|
||||
'cloze_word': row['cloze_check_btn'].isChecked()
|
||||
'fld_name':
|
||||
row['model']['fld_name'],
|
||||
'fld_ord':
|
||||
row['model']['fld_ord'],
|
||||
'word_checked':
|
||||
row['word_check_btn'].isChecked(),
|
||||
'dict_name':
|
||||
row['dict_combo'].currentText().strip(),
|
||||
'dict_unique':
|
||||
row['dict_combo'].itemData(row['dict_combo'].currentIndex()),
|
||||
'dict_fld_name':
|
||||
row['field_combo'].currentText().strip(),
|
||||
'dict_fld_ord':
|
||||
row['field_combo'].itemData(row['field_combo'].currentIndex()),
|
||||
'ignore':
|
||||
row['ignore_check_btn'].isChecked(),
|
||||
'skip_valued':
|
||||
row['skip_check_btn'].isChecked(),
|
||||
'cloze_word':
|
||||
row['cloze_check_btn'].isChecked()
|
||||
})
|
||||
return maps
|
||||
|
||||
@ -624,7 +643,7 @@ class TabContent(QScrollArea):
|
||||
b = False
|
||||
break
|
||||
self.ignore_all_check_btn.setChecked(b)
|
||||
|
||||
|
||||
def skip_all_update(self):
|
||||
b = True
|
||||
for row in self._options:
|
||||
@ -635,8 +654,7 @@ class TabContent(QScrollArea):
|
||||
|
||||
|
||||
class CTabBar(QTabBar):
|
||||
|
||||
def __init__(self, parent = None):
|
||||
def __init__(self, parent=None):
|
||||
super(CTabBar, self).__init__(parent)
|
||||
# style
|
||||
self.setTabsClosable(True)
|
||||
@ -651,14 +669,17 @@ class CTabBar(QTabBar):
|
||||
self._editor.installEventFilter(self)
|
||||
|
||||
def eventFilter(self, widget, event):
|
||||
if ((event.type() == QEvent.MouseButtonPress and \
|
||||
not self._editor.geometry().contains(event.globalPos()) \
|
||||
) or \
|
||||
(event.type() == QEvent.KeyPress and \
|
||||
event.key() == Qt.Key_Escape)
|
||||
):
|
||||
self.hideEditor()
|
||||
return True
|
||||
bhide = False
|
||||
if event.type() == QEvent.MouseButtonPress:
|
||||
if not self._editor.geometry().contains(event.globalPos()):
|
||||
bhide = True
|
||||
if not bhide:
|
||||
if event.type() == QEvent.KeyPress:
|
||||
if event.key() == Qt.Key_Escape:
|
||||
bhide = True
|
||||
if bhide:
|
||||
self.hideEditor()
|
||||
return True
|
||||
return QTabBar.eventFilter(self, widget, event)
|
||||
|
||||
def mouseDoubleClickEvent(self, event):
|
||||
@ -676,7 +697,7 @@ class CTabBar(QTabBar):
|
||||
self._editor.selectAll()
|
||||
self._editor.setEnabled(True)
|
||||
self._editor.setFocus()
|
||||
|
||||
|
||||
def hideEditor(self):
|
||||
if self._editor.isVisible():
|
||||
self._editor.setEnabled(False)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2018 sthoo <sth201807@gmail.com>
|
||||
#
|
||||
@ -17,24 +17,22 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import time
|
||||
from collections import defaultdict
|
||||
|
||||
from aqt.qt import *
|
||||
from ..lang import _
|
||||
from ..context import APP_ICON
|
||||
|
||||
from ..context import APP_ICON
|
||||
from ..lang import _
|
||||
|
||||
__all__ = ['ProgressWindow']
|
||||
|
||||
|
||||
_INFO_TEMPLATE = u''.join([
|
||||
u'<strong>' + _('QUERIED') + u'</strong>',
|
||||
u'<p>' + 45 * u'-' + u'</p>',
|
||||
u'<p>' + _('SUCCESS') + u' <b>{}</b> ' + _('WORDS') + u'</p>',
|
||||
u'<p>' + _('SKIPED') + u' <b>{}</b> ' + _('WORDS') + u'</p>',
|
||||
u'<p>' + _('UPDATE') + u' <b>{}</b> ' + _('FIELDS') + u'</p>',
|
||||
u'<p>' + _('UPDATE') + u' <b>{}</b> ' + _('FIELDS') + u'</p>',
|
||||
u'<p>' + _('FAILURE') + u' <b>{}</b> ' + _('WORDS') + u'</p>',
|
||||
])
|
||||
|
||||
@ -66,15 +64,12 @@ class ProgressWindow(object):
|
||||
self._msg_count.get('words_number', 0),
|
||||
self._msg_count.get('fields_number', 0),
|
||||
self._msg_count.get('fails_number', 0),
|
||||
self._msg_count.get('skips_number', 0)
|
||||
)
|
||||
number_info = _INFO_TEMPLATE.format(
|
||||
words_number,
|
||||
skips_number,
|
||||
fields_number,
|
||||
fails_number
|
||||
)
|
||||
self._update(label=number_info, value=words_number+skips_number+fails_number)
|
||||
self._msg_count.get('skips_number', 0))
|
||||
number_info = _INFO_TEMPLATE.format(words_number, skips_number,
|
||||
fields_number, fails_number)
|
||||
self._update(
|
||||
label=number_info,
|
||||
value=words_number + skips_number + fails_number)
|
||||
self._win.adjustSize()
|
||||
self.app.processEvents()
|
||||
|
||||
@ -95,9 +90,7 @@ class ProgressWindow(object):
|
||||
self._win.setWindowTitle("FastWQ - Querying...")
|
||||
self._win.setModal(True)
|
||||
self._win.setWindowFlags(
|
||||
self._win.windowFlags() &
|
||||
~Qt.WindowContextHelpButtonHint
|
||||
)
|
||||
self._win.windowFlags() & ~Qt.WindowContextHelpButtonHint)
|
||||
self._win.setWindowIcon(APP_ICON)
|
||||
self._win.setAutoReset(True)
|
||||
self._win.setAutoClose(True)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2018 sthoo <sth201807@gmail.com>
|
||||
#
|
||||
@ -18,10 +18,10 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from aqt.qt import *
|
||||
from .base import Dialog, WIDGET_SIZE
|
||||
|
||||
from ..context import config
|
||||
from ..lang import _
|
||||
|
||||
from .base import Dialog
|
||||
|
||||
__all__ = ['SettingDialog']
|
||||
|
||||
@ -36,7 +36,6 @@ class SettingDialog(Dialog):
|
||||
self.setFixedWidth(400)
|
||||
self.check_force_update = None
|
||||
self.check_ignore_accents = None
|
||||
# self.check_auto_update = None
|
||||
self.input_thread_number = None
|
||||
self.build()
|
||||
|
||||
@ -53,11 +52,6 @@ class SettingDialog(Dialog):
|
||||
layout.addWidget(check_ignore_accents)
|
||||
layout.addSpacing(10)
|
||||
|
||||
# check_auto_update = QCheckBox(_("AUTO_UPDATE"))
|
||||
# check_auto_update.setChecked(config.auto_update)
|
||||
# layout.addWidget(check_auto_update)
|
||||
# layout.addSpacing(10)
|
||||
|
||||
check_ighore_mdx_wordcase = QCheckBox(_("IGNORE_MDX_WORDCASE"))
|
||||
check_ighore_mdx_wordcase.setChecked(config.ignore_mdx_wordcase)
|
||||
layout.addWidget(check_ighore_mdx_wordcase)
|
||||
@ -84,35 +78,55 @@ class SettingDialog(Dialog):
|
||||
hbox.setStretchFactor(input_cloze_str, 2)
|
||||
layout.addLayout(hbox)
|
||||
|
||||
buttonBox = QDialogButtonBox(parent=self)
|
||||
buttonBox.setStandardButtons(QDialogButtonBox.Ok)
|
||||
buttonBox.accepted.connect(self.accept) # 确定
|
||||
|
||||
hbox = QHBoxLayout()
|
||||
okbtn = QDialogButtonBox(parent=self)
|
||||
okbtn.setStandardButtons(QDialogButtonBox.Ok)
|
||||
okbtn.clicked.connect(self.accept)
|
||||
resetbtn = QDialogButtonBox(parent=self)
|
||||
resetbtn.setStandardButtons(QDialogButtonBox.Reset)
|
||||
resetbtn.clicked.connect(self.reset)
|
||||
hbox.setAlignment(Qt.AlignRight)
|
||||
hbox.addSpacing(300)
|
||||
hbox.addWidget(resetbtn)
|
||||
hbox.addWidget(okbtn)
|
||||
|
||||
layout.addSpacing(48)
|
||||
layout.addWidget(buttonBox)
|
||||
|
||||
layout.addLayout(hbox)
|
||||
|
||||
self.check_force_update = check_force_update
|
||||
self.check_ignore_accents = check_ignore_accents
|
||||
# self.check_auto_update = check_auto_update
|
||||
self.check_ighore_mdx_wordcase = check_ighore_mdx_wordcase
|
||||
self.input_thread_number = input_thread_number
|
||||
self.input_cloze_str = input_cloze_str
|
||||
|
||||
layout.setAlignment(Qt.AlignTop|Qt.AlignLeft)
|
||||
layout.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
||||
self.setLayout(layout)
|
||||
|
||||
def accept(self):
|
||||
self.save()
|
||||
super(SettingDialog, self).accept()
|
||||
|
||||
|
||||
def reset(self):
|
||||
data = {
|
||||
'force_update': False,
|
||||
'ignore_accents': False,
|
||||
'ignore_mdx_wordcase': False,
|
||||
'thread_number': 16,
|
||||
'cloze_str': '{{c1::%s}}'
|
||||
}
|
||||
config.update(data)
|
||||
self.check_force_update.setChecked(config.force_update)
|
||||
self.check_ignore_accents.setChecked(config.ignore_accents)
|
||||
self.check_ighore_mdx_wordcase.setChecked(config.ignore_mdx_wordcase)
|
||||
self.input_thread_number.setValue(config.thread_number)
|
||||
self.input_cloze_str.setText(config.cloze_str)
|
||||
|
||||
def save(self):
|
||||
data = {
|
||||
'force_update': self.check_force_update.isChecked(),
|
||||
'ignore_accents': self.check_ignore_accents.isChecked(),
|
||||
# 'auto_update': self.check_auto_update.isChecked(),
|
||||
'ignore_mdx_wordcase': self.check_ighore_mdx_wordcase.isChecked(),
|
||||
'thread_number': self.input_thread_number.value(),
|
||||
'cloze_str': self.input_cloze_str.text()
|
||||
}
|
||||
config.update(data)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2018 sthoo <sth201807@gmail.com>
|
||||
#
|
||||
@ -18,15 +18,15 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from anki.lang import currentLang
|
||||
|
||||
try:
|
||||
basestring
|
||||
basestring
|
||||
except NameError:
|
||||
basestring = str
|
||||
basestring = str
|
||||
|
||||
__all__ = ['_', '_cl', '_sl']
|
||||
|
||||
|
||||
#Language Define, [Key, zh_CN, en]
|
||||
# Language Define, [Key, zh_CN, en]
|
||||
_arr = [
|
||||
['CHECK_FILENAME_LABEL', u'使用文件名作为标签', u'Use the Filename as Label'],
|
||||
['EXPORT_MEDIA', u'导出媒体文件', u'Export Media Files'],
|
||||
@ -42,13 +42,19 @@ _arr = [
|
||||
['QUERIED', u'查询', u'Queried'],
|
||||
['FIELDS', u'字段', u'Fields'],
|
||||
['WORDS', u'单词', u'Words'],
|
||||
['NOT_DICT_FIELD', u'忽略', u'Ignore'], #不是字典字段
|
||||
['NOT_DICT_FIELD', u'忽略', u'Ignore'], # 不是字典字段
|
||||
['NOTE_TYPE_FIELDS', u'<b>笔记字段</b>', u'<b>Note Fields</b>'],
|
||||
['DICTS', u'<b>字典</b>', u'<b>Dictionary</b>'],
|
||||
['DICT_FIELDS', u'<b>字典字段</b>', u'<b>Fields</b>'],
|
||||
['RADIOS_DESC', u'<b>单选框选中为待查询单词字段</b>', u'<b> Select the field to be queried with single selection.</b>'],
|
||||
[
|
||||
'RADIOS_DESC', u'<b>单选框选中为待查询单词字段</b>',
|
||||
u'<b> Select the field to be queried with single selection.</b>'
|
||||
],
|
||||
['NO_QUERY_WORD', u'查询字段无单词', u'The query field is empty'],
|
||||
['CSS_NOT_FOUND', u'没有找到CSS文件,请手动选择', u'No CSS file found, please select one manually.'],
|
||||
[
|
||||
'CSS_NOT_FOUND', u'没有找到CSS文件,请手动选择',
|
||||
u'No CSS file found, please select one manually.'
|
||||
],
|
||||
['ABOUT', u'关于', u'About'],
|
||||
['REPOSITORY', u'项目地址', u'Project Repo'],
|
||||
['FEEDBACK', u'反馈', u'Feedback'],
|
||||
@ -60,7 +66,10 @@ _arr = [
|
||||
['UPDATE', u'更新', u'Update'],
|
||||
['AUTO_UPDATE', u'自动检测新版本', u'Auto check new version'],
|
||||
['CHECK_UPDATE', u'检测更新', u'Check Update'],
|
||||
['IGNORE_MDX_WORDCASE', u'忽略本地词典单词大小写', u'Ignore MDX dictionary word case'],
|
||||
[
|
||||
'IGNORE_MDX_WORDCASE', u'忽略本地词典单词大小写',
|
||||
u'Ignore MDX dictionary word case'
|
||||
],
|
||||
['FORCE_UPDATE', u'强制更新字段', u'Forced Updates of all fields'],
|
||||
['IGNORE_ACCENTS', u'忽略声调', u'Ignore Accents'],
|
||||
['SKIP_VALUED', u'跳过有值项', u'Skip non-empty'],
|
||||
@ -68,7 +77,10 @@ _arr = [
|
||||
['SETTINGS', u'参数', u'Settings'],
|
||||
['THREAD_NUMBER', u'线程数', u'Number of Threads'],
|
||||
['INITLIZING_DICT', u'初始化词典...', u'Initlizing Dictionary...'],
|
||||
['PLS_SET_DICTIONARY_FIELDS', u'请设置字典和字段', u'Please set the dictionary and fields.'],
|
||||
[
|
||||
'PLS_SET_DICTIONARY_FIELDS', u'请设置字典和字段',
|
||||
u'Please set the dictionary and fields.'
|
||||
],
|
||||
['CONFIG_INDEX', u'配置 %s', u'Config %s'],
|
||||
['SELECT_ALL', u'全选', u'All'],
|
||||
['DICTS_NAME', u'字典名称', u'Dictionary Name'],
|
||||
@ -80,7 +92,6 @@ _arr = [
|
||||
['OPTIONS', u'选项', u'Options'],
|
||||
['CLOZE_WORD', u'单词填空', u'Cloze word'],
|
||||
['CLOZE_WORD_FORMAT', '单词填空格式', 'Cloze word formater'],
|
||||
|
||||
['BRE_PRON', u'英式发音', u'British Pronunciation'],
|
||||
['AME_PRON', u'美式发音', u'American Pronunciation'],
|
||||
['PRON', u'发音', u'Audio Pronunciation'],
|
||||
@ -103,7 +114,7 @@ def _(key, lang=currentLang):
|
||||
|
||||
def disp(s):
|
||||
return s.lower().capitalize()
|
||||
|
||||
|
||||
if key not in _trans or lang not in _trans[key]:
|
||||
return disp(key)
|
||||
return _trans[key][lang]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,48 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from aqt.qt import *
|
||||
|
||||
try:
|
||||
_encoding = QApplication.UnicodeUTF8
|
||||
def _translate(context, text, disambig):
|
||||
return QApplication.translate(context, text, disambig, _encoding)
|
||||
except AttributeError:
|
||||
def _translate(context, text, disambig):
|
||||
return QApplication.translate(context, text, disambig)
|
||||
|
||||
class Ui_DialogUpdates(object):
|
||||
def setupUi(self, DialogUpdates):
|
||||
DialogUpdates.setObjectName(u"DialogUpdates")
|
||||
DialogUpdates.resize(500, 400)
|
||||
self.verticalLayout = QVBoxLayout(DialogUpdates)
|
||||
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||
self.labelUpdates = QLabel(DialogUpdates)
|
||||
self.labelUpdates.setWordWrap(True)
|
||||
self.labelUpdates.setOpenExternalLinks(True)
|
||||
self.labelUpdates.setObjectName(u"labelUpdates")
|
||||
self.verticalLayout.addWidget(self.labelUpdates)
|
||||
self.textBrowser = QTextBrowser(DialogUpdates)
|
||||
self.textBrowser.setObjectName(u"textBrowser")
|
||||
self.verticalLayout.addWidget(self.textBrowser)
|
||||
self.horizontalLayout = QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||
self.update = QPushButton(DialogUpdates)
|
||||
self.update.setObjectName(u"update")
|
||||
self.horizontalLayout.addWidget(self.update, 0, Qt.AlignCenter)
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
|
||||
self.retranslateUi(DialogUpdates)
|
||||
QMetaObject.connectSlotsByName(DialogUpdates)
|
||||
|
||||
def retranslateUi(self, DialogUpdates):
|
||||
DialogUpdates.setWindowTitle(_translate("DialogUpdates", "FastWQ - Updater", None))
|
||||
self.labelUpdates.setText(_translate(
|
||||
"DialogUpdates",
|
||||
"<html><head/><body>\
|
||||
<p>A new version of {0} is available for download! </p>\
|
||||
<p>Do you want to update {1}to version {2}?</p>\
|
||||
<p>Changes from your version are listed below:</p>\
|
||||
</body></html>",
|
||||
None
|
||||
))
|
||||
self.update.setText(_translate("DialogUpdates", "Update", None))
|
||||
@ -1,311 +0,0 @@
|
||||
try:
|
||||
import httplib
|
||||
except:
|
||||
import http.client as httplib
|
||||
try:
|
||||
import urllib2
|
||||
except:
|
||||
import urllib.request as urllib2
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import zipfile
|
||||
import traceback
|
||||
import io
|
||||
import aqt
|
||||
from aqt import mw
|
||||
from aqt.qt import *
|
||||
from aqt.utils import showInfo
|
||||
from anki.hooks import addHook
|
||||
from anki.utils import isMac, isWin
|
||||
from ..context import APP_ICON
|
||||
from .AnkiHub.updates import Ui_DialogUpdates
|
||||
from .AnkiHub.markdown2 import markdown
|
||||
|
||||
|
||||
__all__ = ['update']
|
||||
|
||||
|
||||
# taken from Anki's aqt/profiles.py
|
||||
def defaultBase():
|
||||
path = mw.pm.addonFolder()
|
||||
return os.path.dirname(os.path.abspath(path))
|
||||
|
||||
|
||||
headers = {"User-Agent": "AnkiHub"}
|
||||
dataPath = os.path.join(defaultBase(),'.fastwq_2.1.x_ankihub.json')
|
||||
|
||||
|
||||
class DialogUpdates(QDialog, Ui_DialogUpdates):
|
||||
|
||||
def __init__(self, parent, data, oldData, callback):
|
||||
parent = parent if parent else mw
|
||||
QDialog.__init__(self, parent)
|
||||
self.setModal(True)
|
||||
self.setWindowFlags(
|
||||
self.windowFlags() &
|
||||
~Qt.WindowContextHelpButtonHint
|
||||
)
|
||||
self.setWindowIcon(APP_ICON)
|
||||
self.setupUi(self)
|
||||
totalSize = sum(map(lambda x:x['size'],data['assets']))
|
||||
def answer():
|
||||
self.update.setEnabled(False)
|
||||
callback(self.appendHtml, self.finish)
|
||||
|
||||
self.html = u''
|
||||
self.appendHtml(markdown(data['body']))
|
||||
|
||||
#if not automaticAnswer:
|
||||
self.update.clicked.connect(lambda:answer())
|
||||
|
||||
fromVersion = ''
|
||||
if 'tag_name' in oldData:
|
||||
fromVersion = u'from {0} '.format(oldData['tag_name'])
|
||||
self.labelUpdates.setText(
|
||||
str(self.labelUpdates.text()).format(
|
||||
data['name'],
|
||||
fromVersion,
|
||||
data['tag_name']))
|
||||
|
||||
def appendHtml(self,html='',temp=''):
|
||||
self.html += html
|
||||
self.textBrowser.setHtml(u'<html><body>{0}{1}{2}</body></html>'.format(self.html, temp, u'<div id="text_bottom"></div>'))
|
||||
self.textBrowser.scrollToAnchor('text_bottom')
|
||||
|
||||
def finish(self):
|
||||
self.hide()
|
||||
self.destroy()
|
||||
showInfo('Updated. Please restart Anki.')
|
||||
pass
|
||||
|
||||
|
||||
def installZipFile(data, fname):
|
||||
#base = os.path.join(mw.pm.addonFolder(), 'fastwq')
|
||||
base = os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../'))
|
||||
if fname.endswith(".py"):
|
||||
path = os.path.join(base, fname)
|
||||
with open(path, "wb") as file:
|
||||
file.write(data)
|
||||
file.close()
|
||||
return True
|
||||
# .zip file
|
||||
try:
|
||||
z = zipfile.ZipFile(io.BytesIO(data))
|
||||
except zipfile.BadZipfile:
|
||||
return False
|
||||
for n in z.namelist():
|
||||
if n.endswith("/"):
|
||||
# folder; ignore
|
||||
continue
|
||||
# write
|
||||
try:
|
||||
z.extract(n, base)
|
||||
except:
|
||||
print(n)
|
||||
return True
|
||||
|
||||
|
||||
def asset(a):
|
||||
return {
|
||||
'url': a['browser_download_url'],
|
||||
'size': a['size']
|
||||
}
|
||||
|
||||
|
||||
def updateSingle(repositories, path, data):
|
||||
def callback(appendHtml, onReady):
|
||||
for asset in data['assets']:
|
||||
code = asset['url']
|
||||
p, fname = os.path.split(code)
|
||||
appendHtml(temp='<br />Downloading {1}: {0}%<br/>'.format(0,fname))
|
||||
try:
|
||||
urlthread = UrlThread(code)
|
||||
urlthread.start()
|
||||
urlthread.join()
|
||||
response = urlthread.response#urllib2.urlopen(code)
|
||||
meta = response.info()
|
||||
file_size = int(meta.get("Content-Length"))
|
||||
except:
|
||||
appendHtml('Downloading file error!<br/>')
|
||||
return
|
||||
d = b''
|
||||
dl = 0
|
||||
i = 0
|
||||
lastPercent = None
|
||||
while True:
|
||||
dkb = response.read(1024)
|
||||
if not dkb:
|
||||
break
|
||||
dl += len(dkb)
|
||||
d += dkb
|
||||
if dl*100/file_size>i:
|
||||
lastPercent = int(dl*100/file_size)
|
||||
i = lastPercent+1
|
||||
appendHtml(temp='<br />Downloading {1}: {0}%<br/>'.format(lastPercent,fname))
|
||||
QApplication.instance().processEvents()
|
||||
appendHtml('<br />Downloading {1}: 100%<br/>'.format(int(dl*100/file_size),fname))
|
||||
appendHtml('Installing ...<br/>')
|
||||
if not installZipFile(d, fname):
|
||||
appendHtml('Corrupt file<br/>')
|
||||
else:
|
||||
repositories[path] = data
|
||||
repositories[path]['update'] = 'ask'
|
||||
with open(dataPath,'w') as f:
|
||||
json.dump(repositories,f,indent=2)
|
||||
f.close()
|
||||
appendHtml('Done.<br/>')
|
||||
onReady() # close the AnkiHub update window
|
||||
|
||||
return callback
|
||||
|
||||
|
||||
def update(add=[], VERSION='v0.0.0', background=False, parent=None):
|
||||
parent = parent if parent else mw
|
||||
# progress win
|
||||
if not background:
|
||||
progresswin = QProgressDialog('Update Checking...', '', 0, 0, parent)
|
||||
progresswin.setWindowModality(Qt.ApplicationModal)
|
||||
progresswin.setCancelButton(None)
|
||||
progresswin.setWindowFlags(
|
||||
progresswin.windowFlags() &
|
||||
~Qt.WindowContextHelpButtonHint
|
||||
)
|
||||
progresswin.setWindowTitle('FastWQ - Updater')
|
||||
progresswin.setWindowIcon(APP_ICON)
|
||||
progresswin.resize(280, 60)
|
||||
progresswin.show()
|
||||
else:
|
||||
progresswin = None
|
||||
#
|
||||
conn = httplib.HTTPSConnection("api.github.com")
|
||||
try:
|
||||
with open(dataPath,'r') as f:
|
||||
repositories = json.load(f)
|
||||
f.close()
|
||||
except:
|
||||
repositories = {}
|
||||
|
||||
for a in add:
|
||||
if a not in repositories:
|
||||
repositories[a] = {
|
||||
'id': 0,
|
||||
'update': 'ask'
|
||||
}
|
||||
|
||||
for path,repository in repositories.items():
|
||||
username,repositoryName = path.split('/')
|
||||
try:
|
||||
urlthread = UrlThread("https://api.github.com/repos/{0}/releases/latest".format(path))
|
||||
urlthread.start()
|
||||
urlthread.join()
|
||||
release = json.loads(urlthread.response.read())
|
||||
except Exception as e:
|
||||
release = {}
|
||||
|
||||
if 'id' in release:
|
||||
if release['id'] != repository['id']:
|
||||
data = {
|
||||
'id': release['id'],
|
||||
'name': repositoryName,
|
||||
'tag_name': release['tag_name'],
|
||||
'body': '### {0}\n'.format(release['name']) + release['body'],
|
||||
'assets': [asset(release['assets'][1])],
|
||||
'update': 'ask'
|
||||
}
|
||||
if 'tag_name' in repository:
|
||||
oldVersion = map(int,repository['tag_name'][1:].split('.'))
|
||||
oldVersion = [x for x in oldVersion]
|
||||
while len(oldVersion)<3:
|
||||
oldVersion.append(0)
|
||||
else:
|
||||
oldVersion = map(int,VERSION[1:].split('.'))#[0,0,0]
|
||||
oldVersion = [x for x in oldVersion]
|
||||
newVersion = map(int,data['tag_name'][1:].split('.'))
|
||||
newVersion = [x for x in newVersion]
|
||||
isMinor = len(newVersion)>2 and newVersion[2]>0
|
||||
while len(newVersion)<3:
|
||||
newVersion.append(0)
|
||||
i = oldVersion[2]+1
|
||||
if oldVersion[0]<newVersion[0] or oldVersion[1]<newVersion[1]:
|
||||
if isMinor:
|
||||
i = 1
|
||||
while i<newVersion[2]:
|
||||
if progresswin and progresswin.wasCanceled():
|
||||
break
|
||||
try:
|
||||
minorTagName = 'v{0}.{1}.{2}'.format(newVersion[0],oldVersion[1],i)
|
||||
urlthread = UrlThread("https://api.github.com/repos/{0}/releases/tags/{1}".format(path,minorTagName))
|
||||
urlthread.start()
|
||||
urlthread.join()
|
||||
responseData = urlthread.response.read()
|
||||
minor = json.loads(responseData)
|
||||
data['body'] += '\n\n### {0}\n'.format(minor['name']) + minor['body']
|
||||
except:
|
||||
pass
|
||||
i += 1
|
||||
if oldVersion[0]<newVersion[0] or oldVersion[1]<newVersion[1]:
|
||||
# new major release necessary!
|
||||
# if the newest version is minor, fetch the additional assets from the major
|
||||
if isMinor and (background or not progresswin.wasCanceled()):
|
||||
try:
|
||||
majorTagName = 'v{0}.{1}'.format(newVersion[0],newVersion[1])
|
||||
urlthread = UrlThread(
|
||||
"https://api.github.com/repos/{0}/releases/tags/{1}".format(path,majorTagName),
|
||||
"https://api.github.com/repos/{0}/releases/tags/{1}.0".format(path,majorTagName)
|
||||
)
|
||||
urlthread.start()
|
||||
urlthread.join()
|
||||
responseData = urlthread.response.read()
|
||||
major = json.loads(responseData)
|
||||
data['body'] += '\n\n### {0}\n'.format(major['name']) + major['body']
|
||||
except:
|
||||
pass
|
||||
|
||||
if background or not progresswin.wasCanceled():
|
||||
if progresswin:
|
||||
progresswin.hide()
|
||||
progresswin.destroy()
|
||||
dialog = DialogUpdates(parent, data, repository, updateSingle(repositories, path, data))
|
||||
dialog.exec_()
|
||||
dialog.destroy()
|
||||
else:
|
||||
if progresswin:
|
||||
progresswin.hide()
|
||||
progresswin.destroy()
|
||||
return 1
|
||||
else:
|
||||
if progresswin:
|
||||
progresswin.hide()
|
||||
progresswin.destroy()
|
||||
return 0
|
||||
if progresswin:
|
||||
progresswin.hide()
|
||||
progresswin.destroy()
|
||||
return -1
|
||||
|
||||
|
||||
class UrlThread(QThread):
|
||||
|
||||
def __init__(self, url, backurl=None):
|
||||
super(UrlThread, self).__init__()
|
||||
self.response = None
|
||||
self.url = url
|
||||
self.backurl = backurl
|
||||
self.finished = False
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.response = urllib2.urlopen(self.url)
|
||||
except:
|
||||
if self.backurl:
|
||||
try:
|
||||
self.response = urllib2.urlopen(self.backurl)
|
||||
except:
|
||||
pass
|
||||
self.finished = True
|
||||
|
||||
def join(self):
|
||||
while not self.finished:
|
||||
QApplication.instance().processEvents()
|
||||
self.wait(30)
|
||||
@ -1,17 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from .readmdict import MDX, MDD
|
||||
from struct import pack, unpack
|
||||
from io import BytesIO
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
import sqlite3
|
||||
import json
|
||||
|
||||
import os
|
||||
import re
|
||||
import sqlite3
|
||||
import sys
|
||||
# zlib compression is used for engine version >=2.0
|
||||
import zlib
|
||||
from io import BytesIO
|
||||
from struct import pack, unpack
|
||||
|
||||
import chardet
|
||||
|
||||
from .readmdict import MDD, MDX
|
||||
|
||||
# LZO compression is used for engine version < 2.0
|
||||
try:
|
||||
import lzo
|
||||
@ -110,16 +112,22 @@ class IndexBuilder(object):
|
||||
|
||||
def _replace_stylesheet(self, txt):
|
||||
# substitute stylesheet definition
|
||||
encoding = 'utf-8'
|
||||
if isinstance(txt, bytes):
|
||||
encode_type = chardet.detect(txt)
|
||||
encoding = encode_type['encoding']
|
||||
txt = txt.decode(encoding)
|
||||
txt_list = re.split('`\d+`', txt)
|
||||
txt_tag = re.findall('`\d+`', txt)
|
||||
txt_styled = txt_list[0]
|
||||
for j, p in enumerate(txt_list[1:]):
|
||||
style = self._stylesheet[txt_tag[j][1:-1]]
|
||||
if p and p[-1] == '\n':
|
||||
txt_styled = txt_styled + style[0] + p.rstrip() + style[1] + '\r\n'
|
||||
txt_styled = txt_styled + style[0] + p.rstrip(
|
||||
) + style[1] + '\r\n'
|
||||
else:
|
||||
txt_styled = txt_styled + style[0] + p + style[1]
|
||||
return txt_styled
|
||||
return txt_styled.encode(encoding)
|
||||
|
||||
def _make_mdx_index(self, db_name):
|
||||
if os.path.exists(db_name):
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2018 sthoo <sth201807@gmail.com>
|
||||
#
|
||||
@ -17,29 +17,26 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import io
|
||||
import shutil
|
||||
import unicodedata
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from aqt.qt import *
|
||||
from aqt.utils import showInfo
|
||||
|
||||
from ..constants import Template
|
||||
from ..context import config
|
||||
from ..service import service_pool, QueryResult, copy_static_file
|
||||
from ..libs.snowballstemmer import stemmer
|
||||
from ..service import QueryResult, copy_static_file, service_pool
|
||||
from ..service.base import LocalService
|
||||
from ..utils import wrap_css
|
||||
from ..libs.snowballstemmer import stemmer
|
||||
|
||||
|
||||
__all__ = [
|
||||
'InvalidWordException', 'update_note_fields',
|
||||
'update_note_field', 'promot_choose_css', 'add_to_tmpl',
|
||||
'query_flds', 'inspect_note'
|
||||
'InvalidWordException', 'update_note_fields', 'update_note_field',
|
||||
'promot_choose_css', 'add_to_tmpl', 'query_flds', 'inspect_note'
|
||||
]
|
||||
|
||||
|
||||
@ -127,19 +124,14 @@ def promot_choose_css(missed_css):
|
||||
if not os.path.exists(filename) and not css['file'] in checked:
|
||||
checked.add(css['file'])
|
||||
showInfo(
|
||||
Template.miss_css.format(
|
||||
dict=css['title'],
|
||||
css=css['file']
|
||||
)
|
||||
)
|
||||
Template.miss_css.format(dict=css['title'], css=css['file']))
|
||||
try:
|
||||
filepath = css['dict_path'][:css['dict_path'].rindex(
|
||||
os.path.sep)+1]
|
||||
filepath = css['dict_path'][:css['dict_path'].rindex(os.path.
|
||||
sep) + 1]
|
||||
filepath = QFileDialog.getOpenFileName(
|
||||
directory=filepath,
|
||||
caption=u'Choose css file',
|
||||
filter=u'CSS (*.css)'
|
||||
)
|
||||
filter=u'CSS (*.css)')
|
||||
if filepath:
|
||||
shutil.copy(filepath, filename)
|
||||
wrap_css(filename)
|
||||
@ -161,15 +153,17 @@ def add_to_tmpl(note, **kwargs):
|
||||
if js and js.strip():
|
||||
addings = js.strip()
|
||||
if addings not in afmt:
|
||||
if not addings.startswith(u'<script') and not addings.endswith(u'/script>'):
|
||||
addings = u'\n<script type="text/javascript">\n{}\n</script>'.format(addings)
|
||||
if not addings.startswith(u'<script') and not addings.endswith(
|
||||
u'/script>'):
|
||||
addings = u'\n<script type="text/javascript">\n{}\n</script>'.format(
|
||||
addings)
|
||||
afmt += addings
|
||||
if jsfile:
|
||||
#new_jsfile = u'_' + \
|
||||
# jsfile if not jsfile.startswith(u'_') else jsfile
|
||||
#copy_static_file(jsfile, new_jsfile)
|
||||
#addings = u'\r\n<script src="{}"></script>'.format(new_jsfile)
|
||||
#afmt += addings
|
||||
# new_jsfile = u'_' + \
|
||||
# jsfile if not jsfile.startswith(u'_') else jsfile
|
||||
# copy_static_file(jsfile, new_jsfile)
|
||||
# addings = u'\r\n<script src="{}"></script>'.format(new_jsfile)
|
||||
# afmt += addings
|
||||
jsfile = jsfile if isinstance(jsfile, list) else [jsfile]
|
||||
for fn in jsfile:
|
||||
addings = '''
|
||||
@ -204,17 +198,17 @@ def query_flds(note, fileds=None):
|
||||
continue
|
||||
if i == len(note.fields):
|
||||
break
|
||||
#ignore field
|
||||
# ignore field
|
||||
ignore = each.get('ignore', False)
|
||||
if ignore:
|
||||
continue
|
||||
#skip valued
|
||||
# skip valued
|
||||
skip = each.get('skip_valued', False)
|
||||
if skip and len(note.fields[i]) != 0:
|
||||
continue
|
||||
#cloze
|
||||
# cloze
|
||||
cloze = each.get('cloze_word', False)
|
||||
#normal
|
||||
# normal
|
||||
dict_unique = each.get('dict_unique', '').strip()
|
||||
dict_fld_ord = each.get('dict_fld_ord', -1)
|
||||
fld_ord = each.get('fld_ord', -1)
|
||||
@ -227,9 +221,9 @@ def query_flds(note, fileds=None):
|
||||
services[dict_unique] = s
|
||||
if s and s.support:
|
||||
tasks.append({
|
||||
'k': dict_unique,
|
||||
'k': dict_unique,
|
||||
'w': word,
|
||||
'f': dict_fld_ord,
|
||||
'f': dict_fld_ord,
|
||||
'i': fld_ord,
|
||||
'cloze': cloze,
|
||||
})
|
||||
@ -237,17 +231,17 @@ def query_flds(note, fileds=None):
|
||||
success_num = 0
|
||||
result = defaultdict(QueryResult)
|
||||
for task in tasks:
|
||||
#try:
|
||||
service = services.get(task['k'], None)
|
||||
qr = service.active(task['f'], task['w'])
|
||||
if qr:
|
||||
if task['cloze']:
|
||||
qr['result'] = cloze_deletion(qr['result'], word)
|
||||
result.update({task['i']: qr})
|
||||
success_num += 1
|
||||
#except:
|
||||
# showInfo(_("NO_QUERY_WORD"))
|
||||
# pass
|
||||
try:
|
||||
service = services.get(task['k'], None)
|
||||
qr = service.active(task['f'], task['w'])
|
||||
if qr:
|
||||
if task['cloze']:
|
||||
qr['result'] = cloze_deletion(qr['result'], word)
|
||||
result.update({task['i']: qr})
|
||||
success_num += 1
|
||||
except Exception as e:
|
||||
print(_("NO_QUERY_WORD"), e)
|
||||
pass
|
||||
|
||||
missed_css = list()
|
||||
for service in services.values():
|
||||
@ -284,13 +278,15 @@ def cloze_deletion(text, cloze):
|
||||
continue
|
||||
word = text[s:e]
|
||||
if _stemmer.stemWord(word).lower() == term:
|
||||
l = len(cloze)
|
||||
ln = len(cloze)
|
||||
w = word
|
||||
if w[:l].lower() == cloze.lower():
|
||||
e = s + l
|
||||
w = word[:l]
|
||||
result = result[:s+offset] + (config.cloze_str % w) + result[e+offset:]
|
||||
offset += len(config.cloze_str)-2
|
||||
if w[:ln].lower() == cloze.lower():
|
||||
e = s + ln
|
||||
w = word[:ln]
|
||||
result = result[:s + offset] + (
|
||||
config.cloze_str % w) + result[e + offset:]
|
||||
offset += len(config.cloze_str) - 2
|
||||
return result
|
||||
|
||||
|
||||
_stemmer = stemmer('english')
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#-*- coding:utf-8 -*-
|
||||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2018 sthoo <sth201807@gmail.com>
|
||||
#
|
||||
@ -19,6 +19,7 @@
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import random
|
||||
# use ntpath module to ensure the windows-style (e.g. '\\LDOCE.css')
|
||||
# path can be processed on Unix platform.
|
||||
# However, anki version on mac platforms doesn't including this package?
|
||||
@ -27,32 +28,33 @@ import re
|
||||
import shutil
|
||||
import sqlite3
|
||||
import urllib
|
||||
import zlib
|
||||
from collections import defaultdict
|
||||
from functools import wraps
|
||||
from hashlib import md5, sha1
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from aqt import mw
|
||||
from aqt.qt import QMutex, QThread
|
||||
|
||||
from ..context import config
|
||||
from ..lang import _cl
|
||||
from ..libs import MdxBuilder, StardictBuilder
|
||||
from ..utils import MapDict, wrap_css
|
||||
|
||||
try:
|
||||
import urllib2
|
||||
except:
|
||||
except Exception:
|
||||
import urllib.request as urllib2
|
||||
|
||||
import zlib
|
||||
import random
|
||||
from collections import defaultdict
|
||||
from functools import wraps
|
||||
from hashlib import md5
|
||||
from hashlib import sha1
|
||||
|
||||
try:
|
||||
from cookielib import CookieJar
|
||||
except:
|
||||
except Exception:
|
||||
from http.cookiejar import CookieJar
|
||||
|
||||
from aqt import mw
|
||||
from aqt.qt import QThread, QMutex
|
||||
from bs4 import BeautifulSoup
|
||||
from ..context import config
|
||||
from ..libs import MdxBuilder, StardictBuilder
|
||||
from ..utils import MapDict, wrap_css
|
||||
from ..lang import _cl
|
||||
|
||||
try:
|
||||
import threading as _threading
|
||||
@ -69,18 +71,14 @@ __all__ = [
|
||||
def get_hex_name(prefix, val, suffix):
|
||||
''' get sha1 hax name '''
|
||||
hex_digest = sha1(val.encode('utf-8')).hexdigest().lower()
|
||||
name = '.'.join([
|
||||
'-'.join([
|
||||
prefix, hex_digest[:8], hex_digest[8:16],
|
||||
hex_digest[16:24], hex_digest[24:32], hex_digest[32:],
|
||||
]),
|
||||
suffix,
|
||||
])
|
||||
name = '.'.join(['-'.join([prefix, hex_digest[:8], hex_digest[8:16], hex_digest[16:24], hex_digest[24:32], hex_digest[32:], ]), suffix, ])
|
||||
return name
|
||||
|
||||
|
||||
def _is_method_or_func(object):
|
||||
return inspect.isfunction(object) or inspect.ismethod(object)
|
||||
|
||||
|
||||
def register(labels):
|
||||
"""
|
||||
register the dict service with a labels, which will be shown in the dicts list.
|
||||
@ -120,6 +118,8 @@ def export(labels):
|
||||
export.EXPORT_INDEX += 1
|
||||
return _deco
|
||||
return _with
|
||||
|
||||
|
||||
export.EXPORT_INDEX = 0
|
||||
|
||||
|
||||
@ -181,8 +181,9 @@ def with_styles(**styles):
|
||||
return _deco
|
||||
return _with
|
||||
|
||||
# bs4 threading lock, overload protection
|
||||
_BS_LOCKS = [_threading.Lock(), _threading.Lock()]
|
||||
|
||||
_BS_LOCKS = [_threading.Lock(), _threading.Lock()] # bs4 threading lock, overload protection
|
||||
|
||||
|
||||
def parse_html(html):
|
||||
'''
|
||||
@ -238,6 +239,7 @@ class Service(object):
|
||||
@property
|
||||
def unique(self):
|
||||
return self._unique
|
||||
|
||||
@unique.setter
|
||||
def unique(self, value):
|
||||
self._unique = value
|
||||
@ -326,7 +328,7 @@ class WebService(Service):
|
||||
if response.info().get('Content-Encoding') == 'gzip':
|
||||
data = zlib.decompress(data, 16 + zlib.MAX_WBITS)
|
||||
return data
|
||||
except:
|
||||
except Exception:
|
||||
return u''
|
||||
|
||||
@classmethod
|
||||
@ -340,7 +342,7 @@ class WebService(Service):
|
||||
'(KHTML, like Gecko) Chrome/31.0.1623.0 Safari/537.36'
|
||||
}).content)
|
||||
return True
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
class TinyDownloadError(ValueError):
|
||||
@ -370,7 +372,7 @@ class WebService(Service):
|
||||
be added onto the stream returned. This is helpful for some web
|
||||
services that sometimes return MP3s that `mplayer` clips early.
|
||||
"""
|
||||
DEFAULT_UA = 'Mozilla/5.0'
|
||||
DEFAULT_UA = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36'
|
||||
DEFAULT_TIMEOUT = 3
|
||||
|
||||
PADDING = '\0' * 2**11
|
||||
@ -434,7 +436,7 @@ class WebService(Service):
|
||||
try:
|
||||
value_error.payload = response.read()
|
||||
response.close()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
raise value_error
|
||||
|
||||
@ -475,7 +477,7 @@ class WebService(Service):
|
||||
f.write(payload)
|
||||
f.close()
|
||||
return True
|
||||
except:
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
@ -516,7 +518,7 @@ class LocalService(Service):
|
||||
def _get_builer(key, func=None):
|
||||
LocalService._mutex_builder.lock()
|
||||
key = md5(str(key).encode('utf-8')).hexdigest()
|
||||
if not func is None:
|
||||
if not(func is None):
|
||||
if not LocalService._mdx_builders[key]:
|
||||
worker = _DictBuildWorker(func)
|
||||
worker.start()
|
||||
@ -582,21 +584,22 @@ class MdxService(LocalService):
|
||||
jsfile = re.findall(r'<script .*?src=[\'\"](.+?)[\'\"]', html, re.DOTALL)
|
||||
return QueryResult(result=html, js=u'\n'.join(js), jsfile=jsfile)
|
||||
|
||||
|
||||
def _get_definition_mdx(self):
|
||||
"""according to the word return mdx dictionary page"""
|
||||
content = self.builder.mdx_lookup(self.word, ignorecase=config.ignore_mdx_wordcase)
|
||||
ignorecase = config.ignore_mdx_wordcase and (self.word != self.word.lower() or self.word != self.word.upper())
|
||||
content = self.builder.mdx_lookup(self.word, ignorecase=ignorecase)
|
||||
str_content = ""
|
||||
if len(content) > 0:
|
||||
for c in content:
|
||||
str_content += c.replace("\r\n","").replace("entry:/","")
|
||||
str_content += c.replace("\r\n", "").replace("entry:/", "")
|
||||
|
||||
return str_content
|
||||
|
||||
def _get_definition_mdd(self, word):
|
||||
"""according to the keyword(param word) return the media file contents"""
|
||||
word = word.replace('/', '\\')
|
||||
content = self.builder.mdd_lookup(word, ignorecase=config.ignore_mdx_wordcase)
|
||||
ignorecase = config.ignore_mdx_wordcase and (word != word.lower() or word != word.upper())
|
||||
content = self.builder.mdd_lookup(word, ignorecase=ignorecase)
|
||||
if len(content) > 0:
|
||||
return [content[0]]
|
||||
else:
|
||||
@ -620,7 +623,7 @@ class MdxService(LocalService):
|
||||
f.write(bytes_list[0])
|
||||
return savepath
|
||||
except sqlite3.OperationalError as e:
|
||||
#showInfo(str(e))
|
||||
print(e)
|
||||
pass
|
||||
return ''
|
||||
|
||||
@ -677,7 +680,7 @@ class MdxService(LocalService):
|
||||
# folder first, and it will also execute the wrap process to generate
|
||||
# the desired file.
|
||||
if not os.path.exists(cssfile):
|
||||
css_src = self.dict_path.replace(self._filename+u'.mdx', f)
|
||||
css_src = self.dict_path.replace(self._filename + u'.mdx', f)
|
||||
if os.path.exists(css_src):
|
||||
shutil.copy(css_src, cssfile)
|
||||
else:
|
||||
@ -700,17 +703,19 @@ class MdxService(LocalService):
|
||||
if os.path.exists(savepath):
|
||||
return savepath
|
||||
try:
|
||||
src_fn = self.dict_path.replace(self._filename+u'.mdx', basename)
|
||||
src_fn = self.dict_path.replace(self._filename + u'.mdx', basename)
|
||||
if os.path.exists(src_fn):
|
||||
shutil.copy(src_fn, savepath)
|
||||
return savepath
|
||||
else:
|
||||
bytes_list = self.builder.mdd_lookup(filepath_in_mdx, ignorecase=config.ignore_mdx_wordcase)
|
||||
ignorecase = config.ignore_mdx_wordcase and (filepath_in_mdx != filepath_in_mdx.lower() or filepath_in_mdx != filepath_in_mdx.upper())
|
||||
bytes_list = self.builder.mdd_lookup(filepath_in_mdx, ignorecase=ignorecase)
|
||||
if bytes_list:
|
||||
with open(savepath, 'wb') as f:
|
||||
f.write(bytes_list[0])
|
||||
return savepath
|
||||
except sqlite3.OperationalError as e:
|
||||
print('save default file error', e)
|
||||
pass
|
||||
|
||||
def save_media_files(self, data):
|
||||
@ -752,7 +757,7 @@ class StardictService(LocalService):
|
||||
dict_path,
|
||||
service_wrap(StardictBuilder, dict_path, in_memory=False)
|
||||
)
|
||||
#if self.builder:
|
||||
# if self.builder:
|
||||
# self.builder.get_header()
|
||||
|
||||
@staticmethod
|
||||
@ -772,7 +777,7 @@ class StardictService(LocalService):
|
||||
|
||||
@export([u'默认', u'Default'])
|
||||
def fld_whole(self):
|
||||
#self.builder.check_build()
|
||||
# self.builder.check_build()
|
||||
try:
|
||||
result = self.builder[self.word]
|
||||
result = result.strip().replace('\r\n', '<br />')\
|
||||
@ -788,7 +793,7 @@ class QueryResult(MapDict):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(QueryResult, self).__init__(*args, **kwargs)
|
||||
# avoid return None
|
||||
if self['result'] == None:
|
||||
if self['result'] is None:
|
||||
self['result'] = ""
|
||||
|
||||
def set_styles(self, **kwargs):
|
||||
|
||||
@ -51,6 +51,9 @@ class Cambridge(WebService):
|
||||
if snd:
|
||||
result['pronunciation'][pn+'mp3'] = cambridge_url_base + snd.get('data-src-mp3')
|
||||
header_found = True
|
||||
# 词性
|
||||
pg = element.find('span', class_='posgram ico-bg')
|
||||
|
||||
#义
|
||||
body = element.find('div', class_='pos-body')
|
||||
if body:
|
||||
@ -63,9 +66,11 @@ class Cambridge(WebService):
|
||||
trans = tag.find('span', class_='trans')
|
||||
es = tag.find_all('div', class_='examp emphasized')
|
||||
l.append(
|
||||
u'<li>{0}{1}{2}{3}</li>'.format(
|
||||
u'<li>{0}{1}{2} {3}{4}</li>'.format(
|
||||
'<span class="epp-xref">{0}</span>'.format(pg.get_text() if pg else ''),
|
||||
u'<span class="epp-xref">{0}</span>'.format(i.get_text()) if i else u'',
|
||||
u'<b class="def">{0}</b>'.format(d.get_text()) if d else u'',
|
||||
|
||||
u'<span class="trans">{0}</span>'.format(trans.get_text()) if trans else u'',
|
||||
u''.join(
|
||||
u'<div class="examp">{0}</div>'.format(e.get_text()) if e else u''
|
||||
|
||||
Loading…
Reference in New Issue
Block a user