7
$\begingroup$

Example: I want to press a button in the UI and have a label/list change to display below the button. Not on the Operator's F6/Redo panel, but once the Operator has been appended somewhere else in the UI.

So perhaps the question would be if it's possible to use the Operator's draw function somewhere else in the UI (appended or on it's own Panel).

I know it can be done updating a global variable, then using it from the UI code, but I know globals a no-no in general.

Thanks!

$\endgroup$

3 Answers 3

7
$\begingroup$

You can use static members (class variables) to store information in the operator.

Here's an example that adds a panel and an operator. The operator increases a class variable to reflect how often it has been called. This number is drawn in a panel in the Scene tab of the Properties Edtior:

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 = "scene"

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

        layout.operator(SimpleOperator.bl_idname)
        layout.label(str(SimpleOperator.run))


class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"

    run = 0

    def execute(self, context):
        self.__class__.run += 1
        self.report({'INFO'}, str(self.__class__.run))
        return {'FINISHED'}


def register():
    bpy.utils.register_module(__name__)


def unregister():
    bpy.utils.unregister_module(__name__)


if __name__ == "__main__":
    register()
$\endgroup$
2
  • $\begingroup$ Thank you very much CoDEmanX! Had no idea this was possible, this solved it in a super clean way. Cheers! $\endgroup$ Commented Mar 9, 2014 at 0:25
  • $\begingroup$ This can for sure be useful, but in this case SimpleOperator.run is effectively a global, just using a class as storage. If you want to do this it can be ok, but to avoid confusion you could also just define a class and store globals there, rather then mixing it up with panel/operator classes. $\endgroup$
    – ideasman42
    Commented Mar 9, 2014 at 2:24
7
$\begingroup$

The issue here is it sounds like you want to use globals, without using the global keyword.

As far as I'm concerned a global is a global - no matter what you call it.

So don't trick yourself here, rather then identifying globals as bad, just accept there are times when a state is global and try to do it in the least error-prone way.

Of course try avoid these situations - but lets just assume you checked the alternatives and came to the conclusion some global state is needed.

Rather then mixing global state in with existing classes, I prefer to explicitly define a class which stores state, especially if its shared between multiple functions/classes.

# Singleton for storing global state
class _MyState:
    __slots__ = (
        "uploading",
        "token_reload",
        "size_text",
        )

 def __init__(self):
    self.uploading = False
    self.token_reload = True
    self.size_text = ""

# One state to rule them all (otherwise known as a singleton)
my_state = _MyState()
del _MyState

The advantage with using __slots__ is you clearly define whats global, and you can't accidentally assign some invalid var.

eg, .size_tetx = "" - will raise an error, with very large scripts allowing such mistakes to fail silently can loose your time debugging silly typos.


Heres an example of such a class used in the Sketchfab addon

$\endgroup$
7
$\begingroup$

The question seems to assume that globals are needed, however an operator can store Python attributes in its instance, and using draw and check callbacks it can refresh the operator redo panel in a way the script author can control.

Heres an example of how you can store some operator variables used in the UI without having to use globals.

import bpy
from bpy.props import BoolProperty

class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"
    bl_options = {'REGISTER', 'UNDO'}

    toggle: BoolProperty(
        name="MyToggle",
    )
    use_count: BoolProperty(
        name="Use Count",
    )

    def __init__(self):
        # notice this is a typical python attribute
        # which Blender doesn't know about (not from bpy.props)
        # no global's needed here, just an attribute instance.
        self.label_counter = 0

    def draw(self, context):
        layout = self.layout
        layout.label("Count %d" % self.label_counter)
        layout.prop(self, "toggle")
        layout.prop(self, "use_count")

    def check(self, context):
        # should we redraw when a button is pressed?
        if self.use_count:
            return True
        return False

    def execute(self, context):
        if self.use_count:
            self.label_counter += 1
            self.report({'INFO'}, "Count %d" % self.label_counter)
        return {'FINISHED'}


def register():
    bpy.utils.register_module(__name__)


def unregister():
    bpy.utils.unregister_module(__name__)


if __name__ == "__main__":
    register()
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .