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