0

I have a QTableWidget in PyQt5 which I fill from a python dictionary of numpy array or a pandas DataFrame.

Question

I want to fill or read from QTableWidget in a 'vectorized' way as per operating over array rather than row,col values. I have seen other examples using QTableView in this post How can I retrieve data from a QTableWidget to Dataframe? but this still operates over individuals values rather than arrays or lists. Is this possible?

Generally I do this when I operate over row, col values:

# To Writte
TableWidget.setItem(row, col, tableItem)

# To Read
TableWidget.item(i, j).data()

I would like to do something like this:

# To Writte
TableWidget.setColumn(col, array_items)

# To Read
TableWidget.getColumn(col).data()

I would prefer to keep using QTableWidget, but I am open to other options like QTableView. I appreciate any help, Marcelo.

MBV
  • 591
  • 3
  • 17

1 Answers1

3

It's possible to do that using TableView and re-implemented QAbstractTableModel. Instead of TableWidget.setColumn(col, array_items), You will call TableWidget.model().setColumn(col, array_items). In principle to have it working with TableWidget, You have to re-implement QTableWidget class, but I found that unnecessary.

In my example I used pandas DataFrame as a data holder. You can set and get data using column name.

Here is fully working example:

import sys
import typing

import pandas as pd
from PyQt5.QtCore import QAbstractTableModel, QModelIndex, Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableView, QWidget, QGridLayout, QPushButton


class TableModel(QAbstractTableModel):

    def __init__(self, table_data, parent=None):
        super().__init__(parent)
        self.table_data = table_data

    def rowCount(self, parent: QModelIndex = ...) -> int:
        return self.table_data.shape[0]

    def columnCount(self, parent: QModelIndex = ...) -> int:
        return self.table_data.shape[1]

    def data(self, index: QModelIndex, role: int = ...) -> typing.Any:
        if role == Qt.DisplayRole:
            return str(self.table_data.loc[index.row()][index.column()])

    def headerData(self, section: int, orientation: Qt.Orientation, role: int = ...) -> typing.Any:
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return str(self.table_data.columns[section])

    def setColumn(self, col, array_items):
        """Set column data"""
        self.table_data[col] = array_items
        # Notify table, that data has been changed
        self.dataChanged.emit(QModelIndex(), QModelIndex())

    def getColumn(self, col):
        """Get column data"""
        return self.table_data[col]


if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = QMainWindow()

    widget = QWidget()

    layout = QGridLayout()
    widget.setLayout(layout)
    table_data = pd.DataFrame(data={'col1': [1, 2, 3, 4, 5, 6, 7, 8], 'col2': [1, 2, 3, 4, 5, 6, 7, 8]})
    table = QTableView()
    table.setModel(TableModel(table_data=table_data))
    layout.addWidget(table)

    button = QPushButton("Set col1")
    button.clicked.connect(lambda: table.model().setColumn("col1", [8, 7, 6, 5, 4, 3, 2, 1]))

    button_2 = QPushButton("Read col1")
    button_2.clicked.connect(lambda: print(table.model().getColumn("col1")))

    layout.addWidget(button)
    layout.addWidget(button_2)
    win.setCentralWidget(widget)

    win.show()
    app.exec()
Domarm
  • 2,360
  • 1
  • 5
  • 17
  • Thanks @Domarm, the thing is I should do an extensive refactor of my code if a swicth to TableView, this is something I am trying to avoid. – MBV Feb 11 '22 at 22:08
  • The thing is, that QTableWidget is the same as QTableView, but has already defined QAbstractTableModel. You can call `myTableWidget.model()` and You will get `QAbstractTableModel` object. The only difference is, that You can't execute `myTableWidget.setModel()`, You will get `TypeError: QTableWidget.setModel() is a private method`. So You are stuck with item based data handling, if You keep using `QTableWidget`. I would strongly recommend switching to `QTableView` as it gives You complete freedom over Your table data management. Even if You have to refactor Your code in the end. – Domarm Feb 12 '22 at 07:44
  • 1
    Thnaks @Domarm, I will do this, there is a bigger upside when using the QTableView. Greetings, Marcelo. – MBV Feb 12 '22 at 20:36