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 只查找直接子对象
- 参数1:
- 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) 设置鼠标位置
- QCursor对象
鼠标跟踪
- 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仍可使其获得焦点
- Policy
- 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_())
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
动画曲线
- setEasingCurve(self, Union[QEasingCurve, QEasingCurve.Type])
- easingCurve() -> Union[QEasingCurve, QEasingCurve.Type]
- QEasingCurve取值: https://doc.qt.io/qt-5/qeasingcurve.html#Type-enum
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
设置动画曲线
- setEasingCurve(self, Union[QEasingCurve, QEasingCurve.Type])
- QEasingCurve取值:https://doc.qt.io/qt-5/qeasingcurve.html#Type-enum
#这里通过动画曲线,改变动画节奏:
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:“事件*
”
开源项目收集
- Qt-Material:PySide6、PySide2和PyQt5的 Material Design 风格样式表
- QDarkStyleSheet : 一个黑暗风格的样式表
- PyQt5_stylesheets :PyQt5 风格样式表
- CustomWidgets:PyQt Custom Widgets - PyQt 自定义控件
- PyQt Examples:PyQt各种测试和例子
- 3rd-Apps:Collecting 3rd Apps
赞助💰
如果你觉得对你有帮助,你可以请我喝一杯冰可乐!嘻嘻🤭
支付宝支付 | 微信支付 |