Passing a lister variable from a command script to an event script?

Just in case your script command uses "GO", there's a NOSCRIPT switch to not trigger any events.
Regarding the variables, it's really a pity that variables are "snapshotted" and don't reflect external updates. o(

You can try to use the "SetVar" add-in to handle setting of the variable and testing if it exists. SetVar always runs externally and has a fresh copy of the variables set by a another script command or event (if that makes use of SetVar as well or is finished already).

Maybe it's different for global variables, but I doubt it. Still worth a test I think. With SetVar it should definitely work with global variables, if you need to scope your variable to a lister, you probably can still do that with a global by naming it "MyCommandRunning." and test within your event based script for existence of a global with that specific name. SetVar has an EXISTS switch and if run via RunCommand() will return error/success. Getting an actual value from "outside" is unfortunately not possible, but you probably can get away with that.

I think the easiest way to access some refreshed lister variables is to let Lister.Update() method synchronizes them. I actually already checked this but it looks as the update method does not maintain the changed variables. Maybe Jon and Leo think this is a good reason to make this happen!?

Is there any other trick, how a command script can pass a variable to an event script, that was triggered by the command script? :slight_smile:

You could use an environment variable.

In the command:

Set wshShell = CreateObject( "WScript.Shell" ) Set wshEnv = wshShell.Environment( "Process" ) wshEnv( "MyScript_" & sourceTab ) = "1" ' Include the sourceTab ID as part of the name for uniqueness ... <trigger event> ... wshEnv.Remove( "MyScript_" & sourceTab )

In the event:

Set wshShell = CreateObject( "WScript.Shell" ) Set wshEnv = wshShell.Environment( "Process" ) if (wshEnv( "MyScript_" & sourceTab ) = "1") then ... end if

(More about env vars in VBScript here and here and here.)

You're kidding us, hu? o)

Yes, I researched and wrote that code as a joke. ¬.¬

One thing to get used to with programming is stuff doesn't always work how you want, but there's usually another way to get things done with what you have, which gets you there faster than waiting for ideal methods that do exactly what you want to be implemented or discovered.

I adapted it to a JavaScript snippet, that works fine as workaround at least for my issue ...

[code]function getEnvVar(name) {
var wsh = new ActiveXObject("WScript.Shell");
var objEnv = wsh.Environment("Process");
return objEnv(name);
}

function setEnvVar(name, value) {
var wsh = new ActiveXObject("WScript.Shell");
var objEnv = wsh.Environment("Process");
objEnv(name) = value;
}

function clearEnvVar(name) {
var wsh = new ActiveXObject("WScript.Shell");
var objEnv = wsh.Environment("Process");
objEnv.Remove(name);
}
[/code]

@leo
Please excuse! o) But seriously, you gave us a ton of different variable collections, scopes, methods and variable types to work with, and then we are suggested to make use of env-vars. It's like repairing a space shuttle with dinosaur bones! o) This solution really isn't up to DOs standards (take that as a compliment).

That "snapshot" approach for variable values is quite a hurdle to get specific things going and hit me right from the beginning. You actually don't expect a global variable to work like that. I'll keep pressing my thumbs for some kind of solution to this. Especially since scriptcommands can only return success/error right now and the env-var approach won't fit all scenarios.

I'm surprised that using tab/lister variables doesn't work, since they actually aren't "snapshotted" like that.

Can you give me an easy way to reproduce this?

You can see for yourself that they can be used to pass values between scripts, e.g. if you have a user command called ScriptTest:

[code]@script vbscript

Function OnClick(ByRef ClickData)
DOpus.Output "incrementing"
ClickData.func.sourcetab.vars.Set "blah", ClickData.func.sourcetab.vars.Get("blah") + 1
End Function
[/code]

And a button that invokes the user command:

[code]@script vbscript

Function OnClick(ByRef ClickData)
If Not ClickData.func.sourcetab.vars.Exists("blah") Then
ClickData.func.sourcetab.vars.Set "blah", 0
End If

DOpus.Output "before = " & ClickData.func.sourcetab.vars.Get("blah")
ClickData.func.command.RunCommand "ScriptTest"
DOpus.Output "after = " & ClickData.func.sourcetab.vars.Get("blah")

End Function[/code]

Clicking the button repeatedly will show that the value is incremented in the user command script and the new value does make it back to the button script:

before = 0 incrementing after = 1 before = 1 incrementing after = 2 before = 2 incrementing after = 3 before = 3 incrementing after = 4

Good morning Jon! o) Do you actually mean script- or user-commands?

If they are not snapshotted I guess it may be a race condition.

We seen this issue the first time when trying to suspend an event script if this was triggered by a certain command script.

In other words, we have a command script that opens a new tab and an event script that shall be suspended if the event was triggered by this certain command script. The most obvious solution is to introduce a lister variable that can be used to temporarily suspend the event script.

Here is the event script:

function OnOpenTab(data) {
	DOpus.output("open tab: " + data.tab.path);
	DOpus.output("Var 'SuspendEventScript' exists: " + data.tab.lister.vars.Exists("SuspendEventScript"));
}

function OnActivateTab(data){
	DOpus.output("activate tab: " + data.newtab.path);
	DOpus.output("Var 'SuspendEventScript' exists: " + data.newtab.lister.vars.Exists("SuspendEventScript"));
}

function OnSourceDestChange(data){
	DOpus.output("open tab: " + data.tab.path);
	DOpus.output("Var 'SuspendEventScript' exists: " + data.tab.lister.vars.Exists("SuspendEventScript"));
}

Here is the command script:

@script jscript

function OnClick(clickData) {
	clickData.func.sourcetab.lister.vars.Set("SuspendEventScript", true);
	var cmd = clickData.func.command;
	cmd.RunCommand("Go C:\ NEWTAB TABPOS=+1");
	clickData.func.sourcetab.lister.vars.Delete("SuspendEventScript");
}

And here is the output:

Test: activate tab: C:\
Test: open tab: C:\
Test: Var 'SuspendEventScript' exists: false
Test: Var 'SuspendEventScript' exists: false
Test: open tab: C:\
Test: activate tab: C:\
Test: Var 'SuspendEventScript' exists: false
Test: Var 'SuspendEventScript' exists: false
Test: activate tab: C:\
Test: open tab: C:\
Test: Var 'SuspendEventScript' exists: false
Test: Var 'SuspendEventScript' exists: false
Test: activate tab: C:\
Test: open tab: C:\
Test: activate tab: C:\
Test: open tab: C:\
Test: Var 'SuspendEventScript' exists: true
Test: Var 'SuspendEventScript' exists: true
Test: Var 'SuspendEventScript' exists: true
Test: Var 'SuspendEventScript' exists: true
Test: activate tab: C:\
Test: open tab: C:\
Test: Var 'SuspendEventScript' exists: false
Test: Var 'SuspendEventScript' exists: false

Since it pretty much looks like a race condition, this thread may be related:

Obviously the cmd.RunCommand(...) returns before the event handling was finished. Thus the variable is cleared by the command script thread before the event script thread has read it.

Unfortunately in our real world example the variable can not be cleared at the end of the event script (instead of the command script) because there are 0..n tabs affected and you do not know which event notification is actually the last.

Havr you tried using the NOSCRIPT argument mentioned in the first reply?

My example uses the Go command for demonstration purposes only. Actually the scripts doing much more complex things.

For example one script toggles the visibility of the different panes (you certainly remember the toggle source display to fullscreen script). If you re-enable all lister components (e.g. enabling the dual display by remembering the tabs) there will be many different events triggered leading to weird effects. Thus it would be the best to introduce a global lister variable that can be evaluated by all affected event scripts to decide whether these events are indirectly triggered by a complex command script or by common user interactions.

It's never going to work when some events are asynchronous to the things that trigger them (and this will be true for at least some events no matter what happens). It might be better to re-think how you're doing whatever you're doing.

A) Script "maximize source pane"

:sunglasses: Script "viewer pane follow focus"

The script A (maximize source pane) itself works awesome and the same applies to script B (viewer pane follow focus). Unfortunately if both are enabled together there are some intereferences since script B is triggered multiple times by script A. Actually I already have seen broken DOpus layouts and some freezes due to these weird interactions of both scripts. Thus it would be nice to simply suspend script B as long as script A is running. Currently I use an artifical delay in the command script and these environment variable tricks in the event script which reduces the seen issues to an acceptable amount.

BTW: This thread seems to be related to this topic too ...

I also have seen effects where script commands returns even if the user interface does not reflect the current state. This obviously leads to unexpected issues if the succeeding script code relies on these changes.

Yep. o)

I think what you really need, if you can't re-think how the scripts work together or combine them into one coordinated script, is to be able to attach variables (or "argument" strings or something similar) to events, so the information you feed in to a command exists in the context of the command and any events that it triggers, whether or not they are asynchronous.

But that is outside the scope of things for now.

Some events will always be asynchronous to whatever triggered them, there's no getting around that.

I successfully used a tab scoped variable to pass data from a scriptcommand back into a script button now.
Thanks for pointing out the difference to the global variable stack, that's quite important to know about.

So with lister/tab scoped variables working like this, the situation is more friendly and offers new tricks! Thank you! o)

I also had some tests whether environment variables not only work, but how they compare to when using DO internals.
I didn't found much difference, setting an env-variable only takes about 3-10 milliseconds here, I expected longer delays.

For anybody in need to deal with env-variables, this is a little wrapper to easify general usage. It defaults to "Process" scope, but can be overriden for each method-call, it also picks up a global shell object, to not instantiate a new copy unecessarily.

/////////////////////////////////////////////////////////////////////////////// function EnvMagic(scope){ //v0.1 this.scope = scope || "Process"; try{this.shell=shell} catch(e){this.shell=new ActiveXObject("WScript.Shell")}; this.env = this.shell.Environment(this.scope); /////////////////////////////////////////////////////////////////////////// this.Set = function (varname, value, scope){ var env = scope?this.shell.Environment(scope):this.env; env(varname) = value; return value; } /////////////////////////////////////////////////////////////////////////// this.Get = function (varname, scope){ var env = scope?this.shell.Environment(scope):this.env; return env(varname); } /////////////////////////////////////////////////////////////////////////// this.Remove = function (varname, scope){ var env = scope?this.shell.Environment(scope):this.env; return env.Remove(varname); } /////////////////////////////////////////////////////////////////////////// this.Expand = function (varString){ return this.shell.ExpandEnvironmentStrings(varString); } }
While playing with Shell.Environment, I noticed that the devs decided to use the same syntax and style you guys "invented".
Assigning values to method calls is still odd to me (visually), but does not seem to be specific to DO anymore. o))