mwxlib 0.99.0__py3-none-any.whl → 1.7.13__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/matplot2lg.py CHANGED
@@ -8,7 +8,7 @@ import wx
8
8
  from matplotlib import patches
9
9
  import numpy as np
10
10
  from scipy import signal
11
- ## from scipy import ndimage as ndi
11
+ from scipy import ndimage
12
12
 
13
13
  from . import framework as mwx
14
14
  from .utilus import funcall as _F
@@ -18,7 +18,7 @@ from .matplot2 import NORMAL, MARK, LINE, REGION
18
18
 
19
19
 
20
20
  class LinePlot(MatplotPanel):
21
- """Line plot 1D base panel
21
+ """Line plot 1D base panel.
22
22
 
23
23
  region : selected range (l,r) on the plot
24
24
  """
@@ -45,7 +45,7 @@ class LinePlot(MatplotPanel):
45
45
  },
46
46
  })
47
47
  self.modeline.Show(0)
48
-
48
+
49
49
  def clear(self):
50
50
  MatplotPanel.clear(self)
51
51
 
@@ -64,14 +64,14 @@ class LinePlot(MatplotPanel):
64
64
  #<matplotlib.patches.Rectangle>
65
65
  self.__vspan = self.axes.axvspan(0, 0,
66
66
  color='none', ls='dashed', lw=1, ec='black', visible=0, zorder=2)
67
-
68
- ## the limit for dragging region
67
+
68
+ ## The limit for dragging region.
69
69
  boundary = None
70
-
70
+
71
71
  @property
72
72
  def region(self):
73
73
  return self.__region
74
-
74
+
75
75
  @region.setter
76
76
  def region(self, v):
77
77
  if v is not None:
@@ -95,11 +95,11 @@ class LinePlot(MatplotPanel):
95
95
  self.__vspan.set_visible(0)
96
96
  self.handler('region_unset', self.frame)
97
97
  self.__region = v
98
-
98
+
99
99
  @region.deleter
100
100
  def region(self):
101
101
  self.region = None
102
-
102
+
103
103
  def annotate(self):
104
104
  for art in self.__annotations:
105
105
  art.remove()
@@ -116,9 +116,9 @@ class LinePlot(MatplotPanel):
116
116
  x = (b + a) / 2
117
117
  y = self.ylim[0] + 20/self.ddpu[1]
118
118
  if (b - a) > 60/self.ddpu[0]:
119
- p = _A(b-a, (x,y), (-20,8), arrowstyle='-') # wide space
119
+ p = _A(b-a, (x,y), (-20,8), arrowstyle='-') # wide space
120
120
  else:
121
- p = _A(b-a, (x,y), (16,16), arrowstyle='-', # narrow space
121
+ p = _A(b-a, (x,y), (16,16), arrowstyle='-', # narrow space
122
122
  connectionstyle="angle,angleA=0,angleB=90,rad=8")
123
123
  self.__annotations = [
124
124
  _A(a, (a,y), (-54,-3), arrowstyle='->'),
@@ -126,53 +126,53 @@ class LinePlot(MatplotPanel):
126
126
  _A(None, (a,y), (b,y), textcoords='data', arrowstyle='<->'),
127
127
  p,
128
128
  ]
129
-
129
+
130
130
  ## --------------------------------
131
- ## Motion/Drag actions (override)
131
+ ## Region/Drag actions (override).
132
132
  ## --------------------------------
133
-
133
+
134
134
  def region_test(self, evt):
135
135
  if self.region is not None:
136
136
  x = evt.xdata
137
137
  a, b = self.region
138
138
  d = 4 / self.ddpu[0]
139
- if a+d < x < b-d: return 1 # insdie
140
- elif a-d < x < a+d: return 2 # left-edge
141
- elif b-d < x < b+d: return 3 # right-edge
142
- else: return 0 # outside
143
-
139
+ if a+d < x < b-d: return 1 # insdie
140
+ elif a-d < x < a+d: return 2 # left-edge
141
+ elif b-d < x < b+d: return 3 # right-edge
142
+ else: return 0 # outside
143
+
144
144
  def OnDraw(self, evt):
145
145
  """Called before canvas.draw."""
146
146
  self.annotate()
147
-
147
+
148
148
  def OnMotion(self, evt):
149
149
  MatplotPanel.OnMotion(self, evt)
150
150
 
151
151
  v = self.region_test(evt)
152
152
  if v == 1:
153
- self.set_wxcursor(wx.CURSOR_HAND) # insdie
153
+ self.set_wxcursor(wx.CURSOR_HAND) # insdie
154
154
  elif v in (2,3):
155
- self.set_wxcursor(wx.CURSOR_SIZEWE) # on-edge
155
+ self.set_wxcursor(wx.CURSOR_SIZEWE) # on-edge
156
156
  else:
157
- self.set_wxcursor(wx.CURSOR_ARROW) # outside or None
158
-
157
+ self.set_wxcursor(wx.CURSOR_ARROW) # outside or None
158
+
159
159
  def OnDragLock(self, evt):
160
160
  self.__lastpoint = evt.xdata
161
161
  self.__selection = self.region_test(evt)
162
-
162
+
163
163
  def OnDragBegin(self, evt):
164
164
  v = self.__selection
165
165
  if v == 1:
166
- self.set_wxcursor(wx.CURSOR_HAND) # inside
166
+ self.set_wxcursor(wx.CURSOR_HAND) # inside
167
167
  elif v == 2:
168
- self.set_wxcursor(wx.CURSOR_SIZEWE) # left-edge
169
- self.__lastpoint = self.region[1] # set origin right
168
+ self.set_wxcursor(wx.CURSOR_SIZEWE) # left-edge
169
+ self.__lastpoint = self.region[1] # set origin right
170
170
  elif v == 3:
171
- self.set_wxcursor(wx.CURSOR_SIZEWE) # right-edge
172
- self.__lastpoint = self.region[0] # set origin left
171
+ self.set_wxcursor(wx.CURSOR_SIZEWE) # right-edge
172
+ self.__lastpoint = self.region[0] # set origin left
173
173
  else:
174
- self.set_wxcursor(wx.CURSOR_SIZEWE) # outside
175
-
174
+ self.set_wxcursor(wx.CURSOR_SIZEWE) # outside
175
+
176
176
  def OnDragMove(self, evt):
177
177
  x = evt.xdata
178
178
  if self.__selection != 1:
@@ -196,12 +196,12 @@ class LinePlot(MatplotPanel):
196
196
  self.region = (a+d, b+d)
197
197
  self.__lastpoint = x
198
198
  else:
199
- self.message("- No region.") #<FSM logic-error>
199
+ self.message("- No region.") #<FSM logic-error>
200
200
  self.draw()
201
-
201
+
202
202
  def OnDragEnd(self, evt):
203
203
  self.set_wxcursor(wx.CURSOR_ARROW)
204
-
204
+
205
205
  def OnEscapeSelection(self, evt):
206
206
  MatplotPanel.OnEscapeSelection(self, evt)
207
207
 
@@ -211,23 +211,19 @@ class LinePlot(MatplotPanel):
211
211
 
212
212
 
213
213
  class Histogram(LinePlot):
214
- """LinePlot panel for histogram (Multi-graph : Single-frame)
215
-
216
- frame.image <uint8> (buffer ではない) を参照して,ヒストグラムをプロットする
217
- 常に整数ビット画像となるので,高速なビンづめ法で計算する
214
+ """LinePlot panel for histogram (Multi-graph : Single-frame).
218
215
 
219
- Attributes:
220
- __graphs : list of attached graph <matplot2g.GraphPlot>
221
- __frame : reference to the current frame
216
+ frame.image <uint8> (buffer ではない) を参照して,ヒストグラムをプロットする.
217
+ 常に整数ビット画像となるので,高速なビンづめ法で計算する.
222
218
  """
223
219
  def __init__(self, *args, **kwargs):
224
220
  LinePlot.__init__(self, *args, **kwargs)
225
221
 
226
222
  self.handler.update({ # DNA<Histogram>
227
223
  NORMAL : {
228
- 'ctrl pressed' : (NORMAL, ), # Pan prohibits
229
- 'space pressed' : (NORMAL, ), # 〃
230
- 'z pressed' : (NORMAL, ), # Zoom prohibits
224
+ 'ctrl pressed' : (NORMAL, ), # Pan prohibits
225
+ 'space pressed' : (NORMAL, ), # 〃
226
+ 'z pressed' : (NORMAL, ), # Zoom prohibits
231
227
  },
232
228
  })
233
229
  self.context = { # DNA<GraphPlot>
@@ -240,17 +236,17 @@ class Histogram(LinePlot):
240
236
  self.modeline.Show(0)
241
237
 
242
238
  self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
243
-
239
+
244
240
  def OnDestroy(self, evt):
245
- for graph in self.__graphs:
246
- self.detach(graph)
241
+ for view in self.__views:
242
+ self.detach(view)
247
243
  evt.Skip()
248
-
244
+
249
245
  def clear(self):
250
246
  LinePlot.clear(self)
251
247
 
252
- self.__graphs = []
253
- self.__frame = None
248
+ self.__views = [] # A list of attached view <matplot2g.GraphPlot>.
249
+ self.__frame = None # Reference to the current frame.
254
250
 
255
251
  #<matplotlib.lines.Line2D>
256
252
  self.__plot, = self.axes.plot([], [], lw=1, color='c', alpha=1)
@@ -258,56 +254,54 @@ class Histogram(LinePlot):
258
254
  #<matplotlib.patches.Polygon>
259
255
  self.__fil = patches.Polygon([(0,0)], color='c', alpha=1)
260
256
  self.axes.add_patch(self.__fil)
261
-
262
- def attach(self, *graphs):
263
- for graph in graphs:
264
- if graph not in self.__graphs:
265
- self.__graphs.append(graph)
266
- graph.handler.append(self.context)
267
-
268
- def detach(self, *graphs):
269
- for graph in graphs:
270
- if graph in self.__graphs:
271
- self.__graphs.remove(graph)
272
- graph.handler.remove(self.context)
273
-
257
+
258
+ def attach(self, *views):
259
+ for view in views:
260
+ if view not in self.__views:
261
+ self.__views.append(view)
262
+ view.handler.append(self.context)
263
+
264
+ def detach(self, *views):
265
+ for view in views:
266
+ if view in self.__views:
267
+ self.__views.remove(view)
268
+ view.handler.remove(self.context)
269
+
274
270
  @property
275
271
  def boundary(self):
276
- return [0,255]
277
-
272
+ return [0, 255]
273
+
278
274
  def calc(self, frame):
279
275
  BINS = 256
280
276
  img = frame.image
281
277
  if img.dtype == np.uint8:
282
- ## 整数ビット画像は,高速なビンづめ法で計算する
278
+ ## 整数ビット画像は,高速なビンづめ法で計算する.
283
279
  hist = np.bincount(img.ravel(), minlength=BINS)
284
280
  bins = np.arange(BINS)
285
281
  else:
286
- ## hist は [min:max] 段階 (256 BINS) で保持されている
287
- ## bins は 端数含め [0:BINS] (257 コ) あるので1個減す
282
+ ## hist は [min:max] 段階 (256 BINS) で保持されている.
283
+ ## bins は 端数含め [0:BINS] (257 コ) あるので1個減す.
288
284
  hist, bins = np.histogram(img, BINS)
289
285
  bins = np.linspace(img.min(), img.max(), BINS)
290
286
  return bins, hist
291
-
287
+
292
288
  def hplot(self, frame):
293
- self.__frame = frame # update reference of the frame
289
+ self.__frame = frame # Update reference of the frame.
294
290
  if frame:
295
- x, y = frame.__data = self.calc(frame) # histogram_data buffer
291
+ x, y = frame.__data = self.calc(frame) # histogram_data buffer
296
292
  self.__plot.set_data(x, y)
297
293
  self.xlim = x.min(), x.max()
298
294
  self.ylim = 0, y.max()
299
295
  self.region = None
300
- self.toolbar.update()
301
- self.toolbar.push_current()
302
296
  self.draw()
303
-
297
+
304
298
  def hreplot(self, frame):
305
- self.__frame = frame # update reference of the frame
299
+ self.__frame = frame # Update reference of the frame.
306
300
  if frame:
307
301
  try:
308
- x, y = frame.__data # reuse cached data
302
+ x, y = frame.__data # Reuse cached data.
309
303
  except Exception:
310
- x, y = frame.__data = self.calc(frame) # new histogram_data buffer
304
+ x, y = frame.__data = self.calc(frame) # new histogram_data buffer
311
305
 
312
306
  self.__plot.set_data(x, y)
313
307
  self.xlim = x.min(), x.max()
@@ -322,10 +316,8 @@ class Histogram(LinePlot):
322
316
  self.__plot.set_data([], [])
323
317
  self.region = None
324
318
 
325
- self.toolbar.update()
326
- self.toolbar.push_current()
327
319
  self.draw()
328
-
320
+
329
321
  def writeln(self):
330
322
  if not self.modeline.IsShown():
331
323
  return
@@ -343,11 +335,11 @@ class Histogram(LinePlot):
343
335
  ))
344
336
  else:
345
337
  self.modeline.SetLabel("")
346
-
338
+
347
339
  ## --------------------------------
348
- ## Motion/Drag actions (override)
340
+ ## Region/Drag actions (override).
349
341
  ## --------------------------------
350
-
342
+
351
343
  def annotate(self):
352
344
  if self.__frame:
353
345
  x, y = self.__frame.__data
@@ -356,24 +348,24 @@ class Histogram(LinePlot):
356
348
  i, j = x.searchsorted(self.region)
357
349
  else:
358
350
  i, j = (0, -1)
359
- self.__fil.set_xy(list(chain([(x[i],0)], zip(x[i:j],y[i:j]), [(x[j-1],0)])))
351
+ self.__fil.set_xy(list(chain([(x[i], 0)], zip(x[i:j], y[i:j]), [(x[j-1], 0)])))
360
352
  else:
361
353
  self.__fil.set_xy([(0,0)])
362
354
  else:
363
355
  self.__fil.set_xy([(0,0)])
364
356
  self.writeln()
365
-
357
+
366
358
  def OnDragEnd(self, evt):
367
359
  LinePlot.OnDragEnd(self, evt)
368
360
 
369
361
  if self.__frame:
370
- self.xbound = self.region # 拡大表示したのち region 消去
362
+ self.xbound = self.region # 拡大表示したのち region 消去
371
363
  self.region = None
372
364
  self.toolbar.push_current()
373
365
  self.draw()
374
366
  self.__frame.clim = self.xlim
375
367
  self.__frame.parent.draw()
376
-
368
+
377
369
  def OnEscapeSelection(self, evt):
378
370
  LinePlot.OnEscapeSelection(self, evt)
379
371
 
@@ -384,13 +376,7 @@ class Histogram(LinePlot):
384
376
 
385
377
 
386
378
  class LineProfile(LinePlot):
387
- """LinePlot panel for line profile (Multi-graph : Single-frame)
388
-
389
- Attributes:
390
- __graphs : list of attached graph <matplot2g.GraphPlot>
391
- __frame : reference to the current frame
392
- __logicp : line axis in logical unit
393
- __linewidth : line width to integrate [pixel]
379
+ """LinePlot panel for line profile (Multi-graph : Single-frame).
394
380
  """
395
381
  def __init__(self, *args, **kwargs):
396
382
  LinePlot.__init__(self, *args, **kwargs)
@@ -399,8 +385,8 @@ class LineProfile(LinePlot):
399
385
  None : {
400
386
  'left pressed' : [ None, self.OnRegionShift ],
401
387
  'right pressed' : [ None, self.OnRegionShift ],
402
- '[+-] pressed' : [ None, self.OnLineWidth ], # [+-] using numpad
403
- 'S-[;-] pressed' : [ None, self.OnLineWidth ], # [+-] using JP-keyboard
388
+ '[+-] pressed' : [ None, self.OnLineWidth ], # [+-] using numpad
389
+ 'S-[;-] pressed' : [ None, self.OnLineWidth ], # [+-] using JP-keyboard
404
390
  },
405
391
  NORMAL : {
406
392
  'S-Lbutton pressed' : (LINE, self.OnDragLock, self.OnRegionLock),
@@ -441,7 +427,7 @@ class LineProfile(LinePlot):
441
427
  'line_moved' : [ None, _F(self.linplot, fit=0) ],
442
428
  'frame_shown' : [ None, _F(self.linplot, fit=0) ],
443
429
  'frame_modified' : [ None, _F(self.linplot, fit=0) ],
444
- 'frame_selected' : [ None, _F(self.linplot, fit=0, force=0) ],
430
+ 'frame_selected' : [ None, _F(self.linplot, fit=1, force=0) ],
445
431
  }
446
432
  }
447
433
  self.modeline.Show(1)
@@ -460,17 +446,17 @@ class LineProfile(LinePlot):
460
446
  ]
461
447
 
462
448
  self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
463
-
449
+
464
450
  def OnDestroy(self, evt):
465
- for graph in self.__graphs:
466
- self.detach(graph)
451
+ for view in self.__views:
452
+ self.detach(view)
467
453
  evt.Skip()
468
-
454
+
469
455
  def clear(self):
470
456
  LinePlot.clear(self)
471
457
 
472
- self.__graphs = []
473
- self.__frame = None
458
+ self.__views = [] # A list of attached view <matplot2g.GraphPlot>.
459
+ self.__frame = None # Reference to the current frame.
474
460
 
475
461
  #<matplotlib.lines.Line2D>
476
462
  self.__plot, = self.axes.plot([], [], lw=0.1, color='c', alpha=1,
@@ -484,27 +470,27 @@ class LineProfile(LinePlot):
484
470
  self.__hline = self.axes.axhline(0, color='gray', ls='dashed', lw=1,
485
471
  visible=0, zorder=2)
486
472
 
487
- self.__linewidth = 1
488
- self.__logicp = True
473
+ self.__linewidth = 1 # Line width to integrate [pixel].
474
+ self.__logicp = True # Line axis in logical unit.
489
475
 
490
476
  self.selected.set_linestyle('')
491
-
492
- def attach(self, *graphs):
493
- for graph in graphs:
494
- if graph not in self.__graphs:
495
- self.__graphs.append(graph)
496
- graph.handler.append(self.context)
497
-
498
- def detach(self, *graphs):
499
- for graph in graphs:
500
- if graph in self.__graphs:
501
- self.__graphs.remove(graph)
502
- graph.handler.remove(self.context)
503
-
477
+
478
+ def attach(self, *views):
479
+ for view in views:
480
+ if view not in self.__views:
481
+ self.__views.append(view)
482
+ view.handler.append(self.context)
483
+
484
+ def detach(self, *views):
485
+ for view in views:
486
+ if view in self.__views:
487
+ self.__views.remove(view)
488
+ view.handler.remove(self.context)
489
+
504
490
  def set_logic(self, p):
505
491
  prep = self.__logicp
506
492
  self.__logicp = p = bool(p)
507
- if self.__frame and prep != p: # replot if toggled
493
+ if self.__frame and prep != p: # Replot if toggled.
508
494
  u = self.__frame.unit
509
495
  ru = u if p else 1/u
510
496
  self.xlim *= ru
@@ -512,60 +498,60 @@ class LineProfile(LinePlot):
512
498
  self.__plot.set_xdata(x * ru)
513
499
  if self.region is not None:
514
500
  self.region *= ru
515
- sel = self.Selector
516
- self.Selector = (sel[0] * ru, sel[1])
501
+ sel = self.selector
502
+ self.selector = (sel[0] * ru, sel[1])
517
503
  self.draw()
518
-
504
+
519
505
  def set_linewidth(self, w):
520
506
  if 0 < w < 256:
521
507
  self.__linewidth = w
522
508
  if self.__frame:
523
509
  self.linplot(self.__frame, fit=0)
524
510
  self.writeln()
525
-
511
+
526
512
  @property
527
513
  def boundary(self):
528
514
  x = self.__plot.get_xdata(orig=0)
529
515
  if x.size:
530
516
  return x[[0,-1]]
531
-
517
+
532
518
  @property
533
519
  def plotdata(self):
534
520
  """Plotted (xdata, ydata) in single plot."""
535
521
  return self.__plot.get_data(orig=0)
536
-
522
+
537
523
  def calc_average(self):
538
- X, Y = self.plotdata
524
+ x, y = self.plotdata
539
525
  if self.region is not None:
540
526
  a, b = self.region
541
- Y = Y[(a <= X) & (X <= b)]
542
- if Y.size:
543
- return Y.mean()
544
-
527
+ y = y[(a <= x) & (x <= b)]
528
+ if y.size:
529
+ return y.mean()
530
+
545
531
  def linplot(self, frame, fit=True, force=True):
546
532
  if not force:
547
533
  if frame is self.__frame:
548
534
  return
549
- self.__frame = frame # update reference of the frame
535
+ self.__frame = frame # Update reference of the frame.
550
536
  if frame:
551
537
  sel = frame.selector
552
538
  if sel.shape[1] < 2:
553
539
  return
554
- if len(frame.buffer.shape) > 2: # RGB image
540
+ if len(frame.buffer.shape) > 2: # RGB image
555
541
  return
556
542
 
557
- xx, yy = sel[:,-2:] # get the last 2-selected line
558
- nx, ny = frame.xytopixel(xx, yy) # converts to pixel [ny,nx]
543
+ xx, yy = sel[:,-2:] # Get the last 2-selected line.
544
+ nx, ny = frame.xytopixel(xx, yy) # Converts to pixel [ny,nx]
559
545
  lx = nx[1] - nx[0]
560
546
  ly = ny[1] - ny[0]
561
547
  if lx or ly:
562
- L = np.hypot(lx, ly) # pixel length
563
- nv = (-ly/L, lx/L) # and norm vector to L
548
+ L = np.hypot(lx, ly) # pixel length
549
+ nv = (-ly/L, lx/L) # and norm vector to L
564
550
  else:
565
551
  L = 0
566
552
  nv = (0, 0)
567
553
 
568
- ## ピクセル空間:長さ L, サイズ N 分割でラインプロファイルをとる
554
+ ## ピクセル空間:長さ L, サイズ N 分割でラインプロファイルをとる.
569
555
  lw = self.__linewidth
570
556
  N = int(L) + 1
571
557
  xs = np.linspace(nx[0], nx[1], N)
@@ -579,29 +565,27 @@ class LineProfile(LinePlot):
579
565
  if any(mask):
580
566
  x = x[mask]
581
567
  y = y[mask]
582
- zi = frame.buffer[y.astype(int), x.astype(int)] # nearest: 速くてそこそこ正確
583
- ## zi = ndi.map_coordinates(frame.buffer, np.vstack((y, x))) # spline: 遅いが正確
568
+ zi = frame.buffer[y.astype(int), x.astype(int)] # nearest: 速くてそこそこ正確
569
+ # zi = ndimage.map_coordinates(frame.buffer, np.vstack((y, x))) # spline: 遅いが正確
584
570
  if zi.dtype in (np.complex64, np.complex128):
585
571
  zi = np.log(1 + abs(zi))
586
572
  zs[mask] += zi
587
573
  zs /= lw
588
574
 
589
- if self.__logicp: # axis to logical length # 論理長さ空間を使用する
575
+ if self.__logicp: # axis to logical length # 論理長さ空間を使用する
590
576
  L = np.hypot(xx[1]-xx[0], yy[1]-yy[0])
591
577
 
592
578
  ls = np.linspace(0, L, N)
593
579
  self.__plot.set_data(ls, zs)
594
580
  self.__plot.set_visible(1)
595
581
 
596
- if fit and len(ls) > 1: # drawing area
582
+ if fit and len(ls) > 1: # drawing area
597
583
  ly = self.ylim
598
584
  self.xlim = ls[0], ls[-1]
599
585
  self.ylim = ly[0], max(ly[1], max(zs))
600
-
601
- self.toolbar.update()
602
- self.toolbar.push_current()
586
+
603
587
  self.draw()
604
-
588
+
605
589
  def writeln(self):
606
590
  if not self.modeline.IsShown():
607
591
  return
@@ -609,7 +593,7 @@ class LineProfile(LinePlot):
609
593
  if frame:
610
594
  self.modeline.SetLabel(
611
595
  "[--] -{a}- {name} ({type}:{mode}) "
612
- "[{length}:{width}] {x} [{unit:g}/pixel]".format(
596
+ "[{length}:{width}] {x} [{unit:g}/pix]".format(
613
597
  name = frame.name,
614
598
  type = frame.buffer.dtype,
615
599
  mode = "logic" if self.__logicp else "pixel",
@@ -620,7 +604,7 @@ class LineProfile(LinePlot):
620
604
  a = '%%' if not frame.buffer.flags.writeable else '--'))
621
605
  else:
622
606
  self.modeline.SetLabel("")
623
-
607
+
624
608
  def write_data_to_clipboard(self):
625
609
  """Write plot data to clipboard."""
626
610
  X, Y = self.plotdata
@@ -629,71 +613,85 @@ class LineProfile(LinePlot):
629
613
  o.write("{:g}\t{:g}\n".format(x, y))
630
614
  Clipboard.write(o.getvalue())
631
615
  self.message("Write data to clipboard.")
632
-
616
+
633
617
  def annotate(self):
634
618
  LinePlot.annotate(self)
635
619
 
636
620
  x, y = self.plotdata
637
621
  if x.size:
638
- self.__fil.set_xy(list(chain([(x[0],0)], zip(x,y), [(x[-1],0)])))
622
+ self.__fil.set_xy(list(chain([(x[0], 0)], zip(x, y), [(x[-1], 0)])))
639
623
  self.writeln()
640
-
624
+
641
625
  ## --------------------------------
642
- ## Motion/Drag actions (override)
626
+ ## Region/Drag actions (override).
643
627
  ## --------------------------------
644
-
628
+
629
+ def OnHomePosition(self, evt):
630
+ """Go back to home position."""
631
+ x, y = self.plotdata
632
+ if x.size and y.size:
633
+ self.xlim = x[0], x[-1]
634
+ self.ylim = 0, y.max()
635
+ self.toolbar.update()
636
+ self.toolbar.push_current()
637
+ self.draw()
638
+
645
639
  def OnHomeXPosition(self, evt):
646
640
  x = self.plotdata[0]
647
641
  if x.size:
648
642
  self.xlim = x[0], x[-1]
649
643
  self.toolbar.push_current()
650
644
  self.draw()
651
-
645
+
652
646
  def OnHomeYPosition(self, evt):
653
647
  y = self.plotdata[1]
654
648
  if y.size:
655
649
  self.ylim = 0, y.max()
656
650
  self.toolbar.push_current()
657
651
  self.draw()
658
-
652
+
659
653
  def OnLineWidth(self, evt):
660
654
  n = -2 if evt.key[-1] == '-' else 2
661
655
  self.set_linewidth(self.__linewidth + n)
662
-
656
+
663
657
  def OnRegionShift(self, evt):
664
658
  if self.__frame and self.region is not None:
665
659
  u = self.__frame.unit
666
660
  if evt.key == "left": self.region -= u
667
661
  if evt.key == "right": self.region += u
668
662
  self.draw()
669
-
663
+
670
664
  def OnEscapeSelection(self, evt):
671
665
  self.__hline.set_visible(0)
672
666
  LinePlot.OnEscapeSelection(self, evt)
673
-
667
+
668
+ ## --------------------------------
669
+ ## Region-(H)Line/Drag actions.
670
+ ## --------------------------------
671
+
674
672
  def OnDragLineBegin(self, evt):
675
673
  self.set_wxcursor(wx.CURSOR_SIZENS)
676
-
674
+
677
675
  def OnDragTrace(self, evt):
678
676
  """Show average value."""
679
677
  y = self.calc_average()
680
678
  if y is not None:
681
679
  self.message(f"ya = {y:g}")
682
-
680
+
683
681
  def OnRegionLock(self, evt):
684
682
  """Show FWHM region."""
685
683
  x, y = self.plotdata
686
684
  if x.size:
687
685
  xc, yc = evt.xdata, evt.ydata
688
- u = x[1] - x[0] # != frame.unit (斜め線の場合 dx=unit とは限らない)
686
+ u = x[1] - x[0] # != frame.unit (斜め線の場合 dx=unit とは限らない)
689
687
  v = (y < yc)
690
688
  if v.all():
691
- self.region = None # all y < yc
689
+ self.region = None # all y < yc
692
690
  elif v.any():
693
691
  xa = x[(x < xc) & v]
694
692
  xb = x[(x > xc) & v]
695
- a = xa[-1] if xa.any() else x[ 0] # left-under bound
696
- b = xb[ 0] if xb.any() else x[-1] # right-over bound
693
+ a = xa[-1] if xa.any() else x[ 0] # left-under bound
694
+ b = xb[ 0] if xb.any() else x[-1] # right-over bound
697
695
  if (b-a-u)/u > 1e-3:
698
696
  if a > x[0]:
699
697
  n = np.where(x == a)[0][0]
@@ -707,39 +705,48 @@ class LineProfile(LinePlot):
707
705
  else:
708
706
  self.region = None
709
707
  else:
710
- self.region = x[[0,-1]] # all y > yc
708
+ self.region = x[[0,-1]] # all y > yc
711
709
 
712
710
  self.__hline.set_ydata([yc])
713
711
  self.__hline.set_visible(1)
714
712
  self.draw()
715
713
  self.message(f"yc = {yc:g}")
716
-
714
+
715
+ ## --------------------------------
716
+ ## Region-Mark/Drag actions.
717
+ ## --------------------------------
718
+ peak_blur_ratio = 0.01
719
+ peak_prominence_ratio = 0.1
720
+
717
721
  def OnMarkPeaks(self, evt):
718
722
  """Set markers on peaks."""
719
723
  x, y = self.plotdata
720
- if x.size:
721
- lw = 5
722
- window = np.hanning(lw)
723
- ys = np.convolve(window/window.sum(), y, mode='same')
724
-
725
- ## maxima = signal.find_peaks_cwt(ys, np.arange(lw,lw*2))
726
- maxima,_ = signal.find_peaks(ys, width=lw, prominence=20)
724
+ if x.size > 1:
725
+ # lw = 5
726
+ ux = x[1] - x[0] # equal spacing length
727
+ lw = max(1, self.peak_blur_ratio * (self.xbound[1] - self.xbound[0]) / ux)
728
+ lp = self.peak_prominence_ratio * (self.ybound[1] - self.ybound[0])
729
+ # window = np.hanning(lw)
730
+ # window = signal.windows.gaussian(lw, std=lw/6)
731
+ # ys = np.convolve(window/window.sum(), y, mode='same')
732
+ ys = ndimage.gaussian_filter1d(y, sigma=lw/6)
727
733
 
728
- ## minima = signal.find_peaks_cwt(-ys, np.arange(lw,lw*2))
729
- minima,_ = signal.find_peaks(-ys, width=lw, prominence=20)
734
+ maxima, _ = signal.find_peaks(ys, prominence=lp)
735
+ minima, _ = signal.find_peaks(-ys, prominence=lp)
730
736
 
731
737
  peaks = np.sort(np.append(maxima, minima))
732
738
  if peaks.size:
733
- self.Selector = x[peaks], y[peaks]
734
-
739
+ self.selector = x[peaks], y[peaks]
740
+ self.message(f"Peak detection: blur {lw=:g}, prom {lp=:g}")
741
+
735
742
  def OnMarkErase(self, evt):
736
743
  """Erase markers on peaks."""
737
- ## del self.Selector
744
+ ## del self.selector
738
745
  self.OnEscapeSelection(evt)
739
-
746
+
740
747
  def OnMarkSelectionBegin(self, evt):
741
748
  org = self.p_event
742
- xs, ys = self.Selector
749
+ xs, ys = self.selector
743
750
  xc, yc = org.xdata, org.ydata
744
751
  ## xc, yc = evt.xdata, evt.ydata
745
752
  if xs.size:
@@ -748,14 +755,16 @@ class LineProfile(LinePlot):
748
755
  self.__orgpoint = xs[j]
749
756
  self.set_wxcursor(wx.CURSOR_SIZEWE)
750
757
  self.draw()
751
-
758
+
752
759
  def OnMarkSelectionMove(self, evt):
753
- xs, ys = self.Selector
760
+ xs, ys = self.selector
754
761
  xc, yc = evt.xdata, evt.ydata
755
762
  if xs.size:
756
763
  ld = np.hypot((xs-xc)*self.ddpu[0], (ys-yc)*self.ddpu[1])
757
764
  j = np.argmin(ld)
758
- if ld[j] < 20: # check display-dot distance, snap to the nearest mark
765
+ if ld[j] < 20: # check display-dot distance, snap to the nearest mark
759
766
  xc = xs[j]
767
+ yc = ys[j]
768
+ self.message(f"({xc:g}, {yc:g})")
760
769
  self.region = (self.__orgpoint, xc)
761
770
  self.draw()