You are using FloatProperty, although you only imported StringProperty. You can use it as bpy.props.FloatProperty, or you can import it by adding it to the import from bpy.props import StringProperty, FloatProperty. You will need to define how it is controlled during the modal operation inside invoke as well. See the examples in the Templates menu in the Text Editor's Header.
You could open the example of the modal operator from the Templates menu in the Text Editor's header and then just modify it with what you need carefully checking that you don't break it. I would suggest using some event like mouse move or mouse wheel to control the variables, because you will have a floating panel at the bottom of the 3d view with the value after the operation anyway if you define bl_options = {'REGISTER', 'UNDO'} that is the usual Blender's behavior - adjusting with mouse on the fly and then having that last operator's history panel.
It would be something like this:
import bpy
C = bpy.context
class OBJECT_OT_make_proxy(bpy.types.Operator):
"""Saves the world from war and hunger"""
bl_idname = "object.make_proxy"
bl_label = "mProxy"
bl_options = {'REGISTER', 'UNDO'}
ratio: bpy.props.FloatProperty( name = "Decimate Ratio",
description = "Does something",
default = 0.25,
min = 0, max = 1
)
@classmethod
def poll(cls, context):
return context.object is not None
def execute(self, context): #This is apparently needed for the history panel to work
if "mProxyDecimate" not in C.object.modifiers:
bpy.ops.object.modifier_add(type='DECIMATE')
C.object.modifiers[len(C.object.modifiers)-1].name = 'mProxyDecimate'
C.object.modifiers["mProxyDecimate"].ratio = self.ratio
return {'FINISHED'}
def modal(self, context, event):
if event.type == 'WHEELUPMOUSE':
self.ratio += 0.02
C.object.modifiers["mProxyDecimate"].ratio = self.ratio
elif event.type == 'WHEELDOWNMOUSE':
self.ratio -= 0.02
C.object.modifiers["mProxyDecimate"].ratio = self.ratio
elif event.type == 'LEFTMOUSE': #You need to be able to confirm it
return {'FINISHED'}
elif event.type in {'RIGHTMOUSE', 'ESC'}: #..and also to get out of it
bpy.ops.object.modifier_remove(modifier="mProxyDecimate")
return {'CANCELLED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
if context.object:
if "mProxyDecimate" not in C.object.modifiers:
bpy.ops.object.modifier_add(type='DECIMATE')
C.object.modifiers[len(C.object.modifiers)-1].name = 'mProxyDecimate'
C.object.modifiers["mProxyDecimate"].ratio = self.ratio
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
else:
self.report({'WARNING'}, "No active object, could not finish")
return {'CANCELLED'}
def register():
bpy.utils.register_class(OBJECT_OT_make_proxy)
def unregister():
bpy.utils.unregister_class(OBJECT_OT_make_proxy)
if __name__ == "__main__":
register()
OBJECT_OT_object_make_proxysounds a bit similar to Display Port port of a display... It's generally a good idea to keep names short and logical. – Martynas Žiemys Dec 12 '18 at 11:47Guess I need to revisit it, as I obviously did not get it right. I am sorry, there is so much information to put into my little brain from so many sides, at times its a little overwhelming.
– morph3us Dec 12 '18 at 12:40OBJECT_OT_already means it's an operator in object category. If you think about it, you read the code a lot more than you write it so it should be easy to read and it's easier to read if it's only what is needed, but not more. The way I understand it operator class OBJECT_OT_some_tool with bl_idname object.some_tool still follows the conventions. – Martynas Žiemys Dec 12 '18 at 13:08