PyQT5 - 当 ComboBox 未选中时更改其他单元格的值
PyQT5 - Change value of other cells when ComboBox is checked of Unchecked
我正在使用 QTableView 显示 Pandas.Thank 来自 Whosebug 的代码,我可以做到。
我想在选中或取消选中 ComboBox 的某些值时更新 ComboBox 右侧单元格的值,但我是 PyQt5 的新手,我不知道该怎么做。
当我 select 'A' => 右边的单元格将是 'A',然后我检查 'B' => 右边的单元格将是 'A,B'。如果我 deselect A => 右边的单元格将是 'B'
我一直在寻找,但找不到解决方案。
我真的需要你的帮助。
这就是我想要做的。
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QComboBox, QVBoxLayout
from PyQt5.QtCore import Qt
dept_list = ['A','B','C','D','E','F','G','H']
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = data
def data(self, index, role):
if role == Qt.DisplayRole or role == Qt.EditRole:
# See below for the nested-list data structure.
# .row() indexes into the outer list,
# .column() indexes into the sub-list
value = self._data.iloc[index.row(), index.column()]
return str(value)
def rowCount(self, index):
# The length of the outer list.
return self._data.shape[0]
def columnCount(self, index):
# The following takes the first sub-list, and returns
# the length (only works if all rows are an equal length)
return self._data.shape[1]
def flags(self, index):
return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable
def setData(self, index, value, role):
if role == Qt.EditRole:
self._data.iloc[index.row(), index.column()] = value
self._data.iat[index.row(), self._data.shape[1]-1] = value
self.dataChanged.emit(index, index)
return True
def headerData(self, section, orientation, role):
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return str(self._data.columns[section])
if orientation == Qt.Vertical:
return str(self._data.index[section])
class CheckableComboBox(QComboBox):
def __init__(self):
super().__init__()
self._changed = False
self.view().pressed.connect(self.handleItemPressed)
self.checked_item=[]
for i in range(len(dept_list)):
self.addItem(dept_list[i])
self.setItemChecked(i, False)
def setItemChecked(self, index, checked=False):
item = self.model().item(index, self.modelColumn()) # QStandardItem object
if checked:
item.setCheckState(Qt.Checked)
else:
item.setCheckState(Qt.Unchecked)
def handleItemPressed(self, index):
item = self.model().itemFromIndex(index)
if item.checkState() == Qt.Checked:
item.setCheckState(Qt.Unchecked)
if item.text() in self.checked_item:
self.checked_item.remove(item.text())
print(self.checked_item)
else:
item.setCheckState(Qt.Checked)
self.checked_item.append(item.text())
print(self.checked_item)
self._changed = True
self.check_items()
def hidePopup(self):
if not self._changed:
super().hidePopup()
self._changed = False
def item_checked(self, index):
# getting item at index
item = self.model().item(index, 0)
# return true if checked else false
return item.checkState() == Qt.Checked
def check_items(self):
# blank list
checkedItems = []
# traversing the items
for i in range(self.count()):
# if item is checked add it to the list
if self.item_checked(i):
checkedItems.append(i)
# checkedItems.append(self.model().item(i, 0).text())
# call this method
self.update_labels(checkedItems)
# method to update the label
def update_labels(self, item_list):
n = ''
count = 0
# traversing the list
for i in item_list:
# if count value is 0 don't add comma
if count == 0:
n += ' % s' % i
# else value is greater then 0
# add comma
else:
n += ', % s' % i
# increment count
count += 1
# loop
for i in range(self.count()):
# getting label
text_label = self.model().item(i, 0).text()
# default state
if text_label.find('-') >= 0:
text_label = text_label.split('-')[0]
# shows the selected items
item_new_text_label = text_label + ',' + n
# setting text to combo box
self.setItemText(i, item_new_text_label)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.table = QtWidgets.QTableView()
self.combo_box_list=[]
import pandas as pd
df = pd.read_excel(r"E:2021-12 2022 1001.xlsx", skiprows=7)
# data = pd.DataFrame([[1, 9, 2], [1, 0, -1], [3, 5, 2], [3, 3, 2], [5, 8, 9],],
# columns=["A", "B", "C"],
# index=["Row 1", "Row 2", "Row 3", "Row 4", "Row 5"],)
df['Allocation Selection'] = ''
df['Allocation']=''
self.model = TableModel(df)
self.table.setModel(self.model)
# combo = CheckableComboBox()
# for i in range(len(dept_list)):
# combo.addItem(dept_list[i])
# combo.setItemChecked(i, False)
for i in range(df.shape[0]):
combo = CheckableComboBox()
self.combo_box_list.append(combo)
# pix = QPersistentModelIndex(index)
# combo.currentIndexChanged[str].connect(lambda txt, pix=pix: self.tableView.model().setData(QModelIndex(pix), txt))
self.table.setIndexWidget(self.model.index(i, df.shape[1] - 2), combo)
self.setCentralWidget(self.table)
self.setGeometry(600, 100, 400, 200)
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
如果我没理解错的话,您正在使用 QTableView 来显示 pandas 数据框。在这种情况下,它应该非常简单。选择项目时,您需要使用来自 QComboBox 的信号 - 可能 currentIndexChanged()
。您需要将它连接到一个函数,该函数将识别您的 QComboBox 中的哪些项目被选中,并从中构建将在正确位置进入数据框的字符串。更新数据框后,您的 QTableView 应该更新以显示数据框的新单元格。在不了解您的确切设置(可检查的组合框)的情况下,我不能更具体。
编辑:这是一个可行的解决方案。第一步是让组合框知道它在哪一行——在这种情况下,我只是在创建时将行号作为变量传递给组合框。这是一种快速而肮脏的方法 - 最好找到小部件动态所在的 table 行。第二步是定义一个自定义信号,该信号将行和选中的项目列表传递给主 class 中的函数,该函数使用新的选中列表更新该行的数据框。
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QComboBox, QVBoxLayout
from PyQt5.QtCore import Qt, pyqtSignal
dept_list = ['A','B','C','D','E','F','G','H']
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = data
def data(self, index, role):
if role == Qt.DisplayRole or role == Qt.EditRole:
# See below for the nested-list data structure.
# .row() indexes into the outer list,
# .column() indexes into the sub-list
value = self._data.iloc[index.row(), index.column()]
return str(value)
def rowCount(self, index):
# The length of the outer list.
return self._data.shape[0]
def columnCount(self, index):
# The following takes the first sub-list, and returns
# the length (only works if all rows are an equal length)
return self._data.shape[1]
def flags(self, index):
return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable
def setData(self, index, value, role):
if role == Qt.EditRole:
self._data.iloc[index.row(), index.column()] = value
self._data.iat[index.row(), self._data.shape[1]-1] = value
self.dataChanged.emit(index, index)
return True
def headerData(self, section, orientation, role):
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return str(self._data.columns[section])
if orientation == Qt.Vertical:
return str(self._data.index[section])
class CheckableComboBox(QComboBox):
check_changed = pyqtSignal(int, list)
def __init__(self, row):
super().__init__()
self._changed = False
self.view().pressed.connect(self.handleItemPressed)
self.checked_item=[]
self.row = row
for i in range(len(dept_list)):
self.addItem(dept_list[i])
self.setItemChecked(i, False)
def setItemChecked(self, index, checked=False):
item = self.model().item(index, self.modelColumn()) # QStandardItem object
if checked:
item.setCheckState(Qt.Checked)
else:
item.setCheckState(Qt.Unchecked)
def handleItemPressed(self, index):
item = self.model().itemFromIndex(index)
if item.checkState() == Qt.Checked:
item.setCheckState(Qt.Unchecked)
if item.text() in self.checked_item:
self.checked_item.remove(item.text())
print(self.checked_item)
else:
item.setCheckState(Qt.Checked)
self.checked_item.append(item.text())
print(self.checked_item)
self._changed = True
self.check_changed.emit(self.row, self.checked_item)
self.check_items()
def hidePopup(self):
if not self._changed:
super().hidePopup()
self._changed = False
def item_checked(self, index):
# getting item at index
item = self.model().item(index, 0)
# return true if checked else false
return item.checkState() == Qt.Checked
def check_items(self):
# blank list
checkedItems = []
# traversing the items
for i in range(self.count()):
# if item is checked add it to the list
if self.item_checked(i):
checkedItems.append(i)
# checkedItems.append(self.model().item(i, 0).text())
# call this method
self.update_labels(checkedItems)
# method to update the label
def update_labels(self, item_list):
n = ''
count = 0
# traversing the list
for i in item_list:
# if count value is 0 don't add comma
if count == 0:
n += ' % s' % i
# else value is greater then 0
# add comma
else:
n += ', % s' % i
# increment count
count += 1
# loop
for i in range(self.count()):
# getting label
text_label = self.model().item(i, 0).text()
# default state
if text_label.find('-') >= 0:
text_label = text_label.split('-')[0]
# shows the selected items
item_new_text_label = text_label + ',' + n
# setting text to combo box
self.setItemText(i, item_new_text_label)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.table = QtWidgets.QTableView()
self.combo_box_list=[]
import pandas as pd
#df = pd.read_excel(r"E:2021-12 2022 1001.xlsx", skiprows=7)
self.df = pd.DataFrame([[1, 9, 2], [1, 0, -1], [3, 5, 2], [3, 3, 2], [5, 8, 9],],
columns=["A", "B", "C"])
self.df['Allocation Selection'] = ''
self.df['Allocation']=''
self.model = TableModel(self.df)
self.table.setModel(self.model)
for i in range(self.df.shape[0]):
combo = CheckableComboBox(i)
combo.check_changed.connect(self.update_df)
self.combo_box_list.append(combo)
self.table.setIndexWidget(self.model.index(i, self.df.shape[1] - 2), combo)
self.setCentralWidget(self.table)
self.setGeometry(600, 100, 400, 200)
def update_df(self, row, checked):
print(row, checked)
self.df.at[row,'Allocation'] = checked
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
我正在使用 QTableView 显示 Pandas.Thank 来自 Whosebug 的代码,我可以做到。
我想在选中或取消选中 ComboBox 的某些值时更新 ComboBox 右侧单元格的值,但我是 PyQt5 的新手,我不知道该怎么做。
当我 select 'A' => 右边的单元格将是 'A',然后我检查 'B' => 右边的单元格将是 'A,B'。如果我 deselect A => 右边的单元格将是 'B'
我一直在寻找,但找不到解决方案。 我真的需要你的帮助。
这就是我想要做的。
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QComboBox, QVBoxLayout
from PyQt5.QtCore import Qt
dept_list = ['A','B','C','D','E','F','G','H']
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = data
def data(self, index, role):
if role == Qt.DisplayRole or role == Qt.EditRole:
# See below for the nested-list data structure.
# .row() indexes into the outer list,
# .column() indexes into the sub-list
value = self._data.iloc[index.row(), index.column()]
return str(value)
def rowCount(self, index):
# The length of the outer list.
return self._data.shape[0]
def columnCount(self, index):
# The following takes the first sub-list, and returns
# the length (only works if all rows are an equal length)
return self._data.shape[1]
def flags(self, index):
return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable
def setData(self, index, value, role):
if role == Qt.EditRole:
self._data.iloc[index.row(), index.column()] = value
self._data.iat[index.row(), self._data.shape[1]-1] = value
self.dataChanged.emit(index, index)
return True
def headerData(self, section, orientation, role):
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return str(self._data.columns[section])
if orientation == Qt.Vertical:
return str(self._data.index[section])
class CheckableComboBox(QComboBox):
def __init__(self):
super().__init__()
self._changed = False
self.view().pressed.connect(self.handleItemPressed)
self.checked_item=[]
for i in range(len(dept_list)):
self.addItem(dept_list[i])
self.setItemChecked(i, False)
def setItemChecked(self, index, checked=False):
item = self.model().item(index, self.modelColumn()) # QStandardItem object
if checked:
item.setCheckState(Qt.Checked)
else:
item.setCheckState(Qt.Unchecked)
def handleItemPressed(self, index):
item = self.model().itemFromIndex(index)
if item.checkState() == Qt.Checked:
item.setCheckState(Qt.Unchecked)
if item.text() in self.checked_item:
self.checked_item.remove(item.text())
print(self.checked_item)
else:
item.setCheckState(Qt.Checked)
self.checked_item.append(item.text())
print(self.checked_item)
self._changed = True
self.check_items()
def hidePopup(self):
if not self._changed:
super().hidePopup()
self._changed = False
def item_checked(self, index):
# getting item at index
item = self.model().item(index, 0)
# return true if checked else false
return item.checkState() == Qt.Checked
def check_items(self):
# blank list
checkedItems = []
# traversing the items
for i in range(self.count()):
# if item is checked add it to the list
if self.item_checked(i):
checkedItems.append(i)
# checkedItems.append(self.model().item(i, 0).text())
# call this method
self.update_labels(checkedItems)
# method to update the label
def update_labels(self, item_list):
n = ''
count = 0
# traversing the list
for i in item_list:
# if count value is 0 don't add comma
if count == 0:
n += ' % s' % i
# else value is greater then 0
# add comma
else:
n += ', % s' % i
# increment count
count += 1
# loop
for i in range(self.count()):
# getting label
text_label = self.model().item(i, 0).text()
# default state
if text_label.find('-') >= 0:
text_label = text_label.split('-')[0]
# shows the selected items
item_new_text_label = text_label + ',' + n
# setting text to combo box
self.setItemText(i, item_new_text_label)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.table = QtWidgets.QTableView()
self.combo_box_list=[]
import pandas as pd
df = pd.read_excel(r"E:2021-12 2022 1001.xlsx", skiprows=7)
# data = pd.DataFrame([[1, 9, 2], [1, 0, -1], [3, 5, 2], [3, 3, 2], [5, 8, 9],],
# columns=["A", "B", "C"],
# index=["Row 1", "Row 2", "Row 3", "Row 4", "Row 5"],)
df['Allocation Selection'] = ''
df['Allocation']=''
self.model = TableModel(df)
self.table.setModel(self.model)
# combo = CheckableComboBox()
# for i in range(len(dept_list)):
# combo.addItem(dept_list[i])
# combo.setItemChecked(i, False)
for i in range(df.shape[0]):
combo = CheckableComboBox()
self.combo_box_list.append(combo)
# pix = QPersistentModelIndex(index)
# combo.currentIndexChanged[str].connect(lambda txt, pix=pix: self.tableView.model().setData(QModelIndex(pix), txt))
self.table.setIndexWidget(self.model.index(i, df.shape[1] - 2), combo)
self.setCentralWidget(self.table)
self.setGeometry(600, 100, 400, 200)
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
如果我没理解错的话,您正在使用 QTableView 来显示 pandas 数据框。在这种情况下,它应该非常简单。选择项目时,您需要使用来自 QComboBox 的信号 - 可能 currentIndexChanged()
。您需要将它连接到一个函数,该函数将识别您的 QComboBox 中的哪些项目被选中,并从中构建将在正确位置进入数据框的字符串。更新数据框后,您的 QTableView 应该更新以显示数据框的新单元格。在不了解您的确切设置(可检查的组合框)的情况下,我不能更具体。
编辑:这是一个可行的解决方案。第一步是让组合框知道它在哪一行——在这种情况下,我只是在创建时将行号作为变量传递给组合框。这是一种快速而肮脏的方法 - 最好找到小部件动态所在的 table 行。第二步是定义一个自定义信号,该信号将行和选中的项目列表传递给主 class 中的函数,该函数使用新的选中列表更新该行的数据框。
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QComboBox, QVBoxLayout
from PyQt5.QtCore import Qt, pyqtSignal
dept_list = ['A','B','C','D','E','F','G','H']
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data):
super().__init__()
self._data = data
def data(self, index, role):
if role == Qt.DisplayRole or role == Qt.EditRole:
# See below for the nested-list data structure.
# .row() indexes into the outer list,
# .column() indexes into the sub-list
value = self._data.iloc[index.row(), index.column()]
return str(value)
def rowCount(self, index):
# The length of the outer list.
return self._data.shape[0]
def columnCount(self, index):
# The following takes the first sub-list, and returns
# the length (only works if all rows are an equal length)
return self._data.shape[1]
def flags(self, index):
return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable
def setData(self, index, value, role):
if role == Qt.EditRole:
self._data.iloc[index.row(), index.column()] = value
self._data.iat[index.row(), self._data.shape[1]-1] = value
self.dataChanged.emit(index, index)
return True
def headerData(self, section, orientation, role):
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return str(self._data.columns[section])
if orientation == Qt.Vertical:
return str(self._data.index[section])
class CheckableComboBox(QComboBox):
check_changed = pyqtSignal(int, list)
def __init__(self, row):
super().__init__()
self._changed = False
self.view().pressed.connect(self.handleItemPressed)
self.checked_item=[]
self.row = row
for i in range(len(dept_list)):
self.addItem(dept_list[i])
self.setItemChecked(i, False)
def setItemChecked(self, index, checked=False):
item = self.model().item(index, self.modelColumn()) # QStandardItem object
if checked:
item.setCheckState(Qt.Checked)
else:
item.setCheckState(Qt.Unchecked)
def handleItemPressed(self, index):
item = self.model().itemFromIndex(index)
if item.checkState() == Qt.Checked:
item.setCheckState(Qt.Unchecked)
if item.text() in self.checked_item:
self.checked_item.remove(item.text())
print(self.checked_item)
else:
item.setCheckState(Qt.Checked)
self.checked_item.append(item.text())
print(self.checked_item)
self._changed = True
self.check_changed.emit(self.row, self.checked_item)
self.check_items()
def hidePopup(self):
if not self._changed:
super().hidePopup()
self._changed = False
def item_checked(self, index):
# getting item at index
item = self.model().item(index, 0)
# return true if checked else false
return item.checkState() == Qt.Checked
def check_items(self):
# blank list
checkedItems = []
# traversing the items
for i in range(self.count()):
# if item is checked add it to the list
if self.item_checked(i):
checkedItems.append(i)
# checkedItems.append(self.model().item(i, 0).text())
# call this method
self.update_labels(checkedItems)
# method to update the label
def update_labels(self, item_list):
n = ''
count = 0
# traversing the list
for i in item_list:
# if count value is 0 don't add comma
if count == 0:
n += ' % s' % i
# else value is greater then 0
# add comma
else:
n += ', % s' % i
# increment count
count += 1
# loop
for i in range(self.count()):
# getting label
text_label = self.model().item(i, 0).text()
# default state
if text_label.find('-') >= 0:
text_label = text_label.split('-')[0]
# shows the selected items
item_new_text_label = text_label + ',' + n
# setting text to combo box
self.setItemText(i, item_new_text_label)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.table = QtWidgets.QTableView()
self.combo_box_list=[]
import pandas as pd
#df = pd.read_excel(r"E:2021-12 2022 1001.xlsx", skiprows=7)
self.df = pd.DataFrame([[1, 9, 2], [1, 0, -1], [3, 5, 2], [3, 3, 2], [5, 8, 9],],
columns=["A", "B", "C"])
self.df['Allocation Selection'] = ''
self.df['Allocation']=''
self.model = TableModel(self.df)
self.table.setModel(self.model)
for i in range(self.df.shape[0]):
combo = CheckableComboBox(i)
combo.check_changed.connect(self.update_df)
self.combo_box_list.append(combo)
self.table.setIndexWidget(self.model.index(i, self.df.shape[1] - 2), combo)
self.setCentralWidget(self.table)
self.setGeometry(600, 100, 400, 200)
def update_df(self, row, checked):
print(row, checked)
self.df.at[row,'Allocation'] = checked
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()