PyQt 根据 QComboBox 数据在 QSpinBox 和 QTimeEdit 小部件之间动态切换

PyQt dynamically switch between QSpinBox to QTimeEdit widgets depending on QComboBox data

I have a combo box with 2 options 'time' or 'interval' when 'time' is selected I would like to show a QTimeEdit and When 'interval is selected I would like to show一个 QSpinBox。

我可以隐藏间隔小部件并显示时间小部件,但我不知道如何重新定位它以使其显示在间隔小部件所在的位置。

这是我目前的情况:

import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtGui as qtg
from PyQt5 import QtCore as qtc

class MainWindow(qtw.QMainWindow):

    def __init__(self):
        super().__init__()
        
        form = qtw.QWidget()
        self.setCentralWidget(form)
        layout = qtw.QFormLayout()
        form.setLayout(layout)
        
        self.when_list = qtw.QComboBox(currentIndexChanged=self.on_change)
        self.when_list.addItem('Every X Minutes', 'interval')
        self.when_list.addItem('At a specific time', 'time')
        self.interval_box = qtw.QSpinBox()
        self.time_edit = qtw.QTimeEdit()
        self.event_list = qtw.QComboBox()
        self.event_list.addItem('Event 1')
        self.event_list.addItem('Event 2')
        self.event_msg = qtw.QLineEdit()
        self.add_button = qtw.QPushButton('Add Event', clicked=self.add_event)

        layout.addRow(self.when_list, self.interval_box)
        layout.addRow(self.event_list)
        layout.addRow(self.event_msg)
        layout.addRow(self.add_button)

        self.show()

    def on_change(self):
        if self.when_list.currentData() == 'time':
            # Hide interval
            self.interval_box.hide()

            # Show time - how do I put this where interval_box was?
            self.time_edit.show()
        elif self.when_list.currentData() == 'interval':
            # Hide time - ERROR object has no attribute time_edit
            self.time_edit.hide()

            # show interval - ERROR object has no attribute interval_box
            self.interval_box.show()

    def add_event(self):
        pass

if __name__ == '__main__':
    app = qtw.QApplication(sys.argv)
    mw = MainWindow()
    sys.exit(app.exec())

如何修复错误并在小部件之间动态切换?

您可以使用 QStackedWidget(类似于选项卡小部件,但没有选项卡)并使用组合框信号 select 显示哪个小部件,而不是隐藏和显示小部件。

请注意,如果您要设置可以调用该信号的属性并且插槽使用尚不存在的对象,则不应在构造函数中连接到 *changed 信号:在您的情况下您在构造函数中连接了 currentIndexChanged 信号,但是当一个项目被添加到先前为空的组合框时,该信号总是被调用,并且由于此时尚未创建 time_edit 对象,您将添加第一项后立即收到 AttributeError。

虽然在构造函数中使用信号连接很有用,但必须始终小心使用。

class MainWindow(qtw.QMainWindow):

    def __init__(self):
        # ...
        self.when_list = qtw.QComboBox()
        self.when_list.addItem('Every X Minutes', 'interval')
        self.when_list.addItem('At a specific time', 'time')
        self.when_list.currentIndexChanged.connect(self.on_change)
        # ...
        self.time_stack = qtw.QStackedWidget()
        self.time_stack.addWidget(self.interval_box)
        self.time_stack.addWidget(self.time_edit)
        layout.addRow(self.when_list, self.time_stack)
        # ...

    def on_change(self):
        if self.when_list.currentData() == 'time':
            self.time_stack.setCurrentWidget(self.time_edit)
        elif self.when_list.currentData() == 'interval':
            self.time_stack.setCurrentWidget(self.interval_box)

另一种解决方案是删除要隐藏的小部件并使用 QFormLayout 函数在同一位置插入一行,但是虽然该布局在许多情况下都很有用,但它主要用于漂亮的“静态”界面。另一种方法是使用 QGridLayout,它允许在同一个“单元格”上设置更多小部件:在这种情况下,您可以轻松切换项目的可见性,但它可能会产生一些问题,因为布局也会尝试每次调整其内容(可以通过对小部件的大小策略使用 setRetainSizeWhenHidden() 来解决。