Package Gnumed :: Package timelinelib :: Package calendar :: Package gregorian :: Package timepicker :: Module datetime
[frames] | no frames]

Source Code for Module Gnumed.timelinelib.calendar.gregorian.timepicker.datetime

  1  # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg 
  2  # 
  3  # This file is part of Timeline. 
  4  # 
  5  # Timeline is free software: you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation, either version 3 of the License, or 
  8  # (at your option) any later version. 
  9  # 
 10  # Timeline is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with Timeline.  If not, see <http://www.gnu.org/licenses/>. 
 17   
 18   
 19  import os.path 
 20   
 21  import wx.adv 
 22   
 23  from timelinelib.calendar.gregorian.gregorian import GregorianDateTime 
 24  from timelinelib.calendar.gregorian.time import GregorianTime 
 25  from timelinelib.calendar.gregorian.timepicker.date import GregorianDatePicker 
 26  from timelinelib.calendar.gregorian.timepicker.time import GregorianTimePicker 
 27  from timelinelib.calendar.gregorian.timetype import GregorianTimeType 
 28  from timelinelib.config.paths import ICONS_DIR 
 29  from timelinelib.wxgui.utils import display_information_message 
 30   
 31   
 32  ERROR_MESSAGE = _("The date control can't handle the given date") 
 33   
 34   
35 -class GregorianDateTimePicker(wx.Panel):
36
37 - def __init__(self, parent, show_time=True, config=None, on_change=None):
38 wx.Panel.__init__(self, parent) 39 self.config = config 40 self._create_gui() 41 self.controller = GregorianDateTimePickerController(self, 42 self.date_picker, 43 self.time_picker, 44 GregorianTimeType().now, on_change) 45 self.show_time(show_time) 46 self.parent = parent
47
48 - def PopupCalendar(self, evt, wx_date):
49 calendar_popup = CalendarPopup(self, wx_date, self.config) 50 calendar_popup.Bind(wx.adv.EVT_CALENDAR_SEL_CHANGED, 51 self._calendar_on_date_changed) 52 calendar_popup.Bind(wx.adv.EVT_CALENDAR, 53 self._calendar_on_date_changed_dclick) 54 btn = evt.GetEventObject() 55 pos = btn.ClientToScreen((0, 0)) 56 sz = btn.GetSize() 57 calendar_popup.Position(pos, (0, sz[1])) 58 calendar_popup.Popup() 59 self.calendar_popup = calendar_popup
60
61 - def on_return(self):
62 try: 63 self.parent.on_return() 64 except AttributeError: 65 pass
66
67 - def on_escape(self):
68 try: 69 self.parent.on_escape() 70 except AttributeError: 71 pass
72
73 - def show_time(self, show=True):
74 self.time_picker.Show(show) 75 self.GetSizer().Layout()
76
77 - def get_value(self):
78 try: 79 return self.controller.get_value() 80 except ValueError: 81 pass
82
83 - def set_value(self, value):
85
86 - def _create_gui(self):
87 self.date_picker = self._create_date_picker() 88 image = wx.Bitmap(os.path.join(ICONS_DIR, "calendar.bmp")) 89 self.date_button = wx.BitmapButton(self, bitmap=image) 90 self.Bind(wx.EVT_BUTTON, self._date_button_on_click, self.date_button) 91 self.time_picker = GregorianTimePicker(self, self.config) 92 # Layout 93 sizer = wx.BoxSizer(wx.HORIZONTAL) 94 sizer.Add(self.date_picker, proportion=1, 95 flag=wx.ALIGN_CENTER_VERTICAL) 96 sizer.Add(self.date_button, proportion=0, 97 flag=wx.ALIGN_CENTER_VERTICAL) 98 sizer.Add(self.time_picker, proportion=0, 99 flag=wx.ALIGN_CENTER_VERTICAL) 100 self.SetSizerAndFit(sizer)
101
102 - def _create_date_picker(self):
104
105 - def _date_button_on_click(self, evt):
107
108 - def _out_of_date_range(self, wx_date):
109 """It's is a limitation in the wx.adv.CalendarCtrl class 110 that has this date limit.""" 111 return str(wx_date) < '1601-01-01 00:00:00'
112
113 - def _calendar_on_date_changed(self, evt):
114 wx_date = evt.GetEventObject().GetDate() 115 date = self.controller.wx_date_to_date_tuple(wx_date) 116 self.date_picker.SetGregorianDate(date)
117
118 - def _calendar_on_date_changed_dclick(self, evt):
119 self.time_picker.SetFocus() 120 self.calendar_popup.Dismiss()
121 122
123 -class GregorianDateTimePickerController(object):
124
125 - def __init__(self, view, date_picker, time_picker, now_fn, on_change):
126 self._view = view 127 self.date_picker = date_picker 128 self.time_picker = time_picker 129 self.now_fn = now_fn 130 self.on_change = on_change
131
132 - def get_value(self):
133 if self.time_picker.IsShown(): 134 hour, minute, second = self.time_picker.GetGregorianTime() 135 else: 136 hour, minute, second = (0, 0, 0) 137 year, month, day = self.date_picker.GetGregorianDate() 138 return GregorianDateTime(year, month, day, hour, minute, second).to_time()
139
140 - def set_value(self, time):
141 if time is None: 142 time = self.now_fn() 143 self.date_picker.SetGregorianDate(GregorianDateTime.from_time(time).to_date_tuple()) 144 self.time_picker.SetGregorianTime(GregorianDateTime.from_time(time).to_time_tuple()) 145 if self.on_change is not None: 146 self.on_change()
147
148 - def date_tuple_to_wx_date(self, date):
149 year, month, day = date 150 return wx.DateTime.FromDMY(day, month - 1, year, 0, 0, 0)
151
152 - def wx_date_to_date_tuple(self, wx_date):
153 return (wx_date.year, wx_date.month + 1, wx_date.day)
154
155 - def date_button_on_click(self, evt):
156 try: 157 dt = self.date_picker.GetGregorianDate() 158 wx_date = self.date_tuple_to_wx_date(dt) 159 except ValueError: 160 wx_date = wx.DateTime.Now() 161 except wx._core.PyAssertionError: 162 display_information_message('wx.DateTime limitation', ERROR_MESSAGE) 163 else: 164 try: 165 self._view.PopupCalendar(evt, wx_date) 166 except wx._core.PyAssertionError: 167 display_information_message('GUI control limitation', ERROR_MESSAGE)
168 169
170 -class CalendarPopup(wx.PopupTransientWindow):
171
172 - def __init__(self, parent, wx_date, config):
173 self.config = config 174 wx.PopupTransientWindow.__init__(self, parent, flags=wx.BORDER_NONE) 175 self._create_gui(wx_date) 176 self.controller = CalendarPopupController(self) 177 self._bind_events()
178
179 - def _create_gui(self, wx_date):
180 BORDER = 2 181 self.cal = self._create_calendar_control(wx_date, BORDER) 182 size = self.cal.GetBestSize() 183 self.SetSize((size.width + BORDER * 2, size.height + BORDER * 2))
184
185 - def _create_calendar_control(self, wx_date, border):
186 style = self._get_cal_style() 187 cal = wx.adv.CalendarCtrl(self, -1, wx_date, 188 pos=(border, border), style=style) 189 self._set_cal_range(cal) 190 return cal
191
192 - def _get_cal_style(self):
193 style = (wx.adv.CAL_SHOW_HOLIDAYS | 194 wx.adv.CAL_SEQUENTIAL_MONTH_SELECTION) 195 if self.config.get_week_start() == "monday": 196 style |= wx.adv.CAL_MONDAY_FIRST 197 else: 198 style |= wx.adv.CAL_SUNDAY_FIRST 199 return style
200
201 - def _set_cal_range(self, cal):
202 min_date = GregorianTimeType().get_min_time() 203 max_date = GregorianTimeType().get_max_time() 204 min_date = self.time_to_wx_date(min_date) 205 max_date = self.time_to_wx_date(max_date) - wx.DateSpan.Day() 206 cal.SetDateRange(min_date, max_date)
207
208 - def time_to_wx_date(self, time):
209 year, month, day = GregorianDateTime.from_time(time).to_date_tuple() 210 try: 211 return wx.DateTime.FromDMY(day, month - 1, year, 0, 0, 0) 212 except OverflowError: 213 if year < 0: 214 year, month, day = GregorianDateTime.from_time(GregorianTime(0, 0)).to_date_tuple() 215 return wx.DateTime.FromDMY(day, month - 1, year, 0, 0, 0)
216
217 - def _bind_events(self):
218 def on_month(evt): 219 self.controller.on_month()
220 221 def on_day(evt): 222 self.controller.on_day()
223 224 self.cal.Bind(wx.adv.EVT_CALENDAR_MONTH, on_month) 225 self.cal.Bind(wx.adv.EVT_CALENDAR_DAY, on_day) 226
227 - def OnDismiss(self):
228 self.controller.on_dismiss()
229 230
231 -class CalendarPopupController(object):
232
233 - def __init__(self, calendar_popup):
234 self.calendar_popup = calendar_popup 235 self.repop = False 236 self.repoped = False
237
238 - def on_month(self):
239 self.repop = True
240
241 - def on_day(self):
242 self.repop = True
243
244 - def on_dismiss(self):
245 # This funny code makes the calender control stay open when you change 246 # month or day. The control is closed on a double-click on a day or 247 # a single click outside of the control 248 if self.repop and not self.repoped: 249 try: 250 self.calendar_popup.Popup() 251 except wx.PyAssertionError: 252 # This happens if you open the calendar popup, clik and hold 253 # down the mouse on a day and thereafter drag the mouse outside 254 # of the calendar control, release the mouse, and click outside 255 # the clandar control. 256 pass 257 self.repoped = True
258