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

Skip to content

Commit 80c20f3

Browse files
HastingsGreerfariza
authored andcommitted
Added a text entry widget, that allows usere to register to be notified when text changes or text is submitted
1 parent 6eeb109 commit 80c20f3

File tree

3 files changed

+264
-1
lines changed

3 files changed

+264
-1
lines changed

CHANGELOG

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
12
2015-11-16 Levels passed to contour(f) and tricontour(f) must be in increasing
23
order.
34

5+
2015-10-21 Added TextBox widget
6+
7+
48
2015-10-21 Added get_ticks_direction()
59

610
2015-02-27 Added the rcParam 'image.composite_image' to permit users

doc/users/whats_new.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.. _whats-new:
1+
w.. _whats-new:
22

33
************************
44
What's new in matplotlib
@@ -244,6 +244,13 @@ Some parameters have been added, others have been improved.
244244
Widgets
245245
-------
246246

247+
Added TextBox Widget
248+
249+
````````````````````
250+
Added a widget that allows text entry by reading key events when it is active.
251+
Text caret in text box is visible when it is active, can be moved using arrow keys but not mouse
252+
253+
247254
Active state of Selectors
248255
`````````````````````````
249256

lib/matplotlib/widgets.py

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from six.moves import zip
1818

1919
import numpy as np
20+
from matplotlib import rcParams
2021

2122
from .mlab import dist
2223
from .patches import Circle, Rectangle, Ellipse
@@ -627,6 +628,256 @@ def disconnect(self, cid):
627628
except KeyError:
628629
pass
629630

631+
class TextBox(AxesWidget):
632+
"""
633+
A GUI neutral text input box.
634+
635+
For the text box to remain responsive
636+
you must keep a reference to it.
637+
638+
The following attributes are accessible
639+
640+
*ax*
641+
The :class:`matplotlib.axes.Axes` the button renders into.
642+
643+
*label*
644+
A :class:`matplotlib.text.Text` instance.
645+
646+
*color*
647+
The color of the text box when not hovering.
648+
649+
*hovercolor*
650+
The color of the text box when hovering.
651+
652+
Call :meth:`on_text_change` to be updated whenever the text changes
653+
Call :meth:`on_submit` to be updated whenever the user hits enter or leaves the text entry field
654+
"""
655+
656+
def __init__(self, ax, label, initial = '',
657+
color='.95', hovercolor='1'):
658+
"""
659+
Parameters
660+
----------
661+
ax : matplotlib.axes.Axes
662+
The :class:`matplotlib.axes.Axes` instance the button
663+
will be placed into.
664+
665+
label : str
666+
Label for this text box. Accepts string.
667+
668+
initial : str
669+
Initial value in the text box
670+
671+
color : color
672+
The color of the box
673+
674+
hovercolor : color
675+
The color of the box when the mouse is over it
676+
"""
677+
AxesWidget.__init__(self, ax)
678+
679+
self.DIST_FROM_LEFT = .05
680+
681+
self.params_to_disable = []
682+
for key in rcParams.keys():
683+
if u'keymap' in key:
684+
self.params_to_disable += [key]
685+
686+
self.text = initial
687+
688+
689+
690+
691+
self.label = ax.text(0.0,0.5, label,
692+
verticalalignment='center',
693+
horizontalalignment='right',
694+
transform=ax.transAxes)
695+
self.text_disp = self._make_text_disp(self.text)
696+
697+
self.cnt = 0
698+
self.change_observers = {}
699+
self.submit_observers = {}
700+
701+
self.ax.set_xlim(0, 1) #If these lines are removed, the cursor won't appear
702+
self.ax.set_ylim(0, 1) #the first time the box is clicked
703+
704+
self.cursor_index = 0;
705+
self.cursor = self.ax.vlines(0, 0, 0) #because this is initialized, _render_cursor
706+
self.cursor.set_visible(False) #can assume that cursor exists
707+
708+
709+
self.connect_event('button_press_event', self._click)
710+
self.connect_event('button_release_event', self._release)
711+
self.connect_event('motion_notify_event', self._motion)
712+
self.connect_event('key_press_event', self._keypress)
713+
ax.set_navigate(False)
714+
ax.set_axis_bgcolor(color)
715+
ax.set_xticks([])
716+
ax.set_yticks([])
717+
self.color = color
718+
self.hovercolor = hovercolor
719+
720+
self._lastcolor = color
721+
722+
self.capturekeystrokes = False
723+
724+
725+
726+
727+
def _make_text_disp(self, string):
728+
return self.ax.text(self.DIST_FROM_LEFT, 0.5, string,
729+
verticalalignment='center',
730+
horizontalalignment='left',
731+
transform=self.ax.transAxes)
732+
def _rendercursor(self):
733+
#this is a hack to figure out where the cursor should go.
734+
#we draw the text up to where the cursor should go, measure
735+
#save its dimensions, draw the real text, then put the cursor
736+
#at the saved dimensions
737+
738+
widthtext = self.text[:self.cursor_index]
739+
no_text = False
740+
if(widthtext == "" or widthtext == " " or widthtext == " "):
741+
no_text = widthtext == ""
742+
widthtext = ","
743+
744+
745+
wt_disp = self._make_text_disp(widthtext)
746+
747+
self.ax.figure.canvas.draw()
748+
bb = wt_disp.get_window_extent()
749+
inv = self.ax.transData.inverted()
750+
bb = inv.transform(bb)
751+
wt_disp.set_visible(False)
752+
if no_text:
753+
bb[1, 0] = bb[0, 0]
754+
#hack done
755+
self.cursor.set_visible(False)
756+
757+
758+
self.cursor = self.ax.vlines(bb[1, 0], bb[0, 1], bb[1, 1])
759+
self.ax.figure.canvas.draw()
760+
761+
def _notify_submit_observers(self):
762+
for cid, func in six.iteritems(self.submit_observers):
763+
func(self.text)
764+
765+
def _release(self, event):
766+
if self.ignore(event):
767+
return
768+
if event.canvas.mouse_grabber != self.ax:
769+
return
770+
event.canvas.release_mouse(self.ax)
771+
772+
def _keypress(self, event):
773+
if self.ignore(event):
774+
return
775+
if self.capturekeystrokes:
776+
key = event.key
777+
778+
if(len(key) == 1):
779+
self.text = (self.text[:self.cursor_index] + key +
780+
self.text[self.cursor_index:])
781+
self.cursor_index += 1
782+
elif key == "right":
783+
if self.cursor_index != len(self.text):
784+
self.cursor_index += 1
785+
elif key == "left":
786+
if self.cursor_index != 0:
787+
self.cursor_index -= 1
788+
elif key == "home":
789+
self.cursor_index = 0
790+
elif key == "end":
791+
self.cursor_index = len(self.text)
792+
elif(key == "backspace"):
793+
if self.cursor_index != 0:
794+
self.text = (self.text[:self.cursor_index - 1] +
795+
self.text[self.cursor_index:])
796+
self.cursor_index -= 1
797+
elif(key == "delete"):
798+
if self.cursor_index != len(self.text):
799+
self.text = (self.text[:self.cursor_index] +
800+
self.text[self.cursor_index + 1:])
801+
self.text_disp.remove()
802+
self.text_disp = self._make_text_disp(self.text)
803+
self._rendercursor()
804+
for cid, func in six.iteritems(self.change_observers):
805+
func(self.text)
806+
if key == "enter":
807+
self._notify_submit_observers()
808+
809+
def _click(self, event):
810+
if self.ignore(event):
811+
return
812+
if event.inaxes != self.ax:
813+
notifysubmit = False
814+
#because _notify_submit_users might throw an error in the
815+
#user's code, we only want to call it once we've already done
816+
#our cleanup.
817+
if self.capturekeystrokes:
818+
for key in self.params_to_disable:
819+
rcParams[key] = self.reset_params[key]
820+
notifysubmit = True
821+
self.capturekeystrokes = False
822+
self.cursor.set_visible(False)
823+
self.ax.figure.canvas.draw()
824+
825+
if notifysubmit:
826+
self._notify_submit_observers()
827+
return
828+
if not self.eventson:
829+
return
830+
if event.canvas.mouse_grabber != self.ax:
831+
event.canvas.grab_mouse(self.ax)
832+
if not(self.capturekeystrokes):
833+
self.capturekeystrokes = True
834+
self.reset_params = {}
835+
for key in self.params_to_disable:
836+
self.reset_params[key] = rcParams[key]
837+
rcParams[key] = []
838+
self.cursor_index = len(self.text)
839+
self._rendercursor()
840+
841+
842+
def _motion(self, event):
843+
if self.ignore(event):
844+
return
845+
if event.inaxes == self.ax:
846+
c = self.hovercolor
847+
else:
848+
c = self.color
849+
if c != self._lastcolor:
850+
self.ax.set_axis_bgcolor(c)
851+
self._lastcolor = c
852+
if self.drawon:
853+
self.ax.figure.canvas.draw()
854+
855+
def on_text_change(self, func):
856+
"""
857+
When the text changes, call this *func* with event
858+
859+
A connection id is returned which can be used to disconnect
860+
"""
861+
cid = self.cnt
862+
self.change_observers[cid] = func
863+
self.cnt += 1
864+
return cid
865+
def on_submit(self, func):
866+
"""
867+
When the user hits enter or leaves the submision box, call this *func* with event
868+
869+
A connection id is returned which can be used to disconnect
870+
"""
871+
cid = self.cnt
872+
self.submit_observers[cid] = func
873+
self.cnt += 1
874+
return cid
875+
def disconnect(self, cid):
876+
"""remove the observer with connection id *cid*"""
877+
try:
878+
del self.observers[cid]
879+
except KeyError:
880+
pass
630881

631882
class RadioButtons(AxesWidget):
632883
"""
@@ -925,6 +1176,7 @@ def funchspace(self, val):
9251176
self.targetfig.canvas.draw()
9261177

9271178

1179+
9281180
class Cursor(AxesWidget):
9291181
"""
9301182
A horizontal and vertical line that spans the axes and moves with

0 commit comments

Comments
 (0)