Applescript app with multiple buttons/multiple scriptlets

Hello all,

I’m squeaky green brand spankin’ new here, so please go easy on me. I’ve searched, I’ve thought, I’ve noodled, I’ve tested all to no avail. I can’t find my answer so I’m asking. Note: This question is for a project due MONDAY - a real live double clickable tiny little app that does something. Help! Also, forgive me if this is long…I’m trying to be thorough. :slight_smile:

I have a little app. I’ve built it and it works, and I’m trying to make an advancement. The little app presents a user with a window with some buttons. Each button is a toggle, and it sends an apple event to an application running on another machine when the user clicks the first time(i.e. toggles it on), then sends a different apple event to an application running on another machine when they click the button again(i.e. toggles it back off). That much of my app is working fabulously.

Here’s the problem: I have the “target” machine hard coded in the script for each button, and now I want to go back and take that hard declaration out and use a variable I can edit once. Someday I’ll turn that one reusable variable containing the “eppc://blah.blah.blah.local” target into a spiffy text entry field for the user, but not now. I don’t mind changing it in ONE place and rebuilding the app.

I believe I need to declare a property somewhere like <property targetMac : “eppc://blah.blah.blah.local”>, and I’ve done exactly that. I put that in a scriptlet called “start”, and have tried attaching to all sorts of things: the only window that opens (at launch), the various application event handlers, et cetera. I’ve even followed the property delcaration wtih a little dialog box to read it back to me after it’s set.

It seems that I can never get that property as a variable in any other script. I click on a button, and the script that should be getting it’s "“eppc://blah.blah.blah.local” string from the property I called out always errors with “the variable targetMac is not defined”.

What I DO know: A property is a variable that isn’t disposed of when a script ends, and a variable is gone at the end of the script it’s created in. So, a property is what I want here yes? Create the property in some sort of startup script, and every other script (the ones attached to the 26 buttons…) can use it, right? It seems not.

What I DON’T know (too much to list here really…): I’m not sure exactly what script runs first, how I make sure the script that runs first doesn’t somehow end and throw away all it’s properties (which is what it seems like is going on), and make it so for now I can change this property in one place to glue this application to a specific remote machine as a target to send it’s remote apple events to.

I’m painfully aware that I could probably do the whole thing without single scripts for every button and all that, but my skill set is so low and so new with this sort of endeavor this way works for me. And I did manage to get a program I can put in front of someone and they can click away and the results of their clicks are exactly as expected.

Call for the question: If there’s multiple scripts using a variable, what script should set up a property and what event should it be handled by? Moreover, is it even possible and should I give up on the idea until I learn how to boil the 26 control buttons in one window to a single script?

Thanks in advance for your help and patience, and following is some of the stuff from my xCode project and the scripts.

Here’s one of my button scripts that works great(ignore the “event” stuff…it’s very specific to the product I’m talking to):

on clicked theObject
	-- the state you're after is the state POST click...
	-- toggles start at 0, go to 1, go back to zero
	if state of theObject is 1 then ¬
		tell application "Visionary 3D" of machine "eppc://G5-2x2g-Office.local"
			Event "recall macro 602"
		end tell
	if state of theObject is 0 then ¬
		tell application "Visionary 3D" of machine "eppc://G5-2x2g-Office.local"
			Event "recall macro 642"
		end tell
end clicked

Here’s what I want it to look like:

on clicked theObject
	-- targetMac was set in start.applescript in a galaxy far far away...
	-- the state you're after is the state POST click...
	-- buttons start at 0, go to 1, go back to zero
	if state of theObject is 1 then ¬
		tell application "Visionary 3D" of machine targetMac
			event "recall macro 601"
		end tell
	if state of theObject is 0 then ¬
		tell application "Visionary 3D" of machine targetMac
			event "recall macro 641"
		end tell
	-- set visible of theObject to false (maybe hide the used button someday?)
end clicked

Lather, rinse, repeat for 26 buttons.

Here’s the start script I can’t figure out where to attach, or how to ensure it’s always there supplying it’s property to the worker bee scripts. You can see me goofing around with what event should handle it:

property targetMac : "eppc://G5-2x2g-Office.local"
(*
on opened theObject
	display dialog "your target machine is called " & targetMac buttons {"Thanks"} default button 1
end opened
*)
on will finish launching theObject -- just to check...
	display dialog "your target machine is called " & targetMac buttons {"Thanks"} default button 1
end will finish launching

Thanks for reading,

WM

Model: various
AppleScript: whatever Xcode 2.1.1 uses
Browser: Safari 417.8
Operating System: Mac OS X (10.4)

WM,

Properties in AppleScript Studio (ASS) behave a little different than they do in AppleScript scripts. In the later, they are available to the entire script AND hold their value from one run to the next (provided you have recompiled it). In ASS properties are available to the entire script file in which they reside. They do not retain their modified values from 1 run to the next and they are not available to other script files in the same project.

My first thought is to use the User Defaults system. User Defaults is (roughly) equivalent to properties retaining their values in AppleScript script files (the later example in the paragraph above). Every script file in your ASS project can access the User Defaults. Your start.applescript file could be used to set/update the defaults for the rest of the scripts. On a side note, User Defaults are how preferences are utilitized in many applications.

Hopefully this will get you pointed in the right direction.

Good Luck,
Brad Bumgarner, CTA

Thank you Brad, what you wrote is super helpful and I feel like I’m on the road to a solution. I’ve just spent HOURS sifting thru the boards here about writing to and reading from the user defaults, but unfortunately it’s just not working. Here’s what I have:

startup script:

on will finish launching theObject
	make new default entry at end of default entries of user defaults with properties 
	{name:"primaryMac", contents:"eppc://G5-2x2g-Office.local"}
	call method "synchronize" of object user defaults
end will finish launching

one of the button scripts:

on clicked theObject
	set targetMac to contents of default entry "primaryMac" of user defaults -- as string?
	if state of theObject is 1 then ¬
		tell application "Visionary 3D" of machine targetMac
			event "recall macro 601"
		end tell
	if state of theObject is 0 then ¬
		tell application "Visionary 3D" of machine targetMac
			event "recall macro 641"
		end tell
end clicked

As far as I can tell, this is exactly how many other posters say it needs to happen. However, I do it, run it, click on the button and get the error “Visionary 3D got an error: Can’t get «class eveE» “recall macro 601”. (-1728)”.

I’m nearing the end of my rope. A colleague of mine tells me he failed completely at variables/properties/user defaults this way, and resorted to writing the value to a text file and reading it back when needed. I think that’s a VERY bad idea for my application, because it would cause reading from the file on the disk over and over and over again with every click on and click off of every button and their resulting scripts running…

What in the world have I missed? The most frustrating part is I was able to successfully see the value I wanted residing in the “targetMac” variable when I did a little test script that simply made the button show a dialog and read back the contents of the Variable. I’m left wondering if the variable that I’m filling with the value from the user defaults is ending up as something OTHER than text, or if there’s quotes fouled up or not ending up there when I fill the variable with the value from the user default item.

Help!

Cheers,

WM

I have never used the “synchronize” method before. I don’t know if that is needed or not. Have you read the section regarding user defaults in the Applescript Studio Terminology Reference Manual? I spend so much time in that manual that I have it bookmarked in the help reference library :slight_smile:

Have you tried putting a Log statment in your code so that you can see what targetMac contains? I added the log statement where I’d check the contents of targetMac and I modified your code slightly. I removed the “contuation” characters that were in your if-then-else statement and I change your if-then-else to an if-then-else if statement.

on clicked theObject
	set targetMac to contents of default entry "primaryMac" of user defaults -- as string?
	log targetMac -- doing this will put the contents of targetMac in the run log
	if state of theObject is 1 then
		tell application "Visionary 3D" of machine targetMac
			event "recall macro 601"
		end tell
	else if state of theObject is 0 then
		tell application "Visionary 3D" of machine targetMac
			event "recall macro 641"
		end tell
	end if
end clicked

Brad Bumgarner, CTA

HI again Brad,

Thank you again for the help! For the better part of two days I couldn’t figure out why the interpreter was whining about my "else"s…now I see that the tell block has to close and open again! :lol:

However, the variable logs just fine, but it still doesn’t work. It’s as if the “of machine” part is NOT compatible with variables, no matter where or how you set them up. I’m not really sure what else to do about it…the variable is right, but “of machine” doesn’t see a real live quote right there on the line and tries to meld it into the title of the class or something, and the “event” gets fouled up and errors out.

Another question: how would one store the text string WITH the close and open quotes in the variable? I feel like the problem is so simple and stupid like targetMac is returning eppc://blah when it needs to be returning open-quotes-eppc://blah-closed-quotes.

Isn’t there some qualifier like “quoted form of” or something to get text out of a variable WITH quotes?

Jeez this simple thing is far more difficult than it should be.

I’ve put it together in the bad way and done a test where the button script itself goes ahead and writes to disk so there’s no question about what was run when. It seems like, as I said, maybe “machine” just plain doesn’t work wtih variables.

on clicked theObject
	set targetMac to contents of default entry "primaryMac" of user defaults
	log targetMac -- doing this will put the contents of targetMac in the run log
	if state of theObject is 1 then
		log "sending macro 601-pos 1 ON to " & targetMac
		tell application "Visionary 3D" of machine targetMac
			event "recall macro 601"
		end tell
	else if state of theObject is 0 then
		log "sending macro 641-pos 1 off to " & targetMac
		tell application "Visionary 3D" of machine targetMac
			event "recall macro 641"
		end tell
	end if
end clicked

The log reflects with the two button presses:
2006-01-20 16:47:59.540 dond06[8022] “eppc://G5-2x2g-Office.local”
2006-01-20 16:47:59.547 dond06[8022] “sending macro 601-pos 1 ON to eppc://G5-2x2g-Office.local”
2006-01-20 16:48:17.562 dond06[8022] “eppc://G5-2x2g-Office.local”
2006-01-20 16:48:17.575 dond06[8022] “sending macro 641-pos 1 off to eppc://G5-2x2g-Office.local”

but I get the fouled up class error:
Visionary 3D got an error: Can’t get «class eveE» “recall macro 601”. (-1728)
…and nothing shows up in the target application’s log, which always logs external apple events coming in.

AAAAAArgh!

Back to searching the docs in vain. Any more help would sure be appreciated.

WM

Here’s an article I found over at MacOSXhints that hints (grin) at the fact that variables are indeed useable with the “of machine” qualifier:
http://www.macosxhints.com/article.php?story=2005021010302911

But I’ll be damned if it’s working in the scripts in question.

WM

Wish I had more to offer you, but I haven’t done anything of this nature and I don’t know what’s causing your problems.

Good luck,
Brad Bumgarner, CTA

Okay, final attempt. This script should work, but it doesn’t.

on clicked theObject
	make new default entry at end of default entries of user defaults with properties ¬
		{name:"primaryMac", contents:"eppc://G5-2x2g-Office.local"}
	log "making default entry..."
	set targetMac to contents of default entry "primaryMac" of user defaults
	log targetMac -- doing this will put the contents of targetMac in the run log
	if state of theObject is 1 then
		log "sending macro 601-pos 1 ON to " & targetMac
		tell application "Visionary 3D" of machine targetMac
			-- does the variable after of machine have to be enclosed in something?
			event "recall macro 601"
		end tell
	else if state of theObject is 0 then
		log "sending macro 641-pos 1 off to " & targetMac
		tell application "Visionary 3D" of machine targetMac
			event "recall macro 641"
		end tell
	end if
end clicked

Why in the holy heck does “of machine” not accept a variable???

Thanks in advance,

WM

Greetings, WM.

Don’t give up yet… you’ll get it. :wink:

Are you certain that this same code works with a hard-coded address and not the variable one? It appears as if everything’s in order, and I can guarantee that using a variable in a call to another machine is possible. Try opening script editor and running the script below and confirm that it does work…

set theMachine to "eppc://G5-2x2g-Office.local"
tell application "Finder" of machine theMachine
	activate
	make new Finder window
	beep
end tell

If this doesn’t work, there are a bunch of details that may or may not be effecting whether your script runs correctly.

  1. I assume that you’ve enabled remote events on the target machine. Some functions require that you pass a valid username and password before the url in order to authorize the event. You may need to use something like…
set theMachine to "eppc://USERNAME:PASSWORD@G5-2x2g-Office.local"

Also note that some interactions are not allowed if the sending user is not the same user as the one currently logged in on the target machine.

  1. Because you’re working with a dictionary that resides on another computer, you may have to explicitly declare the application terms you want to include when sending the command. For example, if the first code above didn’t work, try this instead…
using terms from application "Finder"
	set theMachine to "eppc://G5-2x2g-Office.local"
	tell application "Finder" of machine theMachine
		make new Finder window
		beep
	end tell
end using terms from

This may seem a bit redundant, but because the app version that’s actually executing the code is on another computer and it’s not actually verifying whether the commands you’re sending it are valid, you may not be getting correct compilation or dictionary compliance. What sometimes happens, and may be happening in this case, is that when that you issue a command like event “recall macro 641” it’s approved by the compiler not as valid syntax for your target app, but just by the standard scripting syntax. An ‘event’ is a fundamental class, so it compiles even though the remote app which will eventually call it may not know how to handle this request. This may be compounded even more by putting the name of the machine in a variable, as there becomes no possible way for AS to verify if the syntax is valid until runtime. Although your code may compile and run without errors, you won’t know until it’s done running whether there was actually a problem… and it may not throw an error to tell you about it. So, you may need to install a copy of ‘Visionary 3D’ on the sending computer too, and then use the ‘using terms from application “Visionary 3D”’ block around your script to make sure that the command gets compiled correctly.

  1. In one of your posts, you mention that you get the error…

This may mean that Visionary 3D doesn’t actually respond to this command, or perhaps the ‘using terms from’ command noted above applies to this conflict. Make sure your spelling is solid, and see if compiling with it all enclosed in a ‘using terms from’ command helps this.

  1. Sometimes AS is picky about the class of data you’re passing to it. Although saying something like set theMachine to “eppc://G5-2x2g-Office.local” seems right to a human, and WE know it’s a string, the dumputer doesn’t always figure it out on it’s own. You may have to make an explicit cast to a type that the command understands, such as…
set theMachine to "eppc://G5-2x2g-Office.local"
-- and, then... --
set theMachine to (theMachine as string)
-- OR --
set theMachine to (theMachine as Unicode text)

I can’t say with any certainty if these ideas will solve anything, but start there and see if any solve your problem. As I said, the above Finder code works for me, so start there and confirm that you can get a known bit of working code with a universally available target up and running. Then start adding bits and pieces of your code back in and see at which point it begins to fail.

Good luck,
j

Hi all and Hi Jobu,

First I want to say that this board is the most civilized and helpful I think I’ve ever participated in - and I date back to hopping a uucp feed for usenet groups in 1989!!! Thank you now and in advance for the GREAT help and encouragement! The best part being jobu has given me a solution. Read on…I’m following along with jobu’s suggestions and getting some interesting results!

I copied this script into a new script editor window, and get an error before it will interpret and run:
“Syntax Error: Expected end of line, etc. but found class name.” and the editor selects “window” in the line “make new Finder window”.

So, I moved on to the next suggestion.

Fortunately the apps in question reside on both the source and target machines, and I’ve discovered that the first time I use my application and it asks for a username and password, if I put the pair in the keychain it never asks again and authenticates with a very short (like, 1 or 2 second) delay on each subsequent quit/launch. Works great.

Then, the glorious solution! Or, part of it anyway…

WooHoo! It works. As you pointed out it feels redundant because the apps in question reside on both machines, the user on both machines is the same (me), and the authentication is successfully handled by the keychain after the first one. Here’s what I see happening:

If ‘tell application’ is followed by a proper name of an app either local or remote, AND the ‘of machine’ qualifier is followed by a proper name, the dictionary is found and ‘using terms from’ isn’t needed. However, the ‘of machine’ qualifier is supplied by the contents of a variable, ‘tell application’ seems to lose it’s mind and forget what dictionary it should be using! ‘using terms from’ seems to in some way force the place the dictionary can be found into the scene in a more permanent way.

I dunno what I’m talking about, but I’d say it’s a bug in “tell application/of machine” in applescript itself.

SO…I’ve made great progress, it’s mostly working now, and of course I’ve uncovered a new problem I need guidance with: Now that I’ve taken out just about all the hard declarations of the app to target and the machine to target, I’m inches away from having the underlying structure to use a preferences window and never hard code the name of my targets in the app right? WRONG. Now, “using terms from” is refusing to take a variable!!! It’s refusing to take a variable in such an obnoxious way that the script won’t even compile. When the script looks like this:


if state of theObject is 1 then
		log "sending macro 601-pos 1 ON to app " & targetApp & targetMac
		using terms from application targetApp
			tell application targetApp of machine targetMac
				Event "recall macro 601"
			end tell
		end using terms from

I get the dreaded and not very well understood on my part error “Syntax Error: Can’t make some data into the expected type.” I think jobu is hot on the heels of this too unknowingly, as he went on to say

Exactly what it seems like is happening, right, wrong again. I tried changing the variable in this way, and the script still refuses to interpret due to the variable at teh end of “using terms from application ”

So. That’s where I’m stuck now. How in the world does one supply the name of an application to “using terms from application” as a variable?

As always, thanks again now and in advance - my app would be a mess of hard coded targets were it not for this insanely great board. Now, it’s 95% clean…

Looking forward to replies, and I’ve also posed this question in a thread from a few weeks ago with exactly the same question and problem: http://bbs.applescript.net/viewtopic.php?pid=51808

You’re just on a quest to make this as complicated as possible… aren’t you. :lol:

I tested this out and see what you mean. Ultimately, I think that you’re going to find that some key reference is going to need to be hard-coded somewhere, otherwise AS is going to just going to sh!t it’s pants. For the compiler to get it’s bearings it has to be able to look down the chain of events and actually be able to make a solid connection to hard data SOMEWHERE. It can’t be responsible for pre-compiling everything. Even though we know that in the end it will work out ok, AS isn’t so flexible, and needs to have a reference point to work from.

That said, I see you having two options. One, is to define your system and stick to it. By this, I mean pick a name for your program, why don’t ya’? :cool: You shouldn’t need to modify this reference programmatically. If you’re making multiple calls to the same code, but for different apps, it may be better to just add a new routine for each one. Compacting code can be a benefit in some situations, but here it may lead to more trouble than it’s worth.

Two, you could enclose your code in yet another command… a ‘run script’ command. This will force the code to be compiled at run time rather than precompiled, so you can put whatever you want in it and it WILL compile. Just make sure that what you put in the block is otherwise valid syntax, or you’ll get errors. For example, this should work…

run script "using terms from application \"" & targetApp & "\"
	tell application \"" & targetApp & "\" of machine \"" & targetMac & "\"
		event \"recall macro 601\"
	end tell
end using terms from"

I’m not sure I’d recommend doing it this way, but it does seem to want to work if you must do it this way.

j