mwxlib 0.98.1__py3-none-any.whl → 0.98.5__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/controls.py CHANGED
@@ -9,7 +9,7 @@ import wx.lib.scrolledpanel as scrolled
9
9
  from . import images
10
10
  from .utilus import SSM
11
11
  from .utilus import funcall as _F
12
- from .framework import pack, Menu
12
+ from .framework import pack, Menu, postcall
13
13
 
14
14
  import numpy as np
15
15
  from numpy import nan, inf # noqa: necessary to eval
@@ -20,7 +20,7 @@ def _Tip(*tips):
20
20
  return '\n'.join(filter(None, tips)).strip()
21
21
 
22
22
 
23
- class Param(object):
23
+ class Param:
24
24
  """Standard Parameter
25
25
 
26
26
  Args:
@@ -438,6 +438,8 @@ class Knob(wx.Panel):
438
438
  self.label.SetLabel(v.name + t)
439
439
  self.label.Refresh()
440
440
 
441
+ ## Note: wxAssertionError in text.SetValue
442
+ @postcall
441
443
  def update_ctrl(self, valid=True, notify=False):
442
444
  """Called when value is being changed (internal use only)."""
443
445
  v = self.__par
@@ -724,8 +726,7 @@ class ControlPanel(scrolled.ScrolledPanel):
724
726
  except AttributeError:
725
727
  p.value = v
726
728
  except Exception as e: # failed to eval
727
- print(f"- Failed to reset {p!r}:", e)
728
- pass
729
+ print(f"- Failed to reset {p!r}.", e)
729
730
 
730
731
  reset_params = set_params #: for backward compatibility
731
732
 
mwx/framework.py CHANGED
@@ -1,7 +1,7 @@
1
1
  #! python3
2
2
  """mwxlib framework.
3
3
  """
4
- __version__ = "0.98.1"
4
+ __version__ = "0.98.5"
5
5
  __author__ = "Kazuya O'moto <komoto@jeol.co.jp>"
6
6
 
7
7
  from contextlib import contextmanager
@@ -222,8 +222,6 @@ class KeyCtrlInterfaceMixin:
222
222
  spec-map : 'C-c'
223
223
  esc-map : 'escape'
224
224
  """
225
- message = print # override this in subclass
226
-
227
225
  @postcall
228
226
  def post_message(self, *args, **kwargs):
229
227
  return self.message(*args, **kwargs)
@@ -332,17 +330,22 @@ class CtrlInterface(KeyCtrlInterfaceMixin):
332
330
  """
333
331
  handler = property(lambda self: self.__handler)
334
332
 
333
+ message = print # override this in subclass
334
+
335
335
  def __init__(self):
336
336
  self.__key = ''
337
337
  self.__button = ''
338
338
  self.__isDragging = False
339
- self.__handler = FSM({None:{}, 0:{}})
339
+ self.__handler = FSM({ # DNA<CtrlInterface>
340
+ None : {
341
+ },
342
+ 0 : {
343
+ },
344
+ },
345
+ )
340
346
 
341
347
  _M = self._mouse_handler
342
-
343
- def _N(event, evt):
344
- if self.handler(event, evt) is None:
345
- evt.Skip()
348
+ _N = self._normal_handler
346
349
 
347
350
  def activate(evt):
348
351
  self.handler('focus_set', evt)
@@ -383,7 +386,7 @@ class CtrlInterface(KeyCtrlInterfaceMixin):
383
386
  self.Bind(wx.EVT_MOUSE_AUX2_DCLICK, lambda v: _M('Xbutton2 dblclick', v))
384
387
 
385
388
  self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, lambda v: _N('capture_lost', v))
386
- self.Bind(wx.EVT_MOUSE_CAPTURE_CHANGED, lambda v: _N('capture_lost', v))
389
+ self.Bind(wx.EVT_MOUSE_CAPTURE_CHANGED, lambda v: _N('capture_changed', v))
387
390
 
388
391
  def on_hotkey_press(self, evt): #<wx._core.KeyEvent>
389
392
  """Called when a key is pressed."""
@@ -461,6 +464,10 @@ class CtrlInterface(KeyCtrlInterfaceMixin):
461
464
  self.SetFocusIgnoringChildren() # let the panel accept keys
462
465
  except AttributeError:
463
466
  pass
467
+
468
+ def _normal_handler(self, event, evt): #<wx._core.Event>
469
+ if self.handler(event, evt) is None:
470
+ evt.Skip()
464
471
 
465
472
 
466
473
  ## --------------------------------
@@ -763,15 +770,10 @@ class Frame(wx.Frame, KeyCtrlInterfaceMixin):
763
770
  evt.Skip()
764
771
  self.Bind(wx.EVT_CHAR_HOOK, hook_char)
765
772
 
766
- def close(evt):
767
- """Close the window."""
768
- self.Close()
769
-
770
773
  self.__handler = FSM({ # DNA<Frame>
771
774
  None : {
772
775
  },
773
776
  0 : {
774
- 'M-q pressed' : (0, close),
775
777
  },
776
778
  },
777
779
  )
@@ -829,15 +831,10 @@ class MiniFrame(wx.MiniFrame, KeyCtrlInterfaceMixin):
829
831
  ## To default close >>> self.Unbind(wx.EVT_CLOSE)
830
832
  self.Bind(wx.EVT_CLOSE, lambda v: self.Show(0))
831
833
 
832
- def close(evt):
833
- """Close the window."""
834
- self.Close()
835
-
836
834
  self.__handler = FSM({ # DNA<MiniFrame>
837
835
  None : {
838
836
  },
839
837
  0 : {
840
- 'M-q pressed' : (0, close),
841
838
  },
842
839
  },
843
840
  )
@@ -1010,7 +1007,7 @@ class AuiNotebook(aui.AuiNotebook):
1010
1007
  self._mgr.LoadPerspective(frames)
1011
1008
  self._mgr.Update()
1012
1009
  except Exception as e:
1013
- print("- Failed to load perspective:", e)
1010
+ print("- Failed to load perspective.", e)
1014
1011
  finally:
1015
1012
  self.Parent.Thaw()
1016
1013
 
@@ -1278,16 +1275,14 @@ class ShellFrame(MiniFrame):
1278
1275
 
1279
1276
  @self.Scratch.define_key('C-j')
1280
1277
  def eval_line(evt):
1281
- shell = self.current_shell
1282
- self.Scratch.buffer.py_eval_line(shell.globals, shell.locals)
1283
- evt.Skip(False)
1278
+ self.Scratch.buffer.eval_line()
1279
+ evt.Skip(False) # Don't skip explicitly.
1284
1280
 
1285
1281
  @self.Scratch.define_key('C-S-j')
1286
1282
  @self.Scratch.define_key('M-j')
1287
1283
  def eval_buffer(evt):
1288
- shell = self.current_shell
1289
- self.Scratch.buffer.py_exec_region(shell.globals, shell.locals)
1290
- evt.Skip(False)
1284
+ self.Scratch.buffer.exec_region()
1285
+ evt.Skip(False) # Don't skip explicitly.
1291
1286
 
1292
1287
  ## Session
1293
1288
  self.SESSION_FILE = get_rootpath(".debrc")
@@ -1298,7 +1293,9 @@ class ShellFrame(MiniFrame):
1298
1293
  os.path.abspath(debrc) if debrc else self.SESSION_FILE)
1299
1294
 
1300
1295
  def load_session(self, filename):
1301
- """Load session from file."""
1296
+ """Load session from file.
1297
+ Buffers, pointers, and the entire layout are loaded.
1298
+ """
1302
1299
  def _fload(editor, filename):
1303
1300
  try:
1304
1301
  buffer = editor.default_buffer or editor.new_buffer()
@@ -1325,7 +1322,9 @@ class ShellFrame(MiniFrame):
1325
1322
  self.Position = (0, 0)
1326
1323
 
1327
1324
  def save_session(self):
1328
- """Save session to file."""
1325
+ """Save session to file.
1326
+ Buffers, pointers, and the entire layout are saved.
1327
+ """
1329
1328
  def _fsave(editor, filename):
1330
1329
  try:
1331
1330
  buffer = editor.default_buffer
@@ -1340,20 +1339,20 @@ class ShellFrame(MiniFrame):
1340
1339
  with open(self.SESSION_FILE, 'w', encoding='utf-8', newline='') as o:
1341
1340
  o.write("#! Session file (This file is generated automatically)\n")
1342
1341
 
1343
- for book in self.all_editors:
1342
+ for book in (self.Scratch, self.Log):
1344
1343
  for buf in book.all_buffers:
1345
1344
  if buf.mtdelta is not None:
1346
- o.write("self._load({!r}, {!r}, {!r})\n"
1347
- .format(buf.filename, buf.markline+1, book.Name))
1345
+ o.write("self.{}.load_file({!r}, {})\n"
1346
+ .format(book.Name, buf.filename, buf.markline+1))
1348
1347
  o.write('\n'.join((
1349
1348
  "self.SetSize({})".format(self.Size),
1350
1349
  "self.SetPosition({})".format(self.Position),
1351
- "self.ghost.SetSelection({})".format(self.ghost.Selection),
1352
- "self.watcher.SetSelection({})".format(self.watcher.Selection),
1350
+ "self.Scratch.loadPerspective({!r})".format(self.Scratch.savePerspective()),
1351
+ "self.Log.loadPerspective({!r})".format(self.Log.savePerspective()),
1353
1352
  ## Note: Perspectives should be called after all pages have been added.
1354
- "wx.CallAfter(self._mgr.LoadPerspective, {!r})".format(self._mgr.SavePerspective()),
1355
- "wx.CallAfter(self.ghost.loadPerspective, {!r})".format(self.ghost.savePerspective()),
1356
- "wx.CallAfter(self.watcher.loadPerspective, {!r})".format(self.watcher.savePerspective()),
1353
+ "self.ghost.loadPerspective({!r})".format(self.ghost.savePerspective()),
1354
+ "self.watcher.loadPerspective({!r})".format(self.watcher.savePerspective()),
1355
+ "self._mgr.LoadPerspective({!r})".format(self._mgr.SavePerspective()),
1357
1356
  "self._mgr.Update()\n",
1358
1357
  )))
1359
1358
 
@@ -1547,9 +1546,8 @@ class ShellFrame(MiniFrame):
1547
1546
  pane = self._mgr.GetPane(win)
1548
1547
  if pane.IsDocked():
1549
1548
  if not self.console.IsShown():
1550
- self._mgr.RestoreMaximizedPane()
1551
- self._mgr.Update()
1552
- return
1549
+ self._mgr.RestoreMaximizedPane() # いったん表示切替
1550
+ self._mgr.Update() # 更新後に best_size 取得
1553
1551
  if pane.IsShown():
1554
1552
  pane.best_size = win.Size
1555
1553
  self.popup_window(win, not pane.IsShown())
@@ -1597,7 +1595,7 @@ class ShellFrame(MiniFrame):
1597
1595
  self.indicator.Value = 1
1598
1596
  self.message("Quit")
1599
1597
 
1600
- def _load(self, filename, lineno, editor):
1598
+ def _load(self, filename, lineno, editor): # for backward compatibility
1601
1599
  """Load file in the session (internal use only)."""
1602
1600
  if isinstance(editor, str):
1603
1601
  editor = getattr(self, editor, None)
mwx/graphman.py CHANGED
@@ -50,7 +50,7 @@ def split_paths(obj):
50
50
  return os.path.split(obj)
51
51
 
52
52
 
53
- class Thread(object):
53
+ class Thread:
54
54
  """Thread manager for graphman.Layer
55
55
 
56
56
  The worker:thread runs the given target.
@@ -342,7 +342,6 @@ class LayerInterface(CtrlInterface):
342
342
  'thread_error' : [ None ], # failed in error
343
343
  'page_shown' : [ None, _F(self.Draw, True) ],
344
344
  'page_closed' : [ None, _F(self.Draw, False) ],
345
- 'page_hidden' : [ None, _F(self.Draw, False) ],
346
345
  },
347
346
  0 : {
348
347
  'C-c pressed' : (0, _F(copy_params)),
@@ -387,7 +386,6 @@ class LayerInterface(CtrlInterface):
387
386
  lambda v: Menu.Popup(self, self.menu))
388
387
 
389
388
  self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
390
- self.Bind(wx.EVT_SHOW, self.OnShow)
391
389
 
392
390
  try:
393
391
  self.Init()
@@ -427,15 +425,6 @@ class LayerInterface(CtrlInterface):
427
425
  del self.Arts
428
426
  evt.Skip()
429
427
 
430
- def OnShow(self, evt):
431
- if not self:
432
- return
433
- if evt.IsShown():
434
- self.handler('page_shown', self)
435
- elif isinstance(self.Parent, aui.AuiNotebook):
436
- self.handler('page_hidden', self)
437
- evt.Skip()
438
-
439
428
  Shown = property(
440
429
  lambda self: self.IsShown(),
441
430
  lambda self,v: self.Show(v))
@@ -443,16 +432,19 @@ class LayerInterface(CtrlInterface):
443
432
  def IsShown(self):
444
433
  """Returns True if the window is physically visible on the screen.
445
434
 
446
- Note: This method is overridden to be equivalent to IsShownOnScreen,
447
- as the object may be a page within a notebook.
435
+ (override) Equivalent to ``IsShownOnScreen``.
436
+ Note: The instance could be a page within a notebook.
448
437
  """
449
438
  ## return self.pane.IsShown()
450
439
  return self.IsShownOnScreen()
451
440
 
452
- def Show(self, show=True, interactive=False):
453
- """Show associated pane (override) window."""
454
- ## Note: This might be called from a thread.
455
- wx.CallAfter(self.parent.show_pane, self, show, interactive)
441
+ def Show(self, show=True):
442
+ """Shows or hides the window.
443
+
444
+ (override) Show associated pane window.
445
+ Note: This might be called from a thread.
446
+ """
447
+ wx.CallAfter(self.parent.show_pane, self, show)
456
448
 
457
449
  Drawn = property(
458
450
  lambda self: self.IsDrawn(),
@@ -478,21 +470,16 @@ class LayerInterface(CtrlInterface):
478
470
  if canvas:
479
471
  canvas.draw_idle()
480
472
  except Exception as e:
481
- print(f"- Failed to draw Arts of {self.__module__}:", e)
473
+ print(f"- Failed to draw Arts of {self.__module__}.", e)
482
474
  del self.Arts
483
475
 
484
476
 
485
- class Layer(ControlPanel, LayerInterface):
477
+ class Layer(LayerInterface, ControlPanel):
486
478
  """Graphman.Layer
487
479
  """
488
480
  def __init__(self, parent, session=None, **kwargs):
489
481
  ControlPanel.__init__(self, parent, **kwargs)
490
482
  LayerInterface.__init__(self, parent, session)
491
-
492
- ## Explicit (override) precedence
493
- IsShown = LayerInterface.IsShown
494
- Shown = LayerInterface.Shown
495
- Show = LayerInterface.Show
496
483
 
497
484
 
498
485
  class Graph(GraphPlot):
@@ -942,9 +929,6 @@ class Frame(mwx.Frame):
942
929
  pane.Float()
943
930
  show = True
944
931
 
945
- ## Fork page shown/closed events to emulatie EVT_SHOW => plug.on_show.
946
- ## cf. >>> win.EventHandler.ProcessEvent(wx.ShowEvent(win.Id, show))
947
- ##
948
932
  ## Note: We need to distinguish cases whether:
949
933
  ## - pane.window is AuiNotebook or normal Panel,
950
934
  ## - pane.window is floating (win.Parent is AuiFloatingFrame) or docked.
@@ -1023,6 +1007,7 @@ class Frame(mwx.Frame):
1023
1007
  plug.handler('page_closed', plug)
1024
1008
  else:
1025
1009
  win.handler('page_closed', win)
1010
+ evt.Skip()
1026
1011
 
1027
1012
  ## --------------------------------
1028
1013
  ## Plugin <Layer> interface
@@ -1070,15 +1055,10 @@ class Frame(mwx.Frame):
1070
1055
  module.Plugin = cls
1071
1056
  return cls
1072
1057
 
1073
- class _Plugin(cls, LayerInterface):
1058
+ class _Plugin(LayerInterface, cls):
1074
1059
  def __init__(self, parent, session=None, **kwargs):
1075
1060
  cls.__init__(self, parent, **kwargs)
1076
1061
  LayerInterface.__init__(self, parent, session)
1077
-
1078
- ## Explicit (override) precedence
1079
- IsShown = LayerInterface.IsShown
1080
- Shown = LayerInterface.Shown
1081
- Show = LayerInterface.Show
1082
1062
 
1083
1063
  _Plugin.__module__ = cls.__module__ = module.__name__
1084
1064
  _Plugin.__name__ = cls.__name__ + str("~")
@@ -1110,7 +1090,7 @@ class Frame(mwx.Frame):
1110
1090
  else:
1111
1091
  module = import_module(name)
1112
1092
  except Exception as e:
1113
- print(f"- Unable to load {root!r}:", e)
1093
+ print(f"- Unable to load {root!r}.", e)
1114
1094
  return False
1115
1095
 
1116
1096
  ## the module must have a class `Plugin`.
@@ -1172,7 +1152,7 @@ class Frame(mwx.Frame):
1172
1152
 
1173
1153
  module = self.load_module(root)
1174
1154
  if not module:
1175
- return module # False (failed to import)
1155
+ return False # failed to import
1176
1156
 
1177
1157
  try:
1178
1158
  name = module.Plugin.__module__
@@ -1328,18 +1308,21 @@ class Frame(mwx.Frame):
1328
1308
 
1329
1309
  def reload_plug(self, name):
1330
1310
  plug = self.get_plug(name)
1331
- if not plug:
1311
+ if not plug or not plug.reloadable:
1332
1312
  return
1333
- if plug.reloadable:
1334
- session = {}
1335
- try:
1336
- print("Reloading {}...".format(plug))
1337
- plug.save_session(session)
1338
- except Exception:
1339
- traceback.print_exc()
1340
- print("- Failed to save session of", plug)
1341
- return self.load_plug(plug.__module__, force=1, session=session)
1342
- return False
1313
+ session = {}
1314
+ try:
1315
+ print("Reloading {}...".format(plug))
1316
+ plug.save_session(session)
1317
+ except Exception:
1318
+ traceback.print_exc()
1319
+ print("- Failed to save session of", plug)
1320
+ self.load_plug(plug.__module__, force=1, session=session)
1321
+
1322
+ ## Update shell.target --> new plug
1323
+ for shell in self.shellframe.all_shells:
1324
+ if shell.target is plug:
1325
+ shell.handler('shell_activated', shell)
1343
1326
 
1344
1327
  @ignore(ResourceWarning)
1345
1328
  def edit_plug(self, name):
@@ -1398,12 +1381,16 @@ class Frame(mwx.Frame):
1398
1381
 
1399
1382
  def load_index(self, filename=None, view=None):
1400
1383
  """Load frames :ref to the Index file.
1384
+
1385
+ If no view given, the currently selected view is chosen.
1401
1386
  """
1402
1387
  if not view:
1403
1388
  view = self.selected_view
1404
1389
 
1405
1390
  if not filename:
1391
+ fn = view.frame.pathname if view.frame else ''
1406
1392
  with wx.FileDialog(self, "Select index file to import",
1393
+ defaultDir=os.path.dirname(fn or ''),
1407
1394
  defaultFile=self.ATTRIBUTESFILE,
1408
1395
  wildcard="Index (*.index)|*.index|"
1409
1396
  "ALL files (*.*)|*.*",
@@ -1431,15 +1418,16 @@ class Frame(mwx.Frame):
1431
1418
  def save_index(self, filename=None, frames=None):
1432
1419
  """Save frames :ref to the Index file.
1433
1420
  """
1421
+ view = self.selected_view
1434
1422
  if not frames:
1435
- frames = self.selected_view.all_frames
1423
+ frames = view.all_frames
1436
1424
  if not frames:
1437
1425
  return None
1438
1426
 
1439
1427
  if not filename:
1440
- fn = next((x.pathname for x in frames if x.pathname), '')
1428
+ fn = view.frame.pathname if view.frame else ''
1441
1429
  with wx.FileDialog(self, "Select index file to export",
1442
- defaultDir=os.path.dirname(fn),
1430
+ defaultDir=os.path.dirname(fn or ''),
1443
1431
  defaultFile=self.ATTRIBUTESFILE,
1444
1432
  wildcard="Index (*.index)|*.index",
1445
1433
  style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) as dlg:
@@ -1463,8 +1451,8 @@ class Frame(mwx.Frame):
1463
1451
  frame.name = os.path.basename(fn) # new name and pathname
1464
1452
  output_frames.append(frame)
1465
1453
  print(' ', self.message("\b done."))
1466
- except (PermissionError, OSError):
1467
- print('-', self.message("\b failed."))
1454
+ except (PermissionError, OSError) as e:
1455
+ print('-', self.message("\b failed.", e))
1468
1456
 
1469
1457
  frames = output_frames
1470
1458
  res, mis = self.write_attributes(filename, frames)
@@ -1504,7 +1492,7 @@ class Frame(mwx.Frame):
1504
1492
  except FileNotFoundError:
1505
1493
  pass
1506
1494
  except Exception as e:
1507
- print("- Failed to read attributes:", e)
1495
+ print("- Failed to read attributes.", e)
1508
1496
  wx.MessageBox(str(e), style=wx.ICON_ERROR)
1509
1497
  finally:
1510
1498
  return res, mis # finally raises no exception
@@ -1526,7 +1514,7 @@ class Frame(mwx.Frame):
1526
1514
  print(pformat(tuple(new.items())), file=o)
1527
1515
 
1528
1516
  except Exception as e:
1529
- print("- Failed to write attributes:", e)
1517
+ print("- Failed to write attributes.", e)
1530
1518
  wx.MessageBox(str(e), style=wx.ICON_ERROR)
1531
1519
  finally:
1532
1520
  return new, mis # finally raises no exception
@@ -1607,7 +1595,10 @@ class Frame(mwx.Frame):
1607
1595
  paths = [paths]
1608
1596
 
1609
1597
  if paths is None:
1598
+ fn = view.frame.pathname if view.frame else ''
1610
1599
  with wx.FileDialog(self, "Open image files",
1600
+ defaultDir=os.path.dirname(fn or ''),
1601
+ defaultFile='',
1611
1602
  wildcard='|'.join(self.wildcards),
1612
1603
  style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST
1613
1604
  |wx.FD_MULTIPLE) as dlg:
@@ -1653,16 +1644,17 @@ class Frame(mwx.Frame):
1653
1644
 
1654
1645
  def save_buffer(self, path=None, frame=None):
1655
1646
  """Save buffer of the frame to a file.
1656
-
1657
- If no view given, the currently selected view is chosen.
1658
1647
  """
1648
+ view = self.selected_view
1659
1649
  if not frame:
1660
- frame = self.selected_view.frame
1650
+ frame = view.frame
1661
1651
  if not frame:
1662
1652
  return None
1663
1653
 
1664
1654
  if not path:
1655
+ fn = view.frame.pathname if view.frame else ''
1665
1656
  with wx.FileDialog(self, "Save buffer as",
1657
+ defaultDir=os.path.dirname(fn or ''),
1666
1658
  defaultFile=re.sub(r'[\/:*?"<>|]', '_', frame.name),
1667
1659
  wildcard='|'.join(self.wildcards),
1668
1660
  style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) as dlg:
mwx/matplot2g.py CHANGED
@@ -110,7 +110,7 @@ def _Property(name):
110
110
  lambda self: delattr(self.parent, name))
111
111
 
112
112
 
113
- class AxesImagePhantom(object):
113
+ class AxesImagePhantom:
114
114
  """Phantom of frame facade
115
115
 
116
116
  Args:
@@ -957,7 +957,7 @@ class GraphPlot(MatplotPanel):
957
957
  self.message("Write buffer to clipboard.")
958
958
  except Exception as e:
959
959
  traceback.print_exc()
960
- self.message("- Failed to write to clipboard:", e)
960
+ self.message("- Failed to write to clipboard.", e)
961
961
 
962
962
  def read_buffer_from_clipboard(self):
963
963
  """Read buffer data from clipboard."""
@@ -974,7 +974,7 @@ class GraphPlot(MatplotPanel):
974
974
  self.load(Clipboard.imread())
975
975
  except Exception as e:
976
976
  traceback.print_exc()
977
- self.message("- No data in clipboard:", e)
977
+ self.message("- No data in clipboard.", e)
978
978
 
979
979
  def destroy_colorbar(self):
980
980
  if self.cbar:
mwx/mgplt.py CHANGED
@@ -12,7 +12,7 @@ from . import framework as mwx
12
12
  from .controls import ControlPanel
13
13
 
14
14
 
15
- class Gnuplot(object):
15
+ class Gnuplot:
16
16
  """Gnuplot - gnuplot:pipe wrapper
17
17
  """
18
18
  debug = 0
mwx/nutshell.py CHANGED
@@ -10,7 +10,6 @@ import traceback
10
10
  import inspect
11
11
  import builtins
12
12
  import operator
13
- import dis
14
13
  import pydoc
15
14
  import keyword
16
15
  import linecache
@@ -102,12 +101,13 @@ class AutoCompInterfaceMixin:
102
101
  (override) Snip the tip of max N lines if it is too long.
103
102
  Keep the tip for calltip-click event.
104
103
  """
105
- self._calltips = (pos, tip)
104
+ self._calltips = [pos, tip, False]
106
105
  lines = tip.splitlines()
107
106
  if N and len(lines) > N > 0:
108
107
  lines[N+1:] = ["\n...(snip) This tips are too long... "
109
108
  "Click to show more details."]
110
109
  tip = '\n'.join(lines)
110
+ self._calltips[-1] = True # snipped (needs to be shown)
111
111
  super().CallTipShow(pos, tip)
112
112
 
113
113
  def autoCallTipShow(self, command, insertcalltip=True):
@@ -142,7 +142,7 @@ class AutoCompInterfaceMixin:
142
142
  self.message("- {} : {!r}".format(e, text))
143
143
 
144
144
  def call_helpTip(self, evt):
145
- """Show tooltips for the selected topic."""
145
+ """Show a calltip for the selected function."""
146
146
  if self.CallTipActive():
147
147
  self.CallTipCancel()
148
148
 
@@ -174,8 +174,11 @@ class AutoCompInterfaceMixin:
174
174
  n = len(self.__comp_hint)
175
175
  p = self.cpos
176
176
  if not self.SelectedText:
177
- p, self.anchor, sty = self.get_following_atom(p) # word-right-selection
178
- self.ReplaceSelection(word[n:]) # Modify (or insert) the selected range
177
+ p, q, sty = self.get_following_atom(p) # word-right-selection
178
+ if sty == 'word':
179
+ self.anchor = q
180
+ with self.off_undocollection():
181
+ self.ReplaceSelection(word[n:])
179
182
  self.cpos = p # backward selection to the point
180
183
  self.__comp_ind = j
181
184
  except IndexError:
@@ -264,7 +267,7 @@ class AutoCompInterfaceMixin:
264
267
  self.message("[module]>>> loading {}...".format(text))
265
268
  modules = set(dir(import_module(text)))
266
269
  except ImportError as e:
267
- self.message("\b failed:", e)
270
+ self.message("\b failed.", e)
268
271
  return
269
272
  else:
270
273
  ## Add unimported module names.
@@ -356,7 +359,7 @@ class AutoCompInterfaceMixin:
356
359
  self.message("- {} : {!r}".format(e, text))
357
360
 
358
361
 
359
- class EditorInterface(CtrlInterface):
362
+ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
360
363
  """Interface of Python code editor.
361
364
 
362
365
  Note:
@@ -364,6 +367,7 @@ class EditorInterface(CtrlInterface):
364
367
  """
365
368
  def __init__(self):
366
369
  CtrlInterface.__init__(self)
370
+ AutoCompInterfaceMixin.__init__(self)
367
371
 
368
372
  def dispatch(evt):
369
373
  """Fork events to the parent."""
@@ -414,8 +418,8 @@ class EditorInterface(CtrlInterface):
414
418
  'C-S-; pressed' : (0, _F(self.comment_out_line)),
415
419
  'C-: pressed' : (0, _F(self.uncomment_line)),
416
420
  'C-S-: pressed' : (0, _F(self.uncomment_line)),
417
- 'select_line' : (100, self.on_linesel_begin),
418
- 'select_lines' : (100, self.on_linesel_next),
421
+ 'select_line' : (11, self.on_linesel_begin),
422
+ 'select_lines' : (11, self.on_linesel_next),
419
423
  },
420
424
  10 : {
421
425
  'quit' : (0, self.on_itext_exit),
@@ -424,8 +428,8 @@ class EditorInterface(CtrlInterface):
424
428
  'down pressed' : (10, skip),
425
429
  'enter pressed' : (0, self.on_itext_selection),
426
430
  },
427
- 100 : {
428
- '*Ldrag move' : (100, self.on_linesel_motion),
431
+ 11 : {
432
+ '*Ldrag move' : (11, self.on_linesel_motion),
429
433
  'capture_lost' : (0, self.on_linesel_end),
430
434
  'Lbutton released' : (0, self.on_linesel_end),
431
435
  },
@@ -887,6 +891,7 @@ class EditorInterface(CtrlInterface):
887
891
  ## --------------------------------
888
892
  ## Python syntax and indentation
889
893
  ## --------------------------------
894
+
890
895
  def on_indent_line(self, evt):
891
896
  if self.SelectedText:
892
897
  evt.Skip()
@@ -1191,10 +1196,10 @@ class EditorInterface(CtrlInterface):
1191
1196
  """
1192
1197
  n = self.LinesOnScreen() # lines completely visible
1193
1198
  m = n//2 if ln is None else ln % n if ln < n else n # ln[0:n]
1194
- vl = self.calc_vline(self.cline)
1199
+ vl = self._calc_vline(self.cline)
1195
1200
  self.ScrollToLine(vl - m)
1196
1201
 
1197
- def calc_vline(self, line):
1202
+ def _calc_vline(self, line):
1198
1203
  """Virtual line numberin the buffer window."""
1199
1204
  pos = self.PositionFromLine(line)
1200
1205
  w, h = self.PointFromPosition(pos)
@@ -1206,7 +1211,7 @@ class EditorInterface(CtrlInterface):
1206
1211
  """
1207
1212
  n = self.LinesOnScreen() # lines completely visible
1208
1213
  hl = self.FirstVisibleLine
1209
- vl = self.calc_vline(line)
1214
+ vl = self._calc_vline(line)
1210
1215
  if vl < hl:
1211
1216
  self.ScrollToLine(vl)
1212
1217
  elif vl > hl + n - 1:
@@ -1219,7 +1224,7 @@ class EditorInterface(CtrlInterface):
1219
1224
  """
1220
1225
  n = self.LinesOnScreen() # lines completely visible
1221
1226
  hl = self.FirstVisibleLine
1222
- vl = self.calc_vline(line)
1227
+ vl = self._calc_vline(line)
1223
1228
  if not hl + offset < vl < hl + n - 1 - offset:
1224
1229
  self.ScrollToLine(vl - n//2)
1225
1230
 
@@ -1512,16 +1517,6 @@ class EditorInterface(CtrlInterface):
1512
1517
  else:
1513
1518
  self.anchor = q
1514
1519
 
1515
- @contextmanager
1516
- def off_readonly(self):
1517
- """Set buffer to be writable (ReadOnly=False) temporarily."""
1518
- r = self.ReadOnly
1519
- try:
1520
- self.ReadOnly = 0
1521
- yield
1522
- finally:
1523
- self.ReadOnly = r
1524
-
1525
1520
  @contextmanager
1526
1521
  def save_attributes(self, **kwargs):
1527
1522
  """Save buffer attributes (e.g. ReadOnly=False)."""
@@ -1534,6 +1529,14 @@ class EditorInterface(CtrlInterface):
1534
1529
  for k, v in kwargs.items():
1535
1530
  setattr(self, k, v)
1536
1531
 
1532
+ def off_readonly(self):
1533
+ """Disables buffer read-only lock temporarily."""
1534
+ return self.save_attributes(ReadOnly=False)
1535
+
1536
+ def off_undocollection(self):
1537
+ """Disables buffer undo stack temporarily."""
1538
+ return self.save_attributes(UndoCollection=False)
1539
+
1537
1540
  ## --------------------------------
1538
1541
  ## Edit: comment / insert / kill
1539
1542
  ## --------------------------------
@@ -1640,7 +1643,7 @@ class EditorInterface(CtrlInterface):
1640
1643
  self.ReplaceSelection('')
1641
1644
 
1642
1645
 
1643
- class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
1646
+ class Buffer(EditorInterface, EditWindow):
1644
1647
  """Python code buffer.
1645
1648
 
1646
1649
  Attributes:
@@ -1766,7 +1769,6 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
1766
1769
  def __init__(self, parent, filename=None, **kwargs):
1767
1770
  EditWindow.__init__(self, parent, **kwargs)
1768
1771
  EditorInterface.__init__(self)
1769
- AutoCompInterfaceMixin.__init__(self)
1770
1772
 
1771
1773
  self.parent = parent
1772
1774
  self.__filename = filename
@@ -1795,18 +1797,18 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
1795
1797
  def clear(evt):
1796
1798
  ## """Clear selection and message, no skip."""
1797
1799
  ## *do not* clear autocomp, so that the event can skip to AutoComp properly.
1798
- ## if self.AutoCompActive():
1799
- ## self.AutoCompCancel() # may delete selection
1800
1800
  if self.CanEdit():
1801
- self.ReplaceSelection("")
1801
+ with self.off_undocollection():
1802
+ self.ReplaceSelection("")
1802
1803
  self.message("")
1803
1804
 
1804
1805
  def clear_autocomp(evt):
1805
- ## """Clear Autocomp, selection, and message."""
1806
+ ## """Clear autocomp, selection, and message."""
1806
1807
  if self.AutoCompActive():
1807
1808
  self.AutoCompCancel()
1808
1809
  if self.CanEdit():
1809
- self.ReplaceSelection("")
1810
+ with self.off_undocollection():
1811
+ self.ReplaceSelection("")
1810
1812
  self.message("")
1811
1813
 
1812
1814
  def fork(evt):
@@ -1840,7 +1842,7 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
1840
1842
  '* released' : (0, skip, dispatch),
1841
1843
  'escape pressed' : (-1, self.on_enter_escmap),
1842
1844
  'C-h pressed' : (0, self.call_helpTip),
1843
- '. pressed' : (2, skip),
1845
+ '. pressed' : (2, self.OnEnterDot),
1844
1846
  'M-. pressed' : (2, self.call_word_autocomp),
1845
1847
  'M-/ pressed' : (3, self.call_apropos_autocomp),
1846
1848
  },
@@ -1922,7 +1924,9 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
1922
1924
  def OnCallTipClick(self, evt):
1923
1925
  if self.CallTipActive():
1924
1926
  self.CallTipCancel()
1925
- self.CallTipShow(*self._calltips, N=None)
1927
+ pos, tip, more = self._calltips
1928
+ if more:
1929
+ self.CallTipShow(pos, tip, N=None)
1926
1930
  evt.Skip()
1927
1931
 
1928
1932
  def OnIndicatorClick(self, evt):
@@ -1960,6 +1964,14 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
1960
1964
  self.update_caption()
1961
1965
  evt.Skip()
1962
1966
 
1967
+ def OnEnterDot(self, evt):
1968
+ p = self.cpos
1969
+ st = self.get_style(p-1)
1970
+ rst = self.get_style(p)
1971
+ if st not in ('moji', 'word', 'rparen') or rst == 'word':
1972
+ self.handler('quit', evt) # don't enter autocomp
1973
+ evt.Skip()
1974
+
1963
1975
  def on_activated(self, buf):
1964
1976
  """Called when the buffer is activated."""
1965
1977
  self.update_caption()
@@ -2052,6 +2064,9 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
2052
2064
  dispatcher.send(signal='Interpreter.push',
2053
2065
  sender=self, command=None, more=False)
2054
2066
 
2067
+ def eval_line(self):
2068
+ self.py_eval_line(self.globals, self.locals)
2069
+
2055
2070
  def py_eval_line(self, globals=None, locals=None):
2056
2071
  if self.CallTipActive():
2057
2072
  self.CallTipCancel()
@@ -2068,6 +2083,9 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
2068
2083
  return
2069
2084
  self.message(status)
2070
2085
 
2086
+ def exec_region(self):
2087
+ self.py_exec_region(self.globals, self.locals)
2088
+
2071
2089
  def py_exec_region(self, globals=None, locals=None, filename=None):
2072
2090
  if not filename:
2073
2091
  filename = self.filename
@@ -2100,23 +2118,6 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
2100
2118
  self.handler('buffer_region_executed', self)
2101
2119
  self.message("Evaluated {!r} successfully.".format(filename))
2102
2120
  self.AnnotationClearAll()
2103
-
2104
- def py_get_region(self, line):
2105
- """Line numbers of code head and tail containing the line.
2106
-
2107
- Requires a code object.
2108
- If the code doesn't exist, return the folding region.
2109
- """
2110
- if not self.code:
2111
- return self.get_region(line)
2112
- lc, le = 0, self.LineCount
2113
- linestarts = list(dis.findlinestarts(self.code))
2114
- for i, ln in reversed(linestarts):
2115
- if line >= ln-1:
2116
- lc = ln-1
2117
- break
2118
- le = ln-1
2119
- return lc, le
2120
2121
 
2121
2122
 
2122
2123
  class EditorBook(AuiNotebook, CtrlInterface):
@@ -2402,7 +2403,7 @@ class EditorBook(AuiNotebook, CtrlInterface):
2402
2403
  return True
2403
2404
  return False
2404
2405
  except Exception as e:
2405
- self.post_message(f"Failed to load {filename!r}:", e)
2406
+ self.post_message(f"Failed to load {filename!r}.", e)
2406
2407
  self.delete_buffer(buf)
2407
2408
  return False
2408
2409
 
@@ -2410,6 +2411,7 @@ class EditorBook(AuiNotebook, CtrlInterface):
2410
2411
  """Open the specified file."""
2411
2412
  if not filename:
2412
2413
  with wx.FileDialog(self, "Open buffer",
2414
+ defaultDir=os.path.dirname(self.buffer.filename),
2413
2415
  wildcard='|'.join(self.wildcards),
2414
2416
  style=wx.FD_OPEN|wx.FD_MULTIPLE) as dlg:
2415
2417
  if dlg.ShowModal() == wx.ID_OK:
@@ -2444,7 +2446,7 @@ class EditorBook(AuiNotebook, CtrlInterface):
2444
2446
  return True
2445
2447
  return False
2446
2448
  except Exception as e:
2447
- self.post_message(f"Failed to save {filename!r}:", e)
2449
+ self.post_message(f"Failed to save {filename!r}.", e)
2448
2450
  return False
2449
2451
 
2450
2452
  def load_buffer(self, buf=None):
@@ -2477,6 +2479,7 @@ class EditorBook(AuiNotebook, CtrlInterface):
2477
2479
  """Confirm the saveas with the dialog."""
2478
2480
  buf = buf or self.buffer
2479
2481
  with wx.FileDialog(self, "Save buffer as",
2482
+ defaultDir=os.path.dirname(self.buffer.filename),
2480
2483
  defaultFile=re.sub(r'[\/:*?"<>|]', '_', buf.name),
2481
2484
  wildcard='|'.join(self.wildcards),
2482
2485
  style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) as dlg:
@@ -2519,11 +2522,10 @@ class EditorBook(AuiNotebook, CtrlInterface):
2519
2522
  class Interpreter(interpreter.Interpreter):
2520
2523
  """Interpreter based on code.InteractiveInterpreter.
2521
2524
  """
2522
- def __init__(self, *args, **kwargs):
2523
- parent = kwargs.pop('interpShell')
2525
+ def __init__(self, interpShell, *args, **kwargs):
2524
2526
  interpreter.Interpreter.__init__(self, *args, **kwargs)
2525
2527
 
2526
- self.parent = parent
2528
+ self.parent = interpShell
2527
2529
  self.globals = self.locals
2528
2530
 
2529
2531
  def runcode(self, code):
@@ -2587,7 +2589,7 @@ class Interpreter(interpreter.Interpreter):
2587
2589
  return interpreter.Interpreter.getCallTip(self) # dummy
2588
2590
 
2589
2591
 
2590
- class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2592
+ class Nautilus(EditorInterface, Shell):
2591
2593
  """Nautilus in the Shell.
2592
2594
 
2593
2595
  Facade objects for accessing the APIs:
@@ -2624,8 +2626,8 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2624
2626
 
2625
2627
  C-up : [0] retrieve previous history
2626
2628
  C-down : [0] retrieve next history
2627
- C-j, M-j : [0] call tooltip of eval (for the word selected or focused)
2628
- C-h, M-h : [0] call tooltip of help (for the func selected or focused)
2629
+ C-j, M-j : [0] tooltip of eval (for the selected or focused word)
2630
+ C-h, M-h : [0] calltip of help (for the selected or focused func)
2629
2631
  TAB : [1] history-comp
2630
2632
  M-p : [1] retrieve previous history in history-comp mode
2631
2633
  M-n : [1] retrieve next history in history-comp mode
@@ -2697,7 +2699,9 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2697
2699
  raise TypeError("primitive objects cannot be targeted")
2698
2700
 
2699
2701
  self.__target = obj
2700
- self.interp.locals = obj.__dict__
2702
+ self.locals = obj.__dict__
2703
+ self.globals = obj.__dict__
2704
+ self.globals.update(self.__globals)
2701
2705
  try:
2702
2706
  obj.self = obj
2703
2707
  obj.this = inspect.getmodule(obj)
@@ -2730,9 +2734,9 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2730
2734
  @globals.deleter
2731
2735
  def globals(self): # internal use only
2732
2736
  self.interp.globals = self.__target.__dict__
2737
+ self.interp.globals.update(self.__globals)
2733
2738
 
2734
- ## (override)
2735
- wrap = EditorInterface.wrap
2739
+ __globals = {}
2736
2740
 
2737
2741
  def __init__(self, parent, target, name="root",
2738
2742
  introText=None,
@@ -2741,14 +2745,13 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2741
2745
  **kwargs):
2742
2746
  Shell.__init__(self, parent,
2743
2747
  locals=target.__dict__,
2744
- interpShell=self,
2748
+ interpShell=self, # **kwds of InterpClass
2745
2749
  InterpClass=Interpreter,
2746
2750
  introText=introText,
2747
2751
  startupScript=startupScript,
2748
- execStartupScript=execStartupScript, # if True, executes ~/.py
2752
+ execStartupScript=execStartupScript, # executes ~/.py
2749
2753
  **kwargs)
2750
2754
  EditorInterface.__init__(self)
2751
- AutoCompInterfaceMixin.__init__(self)
2752
2755
 
2753
2756
  self.parent = parent #: parent<ShellFrame> is not Parent<AuiNotebook>
2754
2757
  self.target = target
@@ -2780,28 +2783,20 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2780
2783
  def clear(evt):
2781
2784
  ## """Clear selection and message, no skip."""
2782
2785
  ## *do not* clear autocomp, so that the event can skip to AutoComp properly.
2783
- ## if self.AutoCompActive():
2784
- ## self.AutoCompCancel() # may delete selection
2785
2786
  if self.CanEdit():
2786
- self.ReplaceSelection("")
2787
+ with self.off_undocollection():
2788
+ self.ReplaceSelection("")
2787
2789
  self.message("")
2788
2790
 
2789
2791
  def clear_autocomp(evt):
2790
- ## """Clear Autocomp, selection, and message."""
2792
+ ## """Clear autocomp, selection, and message."""
2791
2793
  if self.AutoCompActive():
2792
2794
  self.AutoCompCancel()
2793
2795
  if self.CanEdit():
2794
- self.ReplaceSelection("")
2796
+ with self.off_undocollection():
2797
+ self.ReplaceSelection("")
2795
2798
  self.message("")
2796
2799
 
2797
- def skip_autocomp(evt):
2798
- ## """Don't eat backward prompt whitespace."""
2799
- ## Prevent autocomp from eating prompts.
2800
- ## Quit to avoid backspace over the last non-continuation prompt.
2801
- if self.cpos == self.bolc:
2802
- self.handler('quit', evt)
2803
- evt.Skip()
2804
-
2805
2800
  def fork(evt):
2806
2801
  self.handler.fork(self.handler.current_event, evt)
2807
2802
 
@@ -2844,7 +2839,6 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2844
2839
  'C-enter pressed' : (0, _F(self.insertLineBreak)),
2845
2840
  'C-S-enter pressed' : (0, _F(self.insertLineBreak)),
2846
2841
  '*enter pressed' : (0, ), # -> OnShowCompHistory 無効
2847
- 'left pressed' : (0, self.OnBackspace),
2848
2842
  'C-[ pressed' : (0, _F(self.goto_previous_mark_arrow)),
2849
2843
  'C-S-[ pressed' : (0, _F(self.goto_previous_mark_arrow, selection=1)),
2850
2844
  'C-] pressed' : (0, _F(self.goto_next_mark_arrow)),
@@ -2915,7 +2909,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2915
2909
  'S-[a-z\\] released' : (2, self.call_word_autocomp),
2916
2910
  '\\ released' : (2, self.call_word_autocomp),
2917
2911
  '*delete pressed' : (2, skip),
2918
- '*backspace pressed' : (2, skip_autocomp),
2912
+ '*backspace pressed' : (2, self.OnBackspace),
2919
2913
  '*backspace released' : (2, self.call_word_autocomp),
2920
2914
  'C-S-backspace pressed' : (2, ),
2921
2915
  'C-j pressed' : (2, self.eval_line),
@@ -2943,7 +2937,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2943
2937
  'S-[a-z\\] released' : (3, self.call_apropos_autocomp),
2944
2938
  '\\ released' : (3, self.call_apropos_autocomp),
2945
2939
  '*delete pressed' : (3, skip),
2946
- '*backspace pressed' : (3, skip_autocomp),
2940
+ '*backspace pressed' : (3, self.OnBackspace),
2947
2941
  '*backspace released' : (3, self.call_apropos_autocomp),
2948
2942
  'C-S-backspace pressed' : (3, ),
2949
2943
  'C-j pressed' : (3, self.eval_line),
@@ -2971,7 +2965,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2971
2965
  'S-[a-z\\] released' : (4, self.call_text_autocomp),
2972
2966
  '\\ released' : (4, self.call_text_autocomp),
2973
2967
  '*delete pressed' : (4, skip),
2974
- '*backspace pressed' : (4, skip_autocomp),
2968
+ '*backspace pressed' : (4, self.OnBackspace),
2975
2969
  '*backspace released' : (4, self.call_text_autocomp),
2976
2970
  'C-S-backspace pressed' : (4, ),
2977
2971
  'C-j pressed' : (4, self.eval_line),
@@ -3000,7 +2994,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3000
2994
  '\\ released' : (5, self.call_module_autocomp),
3001
2995
  'M-m pressed' : (5, _F(self.call_module_autocomp, force=1)),
3002
2996
  '*delete pressed' : (5, skip),
3003
- '*backspace pressed' : (5, skip_autocomp),
2997
+ '*backspace pressed' : (5, self.OnBackspace),
3004
2998
  '*backspace released' : (5, self.call_module_autocomp),
3005
2999
  'C-S-backspace pressed' : (5, ),
3006
3000
  '*alt pressed' : (5, ),
@@ -3076,12 +3070,11 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3076
3070
  evt.Skip()
3077
3071
 
3078
3072
  def OnBackspace(self, evt):
3079
- """Called when backspace (or left key) pressed.
3080
- Backspace-guard from Autocomp eating over a prompt whitespace
3073
+ """Called when backspace pressed.
3074
+ Backspace-guard from autocomp eating over a prompt whitespace.
3081
3075
  """
3082
3076
  if self.cpos == self.bolc:
3083
- ## do not skip to prevent autocomp eats prompt,
3084
- ## so not to backspace over the latest non-continuation prompt
3077
+ self.handler('quit', evt) # don't eat backward prompt
3085
3078
  return
3086
3079
  evt.Skip()
3087
3080
 
@@ -3153,7 +3146,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3153
3146
  self.ReplaceSelection('self')
3154
3147
  elif st not in ('moji', 'word', 'rparen') or rst == 'word':
3155
3148
  self.handler('quit', evt) # don't enter autocomp
3156
- self.ReplaceSelection('.') # just write down a dot.
3149
+ evt.Skip()
3157
3150
 
3158
3151
  def on_enter_escmap(self, evt):
3159
3152
  self.__caret_mode = self.CaretPeriod
@@ -3203,7 +3196,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3203
3196
  def magic(self, cmd):
3204
3197
  """Called before command pushed.
3205
3198
 
3206
- (override) disable old magic: `f x --> f(x)`
3199
+ (override) Disable old magic: `f x --> f(x)`.
3207
3200
  """
3208
3201
  if cmd:
3209
3202
  if cmd[0:2] == '??': cmd = 'help({})'.format(cmd[2:])
@@ -3461,7 +3454,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3461
3454
  (override) Add globals when executing su:startupScript.
3462
3455
  Fix history point.
3463
3456
  """
3464
- ## self.globals = self.locals
3457
+ keys = set(self.locals.keys()) # check for locals map changes
3465
3458
  self.promptPosEnd = self.TextLength # fix history point
3466
3459
  if su and os.path.isfile(su):
3467
3460
  self.push("print('Startup script executed:', {0!r})\n".format(su))
@@ -3471,6 +3464,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3471
3464
  else:
3472
3465
  self.push("")
3473
3466
  self.interp.startupScript = None
3467
+ self.__globals = {k: self.locals[k] for k in (self.locals.keys() - keys)}
3474
3468
 
3475
3469
  def Paste(self, rectangle=False):
3476
3470
  """Replace selection with clipboard contents.
@@ -3617,8 +3611,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3617
3611
  self.EnsureCaretVisible()
3618
3612
 
3619
3613
  def eval_line(self, evt):
3620
- """Evaluate the selected word or line.
3621
- """
3614
+ """Evaluate the selected word or line."""
3622
3615
  if self.CallTipActive():
3623
3616
  self.CallTipCancel()
3624
3617
 
@@ -122,7 +122,7 @@ class Plugin(Layer):
122
122
  (1, "&Load file", Icon('open'),
123
123
  lambda v: self.load_media()),
124
124
 
125
- (2, "&Snapshot", Icon('clock'),
125
+ (2, "&Snapshot", Icon('clip'),
126
126
  lambda v: self.snapshot(),
127
127
  lambda v: v.Enable(self._path is not None)),
128
128
  (),
@@ -175,7 +175,7 @@ class Plugin(Layer):
175
175
  def load_media(self, path=None):
176
176
  if path is None:
177
177
  with wx.FileDialog(self, "Choose a media file",
178
- style=wx.FD_OPEN|wx.FD_CHANGE_DIR|wx.FD_FILE_MUST_EXIST) as dlg:
178
+ style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) as dlg:
179
179
  if dlg.ShowModal() != wx.ID_OK:
180
180
  return None
181
181
  path = dlg.Path
mwx/utilus.py CHANGED
@@ -886,7 +886,7 @@ class FSM(dict):
886
886
  return False
887
887
 
888
888
 
889
- class TreeList(object):
889
+ class TreeList:
890
890
  """Interface class for tree list control.
891
891
 
892
892
  >>> list[item:(key,value)]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mwxlib
3
- Version: 0.98.1
3
+ Version: 0.98.5
4
4
  Summary: A wrapper of matplotlib and wxPython (phoenix)
5
5
  Home-page: https://github.com/komoto48g/mwxlib
6
6
  Author: Kazuya O'moto
@@ -1,28 +1,28 @@
1
1
  mwx/__init__.py,sha256=nN62CGTWjME7Zz2h-jIRB8MxwuErIkHPGrlBzydkF0o,643
2
2
  mwx/bookshelf.py,sha256=Y4xI2SrEO22DrI1hyyfFx7DfFZA8znOzX9RWMPsA2BE,5137
3
- mwx/controls.py,sha256=K4GJB81TXv5q0faTELQgbUmtMQBRIM9yINJdTI0xA3g,47556
4
- mwx/framework.py,sha256=xSh2cxTyg6SHU3jh9cGTaiC2u7teJPJwRuFrmEfSXiQ,75509
5
- mwx/graphman.py,sha256=Cyhl_Da_HGBfk721gu1r5iwSH9L3yPEG8Fzmc2gx-EU,70462
3
+ mwx/controls.py,sha256=jhru4HiIijb3QJz2elGt0in9soaR3xilgHsfItYY0JI,47595
4
+ mwx/framework.py,sha256=I4Hvu2swHbgvbjqVXNnuU2V8kkxKmK6eRktqQa5D0cE,75518
5
+ mwx/graphman.py,sha256=A53ufapRAysL0wTCr1anrg_T_PqpumonnYn-swaC598,70165
6
6
  mwx/images.py,sha256=_-Eh3xF7Khu42ivkYp97NXIzSNGbjcidqtWjZQFGtqE,47827
7
7
  mwx/matplot2.py,sha256=xCJ_ZzdDEWmzctpPaOrzTnwXyHINP4nfFHweoTZa6ug,32899
8
- mwx/matplot2g.py,sha256=wiZFDFuQe3ax71fmyeR_9hvAmgT-4nVfZ30UByv8Nv8,64379
8
+ mwx/matplot2g.py,sha256=3hS0ilXCif0mZkSufE_Rf-taRs3m1hIxiIFMuioYYuc,64371
9
9
  mwx/matplot2lg.py,sha256=JRWjWnLJUytbSq6wxs4P0gbVUr3xoLSF6Wwqd5V_pJI,27404
10
- mwx/mgplt.py,sha256=ITzxA97yDwr_35BUk5OqnyskSuKVDbpf2AQCKY1jHTI,5671
11
- mwx/nutshell.py,sha256=nTEKmJ0CMqkVtbmIlDHvZDpkUjefgL6QbxMZKwbZpIk,141553
12
- mwx/utilus.py,sha256=mmqB4P_3mTi7SrFleMiN1599Jm0Us0XKnNA6v2xglSs,37333
10
+ mwx/mgplt.py,sha256=r56SFryorIgO12mvHJY-z6uwWsaPEqk6pHYwKWqbTY4,5663
11
+ mwx/nutshell.py,sha256=vz3RTrIECi9LpjgAj2jKAIjMCeJ7ZvJ8mbH3phBxuvg,141423
12
+ mwx/utilus.py,sha256=B76pDg6_kW8FMNdQ6xO0Bwy4KJ0laY98Gg6N3iqV7c8,37325
13
13
  mwx/wxmon.py,sha256=f3V24EF7kdMlYF7usLYK9QE5KU6fSu0jVqsvwAiA-Ag,12647
14
14
  mwx/wxpdb.py,sha256=lLowkkAgMhPFHAfklD7wZHq0qbSMjRxnBFtSajmVgME,19133
15
15
  mwx/wxwil.py,sha256=hhyB1lPrF9ixeObxCOKQv0Theu-B-kpJg_yVU3EGSNg,5406
16
16
  mwx/wxwit.py,sha256=ifxMwdIz-QhDEr8vyAztToF8VVSxKNXlq4Ap1awBZvo,7362
17
17
  mwx/plugins/__init__.py,sha256=jnJ-Sl9XJ_7BFDslD_r7dsbxsOT57q_IaEriV53XIGY,41
18
- mwx/plugins/ffmpeg_view.py,sha256=1s7QqIbOpqNrSwbNoc4wQQNo8o1ho5JfXLuseAhgzK4,10441
18
+ mwx/plugins/ffmpeg_view.py,sha256=epO_26sPmdDyr_lApa2Jtgxp-yCEdW0OEz0_QbrFEuM,10427
19
19
  mwx/plugins/fft_view.py,sha256=xxTDD-_z4l18u4t2ybPB3xAMIslJmJ0gQlTxEqJUhNI,2782
20
20
  mwx/plugins/frame_listview.py,sha256=hbApzZWa9-BmQthu7uZBlBbGbtf4iJ_prO8IhxoGMs8,10421
21
21
  mwx/plugins/line_profile.py,sha256=--9NIc3x5EfRB3L59JvD7rzENQHyiYfu7wWJo6AuMkA,820
22
22
  mwx/py/__init__.py,sha256=xykgfOytOwNuvXsfkLoumFZSTN-iBsHOjczYXngjmUE,12
23
23
  mwx/py/filling.py,sha256=fumUG1F5M9TL-Dfqni4G85uk7TmvnUunTbdcPDV0vfo,16857
24
- mwxlib-0.98.1.dist-info/LICENSE,sha256=PGtRKCaTkmUDlBQwpptJAxJtdqxIUtAmdBsaT9nUVkA,1091
25
- mwxlib-0.98.1.dist-info/METADATA,sha256=XcUcwip9zlP_OGddgjt4s8fHdRvz7pGU-y1NS1t4kXg,1880
26
- mwxlib-0.98.1.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
27
- mwxlib-0.98.1.dist-info/top_level.txt,sha256=SI1Mh118AstnUFGPNq5aMNKiAnVNmZk1S9Ij-OwAEpY,4
28
- mwxlib-0.98.1.dist-info/RECORD,,
24
+ mwxlib-0.98.5.dist-info/LICENSE,sha256=PGtRKCaTkmUDlBQwpptJAxJtdqxIUtAmdBsaT9nUVkA,1091
25
+ mwxlib-0.98.5.dist-info/METADATA,sha256=nGXEgL3ckNH2HlXpYXQsjteMpc8MhQWDZ_5DtjAtp2A,1880
26
+ mwxlib-0.98.5.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
27
+ mwxlib-0.98.5.dist-info/top_level.txt,sha256=SI1Mh118AstnUFGPNq5aMNKiAnVNmZk1S9Ij-OwAEpY,4
28
+ mwxlib-0.98.5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (72.1.0)
2
+ Generator: setuptools (72.2.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5