1 #----------------------------------------------------------------------------
3 # Purpose: Calendar display control
5 # Author: Lorne White (email: lorne.white@telusplanet.net)
10 # Licence: wxWindows license
11 #----------------------------------------------------------------------------
13 from wxPython
.wx
import *
19 CalDays
= [6, 0, 1, 2, 3, 4, 5]
20 AbrWeekday
= {6:"Sun", 0:"Mon", 1:"Tue", 2:"Wed", 3:"Thu", 4:"Fri", 5:"Sat"}
23 BusCalDays
= [0, 1, 2, 3, 4, 5, 6]
25 # calendar drawing routing
32 monthlist
.append(name
)
36 def __init__(self
, parent
):
40 self
.scale
= parent
.scale
47 self
.num_auto
= TRUE
# auto scale of the cal number day size
48 self
.num_size
= 12 # default size of calendar if no auto size
49 self
.max_num_size
= 12 # maximum size for calendar number
51 self
.num_align_horz
= wxALIGN_CENTRE
# alignment of numbers
52 self
.num_align_vert
= wxALIGN_CENTRE
53 self
.num_indent_horz
= 0 # points indent from position, used to offset if not centered
54 self
.num_indent_vert
= 0
56 self
.week_auto
= TRUE
# auto scale of week font text
58 self
.max_week_size
= 12
60 self
.grid_color
= 'BLACK' # grid and selection colors
61 self
.back_color
= 'WHITE'
62 self
.sel_color
= 'RED'
64 self
.high_color
= 'LIGHT BLUE'
65 self
.border_color
= 'BLACK'
66 self
.week_color
= 'LIGHT GREY'
68 self
.week_font_color
= 'BLACK' # font colors
69 self
.day_font_color
= 'BLACK'
74 self
.hide_title
= FALSE
75 self
.hide_grid
= FALSE
76 self
.outer_border
= TRUE
79 self
.cal_week_scale
= 0.7
80 self
.show_weekend
= FALSE
81 self
.cal_type
= "NORMAL"
83 def SetWeekColor(self
, font_color
, week_color
): # set font and background color for week title
84 self
.week_font_color
= font_color
85 self
.week_color
= week_color
87 def SetSize(self
, size
):
88 self
.set_sizew
= size
.width
89 self
.set_sizeh
= size
.height
91 def InitValues(self
): # default dimensions of various elements of the calendar
94 self
.set_cy_st
= 0 # start position
97 self
.set_y_mrg
= 15 # start of vertical draw default
101 def SetPos(self
, xpos
, ypos
):
102 self
.set_cx_st
= xpos
103 self
.set_cy_st
= ypos
105 def SetMarg(self
, xmarg
, ymarg
):
106 self
.set_x_st
= xmarg
107 self
.set_y_st
= ymarg
108 self
.set_y_end
= ymarg
110 def InitScale(self
): # scale position values
111 self
.sizew
= self
.set_sizew
* self
.pwidth
112 self
.sizeh
= self
.set_sizeh
* self
.pheight
114 self
.cx_st
= self
.set_cx_st
* self
.pwidth
# draw start position
115 self
.cy_st
= self
.set_cy_st
* self
.pheight
117 self
.x_mrg
= self
.set_x_mrg
* self
.pwidth
# calendar draw margins
118 self
.y_mrg
= self
.set_y_mrg
* self
.pheight
119 self
.y_end
= self
.set_y_end
* self
.pheight
121 def DrawCal(self
, DC
, sel_lst
=[]):
126 if self
.hide_title
is FALSE
:
134 if self
.show_weekend
is TRUE
: # highlight weekend dates
137 self
.AddSelect(sel_lst
) # overrides the weekend highlight
139 self
.DrawSel() # highlighted days
143 def AddSelect(self
, list, cfont
=None, cbackgrd
= None):
145 cfont
= self
.sel_color
# font digit color
147 cbackgrd
= self
.high_color
# select background color
150 self
.cal_sel
[val
] = (cfont
, cbackgrd
)
152 def DrawBorder(self
): # draw border around the outside of the main display rectangle
153 brush
= wxBrush(wxNamedColour(self
.back_color
), wxSOLID
)
154 self
.DC
.SetBrush(brush
)
155 self
.DC
.SetPen(wxPen(wxNamedColour(self
.border_color
), 1))
157 if self
.outer_border
is TRUE
:
158 rect
= wxRect(self
.cx_st
, self
.cy_st
, self
.sizew
, self
.sizeh
) # full display window area
159 self
.DC
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, rect
.height
)
161 def DrawNumVal(self
):
164 # calculate the calendar days and offset position
166 def SetCal(self
, year
, month
):
167 self
.InitValues() # reset initial values
173 t
= Date(year
, month
, day
)
174 dow
= self
.dow
= t
.day_of_week
# start day in month
175 dim
= self
.dim
= t
.days_in_month
# number of days in month
176 if self
.cal_type
== "NORMAL":
181 self
.st_pos
= start_pos
184 for i
in range(start_pos
):
188 self
.cal
.append(str(i
))
192 def SetWeekEnd(self
, font_color
='BLACK', backgrd
= 'LIGHT GREY'):
193 date
= 6 - int(self
.dow
) # start day of first saturday
194 while date
<= self
.dim
:
195 self
.cal_sel
[date
] = (font_color
, backgrd
) # Saturday
198 self
.cal_sel
[date
] = (font_color
, backgrd
) # Sunday
203 def GetRect(self
): # get the display rectange list of the day grid
205 for y
in self
.gridy
[1:-1]:
206 for x
in self
.gridx
[:-1]:
207 rect
= wxRect(x
, y
, self
.dl_w
, self
.dl_h
) # create rect region
218 def DrawMonth(self
): # month and year title
219 month
= Month
[self
.month
]
222 if self
.sizeh
< _MIDSIZE
:
225 f
= wxFont(sizef
, self
.font
, wxNORMAL
, self
.bold
)
228 tw
,th
= self
.DC
.GetTextExtent(month
)
229 adjust
= self
.cx_st
+ (self
.sizew
-tw
)/2
230 self
.DC
.DrawText(month
, adjust
, self
.cy_st
+ th
)
232 year
= str(self
.year
)
233 tw
,th
= self
.DC
.GetTextExtent(year
)
234 adjust
= self
.sizew
- tw
- self
.x_mrg
236 self
.title_offset
= th
* 2
238 f
= wxFont(sizef
, self
.font
, wxNORMAL
, self
.bold
)
240 self
.DC
.DrawText(year
, self
.cx_st
+ adjust
, self
.cy_st
+ th
)
242 def DrawWeek(self
): # draw the week days
243 width
= self
.gridx
[1]-self
.gridx
[0]
244 height
= self
.gridy
[1] - self
.gridy
[0]
245 rect_w
= self
.gridx
[7]-self
.gridx
[0]
247 f
= wxFont(10, self
.font
, wxNORMAL
, self
.bold
) # initial font setting
248 if self
.week_auto
== TRUE
:
249 test_size
= self
.max_week_size
# max size
252 f
.SetPointSize(test_size
)
254 tw
,th
= self
.DC
.GetTextExtent(test_day
)
255 if tw
< width
and th
< height
:
257 test_size
= test_size
- 1
259 f
.SetPointSize(self
.week_size
) # set fixed size
262 self
.DC
.SetTextForeground(wxNamedColour(self
.week_font_color
))
267 brush
= wxBrush(wxNamedColour(self
.week_color
), wxSOLID
)
268 self
.DC
.SetBrush(brush
)
269 # self.DC.DrawRectangle(self.gridx[0], self.gridy[0], rect_w+1, height)
271 if self
.cal_type
== "NORMAL":
274 cal_days
= BusCalDays
277 day
= AbrWeekday
[val
]
280 dw
,dh
= self
.DC
.GetTextExtent(day
)
282 diffy
= (height
-dh
)/2
284 x
= self
.gridx
[cnt_x
]
285 y
= self
.gridy
[cnt_y
]
286 self
.DC
.DrawRectangle(self
.gridx
[cnt_x
], self
.gridy
[0], width
+1, height
)
287 self
.DC
.DrawText(day
, x
+diffx
, y
+diffy
)
290 def DrawNum(self
): # draw the day numbers
291 f
= wxFont(10, self
.font
, wxNORMAL
, self
.bold
) # initial font setting
292 if self
.num_auto
== TRUE
:
293 test_size
= self
.max_num_size
# max size
296 f
.SetPointSize(test_size
)
298 tw
,th
= self
.DC
.GetTextExtent(test_day
)
299 if tw
< self
.dl_w
and th
< self
.dl_h
:
302 test_size
= test_size
- 1
304 f
.SetPointSize(self
.num_size
) # set fixed size
310 x
= self
.gridx
[cnt_x
]
311 y
= self
.gridy
[cnt_y
]
315 num_color
= self
.cal_sel
[num_val
][0]
317 num_color
= self
.day_font_color
319 self
.DC
.SetTextForeground(wxNamedColour(num_color
))
322 tw
,th
= self
.DC
.GetTextExtent(val
)
323 if self
.num_align_horz
== wxALIGN_CENTRE
:
324 adj_h
= (self
.dl_w
- tw
)/2
325 elif self
.num_align_horz
== wxALIGN_RIGHT
:
326 adj_h
= self
.dl_w
- tw
328 adj_h
= 0 # left alignment
330 adj_h
= adj_h
+ self
.num_indent_horz
332 if self
.num_align_vert
== wxALIGN_CENTRE
:
333 adj_v
= (self
.dl_h
- th
)/2
334 elif self
.num_align_horz
== wxALIGN_RIGHT
:
335 adj_v
= self
.dl_h
- th
337 adj_v
= 0 # left alignment
339 adj_v
= adj_v
+ self
.num_indent_vert
341 self
.DC
.DrawText(val
, x
+adj_h
, y
+adj_v
)
348 def Center(self
): # calculate the dimensions in the center of the drawing area
350 bdh
= self
.y_mrg
+ self
.y_end
+ self
.title_offset
352 self
.dl_w
= int((self
.sizew
-bdw
)/7)
353 self
.dl_h
= int((self
.sizeh
-bdh
)/7)
355 self
.dl_th
= int(self
.dl_h
*self
.cal_week_scale
) # week title adjustment
356 self
.cwidth
= self
.dl_w
* 7
357 self
.cheight
= self
.dl_h
* 6 + self
.dl_th
359 def DrawSel(self
): # highlighted selected days
360 for key
in self
.cal_sel
.keys():
361 sel_color
= self
.cal_sel
[key
][1]
362 brush
= wxBrush(wxNamedColour(sel_color
), wxSOLID
)
363 self
.DC
.SetBrush(brush
)
365 if self
.hide_grid
is FALSE
:
366 self
.DC
.SetPen(wxPen(wxNamedColour(self
.grid_color
), 0))
368 self
.DC
.SetPen(wxPen(wxNamedColour(self
.back_color
), 0))
369 nkey
= key
+ self
.st_pos
-1
371 self
.DC
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
+1, rect
.height
+1)
373 def DrawGrid(self
): # calculate and draw the grid lines
374 self
.DC
.SetPen(wxPen(wxNamedColour(self
.grid_color
), 0))
379 self
.x_st
= self
.cx_st
+ self
.x_mrg
380 self
.y_st
= self
.cy_st
+ self
.y_mrg
+ self
.title_offset
# start postion of draw
385 y2
= y1
+ self
.cheight
387 if self
.hide_grid
is FALSE
:
388 self
.DC
.DrawLine(x1
, y1
, x1
, y2
)
389 self
.gridx
.append(x1
)
395 x2
= x1
+ self
.cwidth
397 if self
.hide_grid
is FALSE
:
398 self
.DC
.DrawLine(x1
, y1
, x2
, y1
)
399 self
.gridy
.append(y1
)
406 class PrtCalDraw(CalDraw
):
407 def InitValues(self
):
410 self
.set_cx_st
= 1.0 # start draw border location
413 self
.set_y_mrg
= 0.2 # draw offset position
417 def SetPSize(self
, pwidth
, pheight
): # calculate the dimensions in the center of the drawing area
418 self
.pwidth
= int(pwidth
)/self
.scale
419 self
.pheight
= int(pheight
)/self
.scale
421 def SetPreview(self
, preview
):
422 self
.preview
= preview
424 class wxCalendar(wxWindow
):
425 def __init__(self
, parent
, id, pos
=wxDefaultPosition
, size
=wxDefaultSize
):
426 wxWindow
.__init
__(self
, parent
, id, pos
, size
)
428 # set the calendar control attributes
430 self
.grid_color
= 'BLACK'
431 self
.back_color
= 'WHITE'
432 self
.hide_grid
= FALSE
433 self
.sel_color
= 'RED'
434 self
.hide_title
= FALSE
435 self
.show_weekend
= FALSE
436 self
.cal_type
= "NORMAL"
438 self
.week_color
= 'LIGHT GREY'
439 self
.week_font_color
= 'BLACK' # font colors
441 self
.select_list
= []
443 self
.SetBackgroundColour(wxNamedColor(self
.back_color
))
444 self
.Connect(-1, -1, wxEVT_LEFT_DOWN
, self
.OnLeftEvent
)
445 self
.Connect(-1, -1, wxEVT_LEFT_DCLICK
, self
.OnLeftDEvent
)
446 self
.Connect(-1, -1, wxEVT_RIGHT_DOWN
, self
.OnRightEvent
)
447 self
.Connect(-1, -1, wxEVT_RIGHT_DCLICK
, self
.OnRightDEvent
)
449 self
.sel_key
= None # last used by
450 self
.sel_lst
= [] # highlighted selected days
452 self
.SetNow() # default calendar for current month
457 EVT_PAINT(self
, self
.OnPaint
)
458 EVT_SIZE(self
, self
.OnSize
)
460 # control some of the main calendar attributes
463 self
.hide_title
= TRUE
466 self
.hide_grid
= TRUE
468 # determine the calendar rectangle click area and draw a selection
470 def ProcessClick(self
, event
):
471 self
.x
, self
.y
= event
.GetX(), event
.GetY()
472 key
= self
.GetDayHit(self
.x
, self
.y
)
475 # tab mouse click events and process
477 def OnLeftEvent(self
, event
):
479 self
.shiftkey
= event
.ShiftDown()
480 self
.ctrlkey
= event
.ControlDown()
481 self
.ProcessClick(event
)
483 def OnLeftDEvent(self
, event
):
485 self
.ProcessClick(event
)
487 def OnRightEvent(self
, event
):
489 self
.ProcessClick(event
)
491 def OnRightDEvent(self
, event
):
492 self
.click
= 'DRIGHT'
493 self
.ProcessClick(event
)
495 def SetSize(self
, set_size
):
498 def SetSelDay(self
, sel
):
499 self
.sel_lst
= sel
# list of highlighted days
501 # get the current date
505 self
.month
= dt
.month
509 # set the current day
511 def SetCurrentDay(self
):
513 self
.set_day
= self
.day
515 # get the date, day, month, year set in calendar
518 return self
.day
, self
.month
, self
.year
529 # set the day, month, and year
531 def SetDayValue(self
, day
):
534 def SetMonth(self
, month
):
535 if month
>= 1 and month
<= 12:
541 def SetYear(self
, year
):
544 # increment year and month
547 self
.year
= self
.year
+ 1
551 self
.year
= self
.year
- 1
555 self
.month
= self
.month
+ 1
558 self
.year
= self
.year
+ 1
562 self
.month
= self
.month
- 1
565 self
.year
= self
.year
- 1
568 # test to see if the selection has a date and create event
570 def TestDay(self
, key
):
572 self
.day
= int(self
.cal
[key
])
578 evt
= wxPyCommandEvent(2100, self
.GetId())
579 evt
.click
, evt
.day
, evt
.month
, evt
.year
= self
.click
, self
.day
, self
.month
, self
.year
580 evt
.shiftkey
= self
.shiftkey
581 evt
.ctrlkey
= self
.ctrlkey
582 self
.GetEventHandler().ProcessEvent(evt
)
584 self
.set_day
= self
.day
587 # find the clicked area rectangle
589 def GetDayHit(self
, mx
, my
):
590 for key
in self
.rg
.keys():
592 ms_rect
= wxRect(mx
, my
, 1, 1)
593 if wxIntersectRect(ms_rect
, val
) is not None:
594 result
= self
.TestDay(key
)
600 def SetWeekColor(self
, font_color
, week_color
): # set font and background color for week title
601 self
.week_font_color
= font_color
602 self
.week_color
= week_color
604 def AddSelect(self
, list, font_color
, back_color
):
605 list_val
= [list, font_color
, back_color
]
606 self
.select_list
.append(list_val
)
608 def ShowWeekEnd(self
):
609 self
.show_weekend
= TRUE
# highlight weekend
611 def SetBusType(self
):
612 self
.cal_type
= "BUS"
614 def OnSize(self
, evt
):
618 def OnPaint(self
, event
):
622 def DoDrawing(self
, DC
):
626 self
.cal
= cal
= CalDraw(self
)
628 cal
.grid_color
= self
.grid_color
629 cal
.back_color
= self
.back_color
630 cal
.hide_grid
= self
.hide_grid
631 cal
.grid_color
= self
.grid_color
632 cal
.hide_title
= self
.hide_title
633 cal
.show_weekend
= self
.show_weekend
634 cal
.cal_type
= self
.cal_type
636 if self
.size
is None:
637 size
= self
.GetClientSize()
643 cal
.week_font_color
= self
.week_font_color
644 cal
.week_color
= self
.week_color
647 cal
.SetCal(self
.year
, self
.month
)
648 for val
in self
.select_list
:
649 cal
.AddSelect(val
[0], val
[1], val
[2])
651 cal
.DrawCal(DC
, self
.sel_lst
)
653 self
.rg
= cal
.GetRect()
654 self
.cal
= cal
.GetCal()
655 self
.st_pos
= cal
.GetOffset()
656 self
.ymax
= DC
.MaxY()
658 if self
.set_day
!= None:
659 self
.SetDay(self
.set_day
)
662 # draw the selection rectangle
664 def DrawRect(self
, key
, color
= 'BLACK', width
= 0):
667 DC
= wxClientDC(self
)
670 brush
= wxBrush(wxColour(0, 0xFF, 0x80), wxTRANSPARENT
)
672 DC
.SetPen(wxPen(wxNamedColour(color
), width
))
675 DC
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
+1, rect
.height
+1)
679 # set the day selection
681 def SetDay(self
, day
):
682 day
= day
+ self
.st_pos
- 1
685 def SelectDay(self
, key
):
687 self
.DrawRect(self
.sel_key
, self
.back_color
, sel_size
) # clear large selection
688 if self
.hide_grid
is FALSE
:
689 self
.DrawRect(self
.sel_key
, self
.grid_color
)
691 self
.DrawRect(key
, self
.sel_color
, sel_size
)
692 self
.sel_key
= key
# store last used by
693 self
.select_day
= None
698 class CalenDlg(wxDialog
):
699 def __init__(self
, parent
, month
=None, day
= None, year
=None):
700 wxDialog
.__init
__(self
, parent
, -1, "Event Calendar", wxPyDefaultPosition
, wxSize(280, 360))
702 # set the calendar and attributes
703 self
.calend
= wxCalendar(self
, -1, wxPoint(20, 60), wxSize(240, 200))
705 self
.calend
.SetCurrentDay()
706 start_month
= self
.calend
.GetMonth()
707 start_year
= self
.calend
.GetYear()
709 self
.calend
.month
= start_month
= month
710 self
.calend
.year
= start_year
= year
711 self
.calend
.SetDayValue(day
)
713 self
.calend
.HideTitle()
716 # get month list from DateTime
717 monthlist
= GetMonthList()
721 self
.date
= wxComboBox(self
, mID
, Month
[start_month
], wxPoint(20, 20), wxSize(90, -1), monthlist
, wxCB_DROPDOWN
)
722 EVT_COMBOBOX(self
, mID
, self
.EvtComboBox
)
724 # alternate spin button to control the month
726 h
= self
.date
.GetSize().height
727 self
.m_spin
= wxSpinButton(self
, mID
, wxPoint(130, 20), wxSize(h
*2, h
), wxSP_VERTICAL
)
728 self
.m_spin
.SetRange(1, 12)
729 self
.m_spin
.SetValue(start_month
)
731 EVT_SPIN(self
, mID
, self
.OnMonthSpin
)
733 # spin button to control the year
735 self
.dtext
= wxTextCtrl(self
, -1, str(start_year
), wxPoint(160, 20), wxSize(60, -1))
736 h
= self
.dtext
.GetSize().height
738 self
.y_spin
= wxSpinButton(self
, mID
, wxPoint(220, 20), wxSize(h
*2, h
), wxSP_VERTICAL
)
739 self
.y_spin
.SetRange(1980, 2010)
740 self
.y_spin
.SetValue(start_year
)
742 EVT_SPIN(self
, mID
, self
.OnYrSpin
)
744 self
.Connect(self
.calend
.GetId(), -1, 2100, self
.MouseClick
)
748 but_size
= wxSize(60, 25)
751 wxButton(self
, mID
, ' Ok ', wxPoint(x_pos
, y_pos
), but_size
)
752 EVT_BUTTON(self
, mID
, self
.OnOk
)
755 wxButton(self
, mID
, ' Close ', wxPoint(x_pos
+ 120, y_pos
), but_size
)
756 EVT_BUTTON(self
, mID
, self
.OnCancel
)
758 def OnOk(self
, event
):
759 self
.EndModal(wxID_OK
)
761 def OnCancel(self
, event
):
762 self
.EndModal(wxID_CANCEL
)
764 # log the mouse clicks
765 def MouseClick(self
, evt
):
766 self
.month
= evt
.month
767 self
.result
= [evt
.click
, str(evt
.day
), Month
[evt
.month
], str(evt
.year
)] # result click type and date
769 if evt
.click
== 'DLEFT':
770 self
.EndModal(wxID_OK
)
772 # month and year spin selection routines
773 def OnMonthSpin(self
, event
):
774 month
= event
.GetPosition()
775 self
.date
.SetValue(Month
[month
])
776 self
.calend
.SetMonth(month
)
777 self
.calend
.Refresh()
779 def OnYrSpin(self
, event
):
780 year
= event
.GetPosition()
781 self
.dtext
.SetValue(str(year
))
782 self
.calend
.SetYear(year
)
783 self
.calend
.Refresh()
785 def EvtComboBox(self
, event
):
786 name
= event
.GetString()
787 monthval
= self
.date
.FindString(name
)
788 self
.m_spin
.SetValue(monthval
+1)
790 self
.calend
.SetMonth(monthval
+1)
793 # set the calendar for highlighted days
795 def ResetDisplay(self
):
796 month
= self
.calend
.GetMonth()
797 self
.calend
.Refresh()