pyqt学习

本博客的内容参照网上其他博客,本博客仅仅是记录学习过程,如果想学习相关内容,建议移步原创博客

下载qt:https://www.riverbankcomputing.com/software/pyqt/download5/
注:安装的是Qt Creator 进行的ui文件的生成
pyqt核心组件
1) QtCore模块涵盖了包的核心的非GUI功能,此模块被用于处理程序中涉及到的 time、文件、目录、数据类型、文本流、链接、mime、线程或进程等对象。

2) QtGui模块涵盖多种基本图形功能的类; 包括但不限于:窗口集、事件处理、2D图形、基本的图像和界面 和字体文本。

3) QtWidgets模块包含了一整套UI元素组件,用于建立符合系统风格的classic界面,非常方便,可以在安装时选择是否使用此功能

4) QtMultimedia模块包含了一套类库,该类库被用于处理多媒体事件,通过调用API接口访问摄像头、语音设备、收发消息(radio functionality)等

5) QtBluetooth模块包含了处理蓝牙活动的类库,它的功能包括:扫描设备、连接、交互等行为

6) QtNetwork模块包含用于网络编程的类库,这组类程序通过提供便捷的TCP/IP 及 UDP 的 c/s 程式码集合,使得基于Qt的网络编程更容易。

7) QtPositioning模块用于获取位置信息,此模块允许使用多种方式达成定位,包括但不限于:卫星、无线网、文字信息。此应用一般用于网络地图定位系统

8) Enginio模块用于构建客户端的应用程式库,用于在运行时访问 Qt Cloud 服务器托管的应用程序

9) QtWebSockets模块包含了一组类程序,用以实现websocket协议

10) QtWebKit包含了用于实现基于webkit2的网络浏览器的类库

11) QtWebKitWidgets模块提供了一组类库,用于实现一种由Widgets包构建的,基于webkit1的网络浏览器

12) QtXml模块包含了用于处理XML的类库,此模块为SAX和DOM API 的实现提供了方法

13) QtSvg模块通过一组类,为显示矢量图形文件的内容提供了方法

14) QtSql模块提供了数据库对象的接口以供使用

15) QtTest模块包含了可以通过单元测试,以调试PyQt5应用程式的功能

一、 创建窗口并生成py文件

这里写图片描述
将ui文件转成py文件

pyuic5 mainwindow.ui > demo.py  (ubuntu)
D:\Python33\Lib\site-packages\PyQt5\pyuic5.bat main.ui -o frist.py (win)

得到demo.py文件如下:

from PyQt5 import QtCore, QtGui, QtWidgets  #导入模块

class Ui_MainWindow(object):                #创建窗口类
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")   #窗口名
        MainWindow.resize(400, 300)              #窗口大小
        self.centralWidget = QtWidgets.QWidget(MainWindow)
        self.centralWidget.setObjectName("centralWidget")
        self.pushButton = QtWidgets.QPushButton(self.centralWidget)#创建按键  
        self.pushButton.setGeometry(QtCore.QRect(170, 60, 89, 25)) #按键位置
        self.pushButton.setObjectName("pushButton")  #按键名
        #MainWindow.setCentralWidget(self.centralWidget)
        self.menuBar = QtWidgets.QMenuBar(MainWindow)
        self.menuBar.setGeometry(QtCore.QRect(0, 0, 400, 28))
        self.menuBar.setObjectName("menuBar")
        #MainWindow.setMenuBar(self.menuBar)
        self.mainToolBar = QtWidgets.QToolBar(MainWindow)
        self.mainToolBar.setObjectName("mainToolBar")
        #MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.mainToolBar)
        self.statusBar = QtWidgets.QStatusBar(MainWindow)
        self.statusBar.setObjectName("statusBar")
        #MainWindow.setStatusBar(self.statusBar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)   #关联信号草槽

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))  #设置窗口标签
        self.pushButton.setText(_translate("MainWindow", "PushButton"))  #设置按钮显示文字

现在运行这段代码,窗口是不会出现的。如何使窗口出现呢?下面需要添加一段代码

if __name__=="__main__":  
    import sys  
    app=QtWidgets.QApplication(sys.argv)  
    widget=QtWidgets.QWidget()  
    ui=Ui_MainWindow()  
    ui.setupUi(widget)  
    widget.show()  
    sys.exit(app.exec_()) 

因为Qt Designer默认继承的object类,不提供show()显示方法,所以我们生成一个QWidget对象来重载我们设计的Ui_Form类,达到显示效果。

运行会报以下错误,将对应行注释掉即可

AttributeError: 'QWidget' object has no attribute 'setCentralWidget'

AttributeError: 'QWidget' object has no attribute 'setCentralWidget'

AttributeError: 'QWidget' object has no attribute 'setMenuBar'

AttributeError: 'QWidget' object has no attribute 'addToolBar'

新建一个文件,导入我们设计的untitled .py文件,实现代码与界面分离。

from PyQt5 import QtWidgets  
from demo import Ui_MainWindow 

class mywindow(QtWidgets.QWidget):  
    def __init__(self):  
        super(mywindow,self).__init__()  
        self.new=Ui_MainWindow()  
        self.new.setupUi(self)  

if __name__=="__main__":  
    import sys  

    app=QtWidgets.QApplication(sys.argv)  
    myshow=mywindow()  
    myshow.show()  
    sys.exit(app.exec_()) 

直接继承界面类

    from PyQt5 import QtWidgets  
    from untitled import Ui_MainWindow  

    class mywindow(QtWidgets.QWidget,Ui_MainWindow):  
        def __init__(self):  
            super(mywindow,self).__init__()  
            self.setupUi(self)  

    if __name__=="__main__":  
        import sys  

        app=QtWidgets.QApplication(sys.argv)  
        myshow=mywindow()  
        myshow.show()  
        sys.exit(app.exec_())  

二、右侧属性栏

objectName 控件对象名称 例:quitButton= QtWidgets.QPushButton() 等号前面的那个名字
geometry 相对坐标系
sizePolicy 控件大小策略
minimumSize最小宽度、高度
maximumSize最大宽度、高度 如果想让窗体或控件固定大小,可以将mini和max这两个属性设置成一样的数值
font 字体
cursor 光标
windowTitle 窗体标题
windowsIcon / icon 窗体图标/控件图标
iconSize 图标大小
toolTip 提示信息
statusTip 任务栏提示信息
text 控件文字
shortcut 快捷键


三、信号槽

   信号和槽是一种高级接口,应用于对象之间的通信,它是 QT 的核心特性,也是 QT 区别于其它工具包的重要地方。它为高层次的事件处理自动生成所需要的附加代码。在我们所熟知的很多 GUI 工具包中,窗口小部件 (widget) 都有一个回调函数用于响应它们能触发的每个动作,这个回调函数通常是一个指向某个函数的指针。但是,在 QT 中信号和槽取代了这些凌乱的函数指针,使得我们编写这些通信程序更为简洁明了。
    所有从 QObject 或其子类 ( 例如 Qwidget) 派生的类都能够包含信号和槽。当对象改变其状态时,信号就由该对象发射 (emit) 出去,这就是对象所要做的全部事情,它不知道另一端是谁在接收这个信号。这就是真正的信息封装,它确保对象被当作一个真正的软件组件来使用。槽用于接收信号,但它们是普通的对象成员函数。一个槽并不知道是否有任何信号与自己相连接。而且,对象并不了解具体的通信机制。
    你可以将很多信号与单个的槽进行连接,也可以将单个的信号与很多的槽进行连接,甚至于将一个信号与另外一个信号相连接也是可能的,这时无论第一个信号什么时候发射系统都将立刻发射第二个信号。总之,信号与槽构造了一个强大的部件编程机制。

所有QObject类都可以使用信号槽,换句话来说继承自pyqt中的类基本上都可以使用信号槽机制。当然非QObject也是可以通过其他一些办法来使用信号槽的。
仅仅有了信号和槽是不行的,我们还需要了解:信号(Signal)、槽(slot)、连接(connect)、动作事件(action)、发射(emit)、发送者、接受者等等一些列的知识。好吧,别搞的那么复杂行不行,我们还是学学该怎么用吧。在Qt Designer中为我们提供了一些基本的信号槽方法,我们来看看:点击工具栏上的“编辑信号/槽”,进入信号槽编辑模式,我们可以直接在发送者(button)上按住鼠标左键不放,拖动到接收者(Form窗体)上。这样就建立起了连接。
控件下面的蓝色小箭头的那个
这里写图片描述

代码如下:

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(400, 300)
        self.centralWidget = QtWidgets.QWidget(MainWindow)
        self.centralWidget.setObjectName("centralWidget")
        self.pushButton = QtWidgets.QPushButton(self.centralWidget)
        self.pushButton.setGeometry(QtCore.QRect(270, 160, 89, 25))
        self.pushButton.setObjectName("pushButton")
        #MainWindow.setCentralWidget(self.centralWidget)
        self.menuBar = QtWidgets.QMenuBar(MainWindow)
        self.menuBar.setGeometry(QtCore.QRect(0, 0, 400, 28))
        self.menuBar.setObjectName("menuBar")
        #MainWindow.setMenuBar(self.menuBar)
        self.mainToolBar = QtWidgets.QToolBar(MainWindow)
        self.mainToolBar.setObjectName("mainToolBar")
        #MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.mainToolBar)
        self.statusBar = QtWidgets.QStatusBar(MainWindow)
        self.statusBar.setObjectName("statusBar")
        #MainWindow.setStatusBar(self.statusBar)

        self.retranslateUi(MainWindow)
        self.pushButton.clicked.connect(MainWindow.close)  #添加了信号槽,我的是pushButton
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))

引用文件call.py

from PyQt5 import QtWidgets
from demo3 import Ui_MainWindow
import sys
#when you push the button will print hello world
class mywindow(QtWidgets.QWidget,Ui_MainWindow):
    def __init__(self):
        super(mywindow,self).__init__()
        self.setupUi(self)
        self.pushButton.clicked.connect(self.myPrint)  #槽函数不用加括号  
    def myPrint(self):                                 #定义槽
        print('helo world')


if __name__ =='__main__':
    app=QtWidgets.QApplication(sys.argv)
    myshow=mywindow()
    myshow.show()
    sys.exit(app.exec_())

注:「argv」是「argument variable」参数变量的简写形式,一般在命令行调用的时候由系统传递给程序。这个变量其实是一个List列表,argv[0] 一般是被调用的脚本文件名或全路径,和操作系统有关,argv[1]和以后就是传入的数据了。参数从1开始一般参数由空格分隔。Pyqt5 获取命令行参数sys.argv

小提示:

  • 槽其实就个函数(方法),Qt5中的槽函数不在限定必须是slot,可以是普通的函数、类的普通成员函数、lambda函数等。编译期间就会检查信号与槽是否存在!
  • 信号的connect连接最好放在init析构函数里面,这样只会声明一次连接,如果在类方法(函数中)使用的话,要记得disconnect,否则connect会连接多次,导致程序异常。
  • 信号槽函数不用加 (),否则可能会导致连接异常。

四、自定义信号emit及传参

实现原博客的内容
send.py

#-*-coding:utf-8 -*-
from PyQt5 import QtWidgets,QtCore  
from demo import Ui_MainWindow  
import  time  
import sys  

class MyWindow(QtWidgets.QWidget,Ui_MainWindow):  
    _signal=QtCore.pyqtSignal(str)                         #定义信号,定义参数为str类型  
    def __init__(self):    
        super(MyWindow,self).__init__()  
        self.setupUi(self)  
        self.pushButton.clicked.connect(self.myPrint)  
        self._signal.connect(self.mySignal)               #将信号连接到函数mySignal  

    def myPrint(self):  
        self.textBrowser.setText("")  
        self.textBrowser.append("正在打印,请稍候")  
        self._signal.emit("打印结束了吗?")  
    def mySignal(self,string):  
        print(string)  
        self.textBrowser.append("打印结束")  

if __name__=="__main__":    


    app=QtWidgets.QApplication(sys.argv)    
    myshow=MyWindow()  
    myshow.show()    
    sys.exit(app.exec_())    

demo.py

from PyQt5 import QtCore, QtGui, QtWidgets  #导入模块

class Ui_MainWindow(object):                #创建窗口类
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")   #窗口名
        MainWindow.resize(400, 300)              #窗口大小
        self.centralWidget = QtWidgets.QWidget(MainWindow)
        self.centralWidget.setObjectName("centralWidget")
        self.pushButton = QtWidgets.QPushButton(self.centralWidget)#创建按键  
        self.pushButton.setGeometry(QtCore.QRect(250, 200, 89, 25)) #按键位置
        self.pushButton.setObjectName("pushButton")  #按键名
        self.textBrowser = QtWidgets.QTextBrowser(self.centralWidget)
        self.textBrowser.setGeometry(QtCore.QRect(10, 20, 200, 150))
        self.textBrowser.setObjectName("textBrowser")
        #MainWindow.setCentralWidget(self.centralWidget)
        self.menuBar = QtWidgets.QMenuBar(MainWindow)
        self.menuBar.setGeometry(QtCore.QRect(0, 0, 400, 28))
        self.menuBar.setObjectName("menuBar")
        #MainWindow.setMenuBar(self.menuBar)
        self.mainToolBar = QtWidgets.QToolBar(MainWindow)
        self.mainToolBar.setObjectName("mainToolBar")
        #MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.mainToolBar)
        self.statusBar = QtWidgets.QStatusBar(MainWindow)
        self.statusBar.setObjectName("statusBar")
        #MainWindow.setStatusBar(self.statusBar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)   #关联信号草槽

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))  #设置窗口标签
        self.pushButton.setText(_translate("MainWindow", "PushButton"))  #设置按钮显示文字
if __name__=="__main__":  
    import sys  
    app=QtWidgets.QApplication(sys.argv)  
    widget=QtWidgets.QWidget()  
    ui=Ui_MainWindow()  
    ui.setupUi(widget)  
    widget.show()  
    sys.exit(app.exec_()) 

我的实现:
buttoncall.py

#-*-coding:utf-8 -*-
from PyQt5 import QtWidgets,QtCore  
from button import Ui_MainWindow  
import  time  
import sys  

class MyWindow(QtWidgets.QWidget,Ui_MainWindow):  
    _signal=QtCore.pyqtSignal(str)                         #定义信号,定义参数为str类型  
    def __init__(self):    
        super(MyWindow,self).__init__()  
        self.setupUi(self)  
        self.pushButton.clicked.connect(self.quitp) 
        self.pushButton_2.clicked.connect(self.myPrint)
        self.pushButton_3.clicked.connect(self.askp) 
        self._signal.connect(self.mySignal)               #将信号连接到函数mySignal  

    def myPrint(self):  
        self.textBrowser.setText("")  
        self.textBrowser.append("开始打印")  

    def askp(self):
        self.textBrowser.append("打印结束了吗?")

    def quitp(self):
        self._signal.emit("打印结束,并退出")


if __name__=="__main__":    

    app=QtWidgets.QApplication(sys.argv)    
    myshow=MyWindow()  
    myshow.show()    
    sys.exit(app.exec_())  

button.py

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(400, 300)
        self.centralWidget = QtWidgets.QWidget(MainWindow)
        self.centralWidget.setObjectName("centralWidget")
        self.textBrowser = QtWidgets.QTextEdit(self.centralWidget)
        self.textBrowser.setGeometry(QtCore.QRect(30, 30, 181, 191))
        self.textBrowser.setObjectName("textEdit")
        self.pushButton = QtWidgets.QPushButton(self.centralWidget)
        self.pushButton.setGeometry(QtCore.QRect(230, 170, 89, 25))
        self.pushButton.setObjectName("pushButton")
        self.pushButton_2 = QtWidgets.QPushButton(self.centralWidget)
        self.pushButton_2.setGeometry(QtCore.QRect(230, 20, 89, 25))
        self.pushButton_2.setObjectName("pushButton_2")
        self.pushButton_3 = QtWidgets.QPushButton(self.centralWidget)
        self.pushButton_3.setGeometry(QtCore.QRect(230, 90, 89, 25))
        self.pushButton_3.setObjectName("pushButton_3")
        #MainWindow.setCentralWidget(self.centralWidget)
        self.menuBar = QtWidgets.QMenuBar(MainWindow)
        self.menuBar.setGeometry(QtCore.QRect(0, 0, 400, 28))
        self.menuBar.setObjectName("menuBar")
        #MainWindow.setMenuBar(self.menuBar)
        self.mainToolBar = QtWidgets.QToolBar(MainWindow)
        self.mainToolBar.setObjectName("mainToolBar")
        #MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.mainToolBar)
        self.statusBar = QtWidgets.QStatusBar(MainWindow)
        self.statusBar.setObjectName("statusBar")
        #MainWindow.setStatusBar(self.statusBar)

        self.retranslateUi(MainWindow)
        self.pushButton.clicked.connect(MainWindow.close)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "quit"))
        self.pushButton_2.setText(_translate("MainWindow", "print"))
        self.pushButton_3.setText(_translate("MainWindow", "ask"))

五、通用对话框QMessageBox

PyQt5中为我们提供了很多默认信息框QMessageBox,注意为方便使用需要导入模块。

QMessageBox对话框不同的类型只是图标不同,其他无太大差别:
QMessageBox.information 信息框
QMessageBox.question 问答框
QMessageBox.warning 警告
QMessageBox.ctitical危险
QMessageBox.about 关于
实现

# -*- coding: utf-8 -*-
from PyQt5 import QtWidgets
from demo3 import Ui_MainWindow
import sys
from PyQt5.QtWidgets import QMessageBox
#when you push the button will print hello world
class mywindow(QtWidgets.QWidget,Ui_MainWindow):
    def __init__(self):
        super(mywindow,self).__init__()
        self.setupUi(self)
        self.pushButton.clicked.connect(self.myPrint)
    def myPrint(self):
        reply = QMessageBox.information(self,                         #使用infomation信息框  
                                    "标题",  
                                    "消息",  
                                    QMessageBox.No | QMessageBox.Yes)  


if __name__ =='__main__':
    app=QtWidgets.QApplication(sys.argv)
    myshow=mywindow()
    myshow.show()
    sys.exit(app.exec_())

demo3.py

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(400, 300)
        self.centralWidget = QtWidgets.QWidget(MainWindow)
        self.centralWidget.setObjectName("centralWidget")
        self.pushButton = QtWidgets.QPushButton(self.centralWidget)
        self.pushButton.setGeometry(QtCore.QRect(270, 160, 89, 25))
        self.pushButton.setObjectName("pushButton")
        #MainWindow.setCentralWidget(self.centralWidget)
        self.menuBar = QtWidgets.QMenuBar(MainWindow)
        self.menuBar.setGeometry(QtCore.QRect(0, 0, 400, 28))
        self.menuBar.setObjectName("menuBar")
        #MainWindow.setMenuBar(self.menuBar)
        self.mainToolBar = QtWidgets.QToolBar(MainWindow)
        self.mainToolBar.setObjectName("mainToolBar")
        #MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.mainToolBar)
        self.statusBar = QtWidgets.QStatusBar(MainWindow)
        self.statusBar.setObjectName("statusBar")
        #MainWindow.setStatusBar(self.statusBar)

        self.retranslateUi(MainWindow)
        self.pushButton.clicked.connect(MainWindow.close)  #添加了信号槽
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "PushButton"))

这里写图片描述

六、 基本界面组件之Dialog

PyQt5的场景的对话框有,QFileDialog,QColorDialog,QFontDialog,QErrorMessage,QInputDialog,QMessageBox,QPrintDialog,QProcessDialog等。追逐阳光的风的博客

QFileDialog

class QFileDialog(QDialog)
 |  QFileDialog(QWidget, Qt.WindowFlags)
 |  QFileDialog(QWidget parent=None, str caption='', str directory='', str filter='')

QFileDialog继承自QDialog。QFileDialog是PyQt中用于文件打开和保存的标准对话框。

#-*- coding:utf-8 -*-

from PyQt5.QtWidgets import QApplication, QDialog,QWidget, QFileDialog, QPushButton, QLineEdit, QGridLayout
import sys
import os


class FileDialog(QDialog):
    def __init__(self):
        super(FileDialog,self).__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QFileDialog")
        self.setGeometry(400,400,300,260)

        self.fileButton = QPushButton("文件对话框")
        self.fileLineEdit = QLineEdit()
        self.fileButton.clicked.connect(lambda:self.openFile(self.fileLineEdit.text()))

        self.mainLayout = QGridLayout()
        self.mainLayout.addWidget(self.fileButton,0,0)
        self.mainLayout.addWidget(self.fileLineEdit,0,1)

        self.setLayout(self.mainLayout)

    def openFile(self,filePath):
        if os.path.exists(filePath):
            path = QFileDialog.getOpenFileName(self,"Open File Dialog",filePath,"Python files(*.py);;Text files(*.txt)")
        else:
            path = QFileDialog.getOpenFileName(self,"Open File Dialog","/","Python files(*.py);;Text files(*.txt)")

        self.fileLineEdit.setText(str(path[0]))


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

这里写图片描述

控件说明:

表格的写法
控件类型 | 控件名称 | 作用
- | :-: | -:
QPushButton | fileButton| 确认打开文件路径
QLineEdit | fileLineEdit | 显示打开的文件路径
QFileDialog |   | 文件操作对话框类
控件类型控件名称作用
QPushButtonfileButton确认打开文件路径
QLineEditfileLineEdit显示打开的文件路径
QFileDialog文件操作对话框类

通过文件对话框来选择所需的文件(默认为当前路径,或者QLineEdit指定的路径),并且将打开的文件路径显示在QLineEdit中。

 getOpenFileName(...)
 |      QFileDialog.getOpenFileName(QWidget parent=None, str caption='', str directory='', str filter='', str initialFilter='', QFileDialog.Options options=0) -> (str, str)

第1个参数parent,用于指定父组件;

第2个参数caption,是QFileDialog对话框的标题;

第3个参数directory,是对话框显示时默认打开的目录,”.” 代表程序运行目录,”/” 代表当前盘符的根目录(Windows,Linux下/就是根目录了),需要注意不同平台下路径的显示方式,比如Windows平台的C盘”C:\”等

第4个参数filter,是对话框中文件后缀名过滤器。比如我们使用”Python files(.py)”就让它只能显示后缀名是.py的文件。如果需要使用多个过滤器,使用”;;”分割,比如”Python files(.py);;Text files(.txt)”,为两个过滤器分别过滤python文件和txt文件;

第5个参数initialFilter,为默认的过滤器;

第6个参数options,是对话框的一些参数设定,比如只显示文件夹等等,它的取值是 QFileDialog.Options,每个选项可以使用 | 运算组合起来。

QColorDialog

class QColorDialog(QDialog)
 |  QColorDialog(QWidget parent=None)
 |  QColorDialog(QColor, QWidget parent=None)

QColorDialog依旧继承自QDialog。QColorDialog是PyQt中用于颜色原则的标准对话框。

#-*- coding:utf-8 -*-

from PyQt5.QtWidgets import QApplication, QDialog,QWidget, QColorDialog, QPushButton, QGridLayout, QFrame 
from PyQt5.QtGui import QPalette,QColor
import sys
from PyQt5.QtCore import Qt


class ColorDialog(QDialog):
    def __init__(self):
        super(ColorDialog,self).__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("ColorDialog")
        self.setGeometry(400,400,300,260)

        self.colorButton = QPushButton("颜色对话框")
        self.colorFrame = QFrame()
        self.colorFrame.setFrameShape(QFrame.Box)
        self.colorFrame.setAutoFillBackground(True)
        self.colorButton.clicked.connect(self.openColor)

        self.mainLayout = QGridLayout()
        self.mainLayout.addWidget(self.colorButton,0,0)
        self.mainLayout.addWidget(self.colorFrame,0,1)

        self.setLayout(self.mainLayout)

    def openColor(self):
        color = QColorDialog.getColor(Qt.white,None,"Selectting Color")
        if color.isValid():
            self.colorFrame.setPalette(QPalette(color))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = ColorDialog()
    ex.show()
    sys.exit(app.exec_()) 
 getColor(...)
 |      QColorDialog.getColor(QColor initial=Qt.white, QWidget parent=None, str title=QString(), QColorDialog.ColorDialogOptions options=0) -> QColor

第1个参数initial,用于指定默认的颜色,函数默认为Qt.white;

第2个参数parent,用于指定父组件;

第3个参数title,是QColorDialog对话框的标题;

第4个参数options,是对话框的一些参数设定,比如是否显示Alpha值等等,每个选项可以使用 | 运算组合起来。具体可以查看API函数的说明。

QFontDialog

#-*- coding:utf-8 -*-

from PyQt5.QtWidgets import QApplication, QDialog,QWidget, QFontDialog, QPushButton, QLineEdit, QGridLayout
import sys
import os


class FontDialog(QDialog):
    def __init__(self):
        super(FontDialog,self).__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle("QFontDialog")
        self.setGeometry(400,400,300,260)

        self.fontButton = QPushButton("字体选择对话框")
        self.fontLineEdit = QLineEdit("Hello Python")
        self.fontButton.clicked.connect(self.openFont)

        self.mainLayout = QGridLayout()
        self.mainLayout.addWidget(self.fontButton,0,0)
        self.mainLayout.addWidget(self.fontLineEdit,0,1)

        self.setLayout(self.mainLayout)

    def openFont(self):
         font,ok=QFontDialog.getFont()  
         if ok:
            self.fontLineEdit.setFont(font) 


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = FontDialog()
    ex.show()
    sys.exit(app.exec_()) 
 getFont(...)
 |      QFontDialog.getFont(QFont, QWidget parent=None, str caption=QString(), QFontDialog.FontDialogOptions options=0) -> (QFont, bool)
 |      QFontDialog.getFont(QWidget parent=None) -> (QFont, bool)

etFont()方法有两个重载的实现,对应如上所需的参数不同,在上面的示例中,我们采用的是第二个实现。

QFontDialog.getFont(QFont, QWidget parent=None, str caption=QString(), QFontDialog.FontDialogOptions options=0) -> (QFont, bool)

第1个参数parent,用于指定父组件;

第2个参数caption,是QFileDialog对话框的标题;

第3个参数options,是对话框的一些参数设定,它的取值是 QFontDialog.FontDialogOptions ,每个选项可以使用 | 运算组合起来。

返回值为(QFont,bool),即返回值类型为tuple(元组)。

七、Qt Designer 主窗口MainWindows

MainWindows 即主窗口,主要包含了菜单栏、工具栏、任务栏等。双击菜单栏上的“在这里输入”,然后录入文字,回车即可。注意要用回车键确认。
这里写图片描述

这里写图片描述


双击出现下面的框进行修改

这里写图片描述

生成Ui文件转成Python文件

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(987, 813)
        self.centralWidget = QtWidgets.QWidget(MainWindow)
        self.centralWidget.setObjectName("centralWidget")
        MainWindow.setCentralWidget(self.centralWidget)
        self.menuBar = QtWidgets.QMenuBar(MainWindow)
        self.menuBar.setGeometry(QtCore.QRect(0, 0, 987, 28))
        self.menuBar.setObjectName("menuBar")
        self.menuFile = QtWidgets.QMenu(self.menuBar)
        self.menuFile.setObjectName("menuFile")
        self.menuTools = QtWidgets.QMenu(self.menuBar)
        self.menuTools.setObjectName("menuTools")
        self.menuOthers = QtWidgets.QMenu(self.menuBar)
        self.menuOthers.setObjectName("menuOthers")
        MainWindow.setMenuBar(self.menuBar)
        self.statusBar = QtWidgets.QStatusBar(MainWindow)
        self.statusBar.setObjectName("statusBar")
        MainWindow.setStatusBar(self.statusBar)
        self.toolBar = QtWidgets.QToolBar(MainWindow)
        self.toolBar.setObjectName("toolBar")
        MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
        self.actionopen = QtWidgets.QAction(MainWindow)
        self.actionopen.setObjectName("actionopen")
        self.actionnew = QtWidgets.QAction(MainWindow)
        self.actionnew.setObjectName("actionnew")
        self.actionsave = QtWidgets.QAction(MainWindow)
        self.actionsave.setObjectName("actionsave")
        self.actionsearch = QtWidgets.QAction(MainWindow)
        self.actionsearch.setObjectName("actionsearch")
        self.actionsetting = QtWidgets.QAction(MainWindow)
        self.actionsetting.setObjectName("actionsetting")
        self.menuFile.addAction(self.actionopen)
        self.menuFile.addAction(self.actionnew)
        self.menuFile.addAction(self.actionsave)
        self.menuTools.addAction(self.actionsearch)
        self.menuTools.addAction(self.actionsetting)
        self.menuBar.addAction(self.menuFile.menuAction())
        self.menuBar.addAction(self.menuTools.menuAction())
        self.menuBar.addAction(self.menuOthers.menuAction())

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.menuFile.setTitle(_translate("MainWindow", "File"))
        self.menuTools.setTitle(_translate("MainWindow", "Tools"))
        self.menuOthers.setTitle(_translate("MainWindow", "Others"))
        self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
        self.actionopen.setText(_translate("MainWindow", "open"))
        self.actionnew.setText(_translate("MainWindow", "new"))
        self.actionsave.setText(_translate("MainWindow", "save"))
        self.actionsearch.setText(_translate("MainWindow", "search"))
        self.actionsetting.setText(_translate("MainWindow", "setting"))

添加调用的文件

#-*- coding:utf-8 -*-
from PyQt5 import QtWidgets  
from mainwindow1 import Ui_MainWindow  
from PyQt5.QtWidgets import QFileDialog  

class MyWindow(QtWidgets.QMainWindow,Ui_MainWindow):  
    def __init__(self):  
        super(MyWindow,self).__init__()  
        self.setupUi(self)  
        self.actionopen.triggered.connect(self.openMsg)      #菜单的点击事件是triggered  

    def openMsg(self):  
        file,ok=QFileDialog.getOpenFileName(self,"打开","/home/andy/xxs/qt/newui","All Files (*);;Text Files (*.txt)")  
        #self.statusBar.showMessage(file)                   #在状态栏显示文件地址  

if __name__=="__main__":  
    import sys  

    app=QtWidgets.QApplication(sys.argv)  
    myshow=MyWindow()  
    myshow.show()  
    sys.exit(app.exec_()) 

这里写图片描述

出现了AttributeError: 'MyWindow' object has no attribute 'statusbar'
Aborted (core dumped)
的错误,改成self.statusBar.showMessage(file)

八、主窗口动态加载Widget

我们通过Qt Designer设计两个窗口,命名为主窗口(MainForm)和子窗口(ChildrenForm)。我们在主窗口的空白中央添加一个栅格布局并命名为MaingridLayout,等会需要将ChildrenForm放进去。
这里写图片描述
生成的py文件如下

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(987, 813)
        self.centralWidget = QtWidgets.QWidget(MainWindow)
        self.centralWidget.setObjectName("centralWidget")
        self.gridLayoutWidget = QtWidgets.QWidget(self.centralWidget)
        self.gridLayoutWidget.setGeometry(QtCore.QRect(60, 40, 391, 221))
        self.gridLayoutWidget.setObjectName("gridLayoutWidget")
        self.MaingridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
        self.MaingridLayout.setContentsMargins(11, 11, 11, 11)
        self.MaingridLayout.setSpacing(6)
        self.MaingridLayout.setObjectName("MaingridLayout")
        MainWindow.setCentralWidget(self.centralWidget)
        self.menuBar = QtWidgets.QMenuBar(MainWindow)
        self.menuBar.setGeometry(QtCore.QRect(0, 0, 987, 28))
        self.menuBar.setObjectName("menuBar")
        self.menuFile = QtWidgets.QMenu(self.menuBar)
        self.menuFile.setObjectName("menuFile")
        self.menuTools = QtWidgets.QMenu(self.menuBar)
        self.menuTools.setObjectName("menuTools")
        self.menuOthers = QtWidgets.QMenu(self.menuBar)
        self.menuOthers.setObjectName("menuOthers")
        MainWindow.setMenuBar(self.menuBar)
        self.statusBar = QtWidgets.QStatusBar(MainWindow)
        self.statusBar.setObjectName("statusBar")
        MainWindow.setStatusBar(self.statusBar)
        self.toolBar = QtWidgets.QToolBar(MainWindow)
        self.toolBar.setObjectName("toolBar")
        MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
        self.actionopen = QtWidgets.QAction(MainWindow)
        self.actionopen.setObjectName("actionopen")
        self.actionnew = QtWidgets.QAction(MainWindow)
        self.actionnew.setObjectName("actionnew")
        self.actionsave = QtWidgets.QAction(MainWindow)
        self.actionsave.setObjectName("actionsave")
        self.actionsearch = QtWidgets.QAction(MainWindow)
        self.actionsearch.setObjectName("actionsearch")
        self.actionsetting = QtWidgets.QAction(MainWindow)
        self.actionsetting.setObjectName("actionsetting")
        self.menuFile.addAction(self.actionopen)
        self.menuFile.addAction(self.actionnew)
        self.menuFile.addAction(self.actionsave)
        self.menuTools.addAction(self.actionsearch)
        self.menuTools.addAction(self.actionsetting)
        self.menuBar.addAction(self.menuFile.menuAction())
        self.menuBar.addAction(self.menuTools.menuAction())
        self.menuBar.addAction(self.menuOthers.menuAction())

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.menuFile.setTitle(_translate("MainWindow", "File"))
        self.menuTools.setTitle(_translate("MainWindow", "Tools"))
        self.menuOthers.setTitle(_translate("MainWindow", "Others"))
        self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar"))
        self.actionopen.setText(_translate("MainWindow", "open"))
        self.actionnew.setText(_translate("MainWindow", "new"))
        self.actionsave.setText(_translate("MainWindow", "save"))
        self.actionsearch.setText(_translate("MainWindow", "search"))
        self.actionsetting.setText(_translate("MainWindow", "setting"))

我们新生成的窗口是之前的那个button窗口的界面,调用两个界面的文件是按照原作者的

#-*- coding:utf-8 -*-
from PyQt5 import QtWidgets  
from mainwindow1 import Ui_MainWindow  
from button import Ui_MainWindow2  

from PyQt5.QtWidgets import QFileDialog  

class MainForm(QtWidgets.QMainWindow,Ui_MainWindow):  
    def __init__(self):  
        super(MainForm,self).__init__()  
        self.setupUi(self)  

        self.child=ChildrenForm()                          #self.child = children()生成子窗口实例self.child  


        self.actionopen.triggered.connect(self.openMsg)      #菜单的点击事件是triggered  
        self.actionsave.triggered.connect(self.close)  
        self.actionnew.triggered.connect(self.childShow)   #点击actionTst,子窗口就会显示在主窗口的MaingridLayout中  

    def childShow(self):  
        self.MaingridLayout.addWidget(self.child)          #添加子窗口  
        self.child.show()  


    def openMsg(self):  
        file,ok=QFileDialog.getOpenFileName(self,"打开","/home/andy/xxs/qt/newui","All Files (*);;Text Files (*.txt)")  
        #self.statusbar.showMessage(file)                   #在状态栏显示文件地址  

class ChildrenForm(QtWidgets.QWidget,Ui_MainWindow2):  
    def __init__(self):  
        super(ChildrenForm,self).__init__()  
        self.setupUi(self)  

if __name__=="__main__":  
    import sys  

    app=QtWidgets.QApplication(sys.argv)  
    myshow=MainForm()  
    myshow.show()  
    sys.exit(app.exec_())  

九、 pyqt线程间通信

信号(singal)与槽(slot)用于对象相互通信,信号:当某个对象的某个事件发生时,触发一个信号,槽:响应指定信号的所做的反应,其实信号槽类似于.NET里面的委托、事件,比如Repeater控件类,当行数据绑定后,触发一个ItemDataBound事件,不管使用者使用会监听该事件并做额外处理,其控件类内部都会触发该事件,这种机制很多程度提高了类的封装性和完整性。
  PyQt的窗体控件类已经有很多的内置信号,开发者也可以添加自己的自定义信号,信号槽有如下特点:
    - 一个信号可以连接到许多插槽。
    - 一个信号也可以连接到另一个信号。
    - 信号参数可以是任何Python类型。
    - 一个插槽可以连接到许多信号。
    - 连接可能会直接(即同步)或排队(即异步)。
    - 连接可能会跨线程。
    - 信号可能会断开

  (以上几条特点翻译于官方文档),接下来,我将以若干个实例,来体现以上几个特点。

内置信号槽的使用

# -*- coding:utf-8 -*-
from PyQt5.QtWidgets import *  
from PyQt5.QtCore import *  

def sinTest():  
    btn.setText("按钮文本改变")  
    btn.clicked.connect(sinTest2)


def sinTest2():
    btn.setText("按钮文本")  
    btn.clicked.connect(sinTest)



app = QApplication([])  
#import sys      #注释掉的这三句可以替换掉上面的那句,至于原作者为什么考虑将之前的那种写法换成现在的这种写法应该是不需要传入参数
#from PyQt5 import QtWidgets
#app=QtWidgets.QApplication(sys.argv)    
main = QWidget()  
main.resize(200,100)  
btn = QPushButton("按钮文本",main)  
##按钮btn的内置信号连接名为sinTest的槽  
btn.clicked.connect(sinTest)  
main.show()  

app.exec_()  

自定义信号槽的使用

# -*- coding:utf-8 -*-
from PyQt5.QtWidgets import *  
from PyQt5.QtCore import * 
class SinClass(QObject):  

    ##声明一个无参数的信号  
    sin1 = pyqtSignal()  

    ##声明带一个int类型参数的信号  
    sin2 = pyqtSignal(int)  

    ##声明带一个int和str类型参数的信号  
    sin3 = pyqtSignal(int,str)  

    ##声明带一个列表类型参数的信号  
    sin4 = pyqtSignal(list)  

    ##声明带一个字典类型参数的信号  
    sin5 = pyqtSignal(dict)  

    ##声明一个多重载版本的信号,包括了一个带int和str类型参数的信号,以及带str参数的信号  
    sin6 = pyqtSignal([int,str], [str])  

    def __init__(self,parent=None):  
        super(SinClass,self).__init__(parent)  

        ##信号连接到指定槽  
        self.sin1.connect(self.sin1Call)  
        self.sin2.connect(self.sin2Call)  
        self.sin3.connect(self.sin3Call)  
        self.sin4.connect(self.sin4Call)  
        self.sin5.connect(self.sin5Call)  
        self.sin6[int,str].connect(self.sin6Call)  
        self.sin6[str].connect(self.sin6OverLoad)  

        ##信号发射  
        self.sin1.emit()  
        self.sin2.emit(1)  
        self.sin3.emit(1,"text")  
        self.sin4.emit([1,2,3,4])  
        self.sin5.emit({"name":"codeio","age":"25"})  
        self.sin6[int,str].emit(1,"text")  
        self.sin6[str].emit("text")  

    def sin1Call(self):  
        print("sin1 emit")  

    def sin2Call(self,val):  
        print("sin2 emit,value:",val)  

    def sin3Call(self,val,text):  
        print("sin3 emit,value:",val,text)  

    def sin4Call(self,val):  
        print("sin4 emit,value:",val)  

    def sin5Call(self,val):  
        print("sin5 emit,value:",val)  

    def sin6Call(self,val,text):  
        print("sin6 emit,value:",val,text)  

    def sin6OverLoad(self,val):  
        print("sin6 overload emit,value:",val)  

sin = SinClass()  

信号槽N对N连接、断开连接

# -*- coding:utf-8 -*-
from PyQt5.QtWidgets import *  
from PyQt5.QtCore import *  

class SinClass(QObject):  

    ##声明一个无参数的信号  
    sin1 = pyqtSignal()  

    ##声明带一个int类型参数的信号  
    sin2 = pyqtSignal(int)  

    def __init__(self,parent=None):  
        super(SinClass,self).__init__(parent)  

        ##信号sin1连接到sin1Call和sin2Call这两个槽  
        self.sin1.connect(self.sin1Call)  
        self.sin1.connect(self.sin2Call)  

        ##信号sin2连接到信号sin1  
        self.sin2.connect(self.sin1)  

        ##信号发射  
        self.sin1.emit()  
        self.sin2.emit(1)  

        ##断开sin1、sin2信号与各槽的连接  
        self.sin1.disconnect(self.sin1Call)  
        self.sin1.disconnect(self.sin2Call)  
        self.sin2.disconnect(self.sin1)  

        ##信号sin1和sin2连接同一个槽sin1Call  
        self.sin1.connect(self.sin1Call)  
        self.sin2.connect(self.sin1Call)  

        ##信号再次发射  
        self.sin1.emit()  
        self.sin2.emit(1)  

    def sin1Call(self):  
        print("sin1 emit")  

    def sin2Call(self):  
        print("sin2 emit")  

sin = SinClass()  

* 多线程信号槽通信*

#coding:utf-8
from PyQt5.QtWidgets import *  
from PyQt5.QtCore import *  

class Main(QWidget):  
    def __init__(self, parent = None):  
        super(Main,self).__init__(parent)  

        ##创建一个线程实例并设置名称、变量、信号槽  
        self.thread = MyThread()  
        self.thread.setIdentity("thread1")  
        self.thread.sinOut.connect(self.outText)  
        self.thread.setVal(6)  

    def outText(self,text):  
        print(text)  

class MyThread(QThread):  

    sinOut = pyqtSignal(str)  

    def __init__(self,parent=None):  
        super(MyThread,self).__init__(parent)  

        self.identity = None  

    def setIdentity(self,text):  
        self.identity = text  

    def setVal(self,val):  
        self.times = int(val)  

        ##执行线程的run方法  
        self.start()  

    def run(self):  
        while self.times > 0 and self.identity:  
            ##发射信号  
            self.sinOut.emit(self.identity+" "+str(self.times))  
            self.times -= 1  

app = QApplication([])  

main = Main()  
main.show()  

app.exec_()  

十、初识pyqt多线程操作

from PyQt5.QtCore import *  
from PyQt5.QtGui import *  
from PyQt5.QtWidgets import *  

global sec  
sec=0  
def setTime():  
    global  sec  
    sec+=1  
    lcdNumber.display(sec)          #LED显示数字+1  

def work():  
    timer.start(1000)               #计时器每秒计数  
    for i in range(2000000000):  
       pass  
    timer.stop()  

app=QApplication([])  
top=QWidget()  
layout=QVBoxLayout(top)             #垂直布局类QVBoxLayout;  
lcdNumber=QLCDNumber()              #加个显示屏  
layout.addWidget(lcdNumber)  
button=QPushButton("测试")  
layout.addWidget(button)  

timer=QTimer()  
timer.timeout.connect(setTime)      #每次计时结束,触发setTime  
button.clicked.connect(work)  

top.show()  
app.exec_() 

我们的主界面有一个用于显示时间的 LCD 数字面板还有一个用于启动任务的按钮。程序的目的是用户点击按钮,开始一个非常耗时的运算(程序中我们以一个 2000000000 次的循环来替代这个非常耗时的工作,在真实的程序中,这可能是一个网络访问,可能是需要复制一个很大的文件或者其它任务),同时 LCD 开始显示逝去的毫秒数。毫秒数通过一个计时器QTimer进行更新。计算完成后,计时器停止。这是一个很简单的应用,也看不出有任何问题。但是当我们开始运行程序时,问题就来了:点击按钮之后,程序界面直接停止响应,直到循环结束才开始重新更新,于是计时器使用显示0。

有经验的开发者立即指出,这里需要使用线程。这是因为 Qt 中所有界面都是在 UI 线程中(也被称为主线程,就是执行了QApplication::exec()的线程),在这个线程中执行耗时的操作(比如那个循环),就会阻塞 UI 线程,从而让界面停止响应。界面停止响应,用户体验自然不好,不过更严重的是,有些窗口管理程序会检测到你的程序已经失去响应,可能会建议用户强制停止程序,这样一来你的程序可能就此终止,任务再也无法完成。所以,为了避免这一问题,我们要使用 QThread 开启一个新的线程:

#coding:utf-8
from PyQt5.QtCore import *  
from PyQt5.QtGui import *  
from PyQt5.QtWidgets import *  

global sec  
sec=0  

class WorkThread(QThread):  
    trigger = pyqtSignal()  
    def __int__(self):  
        super(WorkThread,self).__init__()  

    def run(self):  
        for i in range(2000000000):  
            pass  
        self.trigger.emit()         #循环完毕后发出信号  

def countTime():  
    global  sec  
    sec+=1  
    lcdNumber.display(sec)          #LED显示数字+1  

def work():  
    timer.start(1000)               #计时器每秒计数  
    workThread.start()              #计时开始  
    workThread.trigger.connect(timeStop)   #当获得循环完毕的信号时,停止计数  

def timeStop():  
    timer.stop()  
    print("运行结束用时",lcdNumber.value())
    global sec  
    sec=0  

app=QApplication([])  
top=QWidget()  
layout=QVBoxLayout(top)             #垂直布局类QVBoxLayout;  
lcdNumber=QLCDNumber()              #加个显示屏  
layout.addWidget(lcdNumber)  
button=QPushButton("测试")  
layout.addWidget(button)  

timer=QTimer()  
workThread=WorkThread()  

button.clicked.connect(work)  
timer.timeout.connect(countTime)      #每次计时结束,触发setTime  

top.show()  
app.exec_()

我增加了一个WorkerThread类。WorkerThread继承自QThread类,重写了其run()函数。可以认为,run()函数就是新的线程需要执行的代码。在这里就是要执行这个循环,然后发出计算完成的信号。而在按钮点击的槽函数中,使用work()中的workThread.start()函数启动一个线程(注意,这里不是run()函数)。再次运行程序,你会发现现在界面已经不会被阻塞了。

十一、PyQt 线程相关类

QThread是我们将要详细介绍的第一个类。它也是 Qt 线程类中最核心的底层类。由于 PyQt 的跨平台特性,QThread要隐藏掉所有平台相关的代码。
正如前面所说,要使用QThread开始一个线程,我们可以创建它的一个子类,然后覆盖其QThread.run()函数:

class Thread(QThread):  
    def __init__(self):  
        super(Thread,self).__init__()  
    def run(self):  
        pass               #线程相关代码 

然后我们这样新建一个新的线程

    thread=Thread()  
    thread.start()  

这个默认实现其实是简单地调用了QThread.exec()函数,而这个函数,按照我们前面所说的,其实是开始了一个事件循环
QRunnable是我们要介绍的第二个类。这是一个轻量级的抽象类,用于开始一个另外线程的任务。这种任务是运行过后就丢弃的。由于这个类是抽象类,我们需要继承QRunnable,然后重写其纯虚函数QRunnable.run():

class Task(QRunnable):  
    def __init__(self):  
        super(QRunnable,self).__init__()  
    def run(self):  
        pass               #线程相关代码 

要真正执行一个QRunnable对象,我们需要使用QThreadPool类。顾名思义,这个类用于管理一个线程池。通过调用QThreadPool.start(runnable)函数,我们将一个QRunnable对象放入QThreadPool的执行队列。一旦有线程可用,线程池将会选择一个QRunnable对象,然后在那个线程开始执行。所有 PyQt 应用程序都有一个全局线程池,我们可以使用QThreadPool.globalInstance()获得这个全局线程池;与此同时,我们也可以自己创建私有的线程池,并进行手动管理。

    task=Task()  
    QThreadPool.globalInstance().start(task)  

需要注意的是,QRunnable不是一个QObject,因此也就没有内建的与其它组件交互的机制。为了与其它组件进行交互,你必须自己编写低级线程原语,例如使用 mutex 守护来获取结果等。

   QtConcurrent是我们要介绍的最后一个对象。这是一个高级 API,构建于QThreadPool之上,用于处理大多数通用的并行计算模式:map、reduce 以及 filter。它还提供了QtConcurrent.run()函数,用于在另外的线程运行一个函数。注意,QtConcurrent是一个命名空间而不是一个类,因此其中的所有函数都是命名空间内的全局函数。


   不同于QThread和QRunnable,QtConcurrent不要求我们使用低级同步原语:所有的QtConcurrent都返回一个QFuture对象。这个对象可以用来查询当前的运算状态(也就是任务的进度),可以用来暂停/回复/取消任务,当然也可以用来获得运算结果。注意,并不是所有的QFuture对象都支持暂停或取消的操作。比如,由QtConcurrent.run()返回的QFuture对象不能取消,但是由QtConcurrent.mappedReduced()返回的是可以的。QFutureWatcher类则用来监视QFuture的进度,我们可以用信号槽与QFutureWatcher进行交互(注意,QFuture也没有继承QObject)。

下面我们可以对比一下上面介绍过的三种类:
这里写图片描述

十二、PyQt信号和槽传递额外参数

使用Pyqt编程过程中,经常会遇到给槽函数传递额外参数的情况。但是信号-槽机制只是指定信号如何连接到槽,信号定义的参数被传递给槽,而额外的参数(用户定义)不能直接传递。
而传递额外参数又是很有用处。你可能使用一个槽处理多个组件的信号,有时要传递额外的信息。
一种方法是使用lambda表达式。

作者给出的是pyqt4版本的

from PyQt4.QtCore import *  
from PyQt4.QtGui import *  

class MyForm(QMainWindow):  
    def __init__(self, parent=None):  
        super(MyForm, self).__init__(parent)  
        button1 = QPushButton('Button 1')  
        button2 = QPushButton('Button 1')  
        button1.clicked.connect(lambda: self.on_button(1))  
        button2.clicked.connect(lambda: self.on_button(2))  

        layout = QHBoxLayout()  
        layout.addWidget(button1)  
        layout.addWidget(button2)  

        main_frame = QWidget()  
        main_frame.setLayout(layout)  

        self.setCentralWidget(main_frame)  

    def on_button(self, n):  
        print('Button {0} clicked'.format(n))  

if __name__ == "__main__":  
    import sys  
    app = QApplication(sys.argv)  
    form = MyForm()  
    form.show()  
    app.exec_() 

解释一下,on_button是怎样处理从两个按钮传来的信号。我们使用lambda传递按钮数字给槽,也可以传递任何其他东西—甚至是按钮组件本身(假如,槽打算把传递信号的按钮修改为不可用)
第2个方法是使用functools里的partial函数。

 button1.clicked.connect(partial(self.on_button, 1))  
button2.clicked.connect(partial(self.on_button, 2)) 

哪个办法好一点?这个属于风格的问题。个人观点,喜欢lambda,条理清楚,而且灵活。

     from PyQt4.QtCore import *  
    from PyQt4.QtGui import *  
    from functools import partial  
    import sys  

    class Bu1(QWidget):  

        def __init__(self, parent=None):  
            super(Bu1, self).__init__(parent)  
            #水平盒式布局  
            layout = QHBoxLayout()  
            #显示  
            self.lbl = QLabel('no button is pressed')  
            #循环5个按钮  
            for i in range(5):  
                but = QPushButton(str(i))  
                layout.addWidget(but)  
                #信号和槽连接  
                but.clicked.connect(self.cliked)  

            #使用封装,lambda  
            but = QPushButton('5')  
            layout.addWidget(but)  
            but.clicked.connect(lambda: self.on_click('5'))  
            #使用个who变量,结果不正常,显示 False is pressed  
            #but.clicked.connect(lambda who="5": self.on_click(who))  

            #使用封装,partial函数  
            but = QPushButton('6')  
            layout.addWidget(but)  
            but.clicked.connect(partial(self.on_click, '6'))  

            layout.addWidget(self.lbl)  
            #设置布局  
            self.setLayout(layout)  

        #传递额外参数     
        def cliked(self):  
            bu = self.sender()  
            if isinstance(bu, QPushButton):  
                self.lbl.setText('%s is pressed' % bu.text())  
            else:  
                self.lbl.setText('no effect')  
        def on_click(self, n):  
            self.lbl.setText('%s is pressed' % n)  

    if __name__ == '__main__':          
        app = QApplication(sys.argv)  
        bu =Bu1()  
        bu.show()  
        app.exec_()  

《Rapid GUI Program with Python and QT》 P143例子。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值