PyQt6 запутался в размещении макетов или виджетов
Я пытаюсь понять, как работают вложения макетов и виджетов, поэтому я вынул все аргументы инициализатора и вместо этого использовал .setWhatever, .setParent, .setText и так далее. Это работало, за исключением второго и третьего виджетов QtWidgets.QVBoxLayout; для первого он устанавливает своего родителя на frame_main, а для второго и третьего он устанавливает его на frame_sensor. Оба этих виджета frame_ относятся к типу QtWidgets.QFrame. Когда второй и третий виджеты QVBoxLayout используют setParent (frame_sensor), он выдает сообщение «У макета может быть только другой макет в качестве родительского», но если я создаю экземпляр этого виджета макета с помощью QtWidgets.QVBoxLayout (frame_sensor), жалоб не будет. Может ли кто-нибудь объяснить, почему иногда я могу, а иногда и не могу использовать .setParent или что не так?
import sys
import time
from PyQt6 import QtGui
from PyQt6 import QtWidgets
from PyQt6 import QtCore
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.widgets = { }
self.frame_main = QtWidgets.QFrame()
self.frame_main.setParent(self)
self.frame_main.setObjectName("frame_main")
self.layout_top = QtWidgets.QVBoxLayout()
self.layout_top.setParent(self.frame_main)
self.layout_top.setObjectName("layout_top")
self.frame_main.setLayout(self.layout_top)
self.setCentralWidget(self.frame_main)
self.show()
self.add_sensor("sensor1", 123)
self.add_sensor("sensor2", 234)
def add_sensor(self, sensor, value):
label_title = QtWidgets.QLabel()
label_title.setText(sensor)
label_title.setObjectName("label_title")
label_value = QtWidgets.QLabel()
label_value.setText("{:.1f}".format(float(value)))
label_value.setObjectName("label_value")
frame_sensor = QtWidgets.QFrame()
frame_sensor.setObjectName("frame_sensor")
frame_sensor.setStyleSheet("border: 2px solid;")
# also makes frame_sensor the child of frame_main
self.layout_top.addWidget(frame_sensor)
# this works
#layout_vbox = QtWidgets.QVBoxLayout(frame_sensor)
# this doesn't work
layout_vbox = QtWidgets.QVBoxLayout()
layout_vbox.setParent(frame_sensor)
layout_vbox.setObjectName("layout_vbox")
layout_vbox.addWidget(label_title)
layout_vbox.addWidget(label_value)
self.widgets.update({ sensor : label_value })
def closeEvent(self, event):
event.accept()
sys.exit()
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = MainWindow()
app.exec()
1 ответ
Нет документации, указывающей, что одна из целей состоит в том, чтобы установить виджет, для которого макет будет обрабатывать геометрию своих дочерних элементов. Если вы хотите сделать это, есть 2 варианта:
- Укажите это в конструкторе макета.
layout_vbox = QtWidgets.QVBoxLayout(frame_sensor)
- Или используйте метод setLayout QWidget.
layout_vbox = QtWidgets.QVBoxLayout()
frame_sensor.setLayout(layout_vbox)
Цель состоит в том, чтобы установить иерархию между объектами QObject, чтобы иметь возможность обрабатывать память дочерних элементов (владение), поскольку Qt реализует создание объектов в куче. Таким образом, логика заключается в том, что если отца удалить из памяти, то сначала он удалит из нее и детей. Таким образом, например, окно удаляется, все компоненты окна также будут удалены, даже если они не являются его прямыми дочерними элементами.
Из вышеизложенного можно сделать вывод, что целью являются объекты QObject, но, используя эту логику, QWidget также использует эту иерархию для рисования.
Еще один аргумент в пользу моего ответа заключается в том, что
setParent()
метод ожидает QObject, а не QWidget: весь QWidget является QObject, но не весь QObject является QWidget-