00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 from Koo.Model import KooModel
00031 from Koo.Model.Group import RecordGroup
00032 from Koo.View.AbstractView import *
00033 from Koo.Fields.AbstractFieldDelegate import *
00034 from PyQt4.QtCore import *
00035 from PyQt4.QtGui import *
00036 from Koo.Common import Numeric
00037 from Koo import Rpc
00038
00039 class KooTreeView(QTreeView):
00040 def contextMenuEvent( self, event ):
00041 index = self.indexAt( event.pos() )
00042 delegate = self.itemDelegate( index )
00043 if not isinstance(delegate, AbstractFieldDelegate):
00044 return
00045 delegate.showPopupMenu( self, self.mapToGlobal( event.pos() ) )
00046
00047 def sizeHintForColumn( self, column ):
00048 QApplication.setOverrideCursor( Qt.WaitCursor )
00049 try:
00050 model = self.model()
00051
00052 group = model.recordGroup()
00053 records = group.loadedRecords()
00054
00055 if len(records) == group.count():
00056 QApplication.restoreOverrideCursor()
00057 return QTreeView.sizeHintForColumn( self, column )
00058 viewOptions = self.viewOptions()
00059 delegate = self.itemDelegateForColumn( column )
00060 hint = 0
00061 for record in records:
00062 index = model.indexFromRecord( record )
00063 index = model.index( index.row(), column, index.parent() )
00064 hint = max( hint, delegate.sizeHint( viewOptions, index ).width() )
00065 except Rpc.RpcException, e:
00066 hint = 100
00067 QApplication.restoreOverrideCursor()
00068 return hint
00069
00070 def moveCursor( self, action, modifiers ):
00071 index = self.currentIndex()
00072 if not index.isValid():
00073 return QModelIndex()
00074 row = index.row()
00075 column = index.column()
00076 lastRow = index.model().rowCount() - 1
00077 lastColumn = index.model().columnCount() - 1
00078 if action == QAbstractItemView.MoveUp:
00079 row -= 1
00080 elif action == QAbstractItemView.MoveDown:
00081 row += 1
00082 elif action == QAbstractItemView.MoveLeft:
00083 column -= 1
00084 elif action == QAbstractItemView.MoveRight:
00085 column += 1
00086 elif action == QAbstractItemView.MoveHome:
00087 if column == 0:
00088 row = 0
00089 else:
00090 column = 0
00091 elif action == QAbstractItemView.MoveEnd:
00092 if column == lastColumn:
00093 row = lastRow
00094 else:
00095 column = lastColumn
00096 elif action == QAbstractItemView.MovePageUp:
00097 row -= 12
00098 elif action == QAbstractItemView.MovePageDown:
00099 row += 12
00100 elif action == QAbstractItemView.MoveNext:
00101 column += 1
00102 elif action == QAbstractItemView.MovePrevious:
00103 column -= 1
00104
00105 if column > lastColumn:
00106 column = 0
00107 row += 1
00108 elif column < 0:
00109 column = lastColumn
00110 row -= 1
00111 if row < 0:
00112 column = 0
00113 row = 0
00114 elif row > lastRow:
00115 row = lastRow
00116 column = 0
00117 return self.model().createIndex( row, column, index.internalPointer() )
00118
00119 class TreeView( AbstractView ):
00120
00121 def __init__(self, parent, widgetType='tree'):
00122 AbstractView.__init__(self, parent)
00123 self._widgetType = widgetType
00124 self.treeModel = None
00125 self.reload = False
00126 self.title=""
00127 self.selecting = False
00128 self.setAddOnTop( False )
00129
00130 if self._widgetType == 'tree':
00131 self.widget = KooTreeView( self )
00132 self.widget.setAllColumnsShowFocus( False )
00133 self.widget.setSortingEnabled(True)
00134 self.widget.setRootIsDecorated( False )
00135 self.widget.setAlternatingRowColors( True )
00136 self.widget.setVerticalScrollMode( QAbstractItemView.ScrollPerItem )
00137 self.widget.sortByColumn( 0, Qt.AscendingOrder )
00138 self.widget.setDragEnabled( True )
00139 self.widget.setAcceptDrops( True )
00140 self.widget.setDropIndicatorShown( True );
00141 self.widget.setDragDropMode( QAbstractItemView.InternalMove )
00142
00143
00144
00145
00146
00147
00148
00149
00150 self.widget.setUniformRowHeights( True )
00151 elif self._widgetType == 'list':
00152 self.widget = QListView( self )
00153 self.widget.setViewMode( QListView.IconMode )
00154 self.widget.setFlow( QListView.LeftToRight )
00155 self.widget.setGridSize( QSize( 200, 20 ) )
00156 self.widget.setResizeMode( QListView.Adjust )
00157 elif self._widgetType == 'table':
00158 self._widgetType = 'table'
00159 self.widget = QTableView( self )
00160
00161
00162
00163 self.setFocusProxy( self.widget )
00164
00165
00166 self.aggregates = []
00167 self.aggregatesContainer = QWidget( self )
00168 self.aggregatesLayout = QHBoxLayout( self.aggregatesContainer )
00169 self.aggregatesLayout.setContentsMargins( 0, 0, 0, 0 )
00170
00171 self.setAllowMultipleSelection(True)
00172
00173 self.connect( self.widget, SIGNAL('activated(QModelIndex)'), self.activated )
00174
00175 self.currentRecord = None
00176
00177 layout = QVBoxLayout()
00178 layout.setContentsMargins(0, 0, 0, 0)
00179 layout.addWidget( self.widget )
00180 layout.addWidget( self.aggregatesContainer )
00181 self.setLayout( layout )
00182 self._readOnly = True
00183
00184 def sizeHint(self):
00185 return QSize(10, 10)
00186
00187 def viewType(self):
00188 return 'tree'
00189
00190 def setGridWidth( self, width ):
00191 if self._widgetType == 'list':
00192 self.widget.setGridSize( QSize( width, self.widget.gridSize().height() ) )
00193
00194 def setGridHeight( self, height ):
00195 if self._widgetType == 'list':
00196 self.widget.setGridSize( QSize( self.widget.gridSize().width(), height ) )
00197
00198 def setModel( self, model ):
00199 self.currentRecord = None
00200 self.treeModel = model
00201 self.widget.setModel( self.treeModel )
00202 self.connect( self.widget.selectionModel(),SIGNAL('currentChanged(QModelIndex, QModelIndex)'),self.currentChanged)
00203 self.connect( self.treeModel, SIGNAL('rowsInserted(const QModelIndex &,int,int)'), self.updateAggregates )
00204 self.connect( self.treeModel, SIGNAL('rowsRemoved(const QModelIndex &,int,int)'), self.updateAggregates )
00205 self.connect( self.treeModel, SIGNAL('modelReset()'), self.updateAggregates )
00206 self.connect( self.treeModel.recordGroup(), SIGNAL('sorting'), self.sorting )
00207
00208 def sorting(self, value):
00209 if value == RecordGroup.SortingNotPossible:
00210 self.emit( SIGNAL('statusMessage(QString)'), _("<font color='red'>Sorting not possible.</font>") )
00211 elif value == RecordGroup.SortingOnlyGroups:
00212 self.emit( SIGNAL('statusMessage(QString)'), _("<font color='red'>Sorting only groups.</font>") )
00213 elif value == RecordGroup.SortingNotPossibleModified:
00214 self.emit( SIGNAL('statusMessage(QString)'), _("<font color='red'>Save changes before sorting.</font>") )
00215 else:
00216 self.emit( SIGNAL('statusMessage(QString)'), '' )
00217
00218 def addAggregate( self, name, label, bold, digits ):
00219 aggLabel = QLabel( label + ':', self.aggregatesContainer )
00220 aggLabel.setSizePolicy( QSizePolicy.Fixed, QSizePolicy.Fixed )
00221 aggValue = QLabel( self.aggregatesContainer )
00222 aggValue.setSizePolicy( QSizePolicy.Fixed, QSizePolicy.Fixed )
00223 if bold:
00224 font = QFont()
00225 font.setBold( True )
00226 aggLabel.setFont( font )
00227 aggValue.setFont( font )
00228 self.aggregatesLayout.addWidget( aggLabel )
00229 self.aggregatesLayout.addWidget( aggValue )
00230 self.aggregatesLayout.addSpacing( 10 )
00231 self.aggregates.append( { 'name': name, 'widget': aggValue, 'digits': digits } )
00232
00233 def finishAggregates(self):
00234 self.aggregatesLayout.addSpacing( 10 )
00235
00236
00237
00238 self.uiUpdateAggregates = QLabel( _('<a href="update">Update totals</a>'), self.aggregatesContainer )
00239 self.uiUpdateAggregates.setSizePolicy( QSizePolicy.Fixed, QSizePolicy.Fixed )
00240 self.connect( self.uiUpdateAggregates, SIGNAL('linkActivated(QString)'), self.forceAggregatesUpdate )
00241 self.aggregatesLayout.addWidget( self.uiUpdateAggregates )
00242 self.aggregatesLayout.addStretch( 0 )
00243
00244
00245 def forceAggregatesUpdate(self, url):
00246 QApplication.setOverrideCursor( Qt.WaitCursor )
00247 try:
00248 self.treeModel.group.ensureAllLoaded()
00249 self.updateAggregates()
00250 except Rpc.RpcException, e:
00251 pass
00252 QApplication.restoreOverrideCursor()
00253
00254
00255
00256
00257
00258 def activated(self, index):
00259 if self._readOnly:
00260 self.emit( SIGNAL('activated()') )
00261
00262 def currentChanged(self, current, previous):
00263 if self.selecting:
00264 return
00265 self.currentRecord = self.treeModel.recordFromIndex( current )
00266
00267
00268 self.emit( SIGNAL("currentChanged(PyQt_PyObject)"), self.currentRecord )
00269 self.updateAggregates()
00270
00271 def store(self):
00272
00273
00274
00275
00276
00277 self.widget.openPersistentEditor( self.widget.currentIndex() )
00278 self.widget.closePersistentEditor( self.widget.currentIndex() )
00279
00280 def reset(self):
00281 pass
00282
00283 def setSelected(self, record):
00284 if self.currentRecord == record:
00285 return
00286 self.currentRecord = record
00287 idx = self.treeModel.indexFromRecord( record )
00288 if idx != None:
00289 self.selecting = True
00290 self.widget.selectionModel().setCurrentIndex( idx, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
00291 self.selecting = False
00292
00293 def display(self, currentRecord, recordGroup ):
00294
00295 self.treeModel.setRecordGroup( recordGroup )
00296 if self._widgetType != 'tree':
00297 self.treeModel.sort( 0, Qt.AscendingOrder )
00298 self.updateAggregates()
00299 if currentRecord:
00300 idx = self.treeModel.indexFromRecord( currentRecord )
00301 if idx:
00302 self.widget.setCurrentIndex( idx )
00303 self.widget.selectionModel().select( self.widget.currentIndex(), QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows )
00304
00305
00306 def selectFirst(self):
00307 index = self.treeModel.index( 0, 0 )
00308 if not index.isValid():
00309 return
00310 self.widget.setCurrentIndex( index )
00311 self.widget.selectionModel().select( self.widget.currentIndex(), QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows )
00312
00313
00314 def selectAll(self):
00315 start = self.treeModel.index( 0, 0 )
00316 end = self.treeModel.index( self.treeModel.rowCount() - 1, 0 )
00317
00318
00319
00320
00321
00322 self.selectFirst()
00323 selection = QItemSelection( start, end )
00324 self.widget.selectionModel().select( selection, QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows )
00325
00326 def selectedRecords(self):
00327 records = []
00328 selectedIndexes = self.widget.selectionModel().selectedRows()
00329 for i in selectedIndexes:
00330 record = self.treeModel.recordFromIndex( i )
00331
00332
00333 if record:
00334 records.append( self.treeModel.recordFromIndex( i ) )
00335 return records
00336
00337 def setAllowMultipleSelection(self, value):
00338 if value:
00339 self.widget.setSelectionMode( QAbstractItemView.ExtendedSelection )
00340 else:
00341 self.widget.setSelectionMode( QAbstractItemView.SingleSelection )
00342
00343 def setReadOnly(self, value):
00344
00345 if not self.treeModel:
00346 return
00347 if self.treeModel.isReadOnly():
00348 return
00349 self._readOnly = value
00350 if self._readOnly:
00351 self.widget.setEditTriggers( QAbstractItemView.NoEditTriggers )
00352 self.widget.setTabKeyNavigation( False )
00353 else:
00354 self.widget.setEditTriggers( QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed | QAbstractItemView.AnyKeyPressed | QAbstractItemView.SelectedClicked )
00355
00356
00357 self.widget.setTabKeyNavigation( True )
00358
00359 def isReadOnly(self):
00360 return self._readOnly
00361
00362
00363
00364
00365 def updateAggregates(self):
00366 if self.treeModel.group and self.aggregates:
00367 if self.treeModel.group.unloadedIds():
00368 self.uiUpdateAggregates.show()
00369 calculate = False
00370 else:
00371 self.uiUpdateAggregates.hide()
00372 calculate = True
00373 else:
00374 calculate = False
00375 self.uiUpdateAggregates.hide()
00376
00377 for agg in self.aggregates:
00378
00379
00380 if calculate:
00381 value = 0.0
00382 for model in self.treeModel.group:
00383 value += model.value(agg['name'])
00384 agg['widget'].setText( Numeric.floatToText( value, agg['digits'], True ) )
00385 else:
00386 agg['widget'].setText( '-' )
00387
00388 def startEditing(self):
00389 self.widget.edit( self.widget.currentIndex() )
00390
00391 def addOnTop(self):
00392 return self._addOnTop
00393
00394 def setAddOnTop(self, add):
00395 self._addOnTop = add
00396
00397 def viewSettings(self):
00398 if self._widgetType in ('list','table'):
00399 return ''
00400 header = self.widget.header()
00401 return str( header.saveState().toBase64() )
00402
00403 def setViewSettings(self, settings):
00404 if not settings or self._widgetType in ('list','table'):
00405 return
00406 header = self.widget.header()
00407 header.restoreState( QByteArray.fromBase64( settings ) )
00408