HI experts,
Background
I wrote series of script (myself) a while ago that give me the option to add 2 track motion keyframes a certain time apart. The first key frame will always be the same value as the previous one and the second will reset to 0. This is really useful to me, as I do a lot of zooms and wanted to keep them standard.
So my workflow, (for example) is:
- Use the script that inserts 2 keyframes exactly 1 second apart
- Edit the zoom factor on the second KF that was placed by the previous step
- When I am ready to zoom back out, run the script again, which inserts 2 KFs, the first one will match the previous zoom factor, and the second one will reset the box to normal
This works great. But occasionally I want to change the values. So I decided I wanted a dockable panel that will sit in Vegas allow me to just click it to do the same job as above. But also, if required I can adjust the timing and settings.
I have never used dockable panels in my scripts before, so I admit I have been using Chat GPT here to help me. The basics that it did are fine. The panel works and comes up in the Extension menu etc.
But the scripting fails. (After hours of trying various things, I am stuck)
It will not allow me to actually insert the keyframes. The basic errors are along the lines of "Cannot open Track Motion"
Well, the project contains 2 files and here they are. The extension compiles with no errors. But it simply does not work when attempting to actually insert the keyframes.
Can anyone see where I am going wrong? Thank you in advance.
KeyframeHelperDock,cs:
using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Windows.Forms; using ScriptPortal.Vegas; public class KeyframeHelperDock : DockableControl { private Vegas _vegas => KeyframeHelperModule.VegasInstance; private NumericUpDown nudSeconds; private ComboBox cboType; private Button btnAdd; private readonly Dictionary<string, VideoKeyframeType> _typeMap = new Dictionary<string, VideoKeyframeType>(StringComparer.OrdinalIgnoreCase) { ["Linear"] = VideoKeyframeType.Linear, ["Hold"] = VideoKeyframeType.Hold, ["Slow"] = VideoKeyframeType.Slow, ["Fast"] = VideoKeyframeType.Fast, ["Smooth"] = VideoKeyframeType.Smooth, ["Sharp"] = VideoKeyframeType.Sharp, }; public KeyframeHelperDock() : base("KeyframeHelperDock") { Text = "Keyframe Helper"; BuildUI(); } public Guid PersistentID => new Guid("3F6B0C6E-0E7A-4E7C-B0F4-5B0C3B3B9F11"); private void BuildUI() { nudSeconds = new NumericUpDown { Minimum = 0, Maximum = 600, DecimalPlaces = 2, Increment = 0.10M, Value = 1.50M, Dock = DockStyle.Top }; cboType = new ComboBox { Dock = DockStyle.Top, DropDownStyle = ComboBoxStyle.DropDownList }; cboType.Items.AddRange(new object[] { "Linear", "Hold", "Slow", "Fast", "Smooth", "Sharp" }); cboType.SelectedIndex = 4; // Smooth btnAdd = new Button { Text = "Add Keyframes", Dock = DockStyle.Top }; btnAdd.Click += (s, e) => { var name = (string)cboType.SelectedItem; var type = _typeMap.TryGetValue(name, out var t) ? t : VideoKeyframeType.Linear; AddKeyframes((double)nudSeconds.Value, type); }; Controls.Add(btnAdd); Controls.Add(new Label { Text = "Keyframe Type:", Dock = DockStyle.Top }); Controls.Add(cboType); Controls.Add(new Label { Text = "Seconds:", Dock = DockStyle.Top }); Controls.Add(nudSeconds); Padding = new Padding(8); } private void AddKeyframes(double seconds, VideoKeyframeType type) { try { if (_vegas == null) { MessageBox.Show("VEGAS unavailable."); return; } if (_vegas.Project == null) { MessageBox.Show("No project open."); return; } // Select video track: prefer selected, else first VideoTrack videoTrack = null; foreach (Track t in _vegas.Project.Tracks) if (t.IsVideo() && t.Selected) { videoTrack = (VideoTrack)t; break; } if (videoTrack == null) videoTrack = _vegas.Project.Tracks.Count > 0 ? _vegas.Project.Tracks[0] as VideoTrack : null; if (videoTrack == null) { MessageBox.Show("No video track found."); return; } var cursor = _vegas.Transport.CursorPosition; // Acquire Track Motion (this is where COM exception happens in the dock) TrackMotion tm; try { tm = videoTrack.TrackMotion; } catch (COMException comx) { MessageBox.Show("Track Motion could not be opened on this track.\n\n" + comx.Message, "Track Motion Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } if (tm == null) { MessageBox.Show("Track Motion unavailable on this track."); return; } // --- Read previous KF values --- double prevX = 0, prevY = 0, prevW = 0, prevH = 0; foreach (TrackMotionKeyframe k in tm.MotionKeyframes) { if (k.Position <= cursor) { prevX = k.PositionX; prevY = k.PositionY; prevW = k.Width; prevH = k.Height; } else break; } // First keyframe var kf1 = tm.InsertMotionKeyframe(cursor); kf1.PositionX = prevX; kf1.PositionY = prevY; kf1.Width = prevW; kf1.Height = prevH; kf1.Type = type; // Second keyframe var later = cursor + Timecode.FromSeconds(seconds); var kf2 = tm.InsertMotionKeyframe(later); int vW = _vegas.Project.Video?.Width ?? 1920; int vH = _vegas.Project.Video?.Height ?? 1080; kf2.PositionX = 0; kf2.PositionY = 0; kf2.Width = vW; kf2.Height = vH; _vegas.Transport.CursorPosition = kf2.Position; } catch (Exception ex) { MessageBox.Show("Keyframe Helper error:\n\n" + ex, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } }
KeyframeHelperModule.cs
using System; using System.Collections; // VEGAS 22 uses non-generic ICollection using ScriptPortal.Vegas; public class KeyframeHelperModule : ICustomCommandModule { internal static Vegas VegasInstance; public void InitializeModule(Vegas vegas) { VegasInstance = vegas; } public ICollection GetCustomCommands() { var cmd = new CustomCommand(CommandCategory.Tools, "KeyframeHelper"); cmd.DisplayName = "Keyframe Helper"; cmd.Invoked += (s, e) => { VegasInstance.LoadDockView(new KeyframeHelperDock()); }; return new CustomCommand[] { cmd }; } }