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()
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))