1
1
import os
2
2
import re
3
+
3
4
import matplotlib
4
5
import numpy as np
5
6
import collections
6
7
7
8
import larray as la
8
9
10
+ from larray_editor .traceback_tools import StackSummary
9
11
from larray_editor .utils import (PY2 , PYQT5 , _ , create_action , show_figure , ima , commonpath , dependencies ,
10
12
get_versions , get_documentation_url , urls , RecentlyUsedList )
11
13
from larray_editor .arraywidget import ArrayEditorWidget
14
16
from qtpy .QtCore import Qt , QUrl , QSettings
15
17
from qtpy .QtGui import QDesktopServices , QKeySequence
16
18
from qtpy .QtWidgets import (QMainWindow , QWidget , QListWidget , QListWidgetItem , QSplitter , QFileDialog , QPushButton ,
17
- QDialogButtonBox , QShortcut , QHBoxLayout , QVBoxLayout , QGridLayout , QLineEdit , QUndoStack ,
19
+ QDialogButtonBox , QShortcut , QVBoxLayout , QGridLayout , QLineEdit , QUndoStack ,
18
20
QCheckBox , QComboBox , QMessageBox , QDialog , QInputDialog , QLabel , QGroupBox , QRadioButton )
19
21
20
22
try :
@@ -338,7 +340,7 @@ def __init__(self, parent=None):
338
340
339
341
self .setup_menu_bar ()
340
342
341
- def _setup_and_check (self , widget , data , title , readonly , ** kwargs ):
343
+ def _setup_and_check (self , widget , data , title , readonly , stack_pos = None ):
342
344
"""Setup MappingEditor"""
343
345
layout = QVBoxLayout ()
344
346
widget .setLayout (layout )
@@ -363,6 +365,7 @@ def _setup_and_check(self, widget, data, title, readonly, **kwargs):
363
365
kernel = kernel_manager .kernel
364
366
365
367
# TODO: use self._reset() instead
368
+ # FIXME: when using the editor as a debugger this is annoying
366
369
kernel .shell .run_cell ('from larray import *' )
367
370
text_formatter = kernel .shell .display_formatter .formatters ['text/plain' ]
368
371
@@ -405,9 +408,34 @@ def void_formatter(array, *args, **kwargs):
405
408
right_panel_widget .setLayout (right_panel_layout )
406
409
407
410
main_splitter = QSplitter (Qt .Horizontal )
408
- main_splitter .addWidget (self ._listwidget )
411
+ debug = isinstance (data , StackSummary )
412
+ if debug :
413
+ self ._stack_frame_widget = QListWidget (self )
414
+ stack_frame_widget = self ._stack_frame_widget
415
+ stack_frame_widget .itemSelectionChanged .connect (self .on_stack_frame_changed )
416
+ stack_frame_widget .setMinimumWidth (60 )
417
+
418
+ for frame_summary in data :
419
+ funcname = frame_summary .name
420
+ filename = os .path .basename (frame_summary .filename )
421
+ listitem = QListWidgetItem (stack_frame_widget )
422
+ listitem .setText ("{}, {}:{}" .format (funcname , filename , frame_summary .lineno ))
423
+ # we store the frame summary object in the user data of the list
424
+ listitem .setData (Qt .UserRole , frame_summary )
425
+ listitem .setToolTip (frame_summary .line )
426
+ row = stack_pos if stack_pos is not None else len (data ) - 1
427
+ stack_frame_widget .setCurrentRow (row )
428
+
429
+ left_panel_widget = QSplitter (Qt .Vertical )
430
+ left_panel_widget .addWidget (self ._listwidget )
431
+ left_panel_widget .addWidget (stack_frame_widget )
432
+ left_panel_widget .setSizes ([500 , 200 ])
433
+ data = self .data
434
+ else :
435
+ left_panel_widget = self ._listwidget
436
+ main_splitter .addWidget (left_panel_widget )
409
437
main_splitter .addWidget (right_panel_widget )
410
- main_splitter .setSizes ([10 , 90 ])
438
+ main_splitter .setSizes ([180 , 620 ])
411
439
main_splitter .setCollapsible (1 , False )
412
440
self .widget_state_settings ['main_splitter' ] = main_splitter
413
441
@@ -427,8 +455,7 @@ def void_formatter(array, *args, **kwargs):
427
455
else :
428
456
QMessageBox .critical (self , "Error" , "File {} could not be found" .format (data ))
429
457
self .new ()
430
- # convert input data to Session if not
431
- else :
458
+ elif not debug :
432
459
self ._push_data (data )
433
460
434
461
def _push_data (self , data ):
@@ -439,6 +466,28 @@ def _push_data(self, data):
439
466
self .add_list_items (arrays )
440
467
self ._listwidget .setCurrentRow (0 )
441
468
469
+ def on_stack_frame_changed (self ):
470
+ selected = self ._stack_frame_widget .selectedItems ()
471
+ if selected :
472
+ assert len (selected ) == 1
473
+ selected_item = selected [0 ]
474
+ assert isinstance (selected_item , QListWidgetItem )
475
+
476
+ frame_summary = selected_item .data (Qt .UserRole )
477
+ frame_globals , frame_locals = frame_summary .globals , frame_summary .locals
478
+ data = collections .OrderedDict ()
479
+ data .update ([(k , frame_globals [k ]) for k in sorted (frame_globals .keys ())])
480
+ data .update ([(k , frame_locals [k ]) for k in sorted (frame_locals .keys ())])
481
+
482
+ # CHECK:
483
+ # * This clears the undo/redo stack, which is safer but is not ideal.
484
+ # When inspecting, for all frames except the last one the editor should be readonly (we should allow
485
+ # creating new temporary variables but not change existing ones).
486
+ # * Does changing the last frame values has any effect after quitting the editor?
487
+ # It would be nice if we could do that (possibly with a warning when quitting the debug window)
488
+ self ._reset ()
489
+ self ._push_data (data )
490
+
442
491
def _reset (self ):
443
492
self .data = la .Session ()
444
493
self ._listwidget .clear ()
@@ -521,9 +570,9 @@ def add_list_items(self, names):
521
570
522
571
def delete_list_item (self , to_delete ):
523
572
deleted_items = self ._listwidget .findItems (to_delete , Qt .MatchExactly )
524
- assert len (deleted_items ) == 1
525
- deleted_item_idx = self ._listwidget .row (deleted_items [0 ])
526
- self ._listwidget .takeItem (deleted_item_idx )
573
+ if len (deleted_items ) == 1 :
574
+ deleted_item_idx = self ._listwidget .row (deleted_items [0 ])
575
+ self ._listwidget .takeItem (deleted_item_idx )
527
576
528
577
def select_list_item (self , to_display ):
529
578
changed_items = self ._listwidget .findItems (to_display , Qt .MatchExactly )
@@ -644,7 +693,7 @@ def ipython_cell_executed(self):
644
693
# last command. Which means that if the last command did not produce any output, _ is not modified.
645
694
cur_output = user_ns ['_oh' ].get (cur_input_num )
646
695
if cur_output is not None :
647
- if self ._display_in_grid ('_ ' , cur_output ):
696
+ if self ._display_in_grid ('<expr> ' , cur_output ):
648
697
self .view_expr (cur_output )
649
698
650
699
if isinstance (cur_output , collections .Iterable ):
0 commit comments