pyqt5 父控件 与 子控件 之间的关系 ---- 多窗口动画切换的实现

本文深入探讨PyQT中__init__方法的parent参数作用,解释parent-child关系如何影响窗体管理和资源回收,并通过多个代码示例展示不同情况下子窗体的显示行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PyQT中__init__(self,parent==None)的parent含义

在PyQt中,所以class都是从QObject派生而来,QWidget对象就可以有一个parent。这种parent-child关系主要用于两个方面:

没有parent的QWidget类被认为是最上层的窗体(通常是MainWindow),由于MainWindow的一些操作生成的新窗体对象,parent都应该指向MainWindow。
由于parent-child关系的存在,它保证了child窗体在主窗体被回收之时也被回收。
parent作为构造函数的最后一个参数被传入,但通常情况下不必显示去指定parent对象。因为当调用局管理器时,部局管理器会自动处理这种parent-child关系。但是在一些特殊的情况下,我们必须显示的指定parent-child关系。如当生成的子类不是QWidget对象但继承了QObject对象,用作dock widgets的QWidget对象。
来自:
https://blog.youkuaiyun.com/thumb3344/article/details/5644789

一个 子控件 在主窗口show之前,设置主窗口为其父类,主窗口show的时候,会显示这个字控件。
如果这个子控件在 主控口show之后,设置主窗口为其父类, 这个控件就不会被显示

代码示例1:显示的是两个独立的窗口

multi_win1.py

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

import sys
import urllib.request


class ChildWidget(QWidget):

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        print('ChildWidget init')
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowTitle('ChildWidget 模板')

        self.top_layout = QHBoxLayout()
        self.setLayout(self.top_layout)
        self.resize(500, 300)
        self.left_widget = QWidget()
        self.mid_layout = QHBoxLayout()
        self.right_widget = QWidget()
        self.setObjectName('Top_ChildWidget')
        self.setStyleSheet("#Top_ChildWidget{border: 2px solid red;}")

        self.top_layout.addWidget(self.left_widget)
        self.top_layout.addLayout(self.mid_layout)
        self.top_layout.addWidget(self.right_widget)

        self.next_btn = QPushButton('下一页')
        self.back_btn = QPushButton('返回')
        self.mid_layout.addWidget(self.next_btn)
        self.mid_layout.addWidget(self.back_btn)



class MainWidget(QWidget):
    main_widget_signal = pyqtSignal(dict)

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        print("MainWidget init")
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowTitle('MainWidget 模板')

        #print(type(self).__name__)

        self.top_layout = QVBoxLayout()
        self.setLayout(self.top_layout)
        self.resize(500, 300)

        self.dataView = QTableView()
        self.model = QStandardItemModel(3, 0, self)

        self.model.setHorizontalHeaderItem(0, QStandardItem("姓名"))
        self.model.setHorizontalHeaderItem(1, QStandardItem("性别"))
        self.model.setHorizontalHeaderItem(2, QStandardItem("体重(kg)"))

        self.dataView.setModel(self.model)

        self.child_layout = QHBoxLayout()
        self.next_btn = QPushButton('下一页')
        self.back_btn = QPushButton('返回')
        self.child_layout.addWidget(self.next_btn)
        self.child_layout.addWidget(self.back_btn)

        self.top_layout.addLayout(self.child_layout)
        self.top_layout.addWidget(self.dataView)

        self.next_btn.clicked.connect(self.slot_next_btn_func)
        self.back_btn.clicked.connect(self.slot_back_btn_func)

    def slot_next_btn_func(self):
        msg_dict = {}
        msg_dict['name'] = 'MainWidget'
        print('slot_next_btn_func')
        msg_dict['action'] = 'next'
        self.main_widget_signal.emit(msg_dict)
        #print('MainWidget slot_next_btn_func', msg_dict)


    def slot_back_btn_func(self):
        msg_dict = {}
        msg_dict['name'] = 'MainWidget'
        msg_dict['action'] = 'back'
        self.main_widget_signal.emit(msg_dict)
        print('MainWidget slot_back_btn_func', msg_dict)


def slot_call_back_func(my_dict):
    print("slot_call_back_func", my_dict)



if __name__ == '__main__':

    app = QApplication(sys.argv)

    main = MainWidget()

    main.main_widget_signal.connect(slot_call_back_func)

    child_win = ChildWidget()
    main.show()
    child_win.show()

    sys.exit(app.exec_())


在这里插入图片描述
在这里插入图片描述

代码示例2:显示的是两个独立的窗口 变为 父子窗口 show在后
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

import sys
import urllib.request


class ChildWidget(QWidget):

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        print('ChildWidget init')
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowTitle('ChildWidget 模板')

        self.top_layout = QHBoxLayout()
        self.setLayout(self.top_layout)
        self.resize(500, 300)
        self.left_widget = QWidget()
        self.mid_layout = QHBoxLayout()
        self.right_widget = QWidget()
        self.setObjectName('Top_ChildWidget')
        self.setStyleSheet("#Top_ChildWidget{border: 2px solid red;background:rgba(0,0,0,0.2)}")

        self.top_layout.addWidget(self.left_widget)
        self.top_layout.addLayout(self.mid_layout)
        self.top_layout.addWidget(self.right_widget)

        self.next_btn = QPushButton('下一页')
        self.back_btn = QPushButton('返回')
        self.mid_layout.addWidget(self.next_btn)
        self.mid_layout.addWidget(self.back_btn)



class MainWidget(QWidget):
    main_widget_signal = pyqtSignal(dict)

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        print("MainWidget init")
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowTitle('MainWidget 模板')

        #print(type(self).__name__)

        self.top_layout = QVBoxLayout()
        self.setLayout(self.top_layout)
        self.resize(500, 300)

        self.dataView = QTableView()
        self.model = QStandardItemModel(3, 0, self)

        self.model.setHorizontalHeaderItem(0, QStandardItem("姓名"))
        self.model.setHorizontalHeaderItem(1, QStandardItem("性别"))
        self.model.setHorizontalHeaderItem(2, QStandardItem("体重(kg)"))

        self.dataView.setModel(self.model)

        self.child_layout = QHBoxLayout()
        self.next_btn = QPushButton('下一页')
        self.back_btn = QPushButton('返回')
        self.child_layout.addWidget(self.next_btn)
        self.child_layout.addWidget(self.back_btn)

        self.top_layout.addLayout(self.child_layout)
        self.top_layout.addWidget(self.dataView)

        self.next_btn.clicked.connect(self.slot_next_btn_func)
        self.back_btn.clicked.connect(self.slot_back_btn_func)

    def slot_next_btn_func(self):
        msg_dict = {}
        msg_dict['name'] = 'MainWidget'
        print('slot_next_btn_func')
        msg_dict['action'] = 'next'
        self.main_widget_signal.emit(msg_dict)
        #print('MainWidget slot_next_btn_func', msg_dict)


    def slot_back_btn_func(self):
        msg_dict = {}
        msg_dict['name'] = 'MainWidget'
        msg_dict['action'] = 'back'
        self.main_widget_signal.emit(msg_dict)
        print('MainWidget slot_back_btn_func', msg_dict)


def slot_call_back_func(my_dict):
    print("slot_call_back_func", my_dict)



if __name__ == '__main__':

    app = QApplication(sys.argv)

    main = MainWidget()

    main.main_widget_signal.connect(slot_call_back_func)

    child_win = ChildWidget(main)
    main.show()


    sys.exit(app.exec_())


显示效果:

在这里插入图片描述
在这里插入图片描述

代码示例3:显示的是两个独立的窗口 变为 父子窗口 show在前
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

import sys
import urllib.request


class ChildWidget(QWidget):

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        print('ChildWidget init')
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowTitle('ChildWidget 模板')

        self.top_layout = QHBoxLayout()
        self.setLayout(self.top_layout)
        self.resize(500, 300)
        self.left_widget = QWidget()
        self.mid_layout = QHBoxLayout()
        self.right_widget = QWidget()
        self.setObjectName('Top_ChildWidget')
        self.setStyleSheet("#Top_ChildWidget{border: 2px solid red;background:rgba(0,0,0,0.2)}")

        self.top_layout.addWidget(self.left_widget)
        self.top_layout.addLayout(self.mid_layout)
        self.top_layout.addWidget(self.right_widget)

        self.next_btn = QPushButton('下一页')
        self.back_btn = QPushButton('返回')
        self.mid_layout.addWidget(self.next_btn)
        self.mid_layout.addWidget(self.back_btn)



class MainWidget(QWidget):
    main_widget_signal = pyqtSignal(dict)

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        print("MainWidget init")
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowTitle('MainWidget 模板')

        #print(type(self).__name__)

        self.top_layout = QVBoxLayout()
        self.setLayout(self.top_layout)
        self.resize(500, 300)

        self.dataView = QTableView()
        self.model = QStandardItemModel(3, 0, self)

        self.model.setHorizontalHeaderItem(0, QStandardItem("姓名"))
        self.model.setHorizontalHeaderItem(1, QStandardItem("性别"))
        self.model.setHorizontalHeaderItem(2, QStandardItem("体重(kg)"))

        self.dataView.setModel(self.model)

        self.child_layout = QHBoxLayout()
        self.next_btn = QPushButton('下一页')
        self.back_btn = QPushButton('返回')
        self.child_layout.addWidget(self.next_btn)
        self.child_layout.addWidget(self.back_btn)

        self.top_layout.addLayout(self.child_layout)
        self.top_layout.addWidget(self.dataView)

        self.next_btn.clicked.connect(self.slot_next_btn_func)
        self.back_btn.clicked.connect(self.slot_back_btn_func)

    def slot_next_btn_func(self):
        msg_dict = {}
        msg_dict['name'] = 'MainWidget'
        print('slot_next_btn_func')
        msg_dict['action'] = 'next'
        self.main_widget_signal.emit(msg_dict)
        #print('MainWidget slot_next_btn_func', msg_dict)


    def slot_back_btn_func(self):
        msg_dict = {}
        msg_dict['name'] = 'MainWidget'
        msg_dict['action'] = 'back'
        self.main_widget_signal.emit(msg_dict)
        print('MainWidget slot_back_btn_func', msg_dict)


def slot_call_back_func(my_dict):
    print("slot_call_back_func", my_dict)



if __name__ == '__main__':

    app = QApplication(sys.argv)

    main = MainWidget()

    main.main_widget_signal.connect(slot_call_back_func)

    main.show()
    child_win = ChildWidget(main)
    #main.show()


    sys.exit(app.exec_())


在这里插入图片描述

代码示例4:显示的是两个独立的窗口 变为 父子窗口 子窗口show
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

import sys
import urllib.request


class ChildWidget(QWidget):

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        print('ChildWidget init')
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowTitle('ChildWidget 模板')

        self.top_layout = QHBoxLayout()
        self.setLayout(self.top_layout)
        self.resize(500, 300)
        self.left_widget = QWidget()
        self.mid_layout = QHBoxLayout()
        self.right_widget = QWidget()
        self.setObjectName('Top_ChildWidget')
        self.setStyleSheet("#Top_ChildWidget{border: 2px solid red;background:rgba(0,0,0,0.2)}")

        self.top_layout.addWidget(self.left_widget)
        self.top_layout.addLayout(self.mid_layout)
        self.top_layout.addWidget(self.right_widget)

        self.next_btn = QPushButton('下一页')
        self.back_btn = QPushButton('返回')
        self.mid_layout.addWidget(self.next_btn)
        self.mid_layout.addWidget(self.back_btn)



class MainWidget(QWidget):
    main_widget_signal = pyqtSignal(dict)

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        print("MainWidget init")
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowTitle('MainWidget 模板')

        #print(type(self).__name__)

        self.top_layout = QVBoxLayout()
        self.setLayout(self.top_layout)
        self.resize(500, 300)

        self.dataView = QTableView()
        self.model = QStandardItemModel(3, 0, self)

        self.model.setHorizontalHeaderItem(0, QStandardItem("姓名"))
        self.model.setHorizontalHeaderItem(1, QStandardItem("性别"))
        self.model.setHorizontalHeaderItem(2, QStandardItem("体重(kg)"))

        self.dataView.setModel(self.model)

        self.child_layout = QHBoxLayout()
        self.next_btn = QPushButton('下一页')
        self.back_btn = QPushButton('返回')
        self.child_layout.addWidget(self.next_btn)
        self.child_layout.addWidget(self.back_btn)

        self.top_layout.addLayout(self.child_layout)
        self.top_layout.addWidget(self.dataView)

        self.next_btn.clicked.connect(self.slot_next_btn_func)
        self.back_btn.clicked.connect(self.slot_back_btn_func)

    def slot_next_btn_func(self):
        msg_dict = {}
        msg_dict['name'] = 'MainWidget'
        print('slot_next_btn_func')
        msg_dict['action'] = 'next'
        self.main_widget_signal.emit(msg_dict)
        #print('MainWidget slot_next_btn_func', msg_dict)


    def slot_back_btn_func(self):
        msg_dict = {}
        msg_dict['name'] = 'MainWidget'
        msg_dict['action'] = 'back'
        self.main_widget_signal.emit(msg_dict)
        print('MainWidget slot_back_btn_func', msg_dict)


def slot_call_back_func(my_dict):
    print("slot_call_back_func", my_dict)



if __name__ == '__main__':

    app = QApplication(sys.argv)

    main = MainWidget()

    main.main_widget_signal.connect(slot_call_back_func)

    main.show()
    child_win = ChildWidget(main)
    #main.show()
    child_win.show()

    sys.exit(app.exec_())


在这里插入图片描述

代码示例5:显示的是 父子窗口 子窗口show 向下move
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

import sys
import urllib.request


class ChildWidget(QWidget):

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        print('ChildWidget init')
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowTitle('ChildWidget 模板')

        self.top_layout = QHBoxLayout()
        self.setLayout(self.top_layout)
        self.resize(500, 300)
        self.left_widget = QWidget()
        self.mid_layout = QHBoxLayout()
        self.right_widget = QWidget()
        self.setObjectName('Top_ChildWidget')
        self.setStyleSheet("#Top_ChildWidget{border: 2px solid red;background:rgba(0,0,0,0.2)}")

        self.top_layout.addWidget(self.left_widget)
        self.top_layout.addLayout(self.mid_layout)
        self.top_layout.addWidget(self.right_widget)

        self.next_btn = QPushButton('下一页')
        self.back_btn = QPushButton('返回')
        self.mid_layout.addWidget(self.next_btn)
        self.mid_layout.addWidget(self.back_btn)



class MainWidget(QWidget):
    main_widget_signal = pyqtSignal(dict)

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        print("MainWidget init")
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowTitle('MainWidget 模板')

        #print(type(self).__name__)

        self.top_layout = QVBoxLayout()
        self.setLayout(self.top_layout)
        self.resize(500, 300)

        self.dataView = QTableView()
        self.model = QStandardItemModel(3, 0, self)

        self.model.setHorizontalHeaderItem(0, QStandardItem("姓名"))
        self.model.setHorizontalHeaderItem(1, QStandardItem("性别"))
        self.model.setHorizontalHeaderItem(2, QStandardItem("体重(kg)"))

        self.dataView.setModel(self.model)

        self.child_layout = QHBoxLayout()
        self.next_btn = QPushButton('下一页')
        self.back_btn = QPushButton('返回')
        self.child_layout.addWidget(self.next_btn)
        self.child_layout.addWidget(self.back_btn)

        self.top_layout.addLayout(self.child_layout)
        self.top_layout.addWidget(self.dataView)

        self.next_btn.clicked.connect(self.slot_next_btn_func)
        self.back_btn.clicked.connect(self.slot_back_btn_func)

    def slot_next_btn_func(self):
        msg_dict = {}
        msg_dict['name'] = 'MainWidget'
        print('slot_next_btn_func')
        msg_dict['action'] = 'next'
        self.main_widget_signal.emit(msg_dict)
        #print('MainWidget slot_next_btn_func', msg_dict)


    def slot_back_btn_func(self):
        msg_dict = {}
        msg_dict['name'] = 'MainWidget'
        msg_dict['action'] = 'back'
        self.main_widget_signal.emit(msg_dict)
        print('MainWidget slot_back_btn_func', msg_dict)


def slot_call_back_func(my_dict):
    print("slot_call_back_func", my_dict)



if __name__ == '__main__':

    app = QApplication(sys.argv)

    main = MainWidget()

    main.main_widget_signal.connect(slot_call_back_func)

    main.show()
    child_win = ChildWidget(main)
    #main.show()
    child_win.show()
    child_win.move(0, main.height()/3)
    sys.exit(app.exec_())


在这里插入图片描述
上面的可以点击 ,下面的也可以点击了
在这里插入图片描述
子控件被隐藏的 1/3 窗体去哪儿了,会影响其他的,紧挨着的 其他应用的窗口不?

代码示例6:显示的是 父子窗口 子窗口show 动画
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

import sys
import urllib.request

class ChildWidget(QWidget):

    child_widget_signal = pyqtSignal(dict)
    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        print('ChildWidget init')
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowTitle('ChildWidget 模板')

        self.top_layout = QHBoxLayout()
        self.setLayout(self.top_layout)
        self.resize(500, 300)
        self.left_widget = QWidget()
        self.mid_layout = QHBoxLayout()
        self.right_widget = QWidget()
        self.setObjectName('Top_ChildWidget')
        self.setStyleSheet("#Top_ChildWidget{border: 2px solid red;background:rgba(0,0,0,0.2)}")

        self.top_layout.addWidget(self.left_widget)
        self.top_layout.addLayout(self.mid_layout)
        self.top_layout.addWidget(self.right_widget)

        self.next_btn = QPushButton('下一页')
        self.back_btn = QPushButton('返回')
        self.mid_layout.addWidget(self.next_btn)
        self.mid_layout.addWidget(self.back_btn)

        self.next_btn.clicked.connect(self.slot_next_btn_func)

    def slot_next_btn_func(self):
        pass
        msg_dict = dict()
        msg_dict['name'] = 'ChildWidget'
        msg_dict['action'] = 'next'
        self.child_widget_signal.emit(msg_dict)


class MainWidget(QWidget):
    main_widget_signal = pyqtSignal(dict)

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        print("MainWidget init")
        self.setAttribute(Qt.WA_StyledBackground, True)
        self.setWindowTitle('MainWidget 模板')

        #print(type(self).__name__)

        self.top_layout = QVBoxLayout()
        self.setLayout(self.top_layout)
        self.resize(500, 300)

        self.dataView = QTableView()
        self.model = QStandardItemModel(3, 0, self)

        self.model.setHorizontalHeaderItem(0, QStandardItem("姓名"))
        self.model.setHorizontalHeaderItem(1, QStandardItem("性别"))
        self.model.setHorizontalHeaderItem(2, QStandardItem("体重(kg)"))

        self.dataView.setModel(self.model)

        self.child_layout = QHBoxLayout()
        self.next_btn = QPushButton('下一页')
        self.back_btn = QPushButton('返回')
        self.child_layout.addWidget(self.next_btn)
        self.child_layout.addWidget(self.back_btn)

        self.top_layout.addLayout(self.child_layout)
        self.top_layout.addWidget(self.dataView)

        self.next_btn.clicked.connect(self.slot_next_btn_func)
        self.back_btn.clicked.connect(self.slot_back_btn_func)

    def slot_next_btn_func(self):
        msg_dict = {}
        msg_dict['name'] = 'MainWidget'
        print('slot_next_btn_func')
        msg_dict['action'] = 'next'
        self.main_widget_signal.emit(msg_dict)
        #print('MainWidget slot_next_btn_func', msg_dict)


    def slot_back_btn_func(self):
        msg_dict = {}
        msg_dict['name'] = 'MainWidget'
        msg_dict['action'] = 'back'
        self.main_widget_signal.emit(msg_dict)
        print('MainWidget slot_back_btn_func', msg_dict)


def slot_call_back_func(my_dict):
    print("slot_call_back_func", my_dict)
    animation = QPropertyAnimation(child_win)
    animation.setTargetObject(child_win)
    animation.setPropertyName(b"pos")
    x_start = 0
    y_start = child_win.height()
    point_start = QPoint(x_start, y_start)
    animation.setStartValue(point_start)
    x_end = 0
    y_end = 0
    point_end = QPoint(x_end, y_end)
    animation.setEndValue(point_end)
    animation.setDuration(1000)
    animation.start(QAbstractAnimation.DeleteWhenStopped)
    animation.setEasingCurve(QEasingCurve.OutBounce)

def slot_child_widget_signal_func(my_dict):
    pass
    child_win.move(0, main.height())

if __name__ == '__main__':

    app = QApplication(sys.argv)

    main = MainWidget()

    main.main_widget_signal.connect(slot_call_back_func)

    main.show()
    child_win = ChildWidget(main)
    #main.show()
    child_win.show()
    child_win.move(0, main.height())
    child_win.child_widget_signal.connect(slot_child_widget_signal_func)
    sys.exit(app.exec_())


显示效果:
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值