PyQt5控件学习(一)___QObject

248 阅读11分钟

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选择器,属性选择器

是对象和属性的应用

  1. qss是qt的样式,普通的写法如下
    image.png
  2. 但一般是引入qss文件
    image.png
    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+点击信号可以查看 image.png
  • 信号和槽函数连接 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_())


四、类型判断

  1. obj.isWidgetType()判断是否为控件(Qwidget) 窗口、按钮、标签、输入框都是控件
    obj = QObject()
    obj.isWidgetType()     # False
    
    w = QWidget()
    w.isWidgetType()     # True
    
    btn = QPushButton()
    btn.isWidgetType()     # True
    
    label = QLabel()
    label.isWidgetType()     # True
    
  2. obj.inherits("QWidget")obj.inherits("QPushButton") 判断是否继承某一类(比1更全) image.png
    用法:
    • 可以用来做判断,然后给某类控件添加样式 (就是筛选控件的意思) 添加样式小总结
      1. 使用QSS选择器,qss文件
      2. 使用window.findChildren(Qlable), 然后for循环,遍历来加样式
      3. 遍历 所有的 children(),然后再用inherits来判断,再加样式(没啥用吧,不如上面两种)
        image.png

五、对象删除

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_())   # 让持续运行

如: 要让一个按钮释放出被点击信号,经过了(层层分发)后

  1. QApplication 中notify 方法
  2. QPushButton 中 event方法
  3. 和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_())