Tuesday, December 19, 2006

Python as macro revisited

In the first VrPython example I gave which has now take on it's official name of fix_sym_z.py my purpose was to show how it is possible to get some working code very fast just using some basic Vr keystrokes. I very rarely use the vr button (B1, B2, B# etc) commands to actually push the buttons, but it makes a point. The real problem with that script in a public setting is the drive point functionality that wasn't explained, not to mention that the person using it had several key suggestions to make it much more useful (don't end users drive you crazy like that sometimes). In any case, let's look at the current evolution of the script and the positive and negative effects of the changes. While I'm still going to use the button press commands at the end to actually do the work, by necessity this will start looking more like a python script and less like a simple extended macro, but then that's the whole point.
Remember the dots are used for place holders and to visually represent the indentation which is life and death in python (2 dots = 1 tab)

Gui = PyVrGui()
stat=0
Layer2Drive=25
Sym=PyVrSym()
Ws
=PyVrWs()
WsNum=Ws.Aws()

Gui.PushKeyin ("digxyz")
for EntNum in range (Ws.GetSymCount(WsNum)):
..if Layer2Drive==Ws.GetSymLayer(WsNum,EntNum):
....Sym.Load (WsNum, EntNum)
....x,y,z=Sym.GetCoord()
....Gui.PushKeyin ('xyz '+repr(x)+' '+repr(y)+' +0')
....stat, x, y, z = Gui.GetCoord ('Select Coordinate')
....if stat==0:
......Gui.PushKeyin ("chaele")
......Gui.PushKeyin ("sealin=0")
......Gui.PushKeyin ("seasym=1")
......Gui.PushKeyin ("b1")
......Gui.PushKeyin ("b1")
......Gui.PushKeyin ("b#")
......Gui.PushKeyin ("chalay")
......Gui.PushKeyin ("sealin=0")
......Gui.PushKeyin ("seasym=1")
......Gui.PushKeyin ("lay=76")
......Gui.PushKeyin ("b1")
......Gui.PushKeyin ("b1")
......Gui.PushKeyin ("b#")

Looking at it a line at a time

Gui = PyVrGui()
stat=0
Layer2Drive=25
Sym=PyVrSym()

As before, just setting up some variables. Creating an instance of the Gui object, same for a Symbol, and initializing the others. This is a good place to note that in Python you don't actually have to declare variables, or initialize them in any particular place. I could have put the Layer2Drive=25 on the line right before the first place I used it, or if it were the result of a calculation, just let it be created in place. I am only setting them up early like this because I want to know what they are set to, and putting it all together seems like sound practice. In the first pass at this script the external "drv2lay" script prompted for the layer, but in reality the user just wanted it to always use the same layer. I could have used the desired number rather than a variable later, but using a variable and setting it at the outset makes it easy for other users to find and change if they want.

Ws=PyVrWs()
WsNum=Ws.Aws()

Gui.PushKeyin ("digxyz")
Because Vr allows several workspaces to be open at the same time and in many cases you have to specify which one you are interacting with you need to create an instance of a workspace object, and in this case set it to the currently active workspace. Then let's make sure that the stereo digitizer is active before we start driving to points.

for EntNum in range (Ws.GetSymCount(WsNum)):
..
..if Layer2Drive==Ws.GetSymLayer(WsNum,EntNum):
This is going to represent one of the biggest changes to this script, driving to the points will actually become part of the script, but before we can drive, they have to be identified. The for loop in Python is fantastic and I won't take time to explain it now (remember the books recommended in the introduction). Let's just say that in plain English these two loops (the for and the if) would sound like "For entity numbers in the range zero through how ever many symbols there are, if the desired layer to drive to is equal to the layer of the symbol with the entity number currently being checked". Yes we are going to loop over the entire file and check the layer of each symbol, the power behind this loop is the way the checking is done which I won't really go into, but suffice it to say it seems instant on even the largest files.

....Sym.Load (WsNum, EntNum)
....x,y,z=Sym.GetCoord()
At this point if the symbol does match the layer we plan to drive to, load the symbol based on the workspace we are working in, and the entity number of the symbol currently being checked. Then from the loaded symbol grab the coordinates. Note that since GetCoord returns 3 numbers, I can assign all 3 at the same time. This is also an example of assigning data to a variable that has been neither declared nor initialized. It becomes a floating point variable because that's what I assigned to it.

....Gui.PushKeyin ('xyz '+repr(x)+' '+repr(y)+' +0')
Once I have the coordinates I can drive the instrument using the Vr command 'xyz' then supplying an x, a y, and the +0 just means rather than an absolute z use one relative to the current position and add zero. Since PushKeyin expects a string we have to construct one using a text+text method. In this case since x and y are numbers, they have to be converted to text before they can be added to the string using the Python repr() command. There are some extra spaces inserted after the xyz and before the +0 to keep things separated, so if x was 1.0, y was 2.0 the final string pushed to vr would be.
xyz 1.0 2.0 +0

....stat, x, y, z = Gui.GetCoord ('Select Coordinate')
....if stat==0:
Once the appropriate symbol has been found, and the instrument has been positioned there, use the same method as before to pause for the operator to put the dot on the ground and digitize the elevation at that point. The main difference from before is that now if the user digitized a point all the changes will be made. If they hit end, rather than abandoning the whole script, it simply doesn't change anything and continues looking for symbols that match the layer criteria. There really isn't any way to abandon the script, but in this case the user wanted it this way.

......Gui.PushKeyin ("chaele")
......Gui.PushKeyin ("sealin=0")
......Gui.PushKeyin ("seasym=1")
......Gui.PushKeyin ("b1")
......Gui.PushKeyin ("b1")
......Gui.PushKeyin ("b#")
......Gui.PushKeyin ("chalay")
......Gui.PushKeyin ("sealin=0")
......Gui.PushKeyin ("seasym=1")
......Gui.PushKeyin ("lay=76")
......Gui.PushKeyin ("b1")
......Gui.PushKeyin ("b1")
......Gui.PushKeyin ("b#")
Same basic set of changes, except that before we waited until the elevations had been changed, then globally changed the layer to put it in the dtm data set. Now since if there is no point digitized, no changes are made, we can put the layer change right into the symbol modifications which includes everything including and after the chalay line. Note that I hard coded the layer to 76. As I write this I'll probably change that to a variable for the same reasons I mentioned for the layer to check. The parameter for the PushKeyin will be something like..
("lay="+ChangeToLayer)
There won't be a repr() command because I'll just store the variable as a string in the first place, something like...
ChangeToLayer='76'

All in all still much more brute force than elegance, but now we are doing some basic looping over entities including loading and pulling information from them.

No comments:

For anyone interested in trying VrPython for the first time or if you are early in the game, I suggest going to the earliest posts and working forward. I use VrPython every day for many wonderful things, needless to say it will change and could potentially damage a file. Any risk associated with using VrPython or any code or scripts mentioned here lies solely with the end user.

The "Personal VrPython page" in the link section will contain many code examples and an organized table of contents to this blog in a fairly un-attractive (for now) form.