2
$\begingroup$

Edit. I think I may have solved this in a slightly different way. I can now join the objects together without much issue. Please note that I've updated the script for blender 2.8 also. I've added the script below this text but kept all the original stuff for historic purposes. Thanks to everyone who helped out on this :)

class SameJoin(Operator):
"""Select and Join Similarly Named objects"""
bl_idname = "same.join"
bl_label = "Join Similar"
bl_description = 'joins objects who\'s names are the same'
bl_options = {'REGISTER', 'UNDO'}

def execute(self, context):
    """gets all the names of everything in the scene
    and then updates the names without their object ids
    so they can be joined as same name items"""
    for obj in bpy.context.selected_objects:
        namestore_1 = obj.name
        namestore_1A = re.sub('[^a-zA-Z]+', '', namestore_1)
        print(namestore_1A)
        context.view_layer.objects.active = obj
        for ob in bpy.context.scene.objects:
            namestore_2 = ob.name
            namestore_2A = re.sub('[^a-zA-Z]+', '', namestore_2)
            print(namestore_2A)

            """makes sure all objects are mesh before
            performing mesh operation"""
            if ob.type == 'MESH':
                if str(namestore_1A) == (namestore_2A):
                    ob.select_set(True)
                else:
                    ob.select_set(False)                
    for selected in bpy.context.selected_objects:            
        try:
            if selected.type == 'MESH':
                bpy.ops.object.join()
                self.report({'INFO',}, 'Objects joined')    
        except:
            self.report({'INFO',}, 'Object not joined')
    return {'FINISHED'}

I've read the other threads related to this subject and have almost gotten to the end. I have quite a specific query though that I can't seem to find answered exactly so here goes....

I'm trying to have create GUI for my script which joins objects whos names are similar, to a certain number of characters.

See the script below.

import bpy
from bpy.types import Panel, Operator
from bpy.props import * 


#Select and Join Similarly Named objects

charlen = 15     

class SameJoin(Operator):
    bl_idname = "same.join"
    bl_label = "Join SameName"
    bl_description = 'This tool will allow you to join,objects who\'s names have the same sequential characters up to a certain number of characters.'
    bl_options = {'REGISTER', 'UNDO'}
    #description

    charln = IntProperty(
            name="Character Match Value",
            description="Number of characters to match with other objects name in scene before joining",
            min=1, max=30,
            default=15,
            soft_min=1,
            soft_max=30,
            )

    def joinsame(self, charln):
        for obj in bpy.context.selected_objects:

            namestore_1 = obj.name[:charlen]
            bpy.context.scene.objects.active = obj
            for ob in bpy.context.scene.objects:
                namestore_2 = ob.name[:charlen]
                if ob.type == 'MESH':
                    if str(namestore_1) == (namestore_2):
                        ob.select = True
                    else:
                        ob.select = False
        for selected in bpy.context.selected_objects:
            if selected.type == 'MESH':
                bpy.ops.object.join()

    def execute(self, context):
        self.joinsame(self.charln)
        self.report({'INFO'}, 'similar joined')
        return {'FINISHED'}

class JoinSame(bpy.types.Panel):
    #"""A custom panel in the Viewport Toolbar"""
    bl_label = "JoinSameName"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"
    bl_context = "objectmode"
    bl_category = "JoinSame"


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

        row = layout.row()
        row.label(text = "Selected Object")

        row = layout.row()
        row.label(text = "Name match = %s characters" % (charlen))


        row = layout.row()
        row.operator("same.join", text = "1. Join SameName")



def register():
    bpy.utils.register_class(SameJoin)
    bpy.utils.register_class(JoinSame) 

def unregister():
    bpy.utils.unregister_class(SameJoin)
    bpy.util.unregister_class(JoinSame)


if __name__ == "__main__":
    register()

Sorry in advance for how ugly my code is.

The problem I'm having at the moment is that 1. you have to push the button before the number slider appears so the user has no idea that they have the option till the objects are all joined (we're talking lists of tens of thousands of objects here so updating dynamically doesnt seem like a good idea) 2.I'd much rather the slider sat above the button and was there from the begining.

Is any of this possible?

Thanks in advance for any help at all. I know this is a long one...

$\endgroup$
3
$\begingroup$

Included active objects name. Included verification that a selection is made and that active object is a mesh type to avoid issues when joining. Updated report to include count of objects joined to active object and active objects name. Included visual queue of criteria under character match value property. Please note the below script may not fully comply with PEP8 style guide.

import bpy
from bpy.types import Panel, Operator


def initprop():
    bpy.types.Scene.charln = bpy.props.IntProperty(
        name="Character Match Value",
        description="Number of characters to match with other objects name in \
        scene before joining",
        min=1,
        max=30,
        default=15
        )


def delprop():
    del bpy.types.Scene.charln


class SameJoin(Operator):
    bl_idname = "same.join"
    bl_label = "Join SameName"
    bl_description = 'This tool will allow you to join,objects who\'s names \
    have the same sequential characters up to a certain number of characters.'
    bl_options = {'REGISTER', 'UNDO'}
    scene = bpy.context.scene

    @classmethod
    def poll(cls, context):
        selection_made = (bpy.context.selected_objects != [])
        active_mesh = bpy.context.active_object.type == 'MESH'
        return (selection_made and active_mesh) is True

    def joinsame(self, charlen):
        count = 0
        for obj in bpy.context.selected_objects:
            if bpy.context.active_object == obj:
                namestore_1 = obj.name[:charlen]
        for ob in bpy.context.scene.objects:
            namestore_2 = ob.name[:charlen]
            if ob.type == 'MESH':
                if str(namestore_1) == (namestore_2):
                    ob.select = True
                else:
                    ob.select = False
        for selected in bpy.context.selected_objects:
            if selected.type == 'MESH':
                bpy.ops.object.join()
                count += 1
        return count - 1

    def execute(self, context):
        scene = bpy.context.scene
        self.report({'INFO'}, '%s similar objects joined to %s' %
            (self.joinsame(scene.charln), bpy.context.active_object.name))
        return {'FINISHED'}


class JoinSame(bpy.types.Panel):
    bl_label = "JoinSameName"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"
    bl_context = "objectmode"
    bl_category = "JoinSame"

    def draw(self, context):
        obj = bpy.context.object
        scene = bpy.context.scene
        layout = self.layout
        col = layout.column(align=True)
        col.label(text="Last Active Object")
        if obj and bpy.context.active_object:
            col.label(text=obj.name)
        else:
            col.label(text="Please select an object")
        col.prop(scene, "charln")
        col.label(text=str(obj.name[:scene.charln]))
        col.operator("same.join", text="1. Join Same Name Scene")


def register():
    bpy.utils.register_class(SameJoin)
    bpy.utils.register_class(JoinSame)
    initprop()


def unregister():
    bpy.utils.unregister_class(SameJoin)
    bpy.util.unregister_class(JoinSame)
    delprop()


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

Add a property to the bpy.types.WindowManager class and draw that above the button. Use the value of that property to set the charln property for the button. row.operator() returns an object you can set this on.

$\endgroup$
  • 2
    $\begingroup$ Hi, Thanks very much for your reply! I'm currently trying to go through what you said but its a bit slow going for me at the moment since I'm having to learn stuff as I go so it may be a while before i return back to check this as an answer :) $\endgroup$ – dood Aug 2 '17 at 22:58
  • $\begingroup$ @dood I was looking into this a bit and have some other concerns you may want to work on as well I'll keep it brief as Dr. Sybren can help much more than i can, but you have no verification that an object is selected prior to executing your operator which may cause issues later. Also i don't believe the initial for loop in the joinsame function is necessary given the nested loop is testing against all objects in the scene instead of only joining similarly named objects of the original selection. Depending on your original goal this may cause issues. $\endgroup$ – Ratt Nov 23 '18 at 1:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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