e68ca21077aaeb2e2f1c663f6810495a798c13a6
[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.setAttribute(Qt.WA_MacShowFocusRect, 0)
21         self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
22
23 class SelectDelegate(QStyledItemDelegate):
24     pass
25
26 class NodeNameDelegate(QStyledItemDelegate):
27     def __init__(self, parent):
28         QStyledItemDelegate.__init__(self)
29
30     def paint(self, painter, option, index):
31         data = "%s" % index.data().toString()
32
33 #         model = index.model()
34 #         select_index = model.index(index.row(), 2, index.parent())
35 #         select_data = select_index.data().toString()
36 #         if select_data == "true":
37 #             print select_data
38 #             model.setData(select_index, QString("TEST"), Qt.DisplayRole)
39 #             model.emit(SIGNAL('dataChanged(QModelIndex, QModelIndex)'),
40 #                        select_index, select_index)
41
42
43         if data.startswith("*"): # already in the sliver
44             data = " %s " % data[1:]
45
46             fm = QFontMetrics(option.font)
47             rect = option.rect
48             rect.setWidth(fm.width(QString(data)))
49             rect.setHeight(rect.height() - 2)
50             rect.setX(rect.x() + 1)
51             x, y, h, w = rect.x(), rect.y(), rect.height(), rect.width()
52
53             path = QPainterPath()
54             path.addRoundedRect(x, y, w, h, 4, 4)
55
56             painter.save()
57             painter.setRenderHint(QPainter.Antialiasing)
58             painter.drawRoundedRect(rect, 4, 4)
59             painter.fillPath(path, QColor.fromRgb(0, 250, 0))
60             painter.setPen(QColor.fromRgb(0, 0, 0))
61             painter.drawText(option.rect, 0, QString(data))
62             painter.restore()
63         else: # others, fall back to default view
64             QStyledItemDelegate.paint(self, painter, option, index)
65
66 class TreeItem:
67     def __init__(self, data, parent=None):
68         self.parentItem = parent
69         self.itemData = data
70         self.childItems = []
71
72     def clear(self):
73         for child in self.childItems:
74             child.clear()
75             del child
76         del self.childItems
77         self.childItems = []
78
79     def appendChild(self, child):
80         self.childItems.append(child)
81
82     def child(self, row):
83         return self.childItems[row]
84     
85     def childCount(self):
86         return len(self.childItems)
87
88     def columnCount(self):
89         return len(self.itemData)
90
91     def data(self, column):
92         return self.itemData[column]
93
94     def row(self):
95         if (self.parentItem):
96             try:
97                 return self.parentItem.childItems.index(self)
98             except ValueError:
99                 return 0
100         return 0
101     
102     def parent(self):
103         return self.parentItem
104
105
106
107 class NodeModel(QAbstractItemModel):
108     def __init__(self, parent):
109         QAbstractItemModel.__init__(self, parent)
110         self.__initRoot()
111
112     def clear(self):
113         self.rootItem.clear()
114         self.__initRoot()
115
116     def __initRoot(self):
117         self.rootItem = TreeItem([QString("Testbed"), QString("Hostname"), QString("Selected")])
118
119     def columnCount(self, parent):
120         if parent.isValid():
121             return parent.internalPointer().columnCount()
122         else:
123             return self.rootItem.columnCount()
124
125     def data(self, index, role):
126         if not index.isValid():
127             return QVariant()
128
129         if role != Qt.DisplayRole:
130             return QVariant()
131
132         item = index.internalPointer()
133         return item.data(index.column())
134
135     def headerData(self, section, orientation, role):
136         if orientation == Qt.Horizontal and role == Qt.DisplayRole:
137             return self.rootItem.data(section)
138         return QVariant()
139
140     def flags(self, index):
141         if not index.isValid():
142             return 0
143         return Qt.ItemIsEnabled | Qt.ItemIsSelectable # | Qt.ItemIsEditable
144
145     def index(self, row, column, parent):
146         if not self.hasIndex(row, column, parent):
147             return QModelIndex()
148
149         parentItem = None
150         if not parent.isValid():
151             parentItem = self.rootItem
152         else:
153             parentItem = parent.internalPointer()
154             
155         childItem = parentItem.child(row)
156         if childItem:
157             return self.createIndex(row, column, childItem)
158         else:
159             return QModelIndex()
160
161     def parent(self, index):
162         if not index.isValid():
163             return QModelIndex()
164
165         childItem = index.internalPointer()
166         parentItem = childItem.parent()
167
168         if not parentItem:
169             return QModelIndex()
170
171         if parentItem is self.rootItem:
172             return QModelIndex()
173
174         return self.createIndex(parentItem.row(), 0, parentItem)
175
176     def rowCount(self, parent):
177         if parent.column() > 0:
178             return 0
179
180         parentItem = None
181         if not parent.isValid():
182             parentItem = self.rootItem
183         else:
184             parentItem = parent.internalPointer()
185
186         return parentItem.childCount()
187
188
189 class SliceWidget(QWidget):
190     def __init__(self, parent):
191         QWidget.__init__(self, parent)
192
193         slicename = QLabel ("Slice : %s"%(config.getSlice() or "None"),self)
194         slicename.setScaledContents(False)
195
196         self.nodeView = NodeView(self)
197         self.nodeModel = NodeModel(self)
198         self.nodeView.setModel(self.nodeModel)
199         self.nodeNameDelegate = NodeNameDelegate(self)
200         self.selectDelegate = SelectDelegate(self)
201
202
203         refresh = QPushButton("Update Slice Data", self)
204         refresh.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
205         submit = QPushButton("Submit", self)
206         submit.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
207
208         hlayout = QHBoxLayout()
209         hlayout.addWidget(refresh, 0, Qt.AlignLeft)
210         hlayout.addStretch()
211         hlayout.addWidget(submit, 0, Qt.AlignRight)
212
213         layout = QVBoxLayout()
214         layout.addWidget(slicename)
215         layout.addWidget(self.nodeView)
216         layout.addLayout(hlayout)
217
218         self.setLayout(layout)
219         self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
220
221         self.connect(refresh, SIGNAL('clicked()'), self.refresh)
222         self.connect(submit, SIGNAL('clicked()'), self.submit)
223
224         rspec_file = os.path.expanduser("~/.sfi/%s.rspec" % config.getSlice())
225         if os.path.exists(rspec_file):
226             self.updateView()
227
228     def submit(self):
229         self.parent().setStatus("TODO: Submit not implemented yet!", 3000)
230         
231     def readSliceRSpec(self):
232         rspec_file = config.getSliceRSpecFile()
233         xml = open(rspec_file).read()
234         return xml
235
236     def refresh(self):
237         if not config.getSlice():
238             self.parent().setStatus("<font color='red'>Slice not set yet!</font>", timeout=None)
239             return
240
241         self.process = SfiProcess()
242         outfile = self.process.getRSpecFromSM()
243         self.parent().setStatus("Updating slice data. This may take some time...", timeout=None)
244         
245         self.connect(self.process, SIGNAL('finished()'), self.refreshFinished)
246
247     def refreshFinished(self):
248         del self.process
249         self.parent().setStatus("<font color='green'>Slice data updated.</font>", timeout=5000)
250         self.updateView()
251
252     def updateView(self):
253         self.nodeModel.clear()
254         rspec_string = self.readSliceRSpec()
255         networks = rspec_get_networks(rspec_string)
256
257         for network in networks:
258             networkItem = TreeItem([QString(network), QString(""), QString("")], self.nodeModel.rootItem)
259
260             all_nodes = rspec_get_nodes_from_network(rspec_string, network)
261             sliver_nodes = rspec_get_sliver_nodes_from_network(rspec_string, network)
262             available_nodes = filter(lambda x:x not in sliver_nodes, all_nodes)
263
264             for node in sliver_nodes:
265                 nodeItem = TreeItem([QString(""), QString("*%s" % node), QString("true")], networkItem)
266                 networkItem.appendChild(nodeItem)
267
268             for node in available_nodes:
269                 nodeItem = TreeItem([QString(""), QString(node), QString("false")], networkItem)
270                 networkItem.appendChild(nodeItem)
271
272             self.nodeModel.rootItem.appendChild(networkItem)
273
274         self.nodeView.expandAll()
275         self.nodeView.resizeColumnToContents(1)
276
277         self.nodeView.setItemDelegateForColumn(1, self.nodeNameDelegate)
278         self.nodeView.setItemDelegateForColumn(2, self.selectDelegate)
279
280
281 class MainScreen(SfaScreen):
282     def __init__(self, parent):
283         SfaScreen.__init__(self, parent)
284
285         slice = SliceWidget(self)
286         self.init(slice, "Main Window", "OneLab Federation GUI")