1
$\begingroup$

Blender Python (overall Python) noob here. I simply want to make a Custom UI that toggles my models accessories by enabling and disabling their viewport and render visibility. Generally, I want to know how to make customizeable input for operators but I simply can't wrap my head around it. I'm trying to avoid using Custom Drivers since I need a more user friendly input and not just a float property.

I don't exactly have a full script since I'm not sure how to approach it.

Here's a script that toggles the visibility of both the viewport and render but isn't a toggle:

enter image description here

import bpy

class UiPanel(bpy.types.Panel):
    bl_label = "Custom UI Visibility Toggles"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_category = 'Item'

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

        layout.operator("cube.button")

class CubeVisibility(bpy.types.Operator):
    bl_idname = "cube.button"
    bl_label = "Cube Visibility"

    def execute(self, context):
        if bpy.data.objects['Cube'].hide_viewport == True:
            bpy.data.objects['Cube'].hide_viewport = False
        else:
            bpy.data.objects['Cube'].hide_viewport = True

        if bpy.data.objects['Cube'].hide_render == True:
            bpy.data.objects['Cube'].hide_render = False
        else:
            bpy.data.objects['Cube'].hide_render = True

        return{'FINISHED'}

def register():
    bpy.utils.register_class(UiPanel)
    bpy.utils.register_class(CubeVisibility)


def unregister():
    bpy.utils.unregister_class(UiPanel)
    bpy.utils.unregister_class(CubeVisibility)

if __name__ == "__main__":
    register()

This one has the toggle feature but are on separate buttons (needs to be only one):

enter image description here

import bpy

class customMenu(bpy.types.Panel):
    """Sample Toggle"""
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_label = "Toggle"
    bl_idname = "custom_ui"
    bl_category = 'Item'

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

        row = layout.row(align=True)
        row.prop(bpy.data.objects['Cube'], "hide_viewport",text="Viewport")
        row.prop(bpy.context.object, "hide_render",text="View")

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


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


if __name__ == "__main__":
    register()
$\endgroup$
  • $\begingroup$ Hi. Which specific part of making the script are you having trouble with? What do you have so far? $\endgroup$ – Ray Mairlot Sep 2 '19 at 11:53
  • $\begingroup$ I don't have a full script yet but here are some scripts I found similar to what I'm trying to achieve. $\endgroup$ – Scottie Doria Sep 2 '19 at 14:24
  • $\begingroup$ This is a bad idea, what if "Cube" doesn't exist? What you can do is providing the user a list of objects in the scene to choose from (via pointer property) otherwise this script will always return an error if the cube can't be found... $\endgroup$ – brockmann Sep 3 '19 at 9:52
  • $\begingroup$ The "Cube" is just an example what's important is to have UI that toggles an object's visibility while being a bool property. $\endgroup$ – Scottie Doria Sep 3 '19 at 11:38
  • $\begingroup$ The important bit is what object exactly. The object in context or the active object or any selection based on what? BTW: Wasn't notified, you have to use the @-sign to ping someone @ScottieDoria. $\endgroup$ – brockmann Sep 3 '19 at 15:19
1
$\begingroup$

Syncing both properties and controlling them by another BoolProperty, takes a lot effort to manage situations like the User disables one of the properties in the Outliner: Is your custom (single) property in this case False or True?

One simple approach is using an operator to set the state of both properties and also store the name of the object (unique). In case the string property is not empty, the operator "resets" all the values:

import bpy

from bpy.props import (StringProperty,
                       PointerProperty,
                       )

from bpy.types import (Panel,
                       Operator,
                       PropertyGroup,
                       )



class MySettings(PropertyGroup):

    my_obj: StringProperty(
        name="Object Name",
        description="Helper property to store an object name",
        )

# ------------------------------------------------------------------------
#    Operators
# ------------------------------------------------------------------------

class SimpleOperator(Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Disable in Viewports and Renders"

    @classmethod
    def poll(cls, context):
        #return context.active_object is not None 
        return context.object is not None

    def execute(self, context):
        scene = context.scene
        mytool = scene.my_tool

        if not mytool.my_obj:
            # Disable both properties and store the name
            mytool.my_obj = context.active_object.name
            context.active_object.hide_render = True
            context.active_object.hide_viewport = True

        else:
            # enable both properties
            obj = context.scene.objects.get(mytool.my_obj)
            mytool.my_obj = "" # Set the string to None
            if obj: 
                obj.hide_viewport = False
                obj.hide_render = False
                obj.select_set(True)

            # Set the active_object:
            # https://blender.stackexchange.com/a/132829/3710
            #bpy.context.view_layer.objects.active = obj

        return {'FINISHED'}


# ------------------------------------------------------------------------
#    Panel in Object Mode
# ------------------------------------------------------------------------

class OBJECT_PT_CustomPanel(Panel):
    bl_label = "My Panel"
    bl_idname = "OBJECT_PT_custom_panel"
    bl_space_type = "VIEW_3D"   
    bl_region_type = "UI"
    bl_category = "Tools"
    bl_context = "objectmode"   


    @classmethod
    def poll(self,context):
        return context.object is not None

    def draw(self, context):
        layout = self.layout
        scene = context.scene
        mytool = scene.my_tool

        layout.label(text="Object Properties")
        row = layout.row(align=True)
        row.prop(context.object, "hide_viewport")
        row.prop(context.object, "hide_render")

        layout.label(text="Toggle Operator")
        layout.operator(SimpleOperator.bl_idname)
        layout.separator()

# ------------------------------------------------------------------------
#    Registration
# ------------------------------------------------------------------------

classes = (
    MySettings,
    SimpleOperator,
    OBJECT_PT_CustomPanel
)

def register():
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)

    bpy.types.Scene.my_tool = PointerProperty(type=MySettings)

def unregister():
    from bpy.utils import unregister_class
    for cls in reversed(classes):
        unregister_class(cls)
    del bpy.types.Scene.my_tool


if __name__ == "__main__":
    register()

Layout code from: How to create a custom UI?

| improve this answer | |
$\endgroup$
0
$\begingroup$

Finally got something from other forums

enter image description here

enter image description here

import bpy

"""Viewport and render visibility toggle for "Cube" object"""

def update_bool(self, context):
    bpy.data.objects['Cube'].hide_viewport = not self.my_bool
    bpy.data.objects['Cube'].hide_render = not self.my_bool

class My_test(bpy.types.PropertyGroup):

    my_bool: bpy.props.BoolProperty(
        name='Cube Visibility',
        description="Hide or unhide render and viewport",
        default=False,
        update = update_bool
        #update = bpy.data.objects['Cube'].hide_viewport = True
    )
class TEST_PT_panel(bpy.types.Panel):
    """Creates a Panel in the scene context of the properties editor"""
    bl_label = "Custom UI Visibility Toggles"
    bl_idname = "TEST_PT_panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "Test"

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

        row = layout.row()
        my_test = context.scene.my_test

        row = layout.row()
        row.prop(my_test, "my_bool", toggle=True)


classes = (
    My_test,
    TEST_PT_panel,
)


def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.my_test = bpy.props.PointerProperty(type=My_test)


def unregister():
    del bpy.types.Scene.my_test
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)


if __name__ == "__main__":
    register()
| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ Thinking ahead: What if the user toggles one of the properties in the Outliner? $\endgroup$ – brockmann Sep 5 '19 at 7:49
  • $\begingroup$ Make it impossible by adding a driver that has the custom UI's input values. $\endgroup$ – Ben Oct 26 '19 at 11:24

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.