Skip to content

Commit

Permalink
ENH: Add splash for coreg gui (#10412)
Browse files Browse the repository at this point in the history
* ENH: Add splash for coreg gui

* FIX: Splash style

* FIX: Fix for Qt dep

* FIX: Commit

* FIX: Better splash
  • Loading branch information
larsoner authored Mar 5, 2022
1 parent fec2972 commit a93c198
Show file tree
Hide file tree
Showing 11 changed files with 3,047 additions and 1,149 deletions.
29 changes: 22 additions & 7 deletions logo/generate_mne_logos.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from scipy.stats import multivariate_normal
from matplotlib.path import Path
from matplotlib.text import TextPath
from matplotlib.patches import PathPatch, Ellipse
from matplotlib.patches import PathPatch, Ellipse, FancyBboxPatch
from matplotlib.colors import LinearSegmentedColormap

# manually set values
Expand Down Expand Up @@ -71,8 +71,8 @@
mne_field_line_cols = LinearSegmentedColormap('mne_line', redbl)

# plot gradient and contour lines
im = plt.imshow(Z, cmap=mne_field_grad_cols, aspect='equal')
cs = plt.contour(Z, 9, cmap=mne_field_line_cols, linewidths=1)
im = ax.imshow(Z, cmap=mne_field_grad_cols, aspect='equal', zorder=1)
cs = ax.contour(Z, 9, cmap=mne_field_line_cols, linewidths=1, zorder=1)
plot_dims = np.r_[np.diff(ax.get_xbound()), np.diff(ax.get_ybound())]

# create MNE clipping mask
Expand Down Expand Up @@ -114,17 +114,32 @@
assert op.isdir(static_dir)
plt.savefig(op.join(static_dir, 'mne_logo.svg'), transparent=True)

# modify to make an icone
# modify to make the splash screen
data_dir = op.join(op.dirname(__file__), '..', 'mne', 'icons')
ax.patches.pop(-1) # no tag line for our icon
ax.collections[:] = []
ax.patches[-1].set_facecolor('w')
for coll in list(ax.collections):
coll.remove()
bounds = np.array([
[mne_path.vertices[:, ii].min(), mne_path.vertices[:, ii].max()]
for ii in range(2)])
bounds *= (plot_dims / dims)
xy = np.mean(bounds, axis=1) - [100, 0]
r = np.diff(bounds, axis=1).max() * 1.2
ax.add_patch(Ellipse(xy, r, r, clip_on=False, zorder=-1, fc='k'))
w, h = r, r * (2 / 3)
box_xy = [xy[0] - w * 0.5, xy[1] - h * (2 / 5)]
ax.set_ylim(box_xy[1] + h * 1.001, box_xy[1] - h * 0.001)
patch = FancyBboxPatch(
box_xy, w, h, clip_on=False, zorder=-1, fc='k', ec='none', alpha=0.75,
boxstyle="round,rounding_size=200.0", mutation_aspect=1)
ax.add_patch(patch)
fig.set_size_inches((512 / dpi, 512 * (h / w) / dpi))
plt.savefig(op.join(data_dir, 'mne-splash.png'), transparent=True)
patch.remove()

# modify to make an icon
ax.patches.pop(-1) # no tag line for our icon
patch = Ellipse(xy, r, r, clip_on=False, zorder=-1, fc='k')
ax.add_patch(patch)
ax.set_ylim(xy[1] + r / 1.9, xy[1] - r / 1.9)
fig.set_size_inches((256 / dpi, 256 / dpi))
# Qt does not support clip paths in SVG rendering so we have to use PNG here
Expand Down
4 changes: 3 additions & 1 deletion mne/gui/_coreg.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,10 @@ def _get_default(var, val):
subject = _get_default(subject, self._get_subjects(subjects_dir)[0])

# setup the window
splash = 'Initializing coregistration GUI...' if show else False
self._renderer = _get_renderer(
size=self._defaults["size"], bgcolor=self._defaults["bgcolor"])
size=self._defaults["size"], bgcolor=self._defaults["bgcolor"],
splash=splash)
self._renderer._window_close_connect(self._clean)
self._renderer._window_close_connect(self._close_callback, after=False)
self._renderer.set_interaction(interaction)
Expand Down
Binary file added mne/icons/mne-splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions mne/icons/mne.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
<file alias="movie.svg">movie-black-18dp.svg</file>
<file alias="mne-icon.png">mne-circle-black.png</file>
<file alias="mne-bigsur-icon.png">mne-bigsur-white.png</file>
<file alias="mne-splash.png">mne-splash.png</file>
</qresource>
</RCC>
4,077 changes: 2,950 additions & 1,127 deletions mne/icons/resources.py

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions mne/viz/_figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .. import verbose, get_config, set_config
from ..annotations import _sync_onset
from ..defaults import _handle_default
from ..fixes import _get_args
from ..utils import logger, _validate_type, _check_option
from ..io.pick import _DATA_CH_TYPES_SPLIT
from .backends._utils import VALID_BROWSE_BACKENDS
Expand Down Expand Up @@ -648,6 +649,8 @@ def _get_browser(show, block, **kwargs):
return fig

# Initialize Browser
if 'splash' in _get_args(backend._init_browser):
kwargs['splash'] = True if show else False
fig = backend._init_browser(**kwargs)
_show_browser(show=show, block=block, fig=fig)

Expand Down
5 changes: 4 additions & 1 deletion mne/viz/_mpl_figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -2220,11 +2220,14 @@ def _patched_canvas(fig):
fig.canvas = old_canvas


def _init_browser(**kwargs):
def _init_browser(splash=False, **kwargs):
"""Instantiate a new MNE browse-style figure."""
from mne.io import BaseRaw
fig = _figure(toolbar=False, FigureClass=MNEBrowseFigure, **kwargs)

# splash is ignored (maybe we could do it for mpl if we get_backend() and
# check if it's Qt... but seems overkill)

# initialize zen mode
# (can't do in __init__ due to get_position() calls)
fig.canvas.draw()
Expand Down
4 changes: 2 additions & 2 deletions mne/viz/backends/_abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class _AbstractRenderer(ABC):

@abstractclassmethod
def __init__(self, fig=None, size=(600, 600), bgcolor=(0., 0., 0.),
name=None, show=False, shape=(1, 1)):
name=None, show=False, shape=(1, 1), splash=False):
"""Set up the scene."""
pass

Expand Down Expand Up @@ -919,7 +919,7 @@ class Figure3D(ABC):

@abstractclassmethod
def _init(self, fig=None, size=(600, 600), bgcolor=(0., 0., 0.),
name=None, show=False, shape=(1, 1)):
name=None, show=False, shape=(1, 1), splash=False):
pass

@property
Expand Down
20 changes: 15 additions & 5 deletions mne/viz/backends/_pyvista.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,15 @@ def __init__(self):

def _init(self, plotter=None, show=False, title='PyVista Scene',
size=(600, 600), shape=(1, 1), background_color='black',
smooth_shading=True, off_screen=False, notebook=False):
smooth_shading=True, off_screen=False, notebook=False,
splash=False):
self._plotter = plotter
self.display = None
self.background_color = background_color
self.smooth_shading = smooth_shading
self.notebook = notebook
self.title = title
self.splash = splash

self.store = dict()
self.store['window_size'] = size
Expand Down Expand Up @@ -90,8 +93,15 @@ def _build(self):

if self.plotter is None:
if not self.notebook:
app = _init_mne_qtapp(enable_icon=hasattr(plotter_class,
'set_icon'))
out = _init_mne_qtapp(
enable_icon=hasattr(plotter_class, 'set_icon'),
splash=self.splash)
# replace it with the Qt object
if self.splash:
self.splash = out[1]
app = out[0]
else:
app = out
self.store['app'] = app
plotter = plotter_class(**self.store)
plotter.background_color = self.background_color
Expand Down Expand Up @@ -145,14 +155,14 @@ class _PyVistaRenderer(_AbstractRenderer):

def __init__(self, fig=None, size=(600, 600), bgcolor='black',
name="PyVista Scene", show=False, shape=(1, 1),
notebook=None, smooth_shading=True):
notebook=None, smooth_shading=True, splash=False):
from .._3d import _get_3d_option
_require_version('pyvista', 'use 3D rendering', '0.32')
figure = PyVistaFigure()
figure._init(
show=show, title=name, size=size, shape=shape,
background_color=bgcolor, notebook=notebook,
smooth_shading=smooth_shading)
smooth_shading=smooth_shading, splash=splash)
self.font_family = "arial"
self.tube_n_sides = 20
self.antialias = _get_3d_option('antialias')
Expand Down
12 changes: 11 additions & 1 deletion mne/viz/backends/_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
_AbstractWindow, _AbstractMplCanvas, _AbstractPlayback,
_AbstractBrainMplCanvas, _AbstractMplInterface,
_AbstractWidgetList, _AbstractAction, _AbstractDialog)
from ._utils import _init_qt_resources, _qt_disable_paint
from ._utils import _init_qt_resources, _qt_disable_paint, _qt_raise_window
from ..utils import logger, _check_option, safe_event


Expand Down Expand Up @@ -835,7 +835,17 @@ def show(self):
for plotter in self._all_plotters:
plotter.updateGeometry()
plotter._render()
# Ideally we would just put a `splash.finish(plotter.window())` in the
# same place that we initialize this (_init_qt_app call). However,
# the window show event is triggered (closing the splash screen) well
# before the window actually appears for complex scenes like the coreg
# GUI. Therefore, we close after all these events have been processed
# here.
self._process_events()
splash = getattr(self.figure, 'splash', False)
if splash:
splash.close()
_qt_raise_window(self.plotter.app_window)


def _set_widget_tooltip(widget, tooltip):
Expand Down
41 changes: 36 additions & 5 deletions mne/viz/backends/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def _qt_disable_paint(widget):
widget.paintEvent = paintEvent


def _init_mne_qtapp(enable_icon=True, pg_app=False):
def _init_mne_qtapp(enable_icon=True, pg_app=False, splash=False):
"""Get QApplication-instance for MNE-Python.
Parameter
Expand All @@ -109,14 +109,21 @@ def _init_mne_qtapp(enable_icon=True, pg_app=False):
If to create the QApplication with pyqtgraph. For an until know
undiscovered reason the pyqtgraph-browser won't show without
mkQApp from pyqtgraph.
splash : bool | str
If not False, display a splash screen. If str, set the message
to the given string.
Returns
-------
app: ``PyQt5.QtWidgets.QApplication``
app : ``PyQt5.QtWidgets.QApplication``
Instance of QApplication.
splash : ``PyQt5.QtWidgets.QSplashScreen``
Instance of QSplashScreen. Only returned if splash is True or a
string.
"""
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtWidgets import QApplication, QSplashScreen

app_name = 'MNE-Python'
organization_name = 'MNE'
Expand Down Expand Up @@ -150,7 +157,19 @@ def _init_mne_qtapp(enable_icon=True, pg_app=False):
kind = 'bigsur-' if platform.mac_ver()[0] >= '10.16' else ''
app.setWindowIcon(QIcon(f":/mne-{kind}icon.png"))

return app
out = app
if splash:
qsplash = QSplashScreen(
QPixmap(':/mne-splash.png'), Qt.WindowStaysOnTopHint)
if isinstance(splash, str):
alignment = int(Qt.AlignBottom | Qt.AlignHCenter)
qsplash.showMessage(
splash, alignment=alignment, color=Qt.white)
qsplash.show()
app.processEvents()
out = (out, qsplash)

return out


# https://stackoverflow.com/questions/5160577/ctrl-c-doesnt-work-with-pyqt
Expand All @@ -166,3 +185,15 @@ def _qt_app_exec(app):
# reset the SIGINT exception handler
if is_python_signal_handler:
signal.signal(signal.SIGINT, old_signal)


def _qt_raise_window(widget):
# Set raise_window like matplotlib if possible
try:
from matplotlib import rcParams
raise_window = rcParams['figure.raise_window']
except ImportError:
raise_window = True
if raise_window:
widget.activateWindow()
widget.raise_()

0 comments on commit a93c198

Please sign in to comment.