Почему виджет с абсолютным позиционированием не отображается?

Используя Qt5, я пытаюсь заставить виджет работать, используя абсолютное позиционирование. Приведенный ниже код является минимальным рабочим примером того, что я пытаюсь сделать. Краткий обзор кода:

  • CentralWidget является центральным виджетом главного окна и содержит MyWidget с использованием абсолютного позиционирования, например. без использования каких-либо макетов.
  • MyWidget не устанавливает свои дочерние виджеты сразу, а предоставляет метод SetData, который сначала удаляет все текущие дочерние виджеты, а затем добавляет новые дочерние виджеты в свой макет.
  • SetData запускается с помощью таймера в главном окне.

Я закомментировал две строчки кода. Первый «включает» относительное позиционирование с использованием макетов, добавляя макет к CentralWidget. Эта строка показывает, чего я пытаюсь достичь, но с абсолютным позиционированием. Второй комментарий включает некоторую отладочную информацию:

MyWidget
  layout.count: 3
  size: PyQt5.QtCore.QSize(-1, -1)
  sizeHint: PyQt5.QtCore.QSize(200, 100)

CentralWidget
  size: PyQt5.QtCore.QSize(18, 18)
  sizeHint: PyQt5.QtCore.QSize(18, 18)

Что я делаю неправильно, чтобы MyWidget было видно с использованием абсолютного позиционирования?

Код:

from PyQt5 import QtCore, QtWidgets
import sys


class MyWidget(QtWidgets.QWidget):
    z = 0

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

        self._layout = QtWidgets.QVBoxLayout(self)

    def SetData(self):
        while self._layout.count() > 0:
            widget = self._layout.takeAt(0).widget()
            widget.hide()
            widget.deleteLater()

        for i in range(3):
            self._layout.addWidget(QtWidgets.QLabel(str(MyWidget.z * 10 + i)))

        MyWidget.z += 1


class CentralWidget(QtWidgets.QWidget):
    def __init__(self, parent):
        super().__init__(parent)

        self._myWidget = MyWidget(self)

        # QtWidgets.QHBoxLayout(self).addWidget(self._myWidget)

    def SetData(self):
        self._myWidget.SetData()
        # print("MyWidget\n  layout.count: {}\n  size: {}\n  sizeHint: {}\n\nCentralWidget\n  size: {}\n  sizeHint: {}\n\n".format(self._myWidget.layout().count(), self.sizeHint(), self.size(), self._myWidget.sizeHint(), self._myWidget.size()))


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        centralWidget = CentralWidget(self)
        self.setCentralWidget(centralWidget)

        self._timer = QtCore.QTimer(self)
        self._timer.timeout.connect(centralWidget.SetData)
        self._timer.start(500)


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

    mainWindow = MainWindow()
    mainWindow.show()

    app.exec_()


if __name__ == "__main__":
    main()


person Woltan    schedule 30.04.2020    source источник
comment
попробуйте заменить строку self._layout = QtWidgets.QVBoxLayout(self) на self._layout = QtWidgets.QVBoxLayout(parent)   -  person S. Nick    schedule 30.04.2020
comment
@ S.Nick В вашем предложении отображаются метки, но это не то, что мне нужно. Ваше предложение добавляет макет к CentralWidget и, как следствие, не использует абсолютное позиционирование для меток.   -  person Woltan    schedule 30.04.2020
comment
Если я правильно помню, вы должны явно вызвать self._myWidget.show() после его создания, если вы не используете макеты.   -  person titusjan    schedule 30.04.2020
comment
@titusjan Нет, вызов self._myWidget.show() не отображает виджет в окне :/.   -  person Woltan    schedule 30.04.2020


Ответы (1)


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

На самом деле, если вы вызовете centralWidget.SetData() при инициализации и до mainWindow.show(), все будет работать так, как ожидалось.

Когда вы добавляете дочерний виджет в макет, происходит много вещей, и это обычно включает в себя несколько вызовов дочерних подсказок размера, позволяя родителю адаптировать свою собственную подсказку размера и, после этого, адаптировать его размер и размер его детей.

Если этот «виджет-контейнер» сам содержится в другом макете, размер этого виджета будет автоматически изменен (на основе его подсказки) в следующем цикле событий, но в вашем случае этого не происходит, поскольку ваш виджет «бесплатный». .

Функция, которую вы ищете, это QWidget.adjustSize(), но для вышеупомянутого причинам вы не можете вызвать его сразу после добавления дочерних виджетов.
Чтобы решить вашу проблему, вы можете вызвать QApplication.processEvents() перед adjustSize() или, в конечном счете, использовать одиночный QTimer с отсчетом от 0:

    def SetData(self):
        while self._layout.count() > 0:
            widget = self._layout.takeAt(0).widget()
            widget.hide()
            widget.deleteLater()

        for i in range(3):
            self._layout.addWidget(QtWidgets.QLabel(str(MyWidget.z * 10 + i)))

        MyWidget.z += 1

        QtCore.QTimer.singleShot(0, self.adjustSize)
person musicamante    schedule 30.04.2020
comment
Спасибо за ваш ответ. Я уже экспериментировал с adjustSize, но получил странные результаты, потому что не поставил таймер и не вызвал processEvents. У вас есть ссылка на документ, в котором более подробно описано много вещей, которые происходят, когда вы добавляете дочерний виджет в макет? - person Woltan; 01.05.2020
comment
@Woltan, к сожалению, нет. Я знаю это только по опыту и изучая исходный код виджетов и макетов. - person musicamante; 03.05.2020