diff --git a/changelog.md b/changelog.md
index 2f401ea..69b8ff9 100644
--- a/changelog.md
+++ b/changelog.md
@@ -72,10 +72,13 @@ b['1']['2'] = {'3':1} # OK
so I need to use resouce file(.qrc) to manage resouce(icons),
and generator rcc binnay file, must kmanapp\_rc.py
command: pyside2-rcc -binary kmanapp.qrc -o kmanapp\_rc.py
+- use QTime to check kindle kindle is connected or not
# feature plan
## 20200528
- first abstract from kindle hard / local directory for different OS **done**
- add GUI use QT **done**
-- new thread to check kindle connection status
+- use thread to check kindle connection status **XXXX**
+- export function
+
diff --git a/icons/person.png b/icons/person.png
new file mode 100644
index 0000000..8e0ea24
Binary files /dev/null and b/icons/person.png differ
diff --git a/icons/user.png b/icons/user.png
new file mode 100644
index 0000000..559636c
Binary files /dev/null and b/icons/user.png differ
diff --git a/kman.py b/kman.py
index 56c7377..a97937c 100644
--- a/kman.py
+++ b/kman.py
@@ -9,10 +9,12 @@
import re
import os
+import io
import json
import time
import logging
import platform
+import subprocess
from collections import defaultdict
# data structure - use dict
@@ -123,7 +125,7 @@ class kMan:
kp = self.get_kindle_path()
if not kp:
- s2 = u'Disconnected'
+ s2 = u'Disconnected ({})'.format(CLIPPATH)
else:
with open(kp+'/system/version.txt' , 'r', encoding='utf8', errors='ignore') as f:
s2 = u'Connected ({}) version {}'.format(kp,f.read().strip())
@@ -189,8 +191,8 @@ class kMan:
# parse #1 line
aus = au.search(authinfo)
- bookname = aus.group(1)
- author = aus.group(2)
+ bookname = aus.group(1).strip()
+ author = aus.group(2).strip()
section[bookname]['author'] = author
section['bookname'] = bookname
@@ -314,6 +316,73 @@ class kMan:
return bks
+ def get_bookname_num(self,bks):
+ """ get note number of booknames
+ Args:
+ bks: books dict
+ Return: dict {bookname:num,...}
+ """
+ bksnum = defaultdict(dict)
+ nu = 0
+ for kb,vb in bks.items():
+ bksnum.setdefault(kb, 0)
+ for ks, vs in vb.copy().items():
+ if ks in ['author', 'lines']: continue
+ bksnum[kb] += 1
+ nu += 1
+
+ return [nu, bksnum]
+
+ def get_author_num(self,bks):
+ """ get note number of author
+ Args:
+ bks: books dict
+ Return: dict {bookname:num,...}
+ """
+ bksnum = defaultdict(dict)
+ nu = 0
+ for kb,vb in bks.items():
+ for ks, vs in vb.copy().items():
+ if ks in ['author', 'lines']: continue
+ au = vb['author']
+ bksnum.setdefault(au, 0)
+ bksnum[au] += 1
+ nu += 1
+
+ return [nu, bksnum]
+
+ def filter_clips(self, bks, info=None, tp=0):
+ """ filter clips
+ Args:
+ bks: books dict
+ info: filter by bookname or author information
+ tp: type to be filter
+ 0: top/root item clicked
+ 1: bookname item clicked
+ 2: author item clicked
+ """
+ nbks = defaultdict(dict)
+
+ # do not filter
+ if tp==0: nbks = bks
+
+ # do filter
+ for kb, vb in bks.items():
+ if [info, tp] in ([kb, 1], [vb['author'], 2]):
+ nbks[kb] = vb
+
+ seclist = []
+ idx = 0
+ for kb, vb in nbks.items():
+ for ks,vs in vb.items():
+ if ks in ['author', 'lines']: continue
+ tm = self.format_time(' '.join([vs['day'],vs['week'], \
+ vs['meridiem'],vs['time']]))
+ tp = '标注' if vs['type']=='HL' else '笔记'
+ seclist.append((vs['content'],tp,kb,vb['author'],vs['position'],tm))
+ idx += 1
+ return seclist
+
def add_note_to_highlight(self,bks):
""" append note content to corresponding highlight
and remove NT sections
@@ -349,7 +418,8 @@ class kMan:
'TITLE'
'AUTHOR'
'CONTENT'
- Return: search clipping content
+ Return:
+ [number of result , result dict]
"""
nbks = defaultdict(dict)
nu = 0
@@ -405,37 +475,58 @@ class kMan:
else ("ls /Volumes/Kindle" if os.name=='posix' else '')
# not test for windows & linux
- with os.popen(cmd) as s:
- r = s.read()
+ with subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, \
+ stderr=subprocess.PIPE, bufsize=-1) as s:
+ stream_stdout = io.TextIOWrapper(s.stdout, encoding='utf-8')
+ stream_stderr = io.TextIOWrapper(s.stderr, encoding='utf-8')
+ sout = str(stream_stdout.read())
+ #serr = str(stream_stderr.read())
+ #if sout: print('stdout {}'.format(sout))
+ #if serr: print('stderr {}'.format(serr))
if os.name == 'nt': # windows
- for d in r.split('\n'):
+ for d in sout.split('\n'):
if 'Kindle' in d: return d.split('\s+')[0]
elif os.name == 'posix': # mac os
- if r: return('/Volumes/Kindle')
+ if sout: return('/Volumes/Kindle')
else:
pass
+ """
+ # will print error information on stdout
+ with os.popen(cmd) as s:
+ sout = s.read()
+ if os.name == 'nt': # windows
+ for d in sout.split('\n'):
+ if 'Kindle' in d: return d.split('\s+')[0]
+ elif os.name == 'posix': # mac os
+ if sout: return('/Volumes/Kindle')
+ else:
+ pass
+ """
+
return False
- def import_clips(self, tp='local'):
+ def import_clips(self, fp=CLIPPATH):
"""import clips from local file or kindle
4 lines for each section seperated with '======='
so read 4 lines before '======='
- Args: tp: 'local' local clipping file
- 'kindle' kindle clipping file
+ Args: fp - file path
Return: 0 - want to import kindle but kindle is not connected
books dict
"""
+ # check kindle by user just call get_kindle_path()
+ """
if tp=='kindle':
kp = get_kindle_path()
if not kp: return 0
else: path = kp
else:
- path = CLIPPATH
+ path = fn
+ """
# loop to fill books dict
- with open(path, 'r', encoding='utf8', errors='ignore') as f:
+ with open(fp, 'r', encoding='utf8', errors='ignore') as f:
bks = defaultdict(dict)
secd = defaultdict(dict)
sidx = 0
@@ -490,7 +581,7 @@ class kMan:
if __name__=='__main__':
#books = defaultdict(dict)
km = kMan()
- books = km.import_clips('local')
+ books = km.import_clips()
# remove duplication
km.drop_duplicate(books)
@@ -503,6 +594,9 @@ if __name__=='__main__':
searchnote = km.search_clip(books, '巴曙松', 'ALL', 'AUTHOR')
if searchnote[0] > 0: km.format_out(searchnote[1], 'searchauthor', ft='MD')
+ print(km.get_bookname_num(books))
+ print(km.get_author_num(books))
+
# add note content to hightlight, then delete note
km.add_note_to_highlight(books)
diff --git a/kmanapp.py b/kmanapp.py
index dab4e57..aee0b7f 100644
--- a/kmanapp.py
+++ b/kmanapp.py
@@ -1,19 +1,20 @@
import sys
from time import sleep
-from threading import Thread
-import _thread
-import threading
from PySide2.QtWidgets import QApplication
from PySide2.QtWidgets import QMainWindow
+from PySide2.QtWidgets import QLabel
+from PySide2.QtWidgets import QMessageBox
+from PySide2.QtWidgets import QFileDialog
from PySide2.QtCore import (QCoreApplication, QDate, QDateTime, QMetaObject,
- QObject, QPoint, QRect, QSize, QTime, QUrl, Qt)
+ QAbstractTableModel, QObject, QPoint, QRect, QSize, QTime,
+ QUrl, Qt, QThread, Signal, QTimer)
from PySide2.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont,
QFontDatabase, QIcon, QKeySequence, QLinearGradient, QPalette, QPainter,
QPixmap, QRadialGradient, QStandardItem, QStandardItemModel)
-from PySide2.QtWidgets import *
+#from PySide2.QtWidgets import *
from mainwindow import Ui_MainWindow
from kman import *
@@ -21,38 +22,34 @@ from kman import *
# import binary resource file(kmanapp_rc.py)
import kmanapp_rc
-ONLY_TEST = 0
+ONLY_TEST = 1
class kmanWindow(QMainWindow):
"""
def __init__(self, *args, **kwargs):
super(kmanWindow, self).__init__(*args, **kwargs)
"""
+ flag = True
def __init__(self, parent=None):
super(kmanWindow, self).__init__(parent)
- self.stat_str = 'status information'
- self.search_str = ''
- self.local_fn = CLIPPATH
-
# create ui and initial it
ui = Ui_MainWindow()
ui.setupUi(self)
self.ui = ui
self.km = kMan()
- self.books = self.km.import_clips('local')
+ self.books = self.km.import_clips()
- # loop check kindle is connected or not
- # to be implement
- """
- try:
- #_thread.start_new_thread(self.check_kindle_status)
- t1 = threading.Thread(target=check_kindle_status)
- t1.start()
- except:
- print ("Error: can not start thread")
- """
+ self.add_ui_component()
+
+ self.show_status_info()
+ self.fill_treeview()
+
+ # timer to check status of kindle
+ self.timer = QTimer(self)
+ self.timer.timeout.connect(self.show_status_info)
+ self.timer.start(1000)
# connect action/toolbutton to slot functions
ui.actionimportkindle.triggered.connect(lambda: self.import_kindle(self.books))
@@ -62,18 +59,34 @@ class kmanWindow(QMainWindow):
ui.actionstatistic.triggered.connect(lambda: self.statistic())
ui.actionhomepage.triggered.connect(lambda: self.homepage())
ui.actionabout.triggered.connect(lambda: self.about())
- ui.actionflush.triggered.connect(lambda: self.flush())
+ ui.actionflush.triggered.connect(lambda: self.refresh())
ui.searchComboBox.currentIndexChanged.connect(self.search_scope_change)
ui.searchToolButton.clicked.connect(self.search_button_clicked)
- ui.treeView.clicked.connect(self.clicked_items)
-
- self.add_ui_component()
- #add_ui_component() ###! can not found this function
+ ui.treeView.clicked.connect(self.tree_item_clicked)
+ ui.tableView.clicked.connect(self.table_item_clicked)
def add_ui_component(self):
self.ui.searchComboBox.addItems(['ALL','bookname','content','author'])
+ # status bar
+ self.ui.status_label = QLabel()
+ self.ui.pe = QPalette()
+
+ # table
+ #self.ui.tablemodel=QStandardItemModel(100,5)
+ self.ui.tablemodel= nTableModel(100,5)
+ self.ui.tablemodel.setHorizontalHeaderLabels(
+ [u'Content',u'Type',u'Bookname',u'Author',u'Position',u'Date'])
+ self.ui.tablemodel.update(self.km.filter_clips(self.books))
+ self.ui.tableView.verticalHeader().hide()
+ for row in range(5):
+ for column in range(5):
+ i=QStandardItem("row %s,column %s"%(row,column))
+ self.ui.tablemodel.setItem(row,column,i)
+ self.ui.tableView.setModel(self.ui.tablemodel)
+
+ """
if not ONLY_TEST: # XXXXXXXXXXXXX
model = QStandardItemModel()
rootItem = model.invisibleRootItem()
@@ -113,11 +126,25 @@ class kmanWindow(QMainWindow):
icon.addFile(u":/icons/book_open.png", QSize(), QIcon.Normal, QIcon.Off)
item.setIcon(icon)
parentItem.appendRow(item)
+ self.ui.treeView.setModel(model)
+ """
- self.ui.treeView.setModel(model)
+ def tree_item_clicked(self, modelidx):
+ model_index_list = self.ui.treeView.selectedIndexes() # QModelIndexList
+ model_index = model_index_list[0] # QModelIndex
+ itemmodel = model_index.model() #QAbstractItemModel/QStandardItemModel
+ item = itemmodel.itemFromIndex(modelidx) #QStandardItem
- def clicked_items(self):
- print( 'call clicked_items()' )
+ print(item.text())
+
+ # filter clips
+
+ #item = self.ui.treeView.selectedIndexes()[0]
+ #print(item.model().itemFromIndex(modelidx).text())
+
+ def table_item_clicked(self, item):
+ content = item.data()
+ #print('call table_item_clicked() {}'.format(content))
def search_button_clicked(self):
print( 'call search_button_clicked()' )
@@ -129,10 +156,11 @@ class kmanWindow(QMainWindow):
#print(search_clip(self.books,s,'ALL',p[t]))
print('call search_scope_change()')
+ ## XXXX
def check_kindle_status(self):
- while True:
+ while self.flag:
self.show_status_info()
- sleep(1)
+ sleep(2)
def show_status_info(self):
""" show status information on statusbar
@@ -143,33 +171,92 @@ class kmanWindow(QMainWindow):
"""
status = self.km.status_info()
self.ui.statusbar.showMessage(status[0],0)
- clabel = QLabel(status[1])
+ self.ui.status_label.setText(status[1])
if not self.km.status:
- pe = QPalette()
- pe.setColor(QPalette.WindowText,Qt.red)
- #clabel.setAutoFillBackground(True)
- clabel.setPalette(pe)
- self.ui.statusbar.addPermanentWidget(clabel, stretch=0)
+ self.ui.pe.setColor(QPalette.WindowText,Qt.red)
+ #self.ui.status_label.setAutoFillBackground(True)
+ self.ui.status_label.setPalette(pe)
+ self.ui.statusbar.addPermanentWidget(self.ui.status_label, stretch=0)
# define slot functions
def import_kindle(self,bks):
+ fp = self.km.get_kindle_path()
+ if not fp:
+ self.messagebox(ico=2, info='\n\n kindle is not connected')
+ return 0
+
+ self.books = self.km.import_clips(self.km.get_kindle_path())
+
status = self.km.status_info()
self.show_status_info()
print(bks)
- pass
-
def import_local(self):
fn, ft = QFileDialog.getOpenFileName(self,
"choose file to import",
- './', # 起始路径
- "All Files (*);;Text Files (*.txt)") # 设置文件扩展名过滤,用双分号间隔
+ './', # start path
+ "All Files (*);;Text Files (*.txt)") # filter file type
self.fn = fn
+ self.books = self.km.import_clips(fn)
+
+ self.show_status_info()
+
#print('filename ', fn, 'filetype ', ft)
if fn == "": return False
+ def fill_treeview(self):
+ self.ui.model = QStandardItemModel()
+ rootItem = self.ui.model.invisibleRootItem()
+ item = QStandardItem('All Notes')
+ icon = QIcon()
+ icon.addFile(u":/icons/emblem_library.png", QSize(), QIcon.Normal, QIcon.Off)
+ item.setIcon(icon)
+ item.setAccessibleDescription('top')
+ rootItem.appendRow(item)
+ parent_item = item
+
+ # add bookname tree
+ [totalnum, booknum] = self.km.get_bookname_num(self.books)
+ bookname_item = QStandardItem('Bookname ({})'.format(totalnum))
+ icon = QIcon()
+ icon.addFile(u":/icons/book_open.png", QSize(), QIcon.Normal, QIcon.Off)
+ bookname_item.setIcon(icon)
+ bookname_item.setAccessibleDescription('root')
+ parent_item.appendRow(bookname_item)
+
+ if totalnum > 0:
+ for k, v in booknum.items():
+ item = QStandardItem('{} ({})'.format(k, v))
+ icon = QIcon()
+ icon.addFile(u":/icons/book_open_bookmark.png", QSize(), QIcon.Normal, QIcon.Off)
+ item.setIcon(icon)
+ item.setAccessibleDescription('leaf')
+ bookname_item.appendRow(item)
+
+ # add author tree
+ [totalnum, authnum] = self.km.get_author_num(self.books)
+ author_item = QStandardItem('Author ({})'.format(totalnum))
+ icon = QIcon()
+ icon.addFile(u":/icons/person.png", QSize(), QIcon.Normal, QIcon.Off)
+ author_item.setIcon(icon)
+ author_item.setAccessibleDescription('root')
+ parent_item.appendRow(author_item)
+ parent_item = author_item
+
+ if totalnum > 0:
+ for k, v in authnum.items():
+ item = QStandardItem('{} ({})'.format(k, v))
+ icon = QIcon()
+ icon.addFile(u":/icons/user.png", QSize(), QIcon.Normal, QIcon.Off)
+ item.setIcon(icon)
+ item.setAccessibleDescription('leaf')
+ author_item.appendRow(item)
+
+ self.ui.treeView.setModel(self.ui.model)
+ self.ui.treeView.expandAll()
+
def config(self):
print("call slot config()")
pass
@@ -187,7 +274,7 @@ class kmanWindow(QMainWindow):
pass
def about(self):
- self.messagebox('\n'+ \
+ self.messagebox(ico=1, info='\n'+ \
' kindle management tool \n\n' + \
' v1.0.4\n\n' + \
' Author: chengan\n\n' + \
@@ -196,28 +283,87 @@ class kmanWindow(QMainWindow):
print("call slot about()")
pass
- def flush(self):
- print("call slot flush()")
+ def refresh(self):
+ print("call slot refresh()")
pass
- # unify messagebox
- def messagebox(self, showinfo):
+ def messagebox(self, ico=1, info=''):
+ """ unify messagebox
+ Args: ico - QMessageBox.NoIcon 0
+ QMessageBox.Information 1
+ QMessageBox.Warning 2
+ QMessageBox.Critical 3
+ QMessageBox.Question 4
+ """
+ icons = {0:QMessageBox.NoIcon, \
+ 1:QMessageBox.Information, \
+ 2:QMessageBox.Warning, \
+ 3:QMessageBox.Critical, \
+ 4:QMessageBox.Question }
msgBox = QMessageBox()
- msgBox.setText(showinfo)
+ msgBox.setText(info)
msgBox.setInformativeText("")
- msgBox.setIcon(QMessageBox.Information)
+ msgBox.setIcon(icons[ico])
msgBox.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok)
msgBox.setBaseSize(QSize(600, 300))
r = msgBox.exec()
+ ## XXXX
+ def closeEvent(self, e):
+ # stop check thread
+ self.flag = False
+
+## thanks 勤奋的小青蛙 ^_^ - http://www.jyguagua.com/?p=2615
+#class nTableModel(QAbstractTableModel):
+class nTableModel(QStandardItemModel):
+ def __init__(self, parent=None, seclist=None, *args):
+ super(nTableModel, self).__init__()
+ self.seclist = None
+
+ def update(self, seclist):
+ self.seclist = seclist
+
+ def rowCount(self, parent):
+ if not self.seclist: return 0
+ return len(self.seclist)
+
+ def columnCount(self, parent):
+ if not self.seclist: return 0
+ return len(self.seclist[0])
+
+ def data(self, index, role):
+ if not index.isValid():
+ return None
+ elif role != Qt.DisplayRole:
+ return None
+ return self.seclist[index.row()][index.column()]
+
+ def sort(self, col, order):
+ """sort table by given column number col"""
+ self.emit(SIGNAL("layoutAboutToBeChanged()"))
+
+ self.seclist = sorted(self.seclist, key=operator.itemgetter(col))
+ if order == Qt.DescendingOrder:
+ self.seclist.reverse()
+
+ self.emit(SIGNAL("layoutChanged()"))
+
if __name__ == "__main__":
- import sys
- from PySide2.QtWidgets import QApplication, QLabel
app = QApplication(sys.argv)
kmw = kmanWindow()
kmw.resize(900, 600)
kmw.show()
+
+ # loop check kindle is connected or not
+ # BUG to be implement XXXX
+ """
+ try:
+ t = threading.Thread(target=kmw.check_kindle_status)
+ t.start()
+ except:
+ print ("Error: can not start thread")
+ """
+
app.exec_()
-
diff --git a/kmanapp.pyproject.user b/kmanapp.pyproject.user
new file mode 100644
index 0000000..3bdb45e
--- /dev/null
+++ b/kmanapp.pyproject.user
@@ -0,0 +1,175 @@
+
+
+
+
+
+ EnvironmentId
+ {fae58cf6-d2fe-464b-83cf-b657b7f31038}
+
+
+ ProjectExplorer.Project.ActiveTarget
+ 0
+
+
+ ProjectExplorer.Project.EditorSettings
+
+ true
+ false
+ true
+
+ Cpp
+
+ CppGlobal
+
+
+
+ QmlJS
+
+ QmlJSGlobal
+
+
+ 2
+ UTF-8
+ false
+ 4
+ false
+ 80
+ true
+ true
+ 1
+ true
+ false
+ 0
+ true
+ true
+ 0
+ 8
+ true
+ 1
+ true
+ true
+ true
+ false
+
+
+
+ ProjectExplorer.Project.PluginSettings
+
+
+ true
+
+
+
+ ProjectExplorer.Project.Target.0
+
+ Desktop (x86-darwin-generic-mach_o-32bit)
+ Desktop (x86-darwin-generic-mach_o-32bit)
+ {47431a9c-e34f-4b94-83d9-f5f98149f5e9}
+ -1
+ 0
+ 0
+ 0
+
+
+ 0
+ Deploy
+ Deploy
+ ProjectExplorer.BuildSteps.Deploy
+
+ 1
+ ProjectExplorer.DefaultDeployConfiguration
+
+ 1
+
+
+ dwarf
+
+ cpu-cycles
+
+
+ 250
+
+ -e
+ cpu-cycles
+ --call-graph
+ dwarf,4096
+ -F
+ 250
+
+ -F
+ true
+ 4096
+ false
+ false
+ 1000
+
+ true
+
+ false
+ false
+ false
+ false
+ true
+ 0.01
+ 10
+ true
+ kcachegrind
+ 1
+ 25
+
+ 1
+ true
+ false
+ true
+ valgrind
+
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+ 14
+
+ 2
+
+ kmanapp
+ PythonEditor.RunConfiguration./Users/mark/kman/kmanapp.py
+ /Users/mark/kman/kmanapp.py
+ {8add66da-db0d-4573-865d-9852c93e0d0e}
+ /Users/mark/kman/kmanapp.py
+
+ false
+
+ false
+ true
+ false
+ false
+ true
+
+ /Users/mark/kman
+
+ 1
+
+
+
+ ProjectExplorer.Project.TargetCount
+ 1
+
+
+ ProjectExplorer.Project.Updater.FileVersion
+ 22
+
+
+ Version
+ 22
+
+
diff --git a/kmanapp.qrc b/kmanapp.qrc
index 11a956f..fd08ffb 100644
--- a/kmanapp.qrc
+++ b/kmanapp.qrc
@@ -46,5 +46,7 @@
icons/emblem_library.png
icons/book3.png
icons/register.png
+ icons/person.png
+ icons/user.png
diff --git a/kmanapp_rc.py b/kmanapp_rc.py
index a9c6aa4..bedeb79 100644
--- a/kmanapp_rc.py
+++ b/kmanapp_rc.py
@@ -6,94 +6,96 @@
from PySide2 import QtCore
qt_resource_data = b"\
-\x00\x00\x05^\
+\x00\x00\x05w\
\x00\
-\x00\x1c\xaax\x9c\xb5Y\xbd\x8e\xdc6\x10\xee\xef)\x84\
-\xed\xb3\xda=\x5cr\x81\xc1\x93\x91\x0b\x9c\x18\x88\x0f8\
-\xe36v\x19p%\xee\x8a9I\xd4Q\x94o\xd7\x95\
-]8@\x80\x04q\x91\xd2U\x8aT\x81\x93\x22\x8d\x8b\
-\x8dc\xa0\xad\x15\x85\x17\xfb\x8a\xb3\xd0\x92\x1bT\x05\
-=\xdbA\xfdH\x9c\xfb\xf1\xd7\x853\xea\x01\xf4\xe1\x7f\
-{\xf0\xabH\xc6\xc4\x7f\xe7\x0e\xbb \xd7\xc5<\x1a\xd8\
-\xca~\x17\xdc\xa6t\x18\xe0\xca\xd2p\xa8\x95\x9d\x94\xcb\
-\x11z\x00\xae\xaa\xeb\x8c\x06+L~\xb0\xc4\xef\xc2\xb5\
-\x22\x1e\x06\xac65\x1cYm\xc8\xd2\xc9\x9c\xe5\xb0}\
-\x95R\xf7\xb2\xe0\xdcJB\xab\x8bp4\xe0a\x16\xb8\
-L\xba`7\x84\xc3@\x97A\x0c\x87|\x97\x99]X\
-\x96\x13\xc3hx\x16[t\x82Z\xd7\x18\x86lam\
-8\xba\xa5c\x105\xce\x1e\xa9\x96\x8f\x97K*\x06 \
-^\x8cE\xa3\x01\x9e\xed\xd0\x05u)\x1b\x06r\x1e\xc5\
-m4\x0d\x8ff\xaf\x13K0+\x84\x07\xf1n\xc3\xbb\
-\x10V#\xe5h\xf0^\xa5$\xc9\xb6\xe9\x82\xb8*\x1f\
-\x06\xb3\x0ce8\xc6\xa0\x12Y\xd2\x96\xe5Q\xf0\xb6\xef\
-\x16\xdd\xd91\xe4/\xb0\xe3\xc1{N7\xd8#\x9b.\
-t+\xe2\x81\x8d\xa2\xf9/\xca\xc7u\x09i\xc7\x8a\x98\
-\xd83k\x8dG\x0a\xca\xe1I\xa4\x0e\x8d\xdc \xf5\x88\
-\x1c\xba\x85\xfc\x0b\xd1\x04\xcd\x96k\xcd\x15\x08\x00\x88\x88\
-:\x17\x90\x22;\xa5\xce\xc1\x7f\xdc\x00'-\
+\x00\x1d\x01x\x9c\xb5Y\xcd\x8e\xdb6\x10\xbe\xefS\x10\
+\xbe\xd7\xb2\x17\xdbn\xb1\x90\x1dt\x8b\xb4\x01\x9a\x056\
+Xws,h\x89\xb6\xd8\x95D-Ee\xed\x9c\x92\
+C\x0a\x14h\xd1\x1cz\xcc\xa9\x87\x9e\x8a\xb4\x87^r\
+\xe8\xd34\x9b\xe41:\x22)\x89\xfa\xb5\x1b\xadn\x9a\
+\x1f\x0eg>\xce\x0c\x87\xb6}o\x13\xf8\xe8\x09\xe11\
+e\xe1l4\x1dOF\x88\x84\x0esi\xb8\x9e\x8d\xbe\
+]|\xf5\xc9\xe7\xa3{\xf3\x03;\xa1\x85\xd2\x11(\xcd\
+\x0f\x90\xed\xf88\x8e\xe7g\x98\x86\x8fi\xe8\xb2\x1b\xdb\
+R\x1c\x10\xddPwM\x04\x92\xf4l\xf4\xa8\xd0\x19\xa1\
+\x10\x07d628\xa0\x8f\xec\x88\xb3\x88p\xb1\xd5\xe2\
+5a\x01\x11|+\x85\xc8\xe6\xc4\x11\xf2\x0b\xd9\x9b\xf9\
+\xc4\xb66\x9a\xd8\xa6\xc4V\x13\xb0\xa7\xf0\xe6\xc7\xc7G\
+\xb6\xa5>\x15\xdb#t\xed\x89\xf9\xd1\x14T\xf5\xb7\xb4\
+ieFm+\xdb\xbc\xc9\x93\x1b\xe9\xe4\x82\x0a\x9fh\
+gb\xc1\x01\x9c\xf97 \xf0\x09:\xc3!^\x93\x80\
+\x84\xc2\xb6\xb4\xa4n\xb3\x02\xc7cIfP8\xb0\x96\
+c_\xe9\xe8=|\xbceI\xb1\xe0kN\xdd\x87\x92\
+\x95-Z\x17\x1c\x1d'\x15$@\x9c\xdd\xccFp\x84\
+\x0e\xf3\x93 L?\x95\xb4f\xf1\xc1)\xdb\x94-z\
+\x8c\xd3\xa7,\x14\xd8/\xd9\xd5\x963\xa2\x16\xcbC\xbc\
+$~f#&\x98;\x9eb\xe5+j\x90\x0a\xb2\x11\
+\x868G\xf4B\xae6a\xd4\xf2\x12\x98\x9a\xa5\xdc\xc8\
+}\xb4L'wxLCr\xdf\xa5\xa2\xe2t\xc6m\
+\xf7;\xf2\xb1C<\xe6\xbb\x84/ZB\xb8\xfd\xe5\xcf\
+w?\xfd\xf8\xf6\xcd\xef\xb7/\x7f\xfe\xf7\xd9\xf3\xb7\xff\
+\xbc\xfa\xf0\xec\x05|\xdc\xfe\xf0\xe2\xf6\xf5\x9bw/_\
+\xbd\xff\xfb\xb7\xf7\x7f\xfc\xfa\xe1\xf5_\x83\x87\xf9%\x0b\
+\x96\x0cN\xb9\x1cf\xcem\x0f\xd3I8\x87\x94l\x09\
+\xd1\x1a\xcc\xe1\x05c\xfei\x22\x04\x0b\xcb.\x1b\xfc\xff\
+\x9fS\xe3\xf1x/\xa4k\x06\xa9S\xda\x0f|\x07F\
+\x0c\xeer\x12\xb3\x84;\xa0r\x15@\xedG\xd1\xf8\x9a\
+;\xa6&\xb2C\xc6\x03\xec\xb3\xd5j~b\xc9e\x96\
+\x8ad\xfc}D\xd6\xb6\xb5C\xac7\xfax\x9cmK\
+\x15\xbbn\x0c\x86\xc8h\x12\xd3\xa6&Q9\x91\x8b\xc8\
+\xa7B\x10\x9e\x9f\x87\xa6\xbf;,\x9aC\x056h\x22\
+\x90;XP\x13=\x9b\x84I0\x7f$NN\x1e\xe4\
+M\xc6\xb6$\xf3\xa0-\xc4ZvpB.)\xc9/\
+\x10\x91\xd1\xc5.\x15Wb\xfa\x94\x9c3\x9f:[3\
+oRn$\xb9\xc8K\xbf\xc56\x02\xddsNV\x04\
+\xd2\xde\x1d\xa1'\x05\xf7\xfe&\xc2az\x13\x96\x12\x01\
+\x1a%$\x14\x11\x8e\x97\xde>\x06e\xe8\xa4we\xa1\
+cP\xc6\xa1\x16\x9e\x18\xc7Z?\xe9JT\x01\xde\xd0\
+ \x09.`q5,\xd3\x01u\x03\x1eM&\xa5\xcb\
+P\x07\xa0n\xc1\xe9g\xc7\xc7\xc7\x87\xd3OK\xd7\xa2\
+\xe1[\xa7WX@M-\x13A\xb2\xdb\x83`h\x8b\
+\x974\xa6K\xbf\xe4\xd8\x12\xaaw\xbe\xc2~LlK\
+~\x17Vs\x1bE\x16T\xb2z\xcf|l\xcf\x81\xc6\
+t4\xf2\xf1\x12t\xa9S\xcd\xc6\xc6\x88\xab\xf9\x88!\
+\xd0RB\xe6\x8c\xf6\xa6\xd2\x98\x92m9y\xa6Nz\
+wF\xee\x95\x92\xfb\xe4dsR\xee\xd3\x7f\xea\xf0@\
+?6/Y\x91\xd1\x03\x82S\x14q/p\xa6}\xc1\
+\xd9\xb3fkE\x9bUmQ\x98\xd5\xd2-\xa6\xd9\xc9\
+\xa4^\xb6\xd5\xba\xdd\xef\xe2(\xd1e\xd2\xb8:\xcc;\
+\xc5T\xaa\x95(\x14[\x12\x9f\xe2\xa2F%c\x09\x0c\
+\xabI\xff\x0c\xea\xce\xd0\x0e4\xa9\xf6\xec|\x14\x98\xaf\
+\x82\xf2\xb3\xa0\xfc.h{\x18\xe4X\x1e\x1e\x96\xa1,\
+^\x06\xd51\xbe#n9\xa4\x14q\x08M6\xc6Q\
+{R\xe4\xd3\x8a^V\x9aX*\x87X\xeb\xbcz\xcd\
+\x17\x9c\xe0\xcc\x9a\xece\x0b\x16-\x0a\x91\xd1\xe0\xaa\x8d\
+\xb7\xcd\xe2),\xbb\xcaL6\xb6\xf1\xba%\xd7\xc5N\
+\xdal\xb5%E\xd0 b\x5c\x5c\xc9\xf7\x92\xca\x82n\
+M\x9fAGnS\x8cI\x849\x16\x8cw[\xbaa\
+\xdc\x8d\xbbU\xd2\xcc\xa41\xb4\xff\x9e[\xc1\xb8\xb6\xa2\
+\xeb\x9eF\xcc\xfa\
+\xed\xb4\xf2\x93\xd8\xd3\xc5h&\xf4\x8e\xe3h\xccdc\
+H\xdeo>n\x18\x8d\xa1\x14B>\x8e\xc2\xc6\xc1\xd8\
+\x10\x9acq\xb5&\xda\x1f\x03Ye\x19\xa1tVW\
+\xd5\x12\xa4\xfa\x82F\x8d\xc6\xa0\xf6i\x14\x01\x8dV\xd4\
+'h\xc5Y\x80\xe4\x06eA\xc7vP?\x12\xe7n\
+\xfcu\xe1\x0cz\x00]\xf8\xdf\x1d\xfc*\x92!\xf1\xdf\
+\xb9\xc3.\xc8u1\x0f\x06\xb6\xb2\xdf\x06\xb7)\xed\x07\
+\xb8\xb2\xd4\x1fje'\xe1r\x84\xee\x81\xab\xea:\x83\
+\xc1\x0a\x93\x1f,\xf1\xdap-\x89\xfb\x01\xabM\xf5G\
+V\x1bB:\x99\xd3\x1c\xb6\xae\x13\xea\x5c\xe5\x9c;I\
+hu\x11\x0e\x06<\xcc\x02Wq\x1b\xec\x86\xb0\x1f\xe8\
+2\x88\xfe\x90\xef2\xb3\x0b\xcbbb\x18\x0c\xcf|\x8b\
+VP\xab\x1a\xfd\x90\xcd\xad\xf5G\xb7p\x0c\xa2\xc6\xe9\
+#\x15yx\xb9\xa4\xa2\x07\xe2\xf9X4\x18\xe0\xe9\x0e\
+mP\x17\xb2~ gQ\xdcE\xd3pi\xfa:A\
+\x82\xa1\x00\x1e\xc4\xbb\x0d\xefBX\x8d\x94\x83\xc1{\x9d\
+\x908\xdd\xa6\x0d\xe2\xb2\xbc\x1f\xcc2\x94\xfe\x18\x83J\
+\x88\xa4-\xe4R\xf0\xb6\xeb\x16\xdd\xd91\xe4\x0f\xbb\xc3\
+\xc1{N7\xd8%\x9b6tK\xe2\x9e\x8d\xa2\xfe\xe7\
+\xcc\xc7u\x09i\x07\x85L\xec\x99\xb5\xc6#\xc5\xce\xe0\
+\x89\xa5\x0e\x0d\x1d?q\x89\x1c\xba\x85\xfcg\xd2\x04\xcd\
+\x92k\xcd\x156\x00\x10\x12u. \xb5\xad\x84\xce\x0f\
+\xfe\x03Ie@\xd5\
\x00\x00\x0f\xf6\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -29968,6 +29970,84 @@ w\x0b\xbe\x86\xd8\xb8\xeb\x81L\x83\xc8\x9cP\x80\xe9<\
\x02\xbf&;^~3\xb09K\xae\xa5$\xcaR\xec\
\xec1\xfc\x7f\x89E]\xa3\xc9\xb4-\xf9\x00\x00\x00\x00\
IEND\xaeB`\x82\
+\x00\x00\x04\xbb\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
+\x00\x00\x04\x82IDATx\xda\xedV}L\x95e\
+\x14?\xcf\xfbu\xe1~\xc4\xbdH\x08\x04\x13\xe7\x1d\x5c\
+\xb4\x11e\x94\xa3\xa4\xcd\xa6\x92\xc9\xac\xd5\x1fmQ\xab\
+\xa5l\xc6\xc8\xd6\xea\x16E\xa9\xfdS\xd9\x96\xc3\xd9\xd6\
+\x07F\x06\xd4T6\x1c\x023\xa5@7L\x92\x85+\
+q0@\x91\xb05\x09\x10\x02\xee\xbd\xef\xc7\xf3t\x1e\
+>op\xa1^Z\xab?8\xdb\xd9{\xde\xf3\x9c\xe7\
+\x9c\xdf{\x9es\xce\xf3\x12\xf8\x8f\x89,\x01X\x02\xb0\
+\x04\xe0\xaf\x0cX#$\x80\x08\x1bQ\x94\xc0\x80Zr\
+\x1f\xf4.d\xff\xd9\x95$\x22\x10\xb2^\x16\x94T\x9d\
+\xea\xd7)\xd0\x13\xcf\xael\xd7\x17\x05\x80\x9e\x83\xd7\xd1\
+`\x0fh \x03C\x85\x02\xa3\xaa\x11\x96mY\xef\xaf\
+\xdfu\xb2[@M$\xf2@QV\x22\xe5\xf6\xc5]\
+\xeehYT*\x0d\xaae\xf8\x0d? \x08d\xf9\xbc\
+F\xf5\x0d;Vu\xfaL\x010\xce\xc06\xc2\xe08\
+\xcc\xc2>*;\xdb\x8b\xd4\x9a\x13\xfd,\xfe9`\xcc\
+E\x08\xe9\xd3)\xf5\x1e\xc8Z\xf9yqWr\xad\xcf\
+\x18y\x8816m/\x10\x11\x14\xc1\xf2R\xae\xbbs\
+\xbf)\x00\xda7P\x85\xc1\xb3\xfft\x1c\x8c\xc0\xc7P\
+\x06-\xbet\xd0\xe8L\x10\x9b\x22RWD\xf5\x13\xf1\
+1EGt\xaa\xce\xf1)\x09\xca\xe9\xbc\xa4\xab\x9bL\
+\x01\x08\xd4\xc2O\x98\xf6\xdb\x83u?\xd2\x07\xe1\xa0z\
+\x104\x9d\xce\xb1\x8fv]\xecLL(p36w\
+\x0d\xb3pyW\xca\xb55\xa6\x00\xf8\x8e\xc3\xf7\xb8\x9a\
+\x1e\xac;fx\xa1\xc6\x9f\x13\xd2\xdea\xfb\x99y\xdc\
+;\xe7\xf3w\xf9\xe55=\xe6\x00\x8cT\xc0a\x5c}\
+:XW\xa2\xbd\x05\x0d\xda#!\xed\x15yd\xf0\xce\
+\xd5O\xd9Q\x94C,\x9f\xf2\xa6^\xdbl\x0a\xc0\xf0\
+\x91\xf1\xf3\xaf\x0a\xd6\x1d\xd5\xf2\xa0Z\x7f&\xa4\xbd \
+\xf8\xdb\xef\xf0\xbc\xd8\xa1(\x03[\xe7\xae\xb2\xd7\x0a\xd2\
+\xba\xdf3\x05\xe0\xe6\x97@\xb0\x98\xebP\xdc0\xa5k\
+6\x1e\x80\x03\xda\xbb\xa1\x01\x10\xb5jU\xe2\x87\x85\x91\
+\x11\xcdM\xf8\x1a\x1e\xb4\xd4\x87\x00R\x0a\xd7^\xed7\
+\x05\x80S\xff\x17\xe0\xc4BlE1n\xdc\x13\x8b9\
+\xe9U\xcb\xd3p,\xc4\x84p\xb4\xa367\xb3x\xef\
+\x85\xa4\x1c,\xc4\xd2i=!\xd9\xbb\xd3;\xaa\xe7\x8b\
+\xb1 \x80\xbeRH\xc5Y\xd0@5p\xf1w\xbfl\
+=V\xaa\xe6W\x9cQ\xb3\xbe\xe2\x1f=\x13\xc4\xb8\x14\
+\xe5j\xbb;yE%U\xe4\xa1\x821}d/\xef\
+\x06\x81H|\x18m.\xbc\xe7\xe2\xa9\xbf\x0d\xe0\xd7\xc3\
+\xb8G\x80Gq~<\x8f>2\xe9\xe8\x8c\x0d\xc1\xf2\
+\xd2-JG\x89/?\xac\xde\x97\x950\xa5\x8f\xbd\xb5\
+y\x8bg\xc5\xd9u\x924\xb4=\xa0\xfb\xe3\xfc\xfa\xcc\
+\xd0\xb3+\x0e]\x16-\xdf\xe2\xee\xea\x80\xaeU\xbe\xb9\
+\xee\xbb\xde\x90\x00~9\x04\x0eA\x86\xf7\xdes\xcf\xb9W\xa8\
+R\xcd\xfd\xda`,\xba\x11v\x01O\x03m(\xcd\xb1\
+\xeb\x8e\x07L\xa2\xdcD\x18\x10\xf4\xa4gY}W^\
+\x96\xa9j\xf2J\xa5\x80\xd8\x80&\xd5\xe3\x80\x08\xaf\x00\
+\x91\x22\xff5\xa7\xd4a3*\x1c1x\x87.\xf7\xd4\
+\x0e/\x09 \xf1\x8b\x863\x11\xdeGy\x13\x08\xfa\x02\
+\x96\x06\xc8\xcb\x11\xd1\xc3\xb3S\xd6\xc1\x9b\xafI\xa6j\
+\x80xJ\xd7\x01\xdf*l\xf2\xcbl\xcd(\xd1q\x07\
+kl\x02\xaf\xb1\x0e\x0dY\xfe\x18J\xbf\x04j\xf6\x5c\
+\xde+\x7fU\x04\x88\x0f\xe8\x16UN\x00\xf1R\xb9\x9a\
+\xfftH\x1e\x9f!>\x9cE\x14\x86\xdc>T\x047\
+\xd1Bf{'nK\xcc\x87BFU\xbc\xee+=\
+\xa1K\xbe\x00\xf1\x94\xaeS8\xebW\xbc\xfd\xcc,\x1b\
+\xbf\x9bF\xf4\xb1m\xc8\xed{|\xa2\x22dvt\x91\
+\xed\xec\xf0\x83\x18\xf3\xd4y\xe6jo\xe4v\xdeb\xf2\
+?\xdaNi\xad\xc2W~\xc5[\x07m6-*^\
+\x94^\x95\xf0\xe9\x0b\xd4\xdc\x18\xf5\x89\xd0\x15F\x82G\
+\x13_j\xb8\x08`\xba\x89\x0f\xf8\xf7\xf1*\x92qa\
+\xe3\xf7i(S\xfcq\x0d\xa8=}\x01\x5c\xcf/\xa2\
+\xab\xced\xdfZ\x00\x10\x1b\xd0\xe4\x5c\xb7\x97Tl8\
+Kd\xc2\xad\xa2\xfa\x5c\xd2\xa94\xc1\xdbw\xcb@\xca\
+\xfe\xf5\xdfh\xeb<\x80z\x1c\xa0\xcc\xa3\xb6\xecV\xae\
+\xea\xe2y\x05\xc6\x1f\x94sGQ\xe7\x1d\x00\xd3\xdc\xaf\
+\x0dsC\xc6?Y\xb6\x9ak\xbfP\x92-\x0f\xad\xca\
+\xbe\x8e\xa3Zo\x8cE7%&\x5c\xa12\x0d\xa6\x9c\
+\xbbt\x81h\xb8RH\xd4\xd8\xd9\xddfn\xb6\x97\xd5\
+\xfdd\x99!\xe3\xa3\xdc\x93\xad\x15c\xd4\xc8.\x83O\
+\xe7\x17jjy\x80\xbb\x1b\xaa\x87p\xd6\xac\xc4mj\
+\xa8\x1c\xe8\xd1i\x805\xd5$\xbd\xd4S_\xd5\xad\xd0\
+h\x98\xcc\xceg\xabI\x09B\xbb\x01\xaa@\x85\xd9e\
+\x86\xb3o,\xe3\xd1\x0a\xdf\x87\x05/\xd6D\xba\xe7E\
+\xbc\xca\xf7?\xafF\x89\xa5\xd4\x06\xaa\xbe\xbe\xa2\xb02\
+e\xd3:hS?j3\xe2\x9e\xc5mj\xc0yj\
+\x15\xb9u\xabQ\xa9\xb8\xe1\x0beK,\xa5\xf7\x00\xbf\
+\x0dRQ\x15\xd6q%\x8d\x07\x81\x91r\x00\xc6\x85\xe8\
+}\x97\xd0\x94\x87x\x8b\xe6\x81\xab\x04\xee\x94\x98x\xc6\
+\xa0\x910nc\x1d\x982}\xa3\xdc\x08\x02\x03\xc0\xb6\
+\xc5>kFI\xfe\x98&\xf1\xab\x8d5\xe3;\xd7\x19\
+rO\xfa\xe7\xaf\x0d\x91]\xdfNf\xdb&(\xf5\xbe\
+ \xfcnP\x8a2\xd4\x8d\xbb\xec\xf8\xe8!\xed?\xcf\
+\x96-^I\x92\xb1\x09\xa5.S\x7f\xe4\x18f\xe2Q\
+1 \xfa\x93\x91\x10?\x00\xe9BG\xd7\xa7\x8f\x88L\
+.\xbd\xf0b\x99\xa94\x91\x13g\x16\x9b\xd3A\xb5\x8e\
+\x9b{\x1be\x1a\xe1\x8bB\xcf\xf5\xe7\xcbN\xe6%\xc9\
+\xde\xbaa\xb1\xe9\xf3\xa1^\x996\x00\xa2\x1c\x02\xe6\xdb\
+y\xb4+\xc4\xd8\xe6\xd0\xffV\xdcI\xb6\xe1$\xdb\x0a\
+MY\xcfx\x1f\xc2\xdc:\xbe\xb7E\x86\x81\xc3\x85\x11\
+\x17_\xad\xe7A{\xcd\x7f.\x9eK,g\xf6\x85\xed\
+\x0b\x8d\xa2\x9f\x5c\xdd[{c\x1e\x00\xa0n\x92\x83\x22\
+\xf4\xe7\xff{5\xc2\xb9\xd7\x1b\xb9\xbde\xe9W\x22\xdb\
+\xd1\xc6\xccK;\xd1@\xa0\xd0|.\x17\xb1\xde\x9dg\
+)\xf4\xc4\xcfk\x8b\x069\x8f\xb0\xaa\xd0\x9e\xb8h\xb3\
+\xbeo\x9a\xf0Dqc\x16\xbe\x94\xe6\xa5\xf5\x11f\x9f\
+\xdb\x8a\x93\x5c\xbd\xc8S\xfcRZ47\x9f\xf8M;\
+=C\x1f\x90(\xb4\x9b\x9c\x92H\xd9\xacH\xd94\x8f\
+8\x04m]\x00\xa0V\x0d\xb9\xd68NG\x1b\xce\xda\
+\xd5\x10X8\x80T\xb8%\x9ev\xff\xd1\x1b\x1a\x5c\x80\
+T\x84\x0f\xb4\x5c\xd4xN\xf8\x1a\xd8Q\xca/\x0a\xe1\
+\x87.\xa1\xbfsXc\x0f\xd1p-^C\x14\xfc\xf7\
+\xc0\xb9\x80\xd6\xec\x19\xea\x95;E\xb9\xfc\x8eX;\xac\
+\xa1\xc94o\xa3\xec\x07\xa2~q\x15vA\x16\xf4\xe3\
+\x5c\xd4z\xef\xdan\xb1K\x9eL\xb9\xa3a\xae/j\
+8\x08\xec+\x05\xe2\x03\x90F\xf8\xcc\x13\xefP\xbe\xdb\
+\xfdT\xf5\xee\x8c\x0fi\x1d\x0e\xdd\xea\xb1\x13a3\xb0\
+f\xc1\xe79\x8c\x08\xa4<\xd1SA\xcf:6\xd4+\
+\xd3\xd5\xe4\xfd\x07O\xc0\xaf\xb9s\x8fj\x8f\x00\x00\x00\
+\x00IEND\xaeB`\x82\
\x00\x00kN\
\x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -46350,6 +46512,10 @@ qt_resource_name = b"\
\x03v\xc2\x07\
\x00q\
\x00u\x00e\x00s\x00t\x00i\x00o\x00n\x00.\x00p\x00n\x00g\
+\x00\x0a\
+\x0ao\x8eg\
+\x00p\
+\x00e\x00r\x00s\x00o\x00n\x00.\x00p\x00n\x00g\
\x00\x06\
\x07\x84WG\
\x00q\
@@ -46374,6 +46540,10 @@ qt_resource_name = b"\
\x0c\xcb\xdb\xc7\
\x00m\
\x00o\x00n\x00e\x00y\x002\x00.\x00p\x00n\x00g\
+\x00\x08\
+\x09\xc5X\xc7\
+\x00u\
+\x00s\x00e\x00r\x00.\x00p\x00n\x00g\
\x00\x0b\
\x0e|&g\
\x00w\
@@ -46415,99 +46585,103 @@ qt_resource_name = b"\
qt_resource_struct = b"\
\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\x02\x00\x00\x00-\x00\x00\x00\x03\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00/\x00\x00\x00\x03\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x10\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x01r\x82\xd3\xbeg\
-\x00\x00\x03|\x00\x00\x00\x00\x00\x01\x00\x0838\
+\x00\x00\x01r\x8a17\x84\
+\x00\x00\x03\x96\x00\x00\x00\x00\x00\x01\x00\x088\x10\
\x00\x00\x01ro \xc0+\
-\x00\x00\x00\xf0\x00\x00\x00\x00\x00\x01\x00\x01\x92\x83\
+\x00\x00\x00\xf0\x00\x00\x00\x00\x00\x01\x00\x01\x92\x9c\
\x00\x00\x01rybF\xf5\
-\x00\x00\x04.\x00\x00\x00\x00\x00\x01\x00\x09\xdbM\
+\x00\x00\x04^\x00\x00\x00\x00\x00\x01\x00\x09\xe5#\
\x00\x00\x01ro-O\xd6\
-\x00\x00\x00\x94\x00\x00\x00\x00\x00\x01\x00\x006\xd5\
+\x00\x00\x00\x94\x00\x00\x00\x00\x00\x01\x00\x006\xee\
\x00\x00\x01ryefk\
-\x00\x00\x036\x00\x00\x00\x00\x00\x01\x00\x074\x93\
+\x00\x00\x036\x00\x00\x00\x00\x00\x01\x00\x074\xac\
\x00\x00\x01ro!\xbci\
-\x00\x00\x01\xfc\x00\x00\x00\x00\x00\x01\x00\x04(\xe1\
+\x00\x00\x01\xfc\x00\x00\x00\x00\x00\x01\x00\x04(\xfa\
\x00\x00\x01r\x83\x0a\xde,\
-\x00\x00\x04d\x00\x00\x00\x00\x00\x01\x00\x0a\x10\xdd\
+\x00\x00\x04\x94\x00\x00\x00\x00\x00\x01\x00\x0a\x1a\xb3\
\x00\x00\x01ro \x94\xf5\
-\x00\x00\x01j\x00\x00\x00\x00\x00\x01\x00\x03N\xc8\
+\x00\x00\x01j\x00\x00\x00\x00\x00\x01\x00\x03N\xe1\
\x00\x00\x01rn\x81\xa0K\
-\x00\x00\x00j\x00\x00\x00\x00\x00\x01\x00\x001\xd8\
+\x00\x00\x00j\x00\x00\x00\x00\x00\x01\x00\x001\xf1\
\x00\x00\x01r\x83\x17T#\
-\x00\x00\x03\x18\x00\x00\x00\x00\x00\x01\x00\x071\xfb\
+\x00\x00\x03\x18\x00\x00\x00\x00\x00\x01\x00\x072\x14\
\x00\x00\x01r\x83\x18\xc72\
-\x00\x00\x000\x00\x00\x00\x00\x00\x01\x00\x00\x05b\
+\x00\x00\x000\x00\x00\x00\x00\x00\x01\x00\x00\x05{\
\x00\x00\x01r\x83\x18J\xbf\
-\x00\x00\x01\x9a\x00\x00\x00\x00\x00\x01\x00\x03\xd6&\
+\x00\x00\x01\x9a\x00\x00\x00\x00\x00\x01\x00\x03\xd6?\
\x00\x00\x01r\x83\x0b\xa39\
-\x00\x00\x01R\x00\x00\x00\x00\x00\x01\x00\x02\xe4\x97\
+\x00\x00\x01R\x00\x00\x00\x00\x00\x01\x00\x02\xe4\xb0\
\x00\x00\x01rn\x7f\x9a\xa2\
-\x00\x00\x02\xb2\x00\x00\x00\x00\x00\x01\x00\x06'b\
+\x00\x00\x02\xb2\x00\x00\x00\x00\x00\x01\x00\x06'{\
\x00\x00\x01rx\xd0\xef\xa4\
-\x00\x00\x01\xe6\x00\x00\x00\x00\x00\x01\x00\x04\x0c\xf0\
+\x00\x00\x01\xe6\x00\x00\x00\x00\x00\x01\x00\x04\x0d\x09\
\x00\x00\x01ro&U\x86\
-\x00\x00\x03\x92\x00\x00\x00\x00\x00\x01\x00\x08\xa35\
+\x00\x00\x03\xac\x00\x00\x00\x00\x00\x01\x00\x08\xa8\x0d\
\x00\x00\x01ro'\x5c\xdd\
-\x00\x00\x04\xb2\x00\x00\x00\x00\x00\x01\x00\x0b\x0a\xde\
+\x00\x00\x04\xe2\x00\x00\x00\x00\x00\x01\x00\x0b\x14\xb4\
\x00\x00\x01rxL\xc3L\
-\x00\x00\x02\x84\x00\x00\x00\x00\x00\x01\x00\x05t\xa8\
+\x00\x00\x02\x84\x00\x00\x00\x00\x00\x01\x00\x05t\xc1\
\x00\x00\x01rn\x80a\x98\
-\x00\x00\x03T\x00\x00\x00\x00\x00\x01\x00\x07L`\
+\x00\x00\x03n\x00\x00\x00\x00\x00\x01\x00\x07Q8\
\x00\x00\x01ro!\x10\x8b\
-\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x015\xfa\
+\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x016\x13\
\x00\x00\x01ro6\xcc\x14\
-\x00\x00\x03f\x00\x00\x00\x00\x00\x01\x00\x07\xbe\xef\
+\x00\x00\x03\x80\x00\x00\x00\x00\x00\x01\x00\x07\xc3\xc7\
\x00\x00\x01ryf\xd9E\
-\x00\x00\x02\xe4\x00\x00\x00\x00\x00\x01\x00\x06~\xdc\
+\x00\x00\x02\xe4\x00\x00\x00\x00\x00\x01\x00\x06~\xf5\
\x00\x00\x01rn\x83W\xb2\
-\x00\x00\x02T\x00\x00\x00\x00\x00\x01\x00\x04\xd7\xa1\
+\x00\x00\x02T\x00\x00\x00\x00\x00\x01\x00\x04\xd7\xba\
\x00\x00\x01rn\x80\x8e_\
-\x00\x00\x01\xcc\x00\x00\x00\x00\x00\x01\x00\x03\xd8\xa1\
+\x00\x00\x01\xcc\x00\x00\x00\x00\x00\x01\x00\x03\xd8\xba\
\x00\x00\x01ro'\xd9\xb1\
-\x00\x00\x02\x9a\x00\x00\x00\x00\x00\x01\x00\x05\xc2\xb6\
+\x00\x00\x02\x9a\x00\x00\x00\x00\x00\x01\x00\x05\xc2\xcf\
\x00\x00\x01ro!\x80\x9f\
-\x00\x00\x04B\x00\x00\x00\x00\x00\x01\x00\x09\xf2'\
+\x00\x00\x04r\x00\x00\x00\x00\x00\x01\x00\x09\xfb\xfd\
\x00\x00\x01ro*{\xa5\
-\x00\x00\x04\x96\x00\x00\x00\x00\x00\x01\x00\x0a\xd1\xcb\
+\x00\x00\x04\xc6\x00\x00\x00\x00\x00\x01\x00\x0a\xdb\xa1\
\x00\x00\x01rxA\xa0\xe6\
-\x00\x00\x02\xc8\x00\x00\x00\x00\x00\x01\x00\x06[\x1e\
+\x00\x00\x03\xfc\x00\x00\x00\x00\x00\x01\x00\x09(\xc5\
+\x00\x00\x01r\x8a:\x08\x8f\
+\x00\x00\x02\xc8\x00\x00\x00\x00\x00\x01\x00\x06[7\
\x00\x00\x01ro'\xf7\xd9\
-\x00\x00\x024\x00\x00\x00\x00\x00\x01\x00\x04xM\
+\x00\x00\x03T\x00\x00\x00\x00\x00\x01\x00\x07Ly\
+\x00\x00\x01r\x8a6j\xde\
+\x00\x00\x024\x00\x00\x00\x00\x00\x01\x00\x04xf\
\x00\x00\x01ro+U:\
-\x00\x00\x00H\x00\x00\x00\x00\x00\x01\x00\x00\x15\x5c\
+\x00\x00\x00H\x00\x00\x00\x00\x00\x01\x00\x00\x15u\
\x00\x00\x01ro\x22L\x9b\
-\x00\x00\x01\x1c\x00\x00\x00\x00\x00\x01\x00\x02QY\
+\x00\x00\x01\x1c\x00\x00\x00\x00\x00\x01\x00\x02Qr\
\x00\x00\x01rn\x80\xbaA\
-\x00\x00\x016\x00\x00\x00\x00\x00\x01\x00\x02\x9d\x07\
+\x00\x00\x016\x00\x00\x00\x00\x00\x01\x00\x02\x9d \
\x00\x00\x01ro\x14<\x9f\
-\x00\x00\x03\xae\x00\x00\x00\x00\x00\x01\x00\x08\xc3\xe4\
+\x00\x00\x03\xc8\x00\x00\x00\x00\x00\x01\x00\x08\xc8\xbc\
\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\x03\xfe\x00\x00\x00\x00\x00\x01\x00\x09\x8f?\
+\x00\x00\x04.\x00\x00\x00\x00\x00\x01\x00\x09\x99\x15\
\x00\x00\x01rxK\x8a\x1d\
-\x00\x00\x02p\x00\x00\x00\x00\x00\x01\x00\x05\x22\x97\
+\x00\x00\x02p\x00\x00\x00\x00\x00\x01\x00\x05\x22\xb0\
\x00\x00\x01ro!G\x15\
-\x00\x00\x01\x82\x00\x00\x00\x00\x00\x01\x00\x03\xb6\x09\
+\x00\x00\x01\x82\x00\x00\x00\x00\x00\x01\x00\x03\xb6\x22\
\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\x03\xc8\x00\x00\x00\x00\x00\x01\x00\x08\xcd~\
+\x00\x00\x03\xe2\x00\x00\x00\x00\x00\x01\x00\x08\xd2V\
\x00\x00\x01ryaf\xee\
-\x00\x00\x02\xfe\x00\x00\x00\x00\x00\x01\x00\x06\xcc\xa4\
+\x00\x00\x02\xfe\x00\x00\x00\x00\x00\x01\x00\x06\xcc\xbd\
\x00\x00\x01ryg+\xf0\
-\x00\x00\x04|\x00\x00\x00\x00\x00\x01\x00\x0an\xf3\
+\x00\x00\x04\xac\x00\x00\x00\x00\x00\x01\x00\x0ax\xc9\
\x00\x00\x01rx\xcbU\xa4\
-\x00\x00\x04\x1a\x00\x00\x00\x00\x00\x01\x00\x09\xb3\xbc\
+\x00\x00\x04J\x00\x00\x00\x00\x00\x01\x00\x09\xbd\x92\
\x00\x00\x01rx\xcc\xdf'\
-\x00\x00\x02\x1c\x00\x00\x00\x00\x00\x01\x00\x04-y\
+\x00\x00\x02\x1c\x00\x00\x00\x00\x00\x01\x00\x04-\x92\
\x00\x00\x01rn\x80\xf2c\
-\x00\x00\x03\xe2\x00\x00\x00\x00\x00\x01\x00\x09#\xed\
+\x00\x00\x04\x12\x00\x00\x00\x00\x00\x01\x00\x09-\xc3\
\x00\x00\x01ro!-\x86\
-\x00\x00\x01\x08\x00\x00\x00\x00\x00\x01\x00\x01\xe3\xa2\
+\x00\x00\x01\x08\x00\x00\x00\x00\x00\x01\x00\x01\xe3\xbb\
\x00\x00\x01rn\x7f\xe8!\
"
diff --git a/mainwindow.py b/mainwindow.py
index eeb1fb5..874e7fb 100644
--- a/mainwindow.py
+++ b/mainwindow.py
@@ -111,6 +111,7 @@ class Ui_MainWindow(object):
self.treeView.setSizePolicy(sizePolicy)
self.treeView.setMaximumSize(QSize(400, 16777215))
self.splitter_2.addWidget(self.treeView)
+ self.treeView.header().setVisible(False)
self.splitter = QSplitter(self.splitter_2)
self.splitter.setObjectName(u"splitter")
self.splitter.setOrientation(Qt.Vertical)
diff --git a/mainwindow.ui b/mainwindow.ui
index 36af336..207f5b3 100644
--- a/mainwindow.ui
+++ b/mainwindow.ui
@@ -69,6 +69,9 @@
16777215
+
+ false
+
diff --git a/searchtitle.md b/searchtitle.md
index 3727ac9..c07cfea 100644
--- a/searchtitle.md
+++ b/searchtitle.md
@@ -1,6 +1,5 @@
TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT
--|--|--|--|--
-HL|薛兆丰经济学讲义 |薛兆丰|2020/1/13 8:11:05|么到底什么叫边际?边际就是“新增”带来的“新增”。 例如,边际成本就是每新增一个单位产品所需要付出的新增成本;边际收入是每多卖一个产品能够带来的新增收入;边际产量是每新增一份投入所带来的新增产量;边际效用是每消耗一个单位的商品所能带来的新增享受。
-HL|薛兆丰经济学讲义 |薛兆丰|2020/1/30 10:23:58|一个国家很大,贫富有差距,并非每个学校和家长都能负担得起这样标准的校车。标准太高,就会逼着很多学校,尤其是农村的学校放弃提供校车,家长们就只能使用安全性能更低的交通工具,比如自己骑自行车或雇用黑车等,结果是孩子们享受到的安全保障反而降低了。
-NT|薛兆丰经济学讲义 |薛兆丰|2020/1/30 10:26:31|山寨 假货 问题
-HL|薛兆丰经济学讲义 |薛兆丰|2020/1/30 10:29:41|为了克服信息不对称,建立互信,人类社会构想出了各种各样有趣的解决方案,从重复交易到第三方背书,从质保、延保,再到收益共享。此外,还有三种非常接近的建立信任的办法:付出沉没成本、给出人质或者给出抵押。
+HL|薛兆丰经济学讲义|薛兆丰|2020/1/13 8:11:05|么到底什么叫边际?边际就是“新增”带来的“新增”。 例如,边际成本就是每新增一个单位产品所需要付出的新增成本;边际收入是每多卖一个产品能够带来的新增收入;边际产量是每新增一份投入所带来的新增产量;边际效用是每消耗一个单位的商品所能带来的新增享受。
+HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:23:58|一个国家很大,贫富有差距,并非每个学校和家长都能负担得起这样标准的校车。标准太高,就会逼着很多学校,尤其是农村的学校放弃提供校车,家长们就只能使用安全性能更低的交通工具,比如自己骑自行车或雇用黑车等,结果是孩子们享受到的安全保障反而降低了。
+NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:26:31|山寨 假货 问题
diff --git a/tclip.txt b/tclip.txt
index 9a8dad3..1e9d0b1 100755
--- a/tclip.txt
+++ b/tclip.txt
@@ -18,8 +18,13 @@
山寨 假货 问题
==========
-薛兆丰经济学讲义 (薛兆丰)
-- 您在位置 #4382-4384的标注 | 添加于 2020年1月30日星期四 上午10:29:41
+庆余年(精校版) (猫腻)
+- 您在位置 #48484-48484的标注 | 添加于 2020年1月19日星期日 下午8:00:29
-为了克服信息不对称,建立互信,人类社会构想出了各种各样有趣的解决方案,从重复交易到第三方背书,从质保、延保,再到收益共享。此外,还有三种非常接近的建立信任的办法:付出沉没成本、给出人质或者给出抵押。
+园子里的护卫能掺多少人就掺多少人,我会派人盯着
+==========
+庆余年(精校版) (猫腻)
+- 您在位置 #49901-49901的标注 | 添加于 2020年1月20日星期一 下午7:57:10
+
+叶灵儿叹了口
==========
diff --git a/test_kman.py b/test_kman.py
index 1592113..d671699 100644
--- a/test_kman.py
+++ b/test_kman.py
@@ -1,11 +1,11 @@
-import unittest
+import unittest2
from collections import defaultdict
from kman import *
-class TestKman(unittest.TestCase):
+class TestKman(unittest2.TestCase):
# initial
def setUp(self):
CLIPPATH = './test.data'
@@ -13,9 +13,15 @@ class TestKman(unittest.TestCase):
LOG2FILE = 1
DELIMITER= '|'
+ self.km = kMan()
+
global t_bm_sec
global t_hl_sec
global t_nt_sec
+ global t_hl_sec2
+ global t_hl_sec3
+ global t_hl_sec4
+ self.t_num_nt = 0
global t_books
t_bm_sec = ["""另一半中国史 (高洪雷)
""", \
"""- 您在位置 #2468 的书签 | 添加于 2020年1月12日星期日 下午11:09:06
"""]
@@ -25,22 +31,55 @@ class TestKman(unittest.TestCase):
t_nt_sec = ["""薛兆丰经济学讲义 (薛兆丰)
""", \
"""- 您在位置 #4286 的笔记 | 添加于 2020年1月30日星期四 下午10:26:31
""", \
"""山寨 假货 问题
"""]
+ t_hl_sec2 = ["""枪炮、病菌与钢铁 : 人类社会的命运 (世纪人文系列丛书·开放人文) (贾雷德·戴蒙德)
""",\
+ """- 您在位置 #4267-4268的标注 | 添加于 2020年1月29日星期三 上午12:42:32
""",\
+ """从柏拉图到马克思的所有政治理论家都提出过这个问题xxxx"""]
+ t_hl_sec3 = ["""枪炮、病菌与钢铁(贾雷德·戴蒙德)""",\
+ """- 您在位置 #4267-4268的标注 | 添加于 2020年1月29日星期三 上午12:42:32""",\
+ """从柏拉图到马克思的所有政治理论家都提出过这个问题yyyy"""]
+ t_hl_sec4 = ["""枪炮、病菌与钢铁 : 人类社会的命运 (世纪人文系列丛书·开放人文) (贾雷德·戴蒙德)""",\
+ """- 您在位置 #4267-4268的标注 | 添加于 2020年1月29日星期三 上午12:42:32""",\
+ """从柏拉图到马克思的所有政治理论家都提出过这个问题zzzz"""]
t_books = defaultdict(dict)
def cre_tbooks(self):
# parsing section & fill data structure
- t_secd = parse_section(t_bm_sec,0)
+ self.t_num_nt = 0
+ t_secd = self.km.parse_section(t_bm_sec,0)
- t_secd = parse_section(t_hl_sec,1)
+ self.t_num_nt += 1
+ t_secd = self.km.parse_section(t_hl_sec,self.t_num_nt)
bn = t_secd['bookname']
t_books[bn]['author'] = t_secd[bn]['author']
- t_books[bn]['1'] = t_secd[bn]['1']
+ t_books[bn][str(self.t_num_nt)] = t_secd[bn][str(self.t_num_nt)]
t_secd.clear()
- t_secd = parse_section(t_nt_sec,2)
+ self.t_num_nt += 1
+ t_secd = self.km.parse_section(t_nt_sec,self.t_num_nt)
bn = t_secd['bookname']
t_books[bn]['author'] = t_secd[bn]['author']
- t_books[bn]['2'] = t_secd[bn]['2']
+ t_books[bn][str(self.t_num_nt)] = t_secd[bn][str(self.t_num_nt)]
+ t_secd.clear()
+
+ self.t_num_nt += 1
+ t_secd = self.km.parse_section(t_hl_sec2,self.t_num_nt)
+ bn = t_secd['bookname']
+ t_books[bn]['author'] = t_secd[bn]['author']
+ t_books[bn][str(self.t_num_nt)] = t_secd[bn][str(self.t_num_nt)]
+ t_secd.clear()
+
+ self.t_num_nt += 1
+ t_secd = self.km.parse_section(t_hl_sec3,self.t_num_nt)
+ bn = t_secd['bookname']
+ t_books[bn]['author'] = t_secd[bn]['author']
+ t_books[bn][str(self.t_num_nt)] = t_secd[bn][str(self.t_num_nt)]
+ t_secd.clear()
+
+ self.t_num_nt += 1
+ t_secd = self.km.parse_section(t_hl_sec4,self.t_num_nt)
+ bn = t_secd['bookname']
+ t_books[bn]['author'] = t_secd[bn]['author']
+ t_books[bn][str(self.t_num_nt)] = t_secd[bn][str(self.t_num_nt)]
t_secd.clear()
return t_books
@@ -48,13 +87,13 @@ class TestKman(unittest.TestCase):
# test function parse_section
def test_parse_section(self):
# parsing section & fill data structure
- t_secd = parse_section(t_bm_sec,0)
+ t_secd = self.km.parse_section(t_bm_sec,0)
self.assertEqual(t_secd,False)
- t_secd = parse_section(t_hl_sec,1)
+ t_secd = self.km.parse_section(t_hl_sec,1)
bn = t_secd['bookname']
self.assertIsNotNone(t_secd)
- self.assertEqual(bn,'薛兆丰经济学讲义 ')
+ self.assertEqual(bn,'薛兆丰经济学讲义')
self.assertEqual(t_secd[bn]['author'],'薛兆丰')
self.assertEqual(t_secd[bn]['1']['type'],'HL')
self.assertEqual(t_secd[bn]['1']['position'],'1408-1410')
@@ -67,10 +106,10 @@ class TestKman(unittest.TestCase):
t_books[bn]['1'] = t_secd[bn]['1']
t_secd.clear()
- t_secd = parse_section(t_nt_sec,2)
+ t_secd = self.km.parse_section(t_nt_sec,2)
bn = t_secd['bookname']
self.assertIsNotNone(t_secd)
- self.assertEqual(bn,'薛兆丰经济学讲义 ')
+ self.assertEqual(bn,'薛兆丰经济学讲义')
self.assertEqual(t_secd[bn]['author'],'薛兆丰')
self.assertEqual(t_secd[bn]['2']['type'],'NT')
self.assertEqual(t_secd[bn]['2']['position'],'4286')
@@ -86,7 +125,7 @@ class TestKman(unittest.TestCase):
# test drop_duplicate
def test_drop_duplicate(self):
t_books = self.cre_tbooks()
- t_secd = parse_section(t_hl_sec,3)
+ t_secd = self.km.parse_section(t_hl_sec,3)
bn = t_secd['bookname']
t_books_du = t_books.copy()
t_books_du[bn]['3'] = t_secd[bn]['3']
@@ -94,7 +133,7 @@ class TestKman(unittest.TestCase):
self.assertIsInstance(t_books_du[bn]['3'],dict)
try:
- t_books_du = drop_duplicate(t_books_du)
+ t_books_du = self.km.drop_duplicate(t_books_du)
t = t_books_du[bn]['3']
except KeyError as keyerror:
print("与预期匹配,sidx 3 重复被删除,抛出: %s" % 'keyerror')
@@ -104,31 +143,58 @@ class TestKman(unittest.TestCase):
# test function format_time()
def test_format_time(self):
t_ds = '2020年1月13日 星期一 下午 8:11:05'
- t_ds = format_time(t_ds)
+ t_ds = self.km.format_time(t_ds)
self.assertEqual(t_ds, '2020/1/13 20:11:05')
# test function format_data
def test_format_data(self):
t_books = self.cre_tbooks()
- t_out = format_data(t_books, ft='MD')
+ t_out = self.km.format_data(t_books, ft='MD')
self.assertEqual(t_out[0], 'TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT')
self.assertEqual(t_out[1], '--|--|--|--|--')
- self.assertEqual(t_out[2], 'HL|薛兆丰经济学讲义 |薛兆丰|2020/1/13 8:11:05|边际就是“新增”带来的“新增”。\n')
+ self.assertEqual(t_out[2], 'HL|薛兆丰经济学讲义|薛兆丰|2020/1/13 8:11:05|边际就是“新增”带来的“新增”。\n')
t_out.clear()
def test_add_note_to_highlight(self):
t_books = self.cre_tbooks()
- t_books_remove_nt = add_note_to_highlight(t_books)
- for k in t_books_remove_nt.keys():
- bn = k
- self.assertEqual((t_books_remove_nt[bn]['1']['content']).replace('\n',''),\
+ t_books_remove_nt = self.km.add_note_to_highlight(t_books)
+ self.assertEqual((t_books_remove_nt['薛兆丰经济学讲义']['1']['content']).replace('\n',''),\
'边际就是“新增”带来的“新增”。'+NTPREF+ '山寨 假货 问题')
def test_get_kindle_path(self):
- kp = get_kindle_path()
+ kp = self.km.get_kindle_path()
s = u"kindle disconnected" if not kp else u"kindle connected {}".format(kp)
print(s)
+ def test_get_bookname_num(self):
+ t_books = self.cre_tbooks()
+ [nu, bn] = self.km.get_bookname_num(t_books)
+ self.assertEqual(nu, self.t_num_nt)
+ self.assertEqual(bn['薛兆丰经济学讲义'],2)
+
+ def test_get_author_num(self):
+ t_books = self.cre_tbooks()
+ [nu, bn] = self.km.get_author_num(t_books)
+ self.assertEqual(nu, self.t_num_nt)
+ self.assertEqual(bn['薛兆丰'],2)
+
+ def test_filter_clips(self):
+ t_books = self.cre_tbooks()
+ # no filter
+ bn = self.km.filter_clips(t_books, '薛兆丰', 0)
+ print('filter 0 \n',bn)
+ bn = {}
+
+ # by bookname
+ bn = self.km.filter_clips(t_books, '枪炮、病菌与钢铁 : 人类社会的命运 (世纪人文系列丛书·开放人文)', 1)
+ print('filter 1 \n',bn)
+ bn = {}
+
+ # by author
+ bn = self.km.filter_clips(t_books, '贾雷德·戴蒙德', 2)
+ print('filter 2 \n',bn,'\n===========\n')
+ bn = {}
+
"""
def test_search_clip(self):
pass
@@ -149,17 +215,17 @@ class TestKman(unittest.TestCase):
if __name__ == '__main__':
"""
- suite = unittest.TestSuite ()
+ suite = unittest2.TestSuite ()
suite.addTest(TestKman('test_parse_section'))
suite.addTest(TestKman('test_format_time'))
suite.addTest(TestKman('test_format_data'))
suite.addTest(TestKman('test_drop_duplicate'))
suite.addTest(TestKman('test_add_note_to_highlight'))
- run = unittest.TextTestRunner (verbosity=2)
+ run = unittest2.TextTestRunner (verbosity=2)
run.run (suite)
"""
# not callable sequency
- unittest.main()
+ unittest2.main()
diff --git a/tthread.py b/tthread.py
new file mode 100644
index 0000000..039c0ca
--- /dev/null
+++ b/tthread.py
@@ -0,0 +1,106 @@
+
+import sys
+import pyqtgraph as pg
+from PyQt5.QtGui import *
+from PyQt5.QtWidgets import *
+from PyQt5.QtCore import QThread, pyqtSignal, QTimer
+import numpy as np
+
+class PlotSin(QThread):
+
+ # 定义信号,self.y 是 numpy.array,所以信号数据类型为 object
+ signal = pyqtSignal(object)
+
+ def __init__(self, parent=None):
+ super().__init__()
+ self.y = None
+ self.phase = 0
+
+ def sin(self):
+ self.x = np.arange(0, 3.0, 0.01)
+ self.y = np.sin(2 * np.pi * self.x + self.phase)
+ self.phase += 0.1
+ QThread.msleep(200) # 等待200毫秒
+
+ def run(self):
+ for _ in range(300):
+ self.sin()
+ self.signal.emit(self.y) # 向连接槽发射信号 self.y
+
+class PlotSin_MainWindow(QDialog):
+
+ def __init__(self):
+ super().__init__()
+ self.initUI()
+ self.clock_time = 0
+ self.timer = QTimer(self) # 生成定时器
+ self.timer.timeout.connect(self.clock) # 绑定计时函数 self.clock
+
+ def initUI(self):
+
+ self.creatContorls("时间显示:")
+ self.creatResult("函数绘制:")
+
+ layout = QHBoxLayout()
+ layout.addWidget(self.controlsGroup)
+ layout.addWidget(self.resultGroup)
+ self.setLayout(layout)
+ self.beginButton.clicked.connect(self.clock_begin)
+ self.setGeometry(300, 300, 600, 300)
+ self.setWindowTitle('Plot Sine')
+ self.show()
+
+ def creatContorls(self,title):
+ self.controlsGroup = QGroupBox(title)
+ self.beginButton = QPushButton("开始")
+ numberLabel = QLabel("运行时间:")
+ self.clockLabel = QLabel("")
+ controlsLayout = QGridLayout()
+ controlsLayout.addWidget(numberLabel, 0, 0)
+ controlsLayout.addWidget(self.clockLabel, 0, 1)
+ controlsLayout.addWidget(self.beginButton, 3, 0)
+ self.controlsGroup.setLayout(controlsLayout)
+
+ def creatResult(self,title):
+ self.resultGroup = QGroupBox(title)
+ self.guiplot = pg.PlotWidget()
+ gridLayout = QGridLayout()
+ gridLayout.addWidget(self.guiplot,0,2,2,3)
+ self.resultGroup.setLayout(gridLayout)
+
+ def clock_begin(self):
+ if not self.timer.isActive():
+ self.recorder_thread = PlotSin()
+ self.recorder_thread.signal.connect(self.displaySin) # 绑定信号槽函数
+ self.recorder_thread.start() # 线程执行
+ self.clock()
+ self.timer.start(1000)
+ else:
+ self.beginButton.setText("开始")
+ self.clockLabel.setText("")
+ self.recorder_thread.terminate() # 终止线程
+ self.timer.stop() # 终止定时器
+ self.clock_time = 0
+ text = str(self.clock_time) + "s"
+ self.clockLabel.setText(text)
+
+ def clock(self):
+ text = str(self.clock_time) + "s"
+ self.clockLabel.setText(text)
+ if self.clock_time == 0:
+ self.beginButton.setText("结束")
+
+ self.clock_time += 1
+
+ def plotSin(self, y):
+ self.guiplot.clear()
+ self.guiplot.plot(y)
+
+ def displaySin(self, y):
+ self.plotSin(y)
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ window = PlotSin_MainWindow()
+ window.show()
+ sys.exit(app.exec_())
diff --git a/xx b/xx
index 925c5fd..b3a5c9b 100644
--- a/xx
+++ b/xx
@@ -1 +1 @@
-{"\u859b\u5146\u4e30\u7ecf\u6d4e\u5b66\u8bb2\u4e49 ": {"author": "\u859b\u5146\u4e30", "1": {"type": "HL", "position": "1408-1410", "day": "2020\u5e741\u670813\u65e5", "week": "\u661f\u671f\u4e00", "meridiem": "\u4e0a\u5348", "time": "8:11:05", "content": "\u4e48\u5230\u5e95\u4ec0\u4e48\u53eb\u8fb9\u9645\uff1f\u8fb9\u9645\u5c31\u662f\u201c\u65b0\u589e\u201d\u5e26\u6765\u7684\u201c\u65b0\u589e\u201d\u3002 \u4f8b\u5982\uff0c\u8fb9\u9645\u6210\u672c\u5c31\u662f\u6bcf\u65b0\u589e\u4e00\u4e2a\u5355\u4f4d\u4ea7\u54c1\u6240\u9700\u8981\u4ed8\u51fa\u7684\u65b0\u589e\u6210\u672c\uff1b\u8fb9\u9645\u6536\u5165\u662f\u6bcf\u591a\u5356\u4e00\u4e2a\u4ea7\u54c1\u80fd\u591f\u5e26\u6765\u7684\u65b0\u589e\u6536\u5165\uff1b\u8fb9\u9645\u4ea7\u91cf\u662f\u6bcf\u65b0\u589e\u4e00\u4efd\u6295\u5165\u6240\u5e26\u6765\u7684\u65b0\u589e\u4ea7\u91cf\uff1b\u8fb9\u9645\u6548\u7528\u662f\u6bcf\u6d88\u8017\u4e00\u4e2a\u5355\u4f4d\u7684\u5546\u54c1\u6240\u80fd\u5e26\u6765\u7684\u65b0\u589e\u4eab\u53d7\u3002"}, "2": {"type": "HL", "position": "4284-4286", "day": "2020\u5e741\u670830\u65e5", "week": "\u661f\u671f\u56db", "meridiem": "\u4e0a\u5348", "time": "10:23:58", "content": "\u4e00\u4e2a\u56fd\u5bb6\u5f88\u5927\uff0c\u8d2b\u5bcc\u6709\u5dee\u8ddd\uff0c\u5e76\u975e\u6bcf\u4e2a\u5b66\u6821\u548c\u5bb6\u957f\u90fd\u80fd\u8d1f\u62c5\u5f97\u8d77\u8fd9\u6837\u6807\u51c6\u7684\u6821\u8f66\u3002\u6807\u51c6\u592a\u9ad8\uff0c\u5c31\u4f1a\u903c\u7740\u5f88\u591a\u5b66\u6821\uff0c\u5c24\u5176\u662f\u519c\u6751\u7684\u5b66\u6821\u653e\u5f03\u63d0\u4f9b\u6821\u8f66\uff0c\u5bb6\u957f\u4eec\u5c31\u53ea\u80fd\u4f7f\u7528\u5b89\u5168\u6027\u80fd\u66f4\u4f4e\u7684\u4ea4\u901a\u5de5\u5177\uff0c\u6bd4\u5982\u81ea\u5df1\u9a91\u81ea\u884c\u8f66\u6216\u96c7\u7528\u9ed1\u8f66\u7b49\uff0c\u7ed3\u679c\u662f\u5b69\u5b50\u4eec\u4eab\u53d7\u5230\u7684\u5b89\u5168\u4fdd\u969c\u53cd\u800c\u964d\u4f4e\u4e86\u3002--CG\u6ce8:\u5c71\u5be8 \u5047\u8d27 \u95ee\u9898"}, "4": {"type": "HL", "position": "4382-4384", "day": "2020\u5e741\u670830\u65e5", "week": "\u661f\u671f\u56db", "meridiem": "\u4e0a\u5348", "time": "10:29:41", "content": "\u4e3a\u4e86\u514b\u670d\u4fe1\u606f\u4e0d\u5bf9\u79f0\uff0c\u5efa\u7acb\u4e92\u4fe1\uff0c\u4eba\u7c7b\u793e\u4f1a\u6784\u60f3\u51fa\u4e86\u5404\u79cd\u5404\u6837\u6709\u8da3\u7684\u89e3\u51b3\u65b9\u6848\uff0c\u4ece\u91cd\u590d\u4ea4\u6613\u5230\u7b2c\u4e09\u65b9\u80cc\u4e66\uff0c\u4ece\u8d28\u4fdd\u3001\u5ef6\u4fdd\uff0c\u518d\u5230\u6536\u76ca\u5171\u4eab\u3002\u6b64\u5916\uff0c\u8fd8\u6709\u4e09\u79cd\u975e\u5e38\u63a5\u8fd1\u7684\u5efa\u7acb\u4fe1\u4efb\u7684\u529e\u6cd5\uff1a\u4ed8\u51fa\u6c89\u6ca1\u6210\u672c\u3001\u7ed9\u51fa\u4eba\u8d28\u6216\u8005\u7ed9\u51fa\u62b5\u62bc\u3002"}, "lines": 4}}
\ No newline at end of file
+{"\u859b\u5146\u4e30\u7ecf\u6d4e\u5b66\u8bb2\u4e49": {"author": "\u859b\u5146\u4e30", "1": {"type": "HL", "position": "1408-1410", "day": "2020\u5e741\u670813\u65e5", "week": "\u661f\u671f\u4e00", "meridiem": "\u4e0a\u5348", "time": "8:11:05", "content": "\u4e48\u5230\u5e95\u4ec0\u4e48\u53eb\u8fb9\u9645\uff1f\u8fb9\u9645\u5c31\u662f\u201c\u65b0\u589e\u201d\u5e26\u6765\u7684\u201c\u65b0\u589e\u201d\u3002 \u4f8b\u5982\uff0c\u8fb9\u9645\u6210\u672c\u5c31\u662f\u6bcf\u65b0\u589e\u4e00\u4e2a\u5355\u4f4d\u4ea7\u54c1\u6240\u9700\u8981\u4ed8\u51fa\u7684\u65b0\u589e\u6210\u672c\uff1b\u8fb9\u9645\u6536\u5165\u662f\u6bcf\u591a\u5356\u4e00\u4e2a\u4ea7\u54c1\u80fd\u591f\u5e26\u6765\u7684\u65b0\u589e\u6536\u5165\uff1b\u8fb9\u9645\u4ea7\u91cf\u662f\u6bcf\u65b0\u589e\u4e00\u4efd\u6295\u5165\u6240\u5e26\u6765\u7684\u65b0\u589e\u4ea7\u91cf\uff1b\u8fb9\u9645\u6548\u7528\u662f\u6bcf\u6d88\u8017\u4e00\u4e2a\u5355\u4f4d\u7684\u5546\u54c1\u6240\u80fd\u5e26\u6765\u7684\u65b0\u589e\u4eab\u53d7\u3002"}, "2": {"type": "HL", "position": "4284-4286", "day": "2020\u5e741\u670830\u65e5", "week": "\u661f\u671f\u56db", "meridiem": "\u4e0a\u5348", "time": "10:23:58", "content": "\u4e00\u4e2a\u56fd\u5bb6\u5f88\u5927\uff0c\u8d2b\u5bcc\u6709\u5dee\u8ddd\uff0c\u5e76\u975e\u6bcf\u4e2a\u5b66\u6821\u548c\u5bb6\u957f\u90fd\u80fd\u8d1f\u62c5\u5f97\u8d77\u8fd9\u6837\u6807\u51c6\u7684\u6821\u8f66\u3002\u6807\u51c6\u592a\u9ad8\uff0c\u5c31\u4f1a\u903c\u7740\u5f88\u591a\u5b66\u6821\uff0c\u5c24\u5176\u662f\u519c\u6751\u7684\u5b66\u6821\u653e\u5f03\u63d0\u4f9b\u6821\u8f66\uff0c\u5bb6\u957f\u4eec\u5c31\u53ea\u80fd\u4f7f\u7528\u5b89\u5168\u6027\u80fd\u66f4\u4f4e\u7684\u4ea4\u901a\u5de5\u5177\uff0c\u6bd4\u5982\u81ea\u5df1\u9a91\u81ea\u884c\u8f66\u6216\u96c7\u7528\u9ed1\u8f66\u7b49\uff0c\u7ed3\u679c\u662f\u5b69\u5b50\u4eec\u4eab\u53d7\u5230\u7684\u5b89\u5168\u4fdd\u969c\u53cd\u800c\u964d\u4f4e\u4e86\u3002--CG\u6ce8:\u5c71\u5be8 \u5047\u8d27 \u95ee\u9898"}, "lines": 3}, "\u5e86\u4f59\u5e74(\u7cbe\u6821\u7248\uff09": {"author": "\u732b\u817b", "4": {"type": "HL", "position": "48484-48484", "day": "2020\u5e741\u670819\u65e5", "week": "\u661f\u671f\u65e5", "meridiem": "\u4e0b\u5348", "time": "8:00:29", "content": "\uff1a\u201c\u56ed\u5b50\u91cc\u7684\u62a4\u536b\u80fd\u63ba\u591a\u5c11\u4eba\u5c31\u63ba\u591a\u5c11\u4eba\uff0c\u6211\u4f1a\u6d3e\u4eba\u76ef\u7740\uff0c"}, "5": {"type": "HL", "position": "49901-49901", "day": "2020\u5e741\u670820\u65e5", "week": "\u661f\u671f\u4e00", "meridiem": "\u4e0b\u5348", "time": "7:57:10", "content": "\u53f6\u7075\u513f\u53f9\u4e86\u53e3"}, "lines": 2}}
\ No newline at end of file