Python:PyQt学习


PyQt是一个用于创建GUI应用程序的跨平台工具包,它将Python与Qt库融为一体。PyQt允许使用Python语言调用Qt库中的API。这样做的最大好处就是在保留了Qt高运行效率的同时,大大提高了开发效率。

基础

第一个 PyQt 程序

安装PyQt5

pip install pyqt5-tools

即可同时安装 PyQt5 和一些重要的工具,比如 Qt designer。

基础代码

from PyQt5.QtWidgets import QApplication, QMainWindow
#from PyQt5.Qt import *   # 导入所有包
import sys

#创建应用程序对象
app = QApplication(sys.argv)
# 创建控件
window = QMainWindow()
# 设置控件
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle('第一个窗口')
# 展示控件
window.show()
sys.exit(app.exec_())

封装到类中

上面的代码把控件对应的变量名全部作为全局变量。如果要设计稍微复杂一些的程序,就会出现太多的控件对应的变量名。而且这样也不利于代码的模块化。所以,我们通常应该把 一个窗口和其包含的控件,对应的代码全部封装到类中,如下所示:

from PyQt5.QtWidgets import QApplication, QMainWindow
import sys

class Window():
    def __init__(self):
        self.window = QMainWindow()
        self.window.resize(500, 400)
        self.window.move(300, 300)
        self.window.setWindowTitle('第一个窗口')

if __name__ == '__main__':
    app = QApplication(sys.argv)
    stats = Window()
    stats.window.show()
    sys.exit(app.exec_())

if __name__ == '__main__': 作用:python文件通常有两种使用方法,第一是作为脚本直接执行,第二是 import 到其他的 python 脚本中被调用(模块重用)执行。因此 if __name__ == 'main': 的作用就是控制这两种情况执行代码的过程,在 if __name__ == 'main': 下的代码只有在第一种情况下(即文件作为脚本直接执行)才会被执行,而 import 到其他脚本中是不会被执行的,因此可以在if __name__ == 'main':下放这个Python 文件的测试代码,即 import 到其他的 python 脚本中不会被执行的代码 。

面向对象版本

面向对象就是在编程的时候尽可能的去模拟真实的现实世界,按照现实世界中的逻辑去处理一个问题,分析问题中参与其中的有哪些实体,这些实体应该有什么属性和方法,我们如何通过调用这些实体的属性和方法去解决问题。

面向过程就是面向解决问题的过程进行编程。是一种以事件为中心的编程思想,编程的时候把解决问题的步骤分析出来,然后用函数把这些步骤实现,在一步一步的具体步骤中再按顺序调用函数。

from PyQt5.QtWidgets import QApplication, QMainWindow,QLabel
import sys

class Window(QMainWindow):
    def __init__(self):
        super().__init__() # 继承父类的init方法
        self.resize(500, 400)
        self.move(300, 300)
        self.setWindowTitle('第一个窗口')
        self.setup_ui()

    def setup_ui(self):
        label = QLabel(self)
        label.setText("这是一个标签")

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

PyCharm 使用技巧

PyCharm 设置活动模板设置

活动模板:很简单的一个示例,就是在 PyCharm 中输入 main 关键字,会自动带出一些默认的代码,这个就是活动模板配置

Pycharm 中 File -》Settings –> Editor –> Live Templates –> Python –>右上角“+”号

Pycharm 设置代码作者信息

Pycharm 中 File –> Settings –> Editor –> File and Code Templates–> Python Script

# -*- coding: utf-8 -*-
"""
-------------------------------------------------
# @Project  :${PROJECT_NAME}
# @File     :${NAME}
# @Date     :${DATE} ${TIME}
# @Author   :作者名
# @Software :${PRODUCT_NAME}
-------------------------------------------------
"""

查看继承关系

1、Ctrl+鼠标左键,点入到内置文件中,翻到类名处查看

2、 通过保留属性__base__,返回的是直接继承关系

print(QWidget.__bases__) # 返回结果是元组

3、使用 mro() ,返回整个继承链条!

print(QObject.mro()) # 返回结果是列表

控件学习

QObject

QObject 所有 Qt 对象的基类

对象名称和属性

API

  • setObjectName(“unique name”) 设置对像名称
  • objectName() 获取Qt对象的名称
  • setProperty(“name”, “value”) 设置对像属性
  • property(“name”) 获取对象的属性值
  • dynamicPropertyNames() 获取对象中所有通过setProperty()设置的属性名称

API测试:

from PyQt5.Qt import *  # 刚开始学习可以这样一下导入
import sys


class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QObject的学习")
        self.resize(400, 400)
        self.set_ui()

    def set_ui(self):
        self.QObject_test()

    def QObject_test(self):
        obj = QObject()
        obj.setObjectName("jianjian")
        print(obj.objectName()) 
        obj.setProperty("notice_level", "error")
        obj.setProperty("notice_level2", "warning")
        print(obj.property("notice_level"))
        print(obj.dynamicPropertyNames())


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

输出:

1 jianjian
2 error
3 [PyQt5.QtCore.QByteArray(b'notice_level'), PyQt5.QtCore.QByteArray(b'notice_level2')]

应用场景

  • 用于qss的ID选择器,属性选择器,方便统一设置样式

QObejct.qss

QLabel#notice {
    font-size :20px;
    color :green;
    border:1px solid gray;
    border-radius:8px;
}
QLabel#notice[notice_level = "warning"] {
    color :yellow;
    border-color:yellow;
}
QLabel#notice[notice_level = "error"] {
    color :red;
    border-color:red;
}

test.py

from PyQt5.Qt import *  # 刚开始学习可以这样一下导入
import sys

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QObject的学习")
        self.resize(300, 300)
        self.set_ui()

    def set_ui(self):
        self.QObject_test()

    def QObject_test(self): # 读取QObject.qss
        with open("QObject.qss", "r") as f:
            qApp.setStyleSheet(f.read())

        label = QLabel(self)
        label.setText("标签1")
        label.setObjectName("notice")

        label2 = QLabel(self)
        label2.setText("标签2")
        label2.move(100, 100)
        label2.setObjectName("notice")
        label2.setProperty("notice_level", "warning")

        label3 = QLabel(self)
        label3.setText("标签3")
        label3.move(200, 200)
        label3.setObjectName("notice")
        label3.setProperty("notice_level","error")

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

父子关系操作

API

  • setParent(parent) 设置父对象,父对象只能设置一个
  • parent() 获取父对象
  • children() 获取所有直接子对象
  • findChild(参数1,参数2,参数3) 获取某一个指定类型和名称的子对象
    • 参数1:
      • 类型 QObject
      • 类型元组 (QPushButton, QLabel)
    • 参数2:对象名称
    • 参数3:查找选项
      • Qt.FindChildrenRecursively 递归查找(默认选项)
      • Qt.FindDirectChildrenOnly 只查找直接子对象
  • findChildren(参数1,参数2,参数3) 获取某一个指定名称所有子对象,参数同上

API测试:构造如下父子图

from PyQt5.Qt import *
import sys

class Window(QMainWindow):
    def __init__(self):
        super().__init__() # 继承父类的init方法
        self.resize(500, 400)
        self.move(300, 300)
        self.setWindowTitle('父子对象')
        self.set_ui()

    def set_ui(self):
        self.QObject_test()

    def QObject_test(self):
        obj0 = QObject()
        obj1 = QObject()
        obj2 = QObject()
        obj3 = QObject()
        obj4 = QObject()
        obj5 = QObject()

        # 打印各个对象变量  也可print('obj0:', obj0)
        for i in range(6):
            name = "obj" + str(i)
            print(name,eval(name))

        #设置父对象
        obj1.setParent(obj0)
        obj2.setParent(obj0)
        obj3.setParent(obj1)
        obj4.setParent(obj2)
        obj5.setParent(obj2)

        # 设置对象名称
        obj2.setObjectName("2")
        obj3.setObjectName("3")
        
        # API测试结果
        print('obj0父对象:', obj0.parent())
        print('obj0所有直接子对象:', obj0.children())
        print('obj0的第一个子对象:', obj0.findChild(QObject))
        print('obj0的第二个子对象:', obj0.findChild(QObject,"2"))
        print('obj0的第三个子对象(孙子):', obj0.findChild(QObject, "3", Qt.FindChildrenRecursively)) #递归(默认选项,可不写)
        print('obj0的第三个子对象(孙子):', obj0.findChild(QObject, "3", Qt.FindDirectChildrenOnly))  #只查找直接子对象
        print('obj0的所有子对象:', obj0.findChildren(QObject))

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

输出:

obj0 <PyQt5.QtCore.QObject object at 0x000002749F7F33A8>
obj1 <PyQt5.QtCore.QObject object at 0x000002749F7F3438>
obj2 <PyQt5.QtCore.QObject object at 0x000002749F7F34C8>
obj3 <PyQt5.QtCore.QObject object at 0x000002749F7F3558>
obj4 <PyQt5.QtCore.QObject object at 0x000002749F7F35E8>
obj5 <PyQt5.QtCore.QObject object at 0x000002749F7F3678>
obj0父对象: None
obj0所有直接子对象: [<PyQt5.QtCore.QObject object at 0x000002749F7F3438>, <PyQt5.QtCore.QObject object at 0x000002749F7F34C8>]
obj0的第一个子对象: <PyQt5.QtCore.QObject object at 0x000002749F7F3438>
obj0的第二个子对象: <PyQt5.QtCore.QObject object at 0x000002749F7F34C8>
obj0的第三个子对象(孙子)<PyQt5.QtCore.QObject object at 0x000002749F7F3558>
obj0的第三个子对象(孙子): None
obj0的所有子对象: [<PyQt5.QtCore.QObject object at 0x000002749F7F3438>, <PyQt5.QtCore.QObject object at 0x000002749F7F3558>, <PyQt5.QtCore.QObject object at 0x000002749F7F34C8>, <PyQt5.QtCore.QObject object at 0x000002749F7F35E8>, <PyQt5.QtCore.QObject object at 0x000002749F7F3678>]

应用场景

  • 内存管理机制-当父对象销毁的时候,子对象也会被销毁。

  • 如果一个控件,没有任何父控件。那么就会被当或顶层控件(窗口)。

  • 如果相要一个控件被包含在另外一个控件内部.就需要设置父子关系。

信号与槽机制

API

  • widget.信号.connect(槽) 连接信号与槽
  • widget.信号.disconnect() 取消连接信号与槽
  • widget.blockSignals(bool) 临时(取消)阻止指定控件所有的信号与槽的连接
  • widget.signalsBlocked() 信号是否被阻止
  • widget.receivers(信号) 返回连接到信号的接收器数量(槽的数量)

应用场景

监听信号, 响应用户行为

案例一:点击按钮显示内容

from PyQt5.Qt import *
import sys

class Window(QMainWindow):
    def __init__(self):
        super().__init__() # 继承父类的init方法
        self.resize(500, 400)
        self.move(300, 300)
        self.setWindowTitle('信号与槽')
        self.setup_ui()

    def setup_ui(self):
        self.QObject_signal_test()

    def QObject_signal_test(self):
        btn = QPushButton(self)
        btn.setText("点我呀")

        def clicked_slot(self):
            print("点我干啥")

        btn.clicked.connect(clicked_slot)

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

案例二:在所有修改的窗口标题前, 添加前缀”简简-“

from PyQt5.Qt import *  # 刚开始学习可以这样一下导入
import sys

class Window(QMainWindow):
    def __init__(self):
        super().__init__() # 继承父类的init方法
        self.resize(500, 400)
        self.move(300, 300)
        self.setWindowTitle('信号与槽')

if __name__ == '__main__':
    app = QApplication(sys.argv) # 创建应用程序对象
    window = Window()

    def windowTitleChanged_slot(arg):
        print("修改的标题是:",arg)    # arg为windowTitleChanged传回的参数
        window.blockSignals(True)   # 临时阻塞信号,不阻断将会陷入死循环
        print("信号是否阻断:",window.signalsBlocked())
        window.setWindowTitle("简简-"+ arg)
        window.blockSignals(False)  # 再次开启信号的连接,不开启的话只能成功添加一次前缀
        print("信号是否阻断:", window.signalsBlocked())

    window.windowTitleChanged.connect(windowTitleChanged_slot) # 连接信号和槽
    window.setWindowTitle("测试")
    window.setWindowTitle("测试2")
    print("信号有{}个槽函数".format(window.receivers(window.windowTitleChanged)))
    window.windowTitleChanged.disconnect()  # 取消信号和槽之间的联系
    window.setWindowTitle("测试3")
    print("信号有{}个槽函数".format(window.receivers(window.windowTitleChanged)))

    window.show() # 展示控件
    sys.exit(app.exec_())

输出:

修改的标题是: 测试
信号是否阻断: True
信号是否阻断: False
修改的标题是: 测试2
信号是否阻断: True
信号是否阻断: False
信号有1个槽函数
信号有0个槽函数

类型判定

API

  • isWidgetType() 是否是控件类型
  • inherits(父类) 一个对象是否继承(直接或者间接)自某个类

API测试:

from PyQt5.Qt import *  # 刚开始学习可以这样一下导入
import sys

class Window(QMainWindow):
    def __init__(self):
        super().__init__() # 继承父类的init方法
        self.resize(500, 400)
        self.move(300, 300)
        self.setWindowTitle('类型判定')
        self.setup_ui()

    def setup_ui(self):
        self.QObject_type_judge()

    def QObject_type_judge(self):
        obj = QObject()
        w = QWidget()
        btn = QPushButton()
        label = QLabel()
        list = [obj, w, btn, label]
        print("isWidgetType测试")
        for i in list:
            print("是否是控件类型:", i.isWidgetType())
        print("inherits测试")
        for i in list:
            print("是否继承自QWidget:", i.inherits("QWidget"))

if __name__ == '__main__':
    app = QApplication(sys.argv) # 创建应用程序对象
    window = Window()
    window.show() # 展示控件
    sys.exit(app.exec_())

输出:

isWidgetType测试
是否是控件类型: False
是否是控件类型: True
是否是控件类型: True
是否是控件类型: True
inherits测试
是否继承自QWidget: False
是否继承自QWidget: True
是否继承自QWidget: True
是否继承自QWidget: True

应用场景

过滤筛选控件

案例: 创建一个窗口, 包含多个QLabel或其他控件,将包含在窗口内所有的QLabel控件, 设置背景色cyan

from PyQt5.Qt import *  # 刚开始学习可以这样一下导入
import sys

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QObject的学习")
        self.resize(400, 400)
        self.set_ui()

    def set_ui(self):
        self.QObject_type_judge()

    def QObject_type_judge(self):
        label_1 = QLabel(self)
        label_1.setText("标签1")

        label_2 = QLabel(self)
        label_2.setText("标签2")
        label_2.move(100,100)

        btn = QPushButton(self)
        btn.setText("按钮")
        btn.move(200,200)

        for widget in self.children():
            if widget.inherits("QLabel"):
                widget.setStyleSheet("background-color:cyan;")

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

对象删除

API

  • obj.deleteLater() 稍后删除,删除一个对象时, 也会解除它与父对象之间的关系
  • deleteLater()并没有将对象立即销毁,而是向主消息循环发送了一个event,下一次主消息循环收到这个event之后才会销毁对象,这样做的好处是可以在这些延迟删除的时间内完成一些操作,坏处就是内存释放会不及时

API测试:

from PyQt5.Qt import *  # 刚开始学习可以这样一下导入
import sys

class Window(QMainWindow):
    def __init__(self):
        super().__init__() # 继承父类的init方法
        self.resize(500, 400)
        self.move(300, 300)
        self.setWindowTitle('对象删除')
        self.setup_ui()

    def setup_ui(self):
        self.QObject_delete()

    def QObject_delete(self):
        obj1 = QObject()
        self.obj = obj1
        obj2 = QObject()
        obj3 = QObject()

        obj3.setParent(obj2)
        obj2.setParent(obj1)

        obj1.destroyed.connect(lambda: print("obj1 被释放"))
        obj2.destroyed.connect(lambda: print("obj2 被释放"))
        obj3.destroyed.connect(lambda: print("obj3 被释放"))

        # del obj2 # 这时候没有任何效果,因为obj2还被obj1引用着呢!
        obj2.deleteLater()     # 删除对象时,也会解除它与父对象的关系,而且是稍后删除。
        print(obj1.children()) # 因为deleteLater是稍后删除,所以子节点还在


if __name__ == '__main__':
    app = QApplication(sys.argv) # 创建应用程序对象
    window = Window()
    window.show() # 展示控件
    sys.exit(app.exec_())

输出:

[<PyQt5.QtCore.QObject object at 0x0000022E610E45E8>]
obj2 被释放
obj3 被释放

应用场景

想要移除某一个对象的时候使用

事件处理

API

  • childEvent()

  • customEvent()

  • eventFilter()

  • installEventFilter()

  • removeEventFilter

  • event()

应用场景

事件机制、拦截事件, 监听特定行为

案例

暂时欠着。。。

定时器

API

  • startTimer(ms, Qt.TimerType) -> timer_id 启动一个定时器事件并返回一个定时器timer_id

    • ms 每隔ms毫秒就会启动一次
  • Qt.TimerType

    • Qt.PreciseTimer 精确定时器:尽可能保持毫秒准确
    • Qt.CoarseTimer 粗定时器:5%的误差间隔
    • Qt.VeryCoarseTimer 很粗的定时器:只能到秒级
  • timer_id 定时器的唯一标识

  • killTimer(timer_id) 根据定时器ID,杀死定时器

  • timerEvent() 定时器执行事件

API测试

from PyQt5.Qt import *
import sys

class MyQObject(QObject):  # 重写QObject()中的timerevent() 方法
    def timerEvent(self,evnt):
        print(evnt,"每隔一秒打印")

app = QApplication(sys.argv)
window = QMainWindow()
window.setWindowTitle('定时器')
obj = MyQObject()
time_id = obj.startTimer(1000)  # 每隔1s调用QObject() 中的timerevent() 方法  #所以此时应该重写QObject()中的timerevent() 方法
# obj.killTimer(time_id)  #如果想停止它,将定时器id 传给 killTimer() 即可

window.show()
sys.exit(app.exec_())

输出:

<PyQt5.QtCore.QTimerEvent object at 0x000002081A182318> 每隔一秒打印
<PyQt5.QtCore.QTimerEvent object at 0x000002081A182318> 每隔一秒打印
<PyQt5.QtCore.QTimerEvent object at 0x000002081A182318> 每隔一秒打印

应用场景

轮询、倒计时

案例1:创建一个窗口, 并设置一个子控件QLabel,展示10s倒计时,倒计时结束, 就停止计时。

from PyQt5.Qt import *
import sys

class MyLabel(QLabel): # 封装版本

    def __init__(self, *args, **kwargs):  # 这更加通用
        super().__init__(*args, **kwargs)
        self.setText("10")
        self.setStyleSheet("font-size:22px;")
        self.move(200, 200)
        self.timer_id = self.startTimer(1000)

    def timerEvent(self, *args, **kwargs):
        # 获取当前的标签内容
        print("开始计时")
        current_sec = int(self.text())
        current_sec -= 1
        self.setText(str(current_sec))
        if current_sec == 0:
            self.killTimer(self.timer_id)
            print("计时结束")

app = QApplication(sys.argv)
window = QMainWindow()
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle('定时器')

label = MyLabel(window)

window.show()
sys.exit(app.exec_())

案例2:创建一个窗口, 通过定时器不断增加该窗口的尺寸大小,每100ms 宽高均增加1px

from PyQt5.Qt import *
import sys

class MyWidget(QWidget):
     def timerEvent(self, *args, **kwargs):
         current_w = self.width()
         current_h = self.height()
         self.resize(current_w+10,current_h+10)

app = QApplication(sys.argv)
window = MyWidget()
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle('定时器')

window.startTimer(100)

window.show()
sys.exit(app.exec_())

QWidget

所有的可视控件的基类

坐标系统

左上角为坐标原点,向右为x轴正方向,向下为y轴正方向

控件位置参照:

  • 一般控件参照父控件

  • 顶层控件则参照桌面

尺寸位置

尺寸获取API

  • x() 相对于父控件的x位置,没有父控件则相对于桌面的x位置
  • y() 同上
  • pos() x和y的组合,包含窗口框架
  • width() 控件的宽度,不包含任何窗口框架
  • height() 控件的高度,不包含任何窗口框架
  • size() width和height的组合,不包含任何窗口框架
  • geometry() 用户区域相对于父控件的位置和尺寸组合
  • rect() 0, 0, width, height的组合
  • frameSize() 框架大小
  • frameGeometry() 框架尺寸

注意: 控件显示完毕之后, 具体的位置或者尺寸数据才会正确

相关尺寸图示

from PyQt5.QtWidgets import QApplication, QWidget
import sys

app = QApplication(sys.argv)
window = QWidget()
window.resize(200, 200)
window.move(100, 100)
window.setWindowTitle('尺寸大小')
print(window.geometry())  # 输出: PyQt5.QtCore.QRect(100, 100, 200, 200) 错误

window.show()
print(window.geometry())  # 输出: PyQt5.QtCore.QRect(101, 131, 200, 200) 正确
sys.exit(app.exec_())

所以,要获取控件的尺寸,一定要在控件显示完毕之后再去获取!

尺寸设置API

  • move(x,y) 操控的是x, y;也就是pos,包括窗口框架
  • resize(width,height) 操控的是宽高,不包括窗口框架
  • setGeometry(x_noFrame, y_noFrame, width, height) 注意,此处参照为用户区域
  • adjustSize() 根据内容自适应大小
  • setFixedSize() 设置固定尺寸

点击按钮,复制标签内容,展示在标签后面

from PyQt5.QtWidgets import QApplication, QWidget,QPushButton,QLabel
import sys

app = QApplication(sys.argv)
window = QWidget()
window.resize(400, 400)
window.move(100, 100)
window.setWindowTitle('自适应尺寸')

label = QLabel(window)
label.setText("hello world")
label.move(100, 100)
label.setStyleSheet("background-color:cyan;")  # 为了更清晰看到具体的区域

def addContent():
    new_content = label.text() + "hello world"
    label.setText(new_content)
    # label.resize(label.width()+100,label.height())  笨方法 改变大小
    label.adjustSize() #自适应大小

btn = QPushButton(window)
btn.setText("增加内容")
btn.move(200, 200)
btn.clicked.connect(addContent)

window.show()
sys.exit(app.exec_())

案例

通过给定的的个数,你负责在一个窗口内创建相应个数的子控件,按照九宫格的布局进行摆放

from PyQt5.Qt import * #刚开始学习可以这样一下导入
import sys
#创建app
app  = QApplication(sys.argv)
window = QWidget()
window.show()
window.resize(500,500)
window.move(300,300)

#总控件的个数
widget_count = 10

#有多少列
column_count = 3
#一个控件的宽度
widget_width = window.width() / column_count

#有多少行
row_count  = (widget_count-1) // column_count +1
#一个控件的高度
widget_height = window.height() / row_count

for i in range(widget_count):
     w = QWidget(window)
     w.resize(widget_width,widget_height)

     # w.move(widget_width * (i % column_count), widget_height * (i // column_count))
     x,y = divmod(i,column_count) # x,y = (i//column_count, i%column_count)
     w.move(widget_width*y,widget_height*x)

     w.setStyleSheet("background-color:red;border:1px solid yellow")
     w.show()

sys.exit(app.exec_())

辅助工具

下载地址:https://jwt1399.lanzoui.com/iGnrtrz68fg

此工具对 Qwidget 尺寸大小位置 API 进行了全面使用,拖动窗口可实时显示相关位置信息。

辅助工具图示

最大最小尺寸

获取API

  • minimumWidth() 最小尺寸的宽度

  • minimumHeight() 最小尺寸的高度

  • minimumSize() 最小尺寸(宽,高)

  • maximumWidth() 最大尺寸的宽度

  • maximumHeight() 最大尺寸的高度

  • maximumSize() 最大尺寸

设置API

  • setMinimumWidth() 设置最小宽度

  • setMaximumWidth() 设置最大宽度

  • setMinimumHeight() 设置最小高度

  • setMinimumSize() 设置最小尺寸

  • setMaximumHeight() 设置最大高度

  • setMaximumSize() 设置最大尺寸

案例

创建一个窗口, 设置最小尺寸(200,200)和最大尺寸(400,400),测试通过resize是否可以改变

from PyQt5.QtWidgets import QApplication, QMainWindow
import sys

app = QApplication(sys.argv)

window = QMainWindow()
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle('最大最小尺寸')
window.setMinimumSize(200,200) #也可单独限制宽和高
window.setMaximumSize(400,400)
print(window.maximumSize())
window.resize(500,500)  #通过代码也是无法修改的
print(window.maximumSize())

window.show()
sys.exit(app.exec_())

'''
输出:
PyQt5.QtCore.QSize(400, 400)
PyQt5.QtCore.QSize(400, 400)
'''

内容边距

API

  • setContentsMargins(左, 上, 右, 下) 设置内容边距

  • getContentsMargins() 获取内容边距(左, 上, 右, 下),元组

  • contentsRect() 获取内容区域

案例

创建一个窗口, 包含一个标签,标签文本为”Hello World”,标签大小为(100, 60),将文本放在标签的右下角,默认文本显示是水平靠左,垂直居中的

from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)

window = QMainWindow()
window.resize(400, 400)
window.move(300, 300)
window.setWindowTitle('最大最小尺寸')

label = QLabel(window)
label.setText("Hello,world")
label.resize(200,200)
label.setStyleSheet("background-color:cyan;border:1px solid red")
label.setContentsMargins(100,100,0,0)  # 设置内容边距,四个参数是边距,顺时针(左上右下)
print(label.getContentsMargins())      #打印各个边内容边距设置的具体值
print(label.contentsRect())            #打印各个边内容边距设置的具体值

window.show()
sys.exit(app.exec_())

'''
输出:
(100, 100, 0, 0)
PyQt5.QtCore.QRect(100, 100, 100, 100)
'''

事件*

API

显示和关闭事件

  • showEvent(QShowEvent) 控件显示时调用
  • closeEvent(QCloseEvent) 控件关闭时调用

移动事件

  • moveEvent(QMoveEvent) 控件移动时调用

调整大小

  • resizeEvent(QResizeEvent) 控件调整大小时调用

鼠标事件

  • enterEvent(QEvent) 鼠标进入时触发
  • leaveEvent(QEvent) 鼠标离开时触发
  • mousePressEvent(QMouseEvent) 鼠标按下时触发
  • mouseReleaseEvent(QMouseEvent) 鼠标释放时触发
  • mouseDoubleClickEvent(QMouseEvent) 鼠标双击时触发
  • mouseMoveEvent(QMouseEvent)
    • 鼠标按下后移动时触发
    • setMouseTracking(True) 追踪设置后,没有按下的移动也能触发

键盘事件

  • keyPressEvent(QKeyEvent) 键盘按下时调用
  • keyReleaseEvent(QKeyEvent) 键盘释放时调用

焦点事件

  • focusInEvent(QFocusEvent) 获取焦点时调用
  • focusOutEvent(QFocusEvent) 失去焦点时调用

拖拽事件

  • dragEnterEvent(QDragEnterEvent) 拖拽进入控件时调用
  • dragLeaveEvent(QDragLeaveEvent) 拖拽离开控件时调用
  • dragMoveEvent(QDragMoveEvent) 拖拽在控件内移动时调用
  • dropEvent(QDropEvent) 拖拽放下时调用

绘制事件

  • paintEvent(QPaintEvent) 显示控件, 更新控件时调用

改变事件

  • changeEvent(QEvent) 窗体改变, 字体改变时调用

右键菜单

  • contextMenuEvent(QContextMenuEvent) 访问右键菜单时调用

输入法

  • inputMethodEvent(QInputMethodEvent) 输入法调用

API测试

from PyQt5.Qt import *  # 刚开始学习可以这样一下导入
import sys

class Window(QMainWindow):
    def __init__(self):
        super().__init__() # 继承父类的init方法
        self.resize(500, 400)
        self.move(300, 300)
        self.setWindowTitle('事件')
        self.setup_ui()


    def setup_ui(self):
        pass

    def showEvent(self, QShowEvent):
        print("窗口被展示了")
    def closeEvent(self,QCloseEvent):
        print("窗口被关闭了")
    def moveEvent(self,QMoveEvent):
        print("窗口被移动了")
    def resizeEvent(self, QSizeEvent):
        print("窗口被改变了大小")
    def enterEvent(self, QEvent):
        print("鼠标进入")
    def leaveEvent(self, QEvent):
        print("鼠标离开")
    def mousePressEvent(self, QMouseEvent):
        print("鼠标按压")
    def mouseMoveEvent(self, QMouseEvent):
        print("鼠标按下移动")


if __name__ == '__main__':
    app = QApplication(sys.argv) # 创建应用程序对象
    window = Window()
    window.setMouseTracking(True)  # 设置鼠标追踪
    window.show() # 展示控件
    sys.exit(app.exec_())

案列

。。。

鼠标相关*

API

  • setCursor(参数) 设置鼠标形状

鼠标形状

除了以上系统提供参数,还可以自定义QCursor对象

  • unsetCursor() 重置形状

  • cursor() -> QCursor 获取鼠标

    • QCursor对象
      • pixmap() 图片
      • pos() 鼠标位置,相对于整个屏幕的左上角
      • setPos(x, y) 设置鼠标位置

鼠标跟踪

  • hasMouseTracking() 判定是否设置了鼠标跟踪
  • setMouseTracking(bool) 设置鼠标是否跟踪
    • 所谓的鼠标跟踪,其实就是设置检测鼠标移动事件的条件
    • 不跟踪 鼠标移动时,必须处于按下状态,才会触发mouseMoveEvent事件
    • 跟踪 鼠标移动时,不处于按下状态,也会触发mouseMoveEvent事件

API测试

from PyQt5.Qt import *
import sys

app = QApplication(sys.argv)

class MyWindow(QWidget):
     def mouseMoveEvent(self, event): # 点击 QMouseEvent 可以查看里面包含的方法
         # print("鼠标移动了",event.globalPos())  # globalPos() 是整个屏幕为准
         print("鼠标移动了",event.localPos())  # localPos() 是控件本身为准

window = MyWindow()
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle('鼠标')
# window.setCursor(Qt.BusyCursor) #设置忙碌鼠标,转圈圈的样式
pixmap = QPixmap("C:/Users/admin/Pictures/centos.png").scaled(50,50) #设置鼠标样式和大小
cursor = QCursor(pixmap,0,0)  #设置鼠标样式和热点,默认热点为图片中心(-1,-1),热点点击相应位置鼠标点击才会触发,修改为(0,0), 这就是图片的左上角成为热点。
window.setCursor(cursor)
# window.unsetCursor()  # 重置鼠标形状,使上面的设置失效

current_cursor = window.cursor()  #获取鼠标对象
print(current_cursor.pos())  #获取鼠标的位置,它是相对于整个电脑屏幕的
current_cursor.setPos(0,0)  # 这时鼠标的位置在屏幕的左上角

window.setMouseTracking(True)  #设置跟踪
print(window.hasMouseTracking())  #查看鼠标是否处于跟踪状态

window.show()
sys.exit(app.exec_())

案例

。。。

辅助工具

下载地址:https://jwt1399.lanzoui.com/iy5gBrz6api

此工具对 QCursor 鼠标形状 API 进行了全面使用,选择对应单选框,可查看对应鼠标效果,还可自定义鼠标形状。

辅助工具

父子关系

API

  • childAt(x, y) 获取在指定坐标的控件
  • parentWidget() 获取指定控件的父控件
  • childrenRect() 所有子控件组成的边界矩形

案例

创建窗口, 包含若干 Label 控件,点击哪个标签, 就让哪个标签背景变红,使用父控件处理和自定义 QLabel 子类 两种方法实现

方法一:自定义 QLabel 子类

from PyQt5.Qt import *
import sys

class Label(QLabel):
    def mousePressEvent(self, QMouseEvent):
        self.setStyleSheet("background-color:red")
         
#创建app
app = QApplication(sys.argv)

#创建控件
window = QMainWindow()

#设置控件
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle("父子关系")

for i in range(10):
    label = Label(window)
    label.setText("标签"+str(i))
    label.move(30*i,30*i)

#展示控件
window.show()
#进入消息循环
sys.exit(app.exec_())

方法二:使用父控件处理

from PyQt5.Qt import *
import sys

class Window(QWidget):
    def mousePressEvent(self, event):
        local_x = event.x()
        local_y = event.y()
        sub_widget = self.childAt(local_x,local_y) #子控件
        if sub_widget:  # 排除sub_widget 是None
            sub_widget.setStyleSheet("background-color:red;")

#创建app
app = QApplication(sys.argv)

#创建控件
window = Window()

#设置控件
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle("父子关系")

for i in range(10):
    label = QLabel(window)
    label.setText("标签"+str(i))
    label.move(30*i,30*i)


#展示控件
window.show()
#进入消息循环
sys.exit(app.exec_())

层级控制

API

  • lower() 将控件降低到最底层
  • raise_() 将控件提升到最上层
  • a.stackUnder(b) 让a放在b下面

顶层窗口相关

API

​ 图标

  • setWindowIcon(QIcon(“resource/header_icon.png”))

  • windowIcon()

标题

  • setWindowTitle(“社会我顺哥”)

  • windowTitle()

不透明度

  • setWindowOpacity(float)

    • 1.0 不透明
    • 0.0 透明
  • windowOpacity()

​ 窗口状态

  • setWindowState(state)
    • Qt.WindowNoState 无状态
    • Qt.WindowMinimized 最小化
    • Qt.WindowMaximized 最大化
    • Qt.WindowFullScreen 全屏
    • Qt.WindowActive 活动窗口
  • windowState()

最大化最小化

​ 控制

  • showFullScreen() 全屏显示 不包含窗口框架

  • showMaximized() 最大化 包括窗口框架

  • showMinimized() 最小化

  • showNormal() 正常

    判定

  • isMinimized() 是否是最小化窗口

  • isMaximized() 是否是最大化窗口

  • isFullScreen() 是否全屏

窗口标志

  • windowFlags()

  • setWindowFlags(Qt.WindowStaysOnTopHint)

    • 窗口样式
    Qt.Widget 		#默认,一个窗口或控件,包含窗口边框、标题栏(图标、标题、最小化、最大化、关闭)
    Qt.Window 		#一个窗口,包含窗口边框和标题栏(图标、标题、最小化、最大化、关闭)
    Qt.Dialog 		#一个对话框窗口,窗口边框、标题栏(图标、标题、问号、关闭)
    Qt.Sheet 		#一个窗口或部件Macintosh表单
    Qt.Drawer 		#一个窗口或部件Macintosh抽屉
    Qt.Popup 		#一个弹出式顶层窗口
    Qt.Tool 		#一个工具窗口
    Qt.ToolTip 		#一个提示窗口,没有标题栏和窗口边框
    Qt.SplashScreen #一个欢迎窗口,是QSplashScreen构造函数的默认值
    Qt.SubWindow 	#一个子窗口
    
    • 顶层窗口外观标志

    Qt.MSWindowsFixedSizeDialogHint 	#窗口无法调整大小
    Qt.FramelessWindowHint 				#窗口无边框
    Qt.CustomizeWindowHint 				#有边框但无标题栏和按钮,不能移动和拖动
    Qt.WindowTitleHint 					#添加标题栏和一个关闭按钮
    Qt.WindowSystemMenuHint 			#添加系统目录和一个关闭按钮
    Qt.WindowMaximizeButtonHint			#激活最大化和关闭按钮,禁止最小化按钮
    Qt.WindowMinimizeButtonHint			#激活最小化和关闭按钮,禁止最大化按钮
    Qt.WindowMinMaxButtonsHint			#激活最小化,最大化和关闭按钮
    Qt.WindowCloseButtonHint			#添加一个关闭按钮
    Qt.WindowContextHelpButtonHint		#添加问号和关闭按钮,同对话框
    Qt.WindowStaysOnTopHint				#窗口始终处于顶层位置
    Qt.WindowStaysOnBottomHint			#窗口始终处于底层位置
    

通过 setWindowFlags来设置窗口的 WIndowFlags,多个 WindowFlags之间用 | 连接,如:

window = QWidget()
window.setWindowFlags(Qt.Tool | Qt.FramelessWindowHint)

取消某个 WindowFlags:

window.setWindowFlags(window.windowFlags() & ~Qt.FramelessWindowHint)

判断是否设置了某个 WindowFlags:

(widget.windowFlags() | Qt.FramelessWindowHint) == window.windowFlags()

API测试

from PyQt5.Qt import *
import sys

class Window(QWidget): #通过判定窗口的状态实现点击窗口切换最大和正常
    def mousePressEvent(self, QMouseEvent):
        if self.isMaximized():
            self.showNormal()
        else:
            self.showMaximized()

#创建app
app = QApplication(sys.argv)

#创建控件
window = Window()

#设置控件
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle("顶层窗口相关") #设置标题
print(window.windowTitle())  #获取标题

window.setWindowIcon(QIcon("C:/Users/admin/Pictures/jj.ico"))  #设置图标
print(window.windowIcon())  #获取图标

window.setWindowOpacity(0.8)  #设置不透明度
print(window.windowOpacity()) #获取不透明度

print(window.windowState() == Qt.WindowNoState)  # True  说明默认是无状态
window.setWindowState(Qt.WindowMinimized)  #最小化

#展示控件
window.show()
# window.showMaximized()  #展示最大,这时上面的window.show() 也可以不用要
# window.showFullScreen()
# window.showMinimized()
#进入消息循环
sys.exit(app.exec_())

案例

创建一个窗口,要求无边框,无标题,窗口不透明度0.9,自定义最小化,最大化,关闭按钮,支持拖拽用户区移动。

from PyQt5.Qt import *
import sys
class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.resize(500, 400)
        self.move(300, 300)
        self.setWindowTitle("顶层窗口操作案例")
        self.setWindowFlags(Qt.FramelessWindowHint)  # 无边框无标题
        self.setWindowOpacity(0.9)  # 不透明度
        self.setup_ui()

    def setup_ui(self):
        self.btn_width = 40
        self.btn_height = 25
        self.top_margin = 0
        self.add_close_max_min()

    def add_close_max_min(self): # 添加三个按钮
        self.close_btn = QPushButton(self)
        self.close_btn.setText("关闭")
        self.close_btn.resize(self.btn_width,self.btn_height)

        self.max_btn = QPushButton(self)
        self.max_btn.setText("最大")
        self.max_btn.resize(self.btn_width, self.btn_height)

        self.min_btn = QPushButton(self)
        self.min_btn.setText("最小")
        self.min_btn.resize(self.btn_width, self.btn_height)

    def resizeEvent(self, QResizeEvent): #监听窗口大小的变化,计算按钮的位置。
        self.close_btn.move(self.width() - self.close_btn.width(), self.top_margin)
        self.max_btn.move(self.width() - 2 * self.close_btn.width(), self.top_margin)
        self.min_btn.move(self.width() - 3 * self.close_btn.width(), self.top_margin)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton: #按压鼠标左键
            self.move_flags = True  #移动标志
            self.mouse_x = event.globalX()
            self.mouse_y = event.globalY()
            self.window_x = self.x()
            self.window_y = self.y()

    def mouseMoveEvent(self, event):
        if self.move_flags: #当按压鼠标左键,才进行下方移动
            self.move_x = event.globalX() - self.mouse_x
            self.move_y = event.globalY() - self.mouse_y
            self.move(self.window_x + self.move_x, self.window_y + self.move_y)

    def mouseReleaseEvent(self, event):
        self.move_flags = False


#创建app
app = QApplication(sys.argv)

#创建控件
window = Window()
# window = Window(flags=Qt.FramelessWindowHint) #初始化窗口设置Flags


def max_normal_change_slot(): #(最大化/正常)切换槽函数
    if window.isMaximized():
        window.showNormal()
        window.max_btn.setText("最大")
    else:
        window.showMaximized()
        window.max_btn.setText("恢复")

window.close_btn.pressed.connect(lambda: window.close())
window.min_btn.pressed.connect(lambda: window.showMinimized())
window.max_btn.pressed.connect(max_normal_change_slot)

#展示控件
window.show()
#进入消息循环
sys.exit(app.exec_())

辅助工具

下载地址:https://jwt1399.lanzoui.com/i6NwYrz69qd

此工具对窗口标志 API 进行了全面使用,选择对应窗口样式和顶层窗口外观标志,可查看对应窗口显示效果。

辅助工具

Qt官方也有类似的工具:

https://doc.qt.io/qt-5/qtwidgets-widgets-windowflags-example.html

交互状态*

。。。

信息提示

API

状态提示

  • setStatusTip(str) 鼠标停在控件上时, 展示在状态栏
  • statusTip() 获取设置的状态提示信息

工具提示

  • setToolTip(str) 鼠标悬停在控件上一会后, 展示在旁边

  • toolTip() 获取设置的工具提示信息

  • setToolTipDuration(msec) 设置工具提示的时长

  • toolTipDuration() 获取工具提示的时长

这是啥提示

  • setWhatsThis(str) 切换到”查看这是啥”模式, 再点击该控件时显示
  • whatsThis() 获取设置的这是啥提示信息

API测试

from PyQt5.Qt import *
import sys

#创建app
app = QApplication(sys.argv)

#创建控件
window = QMainWindow()  # QMainWindow采用懒加载的方式(当控件用的时候才会加载上去)
window.statusBar()      # 使用状态栏,状态栏显示
window.setWindowFlags(Qt.WindowContextHelpButtonHint)  #换个带问号的窗口样式
label = QLabel(window)

#设置控件
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle("信息提示")
label.setText("标签")

#设置状态提示
window.setStatusTip("这是一个窗口")
print(window.statusTip()) #获取提示信息
#设置工具提示
label.setToolTip("这是一个标签")
print(label.toolTip())
#设置工具提示时长
label.setToolTipDuration(2000)  #设置提示时长为2s
print(label.toolTipDuration())  #获取提示时长
#设置这是啥提示
label.setWhatsThis("这是啥?这是标签")  #点击右上角?号,再点击标签才显示
print(label.whatsThis()) #获取提示信息

#展示控件
window.show()
#进入消息循环
sys.exit(app.exec_())

焦点控制

API

单个控件角度

  • setFocus() 指定控件获取焦点
  • setFocusPolicy(Policy) 设置焦点获取策略
    • Policy
      • Qt.TabFocus 通过Tab键获得焦点
      • Qt.ClickFocus 通过被单击获得焦点
      • Qt.StrongFocus 可通过上面两种方式获得焦点
      • Qt.NoFocus 不能通过上两种方式获得焦点(默认值),setFocus仍可使其获得焦点
  • clearFocus() 取消焦点

父控件角度

  • focusWidget() 获取子控件中当前聚焦的控件
  • focusNextChild() 聚焦下一个子控件
  • focusPreviousChild() 聚焦上一个子控件
  • focusNextPrevChild(bool)
    • True: 下一个
    • False: 上一个
  • setTabOrder(pre_widget , next_widget) 静态方法,设置子控件获取焦点的先后顺序

API测试

from PyQt5.Qt import *
import sys

class Window(QWidget):
    def mousePressEvent(self, event):
        # self.focusNextChild()  #在子控件中切换焦点
        # self.focusPreviousChild()  #反序
        self.focusNextPrevChild(False)  # True 是前面的Next False 是后面的Prev
        # print(self.focusWidget())  # 点击时获取它的子控件中获取焦点的那个


#创建app
app = QApplication(sys.argv)

#创建控件
window = Window()
lineEdit1 = QLineEdit(window)
lineEdit2 = QLineEdit(window)
lineEdit3 = QLineEdit(window)

#设置控件
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle("焦点控制")

lineEdit1.move(100,50)
lineEdit2.move(100,100)
lineEdit3.move(100,150)

lineEdit2.setFocus() #先让第二个获取焦点
lineEdit3.setFocusPolicy(Qt.TabFocus)  #第三个只能Tab键来获得焦点
# lineEdit2.clearFocus() #取消前面获得的焦点

print(window.focusWidget())  # 获取当前窗口的获取焦点的子控件

# tab 切换  2  1  3
Window.setTabOrder(lineEdit2,lineEdit1)
Window.setTabOrder(lineEdit1,lineEdit3)

#展示控件
window.show()

#进入消息循环
sys.exit(app.exec_())

QAbstractButton

所有按钮控件的基类

提示文本

  • setText(str) 设置按钮提示文本
  • text() 获取按钮提示文本

图标相关

  • setIcon(QIcon(“resource/h1.png”)) 设置图标
  • setIconSize(QSize(w, h)) 设置图标大小
  • icon() 获取图标
  • iconSize() 获取图标大小

设置快捷键

  • 方式1: 有提示文本的

    • 如果提示文本包含&符号, 则QAbstractButton会自动创建快捷键 btn.setText(“按钮2&abc”) —> Alt+a
  • 方式2: 没有提示文本的

    • setShortcut(“Alt+G”)

自动重复

  • setAutoRepeat(bool) 设置自动重复
  • setAutoRepeatInterval(毫秒) 设置自动重复检测间隔
  • setAutoRepeatDelay(毫秒) 设置初次检测延迟
  • autoRepeat() 获取是否自动重复
  • autoRepeatInterval() 获取自动重复检测间隔
  • autoRepeatDelay() 获取初次检测延迟
#查看是否自动重复
print(btn.autoRepeat())

#设置自动重复
btn.setAutoRepeat(True)
btn.setAutoRepeatDelay(2000)  #初次检测延迟为2s
btn.setAutoRepeatInterval(1000)  #重复检测间隔为1s
 
print(btn.autoRepeatDelay())   #首次延迟
print(btn.autoRepeatInterval()) #以后的触发间隔

状态

  • isDown() 是否按下按钮
  • setDown(bool) 设置按钮, 是否被按下
  • isChecked() 是否选中了按钮
  • isCheckable() 按钮是否可以被选中
  • setCheckable(bool) 设置按钮, 是否可以被选中
  • setChecked(bool) 设置按钮, 是否被选中
  • toggle() 切换选中与非选中状态
  • 继承于QWidget中的能用状态
    • isEnabled()
    • setEnabled(bool)

排他性

  • autoExclusive() 是否自动排他。一般按钮都是False,只有单选按钮是True

  • setAutoExclusive(bool) 设置自动排他

点击

  • click() 普通点击
  • animateClick(ms) 带动画效果的点击

设置有效区域

  • 重写hitButton(QPoint) 有效返回True,无效返回False

    应用场景:指定用户点击某个区域有效,而不是单一的矩形

案例:

设置只点击按钮中心的圆形区域才会有效

from PyQt5.Qt import *
import sys

class Btn(QPushButton):
    def hitButton(self, point):
        print("点击点坐标:", point)  # 用户点击按钮之后,这个函数会将用户点击的点传出来
        #圆心坐标
        cir_x = self.width()/2
        cir_y = self.height()/2
        #鼠标点击点坐标
        hit_x = point.x()
        hit_y = point.y()
        #点击点到圆心距离 > 半径
        if pow(hit_x-cir_x,2)+pow(hit_y-cir_y,2) > pow(self.width()/2,2):
            return False #代表点击无效,不会触发信号
        return True #True代表用户点击的用效 ,会触发信号的发射
    
############################画内切圆###############################
    def paintEvent(self, event):
        super().paintEvent(event)
        # 画家和纸
        painter = QPainter(self) # 它里面的参数是 QPaintDevice “纸”

        #给画家支笔
        pen = QPen(QColor(100,10,155),4)  #笔的颜色和宽度
        painter.setPen(pen)

        #画家开始画
        painter.drawEllipse(self.rect()) #这就是两个点,四个值
############################画内切圆###############################


#创建app
app = QApplication(sys.argv)

#创建控件
window = QMainWindow()

#设置控件
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle("设置有效区域")

btn = Btn("按钮",window)
btn.resize(200,200)
btn.pressed.connect(lambda :print("按钮被点击"))

#展示控件
window.show()
#进入消息循环
sys.exit(app.exec_())

信号

  • pressed() 鼠标按下信号

  • released() 鼠标释放(1.控件内松开鼠标、2.鼠标移出控件范围后)

  • clicked(checked = false) 控件内按下+控件内释放

  • toggled(bool checked) 切换信号,按钮选中状态发生改变(一般在单选框或者复选框中使用)

from PyQt5.Qt import *
import sys

#创建app
app = QApplication(sys.argv)

#创建控件
window = QMainWindow()

#设置控件
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle("信号")

btn = QPushButton("按钮",window)
btn.move(200,200)

btn.pressed.connect(lambda :print("按钮被鼠标按下了"))
btn.released.connect(lambda :print("按钮被鼠标释放了"))

#clicked 往外传递一个参数,它表示的是当前按钮是否是被选中的状态:
btn.clicked.connect(lambda arg:print("按钮被鼠标点击了",arg))

#toggled也会往外传递个参数,用来反馈按钮是否被选中。(前提是按钮可以被选中,如果按钮本身不能被选中不会触发它。)
btn.toggled.connect(lambda arg:print("按钮选中状态发生改变",arg))
btn.setCheckable(True) #设置按钮可选中,不设的话 toggled 不会触发

#展示控件
window.show()
#进入消息循环
sys.exit(app.exec_())

QPushButton

QAbstractButton 的子类

创建按钮控件

API

  • QPushButton() 创建一个无父控件的按钮控件
  • QPushButton(parent) 创建控件的同时, 设置父控件
  • QPushButton(text, parent) 创建控件的同时, 设置提示文本和父控件
  • QPushButton(icon, text, parent) 创建控件的同时, 设置图标, 提示文本和父控件

API测试

from PyQt5.Qt import *
import sys

#创建app
app = QApplication(sys.argv)

#创建控件
window = QMainWindow()

#设置控件
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle("按钮创建")

btn1 = QPushButton()
btn2 = QPushButton(window)
btn3 = QPushButton("按钮",window)
btn4 = QPushButton(QIcon("image/jj.ico"),"按钮",window)

btn1.move(200,50)
btn2.move(200,100)
btn3.move(200,150)
btn4.move(200,200)

#展示控件
window.show()
#进入消息循环
sys.exit(app.exec_())

快捷键

API

  • QPushButton(“&f”, window)
  • setText(“&F”)
  • setShortcut(“Alt+F”)

应用场景:想通过快捷键触发按钮点击事件的时候设置

菜单

API

  • setMenu(QMenu)设置菜单
  • menu() 获取菜单
  • showMenu() 展示菜单

应用场景:可以设置点击按钮是弹出的菜单, 供用户选择

from PyQt5.Qt import *
import sys

#创建app
app = QApplication(sys.argv)

#创建控件
window = QMainWindow()

#设置控件
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle("菜单")

############################菜单的设置###############################
btn = QPushButton("菜单",window)
menu = QMenu()
# 菜单之行为动作 新建,打开,退出
new_action = QAction(menu)  # 父控件是menu
new_action.setText("新建")
new_action.setIcon(QIcon("image/jj.ico"))
new_action.triggered.connect(lambda: print("新建文件"))

open_action = QAction(menu)  # 父控件是menu
open_action.setText("打开")
open_action.setIcon(QIcon("image/jj.ico"))
open_action.triggered.connect(lambda: print("打开文件"))


menu.addAction(new_action)
menu.addAction(open_action)
# 分割线
menu.addSeparator()

# 菜单之子菜单   最近打开
sub_menu = QMenu(menu)
sub_menu.setTitle("最近打开 ")  # 注意不是setText
sub_menu.setIcon(QIcon("image/jj.ico"))

file_action = QAction("Python_Gui_编程")
sub_menu.addAction(file_action)
menu.addMenu(sub_menu)
btn.setMenu(menu)
############################菜单的设置###############################


###########################################################
# btn.showMenu()  #此时,先展示的是菜单,它可以独自展示的,因为它直接继承的QWidget
###########################################################

#展示控件
window.show()

###########################################################
btn.showMenu()  #此时,先展示的是窗口,然后再展示菜单
###########################################################

#进入消息循环
sys.exit(app.exec_())

边框是否保持扁平

  • setFlat(bool) 默认值为False
  • isFlat() 获取当前按钮边框是否扁平

默认处理*

  • setAutoDefault(bool) 设置为自动默认按钮
  • autoDefault()
  • setDefault(bool)
  • isDefault()

应用场景:主要在对话框中,当我们打开一个对话框之后,可以设置默认处理的按钮。

信号*

​ 都是继承下来的

  • QAbstractButton
    • pressed() 鼠标按下信号
    • released() 鼠标释放
    • clicked(checked = false) 控件内按下+控件内释放
    • toggled(bool checked) 切换信号(一般在单选框或者复选框中使用)
  • QWidget
    • windowTitleChanged(QString) 窗口标题改变信号
    • windowIconChanged(QIcon) 窗口图标改变信号
    • customContextMenuRequested(QPoint) 自定义上下文菜单请求信号*

QMenu*

  • QMenu() 创建菜单

  • addMenu(QMenu) 添加子菜单

  • addSeparator() 添加分割线

  • addAction() 添加行为动作

  • triggered QAction信号

QCommandLinkButton

命令链接是Windows Vista引入的新控件,命令链接按钮不应单独使用,而应作为向导和对话框中单选按钮的替代选项

QPushButton 的子类

创建

  • QCommandLinkButton(parent)
  • QCommandLinkButton(text, parent)
  • QCommandLinkButton(text, description ,parent)

描述

  • setDescription(str) 设置命令链接按钮的描述文本
  • description()
from PyQt5.Qt import *
import sys

#创建app
app = QApplication(sys.argv)

#创建控件
window = QMainWindow()

#设置控件
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle("QCommandLinkButton")

clbtn = QCommandLinkButton("标题","描述",window)
clbtn.resize(100,80)
# clbtn.setText("Python")
# clbtn.setDescription("hello world")
# clbtn.setIcon(QIcon("icon.ico"))

#展示控件
window.show()
#进入消息循环
sys.exit(app.exec_())

image-20210809172240677

QToolButton

通常是在工具栏内部使用,工具按钮通常不显示文本标签,而是显示图标

QAbstractButton 的子类

创建

  • QToolButton(parent: QWidget = None)

(继承)设置文本,图标,工具提示

  • setText(str)
  • setIcon(QIcon)
  • setIconSize(QSize)
  • setToolTip(str)
  • 注:如果文本和图标同时设置, 则默认只展示图标
from PyQt5.Qt import *
import sys

#创建app
app = QApplication(sys.argv)

#创建控件
window = QMainWindow()

#设置控件
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle("QToolButton ")

tool_btn = QToolButton(window)
tool_btn.setText("工具按钮")

###########################当有图标显示时候,文本就不会显示了###############################
#它和QPushButton 的不同,就是在于它一般只显示图标,不显示文本
tool_btn.setIcon(QIcon("image/jj.ico"))
tool_btn.setIconSize(QSize(20,20))
############################当有图标显示时候,文本就不会显示了###############################
tool_btn.setToolTip("这是一个新建按钮")


#展示控件
window.show()
#进入消息循环
sys.exit(app.exec_())

按钮样式风格

  • setToolButtonStyle(Qt.ToolButtonStyle)
    • Qt.ToolButtonIconOnly 仅显示图标 0
    • Qt.ToolButtonTextOnly 仅显示文字 1
    • Qt.ToolButtonTextBesideIcon 文本显示在图标旁边 2
    • Qt.ToolButtonTextUnderIcon 文本显示在图标下方 3
    • Qt.ToolButtonFollowStyle 遵循风格 4
  • toolButtonStyle()
tool_btn.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
print(tool_btn.toolButtonStyle()) # 输出:3

设置箭头

  • setArrowType(Qt.ArrowType)
    • Qt.NoArrow 无箭头 0
    • Qt.UpArrow 向上箭头 1
    • Qt.DownArrow 向下箭头 2
    • Qt.LeftArrow 向左箭头 3
    • Qt.RightArrow 向右箭头 4
  • arrowType()
tool_btn.setArrowType(Qt.RightArrow) #注意,如果箭头和图标同时存在的话,箭头的优先级高
print(tool_btn.arrowType()) # 输出:4

自动提升

  • setAutoRaise(bool)
  • autoRaise()
  • 在自动提升模式下,该按钮仅在鼠标指向时才会绘制3D帧
tool_btn = QToolButton(window)
tool_btn.setIcon(QIcon("image/jj.ico"))
tool_btn.setIconSize(QSize(20,20))
tool_btn.setAutoRaise(True) #当鼠标放上去的时候,会有自动提升的效果

btn = QPushButton(window)
btn.setIcon(QIcon("image/jj.ico"))
btn.setIconSize(QSize(20,20))
btn.move(100,100)
btn.setFlat(True)#当鼠标放上去的时候,也不会提升上来。

菜单

  • setMenu(QMenu)
  • menu()
from PyQt5.Qt import *
import sys

#创建app
app = QApplication(sys.argv)

#创建控件
window = QMainWindow()

tool_btn = QToolButton(window)
tool_btn.setText("工具按钮")
tool_btn.setIcon(QIcon("image/jj.ico"))
tool_btn.setIconSize(QSize(20,20))

#设置控件
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle("QToolButton")

############################按钮设置菜单###############################
menu = QMenu(tool_btn)

new_action = QAction("新建")
menu.addAction(new_action)
open_action = QAction("打开")
menu.addAction(open_action)

menu.addSeparator()

sub_menu = QMenu(menu)
sub_menu.setTitle("子菜单")
relative_action  = QAction("最近打开")
sub_menu.addAction(relative_action)
menu.addMenu(sub_menu)


tool_btn.setMenu(menu)
############################设置菜单###############################


#展示控件
window.show()
#进入消息循环
sys.exit(app.exec_())

但是 ,却不能点击显示菜单效果,这时因为有个东西要设置的。这里涉及到下面的菜单弹出模式

它的默认弹出模式是,按住一会才会弹出菜单。

菜单弹出模式

  • setPopupMode(QToolButton.ToolButtonPopupMode)
    • QToolButton.DelayedPopup 鼠标按住一会才显示 类似于浏览器后退按钮 0
    • QToolButton.MenuButtonPopup 有一个专门的指示箭头 点击箭头才显示 1
    • QToolButton.InstantPopup 点了按钮就显示 点击信号不会发射 2
  • popupMode()
tool_btn.setPopupMode(QToolButton.InstantPopup)
print(tool_btn.popupMode()) #输出:2

信号

  • triggered(QAction *action) 当点击某个action时触发, 并会将action传递出来

  • QAction对象可以通过 setData(Any) 绑定数据 data() 获取数据

from PyQt5.Qt import *
import sys

#创建app
app = QApplication(sys.argv)

#创建控件
window = QMainWindow()

tool_btn = QToolButton(window)
tool_btn.setText("工具按钮")
tool_btn.setIcon(QIcon("image/jj.ico"))
tool_btn.setIconSize(QSize(20,20))

#设置控件
window.resize(500, 400)
window.move(300, 300)
window.setWindowTitle("QToolButton")

############################按钮设置菜单###############################
menu = QMenu(tool_btn)

new_action = QAction("新建")
menu.addAction(new_action)
new_action.setData("new")  #绑定数据

open_action = QAction("打开")
menu.addAction(open_action)
open_action.setData("open") #绑定数据

menu.addSeparator()

sub_menu = QMenu(menu)
sub_menu.setTitle("子菜单")
relative_action  = QAction("最近打开")
sub_menu.addAction(relative_action)
menu.addMenu(sub_menu)

tool_btn.setMenu(menu)
tool_btn.setPopupMode(QToolButton.InstantPopup)
############################设置菜单###############################

def tool_btn_triggered_slot(action):
    if action.data()  == "new":
        print("你点击的是新建",action)
    elif action.data() == "open":
        print("你点击的是打开",action)
        
tool_btn.triggered.connect(tool_btn_triggered_slot)

#展示控件
window.show()
#进入消息循环
sys.exit(app.exec_())

QRadioButton

一般用于给用户提供若干选项中的单选操作

QAbstractButton 的子类

创建

  • QRadioButton(parent)
  • QRadioButton(text, parent)
radio_button_1 = QRadioButton("男",window)
radio_button_1.move(100,100)
radio_button_1.setIcon(QIcon("icon.ico"))
radio_button_1.setChecked(True)  # 它是默认选中

radio_button_2 = QRadioButton("女",window)
radio_button_2.move(100,200)
radio_button_2.setIcon(QIcon("icon.ico"))

信号

  • toggled(bool)
radio_button_2.toggled.connect(lambda :print("状态切换"))

QButtonGroup

提供 一个抽象的按钮容器, 可以将多个按钮划分为一组

QObject 的子类

创建

  • QButtonGroup(parent)

添加按钮

  • addButton(QAbstractButton, id = -1)
  • 如果id为-1,则将为该按钮分配一个id。自动分配的ID保证为负数,从-2开始。
  • 如果要分配自己的ID,请使用正值以避免冲突

查看按钮

  • buttons() 查看所有按钮组中的按钮
  • button(ID) 根据ID获取对应按钮, 没有则返回None
  • checkedButton() 获取选中的那个按钮

移除按钮

  • removeButton(QAbstractButton)
  • 它并不是从界面上删除这个按钮,而只是将其移出抽象的按钮组。
from PyQt5.Qt import * #刚开始学习可以这样一下导入
import sys
#1,创建app
app  = QApplication(sys.argv)


#2,控件的操作:
#创建控件
window = QWidget()

radio_button_1 = QRadioButton("男-Male",window)
radio_button_1.move(100,100)
radio_button_2 = QRadioButton("女-Famale",window)
radio_button_2.move(100,200)

radio_button_yes = QRadioButton("yes",window)
radio_button_yes.move(300,100)
radio_button_no = QRadioButton("yes",window)
radio_button_no.move(300,200)

#设置控件
window.setWindowTitle("QRadioButton")
window.resize(500,500)

###########################################################
sex_group = QButtonGroup(window)
sex_group.addButton(radio_button_1)
sex_group.addButton(radio_button_2)
###########################################################

###########################################################
judge_group = QButtonGroup(window)
judge_group.addButton(radio_button_yes,1) # 设置id=1
judge_group.addButton(radio_button_no,2)  # 设置id=2
###########################################################

print(sex_group.buttons())
print(judge_group.button(1))
print(judge_group.checkedButton())
sex_group.removeButton(radio_button_2)
#展示控件
window.show()
#3,进入消息循环
sys.exit(app.exec_())

绑定和获取ID

  • setId(QAbstractButton,int)
  • id(QAbstractButton) 指定按钮对应的ID,如果不存在此按钮,则返回-1
  • checkedId() 获取选中的ID,如果没有选中按钮则返回-1
#****************************绑定id *******************************
sex_group.setId(radio_button_1,1)
sex_group.setId(radio_button_2,2)
#****************************绑定id *******************************

#****************************获取id *******************************
print(sex_group.id(radio_button_1))
print(sex_group.id(radio_button_2))
#****************************获取id *******************************

#****************************查看当前选中的按钮的id*******************
print(sex_group.checkedId())
#****************************查看当前选中的按钮的id*******************

独占设置

  • setExclusive(bool)

  • exclusive()

  • 应用场景:统一设置按钮组中的按钮是否是独占(选择互斥)

#****************************将一个组的独占设置为否定*******************************
sex_group.setExclusive(False)
#****************************将一个组的独占设置为否定*******************************

信号

  • buttonClicked(int/QAbstractButton) 当按钮组中的按钮被点击时, 发射此信号

  • buttonPressed(int/QAbstractButton) 当按钮组中的按钮被按下时, 发射此信号

  • buttonReleased(int/QAbstractButton) 当按钮组中的按钮被释放时, 发射此信号

  • buttonToggled(QAbstractButton/int, bool) 当按钮组中的按钮被切换状态时, 发射此信号

  • QButtonGroup 信号会传递两种类型的值,一个是具体哪个按钮,一个是它的 id。发出信号时向外传出的int 指的是之前设置的 id 。

信号传出多个参数

  • 如果信号名称一样,但参数不一样,外界在使用信号时, 可以使用如下格式进行选择
  • signal_name[type] 信号名称[参数类型]
from PyQt5.Qt import * #刚开始学习可以这样一下导入
import sys
#1,创建app
app  = QApplication(sys.argv)


#2,控件的操作:
#创建控件
window = QWidget()

radio_button_1 = QRadioButton("男-Male",window)
radio_button_1.move(100,100)
radio_button_2 = QRadioButton("女-Famale",window)
radio_button_2.move(100,200)

radio_button_yes = QRadioButton("yes",window)
radio_button_yes.move(300,100)
radio_button_no = QRadioButton("yes",window)
radio_button_no.move(300,200)

#设置控件
window.setWindowTitle("QRadioButton")
window.resize(500,500)


###########################################################
sex_group = QButtonGroup(window)
sex_group.addButton(radio_button_1)
sex_group.addButton(radio_button_2)
###########################################################


###########################################################
judge_group = QButtonGroup(window)
judge_group.addButton(radio_button_yes,1) # 设置id=1
judge_group.addButton(radio_button_no,2)  # 设置id=2
###########################################################

#****************************绑定id *******************************
sex_group.setId(radio_button_1,1)
sex_group.setId(radio_button_2,2)
#****************************绑定id *******************************


sex_group.buttonClicked.connect(lambda val:print(val)) #向外传出的是具体的按钮
sex_group.buttonClicked[int].connect(lambda val:print(val)) #向外传出的是按钮的id

sex_group.buttonClicked.connect(lambda val:print(val,sex_group.id(val))) # 当然获取了具体的按钮之后,自然很简单就可以获得它的id

#展示控件
window.show()

#3,进入消息循环
sys.exit(app.exec_())

QCheckBox

一般用于给用户提供若干选项中的多选操作

QAbstractButton 的子类

创建

  • QCheckBox(parent)
  • QCheckBox(text, parent)
checkbox1 = QCheckBox(window)
checkbox1.setText("Python")
checkbox1.move(50,30)

checkbox2 = QCheckBox(window)
checkbox2.setText("C++")
checkbox2.move(50,60)

checkbox3 = QCheckBox(window)
checkbox3.setText("C")
checkbox3.move(50,90)

设置是否三态

  • setTristate(bool=True)
  • isTristate()
  • 它默认是展示两种状态,可以通过设置来展示三种状态(选中、半选中、没选中)。
checkbox1.setTristate(True)
print(checkbox1.isTristate())

设置复选框状态

  • setCheckState(Qt.CheckState)

    • Qt.Unchecked 该项目未选中 0
    • Qt.PartiallyChecked 部分选中 1
    • Qt.Checked 真的被选中 2
  • checkState()

checkbox2.setCheckState(Qt.PartiallyChecked)  # 半选中状态
# checkbox2.setCheckState(Qt.Checked)  #选中
# checkbox2.setCheckState(Qt.Unchecked) #未选中

信号

  • stateChanged(int state) 选中或清除选中时, 发射此信号
  • 其他信号继承父类

它之所以多了个信号,是因为它有的时候是三态,所以之前的toggled 就不支持三态了。

checkbox1.setTristate(True)
checkbox1.stateChanged.connect(lambda state:print(state)) # 0,1,2

checkbox3.toggled.connect(lambda bool:print(bool)) # True,False

QLineEdit

67-91

79-85 ???

QFrame

92-93

QAbstractScrollArea

94

QTextEdit

95-130

QPlainTextEdit

131-140

QKeySequenceEdit

141-143

QAbstractSpinBox

144-151

QSpinBox

152-160

布局管理器

261-295

QSS

296-327

QTDesigner

Qt Designer 通过拖拽的方式放置控件可以随时查看控件效果,设计符合MVC的架构,实现了视图和逻辑的分离,从而实现了开发的便捷

配置QTDesigner

为了不每次都在外部打开 QTDesigner,可以在 PyCharm 中配置快捷按钮,只需点击即可打开QTDesigner

操作步骤:点击设置–>工具–>外部工具–>点击“+”–>进行如下配置

Name:QTDesigner
Program:D:\Qt\Qt5.14.2\5.14.2\mingw73_32\bin\designer.exe   # QTDesigner的位置
Working directory:$ProjectFileDir$ #当前项目文件目录

配置完成后,在 Pycharm 的菜单栏 Tools 或者右击——>External Tools——>QTDesigner,可以看到刚才配置菜单 QTDesigner,点击即可打开 QTDesigner

加载UI文件

动态加载UI文件

from PyQt5.QtWidgets import QApplication
from PyQt5 import uic
import sys

# 从文件中加载UI定义
app = QApplication(sys.argv)
ui = uic.loadUi("test.ui")
ui.show()
sys.exit(app.exec_())

转UI文件为Py文件进行加载

1.执行如下的命令把 UI 文件直接转化为包含界面定义的Py文件

python -m PyQt5.uic.pyuic test.ui -o test.py -x

# -o 输出文件
# -x 生成if __name__ == "__main__":测试代码

2.然后在你的代码文件中这样使用定义界面的类

from PyQt5.QtWidgets import QApplication, QMainWindow
import sys
import test  # ui文件转为py文件的文件名

app = QApplication(sys.argv)
window = QMainWindow()
ui = test.Ui_MainWindow()  # py文件的类名
ui.setupUi(window)
window.show()
sys.exit(app.exec_())

配置PyUIC

为了不每次都执行 ui 转 py 代码,可以在 PyCharm 中配置快捷按钮,只需点击即可进行 ui 文件到 py 文件的转化。

操作步骤:点击设置–>工具–>外部工具–>点击“+”–>进行如下配置

Name:PyUIC
Program:D:\Python37\python.exe   #python安装路径,要确保已经安装PyQt5
Arguments:-m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$_ui.py -x #把ui文件转成同名py文件
Working directory:$FileDir$ #当前文件目录

配置完成后,在 Pycharm 的菜单栏 Tools 或者右击需要转换的ui文件——>External Tools——>PyUIC,点击 PyUIC 即可进行转换

配置PyQrc

可以在 PyCharm 中配置快捷按钮,只需点击即可进行 qrc 文件到 py 文件的转化。

操作步骤:点击设置–>工具–>外部工具–>点击“+”–>进行如下配置

Name:PyQrc
Program:D:\Python37\Scripts\pyrcc5.exe   #pyrcc5路径,要确保已经安装PyQt5
Arguments:$FileName$ -o $FileNameWithoutExtension$_rc.py #把qrc文件转成同名py文件
Working directory:$FileDir$ #当前文件目录

配置完成后,在 Pycharm 的菜单栏 Tools 或者右击需要转换的qrc文件——>External Tools——>PyQrc,点击 PyQrc 即可进行转换

自定义信号

如果系统提供的信号不能满足我们的需求,这时就需要我们自定义信号

例如鼠标单击往往指的是左击,而不是右击,如果想要拥有右击信号,就需要自己自定义信号了!

信号的定义

在类的内部, 以类属性的形式定义

  • pyqtSignal(类型1, 类型2…)
  • 重载版本 pyqtSignal([int],[str])
class Btn(QPushButton):
    doubleClick = pyqtSignal()
    doubleClick2 = pyqtSignal([int], [str])

QTDeasigner中使用自定义信号要将控件提升为自定义的类(Btn)

信号的发射

  • 信号.emit(参数1, 参数2…)
  • 注意 重载的信号选择问题 信号[类型]
from PyQt5.Qt import * #刚开始学习可以这样一下导入
import sys

class Btn(QPushButton):
    right_clicked = pyqtSignal([str],[int],[int,str])  #中括号的意思是重载

    def mousePressEvent(self,event):
        super().mousePressEvent(event)
        if event.button() == Qt.RightButton:  #右击时发射信号
            self.right_clicked.emit(self.text())   #发射参数是str的信号
            self.right_clicked[int,str].emit(100,self.text())   #发射参数是str和int的信号
            self.right_clicked[int].emit(100)   #发射参数是int的信号

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("信号的学习")
        self.resize(400,400)
        self.set_ui()
    def set_ui(self):
        btn = Btn("我是按钮",self)
        btn.right_clicked.connect(lambda arg:print("右键被点击了",arg))
        # btn.right_clicked[int,str].connect(lambda v1,arg :print("右键被点击了",v1,arg))
        # btn.right_clicked[int].connect(lambda val:print(val))

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

装饰器自动连接信号与槽

  • QtCore.QMetaObject.connectSlotsByName(obj) 将obj内部的子孙对象的信号, 按照其objectName连接到相关的槽函数
  • 一定要等到self中的对象创建完成,因此语句放在最后面,PyUIC会自动生成

使用规则:

@pyqtSlot()
def on_objectName_信号名(self):  # 名字是 on_$ObjectName$_$Singnal$
    pass

示例:

from PyQt5 import QtCore
from PyQt5.Qt import *  
import sys


class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("装饰器连接信号和槽的学习")
        self.resize(400, 400)
        self.set_ui()

    def set_ui(self):
        btn = QPushButton("测试按钮", self)
        btn.resize(200, 200)
        btn.move(100, 100)
        btn.setObjectName("btn")
        QtCore.QMetaObject.connectSlotsByName(self)  # 它一定要等到self中的对象创建完成

    @pyqtSlot()
    def on_btn_clicked(self):  # 名字是 on_$ObjectName$_$Signal$
        print("按钮被点击了")
        

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

动画效果

动画类继承结构图

QAbstractAnimation

所有动画共享的功能,继承此类, 实现一些自定义动画

循环操作

  • setLoopCount(int loopCount) 设置动画循环次数
  • loopCount() -> int 获取循环次数
  • currentLoop() -> int 动画的当前循环次数,从0开始
  • currentLoopTime() -> int 当前循环次数内的时间

时间操作

  • duration() -> int 单次时长
  • totalDuration() -> int 动画总时长
  • currentTime() -> int 当前动画运行时长
from PyQt5.Qt import *  # 刚开始学习可以这样一下导入
import sys


class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("动画的学习")
        self.resize(400, 400)
        self.set_ui()

    def set_ui(self):
        btn = QPushButton("按钮", self)
        btn.resize(200, 200)
        btn.move(100, 100)
        btn.setStyleSheet("background-color:cyan;")

        # 1, 创建一个动画对象 ,并且设置目标属性
        animation = QPropertyAnimation(btn, b"pos", self)

        # 2,设置属性值  包括 开始值 (插值) 结束值
        animation.setStartValue(QPoint(0, 0))
        animation.setKeyValueAt(0.5, QPoint(0, 200))  # 在动画时长的中间要插值
        animation.setEndValue(QPoint(200, 200))

        # 3,动画时长
        animation.setDuration(2000)  # 2s

        # 4,启动动画
        animation.start()
        animation.setLoopCount(3)  # 循环三遍

        #时间操作
        print("循环次数:", animation.loopCount())
        print("单次时长:", animation.duration(), "总时长:", animation.totalDuration())
        btn.clicked.connect(lambda: print("当前循环次数:", animation.currentLoop(),"当前循环次内的时长:", animation.currentLoopTime(), "当前动画运行时长:", animation.currentTime()))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
    """
    循环次数: 3
    单次时长: 2000 总时长: 6000
    当前循环次数: 0 当前循环次内的时长: 1456 当前动画运行时长: 1456
    当前循环次数: 1 当前循环次内的时长: 1296 当前动画运行时长: 3296
    当前循环次数: 2 当前循环次内的时长: 1328 当前动画运行时长: 5328
    """

动画方向

  • setDirection(QAbstractAnimation.Direction)
    • 参数(QAbstractAnimation.Direction):
    • QAbstractAnimation.Forward 0 动画的当前时间随着时间而增加(即,从0移动到结束/持续时间)
    • QAbstractAnimation.Backward 1 动画的当前时间随着时间而减少(即,从结束/持续时间向0移动)
  • direction() -> QAbstractAnimation.Direction
# 动画方向设置一定要在启动动画之前
animation.setDirection(QAbstractAnimation.Backward)
print("动画方向:",animation.direction())

动画状态

  • state() -> QAbstractAnimation.State
    • 参数(QAbstractAnimation.State):
    • QAbstractAnimation.Stopped 0 停止
    • QAbstractAnimation.Paused 1 暂停
    • QAbstractAnimation.Running 2 运行

案例:当用户点击按钮的时候,动画停止,再次点击时动画继续。

from PyQt5.Qt import * #刚开始学习可以这样一下导入
import sys

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("动画的学习")
        self.resize(400,400)
        self.set_ui()


    def set_ui(self):
        btn = QPushButton("按钮",self)
        btn.resize(200,200)
        btn.move(100,100)
        btn.setStyleSheet("background-color:cyan;")

        #1, 创建一个动画对象 ,并且设置目标属性
        animation = QPropertyAnimation(btn,b"pos",self)

        #2,设置属性值  包括 开始值 (插值) 结束值
        animation.setStartValue(QPoint(0,0))
        animation.setKeyValueAt(0.5,QPoint(0,200))  #在动画时长的中间要插值
        animation.setEndValue(QPoint(200,200))

        #3,动画时长
        animation.setDuration(2000)   #2s

        #这里通过动画曲线,改变动画节奏:
        animation.setEasingCurve(QEasingCurve.InOutBounce)

        animation.setLoopCount(3)  # 循环三遍


        #动画方向设置一定要在 启动动画之前
        animation.setDirection(QAbstractAnimation.Backward)


        #4,启动动画
        animation.start()


        def btn_clicked_slot():
            if animation.state() == QAbstractAnimation.Running:
                animation.pause()
            elif animation.state() == QAbstractAnimation.Paused:
                animation.resume()

        btn.clicked.connect(btn_clicked_slot)


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

常用操作

  • start(QAbstractAnimation.DeletionPolicy)

    • 参数(QAbstractAnimation.DeletionPolicy):
    • QAbstractAnimation.KeepWhenStopped 停止时不会删除动画。
    • QAbstractAnimation.DeleteWhenStopped 停止时动画将自动删除。
  • pause() 动画暂停

  • stop() 动画停止

    • stop() 和pause() 的区别:它们都可以停止运行。stop() 是不可恢复的,pause() 是可以恢复的
  • resume() 动画恢复

  • setCurrentTime(int) 设置当前时间

  • setPause(bool) 设置是否暂停

常用信号

  • currentLoopChanged(int currentLoop) 循环改变触发
  • directionChanged(QAbstractAnimation.Direction newDirection) 动画方向改变触发
  • finished() 动画完成时触发
  • stateChanged(QAbstractAnimation.State newState, QAbstractAnimation.State oldState) 动画状态改变触发(返回两个参数,新状态和老状态)

QVariantAnimation

设置时长

  • setDuration(int msecs)

开始和结束值

  • setStartValue(QVariant value)
  • startValue() -> QVariant
  • setEndValue(QVariant value)
  • endValue() -> QVariant

关键值

  • setKeyValueAt(double step, QVariant value)
  • keyValueAt(double step) -> QVariant
  • setKeyValues(QVariantAnimation.KeyValues keyValues)
  • keyValues() -> QVariantAnimation.KeyValues

动画曲线

QPropertyAnimation

用于实现某个属性值从x到y的动画变化

构造动画对象并设置目标属性

方式1

  • QPropertyAnimation(parent: QObject = None)
  • setTargetObject(self, QObject)
  • targetObject(self)
  • setPropertyName(self, Union[QByteArray, bytes, bytearray])
  • propertyName(self) -> QByteArray
animation = QPropertyAnimation(self)
animation.setTargetObject(btn)  #对 btn 做动画
animation.setPropertyName(b"pos")  #对btn 的 pos 属性做动画

方式2

  • QPropertyAnimation(QObject, Union[QByteArray, bytes, bytearray], parent: QObject = None)
animation = QPropertyAnimation(btn, b"pos", self)

常用属性

  • geometry 位置和尺寸
  • pos 位置
  • size 尺寸
  • windowOpacity 透明度
#对位置做动画
animation = QPropertyAnimation(btn,b"pos",self)
animation.setStartValue(QPoint(0,0))
animation.setEndValue(QPoint(300,300))

#对尺寸做动画
animation = QPropertyAnimation(btn,b"size",self)
animation.setStartValue(QSize(0,0))
animation.setEndValue(QSize(300,300))

#对位置和尺寸同时做动画
animation = QPropertyAnimation(btn,b"geometry",self)
animation.setStartValue(QRect(0,0,100,100))
animation.setEndValue(QRect(200,200,300,300))

#对透明度做动画
animation = QPropertyAnimation(self,b"windowOpacity",self)
animation.setStartValue(1)
animation.setEndValue(0.5)

设置开始值和结束值

  • setStartValue(self, Any) 开始
  • setEndValue(self, Any) 结束
  • setKeyValueAt(self, float, Any) 开始和结束之间
  • setKeyValues(self, object)
animation.setStartValue(QSize(0,0))
animation.setKeyValueAt(0.5,QPoint(0,200))  #在动画时长的中间要插值
animation.setEndValue(QSize(200,200))

设置动画时长

  • setDuration(int mesc)
animation.setDuration(2000)   #2s

设置动画曲线

#这里通过动画曲线,改变动画节奏:
animation.setEasingCurve(QEasingCurve.InOutBounce)

启动动画

  • start(QAbstractAnimation.DeletionPolicy)
    • 参数(QAbstractAnimation.DeletionPolicy):
    • QAbstractAnimation.KeepWhenStopped 停止时不会删除动画。
    • QAbstractAnimation.DeleteWhenStopped 停止时动画将自动删除。
#启动动画
animation.start()

完整动画示例

from PyQt5.Qt import * #刚开始学习可以这样一下导入
import sys

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("动画的学习")
        self.resize(400,400)
        self.set_ui()


    def set_ui(self):
        btn = QPushButton("按钮",self)
        btn.resize(200,200)
        btn.move(100,100)
        btn.setStyleSheet("background-color:cyan;")

        #1, 创建一个动画对象 ,并且设置目标属性
        animation = QPropertyAnimation(btn,b"pos",self)  #可一起写,对象,属性
        # animation.setTargetObject(btn)  #对 btn 做动画
        # animation.setPropertyName(b"pos")  #对btn 的 pos 属性做动画


        #2,设置属性值  包括 开始值 (插值) 结束值
        animation.setStartValue(QPoint(0,0))
        animation.setKeyValueAt(0.5,QPoint(0,200))  #在动画时长的中间要插值
        animation.setEndValue(QPoint(200,200))

        #3,动画时长
        animation.setDuration(2000)   #2s

        #这里通过动画曲线,改变动画节奏:
        animation.setEasingCurve(QEasingCurve.InOutBounce)


        #4,启动动画
        animation.start()

if __name__ == '__main__':
    app =QApplication(sys.argv)

    window = Window()
    window.show()

    sys.exit(app.exec_())

QPauseAnimation

暂停动画, 在串行动画中使用

  • setDuration(int msecs) 设置暂停时长

动画组

QAnimationGroup

可以将一组动画, 同时播放或者按顺序播放

添加动画

  • addAnimation(QAbstractAnimation animation)
  • insertAnimation(int index, QAbstractAnimation animation)

移除动画

  • removeAnimation(QAbstractAnimation animation)

获取动画

  • animationAt(int index) -> QAbstractAnimation

获取并移除

  • takeAnimation(int index) -> QAbstractAnimation

动画个数

  • animationCount() -> int

清空动画

  • clear()

QParallelAnimationGroup

并行动画,多个动画同时执行

  • 功能参照父类
  • 只是添加的所有动画, 都是同时执行

QSequentialAnimationGroup

串行动画,多个动画先后执行

  • 功能参照父类

  • 只是添加的所有动画, 都是串行顺序执行

  • addPause(int msecs) -> QPauseAnimation 设置第一个动画执行完后暂停时间

  • insertPause(int index, int msecs) -> QPauseAnimation

  • currentAnimation() -> QAbstractAnimation

  • 信号:currentAnimationChanged(QAbstractAnimation current)

from PyQt5.Qt import * #刚开始学习可以这样一下导入
import sys

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("动画组的学习")
        self.resize(800,800)
        self.set_ui()

    def set_ui(self):
        red_btn = QPushButton("红色按钮",self)
        green_btn = QPushButton("绿色按钮",self)
        red_btn.resize(100,100)
        green_btn.resize(100,100)
        green_btn.move(150,150)
        red_btn.setStyleSheet("background-color:red;")
        green_btn.setStyleSheet("background-color:green;")

        #动画设置
        animation_green = QPropertyAnimation(green_btn,b"pos",self)
        animation_green.setKeyValueAt(0,QPoint(150,150))
        animation_green.setKeyValueAt(0.25,QPoint(550,150))
        animation_green.setKeyValueAt(0.5,QPoint(550,550))
        animation_green.setKeyValueAt(0.75,QPoint(150,550))
        animation_green.setKeyValueAt(1,QPoint(150,150))
        animation_green.setDuration(2000)
        #animation_green.start()  # 动画不是阻塞的,动画运行时会继续往下运行
        ###########################################################
        animation_red = QPropertyAnimation(red_btn,b"pos",self)
        animation_red.setKeyValueAt(0,QPoint(0,0))
        animation_red.setKeyValueAt(0.25,QPoint(0,700))
        animation_red.setKeyValueAt(0.5,QPoint(700,700))
        animation_red.setKeyValueAt(0.75,QPoint(700,0))
        animation_red.setKeyValueAt(1,QPoint(0,0))
        animation_red.setDuration(2000)
        #animation_red.start()

        #用串行动画组来管理上面两个动画
        animation_group  = QSequentialAnimationGroup(self)
        animation_group.addAnimation(animation_red)
        animation_group.addAnimation(animation_green)
        animation_group.start()
        
        #用并行动画组来管理上面两个动画
        # animation_group  = QParallelAnimationGroup(self)
        # animation_group.addAnimation(animation_red)
        # animation_group.addAnimation(animation_green)
        # animation_group.start()
        

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

案例:第一个动画执行执行完 5s 之后再执行第二个

方法一:使用addPause

#用动画组来管理上面两个动画
animation_group  = QSequentialAnimationGroup(self)
animation_group.addAnimation(animation_red)
#############################设置暂停时长##############################
animation_group.addPause(5000)  # 只有串行中才会有这个设置  暂停5s
#############################设置暂停时长##############################
animation_group.addAnimation(animation_green)
animation_group.start()

方法二:使用QPauseAnimation

#用动画组来管理上面两个动画
animation_group  = QSequentialAnimationGroup(self)
animation_group.addAnimation(animation_red)
#############################设置暂停时长##############################
pause_animation = QPauseAnimation()
pause_animation.setDuration(5000)
animation_group.addAnimation(pause_animation)
#############################设置暂停时长##############################
animation_group.addAnimation(animation_green)
animation_group.start()

综合案例

358

注:标题后含 “*” 号,说明该内容还未写完,eg:“事件*

开源项目收集

赞助💰

如果你觉得对你有帮助,你可以请我喝一杯冰可乐!嘻嘻🤭

支付宝支付 微信支付

文章作者: 简简
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 简简 !
评论
填上邮箱会收到评论回复提醒哦!!!
 上一篇
LeetCode 算法入门 LeetCode 算法入门
在数学和计算机科学之中,算法是一个被定义好的、计算机可施行之指示的有限步骤或次序,常用于计算、数据处理和自动推理。作为一个有效方法,算法被用于计算函数,它包含了一系列定义清晰的指令,并可于有限的时间及空间内清楚的表述出来。
2021-07-26
下一篇 
Cryptography Review Cryptography Review
初次接触密码学是在大二的<<现代密码学>>课堂上,彼时仅对理论基础进行了学习,后来参加CTF比赛,为了解答其中 Crypto 模块,将课堂学到的知识运用了起来,但总的来说研究并不深,加之近段时间没有再碰过了,故此有了
2021-07-12
  目录