Initial commit
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import pathops
|
||||
import os
|
||||
from matrix_test.generate_matrix_stimulus import generateStimulus
|
||||
import time
|
||||
|
||||
import config
|
||||
|
||||
'''
|
||||
Module containing functions to be processed asynchronously
|
||||
'''
|
||||
|
||||
def generate_matrix_stimulus(obj_response, n_part, snr_len, snr_num, mat_dir, save_dir):
|
||||
#from celery.contrib import rdb
|
||||
#rdb.set_trace()
|
||||
n_part = int(n_part)
|
||||
|
||||
for participant_n in range(n_part):
|
||||
obj_response.attr('#progress-bar', 'style','width={}'.format((participant_n+1/n_part)*100.))
|
||||
time.sleep(1)
|
||||
return None
|
||||
'''
|
||||
save_dir = os.path.join(save_dir)
|
||||
mat_dir = os.path.join(mat_dir)
|
||||
pathops.dir_must_exist(save_dir)
|
||||
pathops.dir_must_exist(mat_dir)
|
||||
n_part = int(n_part)
|
||||
snr_len = float(snr_len)
|
||||
snr_num = int(snr_num)
|
||||
|
||||
for participant_n in range(n_part):
|
||||
obj_response.attr('#progress-bar', 'style','width={}'.format((participant_n+1/n_part)*100.))
|
||||
genLength = (snr_len * snr_num) * 60.
|
||||
partDir = os.path.join(save_dir, "Partcipant{0:02d}".format(participant_n))
|
||||
pathops.dir_must_exist(partDir)
|
||||
|
||||
filenames = generateStimulus(mat_dir, genLength, partDir)
|
||||
return {'current': n_part, 'total': n_part, 'status': 'Task completed!',
|
||||
'result': 42}
|
||||
'''
|
||||
@@ -0,0 +1,11 @@
|
||||
from flask import Flask
|
||||
from flask_socketio import SocketIO, emit
|
||||
import os
|
||||
|
||||
gui_dir = os.path.join(os.getcwd(), "gui") # development path
|
||||
if not os.path.exists(gui_dir): # frozen executable path
|
||||
gui_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "gui")
|
||||
|
||||
server = Flask(__name__)
|
||||
server.config["SEND_FILE_MAX_AGE_DEFAULT"] = 1 # disable caching
|
||||
socketio = SocketIO(async_mode='threading')
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from threading import Thread, Lock
|
||||
import logging
|
||||
import webview
|
||||
from time import sleep
|
||||
from server import run_server
|
||||
|
||||
server_lock = Lock()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def url_ok(url, port):
|
||||
# Use httplib on Python 2
|
||||
try:
|
||||
from http.client import HTTPConnection
|
||||
except ImportError:
|
||||
from httplib import HTTPConnection
|
||||
|
||||
try:
|
||||
conn = HTTPConnection(url, port)
|
||||
conn.request("GET", "/")
|
||||
r = conn.getresponse()
|
||||
return r.status == 200
|
||||
except:
|
||||
logger.exception("Server not started")
|
||||
return False
|
||||
|
||||
if __name__ == '__main__':
|
||||
logger.debug("Starting server")
|
||||
# Run server in seperate thread
|
||||
t = Thread(target=run_server)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
logger.debug("Checking server")
|
||||
|
||||
# Check server is up and running
|
||||
while not url_ok("127.0.0.1", 23948):
|
||||
sleep(0.1)
|
||||
|
||||
logger.debug("Server started")
|
||||
# Create browser window for user interaction with GUI
|
||||
webview.create_window("BPLabs",
|
||||
"http://127.0.0.1:23948/home",
|
||||
width=1000, height=700, min_size=(1000, 500), debug=True)
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
import os
|
||||
import shutil
|
||||
from six import string_types
|
||||
|
||||
def get_filename(path):
|
||||
"""
|
||||
Returns the filename without extension from path provided
|
||||
"""
|
||||
return os.path.splitext(os.path.basename(path))[0]
|
||||
|
||||
def file_must_exist(path):
|
||||
"""
|
||||
Checks that the path specified exists
|
||||
If not or the path is a directory then raises and IOError
|
||||
"""
|
||||
# Check that path is a string value
|
||||
if not isinstance(path, string_types):
|
||||
raise TypeError("File path argument is not a string")
|
||||
if not os.path.isfile(path):
|
||||
raise IOError("File doesn't exist: ", path)
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def dir_must_exist(path):
|
||||
"""
|
||||
Checks that the directory exists.
|
||||
If not then tries to create the directory.
|
||||
If this fails then an IOError is raised.
|
||||
"""
|
||||
if not isinstance(path, string_types):
|
||||
raise TypeError("Directory path argument is not a string")
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError:
|
||||
if not os.path.isdir(path):
|
||||
raise IOError("Directory can't be created: ", path)
|
||||
|
||||
return path
|
||||
|
||||
|
||||
def listdir_nohidden(path):
|
||||
"""
|
||||
List all files and subdirectories of a directory that aren't hidden.
|
||||
"""
|
||||
dir_must_exist(path)
|
||||
return [os.path.join(path, i) for i in os.listdir(path) if not i.startswith('.')]
|
||||
|
||||
|
||||
def delete_if_exists(path):
|
||||
"""
|
||||
Trys to delete the file at the path specified if it exists.
|
||||
Returns True on succesful deletion else returns False.
|
||||
Either way there won't be a file at that location after running.
|
||||
"""
|
||||
try:
|
||||
if os.path.isdir(path):
|
||||
shutil.rmtree(path)
|
||||
else:
|
||||
os.remove(path)
|
||||
except OSError:
|
||||
return False
|
||||
return True
|
||||
@@ -0,0 +1,5 @@
|
||||
Flask_SocketIO==3.0.2
|
||||
Flask==1.0.2
|
||||
six==1.10.0
|
||||
pywebview==2.1
|
||||
webview==0.1.5
|
||||
@@ -0,0 +1,159 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
from flask import Flask, url_for, render_template, jsonify, request, make_response, g
|
||||
from flask_socketio import emit
|
||||
|
||||
import webview
|
||||
import webbrowser
|
||||
import app
|
||||
import time
|
||||
from threading import Thread, Event
|
||||
|
||||
import config
|
||||
|
||||
server = config.server
|
||||
socketio = config.socketio
|
||||
|
||||
@server.after_request
|
||||
def add_header(response):
|
||||
# Disable caching? unsure why...
|
||||
response.headers['Cache-Control'] = 'no-store'
|
||||
return response
|
||||
|
||||
|
||||
@server.route("/")
|
||||
def landing():
|
||||
"""
|
||||
Render index.html
|
||||
"""
|
||||
return render_template("index.html")
|
||||
|
||||
|
||||
@server.route("/choose/path")
|
||||
def choose_path():
|
||||
"""
|
||||
Invokes a folder selection dialog
|
||||
"""
|
||||
dirs = webview.create_file_dialog(webview.FOLDER_DIALOG)
|
||||
if dirs and len(dirs) > 0:
|
||||
directory = dirs[0]
|
||||
if isinstance(directory, bytes):
|
||||
directory = directory.decode("utf-8")
|
||||
|
||||
response = {"status": "ok", "directory": directory}
|
||||
else:
|
||||
response = {"status": "cancel"}
|
||||
|
||||
return jsonify(response)
|
||||
|
||||
@server.route("/fullscreen")
|
||||
def fullscreen():
|
||||
webview.toggle_fullscreen()
|
||||
return jsonify({})
|
||||
|
||||
@server.route('/home')
|
||||
def homepage():
|
||||
title = "Welcome"
|
||||
paragraph = [
|
||||
"This web app was developed for the generation of stimulus and "
|
||||
"running of experiments for the PhD project \"Predicting speech "
|
||||
"in noise performance using evoked responses\".",
|
||||
"Use the drop down menus to access the various modules of this app."
|
||||
]
|
||||
|
||||
try:
|
||||
return render_template("home.html", title = title, paragraph=paragraph)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
@server.route('/mat_dec_stim')
|
||||
def matDecStim():
|
||||
return render_template("matrix_decode_stim.html")
|
||||
|
||||
thread = Thread()
|
||||
thread_stop_event = Event()
|
||||
|
||||
class StimGenThread(Thread):
|
||||
'''
|
||||
Thread object for asynchronous processing of data in Python without locking
|
||||
up the GUI
|
||||
'''
|
||||
def __init__(self):
|
||||
super(StimGenThread, self).__init__()
|
||||
|
||||
|
||||
def process_stimulus(self):
|
||||
'''
|
||||
An example process
|
||||
'''
|
||||
for participant_n in range(15):
|
||||
percent = ((participant_n+1) / 15)*100.
|
||||
# Emit a message to update the progress bar during execution of the
|
||||
# python process (see relevant javascript code in
|
||||
# matrix_decode_stim.html)
|
||||
socketio.emit('update-progress', {'data': '{}%'.format(percent)}, namespace='/main')
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def run(self):
|
||||
'''
|
||||
This function is called when the thread starts
|
||||
'''
|
||||
self.process_stimulus()
|
||||
socketio.emit('processing-complete', {'data': ''}, namespace='/main')
|
||||
|
||||
|
||||
@socketio.on('run_mat_stim_gen', namespace='/main')
|
||||
def generateStim(msg):
|
||||
'''
|
||||
When process buton is clicked in GUI, start an asynchronous thread to run
|
||||
process
|
||||
'''
|
||||
thread = StimGenThread()
|
||||
thread.start()
|
||||
|
||||
@socketio.on('open_save_dialog', namespace='/main')
|
||||
def openSaveDialog():
|
||||
# Open a file dialog interface for selecting a directory
|
||||
dirs = webview.create_file_dialog(webview.FOLDER_DIALOG)
|
||||
if dirs and len(dirs) > 0:
|
||||
directory = dirs[0]
|
||||
if isinstance(directory, bytes):
|
||||
directory = directory.decode("utf-8")
|
||||
# TODO: Add filepath checking here...
|
||||
# Send message with selected directory to the GUI
|
||||
socketio.emit('save-dialog-resp', {'data': directory}, namespace='/main')
|
||||
|
||||
@socketio.on('open_mat_dialog', namespace='/main')
|
||||
def openMatDialog():
|
||||
# Open a file dialog interface for selecting a directory
|
||||
dirs = webview.create_file_dialog(webview.FOLDER_DIALOG)
|
||||
if dirs and len(dirs) > 0:
|
||||
directory = dirs[0]
|
||||
if isinstance(directory, bytes):
|
||||
directory = directory.decode("utf-8")
|
||||
# TODO: Add filepath checking here...
|
||||
# Send message with selected directory to the GUI
|
||||
socketio.emit('mat-dialog-resp', {'data': directory}, namespace='/main')
|
||||
|
||||
@server.route('/click_stim')
|
||||
def clickStim():
|
||||
return render_template("click_stim.html")
|
||||
|
||||
@server.route('/da_stim')
|
||||
def daStim():
|
||||
return render_template("da_stim.html")
|
||||
|
||||
def run_server():
|
||||
'''
|
||||
Start the Flask server
|
||||
'''
|
||||
# SocketIO objects are defined in config.py
|
||||
socketio.init_app(server)
|
||||
socketio.run(server, host="127.0.0.1", port=23948)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_server()
|
||||
|
||||
Vendored
+1912
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Vendored
+7
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+331
@@ -0,0 +1,331 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2018 The Bootstrap Authors
|
||||
* Copyright 2011-2018 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-ms-overflow-style: scrollbar;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@-ms-viewport {
|
||||
width: device-width;
|
||||
}
|
||||
|
||||
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
[tabindex="-1"]:focus {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-original-title] {
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
-webkit-text-decoration-skip: objects;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
svg {
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
color: #6c757d;
|
||||
text-align: left;
|
||||
caption-side: bottom;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
button,
|
||||
html [type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type="date"],
|
||||
input[type="time"],
|
||||
input[type="datetime-local"],
|
||||
input[type="month"] {
|
||||
-webkit-appearance: listbox;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: .5rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type="search"] {
|
||||
outline-offset: -2px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
[type="search"]::-webkit-search-cancel-button,
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
||||
File diff suppressed because one or more lines are too long
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2018 The Bootstrap Authors
|
||||
* Copyright 2011-2018 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
|
||||
/*# sourceMappingURL=bootstrap-reboot.min.css.map */
|
||||
File diff suppressed because one or more lines are too long
Vendored
+6461
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Vendored
+7
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+9030
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Vendored
+3944
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Vendored
+7
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+7
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,11 @@
|
||||
{% extends 'index.html' %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h1>{{ title }}</h1>
|
||||
{% for p in paragraph %}
|
||||
<p>{{ p }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'index.html' %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h1>{{ title }}</h1>
|
||||
{% for p in paragraph %}
|
||||
<p>{{ p }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,8 @@
|
||||
{% extends 'index.html' %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
Processing complete!
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'index.html' %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h1>{{ title }}</h1>
|
||||
{% for p in paragraph %}
|
||||
<p>{{ p }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,102 @@
|
||||
<html>
|
||||
<link rel="stylesheet" media="screen" href = "{{ url_for('static', filename='bootstrap.min.css') }}">
|
||||
<meta name="viewport" content = "width=device-width, initial-scale=1.0">
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
|
||||
<script src="http://code.highcharts.com/highcharts.js"></script>
|
||||
<script type="text/javascript" src = "/static/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body class = "body">
|
||||
<nav id="main-nav" class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="#">BPLabs v0.1</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="Home">Home <span class="sr-only">(current)</span></a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Stimulus generation
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<a class="dropdown-item" href="mat_dec_stim">Matrix decoder stimulus</a>
|
||||
<a class="dropdown-item" href="click_Stim">Click stimulus</a>
|
||||
<a class="dropdown-item" href="da_stim">/da/ stimulus</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Experiment procedure
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<a class="dropdown-item" href="#">PTA</a>
|
||||
<a class="dropdown-item" href="#">Tympanometry</a>
|
||||
<a class="dropdown-item" href="#">EEG recording</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
<script>
|
||||
// From https://gist.github.com/dharmavir/936328
|
||||
function getHttpRequestObject()
|
||||
{
|
||||
// Define and initialize as false
|
||||
var xmlHttpRequst = false;
|
||||
|
||||
// Mozilla/Safari/Non-IE
|
||||
if (window.XMLHttpRequest)
|
||||
{
|
||||
xmlHttpRequst = new XMLHttpRequest();
|
||||
}
|
||||
// IE
|
||||
else if (window.ActiveXObject)
|
||||
{
|
||||
xmlHttpRequst = new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
return xmlHttpRequst;
|
||||
}
|
||||
// Does the AJAX call to URL specific with rest of the parameters
|
||||
function doAjax(url, method, responseHandler, data)
|
||||
{
|
||||
// Set the variables
|
||||
url = url || "";
|
||||
method = method || "GET";
|
||||
async = true;
|
||||
data = data || null;
|
||||
|
||||
if(url == "") {
|
||||
alert("URL can not be null/blank");
|
||||
return false;
|
||||
}
|
||||
var xmlHttpRequest = getHttpRequestObject();
|
||||
|
||||
// If AJAX supported
|
||||
if(xmlHttpRequest != false) {
|
||||
xmlHttpRequest.open(method, url, async);
|
||||
// Set request header (optional if GET method is used)
|
||||
if(method == "POST") {
|
||||
xmlHttpRequest.setRequestHeader("Content-Type", "application/json");
|
||||
}
|
||||
// Assign (or define) response-handler/callback when ReadyState is changed.
|
||||
xmlHttpRequest.onreadystatechange = responseHandler;
|
||||
// Send data
|
||||
xmlHttpRequest.send(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
alert("Please use browser with Ajax support.!");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,92 @@
|
||||
{% extends 'index.html' %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div id="main-div" class="card-body">
|
||||
<form action="{{ url_for('matDecStim') }}" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="snr_num">Number of SNRs:</label>
|
||||
<br>
|
||||
<input id="snr_num" name="snr_num" type="number" value="5" style="width:85%">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="snr_len">Length of SNRs: </label>
|
||||
<br>
|
||||
<input id="snr_len" name="snr_len" type="number" value="15" style="width:85%">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="n_part">Number of participants: </label>
|
||||
<br>
|
||||
<input id="n_part" name="n_part" type="number" value="70" style="width:85%">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="open-mat-folder-container">Matrix data folder: </label>
|
||||
<br>
|
||||
<input type="text" id="mat-dir" name='mat_dir' style="width:85%"></input>
|
||||
<button type="button" id="mat-dir-button" class="btn btn-primary">Browse...</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="open-save-folder-container">New data generation folder: </label>
|
||||
<br>
|
||||
<input type="text" id="save-dir" name='save_dir' style="width:85%"></input>
|
||||
<button type="button" id="save-dir-button" class="btn btn-primary">Browse...</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="button" id="process-button" class="btn btn-primary">Process</button>
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<div id="progress-div" class="progress">
|
||||
<div id="progress" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
// Initialise socketio with a namespace called "main"
|
||||
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + '/main');
|
||||
|
||||
// Catch progress bar update messages
|
||||
socket.on('update-progress', function(msg) {
|
||||
// Update width of progress bar
|
||||
$('#progress').css("width", msg.data);
|
||||
});
|
||||
|
||||
// Catch message when asynchronous process is complete
|
||||
socket.on('processing-complete', function(msg) {
|
||||
// Re-enable all inputs
|
||||
$('#main-div').find('input, textarea, button, select').removeAttr('disabled');
|
||||
});
|
||||
|
||||
$('#process-button').click(function(event) {
|
||||
// Disable all inputs whilst processing
|
||||
$('#main-div').find('input, textarea, button, select').attr('disabled','disabled');
|
||||
// Send message to call stimulus generation function in Python
|
||||
socket.emit('run_mat_stim_gen', {data: "YES"});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#save-dir-button').click(function(event) {
|
||||
// Send message to call stimulus generation function in Python
|
||||
socket.emit('open_save_dialog');
|
||||
return false;
|
||||
})
|
||||
|
||||
socket.on('save-dialog-resp', function(msg) {
|
||||
// Set form test to filepath returned by dialog
|
||||
document.getElementById("save-dir").value = msg.data
|
||||
});
|
||||
|
||||
$('#mat-dir-button').click(function(event) {
|
||||
// Send message to call stimulus generation function in Python
|
||||
socket.emit('open_mat_dialog');
|
||||
return false;
|
||||
})
|
||||
|
||||
socket.on('mat-dialog-resp', function(msg) {
|
||||
// Set form test to filepath returned by dialog
|
||||
document.getElementById("mat-dir").value = msg.data
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user