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