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 node_status = { "in": "Already Selected",
15 "out": "Not Selected",
17 "remove": "To be Removed"}
19 class NodeView(QTreeView):
20 def __init__(self, parent):
21 QTreeView.__init__(self, parent)
23 self.setAnimated(True)
24 self.setItemsExpandable(True)
25 self.setRootIsDecorated(True)
26 self.setAlternatingRowColors(True)
27 # self.setSelectionMode(self.MultiSelection)
28 self.setAttribute(Qt.WA_MacShowFocusRect, 0)
29 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
30 self.setToolTip("Double click on a row to change its status")
32 def mouseDoubleClickEvent(self, event):
33 index = self.currentIndex()
35 status_index = model.index(index.row(), 2, index.parent())
36 status_data = status_index.data().toString()
37 hostname_index = model.index(index.row(), 1, index.parent())
38 hostname_data = hostname_index.data().toString()
40 if status_data == node_status['in']:
41 model.setData(status_index, QString(node_status['remove']))
42 elif status_data == node_status['out']:
43 model.setData(status_index, QString(node_status['add']))
44 elif status_data in (node_status['add'], node_status['remove']):
45 if hostname_data in already_in_nodes: model.setData(status_index, QString(node_status['in']))
46 else: model.setData(status_index, QString(node_status['out']))
48 model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), hostname_index, hostname_index)
52 class NodeNameDelegate(QStyledItemDelegate):
53 def __init__(self, parent):
54 QStyledItemDelegate.__init__(self, parent)
56 def paint(self, painter, option, index):
58 data = "%s" % index.data().toString()
59 status_index = model.index(index.row(), 2, index.parent())
60 status_data = status_index.data().toString()
62 if status_data not in (node_status['in'], node_status['remove'], node_status['add']):
64 QStyledItemDelegate.paint(self, painter, option, index)
67 fm = QFontMetrics(option.font)
69 rect.setWidth(fm.width(QString(data)) + 8)
70 rect.setHeight(rect.height() - 2)
71 rect.setX(rect.x() + 4)
72 x, y, h, w = rect.x(), rect.y(), rect.height(), rect.width()
75 path.addRoundedRect(x, y, w, h, 4, 4)
78 painter.setRenderHint(QPainter.Antialiasing)
79 painter.drawRoundedRect(rect, 4, 4)
81 if status_data == node_status['in']: # already in 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))
86 elif status_data == node_status['add']: # newly added to the slice
87 painter.fillPath(path, QColor.fromRgb(0, 250, 0))
88 painter.setPen(QColor.fromRgb(0, 0, 0))
89 painter.drawText(option.rect, 0, QString(data))
90 painter.drawRect(x + w + 10, y + 3, 10, 10)
91 painter.fillRect(x + w + 10, y + 3, 10, 10, QColor.fromRgb(0, 250, 0))
93 elif status_data == node_status['remove']: # removed from the slice
94 painter.fillPath(path, QColor.fromRgb(250, 0, 0))
95 painter.setPen(QColor.fromRgb(0, 0, 0))
96 painter.drawText(option.rect, 0, QString(data))
97 painter.drawRect(x + w + 10, y + 3, 10, 10)
98 painter.fillRect(x + w + 10, y + 3, 10, 10, QColor.fromRgb(250, 0, 0))
103 def __init__(self, data, parent=None):
104 self.parentItem = parent
109 for child in self.childItems:
115 def appendChild(self, child):
116 self.childItems.append(child)
118 def child(self, row):
119 return self.childItems[row]
121 def childCount(self):
122 return len(self.childItems)
124 def childNumber(self):
126 return self.parentItem.childItems.index(self)
129 def columnCount(self):
130 return len(self.itemData)
132 def data(self, column):
133 return self.itemData[column]
135 def insertChildren(self, position, count, columns):
136 if position < 0 or position > len(self.childItems):
139 for row in range(count):
140 data = self.data(columns)
141 item = TreeItem(data, self)
142 self.childItems.insert(position, item)
146 def insertColumns(self, position, columns):
147 if position < 0 or position > len(self.itemData):
150 for column in range(columns):
151 self.itemData.insert(position, QVariant())
153 for child in self.childItems:
154 child.insertColumns(position, columns)
158 def setData(self, column, value):
159 if column < 0 or column >= len(self.itemData):
162 self.itemData[column] = value
166 return self.parentItem
170 class NodeModel(QAbstractItemModel):
171 def __init__(self, parent):
172 QAbstractItemModel.__init__(self, parent)
176 self.rootItem.clear()
179 def __initRoot(self):
180 self.rootItem = TreeItem([QString("Testbed"), QString("Hostname"), QString("Status")])
182 def getItem(self, index):
184 item = index.internalPointer()
188 def headerData(self, section, orientation, role):
189 if orientation == Qt.Horizontal and role in (Qt.DisplayRole, Qt.EditRole):
190 return self.rootItem.data(section)
193 def index(self, row, column, parent):
194 if not self.hasIndex(row, column, parent):
197 parentItem = self.getItem(parent)
198 childItem = parentItem.child(row)
200 return self.createIndex(row, column, childItem)
204 def insertColumns(self, position, columns, parent):
205 self.beginInsertColumns(parent, position, position + columns -1)
206 ret = self.rootItem.insertColumns(position, columns)
207 self.endInsertColumns()
210 def insertRows(self, position, rows, parent):
211 parentItem = self.getItem(parent)
212 self.beginInsertRows(parent, position, position + rows -1)
213 ret = parentItem.insertChildren(position, rows, self.rootItem.columnCount())
217 def parent(self, index):
218 if not index.isValid():
221 childItem = self.getItem(index)
222 parentItem = childItem.parent()
223 if parentItem is self.rootItem:
226 return self.createIndex(parentItem.childNumber(), 0, parentItem)
228 def rowCount(self, parent=QModelIndex()):
229 parentItem = self.getItem(parent)
230 return parentItem.childCount()
232 def columnCount(self, parent=None):
233 return self.rootItem.columnCount()
235 def data(self, index, role):
236 if not index.isValid():
239 if role != Qt.DisplayRole and role != Qt.EditRole:
242 item = self.getItem(index)
243 return item.data(index.column())
245 def flags(self, index):
246 if not index.isValid():
248 return Qt.ItemIsEnabled | Qt.ItemIsSelectable# | Qt.ItemIsEditable
250 def setData(self, index, value, role):
251 if role != Qt.EditRole:
254 item = self.getItem(index)
255 ret = item.setData(index.column(), value)
257 self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), index, index)
262 class SliceWidget(QWidget):
263 def __init__(self, parent):
264 QWidget.__init__(self, parent)
266 self.network_names = []
268 slicename = QLabel ("Slice : %s"%(config.getSlice() or "None"),self)
269 slicename.setScaledContents(False)
270 searchlabel = QLabel ("Search: ", self)
271 searchlabel.setScaledContents(False)
272 searchbox = QLineEdit(self)
273 searchbox.setAttribute(Qt.WA_MacShowFocusRect, 0)
275 toplayout = QHBoxLayout()
276 toplayout.addWidget(slicename, 0, Qt.AlignLeft)
277 toplayout.addStretch()
278 toplayout.addWidget(searchlabel, 0, Qt.AlignRight)
279 toplayout.addWidget(searchbox, 0, Qt.AlignRight)
281 self.nodeView = NodeView(self)
282 self.nodeModel = NodeModel(self)
283 self.filterModel = QSortFilterProxyModel(self) # enable filtering
285 self.nodeNameDelegate = NodeNameDelegate(self)
287 refresh = QPushButton("Update Slice Data", self)
288 refresh.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
289 submit = QPushButton("Submit", self)
290 submit.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
292 bottomlayout = QHBoxLayout()
293 bottomlayout.addWidget(refresh, 0, Qt.AlignLeft)
294 bottomlayout.addStretch()
295 bottomlayout.addWidget(submit, 0, Qt.AlignRight)
297 layout = QVBoxLayout()
298 layout.addLayout(toplayout)
299 layout.addWidget(self.nodeView)
300 layout.addLayout(bottomlayout)
301 self.setLayout(layout)
302 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
304 self.connect(refresh, SIGNAL('clicked()'), self.refresh)
305 self.connect(submit, SIGNAL('clicked()'), self.submit)
306 self.connect(searchbox, SIGNAL('textChanged(QString)'), self.filter)
310 def filter(self, filter_string):
311 # for hierarchical models QSortFilterProxyModel applies the
312 # sort recursively. if the parent doesn't match the criteria
313 # we won't be able to match the children. so we need to match
314 # parent (by matching the network_names)
315 networks = ["^%s$" % n for n in self.network_names]
316 filters = networks + [str(filter_string)]
317 self.filterModel.setFilterRegExp(QRegExp('|'.join(filters)))
320 self.parent().setStatus("TODO: Submit not implemented yet!", 3000)
322 def readSliceRSpec(self):
323 rspec_file = config.getSliceRSpecFile()
324 if os.path.exists(rspec_file):
325 xml = open(rspec_file).read()
330 if not config.getSlice():
331 self.parent().setStatus("<font color='red'>Slice not set yet!</font>", timeout=None)
334 self.process = SfiProcess()
335 outfile = self.process.getRSpecFromSM()
336 self.parent().setStatus("Updating slice data. This may take some time...", timeout=None)
338 self.connect(self.process, SIGNAL('finished()'), self.refreshFinished)
340 def refreshFinished(self):
342 self.parent().setStatus("<font color='green'>Slice data updated.</font>", timeout=5000)
345 def updateView(self):
346 global already_in_nodes
347 already_in_nodes = []
348 self.network_names = []
349 self.nodeModel.clear()
351 rspec_string = self.readSliceRSpec()
355 networks = rspec_get_networks(rspec_string)
356 for network in networks:
357 self.network_names.append(network)
358 networkItem = TreeItem([QString(network), QString(""), QString("")], self.nodeModel.rootItem)
360 all_nodes = rspec_get_nodes_from_network(rspec_string, network)
361 sliver_nodes = rspec_get_sliver_nodes_from_network(rspec_string, network)
362 available_nodes = filter(lambda x:x not in sliver_nodes, all_nodes)
364 already_in_nodes += sliver_nodes
366 for node in sliver_nodes:
367 nodeItem = TreeItem([QString(""), QString("%s" % node), QString(node_status['in'])], networkItem)
368 networkItem.appendChild(nodeItem)
370 for node in available_nodes:
371 nodeItem = TreeItem([QString(""), QString(node), QString(node_status['out'])], networkItem)
372 networkItem.appendChild(nodeItem)
374 self.nodeModel.rootItem.appendChild(networkItem)
376 self.filterModel.setSourceModel(self.nodeModel)
377 self.filterModel.setFilterKeyColumn(-1)
378 self.filterModel.setDynamicSortFilter(True)
380 self.nodeView.setItemDelegateForColumn(1, self.nodeNameDelegate)
381 self.nodeView.setModel(self.filterModel)
382 self.nodeView.expandAll()
383 self.nodeView.resizeColumnToContents(1)
386 class MainScreen(SfaScreen):
387 def __init__(self, parent):
388 SfaScreen.__init__(self, parent)
390 slice = SliceWidget(self)
391 self.init(slice, "Main Window", "OneLab Federation GUI")