Public Access
add toolbar-free mouse panning and zooming to the plot
Connect matplotlib event handlers on the plot canvas so the display can be navigated without engaging the toolbar modes: left button drag pans the view, the mouse wheel zooms both axes around the pointer, and right button drag zooms single axes (horizontal motion zooms x, vertical motion zooms y, like the right button in the toolbar pan mode). Linear (including inverted ppm) and logarithmic axes are handled, the latter in log space. While a toolbar pan/zoom mode is active the handlers stand back so the toolbar behavior is unchanged. Documented in doc/manual.rst. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
1 parent
1bed381639
commit
7164531351
2 files changed
+104
No files matched your search
@@ -100,6 +100,8 @@ There are now two possibilities to export the figure:
|
|||||||
1. Saving the plot as a figure (EPS, PNG, JPEG and more) by pressing the save button in the toolbar below the plot area
|
1. Saving the plot as a figure (EPS, PNG, JPEG and more) by pressing the save button in the toolbar below the plot area
|
||||||
2. Saving the data of the plot as CSV file (comma separated values) by pressing the button on the right side of the toolbar under the plot
|
2. Saving the data of the plot as CSV file (comma separated values) by pressing the button on the right side of the toolbar under the plot
|
||||||
|
|
||||||
|
The plot can be navigated directly with the mouse: dragging with the left mouse button pans the view and the mouse wheel zooms in and out around the pointer position. Holding the right mouse button and dragging horizontally zooms only the x axis, dragging vertically zooms only the y axis (dragging right or up zooms in), just like the right button in the toolbar pan mode. This works on linear and logarithmic axes. The pan and zoom buttons in the toolbar below the plot are still available; while one of these toolbar modes is active, the direct mouse panning and right-button zooming are suspended so the toolbar behavior is unchanged. Note that with the autoscaling option enabled, the view is reset to the full data range whenever a new result arrives.
|
||||||
|
|
||||||
|
|
||||||
Help Menu
|
Help Menu
|
||||||
=========
|
=========
|
||||||
|
|||||||
@@ -3306,6 +3306,16 @@ class MonitorWidgets:
|
|||||||
self.display_table.attach( self.matplot_toolbar, 0, 1, 1, 2, gtk.AttachOptions.FILL | gtk.AttachOptions.EXPAND, 0, 0, 0 )
|
self.display_table.attach( self.matplot_toolbar, 0, 1, 1, 2, gtk.AttachOptions.FILL | gtk.AttachOptions.EXPAND, 0, 0, 0 )
|
||||||
self.matplot_toolbar.show( )
|
self.matplot_toolbar.show( )
|
||||||
|
|
||||||
|
# mouse pan (left button drag), single-axis zoom (middle button
|
||||||
|
# drag) and wheel zoom, active whenever no toolbar pan/zoom
|
||||||
|
# mode is on
|
||||||
|
self._pan_start = None
|
||||||
|
self._zoom_drag = None
|
||||||
|
self.matplot_canvas.mpl_connect( "scroll_event", self.plot_wheel_zoom )
|
||||||
|
self.matplot_canvas.mpl_connect( "button_press_event", self.plot_button_press )
|
||||||
|
self.matplot_canvas.mpl_connect( "motion_notify_event", self.plot_button_motion )
|
||||||
|
self.matplot_canvas.mpl_connect( "button_release_event", self.plot_button_release )
|
||||||
|
|
||||||
# select display source
|
# select display source
|
||||||
self.display_source_combobox = self.xml_gui.get_object( "display_source_combobox" )
|
self.display_source_combobox = self.xml_gui.get_object( "display_source_combobox" )
|
||||||
self.display_source_treestore = gtk.TreeStore( gobject.TYPE_STRING )
|
self.display_source_treestore = gtk.TreeStore( gobject.TYPE_STRING )
|
||||||
@@ -3802,6 +3812,98 @@ class MonitorWidgets:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _zoomed_limits( limits, center, factor, logarithmic ):
|
||||||
|
"""Return the limits zoomed by factor around center.
|
||||||
|
|
||||||
|
On logarithmic axes the scaling happens in log space.
|
||||||
|
"""
|
||||||
|
if logarithmic:
|
||||||
|
limits = numpy.log10( limits )
|
||||||
|
center = numpy.log10( center )
|
||||||
|
new_limits = [ center + ( limit - center ) * factor for limit in limits ]
|
||||||
|
if logarithmic:
|
||||||
|
new_limits = [ 10 ** limit for limit in new_limits ]
|
||||||
|
return tuple( new_limits )
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _panned_limits( limits, grabbed, current, logarithmic ):
|
||||||
|
"""Return the limits shifted so that current moves to grabbed.
|
||||||
|
|
||||||
|
On logarithmic axes the shift happens in log space.
|
||||||
|
"""
|
||||||
|
if logarithmic:
|
||||||
|
shift = 10 ** ( numpy.log10( grabbed ) - numpy.log10( current ) )
|
||||||
|
return tuple( limit * shift for limit in limits )
|
||||||
|
shift = grabbed - current
|
||||||
|
return tuple( limit + shift for limit in limits )
|
||||||
|
|
||||||
|
def plot_wheel_zoom( self, event ):
|
||||||
|
"""Zoom the plot around the pointer position with the mouse wheel."""
|
||||||
|
if event.inaxes is not self.matplot_axes or event.xdata is None or event.ydata is None:
|
||||||
|
return
|
||||||
|
factor = 1.0 / 1.5 if event.step > 0 else 1.5
|
||||||
|
axes = self.matplot_axes
|
||||||
|
axes.set_xlim( self._zoomed_limits( axes.get_xlim( ), event.xdata, factor,
|
||||||
|
axes.get_xscale( ) == "log" ) )
|
||||||
|
axes.set_ylim( self._zoomed_limits( axes.get_ylim( ), event.ydata, factor,
|
||||||
|
axes.get_yscale( ) == "log" ) )
|
||||||
|
self.matplot_canvas.draw_idle( )
|
||||||
|
|
||||||
|
def plot_button_press( self, event ):
|
||||||
|
"""Start panning the plot with a left button drag.
|
||||||
|
|
||||||
|
A right button drag starts zooming single axes, like the right
|
||||||
|
button in the toolbar pan mode: horizontal motion zooms the x
|
||||||
|
axis, vertical motion the y axis. Both are inactive while a
|
||||||
|
toolbar pan/zoom mode is on, so the toolbar behavior stays
|
||||||
|
unchanged.
|
||||||
|
"""
|
||||||
|
if event.inaxes is not self.matplot_axes or event.xdata is None or event.ydata is None:
|
||||||
|
return
|
||||||
|
if event.button == 3 and not self.matplot_toolbar.mode:
|
||||||
|
self._zoom_drag = ( event.x, event.y, event.xdata, event.ydata )
|
||||||
|
elif event.button == 1 and not self.matplot_toolbar.mode:
|
||||||
|
self._pan_start = ( event.xdata, event.ydata )
|
||||||
|
|
||||||
|
def plot_button_motion( self, event ):
|
||||||
|
"""Pan the plot so the grabbed data point follows the pointer.
|
||||||
|
|
||||||
|
With the right button held, zoom single axes around the
|
||||||
|
pressed position instead: horizontal motion zooms the x axis,
|
||||||
|
vertical motion the y axis, dragging right or up zooms in.
|
||||||
|
"""
|
||||||
|
if self._zoom_drag is not None and event.x is not None and event.y is not None:
|
||||||
|
x0, y0, xdata0, ydata0 = self._zoom_drag
|
||||||
|
axes = self.matplot_axes
|
||||||
|
dx = event.x - x0
|
||||||
|
dy = event.y - y0
|
||||||
|
if dx:
|
||||||
|
factor = 10 ** ( -dx / axes.bbox.width )
|
||||||
|
axes.set_xlim( self._zoomed_limits( axes.get_xlim( ), xdata0, factor,
|
||||||
|
axes.get_xscale( ) == "log" ) )
|
||||||
|
if dy:
|
||||||
|
factor = 10 ** ( -dy / axes.bbox.height )
|
||||||
|
axes.set_ylim( self._zoomed_limits( axes.get_ylim( ), ydata0, factor,
|
||||||
|
axes.get_yscale( ) == "log" ) )
|
||||||
|
self.matplot_canvas.draw_idle( )
|
||||||
|
return
|
||||||
|
if self._pan_start is None or event.xdata is None or event.ydata is None:
|
||||||
|
return
|
||||||
|
x0, y0 = self._pan_start
|
||||||
|
axes = self.matplot_axes
|
||||||
|
axes.set_xlim( self._panned_limits( axes.get_xlim( ), x0, event.xdata,
|
||||||
|
axes.get_xscale( ) == "log" ) )
|
||||||
|
axes.set_ylim( self._panned_limits( axes.get_ylim( ), y0, event.ydata,
|
||||||
|
axes.get_yscale( ) == "log" ) )
|
||||||
|
self._pan_start = ( event.xdata, event.ydata )
|
||||||
|
self.matplot_canvas.draw_idle( )
|
||||||
|
|
||||||
|
def plot_button_release( self, event ):
|
||||||
|
"""Stop panning or single-axis zooming the plot."""
|
||||||
|
self._pan_start = None
|
||||||
|
self._zoom_drag = None
|
||||||
|
|
||||||
def toggle_cursor(self, widget):
|
def toggle_cursor(self, widget):
|
||||||
"""Toggle matplotlib crosshair cursor"""
|
"""Toggle matplotlib crosshair cursor"""
|
||||||
if widget.get_active():
|
if widget.get_active():
|
||||||
|
|||||||
Reference in new issue
Block a user