mwxlib 1.5.0__py3-none-any.whl → 1.5.9__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.

Potentially problematic release.


This version of mwxlib might be problematic. Click here for more details.

mwx/graphman.py CHANGED
@@ -113,7 +113,6 @@ class Thread:
113
113
  Allows only this worker (but no other thread) to enter.
114
114
  """
115
115
  frame = inspect.currentframe().f_back.f_back
116
- filename = frame.f_code.co_filename
117
116
  name = frame.f_code.co_name
118
117
 
119
118
  ## Other threads are not allowed to enter.
@@ -125,12 +124,16 @@ class Thread:
125
124
  yield self
126
125
 
127
126
  def enters(self, f):
128
- """Decorator to register a one-time handler for the enter event."""
129
- return self.handler.binds('thread_begin', f)
127
+ """Decorator to add a one-time handler for the enter event.
128
+ The specified function will be called from the main thread.
129
+ """
130
+ return self.handler.binds('thread_begin', _F(f))
130
131
 
131
132
  def exits(self, f):
132
- """Decorator to register a one-time handler for the exit event."""
133
- return self.handler.binds('thread_end', f)
133
+ """Decorator to add a one-time handler for the exit event.
134
+ The specified function will be called from the main thread.
135
+ """
136
+ return self.handler.binds('thread_end', _F(f))
134
137
 
135
138
  def wraps(self, f, *args, **kwargs):
136
139
  """Decorator for a function that starts a new thread."""
@@ -190,11 +193,14 @@ class Thread:
190
193
  pass
191
194
  except KeyboardInterrupt as e:
192
195
  print("- Thread terminated by user:", e)
193
- ## wx.CallAfter(self.handler, 'thread_quit', self)
194
196
  except Exception as e:
197
+ tbstr = traceback.format_tb(e.__traceback__)
198
+ wx.CallAfter(wx.MessageBox,
199
+ f"{e}\n\n" + tbstr[-1] + f"{type(e).__name__}: {e}",
200
+ f"Error in the thread running {f.__name__!r}\n\n",
201
+ style=wx.ICON_ERROR)
195
202
  traceback.print_exc()
196
203
  print("- Thread failed in error:", e)
197
- ## wx.CallAfter(self.handler, 'thread_error', self)
198
204
  finally:
199
205
  self.active = 0
200
206
  wx.CallAfter(self.handler, 'thread_end', self)
@@ -226,7 +232,7 @@ class Thread:
226
232
  self.worker.join(1)
227
233
  if self.running:
228
234
  self.active = 0
229
- wx.CallAfter(_stop) # main-thread で終了させる
235
+ wx.CallAfter(_stop) # main thread で終了させる
230
236
 
231
237
 
232
238
  class LayerInterface(CtrlInterface):
@@ -440,7 +446,7 @@ class LayerInterface(CtrlInterface):
440
446
 
441
447
  Shown = property(
442
448
  lambda self: self.IsShown(),
443
- lambda self,v: self.Show(v))
449
+ lambda self, v: self.Show(v))
444
450
 
445
451
  def IsShown(self):
446
452
  """Return True if the window is physically visible on the screen.
@@ -457,11 +463,11 @@ class LayerInterface(CtrlInterface):
457
463
  (override) Show associated pane window.
458
464
  Note: This might be called from a thread.
459
465
  """
460
- wx.CallAfter(self.parent.show_pane, self, show)
466
+ wx.CallAfter(self.parent.show_pane, self, show) # Show pane windows in the main thread.
461
467
 
462
468
  Drawn = property(
463
469
  lambda self: self.IsDrawn(),
464
- lambda self,v: self.Draw(v))
470
+ lambda self, v: self.Draw(v))
465
471
 
466
472
  def IsDrawn(self):
467
473
  return any(art.get_visible() for art in self.Arts)
@@ -495,6 +501,30 @@ class Layer(LayerInterface, KnobCtrlPanel):
495
501
  LayerInterface.__init__(self, parent, session)
496
502
 
497
503
 
504
+ def register(cls, module=None):
505
+ """Register dummy plug; Add module.Plugin <Layer>.
506
+ """
507
+ if not module:
508
+ module = inspect.getmodule(cls) # rebase module or __main__
509
+
510
+ if issubclass(cls, LayerInterface):
511
+ cls.__module__ = module.__name__ # __main__ to module
512
+ warn(f"Duplicate iniheritance of LayerInterface by {cls}.")
513
+ module.Plugin = cls
514
+ return cls
515
+
516
+ class _Plugin(LayerInterface, cls):
517
+ def __init__(self, parent, session=None, **kwargs):
518
+ cls.__init__(self, parent, **kwargs)
519
+ LayerInterface.__init__(self, parent, session)
520
+
521
+ _Plugin.__module__ = cls.__module__ = module.__name__
522
+ _Plugin.__name__ = cls.__name__ + str("~")
523
+ _Plugin.__doc__ = cls.__doc__
524
+ module.Plugin = _Plugin
525
+ return _Plugin
526
+
527
+
498
528
  class Graph(GraphPlot):
499
529
  """GraphPlot (override) to better make use for graph manager
500
530
 
@@ -575,7 +605,7 @@ class MyFileDropLoader(wx.FileDropTarget):
575
605
  self.loader = loader
576
606
 
577
607
  def OnDropFiles(self, x, y, filenames):
578
- pos = self.view.ScreenPosition + (x,y)
608
+ pos = self.view.ScreenPosition + (x, y)
579
609
  paths = []
580
610
  for fn in filenames:
581
611
  name, ext = os.path.splitext(fn)
@@ -652,7 +682,7 @@ class Frame(mwx.Frame):
652
682
  self.histogram.Name = "histogram"
653
683
 
654
684
  self._mgr.AddPane(self.graph,
655
- aui.AuiPaneInfo().CenterPane().CloseButton(1)
685
+ aui.AuiPaneInfo().CenterPane()
656
686
  .Name("graph").Caption("graph").CaptionVisible(1))
657
687
 
658
688
  size = (200, 200)
@@ -777,7 +807,7 @@ class Frame(mwx.Frame):
777
807
  self.menubar.reset()
778
808
 
779
809
  def show_frameview(frame):
780
- wx.CallAfter(self.show_pane, frame.parent) # Show graph / output
810
+ wx.CallAfter(self.show_pane, frame.parent) # Show graph / output in the main thread.
781
811
 
782
812
  self.graph.handler.append({ # DNA<Graph:Frame>
783
813
  None : {
@@ -816,8 +846,8 @@ class Frame(mwx.Frame):
816
846
  _display(self.graph, show)
817
847
  _display(self.output, show)
818
848
  evt.Skip()
819
- self.Bind(wx.EVT_MOVE_START, lambda v :on_move(v, show=0))
820
- self.Bind(wx.EVT_MOVE_END, lambda v :on_move(v, show=1))
849
+ self.Bind(wx.EVT_MOVE_START, lambda v: on_move(v, show=0))
850
+ self.Bind(wx.EVT_MOVE_END, lambda v: on_move(v, show=1))
821
851
 
822
852
  ## Custom Key Bindings
823
853
  self.define_key('* C-g', self.Quit)
@@ -835,13 +865,13 @@ class Frame(mwx.Frame):
835
865
  def sync(self, a, b):
836
866
  """Synchronize b to a."""
837
867
  if (self.SYNC_SWITCH
838
- and a.frame and b.frame
839
- and a.frame.unit == b.frame.unit
840
- and a.buffer.shape == b.buffer.shape):
841
- b.xlim = a.xlim
842
- b.ylim = a.ylim
843
- b.OnDraw(None)
844
- b.canvas.draw_idle()
868
+ and a.frame and b.frame
869
+ and a.frame.unit == b.frame.unit
870
+ and a.buffer.shape == b.buffer.shape):
871
+ b.xlim = a.xlim
872
+ b.ylim = a.ylim
873
+ b.OnDraw(None)
874
+ b.canvas.draw_idle()
845
875
 
846
876
  def set_title(self, frame):
847
877
  ssn = os.path.basename(self.session_file or '--')
@@ -919,7 +949,7 @@ class Frame(mwx.Frame):
919
949
  ## ドッキング時に再計算される
920
950
  if name == "output" or name is self.output:
921
951
  w, h = self.graph.GetClientSize()
922
- pane.best_size = (w//2 - 3, h) # 分割線幅補正 -12pix (Windows only ?)
952
+ pane.best_size = (w//2 - 3, h) # 分割線幅補正 -12pix (Windows only ?)
923
953
 
924
954
  ## Force Layer windows to show.
925
955
  if interactive:
@@ -935,12 +965,8 @@ class Frame(mwx.Frame):
935
965
  pane.Float()
936
966
  show = True
937
967
 
938
- ## Note: We need to distinguish cases whether:
939
- ## - pane.window is AuiNotebook or normal Panel,
940
- ## - pane.window is floating (win.Parent is AuiFloatingFrame) or docked.
941
-
942
- plug = self.get_plug(name) # -> None if pane.window is a Graph
943
- win = pane.window # -> Window (plug / notebook / Graph)
968
+ plug = self.get_plug(name) # -> None if pane.window is a Graph
969
+ win = pane.window
944
970
  try:
945
971
  shown = plug.IsShown()
946
972
  except AttributeError:
@@ -950,7 +976,7 @@ class Frame(mwx.Frame):
950
976
  if isinstance(win, aui.AuiNotebook):
951
977
  j = win.GetPageIndex(plug)
952
978
  if j != win.Selection:
953
- win.Selection = j # the focus moves => EVT_SHOW
979
+ win.Selection = j # the focus moves => EVT_SHOW
954
980
  else:
955
981
  plug.handler('page_shown', plug)
956
982
  else:
@@ -991,7 +1017,7 @@ class Frame(mwx.Frame):
991
1017
  pane.dock_direction = dock
992
1018
  if not plug.caption:
993
1019
  pane.CaptionVisible(False) # no caption bar
994
- pane.Gripper(dock not in (0, 5)) # show a grip when docked
1020
+ pane.Gripper(dock not in (0,5)) # show a grip when docked
995
1021
  pane.Dockable(dock)
996
1022
 
997
1023
  if pane.dock_direction:
@@ -1042,30 +1068,6 @@ class Frame(mwx.Frame):
1042
1068
  elif isinstance(name, LayerInterface):
1043
1069
  return name
1044
1070
 
1045
- @staticmethod
1046
- def register(cls, module=None):
1047
- """Register dummy plug; Add module.Plugin <Layer>.
1048
- """
1049
- if not module:
1050
- module = inspect.getmodule(cls) # rebase module or __main__
1051
-
1052
- if issubclass(cls, LayerInterface):
1053
- cls.__module__ = module.__name__ # __main__ to module
1054
- warn(f"Duplicate iniheritance of LayerInterface by {cls}.")
1055
- module.Plugin = cls
1056
- return cls
1057
-
1058
- class _Plugin(LayerInterface, cls):
1059
- def __init__(self, parent, session=None, **kwargs):
1060
- cls.__init__(self, parent, **kwargs)
1061
- LayerInterface.__init__(self, parent, session)
1062
-
1063
- _Plugin.__module__ = cls.__module__ = module.__name__
1064
- _Plugin.__name__ = cls.__name__ + str("~")
1065
- _Plugin.__doc__ = cls.__doc__
1066
- module.Plugin = _Plugin
1067
- return _Plugin
1068
-
1069
1071
  def load_module(self, root):
1070
1072
  """Load module of plugin (internal use only).
1071
1073
 
@@ -1094,17 +1096,17 @@ class Frame(mwx.Frame):
1094
1096
  print(f"- Unable to load {root!r}.", e)
1095
1097
  return False
1096
1098
 
1097
- ## the module must have a class `Plugin`.
1099
+ ## Check if the module has a class `Plugin`.
1098
1100
  if not hasattr(module, 'Plugin'):
1099
1101
  if isinstance(root, type):
1100
1102
  warn(f"Use dummy plug for debugging {name!r}.")
1101
1103
  module.__dummy_plug__ = root
1102
- self.register(root, module)
1104
+ register(root, module)
1103
1105
  else:
1104
1106
  if hasattr(module, '__dummy_plug__'):
1105
1107
  root = module.__dummy_plug__ # old class (imported)
1106
1108
  cls = getattr(module, root.__name__) # new class (reloaded)
1107
- self.register(cls, module)
1109
+ register(cls, module)
1108
1110
  return module
1109
1111
 
1110
1112
  def load_plug(self, root, force=False, session=None, show=False,
@@ -1151,48 +1153,47 @@ class Frame(mwx.Frame):
1151
1153
 
1152
1154
  module = self.load_module(root)
1153
1155
  if not module:
1154
- return False # failed to import
1156
+ return False
1155
1157
 
1158
+ ## assert name == Plugin.__module__
1156
1159
  try:
1157
- name = module.Plugin.__module__
1158
- title = module.Plugin.category
1159
-
1160
- pane = self._mgr.GetPane(title)
1160
+ Plugin = module.Plugin # Check if the module has a class `Plugin`.
1161
+ title = Plugin.category # Plugin <LayerInterface>
1161
1162
 
1162
- if pane.IsOk(): # <pane:title> is already registered
1163
- nb = pane.window
1164
- if not isinstance(nb, aui.AuiNotebook):
1165
- raise NameError("Notebook name must not be the same as any other plugins")
1166
-
1167
- pane = self.get_pane(name)
1163
+ pane = self._mgr.GetPane(title) # Check if <pane:title> is already registered.
1164
+ if pane.IsOk():
1165
+ if not isinstance(pane.window, aui.AuiNotebook):
1166
+ raise NameError("Notebook name must not be the same as any other plugin")
1168
1167
 
1169
- if pane.IsOk(): # <pane:name> is already registered
1168
+ pane = self.get_pane(name) # Check if <pane:name> is already registered.
1169
+ if pane.IsOk():
1170
1170
  if name not in self.plugins:
1171
- raise NameError("Plugin name must not be the same as any other panes")
1172
-
1173
- show = show or pane.IsShown()
1174
- props.update(
1175
- dock_direction = pane.IsDocked() and pane.dock_direction,
1176
- floating_pos = floating_pos or pane.floating_pos[:], # copy unloading pane
1177
- floating_size = floating_size or pane.floating_size[:], # copy unloading pane
1178
- )
1171
+ raise NameError("Plugin name must not be the same as any other pane")
1172
+
1179
1173
  except (AttributeError, NameError) as e:
1180
1174
  traceback.print_exc()
1181
- wx.CallAfter(wx.MessageBox,
1175
+ wx.CallAfter(wx.MessageBox, # Show the message after load_session has finished.
1182
1176
  f"{e}\n\n" + traceback.format_exc(),
1183
1177
  f"Error in loading {module.__name__!r}",
1184
1178
  style=wx.ICON_ERROR)
1185
1179
  return False
1186
1180
 
1187
- ## Create and register the plugin
1181
+ ## Unload the plugin if loaded.
1188
1182
  if pane.IsOk():
1189
- self.unload_plug(name) # unload once right here
1183
+ show = show or pane.IsShown()
1184
+ props.update(
1185
+ dock_direction = pane.IsDocked() and pane.dock_direction,
1186
+ floating_pos = floating_pos or pane.floating_pos[:], # copy unloading pane
1187
+ floating_size = floating_size or pane.floating_size[:], # copy unloading pane
1188
+ )
1189
+ self.unload_plug(name)
1190
1190
 
1191
+ ## Create the plugin object.
1191
1192
  try:
1192
- plug = module.Plugin(self, session, **kwargs)
1193
+ plug = Plugin(self, session, **kwargs)
1193
1194
  except Exception as e:
1194
1195
  traceback.print_exc()
1195
- wx.CallAfter(wx.MessageBox,
1196
+ wx.CallAfter(wx.MessageBox, # Show the message after load_session has finished.
1196
1197
  f"{e}\n\n" + traceback.format_exc(),
1197
1198
  f"Error in loading {name!r}",
1198
1199
  style=wx.ICON_ERROR)
@@ -1201,10 +1202,10 @@ class Frame(mwx.Frame):
1201
1202
  ## Add to the list after the plug is created successfully.
1202
1203
  self.plugins[name] = module
1203
1204
 
1204
- ## set reference of a plug (one module, one plugin)
1205
+ ## Set reference of a plug (one module, one plugin).
1205
1206
  module.__plug__ = plug
1206
1207
 
1207
- ## Create pane or notebook pane
1208
+ ## Create pane or notebook pane.
1208
1209
  caption = plug.caption
1209
1210
  if not isinstance(caption, str):
1210
1211
  caption = name
@@ -1216,7 +1217,7 @@ class Frame(mwx.Frame):
1216
1217
  nb = pane.window
1217
1218
  nb.AddPage(plug, caption)
1218
1219
  else:
1219
- size = plug.GetSize() + (2,30) # padding for notebook
1220
+ size = plug.GetSize() + (2,30) # padding for notebook
1220
1221
  nb = AuiNotebook(self, name=title)
1221
1222
  nb.AddPage(plug, caption)
1222
1223
  self._mgr.AddPane(nb, aui.AuiPaneInfo()
@@ -1238,11 +1239,11 @@ class Frame(mwx.Frame):
1238
1239
  self.update_pane(name, **props)
1239
1240
  self.show_pane(name, show)
1240
1241
 
1241
- ## Create a menu
1242
+ ## Create a menu.
1242
1243
  plug.__Menu_item = None
1243
1244
 
1244
- if not hasattr(module, 'ID_'): # give a unique index to the module
1245
- global __plug_ID__ # cache ID *not* in [ID_LOWEST(4999):ID_HIGHEST(5999)]
1245
+ if not hasattr(module, 'ID_'): # give a unique index to the module
1246
+ global __plug_ID__ # cache ID *not* in [ID_LOWEST(4999):ID_HIGHEST(5999)]
1246
1247
  try:
1247
1248
  __plug_ID__
1248
1249
  except NameError:
@@ -1314,11 +1315,6 @@ class Frame(mwx.Frame):
1314
1315
  traceback.print_exc()
1315
1316
  print("- Failed to save session of", plug)
1316
1317
  self.load_plug(plug.__module__, force=1, session=session)
1317
-
1318
- ## Update shell.target --> new plug
1319
- for shell in self.shellframe.get_all_shells():
1320
- if shell.target is plug:
1321
- shell.handler('shell_activated', shell)
1322
1318
 
1323
1319
  def inspect_plug(self, name):
1324
1320
  """Dive into the process to inspect plugs in the shell.
@@ -1331,10 +1327,11 @@ class Frame(mwx.Frame):
1331
1327
 
1332
1328
  @shell.handler.bind("shell_activated")
1333
1329
  def init(shell):
1330
+ """Called when the plug shell is activated."""
1334
1331
  nonlocal plug
1335
1332
  _plug = self.get_plug(name)
1336
1333
  if _plug is not plug:
1337
- shell.target = _plug or self # reset for loaded/unloaded plug
1334
+ shell.target = _plug or self # Reset the target to the reloaded plug.
1338
1335
  plug = _plug
1339
1336
  init(shell)
1340
1337
  self.shellframe.Show()
@@ -1451,7 +1448,7 @@ class Frame(mwx.Frame):
1451
1448
  return frames
1452
1449
 
1453
1450
  ## --------------------------------
1454
- ## load/save frames and attributes
1451
+ ## load/save frames and attributes
1455
1452
  ## --------------------------------
1456
1453
 
1457
1454
  @classmethod
@@ -1736,14 +1733,18 @@ class Frame(mwx.Frame):
1736
1733
  self._mgr.Update()
1737
1734
  self.menubar.reset()
1738
1735
 
1739
- dirname_ = os.path.dirname(i.name)
1740
- if dirname_:
1741
- os.chdir(dirname_)
1742
-
1743
1736
  ## Reposition the window if it is not on the desktop.
1744
1737
  if wx.Display.GetFromWindow(self) == -1:
1745
1738
  self.Position = (0, 0)
1746
1739
 
1740
+ ## LoadPerspective => 表示状態の不整合.手動でイベントを発生させる.
1741
+ for pane in self._mgr.GetAllPanes():
1742
+ if pane.IsShown():
1743
+ win = pane.window
1744
+ if isinstance(win, aui.AuiNotebook):
1745
+ win = win.CurrentPage
1746
+ win.handler('page_shown', win)
1747
+
1747
1748
  self.message("\b done.")
1748
1749
 
1749
1750
  def save_session_as(self):
@@ -1791,8 +1792,8 @@ class Frame(mwx.Frame):
1791
1792
  o.write(f"self.{view.Name}.unit = {view.unit:g}\n")
1792
1793
  o.write(f"self.load_frame({paths!r}, self.{view.Name})\n")
1793
1794
  try:
1794
- index = paths.index(view.frame.pathname)
1795
- o.write(f"self.{view.Name}.select({index})\n")
1795
+ if view.frame.pathname in paths:
1796
+ o.write(f"self.{view.Name}.select({view.frame.name!r})\n")
1796
1797
  except Exception:
1797
1798
  pass
1798
1799
 
mwx/matplot2.py CHANGED
@@ -11,7 +11,7 @@ from matplotlib.figure import Figure
11
11
  import numpy as np
12
12
 
13
13
  from . import framework as mwx
14
- from .framework import hotkey, regulate_key, pack, Menu, FSM
14
+ from .framework import hotkey, regulate_key, postcall, pack, Menu, FSM
15
15
 
16
16
 
17
17
  ## state constants
@@ -178,8 +178,6 @@ class MatplotPanel(wx.Panel):
178
178
  'axes_enter' : [ None, ],
179
179
  'axes_leave' : [ None, ],
180
180
  'home pressed' : [ None, self.OnHomePosition ],
181
- 'left pressed' : [ None, self.OnBackPosition ],
182
- 'right pressed' : [ None, self.OnForwardPosition ],
183
181
  'Xbutton1 pressed' : [ None, self.OnBackPosition ],
184
182
  'Xbutton2 pressed' : [ None, self.OnForwardPosition ],
185
183
  },
@@ -314,6 +312,8 @@ class MatplotPanel(wx.Panel):
314
312
  self.cursor = Cursor(self.axes, useblit=True, color='grey', linewidth=1)
315
313
  self.cursor.visible = 1
316
314
 
315
+ ## Note: To avoid a wxAssertionError when running in a thread.
316
+ @postcall
317
317
  def draw(self, art=None):
318
318
  """Draw plots.
319
319
  Call each time the drawing should be updated.
@@ -345,22 +345,22 @@ class MatplotPanel(wx.Panel):
345
345
 
346
346
  xbound = property(
347
347
  lambda self: np.array(self.axes.get_xbound()),
348
- lambda self,v: self.axes.set_xbound(v),
348
+ lambda self, v: self.axes.set_xbound(v),
349
349
  doc="X-axis numerical bounds where lowerBound < upperBound)")
350
350
 
351
351
  ybound = property(
352
352
  lambda self: np.array(self.axes.get_ybound()),
353
- lambda self,v: self.axes.set_ybound(v),
353
+ lambda self, v: self.axes.set_ybound(v),
354
354
  doc="Y-axis numerical bounds where lowerBound < upperBound)")
355
355
 
356
356
  xlim = property(
357
357
  lambda self: np.array(self.axes.get_xlim()),
358
- lambda self,v: self.axes.set_xlim(v),
358
+ lambda self, v: self.axes.set_xlim(v),
359
359
  doc="X-axis range [left, right]")
360
360
 
361
361
  ylim = property(
362
362
  lambda self: np.array(self.axes.get_ylim()),
363
- lambda self,v: self.axes.set_ylim(v),
363
+ lambda self, v: self.axes.set_ylim(v),
364
364
  doc="Y-axis range [bottom, top]")
365
365
 
366
366
  @property
@@ -418,7 +418,7 @@ class MatplotPanel(wx.Panel):
418
418
  wx.UIActionSimulator().KeyUp(wx.WXK_ESCAPE)
419
419
 
420
420
  ## --------------------------------
421
- ## External I/O file and clipboard
421
+ ## External I/O file and clipboard
422
422
  ## --------------------------------
423
423
 
424
424
  def copy_to_clipboard(self):
@@ -561,7 +561,7 @@ class MatplotPanel(wx.Panel):
561
561
  ## Overwrite evt.key with modifiers.
562
562
  key = self.__key
563
563
  if evt.button in (1,2,3):
564
- key += 'LMR'[evt.button-1] #{1:L, 2:M, 3:R}
564
+ key += 'LMR'[evt.button-1] # {1:L, 2:M, 3:R}
565
565
  evt.key = key + 'button'
566
566
  elif evt.button in ('up', 'down'):
567
567
  key += 'wheel{}'.format(evt.button) # wheel[up|down]
@@ -621,10 +621,11 @@ class MatplotPanel(wx.Panel):
621
621
  self.p_event = None
622
622
 
623
623
  ## --------------------------------
624
- ## Pan/Zoom actions
624
+ ## Pan/Zoom actions
625
625
  ## --------------------------------
626
626
 
627
- ZOOM_RATIO = 10**0.2
627
+ ZOOM_RATIO = 10 ** 0.2
628
+ ZOOM_LIMIT = 0.1 # logical limit <= epsilon
628
629
 
629
630
  def OnDraw(self, evt):
630
631
  """Called before canvas.draw."""
@@ -663,7 +664,7 @@ class MatplotPanel(wx.Panel):
663
664
  if c is None:
664
665
  c = (lim[1] + lim[0]) / 2
665
666
  y = c - M * (c - lim)
666
- if abs(y[1] - y[0]) > 0.1 or M > 1:
667
+ if abs(y[1] - y[0]) > self.ZOOM_LIMIT:
667
668
  return y
668
669
 
669
670
  def OnZoom(self, evt):
mwx/matplot2g.py CHANGED
@@ -109,9 +109,9 @@ def _to_image(src, cutoff=0, threshold=None, binning=1):
109
109
 
110
110
  def _Property(name):
111
111
  return property(
112
- lambda self: getattr(self.parent, name),
113
- lambda self,v: setattr(self.parent, name, v),
114
- lambda self: delattr(self.parent, name))
112
+ lambda self: getattr(self.parent, name),
113
+ lambda self, v: setattr(self.parent, name, v),
114
+ lambda self: delattr(self.parent, name))
115
115
 
116
116
 
117
117
  class AxesImagePhantom:
@@ -227,7 +227,7 @@ class AxesImagePhantom:
227
227
 
228
228
  clim = property(
229
229
  lambda self: self.__art.get_clim(),
230
- lambda self,v: self.__art.set_clim(v),
230
+ lambda self, v: self.__art.set_clim(v),
231
231
  doc="Lower/Upper color limit values of the buffer.")
232
232
 
233
233
  attributes = property(
@@ -236,12 +236,12 @@ class AxesImagePhantom:
236
236
 
237
237
  pathname = property(
238
238
  lambda self: self.__attributes.get('pathname'),
239
- lambda self,v: self.update_attr({'pathname': v}),
239
+ lambda self, v: self.update_attr({'pathname': v}),
240
240
  doc="Fullpath of the buffer, if bound to a file.")
241
241
 
242
242
  annotation = property(
243
243
  lambda self: self.__attributes.get('annotation', ''),
244
- lambda self,v: self.update_attr({'annotation': v}),
244
+ lambda self, v: self.update_attr({'annotation': v}),
245
245
  doc="Annotation of the buffer.")
246
246
 
247
247
  @property
@@ -321,9 +321,9 @@ class AxesImagePhantom:
321
321
  """Current buffer ROI (region of interest)."""
322
322
  if self.parent.region.size:
323
323
  nx, ny = self.xytopixel(*self.parent.region)
324
- sx = slice(max(0,nx[0]), nx[1]) # nx slice
325
- sy = slice(max(0,ny[1]), ny[0]) # ny slice 反転 (降順)
326
- return self.__buf[sy,sx]
324
+ sx = slice(max(0, nx[0]), nx[1]) # nx slice
325
+ sy = slice(max(0, ny[1]), ny[0]) # ny slice 反転 (降順)
326
+ return self.__buf[sy, sx]
327
327
  return self.__buf
328
328
 
329
329
  @roi.setter
@@ -342,7 +342,7 @@ class AxesImagePhantom:
342
342
 
343
343
  def xytoc(self, x, y=None, nearest=True):
344
344
  """Convert xydata (x,y) -> data[(x,y)] value of neaerst pixel.
345
- if nearest is False, retval is interpolated with spline
345
+ If `nearest` is False, the return value is interpolated with spline.
346
346
  """
347
347
  h, w = self.__buf.shape[:2]
348
348
  nx, ny = self.xytopixel(x, y, cast=nearest)
@@ -355,7 +355,7 @@ class AxesImagePhantom:
355
355
 
356
356
  def xytopixel(self, x, y=None, cast=True):
357
357
  """Convert xydata (x,y) -> [nx,ny] pixel.
358
- If cast, convert pixel-based lengths to pixel numbers.
358
+ If `cast` is True, the return value will be integer pixel values.
359
359
  """
360
360
  def _cast(n):
361
361
  return np.int32(np.floor(np.round(n, 1)))
@@ -372,7 +372,8 @@ class AxesImagePhantom:
372
372
  return (nx-0.5, ny-0.5)
373
373
 
374
374
  def xyfrompixel(self, nx, ny=None):
375
- """Convert pixel [nx,ny] -> (x,y) xydata (float number)."""
375
+ """Convert pixel [nx,ny] -> (x,y) xydata (float number).
376
+ """
376
377
  if ny is None:
377
378
  ## warn("Setting xy data with single tuple.", DeprecationWarning)
378
379
  nx, ny = nx
@@ -831,15 +832,6 @@ class GraphPlot(MatplotPanel):
831
832
  else:
832
833
  self.load(v)
833
834
 
834
- @property
835
- def newbuffer(self):
836
- """New buffer loader."""
837
- return None
838
-
839
- @newbuffer.setter
840
- def newbuffer(self, v):
841
- self.load(v)
842
-
843
835
  @property
844
836
  def unit(self):
845
837
  """Logical length per pixel arb.unit [u/pixel]."""
@@ -1217,9 +1209,9 @@ class GraphPlot(MatplotPanel):
1217
1209
  If centred, correct the point to the center of the nearest pixel.
1218
1210
  """
1219
1211
  dx, dy = x-xo, y-yo
1220
- L = np.hypot(dy,dx)
1221
- a = np.arctan2(dy,dx)
1222
- aa = np.linspace(-pi,pi,9) + pi/8 # 角度の検索範囲
1212
+ L = np.hypot(dy, dx)
1213
+ a = np.arctan2(dy, dx)
1214
+ aa = np.linspace(-pi, pi, 9) + pi/8 # 角度の検索範囲
1223
1215
  k = np.searchsorted(aa, a)
1224
1216
  x = xo + L * np.cos(aa[k] - pi/8)
1225
1217
  y = yo + L * np.sin(aa[k] - pi/8)
@@ -1413,7 +1405,7 @@ class GraphPlot(MatplotPanel):
1413
1405
  if x.size:
1414
1406
  self.__rectarts.append(
1415
1407
  self.axes.add_patch(
1416
- patches.Polygon(list(zip(x,y)),
1408
+ patches.Polygon(list(zip(x, y)),
1417
1409
  color='red', ls='solid', lw=1/2, ec='white', alpha=0.2)
1418
1410
  )
1419
1411
  )
@@ -1458,7 +1450,7 @@ class GraphPlot(MatplotPanel):
1458
1450
  self.__lastpoint = self.calc_point(org.xdata, org.ydata, centred=False)
1459
1451
  if not self.__rectsel:
1460
1452
  x, y = self.__lastpoint
1461
- self.set_current_rect((x,x), (y,y)) # start new region
1453
+ self.set_current_rect((x, x), (y, y)) # start new region
1462
1454
  self.__orgpoints = self.get_current_rect()
1463
1455
 
1464
1456
  def OnRegionDragMove(self, evt, shift=False, meta=False):
@@ -1477,7 +1469,7 @@ class GraphPlot(MatplotPanel):
1477
1469
  i = np.searchsorted(nn, n)
1478
1470
  x = xo + nn[i] * np.sign(x-xo) * ux
1479
1471
  y = yo + nn[i] * np.sign(y-yo) * uy
1480
- self.set_current_rect((xo,x), (yo,y))
1472
+ self.set_current_rect((xo, x), (yo, y))
1481
1473
  else:
1482
1474
  xc, yc = self.__lastpoint
1483
1475
  xo, yo = self.__orgpoints
@@ -1612,7 +1604,7 @@ class GraphPlot(MatplotPanel):
1612
1604
  j = self.__marksel
1613
1605
  if j:
1614
1606
  xm, ym = self.marked.get_data(orig=0)
1615
- xm, ym = np.delete(xm,j), np.delete(ym,j)
1607
+ xm, ym = np.delete(xm, j), np.delete(ym, j)
1616
1608
  self.__marksel = []
1617
1609
  self.marked.set_data(xm, ym)
1618
1610
  n = len(xm)
@@ -1621,7 +1613,7 @@ class GraphPlot(MatplotPanel):
1621
1613
 
1622
1614
  def update_art_of_mark(self, *args):
1623
1615
  if args:
1624
- for k,x,y in zip(*args):
1616
+ for k, x, y in zip(*args):
1625
1617
  art = self.__markarts[k] # art の再描画処理をして終了
1626
1618
  art.xy = x, y
1627
1619
  self.draw(self.marked)
@@ -1632,7 +1624,7 @@ class GraphPlot(MatplotPanel):
1632
1624
  if self.marked.get_visible() and self.handler.current_state in (MARK, MARK+DRAGGING):
1633
1625
  N = self.maxnum_markers
1634
1626
  xm, ym = self.marked.get_data(orig=0)
1635
- for k, (x,y) in enumerate(zip(xm[:N],ym[:N])):
1627
+ for k, (x, y) in enumerate(zip(xm[:N], ym[:N])):
1636
1628
  self.__markarts.append(
1637
1629
  self.axes.annotate(k, #<matplotlib.text.Annotation>
1638
1630
  xy=(x,y), xycoords='data',