wxSchedulerPaint.py :  » Project-Management » Task-Coach » TaskCoach-1.0.3 » taskcoachlib » thirdparty » calendar » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Project Management » Task Coach 
Task Coach » TaskCoach 1.0.3 » taskcoachlib » thirdparty » calendar » wxSchedulerPaint.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from wxSchedule import wxSchedule
from wxDrawer import wxBaseDrawer,wxFancyDrawer
from wxSchedulerCore import *
import calendar
import math
import sys
import wx
import wxScheduleUtils as utils

if sys.version.startswith( "2.3" ):
  from sets import Set


# Events 
wxEVT_COMMAND_SCHEDULE_ACTIVATED = wx.NewEventType()
EVT_SCHEDULE_ACTIVATED = wx.PyEventBinder( wxEVT_COMMAND_SCHEDULE_ACTIVATED )

wxEVT_COMMAND_SCHEDULE_RIGHT_CLICK = wx.NewEventType()
EVT_SCHEDULE_RIGHT_CLICK = wx.PyEventBinder( wxEVT_COMMAND_SCHEDULE_RIGHT_CLICK )

wxEVT_COMMAND_SCHEDULE_DCLICK = wx.NewEventType()
EVT_SCHEDULE_DCLICK = wx.PyEventBinder( wxEVT_COMMAND_SCHEDULE_DCLICK )


class wxSchedulerSizer(wx.PySizer):
  def __init__(self, minSizeCallback):
    super(wxSchedulerSizer, self).__init__()

    self._minSizeCallback = minSizeCallback

  def CalcMin(self):
    return self._minSizeCallback()


# Main class
class wxSchedulerPaint( object ):
  
  def __init__( self, *args, **kwds ):
    super( wxSchedulerPaint, self ).__init__( *args, **kwds )

    self._resizable    = False
    self._style = wxSCHEDULER_VERTICAL

    self._drawerClass = wxBaseDrawer
    #self._drawerClass = wxFancyDrawer

    self._bitmap = None
    self._minSize = None

    if isinstance(self, wx.ScrolledWindow):
      self.SetSizer(wxSchedulerSizer(self.CalcMinSize))

  def _doClickControl( self, point ):
    self._processEvt( wxEVT_COMMAND_SCHEDULE_ACTIVATED, point )

  def _doRightClickControl( self, point ):
    self._processEvt( wxEVT_COMMAND_SCHEDULE_RIGHT_CLICK, point )
    
  def _doDClickControl( self, point ):
    self._processEvt( wxEVT_COMMAND_SCHEDULE_DCLICK, point )

  def _findSchedule( self, point ):
    """
    Check if the point is on a schedule and return the schedule
    """
    for schedule, pointMin, pointMax in self._schedulesCoords:
      inX = ( pointMin.x <= point.x ) & ( point.x <= pointMax.x )
      inY = ( pointMin.y <= point.y ) & ( point.y <= pointMax.y )
      
      if inX & inY:
        return schedule.GetClientData()

    for dt, pointMin, pointMax in self._datetimeCoords:
      inX = ( pointMin.x <= point.x ) & ( point.x <= pointMax.x )
      inY = ( pointMin.y <= point.y ) & ( point.y <= pointMax.y )
      
      if inX & inY:
        return dt


  def _getSchedInPeriod( schedules, start, end):
    """
    Returns a list of copied schedules that intersect with
    the  period  defined by   'start'  and 'end'.  Schedule
    start and end are trimmed so as to lie between 'start'
    and 'end'.
    """
    results = []

    for schedule in schedules:
      schedule.bounds = None

      if schedule.start.IsLaterThan(end):
        continue
                        if start.IsLaterThan(schedule.end):
        continue

      newSchedule = schedule.Clone()
      # This is used to find the original schedule object in _findSchedule.
      newSchedule.clientdata  = schedule

      if start.IsLaterThan(schedule.start):
        newSchedule.start = utils.copyDateTime(start)
      if schedule.end.IsLaterThan(end):
        newSchedule.end = utils.copyDateTime(end)

                        results.append(newSchedule)

    return results

  _getSchedInPeriod = staticmethod(_getSchedInPeriod)

  def _splitSchedules( self, schedules ):
    """
    Returns   a list   of lists  of schedules.  Schedules in
    each list are guaranteed not to collide.
    """
    results = []
    current = []

    schedules = schedules[:] # Don't alter original list
##     def compare(a, b):
##       if a.start.IsEqualTo(b.start):
##         return cmp(a.description, b.description)
##       if a.start.IsEarlierThan(b.start):
##         return -1
##       return 1
##     schedules.sort(compare)

    def findNext(schedule):
      # Among schedules that start after this one ends, find the "nearest".
      candidateSchedule = None
      minDelta = None
      for sched in schedules:
        if sched.start.IsLaterThan(schedule.end):
          delta = sched.start.Subtract(schedule.end)
          if minDelta is None or minDelta > delta:
            minDelta = delta
            candidateSchedule = sched
      return candidateSchedule

    while schedules:
      schedule = schedules[0]
      while schedule:
        current.append(schedule)
        schedules.remove(schedule)
        schedule = findNext(schedule)
      results.append(current)
      current = []

    return results

  def _paintPeriod(self, drawer, start, daysCount, x, y, width, height):
    end = utils.copyDateTime(start)
    end.AddDS(wx.DateSpan(days=daysCount))

    blocks = self._splitSchedules(self._getSchedInPeriod(self._schedules, start, end))
    offsetY = 0

    if blocks:
      dayWidth = width / len(blocks)

      if self._showOnlyWorkHour:
        workingHours = [(self._startingHour, self._startingPauseHour),
            (self._endingPauseHour, self._endingHour)]
      else:
        workingHours = [(self._startingHour, self._endingHour)]

      for idx, block in enumerate(blocks):
        maxDY = 0

        for schedule in block:
          if self._style == wxSCHEDULER_VERTICAL:
            xx, yy, w, h = drawer.DrawScheduleVertical(schedule, start, workingHours,
                         x + dayWidth * idx, y,
                         dayWidth, height)
          elif self._style == wxSCHEDULER_HORIZONTAL:
            xx, yy, w, h = drawer.DrawScheduleHorizontal(schedule, start, daysCount, workingHours,
                           x, y + offsetY, width, height)
            maxDY = max(maxDY, h)

          self._schedulesCoords.append((schedule, wx.Point(xx, yy), wx.Point(xx + w, yy + h)))

        offsetY += maxDY

    for dayN in xrange(daysCount):
      theDay = utils.copyDateTime(start)
      theDay.AddDS(wx.DateSpan(days=dayN))
      theDay.SetSecond(0)

      nbHours = len(self._lstDisplayedHours)

      for idx, hour in enumerate(self._lstDisplayedHours):
        theDay.SetHour(hour.GetHour())
        theDay.SetMinute(hour.GetMinute())

        if self._style == wxSCHEDULER_VERTICAL:
          self._datetimeCoords.append((utils.copyDateTime(theDay),
                     wx.Point(x + 1.0 * width * dayN / daysCount,
                        y + 1.0 * height * idx / nbHours),
                     wx.Point(x + 1.0 * width * (dayN + 1) / daysCount,
                        y + 1.0 * height * (idx + 1) / nbHours)))
        else:
          self._datetimeCoords.append((utils.copyDateTime(theDay),
                     wx.Point(x + 1.0 * width * (nbHours * dayN + idx) / (nbHours * daysCount),
                        y),
                     wx.Point(x + 1.0 * width * (nbHours * dayN + idx + 1) / (nbHours * daysCount),
                        y + height)))

    if self._style == wxSCHEDULER_VERTICAL:
      return max(width, DAY_SIZE_MIN.width), max(height, DAY_SIZE_MIN.height)
    else:
      return max(width, DAY_SIZE_MIN.width), offsetY

  def _paintDay( self, drawer, day, x, y, width, height ):
    """
    Draw column schedules
    """

    start = utils.copyDateTime(day)
    start.SetHour(0)
    start.SetMinute(0)
    start.SetSecond(0)

    return self._paintPeriod(drawer, start, 1, x, y, width, height)

  def _paintDaily( self, drawer, day, x, y, width, height ):
    """
    Display day schedules
    """

    minWidth = minHeight = 0

    if self._style == wxSCHEDULER_VERTICAL:
      x += LEFT_COLUMN_SIZE
      width -= LEFT_COLUMN_SIZE

    w, h = drawer.DrawDayHeader(day, x, y, width, height)
    minHeight += h
    y += h
    height -= h

    if self._style == wxSCHEDULER_VERTICAL:
      x -= LEFT_COLUMN_SIZE
      width += LEFT_COLUMN_SIZE

    w, h = drawer.DrawHours(x, y, width, height, self._style)

    if self._style == wxSCHEDULER_VERTICAL:
      minWidth += w
      x += w
      width -= w
    else:
      minHeight += h
      y += h
      height -= h

    w, h = self._paintDay( drawer, day, x, y, width, height )

    minWidth += w
    minHeight += h

    return minWidth, minHeight

  def _paintWeekly( self, drawer, day, x, y, width, height ):
    """
    Display weekly schedule
    """

    firstDay = utils.setToWeekDayInSameWeek( day, 0, self._weekstart )
    firstDay.SetHour(0)
    firstDay.SetMinute(0)
    firstDay.SetSecond(0)

    minWidth = minHeight = 0

    if self._style == wxSCHEDULER_VERTICAL:
      x += LEFT_COLUMN_SIZE
      width -= LEFT_COLUMN_SIZE

    maxDY = 0
    for weekday in xrange(7):
      theDay = utils.setToWeekDayInSameWeek(utils.copyDateTime(firstDay), weekday, self._weekstart)
      w, h = drawer.DrawDayHeader(theDay, x + weekday * 1.0 * width / 7, y, 1.0 * width / 7, height,
                highlight=theDay.IsSameDate(wx.DateTime.Now()))
      maxDY = max(maxDY, h)

    if self._style == wxSCHEDULER_VERTICAL:
      x -= LEFT_COLUMN_SIZE
      width += LEFT_COLUMN_SIZE

    minHeight += maxDY
    y += maxDY
    height -= maxDY

    if self._style == wxSCHEDULER_VERTICAL:
      w, h = drawer.DrawHours(x, y, width, height, self._style)

      minWidth += w
      x += w
      width -= w

      for weekday in xrange(7):
        theDay = utils.setToWeekDayInSameWeek(utils.copyDateTime(firstDay), weekday, self._weekstart)
        self._paintDay(drawer, theDay, x + weekday * 1.0 * width / 7, y, 1.0 * width / 7, height)

      return max(WEEK_SIZE_MIN.width, width), max(WEEK_SIZE_MIN.height, height)
    else:
      w, h = self._paintPeriod(drawer, firstDay, 7, x, y, width, height)

      minWidth += w
      minHeight += h

      return max(WEEK_SIZE_MIN.width, minWidth), minHeight

  def _paintMonthly( self, drawer, day, x, y, width, height):
    """
    Draw month's calendar using calendar module functions
    """

    w, h = drawer.DrawMonthHeader(day, x, y, width, height)
    y += h
    height -= h

    if self._style == wxSCHEDULER_VERTICAL:
      month = calendar.monthcalendar( day.GetYear(), day.GetMonth() + 1 )

      for w, monthWeek in enumerate( month ):
        for d, monthDay in enumerate( monthWeek ):
          cellW, cellH = 1.0 * width / 7, 1.0 * height / len(month)

          if monthDay == 0:
            theDay = None
            schedules = []
          else:
            theDay = day
            theDay.SetDay(monthDay)
            theDay.SetHour(0)
            theDay.SetMinute(0)
            theDay.SetSecond(0)

            end = utils.copyDateTime(theDay)
            end.AddDS(wx.DateSpan(days=1))

            schedules = self._getSchedInPeriod(self._schedules, theDay, end)

            self._datetimeCoords.append((utils.copyDateTime(theDay),
                       wx.Point(d * cellW, w * cellH),
                       wx.Point(d * cellW + cellW, w * cellH + cellH)))

          self._schedulesCoords.extend(drawer.DrawSchedulesCompact(theDay, schedules, d * cellW, w * cellH, cellW, cellH))

      return (max(MONTH_CELL_SIZE_MIN.width * 7, width),
        max(MONTH_CELL_SIZE_MIN.height * (w + 1), height))
    else:
      day.SetDay(1)
      day.SetHour(0)
      day.SetMinute(0)
      day.SetSecond(0)

      minHeight = h

      daysCount = wx.DateTime.GetNumberOfDaysInMonth(day.GetMonth())

      maxDY = 0
      for idx in xrange(daysCount):
        theDay = utils.copyDateTime(day)
        theDay.AddDS(wx.DateSpan(days=idx))
        w, h = drawer.DrawSimpleDayHeader(theDay, x + 1.0 * idx * width / daysCount,
                  y, 1.0 * width / daysCount, height,
                  theDay.IsSameDate(wx.DateTime.Now()))
        maxDY = max(maxDY, h)

      y += maxDY
      height -= maxDY
      minHeight += maxDY

      w, h = self._paintPeriod(drawer, day, daysCount, x, y, width, height)
      minHeight += h

      return w, minHeight

  def _processEvt( self, commandEvent, point ):
    """ 
    Process the command event passed at the given point
    """
    evt = wx.PyCommandEvent( commandEvent )
    sch = self._findSchedule( point )
    if isinstance( sch, wxSchedule ):
      mySch = sch
      myDate = None
    else:
      mySch = None
      myDate = sch
    
    evt.schedule = mySch
    evt.date = myDate
    evt.SetEventObject( self )
    self.ProcessEvent( evt ) 

  def DoPaint(self, drawer, x, y, width, height):
    self._schedulesCoords = list()
    self._datetimeCoords = list()

    day = utils.copyDate(self.GetDate())

    if self._viewType == wxSCHEDULER_DAILY:
      return self._paintDaily(drawer, day, x, y, width, height)
    elif self._viewType == wxSCHEDULER_WEEKLY:
      return self._paintWeekly(drawer, day, x, y, width, height)
    elif self._viewType == wxSCHEDULER_MONTHLY:
      return self._paintMonthly(drawer, day, x, y, width, height)

  def GetViewSize(self):
    # Used by wxSchedulerReport

    size = self.GetSize()
    minSize = self.CalcMinSize()

    return wx.Size(max(size.width, minSize.width), max(size.height, minSize.height))

  def _CalcMinSize(self):
    if self._viewType == wxSCHEDULER_DAILY:
      minW, minH = DAY_SIZE_MIN.width, DAY_SIZE_MIN.height
    elif self._viewType == wxSCHEDULER_WEEKLY:
      minW, minH = WEEK_SIZE_MIN.width, WEEK_SIZE_MIN.height
    elif self._viewType == wxSCHEDULER_MONTHLY:
      minW, minH = MONTH_CELL_SIZE_MIN.width * 7, 0 # will be computed

    if self._viewType == wxSCHEDULER_MONTHLY or self._style == wxSCHEDULER_HORIZONTAL:
      memDC = wx.MemoryDC()
      bmp = wx.EmptyBitmap(1, 1)
      memDC.SelectObject(bmp)
      try:
        if self._drawerClass.use_gc:
          context = wx.GraphicsContext.Create(memDC)
          context.SetFont(wx.NORMAL_FONT, wx.BLACK)
        else:
          context = memDC

        if isinstance(self, wx.ScrolledWindow):
          size = self.GetVirtualSize()
        else:
          size = self.GetSize()

        # XXXFIXME: find a better way not to alter coordinates...
        tmpCoords = self._datetimeCoords[:]

        # Actually, only the min height may vary...
        _, minH = self.DoPaint(self._drawerClass(context, self._lstDisplayedHours),
                   0, 0, size.GetWidth(), 0)

        self._datetimeCoords = tmpCoords
      finally:
        memDC.SelectObject(wx.NullBitmap)

    return wx.Size(minW, minH)

  def CalcMinSize(self):
    if self._minSize is None:
      self._minSize = self._CalcMinSize()
    return self._minSize

  def InvalidateMinSize(self):
    self._minSize = None

  def DrawBuffer( self ):
    if isinstance(self, wx.ScrolledWindow):
      size = self.GetVirtualSize()
    else:
      size = self.GetSize()

    self._bitmap = wx.EmptyBitmap(size.GetWidth(), size.GetHeight())
    memDC = wx.MemoryDC()
    memDC.SelectObject(self._bitmap)
    try:
      memDC.BeginDrawing()
      try:
        memDC.SetBackground( wx.Brush( SCHEDULER_BACKGROUND_BRUSH ) )
        memDC.SetPen( FOREGROUND_PEN )
        memDC.Clear()
        memDC.SetFont(wx.NORMAL_FONT)

        if self._drawerClass.use_gc:
          context = wx.GraphicsContext.Create(memDC)
        else:
          context = memDC

        width, height = self.DoPaint(self._drawerClass(context, self._lstDisplayedHours),
                   0, 0, size.GetWidth(), size.GetHeight())
      finally:
        memDC.EndDrawing()
    finally:
      memDC.SelectObject(wx.NullBitmap)

    # Bad things may happen here from time to time.
    if isinstance(self, wx.ScrolledWindow):
      if int(width) > size.GetWidth() or int(height) > size.GetHeight():
        self.SetVirtualSize(wx.Size(int(width), int(height)))
        self.DrawBuffer()

  def RefreshSchedule( self, schedule ):
    if schedule.bounds is not None:
      memDC = wx.MemoryDC()
      memDC.SelectObject(self._bitmap)
      try:
        memDC.BeginDrawing()
        memDC.SetBackground( wx.Brush( SCHEDULER_BACKGROUND_BRUSH ) )
        memDC.SetPen( FOREGROUND_PEN )
        memDC.SetFont(wx.NORMAL_FONT)

        if self._drawerClass.use_gc:
          context = wx.GraphicsContext.Create(memDC)
        else:
          context = memDC

        self._drawerClass(context, self._lstDisplayedHours)._DrawSchedule(schedule, *schedule.bounds)
      finally:
        memDC.SelectObject(wx.NullBitmap)

      originX, originY = self.GetViewStart()
      unitX, unitY = self.GetScrollPixelsPerUnit()
      x, y, w, h = schedule.bounds
      self.RefreshRect(wx.Rect(math.floor(x - originX * unitX) - 1, math.floor(y - originY * unitY) - 1,
             math.ceil(w) + 2, math.ceil(h) + 2))

  def OnPaint( self, evt = None ):
    # Do the draw

    if self._dc:
      dc = self._dc
    else:
      dc = wx.PaintDC(self)
      self.PrepareDC(dc)

    dc.BeginDrawing()
    try:
      dc.DrawBitmap(self._bitmap, 0, 0, False)
    finally:
      dc.EndDrawing()

  def SetResizable( self, value ):
    """
    Draw proportionally of actual space but not down on minimun sizes
    The actual sze is retrieved by GetSize() method of derived object
    """
    self._resizable = bool( value )

  def SetStyle(self, style):
    """
    Sets  the drawing  style.  Values  for 'style'  may be
    wxSCHEDULER_VERTICAL     (the        default)      or
    wxSCHEDULER_HORIZONTAL.
    """
    self._style = style
    self.InvalidateMinSize()
    self.Refresh()

  def GetStyle( self ):
    """
    Returns the current drawing style.
    """
    return self._style

  def SetDrawer(self, drawerClass):
    """
    Sets the drawer class.
    """
    self._drawerClass = drawerClass
    self.InvalidateMinSize()
    self.Refresh()

  def GetDrawer(self):
    return self._drawerClass
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.