Package mcbase :: Package widgets :: Module mctablemodelro
[hide private]
[frames] | no frames]

Source Code for Module mcbase.widgets.mctablemodelro

  1  # -*- coding: utf-8 -*- 
  2   
  3  ############################################################ 
  4  # mctablemodelro.py 
  5  # 
  6  # Implements a table model for retreiving tabular data 
  7  # from application server. 
  8  # 
  9  # (C) 2008 Likya Software 
 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 
20 21 22 -class MCTableModelRO(QAbstractTableModel):
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 # Save most of the init parameters 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 # Start and end of current cache 44 self.start = self.end = 0 45 self._maxData = 0 46 # Cache for objects displayed in table 47 self.cache = None 48 # Stores cache indexes 49 self.cacheIndex = None 50 # We are initially not busy 51 self._busy = False 52 # Initilize cache 53 self.initCache()
54
55 - def getColumnLabels(self):
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
67 - def getColumnSizes(self):
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
79 - def setModelObjectProperties(self, modelObject):
80 # Column names for this object 81 pass
82
83 - def parent(self):
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
92 - def getData(self, start, end):
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
110 - def initCache(self):
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
118 - def cacheMiss(self, row):
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 # If we are already busy then do nothing. 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 # If we are already at the start, we can not go back. 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
145 - def receivedData(self, rows):
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 # If we reach here then we are end of data 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
185 - def headerData(self, section, orientation, role=Qt.DisplayRole):
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 # Display role 219 if role in (Qt.DisplayRole, Qt.ToolTipRole): 220 rowObject = self.cache[row - self.start] 221 value = getattr(rowObject, self._columnNames[col]) 222 223 # If the data type is python date, convert it to QDate 224 if type(value) == date: 225 dt = getattr(rowObject, self._columnNames[col]) 226 return QVariant(QDate(dt.year, dt.month, dt.day)) 227 228 # Special case for numeric fields 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 # Right align numeric fields 240 return Qt.AlignRight | Qt.AlignVCenter 241 else: 242 return Qt.AlignLeft | Qt.AlignVCenter 243 244 return MC.EMPTY_QVARIANT
245
246 - def isBusy(self):
247 ''' 248 Returns True if model is busy, False otherwise. 249 ''' 250 return self._busy
251
252 - def hideKeyColumns(self):
253 ''' 254 Hides key columns. 255 ''' 256 if not self.headerLoaded: 257 self.headerLoaded = True 258 for i, headerItem in enumerate(self.header): 259 if headerItem[MC.SOURCE_HEADER_STATUS] in MC.TABLE_HIDDEN_COL_TYPES: 260 if not self.parent()._main.isColumnHidden(i): 261 self.parent().hideColumn(i)
262
263 - def rowCount(self, parentModel=None):
264 ''' 265 Overriden rowCount method, returns the number of rows present in the model 266 ''' 267 return self._maxData
268
269 - def columnCount(self, parentModel=None):
270 ''' 271 Returns the number of columns displayed in table, overriden 272 Qt model method. 273 ''' 274 return len(self._columnLabels)
275
276 - def selectedObject(self, row):
277 return self.cache[row - self.start]
278