简体   繁体   中英

Move items and read values from QTableWidget with spinbox widget (PyQt5)

I have a software tool that has a table filled with two columns: parameter and precision. Some parameters in the "Precision" column have spinbox widget and some parameters have just text "N/A". I have two questions:

  1. How to move the rows in a table up and down including the spinbox widget. Currently I was able to move only the parameter name and the spinbox disappears as soon as I try to move row up or down. Below is the code with functions defining the move of selected row up or down: '''

     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)

'''

  1. I need to save the values from table to the lists. How to read the values from the cells with spinbox widget? The way how I'm trying to save it now:

'''

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())

''' This is the full code for the software tool:

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()

You cannot directly do that with QTableWidget, as it is an higher level widget that doesn't implement index movements in its model.

A proper solution would be to create a QAbstractTableItem subclass that would also implement moveRows() .

A simpler alternative is available, but remember that model views should always keep track of indexes, and deleting/adding rows won't allow it. If you want to do a good implementation, an item model subclass is mandatory.

The alternative comes from the fact that whenever rows (or columns) are removed, their editors are destroyed too, and there's very little you can do with it.
Assuming that you always want to move only one row , the solution is to get the current editor (and its value), delete the row, insert the new one, and create a new editor for it with the previously read value.

For simplicity reasons (and following OOP patterns), I created a common function that accepts the current row and a "delta" argument that tells it if the row has to be "moved" up or down.

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)

As said, this is a very simplistic solution, and only works for hardcoded row/column widgets. A more correct and aware solution is to create a subclass of QAbstractTableModel and implement the moveRows() function: doing this allows a more proper implementation, will provide the correct editors (so that no setCellWidget() would be required) and will allow more correct row movements.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM