Gui stuff on multiple windows

I finally found a couple of mins to try this all out, been spending some time reading up on python.

import direct.directbase.DirectStart
from direct.gui.DirectGui import *

base.setBackgroundColor(0, 0, 0)
base.disableMouse()
camera.setPos ( 0, 0, 45 )
camera.setHpr ( 0, -90, 0 )

wp = WindowProperties()
wp.setSize(200, 200)
wp.setOrigin(0, 0)
win = base.openWindow(props = wp, aspectRatio = 1)

myRender = NodePath('myRender')

base.camList[-1].reparentTo(myRender)
myRender2d = NodePath('myRender2d')
myCamera2d = base.makeCamera2d(win)
myCamera2d.reparentTo(myRender2d)

testButton = DirectButton(text = "Button1", scale = 0.5)
testButton.setPos(0, 0, 0) 
testButton.reparentTo(myRender2d)

run()

Right, Im setting up the default window and camera, then I create another window and render it top left of screen. So I setup a nodelist and add a camera to the new window. Now I create a button and reparent it to the new window.

The problem is, no text appears to be rendered to it. If I take out the reparent line so it renders to the main window then I see the button text. Any ideas greatly welcomed.

Oops, I left out some important information.

To set up your window for rendering 2-d widgets, you need to turn off some properties that are normally on by default, but which are inconvenient for 2-d objects:

myRender2d.setDepthWrite(0)
myRender2d.setMaterialOff(1)
myRender2d.setTwoSided(1)

This will make the button text show up (currently, it’s being drawn “behind” the background, so you’re not seeing it; turning off writing to the depth buffer solves that). However, there’s still some more setup work to do to make the button clickable. I will look into that a bit more and get back with you.

David

Thanks for your help David. Ive been playing around with python so not really tried much in panda yet. This will be a great help :slight_smile:

Thanks again for your help.

Had one of those days in work, so after a few beers decided to go into depth with the ‘direct’ python code. I think I am slowly understanding a little more about ‘base’ so I had a play around with my test code trying to hook the input devices to it. This failed nicely :slight_smile:

I dump out the input devices for the window with getInputDevice() and getInputName(n). This shows that a device keyboard/mouse is attached to it. So I presume I am mean to somehow attach this to the render2d node?

Now looking at ‘base’ I see it creates(?) a new node to ‘dataroot’ with the device name and window.

At this point I am a little lost. What exactly is ‘dataroot’ and how does it handle the input? Is it just for the base window?

Any info on this greatly appreciated.

Thanks in advance.

Came back to this for a couple of mins after a little more python reading, this is the code I have so far…

import direct.directbase.DirectStart
from direct.gui.DirectGui import *

class myWin:
  """myWin : My first python/panda3d program"""
  
  def __init__(self):
    # Just to know what Im dealing with
    self.win = None
    self.camera2d = None
    self.windowProperties = None
    self.render3d = None
    self.render2d = None
    self.dataRoot = None
    self.dataRootNode = None
    
  def openWin(self, sizeX, sizeY, originX, originY):
    # setup the window properties
    self.windowProperties = WindowProperties()
    self.windowProperties.setSize( sizeX, sizeY )
    self.windowProperties.setOrigin( originX, originY )
    
    # open a window
    self.win = base.openWindow(props = self.windowProperties, aspectRatio = 1, type = 'onscreen')
    
    # create a 3d rendering context
    self.render3d = NodePath('myWin_render3d')
    # give the newly created camera to the renderer
    base.camList[-1].reparentTo(self.render3d)
    
    # create a 2d rendering context
    self.render2d = NodePath('myWin_render2d')
    
    # create a camera and parent it to the 2d renderer
    self.camera2d = base.makeCamera2d( self.win )
    self.camera2d.reparentTo( self.render2d )

    # setup the default 2d rendering aspects
    self.render2d.setDepthWrite( 0 )
    self.render2d.setMaterialOff( 1 )
    self.render2d.setTwoSided( 1 )

    # set the background colour to black
    base.setBackgroundColor( 0, 0, 0, 0, self.win )

    # set the data root which will (hopefully) handle messages
    self.dataRoot = NodePath('myWin_dataRoot')
    self.dataRootNode = self.dataRoot.node()

    # loop through the devices and add them to the dataRoot
    for i in range(self.win.getNumInputDevices()):
      name = self.win.getInputDeviceName(i)
      mk = self.dataRoot.attachNewNode(MouseAndKeyboard(self.win, i, name))

    # add the dataloop to the task manager
    base.taskMgr.add(self.dataLoop, 'myWin_dataLoop', priority = -50)

  def testButton(self, buttonText):
    tmpButton = DirectButton( text = buttonText, scale = 0.5)
    tmpButton.setPos(0, 0, 0)
    tmpButton.reparentTo( self.render2d )

  def dataLoop(self, state):
    # traverse the data graph.  This reads all the control
    # inputs (from the mouse and keyboard, for instance) and also
    # directly acts upon them (for instance, to move the avatar).
    base.dgTrav.traverse(self.dataRootNode)
    return Task.cont
    
# Panda startup 
base.setBackgroundColor(0, 0, 0)
base.disableMouse()
camera.setPos ( 0, 0, 45 )
camera.setHpr ( 0, -90, 0 )

# My test program
newWin = myWin()
newWin.openWin( 100, 100, 0, 0 )
newWin.testButton('A Button!')

# *I used this line in first test and not the dataRoot node stuff in class*
#base.setupMouse(newWin.win)

# run app
run()

When I try to use base.setupMouse( newWin.win ) I got this error…
File “DataGraphTraverser”, line 92, in traverse
AssertionError: has_size() at line 146 of windowProperties.I

So I attempted to setup a similar thing to base with a dataRoot and a dataLoop function. However that generated the same error,

I also tried a few other things ( forgot exactly, I was just toying around ) and I was getting quite a few ‘AssertionError: has_size()’ errors.

I guess I must be doing something wrong with the windowProperties? Or perhaps I have completely misunderstood this :slight_smile:

Any advice greatly appreciated.

I apologize for not getting a chance to research this more carefully for you yet. But I can answer the current question.

The assertion failure is triggered because the window has not finished opening yet. When base.openWindow() returns, the window object has been created but the window itself is not yet visible onscreen. Usually that’s not a problem, but some parts of the code (like the MouseWatcher) need to know what the actual size of the window is going to be, so they require the window to be fully opened before you use them.

To resolve this, call base.graphicsEngine.openWindows() after you call base.openWindow(). This will not return until the windows have been fully manifested.

As to the dataRoot thing, that’s Panda’s way of representing the flow of data from the user’s input devices. Panda uses a special variant on the scene graph called the “data graph”; each node in the data graph represents a step in the data pipeline. In general, data flows from parent nodes to child nodes.

David

You dont have to apologize, the help you have given so far is great. You are a real asset to thie panda3d project and any help, no mater how small, is greatly appreciated.

I am having fun tinkering with the current code and examining the ‘direct’ python source. Its a great way to learn a new language :slight_smile:

Just a shame I have very little time at the moment to be able to spend more than 5mins at a time reading up, etc.

Ill have a play when I get back tonight with the openWindows function and then try to extend the code a little more.

Thanks again for your help.

Ok, I had a little play and had a button on the main window clicking when I clicked the 2nd windows button. dataroot makes complete sense now :slight_smile:

I had a bit more of a look around the ShowBase code and I think I understand why multiple windows seem to be a little complex when compared to the main window :wink:

Im going to see if its possible to extend the ShowBase code in some way ( just for a play ) without breaking it :slight_smile:

Some questions I have,

when windows are created you need to use a ‘pipe’ and ‘gsg’. I presume the ‘pipe’ is the actual graphics device to use but I dont understand what the ‘gsg’ is in call to self.graphicsEngine.makeGsg(pipe). Perhaps its the “actual” os window?

I think that each window will be created with the same ‘pipe’ but do they need a seperate ‘gsg’ ? Perhaps the answer to that will probably depend on the first question :slight_smile:

This one is hard to explain,
How does the dataRoot interact with the MouseAndKeyboard node?
I noticed that this node is attached to the dataRoot and then the parent node of buttonThrowers[] ( which does not seem obvious in the code ) is the MouseWatcher node. This node (mw) is is then given to the aspect2d.node().setMouseWatcher(mw.node())

I think :slight_smile:

So if I do the same kinda setup for a new window, can I then parent the new MouseAndKeyboard node to the base.dataRoot so that when dataLoop is called it will run through my new windows data nodes?

Man, I really hope that makes sense :slight_smile:

ShowBase does do a lot of setup stuff in base.setupMouse(). Much of it is for very esoteric reasons, and a lot of it can be omitted if you just want to use the window for basic DirectGui operations. The key things that we need to make the DirectGui system work are (a) a MouseAndKeyboard object in the the data graph, that reports the mouse data from the window in question, (b) a MouseWatcher parented to the MouseAndKeyboard, which monitors this mouse data and feeds it to the DirectGui system, and © a PGTop node, serving the same purpose as aspect2d, which must be the root of all DirectGui objects and polls the MouseWatcher for mouse information.

Here is the sample code, modified to work with multiple windows:

import direct.directbase.DirectStart
from direct.gui.DirectGui import *

# Create an auxiliary window
wp = WindowProperties()
wp.setSize(200, 200)
wp.setOrigin(0, 0)
win = base.openWindow(props = wp, aspectRatio = 1)

# Set up a 3-d scene for this auxiliary window.
myRender = NodePath('myRender')
base.camList[-1].reparentTo(myRender)

# Set up a 2-d scene for GUI objects in this window.
myRender2d = NodePath('myRender2d')
myCamera2d = base.makeCamera2d(win)
myCamera2d.reparentTo(myRender2d)

# Turn off certain rendering attributes that are inappropriate for 2-d
# objects.
myRender2d.setDepthWrite(0)
myRender2d.setMaterialOff(1)
myRender2d.setTwoSided(1)

# Force the window to be open now.
base.graphicsEngine.openWindows()

# Set up an aspect2d node on this new window, which serves as the root
# of all DirectGui items.  Tell this node to use device 0, the default
# (and often only) keyboard/mouse.
mk = base.dataRoot.attachNewNode(MouseAndKeyboard(win, 0, 'myWindowMouse'))
mw = mk.attachNewNode(MouseWatcher('mw'))
myAspect2d = myRender2d.attachNewNode(PGTop('myAspect2d'))
myAspect2d.node().setMouseWatcher(mw.node())

# Create a pair of buttons, one on the new window and one on the
# original window, to demonstrate the whole thing working.

def clickedButtonA():
    print 'clickedButtonA'

def clickedButtonB():
    print 'clickedButtonB'

buttonA = DirectButton(parent = myAspect2d,
                       text = 'Button A', scale = 0.5, relief = RAISED,
                       command = clickedButtonA)

buttonB = DirectButton(parent = aspect2d,
                       text = 'Button B', scale = 0.5, relief = RAISED,
                       command = clickedButtonB)

Now, to answer your specific questions. :slight_smile:

The ‘pipe’ does indeed represent the graphics device; it is a Panda object called the GraphicsPipe. The ‘gsg’ is short for GraphicsStateGuardian; this object corresponds to a graphics context. It is possible to have multiple graphics contexts on a single pipe, as many as one per window, but typically, all windows share the same GSG (and I think the panda DirectX implementation requires this anyway).

Oops, I have to run upstairs now; but hopefully the above example code will serve to clarify your questions about the data graph.

David

Thanks for the info David. I have been merrily modding ShowBase to learn how it glues everything together and I think Im getting there :slight_smile:

I took out alot of the features of ShowBase and put them into a new class called windowHandler, this contains (as close as you can get to) a class static reference to the graphicsEngine for window creation. This is why I was wondering about the gsg :wink: Now I know its the context I can build that in properly.

So the base I am using is now called ShowMultiBase and basically does everything ShowBase does but contains a list of windowHandlers. To keep it as close to ShowBase I have self.mainWindow = windowHandler() and all the self.win calls have been changed to self.mainWindow.win This way I can imitate the ShowBase calls to some success. Infact, I might change this at some stage to windowList[0]

When I ran my first (after cleaning up the typos) test I managed to get the base window displaying, so tonight I will finish the showMouse function. This will return the node to reparent to the base.dataRoot in the ShowBaseMulti, as I think this is probably the tidiest way to handle it all.

If that works then its just a case of…

multiBase.openWindow(foo)
myWin = multiBase.windowList[-1]
myWin.setupRender2d()
myWin.makeCamera2d()
myWin.waitForOpen()
multiBase.enableInput(myWin)

Well, thats the plan :slight_smile:

Anyway, this is only a test to learn some python and panda so Im not too bothered if it goes haywire :laughing:

Oops! Forgot I was not logged in.

A beer and 30mins of coding…

http://usera.imagecave.com/The_Sandman/pandamulti.jpg

File Size is ~139k

I created a new panda install and replaced ShowBase with this modded one. Now I wonder how much is broken :slight_smile: I have a few small issues still to resolve and then I might test it out with airblade to see if its backwards compatible with it. Somehow I dont think it will be :laughing: but should help me learn a little more about python in the attempted process :smiley:

Thanks for your help David, you steered me in the right path although I seem to have taken the scenic route :wink:

Great job!

David