Is there a better way to assign dimensions to GUI objects?

Looking at the example in the manual for creating a DirectButton:

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

#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():
        bk_text = "Button Clicked"
        textObject.setText(bk_text)

#add button
b = DirectButton(text = ("OK", "click!", "rolling over", "disabled"),scale=.05,command=setText)

#run the tutorial
run()

It’s very painful to design a complex gui and translate coordinates by hand to the 2.6x2.0 aspect3d coordinate system.

Would it be possible to create some code to change the aspect3d coordinate system to use 800x600 screen coordinates?

I design my GUIs with gimp using guides then i translate by hand. It would be easier to setup a transformation in advance then use the same values i used in gimp to layout gui elements:

...
#add some text
bk_text = "This is my Demo"

( Code to change the aspect2d coordinate system to use 800x600 with (0,0) position on top-left corner)

# from now on pass coordinates for the new system

textObject = OnscreenText(text = bk_text, pos = (685,585),  ...
...

Gui objects are NodePaths (acording to the manual) so would this be possible and how? Thanks in advance.

Sure. aspect2d is an ordinary node like anything else; by default, it has a transform set up on it to scale the default range from (1, 1) to (1.333, 1). You can change that transform however you like, for instance:

aspect2d.setScale(1./400, 1, 1./300)
aspect2d.setPos(-1, 0, -1)

This will set up your screen so that it ranges from (0, 0) in the lower left corner to (799, 599) in the upper right. Of course, this is upside-down from the normal pixel coordinates. You could do it this way instead:

aspect2d.setScale(1./400, 1, -1./300)
aspect2d.setPos(-1, 0, 1)

which flips the axes around so that (0, 0) is now in the upper left, and (799, 599) is in the lower right. This will also introduce the unfortunate side effect of inverting all of the text and graphics on your DirectGui objects, so you may need to specify a scale of something like (30, 30, -30) on each object, to make a button 30 pixels high. Or, if you are painting your own graphics, you could simply paint them upside-down; and you could invert just the text with a text_scale like (1, -1) (the text_scale parameter is 2-dimensional, unlike the overall scale parameter).

David

David

Note also the handy Config.prc variable:

direct-gui-edit 1

which allows you to place widgets interactively using the mouse, without having to fuss with coordinates much at all.

David

“This will also introduce the unfortunate side effect of inverting all of the text and graphics on your DirectGui objects, so you may need to specify a scale of something like (30, 30, -30) on each object, to make a button 30 pixels high.”

Say are the arguments we pass to the directgui object constructors transformed by aspect2d node? In that case it would not be a problem since the coordinates would be inverted back after transformed by aspect2d. If not then perhaps we could use a new dummy node applied to aspect2d and apply the transformation to this dummy node, then parent all direct gui objects to the dummy node.

One sugestion it would be great if we could set the coordenate system used by directgui objects with just one command like:

DirectGUI.setGUICoordinateSystem(SCREEN)
DirectGUI.setGUICoordinateSystemScreenResolution(1600,1200)

Anyway i solved the problem in a way by creating a class with utility methods to translate arguments in directgui object constructors. This avoids the problem of inverted text and images.

class GUICoordTransl:
  # sx is screen x
  # sy is screen x
  # sl is screen left
  # sr is screen right
  # sb is screen bottom
  # st is screen top
  # the same applies to panda coord variables
  def __init__(self,sl,sb,sr,st,pl,pb,pr,pt):
    self.sl = sl
    self.sb = sb
    self.sr = sr
    self.st = st
    self.pl = pl
    self.pb = pb
    self.pr = pr
    self.pt = pt

  # Proportions to translate from on space to the other:
  # (sx-sl)/(sr-sl)=(px-pl)/(pr-pl)
  # (sy-sb)/(st-sb)=(py-pb)/(pt-pb)

  # px = pl + (sx-sl)*(pr-pl)/(sr-sl)
  def topx(self,sx):
    return self.pl + (sx-self.sl)*(self.pr-self.pl)/(self.sr-self.sl)

  # py = pb + (sy-sb)*(pt-pb)/(st-sb)
  def topy(self,sy):
    return self.pb + (sy-self.sb)*(self.pt-self.pb)/(self.st-self.sb)

  # sx = sl + (px-pl)*(sr-sl)/(pr-pl)
  def tosx(self,px):
    return self.sl + (px-self.pl)*(self.sr-self.sl)/(self.pr-self.pl)

  # sy = sb + (py-pb)*(st-sb)/(pt-pb)
  def tosy(self,py):
    return self.sb + (py-self.pb)*(self.st-self.sb)/(self.pt-self.pb)

  def dist(self,a,b):
    if a >= b: return a - b
    else: return b - a

  def pwidth(self):
    return self.dist(self.pr,self.pl)

  def pheight(self):
    return self.dist(self.pt,self.pb)

  def swidth(self):
    return self.dist(self.sr,self.sl)

  def sheight(self):
    return self.dist(self.st,self.sb)

  def topw(self,sw):
    return sw/self.swidth()*self.pwidth()

  def toph(self,sh):
    return sh/self.sheight()*self.pheight()

  def tosw(self,pw):
    return pw/self.pwidth()*self.swidth()

  def tosh(self,ph):
    return ph/self.pheight()*self.sheight()