Заменить виджет во время выполнения в Qt5 - PullRequest
0 голосов
/ 13 марта 2020

У меня есть этот Qt5 GUI, который я делаю в python3 (. 8). Он имеет QButtonGroup на левой стороне. Нажатие одной из этих кнопок должно отобразить связанный класс устройства QWidget, который сделан (не показан здесь) с правой стороны. Таким образом, в основном макете присутствуют только 2 виджета.

Этот код выполняется. Я знаю, что в device_clicked функциональность на самом деле не имеет смысла, но это просто для того, чтобы продемонстрировать небольшой фрагмент рабочего кода.

import logging
import sys

from PyQt5.QtWidgets import (QApplication, QButtonGroup, QHBoxLayout, QTabBar, QTabWidget,
                             QMainWindow, QPushButton, QVBoxLayout, QWidget, QLabel)

logging.basicConfig(
    format="%(asctime)s,%(msecs)d - %(name)s - %(levelname)s: %(message)s",
    datefmt="%H:%M:%S",
    level=logging.INFO,
)

DEVICES = [("DEV1", QLabel), ("2222", QLabel), ("HUPP", QLabel), ("FOOO", QLabel), ("BOOO", QLabel)]


class MainApp(QMainWindow):
    """Documentation for MainApp(QMainWindow)

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

        self.logger = logging.getLogger("MAIN")
        self.logger.info("Starting main program")

        self.title = "Title"
        self.left = 300
        self.top = 200
        self.width = 1100
        self.height = 600

        self.init_ui()

    def init_ui(self) -> None:
        """
        Initializs the UI
        """
        self.logger.info("Starting UI")

        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.main_widget = QWidget()
        self.setCentralWidget(self.main_widget)

        layout = QHBoxLayout()
        self.main_widget.setLayout(layout)

        dev_layout = QVBoxLayout()
        self.devices = QButtonGroup()
        for i, (t, w) in enumerate(DEVICES):
            btn = QPushButton(t)
            self.devices.addButton(btn, i)
            dev_layout.addWidget(btn)

        self.devices.buttonClicked[int].connect(self.device_clicked)

        layout.addLayout(dev_layout, 10)
        layout.addWidget(QLabel("test"), 90)
        self.prev_device_id = 0
        self.devices.button(0).setDown(True)

    def device_clicked(self, btn_id: int) -> None:
        self.logger.info(f"BTN {btn_id} clicked")
        self.devices.button(self.prev_device_id).setDown(False)
        self.devices.button(btn_id).setDown(True)

        # replace gives my an AttributeError: 'QHBoxLayout' object has no attribute 'replace'
        # self.main_widget.layout().replace(DEVICES[self.prev_device_id][1], DEVICES[btn_id][1])

        self.main_widget.layout().takeAt(1)
        self.main_widget.layout().addWidget(DEVICES[btn_id][1](DEVICES[btn_id][0]), 90)
        self.main_widget.layout().update()

        self.prev_device_id = btn_id

if __name__ == "__main__":
    app = QApplication(sys.argv)
    main = MainApp()
    main.show()
    app.exec_()

У меня сейчас проблема в том, что старый виджет не удаляется, и новый виджет помещается поверх него, отображая их одновременно.

Это потому, что MainApp является QMainWindow и имеет self.main_widget = QWidget() centralWidget? Рекомендуется ли работать с setCentralWidget()?

1 Ответ

1 голос
/ 13 марта 2020

takeAt() только удаляет элемент макета из макета, но не удаляет его виджет. Обратите внимание, что я указал элемент макета , поскольку макеты используют QLayoutItems , которые являются абстрактными элементами, которые макет использует для управления его содержимым: элемент макета может содержать сам макет или виджет.

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

    layoutItem = self.main_widget.layout().itemAt(1)
    if layoutItem.widget():
        layoutItem.widget().deleteLater()

или, если у вас уже есть ссылка на виджет:

    self.someWidget.deleteLater()
...