我们从最简单的示例来认识PyQt5的程序框架
从 文件->新建项目,创建一个默认的python脚本
点击创建;目录结构如下图
直接点击右上角的绿色小三角,运行程序,可以看到下面的"Hi,Pycharm"输出。
这是pycharm创建一个默认python脚本文件,然而与本节的PyQt5还没有关系,我们可以修改下main.py内容如下:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets #导入PyQt5包中的几个模块
app = QtWidgets.QApplication(sys.argv) #创建App,用QApplication类
LabHello = QtWidgets.QLabel() #创建一个标签LabHello
LabHello.setText("Hello World, PyQt5") #设置标签文字
LabHello.setMinimumSize(400,200) #设置最小宽高
LabHello.show()
sys.exit(app.exec_()) #应用程序运行
运行脚本:效果如下
从代码可以看出,PyQt5的基本结构有:
Qt GUI 提供了两种方式来开发GUI程序,一种是纯代码的方式,一种是通过Qt Designer可视化界面来设计,当界面的元素很多,其中有各种的布局、按钮、信号、槽的情况下,使用可视化Designer通过简单的拖动,设置可以大大便利功能的开发。
那如何在PyQt5中使用Qt Desinger呢? 步骤主要有如下
首先,我们用QtCreator创建一个最贱的基于QWidget的窗口程序:如下
用Desinger编辑widget.ui,拖入一个QLabel和一个QPushButton
我们切换到编辑模式,可以看到ui文件内容,其实是个xml结构的文件,里面定义了元素的各种属性
我们运行下,可以看到程序的效果:跑题了,我们应该如何在Python下用UI文件呢?
pyuic5.exe 在Python安装目录的/scripts/目录下,打开cmd,进入到上面ui文件所在目录
C:\Users\wmm\Desktop\test\qt\L1,然后执行命令pyuic5 -o widget.py widget.ui
,可以看到当前目录生成了一个 widget,py文件,如下
widget.py文件内容如下:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Widget(object):
def setupUi(self, Widget):
Widget.setObjectName("Widget")
Widget.resize(373, 233)
self.pushButton = QtWidgets.QPushButton(Widget)
self.pushButton.setGeometry(QtCore.QRect(140, 160, 80, 20))
self.pushButton.setObjectName("pushButton")
self.label = QtWidgets.QLabel(Widget)
self.label.setGeometry(QtCore.QRect(150, 110, 121, 21))
font = QtGui.QFont()
font.setFamily("Agency FB")
font.setPointSize(14)
self.label.setFont(font)
self.label.setObjectName("label")
self.retranslateUi(Widget)
QtCore.QMetaObject.connectSlotsByName(Widget)
def retranslateUi(self, Widget):
_translate = QtCore.QCoreApplication.translate
Widget.setWindowTitle(_translate("Widget", "Widget"))
self.pushButton.setText(_translate("Widget", "关闭"))
self.label.setText(_translate("Widget", "Hello pyQt"))
可以看到定义了基于object的Ui_Widget类和 两个方法,def setupUi和 def retranslateUi
需要注意的是:
Ui_Widget是基于object,其本身并不是一个窗体,窗体是通过setupUi的参数Widget来传入的,Widget作为其控件的父窗口.
我们把上面生成widget.py拷贝我们第一步的main.py目录下
然后修改我们的main.py内容如下:
import sys
from PyQt5 import QtWidgets
import widget
app = QtWidgets.QApplication(sys.argv)
parentWidget = QtWidgets.QWidget() #创建窗口的基类QWidget的实例
ui = widget.Ui_Widget() #创建UI窗口的实例
ui.setupUi(parentWidget) #以baseWidget作为传递参数
ui.label.setText("修改了文本")
parentWidget.show()
sys.exit(app.exec_())
运行下main.py,结果如下
至此,我们已经在python中使用了我们在Qtcreator创建的ui文件,并且成功运行展示了窗口。
信号和槽是Qt框架的精髓,为了更全面的展示信号槽机制,我们对上面的ui文件做了些修改。如图
我们增加了两个按钮Text1 和 Text2, 一个QCheckBox,并且内置修改了关闭按钮的槽函数连接,仍然用 pyuic5 -o widget.py widget.ui
生成widget.py脚本,并且拷贝到我们的Pyqt工程中,打开widget.py,我们可以看到内容如下:
可以看到我们给窗口的元素重新设置了objectname.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Widget(object):
def setupUi(self, Widget):
Widget.setObjectName("Widget")
Widget.resize(373, 233)
self.pbn_close = QtWidgets.QPushButton(Widget)
self.pbn_close.setGeometry(QtCore.QRect(260, 180, 80, 20))
self.pbn_close.setObjectName("pbn_close")
self.lab_hello = QtWidgets.QLabel(Widget)
self.lab_hello.setGeometry(QtCore.QRect(90, 40, 251, 51))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(14)
self.lab_hello.setFont(font)
self.lab_hello.setObjectName("lab_hello")
self.chb_visible = QtWidgets.QCheckBox(Widget)
self.chb_visible.setGeometry(QtCore.QRect(40, 130, 73, 18))
self.chb_visible.setObjectName("chb_visible")
self.pbn_text1 = QtWidgets.QPushButton(Widget)
self.pbn_text1.setGeometry(QtCore.QRect(40, 180, 80, 20))
self.pbn_text1.setObjectName("pbn_text1")
self.pbn_text2 = QtWidgets.QPushButton(Widget)
self.pbn_text2.setGeometry(QtCore.QRect(140, 180, 80, 20))
self.pbn_text2.setObjectName("pbn_text2")
self.retranslateUi(Widget)
self.pbn_close.clicked.connect(Widget.close) #关闭按钮连接了Widget的close函数
QtCore.QMetaObject.connectSlotsByName(Widget)
def retranslateUi(self, Widget):
_translate = QtCore.QCoreApplication.translate
Widget.setWindowTitle(_translate("Widget", "Widget"))
self.pbn_close.setText(_translate("Widget", "关闭"))
self.lab_hello.setText(_translate("Widget", "Hello pyQt"))
self.chb_visible.setText(_translate("Widget", "隐藏"))
self.pbn_text1.setText(_translate("Widget", "Text1"))
self.pbn_text2.setText(_translate("Widget", "Text2"))
其中重点注意这两句:
self.pbn_close.clicked.connect(Widget.close)
QtCore.QMetaObject.connectSlotsByName(Widget)
可以看到在pyqt5里面,信号槽的链接方式是:
sender.signalName.connect(receiver.slotName)
QtCore.QMetaObject.connectSlotsByName(Widget)是使用qt的元对象默认将信号和槽函数关联起来,规则是:
on__
比如我们上面的pbn_text1按钮,默认的槽函数链接会是:
on_pbn_text1_clicked()
我们都知道QPushButton 有clicked()信号,所以我们尝试在widget.py文件中定义槽函数
def onPbnText1Clicked(self):
t = self.pbn_text1.text()
self.lab_hello.setText(t)
并在def setupUi(self, Widget):
中添加这句self.pbn_text1.clicked.connect(self.onPbnText1Clicked)
运行程序,点击Text1按钮,可以看到
完整的widget.py文件内容如下:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Widget(object):
def setupUi(self, Widget):
Widget.setObjectName("Widget")
Widget.resize(373, 233)
self.pbn_close = QtWidgets.QPushButton(Widget)
self.pbn_close.setGeometry(QtCore.QRect(260, 180, 80, 20))
self.pbn_close.setObjectName("pbn_close")
self.lab_hello = QtWidgets.QLabel(Widget)
self.lab_hello.setGeometry(QtCore.QRect(90, 40, 251, 51))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(14)
self.lab_hello.setFont(font)
self.lab_hello.setObjectName("lab_hello")
self.chb_visible = QtWidgets.QCheckBox(Widget)
self.chb_visible.setGeometry(QtCore.QRect(40, 130, 73, 18))
self.chb_visible.setObjectName("chb_visible")
self.pbn_text1 = QtWidgets.QPushButton(Widget)
self.pbn_text1.setGeometry(QtCore.QRect(40, 180, 80, 20))
self.pbn_text1.setObjectName("pbn_text1")
self.pbn_text2 = QtWidgets.QPushButton(Widget)
self.pbn_text2.setGeometry(QtCore.QRect(140, 180, 80, 20))
self.pbn_text2.setObjectName("pbn_text2")
self.retranslateUi(Widget)
self.pbn_close.clicked.connect(Widget.close) #关闭按钮连接了Widget的close函数
self.pbn_text1.clicked.connect(self.onPbnText1Clicked)
QtCore.QMetaObject.connectSlotsByName(Widget)
def retranslateUi(self, Widget):
_translate = QtCore.QCoreApplication.translate
Widget.setWindowTitle(_translate("Widget", "Widget"))
self.pbn_close.setText(_translate("Widget", "关闭"))
self.lab_hello.setText(_translate("Widget", "Hello pyQt"))
self.chb_visible.setText(_translate("Widget", "隐藏"))
self.pbn_text1.setText(_translate("Widget", "Text1"))
self.pbn_text2.setText(_translate("Widget", "Text2"))
def onPbnText1Clicked(self):
t = self.pbn_text1.text()
self.lab_hello.setText(t)
我们用一下内容,保存成human.py 来演示自定义信号、槽的用法
import sys
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal
class Human(QObject):
## 定义一个带str类型参数的信号
nameChanged = pyqtSignal(str)
## overload型信号,两种参数,一种int,一种str
ageChanged = pyqtSignal([int],[str])
def __init__(self,name='Mike',age=10,parent=None):
super().__init__(parent) #调用父类构造函数
self.setAge(age)
self.setName(name)
def setAge(self,age):
self.__age= age
self.ageChanged.emit(self.__age) #int参数信号
if age<=18:
ageInfo="你是 少年"
elif (18< age <=35):
ageInfo="你是 年轻人"
elif (35< age <=55):
ageInfo="你是 中年人"
elif (55< age <=80):
ageInfo="您是 老人"
else:
ageInfo="您是 寿星啊"
self.ageChanged[str].emit(ageInfo) #str参数信号
def setName(self,name):
self.__name = name
self.nameChanged.emit(self.__name)
class Responsor(QObject):
@pyqtSlot(int)
def do_ageChanged_int(self,age):
print("你的年龄是:"+str(age))
@pyqtSlot(str)
def do_ageChanged_str(self,ageInfo):
print(ageInfo)
# @pyqtSlot(str)
def do_nameChanged(self, name):
print("Hello,"+name)
if __name__ == "__main__": ##测试程序
print("**创建对象时**")
boy=Human("Boy",16)
resp=Responsor()
boy.nameChanged.connect(resp.do_nameChanged)
## overload的信号如果都定义了槽函数,两个槽函数不能同名,连接时需要给信号加参数区分
boy.ageChanged.connect(resp.do_ageChanged_int) #缺省参数,int型
boy.ageChanged[str].connect(resp.do_ageChanged_str) #str型参数
print("\n **建立连接后**")
boy.setAge(35) #发射 两个ageChanged 信号
boy.setName("Jack") #发射nameChanged信号
boy.ageChanged[str].disconnect(resp.do_ageChanged_str) #断开连接
print("\n **断开ageChanged[str]的连接后**")
boy.setAge(10) #发射 两个ageChanged 信号
可以看到,信号的定义需要注意一下几点:
## 定义一个带str类型参数的信号
nameChanged = pyqtSignal(str)
## overload型信号,两种参数,一种int,一种str,第一个int时默认信号参数
ageChanged = pyqtSignal([int],[str])
self.ageChanged[str].emit(ageInfo) #str参数信号
self.nameChanged.emit(self.__name) //name参数
boy.nameChanged.connect(resp.do_nameChanged)
修饰符@pyqtSlot用来声明槽函数的参数类型,特别是用在overload,以确保信号和槽函数能正确的连接
@pyqtSlot(int)
def do_ageChanged_int(self,age):
print("你的年龄是:"+str(age))
@pyqtSlot(str)
def do_ageChanged_str(self,ageInfo):
print(ageInfo)
boy.ageChanged[str].disconnect(resp.do_ageChanged_str) #断开连接
自定义信号时,尽量不要定义overload型信号,因为python的某些类型转换为C++时,对C++来说可能时同一种类型的参数,比如,若定义一个overload型的信号:
valueChanged = pyqtSignale([dict],[list])
dict和lict在Python中是不同的数据类型,但是转化为c++时,有可能就是同一个数据类型了,这样的连接方式就会出现问题。
资源文件主要功能是存储图片和图标文件。
在Qt Creator里,右键工程add new->选择Qt-> Qt Resource File
生成了L1.qrc文件
右键,可拿到该图片的路径 比如 “:/bg.png” 或 “qrc:/bg.png”
资源文件用过pyrcc5来编译成Python文件
pyrcc5 L1.qrc -o L1_rc.qrc
注意 编译后的资源文件名,必须是源文件名后面加"_rc"