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