Merge branch 'blender-v4.3-release'
This commit is contained in:
@@ -1,61 +1,63 @@
|
||||
"""
|
||||
Invoke Function
|
||||
+++++++++++++++
|
||||
.. _operator_modifying_blender_data_undo:
|
||||
|
||||
:class:`Operator.invoke` is used to initialize the operator from the context
|
||||
at the moment the operator is called.
|
||||
invoke() is typically used to assign properties which are then used by
|
||||
execute().
|
||||
Some operators don't have an execute() function, removing the ability to be
|
||||
repeated from a script or macro.
|
||||
Modifying Blender Data & Undo
|
||||
+++++++++++++++++++++++++++++
|
||||
|
||||
This example shows how to define an operator which gets mouse input to
|
||||
execute a function and that this operator can be invoked or executed from
|
||||
the python api.
|
||||
Any operator modifying Blender data should enable the ``'UNDO'`` option.
|
||||
This will make Blender automatically create an undo step when the operator
|
||||
finishes its ``execute`` (or ``invoke``, see below) functions, and returns
|
||||
``{'FINISHED'}``.
|
||||
|
||||
Otherwise, no undo step will be created, which will at best corrupt the
|
||||
undo stack and confuse the user (since modifications done by the operator
|
||||
may either not be undoable, or be undone together with other edits done
|
||||
before). In many cases, this can even lead to data corruption and crashes.
|
||||
|
||||
Note that when an operator returns ``{'CANCELLED'}``, no undo step will be
|
||||
created. This means that if an error occurs *after* modifying some data
|
||||
already, it is better to return ``{'FINISHED'}``, unless it is possible to
|
||||
fully undo the changes before returning.
|
||||
|
||||
.. note::
|
||||
|
||||
Most examples in this page do not do any edit to Blender data, which is
|
||||
why it is safe to keep the default ``bl_options`` value for these operators.
|
||||
|
||||
.. note::
|
||||
|
||||
In some complex cases, the automatic undo step created on operator exit may
|
||||
not be enough. For example, if the operator does mode switching, or calls
|
||||
other operators that should create an extra undo step, etc.
|
||||
|
||||
Such manual undo push is possible using the :class:`bpy.ops.ed.undo_push`
|
||||
function. Be careful though, this is considered an advanced feature and
|
||||
requires some understanding of the actual undo system in Blender code.
|
||||
|
||||
Also notice this operator defines its own properties, these are different
|
||||
to typical class properties because blender registers them with the
|
||||
operator, to use as arguments when called, saved for operator undo/redo and
|
||||
automatically added into the user interface.
|
||||
"""
|
||||
import bpy
|
||||
|
||||
|
||||
class SimpleMouseOperator(bpy.types.Operator):
|
||||
""" This operator shows the mouse location,
|
||||
this string is used for the tooltip and API docs
|
||||
"""
|
||||
bl_idname = "wm.mouse_position"
|
||||
bl_label = "Invoke Mouse Operator"
|
||||
|
||||
x: bpy.props.IntProperty()
|
||||
y: bpy.props.IntProperty()
|
||||
class DataEditOperator(bpy.types.Operator):
|
||||
bl_idname = "object.data_edit"
|
||||
bl_label = "Data Editing Operator"
|
||||
# The default value is only 'REGISTER', 'UNDO' is mandatory when Blender data is modified
|
||||
# (and does require 'REGISTER' as well).
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
# rather than printing, use the report function,
|
||||
# this way the message appears in the header,
|
||||
self.report({'INFO'}, "Mouse coords are {:d} {:d}".format(self.x, self.y))
|
||||
context.object.location.x += 1.0
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.x = event.mouse_x
|
||||
self.y = event.mouse_y
|
||||
return self.execute(context)
|
||||
|
||||
|
||||
# Only needed if you want to add into a dynamic menu.
|
||||
def menu_func(self, context):
|
||||
self.layout.operator(SimpleMouseOperator.bl_idname, text="Simple Mouse Operator")
|
||||
self.layout.operator(DataEditOperator.bl_idname, text="Blender Data Editing Operator")
|
||||
|
||||
|
||||
# Register and add to the view menu (required to also use F3 search "Simple Mouse Operator" for quick access)
|
||||
bpy.utils.register_class(SimpleMouseOperator)
|
||||
# Register.
|
||||
bpy.utils.register_class(DataEditOperator)
|
||||
bpy.types.VIEW3D_MT_view.append(menu_func)
|
||||
|
||||
# Test call to the newly defined operator.
|
||||
# Here we call the operator and invoke it, meaning that the settings are taken
|
||||
# from the mouse.
|
||||
bpy.ops.wm.mouse_position('INVOKE_DEFAULT')
|
||||
|
||||
# Another test call, this time call execute() directly with pre-defined settings.
|
||||
bpy.ops.wm.mouse_position('EXEC_DEFAULT', x=20, y=66)
|
||||
bpy.ops.object.data_edit()
|
||||
|
@@ -1,52 +1,61 @@
|
||||
"""
|
||||
Calling a File Selector
|
||||
+++++++++++++++++++++++
|
||||
This example shows how an operator can use the file selector.
|
||||
Invoke Function
|
||||
+++++++++++++++
|
||||
|
||||
Notice the invoke function calls a window manager method and returns
|
||||
``{'RUNNING_MODAL'}``, this means the file selector stays open and the operator does not
|
||||
exit immediately after invoke finishes.
|
||||
:class:`Operator.invoke` is used to initialize the operator from the context
|
||||
at the moment the operator is called.
|
||||
invoke() is typically used to assign properties which are then used by
|
||||
execute().
|
||||
Some operators don't have an execute() function, removing the ability to be
|
||||
repeated from a script or macro.
|
||||
|
||||
The file selector runs the operator, calling :class:`Operator.execute` when the
|
||||
user confirms.
|
||||
This example shows how to define an operator which gets mouse input to
|
||||
execute a function and that this operator can be invoked or executed from
|
||||
the python api.
|
||||
|
||||
The :class:`Operator.poll` function is optional, used to check if the operator
|
||||
can run.
|
||||
Also notice this operator defines its own properties, these are different
|
||||
to typical class properties because blender registers them with the
|
||||
operator, to use as arguments when called, saved for operator undo/redo and
|
||||
automatically added into the user interface.
|
||||
"""
|
||||
import bpy
|
||||
|
||||
|
||||
class ExportSomeData(bpy.types.Operator):
|
||||
"""Test exporter which just writes hello world"""
|
||||
bl_idname = "export.some_data"
|
||||
bl_label = "Export Some Data"
|
||||
class SimpleMouseOperator(bpy.types.Operator):
|
||||
""" This operator shows the mouse location,
|
||||
this string is used for the tooltip and API docs
|
||||
"""
|
||||
bl_idname = "wm.mouse_position"
|
||||
bl_label = "Invoke Mouse Operator"
|
||||
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object is not None
|
||||
x: bpy.props.IntProperty()
|
||||
y: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
file = open(self.filepath, 'w')
|
||||
file.write("Hello World " + context.object.name)
|
||||
# rather than printing, use the report function,
|
||||
# this way the message appears in the header,
|
||||
self.report({'INFO'}, "Mouse coords are {:d} {:d}".format(self.x, self.y))
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
self.x = event.mouse_x
|
||||
self.y = event.mouse_y
|
||||
return self.execute(context)
|
||||
|
||||
|
||||
# Only needed if you want to add into a dynamic menu.
|
||||
def menu_func(self, context):
|
||||
self.layout.operator_context = 'INVOKE_DEFAULT'
|
||||
self.layout.operator(ExportSomeData.bl_idname, text="Text Export Operator")
|
||||
self.layout.operator(SimpleMouseOperator.bl_idname, text="Simple Mouse Operator")
|
||||
|
||||
|
||||
# Register and add to the file selector (required to also use F3 search "Text Export Operator" for quick access)
|
||||
bpy.utils.register_class(ExportSomeData)
|
||||
bpy.types.TOPBAR_MT_file_export.append(menu_func)
|
||||
# Register and add to the view menu (required to also use F3 search "Simple Mouse Operator" for quick access)
|
||||
bpy.utils.register_class(SimpleMouseOperator)
|
||||
bpy.types.VIEW3D_MT_view.append(menu_func)
|
||||
|
||||
# Test call to the newly defined operator.
|
||||
# Here we call the operator and invoke it, meaning that the settings are taken
|
||||
# from the mouse.
|
||||
bpy.ops.wm.mouse_position('INVOKE_DEFAULT')
|
||||
|
||||
# test call
|
||||
bpy.ops.export.some_data('INVOKE_DEFAULT')
|
||||
# Another test call, this time call execute() directly with pre-defined settings.
|
||||
bpy.ops.wm.mouse_position('EXEC_DEFAULT', x=20, y=66)
|
||||
|
@@ -1,40 +1,52 @@
|
||||
"""
|
||||
Dialog Box
|
||||
++++++++++
|
||||
Calling a File Selector
|
||||
+++++++++++++++++++++++
|
||||
This example shows how an operator can use the file selector.
|
||||
|
||||
This operator uses its :class:`Operator.invoke` function to call a popup.
|
||||
Notice the invoke function calls a window manager method and returns
|
||||
``{'RUNNING_MODAL'}``, this means the file selector stays open and the operator does not
|
||||
exit immediately after invoke finishes.
|
||||
|
||||
The file selector runs the operator, calling :class:`Operator.execute` when the
|
||||
user confirms.
|
||||
|
||||
The :class:`Operator.poll` function is optional, used to check if the operator
|
||||
can run.
|
||||
"""
|
||||
import bpy
|
||||
|
||||
|
||||
class DialogOperator(bpy.types.Operator):
|
||||
bl_idname = "object.dialog_operator"
|
||||
bl_label = "Simple Dialog Operator"
|
||||
class ExportSomeData(bpy.types.Operator):
|
||||
"""Test exporter which just writes hello world"""
|
||||
bl_idname = "export.some_data"
|
||||
bl_label = "Export Some Data"
|
||||
|
||||
my_float: bpy.props.FloatProperty(name="Some Floating Point")
|
||||
my_bool: bpy.props.BoolProperty(name="Toggle Option")
|
||||
my_string: bpy.props.StringProperty(name="String Value")
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object is not None
|
||||
|
||||
def execute(self, context):
|
||||
message = "Popup Values: {:f}, {:d}, '{:s}'".format(
|
||||
self.my_float, self.my_bool, self.my_string,
|
||||
)
|
||||
self.report({'INFO'}, message)
|
||||
file = open(self.filepath, 'w')
|
||||
file.write("Hello World " + context.object.name)
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
|
||||
# Only needed if you want to add into a dynamic menu.
|
||||
def menu_func(self, context):
|
||||
self.layout.operator(DialogOperator.bl_idname, text="Dialog Operator")
|
||||
self.layout.operator_context = 'INVOKE_DEFAULT'
|
||||
self.layout.operator(ExportSomeData.bl_idname, text="Text Export Operator")
|
||||
|
||||
|
||||
# Register and add to the object menu (required to also use F3 search "Dialog Operator" for quick access)
|
||||
bpy.utils.register_class(DialogOperator)
|
||||
bpy.types.VIEW3D_MT_object.append(menu_func)
|
||||
# Register and add to the file selector (required to also use F3 search "Text Export Operator" for quick access)
|
||||
bpy.utils.register_class(ExportSomeData)
|
||||
bpy.types.TOPBAR_MT_file_export.append(menu_func)
|
||||
|
||||
# Test call.
|
||||
bpy.ops.object.dialog_operator('INVOKE_DEFAULT')
|
||||
|
||||
# test call
|
||||
bpy.ops.export.some_data('INVOKE_DEFAULT')
|
||||
|
@@ -1,55 +1,40 @@
|
||||
"""
|
||||
Custom Drawing
|
||||
++++++++++++++
|
||||
Dialog Box
|
||||
++++++++++
|
||||
|
||||
By default operator properties use an automatic user interface layout.
|
||||
If you need more control you can create your own layout with a
|
||||
:class:`Operator.draw` function.
|
||||
|
||||
This works like the :class:`Panel` and :class:`Menu` draw functions, its used
|
||||
for dialogs and file selectors.
|
||||
This operator uses its :class:`Operator.invoke` function to call a popup.
|
||||
"""
|
||||
import bpy
|
||||
|
||||
|
||||
class CustomDrawOperator(bpy.types.Operator):
|
||||
bl_idname = "object.custom_draw"
|
||||
bl_label = "Simple Modal Operator"
|
||||
class DialogOperator(bpy.types.Operator):
|
||||
bl_idname = "object.dialog_operator"
|
||||
bl_label = "Simple Dialog Operator"
|
||||
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
my_float: bpy.props.FloatProperty(name="Float")
|
||||
my_float: bpy.props.FloatProperty(name="Some Floating Point")
|
||||
my_bool: bpy.props.BoolProperty(name="Toggle Option")
|
||||
my_string: bpy.props.StringProperty(name="String Value")
|
||||
|
||||
def execute(self, context):
|
||||
print("Test", self)
|
||||
message = "Popup Values: {:f}, {:d}, '{:s}'".format(
|
||||
self.my_float, self.my_bool, self.my_string,
|
||||
)
|
||||
self.report({'INFO'}, message)
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
col.label(text="Custom Interface!")
|
||||
|
||||
row = col.row()
|
||||
row.prop(self, "my_float")
|
||||
row.prop(self, "my_bool")
|
||||
|
||||
col.prop(self, "my_string")
|
||||
|
||||
|
||||
# Only needed if you want to add into a dynamic menu.
|
||||
def menu_func(self, context):
|
||||
self.layout.operator(CustomDrawOperator.bl_idname, text="Custom Draw Operator")
|
||||
self.layout.operator(DialogOperator.bl_idname, text="Dialog Operator")
|
||||
|
||||
|
||||
# Register and add to the object menu (required to also use F3 search "Custom Draw Operator" for quick access).
|
||||
bpy.utils.register_class(CustomDrawOperator)
|
||||
# Register and add to the object menu (required to also use F3 search "Dialog Operator" for quick access)
|
||||
bpy.utils.register_class(DialogOperator)
|
||||
bpy.types.VIEW3D_MT_object.append(menu_func)
|
||||
|
||||
# test call
|
||||
bpy.ops.object.custom_draw('INVOKE_DEFAULT')
|
||||
# Test call.
|
||||
bpy.ops.object.dialog_operator('INVOKE_DEFAULT')
|
||||
|
@@ -1,72 +1,55 @@
|
||||
"""
|
||||
.. _modal_operator:
|
||||
Custom Drawing
|
||||
++++++++++++++
|
||||
|
||||
Modal Execution
|
||||
+++++++++++++++
|
||||
By default operator properties use an automatic user interface layout.
|
||||
If you need more control you can create your own layout with a
|
||||
:class:`Operator.draw` function.
|
||||
|
||||
This operator defines a :class:`Operator.modal` function that will keep being
|
||||
run to handle events until it returns ``{'FINISHED'}`` or ``{'CANCELLED'}``.
|
||||
|
||||
Modal operators run every time a new event is detected, such as a mouse click
|
||||
or key press. Conversely, when no new events are detected, the modal operator
|
||||
will not run. Modal operators are especially useful for interactive tools, an
|
||||
operator can have its own state where keys toggle options as the operator runs.
|
||||
Grab, Rotate, Scale, and Fly-Mode are examples of modal operators.
|
||||
|
||||
:class:`Operator.invoke` is used to initialize the operator as being active
|
||||
by returning ``{'RUNNING_MODAL'}``, initializing the modal loop.
|
||||
|
||||
Notice ``__init__()`` and ``__del__()`` are declared.
|
||||
For other operator types they are not useful but for modal operators they will
|
||||
be called before the :class:`Operator.invoke` and after the operator finishes.
|
||||
This works like the :class:`Panel` and :class:`Menu` draw functions, its used
|
||||
for dialogs and file selectors.
|
||||
"""
|
||||
import bpy
|
||||
|
||||
|
||||
class ModalOperator(bpy.types.Operator):
|
||||
bl_idname = "object.modal_operator"
|
||||
class CustomDrawOperator(bpy.types.Operator):
|
||||
bl_idname = "object.custom_draw"
|
||||
bl_label = "Simple Modal Operator"
|
||||
|
||||
def __init__(self):
|
||||
print("Start")
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
|
||||
def __del__(self):
|
||||
print("End")
|
||||
my_float: bpy.props.FloatProperty(name="Float")
|
||||
my_bool: bpy.props.BoolProperty(name="Toggle Option")
|
||||
my_string: bpy.props.StringProperty(name="String Value")
|
||||
|
||||
def execute(self, context):
|
||||
context.object.location.x = self.value / 100.0
|
||||
print("Test", self)
|
||||
return {'FINISHED'}
|
||||
|
||||
def modal(self, context, event):
|
||||
if event.type == 'MOUSEMOVE': # Apply
|
||||
self.value = event.mouse_x
|
||||
self.execute(context)
|
||||
elif event.type == 'LEFTMOUSE': # Confirm
|
||||
return {'FINISHED'}
|
||||
elif event.type in {'RIGHTMOUSE', 'ESC'}: # Cancel
|
||||
# Revert all changes that have been made
|
||||
context.object.location.x = self.init_loc_x
|
||||
return {'CANCELLED'}
|
||||
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.init_loc_x = context.object.location.x
|
||||
self.value = event.mouse_x
|
||||
self.execute(context)
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
context.window_manager.modal_handler_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
col.label(text="Custom Interface!")
|
||||
|
||||
row = col.row()
|
||||
row.prop(self, "my_float")
|
||||
row.prop(self, "my_bool")
|
||||
|
||||
col.prop(self, "my_string")
|
||||
|
||||
|
||||
# Only needed if you want to add into a dynamic menu.
|
||||
def menu_func(self, context):
|
||||
self.layout.operator(ModalOperator.bl_idname, text="Modal Operator")
|
||||
self.layout.operator(CustomDrawOperator.bl_idname, text="Custom Draw Operator")
|
||||
|
||||
|
||||
# Register and add to the object menu (required to also use F3 search "Modal Operator" for quick access).
|
||||
bpy.utils.register_class(ModalOperator)
|
||||
# Register and add to the object menu (required to also use F3 search "Custom Draw Operator" for quick access).
|
||||
bpy.utils.register_class(CustomDrawOperator)
|
||||
bpy.types.VIEW3D_MT_object.append(menu_func)
|
||||
|
||||
# test call
|
||||
bpy.ops.object.modal_operator('INVOKE_DEFAULT')
|
||||
bpy.ops.object.custom_draw('INVOKE_DEFAULT')
|
||||
|
@@ -1,45 +1,75 @@
|
||||
"""
|
||||
Enum Search Popup
|
||||
+++++++++++++++++
|
||||
.. _modal_operator:
|
||||
|
||||
You may want to have an operator prompt the user to select an item
|
||||
from a search field, this can be done using :class:`bpy.types.Operator.invoke_search_popup`.
|
||||
Modal Execution
|
||||
+++++++++++++++
|
||||
|
||||
This operator defines a :class:`Operator.modal` function that will keep being
|
||||
run to handle events until it returns ``{'FINISHED'}`` or ``{'CANCELLED'}``.
|
||||
|
||||
Modal operators run every time a new event is detected, such as a mouse click
|
||||
or key press. Conversely, when no new events are detected, the modal operator
|
||||
will not run. Modal operators are especially useful for interactive tools, an
|
||||
operator can have its own state where keys toggle options as the operator runs.
|
||||
Grab, Rotate, Scale, and Fly-Mode are examples of modal operators.
|
||||
|
||||
:class:`Operator.invoke` is used to initialize the operator as being active
|
||||
by returning ``{'RUNNING_MODAL'}``, initializing the modal loop.
|
||||
|
||||
Notice ``__init__()`` and ``__del__()`` are declared.
|
||||
For other operator types they are not useful but for modal operators they will
|
||||
be called before the :class:`Operator.invoke` and after the operator finishes.
|
||||
"""
|
||||
import bpy
|
||||
from bpy.props import EnumProperty
|
||||
|
||||
|
||||
class SearchEnumOperator(bpy.types.Operator):
|
||||
bl_idname = "object.search_enum_operator"
|
||||
bl_label = "Search Enum Operator"
|
||||
bl_property = "my_search"
|
||||
class ModalOperator(bpy.types.Operator):
|
||||
bl_idname = "object.modal_operator"
|
||||
bl_label = "Simple Modal Operator"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
my_search: EnumProperty(
|
||||
name="My Search",
|
||||
items=(
|
||||
('FOO', "Foo", ""),
|
||||
('BAR', "Bar", ""),
|
||||
('BAZ', "Baz", ""),
|
||||
),
|
||||
)
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
print("Start")
|
||||
|
||||
def __del__(self):
|
||||
super().__del__()
|
||||
print("End")
|
||||
|
||||
def execute(self, context):
|
||||
self.report({'INFO'}, "Selected:" + self.my_search)
|
||||
context.object.location.x = self.value / 100.0
|
||||
return {'FINISHED'}
|
||||
|
||||
def modal(self, context, event):
|
||||
if event.type == 'MOUSEMOVE': # Apply
|
||||
self.value = event.mouse_x
|
||||
self.execute(context)
|
||||
elif event.type == 'LEFTMOUSE': # Confirm
|
||||
return {'FINISHED'}
|
||||
elif event.type in {'RIGHTMOUSE', 'ESC'}: # Cancel
|
||||
# Revert all changes that have been made
|
||||
context.object.location.x = self.init_loc_x
|
||||
return {'CANCELLED'}
|
||||
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.invoke_search_popup(self)
|
||||
self.init_loc_x = context.object.location.x
|
||||
self.value = event.mouse_x
|
||||
self.execute(context)
|
||||
|
||||
context.window_manager.modal_handler_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
|
||||
# Only needed if you want to add into a dynamic menu.
|
||||
def menu_func(self, context):
|
||||
self.layout.operator(SearchEnumOperator.bl_idname, text="Search Enum Operator")
|
||||
self.layout.operator(ModalOperator.bl_idname, text="Modal Operator")
|
||||
|
||||
|
||||
# Register and add to the object menu (required to also use F3 search "Search Enum Operator" for quick access)
|
||||
bpy.utils.register_class(SearchEnumOperator)
|
||||
# Register and add to the object menu (required to also use F3 search "Modal Operator" for quick access).
|
||||
bpy.utils.register_class(ModalOperator)
|
||||
bpy.types.VIEW3D_MT_object.append(menu_func)
|
||||
|
||||
# test call
|
||||
bpy.ops.object.search_enum_operator('INVOKE_DEFAULT')
|
||||
bpy.ops.object.modal_operator('INVOKE_DEFAULT')
|
||||
|
45
doc/python_api/examples/bpy.types.Operator.7.py
Normal file
45
doc/python_api/examples/bpy.types.Operator.7.py
Normal file
@@ -0,0 +1,45 @@
|
||||
"""
|
||||
Enum Search Popup
|
||||
+++++++++++++++++
|
||||
|
||||
You may want to have an operator prompt the user to select an item
|
||||
from a search field, this can be done using :class:`bpy.types.Operator.invoke_search_popup`.
|
||||
"""
|
||||
import bpy
|
||||
from bpy.props import EnumProperty
|
||||
|
||||
|
||||
class SearchEnumOperator(bpy.types.Operator):
|
||||
bl_idname = "object.search_enum_operator"
|
||||
bl_label = "Search Enum Operator"
|
||||
bl_property = "my_search"
|
||||
|
||||
my_search: EnumProperty(
|
||||
name="My Search",
|
||||
items=(
|
||||
('FOO', "Foo", ""),
|
||||
('BAR', "Bar", ""),
|
||||
('BAZ', "Baz", ""),
|
||||
),
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
self.report({'INFO'}, "Selected:" + self.my_search)
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.invoke_search_popup(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
|
||||
# Only needed if you want to add into a dynamic menu.
|
||||
def menu_func(self, context):
|
||||
self.layout.operator(SearchEnumOperator.bl_idname, text="Search Enum Operator")
|
||||
|
||||
|
||||
# Register and add to the object menu (required to also use F3 search "Search Enum Operator" for quick access)
|
||||
bpy.utils.register_class(SearchEnumOperator)
|
||||
bpy.types.VIEW3D_MT_object.append(menu_func)
|
||||
|
||||
# test call
|
||||
bpy.ops.object.search_enum_operator('INVOKE_DEFAULT')
|
@@ -9,9 +9,7 @@ user input.
|
||||
|
||||
The function should return ``{'FINISHED'}`` or ``{'CANCELLED'}``, the latter
|
||||
meaning that operator execution was aborted without making any changes, and
|
||||
saving an undo entry isn't neccesary. If an error is detected after some changes
|
||||
have already been made, use the ``{'FINISHED'}`` return code, or the behavior
|
||||
of undo will be confusing for the user.
|
||||
that no undo step will created (see next example for more info about undo).
|
||||
|
||||
.. note::
|
||||
|
||||
|
@@ -16,4 +16,3 @@ that can be troublesome and avoid practices that are known to cause instability.
|
||||
info_gotchas_meshes.rst
|
||||
info_gotchas_armatures_and_bones.rst
|
||||
info_gotchas_file_paths_and_encoding.rst
|
||||
|
||||
|
@@ -202,6 +202,15 @@ interactively by the user is the only way to make sure that the script doesn't b
|
||||
guarantee of any kind that it will be safe and consistent. Use it at your own risk.
|
||||
|
||||
|
||||
Modifying Blender Data & Undo
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In general, when Blender data is modified, there should always be an undo step created for it.
|
||||
Otherwise, there will be issues, ranging from invalid/broken undo stack, to crashes on undo/redo.
|
||||
|
||||
This is especially true when modifying Blender data :ref:`in operators <operator_modifying_blender_data_undo>`.
|
||||
|
||||
|
||||
Undo & Library Data
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@@ -262,9 +262,7 @@ static void object_copy_data(Main *bmain,
|
||||
ob_dst->avs = ob_src->avs;
|
||||
ob_dst->mpath = animviz_copy_motionpath(ob_src->mpath);
|
||||
|
||||
/* Do not copy object's preview
|
||||
* (mostly due to the fact renderers create temp copy of objects). */
|
||||
if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0 && false) { /* XXX TODO: temp hack. */
|
||||
if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) {
|
||||
BKE_previewimg_id_copy(&ob_dst->id, &ob_src->id);
|
||||
}
|
||||
else {
|
||||
|
@@ -520,7 +520,12 @@ const EnumPropertyItem rna_enum_operator_type_flag_items[] = {
|
||||
0,
|
||||
"Register",
|
||||
"Display in the info window and support the redo toolbar panel"},
|
||||
{OPTYPE_UNDO, "UNDO", 0, "Undo", "Push an undo event (needed for operator redo)"},
|
||||
{OPTYPE_UNDO,
|
||||
"UNDO",
|
||||
0,
|
||||
"Undo",
|
||||
"Push an undo event when the operator returns `FINISHED` (needed for operator redo, "
|
||||
"mandatory if the operator modifies Blender data)"},
|
||||
{OPTYPE_UNDO_GROUPED,
|
||||
"UNDO_GROUPED",
|
||||
0,
|
||||
|
Reference in New Issue
Block a user