mwxlib 1.7.13__py3-none-any.whl → 1.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mwx/matplot2g.py CHANGED
@@ -13,7 +13,6 @@ from scipy import ndimage as ndi
13
13
 
14
14
  from . import framework as mwx
15
15
  from .framework import Menu
16
- # from .utilus import warn
17
16
  from .utilus import funcall as _F
18
17
  from .controls import Clipboard
19
18
  from .matplot2 import MatplotPanel
@@ -125,30 +124,35 @@ class AxesImagePhantom:
125
124
  **kwargs: frame attributes
126
125
 
127
126
  Note:
128
- Due to the problem of performance,
129
- the image pixel size could be reduced by binning.
127
+ Due to the problem of performance, the image pixel size could be reduced by binning.
130
128
  """
131
129
  def __init__(self, parent, buf, name, show=True, **kwargs):
132
130
  self.parent = parent
131
+
132
+ ## Properties of the frame/image.
133
133
  self.__name = name
134
134
  self.__attributes = kwargs
135
+ self.__pathname = kwargs.get('pathname')
136
+ self.__annotation = kwargs.get('annotation', '')
135
137
  self.__localunit = kwargs.get('localunit')
136
- self.__center = kwargs.get('center', (0, 0))
137
- self.__aspect_ratio = 1
138
+ self.__center = kwargs.get('center', [0, 0])
139
+
140
+ ## Conditions for image loading.
138
141
  self.__buf = _to_buffer(buf)
139
142
  bins, vlim, img = _to_image(self.__buf,
140
- cutoff = self.parent.score_percentile,
141
- threshold = self.parent.nbytes_threshold,
142
- )
143
+ cutoff=self.parent.score_percentile,
144
+ threshold=self.parent.nbytes_threshold,
145
+ )
143
146
  self.__bins = bins
144
147
  self.__cuts = vlim
145
148
  self.__art = parent.axes.imshow(img,
146
- cmap = cm.gray,
147
- aspect = 'equal',
148
- interpolation = 'nearest',
149
- visible = show,
150
- picker = True,
151
- )
149
+ cmap=cm.gray,
150
+ aspect='equal', # cf. aspect_ratio => xy_unit
151
+ interpolation='nearest',
152
+ visible=show,
153
+ picker=True,
154
+ )
155
+ self.aspect_ratio = 1
152
156
  self.update_extent()
153
157
 
154
158
  def __getattr__(self, attr):
@@ -159,29 +163,47 @@ class AxesImagePhantom:
159
163
  return x is self.__art
160
164
 
161
165
  def update_attr(self, attr):
162
- """Update frame-specifc attributes:
163
-
164
- annotation : aux info (also displayed as a message in the infobar)
165
- center : frame.center defaults to (0, 0)
166
- localunit : frame.unit
167
- pathname : full path of the buffer file
168
- """
166
+ """Update frame-specifc attributes."""
169
167
  if not attr:
170
168
  return
171
- self.__attributes.update(attr)
172
169
 
173
- if 'localunit' in attr:
174
- self.unit = attr['localunit'] # => [frame_updated]
175
-
176
- if 'center' in attr:
177
- self.center = attr['center'] # => [frame_updated]
170
+ FLAG_ANNOTATION = 1
171
+ FLAG_UPDATE_EXTENT = 2
172
+ flag = 0
173
+ if 'pathname' in attr:
174
+ self.__pathname = attr['pathname']
175
+ flag |= FLAG_ANNOTATION
178
176
 
179
177
  if 'annotation' in attr:
180
- v = attr['annotation']
178
+ self.__annotation = attr['annotation']
181
179
  if self.parent.frame is self:
182
- self.parent.infobar.ShowMessage(v)
180
+ self.parent.infobar.ShowMessage(attr['annotation'])
181
+ flag |= FLAG_ANNOTATION
182
+
183
+ if 'center' in attr:
184
+ v = list(attr['center']) # for json format
185
+ if v != self.__center:
186
+ self.__center = v
187
+ flag |= FLAG_UPDATE_EXTENT
188
+
189
+ if 'localunit' in attr:
190
+ v = attr['localunit']
191
+ if v is None or np.isnan(v): # nan => None: undefined.
192
+ v = None
193
+ elif np.isinf(v):
194
+ raise ValueError("The unit value must not be inf")
195
+ elif v <= 0:
196
+ raise ValueError("The unit value must be greater than zero")
197
+ if v != self.__localunit:
198
+ self.__localunit = v
199
+ flag |= FLAG_UPDATE_EXTENT
200
+
201
+ self.__attributes.update(attr)
183
202
 
184
- if {'pathname', 'annotation'} & attr.keys():
203
+ if flag & FLAG_UPDATE_EXTENT:
204
+ self.update_extent()
205
+ self.parent.canvas.draw_idle()
206
+ if flag:
185
207
  self.parent.handler('frame_updated', self)
186
208
 
187
209
  def update_buffer(self, buf=None):
@@ -190,9 +212,9 @@ class AxesImagePhantom:
190
212
  self.__buf = _to_buffer(buf)
191
213
 
192
214
  bins, vlim, img = _to_image(self.__buf,
193
- cutoff = self.parent.score_percentile,
194
- threshold = self.parent.nbytes_threshold,
195
- )
215
+ cutoff = self.parent.score_percentile,
216
+ threshold = self.parent.nbytes_threshold,
217
+ )
196
218
  self.__bins = bins
197
219
  self.__cuts = vlim
198
220
  self.__art.set_array(img)
@@ -232,80 +254,44 @@ class AxesImagePhantom:
232
254
  doc="Auxiliary info about the frame.")
233
255
 
234
256
  pathname = property(
235
- lambda self: self.__attributes.get('pathname'),
257
+ lambda self: self.__pathname,
236
258
  lambda self, v: self.update_attr({'pathname': v}),
237
259
  doc="Fullpath of the buffer, if bound to a file.")
238
260
 
239
261
  annotation = property(
240
- lambda self: self.__attributes.get('annotation', ''),
262
+ lambda self: self.__annotation,
241
263
  lambda self, v: self.update_attr({'annotation': v}),
242
264
  doc="Annotation of the buffer.")
243
265
 
244
- @property
245
- def name(self):
246
- return self.__name
266
+ center = property(
267
+ lambda self: self.__center,
268
+ lambda self, v: self.update_attr({'center': v}),
269
+ doc="Center coordinates of the frame in logical units.")
247
270
 
248
- @name.setter
249
- def name(self, v):
250
- self.__name = v
251
- self.parent.handler('frame_updated', self)
271
+ localunit = property(
272
+ lambda self: self.__localunit,
273
+ lambda self, v: self.update_attr({'localunit': v}),
274
+ doc="Logical length per pixel in arbitrary units [u/pix], or None if not assigned.")
252
275
 
253
- @property
254
- def localunit(self):
255
- return self.__localunit
256
-
257
- @property
258
- def unit(self):
259
- """Logical length per pixel arb.unit [u/pix]."""
260
- return self.__localunit or self.parent.unit
261
-
262
- @unit.setter
263
- def unit(self, v):
264
- if v == self.__localunit: # no effect
265
- return
266
- if v is None or np.isnan(v): # nan => undefined
267
- v = None
268
- elif np.isinf(v):
269
- raise ValueError("The unit value must not be inf")
270
- elif v <= 0:
271
- raise ValueError("The unit value must be greater than zero")
272
-
273
- self.__localunit = v
274
- self.__attributes['localunit'] = self.__localunit
275
- self.update_extent()
276
- self.parent.handler('frame_updated', self)
277
- self.parent.canvas.draw_idle()
278
-
279
- @unit.deleter
280
- def unit(self):
281
- self.unit = None
276
+ unit = property(
277
+ lambda self: self.__localunit or self.parent.unit,
278
+ lambda self, v: self.update_attr({'localunit': v}),
279
+ doc="Logical length per pixel in arbitrary units [u/pix].")
282
280
 
283
281
  @property
284
282
  def xy_unit(self):
285
- u = self.__localunit or self.parent.unit
286
- return (u, u * self.__aspect_ratio)
287
-
288
- @property
289
- def center(self):
290
- """Center of logical unit."""
291
- return self.__center
292
-
293
- @center.setter
294
- def center(self, v):
295
- self.__center = tuple(v)
296
- self.__attributes['center'] = self.__center
297
- self.update_extent()
298
- self.parent.handler('frame_updated', self)
283
+ """Logical length per pixel in arbitrary units [u/pix] for (X, Y) directions."""
284
+ u = self.unit
285
+ r = self.aspect_ratio
286
+ return (u, u) if r == 1 else (u, u * r)
299
287
 
300
288
  @property
301
- def aspect_ratio(self):
302
- """Aspect ratio of logical unit."""
303
- return self.__aspect_ratio
289
+ def name(self):
290
+ return self.__name
304
291
 
305
- @aspect_ratio.setter
306
- def aspect_ratio(self, v):
307
- self.__aspect_ratio = v or 1
308
- self.update_extent()
292
+ @name.setter
293
+ def name(self, v):
294
+ self.__name = v
309
295
  self.parent.handler('frame_updated', self)
310
296
 
311
297
  @property
@@ -317,7 +303,7 @@ class AxesImagePhantom:
317
303
  def roi(self):
318
304
  """Current buffer ROI (region of interest)."""
319
305
  if self.parent.region.size:
320
- nx, ny = self.xytopixel(*self.region)
306
+ nx, ny = self.xytopixel(self.region)
321
307
  sx = slice(max(0, nx[0]), nx[1]) # nx slice
322
308
  sy = slice(max(0, ny[1]), ny[0]) # ny slice 反転 (降順)
323
309
  return self.__buf[sy, sx]
@@ -326,7 +312,7 @@ class AxesImagePhantom:
326
312
  @roi.setter
327
313
  def roi(self, v):
328
314
  if not self.parent.region.size:
329
- raise ValueError("region is not selected.")
315
+ raise ValueError("region is not selected.")
330
316
  self.roi[:] = v # cannot broadcast input array into different shape
331
317
  self.update_buffer()
332
318
 
@@ -393,43 +379,31 @@ class AxesImagePhantom:
393
379
 
394
380
  @property
395
381
  def selector_pix(self):
396
- """Selected points array [[x],[y]] in pixels."""
382
+ """Selected points array [[x], [y]] in pixels."""
397
383
  return self.xytopixel(self.selector)
398
384
 
399
385
  @selector_pix.setter
400
386
  def selector_pix(self, v):
401
387
  self.selector = self.xyfrompixel(v)
402
388
 
403
- @selector_pix.deleter
404
- def selector_pix(self):
405
- del self.selector
406
-
407
389
  @property
408
390
  def markers_pix(self):
409
- """Marked points data array [[x],[y]] in pixels."""
391
+ """Marked points data array [[x], [y]] in pixels."""
410
392
  return self.xytopixel(self.markers)
411
393
 
412
394
  @markers_pix.setter
413
395
  def markers_pix(self, v):
414
396
  self.markers = self.xyfrompixel(v)
415
397
 
416
- @markers_pix.deleter
417
- def markers_pix(self):
418
- del self.markers
419
-
420
398
  @property
421
399
  def region_pix(self):
422
- """Cropped points data array [l,r],[b,t] in pixels."""
400
+ """Cropped points data array [[l,r], [b,t]] in pixels."""
423
401
  return self.xytopixel(self.region)
424
402
 
425
403
  @region_pix.setter
426
404
  def region_pix(self, v):
427
405
  self.region = self.xyfrompixel(v)
428
406
 
429
- @region_pix.deleter
430
- def region_pix(self):
431
- del self.region
432
-
433
407
 
434
408
  class GraphPlot(MatplotPanel):
435
409
  """Graph panel for 2D graph.
@@ -440,41 +414,41 @@ class GraphPlot(MatplotPanel):
440
414
  def _draw(evt):
441
415
  self.canvas.draw_idle()
442
416
 
443
- self.handler.update({ # DNA<GraphPlot>
417
+ self.handler.update({ # DNA<GraphPlot>
444
418
  None : {
445
- 'frame_shown' : [ None ], # show
446
- 'frame_hidden' : [ None ], # show
447
- 'frame_loaded' : [ None ], # load
448
- 'frame_removed' : [ None ], # del[] ! event arg is indices, not frames.
449
- 'frame_selected' : [ None ], # = focus_set
450
- 'frame_deselected' : [ None ], # = focus_kill
451
- 'frame_modified' : [ None, _F(self.writeln) ], # set[],load,roi => update_buffer
452
- 'frame_updated' : [ None, _F(self.writeln) ], # unit,name,ratio => update_extent
453
- 'frame_cmapped' : [ None, _F(self.writeln) ], # cmap
454
- 'line_draw' : [ None ],
455
- 'line_drawn' : [ None, _draw ],
456
- 'line_move' : [ None ],
457
- 'line_moved' : [ None, _draw ],
458
- 'line_removed' : [ None, _draw ],
459
- 'mark_draw' : [ None ],
460
- 'mark_drawn' : [ None, _draw ],
461
- 'mark_removed' : [ None, _draw ],
462
- 'region_draw' : [ None ],
463
- 'region_drawn' : [ None, _draw ],
464
- 'region_removed' : [ None, _draw ],
465
- 'M-up pressed' : [ None, self.OnPageUp ],
466
- 'M-down pressed' : [ None, self.OnPageDown ],
467
- 'pageup pressed' : [ None, self.OnPageUp ],
468
- 'pagedown pressed' : [ None, self.OnPageDown ],
469
- 'home pressed' : [ None, _F(self.select, index=0) ],
470
- 'end pressed' : [ None, _F(self.select, index=-1) ],
471
- 'M-a pressed' : [ None, _F(self.fit_to_canvas) ],
472
- 'C-a pressed' : [ None, _F(self.fit_to_axes) ],
473
- 'C-i pressed' : [ None, _F(self.invert_cmap) ],
474
- 'C-k pressed' : [ None, _F(self.kill_buffer) ],
475
- 'C-S-k pressed' : [ None, _F(self.kill_buffer_all) ],
476
- 'C-c pressed' : [ None, _F(self.write_buffer_to_clipboard) ],
477
- 'C-v pressed' : [ None, _F(self.read_buffer_from_clipboard) ],
419
+ 'frame_shown' : [None, ], # show
420
+ 'frame_hidden' : [None, ], # show
421
+ 'frame_loaded' : [None, ], # load
422
+ 'frame_removed' : [None, ], # del[] ! event arg is indices, not frames.
423
+ 'frame_selected' : [None, ], # = focus_set
424
+ 'frame_deselected' : [None, ], # = focus_kill
425
+ 'frame_modified' : [None, _F(self.writeln)], # set[],load,roi => update_buffer
426
+ 'frame_updated' : [None, _F(self.writeln)], # unit,name,ratio => update_extent
427
+ 'frame_cmapped' : [None, _F(self.writeln)], # cmap
428
+ 'line_draw' : [None, ],
429
+ 'line_drawn' : [None, _draw],
430
+ 'line_move' : [None, ],
431
+ 'line_moved' : [None, _draw],
432
+ 'line_removed' : [None, _draw],
433
+ 'mark_draw' : [None, ],
434
+ 'mark_drawn' : [None, _draw],
435
+ 'mark_removed' : [None, _draw],
436
+ 'region_draw' : [None, ],
437
+ 'region_drawn' : [None, _draw],
438
+ 'region_removed' : [None, _draw],
439
+ 'M-up pressed' : [None, self.OnPageUp],
440
+ 'M-down pressed' : [None, self.OnPageDown],
441
+ 'pageup pressed' : [None, self.OnPageUp],
442
+ 'pagedown pressed' : [None, self.OnPageDown],
443
+ 'home pressed' : [None, _F(self.select, index=0)],
444
+ 'end pressed' : [None, _F(self.select, index=-1)],
445
+ 'M-a pressed' : [None, _F(self.fit_to_canvas)],
446
+ 'C-a pressed' : [None, _F(self.fit_to_axes)],
447
+ 'C-i pressed' : [None, _F(self.invert_cmap)],
448
+ 'C-k pressed' : [None, _F(self.kill_buffer)],
449
+ 'C-S-k pressed' : [None, _F(self.kill_all_buffers)],
450
+ 'C-c pressed' : [None, _F(self.write_buffer_to_clipboard)],
451
+ 'C-v pressed' : [None, _F(self.read_buffer_from_clipboard)],
478
452
  },
479
453
  NORMAL : {
480
454
  'image_picked' : (NORMAL, self.OnImagePicked),
@@ -581,6 +555,7 @@ class GraphPlot(MatplotPanel):
581
555
  'delete pressed' : (NORMAL, self.OnRegionRemove),
582
556
  'space pressed' : (PAN, self.OnPanBegin),
583
557
  'ctrl pressed' : (PAN, self.OnPanBegin),
558
+ 'c pressed' : (REGION, self.OnRegionCenter),
584
559
  'z pressed' : (ZOOM, self.OnZoomBegin),
585
560
  '*Ldrag begin' : (REGION+DRAGGING, self.OnRegionDragBegin),
586
561
  'Rbutton pressed' : (REGION, self.on_menu_lock),
@@ -618,7 +593,7 @@ class GraphPlot(MatplotPanel):
618
593
  lambda v: v.Enable(self.frame is not None)),
619
594
 
620
595
  (wx.ID_CLOSE_ALL, "&Kill all buffer\t(C-S-k)", "Kill buffers", _Icon(wx.ART_DELETE),
621
- lambda v: self.kill_buffer_all(),
596
+ lambda v: self.kill_all_buffers(),
622
597
  lambda v: v.Enable(self.frame is not None)),
623
598
  ]
624
599
 
@@ -628,14 +603,13 @@ class GraphPlot(MatplotPanel):
628
603
  lambda v: self.select(s),
629
604
  lambda v: v.Check(self.frame is not None and self.frame.name == s))
630
605
 
631
- self.modeline.Bind(wx.EVT_CONTEXT_MENU, lambda v:
632
- Menu.Popup(self,
633
- (_menu(j, art.name) for j, art in enumerate(self.__Arts))))
634
-
635
- self.modeline.Show(1)
636
- self.Layout()
606
+ self.modeline.Bind(wx.EVT_CONTEXT_MENU,
607
+ lambda v: Menu.Popup(self, (_menu(j, art.name)
608
+ for j, art in enumerate(self.__Arts))))
637
609
 
610
+ self.modeline.Show()
638
611
  self.writeln()
612
+ self.Layout()
639
613
 
640
614
  def clear(self):
641
615
  MatplotPanel.clear(self)
@@ -646,14 +620,14 @@ class GraphPlot(MatplotPanel):
646
620
  ## cf. self.figure.dpi = 80 dpi (0.3175 mm/pix)
647
621
  self.__unit = 1.0
648
622
 
649
- #<matplotlib.lines.Line2D>
623
+ # <matplotlib.lines.Line2D>
650
624
  (self.marked,) = self.axes.plot([], [], "r+", ms=8, mew=1,
651
625
  picker=8)
652
626
  self.__marksel = []
653
627
  self.__markarts = []
654
628
  self.marked.set_clip_on(False)
655
629
 
656
- #<matplotlib.lines.Line2D>
630
+ # <matplotlib.lines.Line2D>
657
631
  (self.rected,) = self.axes.plot([], [], "r+--", ms=4, lw=3/4,
658
632
  picker=4, alpha=0.8)
659
633
  self.__rectsel = []
@@ -688,13 +662,13 @@ class GraphPlot(MatplotPanel):
688
662
  if isinstance(buf, str):
689
663
  buf = Image.open(buf)
690
664
 
691
- pathname = kwargs.get('pathname')
665
+ path = kwargs.get('pathname')
692
666
  paths = [art.pathname for art in self.__Arts]
693
667
  names = [art.name for art in self.__Arts]
694
668
  j = -1
695
- if pathname:
696
- if pathname in paths:
697
- j = paths.index(pathname) # identical path
669
+ if path:
670
+ if path in paths:
671
+ j = paths.index(path) # existing path
698
672
  elif name in names:
699
673
  j = names.index(name) # existing frame
700
674
  if j != -1:
@@ -880,12 +854,12 @@ class GraphPlot(MatplotPanel):
880
854
 
881
855
  @property
882
856
  def unit(self):
883
- """Logical length per pixel arb.unit [u/pix]."""
857
+ """Logical length per pixel in arbitrary units [u/pix]."""
884
858
  return self.__unit
885
859
 
886
860
  @unit.setter
887
861
  def unit(self, v):
888
- if v == self.__unit: # no effect unless unit changes
862
+ if v == self.__unit: # no effect
889
863
  return
890
864
  if v is None or np.isnan(v) or np.isinf(v):
891
865
  raise ValueError("The unit value must not be nan or inf")
@@ -902,7 +876,7 @@ class GraphPlot(MatplotPanel):
902
876
  if self.frame:
903
877
  del self[self.__index]
904
878
 
905
- def kill_buffer_all(self):
879
+ def kill_all_buffers(self):
906
880
  del self[:]
907
881
 
908
882
  def fit_to_axes(self):
@@ -962,21 +936,22 @@ class GraphPlot(MatplotPanel):
962
936
 
963
937
  def trace_point(self, x, y, type=NORMAL):
964
938
  """Puts (override) a message of points x and y."""
939
+ if not hasattr(x, '__iter__'): # called from OnMotion
940
+ return self.trace_point([x], [y], type)
941
+
965
942
  frame = self.frame
966
943
  if frame:
967
- if not hasattr(x, '__iter__'): # called from OnMotion
968
- nx, ny = frame.xytopixel(x, y)
969
- z = frame.xytoc(x, y)
970
- self.message(f"[{nx:-4d},{ny:-4d}] ({x:-8.3f},{y:-8.3f}) value: {z}")
971
- return
972
-
973
944
  if len(x) == 0: # no selection
974
945
  return
975
946
 
976
- if len(x) == 1: # 1-selector trace point (called from Marker:setter)
977
- return self.trace_point(x[0], y[0], type)
947
+ if len(x) == 1: # 1-selector trace point (called from markers.setter)
948
+ x, y = x[0], y[0]
949
+ z = frame.xytoc(x, y)
950
+ nx, ny = frame.xytopixel(x, y)
951
+ self.message(f"[{nx:-4d},{ny:-4d}] ({x:-8.3f},{y:-8.3f}) value: {z}")
952
+ return
978
953
 
979
- if len(x) == 2: # 2-selector trace line (called from selector:setter)
954
+ if len(x) == 2: # 2-selector trace line (called from selector.setter)
980
955
  nx, ny = frame.xytopixel(x, y)
981
956
  dx = x[1] - x[0]
982
957
  dy = y[1] - y[0]
@@ -985,11 +960,11 @@ class GraphPlot(MatplotPanel):
985
960
  li = np.hypot(nx[1]-nx[0], ny[1]-ny[0])
986
961
  self.message(f"[Line] Length: {li:.1f} pixel ({lu:g}u) Angle: {a:.1f} deg")
987
962
 
988
- elif type == REGION: # N-selector trace polygon (called from region:setter)
963
+ elif type == REGION: # N-selector trace polygon (called from region.setter)
989
964
  nx, ny = frame.xytopixel(x, y)
990
- xo, yo = min(nx), min(ny) # top-left
991
- xr, yr = max(nx), max(ny) # bottom-right
992
- self.message(f"[Region] crop={xr-xo}:{yr-yo}:{xo}:{yo}") # (W:H:left:top)
965
+ xo, xp = min(nx), max(nx)
966
+ yo, yp = min(ny), max(ny)
967
+ self.message(f"[Region] crop={xp-xo}:{yp-yo}:{xo}:{yo}") # (W:H:left:top)
993
968
 
994
969
  def writeln(self):
995
970
  """Puts (override) attributes of current frame to the modeline."""
@@ -1086,7 +1061,7 @@ class GraphPlot(MatplotPanel):
1086
1061
  ## matplotlib interface.
1087
1062
  ## --------------------------------
1088
1063
 
1089
- def on_pick(self, evt): #<matplotlib.backend_bases.PickEvent>
1064
+ def on_pick(self, evt): # <matplotlib.backend_bases.PickEvent>
1090
1065
  """Pickup image and other arts.
1091
1066
  Called (maybe) after mouse buttons are pressed.
1092
1067
  """
@@ -1135,19 +1110,18 @@ class GraphPlot(MatplotPanel):
1135
1110
  def on_picker_unlock(self, evt):
1136
1111
  self.__isPicked = False
1137
1112
 
1138
- def OnImagePicked(self, evt): #<matplotlib.backend_bases.PickEvent>
1113
+ def OnImagePicked(self, evt): # <matplotlib.backend_bases.PickEvent>
1139
1114
  x = evt.mouseevent.xdata
1140
1115
  y = evt.mouseevent.ydata
1141
1116
  nx, ny = self.frame.xytopixel(x, y)
1142
- x, y = self.frame.xyfrompixel(nx, ny)
1143
1117
  evt.ind = (ny, nx)
1144
- self.selector = (x, y)
1118
+ self.selector = self.frame.xyfrompixel(nx, ny)
1145
1119
 
1146
1120
  def _inaxes(self, evt):
1147
1121
  try:
1148
- return evt.inaxes is not self.axes #<matplotlib.backend_bases.MouseEvent>
1122
+ return evt.inaxes is not self.axes # <matplotlib.backend_bases.MouseEvent>
1149
1123
  except AttributeError:
1150
- return None #<wx._core.KeyEvent>
1124
+ return None # <wx._core.KeyEvent>
1151
1125
 
1152
1126
  ## --------------------------------
1153
1127
  ## Pan/Zoom actions (override).
@@ -1314,7 +1288,7 @@ class GraphPlot(MatplotPanel):
1314
1288
  dots = np.hypot(x-xs[k], y-ys[k]) * self.ddpu[0]
1315
1289
  self.__linesel = k if dots < 8 else None
1316
1290
 
1317
- def OnLineDeselected(self, evt): #<matplotlib.backend_bases.PickEvent>
1291
+ def OnLineDeselected(self, evt): # <matplotlib.backend_bases.PickEvent>
1318
1292
  self.__linesel = None
1319
1293
 
1320
1294
  def OnLineDragBegin(self, evt):
@@ -1365,10 +1339,10 @@ class GraphPlot(MatplotPanel):
1365
1339
  if self.selector.size and self.frame:
1366
1340
  ux, uy = self.frame.xy_unit
1367
1341
  du = {
1368
- 'up' : ( 0., uy),
1369
- 'down' : ( 0.,-uy),
1370
- 'left' : (-ux, 0.),
1371
- 'right' : ( ux, 0.),
1342
+ 'up' : (0, +uy),
1343
+ 'down' : (0, -uy),
1344
+ 'left' : (-ux, 0),
1345
+ 'right' : (+ux, 0),
1372
1346
  }
1373
1347
  self.selector += np.resize(du[evt.key], (2,1))
1374
1348
  self.handler('line_move', self.frame)
@@ -1399,7 +1373,7 @@ class GraphPlot(MatplotPanel):
1399
1373
  return
1400
1374
  self.marked.set_data(x, y)
1401
1375
  self.__marksel = []
1402
- self.update_art_of_mark()
1376
+ self.update_mark_art()
1403
1377
  self.handler('mark_drawn', self.frame)
1404
1378
 
1405
1379
  @markers.deleter
@@ -1407,7 +1381,7 @@ class GraphPlot(MatplotPanel):
1407
1381
  if self.markers.size:
1408
1382
  self.marked.set_data([], [])
1409
1383
  self.__marksel = []
1410
- self.update_art_of_mark()
1384
+ self.update_mark_art()
1411
1385
  self.handler('mark_removed', self.frame)
1412
1386
 
1413
1387
  def get_current_mark(self):
@@ -1421,7 +1395,7 @@ class GraphPlot(MatplotPanel):
1421
1395
  if j:
1422
1396
  xm[j], ym[j] = x, y
1423
1397
  self.marked.set_data(xm, ym)
1424
- self.update_art_of_mark(j, xm[j], ym[j])
1398
+ self.update_mark_art(j, xm[j], ym[j])
1425
1399
  else:
1426
1400
  n = len(xm)
1427
1401
  k = len(x) if hasattr(x, '__iter__') else 1
@@ -1429,7 +1403,7 @@ class GraphPlot(MatplotPanel):
1429
1403
  xm, ym = np.append(xm, x), np.append(ym, y)
1430
1404
  self.marked.set_data(xm, ym)
1431
1405
  self.marked.set_visible(1)
1432
- self.update_art_of_mark()
1406
+ self.update_mark_art()
1433
1407
  self.selector = (x, y)
1434
1408
 
1435
1409
  def del_current_mark(self):
@@ -1441,9 +1415,9 @@ class GraphPlot(MatplotPanel):
1441
1415
  self.marked.set_data(xm, ym)
1442
1416
  n = len(xm)
1443
1417
  self.__marksel = [j[-1] % n] if n > 0 else []
1444
- self.update_art_of_mark()
1418
+ self.update_mark_art()
1445
1419
 
1446
- def update_art_of_mark(self, *args):
1420
+ def update_mark_art(self, *args):
1447
1421
  if args:
1448
1422
  for k, x, y in zip(*args):
1449
1423
  art = self.__markarts[k] # art の再描画処理をして終了
@@ -1458,11 +1432,11 @@ class GraphPlot(MatplotPanel):
1458
1432
  xm, ym = self.marked.get_data(orig=0)
1459
1433
  for k, (x, y) in enumerate(zip(xm[:N], ym[:N])):
1460
1434
  self.__markarts.append(
1461
- self.axes.annotate(k, #<matplotlib.text.Annotation>
1435
+ self.axes.annotate(k, # <matplotlib.text.Annotation>
1462
1436
  xy=(x,y), xycoords='data',
1463
1437
  xytext=(6,6), textcoords='offset points',
1464
1438
  bbox=dict(boxstyle="round", fc=(1,1,1,), ec=(1,0,0,)),
1465
- color='red', size=7, #fontsize=8,
1439
+ color='red', size=7, # fontsize=8,
1466
1440
  )
1467
1441
  )
1468
1442
  self.trace_point(*self.get_current_mark(), type=MARK)
@@ -1473,28 +1447,28 @@ class GraphPlot(MatplotPanel):
1473
1447
  if not self.__marksel and len(xs) > 0:
1474
1448
  self.set_current_mark(xs, ys)
1475
1449
  self.handler('mark_drawn', self.frame)
1476
- self.update_art_of_mark()
1450
+ self.update_mark_art()
1477
1451
 
1478
1452
  def OnMarkRemove(self, evt):
1479
1453
  if self.__marksel:
1480
1454
  self.del_current_mark()
1481
1455
  self.handler('mark_removed', self.frame)
1482
1456
 
1483
- def OnMarkSelected(self, evt): #<matplotlib.backend_bases.PickEvent>
1457
+ def OnMarkSelected(self, evt): # <matplotlib.backend_bases.PickEvent>
1484
1458
  k = evt.ind[0]
1485
1459
  if evt.mouseevent.key == 'shift': # 多重マーカー選択
1486
1460
  if k not in self.__marksel:
1487
1461
  self.__marksel += [k]
1488
1462
  else:
1489
1463
  self.__marksel = [k]
1490
- self.update_art_of_mark()
1464
+ self.update_mark_art()
1491
1465
  self.selector = self.get_current_mark()
1492
1466
  if self.selector.shape[1] > 1:
1493
1467
  self.handler('line_drawn', self.frame) # 多重マーカー選択時
1494
1468
 
1495
- def OnMarkDeselected(self, evt): #<matplotlib.backend_bases.PickEvent>
1469
+ def OnMarkDeselected(self, evt): # <matplotlib.backend_bases.PickEvent>
1496
1470
  self.__marksel = []
1497
- self.update_art_of_mark()
1471
+ self.update_mark_art()
1498
1472
 
1499
1473
  def OnMarkDragBegin(self, evt):
1500
1474
  if not self.frame or self._inaxes(evt):
@@ -1519,10 +1493,10 @@ class GraphPlot(MatplotPanel):
1519
1493
  if j and self.frame:
1520
1494
  ux, uy = self.frame.xy_unit
1521
1495
  du = {
1522
- 'up' : ( 0., uy),
1523
- 'down' : ( 0.,-uy),
1524
- 'left' : (-ux, 0.),
1525
- 'right' : ( ux, 0.),
1496
+ 'up' : (0, uy),
1497
+ 'down' : (0, -uy),
1498
+ 'left' : (-ux, 0),
1499
+ 'right' : ( ux, 0),
1526
1500
  }
1527
1501
  p = self.get_current_mark() + np.resize(du[evt.key], (2,1))
1528
1502
  self.set_current_mark(*p)
@@ -1562,13 +1536,13 @@ class GraphPlot(MatplotPanel):
1562
1536
 
1563
1537
  @property
1564
1538
  def region(self):
1565
- """Cropped rectangle points data array [l,r],[b,t]."""
1539
+ """Cropped rectangle points data array [[l,r], [b,t]]."""
1566
1540
  x, y = self.rected.get_data(orig=0)
1567
1541
  if len(x) and len(y):
1568
- xo, x = min(x), max(x) # x[[0, 2]]
1569
- yo, y = min(y), max(y) # y[[0, 2]]
1570
- return np.array(((xo, x), (yo, y)))
1571
- return np.resize(0., (2,0))
1542
+ l, r = min(x), max(x)
1543
+ b, t = min(y), max(y)
1544
+ return np.array(((l,r), (b,t)))
1545
+ return np.resize(0., (2, 0))
1572
1546
 
1573
1547
  @region.setter
1574
1548
  def region(self, v):
@@ -1597,8 +1571,6 @@ class GraphPlot(MatplotPanel):
1597
1571
  l,r,b,t = self.frame.get_extent()
1598
1572
  xa, xb = min(x), max(x)
1599
1573
  ya, yb = min(y), max(y)
1600
- # if (xa < l or xb > r) or (ya < b or yb > t):
1601
- # return
1602
1574
  ## Modify range so that it does not exceed the extent.
1603
1575
  w, h = xb-xa, yb-ya
1604
1576
  if xa < l: xa, xb = l, l+w
@@ -1609,15 +1581,15 @@ class GraphPlot(MatplotPanel):
1609
1581
  y = [ya, ya, yb, yb, ya]
1610
1582
  self.rected.set_data(x, y)
1611
1583
  self.rected.set_visible(1)
1612
- self.update_art_of_region()
1584
+ self.update_rect_art()
1613
1585
 
1614
1586
  def del_current_rect(self):
1615
1587
  self.__rectsel = []
1616
1588
  self.rected.set_data([], [])
1617
1589
  self.rected.set_visible(0)
1618
- self.update_art_of_region()
1590
+ self.update_rect_art()
1619
1591
 
1620
- def update_art_of_region(self, *args):
1592
+ def update_rect_art(self, *args):
1621
1593
  if args:
1622
1594
  art = self.__rectarts # art の再描画処理をして終了
1623
1595
  art.xy = args
@@ -1638,6 +1610,12 @@ class GraphPlot(MatplotPanel):
1638
1610
  self.trace_point(x, y, type=REGION)
1639
1611
  self.draw(self.rected)
1640
1612
 
1613
+ def OnRegionCenter(self, evt):
1614
+ if self.region.size and self.frame:
1615
+ (l,r), (b,t) = self.region
1616
+ c = np.array(((l+r)/2, (b+t)/2))
1617
+ self.region += self.frame.center - c[:,None]
1618
+
1641
1619
  def OnRegionAppend(self, evt):
1642
1620
  xs, ys = self.selector
1643
1621
  if len(xs) > 0 and self.frame:
@@ -1645,7 +1623,7 @@ class GraphPlot(MatplotPanel):
1645
1623
  xs = (xs.min()-ux/2, xs.max()+ux/2)
1646
1624
  ys = (ys.max()+uy/2, ys.min()-uy/2)
1647
1625
  self.set_current_rect(xs, ys)
1648
- self.update_art_of_region()
1626
+ self.update_rect_art()
1649
1627
  self.handler('region_drawn', self.frame)
1650
1628
 
1651
1629
  def OnRegionRemove(self, evt):
@@ -1654,18 +1632,18 @@ class GraphPlot(MatplotPanel):
1654
1632
  self.handler('region_removed', self.frame)
1655
1633
  self.set_wxcursor(wx.CURSOR_ARROW)
1656
1634
 
1657
- def OnRegionSelected(self, evt): #<matplotlib.backend_bases.PickEvent>
1635
+ def OnRegionSelected(self, evt): # <matplotlib.backend_bases.PickEvent>
1658
1636
  k = evt.ind[0]
1659
1637
  x = evt.mouseevent.xdata
1660
1638
  y = evt.mouseevent.ydata
1661
1639
  xs, ys = evt.artist.get_data(orig=0)
1662
1640
  dots = np.hypot(x-xs[k], y-ys[k]) * self.ddpu[0]
1663
1641
  self.__rectsel = [k] if dots < 8 else [0,1,2,3,4] # リージョンの全選択
1664
- self.update_art_of_region()
1642
+ self.update_rect_art()
1665
1643
 
1666
- def OnRegionDeselected(self, evt): #<matplotlib.backend_bases.PickEvent>
1644
+ def OnRegionDeselected(self, evt): # <matplotlib.backend_bases.PickEvent>
1667
1645
  self.__rectsel = []
1668
- self.update_art_of_region()
1646
+ self.update_rect_art()
1669
1647
  self.set_wxcursor(wx.CURSOR_ARROW)
1670
1648
 
1671
1649
  def OnRegionDragBegin(self, evt):
@@ -1723,10 +1701,10 @@ class GraphPlot(MatplotPanel):
1723
1701
  if j and self.frame:
1724
1702
  ux, uy = self.frame.xy_unit
1725
1703
  du = {
1726
- 'up' : ( 0., uy),
1727
- 'down' : ( 0.,-uy),
1728
- 'left' : (-ux, 0.),
1729
- 'right' : ( ux, 0.),
1704
+ 'up' : (0, uy),
1705
+ 'down' : (0, -uy),
1706
+ 'left' : (-ux, 0),
1707
+ 'right' : ( ux, 0),
1730
1708
  }
1731
1709
  dp = du[evt.key]
1732
1710
  p = self.get_current_rect().T