kindle manager

This commit is contained in:
gavin
2020-06-11 17:21:16 +08:00
parent c903004b8f
commit 43c602d8d6
38 changed files with 565 additions and 248 deletions

BIN
.DS_Store vendored

Binary file not shown.

3
1.htm
View File

@@ -4,5 +4,6 @@
<span style='font-size:22pt;color:maroon'>ʱ<EFBFBD><EFBFBD></span> <span style='font-size:22pt;color:maroon'>ʱ<EFBFBD><EFBFBD></span>
<span style='font-size:11pt;color:maroon'><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ע<EFBFBD><EFBFBD></span><br> <span style='font-size:11pt;color:maroon'><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ע<EFBFBD><EFBFBD></span><br>
<span>********************************************************</span><br> <span>********************************************************</span><br>
<span style='font-size:11.0pt;color:#31849B'><EFBFBD><EFBFBD><EFBFBD>ݷ缱<EFBFBD>˴󼡷<EFBFBD><EFBFBD><EFBFBD><EFBFBD>̴<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʣ<EFBFBD><EFBFBD>ϴ<EFBFBD><EFBFBD>Ͽξͷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˣ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʴ<EFBFBD><EFBFBD><EFBFBD></span><span style='font-size:14pt;color:maroon'><EFBFBD><EFBFBD>P123-126<32><36></span><br> <span style='font-size:11.0pt;color:#31849B'><EFBFBD><EFBFBD><EFBFBD><EFBFBD></span>
<span style='font-size:14pt;color:maroon'><EFBFBD><EFBFBD>P123-126<32><36></span><br>
</html> </html>

1
backup/bk.data Normal file

File diff suppressed because one or more lines are too long

View File

@@ -76,11 +76,37 @@ b['1']['2'] = {'3':1} # OK
- implement table model which inherited from QAbstractTableModel, so - implement table model which inherited from QAbstractTableModel, so
we can use **panda data structure** we can use **panda data structure**
# feature plan ## 1.0.5 (20200604)
## 20200528 ### feature
- backup clips after kman closed, check and read backup file when kman started
## learn lesson
# feature list
- first abstract from kindle hard / local directory for different OS **done** - first abstract from kindle hard / local directory for different OS **done**
- add GUI use QT **done** - add GUI use QT **done**
- use thread to check kindle connection status **XXXX** - use thread to check kindle connection status **XXXX**
- export function - import function:
- local **done**
- kindle **done**
- duokan
- amazon
- export function:
- to [evernote](https://github.com/benhorvath/kindle2evernote/blob/master/kindle2evernote.py)
- to web html format
- pdf
- onenote
- txt **done**
- markdown **done**
- easily coly filter notes to clipboard
- implement command line based on [argparse](https://www.jianshu.com/p/a41fbd4919f8)
- mobi / epub reader **XXXX**
- convert epub to mobi
- parse /Volumes/Kindle/system/vocabulary/vocab.db
- [hotkey](https://toolinbox.net/Klib/)
- write [tutorial](https://help.knotesapp.com/docs/tutorial/)
- search word in online dictionary
- chrome extension

View File

Before

Width:  |  Height:  |  Size: 1004 KiB

After

Width:  |  Height:  |  Size: 1004 KiB

View File

Before

Width:  |  Height:  |  Size: 981 KiB

After

Width:  |  Height:  |  Size: 981 KiB

View File

Before

Width:  |  Height:  |  Size: 991 KiB

After

Width:  |  Height:  |  Size: 991 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

Before

Width:  |  Height:  |  Size: 958 KiB

After

Width:  |  Height:  |  Size: 958 KiB

View File

Before

Width:  |  Height:  |  Size: 867 KiB

After

Width:  |  Height:  |  Size: 867 KiB

View File

Before

Width:  |  Height:  |  Size: 869 KiB

After

Width:  |  Height:  |  Size: 869 KiB

View File

Before

Width:  |  Height:  |  Size: 837 KiB

After

Width:  |  Height:  |  Size: 837 KiB

View File

Before

Width:  |  Height:  |  Size: 873 KiB

After

Width:  |  Height:  |  Size: 873 KiB

View File

Before

Width:  |  Height:  |  Size: 912 KiB

After

Width:  |  Height:  |  Size: 912 KiB

View File

Before

Width:  |  Height:  |  Size: 890 KiB

After

Width:  |  Height:  |  Size: 890 KiB

View File

Before

Width:  |  Height:  |  Size: 925 KiB

After

Width:  |  Height:  |  Size: 925 KiB

View File

Before

Width:  |  Height:  |  Size: 806 KiB

After

Width:  |  Height:  |  Size: 806 KiB

View File

Before

Width:  |  Height:  |  Size: 798 KiB

After

Width:  |  Height:  |  Size: 798 KiB

2
export.md Normal file
View File

@@ -0,0 +1,2 @@
TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT
--|--|--|--|--

BIN
icons/homepage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

BIN
icons/import.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 B

BIN
icons/kindle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

89
kman.py
View File

@@ -56,6 +56,16 @@ books =
} }
''' '''
'''
# vocab.db -> table: book_info
bookname = {'id':{
'title':'bookname_xxx',
'authors':'xxx'
},}
words = {
"bookname_xxx": {
'''
# modi clippath for different os # modi clippath for different os
SYS = 'WIN' if platform.system()=='Windows' else \ SYS = 'WIN' if platform.system()=='Windows' else \
('LINUX' if platform.system()=='LINUX' else 'MAC') ('LINUX' if platform.system()=='LINUX' else 'MAC')
@@ -63,14 +73,14 @@ SYS = 'WIN' if platform.system()=='Windows' else \
# some constants # some constants
LASTLINE = '==========' LASTLINE = '=========='
NTPREF = '--CG注:' NTPREF = '--CG注:'
CLIPFN = 'My Clippings.txt' KINDLEFN = 'My Clippings.txt'
CLIPPATH = './' # /Volumes/Kindle/documents/My\ Clippings.txt CLIPPATH = './'
#CLIPPATH = './tclip.txt'
OUTPREF = './clip' OUTPREF = './clip'
DEBUG = 1 # 0 - INFO; 1 - DEBUG DEBUG = 1 # 0 - INFO; 1 - DEBUG
LOG2FILE = 1 # 0 - to stdio; 1 - to file LOG2FILE = 1 # 0 - to stdio; 1 - to file
LOGFILE = 'log' LOGFILE = 'log'
DELIMITER= '|' DELIMITER= '|'
BACKUPFN = './backup/bk.data'
#HEADER = {0:'type',1:'bookname',2:'author',3:'position',4:'date',5:'content'} #HEADER = {0:'type',1:'bookname',2:'author',3:'position',4:'date',5:'content'}
# log info # log info
@@ -127,7 +137,7 @@ class kMan:
kp = self.get_kindle_path() kp = self.get_kindle_path()
if not kp: if not kp:
s2 = u'Disconnected ({})'.format(CLIPPATH+CLIPFN) s2 = u'Disconnected ({})'.format(CLIPPATH+KINDLEFN)
else: else:
with open(kp+'/system/version.txt' , 'r', encoding='utf8', errors='ignore') as f: with open(kp+'/system/version.txt' , 'r', encoding='utf8', errors='ignore') as f:
s2 = u'Connected ({}) version {}'.format(kp,f.read().strip()) s2 = u'Connected ({}) version {}'.format(kp,f.read().strip())
@@ -246,14 +256,14 @@ class kMan:
for kb,vb in bks.items(): for kb,vb in bks.items():
author = vb['author'] author = vb['author']
for ks, vs in vb.items(): for ks, vs in vb.items():
if ks in ['author', 'lines']: continue if ks == 'author': continue
secs.append(DELIMITER.join([vs['type'],kb,author, \ secs.append(DELIMITER.join([vs['type'],kb,author, \
self.format_time(' '.join([vs['day'],vs['week'],\ self.format_time(' '.join([vs['day'],vs['week'],\
vs['meridiem'],vs['time']])),vs['content']])) vs['meridiem'],vs['time']])),vs['content']]))
return hd+secs return hd+secs
def format_out(self,bks, fnpref, ft='MD'): def export_notes(self, bks, fnpref, ft='MD'):
"""format output and write to file """format output and write to file
markdown format: markdown format:
TYPE | bookname | author | marktime | content TYPE | bookname | author | marktime | content
@@ -299,12 +309,10 @@ class kMan:
""" """
[preks,prevs] = ['',{'content':'!#$%^&$%','type':'xx'}] [preks,prevs] = ['',{'content':'!#$%^&$%','type':'xx'}]
for kb,vb in bks.items(): for kb,vb in bks.items():
bks[kb]['lines'] = 0
# add copy() or throw RuntimeError: dictionary changed size during iteration # add copy() or throw RuntimeError: dictionary changed size during iteration
# reference - http://www.cocoachina.com/articles/89748 # reference - http://www.cocoachina.com/articles/89748
for ks, vs in vb.copy().items(): for ks, vs in vb.copy().items():
if ks in ['author', 'lines']: continue if ks == 'author': continue
bks[kb]['lines'] += 1
if (vs['content'] in prevs['content'] or \ if (vs['content'] in prevs['content'] or \
prevs['content'] in vs['content']) and \ prevs['content'] in vs['content']) and \
prevs['type'] == vs['type']: prevs['type'] == vs['type']:
@@ -318,20 +326,34 @@ class kMan:
return bks return bks
def get_totalnum_nt(self,bks):
""" get total number of note
Args:
bks: books dict
Return: total number of note
"""
nu = 0
for kb,vb in bks.items():
for ks, vs in vb.copy().items():
if ks == 'author': continue
nu += 1
return nu
def get_bookname_num(self,bks): def get_bookname_num(self,bks):
""" get note number of booknames """ get note number of booknames
Args: Args:
bks: books dict bks: books dict
Return: dict {bookname:num,...} Return: [total books, note number of books]
""" """
bksnum = defaultdict(dict) bksnum = defaultdict(dict)
nu = 0 nu = 0 # number of books
for kb,vb in bks.items(): for kb,vb in bks.items():
bksnum.setdefault(kb, 0) bksnum.setdefault(kb, 0)
for ks, vs in vb.copy().items():
if ks in ['author', 'lines']: continue
bksnum[kb] += 1
nu += 1 nu += 1
for ks, vs in vb.copy().items():
if ks == 'author': continue
bksnum[kb] += 1
return [nu, bksnum] return [nu, bksnum]
@@ -339,22 +361,23 @@ class kMan:
""" get note number of author """ get note number of author
Args: Args:
bks: books dict bks: books dict
Return: dict {bookname:num,...} Return: [total authors, note number of authors]
""" """
bksnum = defaultdict(dict) bksnum = defaultdict(dict)
nu = 0 nu = 0 # number of authors
for kb,vb in bks.items(): for kb,vb in bks.items():
for ks, vs in vb.copy().items(): for ks, vs in vb.copy().items():
if ks in ['author', 'lines']: continue if ks == 'author':
nu += 1
continue
au = vb['author'] au = vb['author']
bksnum.setdefault(au, 0) bksnum.setdefault(au, 0)
bksnum[au] += 1 bksnum[au] += 1
nu += 1
return [nu, bksnum] return [nu, bksnum]
def filter_clips(self, bks, info=None, tp=0): def filter_clips(self, bks, info=None, tp=0):
""" filter clips """ filter clips to show the clips in table view
Args: Args:
bks: books dict bks: books dict
info: filter by bookname or author information info: filter by bookname or author information
@@ -381,14 +404,14 @@ class kMan:
idx = 0 idx = 0
for kb, vb in nbks.items(): for kb, vb in nbks.items():
for ks,vs in vb.items(): for ks,vs in vb.items():
if ks in ['author', 'lines']: continue if ks == 'author': continue
tm = self.format_time(' '.join([vs['day'],vs['week'], \ tm = self.format_time(' '.join([vs['day'],vs['week'], \
vs['meridiem'],vs['time']])) vs['meridiem'],vs['time']]))
nttype = '标注' if vs['type']=='HL' else '笔记' nttype = '标注' if vs['type']=='HL' else '笔记'
seclist.append([nttype,kb,vb['author'],vs['position'],tm,vs['content']]) seclist.append([nttype,kb,vb['author'],vs['position'],tm,vs['content']])
idx += 1 idx += 1
return seclist return [nbks, seclist]
def add_note_to_highlight(self,bks): def add_note_to_highlight(self,bks):
""" append note content to corresponding highlight """ append note content to corresponding highlight
@@ -402,7 +425,7 @@ class kMan:
[preks,prevs] = ['',{'content':'!#$%^&$%','type':'xx'}] [preks,prevs] = ['',{'content':'!#$%^&$%','type':'xx'}]
for kb,vb in bks.items(): for kb,vb in bks.items():
for ks,vs in vb.copy().items(): for ks,vs in vb.copy().items():
if ks in ['author', 'lines']: continue if ks == 'author': continue
if [prevs['type'], vs['type']] == ['HL','NT']: if [prevs['type'], vs['type']] == ['HL','NT']:
bks[kb][preks]['content'] += str(NTPREF+vs['content']) bks[kb][preks]['content'] += str(NTPREF+vs['content'])
bks[kb].pop(ks) bks[kb].pop(ks)
@@ -431,9 +454,9 @@ class kMan:
nbks = defaultdict(dict) nbks = defaultdict(dict)
nu = 0 nu = 0
for kb,vb in bks.items(): for kb,vb in bks.items():
nbks[kb]['lines'] = 0 num_nt_book = 0
for ks,vs in vb.copy().items(): for ks,vs in vb.copy().items():
if ks in ['author', 'lines']: if ks == 'author':
nbks[kb][ks] = vs nbks[kb][ks] = vs
continue continue
if t in ['ALL', vs['type']]: if t in ['ALL', vs['type']]:
@@ -442,9 +465,9 @@ class kMan:
found = re.search(s, scopestr[p]) found = re.search(s, scopestr[p])
if found: if found:
nbks[kb][ks] = vs nbks[kb][ks] = vs
nbks[kb]['lines'] += 1 num_nt_book += 1
nu += 1 nu += 1
if nbks[kb]['lines']==0: if num_nt_book==0:
nbks.pop(kb) nbks.pop(kb)
return [nu,nbks] return [nu,nbks]
@@ -513,7 +536,7 @@ class kMan:
return False return False
def import_clips(self, fp=(CLIPPATH+CLIPFN)): def import_clips(self, fp=(CLIPPATH+KINDLEFN)):
"""import clips from local file or kindle """import clips from local file or kindle
4 lines for each section seperated with '=======' 4 lines for each section seperated with '======='
so read 4 lines before '=======' so read 4 lines before '======='
@@ -583,7 +606,8 @@ class kMan:
self.refleshtime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) self.refleshtime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
return bks return self.drop_duplicate(bks)
if __name__=='__main__': if __name__=='__main__':
#books = defaultdict(dict) #books = defaultdict(dict)
@@ -595,11 +619,11 @@ if __name__=='__main__':
# test search note function # test search note function
searchnote = km.search_clip(books, '三大都市圈', 'ALL', 'CONTENT') searchnote = km.search_clip(books, '三大都市圈', 'ALL', 'CONTENT')
if searchnote[0] > 0: km.format_out(searchnote[1], 'searchcontent', ft='MD') if searchnote[0] > 0: km.export_notes(searchnote[1], 'searchcontent', ft='MD')
searchnote = km.search_clip(books, '经济', 'ALL', 'TITLE') searchnote = km.search_clip(books, '经济', 'ALL', 'TITLE')
if searchnote[0] > 0: km.format_out(searchnote[1], 'searchtitle', ft='MD') if searchnote[0] > 0: km.export_notes(searchnote[1], 'searchtitle', ft='MD')
searchnote = km.search_clip(books, '巴曙松', 'ALL', 'AUTHOR') searchnote = km.search_clip(books, '巴曙松', 'ALL', 'AUTHOR')
if searchnote[0] > 0: km.format_out(searchnote[1], 'searchauthor', ft='MD') if searchnote[0] > 0: km.export_notes(searchnote[1], 'searchauthor', ft='MD')
print(km.get_bookname_num(books)) print(km.get_bookname_num(books))
print(km.get_author_num(books)) print(km.get_author_num(books))
@@ -612,7 +636,8 @@ if __name__=='__main__':
fw.write(km.dict2json(books)) fw.write(km.dict2json(books))
if km.json2dict('./xx')==books: print( 'test OK') if km.json2dict('./xx')==books: print( 'test OK')
km.format_out(books, OUTPREF, ft='MD') km.export_notes(books, OUTPREF, ft='MD')
# print data with json format # print data with json format
logger.debug(json.dumps(books, indent=4, sort_keys=True, ensure_ascii=False)) logger.debug(json.dumps(books, indent=4, sort_keys=True, ensure_ascii=False))

View File

@@ -8,6 +8,7 @@
######################################################### #########################################################
import sys import sys
import os
from time import sleep from time import sleep
import pandas as pd import pandas as pd
@@ -42,11 +43,20 @@ class kmanWindow(QMainWindow):
ui.setupUi(self) ui.setupUi(self)
self.ui = ui self.ui = ui
self.km = kMan()
self.books = self.km.import_clips()
self.filter_data = self.km.filter_clips(self.books)
self.add_ui_component() self.add_ui_component()
self.km = kMan()
# initial check order:
# 1. backup file bk.data ->
# 2. kindle(My Clippings.txt) ->
# 3. local file(config) ->
if os.path.exists(BACKUPFN) and os.path.getsize(BACKUPFN) != 0:
self.books = self.km.json2dict(BACKUPFN)
elif self.km.get_kindle_path():
self.import_kindle()
else:
self.books = self.km.import_clips()
[self.filter_books, self.filter_data] = self.km.filter_clips(self.books)
self.refresh_ui_component() self.refresh_ui_component()
# timer to check status of kindle # timer to check status of kindle
@@ -55,7 +65,7 @@ class kmanWindow(QMainWindow):
self.timer.start(1000) self.timer.start(1000)
# connect action/toolbutton to slot functions # connect action/toolbutton to slot functions
ui.actionimportkindle.triggered.connect(lambda: self.import_kindle(self.books)) ui.actionimportkindle.triggered.connect(lambda: self.import_kindle())
ui.actionimportlocal.triggered.connect(lambda: self.import_local()) ui.actionimportlocal.triggered.connect(lambda: self.import_local())
ui.actionconfig.triggered.connect(lambda: self.config()) ui.actionconfig.triggered.connect(lambda: self.config())
ui.actionwords.triggered.connect(lambda: self.words()) ui.actionwords.triggered.connect(lambda: self.words())
@@ -63,46 +73,53 @@ class kmanWindow(QMainWindow):
ui.actionhomepage.triggered.connect(lambda: self.homepage()) ui.actionhomepage.triggered.connect(lambda: self.homepage())
ui.actionabout.triggered.connect(lambda: self.about()) ui.actionabout.triggered.connect(lambda: self.about())
ui.actionflush.triggered.connect(lambda: self.refresh()) ui.actionflush.triggered.connect(lambda: self.refresh())
ui.actionexport.triggered.connect(lambda: self.export())
#ui.searchLineEdit.returnPressed.connect(self.search_return_press())
ui.searchComboBox.currentIndexChanged.connect(self.search_scope_change) ui.searchComboBox.currentIndexChanged.connect(self.search_scope_change)
ui.searchToolButton.clicked.connect(self.search_button_clicked) ui.searchToolButton.clicked.connect(self.search_button_clicked)
ui.treeView.clicked.connect(self.tree_item_clicked) ui.treeView.clicked.connect(self.tree_item_clicked)
ui.tableView.clicked.connect(self.table_item_clicked) ui.tableView.clicked.connect(self.table_item_clicked)
ui.tableView.horizontalHeader().setStretchLastSection(True)
ui.tableView.verticalHeader().hide()
ui.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
ui.tableView.setColumnWidth(0, 40) # type ui.tableView.setColumnWidth(0, 40) # type
ui.tableView.setColumnWidth(2, 50) # author ui.tableView.setColumnWidth(2, 50) # author
ui.tableView.selectRow(0) ui.tableView.selectRow(0)
# creat tablemodel
data = self.convert_to_panda(self.filter_data)
ui.tablemodel = nTableModel(data)
def add_ui_component(self): def add_ui_component(self):
self.ui.searchComboBox.addItems(['ALL','bookname','content','author']) self.ui.searchComboBox.addItems([u'ALL',u'BOOKNAME',u'CONTENT',u'AUTHOR'])
self.ui.treeView.resize(200,200) self.ui.treeView.resize(200,200)
# status bar # status bar
self.ui.status_label = QLabel() self.ui.status_label = QLabel()
self.ui.pe = QPalette() self.ui.pe = QPalette()
# table view def refresh_ui_component(self, comp=0):
self.ui.tablemodel = nTableModel(self.convert_to_panda(self.filter_data)) """ refresh treeView, tableview, textedit information
self.ui.tableView.verticalHeader().hide() after import or open clips file
self.ui.tableView.setModel(self.ui.tablemodel) Args: comp 0 - treeview + tablevew + textedit
self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) 1 - tablevew + textedit
# fit window 2 - textedit
#headerView = self.ui.tableView.horizontalHeader() """
#headerView.setSectionResizeMode(QHeaderView.Stretch) if not comp in [1,2]:
self.ui.tableView.horizontalHeader().setStretchLastSection(True)
self.ui.tablemodel.tabledata_update.connect(self.tabledata_update_slot)
def refresh_ui_component(self):
self.fill_treeview() self.fill_treeview()
self.show_status_info()
# refresh tableview # refresh tableview
if comp != 2:
data = self.convert_to_panda(self.filter_data) data = self.convert_to_panda(self.filter_data)
if hasattr(self.ui, 'tablemodel'):
del self.ui.tablemodel del self.ui.tablemodel
self.ui.tablemodel = nTableModel(data) self.ui.tablemodel = nTableModel(data)
self.ui.tableView.verticalHeader().hide() self.ui.tableView.verticalHeader().hide()
self.ui.tableView.setModel(self.ui.tablemodel) self.ui.tableView.setModel(self.ui.tablemodel)
#self.ui.tablemodel.tabledata_update.connect(self.tabledata_update_slot)
# refresh textedit content # refresh textedit content
if len(self.filter_data)>0:
[stype,sbookname,sauthor,sposition,stime,scontent] = self.filter_data[0] [stype,sbookname,sauthor,sposition,stime,scontent] = self.filter_data[0]
self.ui.textEdit.setHtml("""<span style='font-size:22pt;color:maroon'>《{bookname}》</span> self.ui.textEdit.setHtml("""<span style='font-size:22pt;color:maroon'>《{bookname}》</span>
<span style='font-size:22pt;color:maroon'> {author} </span> <span style='font-size:22pt;color:maroon'> {author} </span>
@@ -114,6 +131,8 @@ class kmanWindow(QMainWindow):
format(bookname=sbookname, author=sauthor, time=stime, format(bookname=sbookname, author=sauthor, time=stime,
note=stype, content=scontent, position=sposition)) note=stype, content=scontent, position=sposition))
self.show_status_info()
def convert_to_panda(self, seclist): def convert_to_panda(self, seclist):
pdframe = pd.DataFrame(seclist, \ pdframe = pd.DataFrame(seclist, \
columns = ['Type','Bookname','Author','Position','Date','Content']) columns = ['Type','Bookname','Author','Position','Date','Content'])
@@ -134,23 +153,15 @@ class kmanWindow(QMainWindow):
# filter clips # filter clips
self.filter_data = None self.filter_data = None
if description in ['root', 'bookname', 'author']: if description in ['root', 'bookname', 'author']:
self.filter_data = self.km.filter_clips(self.books) [self.filter_books, self.filter_data] = self.km.filter_clips(self.books)
elif description == 'bleaf': # bookname leaf elif description == 'bleaf': # bookname leaf
info = re.split(r'\s+',item.text())[0] info = re.split(r'\s+',item.text())[0]
self.filter_data = self.km.filter_clips(self.books, info, 1) [self.filter_books, self.filter_data] = self.km.filter_clips(self.books, info, 1)
else: # author leaf else: # author leaf
info = re.split(r'\s+',item.text())[0] info = re.split(r'\s+',item.text())[0]
self.filter_data = self.km.filter_clips(self.books, info, 2) [self.filter_books, self.filter_data] = self.km.filter_clips(self.books, info, 2)
''' self.refresh_ui_component(comp=1)
data = self.convert_to_panda(self.filter_data)
del self.ui.tablemodel
self.ui.tablemodel = nTableModel(data)
self.ui.tableView.verticalHeader().hide()
self.ui.tableView.setModel(self.ui.tablemodel)
'''
self.refresh_ui_component()
def table_item_clicked(self, index): def table_item_clicked(self, index):
if index.isValid(): if index.isValid():
@@ -175,8 +186,32 @@ class kmanWindow(QMainWindow):
note=stype, content=scontent, position=sposition)) note=stype, content=scontent, position=sposition))
def search_button_clicked(self): def search_button_clicked(self):
search_word = self.ui.searchLineEdit.text()
if search_word.strip() == '':
self.messagebox(ico=2, info=u'\n\n search content is empty!')
return 0
content_type = self.ui.searchComboBox.currentText()
#content_idx = self.ui.searchComboBox.currentIndex()
[nu, sbks] = self.km.search_clip(self.books, search_word, 'ALL', content_type)
[self.filter_books, self.filter_data] = self.km.filter_clips(sbks)
self.refresh_ui_component()
print( 'call search_button_clicked()' ) print( 'call search_button_clicked()' )
def keyPressEvent(self, event):
#print('hasfocus {} key {} {} {}'.format(self.ui.searchLineEdit.hasFocus(),\
# event.key(), Qt.Key_Return, event.key()==Qt.Key_Return))
if (self.ui.searchLineEdit.hasFocus() and event.key() == Qt.Key_Return):
print('call keyPressEvent() {} '.format(event.key()))
self.search_button_clicked()
def search_return_press(self):
self.search_button_clicked()
print('call search_return_press()')
# XXX
def search_scope_change(self,t): def search_scope_change(self,t):
p = {0:'ALL',1:'TITLE',2:'AUTHOR',3:'CONTENT'} p = {0:'ALL',1:'TITLE',2:'AUTHOR',3:'CONTENT'}
s = self.ui.searchLineEdit.text() s = self.ui.searchLineEdit.text()
@@ -209,20 +244,16 @@ class kmanWindow(QMainWindow):
self.ui.statusbar.addPermanentWidget(self.ui.status_label, stretch=0) self.ui.statusbar.addPermanentWidget(self.ui.status_label, stretch=0)
# define slot functions # define slot functions
def import_kindle(self,bks): def import_kindle(self):
fp = self.km.get_kindle_path() fp = self.km.get_kindle_path()
if not fp: if not fp:
self.messagebox(ico=2, info='\n\n kindle is not connected') self.messagebox(ico=2, info='\n\n kindle is not connected')
return 0 return 0
self.books = self.km.import_clips(fp+'documents/'+CLIPFN) self.books = self.km.import_clips(fp+'documents/'+KINDLEFN)
self.filter_data = self.km.filter_clips(self.books) [self.filter_books, self.filter_data] = self.km.filter_clips(self.books)
status = self.km.status_info()
self.refresh_ui_component() self.refresh_ui_component()
#print(bks)
def import_local(self): def import_local(self):
fn, ft = QFileDialog.getOpenFileName(self, fn, ft = QFileDialog.getOpenFileName(self,
"choose file to import", "choose file to import",
@@ -231,8 +262,7 @@ class kmanWindow(QMainWindow):
self.fn = fn self.fn = fn
self.books = self.km.import_clips(fn) self.books = self.km.import_clips(fn)
self.filter_data = self.km.filter_clips(self.books) [self.filter_books, self.filter_data] = self.km.filter_clips(self.books)
self.refresh_ui_component() self.refresh_ui_component()
#print('filename ', fn, 'filetype ', ft) #print('filename ', fn, 'filetype ', ft)
@@ -241,7 +271,7 @@ class kmanWindow(QMainWindow):
def fill_treeview(self): def fill_treeview(self):
self.ui.model = QStandardItemModel() self.ui.model = QStandardItemModel()
rootItem = self.ui.model.invisibleRootItem() rootItem = self.ui.model.invisibleRootItem()
item = QStandardItem('All Notes') item = QStandardItem('All Notes ({})'.format(self.km.get_totalnum_nt(self.books)))
icon = QIcon() icon = QIcon()
icon.addFile(u":/icons/emblem_library.png", QSize(), QIcon.Normal, QIcon.Off) icon.addFile(u":/icons/emblem_library.png", QSize(), QIcon.Normal, QIcon.Off)
item.setIcon(icon) item.setIcon(icon)
@@ -250,15 +280,15 @@ class kmanWindow(QMainWindow):
parent_item = item parent_item = item
# add bookname tree # add bookname tree
[totalnum, booknum] = self.km.get_bookname_num(self.books) [numbooks, booknum] = self.km.get_bookname_num(self.books)
bookname_item = QStandardItem('Bookname ({})'.format(totalnum)) bookname_item = QStandardItem('Bookname ({})'.format(numbooks))
icon = QIcon() icon = QIcon()
icon.addFile(u":/icons/book_open.png", QSize(), QIcon.Normal, QIcon.Off) icon.addFile(u":/icons/book_open.png", QSize(), QIcon.Normal, QIcon.Off)
bookname_item.setIcon(icon) bookname_item.setIcon(icon)
bookname_item.setAccessibleDescription('bookname') bookname_item.setAccessibleDescription('bookname')
parent_item.appendRow(bookname_item) parent_item.appendRow(bookname_item)
if totalnum > 0: if numbooks > 0:
for k, v in booknum.items(): for k, v in booknum.items():
item = QStandardItem('{} ({})'.format(k, v)) item = QStandardItem('{} ({})'.format(k, v))
icon = QIcon() icon = QIcon()
@@ -268,8 +298,8 @@ class kmanWindow(QMainWindow):
bookname_item.appendRow(item) bookname_item.appendRow(item)
# add author tree # add author tree
[totalnum, authnum] = self.km.get_author_num(self.books) [numauthor, authnum] = self.km.get_author_num(self.books)
author_item = QStandardItem('Author ({})'.format(totalnum)) author_item = QStandardItem('Author ({})'.format(numauthor))
icon = QIcon() icon = QIcon()
icon.addFile(u":/icons/person.png", QSize(), QIcon.Normal, QIcon.Off) icon.addFile(u":/icons/person.png", QSize(), QIcon.Normal, QIcon.Off)
author_item.setIcon(icon) author_item.setIcon(icon)
@@ -277,7 +307,7 @@ class kmanWindow(QMainWindow):
parent_item.appendRow(author_item) parent_item.appendRow(author_item)
parent_item = author_item parent_item = author_item
if totalnum > 0: if numauthor > 0:
for k, v in authnum.items(): for k, v in authnum.items():
item = QStandardItem('{} ({})'.format(k, v)) item = QStandardItem('{} ({})'.format(k, v))
icon = QIcon() icon = QIcon()
@@ -302,6 +332,9 @@ class kmanWindow(QMainWindow):
pass pass
def homepage(self): def homepage(self):
import webbrowser
webbrowser.open('https://gitee.com/douboer/kman')
print("call slot homepage()") print("call slot homepage()")
pass pass
@@ -316,10 +349,15 @@ class kmanWindow(QMainWindow):
pass pass
def refresh(self): def refresh(self):
self.import_kindle(self.books) self.import_kindle()
print("call slot refresh()") print("call slot refresh()")
pass pass
def export(self):
self.km.export_notes(self.filter_books, 'export', ft='MD')
print("call export()")
pass
def messagebox(self, ico=1, info=''): def messagebox(self, ico=1, info=''):
""" unify messagebox """ unify messagebox
Args: ico - QMessageBox.NoIcon 0 Args: ico - QMessageBox.NoIcon 0
@@ -341,8 +379,12 @@ class kmanWindow(QMainWindow):
msgBox.setBaseSize(QSize(600, 300)) msgBox.setBaseSize(QSize(600, 300))
r = msgBox.exec() r = msgBox.exec()
## XXXX # backup file when kman closed
# read backup file when kman start
def closeEvent(self, e): def closeEvent(self, e):
with open(BACKUPFN, 'w', encoding='utf8', errors='ignore') as fw:
fw.write(self.km.dict2json(self.books))
# stop check thread # stop check thread
self.flag = False self.flag = False
@@ -376,6 +418,7 @@ class nTableModel(QAbstractTableModel):
if orientation == Qt.Vertical: if orientation == Qt.Vertical:
return str(self._data.index[section]) return str(self._data.index[section])
if __name__ == "__main__": if __name__ == "__main__":
app = QApplication(sys.argv) app = QApplication(sys.argv)
@@ -384,8 +427,8 @@ if __name__ == "__main__":
icon.addFile(u":/icons/Cbb20.png", QSize(), QIcon.Normal, QIcon.Off) icon.addFile(u":/icons/Cbb20.png", QSize(), QIcon.Normal, QIcon.Off)
kmw.setWindowIcon(icon) kmw.setWindowIcon(icon)
kmw.setWindowTitle("kindle management") kmw.setWindowTitle("kindle management")
#kmw.resize(900, 600) kmw.resize(900, 600)
kmw.showFullScreen() #kmw.showFullScreen()
kmw.show() kmw.show()
# loop check kindle is connected or not # loop check kindle is connected or not

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject> <!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.11.2, 2020-06-08T13:33:08. --> <!-- Written by QtCreator 4.11.2, 2020-06-11T11:09:47. -->
<qtcreator> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>

View File

@@ -48,5 +48,8 @@
<file>icons/register.png</file> <file>icons/register.png</file>
<file>icons/person.png</file> <file>icons/person.png</file>
<file>icons/user.png</file> <file>icons/user.png</file>
<file>icons/kindle.png</file>
<file>icons/homepage.png</file>
<file>icons/import.png</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -6,96 +6,98 @@
from PySide2 import QtCore from PySide2 import QtCore
qt_resource_data = b"\ qt_resource_data = b"\
\x00\x00\x05w\ \x00\x00\x05\x9a\
\x00\ \x00\
\x00\x1d\x01x\x9c\xb5Y\xcd\x8e\xdb6\x10\xbe\xefS\x10\ \x00\x1ezx\x9c\xb5Y\xcd\x8e\xdb6\x10\xbe\xefS\x10\
\xbe\xd7\xb2\x17\xdbn\xb1\x90\x1dt\x8b\xb4\x01\x9a\x056\ \xbe\xd7\xb2\x17\xdbn\xb1\xd0:\xe8\x16i\x034\x0bl\
Xws,h\x89\xb6\xd8\x95D-Ee\xed\x9c\x92\ \xb0\xee\xe6X\xd0\x12m\xb1+\x89Z\x8a\x8a\xed\x9c\x92\
C\x0a\x14h\xd1\x1cz\xcc\xa9\x87\x9e\x8a\xb4\x87^r\ C\x0a\x14h\xd1\x1cz\xcc\xa9\x87\x9e\x8a\xb4\x87^r\
\xe8\xd34\x9b\xe41:\x22)\x89\xfa\xb5\x1b\xadn\x9a\ \xe8\xd34\x9b\xe41:\x22)\x89\xfa\xb5\x1b\xadn\x9a\
\x1f\x0eg>\xce\x0c\x87\xb6}o\x13\xf8\xe8\x09\xe11\ \x1f\x0eg>\xce\x0c9\xb6}o\x13\xf8\xe8\x09\xe11\
e\xe1l4\x1dOF\x88\x84\x0esi\xb8\x9e\x8d\xbe\ e\xe1\xe9h:\x9e\x8c\x10\x09\x1d\xe6\xd2pu:\xfa\
]|\xf5\xc9\xe7\xa3{\xf3\x03;\xa1\x85\xd2\x11(\xcd\ v\xfe\xd5'\x9f\x8f\xee\xcd\x0e\xec\x84\x16JG\xa04\
\x0f\x90\xed\xf88\x8e\xe7g\x98\x86\x8fi\xe8\xb2\x1b\xdb\ ;@\xb6\xe3\xe38\x9e\x9dc\x1a>\xa6\xa1\xcb\xd6\xb6\
R\x1c\x10\xddPwM\x04\x92\xf4l\xf4\xa8\xd0\x19\xa1\ \xa58 ZSwE\x04\x92\xf4\xe9\xe8Q\xa13B\
\x10\x07d628\xa0\x8f\xec\x88\xb3\x88p\xb1\xd5\xe2\ !\x0e\xc8\xe9\xc8\xe0\x80>\xb2#\xce\x22\xc2\xc5V\x8b\
5a\x01\x11|+\x85\xc8\xe6\xc4\x11\xf2\x0b\xd9\x9b\xf9\ W\x84\x05D\xf0\xad\x14\x22\x9b\x13G\xc8/dof\
\xc4\xb66\x9a\xd8\xa6\xc4V\x13\xb0\xa7\xf0\xe6\xc7\xc7G\ \x13\xdb\xdahb\x9b\x12[M\xc0\x9e\xc2\x9b\x1d\x1f\x1f\
\xb6\xa5>\x15\xdb#t\xed\x89\xf9\xd1\x14T\xf5\xb7\xb4\ \xd9\x96\xfaTl\x8f\xd0\x95'fGSP\xd5\xdf\xd2\
ieFm+\xdb\xbc\xc9\x93\x1b\xe9\xe4\x82\x0a\x9fh\ \xa6\x95\x19\xb5\xadl\xf3&O\xd6\xd2\xc99\x15>\xd1\
gb\xc1\x01\x9c\xf97 \xf0\x09:\xc3!^\x93\x80\ \xce\xc4\x82\x038\xb3o@\xe0\x13t\x8eC\xbc\x22\x01\
\x84\xc2\xb6\xb4\xa4n\xb3\x02\xc7cIfP8\xb0\x96\ \x09\x85miI\xddf\x05\x8e\xc7\x92\xcc\xa0p`-\
c_\xe9\xe8=|\xbceI\xb1\xe0kN\xdd\x87\x92\ \xc7\xbe\xd2\xd1{\xf8x\xcb\x92b\xc1\xd7\x9c\xba\x0f%\
\x95-Z\x17\x1c\x1d'\x15$@\x9c\xdd\xccFp\x84\ +[\xb4*8:N*H\x808[\x9f\x8e\xe0\x08\
\x0e\xf3\x93 L?\x95\xb4f\xf1\xc1)\xdb\x94-z\ \x1d\xe6'A\x98~*i\xcd\xe2\x833\xb6)[\xf4\
\x8c\xd3\xa7,\x14\xd8/\xd9\xd5\x963\xa2\x16\xcbC\xbc\ \x18\xa7OY(\xb0_\xb2\xab-gD-\x96\x87x\
$~f#&\x98;\x9eb\xe5+j\x90\x0a\xb2\x11\ A\xfc\xccFL0w<\xc5\xcaW\xd4 \x15d#\
\x868G\xf4B\xae6a\xd4\xf2\x12\x98\x9a\xa5\xdc\xc8\ \x0cq\x8e\xe8\xa5\x5cm\xc2\xa8\xe5%05K\xb9\x91\
}\xb4L'wxLCr\xdf\xa5\xa2\xe2t\xc6m\ \xfbh\x99N\xee\xf0\x98\x86\xe4\xbeKE\xc5\xe9\x8c\xdb\
\xf7;\xf2\xb1C<\xe6\xbb\x84/ZB\xb8\xfd\xe5\xcf\ \xeew\xe4c\x87x\xccw\x09\x9f\xb7\x84p\xfb\xcb\x9f\
w?\xfd\xf8\xf6\xcd\xef\xb7/\x7f\xfe\xf7\xd9\xf3\xb7\xff\ \xef~\xfa\xf1\xed\x9b\xdfo_\xfe\xfc\xef\xb3\xe7o\xff\
\xbc\xfa\xf0\xec\x05|\xdc\xfe\xf0\xe2\xf6\xf5\x9bw/_\ y\xf5\xe1\xd9\x0b\xf8\xb8\xfd\xe1\xc5\xed\xeb7\xef^\xbe\
\xbd\xff\xfb\xb7\xf7\x7f\xfc\xfa\xe1\xf5_\x83\x87\xf9%\x0b\ z\xff\xf7o\xef\xff\xf8\xf5\xc3\xeb\xbf\x06\x0f\xf3K\x16\
\x96\x0cN\xb9\x1cf\xcem\x0f\xd3I8\x87\x94l\x09\ ,\x18\x9cr9\xcc\x9c\xdb\x1e\xa6\x93p\x0e)\xd9\x12\
\xd1\x1a\xcc\xe1\x05c\xfei\x22\x04\x0b\xcb.\x1b\xfc\xff\ \xa25\x98\xc3s\xc6\xfc\xb3D\x08\x16\x96]6\xf8\xff\
\x9fS\xe3\xf1x/\xa4k\x06\xa9S\xda\x0f|\x07F\ ?\xa7\xc6\xe3\xf1^H\xd7\x0cR\xa7\xb4\x1f\xf8\x0e\x8c\
\x0c\xeer\x12\xb3\x84;\xa0r\x15@\xedG\xd1\xf8\x9a\ \x18\xdc\xe5$f\x09w@\xe5:\x80\xda\x8f\xa2\xf1\x0d\
;\xa6&\xb2C\xc6\x03\xec\xb3\xd5j~b\xc9e\x96\ wLMd\x87\x8c\x07\xd8g\xcb\xe5\xec\xc4\x92\xcb,\
\x8ad\xfc}D\xd6\xb6\xb5C\xac7\xfax\x9cmK\ \x15\xc9\xf8\xfb\x88\xaclk\x87Xo\xf4\xf18\xdb\x96\
\x15\xbbn\x0c\x86\xc8h\x12\xd3\xa6&Q9\x91\x8b\xc8\ *v\xdd\x18\x0c\x91\xd1$\xa6MM\xa2r\x22\x97\x91\
\xa7B\x10\x9e\x9f\x87\xa6\xbf;,\x9aC\x056h\x22\ O\x85 <?\x0fM\x7fwX4\x87\x0al\xd0D\
\x90;XP\x13=\x9b\x84I0\x7f$NN\x1e\xe4\ w\xb0\xa0&z6\x09\x93`\xf6H\x9c\x9c<\xc8\
M\xc6\xb6$\xf3\xa0-\xc4ZvpB.)\xc9/\ \x9b\x8cmI\xe6A[\x88\xb5\xec\xe0\x84\x5cQ\x92_\
\x10\x91\xd1\xc5.\x15Wb\xfa\x94\x9c3\x9f:[3\ \x22\xa3\x8b]*\xae\xc4\xf4)\xb9`>u\xb6f\
oRn$\xb9\xc8K\xbf\xc56\x02\xddsNV\x04\ \xde\xa4\xdcHr\x91\x97~\x8bm\x04\xba\x17\x9c,\x09\
\xd2\xde\x1d\xa1'\x05\xf7\xfe&\xc2az\x13\x96\x12\x01\ \xa4\xbd;BO\x0a\xee\xfdM\x84\xc3\xf4&,%\x02\
\x1a%$\x14\x11\x8e\x97\xde>\x06e\xe8\xa4we\xa1\ 4JH(\x22\x1c/\xbd}\x0c\xca\xd0I\xef\xcaB\
cP\xc6\xa1\x16\x9e\x18\xc7Z?\xe9JT\x01\xde\xd0\ \xc7\xa0\x8cC-<1\x8e\xb5~\xd2\x95\xa8\x02\xbc\xa1\
\x09.`q5,\xd3\x01u\x03\x1eM&\xa5\xcb\ A\x12\x5c\xc2\xe2jX\xa6\x03\xea\x06<\x9aLJ\x97\
P\x07\xa0n\xc1\xe9g\xc7\xc7\xc7\x87\xd3OK\xd7\xa2\ \xa1\x0e@\xdd\x82\xd3\xcf\x8e\x8f\x8f\x0f\xa7\x9f\x96\xaeE\
\xe1[\xa7WX@M-\x13A\xb2\xdb\x83`h\x8b\ \xc3\xb7N\xaf\xb0\x80\x9aZ$\x82d\xb7\x07\xc1\xd0\x16\
\x974\xa6K\xbf\xe4\xd8\x12\xaaw\xbe\xc2~LlK\ \xafhL\x17~\xc9\xb1\x05T\xefl\x89\xfd\x98\xd8\x96\
~\x17Vs\x1bE\x16T\xb2z\xcf|l\xcf\x81\xc6\ \xfc.\xac\xe66\x8a,\xa8d\xf5\x9e\xf9\xd8\x9e\x03\x8d\
t4\xf2\xf1\x12t\xa9S\xcd\xc6\xc6\x88\xab\xf9\x88!\ \xe9h\xe4\xe3\x15\xe8R\xa7\x9a\x8d\x8d\x11W\xf3\x11C\
\xd0RB\xe6\x8c\xf6\xa6\xd2\x98\x92m9y\xa6Nz\ \xa0\xa5\x84\xcc\x19\xedM\xa51%\xdbr\xf2\x5c\x9d\xf4\
wF\xee\x95\x92\xfb\xe4dsR\xee\xd3\x7f\xea\xf0@\ \xee\x8c\xdc+%\xf7\xc9\xc9\xe6\xa4\xdc\xa7\xff\xd4\xe1\x81\
?6/Y\x91\xd1\x03\x82S\x14q/p\xa6}\xc1\ ~l^\xb2\x22\xa3\x07\x04\xa7(\xe2^\xe0L\xfb\x82\
\xd9\xb3fkE\x9bUmQ\x98\xd5\xd2-\xa6\xd9\xc9\ \xb3g\xcd\xd6\x8a6\xab\xda\xa20\xab\xa5[\xbcf'\
\xa4^\xb6\xd5\xba\xdd\xef\xe2(\xd1e\xd2\xb8:\xcc;\ \x93z\xd9V\xebv\xbf\x8b\xa3D\x97I\xe3\xea0\xef\
\xc5T\xaa\x95(\x14[\x12\x9f\xe2\xa2F%c\x09\x0c\ \x14S\xa9V\xa2PlI|\x86\x8b\x1a\x95\x8c\x050\
\xabI\xff\x0c\xea\xce\xd0\x0e4\xa9\xf6\xec|\x14\x98\xaf\ \xac&\xfds\xa8;C;\xd0\xa4\xda\xb3s(0\xa7\
\x82\xf2\xb3\xa0\xfc.h{\x18\xe4X\x1e\x1e\x96\xa1,\ \x82\xf2XP\x9e\x0b\xda\x06\x83\x1c\xcb\xc3\xc32\x94\xc5\
^\x06\xd51\xbe#n9\xa4\x14q\x08M6\xc6Q\ dP}\xc6w\xc4-\x1f)E\x1cB\x93\x8dq\xd4\
{R\xe4\xd3\x8a^V\x9aX*\x87X\xeb\xbcz\xcd\ F\x8a\xfc\xb5\xa2\x97\x95^,\x95C\xacu^\xbd\xe6\
\x17\x9c\xe0\xcc\x9a\xece\x0b\x16-\x0a\x91\xd1\xe0\xaa\x8d\ \x0bNpfM\xf6\xb29\x8b\xe6\x85\xc8hp\xd5\xc6\
\xb7\xcd\xe2),\xbb\xcaL6\xb6\xf1\xba%\xd7\xc5N\ \xdbf\xf1\x0c\x96]g&\x1b\xdbx\xdd\x92\xebb'\
\xdal\xb5%E\xd0 b\x5c\x5c\xc9\xf7\x92\xca\x82n\ m\xb6\xda\x92\x22h\x101.\xae\xe5\xbc\xa4\xb2\xa0[\
M\x9fAGnS\x8cI\x849\x16\x8cw[\xbaa\ \xd3g\xd0\x91\xdb\x14c\x12a\x8e\x05\xe3\xdd\x96\xc8&\
\xdc\x8d\xbbU\xd2\xcc\xa41\xb4\xff\x9e[\xc1\xb8\xb6\xa2\ \xb5\xd4\xad\xb3f\xdc\x8d\xbbU\xd2\xec\xa51\x5c\x11=\
\xeb\x9eF<H\xed\x08\x1e\x92\xddZx\x99>\xcc\xfa\ \xdd\x81'\xdd\x92\xaez\x1a\xf1 \xfd#\x186\xbb\xb5\
\xed\xb4\xf2\x93\xd8\xd3\xc5h&\xf4\x8e\xe3h\xccdc\ \xf0\x22\x1d\xde\xfa\xed\xb4\xf4\x93\xd8\xd3\x05k&\xfd\x8e\
H\xdeo>n\x18\x8d\xa1\x14B>\x8e\xc2\xc6\xc1\xd8\ #k\xccv\xe3!\xbd\xdf\x1b\xba\xe1\xf9\x0c\xe5\x12\xf2\
\x10\x9acq\xb5&\xda\x1f\x03Ye\x19\xa1tVW\ q\x146>\x9e\x0d\xa1\xf9t\xae\xd6M\xfb\xc0\x90U\
\xd5\x12\xa4\xfa\x82F\x8d\xc6\xa0\xf6i\x14\x01\x8dV\xd4\ \x9f\x11Jg\x05V-A9\xcci\xd4h\x0c\xfa\x03\
'h\xc5Y\x80\xe4\x06eA\xc7vP?\x12\xe7n\ \x8d\x22\xa0\xd1\x92\xfa\x04-9\x0b\x90\xdc\xa0,\xe8\xd8\
\xfcu\xe1\x0cz\x00]\xf8\xdf\x1d\xfc*\x92!\xf1\xdf\ \x0ejL\xe2\xdc\x8d\xbf.\xae\xc1\x0e@\xd9o;\x01\
\xb9\xc3.\xc8u1\x0f\x06\xb6\xb2\xdf\x06\xb7)\xed\x07\ Sz\x17G\xa0\xec\x0dy\x06;w\xd8\x05\xbb.\xe8\
\xb8\xb2\xd4\x1fje'\xe1r\x84\xee\x81\xab\xea:\x83\ \xc1\x00W\xf6\xdb\x007\xa5\xfd\x00W\x96\xfaC\xad\xec\
\xc1\x0a\x93\x1f,\xf1\xdap-\x89\xfb\x01\xabM\xf5G\ $\x5c>\xb5{\xe0\xaa:\xcf`\xb0\xc2\x0b\x11\x96x\
V\x1bB:\x99\xd3\x1c\xb6\xae\x13\xea\x5c\xe5\x9c;I\ m\xb8\x96\xc4\xfd\x80\xd5\xa6\xfa#\xab\x0d!\x9d\xcci\
hu\x11\x0e\x06<\xcc\x02Wq\x1b\xec\x86\xb0\x1f\xe8\ \x0e[7\x09u\xaes\xce\x9d$\xb4\xba\x0c\x07\x03\x1e\
2\x88\xfe\x90\xef2\xb3\x0b\xcbbb\x18\x0c\xcf|\x8b\ \xde\x0c\xd7q\x1b\xec\x86\xb0\x1f\xe82\x88\xfe\x90\xef2\
VP\xab\x1a\xfd\x90\xcd\xad\xf5G\xb7p\x0c\xa2\xc6\xe9\ \xb3\x0b\xcb\xe2\xd50\x18\x9e\xf9\x16\xad\xa0V5\xfa!\
#\x15yx\xb9\xa4\xa2\x07\xe2\xf9X4\x18\xe0\xe9\x0e\ \x9b[\xeb\x8fn\xe1\x18D\x8d\xd3a\x16yx\xb1\xa0\
mP\x17\xb2~ gQ\xdcE\xd3pi\xfa:A\ \xa2\x07\xe2\xf9\xd3h0\xc0\xd7d\xd1\x86t.\xea\x07\
\x82\xa1\x00\x1e\xc4\xbb\x0d\xefBX\x8d\x94\x83\xc1{\x9d\ q\x16\xc3]\xb4\x0c\x97\xa63\x0c\x12\x0c\x0506\xef\
\x908\xdd\xa6\x0d\xe2\xb2\xbc\x1f\xcc2\x94\xfe\x18\x83J\ 6\xbc\x0b_\xf5\xa8\x1c\x0c\xdc\x9b\x84\xc4\xe96m\x08\
\x88\xa4-\xe4R\xf0\xb6\xeb\x16\xdd\xd91\xe4\x0f\xbb\xc3\ \x97\xe5\xfd`\x96\xa1\xf4\xc7\x18TB$m!\x97\x82\
\xc1{N7\xd8%\x9b6tK\xe2\x9e\x8d\xa2\xfe\xe7\ \xb7]w\xe8\xce~!\x7f\xfe\x1d\x0e\xde\x0b\xba\xc1.\
\xcc\xc7u\x09i\x07\x85L\xec\x99\xb5\xc6#\xc5\xce\xe0\ \xd9\xb4\xa1[\x12\xf7l\x13\xf5\xbfp>\xaeGH;\
\x89\xa5\x0e\x0d\x1d?q\x89\x1c\xba\x85\xfcg\xd2\x04\xcd\ (d\xa2O\xd6\xea!p0\x5c\x03\xf7\xb0\x0d\xd3\x5c\
\x92k\xcd\x156\x00\x10\x12u. \xb5\xad\x84\xce\x0f\ \xd4\x0fO\x15A\x7f<\x95\x9d\xb4\x1f\xec?n\x18\x93\
\xfe\x03Ie@\xd5\ \x9f\x9d!\x13K\x1d\x1a:~\xe2\x129\xc9\x08\xf9\x97\
\xb0\x89\x97%\xd7\x9a+l\xc0 $*\xd5Aj[\
\x09\x9d\x1d\xfc\x07\x88\x0b\xbd2\
\x00\x00\x0f\xf6\ \x00\x00\x0f\xf6\
\x89\ \x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -15755,6 +15757,42 @@ V\xd73O\xbf\xb7f\x05\xc6\x0e$\x99F@\x81\xb0\
\x05\xa6+0]\x81\xe9\x0aLW`\xba\x02\xd3\x15\x98\ \x05\xa6+0]\x81\xe9\x0aLW`\xba\x02\xd3\x15\x98\
\xef+\xf0\x7f\x11\x98Y\xc3\x8bC\xfc\xd3\x00\x00\x00\x00\ \xef+\xf0\x7f\x11\x98Y\xc3\x8bC\xfc\xd3\x00\x00\x00\x00\
IEND\xaeB`\x82\ IEND\xaeB`\x82\
\x00\x00\x02\x11\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\
\x00\x00\x00\x09pHYs\x00\x00\x00\xec\x00\x00\x00\xec\
\x01y(q\xbd\x00\x00\x00\x19tEXtSof\
tware\x00www.inksca\
pe.org\x9b\xee<\x1a\x00\x00\x01\x8eID\
ATX\x85\xd5\x96\xbfJ\xc3P\x14\xc6\x7f\xb7\x7fR\
\xe9\xd0,\x0e\x0aEm\xa9\xbe\x80\xe0\x9a\xae\x82.\xe2\
\xd6E\xc1'pp\xee,\xe8\x12pq\xb5O 8\
7\xab\xe0\x0btP\x04A\x07A\xab.ik\xaf\x83\
T\xda$m\x93\xdb\x9b\x14\xbf)9'\x9c\xef\xbb_\
\xce\xb9\x1c\x983\x847pP\xaf/\xe4\xdf\xbej\x12\
\x165\x13\xbd\xa6\xfb\xee\x95m\xdb\xeep<\xe3\xfd0\
\xff\xfe\xd9@\xb0\xe7S\xa6\x01\xbdLn\x1b\xd8\x9f(\
@\xc0\x8e\x04\xaa\x96\x05\xc0\xea\xdd\xedH\xfeqsK\
\x89\xbc\xe98\xa4\xa4\xdc\xf5\xc6}\x02$\x18\x00\x96U\
\x05 e\x9f\x8d\xe4\xd7\x8eO\x94\x05\x0cj\x0f#\xa5\
TM#|\x0e\x84\xc1G\xbb\xcd\xcb\xf3\xd3Hli\
\xb9H\xc14\x93\x11P0M%2m\x02\x82\x1c\x18\
\x87i\xceL\x17`\xe4\x90\x87G\x905\x10\x97\x17@\
\xc2\x0e\xc8\xd3sdq\xe5\xf7\xa5R\x01\x12v\xe0\x8f\
|\xe891\x07\xa2\x9ct\x1cfr@\xe7I\x95\x04\
\x8c\x9bw \xd0\x99\xa0\x5cl\x0eL*\x1a\xc5\xb5\x99\
{`\x92#\x83|\x22=\xa0\xda+\x91\x1c\xc8\x1a\x06\
\xa5\xf2:\x0f\xf7-\xba\x9dn(\x82X\x1c(\x957\
B\x91\x87A$\x07\xa2N\xc0 \xae\xdd\x01]\x130\
U\xc0\xac7a\x98\x1d\xe1\x7f\xdd\x84AS\xa0\xba\x09\
\x85\x120\xd7)p\x9c\xa66\x12%\x01M\xc7ID\
\x80o-\x17\xd0\x89\x91\xcf\xf5\x06|\x02\xfaB\x5c\xc7\
(\xc0W\xdb\xf7\x0b2=\xb7\xd6Ogo\x10\xe8\xeb\
4\x00I+\xf5\xddmh\xad\xa9\x03?B\xf6\x97\x8a\
\x078n~\x00\x00\x00\x00IEND\xaeB`\x82\
\
\x00\x00\x02w\ \x00\x00\x02w\
\x89\ \x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -16636,6 +16674,65 @@ MHo~\x11\x02i_L&OH\xfa=\xef\xb7\
\x17\xbc\xa5\xc0\x02\x05\x16(\xb0@\x81\x05\x0a,P`\ \x17\xbc\xa5\xc0\x02\x05\x16(\xb0@\x81\x05\x0a,P`\
\x81?\x8e\x05\xfe?\xf6\xe6Yf\x85Pi\xa8\x00\x00\ \x81?\x8e\x05\xfe?\xf6\xe6Yf\x85Pi\xa8\x00\x00\
\x00\x00IEND\xaeB`\x82\ \x00\x00IEND\xaeB`\x82\
\x00\x00\x03\x8f\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\
\x00\x00\x00\x09pHYs\x00\x00\x00\xec\x00\x00\x00\xec\
\x01y(q\xbd\x00\x00\x00\x19tEXtSof\
tware\x00www.inksca\
pe.org\x9b\xee<\x1a\x00\x00\x03\x0cID\
ATX\x85\xed\x97[O\x13A\x18\x86gv\xb7-\
DCb\x11%\x9eI\x9bp\x08A\x0ce\x01a\
\xbb\xdd\x1e\x11\x0c1\x0d\xf7\xea\xbd\xd1\xdf\xa1\xfe\x0dD\
C\x04\xa1\xa5\xddn\x17,\xb4\x10\x91\x10(I\x09E\
QQbM\x88\x06\xac=\xec\xcexAJ8\x15h\
\xa1r\xc3s9;\xdf\xf7>\x99\xcc\xec\xecB\x8c1\
\xc8\x06\xab\xd5\xaa\xc2\x18\xf4\x00\x00\x00\x84\xc0\xeep8\
\xe2\xd9\xf4!\xb2)\xa2i:\x1fI\xd8\xa3V\x97p\
ju\x09\x87$\xec\xa1i:?\x9b^0\xd3\x15`\
\x18\xa6P\xa5\xcc\x13.\x95\x95U\xdb\xbb\xba\xf2\x00\x00\
\xa0\xa7\xbb;\xf6myy&\x9e\x88\xb1\xa2(\xae\xe7\
L\x80\xe3\xb8\xb3$A\x8e\x5c\xb9zMs\xdfn\xcf\
#\x88\x8d\x05D\x08\x81\xd7==\xb1\xcfK\x9f\xe6e\
$\xb7\xf0<\xff\xfb\xd8\x05\xccfs1F`\xf4f\
y\xf9\xf5{\x9d\x9d\xaaTx\x0a\x84\x10x\xd3\xdb\x1b\
\x0f/,|\x84\x04h\x1a\x1a\x1aZ=6\x01\x93\xc9\
\xa4\x86\x80\xf0k\xb4\xda\xb2\xbb\x1d\x1d*\x08\xe1\x9e\xf3\
0\xc6\xe0m__|>\x14Z\xc6\x00\xd1.\x97+\
rd\x01\xa3\xd1x\x91\x00\xa4\xbf\xb2\xba\xaa\xd4b\xb3\
)\x0f\xb4\x05\x008\x07\x07\x13\xc1\x99\xd9\x15\x04d\xda\
\xedv\x7f\xcfZ\x80\xe3\xb8\xcb\x04$\x035\xb5\xb5\xe7\
\x8d&\x93\xe20\xe1)\xdc.Wrzj\xea'\xc2\
r\x03\xcf\xf3_2\x16`Y\xf6\x06E*\xfc\xb7\xeb\
\xeb\x8b\x19\xbd\x9e\xca$<\x85\xe8\xf5J\xef'&V\
%9I\x0b\x82\xb0xh\x01\xbd^\xaf\xa1(\xc5X\
SS\xf3\xb9\xc6\xe6f2\x9b\xf0\x14c>\x9f<:\
\xea\xfb%I\xc9F\xaf\xd7;\x7f\xa0@K\x0bW\xa5\
R\x12\xbe\x16\x86)\xd254d\xf5\xa2\xda\xc9x \
\x80FDq-\x9e@\xcd##\xfclZ\x01\x96e\
k\x09H\x0ds&\xe3\x99[uu\xc7\x12\x9e\xe2\xc3\
\xe4$\xe2]\xee?\x08K\xad\x82 L\xed\x12`\x18\
F\xa7\xa0\x94\x1e\x8b\xd5ZP]S\xb3\xf79;\x22\
3\xd3\xd3\xd8\xe9pD\x93R\xc2 \x8a\xe2\xf8\xa6\x00\
\xcb\xb2wH\x82r\xb6\xb5\xb7\x17TTV\xe6\x22{\
\x93\xb9`\x10\x0c\xf4\xf7Ge$Y\x04Ax\x07[\
[\x0d\x06\x8a\x84\x03\x16\x9bM\xa5\xd1js\x1a\x9eb\
>\x14\x02\xce\xc1\xc1\xb8$\xe36\xc8\xb1\x5cP\x92\xe5\
\x8a\xff\x92\xbc\x03\x8a$\xe7\xb6mB\x861\x98K/\
\x94\xbcz\xf6\xe2ya.\x02\x9f>~\xb2\xb6\xf2#\
\xd2.\x8a\xfcpj\xecXwz6\x9c\x0a\x9c\xb8@\
\xc6\x97\x8c\xdf\xef\x97'\x02\xe3\xb1\xbd\x9e54\xd2y\
:\x9d.\xa3\xbb#c\x81!\xa7\xf3\xefb8\xfc\x12\
\x028\xb9u\x1c\x03\x5c\xb7\xb6\xbef\xd7\xe9tE9\
\x15\xd8\x08#<^\xd1\xd3\xbdu\x8ca\x0c]\x00\x00\
{\xa6\xbdN|\x0f\x9c\x0a\x9c\x0a\x9c\xb8\xc0\xaec\x88\
1\x86\xd1h4m\x81,\xcbi\xbf\x96\x10B\xfb\xd7\
\xe2\xdd\xb5\xdb\x04 D_#\x91\x88\xf2\xd1\x83\x87i\
\x9b@\x08\xf3\x01\x90\x97\xf6h\xbf\x14^\x08\x17\xecW\
K\x92\x84\x82$\xf1\xb6\x1f\x95\x7f\xa3\xdbZ\x0c\xfd\x7f\
\x9e\x85\x00\x00\x00\x00IEND\xaeB`\x82\
\x00\x00\x1b\xed\ \x00\x00\x1b\xed\
\x89\ \x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -37606,6 +37703,47 @@ W\x22\xf9z\xc2Q\xafD\x96\xf5\xbcVnK\xe3x\
\x83(\xf25\x01G\xac\x0a*,ucW:\xd8\xa2\ \x83(\xf25\x01G\xac\x0a*,ucW:\xd8\xa2\
n\xaa\xfd\xc2\xff\x0f\xa9U{(\xf7\x16\xc1\xc8\x00\x00\ n\xaa\xfd\xc2\xff\x0f\xa9U{(\xf7\x16\xc1\xc8\x00\x00\
\x00\x00IEND\xaeB`\x82\ \x00\x00IEND\xaeB`\x82\
\x00\x00\x02h\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\
\x00\x00\x00\x09pHYs\x00\x00\x01X\x00\x00\x01X\
\x01\xdfo*\xe1\x00\x00\x00\x19tEXtSof\
tware\x00www.inksca\
pe.org\x9b\xee<\x1a\x00\x00\x01\xe5ID\
ATX\x85\xc5\x96=k\x15A\x14\x86\x9fW\xa3Q\
\x02\x09\x821v\xb6\x06\x0c\x08\x16\x89\xa4\x89\x10P\x8c\
\x01\xd1\xc6\xd22\xbd\xfe\x09\xc1J,,$`\xfaX\
\x89(B\x10\x1b\xb1\xb0\x13\xf1\xabM\xb8\x1a\x0b\x95+\
\x181yS\xec.\x0e\x97\xc5\xbd3\xb3\xb9\x1e8\xb0\
\xf3q\xce<{\xce\xcc\x99\x01\xb8\x01t\x01G\xea:\
0m\x9b\x1c%q\xf1J\xbf\xe5B\xa8t\x04\xb0\x03\
l\xd2,\x87\x81\xd1\xa0\xfd\x1d8o\xfbU\x1f\xb6\xb5\
R\xfdM\xa7\xcf\x90-\xb5\x19\x89}\xa9\xd4=2\x06\
<\x954\x1dk\x98\x0bp\x1f\xf8\x94\x03\x91\x0b\xb0\x0e\
\xcc\x01\x1fS!\xb2S`\xbb\x82\xf8\x90\x02\xd1\xca\x1e\
\xb0\xbd\x01\x9cK\x81hk\x13V\x10s\xc0\xfb\x18\x88\
\xa1\xccuOI\xba\xd6\xd3w\x0f\xb8\x05\x1c\x0c \xfe\
Y'\xda\xa8\x03M\xba\x09\x0c\xb7U\x07\xde&\xd8\x1c\
\x05N\xd6\x0dD\xa7\xc0\xf6\x8b2\xec3\x80\x1a\xa6_\
\x06N4\xfa$\x22\x05\x917\xdd\x93\xc0\xff\xe9\xbd,\
\xc5\xc9\x92\x0d i^\xd23I7S}$\xa7\x00\
\xb8\x04l\x95\xf6\xdb\xc0\xe8\xc0R \xe9\x22\xb0Jq\
\xde\x01\xd6l\xff\x88\xf5\x93T\x88$]\x00\x1e\x06\x8b\
w\x80\xdb\x92fz\xa6\x8e\x05\xdfS\x92\x0e\x05\xed\xae\
\xed7\x10_\x88\xa6\x80_\xc4\x17\xa3:]II\xc1\
$0\x9c`W'\xb3\xe1\x9b\xf0\xb3\xed\xe3M\x16\x92\
\x0eP\xe4~1\xe8\xfe\x02\xbc\xac\x99~\x168V~\
?\xa7x?V\xd2\x05\xeeB\xc2)\xa0\xc8\xfd\xa3\xc0\
v\x07\xb8>\xb0Bd\xfb7p\x15x\x5c\x05\x06\xb8\
#i$\xd6W\xf21\xb4\xbd\x05\x5c\x09 \xbeRl\
\xce(\xc9z\x0f\x94\x10\x0b\x92\xce\x00\xeflo\x0f\x14\
\x00y\x9dj\xfb\xdf/\xa30\x02\xe3\x92:-\xfb\
?\xd2\x0f\xc0O`\x84\x22\x1a\x13-\x03\x84\xd2\xad\xeb\
\xdcOq\x9b\xcd\xf2\xb7\xae\xb7-\x7f\x80e\xdb\x0f\xea\
\x06w\x01\xacLr\xc3\xf3\xf0\xe9\x0e\x00\x00\x00\x00I\
END\xaeB`\x82\
\x00\x00\x04\xfa\ \x00\x00\x04\xfa\
\x89\ \x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -46447,6 +46585,10 @@ qt_resource_name = b"\
\x0c\x9b\x8d'\ \x0c\x9b\x8d'\
\x00f\ \x00f\
\x00l\x00u\x00s\x00h\x00.\x00p\x00n\x00g\ \x00l\x00u\x00s\x00h\x00.\x00p\x00n\x00g\
\x00\x0a\
\x0b*\x17\xc7\
\x00k\
\x00i\x00n\x00d\x00l\x00e\x00.\x00p\x00n\x00g\
\x00\x16\ \x00\x16\
\x06\x07\xbf'\ \x06\x07\xbf'\
\x00b\ \x00b\
@@ -46456,6 +46598,10 @@ qt_resource_name = b"\
\x08\x83x\xc7\ \x08\x83x\xc7\
\x00S\ \x00S\
\x00a\x00f\x00a\x00r\x00i\x00.\x00p\x00n\x00g\ \x00a\x00f\x00a\x00r\x00i\x00.\x00p\x00n\x00g\
\x00\x0c\
\x0a</\x87\
\x00h\
\x00o\x00m\x00e\x00p\x00a\x00g\x00e\x00.\x00p\x00n\x00g\
\x00\x08\ \x00\x08\
\x06IZ\x07\ \x06IZ\x07\
\x00c\ \x00c\
@@ -46540,6 +46686,10 @@ qt_resource_name = b"\
\x0c\xcb\xdb\xc7\ \x0c\xcb\xdb\xc7\
\x00m\ \x00m\
\x00o\x00n\x00e\x00y\x002\x00.\x00p\x00n\x00g\ \x00o\x00n\x00e\x00y\x002\x00.\x00p\x00n\x00g\
\x00\x0a\
\x06\x99_\xa7\
\x00i\
\x00m\x00p\x00o\x00r\x00t\x00.\x00p\x00n\x00g\
\x00\x08\ \x00\x08\
\x09\xc5X\xc7\ \x09\xc5X\xc7\
\x00u\ \x00u\
@@ -46585,103 +46735,109 @@ qt_resource_name = b"\
qt_resource_struct = b"\ qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x02\x00\x00\x00/\x00\x00\x00\x03\ \x00\x00\x00\x00\x00\x02\x00\x00\x002\x00\x00\x00\x03\
\x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x10\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\ \x00\x00\x00\x10\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\
\x00\x00\x01r\x8a17\x84\ \x00\x00\x01r\xa2o\xfc\xf3\
\x00\x00\x03\x96\x00\x00\x00\x00\x00\x01\x00\x088\x10\ \x00\x00\x03\xce\x00\x00\x00\x00\x00\x01\x00\x08=\xdb\
\x00\x00\x01ro \xc0+\ \x00\x00\x01ro \xc0+\
\x00\x00\x00\xf0\x00\x00\x00\x00\x00\x01\x00\x01\x92\x9c\ \x00\x00\x00\xf0\x00\x00\x00\x00\x00\x01\x00\x01\x92\xbf\
\x00\x00\x01rybF\xf5\ \x00\x00\x01rybF\xf5\
\x00\x00\x04^\x00\x00\x00\x00\x00\x01\x00\x09\xe5#\ \x00\x00\x04\xb0\x00\x00\x00\x00\x00\x01\x00\x09\xedZ\
\x00\x00\x01ro-O\xd6\ \x00\x00\x01ro-O\xd6\
\x00\x00\x00\x94\x00\x00\x00\x00\x00\x01\x00\x006\xee\ \x00\x00\x00\x94\x00\x00\x00\x00\x00\x01\x00\x007\x11\
\x00\x00\x01ryefk\ \x00\x00\x01ryefk\
\x00\x00\x036\x00\x00\x00\x00\x00\x01\x00\x074\xac\ \x00\x00\x03n\x00\x00\x00\x00\x00\x01\x00\x07:w\
\x00\x00\x01ro!\xbci\ \x00\x00\x01ro!\xbci\
\x00\x00\x01\xfc\x00\x00\x00\x00\x00\x01\x00\x04(\xfa\ \x00\x00\x024\x00\x00\x00\x00\x00\x01\x00\x04.\xc5\
\x00\x00\x01r\x83\x0a\xde,\ \x00\x00\x01r\x83\x0a\xde,\
\x00\x00\x04\x94\x00\x00\x00\x00\x00\x01\x00\x0a\x1a\xb3\ \x00\x00\x04\xe6\x00\x00\x00\x00\x00\x01\x00\x0a\x22\xea\
\x00\x00\x01ro \x94\xf5\ \x00\x00\x01ro \x94\xf5\
\x00\x00\x01j\x00\x00\x00\x00\x00\x01\x00\x03N\xe1\ \x00\x00\x01j\x00\x00\x00\x00\x00\x01\x00\x03O\x04\
\x00\x00\x01rn\x81\xa0K\ \x00\x00\x01rn\x81\xa0K\
\x00\x00\x00j\x00\x00\x00\x00\x00\x01\x00\x001\xf1\ \x00\x00\x00j\x00\x00\x00\x00\x00\x01\x00\x002\x14\
\x00\x00\x01r\x83\x17T#\ \x00\x00\x01r\x83\x17T#\
\x00\x00\x03\x18\x00\x00\x00\x00\x00\x01\x00\x072\x14\ \x00\x00\x03P\x00\x00\x00\x00\x00\x01\x00\x077\xdf\
\x00\x00\x01r\x83\x18\xc72\ \x00\x00\x01r\x83\x18\xc72\
\x00\x00\x000\x00\x00\x00\x00\x00\x01\x00\x00\x05{\ \x00\x00\x000\x00\x00\x00\x00\x00\x01\x00\x00\x05\x9e\
\x00\x00\x01r\x83\x18J\xbf\ \x00\x00\x01r\x83\x18J\xbf\
\x00\x00\x01\x9a\x00\x00\x00\x00\x00\x01\x00\x03\xd6?\ \x00\x00\x01\xb4\x00\x00\x00\x00\x00\x01\x00\x03\xd8w\
\x00\x00\x01r\x83\x0b\xa39\ \x00\x00\x01r\x83\x0b\xa39\
\x00\x00\x01R\x00\x00\x00\x00\x00\x01\x00\x02\xe4\xb0\ \x00\x00\x01R\x00\x00\x00\x00\x00\x01\x00\x02\xe4\xd3\
\x00\x00\x01rn\x7f\x9a\xa2\ \x00\x00\x01rn\x7f\x9a\xa2\
\x00\x00\x02\xb2\x00\x00\x00\x00\x00\x01\x00\x06'{\ \x00\x00\x02\xea\x00\x00\x00\x00\x00\x01\x00\x06-F\
\x00\x00\x01rx\xd0\xef\xa4\ \x00\x00\x01rx\xd0\xef\xa4\
\x00\x00\x01\xe6\x00\x00\x00\x00\x00\x01\x00\x04\x0d\x09\ \x00\x00\x02\x1e\x00\x00\x00\x00\x00\x01\x00\x04\x12\xd4\
\x00\x00\x01ro&U\x86\ \x00\x00\x01ro&U\x86\
\x00\x00\x03\xac\x00\x00\x00\x00\x00\x01\x00\x08\xa8\x0d\ \x00\x00\x03\xe4\x00\x00\x00\x00\x00\x01\x00\x08\xad\xd8\
\x00\x00\x01ro'\x5c\xdd\ \x00\x00\x01ro'\x5c\xdd\
\x00\x00\x04\xe2\x00\x00\x00\x00\x00\x01\x00\x0b\x14\xb4\ \x00\x00\x044\x00\x00\x00\x00\x00\x01\x00\x09.\x90\
\x00\x00\x01r\xa2l\xb8\xf8\
\x00\x00\x054\x00\x00\x00\x00\x00\x01\x00\x0b\x1c\xeb\
\x00\x00\x01rxL\xc3L\ \x00\x00\x01rxL\xc3L\
\x00\x00\x02\x84\x00\x00\x00\x00\x00\x01\x00\x05t\xc1\ \x00\x00\x02\xbc\x00\x00\x00\x00\x00\x01\x00\x05z\x8c\
\x00\x00\x01rn\x80a\x98\ \x00\x00\x01rn\x80a\x98\
\x00\x00\x03n\x00\x00\x00\x00\x00\x01\x00\x07Q8\ \x00\x00\x03\xa6\x00\x00\x00\x00\x00\x01\x00\x07W\x03\
\x00\x00\x01ro!\x10\x8b\ \x00\x00\x01ro!\x10\x8b\
\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x016\x13\ \x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x0166\
\x00\x00\x01ro6\xcc\x14\ \x00\x00\x01ro6\xcc\x14\
\x00\x00\x03\x80\x00\x00\x00\x00\x00\x01\x00\x07\xc3\xc7\ \x00\x00\x03\xb8\x00\x00\x00\x00\x00\x01\x00\x07\xc9\x92\
\x00\x00\x01ryf\xd9E\ \x00\x00\x01ryf\xd9E\
\x00\x00\x02\xe4\x00\x00\x00\x00\x00\x01\x00\x06~\xf5\ \x00\x00\x03\x1c\x00\x00\x00\x00\x00\x01\x00\x06\x84\xc0\
\x00\x00\x01rn\x83W\xb2\ \x00\x00\x01rn\x83W\xb2\
\x00\x00\x02T\x00\x00\x00\x00\x00\x01\x00\x04\xd7\xba\ \x00\x00\x02\x8c\x00\x00\x00\x00\x00\x01\x00\x04\xdd\x85\
\x00\x00\x01rn\x80\x8e_\ \x00\x00\x01rn\x80\x8e_\
\x00\x00\x01\xcc\x00\x00\x00\x00\x00\x01\x00\x03\xd8\xba\ \x00\x00\x01\xe6\x00\x00\x00\x00\x00\x01\x00\x03\xda\xf2\
\x00\x00\x01ro'\xd9\xb1\ \x00\x00\x01ro'\xd9\xb1\
\x00\x00\x02\x9a\x00\x00\x00\x00\x00\x01\x00\x05\xc2\xcf\ \x00\x00\x02\xd2\x00\x00\x00\x00\x00\x01\x00\x05\xc8\x9a\
\x00\x00\x01ro!\x80\x9f\ \x00\x00\x01ro!\x80\x9f\
\x00\x00\x04r\x00\x00\x00\x00\x00\x01\x00\x09\xfb\xfd\ \x00\x00\x04\xc4\x00\x00\x00\x00\x00\x01\x00\x0a\x044\
\x00\x00\x01ro*{\xa5\ \x00\x00\x01ro*{\xa5\
\x00\x00\x04\xc6\x00\x00\x00\x00\x00\x01\x00\x0a\xdb\xa1\ \x00\x00\x05\x18\x00\x00\x00\x00\x00\x01\x00\x0a\xe3\xd8\
\x00\x00\x01rxA\xa0\xe6\ \x00\x00\x01rxA\xa0\xe6\
\x00\x00\x03\xfc\x00\x00\x00\x00\x00\x01\x00\x09(\xc5\ \x00\x00\x04N\x00\x00\x00\x00\x00\x01\x00\x090\xfc\
\x00\x00\x01r\x8a:\x08\x8f\ \x00\x00\x01r\x8a:\x08\x8f\
\x00\x00\x02\xc8\x00\x00\x00\x00\x00\x01\x00\x06[7\ \x00\x00\x02\x00\x00\x00\x00\x00\x00\x01\x00\x04\x0fA\
\x00\x00\x01r\xa2b\xf5\xfa\
\x00\x00\x03\x00\x00\x00\x00\x00\x00\x01\x00\x06a\x02\
\x00\x00\x01ro'\xf7\xd9\ \x00\x00\x01ro'\xf7\xd9\
\x00\x00\x03T\x00\x00\x00\x00\x00\x01\x00\x07Ly\ \x00\x00\x03\x8c\x00\x00\x00\x00\x00\x01\x00\x07RD\
\x00\x00\x01r\x8a6j\xde\ \x00\x00\x01r\x8a6j\xde\
\x00\x00\x024\x00\x00\x00\x00\x00\x01\x00\x04xf\ \x00\x00\x02l\x00\x00\x00\x00\x00\x01\x00\x04~1\
\x00\x00\x01ro+U:\ \x00\x00\x01ro+U:\
\x00\x00\x00H\x00\x00\x00\x00\x00\x01\x00\x00\x15u\ \x00\x00\x00H\x00\x00\x00\x00\x00\x01\x00\x00\x15\x98\
\x00\x00\x01ro\x22L\x9b\ \x00\x00\x01ro\x22L\x9b\
\x00\x00\x01\x1c\x00\x00\x00\x00\x00\x01\x00\x02Qr\ \x00\x00\x01\x1c\x00\x00\x00\x00\x00\x01\x00\x02Q\x95\
\x00\x00\x01rn\x80\xbaA\ \x00\x00\x01rn\x80\xbaA\
\x00\x00\x016\x00\x00\x00\x00\x00\x01\x00\x02\x9d \ \x00\x00\x01\x9a\x00\x00\x00\x00\x00\x01\x00\x03\xd6b\
\x00\x00\x01r\x9e\xb0mx\
\x00\x00\x016\x00\x00\x00\x00\x00\x01\x00\x02\x9dC\
\x00\x00\x01ro\x14<\x9f\ \x00\x00\x01ro\x14<\x9f\
\x00\x00\x03\xc8\x00\x00\x00\x00\x00\x01\x00\x08\xc8\xbc\ \x00\x00\x04\x00\x00\x00\x00\x00\x00\x01\x00\x08\xce\x87\
\x00\x00\x01ro\x14\xa8\xb6\ \x00\x00\x01ro\x14\xa8\xb6\
\x00\x00\x00\xa8\x00\x00\x00\x00\x00\x01\x00\x00\xb0:\ \x00\x00\x00\xa8\x00\x00\x00\x00\x00\x01\x00\x00\xb0]\
\x00\x00\x01ro\x1e\xc7F\ \x00\x00\x01ro\x1e\xc7F\
\x00\x00\x04.\x00\x00\x00\x00\x00\x01\x00\x09\x99\x15\ \x00\x00\x04\x80\x00\x00\x00\x00\x00\x01\x00\x09\xa1L\
\x00\x00\x01rxK\x8a\x1d\ \x00\x00\x01rxK\x8a\x1d\
\x00\x00\x02p\x00\x00\x00\x00\x00\x01\x00\x05\x22\xb0\ \x00\x00\x02\xa8\x00\x00\x00\x00\x00\x01\x00\x05({\
\x00\x00\x01ro!G\x15\ \x00\x00\x01ro!G\x15\
\x00\x00\x01\x82\x00\x00\x00\x00\x00\x01\x00\x03\xb6\x22\ \x00\x00\x01\x82\x00\x00\x00\x00\x00\x01\x00\x03\xb6E\
\x00\x00\x01ro*\xb4%\ \x00\x00\x01ro*\xb4%\
\x00\x00\x00\xd0\x00\x00\x00\x00\x00\x01\x00\x01\x82:\ \x00\x00\x00\xd0\x00\x00\x00\x00\x00\x01\x00\x01\x82]\
\x00\x00\x01r\x82\xb4i\x96\ \x00\x00\x01r\x82\xb4i\x96\
\x00\x00\x03\xe2\x00\x00\x00\x00\x00\x01\x00\x08\xd2V\ \x00\x00\x04\x1a\x00\x00\x00\x00\x00\x01\x00\x08\xd8!\
\x00\x00\x01ryaf\xee\ \x00\x00\x01ryaf\xee\
\x00\x00\x02\xfe\x00\x00\x00\x00\x00\x01\x00\x06\xcc\xbd\ \x00\x00\x036\x00\x00\x00\x00\x00\x01\x00\x06\xd2\x88\
\x00\x00\x01ryg+\xf0\ \x00\x00\x01ryg+\xf0\
\x00\x00\x04\xac\x00\x00\x00\x00\x00\x01\x00\x0ax\xc9\ \x00\x00\x04\xfe\x00\x00\x00\x00\x00\x01\x00\x0a\x81\x00\
\x00\x00\x01rx\xcbU\xa4\ \x00\x00\x01rx\xcbU\xa4\
\x00\x00\x04J\x00\x00\x00\x00\x00\x01\x00\x09\xbd\x92\ \x00\x00\x04\x9c\x00\x00\x00\x00\x00\x01\x00\x09\xc5\xc9\
\x00\x00\x01rx\xcc\xdf'\ \x00\x00\x01rx\xcc\xdf'\
\x00\x00\x02\x1c\x00\x00\x00\x00\x00\x01\x00\x04-\x92\ \x00\x00\x02T\x00\x00\x00\x00\x00\x01\x00\x043]\
\x00\x00\x01rn\x80\xf2c\ \x00\x00\x01rn\x80\xf2c\
\x00\x00\x04\x12\x00\x00\x00\x00\x00\x01\x00\x09-\xc3\ \x00\x00\x04d\x00\x00\x00\x00\x00\x01\x00\x095\xfa\
\x00\x00\x01ro!-\x86\ \x00\x00\x01ro!-\x86\
\x00\x00\x01\x08\x00\x00\x00\x00\x00\x01\x00\x01\xe3\xbb\ \x00\x00\x01\x08\x00\x00\x00\x00\x00\x01\x00\x01\xe3\xde\
\x00\x00\x01rn\x7f\xe8!\ \x00\x00\x01rn\x7f\xe8!\
" "

View File

@@ -30,7 +30,7 @@ class Ui_MainWindow(object):
self.actionimportkindle = QAction(MainWindow) self.actionimportkindle = QAction(MainWindow)
self.actionimportkindle.setObjectName(u"actionimportkindle") self.actionimportkindle.setObjectName(u"actionimportkindle")
icon1 = QIcon() icon1 = QIcon()
icon1.addFile(u":/icons/down.png", QSize(), QIcon.Normal, QIcon.Off) icon1.addFile(u":/icons/kindle.png", QSize(), QIcon.Normal, QIcon.Off)
self.actionimportkindle.setIcon(icon1) self.actionimportkindle.setIcon(icon1)
self.actionconfig = QAction(MainWindow) self.actionconfig = QAction(MainWindow)
self.actionconfig.setObjectName(u"actionconfig") self.actionconfig.setObjectName(u"actionconfig")
@@ -55,7 +55,7 @@ class Ui_MainWindow(object):
self.actionhomepage = QAction(MainWindow) self.actionhomepage = QAction(MainWindow)
self.actionhomepage.setObjectName(u"actionhomepage") self.actionhomepage.setObjectName(u"actionhomepage")
icon6 = QIcon() icon6 = QIcon()
icon6.addFile(u":/icons/home.png", QSize(), QIcon.Normal, QIcon.Off) icon6.addFile(u":/icons/web.png", QSize(), QIcon.Normal, QIcon.Off)
self.actionhomepage.setIcon(icon6) self.actionhomepage.setIcon(icon6)
self.actionabout = QAction(MainWindow) self.actionabout = QAction(MainWindow)
self.actionabout.setObjectName(u"actionabout") self.actionabout.setObjectName(u"actionabout")
@@ -67,6 +67,11 @@ class Ui_MainWindow(object):
icon8 = QIcon() icon8 = QIcon()
icon8.addFile(u":/icons/Pixadex.png", QSize(), QIcon.Normal, QIcon.Off) icon8.addFile(u":/icons/Pixadex.png", QSize(), QIcon.Normal, QIcon.Off)
self.actionsearch.setIcon(icon8) self.actionsearch.setIcon(icon8)
self.actionexport = QAction(MainWindow)
self.actionexport.setObjectName(u"actionexport")
icon9 = QIcon()
icon9.addFile(u":/icons/md2.png", QSize(), QIcon.Normal, QIcon.Off)
self.actionexport.setIcon(icon9)
self.centralwidget = QWidget(MainWindow) self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget") self.centralwidget.setObjectName(u"centralwidget")
self.gridLayout = QGridLayout(self.centralwidget) self.gridLayout = QGridLayout(self.centralwidget)
@@ -90,9 +95,9 @@ class Ui_MainWindow(object):
self.searchToolButton = QToolButton(self.centralwidget) self.searchToolButton = QToolButton(self.centralwidget)
self.searchToolButton.setObjectName(u"searchToolButton") self.searchToolButton.setObjectName(u"searchToolButton")
icon9 = QIcon() icon10 = QIcon()
icon9.addFile(u":/icons/search.jpeg", QSize(), QIcon.Normal, QIcon.Off) icon10.addFile(u":/icons/search.jpeg", QSize(), QIcon.Normal, QIcon.Off)
self.searchToolButton.setIcon(icon9) self.searchToolButton.setIcon(icon10)
self.horizontalLayout.addWidget(self.searchToolButton) self.horizontalLayout.addWidget(self.searchToolButton)
@@ -151,6 +156,7 @@ class Ui_MainWindow(object):
self.toolBar.addAction(self.actionimportkindle) self.toolBar.addAction(self.actionimportkindle)
self.toolBar.addAction(self.actionimportlocal) self.toolBar.addAction(self.actionimportlocal)
self.toolBar.addSeparator() self.toolBar.addSeparator()
self.toolBar.addAction(self.actionexport)
self.toolBar.addAction(self.actionwords) self.toolBar.addAction(self.actionwords)
self.toolBar.addAction(self.actionstatistic) self.toolBar.addAction(self.actionstatistic)
self.toolBar.addSeparator() self.toolBar.addSeparator()
@@ -203,6 +209,10 @@ class Ui_MainWindow(object):
self.actionsearch.setText(QCoreApplication.translate("MainWindow", u"search", None)) self.actionsearch.setText(QCoreApplication.translate("MainWindow", u"search", None))
#if QT_CONFIG(tooltip) #if QT_CONFIG(tooltip)
self.actionsearch.setToolTip(QCoreApplication.translate("MainWindow", u"search note", None)) self.actionsearch.setToolTip(QCoreApplication.translate("MainWindow", u"search note", None))
#endif // QT_CONFIG(tooltip)
self.actionexport.setText(QCoreApplication.translate("MainWindow", u"export", None))
#if QT_CONFIG(tooltip)
self.actionexport.setToolTip(QCoreApplication.translate("MainWindow", u"export to file", None))
#endif // QT_CONFIG(tooltip) #endif // QT_CONFIG(tooltip)
self.searchLabel.setText(QCoreApplication.translate("MainWindow", u"Search", None)) self.searchLabel.setText(QCoreApplication.translate("MainWindow", u"Search", None))
self.searchLineEdit.setPlaceholderText(QCoreApplication.translate("MainWindow", u"\u53ef\u6309\u4e66\u540d\u3001\u4f5c\u8005\u3001\u5185\u5bb9\u641c\u7d22\u7b14\u8bb0", None)) self.searchLineEdit.setPlaceholderText(QCoreApplication.translate("MainWindow", u"\u53ef\u6309\u4e66\u540d\u3001\u4f5c\u8005\u3001\u5185\u5bb9\u641c\u7d22\u7b14\u8bb0", None))

View File

@@ -128,6 +128,7 @@
<addaction name="actionimportkindle"/> <addaction name="actionimportkindle"/>
<addaction name="actionimportlocal"/> <addaction name="actionimportlocal"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionexport"/>
<addaction name="actionwords"/> <addaction name="actionwords"/>
<addaction name="actionstatistic"/> <addaction name="actionstatistic"/>
<addaction name="separator"/> <addaction name="separator"/>
@@ -153,7 +154,7 @@
<action name="actionimportkindle"> <action name="actionimportkindle">
<property name="icon"> <property name="icon">
<iconset resource="kmanapp.qrc"> <iconset resource="kmanapp.qrc">
<normaloff>:/icons/down.png</normaloff>:/icons/down.png</iconset> <normaloff>:/icons/kindle.png</normaloff>:/icons/kindle.png</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>importkindle</string> <string>importkindle</string>
@@ -213,7 +214,7 @@
<action name="actionhomepage"> <action name="actionhomepage">
<property name="icon"> <property name="icon">
<iconset resource="kmanapp.qrc"> <iconset resource="kmanapp.qrc">
<normaloff>:/icons/home.png</normaloff>:/icons/home.png</iconset> <normaloff>:/icons/web.png</normaloff>:/icons/web.png</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>homepage</string> <string>homepage</string>
@@ -246,6 +247,18 @@
<string>search note</string> <string>search note</string>
</property> </property>
</action> </action>
<action name="actionexport">
<property name="icon">
<iconset resource="kmanapp.qrc">
<normaloff>:/icons/md2.png</normaloff>:/icons/md2.png</iconset>
</property>
<property name="text">
<string>export</string>
</property>
<property name="toolTip">
<string>export to file</string>
</property>
</action>
</widget> </widget>
<resources> <resources>
<include location="kmanapp.qrc"/> <include location="kmanapp.qrc"/>

37
tsqllite.py Normal file
View File

@@ -0,0 +1,37 @@
#########################################################
## @file : tsqlite.py
## @desc : test sqlite3
## @create : 2020/06/11
## @author : Chengan
## @email : douboer@gmail.com
#########################################################
import sqlite3
dbname = 'vocab.db'
conn = sqlite3.connect(dbname)
try:
cursor = conn.cursor()
sql1 = '''select * from words;'''
sql2 = '''select * from lookups;'''
# fetch one record
cursor.execute(sql2)
one=cursor.fetchone()
print(one)
# fetch all records
for row in cursor.fetchall():
print(row)
# fetch table structure
except Exception as e:
print('sql failure {}'.format(e))
pass
finally:
conn.close()

BIN
vocab.db Executable file

Binary file not shown.