Why isn't a variable being set unless the script is run within Script Debugger's step debugger? - ms-word

I have the following AppleScript that works fine when I step through each line one at a time using Script Debugger, but reports that the _doc variable has a missing value when it gets the the save as line.
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
tell application "Finder"
set _folder to choose folder
set _files to files of _folder
repeat with _file in _files
if creator type of _file is "MSWD" then
tell application "Microsoft Word"
open _file
set _doc to document of window 1
save as _doc file format format text
close _doc
end tell
end if
end repeat
end tell
I've tried pausing for as long as 5 seconds using delay 5 with no change in the behavior. Why might this be happening and what can I do about it?

The answer to "why might this be happening" seems to be "it probably is a timing problem" and "because there are a number of problems when automating Word from both AppleScript and VBA on Mac that Microsoft has not yet fixed". I don't think there's much you can do about it except report to Microsoft via the Smiley mechanism or via word.uservoice.com. On uservoice, best to add your vote than existing request if there is one. But there is no reason at all to believe that Microsoft will even acknowledge or fix quite serious automation problems at the moment.
I had not come across the problem where you couldn't even set _doc to document of window 1 successfully. I was always able to use
set _doc to open _file
Here, I found a "delay 5" was enough to solve the problem you report, but there has also long been a problem where the "_doc" variable becomes invalid after a save as. I had a solution to that that iterates through the windows, so have put together this script which
a. should reduce the delay as much as possible
b. works on simple test data here but could do with improvement, especially on error checking
use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
tell application "Finder"
set _folder to choose folder
set _files to files of _folder
repeat with _file in _files
if creator type of _file is "MSWD" then
tell application "Microsoft Word"
--activate
set doc_count to (count of documents)
open _file
-- You have to set the maximum no. of repeats
-- high enough for your system
set repeats to 50
repeat until (count of documents) > doc_count or repeats = 0
set repeats to repeats - 1
end repeat
if (count of documents) > doc_count then
set _doc to (document (doc_count + 1))
set _windows to the windows
repeat with _window in _windows
if the full name of the document of _window is the full name of _doc then
set _windowIndex to the entry_index of _window
exit repeat
end if
end repeat
-- you need to create a new file name for each file.
-- this is a temporary kludge
set _textfilename to (posix full name of _doc) & ".txt"
save as _doc file name _textfilename file format format text
-- _doc now invalid, we need to "reconnect"
set _windows to the windows
repeat with _window in _windows
if the entry_index of _window is _windowIndex then
set _doc to the document of _window
exit repeat
end if
end repeat
close _doc saving no
else
-- you can make this more informative, and you might still need to
-- try to close something.
display dialog "Could not open document: " & POSIX path of _file
end if
end tell
end if
end repeat
end tell
Incidentally, when I tested on a single document using open recent file instead, there was never any problem getting a reference to the document. But that's useless for the kind of thing you're trying to do.

Related

programmatically press an enter key after starting .exe file in Matlab

In Matlab I can start external .exe files that sometime have a pop up that requires an enter key pressed. For example:
system('C:\Program Files (x86)\WinZip\WINZIP32.EXE')
will start Winzip, and then in order to use it you need to pass the "buy now" pop up window by pressing enter.
Now my problem is not with winzip, I only gave it as an example (i use winrar anyway :).
How can I programmatically press an enter key in Matlab in such cases ? (I use win 7)
Can an event listener be used to solve that?
EDIT: The java.awt.Robot class indeed works on explorer, but not on any software that has a pop up window with an OK button that needs to be pressed. I don't know why it doesn't work for that. I gave the winzip example because I assume everybody has winzip/winrar installed in their machine. The actual software I have is different and irrelevant for the question.
There is a way using Java from Matlab, specifically the java.awt.Robot class. See here.
Apparently there are two types of programs, regarding the way they work when called from Matlab with system('...'):
For some programs, Matlab waits until the program has finished before running the next statement. This happens for example with WinRAR (at least in my Windows 7 machine).
For other programs this doesn't happen, and Matlab proceeds with the next statement right after the external program has been started. An example of this type is explorer (the standard Windows file explorer).
Now, it is possible to return execution to Matlab immediately even for type 1 programs: just add & at the end of the string passed to system. This is standard in Linux Bash shell, and it also works in Windows, as discussed here.
So, you would proceed as follows:
robot = java.awt.Robot;
command = '"C:\Program Files (x86)\WinRAR\WinRAR"'; %// external program; full path
system([command ' &']); %// note: ' &' at the end
pause(5) %// allow some time for the external program to start
robot.keyPress (java.awt.event.KeyEvent.VK_ENTER); %// press "enter" key
robot.keyRelease (java.awt.event.KeyEvent.VK_ENTER); %// release "enter" key
If your applications are only on Windows platform, you can try using .net objects.
The SendWait method of the SendKeys objects allows to send virtually any key, or key combination, to the application which has the focus, including the "modifier" keys like Alt, Shift, Ctrl etc ...
The first thing to do is to import the .net library, then the full syntax to send the ENTER key would be:
NET.addAssembly('System.Windows.Forms');
System.Windows.Forms.SendKeys.SendWait('{ENTER}'); %// send the key "ENTER"
If you only do it once the full syntax is OK. If you plan to make extensive use of the command, you can help yourself with an anonymous helper function.
A little example with notepad
%% // import the .NET assembly and define helper function
NET.addAssembly('System.Windows.Forms');
sendkey = #(strkey) System.Windows.Forms.SendKeys.SendWait(strkey) ;
%% // prepare a few things to send to the notepad
str1 = 'Hello World' ;
str2 = 'OMG ... my notepad is alive' ;
file2save = [pwd '\SelfSaveTest.txt'] ;
if exist(file2save,'file')==2 ; delete(file2save) ; end %// this is just in case you run the test multiple times.
%% // go for it
%// write a few things, save the file then close it.
system('notepad &') ; %// Start notepad, without matlab waiting for the return value
sendkey(str1) %// send a full string to the notepad
sendkey('{ENTER}'); %// send the {ENTER} key
sendkey(str2) %// send another full string to the notepad
sendkey('{! 3}'); %// note how you can REPEAT a key send instruction
sendkey('%(FA)'); %// Send key combination to open the "save as..." dialog
pause(1) %// little pause to make sure your hard drive is ready before continuing
sendkey(file2save); %// Send the name (full path) of the file to save to the dialog
sendkey('{ENTER}'); %// validate
pause(3) %// just wait a bit so you can see you file is now saved (check the titlebar of the notepad)
sendkey('%(FX)'); %// Bye bye ... close the Notepad
As explained in the Microsoft documentation the SendKeys class may have some timing issues sometimes so if you want to do complex manipulations (like Tab multiple times to change the button you actually want to press), you may have to introduce a pause in your Matlab calls to SendKeys.
Try without first, but don't forget you are managing a process from another without any synchronization between them, so timing all that can require a bit of trial and error before you get it right, at least for complex sequences (simple one should be straightforward).
In my case above for example I am running all my data from an external hard drive with an ECO function which puts it into standby, so when I called the "save as..." dialog, it takes time for it to display because the HDD has to wake up. If I didn't introduce the pause(1), sometimes the file path would be imcomplete (the first part of the path was send before the dialog had the focus).
Also, do not forget the & character when you execute the external program. All credit to Luis Mendo for highlighting it. (I tend to forget how important it is because I use it by default. I only omit it if I have to specifically wait for a return value from the program, otherwise I let it run on its own)
The special characters have a special code. Here are a few:
Shift +
Control (Ctrl) ^
Alt %
Tab {TAB}
Backspace {BACKSPACE}, {BS}, or {BKSP}
Validation {ENTER} or ~ (a tilde)
Ins Or Insert {INSERT} or {INS}
Delete {DELETE} or {DEL}
Text Navigation {HOME} {END} {PGDN} {PGUP}
Arrow Keys {UP} {RIGHT} {DOWN} {LEFT}
Escape {ESC}
Function Keys {F1} ... {F16}
Print Screen {PRTSC}
Break {BREAK}
The full list from Microsoft can be found here
There is a small javascript utility that simulates keystrokes like this on the Windows javascript interpreter.
Just create a js file with following code:
var WshShell = WScript.CreateObject("WScript.Shell");
WshShell.SendKeys(WScript.Arguments(0));
then call it from Matlab after the necessary timeout like this:
system('c:\my\js\file\script.js {Enter}');
Can't test here now, but I think this should work...
If you need to run a console-only program in a context that permits full DOS redirection, you can create a file called, say, CR.txt containing a carriage return and use the '<' notation to pipe the value into the program.
This only works if you can provide all the keyboard input can be recorded in the file. It fails dismally if the input has to vary based on responses.
An alternative is to duplicate the input (and possibly output) stream(s) for the program and then pipe data into and out of the program. This is more robust and can permit dynamic responses to the data, but will also likely require substantial effort to implement a robot user to the application.
Rog-O-Matic is an example of a large application completely controlled by a program that monitors screen output and simulates keyboard input to play an early (1980s) ASCII graphic adventure game.
The other responses will be required for GUI-based applications.
Python package pywinauto can wait any dialog and click buttons automatically. But it's capable for native and some .NET applications only. You may have problems with pressing WPF button (maybe QT button is clickable - not checked), but in such case code like app.DialogTitle.wait('ready').set_focus(); app.DialogTitle.type_keys('{ENTER}') may help. Your case is quite simple and probably some tricks with pywinauto are enough. Is your "app with popup" 64-bit or 32-bit?
wait and wait_not functions have timeout parameter. But if you need precisely listener with potentially infinite loop awaiting popups, good direction is global Windows hooks (pyHook can listen mouse and keybd events, but cannot listen dialog opening). I'll try to find my prototype that can detect new windows. It uses UI Automation API event handlers... and... ops... it requires IronPython. I still don't know how to set UI Automation handler with COM interface from standard CPython.
EDIT (2019, January): new module win32hooks was implemented in pywinauto a while ago. Example of usage is here: examples/hook_and_listen.py.

Automating editing SystemVersion.plist

I am currently using OSX10.10 and need to use MATLAB; however because they haven't updated the application to support 10.10 it will crash on launch.
Up until now I have been using pico to edit the SystemVersion.plist [1] (changing the version from 10.10 to 10.9); and that works great except that it is really annoying to edit the file every time I need to open MATLAB and re-edit it every time I close MATLAB.
What I want to do is when I start the script it will edit the SystemVersion.plist to the correct version so that I can run MATLAB without it crashing; and then when MATLAB exits it resets the version back from 10.9 to 10.10). I have a bit of code (which may be poorly written; I have never used applescript before);
tell application "System Events"
set ProcessList to name of every process
if "MATLAB" is in ProcessList then
tell application "System Events"
tell property list file "/System/Library/CoreServices/SystemVersion.plist"
tell contents
set value of property list item "ProductUserVisibleVersion" to "10.9"
set value of property list item "ProductVersion" to "10.9"
end tell
end tell
end tell
else
tell application "System Events"
tell property list file "/System/Library/CoreServices/SystemVersion.plist"
tell contents
set value of property list item "ProductUserVisibleVersion" to "10.10"
set value of property list item "ProductVersion" to "10.10"
end tell
end tell
end tell
end if
end tell
[1] - Error trying to installing JDK8 U11 OSX 10.10 Yosemite
I had the same approach, but came down to this solution: (for os x yosemite and matlab r2014a)
tell application "System Events"
set plistFile to property list file "/System/Library/CoreServices/SystemVersion.plist"
tell plistFile
get property list item "ProductVersion"
set value of property list item "ProductVersion" to "10.90"
end tell
end tell
do shell script "export MATLAB_USE_USERWORK=1" & ";/Applications/MATLAB_R2014a.app/bin/matlab -desktop &> /dev/null &"
display dialog "..." buttons {"Ok"} with icon note giving up after 10
tell application "System Events"
set plistFile to property list file "/System/Library/CoreServices/SystemVersion.plist"
tell plistFile
get property list item "ProductVersion"
set value of property list item "ProductVersion" to "10.10"
end tell
end tell
the dialog box is needed. delay (in seconds) does not do it for any reasons (i first used applescript to solve the matlab problem). there might be another solution, but this works for me.
if you are using a mac with retina display, you may want to install an java 7 runtime environment and replace the do shell script part with the following:
do shell script "export MATLAB_JAVA=\"/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home\"" & "; export MATLAB_USE_USERWORK=1" & ";/Applications/MATLAB_R2014a.app/bin/matlab -desktop &> /dev/null &"
the icons still do look a little shitty, but the fonts aren't blurry anymore.
i hope this may help anybody facing the problem after updating to yosemite lately.
jens

Applescript: "set user interaction level to never interact" not working in illustrator

I'm trying to set it so that there's no user interaction when I open up my illustrator file using applescript, but the standard:
tell application id "com.adobe.Illustrator"
activate
set user interaction level to never interact
open theFile without dialogs
doesn't work for this plugin I have installed that checks for white overprints.
If it were up to me I'd just uninstall the plugin but it's for a work pc.
I also tried clicking the button automatically (with help from Tim Joe) by using:
try
tell application "System Events"
tell process "Finder"
click button "OK" of window "Adobe Illustrator"
end tell
end tell
end try
and I've tried
tell application "System Events"
tell process "Adobe Illustrator"
keystroke return
end tell
end tell
Does anyone know a way of solving this?
below is the full code as it currently stands:
set saveLocation to ((path to desktop) as string) --place to save the files
set theFile to choose file with prompt "Choose the Illustrator file to get outlines on"
set outputFolder to choose folder with prompt "Select the output folder"
tell application "Finder" to set fileName to name of theFile
set fullPath to (saveLocation & fileName) --file path of new .ai
set fileName to (text 1 thru ((length of fileName) - 3) of fileName) --remove .ai from fileName
set olPath to text 1 thru ((length of fullPath) - 3) of fullPath & "_OL.ai" --path of outlined file
tell application id "com.adobe.Illustrator"
activate
ignoring application responses
open theFile without dialogs
end ignoring
tell application "System Events"
tell process "Adobe Illustrator"
repeat 60 times -- wait up to 60 seconds for WOPD window to appear
try
tell window "White Overprint Detector"
keystroke return
exit repeat
end tell
on error
delay 1
end try
end repeat
end tell
end tell
save current document in file fullPath as Illustrator with options {class:Illustrator save options, compatibility:Illustrator 15, font subset threshold:0.0, embed linked files:true, save multiple artboards:false} --save file to desktop
convert to paths (every text frame of current document) --convert text to paths
save current document in file olPath as Illustrator with options {class:Illustrator save options, compatibility:Illustrator 15, font subset threshold:0.0, embed linked files:true, save multiple artboards:false} --save another copy to desktop with name + _OL.ai
end tell
tell application "Finder"
set newFolder to make new folder at saveLocation with properties {name:fileName}
move fullPath to newFolder --create new folder and move both new files into it
move olPath to newFolder
set newFolderPath to (newFolder) as string
set newFolderPath to text 1 thru -2 of newFolderPath --remove the trailing ":"
tell current application --zip up the new folder
set qpp to quoted form of POSIX path of newFolderPath
do shell script "cd $(dirname " & qpp & ")
zip -r \"$(basename " & qpp & ").zip\" \"$(basename " & qpp & ")\""
end tell
set zipFile to newFolderPath & ".zip"
move zipFile to outputFolder --move .zip to output
delete newFolder --delete folder on desktop left from zipping
end tell
--prepare a notification email
set presetText to "Hello,
Files Uploaded:
" & fileName & ".zip
To access our FTP Server:
http://217.207.130.162:8080/WebInterface/login.html
To access our FTP server, log onto our website below:
Username:
Password:
Thanks,
Joe"
tell application "Mail" --open up prepared email
activate
set theMEssage to make new outgoing message with properties {visible:true, subject:fileName, content:presetText}
end tell
--open file containing usernames and passwords for the FTP
do shell script "open /Users/produser/Desktop/FTP_Users"
I tracked down and installed White Overprint Detector I could see what you mean. I had to use an older version as I only have CS3, and I saw the dialog it produces when you open a document. The following worked for me to get it to dismiss:
tell application "Adobe Illustrator" to activate
tell application "System Events"
tell process "Adobe Illustrator"
repeat 60 times -- wait up to 60 seconds for WOPD window to appear
try
tell window "White Overprint Detector"
keystroke return
exit repeat
end tell
on error
delay 1
end try
end repeat
end tell
end tell
Since my original post seemed too objective to understand I will revise.
With in the tell block for illustrator look for your line that opens the file. Some commands allow with and without properties. Try applying the "without dialogs" property to look something like this.
tell application id "com.adobe.Illustrator"
open file (VariableOfFilePath) without dialogs
end tell
Update:
Two work arounds I can think of. 1) Try telling system events to tell AI to open without dialogs
tell application "system events"
tell application id "com.adobe.Illustrator"
open file (VariableOfFilePath) without dialogs
end tell
end tell
Other is just add in a bit that will just okay the prompt.
try
tell application "System Events"
tell process "Finder"
click button "Continue" of window "Adobe Illustrator"
end tell
end tell
end try
Can try just having it accept the default button.
tell application "System Events"
tell process "Finder"
keystroke return
end tell
end tell

Applescript: Close Word document? (Office 2011)

i'm simply trying to close a document (without quitting word), but this does not work.
I've tried :
close documents saving no
close active document
close document 1
but none of this is working..
The error I get (on the close line) is:
Microsoft Word got an error: The object you are trying to access does not exist
Here is a simplified script. Anyone have any ideas? All examples online seem to use this syntax, not sure if anything changed in the mac office 2011 version?
set input to {POSIX path of "/Users/Wesley/Desktop/test.doc"}
--ENABLE GUI SCRIPTING
tell application "System Events"
if UI elements enabled is false then set UI elements enabled to true
end tell
--RUN THE GUISCRIPT
set pdfSavePath to POSIX path of (choose folder with prompt "Set destination folder")
repeat with x in input
display dialog x
tell application "Microsoft Word"
activate
open x
set theActiveDoc to the active document
close theActiveDoc saving no
return
end tell
end repeat
close active document and close document 1 both work for me. Perhaps you need a delay statement before the theActiveDoc variable is assigned?

Simple script no longer works on Lion

I have a simple Applescript that takes a number of images in a folder that you select and then mirror images the images using Graphic Converter. This script will run if I place it in a new AS file; however, if I try to run it a second time I get the following error "Can't get window 1 of application "GraphicConverter". Invalid index."
This script always ran on OSX 10.6
I'm running OSX 10.7.4 and Graphic Converter 8.1 (latest version).
Here is the script
tell application "Finder"
activate
set loopFinish1 to 0
set pathName to (choose folder with prompt "Choose Folder Containing Images")
set fileList1 to every file of folder pathName
set loopFinish1 to count of items of fileList1
end tell
tell application "GraphicConverter"
activate
repeat with i from 1 to loopFinish1
set currentFile to item i of fileList1
open currentFile as alias
mirror window 1 in horizontal
close window 1 saving yes
end repeat
end tell
This is driving me crazy!
GraphicConverter has other windows (visible and invisible), it's preferable to use the document to get the right window.
Also, perhaps the image doesn't open, so no window, use a try block.
activate
set pathName to (choose folder with prompt "Choose Folder Containing Images")
tell application "Finder" to set fileList1 to (document files of folder pathName) as alias list
tell application "GraphicConverter"
activate
repeat with tFile in fileList1
set currentDoc to open tFile
set CurrWindow to (first window whose its document is currentDoc)
mirror CurrWindow in horizontal
close currentDoc saving yes
end repeat
end tell