PyQt5: QTableWidget
Torna all’indice degli appunti
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.
Commenti recenti