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