使用旋转框小部件 (PyQt5) 从 QTableWidget 移动项目和读取值
Move items and read values from QTableWidget with spinbox widget (PyQt5)
我有一个软件工具,其中 table 填充了两列:参数和精度。 “精度”列中的一些参数具有微调框小部件,而一些参数只有文本“N/A”。
我有两个问题:
如何上下移动 table 中的行,包括旋转框小部件。目前我只能移动参数名称,一旦我尝试向上或向下移动行,旋转框就会消失。
下面是定义选定行向上或向下移动的函数的代码:
'''
def _buttonMoveUpClicked(self):
rowIndex = self.table.currentRow()
self.table.insertRow(rowIndex - 1)
for i in range(self.table.columnCount()):
self.table.setItem(rowIndex - 1, i, self.table.takeItem(rowIndex + 1, i))
self.table.selectRow(rowIndex - 1)
self.table.removeRow(rowIndex + 1)
def _buttonMoveDownClicked(self):
rowIndex = self.table.currentRow()
self.table.insertRow(rowIndex + 2)
for i in range(self.table.columnCount()):
self.table.setItem(rowIndex + 2, i,
self.table.takeItem(rowIndex, i))
self.table.selectRow(rowIndex + 2)
self.table.removeRow(rowIndex)
'''
- 我需要将 table 中的值保存到列表中。如何使用旋转框小部件从单元格中读取值?
我现在尝试保存它的方式:
'''
def _readTable(self):
list_Parameters = []
list_Precision = []
print('1')
for i in range(self.table.rowCount()):
print(self.table.item(i, 0).text())
list_Parameters.append(self.table.item(i, 0).text())
list_Precision.append(self.table.item(i, 1).text())
'''
这是软件工具的完整代码:
import sys
from PyQt5.QtWidgets import *
from PyQt5 import QtCore
import pandas as pd
class Window(QWidget):
singleton: 'Window' = None
def __init__(self):
super(Window, self).__init__()
self.setWindowTitle("Software tool")
self.setGeometry(50, 50, 1800, 900)
self.mainLayout=QHBoxLayout()
self.setLayout(self.mainLayout)
self.UI()
def UI(self):
self.sublayouts = {}
self.buttons = {}
self._view()
self._fillListWidget()
self.table.selectionModel().selectionChanged.connect(self._updateButtonStatus)
self.buttons['Up'].clicked.connect(self._buttonMoveUpClicked)
self.buttons['Down'].clicked.connect(self._buttonMoveDownClicked)
self.show()
def _view(self):
self.table = QTableWidget(0, 2)
self.table.setHorizontalHeaderLabels(['Parameter', 'Precision'])
self.table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table.setEditTriggers(QTableWidget.NoEditTriggers)
self.buttons['Up'] = QPushButton('&Up')
self.buttons['Down'] = QPushButton('&Down')
self.sublayouts['table'] = QGridLayout()
self.sublayouts['table'].addWidget(self.table, 1, 0, 4, 4)
self.sublayouts['table'].setRowStretch(4, 1)
self.sublayouts['table'].addWidget(self.buttons['Up'], 1, 4, 1, 1, alignment=QtCore.Qt.AlignTop)
self.sublayouts['table'].addWidget(self.buttons['Down'], 2, 4, 1, 1, alignment=QtCore.Qt.AlignTop)
self.mainLayout.addLayout(self.sublayouts['table'])
def _fillListWidget(self):
list = {
'Parameters': ['a', 'b', 'c', 'd', 'e'],
'Precision': [-1, -1, 2, 3, 1]}
self.df = pd.DataFrame(list)
for row in zip(*self.df.to_dict('list').values()):
itemColumn = QTableWidgetItem(
row[self.df.columns.get_loc("Parameters")])
rowPosition = self.table.rowCount()
self.table.insertRow(rowPosition)
itemTable = self._tableCell(row[self.df.columns.get_loc("Parameters")])
self.table.setItem(rowPosition, 0, itemTable)
df_tmp = self.df[self.df['Parameters'] == row[self.df.columns.get_loc("Parameters")]]
if df_tmp['Precision'].values[0] >= 0:
self.spinBox = QSpinBox()
self.spinBox.setValue(df_tmp['Precision'].values[0])
self.table.setCellWidget(rowPosition, 1, self.spinBox)
else:
itemTable = self._tableCell('N/A')
self.table.setItem(rowPosition, 1, itemTable)
def _updateButtonStatus(self):
self.buttons['Up'].setDisabled(not bool(self.table.selectedItems()) or self.table.rowCount() == 0 or self.table.currentRow()==0)
self.buttons['Down'].setDisabled(not bool(self.table.selectedItems()) or self.table.rowCount() == 0 or self.table.currentRow()==self.table.rowCount()-1)
# self._readTable()
def _buttonMoveUpClicked(self):
rowIndex = self.table.currentRow()
self.table.insertRow(rowIndex - 1)
for i in range(self.table.columnCount()):
self.table.setItem(rowIndex - 1, i, self.table.takeItem(rowIndex + 1, i))
self.table.selectRow(rowIndex - 1)
self.table.removeRow(rowIndex + 1)
def _buttonMoveDownClicked(self):
rowIndex = self.table.currentRow()
self.table.insertRow(rowIndex + 2)
for i in range(self.table.columnCount()):
self.table.setItem(rowIndex + 2, i,
self.table.takeItem(rowIndex, i))
self.table.selectRow(rowIndex + 2)
self.table.removeRow(rowIndex)
def _tableCell(self, text):
item = QTableWidgetItem()
item.setText(text)
return item
def _readTable(self):
list_Parameters = []
list_Precision = []
print('1')
for i in range(self.table.rowCount()):
print(self.table.item(i, 0).text())
list_Parameters.append(self.table.item(i, 0).text())
list_Precision.append(self.table.item(i, 1).text())
def main():
App=QApplication(sys.argv)
window =Window()
sys.exit(App.exec_())
if __name__ == '__main__':
main()
您不能直接使用 QTableWidget 执行此操作,因为它是一个更高级别的小部件,不会在其模型中实现索引移动。
正确的 解决方案是创建一个 QAbstractTableItem 子类,该子类也将实现 moveRows()
.
可以使用更简单的替代方法,但请记住,模型视图应始终跟踪索引,deleting/adding 行不允许这样做。如果你想做一个好的实现,一个项目模型子类是强制性的。
另一种选择来自这样一个事实,即每当删除行(或列)时,它们的编辑器也会被销毁,您对此无能为力。
假设您总是只想移动一行,解决方案是获取当前编辑器(及其值),删除该行,插入新的,并使用之前读取的值为其创建一个新的编辑器。
出于简单原因(并遵循 OOP 模式),我创建了一个通用函数,它接受当前行和一个“delta”参数,告诉它该行是否必须向上或向下“移动”。
def _moveRow(self, row, delta):
colIndex = self.table.currentColumn()
items = [self.table.takeItem(row, c) for c in range(self.table.columnCount())]
oldSpinBox = self.table.cellWidget(row, 1)
self.table.removeRow(row)
newRow = row + delta
self.table.insertRow(newRow)
for i, item in enumerate(items):
self.table.setItem(newRow, i, item)
if isinstance(oldSpinBox, QAbstractSpinBox):
newSpinBox = QSpinBox()
newSpinBox.setValue(oldSpinBox.value())
self.table.setCellWidget(newRow, 1, newSpinBox)
if colIndex >= 0:
self.table.setCurrentCell(newRow, colIndex)
def _buttonMoveUpClicked(self):
rowIndex = self.table.currentRow()
if rowIndex > 0:
self._moveRow(rowIndex, -1)
def _buttonMoveDownClicked(self):
rowIndex = self.table.currentRow()
if rowIndex < self.table.rowCount() - 1:
self._moveRow(rowIndex, 1)
如前所述,这是一个非常简单的解决方案,仅适用于硬编码的 row/column 小部件。一个更正确和更明智的解决方案是创建 QAbstractTableModel 的子类并实现 moveRows()
函数:这样做允许更正确的实现,将提供正确的编辑器(因此不需要 setCellWidget()
)和将允许更正确的行移动。
我有一个软件工具,其中 table 填充了两列:参数和精度。 “精度”列中的一些参数具有微调框小部件,而一些参数只有文本“N/A”。 我有两个问题:
如何上下移动 table 中的行,包括旋转框小部件。目前我只能移动参数名称,一旦我尝试向上或向下移动行,旋转框就会消失。 下面是定义选定行向上或向下移动的函数的代码: '''
def _buttonMoveUpClicked(self): rowIndex = self.table.currentRow() self.table.insertRow(rowIndex - 1) for i in range(self.table.columnCount()): self.table.setItem(rowIndex - 1, i, self.table.takeItem(rowIndex + 1, i)) self.table.selectRow(rowIndex - 1) self.table.removeRow(rowIndex + 1) def _buttonMoveDownClicked(self): rowIndex = self.table.currentRow() self.table.insertRow(rowIndex + 2) for i in range(self.table.columnCount()): self.table.setItem(rowIndex + 2, i, self.table.takeItem(rowIndex, i)) self.table.selectRow(rowIndex + 2) self.table.removeRow(rowIndex)
'''
- 我需要将 table 中的值保存到列表中。如何使用旋转框小部件从单元格中读取值? 我现在尝试保存它的方式:
'''
def _readTable(self):
list_Parameters = []
list_Precision = []
print('1')
for i in range(self.table.rowCount()):
print(self.table.item(i, 0).text())
list_Parameters.append(self.table.item(i, 0).text())
list_Precision.append(self.table.item(i, 1).text())
''' 这是软件工具的完整代码:
import sys
from PyQt5.QtWidgets import *
from PyQt5 import QtCore
import pandas as pd
class Window(QWidget):
singleton: 'Window' = None
def __init__(self):
super(Window, self).__init__()
self.setWindowTitle("Software tool")
self.setGeometry(50, 50, 1800, 900)
self.mainLayout=QHBoxLayout()
self.setLayout(self.mainLayout)
self.UI()
def UI(self):
self.sublayouts = {}
self.buttons = {}
self._view()
self._fillListWidget()
self.table.selectionModel().selectionChanged.connect(self._updateButtonStatus)
self.buttons['Up'].clicked.connect(self._buttonMoveUpClicked)
self.buttons['Down'].clicked.connect(self._buttonMoveDownClicked)
self.show()
def _view(self):
self.table = QTableWidget(0, 2)
self.table.setHorizontalHeaderLabels(['Parameter', 'Precision'])
self.table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table.setEditTriggers(QTableWidget.NoEditTriggers)
self.buttons['Up'] = QPushButton('&Up')
self.buttons['Down'] = QPushButton('&Down')
self.sublayouts['table'] = QGridLayout()
self.sublayouts['table'].addWidget(self.table, 1, 0, 4, 4)
self.sublayouts['table'].setRowStretch(4, 1)
self.sublayouts['table'].addWidget(self.buttons['Up'], 1, 4, 1, 1, alignment=QtCore.Qt.AlignTop)
self.sublayouts['table'].addWidget(self.buttons['Down'], 2, 4, 1, 1, alignment=QtCore.Qt.AlignTop)
self.mainLayout.addLayout(self.sublayouts['table'])
def _fillListWidget(self):
list = {
'Parameters': ['a', 'b', 'c', 'd', 'e'],
'Precision': [-1, -1, 2, 3, 1]}
self.df = pd.DataFrame(list)
for row in zip(*self.df.to_dict('list').values()):
itemColumn = QTableWidgetItem(
row[self.df.columns.get_loc("Parameters")])
rowPosition = self.table.rowCount()
self.table.insertRow(rowPosition)
itemTable = self._tableCell(row[self.df.columns.get_loc("Parameters")])
self.table.setItem(rowPosition, 0, itemTable)
df_tmp = self.df[self.df['Parameters'] == row[self.df.columns.get_loc("Parameters")]]
if df_tmp['Precision'].values[0] >= 0:
self.spinBox = QSpinBox()
self.spinBox.setValue(df_tmp['Precision'].values[0])
self.table.setCellWidget(rowPosition, 1, self.spinBox)
else:
itemTable = self._tableCell('N/A')
self.table.setItem(rowPosition, 1, itemTable)
def _updateButtonStatus(self):
self.buttons['Up'].setDisabled(not bool(self.table.selectedItems()) or self.table.rowCount() == 0 or self.table.currentRow()==0)
self.buttons['Down'].setDisabled(not bool(self.table.selectedItems()) or self.table.rowCount() == 0 or self.table.currentRow()==self.table.rowCount()-1)
# self._readTable()
def _buttonMoveUpClicked(self):
rowIndex = self.table.currentRow()
self.table.insertRow(rowIndex - 1)
for i in range(self.table.columnCount()):
self.table.setItem(rowIndex - 1, i, self.table.takeItem(rowIndex + 1, i))
self.table.selectRow(rowIndex - 1)
self.table.removeRow(rowIndex + 1)
def _buttonMoveDownClicked(self):
rowIndex = self.table.currentRow()
self.table.insertRow(rowIndex + 2)
for i in range(self.table.columnCount()):
self.table.setItem(rowIndex + 2, i,
self.table.takeItem(rowIndex, i))
self.table.selectRow(rowIndex + 2)
self.table.removeRow(rowIndex)
def _tableCell(self, text):
item = QTableWidgetItem()
item.setText(text)
return item
def _readTable(self):
list_Parameters = []
list_Precision = []
print('1')
for i in range(self.table.rowCount()):
print(self.table.item(i, 0).text())
list_Parameters.append(self.table.item(i, 0).text())
list_Precision.append(self.table.item(i, 1).text())
def main():
App=QApplication(sys.argv)
window =Window()
sys.exit(App.exec_())
if __name__ == '__main__':
main()
您不能直接使用 QTableWidget 执行此操作,因为它是一个更高级别的小部件,不会在其模型中实现索引移动。
正确的 解决方案是创建一个 QAbstractTableItem 子类,该子类也将实现 moveRows()
.
可以使用更简单的替代方法,但请记住,模型视图应始终跟踪索引,deleting/adding 行不允许这样做。如果你想做一个好的实现,一个项目模型子类是强制性的。
另一种选择来自这样一个事实,即每当删除行(或列)时,它们的编辑器也会被销毁,您对此无能为力。
假设您总是只想移动一行,解决方案是获取当前编辑器(及其值),删除该行,插入新的,并使用之前读取的值为其创建一个新的编辑器。
出于简单原因(并遵循 OOP 模式),我创建了一个通用函数,它接受当前行和一个“delta”参数,告诉它该行是否必须向上或向下“移动”。
def _moveRow(self, row, delta):
colIndex = self.table.currentColumn()
items = [self.table.takeItem(row, c) for c in range(self.table.columnCount())]
oldSpinBox = self.table.cellWidget(row, 1)
self.table.removeRow(row)
newRow = row + delta
self.table.insertRow(newRow)
for i, item in enumerate(items):
self.table.setItem(newRow, i, item)
if isinstance(oldSpinBox, QAbstractSpinBox):
newSpinBox = QSpinBox()
newSpinBox.setValue(oldSpinBox.value())
self.table.setCellWidget(newRow, 1, newSpinBox)
if colIndex >= 0:
self.table.setCurrentCell(newRow, colIndex)
def _buttonMoveUpClicked(self):
rowIndex = self.table.currentRow()
if rowIndex > 0:
self._moveRow(rowIndex, -1)
def _buttonMoveDownClicked(self):
rowIndex = self.table.currentRow()
if rowIndex < self.table.rowCount() - 1:
self._moveRow(rowIndex, 1)
如前所述,这是一个非常简单的解决方案,仅适用于硬编码的 row/column 小部件。一个更正确和更明智的解决方案是创建 QAbstractTableModel 的子类并实现 moveRows()
函数:这样做允许更正确的实现,将提供正确的编辑器(因此不需要 setCellWidget()
)和将允许更正确的行移动。