diff --git a/app.py b/app.py new file mode 100644 index 0000000..a7af96c --- /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 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 + 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", message=None) + except Exception as 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(): + # 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/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 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 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 96% rename from applications/gui.py rename to gui.py index 20c081d..b5031bd 100644 --- a/applications/gui.py +++ b/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 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 99% rename from applications/keystroke.py rename to keystroke.py index 4268674..b85eae3 100644 --- a/applications/keystroke.py +++ b/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 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..bd8cf8b --- /dev/null +++ b/static/style.css @@ -0,0 +1,125 @@ +body { + font-family: Arial, sans-serif; + background-color: #f4f4f4; + margin: 0; + padding: 0; +} + +.container { + text-align: center; + padding: 20px; +} + +.header h1 { + font-size: 2.5em; + margin-bottom: 20px; + color: #333; +} + +.controls button { + padding: 15px 30px; + margin: 10px; + font-size: 18px; + cursor: pointer; + border: none; + 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 { + background-color: hsl(34, 47%, 40%); /* Dark beige on hover */ +} + +button:disabled { + background-color: #cccccc; + cursor: not-allowed; +} + +/* App button layout */ +.app-buttons { + margin-top: 20px; +} + +.row { + display: flex; + justify-content: center; + flex-wrap: wrap; + margin-bottom: 20px; +} + +.app-buttons form { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + gap: 15px; /* Spacing between 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; +} + +/* Hover effect for app buttons */ +.app-buttons button:hover { + background-color: #656d67; /* Darker shade on hover */ + transform: scale(1.1); + opacity: 0.9; +} + +/* 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; +} + +.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 new file mode 100644 index 0000000..ca2fe9c --- /dev/null +++ b/templates/index.html @@ -0,0 +1,55 @@ + + + + + + Chords-Python + + + +
+
+

Chords-Python

+
+ + + {% if message %} + + {% endif %} + +
+ {% if not lsl_started %} +
+ +
+ {% else %} + + {% endif %} +
+ +
+ +
+
+ + + + +
+
+ + +
+
+ + + + +
+
+
+
+ + \ No newline at end of file