0
$\begingroup$

I made a panel with an Export Modified Model STL button. Right now I am using...

class Export_STL(bpy.types.Operator):
    bl_idname = "diamond.export_stl"
    bl_label = "Export Modified Model STL"

    def execute(self, context):
        bpy.ops.export_mesh.stl('INVOKE_DEFAULT')
        self.report({'INFO'}, f"This is {self.bl_idname}")
        return {'FINISHED'}

It works but I need the default settings to have "Batch Mode" to Object" and a check on the "Selection Only". How do I write this in the script?

$\endgroup$
0
3
$\begingroup$

You don't necessarily need to write a custom operator to re-use the existing one. You can call the operator in your layout and change its properties, see How to pass multiple operator properties via UI layout?

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
        op = layout.operator("export_mesh.stl", text="My Custom STL Exporter")
        op.use_selection = True
        op.batch_mode = 'OBJECT'


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


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


if __name__ == "__main__":
    register()

enter image description here

You can find the operator's parameters in your blender install path / 2.** / Scripts / addons / io_mesh_stl / __init__.py under the ExportSTL class. :

use_selection: BoolProperty(
    name="Selection Only",
    description="Export selected objects only",
    default=False,
)
global_scale: FloatProperty(
    name="Scale",
    min=0.01, max=1000.0,
    default=1.0,
)
use_scene_unit: BoolProperty(
    name="Scene Unit",
    description="Apply current scene's unit (as defined by unit scale) to exported data",
    default=False,
)
ascii: BoolProperty(
    name="Ascii",
    description="Save the file in ASCII file format",
    default=False,
)
use_mesh_modifiers: BoolProperty(
    name="Apply Modifiers",
    description="Apply the modifiers before saving",
    default=True,
)
batch_mode: EnumProperty(
    name="Batch Mode",
    items=(
        ('OFF', "Off", "All data in one file"),
        ('OBJECT', "Object", "Each object as a file"),
    ),
)
$\endgroup$
1
  • 1
    $\begingroup$ Thank you for all your information. I see your example is for a properties button. I tried to adapt it to work with my View_3d button but it did not work. Could you help me make it work for this? $\endgroup$ – Laroc Feb 17 at 17:45
2
$\begingroup$

Overwrite the menu function

enter image description here

Firstly, for the simplest approach, assigning default via UI is the goto option IIRC have seen others suggesting similar elsewhere

Here we will use the little used, but very handy Menu._dyn_ui_initialize() method to expose the list where methods are appended / prepended.

Running thru and printing the functions module and name reveals,

bl_ui.space_topbar draw
io_scene_fbx menu_func_export
io_anim_bvh menu_func_export
io_mesh_ply menu_func_export
io_scene_obj menu_func_export
io_mesh_stl menu_export
io_scene_gltf2 menu_func_export
io_scene_x3d menu_func_export

so let's simply find the old one and replace with draw from @Gorgeous' answer.

import bpy
from bpy.types import TOPBAR_MT_file_export
   
def draw(self, context):
    layout = self.layout
    op = layout.operator("export_mesh.stl", text="My Custom STL Exporter")
    op.use_selection = True
    op.batch_mode = 'OBJECT'
    
funcs = TOPBAR_MT_file_export._dyn_ui_initialize()

for i, f in enumerate(funcs):
    if f.__module__ == "io_mesh_stl":
        funcs[i] = draw
        break

Editing or Copying the source

Another option is to edit original addon source, or copy the source to our own addons folder give the addon a new name. Disable the original and enable our edited copy.

IMO editing the original source, (and even making a copy) is a slippery slope and should be avoided as it may need to be re-edited for each blender update.

Stealing annotations

Since the advent of 2.8 and the introduction of annotated properties, these can be easily "pinched" from the defining class.

Can very simply make a propertygroup or an operator or a preset (emulate 'PRESET' in operator options).

Here is the set of definitions illustrated in @Gorgeoous' answer

>>> from io_mesh_stl import ExportSTL
>>> for k, v in ExportSTL.__annotations__.items():
...     k, v
...     
('filter_glob', (<built-in function StringProperty>, {'default': '*.stl', 'options': {'HIDDEN'}, 'attr': 'filter_glob'}))
('use_selection', (<built-in function BoolProperty>, {'name': 'Selection Only', 'description': 'Export selected objects only', 'default': False, 'attr': 'use_selection'}))
('global_scale', (<built-in function FloatProperty>, {'name': 'Scale', 'min': 0.01, 'max': 1000.0, 'default': 1.0, 'attr': 'global_scale'}))
('use_scene_unit', (<built-in function BoolProperty>, {'name': 'Scene Unit', 'description': "Apply current scene's unit (as defined by unit scale) to exported data", 'default': False, 'attr': 'use_scene_unit'}))
('ascii', (<built-in function BoolProperty>, {'name': 'Ascii', 'description': 'Save the file in ASCII file format', 'default': False, 'attr': 'ascii'}))
('use_mesh_modifiers', (<built-in function BoolProperty>, {'name': 'Apply Modifiers', 'description': 'Apply the modifiers before saving', 'default': True, 'attr': 'use_mesh_modifiers'}))
('batch_mode', (<built-in function EnumProperty>, {'name': 'Batch Mode', 'items': (('OFF', 'Off', 'All data in one file'), ('OBJECT', 'Object', 'Each object as a file')), 'attr': 'batch_mode'}))
('axis_forward', (<built-in function EnumProperty>, {'name': 'Forward', 'items': (('X', 'X Forward', ''), ('Y', 'Y Forward', ''), ('Z', 'Z Forward', ''), ('-X', '-X Forward', ''), ('-Y', '-Y Forward', ''), ('-Z', '-Z Forward', '')), 'default': 'Y', 'update': <function orientation_helper.<locals>.wrapper.<locals>._update_axis_forward at 0x7f26dc2fd9d0>, 'attr': 'axis_forward'}))
('axis_up', (<built-in function EnumProperty>, {'name': 'Up', 'items': (('X', 'X Up', ''), ('Y', 'Y Up', ''), ('Z', 'Z Up', ''), ('-X', '-X Up', ''), ('-Y', '-Y Up', ''), ('-Z', '-Z Up', '')), 'default': 'Z', 'update': <function orientation_helper.<locals>.wrapper.<locals>._update_axis_up at 0x7f26dc2fd

Preset Example

This doesn't change the default, but is a one time one menu choice to set multiple defaults quickly.

enter image description here

As an example am going to run over Preset System Error and make presets for the mesh export stl operator.

import bpy
from bpy.types import Operator, Menu
from bl_operators.presets import AddPresetBase
from io_mesh_stl import ExportSTL

class ExportSTLPresetMenu(Menu):
    bl_label = "STL Export"
    preset_subdir = ExportSTL.bl_idname
    preset_operator = "script.execute_preset"
    #draw = Menu.draw_preset
    def draw(self, context):
        self.draw_preset(context)
        

class ExportSTLPresetOperator(AddPresetBase, Operator):
    '''Save STL Export Settings'''
    bl_idname = "export_stl.preset"
    bl_label = "STL Export Settings"
    preset_menu = "ExportSTLPresetMenu"

    # variable used for all preset values
    preset_defines = [
        "op  = bpy.context.active_operator"

    ]
    
    # properties to store in the preset
    preset_values = [ 
        f"op.{k}" 
        for k in ExportSTL.__annotations__.keys()

    ]

    # where to store the preset
    preset_subdir = ExportSTL.bl_idname
    @classmethod
    def poll(cls, context):
        return True 

# Display into an existing panel
def panel_func(self, context):
    layout = self.layout
    
    
    row = layout.row(align=True)
    
    row.menu(ExportSTLPresetMenu.__name__, text=ExportSTLPresetMenu.bl_label)
    
    row.operator(ExportSTLPresetOperator.bl_idname, text="", icon='ZOOM_IN')
    row.operator(ExportSTLPresetOperator.bl_idname, text="", icon='ZOOM_OUT').remove_active = True


classes = (
    ExportSTLPresetMenu,
    ExportSTLPresetOperator,
)


def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.STL_PT_export_main.prepend(panel_func)


def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)
    bpy.types.STL_PT_export_main.remove(panel_func)


if __name__ == "__main__":
    register()

TODO Messed up menu class name naming convention, requires edit.

This creates a preset Batch_Sel.py in your presets/export_mesh.stl folder , TODO perhaps could be in presets/operators/export_mesh.stl )

import bpy
op  = bpy.context.active_operator

op.filter_glob = '*.stl'
op.use_selection = True
op.global_scale = 1.0
op.use_scene_unit = False
op.ascii = True
op.use_mesh_modifiers = True
op.batch_mode = 'OFF'
op.axis_forward = 'Y'
op.axis_up = 'Z'

Revealing an oops, get eyest ttestted moment in GIF that I set ASCII, instead of batch, but you get the drift

Make a new op from old.

Will run thru the very basic theory here, rather than add a MWE

Similarly to above can very quickly make a new operator from original using

from io_mesh_stl import ExportSTL

new_op_class = type(
        "MyNewSTLExporter",
        (ExportSTL, Operator),
        change_dictionary,
        )

where change dictionary property : value keys we wish to change from the original. Eg to just change the label

change_dictionary = {"bl_label", "My STL Export"}
$\endgroup$

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