QTableView不会将预期的FocusIn/FocusOut事件发送到eventFilter [英] QTableView doesn't send expected FocusIn / FocusOut events to eventFilter

查看:173
本文介绍了QTableView不会将预期的FocusIn/FocusOut事件发送到eventFilter的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个QTableWidget,它带有浮点数或需要大量水平空间的复杂条目.通过字符串格式显示数字位数减少的值可以正常工作,但是显然,在表中编辑和存储条目时,我的精度降低了.

I have a QTableWidget with floats or complex entries that need a lot of horizontal space. Displaying the values with reduced number of digits via string formatting works fine, but obviously I loose precision when editing and storing entries in the table.

我通过使用eventFilter找到了QLineEdit小部件的解决方案:FocusIn事件将存储的值完全精确地复制到QLineEdit文本字段中,FocusOut事件或Return_Key存储更改后的值并覆盖位数减少的文本字段.

I have found a solution for QLineEdit widgets by using an eventFilter: A FocusIn event copies the stored value with full precision to the QLineEdit textfield, a FocusOut event or a Return_Key stores the changed value and overwrites the text field with reduced number of digits.

对QTableWidgets使用相同的方法会给我带来以下(可能是相关的)问题:

Using the same approach with a QTableWidgets gives me the following (possibly related) problems:

  • FocusIn和FocusOut事件未按预期生成:当我双击一个项目时,我得到一个FocusOut事件,单击另一个项目会产生一个FocusIn事件
  • 我无法复制已编辑的选定项目的内容,我总是会得到未编辑的值.
  • 通过单击选择项目不会产生事件.

我尝试评估QTableWidgetItem事件,但没有收到任何通知-我需要在每个QTableWidgetItem上设置事件过滤器吗?如果是这样,我每次调整表大小时是否都需要断开QTableWidgetItem eventFilters的连接(在我的应用程序中经常这样做)?而是用QLineEdit小部件填充我的表是否有意义?

I've tried evaluating QTableWidgetItem events, but I don't receive any - do I need to setup an event filter on every QTableWidgetItem? If so, do I need to disconnect the QTableWidgetItem eventFilters every time I resize the table (which do frequently in my application)? Would it make sense to populate my table with QLineEdit widgets instead?

附带的MWE并不是很小,但是我可以进一步缩小它.

The attached MWE is not exactly small, but I could shrink it any further.

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

推荐答案

您将以完全错误的方式进行操作.现有的API已经可以满足这类用例的需要,因此有几种可用的解决方案比您目前拥有的解决方案简单得多.

You're going about this in completely the wrong way. These kinds of use-cases are already catered for by the existing APIs, so there are several solutions available that are much simpler than what you currently have.

可能最简单的方法是使用 QStyledItemDelegate 并重新实现其 dispalyText 方法.这将使您可以将完整值存储在表中,但可以采用不同的格式来显示.编辑时,完整值将始终显示(作为字符串):

Probably the simplest of all would be to use a QStyledItemDelegate and reimplement its dispalyText method. This will allow you to store the full values in the table, but format them differently for display. When editing, the full value will always be shown (as a string):

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


注意:很容易使用项目角色解决这个问题.但是,QTableWidgetItemQStandardItem都将DisplayRoleEditRole视为一个角色,这意味着有必要重新实现它们的datasetData方法以获得所需的功能.


NB: it's tempting to use item roles to solve this issue. However, both QTableWidgetItem and QStandardItem treat the DisplayRole and EditRole as one role, which means it would be necessary to reimplement their data and setData methods to get the required functionality.

这篇关于QTableView不会将预期的FocusIn/FocusOut事件发送到eventFilter的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆