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 from PyQt4.QtCore import *
00029 from PyQt4.QtGui import *
00030 from PyQt4.uic import *
00031 from Koo.Common import Common
00032 from Koo.Common import Calendar
00033 from Koo.View.AbstractView import *
00034 from Koo.KooChart import ColorManager
00035 import math
00036
00037
00038 class GraphicsTaskItem( QGraphicsRectItem ):
00039
00040 def __init__(self, parent=None):
00041 QGraphicsRectItem.__init__(self, parent)
00042 self.setFlag( QGraphicsItem.ItemClipsChildrenToShape, True )
00043 self._text = QGraphicsTextItem( self )
00044 self._text.setZValue( 1 )
00045 self.setSize( QSize(100, 100) )
00046 self._index = None
00047 self._start = ''
00048 self._duration = ''
00049 self._backgroundColor = QColor( 200, 250, 200 )
00050 self._edgeColor = QColor( 100, 200, 100 )
00051 self.setActive( False )
00052
00053 def setBackgroundColor(self, color):
00054 self._backgroundColor = color
00055 self.updateColors()
00056
00057 def setEdgeColor(self, color):
00058 self._edgeColor = color
00059 self.updateColors()
00060
00061 def setActive(self, active):
00062 self._active = active
00063 self.updateColors()
00064
00065 def isActive(self):
00066 return self_active
00067
00068 def setSize(self, size):
00069 self._size = size
00070 self._text.setTextWidth( self._size.width() )
00071 self.setRect( 0, 0, size.width(), size.height() )
00072
00073 def setTitle(self, title):
00074 self._title = title
00075 self._text.setPlainText( title )
00076 self.updateToolTip()
00077
00078 def setIndex(self, index):
00079 self._index = index
00080
00081 def index(self):
00082 return self._index
00083
00084 def updateColors(self):
00085 if self._active:
00086 self.setBrush( QBrush( QColor( 250, 200, 200 ) ) )
00087 self.setPen( QPen( QColor( 200, 100, 100 ) ) )
00088 else:
00089 self.setBrush( self._backgroundColor )
00090 self.setPen( self._edgeColor )
00091
00092 def updateToolTip(self):
00093 text = _('<b>%(title)s</b><br/><b>Start:</b> %(start)s<br/><b>Duration:</b> %(duration)s<br/>') % {
00094 'title': self._title,
00095 'start': self._start,
00096 'duration': self._duration
00097 }
00098 self.setToolTip( text )
00099
00100 def setStart(self, start):
00101 self._start = start
00102 self.updateToolTip()
00103
00104 def setDuration(self, duration):
00105 self._duration = duration
00106 self.updateToolTip()
00107
00108
00109 class GraphicsDayItem( QGraphicsItemGroup ):
00110 colorManager = ColorManager( 30 )
00111
00112 def __init__(self, parent=None):
00113 QGraphicsItemGroup.__init__(self, parent)
00114
00115 self._background = QGraphicsRectItem(self)
00116 self._background.setBrush( QBrush( QColor( 200, 200, 250) ) )
00117 self._background.setPen( QPen( QColor( 100, 100, 150) ) )
00118 self.addToGroup( self._background )
00119
00120 self._title = QGraphicsTextItem(self)
00121 self._title.setPos( 0, 0 )
00122 self._title.setZValue( 1 )
00123 font = QFont()
00124 font.setBold( True )
00125 self._title.setFont( font )
00126 self.addToGroup( self._title )
00127
00128 self._indexes = {}
00129 self._tasks = {}
00130
00131 self.setSize( QSize(100, 100) )
00132 self.setDate( QDate() )
00133
00134 def setSize(self, size):
00135 self._size = size
00136 self._background.setRect( QRectF( 0, 0, self._size.width(), self._size.height() ) )
00137 self._title.setTextWidth( self._size.width() )
00138
00139 def setDate(self, date):
00140 self._date = date
00141 self.updateData()
00142
00143 def date(self):
00144 return self._date
00145
00146 def addModelIndex(self, index):
00147 if index in self._tasks.keys():
00148 return
00149 task = GraphicsTaskItem( self )
00150 task.setZValue( 1 )
00151 task.setIndex( index )
00152 self.addToGroup( task )
00153 self._indexes[ task ] = index
00154 self._tasks[ index ] = task
00155 self.updateData()
00156
00157 def removeModelIndex(self, index):
00158 if not index in self._tasks.keys():
00159 return
00160 task = self._tasks[ index ]
00161 self.removeFromGroup( task )
00162 del self._tasks[ index ]
00163 del self._indexes[ task ]
00164 self.updateData()
00165
00166 def clear(self):
00167 self._indexes = {}
00168 self._tasks = {}
00169
00170 def updateData(self):
00171 title = '<center>%s</center>' % ( unicode(self._date.toString()) )
00172 self._title.setHtml( title )
00173 for index in self._tasks.keys():
00174 task = self._tasks[index]
00175 model = index.model()
00176 titleIdx = model.index( index.row(), self.parentItem()._modelTitleColumn )
00177 dateIdx = model.index( index.row(), self.parentItem()._modelDateColumn )
00178 task.setTitle( titleIdx.data().toString() )
00179 task.setStart( dateIdx.data().toString() )
00180
00181 if self.parentItem()._modelColorColumn >= 0:
00182 colorIdx = model.index( index.row(), self.parentItem()._modelColorColumn )
00183 task.setBackgroundColor( GraphicsDayItem.colorManager.color( colorIdx.data().toInt()[0] ) )
00184 task.setEdgeColor( GraphicsDayItem.colorManager.edgeColor( colorIdx.data().toInt()[0] ) )
00185 if self.parentItem()._hasDurationColumn:
00186 durationIdx = model.index( index.row(), self.parentItem()._modelDurationColumn )
00187 task.setDuration( durationIdx.data().toString() )
00188 durationTime, ok = durationIdx.data( self.parentItem().ValueRole ).toDouble()
00189 else:
00190 task.setDuration( '--' )
00191 durationTime = 1.0
00192
00193 startTime = self.parentItem().dateTimeFromIndex( dateIdx ).time()
00194
00195 height = self._size.height()
00196
00197
00198 secs = QTime( 0, 0 ).secsTo( startTime )
00199 y = QTime( 0, 0 ).secsTo( startTime ) * height / 86400.0
00200 task.setPos( 0, y )
00201
00202
00203 secs = durationTime * 3600.0
00204 y = secs * height / 86400.0
00205 task.setSize( QSize( self._size.width(), y ) )
00206
00207 title = unicode( titleIdx.data().toString() )
00208
00209 def taskFromIndex(self, index):
00210 return self._tasks.get( index, None )
00211
00212
00213 class GraphicsCalendarItem( QGraphicsItemGroup ):
00214
00215 ValueRole = Qt.UserRole + 1
00216
00217 def __init__(self, parent=None):
00218 QGraphicsItemGroup.__init__(self, parent)
00219 self._size = QSize( 100, 100 )
00220 self._multiRow = True
00221 self._itemsPerRow = 7
00222 self._startDate = QDate.currentDate()
00223 self._endDate = QDate.currentDate()
00224 self._days = {}
00225 self._model = None
00226 self._modelDateColumn = 0
00227 self._modelTitleColumn = 0
00228 self._modelDurationColumn = 0
00229 self._modelColorColumn = -1
00230 self._hasDurationColumn = False
00231
00232 def setSize( self, size ):
00233 self._size = size
00234 self.updateCalendarView()
00235
00236 def size(self):
00237 return self._size
00238
00239 def setStartDate( self, date ):
00240 self._startDate = date
00241 self.updateCalendarView()
00242
00243 def startDate(self):
00244 return self._startDate
00245
00246 def setEndDate( self, date ):
00247 self._endDate = date
00248 self.updateCalendarView()
00249
00250 def endDate(self):
00251 return self._endDate
00252
00253 def daysCount(self):
00254 count = self._startDate.daysTo( self._endDate ) + 1
00255 if count < 0:
00256 return 0
00257 else:
00258 return count
00259
00260 def updateCalendarView(self):
00261 self.clear()
00262 if not self.daysCount():
00263 return
00264 daysPerRow = 7
00265 offset = self._startDate.dayOfWeek() - 1
00266 rows = math.ceil( ( offset + self.daysCount() ) / 7.0 )
00267 if rows == 1:
00268 offset = 0
00269 dayWidth = self._size.width() / min( daysPerRow, self.daysCount() )
00270 dayHeight = self._size.height() / rows
00271 date = self._startDate
00272 for x in range( self._startDate.daysTo( self._endDate ) + 1 ):
00273 item = GraphicsDayItem( self )
00274 item.setDate( date )
00275 item.setPos( (x+offset) % 7 * dayWidth, dayHeight * ( (x+offset) // 7 ) )
00276 item.setSize( QSize( dayWidth, dayHeight ) )
00277 self._days[ str(Calendar.dateToText( date )) ] = item
00278 date = date.addDays( 1 )
00279 self.updateCalendarData()
00280
00281 def extractDate(self, variant):
00282 if not variant:
00283 return QDate()
00284 if variant.type() == QVariant.Date:
00285 return variant.toDate()
00286 elif variant.type() == QVariant.DateTime:
00287 return variant.toDateTime().date()
00288 else:
00289 return QDate()
00290
00291 def updateCalendarData(self):
00292 if not self._model:
00293 return
00294 if self._modelDateColumn >= self._model.columnCount():
00295 return
00296 if self._modelTitleColumn >= self._model.columnCount():
00297 return
00298
00299 for x in self._days.values():
00300 x.clear()
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310 startField = self._model.field(self._modelDateColumn)
00311 durationField = self._model.field(self._modelDurationColumn)
00312 if startField == durationField:
00313 startDate = Calendar.dateToStorage( self.startDate() )
00314 else:
00315
00316
00317 startDate = Calendar.dateToStorage( self.startDate().addMonths(-1) )
00318 endDate = Calendar.dateToStorage( self.endDate() )
00319
00320 oldFilter = self._model.group.filter() or []
00321 newFilter = []
00322
00323 for x in oldFilter:
00324 if x[0] != startField:
00325 newFilter.append(x)
00326
00327 newFilter.extend([
00328 (startField, '>=', startDate),
00329 (startField, '<=', endDate),
00330 ])
00331 self._model.group.setFilter(newFilter)
00332
00333
00334 for x in range(self._model.rowCount()):
00335 idx = self._model.index( x, self._modelDateColumn )
00336 date = Calendar.dateToText( self.dateTimeFromIndex( idx ).date() )
00337 if date in self._days:
00338 idx = self._model.index( x, self._modelTitleColumn )
00339 self._days[date].addModelIndex( idx )
00340
00341
00342 self._model.group.setFilter(oldFilter)
00343
00344 def dateTimeFromIndex(self, idx):
00345 data = self._model.data( idx )
00346 if data.type() == QVariant.DateTime:
00347 return data.toDateTime()
00348 data = self._model.data( idx, self.ValueRole )
00349 if data.type() == QVariant.DateTime:
00350 return data.toDateTime()
00351
00352
00353 return QDateTime()
00354
00355 def clear(self):
00356 for item in self._days.keys():
00357 self._days[item].setParentItem( None )
00358 del self._days[item]
00359 self._days = {}
00360
00361 def setModel(self, model):
00362 self._model = model
00363 self.updateCalendarData()
00364
00365 def model(self):
00366 return self._model
00367
00368 def setModelDateColumn(self, column):
00369 self._modelDateColumn = column
00370 self.updateCalendarData()
00371
00372 def modelDateColumn(self):
00373 return self._modelDateColumn
00374
00375 def setModelTitleColumn(self, column):
00376 self._modelTitleColumn = column
00377 self.updateCalendarData()
00378
00379 def modelTitleColumn(self):
00380 return self._modelTitleColumn
00381
00382 def setModelDurationColumn(self, column):
00383 self._modelDurationColumn = column
00384 self._hasDurationColumn = True
00385 self.updateCalendarData()
00386
00387 def modelDurationColumn(self):
00388 return self._modelDurationColumn
00389
00390 def setModelColorColumn(self, column):
00391 self._modelColorColumn = column
00392 self.updateCalendarData()
00393
00394 def tasksFromIndex(self, index):
00395 tasks = []
00396 for item in self._days.values():
00397 task = item.taskFromIndex( index )
00398 if task:
00399 tasks.append( task )
00400 return tasks
00401
00402 class GraphicsCalendarScene( QGraphicsScene ):
00403 def __init__(self, parent=None):
00404 QGraphicsScene.__init__(self, parent)
00405 self._calendar = GraphicsCalendarItem()
00406 self.addItem( self._calendar )
00407 self._activeIndex = QModelIndex()
00408
00409 def mousePressEvent( self, event ):
00410 self.setActiveIndex( False )
00411 self._activeIndex = QModelIndex()
00412 for item in self.items( event.scenePos() ):
00413 if isinstance(item, GraphicsTaskItem ):
00414 self._activeIndex = item.index()
00415 break
00416 self.setActiveIndex( True )
00417 self.emit( SIGNAL("currentChanged(PyQt_PyObject)"), self._calendar.model().recordFromIndex(self._activeIndex) )
00418
00419 def setActiveIndex( self, active ):
00420 if not self._activeIndex.isValid():
00421 return
00422 for x in self._calendar.tasksFromIndex( self._activeIndex ):
00423 x.setActive( active )
00424
00425 def mouseDoubleClickEvent( self, event ):
00426 self.setActiveIndex( False )
00427 self._activeIndex = QModelIndex()
00428 for item in self.items( event.scenePos() ):
00429 if isinstance(item, GraphicsTaskItem ):
00430 self._activeIndex = item.index()
00431 break
00432 self.setActiveIndex( True )
00433 self.emit( SIGNAL("currentChanged(PyQt_PyObject)"), self._calendar.model().recordFromIndex(self._activeIndex) )
00434 self.emit( SIGNAL('activated()') )
00435
00436 def setSize(self, size):
00437 self._calendar.setSize( size )
00438
00439 def setModel(self, model):
00440 self._calendar.setModel( model )
00441
00442 def model(self, model):
00443 return self.model()
00444
00445 def setModelDateColumn(self, column):
00446 self._calendar.setModelDateColumn( column )
00447
00448 def setModelTitleColumn(self, column):
00449 self._calendar.setModelTitleColumn( column )
00450
00451 def setModelDurationColumn(self, column):
00452 self._calendar.setModelDurationColumn( column )
00453
00454 def setModelColorColumn(self, column):
00455 self._calendar.setModelColorColumn( column )
00456
00457 def updateData(self):
00458 self._calendar.updateCalendarData()
00459 self._calendar.updateCalendarView()
00460
00461 def setStartDate(self, date):
00462 self._calendar.setStartDate( date )
00463
00464 def setEndDate(self, date):
00465 self._calendar.setEndDate( date )
00466
00467
00468 class GraphicsCalendarView( QGraphicsView ):
00469 def __init__(self, parent=None):
00470 QGraphicsView.__init__(self, parent)
00471 self._model = None
00472 self.setScene( GraphicsCalendarScene(self) )
00473
00474 def resizeEvent(self, event):
00475 self.scene().setSize( QSize( self.size().width()-20, self.size().height()-20 ) )
00476
00477 def setModel(self, model):
00478 self.scene().setModel( model )
00479
00480 def model(self, model):
00481 return self.scene().model()
00482
00483 def setModelDateColumn(self, column):
00484 self.scene().setModelDateColumn( column )
00485
00486 def setModelTitleColumn(self, column):
00487 self.scene().setModelTitleColumn( column )
00488
00489 def setModelDurationColumn(self, column):
00490 self.scene().setModelDurationColumn( column )
00491
00492 def setModelColorColumn(self, column):
00493 self.scene().setModelColorColumn( column )
00494
00495 def updateData(self):
00496 self.scene().updateData()
00497
00498 def setStartDate(self, date):
00499 self.scene().setStartDate( date )
00500
00501 def setEndDate(self, date):
00502 self.scene().setEndDate( date )
00503
00504
00505 (CalendarViewUi, CalendarViewBase) = loadUiType( Common.uiPath('calendarview.ui') )
00506
00507 class CalendarView( AbstractView, CalendarViewUi ):
00508 def __init__(self, parent):
00509 AbstractView.__init__(self, parent)
00510 CalendarViewUi.__init__(self)
00511 self.setupUi( self )
00512 self.connect( self.calendarWidget, SIGNAL('selectionChanged()'), self.updateCalendarView )
00513 self.connect( self.pushMonthlyView, SIGNAL('clicked()'), self.updateCalendarView )
00514 self.connect( self.pushWeeklyView, SIGNAL('clicked()'), self.updateCalendarView )
00515 self.connect( self.pushDailyView, SIGNAL('clicked()'), self.updateCalendarView )
00516 self.setReadOnly( True )
00517 self.title = ""
00518 self.updateCalendarView()
00519 self.connect( self.calendarView.scene(), SIGNAL('currentChanged(PyQt_PyObject)'), self.currentChanged )
00520 self.connect( self.calendarView.scene(), SIGNAL('activated()'), self.activated )
00521
00522 def viewType(self):
00523 return 'calendar'
00524
00525 def setModel(self, model):
00526 self.calendarView.setModel( model )
00527
00528 def setModelDateColumn(self, column):
00529 self.calendarView.setModelDateColumn( column )
00530
00531 def setModelTitleColumn(self, column):
00532 self.calendarView.setModelTitleColumn( column )
00533
00534 def setModelDurationColumn(self, column):
00535 self.calendarView.setModelDurationColumn( column )
00536
00537 def setModelColorColumn(self, column):
00538 self.calendarView.setModelColorColumn( column )
00539
00540 def updateCalendarView(self):
00541 date = self.calendarWidget.selectedDate()
00542 if self.pushMonthlyView.isChecked():
00543 start = QDate( date.year(), date.month(), 1 )
00544 end = QDate( date.year(), date.month(), date.daysInMonth() )
00545 elif self.pushWeeklyView.isChecked():
00546 start = date.addDays( 1 - date.dayOfWeek() )
00547 end = date.addDays( 7 - date.dayOfWeek() )
00548 else:
00549 start = date
00550 end = date
00551 self.calendarView.setStartDate( start )
00552 self.calendarView.setEndDate( end )
00553
00554 def display(self, currentModel, models):
00555 self.calendarView.updateData()
00556
00557 def currentChanged(self, obj):
00558 self.emit( SIGNAL('currentChanged(PyQt_PyObject)'), obj )
00559
00560 def activated( self ):
00561 self.emit( SIGNAL('activated()') )
00562