Create contextMenu on lots of buttons PyQt - QtDesigner - Maya

2,246 views
Skip to first unread message

mAtt RINGOT

unread,
May 2, 2014, 5:20:14 PM5/2/14
to python_in...@googlegroups.com

Hey guys,

I hope you are well !

I'm currently trying to create quite a lot of contextMenu for lots of different buttons in a maya UI I'm working on.

The issue is that I've a long list of buttons and I need the same contextMenu for all of them.... What I'm doing now is not really elegant, that's why I'm asking if you could help me...

Because with the following process, my script will be reallllyyy realllyyy long but I can't figure out how to clean that properly :(

Here's an example of my code :

from PyQt4 import QtCore,QtGui,uic
from functools import partial
import os


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

uiFilePath = os.path.join(os.path.dirname(__file__),"myfile.ui") 
self.ui = uic.loadUi(uiFilePath, self)
'''
Right Click Menu
'''
self.ui.buttonA.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ui.buttonA.customContextMenuRequested.connect(self.buttonAMenu)
self.ui.buttonB.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.ui.buttonB.customContextMenuRequested.connect(self.buttonBMenu)
'''
BUTTON A
'''
@QtCore.pyqtSlot()
def on_buttonA_released(self):
print ('Doing Stuff when clicking on Button A')
def buttonAMenu(self, pos):
menu = QtGui.QMenu()
menu.addAction('First Action', lambda:self.FirstActionButtonA(objects))
menu.addAction('Second Action', lambda:self.SecondActionButtonA(objects))
menu.exec_(QtGui.QCursor.pos())
def FirstActionButtonA(self, objects):
print ('First Action working on :')
print (objects)
def SecondActionButtonA(self):
print ('Second Action working on :')
print (objects)
'''
BUTTON B
'''
@QtCore.pyqtSlot()
def on_buttonB_released(self):
print ('Doing Stuff when clicking on Button B')
def buttonBMenu(self, pos):
menu = QtGui.QMenu()
menu.addAction('First Action', lambda:self.FirstActionButtonB(objects))
menu.addAction('Second Action', lambda:self.SecondActionButtonB(objects))
menu.exec_(QtGui.QCursor.pos())
def FirstActionButtonB(self, objects):
print ('First Action working on :')
print (objects)
def SecondActionButtonB(self):
print ('Second Action working on :')
print (objects)


Sorry, the code highlighting seems to not work...

Thanks a lot for your help !

m.

Justin Israel

unread,
May 2, 2014, 6:50:44 PM5/2/14
to python_in...@googlegroups.com
If you have the exact same menu to show for all the buttons then you could just handle the menu from the window instead of individually for each button:

class Window(QtGui.QDialog):

    def __init__(self):
        super(Window, self).__init__()
        self.resize(800,600)

        layout = QtGui.QVBoxLayout(self)
        for i in xrange(10):
            button = QtGui.QPushButton("Button %d" % i, self)
            layout.addWidget(button)

    def contextMenuEvent(self, event):
        child = self.childAt(event.pos())
        if isinstance(child, QtGui.QPushButton):
            event.accept()
            self._showButtonMenu(child, event.globalPos())
            return

        super(Window, self).contextMenuEvent(event)

    def _showButtonMenu(self, button, pos):
        menu = QtGui.QMenu()
        menu.addAction('First Action')
        menu.addAction('Second Action')
        menu.exec_(pos)




--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/cd18e5e1-8b14-4e74-9017-9dac18a6c69a%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Manuel Macha

unread,
May 3, 2014, 4:47:01 AM5/3/14
to python_in...@googlegroups.com
Alternatively you could sub-class QPushButton,
implement the context-menu and create your buttons from the sub-class instead of from QPushButton.


mAtt RINGOT

unread,
May 3, 2014, 6:58:42 AM5/3/14
to python_in...@googlegroups.com
Waow, thanks a lot for your quick reply !!

I will have a try !

Thanks a lot, I let you know !
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.

mAtt RINGOT

unread,
May 3, 2014, 3:01:38 PM5/3/14
to python_in...@googlegroups.com
Hey guys,

It works really nicely but now I would like to have the contextMenu only on some of the buttons, not all of them, can I do that from a list of string ?... And also, how can I connect buttons from a list of string to the same command and passing the button Name as an argument ?

pretty much like (in a perfect and too easy world ahah) :

        buttonList= [self.ui.buttonA, self.ui.buttonB, self.ui.buttonC]
        for btn in buttonList:
            btn.clicked.connect(self.doStuff(btn))

Thanks a lot for your time !

m.

Justin Israel

unread,
May 3, 2014, 5:39:26 PM5/3/14
to python_in...@googlegroups.com

What you want to do will work just fine if you use functools.partial

#---
from functools import partial
...
btn.clicked.connect(partial(self.doStuff, btn))
#---

Or you can use the self.sender() call to find out who emitted the signal :

#----
def doStuff(self):
    btn = self.sender()

btn.clicked.connect(self.doStuff)
#----

You have to be a bit aware of the nuisances of using partial. Normally when you connect a signal from one object to the method of another, and the slot object gets deleted, Qt will clean up the connection for you. But if you wrap it in partial, then Qt only sees a function object and doesn't clean it up when the object it wraps is deleted. Its not a problem for things like this where all your buttons live on the parent and all stay together for the same life cycle. But for something where you create temporary objects and connect signals, partial can make them stay connected and still fire on a deleted reference.
I've also read that the same situation can cause a memory leak, since the partial is holding on to the reference of an object, and the signal slot is not being cleaned up, the object reference   never hits zero so it stays around.


>>>>> To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.


>>>>> To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/cd18e5e1-8b14-4e74-9017-9dac18a6c69a%40googlegroups.com.
>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>
>>>>
>>>> --
>>>> You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.

>>>> To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.

> --
> You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.

> To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/02e91e83-ca91-4f41-880f-ba4448233210%40googlegroups.com.

mAtt RINGOT

unread,
May 4, 2014, 1:33:51 PM5/4/14
to python_in...@googlegroups.com
It worked perfectly ! I used the self.sender method instead of the partial. Thank you for your really great and clear reply !

I'm also listing all the children from my widget instead of having a long list of strings using :

buttonList = self.ui.myWidget.findChildren(QtGui.QPushButton)

I would love to ask you more few things if you don't mind, all of that helped me a lot...

- Is there a way to add a 'legend' when the mouse is on a button for 2 sec. You just put the mouse on the button and a legend appear explaining what the button is doing. (It's not for all the buttons, only for some of them with a special legend for each.)

- And also, I've lot's of buttons selecting controls in maya. All of them are in a QWidget. Could I deselect everything when I click in the QWidget, I mean, click everywhere but not on a button will deselect all controls in maya? Do you know what I mean ? Just get a callBack when there's a click in the QWidget ?
I tried with something like we add on top but I don't get in the previous case when contextMenuEvent(self, event) is called :

    def deselectControlsEvent(self, event):
        child = self.childAt(event.pos())
        if isinstance(child, QtGui.QWidget):
            event.accept()
            print event.globalPos()
            print 'QWidget Clciked'
            return 

This is not working....

Thanks again !!

m.

>>>>> To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.


>>>>> To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/cd18e5e1-8b14-4e74-9017-9dac18a6c69a%40googlegroups.com.
>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>
>>>>
>>>> --
>>>> You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.

>>>> To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.


>>>> To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAPGFgA0gZZPBoLV-N66t_62oAhvCjgiFPoczJHr_QHBvh57WXA%40mail.gmail.com.
>>>>
>>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>>
> --
> You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.

> To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.

Justin Israel

unread,
May 4, 2014, 3:45:09 PM5/4/14
to python_in...@googlegroups.com


On May 5, 2014 5:33 AM, "mAtt RINGOT" <matt....@gmail.com> wrote:
>
> It worked perfectly ! I used the self.sender method instead of the partial. Thank you for your really great and clear reply !
>
> I'm also listing all the children from my widget instead of having a long list of strings using :
>
> buttonList = self.ui.myWidget.findChildren(QtGui.QPushButton)
>
> I would love to ask you more few things if you don't mind, all of that helped me a lot...
>
> - Is there a way to add a 'legend' when the mouse is on a button for 2 sec. You just put the mouse on the button and a legend appear explaining what the button is doing. (It's not for all the buttons, only for some of them with a special legend for each.)
>

This sounds like you just want to make use of the tooltip.  They are displayed when you hover a QWidget for a moment. They also support rich text like html so you can define something a little more formatted.
self.button.setToolTip()
Make sure the first part of the text is an html tag if you want it to detect as html.
If you want even more control to do something when they hover,  then you can implement the event() method on QWidget and check for event.type() == event.ToolTip.  And then do whatever you want.

> - And also, I've lot's of buttons selecting controls in maya. All of them are in a QWidget. Could I deselect everything when I click in the QWidget, I mean, click everywhere but not on a button will deselect all controls in maya? Do you know what I mean ? Just get a callBack when there's a click in the QWidget ?
> I tried with something like we add on top but I don't get in the previous case when contextMenuEvent(self, event) is called :
>
>     def deselectControlsEvent(self, event):
>         child = self.childAt(event.pos())
>         if isinstance(child, QtGui.QWidget):
>             event.accept()
>             print event.globalPos()
>             print 'QWidget Clciked'
>             return 
>
> This is not working....
>

Unfortunately you can't make up descriptive event names and have Qt figure out what you want :-)

Maybe you want to implement the mousePressEvent() method on your main widget. It should only be catching events for the times that the buttons didn't catch them first. Events travel up from the original target,  to each parent until someone handles it.

Make sure to read the docs to see all the available methods you can use on a widget. Don't forget to check the parent class for more functionality that was inherited.

>> >>>>> To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.


>> >>>>> To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/cd18e5e1-8b14-4e74-9017-9dac18a6c69a%40googlegroups.com.
>> >>>>> For more options, visit https://groups.google.com/d/optout.
>> >>>>
>> >>>>
>> >>>> --
>> >>>> You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.

>> >>>> To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.


>> >>>> To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAPGFgA0gZZPBoLV-N66t_62oAhvCjgiFPoczJHr_QHBvh57WXA%40mail.gmail.com.
>> >>>>
>> >>>> For more options, visit https://groups.google.com/d/optout.
>> >>>
>> >>>
>> > --
>> > You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.

>> > To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.

> --
> You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.

> To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/7a7b12b0-8e98-48a4-98aa-14c211420ea1%40googlegroups.com.

mAtt RINGOT

unread,
May 4, 2014, 4:26:45 PM5/4/14
to python_in...@googlegroups.com
The toolTip is perfect :D

About the mousePressEvent() I've linked a clear selection to this command and it works too :D

    def mousePressEvent(self, event):
        self.offset = event.pos()
        QtGui.QWidget.mousePressEvent(self, event)
        cmds.select(clear=True)

Thanks for everything :D

>> >>>>> To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.


>> >>>>> To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/cd18e5e1-8b14-4e74-9017-9dac18a6c69a%40googlegroups.com.
>> >>>>> For more options, visit https://groups.google.com/d/optout.
>> >>>>
>> >>>>
>> >>>> --
>> >>>> You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.

>> >>>> To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.


>> >>>> To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAPGFgA0gZZPBoLV-N66t_62oAhvCjgiFPoczJHr_QHBvh57WXA%40mail.gmail.com.
>> >>>>
>> >>>> For more options, visit https://groups.google.com/d/optout.
>> >>>
>> >>>
>> > --
>> > You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.

>> > To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.


>> > To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/02e91e83-ca91-4f41-880f-ba4448233210%40googlegroups.com.
>> >
>> > For more options, visit https://groups.google.com/d/optout.
>
> --
> You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.

> To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.

Justin Israel

unread,
May 4, 2014, 6:22:39 PM5/4/14
to python_in...@googlegroups.com

Awesome. Glad it worked!

To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/41e53f18-ad84-4871-a8f7-dd977099a414%40googlegroups.com.

Chris Gardner

unread,
May 5, 2014, 4:08:04 AM5/5/14
to python_in...@googlegroups.com, python_in...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages