Пример #1
0
 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()
Пример #2
0
 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()
Пример #3
0
 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()
Пример #4
0
 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()
Пример #5
0
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())
Пример #6
0
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()
Пример #7
0
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()
Пример #8
0
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())