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)
31 def mouseDoubleClickEvent(self, event):
32 index = self.currentIndex()
34 status_index = model.index(index.row(), 2, index.parent())
35 status_data = status_index.data().toString()
36 hostname_index = model.index(index.row(), 1, index.parent())
37 hostname_data = hostname_index.data().toString()
39 if status_data == node_status['in']:
40 model.setData(status_index, QString(node_status['remove']))
41 elif status_data == node_status['out']:
42 model.setData(status_index, QString(node_status['add']))
43 elif status_data in (node_status['add'], node_status['remove']):
44 if hostname_data in already_in_nodes: model.setData(status_index, QString(node_status['in']))
45 else: model.setData(status_index, QString(node_status['out']))
47 model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), hostname_index, hostname_index)
51 class NodeNameDelegate(QStyledItemDelegate):
52 def __init__(self, parent):
53 QStyledItemDelegate.__init__(self, parent)
55 def paint(self, painter, option, index):
57 data = "%s" % index.data().toString()
58 status_index = model.index(index.row(), 2, index.parent())
59 status_data = status_index.data().toString()
61 if status_data not in (node_status['in'], node_status['remove'], node_status['add']):
63 QStyledItemDelegate.paint(self, painter, option, index)
66 fm = QFontMetrics(option.font)
68 rect.setWidth(fm.width(QString(data)) + 8)
69 rect.setHeight(rect.height() - 2)
70 rect.setX(rect.x() + 4)
71 x, y, h, w = rect.x(), rect.y(), rect.height(), rect.width()
74 path.addRoundedRect(x, y, w, h, 4, 4)
77 painter.setRenderHint(QPainter.Antialiasing)
78 painter.drawRoundedRect(rect, 4, 4)
80 if status_data == node_status['in']: # already in the slice
81 painter.fillPath(path, QColor.fromRgb(0, 250, 0))
82 painter.setPen(QColor.fromRgb(0, 0, 0))
83 painter.drawText(option.rect, 0, QString(data))
85 elif status_data == node_status['add']: # newly added to the slice
86 painter.fillPath(path, QColor.fromRgb(0, 250, 0))
87 painter.setPen(QColor.fromRgb(0, 0, 0))
88 painter.drawText(option.rect, 0, QString(data))
89 painter.drawRect(x + w + 10, y + 3, 10, 10)
90 painter.fillRect(x + w + 10, y + 3, 10, 10, QColor.fromRgb(0, 250, 0))
92 elif status_data == node_status['remove']: # removed from the slice
93 painter.fillPath(path, QColor.fromRgb(250, 0, 0))
94 painter.setPen(QColor.fromRgb(0, 0, 0))
95 painter.drawText(option.rect, 0, QString(data))
96 painter.drawRect(x + w + 10, y + 3, 10, 10)
97 painter.fillRect(x + w + 10, y + 3, 10, 10, QColor.fromRgb(250, 0, 0))
102 def __init__(self, data, parent=None):
103 self.parentItem = parent
108 for child in self.childItems:
114 def appendChild(self, child):
115 self.childItems.append(child)
117 def child(self, row):
118 return self.childItems[row]
120 def childCount(self):
121 return len(self.childItems)
123 def childNumber(self):
125 return self.parentItem.childItems.index(self)
128 def columnCount(self):
129 return len(self.itemData)
131 def data(self, column):
132 return self.itemData[column]
134 def insertChildren(self, position, count, columns):
135 if position < 0 or position > len(self.childItems):
138 for row in range(count):
139 data = self.data(columns)
140 item = TreeItem(data, self)
141 self.childItems.insert(position, item)
145 def insertColumns(self, position, columns):
146 if position < 0 or position > len(self.itemData):
149 for column in range(columns):
150 self.itemData.insert(position, QVariant())
152 for child in self.childItems:
153 child.insertColumns(position, columns)
157 def setData(self, column, value):
158 if column < 0 or column >= len(self.itemData):
161 self.itemData[column] = value
165 return self.parentItem
169 class NodeModel(QAbstractItemModel):
170 def __init__(self, parent):
171 QAbstractItemModel.__init__(self, parent)
175 self.rootItem.clear()
178 def __initRoot(self):
179 self.rootItem = TreeItem([QString("Testbed"), QString("Hostname"), QString("Status")])
181 def getItem(self, index):
183 item = index.internalPointer()
187 def headerData(self, section, orientation, role):
188 if orientation == Qt.Horizontal and role in (Qt.DisplayRole, Qt.EditRole):
189 return self.rootItem.data(section)
192 def index(self, row, column, parent):
193 if not self.hasIndex(row, column, parent):
196 parentItem = self.getItem(parent)
197 childItem = parentItem.child(row)
199 return self.createIndex(row, column, childItem)
203 def insertColumns(self, position, columns, parent):
204 self.beginInsertColumns(parent, position, position + columns -1)
205 ret = self.rootItem.insertColumns(position, columns)
206 self.endInsertColumns()
209 def insertRows(self, position, rows, parent):
210 parentItem = self.getItem(parent)
211 self.beginInsertRows(parent, position, position + rows -1)
212 ret = parentItem.insertChildren(position, rows, self.rootItem.columnCount())
216 def parent(self, index):
217 if not index.isValid():
220 childItem = self.getItem(index)
221 parentItem = childItem.parent()
222 if parentItem is self.rootItem:
225 return self.createIndex(parentItem.childNumber(), 0, parentItem)
227 def rowCount(self, parent=QModelIndex()):
228 parentItem = self.getItem(parent)
229 return parentItem.childCount()
231 def columnCount(self, parent=None):
232 return self.rootItem.columnCount()
234 def data(self, index, role):
235 if not index.isValid():
238 if role != Qt.DisplayRole and role != Qt.EditRole:
241 item = self.getItem(index)
242 return item.data(index.column())
244 def flags(self, index):
245 if not index.isValid():
247 return Qt.ItemIsEnabled | Qt.ItemIsSelectable# | Qt.ItemIsEditable
249 def setData(self, index, value, role):
250 if role != Qt.EditRole:
253 item = self.getItem(index)
254 ret = item.setData(index.column(), value)
256 self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), index, index)
261 class SliceWidget(QWidget):
262 def __init__(self, parent):
263 QWidget.__init__(self, parent)
265 self.network_names = []
267 slicename = QLabel ("Slice : %s"%(config.getSlice() or "None"),self)
268 slicename.setScaledContents(False)
269 searchlabel = QLabel ("Search: ", self)
270 searchlabel.setScaledContents(False)
271 searchbox = QLineEdit(self)
272 searchbox.setAttribute(Qt.WA_MacShowFocusRect, 0)
274 toplayout = QHBoxLayout()
275 toplayout.addWidget(slicename, 0, Qt.AlignLeft)
276 toplayout.addStretch()
277 toplayout.addWidget(searchlabel, 0, Qt.AlignRight)
278 toplayout.addWidget(searchbox, 0, Qt.AlignRight)
280 self.nodeView = NodeView(self)
281 self.nodeModel = NodeModel(self)
282 self.filterModel = QSortFilterProxyModel(self) # enable filtering
284 self.nodeNameDelegate = NodeNameDelegate(self)
286 refresh = QPushButton("Update Slice Data", self)
287 refresh.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
288 submit = QPushButton("Submit", self)
289 submit.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
291 bottomlayout = QHBoxLayout()
292 bottomlayout.addWidget(refresh, 0, Qt.AlignLeft)
293 bottomlayout.addStretch()
294 bottomlayout.addWidget(submit, 0, Qt.AlignRight)
296 layout = QVBoxLayout()
297 layout.addLayout(toplayout)
298 layout.addWidget(self.nodeView)
299 layout.addLayout(bottomlayout)
300 self.setLayout(layout)
301 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
303 self.connect(refresh, SIGNAL('clicked()'), self.refresh)
304 self.connect(submit, SIGNAL('clicked()'), self.submit)
305 self.connect(searchbox, SIGNAL('textChanged(QString)'), self.filter)
309 def filter(self, filter_string):
310 # for hierarchical models QSortFilterProxyModel applies the
311 # sort recursively. if the parent doesn't match the criteria
312 # we won't be able to match the children. so we need to match
313 # parent (by matching the network_names)
314 networks = ["^%s$" % n for n in self.network_names]
315 filters = networks + [str(filter_string)]
316 self.filterModel.setFilterRegExp(QRegExp('|'.join(filters)))
319 self.parent().setStatus("TODO: Submit not implemented yet!", 3000)
321 def readSliceRSpec(self):
322 rspec_file = config.getSliceRSpecFile()
323 if os.path.exists(rspec_file):
324 xml = open(rspec_file).read()
329 if not config.getSlice():
330 self.parent().setStatus("<font color='red'>Slice not set yet!</font>", timeout=None)
333 self.process = SfiProcess()
334 outfile = self.process.getRSpecFromSM()
335 self.parent().setStatus("Updating slice data. This may take some time...", timeout=None)
337 self.connect(self.process, SIGNAL('finished()'), self.refreshFinished)
339 def refreshFinished(self):
341 self.parent().setStatus("<font color='green'>Slice data updated.</font>", timeout=5000)
344 def updateView(self):
345 global already_in_nodes
346 already_in_nodes = []
347 self.network_names = []
348 self.nodeModel.clear()
350 rspec_string = self.readSliceRSpec()
354 networks = rspec_get_networks(rspec_string)
355 for network in networks:
356 self.network_names.append(network)
357 networkItem = TreeItem([QString(network), QString(""), QString("")], self.nodeModel.rootItem)
359 all_nodes = rspec_get_nodes_from_network(rspec_string, network)
360 sliver_nodes = rspec_get_sliver_nodes_from_network(rspec_string, network)
361 available_nodes = filter(lambda x:x not in sliver_nodes, all_nodes)
363 already_in_nodes += sliver_nodes
365 for node in sliver_nodes:
366 nodeItem = TreeItem([QString(""), QString("%s" % node), QString(node_status['in'])], networkItem)
367 networkItem.appendChild(nodeItem)
369 for node in available_nodes:
370 nodeItem = TreeItem([QString(""), QString(node), QString(node_status['out'])], networkItem)
371 networkItem.appendChild(nodeItem)
373 self.nodeModel.rootItem.appendChild(networkItem)
375 self.filterModel.setSourceModel(self.nodeModel)
376 self.filterModel.setFilterKeyColumn(-1)
377 self.filterModel.setDynamicSortFilter(True)
379 self.nodeView.setItemDelegateForColumn(1, self.nodeNameDelegate)
380 self.nodeView.setModel(self.filterModel)
381 self.nodeView.expandAll()
382 self.nodeView.resizeColumnToContents(1)
385 class MainScreen(SfaScreen):
386 def __init__(self, parent):
387 SfaScreen.__init__(self, parent)
389 slice = SliceWidget(self)
390 self.init(slice, "Main Window", "OneLab Federation GUI")