WxPython hosting a Panda3D display

I tried using this method, but when ever I exit the window created by wxPython, the application actually didn’t stop. I checked the Task Manager, there is still python.exe process running, and consuming quite a number of memory. Is there a new implementation for Panda 1.5.3 on using wxPython?

It has nothing to do with Panda.
If you want to shutdown Python when user closes wxPython-created window, you must respond to its close event.

self.parent.Bind(wx.EVT_CLOSE,sys.exit)

Import sys first.

wow… Stupid me… Thanks for the help! :slight_smile:

I have done some small modifications to the code, because on my System (Windows Vista) the 3D-Area always has a big white Border around. I have set the Origin to (0,0). Also the 3D-Window is now resized automatically.

import wx
import time

import direct
from pandac.PandaModules import *
loadPrcFileData('startup', 'window-type none')
from direct.directbase.DirectStart import *
from direct.showbase import DirectObject

class App(wx.PySimpleApp, DirectObject.DirectObject):
    def __init__(self):
        wx.PySimpleApp.__init__(self)

        #Create a new event loop (to overide default wxEventLoop)
        self.evtloop = wx.EventLoop()
        self.old = wx.EventLoop.GetActive()
        wx.EventLoop.SetActive(self.evtloop)
        taskMgr.add(self.wx, "Custom wx Event Loop")

    # wxWindows calls this method to initialize the application
    def OnInit(self):
        self.SetAppName('Editor')
        self.SetClassName('MyAppClass')

        self.parent = wx.MDIParentFrame(None, -1, 'Editor')
        self.child = wx.MDIChildFrame(self.parent, -1, 'Panda3D')

        #Hauptfenster --> Main Window
        self.parent.SetClientSize((1024, 768))
        self.parent.Show(True)

        #Panda Unter Fenster --> Panda Child Window
        self.child.SetClientSize((640, 480))
        self.child.Show(True)
            

        base.windowType = 'onscreen'
        props = WindowProperties.getDefault()
        props.setParentWindow(self.child.GetHandle())
        props.setOrigin(0,0) #
        base.openDefaultWindow(props = props)
        base.setFrameRateMeter(True) #base is panda

        #Panda Child infos
        self.child.Bind(wx.EVT_SIZE, self.childSize)

        return True

    def wx(self, task):
        while self.evtloop.Pending():
            self.evtloop.Dispatch()
        #time.sleep(0.01)
        self.ProcessIdle()
        return task.cont

    def childSize(self,event):
        size = event.GetSize()
        props = WindowProperties()
        props.setOrigin(0,0)
        props.setSize(size.width-10, size.height-10)
        base.win.requestProperties(props)

    def close(self):
        wx.EventLoop.SetActive(self.old)
        self.parent.Bind(wx.EVT_CLOSE,sys.exit)

app = App()
run()

Hi Folks,

some trouble with passing control between panda and wxPython

I took the above code

#!/usr/bin/env python
#Boa:PyApp:main

modules ={u'MDIParentFrame1': [0, '', u'MDIParentFrame1.py'],
 u'MDIParentFrame2A': [0, '', u'../Shadows/MDIParentFrame2A.py']}



import wx
import time

import direct
from pandac.PandaModules import *
loadPrcFileData('startup', 'window-type none')
from direct.directbase.DirectStart import *
from direct.showbase import DirectObject
import MDIParentFrame1
import MDIParentFrame2A

class App(wx.PySimpleApp, DirectObject.DirectObject):
    def __init__(self):
        wx.PySimpleApp.__init__(self)

        #Create a new event loop (to overide default wxEventLoop)
        self.evtloop = wx.EventLoop()
        self.old = wx.EventLoop.GetActive()
        wx.EventLoop.SetActive(self.evtloop)
        taskMgr.add(self.wx, "Custom wx Event Loop")

    # wxWindows calls this method to initialize the application
    def OnInit(self):
        self.SetAppName('Editor')
        self.SetClassName('MyAppClass')

        #self.parent = wx.MDIParentFrame(None, -1, 'Editor')
        self.parent = MDIParentFrame1.create(None)
        #self.parent = MDIParentFrame2A.create(None)
        self.child = wx.MDIChildFrame(self.parent, -1, 'Panda3D')

        #Hauptfenster --> Main Window
        self.parent.SetClientSize((1024, 768))
        self.parent.Show(True)

        #Panda Unter Fenster --> Panda Child Window
        self.child.SetClientSize((640, 480))
        self.child.Show(True)
           

        base.windowType = 'onscreen'
        props = WindowProperties.getDefault()
        props.setParentWindow(self.child.GetHandle())
        props.setOrigin(0,0) #
        base.openDefaultWindow(props = props)
        base.setFrameRateMeter(True) #base is panda

        #Panda Child infos
        self.child.Bind(wx.EVT_SIZE, self.childSize)

        return True

    def wx(self, task):
        while self.evtloop.Pending():
            self.evtloop.Dispatch()
        time.sleep(0.01)
        self.ProcessIdle()
        return task.cont

    def childSize(self,event):
        size = event.GetSize()
        props = WindowProperties()
        props.setOrigin(0,0)
        props.setSize(size.width-10, size.height-10)
        base.win.requestProperties(props)

    def close(self):
        wx.EventLoop.SetActive(self.old)
        self.parent.Bind(wx.EVT_CLOSE,sys.exit)

app = App()
run() 

and a wx MDIParentFrame with just a panel and 2 buttons

#Boa:MDIParent:MDIParentFrame1

import wx

def create(parent):
    return MDIParentFrame1(parent)

[wxID_MDIPARENTFRAME1, wxID_MDIPARENTFRAME1BUTTON1, 
 wxID_MDIPARENTFRAME1BUTTON2, wxID_MDIPARENTFRAME1PANEL1, 
] = [wx.NewId() for _init_ctrls in range(4)]

class MDIParentFrame1(wx.MDIParentFrame):
    def _init_ctrls(self, prnt):
        # generated method, don't edit
        wx.MDIParentFrame.__init__(self, id=wxID_MDIPARENTFRAME1, name='',
              parent=prnt, pos=wx.Point(685, 307), size=wx.Size(837, 656),
              style=wx.DEFAULT_FRAME_STYLE | wx.VSCROLL | wx.HSCROLL,
              title='MDIParentFrame1')
        self.SetClientSize(wx.Size(829, 622))

        self.panel1 = wx.Panel(id=wxID_MDIPARENTFRAME1PANEL1, name='panel1',
              parent=self, pos=wx.Point(0, 0), size=wx.Size(800, 504),
              style=wx.TAB_TRAVERSAL)

        self.button1 = wx.Button(id=wxID_MDIPARENTFRAME1BUTTON1,
              label='button1', name='button1', parent=self.panel1,
              pos=wx.Point(688, 24), size=wx.Size(75, 23), style=0)
        self.button1.Bind(wx.EVT_BUTTON, self.OnButton1Button,
              id=wxID_MDIPARENTFRAME1BUTTON1)

        self.button2 = wx.Button(id=wxID_MDIPARENTFRAME1BUTTON2,
              label='button2', name='button2', parent=self.panel1,
              pos=wx.Point(688, 56), size=wx.Size(75, 23), style=0)

    def __init__(self, parent):
        self._init_ctrls(parent)

    def OnButton1Button(self, event):
        self.log.write("Click! (%d)\n" % event.GetId())

that Button 2 is not reacting is evident but Button 1 also shows no reaction.

Greatful for any help

Martin

Hi,

I don’t have a solution for you, but here’s a bit of wx + panda code I’ve just got working; there don’t seem to be a whole ton of examples out there so hopefully this will be helpful. This is an example of loading an XRC file for the interface, and inserting a Panda window into it. The XRC file has a main window and a “properties” panel; this code sets up a splitter in the main window and inserts the properties panel and a panda panel into it.

import wxversion
wxversion.select('2.8')
import wx
from wx import xrc

from direct.showbase import DirectObject
from pandac.PandaModules import *
loadPrcFileData("", "window-type none")

from direct.directbase import DirectStart

class P3dViewport(wx.Panel):
  # this panel contains the panda 3d window
  def __init__(self, *args, **kwargs):
    wx.Panel.__init__(self, *args, **kwargs)

  def initialize(self):
    # the panda3d window must be put into the panel after the wx-window has
    # been created
    assert self.GetHandle() != 0

    # put the panda3d 
    wp = WindowProperties()
    wp.setOrigin(0, 0)
    wp.setSize(self.ClientSize.GetWidth(), self.ClientSize.GetHeight())
    wp.setParentWindow(self.GetHandle())
    base.openDefaultWindow(props = wp, gsg = None)

    # attach the resize event
    wx.EVT_SIZE(self, self.OnResize)

  def OnResize(self, event):
    # when the wx-panel is resized, fit the panda3d window into it
    frame_size = event.GetSize()
    wp = WindowProperties()
    wp.setOrigin(0, 0)
    wp.setSize(frame_size.GetWidth(), frame_size.GetHeight())
    base.win.requestProperties(wp)

class EditorApp(DirectObject.DirectObject):
  def __init__(self):

    self.wxApp = wx.App(redirect = False)
    self.wxApp.SetAppName("Panda Editor")
    self.wxApp.SetClassName("PEditor")

    self.appInit()

    self.res = xrc.XmlResource ('viewer.xrc')
    self.MainWindow = self.res.LoadFrame(None,'MainWindow')
    self.MainWindow.Bind(wx.EVT_CLOSE, self.quit)
    
    
    self.MainWindow.Show()  
    self.PropertiesPanel=self.res.LoadPanel(self.MainWindow,'PropertiesPanel')
    
    self.MainSplitter = wx.SplitterWindow(self.MainWindow,style=wx.SP_3D | wx.SP_BORDER)
    self.MainSplitter.SetMinimumPaneSize(200)
    self.p3dViewport = P3dViewport(self.MainSplitter)
    self.PropertiesPanel.Reparent(self.MainSplitter)
    self.MainSplitter.SplitVertically (self.p3dViewport,self.PropertiesPanel,-220)
    
    
    sizer = wx.BoxSizer(wx.VERTICAL)

    sizer.Add(self.MainSplitter,1,wx.EXPAND,0)
    self.MainWindow.SetSizer(sizer)
    self.MainWindow.Layout()

    # update the wx-window and run a step (window created?)
    self.MainWindow.Update()
    self.wxStep()
    # update the p3d window
    self.p3dViewport.initialize()

    # do something with the panda3d view
    loader.loadModel('environment').reparentTo(render)



  def appInit(self):
    # Create a new event loop (to overide default wxEventLoop)
    self.evtLoop = wx.EventLoop()
    self.oldLoop = wx.EventLoop.GetActive()
    wx.EventLoop.SetActive(self.evtLoop)
    taskMgr.add(self.wxStep, "evtLoopTask")

  def onDestroy(self, evt):
    # called on wx window destroy
    wx.EventLoop.SetActive(self.oldLoop)
    
  def quit(self,event):
    self.onDestroy(event)
      
      # to close Panda
    try:
      base
    except NameError:
      sys.exit()
        
    base.userExit()

  def wxStep(self, task = None):
    # process the events wx sends
    while self.evtLoop.Pending():
      self.evtLoop.Dispatch()
    self.wxApp.ProcessIdle()
    if task != None: return task.cont
    

editorApp = EditorApp()
run()

Hi benchang,

could you include the .xrc as well? To analyse what’s going on this would be verry verry helpful.

Thanks

Martin

oh, yes - here you go. uh, I don’t see how to attach files so I’ll just paste it in. Sorry, it’s pretty long … save this as “viewer.xrc”. None of the gui elements do anything yet with this code, it’s just a starting point.

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<resource xmlns="http://www.wxwindows.org/wxxrc" version="2.3.0.1">
	<object class="wxFrame" name="MainWindow">
		<style>wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL</style>
		<size>783,560</size>
		<title></title>
		<object class="wxMenuBar" name="m_menubar1">
			<label>MyMenuBar</label>
			<object class="wxMenu" name="m_file">
				<label>File</label>
				<object class="wxMenuItem" name="m_menuItem141">
					<label>&amp;New</label>
					<help></help>
				</object>
				<object class="wxMenuItem" name="m_menuItem151">
					<label>&amp;Open</label>
					<help></help>
				</object>
				<object class="wxMenuItem" name="m_menuItem16">
					<label>&amp;Save</label>
					<help></help>
				</object>
				<object class="wxMenuItem" name="m_menuItem17">
					<label>&amp;Save As</label>
					<help></help>
				</object>
				<object class="wxMenuItem" name="m_Quit">
					<label>&amp;Quit</label>
					<help></help>
				</object>
			</object>
			<object class="wxMenu" name="m_edit">
				<label>Edit</label>
				<object class="wxMenuItem" name="m_menuItem11">
					<label>Undo</label>
					<help></help>
				</object>
				<object class="wxMenuItem" name="m_menuItem12">
					<label>Copy</label>
					<help></help>
				</object>
				<object class="wxMenuItem" name="m_menuItem13">
					<label>Cut</label>
					<help></help>
				</object>
				<object class="wxMenuItem" name="m_menuItem14">
					<label>Paste</label>
					<help></help>
				</object>
				<object class="wxMenuItem" name="m_menuItem15">
					<label>Delete</label>
					<help></help>
				</object>
			</object>
			<object class="wxMenu" name="m_view">
				<label>View</label>
			</object>
			<object class="wxMenu" name="m_settings">
				<label>Settings</label>
				<object class="wxMenuItem" name="m_menuItem9">
					<label>Viewer Settings</label>
					<help></help>
				</object>
				<object class="wxMenuItem" name="m_menuItem10">
					<label>Network Settings</label>
					<help></help>
				</object>
			</object>
			<object class="wxMenu" name="m_add">
				<label>Add</label>
				<object class="wxMenuItem" name="m_AddObject">
					<label>Object</label>
					<help></help>
				</object>
				<object class="wxMenuItem" name="m_menuItem4">
					<label>Environment</label>
					<help></help>
				</object>
				<object class="wxMenuItem" name="m_menuItem5">
					<label>Animated Object</label>
					<help></help>
				</object>
				<object class="separator" />
				<object class="wxMenuItem" name="m_menuItem6">
					<label>Point Light</label>
					<help></help>
				</object>
				<object class="wxMenuItem" name="m_menuItem7">
					<label>Directional Light</label>
					<help></help>
				</object>
				<object class="wxMenuItem" name="m_menuItem8">
					<label>Spotlight</label>
					<help></help>
				</object>
			</object>
		</object>
		<object class="wxStatusBar" name="m_statusBar1">
			<style>wxST_SIZEGRIP</style>
			<fields>1</fields>
		</object>
	</object>
	<object class="wxPanel" name="PropertiesPanel">
		<style>wxDOUBLE_BORDER|wxSUNKEN_BORDER|wxTAB_TRAVERSAL</style>
		<size>238,589</size>
		<font>
			<family>default</family>
			<style>normal</style>
			<weight>normal</weight>
			<underlined>0</underlined>
		</font>
		<object class="wxBoxSizer">
			<orient>wxVERTICAL</orient>
			<object class="sizeritem">
				<option>0</option>
				<flag>wxALIGN_CENTER_HORIZONTAL|wxALL</flag>
				<border>5</border>
				<object class="wxStaticText" name="m_staticText12">
					<label>Properties</label>
				</object>
			</object>
			<object class="sizeritem">
				<option>1</option>
				<flag>wxALL|wxEXPAND</flag>
				<border>8</border>
				<object class="wxPanel" name="m_panel7">
					<style>wxTAB_TRAVERSAL</style>
					<object class="wxGridSizer">
						<rows>10</rows>
						<cols>2</cols>
						<vgap>2</vgap>
						<hgap>2</hgap>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxStaticText" name="m_staticText2">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<label>Translate X</label>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxTextCtrl" name="m_textCtrl5">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<value></value>
								<maxlength>10</maxlength>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxStaticText" name="m_staticText3">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<label>Translate Y</label>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxTextCtrl" name="m_textCtrl6">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<value></value>
								<maxlength>0</maxlength>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxStaticText" name="m_staticText4">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<label>Translate Z</label>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxTextCtrl" name="m_textCtrl7">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<value></value>
								<maxlength>0</maxlength>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxStaticText" name="m_staticText6">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<label>Rotate X</label>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxTextCtrl" name="m_textCtrl8">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<value></value>
								<maxlength>0</maxlength>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxStaticText" name="m_staticText7">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<label>Rotate Y</label>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxTextCtrl" name="m_textCtrl9">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<value></value>
								<maxlength>0</maxlength>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxStaticText" name="m_staticText8">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<label>Rotate Z</label>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxTextCtrl" name="m_textCtrl10">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<value></value>
								<maxlength>0</maxlength>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxStaticText" name="m_staticText9">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<label>Scale X</label>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxTextCtrl" name="m_textCtrl11">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<value></value>
								<maxlength>0</maxlength>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxStaticText" name="m_staticText10">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<label>Scale Y</label>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxTextCtrl" name="m_textCtrl12">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<value></value>
								<maxlength>0</maxlength>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxStaticText" name="m_staticText11">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<label>Scale Z</label>
							</object>
						</object>
						<object class="sizeritem">
							<option>0</option>
							<flag>wxALL</flag>
							<border>5</border>
							<object class="wxTextCtrl" name="m_textCtrl13">
								<font>
									<size>8</size>
									<family>default</family>
									<style>normal</style>
									<weight>normal</weight>
									<underlined>0</underlined>
								</font>
								<value></value>
								<maxlength>0</maxlength>
							</object>
						</object>
					</object>
				</object>
			</object>
			<object class="spacer">
				<option>1</option>
				<flag>wxEXPAND</flag>
				<border>5</border>
				<size>0,0</size>
			</object>
		</object>
	</object>
</resource>

THX!

I have trouble with the xrc module on the machine I’m working now but I will get them sorted.

cheers
MArtin

Hi Benchang,

I get the following error when I try to run it:

C:\incoming>ppython viewer.py
DirectStart: Starting the game.
Traceback (most recent call last):
File “viewer.py”, line 111, in
editorApp = EditorApp()
File “viewer.py”, line 55, in init
self.PropertiesPanel=self.res.LoadPanel(self.MainWindow,‘PropertiesPanel’)
File “C:\Panda3D-1.6.2\python\lib\site-packages\wx-2.8-msw-unicode\wx\xrc.py”,
line 141, in LoadPanel
return _xrc.XmlResource_LoadPanel(*args, **kwargs)
wx._core.PyAssertionError: C++ assertion “wxAssertFailure” failed at …\src\m
sw\window.cpp(1408) in wxWindow::MSWGetStyle(): unknown border style

oh, you’re on windows … I wonder if that panel has something linux-specific about it. Try opening the XRC file in WxFormBuilder?

Or, in the meantime: comment out that line, and the ones referring to PropertiesPanel; and replace the split() function so it just splits with a dummy panel

replace this

self.MainSplitter.SplitVertically (self.p3dViewport,self.PropertiesPanel,-220) 

with this

self.MainSplitter.SplitVertically (self.p3dViewport,wx.Panel(),-220)

Hi sieracharlie,

or open the viewer.xrc with xrced and set a different border style under wxPanel"PropertiesPane"–Style.

and now I have get the buttons working.

By the way, benchang thank you; this will be a nice piece once its finished

thx

mARTIN

Turning off the double border style bit in the Properties panel lets the program come up on XP.

Very nice example of Panda hosted in WxPython.

Dear Folks,

I’m not shure if it’s coded elegantly but as benchang noted some exampels could be usefull

So I start with:

#!/usr/bin/env python
import wxversion
wxversion.select('2.8')
import wx
from wx import xrc 

from direct.showbase import DirectObject
from pandac.PandaModules import *
loadPrcFileData("","window-type none")

from direct.directbase import DirectStart


#----------------------------------------------------------------------
# There are better ways to do IDs, but this demo requires that the window
# IDs be in a specific range. There are better ways to do that, too, but
# this will do for purposes of this demo.

ID_Menu_New         = 5004
ID_Menu_Exit        = 5005

ID_WINDOW_TOP       = 5000
ID_WINDOW_LEFT1     = 5001
ID_WINDOW_LEFT2     = 5002
ID_WINDOW_BOTTOM    = 5003

#----------------------------------------------------------------------
class P3dViewport(wx.Panel):
  # this panel contains the panda 3d window
    def __init__(self, *args, **kwargs):
        wx.Panel.__init__(self, *args, **kwargs)

    def initialize(self):
    # the panda3d window must be put into the panel after the wx-window has
    # been created
        assert self.GetHandle() != 0

    # put the panda3d
        wp = WindowProperties()
        wp.setOrigin(0,0)
        wp.setSize(self.ClientSize.GetWidth(), self.ClientSize.GetHeight())
        wp.setParentWindow(self.GetHandle())
        base.openDefaultWindow(props = wp, gsg = None)

    # attach the resize event
        wx.EVT_SIZE(self, self.OnResize)

    def OnResize(self, event):
    # when the wx-panel is resized, fit the panda3d window into it
        frame_size = event.GetSize()
        wp = WindowProperties()
        wp.setOrigin(0,0)
        wp.setSize(frame_size.GetWidth(), frame_size.GetHeight())
        base.win.requestProperties(wp)

class MyParentFrame(wx.MDIParentFrame):
    def __init__(self):
        wx.MDIParentFrame.__init__(
            self, None, -1, "MDI Parent B", size=(690,785),
            style = wx.DEFAULT_FRAME_STYLE | wx.HSCROLL | wx.VSCROLL
            )

        self.winCount = 0
        
        self.res = xrc.XmlResource('WWLayout.xrc')
        menubar = self.res.LoadMenuBar('Menu')
        self.SetMenuBar(menubar)

        self.CreateStatusBar()

        #self.Bind(wx.EVT_MENU, self.OnNewWindow, id=ID_Menu_New)
        self.Bind(wx.EVT_MENU, self.OnExit, id=ID_Menu_Exit)

        self.Bind(
            wx.EVT_SASH_DRAGGED_RANGE, self.OnSashDrag, id=ID_WINDOW_TOP, 
            id2=ID_WINDOW_BOTTOM
            )

        self.Bind(wx.EVT_SIZE, self.OnSize)
        
        self.MainWindow = self.res.LoadFrame(None,'Main')
        self.p3dViewport = P3dViewport(self.MainWindow)
        self.MainWindow.SetWindowStyle(wx.NO_BORDER)
        self.MainWindow.Refresh()
        self.MainWindow.Reparent(self)
        self.MainWindow.Show()

 
        self.MainWindow.Update()
        # update the p3d window
        self.p3dViewport.initialize()
        
        


        #Create TopPanel
        win = wx.SashLayoutWindow(self,-1,style=wx.SW_3D | wx.NO_BORDER)
        win = wx.SashLayoutWindow(self, ID_WINDOW_TOP, style=wx.NO_BORDER|wx.SW_3D)
        win.SetDefaultSize((690, 60))
        win.SetOrientation(wx.LAYOUT_HORIZONTAL)
        win.SetAlignment(wx.LAYOUT_TOP)
        win.SetBackgroundColour(wx.Colour(0, 0, 0))
        #win.SetSashVisible(wx.SASH_BOTTOM, True)
        panel = self.res.LoadPanel(win,'TopPanel')

        self.topWindow = win


        #Create BottomPanel
        win = wx.SashLayoutWindow(self, ID_WINDOW_BOTTOM, style=wx.NO_BORDER|wx.SW_3D)
        win.SetDefaultSize((690,90))
        win.SetOrientation(wx.LAYOUT_HORIZONTAL)
        win.SetAlignment(wx.LAYOUT_BOTTOM)
        win.SetBackgroundColour(wx.Colour(0, 0, 0))
        #win.SetSashVisible(wx.SASH_TOP, True)
        panel = self.res.LoadPanel(win,'BottomPanel')
        
        self.bottomWindow = win


        #Create LeftPanel
        win =  wx.SashLayoutWindow(self, ID_WINDOW_LEFT1, style=wx.NO_BORDER|wx.SW_3D)
        win.SetDefaultSize((100, 645))
        win.SetOrientation(wx.LAYOUT_VERTICAL)
        win.SetAlignment(wx.LAYOUT_LEFT)
        win.SetBackgroundColour(wx.Colour(0, 0, 0))
        #win.SetSashVisible(wx.SASH_RIGHT, True)
        #win.SetExtraBorderSize(10)
        panel = self.res.LoadPanel(win,'LeftPanel')

        self.leftWindow1 = win


        #Create RightPanel
        win = wx.SashLayoutWindow(self, ID_WINDOW_LEFT2, style=wx.NO_BORDER|wx.SW_3D)
        win.SetDefaultSize((100, 645))
        win.SetOrientation(wx.LAYOUT_VERTICAL)
        win.SetAlignment(wx.LAYOUT_RIGHT)
        win.SetBackgroundColour(wx.Colour(0, 0, 0))
        #win.SetSashVisible(wx.SASH_LEFT, True)
        panel = self.res.LoadPanel(win,'RightPanel')

        self.leftWindow2 = win


    def OnSashDrag(self, event):
        if event.GetDragStatus() == wx.SASH_STATUS_OUT_OF_RANGE:
            return

        eID = event.GetId()
        
        if eID == ID_WINDOW_TOP:
            self.topWindow.SetDefaultSize((1000, event.GetDragRect().height))

        elif eID == ID_WINDOW_LEFT1:
            self.leftWindow1.SetDefaultSize((event.GetDragRect().width, 1000))

        elif eID == ID_WINDOW_LEFT2:
            self.leftWindow2.SetDefaultSize((event.GetDragRect().width, 1000))

        elif eID == ID_WINDOW_BOTTOM:
            self.bottomWindow.SetDefaultSize((1000, event.GetDragRect().height))

        wx.LayoutAlgorithm().LayoutMDIFrame(self)
        self.GetClientWindow().Refresh()


    def OnSize(self, event):
        wx.LayoutAlgorithm().LayoutMDIFrame(self)
        xsize = self.GetSize().width-189
        ysize = self.GetSize().height-227
        self.MainWindow.SetSize((xsize,ysize))
        self.MainWindow.SetPosition((83,60))

    def OnExit(self, evt):
        self.Close(True)


    #def OnNewWindow(self, evt):
        #self.winCount = self.winCount + 1
        #win = wx.MDIChildFrame(self, -1, "Child Window: %d" % self.winCount)
        #canvas = ScrolledWindow.MyCanvas(win)
        #win.Show(True)


#----------------------------------------------------------------------


class EditorApp(DirectObject.DirectObject):
  def __init__(self):

    self.wxApp = wx.App(redirect = False)
    self.wxApp.SetAppName("Panda Editor")
    self.wxApp.SetClassName("PEditor")

    self.appInit() 
    
    self.frame = MyParentFrame()
    self.frame.Show()
    self.frame.Update()
    self.wxStep()    


    # do something with the panda3d view
    loader.loadModel('A4.egg').reparentTo(render)



  def appInit(self):
    # Create a new event loop (to overide default wxEventLoop)
    self.evtLoop = wx.EventLoop()
    self.oldLoop = wx.EventLoop.GetActive()
    wx.EventLoop.SetActive(self.evtLoop)
    taskMgr.add(self.wxStep, "evtLoopTask")

  def onDestroy(self, evt):
    # called on wx window destroy
    wx.EventLoop.SetActive(self.oldLoop)
   
  def quit(self,event):
    self.onDestroy(event)
     
      # to close Panda
    try:
      base
    except NameError:
      sys.exit()
       
    base.userExit()

  def wxStep(self, task = None):
    # process the events wx sends
    while self.evtLoop.Pending():
      self.evtLoop.Dispatch()
    self.wxApp.ProcessIdle()
    if task != None: return task.cont
   

editorApp = EditorApp()
run()

with the .xrc

<?xml version="1.0" encoding="UTF-8"?>
<resource version="2.3.0.1" xmlns="http://www.wxwindows.org/wxxrc">
  <object class="wxMenuBar" name="Menu">
    <object class="wxMenu" name="File">
      <label>File</label>
      <object class="wxMenuItem" name="Open">
        <label>Open</label>
      </object>
      <object class="separator"/>
      <object class="wxMenuItem" name="Exit">
        <label>Exit</label>
      </object>
    </object>
  </object>
  <object class="wxPanel" name="TopPanel">
    
  </object>
  
  <object class="wxPanel" name="LeftPanel">
    
  </object>
  <object class="wxPanel" name="RightPanel">
    
  </object>
  <object class="wxPanel" name="BottomPanel">
    
  </object>
  
  <object class="wxFrame" name="Main">
    <pos>100,90</pos>
    <size>490,635</size>
  </object>
</resource>

I’m not using the sash to resize the panda window so they have been disabeled

cheers

Martin

I have shortened it a bit, as I find it useful to have the most concise example possible so that I can elaborate on it myself. So if you’re like me, you might appreciate this :

import sys
import wx

import direct
from pandac.PandaModules import *
loadPrcFileData('startup', 'window-type none')
from direct.directbase.DirectStart import *
from direct.showbase import DirectObject

class PandaPanel(wx.Panel):
    def __init__(self, *args, **kwargs):
        wx.Panel.__init__(self, *args, **kwargs)
    def initialize(self):
        assert self.GetHandle() != 0
        wp = WindowProperties()
        wp.setOrigin(0,0)
        wp.setSize(self.ClientSize.GetWidth(), self.ClientSize.GetHeight())
        wp.setParentWindow(self.GetHandle())
        base.openDefaultWindow(props = wp, gsg = None)
        self.Bind(wx.EVT_SIZE, self.OnResize)
    def OnResize(self, event):
        frame_size = event.GetSize()
        wp = WindowProperties()
        wp.setOrigin(0,0)
        wp.setSize(frame_size.GetWidth(), frame_size.GetHeight())
        base.win.requestProperties(wp) 

class PandaFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.pandapanel = PandaPanel(self, wx.ID_ANY)
        self.pandapanel.initialize()
        
class PandaApp(wx.App, DirectObject.DirectObject):
    def __init__(self):
        wx.App.__init__(self)
        self.replaceEventLoop()
        self.frame = PandaFrame(None, wx.ID_ANY, 'Panda App')
        self.frame.Bind(wx.EVT_CLOSE, self.quit)
        self.frame.Show()
        self.wxStep()   
    def replaceEventLoop(self):
        self.evtLoop = wx.EventLoop()
        self.oldLoop = wx.EventLoop.GetActive()
        wx.EventLoop.SetActive(self.evtLoop)
        taskMgr.add(self.wxStep, "evtLoopTask")
    def onDestroy(self, event=None):
        self.wxStep()
        wx.EventLoop.SetActive(self.oldLoop)
    def quit(self, event=None):
        self.onDestroy(event)
        try:
            base
        except NameError:
            sys.exit()
        base.userExit()
    def wxStep(self, task=None):
        while self.evtLoop.Pending():
            self.evtLoop.Dispatch()
        self.ProcessIdle()
        if task != None: return task.cont 

app = PandaApp()
run()

Edit to add, no .xrc file needed - it’s just a single frame.

I’ve got this message repetedly from command shell, that when I close the application I can’t use the command shell anymore and I have to close it immediately:

:display:gsg:dxgsg8(error): show_frame() - Present() failed at (panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx:3952), hr=D3DERR_INVALIDCALL: Invalid call :open_mouth:

I’m using the dwrw code in the first page

I guess Panda is mad that its window has been destroyed by some external event (such as wx shutting down). Perhaps you need to have a callback on wx.Exit() or whatever call it is that wx makes when the program exits, and call base.destroy() at that point.

David

Thanks David for giving me a light. Panda is not mad anymore.

I found this on this thread, benchang posted it:

    
    def onDestroy(self, evt): 
        # called on wx window destroy 
        wx.EventLoop.SetActive(self.old) 
        
    def quit(self,event): 
        self.onDestroy(event) 
          
          # to close Panda 
        try: 
          base
        except NameError: 
          sys.exit() 
            
        base.userExit() 

hey, do you know why wxpython takes long to respond to shortcuts such as Ctrl+key when running with panda? how do I avoid this behavior?

thanks.

Thanks for this, steego! I had to make two minor adjustments to get it to work, though. I’m using Ubuntu 10.04, with wxPython 2.8.10.1.

With the code you provided, I got the following error:

(python:10821): Gdk-WARNING **: /build/buildd/gtk+2.0-2.20.1/gdk/x11/gdkdrawable-x11.c:952 drawable is not a pixmap or window
Traceback (most recent call last):
  File "panda-in-wx.py", line 71, in <module>
    app = PandaApp()
  File "panda-in-wx.py", line 42, in __init__
    self.frame = PandaFrame(None, wx.ID_ANY, 'Panda App')
  File "panda-in-wx.py", line 35, in __init__
    self.pandapanel.initialize()
  File "panda-in-wx.py", line 15, in initialize
    assert self.GetHandle() != 0
AssertionError

To solve this, I had to make the PandaFrame show itself before initializing the PandaPanel. So I changed its init function to the following:

class PandaFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.Show()
        self.pandapanel = PandaPanel(self, wx.ID_ANY)
        self.pandapanel.initialize()

Of course, this means you can remove the self.frame.Show() line from the PandaApp init function.

But after getting it to work, I noticed that the PandaPanel started out much smaller than the window size: just 20x20. It didn’t grow to match the window size until I actually resized the window (with the mouse, while the app was running). So I explicitly passed the PandaFrame’s size to the PandaPanel constructor:

class PandaFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.Show()
        self.pandapanel = PandaPanel(self, wx.ID_ANY, size=self.ClientSize)
        self.pandapanel.initialize()

That did the trick.