Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning Python - From Novice To Professional (2005)

.pdf
Скачиваний:
139
Добавлен:
17.08.2013
Размер:
13.91 Mб
Скачать

280

C H A P T E R 1 2 G R A P H I C A L U S E R I N T E R F A C E S

You can try out the editor using the following steps:

1.Run the program. You should get a window like the one in the previous runs.

2.Type something in the large text area (for example, “Hello, world!”).

3.Type a file name in the small text field (for example, hello.txt). Make sure that this file does not already exist or it will be overwritten.

4.Click the Save button.

5.Close the editor window (just for fun).

6.Restart the program.

7.Type the same file name in the little text field.

8.Click the Open button. The text of the file should reappear in the large text area.

9.Edit the file to your heart’s content, and save it again.

Now you can keep opening, editing, and saving until you grow tired of that—then you can start thinking of improvements. (How about allowing your program to download files with urllib, for example?)

HEY! WHAT ABOUT PYW?

In Chapter 1, I asked you to give your file the .pyw ending and double-click it (in Windows). Nothing happened, and I promised to explain it later. In Chapter 10, I mentioned it again, and said I’d explain it in this chapter. So I will.

It’s no big deal, really. It’s just that when you double-click an ordinary Python script in Windows, a DOS window appears with a Python prompt in it. That’s fine if you use print and raw_input as the basis of your interface, but now that you know how to make graphical user interfaces, this DOS window will only be in your way. The truth behind the .pyw window is that it will run Python without the DOS window—which is just perfect for GUI programs.

But I’d Rather Use . . .

There are so many GUI toolkits for Python that I can’t possibly show you how to use all of them; I will, however, give you some examples from a couple of the more popular ones (Tkinter and Jython/Swing).

C H A P T E R 1 2 G R A P H I C A L U S E R I N T E R F A C E S

281

Example

To illustrate the various packages, I’ve created a simple example—simpler, even, than the editor example used earlier in the chapter. It’s just a single window containing a single button with the label “Hello” filling the window. When you click the button, it prints out the words “Hello, world!” A simple wxPython version is shown in Listing 12-7.

Listing 12-7. A Simple GUI Example

import wx

def hello(event):

print "Hello, world!"

app = wx.App()

win = wx.Frame(None, title="Hello, wxPython!", size=(200, 100))

button = wx.Button(win, label="Hello") button.Bind(wx.EVT_BUTTON, hello)

win.Show()

app.MainLoop()

In the interest of simplicity, I’m not using any fancy layout features here. The resulting window is shown in Figure 12-6.

Figure 12-6. A simple GUI example

Using Tkinter

Tkinter is an old-timer in the Python GUI business. It is a wrapper around the Tk GUI toolkit (associated with the programming language Tcl). It is included by default in the Windows distribution. The following URLs may be useful:

http://www.ibm.com/developerworks/linux/library/l-tkprg

http://www.nmt.edu/tcc/help/lang/python/tkinter.pdf

282

C H A P T E R 1 2 G R A P H I C A L U S E R I N T E R F A C E S

Example

Here is the GUI example implemented with Tkinter:

from Tkinter import *

def hello(): print 'Hello, world' win = Tk() # Tkinter's 'main window' win.title('Hello, Tkinter! ')

win.geometry('200x100') # Size 200, 200

btn = Button(win, text='Hello ', command=hello) btn.pack(expand=YES, fill=BOTH)

mainloop()

Using Jython and Swing

If you’re using Jython (the Java implementation of Python), packages such as wxPython and Tkinter aren’t available. The only GUI toolkits that are readily available are the Java standard library packages AWT and Swing (Swing is the most recent and considered the standard Java GUI toolkit). The good news is that both of these are automatically available so you don’t have to install them separately. For more information, visit the Jython Web site and look into the Swing documentation written for Java:

http://www.jython.org

http://java.sun.com/docs/books/tutorial/uiswing

Example

Here is the GUI example implemented with Jython and Swing:

from javax.swing import * import sys

def hello(event): print 'Hello, world! ' btn = JButton('Hello') btn.actionPerformed = hello

win = JFrame('Hello, Swing!') win.contentPane.add(btn)

def closeHandler(event): sys.exit() win.windowClosing = closeHandler

C H A P T E R 1 2 G R A P H I C A L U S E R I N T E R F A C E S

283

btn.size = win.size = 200, 100 win.show()

Note that one additional event handler has been added here (closeHandler) because the Close button doesn’t have any useful default behavior in Java Swing. Also note that you don’t have to explicitly enter the main event loop because it’s running in parallel with the program (in a separate thread).

Using Something Else

The basics of most GUI toolkits are the same; unfortunately, however, when learning how to use a new package, it takes time to find your way through all the details that enable you to do exactly what you want. So you should take your time before deciding which package you want to work with (the section “A Plethora of Platforms” earlier in this chapter should give you some idea of where to start), and then immerse yourself in its documentation and start writing code. I hope this chapter has provided the basic concepts you need to make sense of that documentation.

A Quick Summary

Once again, let’s review what we’ve covered in this chapter:

Graphical user interfaces. Graphical user interfaces are useful in making your programs more user-friendly. Not all programs need them, but whenever your program interacts with a user, a GUI is probably helpful.

GUI platforms for Python. Many GUI platforms are available to the Python programmer. Although this richness is definitely a boon, the choice can sometimes be difficult.

wxPython. wxPython is a mature and feature-rich cross-platform GUI toolkit for Python.

Layout. You can position components quite simply by specifying their geometry directly. However, to make them behave properly when their containing window is resized, you will have to use some sort of layout manager. One common layout mechanism in wxPython is sizers.

Event handling. Actions performed by the user trigger events in the GUI toolkit. To be of any use, your program will probably be set up to react to some of these events; otherwise the user won’t be able to interact with it. In wxPython, event handlers are added to components with the Bind method.

What Now?

That’s it. You now know how to write programs that can interact with the outside world through files and GUIs. In the next chapter you learn about another important component of many program systems: Databases.

C H A P T E R 1 3

■ ■ ■

Database Support

Using simple, plain text files can only get you so far. Yes they can get you very far, but at some point you may need some extra functionality. You may want some automated serialization, and you can turn to pickle and shelve (see Chapter 10). But you may want features that go beyond even this. For example, you might want to have automated support for concurrent access to your data; that is, you want to allow several users to read from and write to your diskbased data without causing any corrupted files or the like. Or you may want to be able to perform complex searches using many data fields or properties at the same time, rather than the simple single-key lookup of shelve. There are plenty of solutions to choose from, but if you want this to scale to large amounts of data, and you want the solution to be easily understandable by other programmers, choosing a relatively standard form of database is probably a good idea.

This chapter discusses the Python Database API, a standardized way of connecting to SQL databases, and demonstrates how to execute some basic SQL using this API. The last section also discusses some alternative database technology.

I won’t be giving you a tutorial on relational databases or the SQL language. The documentation for most databases (such as PostgreSQL or MySQL, or, the one used in this chapter, SQLite) should cover what you need to know. If you haven’t used relational databases before, you might want to check out http://www.sqlcourse.com (or just do a Web search on the subject) or The Programmer’s Guide to SQL by Cristian Darie and Karli Watson (Apress, 2003).

The simple database used throughout this chapter (SQLite, discussed in more detail later) is, of course, not the only choice—by far. There are several popular commercial choices (such as Oracle or Microsoft Access) as well as some solid and widespread open source databases (such as MySQL, PostgreSQL and Firebird). Chapter 26 uses PostgreSQL and has some instructions for MySQL. For a list of some other databases supported by Python packages, check out http://www.python.org/topics/database/modules.html, or visit the database category of Vaults of Parnassus (http://www.vex.net/parnassus).

Relational (SQL) databases aren’t the only kind around, of course. There are object databases such as ZODB (http://www.zope.org/Wikis/ZODB), compact table-based ones such as Metakit (http://www.equi4.com/metakit/python.html), or even DB-style databases, such as BSD DB (http://docs.python.org/lib/module-bsddb.html).

The Python DB API

There are lots and lots of SQL databases available out there, and many of them have corresponding client modules in Python (some databases even have several). Most of the basic functionality

285

286 C H A P T E R 1 3 D A T A B A S E S U P P O R T

of all the databases is the same, so a program written to use one of them might easily—in theory—be used with another. The problem with switching between different modules that provide the same functionality (more or less) is usually that their interfaces (APIs) are different. In order to solve this problem for database modules in Python, a standard database API has been agreed upon. The current version of the API (2.0) is defined in PEP 249, Python Database API Specification v2.0 (available from http://python.org/peps/pep-0249.html).

This section gives you an overview of the basics. I won’t cover the optional parts of the API, because they don’t apply to all databases. You can find more information in the PEP mentioned, or in the official Python Database Topic Guide (available from http://python.org/topics/ database). If you’re not really interested in all the API details, you can skip ahead to “Getting Started,” later in this chapter.

Global Variables

Any compliant (that is, compliant with the DB API, version 2.0) database module must have three global variables, which describe the peculiarities of the module. The reason for this is that the API is designed to be very flexible, and to work with several different underlying mechanisms without too much wrapping. If you want your program to work with several different databases, this can be a nuisance, because you have to cover many different possibilities. A more realistic course of action, in many cases, would be to simply check these variables to see that a given database module is acceptable to your program. If it isn’t, you could simply exit with an appropriate error message, for example, or raise some exception. The global variables are summarized in Table 13-1.

Table 13-1. The Module Properties of the Python DB API

Variable Name

Use

apilevel

The version of the Python DB API in use

threadsafety

How thread-safe the module is

paramstyle

Which parameter style is used in the SQL queries

 

 

The API level (apilevel) is simply a string constant, giving the API version in use. According to the DB API version 2.0, it may either have the value '1.0' or the value '2.0'. If the variable isn’t there, the module is not 2.0-compliant, and you should (according to the API) assume that the DB API version 1.0 is in effect. It also probably wouldn’t hurt to write your code to allow other values here; who knows when, say, version 3.0 of the DB API will come out . . .

The thread safety level (threadsafety) is an integer ranging from 0 to 3, inclusive. 0 means that threads may not share the module at all, and 3 means that the module is completely thread-safe. A value of 1 means that threads may share the module itself, but not connections (see “Connections and Cursors,” later), and 2 means that threads may share modules and connections, but not cursors. If you don’t use threads (which, most of the time, you probably won’t), you don’t have to worry about this variable at all.

The parameter style (paramstyle) indicates how parameters are spliced into SQL queries when you make the database perform multiple similar queries (more on that later). The value 'format' indicates standard string formatting (in the C tradition, as used in Python), so you

C H A P T E R 1 3 D A T A B A S E S U P P O R T

287

insert %s where you want to splice in parameters, for example. The value 'pyformat' indicates extended format codes, as used with dictionary splicing, such as %(foo)s. In addition to these Pythonic styles, there are three ways of writing the splicing fields: 'qmark' means that question marks are used, 'numeric' means fields of the form :1 or :2 (where the numbers are the numbers of the parameters), and 'named' means fields like :foobar, where foobar is a parameter name.

Exceptions

The API defines several exceptions, to make fine-grained error handling possible. However, they’re defined in a hierarchy, so you can also catch several types of exceptions with a single except block. (Of course, if you expect everything to work nicely, and you don’t mind having your program shut down in the unlikely event of something going wrong, you can just ignore the exceptions altogether.)

The exception hierarchy is shown in Table 13-2. The exceptions should be available globally in the given database module. For more in-depth descriptions of these exceptions, please see the API specification (the PEP mentioned previously).

Table 13-2. Exceptions Specified in the DB-API

Exception

Superclass

Description

StandardError

 

Generic superclass of all exceptions

Warning

StandardError

Raised if a nonfatal problem occurs

Error

StandardError

Generic superclass of all error conditions

InterfaceError

Error

Errors relating to the interface, not the database

DatabaseError

Error

Superclass for errors relating to the database

DataError

DatabaseError

Problems related to the data; e.g., values out

 

 

of range

OperationalError

DatabaseError

Errors internal to the operation of the database

IntegrityError

DatabaseError

Relational integrity compromised; e.g., key

 

 

check fails

InternalError

DatabaseError

Internal errors in the database; e.g., invalid cursor

ProgrammingError

DatabaseError

User programming error; e.g., table not found

NotSupportedError

DatabaseError

An unsupported feature (e.g., rollback) requested

 

 

 

Connections and Cursors

In order to use the underlying database system, you must first connect to it. For this you use the aptly named function connect. It takes several parameters; exactly which depends on the database. The API defines the parameters in Table 13-3 as a guideline. It recommends that they be usable as keyword arguments, and that they follow the order given in the table. The arguments should all be strings.

288 C H A P T E R 1 3 D A T A B A S E S U P P O R T

Table 13-3. Common Parameters of the connect Function

Parameter Name

Description

Optional?

dsn

Data source name. Specific meaning

No

 

database dependent.

 

user

User name.

Yes

password

User password.

Yes

host

Host name.

Yes

database

Database name.

Yes

 

 

 

You’ll see specific examples of using the connect function in the section “Getting Started” later in this chapter, as well as in Chapter 26.

The connect function returns a connection object. This represents your current session with the database. They support the methods shown in Table 13-4.

Table 13-4. Connection Object Methods

Method Name

Description

close()

Close the connection. Connection object and its cursors are now unusable.

commit()

Commit pending transactions, if supported. Otherwise do nothing.

rollback()

Roll back pending transactions. (May not be available.)

cursor()

Return a cursor object for the connection.

 

 

The rollback function may not be available, because not all databases support transactions. (Transactions are just sequences of actions.) If it exists, it will “undo” any transactions that have not been committed. The commit function is always available, but if the database doesn’t support transactions, it doesn’t actually do anything. If you close a connection and there are still transactions that have not been committed, they will implicitly be rolled back— but only if the database supports rollbacks! So if you don’t want to rely on this, you should always commit before you close your connection. If you commit, you probably don’t have to worry too much about closing your connection; it’s automatically closed when it’s garbage collected. If you want to be on the safe side, though, a call to close won’t cost you that many keystrokes . . .

The cursor function leads us to another topic: cursor objects. You use cursors to execute SQL queries and to examine the results. Cursors support more methods than connections, and probably will be quite a bit more prominent in your programs. Table 13-5 gives an overview of the cursor methods, and Table 13-6 gives an overview of the attributes.

C H A P T E R 1 3 D A T A B A S E S U P P O R T

289

Table 13-5. Cursor Object Methods

Name

Description

callproc(name[, params])

Call a named database procedure with given name and

 

params (optional).

close()

Close the cursor. Cursor is now unusable.

execute(oper[, params])

Execute an SQL operation, possibly with parameters.

executemany(oper, pseq)

Execute an SQL operation for each param set in a sequence.

fetchone()

Fetch the next row of a query result set as a sequence, or None.

fetchmany([size])

Fetch several rows of a query result set. Default size is arraysize.

fetchall()

Fetch all (remaining) rows as a sequence of sequences.

nextset()

Skip to the next available result set (optional).

setinputsizes(sizes)

Used to predefine memory areas for parameters.

setoutputsize(size[, col])

Set a buffer size for fetching big data values.

 

 

Table 13-6. Cursor Object Attributes

Name

Description

description

Sequence of result column descriptions. Read-only.

rowcount

The number of rows in the result. Read-only.

arraysize

How many rows to return in fetchmany. Default is 1.

 

 

Some of these methods will be explained in more detail in the upcoming text, while some (such as setinputsizes and setoutputsizes) will not be discussed. Please consult the PEP for more details.

Types

In order to interoperate properly with the underlying SQL databases, which may place various requirements on the values inserted into columns of certain types, the DB API defines certain constructors and constants (singletons) used for special types and values. For example, if you want to add a date to a database, it should be constructed with (for example) the Date constructor of the corresponding database connectivity module. That allows the connectivity module to perform any necessary transformations behind the scenes. Each module is required to implement the constructors and special values shown in Table 13-7.