mouseWatcherNode.setGeometry base.openMainWindow [solved]

Hi everyone,

Over the past year or so I’ve been figuring things out in Panda3D and for the most part I’ve been able to do so by reading documentation, reading forum posts, and just trying things. I’ve run into an issue that I can’t seem to figure out. I could ignore it and worry about it later but I’d rather build my program one step at a time and not skip over things.

I found code somewhere that mentioned that this was a good way to switch from fullscreen to windowed or vise versa:

base.openMainWindow(**window_config)

passing in:

{'requireWindow': True,
 'type': 'onscreen',
 'gsg': base.win.getGsg(),
 'props': self.props} 

self.props is: origin=(640, 280) size=(1920, 1080) fullscreen

Recently I created a class to store the mouse which gives me quick access to 2d coordinates (not shown below) and has a way to set the texture of the pointer using parts of an egg I found in some example code. Later on I’ll probably make my own but for now that is the egg:

<CoordinateSystem> { Z-Up }

<VertexPool> poly {
  <Vertex> 0 {
    -0.5 0 0.5
    <UV> { 0 1 }
  }
  <Vertex> 1 {
    0.5 0 0.5
    <UV> { 1 1 }
  }
  <Vertex> 2 {
    -0.5 0 -0.5
    <UV> { 0 0 }
  }
  <Vertex> 3 {
    0.5 0 -0.5
    <UV> { 1 0 }
  }
}
<Group> poly {
  <Polygon> {
    <VertexRef> { 0 2 3 1 <Ref> { poly } }
  }
}

So the issue I’m having is with combining both of these things. I set the mouse pointer to a png which is just a 16x16 black box with a single pixel white border. Then I toggle between fullscreen and windowed (or vise versa) and the pointer disappears. I’ve tried a number of things to make it display again but none have been successful.

Some of the code for my 2d mouse class:
plane is the name of the nodepath (like pixel2d). I have another class which stores them all for easy access/sorting.
resized is the function I call right after this:

        base.openMainWindow(**window_config)
class TwoDMouse():
    def __init__(self, plane, texture=None):
        self.plane = plane
        self.pointer = None
        self.texture = texture

        self.pointer_node = plane.attachNewNode('pointer')
        self.pointer = loader.loadModel('..\\data_source\\eggs\\pointer.egg')
        self.pointer.reparentTo(self.pointer_node)
        base.mouseWatcherNode.setGeometry(self.pointer_node.node())
        self.setTexture(texture)

        self.getMouse()

    def getMouse(self, event=None):
        if base.mouseWatcherNode.hasMouse():
            self._mouse = base.mouseWatcherNode.getMouse()
        else:
            base.taskMgr.doMethodLater(1, self.getMouse, 'getMouse')

    def setTexture(self, texture=None):
        if texture:
            pointer_texture = loader.loadTexture(texture)
            self.pointer.setTexture(pointer_texture)
            aspectRatio = base.getAspectRatio()
            height = base.win.getSbsLeftYSize()
            self.pointer.setScale(32.0 / height / aspectRatio, 1.0, 32.0 / height)
            self.pointer.setPos(16.0 / height / aspectRatio, 1.0, -16.0 / height)
            self.hideCursor()
        else:
            self.pointer.clearTexture()
            self.pointer.setScale(0, 0, 0)
            self.showCursor()
        self.texture = texture

    def clearTexture(self, event=None):
        self.setTexture()

    def hideCursor(self):
        props = WindowProperties()
        props.setCursorHidden(True)
        base.win.requestProperties(props)

    def showCursor(self):
        props = WindowProperties()
        props.setCursorHidden(False)
        base.win.requestProperties(props)

    def resized(self, width, height):
        self.pointer_node.removeNode()

        self.pointer_node = self.plane.attachNewNode('pointer')
        self.pointer = loader.loadModel('..\\data_source\\eggs\\pointer.egg')
        self.pointer.reparentTo(self.pointer_node)
        base.mouseWatcherNode.setGeometry(self.pointer_node.node())
        self.setTexture(self.texture)

        self.getMouse()

        render2d.setClearDepthActive(True)

The code which removes the node in resized doesn’t help. Originally had it without this.
The last line which I saw somewhere doesn’t help either. My nodepath (self.plane) which has the pointer attached to it with a scale of (1, 1, 1) to match render2d doesn’t have setClearDepthActive.

The code for the mouse itself works fine. It is only after reopening the window where things go wrong.

I’ve searched with google for a number of things and can’t seem to find anything that has helped. Has anyone implemented something similar and/or knows what I am doing incorrectly or missing?

Help would be greatly appreciated.

I’m not familiar with the method that you’re using to toggle between full-screen and windowed modes, but I’m a little wary of it, given the method name.

Have you tried this approach?

#  Presume that we have a variable named "wantsFullscreen"
# that contains a boolean value indicating whether
# we want full-screen mode or not
        wp = WindowProperties()
        wp.setFullscreen(wantsFullscreen)
        base.win.requestProperties(wp)

Trying your technique takes me back to when I started to try and figure out switching between fullscreen and a windowed mode. I didn’t like how long it took to switch (more than a second) so I looked around online. I think this is where I got the idea I am using:
[url]setting full screen during runtime]

It takes about a quarter of a second to switch which is what I like. Of course it will probably take longer once I have more going on, but I don’t know that for sure. Right now I am only building some UI.

Trying that technique however doesn’t correct the disappearing mouse texture issue. I’ll keep trying things to see if I can figure something out.

Ok. I figured it out. I recently created a way to for my nodes to create a render2d scaled version but didn’t alter the resize code after the window is changed. So my problem was after I change the window size I go though all the nodes I’ve created and sets the scale to the size of the window. My render2d version needed to be (1, 1, 1) not the (width, 1, height) version. So it was displaying, just really far off the screen.

    def resize(self, width, height):
        for plane in self._instances.values():
            plane.setScale(width, 1, height)
            plane.node().setMouseWatcher(base.mouseWatcherNode)

After making a change so that my nodepaths are scaled correctly based on the kind of nodepath things work correctly. The window loads fast like I mentioned in the previous post and my software mouse texture displays on the screen as expected.

It’s always in code changes/new features where errors occur and they are usually not obvious.

I would suggest trying out the base.openMainWindow method. Here’s a simple example showing the technique I am using. You’ll want to alter a few numbers at the top and it’s set up so F12 toggles between the fullscreen/windowed sizes and the Escape key quits the application.

screen_width = 1920
screen_height_minus_taskbar = 1040 # Taskbar in windows 7 is 40 pixels tall by default
fullscreen_width = 1920
fullscreen_height = 1080
windowed_width = 600
windowed_height = 480
start_fullscreen = 1

from direct.showbase.ShowBase import ShowBase
from panda3d.core import ConfigVariableString
from panda3d.core import WindowProperties
import sys

class App(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        base.makeDefaultPipe()
        self.main = MainWindow()


class MainWindow(object):
    def __init__(self):
        self.props = WindowProperties()
        self.props.setFullscreen(start_fullscreen) # I actually grab this value from my tkinter window
        self.openWindow(after_initial_window=False)

        base.setFrameRateMeter(True)
        base.accept('f12', self.toggleFullscreen)
        base.acceptOnce('escape', self.endGame)

    def endGame(self):
        base.ignore('window-event')
        sys.exit(0)

    def toggleFullscreen(self, event=None):
        self.props.setFullscreen(not self.props.getFullscreen())
        self.openWindow({'gsg' : base.win.getGsg()})
        base.setFrameRateMeter(True)

    def openWindow(self, extra_config={}, after_initial_window=True):
        if self.props.getFullscreen():
            width = fullscreen_width  # I get this value from my tkinter window.
            height = fullscreen_height  # I get this value from my tkinter window.
        else:
            width = windowed_width  # I get this value from my tkinter window.
            height = windowed_height  # I get this value from my tkinter window.
            self.props.setOrigin((screen_width - width) / 2, (screen_height_minus_taskbar - height) / 2)
        self.props.setSize(width, height)
        window_width = width
        window_height = height

        window_config = {'props'         : self.props,
                         'requireWindow' : True,
                         'type'          : 'onscreen'}
        window_config.update(extra_config)

        print(window_config)

        base.openMainWindow(**window_config)

        if after_initial_window:
            # resize things here that you need to, like any nodepaths like pixel2d you created
            # resize the egg loaded (self.pointer in my case from the code in a previous post) using setScale and setPos
            pass

if __name__ == '__main__':
    # Don't open a window until we start the game (the code is like this because I have a tkinter window open before creating an instance of MainWindow)
    window_type = ConfigVariableString('window-type')
    window_type.setValue('none')
    app = App()
    app.run()

Ah, interesting, and I’m glad that you found and fixed the problem. :slight_smile:

As to your recommending the “base.openWindow” method over the “requestProperties”, thank you, I’ll hopefully give that a try indeed. :slight_smile: