根据多个 QComboBox 的文本启用或禁用 QDialogButtonBox
Posted
技术标签:
【中文标题】根据多个 QComboBox 的文本启用或禁用 QDialogButtonBox【英文标题】:enable or disable QDialogButtonBox based on the text of multiple QComboBox 【发布时间】:2019-12-04 17:44:56 【问题描述】:我想根据两个 QComboBox(es) 的选定文本启用或禁用 QDialogButtonBox,或者最好只启用 QDialog 中的 OK 按钮。
我的例子如下。它目前不工作,并且在启用或禁用 QDialogButtonBox 时,两个 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) 不是 ' ' 时启用 QDialogButtonBox,而当它们都是 ' ' 时它被禁用。
我尝试使用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)
谁能告诉我我做错了什么,或者有更好的方法吗?
提前致谢
【问题讨论】:
【参考方案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()
,可读性更强。以上是关于根据多个 QComboBox 的文本启用或禁用 QDialogButtonBox的主要内容,如果未能解决你的问题,请参考以下文章
Oracle Apex 根据 LOV 选择的值动态启用/禁用文本字段