Python controller - Module vs. Script

Hi,

I have the feeling have the feeling there is no real understanding of the two Python options the python controller offers. Therefore I added a new page to

wiki.blender.org.

I have to say I didn’t liked the module mode at the time it was introduced. After a short time I was able to see all the benefits. Now I really like them and skip the script mode without regret. It is very easy to transform a script into a module. Even a beginner can do that. Blender offers a nice template for new modules.

At wiki.blender.org is a very basic tutorial on module mode too.

Monster

Great page, this was new for me:

‘The entry point to a module can be changed by assigning a new value to the attribute “script”.’

Great - allow for efficient state machines, where each state is a entry point!

I was well aware of this.

:smiley:

Hey cool thanks for that nice explanation.

Once I had no idea why this doesn’t work as a module.

def SwitchKeyObj():
And surprisingly after adding “cont” it worked. Even if cont is nowhere defined.

def SwitchKeyObj(cont):

Edit:
So I wasn’t still sure if that was right scripted even if it is working

As far as I know both defs should work.
At the first one you just do not get the controller as argument. Which means you need to read it from the API.

As far as I know both defs should work.
Not by me. If I add for example a sensor it gets the error “cont is not defined”

def SwitchKeyObj():
Sens = cont.sensors[‘test’]

Edit:
Ok thanks LaH for your knowledge. :slight_smile:

You have to have cont in the parameter list if You use it in the function body - but not else… But we usually have use for cont so it’s a good habit to add it in the parameter list.

If you think about it should be naturally. You just write “cont”.
This is like shouting “Klaus” into your flat. There is not Klaus to listen to you. First you need to invite him.
You can do that by importing Klaus from somewhere else or by defining what Klaus is.

There are exceptions from this rule: the Python syntax (e.g. if, while, def) and the build-ins (e.h. print(), range(), list, object). They are implicit defined within the namespace of your code.

I hope this helps to understand this bit of Python.

Yes, the modules are very comfortable, and avoid having tons of scripts
so scripts too clean, too shorter (and faster!)

to initialize, however, only work if the object is not duplicated, seems
I prefer to use

if not “init” in own:
own[“init”] = 1

more secure

Thanks for the information, Monster. This is the stuff I need to get my head round this.

I am beginning to use module mode for all my Python- I don’t understand it totally but it seems the pain is worth it!

The object specific initialization can be placed in a separate entry point:

Triggered by an always sensor (no pulses)


def init(cont):
  ...whatever to initialize

and
normal processing
Triggered when needed.


def processing(cont):
  ... 

Make sure the initialization controller is placed above all others.

You get rid of the additional property.

short tutorial on converting:

if you use the “main” method. e.g.


import ...
def main():
  own = ...

main()

quickly turn into:


import ...
def process(cont): 
  '''
  Please choose a better name than "main" ;)
  '''
  own = ...

the plain script method:


import

own = ...

into


import ...
def processing(cont):
  own = ...

I hope it helps

Is there any recommended method for passing variables and constants from the init module to the processing module, and for passing variables from the processing module to itself, to be used in next iteration?
I currently use “globalization”, as in the example below, and it works well, but I’m not sure it is the most efficient solution.

def init():
        global const1, const2, X  # global definition of constants and variables
        const1, const2 = pi/4, 1.41 # define constants
        X = 0     # initialize state variable(s)

def processing():
        global X  # redefine X as global in order to make it available in next call
        X = (X + const1)/const2  # update X

Thanks.

I recommend to make each and every entry point (BGE callable function) independent from any other. That is a good idea even in script mode.
The following methods are independent from script or property modes. The examples show the module mode methods only.

An object initialization should initialize the object not the module, bge or whatever.
The interface between the entry points is the object itself (accessible via the controller). If you want to share data you should use this object. The simplest way is to use properties as before.

When you do that always ask yourself:

  • What happens if this code is called by another object?
  • What happens when this code is called from another scene (important on level switch and HUD)?

You can set up module initialization that is shared by all objects. In your example:


PI_QUARTER = math.pi / 4 # set up once never change ~ constant
CONST2 = 1.41 

def init(cont):
  x = <really complicated code>     # initialize function local variable(s)
  cont.owner.obj["X"] = x

def processing(cont):
  obj = cont.owner
  x =obj["X"]
  x = (x + PI_QUARTER)/CONST2  # update X
  obj["X"] = x

You can call init() from any object.
While executing processing() x and it s value always belong to the calling object. Because they are read from the property which belongs to the executing object.

PI_QUARTER is shared by all objects as it is a module variable (per convention used as constant).

For simple property initialization I recommend:

  1. set up the properties in the GUI for individual values
  2. set up default values if the properties are not configured in the GUI
    Reason: Never expect a user to change your code!!!

# module constants
PI_QUARTER = math.pi / 4 # set up once never change ~ constant
CONST2 = 1.41 

# property default values
DEFAULT_X = 0.0

def processing(cont):
  obj = cont.owner
  x = obj.get("X", DEFAULT_X)
  x = (x + PI_QUARTER)/CONST2 
  obj["X"] = x

The interfaces for configuration are:

  • Properties (in 90% of cases)
  • Object names (should be used very very careful)
  • Parent relationship (parent and children)
  • Logic brick owner relationship (the owner of a sensor does not need to be the owner of the controller!)
  • Group instance relationship (important but not implemented yet)

Very clean indeed, thank you very much.
What surprises me is that constant definition statements are executed only once (at compilation time, may be?) and not at every call of the module, as I thought!
With such premises, in most applications I won’t even need any dedicated initialization module.

One final point: since it is important for me that modules can be ported (back and forth…) to other python development environments with minimum changes, I have in mind to create a dummy object where to store the variables that are updated at every iteration, rather than in the blender-specific “controller owner”. Does this make sense? Are there any simpler methods?

<b># THE FOLLOWING CODE NOT TESTED YET!!!</b>
class EmptyClass:  <i># create dummy class</i>
       pass
PI_QUARTER = math.pi / 4 <i># set up once</i>
CONST2 = 1.41
VariableCarrierObject = EmptyClass()
VariableCarrierObject.X = 0.0  <i># create and initialize X-property</i>

def processing():
       <i># Retrieve variables from previous call:</i>
       x = VariableCarrierObject.X  <i># replaces x = cont.owner["X"] for portability</i>
      <i># Update variables:</i>
       x = (x + PI_QUARTER)/CONST2
      <i># Store variables for next iteration</i>:
       VariableCarrierObject.X = x <i># replaces cont.owner["X"] = x</i>

The module initialization is executed with the first access to the module:
via import statement
or
via calling a function

There is already an object that exist ones only (singleton). It is the module itself.
Sure you can add any data you like. It will be accessible from each active scene. It even survives a scene switch.

If you prefer the class style syntax, you can do what you wrote in your example.
The instance of your class is a module variable (belongs to the module, not to a function). It will survive as long as the module lives (end of the game).
It is recommended to name variables and functions with a beginning lowercase letter and class names with a beginning Uppercase letter. This makes it easier to differentiate between them.
E.g.


myClassInstance = MyClass()
myClassInstance.myMemberFunction()
myClassInstance.myMemberVariable
myFunction() 
myVariable

You can read about this in PEP#008 which contains a lot of tricks and hints when using Python too.

Thank you: I will make some experiments…

collage classes and text books have told me that “main” is a professional programming standard. I imagine “processing” is a personal preference?

1 Like

It depends. In this case, when writing Python controller modules, you effectively program a Python controller to do something. Chances are, you will use a lot of these programmed controllers.

The word “main” expresses the idea of a unique entry point for some program. So you can name your Python controller functions “main” if you organize your code in such a way that 1 Python file = code for 1 Python controller.

If you happen to write multiple functions for multiple controllers into one single file, then surely you’ll need to find better names than “main1”, “main2”, “main3”, etc.

TL;DR: Python modules allow you to program a Python controller, so “main” seems too broad.

1 Like

It can be a standard, but what does it do?

‘main’ does not say anything at all beside that is is an main function for something. If it for example get the distance and returns that, then i would call the function ‘get_distance’ or something like that.