Fix ui bug; Suport Multiple language for Service;

This commit is contained in:
St.Huang 2018-07-06 20:56:20 +08:00
parent 4aee5365ba
commit f95f8e9f6e
10 changed files with 145 additions and 119 deletions

View File

@ -51,7 +51,7 @@ class Config(object):
data['%s_last' % self.pmname] = data.get('last_model', self.last_model_id)
self.data.update(data)
with open(self.path, 'wb') as f:
json.dump(self.data, f)
json.dump(self.data, f, indent=4, sort_keys=True)
f.close()
def read(self):

View File

@ -18,54 +18,80 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from anki.lang import currentLang
trans = {
'CHECK_FILENAME_LABEL': {'zh_CN': u'使用文件名作为标签', 'en': u'Use filename as dict label', 'fr': r"Utiliser le nom de fichier en tant qu'étiquette de dico"},
'EXPORT_MEDIA': {'zh_CN': u'导出媒体文件', 'en': u'Export media files', 'fr': u'Exporter les fichiers multimédias'},
'DICTS_FOLDERS': {'zh_CN': u'字典文件夹', 'en': u'Dict folders', 'fr': u'Dossiers dico'},
'CHOOSE_NOTE_TYPES': {'zh_CN': u'选择笔记类型', 'en': u'Choose note types', 'fr': u'Choisir le type de note '},
'CURRENT_NOTE_TYPE': {'zh_CN': u'当前类型', 'en': u'Current type', 'fr': u'Type utilisé en cours'},
'MDX_SERVER': {'zh_CN': u'MDX服务器', 'en': u'MDX server', 'fr': u'serveur MDX'},
'USE_DICTIONARY': {'zh_CN': u'使用字典', 'en': u'Use dict', 'fr': u'Utilisé un dico'},
'UPDATED': {'zh_CN': u'更新', 'en': u'Updated', 'fr': u'Mettre à jour'},
'CARDS': {'zh_CN': u'卡片', 'en': u'Cards', 'fr': u'Cartes'},
'FAILURE': {'zh_CN': u'失败', 'en': u'Failure', 'fr': u'Échec'},
'SUCCESS': {'zh_CN': u'成功', 'en': u'Success', 'fr': u'Succès'},
'QUERIED': {'zh_CN': u'查询', 'en': u'Queried', 'fr': u'Quêté'},
'FIELDS': {'zh_CN': u'字段', 'en': u'Fields', 'fr': u'Champs'},
'WORDS': {'zh_CN': u'单词', 'en': u'Words', 'fr': u'Mots'},
'NOT_DICT_FIELD': {'zh_CN': u'不是字典字段', 'en': u'Not dict field', 'fr': u'Pas un champ de dico'},
'NOTE_TYPE_FIELDS': {'zh_CN': u'<b>笔记字段</b>', 'en': u'<b>Note fields</b>', 'fr': u'<b>Champ de note</b>'},
'DICTS': {'zh_CN': u'<b>字典</b>', 'en': u'<b>Dict</b>', 'fr': u'<b>Dico</b>'},
'DICT_FIELDS': {'zh_CN': u'<b>字典字段</b>', 'en': u'<b>Dict fields</b>', 'fr': u'<b>Champ de dico</b>'},
'RADIOS_DESC': {'zh_CN': u'<b>单选框选中为待查询单词字段</b>', 'en': u'<b>Word field needs to be selected.</b>', 'fr': u'<b>Champ de mot doit d\'être sélectionné. </b>'},
'NO_QUERY_WORD': {'zh_CN': u'查询字段无单词', 'en': u'No word is found in the query field', 'fr': u'Aucun est trouvé dans le champ'},
'CSS_NOT_FOUND': {'zh_CN': u'没有找到CSS文件请手动选择', 'en': u'No valid css stylesheets found, please choose the file', 'fr': u'Aucun fichier de style CSS est valide, veuillez choisir le fichier'},
'ABOUT': {'zh_CN': u'关于', 'en': u'About', 'fr': u'À propos'},
'REPOSITORY': {'zh_CN': u'项目地址', 'en': u'Project homepage', 'fr': u'Accueil du projet'},
'FEEDBACK': {'zh_CN': u'反馈', 'en': u'Feedback', 'fr': u'Retourner de l\'information'},
'VERSION': {'zh_CN': u'版本', 'en': u'Version', 'fr': u'Version'},
'LATEST_VERSION': {'zh_CN': u'无更新版本.', 'en': u'No update version.', 'fr': u'Pas de mise à jour.'},
'ABNORMAL_VERSION': {'zh_CN': u'当前版本异常.', 'en': u'The current version is abnormal.', 'fr': u'La version actuelle est anormale.'},
'CHECK_FAILURE': {'zh_CN': u'版本检查失败.', 'en': u'Version check failure.', 'fr': u'Erreur de vérifier la version.'},
'NEW_VERSION': {'zh_CN': u'检查到新版本:', 'en': u'New version:', 'fr': u'Nouvelle version:'},
'UPDATE': {'zh_CN': u'更新', 'en': u'Update', 'fr': u'Mise à jour'},
'FORCE_UPDATE': {'zh_CN': u'强制更新字段', 'en': u'Force update'},
'SETTINGS': {'zh_CN': u'参数', 'en': u'Settings'},
'THREAD_NUMBER': {'zh_CN': u'线程数', 'en': u'Thread'},
}
__all__ = ['_', '_cl', '_sl']
#Language Define, [Key, zh_CN, en]
arr = [
['CHECK_FILENAME_LABEL', u'使用文件名作为标签', u'Use filename as dict label'],
['EXPORT_MEDIA', u'导出媒体文件', u'Export media files'],
['DICTS_FOLDERS', u'字典文件夹', u'Dict folders'],
['CHOOSE_NOTE_TYPES', u'选择笔记类型', u'Choose note types'],
['CURRENT_NOTE_TYPE', u'当前类型', u'Current type'],
['MDX_SERVER', u'MDX服务器', u'MDX server'],
['USE_DICTIONARY', u'使用字典', u'Use dict'],
['UPDATED', u'更新', u'Updated'],
['CARDS', u'卡片', u'Cards'],
['FAILURE', u'失败', u'Failure'],
['SUCCESS', u'成功', u'Success'],
['QUERIED', u'查询', u'Queried'],
['FIELDS', u'字段', u'Fields'],
['WORDS', u'单词', u'Words'],
['NOT_DICT_FIELD', u'忽略', u'Ignore'], #不是字典字段
['NOTE_TYPE_FIELDS', u'<b>笔记字段</b>', u'<b>Note fields</b>'],
['DICTS', u'<b>字典</b>', u'<b>Dict</b>'],
['DICT_FIELDS', u'<b>字典字段</b>', u'<b>Dict fields</b>'],
['RADIOS_DESC', u'<b>单选框选中为待查询单词字段</b>', u'<b>Word field needs to be selected.</b>'],
['NO_QUERY_WORD', u'查询字段无单词', u'No word is found in the query field'],
['CSS_NOT_FOUND', u'没有找到CSS文件请手动选择', u'No valid css stylesheets found, please choose the file'],
['ABOUT', u'关于', u'About'],
['REPOSITORY', u'项目地址', u'Project homepage'],
['FEEDBACK', u'反馈', u'Feedback'],
['VERSION', u'版本', u'Version'],
['LATEST_VERSION', u'已经是最新版本.', u'It\'s the lastest version.'],
['ABNORMAL_VERSION', u'当前版本异常.', u'The current version is abnormal.'],
['CHECK_FAILURE', u'版本检查失败.', u'Version check failure.'],
['NEW_VERSION', u'检查到新版本:', u'New version:'],
['UPDATE', u'更新', u'Update'],
['FORCE_UPDATE', u'强制更新字段', u'Force update'],
['SETTINGS', u'参数', u'Settings'],
['THREAD_NUMBER', u'线程数', u'Thread'],
['BRE_PRON', u'英式发音', u'British Pronunciation'],
['AME_PRON', u'美式发音', u'American Pronunciation'],
['PRON', u'发音', u'Pronunciation'],
['EXAMPLE', u'例句', u'Example'],
['TRANS', u'翻译', u'Translation'],
['DEF', u'释义', u'Definition'],
['PHON', u'音标', u'Phonetic'],
['BRE_PHON', u'英式音标', u'British Phonetic'],
['AME_PHON', u'美式音标', u'American Phonetic'],
['IMAGE', u'图片', u'Image'],
]
trans = {item[0]: {'zh_CN': item[1], 'en': item[2]} for item in arr}
def _(key, lang=currentLang):
if lang != 'zh_CN' and lang != 'en' and lang != 'fr':
lang = 'en' # fallback
if lang != 'zh_CN' and lang != 'en':
lang = 'en'
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]
def _cl(labels, lang=currentLang):
if isinstance(labels, basestring):
return _(labels)
if lang != 'zh_CN' and lang != 'en':
lang = 'en'
return labels[0] if lang == 'zh_CN' else labels[1]
def _sl(key):
return trans[key].values()

View File

@ -52,12 +52,12 @@ class ProgressWindow(object):
self._msg_count['fails_number']
)
if words_number or fields_number:
number_info += _('QUERIED') + '<br>' + 45 * '-'
number_info += u'<br>{0}: {1}{2}'.format(
number_info += _('QUERIED') + u'<br>' + 45 * u'-'
number_info += u'<br>{0}: {1} {2}'.format(
_('SUCCESS'), words_number, _('WORDS'))
number_info += u'<br>{0}: {1}{2}'.format(
number_info += u'<br>{0}: {1} {2}'.format(
_('UPDATE'), fields_number, _('FIELDS'))
number_info += u'<br>{0}: {1}{2}'.format(
number_info += u'<br>{0}: {1} {2}'.format(
_('FAILURE'), fails_number, _('WORDS'))
self._update(label=number_info, value=words_number)

View File

@ -12,7 +12,7 @@ MAPPINGS = [
LANG_TO_REGEXPS = {lang: regexps for lang, regexps in MAPPINGS}
@register(u'本地词典-LDOCE6')
@register([u'本地词典-LDOCE6', u'MDX-LDOCE6'])
class Ldoce6(MdxService):
def __init__(self):
@ -26,7 +26,7 @@ class Ldoce6(MdxService):
def title(self):
return self.__register_label__
@export(u'音标', 1)
@export('PHON', 1)
def fld_phonetic(self):
html = self.get_html()
m = re.search(r'<span class="pron">(.*?)</span>', html)
@ -59,15 +59,15 @@ class Ldoce6(MdxService):
return self.get_anki_label(name, 'audio')
return ''
@export(u'英式发音', 2)
@export('BRE_PRON', 2)
def fld_voicebre(self):
return self._fld_voice(self.get_html(), 'br')
@export(u'美式发音', 3)
@export('AME_PRON', 3)
def fld_voiceame(self):
return self._fld_voice(self.get_html(), 'us')
@export(u'例句', 4)
@export('EXAMPLE', 4)
def fld_sentence(self):
m = re.findall(r'<span class="example"\s*.*>\s*.*<\/span>', self.get_html())
if m:
@ -84,7 +84,7 @@ class Ldoce6(MdxService):
return self._css(my_str)
return ''
@export(u'释义', 5)
@export('DEF', 5)
def fld_definate(self):
m = m = re.findall(r'<span class="def"\s*.*>\s*.*<\/span>', self.get_html())
if m:

View File

@ -1,24 +1,16 @@
#-*- coding:utf-8 -*-
import json
import os
import re
import urllib
import urllib2
import xml.etree.ElementTree
from collections import defaultdict
from aqt.utils import showInfo
from .base import WebService, export, register
from ..utils import ignore_exception
bcz_download_mp3 = True
bcz_download_img = True
@register(u'百词斩')
@register([u'百词斩', u'Baicizhan'])
class Baicizhan(WebService):
bcz_download_mp3 = True
bcz_download_img = True
def __init__(self):
super(Baicizhan, self).__init__()
@ -26,18 +18,17 @@ class Baicizhan(WebService):
word = self.word.replace(' ', '_')
url = u"http://mall.baicizhan.com/ws/search?w={}".format(word)
try:
html = urllib2.urlopen(url, timeout=5).read()
html = self.get_response(url, timeout=5)#urllib2.urlopen(url, timeout=5).read()
return self.cache_this(json.loads(html))
except:
return defaultdict(str)
# @ignore_exception
@export(u'发音', 0)
@export('PRON', 0)
def fld_phonetic(self):
word = self.word.replace(' ', '_')
url = u'http://baicizhan.qiniucdn.com/word_audios/{}.mp3'.format(word)
audio_name = 'bcz_%s.mp3' % self.word
if bcz_download_mp3:
if self.bcz_download_mp3:
if self.download(url, audio_name):
# urllib.urlretrieve(url, audio_name)
with open(audio_name, 'rb') as f:
@ -56,43 +47,43 @@ class Baicizhan(WebService):
def _get_field(self, key, default=u''):
return self.cache_result(key) if self.cached(key) else self._get_from_api().get(key, default)
@export(u'音标', 1)
@export('PHON', 1)
def fld_explains(self):
return self._get_field('accent')
@export(u'图片', 2)
@export('IMAGE', 2)
def fld_img(self):
url = self._get_field('img')
if url and bcz_download_img:
if url and self.bcz_download_img:
filename = url[url.rindex('/') + 1:]
if self.download(url, filename):
return self.get_anki_label(filename, 'img')
#return self.get_anki_label(url, 'img')
return ''
@export(u'象形', 3)
@export([u'象形', u'Pictogram'], 3)
def fld_df(self):
url = self._get_field('df')
if url and bcz_download_img:
if url and self.bcz_download_img:
filename = url[url.rindex('/') + 1:]
if self.download(url, filename):
return self.get_anki_label(filename, 'img')
#return self.get_anki_label(url, 'img')
return ''
@export(u'中文释义', 6)
@export(u'DEF', 6)
def fld_mean(self):
return self._get_field('mean_cn')
@export(u'英文例句', 4)
@export(u'EXAMPLE', 4)
def fld_st(self):
return self._get_field('st')
@export(u'例句翻译', 5)
@export('TRANS', 5)
def fld_sttr(self):
return self._get_field('sttr')
@export(u'单词tv', 7)
@export([u'单词tv', u'TV'], 7)
def fld_tv_url(self):
video = self._get_field('tv')
if video:

View File

@ -38,6 +38,7 @@ from ..context import config
from ..libs import MdxBuilder, StardictBuilder
from ..utils import MapDict, wrap_css
from ..libs.bs4 import BeautifulSoup
from ..lang import _cl
try:
import threading as _threading
@ -45,22 +46,26 @@ except ImportError:
import dummy_threading as _threading
def register(label):
"""register the dict service with a label, which will be shown in the dicts list."""
def register(labels):
"""
register the dict service with a labels, which will be shown in the dicts list.
"""
def _deco(cls):
cls.__register_label__ = label
cls.__register_label__ = _cl(labels)
return cls
return _deco
def export(label, index):
"""export dict field function with a label, which will be shown in the fields list."""
def export(labels, index):
"""
export dict field function with a labels, which will be shown in the fields list.
"""
def _with(fld_func):
@wraps(fld_func)
def _deco(cls, *args, **kwargs):
res = fld_func(cls, *args, **kwargs)
return QueryResult(result=res) if not isinstance(res, QueryResult) else res
_deco.__export_attrs__ = (label, index)
_deco.__export_attrs__ = (_cl(labels), index)
return _deco
return _with
@ -123,12 +128,12 @@ def with_styles(**styles):
return _deco
return _with
# BS4资源锁防止程序卡死
# bs4 threading lock, overload protection
BS_LOCKS = [_threading.Lock(), _threading.Lock()]
def parseHtml(html):
'''
使用BS4解析html
use bs4 lib parse HTML, run only 2 BS at the same time
'''
lock = BS_LOCKS[random.randrange(0, len(BS_LOCKS) - 1, 1)]
lock.acquire()
@ -138,7 +143,9 @@ def parseHtml(html):
class Service(object):
'''service base class'''
'''
Dictionary Service Abstract Class
'''
def __init__(self):
self.cache = defaultdict(defaultdict)
@ -207,7 +214,9 @@ class Service(object):
class WebService(Service):
'''web service class'''
"""
Web Dictionary Service
"""
def __init__(self):
super(WebService, self).__init__()
@ -250,7 +259,7 @@ class WebService(Service):
class LocalService(Service):
"""
本地词典
Local Dictionary Service
"""
def __init__(self, dict_path):
@ -275,12 +284,12 @@ class LocalService(Service):
def _filename(self):
return os.path.splitext(os.path.basename(self.dict_path))[0]
# mdx字典实例集
# MdxBuilder instances map
mdx_builders = defaultdict(dict)
class MdxService(LocalService):
"""
Mdx本地词典
MDX Local Dictionary Service
"""
def __init__(self, dict_path):
@ -304,14 +313,14 @@ class MdxService(LocalService):
else:
return self.builder['_title']
@export(u"default", 0)
@export([u'默认', u'Default'], 0)
def fld_whole(self):
html = self.get_html()
js = re.findall(r'<script.*?>.*?</script>', html, re.DOTALL)
return QueryResult(result=html, js=u'\n'.join(js))
def _get_definition_mdx(self):
"""根据关键字得到MDX词典的解释"""
"""according to the word return mdx dictionary page"""
content = self.builder.mdx_lookup(self.word)
str_content = ""
if len(content) > 0:
@ -321,7 +330,7 @@ class MdxService(LocalService):
return str_content
def _get_definition_mdd(self, word):
"""根据关键字得到MDX词典的媒体"""
"""according to the keyword(param word) return the media file contents"""
word = word.replace('/', '\\')
content = self.builder.mdd_lookup(word)
if len(content) > 0:
@ -330,7 +339,7 @@ class MdxService(LocalService):
return []
def get_html(self):
"""取得self.word对应的html页面"""
"""get self.word's html page from MDX"""
if not self.html_cache[self.word]:
html = self._get_definition_mdx()
if html:
@ -338,7 +347,7 @@ class MdxService(LocalService):
return self.html_cache[self.word]
def save_file(self, filepath_in_mdx, savepath):
"""从mmd中取出filepath_in_mdx媒体文件并保存到savepath"""
"""according to filepath_in_mdx to get media file and save it to savepath"""
try:
bytes_list = self._get_definition_mdd(filepath_in_mdx)
if bytes_list:
@ -372,7 +381,7 @@ class StardictService(LocalService):
else:
return self.builder.ifo.bookname.decode('utf-8')
@export(u"default", 0)
@export([u'默认', u'Default'], 0)
def fld_whole(self):
#self.builder.check_build()
try:

View File

@ -5,7 +5,7 @@ from aqt.utils import showInfo, showText
from .base import WebService, export, register, with_styles, parseHtml
@register(u'Bing')
@register([u'必应', u'Bing'])
class Bing(WebService):
def __init__(self):
@ -40,15 +40,15 @@ class Bing(WebService):
def _get_field(self, key, default=u''):
return self.cache_result(key) if self.cached(key) else self._get_content().get(key, default)
@export(u'美式音标', 1)
@export('AME_PHON', 1)
def fld_phonetic_us(self):
return self._get_field('phonitic_us')
@export(u'英式音标', 2)
@export('BRE_PHON', 2)
def fld_phonetic_uk(self):
return self._get_field('phonitic_uk')
@export(u'词语时态', 3)
@export([u'词语时态', u'Participle'], 3)
def fld_participle(self):
return self._get_field('participle')
@ -56,7 +56,7 @@ class Bing(WebService):
def _css(self, val):
return val
@export(u'释义', 4)
@export('DEF', 4)
def fld_definition(self):
val = self._get_field('def')
if val == None or val == '':

View File

@ -1,9 +1,5 @@
#-*- coding:utf-8 -*-
import json
import re
import urllib2
from aqt.utils import showInfo, showText
from .base import WebService, export, register, with_styles
bing_download_mp3 = True
@ -21,13 +17,15 @@ class BingXtk(WebService):
'Accept-Language': 'en-US,zh-CN;q=0.8,zh;q=0.6,en;q=0.4',
'User-Agent': 'WordQuery Addon (Anki)',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'}
word = self.word.replace(' ', '_')
url = u'http://xtk.azurewebsites.net/BingDictService.aspx?Word={}'.format(word.encode('utf-8'))
try:
word = self.word.replace(' ', '_')
request = urllib2.Request(u'http://xtk.azurewebsites.net/BingDictService.aspx?Word=' +\
word.encode('utf-8'), headers=headers)
resp = json.loads(urllib2.urlopen(request).read())
#request = urllib2.Request(u'http://xtk.azurewebsites.net/BingDictService.aspx?Word=' +\
# word.encode('utf-8'), headers=headers)
#resp = json.loads(urllib2.urlopen(request).read())
resp = json.loads(self.get_response(url, headers=headers, timeout=10))
return self.cache_this(resp)
except Exception as e:
except:
return resp
def _get_field(self, key, default=u''):
@ -41,17 +39,17 @@ class BingXtk(WebService):
subfield = default
return subfield
@export(u'美式音标', 1)
@export('AME_PHON', 1)
def fld_phonetic_us(self):
seg = self._get_field('pronunciation')
return self._get_subfield(seg, 'AmE')
@export(u'英式音标', 2)
@export('BRE_PHON', 2)
def fld_phonetic_uk(self):
seg = self._get_field('pronunciation')
return self._get_subfield(seg, 'BrE')
@export(u'美式发音', 3)
@export('AME_PRON', 3)
def fld_mp3_us(self):
seg = audio_url = self._get_field('pronunciation')
audio_url = self._get_subfield(seg, 'AmEmp3')
@ -61,7 +59,7 @@ class BingXtk(WebService):
return self.get_anki_label(filename, 'audio')
return audio_url
@export(u'英式发音', 4)
@export('BRE_PRON', 4)
def fld_mp3_uk(self):
seg = self._get_field('pronunciation')
audio_url = self._get_subfield(seg, 'BrEmp3')
@ -75,15 +73,16 @@ class BingXtk(WebService):
def _css(self, val):
return val
@export(u'释义', 5)
@export('DEF', 5)
def fld_definition(self):
segs = self._get_field('defs')
if isinstance(segs, list) and len(segs) > 0:
val = u'<br>'.join([u'<span class="pos"><b>{0}</b> </span><span class="def"><span>{1}</span></span>'.format(seg['pos'], seg['def']) for seg in segs])
val = u'<br>'.join([u'''<span class="pos"><b>{0}</b></span>
<span class="def">{1}</span>'''.format(seg['pos'], seg['def']) for seg in segs])
return self._css(val)
return ''
@export(u'例句', 6)
@export('EXAMPLE', 6)
# @with_styles(cssfile='_bing2.css', need_wrap_css=True, wrap_class=u'bing')
def fld_samples(self):
max_numbers = 10
@ -92,11 +91,9 @@ class BingXtk(WebService):
for i, seg in enumerate(segs):
sentences = sentences +\
u"""<li><div class="se_li1">
<div class="se_li1">
<div class="sen_en">{0}</div>
<div class="sen_cn">{1}</div>
</div>
</div></li>""".format(seg['eng'], seg['chn'], i + 1)
</div></li>""".format(seg['eng'], seg['chn'])#, i + 1)
if i == 9:
break
return u"""<div class="se_div">

View File

@ -39,6 +39,8 @@ class ServicePool(object):
return self.manager.get_service(unique)
def put(self, service):
if service is None:
return
unique = service.unique
queue = self.pools.get(unique, None)
if queue == None:

View File

@ -309,6 +309,7 @@ class OptionsDialog(QDialog):
self.setLayout(self.main_layout)
self.resize(widget_size.dialog_width,
(i + 1) * widget_size.map_max_height + widget_size.dialog_height_margin)
self.save()
def show_models(self):
edit = QPushButton(anki.lang._("Manage"),
@ -384,13 +385,13 @@ class OptionsDialog(QDialog):
unique = dict_combo_itemdata
service = service_pool.get(unique)
# problem
field_combo.setEditText(u'')
if service and service.support and service.fields:
for each in service.fields:
field_combo.addItem(each)
if each == field_text:
field_combo.setEditText(field_text)
field_combo.setEnabled(service.support)
field_combo.setEnabled(service != None and service.support)
service_pool.put(service)
def radio_btn_checked(self):