4
$\begingroup$

The code:

import bpy, bmesh
import bgl
import blf
import gpu
from gpu_extras.batch import batch_for_shader

vertices = (
    (60, 425), (310, 425),
    (60, 580), (310, 580))

indices = (
    (0, 1, 2), (2, 1, 3))

shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)
    
def draw_bevel(self, context):

    #box
    shader.bind()
    shader.uniform_float("color", (0.3, 0.3, 0.3, 0.8))
    batch.draw(shader)

    #text
    font_id = 0  
    x_offset = 0
    y_offset = 0
    active = context.active_object
    bevelWV = active.modifiers["Bevel"].width
    bevelWV = round(bevelWV, 4)
    bevelSG = active.modifiers["Bevel"].segments
    bevelSG = round(bevelSG, 4)
    
    blf.color(font_id, 0, 0.292, 1, 0.7)
    blf.position(font_id, 78, 510, 0)
    blf.size(font_id, 27, 176)
    blf.draw(font_id, "BEVEL" )
    
    blf.color(font_id, 0, 1, 0.232, 0.7)
    blf.position(font_id, 83, 480, 0)
    blf.size(font_id, 27, 81)
    blf.draw(font_id, "WIDTH" )
    
    blf.color(font_id, 0, 1, 0.232, 0.7)
    blf.position(font_id, 83, 450, 0)
    blf.size(font_id, 27, 81)
    blf.draw(font_id, "SEGS" )
    
    blf.color(font_id, 1, 1, 1, 0.7)
    blf.position(font_id, 195, 480, 0)
    blf.size(font_id, 27, 81)
    blf.draw(font_id, str(bevelWV))
    
    blf.color(font_id, 1, 1, 1, 0.7)
    blf.position(font_id, 195, 450, 0)
    blf.size(font_id, 27, 81)
    blf.draw(font_id, str(bevelSG))


class BevelSetModal(bpy.types.Operator):
    """Move an object with the mouse, example"""
    bl_idname = "bevel.set_operator"
    bl_label = "Bevel Set modal"

    first_mouse_x = IntProperty()
    first_value = FloatProperty()

    @classmethod
    def poll(cls, context):
        mod_types = set()
        for o in context.selected_objects:
            for m in o.modifiers:
                mod_types.add(m.type)
        return 'BEVEL' in mod_types and context.object.type == 'MESH'


    def modal(self, context, event):
        context.area.tag_redraw()
        if event.type == 'MOUSEMOVE':
            self.mouse_path.append((event.mouse_region_x, event.mouse_region_x))
            delta = self.first_mouse_x - event.mouse_x
            bpy.context.object.modifiers["Bevel"].width = self.first_value + delta * -0.001
            
        elif event.type == 'MOUSEMOVE'+'P':
            self.mouse_path.append((event.mouse_region_x, event.mouse_region_x))
            delta = self.first_mouse_x - event.mouse_x
            bpy.context.object.modifiers["Bevel"].profile = self.first_value + delta * -0.001    
          
        elif event.type == 'V' and event.value=='PRESS':    
            bpy.context.object.modifiers["Bevel"].use_only_vertices = True
      
        elif event.type == "WHEELUPMOUSE":
            bpy.context.object.modifiers["Bevel"].segments += 1
        
        elif event.type == "WHEELDOWNMOUSE":
            bpy.context.object.modifiers["Bevel"].segments -= 1  

        elif event.type == 'LEFTMOUSE':
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'FINISHED'}

        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            bpy.context.object.modifiers["Bevel"].width = self.first_value
            return {'CANCELLED'}

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        
        if context.area.type == 'VIEW_3D':         
            args = (self, context)            
            self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_bevel, args, 'WINDOW', 'POST_PIXEL')
            self.mouse_path = []
            
        
        if context.object:
            self.first_mouse_x = event.mouse_x
            self.first_value = bpy.context.object.modifiers["Bevel"].width
            context.window_manager.modal_handler_add(self) 

            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "No active object, could not finish")
            return {'CANCELLED'}

Basically what this does is draw a 2d polygon box using the new gpu module in 2.8 and some blf text which displays the bevel modifier data, in a modal operator (if there is a bevel modifier present on the mesh object).


As you can see the coordinates of the box and the blf text are both hardcoded. My goal is make the elements get aligned to the right side of the cursor and move along with it. I absolutely have no idea on how to even approach this.

BevelModal


I tried adding the solution provided by @brockmann but this is what happens:

bevelmodal

The box is just moving diagonally instead of following the mouse x,y. The example script works fine on its own but the mouse coordinates seem to be confliciting with the modal operator mouse maps.

$\endgroup$
1
  • 1
    $\begingroup$ Might be an idea to post your code using answer below that produces wrong result shown. Couple of recommendations. Don't rely on gloabally using bpy.context .. if context is an argument of method use that instead. Also look into using other methods than ob.modifiers["Bevel"] which throws an error if no modifiers, if modifier named "Bevel" doesn't exist or has been renamed, or is named "Bevel" but isn't a bevel modifier. eg mods = [m for m in ob.modifiers if m.type == 'BEVEL'] is a list of all bevel mods on object. Do nothing if there is none. $\endgroup$
    – batFINGER
    Aug 3, 2020 at 11:15

1 Answer 1

6
+100
$\begingroup$

Record the cursor path using Event.mouse_region_* and offset the vertices of the rectangle based on x and y coords of the cursor. Following code is based on operator_modal_draw.py template:

enter image description here

import bpy
import bgl
import blf
import gpu
from gpu_extras.batch import batch_for_shader

        
def draw_callback_px(self, context):
    x, y = self.mouse_path[-1]
    
    vertices = (
        (x, y-50), (x+70, y-50),
        (x, y), (x+70, y))

    indices = (
        (0, 1, 2), (2, 1, 3))

    bgl.glEnable(bgl.GL_BLEND)
    bgl.glEnable(bgl.GL_LINE_SMOOTH)
    shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
    batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)
    bgl.glEnable(bgl.GL_BLEND)
    bgl.glLineWidth(1) # Set the line width
    shader.bind()
    shader.uniform_float("color", (0.2, 0.7, 0.2, 0.5))
    batch.draw(shader)

    font_id = 0  # XXX, need to find out how best to get this.

    # draw some text
    font_offset = 10
    blf.position(font_id, x+font_offset, y-font_offset*2, 0)
    blf.size(font_id, 20, 72)
    blf.draw(font_id, "Hello")
    
    # restore opengl defaults
    bgl.glLineWidth(1)
    bgl.glDisable(bgl.GL_BLEND)


class ModalDrawOperator(bpy.types.Operator):
    """Draw a line with the mouse"""
    bl_idname = "view3d.modal_operator"
    bl_label = "Simple Modal View3D Operator"
    
    @classmethod
    def poll(cls, context):
        return context.active_object is not None 
    
    def modal(self, context, event):
        context.area.tag_redraw()

        if event.type == 'MOUSEMOVE':
            self.mouse_path.append((event.mouse_region_x, event.mouse_region_y))

        elif event.type == 'LEFTMOUSE':
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'FINISHED'}

        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'CANCELLED'}

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        if context.area.type == 'VIEW_3D':
            # the arguments we pass the the callback
            args = (self, context)
            # Add the region OpenGL drawing callback
            # draw in view space with 'POST_VIEW' and 'PRE_VIEW'
            self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')

            self.mouse_path = []
            
            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "View3D not found, cannot run operator")
            return {'CANCELLED'}


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


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


if __name__ == "__main__":
    register()

To control the bevel modifier interactively I'd suggest store a reference to the bevel modifier in your invoke() method. This way you can access all the modifier properties from all other methods as well as adding a bevel modifier in case there is no modifier added to the object already.

enter image description here

import bpy
import bgl
import blf
import gpu
from gpu_extras.batch import batch_for_shader

        
def draw_callback_px(self, context):
    x, y = self.mouse_path[-1]
    
    vertices = (
        (x, y-50), (x+100, y-50),
        (x, y), (x+100, y))

    indices = (
        (0, 1, 2), (2, 1, 3))

    bgl.glEnable(bgl.GL_BLEND)
    bgl.glEnable(bgl.GL_LINE_SMOOTH)
    shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
    batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)
    bgl.glEnable(bgl.GL_BLEND)
    bgl.glLineWidth(1) # Set the line width
    shader.bind()
    shader.uniform_float("color", (0.2, 0.7, 0.2, 0.5))
    batch.draw(shader)

    font_id = 0  # XXX, need to find out how best to get this.

    # draw some text
    font_offset = 10
    blf.position(font_id, x+font_offset, y-font_offset*2, 0)
    blf.size(font_id, 20, 72)
    blf.draw(font_id, "{:.2f}".format(self.bevel_mod.width))
    
    # restore opengl defaults
    bgl.glLineWidth(1)
    bgl.glDisable(bgl.GL_BLEND)


class ModalDrawOperator(bpy.types.Operator):
    """Draw a line with the mouse"""
    bl_idname = "view3d.modal_operator"
    bl_label = "Simple Modal View3D Operator"
    
    def bevel_modifier(self, context):
        obj = context.active_object
        mods = [m for m in obj.modifiers if m.type == 'BEVEL']
        return mods[0] if mods else obj.modifiers.new("Interactive Bevel", 'BEVEL')
    
    @classmethod
    def poll(cls, context):
        return context.active_object is not None 
    
    def modal(self, context, event):
        context.area.tag_redraw()

        if event.type == 'MOUSEMOVE':
            self.mouse_path.append((event.mouse_region_x, event.mouse_region_y))
            self.bevel_mod.width = event.mouse_region_x * .001

        elif event.type == 'LEFTMOUSE':
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'FINISHED'}

        elif event.type in {'RIGHTMOUSE', 'ESC'}:
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'CANCELLED'}

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        if context.area.type == 'VIEW_3D':
            # the arguments we pass the the callback
            args = (self, context)
            # Add the region OpenGL drawing callback
            # draw in view space with 'POST_VIEW' and 'PRE_VIEW'
            self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')

            self.mouse_path = []
            
            # Bevel Modifier
            self.bevel_mod = self.bevel_modifier(context)
            self.bevel_offset = self.bevel_mod.width

            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "View3D not found, cannot run operator")
            return {'CANCELLED'}


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


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


if __name__ == "__main__":
    register()
$\endgroup$
6
  • $\begingroup$ please check the edit $\endgroup$
    – Retrax
    Jul 27, 2020 at 17:40
  • 1
    $\begingroup$ Working for me, check out the gif... so I guess there is something wrong with your implementation. @Retrax Suggest add your code to the q. $\endgroup$
    – brockmann
    Jul 27, 2020 at 18:53
  • $\begingroup$ The code is exactly the same as before I just replaced my box implementation with yours $\endgroup$
    – Retrax
    Jul 29, 2020 at 16:31
  • $\begingroup$ I guess that's caused by your additional calculation (delta variable) in your modal method @Retrax I'd suggest try to implement your bevel actions step by step based on my code above. I won't go over that since this is out of question scope IMHO. Also note: according to pep8 variable names should be lower_case... $\endgroup$
    – brockmann
    Jul 30, 2020 at 9:45
  • $\begingroup$ Perhaps use blf to put object name in box. $\endgroup$
    – batFINGER
    Aug 3, 2020 at 11:17

You must log in to answer this question.

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