diff --git a/doc/examples.rst b/doc/examples.rst index 119a62b..05266e0 100644 --- a/doc/examples.rst +++ b/doc/examples.rst @@ -90,3 +90,13 @@ Viewers from Neo objects :download:`viewers_with_neo_objects.py <../examples/viewers_with_neo_objects.py>` .. literalinclude:: ../examples/viewers_with_neo_objects.py + + +Viewers for SpikeInterface objects +---------------------------------- + +.. image:: img/viewers_with_spikeinterface_objects.png + +:download:`viewers_with_spikeinterface_objects.py <../examples/viewers_with_spikeinterface_objects.py>` + +.. literalinclude:: ../examples/viewers_with_spikeinterface_objects.py diff --git a/doc/img/viewers_with_spikeinterface_objects.png b/doc/img/viewers_with_spikeinterface_objects.png new file mode 100644 index 0000000..15d7320 Binary files /dev/null and b/doc/img/viewers_with_spikeinterface_objects.png differ diff --git a/doc/installation.rst b/doc/installation.rst index 32344ee..ac360fa 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -4,12 +4,12 @@ Installation ============ Requirements: - * Python ≥ 3.5 + * Python ≥ 3.7 * numpy * scipy * matplotlib ≥ 2.0 * pyqtgraph ≥ 0.10.0 - * PyQt5, PySide2, or PyQt4 (manual installation required) + * PyQt5 or PySide2 (manual installation required) Optional dependencies: * Neo ≥ 0.6 (standalone app and Neo sources) diff --git a/doc/releasenotes.rst b/doc/releasenotes.rst index 4dd579e..f08cbd4 100644 --- a/doc/releasenotes.rst +++ b/doc/releasenotes.rst @@ -6,6 +6,7 @@ Release Notes .. toctree:: :maxdepth: 1 + releases/1.5.0.rst releases/1.4.0.rst releases/1.3.1.rst releases/1.3.0.rst diff --git a/doc/releases/1.5.0.rst b/doc/releases/1.5.0.rst new file mode 100644 index 0000000..38133cf --- /dev/null +++ b/doc/releases/1.5.0.rst @@ -0,0 +1,33 @@ +Version 1.5.0 +------------- + +2021-09-09 + +Compatibility +............. + +Support for Python < 3.7 and for PyQt4 were dropped. + +* Add compatibility with Neo 0.10.0 + (:pr:`151`, :pr:`157`, :pr:`159`) + +* Add PySide2 support + (:pr:`148`) + +New data sources +................ + +* Add data sources for SpikeInterface recording and sorting objects + (:pr:`153`) + +Continuous integration +...................... + +* Run automated test suite with GitHub Actions + (:pr:`137`, :pr:`138`, :pr:`142`, :pr:`145`, :pr:`149`) + +* Add manually-triggerable GitHub Action workflows for publishing to PyPI + (:pr:`140`) + +* Add Coveralls test coverage reporting + (:pr:`144`) diff --git a/ephyviewer/datasource/epochs.py b/ephyviewer/datasource/epochs.py index d633450..27e4da8 100644 --- a/ephyviewer/datasource/epochs.py +++ b/ephyviewer/datasource/epochs.py @@ -77,7 +77,7 @@ def __init__(self, epoch=None, possible_labels=[], color_labels=None, channel_na # add labels missing from possible_labels but found in epoch data new_labels_from_data = list(set(epoch['label'])-set(self.possible_labels)) if restrict_to_possible_labels: - assert len(new_labels_from_data)==0, 'epoch data contains labels not found in possible_labels: ' + str(new_labels_from_data) + assert len(new_labels_from_data)==0, f'epoch data contains labels not found in possible_labels: {new_labels_from_data}' self.possible_labels += new_labels_from_data # put the epochs into a canonical order after loading diff --git a/ephyviewer/datasource/spikeinterfacesources.py b/ephyviewer/datasource/spikeinterfacesources.py index 79e5823..bda7e16 100644 --- a/ephyviewer/datasource/spikeinterfacesources.py +++ b/ephyviewer/datasource/spikeinterfacesources.py @@ -1,5 +1,5 @@ """ -Integrate +Data sources for SpikeInterface """ from .sourcebase import BaseDataSource @@ -11,9 +11,8 @@ try: from distutils.version import LooseVersion as V import spikeinterface - if V(spikeinterface.__version__)>='0.90.0': + if V(spikeinterface.__version__)>='0.90.1': HAVE_SI = True - from neo.rawio.baserawio import BaseRawIO else: HAVE_SI = False except ImportError: @@ -24,10 +23,10 @@ from .spikes import BaseSpikeSource -class FromSpikeinterfaceRecordingSource(BaseAnalogSignalSource): +class SpikeInterfaceRecordingSource(BaseAnalogSignalSource): def __init__(self, recording, segment_index=0): BaseAnalogSignalSource.__init__(self) - + self.recording = recording self.segment_index = segment_index @@ -67,13 +66,13 @@ def index_to_time(self, ind): -class FromSpikeinterfaceSorintgSource(BaseSpikeSource): +class SpikeInterfaceSortingSource(BaseSpikeSource): def __init__(self, sorting, segment_index=0): BaseSpikeSource.__init__(self) - + self.sorting = sorting self.segment_index = segment_index - + #TODO self._t_stop = 10. diff --git a/ephyviewer/tests/test_datasource.py b/ephyviewer/tests/test_datasource.py index 70c2344..45b5889 100644 --- a/ephyviewer/tests/test_datasource.py +++ b/ephyviewer/tests/test_datasource.py @@ -159,13 +159,13 @@ def test_spikeinterface_sources(): from spikeinterface.core.testing_tools import generate_recording, generate_sorting recording = generate_recording() - source = ephyviewer.FromSpikeinterfaceRecordingSource(recording=recording) + source = ephyviewer.SpikeInterfaceRecordingSource(recording=recording) print(source) print(source.t_start, source.nb_channel, source.sample_rate) sorting = generate_sorting() - source = ephyviewer.FromSpikeinterfaceSorintgSource(sorting=sorting) + source = ephyviewer.SpikeInterfaceSortingSource(sorting=sorting) print(source) print(source.t_start, source.nb_channel, source.get_channel_name()) diff --git a/ephyviewer/tests/test_spikeinterfaceviewer.py b/ephyviewer/tests/test_spikeinterfaceviewer.py index 417ab19..6473f42 100644 --- a/ephyviewer/tests/test_spikeinterfaceviewer.py +++ b/ephyviewer/tests/test_spikeinterfaceviewer.py @@ -8,11 +8,12 @@ def test_spikeinterface_viewer(interactive=False): import spikeinterface as si from spikeinterface.core.testing_tools import generate_recording, generate_sorting + recording = generate_recording() - sig_source = ephyviewer.FromSpikeinterfaceRecordingSource(recording=recording) + sig_source = ephyviewer.SpikeInterfaceRecordingSource(recording=recording) sorting = generate_sorting() - spike_source = ephyviewer.FromSpikeinterfaceSorintgSource(sorting=sorting) + spike_source = ephyviewer.SpikeInterfaceSortingSource(sorting=sorting) app = ephyviewer.mkQApp() win = ephyviewer.MainViewer(debug=True, show_auto_scale=True) diff --git a/ephyviewer/version.py b/ephyviewer/version.py index b70cacd..4139253 100644 --- a/ephyviewer/version.py +++ b/ephyviewer/version.py @@ -1 +1 @@ -version = '1.4.1.dev' +version = '1.5.0' diff --git a/examples/viewers_with_spikeinterface_objects.py b/examples/viewers_with_spikeinterface_objects.py new file mode 100644 index 0000000..f025b0f --- /dev/null +++ b/examples/viewers_with_spikeinterface_objects.py @@ -0,0 +1,43 @@ +""" +Here is an example of opening viewers directly from spikeinterface objects : recording and sorting. + +In this example, recording and sorting are fake ones generated by spikeinterface but of you course +you can use any format supported by spikeinterface. + +Note that you can display any lazy preprocessor from +`spikeinterface.toolkit.preprocessing` (filtering, denoising, whittening, ...) so you can see immediatly +the clean signal. + + + +""" +import ephyviewer +import spikeinterface.full as si +from spikeinterface.core.testing_tools import generate_recording, generate_sorting + + +recording = generate_recording() +sig_source = ephyviewer.SpikeInterfaceRecordingSource(recording=recording) + +filtered_recording = si.bandpass_filter(recording, freq_min=60., freq_max=100.) +sig_filtered_source = ephyviewer.SpikeInterfaceRecordingSource(recording=filtered_recording) + +sorting = generate_sorting() +spike_source = ephyviewer.SpikeInterfaceSortingSource(sorting=sorting) + +app = ephyviewer.mkQApp() +win = ephyviewer.MainViewer(debug=True, show_auto_scale=True) + +view = ephyviewer.TraceViewer(source=sig_source, name='signals') +win.add_view(view) + +view = ephyviewer.TraceViewer(source=sig_filtered_source, name='signals filtered') +win.add_view(view) + +view = ephyviewer.SpikeTrainViewer(source=spike_source, name='spikes') +win.add_view(view) + + +win.show() +app.exec_() +