diff --git a/doc/users/prev_whats_new/whats_new_3.3.0.rst b/doc/users/prev_whats_new/whats_new_3.3.0.rst index fe92f6211ce4..6d3f939edbba 100644 --- a/doc/users/prev_whats_new/whats_new_3.3.0.rst +++ b/doc/users/prev_whats_new/whats_new_3.3.0.rst @@ -612,8 +612,8 @@ Previously, the x/y position displayed by the cursor text would usually include far more significant digits than the mouse pointing precision (typically one pixel). This is now fixed for linear scales. -Qt zoom rectangle now black and white -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +GTK / Qt zoom rectangle now black and white +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This makes it visible even over a dark background. diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py index 31d20f3e6053..c9094eb1df33 100644 --- a/lib/matplotlib/backends/backend_gtk3.py +++ b/lib/matplotlib/backends/backend_gtk3.py @@ -159,12 +159,14 @@ def __init__(self, figure): self._idle_draw_id = 0 self._lastCursor = None + self._rubberband_rect = None self.connect('scroll_event', self.scroll_event) self.connect('button_press_event', self.button_press_event) self.connect('button_release_event', self.button_release_event) self.connect('configure_event', self.configure_event) self.connect('draw', self.on_draw_event) + self.connect('draw', self._post_draw) self.connect('key_press_event', self.key_press_event) self.connect('key_release_event', self.key_release_event) self.connect('motion_notify_event', self.motion_notify_event) @@ -286,6 +288,40 @@ def configure_event(self, widget, event): self.figure.set_size_inches(w / dpi, h / dpi, forward=False) return False # finish event propagation? + def _draw_rubberband(self, rect): + self._rubberband_rect = rect + # TODO: Only update the rubberband area. + self.queue_draw() + + def _post_draw(self, widget, ctx): + if self._rubberband_rect is None: + return + + x0, y0, w, h = self._rubberband_rect + x1 = x0 + w + y1 = y0 + h + + # Draw the lines from x0, y0 towards x1, y1 so that the + # dashes don't "jump" when moving the zoom box. + ctx.move_to(x0, y0) + ctx.line_to(x0, y1) + ctx.move_to(x0, y0) + ctx.line_to(x1, y0) + ctx.move_to(x0, y1) + ctx.line_to(x1, y1) + ctx.move_to(x1, y0) + ctx.line_to(x1, y1) + + ctx.set_antialias(1) + ctx.set_line_width(1) + ctx.set_dash((3, 3), 0) + ctx.set_source_rgb(0, 0, 0) + ctx.stroke_preserve() + + ctx.set_dash((3, 3), 3) + ctx.set_source_rgb(1, 1, 1) + ctx.stroke() + def on_draw_event(self, widget, ctx): # to be overwritten by GTK3Agg or GTK3Cairo pass @@ -507,26 +543,14 @@ def set_cursor(self, cursor): Gtk.main_iteration() def draw_rubberband(self, event, x0, y0, x1, y1): - # adapted from - # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/189744 - ctx = self.canvas.get_property("window").cairo_create() - - # todo: instead of redrawing the entire figure, copy the part of - # the figure that was covered by the previous rubberband rectangle - self.canvas.draw() - height = self.canvas.figure.bbox.height y1 = height - y1 y0 = height - y0 - w = abs(x1 - x0) - h = abs(y1 - y0) - rect = [int(val) for val in (min(x0, x1), min(y0, y1), w, h)] + rect = [int(val) for val in (x0, y0, x1 - x0, y1 - y0)] + self.canvas._draw_rubberband(rect) - ctx.new_path() - ctx.set_line_width(0.5) - ctx.rectangle(rect[0], rect[1], rect[2], rect[3]) - ctx.set_source_rgb(0, 0, 0) - ctx.stroke() + def remove_rubberband(self): + self.canvas._draw_rubberband(None) def _update_buttons_checked(self): for name, active in [("Pan", "PAN"), ("Zoom", "ZOOM")]: @@ -721,6 +745,10 @@ def draw_rubberband(self, x0, y0, x1, y1): NavigationToolbar2GTK3.draw_rubberband( self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1) + def remove_rubberband(self): + NavigationToolbar2GTK3.remove_rubberband( + self._make_classic_style_pseudo_toolbar()) + class SaveFigureGTK3(backend_tools.SaveFigureBase): def trigger(self, *args, **kwargs):