Okay, here is a little parred down script I wrote to show what I’m trying to do. By pressing a button, the user will set off a loop.
However, I want to add a button for the user to exit the loop, but the entire interface becomes completely unresponsive whilst inside the loop. Is there a way to keep a script responsive to user input while running?
on clicked theObject
repeat with i from 1 to 20
DoSomething()
delay 0.5
end repeat
end clicked
I have an AStudio project that I can stop while its running. It’s really complex, and requires a UI.
The interface has a button that is called “start” and when the user presses it, it changes to “stop”.
In the code I have a variable, called something like “continue”. When the loop starts, Continue is set to true. It checks if state of Continue multiple times through everything that happens. It’s a lot of IF-Then statements. Like this:
If Continue is true then… [do all this stuff func 1] … end if
If Continue is true then… [do all this stuff func 2] … end if
If Continue is true then… [do all this stuff func 3] … end if
The user can press the Stop button at any time. This sets the Continue variable to FALSE. So then the next time Continue’s trueness is checked, it’s false and the rest of the script stops. The downside to this is that the function that is currently running will keep going until it’s over and the next Continue check is fired. In my app there’s a lot going on and sometimes the UI is not very responsive, but the button is clickable.
You also could use a While loop
repeat while myContinue is true
[do something]
end repeat
In either of these cases the largest hurdle is when does the Continue check happen, and how quickly is your script running that it can terminate when you need it to.
This is my way of doing it, there might be a more elegant solution.
I see. The problem that I have is that my loop contains a “delay” command. Whilst the half second delay is happening, the program doesn’t check the interface at all.
So what I’m really looking for is a way to create a program that can fire off a command every half second without ignoring the interface.
That’s exactly the principle I was going for, but it doesn’t work. The “Stop” button will simply not respond as long as the repeat loop is going. I made a little project based on your script that you can download here and it clearly doesn’t work. If anybody can get this to work please chime in, but I have a sneaky suspicion that we’re looking at a fundamental shortcoming of ASS…
AS cannot handle multi-threading, so while one handler is running, no action on any element is registered by AS.
I think the magic must lie in the Interface Builder work – the response to the button click must be done outside of AS. Hence me asking for the sample project. It would give us a trick to add a little multithreading to AS.
LG
P.S.: Email of this subscribed topic says there was a reply by “Matt-Boy”, it included a similar script as MRoig’s project. Apparently it has been deleted. --?
Yeah, I just copied Matt-Boy’s script in an actual ASS project just to see what would happen. As I expected, it doesn’t work. Not too sure why his post disappeared.
I was thinking of a solution where the applescript in ASS would run a second applescript. The first one taking care of the interface, the second one doing the actual work. Basically creating two threads by creating two apps. A bit ugly, and I’m not sure how it would work, if at all.
Could it be that SuperMacGuy uses some Obj-C to get his projects to work?
that means the idle handler executes one iteration of the repeat loop,
it could look like
property idx : 0
property goOn : false
on clicked theObject
if name of theObject is "Start" then
set idx to 20
set goOn to true
else if name of theObject is "Stop" then
set goOn to false
end if
end clicked
on idle
if goOn and idx > 0 then
DoSomething()
set idx to idx - 1
return 0.5
end if
end idle
Thanks Stefan, that does the trick. I never looked at the “on idle” handler, assuming that it would take maybe 30 seconds or so before it got called, but it’s pretty much instantaneous. And it hardly seems to consume any CPU time at all.
The only thing to remember is to tick the “idle” event handler for the application in Interface Builder.
Wow, you guys are fast. I posted a response and then less than a minute later realized that it wasn’t much different from what SuperMacGuy had posted and probably wouldn’t help so I deleted it.
I have an Applescript Studio Application where a progress panel is shown once the script initiates. On the progress panel, a I have a progress indicator bar and a “Stop” button, which I need to abort the script when clicked.
The script involves a repeat loop in MS Powerpoint, so I am not sure how the stop button can be implemented to abort the script.
I thought this may have worked, but not so:
on clicked theObject
if name of theObject is "Stop" then
quit -- even tried error number -128 but no luck
close panel window "progress panel"
...
I know I can force quite Powerpoint, but would rather not do that. Hoping for some suggestions and thanks.
With a little searching in this section of the forum (sorry, impatience on my part), this works well:
property stop_loop: false
on clicked theObject
if the name of theObject is "stop" then
set stop_loop to true
end clicked
repeat
if stop_loop is true then
end repeat
set stop_loop to false
end if
....
end repeat
Soon there will be a long article how to achieve this in unscripted forum. If you can’t wait you can mail me and will send a example project how to do this right. Because Applescript applications has only a main thread you’re not able to create a second thread but you can fork your code to a new process and run it in the background. I don’t like those repeat loops because they create many overhead that’s not necessary.