Home > PyQt5, python > PyQt5: QTableWidget

PyQt5: QTableWidget

14 Maggio 2019

Torna all’indice degli appunti

QTableWidget

Il TableWidget è widget complesso che permette di visualizzare i dati in forma di griglia, con righe, colonne e le rispettive intestazioni.
Il costruttore è ovviamente QTableWidget. Dopo aver istanziato l’oggetto, si definiscono le dimensioni della tabella, cno gli appositi metodi setRowCount(row) e setColumnCount(column):

>>> from PyQt5.QtWidgets import QTableWidget, QApplication 
>>> app = QApplication([])
>>> table = QTableWidget(5, 2, None)
>>> table.setRowCount(5)
>>> table.setColumnCount(2)

Le dimensioni della tabella possono essere recuperate con i soliti metodi rowCount e columnCount:

>>> table.rowCount()
5
>>> table.columnCount()
2

ITEMS

Gli elementi della tabella sono tutti oggetti QTableWidgetItem e vengono creati fuori dalla tabella ed inseriti, in seguito, con il metodo
setItem(tablewidgetitem).
I Top-level item di questo tipo vanno costruiti senza parent e possono tutti contenere: testo, icone e checboxes.

HEADER

E’ possibile settare le intestazioni della tabella, sia orizzontali (intestazioni di colonna), sia verticali (intestazioni di riga) con i metodi:

setHorizontalHeader(column, tablewidgetitem): setta un oggetto QTableWidgetItem nell’intestazione della colonna column;
setVerticalHeader(row, tablewidgetitem): setta un oggetto QTableWidgetItem nell’intestazione della riga row;

>>> from PyQt5.QtWidgets import QTableWidgetItem
>>> for position in range(1, 6):
...     table.setVerticalHeaderItem(position, QTableWidgetItem("pos %s" % position))

E’ anche possibile settare multiple intestazioni con una lista di labels, con i metodi:

setHorizontalHeaderLabels(labels): setta le intestazioni presenti nella lista labels, una per colonna;
setVerticalHeaderLabels(labels): setta le intestazioni presenti nella lista labels, una per riga;

>>> table.setHorizontalHeaderLabels(["team", "pts"])

I dati delle intestazioni possono essere recuperati con i metodi:

horizontalHeader(column): ritorna l’oggetto QTableWidgetItem della colonna column;
verticalHeader(row): ritorna l’oggetto QTableWidgetItem della riga row;

>>> table.horizontalHeaderItem(0)
<PyQt5.QtWidgets.QTableWidgetItem object at 0x0379D6C0>
>>> table.horizontalHeaderItem(0).text()
'team'
>>> table.verticalHeaderItem(0).text()
'pos 1'

Come detto, l’inserimento degli elementi in tabella, avviene per mezzo del metodo setItem(qtablewidgetitem):

>>> for row, data in enumerate([("Inter", "66"), ("Milan", "62"), ("Juventus", "89"),
...                             ("Atalanta", "65"), ("Napoli", "76")]):
...     team, pts = data
...     table.setItem(row, 0, QTableWidgetItem(team))
...     table.setItem(row, 1, QTableWidgetItem(pts)) 

MODEL

Il model di riferimento del QTableWidget è il QAbstractTableModel che eredita da QAbstractItemModel.

>>> model = table.model()
>>> index_0_0 = model.index(0, 0)
>>> model.itemData(index_0_0)
{0: 'Inter'}
>>> table.itemFromIndex(index_0_0)
<PyQt5.QtWidgets.QTableWidgetItem object at 0x0379D9E0>
>>> table.itemFromIndex(index_0_0).text()
'Inter'

Ovviamente esiste una via diretta per ottenere un elemento dalla tabella, utilizzando il metodo item(row, column):

>>> table.item(0, 0)
<PyQt5.QtWidgets.QTableWidgetItem object at 0x0379D9E0>
>>> table.item(0, 0).text()
'Inter'

METODI

I metodi più significativi del QTableWidget sono:

setCurrentItem(tablewidgetitem): setta tablewidgetitem come elemento corrente:

>>> table.setCurrentItem(table.item(0, 0))

currentItem: ritorna l’oggetto tablewidgetitem corrente:

>>> table.currentItem()
<PyQt5.QtWidgets.QTableWidgetItem object at 0x0379D9E0>
>>> table.currentItem().text()
'Inter'

setCurrentCell(row, column): setta la cella corrente con coordinate row e column:

table.setCurrentCell(1, 1)

Non c’è un metodo currentCell, ma è possibile utilizzare i già noti currentRow e currentColumn.

sortItems(column, ordering): setta il tipo di ordinamento per la colonna column. il tipo di ordinamento ordering, può essere uno dei due valori Qt.AscendingOrder, o Qt.DescendingOrder:

>>> for row in range(table.rowCount()):
...     print(table.item(row, 0).text(), table.item(row, 1).text())
...     
Inter 66
Milan 62
Juventus 89
Atalanta 65
Napoli 76
>>> table.sortItems(0, Qt.AscendingOrder)
>>> for row in range(table.rowCount()):
...     print(table.item(row, 0).text(), table.item(row, 1).text())
...     
Atalanta 65
Inter 66
Juventus 89
Milan 62
Napoli 76

Come si nota i dati della prima colonna sono stati ordinati.

>>> table.sortItems(1, Qt.DescendingOrder)
>>> for row in range(table.rowCount()):
...     print(table.item(row, 0).text(), table.item(row, 1).text())
...     
Juventus 89
Napoli 76
Inter 66
Atalanta 65
Milan 62

Metodi di eliminazione

Per eliminare tutti gli elementi della view, comprese le selezioni e le intestazioni, si utilizza il metodo clear.
Per eliminare solo i contenuti della tabella, escluse selezioni e intestazioni, si utilizza il metodo clearContents.

Metodi di selezione

Per selezionare tutti gli elementi in tabella, utilizzare il metodo selectAll;
Per per visualizzare gli elementi selezionati, si utilizza il metodo selectedItems:

>>> table.selectedItems()
[<PyQt5.QtWidgets.QTableWidgetItem object at 0x0379D3A0>]
>>> table.selectAll()
>>> table.selectedItems()
[<PyQt5.QtWidgets.QTableWidgetItem object at 0x0379D260>, <PyQt5.QtWidgets.QTabl...

Per selezionare tutti gli elementi di una riga, si utilizza il metodo selectRow(row)
Per selezionare tutti gli elementi di una colonna, si utilizza il metodo selectColumn(column)

>>> table.selectAll()
>>> sel = table.selectedItems()
>>> len(sel)
10
>>> table.selectRow(2)
>>> len(table.selectedItems())
2
>>> table.selectColumn(0)
>>> len(table.selectedItems())
5

Se vogliamo settare una selezione parziale, che non sia appunto, una colonna o una riga intere, oppure un selectAll, si utilizza il metodo setRangeSelected(range, bool), dove range è un oggetto QTableWidgetSelectionRange e bool è
il flag che rende visibile o meno la selezione nel widget.
QTableWidgetSelectionRange altro non è che una classe che immagazzina i riferimenti rispettivamente di: top_row, left_column, bottom_row, right_column.
Supponento di voler selezionare le due righe centrali dovremo fare come segue:

>>> from PyQt5.QtWidgets import QTableWidgetSelectionRange
>>> top_row = 1
>>> left_column = 0
>>> bottom_row = 2
>>> right_column = 1
>>> range_sel = QTableWidgetSelectionRange(top_row, left_column, bottom_row, right_column)
>>> table.selectAll()
>>> for item in table.selectedItems():
...     item.setSelected(False)
...     
>>> sel = QTableWidgetSelectionRange(1, 0, 2, 1)
>>> table.setRangeSelected(sel, True)
>>> len(table.selectedItems())
4

SEGNALI

I più significativi sono:

currentItemChanged: segnale emesso quando cambia l’elemento corrente

>>> table.currentItemChanged.connect(lambda: print("[SIG] Current item changed!"))
<PyQt5.QtCore.QMetaObject.Connection object at 0x0377BE30>
>>> table.setCurrentItem(table.item(0, 0))
[SIG] Current item changed!
>>> table.setCurrentItem(table.item(0, 0))

currentCellChanged: segnale emesso quando cambia la cella corrente. Questo segnale causerà anche l’emissione del segnale currentItemChanged, qualora
cambi ovviamente il current Item

>>> table.currentCellChanged.connect(lambda: print("[SIG] Current cell changed!"))
<PyQt5.QtCore.QMetaObject.Connection object at 0x0535D170>
>>> table.setCurrentCell(2, 0)
[SIG] Current item changed!
[SIG] Current cell changed!

itemClicked: segnale emesso quando clicchiamo su un elemento. Viene passato allo slot connesso, anche l’item sul quale abbiamo cliccato.

cellClicked: segnale emesso quando clicchiamo su una cella

itemSelectionChanged: segnale emesso quando cambia la selezione

>>> table.itemSelectionChanged.connect(lambda: print("[SIG] selection changed!"))
<PyQt5.QtCore.QMetaObject.Connection object at 0x0535D230>
>>> table.setRangeSelected(QTableWidgetSelectionRange(1, 0, 3, 1), True)
[SIG] selection changed!

Di seguito un codice di esempio:

from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtWidgets import QWidget, QApplication, QBoxLayout
from PyQt5.QtWidgets import (QTableWidget, QTableWidgetItem,
                             QTableWidgetSelectionRange)
from PyQt5.QtGui import QIcon


import sys


class MainWindow(QMainWindow):
    def __init__(self, parent=None, data=None):
        super(MainWindow, self).__init__(parent)
        self.setWindowTitle("QTableWidget Example")
        self.central_widget = FormWidget(parent=self, data=data)
        self.setCentralWidget(self.central_widget)
        self.resize(300, 250)


class FormWidget(QWidget):
    def __init__(self, parent, data=None):
        super(FormWidget, self).__init__(parent)
        layout = QBoxLayout(QBoxLayout.TopToBottom)
        self.setLayout(layout)
        self.table = QTableWidget()
        self.table.setRowCount(5)
        self.table.setColumnCount(2)
        layout.addWidget(self.table)
        self.table.setHorizontalHeaderLabels(["Team", "pts"])
        if data:
            for row, tup in enumerate(data):
                team, pts, iconfile = tup
                icon = QIcon(iconfile)
                self.table.setItem(row, 0, QTableWidgetItem(icon, team))
                self.table.setItem(row, 1, QTableWidgetItem(pts))

        self.table.itemClicked.connect(self.on_item_click)
        self.table.itemSelectionChanged.connect(self.on_selection_change)

    def on_item_click(self, item):
        print("[SIG] clicked on %s" % item.text())

    def select_by(self, string):
        rows = self.table.rowCount()
        columns = self.table.columnCount()
        for row in range(rows):
            for column in range(columns):
                item = self.table.item(row, column)
                if item.text().lower() == string.lower():
                    item.setSelected(True)
                else:
                    item.setSelected(False)

    def on_selection_change(self):
        print("[SIG] Selected Items chaged:")
        self.table = self.sender()
        items = self.table.selectedItems()
        for item in items:
            print(item.text())


if __name__ == '__main__':
    app = QApplication(sys.argv)
    teams = [("Inter", "66", "interlogo.png"),
             ("Milan", "62", "milanlogo.png"),
             ("Juventus", "89", "juvelogo.png"),
             ("Napoli", "76", "napolilogo.png"),
             ("Atalanta", "65", "atalantalogo.png")]
    main_window = MainWindow(parent=None, data=teams)
    main_window.show()
    sys.exit(app.exec_())

e con selezione preimpostata:

...

class FormWidget(QWidget):
    def __init__(self, parent, data=None):
        ...
        self.table.setRangeSelected(QTableWidgetSelectionRange(1, 0, 2, 1), 
                                    True)
        ...

E’ ovviamente possibile selezionare per criterio.
Mettiamo ad esempio di voler evidenziare le celle che contengono un determinato valore, possiamo agire in due modi: o selezionando quelle che sposano un certo criterio, o spegnendo quelle che non lo sposano.
Notare che le vecchie selezioni rimangono attive, quindi è sempre bene deselezionare tutto, prima di procedere con una nuova selezione.

caso 1:

...

class FormWidget(QWidget):
    def __init__(self, parent, data=None):
        ...

        self.select_by("Inter")
        
    def select_all(self, select):
        self.table.selectAll()
        for item in self.table.selectedItems():
            item.setSelected(select)

    def select_by(self, string):
        self.select_all(False)  # deseleziono tutto
        rows = self.table.rowCount()
        for row in range(rows):
            item = self.table.item(row, 0)
            if item.text().lower() == string.lower():
                item.setSelected(True)
            else:
                item.setSelected(False)
        ...

caso 2:

...

class FormWidget(QWidget):
    def __init__(self, parent, data=None):
        ...
        self.select_by("Inter")
        
    def select_by(self, string):
        self.select_all(True)  # seleziono tutto
        for item in self.table.selectedItems():
            if item.text().lower() != string.lower():
                item.setSelected(False)
        ...

ORDINAMENTO

E’ possibile attivare l’ordinamento delle colonne della tabella con il metodo setSortingEnabled(bool), in questo modo sarà possibile ordniare i dati, cliccando sull’intestazione della colonna. Gli accoppiamenti sulle altre colonne, verranno mantenuti:

...
        self.table.setSortingEnabled(True)
        self.table.itemClicked.connect(self.on_item_click)
        self.select_by("Inter")
...

Quando si clicca sull’intestazione della colonna, si ottengono alternativamente, un ordinamento crescente e poi decrescente.

Inoltre non si perdono le selezioni precedentemente fatte.

Torna all’indice degli appunti

Categorie:PyQt5, python Tag: ,
I commenti sono chiusi.