0
$\begingroup$

I want to develop Blender 3.1 plugins with Python and use Serpens 3 as well to generate some starting points. I generated a simple layout in an already existing panel in Object Data Properties->Attributes: enter image description here

The following simple code is generated:

bl_info = {
    "name" : "TestAddon",
    "author" : "Mr Testman", 
    "description" : "Test addon development",
    "blender" : (3, 0, 0),
    "version" : (1, 0, 0),
    "location" : "",
    "waring" : "",
    "doc_url": "", 
    "tracker_url": "", 
    "category" : "3D View" 
}

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.


import bpy
import bpy.utils.previews


def string_to_int(value):
    if value.isdigit():
        return int(value)
    return 0


addon_keymaps = {}
_icons = None
nodetree = {}


def sna_add_to_data_pt_mesh_attributes_E322F(self, context):
    if not ( not 'EDIT_MESH'==bpy.context.mode):
        layout = self.layout
        row_24CE0 = layout.row(heading='', align=False)
        row_24CE0.alert = False
        row_24CE0.enabled = True
        row_24CE0.use_property_split = False
        row_24CE0.use_property_decorate = False
        row_24CE0.scale_x = 1.0
        row_24CE0.scale_y = 1.0
        row_24CE0.alignment = 'Expand'.upper()
        op = layout.operator('sn.dummy_button_operator', text='Assign', icon_value=0, emboss=True, depress=False)
        op = layout.operator('sn.dummy_button_operator', text='Remove', icon_value=0, emboss=True, depress=False)
        op = layout.operator('sn.dummy_button_operator', text='Select', icon_value=0, emboss=True, depress=False)
        op = layout.operator('sn.dummy_button_operator', text='Deselect', icon_value=0, emboss=True, depress=False)
        layout.prop(bpy.data.scenes['Scene'].tool_settings, 'vertex_group_weight', text='', icon_value=0, emboss=True)


def register():
    global _icons
    _icons = bpy.utils.previews.new()
    bpy.types.DATA_PT_mesh_attributes.append(sna_add_to_data_pt_mesh_attributes_E322F)


def unregister():
    global _icons
    bpy.utils.previews.remove(_icons)
    wm = bpy.context.window_manager
    kc = wm.keyconfigs.addon
    for km, kmi in addon_keymaps.values():
        km.keymap_items.remove(kmi)
    addon_keymaps.clear()
    bpy.types.DATA_PT_mesh_attributes.remove(sna_add_to_data_pt_mesh_attributes_E322F)

# part added by me
if __name__ == "__main__":
    register()

When I run this script several times during the development/debugging in the GUI, it always stacks additional same buttons to the GUI.

enter image description here

At the same time when I take the code for a completely new panel e.g. from this tutorial https://www.youtube.com/watch?v=jZt3MO5D1R8&t=617s&ab_channel=Darkfall, each Run Script (visually) only updates the elements, if changed, but doesn't add/stack the same elements.

What should I change in the Serpens code to avoid stacking of the GUI elements during the development/rerunning the script?

$\endgroup$

1 Answer 1

0
$\begingroup$

The difference is they are registering a panel which is uniquely identified with bl_idname, so registering it twice overwrites the first iteration.

Here you're adding UI elements to a panel, and UI layout elements are not uniquely identified. So each time you run the script, new instances of everything is generated and you lose the handle on the previous occurences. When you run a script in the text editor, register and unregister are not called automatically, that's why you had to add the last two lines. You can "force" the script to register, but there is now way for Blender to know when to unregister it.

One solution is to add an operator to do it for you. I suggest adding a slight delay to avoid unregistering an operator while it is running :

class UnregisterMyScript(bpy.types.Operator):
    bl_idname = "unregister.myscript"
    bl_label = "Unregister my script"

    def execute(self, context):
        bpy.app.timers.register(unregister, first_interval=.1)
        return {"FINISHED"}


def register():
    global _icons
    _icons = bpy.utils.previews.new()
    bpy.types.DATA_PT_mesh_attributes.append(sna_add_to_data_pt_mesh_attributes_E322F)
    bpy.utils.register_class(UnregisterMyScript)


def unregister():
    global _icons
    bpy.utils.previews.remove(_icons)
    wm = bpy.context.window_manager
    kc = wm.keyconfigs.addon
    for km, kmi in addon_keymaps.values():
        km.keymap_items.remove(kmi)
    addon_keymaps.clear()
    bpy.types.DATA_PT_mesh_attributes.remove(sna_add_to_data_pt_mesh_attributes_E322F)
    bpy.utils.unregister_class(UnregisterMyScript)

And next time right before you run your script, simply search for the operator with F3 or add it to your panel UI layout and click on it to unregister your script.

You can also reset scripts by clicking on the blender icon in the far top left of the interface > System > Reload Scripts.

$\endgroup$

This site is temporarily in read-only mode and not accepting new answers.

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