【问题标题】:enable or disable QDialogButtonBox based on the text of multiple QComboBox根据多个 QComboBox 的文本启用或禁用 QDialogBu​​ttonBox
【发布时间】:2019-12-04 17:44:56
【问题描述】:

我想根据两个 QComboBox(es) 的选定文本启用或禁用 QDialogBu​​ttonBox,或者最好只启用 QDialog 中的 OK 按钮。

我的例子如下。它目前不工作,并且在启用或禁用 QDialogBu​​ttonBox 时,两个 ComboBox 相互独立工作。

import sys

from PyQt5.QtCore import QSignalMapper, pyqtSlot
from PyQt5.QtWidgets import (QGroupBox, QFormLayout, QLabel, QComboBox,
                             QApplication, QDialog, QDialogButtonBox,
                             QVBoxLayout)


class SheetColumns(QDialog):
    def __init__(self, column_header):
        super().__init__()

        self.setMinimumWidth(300)
        self.setWindowTitle("Input Column Names")

        self.column_headers = column_header

        self.column_headers.insert(0, ' ')
        self.setWhatsThis('Please match columns in your data sheet names'
                          ' with the right side labels')

        col_names = ["Student Name:", "Student ID:", "Cohort:", "Gender:",
                     "College:", "Department:", "Major:", "Minor", "Email:",
                     "Adviser", "Adviser Email"]
        self.form_group_box = QGroupBox("Specify Column Names")
        self.layout = QFormLayout()
        for col_name in col_names:
            combo = QComboBox()
            combo.addItems(self.column_headers)
            self.layout.addRow(QLabel(col_name), combo)
        self.form_group_box.setLayout(self.layout)

        self.button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel)

        self.button_box.setEnabled(False)

        self.layout.itemAt(0, 1).widget().currentTextChanged.connect(
            self.check_validity)
        self.layout.itemAt(1, 1).widget().currentTextChanged.connect(
            self.check_validity)

        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.form_group_box)
        main_layout.addWidget(self.button_box)
        self.setLayout(main_layout)

    def check_validity(self, text):
        print(text)
        if text == ' ':
            self.button_box.setEnabled(False)
        else:
            self.button_box.setEnabled(True)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    dialog = SheetColumns(['name student', 'id', 'cohort', 'test 1'])
    sys.exit(dialog.exec_())

我希望当两个 ComboBox 中的 currentText(s) 不是 ' ' 时启用 QDialogBu​​ttonBox,而当它们都是 ' ' 时它被禁用。

我尝试使用QSignalMapper

但是,我无法让它工作。

class SheetColumns(QDialog):
    def __init__(self, column_header):
        super().__init__()

        self.setMinimumWidth(300)
        self.setWindowTitle("Input Column Names")

        self.column_headers = column_header

        self.column_headers.insert(0, ' ')
        self.setWhatsThis('Please match columns in your data sheet names'
                          ' with the right side labels')

        col_names = ["Student Name:", "Student ID:", "Cohort:", "Gender:",
                     "College:", "Department:", "Major:", "Minor", "Email:",
                     "Adviser", "Adviser Email"]
        self.form_group_box = QGroupBox("Specify Column Names")
        self.layout = QFormLayout()
        for col_name in col_names:
            combo = QComboBox()
            combo.addItems(self.column_headers)
            self.layout.addRow(QLabel(col_name), combo)
        self.form_group_box.setLayout(self.layout)

        self.button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel)

        self.button_box.setEnabled(False)

        self.mapper = QSignalMapper(self)

        comb_bx1 = self.layout.itemAt(0, 1).widget()
        comb_bx2 = self.layout.itemAt(1, 1).widget()

        comb_bx1.currentTextChanged.connect(self.mapper.map)
        comb_bx2.currentTextChanged.connect(self.mapper.map)

        self.mapper.setMapping(comb_bx1, comb_bx1.currentText())
        self.mapper.setMapping(comb_bx2, comb_bx2.currentText())

        self.mapper.mapped.connect(self.check_validity)

        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.form_group_box)
        main_layout.addWidget(self.button_box)
        self.setLayout(main_layout)

    def check_validity(self, text):
        print(text)
        if text == ' ':
            self.button_box.setEnabled(False)
        else:
            self.button_box.setEnabled(True)

谁能告诉我我做错了什么,或者有更好的方法吗?

提前致谢

【问题讨论】:

    标签: python pyqt pyqt5


    【解决方案1】:

    使用 QSignalMapper 超出了您的要求,在您的情况下,您只需遍历 QComboBox 并验证它们没有适当的选项,并根据该选项启用按钮,如下所示:

    class SheetColumns(QDialog):
        def __init__(self, column_header, parent=None):
            super().__init__(parent)
    
            self.setMinimumWidth(300)
            self.setWindowTitle("Input Column Names")
    
            self.column_headers = column_header
    
            self.setWhatsThis(
                "Please match columns in your data sheet names"
                " with the right side labels"
            )
    
            col_names = [
                "Student Name:",
                "Student ID:",
                "Cohort:",
                "Gender:",
                "College:",
                "Department:",
                "Major:",
                "Minor",
                "Email:",
                "Adviser",
                "Adviser Email",
            ]
    
            self.combos = []
    
            flay = QFormLayout()
            for i, col_name in enumerate(col_names):
                combo = QComboBox()
                combo.addItems([""] + self.column_headers)
                flay.addRow(col_name, combo)
                if i in (0, 1):
                    combo.currentIndexChanged.connect(self.check_validity)
                    self.combos.append(combo)
    
            self.form_group_box = QGroupBox("Specify Column Names")
            self.form_group_box.setLayout(flay)
    
            self.button_box = QDialogButtonBox(
                QDialogButtonBox.Ok | QDialogButtonBox.Cancel
            )
            self.button_box.accepted.connect(self.accept)
            self.button_box.rejected.connect(self.reject)
    
            main_layout = QVBoxLayout(self)
            main_layout.addWidget(self.form_group_box)
            main_layout.addWidget(self.button_box)
    
        @pyqtSlot()
        def check_validity(self):
            is_enabled = True
            for combo in self.combos:
                if not combo.currentText():
                    is_enabled = False
                    break
            button = self.button_box.button(QDialogButtonBox.Ok)
            button.setEnabled(is_enabled)
    

    @TheKewlStore 在他的回答中指出了一些不正确的地方:QObjects 的所有权不是由 Python 而是由 C++ 部分处理的。 python中QObject的所有权具有创建它的类,如果它是一个属性,在OP的示例中不满足,或者具有另一个QObject,它是在创建它或使用setParent时设置的父对象() 方法。对于 QWidget,当您将其添加到布局时,它会将其管理的小部件设置为父级。在您的情况下,QComboBoxes 的所有权是 QGroupBox,因此 GC 没有问题。

    【讨论】:

      【解决方案2】:

      就个人而言,我发现信号映射器非常令人困惑,并像避免瘟疫一样避免它。这是我将如何做到这一点(将使用一个虚拟类让生活更轻松):

      class DummyDialog(QDialog):
          def __init__(self):
              self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
              self.combo_box_1 = QComboBox()
              self.combo_box_2 = QComboBox()
              self.combo_1_text = ""  # This could also be self.combo_box_1.text()
              self.combo_2_text = ""
              self.combo_box_1.currentTextChanged.connect(self.combo_one_changed)
              self.combo_box_2.currentTextChanged.connect(self.combo_two_changed)
      
          def combo_one_changed(self, text):
              self.combo_1_text = text
      
          def combo_two_changed(self, text):
              self.combo_2_text = text
      
          def check_validity(self):
              if self.combo_1_text and self.combo_2_text:
                  self.button_box.setEnabled(True)
              else:
                  self.button_box.setEnabled(False)
      

      如果您对此有任何问题,请告诉我,我只是很快想到了这个。

      编辑:我还注意到您在 init 方法中将组合框定义为本地人,由于 pyqt 的所有权,这也可能是一个问题。如果您不存储对对象的引用,则不能保证它会继续存在,并且 python 可能会垃圾收集它,这意味着您可能会完全失去信号连接。不能说这是正在发生的事情,但一般来说,通过将它们设置为实例变量,在 init 方法中为它们保留一个实例会更安全。

      【讨论】:

      • 您的“编辑”不正确,因为当您将小部件添加到布局时,小部件的所有权将由设置小部件的小部件取得,在您的情况下,QComboBox 的所有权将是由 QGroupBox 采取,因此 GC 没有问题。
      • 啊,没错,我没有注意到它们是布局的一部分。
      【解决方案3】:

      感谢@eyllanesc 他的回答,效果很好。

      下面的代码更适合我的用例,和@eyllanesc按照他的概念给出的答案非常相似。

      class SheetColumns(QDialog):
          def __init__(self, column_header, parent=None):
              super().__init__(parent)
      
              self.setMinimumWidth(300)
              self.setWindowTitle("Input Column Names")
      
              self.column_headers = column_header
      
              self.setWhatsThis(
                  "Please match columns in your datasheet names"
                  " with the right side labels"
              )
      
              col_names = [
                  "Student Name:",
                  "Student ID:",
                  "Cohort:",
                  "Gender:",
                  "College:",
                  "Department:",
                  "Major:",
                  "Minor",
                  "Email:",
                  "Adviser",
                  "Adviser Email",
              ]
      
              self.combos = []
      
              flay = QFormLayout()
              for i, col_name in enumerate(col_names):
                  combo = QComboBox()
                  combo.addItems([""] + self.column_headers)
                  flay.addRow(col_name, combo)
                  if i in (0, 1):
                      combo.currentIndexChanged.connect(self.check_validity)
                      self.combos.append(combo)
      
              self.form_group_box = QGroupBox("Specify Column Names")
              self.form_group_box.setLayout(flay)
      
              self.button_box = QDialogButtonBox(
                  QDialogButtonBox.Ok | QDialogButtonBox.Cancel
              )
              self.button = self.button_box.button(QDialogButtonBox.Ok)
              self.button.setEnabled(False)
              self.button_box.accepted.connect(self.accept)
              self.button_box.rejected.connect(self.reject)
      
              self.label = QLabel()
              self.label.setText('Student Name and ID cannot be empty')
      
              main_layout = QVBoxLayout(self)
              main_layout.addWidget(self.form_group_box)
              main_layout.addWidget(self.label)
              main_layout.addWidget(self.button_box)
      
          @pyqtSlot()
          def check_validity(self):
              if all([combo.currentText() for combo in self.combos]):
                  self.button.setEnabled(True)
                  self.label.setText('')
              else:
                  self.button.setEnabled(False)
                  self.label.setText('Student Name and ID cannot be empty')
      

      【讨论】:

      • self.label.setText('')改成self.label.clear(),可读性更强。
      猜你喜欢
      • 2022-10-12
      • 1970-01-01
      • 2015-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多