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 *
18 CalDays
= [6, 0, 1, 2, 3, 4, 5]
19 AbrWeekday
= {6:"Sun", 0:"Mon", 1:"Tue", 2:"Wed", 3:"Thu", 4:"Fri", 5:"Sat"}
22 BusCalDays
= [0, 1, 2, 3, 4, 5, 6]
24 # calendar drawing routing
31 monthlist
.append(name
)
35 def __init__(self
, parent
):
39 self
.scale
= parent
.scale
46 self
.num_auto
= True # auto scale of the cal number day size
47 self
.num_size
= 12 # default size of calendar if no auto size
48 self
.max_num_size
= 12 # maximum size for calendar number
50 self
.num_align_horz
= wxALIGN_CENTRE
# alignment of numbers
51 self
.num_align_vert
= wxALIGN_CENTRE
52 self
.num_indent_horz
= 0 # points indent from position, used to offset if not centered
53 self
.num_indent_vert
= 0
55 self
.week_auto
= True # auto scale of week font text
57 self
.max_week_size
= 12
59 self
.grid_color
= 'BLACK' # grid and selection colors
60 self
.back_color
= 'WHITE'
61 self
.sel_color
= 'RED'
63 self
.high_color
= 'LIGHT BLUE'
64 self
.border_color
= 'BLACK'
65 self
.week_color
= 'LIGHT GREY'
67 self
.week_font_color
= 'BLACK' # font colors
68 self
.day_font_color
= 'BLACK'
73 self
.hide_title
= False
74 self
.hide_grid
= False
75 self
.outer_border
= True
78 self
.cal_week_scale
= 0.7
79 self
.show_weekend
= False
80 self
.cal_type
= "NORMAL"
82 def SetWeekColor(self
, font_color
, week_color
): # set font and background color for week title
83 self
.week_font_color
= font_color
84 self
.week_color
= week_color
86 def SetSize(self
, size
):
87 self
.set_sizew
= size
.width
88 self
.set_sizeh
= size
.height
90 def InitValues(self
): # default dimensions of various elements of the calendar
93 self
.set_cy_st
= 0 # start position
96 self
.set_y_mrg
= 15 # start of vertical draw default
100 def SetPos(self
, xpos
, ypos
):
101 self
.set_cx_st
= xpos
102 self
.set_cy_st
= ypos
104 def SetMarg(self
, xmarg
, ymarg
):
105 self
.set_x_st
= xmarg
106 self
.set_y_st
= ymarg
107 self
.set_y_end
= ymarg
109 def InitScale(self
): # scale position values
110 self
.sizew
= self
.set_sizew
* self
.pwidth
111 self
.sizeh
= self
.set_sizeh
* self
.pheight
113 self
.cx_st
= self
.set_cx_st
* self
.pwidth
# draw start position
114 self
.cy_st
= self
.set_cy_st
* self
.pheight
116 self
.x_mrg
= self
.set_x_mrg
* self
.pwidth
# calendar draw margins
117 self
.y_mrg
= self
.set_y_mrg
* self
.pheight
118 self
.y_end
= self
.set_y_end
* self
.pheight
120 def DrawCal(self
, DC
, sel_lst
=[]):
125 if self
.hide_title
is False:
133 if self
.show_weekend
is True: # highlight weekend dates
136 self
.AddSelect(sel_lst
) # overrides the weekend highlight
138 self
.DrawSel() # highlighted days
142 def AddSelect(self
, list, cfont
=None, cbackgrd
= None):
144 cfont
= self
.sel_color
# font digit color
146 cbackgrd
= self
.high_color
# select background color
149 self
.cal_sel
[val
] = (cfont
, cbackgrd
)
151 def DrawBorder(self
): # draw border around the outside of the main display rectangle
152 brush
= wxBrush(wxNamedColour(self
.back_color
), wxSOLID
)
153 self
.DC
.SetBrush(brush
)
154 self
.DC
.SetPen(wxPen(wxNamedColour(self
.border_color
), 1))
156 if self
.outer_border
is True:
157 rect
= wxRect(self
.cx_st
, self
.cy_st
, self
.sizew
, self
.sizeh
) # full display window area
158 self
.DC
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, rect
.height
)
160 def DrawNumVal(self
):
163 # calculate the calendar days and offset position
165 def SetCal(self
, year
, month
):
166 self
.InitValues() # reset initial values
172 t
= Date(year
, month
, day
)
173 dow
= self
.dow
= t
.day_of_week
# start day in month
174 dim
= self
.dim
= t
.days_in_month
# number of days in month
175 if self
.cal_type
== "NORMAL":
180 self
.st_pos
= start_pos
183 for i
in range(start_pos
):
187 self
.cal
.append(str(i
))
191 def SetWeekEnd(self
, font_color
='BLACK', backgrd
= 'LIGHT GREY'):
192 date
= 6 - int(self
.dow
) # start day of first saturday
193 while date
<= self
.dim
:
194 self
.cal_sel
[date
] = (font_color
, backgrd
) # Saturday
197 self
.cal_sel
[date
] = (font_color
, backgrd
) # Sunday
202 def GetRect(self
): # get the display rectange list of the day grid
204 for y
in self
.gridy
[1:-1]:
205 for x
in self
.gridx
[:-1]:
206 rect
= wxRect(x
, y
, self
.dl_w
, self
.dl_h
) # create rect region
217 def DrawMonth(self
): # month and year title
218 month
= Month
[self
.month
]
221 if self
.sizeh
< _MIDSIZE
:
224 f
= wxFont(sizef
, self
.font
, wxNORMAL
, self
.bold
)
227 tw
,th
= self
.DC
.GetTextExtent(month
)
228 adjust
= self
.cx_st
+ (self
.sizew
-tw
)/2
229 self
.DC
.DrawText(month
, adjust
, self
.cy_st
+ th
)
231 year
= str(self
.year
)
232 tw
,th
= self
.DC
.GetTextExtent(year
)
233 adjust
= self
.sizew
- tw
- self
.x_mrg
235 self
.title_offset
= th
* 2
237 f
= wxFont(sizef
, self
.font
, wxNORMAL
, self
.bold
)
239 self
.DC
.DrawText(year
, self
.cx_st
+ adjust
, self
.cy_st
+ th
)
241 def DrawWeek(self
): # draw the week days
242 width
= self
.gridx
[1]-self
.gridx
[0]
243 height
= self
.gridy
[1] - self
.gridy
[0]
244 rect_w
= self
.gridx
[7]-self
.gridx
[0]
246 f
= wxFont(10, self
.font
, wxNORMAL
, self
.bold
) # initial font setting
247 if self
.week_auto
== True:
248 test_size
= self
.max_week_size
# max size
251 f
.SetPointSize(test_size
)
253 tw
,th
= self
.DC
.GetTextExtent(test_day
)
254 if tw
< width
and th
< height
:
256 test_size
= test_size
- 1
258 f
.SetPointSize(self
.week_size
) # set fixed size
261 self
.DC
.SetTextForeground(wxNamedColour(self
.week_font_color
))
266 brush
= wxBrush(wxNamedColour(self
.week_color
), wxSOLID
)
267 self
.DC
.SetBrush(brush
)
268 # self.DC.DrawRectangle(self.gridx[0], self.gridy[0], rect_w+1, height)
270 if self
.cal_type
== "NORMAL":
273 cal_days
= BusCalDays
276 day
= AbrWeekday
[val
]
279 dw
,dh
= self
.DC
.GetTextExtent(day
)
281 diffy
= (height
-dh
)/2
283 x
= self
.gridx
[cnt_x
]
284 y
= self
.gridy
[cnt_y
]
285 self
.DC
.DrawRectangle(self
.gridx
[cnt_x
], self
.gridy
[0], width
+1, height
)
286 self
.DC
.DrawText(day
, x
+diffx
, y
+diffy
)
289 def DrawNum(self
): # draw the day numbers
290 f
= wxFont(10, self
.font
, wxNORMAL
, self
.bold
) # initial font setting
291 if self
.num_auto
== True:
292 test_size
= self
.max_num_size
# max size
295 f
.SetPointSize(test_size
)
297 tw
,th
= self
.DC
.GetTextExtent(test_day
)
298 if tw
< self
.dl_w
and th
< self
.dl_h
:
301 test_size
= test_size
- 1
303 f
.SetPointSize(self
.num_size
) # set fixed size
309 x
= self
.gridx
[cnt_x
]
310 y
= self
.gridy
[cnt_y
]
314 num_color
= self
.cal_sel
[num_val
][0]
316 num_color
= self
.day_font_color
318 self
.DC
.SetTextForeground(wxNamedColour(num_color
))
321 tw
,th
= self
.DC
.GetTextExtent(val
)
322 if self
.num_align_horz
== wxALIGN_CENTRE
:
323 adj_h
= (self
.dl_w
- tw
)/2
324 elif self
.num_align_horz
== wxALIGN_RIGHT
:
325 adj_h
= self
.dl_w
- tw
327 adj_h
= 0 # left alignment
329 adj_h
= adj_h
+ self
.num_indent_horz
331 if self
.num_align_vert
== wxALIGN_CENTRE
:
332 adj_v
= (self
.dl_h
- th
)/2
333 elif self
.num_align_horz
== wxALIGN_RIGHT
:
334 adj_v
= self
.dl_h
- th
336 adj_v
= 0 # left alignment
338 adj_v
= adj_v
+ self
.num_indent_vert
340 self
.DC
.DrawText(val
, x
+adj_h
, y
+adj_v
)
347 def Center(self
): # calculate the dimensions in the center of the drawing area
349 bdh
= self
.y_mrg
+ self
.y_end
+ self
.title_offset
351 self
.dl_w
= int((self
.sizew
-bdw
)/7)
352 self
.dl_h
= int((self
.sizeh
-bdh
)/7)
354 self
.dl_th
= int(self
.dl_h
*self
.cal_week_scale
) # week title adjustment
355 self
.cwidth
= self
.dl_w
* 7
356 self
.cheight
= self
.dl_h
* 6 + self
.dl_th
358 def DrawSel(self
): # highlighted selected days
359 for key
in self
.cal_sel
.keys():
360 sel_color
= self
.cal_sel
[key
][1]
361 brush
= wxBrush(wxNamedColour(sel_color
), wxSOLID
)
362 self
.DC
.SetBrush(brush
)
364 if self
.hide_grid
is False:
365 self
.DC
.SetPen(wxPen(wxNamedColour(self
.grid_color
), 0))
367 self
.DC
.SetPen(wxPen(wxNamedColour(self
.back_color
), 0))
368 nkey
= key
+ self
.st_pos
-1
370 self
.DC
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
+1, rect
.height
+1)
372 def DrawGrid(self
): # calculate and draw the grid lines
373 self
.DC
.SetPen(wxPen(wxNamedColour(self
.grid_color
), 0))
378 self
.x_st
= self
.cx_st
+ self
.x_mrg
379 self
.y_st
= self
.cy_st
+ self
.y_mrg
+ self
.title_offset
# start postion of draw
384 y2
= y1
+ self
.cheight
386 if self
.hide_grid
is False:
387 self
.DC
.DrawLine(x1
, y1
, x1
, y2
)
388 self
.gridx
.append(x1
)
394 x2
= x1
+ self
.cwidth
396 if self
.hide_grid
is False:
397 self
.DC
.DrawLine(x1
, y1
, x2
, y1
)
398 self
.gridy
.append(y1
)
405 class PrtCalDraw(CalDraw
):
406 def InitValues(self
):
409 self
.set_cx_st
= 1.0 # start draw border location
412 self
.set_y_mrg
= 0.2 # draw offset position
416 def SetPSize(self
, pwidth
, pheight
): # calculate the dimensions in the center of the drawing area
417 self
.pwidth
= int(pwidth
)/self
.scale
418 self
.pheight
= int(pheight
)/self
.scale
420 def SetPreview(self
, preview
):
421 self
.preview
= preview
423 class wxCalendar(wxWindow
):
424 def __init__(self
, parent
, id, pos
=wxDefaultPosition
, size
=wxDefaultSize
):
425 wxWindow
.__init
__(self
, parent
, id, pos
, size
)
427 # set the calendar control attributes
429 self
.grid_color
= 'BLACK'
430 self
.back_color
= 'WHITE'
431 self
.hide_grid
= False
432 self
.sel_color
= 'RED'
433 self
.hide_title
= False
434 self
.show_weekend
= False
435 self
.cal_type
= "NORMAL"
437 self
.week_color
= 'LIGHT GREY'
438 self
.week_font_color
= 'BLACK' # font colors
440 self
.select_list
= []
442 self
.SetBackgroundColour(wxNamedColor(self
.back_color
))
443 self
.Connect(-1, -1, wxEVT_LEFT_DOWN
, self
.OnLeftEvent
)
444 self
.Connect(-1, -1, wxEVT_LEFT_DCLICK
, self
.OnLeftDEvent
)
445 self
.Connect(-1, -1, wxEVT_RIGHT_DOWN
, self
.OnRightEvent
)
446 self
.Connect(-1, -1, wxEVT_RIGHT_DCLICK
, self
.OnRightDEvent
)
448 self
.sel_key
= None # last used by
449 self
.sel_lst
= [] # highlighted selected days
451 self
.SetNow() # default calendar for current month
456 EVT_PAINT(self
, self
.OnPaint
)
457 EVT_SIZE(self
, self
.OnSize
)
459 # control some of the main calendar attributes
462 self
.hide_title
= True
465 self
.hide_grid
= True
467 # determine the calendar rectangle click area and draw a selection
469 def ProcessClick(self
, event
):
470 self
.x
, self
.y
= event
.GetX(), event
.GetY()
471 key
= self
.GetDayHit(self
.x
, self
.y
)
474 # tab mouse click events and process
476 def OnLeftEvent(self
, event
):
478 self
.shiftkey
= event
.ShiftDown()
479 self
.ctrlkey
= event
.ControlDown()
480 self
.ProcessClick(event
)
482 def OnLeftDEvent(self
, event
):
484 self
.ProcessClick(event
)
486 def OnRightEvent(self
, event
):
488 self
.ProcessClick(event
)
490 def OnRightDEvent(self
, event
):
491 self
.click
= 'DRIGHT'
492 self
.ProcessClick(event
)
494 def SetSize(self
, set_size
):
497 def SetSelDay(self
, sel
):
498 self
.sel_lst
= sel
# list of highlighted days
500 # get the current date
504 self
.month
= dt
.month
508 # set the current day
510 def SetCurrentDay(self
):
512 self
.set_day
= self
.day
514 # get the date, day, month, year set in calendar
517 return self
.day
, self
.month
, self
.year
528 # set the day, month, and year
530 def SetDayValue(self
, day
):
533 def SetMonth(self
, month
):
534 if month
>= 1 and month
<= 12:
540 def SetYear(self
, year
):
543 # increment year and month
546 self
.year
= self
.year
+ 1
550 self
.year
= self
.year
- 1
554 self
.month
= self
.month
+ 1
557 self
.year
= self
.year
+ 1
561 self
.month
= self
.month
- 1
564 self
.year
= self
.year
- 1
567 # test to see if the selection has a date and create event
569 def TestDay(self
, key
):
571 self
.day
= int(self
.cal
[key
])
577 evt
= wxPyCommandEvent(2100, self
.GetId())
578 evt
.click
, evt
.day
, evt
.month
, evt
.year
= self
.click
, self
.day
, self
.month
, self
.year
579 evt
.shiftkey
= self
.shiftkey
580 evt
.ctrlkey
= self
.ctrlkey
581 self
.GetEventHandler().ProcessEvent(evt
)
583 self
.set_day
= self
.day
586 # find the clicked area rectangle
588 def GetDayHit(self
, mx
, my
):
589 for key
in self
.rg
.keys():
591 ms_rect
= wxRect(mx
, my
, 1, 1)
592 if wxIntersectRect(ms_rect
, val
) is not None:
593 result
= self
.TestDay(key
)
599 def SetWeekColor(self
, font_color
, week_color
): # set font and background color for week title
600 self
.week_font_color
= font_color
601 self
.week_color
= week_color
603 def AddSelect(self
, list, font_color
, back_color
):
604 list_val
= [list, font_color
, back_color
]
605 self
.select_list
.append(list_val
)
607 def ShowWeekEnd(self
):
608 self
.show_weekend
= True # highlight weekend
610 def SetBusType(self
):
611 self
.cal_type
= "BUS"
613 def OnSize(self
, evt
):
617 def OnPaint(self
, event
):
621 def DoDrawing(self
, DC
):
625 self
.cal
= cal
= CalDraw(self
)
627 cal
.grid_color
= self
.grid_color
628 cal
.back_color
= self
.back_color
629 cal
.hide_grid
= self
.hide_grid
630 cal
.grid_color
= self
.grid_color
631 cal
.hide_title
= self
.hide_title
632 cal
.show_weekend
= self
.show_weekend
633 cal
.cal_type
= self
.cal_type
635 if self
.size
is None:
636 size
= self
.GetClientSize()
642 cal
.week_font_color
= self
.week_font_color
643 cal
.week_color
= self
.week_color
646 cal
.SetCal(self
.year
, self
.month
)
647 for val
in self
.select_list
:
648 cal
.AddSelect(val
[0], val
[1], val
[2])
650 cal
.DrawCal(DC
, self
.sel_lst
)
652 self
.rg
= cal
.GetRect()
653 self
.cal
= cal
.GetCal()
654 self
.st_pos
= cal
.GetOffset()
655 self
.ymax
= DC
.MaxY()
657 if self
.set_day
!= None:
658 self
.SetDay(self
.set_day
)
661 # draw the selection rectangle
663 def DrawRect(self
, key
, color
= 'BLACK', width
= 0):
666 DC
= wxClientDC(self
)
669 brush
= wxBrush(wxColour(0, 0xFF, 0x80), wxTRANSPARENT
)
671 DC
.SetPen(wxPen(wxNamedColour(color
), width
))
674 DC
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
+1, rect
.height
+1)
678 # set the day selection
680 def SetDay(self
, day
):
681 day
= day
+ self
.st_pos
- 1
684 def SelectDay(self
, key
):
686 self
.DrawRect(self
.sel_key
, self
.back_color
, sel_size
) # clear large selection
687 if self
.hide_grid
is False:
688 self
.DrawRect(self
.sel_key
, self
.grid_color
)
690 self
.DrawRect(key
, self
.sel_color
, sel_size
)
691 self
.sel_key
= key
# store last used by
692 self
.select_day
= None
697 class CalenDlg(wxDialog
):
698 def __init__(self
, parent
, month
=None, day
= None, year
=None):
699 wxDialog
.__init
__(self
, parent
, -1, "Event Calendar", wxPyDefaultPosition
, wxSize(280, 360))
701 # set the calendar and attributes
702 self
.calend
= wxCalendar(self
, -1, wxPoint(20, 60), wxSize(240, 200))
704 self
.calend
.SetCurrentDay()
705 start_month
= self
.calend
.GetMonth()
706 start_year
= self
.calend
.GetYear()
708 self
.calend
.month
= start_month
= month
709 self
.calend
.year
= start_year
= year
710 self
.calend
.SetDayValue(day
)
712 self
.calend
.HideTitle()
715 # get month list from DateTime
716 monthlist
= GetMonthList()
720 self
.date
= wxComboBox(self
, mID
, Month
[start_month
], wxPoint(20, 20), wxSize(90, -1), monthlist
, wxCB_DROPDOWN
)
721 EVT_COMBOBOX(self
, mID
, self
.EvtComboBox
)
723 # alternate spin button to control the month
725 h
= self
.date
.GetSize().height
726 self
.m_spin
= wxSpinButton(self
, mID
, wxPoint(130, 20), wxSize(h
*2, h
), wxSP_VERTICAL
)
727 self
.m_spin
.SetRange(1, 12)
728 self
.m_spin
.SetValue(start_month
)
730 EVT_SPIN(self
, mID
, self
.OnMonthSpin
)
732 # spin button to control the year
734 self
.dtext
= wxTextCtrl(self
, -1, str(start_year
), wxPoint(160, 20), wxSize(60, -1))
735 h
= self
.dtext
.GetSize().height
737 self
.y_spin
= wxSpinButton(self
, mID
, wxPoint(220, 20), wxSize(h
*2, h
), wxSP_VERTICAL
)
738 self
.y_spin
.SetRange(1980, 2010)
739 self
.y_spin
.SetValue(start_year
)
741 EVT_SPIN(self
, mID
, self
.OnYrSpin
)
743 self
.Connect(self
.calend
.GetId(), -1, 2100, self
.MouseClick
)
747 but_size
= wxSize(60, 25)
750 wxButton(self
, mID
, ' Ok ', wxPoint(x_pos
, y_pos
), but_size
)
751 EVT_BUTTON(self
, mID
, self
.OnOk
)
754 wxButton(self
, mID
, ' Close ', wxPoint(x_pos
+ 120, y_pos
), but_size
)
755 EVT_BUTTON(self
, mID
, self
.OnCancel
)
757 def OnOk(self
, event
):
758 self
.EndModal(wxID_OK
)
760 def OnCancel(self
, event
):
761 self
.EndModal(wxID_CANCEL
)
763 # log the mouse clicks
764 def MouseClick(self
, evt
):
765 self
.month
= evt
.month
766 self
.result
= [evt
.click
, str(evt
.day
), Month
[evt
.month
], str(evt
.year
)] # result click type and date
768 if evt
.click
== 'DLEFT':
769 self
.EndModal(wxID_OK
)
771 # month and year spin selection routines
772 def OnMonthSpin(self
, event
):
773 month
= event
.GetPosition()
774 self
.date
.SetValue(Month
[month
])
775 self
.calend
.SetMonth(month
)
776 self
.calend
.Refresh()
778 def OnYrSpin(self
, event
):
779 year
= event
.GetPosition()
780 self
.dtext
.SetValue(str(year
))
781 self
.calend
.SetYear(year
)
782 self
.calend
.Refresh()
784 def EvtComboBox(self
, event
):
785 name
= event
.GetString()
786 monthval
= self
.date
.FindString(name
)
787 self
.m_spin
.SetValue(monthval
+1)
789 self
.calend
.SetMonth(monthval
+1)
792 # set the calendar for highlighted days
794 def ResetDisplay(self
):
795 month
= self
.calend
.GetMonth()
796 self
.calend
.Refresh()