3 from PyQt4.QtCore import *
4 from PyQt4.QtGui import *
6 from sfa.util.rspecHelper import RSpec
7 from sface.sfahelper import *
8 from sface.config import config
9 from sface.sfiprocess import SfiProcess
10 from sface.screens.sfascreen import SfaScreen
14 class NodeView(QTreeView):
15 def __init__(self, parent):
16 QTreeView.__init__(self, parent)
18 self.setAnimated(True)
19 self.setItemsExpandable(True)
20 self.setRootIsDecorated(True)
21 self.setAlternatingRowColors(True)
22 # self.setSelectionMode(self.MultiSelection)
23 self.setAttribute(Qt.WA_MacShowFocusRect, 0)
24 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
26 def mouseDoubleClickEvent(self, event):
27 index = self.currentIndex()
29 select_index = model.index(index.row(), 2, index.parent())
30 select_data = select_index.data().toString()
31 hostname_index = model.index(index.row(), 1, index.parent())
32 hostname_data = hostname_index.data().toString()
34 if select_data == "true":
35 model.setData(select_index, QString("remove"))
36 elif select_data == "false":
37 model.setData(select_index, QString("add"))
38 elif select_data in ("add", "remove"):
39 if hostname_data in already_in_nodes: model.setData(select_index, QString("true"))
40 else: model.setData(select_index, QString("false"))
42 model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), hostname_index, hostname_index)
46 class NodeNameDelegate(QStyledItemDelegate):
47 def __init__(self, parent):
48 QStyledItemDelegate.__init__(self, parent)
50 def paint(self, painter, option, index):
52 data = "%s" % index.data().toString()
53 select_index = model.index(index.row(), 2, index.parent())
54 select_data = select_index.data().toString()
55 # if select_data == "false":
56 # model.setData(select_index, QString("add"))
58 if select_data not in ("true", "add", "remove"): # default view
59 QStyledItemDelegate.paint(self, painter, option, index)
62 fm = QFontMetrics(option.font)
64 rect.setWidth(fm.width(QString(data)) + 8)
65 rect.setHeight(rect.height() - 2)
66 rect.setX(rect.x() + 4)
67 x, y, h, w = rect.x(), rect.y(), rect.height(), rect.width()
70 path.addRoundedRect(x, y, w, h, 4, 4)
73 painter.setRenderHint(QPainter.Antialiasing)
74 painter.drawRoundedRect(rect, 4, 4)
76 if select_data == "true": # already in the slice
77 painter.fillPath(path, QColor.fromRgb(0, 250, 0))
78 painter.setPen(QColor.fromRgb(0, 0, 0))
79 painter.drawText(option.rect, 0, QString(data))
81 elif select_data == "add": # newly added to the slice
82 painter.fillPath(path, QColor.fromRgb(0, 250, 0))
83 painter.setPen(QColor.fromRgb(0, 0, 0))
84 painter.drawText(option.rect, 0, QString(data))
85 painter.drawRect(x + w + 10, y + 3, 10, 10)
86 painter.fillRect(x + w + 10, y + 3, 10, 10, QColor.fromRgb(0, 250, 0))
88 elif select_data == "remove": # removed from the slice
89 painter.fillPath(path, QColor.fromRgb(250, 0, 0))
90 painter.setPen(QColor.fromRgb(0, 0, 0))
91 painter.drawText(option.rect, 0, QString(data))
92 painter.drawRect(x + w + 10, y + 3, 10, 10)
93 painter.fillRect(x + w + 10, y + 3, 10, 10, QColor.fromRgb(250, 0, 0))
98 def __init__(self, data, parent=None):
99 self.parentItem = parent
104 for child in self.childItems:
110 def appendChild(self, child):
111 self.childItems.append(child)
113 def child(self, row):
114 return self.childItems[row]
116 def childCount(self):
117 return len(self.childItems)
119 def childNumber(self):
121 return self.parentItem.childItems.index(self)
124 def columnCount(self):
125 return len(self.itemData)
127 def data(self, column):
128 return self.itemData[column]
130 def insertChildren(self, position, count, columns):
131 if position < 0 or position > len(self.childItems):
134 for row in range(count):
135 data = self.data(columns)
136 item = TreeItem(data, self)
137 self.childItems.insert(position, item)
141 def insertColumns(self, position, columns):
142 if position < 0 or position > len(self.itemData):
145 for column in range(columns):
146 self.itemData.insert(position, QVariant())
148 for child in self.childItems:
149 child.insertColumns(position, columns)
153 def setData(self, column, value):
154 if column < 0 or column >= len(self.itemData):
157 self.itemData[column] = value
161 return self.parentItem
165 class NodeModel(QAbstractItemModel):
166 def __init__(self, parent):
167 QAbstractItemModel.__init__(self, parent)
171 self.rootItem.clear()
174 def __initRoot(self):
175 self.rootItem = TreeItem([QString("Testbed"), QString("Hostname"), QString("Selected")])
177 def getItem(self, index):
179 item = index.internalPointer()
183 def headerData(self, section, orientation, role):
184 if orientation == Qt.Horizontal and role in (Qt.DisplayRole, Qt.EditRole):
185 return self.rootItem.data(section)
188 def index(self, row, column, parent):
189 if not self.hasIndex(row, column, parent):
192 parentItem = self.getItem(parent)
193 childItem = parentItem.child(row)
195 return self.createIndex(row, column, childItem)
199 def insertColumns(self, position, columns, parent):
200 self.beginInsertColumns(parent, position, position + columns -1)
201 ret = self.rootItem.insertColumns(position, columns)
202 self.endInsertColumns()
205 def insertRows(self, position, rows, parent):
206 parentItem = self.getItem(parent)
207 self.beginInsertRows(parent, position, position + rows -1)
208 ret = parentItem.insertChildren(position, rows, self.rootItem.columnCount())
212 def parent(self, index):
213 if not index.isValid():
216 childItem = self.getItem(index)
217 parentItem = childItem.parent()
218 if parentItem is self.rootItem:
221 return self.createIndex(parentItem.childNumber(), 0, parentItem)
223 def rowCount(self, parent=QModelIndex()):
224 parentItem = self.getItem(parent)
225 return parentItem.childCount()
227 def columnCount(self, parent=None):
228 return self.rootItem.columnCount()
230 def data(self, index, role):
231 if not index.isValid():
234 if role != Qt.DisplayRole and role != Qt.EditRole:
237 item = self.getItem(index)
238 return item.data(index.column())
240 def flags(self, index):
241 if not index.isValid():
243 return Qt.ItemIsEnabled | Qt.ItemIsSelectable# | Qt.ItemIsEditable
245 def setData(self, index, value, role):
246 if role != Qt.EditRole:
249 item = self.getItem(index)
250 ret = item.setData(index.column(), value)
252 self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), index, index)
257 class SliceWidget(QWidget):
258 def __init__(self, parent):
259 QWidget.__init__(self, parent)
261 self.network_names = []
263 slicename = QLabel ("Slice : %s"%(config.getSlice() or "None"),self)
264 slicename.setScaledContents(False)
265 searchlabel = QLabel ("Search: ", self)
266 searchlabel.setScaledContents(False)
267 searchbox = QLineEdit(self)
268 searchbox.setAttribute(Qt.WA_MacShowFocusRect, 0)
270 toplayout = QHBoxLayout()
271 toplayout.addWidget(slicename, 0, Qt.AlignLeft)
272 toplayout.addStretch()
273 toplayout.addWidget(searchlabel, 0, Qt.AlignRight)
274 toplayout.addWidget(searchbox, 0, Qt.AlignRight)
276 self.nodeView = NodeView(self)
277 self.nodeModel = NodeModel(self)
278 self.filterModel = QSortFilterProxyModel(self) # enable filtering
280 self.nodeNameDelegate = NodeNameDelegate(self)
282 refresh = QPushButton("Update Slice Data", self)
283 refresh.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
284 submit = QPushButton("Submit", self)
285 submit.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
287 bottomlayout = QHBoxLayout()
288 bottomlayout.addWidget(refresh, 0, Qt.AlignLeft)
289 bottomlayout.addStretch()
290 bottomlayout.addWidget(submit, 0, Qt.AlignRight)
292 layout = QVBoxLayout()
293 layout.addLayout(toplayout)
294 layout.addWidget(self.nodeView)
295 layout.addLayout(bottomlayout)
296 self.setLayout(layout)
297 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
299 self.connect(refresh, SIGNAL('clicked()'), self.refresh)
300 self.connect(submit, SIGNAL('clicked()'), self.submit)
301 self.connect(searchbox, SIGNAL('textChanged(QString)'), self.filter)
305 def filter(self, filter_string):
306 # for hierarchical models QSortFilterProxyModel applies the
307 # sort recursively. if the parent doesn't match the criteria
308 # we won't be able to match the children. so we need to match
309 # parent (by matching the network_names)
310 networks = ["^%s$" % n for n in self.network_names]
311 filters = networks + [str(filter_string)]
312 self.filterModel.setFilterRegExp(QRegExp('|'.join(filters)))
315 self.parent().setStatus("TODO: Submit not implemented yet!", 3000)
317 def readSliceRSpec(self):
318 rspec_file = config.getSliceRSpecFile()
319 if os.path.exists(rspec_file):
320 xml = open(rspec_file).read()
325 if not config.getSlice():
326 self.parent().setStatus("<font color='red'>Slice not set yet!</font>", timeout=None)
329 self.process = SfiProcess()
330 outfile = self.process.getRSpecFromSM()
331 self.parent().setStatus("Updating slice data. This may take some time...", timeout=None)
333 self.connect(self.process, SIGNAL('finished()'), self.refreshFinished)
335 def refreshFinished(self):
337 self.parent().setStatus("<font color='green'>Slice data updated.</font>", timeout=5000)
340 def updateView(self):
341 global already_in_nodes
342 already_in_nodes = []
343 self.network_names = []
344 self.nodeModel.clear()
346 rspec_string = self.readSliceRSpec()
350 networks = rspec_get_networks(rspec_string)
351 for network in networks:
352 self.network_names.append(network)
353 networkItem = TreeItem([QString(network), QString(""), QString("")], self.nodeModel.rootItem)
355 all_nodes = rspec_get_nodes_from_network(rspec_string, network)
356 sliver_nodes = rspec_get_sliver_nodes_from_network(rspec_string, network)
357 available_nodes = filter(lambda x:x not in sliver_nodes, all_nodes)
359 already_in_nodes += sliver_nodes
361 for node in sliver_nodes:
362 nodeItem = TreeItem([QString(""), QString("%s" % node), QString("true")], networkItem)
363 networkItem.appendChild(nodeItem)
365 for node in available_nodes:
366 nodeItem = TreeItem([QString(""), QString(node), QString("false")], networkItem)
367 networkItem.appendChild(nodeItem)
369 self.nodeModel.rootItem.appendChild(networkItem)
371 self.filterModel.setSourceModel(self.nodeModel)
372 self.filterModel.setFilterKeyColumn(-1)
373 self.filterModel.setDynamicSortFilter(True)
375 self.nodeView.setItemDelegateForColumn(1, self.nodeNameDelegate)
376 self.nodeView.setModel(self.filterModel)
377 self.nodeView.expandAll()
378 self.nodeView.resizeColumnToContents(1)
381 class MainScreen(SfaScreen):
382 def __init__(self, parent):
383 SfaScreen.__init__(self, parent)
385 slice = SliceWidget(self)
386 self.init(slice, "Main Window", "OneLab Federation GUI")