J'ai un QTableWidget avec des flottants ou des entrées complexes qui nécessitent beaucoup d'espace horizontal. L'affichage des valeurs avec un nombre réduit de chiffres via la mise en forme des chaînes fonctionne bien, mais il est évident que je perds la précision lors de l'édition et du stockage des entrées dans la table.QTableView n'envoie pas les événements FocusIn/FocusOut attendus à eventFilter
J'ai trouvé une solution pour les widgets QLineEdit en utilisant un eventFilter: A la valeur stockée des copies d'événement FocusIn
avec toute la précision du champ de texte de QLineEdit, un événement FocusOut
ou un Return_Key
stocke la valeur modifiée et écrase le champ de texte avec un nombre réduit des chiffres.
En utilisant la même approche avec un QTableWidgets me donne les éléments suivants (éventuellement liés) problèmes:
- événements FocusIn et FocusOut ne sont pas générés comme prévu: Lorsque je double-cliquez sur un élément, je reçois un FocusOut événement, en cliquant sur un autre élément produit un événement FocusIn
- Je ne peux pas copier le contenu de mon élément édité, sélectionné, je reçois toujours la valeur non modifiée.
- La sélection d'un élément en cliquant dessus ne produit pas d'événement.
J'ai essayé d'évaluer les événements QTableWidgetItem, mais je n'en reçois aucun. Dois-je configurer un filtre d'événements sur chaque QTableWidgetItem? Si oui, dois-je déconnecter les eventFilters QTableWidgetItem chaque fois que je redimensionne la table (ce qui est fréquent dans mon application)? Serait-il logique de remplir ma table avec des widgets QLineEdit à la place?
Le MWE ci-joint n'est pas exactement petit, mais je pourrais le réduire davantage.
# -*- coding: utf-8 -*-
#from PyQt5.QWidgets import (...)
from PyQt4.QtGui import (QApplication, QWidget, QTableWidget, QTableWidgetItem,
QLabel, QVBoxLayout)
import PyQt4.QtCore as QtCore
from PyQt4.QtCore import QEvent
from numpy.random import randn
class EventTable (QWidget):
def __init__(self, parent = None):
super(EventTable, self).__init__(parent)
self.myTable = QTableWidget(self)
self.myTable.installEventFilter(self) # route all events to self.eventFilter()
myQVBoxLayout = QVBoxLayout()
myQVBoxLayout.addWidget(self.myTable)
self.setLayout(myQVBoxLayout)
self.rows = 3; self.columns = 4 # table + data dimensions
self.data = randn(self.rows, self.columns) # initial data
self._update_table() # create table
def eventFilter(self, source, event):
if isinstance(source, (QTableWidget, QTableWidgetItem)):
# print(type(source).__name__, event.type()) #too much noise
if event.type() == QEvent.FocusIn: # 8: enter widget
print(type(source).__name__, "focus in")
self.item_edited = False
self._update_table_item() # focus: display data with full precision
return True # event processing stops here
elif event.type() == QEvent.KeyPress:
print(type(source).__name__, "key pressed")
self.item_edited = True # table item has been changed
key = event.key() # key press: 6, key release: 7
if key in {QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter}: # store entry
self._store_item() # store edited data in self.data
return True
elif key == QtCore.Qt.Key_Escape: # revert changes
self.item_edited = False
self._update_table() # update table from self.data
return True
elif event.type() == QEvent.FocusOut: # 9: leave widget
print(type(source).__name__, "focus out")
self._store_item()
self._update_table_item() # no focus: use reduced precision
return True
return super(EventTable, self).eventFilter(source, event)
def _update_table(self):
"""(Re-)Create the table with rounded data from self.data """
self.myTable.setRowCount(self.rows)
self.myTable.setColumnCount(self.columns)
for col in range(self.columns):
for row in range(self.rows):
self.myTable.setItem(row,col,
QTableWidgetItem(str("{:.3g}".format(self.data[row][col]))))
self.myTable.resizeColumnsToContents()
self.myTable.resizeRowsToContents()
def _update_table_item(self, source = None):
""" Re-)Create the current table item with full or reduced precision. """
row = self.myTable.currentRow()
col = self.myTable.currentColumn()
item = self.myTable.item(row, col)
if item: # is something selected?
if not item.isSelected(): # no focus, show self.data[row][col] with red. precision
print("\n_update_item (not selected):", row, col)
item.setText(str("{:.3g}".format(self.data[row][col])))
else: # in focus, show self.data[row][col] with full precision
item.setText(str(self.data[row][col]))
print("\n_update_item (selected):", row, col)
def _store_item(self):
""" Store the content of item in self.data """
if self.item_edited:
row = self.myTable.currentRow()
col = self.myTable.currentColumn()
item_txt = self.myTable.item(row, col).text()
self.data[row][col] = float(str(item_txt))
print("\n_store_entry - current item/data:", item_txt, self.data[row][col])
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
mainw = EventTable()
app.setActiveWindow(mainw)
mainw.show()
sys.exit(app.exec_())
Une solution vraiment élégante! Je suppose, je vais devoir mordre dans [delegate_classes] (http://doc.qt.io/qt-5/model-view-programming.html#delegate-classes) ... – Chipmuenk