napari / napari-console

A plugin that adds a console to napari
BSD 3-Clause "New" or "Revised" License
3 stars 12 forks source link

Capture caller frame of napari.run() when starting the console. #18

Closed Carreau closed 18 hours ago

Carreau commented 2 years ago

Should fix napari/napari#4098 Replaces napari/napari#4140, and see discussion there as well Needs napari/napari#4212

psobolewskiPhD commented 1 year ago

FYI, trying this branch with main napari gives a TypeError related to in-n-out I think:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File ~/Dev/miniforge3/envs/napari-dev/lib/python3.10/site-packages/in_n_out/_store.py:774, in Store.inject.<locals>._inner.<locals>._exec(*args=(), **kwargs={})
    773 try:
--> 774     result = func(**_kwargs)
        _kwargs = {'viewer': Viewer(axes=AxesOverlay(visible=False, opacity=1.0, order=1000000, labels=True, colored=True, dashed=False, arrows=True), camera=Camera(center=(0.0, 127.5, 127.5), zoom=2.62734375, angles=(0.0, 0.0, 90.0), perspective=0.0, interactive=True), cursor=Cursor(position=(30.0, 1.0, 0.0, 0.0), scaled=True, size=1, style=<CursorStyle.STANDARD: 'standard'>), dims=Dims(ndim=4, ndisplay=2, last_used=0, range=((0.0, 60.0, 1.0), (0.0, 2.0, 1.0), (0.0, 256.0, 1.0), (0.0, 256.0, 1.0)), current_step=(30, 1, 128, 128), order=(0, 1, 2, 3), axis_labels=('0', '1', '2', '3')), grid=GridCanvas(stride=1, shape=(-1, -1), enabled=False), layers=[<Image layer 'my_image_data' at 0x14db13a90>], scale_bar=ScaleBarOverlay(visible=False, opacity=1.0, order=1000000, position=<CanvasPosition.BOTTOM_RIGHT: 'bottom_right'>, colored=False, color=<class 'numpy.ndarray'> (4,) float32, ticks=True, font_size=10.0, box=False, box_color=<class 'numpy.ndarray'> (4,) float32, unit=None), text_overlay=TextOverlay(visible=False, opacity=1.0, order=1000000, position=<CanvasPosition.BOTTOM_RIGHT: 'bottom_right'>, color=<class 'numpy.ndarray'> (4,) float32, font_size=10.0, text=''), overlays=Overlays(interaction_box=InteractionBox(points=None, show=False, show_handle=False, show_vertices=False, selection_box_drag=None, selection_box_final=None, transform_start=<napari.utils.transforms.transforms.Affine object at 0x13a86ad40>, transform_drag=<napari.utils.transforms.transforms.Affine object at 0x13a86ada0>, transform_final=<napari.utils.transforms.transforms.Affine object at 0x13a86ae00>, transform=<napari.utils.transforms.transforms.Affine object at 0x13a86ae60>, allow_new_selection=True, selected_vertex=None)), help='', status='Ready', tooltip=Tooltip(visible=True, text=''), theme='dark', title='napari', mouse_over_canvas=False, mouse_move_callbacks=[<function InteractionBoxMouseBindings.initialize_mouse_events.<locals>.mouse_move at 0x14a20f2e0>], mouse_drag_callbacks=[<function InteractionBoxMouseBindings.initialize_mouse_events.<locals>.mouse_drag at 0x14a016c20>], mouse_double_click_callbacks=[], mouse_wheel_callbacks=[<function dims_scroll at 0x13a6bef80>], _persisted_mouse_event={}, _mouse_drag_gen={}, _mouse_wheel_gen={}, keymap={'Shift': <function InteractionBoxMouseBindings.initialize_key_events.<locals>.hold_to_lock_aspect_ratio at 0x14a017490>, 'Control-Shift-R': <function InteractionBoxMouseBindings._reset_active_layer_affine at 0x14a1d5bd0>, 'Control-Shift-A': <function InteractionBoxMouseBindings._transform_active_layer at 0x14a1d5b40>})}
        func = <function toggle_console_visibility at 0x13a6edbd0>
    775 except TypeError as e:
    776     # likely a required argument is still missing.
    777     # show what was injected and raise

File ~/Dev/napari/napari/components/_viewer_key_bindings.py:130, in toggle_console_visibility(viewer=Viewer(axes=AxesOverlay(visible=False, opacity=1...indings._transform_active_layer at 0x14a1d5b40>}))
    124 @register_viewer_action(
    125     trans._(
    126         "Show/Hide IPython console (only available when napari started as standalone application)"
    127     )
    128 )
    129 def toggle_console_visibility(viewer: Viewer):
--> 130     viewer.window._qt_viewer.toggle_console_visibility()
        viewer = Viewer(axes=AxesOverlay(visible=False, opacity=1.0, order=1000000, labels=True, colored=True, dashed=False, arrows=True), camera=Camera(center=(0.0, 127.5, 127.5), zoom=2.62734375, angles=(0.0, 0.0, 90.0), perspective=0.0, interactive=True), cursor=Cursor(position=(30.0, 1.0, 0.0, 0.0), scaled=True, size=1, style=<CursorStyle.STANDARD: 'standard'>), dims=Dims(ndim=4, ndisplay=2, last_used=0, range=((0.0, 60.0, 1.0), (0.0, 2.0, 1.0), (0.0, 256.0, 1.0), (0.0, 256.0, 1.0)), current_step=(30, 1, 128, 128), order=(0, 1, 2, 3), axis_labels=('0', '1', '2', '3')), grid=GridCanvas(stride=1, shape=(-1, -1), enabled=False), layers=[<Image layer 'my_image_data' at 0x14db13a90>], scale_bar=ScaleBarOverlay(visible=False, opacity=1.0, order=1000000, position=<CanvasPosition.BOTTOM_RIGHT: 'bottom_right'>, colored=False, color=<class 'numpy.ndarray'> (4,) float32, ticks=True, font_size=10.0, box=False, box_color=<class 'numpy.ndarray'> (4,) float32, unit=None), text_overlay=TextOverlay(visible=False, opacity=1.0, order=1000000, position=<CanvasPosition.BOTTOM_RIGHT: 'bottom_right'>, color=<class 'numpy.ndarray'> (4,) float32, font_size=10.0, text=''), overlays=Overlays(interaction_box=InteractionBox(points=None, show=False, show_handle=False, show_vertices=False, selection_box_drag=None, selection_box_final=None, transform_start=<napari.utils.transforms.transforms.Affine object at 0x13a86ad40>, transform_drag=<napari.utils.transforms.transforms.Affine object at 0x13a86ada0>, transform_final=<napari.utils.transforms.transforms.Affine object at 0x13a86ae00>, transform=<napari.utils.transforms.transforms.Affine object at 0x13a86ae60>, allow_new_selection=True, selected_vertex=None)), help='', status='Ready', tooltip=Tooltip(visible=True, text=''), theme='dark', title='napari', mouse_over_canvas=False, mouse_move_callbacks=[<function InteractionBoxMouseBindings.initialize_mouse_events.<locals>.mouse_move at 0x14a20f2e0>], mouse_drag_callbacks=[<function InteractionBoxMouseBindings.initialize_mouse_events.<locals>.mouse_drag at 0x14a016c20>], mouse_double_click_callbacks=[], mouse_wheel_callbacks=[<function dims_scroll at 0x13a6bef80>], _persisted_mouse_event={}, _mouse_drag_gen={}, _mouse_wheel_gen={}, keymap={'Shift': <function InteractionBoxMouseBindings.initialize_key_events.<locals>.hold_to_lock_aspect_ratio at 0x14a017490>, 'Control-Shift-R': <function InteractionBoxMouseBindings._reset_active_layer_affine at 0x14a1d5bd0>, 'Control-Shift-A': <function InteractionBoxMouseBindings._transform_active_layer at 0x14a1d5b40>})

File ~/Dev/napari/napari/_qt/qt_viewer.py:898, in QtViewer.toggle_console_visibility(self=<napari._qt.qt_viewer.QtViewer object>, event=None)
    897 # force instantiation of console if not already instantiated
--> 898 _ = self.console
        self = <napari._qt.qt_viewer.QtViewer object at 0x13c38b9a0>
    900 viz = not self.dockConsole.isVisible()

File ~/Dev/napari/napari/_qt/qt_viewer.py:488, in QtViewer.console(self=<napari._qt.qt_viewer.QtViewer object>)
    487 warnings.filterwarnings("ignore")
--> 488 self.console = QtConsole(self.viewer)
        self = <napari._qt.qt_viewer.QtViewer object at 0x13c38b9a0>
        QtConsole = <class 'napari_console.qt_console.QtConsole'>
        self.viewer = Viewer(axes=AxesOverlay(visible=False, opacity=1.0, order=1000000, labels=True, colored=True, dashed=False, arrows=True), camera=Camera(center=(0.0, 127.5, 127.5), zoom=2.62734375, angles=(0.0, 0.0, 90.0), perspective=0.0, interactive=True), cursor=Cursor(position=(30.0, 1.0, 0.0, 0.0), scaled=True, size=1, style=<CursorStyle.STANDARD: 'standard'>), dims=Dims(ndim=4, ndisplay=2, last_used=0, range=((0.0, 60.0, 1.0), (0.0, 2.0, 1.0), (0.0, 256.0, 1.0), (0.0, 256.0, 1.0)), current_step=(30, 1, 128, 128), order=(0, 1, 2, 3), axis_labels=('0', '1', '2', '3')), grid=GridCanvas(stride=1, shape=(-1, -1), enabled=False), layers=[<Image layer 'my_image_data' at 0x14db13a90>], scale_bar=ScaleBarOverlay(visible=False, opacity=1.0, order=1000000, position=<CanvasPosition.BOTTOM_RIGHT: 'bottom_right'>, colored=False, color=<class 'numpy.ndarray'> (4,) float32, ticks=True, font_size=10.0, box=False, box_color=<class 'numpy.ndarray'> (4,) float32, unit=None), text_overlay=TextOverlay(visible=False, opacity=1.0, order=1000000, position=<CanvasPosition.BOTTOM_RIGHT: 'bottom_right'>, color=<class 'numpy.ndarray'> (4,) float32, font_size=10.0, text=''), overlays=Overlays(interaction_box=InteractionBox(points=None, show=False, show_handle=False, show_vertices=False, selection_box_drag=None, selection_box_final=None, transform_start=<napari.utils.transforms.transforms.Affine object at 0x13a86ad40>, transform_drag=<napari.utils.transforms.transforms.Affine object at 0x13a86ada0>, transform_final=<napari.utils.transforms.transforms.Affine object at 0x13a86ae00>, transform=<napari.utils.transforms.transforms.Affine object at 0x13a86ae60>, allow_new_selection=True, selected_vertex=None)), help='', status='Ready', tooltip=Tooltip(visible=True, text=''), theme='dark', title='napari', mouse_over_canvas=False, mouse_move_callbacks=[<function InteractionBoxMouseBindings.initialize_mouse_events.<locals>.mouse_move at 0x14a20f2e0>], mouse_drag_callbacks=[<function InteractionBoxMouseBindings.initialize_mouse_events.<locals>.mouse_drag at 0x14a016c20>], mouse_double_click_callbacks=[], mouse_wheel_callbacks=[<function dims_scroll at 0x13a6bef80>], _persisted_mouse_event={}, _mouse_drag_gen={}, _mouse_wheel_gen={}, keymap={'Shift': <function InteractionBoxMouseBindings.initialize_key_events.<locals>.hold_to_lock_aspect_ratio at 0x14a017490>, 'Control-Shift-R': <function InteractionBoxMouseBindings._reset_active_layer_affine at 0x14a1d5bd0>, 'Control-Shift-A': <function InteractionBoxMouseBindings._transform_active_layer at 0x14a1d5b40>})
    489 self.console.push(
    490     {'napari': napari, 'action_manager': action_manager}
    491 )

File ~/Dev/napari-console/napari_console/qt_console.py:143, in QtConsole.__init__(self=<napari_console.qt_console.QtConsole object>, viewer=Viewer(axes=AxesOverlay(visible=False, opacity=1...indings._transform_active_layer at 0x14a1d5b40>}))
    140     raise ValueError(
    141         'ipython shell not recognized; ' f'got {type(shell)}'
    142     )
--> 143 self._capture()
        self = <napari_console.qt_console.QtConsole object at 0x13c38bd00>
    144 # Add any user variables

File ~/Dev/napari-console/napari_console/qt_console.py:160, in QtConsole._capture(self=<napari_console.qt_console.QtConsole object>)
    157 """
    158 Capture variable from first enclosing scope that is not napari
    159 """
--> 160 with CallerFrame(_not_napari) as c:
    161     self.push(dict(c.namespace))

File ~/Dev/napari/napari/utils/naming.py:106, in CallerFrame.__enter__(self=<napari.utils.naming.CallerFrame object>)
    101 n = 1
    102 while (
    103     inspect.isframe(frame)
    104     and inspect.isframe(frame.f_back)
    105     and inspect.iscode(frame.f_code)
--> 106     and (self.predicate(n, frame))
        self = <napari.utils.naming.CallerFrame object at 0x14fc59990>
        n = 1
        self.predicate = <function _not_napari at 0x14bc9f130>
    107 ):
    108     n += 1

TypeError: _not_napari() takes 1 positional argument but 2 were given

The above exception was the direct cause of the following exception:

TypeError                                 Traceback (most recent call last)
File ~/Dev/napari/napari/utils/action_manager.py:227, in ActionManager.bind_button.<locals>.<lambda>()
    219     if isgeneratorfunction(action):
    220         raise ValueError(
    221             trans._(
    222                 '`bind_button` cannot be used with generator functions',
    223                 deferred=True,
    224             )
    225         )
--> 227 button.clicked.connect(lambda: self.trigger(name))
        name = 'napari:toggle_console_visibility'
        self = <napari.utils.action_manager.ActionManager object at 0x129e66e00>
    228 if name in self._actions:
    229     button.setToolTip(
    230         f'{self._build_tooltip(name)} {extra_tooltip_text}'
    231     )

File ~/Dev/napari/napari/utils/action_manager.py:427, in ActionManager.trigger(self=<napari.utils.action_manager.ActionManager object>, name='napari:toggle_console_visibility')
    425 def trigger(self, name: str) -> Any:
    426     """Trigger the action `name`."""
--> 427     return self._actions[name].injected()
        self._actions[name].injected = <function toggle_console_visibility at 0x14a0164d0>
        self._actions[name] = Action(command=<function toggle_console_visibility at 0x13a6edbd0>, description='Show/Hide IPython console (only available when napari started as standalone application)', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False)
        self._actions = {'napari:orient_plane_normal_along_z': Action(command=<function orient_plane_normal_along_z at 0x139da7640>, description='Orient plane normal along z-axis', keymapprovider=<class 'napari.layers.image.image.Image'>, repeatable=False), 'napari:orient_plane_normal_along_y': Action(command=<function orient_plane_normal_along_y at 0x139da7760>, description='orient plane normal along y-axis', keymapprovider=<class 'napari.layers.image.image.Image'>, repeatable=False), 'napari:orient_plane_normal_along_x': Action(command=<function orient_plane_normal_along_x at 0x139da77f0>, description='orient plane normal along x-axis', keymapprovider=<class 'napari.layers.image.image.Image'>, repeatable=False), 'napari:orient_plane_normal_along_view_direction': Action(command=<function orient_plane_normal_along_view_direction at 0x139da75b0>, description='orient plane normal along view direction', keymapprovider=<class 'napari.layers.image.image.Image'>, repeatable=False), 'napari:activate_image_select_mode': Action(command=<function activate_image_select_mode at 0x139da79a0>, description='Transform', keymapprovider=<class 'napari.layers.image.image.Image'>, repeatable=False), 'napari:activate_image_pan_zoom_mode': Action(command=<function activate_image_pan_zoom_mode at 0x139da7a30>, description='Pan/zoom', keymapprovider=<class 'napari.layers.image.image.Image'>, repeatable=False), 'napari:activate_paint_mode': Action(command=<function activate_paint_mode at 0x139d37490>, description='Activate the paint brush', keymapprovider=<class 'napari.layers.labels.labels.Labels'>, repeatable=False), 'napari:activate_fill_mode': Action(command=<function activate_fill_mode at 0x139d375b0>, description='Activate the fill bucket', keymapprovider=<class 'napari.layers.labels.labels.Labels'>, repeatable=False), 'napari:activate_label_pan_zoom_mode': Action(command=<function activate_label_pan_zoom_mode at 0x139d376d0>, description='Pan/zoom mode', keymapprovider=<class 'napari.layers.labels.labels.Labels'>, repeatable=False), 'napari:activate_label_picker_mode': Action(command=<function activate_label_picker_mode at 0x139d377f0>, description='Pick mode', keymapprovider=<class 'napari.layers.labels.labels.Labels'>, repeatable=False), 'napari:activate_label_erase_mode': Action(command=<function activate_label_erase_mode at 0x139d37910>, description='Activate the label eraser', keymapprovider=<class 'napari.layers.labels.labels.Labels'>, repeatable=False), 'napari:new_label': Action(command=<function new_label at 0x139d379a0>, description='Set the currently selected label to the largest used label plus one.', keymapprovider=<class 'napari.layers.labels.labels.Labels'>, repeatable=False), 'napari:decrease_label_id': Action(command=<function decrease_label_id at 0x139d37a30>, description='Decrease the currently selected label by one.', keymapprovider=<class 'napari.layers.labels.labels.Labels'>, repeatable=False), 'napari:increase_label_id': Action(command=<function increase_label_id at 0x139d37ac0>, description='Increase the currently selected label by one.', keymapprovider=<class 'napari.layers.labels.labels.Labels'>, repeatable=False), 'napari:decrease_brush_size': Action(command=<function decrease_brush_size at 0x139d37b50>, description='Decrease the paint brush size by one.', keymapprovider=<class 'napari.layers.labels.labels.Labels'>, repeatable=True), 'napari:increase_brush_size': Action(command=<function increase_brush_size at 0x139d37be0>, description='Increase the paint brush size by one.', keymapprovider=<class 'napari.layers.labels.labels.Labels'>, repeatable=True), 'napari:toggle_preserve_labels': Action(command=<function toggle_preserve_labels at 0x139d37d00>, description='Toggle preserve labels', keymapprovider=<class 'napari.layers.labels.labels.Labels'>, repeatable=False), 'napari:activate_points_add_mode': Action(command=<function activate_points_add_mode at 0x139ef6d40>, description='Add points', keymapprovider=<class 'napari.layers.points.points.Points'>, repeatable=False), 'napari:activate_points_select_mode': Action(command=<function activate_points_select_mode at 0x139ef6dd0>, description='Select points', keymapprovider=<class 'napari.layers.points.points.Points'>, repeatable=False), 'napari:activate_points_pan_zoom_mode': Action(command=<function activate_points_pan_zoom_mode at 0x139ef6ef0>, description='Pan/zoom', keymapprovider=<class 'napari.layers.points.points.Points'>, repeatable=False), 'napari:select_all_in_slice': Action(command=<function select_all_in_slice at 0x139ef7130>, description='Select all points in the current view slice.', keymapprovider=<class 'napari.layers.points.points.Points'>, repeatable=False), 'napari:select_all_data': Action(command=<function select_all_data at 0x139ef71c0>, description='Select all points in the layer.', keymapprovider=<class 'napari.layers.points.points.Points'>, repeatable=False), 'napari:delete_selected_points': Action(command=<function delete_selected_points at 0x139ef70a0>, description='Delete selected points', keymapprovider=<class 'napari.layers.points.points.Points'>, repeatable=False), 'napari:activate_add_rectangle_mode': Action(command=<function activate_add_rectangle_mode at 0x13a58b250>, description='Add rectangles', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:activate_add_ellipse_mode': Action(command=<function activate_add_ellipse_mode at 0x13a58b370>, description='Add ellipses', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:activate_add_line_mode': Action(command=<function activate_add_line_mode at 0x13a58b490>, description='Add lines', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:activate_add_path_mode': Action(command=<function activate_add_path_mode at 0x13a58b5b0>, description='Add path', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:activate_add_polygon_mode': Action(command=<function activate_add_polygon_mode at 0x13a58b6d0>, description='Add polygons', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:activate_direct_mode': Action(command=<function activate_direct_mode at 0x13a58b7f0>, description='Select vertices', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:activate_select_mode': Action(command=<function activate_select_mode at 0x13a58b910>, description='Select shapes', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:activate_shape_pan_zoom_mode': Action(command=<function activate_shape_pan_zoom_mode at 0x13a58ba30>, description='Pan/Zoom', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:activate_vertex_insert_mode': Action(command=<function activate_vertex_insert_mode at 0x13a58bb50>, description='Insert vertex', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:activate_vertex_remove_mode': Action(command=<function activate_vertex_remove_mode at 0x13a58bc70>, description='Remove vertex', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:copy_selected_shapes': Action(command=<function copy_selected_shapes at 0x13a58bd00>, description='Copy any selected shapes', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:paste_shape': Action(command=<function paste_shape at 0x13a58bd90>, description='Paste any copied shapes', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:select_all_shapes': Action(command=<function select_all_shapes at 0x13a58be20>, description='Select all shapes in the current view slice', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:delete_selected_shapes': Action(command=<function delete_selected_shapes at 0x13a58beb0>, description='Delete any selected shapes', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:move_shapes_selection_to_front': Action(command=<function move_shapes_selection_to_front at 0x13a58bf40>, description='Move to front', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:move_shapes_selection_to_back': Action(command=<function move_shapes_selection_to_back at 0x13a598040>, description='Move to back', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:finish_drawing_shape': Action(command=<function finish_drawing_shape at 0x13a5980d0>, description='Finish any drawing, for example when using the path or polygon tool.', keymapprovider=<class 'napari.layers.shapes.shapes.Shapes'>, repeatable=False), 'napari:reset_scroll_progress': Action(command=<function reset_scroll_progress at 0x13a6ed6c0>, description='Reset scroll.', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False), 'napari:toggle_ndisplay': Action(command=<function toggle_ndisplay at 0x13a6ed870>, description='Toggle ndisplay.', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False), 'napari:toggle_theme': Action(command=<function toggle_theme at 0x13a6ed7e0>, description='Toggle theme.', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False), 'napari:reset_view': Action(command=<function reset_view at 0x13a6eda20>, description='Reset view to original state.', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False), 'napari:increment_dims_left': Action(command=<function increment_dims_left at 0x13a6ed990>, description='Increment dimensions slider to the left.', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False), 'napari:increment_dims_right': Action(command=<function increment_dims_right at 0x13a6ed750>, description='Increment dimensions slider to the right.', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False), 'napari:focus_axes_up': Action(command=<function focus_axes_up at 0x13a6ed000>, description='Move focus of dimensions slider up.', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False), 'napari:focus_axes_down': Action(command=<function focus_axes_down at 0x13a6ed900>, description='Move focus of dimensions slider down.', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False), 'napari:roll_axes': Action(command=<function roll_axes at 0x13a6ed510>, description='Change order of the visible axes, e.g. [0, 1, 2] -> [2, 0, 1].', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False), 'napari:transpose_axes': Action(command=<function transpose_axes at 0x13a6ecf70>, description='Transpose order of the last two visible axes, e.g. [0, 1] -> [1, 0].', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False), 'napari:toggle_grid': Action(command=<function toggle_grid at 0x13a6edab0>, description='Toggle grid mode.', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False), 'napari:toggle_selected_visibility': Action(command=<function toggle_selected_visibility at 0x13a6edb40>, description='Toggle visibility of selected layers', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False), 'napari:toggle_console_visibility': Action(command=<function toggle_console_visibility at 0x13a6edbd0>, description='Show/Hide IPython console (only available when napari started as standalone application)', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False), 'napari:show_shortcuts': Action(command=<function show_shortcuts at 0x13abdd750>, description='Show all key bindings', keymapprovider=<class 'napari.components.viewer_model.ViewerModel'>, repeatable=False), 'napari:reset_active_layer_affine': Action(command=<bound method InteractionBoxMouseBindings._reset_active_layer_affine of <napari.components.overlays._interaction_box_mouse_bindings.InteractionBoxMouseBindings object at 0x14a09a110>>, description='Reset the affine transform of the active layer', keymapprovider=Viewer(axes=AxesOverlay(visible=False, opacity=1.0, order=1000000, labels=True, colored=True, dashed=False, arrows=True), camera=Camera(center=(0.0, 127.5, 127.5), zoom=2.62734375, angles=(0.0, 0.0, 90.0), perspective=0.0, interactive=True), cursor=Cursor(position=(30.0, 1.0, 0.0, 0.0), scaled=True, size=1, style=<CursorStyle.STANDARD: 'standard'>), dims=Dims(ndim=4, ndisplay=2, last_used=0, range=((0.0, 60.0, 1.0), (0.0, 2.0, 1.0), (0.0, 256.0, 1.0), (0.0, 256.0, 1.0)), current_step=(30, 1, 128, 128), order=(0, 1, 2, 3), axis_labels=('0', '1', '2', '3')), grid=GridCanvas(stride=1, shape=(-1, -1), enabled=False), layers=[<Image layer 'my_image_data' at 0x14db13a90>], scale_bar=ScaleBarOverlay(visible=False, opacity=1.0, order=1000000, position=<CanvasPosition.BOTTOM_RIGHT: 'bottom_right'>, colored=False, color=<class 'numpy.ndarray'> (4,) float32, ticks=True, font_size=10.0, box=False, box_color=<class 'numpy.ndarray'> (4,) float32, unit=None), text_overlay=TextOverlay(visible=False, opacity=1.0, order=1000000, position=<CanvasPosition.BOTTOM_RIGHT: 'bottom_right'>, color=<class 'numpy.ndarray'> (4,) float32, font_size=10.0, text=''), overlays=Overlays(interaction_box=InteractionBox(points=None, show=False, show_handle=False, show_vertices=False, selection_box_drag=None, selection_box_final=None, transform_start=<napari.utils.transforms.transforms.Affine object at 0x13a86ad40>, transform_drag=<napari.utils.transforms.transforms.Affine object at 0x13a86ada0>, transform_final=<napari.utils.transforms.transforms.Affine object at 0x13a86ae00>, transform=<napari.utils.transforms.transforms.Affine object at 0x13a86ae60>, allow_new_selection=True, selected_vertex=None)), help='', status='Ready', tooltip=Tooltip(visible=True, text=''), theme='dark', title='napari', mouse_over_canvas=False, mouse_move_callbacks=[<function InteractionBoxMouseBindings.initialize_mouse_events.<locals>.mouse_move at 0x14a20f2e0>], mouse_drag_callbacks=[<function InteractionBoxMouseBindings.initialize_mouse_events.<locals>.mouse_drag at 0x14a016c20>], mouse_double_click_callbacks=[], mouse_wheel_callbacks=[<function dims_scroll at 0x13a6bef80>], _persisted_mouse_event={}, _mouse_drag_gen={}, _mouse_wheel_gen={}, keymap={'Shift': <function InteractionBoxMouseBindings.initialize_key_events.<locals>.hold_to_lock_aspect_ratio at 0x14a017490>, 'Control-Shift-R': <function InteractionBoxMouseBindings._reset_active_layer_affine at 0x14a1d5bd0>, 'Control-Shift-A': <function InteractionBoxMouseBindings._transform_active_layer at 0x14a1d5b40>}), repeatable=False), 'napari:transform_active_layer': Action(command=<bound method InteractionBoxMouseBindings._transform_active_layer of <napari.components.overlays._interaction_box_mouse_bindings.InteractionBoxMouseBindings object at 0x14a09a110>>, description='Activate transform mode for the active layer', keymapprovider=Viewer(axes=AxesOverlay(visible=False, opacity=1.0, order=1000000, labels=True, colored=True, dashed=False, arrows=True), camera=Camera(center=(0.0, 127.5, 127.5), zoom=2.62734375, angles=(0.0, 0.0, 90.0), perspective=0.0, interactive=True), cursor=Cursor(position=(30.0, 1.0, 0.0, 0.0), scaled=True, size=1, style=<CursorStyle.STANDARD: 'standard'>), dims=Dims(ndim=4, ndisplay=2, last_used=0, range=((0.0, 60.0, 1.0), (0.0, 2.0, 1.0), (0.0, 256.0, 1.0), (0.0, 256.0, 1.0)), current_step=(30, 1, 128, 128), order=(0, 1, 2, 3), axis_labels=('0', '1', '2', '3')), grid=GridCanvas(stride=1, shape=(-1, -1), enabled=False), layers=[<Image layer 'my_image_data' at 0x14db13a90>], scale_bar=ScaleBarOverlay(visible=False, opacity=1.0, order=1000000, position=<CanvasPosition.BOTTOM_RIGHT: 'bottom_right'>, colored=False, color=<class 'numpy.ndarray'> (4,) float32, ticks=True, font_size=10.0, box=False, box_color=<class 'numpy.ndarray'> (4,) float32, unit=None), text_overlay=TextOverlay(visible=False, opacity=1.0, order=1000000, position=<CanvasPosition.BOTTOM_RIGHT: 'bottom_right'>, color=<class 'numpy.ndarray'> (4,) float32, font_size=10.0, text=''), overlays=Overlays(interaction_box=InteractionBox(points=None, show=False, show_handle=False, show_vertices=False, selection_box_drag=None, selection_box_final=None, transform_start=<napari.utils.transforms.transforms.Affine object at 0x13a86ad40>, transform_drag=<napari.utils.transforms.transforms.Affine object at 0x13a86ada0>, transform_final=<napari.utils.transforms.transforms.Affine object at 0x13a86ae00>, transform=<napari.utils.transforms.transforms.Affine object at 0x13a86ae60>, allow_new_selection=True, selected_vertex=None)), help='', status='Ready', tooltip=Tooltip(visible=True, text=''), theme='dark', title='napari', mouse_over_canvas=False, mouse_move_callbacks=[<function InteractionBoxMouseBindings.initialize_mouse_events.<locals>.mouse_move at 0x14a20f2e0>], mouse_drag_callbacks=[<function InteractionBoxMouseBindings.initialize_mouse_events.<locals>.mouse_drag at 0x14a016c20>], mouse_double_click_callbacks=[], mouse_wheel_callbacks=[<function dims_scroll at 0x13a6bef80>], _persisted_mouse_event={}, _mouse_drag_gen={}, _mouse_wheel_gen={}, keymap={'Shift': <function InteractionBoxMouseBindings.initialize_key_events.<locals>.hold_to_lock_aspect_ratio at 0x14a017490>, 'Control-Shift-R': <function InteractionBoxMouseBindings._reset_active_layer_affine at 0x14a1d5bd0>, 'Control-Shift-A': <function InteractionBoxMouseBindings._transform_active_layer at 0x14a1d5b40>}), repeatable=False)}
        name = 'napari:toggle_console_visibility'
        self = <napari.utils.action_manager.ActionManager object at 0x129e66e00>

File ~/Dev/miniforge3/envs/napari-dev/lib/python3.10/site-packages/in_n_out/_store.py:781, in Store.inject.<locals>._inner.<locals>._exec(*args=(), **kwargs={})
    775 except TypeError as e:
    776     # likely a required argument is still missing.
    777     # show what was injected and raise
    778     _argnames = (
    779         f"arguments: {set(_kwargs)!r}" if _kwargs else "NO arguments"
    780     )
--> 781     raise TypeError(
    782         f"After injecting dependencies for {_argnames}, {e}"
    783     ) from e
    785 return result

TypeError: After injecting dependencies for arguments: {'viewer'}, _not_napari() takes 1 positional argument but 2 were given
codecov[bot] commented 1 year ago

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 90.38%. Comparing base (29b68a3) to head (94e8cbc). Report is 1 commits behind head on main.

Additional details and impacted files ```diff @@ Coverage Diff @@ ## main #18 +/- ## ========================================== + Coverage 87.60% 90.38% +2.78% ========================================== Files 4 4 Lines 121 156 +35 ========================================== + Hits 106 141 +35 Misses 15 15 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

psobolewskiPhD commented 1 year ago

I installed this PR branch and napari main. No more errors, but I must be missing something, because, I made a script:

import napari
a = 3
b = 4
viewer = napari.Viewer()
c = 5
napari.run()

I was expecting c to be available in the napari console, but it's not. 🤔

Carreau commented 1 year ago

as we inspect frames my guess is we see the frame at the point the viewer was created so before C ?

On Wed, Dec 21, 2022 at 21:48 Peter Sobolewski @.***> wrote:

I installed this PR branch and napari main. No more errors, but I must be missing something, because, I made a script:

import napari

a = 3

b = 4

viewer = napari.Viewer()

c = 5

napari.run()

I was expecting c to be available in the napari console, but it's not. 🤔

— Reply to this email directly, view it on GitHub https://github.com/napari/napari-console/pull/18#issuecomment-1362075315, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACR5TZJI3BOYHNNT6OGJLLWONULLANCNFSM5QDP6SXA . You are receiving this because you were mentioned.Message ID: @.***>

psobolewskiPhD commented 1 year ago

Ah, I assumed it would be stuff after the viewer. But checking again, a and b arn't available either. ☚ī¸

Carreau commented 1 year ago

Yeah, I forgot to push...

Carreau commented 1 year ago

Or technically I pushed, but did not commit, so it pushed nothing.

brisvag commented 1 year ago

I was expecting c to be available in the napari console, but it's not.

that would be great too, but probably more complicated cause it requires back and forth communication?

psobolewskiPhD commented 1 year ago

Or technically I pushed, but did not commit, so it pushed nothing.

Ah! I'm so pleased it wasn't me screwing something up again! 😊 Anyhow, refreshing the branch and it works perfectly:

a
Out[1]: 3

b
Out[2]: 4

c
Out[3]: 5
psobolewskiPhD commented 1 year ago

@czaki can you elaborate what is the issue with your PartSeg use case? Does it maybe make sense to have the console disabled in that case?

Czaki commented 1 year ago

@Czaki can you elaborate what is the issue with your PartSeg use case?

exporting internal application variables to the console. (It could be easily workaround - for a person like me who understands napari internals)

Does it maybe make sense to have the console disabled in that case?

I use it regularly in the context of debugging and development. And some person uses it to perform actions not possible by GUI (ex layer scale).

psobolewskiPhD commented 1 year ago

Gotcha. Makes sense. Maybe we need an argument to not do this when launching napari? Even from a script someone may not want the variables passed?

jni commented 1 year ago

Can we try to push this over the line? Todos:

For point 2, I wouldn't mind leaving it to a later PR, as this is already extremely useful.

Czaki commented 1 year ago

I like option 1.

I'm not the favorite of option 2. It should be rather information in the documentation that will inform about this and suggest ways to workaround it. (maybe such variable _not_incude_in_napari_console=True).

Carreau commented 11 months ago

I updated this PR.

max_depth , I changed to min_depth, I'm guessing what you want is to be able to define def my_lancher(...), and capture the context of my_launcher ? In general frames depth here are going to be weird, as there is a lot of internal napari frames.

And in general I would suggest people embedding this to just subclass and overwrite the frame predicate to know when to stop. Like setting a predcate that just always reuturn True # in napari, will thus not embed any namespace.

I added the . at the end of module names.

jni commented 11 months ago

How are you running the viewer? When I run it with examples/add_labels.py, I get the following (in addition to some private ones):

 'data': <module 'skimage.data' from '/Users/jni/projects/scikit-image/skimage/data/__init__.py'>,
 'threshold_otsu': <function skimage.filters.thresholding.threshold_otsu(image=None, nbins=256, *, hist=None)>,
 'label': <function skimage.measure._label.label(label_image, background=None, return_num=False, connectivity=None)>,
 'closing': <function skimage.morphology.gray.closing(image, footprint=None, out=None)>,
 'remove_small_objects': <function skimage.morphology.misc.remove_small_objects(ar, min_size=64, connectivity=1, *, out=None)>,
 'square': <function skimage.morphology.footprints.square(width, dtype=<class 'numpy.uint8'>, *, decomposition=None)>,
 'clear_border': <function skimage.segmentation._clear_border.clear_border(labels, buffer_size=0, bgval=0, mask=None, *, out=None)>,
 'napari': <module 'napari' from '/Users/jni/projects/napari/napari/__init__.py'>,
 'image': array([[158, 141, 160, ..., 142, 199, 182],
        [162, 153, 168, ..., 193, 203, 136],
        [178, 173, 173, ..., 143, 179, 120],
        ...,
        [ 98, 117, 143, ...,  64,  65,  66],
        [128, 125, 153, ...,  63,  64,  63],
        [ 99, 102, 132, ...,  66,  62,  59]], dtype=uint8),
 'thresh': 112,
 'bw': array([[ True,  True,  True, ...,  True,  True,  True],
        [ True,  True,  True, ...,  True,  True,  True],
        [ True,  True,  True, ...,  True,  True,  True],
        ...,
        [ True,  True,  True, ..., False, False, False],
        [ True,  True,  True, ..., False, False, False],
        [False, False,  True, ..., False, False, False]]),
 'cleared': array([[False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False],
        ...,
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False],
        [False, False, False, ..., False, False, False]]),
 'label_image': array([[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]], dtype=int32),
 'viewer': Viewer(camera=Camera(center=(0.0, 101.0, 141.5), zoom=2.0461631546384247, angles=(0.0, 0.0, 90.0), perspective=0.0, mouse_pan=True, mouse_zoom=True), cursor=Cursor(position=(185.75141043603875, 0.3028212509458994), scaled=True, style=<CursorStyle.STANDARD: 'standard'>, size=10.0), dims=Dims(ndim=2, ndisplay=2, order=(0, 1), axis_labels=('0', '1'), range=(RangeTuple(start=0.0, stop=202.0, step=1.0), RangeTuple(start=0.0, stop=283.0, step=1.0)), margin_left=(0.0, 0.0), margin_right=(0.0, 0.0), point=(101.0, 141.0), last_used=0), grid=GridCanvas(stride=1, shape=(-1, -1), enabled=False), layers=[<Image layer 'coins' at 0x2a53c0760>, <Labels layer 'segmentation' at 0x107558f70>], help='use <7> for transform, use <1> for activate the label eraser, use <2> for activate the paint brush, use <3> for activate the polygon tool, use <4> for activate the fill bucket, use <5> for pick mode', status='', tooltip=Tooltip(visible=False, text=''), theme='dark', title='napari', mouse_over_canvas=False, mouse_move_callbacks=[], mouse_drag_callbacks=[], mouse_double_click_callbacks=[], mouse_wheel_callbacks=[<function dims_scroll at 0x17196a200>], _persisted_mouse_event={}, _mouse_drag_gen={}, _mouse_wheel_gen={}, keymap={}),
 'label_layer': <Labels layer 'segmentation' at 0x107558f70>,
 'action_manager': <napari.utils.action_manager.ActionManager at 0x1695d6170>,

The only dodgy one is action_manager at the bottom, dunno what's going on there... 😅 But everything else is as expected. (Edit: and awesome!) If you run it as main, then I wouldn't expect this PR to be doing much useful...

Czaki commented 11 months ago

I run it with napariin shell

jni commented 11 months ago

I run it with napari in shell

I mean, in that situation I wouldn't expect this PR to be useful for anything, but I guess you're saying it should not in that case pollute the namespace? That probably requires a fix in our main function, or wherever we instantiate the console, rather than here?

Czaki commented 11 months ago

guess you're saying it should not in that case pollute the namespace

yes

Czaki commented 11 months ago

Ok. I push commit with the proposition of a solution.

There are two sources of problems. The first is caused by CallerFrame do not stop even on __main__.py (and then run.py). Ic could also be fixed on Napari itself, but we do not want this PR on the napari release.

Also the name of napari/__main__.py is __main__, not napari.__main__

I also allow disabling of collecting variables by creating the global variable NAPARI_EMBED. What did you think about this? If it is a good solution then it will need to update the readme.

jni commented 11 months ago

I'm totally fine with your proposal @Czaki

jni commented 11 months ago

If you yourself are happy with your proposal can you dismiss your review and we can merge? 😃

Czaki commented 11 months ago

@jni I have added tests and updated the readme. Could you check it?

jni commented 11 months ago

Well the tests seem unhappy... 😅

Czaki commented 11 months ago

Well the tests seem unhappy... 😅

I forget that there is a global state in the console, so names in tests cannot collide.

jni commented 11 months ago

I updated the README message to include a usage example.

jni commented 11 months ago

The only dodgy one is action_manager at the bottom, dunno what's going on there...

(from me at https://github.com/napari/napari-console/pull/18#issuecomment-1758781001)

@Carreau it looks like that was added by you in napari/napari#2677 without any discussion. I see now in the PR description your comment, "I also inject action_manager in the qtconsole context, so one does not have to import it everytime", but in general I think the action manager is not really a user-facing object, even though it is convenient for people developing specific things. Was that for debugging and can it be removed? (This would be for 0.5.0 or later in any case...)

Carreau commented 10 months ago

I see now in the PR description your comment, "I also inject action_manager in the qtconsole context, so one does not have to import it everytime",

I belive this was done before we had the shortcut editor so we needed a way to edit shortcuts is a relatively friendly way.

jni commented 9 months ago

Well, I suggest that we merge this and fix the action_manager injection in another PR.

jni commented 9 months ago

(It would be cool to simul-release this improvement with 0.4.19!)

jni commented 18 hours ago

Shit, I should have tested this again before merging 😅

Right now when I run pythonexamples/add_labels.py, I see the viewer, np, napari, and some IPython stuff:

In [1]: locals()
Out[1]: 
{'__name__': '__main__',
 '__doc__': 'Automatically created module for IPython interactive environment',
 '__package__': None,
 '__loader__': None,
 '__spec__': None,
 '__builtin__': <module 'builtins' (built-in)>,
 '__builtins__': <module 'builtins' (built-in)>,
 '_ih': ['', 'locals()'],
 '_oh': {},
 '_dh': [PosixPath('/Users/jni/projects/napari')],
 'In': ['', 'locals()'],
 'Out': {},
 'get_ipython': <bound method InteractiveShell.get_ipython of <ipykernel.inprocess.ipkernel.InProcessInteractiveShell object at 0x301fd1310>>,
 'exit': <IPython.core.autocall.ZMQExitAutocall at 0x3030068d0>,
 'quit': <IPython.core.autocall.ZMQExitAutocall at 0x3030068d0>,
 'open': <function io.open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)>,
 '_': '',
 '__': '',
 '___': '',
 'viewer': Viewer(camera=Camera(center=(0.0, np.float64(101.0), np.float64(141.5)), zoom=np.float64(1.7528169014084505), angles=(0.0, 0.0, 90.0), perspective=0.0, mouse_pan=True, mouse_zoom=True), cursor=Cursor(position=(1.0, 1.0), scaled=True, style=<CursorStyle.STANDARD: 'standard'>, size=np.float64(10.0)), dims=Dims(ndim=2, ndisplay=2, order=(0, 1), axis_labels=('0', '1'), rollable=(True, True), range=(RangeTuple(start=np.float64(0.0), stop=np.float64(202.0), step=np.float64(1.0)), RangeTuple(start=np.float64(0.0), stop=np.float64(283.0), step=np.float64(1.0))), margin_left=(0.0, 0.0), margin_right=(0.0, 0.0), point=(np.float64(101.0), np.float64(141.0)), last_used=0), grid=GridCanvas(stride=1, shape=(-1, -1), enabled=False), layers=[<Image layer 'coins' at 0x1750fa190>, <Labels layer 'segmentation' at 0x1750f9a50>], help='use <7> for transform, use <1> for activate the label eraser, use <2> for activate the paint brush, use <3> for activate the polygon tool, use <4> for activate the fill bucket, use <5> for pick mode', status='Ready', tooltip=Tooltip(visible=False, text=''), theme='dark', title='napari', mouse_over_canvas=False, mouse_move_callbacks=[], mouse_drag_callbacks=[], mouse_double_click_callbacks=[], mouse_wheel_callbacks=[<function dims_scroll at 0x1647e4040>], _persisted_mouse_event={}, _mouse_drag_gen={}, _mouse_wheel_gen={}, _keymap={}),
 'napari': <module 'napari' from '/Users/jni/projects/napari/napari/__init__.py'>,
 'action_manager': <napari.utils.action_manager.ActionManager at 0x15e051910>,
 'np': <module 'numpy' from '/Users/jni/micromamba/envs/all/lib/python3.11/site-packages/numpy/__init__.py'>,
 '_i': '',
 '_ii': '',
 '_iii': '',
 '_i1': 'locals()'}

I wonder if this commit broke things?

5339095

jni commented 18 hours ago

Indeed changing this:

if c.frame.f_globals.get("__name__", "") != "__main__" and "NAPARI_EMBED" not in c.frame.f_globals:

to

if "NAPARI_EMBED" not in c.frame.f_globals:

fixes it again. I'll make a PR, since I don't think the first part is necessary (?) @Czaki