Skip to content

Instantly share code, notes, and snippets.

@tin2tin
Last active September 29, 2019 20:36
Show Gist options
  • Save tin2tin/ad23f414a97d67748cac0aca3798d833 to your computer and use it in GitHub Desktop.
Save tin2tin/ad23f414a97d67748cac0aca3798d833 to your computer and use it in GitHub Desktop.
Draws keyframes in Graph Editor for ex. editing volume levels on VSE strips(only poc).
#Start Draw Volume Keyframes operator from the Graph Editor using F3
import bpy
import bgl
import blf
import gpu
from gpu_extras.batch import batch_for_shader
def draw_callback_px(self, context):
#print("mouse points", len(self.mouse_path))
font_id = 0 # XXX, need to find out how best to get this.
# draw some text
#blf.position(font_id, 15, 30, 0)
#blf.size(font_id, 20, 72)
#blf.draw(font_id, "Hello Word " + str((self.mouse_path[0])))
shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
bgl.glEnable(bgl.GL_BLEND)
bgl.glLineWidth(2)
batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": self.mouse_path})
shader.bind()
shader.uniform_float("color", (1.0, 1.0, 1.0, 0.1))
batch.draw(shader)
# restore opengl defaults
bgl.glLineWidth(1)
bgl.glDisable(bgl.GL_BLEND)
#influence
class ModalDrawOperator(bpy.types.Operator):
"""Draw a line with the mouse"""
bl_idname = "graph.modal_operator"
bl_label = "Draw Volume Keyframes"
def modal(self, context, event):
context.area.tag_redraw()
if event.type == 'MOUSEMOVE':
region = context.region
x = event.mouse_region_x# - offset_x
y = event.mouse_region_y# - offset_y
x, y = region.view2d.region_to_view(x, y)
#bpy.context.scene.sequence_editor.sequences_all["test.mp3"].volume = y
context.scene.frame_current = x
if event.value == 'PRESS':
#bpy.ops.graph.select_all(action='SELECT')
#elif event.value == 'PRESS':
self.mouse_path.append((event.mouse_region_x, event.mouse_region_y))
# Mouse position
width = context.region.width
height = context.region.height
rv1 = context.region.view2d.region_to_view(0, 0)
rv2 = context.region.view2d.region_to_view(width, height)
region = context.region
x = event.mouse_region_x# - offset_x
y = event.mouse_region_y# - offset_y
x, y = region.view2d.region_to_view(x, y)
sequence = context.scene.sequence_editor
scene = context.scene
strip = sequence.active_strip
bpy.context.scene.sequence_editor.sequences_all[strip.name].volume = y
context.scene.frame_current = x
bpy.ops.graph.keyframe_insert(type='SEL')
# bpy.ops.graph.select_all(action='DESELECT')
# bpy.ops.graph.select_column(mode='CFRA')
# bpy.ops.graph.select_more()
# #bpy.ops.graph.select_more()
# #bpy.ops.graph.select_more()
# bpy.ops.graph.smooth() #uncomment this for fun!
# bpy.ops.graph.select_all(action='SELECT')
elif event.value == 'CLICK_DRAG':
self.mouse_path.append((event.mouse_region_x, event.mouse_region_y))
elif event.type == 'RELEASE':
bpy.types.SpaceGraphEditor.draw_handler_remove(self._handle, 'WINDOW')
return {'FINISHED'}
elif event.type in {'RIGHTMOUSE', 'ESC'}:
bpy.types.SpaceGraphEditor.draw_handler_remove(self._handle, 'WINDOW')
return {'CANCELLED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
# We must create a scene action first if there's none
scene = context.scene
if not scene.animation_data:
scene.animation_data_create()
if not scene.animation_data.action:
action = bpy.data.actions.new(scene.name + "Action")
scene.animation_data.action = action
sequence = context.scene.sequence_editor
scene = context.scene
strip = sequence.active_strip
#tmp_current_frame = context.scene.frame_current
animated_property = "volume" if hasattr(strip, "volume") else "blend_alpha"
fade_fcurve = fade_find_or_create_fcurve(context, strip, animated_property)
if context.area.type == 'GRAPH_EDITOR':
# 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.SpaceGraphEditor.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'}, "GRAPH_EDITOR not found, cannot run operator")
return {'CANCELLED'}
def fade_find_or_create_fcurve(context, sequence, animated_property):
"""
Iterates over all the fcurves until it finds an fcurve with a data path
that corresponds to the sequence.
Returns the matching FCurve or creates a new one if the function can't find a match.
"""
fade_fcurve = None
fcurves = context.scene.animation_data.action.fcurves
searched_data_path = sequence.path_from_id(animated_property)
for fcurve in fcurves:
if fcurve.data_path == searched_data_path:
fade_fcurve = fcurve
break
if not fade_fcurve:
fade_fcurve = fcurves.new(data_path=searched_data_path)
keyframe_points = fade_fcurve.keyframe_points
keyframe_points.insert(frame=0, value=1, options={"FAST"})
fade_fcurve.update()
# The graph editor and the audio waveforms only redraw upon "moving" a keyframe
keyframe_points[-1].co = keyframe_points[-1].co
return fade_fcurve
def register():
bpy.utils.register_class(ModalDrawOperator)
def unregister():
bpy.utils.unregister_class(ModalDrawOperator)
if __name__ == "__main__":
register()
#unregister
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment