http://blog.youkuaiyun.com/xiaoyangyang20
python3+PyQt5 树中表达表格数据
#!/usr/bin/env python3
import bisect
import codecs
from PyQt5.QtCore import (QAbstractItemModel, QModelIndex,QVariant, Qt)
KEY, NODE = range(2)
class BranchNode(object):
def __init__(self, name, parent=None):
super(BranchNode, self).__init__()
self.name = name
self.parent = parent
self.children = []
def __lt__(self, other):
if isinstance(other, BranchNode):
return self.orderKey() < other.orderKey()
return False
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
# Causes a -3 deprecation warning. Solution will be to
# reimplement bisect_left and provide a key function.
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__()
self.parent = parent
self.fields = fields
def orderKey(self):
return "\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):
self.beginResetModel()
assert nesting > 0
self.nesting = nesting
self.root = BranchNode("")
exception = None
fh = None
try:
for line in codecs.open(str(filename), "rU", "utf8"):
if not line:
continue
self.addRecord(line.split(separator), False)
except IOError as e:
exception = e
finally:
if fh is not None:
fh.close()
#self.reset()
self.endResetModel()
for i in range(self.columns):
self.headers.append("Column #{0}".format(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.beginResetModel()
self.endResetModel()
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 node.toString() if index.column() == 0 else ""
return 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 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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
/home/yrd/eric_workspace/chap16/serverinfo.pyw
#!/usr/bin/env python3
import os
import sys
from PyQt5.QtCore import (QModelIndex, QVariant, Qt,pyqtSignal)
from PyQt5.QtWidgets import (QApplication, QMainWindow,QMessageBox, QShortcut, QTreeView)
from PyQt5.QtGui import QKeySequence,QPixmap
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):
activated_signal=pyqtSignal(list)
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 as e:
QMessageBox.warning(self, "Server Info - Error", str(e))
self.activated[QModelIndex].connect(self.activate)
self.expanded.connect(self.expand)
self.expand()
def currentFields(self):
return self.model().asRecord(self.currentIndex())
def activate(self, index):
self.activated_signal.emit(self.model().asRecord(index))
def expand(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.treeWidget.activated_signal[list].connect(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()))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
运行结果: