PopUp Alert in Lines

Our first GUI application is a bit odd. First, it must be run from the console, and second it has no "decorations"—no title bar, no system menu, no X close button. Figure 4.1 shows the whole thing.

Wake Up

Figure 4.1 The Alert program To get the output displayed, we could enter a command line like this:

C:\>cd c:\pyqt\chap04 C:\pyqt\chap04>alert.pyw 12:15 Wake Up

When run, the program executes invisibly in the background, simply marking time until the specified time is reached. At that point, it pops up a window with the message text. About a minute after showing the window, the application will automatically terminate.

The specified time must use the 24-hour clock. For testing purposes we can use a time that has just gone; for example, by using 12:15 when it is really 12:30, the window will pop up immediately (well, within less than a second).

Now that we know what it does and how to run it, we will review the implementation. The file is a few lines longer than 25 lines because we have not counted comment lines and blank lines in the total—but there are only 25 lines of executable code. We will begin with the imports.

import sys import time from PyQt4.QtCore import * from PyQt4.QtGui import *

We import the sys module because we want to access the command-line arguments it holds in the sys.argv list. The time module is imported because we need its sleep() function, and we need the PyQt modules for the GUI and for the QTime class.

app = QApplication(sys.argv)

We begin by creating a QApplication object. Every PyQt GUI application must have a QApplication object. This object provides access to global-like information such as the application's directory, the screen size (and which screen the application is on, in a multihead system), and so on. This object also provides the event loop, discussed shortly.

When we create a QApplication object we pass it the command-line arguments; this is because PyQt recognizes some command-line arguments of its own, such as -geometry and -style, so we ought to give it the chance to read them. If QApplication recognizes any of the arguments, it acts on them, and removes them from the list it was given. The list of arguments that QApplication recognizes is given in the QApplication's initializer's documentation.

try:

due = QTime.currentTime() message = "Alert!" if len(sys.argv) < 2: raise ValueError hours, mins = sys.argv[1].split(":") due = QTime(int(hours), int(mins)) if not due.isValid(): raise ValueError if len(sys.argv) > 2:

message = " ".join(sys.argv[2:]) except ValueError:

message = "Usage: alert.pyw HH:MM [optional message]" # 24hr clock

At the very least, the application requires a time, so we set the due variable to the time right now. We also provide a default message. If the user has not given at least one command-line argument (a time), we raise a ValueError exception. This will result in the time being now and the message being the "usage" error message.

If the first argument does not contain a colon, a ValueError will be raised when we attempt to unpack two items from the split() call. If the hours or minutes are not a valid number, a ValueError will be raised by int(), and if the hours or minutes are out of range, due will be an invalid QTime, and we raise a ValueError ourselves. Although Python provides its own date and time classes, the PyQt date and time classes are often more convenient (and in some respects more powerful), so we tend to prefer them.

If the time is valid, we set the message to be the space-separated concatenation of the other command-line arguments if there are any; otherwise, we leave it as the default "Alert!" that we set at the beginning. (When a program is executed on the command line, it is given a list of arguments, the first being the invoking name, and the rest being each sequence of nonwhitespace characters, that is, each "word", entered on the command line. The words may be changed by the shell—for example, by applying wildcard expansion. Python puts the words it is actually given in the sys.argv list.)

Now we know when the message must be shown and what the message is.

while QTime.currentTime() < due: time.sleep(20) # 20 seconds

We loop continuously, comparing the current time with the target time. The loop will terminate if the current time is later than the target time. We could have simply put a pass statement inside the loop, but if we did that Python would loop as quickly as possible, gobbling up processor cycles for no good reason. The time.sleep() command tells Python to suspend processing for the specified number of seconds, 20 in this case. This gives other programs more opportunity to run and makes sense since we don't want to actually do anything while we wait for the due time to arrive.

Apart from creating the QApplication object, what we have done so far is standard console programming.

label = QLabel("<font color=red size=72><b>" + message + "</b></font>")

label.setWindowFlags(Qt.SplashScreen)

label.show()

QTimer.singleShot(60000, app.quit) # 1 minute app.exec_()

We have created a QApplication object, we have a message, and the due time has arrived, so now we can begin to create our application. A GUI application needs widgets, and in this case we need a label to show the message. A QLabel can accept HTML text, so we give it an HTML string that tells it to display bold red text of size 72 points.*

★The supported HTML tags are listed at http://doc.trolltech.com/richtext-html-subset.html.

In PyQt, any widget can be used as a top-level window, even a button or a label. When a widget is used like this, PyQt automatically gives it a title bar. We don't want a title bar for this application, so we set the label's window flags to those used for splash screens since they have no title bar. Once we have set up the label that will be our window, we call show() on it. At this point, the label window is not shown! The call to show() merely schedules a "paint event", that is, it adds a new event to the QApplication object's event queue that is a request to paint the specified widget.

Next, we set up a single-shot timer. Whereas the Python library's time.sleep() function takes a number of seconds, the QTimer.singleShot() function takes a number of milliseconds. We give the singleShot() method two arguments: how long until it should time out (one minute in this case), and a function or method for it to call when it times out.

Signals and slots

So now we have two events scheduled: A paint event that wants to take place immediately, and a timer timeout event that wants to take place in a minute's time.

The call to app.exec_() starts off the QApplication object's event loop.* The first event it gets is the paint event, so the label window pops up on-screen with the given message. About one minute later the timer timeout event occurs and the QApplication.quit() method is called. This method performs a clean termination of the GUI application. It closes any open windows, frees up any resources it has acquired, and exits.

Event loops are used by all GUI applications. In pseudocode, an event loop looks like this:

while True:

event = getNextEvent() if event:

if event == Terminate:

break processEvent(event)

When the user interacts with the application, or when certain other things occur, such as a timer timing out or the application's window being uncovered (maybe because another application was closed), an event is generated inside PyQt and added to the event queue. The application's event loop continuously

In PyQt terminology, the function or method we have given is called a slot , although in the PyQt documentation the terms "callable", "Python slot", and

"Qt slot" are used to distinguish slots from Python's_slots_, a feature of new-style classes that is described in the Python Language Reference. In this book we will use the PyQt terminology, since we never use slots .

*PyQt uses exec_() rather than exec() to avoid conflicting with Python's built-in exec statement.

Classic Batch-processing Application

Figure 4.2 Batch processing applications versus GUI applications

checks to see whether there is an event to process, and if there is, it processes it (or passes it on to the event's associated function or method for processing).

Although complete, and quite useful if you use consoles, the application uses only a single widget. Also, we have not given it any ability to respond to user interaction. It also works rather like traditional batch-processing programs. It is invoked, performs some processing (waits, then shows a message), and terminates. Most GUI programs work differently. Once invoked, they run their event loop and respond to events. Some events come from the user—for example, key presses and mouse clicks—and some from the system, for example, timers timing out and windows being revealed. They process in response to requests that are the result of events such as button clicks and menu selections, and terminate only when told to do so.

The next application we will look at is much more conventional than the one we've just seen, and is typical of many very small GUI applications generally.

0 0

Post a comment