如何在 for 循环中强制执行多个 QComboBox 中任何一个的最小索引? [英] How do I enforce the minimum index of any one of multiple QComboBoxes within a for loop?
问题描述
我有一个 QWizard,它可以读取 CSV 文件的列标题并让用户选择他们想要的每一列.在此向导的第二页中,我将组合框添加到循环 CSV 的列名的 for 循环中.所有组合框都是必填字段.但是,另一个限制是必须至少选择其中一个来选择 3 或以上(在我的 MWE 中为 c1 或 c2),然后用户才能按下一步".
I have a QWizard that reads the column headers of a CSV file and makes the user choose what they want with each column. In the 2nd page of this wizard, I add the combo-boxes in a for loop that loops over the column names of the CSV. All combo-boxes are mandatory fields. However, another constraint is that at least one of them must be selected to choice 3 or above (c1 or c2 in my MWE), before the user can press "Next".
除了self.NextButton.setEnabled(False)
,我还尝试使用isComplete
和completeChanged
根据this 示例和 this 问题,但作为 PyQt 的初学者并且不太擅长 C++,我没有'无法理解现有文档的大部分内容.现在 isComplete
似乎没问题,但向导没有注册它.
In addition to self.NextButton.setEnabled(False)
, I also tried using isComplete
and completeChanged
according to this example and this question, but being a beginner to PyQt and not so good at C++, I haven't been able to understand that much of the existing documentation. For now isComplete
seems to be fine but the wizard isn't registering it.
谁能教我如何实现我想要的(粗体文本)?
Can anyone teach me how I would be able to achieve what I wanted (the text in bold)?
from PyQt5 import QtGui, QtWidgets, QtCore
import csv
class ChooseFile(QtWidgets.QWizardPage):
def __init__(self, parent=None):
super(ChooseFile, self).__init__(parent)
body = QtWidgets.QVBoxLayout()
self.filePathShow = QtWidgets.QLineEdit(self)
self.filePathShow.setReadOnly(True) # not editable
self.registerField("filePathShow*", self.filePathShow) # mandatory
body.addWidget(self.filePathShow)
browseButton = QtWidgets.QPushButton('Browse...', self)
browseButton.clicked.connect(self.browseDialog)
browseBox = QtWidgets.QHBoxLayout()
browseBox.addWidget(browseButton)
body.addLayout(browseBox)
self.setLayout(body)
def browseDialog(self):
filePath, _ = QtWidgets.QFileDialog.getOpenFileName(self, 'Open CSV', '/home', '(*.csv)')
if filePath: # only changes if valid file, stays the same if user presses cancel
self.setField("filePathShow", filePath)
class ChooseColumns(QtWidgets.QWizardPage):
def __init__(self, parent=None):
super(ChooseColumns, self).__init__(parent)
self.box = QtWidgets.QGroupBox()
body = QtWidgets.QVBoxLayout()
body.addWidget(self.box) # these are where the choices (comboboxes) go
self.setLayout(body)
def initializePage(self):
filePath2 = self.field("filePathShow")
with open(str(filePath2), 'r') as f:
reader = csv.reader(f)
self.columns = next(reader)
# make a combobox for each column
grid = QtWidgets.QGridLayout()
self.comboBoxes = [None] * len(self.columns)
for i, col in enumerate(self.columns):
grid.addWidget(QtWidgets.QLabel(col), i, 0) # printscolumn name
self.comboBoxes[i] = QtWidgets.QComboBox()
self.comboBoxes[i].addItem("") # default value since is mandatory field
self.comboBoxes[i].addItem("a")
self.comboBoxes[i].addItem("b")
self.comboBoxes[i].addItem("c1")
self.comboBoxes[i].addItem("c2")
grid.addWidget(self.comboBoxes[i], i, 1)
self.registerField("column" + str(i) + "*", self.comboBoxes[i]) # all mandatory
self.comboBoxes[i].currentIndexChanged.connect(self.isComplete)
#self.connect(self.comboBoxes[i], QtCore.SIGNAL(currentIndexChanged()),
# self, QtCore.SIGNAL(completeChanged()))
self.comboBoxes[i].currentIndexChanged.connect(self.completeChanged) # DOESN'T WORK
self.box.setLayout(grid)
def isComplete(self, other): # WORKS
self.selections = [None] * len(self.columns)
for i in range(len(self.selections)): # first fill the list
self.selections[i] = self.comboBoxes[i].currentIndex()
#print(self.selections)
for item in self.selections: # then evaluate the values
if i >= 3:
return True
return False
class Manager(QtWidgets.QWizard):
def __init__(self, parent=None):
super(Manager, self).__init__(parent)
self.resize(500, 300)
self.addPage(ChooseFile(self))
self.addPage(ChooseColumns(self))
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Manager()
w.show()
sys.exit(app.exec_())
推荐答案
isComplete 信号必须返回一个布尔值,指示它是否可以前进到下一页或终止进程.此方法不应直接调用,而应通过 completeChanged 信号调用.
The isComplete signal must return a Boolean that indicates whether it can advance to the next page or terminate the process. This method should not be invoked directly but through the completeChanged signal.
这种情况比较特殊,因为QComboBox的数量是可变的,所以如果用户返回上一页并选择另一个.csv,QComboBox的数量应该改变并注册将是一个问题,所以在这种情况下我会不做但是直接用isComplete处理.
This case is special since the amount of QComboBox is variable, so if the user goes back to the previous page and selects another .csv, the amount of QComboBox should be changed and registered would be a problem so in this case I will not do it but It is directly handled using isComplete.
最后,由于目标(我想)是获取选定的值,所以我将使用 QWizard 的一个属性来存储该信息,并能够在我为测试添加的最后一页上获取它.
Finally, as the objective (I suppose) is to obtain the selected values then I will use a property of the QWizard to store that information and be able to obtain it on the last page that I added for my test.
import csv
from PyQt5 import QtGui, QtWidgets, QtCore
class ChooseFile(QtWidgets.QWizardPage):
def __init__(self, parent=None):
super(ChooseFile, self).__init__(parent)
body = QtWidgets.QVBoxLayout(self)
self.filePathShow = QtWidgets.QLineEdit(self)
self.filePathShow.setReadOnly(True) # not editable
self.registerField("filePathShow*", self.filePathShow) # mandatory
body.addWidget(self.filePathShow)
browseButton = QtWidgets.QPushButton("Browse...", self)
browseButton.clicked.connect(self.browseDialog)
browseBox = QtWidgets.QHBoxLayout()
browseBox.addWidget(browseButton)
body.addLayout(browseBox)
def browseDialog(self):
filePath, _ = QtWidgets.QFileDialog.getOpenFileName(
self, "Open CSV", "/home", "(*.csv)"
)
if filePath:
self.setField("filePathShow", filePath)
class ChooseColumns(QtWidgets.QWizardPage):
def __init__(self, parent=None):
super(ChooseColumns, self).__init__(parent)
self.comboboxes = []
box = QtWidgets.QGroupBox()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(box)
self.flay = QtWidgets.QFormLayout()
box.setLayout(self.flay)
def initializePage(self):
for combo in self.comboboxes:
self.flay.removeRow(combo)
self.comboboxes = []
self.wizard().setProperty("indexes_selected", [])
self.wizard().setProperty("options_selected", [])
filePath2 = self.field("filePathShow")
options = ("", "a", "b", "c1", "c2")
with open(filePath2, "r") as f:
reader = csv.reader(f)
header = next(reader)
for i, text in enumerate(header):
combo = QtWidgets.QComboBox()
combo.addItems(options)
combo.currentIndexChanged.connect(self.completeChanged)
self.flay.addRow(text, combo)
self.comboboxes.append(combo)
def isComplete(self):
indexes = [combo.currentIndex() for combo in self.comboboxes]
is_completed = all(index >= 1 for index in indexes) and any(
index >= 3 for index in indexes
)
self.wizard().setProperty("indexes_selected", indexes)
self.wizard().setProperty(
"options_selected", [combo.currentText() for combo in self.comboboxes]
)
return is_completed
class FinalPage(QtWidgets.QWizardPage):
def initializePage(self):
print(self.wizard().property("indexes_selected"))
print(self.wizard().property("options_selected"))
class Manager(QtWidgets.QWizard):
def __init__(self, parent=None):
super(Manager, self).__init__(parent)
self.resize(500, 300)
self.addPage(ChooseFile())
self.addPage(ChooseColumns())
self.addPage(FinalPage())
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Manager()
w.show()
sys.exit(app.exec_())
这篇关于如何在 for 循环中强制执行多个 QComboBox 中任何一个的最小索引?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!