Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MRG: Prototype of notebook viz (ipyvtk) #8503

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
50789de
Deploy basic version
GuillaumeFavelier Nov 10, 2020
cfd0678
Merge branch 'master' into proto/ipyvtk_backend
GuillaumeFavelier Nov 13, 2020
d7d2c5b
Merge branch 'master' into proto/ipyvtk_backend
GuillaumeFavelier Dec 16, 2020
4d9b63a
Update prototype [skip ci]
GuillaumeFavelier Dec 16, 2020
dcaff44
Update _Renderer [skip ci]
GuillaumeFavelier Dec 16, 2020
ec0d265
Update picking [skip ci]
GuillaumeFavelier Dec 16, 2020
9a22fb5
Fix disp
GuillaumeFavelier Dec 16, 2020
c254d79
Add shortcuts [skip ci]
GuillaumeFavelier Dec 17, 2020
a3fb818
Update tests
GuillaumeFavelier Dec 17, 2020
909909c
Add ipyvtk_simple
GuillaumeFavelier Dec 17, 2020
f317572
Fix style
GuillaumeFavelier Dec 17, 2020
08fdd85
Update tests
GuillaumeFavelier Dec 17, 2020
040bc3d
Refactor shortcuts
GuillaumeFavelier Dec 17, 2020
4cac2a0
Improve tests
GuillaumeFavelier Dec 17, 2020
a789b46
Remove cruft
GuillaumeFavelier Dec 17, 2020
fd3183b
Remove cruft
GuillaumeFavelier Dec 17, 2020
fd5c6d2
Reorder figures
GuillaumeFavelier Dec 17, 2020
ea8396e
Add tool bar
GuillaumeFavelier Dec 17, 2020
24852c4
Merge branch 'master' into proto/ipyvtk_backend
GuillaumeFavelier Dec 17, 2020
9a3d0fc
Reorder figures
GuillaumeFavelier Dec 17, 2020
137b7b1
Fix visibility
GuillaumeFavelier Dec 17, 2020
403176f
Merge branch 'master' into proto/ipyvtk_backend
GuillaumeFavelier Dec 18, 2020
3c31605
Improve coverage
GuillaumeFavelier Dec 18, 2020
a23e3c6
Fix ratio and layout
GuillaumeFavelier Dec 18, 2020
1cc4757
Merge branch 'master' into proto/ipyvtk_backend
GuillaumeFavelier Jan 4, 2021
b16738c
Update changes
GuillaumeFavelier Jan 4, 2021
195c91b
Do not import pyplot
GuillaumeFavelier Jan 5, 2021
ed680d3
Update mne/viz/_brain/_brain.py
GuillaumeFavelier Jan 5, 2021
57b505f
Use _add_action
GuillaumeFavelier Jan 6, 2021
4059082
Use icon + tooltip
GuillaumeFavelier Jan 6, 2021
af7f988
Fix qt func
GuillaumeFavelier Jan 6, 2021
b4b49f0
Merge branch 'master' into proto/ipyvtk_backend
GuillaumeFavelier Jan 6, 2021
a5e44e7
Reorder actions
GuillaumeFavelier Jan 6, 2021
0dcc7f6
Switch to icon + description
GuillaumeFavelier Jan 6, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 48 additions & 40 deletions mne/viz/_brain/_brain.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,10 +424,13 @@ def __init__(self, subject_id, hemi, surf, title=None,
shape=shape,
fig=figure)

if _get_3d_backend() == "pyvista":
self.plotter = None
self.window = None
if _get_3d_backend() in ("pyvista", "notebook"):
self.plotter = self._renderer.plotter
self.window = self.plotter.app_window
self.window.signal_close.connect(self._clean)
if hasattr(self.plotter, "app_window"):
self.window = self.plotter.app_window
self.window.signal_close.connect(self._clean)

for h in self._hemis:
# Initialize a Surface object as the geometry
Expand Down Expand Up @@ -494,11 +497,6 @@ def setup_time_viewer(self, time_viewer=True, show_traces=True):
self.orientation = list(_lh_views_dict.keys())
self.default_smoothing_range = [0, 15]

# setup notebook
if self.notebook:
self._configure_notebook()
return

# Default configuration
self.playback = False
self.visibility = False
Expand Down Expand Up @@ -527,10 +525,16 @@ def setup_time_viewer(self, time_viewer=True, show_traces=True):

# Direct access parameters:
self._iren = self._renderer.plotter.iren
self.main_menu = self.plotter.main_menu
self.tool_bar = self.window.addToolBar("toolbar")
self.status_bar = self.window.statusBar()
self.interactor = self.plotter.interactor
if self.notebook:
self.main_menu = None
self.tool_bar = None
self.status_bar = None
self.interactor = None
else:
self.main_menu = self.plotter.main_menu
self.tool_bar = self.window.addToolBar("toolbar")
self.status_bar = self.window.statusBar()
self.interactor = self.plotter.interactor

# Derived parameters:
self.playback_speed = self.default_playback_speed_value
Expand Down Expand Up @@ -559,16 +563,20 @@ def setup_time_viewer(self, time_viewer=True, show_traces=True):
self._configure_time_label()
self._configure_sliders()
self._configure_scalar_bar()
self._configure_playback()
self._configure_point_picking()
self._configure_menu()
self._configure_tool_bar()
self._configure_status_bar()
if not self.notebook:
self._configure_playback()
self._configure_menu()
self._configure_tool_bar()
self._configure_status_bar()

# show everything at the end
self.toggle_interface()
with self.ensure_minimum_sizes():
if self.notebook:
self.show()
else:
with self.ensure_minimum_sizes():
self.show()

@safe_event
def _clean(self):
Expand Down Expand Up @@ -646,10 +654,13 @@ def toggle_interface(self, value=None):
self.visibility = value

# update tool bar icon
if self.visibility:
self.actions["visibility"].setIcon(self.icons["visibility_on"])
else:
self.actions["visibility"].setIcon(self.icons["visibility_off"])
if not self.notebook:
if self.visibility:
self.actions["visibility"].setIcon(
self.icons["visibility_on"])
else:
self.actions["visibility"].setIcon(
self.icons["visibility_off"])

# manage sliders
for slider in self.plotter.slider_widgets:
Expand Down Expand Up @@ -779,10 +790,6 @@ def _set_slider_style(self):
)
slider_rep.GetCapProperty().SetOpacity(0)

def _configure_notebook(self):
from ._notebook import _NotebookInteractor
self._renderer.figure.display = _NotebookInteractor(self)

def _configure_time_label(self):
self.time_actor = self._data.get('time_actor')
if self.time_actor is not None:
Expand Down Expand Up @@ -972,19 +979,23 @@ def _configure_point_picking(self):
from ..backends._pyvista import _update_picking_callback
# use a matplotlib canvas
self.color_cycle = _ReuseCycle(_get_color_list())
win = self.plotter.app_window
dpi = win.windowHandle().screen().logicalDotsPerInch()
ratio = (1 - self.interactor_fraction) / self.interactor_fraction
w = self.interactor.geometry().width()
h = self.interactor.geometry().height() / ratio
if self.notebook:
dpi = 90
w, h = self.plotter.window_size
else:
dpi = self.window.windowHandle().screen().logicalDotsPerInch()
ratio = (1 - self.interactor_fraction) / self.interactor_fraction
w = self.interactor.geometry().width()
h = self.interactor.geometry().height() / ratio
# Get the fractional components for the brain and mpl
self.mpl_canvas = MplCanvas(self, w / dpi, h / dpi, dpi)
self.mpl_canvas = MplCanvas(self, w / dpi, h / dpi, dpi,
self.notebook)
xlim = [np.min(self._data['time']),
np.max(self._data['time'])]
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=UserWarning)
self.mpl_canvas.axes.set(xlim=xlim)
if not self.separate_canvas:
if not self.notebook and not self.separate_canvas:
from PyQt5.QtWidgets import QSplitter
from PyQt5.QtCore import Qt
canvas = self.mpl_canvas.canvas
Expand Down Expand Up @@ -2261,7 +2272,11 @@ def close(self):

def show(self):
"""Display the window."""
self._renderer.show()
# Request rendering of the window
try:
return self._renderer.show()
except RuntimeError:
logger.info("No active/running renderer available.")

def show_view(self, view=None, roll=None, distance=None, row=0, col=0,
hemi=None):
Expand Down Expand Up @@ -2928,13 +2943,6 @@ def _iter_time(self, time_idx, callback):
# Restore original time index
func(current_time_idx)

def _show(self):
"""Request rendering of the window."""
try:
return self._renderer.show()
except RuntimeError:
logger.info("No active/running renderer available.")

def _check_stc(self, hemi, array, vertices):
from ...source_estimate import (
_BaseSourceEstimate, _BaseSurfaceSourceEstimate,
Expand Down
67 changes: 0 additions & 67 deletions mne/viz/_brain/_notebook.py

This file was deleted.

26 changes: 16 additions & 10 deletions mne/viz/_brain/mplcanvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
class MplCanvas(object):
"""Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.)."""

def __init__(self, brain, width, height, dpi):
def __init__(self, brain, width, height, dpi, notebook=False):
from PyQt5 import QtWidgets
import matplotlib.pyplot as plt
GuillaumeFavelier marked this conversation as resolved.
Show resolved Hide resolved
from matplotlib import rc_context
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
Expand All @@ -30,16 +31,21 @@ def __init__(self, brain, width, height, dpi):
pass
with context:
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.canvas = FigureCanvasQTAgg(self.fig)
self.axes = self.fig.add_subplot(111)
self.notebook = notebook
if self.notebook:
self.fig, self.axes = plt.subplots()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you set the figsize and dpi and such it will even be the right size! Not 100% sure how you get it to show up below the brain plot, probably has to do with when each is instantiated which seems like it could be a pain to manage

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know where to get correct values for dpi. We were using Qt for that before

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would try just setting it to 100. I don't know if it will matter. If it does we can figure out the right thing to do. One reason you need it is because there figsize is in inches, so you need to know the DPI to get from the pixel width to the figsize.

self.canvas = plt.gcf().canvas
else:
self.canvas = FigureCanvasQTAgg(self.fig)
self.axes = self.fig.add_subplot(111)
self.canvas.setParent(parent)
FigureCanvasQTAgg.setSizePolicy(
self.canvas,
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding
)
FigureCanvasQTAgg.updateGeometry(self.canvas)
self.axes.set(xlabel='Time (sec)', ylabel='Activation (AU)')
self.canvas.setParent(parent)
FigureCanvasQTAgg.setSizePolicy(
self.canvas,
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding
)
FigureCanvasQTAgg.updateGeometry(self.canvas)
self.brain = brain
self.time_func = brain.callbacks["time"]
for event in ('button_press', 'motion_notify') + extra_events:
Expand Down
Loading