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