I will start with a personal opinion that I have come to after a few years of using mne. My general perception of MNE-python is that it is engineered with the intent to be used within python notebooks, at least when it comes to visualizations. I was originally going to include some of these comments as part of a recent issue on Github, but given that these are more architectural discussion, I think they probably belong here.
This issue (at least it is for our group) has become problematic for us after trying to create some 3d visualizations of fnirs activity. We don’t tend to do much of our work in notebooks as we find maintainability and reproducibility to be difficult in such cases. Regardless, we prefer to do our work directly in .py
files.
We first attempted to use plot_nirs_source_detector
, a function created as part of the mne-nirs
package, to visualize the results of multiple anova’s run for each sensor-detector pair. This function doesn’t perfectly fit the type of visualization we would like, but would be sufficient for our needs. However, to my knowledge, there is no proper way to use this function in a raw python file, as it is made with interactive python in mind (ie notebooks), and does not expose any necessary parameters (ie blocking), to prevent the code from running through and immediately closing the window. This function works as intended as part of a notebook, but not in a python file. I attempted to include blocking functionality in the code, but found it difficult, so decided to move on.
We then tried to approach our visualization using the mne.viz.Brain
class, which has an explicit block
parameter as part of its constructor . This blocking works when performing a single call to this function (ie the constructor), but due to a bug causes subsequent calls to methods of the class to fail, as the renderer is cleaned up immediately after a call to the constructor when block
is true. In regard to this class, I wonder how the block
param is handled in general. Do calls to plotting methods of Brain
objects constructed with block
set to true also block, or should one expect to include this information with each method call? Additionally, looking at the internals of the show_view
method, why doesn’t this method ever make a call to the class’s show
method? I mean, this function is called show_view
, but practically, this function only updates certain fields of the object. Those changes are then only reflected in the renderer if the update
param is set. However, if block is set, the renderer is already cleaned up by that point, causing this call to fail. This methods fails to handle the case of either the show
or block
params being set in the object constructor. If this is the intended behavior then a more accurate signature would be update_view
.
Overall, it seems as though mne works wonderfully when used in notebooks for visualizations, but is inconsistent with its ability to function properly outside of them. In some functions, block
is present. In others, it is absent. Is implementing block
an issue at the global/abstract level, or is this more of an architectural decision? Should I be doing visualization in notebooks and not python files?
I would appreciate any input on the subject and would love to know if there is some way that I can help improve the consistency of the codebase in regard to this issue.
Thanks, Alex