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/plugins/ffmpeg_view.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#! python3
|
|
2
2
|
"""FFmpeg wrapper.
|
|
3
3
|
"""
|
|
4
|
-
from functools import partial
|
|
5
4
|
from subprocess import Popen, PIPE
|
|
6
5
|
import numpy as np
|
|
7
6
|
import os
|
|
@@ -10,7 +9,7 @@ import wx.media
|
|
|
10
9
|
|
|
11
10
|
from mwx.framework import _F, hotkey
|
|
12
11
|
from mwx.graphman import Layer
|
|
13
|
-
from mwx.controls import LParam, Icon, Button,
|
|
12
|
+
from mwx.controls import LParam, Icon, Button, TextBox
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
def read_info(path):
|
|
@@ -35,8 +34,8 @@ def capture_video(path, ss=0):
|
|
|
35
34
|
'-pix_fmt', 'rgb24', # rgb24, gray, etc.
|
|
36
35
|
'pipe:' # pipe to stdout: '-'
|
|
37
36
|
]
|
|
38
|
-
bufsize = 4096
|
|
39
|
-
buf = b""
|
|
37
|
+
bufsize = 4096 # w * h * 3
|
|
38
|
+
buf = b""
|
|
40
39
|
with Popen(command, stdout=PIPE) as fp:
|
|
41
40
|
while 1:
|
|
42
41
|
s = fp.stdout.read(bufsize)
|
|
@@ -63,9 +62,9 @@ class MyFileDropLoader(wx.FileDropTarget):
|
|
|
63
62
|
def __init__(self, target):
|
|
64
63
|
wx.FileDropTarget.__init__(self)
|
|
65
64
|
self.target = target
|
|
66
|
-
|
|
65
|
+
|
|
67
66
|
def OnDropFiles(self, x, y, filenames):
|
|
68
|
-
path = filenames[-1]
|
|
67
|
+
path = filenames[-1] # Only the last one will be loaded.
|
|
69
68
|
if len(filenames) > 1:
|
|
70
69
|
print("- Drop only one file please."
|
|
71
70
|
"Loading {!r} ...".format(path))
|
|
@@ -77,15 +76,14 @@ class Plugin(Layer):
|
|
|
77
76
|
"""Media loader using FFMpeg (installation required).
|
|
78
77
|
"""
|
|
79
78
|
menukey = "Plugins/Extensions/FFMpeg viewer"
|
|
80
|
-
## menukey = "FFMpeg/"
|
|
81
79
|
dockable = False
|
|
82
|
-
|
|
80
|
+
|
|
83
81
|
def Init(self):
|
|
84
82
|
self.mc = wx.media.MediaCtrl()
|
|
85
83
|
self.mc.Create(self, size=(300,300),
|
|
86
84
|
style=wx.SIMPLE_BORDER,
|
|
87
85
|
szBackend=wx.media.MEDIABACKEND_WMP10
|
|
88
|
-
|
|
86
|
+
# szBackend=wx.media.MEDIABACKEND_DIRECTSHOW
|
|
89
87
|
)
|
|
90
88
|
self.mc.ShowPlayerControls()
|
|
91
89
|
self.mc.Bind(wx.media.EVT_MEDIA_LOADED, self.OnMediaLoaded)
|
|
@@ -94,15 +92,15 @@ class Plugin(Layer):
|
|
|
94
92
|
|
|
95
93
|
self._path = None
|
|
96
94
|
|
|
97
|
-
self.ss = LParam("ss:",
|
|
95
|
+
self.ss = LParam("ss:", # range/value will be set when loaded later.
|
|
98
96
|
handler=self.set_offset,
|
|
99
97
|
updater=self.get_offset,
|
|
100
98
|
)
|
|
101
|
-
self.to = LParam("to:",
|
|
99
|
+
self.to = LParam("to:", # range/value will be set when loaded later.
|
|
102
100
|
handler=self.set_offset,
|
|
103
101
|
updater=self.get_offset,
|
|
104
102
|
)
|
|
105
|
-
self.crop =
|
|
103
|
+
self.crop = TextBox(self, icon="cut", size=(130,-1),
|
|
106
104
|
handler=self.set_crop,
|
|
107
105
|
updater=self.get_crop,
|
|
108
106
|
)
|
|
@@ -110,13 +108,13 @@ class Plugin(Layer):
|
|
|
110
108
|
self.snp = Button(self, handler=self.snapshot, icon='clip')
|
|
111
109
|
self.exp = Button(self, handler=self.export, icon='save')
|
|
112
110
|
|
|
113
|
-
self.rw = Button(self, handler=lambda v: self.
|
|
114
|
-
self.fw = Button(self, handler=lambda v: self.
|
|
111
|
+
self.rw = Button(self, handler=lambda v: self.seekto(-100), icon='|<-')
|
|
112
|
+
self.fw = Button(self, handler=lambda v: self.seekto(+100), icon='->|')
|
|
115
113
|
|
|
116
114
|
self.layout((self.mc,), expand=2)
|
|
117
115
|
self.layout((self.ss, self.to, self.rw, self.fw,
|
|
118
116
|
self.snp, self.crop, self.exp),
|
|
119
|
-
expand=0, row=7, style='button', lw=
|
|
117
|
+
expand=0, row=7, style='button', lw=28, cw=0, tw=64)
|
|
120
118
|
|
|
121
119
|
self.menu[0:5] = [
|
|
122
120
|
(1, "&Load file", Icon('open'),
|
|
@@ -131,54 +129,52 @@ class Plugin(Layer):
|
|
|
131
129
|
self.parent.handler.bind("unknown_format", self.load_media)
|
|
132
130
|
|
|
133
131
|
self.handler.update({ # DNA<ffmpeg_viewer>
|
|
134
|
-
0 : {
|
|
135
|
-
'play' : (
|
|
136
|
-
'space pressed' : (
|
|
132
|
+
0 : { # MEDIASTATE_STOPPED
|
|
133
|
+
'play' : (2, ),
|
|
134
|
+
'space pressed' : (2, _F(self.mc.Play)),
|
|
135
|
+
'left pressed' : (0, _F(self.seekd, -1000)),
|
|
136
|
+
'right pressed' : (0, _F(self.seekd, 1000)),
|
|
137
|
+
},
|
|
138
|
+
1 : { # MEDIASTATE_PAUSED
|
|
139
|
+
'stop' : (0, ),
|
|
140
|
+
'space pressed' : (2, _F(self.mc.Play)),
|
|
141
|
+
'left pressed' : (1, _F(self.seekd, -1000)),
|
|
142
|
+
'right pressed' : (1, _F(self.seekd, 1000)),
|
|
137
143
|
},
|
|
138
|
-
|
|
144
|
+
2 : { # MEDIASTATE_PLAYING
|
|
139
145
|
'stop' : (0, ),
|
|
140
|
-
'pause' : (
|
|
146
|
+
'pause' : (1, ),
|
|
141
147
|
'space pressed' : (1, _F(self.mc.Pause)),
|
|
148
|
+
'left pressed' : (2, _F(self.seekd, -1000)),
|
|
149
|
+
'right pressed' : (2, _F(self.seekd, 1000)),
|
|
142
150
|
},
|
|
143
151
|
})
|
|
144
152
|
|
|
145
|
-
self.mc.Bind(wx.media.EVT_MEDIA_PAUSE,
|
|
146
|
-
self.mc.Bind(wx.media.EVT_MEDIA_PLAY,
|
|
147
|
-
self.mc.Bind(wx.media.EVT_MEDIA_STOP,
|
|
148
|
-
|
|
149
|
-
## self.mc.Bind(wx.EVT_KEY_DOWN, self.on_hotkey_down)
|
|
150
|
-
## self.mc.Bind(wx.EVT_KEY_UP, self.on_hotkey_up)
|
|
151
|
-
self.mc.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
|
|
152
|
-
self.mc.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
|
|
153
|
+
self.mc.Bind(wx.media.EVT_MEDIA_PAUSE, lambda v: self.handler('pause', v))
|
|
154
|
+
self.mc.Bind(wx.media.EVT_MEDIA_PLAY, lambda v: self.handler('play', v))
|
|
155
|
+
self.mc.Bind(wx.media.EVT_MEDIA_STOP, lambda v: self.handler('stop', v))
|
|
153
156
|
|
|
154
|
-
self.Bind(wx.
|
|
155
|
-
|
|
157
|
+
self.mc.Bind(wx.EVT_KEY_DOWN, self.on_hotkey_down)
|
|
158
|
+
self.mc.Bind(wx.EVT_KEY_UP, self.on_hotkey_up)
|
|
159
|
+
|
|
156
160
|
def Destroy(self):
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def OnKeyDown(self, evt):
|
|
163
|
-
if self.handler('{} pressed'.format(hotkey(evt)), evt) is None:
|
|
164
|
-
evt.Skip()
|
|
165
|
-
|
|
166
|
-
def OnKeyUp(self, evt):
|
|
167
|
-
if self.handler('{} released'.format(hotkey(evt)), evt) is None:
|
|
168
|
-
evt.Skip()
|
|
169
|
-
|
|
161
|
+
self.parent.handler.unbind("unknown_format", self.load_media)
|
|
162
|
+
if self.mc:
|
|
163
|
+
self.mc.Destroy()
|
|
164
|
+
return Layer.Destroy(self)
|
|
165
|
+
|
|
170
166
|
def OnShow(self, evt):
|
|
171
167
|
if not evt.IsShown():
|
|
172
168
|
if self.mc:
|
|
173
169
|
self.mc.Stop()
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
Layer.OnShow(self, evt)
|
|
171
|
+
|
|
176
172
|
def OnMediaLoaded(self, evt):
|
|
177
173
|
self.ss.range = (0, self.video_dur, 0.01)
|
|
178
174
|
self.to.range = (0, self.video_dur, 0.01)
|
|
179
175
|
self.Show()
|
|
180
176
|
evt.Skip()
|
|
181
|
-
|
|
177
|
+
|
|
182
178
|
def load_media(self, path=None):
|
|
183
179
|
if path is None:
|
|
184
180
|
with wx.FileDialog(self, "Choose a media file",
|
|
@@ -186,11 +182,11 @@ class Plugin(Layer):
|
|
|
186
182
|
if dlg.ShowModal() != wx.ID_OK:
|
|
187
183
|
return None
|
|
188
184
|
path = dlg.Path
|
|
189
|
-
self.mc.Load(path)
|
|
185
|
+
self.mc.Load(path) # -> True (always)
|
|
190
186
|
self.info = read_info(path)
|
|
191
187
|
if self.info:
|
|
192
188
|
v = next(x for x in self.info['streams'] if x['codec_type'] == 'video')
|
|
193
|
-
|
|
189
|
+
# self.video_fps = eval(v['r_frame_rate']) # real base frame rate
|
|
194
190
|
self.video_fps = eval(v['avg_frame_rate']) # averaged frame rate
|
|
195
191
|
self.video_dur = eval(v['duration']) # duration [s]
|
|
196
192
|
w, h = v['width'], v['height']
|
|
@@ -206,25 +202,23 @@ class Plugin(Layer):
|
|
|
206
202
|
else:
|
|
207
203
|
self.message(f"Failed to load file {path!r}.")
|
|
208
204
|
return False
|
|
209
|
-
|
|
210
|
-
DELTA = 1000
|
|
211
|
-
|
|
205
|
+
|
|
206
|
+
DELTA = 1000 # correction ▲理由は不明 (WMP10 backend only?)
|
|
207
|
+
|
|
212
208
|
def set_offset(self, tc):
|
|
213
209
|
"""Set offset value by referring to ss/to value."""
|
|
214
|
-
|
|
210
|
+
if self._path:
|
|
215
211
|
self.mc.Seek(self.DELTA + int(tc.value * 1000))
|
|
216
|
-
|
|
217
|
-
pass
|
|
218
|
-
|
|
212
|
+
|
|
219
213
|
def get_offset(self, tc):
|
|
220
214
|
"""Get offset value and assigns it to ss/to value."""
|
|
221
|
-
|
|
215
|
+
if self._path:
|
|
222
216
|
tc.value = round(self.mc.Tell()) / 1000
|
|
223
|
-
|
|
224
|
-
pass
|
|
225
|
-
|
|
217
|
+
|
|
226
218
|
def set_crop(self):
|
|
227
|
-
"""Set crop area (W:H:Left:Top) to
|
|
219
|
+
"""Set crop area (W:H:Left:Top) to ROI."""
|
|
220
|
+
if not self._path:
|
|
221
|
+
return
|
|
228
222
|
frame = self.graph.frame
|
|
229
223
|
if frame:
|
|
230
224
|
try:
|
|
@@ -236,9 +230,11 @@ class Plugin(Layer):
|
|
|
236
230
|
frame.region = frame.xyfrompixel(nx, ny)
|
|
237
231
|
except Exception:
|
|
238
232
|
self.message("Failed to evaluate crop text.")
|
|
239
|
-
|
|
233
|
+
|
|
240
234
|
def get_crop(self):
|
|
241
|
-
"""Get crop area (W:H:Left:Top) from
|
|
235
|
+
"""Get crop area (W:H:Left:Top) from ROI."""
|
|
236
|
+
if not self._path:
|
|
237
|
+
return
|
|
242
238
|
crop = ''
|
|
243
239
|
frame = self.graph.frame
|
|
244
240
|
if frame:
|
|
@@ -247,23 +243,25 @@ class Plugin(Layer):
|
|
|
247
243
|
xo, yo = nx[0], ny[1]
|
|
248
244
|
xp, yp = nx[1], ny[0]
|
|
249
245
|
crop = "{}:{}:{}:{}".format(xp-xo, yp-yo, xo, yo)
|
|
250
|
-
if
|
|
246
|
+
if not crop:
|
|
251
247
|
crop = "{}:{}:0:0".format(*self.video_size)
|
|
252
248
|
self.crop.Value = crop
|
|
253
|
-
|
|
254
|
-
def
|
|
255
|
-
"""Seek
|
|
256
|
-
if
|
|
257
|
-
offset /= 10
|
|
258
|
-
try:
|
|
249
|
+
|
|
250
|
+
def seekto(self, offset):
|
|
251
|
+
"""Seek position with offset [ms] from the `to` position."""
|
|
252
|
+
if self._path:
|
|
259
253
|
t = self.to.value + offset/1000
|
|
260
|
-
|
|
261
|
-
print(e)
|
|
262
|
-
else:
|
|
263
|
-
if self._path and 0 <= t < self.video_dur:
|
|
254
|
+
if 0 <= t < self.video_dur:
|
|
264
255
|
self.to.value = round(t, 3)
|
|
265
|
-
self.set_offset(self.to)
|
|
266
|
-
|
|
256
|
+
self.set_offset(self.to)
|
|
257
|
+
|
|
258
|
+
def seekd(self, offset):
|
|
259
|
+
"""Seek position with offset [ms] from the current position."""
|
|
260
|
+
if self._path:
|
|
261
|
+
t = self.mc.Tell() + offset
|
|
262
|
+
if 0 <= t < self.video_dur * 1000:
|
|
263
|
+
self.mc.Seek(self.DELTA + t)
|
|
264
|
+
|
|
267
265
|
def snapshot(self):
|
|
268
266
|
"""Create a snapshot of the current frame.
|
|
269
267
|
Load the snapshot image into the graph window.
|
|
@@ -275,7 +273,7 @@ class Plugin(Layer):
|
|
|
275
273
|
buf = capture_video(self._path, t/1000).reshape((h,w,3))
|
|
276
274
|
name = "{}-ss{}".format(os.path.basename(self._path), int(t))
|
|
277
275
|
self.graph.load(buf, name)
|
|
278
|
-
|
|
276
|
+
|
|
279
277
|
def export(self):
|
|
280
278
|
"""Export the cropped / clipped data to a media file."""
|
|
281
279
|
if not self._path:
|
mwx/plugins/fft_view.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"""
|
|
4
4
|
import wx
|
|
5
5
|
import numpy as np
|
|
6
|
-
from numpy.fft import fft2,ifft2,fftshift,ifftshift
|
|
6
|
+
from numpy.fft import fft2, ifft2, fftshift, ifftshift
|
|
7
7
|
|
|
8
8
|
from mwx.graphman import Layer
|
|
9
9
|
from mwx.controls import Param
|
|
@@ -13,7 +13,7 @@ def fftcrop(src):
|
|
|
13
13
|
"""Crop src image in 2**N square ROI centered at (x, y)."""
|
|
14
14
|
h, w = src.shape
|
|
15
15
|
m = min(h, w)
|
|
16
|
-
n = 1 if m < 2 else 2 ** int(np.log2(m) - 1)
|
|
16
|
+
n = 1 if m < 2 else 2 ** int(np.log2(m) - 1) # +-m/2
|
|
17
17
|
x, y = w//2, h//2
|
|
18
18
|
return src[y-n:y+n, x-n:x+n]
|
|
19
19
|
|
|
@@ -26,32 +26,32 @@ class Plugin(Layer):
|
|
|
26
26
|
Rectangular regions will result in distorted patterns.
|
|
27
27
|
長方形のリージョンは歪んだパターンになるので要注意
|
|
28
28
|
"""
|
|
29
|
-
menukey = "Plugins/Extensions/&FFT view"
|
|
29
|
+
menukey = "Plugins/Extensions/&FFT view\tAlt+f"
|
|
30
30
|
caption = "FFT view"
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
def Init(self):
|
|
33
33
|
self.pchk = wx.CheckBox(self, label="logical unit")
|
|
34
34
|
self.pchk.Value = True
|
|
35
35
|
|
|
36
|
-
self.ftor = Param("mask", (2,4,8,16,32,64))
|
|
36
|
+
self.ftor = Param("mask", (2,4,8,16,32,64)) # masking area factor of 1/2
|
|
37
37
|
|
|
38
38
|
self.layout((self.pchk,), title="normal FFT")
|
|
39
39
|
self.layout((self.ftor,), title="inverse FFT", style='chkbox', tw=32)
|
|
40
40
|
|
|
41
41
|
self.parent.define_key('C-f', self.newfft)
|
|
42
42
|
self.parent.define_key('C-S-f', self.newifft)
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
def Destroy(self):
|
|
45
45
|
self.parent.undefine_key('C-f')
|
|
46
46
|
self.parent.undefine_key('C-S-f')
|
|
47
47
|
return Layer.Destroy(self)
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
def newfft(self):
|
|
50
50
|
"""New FFT of graph to output."""
|
|
51
51
|
frame = self.graph.frame
|
|
52
52
|
if frame:
|
|
53
53
|
self.message("FFT execution...")
|
|
54
|
-
src = fftcrop(frame.
|
|
54
|
+
src = fftcrop(frame.roi_or_buffer)
|
|
55
55
|
h, w = src.shape
|
|
56
56
|
|
|
57
57
|
dst = fftshift(fft2(src))
|
|
@@ -60,27 +60,25 @@ class Plugin(Layer):
|
|
|
60
60
|
u = 1 / w
|
|
61
61
|
if self.pchk.Value:
|
|
62
62
|
u /= frame.unit
|
|
63
|
-
self.output.load(dst, "*fft of {}*"
|
|
64
|
-
localunit=u)
|
|
63
|
+
self.output.load(dst, f"*fft of {frame.name}*", localunit=u)
|
|
65
64
|
self.message("\b done")
|
|
66
|
-
|
|
65
|
+
|
|
67
66
|
def newifft(self):
|
|
68
67
|
"""New inverse FFT of output to graph."""
|
|
69
68
|
frame = self.output.frame
|
|
70
69
|
if frame:
|
|
71
70
|
self.message("iFFT execution...")
|
|
72
|
-
src = frame.
|
|
71
|
+
src = frame.buffer # Don't crop fft region
|
|
73
72
|
h, w = src.shape
|
|
74
73
|
|
|
75
74
|
if self.ftor.check:
|
|
76
75
|
y, x = np.ogrid[-h/2:h/2, -w/2:w/2]
|
|
77
|
-
mask = np.hypot(y,x) > w / self.ftor.value
|
|
78
|
-
src = src.copy()
|
|
76
|
+
mask = np.hypot(y, x) > w / self.ftor.value
|
|
77
|
+
src = src.copy() # apply mask to the copy
|
|
79
78
|
src[mask] = 0
|
|
80
79
|
|
|
81
80
|
dst = ifft2(ifftshift(src))
|
|
82
81
|
|
|
83
82
|
self.message("\b Loading image...")
|
|
84
|
-
self.graph.load(dst.real, "*ifft of {}*"
|
|
85
|
-
localunit=1/w/frame.unit)
|
|
83
|
+
self.graph.load(dst.real, f"*ifft of {frame.name}*", localunit=1/w/frame.unit)
|
|
86
84
|
self.message("\b done")
|
mwx/plugins/frame_listview.py
CHANGED
|
@@ -21,23 +21,23 @@ class CheckList(wx.ListCtrl, ListCtrlAutoWidthMixin, CtrlInterface):
|
|
|
21
21
|
@property
|
|
22
22
|
def selected_items(self):
|
|
23
23
|
return filter(self.IsSelected, range(self.ItemCount))
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
@property
|
|
26
26
|
def checked_items(self):
|
|
27
27
|
return filter(self.IsItemChecked, range(self.ItemCount))
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
@property
|
|
30
30
|
def focused_item(self):
|
|
31
31
|
return self.FocusedItem
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
@property
|
|
34
34
|
def all_items(self):
|
|
35
35
|
rows = range(self.ItemCount)
|
|
36
36
|
cols = range(self.ColumnCount)
|
|
37
|
-
|
|
37
|
+
# return [[self.GetItemText(j, k) for k in cols] for j in rows]
|
|
38
38
|
for j in rows:
|
|
39
39
|
yield [self.GetItemText(j, k) for k in cols]
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
def __init__(self, parent, target, **kwargs):
|
|
42
42
|
wx.ListCtrl.__init__(self, parent, size=(400,130),
|
|
43
43
|
style=wx.LC_REPORT|wx.LC_HRULES, **kwargs)
|
|
@@ -52,7 +52,7 @@ class CheckList(wx.ListCtrl, ListCtrlAutoWidthMixin, CtrlInterface):
|
|
|
52
52
|
self.__dir = True
|
|
53
53
|
|
|
54
54
|
_alist = ( # assoc-list of column names
|
|
55
|
-
("id",
|
|
55
|
+
("id", 45),
|
|
56
56
|
("name", 160),
|
|
57
57
|
("shape", 90),
|
|
58
58
|
("dtype", 60),
|
|
@@ -65,16 +65,20 @@ class CheckList(wx.ListCtrl, ListCtrlAutoWidthMixin, CtrlInterface):
|
|
|
65
65
|
|
|
66
66
|
for j, frame in enumerate(self.Target.all_frames):
|
|
67
67
|
self.InsertItem(j, str(j))
|
|
68
|
-
self.UpdateInfo(frame)
|
|
68
|
+
self.UpdateInfo(frame) # update all --> 計算が入ると時間がかかる
|
|
69
69
|
|
|
70
70
|
self.handler.update({ # DNA<frame_listview>
|
|
71
71
|
0 : {
|
|
72
|
-
'Lbutton dblclick' : (0, self.OnShowItems),
|
|
73
|
-
'enter pressed' : (0, self.OnShowItems),
|
|
74
|
-
'delete pressed' : (0, self.OnRemoveItems),
|
|
72
|
+
'Lbutton dblclick' : (0, self.OnShowItems), # -> frame_shown
|
|
73
|
+
'enter pressed' : (0, self.OnShowItems), # -> frame_shown
|
|
74
|
+
'delete pressed' : (0, self.OnRemoveItems), # -> frame_removed/shown
|
|
75
75
|
'C-a pressed' : (0, self.OnSelectAllItems),
|
|
76
76
|
'C-o pressed' : (0, self.OnLoadItems),
|
|
77
77
|
'C-s pressed' : (0, self.OnSaveItems),
|
|
78
|
+
'C-S-s pressed' : (0, self.OnSaveItems),
|
|
79
|
+
'C-c pressed' : (0, self.OnCopyInfo),
|
|
80
|
+
'C-l pressed' : (0, self.OnEditLocalUnit),
|
|
81
|
+
'f2 pressed' : (0, self.OnEditAnnotation),
|
|
78
82
|
'M-up pressed' : (0, self.Target.OnPageUp),
|
|
79
83
|
'M-down pressed' : (0, self.Target.OnPageDown),
|
|
80
84
|
},
|
|
@@ -98,7 +102,7 @@ class CheckList(wx.ListCtrl, ListCtrlAutoWidthMixin, CtrlInterface):
|
|
|
98
102
|
|
|
99
103
|
self.menu = [
|
|
100
104
|
(100, "Edit localunit", Icon('image'),
|
|
101
|
-
self.
|
|
105
|
+
self.OnEditLocalUnit,
|
|
102
106
|
lambda v: v.Enable(self.focused_item != -1)),
|
|
103
107
|
|
|
104
108
|
(101, "Edit annotation", Icon('pencil'),
|
|
@@ -111,13 +115,11 @@ class CheckList(wx.ListCtrl, ListCtrlAutoWidthMixin, CtrlInterface):
|
|
|
111
115
|
]
|
|
112
116
|
self.Bind(wx.EVT_CONTEXT_MENU,
|
|
113
117
|
lambda v: Menu.Popup(self, self.menu))
|
|
114
|
-
|
|
118
|
+
|
|
115
119
|
def Destroy(self):
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return wx.ListCtrl.Destroy(self)
|
|
120
|
-
|
|
120
|
+
self.Target.handler.remove(self.context)
|
|
121
|
+
return wx.ListCtrl.Destroy(self)
|
|
122
|
+
|
|
121
123
|
def UpdateInfo(self, frame):
|
|
122
124
|
ls = ("{}".format(frame.index),
|
|
123
125
|
"{}".format(frame.name),
|
|
@@ -132,46 +134,46 @@ class CheckList(wx.ListCtrl, ListCtrlAutoWidthMixin, CtrlInterface):
|
|
|
132
134
|
self.SetItem(j, k, v)
|
|
133
135
|
if frame.pathname:
|
|
134
136
|
self.CheckItem(j)
|
|
135
|
-
|
|
137
|
+
|
|
136
138
|
def OnShowItems(self, evt):
|
|
137
139
|
self.Target.select(self.focused_item)
|
|
138
|
-
|
|
140
|
+
|
|
139
141
|
def OnRemoveItems(self, evt):
|
|
140
142
|
del self.Target[self.selected_items]
|
|
141
|
-
|
|
142
|
-
def OnSortItems(self, evt):
|
|
143
|
+
|
|
144
|
+
def OnSortItems(self, evt): #<wx._controls.ListEvent>
|
|
143
145
|
col = evt.Column
|
|
144
|
-
if col == 0:
|
|
146
|
+
if col == 0: # reverse the first column
|
|
145
147
|
self.__dir = False
|
|
146
|
-
self.__dir = not self.__dir
|
|
148
|
+
self.__dir = not self.__dir # toggle 0:ascend/1:descend
|
|
147
149
|
|
|
148
150
|
frames = self.Target.all_frames
|
|
149
151
|
if frames:
|
|
150
152
|
def _eval(x):
|
|
151
153
|
try:
|
|
152
|
-
return eval(x[col].replace('*', ''))
|
|
154
|
+
return eval(x[col].replace('*', '')) # localunit* とか
|
|
153
155
|
except Exception:
|
|
154
156
|
return x[col]
|
|
155
157
|
frame = self.Target.frame
|
|
156
158
|
items = sorted(self.all_items, reverse=self.__dir, key=_eval)
|
|
157
|
-
frames[:] = [frames[int(c[0])] for c in items]
|
|
159
|
+
frames[:] = [frames[int(c[0])] for c in items] # sort by new Id of items
|
|
158
160
|
|
|
159
161
|
lc = list(self.checked_items)
|
|
160
162
|
|
|
161
163
|
for j, c in enumerate(items):
|
|
162
164
|
self.Select(j, False)
|
|
163
165
|
self.CheckItem(j, int(c[0]) in lc)
|
|
164
|
-
for k, v in enumerate(c[1:]):
|
|
166
|
+
for k, v in enumerate(c[1:]): # update data except for id(0)
|
|
165
167
|
self.SetItem(j, k+1, v)
|
|
166
|
-
self.Target.select(frame)
|
|
167
|
-
|
|
168
|
+
self.Target.select(frame) # invokes [frame_shown] to select the item
|
|
169
|
+
|
|
168
170
|
def OnSelectAllItems(self, evt):
|
|
169
171
|
for j in range(self.ItemCount):
|
|
170
172
|
self.Select(j)
|
|
171
|
-
|
|
173
|
+
|
|
172
174
|
def OnLoadItems(self, evt):
|
|
173
175
|
self.parent.parent.load_index(view=self.Target)
|
|
174
|
-
|
|
176
|
+
|
|
175
177
|
def OnSaveItems(self, evt):
|
|
176
178
|
selected_frames = [self.Target.all_frames[j] for j in self.selected_items]
|
|
177
179
|
if selected_frames:
|
|
@@ -179,82 +181,83 @@ class CheckList(wx.ListCtrl, ListCtrlAutoWidthMixin, CtrlInterface):
|
|
|
179
181
|
self.parent.parent.save_index(frames=selected_frames)
|
|
180
182
|
else:
|
|
181
183
|
self.parent.message("No frame selected.")
|
|
182
|
-
|
|
184
|
+
|
|
183
185
|
def OnCopyInfo(self, evt):
|
|
184
186
|
selected_frames = [self.Target.all_frames[j] for j in self.selected_items]
|
|
185
187
|
if selected_frames:
|
|
186
|
-
text =
|
|
188
|
+
text = []
|
|
187
189
|
for frame in selected_frames:
|
|
188
|
-
text += pformat(frame.attributes, sort_dicts=0)
|
|
189
|
-
|
|
190
|
-
Clipboard.write(text)
|
|
190
|
+
text += [pformat(frame.attributes, sort_dicts=0)] # ALL attributes
|
|
191
|
+
Clipboard.write('\n'.join(text))
|
|
191
192
|
else:
|
|
192
193
|
self.parent.message("No frame selected.")
|
|
193
|
-
|
|
194
|
-
def
|
|
194
|
+
|
|
195
|
+
def OnEditLocalUnit(self, evt):
|
|
195
196
|
frame = self.Target.all_frames[self.focused_item]
|
|
196
197
|
with wx.TextEntryDialog(self, frame.name,
|
|
197
198
|
'Enter localunit', repr(frame.localunit)) as dlg:
|
|
198
199
|
if dlg.ShowModal() == wx.ID_OK:
|
|
199
200
|
frame.unit = eval(dlg.Value or 'None')
|
|
200
|
-
|
|
201
|
+
self.SetFocus()
|
|
202
|
+
|
|
201
203
|
def OnEditAnnotation(self, evt):
|
|
202
204
|
frame = self.Target.all_frames[self.focused_item]
|
|
203
205
|
with wx.TextEntryDialog(self, frame.name,
|
|
204
206
|
'Enter an annotation', frame.annotation) as dlg:
|
|
205
207
|
if dlg.ShowModal() == wx.ID_OK:
|
|
206
208
|
frame.annotation = dlg.Value
|
|
207
|
-
|
|
209
|
+
self.SetFocus()
|
|
210
|
+
|
|
208
211
|
def OnItemSelected(self, evt):
|
|
209
212
|
frame = self.Target.all_frames[evt.Index]
|
|
210
213
|
self.parent.message(frame.pathname)
|
|
211
214
|
evt.Skip()
|
|
212
|
-
|
|
215
|
+
|
|
213
216
|
## --------------------------------
|
|
214
|
-
## Actions of frame-handler
|
|
217
|
+
## Actions of frame-handler.
|
|
215
218
|
## --------------------------------
|
|
216
|
-
|
|
219
|
+
|
|
217
220
|
def on_frame_loaded(self, frame):
|
|
218
221
|
j = frame.index
|
|
219
222
|
self.InsertItem(j, str(j))
|
|
220
|
-
for k in range(j+1, self.ItemCount):
|
|
223
|
+
for k in range(j+1, self.ItemCount): # id(0) を更新する
|
|
221
224
|
self.SetItem(k, 0, str(k))
|
|
222
225
|
self.UpdateInfo(frame)
|
|
223
|
-
|
|
226
|
+
|
|
224
227
|
def on_frame_shown(self, frame):
|
|
225
228
|
j = frame.index
|
|
226
229
|
self.SetItemFont(j, self.Font.Bold())
|
|
227
230
|
self.Select(j)
|
|
228
231
|
self.Focus(j)
|
|
229
|
-
|
|
232
|
+
|
|
230
233
|
def on_frame_hidden(self, frame):
|
|
231
234
|
j = frame.index
|
|
232
235
|
self.SetItemFont(j, self.Font)
|
|
233
236
|
self.Select(j, False)
|
|
234
|
-
|
|
237
|
+
|
|
235
238
|
def on_frames_removed(self, indices):
|
|
236
239
|
with wx.FrozenWindow(self):
|
|
237
240
|
for j in reversed(indices):
|
|
238
241
|
self.DeleteItem(j)
|
|
239
|
-
for k in range(self.ItemCount):
|
|
242
|
+
for k in range(self.ItemCount): # id(0) を更新する
|
|
240
243
|
self.SetItem(k, 0, str(k))
|
|
241
244
|
|
|
242
245
|
|
|
243
246
|
class Plugin(Layer):
|
|
244
247
|
"""Property list of Graph buffers.
|
|
245
248
|
"""
|
|
246
|
-
menukey = "Plugins/Extensions/&Buffer listbox\
|
|
249
|
+
menukey = "Plugins/Extensions/&Buffer listbox\tAlt+b"
|
|
247
250
|
caption = "Property list"
|
|
248
251
|
dockable = False
|
|
249
|
-
|
|
252
|
+
|
|
250
253
|
@property
|
|
251
254
|
def all_pages(self):
|
|
252
255
|
return [self.nb.GetPage(i) for i in range(self.nb.PageCount)]
|
|
253
|
-
|
|
256
|
+
|
|
254
257
|
@property
|
|
255
258
|
def message(self):
|
|
256
259
|
return self.statusline
|
|
257
|
-
|
|
260
|
+
|
|
258
261
|
def Init(self):
|
|
259
262
|
self.nb = aui.AuiNotebook(self, size=(400,150),
|
|
260
263
|
style = (aui.AUI_NB_DEFAULT_STYLE|aui.AUI_NB_RIGHT)
|
|
@@ -275,12 +278,12 @@ class Plugin(Layer):
|
|
|
275
278
|
self.parent.select_view(self.nb.CurrentPage.Target)
|
|
276
279
|
evt.Skip()
|
|
277
280
|
self.nb.Bind(wx.EVT_CHILD_FOCUS, on_focus_set)
|
|
278
|
-
|
|
281
|
+
|
|
279
282
|
def attach(self, target, caption):
|
|
280
283
|
if target not in [lc.Target for lc in self.all_pages]:
|
|
281
284
|
lc = CheckList(self, target)
|
|
282
285
|
self.nb.AddPage(lc, caption)
|
|
283
|
-
|
|
286
|
+
|
|
284
287
|
def detach(self, target):
|
|
285
288
|
for k, lc in enumerate(self.all_pages):
|
|
286
289
|
if target is lc.Target:
|
mwx/plugins/line_profile.py
CHANGED
|
@@ -8,10 +8,10 @@ from mwx.matplot2lg import LineProfile
|
|
|
8
8
|
class Plugin(Layer):
|
|
9
9
|
"""Line profile of the currently selected buffers.
|
|
10
10
|
"""
|
|
11
|
-
menukey = "Plugins/Extensions/&Line profile\
|
|
11
|
+
menukey = "Plugins/Extensions/&Line profile\tAlt+l"
|
|
12
12
|
caption = "Line profile"
|
|
13
13
|
dockable = False
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
def Init(self):
|
|
16
16
|
self.plot = LineProfile(self, log=self.message, size=(300,200))
|
|
17
17
|
|