简体   繁体   English

在 Qt5 中在运行时替换小部件

[英]Replace a widget at runtime in Qt5

I have this Qt5 GUI that I'm making in python3(.8).我有我在 python3(.8) 中制作的这个 Qt5 GUI。 It has a QButtonGroup on the left side.它在左侧有一个QButtonGroup Pressing one of these buttons should present the associated device QWidget class that is made (not shown here) on the right side.按这些按钮之一应该会在右侧显示关联的设备 QWidget 类(此处未显示)。 So only 2 widgets are present in the main layout.所以主布局中只有 2 个小部件。

This code runs.此代码运行。 I know the functionality doens't really make sense in device_clicked but it's just for having a small working code snippet to demonstrate.我知道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_()

The problem I have now, is that the old widget isn't deleted and the new widget is placed over it, displaying them both at the same time.我现在的问题是旧的小部件没有被删除,新的小部件被放置在它上面,同时显示它们。

Is it because the MainApp is a QMainWindow and has a self.main_widget = QWidget() centralWidget?是不是因为MainApp是一个QMainWindow并且有一个self.main_widget = QWidget() centralWidget? Is it recommended to work with setCentralWidget() ?是否建议使用setCentralWidget()

takeAt() only removes the layout item from the layout, it doesn't delete its widget. takeAt()仅从布局中删除布局项,不会删除其小部件。 Note that I specified layout item , because layouts use QLayoutItems , which are abstract items that the layout uses to manage its contents: a layout item could contain a layout itself, or a widget.请注意,我指定了layout item ,因为布局使用QLayoutItems ,它们是布局用来管理其内容的抽象项:布局项可以包含布局本身或小部件。

In any case, after removing the widget (either by removing the layout item or the widget itself by using removeWidget() ), you also have to call deleteLater() .在任何情况下,取出后,小部件(删除布局项目或使用小部件本身或者removeWidget()你也必须调用deleteLater()
This is necessary because, even after removing a widget from a layout, the widget will still have a parent set (the widget on which the layout was set).这是必要的,因为即使从布局中删除小部件后,小部件仍将具有父集(设置布局的小部件)。

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

or, if you already have the reference to the widget:或者,如果您已经拥有对小部件的引用:

    self.someWidget.deleteLater()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM