快捷方式管理(pyqt5升級版)附代碼純乾貨 pytq5 python3.6.0-32

之前用python寫了個小軟件https://blog.51cto.com/ikezcn/2142638
說實話用tk的話界面確實不好看,所以需要改進,看了pyqt5的介紹想要試一下,對於上次寫的軟件來說這裏只是更改了界面而已。上面的程序不再更新。

使用的軟件:
python3.6
pyqt5 安裝:pip insatll pyqt5

界面:
快捷方式管理(pyqt5升級版)附代碼純乾貨 pytq5 python3.6.0-32
快捷方式管理(pyqt5升級版)附代碼純乾貨 pytq5 python3.6.0-32

代碼(乾貨來了,之後會不定時更新):

# -*- 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,樣式一樣會改變好方便,可以做定製,贊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章