Direct GUI

I am working on a system which could benefit from GUI menus and interaction elements. I am finding the documentation for Direct* woefully inadequate. Does anyone have an example where DirectFrame is used to actually perform what it advertises to do. In the following code i was hoping that my DirectEntry and DirectLabel would be relative to the frame that i was defining. The manual entry for Direct Entry is empty to say the least.

Here is the code i have been playing with.

import direct.directbase.DirectStart
from direct.gui.OnscreenText import OnscreenText
from direct.gui.DirectGui import *
from direct.showbase.DirectObject import DirectObject  

from pandac.PandaModules import TextNode

#add some text
bk_text = "This is my Demo"
textObject = OnscreenText(text = bk_text, pos = (0.95,-0.95), 
scale = 0.07,fg=(1,0.5,0.5,1),align=TextNode.ACenter,mayChange=1)

#callback function to set  text
def setText(arg):
    bk_text = str(arg)
    textObject.setText(bk_text)
#end setText




#clear the text
def clearText():
    entry.enterText('')
#end clearText

parentFrame = True

def toggleParent():
    global parentFrame
    if parentFrame:
        setText('Parented to World')
        entry.reparentTo(render2d)
        label.reparentTo(render2d)
        parentFrame = False
    else:
        setText('Parented to Frame')
        entry.reparentTo(frame)
        label.reparentTo(frame)
        parentFrame = True
    #end if
#end toggleParent
    

frame = DirectFrame(frameSize = (-.5,.5,-.5,.5), frameColor=(.3,.2,.1,1))
#frame.reparentTo(render2d)

entry = DirectEntry( pos = (-.5,0,-.5), text = "" ,scale=.05,command=setText,
initialText="Type Something", numLines = 2,focus=1,focusInCommand=clearText)

entry.reparentTo(frame)



label = DirectLabel(parent =frame,text="this is a label", scale = 0.05,pos = (-.5,0,.8))


input = DirectObject()
input.accept('a', toggleParent)       

run()

Obviously i am not understanding what the frame object is doing. Any examples of a hierarchical GUI using direct objects?

cheers

juancho

If you want your other objects to be relative to the frame, you have to parent them to the frame, with “parent = frame” in the keyword list to the constructor, or label.reparentTo(frame) after it is constructed.

David

If you read the code that i posted I do exactly both of those. My issue is that the behaviour of the frame is not consistent with the documentation. Run the program i posted where i parent the two objects to the frame and to the render2d node respectively and see what happens.

sillyfunnypedro

I’m not quite sure what you expect to be happening in the two cases. Can you elaborate on what it is doing wrong when you parent the entry and label to the frame?

It is true, however, that you cannot parent any DirectGui objects to render2d and expect them to work: you have to parent them to aspect2d or lower.

David

What i was expecting in this example was to see the two children enter and exit the frame as their parents were toggled.

With any other gui tool kit i have used a frame is used to lay out objects relative to its own geometry. With DirectFrame the lay out of the objects is not relative to its own geometry. If the frame has a size of -.5 .5 -.5 .5 and i specify a button to be at -.8 -.8 i would expect the location of the button to be -.4 -.4 of screen resolution. with this current implementation of i cannot get the child nodes of the frame to be relative to the size of the frame.

sillyfunnypedro

Ah, I see. Right, that is a common convention of existing 2-D Gui toolkits. DirectGui is a little different, because it also respects the conventions of the 3-D scene graph, which are only somewhat compatible with these established 2-D conventions.

In this case, what happens is that the transform of the parent is inherited by the children, in the same way that a transform is inherited down the 3-D scene graph (in fact, the render2d scene graph is really precisely the same as the render scene graph, with the only difference being the nature of the lens that views it).

This means that the origin and scale of the parent is inherited by its children. If you apply a scale to the DirectFrame, it correspondingly scales all of its children. Similarly, if you execute a frame.setPos(), moving the DirectFrame, this also moves all of its children by the same amount.

But we can’t impose an automatic scale on the DirectFrame that would scale all of the children to the range -1…1 across the frame’s border. The main reason we can’t do this is that this would also scale the size of the children, in a nonintuitive and not particularly useful way.

So, you just need to understand that to position a child within a DirectFrame’s borders, you use the same coordinate range as the frame itself. Thus, to position the button within the frame of -.5 .5 -.5 .5, it must have a position within the range -0.5 … 0.5 in both dimensions. However, once it has been placed, it will remain in this position relative to the DirectFrame, even if you move the DirectFrame to a new location or size with setPos() or setScale().

On the other hand, if you really do want to achieve a range of -1 … 1 within the DirectFrame, you can do this too. Just create a DirectFrame with a frame of size -1 1 -1 1, and use the scale and pos to position the frame where you want it onscreen. Now all of its children should be positioned within -1 … 1, and they will appear within the corresponding part of the parent. Of course, they will also be scaled in strange ways, but if you are expecting this you can counterscale them appropriately (i.e. choose a size appropriate to their new coordinate space).

The key thing to keep in mind with the DirectGui system is that the “frame” of a DirectGui object does not define its coordinate space. Instead, the coordinate space is inherited from the scene graph. You can choose a frame that exactly matches its coordinate space, if you prefer it that way; or you can choose a frame that is independent of its coordinate space.

David

Ok, that now explains things.

Here is the code that now has the behaviour i was looking for.

import direct.directbase.DirectStart
from direct.gui.OnscreenText import OnscreenText
from direct.gui.DirectGui import *
from direct.showbase.DirectObject import DirectObject  

from pandac.PandaModules import TextNode

#add some text
bk_text = "This is my Demo"
textObject = OnscreenText(text = bk_text, pos = (0.95,-0.95), 
scale = 0.07,fg=(1,0.5,0.5,1),align=TextNode.ACenter,mayChange=1)

#callback function to set  text
def setText(arg):
    bk_text = str(arg)
    textObject.setText(bk_text)
#end setText




#clear the text
def clearText():
    entry.enterText('')
#end clearText

parentFrame = True

def toggleParent():
    global parentFrame
    if parentFrame:
        setText('Parented to World')
        entry.reparentTo(render2d)
        label.reparentTo(render2d)
        parentFrame = False
    else:
        setText('Parented to Frame')
        entry.reparentTo(frame)
        label.reparentTo(frame)
        parentFrame = True
    #end if
#end toggleParent
    

frame = DirectFrame(frameSize = (-1,1,-1,1), frameColor=(.3,.2,.1,1))
#frame.reparentTo(render2d)
frame.setScale(0.5,0.5,0.5)
entry = DirectEntry( pos = (-.5,0,-.5), text = "" ,scale=.05,command=setText,
initialText="Type Something", numLines = 2,focus=1,focusInCommand=clearText)

entry.reparentTo(frame)



label = DirectLabel(parent =frame,text="this is a label", scale = 0.05,pos = (-.5,0,.8))


input = DirectObject()
input.accept('a', toggleParent)       

run()

Thanks for this.

sillyfunnypedro

So i have been playing a little with the direct GUI stuff. In the following demo i illustrate the nestability of the different types of direct frames and buttons.

import direct.directbase.DirectStart
from direct.gui.OnscreenText import OnscreenText
from direct.gui.DirectGui import *
from direct.showbase.DirectObject import DirectObject  
import sys

from pandac.PandaModules import TextNode

#add some text
bk_text = "This is my Demo"
textObject = OnscreenText(text = bk_text, pos = (0.95,-0.95), 
scale = 0.07,fg=(1,0.5,0.5,1),align=TextNode.ACenter,mayChange=1)

#callback function to set  text
def setText(arg):
    bk_text = str(arg)
    textObject.setText(bk_text)
#end setText



[code]
#clear the text
def clearText():
    entry.enterText('')
#end clearText

parentFrame = True
frame = DirectFrame(relief = DGG.RAISED, borderWidth = (0.05,0.05), frameSize = (-1,1,-1,1), frameColor=(.3,.2,.1,.5))

x = 0;
z = 0;
def nudgeLeft():
    global x,z,frame
    if x > -0.5:
        x -= 0.05
    #end if
    frame.setPos(x,0,z)
#end nudgeLeft
def nudgeRight():
    global x,z,frame
    if x <  0.5:
        x += 0.05
    #end if
    frame.setPos(x,0,z)
#end nudgeLeft

def nudgeUp():
    global x,z,frame
    global x
    if z< 0.5:
        z+= 0.05
    #end if
    frame.setPos(x,0,z)
#end nudgeUp


def nudgeDown():
    global x,z,frame
    global x
    if z> -0.5:
        z-= 0.05
    #end if
    frame.setPos(x,0,z)
#end nudgeDown

def processText(text):
    if text == "reset":
        x = 0; z = 0; frame.setPos(x,0,z)
    elif text == "quit":
        sys.exit()
    else:
        setText(text)
    #end if
#end processText
    

def clearText():
    global entryText
    
    entryText.enterText('')
#endclearText
def resetWorld():
    global x,z,entryText
    x = 0
    z = 0
    frame.setPos(x,0,z)
#end resetWorld


frame.setPos(x,0,z)
#frame.reparentTo(render2d)
frame.setScale(0.5,0.5,0.5)


buttonFrame = DirectFrame(parent = frame, relief = DGG.RAISED, borderWidth = (0.05,0.05), frameSize = (-1,1,-1,1), frameColor=(.5,.5,.5,.5), pos = (-.6,0,-.6))
DirectButton( parent = buttonFrame, pos = (-.6, 0, 0), text = "left", scale = .4, command = nudgeLeft)
DirectButton( parent = buttonFrame, pos = ( .5, 0, 0), text = "right", scale = .4, command = nudgeRight)
DirectButton( parent = buttonFrame, pos = (0, 0, 0.6), text = "up", scale = .4, command = nudgeUp)
DirectButton( parent = buttonFrame, pos = (0, 0, -.6), text = "down", scale = .4, command = nudgeDown)

buttonFrame.setScale(0.3,0.3,0.3)



DirectButton( parent = frame, pos = (.8,0, -.8), text = "exit", scale = .1, command = sys.exit)
DirectButton( parent = frame, pos = (.8,0,-.65), text = "reset", scale = .1, command = resetWorld)

entryText = DirectEntry( parent = frame, pos = (-.8,0,.5), text = "", scale = .1, command = processText, 
             initialText = "typesomething", numLines = 1, focusInCommand = clearText, relief = DGG.GROOVE)



label = DirectLabel(parent =frame,text="Nested Direct controls", scale = .1,pos = (-.5,0,.8), relief = DGG.RIDGE)


input = DirectObject()
input.accept('escape', sys.exit)       

run()
[/code]

The parenting/inheritance now makes sense to me.  Hopefully this helps some other people understand.

sillyfunnypedro