Hello!First of all,I'm so sorry for this huge post!!I wrote the whole code from the book,that's why the post is so big!!I'm reading this book : Rapid Gui Programming with Python and Qt.And I have problem understanding some parts of the code.I would be really thankful if someone could help me understand.In chapter 16 the writer creates a QTreeView.
-According to the book: "Since we have 6 fields (each field is separated by an asterisk) this means that the first 4 fields will be shown in the tree part of the tree widget with the remaining 2 fields shown as separate columns in the rows that have leaves.The resultant tree view will have 3 columns,one containing the tree and 2 more showing the extra fields." What I don't understand is where in the code is shown that the 4 fields will be shown in the tree part and the 2 fields will be the 2 extra columns that will have the information about the leaves??Where does he fill the 2 columns?I don't get how he comes to this conclusion.Also,if in the text file,each line had different number of fields(and not 6 fields in each line),what would happen then??How could we create the tree view of this example then??
-One last thing that I don't understand is this: In the serverinfo.py ,in the end of the script there is this line of code: form=MainForm(os.path.join(os.path.dirname(__file__), "servers.txt"),nesting, ""),this comes from self.treeWidget=TreeOfTableWidget(filename,nesting,separator)?? And why does he use os.path.join(os.path.dirname(__file__))?


#!/usr/bin/env python    
import os
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import treeoftable

class ServerModel(treeoftable.TreeOfTableModel):
    def __init__(self, parent=None):
        super(ServerModel, self).__init__(parent)

    def data(self, index, role):
        if role == Qt.DecorationRole:
            node = self.nodeFromIndex(index)
            if node is None:
                return QVariant()
            if isinstance(node, treeoftable.BranchNode):
                if index.column() != 0:
                    return QVariant()
                filename = node.toString().replace(" ", "_")
                parent = node.parent.toString()
                if parent and parent != "USA":
                    return QVariant()
                if parent == "USA":
                    filename = "USA_" + filename
                filename = os.path.join(os.path.dirname(__file__),
                                        "flags", filename + ".png")
                pixmap = QPixmap(filename)
                if pixmap.isNull():
                    return QVariant()
                return QVariant(pixmap)
        return treeoftable.TreeOfTableModel.data(self, index, role)

class TreeOfTableWidget(QTreeView):
    def __init__(self, filename, nesting, separator, parent=None):
        super(TreeOfTableWidget, self).__init__(parent)
        model = ServerModel(self)
            model.load(filename, nesting, separator)
        except IOError, e:
            QMessageBox.warning(self, "Server Info - Error",
        self.connect(self, SIGNAL("activated(QModelIndex)"),
        self.connect(self, SIGNAL("expanded(QModelIndex)"),

    def currentFields(self):
        return self.model().asRecord(self.currentIndex())

    def activated(self, index):
        self.emit(SIGNAL("activated"), self.model().asRecord(index))

    def expanded(self):
        for column in range(self.model().columnCount(QModelIndex())):

class MainForm(QMainWindow):

    def __init__(self, filename, nesting, separator, parent=None):
        super(MainForm, self).__init__(parent)
        headers = ["Country/State (US)/City/Provider", "Server", "IP"]
        if nesting != 3:
            if nesting == 1:
                headers = ["Country/State (US)", "City", "Provider",
            elif nesting == 2:
                headers = ["Country/State (US)/City", "Provider",
            elif nesting == 4:
                headers = ["Country/State (US)/City/Provider/Server"]

        self.treeWidget = TreeOfTableWidget(filename, nesting,
        self.treeWidget.model().headers = headers

        QShortcut(QKeySequence("Escape"), self, self.close)
        QShortcut(QKeySequence("Ctrl+Q"), self, self.close)

        self.connect(self.treeWidget, SIGNAL("activated"),

        self.setWindowTitle("Server Info")
        self.statusBar().showMessage("Ready...", 5000)

    def picked(self):
        return self.treeWidget.currentFields()

    def activated(self, fields):
        self.statusBar().showMessage("*".join(fields), 60000)

app = QApplication(sys.argv)
nesting = 3
if len(sys.argv) > 1:
        nesting = int(sys.argv[1])
    if nesting not in (1, 2, 3, 4):
        nesting = 3

form = MainForm(os.path.join(os.path.dirname(__file__), "servers.txt"),
                nesting, "*")
form.resize(750, 550)
print "*".join(form.picked())


#!/usr/bin/env python

import bisect
import codecs
from PyQt4.QtCore import *
from PyQt4.QtGui import *

KEY, NODE = range(2)

class BranchNode(object):
    def __init__(self, name, parent=None):
        super(BranchNode, self).__init__(parent)
        self.name = name
        self.parent = parent
        self.children = []

    def orderKey(self):
        return self.name.lower()

    def toString(self):
        return self.name

    def __len__(self):
        return len(self.children)

    def childAtRow(self, row):
        assert 0 <= row < len(self.children)
        return self.children[row][NODE]

    def rowOfChild(self, child):
        for i, item in enumerate(self.children):
            if item[NODE] == child:
                return i
        return -1

    def childWithKey(self, key):
        if not self.children:
            return None
        i = bisect.bisect_left(self.children, (key, None))
        if i < 0 or i >= len(self.children):
            return None
        if self.children[i][KEY] == key:
            return self.children[i][NODE]
        return None

    def insertChild(self, child):
        child.parent = self
        bisect.insort(self.children, (child.orderKey(), child))

    def hasLeaves(self):
        if not self.children:
            return False
        return isinstance(self.children[0], LeafNode)

class LeafNode(object):
    def __init__(self, fields, parent=None):
        super(LeafNode, self).__init__(parent)
        self.parent = parent
        self.fields = fields

    def orderKey(self):
        return u"\t".join(self.fields).lower()

    def toString(self, separator="\t"):
        return separator.join(self.fields)

    def __len__(self):
        return len(self.fields)

    def asRecord(self):
        record = []
        branch = self.parent
        while branch is not None:
            record.insert(0, branch.toString())
            branch = branch.parent
        assert record and not record[0]
        record = record[1:]
        return record + self.fields

    def field(self, column):
        assert 0 <= column <= len(self.fields)
        return self.fields[column]

class TreeOfTableModel(QAbstractItemModel):
    def __init__(self, parent=None):
        super(TreeOfTableModel, self).__init__(parent)
        self.columns = 0
        self.root = BranchNode("")
        self.headers = []

    def load(self, filename, nesting, separator): 
        assert nesting > 0
        self.nesting = nesting
        self.root = BranchNode("")
        exception = None
        fh = None
            for line in codecs.open(unicode(filename), "rU", "utf8"):
                if not line:
                self.addRecord(line.split(separator), False)
        except IOError, e:
            exception = e
            if fh is not None:
            for i in range(self.columns):
                self.headers.append("Column #%d" % i)
            if exception is not None:
                raise exception

    def addRecord(self, fields, callReset=True):
        assert len(fields) > self.nesting
        root = self.root
        branch = None
        for i in range(self.nesting):
            key = fields[i].lower()
            branch = root.childWithKey(key)
            if branch is not None:
                root = branch
                branch = BranchNode(fields[i])
                root = branch
        assert branch is not None
        items = fields[self.nesting:]
        self.columns = max(self.columns, len(items))
        branch.insertChild(LeafNode(items, branch))
        if callReset:

    def asRecord(self, index):
        leaf = self.nodeFromIndex(index)
        if leaf is not None and isinstance(leaf, LeafNode):
            return leaf.asRecord()
        return []

    def rowCount(self, parent):
        node = self.nodeFromIndex(parent)
        if node is None or isinstance(node, LeafNode):
            return 0
        return len(node)

    def columnCount(self, parent):
        return self.columns

    def data(self, index, role):
        if role == Qt.TextAlignmentRole:
            return QVariant(int(Qt.AlignTop|Qt.AlignLeft))
        if role != Qt.DisplayRole:
            return QVariant()
        node = self.nodeFromIndex(index)
        assert node is not None
        if isinstance(node, BranchNode):
            return QVariant(node.toString()) \
                if index.column() == 0 else QVariant(QString(""))
        return QVariant(node.field(index.column()))

    def headerData(self, section, orientation, role):
        if orientation == Qt.Horizontal and \
           role == Qt.DisplayRole:
            assert 0 <= section <= len(self.headers)
            return QVariant(self.headers[section])
        return QVariant()

    def index(self, row, column, parent):
        assert self.root
        branch = self.nodeFromIndex(parent)
        assert branch is not None
        return self.createIndex(row, column,

    def parent(self, child):
        node = self.nodeFromIndex(child)
        if node is None:
            return QModelIndex()
        parent = node.parent
        if parent is None:
            return QModelIndex()
        grandparent = parent.parent
        if grandparent is None:
            return QModelIndex()
        row = grandparent.rowOfChild(parent)
        assert row != -1
        return self.createIndex(row, 0, parent)

    def nodeFromIndex(self, index):
        return index.internalPointer() \
            if index.isValid() else self.root

A part of the servers.txt file is like this:

Australia(No State)AdelaideDove Traceroute GatewayApache/1.3.6203.15.24.1
(No State)KenwickEscape NetApache/1.1.1203.25.185.2
Australia(No State)(Unknown City)Telstra NetApache/1.3.9210.8.232.2
(No State)ViennaAT NetRoxen·Challenger/1.3.11162.116.33.47
Austria(No State)(Unknown City)ISP AustriaApache/1.2.4195.58.162.92
(No State)(Unknown City)Belgian Research NetworkApache/1.3.3193.190.198.13
Belgium(No State)(Unknown City)IntersightMicrosoft-IIS/3.0193.121.16.55
(No State)SofiaLirex Looking GlassApache/1.3.3194.12.224.34
Canada(No State)Sherwood ParkDavid's Net-PresenceApache/1.3.9204.209.9.33
(No State)Prince GeorgeMag-Net InternetApache/1.3.9207.102.83.2
Czech Republic(No State)Frydek-MistekTraceroute AppleT.czApache/1.3.9212.71.150.6
(No State)TranbjergTeledenmark Erhverv Data DivisionenApache/1.2b8193.162.146.38
Denmark(No State)LyngbyNiels Bohr InstituteCERN/3.0130.225.212.55
(No State)TallinnMicroLink OnlineApache/1.1.1194.106.96.6

Edited by Odyssey2001

2 Years
Discussion Span
Last Post by Odyssey2001
Featured Replies
  • Thank you -both of you- for your time and for your answers! @HiHe : It's actually a very good book.The writer explains most of the code in the book.It's entirely my fault.It's just that I'm a beginner in programming and I can't understand some parts of the code if they … Read More


The MainForm __init__ arguments are
(self, filename, nesting, separator, parent=None)
and should give you a hint

The author of the book does not do any necessary commenting and should be discounted. I would return the lousy book.


file is the name of de module file that is actually in use.
os.path.dirname gets the directory path where the file is stored.
os.path.join creates a new path concatenating first argument to second argument.


Thank you -both of you- for your time and for your answers!
@HiHe : It's actually a very good book.The writer explains most of the code in the book.It's entirely my fault.It's just that I'm a beginner in programming and I can't understand some parts of the code if they are not explained in detail (or at all).

This question has already been answered. Start a new discussion instead.
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.