kindle manager

This commit is contained in:
gavin
2020-06-07 10:21:02 +08:00
parent 722757dadd
commit 76cc0d8c6d
15 changed files with 1007 additions and 233 deletions

View File

@@ -72,10 +72,13 @@ b['1']['2'] = {'3':1} # OK
so I need to use resouce file(.qrc) to manage resouce(icons), so I need to use resouce file(.qrc) to manage resouce(icons),
and generator rcc binnay file, must kmanapp\_rc.py and generator rcc binnay file, must kmanapp\_rc.py
command: pyside2-rcc -binary kmanapp.qrc -o 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 # feature plan
## 20200528 ## 20200528
- 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**
- new thread to check kindle connection status - use thread to check kindle connection status **XXXX**
- export function

BIN
icons/person.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
icons/user.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

122
kman.py
View File

@@ -9,10 +9,12 @@
import re import re
import os import os
import io
import json import json
import time import time
import logging import logging
import platform import platform
import subprocess
from collections import defaultdict from collections import defaultdict
# data structure - use dict # data structure - use dict
@@ -123,7 +125,7 @@ class kMan:
kp = self.get_kindle_path() kp = self.get_kindle_path()
if not kp: if not kp:
s2 = u'Disconnected' s2 = u'Disconnected ({})'.format(CLIPPATH)
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())
@@ -189,8 +191,8 @@ class kMan:
# parse #1 line # parse #1 line
aus = au.search(authinfo) aus = au.search(authinfo)
bookname = aus.group(1) bookname = aus.group(1).strip()
author = aus.group(2) author = aus.group(2).strip()
section[bookname]['author'] = author section[bookname]['author'] = author
section['bookname'] = bookname section['bookname'] = bookname
@@ -314,6 +316,73 @@ class kMan:
return bks 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): def add_note_to_highlight(self,bks):
""" append note content to corresponding highlight """ append note content to corresponding highlight
and remove NT sections and remove NT sections
@@ -349,7 +418,8 @@ class kMan:
'TITLE' 'TITLE'
'AUTHOR' 'AUTHOR'
'CONTENT' 'CONTENT'
Return: search clipping content Return:
[number of result , result dict]
""" """
nbks = defaultdict(dict) nbks = defaultdict(dict)
nu = 0 nu = 0
@@ -405,37 +475,58 @@ class kMan:
else ("ls /Volumes/Kindle" if os.name=='posix' else '') else ("ls /Volumes/Kindle" if os.name=='posix' else '')
# not test for windows & linux # not test for windows & linux
with os.popen(cmd) as s: with subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, \
r = s.read() 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 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] if 'Kindle' in d: return d.split('\s+')[0]
elif os.name == 'posix': # mac os elif os.name == 'posix': # mac os
if r: return('/Volumes/Kindle') if sout: return('/Volumes/Kindle')
else: else:
pass 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 return False
def import_clips(self, tp='local'): def import_clips(self, fp=CLIPPATH):
"""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 '======='
Args: tp: 'local' local clipping file Args: fp - file path
'kindle' kindle clipping file
Return: 0 - want to import kindle but kindle is not connected Return: 0 - want to import kindle but kindle is not connected
books dict books dict
""" """
# check kindle by user just call get_kindle_path()
"""
if tp=='kindle': if tp=='kindle':
kp = get_kindle_path() kp = get_kindle_path()
if not kp: return 0 if not kp: return 0
else: path = kp else: path = kp
else: else:
path = CLIPPATH path = fn
"""
# loop to fill books dict # 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) bks = defaultdict(dict)
secd = defaultdict(dict) secd = defaultdict(dict)
sidx = 0 sidx = 0
@@ -490,7 +581,7 @@ class kMan:
if __name__=='__main__': if __name__=='__main__':
#books = defaultdict(dict) #books = defaultdict(dict)
km = kMan() km = kMan()
books = km.import_clips('local') books = km.import_clips()
# remove duplication # remove duplication
km.drop_duplicate(books) km.drop_duplicate(books)
@@ -503,6 +594,9 @@ if __name__=='__main__':
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.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 # add note content to hightlight, then delete note
km.add_note_to_highlight(books) km.add_note_to_highlight(books)

View File

@@ -1,19 +1,20 @@
import sys import sys
from time import sleep from time import sleep
from threading import Thread
import _thread
import threading
from PySide2.QtWidgets import QApplication from PySide2.QtWidgets import QApplication
from PySide2.QtWidgets import QMainWindow 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, 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, from PySide2.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont,
QFontDatabase, QIcon, QKeySequence, QLinearGradient, QPalette, QPainter, QFontDatabase, QIcon, QKeySequence, QLinearGradient, QPalette, QPainter,
QPixmap, QRadialGradient, QStandardItem, QStandardItemModel) QPixmap, QRadialGradient, QStandardItem, QStandardItemModel)
from PySide2.QtWidgets import * #from PySide2.QtWidgets import *
from mainwindow import Ui_MainWindow from mainwindow import Ui_MainWindow
from kman import * from kman import *
@@ -21,38 +22,34 @@ from kman import *
# import binary resource file(kmanapp_rc.py) # import binary resource file(kmanapp_rc.py)
import kmanapp_rc import kmanapp_rc
ONLY_TEST = 0 ONLY_TEST = 1
class kmanWindow(QMainWindow): class kmanWindow(QMainWindow):
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(kmanWindow, self).__init__(*args, **kwargs) super(kmanWindow, self).__init__(*args, **kwargs)
""" """
flag = True
def __init__(self, parent=None): def __init__(self, parent=None):
super(kmanWindow, self).__init__(parent) super(kmanWindow, self).__init__(parent)
self.stat_str = 'status information'
self.search_str = ''
self.local_fn = CLIPPATH
# create ui and initial it # create ui and initial it
ui = Ui_MainWindow() ui = Ui_MainWindow()
ui.setupUi(self) ui.setupUi(self)
self.ui = ui self.ui = ui
self.km = kMan() self.km = kMan()
self.books = self.km.import_clips('local') self.books = self.km.import_clips()
# loop check kindle is connected or not self.add_ui_component()
# to be implement
""" self.show_status_info()
try: self.fill_treeview()
#_thread.start_new_thread(self.check_kindle_status)
t1 = threading.Thread(target=check_kindle_status) # timer to check status of kindle
t1.start() self.timer = QTimer(self)
except: self.timer.timeout.connect(self.show_status_info)
print ("Error: can not start thread") 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(self.books))
@@ -62,18 +59,34 @@ class kmanWindow(QMainWindow):
ui.actionstatistic.triggered.connect(lambda: self.statistic()) ui.actionstatistic.triggered.connect(lambda: self.statistic())
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.flush()) ui.actionflush.triggered.connect(lambda: self.refresh())
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.clicked_items) ui.treeView.clicked.connect(self.tree_item_clicked)
ui.tableView.clicked.connect(self.table_item_clicked)
self.add_ui_component()
#add_ui_component() ###! can not found this function
def add_ui_component(self): def add_ui_component(self):
self.ui.searchComboBox.addItems(['ALL','bookname','content','author']) 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 if not ONLY_TEST: # XXXXXXXXXXXXX
model = QStandardItemModel() model = QStandardItemModel()
rootItem = model.invisibleRootItem() rootItem = model.invisibleRootItem()
@@ -113,11 +126,25 @@ class kmanWindow(QMainWindow):
icon.addFile(u":/icons/book_open.png", QSize(), QIcon.Normal, QIcon.Off) icon.addFile(u":/icons/book_open.png", QSize(), QIcon.Normal, QIcon.Off)
item.setIcon(icon) item.setIcon(icon)
parentItem.appendRow(item) 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(item.text())
print( 'call clicked_items()' )
# 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): def search_button_clicked(self):
print( 'call search_button_clicked()' ) print( 'call search_button_clicked()' )
@@ -129,10 +156,11 @@ class kmanWindow(QMainWindow):
#print(search_clip(self.books,s,'ALL',p[t])) #print(search_clip(self.books,s,'ALL',p[t]))
print('call search_scope_change()') print('call search_scope_change()')
## XXXX
def check_kindle_status(self): def check_kindle_status(self):
while True: while self.flag:
self.show_status_info() self.show_status_info()
sleep(1) sleep(2)
def show_status_info(self): def show_status_info(self):
""" show status information on statusbar """ show status information on statusbar
@@ -143,33 +171,92 @@ class kmanWindow(QMainWindow):
""" """
status = self.km.status_info() status = self.km.status_info()
self.ui.statusbar.showMessage(status[0],0) self.ui.statusbar.showMessage(status[0],0)
clabel = QLabel(status[1]) self.ui.status_label.setText(status[1])
if not self.km.status: if not self.km.status:
pe = QPalette() self.ui.pe.setColor(QPalette.WindowText,Qt.red)
pe.setColor(QPalette.WindowText,Qt.red) #self.ui.status_label.setAutoFillBackground(True)
#clabel.setAutoFillBackground(True) self.ui.status_label.setPalette(pe)
clabel.setPalette(pe) self.ui.statusbar.addPermanentWidget(self.ui.status_label, stretch=0)
self.ui.statusbar.addPermanentWidget(clabel, stretch=0)
# define slot functions # define slot functions
def import_kindle(self,bks): 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() status = self.km.status_info()
self.show_status_info() self.show_status_info()
print(bks) print(bks)
pass
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",
'./', # 起始路径 './', # start path
"All Files (*);;Text Files (*.txt)") # 设置文件扩展名过滤,用双分号间隔 "All Files (*);;Text Files (*.txt)") # filter file type
self.fn = fn self.fn = fn
self.books = self.km.import_clips(fn)
self.show_status_info()
#print('filename ', fn, 'filetype ', ft) #print('filename ', fn, 'filetype ', ft)
if fn == "": return False 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): def config(self):
print("call slot config()") print("call slot config()")
pass pass
@@ -187,7 +274,7 @@ class kmanWindow(QMainWindow):
pass pass
def about(self): def about(self):
self.messagebox('\n'+ \ self.messagebox(ico=1, info='\n'+ \
' kindle management tool \n\n' + \ ' kindle management tool \n\n' + \
' v1.0.4\n\n' + \ ' v1.0.4\n\n' + \
' Author: chengan\n\n' + \ ' Author: chengan\n\n' + \
@@ -196,28 +283,87 @@ class kmanWindow(QMainWindow):
print("call slot about()") print("call slot about()")
pass pass
def flush(self): def refresh(self):
print("call slot flush()") print("call slot refresh()")
pass pass
# unify messagebox def messagebox(self, ico=1, info=''):
def messagebox(self, showinfo): """ 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 = QMessageBox()
msgBox.setText(showinfo) msgBox.setText(info)
msgBox.setInformativeText("") msgBox.setInformativeText("")
msgBox.setIcon(QMessageBox.Information) msgBox.setIcon(icons[ico])
msgBox.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok) msgBox.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok)
msgBox.setBaseSize(QSize(600, 300)) msgBox.setBaseSize(QSize(600, 300))
r = msgBox.exec() 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__": if __name__ == "__main__":
import sys
from PySide2.QtWidgets import QApplication, QLabel
app = QApplication(sys.argv) app = QApplication(sys.argv)
kmw = kmanWindow() kmw = kmanWindow()
kmw.resize(900, 600) kmw.resize(900, 600)
kmw.show() 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_() app.exec_()

175
kmanapp.pyproject.user Normal file
View File

@@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.11.2, 2020-06-06T20:00:44. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{fae58cf6-d2fe-464b-83cf-b657b7f31038}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop (x86-darwin-generic-mach_o-32bit)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop (x86-darwin-generic-mach_o-32bit)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{47431a9c-e34f-4b94-83d9-f5f98149f5e9}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">-1</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
<value type="int" key="Analyzer.Perf.Frequency">250</value>
<valuelist type="QVariantList" key="Analyzer.Perf.RecordArguments">
<value type="QString">-e</value>
<value type="QString">cpu-cycles</value>
<value type="QString">--call-graph</value>
<value type="QString">dwarf,4096</value>
<value type="QString">-F</value>
<value type="QString">250</value>
</valuelist>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Perf.StackSize">4096</value>
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">kmanapp</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration./Users/mark/kman/kmanapp.py</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/Users/mark/kman/kmanapp.py</value>
<value type="QString" key="PythonEditor.RunConfiguation.Interpreter">{8add66da-db0d-4573-865d-9852c93e0d0e}</value>
<value type="QString" key="PythonEditor.RunConfiguation.Script">/Users/mark/kman/kmanapp.py</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="bool" key="RunConfiguration.Arguments.multi">false</value>
<value type="QString" key="RunConfiguration.OverrideDebuggerStartup"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/Users/mark/kman</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@@ -46,5 +46,7 @@
<file>icons/emblem_library.png</file> <file>icons/emblem_library.png</file>
<file>icons/book3.png</file> <file>icons/book3.png</file>
<file>icons/register.png</file> <file>icons/register.png</file>
<file>icons/person.png</file>
<file>icons/user.png</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -6,94 +6,96 @@
from PySide2 import QtCore from PySide2 import QtCore
qt_resource_data = b"\ qt_resource_data = b"\
\x00\x00\x05^\ \x00\x00\x05w\
\x00\ \x00\
\x00\x1c\xaax\x9c\xb5Y\xbd\x8e\xdc6\x10\xee\xef)\x84\ \x00\x1d\x01x\x9c\xb5Y\xcd\x8e\xdb6\x10\xbe\xefS\x10\
\xed\xb3\xda=\x5cr\x81\xc1\x93\x91\x0b\x9c\x18\x88\x0f8\ \xbe\xd7\xb2\x17\xdbn\xb1\x90\x1dt\x8b\xb4\x01\x9a\x056\
\xe36v\x19p%\xee\x8a9I\xd4Q\x94o\xd7\x95\ Xws,h\x89\xb6\xd8\x95D-Ee\xed\x9c\x92\
]8@\x80\x04q\x91\xd2U\x8aT\x81\x93\x22\x8d\x8b\ C\x0a\x14h\xd1\x1cz\xcc\xa9\x87\x9e\x8a\xb4\x87^r\
<M|\xb6\x1f##\x92\x92\xa8\xdf\xddX\xa7N\xf3\ \xe8\xd34\x9b\xe41:\x22)\x89\xfa\xb5\x1b\xadn\x9a\
\xc3\xe1\xcc\xc7\x99\xe1p\x17\xdd\xdd\x84\x81\xf5\x84\xf0\x84\ \x1f\x0eg>\xce\x0c\x87\xb6}o\x13\xf8\xe8\x09\xe11\
\xb2\xe8d2\x9f\xce&\x16\x89\x5c\xe6\xd1h}2\xf9\ e\xe1l4\x1dOF\x88\x84\x0esi\xb8\x9e\x8d\xbe\
v\xf1\xd5'\x9fO\xee:\x07(\xa5\xa5\xd2\x11(9\ ]|\xf5\xc9\xe7\xa3{\xf3\x03;\xa1\x85\xd2\x11(\xcd\
\x07\x16r\x03\x9c$\xce\x19\xa6\xd1c\x1ay\xec\x1a\xd9\ \x0f\x90\xed\xf88\x8e\xe7g\x98\x86\x8fi\xe8\xb2\x1b\xdb\
\x8a\x03\xa2k\xea\xad\x89\xb0$}2yX\xeaL\xac\ R\x1c\x10\xddPwM\x04\x92\xf4l\xf4\xa8\xd0\x19\xa1\
\x08\x87\xe4dbp@\xdfB1g1\xe1b\xab\xc5\ \x10\x07d628\xa0\x8f\xec\x88\xb3\x88p\xb1\xd5\xe2\
k\xc2B\x22\xf8V\x0a-\xc4\x89+\xe4\x97\x856\xce\ 5a\x01\x11|+\x85\xc8\xe6\xc4\x11\xf2\x0b\xd9\x9b\xf9\
\x0c\xd9\x1bMl3b\xab\x09\xd8S\xf8\xce\xf1\xf1\x11\ \xc4\xb66\x9a\xd8\xa6\xc4V\x13\xb0\xa7\xf0\xe6\xc7\xc7G\
\xb2\xd5\xa7b\xfb\x84\xae}\xe1\x1c\xcdAU\x7fK\x9b\ \xb6\xa5>\x15\xdb#t\xed\x89\xf9\xd1\x14T\xf5\xb7\xb4\
vn\x14\xd9\xf9\xe6m\x9e\x5cK'\x17T\x04D;\ ieFm+\xdb\xbc\xc9\x93\x1b\xe9\xe4\x82\x0a\x9fh\
\x93\x08\x0e\xe08\xdf\x80 \xd6\x19\x8e\xf0\x9a\x84$\ gb\xc1\x01\x9c\xf97 \xf0\x09:\xc3!^\x93\x80\
\x12\xc8\xd6\x92\xa6\xcd\x1a\x1c\x8f%\x99C\xe1\xc2Z\x8e\ \x84\xc2\xb6\xb4\xa4n\xb3\x02\xc7cIfP8\xb0\x96\
\x03\xa5\xa3\xf7\x08\xf0\x96\xa5\xe5\x82\xaf9\xf5\x1eHV\ c_\xe9\xe8=|\xbceI\xb1\xe0kN\xdd\x87\x92\
\xbeh]rt\x9cT\x90\xd0\xe2\xec\xfad\x02G\xe8\ \x95-Z\x17\x1c\x1d'\x15$@\x9c\xdd\xccFp\x84\
\xb2 \x0d\xa3\xecSI\x1b\x16\xef\x9f\xb2M\xd5\xa2\xcf\ \x0e\xf3\x93 L?\x95\xb4f\xf1\xc1)\xdb\x94-z\
8}\xca\x22\x81\x83\x8a]m9'\x1a\xb1<\xc0K\ \x8c\xd3\xa7,\x14\xd8/\xd9\xd5\x963\xa2\x16\xcbC\xbc\
\x12\xe46\x12\x82\xb9\xeb+V\xb1\xa2\x01\xa9 \x1ba\ $~f#&\x98;\x9eb\xe5+j\x90\x0a\xb2\x11\
\x88\x0bD/\xe4j\x13F-\xaf\x80\xa9Y\xca\x8d\xc2\ \x868G\xf4B\xae6a\xd4\xf2\x12\x98\x9a\xa5\xdc\xc8\
G\xdbtr\x87\xc74\x22\xf7<*jN\xe7\xdcn\ }\xb4L'wxLCr\xdf\xa5\xa2\xe2t\xc6m\
\xbf\xe3\x00\xbb\xc4g\x81G\xf8\xa2#\x84\x9b_\xfe|\ \xf7;\xf2\xb1C<\xe6\xbb\x84/ZB\xb8\xfd\xe5\xcf\
\xf7\xd3\x8fo\xdf\xfc~\xf3\xf2\xe7\x7f\x9f=\x7f\xfb\xcf\ w?\xfd\xf8\xf6\xcd\xef\xb7/\x7f\xfe\xf7\xd9\xf3\xb7\xff\
\xab\x0f\xcf^\xc0\xc7\xcd\x0f/n^\xbfy\xf7\xf2\xd5\ \xbc\xfa\xf0\xec\x05|\xdc\xfe\xf0\xe2\xf6\xf5\x9bw/_\
\xfb\xbf\x7f{\xff\xc7\xaf\x1f^\xff5z\x98_\xb2p\ \xbd\xff\xfb\xb7\xf7\x7f\xfc\xfa\xe1\xf5_\x83\x87\xf9%\x0b\
\xc9\xe0\x94\xaba\x16\xdc\xee0\xdd\x94sH\xc9\x8e\x10\ \x96\x0cN\xb9\x1cf\xcem\x0f\xd3I8\x87\x94l\x09\
\xed\xd1\x1c^0\x16\x9c\xa6B\xb0\xa8\xea\xb2\xc1\xff\xff\ \xd1\x1a\xcc\xe1\x05c\xfei\x22\x04\x0b\xcb.\x1b\xfc\xff\
95\x9dN\xf7B\xbaa\x90\xba\x95\xfd\xc0w`$\ \x9fS\xe3\xf1x/\xa4k\x06\xa9S\xda\x0f|\x07F\
\xe0.'\x09K\xb9\x0b*\x97!\xd4~\x1cO\xaf\xb8\ \x0c\xeer\x12\xb3\x84;\xa0r\x15@\xedG\xd1\xf8\x9a\
kjZ(b<\xc4\x01[\xad\x9c;\xb6\x5cf\xab\ ;\xa6&\xb2C\xc6\x03\xec\xb3\xd5j~b\xc9e\x96\
H\xa6\xdf\xc7d\x8d\xec\x1db\xbd\xd1\xc7\xe3\x8clU\ \x8ad\xfc}D\xd6\xb6\xb5C\xac7\xfax\x9cmK\
\xec\xba1\x18\x22\xa3I\xcc\xdb\x9aD\xedD.\xe2\x80\ \x15\xbbn\x0c\x86\xc8h\x12\xd3\xa6&Q9\x91\x8b\xc8\
\x0aAxq\x1e\x9a\xfe\xee\xb0l\x0e5\xd8\xa0\x89@\ \xa7B\x10\x9e\x9f\x87\xa6\xbf;,\x9aC\x056h\x22\
\xee`AM\xf4\x10\x89\xd2\xd0y(\xee\xdc\xb9_4\ \x90;XP\x13=\x9b\x84I0\x7f$NN\x1e\xe4\
\x19dK\xe6AW\x88\x8d\xec\xe0\x84<\xa2\xa4\xb8@\ M\xc6\xb6$\xf3\xa0-\xc4ZvpB.)\xc9/\
DN\x97\xbb\xd4\x5cI\xe8Sr\xce\x02\xean\xcd\xbc\ \x10\x91\xd1\xc5.\x15Wb\xfa\x94\x9c3\x9f:[3\
\xc9\xb8\xb1\xe4Z~\xf6-\xb61\xe8\x9es\xb2\x22\x90\ oRn$\xb9\xc8K\xbf\xc56\x02\xddsNV\x04\
\xf6\xde\xc4zRr\xefmb\x1ce7a%\x11\xa0\ \xd2\xde\x1d\xa1'\x05\xf7\xfe&\xc2az\x13\x96\x12\x01\
QBB\x11\xe1\xfa\xd9\xedcP\x86NvW\x96:\ \x1a%$\x14\x11\x8e\x97\xde>\x06e\xe8\xa4we\xa1\
\x06e\x1cj\xe9\x89q\xac\xcd\x93\xaeE\x15\xe2\x0d\x0d\ cP\xc6\xa1\x16\x9e\x18\xc7Z?\xe9JT\x01\xde\xd0\
\xd3\xf0\x02\x16\xd7\xc32\x1dP7\xe0\xd1lV\xb9\x0c\ \x09.`q5,\xd3\x01u\x03\x1eM&\xa5\xcb\
u\x00\xea\x16\x9c\x7fv|||8\xff\xb4r-\x1a\ P\x07\xa0n\xc1\xe9g\xc7\xc7\xc7\x87\xd3OK\xd7\xa2\
\xbe\xf5x\xd5H\xbf=\x13\xa7\xfb\xb0Z\xf3\xc6H\x9c\ \xe1[\xa7WX@M-\x13A\xb2\xdb\x83`h\x8b\
G\xa0K\xddz\xda\xb4\x02VO\x1c\xbc\x0c\xaa\x99S\ \x974\xa6K\xbf\xe4\xd8\x12\xaaw\xbe\xc2~LlK\
0\xba\xab\xbf5w\xba\x92\xe7L\x1d\xc9\xee\xd4\xd9+\ ~\x17Vs\x1bE\x16T\xb2z\xcf|l\xcf\x81\xc6\
w\xf6I\x9e\xf6\xec\xd9\xa7Q4\xe1\x81\xc6i\xde\x86\ t4\xf2\xf1\x12t\xa9S\xcd\xc6\xc6\x88\xab\xf9\x88!\
\x22\xa7G\x04\xa7\xac\xb6A\xe0\xcc\x87\x82\xb3gq5\ \xd0RB\xe6\x8c\xf6\xa6\xd2\x98\x92m9y\xa6Nz\
\xaa+/\xaf\xb2\x82\xea5V\x8e\x9d\xb3Y\xb3\xbe\xea\ wF\xee\x95\x92\xfb\xe4dsR\xee\xd3\x7f\xea\xf0@\
\x05\xb6_\x87\xaf\xd0U\xd2\xe8\xf1f\xf37\x95\x1a%\ ?6/Y\x91\xd1\x03\x82S\x14q/p\xa6}\xc1\
\x0a\xc5\x96&\xa7\xb8\xacQ\xc9X\x02\xc3n\xd3?\x83\ \xd9\xb3fkE\x9bUmQ\x98\xd5\xd2-\xa6\xd9\xc9\
\xba3\xb4CM\xaa={\xa7ws|\xaf\xce\xef\xd5\ \xa4^\xb6\xd5\xba\xdd\xef\xe2(\xd1e\xd2\xb8:\xcc;\
\x01\xbek\x82/\xb0<<\xacBY\x8e\xf0\xf5y\xbb\ \xc5T\xaa\x95(\x14[\x12\x9f\xe2\xa2F%c\x09\x0c\
'n9M\x94q\x08M\xb6\xc6\xd1\x98\xfd\x8b\xb1B\ \xabI\xff\x0c\xea\xce\xd0\x0e4\xa9\xf6\xec|\x14\x98\xaf\
/\xab\x8c\x16\xb5CDX\x80l\x99\x0aR\xdd\xea\x0b\ \x82\xf2\xb3\xa0\xfc.h{\x18\xe4X\x1e\x1e\x96\xa1,\
NpnM\xf6\xb2\x05\x8b\x17\xa5\xc8hp\xc8.L\ ^\x06\xd51\xbe#n9\xa4\x14q\x08M6\xc6Q\
\xf4Z<\x85e\x97\xb9\xc9%\xf0\x9c\x15\x0e\x12\x82l\ {R\xe4\xd3\x8a^V\x9aX*\x87X\xeb\xbcz\xcd\
\xf9\xddn\xc9\xf3\xb0\x9b5[mI\x114\x8c\x19\x17\ \x17\x9c\xe0\xcc\x9a\xece\x0b\x16-\x0a\x91\xd1\xe0\xaa\x8d\
\x97\xf2a\xa3\xb2\xa0_3`\xd0\x91\xbb\x14\x13\x12c\ \xb7\xcd\xe2),\xbb\xcaL6\xb6\xf1\xba%\xd7\xc5N\
\x8e\x05\xe3\xfd\x96\xae\x19\xf7\x92~\x95,3i\x02\xed\ \xdal\xb5%E\xd0 b\x5c\x5c\xc9\xf7\x92\xca\x82n\
\x7f\xe0V0W\xad\xe8z\xa0\x11\x1fR;\x86\x17_\ M\x9fAGnS\x8cI\x849\x16\x8cw[\xbaa\
\xbf\x16^f/\xa8a;\xad\x824\xf1u1\x9a\x09\ \xdc\x8d\xbbU\xd2\xcc\xa41\xb4\xff\x9e[\xc1\xb8\xb6\xa2\
\xbd\xe38Z3\xd9\x98f\xf7\x1bd[fX(\x85\ \xeb\x9eF<H\xed\x08\x1e\x92\xddZx\x99>\xcc\xfa\
\x88O\xe3\xa8u\x825\x84\xe6\xfcZ\xaf\x89\xee\xa9=\ \xed\xb4\xf2\x93\xd8\xd3\xc5h&\xf4\x8e\xe3h\xccdc\
\xaf,#\x94\xde\xea\xaa[\x82T_\xd0\xb8\xd5\x18\xd4\ H\xdeo>n\x18\x8d\xa1\x14B>\x8e\xc2\xc6\xc1\xd8\
>\x8dc\xa0\xad\x15\x85\x17\xfb\x8a\xb3\xd0\x92\x1bT\x05\ \x10\x9acq\xb5&\xda\x1f\x03Ye\x19\xa1tVW\
=\xdbA\xfdH\x9c\xfb\xf1\xd7\x853\xea\x01\xf4\xe1\x7f\ \xd5\x12\xa4\xfa\x82F\x8d\xc6\xa0\xf6i\x14\x01\x8dV\xd4\
{\xf0\xabH\xc6\xc4\x7f\xe7\x0e\xbb \xd7\xc5<\x1a\xd8\ 'h\xc5Y\x80\xe4\x06eA\xc7vP?\x12\xe7n\
\xca~\x17\xdc\xa6t\x18\xe0\xca\xd2p\xa8\x95\x9d\x94\xcb\ \xfcu\xe1\x0cz\x00]\xf8\xdf\x1d\xfc*\x92!\xf1\xdf\
\x11z\x00\xae\xaa\xeb\x8c\x06+L~\xb0\xc4\xef\xc2\xb5\ \xb9\xc3.\xc8u1\x0f\x06\xb6\xb2\xdf\x06\xb7)\xed\x07\
\x22\x1e\x06\xac65\x1cYm\xc8\xd2\xc9\x9c\xe5\xb0}\ \xb8\xb2\xd4\x1fje'\xe1r\x84\xee\x81\xab\xea:\x83\
\x95R\xf7\xb2\xe0\xdcJB\xab\x8bp4\xe0a\x16\xb8\ \xc1\x0a\x93\x1f,\xf1\xdap-\x89\xfb\x01\xabM\xf5G\
L\xba`7\x84\xc3@\x97A\x0c\x87|\x97\x99]X\ V\x1bB:\x99\xd3\x1c\xb6\xae\x13\xea\x5c\xe5\x9c;I\
\x96\x13\xc3hx\x16[t\x82Z\xd7\x18\x86lam\ hu\x11\x0e\x06<\xcc\x02Wq\x1b\xec\x86\xb0\x1f\xe8\
8\xba\xa5c\x105\xce\x1e\xa9\x96\x8f\x97K*\x06 \ 2\x88\xfe\x90\xef2\xb3\x0b\xcbbb\x18\x0c\xcf|\x8b\
^\x8cE\xa3\x01\x9e\xed\xd0\x05u)\x1b\x06r\x1e\xc5\ VP\xab\x1a\xfd\x90\xcd\xad\xf5G\xb7p\x0c\xa2\xc6\xe9\
m4\x0d\x8ff\xaf\x13K0+\x84\x07\xf1n\xc3\xbb\ #\x15yx\xb9\xa4\xa2\x07\xe2\xf9X4\x18\xe0\xe9\x0e\
\x10V#\xe5h\xf0^\xa5$\xc9\xb6\xe9\x82\xb8*\x1f\ mP\x17\xb2~ gQ\xdcE\xd3pi\xfa:A\
\x06\xb3\x0ce8\xc6\xa0\x12Y\xd2\x96\xe5Q\xf0\xb6\xef\ \x82\xa1\x00\x1e\xc4\xbb\x0d\xefBX\x8d\x94\x83\xc1{\x9d\
\x16\xdd\xd91\xe4/\xb0\xe3\xc1{N7\xd8#\x9b.\ \x908\xdd\xa6\x0d\xe2\xb2\xbc\x1f\xcc2\x94\xfe\x18\x83J\
t+\xe2\x81\x8d\xa2\xf9/\xca\xc7u\x09i\xc7\x8a\x98\ \x88\xa4-\xe4R\xf0\xb6\xeb\x16\xdd\xd91\xe4\x0f\xbb\xc3\
\xd83k\x8dG\x0a\xca\xe1I\xa4\x0e\x8d\xdc \xf5\x88\ \xc1{N7\xd8%\x9b6tK\xe2\x9e\x8d\xa2\xfe\xe7\
\x1c\xba\x85\xfc\x0b\xd1\x04\xcd\x96k\xcd\x15\x08\x00\x88\x88\ \xcc\xc7u\x09i\x07\x85L\xec\x99\xb5\xc6#\xc5\xce\xe0\
:\x17\x90\x22;\xa5\xce\xc1\x7f\xdc\x00'-\ \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\ \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\
@@ -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\ \x02\xbf&;^~3\xb09K\xae\xa5$\xcaR\xec\
\xec1\xfc\x7f\x89E]\xa3\xc9\xb4-\xf9\x00\x00\x00\x00\ \xec1\xfc\x7f\x89E]\xa3\xc9\xb4-\xf9\x00\x00\x00\x00\
IEND\xaeB`\x82\ 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<T\xe4S\x15\
\xe2h 4\xe2\x01\x16\x05\xa5\xca\x1b\xd04\xe6\x19\x7f\
W$\x11\xee\xf2\x94\xebDn\x91\x0cf\xcc\x9bM\x09\
\x9d[e\x1b\x15\x05\xa9N3\xf4\xf7wg4\xd6M\
\x03\xb8\xfe\x09l\xc5l\x15\xd31X\xceB\x5c\x1b|\
\xe6\xb5\x09i\xd0d}\x0c~`\xf7\xc2\xe0\xa8\x0e\xba\
13p\x9cv\x0bD8\xba\xc1jm\x05Yi\x01\
\xec\x84\x85N\x96g\x05\xbbF\xfa\xe8\xed\xfb\xcf\xee$\
\xdd\xfb!JT\xa0\xd7\xf0\x81e\xb6\xa1\x8e\xd7\xe0\x05\
e\x13\xd4+\x8fC\x8fq\x1b\x0c\x8d\xfa\x17t,c\
6\x1cV\x0b\xd8m=`\xb55\x80\xcdzi~[\
\x0cJ)M!W\xf6A2\xff\xc0\xd9\x13\xb4K\x5c\
\x0d\xe5\xd6W\xa0;\xb0\x0c\xc6\x02*\x98%G\xb8\x05\
\x5c\xce\x1epE\x96\x82(\xcc\xbb?\x8dt\xbd\x03\x12\
\x03\xb2\x9d\x81`a\x84_\xf9\x12\xb4\xb2\x94\xb8O\xc3\
^\xf5\xf6\x8dP31\xe7\x90(\x12X\xbe\xac\xad.\
6\xaa\xb1Z\x16\xfd@\xd1;#t\xe2\x09Xi\x84\
\x1e\x9a.\xc2\x8a\x92}\x02C\xac\xd8<\xc2\xf9\xdf\x1d\
\x99\x0d7m\xa71E\xff\x08\x00\xa7\xf8p\xfa\xc1\xc6\
\x18\xdf\x1e\xbbdhXMx\x87r6h\xee\x93/\
\x8c;'eeeN|f \xbb\x91\xa38\x0fk\
,\xf6\xeb\x1b\xb0\x05-\xa7z]Xd|\xba\xd6\x09\
mn;\xe9By\x10\x99\xf7\xe7o\x93\xdc\x89|\x8e\
\x03\xe0A\x1fF\xe6\xb5\x10\x0d\x13\x7f9V\xe4[`\
\xa2K\xb8\x1c\xb6H\x00\xbcj\xc7`\xa2\x91\x86'e\
\xde\x227\x90\xdb\x91k\xc6\x8f\x00A\xf0/\x14'\x99\
\xdff\x12\xf0\x1f\xb0\x09\x0a\x0b\x92\xcd\x92:\x09bJ\
\xe6M\x8eG\x01|`\x18999\xf4\xff\xffW\xbc\
\x04\xe0\xdf\xa6?\x00rj\xad{\x92\x06\x13\xe9\x00\x00\
\x00\x00IEND\xaeB`\x82\
\x00\x00r\x8b\ \x00\x00r\x8b\
\x89\ \x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -37526,6 +37606,88 @@ 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\x04\xfa\
\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\x04wID\
ATX\x85\xad\x97\xedoSu\x14\xc7?\xe7\xd7\xee\
vm\xf7\xc0\x96V6(2&t\xe1!\x13XP\
\xe2\x0b\x0c\xc4\xf8\x82%\x9a\xc0\xb2\xe8\x1b\x12\xdf\xf9\xc2\
\xc4\x17\xbc\xc0\x98H4\xfaB\x125\xfc\x11*\xc6g\
\x12\x07!F\x88\x04dA\xa4\x93eA\x180\x0cc\
\xc2x\xd8d\xeb\xd6\xdb\xdb{\x8f/\x5cG\xb7\xf6\xb6\
\xcd\xf4\xfb\xaa=\xe7\xdcs>\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\ \x00\x00kN\
\x89\ \x89\
PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
@@ -46350,6 +46512,10 @@ qt_resource_name = b"\
\x03v\xc2\x07\ \x03v\xc2\x07\
\x00q\ \x00q\
\x00u\x00e\x00s\x00t\x00i\x00o\x00n\x00.\x00p\x00n\x00g\ \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\ \x00\x06\
\x07\x84WG\ \x07\x84WG\
\x00q\ \x00q\
@@ -46374,6 +46540,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\x08\
\x09\xc5X\xc7\
\x00u\
\x00s\x00e\x00r\x00.\x00p\x00n\x00g\
\x00\x0b\ \x00\x0b\
\x0e|&g\ \x0e|&g\
\x00w\ \x00w\
@@ -46415,99 +46585,103 @@ 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\x00/\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\x82\xd3\xbeg\ \x00\x00\x01r\x8a17\x84\
\x00\x00\x03|\x00\x00\x00\x00\x00\x01\x00\x0838\ \x00\x00\x03\x96\x00\x00\x00\x00\x00\x01\x00\x088\x10\
\x00\x00\x01ro \xc0+\ \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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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!\ \x00\x00\x01rn\x7f\xe8!\
" "

View File

@@ -111,6 +111,7 @@ class Ui_MainWindow(object):
self.treeView.setSizePolicy(sizePolicy) self.treeView.setSizePolicy(sizePolicy)
self.treeView.setMaximumSize(QSize(400, 16777215)) self.treeView.setMaximumSize(QSize(400, 16777215))
self.splitter_2.addWidget(self.treeView) self.splitter_2.addWidget(self.treeView)
self.treeView.header().setVisible(False)
self.splitter = QSplitter(self.splitter_2) self.splitter = QSplitter(self.splitter_2)
self.splitter.setObjectName(u"splitter") self.splitter.setObjectName(u"splitter")
self.splitter.setOrientation(Qt.Vertical) self.splitter.setOrientation(Qt.Vertical)

View File

@@ -69,6 +69,9 @@
<height>16777215</height> <height>16777215</height>
</size> </size>
</property> </property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget> </widget>
<widget class="QSplitter" name="splitter"> <widget class="QSplitter" name="splitter">
<property name="orientation"> <property name="orientation">

View File

@@ -1,6 +1,5 @@
TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT
--|--|--|--|-- --|--|--|--|--
HL|薛兆丰经济学讲义 |薛兆丰|2020/1/13 8:11:05|么到底什么叫边际?边际就是“新增”带来的“新增”。 例如,边际成本就是每新增一个单位产品所需要付出的新增成本;边际收入是每多卖一个产品能够带来的新增收入;边际产量是每新增一份投入所带来的新增产量;边际效用是每消耗一个单位的商品所能带来的新增享受。 HL|薛兆丰经济学讲义|薛兆丰|2020/1/13 8:11:05|么到底什么叫边际?边际就是“新增”带来的“新增”。 例如,边际成本就是每新增一个单位产品所需要付出的新增成本;边际收入是每多卖一个产品能够带来的新增收入;边际产量是每新增一份投入所带来的新增产量;边际效用是每消耗一个单位的商品所能带来的新增享受。
HL|薛兆丰经济学讲义 |薛兆丰|2020/1/30 10:23:58|一个国家很大,贫富有差距,并非每个学校和家长都能负担得起这样标准的校车。标准太高,就会逼着很多学校,尤其是农村的学校放弃提供校车,家长们就只能使用安全性能更低的交通工具,比如自己骑自行车或雇用黑车等,结果是孩子们享受到的安全保障反而降低了。 HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:23:58|一个国家很大,贫富有差距,并非每个学校和家长都能负担得起这样标准的校车。标准太高,就会逼着很多学校,尤其是农村的学校放弃提供校车,家长们就只能使用安全性能更低的交通工具,比如自己骑自行车或雇用黑车等,结果是孩子们享受到的安全保障反而降低了。
NT|薛兆丰经济学讲义 |薛兆丰|2020/1/30 10:26:31|山寨 假货 问题 NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:26:31|山寨 假货 问题
HL|薛兆丰经济学讲义 |薛兆丰|2020/1/30 10:29:41|为了克服信息不对称,建立互信,人类社会构想出了各种各样有趣的解决方案,从重复交易到第三方背书,从质保、延保,再到收益共享。此外,还有三种非常接近的建立信任的办法:付出沉没成本、给出人质或者给出抵押。

View File

@@ -18,8 +18,13 @@
========== ==========
薛兆丰经济学讲义 (薛兆丰) 薛兆丰经济学讲义 (薛兆丰)
- 您在位置 #4284-4286的标注 | 添加于 2020年1月30日星期四 上午10:23:58 - 您在位置 #4284-4286的标注 | 添加于 2020年1月30日星期四 上午10:23:58
一个国家很大,贫富有差距,并非每个学校和家长都能负担得起这样标准的校车。标准太高,就会逼着很多学校,尤其是农村的学校放弃提供校车,家长们就只能使用安全性能更低的交通工具,比如自己骑自行车或雇用黑车等,结果是孩子们享受到的安全保障反而降低了。
==========

View File

@@ -1,11 +1,11 @@
import unittest import unittest2
from collections import defaultdict from collections import defaultdict
from kman import * from kman import *
class TestKman(unittest.TestCase): class TestKman(unittest2.TestCase):
# initial # initial
def setUp(self): def setUp(self):
CLIPPATH = './test.data' CLIPPATH = './test.data'
@@ -13,9 +13,15 @@ class TestKman(unittest.TestCase):
LOG2FILE = 1 LOG2FILE = 1
DELIMITER= '|' DELIMITER= '|'
self.km = kMan()
global t_bm_sec global t_bm_sec
global t_hl_sec global t_hl_sec
global t_nt_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 global t_books
t_bm_sec = ["""另一半中国史 (高洪雷) t_bm_sec = ["""另一半中国史 (高洪雷)
""", \ """, \
@@ -25,22 +31,55 @@ class TestKman(unittest.TestCase):
""", \ """, \
"""- 您在位置 #1408-1410的标注 | 添加于 2020年1月13日星期一 上午8:11:05 """- 您在位置 #1408-1410的标注 | 添加于 2020年1月13日星期一 上午8:11:05
""", \ """, \
"""边际就是“新增”带来的“新增”。
"""]
t_nt_sec = ["""薛兆丰经济学讲义 (薛兆丰)
""", \
"""- 您在位置 #4286 的笔记 | 添加于 2020年1月30日星期四 下午10:26:31
""", \
"""山寨 假货 问题
"""]
t_hl_sec2 = ["""枪炮、病菌与钢铁 : 人类社会的命运 (世纪人文系列丛书·开放人文) (贾雷德·戴蒙德)
""",\ """,\
"""- 您在位置 #4267-4268的标注 | 添加于 2020年1月29日星期三 上午12:42:32 """- 您在位置 #4267-4268的标注 | 添加于 2020年1月29日星期三 上午12:42:32
""",\ """,\
"""从柏拉图到马克思的所有政治理论家都提出过这个问题xxxx"""] """从柏拉图到马克思的所有政治理论家都提出过这个问题xxxx"""]
"""- 您在位置 #4286 的笔记 | 添加于 2020年1月30日星期四 下午10:26:31 t_hl_sec3 = ["""枪炮、病菌与钢铁(贾雷德·戴蒙德)""",\
"""- 您在位置 #4267-4268的标注 | 添加于 2020年1月29日星期三 上午12:42:32""",\
"""从柏拉图到马克思的所有政治理论家都提出过这个问题yyyy"""] """从柏拉图到马克思的所有政治理论家都提出过这个问题yyyy"""]
"""山寨 假货 问题 t_hl_sec4 = ["""枪炮、病菌与钢铁 : 人类社会的命运 (世纪人文系列丛书·开放人文) (贾雷德·戴蒙德)""",\
"""- 您在位置 #4267-4268的标注 | 添加于 2020年1月29日星期三 上午12:42:32""",\
"""从柏拉图到马克思的所有政治理论家都提出过这个问题zzzz"""] """从柏拉图到马克思的所有政治理论家都提出过这个问题zzzz"""]
t_books = defaultdict(dict) t_books = defaultdict(dict)
def cre_tbooks(self): def cre_tbooks(self):
# parsing section & fill data structure # 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)
self.t_num_nt += 1 self.t_num_nt += 1
bn = t_secd['bookname'] 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][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_nt_sec,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_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'] bn = t_secd['bookname']
t_books[bn]['author'] = t_secd[bn]['author'] 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_books[bn][str(self.t_num_nt)] = t_secd[bn][str(self.t_num_nt)]
@@ -48,13 +87,13 @@ class TestKman(unittest.TestCase):
self.t_num_nt += 1 self.t_num_nt += 1
t_secd = self.km.parse_section(t_hl_sec4,self.t_num_nt) t_secd = self.km.parse_section(t_hl_sec4,self.t_num_nt)
t_books[bn]['2'] = t_secd[bn]['2'] bn = t_secd['bookname']
t_books[bn]['author'] = t_secd[bn]['author'] 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_books[bn][str(self.t_num_nt)] = t_secd[bn][str(self.t_num_nt)]
return t_books t_secd.clear()
return t_books return t_books
def test_parse_section(self):
# test function parse_section # test function parse_section
def test_parse_section(self): def test_parse_section(self):
# parsing section & fill data structure # parsing section & fill data structure
@@ -67,10 +106,10 @@ class TestKman(unittest.TestCase):
self.assertEqual(bn,'薛兆丰经济学讲义') self.assertEqual(bn,'薛兆丰经济学讲义')
self.assertEqual(t_secd[bn]['author'],'薛兆丰') self.assertEqual(t_secd[bn]['author'],'薛兆丰')
self.assertEqual(t_secd[bn]['1']['type'],'HL') self.assertEqual(t_secd[bn]['1']['type'],'HL')
self.assertEqual(t_secd[bn]['1']['week'],'星期一') self.assertEqual(t_secd[bn]['1']['position'],'1408-1410')
self.assertEqual(t_secd[bn]['1']['day'],'2020年1月13日') self.assertEqual(t_secd[bn]['1']['day'],'2020年1月13日')
self.assertEqual(t_secd[bn]['1']['week'],'星期一') self.assertEqual(t_secd[bn]['1']['week'],'星期一')
self.assertEqual(t_secd[bn]['1']['content'],'边际就是“新增”带来的“新增”。\n') self.assertEqual(t_secd[bn]['1']['meridiem'],'上午')
self.assertEqual(t_secd[bn]['1']['time'],'8:11:05') self.assertEqual(t_secd[bn]['1']['time'],'8:11:05')
self.assertEqual(t_secd[bn]['1']['content'],'边际就是“新增”带来的“新增”。\n') self.assertEqual(t_secd[bn]['1']['content'],'边际就是“新增”带来的“新增”。\n')
t_books[bn]['author'] = t_secd[bn]['author'] t_books[bn]['author'] = t_secd[bn]['author']
@@ -86,7 +125,7 @@ class TestKman(unittest.TestCase):
self.assertEqual(t_secd[bn]['2']['position'],'4286') self.assertEqual(t_secd[bn]['2']['position'],'4286')
self.assertEqual(t_secd[bn]['2']['day'],'2020年1月30日') self.assertEqual(t_secd[bn]['2']['day'],'2020年1月30日')
self.assertEqual(t_secd[bn]['2']['week'],'星期四') self.assertEqual(t_secd[bn]['2']['week'],'星期四')
self.assertEqual(t_secd[bn]['2']['content'],'山寨 假货 问题\n') self.assertEqual(t_secd[bn]['2']['meridiem'],'下午')
self.assertEqual(t_secd[bn]['2']['time'],'10:26:31') self.assertEqual(t_secd[bn]['2']['time'],'10:26:31')
self.assertEqual(t_secd[bn]['2']['content'],'山寨 假货 问题\n') self.assertEqual(t_secd[bn]['2']['content'],'山寨 假货 问题\n')
t_books[bn]['author'] = t_secd[bn]['author'] t_books[bn]['author'] = t_secd[bn]['author']
@@ -94,7 +133,7 @@ class TestKman(unittest.TestCase):
t_secd.clear() t_secd.clear()
# test drop_duplicate # test drop_duplicate
t_secd = parse_section(t_hl_sec,3) def test_drop_duplicate(self):
t_books = self.cre_tbooks() t_books = self.cre_tbooks()
t_secd = self.km.parse_section(t_hl_sec,3) t_secd = self.km.parse_section(t_hl_sec,3)
bn = t_secd['bookname'] bn = t_secd['bookname']
@@ -104,31 +143,58 @@ class TestKman(unittest.TestCase):
self.assertIsInstance(t_books_du[bn]['3'],dict) self.assertIsInstance(t_books_du[bn]['3'],dict)
try: try:
except KeyError as keyerror: t_books_du = self.km.drop_duplicate(t_books_du)
t = t_books_du[bn]['3'] t = t_books_du[bn]['3']
except KeyError as keyerror: except KeyError as keyerror:
print("与预期匹配sidx 3 重复被删除,抛出: %s" % 'keyerror') print("与预期匹配sidx 3 重复被删除,抛出: %s" % 'keyerror')
t_secd.clear() t_secd.clear()
def test_format_time(self):
# test function format_time() # test function format_time()
def test_format_time(self): def test_format_time(self):
self.assertEqual(t_ds, '2020/1/13 20:11:05') t_ds = '2020年1月13日 星期一 下午 8:11:05'
t_ds = self.km.format_time(t_ds) t_ds = self.km.format_time(t_ds)
self.assertEqual(t_ds, '2020/1/13 20:11:05') self.assertEqual(t_ds, '2020/1/13 20:11:05')
# test function format_data # test function format_data
t_out = format_data(t_books, ft='MD') def test_format_data(self):
self.assertEqual(t_out[0], 'TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT') t_books = self.cre_tbooks()
self.assertEqual(t_out[1], '--|--|--|--|--')
self.assertEqual(t_out[2], 'HL|薛兆丰经济学讲义 |薛兆丰|2020/1/13 8:11:05|边际就是“新增”带来的“新增”。\n')
t_out = self.km.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[0], 'TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT')
self.assertEqual(t_out[1], '--|--|--|--|--') self.assertEqual(t_out[1], '--|--|--|--|--')
t_books = self.cre_tbooks() self.assertEqual(t_out[2], 'HL|薛兆丰经济学讲义|薛兆丰|2020/1/13 8:11:05|边际就是“新增”带来的“新增”。\n')
t_out.clear() t_out.clear()
def test_add_note_to_highlight(self): def test_add_note_to_highlight(self):
t_books = self.cre_tbooks()
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 = 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 # by bookname
bn = self.km.filter_clips(t_books, '枪炮、病菌与钢铁 : 人类社会的命运 (世纪人文系列丛书·开放人文)', 1) bn = self.km.filter_clips(t_books, '枪炮、病菌与钢铁 : 人类社会的命运 (世纪人文系列丛书·开放人文)', 1)
print('filter 1 \n',bn) print('filter 1 \n',bn)
@@ -149,17 +215,17 @@ class TestKman(unittest.TestCase):
def test_json2dict(self): def test_json2dict(self):
pass pass
def test_import_clips(self): def test_import_clips(self):
pass
""" """
# clean # clean
def tearDown(self): def tearDown(self):
pass pass
if __name__ == '__main__': if __name__ == '__main__':
""" """
suite = unittest2.TestSuite () suite = unittest2.TestSuite ()
suite.addTest(TestKman('test_format_data')) suite.addTest(TestKman('test_parse_section'))
suite.addTest(TestKman('test_format_time')) suite.addTest(TestKman('test_format_time'))
suite.addTest(TestKman('test_format_data')) suite.addTest(TestKman('test_format_data'))

106
tthread.py Normal file
View File

@@ -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_())

2
xx
View File

@@ -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}} {"\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}}