One feller's views on the state of everyday computer science & its application (and now, OTHER STUFF) who isn't rich enough to shell out for www.myfreakinfirst-andlast-name.com
Using 89% of the same design the blog had in 2001.
FOR ENTERTAINMENT PURPOSES ONLY!!!
Back-up your data and, when you bike,
always wear white.
As an Amazon Associate, I earn from qualifying purchases. Affiliate links in green.
That gives you a lot more control than just having test[] for your form element names. So in one page, I want to know the id numbers of the elements that are selected. Now I can put that information in the index of the array and pass a value (in this case, a user-inputed ordinal reflecting the display order of the elements) as well.
So if you want to automate Excel 2008 with Applescript, you have to minimize your tells. There are some strange scoping interferences. For instance, this code breaks.
tell application "Microsoft Excel" set intOffset to (offset of "E" in "ABCDEFG") end tell
Try to compile/run and you get "Syntax Error -- Can't get "E" in "ABCDEFG". Access not allowed." Wack. Fun times with text manipulation and Excel automation, eh?
So code that borks like that needs to be pulled out of the tell block. You maintain values that were pulled out of Excel references, so it's not a big deal other than the inherent wackiness. So in my code now, the tell block to Excel is very small.
-- it seems like you could do all this work within tell -- tags, but that's not the case. For instance, the -- following code WON'T work within a tell to MS Excel: -- set intOffset to (offset of "E" in "ABCDEFG") -- I'm not sure what the problem is, or if it's keyword -- overload or something, but reducing the tell to a -- minimum seems to be the best move. tell application "Microsoft Excel" set theValue to get value of cell c of row r of active sheet end tell
theValue sticks around afterwards and my stock/simple string manipulation calls work again, so no problems. Not sure if doing the tell inside of instead of outside of my logic's loops causes some big bridge between Excel and Applescript to be built, destroyed, and built again, but it's already so freakin' slow it's not like it really matters. There's fast as in "give it to me now" and 'fast' as in "give it to me in a few hours after I go for a run, to the grocery store, and do the dishes". This is in the second, and is nearly already in the "go to sleep on it and hope it's done in the morning" category that comes afterwards. I hate this.
(I ran into this "sleep on it" speed at a shop that used Crystal Reports against very large databases and it drove me MAD. It was bad enough I took the time to figure out a way to have Crystal use SQL directly against the DB, moving the logic where it's supposed to go -- the database -- and left Crystal just with formatting. Thank heavens. Even if I personally only used it a few times, give me a break, folk. We went from clients waiting all night -- even DAYS at times -- for reports to them waiting a few minutes at worst. It's not 1980 any more! Your workstations are fast if you're not gaming, okay? (Not 1980, that is, unless you're using Applescript to automate Excel 2008, apparently.))
FUN IN KLUDGEVILLE!!! I'll probably repost my "practical example" as it is now in a bit. It's not clean at all. Drives me a little crazy.
This stinks. Really. Say what you will about Microsoft, they know how to create and maintain a programming language. Whoever thought that s/vbscript/Applescript/gi would be a fair substitution in Office for Mac (even ignoring that it breaks legacy macros) was not a programmer.
Here's what my "final" code looks like. It's a mess and needs cleaning. But something that's INSANELY EASY in vbscript takes the below to do something similar. I couldn't even get it to know what cell format selections I'd made in Excel and ended up with an ugly kludge for zip codes and a hellah ugly kludge for phone numbers which were coming out in a format like "9.195553662E+9". What a mess.
I could clean it up some, taking out lesser kludges that aren't working, but I think you get the point better as is. Not intuitive. Not fully-featured. The stuff you have to roll yourself in Applescript (afaict) is insane and inane. Boy. Talk about not feeling fully featured.
Now I have to let it wade through over 3000 lines of Excel tonight...
1 on replaceText(find, replace, subject) 2 set prevTIDs to text item delimiters of AppleScript 3 set text item delimiters of AppleScript to find 4 set subject to text items of subject 5 6 set text item delimiters of AppleScript to replace 7 set subject to""& subject 8 set text item delimiters of AppleScript to prevTIDs 9 10 return subject 11 end replaceText 12 13 tell application "MicrosoftExcel" 14 15 --fromhttp://lists.apple.com/archives/applescript-users/2009/Jun/msg00331.html 16 set hc to find (row 1of used range of active sheet) what "PHONE" 17 set n tofirst column indexof hc 18 set f tofirst column indexof(column 1of used range of active sheet) 19 set col to column (n - f +1)of used range of active sheet 20 set number format of col to"0" 21 --doesn'tseemtowork 22 23 24 --say"Savingwitheveryrow" 25 26 set lastCol to10--yes,I'mcheating. 27 --youhavetoknowthelastcolandrowforthistowork 28 set lastRow to100--IsupposeIcouldadddialogboxestoaskmaxnums 29 set c to1 30 set r to90--that'sthefirstrowthat'sgotinfoitin;skipthefirstrow'scolheaders 31 set strCmd to"" 32 33 try 34 repeatuntil r = lastRow +1 35 set strCmd to strCmd &"INSERTINTOrestaurants(phone,company,address,¬ 36 city, state, zip, owner, gm, agent, chef)" 37 set strCmd to strCmd &"VALUES(" 38 set daComma to"" 39 40 --'','','',42,,'','','')" 41 42 set c to1 43 44 repeatuntil c = lastCol +1 45 --displaydialog"Row:"&r&"Col:"&c 46 47 set theValue toget value of cell c of row r of active sheet 48 49 if(c =1)then--thenwehaveaphonenumber 50 --ExcelviaApplescriptisborkingnumberslikemad. 51 --ThereareRightWaystohandlethis.They 52 --don'tseetobeworkingwell(seeabove). 53 --we'regoingtocheatlikemadinstead. 54 --TODO:KLUDGE-A-RIFFIC 55 if(text 11of(theValue as string)="E")then 56 set strOut to text 1of(theValue as string) ¬ 57 & text 3thru10of(theValue as string)&"0" 58 else 59 set strOut to text 1of(theValue as string) ¬ 60 & text 3thru11of(theValue as string) 61 endif 62 63 set strCmd to strCmd & daComma & strOut 64 set daComma to"," 65 66 elseif(c =6)then--thenwehaveazipcode 67 --can'tusethisbranchforphonenumbers 68 --astheyevaluatetoanumberlargerthan 69 --ourlargestpossibleinteger 70 if(countof characters of(theValue as string))=5then 71 set theValue to theValue as integer 72 endif 73 set strCmd to strCmd & daComma &(theValue as string) 74 set daComma to"," 75 elseif(c =5)then 76 set strCmd to strCmd & daComma &"42" 77 --hardcodingSCtosavealookup 78 set daComma to"," 79 else--elseit'sastring 80 if theValue =""then 81 set theValue to"empty" 82 endif 83 set strCmd to strCmd & daComma &"'"&(theValue as string)&"'" 84 set daComma to"," 85 endif 86 87 set c to(c +1) 88 endrepeat 89 set strCmd to strCmd &")"&return--returnaddsaMacOSnewline 90 set r to(r +1) 91 if r mod50=0then 92 --beep--addsomesortoffeedback. 93 say r --addcoolerfeedback.Thistalks. 94 --Btw,ApplescriptforthisinExcelisinsanelyslooooow 95 --vs.myexperiencewithvbs.Ittakesseveralsecondsto 96 --do10rowsoftencolumnsofcustomeraddressandphone,etc. 97 --information. 98 endif 99 endrepeat 100 101 set theFilePath to(path to desktop as string)&"test11.txt"as string 102 set theFileReference toopen for access theFilePath with write permission 103 write strCmd to theFileReference 104 close access theFileReference 105 106 onerror errMessage number errNum from errSource partial result errList ¬ 107 to class_constant --variousvariablesstoreinformationabouttheerror 108 set bigmessage to"Theerroris:"& errMessage &return ¬ 109 &"Thenumberis:"& errNum &return&"Thesourceis:"& errSource &return ¬ 110 &"r&c:"& r &"&"& c 111 error bigmessage --errorstatementdisplaysdialogboxtouser 112 say "errorintheloop" 113 close access theFileReference 114 --thiscloseseemsimportant.Ican'ttellhowtoget 115 --backthehandletoclosethisifIhadanerrorcauseit 116 --tostayopenfromapastscriptinvocation. 117 endtry 118 119 say "Done!" 120 --displaydialog"Donewithwritetofile" 121 endtell
(Edit: See this on tells. I had to tweak the above a good bit for it to work well, but it still serves as a pretty decent low-investment Applescript (vs. VBA) code snippet. It's an order of magnitude more complicated.)
I have Excel 2008 on my Mac. About the only complicated thing I ever want to do with Excel files is to manipulate them line by line and spit the output into a cut and pasteable source for my clipboard. I have a vbscript version for Windows, but when I'm in 2008 land, I'm sunk. No vbs. Only Applescript. Stinks.
Frustrated with having to wait until I'm back on my Windows box, not yet willing to shell out more cash and buy Office 2011 (and fed up enough with Office 2004 that I threw it away the second 2008 hit my box), I'm finally going to figure out how to port the code to Applescript.
So first let's essentially run Hello, World, and put a value into a specific Excel cell.
tell application "Microsoft Excel" tell worksheet "Sheet1" of active workbook set value of cell "A1" to 35 end tell end tell
Going to Sheet2 is exactly like you'd expect.
tell application "Microsoft Excel" tell worksheet "Sheet2" of active workbook set value of cell "A1" to 35 end tell end tell
So once we start playing around, we'll probably want some way to keep track of what we've found. In my case, I want to turn line after line from an Excel file to SQL statements. Instead of a giant dialog box, I'll just slap that into a file.
set theText to "some text" set theFilePath to (path to desktop as string) & "test.txt" as string set theFileReference to open for access theFilePath with write permission write theText to theFileReference close access theFileReference
So now all we've got to do is to throw the contents of each cell into theText. So now we need to find out how to iterate through all Excel cells?
This took a little longer that I would have expected. The key is this line:
get value of cell c of row r of active sheet
That takes the place of .Cells(x,y)
So here's the end result.
tell application "Microsoft Excel"
say "Saving at the end"
set lastCol to 10 -- yes, I'm cheating. -- you have to know the last col and row for this to work set lastRow to 1000 -- I suppose I could add dialog boxes to ask max nums set c to 1 set r to 2 -- that's the first row that's got info it in; skip the first row's col headers set strCells to ""
try repeat until r = lastRow set c to 2 set strCells to strCells & (r as text) & " -- " repeat until c = lastCol --display dialog "Row: " & r & " Col: " & c set theValue to get value of cell c of row r of active sheet set strCells to strCells & theValue & " :: " as text -- just use & to append; cast to text with "as text" set c to (c + 1) end repeat set strCells to strCells & return -- return adds a MacOS newline set r to (r + 1) if r mod 20 = 0 then -- beep -- add some sort of feedback. say r -- add cooler feedback. This talks. -- Btw, Applescript for this in Excel is insanely slooooow -- vs. my experience with vbs. It takes several seconds to -- do 10 rows of ten columns of customer address and phone, etc. -- information. end if end repeat close access theFileReference
-- write strCells to theFileReference on error say "error in the loop" end try
try set theFilePath to (path to desktop as string) & "test2.txt" as string set theFileReference to open for access theFilePath with write permission write strCells to theFileReference close access theFileReference on error say "Error in writing to file" close access theFileReference -- this close seems important. I can't tell how to get -- back the handle to close this if I had an error cause it -- to stay open from a past script invocation. end try
say "Done!"
end tell
This is PAINFULLY slow, however. I'm used to it screaming. I tried rearranging the code to write out the jive to the file with each line, and that's not noticeably quicker.
tell application "Microsoft Excel"
say "Saving with every row"
set lastCol to 10 -- yes, I'm cheating. -- you have to know the last col and row for this to work set lastRow to 100 -- I suppose I could add dialog boxes to ask max nums set c to 1 set r to 2 -- that's the first row that's got info it in; skip the first row's col headers set strCells to ""
set theFilePath to (path to desktop as string) & "test5.txt" as string set theFileReference to open for access theFilePath with write permission try repeat until r = lastRow + 1 set c to 2 repeat until c = lastCol + 1 --display dialog "Row: " & r & " Col: " & c set theValue to get value of cell c of row r of active sheet set strCells to strCells & theValue & " :: " as text -- just use & to append; cast to text with "as text" set c to (c + 1) end repeat set strCells to (r as text) & " -- " & strCells & return -- return adds a MacOS newline write strCells to theFileReference set strCells to "" set r to (r + 1) if r mod 50 = 0 then -- beep -- add some sort of feedback. say r -- add cooler feedback. This talks. -- Btw, Applescript for this in Excel is insanely slooooow -- vs. my experience with vbs. It takes several seconds to -- do 10 rows of ten columns of customer address and phone, etc. -- information. end if end repeat close access theFileReference
-- write strCells to theFileReference on error say "error in the loop" close access theFileReference -- this close seems important. I can't tell how to get -- back the handle to close this if I had an error cause it -- to stay open from a past script invocation. end try
say "Done!" display dialog "Done with write to file" end tell
So it works, in a sort of brute force, inefficient way. The biggest issue I've had is if it's cancelled. Though it says "error in the loops", the file ref seems not to be closed, and then the file I'm writing to remains open with no opportunity to close it. Once the script's done, it just remains open. I tried deleting on the command line with Terminal and the file was still "there" as far as the script was concerned. Perhaps when I reboot it'll be gone, but I can't Google a way to close it. Most answers seem to talk about finding the app that opened it, but when you're the app that did it, I'm not sure what's up.
In any event, it does marginally spider through cell by cell and print things out to a text file, which is what I wanted. You just have to be really patient. No wonder they added
Open your smartphone and you may be pleased to find that its mapping application now fixes your location quite accurately. Just a short year or two ago the GPS signals used by these devices were painfully inaccurate -- particularly in the canyons of cities where we tend to want them most. Then came the remarkable insight (from companies like Loki) that when people buy Wi-Fi access points at Radio Shack and install them in their houses, they don't move them around very often, or turn them off. Each Wi-Fi access point, it turns out, has a unique number that it broadcasts continuously in something called a beacon. The beacon, aptly named, flashes out with the regularity of a lighthouse.
Google wants to trawl the streets for Wi-Fi signals because if it can find these beacons and organize them, then the next time you drive by that same beacon it can guess where you are...
If you use the Add-On Manager to create the foundation to your Firefox extension, you might see that your description is literally "description" even though you've got a different value in <em:description> in your install.rdf file.
You've probably got this line in overlay.properties: extensions.zoteroplay@genreacrossborders.org.description=description (where zoteroplay@genreacrossborders.org will be the id of your own extension)
That'll do it. /sigh The property "covers up" your longer description in install.rdf. Erase that line and your other description will show up.
More fun code! Note today's new lessons -- the OM for tree items, rows, children, and cells; the use of CDATA for encapsulating javascript in XUL files, which are picky XML files, especially when you use a <; and slightly more situation-specific code.
So this ends up looking like the following (after rows have been added to new and dupe sources several times):
It may also be useful to note that I made the sidebar open up apparently about as wide as Firefox is going to let it with this code in ff-sidebar.js:
1 var mainWindow = null; 2 3 function startup() { 4 mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) 5 .getInterface(Components.interfaces.nsIWebNavigation) 6 .QueryInterface(Components.interfaces.nsIDocShellTreeItem) 7 .rootTreeItem 8 .QueryInterface(Components.interfaces.nsIInterfaceRequestor) 9 .getInterface(Components.interfaces.nsIDOMWindow); 10 11 // this spacebar needs a lot of space to display the info 12 // we're going to put there. Give it at least 400 pixels out of the box. 13 // TODO: I'm setting a check to see if it's already 400, but it seems there's 14 // an absolute max width for sidebars in Firefox by default which is less than 15 // (or about) 400. 16 if (mainWindow.document.getElementById("sidebar-box").width <400){ 17 mainWindow.document.getElementById("sidebar-box").width=400; 18 } 19 //Sidebarisloadedandmainwindowisready 20 } 21 22 functionshutdown(){ 23 //Sidebarisunloading 24 } 25 26 window.addEventListener("load",startup,false); 27 window.addEventListener("unload",shutdown,false); 28
The postings on this site are [usually] my own and do not necessarily reflect the views of any employer, past or present, or other entity. About Our Author