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/matplot2.py CHANGED
@@ -3,19 +3,18 @@
3
3
  """
4
4
  import wx
5
5
 
6
- import matplotlib; matplotlib.use('wxagg') # noqa
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
- ## state constants
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
- ## (local) Monkey-patch for matplotlib 3.8/WXAgg
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) # >= 3.8 only
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 : <matplotlib.figure.Figure>
75
- canvas : <matplotlib.backends.backend_wxagg.FigureCanvasWxAgg>
76
- toolbar : <matplotlib.backends.backend_wx.NavigationToolbar2Wx>
77
- cursor : <matplotlib.widgets.Cursor>
78
- selected : Selected points <matplotlib.lines.Line2D>
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)) # inches
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('self._cachedRenderer is not None')
94
- ## To avoid AttributeError("draw_artist can only be used after an "
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) # workaround for incorrect wrap sizing
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)) # if margin is None
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
- ## self.canvas.mpl_connect('key_press_event', self.on_key_press)
131
- ## self.canvas.mpl_connect('key_release_event', self.on_key_release)
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
- ## self.canvas.mpl_connect('resize_event', lambda v: self.handler('canvas_resized', v))
141
- ## self.canvas.mpl_connect('draw_event', lambda v: self.handler('canvas_drawn', v))
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 ], # before canvas.draw
166
- #'canvas_drawn' : [ None, ], # after canvas.draw
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
- 'M-p pressed' : [ None, self.OnBackPosition ],
180
- 'M-n pressed' : [ None, self.OnForwardPosition ],
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
- ## (mwx.ID_(502), "&Export image", "Save canvas image",
289
- ## lambda v: self.save_to_file()),
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 # True if dragging. (None if dblclicked)
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) # cf. add_axes(rect=(l,b,w,h))
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
- ## self.canvas.draw_idle()
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
- ## to be overridden (referenced in draw).
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="x-axis numerical bounds where lowerBound < upperBound)")
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="y-axis numerical bounds where lowerBound < upperBound)")
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="x-axis range [left, right]")
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="y-axis range [bottom, top]")
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
- ## return self.mapxy2disp(1,1) - self.mapxy2disp(0,0)
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-coordinates."""
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-coordinates --> xydata."""
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): #<wx._core.MouseEvent>
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.ToolTip = self.modeline.Label
398
+ self.modeline.SetToolTip(self.modeline.Label)
391
399
  evt.Skip()
392
-
393
- def on_focus_set(self, evt): #<wx._core.FocusEvent>
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): #<wx._core.FocusEvent>
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
- ## b = self.selected.get_visible()
429
+ # b = self.selected.get_visible()
422
430
  c = self.cursor.visible
423
431
  try:
424
- ## self.selected.set_visible(0)
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
- ## self.selected.set_visible(b)
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): #<matplotlib.backend_bases.MouseEvent>
452
+
453
+ def on_figure_enter(self, evt): #<matplotlib.backend_bases.MouseEvent>
446
454
  pass
447
-
448
- def on_figure_leave(self, evt): #<matplotlib.backend_bases.MouseEvent>
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 Selector(self):
461
+ def selector(self):
454
462
  """Selected points array [[x],[y]]."""
455
463
  return np.array(self.selected.get_data(orig=0))
456
-
457
- @Selector.setter
458
- def Selector(self, v):
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
- @Selector.deleter
469
- def Selector(self):
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 interfaces
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): #<matplotlib.backend_bases.MouseEvent>
495
+
496
+ def on_menu_lock(self, evt): #<matplotlib.backend_bases.MouseEvent>
489
497
  self.__isMenu = 1
490
-
491
- def on_menu(self, evt): #<matplotlib.backend_bases.MouseEvent>
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): #<matplotlib.backend_bases.PickEvent>
498
- """Find index near (x,y) and set the Selector.
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()] # index of the nearest point
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.Selector = ([x], [y])
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): #<wx._core.KeyEvent>
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): #<wx._core.KeyEvent>
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): #<wx._core.KeyEvent>
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): #<matplotlib.backend_bases.MouseEvent>
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] #{1:L, 2:M, 3:R}
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) # wheel[up|down]
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): #<matplotlib.backend_bases.MouseEvent>
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): #<matplotlib.backend_bases.MouseEvent>
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: # dblclick end
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): #<matplotlib.backend_bases.MouseEvent>
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): #<matplotlib.backend_bases.MouseEvent>
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.Selector.size:
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.Selector
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]) > 0.1 or M > 1:
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
- ## self.toolbar.set_cursor(2)
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 # save previous state of PAN
688
-
696
+ self.__prev = self.handler.previous_state # save previous state of PAN
697
+
689
698
  def OnPanEnd(self, evt):
690
- ## self.toolbar.set_cursor(1)
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
- ## self.toolbar.set_cursor(3)
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 # save previous state of ZOOM
702
-
710
+ self.__prev = self.handler.previous_state # save previous state of ZOOM
711
+
703
712
  def OnZoomEnd(self, evt):
704
- ## self.toolbar.set_cursor(1)
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
- ## def OnZoomMove(self, evt):
711
- ## """Zoom."""
712
- ## ## http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/189744
713
- ## ## matplotlib.backends.backend_wx - NavigationToolbar2Wx.draw_rubberband
714
- ## dc = wx.ClientDC(self.canvas)
715
- ##
716
- ## ## Set logical function to XOR for rubberbanding
717
- ## dc.SetLogicalFunction(wx.XOR)
718
- ##
719
- ## ## Set dc brush and pen
720
- ## wbrush = wx.Brush(wx.Colour(255,255,255), wx.TRANSPARENT)
721
- ## wpen = wx.Pen(wx.Colour(255,255,255), 1, wx.SOLID)
722
- ## dc.SetBrush(wbrush)
723
- ## dc.SetPen(wpen)
724
- ## dc.ResetBoundingBox()
725
- ## dc.BeginDrawing()
726
- ##
727
- ## height = self.canvas.figure.bbox.height
728
- ## org = self.p_event
729
- ## x0, y0 = org.x, org.y
730
- ## x1, y1 = evt.x, evt.y
731
- ## y0 = height - y0
732
- ## y1 = height - y1
733
- ## if y1 < y0: y0, y1 = y1, y0
734
- ## if x1 < y0: x0, x1 = x1, x0
735
- ## w = x1 - x0
736
- ## h = y1 - y0
737
- ## rect = int(x0), int(y0), int(w), int(h)
738
- ## try:
739
- ## dc.DrawRectangle(*self.__lastrect) #erase last
740
- ## except AttributeError:
741
- ## pass
742
- ##
743
- ## self.__lastrect = rect
744
- ## dc.DrawRectangle(*rect)
745
- ## dc.EndDrawing()
746
- ##
747
- ## def OnZoomEnd(self, evt):
748
- ## try:
749
- ## del self.__lastrect
750
- ## self.xbound = (self.p_event.xdata, evt.xdata)
751
- ## self.ybound = (self.p_event.ydata, evt.ydata)
752
- ## except AttributeError:
753
- ## pass
754
- ## self.toolbar.set_cursor(1)
755
- ## self.draw()
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) # p_event overwrites
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])