[settings] extract tasksettings class

This commit is contained in:
Jan Grewe 2021-03-08 23:30:51 +01:00
parent 4695d60cd1
commit 2b5982eabb
3 changed files with 198 additions and 251 deletions

View File

@ -1,105 +1,13 @@
from PyQt5.QtWidgets import QAction, QComboBox, QFormLayout, QGridLayout, QLabel, QPushButton, QSizePolicy, QSlider, QSpinBox, QSplitter, QTextEdit, QVBoxLayout, QWidget from PyQt5.QtWidgets import QAction, QGridLayout, QLabel, QPushButton, QSizePolicy, QSplitter, QVBoxLayout, QWidget
from PyQt5.QtCore import QPoint, QRandomGenerator, QTimer, Qt, endl, pyqtSignal from PyQt5.QtCore import QPoint, QRandomGenerator, QTimer, Qt, pyqtSignal
from PyQt5.QtGui import QColor, QFont, QIcon, QKeySequence, QPainter, QPen, QPixmap from PyQt5.QtGui import QColor, QFont, QIcon, QKeySequence, QPainter, QPen, QPixmap
from PyQt5.QtMultimedia import QMediaPlayer from PyQt5.QtMultimedia import QMediaPlayer
import os
import blipblop.constants as cnst import blipblop.constants as cnst
from blipblop.ui.countdownlabel import CountdownLabel from blipblop.ui.countdownlabel import CountdownLabel
from blipblop.ui.settings import AuditoryTaskSettings
import datetime as dt import datetime as dt
class SettingsPanel(QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self._trial_spinner = QSpinBox()
self._trial_spinner.setMinimum(5)
self._trial_spinner.setMaximum(25)
self._trial_spinner.setValue(5)
self._trial_spinner.setToolTip("Number of consecutive trials (5 - 25)")
self._min_delay_spinner = QSpinBox()
self._min_delay_spinner.setMinimum(1)
self._min_delay_spinner.setMaximum(10)
self._min_delay_spinner.setValue(1)
self._min_delay_spinner.setToolTip("Minimum delay between start of trial and stimulus display [s]")
self._max_delay_spinner = QSpinBox()
self._max_delay_spinner.setMinimum(1)
self._max_delay_spinner.setMaximum(10)
self._max_delay_spinner.setValue(5)
self._max_delay_spinner.setToolTip("Maximum delay between start of trial and stimulus display [s]")
self._countdown_spinner = QSpinBox()
self._countdown_spinner.setMinimum(1)
self._countdown_spinner.setMaximum(10)
self._countdown_spinner.setValue(3)
self._countdown_spinner.setToolTip("Pause between trials [s]")
self._saliency_slider = QSlider(Qt.Horizontal)
self._saliency_slider.setMinimum(0)
self._saliency_slider.setMaximum(100)
self._saliency_slider.setSliderPosition(100)
self._saliency_slider.setTickInterval(25)
self._saliency_slider.setTickPosition(QSlider.TicksBelow)
self._saliency_slider.setToolTip("Saliency of the stimulus, i.e. its loudness")
self._sound_combo = QComboBox()
for k in cnst.SNDS_DICT.keys():
self._sound_combo.addItem(k)
self._instructions = QTextEdit()
self._instructions.setMarkdown("* fixate central cross\n * press start (enter) when ready\n * press space bar as soon as the stimulus occurs")
self._instructions.setMinimumHeight(200)
self._instructions.setReadOnly(True)
form_layout = QFormLayout()
form_layout.addRow("Settings", None)
form_layout.addRow("number of trials", self._trial_spinner)
form_layout.addRow("minimum delay [s]", self._min_delay_spinner)
form_layout.addRow("maximum delay [s]", self._max_delay_spinner)
form_layout.addRow("pause [s]", self._countdown_spinner)
form_layout.addRow("stimulus saliency", self._saliency_slider)
form_layout.addRow("stimulus sound", self._sound_combo)
form_layout.addRow("instructions", self._instructions)
self.setLayout(form_layout)
@property
def trials(self):
return self._trial_spinner.value()
@property
def saliency(self):
return self._saliency_slider.sliderPosition()
@property
def size(self):
return self._size_slider.sliderPosition()
@property
def min_delay(self):
return self._min_delay_spinner.value()
@property
def max_delay(self):
return self._max_delay_spinner.value()
@property
def countdown(self):
return self._countdown_spinner.value()
@property
def sound(self):
return self._sound_combo.currentText()
def set_enabled(self, enabled):
self._trial_spinner.setEnabled(enabled)
self._saliency_slider.setEnabled(enabled)
self._countdown_spinner.setEnabled(enabled)
self._min_delay_spinner.setEnabled(enabled)
self._max_delay_spinner.setEnabled(enabled)
self._sound_combo.setEnabled(enabled)
class AudioBlop(QWidget): class AudioBlop(QWidget):
task_done = pyqtSignal() task_done = pyqtSignal()
@ -107,7 +15,7 @@ class AudioBlop(QWidget):
def __init__(self, parent=None) -> None: def __init__(self, parent=None) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
widget = QWidget() widget = QWidget()
grid = QGridLayout() grid = QGridLayout()
grid.setColumnStretch(0, 1) grid.setColumnStretch(0, 1)
@ -116,37 +24,37 @@ class AudioBlop(QWidget):
grid.setRowStretch(3, 1) grid.setRowStretch(3, 1)
widget.setLayout(grid) widget.setLayout(grid)
l = QLabel("Auditory reaction test") icon_label = QLabel("Auditory reaction test")
l.setPixmap(QPixmap(":/icons/auditory_task")) icon_label.setPixmap(QPixmap(":/icons/auditory_task"))
grid.addWidget(l, 0, 0, Qt.AlignLeft) grid.addWidget(icon_label, 0, 0, Qt.AlignLeft)
l2 =QLabel("Measurement of auditory reaction times\npress enter to start") heading_label = QLabel("Measurement of auditory reaction times\npress enter to start")
font = QFont() font = QFont()
font.setBold(True) font.setBold(True)
font.setPointSize(20) font.setPointSize(20)
l2.setFont(font) heading_label.setFont(font)
l2.setStyleSheet("color: #2D4B9A") heading_label.setStyleSheet("color: #2D4B9A")
grid.addWidget(l2, 1, 0, 1, 2, Qt.AlignLeft) grid.addWidget(heading_label, 1, 0, 1, 2, Qt.AlignLeft)
settings_btn = QPushButton(QIcon(":/icons/settings"), "") settings_btn = QPushButton(QIcon(":/icons/settings"), "")
settings_btn.setToolTip("edit task settings") settings_btn.setToolTip("edit task settings")
settings_btn.setShortcut(QKeySequence("alt+s")) settings_btn.setShortcut(QKeySequence("alt+s"))
settings_btn.clicked.connect(self.on_toggle_settings) settings_btn.clicked.connect(self.on_toggle_settings)
grid.addWidget(settings_btn, 0, 3, Qt.AlignRight) grid.addWidget(settings_btn, 0, 3, Qt.AlignRight)
self._status_label = QLabel("Ready to start, press enter ...") self._status_label = QLabel("Ready to start, press enter ...")
grid.addWidget(self._status_label, 4, 0, Qt.AlignLeft) grid.addWidget(self._status_label, 4, 0, Qt.AlignLeft)
self._countdown_label = CountdownLabel(text="Next trial in:") self._countdown_label = CountdownLabel(text="Next trial in:")
grid.addWidget(self._countdown_label, 4, 1, Qt.AlignCenter) grid.addWidget(self._countdown_label, 4, 1, Qt.AlignCenter)
self._countdown_label.countdown_done.connect(self.run_trial) self._countdown_label.countdown_done.connect(self.run_trial)
self._draw_area = QLabel() self._draw_area = QLabel()
self._draw_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._draw_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
grid.addWidget(self._draw_area, 2, 1) grid.addWidget(self._draw_area, 2, 1)
self._settings = SettingsPanel() self._settings = AuditoryTaskSettings()
self._splitter = QSplitter() self._splitter = QSplitter()
self._splitter.addWidget(widget) self._splitter.addWidget(widget)
self._splitter.addWidget(self._settings) self._splitter.addWidget(self._settings)
@ -155,7 +63,7 @@ class AudioBlop(QWidget):
vbox = QVBoxLayout() vbox = QVBoxLayout()
vbox.addWidget(self._splitter) vbox.addWidget(self._splitter)
self.setLayout(vbox) self.setLayout(vbox)
self.reset_canvas() self.reset_canvas()
self.create_actions() self.create_actions()
@ -168,12 +76,12 @@ class AudioBlop(QWidget):
self._player = QMediaPlayer() self._player = QMediaPlayer()
self._random_generator = QRandomGenerator() self._random_generator = QRandomGenerator()
self.setFocus() self.setFocus()
def create_actions(self): def create_actions(self):
self._start_action = QAction("start trial") self._start_action = QAction("start trial")
self._start_action.setShortcuts([QKeySequence("enter"), QKeySequence("return")]) self._start_action.setShortcuts([QKeySequence("enter"), QKeySequence("return")])
self._start_action.triggered.connect(self.on_trial_start) self._start_action.triggered.connect(self.on_trial_start)
self._reaction = QAction("reaction") self._reaction = QAction("reaction")
self._reaction.setShortcut(QKeySequence("space")) self._reaction.setShortcut(QKeySequence("space"))
self._reaction.triggered.connect(self.on_reaction) self._reaction.triggered.connect(self.on_reaction)
@ -181,7 +89,7 @@ class AudioBlop(QWidget):
self._abort = QAction("abort") self._abort = QAction("abort")
self._abort.setShortcut(QKeySequence("escape")) self._abort.setShortcut(QKeySequence("escape"))
self._abort.triggered.connect(self.on_abort) self._abort.triggered.connect(self.on_abort)
self.addAction(self._start_action) self.addAction(self._start_action)
self.addAction(self._reaction) self.addAction(self._reaction)
self.addAction(self._abort) self.addAction(self._abort)
@ -189,7 +97,7 @@ class AudioBlop(QWidget):
def on_reaction(self): def on_reaction(self):
if not self._session_running or not self._trial_running: if not self._session_running or not self._trial_running:
return return
self._response_time = dt.datetime.now() self._response_time = dt.datetime.now()
if self._trial_counter < self._settings.trials: if self._trial_counter < self._settings.trials:
self._status_label.setText("Trial %i of %i" % (self._trial_counter, self._settings.trials)) self._status_label.setText("Trial %i of %i" % (self._trial_counter, self._settings.trials))
@ -206,14 +114,13 @@ class AudioBlop(QWidget):
return return
self._countdown_label.start(self._settings.countdown) self._countdown_label.start(self._settings.countdown)
def reset_canvas(self): def reset_canvas(self):
bkg_color = QColor() bkg_color = QColor()
bkg_color.setAlphaF(0.0) bkg_color.setAlphaF(0.0)
canvas = QPixmap(400, 400) canvas = QPixmap(400, 400)
self._canvas_center = QPoint(200, 200) self._canvas_center = QPoint(200, 200)
canvas.fill(bkg_color) canvas.fill(bkg_color)
self.draw_fixation(canvas) self.draw_fixation(canvas)
self._draw_area.setPixmap(canvas) self._draw_area.setPixmap(canvas)
self._draw_area.update() self._draw_area.update()
@ -222,7 +129,7 @@ class AudioBlop(QWidget):
right = QPoint(225, 200) right = QPoint(225, 200)
top = QPoint(200, 175) top = QPoint(200, 175)
bottom = QPoint(200, 225) bottom = QPoint(200, 225)
painter = QPainter(pixmap) painter = QPainter(pixmap)
painter.setPen(QPen(Qt.red, 2, Qt.SolidLine)) painter.setPen(QPen(Qt.red, 2, Qt.SolidLine))
painter.drawLine(left, right) painter.drawLine(left, right)
painter.drawLine(top, bottom) painter.drawLine(top, bottom)
@ -234,7 +141,7 @@ class AudioBlop(QWidget):
def blip(self): def blip(self):
self._player.play() self._player.play()
self._start_time = dt.datetime.now() self._start_time = dt.datetime.now()
def on_trial_start(self): def on_trial_start(self):
if self._trial_running: if self._trial_running:
return return
@ -251,9 +158,10 @@ class AudioBlop(QWidget):
self._trial_counter += 1 self._trial_counter += 1
self._status_label.setText("Trial %i of %i running" % (self._trial_counter, self._settings.trials)) self._status_label.setText("Trial %i of %i running" % (self._trial_counter, self._settings.trials))
content = cnst.get_sound(self._settings.sound) content = cnst.get_sound(self._settings.sound)
self._player.setMedia(content) self._player.setMedia(content)
self._player.setVolume(self._settings.saliency) self._player.setVolume(self._settings.saliency)
min_interval = int(self._settings.min_delay * 10) min_interval = int(self._settings.min_delay * 10)
max_interval = int(self._settings.max_delay * 10) max_interval = int(self._settings.max_delay * 10)
interval = self._random_generator.bounded(min_interval, max_interval) * 100 interval = self._random_generator.bounded(min_interval, max_interval) * 100
@ -263,15 +171,15 @@ class AudioBlop(QWidget):
self._timer.setInterval(int(interval)) self._timer.setInterval(int(interval))
self._timer.timeout.connect(self.blip) self._timer.timeout.connect(self.blip)
self._timer.start() self._timer.start()
def on_abort(self): def on_abort(self):
self.reset() self.reset()
self.task_aborted.emit() self.task_aborted.emit()
@property @property
def results(self): def results(self):
return self._reaction_times return self._reaction_times
def reset(self): def reset(self):
self._trial_counter = 0 self._trial_counter = 0
self._session_running = 0 self._session_running = 0
@ -281,7 +189,7 @@ class AudioBlop(QWidget):
self._status_label.setText("Ready to start...") self._status_label.setText("Ready to start...")
self._settings.set_enabled(True) self._settings.set_enabled(True)
self._countdown_label.stop() self._countdown_label.stop()
def on_toggle_settings(self): def on_toggle_settings(self):
if self._splitter.sizes()[1] > 0: if self._splitter.sizes()[1] > 0:
self._splitter.widget(1).hide() self._splitter.widget(1).hide()

132
blipblop/ui/settings.py Normal file
View File

@ -0,0 +1,132 @@
from PyQt5.QtWidgets import QComboBox, QFormLayout, QSlider, QSpinBox, QTextEdit, QWidget
from PyQt5.QtCore import Qt
import blipblop.constants as cnst
class TaskSettings(QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self._trial_spinner = QSpinBox()
self._trial_spinner.setMinimum(5)
self._trial_spinner.setMaximum(25)
self._trial_spinner.setValue(5)
self._trial_spinner.setToolTip("Number of consecutive trials (5 - 25)")
self._min_delay_spinner = QSpinBox()
self._min_delay_spinner.setMinimum(1)
self._min_delay_spinner.setMaximum(10)
self._min_delay_spinner.setValue(1)
self._min_delay_spinner.setToolTip("Minimum delay between start of trial and stimulus display [s]")
self._max_delay_spinner = QSpinBox()
self._max_delay_spinner.setMinimum(1)
self._max_delay_spinner.setMaximum(10)
self._max_delay_spinner.setValue(5)
self._max_delay_spinner.setToolTip("Maximum delay between start of trial and stimulus display [s]")
self._countdown_spinner = QSpinBox()
self._countdown_spinner.setMinimum(1)
self._countdown_spinner.setMaximum(10)
self._countdown_spinner.setValue(3)
self._countdown_spinner.setToolTip("Pause between trials [s]")
self._saliency_slider = QSlider(Qt.Horizontal)
self._saliency_slider.setMinimum(0)
self._saliency_slider.setMaximum(100)
self._saliency_slider.setSliderPosition(100)
self._saliency_slider.setTickInterval(25)
self._saliency_slider.setTickPosition(QSlider.TicksBelow)
self._instructions = QTextEdit()
self._instructions.setMarkdown("* fixate central cross\n * press start (enter) when ready\n * press space bar as soon as the stimulus occurs")
self._instructions.setMinimumHeight(200)
self._instructions.setReadOnly(True)
self.form_layout = QFormLayout()
self.form_layout.addRow("Settings", None)
self.form_layout.addRow("number of trials", self._trial_spinner)
self.form_layout.addRow("minimum delay [s]", self._min_delay_spinner)
self.form_layout.addRow("maximum delay [s]", self._max_delay_spinner)
self.form_layout.addRow("pause [s]", self._countdown_spinner)
self.form_layout.addRow("stimulus saliency", self._saliency_slider)
self.form_layout.addRow("instructions", self._instructions)
self.setLayout(self.form_layout)
@property
def trials(self):
return self._trial_spinner.value()
@property
def saliency(self):
return self._saliency_slider.sliderPosition()
@property
def size(self):
return self._size_slider.sliderPosition()
@property
def min_delay(self):
return self._min_delay_spinner.value()
@property
def max_delay(self):
return self._max_delay_spinner.value()
@property
def countdown(self):
return self._countdown_spinner.value()
def set_enabled(self, enabled):
self._trial_spinner.setEnabled(enabled)
self._saliency_slider.setEnabled(enabled)
self._countdown_spinner.setEnabled(enabled)
self._min_delay_spinner.setEnabled(enabled)
self._max_delay_spinner.setEnabled(enabled)
class AuditoryTaskSettings(TaskSettings):
def __init__(self, parent=None):
super().__init__(parent=parent)
self._saliency_slider.setToolTip("Saliency of the stimulus, i.e. its loudness")
self._sound_combo = QComboBox()
for k in cnst.SNDS_DICT.keys():
self._sound_combo.addItem(k)
self.form_layout.insertRow(self.form_layout.rowCount() -1, "stimulus sound", self._sound_combo)
def set_enabled(self, enabled):
super().set_enabled(enabled)
self._sound_combo.setEnabled(enabled)
@property
def sound(self):
return self._sound_combo.currentText()
class VisualTaskSettings(TaskSettings):
def __init__(self, parent=None):
super().__init__(parent=parent)
self._saliency_slider.setToolTip("Saliency of the stimulus, i.e. its opacity")
self._size_slider = QSlider(Qt.Horizontal)
self._size_slider.setMinimum(0)
self._size_slider.setMaximum(200)
self._size_slider.setSliderPosition(100)
self._size_slider.setTickInterval(25)
self._size_slider.setTickPosition(QSlider.TicksBelow)
self._size_slider.setToolTip("Diameter of the stimulus in pixel")
self.form_layout.insertRow(self.form_layout.rowCount() -1, "stimulus size", self._size_slider)
def set_enabled(self, enabled):
super().set_enabled(enabled)
self._size_slider.setEnabled(enabled)
@property
def size(self):
return self._size_slider.sliderPosition()

View File

@ -1,104 +1,11 @@
from PyQt5.QtWidgets import QAction, QFormLayout, QGridLayout, QLabel, QLineEdit, QPushButton, QSizePolicy, QSlider, QSpinBox, QSplitter, QTextEdit, QVBoxLayout, QWidget from PyQt5.QtWidgets import QAction, QGridLayout, QLabel, QPushButton, QSizePolicy, QSplitter, QVBoxLayout, QWidget
from PyQt5.QtCore import QPoint, QRandomGenerator, QTimer, Qt, pyqtSignal, QSettings from PyQt5.QtCore import QPoint, QRandomGenerator, QTimer, Qt, pyqtSignal
from PyQt5.QtGui import QColor, QFont, QIcon, QKeySequence, QPainter, QBrush, QPen, QPixmap from PyQt5.QtGui import QColor, QFont, QIcon, QKeySequence, QPainter, QBrush, QPen, QPixmap
import os
import blipblop.constants as cnst
from blipblop.ui.countdownlabel import CountdownLabel from blipblop.ui.countdownlabel import CountdownLabel
from blipblop.ui.settings import VisualTaskSettings
import datetime as dt import datetime as dt
class SettingsPanel(QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self._trial_spinner = QSpinBox()
self._trial_spinner.setMinimum(5)
self._trial_spinner.setMaximum(25)
self._trial_spinner.setValue(5)
self._trial_spinner.setToolTip("Number of consecutive trials (5 - 25)")
self._min_delay_spinner = QSpinBox()
self._min_delay_spinner.setMinimum(1)
self._min_delay_spinner.setMaximum(10)
self._min_delay_spinner.setValue(1)
self._min_delay_spinner.setToolTip("Minimum delay between start of trial and stimulus display [s]")
self._max_delay_spinner = QSpinBox()
self._max_delay_spinner.setMinimum(1)
self._max_delay_spinner.setMaximum(10)
self._max_delay_spinner.setValue(5)
self._max_delay_spinner.setToolTip("Maximum delay between start of trial and stimulus display [s]")
self._saliency_slider = QSlider(Qt.Horizontal)
self._saliency_slider.setMinimum(0)
self._saliency_slider.setMaximum(100)
self._saliency_slider.setSliderPosition(100)
self._saliency_slider.setTickInterval(25)
self._saliency_slider.setTickPosition(QSlider.TicksBelow)
self._saliency_slider.setToolTip("Saliency of the stimulus, i.e. its opacity")
self._size_slider = QSlider(Qt.Horizontal)
self._size_slider.setMinimum(0)
self._size_slider.setMaximum(200)
self._size_slider.setSliderPosition(100)
self._size_slider.setTickInterval(25)
self._size_slider.setTickPosition(QSlider.TicksBelow)
self._size_slider.setToolTip("Diameter of the stimulus in pixel")
self._countdown_spinner = QSpinBox()
self._countdown_spinner.setMinimum(2)
self._countdown_spinner.setMaximum(30)
self._countdown_spinner.setValue(5)
self._countdown_spinner.setToolTip("Pause/countdown for next trial")
self._instructions = QTextEdit()
self._instructions.setMarkdown("* fixate central cross\n * press start (enter) when ready\n * press space bar as soon as the stimulus occurs")
self._instructions.setMinimumHeight(200)
self._instructions.setReadOnly(True)
form_layout = QFormLayout()
form_layout.addRow("Settings", None)
form_layout.addRow("number of trials", self._trial_spinner)
form_layout.addRow("pause until next trial [s]", self._countdown_spinner)
form_layout.addRow("minimum delay [s]", self._min_delay_spinner)
form_layout.addRow("maximum delay [s]", self._max_delay_spinner)
form_layout.addRow("stimulus saliency", self._saliency_slider)
form_layout.addRow("stimulus size", self._size_slider)
form_layout.addRow("instructions", self._instructions)
self.setLayout(form_layout)
@property
def trials(self):
return self._trial_spinner.value()
@property
def saliency(self):
return self._saliency_slider.sliderPosition()
@property
def size(self):
return self._size_slider.sliderPosition()
@property
def min_delay(self):
return self._min_delay_spinner.value()
@property
def max_delay(self):
return self._max_delay_spinner.value()
@property
def countdown(self):
return self._countdown_spinner.value()
def set_enabled(self, enabled):
self._trial_spinner.setEnabled(enabled)
self._saliency_slider.setEnabled(enabled)
self._size_slider.setEnabled(enabled)
self._countdown_spinner.setEnabled(enabled)
self._min_delay_spinner.setEnabled(enabled)
self._max_delay_spinner.setEnabled(enabled)
class VisualBlip(QWidget): class VisualBlip(QWidget):
task_done = pyqtSignal() task_done = pyqtSignal()
@ -106,7 +13,7 @@ class VisualBlip(QWidget):
def __init__(self, parent=None) -> None: def __init__(self, parent=None) -> None:
super().__init__(parent=parent) super().__init__(parent=parent)
widget = QWidget() widget = QWidget()
grid = QGridLayout() grid = QGridLayout()
grid.setColumnStretch(0, 1) grid.setColumnStretch(0, 1)
@ -115,37 +22,37 @@ class VisualBlip(QWidget):
grid.setRowStretch(3, 1) grid.setRowStretch(3, 1)
widget.setLayout(grid) widget.setLayout(grid)
l = QLabel("Visual reaction test") icon_label = QLabel("Visual reaction test")
l.setPixmap(QPixmap(":/icons/visual_task")) icon_label.setPixmap(QPixmap(":/icons/visual_task"))
grid.addWidget(l, 0, 0, Qt.AlignLeft) grid.addWidget(icon_label, 0, 0, Qt.AlignLeft)
l2 = QLabel("Measurement of visual reaction times\npress enter to start") heading_label = QLabel("Measurement of visual reaction times\npress enter to start")
font = QFont() font = QFont()
font.setBold(True) font.setBold(True)
font.setPointSize(20) font.setPointSize(20)
l2.setFont(font) heading_label.setFont(font)
l2.setStyleSheet("color: #2D4B9A") heading_label.setStyleSheet("color: #2D4B9A")
grid.addWidget(l2, 1, 0, 1, 2, Qt.AlignLeft) grid.addWidget(heading_label, 1, 0, 1, 2, Qt.AlignLeft)
settings_btn = QPushButton(QIcon(":/icons/settings"), "") settings_btn = QPushButton(QIcon(":/icons/settings"), "")
settings_btn.setToolTip("edit task settings") settings_btn.setToolTip("edit task settings")
settings_btn.setShortcut(QKeySequence("alt+s")) settings_btn.setShortcut(QKeySequence("alt+s"))
settings_btn.clicked.connect(self.on_toggle_settings) settings_btn.clicked.connect(self.on_toggle_settings)
grid.addWidget(settings_btn, 0, 3, Qt.AlignRight) grid.addWidget(settings_btn, 0, 3, Qt.AlignRight)
self._status_label = QLabel("Ready to start, press enter ...") self._status_label = QLabel("Ready to start, press enter ...")
grid.addWidget(self._status_label, 4, 0, Qt.AlignBaseline) grid.addWidget(self._status_label, 4, 0, Qt.AlignBaseline)
self._countdown_label = CountdownLabel(text="Next trial in:") self._countdown_label = CountdownLabel(text="Next trial in:")
grid.addWidget(self._countdown_label, 4, 1, Qt.AlignCenter) grid.addWidget(self._countdown_label, 4, 1, Qt.AlignCenter)
self._countdown_label.countdown_done.connect(self.run_trial) self._countdown_label.countdown_done.connect(self.run_trial)
self._draw_area = QLabel() self._draw_area = QLabel()
self._draw_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._draw_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
grid.addWidget(self._draw_area, 2, 1) grid.addWidget(self._draw_area, 2, 1)
self._settings = SettingsPanel() self._settings = VisualTaskSettings()
self._splitter = QSplitter() self._splitter = QSplitter()
self._splitter.addWidget(widget) self._splitter.addWidget(widget)
self._splitter.addWidget(self._settings) self._splitter.addWidget(self._settings)
@ -154,7 +61,7 @@ class VisualBlip(QWidget):
vbox = QVBoxLayout() vbox = QVBoxLayout()
vbox.addWidget(self._splitter) vbox.addWidget(self._splitter)
self.setLayout(vbox) self.setLayout(vbox)
self.reset_canvas() self.reset_canvas()
self.create_actions() self.create_actions()
@ -167,12 +74,12 @@ class VisualBlip(QWidget):
self._random_generator = QRandomGenerator() self._random_generator = QRandomGenerator()
self.setFocus() self.setFocus()
def create_actions(self): def create_actions(self):
self._start_action = QAction("start trial") self._start_action = QAction("start trial")
self._start_action.setShortcuts([QKeySequence("enter"), QKeySequence("return")]) self._start_action.setShortcuts([QKeySequence("enter"), QKeySequence("return")])
self._start_action.triggered.connect(self.on_trial_start) self._start_action.triggered.connect(self.on_trial_start)
self._reaction = QAction("reaction") self._reaction = QAction("reaction")
self._reaction.setShortcut(QKeySequence("space")) self._reaction.setShortcut(QKeySequence("space"))
self._reaction.triggered.connect(self.on_reaction) self._reaction.triggered.connect(self.on_reaction)
@ -180,7 +87,7 @@ class VisualBlip(QWidget):
self._abort = QAction("abort") self._abort = QAction("abort")
self._abort.setShortcut(QKeySequence("escape")) self._abort.setShortcut(QKeySequence("escape"))
self._abort.triggered.connect(self.on_abort) self._abort.triggered.connect(self.on_abort)
self.addAction(self._start_action) self.addAction(self._start_action)
self.addAction(self._reaction) self.addAction(self._reaction)
self.addAction(self._abort) self.addAction(self._abort)
@ -188,7 +95,7 @@ class VisualBlip(QWidget):
def on_reaction(self): def on_reaction(self):
if not self._session_running or not self._trial_running: if not self._session_running or not self._trial_running:
return return
self._response_time = dt.datetime.now() self._response_time = dt.datetime.now()
if self._trial_counter < self._settings.trials: if self._trial_counter < self._settings.trials:
self._status_label.setText("Trial %i of %i" % (self._trial_counter, self._settings.trials)) self._status_label.setText("Trial %i of %i" % (self._trial_counter, self._settings.trials))
@ -212,7 +119,7 @@ class VisualBlip(QWidget):
canvas = QPixmap(400, 400) canvas = QPixmap(400, 400)
self._canvas_center = QPoint(200, 200) self._canvas_center = QPoint(200, 200)
canvas.fill(bkg_color) canvas.fill(bkg_color)
self.draw_fixation(canvas) self.draw_fixation(canvas)
self._draw_area.setPixmap(canvas) self._draw_area.setPixmap(canvas)
self._draw_area.update() self._draw_area.update()
@ -221,7 +128,7 @@ class VisualBlip(QWidget):
right = QPoint(225, 200) right = QPoint(225, 200)
top = QPoint(200, 175) top = QPoint(200, 175)
bottom = QPoint(200, 225) bottom = QPoint(200, 225)
painter = QPainter(pixmap) painter = QPainter(pixmap)
painter.setPen(QPen(Qt.red, 2, Qt.SolidLine)) painter.setPen(QPen(Qt.red, 2, Qt.SolidLine))
painter.drawLine(left, right) painter.drawLine(left, right)
painter.drawLine(top, bottom) painter.drawLine(top, bottom)
@ -232,24 +139,24 @@ class VisualBlip(QWidget):
def blip(self): def blip(self):
stim_size = self._settings.size stim_size = self._settings.size
painter = QPainter(self._draw_area.pixmap()) painter = QPainter(self._draw_area.pixmap())
painter.setPen(QPen(Qt.red, 1, Qt.SolidLine)) painter.setPen(QPen(Qt.red, 1, Qt.SolidLine))
color = QColor(Qt.red) color = QColor(Qt.red)
color.setAlphaF(self._settings.saliency/100) color.setAlphaF(self._settings.saliency/100)
painter.setBrush(QBrush(color, Qt.SolidPattern)) painter.setBrush(QBrush(color, Qt.SolidPattern))
painter.drawEllipse(self._canvas_center, stim_size, stim_size) painter.drawEllipse(self._canvas_center, stim_size, stim_size)
painter.end() painter.end()
self._start_time = dt.datetime.now() self._start_time = dt.datetime.now()
self._draw_area.update() self._draw_area.update()
def on_trial_start(self): def on_trial_start(self):
if self._trial_running: if self._trial_running:
return return
if not self._session_running: if not self._session_running:
self._settings.set_enabled(False) self._settings.set_enabled(False)
self._session_running = True self._session_running = True
self._countdown_label.start(time=self._settings.countdown) self._countdown_label.start(time=self._settings.countdown)
def run_trial(self): def run_trial(self):
self._trial_running = True self._trial_running = True
if self._trial_counter >= self._settings.trials: if self._trial_counter >= self._settings.trials:
@ -267,15 +174,15 @@ class VisualBlip(QWidget):
self._timer.setInterval(int(interval)) self._timer.setInterval(int(interval))
self._timer.timeout.connect(self.blip) self._timer.timeout.connect(self.blip)
self._timer.start() self._timer.start()
def on_abort(self): def on_abort(self):
self.reset() self.reset()
self.task_aborted.emit() self.task_aborted.emit()
@property @property
def results(self): def results(self):
return self._reaction_times return self._reaction_times
def reset(self): def reset(self):
self.reset_canvas() self.reset_canvas()
self._trial_counter = 0 self._trial_counter = 0
@ -286,7 +193,7 @@ class VisualBlip(QWidget):
self._status_label.setText("Ready to start...") self._status_label.setText("Ready to start...")
self._countdown_label.stop() self._countdown_label.stop()
self._settings.set_enabled(True) self._settings.set_enabled(True)
def on_toggle_settings(self): def on_toggle_settings(self):
if self._splitter.sizes()[1] > 0: if self._splitter.sizes()[1] > 0:
self._splitter.widget(1).hide() self._splitter.widget(1).hide()