Hi! I recently wrote a script where I got a 'requires UIelement' error and I remembered some discussion here about 'invalidate()', which I added to my script and it solved the issue.
That said, I don't know what it's doing or why, or if I'm using it redundantly.
example:
sf.ui.proTools.windows.invalidate().whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include File List').first.checkboxSet({
targetValue: "Disable",
});
sf.ui.proTools.windows.invalidate().whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include Clip List').first.checkboxSet({
targetValue: "Disable",
});
sf.ui.proTools.windows.invalidate().whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include Markers').first.checkboxSet({
targetValue: "Disable",
});
sf.ui.proTools.windows.invalidate().whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include Plug-In List').first.checkboxSet({
targetValue: "Disable",
});
This works, but do I need to invalidate each option or should I just invalidate the window before disabling the buttons? What is the 'best practice?', and where can we learn more about .invalidate()?
Thanks!
-D
Linked from:
- Clicking a Plug-In Window in Cubase 13
- Help setting track output
- Modifier key + Stream Deck combo implementation
- Modify clipboard value
- Problem restoring a track selection after opening or closing a folder track.
- Refreshing Pro Tools UI when new tracks are created
- How to Create a Macro to View ONLY Midi Clips in the PT Clips Bin
- Could not open popup menu...works...then it doesn't
- Invalidate
- ClipBeGone (delete when clips shorter than length).
- Is there a more elegant/compact way of coding this New Group script?
- Recurring Error with Name Searches
- Christian Scheuer @chrscheuer2019-11-28 08:55:17.379Z
Hi Dustin.
Thank you for this excellent question.
I hope I can shed some light on this.Quick answer
In the way that you're using invalidate specifically as quoted above, it is 100% redundant and should have no effect.
Long answer
Everything under
sf.
is a tree of nodes, that is each node has one parent node and can have many child nodes.
Every node is an in-memory representation of some external state. Thesf.ui.
sub-node is a collection of applications, each of which have subnodes that represent external UI elements.
There are also other node types, for example those that we use to describe Stream Decks, Vienna Ensemble Pro servers, Databases, Surfaces, Decks and so on.
Each of these types of nodes have different characteristics wrt what they hold in their cache and to which degree they can invalidate the cache on their own. For example the new Vienna Ensemble Pro cache holds everything in its cache until you invalidate it.
The UI node hierarchy developed over several years and I regret now that we never struck a "one-size-fits-all" approach to the caching. It works very very well to speed up the access to Pro Tools, but we added stuff later to the API that made caching a little more complex.What does invalidate do
The
invalidate()
call invalidates the node right to the left of your call, and all children of that node. So in your case you'd be invalidating all windows of Pro Tools accessed this way (but not the mainWindow cache since you access that by .mainWindow), and you wouldn't be invalidating the Pro Tools app cache, since that's one more layer up.In short, to sum up the story for UI caching today.
When you use an AxElementArray for querying/filtering, such as
node.windows.whoseXXX.is/contains/...first
- herewindows
is the AxElementArray property - then there is no caching under thenode
level.
This is to make it simple for people to create macros with this "query language" without having to think about invalidation.
So whenever you type something with...windows
or...children
or...checkBoxes
and stuff like that, then that will be cacheless.
What will be cached is when you use SoundFlow's strongly associated nodes. This is by design, and should always do that. For example there's no need to find the Automation Window every time you want to toggle the preview button. This cache works intelligently in that it keeps the cache of where the preview button is, but if the window disappeared, it will re-find the window as well. This is key to why SoundFlow works so fast with UI automation.Caching at the UI element property level
There are 2 hard coded property caches of UI elements that are also used to speed up things. This is
title
andvalue
. These two properties are great examples of something that should be invalidated. So to access the title of a UI element calledmyElement
you'd saymyElement.title.invalidate().value
- this would give you the updated title.General rules of invalidation
- Invalidate as little as possible, only when needed. This speeds up everything. Your invalidations might slow down built in SoundFlow commands, since by invalidating all the time you'll be removing their ability to use the cache.
- Invalidate as far to the right (as small a scope) as possible. For example, invalidate the UI element property instead of invalidating the whole UI element, if all you need is an updated property value.
- When debugging an issue, add invalidate all the way to the far left (right after the app) to figure out if it's a cache issue or not. If it is, move the invalidate() one step further down the hierarchy until it stops working, then trace it one step back.
Christian Scheuer @chrscheuer2019-11-28 09:02:34.172Z
The script where you got the
requires UIElement
I think is most likely to be a script where you're using SoundFlow's strongly associated nodes, and not the dynamic query nodes that you are depicting above.
It'd be great to have an example of such a "strong associated node" script, ie. one that's for example using:
sf.ui.proTools.automationWindow.previewButton
in lieu ofsf.ui.proTools.windows.whoseTitle.isNullOrEmpty.first.buttons.whoseTitle.is('Preview').first
(Note these are just syntax examples to illustrate the point, it's not the actual code one would use)Dustin Harris @Dustin_Harris
First of all, thank you for such an in-depth reply! So, this is the script where I encountered the 'requires UIElement' error. It seems I need to invalidate the 'Session Export Text' window (or it's options?) before I can reliably use the script. What should I do here?
sf.ui.proTools.appActivateMainWindow(); if (sf.ui.proTools.selectedTrackNames[0] == undefined) { log("No tracks are selected"); throw 0; } else {} sf.ui.proTools.menuClick({ menuPath: ["File", "Export", "Session Info as Text..."], }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include File List').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include Clip List').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include Markers').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include Plug-In List').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include Track EDL\'s').first.checkboxSet({ targetValue: "Enable", }); let exportSessionTextWin = sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first; exportSessionTextWin.radioButtons.whoseTitle.startsWith('Selected Tracks Only').first.elementClick(); exportSessionTextWin.buttons.whoseTitle.startsWith('OK').first.elementClick();