Hi Panda people!
It’s been some time I wanted to display the Panda window in a gtk Widget to improve the GUI of the things I develop with it. I understood how to do it by stuying astelix’s code for wx given in this thread :
https://discourse.panda3d.org/viewtopic.php?t=5728&highlight=p3ddojo
I wrote a small sample to help me explain my problem (and help those who would be interested in this). This create a basic GTK window with some boxes (you can change this and load another from a glade if you want), and draw the Panda Window on a GtkDrawingArea.
N.B. : At the begining the Panda Window can be badly resized so you juste have to resize the window to make it correct.
import gtk
import sys
import platform
# ==
from pandac.PandaModules import loadPrcFileData, WindowProperties
from direct.task import Task
from direct.showbase.DirectObject import DirectObject
# == PANDA SETUP == #
def setup() :
# We don't want the basic window to open
loadPrcFileData("", "window-type none")
def launch_panda_window(panda_widget, size) :
"""
Configure and create Panda window
Connect to the gtk widget resize event
Load a panda
"""
props = WindowProperties().getDefault()
props.setOrigin(0, 0)
props.setSize(*size)
props.setParentWindow(get_widget_id(panda_widget))
base.openDefaultWindow(props=props)
# ==
panda_widget.connect("size_allocate", resize_panda_window)
# ==
panda = loader.loadModel("panda")
panda.reparentTo(render)
panda.setPos(0, 40, -5)
def resize_panda_window(widget, request) :
""" Connected to resize event of the widget Panda is draw on so that the Panda window update its size """
props = WindowProperties().getDefault()
props.setOrigin(0, 0)
props.setSize(request.width, request.height)
base.win.requestProperties(props)
def setup_events() :
""" Just to print mouse events """
obj = DirectObject()
obj.accept("mouse1" , print_info, ["Left press"])
obj.accept("mouse1-up" , print_info, ["Left release"])
obj.accept("mouse2" , print_info, ["Wheel press"])
obj.accept("mouse2-up" , print_info, ["Wheel release"])
obj.accept("mouse3" , print_info, ["Right press"])
obj.accept("mouse3-up" , print_info, ["Right release"])
obj.accept("wheel_up" , print_info, ["Scrolling up"])
obj.accept("wheel_down", print_info, ["Scrolling down"])
return obj
# == GTK SETUP == #
def get_widget_id(widget) :
""" Retrieve gtk widget ID to tell the Panda window to draw on it """
if platform.system() == "Windows" : return widget.window.handle
else : return widget.window.xid
def gtk_iteration(*args, **kw):
""" We handle the gtk events in this task added to Panda TaskManager """
while gtk.events_pending():
gtk.main_iteration_do(False)
return Task.cont
def launch_gtk_window(size) :
"""
Here we create the gtk window and all its content (usually I load it from a gladefile).
We return the widget where we want the Panda Window to be drawn
"""
# == Main window
window = gtk.Window()
window.resize(*size)
# == A VBox in which we'll put 2 HBox
vbox = gtk.VBox()
window.add(vbox)
# == First HBox with just a Button in it
top_hbox = gtk.HBox()
vbox.pack_start(top_hbox, expand = False)
# ==
image = gtk.Image()
image.set_from_stock(gtk.STOCK_OK, gtk.ICON_SIZE_BUTTON)
button = gtk.Button()
button.set_image(image)
button.connect("clicked", toggle_print_mouse_infos)
top_hbox.pack_start(button)
# == Second HBox with some gtk thing on the left and the area for Panda on the right
bottom_hbox = gtk.HBox()
vbox.pack_end(bottom_hbox, expand = True)
# ==
left_panel = gtk.TreeView()
left_panel.set_model(gtk.TreeStore(str, object, object))
column = gtk.TreeViewColumn("Name", gtk.CellRendererText(), text = 0)
column.set_sort_column_id(0)
left_panel.append_column(column)
# ==
bottom_hbox.pack_start(left_panel, expand = False)
# ==
drawing_area = gtk.DrawingArea()
bottom_hbox.pack_end(drawing_area)
# ==
def quit(*args, **kw) :
window.destroy()
sys.exit(0)
# ==
window.connect("delete-event", quit)
window.show_all()
# ==
gtk_iteration()
# ==
return drawing_area
# == INFO == #
mouse_info = False
def toggle_print_mouse_infos(*args, **kw) :
global mouse_info
mouse_info = not mouse_info
print "\n\n Printing Mouse Infos : %s \n\n" % mouse_info
def mouse_info_task(*args, **kw) :
global mouse_info
if mouse_info :
md = base.win.getPointer(0)
print "Mouse in window: ", md.getInWindow()
print "Mouse position : ", md.getX(), md.getY()
# ==
return Task.cont
def print_info(message) :
print message
# == MAIN == #
def main() :
setup()
# ==
import direct.directbase.DirectStart
# ==
size = (800, 600)
drawing_area = launch_gtk_window(size)
launch_panda_window(drawing_area, size)
# ==
direct_object = setup_events()
taskMgr.add(mouse_info_task, "print info")
# ==
taskMgr.add(gtk_iteration, "gtk")
# ==
run()
if __name__ == '__main__':
main()
Not that complicated in fact. When we do this, there are some things to handle differently.
1°) Keyboard events are not caught by Panda but by GTK but with an GtkAccelGroup on the window catching keyboard inputs in GTK works kinda great.
2°) On the contrary, Mouse events aren’t caught by GTK but Panda when the mouse is above the Panda window.
But there are some differences between Linux and Windows. 2 examples:
- I have a DirectObject accepting wheelp-up and wheel-down events. It works on Linux but not on WIndows.
- Other difference, on Windows, the base.win.movePointer function works good, but on Linux, it only works when a mouse button is clicked.
- On linux, when a mouse button is clicked of the wheel turned, MouseData.getInWindow return False.
So point 1 is not annoying for me because, like I said, AccelGroup works great.
But point 2 is really a problem because I can’t use the mouse as I did when Panda wasn’t in GTK.
I’m digging to see if thiese problem comes from GTK, Panda or OS Windows system but if anyone has an idea, it’d wee grateful for life
Benjamin