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/matplot2.py
CHANGED
|
@@ -3,19 +3,18 @@
|
|
|
3
3
|
"""
|
|
4
4
|
import wx
|
|
5
5
|
|
|
6
|
-
import matplotlib; matplotlib.use('wxagg')
|
|
6
|
+
import matplotlib; matplotlib.use('wxagg') # noqa
|
|
7
7
|
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
|
|
8
8
|
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg as Toolbar
|
|
9
9
|
from matplotlib.widgets import Cursor
|
|
10
10
|
from matplotlib.figure import Figure
|
|
11
|
-
from matplotlib import cm
|
|
12
11
|
import numpy as np
|
|
13
12
|
|
|
14
13
|
from . import framework as mwx
|
|
15
|
-
from .framework import hotkey, regulate_key, pack, Menu, FSM
|
|
14
|
+
from .framework import hotkey, regulate_key, postcall, pack, Menu, FSM
|
|
16
15
|
|
|
17
16
|
|
|
18
|
-
##
|
|
17
|
+
## State constants.
|
|
19
18
|
NORMAL = 'Normal'
|
|
20
19
|
DRAGGING = '-dragging'
|
|
21
20
|
PAN, ZOOM = 'Pan', 'Zoom'
|
|
@@ -23,7 +22,7 @@ XAXIS, YAXIS = 'Xaxis', 'Yaxis'
|
|
|
23
22
|
MARK, LINE, REGION = 'Mark', 'Line', 'Region'
|
|
24
23
|
|
|
25
24
|
|
|
26
|
-
## Monkey-patch for matplotlib 3.4/WXAgg
|
|
25
|
+
## Monkey-patch for matplotlib 3.4/WXAgg.
|
|
27
26
|
if matplotlib.parse_version(matplotlib.__version__).release < (3,8,0):
|
|
28
27
|
from matplotlib.backend_bases import Event
|
|
29
28
|
|
|
@@ -35,11 +34,13 @@ if matplotlib.parse_version(matplotlib.__version__).release < (3,8,0):
|
|
|
35
34
|
Event.__init__ = __init__
|
|
36
35
|
del __init__
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
|
|
38
|
+
## Monkey-patch (local) for matplotlib 3.8/WXAgg.
|
|
39
39
|
if 1:
|
|
40
40
|
class Cursor(Cursor):
|
|
41
41
|
def onmove(self, event):
|
|
42
42
|
"""Internal event handler to draw the cursor when the mouse moves.
|
|
43
|
+
|
|
43
44
|
(override) If the cursor is off the axes, the xdata and ydata will
|
|
44
45
|
be None, and will simply be cleared rather than drawn.
|
|
45
46
|
"""
|
|
@@ -48,7 +49,7 @@ if 1:
|
|
|
48
49
|
if not self.canvas.widgetlock.available(self):
|
|
49
50
|
return
|
|
50
51
|
|
|
51
|
-
## xdata, ydata = self._get_data_coords(event)
|
|
52
|
+
## xdata, ydata = self._get_data_coords(event) # >= 3.8 only.
|
|
52
53
|
xdata, ydata = event.xdata, event.ydata
|
|
53
54
|
self.linev.set_xdata((xdata, xdata))
|
|
54
55
|
self.linev.set_visible(self.visible and self.vertOn)
|
|
@@ -68,31 +69,31 @@ if 1:
|
|
|
68
69
|
|
|
69
70
|
|
|
70
71
|
class MatplotPanel(wx.Panel):
|
|
71
|
-
"""MPL panel for general graph
|
|
72
|
+
"""MPL panel for general graph.
|
|
72
73
|
|
|
73
74
|
Attributes:
|
|
74
|
-
figure
|
|
75
|
-
canvas
|
|
76
|
-
toolbar
|
|
77
|
-
cursor
|
|
78
|
-
selected
|
|
75
|
+
figure: <matplotlib.figure.Figure>
|
|
76
|
+
canvas: <matplotlib.backends.backend_wxagg.FigureCanvasWxAgg>
|
|
77
|
+
toolbar: <matplotlib.backends.backend_wx.NavigationToolbar2Wx>
|
|
78
|
+
cursor: <matplotlib.widgets.Cursor>
|
|
79
|
+
selected: Selected points <matplotlib.lines.Line2D>
|
|
79
80
|
"""
|
|
80
81
|
handler = property(lambda self: self.__handler)
|
|
81
|
-
|
|
82
|
+
|
|
82
83
|
def __init__(self, parent, log=None, margin=(.1,.1,.9,.9), **kwargs):
|
|
83
84
|
wx.Panel.__init__(self, parent, **kwargs)
|
|
84
85
|
|
|
85
86
|
self.message = log or (lambda s: s)
|
|
86
87
|
|
|
87
88
|
#<matplotlib.figure.Figure>
|
|
88
|
-
self.figure = Figure(facecolor='white', figsize=(.1,.1))
|
|
89
|
+
self.figure = Figure(facecolor='white', figsize=(.1,.1)) # inches
|
|
89
90
|
|
|
90
91
|
#<matplotlib.backends.backend_wxagg.FigureCanvasWxAgg>
|
|
91
92
|
self.canvas = FigureCanvas(self, -1, self.figure)
|
|
92
93
|
|
|
93
|
-
## To avoid AssertionError
|
|
94
|
-
## To avoid AttributeError
|
|
95
|
-
## "initial draw which caches the renderer"
|
|
94
|
+
## To avoid AssertionError "self._cachedRenderer is not None"
|
|
95
|
+
## To avoid AttributeError "draw_artist can only be used after an "
|
|
96
|
+
## "initial draw which caches the renderer"
|
|
96
97
|
self.canvas.draw()
|
|
97
98
|
|
|
98
99
|
#<matplotlib.backends.backend_wxagg.NavigationToolbar2WxAgg>
|
|
@@ -106,7 +107,7 @@ class MatplotPanel(wx.Panel):
|
|
|
106
107
|
self.modeline.Bind(wx.EVT_LEFT_DOWN, lambda v: self.canvas.SetFocus())
|
|
107
108
|
|
|
108
109
|
self.infobar = wx.InfoBar(self)
|
|
109
|
-
self.infobar.Size = (0, 0)
|
|
110
|
+
self.infobar.Size = (0, 0) # workaround for incorrect wrap sizing
|
|
110
111
|
|
|
111
112
|
self.SetSizer(
|
|
112
113
|
pack(self, (
|
|
@@ -121,14 +122,14 @@ class MatplotPanel(wx.Panel):
|
|
|
121
122
|
self.modeline.Show(0)
|
|
122
123
|
self.Layout()
|
|
123
124
|
|
|
124
|
-
self.set_margin(margin or (0,0,1,1))
|
|
125
|
+
self.set_margin(margin or (0,0,1,1)) # if margin is None
|
|
125
126
|
self.clear()
|
|
126
127
|
|
|
127
128
|
## mpl event handler
|
|
128
129
|
self.canvas.mpl_connect('pick_event', self.on_pick)
|
|
129
130
|
self.canvas.mpl_connect('scroll_event', self.on_scroll)
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
# self.canvas.mpl_connect('key_press_event', self.on_key_press)
|
|
132
|
+
# self.canvas.mpl_connect('key_release_event', self.on_key_release)
|
|
132
133
|
self.canvas.mpl_connect('button_press_event', self.on_button_press)
|
|
133
134
|
self.canvas.mpl_connect('button_release_event', self.on_button_release)
|
|
134
135
|
self.canvas.mpl_connect('motion_notify_event', self.on_motion_notify)
|
|
@@ -137,8 +138,8 @@ class MatplotPanel(wx.Panel):
|
|
|
137
138
|
self.canvas.mpl_connect('figure_leave_event', lambda v: self.handler('figure_leave', v))
|
|
138
139
|
self.canvas.mpl_connect('axes_enter_event', lambda v: self.handler('axes_enter', v))
|
|
139
140
|
self.canvas.mpl_connect('axes_leave_event', lambda v: self.handler('axes_leave', v))
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
# self.canvas.mpl_connect('resize_event', lambda v: self.handler('canvas_resized', v))
|
|
142
|
+
# self.canvas.mpl_connect('draw_event', lambda v: self.handler('canvas_drawn', v))
|
|
142
143
|
|
|
143
144
|
self.canvas.Bind(wx.EVT_CHAR_HOOK, self.on_hotkey_press)
|
|
144
145
|
self.canvas.Bind(wx.EVT_KEY_DOWN, self.on_hotkey_down)
|
|
@@ -160,10 +161,16 @@ class MatplotPanel(wx.Panel):
|
|
|
160
161
|
if self.handler.fork(self.handler.current_event, evt) is None:
|
|
161
162
|
evt.Skip()
|
|
162
163
|
|
|
164
|
+
def skip(evt): #<wx._core.KeyEvent> #<matplotlib.backend_bases.MouseEvent>
|
|
165
|
+
try:
|
|
166
|
+
evt.Skip()
|
|
167
|
+
except AttributeError:
|
|
168
|
+
pass
|
|
169
|
+
|
|
163
170
|
self.__handler = FSM({ # DNA<MatplotPanel>
|
|
164
171
|
None : {
|
|
165
|
-
'canvas_draw' : [ None, self.OnDraw ],
|
|
166
|
-
#'canvas_drawn' : [ None, ],
|
|
172
|
+
'canvas_draw' : [ None, self.OnDraw ], # before canvas.draw
|
|
173
|
+
#'canvas_drawn' : [ None, ], # after canvas.draw
|
|
167
174
|
#'canvas_resized' : [ None, ],
|
|
168
175
|
'focus_set' : [ None, self.on_focus_set ],
|
|
169
176
|
'focus_kill' : [ None, self.on_focus_kill ],
|
|
@@ -172,12 +179,10 @@ class MatplotPanel(wx.Panel):
|
|
|
172
179
|
'axes_enter' : [ None, ],
|
|
173
180
|
'axes_leave' : [ None, ],
|
|
174
181
|
'home pressed' : [ None, self.OnHomePosition ],
|
|
175
|
-
'backspace pressed' : [ None, self.OnBackPosition ],
|
|
176
|
-
'M-backspace pressed' : [ None, self.OnForwardPosition ],
|
|
177
182
|
'Xbutton1 pressed' : [ None, self.OnBackPosition ],
|
|
178
183
|
'Xbutton2 pressed' : [ None, self.OnForwardPosition ],
|
|
179
|
-
|
|
180
|
-
|
|
184
|
+
'M-left pressed' : [ None, self.OnBackPosition ],
|
|
185
|
+
'M-right pressed' : [ None, self.OnForwardPosition ],
|
|
181
186
|
},
|
|
182
187
|
NORMAL : {
|
|
183
188
|
'art_picked' : (NORMAL, ),
|
|
@@ -188,6 +193,7 @@ class MatplotPanel(wx.Panel):
|
|
|
188
193
|
'space pressed' : (PAN, self.OnPanBegin),
|
|
189
194
|
'ctrl pressed' : (PAN, self.OnPanBegin),
|
|
190
195
|
'z pressed' : (ZOOM, self.OnZoomBegin),
|
|
196
|
+
'* pressed' : (NORMAL, skip),
|
|
191
197
|
'xaxis motion' : (XAXIS, self.OnAxisEnter),
|
|
192
198
|
'yaxis motion' : (YAXIS, self.OnAxisEnter),
|
|
193
199
|
'y2axis motion' : (YAXIS, self.OnAxisEnter),
|
|
@@ -285,20 +291,20 @@ class MatplotPanel(wx.Panel):
|
|
|
285
291
|
(mwx.ID_(501), "&Copy image", "Copy canvas image to clipboard",
|
|
286
292
|
lambda v: self.copy_to_clipboard()),
|
|
287
293
|
|
|
288
|
-
|
|
289
|
-
|
|
294
|
+
# (mwx.ID_(502), "&Export image", "Save canvas image",
|
|
295
|
+
# lambda v: self.save_to_file()),
|
|
290
296
|
]
|
|
291
297
|
|
|
292
298
|
self.__key = ''
|
|
293
299
|
self.__isMenu = None
|
|
294
300
|
self.__isPressed = None
|
|
295
|
-
self.__isDragging = False
|
|
296
|
-
|
|
301
|
+
self.__isDragging = False # True if dragging. (None if dblclicked)
|
|
302
|
+
|
|
297
303
|
def clear(self):
|
|
298
304
|
"""Initialize the plot figure."""
|
|
299
305
|
#<matplotlib.axes.Axes>
|
|
300
306
|
self.figure.clear()
|
|
301
|
-
self.figure.add_subplot(111)
|
|
307
|
+
self.figure.add_subplot(111) # cf. add_axes(rect=(l,b,w,h))
|
|
302
308
|
|
|
303
309
|
#<matplotlib.lines.Line2D>
|
|
304
310
|
(self.selected,) = self.axes.plot([], [], "yo-", ms=6, lw=2, alpha=0.75,
|
|
@@ -308,7 +314,9 @@ class MatplotPanel(wx.Panel):
|
|
|
308
314
|
#<matplotlib.widgets.Cursor>
|
|
309
315
|
self.cursor = Cursor(self.axes, useblit=True, color='grey', linewidth=1)
|
|
310
316
|
self.cursor.visible = 1
|
|
311
|
-
|
|
317
|
+
|
|
318
|
+
## Note: To avoid a wxAssertionError when running in a thread.
|
|
319
|
+
@postcall
|
|
312
320
|
def draw(self, art=None):
|
|
313
321
|
"""Draw plots.
|
|
314
322
|
Call each time the drawing should be updated.
|
|
@@ -316,146 +324,146 @@ class MatplotPanel(wx.Panel):
|
|
|
316
324
|
if isinstance(art, matplotlib.artist.Artist):
|
|
317
325
|
self.axes.draw_artist(art)
|
|
318
326
|
self.canvas.blit(art.get_clip_box())
|
|
319
|
-
|
|
327
|
+
# self.canvas.draw_idle()
|
|
320
328
|
else:
|
|
321
329
|
self.handler('canvas_draw', self.frame)
|
|
322
330
|
self.canvas.draw()
|
|
323
|
-
|
|
331
|
+
|
|
324
332
|
def set_margin(self, lbrt):
|
|
325
333
|
self.figure.subplots_adjust(*lbrt)
|
|
326
|
-
|
|
334
|
+
|
|
327
335
|
def set_wxcursor(self, c):
|
|
328
336
|
self.canvas.SetCursor(wx.Cursor(c))
|
|
329
|
-
|
|
337
|
+
|
|
330
338
|
## --------------------------------
|
|
331
|
-
## Property of the current frame
|
|
339
|
+
## Property of the current frame.
|
|
332
340
|
## --------------------------------
|
|
333
|
-
|
|
334
|
-
##
|
|
341
|
+
|
|
342
|
+
## To be overridden (referenced in draw).
|
|
335
343
|
frame = property(lambda self: self)
|
|
336
|
-
|
|
344
|
+
|
|
337
345
|
axes = property(
|
|
338
346
|
lambda self: self.figure.axes[0],
|
|
339
|
-
doc="The first figure axes <matplotlib.axes.Axes
|
|
340
|
-
|
|
347
|
+
doc="The first figure axes <matplotlib.axes.Axes>.")
|
|
348
|
+
|
|
341
349
|
xbound = property(
|
|
342
350
|
lambda self: np.array(self.axes.get_xbound()),
|
|
343
|
-
lambda self,v: self.axes.set_xbound(v),
|
|
344
|
-
doc="
|
|
345
|
-
|
|
351
|
+
lambda self, v: self.axes.set_xbound(v),
|
|
352
|
+
doc="X-axis numerical bounds where lowerBound < upperBound.")
|
|
353
|
+
|
|
346
354
|
ybound = property(
|
|
347
355
|
lambda self: np.array(self.axes.get_ybound()),
|
|
348
|
-
lambda self,v: self.axes.set_ybound(v),
|
|
349
|
-
doc="
|
|
350
|
-
|
|
356
|
+
lambda self, v: self.axes.set_ybound(v),
|
|
357
|
+
doc="Y-axis numerical bounds where lowerBound < upperBound.")
|
|
358
|
+
|
|
351
359
|
xlim = property(
|
|
352
360
|
lambda self: np.array(self.axes.get_xlim()),
|
|
353
|
-
lambda self,v: self.axes.set_xlim(v),
|
|
354
|
-
doc="
|
|
355
|
-
|
|
361
|
+
lambda self, v: self.axes.set_xlim(v),
|
|
362
|
+
doc="X-axis range [left, right].")
|
|
363
|
+
|
|
356
364
|
ylim = property(
|
|
357
365
|
lambda self: np.array(self.axes.get_ylim()),
|
|
358
|
-
lambda self,v: self.axes.set_ylim(v),
|
|
359
|
-
doc="
|
|
360
|
-
|
|
366
|
+
lambda self, v: self.axes.set_ylim(v),
|
|
367
|
+
doc="Y-axis range [bottom, top].")
|
|
368
|
+
|
|
361
369
|
@property
|
|
362
370
|
def ddpu(self):
|
|
363
371
|
"""Display-dot resolution (x, y) [dots per arb.unit]."""
|
|
364
|
-
|
|
365
|
-
a, b = self.mapxy2disp([0,1],[0,1])
|
|
372
|
+
# return self.mapxy2disp(1,1) - self.mapxy2disp(0,0)
|
|
373
|
+
a, b = self.mapxy2disp([0,1], [0,1])
|
|
366
374
|
return b - a
|
|
367
|
-
|
|
375
|
+
|
|
368
376
|
def mapxy2disp(self, x, y):
|
|
369
|
-
"""Map xydata --> display dot pixel
|
|
377
|
+
"""Map xydata --> display dot pixel coordinates."""
|
|
370
378
|
v = np.array((x, y)).T
|
|
371
379
|
return self.axes.transData.transform(v)
|
|
372
|
-
|
|
380
|
+
|
|
373
381
|
def mapdisp2xy(self, px, py):
|
|
374
|
-
"""Map display dot pixel
|
|
382
|
+
"""Map display dot pixel coordinates --> xydata."""
|
|
375
383
|
v = np.array((px, py)).T
|
|
376
384
|
return self.axes.transData.inverted().transform(v)
|
|
377
|
-
|
|
385
|
+
|
|
378
386
|
## --------------------------------
|
|
379
|
-
## Property of the modeline
|
|
387
|
+
## Property of the modeline.
|
|
380
388
|
## --------------------------------
|
|
381
|
-
|
|
389
|
+
|
|
382
390
|
selectedModeLineBg = '#000000'
|
|
383
391
|
selectedModeLineFg = '#f0f0f0'
|
|
384
392
|
unselectedModeLineBg = 'auto'
|
|
385
393
|
unselectedModeLineFg = 'auto'
|
|
386
|
-
|
|
387
|
-
def on_modeline_tip(self, evt):
|
|
394
|
+
|
|
395
|
+
def on_modeline_tip(self, evt): #<wx._core.MouseEvent>
|
|
388
396
|
flag = self.modeline.HitTest(evt.Position)
|
|
389
397
|
if flag == wx.HT_WINDOW_INSIDE:
|
|
390
|
-
self.modeline.
|
|
398
|
+
self.modeline.SetToolTip(self.modeline.Label)
|
|
391
399
|
evt.Skip()
|
|
392
|
-
|
|
393
|
-
def on_focus_set(self, evt):
|
|
400
|
+
|
|
401
|
+
def on_focus_set(self, evt): #<wx._core.FocusEvent>
|
|
394
402
|
if self.modeline.IsShown():
|
|
395
403
|
self.modeline.SetBackgroundColour(self.selectedModeLineBg)
|
|
396
404
|
self.modeline.SetForegroundColour(self.selectedModeLineFg)
|
|
397
405
|
self.modeline.Refresh()
|
|
398
406
|
self.escape()
|
|
399
407
|
evt.Skip()
|
|
400
|
-
|
|
401
|
-
def on_focus_kill(self, evt):
|
|
408
|
+
|
|
409
|
+
def on_focus_kill(self, evt): #<wx._core.FocusEvent>
|
|
402
410
|
if self.modeline.IsShown():
|
|
403
411
|
self.modeline.SetBackgroundColour(self.unselectedModeLineBg)
|
|
404
412
|
self.modeline.SetForegroundColour(self.unselectedModeLineFg)
|
|
405
413
|
self.modeline.Refresh()
|
|
406
414
|
self.escape()
|
|
407
415
|
evt.Skip()
|
|
408
|
-
|
|
416
|
+
|
|
409
417
|
def escape(self, evt=None):
|
|
410
418
|
"""Feel like pressing {escape}.
|
|
411
419
|
エスケープキーを押した気持ちになる
|
|
412
420
|
"""
|
|
413
421
|
wx.UIActionSimulator().KeyUp(wx.WXK_ESCAPE)
|
|
414
|
-
|
|
422
|
+
|
|
415
423
|
## --------------------------------
|
|
416
|
-
## External I/O file and clipboard
|
|
424
|
+
## External I/O file and clipboard.
|
|
417
425
|
## --------------------------------
|
|
418
|
-
|
|
426
|
+
|
|
419
427
|
def copy_to_clipboard(self):
|
|
420
428
|
"""Copy canvas image to clipboard."""
|
|
421
|
-
|
|
429
|
+
# b = self.selected.get_visible()
|
|
422
430
|
c = self.cursor.visible
|
|
423
431
|
try:
|
|
424
|
-
|
|
432
|
+
# self.selected.set_visible(0)
|
|
425
433
|
self.cursor.visible = 0
|
|
426
434
|
self.canvas.draw()
|
|
427
435
|
self.canvas.Copy_to_Clipboard()
|
|
428
436
|
self.message("Copy image to clipboard.")
|
|
429
437
|
finally:
|
|
430
|
-
|
|
438
|
+
# self.selected.set_visible(b)
|
|
431
439
|
self.cursor.visible = c
|
|
432
440
|
self.canvas.draw()
|
|
433
|
-
|
|
441
|
+
|
|
434
442
|
## --------------------------------
|
|
435
|
-
## Selector interface
|
|
443
|
+
## Selector interface.
|
|
436
444
|
## --------------------------------
|
|
437
|
-
|
|
445
|
+
|
|
438
446
|
def trace_point(self, x, y):
|
|
439
447
|
if hasattr(x, '__iter__'):
|
|
440
448
|
if not len(x):
|
|
441
449
|
return
|
|
442
450
|
x, y = x[0], y[0]
|
|
443
451
|
self.message("({:g}, {:g})".format(x, y))
|
|
444
|
-
|
|
445
|
-
def on_figure_enter(self, evt):
|
|
452
|
+
|
|
453
|
+
def on_figure_enter(self, evt): #<matplotlib.backend_bases.MouseEvent>
|
|
446
454
|
pass
|
|
447
|
-
|
|
448
|
-
def on_figure_leave(self, evt):
|
|
455
|
+
|
|
456
|
+
def on_figure_leave(self, evt): #<matplotlib.backend_bases.MouseEvent>
|
|
449
457
|
self.cursor.clear(evt)
|
|
450
458
|
self.canvas.draw()
|
|
451
|
-
|
|
459
|
+
|
|
452
460
|
@property
|
|
453
|
-
def
|
|
461
|
+
def selector(self):
|
|
454
462
|
"""Selected points array [[x],[y]]."""
|
|
455
463
|
return np.array(self.selected.get_data(orig=0))
|
|
456
|
-
|
|
457
|
-
@
|
|
458
|
-
def
|
|
464
|
+
|
|
465
|
+
@selector.setter
|
|
466
|
+
def selector(self, v):
|
|
459
467
|
x, y = v
|
|
460
468
|
if not hasattr(x, '__iter__'):
|
|
461
469
|
x, y = [x], [y]
|
|
@@ -464,38 +472,38 @@ class MatplotPanel(wx.Panel):
|
|
|
464
472
|
self.handler('selector_drawn', self.frame)
|
|
465
473
|
self.draw(self.selected)
|
|
466
474
|
self.trace_point(*v)
|
|
467
|
-
|
|
468
|
-
@
|
|
469
|
-
def
|
|
475
|
+
|
|
476
|
+
@selector.deleter
|
|
477
|
+
def selector(self):
|
|
470
478
|
self.selected.set_visible(0)
|
|
471
479
|
self.selected.set_data([], [])
|
|
472
480
|
self.handler('selector_removed', self.frame)
|
|
473
481
|
self.draw(self.selected)
|
|
474
|
-
|
|
482
|
+
|
|
475
483
|
## --------------------------------
|
|
476
|
-
## matplotlib
|
|
484
|
+
## matplotlib interface.
|
|
477
485
|
## --------------------------------
|
|
478
|
-
|
|
486
|
+
|
|
479
487
|
@property
|
|
480
488
|
def p_event(self):
|
|
481
489
|
"""Last `pressed` event <matplotlib.backend_bases.MouseEvent>."""
|
|
482
490
|
return self.__isPressed
|
|
483
|
-
|
|
491
|
+
|
|
484
492
|
@p_event.setter
|
|
485
493
|
def p_event(self, v):
|
|
486
494
|
self.__isPressed = v
|
|
487
|
-
|
|
488
|
-
def on_menu_lock(self, evt):
|
|
495
|
+
|
|
496
|
+
def on_menu_lock(self, evt): #<matplotlib.backend_bases.MouseEvent>
|
|
489
497
|
self.__isMenu = 1
|
|
490
|
-
|
|
491
|
-
def on_menu(self, evt):
|
|
498
|
+
|
|
499
|
+
def on_menu(self, evt): #<matplotlib.backend_bases.MouseEvent>
|
|
492
500
|
if self.__isMenu:
|
|
493
501
|
self.canvas.SetFocus()
|
|
494
502
|
Menu.Popup(self, self.menu)
|
|
495
503
|
self.__isMenu = 0
|
|
496
|
-
|
|
497
|
-
def on_pick(self, evt):
|
|
498
|
-
"""Find index near (x,y) and set the
|
|
504
|
+
|
|
505
|
+
def on_pick(self, evt): #<matplotlib.backend_bases.PickEvent>
|
|
506
|
+
"""Find index near (x,y) and set the selector.
|
|
499
507
|
Called (maybe) after mouse button pressed.
|
|
500
508
|
"""
|
|
501
509
|
if evt.mouseevent.button != 1 or not evt.artist.get_visible():
|
|
@@ -517,22 +525,22 @@ class MatplotPanel(wx.Panel):
|
|
|
517
525
|
xs, ys = evt.artist.get_offsets().T
|
|
518
526
|
|
|
519
527
|
distances = np.hypot(x-xs[indices], y-ys[indices])
|
|
520
|
-
evt.index = k = indices[distances.argmin()]
|
|
528
|
+
evt.index = k = indices[distances.argmin()] # index of the nearest point
|
|
521
529
|
evt.xdata = x = xs[k]
|
|
522
530
|
evt.ydata = y = ys[k]
|
|
523
|
-
self.
|
|
531
|
+
self.selector = ([x], [y])
|
|
524
532
|
self.canvas.draw_idle()
|
|
525
533
|
self.handler('art_picked', evt)
|
|
526
534
|
self.message("({:g}, {:g}) index {}".format(x, y, evt.index))
|
|
527
|
-
|
|
528
|
-
def on_hotkey_press(self, evt):
|
|
535
|
+
|
|
536
|
+
def on_hotkey_press(self, evt): #<wx._core.KeyEvent>
|
|
529
537
|
"""Called when a key is pressed."""
|
|
530
538
|
key = hotkey(evt)
|
|
531
539
|
self.__key = regulate_key(key + '-')
|
|
532
540
|
if self.handler('{} pressed'.format(key), evt) is None:
|
|
533
541
|
evt.Skip()
|
|
534
|
-
|
|
535
|
-
def on_hotkey_down(self, evt):
|
|
542
|
+
|
|
543
|
+
def on_hotkey_down(self, evt): #<wx._core.KeyEvent>
|
|
536
544
|
"""Called when a key is pressed while dragging.
|
|
537
545
|
Specifically called when the mouse is being captured.
|
|
538
546
|
"""
|
|
@@ -540,15 +548,15 @@ class MatplotPanel(wx.Panel):
|
|
|
540
548
|
self.on_hotkey_press(evt)
|
|
541
549
|
else:
|
|
542
550
|
evt.Skip()
|
|
543
|
-
|
|
544
|
-
def on_hotkey_up(self, evt):
|
|
551
|
+
|
|
552
|
+
def on_hotkey_up(self, evt): #<wx._core.KeyEvent>
|
|
545
553
|
"""Called when a key is released."""
|
|
546
554
|
key = hotkey(evt)
|
|
547
555
|
self.__key = ''
|
|
548
556
|
if self.handler('{} released'.format(key), evt) is None:
|
|
549
557
|
evt.Skip()
|
|
550
|
-
|
|
551
|
-
def _on_mouse_event(self, evt):
|
|
558
|
+
|
|
559
|
+
def _on_mouse_event(self, evt): #<matplotlib.backend_bases.MouseEvent>
|
|
552
560
|
"""Called in mouse event handlers."""
|
|
553
561
|
if not evt.inaxes or evt.inaxes is not self.axes:
|
|
554
562
|
(evt.xdata, evt.ydata) = self.mapdisp2xy(evt.x, evt.y)
|
|
@@ -556,14 +564,14 @@ class MatplotPanel(wx.Panel):
|
|
|
556
564
|
## Overwrite evt.key with modifiers.
|
|
557
565
|
key = self.__key
|
|
558
566
|
if evt.button in (1,2,3):
|
|
559
|
-
key += 'LMR'[evt.button-1]
|
|
567
|
+
key += 'LMR'[evt.button-1] # {1:L, 2:M, 3:R}
|
|
560
568
|
evt.key = key + 'button'
|
|
561
569
|
elif evt.button in ('up', 'down'):
|
|
562
|
-
key += 'wheel{}'.format(evt.button)
|
|
570
|
+
key += 'wheel{}'.format(evt.button) # wheel[up|down]
|
|
563
571
|
evt.key = key
|
|
564
572
|
return key
|
|
565
|
-
|
|
566
|
-
def on_button_press(self, evt):
|
|
573
|
+
|
|
574
|
+
def on_button_press(self, evt): #<matplotlib.backend_bases.MouseEvent>
|
|
567
575
|
"""Called when the mouse button is pressed."""
|
|
568
576
|
self.p_event = evt
|
|
569
577
|
key = self._on_mouse_event(evt)
|
|
@@ -573,8 +581,8 @@ class MatplotPanel(wx.Panel):
|
|
|
573
581
|
else:
|
|
574
582
|
self.__isDragging = False
|
|
575
583
|
self.handler('{}button pressed'.format(key), evt)
|
|
576
|
-
|
|
577
|
-
def on_button_release(self, evt):
|
|
584
|
+
|
|
585
|
+
def on_button_release(self, evt): #<matplotlib.backend_bases.MouseEvent>
|
|
578
586
|
"""Called when the mouse button is released."""
|
|
579
587
|
key = self._on_mouse_event(evt)
|
|
580
588
|
if self.__isDragging:
|
|
@@ -582,12 +590,12 @@ class MatplotPanel(wx.Panel):
|
|
|
582
590
|
self.handler('{}drag end'.format(key), evt)
|
|
583
591
|
self.handler('{}button released'.format(key), evt)
|
|
584
592
|
else:
|
|
585
|
-
if self.__isDragging is None:
|
|
593
|
+
if self.__isDragging is None: # dblclick end
|
|
586
594
|
return
|
|
587
595
|
self.handler('{}button released'.format(key), evt)
|
|
588
596
|
self.p_event = None
|
|
589
|
-
|
|
590
|
-
def on_motion_notify(self, evt):
|
|
597
|
+
|
|
598
|
+
def on_motion_notify(self, evt): #<matplotlib.backend_bases.MouseEvent>
|
|
591
599
|
"""Called when the mouse is moved."""
|
|
592
600
|
key = self._on_mouse_event(evt)
|
|
593
601
|
if evt.button in (1,2,3):
|
|
@@ -607,60 +615,61 @@ class MatplotPanel(wx.Panel):
|
|
|
607
615
|
else:
|
|
608
616
|
return
|
|
609
617
|
self.handler('{} motion'.format(event), evt)
|
|
610
|
-
|
|
611
|
-
def on_scroll(self, evt):
|
|
618
|
+
|
|
619
|
+
def on_scroll(self, evt): #<matplotlib.backend_bases.MouseEvent>
|
|
612
620
|
"""Called when scrolling the mouse wheel."""
|
|
613
621
|
self.p_event = evt
|
|
614
622
|
key = self._on_mouse_event(evt)
|
|
615
623
|
self.handler('{} pressed'.format(key), evt)
|
|
616
624
|
self.p_event = None
|
|
617
|
-
|
|
625
|
+
|
|
618
626
|
## --------------------------------
|
|
619
|
-
## Pan/Zoom actions
|
|
627
|
+
## Pan/Zoom actions.
|
|
620
628
|
## --------------------------------
|
|
621
|
-
|
|
622
|
-
ZOOM_RATIO = 10**0.2
|
|
623
|
-
|
|
629
|
+
|
|
630
|
+
ZOOM_RATIO = 10 ** 0.2
|
|
631
|
+
ZOOM_LIMIT = 0.1 # logical limit <= epsilon
|
|
632
|
+
|
|
624
633
|
def OnDraw(self, evt):
|
|
625
634
|
"""Called before canvas.draw."""
|
|
626
635
|
pass
|
|
627
|
-
|
|
636
|
+
|
|
628
637
|
def OnMotion(self, evt):
|
|
629
638
|
"""Called when mouse moves in axes."""
|
|
630
|
-
if not self.
|
|
639
|
+
if not self.selector.size:
|
|
631
640
|
self.trace_point(evt.xdata, evt.ydata)
|
|
632
|
-
|
|
641
|
+
|
|
633
642
|
def OnForwardPosition(self, evt):
|
|
634
643
|
"""Go forward view position."""
|
|
635
644
|
self.toolbar.forward()
|
|
636
645
|
self.draw()
|
|
637
|
-
|
|
646
|
+
|
|
638
647
|
def OnBackPosition(self, evt):
|
|
639
648
|
"""Go backward view position."""
|
|
640
649
|
self.toolbar.back()
|
|
641
650
|
self.draw()
|
|
642
|
-
|
|
651
|
+
|
|
643
652
|
def OnHomePosition(self, evt):
|
|
644
653
|
"""Go back to home position."""
|
|
645
654
|
self.toolbar.home()
|
|
646
655
|
self.toolbar.update()
|
|
647
656
|
self.toolbar.push_current()
|
|
648
657
|
self.draw()
|
|
649
|
-
|
|
658
|
+
|
|
650
659
|
def OnEscapeSelection(self, evt):
|
|
651
660
|
"""Escape from selection."""
|
|
652
|
-
del self.
|
|
661
|
+
del self.selector
|
|
653
662
|
self.canvas.draw_idle()
|
|
654
|
-
|
|
663
|
+
|
|
655
664
|
def zoomlim(self, lim, M, c=None):
|
|
656
665
|
## The limitation of zoom is necessary; If the axes is enlarged too much,
|
|
657
666
|
## the processing speed will significantly slow down.
|
|
658
667
|
if c is None:
|
|
659
668
|
c = (lim[1] + lim[0]) / 2
|
|
660
669
|
y = c - M * (c - lim)
|
|
661
|
-
if abs(y[1] - y[0]) >
|
|
670
|
+
if abs(y[1] - y[0]) > self.ZOOM_LIMIT:
|
|
662
671
|
return y
|
|
663
|
-
|
|
672
|
+
|
|
664
673
|
def OnZoom(self, evt):
|
|
665
674
|
M = 1/self.ZOOM_RATIO if evt.key[-1] in '+;' else self.ZOOM_RATIO
|
|
666
675
|
self.xlim = x = self.zoomlim(self.xlim, M)
|
|
@@ -669,7 +678,7 @@ class MatplotPanel(wx.Panel):
|
|
|
669
678
|
if x is not None or y is not None:
|
|
670
679
|
self.toolbar.push_current()
|
|
671
680
|
self.draw()
|
|
672
|
-
|
|
681
|
+
|
|
673
682
|
def OnScrollZoom(self, evt):
|
|
674
683
|
M = 1/self.ZOOM_RATIO if evt.button == 'up' else self.ZOOM_RATIO
|
|
675
684
|
self.xlim = x = self.zoomlim(self.xlim, M, evt.xdata if evt.inaxes else None)
|
|
@@ -678,114 +687,114 @@ class MatplotPanel(wx.Panel):
|
|
|
678
687
|
if x is not None or y is not None:
|
|
679
688
|
self.toolbar.push_current()
|
|
680
689
|
self.draw()
|
|
681
|
-
|
|
690
|
+
|
|
682
691
|
def OnPanBegin(self, evt):
|
|
683
692
|
"""Toolbar pan - While panning, press x/y to constrain the direction."""
|
|
684
|
-
|
|
693
|
+
# self.toolbar.set_cursor(2)
|
|
685
694
|
self.set_wxcursor(wx.CURSOR_HAND)
|
|
686
695
|
self.toolbar.pan()
|
|
687
|
-
self.__prev = self.handler.previous_state
|
|
688
|
-
|
|
696
|
+
self.__prev = self.handler.previous_state # save previous state of PAN
|
|
697
|
+
|
|
689
698
|
def OnPanEnd(self, evt):
|
|
690
|
-
|
|
699
|
+
# self.toolbar.set_cursor(1)
|
|
691
700
|
self.set_wxcursor(wx.CURSOR_ARROW)
|
|
692
701
|
self.toolbar.pan()
|
|
693
702
|
self.handler.current_state = self.__prev # --> previous state of PAN
|
|
694
703
|
del self.__prev
|
|
695
|
-
|
|
704
|
+
|
|
696
705
|
def OnZoomBegin(self, evt):
|
|
697
706
|
"""Toolbar zoom - While zooming, press x/y to constrain the direction."""
|
|
698
|
-
|
|
707
|
+
# self.toolbar.set_cursor(3)
|
|
699
708
|
self.set_wxcursor(wx.CURSOR_CROSS)
|
|
700
709
|
self.toolbar.zoom()
|
|
701
|
-
self.__prev = self.handler.previous_state
|
|
702
|
-
|
|
710
|
+
self.__prev = self.handler.previous_state # save previous state of ZOOM
|
|
711
|
+
|
|
703
712
|
def OnZoomEnd(self, evt):
|
|
704
|
-
|
|
713
|
+
# self.toolbar.set_cursor(1)
|
|
705
714
|
self.set_wxcursor(wx.CURSOR_ARROW)
|
|
706
715
|
self.toolbar.zoom()
|
|
707
716
|
self.handler.current_state = self.__prev # --> previous state of ZOOM
|
|
708
717
|
del self.__prev
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
718
|
+
|
|
719
|
+
# def OnZoomMove(self, evt):
|
|
720
|
+
# """Zoom."""
|
|
721
|
+
# ## http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/189744
|
|
722
|
+
# ## matplotlib.backends.backend_wx - NavigationToolbar2Wx.draw_rubberband
|
|
723
|
+
# dc = wx.ClientDC(self.canvas)
|
|
724
|
+
#
|
|
725
|
+
# ## Set logical function to XOR for rubberbanding.
|
|
726
|
+
# dc.SetLogicalFunction(wx.XOR)
|
|
727
|
+
#
|
|
728
|
+
# ## Set dc brush and pen.
|
|
729
|
+
# wbrush = wx.Brush(wx.Colour(255,255,255), wx.TRANSPARENT)
|
|
730
|
+
# wpen = wx.Pen(wx.Colour(255,255,255), 1, wx.SOLID)
|
|
731
|
+
# dc.SetBrush(wbrush)
|
|
732
|
+
# dc.SetPen(wpen)
|
|
733
|
+
# dc.ResetBoundingBox()
|
|
734
|
+
# dc.BeginDrawing()
|
|
735
|
+
#
|
|
736
|
+
# height = self.canvas.figure.bbox.height
|
|
737
|
+
# org = self.p_event
|
|
738
|
+
# x0, y0 = org.x, org.y
|
|
739
|
+
# x1, y1 = evt.x, evt.y
|
|
740
|
+
# y0 = height - y0
|
|
741
|
+
# y1 = height - y1
|
|
742
|
+
# if y1 < y0: y0, y1 = y1, y0
|
|
743
|
+
# if x1 < y0: x0, x1 = x1, x0
|
|
744
|
+
# w = x1 - x0
|
|
745
|
+
# h = y1 - y0
|
|
746
|
+
# rect = int(x0), int(y0), int(w), int(h)
|
|
747
|
+
# try:
|
|
748
|
+
# dc.DrawRectangle(*self.__lastrect) #erase last
|
|
749
|
+
# except AttributeError:
|
|
750
|
+
# pass
|
|
751
|
+
#
|
|
752
|
+
# self.__lastrect = rect
|
|
753
|
+
# dc.DrawRectangle(*rect)
|
|
754
|
+
# dc.EndDrawing()
|
|
755
|
+
#
|
|
756
|
+
# def OnZoomEnd(self, evt):
|
|
757
|
+
# try:
|
|
758
|
+
# del self.__lastrect
|
|
759
|
+
# self.xbound = (self.p_event.xdata, evt.xdata)
|
|
760
|
+
# self.ybound = (self.p_event.ydata, evt.ydata)
|
|
761
|
+
# except AttributeError:
|
|
762
|
+
# pass
|
|
763
|
+
# self.toolbar.set_cursor(1)
|
|
764
|
+
# self.draw()
|
|
765
|
+
|
|
757
766
|
## --------------------------------
|
|
758
|
-
## Axis actions
|
|
767
|
+
## Axis actions.
|
|
759
768
|
## --------------------------------
|
|
760
|
-
|
|
769
|
+
|
|
761
770
|
def OnHomeXPosition(self, evt):
|
|
762
771
|
self.OnHomePosition(evt)
|
|
763
|
-
|
|
772
|
+
|
|
764
773
|
def OnHomeYPosition(self, evt):
|
|
765
774
|
self.OnHomePosition(evt)
|
|
766
|
-
|
|
775
|
+
|
|
767
776
|
def OnAxisEnter(self, evt):
|
|
768
777
|
self.set_wxcursor(wx.CURSOR_HAND)
|
|
769
|
-
|
|
778
|
+
|
|
770
779
|
def OnAxisLeave(self, evt):
|
|
771
780
|
self.set_wxcursor(wx.CURSOR_ARROW)
|
|
772
|
-
|
|
781
|
+
|
|
773
782
|
def OnAxisDragBegin(self, evt):
|
|
774
783
|
org = self.p_event
|
|
775
784
|
w, h = self.canvas.Size
|
|
776
785
|
p = self.canvas.ScreenToClient(wx.GetMousePosition())
|
|
777
786
|
org.x, org.y = (p[0], h-p[1])
|
|
778
|
-
org.xdata, org.ydata = self.mapdisp2xy(org.x, org.y)
|
|
779
|
-
|
|
787
|
+
org.xdata, org.ydata = self.mapdisp2xy(org.x, org.y) # p_event overwrites
|
|
788
|
+
|
|
780
789
|
def OnAxisDragEnd(self, evt):
|
|
781
790
|
self.toolbar.push_current()
|
|
782
791
|
if evt.inaxes:
|
|
783
792
|
self.handler('axes_enter', evt)
|
|
784
|
-
|
|
793
|
+
|
|
785
794
|
def OnXAxisPanMove(self, evt):
|
|
786
795
|
self.xlim -= (evt.xdata - self.p_event.xdata)
|
|
787
796
|
self.draw()
|
|
788
|
-
|
|
797
|
+
|
|
789
798
|
def OnXAxisPanZoom(self, evt, c=None):
|
|
790
799
|
org = self.p_event
|
|
791
800
|
M = np.exp(-(evt.x - org.x)/100)
|
|
@@ -794,14 +803,14 @@ class MatplotPanel(wx.Panel):
|
|
|
794
803
|
self.xlim = self.zoomlim(self.xlim, M, c)
|
|
795
804
|
org.x, org.y = evt.x, evt.y
|
|
796
805
|
self.draw()
|
|
797
|
-
|
|
806
|
+
|
|
798
807
|
def OnXAxisPanZoomOrig(self, evt):
|
|
799
808
|
self.OnXAxisPanZoom(evt, c=self.xlim[0])
|
|
800
|
-
|
|
809
|
+
|
|
801
810
|
def OnYAxisPanMove(self, evt):
|
|
802
811
|
self.ylim -= (evt.ydata - self.p_event.ydata)
|
|
803
812
|
self.draw()
|
|
804
|
-
|
|
813
|
+
|
|
805
814
|
def OnYAxisPanZoom(self, evt, c=None):
|
|
806
815
|
org = self.p_event
|
|
807
816
|
M = np.exp(-(evt.y - org.y)/100)
|
|
@@ -810,9 +819,9 @@ class MatplotPanel(wx.Panel):
|
|
|
810
819
|
self.ylim = self.zoomlim(self.ylim, M, c)
|
|
811
820
|
org.x, org.y = evt.x, evt.y
|
|
812
821
|
self.draw()
|
|
813
|
-
|
|
822
|
+
|
|
814
823
|
def OnYAxisPanZoomOrig(self, evt):
|
|
815
824
|
self.OnYAxisPanZoom(evt, c=self.ylim[0])
|
|
816
|
-
|
|
825
|
+
|
|
817
826
|
def OnYAxisPanZoomEdge(self, evt):
|
|
818
827
|
self.OnYAxisPanZoom(evt, c=self.ylim[1])
|