mwxlib 0.98.0__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.0"
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
@@ -72,14 +71,22 @@ def ask(f, prompt="Enter value", type=str):
72
71
 
73
72
 
74
73
  class AutoCompInterfaceMixin:
75
- """Auto completion interface.
74
+ """Auto-complete mode interface.
75
+
76
+ Mode name Mode vars.
77
+ --------------------------------
78
+ [1] history-comp history (an instance variable of the Shell)
79
+ [2] word-comp -
80
+ [3] apropos-comp -
81
+ [4] text-comp fragmwords
82
+ [5] module-comp modules
76
83
 
77
84
  Note:
78
85
  This class is mixed-in ``wx.py.editwindow.EditWindow``.
79
86
  """
80
- modules = set() # to be used in module-comp mode
81
-
82
- fragmwords = set(keyword.kwlist + dir(builtins)) # to be used in text-comp mode
87
+ history = [] # used in history-comp mode
88
+ modules = set() # used in module-comp mode
89
+ fragmwords = set(keyword.kwlist + dir(builtins)) # used in text-comp mode
83
90
 
84
91
  def __init__(self):
85
92
  ## cf. sys.modules
@@ -94,13 +101,13 @@ class AutoCompInterfaceMixin:
94
101
  (override) Snip the tip of max N lines if it is too long.
95
102
  Keep the tip for calltip-click event.
96
103
  """
97
- self._calltip_pos = pos
98
- self._calltip = tip
104
+ self._calltips = [pos, tip, False]
99
105
  lines = tip.splitlines()
100
- if len(lines) > N > 0:
106
+ if N and len(lines) > N > 0:
101
107
  lines[N+1:] = ["\n...(snip) This tips are too long... "
102
108
  "Click to show more details."]
103
109
  tip = '\n'.join(lines)
110
+ self._calltips[-1] = True # snipped (needs to be shown)
104
111
  super().CallTipShow(pos, tip)
105
112
 
106
113
  def autoCallTipShow(self, command, insertcalltip=True):
@@ -135,7 +142,7 @@ class AutoCompInterfaceMixin:
135
142
  self.message("- {} : {!r}".format(e, text))
136
143
 
137
144
  def call_helpTip(self, evt):
138
- """Show tooltips for the selected topic."""
145
+ """Show a calltip for the selected function."""
139
146
  if self.CallTipActive():
140
147
  self.CallTipCancel()
141
148
 
@@ -167,8 +174,11 @@ class AutoCompInterfaceMixin:
167
174
  n = len(self.__comp_hint)
168
175
  p = self.cpos
169
176
  if not self.SelectedText:
170
- p, self.anchor, sty = self.get_following_atom(p) # word-right-selection
171
- 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:])
172
182
  self.cpos = p # backward selection to the point
173
183
  self.__comp_ind = j
174
184
  except IndexError:
@@ -257,7 +267,7 @@ class AutoCompInterfaceMixin:
257
267
  self.message("[module]>>> loading {}...".format(text))
258
268
  modules = set(dir(import_module(text)))
259
269
  except ImportError as e:
260
- self.message("\b failed:", e)
270
+ self.message("\b failed.", e)
261
271
  return
262
272
  else:
263
273
  ## Add unimported module names.
@@ -349,7 +359,7 @@ class AutoCompInterfaceMixin:
349
359
  self.message("- {} : {!r}".format(e, text))
350
360
 
351
361
 
352
- class EditorInterface(CtrlInterface):
362
+ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
353
363
  """Interface of Python code editor.
354
364
 
355
365
  Note:
@@ -357,6 +367,7 @@ class EditorInterface(CtrlInterface):
357
367
  """
358
368
  def __init__(self):
359
369
  CtrlInterface.__init__(self)
370
+ AutoCompInterfaceMixin.__init__(self)
360
371
 
361
372
  def dispatch(evt):
362
373
  """Fork events to the parent."""
@@ -407,8 +418,8 @@ class EditorInterface(CtrlInterface):
407
418
  'C-S-; pressed' : (0, _F(self.comment_out_line)),
408
419
  'C-: pressed' : (0, _F(self.uncomment_line)),
409
420
  'C-S-: pressed' : (0, _F(self.uncomment_line)),
410
- 'select_line' : (100, self.on_linesel_begin),
411
- 'select_lines' : (100, self.on_linesel_next),
421
+ 'select_line' : (11, self.on_linesel_begin),
422
+ 'select_lines' : (11, self.on_linesel_next),
412
423
  },
413
424
  10 : {
414
425
  'quit' : (0, self.on_itext_exit),
@@ -417,8 +428,8 @@ class EditorInterface(CtrlInterface):
417
428
  'down pressed' : (10, skip),
418
429
  'enter pressed' : (0, self.on_itext_selection),
419
430
  },
420
- 100 : {
421
- '*Ldrag move' : (100, self.on_linesel_motion),
431
+ 11 : {
432
+ '*Ldrag move' : (11, self.on_linesel_motion),
422
433
  'capture_lost' : (0, self.on_linesel_end),
423
434
  'Lbutton released' : (0, self.on_linesel_end),
424
435
  },
@@ -880,6 +891,7 @@ class EditorInterface(CtrlInterface):
880
891
  ## --------------------------------
881
892
  ## Python syntax and indentation
882
893
  ## --------------------------------
894
+
883
895
  def on_indent_line(self, evt):
884
896
  if self.SelectedText:
885
897
  evt.Skip()
@@ -1184,10 +1196,10 @@ class EditorInterface(CtrlInterface):
1184
1196
  """
1185
1197
  n = self.LinesOnScreen() # lines completely visible
1186
1198
  m = n//2 if ln is None else ln % n if ln < n else n # ln[0:n]
1187
- vl = self.calc_vline(self.cline)
1199
+ vl = self._calc_vline(self.cline)
1188
1200
  self.ScrollToLine(vl - m)
1189
1201
 
1190
- def calc_vline(self, line):
1202
+ def _calc_vline(self, line):
1191
1203
  """Virtual line numberin the buffer window."""
1192
1204
  pos = self.PositionFromLine(line)
1193
1205
  w, h = self.PointFromPosition(pos)
@@ -1199,7 +1211,7 @@ class EditorInterface(CtrlInterface):
1199
1211
  """
1200
1212
  n = self.LinesOnScreen() # lines completely visible
1201
1213
  hl = self.FirstVisibleLine
1202
- vl = self.calc_vline(line)
1214
+ vl = self._calc_vline(line)
1203
1215
  if vl < hl:
1204
1216
  self.ScrollToLine(vl)
1205
1217
  elif vl > hl + n - 1:
@@ -1212,7 +1224,7 @@ class EditorInterface(CtrlInterface):
1212
1224
  """
1213
1225
  n = self.LinesOnScreen() # lines completely visible
1214
1226
  hl = self.FirstVisibleLine
1215
- vl = self.calc_vline(line)
1227
+ vl = self._calc_vline(line)
1216
1228
  if not hl + offset < vl < hl + n - 1 - offset:
1217
1229
  self.ScrollToLine(vl - n//2)
1218
1230
 
@@ -1505,16 +1517,6 @@ class EditorInterface(CtrlInterface):
1505
1517
  else:
1506
1518
  self.anchor = q
1507
1519
 
1508
- @contextmanager
1509
- def off_readonly(self):
1510
- """Set buffer to be writable (ReadOnly=False) temporarily."""
1511
- r = self.ReadOnly
1512
- try:
1513
- self.ReadOnly = 0
1514
- yield
1515
- finally:
1516
- self.ReadOnly = r
1517
-
1518
1520
  @contextmanager
1519
1521
  def save_attributes(self, **kwargs):
1520
1522
  """Save buffer attributes (e.g. ReadOnly=False)."""
@@ -1527,6 +1529,14 @@ class EditorInterface(CtrlInterface):
1527
1529
  for k, v in kwargs.items():
1528
1530
  setattr(self, k, v)
1529
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
+
1530
1540
  ## --------------------------------
1531
1541
  ## Edit: comment / insert / kill
1532
1542
  ## --------------------------------
@@ -1633,7 +1643,7 @@ class EditorInterface(CtrlInterface):
1633
1643
  self.ReplaceSelection('')
1634
1644
 
1635
1645
 
1636
- class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
1646
+ class Buffer(EditorInterface, EditWindow):
1637
1647
  """Python code buffer.
1638
1648
 
1639
1649
  Attributes:
@@ -1759,7 +1769,6 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
1759
1769
  def __init__(self, parent, filename=None, **kwargs):
1760
1770
  EditWindow.__init__(self, parent, **kwargs)
1761
1771
  EditorInterface.__init__(self)
1762
- AutoCompInterfaceMixin.__init__(self)
1763
1772
 
1764
1773
  self.parent = parent
1765
1774
  self.__filename = filename
@@ -1788,18 +1797,18 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
1788
1797
  def clear(evt):
1789
1798
  ## """Clear selection and message, no skip."""
1790
1799
  ## *do not* clear autocomp, so that the event can skip to AutoComp properly.
1791
- ## if self.AutoCompActive():
1792
- ## self.AutoCompCancel() # may delete selection
1793
1800
  if self.CanEdit():
1794
- self.ReplaceSelection("")
1801
+ with self.off_undocollection():
1802
+ self.ReplaceSelection("")
1795
1803
  self.message("")
1796
1804
 
1797
1805
  def clear_autocomp(evt):
1798
- ## """Clear Autocomp, selection, and message."""
1806
+ ## """Clear autocomp, selection, and message."""
1799
1807
  if self.AutoCompActive():
1800
1808
  self.AutoCompCancel()
1801
1809
  if self.CanEdit():
1802
- self.ReplaceSelection("")
1810
+ with self.off_undocollection():
1811
+ self.ReplaceSelection("")
1803
1812
  self.message("")
1804
1813
 
1805
1814
  def fork(evt):
@@ -1833,10 +1842,9 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
1833
1842
  '* released' : (0, skip, dispatch),
1834
1843
  'escape pressed' : (-1, self.on_enter_escmap),
1835
1844
  'C-h pressed' : (0, self.call_helpTip),
1836
- '. pressed' : (2, skip),
1845
+ '. pressed' : (2, self.OnEnterDot),
1837
1846
  'M-. pressed' : (2, self.call_word_autocomp),
1838
1847
  'M-/ pressed' : (3, self.call_apropos_autocomp),
1839
- 'M-m pressed' : (5, self.call_module_autocomp),
1840
1848
  },
1841
1849
  2 : { # word auto completion AS-mode
1842
1850
  'quit' : (0, clear_autocomp),
@@ -1892,34 +1900,6 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
1892
1900
  '*[LR]win pressed' : (3, ),
1893
1901
  '*f[0-9]* pressed' : (3, ),
1894
1902
  },
1895
- 5 : { # module auto completion AS-mode
1896
- 'quit' : (0, clear_autocomp),
1897
- '* pressed' : (0, clear_autocomp, fork),
1898
- 'tab pressed' : (0, clear, skip),
1899
- 'enter pressed' : (0, clear, skip),
1900
- 'escape pressed' : (0, clear_autocomp),
1901
- 'up pressed' : (5, skip, self.on_completion_backward),
1902
- 'down pressed' : (5, skip, self.on_completion_forward),
1903
- '*left pressed' : (5, skip),
1904
- '*left released' : (5, self.call_module_autocomp),
1905
- '*right pressed' : (5, skip),
1906
- '*right released' : (5, self.call_module_autocomp),
1907
- '[a-z0-9_.,] pressed' : (5, skip),
1908
- '[a-z0-9_.,] released' : (5, self.call_module_autocomp),
1909
- 'S-[a-z\\] pressed' : (5, skip),
1910
- 'S-[a-z\\] released' : (5, self.call_module_autocomp),
1911
- '\\ released' : (5, self.call_module_autocomp),
1912
- 'M-m pressed' : (5, _F(self.call_module_autocomp, force=1)),
1913
- '*delete pressed' : (5, skip),
1914
- '*backspace pressed' : (5, skip),
1915
- '*backspace released' : (5, self.call_module_autocomp),
1916
- 'C-S-backspace pressed' : (5, ),
1917
- '*alt pressed' : (5, ),
1918
- '*ctrl pressed' : (5, ),
1919
- '*shift pressed' : (5, ),
1920
- '*[LR]win pressed' : (5, ),
1921
- '*f[0-9]* pressed' : (5, ),
1922
- },
1923
1903
  })
1924
1904
 
1925
1905
  self.show_folder()
@@ -1944,7 +1924,9 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
1944
1924
  def OnCallTipClick(self, evt):
1945
1925
  if self.CallTipActive():
1946
1926
  self.CallTipCancel()
1947
- self.CallTipShow(self._calltip_pos, self._calltip, N=-1)
1927
+ pos, tip, more = self._calltips
1928
+ if more:
1929
+ self.CallTipShow(pos, tip, N=None)
1948
1930
  evt.Skip()
1949
1931
 
1950
1932
  def OnIndicatorClick(self, evt):
@@ -1982,6 +1964,14 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
1982
1964
  self.update_caption()
1983
1965
  evt.Skip()
1984
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
+
1985
1975
  def on_activated(self, buf):
1986
1976
  """Called when the buffer is activated."""
1987
1977
  self.update_caption()
@@ -2074,6 +2064,9 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
2074
2064
  dispatcher.send(signal='Interpreter.push',
2075
2065
  sender=self, command=None, more=False)
2076
2066
 
2067
+ def eval_line(self):
2068
+ self.py_eval_line(self.globals, self.locals)
2069
+
2077
2070
  def py_eval_line(self, globals=None, locals=None):
2078
2071
  if self.CallTipActive():
2079
2072
  self.CallTipCancel()
@@ -2090,6 +2083,9 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
2090
2083
  return
2091
2084
  self.message(status)
2092
2085
 
2086
+ def exec_region(self):
2087
+ self.py_exec_region(self.globals, self.locals)
2088
+
2093
2089
  def py_exec_region(self, globals=None, locals=None, filename=None):
2094
2090
  if not filename:
2095
2091
  filename = self.filename
@@ -2122,23 +2118,6 @@ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
2122
2118
  self.handler('buffer_region_executed', self)
2123
2119
  self.message("Evaluated {!r} successfully.".format(filename))
2124
2120
  self.AnnotationClearAll()
2125
-
2126
- def py_get_region(self, line):
2127
- """Line numbers of code head and tail containing the line.
2128
-
2129
- Requires a code object.
2130
- If the code doesn't exist, return the folding region.
2131
- """
2132
- if not self.code:
2133
- return self.get_region(line)
2134
- lc, le = 0, self.LineCount
2135
- linestarts = list(dis.findlinestarts(self.code))
2136
- for i, ln in reversed(linestarts):
2137
- if line >= ln-1:
2138
- lc = ln-1
2139
- break
2140
- le = ln-1
2141
- return lc, le
2142
2121
 
2143
2122
 
2144
2123
  class EditorBook(AuiNotebook, CtrlInterface):
@@ -2424,7 +2403,7 @@ class EditorBook(AuiNotebook, CtrlInterface):
2424
2403
  return True
2425
2404
  return False
2426
2405
  except Exception as e:
2427
- self.post_message(f"Failed to load {filename!r}:", e)
2406
+ self.post_message(f"Failed to load {filename!r}.", e)
2428
2407
  self.delete_buffer(buf)
2429
2408
  return False
2430
2409
 
@@ -2432,6 +2411,7 @@ class EditorBook(AuiNotebook, CtrlInterface):
2432
2411
  """Open the specified file."""
2433
2412
  if not filename:
2434
2413
  with wx.FileDialog(self, "Open buffer",
2414
+ defaultDir=os.path.dirname(self.buffer.filename),
2435
2415
  wildcard='|'.join(self.wildcards),
2436
2416
  style=wx.FD_OPEN|wx.FD_MULTIPLE) as dlg:
2437
2417
  if dlg.ShowModal() == wx.ID_OK:
@@ -2466,7 +2446,7 @@ class EditorBook(AuiNotebook, CtrlInterface):
2466
2446
  return True
2467
2447
  return False
2468
2448
  except Exception as e:
2469
- self.post_message(f"Failed to save {filename!r}:", e)
2449
+ self.post_message(f"Failed to save {filename!r}.", e)
2470
2450
  return False
2471
2451
 
2472
2452
  def load_buffer(self, buf=None):
@@ -2499,6 +2479,7 @@ class EditorBook(AuiNotebook, CtrlInterface):
2499
2479
  """Confirm the saveas with the dialog."""
2500
2480
  buf = buf or self.buffer
2501
2481
  with wx.FileDialog(self, "Save buffer as",
2482
+ defaultDir=os.path.dirname(self.buffer.filename),
2502
2483
  defaultFile=re.sub(r'[\/:*?"<>|]', '_', buf.name),
2503
2484
  wildcard='|'.join(self.wildcards),
2504
2485
  style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) as dlg:
@@ -2541,11 +2522,10 @@ class EditorBook(AuiNotebook, CtrlInterface):
2541
2522
  class Interpreter(interpreter.Interpreter):
2542
2523
  """Interpreter based on code.InteractiveInterpreter.
2543
2524
  """
2544
- def __init__(self, *args, **kwargs):
2545
- parent = kwargs.pop('interpShell')
2525
+ def __init__(self, interpShell, *args, **kwargs):
2546
2526
  interpreter.Interpreter.__init__(self, *args, **kwargs)
2547
2527
 
2548
- self.parent = parent
2528
+ self.parent = interpShell
2549
2529
  self.globals = self.locals
2550
2530
 
2551
2531
  def runcode(self, code):
@@ -2609,7 +2589,7 @@ class Interpreter(interpreter.Interpreter):
2609
2589
  return interpreter.Interpreter.getCallTip(self) # dummy
2610
2590
 
2611
2591
 
2612
- class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2592
+ class Nautilus(EditorInterface, Shell):
2613
2593
  """Nautilus in the Shell.
2614
2594
 
2615
2595
  Facade objects for accessing the APIs:
@@ -2642,24 +2622,24 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2642
2622
  ``*`` denotes the original syntax defined in wx.py.shell,
2643
2623
  for which, at present version, enabled with USE_MAGIC switch being on.
2644
2624
 
2645
- Autocomp-key bindings::
2625
+ Auto-complete key bindings::
2646
2626
 
2647
2627
  C-up : [0] retrieve previous history
2648
2628
  C-down : [0] retrieve next history
2649
- C-j(J), M-j : [0] call tooltip of eval (for the word selected or focused)
2650
- C-h(H), M-h : [0] call tooltip of help (for the func selected or focused)
2651
- TAB : [1] history-comp-mode
2652
- M-p : [1] retrieve previous history in comp-mode
2653
- M-n : [1] retrieve next history in comp-mode
2654
- M-. : [2] word-comp-mode
2655
- M-/ : [3] apropos-comp-mode
2656
- M-, : [4] text-comp-mode
2657
- M-m : [5] module-comp-mode
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)
2631
+ TAB : [1] history-comp
2632
+ M-p : [1] retrieve previous history in history-comp mode
2633
+ M-n : [1] retrieve next history in history-comp mode
2634
+ M-. : [2] word-comp
2635
+ M-/ : [3] apropos-comp
2636
+ M-, : [4] text-comp
2637
+ M-m : [5] module-comp
2658
2638
 
2659
2639
  Autocomps are incremental when pressed any alnums,
2660
2640
  and decremental when backspace.
2661
2641
 
2662
- Enter-key bindings::
2642
+ Enter key bindings::
2663
2643
 
2664
2644
  C-enter : insert-line-break
2665
2645
  M-enter : duplicate-command
@@ -2719,7 +2699,9 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2719
2699
  raise TypeError("primitive objects cannot be targeted")
2720
2700
 
2721
2701
  self.__target = obj
2722
- self.interp.locals = obj.__dict__
2702
+ self.locals = obj.__dict__
2703
+ self.globals = obj.__dict__
2704
+ self.globals.update(self.__globals)
2723
2705
  try:
2724
2706
  obj.self = obj
2725
2707
  obj.this = inspect.getmodule(obj)
@@ -2752,9 +2734,9 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2752
2734
  @globals.deleter
2753
2735
  def globals(self): # internal use only
2754
2736
  self.interp.globals = self.__target.__dict__
2737
+ self.interp.globals.update(self.__globals)
2755
2738
 
2756
- ## (override)
2757
- wrap = EditorInterface.wrap
2739
+ __globals = {}
2758
2740
 
2759
2741
  def __init__(self, parent, target, name="root",
2760
2742
  introText=None,
@@ -2763,14 +2745,13 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2763
2745
  **kwargs):
2764
2746
  Shell.__init__(self, parent,
2765
2747
  locals=target.__dict__,
2766
- interpShell=self,
2748
+ interpShell=self, # **kwds of InterpClass
2767
2749
  InterpClass=Interpreter,
2768
2750
  introText=introText,
2769
2751
  startupScript=startupScript,
2770
- execStartupScript=execStartupScript, # if True, executes ~/.py
2752
+ execStartupScript=execStartupScript, # executes ~/.py
2771
2753
  **kwargs)
2772
2754
  EditorInterface.__init__(self)
2773
- AutoCompInterfaceMixin.__init__(self)
2774
2755
 
2775
2756
  self.parent = parent #: parent<ShellFrame> is not Parent<AuiNotebook>
2776
2757
  self.target = target
@@ -2802,28 +2783,20 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2802
2783
  def clear(evt):
2803
2784
  ## """Clear selection and message, no skip."""
2804
2785
  ## *do not* clear autocomp, so that the event can skip to AutoComp properly.
2805
- ## if self.AutoCompActive():
2806
- ## self.AutoCompCancel() # may delete selection
2807
2786
  if self.CanEdit():
2808
- self.ReplaceSelection("")
2787
+ with self.off_undocollection():
2788
+ self.ReplaceSelection("")
2809
2789
  self.message("")
2810
2790
 
2811
2791
  def clear_autocomp(evt):
2812
- ## """Clear Autocomp, selection, and message."""
2792
+ ## """Clear autocomp, selection, and message."""
2813
2793
  if self.AutoCompActive():
2814
2794
  self.AutoCompCancel()
2815
2795
  if self.CanEdit():
2816
- self.ReplaceSelection("")
2796
+ with self.off_undocollection():
2797
+ self.ReplaceSelection("")
2817
2798
  self.message("")
2818
2799
 
2819
- def skip_autocomp(evt):
2820
- ## """Don't eat backward prompt whitespace."""
2821
- ## Prevent autocomp from eating prompts.
2822
- ## Quit to avoid backspace over the last non-continuation prompt.
2823
- if self.cpos == self.bolc:
2824
- self.handler('quit', evt)
2825
- evt.Skip()
2826
-
2827
2800
  def fork(evt):
2828
2801
  self.handler.fork(self.handler.current_event, evt)
2829
2802
 
@@ -2866,7 +2839,6 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2866
2839
  'C-enter pressed' : (0, _F(self.insertLineBreak)),
2867
2840
  'C-S-enter pressed' : (0, _F(self.insertLineBreak)),
2868
2841
  '*enter pressed' : (0, ), # -> OnShowCompHistory 無効
2869
- 'left pressed' : (0, self.OnBackspace),
2870
2842
  'C-[ pressed' : (0, _F(self.goto_previous_mark_arrow)),
2871
2843
  'C-S-[ pressed' : (0, _F(self.goto_previous_mark_arrow, selection=1)),
2872
2844
  'C-] pressed' : (0, _F(self.goto_next_mark_arrow)),
@@ -2937,7 +2909,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2937
2909
  'S-[a-z\\] released' : (2, self.call_word_autocomp),
2938
2910
  '\\ released' : (2, self.call_word_autocomp),
2939
2911
  '*delete pressed' : (2, skip),
2940
- '*backspace pressed' : (2, skip_autocomp),
2912
+ '*backspace pressed' : (2, self.OnBackspace),
2941
2913
  '*backspace released' : (2, self.call_word_autocomp),
2942
2914
  'C-S-backspace pressed' : (2, ),
2943
2915
  'C-j pressed' : (2, self.eval_line),
@@ -2965,7 +2937,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2965
2937
  'S-[a-z\\] released' : (3, self.call_apropos_autocomp),
2966
2938
  '\\ released' : (3, self.call_apropos_autocomp),
2967
2939
  '*delete pressed' : (3, skip),
2968
- '*backspace pressed' : (3, skip_autocomp),
2940
+ '*backspace pressed' : (3, self.OnBackspace),
2969
2941
  '*backspace released' : (3, self.call_apropos_autocomp),
2970
2942
  'C-S-backspace pressed' : (3, ),
2971
2943
  'C-j pressed' : (3, self.eval_line),
@@ -2993,7 +2965,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2993
2965
  'S-[a-z\\] released' : (4, self.call_text_autocomp),
2994
2966
  '\\ released' : (4, self.call_text_autocomp),
2995
2967
  '*delete pressed' : (4, skip),
2996
- '*backspace pressed' : (4, skip_autocomp),
2968
+ '*backspace pressed' : (4, self.OnBackspace),
2997
2969
  '*backspace released' : (4, self.call_text_autocomp),
2998
2970
  'C-S-backspace pressed' : (4, ),
2999
2971
  'C-j pressed' : (4, self.eval_line),
@@ -3022,7 +2994,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3022
2994
  '\\ released' : (5, self.call_module_autocomp),
3023
2995
  'M-m pressed' : (5, _F(self.call_module_autocomp, force=1)),
3024
2996
  '*delete pressed' : (5, skip),
3025
- '*backspace pressed' : (5, skip_autocomp),
2997
+ '*backspace pressed' : (5, self.OnBackspace),
3026
2998
  '*backspace released' : (5, self.call_module_autocomp),
3027
2999
  'C-S-backspace pressed' : (5, ),
3028
3000
  '*alt pressed' : (5, ),
@@ -3070,7 +3042,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3070
3042
  def OnCallTipClick(self, evt):
3071
3043
  if self.CallTipActive():
3072
3044
  self.CallTipCancel()
3073
- self.parent.handler('add_help', self._calltip)
3045
+ self.parent.handler('add_help', self._calltips[1])
3074
3046
  evt.Skip()
3075
3047
 
3076
3048
  def OnDrag(self, evt): #<wx._core.StyledTextEvent>
@@ -3098,12 +3070,11 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3098
3070
  evt.Skip()
3099
3071
 
3100
3072
  def OnBackspace(self, evt):
3101
- """Called when backspace (or left key) pressed.
3102
- Backspace-guard from Autocomp eating over a prompt whitespace
3073
+ """Called when backspace pressed.
3074
+ Backspace-guard from autocomp eating over a prompt whitespace.
3103
3075
  """
3104
3076
  if self.cpos == self.bolc:
3105
- ## do not skip to prevent autocomp eats prompt,
3106
- ## so not to backspace over the latest non-continuation prompt
3077
+ self.handler('quit', evt) # don't eat backward prompt
3107
3078
  return
3108
3079
  evt.Skip()
3109
3080
 
@@ -3175,7 +3146,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3175
3146
  self.ReplaceSelection('self')
3176
3147
  elif st not in ('moji', 'word', 'rparen') or rst == 'word':
3177
3148
  self.handler('quit', evt) # don't enter autocomp
3178
- self.ReplaceSelection('.') # just write down a dot.
3149
+ evt.Skip()
3179
3150
 
3180
3151
  def on_enter_escmap(self, evt):
3181
3152
  self.__caret_mode = self.CaretPeriod
@@ -3225,7 +3196,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3225
3196
  def magic(self, cmd):
3226
3197
  """Called before command pushed.
3227
3198
 
3228
- (override) disable old magic: `f x --> f(x)`
3199
+ (override) Disable old magic: `f x --> f(x)`.
3229
3200
  """
3230
3201
  if cmd:
3231
3202
  if cmd[0:2] == '??': cmd = 'help({})'.format(cmd[2:])
@@ -3483,7 +3454,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3483
3454
  (override) Add globals when executing su:startupScript.
3484
3455
  Fix history point.
3485
3456
  """
3486
- ## self.globals = self.locals
3457
+ keys = set(self.locals.keys()) # check for locals map changes
3487
3458
  self.promptPosEnd = self.TextLength # fix history point
3488
3459
  if su and os.path.isfile(su):
3489
3460
  self.push("print('Startup script executed:', {0!r})\n".format(su))
@@ -3493,6 +3464,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3493
3464
  else:
3494
3465
  self.push("")
3495
3466
  self.interp.startupScript = None
3467
+ self.__globals = {k: self.locals[k] for k in (self.locals.keys() - keys)}
3496
3468
 
3497
3469
  def Paste(self, rectangle=False):
3498
3470
  """Replace selection with clipboard contents.
@@ -3639,8 +3611,7 @@ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
3639
3611
  self.EnsureCaretVisible()
3640
3612
 
3641
3613
  def eval_line(self, evt):
3642
- """Evaluate the selected word or line.
3643
- """
3614
+ """Evaluate the selected word or line."""
3644
3615
  if self.CallTipActive():
3645
3616
  self.CallTipCancel()
3646
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.0
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=kKG4rcBiq-PD5v1sHLtI1XRTve0wugG6JwwNbMv5Pa8,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=iVJGFh45ZtXs_g8LGi1UNI6H-3jI2pDkYNc2eF3T-7k,142853
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.0.dist-info/LICENSE,sha256=PGtRKCaTkmUDlBQwpptJAxJtdqxIUtAmdBsaT9nUVkA,1091
25
- mwxlib-0.98.0.dist-info/METADATA,sha256=qAaHXqyArN_ZNCNoeukt6YxYGPQ-n8h9JIgfAcKuBnU,1880
26
- mwxlib-0.98.0.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
27
- mwxlib-0.98.0.dist-info/top_level.txt,sha256=SI1Mh118AstnUFGPNq5aMNKiAnVNmZk1S9Ij-OwAEpY,4
28
- mwxlib-0.98.0.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