一、PyQt5 Gui程序的基本框架
我们从最简单的示例来认识PyQt5的程序框架 从 文件->新建项目,创建一个默认的python脚本 点击创建;目录结构如下图 直接点击右上角的绿色小三角,运行程序,可以看到下面的"Hi,Pycharm"输出。 这是pycharm创建一个默认python脚本文件,然而与本节的PyQt5还没有关系,我们可以修改下main.py内容如下:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
app = QtWidgets.QApplication(sys.argv)
LabHello = QtWidgets.QLabel()
LabHello.setText("Hello World, PyQt5")
LabHello.setMinimumSize(400,200)
LabHello.show()
sys.exit(app.exec_())
运行脚本:效果如下 从代码可以看出,PyQt5的基本结构有:
- import PyQt5的模块
- app = QtWidgets.QApplication(sys.argv) 创建了QApplication类
- 创建了一个QLabel作为GUI窗体
- sys.exit(app.exec_()) 开启了消息事件循环,窗口关闭后,程序退出。
二、使用 UI Designer
Qt GUI 提供了两种方式来开发GUI程序,一种是纯代码的方式,一种是通过Qt Designer可视化界面来设计,当界面的元素很多,其中有各种的布局、按钮、信号、槽的情况下,使用可视化Designer通过简单的拖动,设置可以大大便利功能的开发。 那如何在PyQt5中使用Qt Desinger呢? 步骤主要有如下
- 在 Qt Desinger中设计窗体 *.ui
- 使用pyuic5 将 *.ui 转换为Python程序文件
- 使用转换后的*.py Python类来构建
2.1 用Desinger设计窗体
首先,我们用QtCreator创建一个最贱的基于QWidget的窗口程序:如下 用Desinger编辑widget.ui,拖入一个QLabel和一个QPushButton 我们切换到编辑模式,可以看到ui文件内容,其实是个xml结构的文件,里面定义了元素的各种属性 我们运行下,可以看到程序的效果:跑题了,我们应该如何在Python下用UI文件呢?
2.2 将ui文件编译成py文件
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作为其控件的父窗口.
2.3 使用widget.py类
我们把上面生成widget.py拷贝我们第一步的main.py目录下 然后修改我们的main.py内容如下:
import sys
from PyQt5 import QtWidgets
import widget
app = QtWidgets.QApplication(sys.argv)
parentWidget = QtWidgets.QWidget()
ui = widget.Ui_Widget()
ui.setupUi(parentWidget)
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)
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__<signal_name>() 比如我们上面的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)
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):
nameChanged = pyqtSignal(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)
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)
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)
def do_nameChanged(self, name):
print("Hello,"+name)
if __name__ == "__main__":
print("**创建对象时**")
boy=Human("Boy",16)
resp=Responsor()
boy.nameChanged.connect(resp.do_nameChanged)
boy.ageChanged.connect(resp.do_ageChanged_int)
boy.ageChanged[str].connect(resp.do_ageChanged_str)
print("\n **建立连接后**")
boy.setAge(35)
boy.setName("Jack")
boy.ageChanged[str].disconnect(resp.do_ageChanged_str)
print("\n **断开ageChanged[str]的连接后**")
boy.setAge(10)
运行结果:
可以看到,信号的定义需要注意一下几点:
说明
1、信号的定义
nameChanged = pyqtSignal(str)
ageChanged = pyqtSignal([int],[str])
2、 信号的发射
self.ageChanged[str].emit(ageInfo)
self.nameChanged.emit(self.__name) //name参数
3、信号的连接
boy.nameChanged.connect(resp.do_nameChanged)
4、修饰符 @pyqtSlot
修饰符@pyqtSlot用来声明槽函数的参数类型,特别是用在overload,以确保信号和槽函数能正确的连接
@pyqtSlot(int)
def do_ageChanged_int(self,age):
print("你的年龄是:"+str(age))
@pyqtSlot(str)
def do_ageChanged_str(self,ageInfo):
print(ageInfo)
5、信号和槽的断开
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"
如图可以展示图片了
|