QScrollArea children 无法访问 parent

QScrollArea children cannot access parent

我有 QScrollArea,里面有几个 QPushButton。我无法让 parent() 方法在子小部件上工作,因此它 return 是父对象。在创建子按钮时,我确实将 self.scroll 作为父级传递,但这似乎不起作用。

为什么我需要使用 parent() 3 次才能到达 QScrollArea 对象?我希望单亲()会给我那个。

这是代码。我借用了 https://www.learnpyqt.com/courses/adanced-ui-features/qscrollarea/ 的代码作为示例。

from PyQt5.QtWidgets import (QWidget, QScrollArea, QVBoxLayout, QMainWindow)
from PyQt5.QtCore import Qt
from PyQt5 import QtWidgets
import sys


class ChildWidget(QtWidgets.QPushButton):

    def __init__(self, parent):
        super(ChildWidget, self).__init__(parent)

    def mousePressEvent(self, event):
        print(self.parent().metaObject().className())
        print(self.parent().parent().metaObject().className())
        print(self.parent().parent().parent().metaObject().className())


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.scroll = QScrollArea()
        self.widget = QWidget()
        self.vbox = QVBoxLayout()

        for i in range(1, 5):
            object = ChildWidget(self.scroll)
            self.vbox.addWidget(object)

        self.widget.setLayout(self.vbox)

        self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.scroll.setWidgetResizable(True)
        self.scroll.setWidget(self.widget)

        self.setCentralWidget(self.scroll)

        self.setGeometry(600, 100, 1000, 900)
        self.setWindowTitle('Scroll Area Demonstration')
        self.show()

        return


def main():
    app = QtWidgets.QApplication(sys.argv)
    main = MainWindow()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

按下按钮的return是:

QWidget
QWidget
QScrollArea

当您将小部件添加到布局时,该小部件是处理布局的容器 (QWidget) 的子项。

在您的情况下,只需添加一些打印即可了解父级何时更改:

# ...
# variable that stores the first ChildWidget:
first_widget = None

for i in range(1, 5):
    object = ChildWidget(self.scroll)
    if first_widget is None:
        first_widget = object

    self.vbox.addWidget(object)

print(first_widget.parent())

self.widget.setLayout(self.vbox)

print(first_widget.parent())

输出:

<PyQt5.QtWidgets.QScrollArea object at 0x7fb699721dc0>
<PyQt5.QtWidgets.QWidget object at 0x7fb699721e50>

因此在您的情况下,ChildWidget 是构造函数末尾“self.widget”的子项。

提示:如果您想了解结构,请使用 dumpObjectTree() 方法:

main = MainWindow()
print(main.dumpObjectTree())

输出:

MainWindow:: 
    QMainWindowLayout::_layout 
    QScrollArea:: 
        QWidget::qt_scrollarea_viewport 
            QWidget:: 
                QVBoxLayout:: 
                ChildWidget:: 
                ChildWidget:: 
                ChildWidget:: 
                ChildWidget:: 
        QWidget::qt_scrollarea_hcontainer 
            QScrollBar:: 
            QBoxLayout:: 
        QPropertyAnimation:: 
        QPropertyAnimation:: 
        QWidget::qt_scrollarea_vcontainer 
            QScrollBar:: 
            QBoxLayout:: 

观察 ChildWidget 和 QScrollArea 的层次结构。

设置父级不是“最终的”。

来自QLayout.addWidget()(以及所有 QLayout 子类的重新实现):

[...] This function uses addItem().

来自QLayout.addItem()

Note: The ownership of item is transferred to the layout, and it's the layout's responsibility to delete it.

当布局已经设置为小部件时,所有权将转移到布局的小部件(如果布局新设置为小部件,也会发生同样的情况)。

此外,当您使用 QAbstractScrollArea 后代(显然包括 QScrollArea)时,您有一个 QWidget 用作“视口”(在滚动区域内滚动的小部件),它具有滚动区域作为父级。使用 QScrollArea 时,setWidget(widget)widget 重新设置为视口。

因此,实际的层次结构是:

scrollarea
   viewport
       "container" widget
           button

如果您正在寻找父滚动区域(假设您没有使用嵌套滚动区域,包括项目视图的后代),您可以使用这样的方法:

class ChildWidget(QtWidgets.QPushButton):

    def __init__(self, parent):
        super(ChildWidget, self).__init__(parent)

    def mousePressEvent(self, event):
        obj = self
        while not isinstance(obj.parent(), QAbstractScrollArea):
            parent = obj.parent()
            if not parent:
                break
            obj = parent
        print(obj)