This commit is contained in:
Jan Grewe 2021-03-04 17:55:40 +01:00
commit 1a53050174
60 changed files with 737 additions and 0 deletions

0
.gitignore vendored Normal file
View File

10
README.md Normal file
View File

@ -0,0 +1,10 @@
# BlibBlop
A PyQt5 based cross-platform experiment for measuring reaction times in human observers.
## Dependencies
- PyQt5
- numpy

5
blipblob.py Normal file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env python
from blipblop import main
if __name__ == "__main__":
main.main()

0
blipblop/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

25
blipblop/constants.py Normal file
View File

@ -0,0 +1,25 @@
import os
import glob
from PyQt5.QtGui import QIcon
organization = "bendalab"
application = "blipblop"
version = 0.1
PACKAGE_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
ICONS_FOLDER = os.path.join(PACKAGE_ROOT, "icons")
DOCS_ROOT_FILE = os.path.join(PACKAGE_ROOT, "docs", "index.md")
ICONS_PATHS = glob.glob(os.path.join(ICONS_FOLDER, "*.png"))
ICONS_PATHS.extend(glob.glob(os.path.join(ICONS_FOLDER, "*.icns")))
ICONS_PATHS = sorted(ICONS_PATHS)
ICON_DICT = {}
for icon in ICONS_PATHS:
ICON_DICT[icon.split(os.sep)[-1].split(".")[0]] = icon
def get_icon(name):
if name in ICON_DICT.keys():
return QIcon(ICON_DICT[name])
else:
return QIcon("nix_logo.png")

20
blipblop/info.json Normal file
View File

@ -0,0 +1,20 @@
{
"NAME": "BlibBlop",
"VERSION": "0.1",
"DESCRIPTION": "Measure reaction times to visual or auditory signals",
"AUTHOR": "Jan Grewe",
"COPYRIGHT": "(c) 2021, Neuroethology lab, Uni Tuebingen",
"CONTACT": "jan.grewe@g-node.org",
"HOMEPAGE": "https://github.com/bendalab/nixview-python",
"CLASSIFIERS": [
"Programming Language :: Python",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Topic :: Scientific/Engineering",
"Intended Audience :: Science/Research",
"Intended Audience :: End Users/Desktop",
"License :: OSI Approved :: BSD License"
]
}

15
blipblop/main.py Normal file
View File

@ -0,0 +1,15 @@
#!/usr/bin/python3
import sys
from PyQt5.QtWidgets import QApplication
from blipblop.ui.mainwindow import BlipBlop
def main():
app = QApplication(sys.argv)
window = BlipBlop()
window.setMinimumWidth(800)
window.setMinimumHeight(600)
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

0
blipblop/ui/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

58
blipblop/ui/about.py Normal file
View File

@ -0,0 +1,58 @@
import os
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QLabel, QVBoxLayout, QWidget
from PyQt5.QtCore import Qt
import blipblop.constants as cnst
class AboutDialog(QDialog):
def __init__(self, parent=None) -> None:
super().__init__(parent=parent)
self.setModal(True)
about = About(self)
self.setLayout(QVBoxLayout())
self.layout().addWidget(about)
bbox = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok)
bbox.accepted.connect(self.accept)
self.layout().addWidget(bbox)
class About(QWidget):
def __init__(self, parent=None) -> None:
super().__init__(parent=parent)
self.setLayout(QVBoxLayout())
heading = QLabel("BlipBlop")
font = heading.font()
font.setPointSize(18)
font.setBold(True)
heading.setFont(font)
heading.setAlignment(Qt.AlignCenter)
subheading = QLabel("How fast are you?\nmeasure your reaction times to visual and auditory stimuli.\nby Jan Grewe")
subheading.setAlignment(Qt.AlignCenter)
nix_link = QLabel("https://github.com/jgrewe/blipblop")
nix_link.setOpenExternalLinks(True)
nix_link.setAlignment(Qt.AlignCenter)
# rtd_link = QLabel("https://nixio.readthedocs.io/en/master/")
# rtd_link.setOpenExternalLinks(True)
# rtd_link.setAlignment(Qt.AlignCenter)
iconlabel = QLabel()
pixmap = QPixmap(os.path.join(cnst.ICONS_FOLDER, "nix_logo.png"))
s = pixmap.size()
new_height = int(s.height() * 300/s.width())
pixmap = pixmap.scaled(300, new_height, Qt.KeepAspectRatio, Qt.FastTransformation)
iconlabel.setPixmap(pixmap)
iconlabel.setMaximumWidth(300)
iconlabel.setAlignment(Qt.AlignCenter)
iconlabel.setScaledContents(True)
self.layout().addWidget(heading)
self.layout().addWidget(subheading)
self.layout().addWidget(iconlabel)
self.layout().addWidget(nix_link)
# self.layout().addWidget(rtd_link)

22
blipblop/ui/audioblop.py Normal file
View File

@ -0,0 +1,22 @@
from PyQt5.QtWidgets import QComboBox, QFrame, QGroupBox, QHBoxLayout, QLabel, QSplitter, QTextEdit, QVBoxLayout, QWidget
from PyQt5.QtCore import QItemSelectionModel, Qt
import blipblop.constants as cnst
class AudioBlop(QWidget):
def __init__(self, parent=None) -> None:
super().__init__(parent=parent)
vbox = QVBoxLayout()
self.setLayout(vbox)
main_splitter = QSplitter(Qt.Vertical)
self.layout().addWidget(main_splitter)
def reset(self):
pass

View File

@ -0,0 +1,45 @@
from PyQt5.QtWidgets import QStackedLayout, QWidget
from blipblop.ui.startscreen import StartScreen
from blipblop.ui.visualblip import VisualBlip
from blipblop.ui.audioblop import AudioBlop
from blipblop.ui.resultsscreen import ResultsScreen
class CentralWidget(QWidget):
def __init__(self, parent=None) -> None:
super().__init__(parent=parent)
self._splash = StartScreen()
self._visual_screen = VisualBlip(self)
self._visual_screen.close_signal.connect(self.on_plot_close)
self._auditory_screen = AudioBlop(self)
self._auditory_screen.close_signal.connect(self.on_plot_close)
self._results_screen = AudioBlop(self)
self._results_screen.close_signal.connect(self.on_plot_close)
self._stack = QStackedLayout(self)
self._stack.addWidget(self._splash)
self._stack.addWidget(self._visual_screen)
self._stack.addWidget(self._auditory_screen)
self._stack.addWidget(self._results_screen)
self.setLayout(self._stack)
def show_file_content(self):
self._stack.setCurrentIndex(1)
self._visual_stims_screen.update()
def plot_item(self, item_descriptor):
self._stack.setCurrentIndex(2)
self._auditory_screen.plot(item_descriptor)
def on_plot_close(self):
self._stack.setCurrentIndex(1)
def reset(self):
self._visual_stims_screen.reset()
self._splash.reset()
self._stack.setCurrentIndex(0)

142
blipblop/ui/filescreen.py Normal file
View File

@ -0,0 +1,142 @@
from PyQt5.QtWidgets import QComboBox, QFrame, QGroupBox, QHBoxLayout, QLabel, QSplitter, QTextEdit, QVBoxLayout, QWidget
from PyQt5.QtCore import QItemSelectionModel, Qt
from nixview.util.file_handler import FileHandler
from nixview.util.descriptors import ItemDescriptor
import nixview.communicator as comm
import nixview.constants as cnst
from nixview.data_models.tree_model import NixTreeView, TreeModel, TreeType
class FileScreen(QWidget):
def __init__(self, parent=None) -> None:
super().__init__(parent=parent)
self._file_handler = FileHandler()
vbox = QVBoxLayout()
self.setLayout(vbox)
main_splitter = QSplitter(Qt.Vertical)
self.layout().addWidget(main_splitter)
self._info = EntityInfo(self)
main_splitter.addWidget(self._info)
self._data_tree = NixTreeView(self)
self._tree_type_combo = QComboBox()
self._tree_type_combo.adjustSize()
self._tree_type_combo.addItems([TreeType.Data.value, TreeType.Full.value, TreeType.Metadata.value])
self._tree_type_combo.currentTextChanged.connect(self.update)
hbox = QHBoxLayout()
hbox.addWidget(QLabel("Tree type:"))
hbox.addWidget(self._tree_type_combo)
hbox.addStretch()
data_group = QGroupBox("Data")
data_vbox = QVBoxLayout()
data_vbox.setContentsMargins(1, 10, 1, 1)
data_vbox.addLayout(hbox)
data_vbox.addWidget(self._data_tree)
data_group.setLayout(data_vbox)
main_splitter.addWidget(data_group)
main_splitter.setSizes([200, 600])
vbox.addWidget(main_splitter)
def dataTreeSelection(self, current_index, last_index):
if not current_index.isValid():
return
item = current_index.internalPointer()
comm.communicator.item_selected.emit(item)
self._info.setEntityInfo(item.node_descriptor)
def update(self):
tt = TreeType.Data
if self._tree_type_combo.currentText() == TreeType.Data.value:
tt = TreeType.Data
elif self._tree_type_combo.currentText() == TreeType.Full.value:
tt = TreeType.Full
elif self._tree_type_combo.currentText() == TreeType.Metadata.value:
tt = TreeType.Metadata
self._info.setEntityInfo(None)
data_model = TreeModel(self._file_handler, tt)
self._data_tree.setModel(data_model)
selection_model = QItemSelectionModel(data_model)
self._data_tree.setSelectionModel(selection_model)
selection_model.currentChanged.connect(self.dataTreeSelection)
for i in range(data_model.columnCount(None)):
self._data_tree.resizeColumnToContents(i)
self._info.setFileInfo(self._file_handler.file_descriptor)
def reset(self):
pass
class EntityInfo(QWidget):
def __init__(self, parent):
super().__init__(parent=parent)
self._file_handler = FileHandler()
self.setLayout(QHBoxLayout())
self._metadata_tree = NixTreeView()
mdata_grp = QGroupBox("Metadata")
mdata_grp.setLayout(QVBoxLayout())
mdata_grp.layout().setContentsMargins(1, 10, 1, 1)
mdata_grp.layout().addWidget(self._metadata_tree)
file_info_grp = QGroupBox("File info")
file_info_grp.setLayout(QVBoxLayout())
file_info_grp.layout().setContentsMargins(1, 10, 1, 1)
self._file_info = QTextEdit("File information")
self._file_info.setEnabled(True)
self._file_info.setTextInteractionFlags(Qt.TextSelectableByKeyboard | Qt.TextSelectableByMouse)
self._file_info.setFrameShape(QFrame.NoFrame)
self._file_info.setLineWrapMode(QTextEdit.WidgetWidth)
file_info_grp.layout().addWidget(self._file_info)
entity_info_grp = QGroupBox("Entity info")
entity_info_grp.setLayout(QVBoxLayout())
entity_info_grp.layout().setContentsMargins(1, 10, 1, 1)
self._entity_info = QTextEdit("Entity information")
self._file_info.setEnabled(True)
self._file_info.setTextInteractionFlags(Qt.TextSelectableByKeyboard | Qt.TextSelectableByMouse)
self._file_info.setFrameShape(QFrame.NoFrame)
self._file_info.setLineWrapMode(QTextEdit.WidgetWidth)
entity_info_grp.layout().addWidget(self._entity_info)
self._splitter = QSplitter(Qt.Horizontal)
self._splitter.addWidget(file_info_grp)
self._splitter.addWidget(entity_info_grp)
self._splitter.addWidget(mdata_grp)
self._splitter.setSizes([200, 400, 0])
self._splitter.setStretchFactor(0, 0)
self._splitter.setStretchFactor(1, 1)
self._splitter.setStretchFactor(2, 1)
self.layout().addWidget(self._splitter)
def setFileInfo(self, file_info):
if file_info is not None:
self._file_info.setText(file_info.toHtml())
def setEntityInfo(self, entity_info):
if entity_info is None or not isinstance(entity_info, ItemDescriptor):
self._splitter.setSizes([200, 400, 0])
self._entity_info.setText("")
self._metadata_tree.setModel(None)
return
if entity_info.metadata_id is not None:
self._splitter.setSizes([200, 400, 400])
else:
self._splitter.setSizes([200, 400, 0])
self._entity_info.setText(entity_info.to_html())
metadata_model = TreeModel(self._file_handler, TreeType.Metadata, root_section_id=entity_info.metadata_id)
self._metadata_tree.setModel(metadata_model)
for i in range(metadata_model.columnCount(None)):
self._metadata_tree.resizeColumnToContents(i)

65
blipblop/ui/help.py Normal file
View File

@ -0,0 +1,65 @@
import os
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QFrame, QHBoxLayout, QPushButton, QSizePolicy, QTextBrowser, QVBoxLayout, QWidget
from PyQt5.QtCore import QUrl
import blipblop.constants as cnst
class HelpDialog(QDialog):
def __init__(self, parent = None) -> None:
super().__init__(parent=parent)
self.setModal(True)
self.setMinimumSize(500, 750)
self.help = HelpBrowser()
self.help._edit.historyChanged.connect(self._on_history_changed)
self.back_btn = QPushButton(QIcon(os.path.join(cnst.ICONS_FOLDER, "back_btn")), "back")
self.back_btn.setEnabled(False)
self.back_btn.clicked.connect(self.help._edit.backward)
self.home_btn = QPushButton(QIcon(os.path.join(cnst.ICONS_FOLDER, "home_btn")),"home")
self.home_btn.clicked.connect(self.help._edit.home)
self.fwd_btn = QPushButton(QIcon(os.path.join(cnst.ICONS_FOLDER, "fwd_btn")),"forward")
self.fwd_btn.setEnabled(False)
self.fwd_btn.clicked.connect(self.help._edit.forward)
empty = QWidget()
empty.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
hbox = QHBoxLayout()
hbox.addWidget(self.back_btn)
hbox.addWidget(self.home_btn)
hbox.addWidget(self.fwd_btn)
hbox.addWidget(empty)
bbox = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok)
bbox.accepted.connect(self.accept)
layout = QVBoxLayout()
layout.addLayout(hbox)
layout.addWidget(self.help)
layout.addWidget(bbox)
self.setLayout(layout)
def _on_history_changed(self):
self.back_btn.setEnabled(self.help._edit.isBackwardAvailable())
self.fwd_btn.setEnabled(self.help._edit.isForwardAvailable())
class HelpBrowser(QWidget):
def __init__(self, parent=None) -> None:
super().__init__(parent=parent)
self.setLayout(QVBoxLayout())
doc_url = QUrl.fromLocalFile(cnst.DOCS_ROOT_FILE)
self._edit = QTextBrowser()
self._edit.setOpenLinks(True)
self._edit.setOpenExternalLinks(True)
self._edit.setSource(doc_url)
self._edit.setEnabled(True)
self._edit.setFrameShape(QFrame.NoFrame)
self.layout().addWidget(self._edit)

133
blipblop/ui/mainwindow.py Normal file
View File

@ -0,0 +1,133 @@
import sys
from PyQt5.QtWidgets import QWidget, QFileDialog, QMainWindow, QMenuBar, QToolBar, QAction, QStatusBar, QSizePolicy
from PyQt5.QtGui import QKeySequence
from PyQt5.QtCore import QSize, QSettings, Qt
import blipblop.constants as cnst
from blipblop.ui.help import HelpDialog
from blipblop.ui.about import AboutDialog
class BlipBlop(QMainWindow):
def __init__(self, *args, **kwargs):
super(BlipBlop, self).__init__(*args, **kwargs)
self._current_item = None
self.setWindowTitle("BlipBlop")
self.setStatusBar(QStatusBar(self))
self.setMenuBar(QMenuBar(self))
self.create_actions()
self._task_results = []
self.show()
def create_actions(self):
self._quit_action = QAction(cnst.get_icon("nixview_quit"), "Quit", self)
self._quit_action.setStatusTip("Close current file and quit")
self._quit_action.setShortcut(QKeySequence("Ctrl+q"))
self._quit_action.triggered.connect(self.on_quit)
self._new_action = QAction(cnst.get_icon("nixview_quit"), "New session", self)
self._new_action.setStatusTip("Start a new session discarding previous results")
self._new_action.setShortcut(QKeySequence("Ctrl+n"))
self._new_action.triggered.connect(self.on_new)
"""
self._plot_action = QAction(cnst.get_icon("nix_data_array"), "Plot", self)
self._plot_action.setStatusTip("Plot currently selected entity")
self._plot_action.setShortcut(QKeySequence("Ctrl+p"))
self._plot_action.setEnabled(False)
self._plot_action.triggered.connect(self.on_item_plot)
"""
self._results_action = QAction(cnst.get_icon("nix_data_frame"), "Show table", self)
self._results_action.setStatusTip("Show data as table")
self._results_action.setShortcut(QKeySequence("Ctrl+t"))
self._results_action.setEnabled(False)
# self._table_action.triggered.connect(self.on_file_close)
self._about_action = QAction("about")
self._about_action.setStatusTip("Show about dialog")
self._about_action.setEnabled(True)
self._about_action.triggered.connect(self.on_about)
self._help_action = QAction(cnst.get_icon("nixview_help"), "help")
self._help_action.setStatusTip("Show help dialog")
self._help_action.setShortcut(QKeySequence("F1"))
self._help_action.setEnabled(True)
self._help_action.triggered.connect(self.on_help)
self._visual_task_action = QAction("visual")
self._visual_task_action.setStatusTip("Start measuring visual reaction times")
self._visual_task_action.setEnabled(False)
self._auditory_task_action = QAction("auditory")
self._auditory_task_action.setStatusTip("Start measuring auditory reaction times")
self._auditory_task_action.setEnabled(False)
self.create_toolbar()
self.create_menu()
def create_toolbar(self):
self._toolbar = QToolBar("My main toolbar")
#self._toolbar.setStyleSheet("QToolButton:!hover {background-color:none}")
self._toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.TopToolBarArea)
self._toolbar.setIconSize(QSize(32, 32))
# self._toolbar.addAction(self._file_open_action)
#self._toolbar.addAction(self._file_close_action)
self._toolbar.addAction(self._new_action)
self._toolbar.addSeparator()
self._toolbar.addAction(self._visual_task_action)
self._toolbar.addAction(self._auditory_task_action)
self._toolbar.addAction(self._results_action)
self._toolbar.addAction(self._help_action)
empty = QWidget()
empty.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self._toolbar.addWidget(empty)
self._toolbar.addSeparator()
self._toolbar.addAction(self._quit_action)
self.addToolBar(Qt.LeftToolBarArea, self._toolbar)
def create_menu(self):
menu = self.menuBar()
file_menu = menu.addMenu("&File")
# file_menu.addAction(self._file_open_action)
# file_menu.addAction(self._file_close_action)
# file_menu.addSeparator()
file_menu.addAction(self._quit_action)
task_menu = menu.addMenu("&Tasks")
task_menu.addAction(self._new_action)
task_menu.addSeparator()
task_menu.addAction(self._visual_task_action)
task_menu.addAction(self._auditory_task_action)
#plot_menu = menu.addMenu("&Plot")
#plot_menu.addAction(self._plot_action)
#plot_menu.addAction(self._table_action)
help_menu = menu.addMenu("&Help")
help_menu.addAction(self._about_action)
help_menu.addAction(self._help_action)
self.setMenuBar(menu)
def on_quit(self, s):
sys.exit()
def on_new(self):
self._task_results = []
self.__
pass
def on_about(self):
about = AboutDialog(self)
about.show()
pass
def on_help(self, e):
help = HelpDialog(self)
help.show()

View File

@ -0,0 +1,19 @@
from PyQt5.QtWidgets import QComboBox, QFrame, QGroupBox, QHBoxLayout, QLabel, QSplitter, QTextEdit, QVBoxLayout, QWidget
from PyQt5.QtCore import QItemSelectionModel, Qt
import blipblop.constants as cnst
class ResultsScreen(QWidget):
def __init__(self, parent=None) -> None:
super().__init__(parent=parent)
vbox = QVBoxLayout()
self.setLayout(vbox)
main_splitter = QSplitter(Qt.Vertical)
self.layout().addWidget(main_splitter)
def reset(self):
pass

View File

@ -0,0 +1,71 @@
import os
from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel, QFrame, QVBoxLayout, QListWidget, QAbstractItemView, QListWidgetItem
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import Qt, QSettings, pyqtSignal
import blipblop.constants as cnst
class StartScreen(QWidget):
keyPressed = pyqtSignal(int)
def __init__(self, parent=None) -> None:
super().__init__(parent=parent)
#self.setStyleSheet("background-color: white;")
layout = QGridLayout()
layout.setColumnStretch(0, 2)
layout.setColumnStretch(1, 1)
layout.setColumnStretch(2, 1)
layout.setColumnStretch(3, 1)
layout.setColumnStretch(4, 2)
layout.setRowStretch(0, 1)
layout.setRowStretch(1, 0)
layout.setRowStretch(2, 1)
layout.setRowStretch(3, 2)
self.setLayout(layout)
label = QLabel("Measure your reaction times!\nselect a task!")
# label.setPixmap(QPixmap(os.path.join(cnst.ICONS_FOLDER, "nixview_transparent.png")))
# label.setMaximumWidth(300)
label.setAlignment(Qt.AlignCenter)
layout.addWidget(label, 1, 1, 1, 3, Qt.AlignCenter)
frame = QFrame()
l = QVBoxLayout()
l.addWidget(QLabel("Recently opened files:"))
self._file_list = QListWidget(self)
self._file_list.setSelectionMode(QAbstractItemView.SingleSelection)
self._file_list.itemClicked.connect(self._on_file_clicked)
self._file_list.setFrameShape(QFrame.NoFrame)
self.keyPressed.connect(self._on_key_pressed)
l.addWidget(self._file_list)
frame.setLayout(l)
layout.addWidget(frame, 3, 1, 1, 3)
self._file_map = {}
def keyPressEvent(self, event):
super(StartScreen, self).keyPressEvent(event)
if event.key() == Qt.Key_Return:
self.keyPressed.emit(event.key())
def _create_short_filename(self, original, index, max_len=40):
short = original
parts = original.split(os.sep)
if len(parts) == 1:
short = "%i: %s" % (index, short[:max_len])
else:
post = parts[-1]
if len(post) > max_len - 4:
post = post[:max_len - 4]
short = str("%i: " % index) + "... " + post
else:
short = str("%i: " % index) + " ... ".join([original[:max_len - len(post) - 4], post])
return short
def reset(self):
pass
def _on_key_pressed(self, key):
pass

22
blipblop/ui/visualblip.py Normal file
View File

@ -0,0 +1,22 @@
from PyQt5.QtWidgets import QComboBox, QFrame, QGroupBox, QHBoxLayout, QLabel, QSplitter, QTextEdit, QVBoxLayout, QWidget
from PyQt5.QtCore import QItemSelectionModel, Qt
import blipblop.constants as cnst
class VisualBlip(QWidget):
def __init__(self, parent=None) -> None:
super().__init__(parent=parent)
vbox = QVBoxLayout()
self.setLayout(vbox)
main_splitter = QSplitter(Qt.Vertical)
self.layout().addWidget(main_splitter)
def reset(self):
pass

View File

24
blipblop/util/results.py Normal file
View File

@ -0,0 +1,24 @@
import datetime as dt
class MeasurementResults():
def __init__(self, task_name, metadata={}):
self._task_name = task_name
self._task_timestamp = dt.datetime.now()
self._reaction_times = []
self._metadata = metadata
def append(self, reaction_time):
self._reaction_times.append(reaction_time)
@property
def name(self):
return self._task_name
@property
def starttime(self):
return str(self._task_timestamp)
@property
def results(self):
return self._reaction_times

4
docs/index.md Normal file
View File

@ -0,0 +1,4 @@
# BlipBlop
Tiny tool for measuring reaction times upon auditory or visual stimuli.

BIN
icons/back_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
icons/fwd_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
icons/home_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
icons/nix_block_1d.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
icons/nix_close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
icons/nix_data_array.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
icons/nix_data_frame.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
icons/nix_dimension.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
icons/nix_fav1_cut.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
icons/nix_feature.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
icons/nix_group.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
icons/nix_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
icons/nix_open.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
icons/nix_plot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1007 B

BIN
icons/nix_property.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
icons/nix_section.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
icons/nix_source.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
icons/nix_table.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
icons/nix_tag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
icons/nixview.icns Normal file

Binary file not shown.

BIN
icons/nixview.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
icons/nixview128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
icons/nixview256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
icons/nixview32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
icons/nixview64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
icons/nixview_close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
icons/nixview_help.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
icons/nixview_open.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
icons/nixview_quit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
icons/quit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

57
setup.py Normal file
View File

@ -0,0 +1,57 @@
import glob
import json
import os
# Use setuptools compulsorily, as the distutils doesn't work out well for the
# installation procedure. The 'install_requires' and 'data_files' have better
# support in setuptools.
from setuptools import setup
with open(os.path.join("blipblop", "info.json")) as infofile:
infodict = json.load(infofile)
NAME = infodict["NAME"]
VERSION = infodict["VERSION"]
AUTHOR = infodict["AUTHOR"]
CONTACT = infodict["CONTACT"]
HOMEPAGE = infodict["HOMEPAGE"]
CLASSIFIERS = infodict["CLASSIFIERS"]
DESCRIPTION = infodict["DESCRIPTION"]
README = "README.md"
with open(README) as f:
description_text = f.read()
packages = [
"blipblop",
]
install_req = ["PyQt5", "numpy"]
data_files = [("icons", glob.glob(os.path.join("icons", "*.png"))),
("icons", glob.glob(os.path.join("icons", "*.ic*"))),
(".", ["LICENSE"]),
("docs", glob.glob(os.path.join("docs", "*.md")))
]
setup(
name=NAME,
version=VERSION,
description=DESCRIPTION,
author=AUTHOR,
author_email=CONTACT,
url=HOMEPAGE,
packages=packages,
install_requires=install_req,
include_package_data=True,
data_files=data_files,
long_description=description_text,
long_description_content_type="text/markdown",
classifiers=CLASSIFIERS,
license="BSD",
entry_points={
"gui_scripts": ["blipblop = blipblop:main []"],
"console_scripts": ["blipblop = blipblop:main []"]
}
)