def __init__(self, parent, name=None, size=(100, 100)): self.monitoring = Monitoring() wx.ScrolledWindow.__init__(self, parent, size=size) self.parent = parent self._create_context_menu() self.Bind(wx.EVT_PAINT, self._on_paint) self.Bind(wx.EVT_SIZE, self._on_size) self.Bind(wx.EVT_LEFT_DOWN, self._on_left_down) self.Bind(wx.EVT_RIGHT_DOWN, self._on_right_down) self.Bind(wx.EVT_LEFT_DCLICK, self._on_left_doubleclick) self.model = CustomCategoryTreeModel() self.model.listen_for_any(self._redraw) self.renderer = CustomCategoryTreeRenderer(self, self.model) self.set_no_timeline_view() self._size_to_model() self._draw_bitmap()
def __init__(self, view, drawer=None): """ The purpose of the drawer argument is make testing easier. A test can mock a drawer and use the mock by sending it in the drawer argument. Normally the drawer is collected with the get_drawer() method. """ self.appearance = None self.monitoring = Monitoring() self.view = view self._set_drawing_algorithm(drawer) self.timeline = None self.set_appearance(Appearance()) self.set_event_box_drawer(DefaultEventBoxDrawer()) self.set_background_drawer(self.get_saved_background_drawer()) self._fast_draw = False self._set_initial_values_to_member_variables() self._set_colors_and_styles()
class CustomCategoryTree(wx.ScrolledWindow): def __init__(self, parent, name=None, size=(100, 100)): self.monitoring = Monitoring() wx.ScrolledWindow.__init__(self, parent, size=size) self.parent = parent self._create_context_menu() self.Bind(wx.EVT_PAINT, self._on_paint) self.Bind(wx.EVT_SIZE, self._on_size) self.Bind(wx.EVT_LEFT_DOWN, self._on_left_down) self.Bind(wx.EVT_RIGHT_DOWN, self._on_right_down) self.Bind(wx.EVT_LEFT_DCLICK, self._on_left_doubleclick) self.model = CustomCategoryTreeModel() self.model.listen_for_any(self._redraw) self.renderer = CustomCategoryTreeRenderer(self, self.model) self.set_no_timeline_view() self._size_to_model() self._draw_bitmap() def set_no_timeline_view(self): self.db = None self.view_properties = None self.model.set_categories(None) def set_timeline_view(self, db, view_properties): self.db = db self.view_properties = view_properties self.model.set_categories(CategoriesFacade(db, view_properties)) def check_categories(self, categories): self.view_properties.set_categories_visible(categories) def uncheck_categories(self, categories): self.view_properties.set_categories_visible(categories, False) def _has_timeline_view(self): return self.db is not None and self.view_properties is not None def _on_paint(self, event): wx.BufferedPaintDC(self, self.buffer_image, wx.BUFFER_VIRTUAL_AREA) def _on_size(self, event): self._size_to_model() def _on_left_doubleclick(self, event): def edit_function(): self._edit_category() safe_locking(self.parent, edit_function) def _on_left_down(self, event): self.SetFocus() self._store_hit_info(event) hit_category = self.last_hit_info.get_category() if self.last_hit_info.is_on_arrow(): self.model.toggle_expandedness(hit_category) elif self.last_hit_info.is_on_checkbox(): self.view_properties.toggle_category_visibility(hit_category) def _on_right_down(self, event): def edit_function(): self._store_hit_info(event) for (menu_item, should_be_enabled_fn) in self.context_menu_items: menu_item.Enable(should_be_enabled_fn(self.last_hit_info)) self.PopupMenu(self.context_menu) safe_locking(self.parent, edit_function) def _on_menu_edit(self, e): self._edit_category() def _on_menu_add(self, e): add_category(self, self.db) def _on_menu_delete(self, e): hit_category = self.last_hit_info.get_category() if hit_category: delete_category(self, self.db, hit_category) def _on_menu_check_all(self, e): self.view_properties.set_categories_visible(self.db.get_categories()) def _on_menu_check_children(self, e): self.view_properties.set_categories_visible( self.last_hit_info.get_immediate_children()) def _on_menu_check_all_children(self, e): self.view_properties.set_categories_visible( self.last_hit_info.get_all_children()) def _on_menu_check_parents(self, e): self.view_properties.set_categories_visible( self.last_hit_info.get_parents()) def _on_menu_check_parents_for_checked_children(self, e): self.view_properties.set_categories_visible( self.last_hit_info.get_parents_for_checked_childs()) def _on_menu_uncheck_all(self, e): self.view_properties.set_categories_visible(self.db.get_categories(), False) def _on_menu_uncheck_children(self, e): self.view_properties.set_categories_visible( self.last_hit_info.get_immediate_children(), False) def _on_menu_uncheck_all_children(self, e): self.view_properties.set_categories_visible( self.last_hit_info.get_all_children(), False) def _on_menu_uncheck_parents(self, e): self.view_properties.set_categories_visible( self.last_hit_info.get_parents(), False) def _edit_category(self): hit_category = self.last_hit_info.get_category() if hit_category: edit_category(self, self.db, hit_category) def _store_hit_info(self, event): (x, y) = self.CalcUnscrolledPosition(event.GetX(), event.GetY()) self.last_hit_info = self.model.hit(x, y) def _redraw(self): self.SetVirtualSize( (-1, self.model.ITEM_HEIGHT_PX * len(self.model.items))) self.SetScrollRate(0, self.model.ITEM_HEIGHT_PX / 2) self._draw_bitmap() self.Refresh() self.Update() def _draw_bitmap(self): width, height = self.GetVirtualSizeTuple() self.buffer_image = wx.EmptyBitmap(width, height) memdc = wx.BufferedDC(None, self.buffer_image) memdc.SetBackground( wx.Brush(self.GetBackgroundColour(), wx.PENSTYLE_SOLID)) memdc.Clear() memdc.BeginDrawing() self.monitoring.timer_start() self.renderer.render(memdc) self.monitoring.timer_end() if DEBUG_ENABLED: (width, height) = self.GetSizeTuple() redraw_time = self.monitoring.timer_elapsed_ms self.monitoring.count_category_redraw() memdc.SetTextForeground((255, 0, 0)) memdc.SetFont(Font(10, weight=wx.FONTWEIGHT_BOLD)) memdc.DrawText( "Redraw count: %d" % self.monitoring._category_redraw_count, 10, height - 35) memdc.DrawText("Last redraw time: %.3f ms" % redraw_time, 10, height - 20) memdc.EndDrawing() del memdc def _size_to_model(self): (view_width, view_height) = self.GetVirtualSizeTuple() self.model.set_view_size(view_width, view_height) def _create_context_menu(self): def add_item(name, callback, should_be_enabled_fn): item = wx.MenuItem(self.context_menu, wx.ID_ANY, name) self.context_menu.AppendItem(item) self.Bind(wx.EVT_MENU, callback, item) self.context_menu_items.append((item, should_be_enabled_fn)) return item self.context_menu_items = [] self.context_menu = wx.Menu() add_item(_("Edit..."), self._on_menu_edit, lambda hit_info: hit_info.has_category()) add_item(_("Add..."), self._on_menu_add, lambda hit_info: self._has_timeline_view()) add_item(_("Delete"), self._on_menu_delete, lambda hit_info: hit_info.has_category()) self.context_menu.AppendSeparator() add_item(_("Check All"), self._on_menu_check_all, lambda hit_info: self._has_timeline_view()) add_item(_("Check children"), self._on_menu_check_children, lambda hit_info: hit_info.has_category()) add_item(_("Check all children"), self._on_menu_check_all_children, lambda hit_info: hit_info.has_category()) add_item(_("Check all parents"), self._on_menu_check_parents, lambda hit_info: hit_info.has_category()) add_item(_("Check parents for checked children"), self._on_menu_check_parents_for_checked_children, lambda hit_info: self._has_timeline_view()) self.context_menu.AppendSeparator() add_item(_("Uncheck All"), self._on_menu_uncheck_all, lambda hit_info: self._has_timeline_view()) add_item(_("Uncheck children"), self._on_menu_uncheck_children, lambda hit_info: hit_info.has_category()) add_item(_("Uncheck all children"), self._on_menu_uncheck_all_children, lambda hit_info: hit_info.has_category()) add_item(_("Uncheck all parents"), self._on_menu_uncheck_parents, lambda hit_info: hit_info.has_category())
class TimelineCanvasController(object): def __init__(self, view, drawer=None): """ The purpose of the drawer argument is make testing easier. A test can mock a drawer and use the mock by sending it in the drawer argument. Normally the drawer is collected with the get_drawer() method. """ self.appearance = None self.monitoring = Monitoring() self.view = view self._set_drawing_algorithm(drawer) self.timeline = None self.set_appearance(Appearance()) self.set_event_box_drawer(DefaultEventBoxDrawer()) self.set_background_drawer(self.get_saved_background_drawer()) self._fast_draw = False self._set_initial_values_to_member_variables() self._set_colors_and_styles() @property def scene(self): return self.drawing_algorithm.scene def get_appearance(self): return self.appearance def set_appearance(self, appearance): if self.appearance is not None: self.appearance.unlisten(self._redraw_timeline) self.appearance = appearance self.appearance.listen_for_any(self._redraw_timeline) self.redraw_timeline() def get_saved_background_drawer(self): return DefaultBackgroundDrawer() def set_event_box_drawer(self, event_box_drawer): self.drawing_algorithm.set_event_box_drawer(event_box_drawer) def set_background_drawer(self, drawer): self.drawing_algorithm.set_background_drawer(drawer) def get_timeline(self): return self.timeline def get_view_properties(self): return self.view_properties def set_timeline(self, timeline): """Inform what timeline to draw.""" self._unregister_timeline(self.timeline) if timeline is None: self._set_null_timeline() else: self._set_non_null_timeline(timeline) def use_fast_draw(self, value): self._fast_draw = value def navigate(self, navigation_fn): old_period = self.view_properties.displayed_period new_period = navigation_fn(old_period) MIN_ZOOM_DELTA, min_zoom_error_text = self.time_type.get_min_zoom_delta( ) if new_period.delta() < MIN_ZOOM_DELTA: raise ValueError(min_zoom_error_text) min_time = self.time_type.get_min_time() if min_time is not None and new_period.start_time < min_time: raise ValueError(_("Can't scroll more to the left")) max_time = self.time_type.get_max_time() if max_time is not None and new_period.end_time > max_time: raise ValueError(_("Can't scroll more to the right")) self.view_properties.displayed_period = new_period self.redraw_timeline() def _set_null_timeline(self): self.timeline = None self.time_type = None self.view.Disable() def _set_non_null_timeline(self, timeline): self.timeline = timeline self.time_type = timeline.get_time_type() self.timeline.register(self._timeline_changed) self.view_properties.unlisten(self._redraw_timeline) properties_loaded = self._load_view_properties() if properties_loaded: self.view_properties.listen_for_any(self._redraw_timeline) self._redraw_timeline() self.view.Enable() self.view.SetFocus() def _load_view_properties(self): properties_loaded = True try: self.view_properties.clear_db_specific() self.timeline.load_view_properties(self.view_properties) if self.view_properties.displayed_period is None: default_tp = self.time_type.get_default_time_period() self.view_properties.displayed_period = default_tp self.view_properties.hscroll_amount = 0 except Exception: properties_loaded = False return properties_loaded def _unregister_timeline(self, timeline): if timeline is not None: timeline.unregister(self._timeline_changed) def get_time_period(self): """Return currently displayed time period.""" if self.timeline is None: raise Exception(_("No timeline set")) return self.view_properties.displayed_period def redraw_timeline(self): self._redraw_timeline() def window_resized(self): self._redraw_timeline() def _one_and_only_one_event_selected(self): selected_event_ids = self.view_properties.get_selected_event_ids() nbr_of_selected_event_ids = len(selected_event_ids) return nbr_of_selected_event_ids == 1 def _get_first_selected_event(self): selected_event_ids = self.view_properties.get_selected_event_ids() if len(selected_event_ids) > 0: event_id = selected_event_ids[0] return self.timeline.find_event_with_id(event_id) return None def get_time(self, x): return self.drawing_algorithm.get_time(x) def event_with_rect_at(self, x, y, alt_down=False): return self.drawing_algorithm.event_with_rect_at(x, y, alt_down) def event_at(self, x, y, alt_down=False): return self.drawing_algorithm.event_at(x, y, alt_down) def set_selected(self, event, is_selected): self.view_properties.set_selected(event, is_selected) def clear_selected(self): self.view_properties.clear_selected() def select_all_events(self): self.view_properties.select_all_events() def is_selected(self, event): return self.view_properties.is_selected(event) def set_hovered_event(self, event): self.view_properties.change_hovered_event(event) def get_hovered_event(self): return self.view_properties.hovered_event def set_selection_rect(self, cursor): self.view_properties.set_selection_rect(cursor.rect) self._fast_draw = True self.redraw_timeline() def remove_selection_rect(self): self.view_properties.set_selection_rect(None) self._fast_draw = True self.redraw_timeline() def get_hscroll_amount(self): return self.view_properties.hscroll_amount def set_hscroll_amount(self, amount): self.view_properties.hscroll_amount = amount def set_period_selection(self, period): if period is None: self.view_properties.period_selection = None else: self.view_properties.period_selection = (period.start_time, period.end_time) self._redraw_timeline() def select_events_in_rect(self, rect): self.view_properties.set_all_selected(self.get_events_in_rect(rect)) def event_has_sticky_balloon(self, event): return self.view_properties.event_has_sticky_balloon(event) def set_event_sticky_balloon(self, event, is_sticky): self.view_properties.set_event_has_sticky_balloon(event, is_sticky) self.redraw_timeline() def add_highlight(self, event, clear): self.view_properties.add_highlight(event, clear) def tick_highlights(self): self.view_properties.tick_highlights(limit=15) def has_higlights(self): return self.view_properties.has_higlights() def filter_events(self, events): return self.view_properties.filter_events(events) def event_is_period(self, event): return self.drawing_algorithm.event_is_period(event.get_time_period()) def snap(self, time): return self.drawing_algorithm.snap(time) def get_selected_events(self): return self.timeline.find_event_with_ids( self.view_properties.get_selected_event_ids()) def get_events_in_rect(self, rect): return self.drawing_algorithm.get_events_in_rect(rect) def get_hidden_event_count(self): return self.drawing_algorithm.get_hidden_event_count() def increment_font_size(self): self.drawing_algorithm.increment_font_size() self._redraw_timeline() def decrement_font_size(self): self.drawing_algorithm.decrement_font_size() self._redraw_timeline() def get_closest_overlapping_event(self, event, up): return self.drawing_algorithm.get_closest_overlapping_event(event, up=up) def balloon_at(self, cursor): return self.drawing_algorithm.balloon_at(*cursor.pos) def _timeline_changed(self, state_change): self._redraw_timeline() def _set_initial_values_to_member_variables(self): self.timeline = None self.view_properties = ViewProperties() self.dragscroll_timer_running = False def _set_colors_and_styles(self): """Define the look and feel of the drawing area.""" self.view.SetBackgroundColour(wx.WHITE) self.view.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) self.view.set_default_cursor() self.view.Disable() def _redraw_timeline(self): def display_monitor_result(dc): (width, height) = self.view.GetSizeTuple() redraw_time = self.monitoring.timer_elapsed_ms self.monitoring.count_timeline_redraw() dc.SetTextForeground((255, 0, 0)) dc.SetFont(Font(12, weight=wx.FONTWEIGHT_BOLD)) index, is_in_transaction, history = self.timeline.transactions_status( ) dc.DrawText("Undo buffer size: %d" % len(history), width - 300, height - 100) dc.DrawText("Undo buffer pos: %d" % index, width - 300, height - 80) dc.DrawText( "Redraw count: %d" % self.monitoring._timeline_redraw_count, width - 300, height - 60) dc.DrawText("Last redraw time: %.3f ms" % redraw_time, width - 300, height - 40) def fn_draw(dc): self.monitoring.timer_start() self.drawing_algorithm.draw(dc, self.timeline, self.view_properties, self.appearance, fast_draw=self._fast_draw) self.monitoring.timer_end() if DEBUG_ENABLED: display_monitor_result(dc) self._fast_draw = False if self.timeline and self.view_properties.displayed_period: self.view_properties.divider_position = ( float(self.view.GetDividerPosition()) / 100.0) self.view.redraw_surface(fn_draw) self.view.PostEvent(create_timeline_redrawn_event()) def _set_drawing_algorithm(self, drawer): """ The drawer interface: methods: draw(d, t, p, a, f) set_event_box_drawer(d) set_background_drawer(d) get_time(x) event_with_rect_at(x, y, k) event_at(x, y, k) event_is_period(p) snap(t) get_events_in_rect(r) get_hidden_event_count() increment_font_size() decrement_font_size() get_closest_overlapping_event(...) balloon_at(c) properties: scene """ if drawer is not None: self.drawing_algorithm = drawer else: self.drawing_algorithm = get_drawer()
class TimelineCanvasController(object): def __init__(self, view, drawer=None): """ The purpose of the drawer argument is make testing easier. A test can mock a drawer and use the mock by sending it in the drawer argument. Normally the drawer is collected with the get_drawer() method. """ self.appearance = None self.monitoring = Monitoring() self.view = view self._set_drawing_algorithm(drawer) self.timeline = None self.set_appearance(Appearance()) self.set_event_box_drawer(DefaultEventBoxDrawer()) self.set_background_drawer(self.get_saved_background_drawer()) self._fast_draw = False self._set_initial_values_to_member_variables() self._set_colors_and_styles() @property def scene(self): return self.drawing_algorithm.scene def get_appearance(self): return self.appearance def set_appearance(self, appearance): if self.appearance is not None: self.appearance.unlisten(self._redraw_timeline) self.appearance = appearance self.appearance.listen_for_any(self._redraw_timeline) self.redraw_timeline() def get_saved_background_drawer(self): return DefaultBackgroundDrawer() def set_event_box_drawer(self, event_box_drawer): self.drawing_algorithm.set_event_box_drawer(event_box_drawer) def set_background_drawer(self, drawer): self.drawing_algorithm.set_background_drawer(drawer) def get_timeline(self): return self.timeline def get_view_properties(self): return self.view_properties def set_timeline(self, timeline): """Inform what timeline to draw.""" self._unregister_timeline(self.timeline) if timeline is None: self._set_null_timeline() else: self._set_non_null_timeline(timeline) def use_fast_draw(self, value): self._fast_draw = value def navigate(self, navigation_fn): old_period = self.view_properties.displayed_period new_period = navigation_fn(old_period) MIN_ZOOM_DELTA, min_zoom_error_text = self.time_type.get_min_zoom_delta() if new_period.delta() < MIN_ZOOM_DELTA: raise ValueError(min_zoom_error_text) min_time = self.time_type.get_min_time() if min_time is not None and new_period.start_time < min_time: raise ValueError(_("Can't scroll more to the left")) max_time = self.time_type.get_max_time() if max_time is not None and new_period.end_time > max_time: raise ValueError(_("Can't scroll more to the right")) self.view_properties.displayed_period = new_period self.redraw_timeline() def _set_null_timeline(self): self.timeline = None self.time_type = None self.view.Disable() def _set_non_null_timeline(self, timeline): self.timeline = timeline self.time_type = timeline.get_time_type() self.timeline.register(self._timeline_changed) self.view_properties.unlisten(self._redraw_timeline) properties_loaded = self._load_view_properties() if properties_loaded: self.view_properties.listen_for_any(self._redraw_timeline) self._redraw_timeline() self.view.Enable() self.view.SetFocus() def _load_view_properties(self): properties_loaded = True try: self.view_properties.clear_db_specific() self.timeline.load_view_properties(self.view_properties) if self.view_properties.displayed_period is None: default_tp = self.time_type.get_default_time_period() self.view_properties.displayed_period = default_tp self.view_properties.hscroll_amount = 0 except Exception: properties_loaded = False return properties_loaded def _unregister_timeline(self, timeline): if timeline is not None: timeline.unregister(self._timeline_changed) def get_time_period(self): """Return currently displayed time period.""" if self.timeline is None: raise Exception(_("No timeline set")) return self.view_properties.displayed_period def redraw_timeline(self): self._redraw_timeline() def window_resized(self): self._redraw_timeline() def _one_and_only_one_event_selected(self): selected_event_ids = self.view_properties.get_selected_event_ids() nbr_of_selected_event_ids = len(selected_event_ids) return nbr_of_selected_event_ids == 1 def _get_first_selected_event(self): selected_event_ids = self.view_properties.get_selected_event_ids() if len(selected_event_ids) > 0: event_id = selected_event_ids[0] return self.timeline.find_event_with_id(event_id) return None def get_time(self, x): return self.drawing_algorithm.get_time(x) def event_with_rect_at(self, x, y, alt_down=False): return self.drawing_algorithm.event_with_rect_at(x, y, alt_down) def event_at(self, x, y, alt_down=False): return self.drawing_algorithm.event_at(x, y, alt_down) def set_selected(self, event, is_selected): self.view_properties.set_selected(event, is_selected) def clear_selected(self): self.view_properties.clear_selected() def select_all_events(self): self.view_properties.select_all_events() def is_selected(self, event): return self.view_properties.is_selected(event) def set_hovered_event(self, event): self.view_properties.change_hovered_event(event) def get_hovered_event(self): return self.view_properties.hovered_event def set_selection_rect(self, cursor): self.view_properties.set_selection_rect(cursor.rect) self._fast_draw = True self.redraw_timeline() def remove_selection_rect(self): self.view_properties.set_selection_rect(None) self._fast_draw = True self.redraw_timeline() def get_hscroll_amount(self): return self.view_properties.hscroll_amount def set_hscroll_amount(self, amount): self.view_properties.hscroll_amount = amount def set_period_selection(self, period): if period is None: self.view_properties.period_selection = None else: self.view_properties.period_selection = (period.start_time, period.end_time) self._redraw_timeline() def select_events_in_rect(self, rect): self.view_properties.set_all_selected(self.get_events_in_rect(rect)) def event_has_sticky_balloon(self, event): return self.view_properties.event_has_sticky_balloon(event) def set_event_sticky_balloon(self, event, is_sticky): self.view_properties.set_event_has_sticky_balloon(event, is_sticky) self.redraw_timeline() def add_highlight(self, event, clear): self.view_properties.add_highlight(event, clear) def tick_highlights(self): self.view_properties.tick_highlights(limit=15) def has_higlights(self): return self.view_properties.has_higlights() def filter_events(self, events): return self.view_properties.filter_events(events) def event_is_period(self, event): return self.drawing_algorithm.event_is_period(event.get_time_period()) def snap(self, time): return self.drawing_algorithm.snap(time) def get_selected_events(self): return self.timeline.find_event_with_ids( self.view_properties.get_selected_event_ids() ) def get_events_in_rect(self, rect): return self.drawing_algorithm.get_events_in_rect(rect) def get_hidden_event_count(self): return self.drawing_algorithm.get_hidden_event_count() def increment_font_size(self): self.drawing_algorithm.increment_font_size() self._redraw_timeline() def decrement_font_size(self): self.drawing_algorithm.decrement_font_size() self._redraw_timeline() def get_closest_overlapping_event(self, event, up): return self.drawing_algorithm.get_closest_overlapping_event(event, up=up) def balloon_at(self, cursor): return self.drawing_algorithm.balloon_at(*cursor.pos) def _timeline_changed(self, state_change): self._redraw_timeline() def _set_initial_values_to_member_variables(self): self.timeline = None self.view_properties = ViewProperties() self.dragscroll_timer_running = False def _set_colors_and_styles(self): """Define the look and feel of the drawing area.""" self.view.SetBackgroundColour(wx.WHITE) self.view.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) self.view.set_default_cursor() self.view.Disable() def _redraw_timeline(self): def display_monitor_result(dc): (width, height) = self.view.GetSizeTuple() redraw_time = self.monitoring.timer_elapsed_ms self.monitoring.count_timeline_redraw() dc.SetTextForeground((255, 0, 0)) dc.SetFont(Font(12, weight=wx.FONTWEIGHT_BOLD)) index, is_in_transaction, history = self.timeline.transactions_status() dc.DrawText("Undo buffer size: %d" % len(history), width - 300, height - 100) dc.DrawText("Undo buffer pos: %d" % index, width - 300, height - 80) dc.DrawText("Redraw count: %d" % self.monitoring._timeline_redraw_count, width - 300, height - 60) dc.DrawText("Last redraw time: %.3f ms" % redraw_time, width - 300, height - 40) def fn_draw(dc): self.monitoring.timer_start() self.drawing_algorithm.draw(dc, self.timeline, self.view_properties, self.appearance, fast_draw=self._fast_draw) self.monitoring.timer_end() if DEBUG_ENABLED: display_monitor_result(dc) self._fast_draw = False if self.timeline and self.view_properties.displayed_period: self.view_properties.divider_position = (float(self.view.GetDividerPosition()) / 100.0) self.view.redraw_surface(fn_draw) self.view.PostEvent(create_timeline_redrawn_event()) def _set_drawing_algorithm(self, drawer): """ The drawer interface: methods: draw(d, t, p, a, f) set_event_box_drawer(d) set_background_drawer(d) get_time(x) event_with_rect_at(x, y, k) event_at(x, y, k) event_is_period(p) snap(t) get_events_in_rect(r) get_hidden_event_count() increment_font_size() decrement_font_size() get_closest_overlapping_event(...) balloon_at(c) properties: scene """ if drawer is not None: self.drawing_algorithm = drawer else: self.drawing_algorithm = get_drawer()
class CustomCategoryTree(wx.ScrolledWindow): def __init__(self, parent, name=None, size=(100, 100)): self.monitoring = Monitoring() wx.ScrolledWindow.__init__(self, parent, size=size) self.parent = parent self._create_context_menu() self.Bind(wx.EVT_PAINT, self._on_paint) self.Bind(wx.EVT_SIZE, self._on_size) self.Bind(wx.EVT_LEFT_DOWN, self._on_left_down) self.Bind(wx.EVT_RIGHT_DOWN, self._on_right_down) self.Bind(wx.EVT_LEFT_DCLICK, self._on_left_doubleclick) self.model = CustomCategoryTreeModel() self.model.listen_for_any(self._redraw) self.renderer = CustomCategoryTreeRenderer(self, self.model) self.set_no_timeline_view() self._size_to_model() self._draw_bitmap() def set_no_timeline_view(self): self.db = None self.view_properties = None self.model.set_categories(None) def set_timeline_view(self, db, view_properties): self.db = db self.view_properties = view_properties self.model.set_categories(CategoriesFacade(db, view_properties)) def check_categories(self, categories): self.view_properties.set_categories_visible(categories) def uncheck_categories(self, categories): self.view_properties.set_categories_visible(categories, False) def _has_timeline_view(self): return self.db is not None and self.view_properties is not None def _on_paint(self, event): wx.BufferedPaintDC(self, self.buffer_image, wx.BUFFER_VIRTUAL_AREA) def _on_size(self, event): self._size_to_model() def _on_left_doubleclick(self, event): def edit_function(): self._edit_category() safe_locking(self.parent, edit_function) def _on_left_down(self, event): self.SetFocus() self._store_hit_info(event) hit_category = self.last_hit_info.get_category() if self.last_hit_info.is_on_arrow(): self.model.toggle_expandedness(hit_category) elif self.last_hit_info.is_on_checkbox(): self.view_properties.toggle_category_visibility(hit_category) def _on_right_down(self, event): def edit_function(): self._store_hit_info(event) for (menu_item, should_be_enabled_fn) in self.context_menu_items: menu_item.Enable(should_be_enabled_fn(self.last_hit_info)) self.PopupMenu(self.context_menu) safe_locking(self.parent, edit_function) def _on_menu_edit(self, e): self._edit_category() def _on_menu_add(self, e): add_category(self, self.db) def _on_menu_delete(self, e): hit_category = self.last_hit_info.get_category() if hit_category: delete_category(self, self.db, hit_category) def _on_menu_check_all(self, e): self.view_properties.set_categories_visible( self.db.get_categories()) def _on_menu_check_children(self, e): self.view_properties.set_categories_visible( self.last_hit_info.get_immediate_children()) def _on_menu_check_all_children(self, e): self.view_properties.set_categories_visible( self.last_hit_info.get_all_children()) def _on_menu_check_parents(self, e): self.view_properties.set_categories_visible( self.last_hit_info.get_parents()) def _on_menu_check_parents_for_checked_children(self, e): self.view_properties.set_categories_visible( self.last_hit_info.get_parents_for_checked_childs()) def _on_menu_uncheck_all(self, e): self.view_properties.set_categories_visible( self.db.get_categories(), False) def _on_menu_uncheck_children(self, e): self.view_properties.set_categories_visible( self.last_hit_info.get_immediate_children(), False) def _on_menu_uncheck_all_children(self, e): self.view_properties.set_categories_visible( self.last_hit_info.get_all_children(), False) def _on_menu_uncheck_parents(self, e): self.view_properties.set_categories_visible( self.last_hit_info.get_parents(), False) def _edit_category(self): hit_category = self.last_hit_info.get_category() if hit_category: edit_category(self, self.db, hit_category) def _store_hit_info(self, event): (x, y) = self.CalcUnscrolledPosition(event.GetX(), event.GetY()) self.last_hit_info = self.model.hit(x, y) def _redraw(self): self.SetVirtualSize((-1, self.model.ITEM_HEIGHT_PX * len(self.model.items))) self.SetScrollRate(0, self.model.ITEM_HEIGHT_PX / 2) self._draw_bitmap() self.Refresh() self.Update() def _draw_bitmap(self): width, height = self.GetVirtualSizeTuple() self.buffer_image = wx.EmptyBitmap(width, height) memdc = wx.BufferedDC(None, self.buffer_image) memdc.SetBackground(wx.Brush(self.GetBackgroundColour(), wx.PENSTYLE_SOLID)) memdc.Clear() memdc.BeginDrawing() self.monitoring.timer_start() self.renderer.render(memdc) self.monitoring.timer_end() if DEBUG_ENABLED: (width, height) = self.GetSizeTuple() redraw_time = self.monitoring.timer_elapsed_ms self.monitoring.count_category_redraw() memdc.SetTextForeground((255, 0, 0)) memdc.SetFont(Font(10, weight=wx.FONTWEIGHT_BOLD)) memdc.DrawText("Redraw count: %d" % self.monitoring._category_redraw_count, 10, height - 35) memdc.DrawText("Last redraw time: %.3f ms" % redraw_time, 10, height - 20) memdc.EndDrawing() del memdc def _size_to_model(self): (view_width, view_height) = self.GetVirtualSizeTuple() self.model.set_view_size(view_width, view_height) def _create_context_menu(self): def add_item(name, callback, should_be_enabled_fn): item = wx.MenuItem(self.context_menu, wx.ID_ANY, name) self.context_menu.AppendItem(item) self.Bind(wx.EVT_MENU, callback, item) self.context_menu_items.append((item, should_be_enabled_fn)) return item self.context_menu_items = [] self.context_menu = wx.Menu() add_item( _("Edit..."), self._on_menu_edit, lambda hit_info: hit_info.has_category()) add_item( _("Add..."), self._on_menu_add, lambda hit_info: self._has_timeline_view()) add_item( _("Delete"), self._on_menu_delete, lambda hit_info: hit_info.has_category()) self.context_menu.AppendSeparator() add_item( _("Check All"), self._on_menu_check_all, lambda hit_info: self._has_timeline_view()) add_item( _("Check children"), self._on_menu_check_children, lambda hit_info: hit_info.has_category()) add_item( _("Check all children"), self._on_menu_check_all_children, lambda hit_info: hit_info.has_category()) add_item( _("Check all parents"), self._on_menu_check_parents, lambda hit_info: hit_info.has_category()) add_item( _("Check parents for checked children"), self._on_menu_check_parents_for_checked_children, lambda hit_info: self._has_timeline_view()) self.context_menu.AppendSeparator() add_item( _("Uncheck All"), self._on_menu_uncheck_all, lambda hit_info: self._has_timeline_view()) add_item( _("Uncheck children"), self._on_menu_uncheck_children, lambda hit_info: hit_info.has_category()) add_item( _("Uncheck all children"), self._on_menu_uncheck_all_children, lambda hit_info: hit_info.has_category()) add_item( _("Uncheck all parents"), self._on_menu_uncheck_parents, lambda hit_info: hit_info.has_category())