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__))
?
serverinfo.pyw
#!/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)
self.setSelectionBehavior(QTreeView.SelectItems)
self.setUniformRowHeights(True)
model = ServerModel(self)
self.setModel(model)
try:
model.load(filename, nesting, separator)
except IOError, e:
QMessageBox.warning(self, "Server Info - Error",
unicode(e))
self.connect(self, SIGNAL("activated(QModelIndex)"),
self.activated)
self.connect(self, SIGNAL("expanded(QModelIndex)"),
self.expanded)
self.expanded()
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())):
self.resizeColumnToContents(column)
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",
"Server"]
elif nesting == 2:
headers = ["Country/State (US)/City", "Provider",
"Server"]
elif nesting == 4:
headers = ["Country/State (US)/City/Provider/Server"]
headers.append("IP")
self.treeWidget = TreeOfTableWidget(filename, nesting,
separator)
self.treeWidget.model().headers = headers
self.setCentralWidget(self.treeWidget)
QShortcut(QKeySequence("Escape"), self, self.close)
QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
self.connect(self.treeWidget, SIGNAL("activated"),
self.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:
try:
nesting = int(sys.argv[1])
except:
pass
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)
form.show()
app.exec_()
print "*".join(form.picked())
treeoftable.py
#!/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
try:
for line in codecs.open(unicode(filename), "rU", "utf8"):
if not line:
continue
self.addRecord(line.split(separator), False)
except IOError, e:
exception = e
finally:
if fh is not None:
fh.close()
self.reset()
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
else:
branch = BranchNode(fields[i])
root.insertChild(branch)
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:
self.reset()
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,
branch.childAtRow(row))
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
Australia(No State)KenwickEscape NetApache/1.1.1203.25.185.2
Australia(No State)(Unknown City)Telstra NetApache/1.3.9210.8.232.2
Austria(No State)ViennaAT NetRoxen·Challenger/1.3.11162.116.33.47
Austria(No State)(Unknown City)ISP AustriaApache/1.2.4195.58.162.92
Belgium(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
Bulgaria(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
Canada(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
Denmark(No State)TranbjergTeledenmark Erhverv Data DivisionenApache/1.2b8193.162.146.38
Denmark(No State)LyngbyNiels Bohr InstituteCERN/3.0130.225.212.55
Estonia(No State)TallinnMicroLink OnlineApache/1.1.1194.106.96.6
etc.