8000 Text box widget, take over of PR5375 by fariza · Pull Request #6988 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

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
Prev Previous commit
Next Next commit
Added a text box example: evaluates any string inputs as y(x).
  • Loading branch information
HastingsGreer authored and fariza committed Aug 29, 2016
commit e70217b1263ea6b00593adb4012c8a0b2e8cf248
4 changes: 2 additions & 2 deletions doc/users/whats_new.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
w.. _whats-new:
.. _whats-new:

************************
What's new in matplotlib
Expand Down Expand Up @@ -245,8 +245,8 @@ 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

Expand Down
23 changes: 23 additions & 0 deletions examples/widgets/textbox.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import TextBox
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.2)
t = np.arange(-2.0, 2.0, 0.001)
s = t ** 2
initial_text = "t ** 2"
l, = plt.plot(t, s, lw=2)


def submit(text):
ydata = eval(text)
l.set_ydata(ydata)
ax.set_ylim(np.min(ydata), np.max(ydata))
plt.draw()

axbox = plt.axes([0.1, 0.05, 0.8, 0.075])
text_box = TextBox(axbox, 'Evaluate', initial=initial_text)
text_box.on_submit(submit)

plt.show()
161 changes: 81 additions & 80 deletions lib/matplotlib/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -628,14 +628,14 @@ 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.
For the text box to remain responsive you must keep a reference to it.

The following attributes are accessible
The following attributes are accessible:

*ax*
The :class:`matplotlib.axes.Axes` the button renders into.
Expand All @@ -649,11 +649,13 @@ class TextBox(AxesWidget):
*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
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 = '',
def __init__(self, ax, label, initial='',
color='.95', hovercolor='1'):
"""
Parameters
Expand All @@ -664,48 +666,48 @@ def __init__(self, ax, label, initial = '',

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.DIST_FROM_LEFT = .05

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

self.text = initial




self.label = ax.text(0.0,0.5, label,
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



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

self.cursor_index = 0

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

self.connect_event('button_press_event', self._click)
self.connect_event('button_release_event', self._release)
self.connect_event('motion_notify_event', self._motion)
Expand All @@ -718,110 +720,107 @@ def __init__(self, ax, label, initial = '',
self.hovercolor = hovercolor

self._lastcolor = color

self.capturekeystrokes = False




self.capturekeystrokes = False

def _make_text_disp(self, string):
return self.ax.text(self.DIST_FROM_LEFT, 0.5, string,
verticalalignment='center',
EDBE horizontalalignment='left',
transform=self.ax.transAxes)
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
# 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
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
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
if self.cursor_index != len(self.text):
self.cursor_index += 1
elif key == "left":
if self.cursor_index != 0:
self.cursor_index -= 1
if self.cursor_index != 0:
self.cursor_index -= 1
elif key == "home":
self.cursor_index = 0
self.cursor_index = 0
elif key == "end":
self.cursor_index = len(self.text)
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.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:] 1E0A )
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()
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.
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:
for key in self.params_to_disable:
rcParams[key] = self.reset_params[key]
notifysubmit = True
notifysubmit = True
self.capturekeystrokes = False
self.cursor.set_visible(False)
self.ax.figure.canvas.draw()

if notifysubmit:
self._notify_submit_observers()
return
Expand All @@ -838,7 +837,6 @@ def _click(self, event):
self.cursor_index = len(self.text)
self._rendercursor()


def _motion(self, event):
if self.ignore(event):
return
Expand All @@ -854,31 +852,35 @@ def _motion(self, event):

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

A connection id is returned which can be used to disconnect
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
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
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):
"""
A GUI neutral radio button.
Expand Down Expand Up @@ -1176,7 +1178,6 @@ 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
0