6.6. Automated Program LaunchersSuppose, for just a moment, that you wish to ship Python programs to an audience that may be in the very early stages of evolving from computer user to computer programmer. Maybe you are shipping a Python application to nontechnical users, or perhaps you're interested in shipping a set of Python demo programs with a book. Whatever the reason, some of the people who will use your software can't be expected to do anything more than click a mouse. They certainly won't be able to edit their system configuration files to set things such as PATH and PYTHONPATH per your programs' assumptions. Your software will have to configure itself. Luckily, Python scripts can do that too. In the next three sections, we're going to study three modules that aim to automatically launch programs with minimal assumptions about the environment on the host machine:
All of these modules are designed to be reusable in any context where you want your software to be user friendly. By searching for files and configuring environments automatically, your users can avoid (or at least postpone) having to learn the intricacies of environment configuration. 6.6.1. Launcher Module ClientsThe three modules in this section see action in many of this book's examples. In fact, we've already used some of these tools. The launchmodes script we met at the end of the prior chapter imported Launcher functions to hunt for the local python.exe interpreter's path, needed by os.spawnv calls. That script could have assumed that everyone who installs it on their machine will edit its source code to add their own Python location; but the technical know-how required for even that task is already light-years beyond many potential users.[*] It's much nicer to invest a negligible amount of startup time to locate Python automatically.
The two modules listed in Examples 6-14 and 6-15, together with launchmodes of the prior chapter, also form the core of the demo-launcher programs at the top of the examples distribution tree. There's nothing quite like being able to witness programs in action first hand, so I wanted to make it as easy as possible to launch the Python examples in this book. Ideally, they should run straight from the book examples distribution package when clicked, and not require readers to wade through a complex environment installation procedure. However, many demos perform cross-directory imports and so require the book's module package directories to be installed in PYTHONPATH; it is not enough just to click on some programs' icons at random. Moreover, when first starting out, users can't be assumed to have added the Python executable to their system search path either; the name "python" might not mean anything in the shell. At least on platforms tested thus far, the following two modules solve such configuration problems. For example, the Launch_PyDemos.pyw script in the root directory automatically configures the system and Python execution environments using Launcher.py tools, and then spawns PyDemos2.pyw, a Tkinter GUI demo interface we'll meet in Chapter 10. PyDemos in turn uses launchmodes to spawn other programs that also inherit the environment settings made at the top. The net effect is that clicking any of the Launch_* scripts starts Python programs even if you haven't touched your environment settings at all. You still need to install Python if it's not present, of course, but the Python Windows self-installer is a simple point-and-click affair too. Because searches and configuration take extra time, it's still to your advantage to eventually configure your environment settings and run programs such as PyDemos directly instead of through the launcher scripts. But there's much to be said for instant gratification when it comes to software. These tools will show up in other contexts later in this text. For instance, a GUI example in Chapter 11, big_gui, will use a Launcher tool to locate canned Python source-distribution demo programs in arbitrary and unpredictable places on the underlying computer. The LaunchBrowser script in Example 6-15 also uses Launcher to locate suitable web browsers and is itself used to start Internet demos in the PyDemos and PyGadgets launcher GUIsthat is, Launcher starts PyDemos, which starts LaunchBrowser, which uses Launcher. By optimizing generality, these modules also optimize reusability. 6.6.2. Launching Programs Without Environment SettingsBecause the Launcher.py file is heavily documented, I won't go over its fine points in narrative here. Instead, I'll just point out that all of its functions are useful by themselves, but the main entry point is the launchBookExamples function near the end; you need to work your way from the bottom of this file up in order to glimpse its larger picture. The launchBookExamples function uses all the others to configure the environment and then spawn one or more programs to run in that environment. In fact, the top-level demo launcher scripts shown in Examples 6-12 and 6-13 do nothing more than ask this function to spawn GUI demo interface programs we'll meet in Chapter 10 (e.g., PyDemos2.pyw and PyGadgets_bar.pyw). Because the GUIs are spawned indirectly through this interface, all programs they spawn inherit the environment configurations too. Example 6-12. PP3E\Launch_PyDemos.pyw
Example 6-13. PP3E\Launch_PyGadgets_bar.pyw
When run directly, PyDemos2.pyw and PyGadgets_bar.pyw instead rely on the configuration settings on the underlying machine. In other words, Launcher effectively hides configuration details from the GUI interfaces by enclosing them in a configuration program layer. To understand how, study Example 6-14. Example 6-14. PP3E\Launcher.py
One way to understand the launcher script is to trace the messages it prints along the way. When run on my Windows test machine for the third edition of this book, I have a PYTHONPATH but have not configured my PATH to include Python. Here is the script's trace output: C:\...\PP3E>Launcher.py C:\Mark\PP3E-cd\Examples\PP3E . \ ; starting on win32... Using this Python executable: C:\Python24\python.exe Press <enter> key Using this examples root directory: C:\Mark\PP3E-cd\Examples Press <enter> key PATH start: C:\WINDOWS\system32;...more deleted...;C:\Program Files\MySQL\MySQL Server 4.1\bin PATH updated: C:\WINDOWS\system32;...more deleted...;C:\Program Files\MySQL\MySQL Server 4.1\bin; C:\Python24 PYTHONPATH start: C:\Mark\PP3E-cd\Examples;C:\Mark\PP2E-cd\Examples Exists C:\Mark\PP3E-cd\Examples PYTHONPATH unchanged Environment configured Press <enter> key Spawning: C:\Mark\PP3E-cd\Examples\PP3E\Gui\TextEditor\textEditor.py Spawning: C:\Mark\PP3E-cd\Examples\PP3E\Lang\Calculator\calculator.py Spawning: C:\Mark\PP3E-cd\Examples\PP3E\PyDemos2.pyw Spawning: C:\Mark\PP3E-cd\Examples\PP3E\echoEnvironment.pyw Press Enter Four programs are spawned with PATH and PYTHONPATH preconfigured according to the location of your Python interpreter program, the location of your examples distribution tree, and the list of required PYTHONPATH enTRies in the script variable, PP3EpackageRoots. Just one directory needs to be added to PYTHONPATH for book examples todaythe one containing the PP3E root directorysince all cross-directory imports are package paths relative to the PP3E root. That makes it easier to configure, but the launcher code still supports a list of entries for generality (it may be used for a different tree). To demonstrate, let's look at some trace outputs obtained with different configurations in the past. When run by itself without a PYTHONPATH setting, the script finds a suitable Python and the examples root directory (by hunting for its README file), uses those results to configure PATH and PYTHONPATH settings if needed and spawns a precoded list of program examples. For example, here is a launch on Windows with an empty PYTHONPATH, a different directory structure, and an older version of Python: C:\temp\examples>set PYTHONPATH= C:\temp\examples>python Launcher.py C:\temp\examples . \ ; starting on win32... Looking for python.exe on ['C:\\WINDOWS', 'C:\\WINDOWS', 'C:\\WINDOWS\\COMMAND', 'C:\\STUFF\\BIN.MKS', 'C:\\PROGRAM FILES\\PYTHON'] Not at C:\WINDOWS\python.exe Not at C:\WINDOWS\python.exe Not at C:\WINDOWS\COMMAND\python.exe Not at C:\STUFF\BIN.MKS\python.exe Found C:\PROGRAM FILES\PYTHON\python.exe Using this Python executable: C:\PROGRAM FILES\PYTHON\python.exe Press <enter> key Using this examples root directory: C:\temp\examples Press <enter> key PATH start C:\WINDOWS;C:\WINDOWS;C:\WINDOWS\COMMAND;C:\STUFF\BIN.MKS; C:\PROGRAM FILES\PYTHON PATH unchanged PYTHONPATH start: Adding C:\temp\examples\Part3 Adding C:\temp\examples\Part2 Adding C:\temp\examples\Part2\Gui Adding C:\temp\examples PYTHONPATH updated: C:\temp\examples\Part3;C:\temp\examples\Part2;C:\temp\examples\Part2\Gui; C:\temp\examples; Environment configured Press <enter> key Spawning: C:\temp\examples\Part2\Gui\TextEditor\textEditor.pyw Spawning: C:\temp\examples\Part2\Lang\Calculator\calculator.py Spawning: C:\temp\examples\PyDemos.pyw Spawning: C:\temp\examples\echoEnvironment.pyw When used by the PyDemos launcher script, Launcher does not pause for key presses along the way (the trace argument is passed in false). Here is the output generated when using the module to launch PyDemos with PYTHONPATH already set to include all the required directories; the script both avoids adding settings redundantly and retains any exiting settings already in your environment (again, this reflects an older tree structure and Python install to demonstrate the search capabilities of the script): C:\PP3rdEd\examples>python Launch_PyDemos.pyw Looking for python.exe on ['C:\\WINDOWS', 'C:\\WINDOWS', 'C:\\WINDOWS\\COMMAND', 'C:\\STUFF\\BIN.MKS', 'C:\\PROGRAM FILES\\PYTHON'] Not at C:\WINDOWS\python.exe Not at C:\WINDOWS\python.exe Not at C:\WINDOWS\COMMAND\python.exe Not at C:\STUFF\BIN.MKS\python.exe Found C:\PROGRAM FILES\PYTHON\python.exe PATH start C:\WINDOWS;C:\WINDOWS;C:\WINDOWS\COMMAND;C:\STUFF\BIN.MKS; C:\PROGRAM FILES\PYTHON PATH unchanged PYTHONPATH start: C:\PP3rdEd\examples\Part3;C:\PP3rdEd\examples\Part2;C:\PP3rdEd\examples\ Part2\Gui;C:\PP3rdEd\examples Exists C:\PP3rdEd\examples\Part3 Exists C:\PP3rdEd\examples\Part2 Exists C:\PP3rdEd\examples\Part2\Gui Exists C:\PP3rdEd\examples PYTHONPATH unchanged Spawning: C:\PP3rdEd\examples\PyDemos.pyw And finally, here is the trace output of a launch on my Linux system; because Launcher is written with portable Python code and library calls, environment configuration and directory searches work just as well there: [mark@toy ~/PP3rdEd/examples]$ unsetenv PYTHONPATH [mark@toy ~/PP3rdEd/examples]$ python Launcher.py /home/mark/PP3rdEd/examples . / : starting on linux2... Looking for python on ['/home/mark/bin', '.', '/usr/bin', '/usr/bin', '/usr/local/ bin', '/usr/X11R6/bin', '/bin', '/usr/X11R6/bin', '/home/mark/ bin', '/usr/X11R6/bin', '/home/mark/bin', '/usr/X11R6/bin'] Not at /home/mark/bin/python Not at ./python Found /usr/bin/python Using this Python executable: /usr/bin/python Press <enter> key Using this examples root directory: /home/mark/PP3rdEd/examples Press <enter> key PATH start /home/mark/bin:.:/usr/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/bin:/ usr /X11R6/bin:/home/mark/bin:/usr/X11R6/bin:/home/mark/bin:/usr/X11R6/bin PATH unchanged PYTHONPATH start: Adding /home/mark/PP3rdEd/examples/Part3 Adding /home/mark/PP3rdEd/examples/Part2 Adding /home/mark/PP3rdEd/examples/Part2/Gui Adding /home/mark/PP3rdEd/examples PYTHONPATH updated: /home/mark/PP3rdEd/examples/Part3:/home/mark/PP3rdEd/examples/Part2:/home/ mark/PP3rdEd/examples/Part2/Gui:/home/mark/PP3rdEd/examples: Environment configured Press <enter> key Spawning: /home/mark/PP3rdEd/examples/Part2/Gui/TextEditor/textEditor.py Spawning: /home/mark/PP3rdEd/examples/Part2/Lang/Calculator/calculator.py Spawning: /home/mark/PP3rdEd/examples/PyDemos.pyw Spawning: /home/mark/PP3rdEd/examples/echoEnvironment.pyw In all but the first of these launches, the Python interpreter was found on the system search path, so no real searches were performed (the Not at lines near the top represent the module's which function, and the first launch used the more recent sys.executable instead of searching). In a moment, we'll also use the launcher's which and guessLocation functions to look for web browsers in a way that kicks off searches in standard install directory trees. Later in the book, we'll use this module in other waysfor instance, to search for demo programs and source code files somewhere on the machine with calls of this form: C:\temp>python >>> from PP3E.Launcher import guessLocation >>> guessLocation('hanoi.py') Searching for hanoi.py in C:\Program Files\Python Searching for hanoi.py in C:\temp\examples Searching for hanoi.py in C:\Program Files Searching for hanoi.py in C:\ 'C:\\PP3rdEd\\cdrom\\Python1.5.2\\SourceDistribution\\Unpacked\\Python-1.5.2 \\Demo\\tkinter\\guido\\hanoi.py' >>> from PP3E.Launcher import findFirst >>> findFirst('.', 'PyMailGui.py') '.\\examples\\Internet\\Email\\PyMailGui.py' >>> findFirst('.', 'peoplecgi.py', True) Scanning . Scanning .\PP3E Scanning .\PP3E\Preview Scanning .\PP3E\Preview\.idlerc Scanning .\PP3E\Preview\cgi-bin '.\\PP3E\\Preview\\cgi-bin\\peoplecgi.py' Such searches aren't necessary if you can rely on an environment variable to give at least part of the path to a file; for instance, paths scripts within the PP3E examples tree can be named by joining the PP3EHOME shell variable with the rest of the script's path (assuming the rest of the script's path won't change and that we can rely on that shell variable being set everywhere). Some scripts may also be able to compose relative paths to other scripts using the sys.path[0] home-directory indicator added for imports (see Chapter 3). But in cases where a file can appear at arbitrary places, searches like those shown previously are sometimes the best scripts can do. The earlier hanoi.py program file, for example, can be anywhere on the underlying machine (if present at all); searching is a more user-friendly final alternative than simply giving up. 6.6.3. Launching Web Browsers PortablyWeb browsers can do amazing things these days. They can serve as document viewers, remote program launchers, database interfaces, media players, and more. Being able to open a browser on a local or remote page file from within a script opens up all kinds of interesting user-interface possibilities. For instance, a Python system might automatically display its HTML-coded documentation when needed by launching the local web browser on the appropriate page file.[*] Because most browsers know how to present pictures, audio files, and movie clips, opening a browser on such a file is also a simple way for scripts to deal with multimedia generically.
The next script listed in this chapter is less ambitious than Launcher.py, but equally reusable: LaunchBrowser.py attempts to provide a portable interface for starting a web browser. Because techniques for launching browsers vary per platform, this script provides an interface that aims to hide the differences from callers. Once launched, the browser runs as an independent program and may be opened to view either a local file or a remote page on the Web. Here's how it works. Because most web browsers can be started with shell command lines, this script simply builds and launches one as appropriate. For instance, to run a Netscape browser on Linux, a shell command of the form netscape url is run, where url begins with file:// for local files and http:// for live remote-page accesses (this is per URL conventions we'll meet in more detail later in Chapter 16). On Windows, a shell command such as start url achieves the same goal. Here are some platform-specific highlights:
All of this is easily customized (this is Python code, after all), and you may need to add additional logic for other platforms. But on all of my machines, the script makes reasonable assumptions that allow me to largely forget most of the platform-specific bits previously discussed; I just call the same launchBrowser function everywhere. For more details, let's look at Example 6-15. Example 6-15. PP3E\LaunchBrowser.py
6.6.3.1. Launching browsers with command linesThis module is designed to be both run and imported. When run by itself on my Windows machine, Firefox starts up. The requested page file is always displayed in a new browser window when os.spawnv is applied but in the currently open browser window (if any) when running a start command: C:\...\PP3E>LaunchBrowser.py Opening file://C:\Mark\PP3E-cd\Examples\PP3E/Internet/Web/PyInternetDemos.html Starting The seemingly odd mix of forward and backward slashes in the URL here works fine within the browser; it pops up the window shown in Figure 6-2. Note that this script may be renamed with a .pyw extension by the time you fetch its source in order to suppress its pop-up window on Windows; rename back to a .py to see its trace outputs. Figure 6-2. Launching a Windows browser on a local fileBy default, a start command is spawned; to see the browser search procedure in action on Windows, set the script's useWinStart variable to False (or 0). The script will search for a browser on your PATH settings, and then search in common Windows install directories hardcoded in Launcher.py. Here is the search in action on an older machine with Internet Explorer as the first in the list of browsers to try (the PATH on my newer machine is too complex to bear): C:\...\PP3E>python LaunchBrowser.py -file C:\Stuff\Website\public_html\about-pp.html Opening file://C:\Stuff\Website\public_html\about-pp.html Looking for IEXPLORE.EXE on ['C:\\WINDOWS', 'C:\\WINDOWS', 'C:\\WINDOWS\\COMMAND', 'C:\\STUFF\\BIN.MKS', 'C:\\PROGRAM FILES\\PYTHON'] Not at C:\WINDOWS\IEXPLORE.EXE Not at C:\WINDOWS\IEXPLORE.EXE Not at C:\WINDOWS\COMMAND\IEXPLORE.EXE Not at C:\STUFF\BIN.MKS\IEXPLORE.EXE Not at C:\PROGRAM FILES\PYTHON\IEXPLORE.EXE IEXPLORE.EXE not on system path Searching for IEXPLORE.EXE in C:\Program Files\Python Searching for IEXPLORE.EXE in C:\PP3rdEd\examples\PP3E Searching for IEXPLORE.EXE in C:\Program Files Spawning C:\Program Files\Internet Explorer\IEXPLORE.EXE If you study these trace message you'll notice that the browser wasn't on the system search path but was eventually located in a local C:\Program Files subdirectory; this is just the Launcher module's which and guessLocation functions at work. As run here, the script searches for Internet Explorer first; if that's not to your liking, try changing the script's TRies list to make Netscape (or Firefox) first: C:\...\PP3E>python LaunchBrowser.py Opening file://C:\PP3rdEd\examples\PP3E/Internet/Cgi-Web/PyInternetDemos.html Looking for netscape.exe on ['C:\\WINDOWS', 'C:\\WINDOWS', 'C:\\WINDOWS\\COMMAND', 'C:\\STUFF\\BIN.MKS', 'C:\\PROGRAM FILES\\PYTHON'] Not at C:\WINDOWS\netscape.exe Not at C:\WINDOWS\netscape.exe Not at C:\WINDOWS\COMMAND\netscape.exe Not at C:\STUFF\BIN.MKS\netscape.exe Not at C:\PROGRAM FILES\PYTHON\netscape.exe netscape.exe not on system path Searching for netscape.exe in C:\Program Files\Python Searching for netscape.exe in C:\PP3rdEd\examples\PP3E Searching for netscape.exe in C:\Program Files Spawning C:\Program Files\Netscape\Communicator\Program\netscape.exe Here, the script eventually found Netscape in a different install directory on the local machine. Besides automatically finding a user's browser for him, this script also aims to be portable. When running this file unchanged on Linux, the local Netscape browser starts if it lives on your PATH; otherwise, others are tried: [mark@toy ~/PP3rdEd/examples/PP3E]$ python LaunchBrowser.py Opening file:///home/mark/PP3rdEd/examples/PP3E/Internet/Cgi- Web/PyInternetDemos.html Looking for netscape on ['/home/mark/bin', '.', '/usr/bin', '/usr/bin', '/usr/local/bin', '/usr/X11R6/bin', '/bin', '/usr/X11R6/bin', '/home/mark/ bin', '/usr/X11R6/bin', '/home/mark/bin', '/usr/X11R6/bin'] Not at /home/mark/bin/netscape Not at ./netscape Found /usr/bin/netscape Running netscape [mark@toy ~/PP3rdEd/examples/PP3E]$ If you have an Internet connection, you can open pages at remote servers toothe next command opens the root page at my site on the starship.python.net server, located somewhere on the East Coast the last time I checked: C:\...\PP3E>python LaunchBrowser.py -live ~lutz starship.python.net Opening http://starship.python.net/~lutz Starting In Chapter 10, we'll see that this script is also run to start Internet examples in the top-level demo launcher system: the PyDemos script presented in that chapter portably opens local or remote web page files with this button-press callback: [File mode] pagepath = os.getcwd( ) + '/Internet/Web' demoButton('PyMailCGI2', 'Browser-based pop/smtp email interface', 'LaunchBrowser.pyw -file %s/PyMailCgi/pymailcgi.html' % pagepath, pymailcgifiles) [Live mode] site = 'localhost:%s' demoButton('PyMailCGI2', 'Browser-based pop/smtp email interface', 'LaunchBrowser.pyw -live pymailcgi.html '+ (site % 8000), pymailcgifiles) 6.6.3.2. Launching browsers with function callsOther programs can spawn LaunchBrowser.py command lines such as those shown previously with tools such as os.system, as usual; but since the script's core logic is coded in a function, it can just as easily be imported and called: >>> from PP3E.LaunchBrowser import launchBrowser >>> launchBrowser(Page=r'C:\Mark\WEBSITE\public_html\about-pp.html') Opening file://C:\Mark\WEBSITE\public_html\about-pp.html Starting >>> When called like this, launchBrowser isn't much different than spawning a start command on DOS or a netscape command on Linux, but the Python launchBrowser function is designed to be a portable interface for browser startup across platforms. Python scripts can use this interface to pop up local HTML documents in web browsers; on machines with live Internet links, this call even lets scripts open browsers on remote pages on the Web: >>> launchBrowser(Mode='-live', Page='index.html', Site='www.python.org') Opening http://www.python.org/index.html Starting >>> launchBrowser(Mode='-live', Page='PyInternetDemos.html', ... Site='localhost') Opening http://localhost/PyInternetDemos.html Starting On a computer where there is just a dial-up connection, the first call here opens a new Internet Explorer GUI window if needed, dials out through a modem, and fetches the Python home page from http://www.python.org on both Windows and Linuxnot bad for a single function call. On broadband connections, the page comes up directly. The second call does the same but, using a locally running web server, opens a web demos page we'll explore in Chapter 16. 6.6.3.3. Viewing multimedia in browsersI mentioned earlier that browsers are a cheap way to present multimedia. Alas, this sort of thing is best viewed live, so the best I can do is show startup commands here. The next command line and function call, for example, display two GIF images in Internet Explorer on my machine (be sure to use full local pathnames). The result of the first of these is captured in Figure 6-3 (you may have to edit the browser tries list and start-mode flags on your machine to make this work). Figure 6-3. Launching a browser on an image fileC:\...\PP3E>python LaunchBrowser.py -file C:\Mark\PP3E-cd\Examples\PP3E\Gui\PIL\images\dublin3.jpg C:\temp>python >>> from LaunchBrowser import launchBrowser >>> launchBrowser(Page=r'C:\temp\Examples\PP3E\Gui\gifs\mp_lumberjack.gif') The next command line and call open the sousa.au audio file on my machine; the second of these downloads the file from http://www.rmi.net first. If all goes as planned, the Monty Python theme song should play on your computer: C:\PP3rdEd\examples>python LaunchBrowser.py -file C:\Mark\PP3E-cd\Examples\PP3E\Internet\Ftp\sousa.au Opening file://C:\PP3E-cd\Examples\PP3E\Internet\Ftp\sousa.au Starting >>> launchBrowser(Mode='-live', ... Site='www.rmi.net', ... Page='~lutz/sousa.au', ... verbose=0) >>> Of course, you could just pass these filenames to a spawned start command or os.startfile call on Windows, or run the appropriate handler program directly with something like os.system. But opening these files in a browser is a more portable approach; you don't need to keep track of a set of file-handler programs per platform. Provided your scripts use a portable browser launcher such as LaunchBrowser, you don't even need to keep track of a browser per platform. That generality is a win unless you wish to do something more specific for certain media types or can't run a web browser. On some PDAs, for instance, you may not be able to open a general web browser on a particular file. In the next section, we'll see how to get more specific when we need to. Finally, I want to point out that LaunchBrowser reflects browsers that I tend to use. For instance, it tries to find Firefox and then Internet Explorer before Netscape on Windows, and prefers Netscape over Mosaic and Lynx on Linux, but you should feel free to change these choices in your copy of the script. In fact, both LaunchBrowser and Launcher make a few heuristic guesses when searching for files that may not make sense on every computer. Configure as needed. 6.6.4. A Portable Media File Player ToolReptilian minds think alike. Roughly one year after I wrote the LaunchBrowser script of the prior section for the second edition of this book, Python sprouted a new standard library module that serves a similar purpose: webbrowser. In this section, we wrap up the chapter with a script that makes use of this new module as well as the Python mimetypes module in order to implement a generic, portable, and extendable media file player. 6.6.4.1. The Python webbrowser moduleLike LaunchBrowser of the prior section, the standard library webbrowser module also attempts to provide a portable interface for launching browsers from scripts. Its implementation is more complex but likely to support more options and platforms than the LaunchBrowser script presented earlier (classic Macintosh browsers, for instance, are directly supported as well). Its interface is straightforward: import webbrowser webbrowser.open_new('file://' + fullfilename) # or http://... The preceding code will open the named file in a new web browser window using whatever browser is found on the underlying computer or raise an exception if it cannot. Use the module's open call to reuse an already-open browser window if possible, and use an argument string of the form "http://..." to open a page on a web server. In fact, you can pass in any URL that the browser understands. The following pops up Python's home page in a new browser window, for example: >>> webbrowser.open_new('http://www.python.org') Among other things, this is an easy way to display HTML documents as well as media files, as shown in the prior section. We'll use this module later in this book as a way to display HTML-formatted email messages in the PyMailGUI program in Chapter 15. See the Python library manual for more details. In Chapter 16, we'll also meet a related call, urllib.urlopen, which fetches a web page's text but does not open it in a browser. To demonstrate the webbrowser module's basic utility, though, let's code another way to open multimedia files. Example 6-16 tries to open a media file on your computer in a somewhat more intelligent way. As a last resort, it always falls back on trying to open the file in a web browser, much like we did in the prior section. Here, though, we first try to run a type-specific player if one is specific in tables, and we use the Python standard library's webbrowser to open a browser instead of using our LaunchBrowser. 6.6.4.2. The Python mimetypes moduleTo make this even more useful, we also use the Python mimetypes standard library module to automatically determine the media type from the filename. We get back a type/subtype MIME content-type string if the type can be determined or None if the guess failed: >>> import mimetypes >>> mimetypes.guess_type('spam.jpg') ('image/jpeg', None) >>> mimetypes.guess_type('TheBrightSideOfLife.mp3') ('audio/mpeg', None) >>> mimetypes.guess_type('lifeofbrian.mpg') ('video/mpeg', None) >>> mimetypes.guess_type('lifeofbrian.xyz') # unknown type (None, None) Stripping off the first part of the content-type string gives the file's general media type, which we can use to select a generic player: >>> contype, encoding = mimetypes.guess_type('spam.jpg') >>> contype.split('/')[0] 'image' A subtle thing: the second item in the tuple returned from the mimetypes guess is an encoding type we won't use here for opening purposes. We still have to pay attention to it, thoughif it is not None, it means the file is compressed (gzip or compress), even if we receive a media content type. For example, if the filename is something like spam.gif.gz, it's a compressed image that we don't want to try to open directly: >>> mimetypes.guess_type('spam.gz') # content unknown (None, 'gzip') >>> mimetypes.guess_type('spam.gif.gz') # don't play me! ('image/gif', 'gzip') >>> mimetypes.guess_type('spam.zip') # skip archives ('application/zip', None) This module is even smart enough to give us a filename extension for a type: >>> mimetypes.guess_type('sousa.au') ('audio/basic', None) >>> mimetypes.guess_extension('audio/basic') '.au' We'll use the mimetypes module again in FTP examples in Chapter 14 to determine transfer type (text or binary), and in our email examples in Chapters 14 and 15 to send, save, and open mail attachments. In Example 6-16, we use mimetypes to select a table of platform-specific player commands for the media type of the file to be played. That is, we pick a player table for the file's media type, and then pick a command from the player table for the platform. At each step, we give up and run a web browser if there is nothing more specific to be done. The end result is a general and smarter media player tool that you can extend as needed. It will be as portable and specific as the tables you provide to it. Example 6-16. PP3E\System\Media\playfile.py
One coding note: we could also write the playknownfile function the following way (this form is more concise, but some future readers of our code might make the case that it is also less explicit and hence less understandable, especially if we code the same way in playfile with an empty table default): defaultplayer = Webbrowser( ) player = playertable.get(sys.platform, defaultplayer) player.run(mediafile, **options) Study this script's code and run it on your own computer to see what happens. As usual, you can test it interactively (use the package path to import from a different directory): >>> from PP3E.System.Media.playfile import playfile >>> playfile('mov10428.mpg') We'll use this example again as an imported library like this in Chapter 14 to open media files downloaded by FTP. When the script file is run directly, if all goes well, its self-test code at the end opens a number of audio, image, and video files located in the script's directory, using either platform-specific players or a general web browser on your machine. Just for fun, it also opens an HTML file and a Word document to test the web browser code. As is, its player tables are only populated with commands for the machines on which I tested it:
Figure 6-4 shows the script's handiwork on Windows. For other platforms and machines, you will likely have to extend the player dictionaries with platform-specific entries, within this file, or by assigning from outside: import playfile playfile.audiotools['platformX'] = playfile.Cmdline('...') playfile.mimetable['newstuff'] = {...} Figure 6-4. Launching media files with specific playersOr you can pass your own player table to the playfile function: from playfile import playfile myplayers = {...} # or start with mimetools.copy( ) playfile('Nautyus_Maximus.xyz', myplayers) The MediaTool classes in this file provide general ways to open files, but you may also need to subclass to customize for unique cases. This script also assumes the media file is located on the local machine (even though the webbrowser module supports remote files with "http://" names), and it does not currently allow different players for different MIME subtypes (you may want to handle both "text/plain" and "text/xml" differently). In fact, this script is really just something of a simple framework that was designed to be extended. As always, hack on; this is Python, after all.
|