specify -t --type option when calling sfi show
[sface.git] / sface / sliceview.py
1 import datetime
2 import os
3 import pickle
4 from PyQt4.QtCore import *
5 from PyQt4.QtGui import *
6
7 from sface.config import config
8 from sface.sfidata import SfiData
9 from sface.sfiprocess import SfiProcess
10
11 NAME_COLUMN = 0
12 MEMBERSHIP_STATUS_COLUMN = 1
13
14 # maximum length of a name to display before clipping
15 NAME_MAX_LEN = 48
16
17 MODE_AUTHORITY_SLICES = 0x01
18 MODE_USER_SLICES =      0x02
19 MODE_SELECT_SINGLE =    0x10
20
21 slice_status = { "in": "Selected",
22                 "out": "Not Selected"}
23
24 color_status = { "in": QColor.fromRgb(0, 250, 250), }
25
26 class SliceNameDelegate(QStyledItemDelegate):
27     def __init__(self, parent):
28         QStyledItemDelegate.__init__(self, parent)
29
30     def displayText(self, value, locale):
31         data = str(QStyledItemDelegate.displayText(self, value, locale))
32         if (len(data)>NAME_MAX_LEN):
33             data = data[:(NAME_MAX_LEN-3)] + "..."
34         return QString(data)
35
36     def paint(self, painter, option, index):
37         model = index.model()
38         data = str(self.displayText(index.data(), QLocale()))
39         status_index = model.index(index.row(), MEMBERSHIP_STATUS_COLUMN, index.parent())
40         status_data = status_index.data().toString()
41
42         fm = QFontMetrics(option.font)
43         rect = QRect(option.rect)
44
45         rect.setHeight(rect.height() - 2)
46         rect.setWidth(fm.width(QString(data)) + 6)
47         rect.setX(rect.x() + 5)
48         rect.setY(rect.y() - 1)
49
50         textRect = QRect(option.rect)
51         textRect.setWidth(fm.width(QString(data)) + 6)
52         textRect.setX(rect.x())
53
54         x, y, h, w = rect.x(), rect.y(), rect.height(), rect.width()
55
56         path = QPainterPath()
57         path.addRoundedRect(x - 1, y + 1, w, h, 4, 4)
58
59         painter.save()
60         painter.setRenderHint(QPainter.Antialiasing)
61
62         if option.state & QStyle.State_Selected:
63             painter.fillRect(option.rect, option.palette.color(QPalette.Active, QPalette.Highlight))
64
65         color = None
66         for x in slice_status.keys():
67             if (slice_status[x] == status_data) and (x in color_status):
68                 color = color_status[x]
69
70         if color != None:
71             painter.fillPath(path, color)
72         painter.setPen(QColor.fromRgb(0, 0, 0))
73         painter.drawText(textRect, Qt.AlignVCenter, QString(data))
74
75         painter.restore()
76
77 class SliceView(QTableView):
78     def __init__(self, mode=0, parent=None):
79         QTableView.__init__(self, parent)
80         self.mode = mode
81
82         self.setSelectionBehavior(QAbstractItemView.SelectRows)
83         self.setSelectionMode(QAbstractItemView.SingleSelection)
84         self.setAlternatingRowColors(True)
85         self.setAttribute(Qt.WA_MacShowFocusRect, 0)
86         self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
87         self.setToolTip("Double click on a row to change its status.")
88
89         self.setItemDelegateForColumn(0, SliceNameDelegate(self))
90
91     def keyPressEvent(self, event):
92         if (event.key() == Qt.Key_Space):
93             self.toggleSelection()
94         else:
95             QTableView.keyPressEvent(self, event)
96
97     def selectionChanged(self, selected, deselected):
98         QTableView.selectionChanged(self, selected, deselected)
99         if (self.mode & MODE_SELECT_SINGLE) != 0:
100             self.unselectAll()
101             self.toggleSelection()
102
103     def mouseDoubleClickEvent(self, event):
104         self.toggleSelection()
105
106     def unselectAll(self):
107         index = self.currentIndex()
108         model = index.model()
109         for row in range(0, model.rowCount()):
110             status_index = model.index(row, MEMBERSHIP_STATUS_COLUMN, index.parent())
111             model.setData(status_index, QString(slice_status['out']))
112
113         #model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"),
114         #            model.index(0, MEMBERSHIP_STATUS_COLUMN, index.parent()),
115         #            model.index(model.rowCount(), MEMBERSHIP_STATUS_COLUMN, index.parent()))
116
117     def toggleSelection(self):
118         index = self.currentIndex()
119         model = index.model()
120         status_index = model.index(index.row(), MEMBERSHIP_STATUS_COLUMN, index.parent())
121         status_data = status_index.data().toString()
122         node_index = model.index(index.row(), NAME_COLUMN, index.parent())
123         node_data = node_index.data().toString()
124
125         if status_data == slice_status['in']:
126             model.setData(status_index, QString(slice_status['out']))
127         else:
128             model.setData(status_index, QString(slice_status['in']))
129
130         model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), node_index, node_index)
131
132     def currentChanged(self, current, previous):
133         model = current.model()
134         node_index = model.index(current.row(), 0, current.parent())
135         node_data = node_index.data().toString()
136
137 class SliceModel(QStandardItemModel):
138     def __init__(self, rows=0, columns=4, parent=None, mode=MODE_AUTHORITY_SLICES):
139          QStandardItemModel.__init__(self, rows, columns, parent)
140          self.mode = mode
141
142     def updateModel(self):
143         self.clear()
144
145         if (self.mode & MODE_AUTHORITY_SLICES) != 0:
146             slice_names = SfiData().getAuthorityHrns(type="slice")
147         else: # MODE_USER_SLICES
148             slice_names = SfiData().getUserSliceHrns()
149
150         rootItem = self.invisibleRootItem()
151
152         for name in slice_names:
153             rootItem.appendRow([self.readOnlyItem(name),
154                                 self.readOnlyItem(slice_status["out"])])
155
156         headers = QStringList() << "Slice Name" << "Status"
157         self.setHorizontalHeaderLabels(headers)
158
159     def readOnlyItem(self, x):
160         item = QStandardItem(QString(x))
161         item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
162         return item
163
164     def getSelectedSlices(self):
165         slices = []
166         item = self.invisibleRootItem()
167         children = item.rowCount()
168         for row in range(0, children):
169             childName = str(item.child(row, NAME_COLUMN).data(Qt.DisplayRole).toString())
170             childStatus = str(item.child(row, MEMBERSHIP_STATUS_COLUMN).data(Qt.DisplayRole).toString())
171
172             if (childStatus == slice_status['in']):
173                 slices.append(childName)
174
175         return slices
176
177 class SlicePickerWindow(QDialog):
178     def __init__(self, mode=MODE_AUTHORITY_SLICES, parent=None):
179         super(SlicePickerWindow, self).__init__(parent)
180         self.mode = mode
181         self.slices = None
182
183         self.setWindowTitle("Slice Picker")
184
185         sliceLabel = QLabel("Slices:")
186         self.sliceView = SliceView(mode=self.mode)
187
188         self.status = QLabel("")
189         self.status.setMaximumWidth(640)
190
191         self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
192         self.buttonBox.button(QDialogButtonBox.Ok).setDefault(True)
193
194         layout = QVBoxLayout()
195         layout.addWidget(sliceLabel)
196         layout.addWidget(self.sliceView)
197         layout.addWidget(self.status)
198         layout.addWidget(self.buttonBox)
199         self.setLayout(layout)
200
201         self.connect(self.buttonBox, SIGNAL("accepted()"), self, SLOT("accept()"))
202         self.connect(self.buttonBox, SIGNAL("rejected()"), self, SLOT("reject()"))
203
204         self.sliceModel = SliceModel(mode=mode)
205
206         self.refresh()
207
208     def accept(self):
209         self.slices = self.sliceModel.getSelectedSlices()
210         print self.slices
211         QDialog.accept(self)
212
213     def setStatus(self, x):
214         self.status.setText(x)
215
216     def refresh(self):
217         if (self.mode & MODE_AUTHORITY_SLICES) != 0:
218             self.refreshAuthority()
219         else:
220             self.refreshUser()
221
222     def refreshUser(self):
223         self.process = SfiProcess(self)
224         self.connect(self.process, SIGNAL('finished()'), self.getUserRecordFinished)
225
226         self.process.getRecord(hrn=config.getUser(), type="user", filename=config.getUserRecordFile())
227         self.setStatus("Refreshing user record. This will take a moment...")
228
229     def getUserRecordFinished(self):
230         self.disconnect(self.process, SIGNAL('finished()'), self.getUserRecordFinished)
231
232         faultString = self.process.getFaultString()
233         if not faultString:
234             self.setStatus("<font color='green'>Slice list refreshed.</font>")
235             self.updateSliceView()
236         else:
237             self.setStatus("<font color='red'>User rec refresh error: %s</font>" % (faultString))
238
239     def refreshAuthority(self):
240         self.process = SfiProcess(self)
241         self.connect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
242
243         self.process.listRecords(config.getAuthority(), None)
244         self.setStatus("Refreshing slice list. This will take a moment...")
245
246     def getAuthorityRecordFinished(self):
247         self.disconnect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
248
249         faultString = self.process.getFaultString()
250         if not faultString:
251             self.setStatus("<font color='green'>Slice list refreshed.</font>")
252             self.updateSliceView()
253         else:
254             self.setStatus("<font color='red'>Authority rec refresh error: %s</font>" % (faultString))
255
256     def updateSliceView(self):
257         self.sliceModel.updateModel()
258
259         self.sliceView.setModel(self.sliceModel)
260         self.sliceView.resizeColumnToContents(0)
261