Files
kman/kmanapp.py
2020-06-07 17:48:28 +08:00

423 lines
15 KiB
Python

#########################################################
## @file : kmanapp.py
## @desc : kindle note managerment tool GUI
## @create : 2020/06/03
## @author : Chengan
## @email : douboer@gmail.com
#########################################################
import sys
from time import sleep
import pandas as pd
from PySide2.QtWidgets import *
from PySide2.QtCore import (QCoreApplication, QDate, QDateTime, QMetaObject,
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 mainwindow import Ui_MainWindow
from kman import *
# import binary resource file(kmanapp_rc.py)
import kmanapp_rc
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)
# create ui and initial it
ui = Ui_MainWindow()
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.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))
ui.actionimportlocal.triggered.connect(lambda: self.import_local())
ui.actionconfig.triggered.connect(lambda: self.config())
ui.actionwords.triggered.connect(lambda: self.words())
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.refresh())
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.setColumnWidth(0, 40) # type
ui.tableView.setColumnWidth(2, 50) # author
ui.tableView.selectRow(0)
def add_ui_component(self):
self.ui.searchComboBox.addItems(['ALL','bookname','content','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)
# initial 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>********************************************************</span><br><br>
<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 not ONLY_TEST: # XXXXXXXXXXXXX
model = QStandardItemModel()
rootItem = model.invisibleRootItem()
idx = 0
for i in range(4):
idx += 1
item = QStandardItem('item {}'.format(idx))
rootItem.appendRow(item)
icon = QIcon()
icon.addFile(u":/icons/book_open_bookmark.png", QSize(), QIcon.Normal, QIcon.Off)
item.setIcon(icon)
if i==0:
parentItem = item
icon = QIcon()
icon.addFile(u":/icons/emblem_library.png", QSize(), QIcon.Normal, QIcon.Off)
item.setIcon(icon)
parentItem.appendRows([QStandardItem('append rows {}'.format(i+idx)) for i in range(5)])
if i==1:
parentItem = item
for i in range(5):
idx += 1
item = QStandardItem('type item {}'.format(i+idx))
#item.setEnabled(False)
item.setEditable(False)
icon = QIcon()
icon.addFile(u":/icons/register.png", QSize(), QIcon.Normal, QIcon.Off)
item.setIcon(icon)
parentItem.appendRow(item)
if i==3:
parentItem = item
for i in range(5):
idx += 1
item = QStandardItem('another item {}'.format(i+idx))
#item.setEnabled(False)
item.setEditable(False)
icon = QIcon()
icon.addFile(u":/icons/book_open.png", QSize(), QIcon.Normal, QIcon.Off)
item.setIcon(icon)
parentItem.appendRow(item)
self.ui.treeView.setModel(model)
"""
def convert_to_panda(self, seclist):
pdframe = pd.DataFrame(seclist, \
columns = ['Type','Bookname','Author','Position','Date','Content'])
return pdframe
def tabledata_update_slot(self, s):
print('call tabledata_update_slot() {}'.format(s))
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
description = item.accessibleDescription()
print(description, item.text())
# filter clips
self.filter_data = None
if description in ['root', 'bookname', 'author']:
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)
else: # author leaf
info = re.split(r'\s+',item.text())[0]
self.filter_data = self.km.filter_clips(self.books, info, 2)
data = self.convert_to_panda(self.filter_data)
self.ui.tablemodel= nTableModel(data)
self.ui.tableView.verticalHeader().hide()
self.ui.tableView.setModel(self.ui.tablemodel)
def table_item_clicked(self, index):
if index.isValid():
row = index.row()
stype = index.sibling(row, 0).data()
sbookname = index.sibling(row, 1).data()
sauthor = index.sibling(row, 2).data()
sposition = index.sibling(row, 3).data()
stime = index.sibling(row, 4).data()
scontent = index.sibling(row, 5).data()
else: return
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>********************************************************</span><br><br>
<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))
def search_button_clicked(self):
print( 'call search_button_clicked()' )
def search_scope_change(self,t):
p = {0:'ALL',1:'TITLE',2:'AUTHOR',3:'CONTENT'}
s = self.ui.searchLineEdit.text()
#print(self.books)
#print(search_clip(self.books,s,'ALL',p[t]))
print('call search_scope_change()')
## XXXX
def check_kindle_status(self):
while self.flag:
self.show_status_info()
sleep(2)
def show_status_info(self):
""" show status information on statusbar
Args:
conn: 1 if kindle is connected else 0
Return:
conn
"""
status = self.km.status_info()
self.ui.statusbar.showMessage(status[0],0)
self.ui.status_label.setText(status[1])
if not self.km.status:
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)
def import_local(self):
fn, ft = QFileDialog.getOpenFileName(self,
"choose file to import",
'./', # 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('root')
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('bookname')
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('bleaf')
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('author')
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('aleaf')
author_item.appendRow(item)
self.ui.treeView.setModel(self.ui.model)
self.ui.treeView.expandAll()
def config(self):
print("call slot config()")
pass
def words(self):
print("call slot words()")
pass
def statistic(self):
print("call slot statistic()")
pass
def homepage(self):
print("call slot homepage()")
pass
def about(self):
self.messagebox(ico=1, info='\n'+ \
' kindle management tool \n\n' + \
' v1.0.4\n\n' + \
' Author: chengan\n\n' + \
' douboer@gmail.com')
print("call slot about()")
pass
def refresh(self):
print("call slot refresh()")
pass
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(info)
msgBox.setInformativeText("")
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 Martin Fitzpatrick ^_^
# https://www.learnpyqt.com/courses/model-views/qtableview-modelviews-numpy-pandas/
class nTableModel(QAbstractTableModel):
tabledata_update = Signal(str)
def __init__(self, data):
super(nTableModel, self).__init__()
self._data = data
def data(self, index, role):
if role == Qt.DisplayRole:
value = self._data.iloc[index.row(), index.column()]
self.tabledata_update[str].emit('{} {}'.format(index.row(),index.column()))
return str(value)
def rowCount(self, index):
return self._data.shape[0]
def columnCount(self, index):
return self._data.shape[1]
def headerData(self, section, orientation, role):
# section is the index of the column/row.
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return str(self._data.columns[section])
if orientation == Qt.Vertical:
return str(self._data.index[section])
if __name__ == "__main__":
app = QApplication(sys.argv)
kmw = kmanWindow()
#kmw.resize(900, 600)
kmw.showFullScreen()
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_()