1
2
3
4
5
6
7
8
9
10
11
12 import copy
13 from datetime import date
14 from decimal import Decimal
15
16 from PyQt4.Qt import *
17 from twisted.internet import reactor, defer
18
19 from mc import MC
23 '''
24 Model for read only tables.
25 '''
26 - def __init__(self, parentObject, modelObject, taskPath, *args, **kw):
27 '''
28 Initializes the model
29 '''
30 QAbstractTableModel.__init__(self, parentObject)
31
32
33 self._modelObject = modelObject
34 self._columnLabels = self.getColumnLabels()
35 self._columnNames = modelObject.__mapper__.columns.keys()
36 self._taskPath = taskPath
37 self._args = args
38 self._kw = kw
39
40 self.numRows = MC.MAX_RECORDS
41 self.numCols = 0
42 self.cacheRows = MC.RO_TABLE_CACHE_SIZE
43
44 self.start = self.end = 0
45 self._maxData = 0
46
47 self.cache = None
48
49 self.cacheIndex = None
50
51 self._busy = False
52
53 self.initCache()
54
56 '''
57 Returns column labels according to current model object set to this
58 view model.
59 '''
60 labels = []
61
62 for column in self._modelObject.__mapper__.columns:
63 labels.append(column.info.get('label') or '')
64
65 return labels
66
68 '''
69 Returns column sizes according to current model object set to this
70 view model.
71 '''
72 sizes = []
73
74 for column in self._modelObject.__mapper__.columns:
75 sizes.append(column.info.get('width') or 0)
76
77 return sizes
78
82
84 '''
85 Original QAbstractTableModel parent() method does something
86 different than QWidget.parent() method. We override it here
87 in order to make it work like the rest.
88 '''
89 return QObject.parent(self)
90
91 @defer.inlineCallbacks
93 '''
94 Gets data from the server. Data indexes are defined by start and end
95 parameters.
96
97 @param start: Start index of the data to be fetched
98 @type start: int
99 @param end: End index of the data to be fetched
100 @type end: int
101 '''
102 kw = copy.deepcopy(self._kw)
103 kw['start'] = start
104 kw['end'] = end
105
106 result = yield self.parent().execute(self._taskPath, *self._args, **kw)
107
108 defer.returnValue(result)
109
111 '''
112 Initializes the structures related with cache structure.
113 '''
114 self.cache = [None] * self.cacheRows
115 self.cacheIndex = [None] * self.cacheRows
116
117 @defer.inlineCallbacks
119 '''
120 Called when a given row is not present in the cache.
121
122 @param row: The index of data that was not found in the cache.
123 '''
124
125 if self._busy:
126 defer.returnValue(None)
127
128 self._busy = True
129 self.initCache()
130 self.start = row - self.cacheRows / 2
131
132
133 if self.start < 0:
134 self.start = 0
135
136 self.end = row + self.cacheRows / 2
137
138 try:
139 result = yield self.getData(self.start, self.end)
140 defer.returnValue(self.receivedData(result))
141 except Exception, e:
142 print '*** Error in cacheMiss(): ', e
143 self._busy = False
144
146 '''
147 This method is called upon receiving data from server.
148
149 @param rows: List containing objects.
150 @type rows: list
151 '''
152 self._busy = False
153
154 oldMaxData = self._maxData
155
156 if not rows:
157 self._maxData = 0
158
159 else:
160
161 for i, currentObject in enumerate(rows):
162 self.cache[i] = currentObject
163 self.cacheIndex[i] = i + self.start
164
165 if i + self.start + 1 < self.end:
166
167 self.numRows = i + self.start + 1
168 else:
169 if self.numRows <= i + self.start + 1:
170 self.numRows = i + self.start + 2
171
172 self._maxData = self.numRows
173
174 if self._maxData > oldMaxData:
175 self.beginInsertRows(QModelIndex(), oldMaxData, self._maxData)
176 self.endInsertRows()
177 self.emit(SIGNAL('rowCountChanged(int)'), self._maxData)
178
179 elif self._maxData < oldMaxData:
180 self.beginRemoveRows(QModelIndex(), self._maxData, oldMaxData)
181 self.endRemoveRows()
182 self.emit(SIGNAL('rowCountChanged(int)'), self._maxData)
183
184 @MC.returnQVariant
186 '''
187 Overriden headerData method that feeds the view with vertical and
188 horizontal header information.
189 '''
190 if role not in (Qt.DisplayRole, Qt.ToolTipRole, Qt.TextAlignmentRole):
191 return None
192
193 if orientation == Qt.Horizontal:
194 extra = ''
195 return u"%s %s" % (self._columnLabels[section], extra)
196
197 - def data(self, index, role=Qt.DisplayRole):
198 '''
199 Overriden data method, called by QTableView to obtain data for the
200 passed index.
201
202 @param index: Index of the data desired
203 @type index: QModelIndex
204 '''
205 if not index.isValid():
206 return MC.EMPTY_QVARIANT
207
208 row = index.row()
209 col = index.column()
210
211 if row not in self.cacheIndex:
212
213 if not self._busy:
214 self.cacheMiss(row)
215
216 return MC.EMPTY_QVARIANT
217
218
219 if role in (Qt.DisplayRole, Qt.ToolTipRole):
220 rowObject = self.cache[row - self.start]
221 value = getattr(rowObject, self._columnNames[col])
222
223
224 if type(value) == date:
225 dt = getattr(rowObject, self._columnNames[col])
226 return QVariant(QDate(dt.year, dt.month, dt.day))
227
228
229 if type(value) == Decimal:
230 value = '%.2f' % value
231
232 return QVariant(value)
233
234 elif role == Qt.TextAlignmentRole:
235 rowObject = self.cache[row - self.start]
236 value = getattr(rowObject, self._columnNames[col])
237
238 if type(value) == Decimal:
239
240 return Qt.AlignRight | Qt.AlignVCenter
241 else:
242 return Qt.AlignLeft | Qt.AlignVCenter
243
244 return MC.EMPTY_QVARIANT
245
247 '''
248 Returns True if model is busy, False otherwise.
249 '''
250 return self._busy
251
262
264 '''
265 Overriden rowCount method, returns the number of rows present in the model
266 '''
267 return self._maxData
268
270 '''
271 Returns the number of columns displayed in table, overriden
272 Qt model method.
273 '''
274 return len(self._columnLabels)
275
277 return self.cache[row - self.start]
278