Python PyQt5

版权声明:Myfour的个人笔记 转载请声明来源 https://blog.csdn.net/sinat_36663351/article/details/83586058

下载PyQt5

  • pip install PyQt5

下载designer

  • pip install PyQt5-tools

使用Qtdesigner

  • 将生产的ui文件转py文件,使用命令pyuic5 xx.ui -o xx.py
  • 此时只自动生成了ui界面的代码,需要自己写主程序调用你的界面类,并且需要自己编写界面的类并注册UI的类
  • from PyQt5 import uic
    • 使用uic的loadUi()方法读取designer设计的ui文件,就可以不必ui文件转py文件这一部。
  • 主程序入口:调用你的Qt窗口,然后show()出来,然后退出
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MyMainWindow()
    window.show()
    sys.exit(app.exec_())
  • 设计的主窗体类:
class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        uipath = os.path.join(os.path.dirname(__file__), 'window.ui')  # 获取designer的ui文件的路径拼接
        uic.loadUi(uipath, self)  # 读取designer设计的ui文件

布局

  • 绝对定位
    • obj.move()
  • 布局类
    • 表格布局QGridLayout

QtSql

  • 引入库
    • from PyQt5.QtSql import QSqlDatabase, QSqlQuery
  • 连接数据库
    • 创建数据库连接并打开
    • 这里是连接SqlServer的例子
    server = '100.88.16.2'
    dbname = '市本级'
    username = 'xx'
    password = 'xx'
    db = QSqlDatabase.addDatabase('QODBC')
    db.setDatabaseName(
                f'Driver={{Sql Server}};Server={server};Database={dbname};Uid={username};Pwd={password}'
            )  # f-String中如果有{dsfd}的正常字符串就再用一个{}来包裹
    db.open()
    
  • 执行sql语句
    query = QSqlQuery()
    query.exec("Select name From sys.databases")
    
    query = QSqlQueryModel()
    query.setQuery("xxxxx")
    
    # 上述两种方式都可以执行sql语句,不过最好分开执行,一次执行多条sql语句会出错
    
  • 获取查询结果
    • 使用QSqlQueryModel对象的属性与方法来获取结果集
    model.columnCount() # 获取总列数
    model.rowCount() # 获取总行数
    model.index(x,y) # 获取一个结果集的位置索引对象,只是一个索引对象,还不是数据
    model.data(indexObj)  # 根据索引对象来取得数据,这里的索引对象就是上面的结果
    
    • model对象不会一次获取所有数据
    while model.canFetchMore():  # model不会一次读取所有,需要获取到所有记录
        model.fetchMore()  # 获取多一点的记录
    
  • 关联tableView控件
    • 需要先建立model对象,再将tableView与model相关联起来
    from PyQt5.QtSql import QSqlDatabase, QSqlQuery, QSqlQueryModel
    # 先建立跟tableView关联的模型类,该类执行sql语句
    qqm = QSqlQueryModel()    
    qqm.setQuery('select * from alltables'# 在窗体主类的tableView对象中设置该model,使用setModel方法 
    self.tableView_3.setModel(qqm)  # tableView设置了setModel后控件自动渲染结果
    

QtSql数据源切换remove问题

  • 在切换数据源时需要执行removeDatabase方法,该方法如果要正常运行,需要关闭与该数据库相关的所有对象
    def DisConnectDb(self):
            self.tableView.setModel(None)
            if hasattr(self, 'model'):
                if self.model:
                    self.model = None
            if hasattr(self, 'db'):
                if self.db:
                    name = self.db.connectionName()
                    QSqlDatabase.database(name).close()
                    self.db = None
                    QSqlDatabase.removeDatabase(name)
    
  • 总之需要把相关联的所有引用给关闭

PyQt5事件与信号

1. 在事件模型中有三个参与者:

  • 事件源:事件源是状态发生变化的对象
  • 事件(对象):事件(对象)封装了事件源中状态的变动
  • 事件接收者:事件源对象将事件处理的工作交给事件接收者
  • PyQt5有一个独特的signal&slot(信号槽)机制来处理事件。信号槽用于对象间的通信。signal在某一特定事件发生时被触发,slot可以是任何callable对象。当signal触发时会调用与之相连的slot。

2. Signals & slots

// 窗体中初始化
// valueChanged信号,lcd.display是槽

sld.valueChanged.connect(lcd.display)  
  • 重写事件处理器
    • KeyPressEvent(self,e) 按键事件
      # KeyPressEvent事件处理窗口中的按键事件,事件处理器
          def keyPressEvent(self, e):
              if e.key() == Qt.Key_Escape:
                  self.close()
              if e.key() == Qt.Key_A:
                  print('你按了a或A')
              if e.key() == Qt.Key_F1:
                  print('你按了F1')
      
  • 自定义信号

自定义信号与槽的使用,是指在发射信号时,不使用窗口控件的函数,而是使用自定义的函数,(简单的说就是使用pyqtsSignal类实例发射信号),之所以要使用自定义的信号与槽,是因为通过内置函数发射信号有自身的缺陷,首先,内置函数只包含一些常用地信号,有些信号发射找不到对应的内置函数,其次在特定的情况下,才能发射此信号,如按钮的点击事件,最后,内置函数传递的参数是特定的,不可以自定义,使用自定义的信号与槽函数则没有这些缺陷
在pyqt5中,自定义信号与槽的适用很灵活,比如因为业务需要,在程序中的某些地方需要发射一个信号,传递多种数据,然后在槽函数接受这些数据,这样就可以很灵活的实现一些业务逻辑

  • 自定义信号的一般流程如下

    • 定义信号
    • 定义槽函数
    • 连接信号与槽
    • 发射信号

    //导入相关类
    from PyQt5.QtCore import pyqtSignal, QObject
    //新建一个类来自定义信号
    class Communicate(QObject):
    closeApp = pyqtSignal() //自定义一个closeApp的信号
    //向窗体类中的信号绑定槽
    self.c.closeApp.connect(self.close) # 为自定义信号绑定槽

    //重写鼠标点击事件的事件处理器
    def mousePressEvent(self, event):
    print(‘触发鼠标点击事件’)
    self.c.closeApp.emit() //触发closeApp信号的事件

  • 自定义的信号需要定义在QObject类下

  • emit()触发信号,信号再触发绑定槽的方法,也就是emit->connet的槽方法

  • 如果信号无参数,但是槽函数需要接受某些参数:

    • 第一种:lamdba表达式
    • 第二种:使用functools中的partial函数
    //单击信号关联槽函数,利用Lanbda表达式传递一个参数
    button1.clicked.connect(lambda :self.onButtonClick(1))
    button2.clicked.connect(lambda :self.onButtonClick(2))
    

PyQt5的状态栏

  • 可用于显示程序的当前状态
  • self.statusBar().showMessage(f'正在连接{server}的{dbname}........')

QTimer控件

如果在应用程序中周期性地进行某项操作,比如周期性的检测主机的cpu值,则需要用到QTimer定时器,QTimer类提供了重复和单次的定时器,要使用定时器,需要先创建一个QTimer实例,将其Timeout信号连接到槽函数,并调用start(),然后,定时器,会以恒定的间隔发出timeout信号

self.timer = QTimer()  # 定义定时器
self.timer.setInterval(500)  # 设置时间间隔毫秒
self.timer.start()  # 开始定时器
self.timer.timeout.connect(self.status)  # 为定时器超时连接一个槽

def status(self):
    pass

PyQt5的Dialog会话框控件

  • 普通的dialog
    • 这个QInputDialog是一个弹出的会话框,getText函数获取输入的值与点击的按钮值,text为输入值,ok为点击的按钮值;getText的参数,第一个为对话框标题,第二个为提示消息
    def showDialog(self):
            # 下面这一句是重点
            text, ok = QInputDialog.getText(self, 'Input Dialog',
                                            'Enter your name:')
            if ok:
                self.le.setText(text)
    
  • 选择文件的dialog
    • fname = QFileDialog.getOpenFileName(self, 'Open file')返回的是一个元组,该元组的信息为选择的文件路径和文件的筛选方式,所以我们通过返回值的第一个元素来取到文件路径
    def showDialog(self):
            # 下面这一句是重点
            fname = QFileDialog.getOpenFileName(self, 'Open file')
            if fname[0]:
                f = open(fname[0], 'r')
                with f:
                    data = f.read()
                    self.textEdit.setText(data)
    
  • 选择文件夹dialog
    • QFileDialog.getExistingDirectory(self, '选择文件夹', './') # 第三个参数为打开时所处的路径,返回选择的文件夹路径
    def showFileDialog(self):
            dic = QFileDialog.getExistingDirectory(
                self, '选择文件夹', './')  # 第三个参数为打开时所处的路径,返回选择的文件夹路径
            return dic
    

分离UI主线程与工作线程

  • PyQt提供了一个QThread类来处理线程问题
  • 创建一个线程类重写其run函数,来实现以往单线程的功能
  • 主程序中设置槽,槽调用的函数中实例化你的线程类并就绪start该线程
# threads.py
from PyQt5.QtCore import QThread
import time


class SleepQThread(QThread):
    def __init__(self, *args, **kwargs):
        super().__init__()

    def run(self):
        time.sleep(10)
        print('sleep thread is going.....')
        
# main.py
from PyQt5.QtWidgets import QApplication, QWidget
from sleep import Ui_Form
from threads import SleepQThread
import time
import sys


class MyWidget(QWidget, Ui_Form):
    def __init__(self, *args, **kwargs):
        super().__init__()
        self.setupUi(self)
        self.pushButton.clicked.connect(self.sleep)

    def sleep(self):
        self.sleepthread = SleepQThread() # 需要赋给类中的变量
        self.sleepthread.start()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    widget = MyWidget()
    widget.show()
    sys.exit(app.exec_())
  • 高级用法,涉及主线程与子线程之间的数据传输
# thread.py
from PyQt5.QtCore import QThread, pyqtSignal
import time


class SleepQThread(QThread):
    # 自定义信号
    finalSignal = pyqtSignal(list)  # list为该信号的返回值,返回一个list对象

    def __init__(self, *args, **kwargs):
        super().__init__()
        self.args = args  # 通过初始化该线程类来传入数据到子线程中

    def run(self):
        time.sleep(10)
        print('sleep thread is going.....')
        self.finalSignal.emit([self.args])  # 信号触发,并传list对象参给connect的槽函数的第一个参数 ,此处便是子线程传数据给主线程

# main.py
from PyQt5.QtWidgets import QApplication, QWidget
from sleep import Ui_Form
from threads import SleepQThread
import time
import sys


class MyWidget(QWidget, Ui_Form):
    def __init__(self, *args, **kwargs):
        super().__init__()
        self.setupUi(self)
        self.pushButton.clicked.connect(self.sleep)

    def sleep(self):
        self.pushButton.setDisabled(True)
        self.sleepthread = SleepQThread(int(1), int(2))  
        self.sleepthread.finalSignal.connect(self.sleepEnd)  # 线程中的自定义信号绑定槽
        self.sleepthread.start()

    def sleepEnd(self, fromfinalsignal):  # fromfinalsigal参数是来源于信号的emit的参数
        print('End')
        print(fromfinalsignal)
        self.pushButton.setDisabled(False)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    widget = MyWidget()
    widget.show()
    sys.exit(app.exec_())

Pyinstaller打包的时候

  • ui文件需要转化为类并装载的写法,使用Pyinstaller打包才ok不然会出错
  • Pyinstaller打包PyQt5程序时,出现Hidden import的错误,此时通过修改命令,忽略某个不需要的包的导入即可;Pyinstaller -F -w xxx.py --hidden-import PyQt5.sip
  • 如果打包的程序需要读取配置文件,就将配置文件复制到dist的程序下,通过创建快捷方式来使用程序
  • 打包的时候程序中如果使用了图片,在开发时是可以看见的,打包后就看不见,其实就是ui生成的py文件中寻找图片的路径改变了,个人做法是将需要用到的图片放到打包后的dist文件夹下,并修改ui生成的py文件中相关图片路径(./xxx.ico)
  • 如果要修改软件的图标,在生成的.spec文件的最后一个小括号内加icon='xxx.ico',然后执行pyinstaller xxx.spec,这里图片的文件名不能是中文,且是ico后缀的图片。

自适应布局

  • 需要利用布局来实现,整个窗体的外围设置一种布局,该布局可以自适应窗口,往里同样需要如此。
  • 先拖几个控件上去,在designer中右键布局,使布局自动适应。

comboBox使用

// comboBox中的选择变更时触发的信号
self.comboBox_1.currentIndexChanged.connect(self.getKm)
// 为一个comboBox添加内容
self.comboBox_3.addItems(['先入先出','后入先出']) # 为ComboBox添加选项,且该list内的元素必须为str类型
// 获取当前comboBox选择项的文本内容
com_name = self.comboBox_1.currentText()
// 清空一个comboBox的值
self.comboBox_2.clear()

QTableWidget使用

  • 设置每一行数据的双击事件self.tableWidget.doubleClicked.connect(self.yourchoice)
  • 设置总的列数,来控制表格表头的数量self.tableWidget.setColumnCount(len(result))
  • 设置表头self.tableWidget.setHorizontalHeaderLabels([i[1] for i in result])
  • 设置总的行数,来控制表格总的记录数self.tableWidget.setRowCount(len(result))
  • 往一个单元格中填充内容
    item = QTableWidgetItem(str(j[1]))  // 单元格中的内容需要时QTableWidgetItem类型的对象,且该对象的内容只能是str类型的
    self.tableWidget.setItem(i[0], j[0], item) // 前两个参数是单元格对应的位置
    
  • 根据表名与sql来实现内容的显示:
    def displayData(self, sql):
    
        self.cursor.execute(sql)
        
        # 根据执行的sql语句获取表头信息
        
        labels = [x[0] for x in self.cursor.description
                  ]  # cursor.description可以获取每个字段的描述
                  
        self.tableWidget.setColumnCount(len(labels))
        self.tableWidget.setHorizontalHeaderLabels(labels)
        
        # 获取sql语句执行结果
        result = self.cursor.fetchall()
        self.tableWidget.setRowCount(len(result))
        self.statusBar().showMessage(f'当前页记录数:{len(result)}条')
        for i in enumerate(result):
            for j in enumerate(i[1]):
                item = QTableWidgetItem(str(j[1]))
                self.tableWidget.setItem(i[0], j[0], item)
    
    
  • 获取选中的记录one = [i.text() for i in self.tableWidget.selectedItems()]
  • 清空该空间中的所有内容self.tableWidget.clear()
  • 获取tablewidget中的内容self.tableWidget.item(row_index, column_index).text()

QTreeWidget使用

  • 类似于QTableWidget控件的使用
self.treeWidget.clear()  # 清空treewidget
self.treeWidget.setColumnCount(1)  # 设置列数
self.treeWidget.setHeaderLabels(['方法'])  # 设置表头
m1 = QTreeWidgetItem(self.treeWidget)  # 创建该控件的子节点
m2 = QTreeWidgetItem(self.treeWidget)
m3 = QTreeWidgetItem(self.treeWidget)
m1.setText(0, filename[0])  # 为m1这个节点0列设置内容
m2.setText(0, filename[1])
m3.setText(0, filename[2])

item = self.treeWidget.currentItem()  # 获取当前选择的tree节点

self.treeWidget.doubleClicked.connect(self.clickTheTree)  # 双击事件
self.treeWidget.clicked.connect(self.clickTheTree)  # 单击事件

设置字体颜色

from PyQt5.QtGui import QPalette
from PyQt5.QtCore import Qt

pe = QPalette()
pe.setColor(QPalette.WindowText, Qt.red)  # 设置文本颜色
self.label_11.setPalette(pe)  # 应用于label

猜你喜欢

转载自blog.csdn.net/sinat_36663351/article/details/83586058