Discussion:
How to override mousePressEvent for an existing QPushButton widget
Dov Grobgeld
2009-07-30 04:52:40 UTC
Permalink
Hello,

I'm a PyQt/Qt newbie who is trying to port some ideas of mine from (Py)Gtk
to PyQt and I'm having some conceptual problems. The problem that I am
trying to solve is how to add "right click" functionality to a QPushButton
that I received e.g. from the designer. I managed to solve this as follows
when I inherit the widget myself:

class MyButton(QtGui.QPushButton):
def __init__(self, parent=None):
QtGui.QPushButton.__init__(self, parent)
self.setText("Press me")

def mousePressEvent(self,event):
if event.button() == QtCore.Qt.LeftButton:
QtGui.QPushButton.mousePressEvent(self,event)
return
print "Processing right click event"

But how would I do the same override in runtime if I all I have is a button
that is a QPushButton? I tried doing:

button.mousePressEvent = myMousePressEvent

But doing that lead to lots of reference problems with the "self" object,
since the myMousePressEvent is not in the inheritance chain from
QPushButton.

This is actually related to another even more fundamental question, why is
the difference between overriding and connecting? I.e. why is "clicked" a
signal and "mousePressEvent" an overrided virtual. Why can't they both be
signals, in which case the problem I describe wouldn't happen? (That's how
it works in Gtk, btw.)

Thanks in advance!
Dov
Dov Grobgeld
2009-07-30 05:23:41 UTC
Permalink
[stuff deleted]
This is actually related to another even more fundamental question, why is
the difference between overriding and connecting? I.e. why is "clicked" a
signal and "mousePressEvent" an overrided virtual. Why can't they both be
signals, in which case the problem I describe wouldn't happen? (That's how
it works in Gtk, btw.)
I realized that the wording above is both unfortunate and wrong. I
understand that there is a need for virtual functions, and Gtk indeed
provides them as well. But what is less clear to me is why there is a
distinction between "clicked" and "mousePressEvent", and why they are
implemented differently? Anyhow, that has nothing to do with the Py part of
PyQt which simply reflects the underlying API.

Dov
Hans-Peter Jansen
2009-07-30 09:32:52 UTC
Permalink
Post by Dov Grobgeld
[stuff deleted]
This is actually related to another even more fundamental question, why
is the difference between overriding and connecting? I.e. why is
"clicked" a signal and "mousePressEvent" an overrided virtual. Why
can't they both be signals, in which case the problem I describe
wouldn't happen? (That's how it works in Gtk, btw.)
I realized that the wording above is both unfortunate and wrong. I
understand that there is a need for virtual functions, and Gtk indeed
provides them as well. But what is less clear to me is why there is a
distinction between "clicked" and "mousePressEvent", and why they are
implemented differently? Anyhow, that has nothing to do with the Py part
of PyQt which simply reflects the underlying API.
Because, conceptually, the clicked signal is generated in the
mousePressEvent, thus the events are low level notifications, and signals
the standard "high level" way of using this functionality (from apps
perspective).

Since there is no rightClicked signal available, you will want to look into
QWidget.eventFilter() et.al.

A cleaner approach would be to subclass a button widget, override
mousePressEvent and generate the missing signal there. It's quite easy to
add simple widgets to designer (nowadays).

Note, the average user does not expect right click functionality from
buttons, this is why such a signal does not exist in the first place. Thus
your users might never discover it..

Pete
Dov Grobgeld
2009-07-30 17:10:44 UTC
Permalink
Hi Henning,

Thanks for your replies. (I'm returning the conversation to the pyqt list as
I like using old archives for looking for answers to my own questions. :-) )

First of all, let me describe what I am striving for. A while back I wrote
an application called GemTcl that loads user interfaces built through the
Glade Gui builder. GemTcl lets the user right click on all the buttons,
which popups up a context menu which allows the user to edit the a callback
script that is called when left clicking on the button.

And that's what I wanted to replicate in PyQt.

Regarding contextMenuPolicy, how would you use that. I tried the following,
but it didn't work...
class MyApp(QtGui.QWidget):
def clicked(self):
print "Processing left button"

def context_menu(self):
print "Processing context menu"

def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)

self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Icon')

self.button = MyButton(self)
self.button.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)

self.connect(self.button, QtCore.SIGNAL('clicked()'), self.clicked)
self.connect(self.button,
QtCore.SIGNAL('customContextMenuRequested()'), self.context_menu)

Thanks,
Dov
I forgot to mention that every widget also has a
customContextMenuRequested signal. This gets emitted if you set the
contextMenuPolicy to Qt.CustomContextMenu.
Henning Schröder
2009-07-30 17:44:33 UTC
Permalink
Post by Dov Grobgeld
Regarding contextMenuPolicy, how would you use that. I tried the following,
but it didn't work...
  [..]
        self.connect(self.button,
QtCore.SIGNAL('customContextMenuRequested()'), self.context_menu)
You always have to use the complete signature. Please try:
SIGNAL('customContextMenuRequested(const QPoint&)')

Henning
Dov Grobgeld
2009-07-30 18:26:42 UTC
Permalink
Thanks! It seems like this is exactly what I was looking for.

I have to say it is quite annoying that there is neither any compile time
nor any run time error reported for errors in the signal signatures.

Regards,
Dov
Post by Dov Grobgeld
Post by Dov Grobgeld
Regarding contextMenuPolicy, how would you use that. I tried the
following,
Post by Dov Grobgeld
but it didn't work...
[..]
self.connect(self.button,
QtCore.SIGNAL('customContextMenuRequested()'), self.context_menu)
SIGNAL('customContextMenuRequested(const QPoint&)')
Henning
Phil Thompson
2009-07-30 20:44:22 UTC
Permalink
Post by Dov Grobgeld
Thanks! It seems like this is exactly what I was looking for.
I have to say it is quite annoying that there is neither any compile time
nor any run time error reported for errors in the signal signatures.
Regards,
Dov
Use the new-style connection API...

self.button.customContextMenuRequested.connect(self.context_menu)

Phil
Post by Dov Grobgeld
Post by Dov Grobgeld
Post by Dov Grobgeld
Regarding contextMenuPolicy, how would you use that. I tried the
following,
Post by Dov Grobgeld
but it didn't work...
[..]
self.connect(self.button,
QtCore.SIGNAL('customContextMenuRequested()'), self.context_menu)
SIGNAL('customContextMenuRequested(const QPoint&)')
Henning
Dov Grobgeld
2009-07-30 21:43:10 UTC
Permalink
Great! Though I'll have to wait a while until Fedora PyQt is updated to 4.5,
it seems.

Dov
Post by Phil Thompson
Post by Dov Grobgeld
Thanks! It seems like this is exactly what I was looking for.
I have to say it is quite annoying that there is neither any compile time
nor any run time error reported for errors in the signal signatures.
Regards,
Dov
Use the new-style connection API...
self.button.customContextMenuRequested.connect(self.context_menu)
Phil
Post by Dov Grobgeld
Post by Dov Grobgeld
Post by Dov Grobgeld
Regarding contextMenuPolicy, how would you use that. I tried the
following,
Post by Dov Grobgeld
but it didn't work...
[..]
self.connect(self.button,
QtCore.SIGNAL('customContextMenuRequested()'), self.context_menu)
SIGNAL('customContextMenuRequested(const QPoint&)')
Henning
Loading...