Click-Blocking Menu [SOLVED]

Hello folks. I’ve got a little dilemma I could use assistance with. I’m creating a menu on one side of my panda3d window by simply covering it with a DirectFrame. I have buttons on the menu. I also have mouse picking in the 3D view. Meaning, when you click on something in the 3D view, stuff happens. I’m using a collision ray that extends from the camera into the scene and checking collisions when a mouse click occurs to do the mouse picking.

I’m trying to figure out a way to prevent a mouse click on a menu button from causing any mouse picking events in the 3D scene that is behind the menu. Basically, when the mouse is over the menu, I want to stop doing mouse picking.

My initial thought was to simply check the mouse position in the window and disallow mouse picking when the mouse was beyond a certain screen position, but this only works for a static window size. If the aspect ratio of the window changes, the system breaks down.

I have also considered placing a collision object in front of the camera, under the menu, so that it is the nearest object to collide with. When the mouse picking occurs I can check to see if the object is the first collision object and if so, ignore the mouse picking. The problem with this idea is properly sizing and positioning the object, which would probably have to be a polygon.

Anyone have any tips or ideas for a good solution to this?

Just pass state = DGG.NORMAL to your DirectFrame constructor. This makes the DirectFrame itself clickable (you can bind an event to it if you like), but another side-effect of it being clickable is that it is no longer transparent to the mouse. If you’re listening for “mouse1”, you won’t hear it if you click within the frame.


You can receive mouse in-out events using this:

from direct.gui.DirectGui import DGG
frame = DirectFrame(...)

def mouseEnter(mwr):
    print "Mouse entered frame!"

def mouseExit(mwr):
    print "Mouse left frame!"

frame.bind(DGG.ENTER, mouseEnter)
frame.bind(DGG.EXIT, mouseExit)

Best of luck,

Aha, that’s an elegant solution. Thanks.

I tried both of these solutions, and I couldn’t get either one to work. I didn’t generate errors, it’s just that nothing changed. The solution DRWR suggested is the one I’d really like to get working. I changed my definition of the frame to

self.bttnFrame = DirectFrame(frameSize = (-.5,0,-2,0), frameColor = (.1,.1,.1,1),
			state = DGG.NORMAL, parent = base.a2dTopRight)

But I still get event response when I click on the frame. I’m not sure what’s wrong here, since I’m getting no errors or anything.

Edit: Does the frame have to have a geom for this to work? I think I’ll try that.

Edit2: Using a geom doesn’t help, I still click straight through the menu despite the state= DGG.NORMAL

Here is an example showing clearly how specifying the state will block mouse events for you. :wink:

from direct.directbase import DirectStart
from direct.gui.DirectGui import DirectFrame
from direct.gui.DirectGui import DGG

frame = DirectFrame(state = DGG.NORMAL, frameSize = (-0.3, 0.3, -0.3, 0.3))

def onMouseClick():
	print 'Mouse clicked!'

base.accept('mouse1', onMouseClick)


Clicking on the frame never prints ‘Mouse clicked!’ whereas clicking anywhere other than the frame does.
Hope this helps,

Alright, I found the reason behind the problem with state = DGG.NORMAL. That only silences the mouse1 event, not the mouse1-up event. I typically use the up event of a click, rather than the down event, because most programs operate that way.

Still not sure why the binding of DGG.ENTER and DGG.EXIT didn’t work correctly. Any thoughts on that?

Panda never blocks the mouse1-up event, or any other up event, because it might be needed to track the state of the mouse button. Imagine you have code that is listening to ‘mouse1’ and ‘mouse1-up’ to determine whether the mouse button is currently down or up. And then imagine that the user clicks the mouse down over the empty space in the screen, generating ‘mouse1’, and then moves the mouse over a frame and release it. If Panda suppressed the mouse1-up event, then your code would be fooled into thinking the mouse1 button was forever down.

If you really want to do some action on mouse1-up instead of mouse1, you have to listen for both mouse1 and mouse1-up. Receiving mouse1 will engage the action, and a subsequent mouse1-up will activate it. A mouse1-up event received on its own should do nothing. This is actually the way that most GUI systems process events (they even usually draw some visible change in response to the mouse1 event, so you can see that you’re about to do something).

As to the DGG.ENTER and DGG.EXIT, that should work too, but again only if you specify state = DGG.NORMAL. Without that, the frame is completely invisible to the mouse.


Alright, that solves all the mysteries. Thanks again, David.

For anyone interested, I achieved the exact results I wanted by setting state = DGG.NORMAL in my frame and using the following code for click response:

self.accept("mouse1", self.LCPrep)

def LCPrep(self):
  self.acceptOnce("mouse1-up", self.LCResponse)

def LCResponse(self):

Admittedly, it is possible to click outside the menu, drag the mouse over the menu, and mouse pick something behind the menu. I’m not too worried about that, though.