Okay, so it took me a little while (and a lot of searching online), but it was this link (https://wiki.blender.org/index.php/Dev:Py/Scripts/Cookbook/Code_snippets/Interface#A_popup_dialog) that showed me what I needed.
What I have done is definitely a hack, and there are more elegant solutions, but if you are just looking for a way that works, then this will help a lot.
(Warning: Lots of code)
So it works by having three classes, which call each other in turn.
The "Clicked On" Operator
The first is an operator that simply exists to invoke the dialog box when clicked in the "search menu".
class slowClick(bpy.types.Operator):
bl_idname = "slowclick.move_operator"
bl_label = "Slow"
def execute(self, context):
# Default can be used since I only have a single dialog
bpy.ops.object.dialog_operator('INVOKE_DEFAULT')
return {'FINISHED'}
The Dialog Box
The second is the dialog box itself. This is invoked in the line bpy.ops.object.dialog_operator('INVOKE_DEFAULT')
, and then displayed.
The actual dialog box is built somewhat counter-intuitively. Rather than putting in sliders using the standard layout.row().prop
, all you actually do is define the variables, and on creation they can be changed. In my program that looked like this:
# create the sliders (and checkbox) in the dialog box so the user can change where the slo-mo starts and ends
firstSlowFrameP = bpy.props.IntProperty(
name="First Slow Frame:", # as you want it to appear on the slider
default=40, # optional
description="This is the first frame that will be in slow motion"
)
globalSelect = bpy.props.BoolProperty(
name="Global", # affect all objects in scene
default=False,
description="If true, all available objects will be affected"
)
As with normal panels, how you define the property defines what kind of item it will be (e.g. slider for 'Int' and checkbox for 'Bool'), and at the bottom it has an "OK" button.
def execute(self, context):
# first thing is to globalise the variables needed, and yes I know this isn't the best way
global firstSlowFrame, globalSelect
# reassign the variables based on the user's input on the sliders
firstSlowFrame = self.firstSlowFrameP
globalSelect = self.globalSelect
# this actually calls the operator that does the editing
bpy.ops.slow.move_operator()
return {'FINISHED'}
def invoke(self, context, event):
# call itself and run
wm = context.window_manager
return wm.invoke_props_dialog(self)
So the def execute()
details what happens when the "OK" button is pressed. It globalises all the variables that were changed in the slider, so they can be accessed by the main and final function.
(The def invoke()
just describes its creation when invoked)
The Main Function
Finally we get to the actual function. I won't put the main code here (If you want the addon go to How can I slow down or animate time?), but this is just your standard operator:
class Slow(bpy.types.Operator):
bl_idname = "slow.move_operator"
bl_label = "OK"
# get the global variables since this is in a class
global firstSlowFrame, globalSelect
#PROGRAM
All you have to do is get the variables that were changed by the sliders, and it all works nicely.
(Technically, if you press "space" and type "OK" you will get this and it will probably not work since the variables don't exist yet, so at the very beginning of the program I would define a "default" value for each that will be used if the user accidentally bypasses the whole dialog box.)
Example
This is a template that can be used, feel free to use it, modify it, I doubt I invented this hack so I mean credit me if you want but I'm really not fussed, just hope this can help someone.
import bpy
import math
from bpy.props import *
scn = bpy.context.scene
# initialising the variables that will be in the dialog
intVariable = 10
strVariable = "Hello"
boolVariable = True
# This is the operator that is clicked by the user, triggering the dialog box
class ToBeClicked(bpy.types.Operator):
bl_idname = "toBeClicked.move_operator"
bl_label = "WHATEVER YOU WANT YOUR USER TO TYPE IN"
def execute(self, context):
# Default can be used since I only have a single dialog
bpy.ops.object.dialog_operator('INVOKE_DEFAULT')
return {'FINISHED'}
class DialogOperator(bpy.types.Operator):
bl_idname = "object.dialog_operator"
bl_label = "Dialog Operator"
# create the sliders (and checkbox) in the dialog box
intVariableProperty = bpy.props.IntProperty(
name="Integer Variable:", # as you want it to appear on the slider
default=10, # optional
description="This is an integer" # what you get when you hover over
)
strVariableProperty = bpy.props.StringProperty(
name="String Variable:", # as you want it to appear on the bar
default="Hello", # optional
description="This is a string" # what you get when you hover over
)
boolVariableProperty = bpy.props.BoolProperty(
name="Boolean Variable:", # as you want it to appear on the checkbox
default=True,
description="This is a boolean checkbox" # what you get when you hover over
)
# This is what happens when "OK is pressed
def execute(self, context):
# first thing is to globalise the variables needed, and yes I know this isn't the best way
global intVariable, strVariable, boolVariable
# this is just for troubleshooting
print("Dialog Runs")
print(self.intVariableProperty, self.strVariableProperty, self.boolVariableProperty)
# reassign the variables based on the user's input on the sliders
intVariable = self.intVariableProperty
strVariable = self.strVariableProperty
boolVariable = self.boolVariableProperty
# this actually calls the operator that does the editing
bpy.ops.slow.move_operator()
return {'FINISHED'}
# This is just saying that when it is invoked, how to display it
def invoke(self, context, event):
# call itself and run
wm = context.window_manager
return wm.invoke_props_dialog(self)
# This is the actual program that does something with the variables
class Main(bpy.types.Operator):
bl_idname = "main.move_operator"
bl_label = "OK"
# get the global variables since this is in a class
global intVariable, strVariable, boolVariable
# This just makes sure that everything can be applied
# if (CONDITION):
# PROGRAM
return {'FINISHED'}
# This can give an error if you want certain conditions met
# else:
# self.report({'ERROR'}, 'There is no animation')
# return {'CANCELLED'}
# registers everything
def register():
bpy.utils.register_class(Main)
bpy.utils.register_class(ToBeClicked)
bpy.utils.register_class(DialogOperator)
if __name__ == "__main__":
register()
I know this is really very long, in answer to a very unimportant question, but it took me a while to solve this and I'm pretty pleased with it, so I hope other people can find it just as useful.
Feel free to add corrections in the comments!