Skip to content

Commit

Permalink
Field creation UI (#114)
Browse files Browse the repository at this point in the history
* Field UI + info alert on successful experiment creation

* update field and experiment methods. Make alerts warn not info in check

* Update READMEs

* revert test change
  • Loading branch information
tab-cmd authored Jan 3, 2021
1 parent 5d8542a commit d402c0e
Show file tree
Hide file tree
Showing 11 changed files with 349 additions and 51 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ This a list of the major modules and their functionality. Each module will conta
- `acquisition`: acquires data, gives back desired time series, saves to file at end of session.
- `display`: handles display of stimuli on screen and passes back stimuli timing.
- `signal`: eeg signal models, filters, processing, evaluators and viewers.
- `gui`: end-user interface into registered bci tasks and parameter editing. See BCInterface.py.
- `gui`: end-user interfaces which allow running of registered bci experiments, field collection and parameter editing.
- `helpers`: helpful functions needed for interactions between modules, basic I/O, and data visualization.
- `language_model`: gives probabilities of next letters during typing.
- `parameters`: location of json parameters.
Expand Down
3 changes: 1 addition & 2 deletions bcipy/acquisition/tests/datastream/test_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"""Tests for datastream generator module"""
from builtins import next
import unittest
import pytest
from past.builtins import map, range
from mock import mock_open, patch
from bcipy.acquisition.datastream.generator import random_data, file_data
Expand Down Expand Up @@ -96,7 +95,7 @@ def test_file_generator_end(self):
for _ in range(row_count):
next(gen)

with pytest.raises(StopIteration):
with self.assertRaises(StopIteration):
data.append(next(gen))

def test_file_with_custom_encoder(self):
Expand Down
14 changes: 5 additions & 9 deletions bcipy/gui/README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
# RSVP Keyboard GUI
======================================

This is the GUI for BciPy. The base window class, BCIGui, is contained in gui_main.py, and contains methods for easily adding widgets to a given window. BCInterface.py launches the main GUI window.

## Features
-----------

-Buttons, text boxes, drop-down menus, and other GUI elements are easy to add
-Read/write framework for JSON files
This module contains all GUI code used in BciPy. The base window class, BCIGui, is contained in gui_main.py, and contains methods for easily adding widgets to a given window. BCInterface.py launches the main GUI window. There are also interfaces for collecting and editing data (parameters and field data for experiments.)

## Dependencies
-------------
This project was written in wxPython version 4.0.0 and PyQt5. We are deprecating the wxPython UIs in future releases.
This project was written in wxPython version 4.0.4 and PyQt5 5.15.1. We are deprecating the wxPython UIs in future releases.

## Project structure
---------------
Name | Description
------------- | -------------
BCInterface.py | Defines main GUI window. Selection of user, experiment and task.
gui_main.py | BCIGui containing methods for adding buttons, images, etc. to GUI window
params_form.py | Defines window for setting BCInterface parameters
parameters/params_form.py | Defines window for setting BCInterface parameters
experiments/ExperimentRegistry.py | GUI for creating new experiments to select in BCInterface.
experiments/FieldRegistry.py | GUI for creating new fields for experiment data collection.
experiments/ExperimentField.py | GUI for collecting a registered experiment's field data.


The folder 'bcipy/static/images/gui_images' contains images for the GUI.
Expand Down
6 changes: 3 additions & 3 deletions bcipy/gui/experiments/ExperimentField.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,15 @@ def do_layout(self) -> None:

def build_form(self) -> None:
"""Build Form.
Loop over the field data and create UI field inputs for data collection.
"""
for field_name, field_type, required, help_text in self.field_data:
self.field_inputs.append(self.field_input(field_name, field_type, help_text, required))

def field_input(self, field_name: str, field_type: str, help_tip: str, required: bool) -> FormInput:
"""Field Input.
Construct a FormInput for the given field based on its python type and other
attributes.
"""
Expand All @@ -102,7 +102,7 @@ def field_input(self, field_name: str, field_type: str, help_tip: str, required:

def build_assets(self) -> None:
"""Build Assets.
Build any needed assests for the Experiment Field Widget. Currently, only a form is needed.
"""
self.build_form()
Expand Down
65 changes: 44 additions & 21 deletions bcipy/gui/experiments/ExperimentRegistry.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import sys
import subprocess

from bcipy.gui.gui_main import BCIGui, app, AlertMessageType

from bcipy.helpers.load import load_experiments, load_fields
from bcipy.helpers.save import save_experiment_data
from bcipy.helpers.system_utils import DEFAULT_EXPERIMENT_PATH, DEFAULT_FIELD_PATH, FIELD_FILENAME, EXPERIMENT_FILENAME
from bcipy.helpers.system_utils import DEFAULT_EXPERIMENT_PATH, EXPERIMENT_FILENAME


class ExperimentRegistry(BCIGui):
Expand All @@ -23,18 +25,20 @@ def __init__(self, *args, **kwargs):

# Structure of an experiment:
# { name: { fields : {name: '', required: bool}, summary: '' } }
self.experiments = load_experiments()
self.experiment_names = self.experiments.keys()
self.fields = [item for item in load_fields()]
self.update_experiment_data()

# These are set in the build_inputs and represent text inputs from the user
self.name_input = None
self.summary_input = None
self.field_input = None

self.fields = []
self.name = None
self.summary = None

self.show_gui()
self.update_field_list()

def build_text(self) -> None:
"""Build Text.
Expand Down Expand Up @@ -165,6 +169,21 @@ def create_experiment(self) -> None:
if self.check_input():
self.add_experiment()
self.save_experiments()
self.throw_alert_message(
title=self.alert_title,
message='Experiment saved successfully! Please exit window or create another experiment!',
message_type=AlertMessageType.INFO,
okay_to_exit=True
)
self.update_experiment_data()

def update_experiment_data(self):
"""Update Experiment Data.
Fetches the experiments and extracts the registered names.
"""
self.experiments = load_experiments()
self.experiment_names = self.experiments.keys()

def add_experiment(self) -> None:
"""Add Experiment:
Expand All @@ -188,15 +207,13 @@ def save_experiments(self) -> None:
def create_field(self) -> None:
"""Create Field.
Not implemented.
Launch to FieldRegistry to create a new field for experiments.
"""
self.throw_alert_message(
title=self.alert_title,
message=(
f'Create Field UI not available yet! Please add fields manually to '
f'{DEFAULT_FIELD_PATH}{FIELD_FILENAME}'),
message_type=AlertMessageType.INFO,
okay_to_exit=True)
subprocess.call(
f'python bcipy/gui/experiments/FieldRegistry.py',
shell=True)

self.update_field_list()

def add_field(self) -> None:
"""Add Field.
Expand Down Expand Up @@ -233,6 +250,14 @@ def update_registered_fields(self) -> None:
# TODO
pass

def update_field_list(self) -> None:
"""Updates the field_input combo box with a list of fields. """

self.field_input.clear()
self.field_input.addItem(ExperimentRegistry.default_text)
self.fields = [item for item in load_fields()]
self.field_input.addItems(self.fields)

def build_assets(self) -> None:
"""Build Assets.
Expand All @@ -253,26 +278,26 @@ def check_input(self) -> bool:
if self.name == ExperimentRegistry.default_text:
self.throw_alert_message(
title=self.alert_title,
message='Please add an Experiment Name',
message_type=AlertMessageType.INFO,
message='Please add an Experiment Name!',
message_type=AlertMessageType.WARN,
okay_to_exit=True)
return False
if self.name in self.experiment_names:
self.throw_alert_message(
title=self.alert_title,
message=(
'Experiment name already registered. '
'Please use a unique Experiment name! '
'Experiment name already registered. \n'
'Please use a unique Experiment name! \n'
f'Registed names: {self.experiment_names}'
),
message_type=AlertMessageType.INFO,
message_type=AlertMessageType.WARN,
okay_to_exit=True)
return False
if self.summary == ExperimentRegistry.default_text:
self.throw_alert_message(
title=self.alert_title,
message='Please add an Experiment Summary',
message_type=AlertMessageType.INFO,
message='Please add an Experiment Summary!',
message_type=AlertMessageType.WARN,
okay_to_exit=True)
return False
except Exception as e:
Expand All @@ -294,8 +319,6 @@ def start_app() -> None:
width=550,
background_color='black')

ex.show_gui()

sys.exit(bcipy_gui.exec_())


Expand Down
Loading

0 comments on commit d402c0e

Please sign in to comment.