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
12 class NodeView(QTreeView):
13 def __init__(self, parent):
14 QTreeView.__init__(self, parent)
16 self.setAnimated(True)
17 self.setItemsExpandable(True)
18 self.setRootIsDecorated(True)
19 self.setAlternatingRowColors(True)
20 # self.setSelectionMode(self.MultiSelection)
21 self.setAttribute(Qt.WA_MacShowFocusRect, 0)
22 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
24 class SelectDelegate(QStyledItemDelegate):
27 class NodeNameDelegate(QStyledItemDelegate):
28 def __init__(self, parent):
29 QStyledItemDelegate.__init__(self)
31 def paint(self, painter, option, index):
33 data = "%s" % index.data().toString()
34 select_index = model.index(index.row(), 2, index.parent())
35 select_data = select_index.data().toString()
37 if select_data not in ("true", "add", "remove"): # default view
38 QStyledItemDelegate.paint(self, painter, option, index)
41 fm = QFontMetrics(option.font)
43 rect.setWidth(fm.width(QString(data)) + 8)
44 rect.setHeight(rect.height() - 2)
45 rect.setX(rect.x() + 4)
46 x, y, h, w = rect.x(), rect.y(), rect.height(), rect.width()
49 path.addRoundedRect(x, y, w, h, 4, 4)
52 painter.setRenderHint(QPainter.Antialiasing)
53 painter.drawRoundedRect(rect, 4, 4)
55 if select_data == "true": # already in the slice
56 painter.fillPath(path, QColor.fromRgb(0, 250, 0))
57 painter.setPen(QColor.fromRgb(0, 0, 0))
58 painter.drawText(option.rect, 0, QString(data))
60 elif select_data == "add": # newly added to the slice
61 painter.fillPath(path, QColor.fromRgb(0, 250, 0))
62 painter.setPen(QColor.fromRgb(0, 0, 0))
63 painter.drawText(option.rect, 0, QString(data))
64 painter.drawRect(x + w + 10, y + 3, 10, 10)
65 painter.fillRect(x + w + 10, y + 3, 10, 10, QColor.fromRgb(0, 250, 0))
67 elif select_data == "remove": # removed from the slice
68 painter.fillPath(path, QColor.fromRgb(250, 0, 0))
69 painter.setPen(QColor.fromRgb(0, 0, 0))
70 painter.drawText(option.rect, 0, QString(data))
71 painter.drawRect(x + w + 10, y + 3, 10, 10)
72 painter.fillRect(x + w + 10, y + 3, 10, 10, QColor.fromRgb(250, 0, 0))
77 def __init__(self, data, parent=None):
78 self.parentItem = parent
83 for child in self.childItems:
89 def appendChild(self, child):
90 self.childItems.append(child)
93 return self.childItems[row]
96 return len(self.childItems)
98 def childNumber(self):
100 return self.parentItem.childItems.index(self)
103 def columnCount(self):
104 return len(self.itemData)
106 def data(self, column):
107 return self.itemData[column]
109 def insertChildren(self, position, count, columns):
110 if position < 0 or position > len(self.childItems):
113 for row in range(count):
114 data = self.data(columns)
115 item = TreeItem(data, self)
116 self.childItems.insert(position, item)
120 def insertColumns(self, position, columns):
121 if position < 0 or position > len(self.itemData):
124 for column in range(columns):
125 self.itemData.insert(position, QVariant())
127 for child in self.childItems:
128 child.insertColumns(position, columns)
132 def setData(self, column, value):
133 if column < 0 or column >= len(self.itemData):
136 self.itemData[column] = value
140 return self.parentItem
144 class NodeModel(QAbstractItemModel):
145 def __init__(self, parent):
146 QAbstractItemModel.__init__(self, parent)
150 self.rootItem.clear()
153 def __initRoot(self):
154 self.rootItem = TreeItem([QString("Testbed"), QString("Hostname"), QString("Selected")])
156 def getItem(self, index):
158 item = index.internalPointer()
162 def headerData(self, section, orientation, role):
163 if orientation == Qt.Horizontal and role in (Qt.DisplayRole, Qt.EditRole):
164 return self.rootItem.data(section)
167 def index(self, row, column, parent):
168 if not self.hasIndex(row, column, parent):
171 parentItem = self.getItem(parent)
172 childItem = parentItem.child(row)
174 return self.createIndex(row, column, childItem)
178 def insertColumns(self, position, columns, parent):
179 self.beginInsertColumns(parent, position, position + columns -1)
180 ret = self.rootItem.insertColumns(position, columns)
181 self.endInsertColumns()
184 def insertRows(self, position, rows, parent):
185 parentItem = self.getItem(parent)
186 self.beginInsertRows(parent, position, position + rows -1)
187 ret = parentItem.insertChildren(position, rows, self.rootItem.columnCount())
191 def parent(self, index):
192 if not index.isValid():
195 childItem = self.getItem(index)
196 parentItem = childItem.parent()
197 if parentItem is self.rootItem:
200 return self.createIndex(parentItem.childNumber(), 0, parentItem)
202 def rowCount(self, parent=QModelIndex()):
203 parentItem = self.getItem(parent)
204 return parentItem.childCount()
206 def columnCount(self, parent=None):
207 return self.rootItem.columnCount()
209 def data(self, index, role):
210 if not index.isValid():
213 if role != Qt.DisplayRole and role != Qt.EditRole:
216 item = self.getItem(index)
217 return item.data(index.column())
219 def flags(self, index):
220 if not index.isValid():
222 return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
224 def setData(self, index, value, role):
225 if role != Qt.EditRole:
228 item = self.getItem(index)
229 ret = item.setData(index.column(), value)
231 self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), index, index)
236 class SliceWidget(QWidget):
237 def __init__(self, parent):
238 QWidget.__init__(self, parent)
240 self.network_names = []
242 slicename = QLabel ("Slice : %s"%(config.getSlice() or "None"),self)
243 slicename.setScaledContents(False)
244 searchlabel = QLabel ("Search: ", self)
245 searchlabel.setScaledContents(False)
246 searchbox = QLineEdit(self)
247 searchbox.setAttribute(Qt.WA_MacShowFocusRect, 0)
249 toplayout = QHBoxLayout()
250 toplayout.addWidget(slicename, 0, Qt.AlignLeft)
251 toplayout.addStretch()
252 toplayout.addWidget(searchlabel, 0, Qt.AlignRight)
253 toplayout.addWidget(searchbox, 0, Qt.AlignRight)
255 self.nodeView = NodeView(self)
256 self.nodeModel = NodeModel(self)
257 self.filterModel = QSortFilterProxyModel(self) # enable filtering
259 self.nodeNameDelegate = NodeNameDelegate(self)
260 self.selectDelegate = SelectDelegate(self)
262 refresh = QPushButton("Update Slice Data", self)
263 refresh.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
264 submit = QPushButton("Submit", self)
265 submit.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
267 bottomlayout = QHBoxLayout()
268 bottomlayout.addWidget(refresh, 0, Qt.AlignLeft)
269 bottomlayout.addStretch()
270 bottomlayout.addWidget(submit, 0, Qt.AlignRight)
272 layout = QVBoxLayout()
273 layout.addLayout(toplayout)
274 layout.addWidget(self.nodeView)
275 layout.addLayout(bottomlayout)
276 self.setLayout(layout)
277 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
279 self.connect(refresh, SIGNAL('clicked()'), self.refresh)
280 self.connect(submit, SIGNAL('clicked()'), self.submit)
281 self.connect(searchbox, SIGNAL('textChanged(QString)'), self.filter)
285 def filter(self, filter_string):
286 # for hierarchical models QSortFilterProxyModel applies the
287 # sort recursively. if the parent doesn't match the criteria
288 # we won't be able to match the children. so we need to match
289 # parent (by matching the network_names)
290 networks = ["^%s$" % n for n in self.network_names]
291 filters = networks + [str(filter_string)]
292 self.filterModel.setFilterRegExp(QRegExp('|'.join(filters)))
295 self.parent().setStatus("TODO: Submit not implemented yet!", 3000)
297 def readSliceRSpec(self):
298 rspec_file = config.getSliceRSpecFile()
299 if os.path.exists(rspec_file):
300 xml = open(rspec_file).read()
305 if not config.getSlice():
306 self.parent().setStatus("<font color='red'>Slice not set yet!</font>", timeout=None)
309 self.process = SfiProcess()
310 outfile = self.process.getRSpecFromSM()
311 self.parent().setStatus("Updating slice data. This may take some time...", timeout=None)
313 self.connect(self.process, SIGNAL('finished()'), self.refreshFinished)
315 def refreshFinished(self):
317 self.parent().setStatus("<font color='green'>Slice data updated.</font>", timeout=5000)
320 def updateView(self):
321 self.network_names = []
322 self.nodeModel.clear()
323 rspec_string = self.readSliceRSpec()
327 networks = rspec_get_networks(rspec_string)
328 for network in networks:
329 self.network_names.append(network)
330 networkItem = TreeItem([QString(network), QString(""), QString("")], self.nodeModel.rootItem)
332 all_nodes = rspec_get_nodes_from_network(rspec_string, network)
333 sliver_nodes = rspec_get_sliver_nodes_from_network(rspec_string, network)
334 available_nodes = filter(lambda x:x not in sliver_nodes, all_nodes)
336 for node in sliver_nodes:
337 nodeItem = TreeItem([QString(""), QString("%s" % node), QString("true")], networkItem)
338 networkItem.appendChild(nodeItem)
340 for node in available_nodes:
341 nodeItem = TreeItem([QString(""), QString(node), QString("false")], networkItem)
342 networkItem.appendChild(nodeItem)
344 self.nodeModel.rootItem.appendChild(networkItem)
346 self.filterModel.setSourceModel(self.nodeModel)
347 self.filterModel.setFilterKeyColumn(-1)
348 self.filterModel.setDynamicSortFilter(True)
350 self.nodeView.setItemDelegateForColumn(1, self.nodeNameDelegate)
351 self.nodeView.setItemDelegateForColumn(2, self.selectDelegate)
352 self.nodeView.setModel(self.filterModel)
353 self.nodeView.expandAll()
354 self.nodeView.resizeColumnToContents(1)
357 class MainScreen(SfaScreen):
358 def __init__(self, parent):
359 SfaScreen.__init__(self, parent)
361 slice = SliceWidget(self)
362 self.init(slice, "Main Window", "OneLab Federation GUI")