nube tries to make dictionary, fails

2.49b: I’m trying to make a dictionary data structure comprised of objects and some related “labels”, so that when a mouseover on an object occurs, the appropriate label will then be made visible.

Intention is on mouseover, run through the dictionary, when find object, make associated “label” visible.

But I can’t seem to properly create such a dictionary. Here’s one of what I’ve tried:

dictionary for objects and their labels

obLabels = {Suzanne:FontSuz, LittleSuzanne:FontLittleSuz }

For the above particular attempt, I get error:
NameError: name ‘FontSuz’ is not defined

I’ve tried various alternatives, like OBFontSuz, etc, none work, though there are objects and fonts with those names.

So first question would be: how to properly make such a dictionary?

Second question would be: if I make the dictionary in one script how can/can I use that dictionary in another script?

If you simulate a compiler or parser you would ask the question:
what is FontSuz?

As you can’t answer the question you would say:

NameError: name ‘FontSuz’ is not defined.
If you look at the code it really isn’t present nor Suzanne, nor LittleSuzanne, nor FontLittleSuz.

Anyway I assume this is just a small syntax error. Strings must be enclosed in ’ or "

e.g

 
obLabel = {
  "Suzanne": "FontSuz"
, "LittleSuzanne" : "FontLittleSuz"
}

You store the dictionary somewhere.
Eg at GameLogic.globalDict


import GameLogic
...
GameLogic.globalDict["myDict"] = obLabel


import GameLogic
...
obLabel = GameLogic.globalDict["myDict"]

BTW you could stored it in a separate module e.g. MyLabels.py

and simply import it:


from MyLabels import obLabel
 
print obLabel.get("Suzanne")

PS: Do not forget to strip of the OB from the GameObject’s name in 2.49!

ok, thanks, I’ll give it a try!

I thought that since dictionaries could store anything, including objects, and Suzanne and FontSuz were objects that I could include them as either Suzanne or OBSuzanne, which I think (at least OBSuzanne) could be recognized, and were accepted at some point, though FontSuz and Font.001 both were not. But I’ll give your suggestions a try, thanks again!

So, just to be sure, is that, to make the dictionary:

 
# dictionary for objects and their labels, <b>in an Init script</b>:
import GameLogic
ObjAndLabels = {"Suzanne":"FontSuz", "LittleSuzanne":"FontLittleSuz" }
GameLogic.globalDict["myDict"] = ObjAndLabels

and then, to use the dictionary in another script:

 
 import GameLogic
ObjAndLabels = GameLogic.globalDict["myDict"]

ok, so far that seems to work fine; so next step would be:

how do I now code something like:
if mouseover hitobject is “in” dictionary, pull out the “label” associated with it to use in:

 
if mOver.positive:
      objects["OBFontSuz"].visible = True
else:
      objects["OBFontSuz"].visible = False
 

replacing “OBFontSuz” with whatever is the font associated with the hitobject?

In other words, do I have to make a for loop to see if the hitobject is in the dict, or is there some kind of “find” (as in a set) type of function?

ok, gotta problem trying to use for loop.

loop is (with prints to help me know what’s happening):

 
print "going to do for loop now"
for n in ObjAndLabels:
     print "items in dict:"
     print n, ObjAndLabels[n]
     print "hit object is:"
     print new
     print "object in dict is:"
     print n
     print "check if new now:"
     if n == new:  #  <b>isn't this the right way to see if the first element in each item in dict is same as hit object?
</b>        print "FOUND IT!"
       print new

And the result is:

got into ShowText
OBSuzanne
going to do for now
items in dict:
OBSuzanne OBFontSuz (first set of items, which happens to include the hit object)
hit object is:
OBSuzanne <----- this hit object and below are the same
object in dict is:
OBSuzanne <------ this first object in dict and above are the same, but my test above fails to recognize that
check if new now:
items in dict:
OBLittleSuzanne OBFontLittleSuz
hit object is:
OBSuzanne
object in dict is:
OBLittleSuzanne
check if new now:
got into drawAline

Obviously I’m doing something wrong, but what?

My guess is that you’re not storing the game objects themselves in the dictionaries - just their names. You need to check if n == new.name. Also, you can store the game objects themselves with

{‘myobject’:scene.objects[‘OBMyObject’]}

YES, that was it! I had orginally meant to store the objects themselves, not necessarily for any good reason that I understood, but couldn’t figure out how to do so, and eventually storing the name worked up to a point so I did it that way. And your suggestion for testing for the name works, but I’ll try your suggestion for storing the object itself, 'cause I’ll need to use it.

(edit)
Is {‘myobject’:scene.objects[‘OBMyObject’]} one item of two elements in the dictionary? there’s a colon in your example, and I have to use a colon to separate elements of each item in the dictionary, right?

My intention is to have a dictionary “list” many items, with two elements per item, the two elements are: an object and its associated font.

Why do you not store the label in a proprty of the gameObject?

Then you already have the right label

eg.

label = own[“myLabel”]

Just an idea

Monster: that sounds like a perfectly wonderful idea, but even with your example I don’t yet know how to do that.

The objects and their fonts that I was as a test storing in dictionary were these:
ObjAndLabels = {“OBSuzanne”:“OBFontSuz”, “OBLittleSuzanne”:“OBFontLittleSuz” }

what would I do specifically to store OBFontSuz as a property of OBSuzanne?
(and then to use it, though I think your example is for that?)

if you want to actually reference a GameObject, then use

objects = GameLogic.getCurrentScene().objects
Suzanne = objects[“OBSuzanne”]
FontSuz = objects[“OBFontSuz”]
objAndLabels = {} #empty dictionary

objAndLabels[Suzanne] = FontSuz

that sets the GameObject reference of Suzanne (in the dicitonary) as the key to the door for FontSuz

a GameObject reference is the term for a variable which acts as a reference to an object in the game.

To access the value of Suzanne, simply type

value = objAndLabels[Suzanne]

There are several options. Here is one:

Store the name of the label in a property at the object it belongs to:

E.g. OBSuzanne: “label” String “OBFontSuz”

So you have the object and the name of the label.

When you want to use the label as object, you can look for the label. Than you can store the reference to it that you do not need to search again.


import GameLogic
try:
  import Mathutils
  PREFIX = "OB" #BGE 2.49
except ImportError:
  PREFIX = "" #BGE 2.5x
 
PlabelObject = "labelObject"
PlabelName = "label"
 
...
# Usage example1 - if you want to execute the code at the object 
myLabel = getLabel(own)
myLabel["Text"] = ("I'm the label of %s" % (own.name))
...
# Usage example2 - if you want to execute the code at another object 
scene = GameLogic.getCurrentScene()
suzanne = scene.objects[PREFIX+"Suzanne"]
 
myLabel = getLabel(suzanne)
myLabel["Text"] = ("I'm the label of %s" % (own.name))
...
 
 
def getLabel(obj):
  label = obj.get(PlabelObject)
  if label is not None:
    return label # we already know the label just return it
 
  labelName = obj.get(PlabelName)
  if labelName is None:
    print ("Error: %s['%s'] not set" % (obj.name, PlabelName) )
    return
 
  label = searchScenesFor(labelName) # find the label
  if label is None:
    print ("Error: object '%s' not found in any scene" % (labelName) )
    return
 
  obj[PlabelObject] = label # here we store the reference to the label
  return label
 
def searchScenesFor(name):
  nameToFind = PREFIX+name
  for scene in GameLogic.getSceneList():
    for obj in scene:
      if obj.name == nameToFind:
        return obj
  return None
 

Fully untested ;).

agoose77, ok, referencing a GameObject as you suggested is working for me, thanks!

Monster, I haven’t yet tried your suggestion re: setting fonts as Properties of Objects, BUT, the statement
“if label is not None:”
is VERY interesting to me, as my code has been having a problem when the mouse LEAVES an object, the statements

mOver = c.sensors[“MouseOff”]

get the hit object

new = mOver.hitObject

have been giving me problems because ‘new’ will return ‘None’, and I have been having trouble dealing with it.

I finally said:
if not new:
#make ALL labels invisible in a for-loop(not very efficient!)
else:
#present the appropriate label

which semi-works, except when mouse moves from one object directly to another, still working on that;
so I’ll look carefully at your code suggestion to see if it might solve that too, thanks!

I’m not sure how to write this for 2.5, and it would only work with a bitmap text object, but this is what I use for something similar to what you’re describing.

I have 2 scenes, the Main scene with the objects, and the HUD scene with text and info.

In HUD you have a text object (in 2.5) or some bitmap text that will be displaying the text you assign to it.
In Main scene, you have objects that have a String property named “info”. Each property can describe the object how ever you want it to.

The Camera in Main scene overlays the HUD scene, and uses this script below to get the ‘info’ property from the object you mouse over, and apply that text to the Display text object in the HUD scene.


from bge import logic as l

##########[ Main Function ]##########

def main():
    c = l.getCurrentController()
    o = c.owner
    
    # Get display text object from HUD scene
    dt = obs('hud')['displaytext']
    
    over = c.sensors['over']
    hit = over.hitObject
    
    if over.positive:
        # Check if hit object has 'info' property
        if 'info' in hit:
            info = hit['info']
            dt.text = info
        else:
            dt.text = ""
    else:
        dt.text = ""

##########[ Internal Functions ]##########

# Get scene:
#   s() = current scene
#   s('name') = specific scene
def s(var=None):
    sce = None
    if var != None:
        sl = l.getSceneList()
        sce = None
        for i in sl:
            if i.name == var:
                sce = i
    else:
        sce = l.getCurrentScene()
    
    return sce

# Get object list
def obs(var=None):
    sce = s(var)
    if sce != None:
        return sce.objects

Tested and works in 2.5

If you have a look at the code (just the Main Functions section), you can see where it gets the ‘info’ property from the object. After that, it just does what ever I wanted it to do. You don’t need to assign it to a text object, you could store it somewhere else or where ever you need it.

You can have the .blend here if you want to try it in 2.5. Apologies for not making it in 2.49 but I don’t have that version on file anymore, but I would also suggest learning Python in Blender 2.5 instead of 2.49. Will save you many headaches in the future :wink:

hitobject hud info.blend (90.7 KB)

is good too

if not … checks for not 0/not None/not 0.0/not “”/not len(x)==0

which is a bit differnt to just
if is not None:
as this does not performes the other checks

In your context this is fine, as hitObject is a gameObject or None.

Alternative:
you can check the sensor for being positive too.

If you use a mouse over any sensor enable the [Pulse] button. The controller will be triggered on hiObject change.

@riyuzakisan:

beside the usage of bge.logic rather than GameLogic I think your code should run in 2.49 too.

I do have the Pulse button for mouseover any for my showText script, and that does effectivly yield presenting the label, my problem was making the previous label invisible, that is, first detecting the change from one object to another, which, understanding my lack of understanding vs your understanding, you’re probably right that I could use that somehow; what I did, instead, (because it was easy once I thought of it) was just use a sledge-hammer, and make every font invisible immediatly before the font associated with the mouseover object was made visible, via a for-loop.

Sloppy, inelegant, and may bite me when I have full many-part model in the viewer, but it works for now.

I have a “spiff it up” question, but I’ll make a new thread for that. (draw line from font to mouse instead of to object center)

Riyuzakisan, I’ve made it work using dictionary, so I’ll put your suggestion in my back pocket in case I need to change how I do it when I have many more objects & labels in real thing. I suspect using Properties is a much better solution!

And, naturally enough, many people suggest ditch 2.49 & step up to 2.5, & I also suspect I’ll need to when I put in my real model, what 2.5 version is nicely stable?

2.58a - the latest version - is the most stable, actually (if I recall).