Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Text box widget, take over of PR5375 #6988

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Sep 3, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Added a text entry widget, that allows usere to register to be notifi…
…ed when text changes or text is submitted
  • Loading branch information
HastingsGreer authored and fariza committed Aug 29, 2016
commit 80c20f33a89c5c9d1bb1452b68cd10b1b45e5570
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@

2015-11-16 Levels passed to contour(f) and tricontour(f) must be in increasing
order.

2015-10-21 Added TextBox widget


2015-10-21 Added get_ticks_direction()

2015-02-27 Added the rcParam 'image.composite_image' to permit users
Expand Down
9 changes: 8 additions & 1 deletion doc/users/whats_new.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.. _whats-new:
w.. _whats-new:

************************
What's new in matplotlib
Expand Down Expand Up @@ -244,6 +244,13 @@ Some parameters have been added, others have been improved.
Widgets
-------

Added TextBox Widget

````````````````````
Added a widget that allows text entry by reading key events when it is active.
Text caret in text box is visible when it is active, can be moved using arrow keys but not mouse


Active state of Selectors
`````````````````````````

Expand Down
252 changes: 252 additions & 0 deletions lib/matplotlib/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from six.moves import zip

import numpy as np
from matplotlib import rcParams

from .mlab import dist
from .patches import Circle, Rectangle, Ellipse
Expand Down Expand Up @@ -627,6 +628,256 @@ def disconnect(self, cid):
except KeyError:
pass

class TextBox(AxesWidget):
"""
A GUI neutral text input box.

For the text box to remain responsive
you must keep a reference to it.

The following attributes are accessible

*ax*
The :class:`matplotlib.axes.Axes` the button renders into.

*label*
A :class:`matplotlib.text.Text` instance.

*color*
The color of the text box when not hovering.

*hovercolor*
The color of the text box when hovering.

Call :meth:`on_text_change` to be updated whenever the text changes
Call :meth:`on_submit` to be updated whenever the user hits enter or leaves the text entry field
"""

def __init__(self, ax, label, initial = '',
color='.95', hovercolor='1'):
"""
Parameters
----------
ax : matplotlib.axes.Axes
The :class:`matplotlib.axes.Axes` instance the button
will be placed into.

label : str
Label for this text box. Accepts string.

initial : str
Initial value in the text box

color : color
The color of the box

hovercolor : color
The color of the box when the mouse is over it
"""
AxesWidget.__init__(self, ax)

self.DIST_FROM_LEFT = .05

self.params_to_disable = []
for key in rcParams.keys():
if u'keymap' in key:
self.params_to_disable += [key]

self.text = initial




self.label = ax.text(0.0,0.5, label,
verticalalignment='center',
horizontalalignment='right',
transform=ax.transAxes)
self.text_disp = self._make_text_disp(self.text)

self.cnt = 0
self.change_observers = {}
self.submit_observers = {}

self.ax.set_xlim(0, 1) #If these lines are removed, the cursor won't appear
self.ax.set_ylim(0, 1) #the first time the box is clicked

self.cursor_index = 0;
self.cursor = self.ax.vlines(0, 0, 0) #because this is initialized, _render_cursor
self.cursor.set_visible(False) #can assume that cursor exists


self.connect_event('button_press_event', self._click)
self.connect_event('button_release_event', self._release)
self.connect_event('motion_notify_event', self._motion)
self.connect_event('key_press_event', self._keypress)
ax.set_navigate(False)
ax.set_axis_bgcolor(color)
ax.set_xticks([])
ax.set_yticks([])
self.color = color
self.hovercolor = hovercolor

self._lastcolor = color

self.capturekeystrokes = False




def _make_text_disp(self, string):
return self.ax.text(self.DIST_FROM_LEFT, 0.5, string,
verticalalignment='center',
horizontalalignment='left',
transform=self.ax.transAxes)
def _rendercursor(self):
#this is a hack to figure out where the cursor should go.
#we draw the text up to where the cursor should go, measure
#save its dimensions, draw the real text, then put the cursor
#at the saved dimensions

widthtext = self.text[:self.cursor_index]
no_text = False
if(widthtext == "" or widthtext == " " or widthtext == " "):
no_text = widthtext == ""
widthtext = ","


wt_disp = self._make_text_disp(widthtext)

self.ax.figure.canvas.draw()
bb = wt_disp.get_window_extent()
inv = self.ax.transData.inverted()
bb = inv.transform(bb)
wt_disp.set_visible(False)
if no_text:
bb[1, 0] = bb[0, 0]
#hack done
self.cursor.set_visible(False)


self.cursor = self.ax.vlines(bb[1, 0], bb[0, 1], bb[1, 1])
self.ax.figure.canvas.draw()

def _notify_submit_observers(self):
for cid, func in six.iteritems(self.submit_observers):
func(self.text)

def _release(self, event):
if self.ignore(event):
return
if event.canvas.mouse_grabber != self.ax:
return
event.canvas.release_mouse(self.ax)

def _keypress(self, event):
if self.ignore(event):
return
if self.capturekeystrokes:
key = event.key

if(len(key) == 1):
self.text = (self.text[:self.cursor_index] + key +
self.text[self.cursor_index:])
self.cursor_index += 1
elif key == "right":
if self.cursor_index != len(self.text):
self.cursor_index += 1
elif key == "left":
if self.cursor_index != 0:
self.cursor_index -= 1
elif key == "home":
self.cursor_index = 0
elif key == "end":
self.cursor_index = len(self.text)
elif(key == "backspace"):
if self.cursor_index != 0:
self.text = (self.text[:self.cursor_index - 1] +
self.text[self.cursor_index:])
self.cursor_index -= 1
elif(key == "delete"):
if self.cursor_index != len(self.text):
self.text = (self.text[:self.cursor_index] +
self.text[self.cursor_index + 1:])
self.text_disp.remove()
self.text_disp = self._make_text_disp(self.text)
self._rendercursor()
for cid, func in six.iteritems(self.change_observers):
func(self.text)
if key == "enter":
self._notify_submit_observers()

def _click(self, event):
if self.ignore(event):
return
if event.inaxes != self.ax:
notifysubmit = False
#because _notify_submit_users might throw an error in the
#user's code, we only want to call it once we've already done
#our cleanup.
if self.capturekeystrokes:
for key in self.params_to_disable:
rcParams[key] = self.reset_params[key]
notifysubmit = True
self.capturekeystrokes = False
self.cursor.set_visible(False)
self.ax.figure.canvas.draw()

if notifysubmit:
self._notify_submit_observers()
return
if not self.eventson:
return
if event.canvas.mouse_grabber != self.ax:
event.canvas.grab_mouse(self.ax)
if not(self.capturekeystrokes):
self.capturekeystrokes = True
self.reset_params = {}
for key in self.params_to_disable:
self.reset_params[key] = rcParams[key]
rcParams[key] = []
self.cursor_index = len(self.text)
self._rendercursor()


def _motion(self, event):
if self.ignore(event):
return
if event.inaxes == self.ax:
c = self.hovercolor
else:
c = self.color
if c != self._lastcolor:
self.ax.set_axis_bgcolor(c)
self._lastcolor = c
if self.drawon:
self.ax.figure.canvas.draw()

def on_text_change(self, func):
"""
When the text changes, call this *func* with event

A connection id is returned which can be used to disconnect
"""
cid = self.cnt
self.change_observers[cid] = func
self.cnt += 1
return cid
def on_submit(self, func):
"""
When the user hits enter or leaves the submision box, call this *func* with event

A connection id is returned which can be used to disconnect
"""
cid = self.cnt
self.submit_observers[cid] = func
self.cnt += 1
return cid
def disconnect(self, cid):
"""remove the observer with connection id *cid*"""
try:
del self.observers[cid]
except KeyError:
pass

class RadioButtons(AxesWidget):
"""
Expand Down Expand Up @@ -925,6 +1176,7 @@ def funchspace(self, val):
self.targetfig.canvas.draw()



class Cursor(AxesWidget):
"""
A horizontal and vertical line that spans the axes and moves with
Expand Down