예제 #1
0
    def trainingScreen(self):
        """ Displays a training screen that displays arrows to different frequency lights
        and records the data within each of these periods. Saves this data in a JSON file """
        window = drawWidget(self)

        # Creates a random ordering of the trials
        channels = [str(i) for i in self.settings["channels"].values()
                    if not i == "None"]
        trials = self.settings["numTrials"]
        time = self.settings["trialLength"]
        bciData = {}

        for t in range(trials):
            print (t+1), "/", trials
            random.shuffle(channels)
            bciData[t] = {}

            for c in channels:
                # Setup
                param = self.settings["freqMap"][c]
                q = multiQueue()

                window.drawCrossTimed(0.4, 3, True)

                # Starting storing packets sent through the pipe when a
                # batch arriving from BCI2000 completes. This is signalled
                # by the last Signal(X,Y) received.
                self.sendPacket.value = True
                lastStamp = bciComms.discardTill(self.pipeReceive, self.endPacketRegex, self.headerRegex)
                lastStamp = int(lastStamp)

                collectProc = Process(target=bciComms.collectData,
                        args=(self.pipeReceive, time, self.headerRegex, self.endPacketRegex, q, lastStamp))
                collectProc.start()

                window.drawArrowTimed(param["x"], param["y"], 200, param["theta"], time, True)

                bciData[t][c] = q.get()

                self.writeFile(bciData[t][c], t, c)
                self.sendPacket.value = False

        # Write the training data to a file, and get the formated training data
        fName, _ = QtGui.QFileDialog.getSaveFileName(self, "Save the test data", "", "JSON (*.json)", "JSON (*.json)")

        # User didn't pick a filename, ask them again and if repeated return
        if fName == "":
            self.showMessage("You didn't select a file! Try again otherwise data is lost")
            fName, _ = QtGui.QFileDialog.getSaveFileName(self, "Save the test data", "", "JSON (*.json)", "JSON (*.json)")

            if fName == "":
                window.close()
                return

        formatedJson = None

        # List of frequencies collected on
        freqList = [int(x.split(" ")[0]) for x in channels]
        freqList.sort()

        with open(fName, "w") as f:
            formatedJson = bciComms.processTrainData(bciData, freqList, f)

        # Saves the training data to the instance field, trains the classifier and transitions to run screen
        self.trainingDataFormater = FormatJson(formatedJson, loadDict=True)
        if self.trainClassifier():
            self.transitionToRun()

        window.close()
예제 #2
0
class BciMain(QtGui.QMainWindow):

    def __init__(self):
        self.IP = '127.0.0.1' # socket.gethostname()
        self.classifier = None
        self.trainingDataFormater = None
        # Default values
        self.settings = {"port": 7337, "numTrials": 10, "trialLength": 10,
                "channels": {},
                "freqMap": {
                    "15 Hz": {"x": 200, "y": 900, "theta": 270},
                    "22 Hz": {"x": 1480, "y": 900, "theta": 270},
                    "17 Hz": {"x": 1560, "y": 760, "theta": 0},
                    "20 Hz": {"x": 120, "y": 750, "theta": 180},
                    "12 Hz": {"x": 820, "y": 900, "theta": 270},

                    "25 Hz": {"x": 1090, "y": 750, "theta": 270}
                    }
                }
        self.directory = os.path.dirname(os.path.realpath(__file__))
        self.settingsFile = os.path.join(self.directory, "config.json")
        super(BciMain, self).__init__()

        # Read in settings file ("config.json")
        self.loadSettings()

        # Set up the menu bar
        self.toolBar()

        # Add option dialog for loading a file or
        # starting a training session
        self.initialPrompt()

        # Networking to BCI
        self.sendPacket = Value(ctypes.c_bool, False, lock=False)
        self.pipeReceive, self.pipeSend = Pipe(True)
        self.proc = Process(target=bciComms.bciConnection, args=(self.IP, int(self.settings["port"]), self.sendPacket, self.pipeSend))
        self.proc.start()

        self.headerRegex = r"SourceTime[\s]+([0-9]+)"
        self.sendPacket.value = True
        self.endPacketRegex = bciComms.determineEndRegex(self.pipeReceive)
        print self.endPacketRegex
        self.sendPacket.value = False

        # Set size and title
        self.setGeometry(300, 300, 640, 480)
        self.center()
        self.setWindowTitle('BCI Operator')

        # Bring to forefront
        self.show()
        self.raise_()

    def closeEvent(self, event):
        """ On close it closes the socket """
        if self.proc.is_alive():
            self.proc.terminate()

    def writeFile(self, data, trial, channel):
        f = open("temp/"+str(trial)+str(channel)+".txt", 'w')

        for d in data:
            f.write(d)

    def trainingScreen(self):
        """ Displays a training screen that displays arrows to different frequency lights
        and records the data within each of these periods. Saves this data in a JSON file """
        window = drawWidget(self)

        # Creates a random ordering of the trials
        channels = [str(i) for i in self.settings["channels"].values()
                    if not i == "None"]
        trials = self.settings["numTrials"]
        time = self.settings["trialLength"]
        bciData = {}

        for t in range(trials):
            print (t+1), "/", trials
            random.shuffle(channels)
            bciData[t] = {}

            for c in channels:
                # Setup
                param = self.settings["freqMap"][c]
                q = multiQueue()

                window.drawCrossTimed(0.4, 3, True)

                # Starting storing packets sent through the pipe when a
                # batch arriving from BCI2000 completes. This is signalled
                # by the last Signal(X,Y) received.
                self.sendPacket.value = True
                lastStamp = bciComms.discardTill(self.pipeReceive, self.endPacketRegex, self.headerRegex)
                lastStamp = int(lastStamp)

                collectProc = Process(target=bciComms.collectData,
                        args=(self.pipeReceive, time, self.headerRegex, self.endPacketRegex, q, lastStamp))
                collectProc.start()

                window.drawArrowTimed(param["x"], param["y"], 200, param["theta"], time, True)

                bciData[t][c] = q.get()

                self.writeFile(bciData[t][c], t, c)
                self.sendPacket.value = False

        # Write the training data to a file, and get the formated training data
        fName, _ = QtGui.QFileDialog.getSaveFileName(self, "Save the test data", "", "JSON (*.json)", "JSON (*.json)")

        # User didn't pick a filename, ask them again and if repeated return
        if fName == "":
            self.showMessage("You didn't select a file! Try again otherwise data is lost")
            fName, _ = QtGui.QFileDialog.getSaveFileName(self, "Save the test data", "", "JSON (*.json)", "JSON (*.json)")

            if fName == "":
                window.close()
                return

        formatedJson = None

        # List of frequencies collected on
        freqList = [int(x.split(" ")[0]) for x in channels]
        freqList.sort()

        with open(fName, "w") as f:
            formatedJson = bciComms.processTrainData(bciData, freqList, f)

        # Saves the training data to the instance field, trains the classifier and transitions to run screen
        self.trainingDataFormater = FormatJson(formatedJson, loadDict=True)
        if self.trainClassifier():
            self.transitionToRun()

        window.close()

    def trainClassifier(self):
        """ Trains the classifier. If any error occurs, it prints the exception.
        Returns true if training was successful and false otherwise """
        try:
            self.classifier = NaiveBayes(self.trainingDataFormater.data())
            return True
        except Exception as e:
            BciMain.showMessage("Could not train classifier. Error:\n" + str(e))
            return False

    def transitionToRun(self):
        """ Sets the main bci app to display a run button to start a run. This
        should only happen after the classifier was properly trained """
        # Layout manager
        hbox = QtGui.QHBoxLayout()
        widget = QtGui.QWidget()

        # Buttons
        btn1 = QtGui.QPushButton("Start Run", widget)
        btn1.clicked.connect(self.runScreen)

        hbox.addWidget(btn1)
        widget.setLayout(hbox)
        self.setCentralWidget(widget)

    def runScreen(self):
        from BCIFront.gui.questions import questionsRunScreen
        self.run = questionsRunScreen(self, self.classifier)

    # Reads in file and updates dictionary. If it doesn't exist
    # it is created with default values
    def loadSettings(self):
        """ Loads settings from the config.json file in the directory containing
        this file. If the file doesn't exist, as would be the case on the first
        run, the file is made with default settings
        """
        # Try to open file
        try:
            jFile = open(self.settingsFile, "r")
        except IOError:
            # Doesn't exist so we create it
            jFile = open(self.settingsFile, "w")
            json.dump(self.settings, jFile)
            return

        # Try to parse json file
        try:
            self.settings = json.load(jFile)
            #print self.settings
            #print "File: " + json.load(jFile)
        except ValueError:
            self.showMessage("""Could not load settings file.\n
                             To reset, delete the config.json file""")


    def center(self):
        """ Centers the application in the middle of the screen """
        qr = self.frameGeometry()
        cp = QtGui.QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())


    def initialPrompt(self):
        """ Returns a widget that contains the buttons to either load a
        training or start a new one """

        # Layout manager
        hbox = QtGui.QHBoxLayout()
        widget = QtGui.QWidget()

        # Buttons
        btn1 = QtGui.QPushButton("Start Training", widget)
        btn1.clicked.connect(self.trainingScreen)

        btn2 = QtGui.QPushButton("Load Train File", widget)
        btn2.clicked.connect(self.loadFile)

        hbox.addWidget(btn1)
        hbox.addWidget(btn2)
        widget.setLayout(hbox)
        self.setCentralWidget(widget)


    def toolBar(self):
        """ Creates a tool bar for the main application """

        ipAction= QtGui.QAction('&IP', self)
        ipAction.triggered.connect(self.showIP)

        settingAction = QtGui.QAction('&Settings', self)
        settingAction.setStatusTip('Settings Page')
        settingAction.triggered.connect(self.settingsPage)

        trainScreenAction = QtGui.QAction('&Train Screen', self)
        trainScreenAction.setStatusTip('Training Screen')
        trainScreenAction.triggered.connect(self.initialPrompt)

        exitAction = QtGui.QAction('&Exit', self)
        exitAction.setStatusTip('Exit application')
        exitAction.triggered.connect(self.close)

        self.toolbar = self.addToolBar("Bar")
        self.toolbar.addAction(ipAction)
        self.toolbar.addAction(settingAction)
        self.toolbar.addAction(trainScreenAction)
        self.toolbar.addAction(exitAction)

    def showIP(self):
        """ Displays a dialog showing the computers IP. This is usefully
        when setting the BCI2000 output parameter
        """

        # Get IP
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        try:
            s.connect(("gmail.com",80))
            ip = s.getsockname()[0]
            self.showMessage("IP: " + ip + "\nHostname: " + socket.gethostname())
        except socket.error as e:
            self.showMessage("""Could not find IP. Possibly not connected to
                           internet. Error code: \n\n""" + str(e))

        finally:
            s.close()

    @classmethod
    def showMessage(cls, string):
        """ Shows a popup screen with the given message """

        msgBox = QtGui.QMessageBox()
        msgBox.setText(string)
        msgBox.show()
        msgBox.raise_()
        msgBox.exec_()


    def settingsPage(self):
        """ Pops up a setting page and then on exit, updates the settings
        and saves them to a file
        """
        s = SettingsPage(self.settings)
        if s.exec_() == QtGui.QDialog.Accepted:
            self.settings["port"] = int(s.portEdit.text())
            self.settings["numTrials"] = int(s.trialNumbEdit.text())
            self.settings["trialLength"] = int(float(s.trialLengthEdit.text()))
            self.settings["channels"] = {}


            # Create a dictionary of the channels to their set index
            for i, combo in enumerate(s.channelValues):
                self.settings["channels"][str(i)] = combo.currentText()

            jFile = open(self.settingsFile, "w")
            json.dump(self.settings, jFile)

    # Prompts for a file and then formats it and saves the list
    # of [("label", [data])] to an instance variable
    def loadFile(self):
        """ Prompts the user for a training file and if it is appropriately formated,
        trains the classifier. Supports both old matlab format and the json format
        generated by this program
        """
        fname, _ = QtGui.QFileDialog.getOpenFileName(self, 'Open file')

        if fname == "":
            self.showMessage("No file was trained with!")
            return

        try:
            formater = FormatJson(fname)
            self.trainingDataFormater = formater

            if self.trainClassifier():
                self.transitionToRun()
        except Exception as e:
            self.showMessage("Couldn't open file: \n" + str(e))