8000 FIX: fixed two problems for the "existing QApplication" case (closes … · larray-project/larray-editor@40024a2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 40024a2

Browse files
committed
FIX: fixed two problems for the "existing QApplication" case (closes #200)
- qt_app.exec_() should NOT be called (there is already an event loop running) - we should NOT change the except hook (given that the function does not block, we change it back right away)
1 parent 784da81 commit 40024a2

File tree

1 file changed

+122
-106
lines changed

1 file changed

+122
-106
lines changed

larray_editor/api.py

Lines changed: 122 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,51 @@
1212
__all__ = ['view', 'edit', 'debug', 'compare', 'REOPEN_LAST_FILE', 'run_editor_on_exception']
1313

1414

15-
def get_app_and_window(app_name):
15+
def _show_dialog(app_name, create_dialog_func, *args, **kwargs):
16+
"""Show dialog created by `create_dialog_func`
17+
18+
Use either the existing QApplication if any, otherwise a new QApplication.
19+
20+
Parameters
21+
----------
22+
app_name : str
23+
Application name when creating a new one.
24+
create_dialog_func : function
25+
The function which creates the dialog.
26+
"""
1627
qt_app = QApplication.instance()
17-
if qt_app is None:
28+
new_app = qt_app is None
29+
if new_app:
1830
qt_app = QApplication(sys.argv)
1931
qt_app.setOrganizationName("LArray")
2032
qt_app.setApplicationName(app_name)
2133
parent = None
2234
else:
2335
parent = qt_app.activeWindow()
24-
return qt_app, parent
2536

37+
if 'depth' in kwargs:
38+
kwargs['depth'] += 1
39+
40+
dlg = create_dialog_func(parent, *args, **kwargs)
41+
if dlg is None:
42+
raise RuntimeError('Could not create dialog')
43+
44+
dlg.show()
45+
if new_app:
46+
# We do not use install_except_hook/restore_except_hook so that we can restore the hook actually used when
47+
# this function is called instead of the one which was used when the module was loaded.
48+
49+
# Note there is no point in changing the except hook when we have an existing QApplication given that
50+
# in that case the function does not block
51+
orig_except_hook = sys.excepthook
52+
sys.excepthook = _qt_except_hook
53+
54+
qt_app.exec_()
2655

27-
def find_names(obj, depth=0):
56+
sys.excepthook = orig_except_hook
57+
58+
59+
def _find_names(obj, depth=0):
2860
"""Return all names an object is bound to.
2961
3062
Parameters
@@ -49,7 +81,7 @@ def find_names(obj, depth=0):
4981
return sorted(names)
5082

5183

52-
def get_title(obj, depth=0, maxnames=3):
84+
def _get_title(obj, depth=0, maxnames=3):
5385
"""Return a title for an object (a combination of the names it is bound to).
5486
5587
Parameters
@@ -65,14 +97,49 @@ def get_title(obj, depth=0, maxnames=3):
6597
str
6698
title for obj. This can be '' if we computed an array just to view it.
6799
"""
68-
names = find_names(obj, depth=depth + 1)
100+
names = _find_names(obj, depth=depth + 1)
69101
# names can be == []
70102
# eg. view(arr['M'])
71103
if len(names) > maxnames:
72104
names = names[:maxnames] + ['...']
73105
return ', '.join(names)
74106

75107

108+
def _edit_dialog(parent, obj=None, title='', minvalue=None, maxvalue=None, readonly=False, depth=0,
109+
display_caller_info=True, add_larray_functions=None):
110+
caller_frame = sys._getframe(depth + 1)
111+
caller_info = getframeinfo(caller_frame) if display_caller_info else None
112+
if add_larray_functions is None:
113+
add_larray_functions = obj is not None
114+
115+
if obj is None:
116+
global_vars = caller_frame.f_globals
117+
local_vars = caller_frame.f_locals
118+
obj = {k: global_vars[k] for k in sorted(global_vars.keys())}
119+
if local_vars is not global_vars:
120+
obj.update({k: local_vars[k] for k in sorted(local_vars.keys())})
121+
122+
if not isinstance(obj, (la.Session, la.Array)) and hasattr(obj, 'keys'):
123+
obj = la.Session(obj)
124+
125+
if not title and obj is not REOPEN_LAST_FILE:
126+
title = _get_title(obj, depth=depth + 1)
127+
128+
if obj is REOPEN_LAST_FILE or isinstance(obj, (str, la.Session)):
129+
dlg = MappingEditor(parent)
130+
assert minvalue is None and maxvalue is None
131+
setup_ok = dlg.setup_and_check(obj, title=title, readonly=readonly, caller_info=caller_info,
132+
add_larray_functions=add_larray_functions)
133+
else:
134+
dlg = ArrayEditor(parent)
135+
setup_ok = dlg.setup_and_check(obj, title=title, readonly=readonly, caller_info=caller_info,
136+
minvalue=minvalue, maxvalue=maxvalue)
137+
if setup_ok:
138+
return dlg
139+
else:
140+
return None
141+
142+
76143
def edit(obj=None, title='', minvalue=None, maxvalue=None, readonly=False, depth=0, display_caller_info=True,
77144
add_larray_functions=None):
78145
"""
@@ -113,46 +180,8 @@ def edit(obj=None, title='', minvalue=None, maxvalue=None, readonly=False, depth
113180
>>> # will open an editor for a1 only
114181
>>> edit(a1) # doctest: +SKIP
115182
"""
116-
# we don't use install_except_hook/restore_except_hook so that we can restore the hook actually used when
117-
# this function is called instead of the one which was used when the module was loaded.
118-
orig_except_hook = sys.excepthook
119-
sys.excepthook = _qt_except_hook
120-
121-
qt_app, parent = get_app_and_window("Viewer")
122-
123-
caller_frame = sys._getframe(depth + 1)
124-
caller_info = getframeinfo(caller_frame) if display_caller_info else None
125-
if add_larray_functions is None:
126-
add_larray_functions = obj is not None
127-
128-
if obj is None:
129-
global_vars = caller_frame.f_globals
130-
local_vars = caller_frame.f_locals
131-
obj = {k: global_vars[k] for k in sorted(global_vars.keys())}
132-
if local_vars is not global_vars:
133-
obj.update({k: local_vars[k] for k in sorted(local_vars.keys())})
134-
135-
if not isinstance(obj, (la.Session, la.Array)) and hasattr(obj, 'keys'):
136-
obj = la.Session(obj)
137-
138-
if not title and obj is not REOPEN_LAST_FILE:
139-
title = get_title(obj, depth=depth + 1)
140-
141-
if obj is REOPEN_LAST_FILE or isinstance(obj, (str, la.Session)):
142-
dlg = MappingEditor(parent)
143-
assert minvalue is None and maxvalue is None
144-
setup_ok = dlg.setup_and_check(obj, title=title, readonly=readonly, caller_info=caller_info,
145-
add_larray_functions=add_larray_functions)
146-
else:
147-
dlg = ArrayEditor(parent)
148-
setup_ok = dlg.setup_and_check(obj, title=title, readonly=readonly, minvalue=minvalue, maxvalue=maxvalue,
149-
caller_info=caller_info)
150-
151-
if setup_ok:
152-
dlg.show()
153-
qt_app.exec_()
154-
155-
sys.excepthook = orig_except_hook
183+
_show_dialog("Viewer", _edit_dialog, obj=obj, title=title, minvalue=minvalue, maxvalue=maxvalue, readonly=readonly,
184+
depth=depth + 2, display_caller_info=display_caller_info, add_larray_functions=add_larray_functions)
156185

157186

158187
def view(obj=None, title='', depth=0, display_caller_info=True, add_larray_functions=None):
@@ -187,26 +216,17 @@ def view(obj=None, title='', depth=0, display_caller_info=True, add_larray_funct
187216
>>> # will open a viewer showing only a1
188217
>>> view(a1) # doctest: +SKIP
189218
"""
190-
edit(obj, title=title, readonly=True, depth=depth + 1, display_caller_info=display_caller_info,
191-
add_larray_functions=add_larray_functions)
219+
_show_dialog("Viewer", _edit_dialog, obj=obj, title=title, readonly=True,
220+
depth=depth + 2, display_caller_info=display_caller_info, add_larray_functions=add_larray_functions)
192221

193222

194-
def _debug(stack_summary, stack_pos=None):
195-
# we don't use install_except_hook/restore_except_hook so that we can restore the hook actually used when
196-
# this function is called instead of the one which was used when the module was loaded.
197-
orig_except_hook = sys.excepthook
198-
sys.excepthook = _qt_except_hook
199-
200-
qt_app, parent = get_app_and_window("Debugger")
201-
223+
def _debug_dialog(parent, stack_summary, stack_pos=None):
202224
assert isinstance(stack_summary, StackSummary)
203225
dlg = MappingEditor(parent)
204-
setup_ok = dlg.setup_and_check(stack_summary, stack_pos=stack_pos)
205-
if setup_ok:
206-
dlg.show()
207-
qt_app.exec_()
208-
209-
sys.excepthook = orig_except_hook
226+
if dlg.setup_and_check(stack_summary, stack_pos=stack_pos):
227+
return dlg
228+
else:
229+
return None
210230

211231

212232
def debug(depth=0):
@@ -220,7 +240,44 @@ def debug(depth=0):
220240
"""
221241
caller_frame = sys._getframe(depth + 1)
222242
stack_summary = extract_stack(caller_frame)
223-
_debug(stack_summary)
243+
_show_dialog("Debugger", _debug_dialog, stack_summary)
244+
245+
246+
def _compare_dialog(parent, *args, **kwargs):
247+
title = kwargs.pop('title', '')
248+
names = kwargs.pop('names', None)
249+
depth = kwargs.pop('depth', 0)
250+
display_caller_info = kwargs.pop('display_caller_info', True)
251+
252+
caller_frame = sys._getframe(depth + 1)
253+
if display_caller_info:
254+
caller_info = getframeinfo(caller_frame)
255+
else:
256+
caller_info = None
257+
258+
if any(isinstance(a, la.Session) for a in args):
259+
from larray_editor.comparator import SessionComparator
260+
dlg = SessionComparator(parent)
261+
default_name = 'session'
262+
else:
263+
from larray_editor.comparator import ArrayComparator
264+
dlg = ArrayComparator(parent)
265+
default_name = 'array'
266+
267+
if names is None:
268+
def get_name(i, obj, depth=0):
269+
obj_names = _find_names(obj, depth=depth + 1)
270+
return obj_names[0] if obj_names else f'{default_name} {i:d}'
271+
272+
# depth + 2 because of the list comprehension
273+
names = [get_name(i, a, depth=depth + 2) for i, a in enumerate(args)]
274+
else:
275+
assert isinstance(names, list) and len(names) == len(args)
276+
277+
if dlg.setup_and_check(args, names=names, title=title, caller_info=caller_info, **kwargs):
278+
return dlg
279+
else:
280+
return None
224281

225282

226283
def compare(*args, **kwargs):
@@ -264,48 +321,7 @@ def compare(*args, **kwargs):
264321
>>> compare(a1, a2, title='first comparison') # doctest: +SKIP
265322
>>> compare(a1 + 1, a2, title='second comparison', names=['a1+1', 'a2']) # doctest: +SKIP
266323
"""
267-
# we don't use install_except_hook/restore_except_hook so that we can restore the hook actually used when
268-
# this function is called instead of the one which was used when the module was loaded.
269-
orig_except_hook = sys.excepthook
270-
sys.excepthook = _qt_except_hook
271-
272-
title = kwargs.pop('title', '')
273-
names = kwargs.pop('names', None)
274-
depth = kwargs.pop('depth', 0)
275-
display_caller_info = kwargs.pop('display_caller_info', True)
276-
277-
qt_app, parent = get_app_and_window("Viewer")
278-
279-
caller_frame = sys._getframe(depth + 1)
280-
if display_caller_info:
281-
caller_info = getframeinfo(caller_frame)
282-
else:
283-
caller_info = None
284-
285-
if any(isinstance(a, la.Session) for a in args):
286-
from larray_editor.comparator import SessionComparator
287-
dlg = SessionComparator(parent)
288-
default_name = 'session'
289-
else:
290-
from larray_editor.comparator import ArrayComparator
291-
dlg = ArrayComparator(parent)
292-
default_name = 'array'
293-
294-
if names is None:
295-
def get_name(i, obj, depth=0):
296-
obj_names = find_names(obj, depth=depth + 1)
297-
return obj_names[0] if obj_names else f'{default_name} {i:d}'
298-
299-
# depth + 2 because of the list comprehension
300-
names = [get_name(i, a, depth=depth + 2) for i, a in enumerate(args)]
301-
else:
302-
assert isinstance(names, list) and len(names) == len(args)
303-
304-
if dlg.setup_and_check(args, names=names, title=title, caller_info=caller_info, **kwargs):
305-
dlg.show()
306-
qt_app.exec_()
307-
308-
sys.excepthook = orig_except_hook
324+
_show_dialog("Comparator", _compare_dialog, *args, **kwargs)
309325

310326

311327
_orig_except_hook = sys.excepthook
@@ -391,7 +407,7 @@ def excepthook(type, value, tback):
391407
stack = extract_tb(main_tb, limit=tb_limit)
392408
stack_pos = user_tb_length - 1 if user_tb_length is not None and usercode_frame else None
393409
print("\nlaunching larray editor to debug...", file=sys.stderr)
394-
_debug(stack, stack_pos=stack_pos)
410+
_show_dialog("Debugger", _debug_dialog, stack, stack_pos=stack_pos)
395411

396412
return excepthook
397413

0 commit comments

Comments
 (0)
0