Skip to content

Commit f510135

Browse files
authored
Merge pull request #26 from PayalLakra/bio_amptool
Changes in Web Interface(Mainly related to LSL)
2 parents 9449369 + d0b3e3d commit f510135

File tree

4 files changed

+143
-67
lines changed

4 files changed

+143
-67
lines changed

app.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,20 @@ def start_lsl():
2727
try:
2828
# Start the LSL stream as a subprocess
2929
if sys.platform == "win32":
30-
lsl_process = subprocess.Popen(["python", "chords.py", "--lsl"], creationflags=subprocess.CREATE_NO_WINDOW)
31-
else:
32-
lsl_process = subprocess.Popen(["python", "chords.py", "--lsl"])
33-
34-
if lsl_process.poll() is None:
35-
return render_template("index.html", lsl_started=True, lsl_status="Running", lsl_color="green")
30+
lsl_process = subprocess.Popen(["python", "chords.py", "--lsl"],stdout=subprocess.PIPE,stderr=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW)
3631
else:
32+
lsl_process = subprocess.Popen(["python", "chords.py", "--lsl"],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
33+
output = lsl_process.stderr.readline().decode().strip() # Read the initial stderr line
34+
35+
print(output)
36+
if output == "No":
3737
return render_template("index.html", lsl_started=False, lsl_status="Failed to Start", lsl_color="red")
38+
else:
39+
return render_template("index.html", lsl_started=True, lsl_status="Running", lsl_color="green")
40+
except subprocess.TimeoutExpired:
41+
return render_template(
42+
"index.html", lsl_started=False, lsl_status="Timeout Error", lsl_color="red"
43+
)
3844
except Exception as e:
3945
return render_template("index.html", lsl_started=False, lsl_status=f"Error: {e}", lsl_color="red")
4046

@@ -44,7 +50,7 @@ def run_app():
4450

4551
# Check if the app is already running
4652
if app_name in app_processes and app_processes[app_name].poll() is None:
47-
return render_template("index.html", lsl_started=True, lsl_status="Running", lsl_color="green", message=f"{app_name} is already Running")
53+
return render_template("index.html", lsl_started=True, lsl_status="Running", lsl_color="green", message=f"{app_name} is already Running", running_apps=app_processes.keys())
4854

4955
try:
5056
# Start the app subprocess
@@ -54,10 +60,20 @@ def run_app():
5460
process = subprocess.Popen(["python", f"{app_name}.py"])
5561

5662
app_processes[app_name] = process
57-
return render_template("index.html", lsl_started=True, lsl_status="Running", lsl_color="green", message=None)
63+
return render_template("index.html", lsl_started=True, lsl_status="Running", lsl_color="green", running_apps=app_processes.keys(), message=None)
64+
5865
except Exception as e:
59-
return render_template("index.html", lsl_started=True, lsl_status="Running", lsl_color="green", message=f"Error starting {app_name}: {e}")
66+
return render_template("index.html", lsl_started=True, lsl_status="Running", lsl_color="green", message=f"Error starting {app_name}: {e}", running_apps=app_processes.keys())
6067

68+
@app.route("/app_status", methods=["GET"])
69+
def app_status():
70+
# Check the status of all apps
71+
statuses = {
72+
app_name: (process.poll() is None) # True if running, False if not
73+
for app_name, process in app_processes.items()
74+
}
75+
return jsonify(statuses)
76+
6177
@app.route("/stop_lsl", methods=['POST'])
6278
def stop_lsl():
6379
# Terminate LSL process

chords.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,9 @@ def main():
342342
ser = connect_hardware(port=args.port, baudrate=args.baudrate)
343343
else:
344344
ser = detect_hardware(baudrate=args.baudrate)
345-
345+
if ser is None:
346+
sys.stderr.write("No\n")
347+
sys.exit(1) # Exit with a non-zero code to indicate failure
346348
if ser is None:
347349
print("Arduino port not specified or detected. Exiting.") # Notify if no port is available
348350
return

static/style.css

Lines changed: 46 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,23 @@ body {
1111
}
1212

1313
.header h1 {
14-
font-size: 2.5em;
15-
margin-bottom: 20px;
14+
font-size: 48px;
15+
font-weight: bold;
16+
background: linear-gradient(to right, #ec4899, #a855f7, #3b82f6); /* Background gradient */
17+
-webkit-background-clip: text; /* Vendor-prefixed for WebKit-based browsers */
18+
background-clip: text; /* Standard property */
19+
-webkit-text-fill-color: transparent; /* Makes the text transparent */
20+
}
21+
22+
.subtitle {
23+
font-size: 1.2em;
1624
color: #333;
25+
margin-top: -10px;
26+
}
27+
28+
.heart {
29+
color: #a855f7;
30+
font-size: 1.2em;
1731
}
1832

1933
.controls button {
@@ -22,22 +36,17 @@ body {
2236
font-size: 18px;
2337
cursor: pointer;
2438
border: none;
25-
border-radius: 25px; /* Rounded shape */
26-
background-color: #d1a56c; /* Beige color */
27-
color: #333; /* Dark text */
39+
border-radius: 10px;
40+
background: linear-gradient(to right, #ec4899, #a855f7, #3b82f6);
41+
color: #fffffe;
2842
transition: transform 0.3s ease, background-color 0.3s ease;
2943
}
3044

31-
.controls button:hover {
32-
background-color: hsl(34, 47%, 40%); /* Dark beige on hover */
33-
}
34-
3545
button:disabled {
36-
background-color: #cccccc;
46+
background: rgb(105, 206, 105);
3747
cursor: not-allowed;
3848
}
3949

40-
/* App button layout */
4150
.app-buttons {
4251
margin-top: 20px;
4352
}
@@ -56,38 +65,37 @@ button:disabled {
5665
gap: 15px; /* Spacing between buttons */
5766
}
5867

59-
/* App button styles */
6068
.app-buttons button {
6169
width: 200px;
6270
height: 80px;
6371
margin: 10px;
64-
font-size: 20px; /* Larger font size */
65-
font-weight: bold;
66-
text-transform: uppercase; /* Stylish text */
67-
border-radius: 25px; /* Rounded edges */
72+
font-size: 17px; /* Larger font size */
73+
font-weight: medium;
74+
border-radius: 10px; /* Rounded edges */
6875
border: none;
69-
color: white; /* Font color */
76+
color: #333; /* Font color */
7077
cursor: pointer;
71-
background-color:#9ba59c; /* gray color */
78+
background-color: #fffffe;
7279
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
7380
transition: transform 0.3s, background-color 0.3s;
7481
}
7582

76-
/* Hover effect for app buttons */
77-
.app-buttons button:hover {
78-
background-color: #656d67; /* Darker shade on hover */
79-
transform: scale(1.1);
80-
opacity: 0.9;
81-
}
82-
83-
/* Button active (on click) effect */
8483
.app-buttons button:active {
8584
transform: scale(1.05);
85+
background-color: rgb(105, 206, 105);
8686
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
87+
color: white;
88+
cursor: not-allowed;
8789
}
8890

89-
button:disabled {
90-
cursor: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><text x="0" y="16" font-size="16">🚫</text></svg>'), not-allowed;
91+
button.running {
92+
background-color: rgb(105, 206, 105) !important;
93+
cursor: not-allowed;
94+
color: white;
95+
}
96+
97+
button.not-running {
98+
background-color: #fffffe;
9199
}
92100

93101
.popup {
@@ -105,21 +113,17 @@ button:disabled {
105113
animation: fade-out 3s forwards;
106114
}
107115

108-
.popup p {
109-
font-size: 18px;
110-
font-weight: bold;
111-
color: #333;
116+
.bottom-text {
117+
position: fixed;
118+
bottom: 1px;
119+
left: 50%;
120+
transform: translateX(-50%);
121+
text-align: center;
122+
font-size: 13px;
112123
}
113124

114125
@keyframes fade-out {
115-
0% {
116-
opacity: 1;
117-
}
118-
80% {
119-
opacity: 1;
120-
}
121-
100% {
122-
opacity: 0;
123-
visibility: hidden;
124-
}
126+
0% { opacity: 1; }
127+
80% { opacity: 1; }
128+
100% { opacity: 0; visibility: hidden; }
125129
}

templates/index.html

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<title>Chords-Python</title>
6+
<title>Chords-Python Applications</title>
77
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
88
</head>
99
<body>
1010
<div class="container">
1111
<div class="header">
12-
<h1>Chords-Python</h1>
12+
<h1>Chords-Python Applications</h1>
13+
<p class="bottom-text">Designed with <span class="heart">&#10084;</span> at Upside Down Labs</p>
1314
</div>
1415

1516
<!-- Pop-up message -->
@@ -19,37 +20,90 @@ <h1>Chords-Python</h1>
1920
</div>
2021
{% endif %}
2122

22-
<div class="controls">
23+
<div class="controls">
2324
{% if not lsl_started %}
2425
<form action="/start_lsl" method="POST">
25-
<button type="submit" id="start_lsl_button">Start LSL Stream</button>
26+
<button type="submit" id="start_lsl_button" class="lsl-not-running">Start LSL Stream</button>
2627
</form>
2728
{% else %}
28-
<button id="start_lsl_button" disabled>LSL Stream Running</button>
29+
<button id="start_lsl_button" class="lsl-running" disabled>LSL Stream Running</button>
2930
{% endif %}
30-
</div>
31-
31+
</div>
3232
<div class="app-buttons">
3333
<!-- Row 1: ECG, EMG, EOG, EEG -->
3434
<div class="row">
3535
<form action="/run_app" method="POST">
36-
<button type="submit" name="app_name" value="heartbeat_ecg" {% if not lsl_started %}disabled{% endif %}>ECG with Heart Rate</button>
37-
<button type="submit" name="app_name" value="emgenvelope" {% if not lsl_started %}disabled{% endif %}>EMG with Envelope</button>
38-
<button type="submit" name="app_name" value="eog" {% if not lsl_started %}disabled{% endif %}>EOG with Blinks</button>
39-
<button type="submit" name="app_name" value="ffteeg" {% if not lsl_started %}disabled{% endif %}>EEG with FFT</button>
36+
<button type="submit" name="app_name" value="heartbeat_ecg"
37+
class="{% if 'heartbeat_ecg' in running_apps %}running{% else %}not-running{% endif %}"
38+
{% if not lsl_started %}disabled{% endif %}>
39+
ECG with Heart Rate
40+
</button>
41+
<button type="submit" name="app_name" value="emgenvelope"
42+
class="{% if 'emgenvelope' in running_apps %}running{% else %}not-running{% endif %}"
43+
{% if not lsl_started %}disabled{% endif %}>
44+
EMG with Envelope
45+
</button>
46+
<button type="submit" name="app_name" value="eog"
47+
class="{% if 'eog' in running_apps %}running{% else %}not-running{% endif %}"
48+
{% if not lsl_started %}disabled{% endif %}>
49+
EOG with Blinks
50+
</button>
51+
<button type="submit" name="app_name" value="ffteeg"
52+
class="{% if 'ffteeg' in running_apps %}running{% else %}not-running{% endif %}"
53+
{% if not lsl_started %}disabled{% endif %}>
54+
EEG with FFT
55+
</button>
4056
</form>
4157
</div>
4258

4359
<!-- Row 2: Game, GUI, Keystroke, CSVPlotter -->
4460
<div class="row">
4561
<form action="/run_app" method="POST">
46-
<button type="submit" name="app_name" value="game" {% if not lsl_started %}disabled{% endif %}>Force Ball Game</button>
47-
<button type="submit" name="app_name" value="gui" {% if not lsl_started %}disabled{% endif %}>GUI of 6 Channels</button>
48-
<button type="submit" name="app_name" value="keystroke" {% if not lsl_started %}disabled{% endif %}>Keystroke Emulator</button>
49-
<button type="submit" name="app_name" value="csvplotter" {% if not lsl_started %}disabled{% endif %}>CSV Plotter</button>
62+
<button type="submit" name="app_name" value="game"
63+
class="{% if 'game' in running_apps %}running{% else %}not-running{% endif %}"
64+
{% if not lsl_started %}disabled{% endif %}>
65+
Force Ball Game
66+
</button>
67+
<button type="submit" name="app_name" value="gui"
68+
class="{% if 'gui' in running_apps %}running{% else %}not-running{% endif %}"
69+
{% if not lsl_started %}disabled{% endif %}>
70+
GUI of 6 Channels
71+
</button>
72+
<button type="submit" name="app_name" value="keystroke"
73+
class="{% if 'keystroke' in running_apps %}running{% else %}not-running{% endif %}"
74+
{% if not lsl_started %}disabled{% endif %}>
75+
Keystroke Emulator
76+
</button>
77+
<button type="submit" name="app_name" value="csvplotter"
78+
class="{% if 'csvplotter' in running_apps %}running{% else %}not-running{% endif %}"
79+
{% if not lsl_started %}disabled{% endif %}>
80+
CSV Plotter
81+
</button>
5082
</form>
5183
</div>
5284
</div>
5385
</div>
86+
<script> // For checking the running status of the apps
87+
function updateAppStatus() {
88+
fetch('/app_status')
89+
.then(response => response.json())
90+
.then(statuses => {
91+
Object.keys(statuses).forEach(app => {
92+
const button = document.querySelector(`button[value="${app}"]`);
93+
if (statuses[app]) {
94+
button.classList.add("running");
95+
button.classList.remove("not-running");
96+
} else {
97+
button.classList.add("not-running");
98+
button.classList.remove("running");
99+
}
100+
});
101+
})
102+
.catch(error => console.error("Error fetching app statuses:", error));
103+
}
104+
105+
setInterval(updateAppStatus, 100); // 100 ms checking
106+
document.addEventListener("DOMContentLoaded", updateAppStatus);
107+
</script>
54108
</body>
55109
</html>

0 commit comments

Comments
 (0)