keyPressEvent() with QCalendarWidget

Phil Thompson phil at riverbankcomputing.com
Mon Nov 7 12:26:25 GMT 2022


On 05/11/2022 23:09, Maurizio Berti wrote:
> [ "crossposted" as John (who I hope doesn't mind) did reply privately, 
> but
> I believe that this might be useful to others]
> 
> Il giorno sab 5 nov 2022 alle ore 19:48 John F Sturtz <john at sturtz.org> 
> ha
> scritto:
> 
>> Your method of installing an event filter worked of course.  But
>> continuing down the path of idle intellectual curiosity ...
>> 
>> Now knowing that the calendar widget has an underlying QTableView, I 
>> might
>> have thought I could get the same effect by reassigning the view's
>> keyPressEvent() method.  Something like this:
>> 
>> class Calendar(QCalendarWidget):
>> 
>>     def __init__(self, parent=None):
>>         super().__init__(parent)
>> 
>>         self.setDateEditEnabled(False)
>> #       self.findChild(QAbstractItemView).installEventFilter(self)
>>         self.findChild(QAbstractItemView).keyPressEvent = 
>> self.key_press
>> 
>> #   def eventFilter(self, obj, event):
>> #       if (
>> #           event.type() == event.KeyPress
>> #           and event.key() == Qt.Key_Space
>> #       ):
>> #           print('space pressed!')
>> #           return True
>> #       return super().eventFilter(obj, event)
>> 
>>     def key_press(self, event):
>>         key = event.key()
>>         mod = int(event.modifiers())
>>         handled = False
>> 
>>         print('key pressed!')
>> 
>>         if key == Qt.Key_Space:
>>             print('space pressed!')
>>             handled = True
>> 
>>         if not handled:
>>             super().keyPressEvent(event)
>> 
>> I realize that brute-force reassigning the keyPressEvent() method (as
>> opposed to subclassing and re-implementing) is ugly.  And I probably
>> wouldn't do it for real.  But I have done it on occasion just for 
>> purposes
>> of mucking around (including just now, in another test file that I 
>> have).
>> And it usually works.  I'm a little surprised that it doesn't here.
>> 
> 
> I cannot provide a complete explanation about the exact reason for 
> which it
> doesn't work, as my knowledge of C++ is limited to reading and
> understanding what code does. Also, I don't know how exactly sip works 
> with
> overridden C++ functions.
> 
> That said, the reason is caused by the fact that the view used in
> QCalendarWidget is actually a private subclass (named QCalendarView) 
> which
> on its own overrides keyPressEvent.
> My understanding is that, since that class is not publicly declared, 
> you
> cannot override any of its methods, and you can only use its functions 
> by
> explicitly calling them (like installing the event filter).
> It's possible that this is related to the "function reference caching" 
> (I
> don't know the exact term) that happens when you try to override a 
> function
> *after* it's been already called: if you overwrite mousePressEvent 
> *after*
> the default implementation has been called, it will not work. This is
> obviously not the case, since you're overwriting it before it can be
> called, but it's possible that it has a similar cause.
> 
> If Phil or anybody with enough knowledge of C++/sip would like to 
> provide
> more precise explanations about this, I'll be glad to read it.

It's not because of the private sub-class (Python would treat it as the 
nearest super class that it knows about) it's because the instance was 
created by C++ code rather than by Python code.

For every C++ class that has virtuals SIP defines a sub-class that has 
an implementation of each virtual that, when called, sees if there is a 
Python method of the same name. If there is it calls the Python method, 
if not it calls the original C++ implementation (in the super-class). 
When you create an instance of a C++ class from Python you are actually 
creating an instance of the SIP-generated sub-class. If C++ has created 
the instance then it is of the original class and there is no mechanism 
to translate the C++ virtuals to Python methods.

Phil


More information about the PyQt mailing list