cosmetic
[sface.git] / sface / screens / mainscreen.py
1
2 import os
3 from PyQt4.QtCore import *
4 from PyQt4.QtGui import *
5
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
11
12 class NodeView(QTreeView):
13     def __init__(self, parent):
14         QTreeView.__init__(self, parent)
15
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)
23
24 class SelectDelegate(QStyledItemDelegate):
25     pass
26
27 class NodeNameDelegate(QStyledItemDelegate):
28     def __init__(self, parent):
29         QStyledItemDelegate.__init__(self)
30
31     def paint(self, painter, option, index):
32         data = "%s" % index.data().toString()
33         model = index.model()
34         select_index = model.index(index.row(), 2, index.parent())
35         select_data = select_index.data().toString()
36
37         if select_data not in ("true", "add", "remove"): # default view
38             QStyledItemDelegate.paint(self, painter, option, index)
39             return
40
41         fm = QFontMetrics(option.font)
42         rect = option.rect
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() 
47
48         path = QPainterPath()
49         path.addRoundedRect(x, y, w, h, 4, 4)
50
51         painter.save()
52         painter.setRenderHint(QPainter.Antialiasing)
53         painter.drawRoundedRect(rect, 4, 4)
54
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))
59
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))
66
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))
73
74         painter.restore()
75
76 class TreeItem:
77     def __init__(self, data, parent=None):
78         self.parentItem = parent
79         self.itemData = data
80         self.childItems = []
81
82     def clear(self):
83         for child in self.childItems:
84             child.clear()
85             del child
86         del self.childItems
87         self.childItems = []
88
89     def appendChild(self, child):
90         self.childItems.append(child)
91
92     def child(self, row):
93         return self.childItems[row]
94     
95     def childCount(self):
96         return len(self.childItems)
97
98     def childNumber(self):
99         if self.parentItem:
100             return self.parentItem.childItems.index(self)
101         return 0
102
103     def columnCount(self):
104         return len(self.itemData)
105
106     def data(self, column):
107         return self.itemData[column]
108
109     def insertChildren(self, position, count, columns):
110         if position < 0 or position > len(self.childItems):
111             return False
112         
113         for row in range(count):
114             data = self.data(columns)
115             item = TreeItem(data, self)
116             self.childItems.insert(position, item)
117
118         return True
119
120     def insertColumns(self, position, columns):
121         if position < 0 or position > len(self.itemData):
122             return False
123
124         for column in range(columns):
125             self.itemData.insert(position, QVariant())
126         
127         for child in self.childItems:
128             child.insertColumns(position, columns)
129         
130         return True
131
132     def setData(self, column, value):
133         if column < 0 or column >= len(self.itemData):
134             return False
135
136         self.itemData[column] = value
137         return True
138     
139     def parent(self):
140         return self.parentItem
141
142
143
144 class NodeModel(QAbstractItemModel):
145     def __init__(self, parent):
146         QAbstractItemModel.__init__(self, parent)
147         self.__initRoot()
148
149     def clear(self):
150         self.rootItem.clear()
151         self.__initRoot()
152
153     def __initRoot(self):
154         self.rootItem = TreeItem([QString("Testbed"), QString("Hostname"), QString("Selected")])
155
156     def getItem(self, index):
157         if index.isValid():
158             item = index.internalPointer()
159             if item: return item
160         return self.rootItem
161
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)
165         return QVariant()
166
167     def index(self, row, column, parent):
168         if not self.hasIndex(row, column, parent):
169             return QModelIndex()
170
171         parentItem = self.getItem(parent)
172         childItem = parentItem.child(row)
173         if childItem:
174             return self.createIndex(row, column, childItem)
175         else:
176             return QModelIndex()
177
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()
182         return ret
183
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())
188         self.endInsertRows()
189         return ret
190
191     def parent(self, index):
192         if not index.isValid():
193             return QModelIndex()
194
195         childItem = self.getItem(index)
196         parentItem = childItem.parent()
197         if parentItem is self.rootItem:
198             return QModelIndex()
199
200         return self.createIndex(parentItem.childNumber(), 0, parentItem)
201
202     def rowCount(self, parent=QModelIndex()):
203         parentItem = self.getItem(parent)
204         return parentItem.childCount()
205
206     def columnCount(self, parent=None):
207         return self.rootItem.columnCount()
208
209     def data(self, index, role):
210         if not index.isValid():
211             return QVariant()
212
213         if role != Qt.DisplayRole and role != Qt.EditRole:
214             return QVariant()
215
216         item = self.getItem(index)
217         return item.data(index.column())
218
219     def flags(self, index):
220         if not index.isValid():
221             return 0
222         return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
223
224     def setData(self, index, value, role):
225         if role != Qt.EditRole:
226             return False
227
228         item = self.getItem(index)
229         ret = item.setData(index.column(), value)
230         if ret:
231             self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), index, index)
232         return ret
233
234
235
236 class SliceWidget(QWidget):
237     def __init__(self, parent):
238         QWidget.__init__(self, parent)
239
240         slicename = QLabel ("Slice : %s"%(config.getSlice() or "None"),self)
241         slicename.setScaledContents(False)
242         searchlabel = QLabel ("Search: ", self)
243         searchlabel.setScaledContents(False)
244         searchbox = QLineEdit(self)
245         searchbox.setAttribute(Qt.WA_MacShowFocusRect, 0)
246
247         toplayout = QHBoxLayout()
248         toplayout.addWidget(slicename, 0, Qt.AlignLeft)
249         toplayout.addStretch()
250         toplayout.addWidget(searchlabel, 0, Qt.AlignRight)
251         toplayout.addWidget(searchbox, 0, Qt.AlignRight)
252
253         self.nodeView = NodeView(self)
254         self.nodeModel = NodeModel(self)
255         self.filterModel = QSortFilterProxyModel(self) # enable filtering
256         self.filterModel.setSourceModel(self.nodeModel)
257         self.nodeView.setModel(self.filterModel)
258         self.filterModel.setDynamicSortFilter(True)
259 #        self.nodeView.setModel(self.filterModel)
260         self.nodeView.setModel(self.nodeModel)
261
262         self.nodeNameDelegate = NodeNameDelegate(self)
263         self.selectDelegate = SelectDelegate(self)
264
265         refresh = QPushButton("Update Slice Data", self)
266         refresh.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
267         submit = QPushButton("Submit", self)
268         submit.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
269
270         bottomlayout = QHBoxLayout()
271         bottomlayout.addWidget(refresh, 0, Qt.AlignLeft)
272         bottomlayout.addStretch()
273         bottomlayout.addWidget(submit, 0, Qt.AlignRight)
274
275         layout = QVBoxLayout()
276         layout.addLayout(toplayout)
277         layout.addWidget(self.nodeView)
278         layout.addLayout(bottomlayout)
279         self.setLayout(layout)
280         self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
281
282         self.connect(refresh, SIGNAL('clicked()'), self.refresh)
283         self.connect(submit, SIGNAL('clicked()'), self.submit)
284         self.connect(searchbox, SIGNAL('textChanged(QString)'), self.filter)
285
286         self.updateView()
287
288     def filter(self, filter):
289         print self.filterModel.rowCount(), self.nodeModel.rowCount()
290         self.filterModel.setFilterRegExp(QRegExp(filter))
291
292     def submit(self):
293         self.parent().setStatus("TODO: Submit not implemented yet!", 3000)
294         
295     def readSliceRSpec(self):
296         rspec_file = config.getSliceRSpecFile()
297         if os.path.exists(rspec_file):
298             xml = open(rspec_file).read()
299             return xml
300         return None
301
302     def refresh(self):
303         if not config.getSlice():
304             self.parent().setStatus("<font color='red'>Slice not set yet!</font>", timeout=None)
305             return
306
307         self.process = SfiProcess()
308         outfile = self.process.getRSpecFromSM()
309         self.parent().setStatus("Updating slice data. This may take some time...", timeout=None)
310         
311         self.connect(self.process, SIGNAL('finished()'), self.refreshFinished)
312
313     def refreshFinished(self):
314         del self.process
315         self.parent().setStatus("<font color='green'>Slice data updated.</font>", timeout=5000)
316         self.updateView()
317
318     def updateView(self):
319         self.nodeModel.clear()
320         rspec_string = self.readSliceRSpec()
321         if not rspec_string:
322             return None
323
324         networks = rspec_get_networks(rspec_string)
325         for network in networks:
326             networkItem = TreeItem([QString(network), QString(""), QString("")], self.nodeModel.rootItem)
327
328             all_nodes = rspec_get_nodes_from_network(rspec_string, network)
329             sliver_nodes = rspec_get_sliver_nodes_from_network(rspec_string, network)
330             available_nodes = filter(lambda x:x not in sliver_nodes, all_nodes)
331
332             for node in sliver_nodes:
333                 nodeItem = TreeItem([QString(""), QString("%s" % node), QString("true")], networkItem)
334                 networkItem.appendChild(nodeItem)
335
336             for node in available_nodes:
337                 nodeItem = TreeItem([QString(""), QString(node), QString("false")], networkItem)
338                 networkItem.appendChild(nodeItem)
339
340             self.nodeModel.rootItem.appendChild(networkItem)
341
342         self.nodeView.expandAll()
343         self.nodeView.resizeColumnToContents(1)
344         self.nodeView.setItemDelegateForColumn(1, self.nodeNameDelegate)
345         self.nodeView.setItemDelegateForColumn(2, self.selectDelegate)
346         self.filterModel.setFilterKeyColumn(1)
347
348
349 class MainScreen(SfaScreen):
350     def __init__(self, parent):
351         SfaScreen.__init__(self, parent)
352
353         slice = SliceWidget(self)
354         self.init(slice, "Main Window", "OneLab Federation GUI")