mwxlib 1.0.0__py3-none-any.whl → 1.7.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mwx/__init__.py +6 -4
- mwx/bookshelf.py +133 -57
- mwx/controls.py +441 -375
- mwx/framework.py +531 -513
- mwx/graphman.py +683 -677
- mwx/images.py +16 -0
- mwx/matplot2.py +225 -216
- mwx/matplot2g.py +735 -652
- mwx/matplot2lg.py +192 -183
- mwx/mgplt.py +20 -22
- mwx/nutshell.py +1063 -939
- mwx/plugins/ffmpeg_view.py +74 -75
- mwx/plugins/fft_view.py +13 -15
- mwx/plugins/frame_listview.py +55 -52
- mwx/plugins/line_profile.py +1 -1
- mwx/py/filling.py +9 -8
- mwx/testsuite.py +38 -0
- mwx/utilus.py +273 -210
- mwx/wxmon.py +39 -38
- mwx/wxpdb.py +81 -83
- mwx/wxwil.py +18 -18
- mwx/wxwit.py +32 -35
- {mwxlib-1.0.0.dist-info → mwxlib-1.7.13.dist-info}/METADATA +12 -5
- mwxlib-1.7.13.dist-info/RECORD +28 -0
- {mwxlib-1.0.0.dist-info → mwxlib-1.7.13.dist-info}/WHEEL +1 -1
- mwxlib-1.0.0.dist-info/LICENSE +0 -21
- mwxlib-1.0.0.dist-info/RECORD +0 -28
- {mwxlib-1.0.0.dist-info → mwxlib-1.7.13.dist-info}/top_level.txt +0 -0
mwx/framework.py
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#! python3
|
|
2
2
|
"""mwxlib framework.
|
|
3
3
|
"""
|
|
4
|
-
__version__ = "1.
|
|
4
|
+
__version__ = "1.7.13"
|
|
5
5
|
__author__ = "Kazuya O'moto <komoto@jeol.co.jp>"
|
|
6
6
|
|
|
7
7
|
from contextlib import contextmanager
|
|
8
|
+
from datetime import datetime
|
|
8
9
|
from functools import wraps, partial
|
|
9
10
|
from importlib import reload
|
|
10
11
|
import traceback
|
|
11
12
|
import builtins
|
|
12
|
-
import datetime
|
|
13
13
|
import textwrap
|
|
14
14
|
import time
|
|
15
15
|
import os
|
|
@@ -24,34 +24,33 @@ from .utilus import get_rootpath, ignore, warn
|
|
|
24
24
|
from .utilus import FSM, TreeList, apropos, typename, where, mro, pp
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
def deb(target=None, loop=True, locals=None,
|
|
27
|
+
def deb(target=None, loop=True, locals=None, **kwargs):
|
|
28
28
|
"""Dive into the process.
|
|
29
29
|
|
|
30
30
|
Args:
|
|
31
|
-
target
|
|
32
|
-
|
|
33
|
-
loop
|
|
34
|
-
locals
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
False => EVT_CLOSE will hide the window.
|
|
31
|
+
target: Object or module (default None).
|
|
32
|
+
If None, the target is set to `__main__`.
|
|
33
|
+
loop: If True, the app and the mainloop will be created.
|
|
34
|
+
locals: Additional context of the shell
|
|
35
|
+
|
|
36
|
+
**kwargs: ShellFrame and Nautilus arguments
|
|
37
|
+
|
|
38
|
+
- session: file name of the session. Defaults to None.
|
|
39
|
+
- standalone: True => EVT_CLOSE will close the window.
|
|
40
|
+
False => EVT_CLOSE will hide the window.
|
|
41
|
+
- introText: introductory of the shell
|
|
42
|
+
- startupScript: startup script file (default None)
|
|
43
|
+
- execStartupScript: True => Execute the startup script.
|
|
45
44
|
|
|
46
45
|
Note:
|
|
47
46
|
This will execute the startup script $(PYTHONSTARTUP).
|
|
48
47
|
"""
|
|
49
48
|
kwargs.setdefault("introText", f"mwx {__version__}\n")
|
|
50
49
|
kwargs.setdefault("execStartupScript", True)
|
|
51
|
-
kwargs.setdefault("
|
|
52
|
-
|
|
50
|
+
kwargs.setdefault("standalone", True)
|
|
51
|
+
|
|
53
52
|
app = wx.GetApp() or wx.App()
|
|
54
|
-
frame = ShellFrame(None, target,
|
|
53
|
+
frame = ShellFrame(None, target, **kwargs)
|
|
55
54
|
frame.Show()
|
|
56
55
|
frame.rootshell.SetFocus()
|
|
57
56
|
if locals:
|
|
@@ -74,12 +73,12 @@ def postcall(f):
|
|
|
74
73
|
@contextmanager
|
|
75
74
|
def save_focus_excursion():
|
|
76
75
|
"""Save window focus excursion."""
|
|
77
|
-
wnd = wx.Window.FindFocus()
|
|
76
|
+
wnd = wx.Window.FindFocus() # original focus
|
|
78
77
|
try:
|
|
79
78
|
yield wnd
|
|
80
79
|
finally:
|
|
81
80
|
if wnd and wnd.IsShownOnScreen():
|
|
82
|
-
wnd.SetFocus()
|
|
81
|
+
wnd.SetFocus() # restore focus
|
|
83
82
|
|
|
84
83
|
|
|
85
84
|
_speckeys = {
|
|
@@ -175,11 +174,11 @@ def hotkey(evt):
|
|
|
175
174
|
mod = ""
|
|
176
175
|
for k, v in ((wx.WXK_WINDOWS_LEFT, 'Lwin-'),
|
|
177
176
|
(wx.WXK_WINDOWS_RIGHT, 'Rwin-'),
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
177
|
+
# (wx.WXK_CONTROL, 'C-'),
|
|
178
|
+
# (wx.WXK_ALT, 'M-'),
|
|
179
|
+
# (wx.WXK_SHIFT, 'S-')
|
|
181
180
|
):
|
|
182
|
-
if key != k and wx.GetKeyState(k):
|
|
181
|
+
if key != k and wx.GetKeyState(k): # Note: lazy-eval state
|
|
183
182
|
mod += v
|
|
184
183
|
|
|
185
184
|
if key != wx.WXK_CONTROL and evt.controlDown: mod += "C-"
|
|
@@ -192,10 +191,10 @@ def hotkey(evt):
|
|
|
192
191
|
|
|
193
192
|
|
|
194
193
|
def regulate_key(key):
|
|
195
|
-
return (key.replace("ctrl-", "C-")
|
|
194
|
+
return (key.replace("ctrl-", "C-") # modifier keys abbreviation
|
|
196
195
|
.replace("alt-", "M-")
|
|
197
196
|
.replace("shift-", "S-")
|
|
198
|
-
.replace("M-C-", "C-M-")
|
|
197
|
+
.replace("M-C-", "C-M-") # modifier key regulation C-M-S-
|
|
199
198
|
.replace("S-M-", "M-S-")
|
|
200
199
|
.replace("S-C-", "C-S-"))
|
|
201
200
|
|
|
@@ -210,20 +209,20 @@ class KeyCtrlInterfaceMixin:
|
|
|
210
209
|
spec-map : 'C-c'
|
|
211
210
|
esc-map : 'escape'
|
|
212
211
|
"""
|
|
213
|
-
message = print
|
|
214
|
-
|
|
212
|
+
message = print # override this in subclass
|
|
213
|
+
|
|
215
214
|
@postcall
|
|
216
215
|
def post_message(self, *args, **kwargs):
|
|
217
216
|
return self.message(*args, **kwargs)
|
|
218
|
-
|
|
217
|
+
|
|
219
218
|
@staticmethod
|
|
220
219
|
def getKeyState(key):
|
|
221
|
-
"""
|
|
220
|
+
"""Return state of speckey (cf. wx.GetKeyState)."""
|
|
222
221
|
try:
|
|
223
222
|
return wx.GetKeyState(_speckeys_wxkmap[key])
|
|
224
223
|
except KeyError:
|
|
225
224
|
pass
|
|
226
|
-
|
|
225
|
+
|
|
227
226
|
@staticmethod
|
|
228
227
|
def setKeyState(key, state):
|
|
229
228
|
"""Makes you feel like having pressed/released speckey."""
|
|
@@ -232,7 +231,7 @@ class KeyCtrlInterfaceMixin:
|
|
|
232
231
|
vk.KeyDown(_speckeys_wxkmap[key])
|
|
233
232
|
else:
|
|
234
233
|
vk.KeyUp(_speckeys_wxkmap[key])
|
|
235
|
-
|
|
234
|
+
|
|
236
235
|
def make_keymap(self, keymap):
|
|
237
236
|
"""Make a basis of extension map in the handler.
|
|
238
237
|
"""
|
|
@@ -240,7 +239,7 @@ class KeyCtrlInterfaceMixin:
|
|
|
240
239
|
|
|
241
240
|
def _Pass(evt):
|
|
242
241
|
self.message("{} {}".format(keymap, evt.key))
|
|
243
|
-
_Pass.__name__ =
|
|
242
|
+
_Pass.__name__ = "pass"
|
|
244
243
|
|
|
245
244
|
state = self.handler.default_state
|
|
246
245
|
event = keymap + ' pressed'
|
|
@@ -260,36 +259,40 @@ class KeyCtrlInterfaceMixin:
|
|
|
260
259
|
'*[LR]win pressed' : [ keymap, _Pass ],
|
|
261
260
|
},
|
|
262
261
|
})
|
|
263
|
-
|
|
262
|
+
|
|
264
263
|
def pre_command_hook(self, evt):
|
|
265
|
-
"""Called when entering extension mode (internal use only)."""
|
|
264
|
+
## """Called when entering extension mode (internal use only)."""
|
|
266
265
|
## Check text selection for [C-c/C-x].
|
|
267
266
|
wnd = wx.Window.FindFocus()
|
|
268
267
|
if isinstance(wnd, wx.TextEntry) and wnd.StringSelection\
|
|
269
|
-
|
|
270
|
-
## or any other of pre-selection-p?
|
|
268
|
+
or isinstance(wnd, stc.StyledTextCtrl) and wnd.SelectedText:
|
|
271
269
|
self.handler('quit', evt)
|
|
272
270
|
else:
|
|
273
271
|
self.message(evt.key + '-')
|
|
274
272
|
evt.Skip()
|
|
275
|
-
|
|
273
|
+
pre_command_hook.__name__ = "enter"
|
|
274
|
+
|
|
276
275
|
def post_command_hook(self, evt):
|
|
277
|
-
"""Called when exiting extension mode (internal use only)."""
|
|
276
|
+
## """Called when exiting extension mode (internal use only)."""
|
|
278
277
|
keymap = self.handler.previous_state
|
|
279
278
|
if keymap:
|
|
280
279
|
self.message("{} {}".format(keymap, evt.key))
|
|
281
280
|
else:
|
|
282
281
|
self.message(evt.key)
|
|
282
|
+
## Check if the event has reached a top-level window; Don't skip text event.
|
|
283
|
+
if isinstance(self, wx.TopLevelWindow):
|
|
284
|
+
return
|
|
283
285
|
evt.Skip()
|
|
284
|
-
|
|
285
|
-
|
|
286
|
+
post_command_hook.__name__ = "exit"
|
|
287
|
+
|
|
288
|
+
def define_key(self, keymap, action=None, /, *args, **kwargs):
|
|
286
289
|
"""Define [map key (pressed)] action.
|
|
287
290
|
|
|
288
291
|
If no action, it invalidates the key and returns @decor(binder).
|
|
289
292
|
The key must be in C-M-S order (ctrl + alt(meta) + shift).
|
|
290
293
|
|
|
291
294
|
Note:
|
|
292
|
-
kwargs `doc` and `alias` are reserved as kw-only-args.
|
|
295
|
+
The funcall kwargs `doc` and `alias` are reserved as kw-only-args.
|
|
293
296
|
"""
|
|
294
297
|
assert isinstance(keymap, str)
|
|
295
298
|
assert callable(action) or action is None
|
|
@@ -304,28 +307,22 @@ class KeyCtrlInterfaceMixin:
|
|
|
304
307
|
|
|
305
308
|
if map not in self.handler:
|
|
306
309
|
warn(f"New map to define_key {keymap!r} in {self}.")
|
|
307
|
-
self.make_keymap(map)
|
|
308
|
-
|
|
309
|
-
transaction = self.handler[map].get(key, [state])
|
|
310
|
-
if len(transaction) > 1:
|
|
311
|
-
warn(f"Duplicate define_key {keymap!r} in {self}.")
|
|
310
|
+
self.make_keymap(map) # make new keymap
|
|
312
311
|
|
|
313
312
|
if action is None:
|
|
314
|
-
self.handler[map].pop(key, None)
|
|
313
|
+
self.handler[map].pop(key, None) # cf. undefine_key
|
|
315
314
|
return lambda f: self.define_key(keymap, f, *args, **kwargs)
|
|
316
315
|
|
|
317
316
|
F = _F(action, *args, **kwargs)
|
|
317
|
+
|
|
318
318
|
@wraps(F)
|
|
319
319
|
def f(*v, **kw):
|
|
320
|
-
self.message(f.__name__)
|
|
320
|
+
self.message(f.__name__) # echo message
|
|
321
321
|
return F(*v, **kw)
|
|
322
322
|
|
|
323
|
-
|
|
324
|
-
self.handler.update({map: {key: [state, self.post_command_hook, f]}})
|
|
325
|
-
else:
|
|
326
|
-
self.handler.update({map: {key: [state, f]}})
|
|
323
|
+
self.handler.update({map: {key: [state, f]}})
|
|
327
324
|
return action
|
|
328
|
-
|
|
325
|
+
|
|
329
326
|
@ignore(UserWarning)
|
|
330
327
|
def undefine_key(self, keymap):
|
|
331
328
|
"""Delete [map key (pressed)] context."""
|
|
@@ -336,7 +333,7 @@ class CtrlInterface(KeyCtrlInterfaceMixin):
|
|
|
336
333
|
"""Mouse/Key event interface mixin.
|
|
337
334
|
"""
|
|
338
335
|
handler = property(lambda self: self.__handler)
|
|
339
|
-
|
|
336
|
+
|
|
340
337
|
def __init__(self):
|
|
341
338
|
self.__key = ''
|
|
342
339
|
self.__button = ''
|
|
@@ -392,17 +389,17 @@ class CtrlInterface(KeyCtrlInterfaceMixin):
|
|
|
392
389
|
|
|
393
390
|
self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, lambda v: _N('capture_lost', v))
|
|
394
391
|
self.Bind(wx.EVT_MOUSE_CAPTURE_CHANGED, lambda v: _N('capture_changed', v))
|
|
395
|
-
|
|
396
|
-
def on_hotkey_press(self, evt):
|
|
392
|
+
|
|
393
|
+
def on_hotkey_press(self, evt): #<wx._core.KeyEvent>
|
|
397
394
|
"""Called when a key is pressed."""
|
|
398
|
-
if evt.EventObject is not self:
|
|
399
|
-
|
|
400
|
-
|
|
395
|
+
# if evt.EventObject is not self:
|
|
396
|
+
# evt.Skip()
|
|
397
|
+
# return
|
|
401
398
|
key = hotkey(evt)
|
|
402
399
|
self.__key = regulate_key(key + '-')
|
|
403
400
|
if self.handler('{} pressed'.format(key), evt) is None:
|
|
404
401
|
evt.Skip()
|
|
405
|
-
|
|
402
|
+
|
|
406
403
|
def on_hotkey_down(self, evt): #<wx._core.KeyEvent>
|
|
407
404
|
"""Called when a key is pressed while dragging.
|
|
408
405
|
Specifically called when the mouse is being captured.
|
|
@@ -411,15 +408,15 @@ class CtrlInterface(KeyCtrlInterfaceMixin):
|
|
|
411
408
|
self.on_hotkey_press(evt)
|
|
412
409
|
else:
|
|
413
410
|
evt.Skip()
|
|
414
|
-
|
|
415
|
-
def on_hotkey_up(self, evt):
|
|
411
|
+
|
|
412
|
+
def on_hotkey_up(self, evt): #<wx._core.KeyEvent>
|
|
416
413
|
"""Called when a key is released."""
|
|
417
414
|
key = hotkey(evt)
|
|
418
415
|
self.__key = ''
|
|
419
416
|
if self.handler('{} released'.format(key), evt) is None:
|
|
420
417
|
evt.Skip()
|
|
421
|
-
|
|
422
|
-
def on_mousewheel(self, evt):
|
|
418
|
+
|
|
419
|
+
def on_mousewheel(self, evt): #<wx._core.MouseEvent>
|
|
423
420
|
"""Called on mouse wheel events.
|
|
424
421
|
Trigger event: 'key+wheel[up|down|right|left] pressed'
|
|
425
422
|
"""
|
|
@@ -430,8 +427,8 @@ class CtrlInterface(KeyCtrlInterfaceMixin):
|
|
|
430
427
|
evt.key = self.__key + "wheel{}".format(p)
|
|
431
428
|
if self.handler('{} pressed'.format(evt.key), evt) is None:
|
|
432
429
|
evt.Skip()
|
|
433
|
-
|
|
434
|
-
def on_motion(self, evt):
|
|
430
|
+
|
|
431
|
+
def on_motion(self, evt): #<wx._core.MouseEvent>
|
|
435
432
|
"""Called on mouse motion events.
|
|
436
433
|
Trigger event: 'key+[LMR]drag begin/motion/end'
|
|
437
434
|
"""
|
|
@@ -445,8 +442,8 @@ class CtrlInterface(KeyCtrlInterfaceMixin):
|
|
|
445
442
|
else:
|
|
446
443
|
self.handler('motion', evt)
|
|
447
444
|
evt.Skip()
|
|
448
|
-
|
|
449
|
-
def _mouse_handler(self, event, evt):
|
|
445
|
+
|
|
446
|
+
def _mouse_handler(self, event, evt): #<wx._core.MouseEvent>
|
|
450
447
|
"""Called on mouse button events.
|
|
451
448
|
Trigger event: 'key+[LMRX]button pressed/released/dblclick'
|
|
452
449
|
"""
|
|
@@ -458,7 +455,7 @@ class CtrlInterface(KeyCtrlInterfaceMixin):
|
|
|
458
455
|
kbtn = self.__key + self.__button
|
|
459
456
|
self.handler('{}drag end'.format(kbtn), evt)
|
|
460
457
|
|
|
461
|
-
k = evt.GetButton()
|
|
458
|
+
k = evt.GetButton() # {1:L, 2:M, 3:R, 4:X1, 5:X2}
|
|
462
459
|
if action == 'pressed' and k in (1,2,3):
|
|
463
460
|
self.__button = 'LMR'[k-1]
|
|
464
461
|
else:
|
|
@@ -466,22 +463,22 @@ class CtrlInterface(KeyCtrlInterfaceMixin):
|
|
|
466
463
|
if self.handler(event, evt) is None:
|
|
467
464
|
evt.Skip()
|
|
468
465
|
try:
|
|
469
|
-
self.SetFocusIgnoringChildren()
|
|
466
|
+
self.SetFocusIgnoringChildren() # let the panel accept keys
|
|
470
467
|
except AttributeError:
|
|
471
468
|
pass
|
|
472
|
-
|
|
473
|
-
def _normal_handler(self, event, evt):
|
|
469
|
+
|
|
470
|
+
def _normal_handler(self, event, evt): #<wx._core.Event>
|
|
474
471
|
if self.handler(event, evt) is None:
|
|
475
472
|
evt.Skip()
|
|
476
473
|
|
|
477
474
|
|
|
478
475
|
## --------------------------------
|
|
479
|
-
## wx Framework and Designer
|
|
476
|
+
## wx Framework and Designer.
|
|
480
477
|
## --------------------------------
|
|
481
478
|
|
|
482
479
|
def ID_(id):
|
|
483
|
-
## Free ID -
|
|
484
|
-
##
|
|
480
|
+
## Free ID - どこで使っているか検索できるように.
|
|
481
|
+
## Do not use [ID_LOWEST(4999):ID_HIGHEST(5999)].
|
|
485
482
|
id += wx.ID_HIGHEST
|
|
486
483
|
assert not wx.ID_LOWEST <= id <= wx.ID_HIGHEST
|
|
487
484
|
return id
|
|
@@ -500,7 +497,7 @@ def pack(self, items, orient=wx.HORIZONTAL, style=None, label=None):
|
|
|
500
497
|
)
|
|
501
498
|
|
|
502
499
|
Args:
|
|
503
|
-
items
|
|
500
|
+
items: wx objects (with some packing parameters)
|
|
504
501
|
|
|
505
502
|
- (obj, 1) -> sized with ratio 1 (parallel to `orient`)
|
|
506
503
|
- (obj, 1, wx.EXPAND) -> expanded with ratio 1 (perpendicular to `orient`)
|
|
@@ -509,9 +506,9 @@ def pack(self, items, orient=wx.HORIZONTAL, style=None, label=None):
|
|
|
509
506
|
- (-1,-1) -> padding space
|
|
510
507
|
- None -> phantom
|
|
511
508
|
|
|
512
|
-
orient
|
|
513
|
-
label
|
|
514
|
-
style
|
|
509
|
+
orient: HORIZONTAL or VERTICAL
|
|
510
|
+
label: StaticBox label
|
|
511
|
+
style: Sizer option (proportion, flag, border)
|
|
515
512
|
|
|
516
513
|
- flag-expansion -> EXPAND, SHAPED
|
|
517
514
|
- flag-border -> TOP, BOTTOM, LEFT, RIGHT, ALL
|
|
@@ -519,7 +516,7 @@ def pack(self, items, orient=wx.HORIZONTAL, style=None, label=None):
|
|
|
519
516
|
ALIGN_CENTER_VERTICAL, ALIGN_CENTER_HORIZONTAL
|
|
520
517
|
"""
|
|
521
518
|
if style is None:
|
|
522
|
-
style = (0, wx.EXPAND | wx.ALL, 0)
|
|
519
|
+
style = (0, wx.EXPAND | wx.ALL, 0) # DEFALT_STYLE (prop, flag, border)
|
|
523
520
|
if label is None:
|
|
524
521
|
sizer = wx.BoxSizer(orient)
|
|
525
522
|
else:
|
|
@@ -529,14 +526,14 @@ def pack(self, items, orient=wx.HORIZONTAL, style=None, label=None):
|
|
|
529
526
|
if not isinstance(item, (wx.Object, list, tuple, type(None))):
|
|
530
527
|
warn(f"pack items must be a wx.Object, tuple or None, not {type(item)}")
|
|
531
528
|
if item is None:
|
|
532
|
-
item = (0, 0), 0, 0, 0
|
|
529
|
+
item = (0, 0), 0, 0, 0 # null space
|
|
533
530
|
elif not item:
|
|
534
|
-
item = (0, 0)
|
|
531
|
+
item = (0, 0) # padding space
|
|
535
532
|
try:
|
|
536
533
|
try:
|
|
537
534
|
sizer.Add(item, *style)
|
|
538
535
|
except TypeError:
|
|
539
|
-
sizer.Add(*item)
|
|
536
|
+
sizer.Add(*item) # using item-specific style
|
|
540
537
|
except TypeError as e:
|
|
541
538
|
traceback.print_exc()
|
|
542
539
|
bmp = wx.StaticBitmap(self, bitmap=wx.ArtProvider.GetBitmap(wx.ART_ERROR))
|
|
@@ -550,8 +547,8 @@ class Menu(wx.Menu):
|
|
|
550
547
|
"""Construct the menu.
|
|
551
548
|
|
|
552
549
|
Args:
|
|
553
|
-
|
|
554
|
-
|
|
550
|
+
owner: window object to bind handlers
|
|
551
|
+
menulist: list of MenuItem args
|
|
555
552
|
|
|
556
553
|
(id, text, hint, style, icon, ... Menu.Append arguments
|
|
557
554
|
action, updater, highlight) ... Menu Event handlers
|
|
@@ -572,10 +569,10 @@ class Menu(wx.Menu):
|
|
|
572
569
|
continue
|
|
573
570
|
id = item[0]
|
|
574
571
|
handlers = [x for x in item if callable(x)]
|
|
575
|
-
icons =
|
|
572
|
+
icons = [x for x in item if isinstance(x, wx.Bitmap)]
|
|
576
573
|
argv = [x for x in item if x not in handlers and x not in icons]
|
|
577
574
|
if isinstance(id, int):
|
|
578
|
-
menu_item = wx.MenuItem(self, *argv)
|
|
575
|
+
menu_item = wx.MenuItem(self, *argv) # <- menu_item.Id
|
|
579
576
|
if icons:
|
|
580
577
|
menu_item.SetBitmaps(*icons)
|
|
581
578
|
self.Append(menu_item)
|
|
@@ -586,16 +583,16 @@ class Menu(wx.Menu):
|
|
|
586
583
|
except IndexError:
|
|
587
584
|
pass
|
|
588
585
|
else:
|
|
589
|
-
subitems = list(argv.pop())
|
|
586
|
+
subitems = list(argv.pop()) # extract the last element as submenu
|
|
590
587
|
submenu = Menu(owner, subitems)
|
|
591
588
|
submenu_item = wx.MenuItem(self, wx.ID_ANY, *argv)
|
|
592
589
|
submenu_item.SetSubMenu(submenu)
|
|
593
590
|
if icons:
|
|
594
591
|
submenu_item.SetBitmaps(*icons)
|
|
595
592
|
self.Append(submenu_item)
|
|
596
|
-
self.Enable(submenu_item.Id, len(subitems))
|
|
597
|
-
submenu.Id = submenu_item.Id
|
|
598
|
-
|
|
593
|
+
self.Enable(submenu_item.Id, len(subitems)) # Disable an empty menu.
|
|
594
|
+
submenu.Id = submenu_item.Id # <- ID_ANY (dummy to check empty sbumenu)
|
|
595
|
+
|
|
599
596
|
def _unbind(self):
|
|
600
597
|
for item in self.MenuItems:
|
|
601
598
|
if item.Id != wx.ID_SEPARATOR:
|
|
@@ -604,17 +601,16 @@ class Menu(wx.Menu):
|
|
|
604
601
|
self.owner.Unbind(wx.EVT_MENU_HIGHLIGHT, item)
|
|
605
602
|
if item.SubMenu:
|
|
606
603
|
item.SubMenu._unbind()
|
|
607
|
-
|
|
604
|
+
|
|
608
605
|
def Destroy(self):
|
|
609
|
-
|
|
606
|
+
if self.owner and not self.owner.IsBeingDeleted():
|
|
610
607
|
self._unbind()
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
608
|
+
return wx.Menu.Destroy(self)
|
|
609
|
+
|
|
614
610
|
@staticmethod
|
|
615
|
-
def Popup(
|
|
616
|
-
menu = Menu(
|
|
617
|
-
|
|
611
|
+
def Popup(owner, menulist, *args, **kwargs):
|
|
612
|
+
menu = Menu(owner, menulist)
|
|
613
|
+
owner.PopupMenu(menu, *args, **kwargs)
|
|
618
614
|
menu.Destroy()
|
|
619
615
|
|
|
620
616
|
|
|
@@ -633,7 +629,7 @@ class MenuBar(wx.MenuBar, TreeList):
|
|
|
633
629
|
def __init__(self, *args, **kwargs):
|
|
634
630
|
wx.MenuBar.__init__(self, *args, **kwargs)
|
|
635
631
|
TreeList.__init__(self)
|
|
636
|
-
|
|
632
|
+
|
|
637
633
|
def getmenu(self, key, root=None):
|
|
638
634
|
if '/' in key:
|
|
639
635
|
a, b = key.split('/', 1)
|
|
@@ -642,7 +638,7 @@ class MenuBar(wx.MenuBar, TreeList):
|
|
|
642
638
|
if root is None:
|
|
643
639
|
return next((menu for menu, label in self.Menus if menu.Title == key), None)
|
|
644
640
|
return next((item.SubMenu for item in root.MenuItems if item.ItemLabel == key), None)
|
|
645
|
-
|
|
641
|
+
|
|
646
642
|
def update(self, key):
|
|
647
643
|
"""Update items of the menu that has specified key:root/branch.
|
|
648
644
|
Call when the menulist is changed.
|
|
@@ -657,19 +653,19 @@ class MenuBar(wx.MenuBar, TreeList):
|
|
|
657
653
|
return
|
|
658
654
|
|
|
659
655
|
menu._unbind()
|
|
660
|
-
for item in menu.MenuItems:
|
|
656
|
+
for item in menu.MenuItems: # delete all items
|
|
661
657
|
menu.Delete(item)
|
|
662
658
|
|
|
663
|
-
menu2 = Menu(self.Parent, self[key])
|
|
659
|
+
menu2 = Menu(self.Parent, self[key]) # new menu2 to swap menu
|
|
664
660
|
for item in menu2.MenuItems:
|
|
665
|
-
menu.Append(menu2.Remove(item))
|
|
661
|
+
menu.Append(menu2.Remove(item)) # 重複しないようにいったん切り離して追加する
|
|
666
662
|
|
|
667
663
|
if hasattr(menu, 'Id'):
|
|
668
|
-
self.Enable(menu.Id, menu.MenuItemCount > 0)
|
|
664
|
+
self.Enable(menu.Id, menu.MenuItemCount > 0) # Disable empty submenu.
|
|
669
665
|
|
|
670
666
|
for j, (key, values) in enumerate(self):
|
|
671
|
-
self.EnableTop(j, bool(values))
|
|
672
|
-
|
|
667
|
+
self.EnableTop(j, bool(values)) # Disable empty main menu.
|
|
668
|
+
|
|
673
669
|
def reset(self):
|
|
674
670
|
"""Recreates the menubar if the Parent was attached.
|
|
675
671
|
Call when the menulist is changed.
|
|
@@ -678,41 +674,41 @@ class MenuBar(wx.MenuBar, TreeList):
|
|
|
678
674
|
warn(f"No parents bound to {self}.")
|
|
679
675
|
return
|
|
680
676
|
|
|
681
|
-
for j in range(self.GetMenuCount()):
|
|
677
|
+
for j in range(self.GetMenuCount()): # remove and del all top-level menu
|
|
682
678
|
menu = self.Remove(0)
|
|
683
679
|
menu.Destroy()
|
|
684
680
|
|
|
685
681
|
for j, (key, values) in enumerate(self):
|
|
686
682
|
menu = Menu(self.Parent, values)
|
|
687
683
|
self.Append(menu, key)
|
|
688
|
-
self.EnableTop(j, bool(values))
|
|
684
|
+
self.EnableTop(j, bool(values)) # Disable empty main menu.
|
|
689
685
|
|
|
690
686
|
|
|
691
687
|
class StatusBar(wx.StatusBar):
|
|
692
688
|
"""Construct the statusbar with read/write interfaces.
|
|
693
689
|
|
|
694
690
|
Attributes:
|
|
695
|
-
field
|
|
696
|
-
pane
|
|
691
|
+
field: list of field widths
|
|
692
|
+
pane: index of status text field
|
|
697
693
|
"""
|
|
698
694
|
def __init__(self, *args, **kwargs):
|
|
699
695
|
wx.StatusBar.__init__(self, *args, **kwargs)
|
|
700
|
-
|
|
696
|
+
|
|
701
697
|
def __call__(self, *args, **kwargs):
|
|
702
698
|
text = ' '.join(str(v) for v in args)
|
|
703
699
|
if self:
|
|
704
700
|
return self.write(text, **kwargs)
|
|
705
|
-
|
|
701
|
+
|
|
706
702
|
def resize(self, field):
|
|
707
703
|
self.SetFieldsCount(len(field))
|
|
708
|
-
self.SetStatusWidths(list(field))
|
|
709
|
-
|
|
704
|
+
self.SetStatusWidths(list(field)) # oldver requires list type
|
|
705
|
+
|
|
710
706
|
def write(self, text, pane=0):
|
|
711
707
|
if text and text[0] == '\b':
|
|
712
708
|
text = self.read(pane) + text[1:]
|
|
713
709
|
self.SetStatusText(text, pane % self.GetFieldsCount())
|
|
714
710
|
return text
|
|
715
|
-
|
|
711
|
+
|
|
716
712
|
def read(self, pane=0):
|
|
717
713
|
return self.GetStatusText(pane % self.GetFieldsCount())
|
|
718
714
|
|
|
@@ -721,19 +717,14 @@ class Frame(wx.Frame, KeyCtrlInterfaceMixin):
|
|
|
721
717
|
"""Frame extension class.
|
|
722
718
|
|
|
723
719
|
Attributes:
|
|
724
|
-
menubar
|
|
725
|
-
statusbar
|
|
726
|
-
shellframe
|
|
720
|
+
menubar: MenuBar
|
|
721
|
+
statusbar: StatusBar
|
|
722
|
+
shellframe: mini-frame of the shell
|
|
727
723
|
"""
|
|
728
724
|
handler = property(lambda self: self.__handler)
|
|
729
|
-
|
|
725
|
+
|
|
730
726
|
message = property(lambda self: self.statusbar)
|
|
731
|
-
|
|
732
|
-
def post_command_hook(self, evt):
|
|
733
|
-
## (override) Don't skip events as a TopLevelWindow.
|
|
734
|
-
pass
|
|
735
|
-
post_command_hook.__name__ = str('noskip')
|
|
736
|
-
|
|
727
|
+
|
|
737
728
|
def __init__(self, *args, **kwargs):
|
|
738
729
|
wx.Frame.__init__(self, *args, **kwargs)
|
|
739
730
|
|
|
@@ -769,7 +760,7 @@ class Frame(wx.Frame, KeyCtrlInterfaceMixin):
|
|
|
769
760
|
## AcceleratorTable mimic
|
|
770
761
|
def hook_char(evt):
|
|
771
762
|
"""Called when key down."""
|
|
772
|
-
if isinstance(evt.EventObject, wx.TextEntry):
|
|
763
|
+
if isinstance(evt.EventObject, wx.TextEntry): # prior to handler
|
|
773
764
|
evt.Skip()
|
|
774
765
|
else:
|
|
775
766
|
if self.handler('{} pressed'.format(hotkey(evt)), evt) is None:
|
|
@@ -784,39 +775,32 @@ class Frame(wx.Frame, KeyCtrlInterfaceMixin):
|
|
|
784
775
|
},
|
|
785
776
|
)
|
|
786
777
|
self.make_keymap('C-x')
|
|
787
|
-
|
|
778
|
+
|
|
788
779
|
def About(self):
|
|
789
780
|
wx.MessageBox(__import__("__main__").__doc__ or "no information",
|
|
790
781
|
"About this software")
|
|
791
|
-
|
|
782
|
+
|
|
792
783
|
def Destroy(self):
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
finally:
|
|
797
|
-
return wx.Frame.Destroy(self)
|
|
784
|
+
self.timer.Stop()
|
|
785
|
+
self.shellframe.Destroy() # shellframe is not my child
|
|
786
|
+
return wx.Frame.Destroy(self)
|
|
798
787
|
|
|
799
788
|
|
|
800
789
|
class MiniFrame(wx.MiniFrame, KeyCtrlInterfaceMixin):
|
|
801
790
|
"""MiniFrame extension class.
|
|
802
791
|
|
|
803
792
|
Attributes:
|
|
804
|
-
menubar
|
|
805
|
-
statusbar
|
|
793
|
+
menubar: MenuBar
|
|
794
|
+
statusbar: StatusBar (not shown by default)
|
|
806
795
|
"""
|
|
807
796
|
handler = property(lambda self: self.__handler)
|
|
808
|
-
|
|
797
|
+
|
|
809
798
|
message = property(lambda self: self.statusbar)
|
|
810
|
-
|
|
811
|
-
def post_command_hook(self, evt):
|
|
812
|
-
## (override) Don't skip events as a TopLevelWindow.
|
|
813
|
-
pass
|
|
814
|
-
post_command_hook.__name__ = str('noskip')
|
|
815
|
-
|
|
799
|
+
|
|
816
800
|
def __init__(self, *args, **kwargs):
|
|
817
801
|
wx.MiniFrame.__init__(self, *args, **kwargs)
|
|
818
802
|
|
|
819
|
-
## To disable, self.SetMenuBar(None)
|
|
803
|
+
## To disable, call self.SetMenuBar(None).
|
|
820
804
|
self.menubar = MenuBar()
|
|
821
805
|
self.SetMenuBar(self.menubar)
|
|
822
806
|
|
|
@@ -824,17 +808,17 @@ class MiniFrame(wx.MiniFrame, KeyCtrlInterfaceMixin):
|
|
|
824
808
|
self.statusbar.Show(0)
|
|
825
809
|
self.SetStatusBar(self.statusbar)
|
|
826
810
|
|
|
827
|
-
## AcceleratorTable mimic
|
|
811
|
+
## AcceleratorTable mimic.
|
|
828
812
|
def hook_char(evt):
|
|
829
813
|
"""Called when key down."""
|
|
830
|
-
if isinstance(evt.EventObject, wx.TextEntry):
|
|
814
|
+
if isinstance(evt.EventObject, wx.TextEntry): # prior to handler
|
|
831
815
|
evt.Skip()
|
|
832
816
|
else:
|
|
833
817
|
if self.handler('{} pressed'.format(hotkey(evt)), evt) is None:
|
|
834
818
|
evt.Skip()
|
|
835
819
|
self.Bind(wx.EVT_CHAR_HOOK, hook_char)
|
|
836
820
|
|
|
837
|
-
## To default close >>> self.Unbind(wx.EVT_CLOSE)
|
|
821
|
+
## To default close >>> self.Unbind(wx.EVT_CLOSE).
|
|
838
822
|
self.Bind(wx.EVT_CLOSE, lambda v: self.Show(0))
|
|
839
823
|
|
|
840
824
|
self.__handler = FSM({ # DNA<MiniFrame>
|
|
@@ -845,9 +829,6 @@ class MiniFrame(wx.MiniFrame, KeyCtrlInterfaceMixin):
|
|
|
845
829
|
},
|
|
846
830
|
)
|
|
847
831
|
self.make_keymap('C-x')
|
|
848
|
-
|
|
849
|
-
def Destroy(self):
|
|
850
|
-
return wx.MiniFrame.Destroy(self)
|
|
851
832
|
|
|
852
833
|
|
|
853
834
|
class AuiNotebook(aui.AuiNotebook):
|
|
@@ -866,52 +847,48 @@ class AuiNotebook(aui.AuiNotebook):
|
|
|
866
847
|
self.Name = name
|
|
867
848
|
|
|
868
849
|
def tab_menu(evt):
|
|
869
|
-
tabs = evt.EventObject
|
|
870
|
-
page = tabs.Pages[evt.Selection]
|
|
850
|
+
tabs = evt.EventObject #<AuiTabCtrl>
|
|
851
|
+
page = tabs.Pages[evt.Selection] # GetPage for split notebook.
|
|
871
852
|
try:
|
|
872
853
|
Menu.Popup(self, page.window.menu)
|
|
873
854
|
except AttributeError:
|
|
874
855
|
pass
|
|
875
856
|
self.Bind(aui.EVT_AUINOTEBOOK_TAB_RIGHT_DOWN, tab_menu)
|
|
876
|
-
|
|
877
|
-
@property
|
|
878
|
-
def all_pages(self):
|
|
879
|
-
"""Returns all window pages."""
|
|
880
|
-
return [self.GetPage(i) for i in range(self.PageCount)]
|
|
881
|
-
|
|
857
|
+
|
|
882
858
|
@property
|
|
883
|
-
def
|
|
884
|
-
"""
|
|
859
|
+
def _all_tabs(self):
|
|
860
|
+
"""Return all AuiTabCtrl objects (internal use only)."""
|
|
885
861
|
return [x for x in self.Children if isinstance(x, aui.AuiTabCtrl)]
|
|
886
|
-
|
|
862
|
+
|
|
887
863
|
@property
|
|
888
|
-
def
|
|
889
|
-
"""
|
|
864
|
+
def _all_panes(self):
|
|
865
|
+
"""Return all AuiPaneInfo excluding `dummy` one (internal use only)."""
|
|
890
866
|
return list(self._mgr.AllPanes)[1:]
|
|
891
|
-
|
|
867
|
+
|
|
892
868
|
def get_pages(self, type=None):
|
|
893
869
|
"""Yields pages of the specified window type."""
|
|
894
|
-
for
|
|
870
|
+
for i in range(self.PageCount):
|
|
871
|
+
win = self.GetPage(i)
|
|
895
872
|
if type is None or isinstance(win, type):
|
|
896
873
|
yield win
|
|
897
|
-
|
|
874
|
+
|
|
898
875
|
def swap_page(self, win):
|
|
899
876
|
"""Replace the page with the specified page w/o focusing."""
|
|
900
877
|
j = self.GetPageIndex(win)
|
|
901
878
|
if j != -1:
|
|
902
|
-
wnd = wx.Window.FindFocus()
|
|
879
|
+
wnd = wx.Window.FindFocus() # original focus
|
|
903
880
|
org = self.CurrentPage
|
|
904
881
|
if j != self.Selection:
|
|
905
|
-
self.Selection = j
|
|
906
|
-
self.CurrentPage.SetFocus()
|
|
907
|
-
if wnd and wnd is not org:
|
|
882
|
+
self.Selection = j # the focus moves if shown
|
|
883
|
+
self.CurrentPage.SetFocus() # reset focus
|
|
884
|
+
if wnd and wnd is not org: # restore focus other window
|
|
908
885
|
wnd.SetFocus()
|
|
909
|
-
|
|
886
|
+
|
|
910
887
|
def get_caption(self, win):
|
|
911
888
|
"""Get caption of tab/page for specifiend window."""
|
|
912
889
|
tab, page = self.find_tab(win)
|
|
913
890
|
return page.caption
|
|
914
|
-
|
|
891
|
+
|
|
915
892
|
def set_caption(self, win, caption):
|
|
916
893
|
"""Set caption of tab/page for specifiend window.
|
|
917
894
|
Returns True if the caption has changed.
|
|
@@ -921,41 +898,43 @@ class AuiNotebook(aui.AuiNotebook):
|
|
|
921
898
|
page.caption = caption
|
|
922
899
|
tab.Refresh()
|
|
923
900
|
return True
|
|
924
|
-
|
|
901
|
+
|
|
925
902
|
def find_tab(self, win):
|
|
926
|
-
"""
|
|
903
|
+
"""Return AuiTabCtrl and AuiNotebookPage for the window.
|
|
927
904
|
|
|
928
905
|
cf. aui.AuiNotebook.FindTab -> bool, tab, idx
|
|
929
906
|
Note:
|
|
930
907
|
Argument `win` can also be page.window.Name (not page.caption).
|
|
931
908
|
"""
|
|
932
|
-
for
|
|
933
|
-
for page in
|
|
909
|
+
for tab in self._all_tabs: #<aui.AuiTabCtrl>
|
|
910
|
+
for page in tab.Pages: #<aui.AuiNotebookPage>
|
|
934
911
|
## if page.window is win or page.caption == win:
|
|
935
912
|
if page.window is win or page.window.Name == win:
|
|
936
|
-
return
|
|
937
|
-
|
|
938
|
-
def move_tab(self, win,
|
|
939
|
-
"""Move the window page to the specified
|
|
913
|
+
return tab, page
|
|
914
|
+
|
|
915
|
+
def move_tab(self, win, tab):
|
|
916
|
+
"""Move the window page to the specified tab."""
|
|
917
|
+
if isinstance(tab, int):
|
|
918
|
+
tab = self._all_tabs[tab]
|
|
940
919
|
try:
|
|
941
920
|
tc1, nb1 = self.find_tab(win)
|
|
942
921
|
win = nb1.window
|
|
943
|
-
except Exception:
|
|
922
|
+
except Exception: # object not found
|
|
944
923
|
return
|
|
945
|
-
page = wx.aui.AuiNotebookPage(nb1)
|
|
924
|
+
page = wx.aui.AuiNotebookPage(nb1) # copy-ctor
|
|
946
925
|
tc1.RemovePage(win) # Accessing nb1 will crash at this point.
|
|
947
|
-
|
|
926
|
+
tab.AddPage(win, page) # Add a page with the copied info.
|
|
948
927
|
if tc1.PageCount == 0:
|
|
949
928
|
## Delete an empty tab and the corresponding pane.
|
|
950
|
-
j = self.
|
|
951
|
-
pane = self.
|
|
929
|
+
j = self._all_tabs.index(tc1)
|
|
930
|
+
pane = self._all_panes[j]
|
|
952
931
|
tc1.Destroy()
|
|
953
932
|
self._mgr.DetachPane(pane.window)
|
|
954
933
|
self._mgr.Update()
|
|
955
|
-
|
|
934
|
+
|
|
956
935
|
## Methods to save / load the perspectives.
|
|
957
936
|
## *** Inspired by wx.lib.agw.aui.AuiNotebook ***
|
|
958
|
-
|
|
937
|
+
|
|
959
938
|
def savePerspective(self):
|
|
960
939
|
"""Saves the entire user interface layout into an encoded string,
|
|
961
940
|
which can then be stored by the application.
|
|
@@ -964,17 +943,16 @@ class AuiNotebook(aui.AuiNotebook):
|
|
|
964
943
|
Perspectives are saved according to page.window.Name.
|
|
965
944
|
User should give it (not page.caption) a unique name.
|
|
966
945
|
"""
|
|
967
|
-
for j, pane in enumerate(self.
|
|
946
|
+
for j, pane in enumerate(self._all_panes):
|
|
968
947
|
pane.name = f"pane{j+1}"
|
|
969
948
|
spec = ""
|
|
970
|
-
for j, tabs in enumerate(self.
|
|
971
|
-
k = next(k for k, page in enumerate(tabs.Pages)
|
|
972
|
-
if page.window.Shown) # get active window
|
|
949
|
+
for j, tabs in enumerate(self._all_tabs):
|
|
950
|
+
k = next(k for k, page in enumerate(tabs.Pages) if page.window.Shown)
|
|
973
951
|
## names = [page.caption for page in tabs.Pages]
|
|
974
952
|
names = [page.window.Name for page in tabs.Pages]
|
|
975
953
|
spec += f"pane{j+1}={names};{k}|"
|
|
976
954
|
return spec + '@' + self._mgr.SavePerspective()
|
|
977
|
-
|
|
955
|
+
|
|
978
956
|
def loadPerspective(self, spec):
|
|
979
957
|
"""Loads a saved perspective.
|
|
980
958
|
|
|
@@ -986,29 +964,26 @@ class AuiNotebook(aui.AuiNotebook):
|
|
|
986
964
|
tabinfo = re.findall(r"pane\w+?=(.*?);(.*?)\|", tabs)
|
|
987
965
|
try:
|
|
988
966
|
self.Parent.Freeze()
|
|
989
|
-
## Collapse all
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
self.move_tab(win, maintab)
|
|
967
|
+
## Collapse all tabctrls to main tabctrl.
|
|
968
|
+
for win in self.get_pages():
|
|
969
|
+
self.move_tab(win, 0)
|
|
993
970
|
|
|
994
|
-
## Create a new
|
|
971
|
+
## Create a new tabctrl using Split method.
|
|
995
972
|
## Note: The normal way of creating panes with `_mgr` crashes.
|
|
996
|
-
|
|
997
|
-
all_names = [win.Name for win in self.all_pages]
|
|
973
|
+
all_names = [win.Name for win in self.get_pages()]
|
|
998
974
|
for names, k in tabinfo[1:]:
|
|
999
975
|
names, k = eval(names), int(k)
|
|
1000
|
-
i = all_names.index(names[0])
|
|
976
|
+
i = all_names.index(names[0]) # Assuming 0:tab is included.
|
|
1001
977
|
self.Split(i, wx.LEFT)
|
|
1002
|
-
newtab = self.all_tabs[-1]
|
|
1003
978
|
for name in names[1:]:
|
|
1004
|
-
self.move_tab(name,
|
|
1005
|
-
self.Selection = all_names.index(names[k])
|
|
979
|
+
self.move_tab(name, -1)
|
|
980
|
+
self.Selection = all_names.index(names[k]) # new tabctrl active window
|
|
1006
981
|
else:
|
|
1007
982
|
names, k = tabinfo[0]
|
|
1008
983
|
names, k = eval(names), int(k)
|
|
1009
|
-
self.Selection = all_names.index(names[k])
|
|
984
|
+
self.Selection = all_names.index(names[k]) # main tabctrl active window
|
|
1010
985
|
|
|
1011
|
-
for j, pane in enumerate(self.
|
|
986
|
+
for j, pane in enumerate(self._all_panes):
|
|
1012
987
|
pane.name = f"pane{j+1}"
|
|
1013
988
|
self._mgr.LoadPerspective(frames)
|
|
1014
989
|
self._mgr.Update()
|
|
@@ -1027,31 +1002,33 @@ class FileDropLoader(wx.DropTarget):
|
|
|
1027
1002
|
self.target = target
|
|
1028
1003
|
self.textdo = wx.TextDataObject()
|
|
1029
1004
|
self.filedo = wx.FileDataObject()
|
|
1030
|
-
self.
|
|
1031
|
-
self.
|
|
1032
|
-
self.
|
|
1033
|
-
|
|
1005
|
+
self.do = wx.DataObjectComposite()
|
|
1006
|
+
self.do.Add(self.textdo)
|
|
1007
|
+
self.do.Add(self.filedo)
|
|
1008
|
+
self.SetDataObject(self.do)
|
|
1009
|
+
|
|
1010
|
+
def OnDragOver(self, x, y, result):
|
|
1011
|
+
index, flags = self.target.HitTest((x, y))
|
|
1012
|
+
if index != -1:
|
|
1013
|
+
self.target.Selection = index
|
|
1014
|
+
result = wx.DragCopy
|
|
1015
|
+
else:
|
|
1016
|
+
result = wx.DragNone
|
|
1017
|
+
return result
|
|
1018
|
+
|
|
1034
1019
|
def OnData(self, x, y, result):
|
|
1035
|
-
editor = self.target.current_editor
|
|
1020
|
+
editor = self.target.Parent.current_editor
|
|
1036
1021
|
self.GetData()
|
|
1037
|
-
if self.textdo.
|
|
1038
|
-
|
|
1039
|
-
res = editor.
|
|
1040
|
-
if res:
|
|
1041
|
-
editor.
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
editor.post_message("Load canceled.")
|
|
1045
|
-
result = wx.DragCancel
|
|
1046
|
-
else:
|
|
1047
|
-
editor.post_message(f"Loading {f!r} failed.")
|
|
1048
|
-
result = wx.DragNone
|
|
1049
|
-
self.textdo.Text = ''
|
|
1022
|
+
if self.textdo.Text:
|
|
1023
|
+
fn = self.textdo.Text.strip()
|
|
1024
|
+
res = editor.parent.handler("text_dropped", fn)
|
|
1025
|
+
if res is None or not any(res):
|
|
1026
|
+
editor.load_file(fn)
|
|
1027
|
+
result = wx.DragCopy
|
|
1028
|
+
self.textdo.SetText("")
|
|
1050
1029
|
else:
|
|
1051
|
-
for
|
|
1052
|
-
|
|
1053
|
-
editor.buffer.SetFocus()
|
|
1054
|
-
editor.post_message(f"Loaded {f!r} successfully.")
|
|
1030
|
+
for fn in self.filedo.Filenames:
|
|
1031
|
+
editor.load_file(fn)
|
|
1055
1032
|
self.filedo.SetData(wx.DF_FILENAME, None)
|
|
1056
1033
|
return result
|
|
1057
1034
|
|
|
@@ -1060,26 +1037,28 @@ class ShellFrame(MiniFrame):
|
|
|
1060
1037
|
"""MiniFrame of the Shell.
|
|
1061
1038
|
|
|
1062
1039
|
Args:
|
|
1063
|
-
target
|
|
1064
|
-
|
|
1065
|
-
session
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1040
|
+
target: target object of the rootshell.
|
|
1041
|
+
If None, it will be __main__.
|
|
1042
|
+
session: file name of the session. Defaults to None.
|
|
1043
|
+
If None, no session will be created or saved.
|
|
1044
|
+
If `''`, the default session (.debrc) will be loaded.
|
|
1045
|
+
standalone: flag for the shell standalone.
|
|
1046
|
+
If True, EVT_CLOSE will close the window.
|
|
1047
|
+
Otherwise the window will be only hidden.
|
|
1048
|
+
**kwargs: Nautilus arguments
|
|
1071
1049
|
|
|
1072
1050
|
Attributes:
|
|
1073
|
-
console
|
|
1074
|
-
ghost
|
|
1075
|
-
watcher
|
|
1076
|
-
Scratch
|
|
1077
|
-
Help
|
|
1078
|
-
Log
|
|
1079
|
-
monitor
|
|
1080
|
-
inspector
|
|
1081
|
-
debugger
|
|
1082
|
-
ginfo
|
|
1051
|
+
console: Notebook of shells
|
|
1052
|
+
ghost: Notebook of editors/buffers
|
|
1053
|
+
watcher: Notebook of global/locals watcher
|
|
1054
|
+
Scratch: Book of scratch (tooltip)
|
|
1055
|
+
Help: Book of help
|
|
1056
|
+
Log: Book of logging
|
|
1057
|
+
monitor: wxmon.EventMonitor object
|
|
1058
|
+
inspector: wxwit.Inspector object
|
|
1059
|
+
debugger: wxpdb.Debugger object
|
|
1060
|
+
ginfo: globals list
|
|
1061
|
+
linfo: locals list
|
|
1083
1062
|
|
|
1084
1063
|
Built-in utility::
|
|
1085
1064
|
|
|
@@ -1098,19 +1077,19 @@ class ShellFrame(MiniFrame):
|
|
|
1098
1077
|
@highlight : Highlight the widget.
|
|
1099
1078
|
@filling : Inspection using ``wx.lib.filling.Filling``.
|
|
1100
1079
|
"""
|
|
1101
|
-
rootshell = property(lambda self: self.__shell)
|
|
1102
|
-
|
|
1103
|
-
def __init__(self, parent, target=None, session=None,
|
|
1080
|
+
rootshell = property(lambda self: self.__shell) # the root shell
|
|
1081
|
+
|
|
1082
|
+
def __init__(self, parent, target=None, session=None, standalone=False, **kwargs):
|
|
1104
1083
|
MiniFrame.__init__(self, parent, size=(1280,720), style=wx.DEFAULT_FRAME_STYLE)
|
|
1105
1084
|
|
|
1106
1085
|
self.statusbar.resize((-1,120))
|
|
1107
1086
|
self.statusbar.Show(1)
|
|
1108
1087
|
|
|
1109
|
-
self.__standalone = bool(
|
|
1088
|
+
self.__standalone = bool(standalone)
|
|
1110
1089
|
|
|
1111
1090
|
self.Init()
|
|
1112
1091
|
|
|
1113
|
-
from .nutshell import Nautilus, EditorBook
|
|
1092
|
+
from .nutshell import Nautilus, EditorBook, Stylus
|
|
1114
1093
|
from .bookshelf import EditorTreeCtrl
|
|
1115
1094
|
|
|
1116
1095
|
self.__shell = Nautilus(self,
|
|
@@ -1125,6 +1104,28 @@ class ShellFrame(MiniFrame):
|
|
|
1125
1104
|
self.Bookshelf = EditorTreeCtrl(self, name="Bookshelf",
|
|
1126
1105
|
style=wx.TR_DEFAULT_STYLE|wx.TR_HIDE_ROOT)
|
|
1127
1106
|
|
|
1107
|
+
## Set shell and editor styles.
|
|
1108
|
+
self.Scratch.set_attributes(Style=Stylus.py_shell_mode)
|
|
1109
|
+
|
|
1110
|
+
self.Log.set_attributes(ReadOnly=True,
|
|
1111
|
+
Style=Stylus.py_log_mode)
|
|
1112
|
+
|
|
1113
|
+
self.Help.set_attributes(ReadOnly=False,
|
|
1114
|
+
Style=Stylus.py_log_mode)
|
|
1115
|
+
|
|
1116
|
+
self.set_hookable(self.Scratch)
|
|
1117
|
+
self.set_hookable(self.Log)
|
|
1118
|
+
|
|
1119
|
+
@self.Scratch.define_key('C-j')
|
|
1120
|
+
def eval_line(evt):
|
|
1121
|
+
self.Scratch.buffer.eval_line()
|
|
1122
|
+
evt.Skip(False) # Don't skip explicitly.
|
|
1123
|
+
|
|
1124
|
+
@self.Scratch.define_key('M-j')
|
|
1125
|
+
def eval_buffer(evt):
|
|
1126
|
+
self.Scratch.buffer.exec_region()
|
|
1127
|
+
evt.Skip(False) # Don't skip explicitly.
|
|
1128
|
+
|
|
1128
1129
|
from .wxpdb import Debugger
|
|
1129
1130
|
from .wxwit import Inspector
|
|
1130
1131
|
from .wxmon import EventMonitor
|
|
@@ -1134,8 +1135,8 @@ class ShellFrame(MiniFrame):
|
|
|
1134
1135
|
self.debugger = Debugger(self,
|
|
1135
1136
|
stdin=self.__shell.interp.stdin,
|
|
1136
1137
|
stdout=self.__shell.interp.stdout,
|
|
1137
|
-
skip=[Debugger.__module__,
|
|
1138
|
-
EventMonitor.__module__,
|
|
1138
|
+
skip=[Debugger.__module__, # Don't enter debugger
|
|
1139
|
+
EventMonitor.__module__, # Don't enter event-hook
|
|
1139
1140
|
FSM.__module__,
|
|
1140
1141
|
'wx.core', 'wx.lib.eventwatcher',
|
|
1141
1142
|
'fnmatch', 'warnings', 'bdb', 'pdb', 'contextlib',
|
|
@@ -1149,9 +1150,7 @@ class ShellFrame(MiniFrame):
|
|
|
1149
1150
|
self.console = AuiNotebook(self, size=(600,400), name='console')
|
|
1150
1151
|
self.console.AddPage(self.__shell, "root", bitmap=Icon('core'))
|
|
1151
1152
|
self.console.TabCtrlHeight = 0
|
|
1152
|
-
## self.console.Name = "console"
|
|
1153
1153
|
|
|
1154
|
-
## self.console.Bind(aui.EVT_AUINOTEBOOK_BUTTON, self.OnConsoleCloseBtn)
|
|
1155
1154
|
self.console.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnConsolePageClose)
|
|
1156
1155
|
self.console.Bind(aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.OnConsolePageChanged)
|
|
1157
1156
|
|
|
@@ -1162,7 +1161,7 @@ class ShellFrame(MiniFrame):
|
|
|
1162
1161
|
|
|
1163
1162
|
self.ghost.AddPage(self.Bookshelf, "Bookshelf", bitmap=Icon('book'))
|
|
1164
1163
|
|
|
1165
|
-
self.ghost.SetDropTarget(FileDropLoader(self))
|
|
1164
|
+
self.ghost.SetDropTarget(FileDropLoader(self.ghost))
|
|
1166
1165
|
|
|
1167
1166
|
self.watcher = AuiNotebook(self, size=(600,400), name="watcher")
|
|
1168
1167
|
self.watcher.AddPage(self.ginfo, "globals")
|
|
@@ -1183,7 +1182,7 @@ class ShellFrame(MiniFrame):
|
|
|
1183
1182
|
self._mgr.AddPane(self.ghost,
|
|
1184
1183
|
aui.AuiPaneInfo().Name("ghost")
|
|
1185
1184
|
.Caption("Ghost in the Shell").Right()
|
|
1186
|
-
.MaximizeButton().Show(
|
|
1185
|
+
.MaximizeButton().Show(1))
|
|
1187
1186
|
|
|
1188
1187
|
self._mgr.AddPane(self.watcher,
|
|
1189
1188
|
aui.AuiPaneInfo().Name("watcher")
|
|
@@ -1205,6 +1204,8 @@ class ShellFrame(MiniFrame):
|
|
|
1205
1204
|
|
|
1206
1205
|
self.Bind(wx.EVT_FIND, self.OnFindNext)
|
|
1207
1206
|
self.Bind(wx.EVT_FIND_NEXT, self.OnFindNext)
|
|
1207
|
+
self.Bind(wx.EVT_FIND_REPLACE, self.OnFindNext)
|
|
1208
|
+
self.Bind(wx.EVT_FIND_REPLACE_ALL, self.OnFindNext)
|
|
1208
1209
|
self.Bind(wx.EVT_FIND_CLOSE, self.OnFindClose)
|
|
1209
1210
|
|
|
1210
1211
|
self.indicator = Indicator(self.statusbar, value=1)
|
|
@@ -1246,29 +1247,29 @@ class ShellFrame(MiniFrame):
|
|
|
1246
1247
|
'trace_end' : [ None, self.on_trace_end ],
|
|
1247
1248
|
'monitor_begin' : [ None, self.on_monitor_begin ],
|
|
1248
1249
|
'monitor_end' : [ None, self.on_monitor_end ],
|
|
1249
|
-
'buffer_new' : [ None, ],
|
|
1250
1250
|
'shell_new' : [ None, ],
|
|
1251
|
+
'book_new' : [ None, ],
|
|
1251
1252
|
'add_log' : [ None, self.add_log ],
|
|
1252
1253
|
'add_help' : [ None, self.add_help ],
|
|
1253
1254
|
'title_window' : [ None, self.on_title_window ],
|
|
1254
|
-
'buffer_caption_updated' : [ None, self.on_buffer_caption ], # => self.OnActivate
|
|
1255
1255
|
},
|
|
1256
1256
|
0 : {
|
|
1257
1257
|
'* pressed' : (0, fork_debugger),
|
|
1258
1258
|
'* released' : (0, fork_debugger),
|
|
1259
1259
|
'C-g pressed' : (0, self.Quit, fork_debugger),
|
|
1260
1260
|
'f1 pressed' : (0, self.About),
|
|
1261
|
-
'C-f pressed' : (0, self.
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1261
|
+
'C-f pressed' : (0, self.on_search_dialog),
|
|
1262
|
+
'C-S-f pressed' : (0, self.on_replace_dialog),
|
|
1263
|
+
'f3 pressed' : (0, self.repeat_forward_search),
|
|
1264
|
+
'S-f3 pressed' : (0, self.repeat_backward_search),
|
|
1265
|
+
'f11 pressed' : (0, _F(self.toggle_window, win=self.ghost, alias='toggle_ghost')),
|
|
1266
|
+
'S-f11 pressed' : (0, _F(self.toggle_window, win=self.watcher, alias='toggle_watcher')),
|
|
1266
1267
|
'f12 pressed' : (0, _F(self.Close, alias="close")),
|
|
1267
1268
|
'*f[0-9]* pressed' : (0, ),
|
|
1268
1269
|
},
|
|
1269
1270
|
})
|
|
1270
1271
|
|
|
1271
|
-
## Session files
|
|
1272
|
+
## Session files.
|
|
1272
1273
|
self.SESSION_FILE = get_rootpath(".debrc")
|
|
1273
1274
|
self.SCRATCH_FILE = get_rootpath("scratch.py")
|
|
1274
1275
|
self.LOGGING_FILE = get_rootpath("deb-logging.log")
|
|
@@ -1277,9 +1278,7 @@ class ShellFrame(MiniFrame):
|
|
|
1277
1278
|
self.load_session(session or self.SESSION_FILE)
|
|
1278
1279
|
else:
|
|
1279
1280
|
self.SESSION_FILE = None
|
|
1280
|
-
|
|
1281
|
-
self.postInit()
|
|
1282
|
-
|
|
1281
|
+
|
|
1283
1282
|
def load_session(self, filename):
|
|
1284
1283
|
"""Load session from file.
|
|
1285
1284
|
Buffers, pointers, and the entire layout are loaded.
|
|
@@ -1292,10 +1291,10 @@ class ShellFrame(MiniFrame):
|
|
|
1292
1291
|
except Exception:
|
|
1293
1292
|
pass
|
|
1294
1293
|
|
|
1295
|
-
_fload(self.Scratch, self.SCRATCH_FILE)
|
|
1294
|
+
_fload(self.Scratch, self.SCRATCH_FILE) # restore scratch
|
|
1296
1295
|
|
|
1297
1296
|
## Re-open the *log* file.
|
|
1298
|
-
self.add_log("#! Opened: <{}>\r\n".format(datetime.
|
|
1297
|
+
self.add_log("#! Opened: <{}>\r\n".format(datetime.now()))
|
|
1299
1298
|
|
|
1300
1299
|
session = os.path.abspath(filename)
|
|
1301
1300
|
try:
|
|
@@ -1303,12 +1302,14 @@ class ShellFrame(MiniFrame):
|
|
|
1303
1302
|
exec(i.read())
|
|
1304
1303
|
except FileNotFoundError:
|
|
1305
1304
|
pass
|
|
1305
|
+
except Exception as e:
|
|
1306
|
+
print("- Failed to load session:", e)
|
|
1306
1307
|
self.SESSION_FILE = session
|
|
1307
1308
|
|
|
1308
1309
|
## Reposition the window if it is not on the desktop.
|
|
1309
1310
|
if wx.Display.GetFromWindow(self) == -1:
|
|
1310
1311
|
self.Position = (0, 0)
|
|
1311
|
-
|
|
1312
|
+
|
|
1312
1313
|
def save_session(self):
|
|
1313
1314
|
"""Save session to file.
|
|
1314
1315
|
Buffers, pointers, and the entire layout are saved.
|
|
@@ -1321,8 +1322,8 @@ class ShellFrame(MiniFrame):
|
|
|
1321
1322
|
except Exception:
|
|
1322
1323
|
pass
|
|
1323
1324
|
|
|
1324
|
-
_fsave(self.Scratch, self.SCRATCH_FILE)
|
|
1325
|
-
_fsave(self.Log, self.LOGGING_FILE)
|
|
1325
|
+
_fsave(self.Scratch, self.SCRATCH_FILE) # save scratch
|
|
1326
|
+
_fsave(self.Log, self.LOGGING_FILE) # save log
|
|
1326
1327
|
|
|
1327
1328
|
if not self.SESSION_FILE:
|
|
1328
1329
|
return
|
|
@@ -1332,8 +1333,10 @@ class ShellFrame(MiniFrame):
|
|
|
1332
1333
|
o.write("self.SetSize({})\n".format(self.Size))
|
|
1333
1334
|
o.write("self.SetPosition({})\n".format(self.Position))
|
|
1334
1335
|
|
|
1335
|
-
for book in self.
|
|
1336
|
-
|
|
1336
|
+
for book in self.get_all_editors():
|
|
1337
|
+
if book.Name not in ("Scratch", "Log", "Help"): # Save default editors only.
|
|
1338
|
+
continue
|
|
1339
|
+
for buf in book.get_all_buffers():
|
|
1337
1340
|
if buf.mtdelta is not None:
|
|
1338
1341
|
o.write("self.{}.load_file({!r}, {})\n"
|
|
1339
1342
|
.format(book.Name, buf.filename, buf.markline+1))
|
|
@@ -1345,35 +1348,7 @@ class ShellFrame(MiniFrame):
|
|
|
1345
1348
|
"self._mgr.LoadPerspective({!r})".format(self._mgr.SavePerspective()),
|
|
1346
1349
|
"self._mgr.Update()\n",
|
|
1347
1350
|
)))
|
|
1348
|
-
|
|
1349
|
-
def postInit(self):
|
|
1350
|
-
"""Set shell and editor styles.
|
|
1351
|
-
Note:
|
|
1352
|
-
This is called after loading session.
|
|
1353
|
-
"""
|
|
1354
|
-
from .nutshell import Stylus
|
|
1355
|
-
|
|
1356
|
-
self.Scratch.set_attributes(Style=Stylus.py_shell_mode)
|
|
1357
|
-
|
|
1358
|
-
self.Log.set_attributes(ReadOnly=True,
|
|
1359
|
-
Style=Stylus.py_log_mode)
|
|
1360
|
-
|
|
1361
|
-
self.Help.set_attributes(ReadOnly=False,
|
|
1362
|
-
Style=Stylus.py_log_mode)
|
|
1363
|
-
|
|
1364
|
-
self.set_hookable(self.Scratch)
|
|
1365
|
-
self.set_hookable(self.Log)
|
|
1366
|
-
|
|
1367
|
-
@self.Scratch.define_key('C-j')
|
|
1368
|
-
def eval_line(evt):
|
|
1369
|
-
self.Scratch.buffer.eval_line()
|
|
1370
|
-
evt.Skip(False) # Don't skip explicitly.
|
|
1371
|
-
|
|
1372
|
-
@self.Scratch.define_key('M-j')
|
|
1373
|
-
def eval_buffer(evt):
|
|
1374
|
-
self.Scratch.buffer.exec_region()
|
|
1375
|
-
evt.Skip(False) # Don't skip explicitly.
|
|
1376
|
-
|
|
1351
|
+
|
|
1377
1352
|
def Init(self):
|
|
1378
1353
|
"""Initialize self-specific builtins.
|
|
1379
1354
|
Note:
|
|
@@ -1382,7 +1357,7 @@ class ShellFrame(MiniFrame):
|
|
|
1382
1357
|
try:
|
|
1383
1358
|
builtins.dive
|
|
1384
1359
|
except AttributeError:
|
|
1385
|
-
## Add useful built-in functions and methods
|
|
1360
|
+
## Add useful built-in functions and methods.
|
|
1386
1361
|
builtins.apropos = apropos
|
|
1387
1362
|
builtins.typename = typename
|
|
1388
1363
|
builtins.reload = reload
|
|
@@ -1401,10 +1376,10 @@ class ShellFrame(MiniFrame):
|
|
|
1401
1376
|
builtins.profile = self.profile
|
|
1402
1377
|
builtins.highlight = self.highlight
|
|
1403
1378
|
builtins.filling = filling
|
|
1404
|
-
|
|
1379
|
+
|
|
1405
1380
|
def Destroy(self):
|
|
1406
1381
|
try:
|
|
1407
|
-
## Remove built-in self methods
|
|
1382
|
+
## Remove built-in self methods.
|
|
1408
1383
|
del builtins.info
|
|
1409
1384
|
del builtins.help
|
|
1410
1385
|
del builtins.load
|
|
@@ -1416,16 +1391,21 @@ class ShellFrame(MiniFrame):
|
|
|
1416
1391
|
del builtins.highlight
|
|
1417
1392
|
except AttributeError:
|
|
1418
1393
|
pass
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1394
|
+
|
|
1395
|
+
self.timer.Stop()
|
|
1396
|
+
self.save_session()
|
|
1397
|
+
self._mgr.UnInit()
|
|
1398
|
+
return MiniFrame.Destroy(self)
|
|
1399
|
+
|
|
1400
|
+
def Close(self):
|
|
1401
|
+
if self.__standalone:
|
|
1402
|
+
MiniFrame.Close(self)
|
|
1403
|
+
else:
|
|
1404
|
+
self.Show(not self.Shown)
|
|
1405
|
+
|
|
1426
1406
|
def OnClose(self, evt):
|
|
1427
1407
|
if self.debugger.busy:
|
|
1428
|
-
if wx.MessageBox( # Confirm debugger
|
|
1408
|
+
if wx.MessageBox( # Confirm closing the debugger.
|
|
1429
1409
|
"The debugger is running.\n\n"
|
|
1430
1410
|
"Enter [q]uit to exit before closing.\n"
|
|
1431
1411
|
"Continue closing?",
|
|
@@ -1433,20 +1413,20 @@ class ShellFrame(MiniFrame):
|
|
|
1433
1413
|
self.message("The close has been canceled.")
|
|
1434
1414
|
evt.Veto()
|
|
1435
1415
|
return
|
|
1436
|
-
|
|
1416
|
+
## RuntimeError('wrapped C/C++ object ... has been deleted')
|
|
1437
1417
|
self.Quit()
|
|
1438
1418
|
|
|
1439
1419
|
if self.debugger.tracing:
|
|
1440
1420
|
wx.MessageBox("The debugger ends tracing.\n\n"
|
|
1441
1421
|
"The trace pointer will be cleared.")
|
|
1442
|
-
self.debugger.unwatch()
|
|
1422
|
+
self.debugger.unwatch() # cf. [pointer_unset] stop_trace
|
|
1443
1423
|
|
|
1444
|
-
for book in self.
|
|
1445
|
-
for buf in book.
|
|
1424
|
+
for book in self.get_all_editors():
|
|
1425
|
+
for buf in book.get_all_buffers():
|
|
1446
1426
|
if buf.need_buffer_save:
|
|
1447
1427
|
self.popup_window(book)
|
|
1448
1428
|
buf.SetFocus()
|
|
1449
|
-
if wx.MessageBox( # Confirm
|
|
1429
|
+
if wx.MessageBox( # Confirm closing the buffer.
|
|
1450
1430
|
"You are closing unsaved content.\n\n"
|
|
1451
1431
|
"Changes to the content will be discarded.\n"
|
|
1452
1432
|
"Continue closing?",
|
|
@@ -1455,58 +1435,60 @@ class ShellFrame(MiniFrame):
|
|
|
1455
1435
|
self.message("The close has been canceled.")
|
|
1456
1436
|
evt.Veto()
|
|
1457
1437
|
return
|
|
1458
|
-
break
|
|
1438
|
+
break # Don't ask any more.
|
|
1459
1439
|
if self.__standalone:
|
|
1460
|
-
evt.Skip()
|
|
1440
|
+
evt.Skip() # Close the window
|
|
1461
1441
|
else:
|
|
1462
|
-
self.Show(0)
|
|
1463
|
-
|
|
1442
|
+
self.Show(0) # Don't destroy the window
|
|
1443
|
+
|
|
1464
1444
|
def OnActivate(self, evt):
|
|
1465
1445
|
if not evt.Active:
|
|
1466
1446
|
## Reset autoload when active focus going outside.
|
|
1467
1447
|
self.__autoload = True
|
|
1468
|
-
elif evt.GetActivationReason() == evt.Reason_Mouse
|
|
1469
|
-
|
|
1448
|
+
## elif evt.GetActivationReason() == evt.Reason_Mouse and self.__autoload:
|
|
1449
|
+
elif self.__autoload:
|
|
1470
1450
|
## Check all buffers that need to be loaded.
|
|
1471
|
-
|
|
1472
|
-
|
|
1451
|
+
verbose = 1
|
|
1452
|
+
for book in self.get_all_editors():
|
|
1453
|
+
for buf in book.get_all_buffers():
|
|
1473
1454
|
if buf.need_buffer_load:
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1455
|
+
buf.update_caption()
|
|
1456
|
+
if verbose:
|
|
1457
|
+
with wx.MessageDialog(self, # Confirm load.
|
|
1458
|
+
"The file has been modified externally.\n\n"
|
|
1459
|
+
"The contents of the buffer will be overwritten.\n"
|
|
1460
|
+
"Continue loading {}/{}?".format(book.Name, buf.name),
|
|
1461
|
+
"Load {!r}".format(buf.name),
|
|
1462
|
+
style=wx.YES_NO|wx.CANCEL|wx.HELP|wx.ICON_INFORMATION) as dlg:
|
|
1463
|
+
dlg.SetHelpLabel("Yes to All")
|
|
1464
|
+
ret = dlg.ShowModal()
|
|
1465
|
+
if ret == wx.ID_NO:
|
|
1466
|
+
continue
|
|
1467
|
+
if ret == wx.ID_CANCEL:
|
|
1468
|
+
break # all
|
|
1469
|
+
if ret == wx.ID_HELP: # ID_YESTOALL
|
|
1470
|
+
verbose = 0
|
|
1471
|
+
book.load_file(buf.filename, buf.markline+1)
|
|
1472
|
+
self.__autoload = False
|
|
1483
1473
|
## Reinitialize self-specific builtins if other instances are destroyed.
|
|
1484
1474
|
if evt.Active:
|
|
1485
1475
|
self.Init()
|
|
1486
1476
|
evt.Skip()
|
|
1487
|
-
|
|
1477
|
+
|
|
1488
1478
|
def OnShow(self, evt):
|
|
1489
|
-
pane
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
self.inspector.watch()
|
|
1493
|
-
self.monitor.watch()
|
|
1494
|
-
else:
|
|
1495
|
-
if pane.IsDocked():
|
|
1496
|
-
self.inspector.unwatch()
|
|
1497
|
-
self.monitor.unwatch()
|
|
1479
|
+
for pane in self._mgr.GetAllPanes():
|
|
1480
|
+
## When the window is hidden, disable docking and keep child panes floating.
|
|
1481
|
+
pane.Dockable(evt.IsShown() or pane.IsDocked())
|
|
1498
1482
|
evt.Skip()
|
|
1499
|
-
|
|
1483
|
+
|
|
1500
1484
|
def OnGhostShow(self, evt):
|
|
1501
1485
|
if evt.IsShown():
|
|
1502
1486
|
self.inspector.watch()
|
|
1503
|
-
self.monitor.watch()
|
|
1504
1487
|
else:
|
|
1505
1488
|
self.inspector.unwatch()
|
|
1506
|
-
self.monitor.unwatch()
|
|
1507
1489
|
evt.Skip()
|
|
1508
|
-
|
|
1509
|
-
def OnConsolePageChanged(self, evt):
|
|
1490
|
+
|
|
1491
|
+
def OnConsolePageChanged(self, evt): #<wx._aui.AuiNotebookEvent>
|
|
1510
1492
|
nb = evt.EventObject
|
|
1511
1493
|
win = nb.CurrentPage
|
|
1512
1494
|
if win is self.rootshell:
|
|
@@ -1515,24 +1497,12 @@ class ShellFrame(MiniFrame):
|
|
|
1515
1497
|
nb.WindowStyle |= aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
|
|
1516
1498
|
nb.TabCtrlHeight = 0 if nb.PageCount == 1 else -1
|
|
1517
1499
|
evt.Skip()
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
## tabs = evt.EventObject
|
|
1521
|
-
## win = tabs.Pages[evt.Selection].window # GetPage for split notebook.
|
|
1522
|
-
## if win is self.rootshell:
|
|
1523
|
-
## ## self.message("- Don't close the root shell.")
|
|
1524
|
-
## return
|
|
1525
|
-
## elif self.debugger.busy and win is self.debugger.interactive_shell:
|
|
1526
|
-
## wx.MessageBox("The debugger is running.\n\n"
|
|
1527
|
-
## "Enter [q]uit to exit before closing.")
|
|
1528
|
-
## return
|
|
1529
|
-
## evt.Skip()
|
|
1530
|
-
|
|
1531
|
-
def OnConsolePageClose(self, evt): #<wx._aui.AuiNotebookEvent>
|
|
1500
|
+
|
|
1501
|
+
def OnConsolePageClose(self, evt): #<wx._aui.AuiNotebookEvent>
|
|
1532
1502
|
nb = evt.EventObject
|
|
1533
|
-
win = nb.
|
|
1503
|
+
win = list(nb.get_pages())[evt.Selection]
|
|
1534
1504
|
if win is self.rootshell:
|
|
1535
|
-
|
|
1505
|
+
# self.message("Don't close the root shell.")
|
|
1536
1506
|
nb.WindowStyle &= ~aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
|
|
1537
1507
|
evt.Veto()
|
|
1538
1508
|
elif self.debugger.busy and win is self.debugger.interactive_shell:
|
|
@@ -1541,39 +1511,37 @@ class ShellFrame(MiniFrame):
|
|
|
1541
1511
|
evt.Veto()
|
|
1542
1512
|
else:
|
|
1543
1513
|
evt.Skip()
|
|
1544
|
-
|
|
1514
|
+
|
|
1545
1515
|
def About(self, evt=None):
|
|
1546
1516
|
self.add_help(
|
|
1547
1517
|
'\n\n'.join((
|
|
1548
|
-
"#<module 'mwx' from {!r}>"
|
|
1549
|
-
"Author: {!r}"
|
|
1550
|
-
"Version: {!s}"
|
|
1518
|
+
f"#<module 'mwx' from {__file__!r}>",
|
|
1519
|
+
f"Author: {__author__!r}",
|
|
1520
|
+
f"Version: {__version__!s}",
|
|
1551
1521
|
self.__class__.__doc__,
|
|
1552
1522
|
self.rootshell.__class__.__doc__,
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
"
|
|
1556
|
-
"
|
|
1557
|
-
"Version: {!s}".format(wx.py.version.VERSION),
|
|
1523
|
+
## <--- Thanks to <wx.py.shell> --->
|
|
1524
|
+
f"#{wx.py!r}",
|
|
1525
|
+
f"Author: {wx.py.version.__author__!r}",
|
|
1526
|
+
f"Version: {wx.py.version.VERSION!s}",
|
|
1558
1527
|
wx.py.shell.Shell.__doc__,
|
|
1559
1528
|
textwrap.indent("*original" + wx.py.shell.HELP_TEXT, ' '*4),
|
|
1560
|
-
|
|
1561
|
-
##
|
|
1562
|
-
"#
|
|
1563
|
-
"To show the credit, press C-M-Mbutton.\n",
|
|
1529
|
+
## <--- Thanks are also due to <wx> --->
|
|
1530
|
+
## f"#{wx!r}".format(wx),
|
|
1531
|
+
## f"To show the credit, press [C-M-Mbutton].", # cf. wx.InfoMessageBox(None)
|
|
1564
1532
|
))
|
|
1565
1533
|
)
|
|
1566
|
-
|
|
1534
|
+
|
|
1567
1535
|
def toggle_window(self, win):
|
|
1568
1536
|
pane = self._mgr.GetPane(win)
|
|
1569
1537
|
if pane.IsDocked():
|
|
1570
1538
|
if not self.console.IsShown():
|
|
1571
|
-
self._mgr.RestoreMaximizedPane()
|
|
1572
|
-
self._mgr.Update()
|
|
1539
|
+
self._mgr.RestoreMaximizedPane() # いったん表示切替
|
|
1540
|
+
self._mgr.Update() # 更新後に best_size 取得
|
|
1573
1541
|
if pane.IsShown():
|
|
1574
1542
|
pane.best_size = win.Size
|
|
1575
1543
|
self.popup_window(win, not pane.IsShown())
|
|
1576
|
-
|
|
1544
|
+
|
|
1577
1545
|
@save_focus_excursion()
|
|
1578
1546
|
def popup_window(self, win, show=True):
|
|
1579
1547
|
"""Show the notebook page and keep the focus."""
|
|
@@ -1581,47 +1549,48 @@ class ShellFrame(MiniFrame):
|
|
|
1581
1549
|
nb = pane.window
|
|
1582
1550
|
if nb is win:
|
|
1583
1551
|
break
|
|
1584
|
-
j = nb.GetPageIndex(win)
|
|
1552
|
+
j = nb.GetPageIndex(win) # find and select page
|
|
1585
1553
|
if j != -1:
|
|
1586
1554
|
if j != nb.Selection:
|
|
1587
|
-
nb.Selection = j
|
|
1555
|
+
nb.Selection = j # the focus moves
|
|
1588
1556
|
break
|
|
1589
1557
|
else:
|
|
1590
|
-
return
|
|
1558
|
+
return # no such pane.window
|
|
1591
1559
|
|
|
1592
1560
|
## Modify the floating position of the pane when displayed.
|
|
1593
1561
|
## Note: This is a known bug in wxWidgets 3.17 -- 3.20,
|
|
1594
|
-
## and will be fixed in
|
|
1562
|
+
## and will be fixed in wx ver 4.2.1.
|
|
1595
1563
|
if wx.Display.GetFromWindow(pane.window) == -1:
|
|
1596
1564
|
pane.floating_pos = wx.GetMousePosition()
|
|
1597
1565
|
|
|
1598
1566
|
nb.Show(show)
|
|
1599
1567
|
pane.Show(show)
|
|
1600
1568
|
self._mgr.Update()
|
|
1601
|
-
|
|
1569
|
+
|
|
1602
1570
|
## --------------------------------
|
|
1603
|
-
## Actions for handler
|
|
1571
|
+
## Actions for handler.
|
|
1604
1572
|
## --------------------------------
|
|
1605
|
-
|
|
1573
|
+
|
|
1606
1574
|
def Quit(self, evt=None):
|
|
1607
1575
|
"""Stop debugger and monitor."""
|
|
1576
|
+
self.monitor.unwatch()
|
|
1608
1577
|
self.debugger.unwatch()
|
|
1609
|
-
self.debugger.send_input('\n')
|
|
1610
|
-
shell = self.debugger.interactive_shell
|
|
1578
|
+
self.debugger.send_input('\n') # terminates the reader of threading pdb
|
|
1579
|
+
shell = self.debugger.interactive_shell # reset interp locals
|
|
1611
1580
|
del shell.locals
|
|
1612
1581
|
del shell.globals
|
|
1613
1582
|
self.indicator.Value = 1
|
|
1614
1583
|
self.message("Quit")
|
|
1615
|
-
|
|
1584
|
+
|
|
1616
1585
|
@save_focus_excursion()
|
|
1617
1586
|
def load(self, filename, lineno=0, show=True):
|
|
1618
1587
|
"""Load file @where the object is defined.
|
|
1619
1588
|
|
|
1620
1589
|
Args:
|
|
1621
|
-
filename
|
|
1622
|
-
|
|
1623
|
-
lineno
|
|
1624
|
-
show
|
|
1590
|
+
filename: target filename:str or object.
|
|
1591
|
+
It also supports <'filename:lineno'> format.
|
|
1592
|
+
lineno: Set mark to lineno on load.
|
|
1593
|
+
show: Show the page.
|
|
1625
1594
|
"""
|
|
1626
1595
|
if not isinstance(filename, str):
|
|
1627
1596
|
filename = where(filename)
|
|
@@ -1632,18 +1601,18 @@ class ShellFrame(MiniFrame):
|
|
|
1632
1601
|
if m:
|
|
1633
1602
|
filename, ln = m.groups()
|
|
1634
1603
|
lineno = int(ln)
|
|
1635
|
-
editor = self.
|
|
1604
|
+
editor = next(self.get_all_editors(filename), self.Log)
|
|
1636
1605
|
ret = editor.load_file(filename, lineno)
|
|
1637
|
-
if ret:
|
|
1606
|
+
if ret and show:
|
|
1638
1607
|
self.popup_window(editor, show)
|
|
1639
1608
|
return ret
|
|
1640
|
-
|
|
1609
|
+
|
|
1641
1610
|
def info(self, obj):
|
|
1642
1611
|
self.rootshell.info(obj)
|
|
1643
|
-
|
|
1612
|
+
|
|
1644
1613
|
def help(self, obj):
|
|
1645
1614
|
self.rootshell.help(obj)
|
|
1646
|
-
|
|
1615
|
+
|
|
1647
1616
|
def watch(self, obj):
|
|
1648
1617
|
if isinstance(obj, wx.Object):
|
|
1649
1618
|
self.monitor.watch(obj)
|
|
@@ -1652,10 +1621,12 @@ class ShellFrame(MiniFrame):
|
|
|
1652
1621
|
self.linfo.watch(obj.__dict__)
|
|
1653
1622
|
self.ginfo.watch(None)
|
|
1654
1623
|
self.popup_window(self.linfo)
|
|
1655
|
-
|
|
1624
|
+
else:
|
|
1625
|
+
raise TypeError("primitive objects cannot be set as watch targets")
|
|
1626
|
+
|
|
1656
1627
|
def highlight(self, obj, *args, **kwargs):
|
|
1657
1628
|
self.inspector.highlight(obj, *args, **kwargs)
|
|
1658
|
-
|
|
1629
|
+
|
|
1659
1630
|
def timeit(self, obj, *args, **kwargs):
|
|
1660
1631
|
"""Measure the duration cpu time (per one execution)."""
|
|
1661
1632
|
from timeit import timeit
|
|
@@ -1673,8 +1644,8 @@ class ShellFrame(MiniFrame):
|
|
|
1673
1644
|
except Exception as e:
|
|
1674
1645
|
print(e)
|
|
1675
1646
|
else:
|
|
1676
|
-
print("- obj
|
|
1677
|
-
|
|
1647
|
+
print("- obj must be either a string or a callable.")
|
|
1648
|
+
|
|
1678
1649
|
def profile(self, obj, *args, **kwargs):
|
|
1679
1650
|
"""Profile a single function call."""
|
|
1680
1651
|
from profile import Profile
|
|
@@ -1694,9 +1665,9 @@ class ShellFrame(MiniFrame):
|
|
|
1694
1665
|
except TypeError as e:
|
|
1695
1666
|
print(e)
|
|
1696
1667
|
else:
|
|
1697
|
-
print("- obj must be callable or
|
|
1698
|
-
|
|
1699
|
-
## Note: history
|
|
1668
|
+
print("- obj must be callable, or a string, bytes, or code object.")
|
|
1669
|
+
|
|
1670
|
+
## Note: history に余計な文字列が入らないようにする.
|
|
1700
1671
|
@postcall
|
|
1701
1672
|
def debug(self, obj, *args, **kwargs):
|
|
1702
1673
|
shell = self.debugger.interactive_shell
|
|
@@ -1723,7 +1694,7 @@ class ShellFrame(MiniFrame):
|
|
|
1723
1694
|
style=wx.ICON_ERROR)
|
|
1724
1695
|
finally:
|
|
1725
1696
|
self.debugger.interactive_shell = shell
|
|
1726
|
-
|
|
1697
|
+
|
|
1727
1698
|
def on_debug_begin(self, frame):
|
|
1728
1699
|
"""Called before set_trace."""
|
|
1729
1700
|
if not self:
|
|
@@ -1739,7 +1710,7 @@ class ShellFrame(MiniFrame):
|
|
|
1739
1710
|
self.indicator.Value = 2
|
|
1740
1711
|
if wx.IsBusy():
|
|
1741
1712
|
wx.EndBusyCursor()
|
|
1742
|
-
|
|
1713
|
+
|
|
1743
1714
|
def on_debug_next(self, frame):
|
|
1744
1715
|
"""Called from cmdloop."""
|
|
1745
1716
|
if not self:
|
|
@@ -1760,7 +1731,7 @@ class ShellFrame(MiniFrame):
|
|
|
1760
1731
|
command = re.sub(r"^(.*)", r" \1", command, flags=re.M)
|
|
1761
1732
|
self.add_log(command)
|
|
1762
1733
|
self.message("Debugger is busy now (Press [C-g] to quit).")
|
|
1763
|
-
|
|
1734
|
+
|
|
1764
1735
|
def on_debug_end(self, frame):
|
|
1765
1736
|
"""Called after set_quit."""
|
|
1766
1737
|
if not self:
|
|
@@ -1777,7 +1748,7 @@ class ShellFrame(MiniFrame):
|
|
|
1777
1748
|
self.indicator.Value = 1
|
|
1778
1749
|
if wx.IsBusy():
|
|
1779
1750
|
wx.EndBusyCursor()
|
|
1780
|
-
|
|
1751
|
+
|
|
1781
1752
|
def set_hookable(self, editor, traceable=True):
|
|
1782
1753
|
"""Bind pointer to set/unset trace."""
|
|
1783
1754
|
if traceable:
|
|
@@ -1786,100 +1757,103 @@ class ShellFrame(MiniFrame):
|
|
|
1786
1757
|
else:
|
|
1787
1758
|
editor.handler.unbind('pointer_set')
|
|
1788
1759
|
editor.handler.unbind('pointer_unset')
|
|
1789
|
-
|
|
1760
|
+
|
|
1790
1761
|
def start_trace(self, line, editor):
|
|
1791
1762
|
if not self.debugger.busy:
|
|
1792
1763
|
self.debugger.unwatch()
|
|
1793
1764
|
self.debugger.editor = editor
|
|
1794
1765
|
self.debugger.watch((editor.buffer.filename, line+1))
|
|
1795
|
-
self.debugger.send_input('')
|
|
1796
|
-
|
|
1766
|
+
self.debugger.send_input('') # clear input
|
|
1767
|
+
|
|
1797
1768
|
def stop_trace(self, line, editor):
|
|
1798
1769
|
if self.debugger.busy:
|
|
1799
1770
|
return
|
|
1800
1771
|
if self.debugger.tracing:
|
|
1801
1772
|
self.debugger.editor = None
|
|
1802
1773
|
self.debugger.unwatch()
|
|
1803
|
-
|
|
1774
|
+
|
|
1804
1775
|
def on_trace_begin(self, frame):
|
|
1805
1776
|
"""Called when set-trace."""
|
|
1806
1777
|
self.message("Debugger has started tracing {!r}.".format(frame))
|
|
1807
1778
|
self.indicator.Value = 3
|
|
1808
|
-
|
|
1779
|
+
|
|
1809
1780
|
def on_trace_hook(self, frame):
|
|
1810
1781
|
"""Called when a breakpoint is reached."""
|
|
1811
1782
|
self.message("Debugger hooked {!r}.".format(frame))
|
|
1812
|
-
|
|
1783
|
+
|
|
1813
1784
|
def on_trace_end(self, frame):
|
|
1814
1785
|
"""Called when unset-trace."""
|
|
1815
1786
|
self.message("Debugger has stopped tracing {!r}.".format(frame))
|
|
1816
1787
|
self.indicator.Value = 1
|
|
1817
|
-
|
|
1788
|
+
|
|
1818
1789
|
def on_monitor_begin(self, widget):
|
|
1819
1790
|
"""Called when monitor watch."""
|
|
1820
1791
|
self.inspector.set_colour(widget, 'blue')
|
|
1821
1792
|
self.message("Started monitoring {!r}.".format(widget))
|
|
1822
|
-
|
|
1793
|
+
|
|
1823
1794
|
def on_monitor_end(self, widget):
|
|
1824
1795
|
"""Called when monitor unwatch."""
|
|
1825
1796
|
self.inspector.set_colour(widget, 'black')
|
|
1826
1797
|
self.message("Stopped monitoring {!r}.".format(widget))
|
|
1827
|
-
|
|
1798
|
+
|
|
1828
1799
|
def on_title_window(self, obj):
|
|
1829
1800
|
"""Set title to the frame."""
|
|
1830
1801
|
title = obj if isinstance(obj, str) else repr(obj)
|
|
1831
1802
|
self.SetTitle("Nautilus - {}".format(title))
|
|
1832
|
-
|
|
1833
|
-
def on_buffer_caption(self, buf):
|
|
1834
|
-
"""Called when the buffer caption is updated."""
|
|
1835
|
-
if buf.caption_prefix.startswith('!'):
|
|
1836
|
-
v = wx.ActivateEvent(wx.wxEVT_ACTIVATE, True,
|
|
1837
|
-
buf.Id, ActivationReason=0)
|
|
1838
|
-
self.EventHandler.ProcessEvent(v) # => self.OnActivate
|
|
1839
|
-
|
|
1803
|
+
|
|
1840
1804
|
def add_log(self, text, noerr=None):
|
|
1841
1805
|
"""Add text to the logging buffer.
|
|
1842
|
-
If noerr
|
|
1806
|
+
If noerr:bool is specified, add a line-marker.
|
|
1843
1807
|
"""
|
|
1844
1808
|
buf = self.Log.default_buffer or self.Log.new_buffer()
|
|
1845
1809
|
with buf.off_readonly():
|
|
1846
|
-
buf.goto_char(buf.TextLength)
|
|
1810
|
+
buf.goto_char(buf.TextLength) # line to set an arrow marker
|
|
1847
1811
|
buf.write(text)
|
|
1848
1812
|
if noerr is not None:
|
|
1849
1813
|
## Set a marker on the current line.
|
|
1850
|
-
buf.add_marker(buf.cline, 1 if noerr else 2)
|
|
1814
|
+
buf.add_marker(buf.cline, 1 if noerr else 2) # 1:white 2:red-arrow
|
|
1851
1815
|
return
|
|
1852
1816
|
|
|
1853
1817
|
## Logging text every step in case of crash.
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
def add_help(self, text):
|
|
1858
|
-
"""Add text to the help buffer.
|
|
1818
|
+
# with open(self.LOGGING_FILE, 'a', encoding='utf-8', newline='') as o:
|
|
1819
|
+
# o.write(text)
|
|
1820
|
+
|
|
1821
|
+
def add_help(self, text, title=None):
|
|
1822
|
+
"""Add text to the help buffer.
|
|
1823
|
+
If title:str is specified, create a new buffer with that title.
|
|
1824
|
+
"""
|
|
1859
1825
|
buf = self.Help.default_buffer or self.Help.new_buffer()
|
|
1826
|
+
if title is not None:
|
|
1827
|
+
self.Help.find_file(f"*{title}*")
|
|
1828
|
+
buf = self.Help.buffer
|
|
1860
1829
|
with buf.off_readonly():
|
|
1861
|
-
buf.
|
|
1830
|
+
buf.Text = text
|
|
1831
|
+
buf.EmptyUndoBuffer()
|
|
1832
|
+
buf.SetSavePoint()
|
|
1862
1833
|
## Overwrite text and popup the window.
|
|
1863
1834
|
self.popup_window(self.Help)
|
|
1864
1835
|
self.Help.swap_page(buf)
|
|
1865
|
-
|
|
1836
|
+
|
|
1866
1837
|
def clone_shell(self, target):
|
|
1867
1838
|
if not hasattr(target, '__dict__'):
|
|
1868
1839
|
raise TypeError("primitive objects cannot be targeted")
|
|
1869
1840
|
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1841
|
+
try:
|
|
1842
|
+
shell = next(self.get_all_shells(target))
|
|
1843
|
+
except StopIteration:
|
|
1844
|
+
shell = self.rootshell.__class__(self, target, name="clone",
|
|
1845
|
+
style=wx.CLIP_CHILDREN|wx.BORDER_NONE)
|
|
1846
|
+
self.console.AddPage(shell, typename(shell.target))
|
|
1847
|
+
self.handler('shell_new', shell)
|
|
1875
1848
|
self.popup_window(shell)
|
|
1849
|
+
self.Show()
|
|
1876
1850
|
shell.SetFocus()
|
|
1877
1851
|
return shell
|
|
1878
|
-
|
|
1852
|
+
|
|
1879
1853
|
def delete_shell(self, shell):
|
|
1880
1854
|
"""Close the current shell."""
|
|
1881
1855
|
if shell is self.rootshell:
|
|
1882
|
-
|
|
1856
|
+
# self.message("- Don't close the root shell.")
|
|
1883
1857
|
return
|
|
1884
1858
|
if self.debugger.busy and shell is self.debugger.interactive_shell:
|
|
1885
1859
|
wx.MessageBox("The debugger is running.\n\n"
|
|
@@ -1887,63 +1861,85 @@ class ShellFrame(MiniFrame):
|
|
|
1887
1861
|
return
|
|
1888
1862
|
j = self.console.GetPageIndex(shell)
|
|
1889
1863
|
if j != -1:
|
|
1890
|
-
self.console.DeletePage(j)
|
|
1891
|
-
|
|
1864
|
+
self.console.DeletePage(j) # Destroy the window
|
|
1865
|
+
|
|
1892
1866
|
## --------------------------------
|
|
1893
|
-
## Attributes for notebook pages
|
|
1867
|
+
## Attributes for notebook pages.
|
|
1894
1868
|
## --------------------------------
|
|
1895
|
-
|
|
1869
|
+
|
|
1896
1870
|
def get_all_pages(self, type=None):
|
|
1897
1871
|
"""Yields all pages of the specified type in the notebooks."""
|
|
1898
1872
|
yield from self.console.get_pages(type)
|
|
1899
1873
|
yield from self.ghost.get_pages(type)
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1874
|
+
|
|
1875
|
+
def get_all_shells(self, target=None):
|
|
1876
|
+
"""Yields all shells with specified target.
|
|
1877
|
+
|
|
1878
|
+
If the shell is found, it switches to the corresponding page.
|
|
1879
|
+
If `target` is not provided, it yields all shells in the notebooks.
|
|
1880
|
+
"""
|
|
1881
|
+
if target is None:
|
|
1882
|
+
yield from self.console.get_pages(type(self.rootshell))
|
|
1883
|
+
else:
|
|
1884
|
+
for shell in self.console.get_pages(type(self.rootshell)):
|
|
1885
|
+
if shell.target is target:
|
|
1886
|
+
self.console.swap_page(shell)
|
|
1887
|
+
yield shell
|
|
1888
|
+
|
|
1889
|
+
def get_all_editors(self, fn=None):
|
|
1890
|
+
"""Yields all editors with specified fn:filename or code.
|
|
1891
|
+
|
|
1892
|
+
If the editor is found, it switches to the corresponding page.
|
|
1893
|
+
If `fn` is not provided, it yields all editors in the notebooks.
|
|
1894
|
+
"""
|
|
1895
|
+
if fn is None:
|
|
1896
|
+
yield from self.ghost.get_pages(type(self.Log))
|
|
1897
|
+
else:
|
|
1898
|
+
for book in self.ghost.get_pages(type(self.Log)):
|
|
1899
|
+
buf = book.find_buffer(fn)
|
|
1900
|
+
if buf:
|
|
1901
|
+
book.swap_page(buf)
|
|
1902
|
+
yield book
|
|
1903
|
+
|
|
1906
1904
|
@property
|
|
1907
1905
|
def current_shell(self):
|
|
1908
1906
|
"""Currently selected shell or rootshell."""
|
|
1909
1907
|
return self.console.CurrentPage
|
|
1910
|
-
|
|
1911
|
-
@property
|
|
1912
|
-
def all_editors(self):
|
|
1913
|
-
"""Yields all editors in the notebooks."""
|
|
1914
|
-
return self.ghost.get_pages(type(self.Log))
|
|
1915
|
-
|
|
1908
|
+
|
|
1916
1909
|
@property
|
|
1917
1910
|
def current_editor(self):
|
|
1918
1911
|
"""Currently selected editor or scratch."""
|
|
1919
1912
|
editor = self.ghost.CurrentPage
|
|
1920
1913
|
if isinstance(editor, type(self.Log)):
|
|
1921
1914
|
return editor
|
|
1922
|
-
return next((
|
|
1923
|
-
|
|
1924
|
-
def
|
|
1925
|
-
"""
|
|
1926
|
-
If
|
|
1915
|
+
return next((book for book in self.get_all_editors() if book.IsShown()), self.Scratch)
|
|
1916
|
+
|
|
1917
|
+
def create_editor(self, bookname):
|
|
1918
|
+
"""Create a new editor (internal use only)..
|
|
1919
|
+
If such an editor already exists, no new editor is created.
|
|
1927
1920
|
"""
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1921
|
+
try:
|
|
1922
|
+
return next(book for book in self.get_all_editors() if book.Name == bookname)
|
|
1923
|
+
except StopIteration:
|
|
1924
|
+
with wx.FrozenWindow(self.ghost):
|
|
1925
|
+
editor = self.Log.__class__(self, bookname)
|
|
1926
|
+
self.ghost.AddPage(editor, bookname)
|
|
1927
|
+
self.ghost.move_tab(editor, 0)
|
|
1928
|
+
self.handler('book_new', editor)
|
|
1929
|
+
def _attach():
|
|
1930
|
+
editor.handler.append(self.Bookshelf.context)
|
|
1931
|
+
self.Bookshelf.build_tree(clear=0)
|
|
1932
|
+
wx.CallAfter(_attach)
|
|
1933
|
+
return editor
|
|
1934
|
+
|
|
1939
1935
|
## --------------------------------
|
|
1940
|
-
## Find text dialog
|
|
1936
|
+
## Find / Replace text dialog.
|
|
1941
1937
|
## --------------------------------
|
|
1942
1938
|
## *** The following code is a modification of <wx.py.frame.Frame> ***
|
|
1943
|
-
|
|
1939
|
+
|
|
1944
1940
|
__find_target = None
|
|
1945
|
-
|
|
1946
|
-
def
|
|
1941
|
+
|
|
1942
|
+
def on_search_dialog(self, evt, flags=0):
|
|
1947
1943
|
if self.findDlg is not None:
|
|
1948
1944
|
self.findDlg.SetFocus()
|
|
1949
1945
|
return
|
|
@@ -1952,32 +1948,54 @@ class ShellFrame(MiniFrame):
|
|
|
1952
1948
|
if not isinstance(wnd, stc.StyledTextCtrl):
|
|
1953
1949
|
return
|
|
1954
1950
|
self.__find_target = wnd
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1951
|
+
topic = wnd.topic_at_caret
|
|
1952
|
+
if topic:
|
|
1953
|
+
self.findData.FindString = topic
|
|
1954
|
+
self.findData.Flags |= wx.FR_DOWN
|
|
1955
|
+
self.findDlg = wx.FindReplaceDialog(wnd, self.findData, "Find", flags)
|
|
1958
1956
|
self.findDlg.Show()
|
|
1959
|
-
|
|
1960
|
-
def
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
if not isinstance(wnd, stc.StyledTextCtrl):
|
|
1968
|
-
wnd = self.__find_target
|
|
1969
|
-
if not wnd:
|
|
1970
|
-
return
|
|
1971
|
-
wnd.DoFindNext(data, self.findDlg or wnd)
|
|
1972
|
-
if self.findDlg:
|
|
1973
|
-
self.OnFindClose(None)
|
|
1974
|
-
wnd.EnsureVisible(wnd.cline)
|
|
1975
|
-
wnd.EnsureLineMoreOnScreen(wnd.cline)
|
|
1976
|
-
|
|
1977
|
-
def OnFindPrev(self, evt):
|
|
1957
|
+
|
|
1958
|
+
def on_replace_dialog(self, evt):
|
|
1959
|
+
self.on_search_dialog(evt, flags=wx.FR_REPLACEDIALOG)
|
|
1960
|
+
|
|
1961
|
+
def repeat_forward_search(self, evt):
|
|
1962
|
+
self.OnFindNext(evt, backward=False)
|
|
1963
|
+
|
|
1964
|
+
def repeat_backward_search(self, evt):
|
|
1978
1965
|
self.OnFindNext(evt, backward=True)
|
|
1979
|
-
|
|
1980
|
-
def
|
|
1966
|
+
|
|
1967
|
+
def OnFindNext(self, evt, backward=None): #<wx._core.FindDialogEvent>
|
|
1968
|
+
if not self.findData.FindString:
|
|
1969
|
+
self.message("No last search.")
|
|
1970
|
+
return
|
|
1971
|
+
if isinstance(evt, wx.FindDialogEvent):
|
|
1972
|
+
wnd = self.findDlg.Parent
|
|
1973
|
+
else:
|
|
1974
|
+
wnd = evt.EventObject
|
|
1975
|
+
if not isinstance(wnd, stc.StyledTextCtrl):
|
|
1976
|
+
wnd = self.__find_target
|
|
1977
|
+
if backward:
|
|
1978
|
+
self.findData.Flags &= ~wx.FR_DOWN
|
|
1979
|
+
else:
|
|
1980
|
+
self.findData.Flags |= wx.FR_DOWN
|
|
1981
|
+
|
|
1982
|
+
if evt.EventType == wx.EVT_FIND_REPLACE_ALL.typeId: # replace-all
|
|
1983
|
+
n = wnd.DoReplaceAll(self.findData)
|
|
1984
|
+
self.message(f"Replaced {n} strings.")
|
|
1985
|
+
if self.findDlg:
|
|
1986
|
+
self.OnFindClose(None)
|
|
1987
|
+
return
|
|
1988
|
+
elif evt.EventType == wx.EVT_FIND_REPLACE.typeId: # replace
|
|
1989
|
+
loc = wnd.DoReplaceNext(self.findData)
|
|
1990
|
+
else:
|
|
1991
|
+
loc = wnd.DoFindNext(self.findData)
|
|
1992
|
+
if self.findDlg:
|
|
1993
|
+
if not (self.findDlg.WindowStyle & wx.FR_REPLACEDIALOG): # for search-dialog
|
|
1994
|
+
self.OnFindClose(None)
|
|
1995
|
+
if loc < 0:
|
|
1996
|
+
self.message("Unable to find the search text.")
|
|
1997
|
+
|
|
1998
|
+
def OnFindClose(self, evt): #<wx._core.FindDialogEvent>
|
|
1981
1999
|
self.findDlg.Destroy()
|
|
1982
2000
|
self.findDlg = None
|
|
1983
2001
|
|
|
@@ -1989,7 +2007,7 @@ def filling(obj=None, **kwargs):
|
|
|
1989
2007
|
rootLabel=typename(obj),
|
|
1990
2008
|
pos=wx.GetMousePosition(),
|
|
1991
2009
|
**kwargs)
|
|
1992
|
-
frame.filling.text.WrapMode = 0
|
|
1993
|
-
frame.filling.text.Zoom = -1
|
|
2010
|
+
frame.filling.text.WrapMode = 0 # no wrap
|
|
2011
|
+
frame.filling.text.Zoom = -1 # zoom level of size of fonts
|
|
1994
2012
|
frame.Show()
|
|
1995
2013
|
return frame
|