Building GUIs With Fusion's UI Manager

User avatar
AndrewHazelden
Fusionator
Posts: 1386
Joined: Fri Apr 03, 2015 3:20 pm
Answers: 4
Location: West Dover, Nova Scotia, Canada
Been thanked: 59 times
Contact:

Button with Icon

#46

Post by AndrewHazelden » Thu Sep 28, 2017 8:11 am

Overview

This script is a Fusion Lua based example that works in Fusion 9.0.1+ that shows how you can attach an image to a ui:Button with a ui:Icon resource.

The "Button with Icon Checked.lua" version of the script shows buttons that can be toggled on/off.

Download
Button-with-Icon.zip
Screenshot
button-with-icon.png
Installation

Step 1. Create a new "UI Manager" folder inside the Fusion user preferences "Scripts/Comp/" folder.

Step 2. Copy the "Button with Icon.lua" and "Button with Icon Checked.lua" scripts and the "fusion-logo.png" image into the "UI Manager" folder.

Step 3. In Fusion select the "Script > UI Manager > Button with Icon" menu item.

Notes

If you want to make a button that can be toggled and left visibly in a Pressed/Unpressed state, that is done with the addition of a "Checkable = true," entry when you create the ui:Button like this:
checked-button-state.png

Code: Select all

ui:Button{
  ID = 'IconButton1', 
  Flat = true,
  IconSize = {64,64},
  Icon = ui:Icon{File = 'Scripts:/Comp/UI Manager/fusion-logo.png'},
  Checkable = true,
},
You do not have the required permissions to view the files attached to this post.

Tags:

User avatar
theotheo
Fusionista
Posts: 312
Joined: Thu Aug 07, 2014 8:35 am
Answers: 2
Been thanked: 20 times

Re: Building GUIs With Fusion's UI Manager

#47

Post by theotheo » Fri Sep 29, 2017 10:58 am

Lots of really really useful stuff in here Andrew, thanks a bunch!

User avatar
AndrewHazelden
Fusionator
Posts: 1386
Joined: Fri Apr 03, 2015 3:20 pm
Answers: 4
Location: West Dover, Nova Scotia, Canada
Been thanked: 59 times
Contact:

Dynamic Hotkeys

#48

Post by AndrewHazelden » Sat Sep 30, 2017 3:29 am

Overview
This script is a Fusion Lua based UI Manager example that works in Fusion 9.0.1. The app:AddConfig() command will capture the "Control + W" / "Control + F4" hotkeys on Windows/Linux, or the "Command + W" / "Command + F4" hotkeys on Mac so they will close the "Dynamic Hotkeys" window instead of closing the foreground composite.

Download
Dynamic Hotkeys.lua
Screenshot
dynamic-hotkeys.png
Installation
Copy the "Dynamic Hotkeys.lua" script into your Fusion user preferences "Scripts/Comp/" folder.

Usage
In Fusion you can then run the script from inside Fusion's GUI by selecting the "Script > Dynamic Hotkeys" item.
You do not have the required permissions to view the files attached to this post.

User avatar
AndrewHazelden
Fusionator
Posts: 1386
Joined: Fri Apr 03, 2015 3:20 pm
Answers: 4
Location: West Dover, Nova Scotia, Canada
Been thanked: 59 times
Contact:

OFX Blacklist Generator

#49

Post by AndrewHazelden » Sat Sep 30, 2017 7:48 am

Overview
This script is a Fusion Lua based UI Manager example that works in Fusion 9.0.1.

The tool scans the OFX Plugins directory and returns a list of all of the plugin libraries found. This list is used to quickly create a FusionOFX.blacklist file that lets Fusion know it should skip loading the OFX plugins at startup.

The OFX blacklist file is stored at: "Profile:/FusionOFX.blacklist".

WARNING: If you are manually creating an FusionOFX.blacklist file by hand you need to have an extra newline character added to the end of the document. This means if you are only adding a single OFX module to the blacklist document you will need to add an extra blank line to the end of the textfile! Failure to do this will cause the FusionOFX.blacklist file entry to be ignored.

Download
Version 1.1 - 2017-11-28
OFX Blacklist Generator.lua
Screenshot
OFX Blacklist Generator.png
Installation
Copy the "OFX Blacklist Generator.lua" script into your Fusion user preferences "Scripts:/Comp/" folder.

Usage
You can run the script from inside Fusion's GUI by selecting the "Script > OFX Blacklist Generator" item.

You need to manually delete the OFX plugins you want to load in Fusion from the "OFX Directory Contents:" section. Then click the "Save Blacklist" button.

OFX Plugin Folder Location

Windows:
C:\Program Files\Common Files\OFX\Plugins\

Mac:
/Library/OFX/Plugins/

Linux:
/usr/OFX/Plugins/
You do not have the required permissions to view the files attached to this post.

User avatar
AndrewHazelden
Fusionator
Posts: 1386
Joined: Fri Apr 03, 2015 3:20 pm
Answers: 4
Location: West Dover, Nova Scotia, Canada
Been thanked: 59 times
Contact:

Action Listener

#50

Post by AndrewHazelden » Sat Sep 30, 2017 4:03 pm

Overview
Action Listener v2 - Last Updated 2019-10-01

The Action Listener script uses the Fusion ActionManager and ui:AddNotify() functions to log events as they happen inside the Fusion GUI. Only a small percentage of the compositing tasks you carry out in Fusion will show up in the "Recorded Action Log" view since the window is only able to track tools and commands that are applied using the new "action" system.

This script makes use of the fact Lua is a dynamic programming language by creating new functions for handling each ui:AddNotify event on the fly. This script is a Fusion Lua based UI Manager example that works in Fusion 9+ and Resolve 15+.

What's new in V2?

Version 2 of Action listener now tracks "Fusion" scope actions, in addition to the previous "comp" scope actions. This means creating a new comps, saving comps, and other tasks are captured more accurately now. Also, "comp:DoAction()" is used for translating generic actions.

Download

Code: [Select all] [Expand/Collapse] [Download] (Action Listener.lua)
  1. --[[
  2. Action Listener v2 - 2019-10-01
  3. by Andrew Hazelden <andrew@andrewhazelden.com>
  4. www.andrewhazelden.com
  5.  
  6. ## Overview ##
  7.  
  8. The Action Listener script uses the Fusion ActionManager and ui:AddNotify() functions to log events as they happen inside the Fusion GUI. Only a small percentage of the compositing tasks you carry out in Fusion will show up in the "Recorded Action Log" view since the window is only able to track tools and commands that are applied using the new "action" system.
  9.  
  10. This script makes use of the fact Lua is a dynamic programming language by creating new functions for handling each ui:AddNotify event on the fly. This script is a Fusion Lua based UI Manager example that works in Fusion 9+ and Resolve 15+
  11.  
  12. Version 2 of Action listener now tracks "Fusion" scope actions, in addition to the previous "comp" scope actions. This means creating new comps, saving comps, and other tasks are captured more accurately now. Also, "comp:DoAction()" is used for translating generic actions.
  13.  
  14. ## Installation ##
  15.  
  16. Copy the "Action Listener.lua" script into your Fusion user preferences "Scripts/Comp/" folder.
  17.  
  18. Copy the "Action Listener.fu" hotkey file into your Fusion user preferences "Config/" folder.
  19.  
  20. ## Usage ##
  21.  
  22. In Fusion, you can then run the script from inside Fusion's GUI by selecting the "Script > Action Listener" item.
  23.  
  24. Start adding nodes and carrying out compositing tasks to see the Recorded Action Log update and scroll.
  25.  
  26. If you install the "Action Listenr.fu" hotkey file you can use the (Command + R) or (Control + R) hotkey to open the "Action Listener" window.
  27.  
  28.  
  29. ## Version History ##
  30.  
  31. v1.3 - 2017-11-21
  32. - Action Listener WSL "Reactor Edition"
  33.  
  34. v2.0 - 2019-10-01
  35. - Added Fusion scope action listening to AddNotify, which massively expands the amount of actions reported by the "Action Listener" script compared to what the previous composite scope only action listening approach was able to output.
  36. - Added comp:DoAction() translation of generic actions to make the snippets runnable.
  37.  
  38. ## GUI Controls ##
  39.  
  40. ### Button Controls ###
  41.  
  42. The "Print Actions List" button will output a list of all the actions that are present in Fusion to the Console tab.
  43.  
  44. The "Clear Event and Actions Logs" button will clear the text fields in the Action Listener GUI and give you a fresh recording.
  45.  
  46. ### Checkbox Controls
  47.  
  48. The "Translate Actions to Lua" checkbox tries to convert the action commands to the nearest Lua approximation.
  49.  
  50. The "Print Action Log to Console" checkbox will print a running log of the Action and event logs to the Console tabs. When the "ev.Args.tool", "ev.Args.prev", "ev.Rets.tool", and "ev.sender" table items are printed to the console they will have their pointer values probed using :GetAttrs() to print out the extra details.
  51.  
  52. The "Track Elapsed Time" checkbox adds a pause that will match the speed of the original recorded actions.
  53.  
  54. ## Actions List ##
  55.  
  56. Here is a list of the typical Lua translated actions you would see in Fusion 16.1 with Reactor installed:
  57.  
  58. ACTION_CLOSE_COMP
  59. ACTION_GET_TEMPLATES_LIST
  60. ACTION_GET_UI_LAYOUT
  61. ACTION_PRINT_CONSOLE
  62. ACTION_SET_UI_LAYOUT
  63. ACTION_SHOW_CONSOLE
  64. AddLUT
  65. AddSetting
  66. AddTool
  67. App_About
  68. App_Copy
  69. App_CustomizeHotkeys
  70. App_CustomizeToolBars
  71. App_Cut
  72. App_Delete
  73. App_DeselectAll
  74. App_Exit
  75. App_Help
  76. App_NewImageView
  77. App_OnlyActiveComp
  78. App_Paste
  79. App_PasteSettings
  80. App_SelectAll
  81. App_ShowUI
  82. Bin_Icon_Size
  83. Bin_New_Folder
  84. Bin_New_Item
  85. Bin_New_Reel
  86. Bin_Show_Checker
  87. Bin_View_Mode
  88. Bins_Delete
  89. Bins_Mode_Exit
  90. Bins_Play
  91. Bins_Refresh
  92. Bins_Rename
  93. Bins_SelectAll
  94. Bins_Stop
  95. Close_Comps_To_Left
  96. Close_Comps_To_Right
  97. Close_Other_Comps
  98. Comp_Abort
  99. Comp_Activate_Tool
  100. Comp_Auto_Proxy
  101. Comp_BackgroundRender
  102. Comp_Choose_Action
  103. Comp_Choose_Tool
  104. Comp_Close
  105. Comp_High_Quality
  106. Comp_Motion_Blur
  107. Comp_New
  108. Comp_NewFloatFrame
  109. Comp_NewTabbedFrame
  110. Comp_Open
  111. Comp_Opened
  112. Comp_Play_Loop
  113. Comp_Play_PingPong
  114. Comp_Proxy
  115. Comp_Recent_Clear
  116. Comp_Recent_Open
  117. Comp_Redo
  118. Comp_Render
  119. Comp_Render_End
  120. Comp_Render_Frame
  121. Comp_Reset
  122. Comp_Save
  123. Comp_SaveAs
  124. Comp_SaveCopyAs
  125. Comp_SaveVersion
  126. Comp_ShowTimeCode
  127. Comp_Start_Render
  128. Comp_StartRender
  129. Comp_TimeCodeFormat
  130. Comp_Undo
  131. Console_Show
  132. Drag_Drop
  133. Execute
  134. Frame_Activate_Frame
  135. Frame_Activate_Next
  136. Frame_Activate_Prev
  137. Frame_Activate_SubWnd
  138. Fusion_Screen_Mode
  139. Fusion_Time_Get
  140. Fusion_Time_Set
  141. Fusion_View_Show
  142. Fusion_Zone_Expand
  143. GraphView_SortMenu
  144. GraphView_ZoomToFit
  145. GraphView_ZoomToRect
  146. GraphView_ZoomX
  147. GraphView_ZoomY
  148. KeyframeView_ZoomY
  149. Layout_Load
  150. Layout_Reset
  151. Layout_Save
  152. Layout_Switch
  153. LifeSaverVersionDown
  154. LifeSaverVersionUp
  155. NetRender_Allow
  156. No_Action
  157. Open_A_Copy
  158. Open_All_Comps_In_Script_Editor
  159. Open_Comp_in_Script_Editor
  160. Playback_Mode
  161. Playback_Seek
  162. Playback_Seek_End
  163. Playback_Seek_Start
  164. Player_Channel
  165. Player_Device_DeckLink
  166. Player_Gain
  167. Player_Gamma
  168. Player_Guide_Enable
  169. Player_Guide_Select
  170. Player_Item_Next
  171. Player_Item_Prev
  172. Player_Loop_Reset
  173. Player_Loop_Set_In
  174. Player_Loop_Set_Out
  175. Player_Loop_Set_Shot
  176. Player_Play
  177. Player_Play_Forward
  178. Player_Play_Reverse
  179. Player_Seek_By
  180. Player_Seek_End
  181. Player_Seek_Next
  182. Player_Seek_Prev
  183. Player_Seek_Start
  184. Player_Seek_To
  185. Player_Set_FPS
  186. Player_Set_Loop
  187. Player_Set_Time
  188. Player_Show_Metadata
  189. Player_Sync_Mode
  190. Player_Trim_Exit
  191. Player_Trim_Set_In
  192. Player_Trim_Set_Out
  193. Prefs_Show
  194. Reactor_About
  195. Reactor_Atomizer
  196. Reactor_Comps_Folder
  197. Reactor_Config_Folder
  198. Reactor_Docs_Folder
  199. Reactor_Folder
  200. Reactor_Fuse_Scanner
  201. Reactor_Macro_Scanner
  202. Reactor_Open
  203. Reactor_Open_Reactor_Log
  204. Reactor_Plugin_Scanner
  205. Reactor_Prefs
  206. Reactor_Reinstall_Reactor
  207. Reactor_Resync
  208. Reactor_Temp_Folder
  209. Reel_Delete_Selected
  210. Reel_Delete_Selected
  211. Resources_Bug_Tracker
  212. Resources_Online_Discussion
  213. Resources_Reactor_Online_Repository
  214. Resources_WSL
  215. Restart_Fusion
  216. RunScript
  217. Script_Edit
  218. Target_Show_Menu
  219. Target_Show_Menu
  220. Target_Show_Scripts
  221. Time_Goto_GlobalEnd
  222. Time_Goto_GlobalStart
  223. Time_Goto_Key_Next
  224. Time_Goto_Key_Prev
  225. Time_Goto_RenderEnd
  226. Time_Goto_RenderStart
  227. Time_Set
  228. Time_Step_Back
  229. Time_Step_Forward
  230. Time_Step_NextKey
  231. Time_Step_PrevKey
  232. Tool_Settings_Activate
  233. Tool_Settings_Store
  234. Tool_ViewClear
  235. Tool_ViewOn
  236. Utility_Show
  237. View_Pan_Mode
  238. View_Reset
  239. View_Show
  240. View_Zoom_Absolute
  241. View_Zoom_Fit
  242. View_Zoom_In
  243. View_Zoom_Mode
  244. View_Zoom_Out
  245. View_Zoom_Rectangle
  246. View_Zoom_Relative
  247. Viewer_3D_CentreSelected
  248. Viewer_3D_FitAll
  249. Viewer_3D_FitSelected
  250. Viewer_3D_Lighting
  251. Viewer_3D_Lights
  252. Viewer_3D_Shadows
  253. Viewer_3D_Solid
  254. Viewer_3D_Transparency
  255. Viewer_3D_Wireframe
  256. Viewer_Buffer
  257. Viewer_Channel
  258. Viewer_Checker_Enable
  259. Viewer_Controls_Show
  260. Viewer_DOD_Enable
  261. Viewer_Guides_Show
  262. Viewer_Lock
  263. Viewer_LUT_Edit
  264. Viewer_LUT_Enable
  265. Viewer_QuadView
  266. Viewer_Reset
  267. Viewer_ROI_Auto
  268. Viewer_ROI_Enable
  269. Viewer_ROI_Lock
  270. Viewer_ROI_Reset
  271. Viewer_ROI_Set
  272. Viewer_Scale_Abs
  273. Viewer_Scale_Rel
  274. Viewer_Show_GainGamma
  275. Viewer_SubView_Enable
  276. Viewer_SubView_Swap
  277. Viewer_Tools_Disable
  278. Viewer_Unview_All
  279.  
  280. ]]
  281.  
  282. -- ----------------------------------------------------------------------------
  283. -- ----------------------------------------------------------------------------
  284. print('[Action Listener]')
  285.  
  286. -- Notification tracking table
  287. notify = {}
  288. notifyCount = 1
  289.  
  290. local trackTime = true
  291. local prev_when = 0
  292.  
  293. -- Convert a table to a string
  294. -- Example: TableToString({})
  295. -- https://stackoverflow.com/questions/9168058/how-to-dump-a-table-to-console
  296. function TableToString(tbl)
  297.     if type(tbl) == 'table' then
  298.         local str = '\n'
  299.        
  300.         for i,val in pairs(tbl) do
  301.             if i == 1 then
  302.                 str = str .. 'table: ' .. TableToString(val) .. '\n'
  303.             else
  304.                 str = str .. '  ' .. i .. ' = ' .. TableToString(val) .. '\n'
  305.             end
  306.         end
  307.        
  308.         return str
  309.     else
  310.         return tostring(tbl)
  311.     end
  312. end
  313.  
  314. -- Check the current operating system platform
  315. local platform = (FuPLATFORM_WINDOWS and 'Windows') or (FuPLATFORM_MAC and 'Mac') or (FuPLATFORM_LINUX and 'Linux')
  316.  
  317. -- Create the UI Manager GUI
  318. local ui = fu.UIManager
  319. local disp = bmd.UIDispatcher(ui)
  320. local width,height = 1200,700
  321.  
  322. win = disp:AddWindow({
  323.     ID = 'ActionListenerWin',
  324.     TargetID = 'ActionListenerWin',
  325.     WindowTitle = 'Fusion Action Listener',
  326.     Geometry = {0, 100, width, height},
  327.     Margin = 10,
  328.     Spacing = 0,
  329.  
  330.     ui:VGroup{
  331.         ID = 'root',
  332.  
  333.         -- Add your GUI elements here:
  334.  
  335.         -- Recorded Action section
  336.         ui:Label{
  337.             ID = 'LogLabel',
  338.             Weight = 0,
  339.             Text = 'Recorded Action Log:',
  340.         },
  341.         ui:TextEdit{
  342.             ID = 'LogTextEdit',
  343.             Weight = 0.75,
  344.             PlaceholderText = 'Action Listener records the actions that happen in a Fusion compositing session and displays the results in an event log.',
  345.             Font = ui:Font{
  346.                 Family = 'Droid Sans Mono',
  347.                 StyleName = 'Regular',
  348.                 PixelSize = 12,
  349.                 MonoSpaced = true,
  350.                 StyleStrategy = {ForceIntegerMetrics = true},
  351.             },
  352.             TabStopWidth = 28,
  353.             LineWrapMode = 'NoWrap',
  354.             AcceptRichText = false,
  355.             ReadOnly = true,
  356.             -- Use the Fusion hybrid lexer module to add syntax highlighting
  357.             Lexer = 'fusion',
  358.         },
  359.  
  360.         -- Add a the horizontal strip of checkbox controls
  361.         ui:HGroup{
  362.             Weight = 0.05,
  363.             ui:CheckBox{
  364.                 ID = 'TranslateToLuaCheckbox',
  365.                 Weight = 0.5,
  366.                 Text = 'Translate Actions to Lua',
  367.                 Checked = true,
  368.             },
  369.             ui:CheckBox{
  370.                 ID = 'PrintToConsoleCheckbox',
  371.                 Weight = 0.5,
  372.                 Text = 'Print Action Log to Console',
  373.                 Checked = true,
  374.             },
  375.             ui:CheckBox{
  376.                 ID = 'TrackElapsedTimeCheckbox',
  377.                 Weight = 0.5,
  378.                 Text = 'Track Elapsed Time',
  379.                 Checked = false,
  380.             },
  381.         },
  382.  
  383.         -- Event section
  384.         ui:Label{
  385.             ID = 'LastEventLabel',
  386.             Weight = 0,
  387.             Text = 'Last Event:',
  388.         },
  389.         ui:TextEdit{
  390.             ID = 'EventTextEdit',
  391.             Weight = 0.4,
  392.             PlaceholderText = 'This section shows results from the last event.',
  393.             Font = ui:Font{
  394.                 Family = 'Droid Sans Mono',
  395.                 StyleName = 'Regular',
  396.                 PixelSize = 12,
  397.                 MonoSpaced = true,
  398.                 StyleStrategy = {ForceIntegerMetrics = true},
  399.             },
  400.             TabStopWidth = 28,
  401.             LineWrapMode = 'NoWrap',
  402.             AcceptRichText = false,
  403.             ReadOnly = true,
  404.            
  405.             -- Use the Fusion hybrid lexer module to add syntax highlighting
  406.             Lexer = 'fusion',
  407.         },
  408.  
  409.         -- Clear Event Log Button
  410.         ui:HGroup{
  411.             Weight = 0.1,
  412.             ui:Button{
  413.                 Weight = 0.01,
  414.                 ID = 'PrintActionsListButton',
  415.                 Text = 'Print Actions List',
  416.             },
  417.             ui:Button{
  418.                 Weight = 0.001,
  419.                 ID = 'ClearLogButton',
  420.                 Text = 'Clear Event and Action Logs',
  421.             },
  422.         },
  423.     },
  424. })
  425.  
  426.  
  427. -- Add your GUI element based event functions here:
  428. itm = win:GetItems()
  429.  
  430.  
  431. -- The window was closed
  432. function win.On.ActionListenerWin.Close(ev)
  433.     disp:ExitLoop()
  434. end
  435.  
  436.  
  437. -- Print a copy of the actions list to the Console
  438. function win.On.PrintActionsListButton.Clicked(ev)
  439.     -- Track the actions that are available in Fusion
  440.     local actionList = fu.ActionManager:GetActions()
  441.  
  442.     -- Count the total number of actions
  443.     actionCount = 0
  444.     for i, act in ipairs(actionList) do
  445.         if not act:Get('Parent') then
  446.             actionCount = actionCount + 1
  447.         end
  448.     end
  449.     print('[' .. actionCount .. ' Actions Found]')
  450.  
  451.     -- List each action sequentially
  452.     for i, act in ipairs(actionList) do
  453.         if not act:Get('Parent') then
  454.             print(act.ID)
  455.         end
  456.     end
  457. end
  458.  
  459.  
  460. -- The Clear Event Log Button was pressed
  461. function win.On.ClearLogButton.Clicked(ev)
  462.     itm.LogTextEdit.PlainText = ' '
  463.     itm.EventTextEdit.PlainText = ' '
  464. end
  465.  
  466.  
  467. -- Add a new function for each AddNotify event
  468. function ProcessAction(a, win)
  469.     -- Track all scopes of the actions (app, comp, etc...)
  470.     notify[notifyCount] = ui:AddNotify(a.ID, nil)
  471.     notifyCount = notifyCount + 1
  472.  
  473.     print('[AddNotify] ' .. a.ID)
  474.  
  475.     disp.On[a.ID] = function(ev)
  476.     -- win.On[a.ID] = function(ev)
  477.  
  478.     -- List the event that happened
  479.     what = tostring(ev.what)
  480.     when = tonumber(ev.when)
  481.  
  482.     -- Args
  483.     argsId = tostring(ev.Args.id)
  484.     argsTime = tostring(ev.Args.time)
  485.     argsTool = tostring(ev.Args.tool)
  486.     if argsTool ~= 'nil' then
  487.         argsToolName = tostring(ev.Args.tool:GetAttrs('TOOLS_Name'))
  488.         argsToolType = tostring(ev.Args.tool:GetAttrs('TOOLS_RegID'))
  489.     else
  490.         argsToolName = 'nil'
  491.         argsToolType = 'nil'
  492.     end
  493.  
  494.     -- Rets
  495.     retsTool = tostring(ev.Rets.tool)
  496.     if retsTool ~= 'nil' then
  497.         retsToolName = tostring(ev.Rets.tool:GetAttrs('TOOLS_Name') or '')
  498.     else
  499.         retsToolName = 'nil'
  500.     end
  501.  
  502.     -- Sender
  503.     sender = tostring(ev.sender)
  504.     if sender ~= 'nil' then
  505.         senderCompFilename = tostring(ev.sender:GetAttrs('COMPS_FileName') or '')
  506.         -- Debug print the sender table
  507.         -- dump(ev.sender:GetAttrs())
  508.     else
  509.         senderCompFilename = 'nil'
  510.     end
  511.  
  512.     -- Elapsed time
  513.     if prev_when == 0 then
  514.         prev_when = when
  515.     end
  516.     elapsed = when - prev_when
  517.  
  518.     local luaCommand = ''
  519.     local luaElapsed = 'bmd.wait(' .. elapsed .. ') -- Pause'
  520.  
  521.     -- Translate the Action events back into Lua commands
  522.     if itm.TranslateToLuaCheckbox.Checked then
  523.         if what == 'AddTool' then
  524.             -- Add a node to the comp
  525.             -- The magic coordinate values "-32768, -32768" mean Fusion will automatically place the node at the right spot relative to the current selection
  526.             luaCommand = 'AddTool("' .. argsId .. '", -32768, -32768) -- Add a ' .. argsId .. ' node to the comp called "' .. retsToolName .. '"'
  527.             -- luaCommand = 'AddTool("' .. argsId .. '")'
  528.         elseif what == 'App_About' then
  529.             -- About Fusion Dialog
  530.             luaCommand = 'app:ShowAbout() -- About Fusion dialog'
  531.         elseif what == 'AddSetting' then
  532.             -- Add a macro to the comp
  533.             luaCommand = 'comp:Paste(bmd.readfile(comp:MapPath("' .. tostring(ev.Args.filename) .. '"))) -- Add a macro .setting file to the comp'
  534.         elseif what == 'Time_Set' then
  535.             -- Move the playhead
  536.             luaCommand = 'comp.CurrentTime = ' .. argsTime .. ' -- Move the playhead'
  537.         elseif what == 'Time_Step_Forward' then
  538.             -- Step the playhead forward by 1 frame
  539.             luaCommand = 'comp.CurrentTime = comp.CurrentTime + 1 -- Step the playhead forward by 1 frame'
  540.         elseif what == 'Time_Step_Back' then
  541.             -- Step the playhead back by 1 frame
  542.             luaCommand = 'comp.CurrentTime = comp.CurrentTime - 1 -- Step the playhead back by 1 frame'
  543.         elseif what == 'Time_Goto_GlobalStart' then
  544.             -- Move the playhead to the GlobalStart
  545.             luaCommand = 'comp.CurrentTime = composition:GetAttrs().COMPN_GlobalStart -- Move the playhead to the GlobalStart'
  546.         elseif what == 'Time_Goto_GlobalEnd' then
  547.             -- Move the playhead to the GlobalEnd
  548.             luaCommand = 'comp.CurrentTime = composition:GetAttrs().COMPN_GlobalEnd -- Move the playhead to the GlobalEnd'
  549.         elseif what == 'Time_Goto_RenderStart' then
  550.             -- Move the playhead to the RenderStart
  551.             luaCommand = 'comp.CurrentTime = composition:GetAttrs().COMPN_RenderStart -- Move the playhead to the RenderStart'
  552.         elseif what == 'Time_Goto_RenderEnd' then
  553.             -- Move the playhead to the RenderEnd
  554.             luaCommand = 'comp.CurrentTime = composition:GetAttrs().COMPN_RenderEnd -- Move the playhead to the RenderEnd'
  555.         elseif what == 'Comp_Abort' then
  556.             -- The Esc key was press to abort the current render
  557.             luaCommand = 'comp:AbortRender() -- The Esc key was press to abort the current render'
  558.         elseif what == 'Comp_Activate_Tool' then
  559.             if argsToolType ~= 'nil' and argsToolName ~= 'nil' and argsTool ~= 'nil' then
  560.                 -- Select a node
  561.                 luaCommand = 'comp:SetActiveTool(' .. tostring(argsToolName) .. ') -- Selected a ' .. tostring(argsToolType) .. ' node called "' .. tostring(argsToolName) .. '"'
  562.                 -- luaCommand = 'comp.CurrentFrame.FlowView:Select("' .. argsId .. '")'
  563.             else
  564.                 -- Deselect all nodes
  565.                 luaCommand = 'comp.CurrentFrame.FlowView:Select() -- Deselect the nodes'
  566.             end
  567.         elseif what == 'Comp_Start_Render' then
  568.             -- Start a batch sequence rendering task
  569.             luaCommand = 'comp:Render() -- Start a batch sequence rendering task'
  570.         elseif what == 'Comp_Render_Frame' then
  571.             -- A frame is being rendered to disk as part of a batch sequence rendering task
  572.             luaCommand = 'comp:Render({ FrameRange = "' .. argsTime .. '", Wait = true }) -- Render a frame to disk'
  573.         elseif what == 'Comp_Undo' then
  574.             -- The Undo command
  575.             luaCommand = 'comp:Undo(1) -- Undo'
  576.         elseif what == 'Comp_Redo' then
  577.             -- The Redo command
  578.             luaCommand = 'comp:Redo(1) -- Redo'
  579.         elseif what == 'Comp_Save' then
  580.             -- The Save command
  581.             luaCommand = 'comp:Save("' .. senderCompFilename .. '") -- Saved the comp file "' .. senderCompFilename .. '"'
  582.         elseif what == 'Comp_SaveAs' then
  583.             -- The SaveAs command
  584.             luaCommand = 'comp:SaveAs() -- Saved the comp file as "' .. senderCompFilename .. '"'
  585.         elseif what == 'Comp_SaveCopyAs' then
  586.             -- The SaveCopyAs command
  587.             luaCommand = 'comp:SaveCopyAs() -- Saved a copy of the comp file'
  588.         elseif what == 'Comp_SaveVersion' then
  589.             -- The SaveAs command
  590.             luaCommand = 'comp:SaveVersion() -- Saved a version of the comp with the name of "' .. senderCompFilename .. '"'
  591.         elseif what == 'Execute' then
  592.             -- The execute command runs a snippet of script code from a variable
  593.             luaCommand = 'comp:Execute() -- Run a snippet of script code from a variable'
  594.         elseif what == 'RunScript' then
  595.             -- The RunScript command runs a script from disk
  596.             luaCommand = 'comp:RunScript("' .. tostring(ev.Args.filename) .. '") -- Run a script from disk'
  597.         elseif what == 'Viewer_Lock' then
  598.             -- The viewer window lock command
  599.             -- Will require checking the actual right or left viewer context with something like:
  600.             -- left = comp:GetPreviewList().Left.View.SetLocked()
  601.             luaCommand = 'self:SetLocked() -- Lock the viewer'
  602.         elseif what == 'Viewer_3D_FitAll' then
  603.             -- Fit the 3D view to all
  604.             -- Will require checking the actual right or left viewer context with something like:
  605.             -- left = comp:GetPreviewList().Left.View.SetLocked()
  606.             luaCommand = 'self:FitAll() -- Fit the 3D View to All'
  607.         elseif what == 'Viewer_3D_CentreSelected' then
  608.             -- Center the 3D View
  609.             -- Will require checking the actual right or left viewer context with something like:
  610.             -- left = comp:GetPreviewList().Left.View.SetLocked()
  611.             luaCommand = 'self:CenterSelected() -- Center the 3D View'
  612.         elseif what == 'Viewer_3D_FitSelected' then
  613.             -- Fit the 3D view to selected
  614.             -- Will require checking the actual right or left viewer context with something like:
  615.             -- left = comp:GetPreviewList().Left.View.SetLocked()
  616.             luaCommand = 'self:FitSelected() -- Fit the 3D view to selected'
  617.         elseif what == 'Fusion_View_Show' then
  618.             -- View layout change
  619.             if ev.Rets and ev.Rets.state ~= nil then
  620.                 local viewState = ev.Rets.state and "show" or "Hide"
  621.                 luaCommand = 'comp:DoAction("Fusion_View_Show", {view = "' .. tostring(ev.Args.view) .. '"}) -- ' .. tostring(viewState) .. ' the "' .. tostring(ev.Args.view) .. '" view'
  622.             else
  623.                 luaCommand = 'comp:DoAction("Fusion_View_Show", {view = "' .. tostring(ev.Args.view) .. '"})'
  624.             end
  625.         elseif what == 'Console_Show' then
  626.             -- Console window change
  627.             luaCommand = 'comp:DoAction("Console_Show", {}) -- Toggle the display of the Console view'
  628.         elseif what == 'Playback_Mode' then
  629.             -- Play the sequence
  630.            
  631.             -- Check if play in reverse is active
  632.             if ev.Args.play == false then
  633.                 -- Stop playing the sequence
  634.                 luaCommand = 'comp:Stop() -- Stop playing the sequence'
  635.             else
  636.                 -- Play the sequence forwards
  637.                 if tostring(ev.Args.reverse) ~= 'nil' then
  638.                     luaCommand = 'comp:Play(' .. tostring(ev.Args.reverse) .. ') -- Play the sequence in reverse'
  639.                 else
  640.                     luaCommand = 'comp:Play() -- Play the sequence forwards'
  641.                 end
  642.             end
  643.         else
  644.             -- Add the action record to the "Last Event" Log
  645.             if argsId == 'nil' then
  646.                  luaCommand = 'comp:DoAction("' .. what .. '", {})'
  647.             else
  648.                 luaCommand = 'comp:DoAction("' .. what .. '", {}) -- ' .. what .. '("' .. argsId .. '")'
  649.             end
  650.         end
  651.     else
  652.         -- Lua translation is off - Add the action record to the "Last Event" Log
  653.         if argsId == 'nil' then
  654.              luaCommand = what .. '()'
  655.         else
  656.             luaCommand = what .. '("' .. argsId .. '")'
  657.         end
  658.     end
  659.    
  660.     -- Should time be tracked in the logging window
  661.     --if trackTime == true then
  662.     if itm.TrackElapsedTimeCheckbox.Checked then
  663.         logEntry = luaElapsed .. '\n' .. luaCommand
  664.     else
  665.         logEntry = luaCommand
  666.     end
  667.    
  668.     -- Append the record to the event log
  669.     itm.LogTextEdit.PlainText = logEntry .. '\n' .. itm.LogTextEdit.PlainText
  670.    
  671.     -- List the raw event table
  672.     itm.EventTextEdit.PlainText = TableToString(ev)
  673.    
  674.     -- Print Action Log to Console
  675.     if itm.PrintToConsoleCheckbox.Checked then
  676.         print('[Action] ' .. luaCommand)
  677.         print('[Event]')
  678.         dump(ev)
  679.        
  680.         if ev.Args.tool ~= nil then
  681.             print('[ev.Args.tool]')
  682.             dump(ev.Args.tool:GetAttrs())
  683.         end
  684.        
  685.         if ev.Args.prev ~= nil then
  686.             print('[ev.Args.prev]')
  687.             dump(ev.Args.prev:GetAttrs())
  688.         end
  689.        
  690.         if ev.Rets.tool ~= nil then
  691.             print('[ev.Rets.tool]')
  692.             dump(ev.Rets.tool:GetAttrs())
  693.         end
  694.        
  695.         if ev.sender ~= nil then
  696.             print('[ev.sender]')
  697.             dump(ev.sender:GetAttrs())
  698.         end
  699.        
  700.         print('-------------------------------------------------------------------------------')
  701.     end
  702.    
  703.     -- Track the time interval between actions
  704.     prev_when = when
  705.     end
  706. end
  707.  
  708.  
  709. -- The main function
  710. function Main()
  711.     -- Track the actions that are available in Fusion
  712.     local actionList = fu.ActionManager:GetActions()
  713.     for i, act in ipairs(actionList) do
  714.         if not act:Get('Parent') then
  715.             -- Add a new AddNotify event for each action found
  716.             ProcessAction(act, win)
  717.         end
  718.     end
  719.  
  720.     -- The app:AddConfig() command that will capture the "Control + W" or "Control + F4" hotkeys so they will close the window instead of closing the foreground composite.
  721.     app:AddConfig('ActionListener', {
  722.         Target {
  723.             ID = 'ActionListenerWin',
  724.         },
  725.  
  726.         Hotkeys {
  727.             Target = 'ActionListenerWin',
  728.             Defaults = true,
  729.            
  730.             CONTROL_W = 'Execute{cmd = [[app.UIManager:QueueEvent(obj, "Close", {})]]}',
  731.             CONTROL_F4 = 'Execute{cmd = [[app.UIManager:QueueEvent(obj, "Close", {})]]}',
  732.         },
  733.     })
  734.  
  735.     -- Adjust the syntax highlighting colors
  736.     bgcol = {
  737.         R = 0.125,
  738.         G = 0.125,
  739.         B = 0.125,
  740.         A = 1
  741.     }
  742.  
  743.     -- Set the Log text region background color
  744.     itm.LogTextEdit.BackgroundColor = bgcol
  745.     itm.LogTextEdit:SetPaletteColor('All', 'Base', bgcol)
  746.  
  747.     -- Set the event text region backgrouond color
  748.     itm.EventTextEdit.BackgroundColor = bgcol
  749.     itm.EventTextEdit:SetPaletteColor('All', 'Base', bgcol)
  750. end
  751.  
  752. -- Display the window
  753. win:Show()
  754.  
  755. -- Add the action listener functions, setup the hotkeys and adjust the ui:TextEdit background colors
  756. Main()
  757.  
  758. -- Keep the window updating until the script is quit
  759. disp:RunLoop()
  760. win:Hide()
  761. app:RemoveConfig('ActionListener')
  762. collectgarbage()
  763.  

Screenshot

Action Listener_v1_1_updated.png

Installation

The easiest approach to install Action Listener is to use Reactor and install the "Scripts/Comp" category based "UI Manager Lua & Python Examples" atom package.

Manual installation of Action Listener is possibly, too:

Copy the "Action Listener.lua" script into your Fusion user preferences "Scripts/Comp/" folder.

Usage

In Fusion you can then run the script from inside Fusion's GUI by selecting the "Script > Action Listener" item.

Start adding nodes and carrying out compositing tasks to see the Recorded Action Log update and scroll.

If you install the "Action Listener.fu" hotkey file you can use the (Command + R) or (Control + R) hotkey to open the "Action Listener" window.

GUI Controls

Button Controls

The "Print Actions List" button will output a list of all the actions that are present in Fusion to the Console tab.

The "Clear Event and Actions Logs" button will clear the text fields in the Action Listener GUI and give you a fresh recording.

Checkbox Controls

The "Translate Actions to Lua" checkbox tries to convert the action commands to the nearest Lua approximation.

The "Print Action Log to Console" checkbox will print a running log of the Action and event logs to the Console tabs. When the "ev.Args.tool", "ev.Args.prev", "ev.Rets.tool", and "ev.sender" table items are printed to the console they will have their pointer values probed using :GetAttrs() to print out the extra details.

The "Track Elapsed Time" checkbox adds a pause that will match the speed of the original recorded actions.

Supported Action to Lua Translations

The process of translating each action event to its matching Lua script output is a manual process and will take a while to fully implement.

Here is a list of the typical Lua translated actions you would see in Fusion 16.1 with Reactor installed:
[221 Actions Found]
Bin_New_Reel
Bin_New_Folder
Bin_New_Item
Bin_Icon_Size
Bin_Show_Checker
Bin_View_Mode
GraphView_ZoomToFit
GraphView_ZoomToRect
GraphView_SortMenu
GraphView_ZoomX
KeyframeView_ZoomY
GraphView_ZoomY
Player_Item_Next
Player_Item_Prev
Player_Play
Player_Play_Forward
Player_Play_Reverse
Player_Seek_By
Player_Seek_To
Player_Seek_Start
Player_Seek_End
Player_Seek_Prev
Player_Seek_Next
Player_Set_Loop
Player_Trim_Set_In
Player_Trim_Set_Out
Player_Gamma
Player_Gain
Player_Set_FPS
Player_Set_Time
Player_Guide_Enable
Player_Guide_Select
Player_Channel
Player_Show_Metadata
Player_Sync_Mode
Player_Loop_Set_In
Player_Loop_Set_Out
Player_Loop_Reset
Player_Loop_Set_Shot
Player_Device_DeckLink
Fusion_Screen_Mode
Fusion_View_Show
Fusion_Zone_Expand
Fusion_Time_Set
Fusion_Time_Get
ACTION_GET_TEMPLATES_LIST
ACTION_GET_UI_LAYOUT
ACTION_SET_UI_LAYOUT
ACTION_SHOW_CONSOLE
ACTION_PRINT_CONSOLE
ACTION_CLOSE_COMP
No_Action
Viewer_Checker_Enable
Viewer_ROI_Enable
Viewer_ROI_Auto
Viewer_ROI_Lock
Viewer_ROI_Set
Viewer_ROI_Reset
Viewer_DOD_Enable
Viewer_3D_Lighting
Viewer_3D_Wireframe
Viewer_3D_Solid
Viewer_3D_Lights
Viewer_3D_Shadows
Viewer_3D_Transparency
Frame_Activate_SubWnd
Frame_Activate_Frame
Frame_Activate_Next
Frame_Activate_Prev
Comp_Choose_Tool
Comp_Choose_Action
Execute
Comp_New
Comp_Open
Comp_Save
Comp_SaveVersion
AddTool
AddLUT
RunScript
AddSetting
Layout_Switch
Prefs_Show
App_Exit
Comp_BackgroundRender
Comp_Undo
Comp_Redo
Comp_ShowTimeCode
Comp_TimeCodeFormat
App_About
App_NewImageView
Comp_NewTabbedFrame
Comp_NewFloatFrame
App_Help
App_OnlyActiveComp
App_ShowUI
App_CustomizeToolBars
App_CustomizeHotkeys
App_Cut
App_Copy
App_Paste
App_Delete
App_SelectAll
App_DeselectAll
App_PasteSettings
View_Show
Comp_Close
Comp_SaveAs
Comp_SaveCopyAs
Script_Edit
Layout_Load
Layout_Save
Layout_Reset
Comp_Recent_Open
Comp_Recent_Clear
Utility_Show
Tool_ViewOn
Tool_ViewClear
Bins_Play
Bins_Stop
Bins_Delete
Bins_Rename
Bins_Refresh
Bins_SelectAll
Time_Step_Forward
Time_Step_Back
Time_Step_NextKey
Time_Step_PrevKey
Time_Goto_GlobalStart
Time_Goto_GlobalEnd
Time_Goto_RenderStart
Time_Goto_RenderEnd
Time_Set
Tool_Settings_Activate
Tool_Settings_Store
Viewer_SubView_Enable
Viewer_Lock
Viewer_QuadView
Viewer_Scale_Rel
Viewer_Scale_Abs
Viewer_Buffer
Viewer_Reset
Viewer_SubView_Swap
Viewer_Tools_Disable
Viewer_Unview_All
Viewer_LUT_Enable
Viewer_Show_GainGamma
Viewer_LUT_Edit
Viewer_Channel
Viewer_Guides_Show
Viewer_Controls_Show
Viewer_3D_CentreSelected
Viewer_3D_FitSelected
Viewer_3D_FitAll
View_Zoom_In
View_Zoom_Out
View_Zoom_Fit
View_Zoom_Rectangle
Time_Goto_Key_Next
Time_Goto_Key_Prev
Playback_Mode
Playback_Seek
Playback_Seek_Start
Playback_Seek_End
NetRender_Allow
Comp_Render_Frame
Comp_Render_End
Comp_Activate_Tool
Comp_StartRender
Comp_Start_Render
Comp_Render
Comp_Abort
Comp_Opened
Drag_Drop
Comp_High_Quality
Comp_Motion_Blur
Comp_Proxy
Comp_Auto_Proxy
Comp_Play_Loop
Comp_Play_PingPong
Reel_Delete_Selected
Target_Show_Menu
Target_Show_Scripts
Console_Show
Comp_Reset
View_Zoom_Relative
View_Zoom_Absolute
View_Reset
View_Pan_Mode
View_Zoom_Mode
Target_Show_Menu
Reel_Delete_Selected
Player_Trim_Exit
Bins_Mode_Exit
Reactor_Open
Reactor_Reinstall_Reactor
Reactor_Atomizer
Reactor_Fuse_Scanner
Reactor_Macro_Scanner
Reactor_Plugin_Scanner
Reactor_Folder
Reactor_Docs_Folder
Reactor_Comps_Folder
Reactor_Config_Folder
Reactor_Temp_Folder
Reactor_Resync
Reactor_Open_Reactor_Log
Resources_Reactor_Online_Repository
Resources_Online_Discussion
Resources_WSL
Resources_Bug_Tracker
Reactor_Prefs
Reactor_About
Close_Other_Comps
Close_Comps_To_Left
Close_Comps_To_Right
LifeSaverVersionUp
LifeSaverVersionDown
Open_A_Copy
Open_Comp_in_Script_Editor
Open_All_Comps_In_Script_Editor
Restart_Fusion
You do not have the required permissions to view the files attached to this post.
Last edited by AndrewHazelden on Tue Oct 01, 2019 6:45 am, edited 8 times in total.

User avatar
AndrewHazelden
Fusionator
Posts: 1386
Joined: Fri Apr 03, 2015 3:20 pm
Answers: 4
Location: West Dover, Nova Scotia, Canada
Been thanked: 59 times
Contact:

Running UI Manager GUIs from a Terminal FuScript Session

#51

Post by AndrewHazelden » Tue Oct 03, 2017 8:47 am

Fusion Studio comes with the FuScript program that allows you to run Fusion based Lua and python scripts from a terminal session. FuScript is a Fusion Studio (paid) feature that is not available in the base Fusion (Free) version. With FuScript you are able to use the terminal/command prompt to run scripts in a headless batch processing mode, or you can connect to an already running Fusion graphical session which allows a pipeline TD to automate and control Fusion based tasks externally. :buttrock:

For the official documentation on FuScript check out page 24/25 of the "Fusion 8 Script Manual.pdf" guide.

A neat trick with FuScript is that you can start and run a Lua script that uses a UI Manager GUI from a Terminal session, too. If FuScript was the process that created a Fusion UI Manager based window it will wait until the view is closed and the Lua script finishes running. Then the FuScript process will close and return a status code to the shell. This allows FuScript to be nested inside of a larger automation tool so the remaining tasks a Windows Command Prompt .bat script or Mac/Linux .sh shell script can run.

This concept of using FuScript + Fusion from the command prompt can allow a pipeline TD to develop some really creative and useful tools that are able to take advantage of Fusion's VFX tools in ways you might not have considered previously. This means you could use on Fusion Studio inside of a shell tool as a bigger and more full featured node based version of Imagemagick or FFMPEG. :popcorn:

FuScript is installed with Fusion Studio on Windows/Mac/Linux at the following location:

Windows Path:

Code: Select all

C:\Program Files\Blackmagic Design\Fusion 9\FuScript.exe
Linux Path:

Code: Select all

/opt/BlackmagicDesign/Fusion9/fuscript
Mac Path: (You need to browse inside the Fusion.app package contents to access this folder.)

Code: Select all

/Applications/Blackmagic Fusion 9/Fusion.app/Contents/MacOS/fuscript
fuscript-in-fusion-program-folder.png
Adding FuScript to the System PATH

The following terminal examples in this FuScript tutorial assume you have added the path to the FuScript program to your system %PATH% / $PATH environment variable.

On Windows you can add a new System %PATH% variable entry using the Control Panels > System > Environment Variables... window. You will need to add the "C:\Program Files\Blackmagic Design\Fusion 9\" folder to the end of the entries for the PATH variable.

On Mac you can add FuScript's parent folder to the System $PATH variable by adding this snippet to your $HOME/.bash_profile document:

Code: Select all

# Add fusion and fuscript to the system $PATH
export PATH="$PATH:/Applications/Blackmagic Fusion 9/Fusion.app/Contents/MacOS/"
On Linux you can add FuScript's parent folder to the System $PATH variable by adding this snippet to your $HOME/.bash_profile document:

Code: Select all

# Add fusion and fuscript to the system $PATH
export PATH="$PATH:/opt/BlackmagicDesign/Fusion9/"
After you have added the "Fusion9" folder to your environment PATH variable you will typically have to restart your system or login/logout to make those changes active in your current shell session.

If you haven't added the Fusion9 folder to your PATH variable then you will need to replace the short program name "fuscript" with the full absolute filepath to the fuscript executable when running the terminal commands in this tutorial. :)

Running FuScript for the First Time

To find out what options are provided by fuscript, you can run the fuscript program without supplying any additional arguments. This will print out details on how you can use the tool:

Code: Select all

fuscript
Usage: fuscript [opts] <script> [args]
   -i                   - Enter interactive mode
   -v                   - Print version information
   -s                   - Run as script server
   -S                   - Run as script server with no timeout
   -p [appname]         - Ping script apps
   -P [appname]         - Ping script apps (longer timeout)
   -q                   - Be quiet
   -b                   - Run with commandline debugger
   -l <lang>            - Explicitly set language, from:
      lua
      py2 or python2
      py3 or python3
   -x <string>          - Execute string
If you want to run FuScript in an interactive lua shell session you would add the "-i" argument when FuScript is launched. Pressing Control + C twice will exit you from the Lua interactive session.

Run in an interactive mode:

Code: Select all

fuscript -i
running-fuscript-interactive-prompt.png
Fuscript can run a single line string of lua code like this:

Code: Select all

fuscript -x "print([[Hello Fusion World!]])"
This results in the FuScript terminal displaying the following output:

Code: Select all

Fusion Script Interpreter
Copyright (C) 2005 - 2017 Blackmagic Design Pty. Ltd.

Hello Fusion World!
fuscript-running-a-lua-string.png
Using FuScript with a Fusion Graphical Session

If you want FuScript to connect to an active Fusion graphical session and control it remotely you could use the "-x" argument along with a quoted chunk of Lua code that calls the Fusion() function. In this example a Blur node will be added to the current foreground comp. If you want to run multiple Lua commands at the same time from a string the easiest thing to do is to add semicolons to separate each command since the terminal/command prompt doesn't like to deal with line breaks.

Run a block of Lua code:

Code: Select all

fuscript -x "fusion = Fusion();fu = fusion;app = fu;composition = fu.CurrentComp;comp = composition;SetActiveComp(comp);blur = Blur();"
If FuScript is able to successfully connect to the Fusion GUI session and run the Lua string you will see a blur node added to your composite.
blur-node-added-to-fusion-composite.png
For FuScript to work correctly in this role your firewall rules have to be set to allow Fusion and FuScript to do inter-application communication. If FuScript has difficulty connecting to an existing Fusion graphical session you will get back an error message like this:

Code: Select all

"[string "???"]:1: attempt to index global 'fu' (a nil value)"
Starting the Fusion GUI session up first and then running the FuScript code will typically make that error message go away. If that doesn't fix the issue when you go to add a node to a comp from FuScript then you will have to explore your firewall settings. ;)

When you are using FuScript to carry out tasks, you will likely want to stop Loader/Saver node dialogs from opening up while a Lua script is running. You can do that by adding "comp:Lock()" to the FuScript launching command. After you have finished running your script commands, you can then add a final "comp:Unlock()" action to restore the dialog opening actions in the Fusion graphical session.

Running a Lua UI Manager Based Script

FuScript is able to run a Lua script off disk by adding the name of the script at the end of the launching arguments. The neatest thing about FuScript is you can also show UI Manager based GUIs when running FuScript with the help of the following terminal command example. In this example <script.lua> would be the absolute filepath to your lua script. Make sure to wrap the filepath in quotes if there are spaces in the folder/filenames in the path.

Run a Lua script off disk with UI Manager support enabled:

Code: Select all

"fuscript" -x "fusion = Fusion();fu = fusion;app = fu;composition = fu.CurrentComp;comp = composition;SetActiveComp(comp);" <script.lua>
Let's try this process out. For the next example we are going to download the "myfirstwindow.lua" script that is included below and save it to the Fusion user prefs "Scripts:/Comp/" folder. The "myfirstwindow.lua" script was one of the first examples in the UI Manager tutorial series and it creates a simple UI Manager based window.
Code: [Select all] [Expand/Collapse] [Download] (myfirstwindow.lua)
  1. -- Create a new window
  2. local ui = fu.UIManager
  3. local disp = bmd.UIDispatcher(ui)
  4. local width,height = 400,200
  5.  
  6. win = disp:AddWindow({
  7.   ID = 'MyWin',
  8.   WindowTitle = 'My First Window',
  9.   Geometry = {100, 100, width, height},
  10.   Spacing = 10,
  11.  
  12.   ui:VGroup{
  13.     ID = 'root',
  14.    
  15.     -- Add your GUI elements here:
  16.     ui:Label{
  17.       ID = 'TextLabel',
  18.       Text = 'This is a Label',
  19.     },
  20.   },
  21. })
  22.  
  23. -- The window was closed
  24. function win.On.MyWin.Close(ev)
  25.   disp:ExitLoop()
  26. end
  27.  
  28. -- Add your GUI element based event functions here:
  29. itm = win:GetItems()
  30.  
  31. win:Show()
  32. disp:RunLoop()
  33. win:Hide()
  34.  
To move the "myfirstwindow.lua" script to the correct location on disk can quickly open the Fusion user prefs "Scripts:/comp" folder up using the following command in the Terminal/Command Prompt window:

Windows Explorer Folder Open Command:

Code: Select all

explorer "%appdata%\Blackmagic Design\Fusion\Scripts\Comp\"
Mac Finder Folder Open Command:

Code: Select all

open "$HOME/Library/Application Support/Blackmagic Design/Fusion/Scripts/Comp/"
Linux Nautilus Folder Open Command:

Code: Select all

nautilus "$HOME/.fusion/BlackmagicDesign/Fusion/Scripts/Comp/" &
You could also open the Fusion user prefs "Scripts:/comp" folder up using the following Fusion Console tab Lua command:

Code: Select all

bmd.openfileexternal("Open", comp:MapPath("Scripts:/Comp/"))
With the script saved in the correct folder, we can now run the fuscript program and have it launch the "myfirstwindow.lua" script. When you close the "My First Window" dialog in Fusion the Fuscript process will exit gracefully and the terminal session will go back to the shell.
running-fuscript-and-myfirstwindow-on-centos-linux.png
Windows Script Command:

Code: Select all

REM Run a Lua script off disk
fuscript -x "fusion = Fusion();fu = fusion;app = fu;composition = fu.CurrentComp;comp = composition;SetActiveComp(comp);" "%appdata%\Blackmagic Design\Fusion\Scripts\Comp\myfirstwindow.lua"
Linux Script Command:

Code: Select all

# Run a Lua script off disk
fuscript -x "fusion = Fusion();fu = fusion;app = fu;composition = fu.CurrentComp;comp = composition;SetActiveComp(comp);" "$HOME/.fusion/BlackmagicDesign/Fusion/Scripts/Comp/myfirstwindow.lua"
Mac Script Command:

Code: Select all

# Run a Lua script off disk
fuscript -x "fusion = Fusion();fu = fusion;app = fu;composition = fu.CurrentComp;comp = composition;SetActiveComp(comp);" "$HOME/Library/Application Support/Blackmagic Design/Fusion/Scripts/Comp/myfirstwindow.lua"
running-fuscript-myfirstwindow-dialog.png

Running FuScript Code on Remote Systems

FuScript also has an RPC like remote connection option that lets you connect from your FuScript program's local terminal session to a copy of Fusion running on another server/workstation. This can let an artist workstation tie into a high powered server to finish rendering a shot or it allows you to have maintenance functions run as part of a regular pipeline automation task.

To use this remote connection approach in FuScript you simply need to change the Fusion connection function from "fusion = Fusion()" to the scriptapp version of "fusion = bmd.scriptapp('Fusion', 'localhost')". You would also need to write in the actual remote system hostname you want to use in place of "localhost" so you can make the initial Fusion GUI socket connection.

In this example I will perform a remote FuScript connection to a system with a hostname of "Pine":

Code: Select all

fuscript -x "fusion = bmd.scriptapp('Fusion', 'Pine');fu = fusion;app = fu;composition = fu.CurrentComp;comp = composition;SetActiveComp(comp);dump(fu:GetAttrs())" 
FuScript then responds with the host name details since I added "dump(fu:GetAttrs())" to the end of the Lua code string and that command will list information about the fu process.
FuScript wrote: Fusion Script Interpreter
Copyright (C) 2005 - 2017 Blackmagic Design Pty. Ltd.

table: 0x0c5c7988
FUSIONS_CLVendor = AMD
FUSIONS_FileName = /Applications/Blackmagic Fusion 9/Fusion.app/Contents/MacOS/Fusion
FUSIONH_CurrentComp = Composition (0x0x7fa6e3e70a00) [App: 'Fusion' on 127.0.0.1, UUID: ae048308-4eac-4546-be76-da29dfbe2c46]
FUSIONI_PhysicalRAMTotalMB = 16384
FUSIONS_CLVersion = OpenCL 1.2 1.2 (Jun 15 2017 18:41:53)
FUSIONI_VersionLo = 65543
FUSIONS_MachineType = IA32
FUSIONI_PhysicalRAMFreeMB = 9729
FUSIONI_VersionHi = 589824
FUSIONB_IsManager = false
FUSIONS_Version = 9.0.1
FUSIONS_CLType = GPU
FUSIONS_CLDevice = AMD Radeon R9 M370X Compute Engine
FUSIONI_SerialHi = 000000000 (Blanked out by Andrew for security reasons)
FUSIONI_VirtualRAMTotalMB = 16384
FUSIONS_GLVendor = ATI Technologies Inc.
FUSIONS_GLVersion = 2.1 ATI-1.51.8
FUSIONI_VirtualRAMUsedMB = 6654
FUSIONB_IsRenderNode = false
FUSIONS_GLDevice = AMD Radeon R9 M370X OpenGL Engine
FUSIONI_NumProcessors = 8
FUSIONI_SerialLo = 0
By the end of this tutorial you should be able to run your own Lua scripts from a FuScript terminal session and will be able to take control of a graphical Fusion session. :cheer:
You do not have the required permissions to view the files attached to this post.
Last edited by AndrewHazelden on Wed Oct 04, 2017 6:47 am, edited 9 times in total.

User avatar
Dunn
Moderator
Posts: 477
Joined: Mon Aug 04, 2014 4:27 am
Location: Hamburg, Germany
Been thanked: 2 times
Contact:

Re: Building GUIs With Fusion's UI Manager

#52

Post by Dunn » Tue Oct 03, 2017 9:05 am

Oh man! have you ever thought about bring out a book, "Learn: Fusion UI Manager in 24 hours" :D
. sure the title is open for debate :) ... this is totally amazing ! We are so glad to have you onboard man!

User avatar
AndrewHazelden
Fusionator
Posts: 1386
Joined: Fri Apr 03, 2015 3:20 pm
Answers: 4
Location: West Dover, Nova Scotia, Canada
Been thanked: 59 times
Contact:

Apple Scripting Fusion Studio???

#53

Post by AndrewHazelden » Tue Oct 03, 2017 9:27 am

Dunn wrote:We are so glad to have you onboard man!
Hi Dunn.

I'm just happy to have people following along with the series. It has been a fun adventure exploring what is possible with Fusion 9 and the UI Manager system.

After checking out FuScript today I think it *might* be possible for a 3rd party developer to use this approach to create a Fusion Studio on MacOS Apple Events bridge. In theory that would let Apple Scripts / Apple Events / Automator Actions be used to control the current Fusion graphical compositing session. I'm quite excited by this possibility. :D

User avatar
AndrewHazelden
Fusionator
Posts: 1386
Joined: Fri Apr 03, 2015 3:20 pm
Answers: 4
Location: West Dover, Nova Scotia, Canada
Been thanked: 59 times
Contact:

FuScript Post Updated with Remote Connection Details

#54

Post by AndrewHazelden » Wed Oct 04, 2017 3:51 am

I wanted to highlight the fact I updated the "Running UI Manager GUIs from a Terminal FuScript Session" post with an additional topic at the end labelled "Running FuScript Code on Remote Systems".

FuScript has an RPC like remote connection option that lets you connect from your FuScript program's local terminal session to a copy of Fusion running on another server/workstation. This can let an artist workstation tie into a high powered server to finish rendering a shot or it allows you to have maintenance functions run as part of a regular pipeline automation task.

User avatar
SirEdric
Fusionator
Posts: 1958
Joined: Tue Aug 05, 2014 10:04 am
Answers: 4
Real name: Eric Westphal
Been thanked: 135 times
Contact:

Re: Building GUIs With Fusion's UI Manager

#55

Post by SirEdric » Wed Oct 04, 2017 9:13 pm

Fantastic stuff, Andrew!
Thanks a bunch for sharing.

User avatar
AndrewHazelden
Fusionator
Posts: 1386
Joined: Fri Apr 03, 2015 3:20 pm
Answers: 4
Location: West Dover, Nova Scotia, Canada
Been thanked: 59 times
Contact:

Creating a UI Manager GUI Inside of a Node's UserControls Interface

#56

Post by AndrewHazelden » Fri Oct 06, 2017 2:48 am

This example shows how a compact Lua UI Manager based GUI window can be packed inside of a Fusion node's UserControls interface element.

Fusion 9.0.1 is required to use this example since I used the new fu:GetMousePos() command to read the current mouse position which allows the window to be created intelligently next to your mouse pointer, and a ui:Font tag was used to add a custom font and font size adjustment on the GUI element displayed in the UI Manager window. If you didn't need these niceties it would be possible to do everything else shown in this example using Fusion 8.2.1.

The UI Manager based view that is shown has a single GUI element of a ui:Button with the placeholder text "That is all. Thank You". This button can be clicked to close the tiny popup window. This UI Manager code is self contained in the node and lives 100% in the Fusion ".comp" file so it travels with the rest of your composite data as it makes it way through your pipeline with no external file dependencies or paths to break.
loader-button-control-based-ui-manager-window.png
A practical use for this type of UI Manager view that is nested inside of a node is that you can use it to adjust preferences or settings that are saved via Lua's fusion:SetData()/comp:SetData() and fusion:GetData()/comp:GetData() functions. I haven't tested it but I think you can also try saving your custom settings using self:SetData()/self:GetData() too in the node. These new preferences configured in a UI Manager based ButtonControl are then accessible by an Intool script, a utility tool/comp script, simple hotkey bindings/actions added to a Config:/hotkeys.fu file, or other scripted UserControls on the node. It's hard to express just how useful this could be if you wanted to really customize how each of your Fusion nodes are configured for performing specialized tasks quickly and efficiently. :)

When you select the Loader node in your comp that is added by the "LoaderButtonControl.setting" example you will see a "Show UI Manager Dialog" button in the Tools UI at the bottom of the File tab section.

This button was embedded in the Loader node using a UserControls code block with a handcrafted "ButtonControl" element. The ButtonControl GUI element has a chunk of Lua code added inline that is run when the button is pressed.

For simplicity of delivering this example via a WSL forum post I placed the Lua code into the file using a pair of double square brackets like [[ ]]. You could also use a single line lua code technique if you wanted to that would separate each newline with a semi-colon character.

You could also link to an external Lua script using a ButtonControl tag like 'BTNCS_Execute = "@scripts:/Tool/YourCustomScript.lua",' that would be run when the ButtonControl is clicked if you wanted to keep the code tidy and more managed. The only real downside to the external script approach is this adds a separate file dependency that has to be installed and managed on each of the artist's systems. Going for inline code vs an external Lua script file is a decision you would have to think about when you deploy your own UserControls based scripts based upon how often you need to update the script vs how far and wide you want to distribute the custom node without having to deal with any additional installation related tech support burden. :wip:

The WSL Forum has an archived copy of the classic Fusion VFXPedia reference site. You can click here to read the VFXPedia entry on UserControls. This will give you a better background on the ButtonControl syntax. :bowdown:

The "Fusion 8 Script Manual.pdf" document that comes with Fusion 8/9 also discusses UserControls/ButtonControl and going to page 27 will show a screenshot of the Fusion "Add Controls" dialog window being used to add a new ButtonControl.

The example file that is attached to this post is a raw macro ".setting" style copy/paste clipping of a Fusion Loader node. You can either paste this node text right into the Fusion flow area to have the loader node with the ButtonControl added to your comp, or you could download the file to disk and save this example to your Fusion user preferences "Macros:/" folder.
Code: [Select all] [Expand/Collapse] [Download] (LoaderButtonControl.setting)
  1. {
  2.     Tools = ordered() {
  3.         LoaderButtonControl = Loader {
  4.             Clips = {
  5.             },
  6.             CustomData = {
  7.                 HelpPage = "https://www.steakunderwater.com/wesuckless/viewtopic.php?f=6&t=1411&p=11562#p11562",
  8.             },
  9.             CtrlWZoom = false,
  10.             NameSet = true,
  11.             Inputs = {
  12.                 ["Gamut.SLogVersion"] = Input { Value = FuID { "SLog2" }, },
  13.             },
  14.             ViewInfo = OperatorInfo { Pos = { 1489, 117 } },
  15.             UserControls = ordered() {
  16.                 UIManagerButton = {
  17.                     LINKS_Name = "Show UI Manager Dialog",
  18.                     LINKID_DataType = "Number",
  19.                     INPID_InputControl = "ButtonControl",
  20.                     BTNCS_Execute = [[
  21. local ui = fu.UIManager
  22. local disp = bmd.UIDispatcher(ui)
  23. local width,height = 300,200
  24. local x = fu:GetMousePos()[1]
  25. local y = fu:GetMousePos()[2]
  26.  
  27. win = disp:AddWindow({
  28.   ID = 'ButtonControlWin',
  29.   TargetID = 'ButtonControlWin',
  30.   WindowTitle = 'A ButtonControl Window',
  31.   Geometry = {x-(width/2), y+8, width, height},
  32.   Spacing = 10,
  33.  
  34.   ui:VGroup{
  35.     ID = 'root',
  36.  
  37.     ui:Button{
  38.       ID = 'DoneButton',
  39.       Text = 'That is all. Thank you.',
  40.       Font = ui:Font{
  41.         Family = 'Droid Sans Mono',
  42.         StyleName = 'Regular',
  43.         PixelSize = 18,
  44.         MonoSpaced = true,
  45.         StyleStrategy = {ForceIntegerMetrics = true},
  46.       },
  47.     },
  48.  
  49.   },
  50. })
  51.  
  52. function win.On.ButtonControlWin.Close(ev)
  53.   disp:ExitLoop()
  54. end
  55.  
  56. function win.On.DoneButton.Clicked(ev)
  57.   disp:ExitLoop()
  58. end
  59.  
  60. itm = win:GetItems()
  61. win:Show()
  62. disp:RunLoop()
  63. win:Hide()
  64. ]],
  65.                     INP_Integer = false,
  66.                     ICS_ControlPage = "File"
  67.                 },
  68.             }
  69.         }
  70.     },
  71.     ActiveTool = "LoaderButtonControl"
  72. }
Edit: Thanks Midgardsormr for the formatting suggestion. It is a lot easier to have the code displayed this way for an example script people are going to learn from and study. :)
You do not have the required permissions to view the files attached to this post.
Last edited by AndrewHazelden on Fri Oct 06, 2017 9:59 am, edited 4 times in total.

User avatar
Midgardsormr
Fusionator
Posts: 1213
Joined: Wed Nov 26, 2014 8:04 pm
Answers: 3
Location: Los Angeles, CA, USA
Been thanked: 94 times
Contact:

Re: Building GUIs With Fusion's UI Manager

#57

Post by Midgardsormr » Fri Oct 06, 2017 8:40 am

If you want a complex script inline on your button but don't want to deal with trying to convert it into a single-line string, you can use Lua's literal string delimiter: [[ ]].

Code: Select all

 
BTNCS_Execute = [[
  local ui = fu.UIManager
  local disp = bmd.UIDispatcher(ui)
  local width,height = 300,200
  local x = fu:GetMousePos()[1]
  local y = fu:GetMousePos()[2]
  
  win = disp:AddWindow({
      ID = 'ButtonControlWin',   
      TargetID = 'ButtonControlWin',   
      WindowTitle = 'A ButtonControl Window',  
      Geometry = {x-(width/2), y+8, width, height},   
      Spacing = 10,     
      ui:VGroup{     
          ID = 'root',     
          ui:Button{       
              ID = 'DoneButton',       
              Text = 'That is all. Thank you.',      
              Font = ui:Font{         
                  Family = 'Droid Sans Mono',         
                  StyleName = 'Regular',         
                  PixelSize = 18,        
                  MonoSpaced = true,         
                  StyleStrategy = {ForceIntegerMetrics = true},       
              }, -- font    
          }, -- button   
      }, --vGroup 
  }) --AddWindow
  
  function win.On.ButtonControlWin.Close(ev) 
    disp:ExitLoop()
  end
  
  function win.On.DoneButton.Clicked(ev) 
    disp:ExitLoop()
  end
  
  itm = win:GetItems()
  win:Show() 
  disp:RunLoop()
  win:Hide()
]], -- BTNCS_Execute

User avatar
AndrewHazelden
Fusionator
Posts: 1386
Joined: Fri Apr 03, 2015 3:20 pm
Answers: 4
Location: West Dover, Nova Scotia, Canada
Been thanked: 59 times
Contact:

Re: Building GUIs With Fusion's UI Manager

#58

Post by AndrewHazelden » Fri Oct 06, 2017 9:14 am

Midgardsormr wrote:If you want a complex script inline on your button but don't want to deal with trying to convert it into a single-line string, you can use Lua's literal string delimiter: [[ ]].

Thanks for the code formatting suggestion Midgardsormr! I re-arranged the code block in the example so it would be clearer to read and decode.

User avatar
AndrewHazelden
Fusionator
Posts: 1386
Joined: Fri Apr 03, 2015 3:20 pm
Answers: 4
Location: West Dover, Nova Scotia, Canada
Been thanked: 59 times
Contact:

Using Compiled Lua Modules With Fusion on macOS

#59

Post by AndrewHazelden » Thu Oct 12, 2017 7:54 am

This tip is aimed at the small group of Fusion 9.0.1 on macOS based technical artists who are on WSL. If you use the Brew package manager + LuaRocks to add compiled Lua modules to your Fusion system then the following details might be helpful:

If you want to run Lua modules from inside a Comp or Tool script you can add your custom Lua modules to Fusion 9.0.1's own personal application package based internal Lua Modules folder using the following (rough but effective) approach in the Terminal:

Code: Select all

# Create the required Fusion 9.0.1 folder for loading Lua binary and .lua script based modules:
sudo mkdir -p "/Applications/Blackmagic Fusion 9/Fusion.app/Contents/MacOS/Modules/Lua/"
sudo chmod -R 755 "/Applications/Blackmagic Fusion 9/Fusion.app/Contents/MacOS/Modules/"
open "/Applications/Blackmagic Fusion 9/Fusion.app/Contents/MacOS/Modules/"

# Copy the system wide Lua 5.1 precompiled .so library and raw .lua script based modules to the same folder:
cp -R /usr/local/share/lua/5.1/* "/Applications/Blackmagic Fusion 9/Fusion.app/Contents/MacOS/Modules/Lua/"
cp -R /usr/local/lib/lua/5.1/* "/Applications/Blackmagic Fusion 9/Fusion.app/Contents/MacOS/Modules/Lua/"
Note: Fuses that are trying to access Lua modules on Fusion 9.0.1 on macOS should still put their uncompiled .lua script files in the "/usr/local/share/lua/5.1/" folder, and the .so files in the "/usr/local/lib/lua/5.1/" folder.

Trying Out Your First Lua Module

You can try the Fusion RSS Headlines Script out if you want to see this workflow in action since it has a pre-compiled Lua Module dependency.
Last edited by AndrewHazelden on Thu Oct 12, 2017 7:56 am, edited 1 time in total.

User avatar
Chad
Fusionator
Posts: 1419
Joined: Fri Aug 08, 2014 1:11 pm
Been thanked: 14 times

Re: Building GUIs With Fusion's UI Manager

#60

Post by Chad » Thu Oct 12, 2017 7:55 am

Neat. Now we can add copy protection to scripts and fuses.