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/__init__.py +7 -5
- mwx/bookshelf.py +133 -57
- mwx/controls.py +555 -518
- mwx/framework.py +569 -544
- mwx/graphman.py +708 -702
- mwx/images.py +29 -0
- mwx/matplot2.py +226 -217
- mwx/matplot2g.py +733 -657
- mwx/matplot2lg.py +192 -183
- mwx/mgplt.py +21 -23
- mwx/nutshell.py +1162 -1019
- mwx/plugins/ffmpeg_view.py +74 -76
- mwx/plugins/fft_view.py +14 -16
- mwx/plugins/frame_listview.py +56 -53
- mwx/plugins/line_profile.py +2 -2
- mwx/py/filling.py +9 -8
- mwx/testsuite.py +38 -0
- mwx/utilus.py +273 -208
- mwx/wxmon.py +45 -40
- mwx/wxpdb.py +81 -92
- mwx/wxwil.py +18 -18
- mwx/wxwit.py +49 -51
- {mwxlib-0.99.0.dist-info → mwxlib-1.7.13.dist-info}/METADATA +19 -17
- mwxlib-1.7.13.dist-info/RECORD +28 -0
- {mwxlib-0.99.0.dist-info → mwxlib-1.7.13.dist-info}/WHEEL +1 -1
- mwxlib-0.99.0.dist-info/LICENSE +0 -21
- mwxlib-0.99.0.dist-info/RECORD +0 -28
- {mwxlib-0.99.0.dist-info → mwxlib-1.7.13.dist-info}/top_level.txt +0 -0
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
|
-
|
|
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
|
-
##
|
|
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='-')
|
|
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='-',
|
|
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
|
-
##
|
|
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
|
|
140
|
-
elif a-d < x < a+d: return 2
|
|
141
|
-
elif b-d < x < b+d: return 3
|
|
142
|
-
else: return 0
|
|
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)
|
|
153
|
+
self.set_wxcursor(wx.CURSOR_HAND) # insdie
|
|
154
154
|
elif v in (2,3):
|
|
155
|
-
self.set_wxcursor(wx.CURSOR_SIZEWE)
|
|
155
|
+
self.set_wxcursor(wx.CURSOR_SIZEWE) # on-edge
|
|
156
156
|
else:
|
|
157
|
-
self.set_wxcursor(wx.CURSOR_ARROW)
|
|
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)
|
|
166
|
+
self.set_wxcursor(wx.CURSOR_HAND) # inside
|
|
167
167
|
elif v == 2:
|
|
168
|
-
self.set_wxcursor(wx.CURSOR_SIZEWE)
|
|
169
|
-
self.__lastpoint = self.region[1]
|
|
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)
|
|
172
|
-
self.__lastpoint = self.region[0]
|
|
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)
|
|
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.")
|
|
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
|
-
|
|
220
|
-
|
|
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, ),
|
|
229
|
-
'space pressed' : (NORMAL, ),
|
|
230
|
-
'z pressed' : (NORMAL, ),
|
|
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
|
|
246
|
-
self.detach(
|
|
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.
|
|
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, *
|
|
263
|
-
for
|
|
264
|
-
if
|
|
265
|
-
self.
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
def detach(self, *
|
|
269
|
-
for
|
|
270
|
-
if
|
|
271
|
-
self.
|
|
272
|
-
|
|
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 コ)
|
|
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
|
|
289
|
+
self.__frame = frame # Update reference of the frame.
|
|
294
290
|
if frame:
|
|
295
|
-
x, y = frame.__data = self.calc(frame)
|
|
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
|
|
299
|
+
self.__frame = frame # Update reference of the frame.
|
|
306
300
|
if frame:
|
|
307
301
|
try:
|
|
308
|
-
x, y = frame.__data
|
|
302
|
+
x, y = frame.__data # Reuse cached data.
|
|
309
303
|
except Exception:
|
|
310
|
-
x, y = frame.__data = self.calc(frame)
|
|
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
|
-
##
|
|
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
|
|
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 ],
|
|
403
|
-
'S-[;-] pressed' : [ None, self.OnLineWidth ],
|
|
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=
|
|
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
|
|
466
|
-
self.detach(
|
|
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.
|
|
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, *
|
|
493
|
-
for
|
|
494
|
-
if
|
|
495
|
-
self.
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
def detach(self, *
|
|
499
|
-
for
|
|
500
|
-
if
|
|
501
|
-
self.
|
|
502
|
-
|
|
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:
|
|
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.
|
|
516
|
-
self.
|
|
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
|
-
|
|
524
|
+
x, y = self.plotdata
|
|
539
525
|
if self.region is not None:
|
|
540
526
|
a, b = self.region
|
|
541
|
-
|
|
542
|
-
if
|
|
543
|
-
return
|
|
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
|
|
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:
|
|
540
|
+
if len(frame.buffer.shape) > 2: # RGB image
|
|
555
541
|
return
|
|
556
542
|
|
|
557
|
-
xx, yy = sel[:,-2:]
|
|
558
|
-
nx, ny = frame.xytopixel(xx, yy)
|
|
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)
|
|
563
|
-
nv = (-ly/L, lx/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)]
|
|
583
|
-
|
|
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:
|
|
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:
|
|
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}/
|
|
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
|
-
##
|
|
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]
|
|
686
|
+
u = x[1] - x[0] # != frame.unit (斜め線の場合 dx=unit とは限らない)
|
|
689
687
|
v = (y < yc)
|
|
690
688
|
if v.all():
|
|
691
|
-
self.region = None
|
|
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]
|
|
696
|
-
b = xb[ 0] if xb.any() else x[-1]
|
|
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]]
|
|
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
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
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
|
-
|
|
729
|
-
minima,_ = signal.find_peaks(-ys,
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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:
|
|
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()
|