之前用python写了个小软件https://blog.51cto.com/ikezcn/2142638
说实话用tk的话界面确实不好看,所以需要改进,看了pyqt5的介绍想要试一下,对于上次写的软件来说这里只是更改了界面而已。上面的程序不再更新。
使用的软件:
python3.6
pyqt5 安装:pip insatll pyqt5
界面:
代码(干货来了,之后会不定时更新):
# -*- coding: utf-8 -*- #v2.0 2018-08-30更新 #v2.1 2018-08-31更新 #现在可以使用pyinsatller打包了,新增了样式表,就算没有数据库也会自己创建并增加数据表,隐藏列名 #v2.2 2018-09-05更新 #增加了左边一列用来做查询条件,双击就可以搜索. #右键右边的路径可以添加搜索条件. #最后遇到一个问题:多次使用setFilter会造成模型出现问题导致无法写入数据库,这里就直接跳过模型自己插入数据库 #v2.3 2018-09-06更新 #增加刷新菜单,显示所有路径,修复bug #v2.4 2018-09-11更新 #小修小补 import sys,os from PyQt5 import sip from PyQt5.QtCore import Qt,QVariant,QFile,QIODevice,QItemSelectionModel from PyQt5.QtSql import QSqlDatabase,QSqlTableModel,QSqlQuery,QSqlRecord from PyQt5.QtWidgets import QPushButton,QLabel,QCheckBox,QHBoxLayout,QVBoxLayout,QWidget,QMenu,QMainWindow,QMessageBox,QHeaderView,QAction, qApp, QApplication, QDesktopWidget , QGridLayout,QTableView,QAbstractItemView from PyQt5.QtGui import QIcon,QContextMenuEvent import win32api import win32con class Icon(QMainWindow): def __init__(self,model,sqlite,query,view,sview,smodel): super().__init__() self.model = model self.sqlite = sqlite self.query = query self.view = view self.sview = sview self.smodel = smodel if self.sqlite.open():#如果没有找到数据库文件就自动创建 self.query.exec("select count(*) from sqlite_master where type='table' and name='lj'") if self.query.next(): if self.query.value(0) == 0: self.query.prepare("CREATE TABLE lj(id integer primary key,lj text not NULL,isdel BOOLEAN DEFAULT 0)") if not self.query.exec(): QMessageBox.information(self,"数据表创建失败",self.query.lastError().text()) self.query.prepare("CREATE TABLE cxtj(cxtj text not NULL)") if not self.query.exec(): QMessageBox.information(self,"数据表创建失败",self.query.lastError().text()) else: QMessageBox.information(self,"程序将关闭",self.sqlite.lastError().text()) sys.exit(app.exec_()) self.setAcceptDrops(True) self.statusBar().showMessage('Reday') delAction = QAction("&删除", self) delAction.setShortcut("Ctrl+D") delAction.setStatusTip("删除选定的行") delAction.triggered.connect(self.delrow) deltableAction = QAction("&全部删除", self) deltableAction.setStatusTip("删除所有的行") deltableAction.triggered.connect(self.deltable) vacuumAction = QAction("&整理数据库", self) vacuumAction.setShortcut("Ctrl+D") vacuumAction.setStatusTip("让数据库变小") vacuumAction.triggered.connect(self.vacuum) selectAction = QAction("&刷新", self) selectAction.setShortcut("Ctrl+R") selectAction.setStatusTip("显示所有路径") selectAction.triggered.connect(self.selectAll) menubar = self.menuBar() menubar.addAction(delAction) setMenu = menubar.addMenu('&设置') setMenu.addAction(vacuumAction) setMenu.addAction(deltableAction) menubar.addAction(selectAction) widget = QWidget() QMainWindow.setCentralWidget(self,widget) layout = QHBoxLayout(self) #QHBoxLayout水平排列 layout.addWidget(self.sview) layout.addWidget(self.view) layout.setStretchFactor(self.sview,1) #显示的比例 1:3 layout.setStretchFactor(self.view,3) self.table1() self.stable() widget.setLayout(layout) self.resize(800, 600) self.center() #居中 self.setWindowTitle("快捷方式") #self.setWindowIcon(QIcon("icon.png")) self.show() def selectAll(self): self.model.setFilter("1=1") def delrow(self): if self.view.selectionModel().hasSelection(): self.query.prepare("delete from lj where lj=?") self.query.bindValue(0,QVariant(self.view.currentIndex().data())) if self.query.exec(): self.model.setFilter("1=1") else: QMessageBox.information(self,"删除错误",self.model.lastError().text()) if self.sview.selectionModel().hasSelection(): self.query.prepare("delete from cxtj where cxtj=?") self.query.bindValue(0,QVariant(self.sview.currentIndex().data())) if self.query.exec(): self.smodel.setFilter("1=1") else: QMessageBox.information(self,"删除错误",self.smodel.lastError().text()) def deltable(self): ret = QMessageBox.question(self,"提示!","是否删除全部路径",QMessageBox.Ok | QMessageBox.Cancel,QMessageBox.Cancel) if 0x00000400 == ret: #OK self.query.prepare("delete from lj") if not self.query.exec(): QMessageBox.information(self,"删除错误",self.model.lastError().text()) self.query.prepare("delete from cxtj") if not self.query.exec(): QMessageBox.information(self,"删除错误",self.smodel.lastError().text()) def vacuum(self): self.query.exec("VACUUM") def center(self): #窗口居中 #QtGui.QDesktopWidget提供了关于用户桌面的信息,包括屏幕尺寸。 qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def sql(self): sqlite = QSqlDatabase.addDatabase("QSQLITE") sqlite.setDatabaseName("db") return sqlite def table1(self): self.model.setTable("lj") #self.model.setFilter("isdel = 0") #where 条件 self.model.setSort(1,Qt.AscendingOrder) #按lj列排序 self.model.select() #self.model.setHeaderData(0, Qt.Horizontal, "ID") #self.model.setHeaderData(1, Qt.Horizontal,"路径") self.view.setModel(self.model) self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) #禁止对表格编辑 self.view.horizontalHeader().setStretchLastSection(True) #是否填满宽度 self.view.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.view.hideColumn(0)#隐藏列 self.view.hideColumn(2)#隐藏列 self.view.setSelectionBehavior(QAbstractItemView.SelectRows) #点击整行选中 #self.view.setAlternatingRowColors(True) #隔行自动变色 self.view.verticalHeader().setVisible(False)#隐藏表头 self.view.horizontalHeader().setVisible(False)#隐藏列名 self.view.doubleClicked.connect(lambda:self.slotRowDoubleClicked()) self.view.clicked.connect(lambda:self.singleSelect(view)) #view.setAcceptDrops(True) self.view.show() def slotRowDoubleClicked(self): try: modedata = self.view.currentIndex().data() win32api.ShellExecute(0,'open',str(modedata),'','',1) except (BaseException): QMessageBox.information(self,"错误!","路径不存在!") def stable(self): self.smodel.setTable("cxtj") #self.model.setFilter("isdel = 0") #where 条件 self.smodel.setSort(0,Qt.AscendingOrder) #按cxtj列排序 self.smodel.setHeaderData(0, Qt.Horizontal,"搜索条件") self.smodel.select() self.sview.setModel(self.smodel) self.sview.setEditTriggers(QAbstractItemView.NoEditTriggers) #禁止对表格编辑 self.sview.horizontalHeader().setStretchLastSection(True) #是否填满宽度 self.sview.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.sview.setSelectionBehavior(QAbstractItemView.SelectRows) #点击整行选中 self.sview.verticalHeader().setVisible(False)#隐藏表头 self.sview.horizontalHeader().setVisible(False)#隐藏列名 self.sview.doubleClicked.connect(lambda:self.slotRowDoubleClickedS()) self.sview.clicked.connect(lambda:self.singleSelect(sview)) self.sview.show() def slotRowDoubleClickedS(self): modedata = self.sview.currentIndex().data() data = str(modedata) if self.sqlite.open(): self.model.setFilter("lj like '%" + data + "%'") #where 条件 else: QMessageBox.information(self,"程序将关闭",self.sqlite.lastError().text()) sys.exit(app.exec_()) def contextMenuEvent(self,event): #右键菜单 popAction = QAction(self) popAction.setText("创建搜索条件") popAction.triggered.connect(self.popMenu) popMenubar = QMenu(self) popMenubar.addAction(popAction) popMenubar.exec(self.cursor().pos()) def popMenu(self): i = 0 sdata = [] modedata = self.view.currentIndex().data() data = str(modedata) for txt in data.split("\\"): sdata.append((i,txt)) i = i + 1 self.cbWindow = cbWindow(sdata,self.sqlite,self.query,self.smodel) # def enableBorder(self, enable): # if enable: # self.setStyleSheet("MainWidget{border:3px solid #165E23}") # else: # self.setStyleSheet('') def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.acceptProposedAction() #self.enableBorder(True) else: event.ignore() def dragMoveEvent(self, event): if event.mimeData().hasUrls(): event.setDropAction(Qt.LinkAction) event.accept() else: event.ignore() #def dragLeaveEvent(self, event): #print('dragLeaveEvent...') #self.enableBorder(False) def dropEvent(self, event): #self.model.setFilter("1=1") if event.mimeData().hasUrls(): counts = -1 #record = self.model.record() # 遍历输出拖动进来的所有文件路径 for url in event.mimeData().urls(): self.query.prepare("select count(*) as c from lj where lj=?") #不加 as c会报错 pathStr = url.toLocalFile().replace('/','\\') self.query.bindValue(0,QVariant(pathStr)) if self.query.exec(): while self.query.next(): counts = self.query.value(0) if counts > 0: QMessageBox.information(self,"已存在!",pathStr) elif counts == 0: #record.setValue(1,QVariant(pathStr)) #lj列 #record.setValue(2,QVariant(0)) #isdel列 #self.model.insertRecord(-1,record) self.query.prepare("insert into lj(lj) values(?)") self.query.bindValue(0,QVariant(pathStr)) self.query.exec() elif counts == -1: return event.acceptProposedAction() self.model.setFilter("1=1") #print(self.model.lastError().text()) else: event.ignore() def singleSelect(self,lstView): if lstView == self.view: if self.sview.selectionModel().hasSelection(): self.sview.selectionModel().clearSelection() if lstView == self.sview: if self.view.selectionModel().hasSelection(): self.view.selectionModel().clearSelection() # for lstViewI in lstViews: # if lstViewI == lstView: # continue # # the check is necessary to prevent recursions... # if lstViewI.selectionModel().hasSelection(): # # ...as this causes emission of selectionChanged() signal as well: # lstViewI.selectionModel().clearSelection() def closeEvent(self,event): if self.sqlite.isOpen(): self.sqlite.close() self.close() class cbWindow(QWidget): def __init__(self,sdata,sqlite,query,smodel): super().__init__() self.sdata = sdata self.sqlite = sqlite self.query = query self.smodel = smodel self.cbStr = [] layout = QVBoxLayout() #items = [(0, 'Python'), (1, 'Golang'), (2, 'JavaScript'), (3, 'Ruby')] for id_, txt in self.sdata: checkBox = QCheckBox(txt, self) checkBox.id_ = id_ checkBox.stateChanged.connect(self.checkLanguage) #1 layout.addWidget(checkBox) self.lMessage = QLabel(self) layout.addWidget(self.lMessage) self.button1 = QPushButton("保存",self) self.button1.clicked.connect(self.checkbutton1) layout.addWidget(self.button1) self.setLayout(layout) self.setWindowTitle("选择搜索条件") self.resize(200, 300) self.show() def checkLanguage(self, state): checkBox = self.sender() if state == Qt.Unchecked: self.cbStr.remove((checkBox.id_, checkBox.text())) #print(self.cbStr) #self.lMessage.setText(u'取消选择了{0}: {1}'.format(checkBox.id_, checkBox.text())) elif state == Qt.Checked: self.cbStr.append((checkBox.id_, checkBox.text())) #print(self.cbStr) #self.lMessage.setText(u'选择了{0}: {1}'.format(checkBox.id_, checkBox.text())) def checkbutton1(self): counts = -1 if self.sqlite.isOpen(): for id_, txt in self.cbStr: self.query.prepare("select count(*) as c from cxtj where cxtj=?") self.query.bindValue(0,QVariant(txt)) if self.query.exec(): while query.next(): counts = query.value(0) if counts == 0: self.query.prepare("insert into cxtj(cxtj) values(?)") self.query.bindValue(0,QVariant(txt)) if self.query.exec(): self.smodel.setFilter("1=1") else: QMessageBox.information(self,"查询条件写入错误","查询条件写入错误") self.close() else: QMessageBox.information(self,"程序将关闭",self.sqlite.lastError().text()) sys.exit(app.exec_()) def closeEvent(self,event): self.close() if __name__ == "__main__": app = QApplication(sys.argv) qss = QFile("stylesheet.qss") #样式表 qss.open(QIODevice.ReadOnly) #样式表 app.setStyleSheet(str(qss.readAll(),encoding='utf-8')) #样式表 #样式表 http://doc.qt.io/qt-5/stylesheet-customizing.html #样式表 http://doc.qt.io/qt-5/stylesheet-reference.html (QTableView stylesheet) qss.close() #样式表 sqlite = QSqlDatabase.addDatabase("QSQLITE") sqlite.setDatabaseName("db") model = QSqlTableModel(None,sqlite) model.setEditStrategy(QSqlTableModel.OnManualSubmit) smodel = QSqlTableModel(None,sqlite) smodel.setEditStrategy(QSqlTableModel.OnManualSubmit) query = QSqlQuery(sqlite) view = QTableView() #view.setSelectionMode(QTableView.SingleSelection) sview = QTableView() #view.setSelectionMode(QTableView.SingleSelection) icon = Icon(model,sqlite,query,view,sview,smodel) sys.exit(app.exec_())
数据库 db
CREATE TABLE lj(id integer primary key,lj text not NULL,isdel BOOLEAN DEFAULT 0) CREATE TABLE cxtj(cxtj text not NULL)
qss样式表 stylesheet.qss
QTableView { gridline-color: white; selection-background-color: rgb(74,112,139); /*选中区域的背景色*/ font: bold 14px; /*注释*/ } QMenuBar{ font: 14px; } QStatusBar{ font: 14px; }
QT的样式表使用起来确实很方便,按CSS做的很多地方都是一样的,代码里有qt官网的样式表介绍连接
说说PYQT5与TK的使用感觉,TK上手方便,拿起来就能写,PYQT5需要了解它的工作机制所以上手时间会比较长。比较喜欢qss、tableview、tablemodel,qss可以很方便的调整样式而且和js很像,用过js的上手那叫一个快,tableview与tablemodel联动可以少些很多代码,最主要的它的容错度很高,有些可以不用写try也不会造成程序停止运行,这点很重要,哈哈,重要的点就在于‘懒’。
程序打包之后可以直接修改stylesheet.qss,样式一样会改变好方便,可以做定制,赞。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。