Well 2.2 is now gold! It's no longer beta and it's ready to roll.
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)
Monday, June 25, 2007
Thursday, June 21, 2007
Lesson 6: Getting Stuck in the GUI Hacks
Getting Stuck in the GUI Hacks
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
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.
There it is; a macro with built rest stops (but smells better) for when you just 'have' to GUI hack.
So have at it and I'll see you next time cause the hook always brings you back.
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.spe 50
Acc option_a=acc("Options..." "DROPLIST" win(".*Message" "OpusApp" "" 0x200) "MsoCommandBar" "" 0x1001)
option_a.Mouse(1)
0 WA "Message Options"
'Ar
'An
'Y
0 WA "Message"
'As
So have at it and I'll see you next time cause the hook always brings you back.
Monday, June 18, 2007
Beta Alert: ROUND 2!
2.2.0.10 is out now.
Looks like Gintaras forgot to roll in a feature he'd promised on the forums.
Looks like Gintaras forgot to roll in a feature he'd promised on the forums.
- Popup menu expandable folders: removed flag 64 (apply include/exclude to folders), and instead added /filterfolders.
Beta Alert!
New beta is now out (download here).
Not a lot of ground breaking things but some enhancements for the deep end programmers.
Not a lot of ground breaking things but some enhancements for the deep end programmers.
- Better supports ActiveX controls.
- Adding files to exe.
Monday, June 4, 2007
Paying the Automation Monkey
Well, I had an interesting discussion with a friend of mine the other day; let's call him "Dimitri". He told me about a reporting project that he was doing where his "superiors" were loath to allow him to automate. As you can tell from one of my posts, I'm not really hip to that sort of....well, I really can't express how I feel about that without offending a LOT of people and risking a visit from the FBI, Homeland Security, and theEYP (why the EYP ?...well, I can't into that either...but it was a total misunderstanding I swear!)...not to mention a courtesy call from the ELF. So, let's just say, "I am somewhat opposed to management outright denying permission to automate a re-occurring process." And what struck me most abhorrent about this situation was not the, capricious Jimmy Carter style micro-management of high-level technical staff, but the apparent lack of understanding of time/worker management or rather the limited understanding of what it costs to actually have people do things....No, I will say it plainly....The COMPLETE lack of understanding math....simple A+B+C+D+E=F kind of math. These are people that manage people for a LIVING for crying out loud; this is not a hobby they do; this IS what they do to eat and buy jet-skis. Stop drinking theKool Aid and take a work-force management course for crying out loud....ehmmm...ok, I'm done....for now....anyway...back to Dimitri. Here's the story.
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:
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."
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."
Friday, May 25, 2007
RELEASE ALERT: New Beta is Out
The new Beta is out; mostly under-the-hood enhancements but there may be a few things of interest.
- computer unlocking that now works on Vista
- made the exe's smaller
Wednesday, May 23, 2007
Friday, May 18, 2007
The Evolution of Automation
After 5 lessons (well, 10 really), I thought I'd pause and take a look, not at specific automation code, but how automation evolves out of current processes. One of my first posts on this site was about the reasons to automate (I'M HAVING AN INDIGNATION MEETING). It gave four reasons why to automate and I've spent the last five weeks showing how to automate. In this article, I want to discuss how current processes evolve into an automated process in an office environment (can you use anymore Darwin buzz words without talking about 'Natural Selection'?!). There were a couple of surprisingly useful developments in a recent project that I hadn't actually planned out but just organically evolved when the project came to critical junctures.
The other day a project came across my desk that needed some updating. For some time, I've been retrieving some text files via a secured web server and adding some html to the contents to deliver to end users via a different secured web connection. Due to the security setup at my organisation, there had to be a lot of 'tweaking' done to make the automation server (let's call it 'Athens') connect with the data server (let's call it 'Carthage') as well as the machine that was to be the final resting place of the data where the end users will access the report (let's call that one 'Hellespont'). The process was executed by a 7 year old VB exe that frankly was a pain to deal with. Removing reports were a pain; adding reports were a pain; not fubar-ing the entire process doing either of the above was a pain...Not knowing what you were doing with VB was a pain. The whole thing was enough to make you ignore it till you could get your hands on an Impala outfitted with a military class JATO rocket, put the entire source-code and exe in the front seat, flip the switch, sit back and watch the glory. Well, as it turns out, the final security piece fell into place before I could get my hands on that JATO and I knew it was time to grab a large-caliber handgun, a tac-light, and crawl into that Damnbeast den (better make it machine pistol...um...actually two (John Woo style you know)....with extra ammo..all armour piercing of course...(the bullets not the pistols)...although....armour piercing pistols....hmmmmm...anyway). I then started to map out how I was going to make this thing work.
My first decision was to make it easy to add and subtract reports from the process (see above). I didn't want to code each report into the function separeatly because that would take a 10 line process and make it 'the-number-of-reports' times 10 lines. In case you weren't sure of what to call that, it's called 'Ugly and Kludgey'. I decided to create a text file that would contain the address of each report needed on the server Carthage, as well as, the final name of the report when I place it on the server Hellespont for distribution. These two pieces of info would be separated by a '|' character. It is just a very simple text document that I can step through one line at a time using '
And here we have all this simple and wonderful code using cURL to retrieve the files from Carthage and load them each individually into a string variable . I'm not going to go into... (wow, that didn't work well at all)...I'm not going to go into what I'm doing to get them or how I'm manipulating them (maybe I'll go over that another time); I'll just say that for https retrieval, cURL is really, really, nice!
So, to sum up, I've retrieved the files from Carthage and modified them so they look better for the end user and then placed them out on Hellespont for the end user. When I ran it the 'first' time it worked great till I realized one of the reports is no longer used. No problem, all I have to do is delete that line and....wait....that is never a good idea when dealing with capricious users (or any for that matter). I'll tell you a little principle I've developed in my years of report design. The report that has not been looked at for over a year by a single living soul will never even be thought of until you delete it from your report delivery system. Then an admin assistant is on the phone saying they want it....oh, and they want it for a meeting....today....at 9:30.......um, it's 9:40 now....yep. And here it is in business speak:
So, as you can see, I'm a little reluctant to just delete the report entry. [Enter Stage Right: Comment Delimiter] ....Ooooh, excellent! Comment delimiter! I can actually use almost any character since all the report addresses have to start with the '/' character. I chose to use the '#' symbol because I'm used to it when I'm doing Korn shell work in Unix. My next step was to use '
The 'definitions' file and the commenting capability were things that weren't specifically planned on, they just 'grew' out of the process as it went along. It solved problems I hadn't realized and became more and more useful as the project progressed. The comment capability really makes the whole project much more Agile since anyone who comes in behind me can easily find out what I'm doing from the comments in this definitions file. Which leads me to the next step...documentation.....uhgh.....talk about a Damnbeast.
The other day a project came across my desk that needed some updating. For some time, I've been retrieving some text files via a secured web server and adding some html to the contents to deliver to end users via a different secured web connection. Due to the security setup at my organisation, there had to be a lot of 'tweaking' done to make the automation server (let's call it 'Athens') connect with the data server (let's call it 'Carthage') as well as the machine that was to be the final resting place of the data where the end users will access the report (let's call that one 'Hellespont'). The process was executed by a 7 year old VB exe that frankly was a pain to deal with. Removing reports were a pain; adding reports were a pain; not fubar-ing the entire process doing either of the above was a pain...Not knowing what you were doing with VB was a pain. The whole thing was enough to make you ignore it till you could get your hands on an Impala outfitted with a military class JATO rocket, put the entire source-code and exe in the front seat, flip the switch, sit back and watch the glory. Well, as it turns out, the final security piece fell into place before I could get my hands on that JATO and I knew it was time to grab a large-caliber handgun, a tac-light, and crawl into that Damnbeast den (better make it machine pistol...um...actually two (John Woo style you know)....with extra ammo..all armour piercing of course...(the bullets not the pistols)...although....armour piercing pistols....hmmmmm...anyway). I then started to map out how I was going to make this thing work.
My first decision was to make it easy to add and subtract reports from the process (see above). I didn't want to code each report into the function separeatly because that would take a 10 line process and make it 'the-number-of-reports' times 10 lines. In case you weren't sure of what to call that, it's called 'Ugly and Kludgey'. I decided to create a text file that would contain the address of each report needed on the server Carthage, as well as, the final name of the report when I place it on the server Hellespont for distribution. These two pieces of info would be separated by a '|' character. It is just a very simple text document that I can step through one line at a time using '
foreach' to parse the file and and 'gett' to parse the line. The file is sort of a 'definitions' file. This method has several benefits:- to add a report, I just add one line to the end of a text document rather than opening up QM to add like 10 lines of code to the 180 that are already there (if there were say 18 reports). When I say 'I', I'm really saying 'any of my co-workers' who need to add a report later on because I got hit by a bus or won the lottery.
- to remove a report, all I have to do is delete one line rather than opening up QM and deleting like 10 lines of code. When I say 'I', I really....well you get the point....It's called Agile baby!!
- If reports are added or deleted, there's no need to make 'versions' of the program (like we do now) because I'm not actually changing the code.
So, to sum up, I've retrieved the files from Carthage and modified them so they look better for the end user and then placed them out on Hellespont for the end user. When I ran it the 'first' time it worked great till I realized one of the reports is no longer used. No problem, all I have to do is delete that line and....wait....that is never a good idea when dealing with capricious users (or any for that matter). I'll tell you a little principle I've developed in my years of report design. The report that has not been looked at for over a year by a single living soul will never even be thought of until you delete it from your report delivery system. Then an admin assistant is on the phone saying they want it....oh, and they want it for a meeting....today....at 9:30.......um, it's 9:40 now....yep. And here it is in business speak:
The value of a report is not based on its actual use or even its usefulness but rather directly on its difficulty in recreating its process after it has been discontinued....for six months....by someone other than yourself.....who quit because they won the lottery.......you just wish they would get hit by a bus....while you were driving it.....which is what you'll be doing for a career if that admin assistant doesn't get that report printed within the next 10 minutes.
I don't have a catchy name for it yet but now that it's published I have a copy-write on it 80) ....BTW: I'm taking suggestions for the name.So, as you can see, I'm a little reluctant to just delete the report entry. [Enter Stage Right: Comment Delimiter] ....Ooooh, excellent! Comment delimiter! I can actually use almost any character since all the report addresses have to start with the '/' character. I chose to use the '#' symbol because I'm used to it when I'm doing Korn shell work in Unix. My next step was to use '
find' in a simple 'if' statement to skip the entire process if the first character is a '#'. Done. Now I can keep that report ready in case someone wants it again.....wait a minute....I can also use a 'Comment Delimiter' to do things like 'make comments'...just in case I'm found guilty of a crime for hitting that guy with a bus and have to go to prison or something...yeah, I know, long shot; no jury is going to convict me of 'painting a new center-line' with that guy after deleting that report process without documentation. So without any extra code, I've also added the ability to place as many comments as I want anywhere inside the file as well as deactivating reports.The 'definitions' file and the commenting capability were things that weren't specifically planned on, they just 'grew' out of the process as it went along. It solved problems I hadn't realized and became more and more useful as the project progressed. The comment capability really makes the whole project much more Agile since anyone who comes in behind me can easily find out what I'm doing from the comments in this definitions file. Which leads me to the next step...documentation.....uhgh.....talk about a Damnbeast.
Labels:
Automation Theory,
cURL,
findreplace,
gett,
https,
Report Delivery
Monday, May 14, 2007
Lesson 5: The Extra Mile
Ok, so there's more than one way to skin a cat and there's more than just a few ways to get text into a window. This next method is much more complicated but hang with me; if I can do it, I think you will be able to as well.
Go to the "Windows, controls" icon on the QM Toolbar and go down to the "Accessible object actions" menu choice. This opens up a dialog that looks like this:

Take a look at all the different actions on the left-hand side. These will really start to open up your possibilities as you get more and more skilled at QM (or as some say, 'more kung-fu'). In this lesson, I'm just going to be dealing with the 'Get value' and the 'Set value'.
Let's start with a text editor of your choice; I'm going to use Notepad but you can use anything you like. First, let's set the value of a window/field or whatever you are wanting to change. Click on 'Set value' and then click-and-hold the 'Drag' icon; drag it over and drop it on the part of the window for which you want to set the value.
You'll notice that there is a thick black line surrounding the parts of the window as you hover over them. Let go of the mouse button when you have selected the right part of the window. QM now shows you what it 'sees' of that area.
Most of the time, you'll need to modify the 'Window' title; you can do that by clicking the button with three dots right next to it. You'll see this dialog after doing so.

The first thing I usually do is start adding wildcards to the Title. So, '- Notepad' becomes '*- Notepad'. Don't forget to also check the 'Use *' checkbox below! There are other options you can modify but I rarely have to.
There are also other values you can change in the main dialog too but I'll not be covering that in this lesson. So, now my dialog looks like this:
You can change the variable assignment if you wish within this dialog before letting QM enter this info into the macro. Now, hit 'Ok' and it puts this info into your macro.
So have at it and I'll see you next time cause the hook always brings you back.
Go to the "Windows, controls" icon on the QM Toolbar and go down to the "Accessible object actions" menu choice. This opens up a dialog that looks like this:
Take a look at all the different actions on the left-hand side. These will really start to open up your possibilities as you get more and more skilled at QM (or as some say, 'more kung-fu'). In this lesson, I'm just going to be dealing with the 'Get value' and the 'Set value'.
Let's start with a text editor of your choice; I'm going to use Notepad but you can use anything you like. First, let's set the value of a window/field or whatever you are wanting to change. Click on 'Set value' and then click-and-hold the 'Drag' icon; drag it over and drop it on the part of the window for which you want to set the value.
You'll notice that there is a thick black line surrounding the parts of the window as you hover over them. Let go of the mouse button when you have selected the right part of the window. QM now shows you what it 'sees' of that area.
The first thing I usually do is start adding wildcards to the Title. So, '- Notepad' becomes '*- Notepad'. Don't forget to also check the 'Use *' checkbox below! There are other options you can modify but I rarely have to.
There are also other values you can change in the main dialog too but I'll not be covering that in this lesson. So, now my dialog looks like this:
Acc a=acc("" "TEXT" win("*- Notepad" "Notepad" "" 0x3) "Edit" "" 0x1800 0x0 0x20000040)
a.SetValue("Hey, set this value.") Now that we have a target, let's start putting in the info. As you can see, the second line of my code is already doing just that. I can also have it set the value using a variable as well. Now, the thing to keep in mind about this function is that it will erase anything that is there already....unless......Yep, that's just what we were looking for. Now, you can 'append' info to the window by feeding its value back into a variable and manipulating than variable. You may not want to use this technique for more than small dialog fields but who knows, it may be just the thing you're looking for in that hard to get-at window._s=a.Value();;this, of course, gets the value_s.from(_s "[]Now it's both values")
a.SetValue(_s)
So have at it and I'll see you next time cause the hook always brings you back.
Labels:
Accessible Objects,
from,
Lesson 5,
SetValue,
The Extra Mile,
Value
Thursday, May 10, 2007
Lesson 5: Swimming The Keystroke
By this time you've probably started to out pace these lessons or at least come across problems you've had trouble solving when it comes to entering text. Some windows just don't cooperate...at all. Well, like ancient archers who picked certain arrows for certain jobs, you can do the same with keystrokes methods. So far, I've covered 'outp' with quoted text and with strings but there are two other ways as well: the 'key' function and QM key-codes (QM uses special characters to represent the special keys like Alt, Ctl, Shift, etc. (well, not 'etc' that's just one of those Latin things; it's not a keyboard thing)). Let's start with the apostrophe first.
Look at the QM Toolbar that starts up with the Editor; the second icon opens a dialog to show you all the special characters. You can start using these very easily by invoking the function with an apostrophe like this ' .
Note: I'll be taking many of the examples directly out of the QM Help. BTW: you can easily access the help documentation and go to the function you are curious about by just placing your cursor on the function and hitting F1; often times there is a very well documented article on the function (sometimes the help text appears in the output window below the editor instead).
So have at it and I'll see you next time cause the hook always brings you back.
Look at the QM Toolbar that starts up with the Editor; the second icon opens a dialog to show you all the special characters. You can start using these very easily by invoking the function with an apostrophe like this ' .
Note: I'll be taking many of the examples directly out of the QM Help. BTW: you can easily access the help documentation and go to the function you are curious about by just placing your cursor on the function and hitting F1; often times there is a very well documented article on the function (sometimes the help text appears in the output window below the editor instead).
The apostrophe is a quick and dirty way of getting text to type rather than pasting text via the 'outp' function. The 'key' function works similarly but has more flexibility. Here is how those examples would look when using 'key'.'CSf A{ec} Wd ;;Ctrl+Shift+F, Alt+E+C, Win+Dnote: the Alt key is released after the c key
'SnewVSyork ;;type "New York" using QM key codes
Yeah I know but that was just to show you how it looks; now let's really get into it.key CSf A{ec} Wd ;;Ctrl+Shift+F, Alt+E+C, Win+D note: the Alt key is released after the c key
key SnewVSyork ;;type "New York" using QM key codes
Here's another way of doing multiple keystrokes while holding down modifier keys. You can put all kinds of things inside that block; not sure what other types of things you would want to though.key (VK_MEDIA_PLAY_PAUSE);;Mimics hitting the Play/Pause button
key a (0.5) b ;;a waits 0.5 second b
key+ CS
lef
key- SC 'Key' will also let you feed the contents of a string variable into it. So, you can easily create the variable value from any other method and then you can have 'key' type it in. This works especially well for a telnet program I use that won't allow direct pasting into the command line.
key _s
Now usually, you won't have to go to such drastic measures just to input some text but if you need it (like in my telnet program) you've got it. Granted, it's not particularly strong me (mĀ), but when you gotta have it, you gotta have it.So have at it and I'll see you next time cause the hook always brings you back.
Labels:
apostrophe,
key,
Lesson 5,
me,
QM Help,
Virtual-key codes
Monday, May 7, 2007
Lesson 4: The Extra Mile
For me string variables are the sonic screwdriver of coding. I use them to build not only the functions I need but the logs that monitor them, so being able to manipulate them is crucial for doing the things I want.
Let's take a look at the whole log file concept. I have a text file that is filled with lines of text something like this:
Now, let's remove the line that we loaded into 'b' and put it back into 'a' but in the 2nd position.
Now let's say you want to pull out the last part of each string and take a look at it (e.g. Menelaus, Thersander, Ascalaphus, etc). Here's how to set up the loop and grab the last section.
Now, about the code, I'm using the '
So there you have it, some of the really useful string functions. And if you really want to turn it up, you can start getting into using 'replacerx' and Regular Expression coding...not that I would know anything about that. ;o)
So have at it and I'll see you next time cause the hook always brings you back.
BTW: Here's the entire code block to make it easy to import into the Editor (don't forget to use the Paste-Escaped option).
Let's take a look at the whole log file concept. I have a text file that is filled with lines of text something like this:
Lacedaemonians|60|MenelausThe first thing you need to do is get the file data into a string so you can start modifying it.
Boeotians|50|Thersander
Minyans|30|Ascalaphus
Arcadians|60|Agapenor
Now lets get just the last line of that text file by using two different functions.str a b c
int za.getfile("C:\qm\ships.txt")
b.getl(a (numlines(a)-1) 2)
out b The 'getl' function first asks you for the string from which you want to get the line (variable 'a'). The second part is a little trickier; the 'numlines' function tells you how many lines are in the string 'a', but the '-1' needs some explaining. The 'getl' function is what is called 'zero-based' which means (it's terribly annoying and prone to 'one-off' mistakes) it starts counting at '0' not '1' just like a computer....um...ok yeah, that does make sense now but it's still annoying. So, it has 4 lines but the last line is 3 hence the '-1'. So now 'b' contains the last line of 'a'. Now, let's remove the line that we loaded into 'b' and put it back into 'a' but in the 2nd position.
For the next step, let's add a new line. This new line can come from anywhere i.e. a file, the clipboard, or text selection, but here I've just set 'b' to it and then dropped it onto the end of 'a'.a.RemoveLineN(numlines(a)-1);;this removes the line at the end (that we just copied) so it won't be duped.a.InsertLineN(b 1);;again zero-based function.
out a
b="Salamis|12|Telamonian Ajax"
a.addline(b 1)
out a A note about the flag used on line two (not zero-based 80) ). The '1' tells 'addline' to not add a 'new line' character at the end of 'a'.
A note about flags: flags are just parameter codes for functions that allow you to modify the basic behavior of a function.
A note about flags: flags are just parameter codes for functions that allow you to modify the basic behavior of a function.
Now let's say you want to pull out the last part of each string and take a look at it (e.g. Menelaus, Thersander, Ascalaphus, etc). Here's how to set up the loop and grab the last section.
rep numlines(a)
,b.getl(a)
,c.gett(b 2 "|[]")
,out "c=%s" c Note: the commas are another way of indenting the code within the QM Editor. However, when you copy/paste the code into another program, the tabs are replaced with commas. If you copy the code above to your QM Editor, you'll get the commas unless you use the menu Edit -> Other formats -> Paste escaped option.Now, about the code, I'm using the '
numlines(a)' instead of checking for the error code output from the 'getl' just as a matter of preference (I'll explain more about that in a minute). The 'getl' is missing the line number to pull but when that value is missing, it just pulls the next line; you could use an incrementing integer but this is simpler. If, however, there is no next line, the 'getl' function returns -1; it would be that -1 that you would check for if you didn't know exactly how many lines where in the variable. The real money maker is the 'gett' or 'get token'. Think of a token as a 'chunk' of the string. To break up each line, I've used the pipe character '|' as my delimiter because it was very unlikely to show up in the data. But I also have the '[]' within the 'gett' command in its delimiter section. This tells QM that the last chunk of data in a line will end at the new-line character. Otherwise, it will grab the next line's first section (you can try it out to see how it affects the data pull). And finally, you'll notice that through out this lesson I've been using the 'out' command to monitor my changes in the data but I've used a modified version of this at the end to include some prefix text to identify the variable. This technique really comes in handy when I'm debugging and vetting several variables at once and it can contain many variables on the same line as well as identifying them.So there you have it, some of the really useful string functions. And if you really want to turn it up, you can start getting into using 'replacerx' and Regular Expression coding...not that I would know anything about that. ;o)
So have at it and I'll see you next time cause the hook always brings you back.
BTW: Here's the entire code block to make it easy to import into the Editor (don't forget to use the Paste-Escaped option).
ClearOutput
str a b c
b.getl(a (numlines(a)-1) 2)
a.RemoveLineN(numlines(a)-1)
a.InsertLineN(b 1)
b="Salamis|12|Telamonian Ajax"
a.addline(b 1)
out a
rep numlines(a)
,b.getl(a)
,c.gett(b 2 "|[]")
,out "c=%s" c
Labels:
[],
addline,
commas and tabs,
delimiter,
flags,
getfile,
gett,
InsertLineN,
Lesson 4,
new-line character,
numlines,
out,
Paste Escaped,
RemoveLineN,
rep,
Strings,
The Extra Mile,
zero-based
Saturday, May 5, 2007
NEW-NEW BETA OUT!
There was a bug in the new beta; if you have 2.2.0.6 you should upgrade ASAP (turns out the predefined variables got unassigned).
2.2.0.7
2.2.0.7
Subscribe to:
Posts (Atom)