What instantiates classes in Blender?

Normally in pure Python I instantiate a class like this:

class animal():
    saying = "something"
     # blah blah, etc. 

# going to instantiate:
bird = animal() #instance

But when I make a panel class, we don’t say like:
my_first_panel = panel_template()

Instead, the panel class we create in Blender, got instantiated automatically after running the script; the class has a self, and the panel that appears in our toolpanel is an instance, right? Is the instantiation caused by register(), or by Blender’s build in Panel class?
( I bump upon this because I look for a precise definition of cls, self, context and @classmethod )

I think I found the answer on: https://docs.blender.org/api/current/info_overview.html?highlight=registration
There is says:
Notice these classes don’t define an __init__(self) function …
… So once the class is registered with Blender, instancing the class and calling the functions is left up to Blender.

So I think it goes via the register() function.

class Point():
  def __init__(self):
      self.x = 0
      self.y = 0
      self.z = 0

point = Point()   <--- Actual initialization happens here

This works if you do not need to pass parameters in.

class Point:
  def __init__(self):
      self.x = 0
      self.y = 0
      self.z = 0

point = Point()   <--- Actual initialization happens here

You could get away with this too, but I am not sure just what the effect might be in plugins, ect.

class Point:
      x = 0
      y = 0
      z = 0

point = Point()

That is how we do it in pure python, but in Blender it seems different:

  • In Blender, we don’t see __init__(self) in classes (like property-, panel-classes, etc).
  • In scripts in Blender, for example to instantiate a Panel Class, we don’t use: a = my_panel_class() See below. It looks like the classes we make in Blender will be instantiated automatically. And I think its via the register() function.

For example, here is the hello_world_panel template:

import bpy


class HelloWorldPanel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Hello World Panel"
    bl_idname = "OBJECT_PT_hello"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"

    def draw(self, context):
        layout = self.layout

        obj = context.object

        row = layout.row()
        row.label(text="Hello world!", icon='WORLD_DATA')

        row = layout.row()
        row.label(text="Active object is: " + obj.name)
        row = layout.row()
        row.prop(obj, "name")

        row = layout.row()
        row.operator("mesh.primitive_cube_add")


def register():
    bpy.utils.register_class(HelloWorldPanel)


def unregister():
    bpy.utils.unregister_class(HelloWorldPanel)


if __name__ == "__main__":
    register()

The panel that will appear in the tool-bar is an instance, but we didn’t make an instance with:
my_second_or_third_Panel = HelloWorldPanel(). And that the panel we see in the 3D view’s toolbar is an instance, we know that - because in the Panel class here above, we use self in def draw(self, context): That self, refers to the instance of a class, and a @classmethod (if we use that) refers to the class before it’s instantiated. Is it not?

You do not instantiate a class in Blender for such purposes, because things like UI toolkits are part of an API and what you do actually is called subclassing (inheritance). Basically you first inherit the UI class then overload the original methods/functions. This is a very clean way of working on tools and it gives Blender control over such utility tools.

1 Like

I see, ok. It’s that I try to understand the underlying process (rather than experimenting until it works).
So self is not refering to an instance of a class, as I often read in articles online. Here, in a Panel Class for example, self refers to the subclass? ( so we created a subclass with class HelloWorldPanel(bpy.types.Panel):) , is it like that?

Well, it’s getting a bit though now. I wanted to figure out what self and context exactly means.
I found some explanation but still a bit vague to me so far:

Self is a reference to the actual object the method is running. Represents the instance of a class. With self, you can also access the attributes with the given arguments.
Context is a reference to the program context.

Self refers to instance of class. You understand it right. In whichever class self is used, it refers to that instance of class.
The panel you defined is class itself but is also child of (subclass) of bpy.types.panel. Blender defines init and other functions which needs for blender to work properly. By inheriting you also get all that methods/functions for your panel class.
Note that one class can have more than one instances.
Class variables and methods are same for every instance of class. But variables defined using self only refers to the that instance of class. So any change in variable defined using self does not affect the other instances of that class.

Sorry for the wall of text. Currently I’m away from computer so can’t give any code examples. And if you don’t understand after research I suggest you to ask this question in devtalk.blender.org Blender dev are quite active there and you can get answer from Very knowledgeable people.

Footnote : This parent child relationship is called inheritance.( This is fundamental in oop- object oriented programming. If you don’t know about this you should look into that.)

1 Like

Blender instanciate Panels at every redraw ! you may check this by print(id(self)) in the draw function body.
So you can’t store any value into a panel instance variable, but a class variable may do the trick.

2 Likes

@pvn31
Thanks, I come a bit closer with that. Yes, I got inheritance concept of which I had a question earlier. So by inheriting , I got a lot of presents, like an init, functions, etc. Got that.

[quote=“pvn31, post:7, topic:1277235”]
self does not affect the other instances of that class
[/quote] ah, that makes sense (allthough, I think we usually have only one instance of a Panel or Operator class etc.

@stephen_leger Yes, I’ve read that before: at every redraw. What is redraw exactly? Let’s say I add a poll function that a menu-item only appear when there is an object selected. Is that a redraw of the Panel? Or is a redraw when I install the addon / run a script?

Redraw occurs when panel is visible and the ui redraw.
Poll is a class method and does not require any instance.

1 Like

I press N to close the N-panel, and press N to open the N-Panel again to see the addon.
We call that redrawing as well?

You must try to get the class id in the draw body, but i guess yes, even value change does trigger a redraw.

1 Like

The way I look at it, and correct me if I am wrong, but how you create classes can differ. If you are creating a panel or something like:

  • HT – Header
  • MT – Menu
  • OT – Operator
  • PT – Panel
  • UL – UI list

you use one way to create the class, but with the class like:

class Animal():

it is pretty much standard Python. Because of the crossover between normal Python and Blender Python, if it works in Blender, I go with it, because generally I have no reasons to believe it will fail later.

Yes, I remember those are the new names convention, but the main difference in regular python classes and classes in Blender like Panels and Operators, is as far as I see:

Blender:  class MESH_OT_monkey_grid(bpy.types.Operator):
Regular:  class monkey_grid():  # or class monkey_grid(grid_baseclass):
-------
Blender: don't use def __init__ 
Regular: often use def __init__
-------
Blender: panel, operator classes, etc got instanciated by every redraw
Regular:  you instantiate it with: small_monkey = class_monkey_grid()

I noticed most regular pyton stuff works; we can also make classes with instantiation and inheritance like we normally do in python.

2 Likes

Inheritance.
I checked this to see what would happen without the init, and it was not pretty, ha ha.
Note that this is NOT the way you would write Header, Menu, Operator, Panel or UI list classes!

import bpy

class Character():
    def __init__(self):
        self.name = 'unknown'
        self.location = (0,0,0)
        # 
    def get(self): 
        print(self.name) 
        print(self.location) 
  

# child class 
class Magician(Character ):
    def __init__(self):
        #Character.__init__(self)  # < ------ Uncomment for test
        self.hitpoints = 959 
        self.damage = 88 
    # 
    def display(self):
        print(self.hitpoints) 
        print(self.damage) 
            
  
m = Magician()
m.
# Now type in "m." as above and hit TAB, and you will see:
      damage
      display(
      get(
      hitpoints

# Now uncomment line 16, Character.__init__(self)  # < ------ Uncomment for test
# type in "m." and hit TAB, and you will see:

      damage
      display(
      get(
      hitpoints
      location
      name

Also, these types of classes do not need to be registered.
Python supports multiple inheritance too.

That’s interesting. I still have to experiment with the scopes and attributes of inherited classes. Now you say so, I think at the moment you register a class, blender will instance those.

Qua use of standard python classes: I checked quite some addons to see it’s code and noticed they don’t use much standard python classes (that use inheritance). I’ve seen one example in archipack, in the archipack_abstraction.py.
And I found another example standard python classes with inheritance, and that was more to reduce repetitive coding.

Experimenting a bit more, I noticed that functions have access to attributes of classes. Whereas when a function wants to access a standalone variable, it has to be declared global (of which use is not encouraged I found out). Here an example access to variable vs attribute of a class by a function:

# Checking standalone var in script:  that didn't work unless we declare the variable global inside the function
# When we remove global, the function has no access to the variable: standalone_var
print()
print("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ")
print("----- A Function will change the value of a standalone variable:---------") 

standalone_var = "I am a var, alone"

def change_my_value():
    global standalone_var
    standalone_var = "A function said I am Global, and then changed me"

print(standalone_var)
change_my_value()
print(standalone_var)


## whereas if we use attributes of a class, a function has access to it: 
print()
print("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ")
print("----- A Function will change the value of an class it's attribute:----------") 

class storage:
    standalone_var = "I am an attribute in a class"
    
def change_attribute():
    storage.standalone_var = "Function changed attribute"
    
print(storage.standalone_var)
change_attribute()
print(storage.standalone_var)

So I tried out, as you say functions, scope, and instanced and inherited classes:

## So why not using standard python classes to organise and store your data? 
print()
print("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ")
print("----- Function accessing attributes of classes: inheritance and instances----------") 

class global_condition:
    weather = "sunny"    
    
class local_condition(global_condition):
    landscape = "hill-landscape"
    
print("---- Here a function is going to change the value of an attribute of an inherited and instanced class")

iceland = local_condition()

def change_iceland():
    iceland.weather = "Snow"
    iceland.landscape = "Ice-landscape"
    
print("Original value: " + iceland.weather)
print("Original value: " + iceland.landscape)

change_iceland()

print("New value: " + iceland.weather)
print("New value: " + iceland.landscape)

I’ve seen addons using system variables (instead), but I am a bit hesitant using them.
(Still using the global attributes since I am not using self and init here. Will experiment more, later)