1# -*- coding: utf-8 -*- 2# #---------------------------------------------------------------------------- 3# Name: calendar.py 4# Purpose: Calendar display control 5# 6# Author: Lorne White (email: lorne.white@telusplanet.net) 7# 8# Created: 9# Version: 0.92 10# Date: Nov 26, 2001 11# Licence: wxWindows license 12# Tags: phoenix-port, documented, unittest, py3-port 13#---------------------------------------------------------------------------- 14# 12/01/2003 - Jeff Grimmett (grimmtooth@softhome.net) 15# 16# o Updated for wx namespace 17# o Tested with updated demo 18# o Added new event type EVT_CALENDAR. The reason for this is that the original 19# library used a hardcoded ID of 2100 for generating events. This makes it 20# very difficult to fathom when trying to decode the code since there's no 21# published API. Creating the new event binder might seem like overkill - 22# after all, you might ask, why not just use a new event ID and be done with 23# it? However, a consistent interface is very useful at times; also it makes 24# it clear that we're not just hunting for mouse clicks -- we're hunting 25# wabbit^H^H^H^H (sorry bout that) for calender-driven mouse clicks. So 26# that's my sad story. Shoot me if you must :-) 27# o There's still one deprecation warning buried in here somewhere, but I 28# haven't been able to find it yet. It only occurs when displaying a 29# print preview, and only the first time. It *could* be an error in the 30# demo, I suppose. 31# 32# Here's the traceback: 33# 34# C:\Python\lib\site-packages\wx\core.py:949: DeprecationWarning: 35# integer argument expected, got float 36# newobj = _core.new_Rect(*args, **kwargs) 37# 38# 12/17/2003 - Jeff Grimmett (grimmtooth@softhome.net) 39# 40# o A few style-guide nips and tucks 41# o Renamed wxCalendar to Calendar 42# o Couple of bugfixes 43# 44# 06/02/2004 - Joerg "Adi" Sieker adi@sieker.info 45# 46# o Changed color handling, use dictionary instead of members. 47# This causes all color changes to be ignored if they manipluate the members directly. 48# SetWeekColor and other method color methods were adapted to use the new dictionary. 49# o Added COLOR_* constants 50# o Added SetColor method for Calendar class 51# o Added 3D look of week header 52# o Added colors for 3D look of header 53# o Fixed width calculation. 54# Because of rounding difference the total width and height of the 55# calendar could be up to 6 pixels to small. The last column and row 56# are now wider/taller by the missing amount. 57# o Added SetTextAlign method to wxCalendar. This exposes logic 58# which was already there. 59# o Fixed CalDraw.SetMarg which set set_x_st and set_y_st which don't get used anywhere. 60# Instead set set_x_mrg and set_y_mrg 61# o Changed default X and Y Margin to 0. 62# o Added wxCalendar.SetMargin. 63# 64# 17/03/2004 - Joerg "Adi" Sieker adi@sieker.info 65# o Added keyboard navigation to the control. 66# Use the cursor keys to navigate through the ages. :) 67# The Home key function as go to today 68# o select day is now a filled rect instead of just an outline 69# 70# 15/04/2005 - Joe "shmengie" Brown joebrown@podiatryfl.com 71# o Adjusted spin control size/placement (On Windows ctrls were overlapping). 72# o Set Ok/Cancel buttons to wx.ID_OK & wx.ID_CANCEL to provide default dialog 73# behaviour. 74# o If no date has been clicked clicked, OnOk set the result to calend's date, 75# important if keyboard only navigation is used. 76# 77# 12/10/2006 - Walter Barnes walter_barnes05@yahoo.com 78# o Fixed CalDraw to properly render months that start on a Sunday. 79# 80# 21/10/2006 - Walter Barnes walter_barnes05@yahoo.com 81# o Fixed a bug in Calendar: Shift and Control key status was only recorded for 82# left-down events. 83# o Added handlers for wxEVT_MIDDLE_DOWN and wxEVT_MIDDLE_DCLICK to generate 84# EVT_CALENDAR for these mouse events. 85"""A module with a calendar control and a calendar dialog and some utility 86methods and classes. 87""" 88 89import wx 90_ = wx.GetTranslation 91 92import datetime 93 94CalDays = [6, 0, 1, 2, 3, 4, 5] 95AbrWeekday = {6: _("Sun"), 0: _("Mon"), 1: _("Tue"), 2: _("Wed"), 3: _("Thu"), 96 4: _("Fri"), 5: _("Sat")} 97Month = {0: None, 98 1: _('January'), 2: _('February'), 3: _('March'), 99 4: _('April'), 5: _('May'), 6: _('June'), 100 7: _('July'), 8: _('August'), 9: _('September'), 101 10: _('October'), 11: _('November'), 12: _('December')} 102_MIDSIZE = 180 103 104COLOR_GRID_LINES = "grid_lines" 105COLOR_BACKGROUND = "background" 106COLOR_SELECTION_FONT = "selection_font" 107COLOR_SELECTION_BACKGROUND = "selection_background" 108COLOR_BORDER = "border" 109COLOR_HEADER_BACKGROUND = "header_background" 110COLOR_HEADER_FONT = "header_font" 111COLOR_WEEKEND_BACKGROUND = "weekend_background" 112COLOR_WEEKEND_FONT = "weekend_font" 113COLOR_FONT = "font" 114COLOR_3D_LIGHT = "3d_light" 115COLOR_3D_DARK = "3d_dark" 116COLOR_HIGHLIGHT_FONT = "highlight_font" 117COLOR_HIGHLIGHT_BACKGROUND = "highlight_background" 118 119BusCalDays = [0, 1, 2, 3, 4, 5, 6] 120 121# Calendar click event - added 12/1/03 by jmg (see above) 122wxEVT_COMMAND_PYCALENDAR_DAY_CLICKED = wx.NewEventType() 123EVT_CALENDAR = wx.PyEventBinder(wxEVT_COMMAND_PYCALENDAR_DAY_CLICKED, 1) 124 125 126def GetMonthList(): 127 """ 128 Get a list of the defined month names. 129 130 rtype: `list` 131 """ 132 133 monthlist = [] 134 for i in range(13): 135 name = Month[i] 136 if name is not None: 137 monthlist.append(name) 138 return monthlist 139 140 141def MakeColor(in_color): 142 """ 143 Try and create a :class:`wx.Colour`. 144 145 :returns: a :class:`wx.Colour` instance to `in_colour` 146 """ 147 try: 148 color = wx.Colour(in_color) 149 except Exception: 150 color = in_color 151 return color 152 153 154def DefaultColors(): 155 """Define some default colors.""" 156 colors = {} 157 colors[COLOR_GRID_LINES] = 'BLACK' 158 colors[COLOR_BACKGROUND] = 'WHITE' 159 colors[COLOR_SELECTION_FONT] = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) 160 colors[COLOR_SELECTION_BACKGROUND] = wx.Colour(255, 255, 225) 161 colors[COLOR_BORDER] = 'BLACK' 162 colors[COLOR_HEADER_BACKGROUND] = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE) 163 colors[COLOR_HEADER_FONT] = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) 164 colors[COLOR_WEEKEND_BACKGROUND] = 'LIGHT GREY' 165 colors[COLOR_WEEKEND_FONT] = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) 166 colors[COLOR_FONT] = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) 167 colors[COLOR_3D_LIGHT] = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNHIGHLIGHT) 168 colors[COLOR_3D_DARK] = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNSHADOW) 169 colors[COLOR_HIGHLIGHT_FONT] = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT) 170 colors[COLOR_HIGHLIGHT_BACKGROUND] = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT) 171 return colors 172 173 174class CalDraw: 175 """A class to draw a calendar.""" 176 def __init__(self, parent): 177 """ 178 Default class constructor 179 180 :param wx.Window `parent`: parent window. 181 182 """ 183 self.pwidth = 1 184 self.pheight = 1 185 try: 186 self.scale = parent.scale 187 except Exception: 188 self.scale = 1 189 190 self.gridx = [] 191 self.gridy = [] 192 193 self.DefParms() 194 195 def DefParms(self): 196 """Setup the default parameters.""" 197 self.num_auto = True # auto scale of the cal number day size 198 self.num_size = 12 # default size of calendar if no auto size 199 self.max_num_size = 12 # maximum size for calendar number 200 201 self.num_align_horz = wx.ALIGN_CENTRE # alignment of numbers 202 self.num_align_vert = wx.ALIGN_CENTRE 203 self.num_indent_horz = 0 # points indent from position, used to offset if not centered 204 self.num_indent_vert = 0 205 206 self.week_auto = True # auto scale of week font text 207 self.week_size = 10 208 self.max_week_size = 12 209 210 self.colors = DefaultColors() 211 212 self.fontfamily = wx.FONTFAMILY_SWISS 213 self.fontstyle = wx.FONTSTYLE_NORMAL 214 self.fontweight = wx.FONTWEIGHT_NORMAL 215 216 self.hide_title = False 217 self.hide_grid = False 218 self.outer_border = True 219 220 self.title_offset = 0 221 self.cal_week_scale = 0.7 222 self.show_weekend = False 223 self.cal_type = "NORMAL" 224 225 def SetWeekColor(self, font_color, week_color): 226 """ 227 Set the font and background color of the week title. 228 229 :param `font_color`: the font color, a value as is accepted by :class:`wx.Colour` 230 :param `week_color`: the week color, a value as is accepted by :class:`wx.Colour` 231 """ 232 self.colors[COLOR_HEADER_FONT] = MakeColor(font_color) 233 self.colors[COLOR_HEADER_BACKGROUND] = MakeColor(week_color) 234 self.colors[COLOR_3D_LIGHT] = MakeColor(week_color) 235 self.colors[COLOR_3D_DARK] = MakeColor(week_color) 236 237 def SetSize(self, size): 238 """ 239 Set the size. 240 241 :param `size`: a tuple/list with width and height 242 243 """ 244 self.set_sizew = size[0] 245 self.set_sizeh = size[1] 246 247 def InitValues(self): 248 """Default dimensions of various elements of the calendar.""" 249 self.rg = {} 250 self.cal_sel = {} 251 self.set_cy_st = 0 # start position 252 self.set_cx_st = 0 253 254 self.set_y_mrg = 1 # start of vertical draw default 255 self.set_x_mrg = 1 256 self.set_y_end = 1 257 258 def SetPos(self, xpos, ypos): 259 """ 260 Set the position. 261 262 :param int `xpos`: the x position 263 :param int `ypos`: the y position 264 265 """ 266 self.set_cx_st = xpos 267 self.set_cy_st = ypos 268 269 def SetMarg(self, xmarg, ymarg): 270 """ 271 Set the margins. 272 273 :param `xmarg`: the x margin 274 :param `ymarg`: the y margin, also used for the end margin 275 276 """ 277 self.set_x_mrg = xmarg 278 self.set_y_mrg = ymarg 279 self.set_y_end = ymarg 280 281 def InitScale(self): 282 """Set the default scale values.""" 283 self.sizew = int(self.set_sizew * self.pwidth) 284 self.sizeh = int(self.set_sizeh * self.pheight) 285 286 self.cx_st = int(self.set_cx_st * self.pwidth) # draw start position 287 self.cy_st = int(self.set_cy_st * self.pheight) 288 289 self.x_mrg = int(self.set_x_mrg * self.pwidth) # calendar draw margins 290 self.y_mrg = int(self.set_y_mrg * self.pheight) 291 self.y_end = int(self.set_y_end * self.pheight) 292 293 def DrawCal(self, DC, sel_lst=[]): 294 """ 295 Draw the calendar. 296 297 :param `DC`: the :class:`wx.DC` to use to draw upon. 298 :param `sel_list`: a list of days to override the weekend highlight. 299 """ 300 self.InitScale() 301 302 self.DrawBorder(DC) 303 304 if self.hide_title is False: 305 self.DrawMonth(DC) 306 307 self.Center() 308 309 self.DrawGrid(DC) 310 self.GetRect() 311 if self.show_weekend is True: # highlight weekend dates 312 self.SetWeekEnd() 313 314 self.AddSelect(sel_lst) # overrides the weekend highlight 315 316 self.DrawSel(DC) # highlighted days 317 self.DrawWeek(DC) 318 self.DrawNum(DC) 319 320 def AddSelect(self, list, cfont=None, cbackgrd=None): 321 """ 322 Add a selection of days. 323 324 :param `list`: a list of days to select 325 :param `cfont`: the font color to use 326 :param `cbackgrd`: the background color to use 327 328 """ 329 if cfont is None: 330 cfont = self.colors[COLOR_SELECTION_FONT] # font digit color 331 if cbackgrd is None: 332 cbackgrd = self.colors[COLOR_SELECTION_BACKGROUND] # select background color 333 334 for val in list: 335 self.cal_sel[val] = (cfont, cbackgrd) 336 337 def DrawBorder(self, DC, transparent=False): 338 """ 339 Draw a border around the outside of the main display rectangle. 340 341 :param `DC`: the :class:`wx.DC` to use 342 :param `transparent`: use a transparent brush, default is ``False``. 343 344 """ 345 if self.outer_border is True: 346 if transparent is False: 347 brush = wx.Brush(MakeColor(self.colors[COLOR_BACKGROUND]), wx.BRUSHSTYLE_SOLID) 348 else: 349 brush = wx.TRANSPARENT_BRUSH 350 DC.SetBrush(brush) 351 DC.SetPen(wx.Pen(MakeColor(self.colors[COLOR_BORDER]))) 352 # full display window area 353 rect = wx.Rect(self.cx_st, self.cy_st, self.sizew, self.sizeh) 354 DC.DrawRectangle(rect) 355 356 def DrawFocusIndicator(self, DC): 357 """ 358 Draw the focus indicator 359 360 :param `DC`: the :class:`wx.DC` to use 361 362 """ 363 if self.outer_border is True: 364 DC.SetBrush(wx.TRANSPARENT_BRUSH) 365 DC.SetPen(wx.Pen(MakeColor(self.colors[COLOR_HIGHLIGHT_BACKGROUND]), style=wx.PENSTYLE_DOT)) 366 # full display window area 367 rect = wx.Rect(self.cx_st, self.cy_st, self.sizew, self.sizeh) 368 DC.DrawRectangle(rect) 369 370 def DrawNumVal(self): 371 """Draw the numeric values.""" 372 self.DrawNum() 373 374 def SetCal(self, year, month): 375 """ 376 Calculate the calendar days and offset position. 377 378 :param int `year`: the year to calculate. 379 :param int `month`: the month to calculate. 380 381 """ 382 self.InitValues() # reset initial values 383 384 self.year = year 385 self.month = month 386 387 # first day of week (Mon->0), number of days in month 388 self.dow = dow = datetime.date(year, month, 1).weekday() 389 self.dim = dim = wx.DateTime().GetLastMonthDay(month-1, year).GetDay() 390 391 if self.cal_type == "NORMAL": 392 start_pos = dow + 1 393 else: 394 start_pos = dow 395 396 if start_pos > 6: 397 start_pos = 0 398 399 self.st_pos = start_pos 400 401 self.cal_days = [] 402 for i in range(start_pos): 403 self.cal_days.append('') 404 405 i = 1 406 while i <= dim: 407 self.cal_days.append(str(i)) 408 i = i + 1 409 410 self.end_pos = start_pos + dim - 1 411 412 return start_pos 413 414 def SetWeekEnd(self, font_color=None, backgrd=None): 415 """ 416 Set the weekend backgrounds. 417 418 :param `font_color`: the font color to use, if ``None`` the default is used. 419 :param `backgrd`: the background color to use, if ``None`` the default is used. 420 421 """ 422 if font_color is not None: 423 self.SetColor(COLOR_WEEKEND_FONT, MakeColor(font_color)) 424 if backgrd is not None: 425 self.SetColor(COLOR_WEEKEND_BACKGROUND, MakeColor(backgrd)) 426 427 date = 6 - int(self.dow) # start day of first saturday 428 if date == 0: # ...unless we start on Sunday 429 self.cal_sel[1] = (self.GetColor(COLOR_WEEKEND_FONT), self.GetColor(COLOR_WEEKEND_BACKGROUND)) 430 date = 7 431 432 while date <= self.dim: 433 self.cal_sel[date] = (self.GetColor(COLOR_WEEKEND_FONT), self.GetColor(COLOR_WEEKEND_BACKGROUND)) # Saturday 434 date = date + 1 435 436 if date <= self.dim: 437 self.cal_sel[date] = (self.GetColor(COLOR_WEEKEND_FONT), self.GetColor(COLOR_WEEKEND_BACKGROUND)) # Sunday 438 date = date + 6 439 else: 440 date = date + 7 441 442 def GetRect(self): 443 """Get the display rectange list of the day grid.""" 444 cnt = 0 445 h = 0 446 w = 0 447 for y in self.gridy[1:-1]: 448 if y == self.gridy[-2]: 449 h = h + self.restH 450 451 for x in self.gridx[:-1]: 452 assert type(y) == int 453 assert type(x) == int 454 455 w = self.cellW 456 h = self.cellH 457 458 if x == self.gridx[-2]: 459 w = w + self.restW 460 461 rect = wx.Rect(x, y, w + 1, h + 1) # create rect region 462 463 self.rg[cnt] = rect 464 cnt = cnt + 1 465 466 return self.rg 467 468 def GetCal(self): 469 """Get the calendar days.""" 470 return self.cal_days 471 472 def GetOffset(self): 473 """Get the offset position.""" 474 return self.st_pos 475 476 def DrawMonth(self, DC): 477 """ 478 Draw the month and year titles. 479 480 :param `DC`: the :class:`wx.DC` to use. 481 482 """ 483 month = Month[self.month] 484 485 sizef = 11 486 if self.sizeh < _MIDSIZE: 487 sizef = 10 488 489 f = wx.Font(sizef, self.fontfamily, self.fontstyle, self.fontweight) 490 DC.SetFont(f) 491 492 tw, th = DC.GetTextExtent(month) 493 adjust = self.cx_st + (self.sizew - tw) / 2 494 DC.DrawText(month, adjust, self.cy_st + th) 495 496 year = str(self.year) 497 tw, th = DC.GetTextExtent(year) 498 adjust = self.sizew - tw - self.x_mrg 499 500 self.title_offset = th * 2 501 502 f = wx.Font(sizef, self.fontfamily, self.fontstyle, self.fontweight) 503 DC.SetFont(f) 504 DC.DrawText(year, self.cx_st + adjust, self.cy_st + th) 505 506 def DrawWeek(self, DC): 507 """ 508 Draw the week days. 509 510 :param `DC`: the :class:`wx.DC` to use. 511 512 """ 513 # increase by 1 to include all gridlines 514 width = self.gridx[1] - self.gridx[0] + 1 515 height = self.gridy[1] - self.gridy[0] + 1 516 rect_w = self.gridx[-1] - self.gridx[0] 517 518 f = wx.Font(10, self.fontfamily, self.fontstyle, self.fontweight) # initial font setting 519 520 if self.week_auto is True: 521 test_size = self.max_week_size # max size 522 test_day = ' Sun ' 523 while test_size > 2: 524 f.SetPointSize(test_size) 525 DC.SetFont(f) 526 tw, th = DC.GetTextExtent(test_day) 527 528 if tw < width and th < height: 529 break 530 531 test_size = test_size - 1 532 else: 533 f.SetPointSize(self.week_size) # set fixed size 534 DC.SetFont(f) 535 536 DC.SetTextForeground(MakeColor(self.colors[COLOR_HEADER_FONT])) 537 538 cnt_x = 0 539 cnt_y = 0 540 541 brush = wx.Brush(MakeColor(self.colors[COLOR_HEADER_BACKGROUND]), wx.BRUSHSTYLE_SOLID) 542 DC.SetBrush(brush) 543 544 if self.cal_type == "NORMAL": 545 cal_days = CalDays 546 else: 547 cal_days = BusCalDays 548 549 for val in cal_days: 550 if val == cal_days[-1]: 551 width = width + self.restW 552 553 day = AbrWeekday[val] 554 555 if self.sizew < 200: 556 day = day[0] 557 558 dw, dh = DC.GetTextExtent(day) 559 560 diffx = (width - dw) / 2 561 diffy = (height - dh) / 2 562 563 x = self.gridx[cnt_x] 564 y = self.gridy[cnt_y] 565 pointXY = (x, y) 566 pointWH = (width, height) 567 if self.hide_grid is False: 568 pen = wx.Pen(MakeColor(self.GetColor(COLOR_GRID_LINES)), 1, wx.PENSTYLE_SOLID) 569 else: 570 pen = wx.Pen(MakeColor(self.GetColor(COLOR_BACKGROUND)), 1, wx.PENSTYLE_SOLID) 571 DC.SetPen(pen) 572 DC.DrawRectangle(pointXY, pointWH) 573 574 old_pen = DC.GetPen() 575 576 pen = wx.Pen(MakeColor(self.colors[COLOR_3D_LIGHT]), 1, wx.PENSTYLE_SOLID) 577 DC.SetPen(pen) 578 # draw the horizontal hilight 579 startPoint = wx.Point(x + 1, y + 1) 580 endPoint = wx.Point(x + width - 1, y + 1) 581 DC.DrawLine(startPoint, endPoint) 582 583 # draw the vertical hilight 584 startPoint = wx.Point(x + 1, y + 1) 585 endPoint = wx.Point(x + 1, y + height - 2) 586 DC.DrawLine(startPoint, endPoint) 587 588 pen = wx.Pen(MakeColor(self.colors[COLOR_3D_DARK]), 1, wx.PENSTYLE_SOLID) 589 DC.SetPen(pen) 590 591 # draw the horizontal lowlight 592 startPoint = wx.Point(x + 1, y + height - 2) 593 endPoint = wx.Point(x + width - 1, y + height - 2) 594 DC.DrawLine(startPoint, endPoint) 595 596 # draw the vertical lowlight 597 startPoint = wx.Point(x + width - 2, y + 2) 598 endPoint = wx.Point(x + width - 2, y + height - 2) 599 DC.DrawLine(startPoint, endPoint) 600 601 pen = wx.Pen(MakeColor(self.colors[COLOR_FONT]), 1, wx.PENSTYLE_SOLID) 602 603 DC.SetPen(pen) 604 605 point = (x + diffx, y + diffy) 606 DC.DrawText(day, point) 607 cnt_x = cnt_x + 1 608 609 def _CalcFontSize(self, DC, f): 610 if self.num_auto is True: 611 test_size = self.max_num_size # max size 612 test_day = ' 99 ' 613 614 while test_size > 2: 615 f.SetPointSize(test_size) 616 DC.SetFont(f) 617 tw, th = DC.GetTextExtent(test_day) 618 619 if tw < self.cellW and th < self.cellH: 620 sizef = test_size 621 break 622 test_size = test_size - 1 623 else: 624 f.SetPointSize(self.num_size) # set fixed size 625 DC.SetFont(f) 626 627 def DrawNum(self, DC): 628 """ 629 Draw the day numbers 630 631 :param `DC`: the :class:`wx.DC` to use. 632 633 """ 634 f = wx.Font(10, self.fontfamily, self.fontstyle, self.fontweight) # initial font setting 635 self._CalcFontSize(DC, f) 636 637 cnt_x = 0 638 cnt_y = 1 639 for val in self.cal_days: 640 x = self.gridx[cnt_x] 641 y = self.gridy[cnt_y] 642 643 self._DrawDayText(x, y, val, f, DC) 644 645 if cnt_x < 6: 646 cnt_x = cnt_x + 1 647 else: 648 cnt_x = 0 649 cnt_y = cnt_y + 1 650 651 def _DrawDayText(self, x, y, text, font, DC): 652 653 try: 654 num_val = int(text) 655 num_color = self.cal_sel[num_val][0] 656 except Exception: 657 num_color = self.colors[COLOR_FONT] 658 659 DC.SetTextForeground(MakeColor(num_color)) 660 DC.SetFont(font) 661 662 tw, th = DC.GetTextExtent(text) 663 664 if self.num_align_horz == wx.ALIGN_CENTRE: 665 adj_h = (self.cellW - tw) / 2 666 elif self.num_align_horz == wx.ALIGN_RIGHT: 667 adj_h = self.cellW - tw 668 else: 669 adj_h = 0 # left alignment 670 671 adj_h = adj_h + self.num_indent_horz 672 673 if self.num_align_vert == wx.ALIGN_CENTRE: 674 adj_v = (self.cellH - th) / 2 675 elif self.num_align_vert == wx.ALIGN_BOTTOM: 676 adj_v = self.cellH - th 677 else: 678 adj_v = 0 # left alignment 679 680 adj_v = adj_v + self.num_indent_vert 681 682 DC.DrawText(text, (x + adj_h, y + adj_v)) 683 684 def DrawDayText(self, DC, key): 685 """ 686 Draw the day text. 687 688 :param `DC`: the :class:`wx.DC` to use. 689 :param `key`: the day to draw 690 691 """ 692 693 f = wx.Font(10, self.fontfamily, self.fontstyle, self.fontweight) # initial font setting 694 self._CalcFontSize(DC, f) 695 696 if key > self.end_pos: 697 key = self.end_pos 698 699 val = self.cal_days[key] 700 cnt_x = key % 7 701 cnt_y = int(key / 7) + 1 702 x = self.gridx[cnt_x] 703 y = self.gridy[cnt_y] 704 self._DrawDayText(x, y, val, f, DC) 705 706 def Center(self): 707 """Calculate the dimensions in the center of the drawing area.""" 708 borderW = self.x_mrg * 2 709 borderH = self.y_mrg + self.y_end + self.title_offset 710 711 self.cellW = int((self.sizew - borderW) / 7) 712 self.cellH = int((self.sizeh - borderH) / 7) 713 714 self.restW = ((self.sizew - borderW) % 7) - 1 715 716 # week title adjustment 717 self.weekHdrCellH = int(self.cellH * self.cal_week_scale) 718 # recalculate the cell height exkl. the week header and 719 # subtracting the size 720 self.cellH = int((self.sizeh - borderH - self.weekHdrCellH) / 6) 721 722 self.restH = ((self.sizeh - borderH - self.weekHdrCellH) % 6) - 1 723 self.calW = self.cellW * 7 724 self.calH = self.cellH * 6 + self.weekHdrCellH 725 726 # highlighted selected days 727 def DrawSel(self, DC): 728 """ 729 Highlight selected days. 730 731 :param `DC`: the :class:`wx.DC` to use 732 733 """ 734 for key in self.cal_sel.keys(): 735 sel_color = self.cal_sel[key][1] 736 brush = wx.Brush(MakeColor(sel_color), wx.BRUSHSTYLE_SOLID) 737 DC.SetBrush(brush) 738 739 if self.hide_grid is False: 740 DC.SetPen(wx.Pen(MakeColor(self.colors[COLOR_GRID_LINES]), 0)) 741 else: 742 DC.SetPen(wx.Pen(MakeColor(self.colors[COLOR_BACKGROUND]), 0)) 743 744 nkey = key + self.st_pos - 1 745 rect = self.rg[nkey] 746 747 DC.DrawRectangle(rect) 748 749 def DrawGrid(self, DC): 750 """ 751 Calculate and draw the grid lines. 752 753 :param `DC`: the :class:`wx.DC` to use 754 755 """ 756 DC.SetPen(wx.Pen(MakeColor(self.colors[COLOR_GRID_LINES]), 0)) 757 758 self.gridx = [] 759 self.gridy = [] 760 761 self.x_st = self.cx_st + self.x_mrg 762 # start postion of draw 763 self.y_st = self.cy_st + self.y_mrg + self.title_offset 764 765 x1 = self.x_st 766 y1 = self.y_st 767 y2 = y1 + self.calH + self.restH 768 769 for i in range(8): 770 if i == 7: 771 x1 = x1 + self.restW 772 773 if self.hide_grid is False: 774 DC.DrawLine((x1, y1), (x1, y2)) 775 776 self.gridx.append(x1) 777 778 x1 = x1 + self.cellW 779 780 x1 = self.x_st 781 y1 = self.y_st 782 x2 = x1 + self.calW + self.restW 783 784 for i in range(8): 785 if i == 7: 786 y1 = y1 + self.restH 787 788 if self.hide_grid is False: 789 DC.DrawLine((x1, y1), (x2, y1)) 790 791 self.gridy.append(y1) 792 793 if i == 0: 794 y1 = y1 + self.weekHdrCellH 795 else: 796 y1 = y1 + self.cellH 797 798 def GetColor(self, name): 799 """ 800 Get a color. 801 802 :param `name`: one of the defined color names. 803 804 """ 805 return MakeColor(self.colors[name]) 806 807 def SetColor(self, name, value): 808 """ 809 Set a color. 810 811 :param `name`: the name to assign the color too. 812 :param `value`: the color to use, see :class:`wx.Colour` 813 814 """ 815 self.colors[name] = MakeColor(value) 816 817 818class PrtCalDraw(CalDraw): 819 """A class to optimize :class:`CalDraw` for printing.""" 820 def InitValues(self): 821 """Set initial values.""" 822 self.rg = {} 823 self.cal_sel = {} 824 # start draw border location 825 self.set_cx_st = 1.0 826 self.set_cy_st = 1.0 827 828 # draw offset position 829 self.set_y_mrg = 0.2 830 self.set_x_mrg = 0.2 831 self.set_y_end = 0.2 832 833 def SetPSize(self, pwidth, pheight): 834 """Calculate the dimensions in the center of the drawing area.""" 835 self.pwidth = int(pwidth) / self.scale 836 self.pheight = int(pheight) / self.scale 837 838 def SetPreview(self, preview): 839 """ 840 Set the preview. 841 842 :param `preview`: set the preview state??? 843 844 """ 845 self.preview = preview 846 847 848class Calendar(wx.Control): 849 """A calendar control class.""" 850 def __init__(self, parent, id=-1, pos=wx.DefaultPosition, 851 size=wx.Size(200, 200), style=0, validator=wx.DefaultValidator, 852 name="calendar"): 853 """ 854 Default class constructor. 855 856 :param wx.Window `parent`: parent window. Must not be ``None``; 857 :param integer `id`: window identifier. A value of -1 indicates a default value; 858 :param `pos`: the control position. A value of (-1, -1) indicates a default position, 859 chosen by either the windowing system or wxPython, depending on platform; 860 :type `pos`: tuple or :class:`wx.Point` 861 :param `size`: the control size. A value of (-1, -1) indicates a default size, 862 chosen by either the windowing system or wxPython, depending on platform; 863 :type `size`: tuple or :class:`wx.Size` 864 :param integer `style`: the button style (unused); 865 :param wx.Validator `validator`: the validator associated to the button; 866 :param string `name`: the calendar name. 867 868 """ 869 wx.Control.__init__(self, parent, id, pos, size, style | wx.WANTS_CHARS, validator, name) 870 871 self.hasFocus = False 872 # set the calendar control attributes 873 874 self.hide_grid = False 875 self.hide_title = False 876 self.show_weekend = False 877 self.cal_type = "NORMAL" 878 self.outer_border = True 879 self.num_align_horz = wx.ALIGN_CENTRE 880 self.num_align_vert = wx.ALIGN_CENTRE 881 self.colors = DefaultColors() 882 self.set_x_mrg = 1 883 self.set_y_mrg = 1 884 self.set_y_end = 1 885 886 self.select_list = [] 887 888 self.SetBackgroundColour(MakeColor(self.colors[COLOR_BACKGROUND])) 889 self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftEvent) 890 self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDEvent) 891 self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightEvent) 892 self.Bind(wx.EVT_RIGHT_DCLICK, self.OnRightDEvent) 893 self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleEvent) 894 self.Bind(wx.EVT_MIDDLE_DCLICK, self.OnMiddleDEvent) 895 self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus) 896 self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) 897 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) 898 899 self.sel_key = None # last used by 900 self.sel_lst = [] # highlighted selected days 901 902 # default calendar for current month 903 self.SetNow() 904 905 self.size = None 906 907 self.Bind(wx.EVT_PAINT, self.OnPaint) 908 self.Bind(wx.EVT_SIZE, self.OnSize) 909 910 def AcceptsFocus(self): 911 """Can it accept focus?""" 912 return self.IsShown() and self.IsEnabled() 913 914 def GetColor(self, name): 915 """ 916 Get a color. 917 918 :param `name`: a valid color name, can be defined using :meth:`SetColor` 919 920 """ 921 return MakeColor(self.colors[name]) 922 923 def SetColor(self, name, value): 924 """ 925 Set a color. 926 927 :param `name`: the name to be assigned to the color. 928 :param `value`: the color value, see :class:`wx.Colour` for valid values 929 930 """ 931 self.colors[name] = MakeColor(value) 932 933 def HideTitle(self): 934 """Hide the calendar title.""" 935 self.hide_title = True 936 937 def HideGrid(self): 938 """Hide the calendar grid.""" 939 self.hide_grid = True 940 941 def ProcessClick(self, event): 942 """Determine the calendar rectangle click area and draw a selection.""" 943 self.SetFocus() 944 self.x, self.y = event.GetX(), event.GetY() 945 self.shiftkey = event.ShiftDown() 946 self.ctrlkey = event.ControlDown() 947 key = self.GetDayHit(self.x, self.y) 948 self.SelectDay(key) 949 950 def OnLeftEvent(self, event): 951 """Left mouse click event handler.""" 952 self.click = 'LEFT' 953 self.ProcessClick(event) 954 955 def OnLeftDEvent(self, event): 956 """Left double mouse click event handler.""" 957 self.click = 'DLEFT' 958 self.ProcessClick(event) 959 960 def OnRightEvent(self, event): 961 """Right mouse click event handler.""" 962 self.click = 'RIGHT' 963 self.ProcessClick(event) 964 965 def OnRightDEvent(self, event): 966 """Right double mouse click event handler.""" 967 self.click = 'DRIGHT' 968 self.ProcessClick(event) 969 970 def OnMiddleEvent(self, event): 971 """Middle mouse click event handler.""" 972 self.click = 'MIDDLE' 973 self.ProcessClick(event) 974 975 def OnMiddleDEvent(self, event): 976 """Middle double mouse click event handler.""" 977 self.click = 'DMIDDLE' 978 self.ProcessClick(event) 979 980 def OnSetFocus(self, event): 981 """Set focus event handler.""" 982 self.hasFocus = True 983 self.DrawFocusIndicator(True) 984 985 def OnKillFocus(self, event): 986 """Kill focus event handler.""" 987 self.hasFocus = False 988 self.DrawFocusIndicator(False) 989 990 def OnKeyDown(self, event): 991 """Key down event handler.""" 992 if not self.hasFocus: 993 event.Skip() 994 return 995 996 key_code = event.GetKeyCode() 997 998 if key_code == wx.WXK_TAB: 999 forward = not event.ShiftDown() 1000 ne = wx.NavigationKeyEvent() 1001 ne.SetDirection(forward) 1002 ne.SetCurrentFocus(self) 1003 ne.SetEventObject(self) 1004 self.GetParent().GetEventHandler().ProcessEvent(ne) 1005 event.Skip() 1006 return 1007 1008 self.shiftkey = event.ShiftDown() 1009 self.ctrlkey = event.ControlDown() 1010 self.click = 'KEY' 1011 delta = None 1012 1013 if key_code == wx.WXK_UP: 1014 delta = -7 1015 elif key_code == wx.WXK_DOWN: 1016 delta = 7 1017 elif key_code == wx.WXK_LEFT: 1018 delta = -1 1019 elif key_code == wx.WXK_RIGHT: 1020 delta = 1 1021 elif key_code == wx.WXK_HOME: 1022 curDate = wx.DateTime.FromDMY(int(self.cal_days[self.sel_key]), self.month - 1, self.year) 1023 curDate.SetHour(12) # leave a margin for the occasional DST crossing 1024 newDate = wx.DateTime.Today().SetHour(12) 1025 ts = newDate - curDate 1026 delta = ts.GetDays() 1027 1028 if delta is not None: 1029 curDate = wx.DateTime.FromDMY(int(self.cal_days[self.sel_key]), self.month - 1, self.year) 1030 curDate.SetHour(12) # leave a margin for the occasional DST crossing 1031 timeSpan = wx.TimeSpan.Days(delta) 1032 newDate = curDate + timeSpan 1033 1034 if curDate.GetMonth() == newDate.GetMonth(): 1035 self.set_day = self.day = newDate.GetDay() 1036 key = self.sel_key + delta 1037 self.SelectDay(key) 1038 else: 1039 self.month = newDate.GetMonth() + 1 1040 self.year = newDate.GetYear() 1041 self.set_day = self.day = newDate.GetDay() 1042 self.sel_key = None 1043 self.Refresh() 1044 self._EmitCalendarEvent() 1045 event.Skip() 1046 1047 def SetSize(self, set_size): 1048 """ 1049 Set the size. 1050 1051 :param `set_size`: the control size. A value of (-1, -1) indicates a default size, 1052 chosen by either the windowing system or wxPython, depending on platform; 1053 :type `set_size`: tuple or :class:`wx.Size` 1054 1055 """ 1056 self.size = set_size 1057 1058 def SetSelDay(self, sel): 1059 """ 1060 Set the days to highlight. 1061 1062 :param list `sel`: the list of days to highlight 1063 1064 """ 1065 self.sel_lst = sel 1066 1067 def SetNow(self): 1068 """Set the current day.""" 1069 dt = datetime.date.today() 1070 self.SetDate(dt.day, dt.month, dt.year) 1071 1072 def SetCurrentDay(self): # legacy, this is now an alias for SetNow 1073 """Set the current day to today.""" 1074 self.SetNow() 1075 1076 # get the date, day, month, year set in calendar 1077 1078 def GetDate(self): 1079 """ 1080 Get the set calendar date. 1081 1082 :returns: the day, the month and the year 1083 1084 """ 1085 return self.day, self.month, self.year 1086 1087 def SetDate(self, day, month, year): 1088 """ 1089 Set a calendar date. 1090 1091 :param int `day`: the day 1092 :param int `month`: the month 1093 :param int `year`: the year 1094 :raises: `ValueError` when setting an invalid month/year 1095 :returns: the new date set. 1096 1097 """ 1098 datetime.date(year, month, 1) # let the possible ValueError propagate 1099 try: 1100 datetime.date(year, month, day) 1101 except ValueError: 1102 day = wx.DateTime().GetLastMonthDay(month-1, year).GetDay() 1103 self.year = year 1104 self.month = month 1105 self.set_day = self.day = day 1106 self.sel_key = None 1107 self.Refresh() 1108 return self.GetDate() 1109 1110 def GetDay(self): 1111 """ 1112 Get the set calendar day. 1113 1114 :returns: the day 1115 1116 """ 1117 return self.day 1118 1119 def GetMonth(self): 1120 """ 1121 Get the set calendar month. 1122 1123 :returns: the month 1124 1125 """ 1126 return self.month 1127 1128 def GetYear(self): 1129 """ 1130 Get the set calendar year. 1131 1132 :returns: the year 1133 1134 """ 1135 return self.year 1136 1137 def SetDayValue(self, day): 1138 """ 1139 Set the day. 1140 1141 :param int `day`: the day 1142 :raises: `ValueError` if the resulting date is invalid. 1143 1144 """ 1145 self.SetDate(day, self.month, self.year) 1146 1147 def SetMonth(self, month): 1148 """ 1149 Set the Month. 1150 1151 :param int `month`: the month 1152 :raises: `ValueError` if the resulting date is invalid. 1153 1154 """ 1155 self.SetDate(self.day, month, self.year) 1156 1157 def SetYear(self, year): 1158 """ 1159 Set the year. 1160 1161 :param int `year`: the year 1162 :raises: `ValueError` if the resulting date is invalid. 1163 1164 """ 1165 self.SetDate(self.day, self.month, year) 1166 1167 def MoveDate(self, months=0, years=0): 1168 """ 1169 Move the current date by a given interval of months/years. 1170 1171 :param int `months`: months to add (can be negative) 1172 :param int `years`: years to add (can be negative) 1173 :returns: the new date set. 1174 1175 """ 1176 cur_date = wx.DateTime.FromDMY(self.day, self.month-1, self.year) 1177 new_date = cur_date + wx.DateSpan(years=years, months=months) 1178 self.SetDate(new_date.GetDay(), new_date.GetMonth()+1, new_date.GetYear()) 1179 return self.GetDate() 1180 1181 def IncYear(self): 1182 """Increment the year by 1.""" 1183 return self.MoveDate(years=1) 1184 1185 def DecYear(self): 1186 """Decrement the year by 1.""" 1187 return self.MoveDate(years=-1) 1188 1189 def IncMonth(self): 1190 """Increment the month by 1.""" 1191 return self.MoveDate(months=1) 1192 1193 def DecMonth(self): 1194 """Decrement the month by 1.""" 1195 return self.MoveDate(months=-1) 1196 1197 def TestDay(self, key): 1198 """ 1199 Test to see if the selection has a date and create event. 1200 1201 :param `key`: the day to test 1202 1203 """ 1204 try: 1205 self.day = int(self.cal_days[key]) 1206 except Exception: 1207 return None 1208 1209 if self.day == "": 1210 return None 1211 else: 1212 self._EmitCalendarEvent() 1213 self.set_day = self.day 1214 return key 1215 1216 def GetDayHit(self, mx, my): 1217 """ 1218 Find the clicked area rectangle. 1219 1220 :param `mx`: the x position 1221 :param `my`: the y positon 1222 1223 """ 1224 for key in self.rg.keys(): 1225 val = self.rg[key] 1226 ms_rect = wx.Rect(mx, my, 1, 1) 1227 if wx.IntersectRect(ms_rect, val) is not None: 1228 result = self.TestDay(key) 1229 return result 1230 1231 return None 1232 1233 def SetWeekColor(self, font_color, week_color): 1234 """ 1235 Set the week title color. 1236 1237 :param `font_color`: the font color to use. 1238 :param `week_color`: the week color to use for the background. 1239 """ 1240 self.colors[COLOR_HEADER_FONT] = MakeColor(font_color) 1241 self.colors[COLOR_HEADER_BACKGROUND] = MakeColor(week_color) 1242 self.colors[COLOR_3D_LIGHT] = MakeColor(week_color) 1243 self.colors[COLOR_3D_DARK] = MakeColor(week_color) 1244 1245 def SetTextAlign(self, vert, horz): 1246 """ 1247 Set the text allignment. 1248 1249 :param `vert`: the vertical allignment 1250 :param `horz`: the horizontal allignment 1251 1252 """ 1253 self.num_align_horz = horz 1254 self.num_align_vert = vert 1255 1256 def AddSelect(self, list, font_color, back_color): 1257 """ 1258 Add a selection. 1259 1260 :param `list`: list of days to select 1261 :param `font_color`: the font color to use 1262 :param `back_color`: the back color to use 1263 1264 """ 1265 list_val = [list, font_color, back_color] 1266 self.select_list.append(list_val) 1267 1268 def ShowWeekEnd(self): 1269 """Highlight the weekend.""" 1270 self.show_weekend = True 1271 1272 def SetBusType(self): 1273 """Set the calendar type to 'BUS'.""" 1274 self.cal_type = "BUS" 1275 1276 def OnSize(self, evt): 1277 """The on size event handler.""" 1278 self.Refresh(False) 1279 evt.Skip() 1280 1281 def OnPaint(self, event): 1282 """The on paint event handler.""" 1283 DC = wx.PaintDC(self) 1284 self.DoDrawing(DC) 1285 1286 def DoDrawing(self, DC): 1287 """ 1288 Do the drawing. 1289 1290 :param `DC`: the :class:`wx.DC` to draw 1291 1292 """ 1293 try: 1294 cal = self.caldraw 1295 except Exception: 1296 self.caldraw = CalDraw(self) 1297 cal = self.caldraw 1298 1299 cal.hide_grid = self.hide_grid 1300 cal.hide_title = self.hide_title 1301 cal.show_weekend = self.show_weekend 1302 cal.cal_type = self.cal_type 1303 cal.outer_border = self.outer_border 1304 cal.num_align_horz = self.num_align_horz 1305 cal.num_align_vert = self.num_align_vert 1306 cal.colors = self.colors 1307 1308 if self.size is None: 1309 size = self.GetClientSize() 1310 else: 1311 size = self.size 1312 1313 # drawing attributes 1314 1315 cal.SetSize(size) 1316 cal.SetCal(self.year, self.month) 1317 1318 # these have to set after SetCal as SetCal would overwrite them again. 1319 cal.set_x_mrg = self.set_x_mrg 1320 cal.set_y_mrg = self.set_y_mrg 1321 cal.set_y_end = self.set_y_end 1322 1323 for val in self.select_list: 1324 cal.AddSelect(val[0], val[1], val[2]) 1325 1326 cal.DrawCal(DC, self.sel_lst) 1327 1328 self.rg = cal.GetRect() 1329 self.cal_days = cal.GetCal() 1330 self.st_pos = cal.GetOffset() 1331 self.ymax = DC.MaxY() 1332 1333 if self.set_day is not None: 1334 self.SetDay(self.set_day) 1335 1336 def DrawFocusIndicator(self, draw): 1337 """ 1338 Draw the focus indicator or a border. 1339 1340 :param `draw`: True draws the focus indicator, False a border 1341 1342 """ 1343 DC = wx.ClientDC(self) 1344 try: 1345 if draw is True: 1346 self.caldraw.DrawFocusIndicator(DC) 1347 else: 1348 self.caldraw.DrawBorder(DC, True) 1349 except Exception: 1350 pass 1351 1352 def DrawRect(self, key, bgcolor='WHITE', fgcolor='PINK', width=0): 1353 """ 1354 Draw a rectangle. 1355 1356 :param `key`: the day to draw the rectangle on 1357 :param `bgcolor`: the background color 1358 1359 """ 1360 if key is None: 1361 return 1362 1363 DC = wx.ClientDC(self) 1364 1365 brush = wx.Brush(MakeColor(bgcolor)) 1366 DC.SetBrush(brush) 1367 1368 DC.SetPen(wx.TRANSPARENT_PEN) 1369 1370 rect = self.rg[key] 1371 DC.DrawRectangle(rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2) 1372 1373 self.caldraw.DrawDayText(DC, key) 1374 1375 def DrawRectOrg(self, key, fgcolor='BLACK', width=0): 1376 """ 1377 Draw a rectangle. 1378 1379 :param `key`: the day to draw the rectangle on 1380 :param `fgcolor`: the color for the pen 1381 :param `width`: the width for the pen 1382 1383 """ 1384 if key is None: 1385 return 1386 1387 DC = wx.ClientDC(self) 1388 1389 brush = wx.Brush(wx.Colour(0, 0xFF, 0x80), wx.TRANSPARENT) 1390 DC.SetBrush(brush) 1391 1392 try: 1393 DC.SetPen(wx.Pen(MakeColor(fgcolor), width)) 1394 except Exception: 1395 DC.SetPen(wx.Pen(MakeColor(self.GetColor(COLOR_GRID_LINES)), width)) 1396 1397 rect = self.rg[key] 1398 DC.DrawRectangle(rect) 1399 1400 def SetDay(self, day): 1401 """ 1402 Set the day. 1403 1404 :param `day`: the day to select 1405 1406 """ 1407 d = day + self.st_pos - 1 1408 self.SelectDay(d) 1409 1410 def IsDayInWeekend(self, key): 1411 """ 1412 Is the day in the weekend 1413 1414 :param `key`: the day to check 1415 1416 """ 1417 try: 1418 day = self.cal_days[key] 1419 day = int(day) + wx.DateTime.FromDMY(day, self.month-1, self.year).GetWeekDay() 1420 1421 if day % 7 == 6 or day % 7 == 0: 1422 return True 1423 except Exception: 1424 return False 1425 1426 def SelectDay(self, key): 1427 """ 1428 Select the day. 1429 1430 :param `key`: The day to select 1431 1432 """ 1433 sel_size = 1 1434 # clear large selection 1435 1436 if self.sel_key is not None: 1437 (cfont, bgcolor) = self.__GetColorsForDay(self.sel_key) 1438 self.DrawRect(self.sel_key, bgcolor, cfont, sel_size) 1439 1440 self.DrawRect(key, self.GetColor(COLOR_HIGHLIGHT_BACKGROUND), self.GetColor(COLOR_HIGHLIGHT_FONT), sel_size) 1441 # store last used by 1442 self.sel_key = key 1443 1444 def SetMargin(self, xmarg, ymarg): 1445 """ 1446 Set the margins 1447 1448 :param `xmarg`: the 'x' margin 1449 :param `ymarg`: the 'y' margin 1450 1451 """ 1452 self.set_x_mrg = xmarg 1453 self.set_y_mrg = ymarg 1454 self.set_y_end = ymarg 1455 1456 def __GetColorsForDay(self, key): 1457 cfont = self.GetColor(COLOR_FONT) 1458 bgcolor = self.GetColor(COLOR_BACKGROUND) 1459 1460 if self.IsDayInWeekend(key) is True and self.show_weekend is True: 1461 cfont = self.GetColor(COLOR_WEEKEND_FONT) 1462 bgcolor = self.GetColor(COLOR_WEEKEND_BACKGROUND) 1463 1464 try: 1465 dayIdx = int(self.cal_days[key]) 1466 (cfont, bgcolor) = self.caldraw.cal_sel[dayIdx] 1467 except Exception: 1468 pass 1469 1470 return (cfont, bgcolor) 1471 1472 def _EmitCalendarEvent(self): 1473 evt = wx.PyCommandEvent(wxEVT_COMMAND_PYCALENDAR_DAY_CLICKED, self.GetId()) 1474 evt.click, evt.day, evt.month, evt.year = self.click, self.day, self.month, self.year 1475 evt.shiftkey = self.shiftkey 1476 evt.ctrlkey = self.ctrlkey 1477 self.GetEventHandler().ProcessEvent(evt) 1478 1479 1480class CalenDlg(wx.Dialog): 1481 """A dialog with a calendar control.""" 1482 def __init__(self, parent, month=None, day=None, year=None): 1483 """ 1484 Default class constructor. 1485 1486 :param wx.Window `parent`: parent window. Must not be ``None``; 1487 :param integer `month`: the month, if None the current day will be used 1488 :param integer `day`: the day 1489 :param integer `year`: the year 1490 1491 """ 1492 wx.Dialog.__init__(self, parent, -1, "Event Calendar", wx.DefaultPosition, (280, 360)) 1493 self.result = None 1494 1495 # set the calendar and attributes 1496 self.calend = Calendar(self, -1, (20, 60), (240, 200)) 1497 self.calend.HideTitle() 1498 today = datetime.date.today() 1499 d = day or today.day 1500 m = month or today.month 1501 y = year or today.year 1502 try: 1503 date = datetime.date(y, m, d) 1504 except ValueError: 1505 date = today 1506 self.calend.SetDate(date.day, date.month, date.year) 1507 1508 # get month list from DateTime 1509 monthlist = GetMonthList() 1510 1511 # select the month 1512 self.m_date = wx.ComboBox(self, pos=(20, 20), size=(90, -1), 1513 style=wx.CB_DROPDOWN|wx.TE_READONLY) 1514 for n, month_name in enumerate(monthlist): 1515 self.m_date.Append(month_name, n+1) 1516 self.m_date.SetSelection(date.month-1) 1517 self.Bind(wx.EVT_COMBOBOX, self.EvtComboBox, self.m_date) 1518 1519 # alternate spin button to control the month 1520 h = self.m_date.GetSize().height 1521 self.m_spin = wx.SpinButton(self, -1, (115, 20), (h * 1.5, h), wx.SP_VERTICAL) 1522 self.m_spin.SetRange(1, 12) 1523 self.m_spin.SetValue(date.month) 1524 self.Bind(wx.EVT_SPIN, self.OnMonthSpin, self.m_spin) 1525 1526 # spin button to control the year 1527 self.y_date = wx.TextCtrl(self, -1, str(date.year), (160, 20), (60, -1)) 1528 h = self.y_date.GetSize().height 1529 1530 self.y_spin = wx.SpinButton(self, -1, (225, 20), (h * 1.5, h), wx.SP_VERTICAL) 1531 self.y_spin.SetRange(date.year-100, date.year+100) 1532 self.y_spin.SetValue(date.year) 1533 1534 self.Bind(wx.EVT_SPIN, self.OnYrSpin, self.y_spin) 1535 self.Bind(EVT_CALENDAR, self.MouseClick, self.calend) 1536 1537 x_pos = 50 1538 y_pos = 280 1539 but_size = (60, 25) 1540 1541 btn = wx.Button(self, wx.ID_OK, ' Ok ', (x_pos, y_pos), but_size) 1542 self.Bind(wx.EVT_BUTTON, self.OnOk, btn) 1543 1544 btn = wx.Button(self, wx.ID_CANCEL, ' Close ', (x_pos + 120, y_pos), but_size) 1545 self.Bind(wx.EVT_BUTTON, self.OnCancel, btn) 1546 1547 self.current_month = date.month 1548 self.current_year = date.year 1549 1550 def OnOk(self, evt): 1551 """The OK event handler.""" 1552 self.result = ['None', str(self.calend.day), Month[self.calend.month], str(self.calend.year)] 1553 self.EndModal(wx.ID_OK) 1554 1555 def OnCancel(self, event): 1556 """The Cancel event handler.""" 1557 self.EndModal(wx.ID_CANCEL) 1558 1559 def MouseClick(self, evt): 1560 """The mouse click event handler.""" 1561 # result click type and date 1562 self.result = [evt.click, str(evt.day), Month[evt.month], str(evt.year)] 1563 if evt.click == 'DLEFT': 1564 self.EndModal(wx.ID_OK) 1565 if evt.month != self.current_month or evt.year != self.current_year: 1566 self.m_date.SetSelection(evt.month-1) 1567 self.m_spin.SetValue(evt.month) 1568 self.y_date.SetValue(str(evt.year)) 1569 self.y_spin.SetValue(evt.year) 1570 self.current_month = evt.month 1571 self.current_year = evt.year 1572 1573 def OnMonthSpin(self, event): 1574 """The month spin control event handler.""" 1575 month = self.m_spin.GetValue() 1576 self.m_date.SetSelection(month-1) 1577 self.calend.SetMonth(month) 1578 self.current_month = month 1579 1580 def OnYrSpin(self, event): 1581 """The year spin control event handler.""" 1582 year = self.y_spin.GetValue() 1583 self.y_date.SetValue(str(year)) 1584 self.calend.SetYear(year) 1585 self.current_year = year 1586 1587 def EvtComboBox(self, event): 1588 """The month combobox event handler.""" 1589 month = event.GetClientData() 1590 self.m_spin.SetValue(month) 1591 self.calend.SetMonth(month) 1592 self.current_month = month 1593 1594 def ResetDisplay(self): 1595 """Reset the display.""" 1596 self.calend.Refresh() 1597