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

Skip to content

Commit d6fadfc

Browse files
committed
Cleanup TextBox implementation.
- Reuse a single Text instance to measure cursor width and display text, instead of creating a new one every time the text is edited. - Likewise, reuse a single LineCollection for the cursor (I guess a Line2D would be more efficient but I'm too lazy to go through the API change here). Also, putting the LineCollection in pixel coordinates (`transforms=IdentityTransform()`) makes things much simpler. - Auto-sync `.text` and the contents of the text field. - `._lastcolor` is just the current axes color. - Misc. cleanups.
1 parent dc15e0d commit d6fadfc

File tree

1 file changed

+39
-82
lines changed

1 file changed

+39
-82
lines changed

lib/matplotlib/widgets.py

Lines changed: 39 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,6 @@ def __init__(self, ax, label, image=None,
190190
self.color = color
191191
self.hovercolor = hovercolor
192192

193-
self._lastcolor = color
194-
195193
def _click(self, event):
196194
if (self.ignore(event)
197195
or event.inaxes != self.ax
@@ -706,76 +704,58 @@ def __init__(self, ax, label, initial='',
706704

707705
self.DIST_FROM_LEFT = .05
708706

709-
self.text = initial
710-
self.label = ax.text(-label_pad, 0.5, label,
711-
verticalalignment='center',
712-
horizontalalignment='right',
713-
transform=ax.transAxes)
714-
self.text_disp = self._make_text_disp(self.text)
707+
self.label = ax.text(
708+
-label_pad, 0.5, label, transform=ax.transAxes,
709+
verticalalignment='center', horizontalalignment='right')
710+
self.text_disp = self.ax.text(
711+
self.DIST_FROM_LEFT, 0.5, initial, transform=self.ax.transAxes,
712+
verticalalignment='center', horizontalalignment='left')
715713

716714
self.cnt = 0
717715
self.change_observers = {}
718716
self.submit_observers = {}
719717

720-
# If these lines are removed, the cursor won't appear the first
721-
# time the box is clicked:
722-
self.ax.set_xlim(0, 1)
723-
self.ax.set_ylim(0, 1)
718+
ax.set(
719+
xlim=(0, 1), ylim=(0, 1), # s.t. cursor appears from first click.
720+
navigate=False, facecolor=color,
721+
xticks=[], yticks=[])
724722

725723
self.cursor_index = 0
726724

727-
# Because this is initialized, _render_cursor
728-
# can assume that cursor exists.
729-
self.cursor = self.ax.vlines(0, 0, 0)
730-
self.cursor.set_visible(False)
725+
self.cursor = ax.vlines(0, 0, 0, visible=False,
726+
transform=mpl.transforms.IdentityTransform())
731727

732728
self.connect_event('button_press_event', self._click)
733729
self.connect_event('button_release_event', self._release)
734730
self.connect_event('motion_notify_event', self._motion)
735731
self.connect_event('key_press_event', self._keypress)
736732
self.connect_event('resize_event', self._resize)
737-
ax.set_navigate(False)
738-
ax.set_facecolor(color)
739-
ax.set_xticks([])
740-
ax.set_yticks([])
733+
741734
self.color = color
742735
self.hovercolor = hovercolor
743736

744-
self._lastcolor = color
745-
746737
self.capturekeystrokes = False
747738

748-
def _make_text_disp(self, string):
749-
return self.ax.text(self.DIST_FROM_LEFT, 0.5, string,
750-
verticalalignment='center',
751-
horizontalalignment='left',
752-
transform=self.ax.transAxes)
739+
@property
740+
def text(self):
741+
return self.text_disp.get_text()
753742

754743
def _rendercursor(self):
755744
# this is a hack to figure out where the cursor should go.
756745
# we draw the text up to where the cursor should go, measure
757746
# and save its dimensions, draw the real text, then put the cursor
758747
# at the saved dimensions
759748

760-
widthtext = self.text[:self.cursor_index]
761-
no_text = False
762-
if widthtext in ["", " ", " "]:
763-
no_text = widthtext == ""
764-
widthtext = ","
765-
766-
wt_disp = self._make_text_disp(widthtext)
767-
768-
self.ax.figure.canvas.draw()
769-
bb = wt_disp.get_window_extent()
770-
inv = self.ax.transData.inverted()
771-
bb = inv.transform(bb)
772-
wt_disp.set_visible(False)
773-
if no_text:
774-
bb[1, 0] = bb[0, 0]
775-
# hack done
776-
self.cursor.set_visible(False)
749+
text = self.text_disp.get_text() # Save value before overwriting it.
750+
widthtext = text[:self.cursor_index]
751+
self.text_disp.set_text(widthtext or ",")
752+
bb = self.text_disp.get_window_extent()
753+
if not widthtext: # Use the comma for the height, but keep width to 0.
754+
bb.x1 = bb.x0
755+
self.cursor.set(
756+
segments=[[(bb.x1, bb.y0), (bb.x1, bb.y1)]], visible=True)
757+
self.text_disp.set_text(text)
777758

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

781761
def _notify_submit_observers(self):
@@ -795,33 +775,31 @@ def _keypress(self, event):
795775
return
796776
if self.capturekeystrokes:
797777
key = event.key
798-
778+
text = self.text
799779
if len(key) == 1:
800-
self.text = (self.text[:self.cursor_index] + key +
801-
self.text[self.cursor_index:])
780+
text = (text[:self.cursor_index] + key +
781+
text[self.cursor_index:])
802782
self.cursor_index += 1
803783
elif key == "right":
804-
if self.cursor_index != len(self.text):
784+
if self.cursor_index != len(text):
805785
self.cursor_index += 1
806786
elif key == "left":
807787
if self.cursor_index != 0:
808788
self.cursor_index -= 1
809789
elif key == "home":
810790
self.cursor_index = 0
811791
elif key == "end":
812-
self.cursor_index = len(self.text)
792+
self.cursor_index = len(text)
813793
elif key == "backspace":
814794
if self.cursor_index != 0:
815-
self.text = (self.text[:self.cursor_index - 1] +
816-
self.text[self.cursor_index:])
795+
text = (text[:self.cursor_index - 1] +
796+
text[self.cursor_index:])
817797
self.cursor_index -= 1
818798
elif key == "delete":
819799
if self.cursor_index != len(self.text):
820-
self.text = (self.text[:self.cursor_index] +
821-
self.text[self.cursor_index + 1:])
822-
823-
self.text_disp.remove()
824-
self.text_disp = self._make_text_disp(self.text)
800+
text = (text[:self.cursor_index] +
801+
text[self.cursor_index + 1:])
802+
self.text_disp.set_text(text)
825803
self._rendercursor()
826804
self._notify_change_observers()
827805
if key == "enter":
@@ -831,9 +809,7 @@ def set_val(self, val):
831809
newval = str(val)
832810
if self.text == newval:
833811
return
834-
self.text = newval
835-
self.text_disp.remove()
836-
self.text_disp = self._make_text_disp(self.text)
812+
self.text_disp.set_text(newval)
837813
self._rendercursor()
838814
self._notify_change_observers()
839815
self._notify_submit_observers()
@@ -885,23 +861,8 @@ def position_cursor(self, x):
885861
self.cursor_index = 0
886862
else:
887863
bb = self.text_disp.get_window_extent()
888-
889-
trans = self.ax.transData
890-
inv = self.ax.transData.inverted()
891-
bb = trans.transform(inv.transform(bb))
892-
893-
text_start = bb[0, 0]
894-
text_end = bb[1, 0]
895-
896-
ratio = (x - text_start) / (text_end - text_start)
897-
898-
if ratio < 0:
899-
ratio = 0
900-
if ratio > 1:
901-
ratio = 1
902-
864+
ratio = np.clip((x - bb.x0) / bb.width, 0, 1)
903865
self.cursor_index = int(len(self.text) * ratio)
904-
905866
self._rendercursor()
906867

907868
def _click(self, event):
@@ -924,13 +885,9 @@ def _resize(self, event):
924885
def _motion(self, event):
925886
if self.ignore(event):
926887
return
927-
if event.inaxes == self.ax:
928-
c = self.hovercolor
929-
else:
930-
c = self.color
931-
if c != self._lastcolor:
888+
c = self.hovercolor if event.inaxes == self.ax else self.color
889+
if c != self.ax.get_facecolor():
932890
self.ax.set_facecolor(c)
933-
self._lastcolor = c
934891
if self.drawon:
935892
self.ax.figure.canvas.draw()
936893

0 commit comments

Comments
 (0)