1 Qobject 对象和属性的操作
对象名称————设置和获取定义属性属性值获取所有属性
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("zcb")
print(obj.objectName()) # 读取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.1 qss的id选择器,属性选择器
是对象和属性的应用
- qss是qt的样式,普通的写法如下
- 但一般是引入qss文件
id 和属性结合的写法
- QLabel#label2[notice_level="error"]{ } # {}里面的内容可以在designer中复制
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):
with open("QObject.qss","r") as f:
qApp.setStyleSheet(f.read())
label1 =QLabel(self)
label1.setText("hello")
label2 = QLabel(self)
label2.move(100,100)
label2.setObjectName("label2")
label2.setText("id选择器")
label3 = QLabel(self)
label3.move(300, 100)
label3.setProperty("notice_level", "error")
label3.setText("属性选择器")
label4 = QLabel(self)
label4.move(300, 200)
label4.setProperty("notice_level", "error")
label4.setText("属性选择器")
button =QPushButton(self)
button.move(300, 300)
button.setText("类选择器")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
2 QObject对象父子关系操作
在使用中,父子关系用的最多的就是 btn= QPushButton(self) ,这样 按钮就在窗口上了,其实按钮的父对象就是窗口了
- 设置父子关系
- a.setParent(b)
- obj3.parent()
- 查父对象
- obj0.children()
- 获取直接后代
- obj0.findChildren(QObject)) ----这个又分两种,见下面
- 递归找后代
- 找直接后代
* print(obj2.findChild(QObject)) # 找一个
* # 参数1,类型
* # 参数2,objectName,默认为""
* # 参数3,Qt.FindDirectChildrenOnly 为不递归,默认递归
* print(obj2.findChildren(QObject)) # 找符合的所有的
'''
obj1.setParent(obj2) # 把obj2设置为obj1的父对象
print(obj1.parent()) # 获取父对象
print(obj2.children()) # 获取obj2的直接子对象
print(obj2.findChild(QObject)) # 找一个
# 参数1,类型
# 参数2,objectName,默认为""
# 参数3,Qt.FindDirectChildrenOnly 为不递归,默认递归
print(obj2.findChildren(QObject)) # 找符合的所有的
'''
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()
"""
objo
obj1 obj2
obj3 obj4 obj5
"""
def QObject_test(self):
obj0 = QObject()
obj1 = QObject()
obj2= QObject()
obj3 = QObject()
obj4 = QObject()
obj5 = QObject()
obj1.setParent(obj0)
obj2.setParent(obj0)
obj3.setParent(obj1)
obj4.setParent(obj2)
obj5.setParent(obj2)
print("obj0的直接后代有1和2:",obj0.children()) # 注:获取obj0 的直接子对象 obj1 和obj2
print("3的父对象是1:",obj3.parent()) # 得到obj1
print("0找匹配所有的后代,默认递归:有12345",obj0.findChildren(QObject)) # 有五个
print("0找匹配所有的后代,不递归:有12",obj0.findChildren(QObject,"",Qt.FindDirectChildrenOnly)) # 只有两个
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
下面的代码就可以说明:
- 继承和 通过设置父对象,都可以关联父子关系
- 没有父对象的 对象 (顶级控件),会单独搞一个窗口
'''
2023/1/13 15:43
'''
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):
#---------------------方式1,直接继承 self-----------------------
button1 = QPushButton(self)
button1.setText("继承窗口")
button1.move(100, 100)
button1.destroyed.connect(lambda: print("button1-关闭window会被摧毁"))
print("----",self)
print("----",button1.parent())
# -------------------方式2,此按钮单独显示- (如果一个)-----------------------
self.button2 = QPushButton()
self.button2.setText("没有父控件,自己就是顶级控件,单独展示")
self.button2.destroyed.connect(lambda: print("button2-关闭自己的窗口才会摧毁"))
self.button2.show() # 这种不会自动show
# --------------------方式3,方式2+设置父对象--------------------------------------
self.button3 = QPushButton()
self.button3.setParent(self)
self.button3.setText("此按钮的父对象是window")
self.button3.destroyed.connect(lambda: print("button3-关闭window会被摧毁"))
"""总结:继承 和 设置都可以当儿子"""
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
摧毁机制:
- 父对象摧毁了,子对象也被摧毁了
- 两种删除
-
del obj2 # 只是删 栈中的临时对象
-
print(obj1.deleteLater()) # 即使是全局变量,使用 deleteLater 也可以释放对象。
-
'''
2023/1/13 15:43
'''
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):
obj1 = QObject()
self.obj = obj1 # 有了这个,obj1就不会直接释放
obj2 = QObject()
obj2.setParent(obj1) # 有了这个 obj2也不会被直接释放了
# 监听obj2
obj2.destroyed.connect(lambda: print("obj2被释放"))
# del self.obj
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
2.1 利用findChildren() 遍历子控件做样式
from PyQt5.Qt import * # 刚开始学习可以这样一下导入
import sys
app = QApplication(sys.argv)
win_root = QWidget()
label1 = QLabel()
label1.setText("Label1")
label1.setParent(win_root)
label2 = QLabel()
label2.setText("Label2")
label2.setParent(win_root)
label2.move(50,50)
btn = QPushButton(win_root)
btn.setText("btn")
btn.move(100,100)
for sub_widget in win_root.findChildren(QLabel):
print(sub_widget)
sub_widget.setStyleSheet("background-color:cyan;")
win_root.show()
sys.exit(app.exec_())
3.信号
信号和槽,当被**时,就会触发信号(有参数or无参数),做槽动作
- 当信号带有参数时,用槽函数去接收就好了,有无参数,ctrl+点击信号可以查看
- 信号和槽函数连接
obj.某信号.connect(槽函数) - 某信号 断开(针对信号) # 关闭信号的释放
obj.某信号.disconnect() - 对象 所有信号都临时 不发(针对对象)
obj.blockSignals(bool) - 查看 某信号 关联 了几个槽函数
对象.receivers(obj.信号)
Oobject的信号:
obj.destroyed#当对象被销毁时会触发这个信号obj.objectNameChanged# 当对象的名称改变时触发这个信号
obj.信号.connect(槽函数)
'''
obj.信号.connect(槽函数)
'''
# obj.destroyed #当对象被销毁时
# obj.destroyed(obj) # 参数为 被摧毁的对象
# obj.objectNameChanged # 当对象的名称改变时
# obj.objectNameChanged(QString) # 参数为 修改后的名字
"""信号槽机制:
一个信号可以对应多个槽函数
多个信号连接 对应一个槽函数
信号在发射时,会向外界传递参数
"""
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_signal_test()
def QObject_signal_test(self):
self.obj = QObject()
def nameChanged_slot():
print("对象名称被修改了")
self.obj.objectNameChanged.connect(nameChanged_slot)
self.obj.objectNameChanged.connect(lambda x: print(f"名字被改成了{x}")) # 有参数的
self.obj.setObjectName("zcb")
self.obj.setObjectName("tom")
self.obj.destroyed.connect(lambda: print("摧毁了"))
self.obj.destroyed.connect(lambda x: print(f"{x.objectName()}被摧毁了")) # 有参数的
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
obj.信号.disconnect()
表示 取消 某种信号的释放
'''
2023/1/13 17:30
obj.某信号.disconnect() # 关闭信号的释放
'''
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_signal_test()
def QObject_signal_test(self):
self.obj = QObject()
self.obj.setObjectName("对象0")
def objectNameChanged_slot(arg): # 会自动接收信号发送的参数
print(f"对象的名字被修改成了{arg}")
self.obj.objectNameChanged.connect(objectNameChanged_slot)
self.obj.setObjectName("tom")
self.obj.objectNameChanged.disconnect() # 取消 某种信号的释放
self.obj.setObjectName("jack")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
使用 blockSignals(bool) 来临时阻断对象 发信号
如下代码,被锁住时,就发不出信号,触发不到槽函数
'''
2023/1/13 17:30
obj.blockSignals(True) ----设置
print(obj.signalsBlocked()) ----获取查看
'''
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_signal_test()
def QObject_signal_test(self):
self.obj = QObject()
self.obj.setObjectName("对象0")
def objectNameChanged_slot(arg): # 会自动接收信号发送的参数
print("对象的名字被修改成了{}".format(arg))
self.obj.objectNameChanged.connect(objectNameChanged_slot)
self.obj.setObjectName("tom") # 触发
self.obj.blockSignals(True) # 临时阻断self.obj所有信号的连接(注不是断开连接)
self.obj.setObjectName("jack") # 不触发
self.obj.blockSignals(False) # 将临时阻断去掉
self.obj.setObjectName("richel") # 也想触发
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
查看信号连接了几个槽函数
对象.receivers(obj.信号)
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_signal_test()
def QObject_signal_test(self):
self.obj = QObject()
self.obj.setObjectName("对象0")
def objectNameChanged_slot1(arg):
print("对象的名字被修改成了{}".format(arg))
def objectNameChanged_slot2(arg):
print("对象的名字被修改成了{}".format(arg))
"""先关联两个槽函数"""
self.obj.objectNameChanged.connect(objectNameChanged_slot1)
self.obj.objectNameChanged.connect(objectNameChanged_slot2)
print(f"信号有{self.obj.receivers(self.obj.objectNameChanged)}个槽函数")
"""信号断开全部槽函数"""
self.obj.objectNameChanged.disconnect()
print(f"信号有{self.obj.receivers(self.obj.objectNameChanged)}个槽函数")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
下面是一个小案例,自己搞了一个死循环,信号是点击信号, 槽函数里又有点击, 这种是不允许的,加了对象临时阻断信号,就不会报错了
from PyQt5.Qt import * # 刚开始学习可以这样一下导入
import sys,random,time
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("QObject的学习")
self.resize(400, 400)
self.set_ui()
def set_ui(self):
self.QObject_signal_test()
def QObject_signal_test(self):
self.btn=QPushButton(self)
self.btn.move(30,30)
self.btn.clicked.connect(self.set_color) # 点击 触发 set_color
# 随机设置颜色
def set_color(self):
n1=random.randint(1,255)
n2 = random.randint(1, 255)
n3 = random.randint(1, 255)
self.btn.setStyleSheet(f"background-color: rgb({n1}, {n2}, {n3});")
self.btn.blockSignals(True) # 临时阻塞信号
self.btn.click()
self.btn.blockSignals(False) # 再次开启信号的连接
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
四、类型判断
obj.isWidgetType()判断是否为控件(Qwidget)窗口、按钮、标签、输入框都是控件obj = QObject() obj.isWidgetType() # False w = QWidget() w.isWidgetType() # True btn = QPushButton() btn.isWidgetType() # True label = QLabel() label.isWidgetType() # Trueobj.inherits("QWidget")或obj.inherits("QPushButton")判断是否继承某一类(比1更全)
用法:- 可以用来做判断,然后给某类控件添加样式 (就是筛选控件的意思)
添加样式小总结- 使用QSS选择器,qss文件
- 使用window.findChildren(Qlable), 然后for循环,遍历来加样式
- 遍历 所有的 children(),然后再用inherits来判断,再加样式(没啥用吧,不如上面两种)
- 可以用来做判断,然后给某类控件添加样式 (就是筛选控件的意思)
五、对象删除
obj_2.deleteLater() 表示稍后删除对象
总结:删除控件时不能使用del 删除控件,删除不了!
要用deleteLater() 删除,而且这个删除是稍后删除!
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_delete()
def QObject_delete(self):
obj_1 = QObject()
self.obj = obj_1
obj_2 = QObject()
obj_3 = QObject()
obj_3.setParent(obj_2)
obj_2.setParent(obj_1)
obj_1.destroyed.connect(lambda :print("obj1 被释放"))
obj_2.destroyed.connect(lambda :print("obj2 被释放"))
obj_3.destroyed.connect(lambda :print("obj3 被释放"))
# del obj_2 # 这时候没有任何效果,因为obj_2 还被obj_1 引用着呢!
obj_2.deleteLater() # 删除对象时,也会解除它与父对象的关系
print(obj_1.children()) # 因为是稍后删除,所以是可以打印的
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
'''
42 输出:
43 [<PyQt5.QtCore.QObject object at 0x000001A0A34C6E58>] #这是因为稍后删除
44 obj2 被释放
45 obj3 被释放
46 '''
六、对象事件处理(了解):
要让程序运行,用到了下面这段代码
if __name__ == '__main__':
app = QApplication(sys.argv) # 创建app
window = Window()
window.show()
sys.exit(app.exec_()) # 让持续运行
如: 要让一个按钮释放出被点击信号,经过了(层层分发)后
- QApplication 中notify 方法
- QPushButton 中 event方法
- 和mousePressEvent 方法, 此时就释放了信号,然后再去关联槽函数
from PyQt5.Qt import *
import sys
"""
1.鼠标操作windows
2.应用程序接收到了 按下事件
3.按钮控件接收到了 按下事件
4. 调用mousePressEvent() 方法,最终发射出pressed的信号。
5. 触发槽函数
"""
class App(QApplication):
# 重写方法
def notify(self,receiver,event):
if receiver.inherits("QPushButton") and event.type() == QEvent.MouseButtonPress:
print(receiver)
print("1.app接收到了",event)
return super().notify(receiver, event)
class Btn(QPushButton):
def event(self, event): # 继续往下分发
if event.type() == QEvent.MouseButtonPress:
print("2.btn接收到了",event)
return super().event(event) # 继续往下分发
def mousePressEvent(self, *args, **kwargs): # 按下会触发 mousePressEvent
print("3.------哈哈哈哈哈-------")
return super().mousePressEvent( *args, **kwargs) # 释放信号
#---------------启动程序打开窗口-----
app = App(sys.argv) # 用被重写方法的app , 因为有打印,就会一直打印
# app = QApplication(sys.argv) # 用本来的
window =QWidget()
#---------------启动程序打开窗口-----
btn =Btn(window)
btn.setText("按钮")
btn.move(100,100)
btn.pressed.connect(lambda :print("I am here"))
#鼠标只要是按下就行,而btn.clicked 鼠标是点击之后又起来
#---------------启动程序打开窗口-----
window.show()
sys.exit(app.exec_())
#---------------启动程序打开窗口-----
七、QObject 对象_定时器
1. self窗口做定时器的案例
# -*- coding:utf-8 -*-
from PyQt5.Qt import QWidget,QPushButton,QApplication
import sys
class Window(QWidget):
def __init__(self):
super().__init__() # 调用父类QWidget中的init方法
self.setWindowTitle("软件名称")
self.resize(600, 500)
self.func_list()
# def timerEvent(self, QTimerEvent): # 重写事件 (专门的参数用来接东西的)
# print("我是窗口的定时器")
# 也可以直接这样写,不用再继承什么,这个叫重写方法
def timerEvent(self, *args, **kwargs): # 重写事件 (专门的参数用来接东西的)
print("我是窗口的定时器")
def func_list(self):
btn =QPushButton("开始定时",self)
btn.move(50,50)
btn.clicked.connect(self.start_timeer)
btn = QPushButton("结束定时", self)
btn.move(50, 100)
btn.clicked.connect(self.kill_timer)
def start_timeer(self):
self.timer_id = self.startTimer(1000)
def kill_timer(self):
self.killTimer(self.timer_id)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
2. 把定时器设置到 自定义Label类 的 初始化 init中, 倒计时,变成0就结束
from PyQt5.Qt import * #刚开始学习可以这样一下导入
import sys
"""
把定时器设置到 自定义Label类 的 初始化 init中
其他:
还可以把样式,大小,也设置进来
"""
class MyLabel(QLabel):
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.timer_id =self.startTimer(1000) # 看这里
self.setText("5")
# self.setStyleSheet("font-size:22px;")
def timerEvent(self, *args, **kwargs):
print("python",)
#1,获取当前的标签内容
current_sec = int(self.text())
current_sec -=1
self.setText(str(current_sec))
if current_sec ==0:
self.killTimer(self.timer_id)
#1,创建app
app = QApplication(sys.argv)
#2,控件的操作:
#创建控件
window = QWidget()
#设置控件
window.setWindowTitle("QObject 之定时器的学习")
window.resize(500,500)
label = MyLabel(window)
# label.setText("10")
label.setStyleSheet("font-size:22px;")
label.move(200,200)
# timer_id = label.startTimer(1000) # 因为label 肯定是继承QObject 的,所以肯定有starttimer方法。
# # 此时要想真正工作还是要重写QLabel类中的timerEvent()方法
#
#展示控件
window.show()
#3,进入消息循环
sys.exit(app.exec_())
优化-封装函数,自定义倒计时时间和速度
from PyQt5.Qt import * #刚开始学习可以这样一下导入
import sys
"""
把定时器设置到 自定义Label类的 ,自定义方法中,更加灵活
可以设置倒计时 和 定时间隔
"""
class MyLabel(QLabel):
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
# self.timer_id =self.startTimer(1000)
# self.setText("30")
self.setStyleSheet("font-size:22px;")
self.setText("10")
self.move(200, 200)
def startMytimer(self,start_time,ms):
self.setText(str(start_time))
self.timer_id = self.startTimer(ms)
def timerEvent(self, *args, **kwargs):
print("python",)
#1,获取当前的标签内容
current_sec = int(self.text())
current_sec -=1
self.setText(str(current_sec))
if current_sec ==0:
self.killTimer(self.timer_id)
#1,创建app
app = QApplication(sys.argv)
#2,控件的操作:
#创建控件
window = QWidget()
#设置控件
window.setWindowTitle("QObject 之定时器的学习")
window.resize(500,500)
label = MyLabel(window)
label.startMytimer(10,500)
#展示控件
window.show()
#3,进入消息循环
sys.exit(app.exec_())