Rapid GUI Programming with Python and Qt


ISBN-10: 0132354187  – ISBN-13: 978-0132354189

Errata for All Printings

Issue Resolution Reporter
The imagechanger.pyw application works correctly, but segfaults on termination on 32-bit Linux platforms.This is due to a 32-bit Linux-specific PyQt bug that does not affect the operation of the program. This is fixed in PyQt 4.3.1. Author
Page 3. The license information has changed. For open source applications there is no change: you can use the open source editions of PyQt and Qt under the GPL. But if you want to use PyQt to create commercial applications you must have a commercial PyQt license and a commercial Qt license—unless you use Qt 4.5 or later in which case you must have a commercial PyQt license and must either buy a commercial Qt license or use a (free of charge) LGPL edition of Qt. Author
Pages 46, 49, 50, 55, 66, and 76. Indentation in the syntax illustrations is 2 spaces instead of 4. These are just typesetting mistakes. All the code quoted in the book and all the examples and solutions use 4 spaces as the Python guidelines recommend. Author
Pages 92-99. The OrderedDict class's name is wrong. In Python terminology an "ordered dict" is an insertion order dictionary, whereas a "sorted dict" is a key ordered dictionary. Since OrderedDict is key ordered it should be called SortedDict to be terminologically correct. (I used C++ terminology by mistake.) The class works fine though. (Another implementation, SortedDict, is provided with the examples for Programming in Python 3.) Author
Page 95. Subtle bug. The implementation of the __delitem__() method has a subtle bug. Replace the method as follows:

def __delitem__(self, key):
    del self.__dict[key]
    i = bisect.bisect_left(self.__keys, key)
    del self.__keys[i]
The code has been corrected in the archives.
Rick Morra
Page 148. Bug in numbers.pyw example. The numbers.pyw example has a bug that means it displays numbers incorrectly. The erroneous code is not quoted in the book, but its effect is visible in Figure 5.3. The tarball and zip now have a fixed version of numbers.pyw. Peter Hageman
Page 181. Code error. In the code snippet at the top of this page change editToolBar to self.imageLabel. Karl Gohl
Pages 288/9. The updateWindowMenu() and raiseWindow() methods are easy to understand but naive. The file sditexteditor2.pyw has more sophisticated versions of these methods as explained in README.txt.Author
Page 301. Improved solution to exercise. I improved the logic for the tabbededitor.pyw solution in the Python 2.6 and 3 archives. I also fixed a bug in naming the tabs when new files are created or when file save as is used. Nankun Huang
Page 311. Two blank lines instead of one separate a paragraph from a code snippet on this page. There's nothing missing, so just ignore the blank space. (I made a mistake when I typeset this page.) Author
Pages 374 and 377 From Qt 4.6, QStyleOptionGraphicsItem.levelOfDetail() has been deprecated in favour of QStyleOptionGraphicsItem.levelOfDetailFromTransform(). In view of this it is recommended to replace code like this: option.levelOfDetail > 0.5 with code like this: option.levelOfDetailFromTransform(painter.transform()) > 0.5 (although I haven't tested this myself). Andrew Goh L K
Page 386. The PythonHighlighter syntax highlighting class is easy to understand but naive. The file pythoneditor2.pyw has a more sophisticated PythonHighlighter class that covers more corner-cases at the cost of being a bit more complex and a bit slower. Author
Page 443. Typo. In the Exercise, change ShipTableDelegate.setDataModel() to ShipDelegate.setDataModel(). Eric E. Thomson
Pages 540 & 541. Rare and subtle bug in the threaded server example (chap19/building- servicesserver.py—this doesn't apply to the non-threaded Chapter 18 versions of this server) The code presented on page 540 in the run() method has a failure mode: If a client makes a connection but sends incomplete data (or no data), e.g., due to a poor connection (e.g., wireless), or due to a bug in the client, the thread will wait forever. This doesn't block the rest of the program or stop other threads being created and used, but if errors of this kind kept happening, the server would consume more and more memory. Here is the code that the run() method ought to start with (and that is now in all the archives):

    def run(self):
        socket = QTcpSocket()
        if not socket.setSocketDescriptor(self.socketId):
            self.emit(SIGNAL("error(int)"), socket.error())
        while socket.state() == QAbstractSocket.ConnectedState:
            nextBlockSize = 0
            stream = QDataStream(socket)
            if (socket.waitForReadyRead() and
                socket.bytesAvailable() >= SIZEOF_UINT16):
                nextBlockSize = stream.readUInt16()
                self.sendError(socket, "Cannot read client request")
            if socket.bytesAvailable() < nextBlockSize:
                if (not socket.waitForReadyRead(60000) or
                    socket.bytesAvailable() < nextBlockSize):
                    self.sendError(socket, "Cannot read client data")

The first if statement inside the while loop waits for up to 30 seconds (the default for QTcpSocket.waitForReadyRead()) and if by then it has the size of the block it reads the size and continues to the second if statement. Otherwise it sends an error message to the client and returns (thus terminating the thread and allowing its memory to be reclaimed). Similarly, in the second if statement inside the loop, if we haven't received the entire block of data we are expecting we wait for up to two minutes for the data to become available, and if all the data hasn't arrived by then we send the client an error message and return, again terminating the thread.
Nicola Murino
(who both reported the bug & tested my solution)

Errata for the First Printing only (additional to the errata above—these are all fixed in the Second and subsequent printings)

Issue Resolution Reporter
Page 17. Incorrect forward reference. The forward reference to Chapter 4 should be to Chapter 3. Burkhard Lück
Page 19. Bad hypenation. Namespace is hypenated as names-pace instead of name-space. Lorenz Quack and Steve Shortess
Page 30. Typo in the penultimate paragraph. In the paragraph beginning "Now we have changed the names tuple...", change names[:1] to names[:2]. Giovanni Cavallin
Page 33. Missing item from list. All the fruit lists shown at the bottom of this page should have 'Pear' in front of 'Quince'. Mark M. Henwood
Page 51. Incorrect variable name in code snippet. In the first code snippet change print president, presidents[key] to print key, presidents[key]. Chris O'Halloran
Page 73. Poorly specified exercise. In Exercise 2 change the text “For every lowercase character in text” to “For every character in a lowercased copy of text”. Oleksandr Moskalenko
Page 82. Although the __cmp__() method works as written, it doesn't take advantage of the fact that area is a property. Change the return line to:
    return cmp(self.area, other.area)
This correction has been made to the example source code.
Ralph Wagner
Page 96. Missing period The period is missing from the end of the first sentence of the second paragraph. Kazuyoshi Furutaka
Page 117. Incorrect word. In the last line of the paragraph below the screenshot change the word "encodings" to the phrase "character sets". Erik Dalén
Page 126. Incorrect word. Change the last word in the second paragraph from “readlines()” to the phrase, “the file handle as an iterable” (Kurt Welgehausen also suggested an improvement to the code shown on page 125—this is now in the tarball and zip file.) Kurt Welgehausen
Page 162. Typo in the first sentence of the Summary. Change "showned" to "showed". Lorenz Quack
Page 163. Poor solution to exercise. The solution in the archives has been improved by removing some redundant lines and by rewriting the up() and down() methods. Alexander Kanevskiy and Oleksandr Moskalenko
Pages 333 and 460. Two blank lines instead of one separate a paragraph from a code snippet on each of these pages. There's nothing missing, so just ignore the blank space. (I made a mistake when I typeset these pages.) Author
Page 403. Typo in the first code snippet. Replace page-break-after=always; with page-break-after:always;. This is now fixed in printing.pyw's source code, and gives proper page breaks but it also outputs an extra blank page at the end. A new version, printing2.pyw has been added to the source code with this fix and improved code so that no spurious blank page is output. It also puts the footer that appears when using QPainter a bit lower. Benno Dielmann
Page 463. Workaround for Qt <= 4.3.3/SQLite bug. The QTableView showing the log records sometimes shows spurious blank rows when using SQLite; the workaround is to add the line self.logModel.reset() just before the select() call. (The workaround was provided by Sibylle Koczian and is now in the tarball and zip file.) Sibylle Koczian
Page 465. Missing : in code snippet. In the page's main code snippet there's a colon missing after if not QSqlDatabase.database().commit(). Carla Paredes
Page 615. Misplaced gzip module index entry. This entry appears at the end of the `Q's instead of the end of the `G's on page 596 (i.e., it was filed as "qzip" instead of "gzip"). Robert Withrow