3
3
"""
4
4
5
5
import math
6
+ import copy
6
7
7
8
import matplotlib
8
9
matplotlib .use ('Agg' )
@@ -42,7 +43,9 @@ class MatplotFigure(Widget):
42
43
pos_x_rect_hor = NumericProperty (0 )
43
44
pos_y_rect_hor = NumericProperty (0 )
44
45
pos_x_rect_ver = NumericProperty (0 )
45
- pos_y_rect_ver = NumericProperty (0 )
46
+ pos_y_rect_ver = NumericProperty (0 )
47
+ invert_rect_ver = BooleanProperty (False )
48
+ invert_rect_hor = BooleanProperty (False )
46
49
47
50
def on_figure (self , obj , value ):
48
51
self .figcanvas = _FigureCanvas (self .figure , self )
@@ -53,6 +56,15 @@ def on_figure(self, obj, value):
53
56
self .width = w
54
57
self .height = h
55
58
59
+ if self .figure .axes [0 ]:
60
+ #add copy patch
61
+ ax = self .figure .axes [0 ]
62
+ patch_cpy = copy .copy (ax .patch )
63
+ patch_cpy .set_visible (False )
64
+ ax .spines [:].set_zorder (10 )
65
+ patch_cpy .set_zorder (9 )
66
+ self .background_patch_copy = ax .add_patch (patch_cpy )
67
+
56
68
# Texture
57
69
self ._img_texture = Texture .create (size = (w , h ))
58
70
@@ -85,7 +97,10 @@ def __init__(self, **kwargs):
85
97
#clear touches on touch up
86
98
self ._touches = []
87
99
self ._last_touch_pos = {}
88
-
100
+
101
+ #background
102
+ self .background = None
103
+ self .background_patch_copy = None
89
104
90
105
self .bind (size = self ._onSize )
91
106
@@ -108,8 +123,7 @@ def register_lines(self,lines:list) -> None:
108
123
109
124
#white background for blit method (fast draw)
110
125
props = dict (boxstyle = 'square' ,edgecolor = 'w' , facecolor = 'w' , alpha = 1.0 )
111
- self .text_background = self .axes .text (0.5 , 1.01 , 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' , color = 'w' , transform = self .axes .transAxes , bbox = props )
112
-
126
+
113
127
#cursor text
114
128
self .text = self .axes .text (0.52 , 1.01 , '' , transform = self .axes .transAxes , bbox = props )
115
129
@@ -211,19 +225,22 @@ def hover(self, event) -> None:
211
225
#x y label
212
226
self .text .set_text (f"x={ x } , y={ y } " )
213
227
214
- #blit method (always use because same visual effect as draw)
215
- self .axes .draw_artist (self .axes .patch )
216
- self .axes .draw_artist (self .text_background )
228
+ #blit method (always use because same visual effect as draw)
229
+ if self .background is None :
230
+ self .set_cross_hair_visible (False )
231
+ self .axes .figure .canvas .draw_idle ()
232
+ self .axes .figure .canvas .flush_events ()
233
+ self .background = self .axes .figure .canvas .copy_from_bbox (self .axes .figure .bbox )
234
+ self .set_cross_hair_visible (True )
235
+
236
+ self .axes .figure .canvas .restore_region (self .background )
217
237
self .axes .draw_artist (self .text )
218
- self .axes .draw_artist (list (self .axes .spines .values ())[0 ])
219
238
220
- for line in self .axes .lines :
221
- self .axes .draw_artist (line )
239
+ self .axes .draw_artist ( self . horizontal_line )
240
+ self .axes .draw_artist (self . vertical_line )
222
241
223
- mybbox = self .my_blit_box (ax .bbox .bounds [0 ],ax .bbox .bounds [1 ],ax .bbox .bounds [2 ],ax .bbox .bounds [3 ]+ 50 )
224
-
225
242
#draw (blit method)
226
- self .axes .figure .canvas .blit (mybbox )
243
+ self .axes .figure .canvas .blit (self . axes . bbox )
227
244
self .axes .figure .canvas .flush_events ()
228
245
229
246
#if touch is too far, hide cross hair cursor
@@ -236,12 +253,18 @@ def home(self) -> None:
236
253
Return:
237
254
None
238
255
"""
239
- ax = self .axes
240
- ax .set_xlim (self .xmin , self .xmax )
241
- ax .set_ylim (self .ymin , self .ymax )
242
-
243
- ax .figure .canvas .draw_idle ()
244
- ax .figure .canvas .flush_events ()
256
+ #do nothing is all min/max are not set
257
+ if self .xmin is not None and \
258
+ self .xmax is not None and \
259
+ self .ymin is not None and \
260
+ self .ymax is not None :
261
+
262
+ ax = self .axes
263
+ ax .set_xlim (self .xmin , self .xmax )
264
+ ax .set_ylim (self .ymin , self .ymax )
265
+
266
+ ax .figure .canvas .draw_idle ()
267
+ ax .figure .canvas .flush_events ()
245
268
246
269
def reset_touch (self ) -> None :
247
270
""" reset touch
@@ -251,11 +274,6 @@ def reset_touch(self) -> None:
251
274
"""
252
275
self ._touches = []
253
276
self ._last_touch_pos = {}
254
-
255
- @staticmethod
256
- def my_blit_box (x0 , y0 , width , height ):
257
- """ build custom matplotlib bbox """
258
- return Bbox .from_bounds (x0 , y0 , width , height )
259
277
260
278
def _get_scale (self ):
261
279
""" kivy scatter _get_scale method """
@@ -396,7 +414,10 @@ def on_touch_down(self, event):
396
414
event .grab (self )
397
415
self ._touches .append (event )
398
416
self ._last_touch_pos [event ] = event .pos
399
-
417
+ if len (self ._touches )> 1 :
418
+ #new touch, reset background
419
+ self .background = None
420
+
400
421
return True
401
422
402
423
else :
@@ -455,6 +476,7 @@ def on_touch_up(self, event):
455
476
self .update_lim ()
456
477
457
478
ax = self .axes
479
+ self .background = None
458
480
ax .figure .canvas .draw_idle ()
459
481
ax .figure .canvas .flush_events ()
460
482
@@ -463,7 +485,7 @@ def on_touch_up(self, event):
463
485
def apply_zoom (self , scale_factor , ax , anchor = (0 , 0 ),new_line = None ):
464
486
""" zoom touch method """
465
487
466
- x = anchor [0 ]
488
+ x = anchor [0 ]- self . pos [ 0 ]
467
489
y = anchor [1 ]- self .pos [1 ]
468
490
469
491
trans = ax .transData .inverted ()
@@ -483,11 +505,13 @@ def apply_zoom(self, scale_factor, ax, anchor=(0, 0),new_line=None):
483
505
484
506
if self .fast_draw :
485
507
#use blit method
486
- ax .draw_artist (ax .patch )
487
-
488
- #if you want the left spline during on_move (slower)
489
- if self .draw_left_spline :
490
- ax .draw_artist (list (ax .spines .values ())[0 ])
508
+ if self .background is None :
509
+ self .background_patch_copy .set_visible (True )
510
+ ax .figure .canvas .draw_idle ()
511
+ ax .figure .canvas .flush_events ()
512
+ self .background = ax .figure .canvas .copy_from_bbox (ax .figure .bbox )
513
+ self .background_patch_copy .set_visible (False )
514
+ ax .figure .canvas .restore_region (self .background )
491
515
492
516
for line in ax .lines :
493
517
ax .draw_artist (line )
@@ -514,11 +538,14 @@ def apply_pan(self, ax, event):
514
538
ax .set_ylim (cur_ylim )
515
539
516
540
if self .fast_draw :
517
- #use blit method
518
- ax .draw_artist (ax .patch )
519
- #if you want the left spline during on_move (slower)
520
- if self .draw_left_spline :
521
- ax .draw_artist (list (ax .spines .values ())[0 ])
541
+ #use blit method
542
+ if self .background is None :
543
+ self .background_patch_copy .set_visible (True )
544
+ ax .figure .canvas .draw_idle ()
545
+ ax .figure .canvas .flush_events ()
546
+ self .background = ax .figure .canvas .copy_from_bbox (ax .figure .bbox )
547
+ self .background_patch_copy .set_visible (False )
548
+ ax .figure .canvas .restore_region (self .background )
522
549
523
550
for line in ax .lines :
524
551
ax .draw_artist (line )
@@ -611,6 +638,8 @@ def reset_box(self):
611
638
self ._pos_y_rect_ver = 0
612
639
self ._alpha_hor = 0
613
640
self ._alpha_ver = 0
641
+ self .invert_rect_hor = False
642
+ self .invert_rect_ver = False
614
643
615
644
def draw_box (self , event , x0 , y0 , x1 , y1 ) -> None :
616
645
""" Draw zoombox method
@@ -680,10 +709,10 @@ def draw_box(self, event, x0, y0, x1, y1) -> None:
680
709
x0 = x1_min [0 ][0 ]+ pos_x
681
710
682
711
x0_max = self .axes .transData .transform ([(xmax ,ymin )])
683
- x1 = x0_max [0 ][0 ]+ pos_x
712
+ x1 = x0_max [0 ][0 ]+ pos_x
684
713
685
- self ._alpha_ver = 1
686
-
714
+ self ._alpha_ver = 1
715
+
687
716
elif abs (y1 - y0 )< dp (20 ) and abs (x1 - x0 )> self .minzoom :
688
717
self .pos_x_rect_hor = x0
689
718
self .pos_y_rect_hor = y0
@@ -699,6 +728,15 @@ def draw_box(self, event, x0, y0, x1, y1) -> None:
699
728
else :
700
729
self ._alpha_hor = 0
701
730
self ._alpha_ver = 0
731
+
732
+ if x1 > x0 :
733
+ self .invert_rect_ver = False
734
+ else :
735
+ self .invert_rect_ver = True
736
+ if y1 > y0 :
737
+ self .invert_rect_hor = False
738
+ else :
739
+ self .invert_rect_hor = True
702
740
703
741
self ._box_pos = x0 , y0
704
742
self ._box_size = x1 - x0 , y1 - y0
@@ -754,39 +792,47 @@ def blit(self, bbox=None):
754
792
source: 'border.png'
755
793
pos: self._box_pos
756
794
size: self._box_size
757
- border: 3, 3, 3, 3
758
-
795
+ border:
796
+ dp(1) if root.invert_rect_hor else -dp(1), \
797
+ dp(1) if root.invert_rect_ver else -dp(1), \
798
+ dp(1) if root.invert_rect_hor else -dp(1), \
799
+ dp(1) if root.invert_rect_ver else -dp(1)
800
+
759
801
canvas.after:
760
802
#horizontal rectangle left
761
803
Color:
762
804
rgba:0, 0, 0, self._alpha_hor
763
805
Line:
764
806
width: dp(1)
765
807
rectangle:
766
- (self.pos_x_rect_hor-dp(3), self.pos_y_rect_hor-dp(20), dp(4),dp(40))
808
+ (self.pos_x_rect_hor+dp(1) if root.invert_rect_ver \
809
+ else self.pos_x_rect_hor-dp(4),self.pos_y_rect_hor-dp(20), dp(4),dp(40))
767
810
768
811
#horizontal rectangle right
769
812
Color:
770
813
rgba:0, 0, 0, self._alpha_hor
771
814
Line:
772
815
width: dp(1)
773
816
rectangle:
774
- (self.pos_x_rect_hor-dp(1)+self._box_size[0], self.pos_y_rect_hor-dp(20), dp(4),dp(40))
817
+ (self.pos_x_rect_hor-dp(4)+self._box_size[0] if root.invert_rect_ver \
818
+ else self.pos_x_rect_hor+dp(1)+self._box_size[0], self.pos_y_rect_hor-dp(20), dp(4),dp(40))
775
819
776
820
#vertical rectangle bottom
777
821
Color:
778
822
rgba:0, 0, 0, self._alpha_ver
779
823
Line:
780
824
width: dp(1)
781
825
rectangle:
782
- (self.pos_x_rect_ver-dp(20), self.pos_y_rect_ver-dp(1), dp(40),dp(4))
826
+ (self.pos_x_rect_ver-dp(20),self.pos_y_rect_ver+dp(1) if root.invert_rect_hor else \
827
+ self.pos_y_rect_ver-dp(4), dp(40),dp(4))
783
828
784
829
#vertical rectangle top
785
830
Color:
786
831
rgba:0, 0, 0, self._alpha_ver
787
832
Line:
788
833
width: dp(1)
789
834
rectangle:
790
- (self.pos_x_rect_ver-dp(20), self.pos_y_rect_ver-dp(3)+self._box_size[1], dp(40),dp(4))
791
-
835
+ (self.pos_x_rect_ver-dp(20),self.pos_y_rect_ver-dp(4)+self._box_size[1] \
836
+ if root.invert_rect_hor else self.pos_y_rect_ver+dp(1)+self._box_size[1], \
837
+ dp(40),dp(4))
792
838
''' )
0 commit comments