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.
@@ -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, TextCtrl
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 # w * h * 3
39
- buf = b"" # bytearray()
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] # Only the last one will be loaded.
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
- ## szBackend=wx.media.MEDIABACKEND_DIRECTSHOW
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:", # range/value will be set when loaded later.
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:", # range/value will be set when loaded later.
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 = TextCtrl(self, icon="cut", size=(130,-1),
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.seekdelta(-100), icon='|<-')
114
- self.fw = Button(self, handler=lambda v: self.seekdelta(+100), icon='->|')
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=12, cw=0, tw=64)
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' : (1, ),
136
- 'space pressed' : (1, _F(self.mc.Play)),
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
- 1 : {
144
+ 2 : { # MEDIASTATE_PLAYING
139
145
  'stop' : (0, ),
140
- 'pause' : (0, ),
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, partial(self.handler, 'pause'))
146
- self.mc.Bind(wx.media.EVT_MEDIA_PLAY, partial(self.handler, 'play'))
147
- self.mc.Bind(wx.media.EVT_MEDIA_STOP, partial(self.handler, '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.EVT_SHOW, self.OnShow)
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
- try:
158
- self.parent.handler.unbind("unknown_format", self.load_media)
159
- finally:
160
- return Layer.Destroy(self)
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
- evt.Skip()
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) # -> True (always)
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
- ## self.video_fps = eval(v['r_frame_rate']) # real base frame rate
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 # correction ▲理由は不明 (WMP10 backend only?)
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
- try:
210
+ if self._path:
215
211
  self.mc.Seek(self.DELTA + int(tc.value * 1000))
216
- except Exception:
217
- pass
218
-
212
+
219
213
  def get_offset(self, tc):
220
214
  """Get offset value and assigns it to ss/to value."""
221
- try:
215
+ if self._path:
222
216
  tc.value = round(self.mc.Tell()) / 1000
223
- except Exception:
224
- pass
225
-
217
+
226
218
  def set_crop(self):
227
- """Set crop area (W:H:Left:Top) to roi."""
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 roi."""
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 self._path and not crop:
246
+ if not crop:
251
247
  crop = "{}:{}:0:0".format(*self.video_size)
252
248
  self.crop.Value = crop
253
-
254
- def seekdelta(self, offset):
255
- """Seek relative position [ms]."""
256
- if wx.GetKeyState(wx.WXK_SHIFT):
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
- except Exception as e:
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) # => seek
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) # +-m/2
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)) # masking area factor of 1/2
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.roi)
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 {}*".format(frame.name),
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.roi
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() # apply mask to the 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 {}*".format(frame.name),
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")
@@ -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
- ## return [[self.GetItemText(j, k) for k in cols] for j in rows]
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", 42),
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) # update all --> 計算が入ると時間がかかる
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), # -> frame_shown
73
- 'enter pressed' : (0, self.OnShowItems), # -> frame_shown
74
- 'delete pressed' : (0, self.OnRemoveItems), # -> frame_removed/shown
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.OnEditUnit,
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
- try:
117
- self.Target.handler.remove(self.context)
118
- finally:
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): #<wx._controls.ListEvent>
143
+
144
+ def OnSortItems(self, evt): #<wx._controls.ListEvent>
143
145
  col = evt.Column
144
- if col == 0: # reverse the first column
146
+ if col == 0: # reverse the first column
145
147
  self.__dir = False
146
- self.__dir = not self.__dir # toggle 0:ascend/1:descend
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('*', '')) # localunit* とか
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] # sort by new Id of 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:]): # update data except for id(0)
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) # invokes [frame_shown] to select the item
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) # ALL attributes
189
- ## text += '{}\n{}\n'.format(frame.name, frame.annotation)
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 OnEditUnit(self, evt):
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): # id(0) を更新する
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): # id(0) を更新する
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\tCtrl+b"
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:
@@ -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\tCtrl+l"
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