Tuesday, January 1, 2008
So, at the end of August, I started a new job as a database programmer. A pretty significant step up from my previous position as a report writer not just in database but in automation as well. The shop I moved to is using the Opalis Robot as their automation/scheduling core. A nice product with some kickin' features; however, we are a full version behind and really need to move to get caught up and take advantage of their new version.
Anyway, I'm back and just posted a new category of articles. There are so many dialogs in QM that help you build code and make it much easier, I thought I'd start going over them. I call them 'Code Grinders'. And like a saugsage grinder, you put a bunch of unrealated food products into the top, flip the switch, and it mashes them all together into a nice neat stretched intestinal package. Anyway, the first in on File Enumeration. Ever need to get the list of files in a directory one at a time? Now you can with the Code Grinder:File Enumeration.
So, a buddy of mine was talking to me the other day, and he was needing to get all the names of the files in a certain directory. I told him, easy, just use the 'Code Grinder' File Enumeration that's in the main QM Toolbar under 'Files, Web'. A 'Code Grinder' is a term I came up with to describe dialogs that help you come up with pre-formatted code with just a little bit of info from you. In the same way that you drop some meat, garlic, pepper, salt, sage into the hopper, turn it on and shove it all down the grinder's throat. It's the same here in QM. The grinder is usually a dialog of some kind and the meat and spices are locations or variable names.
Let's take a look at the File Enumeration grinder and see what we can come up with (or is that, ...'see with what we can come up'....no, that's not right either... ummmm....'see what can come up from ourselves'...yeah, that's totally not the right direction....man, I hate that whole, "you can't end a sentence with a preposition" thing...is it any better going on and on like this at the end of a sentence rather than a preposition?). Anyway, so start up that dialog and lets take a look. Click the File Enumeration icon in the 'Files, Web' menu and here's what we've got.
As you can see, it's a pretty straight forward dialog but there are some real useful features. First, you can browse to the folder you are looking for or you can choose 'Special Folders' that will be the same on all computers. It looks like this.
I've circled some of the things that are a real pain when moving from computer to computer, but with 'Special Folders' you can make it quite easy to get to those directories no matter who is logged in (doh, preposition again!). You can set date/time qualifiers as well as 'this folder/subfolders' options as well. Now where this grinder really gets cooking is in the 'Variables' section. If you need just filenames, just put in your variable name in the field. If you need the path, just put in your variable name in that field. If you need the size etc, etc, etc.
Here's the code I get from asking it to list the filenames on the desktop.
foreach(d "$Desktop$\*" FE_Dir)
Go ahead and dump that into your QM and see what happens. It's that easy. Now 'sPath' has the filename of each file as it loops through the directory. So, now you have a way to get some or all that info automatically (or at least so close to automatically that it makes little difference).
Monday, August 20, 2007
OnScreenDisplay("Opening FireFox" -1 0 0 "Arial Black" 30 0xff 4 "FF_Start" 0x343434 )
So, this one has been floating around in the forums for a while and I thought I'd give my slants on it.
When I'm starting a program or a process that might take more than 1 second, I like to give a visual confirmation that it actually did start. For instance:
So, here's how the parameters break out:
- your text. you can also use a string variable instead.
- the time the display stay on screen. the default is 0 (5 seconds) or -1 for indefinite.
- the X Y coordinates of where to put the top left corner of the box. 0 0 is the middle of the screen.
- the font name (mine is "Arial Black"). I get mine from the font drop-down list in Word.
- font size (mine is 30).
- font colour in hex. I use Pixie but you can try http://www.free-webmaster-tools.com/colorpicker.htm to do it online but I don't know how safe the site is.
- flags. Note: you can add the numbers together to have it activate more than one state (i.e. 6 is number 2 and number 4).
- 1 - nontransparent. The background in transparent or not (not supported on Win98/Me).
- 2 - synchronous. The macro pauses till the text disappears (see #2 above).
- 4 - the user can click to hide the window.
Here is another format I use for a differ process.
OnScreenDisplay("Removing Dups...please wait" 0 0 0 "" 18 292929 4 "Rmv_Dps")
Ok, so now you have 5 days to put this into your code or I will mail you macros back to you in a box....one function at a time.
Sunday, August 12, 2007
_i= find(a "5")
This topic has hit the forums a lot lately and so I decided to do a little something on it.
'Find' is one of those funny little functions that returns a number instead of assigning it's value to a string like 'findreplace', 'left', etc. What it's doing is giving you the position of the first character of the sub-string that it finds. Here's an example right out of the Help document (press F1 and surf in there for a while it's really well done).
str s = "Notepad.exe"
int i = find(s ".exe")
out i;; now i is 7
Things to remember:
- it is zero indexed so you need to remember that the first character is in position 0 not 1.
- because it is zero indexed, the position returned for the last character will be 1 less than the length of the string.
- if there is more than one line, the "newline" character in the Windows world is actually 2 characters. Here's an example of that:
_i= find(a "3")
Find is useful even if you don't care where in the source-string the string lies. For example, if you just want to know that it is in there somewhere, just test to see if the number returned is greater than '-1' which is what is returned if it is not present.
Ok, so now you have 5 days to put this into your code and learn the Feng shui of 'find'.
Monday, August 6, 2007
The summer break! What a deal, everyday sit there and say, "holy cow it's been a long time since my last lesson; what am I gonna do?!" Well, since I've been busy doing some job hunt stuff and I still haven't made that million dollars I was hoping to get when I started blogging, I've decided to declare "Bloggging Bankruptcy" and start afresh...or call it "The Summer Break". Well, that's not entirely true. What I have been doing is thinking about making more posts about functions all in themselves in addition to full-blown lessons.
So, be looking for the "QM Quickling" (QMQ) which will be short synopsis of various functions and how to put them to use. This week's QMQ will be on incorporating 'find' in your code.
Tuesday, July 10, 2007
I use Winamp as my media player, not just because I can make it do strange and very useful little things but because I hate llamas. I have macros that will let me move forward and back in the media being played by 5 seconds, 30 seconds, and 3 minutes. I can also grab the current position of the playing media and save it to a text file sort of like "bookmarking". Now, of course you can do this via the buttons on the player and dialogs but it's clunky and not very responsive, so here's what I do.
I went nosing around the SDK for Winamp and got a hold of their 'WM_COMMAND Messages' definitions and menu ids. Now, instead of using a mouse command 'click here' on 'this window', I can forgo the whole graphical interface and it's associated problems. Here's how I'm moving 5 seconds forward or back using QM.
men 40144 win("" "Winamp v1.x") ;;Back 5 seconds
men 40060 win("" "Winamp v1.x") ;;FFwd 5 seconds
This responds MUCH faster via a trigger key than even using a mouse click manually but it's "faking" a menu choice. And, if I know I want to move 30 seconds forward (for skipping commercials on dl.tv-sorry, guys...but I do watch the interesting ones...and GoDaddy ain't one of 'em), I use this code:
int hwnd=win("" "Winamp v1.x")
_i=SendMessage(hwnd WM_USER 0 105)
_i+30000;;time to skip in miliseconds (this is 30 seconds)
SendMessage(hwnd WM_USER _i 106)
If you look closely, you'll see that I'm using the 'SendMessage' capability to get the current position in milliseconds (105) and then sending a new value back to the player (106). I get remarkably fast response times when doing this; the video at worst barely even stutters. This is much better than even using my above macro repeated 6 times.
Once you start getting a feel for what the 'SendMessage' commands start returning, you can really start cooking up some useful things. For instance, when I get a hold of a really good tune, I have a tendency to listen to it for 5 or 6 hours just repeating over and over. So, I came up with a quick method to force Winamp to do this. Since there is no setting for "Repeat Song" you have to build it from 2 settings: "Repeat Playlist" and "Manual Advance".
int y z
int hwnd=win("" "Winamp v1.x")
z=SendMessage(hwnd WM_USER 0 251);;get repeat playlist value
y=SendMessage(hwnd WM_USER 1 634);;get manual advance value
,Speak("Continue" 0 "" 0 100)
,SendMessage(hwnd WM_USER 0 635)
,OnScreenDisplay("Continue" 1 1000 10 "Arial" 0 0x1d60cd 0 "psa")
,Speak("Song Looped" 0 "" 0 100)
,OnScreenDisplay("Song Looped" 1 1000 10 "Arial" 0 0x1d60cd 0 "psa")
,SendMessage(hwnd WM_USER 1 635);;set man advance
,SendMessage(hwnd WM_USER 1 253);;set repeat
Since I want this macro to 'toggle' this state of 'Song Looped' rather than merely just 'turn it on', I have it use an 'if' statement to determine what state it is in and then reverse that state. I also tell it to give me a little audio feedback so that I know that the macro executed and let me know what it did (that's the 'Speak' function).
Now you have two ways of dealing with the GUI (or rather 1 way of dealing with it and 1 way of avoiding it). So have at it and I'll see you next time cause the hook always brings you back.
Monday, June 25, 2007
There were a few upgrades to this version but only if you have Vista, otherwise, it seems to be a version history shake down.
I would like to see some of the new Vista toolbar settings in action though....hint, hint! 90)
Thursday, June 21, 2007
GUI hacks are the processes that you have to create because you can't get at the option or method of doing something to/in a program. For example, a really ugly GUI Hack would be
- open Windows Explorer
- hit Alt-d and put in the directory you want to go to
- then hitting 'Tab' three times to get to the file list
- use the 'key' command to start spelling the name of the file you want so that it is selected
- type Ctl-c
- hit Alt-d to move to the address bar and put in the new directory
- hit 'Tab' three times to get to the file list
- hit Ctl-v
Of course no one would do this; that's what 'ren' or 'cop' is for but sometimes you just have to interact with program GUI dialogs. And God help me, I HATE GUI HACKS! There are just so many more things that can go wrong so many 'edge-cases' you have to account for...God help me but I hate them. Well, if you gotta you gotta so here's some ideas that might help.
Timing is everything in GUI hacking. If the dialog is slow to open, or if the field you need inside the dialog is slow to be drawn, or if...blah blah blah. The first thing I do to make sure my GUI hacks are going to work is put in 'wait's everywhere so that the macro 'paces' itself. Here's an example of that. Sometimes when I send emails via Outlook, I don't want a 'message receipt' nor do I want to keep a copy of the sent message, so I have a macro that changes the message property for me and then sends it. Here's what the dialog looks like.
As you can see, there are two check boxes I want to deselect; they can either be 'clicked' by the mouse (I find this less reliable for various reasons) or the values of the check boxes can be inverted by using keystrokes (Alt-r and Alt-n). First things first; I have to get the dialog to show up. I do that by using the "Accessible object actions" item in the "Windows, controls" button on the QM toolbar and drag the aiming reticle on top of the "Options..." button on the message I'm currently writing. This method works well enough but ask yourself what would happen if the toolbar that had that "Options..." button wasn't visible at the time; the macro would fail immediately (and so would my willowy grasp on sanity). I'd prefer a keyboard shortcut but I don't have one for it, so I make due. But now, I have to guess how long to wait for the dialog to show up before I start hitting my keystrokes....or do I?....Nope. Here's what I do.
This is a version of the 'wait' command. The '0' tells QM to wait indefinitely for the window with the name "Message Options" to become 'active' ('WA'=wait till active). I pulled the window name directly from the QM Editor itself. When you have the editor open the status bar continually gives you information on both the cursor position and the window that it is currently hovering over. The window name is in the first row and comes just after the mouse position relative to the window. So now, when I start the macro, it 'clicks' the "Options..." button and waits for the dialog to become active; it then sends the keystrokes for the above check boxes and hits 'enter'. It then waits for the message itself to become active so that it can send the 'Send Message' command via an Alt-s keystroke.
0 WA "Message Options"
Now you may ask about the second line from the bottom and say, "Gordon Bennett, not ALL your emails can be tittled 'Message'!"...ok, so maybe you wouldn't say that but someone did....probably.....might have....well, ok they could have said it give me that at least.... Well, this kind of gets into the 'win' function which I should cover another time in depth but let's suffice it to say that, it is case sensitive and can be anywhere within the window name. Here's what my window's tittle bar actually looks like with a standard message.
Acc option_a=acc("Options..." "DROPLIST" win(".*Message" "OpusApp" "" 0x200) "MsoCommandBar" "" 0x1001)
0 WA "Message Options"
0 WA "Message"
So have at it and I'll see you next time cause the hook always brings you back.
Monday, June 18, 2007
Monday, June 4, 2007
For the next couple of years, Dimitri is going to be running a diagnostic on systems within his organization and receive a small amount of output from that process. One of his higher-ups, wanted him to put that info into an email and send it out to everyone on a distribution list. He said that he could do that but none of the info from the diagnostics would be useful or informative for anyone but himself and to send that out would be a waste of time for him and everyone on the list. About that time, another higher-up got involved and started demanding the info be in a file and not just in an email and not just any file format but a spreadsheet. At that point, Dimitri went to his supervisor (yes, this is now the 3rd higher-up mixed up in this mess) and told him about the problem this TPS report has now become. His boss asked him if he could automate the file update process to which my buddy said yes, it is very easy unless you want it in Excel, then it will take at least a 2-3 hours for him to get some info on Perl/Excel functions (or maybe have me code something up in Quick Macros for him). After hearing how long it would take to automate, one of the two original higher-ups came to my friend's office and said, "It sounds like it's going to take more time to automate this than it will to just do it; so just do it manually for the next two years rather than automating it." Keep in mind that this person was not even Dimitri's boss.
And, it is at this point that I want to step-in and do a reality check..."Advertising doesn't cost; it pays."...Ok, well I don't have a cool saying for automation like the ad people do for advertising; well, there's "If you do something more than once, you're a sucker" but that seems overly rude at times. But, even that doesn't get to the point here. The point is that if you don't automate it, your going to spend more than that 1-3 hours to automate it. Here's why.
Let's say Dimitri spends five minutes actually doing the process every week. He's going to need to train someone to do it in case he gets hit by a bus (or, God forbid, takes a vacation day). That person will, at some point, realize that they will need to show someone else how to do it as well, just in case. So, now we have a 15 minute training session for the first backup and a 25 minute training session for the second backup. But, if either the first or second backup needs to actually do the process, it's going to take them probably 15 and 25 minutes respectively to do it because it usually 'jumps up at them' and they'll have to scramble to do it which means they'll have to go track down the instructions and then go hunt down their id/password to get into the system to get the data file. So here's how it actually breaks down:
Primary and Secondary 15 min X 2 = 30 min
Secondary and Tertiary 25 min X 2 = 50 min
Running the Process ea wk 510 min = 510
All Goings-back and forth
talking @ automation 60 min = 60
Total 650 min or 10.8 HOURS
10 HOURS and 50 MINUTES! HOLY COW!!! And that doesn't even count the times that the secondary and tertiary run this process. The very worst case scenario for him to automate this project: 3 hours. Yeah, "It sounds like it's going to take more time to automate this than it will to just do it; so just do it manually for the next two years rather than automating it"...IN A PIG'S EYE! Look, I'm the second or third guy to admit that there are times when automating things just doesn't make sense but if you're going to count the cost then count the freakin' cost. There are times, like this one, where it is too expensive to not automate a project and that fact should be painfully obvious even to managers who manage overtime exempt employees. I'll lay it out for you this way:
- An employee only has "x" amount of minutes in a day
- No task takes "0" minutes to complete.
Ok, ok...so, I'm coming off a little over board here; but it is a hard earned sense of indignation that comes from years of seeing "management" (read: "people who get paid more than the 'workers' but don't actually 'know' how to actually 'manage' them") going out of their way to not onlystifle worker productivity in the area of automation but actively seek out the proponents and developers of that automation to discipline with reprimands and down-checks on their yearlyevals. It's all a big crazy game that they are playing up there and only God knows the rules cause to me it looks like a varsity game of Calvin-Ball.
Simply put, this is what I'm trying to say, "Automation doesn't cost you time; it pays everyday with interest."....GAHHHH....that was terrible!...see, I still don't have a good catch-phrase for automation....let's just stick with this:
"If you do something twice, you're a sucker."