Skip to content

Commit

Permalink
Merge pull request #57 from BciPy/1_3_1
Browse files Browse the repository at this point in the history
Version 1.4.0
  • Loading branch information
tab-cmd authored Jun 17, 2019
2 parents a142f87 + a5913a5 commit 364da74
Show file tree
Hide file tree
Showing 48 changed files with 1,539 additions and 1,712 deletions.
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
# 1.4.0

This release focused on bug fixes, exposing parameters, and refactoring. Further dual screen configuration and testing was done to allow for simultaneous signal viewing and task operation.

## Added

- Dual screen configuration / updated support
- Parameters:
- Copy phrase decision thresholds
- Inter-sequence Feedback level thresholds


## Updated
- RSVP Display: refactor
- Decision Maker / Evidence Fusion: refactor
- Signal Viewer: more distinct channel names
- bci_main: shutdown handling and bug fix
- Language Model Helper: bug fix for negative probabilities

## Removed
- Multicolor Text
- Old LSL viewer


# 1.3.0

This release focused on the addition of a Signal Viewer, Inter-sequence Feedback Task, Signal Processing / Decomposition Methods, and miscellaneous cleanup.
Expand Down
17 changes: 11 additions & 6 deletions bci_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ def bci_main(parameters: dict, user: str, exp_type: int, mode: str) -> bool:


def execute_task(task_type: dict, parameters: dict, save_folder: str) -> bool:
"""Excecute Task.
"""Execute Task.
Executes the desired task by setting up the display window and
data acquisition, then passing on to the start_task funtion
data acquisition, then passing on to the start_task function
which will initialize experiment.
Input:
Expand All @@ -82,7 +82,7 @@ def execute_task(task_type: dict, parameters: dict, save_folder: str) -> bool:
try:
signal_model, filename = load_signal_model()
except Exception as e:
logging.debug('Cannot load signal model. Exiting')
print(f'Cannot load signal model. Exiting. {e}')
raise e

# if Language Model enabled init lm
Expand Down Expand Up @@ -116,8 +116,6 @@ def execute_task(task_type: dict, parameters: dict, save_folder: str) -> bool:

def _clean_up_session(display, daq, server):
"""Clean up session."""
# Close the display window
display.close()

# Stop Acquisition
daq.stop_acquisition()
Expand All @@ -126,6 +124,12 @@ def _clean_up_session(display, daq, server):
if server:
server.stop()

# Close the display window
# NOTE: There is currently a bug in psychopy when attempting to shutdown
# windows when using a USB-C monitor. Putting the display close last in
# the sequence allows acquisition to properly shutdown.
display.close()

return True


Expand All @@ -141,6 +145,7 @@ def _clean_up_session(display, daq, server):
f" {task.value}")
for task in ExperimentType])
parser = argparse.ArgumentParser()

# Command line utility for adding arguments/ paths via command line
parser.add_argument('-p', '--parameters', default='bcipy/parameters/parameters.json',
help='Parameter location. Must be in parameters directory. Pass as parameters/parameters.json')
Expand All @@ -151,7 +156,7 @@ def _clean_up_session(display, daq, server):
help='BCI mode. Ex. RSVP, MATRIX, SHUFFLE')
args = parser.parse_args()

# Load a parameters file
# Load parameters
parameters = load_json_parameters(args.parameters, value_cast=True)

# Start BCI Main
Expand Down
97 changes: 1 addition & 96 deletions bcipy/display/display_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def init_display_window(parameters):
display_window = visual.Window(
size=[window_width,
window_height],
screen=0,
screen=int(parameters['stim_screen']),
allowGUI=False,
useFBO=False,
fullscr=full_screen,
Expand All @@ -51,98 +51,3 @@ def init_display_window(parameters):

# Return display window to caller
return display_window


class MultiColorText(object):
"""Multi Color Text.
Implementation of multi color Text Stimuli. Psychopy does not
support multiple color texts. Draws multiple TextStim elements on
the screen with different colors.
Attr:
texts(list[TextStim]): characters that form the string
"""

def __init__(self, win, list_color=['red'] * 5, height=0.2,
text='dummy_text', font='Times', pos=(0, 0), wrap_width=None,
color_space='rgb', opacity=1, depth=-6.0):
"""Initialize Multi Color Text.
Args:
win(visual_window): display window
text(string): string to be displayed
list_color(list[string]): list of colors of the string
height(float): height of each character
pos(tuple): center position of the multi color text
wrap_width, color_space, opacity, depth : to keep consistency
of the visual object definition (required in TextStim)
"""
self.win = win
self.pos = pos
self.text = text
self.font = font
self.height = height
self.list_color = list_color
self.wrap_width = wrap_width
self.color_space = color_space
self.opacity = opacity
self.depth = depth

self.texts = []

# Align characters using pixel wise operations
width_total_in_pix = 0
for idx in range(len(list_color)):
self.texts.append(
visual.TextStim(win=win, color=list_color[idx], height=height,
text=text[idx], font=font, pos=(0, 0),
wrapWidth=self.wrap_width,
colorSpace=self.color_space,
opacity=opacity, depth=depth))
# Bounding box provides pixel information of each letter
width_total_in_pix += self.texts[idx].boundingBox[0]

# Window goes from [-1,1], therefore we need to multiply by 2
x_pos_text = pos[0] - (width_total_in_pix / win.size[0])
for idx in range(len(list_color)):
len_txt = self.texts[idx].boundingBox[0] / win.size[0]
self.texts[idx].pos = (x_pos_text + len_txt, pos[1])
x_pos_text += len_txt * 2

def draw(self):
"""Draw Multi Color Text."""
for idx in range(len(self.texts)):
self.texts[idx].draw()

def update(self, text, color_list, pos):
"""Update (Re-creates) Multicolor Text Object.
It is more
compact to erase the previous one and recreate a new object.
Args:
text(string): string to be displayed
color_list(list[string]): list of colors of the string
pos(tuple): position of the multicolor text
"""
# Align characters using pixel wise operations
width_total_in_pix = 0
self.texts = []
for idx in range(len(color_list)):
self.texts.append(
visual.TextStim(win=self.win, color=color_list[idx],
height=self.height,
text=text[idx], font=self.font, pos=(0, 0),
wrapWidth=self.wrap_width,
colorSpace=self.color_space,
opacity=self.opacity, depth=self.depth))
# Bounding box provides pixel information of each letter
width_total_in_pix += self.texts[idx].boundingBox[0]

# Window goes from [-1,1], therefore we need to multiply by 2
x_pos_text = pos[0] - (width_total_in_pix / self.win.size[0])
for idx in range(len(color_list)):
len_txt = self.texts[idx].boundingBox[0] / self.win.size[0]
self.texts[idx].pos = (x_pos_text + len_txt, pos[1])
x_pos_text += len_txt * 2
3 changes: 3 additions & 0 deletions bcipy/display/matrix/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import sys
from os.path import dirname
sys.path.append(dirname(__file__))
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import logging


class DisplayMatrix(object):
class MatrixDisplay:
""" Matrix Display Object for Sequence Presentation. Animates a sequence
in Matrix. Mode should be determined outside.
Attr:
Expand All @@ -16,36 +16,37 @@ class DisplayMatrix(object):
sti(visual_Text_Stimuli): stimuli text
trialClock(core_clock): timer for presentation """

def __init__(self, window, clock, experiment_clock, color_task='white',
font_task='Times', pos_task=(-.75, .75), task_height=0.1,
def __init__(self, window, clock, experiment_clock, task_color='white',
task_font='Times', task_pos=(-.75, .75), task_height=0.1,
grid_rows=6, grid_columns=6, time_flash=.25,
text_task='1/100', color_text=['white'],
font_text='Times', height_text=0.25,
font_sti='Times', pos_sti=(0, .0), sti_height=0.25,
is_txt_sti=True, alp=None):
task_text='1/100',
stim_font='Times',
stim_pos=(0, .0),
stim_height=0.25,
is_txt_stim=True, alp=None):

self.win = window
self.logger = log = logging.getLogger(__name__)

# TASK TEXT
self.font_task_text = font_task
self.task_font_text = task_font
self.task_height = task_height
self.pos_task_text = pos_task
self.text_task = text_task
self.color_task = color_task
self.task_pos = task_pos
self.task_text = task_text
self.task_color = task_color

# STIM / GRID
self.is_txt_sti = is_txt_sti
self.is_txt_stim = is_txt_stim
self.stimuli = []
self.rows = grid_rows
self.font = font_sti
self.stimuli_font = stim_font
self.columns = grid_columns
self.sti_height = sti_height
self.stim_height = stim_height

self.stim_number = grid_rows + grid_columns

self.max_height_grid = -1 + sti_height
self.max_width_grid = 1 - sti_height
self.max_height_grid = -1 + stim_height
self.max_width_grid = 1 - stim_height
self.uniform_grid_values_row = sorted(
np.linspace(
start=self.max_height_grid,
Expand All @@ -70,7 +71,7 @@ def __init__(self, window, clock, experiment_clock, color_task='white',
self.expClock = experiment_clock

# Length of the stimuli (number of stimuli on screen)
self.len_sti = len(self.stimuli)
self.stim_length = len(self.stimuli)

def draw_static(self):
""" Draws static elements in a stimulus. """
Expand All @@ -83,11 +84,11 @@ def draw_static(self):
stim.draw()

# Next, Draw the task text
task_text = visual.TextStim(win=self.win, color=self.color_task,
task_text = visual.TextStim(win=self.win, color=self.task_color,
height=self.task_height,
text=self.text_task,
font=self.font_task_text,
pos=self.pos_task_text,
text=self.task_text,
font=self.task_font_text,
pos=self.task_pos,
wrapWidth=None, colorSpace='rgb',
opacity=1, depth=-6.0)
task_text.draw()
Expand All @@ -100,7 +101,7 @@ def make_spelling_grid(self):

# Loop through each row
for idx in range(self.rows):
if self.is_txt_sti:
if self.is_txt_stim:

# loop through each column
for x in range(self.columns):
Expand All @@ -113,9 +114,9 @@ def make_spelling_grid(self):
self.uniform_grid_values_row)

stim = visual.TextStim(win=self.win,
height=self.sti_height,
height=self.stim_height,
text=self.alp[alp_idx],
font=self.font, pos=pos,
font=self.stimuli_font, pos=pos,
wrapWidth=None, colorSpace='rgb',
opacity=1, depth=-6.0)
alp_idx += 1
Expand Down Expand Up @@ -183,11 +184,11 @@ def determine_position_on_grid(row_idx, col_idx,
color='black')

# Create a Display Matrix Object
matrix = DisplayMatrix(
matrix = MatrixDisplay(
display_window,
clock.MonotonicClock(),
clock.MonotonicClock(),
text_task='')
task_text='')

# draw matrix grid and other static
matrix.draw_static()
Expand All @@ -196,7 +197,7 @@ def determine_position_on_grid(row_idx, col_idx,
for i in range(task_length):
task_index += 1

matrix.text_task = '%s/5' % task_index
matrix.task_text = '%s/5' % task_index
matrix.draw_static()

# animate!
Expand Down
Loading

0 comments on commit 364da74

Please sign in to comment.