Monday, May 21, 2007

Life Outside Vr 2

Ok I have a few minutes so let's look at actually reading some data out of an open file. We'll stick with ascii files for now. There are so many fun reasons to work with files outside Vr that it would take too long to enumerate all of them but a few of my favorites include.

  • Reading info out of an ImageUtil project and creating all the Vr2Ori project and all models with 1 click.
  • Converting all Vr2Ori and VrAt files to or from their corresponding equivalents in orientp3 (yes we still use it and get along fine) so a setup in the analytical equals a setup in softcopy or vice versa.
  • Reading a PATB and plotting it graphically in VrOne for visual analysis and blunder checking.
  • Reading and plotting AT results including photos, layout data, computed extents and photo scale based on image centers and control averages.
You get the idea, there are just so many files out there that can be of easy use with a click or two. Sometimes there is already a way to do the same things, but a custom program allows you to set things up exactly the way you want without any further intervention. As in the last post we'll be working with vr.cfg in read only mode because it is a file that exists on everyones system (if you are working in Vr, which I assume) and has the same basic patterns. Again I will suggest you work in some kind of python IDE, I suggest pythonwin but you could even work from the python command interpreter if you want.

Let's start by creating a python file object like we did last time, it will be called f (for file).
f=open('/vr/hostdir/vr.cfg','r') # 'r' means read mode.
now if I
print f
there should be some obscure text describing the object. Hopefully there was some time to look over the file object docs to study up on the simple methods, let's look at a few.
Read one line out of the open file
>>> f.readline()
'# VR Configuration File\n'
I should note here that once it reads the line, the address in the file that python is sitting at is the beginning of the next line so if you do it again
>>> f.readline()
'# FileName : c:\\vr\\hostdir\\vr.cfg\n'
if for some reason it is necessary to go back to the beginning of the file we need to "seek" the new position. seek takes two arguments "offset" (how much to jump from the position passed in) and "whence" (where to start the offset). "Whence" defaults to the beginning of the file, so if you want to go the the beginning you can say "see" with an offset of 0 (don't move at all from the position passed in) and a whence of 0 (or if you pass nothing for whence this is the default) so
f.seek(0,0) # 0 offset, 0 whence
f.seek(0) # 0 offset, default whence which is 0
will both take python to the beginning of the file, so do that then readline() again and you should see the first line again. Let's read that first line and store it as a string we'll call "s"
>>> s=f.readline()
>>> print s
# VR Configuration File
It would of course be possible to do this with each line of the file, though there are better methods to be discussed later, but lets look a what we could do with that string now that we have it.
I can split it based on white spaces (the default for split) which returns a list of the words in the string.
>>> s.split()
['#', 'VR', 'Configuration', 'File']
For that matter we could split it based on any character like commas, or in this case the "i"
>>> s.split('i')
['# VR Conf', 'gurat', 'on F', 'le\n']
Note that splitting on white spaces takes off the newline character but splitting on a printable character doesn't. Of course there are plenty of ways to take the newline off, but just take note for now. How about if we needed to test for a sub string inside the string, well let's count occurrences.
>>> s.count('i')
3
>>> s.count('ig')
1
>>> s.count('z')
0
So a test would look something like
>>> if s.count('ig') : print 'yes'
...
yes
I didn't say if s.count equals something or is greater than something because I just wanted to know if it was non zero and anything non zero is true for the if test.

There is no sense typing out all the things you can do with strings because they are documented in the python docs, it is worth noting that there used to be a string module, but now each of these methods is a built-in which is why there was never an import statement above.

Let's go back to the split step and put the resulting list in a variable called "l", then we can loop over the list and do whatever we want, in this example just print each item.
>>> l=s.split()
>>> l
['#', 'VR', 'Configuration', 'File']
>>> for item in l:
... print item
...
#
VR
Configuration
File
or grab individual elements of the list.
>>> l[3]
'File'
or test to see if the list contains certain values.
>>> l.count('File')
1
>>> l.count('Nothing')
0
Like strings I'll leave the explanation of lists and their methods to the professionals, in this case the python tutorials.
But I'll leave with a couple of thoughts:
First, check out readlines() it will come in handy and will probably bear heavily in the next post.
Second, one of the real beauties of python is it's ability to get a lot done with a little code, this can be accomplished by taking whatever data a method returns and acting on it with the appropriate method. Consider the following lines typed into the interpreter.

open() is going to return a file object with all it's accompanying methods.
>>> open('/vr/hostdir/vr.cfg','r')
obscure text describing an open file object

let's just tack the readline() onto the end and let it return the first line which is a string.
>>> open('/vr/hostdir/vr.cfg','r').readline()
'# VR Configuration File\n'

It it is a string we can split it to get a list of words that were included.
>>> open('/vr/hostdir/vr.cfg','r').readline().split()
['#', 'VR', 'Configuration', 'File']

Of course the list is made up of elements that can be addressed lets get the second element whose index is 1.
>>> open('/vr/hostdir/vr.cfg','r').readline().split()[1]
'VR'

Which is a string and can be forced to lower case using the lower() method.
>>> open('/vr/hostdir/vr.cfg','r').readline().split()[1].lower()
'vr'

Is this stuff cool or what.


Thursday, May 10, 2007

Life outside Vr

I have had several folks ask about opening reading and parsing external files. There are so many applications that I don't even know where to begin. Whether plain ascii text files, or binary, python is loaded with tools for doing what you want or need. I think I'll just start rambling and see where we head. One thing that you will notice immediately is that there can be literally dozens of ways of accomplishing the same task. I'm not going to pretend that what I am putting forward is the best or most efficient, I'll just mention some tools that come in handy quite often. For today lets just open a file and grab some data from it. Let's pick a file that everyone should have, that being your Vr configuration file \vr\hostdir\vr.cfg. Don't worry, for now we'll just open it in read only mode. In your favorite python editor or pyedi within VrOne type and run the following.

f=open('\vr\hostdir\vr.cfg','r')

Ok, I'll admit that was a trick, it shouldn't have worked, even though everything about the command is totally correct, except that is for the backslashes "\". But wait you say, we are working in windows, and the backslash is what is always used to specify path hierarchy, and you would be correct. The problem is that the backslash is a special character in python (and several other languages, not to mention unix). Python uses a forward slash to separate path elements, or to use backslashes you have to tell python you are sending it a "raw" string and that it should not treat the backslashes as escape characters. I don't start here to be bothersome, but it is important to understand that python expects certain things and may send back something you aren't expecting if you aren't careful. Note the paste of a session where I open the file using 3 valid commands, look at what comes back, and realize that this is outside VrOne in the PythonWin editor.

First I open it with forward slashes because I grew up on unix, the forward slash is always in the same place no matter what keyboard you have, it is what python prefers, and I just like it better. I'll use "f" as the open file object and will open it, display it, and close it. The 'r' means to open it in read-only mode, we'll talk about modes later but I digress.

>>> f=open('/vr/hostdir/vr.cfg','r')
>>> f
Text describing file object which blogger deletes for some reason.
>>> f.close()

Next we'll use backslashes, but they have to be protected by another character so they aren't evaluated as an escape sequence, by coincidence the character that does this is a backslash so each occurence must be entered as
>>> f=open('\\vr\\hostdir\\vr.cfg','r')
>>> f
Text describing file object which blogger deletes for some reason.
>>> f.close()

Next we'll use the backslashes but we'll tell python that the name string is a raw string and that the backslashes shouldn't be evaluated as anything else. Just put an r in front of it and we're good to go.
>>> f=open(r'\vr\hostdir\vr.cfg','r')
>>> f
Text describing file object which blogger deletes for some reason.
>>> f.close()

It is worth noting that if you grab a filename inside Vr using either of the following, it returns forward slashes.
print PyVrGui().OpenFileNameDialog('title','c:/vr/hostdir','*.cfg')
C:/vr/hostdir/vr.cfg
print PyVrGui().OpenFileNameDialog('title','c:\\vr\\hostdir','*.cfg')
C:/vr/hostdir/vr.cfg

That will do for now. Yeah it's pretty slim, but I need to go and we'll actually read something next time. For now though it wouldn't hurt to get familiar with some of the python file methods, from the python docs. You can also get a little more information on the open function from the built-in functions docs (just scroll down to open()), this will come in handy when we move to appending, writing, and binary files.

Monday, May 07, 2007

Still breathing

Just a quick apology that with one graduating college, one high school, and another 2 with year end activities to take in, I just haven't had time to get into anything. I even know where I would like to head but will have to put it on hold for a few weeks. Check back.

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.


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.