2
$\begingroup$

I know it can be done as shown here: https://www.youtube.com/watch?v=-fhI2imoo4U&feature=youtu.be&list=PLsowJ3v5QWhE9db_GcPnSrTXWJrA5poWg&t=50

(can't buy the addon right now as I'm broke) Does anyone know how that can be achieved in Python? Adding an existing panel inside a popup dialog? Thank you.

$\endgroup$
  • $\begingroup$ Never mind, I found out a way. I may make a tutorial for this, seems useful... $\endgroup$ – Antoni4040 Feb 2 '18 at 17:30
  • 1
    $\begingroup$ If you found the solution, please add and answer and then accept it. $\endgroup$ – Retrax Feb 2 '18 at 19:56
0
$\begingroup$

This turned out to be easier than I thought. You simply do something like this:

import bpy
from bl_ui.properties_paint_common import UnifiedPaintPanel

class BasicSettings(bpy.types.Operator, UnifiedPaintPanel):
    """
    The basic brush settings like strength, radius etc.
    """
    bl_idname = "view3d.basic_settings"
    bl_label = "Brush Basic Settings"

    def execute(self, context):
        """
        Show the basic brush settings popup dialog.
        """
        return context.window_manager.invoke_popup(self, width=300)

    def draw(self, context):
        """
        What follows is basically a copy of the Python code
        responsible for the placement of the settings in the Tools tab,
        taken from Blender itself.
        """
        layout = self.layout
        toolsettings = context.tool_settings
        settings = self.paint_settings(context)
        brush = settings.brush

        capabilities = brush.sculpt_capabilities

        col = layout.column()

        col.separator()

        row = col.row(align=True)

        ups = toolsettings.unified_paint_settings
        if ((ups.use_unified_size and ups.use_locked_size) or
                ((not ups.use_unified_size) and brush.use_locked_size)):
            self.prop_unified_size(
                row, context, brush,
                "use_locked_size", icon='LOCKED')
            self.prop_unified_size(
                row, context, brush, "unprojected_radius", slider=True, text="Radius")
        else:
            self.prop_unified_size(
                row, context, brush,
                "use_locked_size", icon='UNLOCKED')
            self.prop_unified_size(
                row, context, brush,
                "size", slider=True, text="Radius")

Etc. These are just copy pasted from within Blender. Just go to any UI element in the panel you want, right click on click and then Edit Source, it will open a file in the text editor and from that you have to figure out what you want and then just copy paste it. And now, whenever you call this operator shown here, it will create a popup dialog similar to the panel.

$\endgroup$
0
$\begingroup$

To "port" a Blender UI panel you need to access its base classes in the respective space then rewrite the panel class inheriting from these classes. All space scripts are located in the Blender\2.X\scripts\startup\bl_ui folder. For the Toolshelf panels, they are located in the 3D View space under the toolbar. Thus, this equates to the space_view3d_toolbar.py file.

Below is a standalone addon script that imports the Toolbar classes and uses them to write a popup class populated with the panel properties. The contents of the class are taken from the brush tools class (VIEW3D_PT_tools_brush) in the imported script and adapted for popup use:

bl_info = {
    "name": "Brush Panel",
    "author": "Author",
    "category": "3D View"
}

import bpy
from bpy.types import Operator
# Only imported the class the panel inherits from
from bl_ui.space_view3d_toolbar import View3DPaintPanel


class BrushPanel(Operator, View3DPaintPanel):
    bl_idname = "view3d.brush_panel"
    bl_label = "Brush"

    def execute(self, context):
        return context.window_manager.invoke_popup(self, width=300)

    def check(self, context):
        return True

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

        toolsettings = context.tool_settings
        settings = self.paint_settings(context)
        brush = settings.brush

        if not context.particle_edit_object:
            col = layout.split().column()
            col.template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8)

        capabilities = brush.sculpt_capabilities

        col = layout.column()

        col.separator()

        row = col.row(align=True)

        ups = toolsettings.unified_paint_settings
        if ((ups.use_unified_size and ups.use_locked_size) or
                ((not ups.use_unified_size) and brush.use_locked_size)):
            self.prop_unified_size(row, context, brush, "use_locked_size", icon='LOCKED')
            self.prop_unified_size(row, context, brush, "unprojected_radius", slider=True, text="Radius")
        else:
            self.prop_unified_size(row, context, brush, "use_locked_size", icon='UNLOCKED')
            self.prop_unified_size(row, context, brush, "size", slider=True, text="Radius")

        self.prop_unified_size(row, context, brush, "use_pressure_size")

        # strength, use_strength_pressure, and use_strength_attenuation
        col.separator()
        row = col.row(align=True)

        if capabilities.has_space_attenuation:
            row.prop(brush, "use_space_attenuation", toggle=True, icon_only=True)

        self.prop_unified_strength(row, context, brush, "strength", text="Strength")

        if capabilities.has_strength_pressure:
            self.prop_unified_strength(row, context, brush, "use_pressure_strength")

        # auto_smooth_factor and use_inverse_smooth_pressure
        if capabilities.has_auto_smooth:
            col.separator()

            row = col.row(align=True)
            row.prop(brush, "auto_smooth_factor", slider=True)
            row.prop(brush, "use_inverse_smooth_pressure", toggle=True, text="")

        # normal_weight
        if capabilities.has_normal_weight:
            col.separator()
            row = col.row(align=True)
            row.prop(brush, "normal_weight", slider=True)

        # crease_pinch_factor
        if capabilities.has_pinch_factor:
            col.separator()
            row = col.row(align=True)
            row.prop(brush, "crease_pinch_factor", slider=True, text="Pinch")

        # rake_factor
        if capabilities.has_rake_factor:
            col.separator()
            row = col.row(align=True)
            row.prop(brush, "rake_factor", slider=True)

        # use_original_normal and sculpt_plane
        if capabilities.has_sculpt_plane:
            col.separator()
            row = col.row(align=True)

            row.prop(brush, "use_original_normal", toggle=True, icon_only=True)

            row.prop(brush, "sculpt_plane", text="")

        if brush.sculpt_tool == 'MASK':
            col.prop(brush, "mask_tool", text="")

        # plane_offset, use_offset_pressure, use_plane_trim, plane_trim
        if capabilities.has_plane_offset:
            row = col.row(align=True)
            row.prop(brush, "plane_offset", slider=True)
            row.prop(brush, "use_offset_pressure", text="")

            col.separator()

            row = col.row()
            row.prop(brush, "use_plane_trim", text="Trim")
            row = col.row()
            row.active = brush.use_plane_trim
            row.prop(brush, "plane_trim", slider=True, text="Distance")

        # height
        if capabilities.has_height:
            row = col.row()
            row.prop(brush, "height", slider=True, text="Height")

        # use_frontface
        col.separator()
        row = col.row()
        row.prop(brush, "use_frontface", text="Front Faces Only")

        # direction
        col.separator()
        col.row().prop(brush, "direction", expand=True)

        # use_accumulate
        if capabilities.has_accumulate:
            col.separator()

            col.prop(brush, "use_accumulate")

        # use_persistent, set_persistent_base
        if capabilities.has_persistence:
            col.separator()

            ob = context.sculpt_object
            do_persistent = True

            # not supported yet for this case
            for md in ob.modifiers:
                if md.type == 'MULTIRES':
                    do_persistent = False
                    break

            if do_persistent:
                col.prop(brush, "use_persistent")
                col.operator("sculpt.set_persistent_base")


def register():
    bpy.utils.register_module(__name__)


def unregister():
    bpy.utils.unregister_module(__name__)


if __name__ == "__main__":
    register()

Managing the inheritance is key to this sort of modification. You are then free to make all sorts of changes to the panel/display class.

$\endgroup$

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.