apple script to disarm MIDI cues in timeline group

92 views
Skip to first unread message

Thomas Wacker

unread,
Apr 27, 2026, 3:26:46 PM (7 days ago) Apr 27
to QLab
Hi all,
I once again need your help with an apple script...
The scenario :
a timeline group cue with one long audio file, some fade cues and a bunch of MIDI cues (MSC) triggering the light board. 
In rehearsal, when I go to a given point in the group cue, using "load to time" (or place the playhead in the timeline), then press "go", Qlab fires all cues in the group, whose pre wait time is lower than the "load to time" value - which is fine for the fade cues, but creates a mess with the MIDI cues...
I would like to have an apple script, which tells the current playlist to disarm all MIDI cues in the group, whose pre wait time is lower than the "load to time" value, thus preventing all "expired" MIDI cues from firing when I free "go".
Can you help me with this one ?
thanks in advance
Thomas
A timeline group cue with one long audio file, some fade cues and a bunch of MIDI cues (MSC) triggering the light board.
In rehearsal, when I go to a given point in the group cue, using „load to time“ (or placing the playhead in the timeline), then press „go“,
Qlab fires all cues in the group whose pre wait time is lower than the „load to time“ value - which is fine for the fade cues, but creates a mess with the MIDI cues….

I would like to have an apple script which tells the current playlist to disarm all MIDI cues in the group whose pre wait time is lower than the "load to time“ value, thus preventing all „expired“ MIDI cues from firing when I press „go“.

Can you help me with this one ?
 once again need your expertise with an apple script :

The scenario :
A timeline group cue with one long audio file, some fade cues and a bunch of MIDI cues (MSC) triggering the light board.
In rehearsal, when I go to a given point in the group cue, using „load to time“ (or placing the playhead in the timeline), then press „go“,
Qlab fires all cues in the group whose pre wait time is lower than the „load to time“ value - which is fine for the fade cues, but creates a mess with the MIDI cues….

I would like to have an apple script which tells the current playlist to disarm all MIDI cues in the group whose pre wait time is lower than the "load to time“ value, thus preventing all „expired“ MIDI cues from firing when I press „go“.

Can you help me with this one ?
 once again need your expertise with an apple script :

The scenario :
A timeline group cue with one long audio file, some fade cues and a bunch of MIDI cues (MSC) triggering the light board.
In rehearsal, when I go to a given point in the group cue, using „load to time“ (or placing the playhead in the timeline), then press „go“,
Qlab fires all cues in the group whose pre wait time is lower than the „load to time“ value - which is fine for the fade cues, but creates a mess with the MIDI cues….

I would like to have an apple script which tells the current playlist to disarm all MIDI cues in the group whose pre wait time is lower than the "load to time“ value, thus preventing all „expired“ MIDI cues from firing when I press „go“.

Can you help me with this one ?

micpool

unread,
Apr 27, 2026, 3:37:31 PM (7 days ago) Apr 27
to QLab
This script, which is intended to be triggered by space and any MIDI or OSCethods, will perform a normal workspace GO workspace GO  m on any cue at the playhead, unless that cue is a timeline group that is loaded to time.

--Go Workspace (and disable any MIDI cues in a timeline  group prior to load to time)

--Hijack Space Bar (Clear GO key in Settings/Controls/Keyboard/GO)

--Use Space Bar and/or set MIDI triggers or  send OSC to trigger the cue containing this script, to GO workspace


set aFlag to false

tell application id "com.figure53.QLab.5" to tell front workspace

set theCue to playhead of front cue list

if theCue is missing value then

return 1

end if

if q type of theCue is not "group" then

go

return 2

end if

if q type of theCue is "group" and mode of theCue is not timeline then

go

return 3

end if

if running of theCue is true or paused of theCue then

go

return 4

end if

--if cue is not running or paused  and is a Timeline Group Cue then do all this stuff

set theMIDICues to cues of theCue whose q type is "midi"

if the action elapsed of theCue is not 0 then --cue is not running or paused  and is a Timeline Group Cue and is loaded to time.

--disarm any MIDI cues in the timeline group cue  that are before the 'load to time' time

repeat with eachCue in theMIDICues

if (pre wait elapsed of eachCue) = (pre wait of eachCue) then --MIDI cue  is before 'load to time' time

set the armed of eachCue to false

else --MIDI cue  is after 'load to time' time

set the armed of eachCue to true

end if

end repeat

else --cue is not running or paused  and is a Timeline Group Cue and is NOT  loaded to time so will play from beginning

set aFlag to true

repeat with eachCue in theMIDICues

set the armed of eachCue to true

end repeat

end if

go

delay 0.5

--if cues in the timeline group have been disarmed then arm them again.

if aFlag is false then

repeat with eachCue in theMIDICues

set the armed of eachCue to true

end repeat

end if

end tell




Screen Recording 2026-04-27 at 20.37.03.mov

Thomas Wacker

unread,
Apr 27, 2026, 4:55:59 PM (7 days ago) Apr 27
to QLab
thanks again Mic, that´s one hell of an apple script ! 

Paul

unread,
Apr 28, 2026, 6:02:26 AM (6 days ago) Apr 28
to QLab
The essence of this problem can be done in one line

tell application id "com.figure53.QLab.5" to tell front workspace

set armed of every cue whose q type is "MIDI" and pre wait < loadTime to false

end tell


a practical scrip requires a bit more error checking and user interface to make it usable... run on a Hotkey trigger this script will take a selected timeline cue or other selected cue in timeline group, prompt the user for load time, disable the cues with pre wait before that time and load to time and run the group cue.

-- disarm cues with pre wait less than load  time

--  run this script a Hotkey trigger


tell application id "com.figure53.QLab.5" to tell front workspace

-- set a default load time

set loadTime to 7

set cueType to "MIDI" -- couuld use this for Network OSC triggers

-- get the selected cue ..

set myGroup to first item of (selected as list)

-- .. check if its a group or parent is group and use that

if q type of myGroup is not "Group" then

if q type of parent of myGroup is "Group" then

set myGroup to parent of myGroup

else

-- otherwise return an error

display dialog "please select a cue in a timeline group" with title "Error"

return

end if

end if

if mode of myGroup is not timeline then

display dialog "please select a timeline group" with title "Error"

return

end if

-- reset previously disarmed cues

set armed of cues whose parent is myGroup and q type is cueType to true

stop myGroup

-- ask the user for load time

set loadTime to text returned of (display dialog "Load at time" default answer loadTime with title (q display name of myGroup as string))

load myGroup time loadTime

-- disarm cues with pre wait before the load time

set armed of every cue whose q type is cueType and pre wait < loadTime to false

start myGroup

end tell




micpool

unread,
Apr 28, 2026, 1:57:57 PM (6 days ago) Apr 28
to QLab
On Tuesday, April 28, 2026 at 11:02:26 AM UTC+1 Paul wrote:
The essence of this problem can be done in one line

tell application id "com.figure53.QLab.5" to tell front workspace

set armed of every cue whose q type is "MIDI" and pre wait < loadTime to false

end tell


Except that example won't compile. If it was intended to be pseudo-code, then it's probably best to say so, rather than presenting it as a one line formatted AppleScript.

If we change the undeclared  variable loadTime to a number, e.g. 7,  then every MIDI cue in the entire workspace  with a prewait less than 7 will be disarmed, and remain so, which probably isn't desirable or  what the brief was.

While I think a range of solutions to any problem posted in this group is beneficial, I think it's worth pointing out that there are a few issues with the script below that could cause problems  if not addressed. One major problem is that this script  leaves the workspace permanently in a different state after it is run. It's quite likely that the next time the cue is used, it might be triggered conventionally, e.g. by a workspace GO. To avoid MIDI cues being disarmed erroneously in this scenario, the script should restore the timeline group to its original state by rearming any cues it has disarmed as soon as possible after it runs.
Other things that might be worth examining in any practical implementation include:

The  script possibly attempts to address the fact that cues that are in another nested group within a timeline group aren't actually cues of the parent group and won't be disarmed, but unless I am missing something, just reassigning mygroup to the parent of the nested group and applying the load time to that doesn't actually solve the problem. I'm not sure what the best way to address this  is. Scripting cues in an unknown number of nested group levels is one of the more challenging aspects of QLab scripting. Here's a solution from Rich Walsh for a slightly different problem involving nested groups


I think, on the basis that cues of nested groups don't show as individual cues in the timeline view of the parent group, that it's probably easier just to stipulate that for this to work, all MIDI cues must be in the top level of the group, but Rich's post would be a useful starting point to fixing this for nested groups in a more complex script.

The script also just starts the group cue, leaving the playhead where it was, which wouldn't be the behaviour with a cue loaded to time that was started with a workspace GO, as the original question described.

The error checking in this script doesn't verify whether a cue is actually selected, so the script will break if no cue is selected.

I would also question why it's necessary or desirable to use a dialog, rather than the normal load to time slider, or clicking on the timeline ruler above the waveform  in the timeline tab of the group cue to set the load time of the cue.

It takes less than a couple of milliseconds for a script to determine whether the cue at the playhead is a timeline group that is loaded to time and what that time is. This means it's perfectly possible to use a script, triggered by space (after disabling space in settings/control/keyboard), and by any other workspace GO triggers, which will behave as a normal GO in all circumstances except when a timeline group cue is at the playhead and loaded to time. This means that all usual QLab operational methods are retained, and the only difference in operation is a change where MIDI cues prior to a 'load to time' time in a timeline group are not fired when the group cue is triggered. This is achieved transparently to the operator with no need to use any special hotkeys, dialogues, or generate any unnecessary alerts. 

Mic



micpool

unread,
Apr 29, 2026, 3:10:20 PM (5 days ago) Apr 29
to QLab
Hi Tom

I have added some additional script based on Rich Walsh's work, for handling nested groups. (Mainly because if I don't do this now, it would be much more difficult when the now no longer maintained Late Nite Software Script Debugger ceases to function in a future Mac OS.!)

Needs thorough testing, and some of the code can probably be optimised, but I think this is getting close to a complete working method.

--Go Workspace (and disable any MIDI cues in a timeline  group prior to load to time)

--by Mic Pool. Nested Group handling based on a script by Rich Walsh.

--CONTAINS ADDITIONAL CODE FOR NESTED GROUPS

--Hijack Space Bar (Clear GO key in Settings/Controls/Keyboard/GO)

--Use Space Bar and/or set MIDI triggers or  send OSC to trigger the cue containing this script, to GO workspace


set aFlag to false

tell application id "com.figure53.QLab.5" to tell front workspace

set theCue to playhead of front cue list

if theCue is missing value then

return 1

end if

if q type of theCue is not "group" then

go

return 2

end if

if q type of theCue is "group" and mode of theCue is not timeline then

go

return 3

end if

if running of theCue is true or paused of theCue then

go

return 4

end if

--if cue is not running or paused  and is a Timeline Group Cue then do all this stuff

set cuesToProcess to cues of theCue

set containedIDs to {} -- We need to check for nested cues that are already selected so as not to add them twice; lists of "cues" can't be compared, only of IDs

repeat with eachCue in cuesToProcess

set end of containedIDs to uniqueID of eachCue

end repeat

set i to 0

repeat until i = (count containedIDs)

set eachID to item (i + 1) of containedIDs

set eachCue to cue id eachID

if q type of eachCue is "Group" then

repeat with eachChild in cues of eachCue

set childID to uniqueID of eachChild

if childID is not in containedIDs then

set end of cuesToProcess to eachChild -- Cue is not already selected, so add it for processing

set end of containedIDs to childID

end if

end repeat

end if

set i to i + 1

end repeat

if the action elapsed of theCue is not 0 then --cue is not running or paused  and is a Timeline Group Cue and is loaded to time.

--disarm any MIDI cues in the timeline group cue  that are before the 'load to time' time

set cuesToProcess to cues of theCue

set containedIDs to {} -- We need to check for nested cues that are already selected so as not to add them twice; lists of "cues" can't be compared, only of IDs

repeat with eachCue in cuesToProcess

set end of containedIDs to uniqueID of eachCue

end repeat

set i to 0

repeat until i = (count containedIDs)

set eachID to item (i + 1) of containedIDs

set eachCue to cue id eachID

if q type of eachCue is "Group" then

repeat with eachChild in cues of eachCue

set childID to uniqueID of eachChild

if childID is not in containedIDs then

set end of cuesToProcess to eachChild -- Cue is not already selected, so add it for processing

set end of containedIDs to childID

end if

end repeat

end if

set i to i + 1

end repeat

repeat with eachCue in cuesToProcess

if q type of eachCue is "midi" then

if (pre wait elapsed of eachCue) = (pre wait of eachCue) then --MIDI cue  is before 'load to time' time

set the armed of eachCue to false

else --MIDI cue  is after 'load to time' time

set the armed of eachCue to true

end if

end if

end repeat

else --cue is not running or paused  and is a Timeline Group Cue and is NOT  loaded to time so will play from beginning

set aFlag to true

repeat with eachCue in cuesToProcess

if q type of eachCue is "midi" then

set the armed of eachCue to true

end if

end repeat

end if

go

delay 0.5

--if cues in the timeline group have been disarmed then arm them again.

if aFlag is false then

repeat with eachCue in cuesToProcess

if q type of eachCue is "midi" then

set the armed of eachCue to true

end if

end repeat

end if

end tell


Screen recording and Workspace attached contains "Aces High"  by Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 4.0 License
http://creativecommons.org/licenses/by/4.0/



GO ignoring any MIDI cues prior to a group load to time v3 with added nested group Handling 3.mov
GO ignoring any MIDI cues prior to a group load to time v3 with added nested group Handling 3.zip

Paul

unread,
Apr 29, 2026, 3:13:52 PM (5 days ago) Apr 29
to QLab
Thanks for your comments, Mic, all good points. 
I should have made it clear the first line was an example, not complete and it would fail because of the undefined variable.

Yes, my script has a bug in that it should not disarm cues outside the selected group, so that line should be
        set armed of every cue whose q type is cueType and parent is myGroup and pre wait < loadTime to false

In responding to these sort of questions, it's hard to know how complex to make any script or how much error checking to include, without know if the user want's an immediate practical solution or is interesting in learning how to solve the problem. (I suspect the first here).

I used the dialog box for getting the load time because it makes the user process 1 step instead of 3 steps-  CMD-T, set the load time and then run the script.
I also assumed, maybe wrongly, the user has some agency so that if they is willing to disarm cues they would know to re-arm them again. And on the basis of my experience of rehearsals that they restart from the same place a few times and then run the whole number. If you run my script with load time as 0 it will rearm all the cues in a group. Maybe a third "Reset" button on the dialog box would help here. I'm also guessing that the pre wait times may be changed during rehearsing that section, so the workspace is still being worked on.

Using spacebar trigger for the go and determining the load to time is another approach but one I thought was more complicated as this was intended to be used only in rehearsal, when edits to the workspace were likely. When rehearsing just choreograph or music I would normally expect MSC/Network output triggers ('overrides') to be turned off so the lighting team could plot at the same time.
Another possible approach in rehearsal would be to use slice markers for common rehearsal start points and then present the user with a list to choose from. Again more complicated than the question called for I felt.

The whole issue of nested groups is as you said much more complicated, so I avoided it, to keep the answer manageable.


Thomas Wacker

unread,
Apr 29, 2026, 4:45:37 PM (5 days ago) Apr 29
to QLab
Thanks again for everybody involved here, and especially for Mic !
Paul is right : I wanted a practical solution, as although I understand the logic behind apple script, I never get the syntax right...
In this case, I was hoping for a script that would do the disarm job via hot key - I would then arm again via "undo".
The solution Mic came up with not only included the "arm again", but by hijacking the space bar, it also cirumvented the need for a dedicated hot key.
I am amazed by how elegant this solution is...
Now there still remains one thing that bugs me when handling a large amount of MSC cues tied to an audio file :
Of course I have to manually enter the light cue number in the MSC settings, but I have to do it again in the name section of the cue, to be able to navigate the 
timeline to the (by the light guy) wanted point in the timeline group.
Is there a way to hijack the "enter" key to paste the (light) cue number I type in MSC settings as the name for this cue ?

micpool

unread,
Apr 29, 2026, 5:56:39 PM (5 days ago) Apr 29
to QLab

I must be misunderstanding your question, but the default name of an MSC Midi Cue is the Q Number (and list number if present)

Screenshot 2026-04-29 at 22.56.10.png
Screen Recording 2026-04-29 at 22.54.48.mov

Rich Walsh

unread,
Apr 29, 2026, 6:12:31 PM (5 days ago) Apr 29
to ql...@googlegroups.com
On 29 Apr 2026, at 21:45, Thomas Wacker <tomwac...@gmail.com> wrote:

Is there a way to hijack the "enter" key to paste the (light) cue number I type in MSC settings as the name for this cue ?

When I test this the default cue name includes the number entered into the “Q Number” box…?


Anyway, I have NOT tested this at all thoroughly, but based on Mic’s work I would do this:

-- Hijack the GO mechanism and disarm certain cues if loaded past when a cue has been loaded to time

-- Concept by Mic Pool


set userCueTypes to {"Network", "MIDI", "Start"} -- Choose which cue types to disarm

set userZeroLengthOnly to true -- Choose whether to only disarm cues with zero duration


tell application id "com.figure53.QLab.5" to tell front workspace

set theCue to playhead of current cue list

if theCue is missing value then -- Mic's suite of criteria to see if GO should just behave normally

return

else if running of theCue or paused of theCue then

go

return

else if q type of theCue is not "Group" then

go

return

else if mode of theCue is not timeline then

go

return

else if action elapsed of theCue is 0 then

go

return

end if

set cuesToProcess to cues of theCue

set processedIDs to {}

set i to 0

repeat until i = (count cuesToProcess)

set eachCue to item (i + 1) of cuesToProcess

set eachType to q type of eachCue

if eachType is "Group" then -- Recursively add nested Groups

set cuesToProcess to cuesToProcess & cues of eachCue

else if eachType is in userCueTypes then -- Only process specific cue types

if not userZeroLengthOnly or duration of eachCue is 0 then

if pre wait elapsed of eachCue = pre wait of eachCue then -- Mic's clever test for if a cue is loaded past

if armed of eachCue then -- Only change state of armed cues (ie: protect any other edits already done)

set armed of eachCue to false

set end of processedIDs to uniqueID of eachCue

end if

end if

end if

end if

set i to i + 1

end repeat

go

delay 0.5 -- Wait a moment before resetting; also acts as debounce

repeat with eachID in processedIDs -- Reset the cues that were touched

set armed of cue id eachID to true

end repeat

end tell


First use of recursive burrowing: https://groups.google.com/g/qlab/c/ldbL71jGawA/m/y1jwhoBmBgAJ. Example of doing something to the cues whilst burrowing: https://wiki.allthatyouhear.com/doku.php?id=home#clear_cue_number. A bit of stuff about this loading-to-time issue and how timecode settings have a choice about what to do here: https://groups.google.com/g/qlab/c/PEnNa3WscUU/m/cTjFOu1MAwAJ.

Just for fun I chucked this into Google’s AI Mode search tool thing. It did understand what I was trying to do AND spot a typo – but all of its further suggestions actually made the script worse!

The show I send out every year has to deal with this precise problem, but there’s a custom script to load to time based on a dropdown list of reset points retrieved dynamically from an adjacent text file, so it’s a bit more fiddly. I keep all the “outboard” (MIDI, MSC, OSC) in separate cue lists – hence the thing about Start Cues. Mic’s idea is really clever though as a general rehearsal tool, in the absence of a choice about not firing all the MSC that happened earlier in the cue at once…

Rich

micpool

unread,
Apr 29, 2026, 7:24:44 PM (5 days ago) Apr 29
to QLab
Thanks, Rich, those are all good optimisations. Tom and anyone else interested in doing similar, this is the script to use!

Mic

Thomas Wacker

unread,
Apr 30, 2026, 10:11:07 AM (4 days ago) Apr 30
to QLab

"When I test this the default cue name includes the number entered into the “Q Number” box…?"
It was my fault... I changed the somewhat lengthy default name to "cue". When I leave the default name as it is, the Q number is indeed added to the cue name.
As for the "load to time" : using Rich's version now. 
I only tested it at home and no problems so far.
As for real world scenario : next show is in June, but I have no doubt, this script will get the job done...
thanks again
Reply all
Reply to author
Forward
0 new messages