PyQt:当单元格进入 QCalendarWidget 时发出信号

Posted

技术标签:

【中文标题】PyQt:当单元格进入 QCalendarWidget 时发出信号【英文标题】:PyQt: Emit signal when cell entered in QCalendarWidget 【发布时间】:2017-06-22 00:49:32 【问题描述】:

在我的 Qt 应用程序中,我使用的是 QCalendarWidget,我希望在鼠标进入日历的新单元格时收到通知。我知道QCalendarWidget 在内部使用QTableView,它继承自QAbstractItemView,它有一个entered 信号:

当鼠标光标进入指定项目时发出此信号 按索引。需要启用鼠标跟踪才能使用此功能。

我尝试使用以下代码接收信号:

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

class MyCalendar:

    def __init__(self):
        app = QApplication(sys.argv)

        window = QMainWindow()
        cal = QCalendarWidget(window)

        window.resize(320, 240)
        cal.resize(320, 240)

        table = cal.findChild(QTableView)
        table.setMouseTracking(True)
        table.entered.connect(self.onCellEntered)

        window.show()
        sys.exit(app.exec_())

    def onCellEntered(self, index):
        print("CellEntered")

if __name__ == "__main__":
    window = MyCalendar()

但我的回调函数永远不会被调用。你有什么想法吗?

【问题讨论】:

如果您将代码扩展为一个可以运行并显示问题的小示例,那就太好了。 @GeorgSchölly 我已经用一个显示QCalendarWidget 的工作窗口应用程序更新了问题中的代码,但尽管信号已连接,但从未调用onCellEntered 方法。 【参考方案1】:

QCalendarWidget 类使用了一个自定义的表格视图,它绕过了普通的鼠标事件处理程序——所以enetered 信号永远不会被发出。但是,可以通过使用事件过滤器发出执行相同操作的自定义信号来解决此问题。

这是一个实现该功能的演示脚本:

import sys
from PyQt4 import QtCore, QtGui

class Window(QtGui.QWidget):
    cellEntered = QtCore.pyqtSignal(object)

    def __init__(self):
        super(Window, self).__init__()
        self.calendar = QtGui.QCalendarWidget(self)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.calendar)
        self._table = self.calendar.findChild(QtGui.QTableView)
        self._table.setMouseTracking(True)
        self._table.installEventFilter(self)
        self._index = None
        self.cellEntered.connect(self.handleCellEntered)

    def eventFilter(self, source, event):
        if source is self._table:
            if event.type() == QtCore.QEvent.MouseMove:
                index = QtCore.QPersistentModelIndex(
                    source.indexAt(event.pos()))
                if index != self._index:
                    self._index = index
                    self.cellEntered.emit(QtCore.QModelIndex(index))
            elif event.type() == QtCore.QEvent.Leave:
                self._index = None
        return super(Window, self).eventFilter(source, event)

    def handleCellEntered(self, index):
        print(index.row(), index.column())

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 100, 300, 200)
    window.show()
    sys.exit(app.exec_())

【讨论】:

哇,谢谢你的回答!你是怎么知道这一切的? @Cilenco。没有什么大秘密。自 2004/5 年以来我一直在使用 PyQt,所以只是不断积累知识......【参考方案2】:

我调查了一下,我想我知道为什么会发生这种情况。

QCalendarWidget 创建一个名为 QCalendarViewQTableView 的私​​有子类,并将其实例化为自身的子类。 (此实例称为qt_calendar_calendarview。)

如果您查看QCalendarView's code (Qt 5),您会看到:

void QCalendarView::mouseMoveEvent(QMouseEvent *event)

    [...]
    if (!calendarModel) 
        QTableView::mouseMoveEvent(event);
        return;
    
    [...]

这意味着只有在没有calendarModel 的情况下,才会调用负责发出entered 信号的超类mouseMoveEvent。所有这些都是 `QCalendarWidget 的实现细节,所以最好不要依赖任何这些。


因此,对于解决此问题的方法,我不确定最好的方法是什么。您必须在事件出现之前捕捉到它。这可以使用QObject.installEventFilter() 或重新实现QWidget.mouseMoveEvent() 来完成,但是您不会直接获得模型索引。为此,您可能可以使用QAbstractItemView.indexAt()

【讨论】:

以上是关于PyQt:当单元格进入 QCalendarWidget 时发出信号的主要内容,如果未能解决你的问题,请参考以下文章

Pyqt5:QtableWidgetItem“无”

如何使用 PYQT5 使 QTableWidget 单元格只读?

Pyqt5 qtablewidget 检测单元格何时更改

如何在 PyQt5 中设置 QTableWidget 的单元格样式?

在 QTableWidget PyQt5 中突出显示单元格

如何在pyqt中更改Qtablewidget的特定单元格背景颜色