PyQt:当单元格进入 QCalendarWidget 时发出信号
PyQt: Emit signal when cell entered in QCalendarWidget
在我的 Qt 应用程序中,我使用 QCalendarWidget
并且我希望在鼠标进入日历的新单元格时得到通知。我知道 QCalendarWidget
在内部使用 QTableView
继承自 QAbstractItemView 并且它有一个 entered
信号:
This signal is emitted when the mouse cursor enters the item specified
by index. Mouse tracking needs to be enabled for this feature to work.
我尝试使用以下代码接收信号:
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()
但是我的回调函数从未被调用过。你知道为什么吗?
我调查了一下,我想我知道为什么会这样。
QCalendarWidget
创建一个名为 QTableView
的私有子类 QCalendarView
并将其实例化为自身的子类。 (此实例称为 qt_calendar_calendarview
。)
如果您查看 QCalendarView
's code (Qt 5),您会看到:
void QCalendarView::mouseMoveEvent(QMouseEvent *event)
{
[...]
if (!calendarModel) {
QTableView::mouseMoveEvent(event);
return;
}
[...]
}
这意味着仅当没有 calendarModel
时,才会调用负责发出 entered
信号的超类 mouseMoveEvent
。所有这些都是 `QCalendarWidget 的实现细节,因此最好不要依赖任何这些。
所以对于解决这个问题的方法,我不确定最好的方法是什么。您必须在 到达 table 之前捕获事件 。这可以使用 QObject.installEventFilter()
或 re-implementing QWidget.mouseMoveEvent()
来完成,但是您不能直接获取模型索引。您可能会为此目的使用 QAbstractItemView.indexAt()
。
QCalendarWidget
class 使用自定义 table-view 绕过正常的 mouse-event 处理程序 - 因此 enetered
信号永远不会发出。但是,可以 work-around 通过使用 event-filter 来发出执行相同操作的自定义信号。
这是实现该功能的演示脚本:
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_())
在我的 Qt 应用程序中,我使用 QCalendarWidget
并且我希望在鼠标进入日历的新单元格时得到通知。我知道 QCalendarWidget
在内部使用 QTableView
继承自 QAbstractItemView 并且它有一个 entered
信号:
This signal is emitted when the mouse cursor enters the item specified by index. Mouse tracking needs to be enabled for this feature to work.
我尝试使用以下代码接收信号:
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()
但是我的回调函数从未被调用过。你知道为什么吗?
我调查了一下,我想我知道为什么会这样。
QCalendarWidget
创建一个名为 QTableView
的私有子类 QCalendarView
并将其实例化为自身的子类。 (此实例称为 qt_calendar_calendarview
。)
如果您查看 QCalendarView
's code (Qt 5),您会看到:
void QCalendarView::mouseMoveEvent(QMouseEvent *event)
{
[...]
if (!calendarModel) {
QTableView::mouseMoveEvent(event);
return;
}
[...]
}
这意味着仅当没有 calendarModel
时,才会调用负责发出 entered
信号的超类 mouseMoveEvent
。所有这些都是 `QCalendarWidget 的实现细节,因此最好不要依赖任何这些。
所以对于解决这个问题的方法,我不确定最好的方法是什么。您必须在 到达 table 之前捕获事件 。这可以使用 QObject.installEventFilter()
或 re-implementing QWidget.mouseMoveEvent()
来完成,但是您不能直接获取模型索引。您可能会为此目的使用 QAbstractItemView.indexAt()
。
QCalendarWidget
class 使用自定义 table-view 绕过正常的 mouse-event 处理程序 - 因此 enetered
信号永远不会发出。但是,可以 work-around 通过使用 event-filter 来发出执行相同操作的自定义信号。
这是实现该功能的演示脚本:
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_())