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