|
These notes relate to Python in general, including some help and encouragement for people just getting started with programming, all with reference to Python 2.0 (not 2.1+ yet). Any of the author's Python code published on this "Python Notes" page is placed freely into the public domain (see the "License" statement at the top of the "Code Samples" page). You may notice occasional references to "DEMpy," the working title for a utility being attempted to manage large swaths of USGS DEM terrain data. The notes here come from learning Python to use in that project, starting with PullSDTS. The object isn't to create a general Python text or reference here, but rather to share some answers the author had trouble finding in the progress of this work. |
PullSDTS Tkinter Notes Tkinter 3D Code Samples File Formats SDTS Notes Glossary Index |
Python is an interpreted, cross-platform, object-oriented language available for free download from www.python.org. It is a very capable language and yet is also quite possibly the best language available today for learning programming. "Interpreted" means that Python works in real time with scripts rather than with precompiled executable .exe files (Basic, Javascript, and Perl are other examples of interpreted languages, while Pascal, Java, and C/C++ are compiled). It is also the scripting language embedded into the 3D applications Animation Master, Poser Pro Pack, and trueSpace 4+, as well as older versions of Blender (1.8-2.04) [embedded Python].
If you have little or no programming experience, an excellent start can be found at www.crosswinds.net/~agauld/, where Alan Gauld introduces programming concepts with Python as well as with Basic and Tcl. Experienced programmers may also find it a useful introduction to Python. The expanded version of that site, his book Learn to Program Using Python, is likely to be an even better starting point. You can find it and other Python books at www.amk.ca/bookstore/. Note that many books are still available that were published prior to late 2000, while probably very useful, will be missing information on the significantly improved version 2 of Python. Version 2.0 was released in October 2000, and 2.1 in April 2001.
Standalone Python 2+
Here are some key links related to teaching yourself Python as a standalone language:
Embedded Python
Here are a few key links for learning Python as embedded in various 3D applications.
3D programming for beginners
This concludes a brief introduction to Python and to programming. If any of the above links fail, please let me know. The sections that follow are random topics of interest mainly to those who already use Python.
The supplied Python documentation, in covering most of the language's features, manages to completely ignore how to get a Python program to put up a window and do something with it, even something "so simple" as to write to that Window and close it. Everything from error reports to successful program responses are demonstrated as coming through Python's command line interface. What isn't explained up front to the new user is that 1) whatever of several possible windowing interfaces you go on to use will be an add-on to Python, and 2) working with windows at the outset would distract completely from learning Python itself.
There are many windowing toolkits for Python. Tkinter, which comes with Python, seems to be the best bet for a cross-platform windowing solution, and seems to get the best support from the Python community, despite some distaste among Mac Python users. The official Python FAQ says, It is...very likely that in the future the standard Python GUI API will be based on or at least look very much like the Tkinter interface. Elsewhere on the Python site, Tkinter is described as "Python's de-facto standard GUI...package."
Important for those who plan to distribute their software is that Tkinter is not bound by the GNU type of license that constrains you, your successors, and your users from ever controlling, selling, or giving away (free of all restrictions) the software that uses a library under such a license.
Working with Tkinter isn't simple. Learning it is almost like picking up yet another programming language, and, it actually does come from a completely separate language called Tcl/Tk. However, programming for any interactive windowing interface on a modern operating system is always going to involve some fairly deep complexity. Writing code for multiple platforms doesn't make things simpler, but my experience has been that finding a problem with Mac Python or Mac Tkinter often reveals something I really, really needed to know about my code on Windows, so the extra work is a net gain.
To learn more about Tkinter, see the "Tkinter Notes" page. A new supplement to that page, "Tkinter 3D," shows that Tkinter itself can easily be made to show orthogonal 3D wireframe views.
Python & OpenGL
Python can be used cross-platform with OpenGL. Python's own built-in OpenGL functions only work on SGI Irix machines, but the following free extensions are available separately for Python:
The OpenGL home page is at www.opengl.org.
Part of Python's object-oriented structure is to segregate discrete functions into individual modules where they can be developed by themselves (modules often include self-test code), and where they can be made available to other modules. You routinely import modules that come with Python, but how do you get Python to recognize your own modules? That is, if you write a script useful.py, how do you import its functions into another script? If you try "import useful," you will get, "ImportError: No module named useful."
First, some terminology: The .py files you write are "scripts." A "module," for our purposes here, is a folder or subfolder that you put these scripts into, along with a mandatory __init__.py file. A "package" is collection of modules--a folder and subfolders. A module's name is its folder name. Your Python/dothings folder becomes the dothings module. A subfolder Python/dothings/dofirst will be the "dothings.dofirst" module. (It's easier not to use the term "program" here, because a single script, a module, or a package can all be called programs.)
Each folder and subfolder that contains scripts to be used in modules must include a text file named "__init__.py". It can be empty, or can be put to use for notes or for initialization tasks. If you want to be able to do "from dothings.dofirst import *", the __init__.py file in the Python/dothings/dofirst folder must explicitly tell Python what the "*" refers to, using a line like this:
__all__ = ["dothis", "dothat"]where dothis.py and dothat.py are scripts in that same folder.
Second, you must tell Python that your folder is to be appended to the module search path. You can see what the current path is by using the Python command line intepreter:
>>> import sys >>> sys.path ['C:\\Python', 'C:\\Python\\Tools\\idle', 'c:\\python', 'c:\\python\\dlls', 'c:\\python\\lib', 'c:\\python\\lib\\plat-win', 'c:\\python\\lib\\lib-tk'](The blue bracketed list is typical for Win95+ machines, and will look slightly different on other systems.)
Here's how to append your own module to the search path:
import sys, os p = os.path.join(sys.prefix,'dothings') #find Python's root & add folder name p = os.path.abspath(p) #normalize path for local OS sys.path.append(p) #append the pathNotice that the above code only takes care of one folder--not any subfolders, and also that the module folder happens to be put inside the Python folder, which is an option but not necessary. Python scripts can be run from anywhere on a system that has Python installed, and, if you are delivering Python solutions to non-programmers, it may be safer, and less intimidating for them, if they never have to deal with anything inside the Python folder once Python has been installed on their machines.
Path normalization isn't mandatory if you aren't writing cross-platform code, but it's a good safety step, anyway. (For best portability, stick to the old safe 8.3 file name convention, keeping folder and file names to eight or fewer alphanumeric characters with no spaces.)
You will notice that this isn't a permanent change to the module search path. It will be gone the next time you boot Python. Just make these steps come first in your top script, before importing any of your own modules. Transient appending seems safer and more flexible. It's painless, the programmer can change his or her mind about folder structure during development, and future users will have use of the search path without being forced to retain it when it's no longer needed.
And that's the short answer to setting up your own modules. See also Chapter 6, "Modules," in the supplied Python Tutorial. Then examine how some packages are set up under /Python/Tools.
If you are interested in preparing a single-install executable-like version of your Python program to deliver on Windows and/or Linux, see Gordon McMillan's Installer page. That his unusual approach works is strongly attested by Prabhu Ramachandran MayaVi data visualizer. It is available in one binary that packs in not only its own Python and Tkinter code, but also the VTK visual toolkit with its Python wrapper, and Python itself.
Withdrawn: This section, explaining elementary TAR/gzip .tar.gz/.tgz file open mechanisms, has been reworked and posted on the File Formats page under GZ-gzip and TAR. Also take a look at the PullSDTS utility that implements this information.
Most of Python's time functions are explained minimally in its time module documentation. What isn't explained is how the time.ctime() function relates to individual computer systems, and how that relationship affects interpreting file times provided in "seconds since the epoch," such as when extracting files from a TAR archive file. You cannot simply hand that number to ctime() to get a correct date/time string.
On a Windows machine in the U.S. Mountain time zone, you will get this result in the interpreter:
>>> import time >>> time.ctime(0) 'Wed Dec 31 17:00:00 1969'The ctime() function relates to the number handed to it as if that time value originated on the same machine. Here, with zero seconds on Windows, Python is using the Unix epoch of 1970-01-01 0:00:00 UTC and deducting the seven hours' difference between local and UTC time. That means that, when you go to evaluate the epoch time of a file extracted from a TAR, you will get a seven-hour error. You must add in 25,200 seconds. However, since this computer knows that it is in a location with Daylight Savings Time (DST), and passes that fact to Python, if the date being extracted is within DST, then the correction actually must be for one hour less, or 21,600 seconds.
Beyond those factors there is this: If your script is running on a Mac or Amiga, when you check ctime.ctime(0), you will discover that the function is working with Mac or Amiga epochs rather than the Unix epoch. Hand an extracted TAR file's Unix seconds to ctime() on a Mac and the result is off by 66 years, such as 1932 instead of 1998. On Amiga, a 1998 date will come out as 2006. A slight complication is that the Mac's epoch starts at 8:00:00 UTC on 1 January 1904, and the Amiga's at 6:00:00 UTC on 1 January 1978, as compared with 0:00:00 UTC on 1 January 1970 for Unix.
To get ctime() to work right on a Mac for dealing with Unix times, and accounting for leap years, we must add in 24,107 days less 8 hours, or 2,082,816,000.0 seconds. That number is too large to handle as an integer, so it and all variables used in calculations with it must be floating point. Working completely in floating point is a good idea, anyway, for cross-platform time function compatibility. The Amiga adjustment is to subtract 252,439,200.0 seconds. (The commas are used here for clarity, but don't use them in Python, as it will think you are giving it a tuple with multiple values.)
The point in all this is simple: Factor out all local machine time factors before interpreting what the time was on the originating machine when a particular file was last saved there. We have no idea where that machine was or what time zone it was in, so we need to work from an absolute number of seconds.
By the way, it appears that the Python 2.0 time module on Mac OS9 cannot tell whether the machine time is DST or not, and, for some work, the programmer might need to create his or her own DST time correction. For the purpose of extracting files' date/time stamps, however, all that matters is factoring out the local time that Python thinks is in effect, so don't factor out something that isn't being factoring in.
Since version 1.4a of the PullSDTS script, the required time adjustment is handled like this,
def timeAdjust(t):
t = float(t) + epochAdjust + float(time.timezone)
if time.localtime(t)[8] == 1 and time.daylight != 0:
t = t + float(time.altzone - time.timezone)
return t
where t is seconds, epochAdjust is the differential from the Unix epoch in seconds, and time.timezone is the local differential from UTC in seconds, all handled as floating point. Additionally, if the flag is set for this date as being in DST, and if Python recognizes DST (not true in Arizona and apparently not anywhere for Python on Macs), then that also is adjusted for. This function just hands the adjusted time back to the calling routine, but it could as easily return time.ctime(t) or time.localtime(t).
One further time complication for Mac Python 2.0 is that, although the docs say that the os.utime() function works on Macintosh for assigning date/time attributes, it apparently does not, so a cross-platform script must avoid that function when running on Mac.
Faster data buffering: A script that handles arrays, lists, or strings as big data buffers can be made to run far faster by first writing data out to a temporary file as the script creates the data. When done creating that data and file, read it back from disk to a list or array in a single read() or readlines() statement. Consider this a backdoor way to dimension an array. Writing to lists and arrays in memory should be faster than disk read/writes, and it is faster in some other languages. Appending to lists, arrays, and strings is slow in Python, however, and seems to get slower as the structure grows, yet Python is quite fast at both file reads and writes, especially large block moves such as reading or writing an array of image data.
Finding answers: If you can't find something in the supplied documentation using the doc's own indexing and cross-referencing, try doing a system search on *.htm? and *.py files starting at the Python root folder. Searching on the Web, such as from www.google.com, start with a string such as python "global constants" (for discussions on how, or, as it turns out, why not to use them), and .py "Tk.Frame" to see examples of Python code (in this case, code that implements Python Tkinter GUI frames).
A downside to Python's universality is that Web searches can result in endless hits on obsolete duplicates of Python documentation that (far too many) school sites and others still have up, going back as far as version 0.94!
Assigning values to tuples: As of Python version 2.0, you cannot directly assign values to tuple elements, such as you might in working with a Tkinter font defined by font = "Helvetica",8,"bold". That is, you cannot change the size to 10 point with font[1] = 10. One workaround is to create a font-changing function that takes in the old and new values and returns a new tuple, such as font = fontChange(font,size=10). If a tuple isn't required in the first place, then always use a list.
See also "What's New on the Project Site."