I have a QPlainTextEdit and want to process the content when it loses focus. I've seen that I can either do this with the focusChanged event or with the focusOutEvent virtual function.
I don't know how to pass parameters with the new syntax (i.e. my_app.focusChanged.connect(my_handler)
where my_handler
is a locally defined function). So I tried working with the virtual function.
Since the interface is created with QT Designer inheriting QPlainTextEdit
would be an overkill, so I tried to override the virtual function by simply using my_text_edit.focusOutEvent = my_handler
. This did intercept the message as I wanted, but it apparently overwrote some built in functionality in QPlainTextEdit
and I get artefacts - namely the cursor from the text edit does not disappear when it looses focus. I figured that I should somehow call the original event and what worked for me was the following:
In my __init__
method I have:
self.original_handler = self.my_text_edit.focusOutEvent
self.my_text_edit.focusOutEvent = self.my_handler
The definition of my_handler
is:
def my_handler(self, event):
self.original_handler(event)
# my own handling follows...
I am basically replicating what I expect the library to do for me. I find this too clumsy and I can see how it can backfire in many ways during maintenance. Can anyone suggest a neater way to do it? Thanks!
Personally, I never use the monkey-patching style of overriding virtual methods, but the correct way to retain the original behaviour would be to call the base-class method directly, like this:
def my_handler(self, event):
QtGui.QPlainTextEdit.focusOutEvent(self.my_text_edit, event)
# my own handling follows...
I don't understand why you can't use the focusChanged
signal, though. The handler for it would simply be:
def my_handler(self, old, new):
if old is self.my_text_edit:
print('focus out')
elif new is self.my_text_edit:
print('focus in')
However, my own preference would be to use an event filter:
class Window(QtGui.QMainWindow)
def __init__(self):
...
self.my_text_edit.installEventFilter(self)
def eventFilter(self, source, event):
if (event.type() == QtCore.QEvent.FocusOut and
source is self.my_text_edit):
print('eventFilter: focus out')
# return true here to bypass default behaviour
return super(Window, self).eventFilter(source, event)
This is a much more flexible solution, which provides a generic way to handle any event type for any widget that has been imported via Qt Designer (or, in fact, any widget that you just don't want to subclass).
There is also the possibility of widget promotion, which can be used to directly replace the widgets in generated ui modules with your own subclasses. This would then allow you to override any virtual methods via inheritance, rather than monkey-patching individual instances. If you are importing several widgets of the same type from Qt Designer which share a lot of custom functionality, this can provide a very clean solution.
A simple explanation of how to do widget promotion in PyQt can be found in my answer to: Replace QWidget objects at runtime.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With