2017-01-25 17 views
1

У меня есть QTableWidget с поплавками или сложными записями, которым требуется много горизонтального пространства. Отображение значений с уменьшенным числом цифр с помощью форматирования строк работает отлично, но, очевидно, я теряю точность при редактировании и хранении записей в таблице.QTableView не отправляет ожидаемые события FocusIn/FocusOut в eventFilter

Я нашел решение для виджетов QLineEdit с помощью eventFilter: A копии FocusIn событий сохраненного значения с полной точностью до QLineEdit текстового поля, A FocusOut или события Return_Key сохраняет измененное значение и перезаписываю текстовое поле с уменьшенным числом цифр.

Используя тот же подход с QTableWidgets дает мне следующее (возможно, связанные с) проблемы:

  • FocusIn и FocusOut события не генерируются, как и ожидалось: Когда я дважды щелкните на элементе, я получаю FocusOut событие, нажатие на другой элемент вызывает событие FocusIn
  • Я не могу скопировать содержимое моего отредактированного выбранного элемента, я всегда получаю неотредактированное значение.
  • Выбор элемента путем нажатия на него не вызывает события.

Я пробовал оценивать события QTableWidgetItem, но я их не получаю - мне нужно настроить фильтр событий на каждом QTableWidgetItem? Если да, нужно ли отключать QTableWidgetItem eventFilters каждый раз, когда я изменяю размер таблицы (что часто бывает в моем приложении)? Было бы целесообразно заполнить мою таблицу с помощью виджетов QLineEdit?

Прилагаемый MWE не совсем маленький, но я мог бы уменьшить его.

# -*- 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_()) 

ответ

1

Вы идете об этом совершенно неправильно. Эти виды прецедентов уже обслуживаются существующими API-интерфейсами, поэтому есть несколько доступных решений, которые намного проще, чем у вас в настоящее время.

Возможно, самым простым из всех было бы использование QStyledItemDelegate и переоценка его метода dispalyText. Это позволит вам хранить полные значения в таблице, но форматировать их по-разному для отображения. При редактировании, полное значение всегда будет отображаться (в виде строки):

from PyQt4.QtGui import (QApplication, QWidget, QTableWidget, QTableWidgetItem, 
         QLabel, QVBoxLayout,QStyledItemDelegate) 
import PyQt4.QtCore as QtCore 
from PyQt4.QtCore import QEvent 
from numpy.random import randn 

class ItemDelegate(QStyledItemDelegate): 
    def displayText(self, text, locale): 
     return "{:.3g}".format(float(text)) 

class EventTable (QWidget): 
    def __init__(self, parent = None): 
     super(EventTable, self).__init__(parent) 
     self.myTable = QTableWidget(self) 
     self.myTable.setItemDelegate(ItemDelegate(self)) 
     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 _update_table(self): 
     self.myTable.setRowCount(self.rows) 
     self.myTable.setColumnCount(self.columns) 
     for col in range(self.columns): 
      for row in range(self.rows): 
       item = QTableWidgetItem(str(self.data[row][col])) 
       self.myTable.setItem(row, col, item) 
     self.myTable.resizeColumnsToContents() 
     self.myTable.resizeRowsToContents() 

if __name__ == "__main__": 
    import sys 

    app = QApplication(sys.argv) 
    mainw = EventTable() 
    app.setActiveWindow(mainw) 
    mainw.show() 
    sys.exit(app.exec_()) 

NB: это заманчиво использовать item roles, чтобы решить эту проблему. Однако как QTableWidgetItem, так и QStandardItem рассматривают DisplayRole и EditRole как одну роль, а это означает, что для получения требуемой функциональности необходимо переопределить их методы data и setData.

+0

Настоящим изящным решением! Думаю, мне придется укусить в [delegate_classes] (http://doc.qt.io/qt-5/model-view-programming.html#delegate-classes) ... – Chipmuenk

 Смежные вопросы

  • Нет связанных вопросов^_^