diff --git a/src/fastwq/libs/mdict/mdict_query.py b/src/fastwq/libs/mdict/mdict_query.py
index 5db9933..afbb302 100644
--- a/src/fastwq/libs/mdict/mdict_query.py
+++ b/src/fastwq/libs/mdict/mdict_query.py
@@ -1,4 +1,8 @@
# -*- coding: utf-8 -*-
+# version: python 3.5
+
+
+from readmdict import MDX, MDD
from struct import pack, unpack
from io import BytesIO
import re
@@ -6,8 +10,6 @@ import sys
import os
import sqlite3
import json
-from aqt.utils import showInfo, showText, tooltip
-from .readmdict import MDX, MDD
# zlib compression is used for engine version >=2.0
import zlib
@@ -26,11 +28,10 @@ version = '1.1'
class IndexBuilder(object):
- # todo: enable history
-
- def __init__(self, fname, encoding="", passcode=None, force_rebuild=False,
- enable_history=False, sql_index=True, check=False):
+ #todo: enable history
+ def __init__(self, fname, encoding = "", passcode = None, force_rebuild = False, enable_history = False, sql_index = True, check = False):
self._mdx_file = fname
+ self._mdd_file = ""
self._encoding = ''
self._stylesheet = {}
self._title = ''
@@ -38,62 +39,75 @@ class IndexBuilder(object):
self._description = ''
self._sql_index = sql_index
self._check = check
- self._force_rebuild = force_rebuild
_filename, _file_extension = os.path.splitext(fname)
- # assert(_file_extension == '.mdx')
- # assert(os.path.isfile(fname))
+ assert(_file_extension == '.mdx')
+ assert(os.path.isfile(fname))
self._mdx_db = _filename + ".mdx.db"
- self._mdd_db = _filename + ".mdd.db"
- self._mdd_file = _filename + ".mdd"
- self.header_build_flag = False
-
- def get_header(self):
-
- def _():
- self.header_build_flag = True
- mdx = MDX(self._mdx_file, only_header=True)
- self._encoding = mdx.meta['encoding']
- self._stylesheet = json.loads(mdx.meta['stylesheet'])
- self._title = mdx.meta['title']
- self._description = mdx.meta['description']
+ # make index anyway
+ if force_rebuild:
+ self._make_mdx_index(self._mdx_db)
+ if os.path.isfile(_filename + '.mdd'):
+ self._mdd_file = _filename + ".mdd"
+ self._mdd_db = _filename + ".mdd.db"
+ self._make_mdd_index(self._mdd_db)
if os.path.isfile(self._mdx_db):
- # read from META table
- try:
- conn = sqlite3.connect(self._mdx_db)
- #cursor = conn.execute("SELECT * FROM META")
- cursor = conn.execute(
- 'SELECT value FROM META WHERE key IN ("encoding","stylesheet","title","description","version")')
- self._encoding, stylesheet,\
- self._title, self._description, self._version = (
- each[0] for each in cursor)
- self._stylesheet = json.loads(stylesheet)
+ #read from META table
+ conn = sqlite3.connect(self._mdx_db)
+ #cursor = conn.execute("SELECT * FROM META")
+ cursor = conn.execute("SELECT * FROM META WHERE key = \"version\"")
+ #判断有无版本号
+ for cc in cursor:
+ self._version = cc[1]
+ ################# if not version in fo #############
+ if not self._version:
+ print("version info not found")
conn.close()
- if not self._version:
- _()
- except:
- _()
+ self._make_mdx_index(self._mdx_db)
+ print("mdx.db rebuilt!")
+ if os.path.isfile(_filename + '.mdd'):
+ self._mdd_file = _filename + ".mdd"
+ self._mdd_db = _filename + ".mdd.db"
+ self._make_mdd_index(self._mdd_db)
+ print("mdd.db rebuilt!")
+ return None
+ cursor = conn.execute("SELECT * FROM META WHERE key = \"encoding\"")
+ for cc in cursor:
+ self._encoding = cc[1]
+ cursor = conn.execute("SELECT * FROM META WHERE key = \"stylesheet\"")
+ for cc in cursor:
+ self._stylesheet = json.loads(cc[1])
+
+ cursor = conn.execute("SELECT * FROM META WHERE key = \"title\"")
+ for cc in cursor:
+ self._title = cc[1]
+
+ cursor = conn.execute("SELECT * FROM META WHERE key = \"description\"")
+ for cc in cursor:
+ self._description = cc[1]
+
+ #for cc in cursor:
+ # if cc[0] == 'encoding':
+ # self._encoding = cc[1]
+ # continue
+ # if cc[0] == 'stylesheet':
+ # self._stylesheet = json.loads(cc[1])
+ # continue
+ # if cc[0] == 'title':
+ # self._title = cc[1]
+ # continue
+ # if cc[0] == 'title':
+ # self._description = cc[1]
else:
- _()
+ self._make_mdx_index(self._mdx_db)
- def rebuild(self):
- self._make_mdx_index()
- if os.path.isfile(self._mdd_file):
- self._make_mdd_index()
-
- def check_build(self):
- # check if the mdx.db and mdd.db file is available
- if self.header_build_flag or not os.path.isfile(self._mdx_db):
- self._make_mdx_index()
- if os.path.isfile(self._mdd_file) and not os.path.isfile(self._mdd_db):
- self._make_mdd_index()
- self.header_build_flag = False
-
- @property
- def meta(self):
- return {'title': self._title, 'description': self._description,
- 'encoding': self._encoding, 'version': self._version,
- 'stylesheet': self._stylesheet}
+ if os.path.isfile(_filename + ".mdd"):
+ self._mdd_file = _filename + ".mdd"
+ self._mdd_db = _filename + ".mdd.db"
+ if not os.path.isfile(self._mdd_db):
+ self._make_mdd_index(self._mdd_db)
+ pass
+
def _replace_stylesheet(self, txt):
# substitute stylesheet definition
@@ -103,20 +117,19 @@ class IndexBuilder(object):
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].encode('utf-8') + p.rstrip() + \
- style[1].encode('utf-8') + '\r\n'
+ txt_styled = txt_styled + style[0] + p.rstrip() + style[1] + '\r\n'
else:
- txt_styled = txt_styled + \
- style[0].encode('utf-8') + p + style[1].encode('utf-8')
+ txt_styled = txt_styled + style[0] + p + style[1]
return txt_styled
- def _make_mdx_index(self):
- if os.path.exists(self._mdx_db):
- os.remove(self._mdx_db)
- mdx = MDX(self._mdx_file, only_header=False)
- index_list = mdx.get_index(check_block=self._check)
- conn = sqlite3.connect(self._mdx_db)
+ def _make_mdx_index(self, db_name):
+ if os.path.exists(db_name):
+ os.remove(db_name)
+ mdx = MDX(self._mdx_file)
+ self._mdx_db = db_name
+ returned_index = mdx.get_index(check_block = self._check)
+ index_list = returned_index['index_dict_list']
+ conn = sqlite3.connect(db_name)
c = conn.cursor()
c.execute(
''' CREATE TABLE MDX_INDEX
@@ -133,50 +146,65 @@ class IndexBuilder(object):
tuple_list = [
(item['key_text'],
- item['file_pos'],
- item['compressed_size'],
- item['decompressed_size'],
- item['record_block_type'],
- item['record_start'],
- item['record_end'],
- item['offset']
- )
+ item['file_pos'],
+ item['compressed_size'],
+ item['decompressed_size'],
+ item['record_block_type'],
+ item['record_start'],
+ item['record_end'],
+ item['offset']
+ )
for item in index_list
- ]
+ ]
c.executemany('INSERT INTO MDX_INDEX VALUES (?,?,?,?,?,?,?,?)',
tuple_list)
# build the metadata table
+ meta = returned_index['meta']
c.execute(
'''CREATE TABLE META
(key text,
value text
)''')
+
+ #for k,v in meta:
+ # c.execute(
+ # 'INSERT INTO META VALUES (?,?)',
+ # (k, v)
+ # )
+
c.executemany(
- 'INSERT INTO META VALUES (?,?)',
- [('encoding', self.meta['encoding']),
- ('stylesheet', json.dumps(self.meta['stylesheet'])),
- ('title', self.meta['title']),
- ('description', self.meta['description']),
+ 'INSERT INTO META VALUES (?,?)',
+ [('encoding', meta['encoding']),
+ ('stylesheet', meta['stylesheet']),
+ ('title', meta['title']),
+ ('description', meta['description']),
('version', version)
]
- )
-
+ )
+
if self._sql_index:
c.execute(
'''
CREATE INDEX key_index ON MDX_INDEX (key_text)
'''
- )
+ )
conn.commit()
conn.close()
+ #set class member
+ self._encoding = meta['encoding']
+ self._stylesheet = json.loads(meta['stylesheet'])
+ self._title = meta['title']
+ self._description = meta['description']
- def _make_mdd_index(self):
- if os.path.exists(self._mdd_db):
- os.remove(self._mdd_db)
+
+ def _make_mdd_index(self, db_name):
+ if os.path.exists(db_name):
+ os.remove(db_name)
mdd = MDD(self._mdd_file)
- index_list = mdd.get_index(check_block=self._check)
- conn = sqlite3.connect(self._mdd_db)
+ self._mdd_db = db_name
+ index_list = mdd.get_index(check_block = self._check)
+ conn = sqlite3.connect(db_name)
c = conn.cursor()
c.execute(
''' CREATE TABLE MDX_INDEX
@@ -193,16 +221,16 @@ class IndexBuilder(object):
tuple_list = [
(item['key_text'],
- item['file_pos'],
- item['compressed_size'],
- item['decompressed_size'],
- item['record_block_type'],
- item['record_start'],
- item['record_end'],
- item['offset']
- )
+ item['file_pos'],
+ item['compressed_size'],
+ item['decompressed_size'],
+ item['record_block_type'],
+ item['record_start'],
+ item['record_end'],
+ item['offset']
+ )
for item in index_list
- ]
+ ]
c.executemany('INSERT INTO MDX_INDEX VALUES (?,?,?,?,?,?,?,?)',
tuple_list)
if self._sql_index:
@@ -210,13 +238,12 @@ class IndexBuilder(object):
'''
CREATE UNIQUE INDEX key_index ON MDX_INDEX (key_text)
'''
- )
+ )
conn.commit()
conn.close()
- @staticmethod
- def get_data_by_index(fmdx, index):
+ def get_mdx_by_index(self, fmdx, index):
fmdx.seek(index['file_pos'])
record_block_compressed = fmdx.read(index['compressed_size'])
record_block_type = record_block_compressed[:4]
@@ -231,97 +258,111 @@ class IndexBuilder(object):
print("LZO compression is not supported")
# decompress
header = b'\xf0' + pack('>I', index['decompressed_size'])
- _record_block = lzo.decompress(record_block_compressed[
- 8:], initSize=decompressed_size, blockSize=1308672)
- # zlib compression
+ _record_block = lzo.decompress(record_block_compressed[8:], initSize = decompressed_size, blockSize=1308672)
+ # zlib compression
elif record_block_type == 2:
# decompress
_record_block = zlib.decompress(record_block_compressed[8:])
- data = _record_block[index['record_start'] -
- index['offset']:index['record_end'] - index['offset']]
- return data
-
- def get_mdx_by_index(self, fmdx, index):
- data = self.get_data_by_index(fmdx, index)
- record = data.decode(self._encoding, errors='ignore').strip(
- u'\x00').encode('utf-8')
+ record = _record_block[index['record_start'] - index['offset']:index['record_end'] - index['offset']]
+ record = record = record.decode(self._encoding, errors='ignore').strip(u'\x00').encode('utf-8')
if self._stylesheet:
record = self._replace_stylesheet(record)
record = record.decode('utf-8')
return record
def get_mdd_by_index(self, fmdx, index):
- return self.get_data_by_index(fmdx, index)
+ fmdx.seek(index['file_pos'])
+ record_block_compressed = fmdx.read(index['compressed_size'])
+ record_block_type = record_block_compressed[:4]
+ record_block_type = index['record_block_type']
+ decompressed_size = index['decompressed_size']
+ #adler32 = unpack('>I', record_block_compressed[4:8])[0]
+ if record_block_type == 0:
+ _record_block = record_block_compressed[8:]
+ # lzo compression
+ elif record_block_type == 1:
+ if lzo is None:
+ print("LZO compression is not supported")
+ # decompress
+ header = b'\xf0' + pack('>I', index['decompressed_size'])
+ _record_block = lzo.decompress(record_block_compressed[8:], initSize = decompressed_size, blockSize=1308672)
+ # zlib compression
+ elif record_block_type == 2:
+ # decompress
+ _record_block = zlib.decompress(record_block_compressed[8:])
+ data = _record_block[index['record_start'] - index['offset']:index['record_end'] - index['offset']]
+ return data
- @staticmethod
- def lookup_indexes(db, keyword, ignorecase=None):
- indexes = []
- if ignorecase:
- sql = u'SELECT * FROM MDX_INDEX WHERE lower(key_text) = lower("{}")'.format(
- keyword)
- else:
- sql = u'SELECT * FROM MDX_INDEX WHERE key_text = "{}"'.format(
- keyword)
- with sqlite3.connect(db) as conn:
- cursor = conn.execute(sql)
- for result in cursor:
- index = {}
- index['file_pos'] = result[1]
- index['compressed_size'] = result[2]
- index['decompressed_size'] = result[3]
- index['record_block_type'] = result[4]
- index['record_start'] = result[5]
- index['record_end'] = result[6]
- index['offset'] = result[7]
- indexes.append(index)
- return indexes
-
- def mdx_lookup(self, keyword, ignorecase=None):
+ def mdx_lookup(self, keyword):
+ conn = sqlite3.connect(self._mdx_db)
+ cursor = conn.execute("SELECT * FROM MDX_INDEX WHERE key_text = " + "\"" + keyword + "\"")
lookup_result_list = []
- indexes = self.lookup_indexes(self._mdx_db, keyword, ignorecase)
- with open(self._mdx_file, 'rb') as mdx_file:
- for index in indexes:
- lookup_result_list.append(
- self.get_mdx_by_index(mdx_file, index))
+ mdx_file = open(self._mdx_file,'rb')
+ for result in cursor:
+ index = {}
+ index['file_pos'] = result[1]
+ index['compressed_size'] = result[2]
+ index['decompressed_size'] = result[3]
+ index['record_block_type'] = result[4]
+ index['record_start'] = result[5]
+ index['record_end'] = result[6]
+ index['offset'] = result[7]
+ lookup_result_list.append(self.get_mdx_by_index(mdx_file, index))
+ conn.close()
+ mdx_file.close()
+ return lookup_result_list
+
+ def mdd_lookup(self, keyword):
+ conn = sqlite3.connect(self._mdd_db)
+ cursor = conn.execute("SELECT * FROM MDX_INDEX WHERE key_text = " + "\"" + keyword + "\"")
+ lookup_result_list = []
+ mdd_file = open(self._mdd_file,'rb')
+ for result in cursor:
+ index = {}
+ index['file_pos'] = result[1]
+ index['compressed_size'] = result[2]
+ index['decompressed_size'] = result[3]
+ index['record_block_type'] = result[4]
+ index['record_start'] = result[5]
+ index['record_end'] = result[6]
+ index['offset'] = result[7]
+ lookup_result_list.append(self.get_mdd_by_index(mdd_file, index))
+ mdd_file.close()
+ conn.close()
return lookup_result_list
- def mdd_lookup(self, keyword, ignorecase=None):
- lookup_result_list = []
- indexes = self.lookup_indexes(self._mdd_db, keyword, ignorecase)
- with open(self._mdd_file, 'rb') as mdd_file:
- for index in indexes:
- lookup_result_list.append(
- self.get_mdd_by_index(mdd_file, index))
- return lookup_result_list
-
- @staticmethod
- def get_keys(db, query=''):
- if not db:
+ def get_mdd_keys(self, query = ''):
+ if not self._mdd_db:
return []
+ conn = sqlite3.connect(self._mdd_db)
if query:
if '*' in query:
- query = query.replace('*', '%')
+ query = query.replace('*','%')
else:
query = query + '%'
- sql = 'SELECT key_text FROM MDX_INDEX WHERE key_text LIKE \"' + query + '\"'
- else:
- sql = 'SELECT key_text FROM MDX_INDEX'
- with sqlite3.connect(db) as conn:
- cursor = conn.execute(sql)
+ cursor = conn.execute('SELECT key_text FROM MDX_INDEX WHERE key_text LIKE \"' + query + '\"')
keys = [item[0] for item in cursor]
- return keys
+ else:
+ cursor = conn.execute('SELECT key_text FROM MDX_INDEX')
+ keys = [item[0] for item in cursor]
+ conn.close()
+ return keys
- def get_mdd_keys(self, query=''):
- try:
- return self.get_keys(self._mdd_db, query)
- except:
- return []
+ def get_mdx_keys(self, query = ''):
+ conn = sqlite3.connect(self._mdx_db)
+ if query:
+ if '*' in query:
+ query = query.replace('*','%')
+ else:
+ query = query + '%'
+ cursor = conn.execute('SELECT key_text FROM MDX_INDEX WHERE key_text LIKE \"' + query + '\"')
+ keys = [item[0] for item in cursor]
+ else:
+ cursor = conn.execute('SELECT key_text FROM MDX_INDEX')
+ keys = [item[0] for item in cursor]
+ conn.close()
+ return keys
- def get_mdx_keys(self, query=''):
- try:
- return self.get_keys(self._mdx_db, query)
- except:
- return []
# mdx_builder = IndexBuilder("oald.mdx")
diff --git a/src/fastwq/libs/mdict/readmdict.py b/src/fastwq/libs/mdict/readmdict.py
index 2d79358..65b39b0 100644
--- a/src/fastwq/libs/mdict/readmdict.py
+++ b/src/fastwq/libs/mdict/readmdict.py
@@ -23,9 +23,8 @@ import re
import sys
import json
-from .ripemd128 import ripemd128
-from .pureSalsa20 import Salsa20
-from aqt.utils import showInfo, showText, tooltip
+from ripemd128 import ripemd128
+from pureSalsa20 import Salsa20
# zlib compression is used for engine version >=2.0
import zlib
@@ -93,15 +92,12 @@ class MDict(object):
Base class which reads in header and key block.
It has no public methods and serves only as code sharing base class.
"""
-
- def __init__(self, fname, encoding='', passcode=None, only_header=False):
+ def __init__(self, fname, encoding='', passcode=None):
self._fname = fname
self._encoding = encoding.upper()
self._passcode = passcode
self.header = self._read_header()
- if only_header:
- return
try:
self._key_list = self._read_keys()
except:
@@ -139,8 +135,7 @@ class MDict(object):
assert(key_block_info_compressed[:4] == b'\x02\x00\x00\x00')
# decrypt if needed
if self._encrypt & 0x02:
- key_block_info_compressed = _mdx_decrypt(
- key_block_info_compressed)
+ key_block_info_compressed = _mdx_decrypt(key_block_info_compressed)
# decompress
key_block_info = zlib.decompress(key_block_info_compressed[8:])
# adler checksum
@@ -164,12 +159,10 @@ class MDict(object):
while i < len(key_block_info):
# number of entries in current key block
- num_entries += unpack(self._number_format,
- key_block_info[i:i + self._number_width])[0]
+ num_entries += unpack(self._number_format, key_block_info[i:i + self._number_width])[0]
i += self._number_width
# text head size
- text_head_size = unpack(byte_format, key_block_info[
- i:i + byte_width])[0]
+ text_head_size = unpack(byte_format, key_block_info[i:i + byte_width])[0]
i += byte_width
# text head
if self._encoding != 'UTF-16':
@@ -177,8 +170,7 @@ class MDict(object):
else:
i += (text_head_size + text_term) * 2
# text tail size
- text_tail_size = unpack(byte_format, key_block_info[
- i:i + byte_width])[0]
+ text_tail_size = unpack(byte_format, key_block_info[i:i + byte_width])[0]
i += byte_width
# text tail
if self._encoding != 'UTF-16':
@@ -186,15 +178,12 @@ class MDict(object):
else:
i += (text_tail_size + text_term) * 2
# key block compressed size
- key_block_compressed_size = unpack(self._number_format, key_block_info[
- i:i + self._number_width])[0]
+ key_block_compressed_size = unpack(self._number_format, key_block_info[i:i + self._number_width])[0]
i += self._number_width
# key block decompressed size
- key_block_decompressed_size = unpack(self._number_format, key_block_info[
- i:i + self._number_width])[0]
+ key_block_decompressed_size = unpack(self._number_format, key_block_info[i:i + self._number_width])[0]
i += self._number_width
- key_block_info_list += [(key_block_compressed_size,
- key_block_decompressed_size)]
+ key_block_info_list += [(key_block_compressed_size, key_block_decompressed_size)]
assert(num_entries == self._num_entries)
@@ -209,8 +198,7 @@ class MDict(object):
# 4 bytes : compression type
key_block_type = key_block_compressed[start:start + 4]
# 4 bytes : adler checksum of decompressed key block
- adler32 = unpack('>I', key_block_compressed[
- start + 4:start + 8])[0]
+ adler32 = unpack('>I', key_block_compressed[start + 4:start + 8])[0]
if key_block_type == b'\x00\x00\x00\x00':
key_block = key_block_compressed[start + 8:end]
elif key_block_type == b'\x01\x00\x00\x00':
@@ -219,12 +207,10 @@ class MDict(object):
break
# decompress key block
header = b'\xf0' + pack('>I', decompressed_size)
- key_block = lzo.decompress(key_block_compressed[
- start + 8:end], initSize=decompressed_size, blockSize=1308672)
+ key_block = lzo.decompress(key_block_compressed[start + 8:end], initSize = decompressed_size, blockSize=1308672)
elif key_block_type == b'\x02\x00\x00\x00':
# decompress key block
- key_block = zlib.decompress(
- key_block_compressed[start + 8:end])
+ key_block = zlib.decompress(key_block_compressed[start + 8:end])
# extract one single key block into a key list
key_list += self._split_key_block(key_block)
# notice that adler32 returns signed value
@@ -237,11 +223,9 @@ class MDict(object):
key_list = []
key_start_index = 0
while key_start_index < len(key_block):
- temp = key_block[
- key_start_index:key_start_index + self._number_width]
+ temp = key_block[key_start_index:key_start_index + self._number_width]
# the corresponding record's offset in record block
- key_id = unpack(self._number_format, key_block[
- key_start_index:key_start_index + self._number_width])[0]
+ key_id = unpack(self._number_format, key_block[key_start_index:key_start_index + self._number_width])[0]
# key text ends with '\x00'
if self._encoding == 'UTF-16':
delimiter = b'\x00\x00'
@@ -261,12 +245,6 @@ class MDict(object):
key_list += [(key_id, key_text)]
return key_list
- @property
- def meta(self):
- return {'title': self._title, 'description': self._description,
- 'encoding': self._encoding, 'version': self._version,
- 'stylesheet': json.dumps(self._stylesheet)}
-
def _read_header(self):
f = open(self._fname, 'rb')
# number of bytes of header text
@@ -349,8 +327,7 @@ class MDict(object):
if self._encrypt & 1:
if self._passcode is None:
- raise RuntimeError(
- 'user identification is needed to read encrypted file')
+ raise RuntimeError('user identification is needed to read encrypted file')
regcode, userid = self._passcode
if isinstance(userid, unicode):
userid = userid.encode('utf8')
@@ -388,8 +365,7 @@ class MDict(object):
# read key block
key_block_compressed = f.read(key_block_size)
# extract key block
- key_list = self._decode_key_block(
- key_block_compressed, key_block_info_list)
+ key_list = self._decode_key_block(key_block_compressed, key_block_info_list)
self._record_block_offset = f.tell()
f.close()
@@ -434,8 +410,7 @@ class MDict(object):
# read key block
key_block_compressed = f.read(key_block_size)
# extract key block
- key_list = self._decode_key_block(
- key_block_compressed, key_block_info_list)
+ key_list = self._decode_key_block(key_block_compressed, key_block_info_list)
self._record_block_offset = f.tell()
f.close()
@@ -453,7 +428,6 @@ class MDD(MDict):
>>> for filename,content in mdd.items():
... print filename, content[:10]
"""
-
def __init__(self, fname, passcode=None):
MDict.__init__(self, fname, encoding='UTF-16', passcode=passcode)
@@ -500,8 +474,7 @@ class MDD(MDict):
break
# decompress
header = b'\xf0' + pack('>I', decompressed_size)
- record_block = lzo.decompress(record_block_compressed[
- start + 8:end], initSize=decompressed_size, blockSize=1308672)
+ record_block = lzo.decompress(record_block_compressed[start + 8:end], initSize = decompressed_size, blockSize=1308672)
elif record_block_type == b'\x02\x00\x00\x00':
# decompress
record_block = zlib.decompress(record_block_compressed[8:])
@@ -530,16 +503,16 @@ class MDD(MDict):
f.close()
- # 获取 mdx 文件的索引列表,格式为
- # key_text(关键词,可以由后面的 keylist 得到)
- # file_pos(record_block开始的位置)
- # compressed_size(record_block压缩前的大小)
- # decompressed_size(解压后的大小)
- # record_block_type(record_block 的压缩类型)
- # record_start (以下三个为从 record_block 中提取某一调记录需要的参数,可以直接保存)
- # record_end
- # offset
- def get_index(self, check_block=True):
+ ### 获取 mdx 文件的索引列表,格式为
+ ### key_text(关键词,可以由后面的 keylist 得到)
+ ### file_pos(record_block开始的位置)
+ ### compressed_size(record_block压缩前的大小)
+ ### decompressed_size(解压后的大小)
+ ### record_block_type(record_block 的压缩类型)
+ ### record_start (以下三个为从 record_block 中提取某一调记录需要的参数,可以直接保存)
+ ### record_end
+ ### offset
+ def get_index(self, check_block = True):
f = open(self._fname, 'rb')
index_dict_list = []
f.seek(self._record_block_offset)
@@ -584,8 +557,7 @@ class MDD(MDict):
# decompress
header = b'\xf0' + pack('>I', decompressed_size)
if check_block:
- record_block = lzo.decompress(record_block_compressed[
- start + 8:end], initSize=decompressed_size, blockSize=1308672)
+ record_block = lzo.decompress(record_block_compressed[start + 8:end], initSize = decompressed_size, blockSize=1308672)
elif record_block_type == b'\x02\x00\x00\x00':
# decompress
_type = 2
@@ -598,7 +570,7 @@ class MDD(MDict):
assert(len(record_block) == decompressed_size)
# split record block according to the offset info from key block
while i < len(self._key_list):
- # 用来保存索引信息的空字典
+ ### 用来保存索引信息的空字典
index_dict = {}
index_dict['file_pos'] = current_pos
index_dict['compressed_size'] = compressed_size
@@ -606,11 +578,10 @@ class MDD(MDict):
index_dict['record_block_type'] = _type
record_start, key_text = self._key_list[i]
index_dict['record_start'] = record_start
- index_dict['key_text'] = key_text.decode(
- "utf-8", errors='ignore')
+ index_dict['key_text'] = key_text.decode("utf-8")
index_dict['offset'] = offset
# reach the end of current record block
- if record_start - offset >= decompressed_size:
+ if record_start - offset >= decompressed_size:
break
# record end index
if i < len(self._key_list) - 1:
@@ -620,11 +591,10 @@ class MDD(MDict):
index_dict['record_end'] = record_end
i += 1
if check_block:
- data = record_block[
- record_start - offset:record_end - offset]
+ data = record_block[record_start - offset:record_end - offset]
index_dict_list.append(index_dict)
- # yield key_text, data
- offset += decompressed_size
+ #yield key_text, data
+ offset += decompressed_size
size_counter += compressed_size
assert(size_counter == record_block_size)
f.close()
@@ -640,9 +610,8 @@ class MDX(MDict):
>>> for key,value in mdx.items():
... print key, value[:10]
"""
-
- def __init__(self, fname, encoding='', substyle=False, passcode=None, only_header=False):
- MDict.__init__(self, fname, encoding, passcode, only_header)
+ def __init__(self, fname, encoding='', substyle=False, passcode=None):
+ MDict.__init__(self, fname, encoding, passcode)
self._substyle = substyle
def items(self):
@@ -658,8 +627,7 @@ class MDX(MDict):
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
@@ -688,20 +656,20 @@ class MDX(MDict):
offset = 0
i = 0
size_counter = 0
- # 最后的索引表的格式为
- # key_text(关键词,可以由后面的 keylist 得到)
- # file_pos(record_block开始的位置)
- # compressed_size(record_block压缩前的大小)
- # decompressed_size(解压后的大小)
- # record_block_type(record_block 的压缩类型)
- # record_start (以下三个为从 record_block 中提取某一调记录需要的参数,可以直接保存)
- # record_end
- # offset
+ ###最后的索引表的格式为
+ ### key_text(关键词,可以由后面的 keylist 得到)
+ ### file_pos(record_block开始的位置)
+ ### compressed_size(record_block压缩前的大小)
+ ### decompressed_size(解压后的大小)
+ ### record_block_type(record_block 的压缩类型)
+ ### record_start (以下三个为从 record_block 中提取某一调记录需要的参数,可以直接保存)
+ ### record_end
+ ### offset
for compressed_size, decompressed_size in record_block_info_list:
record_block_compressed = f.read(compressed_size)
- # 要得到 record_block_compressed 需要得到 compressed_size (这个可以直接记录)
- # 另外还需要记录当前 f 对象的位置
- # 使用 f.tell() 命令/ 在建立索引是需要 f.seek()
+ ###### 要得到 record_block_compressed 需要得到 compressed_size (这个可以直接记录)
+ ###### 另外还需要记录当前 f 对象的位置
+ ###### 使用 f.tell() 命令/ 在建立索引是需要 f.seek()
# 4 bytes indicates block compression type
record_block_type = record_block_compressed[:4]
# 4 bytes adler checksum of uncompressed content
@@ -716,16 +684,15 @@ class MDX(MDict):
break
# decompress
header = b'\xf0' + pack('>I', decompressed_size)
- record_block = lzo.decompress(record_block_compressed[
- 8:], initSize=decompressed_size, blockSize=1308672)
+ record_block = lzo.decompress(record_block_compressed[8:], initSize = decompressed_size, blockSize=1308672)
# zlib compression
elif record_block_type == b'\x02\x00\x00\x00':
# decompress
record_block = zlib.decompress(record_block_compressed[8:])
- # 这里比较重要的是先要得到 record_block, 而 record_block 是解压得到的,其中一共有三种解压方法
- # 需要的信息有 record_block_compressed, decompress_size,
- # record_block_type
- # 另外还需要校验信息 adler32
+ ###### 这里比较重要的是先要得到 record_block, 而 record_block 是解压得到的,其中一共有三种解压方法
+ ###### 需要的信息有 record_block_compressed, decompress_size,
+ ###### record_block_type
+ ###### 另外还需要校验信息 adler32
# notice that adler32 return signed value
assert(adler32 == zlib.adler32(record_block) & 0xffffffff)
@@ -742,15 +709,13 @@ class MDX(MDict):
else:
record_end = len(record_block) + offset
i += 1
- # 需要得到 record_block , record_start, record_end,
- # offset
- record = record_block[
- record_start - offset:record_end - offset]
+ #############需要得到 record_block , record_start, record_end,
+ #############offset
+ record = record_block[record_start - offset:record_end - offset]
# convert to utf-8
- record = record.decode(self._encoding, errors='ignore').strip(
- u'\x00').encode('utf-8')
+ record = record.decode(self._encoding, errors='ignore').strip(u'\x00').encode('utf-8')
# substitute styles
- # 是否替换样式表
+ #############是否替换样式表
if self._substyle and self._stylesheet:
record = self._substitute_stylesheet(record)
@@ -761,19 +726,19 @@ class MDX(MDict):
f.close()
- # 获取 mdx 文件的索引列表,格式为
- # key_text(关键词,可以由后面的 keylist 得到)
- # file_pos(record_block开始的位置)
- # compressed_size(record_block压缩前的大小)
- # decompressed_size(解压后的大小)
- # record_block_type(record_block 的压缩类型)
- # record_start (以下三个为从 record_block 中提取某一调记录需要的参数,可以直接保存)
- # record_end
- # offset
- # 所需 metadata
- ###
- def get_index(self, check_block=True):
- # 索引列表
+ ### 获取 mdx 文件的索引列表,格式为
+ ### key_text(关键词,可以由后面的 keylist 得到)
+ ### file_pos(record_block开始的位置)
+ ### compressed_size(record_block压缩前的大小)
+ ### decompressed_size(解压后的大小)
+ ### record_block_type(record_block 的压缩类型)
+ ### record_start (以下三个为从 record_block 中提取某一调记录需要的参数,可以直接保存)
+ ### record_end
+ ### offset
+ ### 所需 metadata
+ ###
+ def get_index(self, check_block = True):
+ ### 索引列表
index_dict_list = []
f = open(self._fname, 'rb')
f.seek(self._record_block_offset)
@@ -798,21 +763,21 @@ class MDX(MDict):
offset = 0
i = 0
size_counter = 0
- # 最后的索引表的格式为
- # key_text(关键词,可以由后面的 keylist 得到)
- # file_pos(record_block开始的位置)
- # compressed_size(record_block压缩前的大小)
- # decompressed_size(解压后的大小)
- # record_block_type(record_block 的压缩类型)
- # record_start (以下三个为从 record_block 中提取某一调记录需要的参数,可以直接保存)
- # record_end
- # offset
+ ###最后的索引表的格式为
+ ### key_text(关键词,可以由后面的 keylist 得到)
+ ### file_pos(record_block开始的位置)
+ ### compressed_size(record_block压缩前的大小)
+ ### decompressed_size(解压后的大小)
+ ### record_block_type(record_block 的压缩类型)
+ ### record_start (以下三个为从 record_block 中提取某一调记录需要的参数,可以直接保存)
+ ### record_end
+ ### offset
for compressed_size, decompressed_size in record_block_info_list:
current_pos = f.tell()
record_block_compressed = f.read(compressed_size)
- # 要得到 record_block_compressed 需要得到 compressed_size (这个可以直接记录)
- # 另外还需要记录当前 f 对象的位置
- # 使用 f.tell() 命令/ 在建立索引是需要 f.seek()
+ ###### 要得到 record_block_compressed 需要得到 compressed_size (这个可以直接记录)
+ ###### 另外还需要记录当前 f 对象的位置
+ ###### 使用 f.tell() 命令/ 在建立索引是需要 f.seek()
# 4 bytes indicates block compression type
record_block_type = record_block_compressed[:4]
# 4 bytes adler checksum of uncompressed content
@@ -830,25 +795,24 @@ class MDX(MDict):
# decompress
header = b'\xf0' + pack('>I', decompressed_size)
if check_block:
- record_block = lzo.decompress(record_block_compressed[
- 8:], initSize=decompressed_size, blockSize=1308672)
+ record_block = lzo.decompress(record_block_compressed[8:], initSize = decompressed_size, blockSize=1308672)
# zlib compression
elif record_block_type == b'\x02\x00\x00\x00':
# decompress
_type = 2
if check_block:
record_block = zlib.decompress(record_block_compressed[8:])
- # 这里比较重要的是先要得到 record_block, 而 record_block 是解压得到的,其中一共有三种解压方法
- # 需要的信息有 record_block_compressed, decompress_size,
- # record_block_type
- # 另外还需要校验信息 adler32
+ ###### 这里比较重要的是先要得到 record_block, 而 record_block 是解压得到的,其中一共有三种解压方法
+ ###### 需要的信息有 record_block_compressed, decompress_size,
+ ###### record_block_type
+ ###### 另外还需要校验信息 adler32
# notice that adler32 return signed value
if check_block:
assert(adler32 == zlib.adler32(record_block) & 0xffffffff)
assert(len(record_block) == decompressed_size)
# split record block according to the offset info from key block
while i < len(self._key_list):
- # 用来保存索引信息的空字典
+ ### 用来保存索引信息的空字典
index_dict = {}
index_dict['file_pos'] = current_pos
index_dict['compressed_size'] = compressed_size
@@ -856,11 +820,10 @@ class MDX(MDict):
index_dict['record_block_type'] = _type
record_start, key_text = self._key_list[i]
index_dict['record_start'] = record_start
- index_dict['key_text'] = key_text.decode(
- 'utf-8', errors='ignore')
+ index_dict['key_text'] = key_text.decode('utf-8')
index_dict['offset'] = offset
# reach the end of current record block
- if record_start - offset >= decompressed_size:
+ if record_start - offset >= decompressed_size:
break
# record end index
if i < len(self._key_list) - 1:
@@ -869,27 +832,31 @@ class MDX(MDict):
record_end = decompressed_size + offset
index_dict['record_end'] = record_end
i += 1
- # 需要得到 record_block , record_start, record_end,
- # offset
+ #############需要得到 record_block , record_start, record_end,
+ #############offset
if check_block:
- record = record_block[
- record_start - offset:record_end - offset]
+ record = record_block[record_start - offset:record_end - offset]
# convert to utf-8
- record = record.decode(self._encoding, errors='ignore').strip(
- u'\x00').encode('utf-8')
+ record = record.decode(self._encoding, errors='ignore').strip(u'\x00').encode('utf-8')
# substitute styles
- # 是否替换样式表
+ #############是否替换样式表
if self._substyle and self._stylesheet:
record = self._substitute_stylesheet(record)
index_dict_list.append(index_dict)
- offset += decompressed_size
+ offset += decompressed_size
size_counter += compressed_size
- # todo: 注意!!!
- #assert(size_counter == record_block_size)
+ #todo: 注意!!!
+ #assert(size_counter == record_block_size)
f.close
- return index_dict_list
+ #这里比 mdd 部分稍有不同,应该还需要传递编码以及样式表信息
+ meta = {}
+ meta['encoding'] = self._encoding
+ meta['stylesheet'] = json.dumps(self._stylesheet)
+ meta['title'] = self._title
+ meta['description'] = self._description
+ return {"index_dict_list":index_dict_list, 'meta':meta}
if __name__ == '__main__':
import sys
import os
@@ -905,8 +872,7 @@ if __name__ == '__main__':
try:
regcode = codecs.decode(regcode, 'hex')
except:
- raise argparse.ArgumentTypeError(
- "regcode must be a 32 bytes hexadecimal string")
+ raise argparse.ArgumentTypeError("regcode must be a 32 bytes hexadecimal string")
return regcode, userid
parser = argparse.ArgumentParser()
@@ -987,8 +953,7 @@ if __name__ == '__main__':
sf.close()
# write out optional data files
if mdd:
- datafolder = os.path.join(
- os.path.dirname(args.filename), args.datafolder)
+ datafolder = os.path.join(os.path.dirname(args.filename), args.datafolder)
if not os.path.exists(datafolder):
os.makedirs(datafolder)
for key, value in mdd.items():
diff --git a/src/fastwq/query.py b/src/fastwq/query.py
index d71203f..dd60e74 100644
--- a/src/fastwq/query.py
+++ b/src/fastwq/query.py
@@ -73,6 +73,7 @@ class QueryThread(QThread):
super(QueryThread, self).__init__()
self.index = 0
self.exit = False
+ self.finished = False
self.manager = manager
self.note_flush.connect(manager.handle_flush)
@@ -96,6 +97,8 @@ class QueryThread(QThread):
if self.manager:
self.manager.queue.task_done()
+
+ self.finished = True
class QueryWorkerManager(object):
@@ -122,11 +125,15 @@ class QueryWorkerManager(object):
def start(self):
self.total = self.queue.qsize()
self.progress.start(self.total, min=0)
- for x in range(0, min(config.thread_number, self.total)):
- self.get_worker()
-
- for worker in self.workers:
- worker.start()
+ if self.total > 1:
+ for x in range(0, min(config.thread_number, self.total)):
+ self.get_worker()
+
+ for worker in self.workers:
+ worker.start()
+ else:
+ worker = self.get_worker()
+ worker.run()
def update(self, note, results, success_num):
self.mutex.lock()
@@ -137,11 +144,15 @@ class QueryWorkerManager(object):
val = update_note_fields(note, results)
self.fields += val
self.mutex.unlock()
- return val > 0
+ if self.total > 1:
+ return val > 0
+ else:
+ self.handle_flush(note)
+ return False
def join(self):
for worker in self.workers:
- while not worker.isFinished():
+ while not worker.finished:
if self.progress.abort():
worker.exit = True
break
@@ -323,22 +334,25 @@ def query_all_flds(note):
dict_unique = each.get('dict_unique', '').strip()
if dict_name and dict_name not in _sl('NOT_DICT_FIELD') and dict_field:
s = services.get(dict_unique, None)
- if s == None:
- services[dict_unique] = service_pool.get(dict_unique)#service_manager.get_service(dict_unique)
- tasks.append({'k': dict_unique, 'w': word, 'f': dict_field, 'i': i})
+ if s is None:
+ s = service_pool.get(dict_unique)
+ if s.support:
+ services[dict_unique] = s
+ if s and s.support:
+ tasks.append({'k': dict_unique, 'w': word, 'f': dict_field, 'i': i})
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:
- 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:
+ result.update({task['i']: qr})
+ success_num += 1
+ #except:
+ # showInfo(_("NO_QUERY_WORD"))
+ # pass
for service in services.values():
service_pool.put(service)
diff --git a/src/fastwq/service/LDOCE6.py b/src/fastwq/service/LDOCE6.py
index aa39bd9..c6302bd 100644
--- a/src/fastwq/service/LDOCE6.py
+++ b/src/fastwq/service/LDOCE6.py
@@ -1,17 +1,22 @@
#-*- coding:utf-8 -*-
import re
+from .base import MdxService, export, register, with_styles, parseHtml
-from aqt.utils import showInfo, showText
-from .base import MdxService, export, register, with_styles
+PATH = u'D:\\mdx_server\\mdx\\LDOCE6.mdx'
-path = u'D:\\mdx_server\\mdx\\LDOCE6.mdx'
+VOICE_PATTERN = r'
'
+MAPPINGS = [
+ ['br', [re.compile(VOICE_PATTERN % r'r')]],
+ ['us', [re.compile(VOICE_PATTERN % r'b')]]
+]
+LANG_TO_REGEXPS = {lang: regexps for lang, regexps in MAPPINGS}
@register(u'本地词典-LDOCE6')
class Ldoce6(MdxService):
def __init__(self):
- super(Ldoce6, self).__init__(path)
+ super(Ldoce6, self).__init__(PATH)
@property
def unique(self):
@@ -27,58 +32,74 @@ class Ldoce6(MdxService):
m = re.search(r'(.*?)', html)
if m:
return m.groups()[0]
+ return ''
- @export(u'Bre单词发音', 2)
+ def _fld_voice(self, html, voice):
+ """获取发音字段"""
+ from hashlib import sha1
+ for regexp in LANG_TO_REGEXPS[voice]:
+ match = regexp.search(html)
+ if match:
+ val = '/' + match.group(1)
+ hex_digest = sha1(
+ val.encode('utf-8') if isinstance(val, unicode)
+ else val
+ ).hexdigest().lower()
+
+ assert len(hex_digest) == 40, "unexpected output from hash library"
+ name = '.'.join([
+ '-'.join([
+ 'mdx', self.unique.lower(), hex_digest[:8], hex_digest[8:16],
+ hex_digest[16:24], hex_digest[24:32], hex_digest[32:],
+ ]),
+ 'mp3',
+ ])
+ name = self.save_file(val, name)
+ if name:
+ return self.get_anki_label(name, 'audio')
+ return ''
+
+ @export(u'英式发音', 2)
def fld_voicebre(self):
- html = self.get_html()
- m = re.search(r'(.*?)', html)
- if m:
- return m.groups()[0]
- return ''
+ return self._fld_voice(self.get_html(), 'br')
- @export(u'Ame单词发音', 3)
+ @export(u'美式发音', 3)
def fld_voiceame(self):
- html = self.get_html()
- m = re.search(r'(.*?)', html)
- if m:
- return m.groups()[0]
- return ''
+ return self._fld_voice(self.get_html(), 'us')
- @export(u'sentence', 4)
+ @export(u'例句', 4)
def fld_sentence(self):
- html = self.get_html()
- m = re.search(r'(.*?)', html)
+ m = re.findall(r'\s*.*<\/span>', self.get_html())
if m:
- return re.sub('', '', m.groups()[0])
- return ''
-
- @export(u'def', 5)
- def fld_definate(self):
- html = self.get_html()
- m = re.search(r'(.*?)', html)
- if m:
- return m.groups()[0]
- return ''
-
- @export(u'random_sentence', 6)
- def fld_random_sentence(self):
- html = self.get_html()
- m = re.findall(r'(.*?)', html)
- if m:
- number = len(m)
- index = random.randrange(0, number - 1, 1)
- return re.sub('', '', m[index])
- return ''
-
- @export(u'all sentence', 7)
- def fld_allsentence(self):
- html = self.get_html()
- m = re.findall(
- r'(.+?.+?)', html)
- if m:
- items = 0
+ soup = parseHtml(m[0])
+ el_list = soup.findAll('span', {'class':'example'})
+ if el_list:
+ maps = [u''.join(str(content).decode('utf-8') for content in element.contents)
+ for element in el_list]
my_str = ''
- for items in range(len(m)):
- my_str = my_str + m[items]
- return my_str
+ for i_str in maps:
+ i_str = re.sub(r']+?href=\"sound\:.*\.mp3\".*', '', i_str)
+ i_str = i_str.replace(' ', '')
+ my_str = my_str + '' + i_str + ''
+ return self._css(my_str)
return ''
+
+ @export(u'释义', 5)
+ def fld_definate(self):
+ m = m = re.findall(r'\s*.*<\/span>', self.get_html())
+ if m:
+ soup = parseHtml(m[0])
+ el_list = soup.findAll('span', {'class':'def'})
+ if el_list:
+ maps = [u''.join(str(content).decode('utf-8') for content in element.contents)
+ for element in el_list]
+ my_str = ''
+ for i_str in maps:
+ my_str = my_str + '' + i_str + ''
+ return self._css(my_str)
+ return ''
+
+ @with_styles(cssfile='_ldoce6.css')
+ def _css(self, val):
+ return val
+
\ No newline at end of file
diff --git a/src/fastwq/service/base.py b/src/fastwq/service/base.py
index 0715e71..589b515 100644
--- a/src/fastwq/service/base.py
+++ b/src/fastwq/service/base.py
@@ -1,8 +1,8 @@
#-*- coding:utf-8 -*-
#
-# Copyright © 2016–2017 Liang Feng
+# Copyright © 2016–2017 ST.Huang
#
-# Support: Report an issue at https://github.com/finalion/WordQuery/issues
+# Support: Report an issue at https://github.com/sth2018/FastWordQuery/issues
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -29,17 +29,20 @@ import sqlite3
import urllib
import urllib2
import zlib
+import random
from collections import defaultdict
from functools import wraps
import cookielib
-from aqt import mw
-from aqt.qt import QFileDialog
-from aqt.utils import showInfo, showText
from ..context import config
-from ..lang import _
from ..libs import MdxBuilder, StardictBuilder
from ..utils import MapDict, wrap_css
+from ..libs.bs4 import BeautifulSoup
+
+try:
+ import threading as _threading
+except ImportError:
+ import dummy_threading as _threading
def register(label):
@@ -120,17 +123,45 @@ def with_styles(**styles):
return _deco
return _with
+# BS4资源锁,防止程序卡死
+BS_LOCKS = [_threading.Lock(), _threading.Lock()]
+
+def parseHtml(html):
+ '''
+ 使用BS4解析html
+ '''
+ lock = BS_LOCKS[random.randrange(0, len(BS_LOCKS) - 1, 1)]
+ lock.acquire()
+ soup = BeautifulSoup(html, 'html.parser')
+ lock.release()
+ return soup
+
class Service(object):
'''service base class'''
def __init__(self):
+ self.cache = defaultdict(defaultdict)
self._exporters = self.get_exporters()
self._fields, self._actions = zip(*self._exporters) \
if self._exporters else (None, None)
# query interval: default 500ms
self.query_interval = 0.5
+ def cache_this(self, result):
+ self.cache[self.word].update(result)
+ return result
+
+ def cached(self, key):
+ return (self.word in self.cache) and self.cache[self.word].has_key(key)
+
+ def cache_result(self, key):
+ return self.cache[self.word].get(key, u'')
+
+ @property
+ def support(self):
+ return True
+
@property
def fields(self):
return self._fields
@@ -158,9 +189,9 @@ class Service(object):
self.word = word
# if the service instance is LocalService,
# then have to build then index.
- if isinstance(self, LocalService):
- if isinstance(self, MdxService) or isinstance(self, StardictService):
- self.builder.check_build()
+ #if isinstance(self, LocalService):
+ # if isinstance(self, MdxService) or isinstance(self, StardictService):
+ # self.builder.check_build()
for each in self.exporters:
if action_label == each[0]:
@@ -180,21 +211,10 @@ class WebService(Service):
def __init__(self):
super(WebService, self).__init__()
- self.cache = defaultdict(defaultdict)
self._cookie = cookielib.CookieJar()
self._opener = urllib2.build_opener(
urllib2.HTTPCookieProcessor(self._cookie))
- self.query_interval = 1
-
- def cache_this(self, result):
- self.cache[self.word].update(result)
- return result
-
- def cached(self, key):
- return (self.word in self.cache) and self.cache[self.word].has_key(key)
-
- def cache_result(self, key):
- return self.cache[self.word].get(key, u'')
+ self.query_interval = 1.0
@property
def title(self):
@@ -229,6 +249,9 @@ class WebService(Service):
class LocalService(Service):
+ """
+ 本地词典
+ """
def __init__(self, dict_path):
super(LocalService, self).__init__()
@@ -236,6 +259,10 @@ class LocalService(Service):
self.builder = None
self.missed_css = set()
+ @property
+ def support(self):
+ return os.path.isfile(self.dict_path)
+
@property
def unique(self):
return self.dict_path
@@ -248,28 +275,34 @@ class LocalService(Service):
def _filename(self):
return os.path.splitext(os.path.basename(self.dict_path))[0]
+# mdx字典实例集
+mdx_builders = defaultdict(dict)
class MdxService(LocalService):
+ """
+ Mdx本地词典
+ """
def __init__(self, dict_path):
super(MdxService, self).__init__(dict_path)
- self.media_cache = defaultdict(set)
- self.cache = defaultdict(str)
+ self.html_cache = defaultdict(str)
self.query_interval = 0.01
self.styles = []
- self.builder = MdxBuilder(dict_path)
- self.builder.get_header()
+ if self.support:
+ if not mdx_builders.has_key(dict_path) or not mdx_builders[dict_path]:
+ mdx_builders[dict_path] = MdxBuilder(dict_path)
+ self.builder = mdx_builders[dict_path]
- @staticmethod
- def support(dict_path):
- return os.path.isfile(dict_path) and dict_path.lower().endswith('.mdx')
+ @property
+ def support(self):
+ return os.path.isfile(self.dict_path) and self.dict_path.lower().endswith('.mdx')
@property
def title(self):
if config.use_filename or not self.builder._title or self.builder._title.startswith('Title'):
return self._filename
else:
- return self.builder.meta['title']
+ return self.builder['_title']
@export(u"default", 0)
def fld_whole(self):
@@ -277,94 +310,46 @@ class MdxService(LocalService):
js = re.findall(r'.*?', html, re.DOTALL)
return QueryResult(result=html, js=u'\n'.join(js))
+ def _get_definition_mdx(self):
+ """根据关键字得到MDX词典的解释"""
+ content = self.builder.mdx_lookup(self.word)
+ str_content = ""
+ if len(content) > 0:
+ for c in content:
+ str_content += c.replace("\r\n","").replace("entry:/","")
+
+ return str_content
+
+ def _get_definition_mdd(self, word):
+ """根据关键字得到MDX词典的媒体"""
+ word = word.replace('/', '\\')
+ content = self.builder.mdd_lookup(word)
+ if len(content) > 0:
+ return [content[0]]
+ else:
+ return []
+
def get_html(self):
- if not self.cache[self.word]:
- html = ''
- result = self.builder.mdx_lookup(self.word) # self.word: unicode
- if result:
- if result[0].upper().find(u"@@@LINK=") > -1:
- # redirect to a new word behind the equal symol.
- self.word = result[0][len(u"@@@LINK="):].strip()
- return self.get_html()
- else:
- html = self.adapt_to_anki(result[0])
- self.cache[self.word] = html
- return self.cache[self.word]
+ """取得self.word对应的html页面"""
+ if not self.html_cache[self.word]:
+ html = self._get_definition_mdx()
+ if html:
+ self.html_cache[self.word] = html
+ return self.html_cache[self.word]
- def adapt_to_anki(self, html):
- """
- 1. convert the media path to actual path in anki's collection media folder.
- 2. remove the js codes (js inside will expires.)
- """
- # convert media path, save media files
- media_files_set = set()
- mcss = re.findall(r'href="(\S+?\.css)"', html)
- media_files_set.update(set(mcss))
- mjs = re.findall(r'src="([\w\./]\S+?\.js)"', html)
- media_files_set.update(set(mjs))
- msrc = re.findall(r'', html)
- media_files_set.update(set(msrc))
- msound = re.findall(r'href="sound:(.*?\.(?:mp3|wav))"', html)
- if config.export_media:
- media_files_set.update(set(msound))
- for each in media_files_set:
- html = html.replace(each, u'_' + each.split('/')[-1])
- # find sounds
- p = re.compile(
- r']+?href=\"(sound:_.*?\.(?:mp3|wav))\"[^>]*?>(.*?)')
- html = p.sub(u"[\\1]\\2", html)
- self.save_media_files(media_files_set)
- for cssfile in mcss:
- cssfile = '_' + \
- os.path.basename(cssfile.replace('\\', os.path.sep))
- # if not exists the css file, the user can place the file to media
- # folder first, and it will also execute the wrap process to generate
- # the desired file.
- if not os.path.exists(cssfile):
- self.missed_css.add(cssfile[1:])
- new_css_file, wrap_class_name = wrap_css(cssfile)
- html = html.replace(cssfile, new_css_file)
- # add global div to the result html
- html = u'{1}
'.format(
- wrap_class_name, html)
-
- return html
-
- def save_file(self, filepath_in_mdx, savepath=None):
- basename = os.path.basename(filepath_in_mdx.replace('\\', os.path.sep))
- if savepath is None:
- savepath = '_' + basename
+ def save_file(self, filepath_in_mdx, savepath):
+ """从mmd中取出filepath_in_mdx媒体文件并保存到savepath"""
try:
- bytes_list = self.builder.mdd_lookup(filepath_in_mdx)
- if bytes_list and not os.path.exists(savepath):
- with open(savepath, 'wb') as f:
- f.write(bytes_list[0])
- return savepath
+ bytes_list = self._get_definition_mdd(filepath_in_mdx)
+ if bytes_list:
+ if not os.path.exists(savepath):
+ with open(savepath, 'wb') as f:
+ f.write(bytes_list[0])
+ return savepath
except sqlite3.OperationalError as e:
- showInfo(str(e))
-
- def save_media_files(self, data):
- """
- get the necessary static files from local mdx dictionary
- ** kwargs: data = list
- """
- diff = data.difference(self.media_cache['files'])
- self.media_cache['files'].update(diff)
- lst, errors = list(), list()
- wild = [
- '*' + os.path.basename(each.replace('\\', os.path.sep)) for each in diff]
- try:
- for each in wild:
- keys = self.builder.get_mdd_keys(each)
- if not keys:
- errors.append(each)
- lst.extend(keys)
- for each in lst:
- self.save_file(each)
- except AttributeError:
+ #showInfo(str(e))
pass
-
- return errors
+ return ''
class StardictService(LocalService):
@@ -372,12 +357,13 @@ class StardictService(LocalService):
def __init__(self, dict_path):
super(StardictService, self).__init__(dict_path)
self.query_interval = 0.05
- self.builder = StardictBuilder(self.dict_path, in_memory=False)
- self.builder.get_header()
+ if self.support:
+ self.builder = StardictBuilder(self.dict_path, in_memory=False)
+ self.builder.get_header()
- @staticmethod
- def support(dict_path):
- return os.path.isfile(dict_path) and dict_path.lower().endswith('.ifo')
+ @property
+ def support(self):
+ return os.path.isfile(self.dict_path) and self.dict_path.lower().endswith('.ifo')
@property
def title(self):
@@ -388,7 +374,7 @@ class StardictService(LocalService):
@export(u"default", 0)
def fld_whole(self):
- self.builder.check_build()
+ #self.builder.check_build()
try:
result = self.builder[self.word]
result = result.strip().replace('\r\n', '
')\
diff --git a/src/fastwq/service/bing.py b/src/fastwq/service/bing.py
index b3a8c86..66500d6 100644
--- a/src/fastwq/service/bing.py
+++ b/src/fastwq/service/bing.py
@@ -2,8 +2,7 @@
import re
from aqt.utils import showInfo, showText
-from BeautifulSoup import BeautifulSoup
-from .base import WebService, export, register, with_styles
+from .base import WebService, export, register, with_styles, parseHtml
@register(u'Bing')
@@ -15,55 +14,28 @@ class Bing(WebService):
def _get_content(self):
word = self.word.replace(' ', '_')
data = self.get_response(u"http://cn.bing.com/dict/search?q={}&mkt=zh-cn".format(word))
-
- soup = BeautifulSoup(data)
-
- def _get_element(soup, tag, id=None, class_=None, subtag=None):
- # element = soup.find(tag, id=id, class_=class_) # bs4
- element = None
- if id:
- element = soup.find(tag, {"id": id})
- if class_:
- element = soup.find(tag, {"class": class_})
- if subtag and element:
- element = getattr(element, subtag, '')
- return element
-
+ soup = parseHtml(data)
result = {}
- element = _get_element(soup, 'div', class_='hd_prUS')
+
+ element = soup.find('div', class_='hd_prUS')
if element:
result['phonitic_us'] = str(element).decode('utf-8')
- element = _get_element(soup, 'div', class_='hd_pr')
+
+ element = soup.find('div', class_='hd_pr')
if element:
result['phonitic_uk'] = str(element).decode('utf-8')
- element = _get_element(soup, 'div', class_='hd_if')
+
+ element = soup.find('div', class_='hd_if')
if element:
result['participle'] = str(element).decode('utf-8')
- element = _get_element(soup, 'div', class_='qdef', subtag='ul')
+
+ element = soup.find('div', class_='qdef')
if element:
- result['def'] = u''.join([str(content).decode('utf-8')
- for content in element.contents])
- # for pair in pairs])
- # result = _get_from_element(
- # result, 'advanced_ec', soup, 'div', id='authid')
- # result = _get_from_element(
- # result, 'ec', soup, 'div', id='crossid')
- # result = _get_from_element(
- # result, 'ee', soup, 'div', id='homoid')
- # result = _get_from_element(
- # result, 'web_definition', soup, 'div', id='webid')
- # result = _get_from_element(
- # result, 'collocation', soup, 'div', id='colid')
- # result = _get_from_element(
- # result, 'synonym', soup, 'div', id='synoid')
- # result = _get_from_element(
- # result, 'antonym', soup, 'div', id='antoid')
- # result = _get_from_element(
- # result, 'samples', soup, 'div', id='sentenceCon')
+ element = getattr(element, 'ul', '')
+ if element:
+ result['def'] = u''.join([str(content) for content in element.contents])
+
return self.cache_this(result)
- # except Exception as e:
- # showInfo(str(e))
- # return {}
def _get_field(self, key, default=u''):
return self.cache_result(key) if self.cached(key) else self._get_content().get(key, default)
@@ -90,35 +62,3 @@ class Bing(WebService):
if val == None or val == '':
return ''
return self._css(val)
-
- # @export(u'权威英汉双解', 5)
- # def fld_advanced_ec(self):
- # return self._get_field('advanced_ec')
-
- # @export(u'英汉', 6)
- # def fld_ec(self):
- # return self._get_field('ec')
-
- # @export(u'英英', 7)
- # def fld_ee(self):
- # return self._get_field('ee')
-
- # @export(u'网络释义', 8)
- # def fld_web_definition(self):
- # return self._get_field('web_definition')
-
- # @export(u'搭配', 9)
- # def fld_collocation(self):
- # return self._get_field('collocation')
-
- # @export(u'同义词', 10)
- # def fld_synonym(self):
- # return self._get_field('synonym')
-
- # @export(u'反义词', 11)
- # def fld_antonym(self):
- # return self._get_field('antonym')
-
- # @export(u'例句', 12)
- # def fld_samples(self):
- # return self._get_field('samples')
diff --git a/src/fastwq/service/longman.py b/src/fastwq/service/longman.py
index 40dc25a..13800a1 100644
--- a/src/fastwq/service/longman.py
+++ b/src/fastwq/service/longman.py
@@ -8,9 +8,7 @@ Created: 12/20/2017
"""
import os
from warnings import filterwarnings
-
-import requests as rq
-from bs4 import BeautifulSoup, Tag
+from ..libs.bs4 import BeautifulSoup, Tag
from .base import WebService, export, register, with_styles
diff --git a/src/fastwq/service/manager.py b/src/fastwq/service/manager.py
index e57e0e8..6546f1b 100644
--- a/src/fastwq/service/manager.py
+++ b/src/fastwq/service/manager.py
@@ -84,31 +84,31 @@ class ServiceManager(object):
web_services, local_custom_services = set(), set()
mypath = os.path.dirname(os.path.realpath(__file__))
files = [f for f in os.listdir(mypath)
- if f not in ('__init__.py', 'base.py', 'manager.py', 'pool.py') and not f.endswith('.pyc')]
+ if f not in ('__init__.py', 'base.py', 'manager.py', 'pool.py') and not f.endswith('.pyc') and not os.path.isdir(mypath+os.sep+f)]
base_class = (WebService, LocalService,
MdxService, StardictService)
for f in files:
- try:
- module = importlib.import_module(
- '.%s' % os.path.splitext(f)[0], __package__)
- for name, cls in inspect.getmembers(module, predicate=inspect.isclass):
- if cls in base_class:
- continue
- #try:
- #service = cls(*args)
- service = service_wrap(cls, *args)
- service.__unique__ = name
- if issubclass(cls, WebService):
- web_services.add(service)
- # get the customized local services
- if issubclass(cls, LocalService):
- local_custom_services.add(service)
- #except Exception:
- # exclude the local service whose path has error.
- # pass
- except ImportError:
- continue
+ #try:
+ module = importlib.import_module(
+ '.%s' % os.path.splitext(f)[0], __package__)
+ for name, cls in inspect.getmembers(module, predicate=inspect.isclass):
+ if cls in base_class:
+ continue
+ #try:
+ #service = cls(*args)
+ service = service_wrap(cls, *args)
+ service.__unique__ = name
+ if issubclass(cls, WebService):
+ web_services.add(service)
+ # get the customized local services
+ if issubclass(cls, LocalService):
+ local_custom_services.add(service)
+ #except Exception:
+ # exclude the local service whose path has error.
+ # pass
+ #except ImportError:
+ # continue
return web_services, local_custom_services
def _get_available_local_services(self):
diff --git a/src/fastwq/service/oxford_learning.py b/src/fastwq/service/oxford_learning.py
index b3ee7c8..3cc7995 100644
--- a/src/fastwq/service/oxford_learning.py
+++ b/src/fastwq/service/oxford_learning.py
@@ -1,47 +1,36 @@
# coding=utf-8
-from warnings import filterwarnings
+#from warnings import filterwarnings
+from ..libs.bs4 import Tag
+from .base import WebService, export, register, with_styles, parseHtml
-from bs4 import BeautifulSoup, Tag
-from requests import Session
-
-from .base import WebService, export, register, with_styles
-
-filterwarnings('ignore')
+#filterwarnings('ignore')
import sys
-reload(sys)
-sys.setdefaultencoding('utf8')
+#reload(sys)
+#sys.setdefaultencoding('utf8')
+BASE_URL = u'https://www.oxfordlearnersdictionaries.com/definition/english/{word}'
@register(u'牛津学习词典')
class OxfordLearning(WebService):
- _base_url = 'https://www.oxfordlearnersdictionaries.com/definition/english/'
-
+
def __init__(self):
super(OxfordLearning, self).__init__()
- self.s = Session()
- self.s.headers = {
- 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 '
- '(KHTML, like Gecko) Chrome/31.0.1623.0 Safari/537.36'
- }
- self.s.get(self._base_url)
-
def query(self, word):
"""
-
:param word:
:rtype: WebWord
"""
- _qry_url = self._base_url + word
+ qry_url = BASE_URL.format(word=word)
retried = 10
while retried:
try:
- rsp = self.s.get(_qry_url, )
- if rsp.status_code == 200:
- return OxfordLearningDictWord(rsp.content.decode('utf-8'))
+ rsp = self.get_response(qry_url, timeout=15)
+ if rsp:
+ return OxfordLearningDictWord(rsp.decode('utf-8'))
break
except:
retried -= 1
@@ -121,7 +110,7 @@ class OxfordLearningDictWord:
if not markups:
return
self.markups = markups
- self.bs = BeautifulSoup(self.markups, from_encoding="utf-8")
+ self.bs = parseHtml(self.markups)
self._defs = []
self._defs_html = []
diff --git a/src/fastwq/service/static/_ldoce6.css b/src/fastwq/service/static/_ldoce6.css
new file mode 100644
index 0000000..d855654
--- /dev/null
+++ b/src/fastwq/service/static/_ldoce6.css
@@ -0,0 +1,733 @@
+.entry {
+ line-height: 150%;
+ color: black;
+ display: block;
+}
+.abbr {
+ font-weight: bold;
+}
+.ac,.ac {
+ padding-left: 2px;
+ padding-right: 2px;
+ border-radius: 2px 2px 2px 2px;
+ border-style: solid;
+ border-width: 1px;
+ font-variant: small-caps;
+ font-size: 80%;
+ font-weight: bold;
+ color: blue;
+}
+.amequiv {
+ font-weight: bold;
+}
+.brequiv {
+ font-weight: bold;
+}
+.collo {
+ font-weight: bold;
+}
+.colloexa {
+ display: block;
+}
+.colloinexa {
+ font-style: italic;
+ font-weight: bold;
+}
+.comp {
+ font-weight: bold;
+}
+.deriv {
+ font-weight: bold;
+ color: blue;
+}
+.errorbox {
+ display: block;
+}
+.etymsense {
+ display: block;
+}
+.etymrefhwd {
+ font-style: italic;
+}
+.etymrefhom {
+ font-size: 80%;
+ vertical-align: super;
+ font-style: normal;
+}
+.etymorigin {
+ font-style: italic;
+}
+.etymtran {
+ font-weight: bold;
+}
+.etymbox {
+ margin-top: 1em;
+ display: block;
+}
+.example {
+ font-style: italic;
+ display: block;
+ color: blue;
+}
+.freq,.freq {
+ padding-left: 2px;
+ padding-right: 2px;
+ border-radius: 2px 2px 2px 2px;
+ border-style: solid;
+ border-width: 1px;
+ font-variant: small-caps;
+ font-size: 80%;
+ font-weight: bold;
+ color: red;
+}
+.level {
+ color: red;
+ font-size: 160%;
+}
+.fullform {
+ font-weight: bold;
+}
+.geo,span.geo {
+ font-weight: normal;
+ font-style: italic;
+ color: purple;
+}
+.gloss,.collgloss {
+ font-weight: normal;
+ font-style: normal;
+ color: black;
+}
+.gram {
+ color: green;
+}
+.hintbold {
+ font-weight: bold;
+}
+.hintitalic {
+ font-style: italic;
+}
+hinttitle {
+ font-weight: bold;
+}
+.homnum {
+ vertical-align: super;
+ font-size: 8pt;
+ color: blue;
+ font-weight: bold;
+}
+.hwd {
+ display: none;
+}
+.hyphenation {
+ font-weight: bold;
+ font-size: 120%;
+ color: blue;
+}
+.frequent {
+ color: red;
+}
+.lexunit {
+ font-weight: bold;
+}
+.lexvar {
+ font-weight: bold;
+}
+.linkword {
+ font-style: italic;
+}
+note {
+ display: block;
+}
+object {
+ font-weight: normal;
+}
+.opp {
+ font-weight: bold;
+}
+.orthvar {
+ font-weight: bold;
+}
+.pastpart {
+ font-weight: bold;
+}
+pastpartx {
+ font-weight: bold;
+}
+.pasttense {
+ font-weight: bold;
+}
+pasttensex {
+ font-weight: bold;
+}
+.phrvbentry {
+ display: block;
+ margin-top: 10px;
+ margin-left: 10px;
+}
+.phrvbhwd {
+ font-weight: bold;
+ color: blue;
+}
+.pluralform {
+ font-weight: bold;
+}
+.pos {
+ font-style: italic;
+ color: green;
+ font-weight: normal;
+}
+.prespart {
+ font-weight: bold;
+}
+prespartx {
+ font-weight: bold;
+}
+.propform {
+ font-weight: bold;
+ display: block;
+}
+.propformprep {
+ font-weight: bold;
+ display: block;
+}
+.ptandpp {
+ font-weight: bold;
+}
+ptandppx {
+ font-weight: bold;
+}
+.refhomnum {
+ vertical-align: super;
+ font-size: 60%;
+}
+.refhwd,.refsensenum,refsense {
+ font-style: normal;
+ font-variant: small-caps;
+ text-transform: lowercase;
+}
+.refsensenum {
+ font-size: 80%;
+}
+crossrefto .reflex {
+ display: none;
+}
+.reflex {
+ font-weight: bold;
+}
+.registerlab {
+ font-style: italic;
+ color: purple;
+}
+.relatedwd {
+ font-weight: bold;
+}
+.runon {
+ display: block;
+ margin-top: 8px;
+}
+.sense {
+ display: block;
+ margin-bottom: 14px;
+ margin-top: 6px;
+}
+.signpost {
+ color: white;
+ background-color: #35A3FF;
+ margin-left: .5em;
+ font-weight: bold;
+ font-variant: small-caps;
+ text-transform: uppercase;
+ font-size: 80%;
+ padding: 1px 2px 1px 1px;
+}
+.spokensect {
+ border-top: solid;
+ border-bottom: solid;
+ border-width: 2px;
+ display: block;
+ margin-bottom: 1ex;
+ clear: both;
+ margin-top: 1ex;
+ border-color: #00A2E8;
+ padding: 3px;
+}
+.spokensecthead {
+ display: block;
+ color: #00A2E8;
+ font-weight: bold;
+}
+.pronstrong {
+ font-style: italic;
+}
+.subsense {
+ display: block;
+}
+.superl {
+ font-weight: bold;
+}
+.syn {
+ font-weight: bold;
+}
+.t3perssing {
+ font-weight: bold;
+}
+t3perssingx {
+ font-weight: bold;
+}
+unclassified {
+ font-weight: bold;
+}
+span.neutral {
+ font-style: normal;
+ font-weight: normal;
+ font-variant: normal;
+ color: black;
+ text-decoration: none;
+}
+.cross {
+ color: red;
+ font-weight: bold;
+}
+span.italic {
+ color: black;
+ font-style: italic;
+ font-weight: normal;
+}
+.badexa {
+ text-decoration: line-through;
+ font-style: italic;
+}
+.hint .expl {
+ display: inline;
+}
+span.infllab {
+ font-style: italic;
+ font-weight: normal;
+}
+span.warning {
+ font-style: normal;
+ font-weight: bold;
+ color: red;
+}
+span.sensenum {
+ font-style: normal;
+ font-weight: bold;
+ margin-right: 3px;
+ color: blue;
+}
+span.synopp {
+ padding-left: 3px;
+ padding-right: 3px;
+ border-radius: 2px 2px 2px 2px;
+ border-style: solid;
+ border-width: 1px;
+ font-variant: small-caps;
+ font-size: 80%;
+ font-weight: bold;
+ color: blue;
+}
+.subheading,.secheading {
+ display: block;
+ font-weight: bold;
+ font-weight: bold;
+ color: white;
+ background-color: #8187BF;
+ margin-left: -3px;
+ margin-right: -3px;
+ font-variant: small-caps;
+ padding-left: 3px;
+}
+.collocate,.exponent {
+ display: block;
+ padding-bottom: 5px;
+ margin-top: 5px;
+}
+.collocate.inline {
+ display: inline;
+}
+.expl {
+ display: block;
+ padding: 0 3px;
+}
+.colloc {
+ font-weight: bold;
+}
+.exp {
+ font-weight: bold;
+}
+.expr {
+ font-weight: bold;
+}
+.colloc.key {
+ color: blue;
+}
+span.keycollo {
+ font-weight: bold;
+ color: blue;
+}
+.thespropform {
+ font-weight: bold;
+}
+collexa {
+ font-style: italic;
+}
+collexa .colloinexa {
+ font-weight: normal;
+}
+thesexa {
+ font-style: italic;
+}
+learneritem {
+ display: block;
+}
+.goodcollo {
+ font-weight: bold;
+}
+.badcollo {
+ text-decoration: line-through;
+}
+.defbold {
+ font-weight: bold;
+}
+topic {
+ font-variant: small-caps;
+ color: blue;
+}
+.thesref.newline {
+ display: block;
+}
+.heading.newline {
+ display: block;
+}
+.thesref span.thesaurus {
+ color: blue;
+ font-variant: small-caps;
+}
+.thesref .refhwd,.thesref .refhomnum {
+ color: blue;
+ font-weight: bold;
+}
+i {
+ font-style: italic;
+}
+.imgholder {
+ cursor: pointer;
+ /*float: right;*/
+ display: block;
+ margin-bottom: 1ex;
+ padding: 2px;
+ clear: both}
+.imgholder img {
+ border: 1px solid #DDD;
+}
+.buttons {
+ display: block;
+}
+.popup-button {
+ background-color: #f0f2fc;
+ border-radius: 2px;
+ border: 1px solid #7e92c7;
+ color: #7e92c7;
+ text-transform: uppercase;
+ font-size: 66%;
+ padding: 2px 3px;
+ text-decoration: none;
+}
+.popup-button-hover {
+ background-color: #dfe3f8;
+ cursor: pointer;
+}
+.popverbs {
+ display: block;
+ color: white;
+ font-weight: bold;
+ background-color: #ec008d;
+ padding-left: 3px;
+ margin-bottom: 5px;
+}
+.verbtable .lemma {
+ color: blue;
+ font-size: 120%;
+ font-weight: bold;
+}
+.verbtable table {
+ border-collapse: separate;
+ border-spacing: 1px;
+ margin-top: 10px;
+}
+.verbtable td {
+ padding: 0 5px 0 2px;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #D2D2D2;
+}
+.header {
+ font-weight: bold;
+ font-variant: small-caps;
+}
+.verbtable td.col1 {
+ font-weight: bold;
+}
+.verbtable td.col2 {
+ font-style: italic;
+}
+.verbtable .geo {
+ font-style: italic;
+ color: #000;
+ font-size: normal;
+ font-weight: normal;
+}
+.verbtable .aux {
+ font-weight: bold;
+}
+.verbtable .verb_form {
+ color: blue;
+ font-weight: bold;
+}
+.collocations .last {
+ margin-bottom: 20px;
+}
+.collocations .colloc {
+ display: inline-block;
+ font-weight: bold;
+ margin-left: -2px;
+}
+.collobox,.thesbox,.usagebox,.grambox,spoken,.f2nbox {
+ border-radius: 9px 9px 9px 9px;
+ border-style: solid;
+ border-width: 2px;
+ display: block;
+ margin-bottom: 1ex;
+ clear: both;
+ margin-top: 1ex;
+}
+.f2nbox {
+ border-color: #00A2E8;
+}
+.f2nbox .heading {
+ color: #00A2E8;
+}
+.heading {
+ color: white;
+ font-weight: bold;
+ line-height: 100%;
+ padding: 3px;
+}
+.section {
+ display: block;
+ padding: 0 3px;
+}
+.last {
+ border-bottom-left-radius: 9px;
+ border-bottom-right-radius: 9px;
+}
+.thesbox {
+ background-color: #652D91;
+ border-color: #652D91;
+}
+.thesbox .section {
+ background-color: #E8E2F0;
+}
+.collobox {
+ background-color: #1D3E99;
+ border-color: #1D3E99;
+}
+.collobox .section {
+ background-color: #E2F4FD;
+}
+.usagebox {
+ background-color: #00A2E8;
+ border-color: #00A2E8;
+}
+.usagebox .expl {
+ background-color: #E2F4FD;
+}
+.grambox {
+ background-color: #00ADEE;
+ border-color: #00ADEE;
+}
+.grambox .expl,.grambox .compareword {
+ background-color: #E2F4FD;
+ display: block;
+ padding-left: 3px;
+}
+.compareword {
+ padding-top: 5px;
+ padding-bottom: 5px;
+}
+.gramrefcont {
+ display: block;
+ text-transform: lowercase;
+ font-variant: small-caps;
+ padding-left: 3px;
+ padding-bottom: 1px;
+}
+.thesaurus .sense {
+ padding-left: 3px;
+ padding-right: 3px;
+ margin-top: 0;
+}
+.thesaurus .section,.collocations .section {
+ margin-bottom: 10px;
+}
+.thesaurus .secheading,.thesbox .secheading {
+ background-color: #A186BD;
+}
+.add_exa {
+ font-style: italic;
+ display: block;
+}
+.nodeword {
+ color: blue;
+ font-weight: bold;
+}
+.phrase {
+ display: block;
+}
+.phrasetext {
+ font-weight: bold;
+}
+.expandable {
+ cursor: pointer;
+}
+.entry div.content {
+ display: none;
+ margin-bottom: 10px;
+}
+.group,.w {
+ display: block;
+}
+.group .pos {
+ font-weight: bold;
+ display: block;
+}
+.item {
+ display: block;
+}
+.w {
+ font-weight: bold;
+}
+.popheader {
+ display: block;
+ color: white;
+ font-weight: bold;
+ padding-left: 3px;
+}
+.popheader.popexa {
+ background-color: red;
+}
+.popheader.popphrase {
+ background-color: purple;
+}
+.popheader.popcollo {
+ background-color: #1D3E99;
+}
+.popheader.popthes {
+ background-color: #652D91;
+}
+.popheader.popetym {
+ background-color: green;
+}
+.popheader.popwf {
+ background-color: #FFD448;
+}
+.popheader.pope_menu {
+ background-color: #57C0E0;
+}
+.ws-head {
+ font-weight: bold;
+ color: blue;
+ font-size: larger;
+}
+.ws-head.ref {
+ display: block;
+ margin-top: 10px;
+}
+.wswd {
+ font-weight: bold;
+ display: block;
+}
+.cyan {
+ color: #00A2E8;
+ font-size: larger;
+}
+.menuitem {
+ display: block;
+}
+ul.exas li {
+ font-style: italic;
+ color: blue;
+ margin-bottom: 3px;
+}
+.grammar {
+ display: block;
+}
+.str {
+ font-size: large;
+ font-weight: bold;
+}
+.etymology {
+ margin-top: 10px;
+}
+.group {
+ margin-top: 10px;
+}
+.menuitem .signpost {
+ background-color: #FFF;
+ color: black;
+ margin-left: 0;
+ font-size: 90%;
+}
+.phrvbs {
+ display: block;
+ margin-top: 10px;
+ margin-left: 5px;
+}
+.phrvbs .heading {
+ display: block;
+ color: blue;
+ font-weight: bold;
+}
+.phrv {
+ font-weight: bold;
+}
+.secheading,.subheading {
+ font-variant: normal;
+ text-transform: uppercase;
+}
+.secheading.no_convert {
+ text-transform: none;
+}
+.grambox .heading.newline {
+ background-color: #6CD0F6;
+}
+img[src*="img/spkr_"] {
+ margin-bottom: -5px;
+}
+.chwd {
+ margin-top: 10px;
+ margin-left: 8px;
+ margin-right: 8px;
+}
+.chwd a {
+ display: inline-block;
+ background-color: green;
+ color: white;
+ padding-top: 2px;
+ padding-bottom: 3px;
+ padding-left: 5px;
+ padding-right: 5px;
+ margin-right: 3px;
+ margin-bottom: 3px;
+ text-decoration: none;
+ font-size: 80%;
+ border-radius: 2px;
+}
+.chwd .hw {
+ color: white;
+}
+.entry, .entry.lozenge, .verbtable {
+ margin-top: 10px;
+ margin-left: 10px;
+ margin-right: 10px;
+}
diff --git a/src/fastwq/ui.py b/src/fastwq/ui.py
index 09b2700..64a0277 100644
--- a/src/fastwq/ui.py
+++ b/src/fastwq/ui.py
@@ -345,24 +345,29 @@ class OptionsDialog(QDialog):
for cls in service_manager.local_services:
# combo_data.insert("data", each.label)
service = service_pool.get(cls.__unique__)
- dict_combo.addItem(
- service.title, userData=service.unique)
- service_pool.put(service)
+ if service and service.support:
+ dict_combo.addItem(
+ service.title, userData=service.unique)
+ service_pool.put(service)
dict_combo.insertSeparator(dict_combo.count())
for cls in service_manager.web_services:
service = service_pool.get(cls.__unique__)
- dict_combo.addItem(
- service.title, userData=service.unique)
- service_pool.put(service)
+ if service and service.support:
+ dict_combo.addItem(
+ service.title, userData=service.unique)
+ service_pool.put(service)
def set_dict_combo_index():
- dict_combo.setCurrentIndex(-1)
+ #dict_combo.setCurrentIndex(-1)
for i in range(dict_combo.count()):
if current_text in _sl('NOT_DICT_FIELD'):
dict_combo.setCurrentIndex(0)
+ return
if dict_combo.itemText(i) == current_text:
dict_combo.setCurrentIndex(i)
+ return
+ dict_combo.setCurrentIndex(0)
set_dict_combo_index()
@@ -376,15 +381,19 @@ class OptionsDialog(QDialog):
field_combo.setFocus(Qt.MouseFocusReason) # MouseFocusReason
else:
field_text = field_combo.currentText()
- service_unique = dict_combo_itemdata
- current_service = service_pool.get(service_unique)
+ unique = dict_combo_itemdata
+ service = service_pool.get(unique)
+ text = ''
# problem
- if current_service and current_service.fields:
- for each in current_service.fields:
+ 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)
- service_pool.put(current_service)
+ text = each
+
+ field_combo.setEditText(text)
+ field_combo.setEnabled(text != '')
+ service_pool.put(service)
def radio_btn_checked(self):
rbs = self.findChildren(QRadioButton)