Skip to content

Commit

Permalink
Merge pull request #40 from jfkominsky/0.10.2
Browse files Browse the repository at this point in the history
0.10.2
  • Loading branch information
jfkominsky committed Jul 10, 2022
2 parents 2a02fc7 + 77f1358 commit 837bd63
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 95 deletions.
12 changes: 12 additions & 0 deletions .github/workflows/prep_dummy_soundcard.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
cat << EOF >> /etc/modules.conf
# OSS/Free portion - card #1
alias sound-slot-0 snd-card-0
alias sound-service-0-0 snd-mixer-oss
alias sound-service-0-1 snd-seq-oss
alias sound-service-0-3 snd-pcm-oss
alias sound-service-0-8 snd-seq-oss
alias sound-service-0-12 snd-pcm-oss
EOF
modprobe snd-dummy
# ; modprobe snd-pcm-oss ; modprobe snd-mixer-oss ; modprobe snd-seq-oss
mkdir -p tmp && chmod 777 tmp
93 changes: 64 additions & 29 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,73 @@ permissions:
contents: read

jobs:
testsuite_mac:

testsuite_ubuntu:

runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
fail-fast: false # debugging builds lets see them all!
matrix:
os: [macos-latest]
python-version: [3.8]
os: [ ubuntu-latest ]
python-version: [ 3.8 ]

steps:
- uses: actions/checkout@v2

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Pre-Install
run: |
python -m pip install --upgrade pip
pip install pytest pytest-cov flake8 xmlschema
pip install wheel
pip install six # for configobj and wxpython
pip install distro # used by createInitFile
- name: Install PsychoPy and dependencies
run: |
# for pocketsphinx we need this adapted package:
brew install swig vlc
brew install openal-soft
pip install git+https://github.com/Im-Fran/pocketsphinx-python
pip install mock
pip install psychopy
- name: Test with Pytest
run:
pytest
- uses: actions/checkout@v2

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: get_month
if: runner.os=='Linux'
id: month
run: echo "::set-output name=month::$(date +'%Y-%m')"

- name: cache wxPython for linux
if: runner.os=='Linux'
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ matrix.os }}-py${{ matrix.python-version }}-${{ steps.get_month.outputs.month }}

- name: Install apt-get dependencies
if: runner.os=='Linux'
run: |
# mostly for wxPython:
sudo apt-get update
sudo apt-get install -y -qq python3-dev libgtk-3-dev
sudo apt-get install -y -qq libgstreamer1.0-0 gstreamer1.0-plugins-base
sudo apt-get install -y -qq libwebkit2gtk-4.0-dev
sudo apt-get install -y -qq libpng-dev libjpeg-dev libtiff-dev libnotify-dev libsm-dev
sudo apt-get install -y -qq libsdl-dev libsdl2-mixer-2.0-0 libsdl2-image-2.0-0 libsdl2-2.0-0
sudo apt-get install -y -qq libportaudio2
# virtual frame buffer
sudo apt install llvm-6.0-dev # for xvfb
sudo apt install xvfb xauth libgl1-mesa-dri
# set up fake sound device?
sudo apt-get install -y -qq libasound2-dev alsa-utils alsa-oss
sudo apt-get install -y linux-modules-extra-$(uname -r) # needed for modprobe snd-dummy
sudo sh .github/workflows/prep_dummy_soundcard.sh # Try this again.
# for PyQt:
sudo apt-get install -y -qq libdbus-1-3 libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1
sudo apt-get install -y -qq libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0
export LIBGL_ALWAYS_INDIRECT=0
export QT_DEBUG_PLUGINS=1 # let us know about missing dependencies?
- name: Install wxPython from source for linux
if: runner.os=='Linux'
run: |
# these are needed to build wxPython
pip install pip install -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-20.04/ wxPython
- name: Install packaging and testing libs
run: |
python -m pip install --upgrade pip
pip install pytest pytest-cov flake8 xmlschema
pip install wheel
pip install six # for configobj and wxpython
pip install distro # used by createInitFile
pip install mock
pip install psychopy
- name: Test with Pytest
run: |
xvfb-run -a --server-args="-screen 0 1024x768x24" pytest
Binary file modified PyHab User Manual.pdf
Binary file not shown.
58 changes: 39 additions & 19 deletions PyHab/PyHabBuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def __init__(self, loadedSaved=False, settingsDict={}):
'movieEnd': [],
'maxOff': {},
'minOn': {},
'maxOn': {},
'durationCriterion': [],
'autoRedo': [],
'onTimeDeadline': {},
Expand Down Expand Up @@ -112,6 +113,8 @@ def __init__(self, loadedSaved=False, settingsDict={}):
self.settings['loadSep'] = '0'
if 'hppStimScrOnly' not in self.settings.keys():
self.settings['hppStimScrOnly'] = '[]'
if 'maxOn' not in self.settings.keys():
self.settings['maxOn'] = '{}'
# Settings requiring evaluation to get sensible values. Mostly dicts.
evalList = ['dataColumns','blockSum','trialSum','maxDur','condList','baseCondList','movieEnd','playThrough',
'trialOrder','stimNames', 'stimList', 'ISI', 'maxOff','minOn','durationCriterion','autoRedo',
Expand Down Expand Up @@ -372,7 +375,7 @@ def __init__(self, loadedSaved=False, settingsDict={}):
self.buttonList['functions'].append(self.addStimToLibraryDlg)

if len(list(self.settings['stimList'].keys())) > 0:
addMovButton = visual.Rect(self.win, width=.3, height=.5 * (.2 / self.aspect), pos=[0, -.65],
addMovButton = visual.Rect(self.win, width=.3, height=.5 * (.2 / self.aspect), pos=[.4, -.65],
fillColor="white")
addMovText = visual.TextStim(self.win, text="Add stimulus files \nto trial types", color="black",
height=addMovButton.height * .3, alignHoriz='center', pos=addMovButton.pos)
Expand All @@ -382,7 +385,7 @@ def __init__(self, loadedSaved=False, settingsDict={}):


if len(list(self.settings['blockList'].keys())) > 0: # Add button for block data settings
blockDataButton = visual.Rect(self.win, width=.15, height=.5*(.2/self.aspect), pos=[-.65, -.65],
blockDataButton = visual.Rect(self.win, width=.15, height=.5*(.2/self.aspect), pos=[-.725, -.65],
fillColor="white")
blockDataText = visual.TextStim(self.win, text="Block \ndata", color="black",
height=blockDataButton.height*.3, alignHoriz='center',pos=blockDataButton.pos)
Expand All @@ -396,7 +399,7 @@ def __init__(self, loadedSaved=False, settingsDict={}):
self.buttonList['text'][dataIndex].pos = [-.875, -.65]

if len(self.settings['trialTypes']) > 0:
advTrialButton = visual.Rect(self.win, width=.3, height=.5 * (.2 / self.aspect), pos=[.4, -.65], fillColor="white")
advTrialButton = visual.Rect(self.win, width=.3, height=.5 * (.2 / self.aspect), pos=[0, -.65], fillColor="white")
advTrialText = visual.TextStim(self.win, text="Advanced trial \nsettings", color="black",
height=advTrialButton.height * .3, alignHoriz='center', pos=advTrialButton.pos)
self.buttonList['shapes'].append(advTrialButton)
Expand Down Expand Up @@ -566,7 +569,9 @@ def trialTypeDlg(self, trialType="TrialTypeNew", makeNew=True, prevInfo=[]):
11 = inter-stimulus interveral (ISI) for this trial type
[if movies assigned to trial type already, they occupy 12 - N]
12 = Maximum on-time (single use case, for new gaze contingent trial type mode).
[if movies assigned to trial type already, they occupy 13 - N]
:param trialType: Name of the trial type
:type trialType: str
Expand Down Expand Up @@ -596,12 +601,17 @@ def trialTypeDlg(self, trialType="TrialTypeNew", makeNew=True, prevInfo=[]):
maxOff = self.settings['maxOff'][trialType]
minOn = self.settings['minOn'][trialType]
ISI = self.settings['ISI'][trialType]
if trialType in self.settings['maxOn'].keys():
maxOn = self.settings['maxOn'][trialType]
else:
maxOn = 5.0

else:
typeDlg.addField("Max duration", prevInfo[1]) # Index 1
maxOff = prevInfo[3]
minOn = prevInfo[4]
ISI = prevInfo[11]
maxOn = prevInfo[12]

# Find the index of the existing trial type in the study flow and type pane.
flowIndexes=[]
Expand All @@ -610,32 +620,38 @@ def trialTypeDlg(self, trialType="TrialTypeNew", makeNew=True, prevInfo=[]):
flowIndexes.append(i)
typeIndex = self.trialTypesArray['labels'].index(trialType)
if self.settings['playThrough'][trialType] == 3:
chz = ["EitherOr", "Yes", "OnOnly", "No"]
chz = ["EitherOr", "Yes", "OnOnly", "No", "MaxOff/MaxOn"]
elif self.settings['playThrough'][trialType] == 2:
chz = ["No", "OnOnly", "Yes", "EitherOr"]
chz = ["No", "OnOnly", "Yes", "EitherOr", "MaxOff/MaxOn"]
elif self.settings['playThrough'][trialType] == 1:
chz = ["OnOnly", "Yes", "No", "EitherOr"]
chz = ["OnOnly", "Yes", "No", "EitherOr", "MaxOff/MaxOn"]
elif self.settings['playThrough'][trialType] == 4:
chz = ["MaxOff/MaxOn", "Yes", "No", "OnOnly", "EitherOr"]
else:
chz = ["Yes", "OnOnly", "No", "EitherOr"]
chz = ["Yes", "OnOnly", "No", "EitherOr", "MaxOff/MaxOn"]
elif len(prevInfo) > 0:
typeDlg.addField("Max duration", prevInfo[1]) # Index 1
maxOff = prevInfo[3]
minOn = prevInfo[4]
ISI = prevInfo[11]
maxOn = prevInfo[12]
if prevInfo[2] == 3:
chz = ["EitherOr", "Yes", "OnOnly", "No"]
chz = ["EitherOr", "Yes", "OnOnly", "No", "MaxOff/MaxOn"]
elif prevInfo[2] == 2:
chz = ["No", "Yes", "OnOnly", "EitherOr"]
chz = ["No", "Yes", "OnOnly", "EitherOr", "MaxOff/MaxOn"]
elif prevInfo[2] == 1:
chz = ["OnOnly", "Yes", "EitherOr", "No"]
chz = ["OnOnly", "Yes", "EitherOr", "No", "MaxOff/MaxOn"]
elif prevInfo[2] == 4:
chz = ["MaxOff/MaxOn", "Yes", "OnOnly", "EitherOr", "No"]
else:
chz = ["Yes", "OnOnly", "EitherOr", "No"]
chz = ["Yes", "OnOnly", "EitherOr", "No","MaxOff/MaxOn"]
else: # if there are no existing indexes to refer to
typeDlg.addField("Max duration", 60.0) # Index 1
maxOff = 2.0
minOn = 1.0
ISI = 0.0
chz = ["Yes", "OnOnly", "EitherOr", "No"]
maxOn = 5.0
chz = ["Yes", "OnOnly", "EitherOr", "No","MaxOff/MaxOn"]
typeDlg.addField("Gaze-contingent trial type (next three lines ignored otherwise)", choices=chz) # Index 2
typeDlg.addField("Number of continuous seconds looking away to end trial", maxOff) # Index 3
typeDlg.addField("Minimum time looking at screen before stimuli can be ended (not consecutive)", minOn) # Index 4
Expand Down Expand Up @@ -694,6 +710,7 @@ def trialTypeDlg(self, trialType="TrialTypeNew", makeNew=True, prevInfo=[]):
chz4 = False
typeDlg.addField("Only end trial on end of movie repetition? (Only works when presenting stimuli)", initial = chz4) # Index 10
typeDlg.addField("Inter-stimulus interval on loops (pause between end of one loop and start of next)", ISI) # Index 11
typeDlg.addField("MAXIMUM on-time (only used for 'max-off and max-on' gaze contingent setting", maxOn) # Index 12
if not makeNew:
if len(prevInfo) == 0:
if len(self.settings['stimNames'][trialType]) > 0:
Expand All @@ -703,13 +720,13 @@ def trialTypeDlg(self, trialType="TrialTypeNew", makeNew=True, prevInfo=[]):
typeDlg.addField(self.settings['stimNames'][trialType][i]['C'], initial=True) # HPP defaults to C on everything
else:
typeDlg.addField(self.settings['stimNames'][trialType][i], initial=True)
elif len(prevInfo) > 12: # If there were no movies to start with, this will have a length of 12.
elif len(prevInfo) > 13: # If there were no movies to start with, this will have a length of 13.
typeDlg.addText("Current stimuli in trial type (uncheck to remove)")
for i in range(0,len(self.settings['stimNames'][trialType])):
if self.settings['prefLook'] in [2,'2']:
typeDlg.addField(self.settings['stimNames'][trialType][i+9]['C'], initial=prevInfo[i + 12])
typeDlg.addField(self.settings['stimNames'][trialType][i+9]['C'], initial=prevInfo[i + 13])
else:
typeDlg.addField(self.settings['stimNames'][trialType][i], initial=prevInfo[i+12])
typeDlg.addField(self.settings['stimNames'][trialType][i], initial=prevInfo[i+13])


typeInfo = typeDlg.show()
Expand Down Expand Up @@ -785,6 +802,7 @@ def trialTypeDlg(self, trialType="TrialTypeNew", makeNew=True, prevInfo=[]):
self.settings['maxOff'][trialType] = typeInfo[3]
self.settings['minOn'][trialType] = typeInfo[4]
self.settings['ISI'][trialType] = typeInfo[11]
self.settings['maxOn'][trialType] = typeInfo[12]

# Gaze-contingency settings
if trialType not in self.settings['playThrough'].keys(): #Initialize if needed.
Expand All @@ -797,6 +815,8 @@ def trialTypeDlg(self, trialType="TrialTypeNew", makeNew=True, prevInfo=[]):
self.settings['playThrough'][trialType] = 1
elif typeInfo[2] == "EitherOr" and self.settings['playThrough'][trialType] is not 3:
self.settings['playThrough'][trialType] = 3
elif typeInfo[2] == "MaxOff/MaxOn" and self.settings['playThrough'][trialType] is not 4:
self.settings['playThrough'][trialType] = 4

# Auto-redo trial settings
if typeInfo[5] in [False,0,'False','0'] and trialType in self.settings['autoRedo']: #gaze-contingent trial type, not already tagged as such.
Expand Down Expand Up @@ -842,10 +862,10 @@ def trialTypeDlg(self, trialType="TrialTypeNew", makeNew=True, prevInfo=[]):
self.settings['movieEnd'].append(trialType)

# Remove stimuli if needed
if len(typeInfo) > 12: #Again, if there were movies to list.
if len(typeInfo) > 13: #Again, if there were movies to list.
tempMovies = [] #This will just replace the stimNames list
for i in range(0,len(self.settings['stimNames'][trialType])):
if typeInfo[i+12]:
if typeInfo[i+13]:
tempMovies.append(self.settings['stimNames'][trialType][i])
self.settings['stimNames'][trialType] = tempMovies

Expand All @@ -863,7 +883,7 @@ def trialTypeDlg(self, trialType="TrialTypeNew", makeNew=True, prevInfo=[]):
warnDlg.show()
if self.advTrialSetup not in self.buttonList['functions']:
advTrialButton = visual.Rect(self.win, width=.3, height=.5 * (.2 / self.aspect),
pos=[.8, -.65], fillColor="white")
pos=[0, -.65], fillColor="white")
advTrialText = visual.TextStim(self.win, text="Advanced trial \nsettings",
color="black",
height=advTrialButton.height * .3, alignHoriz='center',
Expand Down

0 comments on commit 837bd63

Please sign in to comment.