Skip to content

Commit b3a113b

Browse files
committed
The button issue with state updates has been resolved, and a "Data stored in CSV file" option has been added when LSL starts.
The NPG button still needs fixing.
1 parent 9a8de65 commit b3a113b

File tree

3 files changed

+251
-149
lines changed

3 files changed

+251
-149
lines changed

app.py

Lines changed: 80 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
from flask import Flask, render_template, jsonify, request
1+
from flask import Flask, render_template, request, redirect, url_for, jsonify
22
import subprocess
33
import psutil
4-
import os
54
import signal
65
import sys
76
import atexit
8-
import threading
7+
import time
98

109
app = Flask(__name__)
1110
lsl_process = None
1211
lsl_running = False
1312
npg_running = False
1413
npg_process = None
1514
app_processes = {}
15+
current_message = None
1616

1717
def is_process_running(name):
1818
for proc in psutil.process_iter(['pid', 'name']):
@@ -22,107 +22,126 @@ def is_process_running(name):
2222

2323
@app.route("/")
2424
def home():
25-
return render_template("index.html", lsl_started=False, lsl_status="Stopped", lsl_color="red")
25+
return render_template("index.html", lsl_started=lsl_running, npg_started=npg_running, running_apps=[k for k,v in app_processes.items() if v.poll() is None], message=current_message)
2626

2727
@app.route("/start_lsl", methods=["POST"])
2828
def start_lsl():
29-
global lsl_process, lsl_running
29+
global lsl_process, lsl_running, current_message
30+
save_csv = request.form.get('csv', 'false').lower() == 'true'
31+
32+
if npg_running:
33+
current_message = "Please stop NPG stream first"
34+
return redirect(url_for('home'))
3035

3136
if lsl_running:
32-
return jsonify({"status": "LSL stream already running", "lsl_started": True})
37+
current_message = "LSL stream already running"
38+
return redirect(url_for('home'))
3339

3440
try:
35-
if sys.platform == "win32":
36-
lsl_process = subprocess.Popen(["python", "chords.py", "--lsl"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW, text=True, bufsize=1)
37-
else:
38-
lsl_process = subprocess.Popen(["python", "chords.py", "--lsl"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1)
41+
command = ["python", "chords.py", "--lsl"]
42+
if save_csv:
43+
command.append("--csv")
3944

40-
output = lsl_process.stderr.readline().strip()
41-
print(output)
45+
creation_flags = subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0
46+
lsl_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=creation_flags, text=True, bufsize=1)
4247

48+
time.sleep(2)
49+
output = lsl_process.stderr.readline().strip()
4350
if "No" in output:
51+
current_message = "Failed to start LSL stream"
4452
lsl_running = False
45-
return render_template("index.html", lsl_started= False, lsl_status= "Failed to Start", lsl_color= "red", apps_enabled=False)
4653
else:
54+
current_message = "LSL stream started successfully"
4755
lsl_running = True
48-
return render_template("index.html", lsl_started= True, lsl_status= "Running", lsl_color= "green", apps_enabled=True)
4956

5057
except Exception as e:
51-
return render_template("index.html", lsl_started= False, lsl_status= f"Error: {e}", lsl_color= "red")
52-
53-
def read_npg_output():
54-
global npg_process, npg_running
58+
current_message = f"Error starting LSL: {str(e)}"
59+
lsl_running = False
5560

56-
if npg_process:
57-
for line in iter(npg_process.stdout.readline, ''):
58-
line = line.strip()
59-
if "NPG WebSocket connected!" in line:
60-
npg_running = True
61+
return redirect(url_for('home'))
6162

6263
@app.route("/start_npg", methods=["POST"])
6364
def start_npg():
64-
global npg_process, npg_running
65+
global npg_process, npg_running, current_message
66+
67+
if lsl_running:
68+
current_message = "Please stop LSL stream first"
69+
return redirect(url_for('home'))
6570

6671
if npg_running:
67-
return jsonify({"status": "NPG already running", "npg_started": True})
72+
current_message = "NPG already running"
73+
return redirect(url_for('home'))
6874

6975
try:
70-
if sys.platform == "win32":
71-
npg_process = subprocess.Popen(["python", "npg.py"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, creationflags=subprocess.CREATE_NO_WINDOW, text=True, bufsize=1)
72-
else:
73-
npg_process = subprocess.Popen(["python3", "npg.py"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
76+
creation_flags = subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0
77+
npg_process = subprocess.Popen(["python", "npg.py"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, creationflags=creation_flags, text=True, bufsize=1)
7478

75-
# Start a separate thread to read npg.py output
76-
threading.Thread(target=read_npg_output, daemon=True).start()
77-
return render_template("index.html", npg_started=True, npg_status="Starting...", npg_color="yellow", apps_enabled=False)
79+
time.sleep(2)
80+
for line in iter(npg_process.stdout.readline, ''):
81+
if "NPG WebSocket connected!" in line.strip():
82+
current_message = "NPG stream started successfully"
83+
npg_running = True
84+
break
85+
else:
86+
current_message = "Failed to connect NPG stream"
87+
npg_running = False
7888

7989
except Exception as e:
90+
current_message = f"Error starting NPG: {str(e)}"
8091
npg_running = False
81-
return render_template("index.html", npg_started=False, npg_status=f"Error: {e}", npg_color="red", apps_enabled=False)
92+
93+
return redirect(url_for('home'))
8294

8395
@app.route("/run_app", methods=["POST"])
8496
def run_app():
85-
global lsl_running, npg_running
97+
global current_message
8698
app_name = request.form.get("app_name")
99+
valid_apps = ["heartbeat_ecg", "emgenvelope", "eog", "ffteeg", "game", "beetle", "gui", "keystroke", "csvplotter"]
87100

88101
if not (lsl_running or npg_running):
89-
return render_template("index.html", message="Start LSL or NPG first!", running_apps=app_processes.keys())
102+
current_message = "Start LSL or NPG first!"
103+
return redirect(url_for('home'))
104+
105+
if app_name not in valid_apps:
106+
current_message = "Invalid application"
107+
return redirect(url_for('home'))
90108

91109
if app_name in app_processes and app_processes[app_name].poll() is None:
92-
return render_template("index.html", message=f"{app_name} is already running", running_apps=app_processes.keys())
110+
current_message = f"{app_name} is already running"
111+
return redirect(url_for('home'))
93112

94113
try:
95-
# Start the app subprocess
96-
if sys.platform == "win32":
97-
process = subprocess.Popen(["python", f"{app_name}.py"], creationflags=subprocess.CREATE_NO_WINDOW)
98-
else:
99-
process = subprocess.Popen(["python", f"{app_name}.py"])
100-
114+
creation_flags = subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0
115+
process = subprocess.Popen(["python", f"{app_name}.py"], creationflags=creation_flags)
116+
101117
app_processes[app_name] = process
102-
return render_template("index.html", running_apps=app_processes.keys(), message=None)
118+
current_message = f"{app_name} started successfully"
103119
except Exception as e:
104-
return render_template("index.html", message=f"Error starting {app_name}: {e}", running_apps=app_processes.keys())
120+
current_message = f"Error starting {app_name}: {str(e)}"
105121

106-
@app.route("/app_status", methods=["GET"])
107-
def app_status():
108-
# Check the status of all apps
109-
try:
110-
statuses = {
111-
"lsl_started": lsl_running,
112-
"npg_started": npg_running
113-
}
114-
statuses.update({app_name: (process.poll() is None) for app_name, process in app_processes.items()})
115-
return jsonify(statuses)
116-
except Exception as e:
117-
return jsonify({"error": str(e)}), 500
118-
119-
@app.route("/stop_lsl", methods=['POST'])
120-
def stop_lsl():
122+
return redirect(url_for('home'))
123+
124+
@app.route("/stop_all", methods=['POST'])
125+
def stop_all():
126+
global current_message
121127
stop_all_processes()
122-
return jsonify({'status': 'LSL Stream and applications stopped and server is shutting down.'})
128+
current_message = "All processes stopped"
129+
return redirect(url_for('home'))
130+
131+
def cleanup_processes():
132+
global app_processes
133+
app_processes = {
134+
k: v for k, v in app_processes.items()
135+
if v.poll() is None # Only keep running processes
136+
}
137+
138+
@app.route("/check_app_status", methods=["GET"])
139+
def check_app_status():
140+
cleanup_processes() # Remove finished processes
141+
return jsonify({"running_apps": list(app_processes.keys())})
123142

124143
def stop_all_processes():
125-
global lsl_process, npg_process, app_processes, lsl_running, npg_running
144+
global lsl_process, npg_process, app_processes, lsl_running, npg_running, current_message
126145

127146
# Terminate LSL process
128147
if lsl_process and lsl_process.poll() is None:

static/style.css

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,19 @@ body {
6161
border-radius: 10px;
6262
background: linear-gradient(to right, #ec4899, #a855f7, #3b82f6);
6363
color: #fffffe;
64-
transition: transform 0.3s ease, background-color 0.3s ease;
64+
transition: all 0.3s ease;
65+
}
66+
67+
.controls button.running {
68+
background: rgb(105, 206, 105) !important;
69+
cursor: not-allowed;
70+
color: white;
71+
}
72+
73+
.controls button:disabled:not(.running) {
74+
background: linear-gradient(to right, #ec4899, #a855f7, #3b82f6) !important;
75+
opacity: 1 !important;
76+
cursor: not-allowed;
6577
}
6678

6779
button:disabled {
@@ -140,9 +152,48 @@ button.not-running {
140152
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
141153
z-index: 1000;
142154
text-align: center;
155+
width: 300px;
143156
animation: fade-out 3s forwards;
144157
}
145158

159+
.popup p {
160+
font-size: 18px;
161+
margin-bottom: 20px;
162+
color: #333;
163+
}
164+
165+
.popup-buttons {
166+
display: flex;
167+
justify-content: center;
168+
gap: 15px;
169+
}
170+
171+
.popup-button {
172+
padding: 10px 25px;
173+
font-size: 16px;
174+
cursor: pointer;
175+
border: none;
176+
border-radius: 10px;
177+
color: #fffffe;
178+
transition: transform 0.3s ease;
179+
}
180+
181+
.yes-button {
182+
background: linear-gradient(to right, #3b82f6, #a855f7);
183+
}
184+
185+
.no-button {
186+
background: linear-gradient(to right, #ec4899, #a855f7);
187+
}
188+
189+
.popup-button:hover {
190+
transform: scale(1.05);
191+
}
192+
193+
.popup-button:active {
194+
transform: scale(0.98);
195+
}
196+
146197
.bottom-text {
147198
position: fixed;
148199
bottom: 1px;

0 commit comments

Comments
 (0)