Skip to content

Commit

Permalink
Merge pull request #123 from jpgill86/standalone-multi-windows
Browse files Browse the repository at this point in the history
Allow standalone app to open multiple files, each in independent windows
  • Loading branch information
jpgill86 committed Jan 4, 2021
2 parents fe42d29 + 6872465 commit bcae787
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 39 deletions.
41 changes: 30 additions & 11 deletions ephyviewer/scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,39 @@
import argparse
from ephyviewer.datasource import HAVE_NEO
from ephyviewer.standalone import all_neo_rawio_dict, rawio_gui_params
from ephyviewer import __version__

def launch_standalone_ephyviewer():
from ephyviewer.standalone import StandAloneViewer
from ephyviewer.standalone import WindowManager
import pyqtgraph as pg
assert HAVE_NEO, 'Must have neo 0.6.0'
assert HAVE_NEO, 'Must have Neo >= 0.6.0'
import neo


argv = sys.argv[1:]

parser = argparse.ArgumentParser(description='tridesclous')
parser.add_argument('file_or_dir', help='', default=None, nargs='?')
parser.add_argument('-f', '--format', help='File format', default=None)
description = """
Visualize electrophysiological data stored in files readable with Neo's
RawIO classes
"""
parser = argparse.ArgumentParser(description=description)
parser.add_argument('file_or_dir', default=None, nargs='?',
help='an optional path to a data file or directory, '
'which will be opened immediately (the file '
'format will be inferred from the file '
'extension, if possible; otherwise, --format is '
'required)')
parser.add_argument('-V', '--version', action='version',
version='ephyviewer {}'.format(__version__))
parser.add_argument('-f', '--format', default=None,
help='specify one of the following formats to '
'override the format detected automatically for '
'file_or_dir: {}'.format(
', '.join(all_neo_rawio_dict.keys())))

app = pg.mkQApp()
win = StandAloneViewer()
win.show()

manager = WindowManager()

if len(argv)>=1:
args = parser.parse_args(argv)
Expand All @@ -32,16 +48,19 @@ def launch_standalone_ephyviewer():
else:
neo_rawio_class = all_neo_rawio_dict.get(args.format, None)

assert neo_rawio_class is not None, 'Unknown format. Format list: ['+','.join(all_neo_rawio_dict.keys())+']'
assert neo_rawio_class is not None, 'Unknown format. Format list: {}'.format(', '.join(all_neo_rawio_dict.keys()))

name = neo_rawio_class.__name__.replace('RawIO', '')
if name in rawio_gui_params:
raise(Exception('This IO needs arguments. Do ephyviewer and use open menu'))
raise(Exception('This IO requires additional parameters. Run ephyviewer without arguments to input these via the GUI.'))

win.load_dataset(neo_rawio_class=neo_rawio_class, file_or_dir_names=[file_or_dir_name])
manager.load_dataset(neo_rawio_class=neo_rawio_class, file_or_dir_names=[file_or_dir_name])

else:
manager.open_dialog()

app.exec_()
if manager.windows:
app.exec_()

if __name__=='__main__':
launch_standalone_ephyviewer()
68 changes: 46 additions & 22 deletions ephyviewer/standalone.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
#~ from __future__ import (unicode_literals, print_function, division, absolute_import)

import gc
from collections import OrderedDict

from .myqt import QT, QT_MODE
Expand Down Expand Up @@ -39,23 +40,10 @@



class StandAloneViewer(MainViewer):
class WindowManager():
def __init__(self):

navigation_params = {}
MainViewer.__init__(self, debug=False, settings_name=None, **navigation_params)

self.navigation_dock.hide()

self.create_actions_and_menu()


def create_actions_and_menu(self):
self.file_menu = self.menuBar().addMenu(self.tr("File"))

do_open = QT.QAction('&Open', self, shortcut = "Ctrl+O")
do_open.triggered.connect(self.open_dialog)
self.file_menu.addAction(do_open)
self.windows = []

def open_dialog(self):
dia = RawNeoOpenDialog()
Expand All @@ -74,17 +62,32 @@ def load_dataset(self, neo_rawio_class=None, file_or_dir_names=[], io_params={})
kargs['dirname'] = file_or_dir_names[0]
kargs.update(io_params)

self.neorawio = neo_rawio_class(**kargs)
self.neorawio.parse_header()
print(self.neorawio)
neorawio = neo_rawio_class(**kargs)
neorawio.parse_header()
print(neorawio)

self.navigation_dock.show()
# initialize a new main window and populate viewers
navigation_params = {}
win = MainViewer(debug=False, settings_name=None, **navigation_params)
sources = get_sources_from_neo_rawio(neorawio)
compose_mainviewer_from_sources(sources, mainviewer=win)

#TODO clear all
sources = get_sources_from_neo_rawio(self.neorawio)
# add menus
file_menu = win.menuBar().addMenu(win.tr("File"))
do_open = QT.QAction('&Open', win, shortcut = "Ctrl+O")
do_open.triggered.connect(self.open_dialog)
file_menu.addAction(do_open)

# store a reference to the window to prevent it from going out of scope
# immediately and being destroyed
self.windows.append(win)

compose_mainviewer_from_sources(sources, mainviewer=self)
# delete window on close so that memory and file resources are released
win.setAttribute(QT.WA_DeleteOnClose, True)
win.destroyed.connect(
lambda qobject, i=len(self.windows)-1: self.free_resources(i))

win.show()

#~ for i, sig_source in enumerate(sources['signal']):
#~ view = TraceViewer(source=sig_source, name='signal {}'.format(i))
Expand All @@ -108,6 +111,27 @@ def load_dataset(self, neo_rawio_class=None, file_or_dir_names=[], io_params={})
#~ view = EventList(source=ep_source, name='Event list')
#~ self.add_view(view, location='bottom', orientation='horizontal')

def free_resources(self, i):
"""
Run garbage collection to unlock files and free memory for the closed
window with index ``i``.
Data files opened by Neo in lazy mode remain locked for as long as the
RawIO objects pointing to them exist in memory. Normally such objects
would be automatically garbage collected when they go out of scope,
i.e., when the window that created them is closed. However, due to an
issue in Neo, circular references to these objects are always created,
so they persist even after the window is closed. This function performs
a manual garbage collection after a window has been closed to clean up
any lingering Neo objects that keep files locked. For more info about
the issue, see https://github.com/NeuralEnsemble/python-neo/issues/684.
"""

# first remove the last remaining reference to the closed window
self.windows[i] = None

# run garbage collection
gc.collect()




Expand Down
13 changes: 7 additions & 6 deletions ephyviewer/tests/test_standalone.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@



from ephyviewer.standalone import StandAloneViewer, RawNeoOpenDialog
from ephyviewer.standalone import WindowManager, RawNeoOpenDialog
import pyqtgraph as pg



def test_StandAloneViewer():
def test_WindowManager():
app = pg.mkQApp()
win = StandAloneViewer()
win.show()
app.exec_()
manager = WindowManager()
manager.open_dialog()
if manager.windows:
app.exec_()


def test_RawNeoOpenDialog():
Expand All @@ -24,5 +25,5 @@ def test_RawNeoOpenDialog():


if __name__=='__main__':
test_StandAloneViewer()
test_WindowManager()
#~ test_RawNeoOpenDialog()

0 comments on commit bcae787

Please sign in to comment.