Дети QScrollArea не могут получить доступ к родителю - PullRequest
0 голосов
/ 02 августа 2020

У меня есть QScrollArea с несколькими QPushButton внутри. Я не могу заставить метод parent () работать с дочерними виджетами, чтобы он возвращал родительский объект. Я передаю self.scroll как родительский при создании дочерних кнопок, но это, похоже, не работает.

Почему мне нужно использовать parent () 3 раза, чтобы добраться до объекта QScrollArea? Я ожидал, что single parent () даст мне именно это.

Вот код. В качестве примера я позаимствовал код из 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()

При нажатии кнопки возвращается:

QWidget
QWidget
QScrollArea

Ответы [ 2 ]

2 голосов
/ 02 августа 2020

Когда вы добавляете виджет в макет, этот виджет является дочерним по отношению к контейнеру (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 соблюдаются.

0 голосов
/ 02 августа 2020

Установка родителя не является «окончательной».

From QLayout.addWidget() (и повторные реализации всех подклассов QLayout):

[.. .] Эта функция использует addItem ().

From QLayout.addItem():

Примечание : право собственности на элемент передается в макет, и макет несет ответственность за его удаление.

Когда макет уже установлен для виджета, право собственности передается виджету макета (то же самое происходит, если макет является новым установлен на виджет).

Кроме того, когда вы используете потомок QAbstractScrollArea (включая, очевидно, QScrollArea), у вас есть QWidget, используемый в качестве «области просмотра» (виджет, который прокручивается внутри области прокрутки), который имеет область прокрутки как родительская. При использовании QScrollArea setWidget(widget) заменяет widget в область просмотра.

Итак, фактическая иерархия:

scrollarea
   viewport
       "container" widget
           button

Если вы ищете для родительской области прокрутки (при условии, что вы не использовали вложенные области прокрутки, включая потомков представлений элементов), вы можете go примерно так:

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)
...