Focus problem with shared panda3d and WxPandaPanel in 1.10.6 on Windows 10

EDIT: For clarity

Hi

I am trying fix a problem that effects keyboard shortcuts (and appearance) when using wxPython Frame containing a WxPandaPanel

Everything works when I use Panda3D 1.9.4 and the latest wxPython 4.0

When I switch to Panda3D 1.10.5 or 1.10.6 I get the problem

The problem is that, with Panda3D 1.10.5 or 1.10.6, on startup everything is normal except that WxPandaPanel is blank.

As soon as I resize the wx.Frame, or maximize, this focus problem shows up

So the focus problem is basically that I cannot send keyboard input to the wxFrame without having to constantly click on the wx.Frame header all the time after every time I click in the WxPandaPanel

I have a minimal example below that shows the problem

I tried on another example to call self.FindFocus() from the wx.Frame object, from within the wx main loop

Before the focus issue shows up, when I click in the WxPandaPanel, FindFocus returns that WxPandaPanel. And when I click on a wx.Button, FindFocus returns that wx.Button

But after the focus issue shows up, FindFocus still returns the wx.Button just fine, but returns None when I click on the WxPandaPanel

So…is this a bug? Did an API change happened?

Is there any other settings that I can check to see whats going on in terms of window properties? i.e. which ones change when resizing or maximizing

I have tried looking at …

but they didnt seem to match the problem

Thank you for any suggestions

Here’s my code

My WxPandaPanel is in the wx.Frame defined in the panel.py file, and I am using Panda3D to run the wx main loop as well. Using wxPython to run the main loop results in the same issue

main.py

import wx
import ui

wxApp=ui.AppShell()

base = ui.MyApp(wxApp)

import panel
frame=panel.MainWin(None)

frame.Show()

base.run()

ui.py

from direct.showbase.DirectObject import DirectObject
from direct.showbase.ShowBase import ShowBase, Loader
from direct.wxwidgets.WxPandaWindow import WxPandaWindow

from panda3d.core import loadPrcFileData

loadPrcFileData("", "window-type none")

import wx

class PandaPanel(WxPandaWindow):
    def __init__(self, *args, **kwargs):
        WxPandaWindow.__init__(self, *args, **kwargs)

class MyApp(ShowBase):

    def __init__(self, wxApp):
        ShowBase.__init__(self)

        # Load the environment model.
        #self.loader=Loader.Loader(self)
        self.scene = self.loader.loadModel("models/environment")
        # Reparent the model to render.
        self.scene.reparentTo(self.render)
        # Apply scale and position transforms on the model.
        self.scene.setScale(0.25, 0.25, 0.25)
        self.scene.setPos(-8, 42, 0)
        self.wxApp=wxApp
        self.frameSort=-100
        self.taskMgr.add(self.doFrame,'doFrames',sort=self.frameSort)

    def doFrame(self, task):
        self.wxApp.wxStep(task)

class AppShell(wx.App):

    def __init__(self):
        wx.App.__init__(self)
        self.SetAppName("Minimal Example")
        self.appInit()

    def wxStep(self, task = None):
        """A step in the WX event loop. You can either call this yourself or use as task."""
        while self.evtLoop.Pending():
            self.evtLoop.Dispatch()
        self.ProcessPendingEvents()
        if task != None:
            return task.cont

    def appInit(self):
        # Create a new event loop (to overide default wxEventLoop)
        self.evtLoop = wx.EventLoop()
        wx.EventLoop.SetActive(self.evtLoop)

panel.py

from ui import PandaPanel
import wx
import wx.xrc

###########################################################################
## Class MainWin
###########################################################################

class MainWin ( wx.Frame ):

	def __init__( self, parent ):
		wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( -1,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

		self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )

		bSizer1 = wx.BoxSizer( wx.VERTICAL )

		bSizer2 = wx.BoxSizer( wx.HORIZONTAL )

		self.m_button1 = wx.Button( self, wx.ID_ANY, u"Panda Sleep", wx.DefaultPosition, wx.DefaultSize, 0 )
		bSizer2.Add( self.m_button1, 0, wx.ALL, 5 )

		self.m_button2 = wx.Button( self, wx.ID_ANY, u"Panda Drink", wx.DefaultPosition, wx.DefaultSize, 0 )
		bSizer2.Add( self.m_button2, 0, wx.ALL, 5 )

		self.m_button3 = wx.Button( self, wx.ID_ANY, u"Panda Rage", wx.DefaultPosition, wx.DefaultSize, 0 )
		bSizer2.Add( self.m_button3, 0, wx.ALL, 5 )

		self.m_button4 = wx.Button( self, wx.ID_ANY, u"Panda Fly", wx.DefaultPosition, wx.DefaultSize, 0 )
		bSizer2.Add( self.m_button4, 0, wx.ALL, 5 )


		bSizer1.Add( bSizer2, 0, wx.EXPAND, 5 )

		bSizer3 = wx.BoxSizer( wx.VERTICAL )

		self.pandaPanel = PandaPanel( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL )
		bSizer3.Add( self.pandaPanel, 1, wx.EXPAND |wx.ALL, 5 )


		bSizer1.Add( bSizer3, 1, wx.EXPAND, 5 )


		self.SetSizer( bSizer1 )
		self.Layout()

		self.Centre( wx.BOTH )

	def __del__( self ):
		pass

Funny, I have been banging my head against the same issue but coming from the opposite direction. Thank you for linking all those other resources btw, you may have solved my current problem as well. Looking at the implementation of WxPandaWindow, under the hood what it is doing is creating a second window and overlaying it on top of the wxPython window, and then relocating/resizing itself when the parent wxPython window changes. However, while the WxPandaWindow attempts to mimic a native wxPanel, it is not actually part of the larger wxPython window and as such only one of them gets the keyboard input focus at a time. My solution has been to toggle the Panda window to the foreground/background depending on whether it should be receiving keyboard input using the WindowProperties setForground() method and then applying the result to base.win.requestProperties(). That said, my interpretation of the problem does not really explain why some versions work and others don’t, and so probably should be taken with a grain of salt.

This sounds like a it may be related to a bug(?) I posted about on Github the other day: https://github.com/panda3d/panda3d/issues/915

That post also includes a questionable workaround, but requires pywin32.