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

Display loading indicator when launching in the standalone app #223

Merged
merged 4 commits into from
Mar 1, 2020
Merged
Changes from all commits
Commits
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
143 changes: 122 additions & 21 deletions neurotic/gui/standalone.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from packaging import version

import quantities as pq
import neo
from ephyviewer import QT, QT_MODE

from .. import __version__, _elephant_tools, default_log_level, log_file
Expand Down Expand Up @@ -63,6 +64,7 @@ class MainWindow(QT.QMainWindow):
"""

request_download = QT.pyqtSignal()
request_load_dataset = QT.pyqtSignal()

def __init__(self, file=None, initial_selection=None, lazy=True, theme='light', ui_scale='small', support_increased_line_width=False, show_datetime=False):
"""
Expand Down Expand Up @@ -104,15 +106,37 @@ def __init__(self, file=None, initial_selection=None, lazy=True, theme='light',

# metadata selector
self.metadata_selector = _MetadataSelectorQt(self)
self.setCentralWidget(self.metadata_selector)

# loading label
loading_label = QT.QLabel('Launching, please wait...')
loading_label.setFrameStyle(QT.QFrame.Panel | QT.QFrame.Sunken)
loading_label.setAlignment(QT.Qt.AlignCenter)
loading_label.setStyleSheet('font: 14pt')

# initially stack the metadata selector above the loading label
self.stacked_layout = QT.QStackedLayout()
self.stacked_layout.addWidget(self.metadata_selector) # index 0
self.stacked_layout.addWidget(loading_label) # index 1
central_widget = QT.QWidget()
central_widget.setLayout(self.stacked_layout)
self.setCentralWidget(central_widget)

# create a worker thread for downloading data
self.download_thread = QT.QThread()
self.download_worker = _DownloadWorker(self.metadata_selector)
self.download_worker = _DownloadWorker(self)
self.download_worker.moveToThread(self.download_thread)
self.request_download.connect(self.download_worker.download)
self.download_worker.download_finished.connect(self.on_download_finished)

# create a worker thread for loading datasets
self.load_dataset_thread = QT.QThread()
self.load_dataset_worker = _LoadDatasetWorker(self)
self.load_dataset_worker.moveToThread(self.load_dataset_thread)
self.request_load_dataset.connect(self.load_dataset_worker.load_dataset)
self.load_dataset_worker.load_dataset_finished.connect(self.on_load_dataset_finished)
self.load_dataset_worker.show_status_msg.connect(self.statusBar().showMessage)
self.blk = None

# construct the menus
self.create_menus()

Expand Down Expand Up @@ -170,9 +194,9 @@ def create_menus(self):
do_open_directory.setShortcut('Ctrl+F')
do_open_directory.triggered.connect(self.open_directory)

do_launch = file_menu.addAction('&Launch')
do_launch.setShortcut('Return')
do_launch.triggered.connect(self.launch)
self.do_launch = file_menu.addAction('&Launch')
self.do_launch.setShortcut('Return')
self.do_launch.triggered.connect(self.start_launch)

options_menu = self.menuBar().addMenu(self.tr('&Options'))

Expand Down Expand Up @@ -319,24 +343,43 @@ def open_directory(self):
self.statusBar().showMessage('Folder not found locally (need to '
'download?)', msecs=5000)

def launch(self):
def start_launch(self):
"""
Load data for the selected dataset and launch the ephyviewer window.
Load data for the selected dataset in a separate thread.
"""

metadata = self.metadata_selector.selected_metadata
self.do_launch.setText('&Launch in progress!')
self.do_launch.setEnabled(False)
self.metadata_selector.setEnabled(False)
self.stacked_layout.setCurrentIndex(1) # show loading label
self.load_dataset_thread.start()
self.request_load_dataset.emit()

def on_load_dataset_finished(self):
"""
Launch the ephyviewer window after loading the dataset.
"""

self.load_dataset_thread.quit()

try:

blk = load_dataset(metadata, lazy=self.lazy)
if self.blk is None:
pass

elif not isinstance(self.blk, neo.Block):
raise ValueError('blk must be a Neo Block but instead is '
f'{self.blk}')

ephyviewer_config = EphyviewerConfigurator(metadata, blk, self.lazy)
ephyviewer_config.show_all()
else:
metadata = self.metadata_selector.selected_metadata
ephyviewer_config = EphyviewerConfigurator(metadata, self.blk, self.lazy)
ephyviewer_config.show_all()

win = ephyviewer_config.create_ephyviewer_window(theme=self.theme, ui_scale=self.ui_scale, support_increased_line_width=self.support_increased_line_width, show_datetime=self.show_datetime)
self.windows.append(win)
win.destroyed.connect(lambda qobject, i=len(self.windows)-1: self.free_resources(i))
win.show()
win = ephyviewer_config.create_ephyviewer_window(theme=self.theme, ui_scale=self.ui_scale, support_increased_line_width=self.support_increased_line_width, show_datetime=self.show_datetime)
self.windows.append(win)
win.destroyed.connect(lambda qobject, i=len(self.windows)-1: self.free_resources(i))
win.show()

except FileNotFoundError as e:

Expand All @@ -353,6 +396,13 @@ def launch(self):
self.statusBar().showMessage('Launch failed (see console for '
'details)', msecs=5000)

finally:

self.do_launch.setText('&Launch')
self.do_launch.setEnabled(True)
self.metadata_selector.setEnabled(True)
self.stacked_layout.setCurrentIndex(0) # show metadata selector

def toggle_debug_logging(self, checked):
"""
Toggle log filtering level between its original level and debug mode
Expand Down Expand Up @@ -445,7 +495,6 @@ def show_about(self):

import platform
import ephyviewer
import neo
import numpy
import pyqtgraph
try:
Expand Down Expand Up @@ -517,8 +566,10 @@ def free_resources(self, i):
the issue, see https://github.com/NeuralEnsemble/python-neo/issues/684.
"""

# first remove the last remaining reference to the closed window
# first remove the last remaining references to the closed window and
# Neo Block
self.windows[i] = None
self.blk = None

# run garbage collection
gc.collect()
Expand All @@ -541,7 +592,7 @@ def __init__(self, parent):
self.setStyleSheet('font: 9pt Courier;')

self.currentRowChanged.connect(self._on_select)
self.itemDoubleClicked.connect(self.parent().launch)
self.itemDoubleClicked.connect(self.parent().start_launch)

def _on_select(self, currentRow):
"""
Expand Down Expand Up @@ -590,19 +641,69 @@ class _DownloadWorker(QT.QObject):

download_finished = QT.pyqtSignal()

def __init__(self, metadata_selector):
def __init__(self, mainwindow):
"""
Initialize a new _DownloadWorker.
"""

QT.QObject.__init__(self)

self.metadata_selector = metadata_selector
self.mainwindow = mainwindow

def download(self):
"""
Download all files and emit a signal when complete.
"""

self.metadata_selector.download_all_data_files()
self.mainwindow.metadata_selector.download_all_data_files()
self.download_finished.emit()


class _LoadDatasetWorker(QT.QObject):
"""
A thread worker for loading data sets.
"""

load_dataset_finished = QT.pyqtSignal()
show_status_msg = QT.pyqtSignal(str, int)

def __init__(self, mainwindow):
"""
Initialize a new _LoadDatasetWorker.
"""

QT.QObject.__init__(self)

self.mainwindow = mainwindow

def load_dataset(self):
"""
Load the selected dataset.
"""

metadata = self.mainwindow.metadata_selector.selected_metadata
lazy = self.mainwindow.lazy

try:

self.mainwindow.blk = load_dataset(metadata, lazy=lazy)

except FileNotFoundError as e:

logger.error('Some files were not found locally and may need to '
f'be downloaded: {e}')
self.show_status_msg.emit('Launch failed because some files are '
'missing (need to download?)', 5000)
self.mainwindow.blk = None

except Exception:

logger.exception('Encountered a fatal error. Traceback will be '
'written to log file.')
self.show_status_msg.emit('Launch failed (see console for '
'details)', 5000)
self.mainwindow.blk = None

finally:

self.load_dataset_finished.emit()