mwxlib 1.0.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 +6 -4
- mwx/bookshelf.py +133 -57
- mwx/controls.py +441 -375
- mwx/framework.py +531 -513
- mwx/graphman.py +683 -677
- mwx/images.py +16 -0
- mwx/matplot2.py +225 -216
- mwx/matplot2g.py +735 -652
- mwx/matplot2lg.py +192 -183
- mwx/mgplt.py +20 -22
- mwx/nutshell.py +1063 -939
- mwx/plugins/ffmpeg_view.py +74 -75
- mwx/plugins/fft_view.py +13 -15
- mwx/plugins/frame_listview.py +55 -52
- mwx/plugins/line_profile.py +1 -1
- mwx/py/filling.py +9 -8
- mwx/testsuite.py +38 -0
- mwx/utilus.py +273 -210
- mwx/wxmon.py +39 -38
- mwx/wxpdb.py +81 -83
- mwx/wxwil.py +18 -18
- mwx/wxwit.py +32 -35
- {mwxlib-1.0.0.dist-info → mwxlib-1.7.13.dist-info}/METADATA +12 -5
- mwxlib-1.7.13.dist-info/RECORD +28 -0
- {mwxlib-1.0.0.dist-info → mwxlib-1.7.13.dist-info}/WHEEL +1 -1
- mwxlib-1.0.0.dist-info/LICENSE +0 -21
- mwxlib-1.0.0.dist-info/RECORD +0 -28
- {mwxlib-1.0.0.dist-info → mwxlib-1.7.13.dist-info}/top_level.txt +0 -0
mwx/matplot2g.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#! python3
|
|
2
2
|
"""mwxlib graph plot for images.
|
|
3
3
|
"""
|
|
4
|
-
import traceback
|
|
5
4
|
import wx
|
|
6
5
|
|
|
7
6
|
from matplotlib import cm
|
|
@@ -14,13 +13,19 @@ from scipy import ndimage as ndi
|
|
|
14
13
|
|
|
15
14
|
from . import framework as mwx
|
|
16
15
|
from .framework import Menu
|
|
17
|
-
|
|
16
|
+
# from .utilus import warn
|
|
18
17
|
from .utilus import funcall as _F
|
|
19
18
|
from .controls import Clipboard
|
|
20
19
|
from .matplot2 import MatplotPanel
|
|
21
20
|
from .matplot2 import NORMAL, DRAGGING, PAN, ZOOM, MARK, LINE, REGION
|
|
22
21
|
|
|
23
22
|
|
|
23
|
+
def _to_array(x):
|
|
24
|
+
if isinstance(x, (list, tuple)):
|
|
25
|
+
x = np.array(x)
|
|
26
|
+
return x
|
|
27
|
+
|
|
28
|
+
|
|
24
29
|
def _to_cvtype(src):
|
|
25
30
|
"""Convert the image to a type that can be applied to the cv2 function.
|
|
26
31
|
Note:
|
|
@@ -31,14 +36,31 @@ def _to_cvtype(src):
|
|
|
31
36
|
return src
|
|
32
37
|
|
|
33
38
|
|
|
34
|
-
def
|
|
35
|
-
if isinstance(
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
def _to_buffer(img):
|
|
40
|
+
if isinstance(img, Image.Image):
|
|
41
|
+
# return np.asarray(img) # ref
|
|
42
|
+
return np.array(img) # copy
|
|
43
|
+
|
|
44
|
+
if isinstance(img, wx.Bitmap): # bitmap to image
|
|
45
|
+
img = img.ConvertToImage()
|
|
46
|
+
|
|
47
|
+
if isinstance(img, wx.Image): # image to RGB array; RGB to grayscale
|
|
48
|
+
w, h = img.GetSize()
|
|
49
|
+
img = np.frombuffer(img.GetDataBuffer(), dtype='uint8').reshape(h, w, 3)
|
|
50
|
+
|
|
51
|
+
if not isinstance(img, np.ndarray):
|
|
52
|
+
raise ValueError("targets must be arrays or images.")
|
|
53
|
+
|
|
54
|
+
if img.ndim < 2:
|
|
55
|
+
raise ValueError("targets must be 2d arrays.")
|
|
56
|
+
|
|
57
|
+
if img.ndim > 2:
|
|
58
|
+
return cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
|
|
59
|
+
return img
|
|
38
60
|
|
|
39
61
|
|
|
40
62
|
def _to_image(src, cutoff=0, threshold=None, binning=1):
|
|
41
|
-
"""Convert buffer to image <uint8
|
|
63
|
+
"""Convert buffer to image <uint8>.
|
|
42
64
|
|
|
43
65
|
>>> dst = (src-a) * 255 / (b-a)
|
|
44
66
|
|
|
@@ -49,29 +71,27 @@ def _to_image(src, cutoff=0, threshold=None, binning=1):
|
|
|
49
71
|
beta = -a * alpha
|
|
50
72
|
|
|
51
73
|
Args:
|
|
52
|
-
cutoff
|
|
53
|
-
threshold
|
|
54
|
-
binning
|
|
74
|
+
cutoff: cutoff score [%] to cut the lo/hi limits
|
|
75
|
+
threshold: limit bytes of image (to make matplotlib light)
|
|
76
|
+
binning: minimum binning number of src array
|
|
55
77
|
"""
|
|
56
|
-
if src.dtype in (np.complex64, np.complex128):
|
|
78
|
+
if src.dtype in (np.complex64, np.complex128): # maybe fft pattern
|
|
57
79
|
src = np.log(1 + abs(src))
|
|
58
80
|
|
|
59
|
-
bins = binning
|
|
60
81
|
if threshold:
|
|
61
|
-
##
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if
|
|
67
|
-
## src = src[::bins,::bins]
|
|
82
|
+
## Reduce the binning by itemsize before finally converting to <uint8>.
|
|
83
|
+
## Select the larger value between binning and threshold.
|
|
84
|
+
n = max(binning, int(np.sqrt(src.nbytes / threshold / src.itemsize)) + 1)
|
|
85
|
+
else:
|
|
86
|
+
n = binning
|
|
87
|
+
if n > 1:
|
|
68
88
|
src = _to_cvtype(src)
|
|
69
|
-
src = cv2.resize(src, None, fx=1/
|
|
89
|
+
src = cv2.resize(src, None, fx=1/n, fy=1/n, interpolation=cv2.INTER_AREA)
|
|
70
90
|
|
|
71
|
-
if src.dtype == np.uint8:
|
|
72
|
-
return
|
|
91
|
+
if src.dtype == np.uint8: # RGB or gray image <uint8>
|
|
92
|
+
return n, (0, 255), src
|
|
73
93
|
|
|
74
|
-
if hasattr(cutoff, '__iter__'):
|
|
94
|
+
if hasattr(cutoff, '__iter__'): # cutoff vlim: (vmin, vmax) is specified.
|
|
75
95
|
a, b = cutoff
|
|
76
96
|
elif cutoff > 0:
|
|
77
97
|
a = np.percentile(src, cutoff)
|
|
@@ -81,59 +101,47 @@ def _to_image(src, cutoff=0, threshold=None, binning=1):
|
|
|
81
101
|
b = src.max()
|
|
82
102
|
|
|
83
103
|
r = (255 / (b - a)) if a < b else 1
|
|
84
|
-
## img = cv2.convertScaleAbs(src, alpha=r, beta=-r*a)
|
|
85
|
-
img = np.uint8((src - a) * r)
|
|
104
|
+
## img = cv2.convertScaleAbs(src, alpha=r, beta=-r*a) # 負数は絶対値になるので以下に変更.
|
|
105
|
+
img = np.uint8((src - a) * r)
|
|
86
106
|
img[src < a] = 0
|
|
87
107
|
img[src > b] = 255
|
|
88
|
-
return
|
|
108
|
+
return n, (a, b), img
|
|
89
109
|
|
|
90
110
|
|
|
91
111
|
def _Property(name):
|
|
92
112
|
return property(
|
|
93
|
-
lambda self:
|
|
94
|
-
lambda self,v: setattr(self.parent, name, v),
|
|
95
|
-
lambda self:
|
|
113
|
+
lambda self: getattr(self.parent, name),
|
|
114
|
+
lambda self, v: setattr(self.parent, name, v),
|
|
115
|
+
lambda self: delattr(self.parent, name))
|
|
96
116
|
|
|
97
117
|
|
|
98
118
|
class AxesImagePhantom:
|
|
99
|
-
"""Phantom of frame facade
|
|
119
|
+
"""Phantom of frame facade.
|
|
100
120
|
|
|
101
121
|
Args:
|
|
102
|
-
buf
|
|
103
|
-
name
|
|
104
|
-
show
|
|
105
|
-
|
|
106
|
-
localunit : initial localunit
|
|
107
|
-
attributes : additional info:dict
|
|
122
|
+
buf: buffer
|
|
123
|
+
name: buffer name
|
|
124
|
+
show: show immediately when loaded
|
|
125
|
+
**kwargs: frame attributes
|
|
108
126
|
|
|
109
127
|
Note:
|
|
110
128
|
Due to the problem of performance,
|
|
111
129
|
the image pixel size could be reduced by binning.
|
|
112
130
|
"""
|
|
113
|
-
def __init__(self, parent, buf, name, show=True,
|
|
114
|
-
|
|
115
|
-
self.__owner = parent
|
|
131
|
+
def __init__(self, parent, buf, name, show=True, **kwargs):
|
|
132
|
+
self.parent = parent
|
|
116
133
|
self.__name = name
|
|
117
|
-
self.
|
|
118
|
-
self.
|
|
119
|
-
self.
|
|
120
|
-
self.
|
|
121
|
-
|
|
122
|
-
buf = np.array(buf) # copy buffer to array
|
|
123
|
-
if isinstance(buf, wx.Bitmap):
|
|
124
|
-
buf = buf.ConvertToImage() # bitmap to image
|
|
125
|
-
if isinstance(buf, wx.Image): # image to RGB array
|
|
126
|
-
w, h = buf.GetSize()
|
|
127
|
-
buf = np.frombuffer(buf.GetDataBuffer(), dtype='uint8').reshape(h, w, 3)
|
|
128
|
-
if buf.ndim > 2:
|
|
129
|
-
buf = cv2.cvtColor(buf, cv2.COLOR_RGB2GRAY) # RGB to grayscale
|
|
130
|
-
self.__buf = buf
|
|
134
|
+
self.__attributes = kwargs
|
|
135
|
+
self.__localunit = kwargs.get('localunit')
|
|
136
|
+
self.__center = kwargs.get('center', (0, 0))
|
|
137
|
+
self.__aspect_ratio = 1
|
|
138
|
+
self.__buf = _to_buffer(buf)
|
|
131
139
|
bins, vlim, img = _to_image(self.__buf,
|
|
132
140
|
cutoff = self.parent.score_percentile,
|
|
133
141
|
threshold = self.parent.nbytes_threshold,
|
|
134
142
|
)
|
|
135
143
|
self.__bins = bins
|
|
136
|
-
self.
|
|
144
|
+
self.__cuts = vlim
|
|
137
145
|
self.__art = parent.axes.imshow(img,
|
|
138
146
|
cmap = cm.gray,
|
|
139
147
|
aspect = 'equal',
|
|
@@ -141,63 +149,32 @@ class AxesImagePhantom:
|
|
|
141
149
|
visible = show,
|
|
142
150
|
picker = True,
|
|
143
151
|
)
|
|
144
|
-
self.update_extent()
|
|
145
|
-
|
|
152
|
+
self.update_extent()
|
|
153
|
+
|
|
146
154
|
def __getattr__(self, attr):
|
|
147
155
|
return getattr(self.__art, attr)
|
|
148
|
-
|
|
156
|
+
|
|
149
157
|
def __eq__(self, x):
|
|
158
|
+
## Called in `on_pick` and `__contains__` to check objects in.
|
|
150
159
|
return x is self.__art
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
lambda self: self.__art.get_array(),
|
|
160
|
-
doc="A displayed image array<uint8>.")
|
|
161
|
-
|
|
162
|
-
vlim = property(
|
|
163
|
-
lambda self: self.__vlim,
|
|
164
|
-
doc="Image lower/upper values.")
|
|
165
|
-
|
|
166
|
-
clim = property(
|
|
167
|
-
lambda self: self.__art.get_clim(),
|
|
168
|
-
lambda self,v: self.__art.set_clim(v),
|
|
169
|
-
doc="Buffer lower/upper values.")
|
|
170
|
-
|
|
171
|
-
attributes = property(
|
|
172
|
-
lambda self: self.__attributes,
|
|
173
|
-
doc="Miscellaneous info about the frame/buffer.")
|
|
174
|
-
|
|
175
|
-
pathname = property(
|
|
176
|
-
lambda self: self.__attributes.get('pathname'),
|
|
177
|
-
lambda self,v: self.update_attributes({'pathname': v}),
|
|
178
|
-
doc="A fullpath of buffer, when bounds to file.")
|
|
179
|
-
|
|
180
|
-
annotation = property(
|
|
181
|
-
lambda self: self.__attributes.get('annotation', ''),
|
|
182
|
-
lambda self,v: self.update_attributes({'annotation': v}),
|
|
183
|
-
doc="Annotation of the buffer.")
|
|
184
|
-
|
|
185
|
-
def update_attributes(self, attr=None, **kwargs):
|
|
186
|
-
"""Update frame-specifc attributes.
|
|
187
|
-
The frame holds any attributes with dictionary
|
|
188
|
-
There are some keys which acts as the value setter when given,
|
|
189
|
-
`annotation` also shows the message with infobar
|
|
190
|
-
`localunit` also updates the frame.unit
|
|
160
|
+
|
|
161
|
+
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
|
|
191
168
|
"""
|
|
192
|
-
|
|
193
|
-
|
|
169
|
+
if not attr:
|
|
170
|
+
return
|
|
194
171
|
self.__attributes.update(attr)
|
|
195
172
|
|
|
196
173
|
if 'localunit' in attr:
|
|
197
|
-
self.unit = attr['localunit']
|
|
174
|
+
self.unit = attr['localunit'] # => [frame_updated]
|
|
198
175
|
|
|
199
|
-
if '
|
|
200
|
-
self.
|
|
176
|
+
if 'center' in attr:
|
|
177
|
+
self.center = attr['center'] # => [frame_updated]
|
|
201
178
|
|
|
202
179
|
if 'annotation' in attr:
|
|
203
180
|
v = attr['annotation']
|
|
@@ -206,113 +183,169 @@ class AxesImagePhantom:
|
|
|
206
183
|
|
|
207
184
|
if {'pathname', 'annotation'} & attr.keys():
|
|
208
185
|
self.parent.handler('frame_updated', self)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
186
|
+
|
|
187
|
+
def update_buffer(self, buf=None):
|
|
188
|
+
"""Update buffer and the image (internal use only)."""
|
|
189
|
+
if buf is not None:
|
|
190
|
+
self.__buf = _to_buffer(buf)
|
|
191
|
+
|
|
192
|
+
bins, vlim, img = _to_image(self.__buf,
|
|
193
|
+
cutoff = self.parent.score_percentile,
|
|
194
|
+
threshold = self.parent.nbytes_threshold,
|
|
195
|
+
)
|
|
196
|
+
self.__bins = bins
|
|
197
|
+
self.__cuts = vlim
|
|
198
|
+
self.__art.set_array(img)
|
|
199
|
+
self.parent.handler('frame_modified', self)
|
|
200
|
+
|
|
201
|
+
def update_extent(self):
|
|
202
|
+
"""Update logical extent of the image (internal use only)."""
|
|
203
|
+
h, w = self.__buf.shape[:2]
|
|
204
|
+
ux, uy = self.xy_unit
|
|
205
|
+
w *= ux/2
|
|
206
|
+
h *= uy/2
|
|
207
|
+
cx, cy = self.center
|
|
208
|
+
self.__art.set_extent((cx-w, cx+w, cy-h, cy+h))
|
|
209
|
+
|
|
210
|
+
artist = property(
|
|
211
|
+
lambda self: self.__art)
|
|
212
|
+
|
|
213
|
+
binning = property(
|
|
214
|
+
lambda self: self.__bins,
|
|
215
|
+
doc="Binning value resulting from the score_percentile.")
|
|
216
|
+
|
|
217
|
+
cuts = property(
|
|
218
|
+
lambda self: self.__cuts,
|
|
219
|
+
doc="Lower/Upper cutoff values of the buffer.")
|
|
220
|
+
|
|
221
|
+
image = property(
|
|
222
|
+
lambda self: self.__art.get_array(),
|
|
223
|
+
doc="Displayed image array<uint8>.")
|
|
224
|
+
|
|
225
|
+
clim = property(
|
|
226
|
+
lambda self: self.__art.get_clim(),
|
|
227
|
+
lambda self, v: self.__art.set_clim(v),
|
|
228
|
+
doc="Lower/Upper color limit values of the buffer.")
|
|
229
|
+
|
|
230
|
+
attributes = property(
|
|
231
|
+
lambda self: self.__attributes,
|
|
232
|
+
doc="Auxiliary info about the frame.")
|
|
233
|
+
|
|
234
|
+
pathname = property(
|
|
235
|
+
lambda self: self.__attributes.get('pathname'),
|
|
236
|
+
lambda self, v: self.update_attr({'pathname': v}),
|
|
237
|
+
doc="Fullpath of the buffer, if bound to a file.")
|
|
238
|
+
|
|
239
|
+
annotation = property(
|
|
240
|
+
lambda self: self.__attributes.get('annotation', ''),
|
|
241
|
+
lambda self, v: self.update_attr({'annotation': v}),
|
|
242
|
+
doc="Annotation of the buffer.")
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def name(self):
|
|
246
|
+
return self.__name
|
|
247
|
+
|
|
214
248
|
@name.setter
|
|
215
249
|
def name(self, v):
|
|
216
250
|
self.__name = v
|
|
217
251
|
self.parent.handler('frame_updated', self)
|
|
218
|
-
|
|
252
|
+
|
|
219
253
|
@property
|
|
220
254
|
def localunit(self):
|
|
221
255
|
return self.__localunit
|
|
222
|
-
|
|
256
|
+
|
|
223
257
|
@property
|
|
224
258
|
def unit(self):
|
|
225
|
-
"""Logical length per pixel arb.unit [u/
|
|
259
|
+
"""Logical length per pixel arb.unit [u/pix]."""
|
|
226
260
|
return self.__localunit or self.parent.unit
|
|
227
|
-
|
|
261
|
+
|
|
228
262
|
@unit.setter
|
|
229
263
|
def unit(self, v):
|
|
230
|
-
if v
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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")
|
|
235
270
|
elif v <= 0:
|
|
236
271
|
raise ValueError("The unit value must be greater than zero")
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
return
|
|
240
|
-
self.__localunit = v
|
|
272
|
+
|
|
273
|
+
self.__localunit = v
|
|
241
274
|
self.__attributes['localunit'] = self.__localunit
|
|
242
275
|
self.update_extent()
|
|
243
276
|
self.parent.handler('frame_updated', self)
|
|
244
277
|
self.parent.canvas.draw_idle()
|
|
245
|
-
|
|
278
|
+
|
|
246
279
|
@unit.deleter
|
|
247
280
|
def unit(self):
|
|
248
281
|
self.unit = None
|
|
249
|
-
|
|
282
|
+
|
|
250
283
|
@property
|
|
251
284
|
def xy_unit(self):
|
|
252
285
|
u = self.__localunit or self.parent.unit
|
|
253
286
|
return (u, u * self.__aspect_ratio)
|
|
254
|
-
|
|
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)
|
|
299
|
+
|
|
255
300
|
@property
|
|
256
301
|
def aspect_ratio(self):
|
|
257
302
|
"""Aspect ratio of logical unit."""
|
|
258
303
|
return self.__aspect_ratio
|
|
259
|
-
|
|
304
|
+
|
|
260
305
|
@aspect_ratio.setter
|
|
261
306
|
def aspect_ratio(self, v):
|
|
262
|
-
|
|
263
|
-
return
|
|
264
|
-
self.__aspect_ratio = v or 1.0
|
|
307
|
+
self.__aspect_ratio = v or 1
|
|
265
308
|
self.update_extent()
|
|
266
309
|
self.parent.handler('frame_updated', self)
|
|
267
|
-
|
|
310
|
+
|
|
268
311
|
@property
|
|
269
312
|
def index(self):
|
|
270
|
-
"""Page number in the parent
|
|
313
|
+
"""Page number in the parent view."""
|
|
271
314
|
return self.parent.index(self)
|
|
272
|
-
|
|
273
|
-
def update_buffer(self, buf=None):
|
|
274
|
-
"""Update buffer and the image (internal use only)."""
|
|
275
|
-
if buf is not None:
|
|
276
|
-
self.__buf = buf
|
|
277
|
-
bins, vlim, img = _to_image(self.__buf,
|
|
278
|
-
cutoff = self.parent.score_percentile,
|
|
279
|
-
threshold = self.parent.nbytes_threshold,
|
|
280
|
-
)
|
|
281
|
-
self.__bins = bins
|
|
282
|
-
self.__vlim = vlim
|
|
283
|
-
self.__art.set_array(img)
|
|
284
|
-
self.parent.handler('frame_modified', self)
|
|
285
|
-
|
|
286
|
-
def update_extent(self):
|
|
287
|
-
"""Update logical extent of the image (internal use only)."""
|
|
288
|
-
h, w = self.__buf.shape[:2]
|
|
289
|
-
ux, uy = self.xy_unit
|
|
290
|
-
w *= ux/2
|
|
291
|
-
h *= uy/2
|
|
292
|
-
self.__art.set_extent((-w,w,-h,h))
|
|
293
|
-
|
|
315
|
+
|
|
294
316
|
@property
|
|
295
317
|
def roi(self):
|
|
296
318
|
"""Current buffer ROI (region of interest)."""
|
|
297
|
-
if self.parent.
|
|
298
|
-
nx, ny = self.xytopixel(*self.
|
|
299
|
-
sx = slice(max(0,nx[0]), nx[1])
|
|
300
|
-
sy = slice(max(0,ny[1]), ny[0])
|
|
301
|
-
return self.__buf[sy,sx]
|
|
302
|
-
return
|
|
303
|
-
|
|
319
|
+
if self.parent.region.size:
|
|
320
|
+
nx, ny = self.xytopixel(*self.region)
|
|
321
|
+
sx = slice(max(0, nx[0]), nx[1]) # nx slice
|
|
322
|
+
sy = slice(max(0, ny[1]), ny[0]) # ny slice 反転 (降順)
|
|
323
|
+
return self.__buf[sy, sx]
|
|
324
|
+
return None
|
|
325
|
+
|
|
304
326
|
@roi.setter
|
|
305
327
|
def roi(self, v):
|
|
306
|
-
self.
|
|
328
|
+
if not self.parent.region.size:
|
|
329
|
+
raise ValueError("region is not selected.")
|
|
330
|
+
self.roi[:] = v # cannot broadcast input array into different shape
|
|
307
331
|
self.update_buffer()
|
|
308
|
-
|
|
332
|
+
|
|
333
|
+
@property
|
|
334
|
+
def roi_or_buffer(self):
|
|
335
|
+
return self.roi if self.parent.region.size else self.buffer
|
|
336
|
+
|
|
337
|
+
@property
|
|
338
|
+
def buffer(self):
|
|
339
|
+
return self.__buf
|
|
340
|
+
|
|
309
341
|
@buffer.setter
|
|
310
342
|
def buffer(self, v):
|
|
311
343
|
self.update_buffer(v)
|
|
312
|
-
|
|
344
|
+
self.update_extent()
|
|
345
|
+
|
|
313
346
|
def xytoc(self, x, y=None, nearest=True):
|
|
314
347
|
"""Convert xydata (x,y) -> data[(x,y)] value of neaerst pixel.
|
|
315
|
-
|
|
348
|
+
If `nearest` is False, the return value is interpolated with spline.
|
|
316
349
|
"""
|
|
317
350
|
h, w = self.__buf.shape[:2]
|
|
318
351
|
nx, ny = self.xytopixel(x, y, cast=nearest)
|
|
@@ -320,42 +353,86 @@ class AxesImagePhantom:
|
|
|
320
353
|
if np.any(nx<0) or np.any(nx>=w) or np.any(ny<0) or np.any(ny>=h):
|
|
321
354
|
return
|
|
322
355
|
if nearest:
|
|
323
|
-
return self.__buf[ny, nx]
|
|
324
|
-
return ndi.map_coordinates(self.__buf, np.vstack((ny, nx)))
|
|
325
|
-
|
|
356
|
+
return self.__buf[ny, nx] # nearest value
|
|
357
|
+
return ndi.map_coordinates(self.__buf, np.vstack((ny, nx))) # spline value
|
|
358
|
+
|
|
326
359
|
def xytopixel(self, x, y=None, cast=True):
|
|
327
360
|
"""Convert xydata (x,y) -> [nx,ny] pixel.
|
|
328
|
-
If cast,
|
|
361
|
+
If `cast` is True, the return value will be integer pixel values.
|
|
329
362
|
"""
|
|
330
363
|
def _cast(n):
|
|
331
364
|
return np.int32(np.floor(np.round(n, 1)))
|
|
332
365
|
if y is None:
|
|
333
|
-
|
|
366
|
+
# warn("Setting xy data with single tuple.", DeprecationWarning)
|
|
334
367
|
x, y = x
|
|
335
368
|
x, y = _to_array(x), _to_array(y)
|
|
336
369
|
l,r,b,t = self.__art.get_extent()
|
|
337
370
|
ux, uy = self.xy_unit
|
|
338
371
|
nx = (x - l) / ux
|
|
339
|
-
ny = (t - y) / uy
|
|
372
|
+
ny = (t - y) / uy # Y ピクセルインデクスは座標と逆
|
|
340
373
|
if cast:
|
|
341
|
-
return (_cast(nx), _cast(ny))
|
|
342
|
-
return (nx-0.5, ny-0.5)
|
|
343
|
-
|
|
374
|
+
return np.array((_cast(nx), _cast(ny)))
|
|
375
|
+
return np.array((nx-0.5, ny-0.5))
|
|
376
|
+
|
|
344
377
|
def xyfrompixel(self, nx, ny=None):
|
|
345
|
-
"""Convert pixel [nx,ny] -> (x,y) xydata (float number).
|
|
378
|
+
"""Convert pixel [nx,ny] -> (x,y) xydata (float number).
|
|
379
|
+
"""
|
|
346
380
|
if ny is None:
|
|
347
|
-
|
|
381
|
+
# warn("Setting xy data with single tuple.", DeprecationWarning)
|
|
348
382
|
nx, ny = nx
|
|
349
383
|
nx, ny = _to_array(nx), _to_array(ny)
|
|
350
384
|
l,r,b,t = self.__art.get_extent()
|
|
351
385
|
ux, uy = self.xy_unit
|
|
352
386
|
x = l + (nx + 0.5) * ux
|
|
353
|
-
y = t - (ny + 0.5) * uy
|
|
354
|
-
return (x, y)
|
|
387
|
+
y = t - (ny + 0.5) * uy # Y ピクセルインデクスは座標と逆
|
|
388
|
+
return np.array((x, y))
|
|
389
|
+
|
|
390
|
+
selector = _Property('selector')
|
|
391
|
+
markers = _Property('markers')
|
|
392
|
+
region = _Property('region')
|
|
393
|
+
|
|
394
|
+
@property
|
|
395
|
+
def selector_pix(self):
|
|
396
|
+
"""Selected points array [[x],[y]] in pixels."""
|
|
397
|
+
return self.xytopixel(self.selector)
|
|
398
|
+
|
|
399
|
+
@selector_pix.setter
|
|
400
|
+
def selector_pix(self, v):
|
|
401
|
+
self.selector = self.xyfrompixel(v)
|
|
402
|
+
|
|
403
|
+
@selector_pix.deleter
|
|
404
|
+
def selector_pix(self):
|
|
405
|
+
del self.selector
|
|
406
|
+
|
|
407
|
+
@property
|
|
408
|
+
def markers_pix(self):
|
|
409
|
+
"""Marked points data array [[x],[y]] in pixels."""
|
|
410
|
+
return self.xytopixel(self.markers)
|
|
411
|
+
|
|
412
|
+
@markers_pix.setter
|
|
413
|
+
def markers_pix(self, v):
|
|
414
|
+
self.markers = self.xyfrompixel(v)
|
|
415
|
+
|
|
416
|
+
@markers_pix.deleter
|
|
417
|
+
def markers_pix(self):
|
|
418
|
+
del self.markers
|
|
419
|
+
|
|
420
|
+
@property
|
|
421
|
+
def region_pix(self):
|
|
422
|
+
"""Cropped points data array [l,r],[b,t] in pixels."""
|
|
423
|
+
return self.xytopixel(self.region)
|
|
424
|
+
|
|
425
|
+
@region_pix.setter
|
|
426
|
+
def region_pix(self, v):
|
|
427
|
+
self.region = self.xyfrompixel(v)
|
|
428
|
+
|
|
429
|
+
@region_pix.deleter
|
|
430
|
+
def region_pix(self):
|
|
431
|
+
del self.region
|
|
355
432
|
|
|
356
433
|
|
|
357
434
|
class GraphPlot(MatplotPanel):
|
|
358
|
-
"""Graph panel for 2D graph
|
|
435
|
+
"""Graph panel for 2D graph.
|
|
359
436
|
"""
|
|
360
437
|
def __init__(self, *args, **kwargs):
|
|
361
438
|
MatplotPanel.__init__(self, *args, **kwargs)
|
|
@@ -365,15 +442,15 @@ class GraphPlot(MatplotPanel):
|
|
|
365
442
|
|
|
366
443
|
self.handler.update({ # DNA<GraphPlot>
|
|
367
444
|
None : {
|
|
368
|
-
'frame_shown' : [ None ],
|
|
369
|
-
'frame_hidden' : [ None ],
|
|
370
|
-
'frame_loaded' : [ None ],
|
|
371
|
-
'frame_removed' : [ None ],
|
|
372
|
-
'frame_selected' : [ None ],
|
|
373
|
-
'frame_deselected' : [ None ],
|
|
374
|
-
'frame_modified' : [ None, _F(self.writeln) ],
|
|
375
|
-
'frame_updated' : [ None, _F(self.writeln) ],
|
|
376
|
-
'frame_cmapped' : [ None, _F(self.writeln) ],
|
|
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
|
|
377
454
|
'line_draw' : [ None ],
|
|
378
455
|
'line_drawn' : [ None, _draw ],
|
|
379
456
|
'line_move' : [ None ],
|
|
@@ -389,6 +466,8 @@ class GraphPlot(MatplotPanel):
|
|
|
389
466
|
'M-down pressed' : [ None, self.OnPageDown ],
|
|
390
467
|
'pageup pressed' : [ None, self.OnPageUp ],
|
|
391
468
|
'pagedown pressed' : [ None, self.OnPageDown ],
|
|
469
|
+
'home pressed' : [ None, _F(self.select, index=0) ],
|
|
470
|
+
'end pressed' : [ None, _F(self.select, index=-1) ],
|
|
392
471
|
'M-a pressed' : [ None, _F(self.fit_to_canvas) ],
|
|
393
472
|
'C-a pressed' : [ None, _F(self.fit_to_axes) ],
|
|
394
473
|
'C-i pressed' : [ None, _F(self.invert_cmap) ],
|
|
@@ -532,7 +611,7 @@ class GraphPlot(MatplotPanel):
|
|
|
532
611
|
(),
|
|
533
612
|
(mwx.ID_(500), "&Invert Color", "Invert colormap", wx.ITEM_CHECK,
|
|
534
613
|
lambda v: self.invert_cmap(),
|
|
535
|
-
lambda v: v.Check(self.
|
|
614
|
+
lambda v: v.Check(self.get_cmapstr()[-2:] == "_r")),
|
|
536
615
|
(),
|
|
537
616
|
(wx.ID_CLOSE, "&Kill buffer\t(C-k)", "Kill buffer", _Icon(wx.ART_DELETE),
|
|
538
617
|
lambda v: self.kill_buffer(),
|
|
@@ -543,7 +622,7 @@ class GraphPlot(MatplotPanel):
|
|
|
543
622
|
lambda v: v.Enable(self.frame is not None)),
|
|
544
623
|
]
|
|
545
624
|
|
|
546
|
-
## modeline menu:
|
|
625
|
+
## modeline menu: バッファリストメニューを追加する.
|
|
547
626
|
def _menu(j, s):
|
|
548
627
|
return (j, s, s, wx.ITEM_CHECK,
|
|
549
628
|
lambda v: self.select(s),
|
|
@@ -557,14 +636,14 @@ class GraphPlot(MatplotPanel):
|
|
|
557
636
|
self.Layout()
|
|
558
637
|
|
|
559
638
|
self.writeln()
|
|
560
|
-
|
|
639
|
+
|
|
561
640
|
def clear(self):
|
|
562
641
|
MatplotPanel.clear(self)
|
|
563
642
|
|
|
564
643
|
self.__Arts = []
|
|
565
644
|
self.__index = None
|
|
566
645
|
|
|
567
|
-
## cf. self.figure.dpi =
|
|
646
|
+
## cf. self.figure.dpi = 80 dpi (0.3175 mm/pix)
|
|
568
647
|
self.__unit = 1.0
|
|
569
648
|
|
|
570
649
|
#<matplotlib.lines.Line2D>
|
|
@@ -584,7 +663,7 @@ class GraphPlot(MatplotPanel):
|
|
|
584
663
|
self.__isPicked = None
|
|
585
664
|
self.selected.set_picker(8)
|
|
586
665
|
self.selected.set_clip_on(False)
|
|
587
|
-
|
|
666
|
+
|
|
588
667
|
def get_uniqname(self, name):
|
|
589
668
|
base = name = name or "*temp*"
|
|
590
669
|
i = 1
|
|
@@ -593,24 +672,18 @@ class GraphPlot(MatplotPanel):
|
|
|
593
672
|
i += 1
|
|
594
673
|
name = "{}<{:d}>".format(base, i)
|
|
595
674
|
return name
|
|
596
|
-
|
|
675
|
+
|
|
597
676
|
def load(self, buf, name=None, pos=None, show=True, **kwargs):
|
|
598
677
|
"""Load a buffer with a name.
|
|
599
678
|
|
|
600
679
|
Args:
|
|
601
|
-
buf
|
|
602
|
-
name
|
|
603
|
-
pos
|
|
604
|
-
show
|
|
605
|
-
|
|
680
|
+
buf: buffer array.
|
|
681
|
+
name: buffer name (default to *temp*).
|
|
682
|
+
pos: Insertion position in the frame list.
|
|
683
|
+
show: Show immediately when loaded.
|
|
606
684
|
**kwargs: frame attributes.
|
|
607
|
-
|
|
608
|
-
- localunit : localunit
|
|
609
|
-
- aspect : aspect ratio
|
|
610
|
-
- pathname : full path of the buffer file
|
|
611
685
|
"""
|
|
612
|
-
|
|
613
|
-
return
|
|
686
|
+
assert buf is not None, "Load buffer must be an array or path:str (not None)"
|
|
614
687
|
|
|
615
688
|
if isinstance(buf, str):
|
|
616
689
|
buf = Image.open(buf)
|
|
@@ -621,13 +694,13 @@ class GraphPlot(MatplotPanel):
|
|
|
621
694
|
j = -1
|
|
622
695
|
if pathname:
|
|
623
696
|
if pathname in paths:
|
|
624
|
-
j = paths.index(pathname)
|
|
697
|
+
j = paths.index(pathname) # identical path
|
|
625
698
|
elif name in names:
|
|
626
|
-
j = names.index(name)
|
|
699
|
+
j = names.index(name) # existing frame
|
|
627
700
|
if j != -1:
|
|
628
701
|
art = self.__Arts[j]
|
|
629
|
-
art.update_buffer(buf)
|
|
630
|
-
art.
|
|
702
|
+
art.update_buffer(buf) # => [frame_modified]
|
|
703
|
+
art.update_attr(kwargs) # => [frame_updated] localunit => [canvas_draw]
|
|
631
704
|
art.update_extent()
|
|
632
705
|
if show:
|
|
633
706
|
self.select(j)
|
|
@@ -635,31 +708,33 @@ class GraphPlot(MatplotPanel):
|
|
|
635
708
|
|
|
636
709
|
name = self.get_uniqname(name)
|
|
637
710
|
|
|
638
|
-
## The first load of axes.imshow (=> self.axes.axis 表示を更新する)
|
|
711
|
+
## The first load of axes.imshow (=> self.axes.axis 表示を更新する).
|
|
639
712
|
art = AxesImagePhantom(self, buf, name, show, **kwargs)
|
|
640
713
|
|
|
641
714
|
j = len(self) if pos is None else pos
|
|
642
715
|
self.__Arts.insert(j, art)
|
|
643
716
|
self.handler('frame_loaded', art)
|
|
644
717
|
if show:
|
|
645
|
-
u = self.frame and self.frame.unit
|
|
718
|
+
u = self.frame and self.frame.unit # current frame unit
|
|
646
719
|
self.select(j)
|
|
647
|
-
## Update view if the unit length is different from before selection
|
|
720
|
+
## Update view if the unit length is different from before selection.
|
|
648
721
|
if u != art.unit:
|
|
649
722
|
self.axes.axis(art.get_extent())
|
|
650
723
|
return art
|
|
651
|
-
|
|
652
|
-
def select(self,
|
|
653
|
-
if isinstance(
|
|
654
|
-
j = self.index(
|
|
724
|
+
|
|
725
|
+
def select(self, index):
|
|
726
|
+
if isinstance(index, (str, AxesImagePhantom)):
|
|
727
|
+
j = self.index(index)
|
|
728
|
+
else:
|
|
729
|
+
j = index
|
|
655
730
|
|
|
656
|
-
for art in self.__Arts:
|
|
731
|
+
for art in self.__Arts: # Hide all frames
|
|
657
732
|
art.set_visible(0)
|
|
658
733
|
|
|
659
734
|
if j != self.__index and self.__index is not None:
|
|
660
735
|
self.handler('frame_hidden', self.frame)
|
|
661
736
|
|
|
662
|
-
if j is not None:
|
|
737
|
+
if j is not None and self.__Arts:
|
|
663
738
|
art = self.__Arts[j]
|
|
664
739
|
art.set_visible(1)
|
|
665
740
|
self.__index = j % len(self)
|
|
@@ -669,10 +744,13 @@ class GraphPlot(MatplotPanel):
|
|
|
669
744
|
|
|
670
745
|
self.draw()
|
|
671
746
|
self.writeln()
|
|
672
|
-
self.trace_point(*self.
|
|
673
|
-
|
|
747
|
+
self.trace_point(*self.selector)
|
|
674
748
|
return self.frame
|
|
675
|
-
|
|
749
|
+
|
|
750
|
+
def __iter__(self):
|
|
751
|
+
for art in self.__Arts:
|
|
752
|
+
yield art.buffer
|
|
753
|
+
|
|
676
754
|
def __getitem__(self, j):
|
|
677
755
|
if isinstance(j, str):
|
|
678
756
|
j = self.index(j)
|
|
@@ -680,27 +758,23 @@ class GraphPlot(MatplotPanel):
|
|
|
680
758
|
buffers = [art.buffer for art in self.__Arts]
|
|
681
759
|
if hasattr(j, '__iter__'):
|
|
682
760
|
return [buffers[i] for i in j]
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
761
|
+
return buffers[j] # j can also be slicing
|
|
762
|
+
|
|
686
763
|
def __setitem__(self, j, v):
|
|
764
|
+
if v is None:
|
|
765
|
+
raise ValueError("values must be buffers, not NoneType")
|
|
766
|
+
|
|
687
767
|
if isinstance(j, str):
|
|
688
|
-
|
|
689
|
-
j = self.index(j) # overwrite buffer
|
|
690
|
-
except ValueError:
|
|
691
|
-
return self.load(v, name=j) # new buffer
|
|
768
|
+
return self.load(v, name=j) # update buffer or new buffer
|
|
692
769
|
|
|
693
|
-
if
|
|
694
|
-
raise ValueError("attempt to assign buffers
|
|
770
|
+
if isinstance(j, slice) or hasattr(j, '__iter__'):
|
|
771
|
+
raise ValueError("attempt to assign buffers via slicing or iterator")
|
|
695
772
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
art.update_extent()
|
|
702
|
-
self.select(j)
|
|
703
|
-
|
|
773
|
+
art = self.__Arts[j]
|
|
774
|
+
art.update_buffer(v) # update buffer
|
|
775
|
+
art.update_extent()
|
|
776
|
+
self.select(j)
|
|
777
|
+
|
|
704
778
|
def __delitem__(self, j):
|
|
705
779
|
if isinstance(j, str):
|
|
706
780
|
j = self.index(j)
|
|
@@ -713,7 +787,7 @@ class GraphPlot(MatplotPanel):
|
|
|
713
787
|
arts = [self.__Arts[j]]
|
|
714
788
|
|
|
715
789
|
if arts:
|
|
716
|
-
indices = [art.index for art in arts]
|
|
790
|
+
indices = [art.index for art in arts] # frames to be removed
|
|
717
791
|
for art in arts:
|
|
718
792
|
art.remove()
|
|
719
793
|
self.__Arts.remove(art)
|
|
@@ -724,105 +798,121 @@ class GraphPlot(MatplotPanel):
|
|
|
724
798
|
n = len(self)
|
|
725
799
|
self.__index = None if n==0 else j if j<n else n-1
|
|
726
800
|
self.select(self.__index)
|
|
727
|
-
|
|
728
|
-
## __len__ は bool() でも呼び出されるため,オブジェクト判定で偽を返すことがある (PY2)
|
|
729
|
-
## __nonzero__ : bool() を追加しておく必要がある (PY2)
|
|
730
|
-
|
|
801
|
+
|
|
802
|
+
## __len__ は bool() でも呼び出されるため,オブジェクト判定で偽を返すことがある (PY2).
|
|
803
|
+
## __nonzero__ : bool() を追加しておく必要がある (PY2).
|
|
804
|
+
|
|
731
805
|
def __len__(self):
|
|
732
806
|
return len(self.__Arts)
|
|
733
|
-
|
|
807
|
+
|
|
734
808
|
def __nonzero__(self):
|
|
735
809
|
return True
|
|
736
|
-
|
|
810
|
+
|
|
737
811
|
def __bool__(self):
|
|
738
812
|
return True
|
|
739
|
-
|
|
813
|
+
|
|
740
814
|
def __contains__(self, j):
|
|
741
815
|
if isinstance(j, str):
|
|
742
816
|
return j in (art.name for art in self.__Arts)
|
|
817
|
+
elif isinstance(j, np.ndarray):
|
|
818
|
+
return any(j is art.buffer for art in self.__Arts)
|
|
743
819
|
else:
|
|
744
820
|
return j in self.__Arts
|
|
745
|
-
|
|
821
|
+
|
|
746
822
|
def index(self, j):
|
|
747
823
|
if isinstance(j, str):
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
824
|
+
return next(i for i, art in enumerate(self.__Arts) if j == art.name)
|
|
825
|
+
elif isinstance(j, np.ndarray):
|
|
826
|
+
return next(i for i, art in enumerate(self.__Arts) if j is art.buffer)
|
|
827
|
+
else:
|
|
828
|
+
return self.__Arts.index(j) # j:frame -> int
|
|
829
|
+
|
|
752
830
|
def find_frame(self, j):
|
|
753
831
|
if isinstance(j, str):
|
|
754
|
-
return next((art for art in self.__Arts if art.name
|
|
755
|
-
|
|
756
|
-
|
|
832
|
+
return next((art for art in self.__Arts if j == art.name), None)
|
|
833
|
+
elif isinstance(j, np.ndarray):
|
|
834
|
+
return next((art for art in self.__Arts if j is art.buffer), None)
|
|
835
|
+
else:
|
|
836
|
+
return self.__Arts[j] # j:int -> frame
|
|
837
|
+
|
|
838
|
+
def get_all_frames(self, j=None):
|
|
839
|
+
"""List of arts <matplotlib.image.AxesImage>."""
|
|
840
|
+
if j is None:
|
|
841
|
+
yield from self.__Arts
|
|
842
|
+
elif isinstance(j, str):
|
|
843
|
+
yield from (art for art in self.__Arts if j in art.name)
|
|
844
|
+
elif isinstance(j, np.ndarray):
|
|
845
|
+
yield from (art for art in self.__Arts if j is art.buffer)
|
|
846
|
+
|
|
757
847
|
## --------------------------------
|
|
758
|
-
## Property of frame / drawer
|
|
848
|
+
## Property of frame / drawer.
|
|
759
849
|
## --------------------------------
|
|
760
|
-
|
|
761
|
-
|
|
850
|
+
|
|
851
|
+
## Image bytes max for loading matplotlib (with wxAgg backend).
|
|
762
852
|
nbytes_threshold = 24e6
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
score_percentile = 0.
|
|
766
|
-
|
|
853
|
+
|
|
854
|
+
## Image cutoff score percentiles.
|
|
855
|
+
score_percentile = 0.005
|
|
856
|
+
|
|
767
857
|
@property
|
|
768
858
|
def all_frames(self):
|
|
769
859
|
"""List of arts <matplotlib.image.AxesImage>."""
|
|
770
860
|
return self.__Arts
|
|
771
|
-
|
|
861
|
+
|
|
772
862
|
@property
|
|
773
863
|
def frame(self):
|
|
774
864
|
"""Current art <matplotlib.image.AxesImage>."""
|
|
775
865
|
if self.__Arts and self.__index is not None:
|
|
776
866
|
return self.__Arts[self.__index]
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
867
|
+
|
|
868
|
+
@property
|
|
869
|
+
def buffer(self):
|
|
870
|
+
"""Current buffer array."""
|
|
871
|
+
if self.frame:
|
|
872
|
+
return self.frame.buffer
|
|
873
|
+
|
|
874
|
+
@buffer.setter
|
|
875
|
+
def buffer(self, v):
|
|
876
|
+
if self.frame:
|
|
877
|
+
self.__setitem__(self.__index, v)
|
|
878
|
+
else:
|
|
879
|
+
self.load(v)
|
|
880
|
+
|
|
789
881
|
@property
|
|
790
882
|
def unit(self):
|
|
791
|
-
"""Logical length per pixel arb.unit [u/
|
|
883
|
+
"""Logical length per pixel arb.unit [u/pix]."""
|
|
792
884
|
return self.__unit
|
|
793
|
-
|
|
885
|
+
|
|
794
886
|
@unit.setter
|
|
795
887
|
def unit(self, v):
|
|
796
|
-
if v
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
raise ValueError("
|
|
888
|
+
if v == self.__unit: # no effect unless unit changes
|
|
889
|
+
return
|
|
890
|
+
if v is None or np.isnan(v) or np.isinf(v):
|
|
891
|
+
raise ValueError("The unit value must not be nan or inf")
|
|
800
892
|
elif v <= 0:
|
|
801
893
|
raise ValueError("The unit value must be greater than zero")
|
|
802
894
|
else:
|
|
803
|
-
if v == self.__unit: # no effect unless unit changes
|
|
804
|
-
return
|
|
805
895
|
self.__unit = v
|
|
806
896
|
for art in self.__Arts:
|
|
807
897
|
art.update_extent()
|
|
808
898
|
self.handler('frame_updated', art)
|
|
809
899
|
self.canvas.draw_idle()
|
|
810
|
-
|
|
900
|
+
|
|
811
901
|
def kill_buffer(self):
|
|
812
|
-
if self.
|
|
813
|
-
del self.
|
|
814
|
-
|
|
902
|
+
if self.frame:
|
|
903
|
+
del self[self.__index]
|
|
904
|
+
|
|
815
905
|
def kill_buffer_all(self):
|
|
816
906
|
del self[:]
|
|
817
|
-
|
|
907
|
+
|
|
818
908
|
def fit_to_axes(self):
|
|
819
909
|
"""Reset the view limits to the current frame extent."""
|
|
820
910
|
if self.frame:
|
|
821
|
-
self.axes.axis(self.frame.get_extent())
|
|
911
|
+
self.axes.axis(self.frame.get_extent()) # reset xlim and ylim
|
|
822
912
|
self.toolbar.update()
|
|
823
913
|
self.toolbar.push_current()
|
|
824
914
|
self.draw()
|
|
825
|
-
|
|
915
|
+
|
|
826
916
|
def fit_to_canvas(self):
|
|
827
917
|
"""Reset the view limits to the canvas range."""
|
|
828
918
|
x, y = self.xlim, self.ylim
|
|
@@ -838,55 +928,55 @@ class GraphPlot(MatplotPanel):
|
|
|
838
928
|
dy = (x[1] - x[0]) / 2 * r
|
|
839
929
|
self.ylim = cy-dy, cy+dy
|
|
840
930
|
self.draw()
|
|
841
|
-
|
|
931
|
+
|
|
842
932
|
def on_focus_set(self, evt):
|
|
843
933
|
"""Called when focus is set (override)."""
|
|
844
934
|
MatplotPanel.on_focus_set(self, evt)
|
|
845
935
|
if self.frame:
|
|
846
936
|
self.handler('frame_selected', self.frame)
|
|
847
937
|
self.on_picker_unlock(evt)
|
|
848
|
-
self.trace_point(*self.
|
|
849
|
-
|
|
938
|
+
self.trace_point(*self.selector)
|
|
939
|
+
|
|
850
940
|
def on_focus_kill(self, evt):
|
|
851
941
|
"""Called when focus is killed (override)."""
|
|
852
942
|
MatplotPanel.on_focus_kill(self, evt)
|
|
853
943
|
if self.frame:
|
|
854
944
|
self.handler('frame_deselected', self.frame)
|
|
855
945
|
self.on_picker_lock(evt)
|
|
856
|
-
|
|
857
|
-
def
|
|
946
|
+
|
|
947
|
+
def get_cmapstr(self):
|
|
858
948
|
if self.frame:
|
|
859
949
|
return self.frame.get_cmap().name
|
|
860
950
|
return ''
|
|
861
|
-
|
|
862
|
-
def
|
|
951
|
+
|
|
952
|
+
def set_cmapstr(self, name):
|
|
863
953
|
if self.frame:
|
|
864
954
|
self.frame.set_cmap(name)
|
|
865
955
|
self.handler('frame_cmapped', self.frame)
|
|
866
956
|
self.draw()
|
|
867
|
-
|
|
957
|
+
|
|
868
958
|
def invert_cmap(self):
|
|
869
959
|
if self.frame:
|
|
870
960
|
name = self.frame.get_cmap().name
|
|
871
|
-
self.
|
|
872
|
-
|
|
961
|
+
self.set_cmapstr(name + "_r" if name[-2:] != "_r" else name[:-2])
|
|
962
|
+
|
|
873
963
|
def trace_point(self, x, y, type=NORMAL):
|
|
874
964
|
"""Puts (override) a message of points x and y."""
|
|
875
965
|
frame = self.frame
|
|
876
966
|
if frame:
|
|
877
|
-
if not hasattr(x, '__iter__'):
|
|
967
|
+
if not hasattr(x, '__iter__'): # called from OnMotion
|
|
878
968
|
nx, ny = frame.xytopixel(x, y)
|
|
879
969
|
z = frame.xytoc(x, y)
|
|
880
970
|
self.message(f"[{nx:-4d},{ny:-4d}] ({x:-8.3f},{y:-8.3f}) value: {z}")
|
|
881
971
|
return
|
|
882
972
|
|
|
883
|
-
if len(x) == 0:
|
|
973
|
+
if len(x) == 0: # no selection
|
|
884
974
|
return
|
|
885
975
|
|
|
886
|
-
if len(x) == 1:
|
|
976
|
+
if len(x) == 1: # 1-selector trace point (called from Marker:setter)
|
|
887
977
|
return self.trace_point(x[0], y[0], type)
|
|
888
978
|
|
|
889
|
-
if len(x) == 2:
|
|
979
|
+
if len(x) == 2: # 2-selector trace line (called from selector:setter)
|
|
890
980
|
nx, ny = frame.xytopixel(x, y)
|
|
891
981
|
dx = x[1] - x[0]
|
|
892
982
|
dy = y[1] - y[0]
|
|
@@ -895,12 +985,12 @@ class GraphPlot(MatplotPanel):
|
|
|
895
985
|
li = np.hypot(nx[1]-nx[0], ny[1]-ny[0])
|
|
896
986
|
self.message(f"[Line] Length: {li:.1f} pixel ({lu:g}u) Angle: {a:.1f} deg")
|
|
897
987
|
|
|
898
|
-
elif type == REGION:
|
|
988
|
+
elif type == REGION: # N-selector trace polygon (called from region:setter)
|
|
899
989
|
nx, ny = frame.xytopixel(x, y)
|
|
900
|
-
xo, yo = min(nx), min(ny)
|
|
901
|
-
xr, yr = max(nx), max(ny)
|
|
902
|
-
self.message(f"[Region] crop={xr-xo}:{yr-yo}:{xo}:{yo}")
|
|
903
|
-
|
|
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)
|
|
993
|
+
|
|
904
994
|
def writeln(self):
|
|
905
995
|
"""Puts (override) attributes of current frame to the modeline."""
|
|
906
996
|
if not self.modeline.IsShown():
|
|
@@ -909,7 +999,7 @@ class GraphPlot(MatplotPanel):
|
|
|
909
999
|
if frame:
|
|
910
1000
|
self.modeline.SetLabel(
|
|
911
1001
|
"[{page}/{maxpage}] -{a}- {name} ({data.dtype}:{cmap}{bins}) "
|
|
912
|
-
"[{data.shape[1]}:{data.shape[0]}] {x} [{unit:g}/
|
|
1002
|
+
"[{data.shape[1]}:{data.shape[0]}] {x} [{unit:g}/pix]".format(
|
|
913
1003
|
page = self.__index,
|
|
914
1004
|
maxpage = len(self),
|
|
915
1005
|
name = frame.name,
|
|
@@ -921,53 +1011,47 @@ class GraphPlot(MatplotPanel):
|
|
|
921
1011
|
a = '%%' if not frame.buffer.flags.writeable else '--'))
|
|
922
1012
|
else:
|
|
923
1013
|
self.modeline.SetLabel(
|
|
924
|
-
"[{page}/{maxpage}] ---- No buffer (-:-) [-:-] -- [{unit:g}/
|
|
1014
|
+
"[{page}/{maxpage}] ---- No buffer (-:-) [-:-] -- [{unit:g}/pix]".format(
|
|
925
1015
|
page = '-',
|
|
926
1016
|
maxpage = len(self),
|
|
927
1017
|
unit = self.__unit))
|
|
928
|
-
|
|
1018
|
+
|
|
929
1019
|
## --------------------------------
|
|
930
|
-
##
|
|
1020
|
+
## 外部入出力/複合インターフェース.
|
|
931
1021
|
## --------------------------------
|
|
932
1022
|
## GraphPlot 間共有のグローバル変数
|
|
933
1023
|
clipboard_name = None
|
|
934
1024
|
clipboard_data = None
|
|
935
|
-
|
|
1025
|
+
|
|
936
1026
|
def write_buffer_to_clipboard(self):
|
|
937
1027
|
"""Write buffer data to clipboard."""
|
|
938
1028
|
frame = self.frame
|
|
939
1029
|
if not frame:
|
|
940
1030
|
self.message("No frame")
|
|
941
1031
|
return
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
traceback.print_exc()
|
|
952
|
-
self.message("- Failed to write to clipboard.", e)
|
|
953
|
-
|
|
1032
|
+
|
|
1033
|
+
name = frame.name
|
|
1034
|
+
data = frame.roi_or_buffer
|
|
1035
|
+
GraphPlot.clipboard_name = name
|
|
1036
|
+
GraphPlot.clipboard_data = data
|
|
1037
|
+
bins, vlim, img = _to_image(data, frame.cuts)
|
|
1038
|
+
Clipboard.imwrite(img)
|
|
1039
|
+
self.message("Write buffer to clipboard.")
|
|
1040
|
+
|
|
954
1041
|
def read_buffer_from_clipboard(self):
|
|
955
1042
|
"""Read buffer data from clipboard."""
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1043
|
+
name = GraphPlot.clipboard_name
|
|
1044
|
+
data = GraphPlot.clipboard_data
|
|
1045
|
+
if name:
|
|
1046
|
+
self.message("Read buffer from clipboard.")
|
|
1047
|
+
GraphPlot.clipboard_name = None
|
|
1048
|
+
GraphPlot.clipboard_data = None
|
|
1049
|
+
else:
|
|
1050
|
+
self.message("Read image from clipboard.")
|
|
1051
|
+
data = Clipboard.imread()
|
|
1052
|
+
if data is not None:
|
|
966
1053
|
self.load(data)
|
|
967
|
-
|
|
968
|
-
traceback.print_exc()
|
|
969
|
-
self.message("- No data in clipboard.", e)
|
|
970
|
-
|
|
1054
|
+
|
|
971
1055
|
def destroy_colorbar(self):
|
|
972
1056
|
if self.cbar:
|
|
973
1057
|
self.cbar = None
|
|
@@ -976,13 +1060,13 @@ class GraphPlot(MatplotPanel):
|
|
|
976
1060
|
self.canvas.draw_idle()
|
|
977
1061
|
self.handler.unbind('frame_cmapped', self.update_colorbar)
|
|
978
1062
|
self.handler.unbind('frame_shown', self.update_colorbar)
|
|
979
|
-
|
|
1063
|
+
|
|
980
1064
|
def update_colorbar(self, frame):
|
|
981
1065
|
if self.cbar:
|
|
982
1066
|
self.cbar.update_normal(frame)
|
|
983
1067
|
self.canvas.draw_idle()
|
|
984
1068
|
self.figure.draw_without_rendering()
|
|
985
|
-
|
|
1069
|
+
|
|
986
1070
|
def create_colorbar(self):
|
|
987
1071
|
"""Make a colorbar.
|
|
988
1072
|
The colorbar is plotted in self.figure.axes[1] (second axes)
|
|
@@ -997,12 +1081,12 @@ class GraphPlot(MatplotPanel):
|
|
|
997
1081
|
self.handler.bind('frame_shown', self.update_colorbar)
|
|
998
1082
|
else:
|
|
999
1083
|
self.message("- A frame must exist to create a colorbar.")
|
|
1000
|
-
|
|
1084
|
+
|
|
1001
1085
|
## --------------------------------
|
|
1002
|
-
## matplotlib
|
|
1086
|
+
## matplotlib interface.
|
|
1003
1087
|
## --------------------------------
|
|
1004
|
-
|
|
1005
|
-
def on_pick(self, evt):
|
|
1088
|
+
|
|
1089
|
+
def on_pick(self, evt): #<matplotlib.backend_bases.PickEvent>
|
|
1006
1090
|
"""Pickup image and other arts.
|
|
1007
1091
|
Called (maybe) after mouse buttons are pressed.
|
|
1008
1092
|
"""
|
|
@@ -1016,70 +1100,70 @@ class GraphPlot(MatplotPanel):
|
|
|
1016
1100
|
if not evt.mouseevent.inaxes:
|
|
1017
1101
|
return
|
|
1018
1102
|
|
|
1019
|
-
##
|
|
1103
|
+
## 画像が選択された場合.
|
|
1020
1104
|
if evt.artist in self.__Arts:
|
|
1021
1105
|
if self.__isPicked:
|
|
1022
|
-
self.__isPicked = None
|
|
1106
|
+
self.__isPicked = None # release pick guard
|
|
1023
1107
|
else:
|
|
1024
1108
|
self.handler('image_picked', evt)
|
|
1025
1109
|
|
|
1026
|
-
##
|
|
1110
|
+
## その他のプロットが選択された場合.
|
|
1027
1111
|
else:
|
|
1028
1112
|
if evt.artist is self.marked:
|
|
1029
|
-
self.__isPicked = 'mark'
|
|
1113
|
+
self.__isPicked = 'mark' # image pick gurad
|
|
1030
1114
|
self.handler('mark_picked', evt)
|
|
1031
1115
|
|
|
1032
1116
|
elif evt.artist is self.rected:
|
|
1033
|
-
self.__isPicked = 'region'
|
|
1117
|
+
self.__isPicked = 'region' # image pick gurad
|
|
1034
1118
|
self.handler('region_picked', evt)
|
|
1035
1119
|
|
|
1036
1120
|
elif evt.artist is self.selected:
|
|
1037
|
-
if (self.
|
|
1038
|
-
or wx.GetKeyState(wx.WXK_SHIFT)):
|
|
1121
|
+
if (self.selector.shape[1] < 2 # single selector
|
|
1122
|
+
or wx.GetKeyState(wx.WXK_SHIFT)): # or polygon mode
|
|
1039
1123
|
return
|
|
1040
|
-
self.__isPicked = 'line'
|
|
1124
|
+
self.__isPicked = 'line' # image pick gurad
|
|
1041
1125
|
self.handler('line_picked', evt)
|
|
1042
1126
|
else:
|
|
1043
1127
|
self.__isPicked = 'art'
|
|
1044
|
-
MatplotPanel.on_pick(self, evt)
|
|
1128
|
+
MatplotPanel.on_pick(self, evt) # [art_picked]
|
|
1045
1129
|
|
|
1046
1130
|
self.canvas.draw_idle()
|
|
1047
|
-
|
|
1131
|
+
|
|
1048
1132
|
def on_picker_lock(self, evt):
|
|
1049
1133
|
self.__isPicked = True
|
|
1050
|
-
|
|
1134
|
+
|
|
1051
1135
|
def on_picker_unlock(self, evt):
|
|
1052
1136
|
self.__isPicked = False
|
|
1053
|
-
|
|
1054
|
-
def OnImagePicked(self, evt):
|
|
1137
|
+
|
|
1138
|
+
def OnImagePicked(self, evt): #<matplotlib.backend_bases.PickEvent>
|
|
1055
1139
|
x = evt.mouseevent.xdata
|
|
1056
1140
|
y = evt.mouseevent.ydata
|
|
1057
1141
|
nx, ny = self.frame.xytopixel(x, y)
|
|
1058
1142
|
x, y = self.frame.xyfrompixel(nx, ny)
|
|
1059
1143
|
evt.ind = (ny, nx)
|
|
1060
|
-
self.
|
|
1061
|
-
|
|
1144
|
+
self.selector = (x, y)
|
|
1145
|
+
|
|
1062
1146
|
def _inaxes(self, evt):
|
|
1063
1147
|
try:
|
|
1064
|
-
return evt.inaxes is not self.axes
|
|
1148
|
+
return evt.inaxes is not self.axes #<matplotlib.backend_bases.MouseEvent>
|
|
1065
1149
|
except AttributeError:
|
|
1066
|
-
return None
|
|
1067
|
-
|
|
1150
|
+
return None #<wx._core.KeyEvent>
|
|
1151
|
+
|
|
1068
1152
|
## --------------------------------
|
|
1069
|
-
## Pan/Zoom actions (override)
|
|
1153
|
+
## Pan/Zoom actions (override).
|
|
1070
1154
|
## --------------------------------
|
|
1071
1155
|
## antialiased, nearest, bilinear, bicubic, spline16,
|
|
1072
1156
|
## spline36, hanning, hamming, hermite, kaiser, quadric,
|
|
1073
|
-
## catrom, gaussian, bessel, mitchell, sinc, lanczos, or none
|
|
1157
|
+
## catrom, gaussian, bessel, mitchell, sinc, lanczos, or none.
|
|
1074
1158
|
interpolation_mode = 'bilinear'
|
|
1075
|
-
|
|
1159
|
+
|
|
1076
1160
|
def OnDraw(self, evt):
|
|
1077
1161
|
"""Called before canvas.draw (overridden)."""
|
|
1078
1162
|
if not self.interpolation_mode:
|
|
1079
1163
|
return
|
|
1080
1164
|
frame = self.frame
|
|
1081
1165
|
if frame:
|
|
1082
|
-
## [dots/
|
|
1166
|
+
## [dots/pix] = [dots/u] * [u/pix]
|
|
1083
1167
|
dots = self.ddpu[0] * frame.unit * frame.binning
|
|
1084
1168
|
|
|
1085
1169
|
if frame.get_interpolation() == 'nearest' and dots < 1:
|
|
@@ -1087,33 +1171,33 @@ class GraphPlot(MatplotPanel):
|
|
|
1087
1171
|
|
|
1088
1172
|
elif frame.get_interpolation() != 'nearest' and dots > 1:
|
|
1089
1173
|
frame.set_interpolation('nearest')
|
|
1090
|
-
|
|
1174
|
+
|
|
1091
1175
|
def OnMotion(self, evt):
|
|
1092
1176
|
"""Called when mouse moves in axes (overridden)."""
|
|
1093
|
-
if self.
|
|
1177
|
+
if self.selector.shape[1] < 2:
|
|
1094
1178
|
self.trace_point(evt.xdata, evt.ydata)
|
|
1095
|
-
|
|
1179
|
+
|
|
1096
1180
|
def OnPageDown(self, evt):
|
|
1097
1181
|
"""Next page."""
|
|
1098
1182
|
i = self.__index
|
|
1099
1183
|
if i is not None and i < len(self)-1:
|
|
1100
1184
|
self.select(i + 1)
|
|
1101
|
-
|
|
1185
|
+
|
|
1102
1186
|
def OnPageUp(self, evt):
|
|
1103
1187
|
"""Previous page."""
|
|
1104
1188
|
i = self.__index
|
|
1105
1189
|
if i is not None and i > 0:
|
|
1106
1190
|
self.select(i - 1)
|
|
1107
|
-
|
|
1191
|
+
|
|
1108
1192
|
def OnHomePosition(self, evt):
|
|
1109
1193
|
self.fit_to_axes()
|
|
1110
|
-
|
|
1194
|
+
|
|
1111
1195
|
def OnEscapeSelection(self, evt):
|
|
1112
|
-
xs, ys = self.
|
|
1113
|
-
del self.
|
|
1196
|
+
xs, ys = self.selector
|
|
1197
|
+
del self.selector
|
|
1114
1198
|
if len(xs) > 1:
|
|
1115
1199
|
self.handler('line_removed', self.frame)
|
|
1116
|
-
|
|
1200
|
+
|
|
1117
1201
|
def OnXAxisPanZoom(self, evt, c=None):
|
|
1118
1202
|
org = self.p_event
|
|
1119
1203
|
M = np.exp(-(evt.x - org.x)/100)
|
|
@@ -1123,7 +1207,7 @@ class GraphPlot(MatplotPanel):
|
|
|
1123
1207
|
self.ylim = self.zoomlim(self.ylim, M)
|
|
1124
1208
|
org.x, org.y = evt.x, evt.y
|
|
1125
1209
|
self.draw()
|
|
1126
|
-
|
|
1210
|
+
|
|
1127
1211
|
def OnYAxisPanZoom(self, evt, c=None):
|
|
1128
1212
|
org = self.p_event
|
|
1129
1213
|
M = np.exp(-(evt.y - org.y)/100)
|
|
@@ -1133,11 +1217,11 @@ class GraphPlot(MatplotPanel):
|
|
|
1133
1217
|
self.ylim = self.zoomlim(self.ylim, M, c)
|
|
1134
1218
|
org.x, org.y = evt.x, evt.y
|
|
1135
1219
|
self.draw()
|
|
1136
|
-
|
|
1220
|
+
|
|
1137
1221
|
## --------------------------------
|
|
1138
|
-
## Selector interface
|
|
1222
|
+
## Selector interface.
|
|
1139
1223
|
## --------------------------------
|
|
1140
|
-
|
|
1224
|
+
|
|
1141
1225
|
def calc_point(self, x, y, centred=True, inaxes=False):
|
|
1142
1226
|
"""Computes the nearest pixelated point from a point (x, y).
|
|
1143
1227
|
If centred, correct the points to the center of the nearest pixel.
|
|
@@ -1165,63 +1249,63 @@ class GraphPlot(MatplotPanel):
|
|
|
1165
1249
|
x = l + nx * ux
|
|
1166
1250
|
y = t - ny * uy
|
|
1167
1251
|
return (x, y)
|
|
1168
|
-
|
|
1252
|
+
|
|
1169
1253
|
def calc_shiftpoint(self, xo, yo, x, y, centred=True):
|
|
1170
1254
|
"""Restrict point (x, y) from (xo, yo) in pi/8 step angles.
|
|
1171
1255
|
If centred, correct the point to the center of the nearest pixel.
|
|
1172
1256
|
"""
|
|
1173
1257
|
dx, dy = x-xo, y-yo
|
|
1174
|
-
L = np.hypot(dy,dx)
|
|
1175
|
-
a = np.arctan2(dy,dx)
|
|
1176
|
-
aa = np.linspace(-pi,pi,9) + pi/8
|
|
1258
|
+
L = np.hypot(dy, dx)
|
|
1259
|
+
a = np.arctan2(dy, dx)
|
|
1260
|
+
aa = np.linspace(-pi, pi, 9) + pi/8 # 角度の検索範囲
|
|
1177
1261
|
k = np.searchsorted(aa, a)
|
|
1178
1262
|
x = xo + L * np.cos(aa[k] - pi/8)
|
|
1179
1263
|
y = yo + L * np.sin(aa[k] - pi/8)
|
|
1180
1264
|
return self.calc_point(x, y, centred)
|
|
1181
|
-
|
|
1265
|
+
|
|
1182
1266
|
def OnSelectorAppend(self, evt):
|
|
1183
|
-
xs, ys = self.
|
|
1267
|
+
xs, ys = self.selector
|
|
1184
1268
|
x, y = self.calc_point(evt.xdata, evt.ydata)
|
|
1185
|
-
self.
|
|
1269
|
+
self.selector = np.append(xs, x), np.append(ys, y)
|
|
1186
1270
|
self.handler('line_drawn', self.frame)
|
|
1187
|
-
|
|
1271
|
+
|
|
1188
1272
|
def OnDragLock(self, evt):
|
|
1189
1273
|
pass
|
|
1190
|
-
|
|
1274
|
+
|
|
1191
1275
|
def OnDragBegin(self, evt):
|
|
1192
1276
|
if not self.frame or self._inaxes(evt):
|
|
1193
1277
|
self.handler('quit', evt)
|
|
1194
1278
|
return
|
|
1195
|
-
org = self.p_event
|
|
1279
|
+
org = self.p_event # the last pressed
|
|
1196
1280
|
self.__lastpoint = self.calc_point(org.xdata, org.ydata)
|
|
1197
|
-
self.__orgpoints = self.
|
|
1198
|
-
|
|
1281
|
+
self.__orgpoints = self.selector
|
|
1282
|
+
|
|
1199
1283
|
def OnDragMove(self, evt, shift=False):
|
|
1200
1284
|
x, y = self.calc_point(evt.xdata, evt.ydata)
|
|
1201
1285
|
xo, yo = self.__lastpoint
|
|
1202
1286
|
if shift:
|
|
1203
1287
|
x, y = self.calc_shiftpoint(xo, yo, x, y)
|
|
1204
|
-
self.
|
|
1288
|
+
self.selector = np.append(xo, x), np.append(yo, y)
|
|
1205
1289
|
self.handler('line_draw', self.frame)
|
|
1206
|
-
|
|
1290
|
+
|
|
1207
1291
|
def OnDragShiftMove(self, evt):
|
|
1208
1292
|
self.OnDragMove(evt, shift=True)
|
|
1209
|
-
|
|
1293
|
+
|
|
1210
1294
|
def OnDragEscape(self, evt):
|
|
1211
|
-
self.
|
|
1295
|
+
self.selector = self.__orgpoints
|
|
1212
1296
|
self.handler('line_draw', self.frame)
|
|
1213
1297
|
|
|
1214
1298
|
def OnDragEnd(self, evt):
|
|
1215
1299
|
x, y = self.calc_point(evt.xdata, evt.ydata)
|
|
1216
1300
|
xo, yo = self.__lastpoint
|
|
1217
1301
|
if x == xo and y == yo:
|
|
1218
|
-
self.
|
|
1302
|
+
self.selector = ([x], [y])
|
|
1219
1303
|
self.handler('line_drawn', self.frame)
|
|
1220
|
-
|
|
1304
|
+
|
|
1221
1305
|
## --------------------------------
|
|
1222
|
-
## Selector +Line interface
|
|
1306
|
+
## Selector + Line interface.
|
|
1223
1307
|
## --------------------------------
|
|
1224
|
-
|
|
1308
|
+
|
|
1225
1309
|
def OnLineSelected(self, evt):
|
|
1226
1310
|
k = evt.ind[0]
|
|
1227
1311
|
x = evt.mouseevent.xdata
|
|
@@ -1229,18 +1313,18 @@ class GraphPlot(MatplotPanel):
|
|
|
1229
1313
|
xs, ys = evt.artist.get_data(orig=0)
|
|
1230
1314
|
dots = np.hypot(x-xs[k], y-ys[k]) * self.ddpu[0]
|
|
1231
1315
|
self.__linesel = k if dots < 8 else None
|
|
1232
|
-
|
|
1233
|
-
def OnLineDeselected(self, evt):
|
|
1316
|
+
|
|
1317
|
+
def OnLineDeselected(self, evt): #<matplotlib.backend_bases.PickEvent>
|
|
1234
1318
|
self.__linesel = None
|
|
1235
|
-
|
|
1319
|
+
|
|
1236
1320
|
def OnLineDragBegin(self, evt):
|
|
1237
1321
|
if not self.frame or self._inaxes(evt):
|
|
1238
1322
|
self.handler('quit', evt)
|
|
1239
1323
|
return
|
|
1240
|
-
org = self.p_event
|
|
1324
|
+
org = self.p_event # the last pressed
|
|
1241
1325
|
self.__lastpoint = self.calc_point(org.xdata, org.ydata)
|
|
1242
|
-
self.__orgpoints = self.
|
|
1243
|
-
|
|
1326
|
+
self.__orgpoints = self.selector
|
|
1327
|
+
|
|
1244
1328
|
def OnLineDragMove(self, evt, shift=False):
|
|
1245
1329
|
x, y = self.calc_point(evt.xdata, evt.ydata)
|
|
1246
1330
|
xc, yc = self.__lastpoint
|
|
@@ -1249,36 +1333,36 @@ class GraphPlot(MatplotPanel):
|
|
|
1249
1333
|
if j is not None:
|
|
1250
1334
|
if shift:
|
|
1251
1335
|
i = j-1 if j else 1
|
|
1252
|
-
xo, yo = xo[i], yo[i]
|
|
1336
|
+
xo, yo = xo[i], yo[i] # となりの点を基準とする
|
|
1253
1337
|
x, y = self.calc_shiftpoint(xo, yo, x, y)
|
|
1254
|
-
xs, ys = self.
|
|
1338
|
+
xs, ys = self.selector
|
|
1255
1339
|
xs[j], ys[j] = x, y
|
|
1256
|
-
self.
|
|
1340
|
+
self.selector = (xs, ys)
|
|
1257
1341
|
self.handler('line_draw', self.frame)
|
|
1258
1342
|
else:
|
|
1259
1343
|
xs = xo + (x - xc)
|
|
1260
1344
|
ys = yo + (y - yc)
|
|
1261
|
-
self.
|
|
1345
|
+
self.selector = (xs, ys)
|
|
1262
1346
|
self.handler('line_move', self.frame)
|
|
1263
|
-
|
|
1347
|
+
|
|
1264
1348
|
def OnLineDragShiftMove(self, evt):
|
|
1265
1349
|
self.OnLineDragMove(evt, shift=True)
|
|
1266
|
-
|
|
1350
|
+
|
|
1267
1351
|
def OnLineDragEscape(self, evt):
|
|
1268
|
-
self.
|
|
1352
|
+
self.selector = self.__orgpoints
|
|
1269
1353
|
if self.__linesel:
|
|
1270
1354
|
self.handler('line_drawn', self.frame)
|
|
1271
1355
|
else:
|
|
1272
1356
|
self.handler('line_moved', self.frame)
|
|
1273
|
-
|
|
1357
|
+
|
|
1274
1358
|
def OnLineDragEnd(self, evt):
|
|
1275
1359
|
if self.__linesel:
|
|
1276
1360
|
self.handler('line_drawn', self.frame)
|
|
1277
1361
|
else:
|
|
1278
1362
|
self.handler('line_moved', self.frame)
|
|
1279
|
-
|
|
1363
|
+
|
|
1280
1364
|
def OnLineShift(self, evt):
|
|
1281
|
-
if self.
|
|
1365
|
+
if self.selector.size and self.frame:
|
|
1282
1366
|
ux, uy = self.frame.xy_unit
|
|
1283
1367
|
du = {
|
|
1284
1368
|
'up' : ( 0., uy),
|
|
@@ -1286,45 +1370,225 @@ class GraphPlot(MatplotPanel):
|
|
|
1286
1370
|
'left' : (-ux, 0.),
|
|
1287
1371
|
'right' : ( ux, 0.),
|
|
1288
1372
|
}
|
|
1289
|
-
self.
|
|
1373
|
+
self.selector += np.resize(du[evt.key], (2,1))
|
|
1290
1374
|
self.handler('line_move', self.frame)
|
|
1291
|
-
|
|
1375
|
+
|
|
1292
1376
|
def OnLineShiftEnd(self, evt):
|
|
1293
1377
|
self.handler('line_moved', self.frame)
|
|
1294
|
-
|
|
1378
|
+
|
|
1295
1379
|
## --------------------------------
|
|
1296
|
-
##
|
|
1380
|
+
## Marker interface.
|
|
1297
1381
|
## --------------------------------
|
|
1298
|
-
|
|
1382
|
+
|
|
1383
|
+
## Limit number of markers to display 最大(表示)数を制限する.
|
|
1384
|
+
maxnum_markers = 1000
|
|
1385
|
+
|
|
1299
1386
|
@property
|
|
1300
|
-
def
|
|
1301
|
-
"""
|
|
1387
|
+
def markers(self):
|
|
1388
|
+
"""Marked points data array [[x],[y]]."""
|
|
1389
|
+
xm, ym = self.marked.get_data(orig=0)
|
|
1390
|
+
return np.array((xm, ym))
|
|
1391
|
+
|
|
1392
|
+
@markers.setter
|
|
1393
|
+
def markers(self, v):
|
|
1394
|
+
x, y = v
|
|
1395
|
+
if not hasattr(x, '__iter__'):
|
|
1396
|
+
x, y = [x], [y]
|
|
1397
|
+
elif len(x) > self.maxnum_markers:
|
|
1398
|
+
self.message("- Got too many markers ({}) to plot".format(len(x)))
|
|
1399
|
+
return
|
|
1400
|
+
self.marked.set_data(x, y)
|
|
1401
|
+
self.__marksel = []
|
|
1402
|
+
self.update_art_of_mark()
|
|
1403
|
+
self.handler('mark_drawn', self.frame)
|
|
1404
|
+
|
|
1405
|
+
@markers.deleter
|
|
1406
|
+
def markers(self):
|
|
1407
|
+
if self.markers.size:
|
|
1408
|
+
self.marked.set_data([], [])
|
|
1409
|
+
self.__marksel = []
|
|
1410
|
+
self.update_art_of_mark()
|
|
1411
|
+
self.handler('mark_removed', self.frame)
|
|
1412
|
+
|
|
1413
|
+
def get_current_mark(self):
|
|
1414
|
+
"""Currently selected mark."""
|
|
1415
|
+
xm, ym = self.marked.get_data(orig=0)
|
|
1416
|
+
return np.take((xm, ym), self.__marksel, axis=1)
|
|
1417
|
+
|
|
1418
|
+
def set_current_mark(self, x, y):
|
|
1419
|
+
xm, ym = self.marked.get_data(orig=0)
|
|
1420
|
+
j = self.__marksel
|
|
1421
|
+
if j:
|
|
1422
|
+
xm[j], ym[j] = x, y
|
|
1423
|
+
self.marked.set_data(xm, ym)
|
|
1424
|
+
self.update_art_of_mark(j, xm[j], ym[j])
|
|
1425
|
+
else:
|
|
1426
|
+
n = len(xm)
|
|
1427
|
+
k = len(x) if hasattr(x, '__iter__') else 1
|
|
1428
|
+
self.__marksel = list(range(n, n+k))
|
|
1429
|
+
xm, ym = np.append(xm, x), np.append(ym, y)
|
|
1430
|
+
self.marked.set_data(xm, ym)
|
|
1431
|
+
self.marked.set_visible(1)
|
|
1432
|
+
self.update_art_of_mark()
|
|
1433
|
+
self.selector = (x, y)
|
|
1434
|
+
|
|
1435
|
+
def del_current_mark(self):
|
|
1436
|
+
j = self.__marksel
|
|
1437
|
+
if j:
|
|
1438
|
+
xm, ym = self.marked.get_data(orig=0)
|
|
1439
|
+
xm, ym = np.delete(xm, j), np.delete(ym, j)
|
|
1440
|
+
self.__marksel = []
|
|
1441
|
+
self.marked.set_data(xm, ym)
|
|
1442
|
+
n = len(xm)
|
|
1443
|
+
self.__marksel = [j[-1] % n] if n > 0 else []
|
|
1444
|
+
self.update_art_of_mark()
|
|
1445
|
+
|
|
1446
|
+
def update_art_of_mark(self, *args):
|
|
1447
|
+
if args:
|
|
1448
|
+
for k, x, y in zip(*args):
|
|
1449
|
+
art = self.__markarts[k] # art の再描画処理をして終了
|
|
1450
|
+
art.xy = x, y
|
|
1451
|
+
self.draw(self.marked)
|
|
1452
|
+
return
|
|
1453
|
+
for art in self.__markarts: # or reset all arts
|
|
1454
|
+
art.remove()
|
|
1455
|
+
self.__markarts = []
|
|
1456
|
+
if self.marked.get_visible() and self.handler.current_state in (MARK, MARK+DRAGGING):
|
|
1457
|
+
N = self.maxnum_markers
|
|
1458
|
+
xm, ym = self.marked.get_data(orig=0)
|
|
1459
|
+
for k, (x, y) in enumerate(zip(xm[:N], ym[:N])):
|
|
1460
|
+
self.__markarts.append(
|
|
1461
|
+
self.axes.annotate(k, #<matplotlib.text.Annotation>
|
|
1462
|
+
xy=(x,y), xycoords='data',
|
|
1463
|
+
xytext=(6,6), textcoords='offset points',
|
|
1464
|
+
bbox=dict(boxstyle="round", fc=(1,1,1,), ec=(1,0,0,)),
|
|
1465
|
+
color='red', size=7, #fontsize=8,
|
|
1466
|
+
)
|
|
1467
|
+
)
|
|
1468
|
+
self.trace_point(*self.get_current_mark(), type=MARK)
|
|
1469
|
+
self.draw(self.marked)
|
|
1470
|
+
|
|
1471
|
+
def OnMarkAppend(self, evt):
|
|
1472
|
+
xs, ys = self.selector
|
|
1473
|
+
if not self.__marksel and len(xs) > 0:
|
|
1474
|
+
self.set_current_mark(xs, ys)
|
|
1475
|
+
self.handler('mark_drawn', self.frame)
|
|
1476
|
+
self.update_art_of_mark()
|
|
1477
|
+
|
|
1478
|
+
def OnMarkRemove(self, evt):
|
|
1479
|
+
if self.__marksel:
|
|
1480
|
+
self.del_current_mark()
|
|
1481
|
+
self.handler('mark_removed', self.frame)
|
|
1482
|
+
|
|
1483
|
+
def OnMarkSelected(self, evt): #<matplotlib.backend_bases.PickEvent>
|
|
1484
|
+
k = evt.ind[0]
|
|
1485
|
+
if evt.mouseevent.key == 'shift': # 多重マーカー選択
|
|
1486
|
+
if k not in self.__marksel:
|
|
1487
|
+
self.__marksel += [k]
|
|
1488
|
+
else:
|
|
1489
|
+
self.__marksel = [k]
|
|
1490
|
+
self.update_art_of_mark()
|
|
1491
|
+
self.selector = self.get_current_mark()
|
|
1492
|
+
if self.selector.shape[1] > 1:
|
|
1493
|
+
self.handler('line_drawn', self.frame) # 多重マーカー選択時
|
|
1494
|
+
|
|
1495
|
+
def OnMarkDeselected(self, evt): #<matplotlib.backend_bases.PickEvent>
|
|
1496
|
+
self.__marksel = []
|
|
1497
|
+
self.update_art_of_mark()
|
|
1498
|
+
|
|
1499
|
+
def OnMarkDragBegin(self, evt):
|
|
1500
|
+
if not self.frame or self._inaxes(evt):
|
|
1501
|
+
self.handler('quit', evt)
|
|
1502
|
+
return
|
|
1503
|
+
self.__orgpoints = self.get_current_mark()
|
|
1504
|
+
|
|
1505
|
+
def OnMarkDragMove(self, evt):
|
|
1506
|
+
x, y = self.calc_point(evt.xdata, evt.ydata)
|
|
1507
|
+
self.set_current_mark(x, y)
|
|
1508
|
+
self.handler('mark_draw', self.frame)
|
|
1509
|
+
|
|
1510
|
+
def OnMarkDragEscape(self, evt):
|
|
1511
|
+
self.set_current_mark(*self.__orgpoints)
|
|
1512
|
+
self.handler('mark_drawn', self.frame)
|
|
1513
|
+
|
|
1514
|
+
def OnMarkDragEnd(self, evt):
|
|
1515
|
+
self.handler('mark_drawn', self.frame)
|
|
1516
|
+
|
|
1517
|
+
def OnMarkShift(self, evt):
|
|
1518
|
+
j = self.__marksel
|
|
1519
|
+
if j and self.frame:
|
|
1520
|
+
ux, uy = self.frame.xy_unit
|
|
1521
|
+
du = {
|
|
1522
|
+
'up' : ( 0., uy),
|
|
1523
|
+
'down' : ( 0.,-uy),
|
|
1524
|
+
'left' : (-ux, 0.),
|
|
1525
|
+
'right' : ( ux, 0.),
|
|
1526
|
+
}
|
|
1527
|
+
p = self.get_current_mark() + np.resize(du[evt.key], (2,1))
|
|
1528
|
+
self.set_current_mark(*p)
|
|
1529
|
+
self.handler('mark_draw', self.frame)
|
|
1530
|
+
|
|
1531
|
+
def OnMarkShiftEnd(self, evt):
|
|
1532
|
+
self.handler('mark_drawn', self.frame)
|
|
1533
|
+
|
|
1534
|
+
def next_mark(self, j):
|
|
1535
|
+
self.__marksel = [j]
|
|
1536
|
+
xs, ys = self.get_current_mark()
|
|
1537
|
+
self.xlim += xs[-1] - (self.xlim[1] + self.xlim[0]) / 2
|
|
1538
|
+
self.ylim += ys[-1] - (self.ylim[1] + self.ylim[0]) / 2
|
|
1539
|
+
self.selector = (xs, ys)
|
|
1540
|
+
self.trace_point(xs, ys, type=MARK)
|
|
1541
|
+
self.draw()
|
|
1542
|
+
|
|
1543
|
+
def OnMarkSkipNext(self, evt):
|
|
1544
|
+
n = self.markers.shape[1]
|
|
1545
|
+
j = self.__marksel
|
|
1546
|
+
if j:
|
|
1547
|
+
self.next_mark((j[-1]+1) % n)
|
|
1548
|
+
elif n:
|
|
1549
|
+
self.next_mark(0)
|
|
1550
|
+
|
|
1551
|
+
def OnMarkSkipPrevious(self, evt):
|
|
1552
|
+
n = self.markers.shape[1]
|
|
1553
|
+
j = self.__marksel
|
|
1554
|
+
if j:
|
|
1555
|
+
self.next_mark((j[-1]-1) % n)
|
|
1556
|
+
elif n:
|
|
1557
|
+
self.next_mark(-1)
|
|
1558
|
+
|
|
1559
|
+
## --------------------------------
|
|
1560
|
+
## Region interface.
|
|
1561
|
+
## --------------------------------
|
|
1562
|
+
|
|
1563
|
+
@property
|
|
1564
|
+
def region(self):
|
|
1565
|
+
"""Cropped rectangle points data array [l,r],[b,t]."""
|
|
1302
1566
|
x, y = self.rected.get_data(orig=0)
|
|
1303
1567
|
if len(x) and len(y):
|
|
1304
|
-
xo, x = min(x), max(x)
|
|
1305
|
-
yo, y = min(y), max(y)
|
|
1568
|
+
xo, x = min(x), max(x) # x[[0, 2]]
|
|
1569
|
+
yo, y = min(y), max(y) # y[[0, 2]]
|
|
1306
1570
|
return np.array(((xo, x), (yo, y)))
|
|
1307
1571
|
return np.resize(0., (2,0))
|
|
1308
|
-
|
|
1309
|
-
@
|
|
1310
|
-
def
|
|
1572
|
+
|
|
1573
|
+
@region.setter
|
|
1574
|
+
def region(self, v):
|
|
1311
1575
|
x, y = v
|
|
1312
1576
|
if len(x) > 1:
|
|
1313
1577
|
self.set_current_rect(x, y)
|
|
1314
1578
|
self.handler('region_drawn', self.frame)
|
|
1315
|
-
|
|
1316
|
-
@
|
|
1317
|
-
def
|
|
1318
|
-
if self.
|
|
1579
|
+
|
|
1580
|
+
@region.deleter
|
|
1581
|
+
def region(self):
|
|
1582
|
+
if self.region.size:
|
|
1319
1583
|
self.del_current_rect()
|
|
1320
1584
|
self.handler('region_removed', self.frame)
|
|
1321
|
-
|
|
1585
|
+
|
|
1322
1586
|
def get_current_rect(self):
|
|
1323
1587
|
"""Currently selected region."""
|
|
1324
1588
|
if self.__rectsel:
|
|
1325
1589
|
x, y = self.rected.get_data(orig=0)
|
|
1326
1590
|
return np.array((x, y))
|
|
1327
|
-
|
|
1591
|
+
|
|
1328
1592
|
def set_current_rect(self, x, y):
|
|
1329
1593
|
if len(x) == 2:
|
|
1330
1594
|
(xa,xb), (ya,yb) = x, y
|
|
@@ -1333,9 +1597,9 @@ class GraphPlot(MatplotPanel):
|
|
|
1333
1597
|
l,r,b,t = self.frame.get_extent()
|
|
1334
1598
|
xa, xb = min(x), max(x)
|
|
1335
1599
|
ya, yb = min(y), max(y)
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
## Modify range so that it does not exceed the extent
|
|
1600
|
+
# if (xa < l or xb > r) or (ya < b or yb > t):
|
|
1601
|
+
# return
|
|
1602
|
+
## Modify range so that it does not exceed the extent.
|
|
1339
1603
|
w, h = xb-xa, yb-ya
|
|
1340
1604
|
if xa < l: xa, xb = l, l+w
|
|
1341
1605
|
if xb > r: xa, xb = r-w, r
|
|
@@ -1346,16 +1610,16 @@ class GraphPlot(MatplotPanel):
|
|
|
1346
1610
|
self.rected.set_data(x, y)
|
|
1347
1611
|
self.rected.set_visible(1)
|
|
1348
1612
|
self.update_art_of_region()
|
|
1349
|
-
|
|
1613
|
+
|
|
1350
1614
|
def del_current_rect(self):
|
|
1351
1615
|
self.__rectsel = []
|
|
1352
1616
|
self.rected.set_data([], [])
|
|
1353
1617
|
self.rected.set_visible(0)
|
|
1354
1618
|
self.update_art_of_region()
|
|
1355
|
-
|
|
1619
|
+
|
|
1356
1620
|
def update_art_of_region(self, *args):
|
|
1357
1621
|
if args:
|
|
1358
|
-
art = self.__rectarts
|
|
1622
|
+
art = self.__rectarts # art の再描画処理をして終了
|
|
1359
1623
|
art.xy = args
|
|
1360
1624
|
self.draw(self.rected)
|
|
1361
1625
|
return
|
|
@@ -1367,15 +1631,15 @@ class GraphPlot(MatplotPanel):
|
|
|
1367
1631
|
if x.size:
|
|
1368
1632
|
self.__rectarts.append(
|
|
1369
1633
|
self.axes.add_patch(
|
|
1370
|
-
patches.Polygon(list(zip(x,y)),
|
|
1634
|
+
patches.Polygon(list(zip(x, y)),
|
|
1371
1635
|
color='red', ls='solid', lw=1/2, ec='white', alpha=0.2)
|
|
1372
1636
|
)
|
|
1373
1637
|
)
|
|
1374
1638
|
self.trace_point(x, y, type=REGION)
|
|
1375
1639
|
self.draw(self.rected)
|
|
1376
|
-
|
|
1640
|
+
|
|
1377
1641
|
def OnRegionAppend(self, evt):
|
|
1378
|
-
xs, ys = self.
|
|
1642
|
+
xs, ys = self.selector
|
|
1379
1643
|
if len(xs) > 0 and self.frame:
|
|
1380
1644
|
ux, uy = self.frame.xy_unit
|
|
1381
1645
|
xs = (xs.min()-ux/2, xs.max()+ux/2)
|
|
@@ -1383,44 +1647,44 @@ class GraphPlot(MatplotPanel):
|
|
|
1383
1647
|
self.set_current_rect(xs, ys)
|
|
1384
1648
|
self.update_art_of_region()
|
|
1385
1649
|
self.handler('region_drawn', self.frame)
|
|
1386
|
-
|
|
1650
|
+
|
|
1387
1651
|
def OnRegionRemove(self, evt):
|
|
1388
1652
|
if self.__rectsel:
|
|
1389
1653
|
self.del_current_rect()
|
|
1390
1654
|
self.handler('region_removed', self.frame)
|
|
1391
1655
|
self.set_wxcursor(wx.CURSOR_ARROW)
|
|
1392
|
-
|
|
1393
|
-
def OnRegionSelected(self, evt):
|
|
1656
|
+
|
|
1657
|
+
def OnRegionSelected(self, evt): #<matplotlib.backend_bases.PickEvent>
|
|
1394
1658
|
k = evt.ind[0]
|
|
1395
1659
|
x = evt.mouseevent.xdata
|
|
1396
1660
|
y = evt.mouseevent.ydata
|
|
1397
1661
|
xs, ys = evt.artist.get_data(orig=0)
|
|
1398
1662
|
dots = np.hypot(x-xs[k], y-ys[k]) * self.ddpu[0]
|
|
1399
|
-
self.__rectsel = [k] if dots < 8 else [0,1,2,3,4]
|
|
1663
|
+
self.__rectsel = [k] if dots < 8 else [0,1,2,3,4] # リージョンの全選択
|
|
1400
1664
|
self.update_art_of_region()
|
|
1401
|
-
|
|
1402
|
-
def OnRegionDeselected(self, evt):
|
|
1665
|
+
|
|
1666
|
+
def OnRegionDeselected(self, evt): #<matplotlib.backend_bases.PickEvent>
|
|
1403
1667
|
self.__rectsel = []
|
|
1404
1668
|
self.update_art_of_region()
|
|
1405
1669
|
self.set_wxcursor(wx.CURSOR_ARROW)
|
|
1406
|
-
|
|
1670
|
+
|
|
1407
1671
|
def OnRegionDragBegin(self, evt):
|
|
1408
1672
|
if not self.frame or self._inaxes(evt):
|
|
1409
1673
|
self.handler('quit', evt)
|
|
1410
1674
|
return
|
|
1411
|
-
org = self.p_event
|
|
1675
|
+
org = self.p_event # the last pressed
|
|
1412
1676
|
self.__lastpoint = self.calc_point(org.xdata, org.ydata, centred=False)
|
|
1413
1677
|
if not self.__rectsel:
|
|
1414
1678
|
x, y = self.__lastpoint
|
|
1415
|
-
self.set_current_rect((x,x), (y,y))
|
|
1679
|
+
self.set_current_rect((x, x), (y, y)) # start new region
|
|
1416
1680
|
self.__orgpoints = self.get_current_rect()
|
|
1417
|
-
|
|
1681
|
+
|
|
1418
1682
|
def OnRegionDragMove(self, evt, shift=False, meta=False):
|
|
1419
1683
|
x, y = self.calc_point(evt.xdata, evt.ydata, centred=False)
|
|
1420
1684
|
xs, ys = self.get_current_rect()
|
|
1421
|
-
j = self.__rectsel
|
|
1685
|
+
j = self.__rectsel # corner-drag[1] or region-drag[4]
|
|
1422
1686
|
if len(j) == 1:
|
|
1423
|
-
k = (j[0] + 2) % 4
|
|
1687
|
+
k = (j[0] + 2) % 4 # 選択された一点の対角点
|
|
1424
1688
|
xo, yo = xs[k], ys[k]
|
|
1425
1689
|
if shift:
|
|
1426
1690
|
x, y = self.calc_shiftpoint(xo, yo, x, y, centred=False)
|
|
@@ -1431,7 +1695,7 @@ class GraphPlot(MatplotPanel):
|
|
|
1431
1695
|
i = np.searchsorted(nn, n)
|
|
1432
1696
|
x = xo + nn[i] * np.sign(x-xo) * ux
|
|
1433
1697
|
y = yo + nn[i] * np.sign(y-yo) * uy
|
|
1434
|
-
self.set_current_rect((xo,x), (yo,y))
|
|
1698
|
+
self.set_current_rect((xo, x), (yo, y))
|
|
1435
1699
|
else:
|
|
1436
1700
|
xc, yc = self.__lastpoint
|
|
1437
1701
|
xo, yo = self.__orgpoints
|
|
@@ -1439,21 +1703,21 @@ class GraphPlot(MatplotPanel):
|
|
|
1439
1703
|
ys = yo + (y - yc)
|
|
1440
1704
|
self.set_current_rect(xs, ys)
|
|
1441
1705
|
self.handler('region_draw', self.frame)
|
|
1442
|
-
|
|
1706
|
+
|
|
1443
1707
|
def OnRegionDragShiftMove(self, evt):
|
|
1444
1708
|
self.OnRegionDragMove(evt, shift=True)
|
|
1445
|
-
|
|
1709
|
+
|
|
1446
1710
|
def OnRegionDragMetaMove(self, evt):
|
|
1447
1711
|
self.OnRegionDragMove(evt, meta=True)
|
|
1448
|
-
|
|
1712
|
+
|
|
1449
1713
|
def OnRegionDragEscape(self, evt):
|
|
1450
1714
|
self.set_current_rect(*self.__orgpoints)
|
|
1451
1715
|
self.handler('region_drawn', self.frame)
|
|
1452
|
-
|
|
1716
|
+
|
|
1453
1717
|
def OnRegionDragEnd(self, evt):
|
|
1454
|
-
|
|
1718
|
+
# self.__rectsel = [0,1,2,3,4] # リージョンの全選択
|
|
1455
1719
|
self.handler('region_drawn', self.frame)
|
|
1456
|
-
|
|
1720
|
+
|
|
1457
1721
|
def OnRegionShift(self, evt):
|
|
1458
1722
|
j = self.__rectsel
|
|
1459
1723
|
if j and self.frame:
|
|
@@ -1467,22 +1731,22 @@ class GraphPlot(MatplotPanel):
|
|
|
1467
1731
|
dp = du[evt.key]
|
|
1468
1732
|
p = self.get_current_rect().T
|
|
1469
1733
|
if len(j) == 1:
|
|
1470
|
-
i = j[0]
|
|
1471
|
-
k = (i + 2) % 4
|
|
1734
|
+
i = j[0] # 選択されている点
|
|
1735
|
+
k = (i + 2) % 4 # 選択された一点の対角点
|
|
1472
1736
|
p[i] += dp
|
|
1473
1737
|
self.set_current_rect(*p[[k,i]].T)
|
|
1474
1738
|
else:
|
|
1475
1739
|
p += dp
|
|
1476
1740
|
self.set_current_rect(*p.T)
|
|
1477
1741
|
self.handler('region_draw', self.frame)
|
|
1478
|
-
|
|
1742
|
+
|
|
1479
1743
|
def OnRegionShiftEnd(self, evt):
|
|
1480
1744
|
self.handler('region_drawn', self.frame)
|
|
1481
|
-
|
|
1745
|
+
|
|
1482
1746
|
def OnRegionMotion(self, evt):
|
|
1483
1747
|
x, y = evt.xdata, evt.ydata
|
|
1484
|
-
if self.
|
|
1485
|
-
(l,r), (b,t) = self.
|
|
1748
|
+
if self.region.size:
|
|
1749
|
+
(l,r), (b,t) = self.region
|
|
1486
1750
|
d = self.rected.pickradius / self.ddpu[0]
|
|
1487
1751
|
x0 = l+d < x < r-d
|
|
1488
1752
|
y0 = b+d < y < t-d
|
|
@@ -1491,198 +1755,17 @@ class GraphPlot(MatplotPanel):
|
|
|
1491
1755
|
y1 = b-d < y < b+d
|
|
1492
1756
|
y2 = t-d < y < t+d
|
|
1493
1757
|
if x0 and y0:
|
|
1494
|
-
|
|
1758
|
+
# self.set_wxcursor(wx.CURSOR_HAND) # insdie
|
|
1495
1759
|
self.set_wxcursor(wx.CURSOR_ARROW)
|
|
1496
1760
|
elif (x1 or x2) and y0:
|
|
1497
|
-
|
|
1761
|
+
# self.set_wxcursor(wx.CURSOR_SIZEWE) # on-x-edge
|
|
1498
1762
|
self.set_wxcursor(wx.CURSOR_SIZING)
|
|
1499
1763
|
elif x0 and (y1 or y2):
|
|
1500
|
-
|
|
1764
|
+
# self.set_wxcursor(wx.CURSOR_SIZENS) # on-y-edge
|
|
1501
1765
|
self.set_wxcursor(wx.CURSOR_SIZING)
|
|
1502
1766
|
elif x1 and y1 or x2 and y2:
|
|
1503
|
-
self.set_wxcursor(wx.CURSOR_SIZENESW)
|
|
1767
|
+
self.set_wxcursor(wx.CURSOR_SIZENESW) # on-NE/SW-corner
|
|
1504
1768
|
elif x1 and y2 or x2 and y1:
|
|
1505
|
-
self.set_wxcursor(wx.CURSOR_SIZENWSE)
|
|
1769
|
+
self.set_wxcursor(wx.CURSOR_SIZENWSE) # on-NW/SE-corner
|
|
1506
1770
|
else:
|
|
1507
|
-
self.set_wxcursor(wx.CURSOR_ARROW)
|
|
1508
|
-
|
|
1509
|
-
## --------------------------------
|
|
1510
|
-
## Markers interface
|
|
1511
|
-
## --------------------------------
|
|
1512
|
-
|
|
1513
|
-
#: Limit number of markers to display 最大(表示)数を制限する
|
|
1514
|
-
maxnum_markers = 1000
|
|
1515
|
-
|
|
1516
|
-
@property
|
|
1517
|
-
def Markers(self):
|
|
1518
|
-
"""Marked points data array [[x],[y]]."""
|
|
1519
|
-
xm, ym = self.marked.get_data(orig=0)
|
|
1520
|
-
return np.array((xm, ym))
|
|
1521
|
-
|
|
1522
|
-
@Markers.setter
|
|
1523
|
-
def Markers(self, v):
|
|
1524
|
-
x, y = v
|
|
1525
|
-
if not hasattr(x, '__iter__'):
|
|
1526
|
-
x, y = [x], [y]
|
|
1527
|
-
elif len(x) > self.maxnum_markers:
|
|
1528
|
-
self.message("- Got too many markers ({}) to plot".format(len(x)))
|
|
1529
|
-
return
|
|
1530
|
-
self.marked.set_data(x, y)
|
|
1531
|
-
self.__marksel = []
|
|
1532
|
-
self.update_art_of_mark()
|
|
1533
|
-
self.handler('mark_drawn', self.frame)
|
|
1534
|
-
|
|
1535
|
-
@Markers.deleter
|
|
1536
|
-
def Markers(self):
|
|
1537
|
-
if self.Markers.size:
|
|
1538
|
-
self.marked.set_data([], [])
|
|
1539
|
-
self.__marksel = []
|
|
1540
|
-
self.update_art_of_mark()
|
|
1541
|
-
self.handler('mark_removed', self.frame)
|
|
1542
|
-
|
|
1543
|
-
def get_current_mark(self):
|
|
1544
|
-
"""Currently selected mark."""
|
|
1545
|
-
xm, ym = self.marked.get_data(orig=0)
|
|
1546
|
-
return np.take((xm, ym), self.__marksel, axis=1)
|
|
1547
|
-
|
|
1548
|
-
def set_current_mark(self, x, y):
|
|
1549
|
-
xm, ym = self.marked.get_data(orig=0)
|
|
1550
|
-
j = self.__marksel
|
|
1551
|
-
if j:
|
|
1552
|
-
xm[j], ym[j] = x, y
|
|
1553
|
-
self.marked.set_data(xm, ym)
|
|
1554
|
-
self.update_art_of_mark(j, xm[j], ym[j])
|
|
1555
|
-
else:
|
|
1556
|
-
n = len(xm)
|
|
1557
|
-
k = len(x) if hasattr(x, '__iter__') else 1
|
|
1558
|
-
self.__marksel = list(range(n, n+k))
|
|
1559
|
-
xm, ym = np.append(xm, x), np.append(ym, y)
|
|
1560
|
-
self.marked.set_data(xm, ym)
|
|
1561
|
-
self.marked.set_visible(1)
|
|
1562
|
-
self.update_art_of_mark()
|
|
1563
|
-
self.Selector = (x, y)
|
|
1564
|
-
|
|
1565
|
-
def del_current_mark(self):
|
|
1566
|
-
j = self.__marksel
|
|
1567
|
-
if j:
|
|
1568
|
-
xm, ym = self.marked.get_data(orig=0)
|
|
1569
|
-
xm, ym = np.delete(xm,j), np.delete(ym,j)
|
|
1570
|
-
self.__marksel = []
|
|
1571
|
-
self.marked.set_data(xm, ym)
|
|
1572
|
-
n = len(xm)
|
|
1573
|
-
self.__marksel = [j[-1] % n] if n > 0 else []
|
|
1574
|
-
self.update_art_of_mark()
|
|
1575
|
-
|
|
1576
|
-
def update_art_of_mark(self, *args):
|
|
1577
|
-
if args:
|
|
1578
|
-
for k,x,y in zip(*args):
|
|
1579
|
-
art = self.__markarts[k] # art の再描画処理をして終了
|
|
1580
|
-
art.xy = x, y
|
|
1581
|
-
self.draw(self.marked)
|
|
1582
|
-
return
|
|
1583
|
-
for art in self.__markarts: # or reset all arts
|
|
1584
|
-
art.remove()
|
|
1585
|
-
self.__markarts = []
|
|
1586
|
-
if self.marked.get_visible() and self.handler.current_state in (MARK, MARK+DRAGGING):
|
|
1587
|
-
N = self.maxnum_markers
|
|
1588
|
-
xm, ym = self.marked.get_data(orig=0)
|
|
1589
|
-
for k, (x,y) in enumerate(zip(xm[:N],ym[:N])):
|
|
1590
|
-
self.__markarts.append(
|
|
1591
|
-
self.axes.annotate(k, #<matplotlib.text.Annotation>
|
|
1592
|
-
xy=(x,y), xycoords='data',
|
|
1593
|
-
xytext=(6,6), textcoords='offset points',
|
|
1594
|
-
bbox=dict(boxstyle="round", fc=(1,1,1,), ec=(1,0,0,)),
|
|
1595
|
-
color='red', size=7, #fontsize=8,
|
|
1596
|
-
)
|
|
1597
|
-
)
|
|
1598
|
-
self.Selector = self.get_current_mark()
|
|
1599
|
-
self.trace_point(*self.Selector, type=MARK)
|
|
1600
|
-
self.draw(self.marked)
|
|
1601
|
-
|
|
1602
|
-
def OnMarkAppend(self, evt):
|
|
1603
|
-
xs, ys = self.Selector
|
|
1604
|
-
if not self.__marksel and len(xs) > 0:
|
|
1605
|
-
self.set_current_mark(xs, ys)
|
|
1606
|
-
self.handler('mark_drawn', self.frame)
|
|
1607
|
-
self.update_art_of_mark()
|
|
1608
|
-
|
|
1609
|
-
def OnMarkRemove(self, evt):
|
|
1610
|
-
if self.__marksel:
|
|
1611
|
-
self.del_current_mark()
|
|
1612
|
-
self.handler('mark_removed', self.frame)
|
|
1613
|
-
|
|
1614
|
-
def OnMarkSelected(self, evt): #<matplotlib.backend_bases.PickEvent>
|
|
1615
|
-
k = evt.ind[0]
|
|
1616
|
-
if evt.mouseevent.key == 'shift': # 多重マーカー選択
|
|
1617
|
-
if k not in self.__marksel:
|
|
1618
|
-
self.__marksel += [k]
|
|
1619
|
-
else:
|
|
1620
|
-
self.__marksel = [k]
|
|
1621
|
-
self.update_art_of_mark()
|
|
1622
|
-
|
|
1623
|
-
if self.Selector.shape[1] > 1:
|
|
1624
|
-
self.handler('line_drawn', self.frame) # 多重マーカー選択時
|
|
1625
|
-
|
|
1626
|
-
def OnMarkDeselected(self, evt): #<matplotlib.backend_bases.PickEvent>
|
|
1627
|
-
self.__marksel = []
|
|
1628
|
-
self.update_art_of_mark()
|
|
1629
|
-
|
|
1630
|
-
def OnMarkDragBegin(self, evt):
|
|
1631
|
-
if not self.frame or self._inaxes(evt):
|
|
1632
|
-
self.handler('quit', evt)
|
|
1633
|
-
return
|
|
1634
|
-
self.__orgpoints = self.get_current_mark()
|
|
1635
|
-
|
|
1636
|
-
def OnMarkDragMove(self, evt):
|
|
1637
|
-
x, y = self.calc_point(evt.xdata, evt.ydata)
|
|
1638
|
-
self.set_current_mark(x, y)
|
|
1639
|
-
self.handler('mark_draw', self.frame)
|
|
1640
|
-
|
|
1641
|
-
def OnMarkDragEscape(self, evt):
|
|
1642
|
-
self.set_current_mark(*self.__orgpoints)
|
|
1643
|
-
self.handler('mark_drawn', self.frame)
|
|
1644
|
-
|
|
1645
|
-
def OnMarkDragEnd(self, evt):
|
|
1646
|
-
self.handler('mark_drawn', self.frame)
|
|
1647
|
-
|
|
1648
|
-
def OnMarkShift(self, evt):
|
|
1649
|
-
j = self.__marksel
|
|
1650
|
-
if j and self.frame:
|
|
1651
|
-
ux, uy = self.frame.xy_unit
|
|
1652
|
-
du = {
|
|
1653
|
-
'up' : ( 0., uy),
|
|
1654
|
-
'down' : ( 0.,-uy),
|
|
1655
|
-
'left' : (-ux, 0.),
|
|
1656
|
-
'right' : ( ux, 0.),
|
|
1657
|
-
}
|
|
1658
|
-
p = self.get_current_mark() + np.resize(du[evt.key], (2,1))
|
|
1659
|
-
self.set_current_mark(*p)
|
|
1660
|
-
self.handler('mark_draw', self.frame)
|
|
1661
|
-
|
|
1662
|
-
def OnMarkShiftEnd(self, evt):
|
|
1663
|
-
self.handler('mark_drawn', self.frame)
|
|
1664
|
-
|
|
1665
|
-
def next_mark(self, j):
|
|
1666
|
-
self.__marksel = [j]
|
|
1667
|
-
xs, ys = self.get_current_mark()
|
|
1668
|
-
self.xlim += xs[-1] - (self.xlim[1] + self.xlim[0]) / 2
|
|
1669
|
-
self.ylim += ys[-1] - (self.ylim[1] + self.ylim[0]) / 2
|
|
1670
|
-
self.Selector = (xs, ys)
|
|
1671
|
-
self.trace_point(xs, ys, type=MARK)
|
|
1672
|
-
self.draw()
|
|
1673
|
-
|
|
1674
|
-
def OnMarkSkipNext(self, evt):
|
|
1675
|
-
n = self.Markers.shape[1]
|
|
1676
|
-
j = self.__marksel
|
|
1677
|
-
if j:
|
|
1678
|
-
self.next_mark((j[-1]+1) % n)
|
|
1679
|
-
elif n:
|
|
1680
|
-
self.next_mark(0)
|
|
1681
|
-
|
|
1682
|
-
def OnMarkSkipPrevious(self, evt):
|
|
1683
|
-
n = self.Markers.shape[1]
|
|
1684
|
-
j = self.__marksel
|
|
1685
|
-
if j:
|
|
1686
|
-
self.next_mark((j[-1]-1) % n)
|
|
1687
|
-
elif n:
|
|
1688
|
-
self.next_mark(-1)
|
|
1771
|
+
self.set_wxcursor(wx.CURSOR_ARROW) # outside
|