Package Gnumed :: Package timelinelib :: Package canvas :: Module timelinecanvascontroller
[frames] | no frames]

Source Code for Module Gnumed.timelinelib.canvas.timelinecanvascontroller

  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 wx 
 20   
 21  from timelinelib.canvas.appearance import Appearance 
 22  from timelinelib.canvas.backgrounddrawers.defaultbgdrawer import DefaultBackgroundDrawer 
 23  from timelinelib.canvas.drawing import get_drawer 
 24  from timelinelib.canvas.drawing.viewproperties import ViewProperties 
 25  from timelinelib.canvas.eventboxdrawers.defaulteventboxdrawer import DefaultEventBoxDrawer 
 26  from timelinelib.canvas.events import create_timeline_redrawn_event 
 27  from timelinelib import DEBUG_ENABLED 
 28  from timelinelib.monitoring import Monitoring 
 29  from timelinelib.wxgui.components.font import Font 
 30   
 31   
 32  CHOICE_WHOLE_PERIOD = _("Whole Timeline") 
 33  CHOICE_VISIBLE_PERIOD = _("Visible Period") 
 34  CHOICES = (CHOICE_WHOLE_PERIOD, CHOICE_VISIBLE_PERIOD) 
35 36 37 -class TimelineCanvasController(object):
38
39 - def __init__(self, view, drawer=None):
40 """ 41 The purpose of the drawer argument is make testing easier. A test can 42 mock a drawer and use the mock by sending it in the drawer argument. 43 Normally the drawer is collected with the get_drawer() method. 44 """ 45 self.appearance = None 46 self.monitoring = Monitoring() 47 self.view = view 48 self._set_drawing_algorithm(drawer) 49 self.timeline = None 50 self.set_appearance(Appearance()) 51 self.set_event_box_drawer(DefaultEventBoxDrawer()) 52 self.set_background_drawer(self.get_saved_background_drawer()) 53 self._fast_draw = False 54 self._set_initial_values_to_member_variables() 55 self._set_colors_and_styles() 56 self._set_search_choises()
57 58 @property
59 - def scene(self):
60 return self.drawing_algorithm.scene
61
62 - def get_appearance(self):
63 return self.appearance
64
65 - def set_appearance(self, appearance):
66 if self.appearance is not None: 67 self.appearance.unlisten(self._redraw_timeline) 68 self.appearance = appearance 69 self.appearance.listen_for_any(self._redraw_timeline) 70 self.redraw_timeline()
71 74
75 - def set_event_box_drawer(self, event_box_drawer):
76 self.drawing_algorithm.set_event_box_drawer(event_box_drawer)
77
78 - def set_background_drawer(self, drawer):
79 self.drawing_algorithm.set_background_drawer(drawer)
80
81 - def get_timeline(self):
82 return self.timeline
83
84 - def get_view_properties(self):
85 return self.view_properties
86
87 - def set_timeline(self, timeline):
88 """Inform what timeline to draw.""" 89 self._unregister_timeline(self.timeline) 90 if timeline is None: 91 self._set_null_timeline() 92 else: 93 self._set_non_null_timeline(timeline)
94
95 - def use_fast_draw(self, value):
96 self._fast_draw = value
97
98 - def navigate(self, navigation_fn):
99 old_period = self.view_properties.displayed_period 100 new_period = navigation_fn(old_period) 101 MIN_ZOOM_DELTA, min_zoom_error_text = self.time_type.get_min_zoom_delta() 102 if new_period.delta() < MIN_ZOOM_DELTA: 103 raise ValueError(min_zoom_error_text) 104 min_time = self.time_type.get_min_time() 105 if min_time is not None and new_period.start_time < min_time: 106 raise ValueError(_("Can't scroll more to the left")) 107 max_time = self.time_type.get_max_time() 108 if max_time is not None and new_period.end_time > max_time: 109 raise ValueError(_("Can't scroll more to the right")) 110 self.view_properties.displayed_period = new_period 111 self.redraw_timeline()
112
113 - def _set_null_timeline(self):
114 self.timeline = None 115 self.time_type = None 116 self.view.Disable()
117
118 - def _set_non_null_timeline(self, timeline):
119 self.timeline = timeline 120 self.time_type = timeline.get_time_type() 121 self.timeline.register(self._timeline_changed) 122 self.view_properties.unlisten(self._redraw_timeline) 123 properties_loaded = self._load_view_properties() 124 if properties_loaded: 125 self.view_properties.listen_for_any(self._redraw_timeline) 126 self._redraw_timeline() 127 self.view.Enable() 128 self.view.SetFocus()
129
130 - def _load_view_properties(self):
131 properties_loaded = True 132 try: 133 self.view_properties.clear_db_specific() 134 self.timeline.load_view_properties(self.view_properties) 135 if self.view_properties.displayed_period is None: 136 default_tp = self.time_type.get_default_time_period() 137 self.view_properties.displayed_period = default_tp 138 self.view_properties.hscroll_amount = 0 139 except Exception: 140 properties_loaded = False 141 return properties_loaded
142
143 - def _unregister_timeline(self, timeline):
144 if timeline is not None: 145 timeline.unregister(self._timeline_changed)
146
147 - def get_time_period(self):
148 """Return currently displayed time period.""" 149 if self.timeline is None: 150 raise Exception(_("No timeline set")) 151 return self.view_properties.displayed_period
152
153 - def redraw_timeline(self):
154 self._redraw_timeline()
155
156 - def window_resized(self):
157 self._redraw_timeline()
158
160 selected_event_ids = self.view_properties.get_selected_event_ids() 161 nbr_of_selected_event_ids = len(selected_event_ids) 162 return nbr_of_selected_event_ids == 1
163
165 selected_event_ids = self.view_properties.get_selected_event_ids() 166 if len(selected_event_ids) > 0: 167 event_id = selected_event_ids[0] 168 return self.timeline.find_event_with_id(event_id) 169 return None
170
171 - def get_time(self, x):
172 return self.drawing_algorithm.get_time(x)
173
174 - def event_with_rect_at(self, x, y, alt_down=False):
175 return self.drawing_algorithm.event_with_rect_at(x, y, alt_down)
176
177 - def event_at(self, x, y, alt_down=False):
178 return self.drawing_algorithm.event_at(x, y, alt_down)
179
180 - def set_selected(self, event, is_selected):
182
183 - def clear_selected(self):
185
186 - def select_all_events(self):
188
189 - def is_selected(self, event):
191
192 - def set_hovered_event(self, event):
194
195 - def get_hovered_event(self):
196 return self.view_properties.hovered_event
197
198 - def set_selection_rect(self, cursor):
199 self.view_properties.set_selection_rect(cursor.rect) 200 self._fast_draw = True 201 self.redraw_timeline()
202
203 - def remove_selection_rect(self):
204 self.view_properties.set_selection_rect(None) 205 self._fast_draw = True 206 self.redraw_timeline()
207
208 - def get_hscroll_amount(self):
209 return self.view_properties.hscroll_amount
210
211 - def set_hscroll_amount(self, amount):
212 self.view_properties.hscroll_amount = amount
213
214 - def set_period_selection(self, period):
215 if period is None: 216 self.view_properties.period_selection = None 217 else: 218 self.view_properties.period_selection = (period.start_time, period.end_time) 219 self._redraw_timeline()
220
221 - def select_events_in_rect(self, rect):
223
224 - def event_has_sticky_balloon(self, event):
226
227 - def set_event_sticky_balloon(self, event, is_sticky):
230
231 - def add_highlight(self, event, clear):
233
234 - def tick_highlights(self):
235 self.view_properties.tick_highlights(limit=15)
236
237 - def has_higlights(self):
238 return self.view_properties.has_higlights()
239
240 - def get_period_choices(self):
241 return CHOICES
242
243 - def _set_search_choises(self):
244 self._search_choice_functions = { 245 CHOICE_WHOLE_PERIOD: self._choose_whole_period, 246 CHOICE_VISIBLE_PERIOD: self._choose_visible_period 247 }
248
249 - def filter_events(self, events, search_period):
250 return self._search_choice_functions[search_period](events)
251
252 - def _choose_whole_period(self, events):
254
255 - def _choose_visible_period(self, events):
256 events = self.view_properties.filter_events(events) 257 period = self.view_properties.displayed_period 258 return [e for e in events if period.overlaps(e.get_time_period())]
259
260 - def event_is_period(self, event):
261 return self.drawing_algorithm.event_is_period(event.get_time_period())
262
263 - def snap(self, time):
264 return self.drawing_algorithm.snap(time)
265
266 - def get_selected_events(self):
267 return self.timeline.find_event_with_ids( 268 self.view_properties.get_selected_event_ids() 269 )
270
271 - def get_events_in_rect(self, rect):
272 return self.drawing_algorithm.get_events_in_rect(rect)
273
274 - def get_hidden_event_count(self):
275 return self.drawing_algorithm.get_hidden_event_count()
276
277 - def increment_font_size(self):
278 self.drawing_algorithm.increment_font_size() 279 self._redraw_timeline()
280
281 - def decrement_font_size(self):
282 self.drawing_algorithm.decrement_font_size() 283 self._redraw_timeline()
284
285 - def get_closest_overlapping_event(self, event, up):
286 return self.drawing_algorithm.get_closest_overlapping_event(event, up=up)
287
288 - def balloon_at(self, cursor):
289 return self.drawing_algorithm.balloon_at(*cursor.pos)
290
291 - def _timeline_changed(self, state_change):
292 self._redraw_timeline()
293
294 - def _set_initial_values_to_member_variables(self):
295 self.timeline = None 296 self.view_properties = ViewProperties() 297 self.dragscroll_timer_running = False
298
299 - def _set_colors_and_styles(self):
300 """Define the look and feel of the drawing area.""" 301 self.view.SetBackgroundColour(wx.WHITE) 302 self.view.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) 303 self.view.set_default_cursor() 304 self.view.Disable()
305
306 - def _redraw_timeline(self):
307 308 def display_monitor_result(dc): 309 (width, height) = self.view.GetSize() 310 redraw_time = self.monitoring.timer_elapsed_ms 311 self.monitoring.count_timeline_redraw() 312 dc.SetTextForeground((255, 0, 0)) 313 dc.SetFont(Font(12, weight=wx.FONTWEIGHT_BOLD)) 314 index, is_in_transaction, history = self.timeline.transactions_status() 315 dc.DrawText("Undo buffer size: %d" % len(history), width - 300, height - 100) 316 dc.DrawText("Undo buffer pos: %d" % index, width - 300, height - 80) 317 dc.DrawText("Redraw count: %d" % self.monitoring._timeline_redraw_count, width - 300, height - 60) 318 dc.DrawText("Last redraw time: %.3f ms" % redraw_time, width - 300, height - 40)
319 320 def fn_draw(dc): 321 self.monitoring.timer_start() 322 self.drawing_algorithm.draw(dc, self.timeline, self.view_properties, self.appearance, fast_draw=self._fast_draw) 323 self.monitoring.timer_end() 324 if DEBUG_ENABLED: 325 display_monitor_result(dc) 326 self._fast_draw = False 327 328 if self.timeline and self.view_properties.displayed_period: 329 self.view_properties.divider_position = (float(self.view.GetDividerPosition()) / 100.0) 330 self.view.RedrawSurface(fn_draw) 331 self.view.PostEvent(create_timeline_redrawn_event()) 332
333 - def _set_drawing_algorithm(self, drawer):
334 """ 335 The drawer interface: 336 methods: 337 draw(d, t, p, a, f) 338 set_event_box_drawer(d) 339 set_background_drawer(d) 340 get_time(x) 341 event_with_rect_at(x, y, k) 342 event_at(x, y, k) 343 event_is_period(p) 344 snap(t) 345 get_events_in_rect(r) 346 get_hidden_event_count() 347 increment_font_size() 348 decrement_font_size() 349 get_closest_overlapping_event(...) 350 balloon_at(c) 351 properties: 352 scene 353 """ 354 if drawer is not None: 355 self.drawing_algorithm = drawer 356 else: 357 self.drawing_algorithm = get_drawer()
358