From ff1a15c129629686536aba87e913320dda441f6f Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Mon, 30 Dec 2024 16:23:44 +0530 Subject: [PATCH 1/6] Changing Colours of channel data. --- applications/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/gui.py b/applications/gui.py index 20c081d..b5031bd 100644 --- a/applications/gui.py +++ b/applications/gui.py @@ -51,7 +51,7 @@ def init_gui(): global plots, curves plots = [] curves = [] - colors = ['#FF3B3B', '#00FF66', '#FF1493', '#007BFF', '#FFA500', '#FF00FF'] # Different colors for each channel + colors = ['#D10054', '#007A8C', '#0A6847', '#674188', '#E65C19', '#2E073F' ] # Different colors for each channel for i in range(6): plot = pg.PlotWidget(title=f"Channel {i + 1}") # Create a plot widget for each channel layout.addWidget(plot) # Add the plot to the layout From fe1484283f57dc76e734faab87920978a0b137e4 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Mon, 30 Dec 2024 16:24:09 +0530 Subject: [PATCH 2/6] Correct the path of eye_icon --- applications/keystroke.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/keystroke.py b/applications/keystroke.py index 4268674..b85eae3 100644 --- a/applications/keystroke.py +++ b/applications/keystroke.py @@ -170,7 +170,7 @@ def move(event): horizontal_frame = tk.Frame(popup) horizontal_frame.pack(expand=True, pady=10) - eye_icon = PhotoImage(file="media/icons8-eye-30.png") + eye_icon = PhotoImage(file="media\\icons8-eye-30.png") blink_button = tk.Button(horizontal_frame, image=eye_icon, width=70, height=38, bg="#FFFFFF") blink_button.image = eye_icon From b2ab1cedb79f1b282d1ffd38d359a35ddd010166 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Tue, 31 Dec 2024 18:18:14 +0530 Subject: [PATCH 3/6] Web Interface created for ease --- app.py | 77 ++++++++++++++++++ applications/emgenvelope.py => emgenvelope.py | 0 applications/eog.py => eog.py | 0 applications/ffteeg.py => ffteeg.py | 0 applications/game.py => game.py | 0 applications/gui.py => gui.py | 0 .../heartbeat_ecg.py => heartbeat_ecg.py | 0 applications/keystroke.py => keystroke.py | 0 ...pani-and-winchimes-reverberated-146260.wav | Bin .../media => media}/icons8-eye-30.png | Bin static/style.css | 77 ++++++++++++++++++ templates/index.html | 52 ++++++++++++ 12 files changed, 206 insertions(+) create mode 100644 app.py rename applications/emgenvelope.py => emgenvelope.py (100%) rename applications/eog.py => eog.py (100%) rename applications/ffteeg.py => ffteeg.py (100%) rename applications/game.py => game.py (100%) rename applications/gui.py => gui.py (100%) rename applications/heartbeat_ecg.py => heartbeat_ecg.py (100%) rename applications/keystroke.py => keystroke.py (100%) rename {applications/media => media}/brass-fanfare-with-timpani-and-winchimes-reverberated-146260.wav (100%) rename {applications/media => media}/icons8-eye-30.png (100%) create mode 100644 static/style.css create mode 100644 templates/index.html diff --git a/app.py b/app.py new file mode 100644 index 0000000..63e7c14 --- /dev/null +++ b/app.py @@ -0,0 +1,77 @@ +from flask import Flask, render_template, jsonify, request +import subprocess +import psutil +import os +import signal +import sys + +app = Flask(__name__) +lsl_process = None +app_processes = {} + +def is_process_running(name): + for proc in psutil.process_iter(['pid', 'name']): + if name in proc.info['name']: + return True + return False + +@app.route("/") +def home(): + return render_template("index.html", lsl_started=False, lsl_status="Stopped", lsl_color="red") + +@app.route("/start_lsl", methods=["POST"]) +def start_lsl(): + global lsl_process + if lsl_process and lsl_process.poll() is None: + return jsonify({"status": "LSL stream already running", "lsl_started": True}) + try: + # Start the LSL stream as a subprocess + if sys.platform == "win32": + lsl_process = subprocess.Popen(["python", "chords.py", "--lsl"], creationflags=subprocess.CREATE_NO_WINDOW) + else: + lsl_process = subprocess.Popen(["python", "chords.py", "--lsl"]) + + if lsl_process.poll() is None: + return render_template("index.html", lsl_started=True, lsl_status="Running", lsl_color="green") + else: + return render_template("index.html", lsl_started=False, lsl_status="Failed to Start", lsl_color="red") + except Exception as e: + return render_template("index.html", lsl_started=False, lsl_status=f"Error: {e}", lsl_color="red") + +@app.route("/run_app", methods=["POST"]) +def run_app(): + app_name = request.form.get("app_name") + + # Check if the app is already running + if app_name in app_processes and app_processes[app_name].poll() is None: + return jsonify({"status": f"{app_name} is already running"}) + + try: + # Start the app subprocess + if sys.platform == "win32": + process = subprocess.Popen(["python", f"{app_name}.py"], creationflags=subprocess.CREATE_NO_WINDOW) + else: + process = subprocess.Popen(["python", f"{app_name}.py"]) + + app_processes[app_name] = process + return render_template("index.html", lsl_started=True, lsl_status="Running", lsl_color="green") + except Exception as e: + return jsonify({"status": f"Error starting {app_name}: {e}"}) + +@app.route("/stop_lsl", methods=['POST']) +def stop_lsl(): + # Terminate LSL process + if lsl_process and lsl_process.poll() is None: + lsl_process.terminate() + + # Terminate all app processes + for app_name, process in app_processes.items(): + if process.poll() is None: + process.terminate() + + # Shutdown the server gracefully + os._exit(0) + return jsonify({'status': 'LSL Stream and applications stopped and server is shutting down.'}) + +if __name__ == "__main__": + app.run(debug=True) \ No newline at end of file diff --git a/applications/emgenvelope.py b/emgenvelope.py similarity index 100% rename from applications/emgenvelope.py rename to emgenvelope.py diff --git a/applications/eog.py b/eog.py similarity index 100% rename from applications/eog.py rename to eog.py diff --git a/applications/ffteeg.py b/ffteeg.py similarity index 100% rename from applications/ffteeg.py rename to ffteeg.py diff --git a/applications/game.py b/game.py similarity index 100% rename from applications/game.py rename to game.py diff --git a/applications/gui.py b/gui.py similarity index 100% rename from applications/gui.py rename to gui.py diff --git a/applications/heartbeat_ecg.py b/heartbeat_ecg.py similarity index 100% rename from applications/heartbeat_ecg.py rename to heartbeat_ecg.py diff --git a/applications/keystroke.py b/keystroke.py similarity index 100% rename from applications/keystroke.py rename to keystroke.py diff --git a/applications/media/brass-fanfare-with-timpani-and-winchimes-reverberated-146260.wav b/media/brass-fanfare-with-timpani-and-winchimes-reverberated-146260.wav similarity index 100% rename from applications/media/brass-fanfare-with-timpani-and-winchimes-reverberated-146260.wav rename to media/brass-fanfare-with-timpani-and-winchimes-reverberated-146260.wav diff --git a/applications/media/icons8-eye-30.png b/media/icons8-eye-30.png similarity index 100% rename from applications/media/icons8-eye-30.png rename to media/icons8-eye-30.png diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..8bd7222 --- /dev/null +++ b/static/style.css @@ -0,0 +1,77 @@ +body { + font-family: Arial, sans-serif; + background-color: #f4f4f4; + margin: 0; + padding: 0; +} + +.container { + text-align: center; + padding: 20px; +} + +.header h1 { + font-size: 2em; + margin-bottom: 20px; +} + +.controls button, .app-button { + padding: 10px 20px; + margin: 10px; + font-size: 16px; + cursor: pointer; + border: none; + border-radius: 5px; + background-color: #4CAF50; + color: white; +} + +.controls button:hover, .app-button:hover { + background-color: #45a049; +} + +button:disabled { + background-color: #cccccc; + cursor: not-allowed; +} + +/* LSL buttons styles */ +#start_lsl_button, #stop_lsl_button { + width: auto; + margin: 10px; +} + +/* App button layout */ +.app-buttons { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 20px; +} + +/* Row layout for app buttons */ +.row { + display: flex; + justify-content: center; + flex-wrap: wrap; + margin-bottom: 15px; +} + +.app-button { + width: 200px; /* Set fixed width for all app buttons */ + margin: 10px; +} + +/* Status section */ +.status { + margin-top: 20px; + font-size: 18px; +} + +.status h3 { + color: #333; +} + +#lsl_status { + font-weight: bold; +} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..6958cf4 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,52 @@ + + + + + + Chords-Python + + + +
+
+

Chords-Python

+
+ +
+ {% if not lsl_started %} +
+ +
+ {% else %} + + {% endif %} +
+ + + +
+ +
+
+ + + + +
+
+ + +
+
+ + + + +
+
+
+
+ + \ No newline at end of file From fecef683f90b13309a6fcb1517f3de512ba4b158 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Thu, 2 Jan 2025 16:13:50 +0530 Subject: [PATCH 4/6] Add support for Nano-Clone --- chords.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chords.py b/chords.py index bebf721..982abf1 100644 --- a/chords.py +++ b/chords.py @@ -54,9 +54,10 @@ board = "" # Variable for Connected Arduino Board supported_boards = { "UNO-R3": {"sampling_rate": 250, "Num_channels": 6}, - "UNO-CLONE": {"sampling_rate": 250, "Num_channels": 6}, + "UNO-CLONE": {"sampling_rate": 250, "Num_channels": 6}, # Baud Rate 115200 "UNO-R4": {"sampling_rate": 500, "Num_channels": 6}, "RPI-PICO-RP2040": {"sampling_rate": 500, "Num_channels": 3}, + "NANO-CLONE": {"sampling_rate": 250, "Num_channels": 8}, # Baud Rate 115200 } # Initialize gloabal variables for Incoming Data From 3331a5fbfc625ddb3c4f121020acbbdb2e919b16 Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Thu, 2 Jan 2025 17:45:52 +0530 Subject: [PATCH 5/6] UI enhanced, pop-up appears if try to open same app again, if one app open then that button is disabled --- app.py | 6 +-- static/style.css | 106 +++++++++++++++++++++++++++++++------------ templates/index.html | 11 +++-- 3 files changed, 87 insertions(+), 36 deletions(-) diff --git a/app.py b/app.py index 63e7c14..a7af96c 100644 --- a/app.py +++ b/app.py @@ -44,7 +44,7 @@ def run_app(): # Check if the app is already running if app_name in app_processes and app_processes[app_name].poll() is None: - return jsonify({"status": f"{app_name} is already running"}) + return render_template("index.html", lsl_started=True, lsl_status="Running", lsl_color="green", message=f"{app_name} is already Running") try: # Start the app subprocess @@ -54,9 +54,9 @@ def run_app(): process = subprocess.Popen(["python", f"{app_name}.py"]) app_processes[app_name] = process - return render_template("index.html", lsl_started=True, lsl_status="Running", lsl_color="green") + return render_template("index.html", lsl_started=True, lsl_status="Running", lsl_color="green", message=None) except Exception as e: - return jsonify({"status": f"Error starting {app_name}: {e}"}) + return render_template("index.html", lsl_started=True, lsl_status="Running", lsl_color="green", message=f"Error starting {app_name}: {e}") @app.route("/stop_lsl", methods=['POST']) def stop_lsl(): diff --git a/static/style.css b/static/style.css index 8bd7222..bd8cf8b 100644 --- a/static/style.css +++ b/static/style.css @@ -11,23 +11,25 @@ body { } .header h1 { - font-size: 2em; + font-size: 2.5em; margin-bottom: 20px; + color: #333; } -.controls button, .app-button { - padding: 10px 20px; +.controls button { + padding: 15px 30px; margin: 10px; - font-size: 16px; + font-size: 18px; cursor: pointer; border: none; - border-radius: 5px; - background-color: #4CAF50; - color: white; + border-radius: 25px; /* Rounded shape */ + background-color: #d1a56c; /* Beige color */ + color: #333; /* Dark text */ + transition: transform 0.3s ease, background-color 0.3s ease; } -.controls button:hover, .app-button:hover { - background-color: #45a049; +.controls button:hover { + background-color: hsl(34, 47%, 40%); /* Dark beige on hover */ } button:disabled { @@ -35,43 +37,89 @@ button:disabled { cursor: not-allowed; } -/* LSL buttons styles */ -#start_lsl_button, #stop_lsl_button { - width: auto; - margin: 10px; -} - /* App button layout */ .app-buttons { - display: flex; - flex-direction: column; - align-items: center; margin-top: 20px; } -/* Row layout for app buttons */ .row { display: flex; justify-content: center; flex-wrap: wrap; - margin-bottom: 15px; + margin-bottom: 20px; +} + +.app-buttons form { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + gap: 15px; /* Spacing between buttons */ } -.app-button { - width: 200px; /* Set fixed width for all app buttons */ +/* App button styles */ +.app-buttons button { + width: 200px; + height: 80px; margin: 10px; + font-size: 20px; /* Larger font size */ + font-weight: bold; + text-transform: uppercase; /* Stylish text */ + border-radius: 25px; /* Rounded edges */ + border: none; + color: white; /* Font color */ + cursor: pointer; + background-color:#9ba59c; /* gray color */ + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); + transition: transform 0.3s, background-color 0.3s; } -/* Status section */ -.status { - margin-top: 20px; - font-size: 18px; +/* Hover effect for app buttons */ +.app-buttons button:hover { + background-color: #656d67; /* Darker shade on hover */ + transform: scale(1.1); + opacity: 0.9; } -.status h3 { - color: #333; +/* Button active (on click) effect */ +.app-buttons button:active { + transform: scale(1.05); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +button:disabled { + cursor: url('data:image/svg+xml,🚫'), not-allowed; +} + +.popup { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: rgba(255, 255, 255, 0.9); + border: 2px solid #333; + padding: 20px; + border-radius: 10px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + z-index: 1000; + text-align: center; + animation: fade-out 3s forwards; } -#lsl_status { +.popup p { + font-size: 18px; font-weight: bold; + color: #333; +} + +@keyframes fade-out { + 0% { + opacity: 1; + } + 80% { + opacity: 1; + } + 100% { + opacity: 0; + visibility: hidden; + } } \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 6958cf4..ca2fe9c 100644 --- a/templates/index.html +++ b/templates/index.html @@ -11,6 +11,13 @@

Chords-Python

+ + + {% if message %} + + {% endif %}
{% if not lsl_started %} @@ -22,10 +29,6 @@

Chords-Python

{% endif %}
- -
From bab280f4bb238a560cfaad6515f1233627bd785a Mon Sep 17 00:00:00 2001 From: Payal Lakra Date: Fri, 3 Jan 2025 11:42:00 +0530 Subject: [PATCH 6/6] app_requirements.txt modified --- app_requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app_requirements.txt b/app_requirements.txt index d94b22c..9741be3 100644 --- a/app_requirements.txt +++ b/app_requirements.txt @@ -6,4 +6,5 @@ pygame==2.6.1 neurokit2==0.2.10 plotly==5.24.1 pandas==2.2.3 -tk==0.1.0 \ No newline at end of file +tk==0.1.0 +PyAutoGUI==0.9.54 \ No newline at end of file