3
$\begingroup$

In the following script I want to perform the loading of a DLL only once when initializing

import ctypes
import mathutils
from ctypes import *

hmd = None

def poll():
    if hmd is None:
        hmd = ctypes.WinDLL("C:\\Program Files\\Blender Foundation\\Blender\\2.68\\scripts\\addons\\game_engine_rift\\openhmd.dll")
    hmdPoll = hmd.poll
    hmdPoll.argtypes = ()
    hmdPoll.restype = ctypes.POINTER(ctypes.c_float)
    pos = hmdPoll()
    print(pos[0], pos[1], pos[2] ,pos[3])
    bge.logic.rotation = mathutils.Quaternion((pos[0], pos[1], pos[2], pos[3]))

poll()

But I get this error:

Python script error - object 'Camera', controller 'Python1':
Traceback (most recent call last):
  File "rift.py", line 21, in <module>
    poll()
  File "rift.py", line 12, in poll
    if hmd == None:
UnboundLocalError: local variable 'hmd' referenced before assignment
$\endgroup$
  • $\begingroup$ Note, this isn't specific to blender (its a general python question), could be moved to stack-overflow. $\endgroup$ – ideasman42 Sep 2 '13 at 21:36
  • $\begingroup$ For initializing python scripts specifically in Blender (eg, after loading a file) see handlers $\endgroup$ – brita_ Oct 17 '16 at 23:57
  • 1
    $\begingroup$ I'm voting to close this question as off-topic because it is a general Python problem which isn't specific to Blender. $\endgroup$ – Ray Mairlot Oct 5 '19 at 13:14
4
$\begingroup$

It's because your variable hmd isn't global...
You can declare it as global, with global hmd in your function:

hmd = None

def poll():
    # declare hmd as global so that the function has (write) access to the global var.
    global hmd
    if hmd is None:
        hmd = ctypes.WinDLL ("C:\\Program Files\\Blender Foundation\\Blender\\2.68\\scripts\\addons\\game_engine_rift\\openhmd.dll")

Or send it as parameter when your call poll() (you have to declare it in the function too):

def poll(hmd):
    if hmd == None:
        # (...)

poll(hmd)
| improve this answer | |
$\endgroup$
  • $\begingroup$ Thanks for your response, with first option I now get:"NameError: global name 'hmd' is not defined". The error occurs in the IF condition. $\endgroup$ – stacker Sep 1 '13 at 11:44
  • $\begingroup$ Well, I don't know what happens (you probably have not share the entire script in your question) , but if you want more info about global variables: stackoverflow.com/questions/423379/… $\endgroup$ – Polosson Sep 1 '13 at 12:31
  • $\begingroup$ this explained what I did wrong, I edited your answer to point this out. $\endgroup$ – stacker Sep 1 '13 at 13:01
6
$\begingroup$

The existing answer from @Polosson is correct, however Im a bit biased against globals since I have had to debug other peoples code with many globals - it can become difficult, if the code becomes complex and there are more then 2-3 of them used across multiple functions. It's a pain to keep track of them, forgetting to add a global keyword, it can fail in a non-obvious way.

Store in a global dictionary

Heres an alternative, which I prefer in some cases.

# store all runtime variables here.
runtime = {}
runtime["hmd"] = None

def poll():
    # declare hmd as global so that the function has (write) access to the global var.
    if runtime["hmd"] is None:
        runtime["hmd"] = ctypes.WinDLL("C:\\Program Files\\Blender Foundation\\Blender\\2.68\\scripts\\addons\\game_engine_rift\\openhmd.dll")

You can store the runtime state in a dictionary, and it has some advantages that you can easily print(runtime), runtime.clear()... etc. and pass it to other modules if you end up splitting your code across files.

Because the dictionary its self is never re-assigned, you don't have to define it as global.

Store in the function

Another trick, is you can store the variable in the function (and avoid using globals)

Note that this is not the functions local namespace, which is why it has to be accessed as an attribute of the function, even when inside the function body.

def poll():
    # declare hmd as global so that the function has (write) access to the global var.
    if poll.hmd is None:
        poll.hmd = ctypes.WinDLL("C:\\Program Files\\Blender Foundation\\Blender\\2.68\\scripts\\addons\\game_engine_rift\\openhmd.dll")
poll.hmd = None

Sometimes I use this for global variables that only the function ever accesses.

For more info on function attributes see:
Q: Python function attributes - uses and abuses

| improve this answer | |
$\endgroup$
2
$\begingroup$

Rather than testing values, it's more pythonic to just try and deal with exception(s) when they arise (EAFP). You can also declare and assign your global variable in one step like so:

def poll():
    try:
        hmdPoll = hmd.poll
    except NameError:
        globals()["hmd"] = ctypes.WinDLL("/path/to/dll")
        return poll() # Retry
    # ...

Warning: Catching NameError exceptions in general is a bad idea, use with care.

Personally I'd recommend explicitly initialising hmd outside of the poll function.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Don't necessarily agree with this being more Pythonic, raising exceptions is OK but normally I only do this when its less involved to try/except then it is to check with an if/else (there are many cases where it is, but this isn't one of them), note that exception handling does have overhead in Python too. Using exceptions you run the risk of some real error in the try block becoming hidden and the except case running every time (without you being aware of it). Of course in this example its not likely to be a problem. $\endgroup$ – ideasman42 Sep 2 '13 at 18:06
  • $\begingroup$ @ideasman42 Actually in cases where exceptions are not expected to occur often, it's normally faster. See here. $\endgroup$ – Aldrik Sep 2 '13 at 18:47
  • $\begingroup$ sure, though in this case I would recommend to use. hmd_poll = getattr(hmd, "poll", None) then check the result isn't None, rather then try/except $\endgroup$ – ideasman42 Sep 2 '13 at 21:16

Not the answer you're looking for? Browse other questions tagged or ask your own question.