Friday, April 20, 2007

Tag team programming

Not long ago I was corresponding with W. Chesson Godfrey aka Wild_Bill from the forum and he shared a little script with me. As so often happens when two people look at an issue from two different perspectives they will each solve the puzzle as it best suits their office culture. This is a little something I hadn't thought about before, which I changed to use some functions he wasn't using and shazam (is that a word) we have WsMan.py or workspace manager. Another reason why discussion is almost always a good thing. This stuff is just too cool.



PromBox=VrPromBox('Set Active Ws',60,1)
# The whole app revolves around a single convenient Prompt box, so create an object.
PromBox.AddMessage ("Don't change workspaces\n in the middle of a function")
# A little helpful text.
PromBox.AddCombo ('Workspaces',PyVrWs().GetWsCount(),0,0)
'''
We'll use a ComboBox as the user interface, so create it with the
number of items set to the number of workspaces. In this case I'm not
implicitly creating a workspace object. I just use the class to call
a method. Someday I'll test to see if this method of creating a
temporary object causes memory problems in things like large loops,
but for now I'm just illustrating that you don't necessarily need to
do the Ws=PyVrWs() thing.
'''
for WsNum in range(PyVrWs().GetWsCount()):
....PromBox.AddComboItem (PyVrWs().GetFileName (WsNum))
# For every workspace, get it's name and add it to the combo box.
if (PromBox.Display(0) == 0):
....PyVrWs().SetAws(PromBox.GetCombo(0))
# If the user didn't hit cancel, then set the active workspace
# to match the one selected.

Get involved in the forums, there is a wealth of experience to be shared.

Monday, April 16, 2007

Utility programs 3

Ok, time flies and all, but we better get some actual working code in this thing. In post 1 all we did was create a usage string and test argv to see if anything had been passed in (in this case we don't want anything). In the second we used glob to build a list of files matching a certain extension in the directory we're working in. Eventually we could us a windows file select dialog but for now this will be a simple command line utility. Let's get right to the pertinent section then put it all together in the end. Using th same indentation as where we left off, lets replace

........print os.path.splitext(File)

which was just used to show that something was working and to illustrate os.path functions with

# Step through the list of files processing each
....for File in Filelist:
# Each file will need to be opened
........OppFile=open(File,'r')
# readlines() reads every line in a file, returns a list in which each member
# of the list is a line in the file, including the end of line character.
........FileData=OppFile.readlines()
# Step through the list of lines in the open file
........for Line in FileData:
# This isn't real tricky or elegant, but it turns out that the coordinate
# positions stored in the .opp file are on lines which have an "l" as the
# second character, so let's just determine first of all if the data line
# is a coordinate
............if Line[1]=='l':
# Then if it is, the first character determines which coordinate it is.
# If it's an X, Y, or Z we'll use the split(function) to break the line
# into a list of strings, which contains the number as the second
# element, then use float() to convert it to a real number, and store it
# in the appropriate variable.
................if Line[0]=='X':
....................X=float(Line.split()[1])
................elif Line[0]=='Y':
....................Y=float(Line.split()[1])
................elif Line[0]=='Z':
....................Z=float(Line.split()[1])
# Once we have parsed each line in the file to pull out the coordinates
# lets use some formatted printing to output the file name and values.
# Remember that "File" contains the name of the file with the extension
# so the split using the dot will give a list ['filename','extension']
# so the base name is element 1 or [0]
# Close the file and your on your way.
........print '%s %.3lf %.3lf %.3lf'%(File.split('.')[0],X,Y,Z)
........OppFile.close()

so in the end after removing the modules that were only temporary, the final program looks like

import sys,glob

def usage():
....'''
....Explanation text to print if anything is entered as an argument.
....'''
....print '\nUsage: python opp2txt.py\nThere are no arguments, it will just look in the current directory\n for files with a .opp extension, strip the coordinates out of them\n and output them to the screen\n\nListings can be redirected into a file.\n eg: python opp2txt.py > photos.txt'
....sys.exit(1)

if len(sys.argv)==1:
....Filelist=glob.glob('*.opp')
....if Filelist==[]:
........print "There don't appear to be any .opp files in this directory"
........sys.exit(2)
....for File in Filelist:
........OppFile=open(File,'r')
........FileData=OppFile.readlines()
........for Line in FileData:
............if Line[1]=='l':
................if Line[0]=='X':
....................X=float(Line.split()[1])
................elif Line[0]=='Y':
....................Y=float(Line.split()[1])
................elif Line[0]=='Z':
....................Z=float(Line.split()[1])
........print '%s %.3lf %.3lf %.3lf'%(File.split('.')[0],X,Y,Z)
........OppFile.close()
else: usage()

Now this just prints to the screen and would have to be redirected to a text file. For the fun of it the first modification once we know it is running would be to send it directly to a file. As always, final working code is found on my personal VrPython website.


Saturday, April 07, 2007

Utility programs 2

Rather than retyping the code from the previous post, I'm just going back to the line with argv and proceed from there with some actual working code. This time I want to focus on getting the filenames that will be processed. Remember the test of argv was just to determine if any arguments were passed. Since this program doesn't take any, having something else appear on the command line indicates a lack of understanding so we printed some usage instructions. Now let's assume the user knows that if he types "python opp2txt.py" he will get every opp file in the directory processed. But how to build a list of file names? Today we'll just build the list and print the filenames to show that it worked. The magic we want is contained in the glob module, no I don't know why it is named that, but isn't that just about the coolest name around? Here is the code that could be inserted into opp2txt.py to grab filenames.

if len(sys.argv)==1:
....Filelist=glob.glob('*.opp')
....if Filelist==[]:
........print "There don't appear to be any .opp files in this directory"
........sys.exit(2)
....for File in Filelist:
........print os.path.splitext(File)
else: usage()


First let's look at glob. The module itself is fairly simple and can be found in the python module docs. The short story is that you can return filenames matching a pattern in a specified directory into a list of strings. Really the best way to illustrate is to try it. In your favorite python editor do the following.

import glob
glob.glob(r'\vr\hostdir\*.par')

This should return a list which contains the names of every .par file in that directory. Something like...
'\\vr\\hostdir\\drivefile.par', '\\vr\\hostdir\\editline.par',
The lower case r before the path name just tells python to treat the string that follows as a raw string and not to see the backslashes as special characters. You could also put in forward slashes without the 'r' since that is a valid path separator in python but the return list comes back as an odd mix of forward and backward slashes.

We are storing the returned list in a variable called Filelist. If there are no .opp files in the directory the list will be empty in which case we print a message to that effect and exit, I just picked a random exit code of 2 to indicate no files found.

Next the for loop will step through the list and process each name string in the list. The variable which represents the name string will be "File".

Meaningful processing will come next time, but for now we could just use the code "print File" to display each filename but let's take this opportunity to explore the extremely useful os.path functions. These are most helpful in separating components of full filenames into paths, extensions, base filenames, etc. In the program which will be run in a directory that includes the .opp files and will not contain a path component the list would look something like...
['1.opp', '2.opp']
and the splitext() will just return a name and an extension similar to...
('1', '.opp')
('2', '.opp')
which could be accessed individually using indexes
>>> print ('2', '.opp')[0]
2
>>> print ('2', '.opp')[1]
.opp
>>>
However to really have some fun lets go back to the previous example in the editor and try some examples with the name list returned from the vr hostdir directory. Here are some truncated examples of what would be returned. Take time to play with the os.path functions, there is some real usefulness there.

>>> for name in glob.glob(r'\vr\hostdir\*.par'):
... print os.path.basename(name)
...
drivefile.par
editline.par

or if we run it through splitext() after it has been stripped to basename.

>>> for name in glob.glob(r'\vr\hostdir\*.par'):
... print os.path.splitext(os.path.basename(name))
...
('drivefile', '.par')
('editline', '.par')

or how about just running splitext to get the filename so that you can create another file with the same name, but with a different extension.

>>> for name in glob.glob(r'\vr\hostdir\*.par'):
... print os.path.splitext(name)
...
('\\vr\\hostdir\\drivefile', '.par')
('\\vr\\hostdir\\editline', '.par')

or what about if you want to get the path name so you can create a file in the same directory.

>>> for name in glob.glob(r'\vr\hostdir\*.par'):
... print os.path.split(name)
...
('\\vr\\hostdir', 'drivefile.par')
('\\vr\\hostdir', 'editline.par')

or just test whether or not the file exists, of course all these do because we're just passing in existing file names, but you get the idea.

>>> for name in glob.glob(r'\vr\hostdir\*.par'):
... print os.path.exists(name)
...
True
True

Cool stuff, and we haven't even done anything useful yet, hopefully next time.



Friday, April 06, 2007

The forums are back!

I'll post more on opp2txt more this weekend but I just had to mention that the Vr Forums are back. As I understand it they aren't technical support forums so much as a gathering place for users to share information. In that sense if used properly they could be an invaluable place to discuss problems, questions, tips, tricks and other useful information. Check them out at http://www.cardinalsystems.net/forum/

Tuesday, April 03, 2007

Utility programs 1

Ok so I said I was going to talk about function key parsing and python dictionaries, and I will... eventually. I got off on a small tangent though and decided that it would be beneficial to start out talking about external utility programs first. There are probably many uses for a nice organized function key data set, both inside Vr and out. But lets start small. I love little command line utilities that take the place of actually working, as a matter of fact if I have a motto it would be "It beats working" and the more times I have reason to say that on any give day the better. Here is an example...

When I am working with ortho's there are times that I would like to have the photo centers plotted in a file. Because almost everything in Vr is stored in easy to read, easy to interpret text files it is a piece of cake to go into the opp files and pull out the xyz coordinates in any text editor. But do that on 100 separate files and you are bordering on work. What if you could just run a simple little command line utility that would give you a space separated listing of the photo name and it's coordinates. That my friend "beats working". Let's think through this program a few lines at a time, when it's all said and done I'll post the final code on the repository web site.

First for the preliminaries


# Lets start out with a docstring to explain what the program is for.
'''
Extracts photo center coordinates from all .opp files in a directory
and sends
the output to the screen, which can then be redirected into
a text file.

'''
# For now just trust me that these are the modules we'll need.
import os,sys,string,glob
# It's always a good idea to put in a usage function. In this case since
# (like it says) there won't be any arguments, will force the usage string
# to print if the user enters any argument at all.
def usage():
....'''
....Explanation text to print if anything is entered as an argument.
....'''
....print '\nUsage: python opp2txt.py'
....print 'There are no arguments, it will just look in the current '
....print '
directory for files with a .opp extension, strip the '
....print '
coordinates out of them and output them to the screen'
....print '\n\nListings can be redirected into a file.'
....print 'eg: python opp2txt.py > photos.txt'

....sys.exit(1)
# This is the main part of the program. First we'll make sure no
# arguments were entered. sys.argv is the command itself the program
# name is always the first argument, if there is only 1 thing in the
# list then no arguments besides the program name were passed.
if len(sys.argv)==1:
....print 'no code yet run\npython opp2txt.py -h\nfor help'
else: usage()

Ok that's it for now. Copy the above to a python file called opp2txt.py , replace the indent holders (....) with your normal indentation and run it with an argument and it will describe where we are headed. You need python installed and in the path. Type "python opp2txt.py -h" and the usage should print.

If you want to get a better idea what argv does just create a python script that contains nothing except
import sys
print sys.argv
Then run the script with different (or no) arguments.
More to come...


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.