""" This provides several classes used for blocking interaction with figure windows:
`BlockingInput` Creates a callable object to retrieve events in a blocking way for interactive sessions. Base class of the other classes listed here.
`BlockingKeyMouseInput` Creates a callable object to retrieve key or mouse clicks in a blocking way for interactive sessions. Used by `waitforbuttonpress`.
`BlockingMouseInput` Creates a callable object to retrieve mouse clicks in a blocking way for interactive sessions. Used by `ginput`.
`BlockingContourLabeler` Creates a callable object to retrieve mouse clicks in a blocking way that will then be used to place labels on a `ContourSet`. Used by `clabel`. """
"""Callable for retrieving events in a blocking way."""
self.fig = fig self.eventslist = eventslist
""" Event handler; will be passed to the current figure to retrieve events. """ # Add a new event to list - using a separate function is overkill for # the base class, but this is consistent with subclasses. self.add_event(event) _log.info("Event %i", len(self.events))
# This will extract info from events. self.post_event()
# Check if we have enough events already. if len(self.events) >= self.n > 0: self.fig.canvas.stop_event_loop()
"""For baseclass, do nothing but collect events."""
"""Disconnect all callbacks.""" for cb in self.callbacks: self.fig.canvas.mpl_disconnect(cb) self.callbacks = []
"""For base class, this just appends an event to events.""" self.events.append(event)
""" Remove an event from the event list -- by default, the last.
Note that this does not check that there are events, much like the normal pop method. If no events exist, this will throw an exception. """ self.events.pop(index)
"""Blocking call to retrieve *n* events.""" if not isinstance(n, Integral): raise ValueError("Requires an integer argument") self.n = n self.events = []
if hasattr(self.fig.canvas, "manager"): # Ensure that the figure is shown, if we are managing it. self.fig.show() # Connect the events to the on_event function call. self.callbacks = [self.fig.canvas.mpl_connect(name, self.on_event) for name in self.eventslist] try: # Start event loop. self.fig.canvas.start_event_loop(timeout=timeout) finally: # Run even on exception like ctrl-c. # Disconnect the callbacks. self.cleanup() # Return the events in this case. return self.events
""" Callable for retrieving mouse clicks in a blocking way.
This class will also retrieve keypresses and map them to mouse clicks: delete and backspace are like mouse button 3, enter is like mouse button 2 and all others are like mouse button 1. """
BlockingInput.__init__(self, fig=fig, eventslist=('button_press_event', 'key_press_event')) self.button_add = mouse_add self.button_pop = mouse_pop self.button_stop = mouse_stop
"""Process an event.""" if len(self.events) == 0: _log.warning("No events yet") elif self.events[-1].name == 'key_press_event': self.key_event() else: self.mouse_event()
"""Process a mouse click event.""" event = self.events[-1] button = event.button if button == self.button_pop: self.mouse_event_pop(event) elif button == self.button_stop: self.mouse_event_stop(event) else: self.mouse_event_add(event)
""" Process a key press event, mapping keys to appropriate mouse clicks. """ event = self.events[-1] if event.key is None: # At least in OSX gtk backend some keys return None. return key = event.key.lower() if key in ['backspace', 'delete']: self.mouse_event_pop(event) elif key in ['escape', 'enter']: self.mouse_event_stop(event) else: self.mouse_event_add(event)
""" Process an button-1 event (add a click if inside axes).
Parameters ---------- event : `~.backend_bases.MouseEvent` """ if event.inaxes: self.add_click(event) else: # If not a valid click, remove from event list. BlockingInput.pop(self)
""" Process an button-2 event (end blocking input).
Parameters ---------- event : `~.backend_bases.MouseEvent` """ # Remove last event just for cleanliness. BlockingInput.pop(self) # This will exit even if not in infinite mode. This is consistent with # MATLAB and sometimes quite useful, but will require the user to test # how many points were actually returned before using data. self.fig.canvas.stop_event_loop()
""" Process an button-3 event (remove the last click).
Parameters ---------- event : `~.backend_bases.MouseEvent` """ # Remove this last event. BlockingInput.pop(self) # Now remove any existing clicks if possible. if self.events: self.pop(event)
""" Add the coordinates of an event to the list of clicks.
Parameters ---------- event : `~.backend_bases.MouseEvent` """ self.clicks.append((event.xdata, event.ydata)) _log.info("input %i: %f, %f", len(self.clicks), event.xdata, event.ydata) # If desired, plot up click. if self.show_clicks: line = mlines.Line2D([event.xdata], [event.ydata], marker='+', color='r') event.inaxes.add_line(line) self.marks.append(line) self.fig.canvas.draw()
""" Remove a click (by default, the last) from the list of clicks.
Parameters ---------- event : `~.backend_bases.MouseEvent` """ self.clicks.pop(index) if self.show_clicks: self.marks.pop(index).remove() self.fig.canvas.draw()
""" Removes a click and the associated event from the list of clicks.
Defaults to the last click. """ self.pop_click(event, index) BlockingInput.pop(self, index)
""" Parameters ---------- event : `~.backend_bases.MouseEvent`, optional Not used """ # Clean the figure. if self.show_clicks: for mark in self.marks: mark.remove() self.marks = [] self.fig.canvas.draw() # Call base class to remove callbacks. BlockingInput.cleanup(self)
""" Blocking call to retrieve *n* coordinate pairs through mouse clicks. """ self.show_clicks = show_clicks self.clicks = [] self.marks = [] BlockingInput.__call__(self, n=n, timeout=timeout) return self.clicks
""" Callable for retrieving mouse clicks and key presses in a blocking way.
Used to place contour labels. """
self.cs = cs BlockingMouseInput.__init__(self, fig=cs.ax.figure)
self.button1(event)
self.button3(event)
""" Process an button-1 event (add a label to a contour).
Parameters ---------- event : `~.backend_bases.MouseEvent` """ # Shorthand if event.inaxes == self.cs.ax: self.cs.add_label_near(event.x, event.y, self.inline, inline_spacing=self.inline_spacing, transform=False) self.fig.canvas.draw() else: # Remove event if not valid BlockingInput.pop(self)
""" Process an button-3 event (remove a label if not in inline mode).
Unfortunately, if one is doing inline labels, then there is currently no way to fix the broken contour - once humpty-dumpty is broken, he can't be put back together. In inline mode, this does nothing.
Parameters ---------- event : `~.backend_bases.MouseEvent` """ if self.inline: pass else: self.cs.pop_label() self.cs.ax.figure.canvas.draw()
self.inline = inline self.inline_spacing = inline_spacing BlockingMouseInput.__call__(self, n=n, timeout=timeout, show_clicks=False)
""" Callable for retrieving mouse clicks and key presses in a blocking way. """
BlockingInput.__init__(self, fig=fig, eventslist=( 'button_press_event', 'key_press_event'))
"""Determine if it is a key event.""" if self.events: self.keyormouse = self.events[-1].name == 'key_press_event' else: _log.warning("No events yet.")
""" Blocking call to retrieve a single mouse click or key press.
Returns ``True`` if key press, ``False`` if mouse click, or ``None`` if timed out. """ self.keyormouse = None BlockingInput.__call__(self, n=1, timeout=timeout)
return self.keyormouse |