PyQt5 应用程序框架

文章目录

  • 一、PyQt5 Gui程序的基本框架
  • 二、使用 UI Designer
      • 2.1 用Desinger设计窗体
      • 2.2 将ui文件编译成py文件
      • 2.3 使用widget.py类
  • 三、信号槽
    • 内建信号、槽函数
    • 自定义信号和槽
    • 自定义信号与槽的演示
    • 说明
      • 1、信号的定义
      • 2、 信号的发射
      • 3、信号的连接
      • 4、修饰符 @pyqtSlot
      • 5、信号和槽的断开
    • 用法注意
  • 四、资源文件
  • 创建和使用资源文件
    • 资源文件的编译

一、PyQt5 Gui程序的基本框架

我们从最简单的示例来认识PyQt5的程序框架
从 文件->新建项目,创建一个默认的python脚本
PyQt5 应用程序框架_第1张图片
点击创建;目录结构如下图
PyQt5 应用程序框架_第2张图片
直接点击右上角的绿色小三角,运行程序,可以看到下面的"Hi,Pycharm"输出。
PyQt5 应用程序框架_第3张图片
这是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 应用程序框架_第4张图片
从代码可以看出,PyQt5的基本结构有:

  1. import PyQt5的模块
  2. app = QtWidgets.QApplication(sys.argv) 创建了QApplication类
  3. 创建了一个QLabel作为GUI窗体
  4. sys.exit(app.exec_()) 开启了消息事件循环,窗口关闭后,程序退出。

二、使用 UI Designer

Qt GUI 提供了两种方式来开发GUI程序,一种是纯代码的方式,一种是通过Qt Designer可视化界面来设计,当界面的元素很多,其中有各种的布局、按钮、信号、槽的情况下,使用可视化Designer通过简单的拖动,设置可以大大便利功能的开发。
那如何在PyQt5中使用Qt Desinger呢? 步骤主要有如下

  1. 在 Qt Desinger中设计窗体 *.ui
  2. 使用pyuic5 将 *.ui 转换为Python程序文件
  3. 使用转换后的*.py Python类来构建

2.1 用Desinger设计窗体

首先,我们用QtCreator创建一个最贱的基于QWidget的窗口程序:如下
PyQt5 应用程序框架_第5张图片
用Desinger编辑widget.ui,拖入一个QLabel和一个QPushButton
PyQt5 应用程序框架_第6张图片
我们切换到编辑模式,可以看到ui文件内容,其实是个xml结构的文件,里面定义了元素的各种属性
PyQt5 应用程序框架_第7张图片
我们运行下,可以看到程序的效果:跑题了,我们应该如何在Python下用UI文件呢?
PyQt5 应用程序框架_第8张图片

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文件,如下
PyQt5 应用程序框架_第9张图片
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目录下
PyQt5 应用程序框架_第10张图片
然后修改我们的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,结果如下
PyQt5 应用程序框架_第11张图片
至此,我们已经在python中使用了我们在Qtcreator创建的ui文件,并且成功运行展示了窗口。

三、信号槽

信号和槽是Qt框架的精髓,为了更全面的展示信号槽机制,我们对上面的ui文件做了些修改。如图
PyQt5 应用程序框架_第12张图片
我们增加了两个按钮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按钮,可以看到
PyQt5 应用程序框架_第13张图片
PyQt5 应用程序框架_第14张图片
完整的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 信号    

运行结果:
PyQt5 应用程序框架_第15张图片

可以看到,信号的定义需要注意一下几点:

说明

1、信号的定义

## 定义一个带str类型参数的信号
   nameChanged = pyqtSignal(str)
## overload型信号,两种参数,一种int,一种str,第一个int时默认信号参数
   ageChanged = pyqtSignal([int],[str])

2、 信号的发射

self.ageChanged[str].emit(ageInfo)   #str参数信号
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
PyQt5 应用程序框架_第16张图片
PyQt5 应用程序框架_第17张图片生成了L1.qrc文件
PyQt5 应用程序框架_第18张图片
PyQt5 应用程序框架_第19张图片
右键,可拿到该图片的路径 比如 “:/bg.png” 或 “qrc:/bg.png”

资源文件的编译

资源文件用过pyrcc5来编译成Python文件

pyrcc5 L1.qrc -o L1_rc.qrc

注意 编译后的资源文件名,必须是源文件名后面加"_rc"

PyQt5 应用程序框架_第20张图片
如图可以展示图片了

你可能感兴趣的:(PyQt5,pyqt5)