From d8a29cbd9e5fbd126745220696b2789032ed444f Mon Sep 17 00:00:00 2001 From: Bryan Erickson <144630755+Spyro3277@users.noreply.github.com> Date: Fri, 20 Mar 2026 00:19:17 -0500 Subject: [PATCH 1/3] Added buttons to switch between headsets This is currently frontend only, there is no backend for Neurosity crown to actually connect. --- ArtificialIntelligence.qml | 117 +++++++++++++++++++++++++++++++++- GUI5.py | 24 +++++-- ReadBrain.qml | 124 +++++++++++++++++++++++++++++++++++-- 3 files changed, 253 insertions(+), 12 deletions(-) diff --git a/ArtificialIntelligence.qml b/ArtificialIntelligence.qml index 65487ecf..fbc79d3f 100644 --- a/ArtificialIntelligence.qml +++ b/ArtificialIntelligence.qml @@ -994,9 +994,122 @@ Rectangle { } } - Item { Layout.fillHeight: true } // spacer + + // Buttons to choose the source headset------------ + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: false + Layout.preferredHeight: Math.min(root.height * 0.25, 220) + Layout.minimumHeight: root.minControlSize + color: "#1f2740" + radius: 8 + border.color: "#1a2035" + + ColumnLayout { + anchors.fill: parent + anchors.margins: 16 + spacing: 12 + + // Title + Text { + text: "Choose Headset" + color: "white" + font.bold: true + font.pixelSize: Math.max(10, Math.min(32, root.height * 0.05)) + horizontalAlignment: Text.AlignHCenter + Layout.alignment: Qt.AlignHCenter + } + + + Item { + Layout.fillHeight: true + } + + // Buttons row + RowLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + spacing: 16 + + // OpenBCI + Rectangle { + Layout.fillWidth: true + Layout.minimumWidth: root.minControlSize + Layout.minimumHeight: root.minControlSize + Layout.maximumWidth: 260 + implicitWidth: Math.max(root.minControlSize, + Math.min(root.width * 0.12, 220)) + implicitHeight: Math.max(root.minControlSize, + Math.min(root.height * 0.07, 80)) + radius: 8 + color: "#2d7a4a" + border.color: currentFramework === "OpenBCI" ? "yellow" : "#2d7a4a" + border.width: currentFramework === "OpenBCI" ? 3 : 1 + + Text { + anchors.centerIn: parent + text: "OpenBCI" + color: currentFramework === "OpenBCI" ? "yellow" : "white" + font.bold: true + font.pixelSize: Math.max(10, + Math.min(18, parent.height * 0.35)) + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.NoWrap + } + + MouseArea { + anchors.fill: parent + onClicked: { + currentFramework = "OpenBCI" + backend.setBCISource("openbci") + + } + } + } + + // Neurosity + Rectangle { + Layout.fillWidth: true + Layout.minimumWidth: root.minControlSize + Layout.minimumHeight: root.minControlSize + Layout.maximumWidth: 260 + implicitWidth: Math.max(root.minControlSize, + Math.min(root.width * 0.12, 220)) + implicitHeight: Math.max(root.minControlSize, + Math.min(root.height * 0.07, 80)) + radius: 8 + color: "#2d7a4a" + border.color: currentFramework === "Neurosity" ? "yellow" : "#2d7a4a" + border.width: currentFramework === "Neurosity" ? 3 : 1 + + Text { + anchors.centerIn: parent + text: "Neurosity" + color: currentFramework === "Neurosity" ? "yellow" : "white" + font.bold: true + font.pixelSize: Math.max(10, + Math.min(18, parent.height * 0.35)) + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.NoWrap + } + + MouseArea { + anchors.fill: parent + onClicked: { + currentFramework = "Neurosity" + backend.setBCISource("Neurosity") + + } + } + } + } + } + } + } } } } } -} diff --git a/GUI5.py b/GUI5.py index 9b688328..dcdb0fc1 100644 --- a/GUI5.py +++ b/GUI5.py @@ -26,14 +26,15 @@ from developers_api import DevelopersAPI + from NA06_Manual_Control import ManualNaoController from NA06_Manual_Control.camera_view import DroneCameraController # Import BCI connection for brainwave prediction try: - sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), 'random-forest-prediction'))) - from client.brainflow1 import bciConnection, DataMode + sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), 'prediction-random-forest', 'tensorflow', 'client'))) + from brainflow1 import bciConnection, DataMode BCI_AVAILABLE = True except ImportError as e: print(f"Warning: BCI connection not available: {e}") @@ -889,6 +890,20 @@ def convert_pdfs_to_images(self): self.imagesReady.emit(self.image_paths) # Send data to QML + #Allows the user to switch between the OpenBCI or Neurosity Crown headsets + @Slot(str) + def setBCISource(self, source): + self.logMessage.emit(f"Switching BCI source to: {source}") + + if source == "neurosity": + self.current_bci_source = "neurosity" + + elif source == "openbci": + self.current_bci_source = "openbci" + + else: + self.logMessage.emit("Unknown BCI source selected") + @Slot(str) def setDataMode(self, mode): """ @@ -974,7 +989,4 @@ def init_live_board(self): # Ensure image model updates correctly backend.imagesReady.connect(lambda images: engine.rootContext().setContextProperty("imageModel", images)) - sys.exit(app.exec()) - - - + sys.exit(app.exec()) \ No newline at end of file diff --git a/ReadBrain.qml b/ReadBrain.qml index 3c3fff7c..7867732d 100644 --- a/ReadBrain.qml +++ b/ReadBrain.qml @@ -8,8 +8,10 @@ import QtQuick3D 6.7 // Read Brain view Rectangle { + id: root property string selectedModel: "Random Forest" property string currentFramework: "PyTorch" + property string selectedHeadset: "OpenBCI" color: "#718399" Layout.fillWidth: true Layout.fillHeight: true @@ -437,8 +439,8 @@ Rectangle { // Model Selection Buttons in dark box Rectangle { - width: parent.width * 0.9 - height: parent.height * 0.18 + width: parent.width * 0.95 + height: parent.height * 0.25 color: "#2c3e50" Column { @@ -449,7 +451,7 @@ Rectangle { // Model Selection Row Row { width: parent.width - height: (parent.height - parent.spacing) * 0.5 + height: (parent.height - parent.spacing * 2) / 3 spacing: parent.width * 0.02 Rectangle { @@ -534,7 +536,7 @@ Rectangle { // Framework Selection Row Row { width: parent.width - height: (parent.height - parent.spacing) * 0.5 + height: (parent.height - parent.spacing * 2) / 3 spacing: parent.width * 0.02 Rectangle { @@ -611,7 +613,71 @@ Rectangle { } } } + + } + + //Headset select row + Row{ + width: parent.width + height: (parent.height - parent.spacing * 2) / 3 + spacing: parent.width * 0.02 + + Rectangle { + width: (parent.width - parent.spacing ) / 2 + height: parent.height + color: "#2d7a4a" + radius: 5 + border.color: selectedHeadset === "OpenBCI" ? "yellow" : "#4a9d6f" + border.width: selectedHeadset === "OpenBCI" ? 3 : 1 + + Text { + text: "OpenBCI" + font.pixelSize: parent.height * 0.3 + font.bold: true + color: selectedHeadset === "OpenBCI" ? "yellow" : "white" + anchors.centerIn: parent + } + + MouseArea { + anchors.fill: parent + onClicked: { + selectedHeadset = "OpenBCI" + backend.setBCISource("openbci") + + } + } + } + + Rectangle { + width: (parent.width - parent.spacing ) / 2 + height: parent.height + color: "#2d7a4a" + radius: 5 + border.color: selectedHeadset === "Neurosity" ? "yellow" : "#4a9d6f" + border.width: selectedHeadset === "Neurosity" ? 3 : 1 + + Text { + text: "Neurosity" + font.pixelSize: parent.height * 0.3 + font.bold: true + color: selectedHeadset === "Neurosity" ? "yellow" : "white" + anchors.centerIn: parent + } + + MouseArea { + anchors.fill: parent + onClicked: { + selectedHeadset = "Neurosity" + backend.setBCISource("neurosity") + + } + } + } + } + + + } } } @@ -689,5 +755,55 @@ Rectangle { } onClicked: backend.setDataMode("synthetic") } + } + Row { + + width: parent.width * 0.25 + height: parent.height * 0.06 + spacing: width * 0.02 + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.leftMargin: parent.width * 0.14 + anchors.bottomMargin: parent.height * 0.01 + + RadioButton { + id: neurosityRadio + text: "Neurosity" + font.pixelSize: parent.width * 0.04 + font.bold: true + checked: true // default + + contentItem: Text { + text: neurosityRadio.text + color: "white" + font.pixelSize: neurosityRadio.font.pixelSize + font.bold: neurosityRadio.font.bold + verticalAlignment: Text.AlignVCenter + leftPadding: neurosityRadio.indicator.width + neurosityRadio.spacing + } + + onClicked: backend.setBCISource("neurosity") + } + + RadioButton { + id: openbciRadio + text: "OpenBCI" + font.pixelSize: parent.width * 0.04 + font.bold: true + + contentItem: Text { + text: openbciRadio.text + color: "white" + font.pixelSize: openbciRadio.font.pixelSize + font.bold: openbciRadio.font.bold + verticalAlignment: Text.AlignVCenter + leftPadding: openbciRadio.indicator.width + openbciRadio.spacing + } + + onClicked: backend.setBCISource("openbci") + } + } + + } From a63cc4eaaf45db2850ee4d17920333b8695d5b9c Mon Sep 17 00:00:00 2001 From: Bryan Erickson <144630755+Spyro3277@users.noreply.github.com> Date: Fri, 20 Mar 2026 17:24:22 -0500 Subject: [PATCH 2/3] Added frontend functionality for choosing BCI headset --- ArtificialIntelligence.qml | 24 +++++++++++-------- ReadBrain.qml | 48 +------------------------------------- 2 files changed, 15 insertions(+), 57 deletions(-) diff --git a/ArtificialIntelligence.qml b/ArtificialIntelligence.qml index fbc79d3f..a4b6f8fa 100644 --- a/ArtificialIntelligence.qml +++ b/ArtificialIntelligence.qml @@ -51,6 +51,9 @@ Rectangle { // Framework selection state property string currentFramework: "PyTorch" + //Headset slection state + property string currentHeadset: "OpenBCI" + // TOP-LEVEL LAYOUT ColumnLayout { anchors.fill: parent @@ -996,7 +999,6 @@ Rectangle { // Buttons to choose the source headset------------ - Rectangle { Layout.fillWidth: true Layout.fillHeight: false @@ -1026,7 +1028,7 @@ Rectangle { Layout.fillHeight: true } - // Buttons row + // Select headset buttons RowLayout { Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter @@ -1044,13 +1046,13 @@ Rectangle { Math.min(root.height * 0.07, 80)) radius: 8 color: "#2d7a4a" - border.color: currentFramework === "OpenBCI" ? "yellow" : "#2d7a4a" - border.width: currentFramework === "OpenBCI" ? 3 : 1 + border.color: currentHeadset === "OpenBCI" ? "yellow" : "#2d7a4a" + border.width: currentHeadset === "OpenBCI" ? 3 : 1 Text { anchors.centerIn: parent text: "OpenBCI" - color: currentFramework === "OpenBCI" ? "yellow" : "white" + color: currentHeadset === "OpenBCI" ? "yellow" : "white" font.bold: true font.pixelSize: Math.max(10, Math.min(18, parent.height * 0.35)) @@ -1062,8 +1064,9 @@ Rectangle { MouseArea { anchors.fill: parent onClicked: { - currentFramework = "OpenBCI" + currentHeadset = "OpenBCI" backend.setBCISource("openbci") + logToConsole("Changed headset to OpenBCI") } } @@ -1081,13 +1084,13 @@ Rectangle { Math.min(root.height * 0.07, 80)) radius: 8 color: "#2d7a4a" - border.color: currentFramework === "Neurosity" ? "yellow" : "#2d7a4a" - border.width: currentFramework === "Neurosity" ? 3 : 1 + border.color: currentHeadset === "Neurosity" ? "yellow" : "#2d7a4a" + border.width: currentHeadset === "Neurosity" ? 3 : 1 Text { anchors.centerIn: parent text: "Neurosity" - color: currentFramework === "Neurosity" ? "yellow" : "white" + color: currentHeadset === "Neurosity" ? "yellow" : "white" font.bold: true font.pixelSize: Math.max(10, Math.min(18, parent.height * 0.35)) @@ -1099,8 +1102,9 @@ Rectangle { MouseArea { anchors.fill: parent onClicked: { - currentFramework = "Neurosity" + currentHeadset = "Neurosity" backend.setBCISource("Neurosity") + logToConsole("Changed headset to Neurosity") } } diff --git a/ReadBrain.qml b/ReadBrain.qml index 7867732d..0b3ba589 100644 --- a/ReadBrain.qml +++ b/ReadBrain.qml @@ -757,53 +757,7 @@ Rectangle { } } - Row { - - width: parent.width * 0.25 - height: parent.height * 0.06 - spacing: width * 0.02 - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.leftMargin: parent.width * 0.14 - anchors.bottomMargin: parent.height * 0.01 - - RadioButton { - id: neurosityRadio - text: "Neurosity" - font.pixelSize: parent.width * 0.04 - font.bold: true - checked: true // default - - contentItem: Text { - text: neurosityRadio.text - color: "white" - font.pixelSize: neurosityRadio.font.pixelSize - font.bold: neurosityRadio.font.bold - verticalAlignment: Text.AlignVCenter - leftPadding: neurosityRadio.indicator.width + neurosityRadio.spacing - } - - onClicked: backend.setBCISource("neurosity") - } - - RadioButton { - id: openbciRadio - text: "OpenBCI" - font.pixelSize: parent.width * 0.04 - font.bold: true - - contentItem: Text { - text: openbciRadio.text - color: "white" - font.pixelSize: openbciRadio.font.pixelSize - font.bold: openbciRadio.font.bold - verticalAlignment: Text.AlignVCenter - leftPadding: openbciRadio.indicator.width + openbciRadio.spacing - } - - onClicked: backend.setBCISource("openbci") - } - } + } From 357e80149488675b5b079ff12c8d0858cbe76aad Mon Sep 17 00:00:00 2001 From: Bryan Erickson <144630755+Spyro3277@users.noreply.github.com> Date: Fri, 20 Mar 2026 17:37:44 -0500 Subject: [PATCH 3/3] Minor fix to ArtificialIntelligence.qml --- ArtificialIntelligence.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArtificialIntelligence.qml b/ArtificialIntelligence.qml index a4b6f8fa..0fdc9719 100644 --- a/ArtificialIntelligence.qml +++ b/ArtificialIntelligence.qml @@ -1103,7 +1103,7 @@ Rectangle { anchors.fill: parent onClicked: { currentHeadset = "Neurosity" - backend.setBCISource("Neurosity") + backend.setBCISource("neurosity") logToConsole("Changed headset to Neurosity") }