#!/usr/bin/env xdg-open | |||||
[Desktop Entry] | |||||
Version=1.0 | |||||
Type=Application | |||||
Terminal=false | |||||
Icon[C]=/home/ubuntu/Downloads/icon.ico | |||||
Name[C]=BCI | |||||
Exec=/home/ubuntu/Desktop/BCIProjekt/start.sh | |||||
Name=BCI | |||||
Icon=/home/ubuntu/Downloads/icon.ico |
'''-------------------------------------------------------------------------------------------------- | |||||
Im Controller wird in der Init eine View erstellt und gestartet. Zudem wird der Acquisitionserver | |||||
gestartet, die shared Librarz geladen, die Konstanten Initialtisiert. | |||||
Mittels der Funktion Action Performed wird die jeweilige Reaktion auf die Benutyeraktion ausgeloest. | |||||
Hierfuer stehen die verschiedenen eigenen Funktionen die die Reaktion in der richtigen reihenfolge | |||||
an ein neu instanizieertes Modell weitergibt. | |||||
Zudem stehen einige Funktionen zur Informationsuebergabe an die View oder das Modell zur Verfuegung | |||||
''' | |||||
from UIModellVisuell import * | |||||
from UIModellTaktil import * | |||||
from UIModell import * | |||||
import UIViewTKinter as viewTkinter | |||||
from shutil import copyfile | |||||
import ctypes | |||||
import os | |||||
class Controller(): | |||||
def __init__(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
In der Init wird die shared Library geladen, die View erstellt und geoeffnet und die verschiedenen | |||||
Konstanten gesetzt | |||||
''' | |||||
self.getSharedLibrary() | |||||
self.view = viewTkinter.View(self, self.dll.getDefaultPath_visuell()) | |||||
self.modi = "visuellesBCI" | |||||
self.pathOVFile = self.dll.getPathOVFile_visuell() | |||||
self.pathSpatialCfg = self.dll.getSpatialCFGFile_visuell() | |||||
self.pathClassifierCfg = self.dll.getClassifierCFGFile_visuell() | |||||
self.infotext = "" | |||||
self.commands = { | |||||
"copySpelling": self.commandoCopySpelling, | |||||
"stop": self.commandStop, | |||||
"freeSpelling": self.commandFreeSpelling, | |||||
"test": self.test, | |||||
"wechsel": self.wechsel, | |||||
"speicherort": self.setDataset | |||||
} | |||||
self.nexts = { | |||||
"filterXdawn" : self.filterXdawn, | |||||
"filterClassic": self.filterClassic, | |||||
"save": self.speichern | |||||
} | |||||
self.pagesTaktil = { | |||||
"stop": "StartPage", | |||||
"copySpelling": "WorkingPageTaktil", | |||||
"freeSpelling": "WorkingPageTaktil", | |||||
"test": "WorkingPageTaktil" | |||||
} | |||||
self.pagesVisuell = { | |||||
"stop": "StartPage", | |||||
"copySpelling": "WorkingPageVisuell", | |||||
"freeSpelling": "WorkingPageVisuell", | |||||
"test": "WorkingPageVisuell" | |||||
} | |||||
self.acquisitionServer = Modell(self, self.dll) | |||||
self.acquisitionServer.start() | |||||
self.model = None | |||||
self.view.mainloop() | |||||
def getSharedLibrary(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Laedt die shared Library (diese muss sich im gleichen Ordner wie die ausfuehrunde Datei befinden) | |||||
und setzt die Eingabe und Rueckgabewerte. | |||||
''' | |||||
path = os.path.abspath(".") | |||||
path = path + "/dll.so" | |||||
self.dll = ctypes.CDLL(path) | |||||
self.dll.getCommandFreespellingTaktil.argtypes = [] | |||||
self.dll.getCommandFreespellingTaktil.restype = ctypes.c_char_p | |||||
self.dll.getCommandFreespellingVisuell.argtypes = [] | |||||
self.dll.getCommandFreespellingVisuell.restype = ctypes.c_char_p | |||||
self.dll.getCommandCopyspellingTaktil.argtypes = [] | |||||
self.dll.getCommandCopyspellingTaktil.restype = ctypes.c_char_p | |||||
self.dll.getCommandCopyspellingVisuell.argtypes = [] | |||||
self.dll.getCommandCopyspellingVisuell.restype = ctypes.c_char_p | |||||
self.dll.getCommandXDawn_taktil.argtypes = [] | |||||
self.dll.getCommandXDawn_taktil.restype = ctypes.c_char_p | |||||
self.dll.getCommandXDawn_visuell.argtypes = [] | |||||
self.dll.getCommandXDawn_visuell.restype = ctypes.c_char_p | |||||
self.dll.getCommandClassifier_visuell.argtypes = [] | |||||
self.dll.getCommandClassifier_visuell.restype = ctypes.c_char_p | |||||
self.dll.getCommandClassifier_taktil.argtypes = [] | |||||
self.dll.getCommandClassifier_taktil.restype = ctypes.c_char_p | |||||
self.dll.getDefaultPath_visuell.argtypes = [] | |||||
self.dll.getDefaultPath_visuell.restype = ctypes.c_char_p | |||||
self.dll.getDefaultPath_taktil.argtypes = [] | |||||
self.dll.getDefaultPath_taktil.restype = ctypes.c_char_p | |||||
self.dll.getPathOVFile_visuell.argtypes = [] | |||||
self.dll.getPathOVFile_visuell.restype = ctypes.c_char_p | |||||
self.dll.getSpatialCFGFile_visuell.argtypes = [] | |||||
self.dll.getSpatialCFGFile_visuell.restype = ctypes.c_char_p | |||||
self.dll.getClassifierCFGFile_visuell.argtypes = [] | |||||
self.dll.getClassifierCFGFile_visuell.restype = ctypes.c_char_p | |||||
self.dll.getPathOVFile_taktil.argtypes = [] | |||||
self.dll.getPathOVFile_taktil.restype = ctypes.c_char_p | |||||
self.dll.getSpatialCFGFile_taktil.argtypes = [] | |||||
self.dll.getSpatialCFGFile_taktil.restype = ctypes.c_char_p | |||||
self.dll.getClassifierCFGFile_taktil.argtypes = [] | |||||
self.dll.getClassifierCFGFile_taktil.restype = ctypes.c_char_p | |||||
self.dll.getCommandStartAquisitionServer.argtypes = [] | |||||
self.dll.getCommandStartAquisitionServer.restype = ctypes.c_char_p | |||||
def actionPerformed(self, action): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Wird aufgerufen wenn ein Button in der Init gedrueckt wird zuerst wird kontrolliert ob die Seite | |||||
gewechselt werden muss, dann nach dem Buttonnamen die Funktion zugeordnert. Der Funktion zum | |||||
Buttomnamen wird in der Init festgelegt | |||||
''' | |||||
self.changePage(action) | |||||
func = self.commands.get(action) | |||||
if(func is not None): | |||||
func() | |||||
else: | |||||
print("Kommado existiert nicht") | |||||
def changePage(self,action): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Change Page ruft je nach Aktion und Modi die jeweilige Seite auf | |||||
''' | |||||
if(self.modi == "taktilesBCI"): | |||||
page = self.pagesTaktil.get(action) | |||||
elif(self.modi == "visuellesBCI"): | |||||
page = self.pagesVisuell.get(action) | |||||
if(page is not None): | |||||
self.view.changeFrame(page) | |||||
def wechsel(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Wechsel aendert den Modi. Dafuer wird zuerst der aktuelle Modi ueberprueft und dann auf den anderen | |||||
Modi gesetzt. | |||||
''' | |||||
self.resetInfo() | |||||
self.addInfoText("") | |||||
if(self.modi == "taktilesBCI"): | |||||
self.view.setChangeBtnText("Wechsel zu taktilen BCI") | |||||
self.view.setTitleText("visuelles BCI") | |||||
self.view.setDefaultPath(self.dll.getDefaultPath_visuell()) | |||||
self.pathOVFile = self.dll.getPathOVFile_visuell() | |||||
self.pathSpatialCfg = self.dll.getSpatialCFGFile_visuell() | |||||
self.pathClassifierCfg = self.dll.getClassifierCFGFile_visuell() | |||||
self.modi = "visuellesBCI" | |||||
elif(self.modi == "visuellesBCI"): | |||||
self.setTitle("taktiles BCI") | |||||
self.view.setChangeBtnText("Wechsel zu visuellen BCI") | |||||
self.view.setDefaultPath(self.dll.getDefaultPath_taktil()) | |||||
self.pathOVFile = self.dll.getPathOVFile_taktil() | |||||
self.pathSpatialCfg = self.dll.getSpatialCFGFile_taktil() | |||||
self.pathClassifierCfg = self.dll.getClassifierCFGFile_taktil() | |||||
self.modi = "taktilesBCI" | |||||
def speichern(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
wird nach dem Classifier Training aufgerufen und speichert die erstellte .ov Datei und die | |||||
spatial.cfg, classifier.cgf-Dateine unter dem ausgewaehlten Ordnername. | |||||
(Ordnername wird zu Beginn des CopySpellings festgelegt) | |||||
''' | |||||
print("saveFile") | |||||
print(self.file) | |||||
if not os.path.exists(self.file): | |||||
os.mkdir(self.file) | |||||
copyfile(self.pathOVFile, (self.file + "/p300-xdawn-train.ov")) | |||||
copyfile(self.pathClassifierCfg, (self.file + "/p300-classifier.cfg")) | |||||
copyfile(self.pathSpatialCfg, (self.file + "/p300-spatial-filter.cfg")) | |||||
self.changeScreen("StartPage") | |||||
#copyfile(self.pathOVFile, self.file) | |||||
def setDataset(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Legt fest welches Datenset fuer das Freestelling genutzt wird | |||||
''' | |||||
file = self.view.openfiledialog() | |||||
print(file) | |||||
if not file: | |||||
print("nichts ausgewaehlt") | |||||
else: | |||||
if os.path.exists((file + "/p300-classifier.cfg")): | |||||
print("classifier kopiert") | |||||
copyfile((file + "/p300-classifier.cfg"), self.pathClassifierCfg) | |||||
if os.path.exists((file + "/p300-spatial-filter.cfg")): | |||||
print("spatialfilter kopiert") | |||||
copyfile((file + "/p300-spatial-filter.cfg"), self.pathSpatialCfg) | |||||
if os.path.exists((file + "/p300-xdawn-train.ov")): | |||||
print(".ov kopiert") | |||||
copyfile((file + "/p300-xdawn-train.ov"), self.pathOVFile) | |||||
def test(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Funktion zum testen von xDawn und ClassifierTraining | |||||
''' | |||||
self.file = self.view.savefiledialog() | |||||
if(self.modi == "taktilesBCI"): | |||||
self.model = ModellTaktil(self, dll=self.dll) | |||||
elif(self.modi == "visuellesBCI"): | |||||
self.model = ModellVisuell(self, dll=self.dll) | |||||
self.model.setFunktion(self.model.trainXDawn) | |||||
self.model.start() | |||||
def commandoCopySpelling(self): | |||||
self.view.savefiledialog() | |||||
def copyspelling(self, path): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Speichert den gewuenschte Pfad und erstellt anschliessend ein Model (als Thread) und fuehrt die | |||||
Funktion CopySpelling aus. | |||||
''' | |||||
self.file = path | |||||
print(path) | |||||
if self.file == "-1": | |||||
print("kein gueltiger Pfad") | |||||
self.view.changeFrame("StartPage") | |||||
else: | |||||
if(self.modi == "taktilesBCI"): | |||||
self.model = ModellTaktil(self, dll=self.dll) | |||||
elif(self.modi == "visuellesBCI"): | |||||
self.model = ModellVisuell(self, dll=self.dll) | |||||
self.model.setFunktion(self.model.startCopySpelling) | |||||
self.model.start() | |||||
def filterXdawn(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
wird nach dem CopySpelling gestartet. Die Funktion erstellt ein Model (als Thread) und fuehrt die | |||||
Funktion trainXDawn aus. | |||||
''' | |||||
print("filterXDawn-Controller -Modi: " + self.modi) | |||||
if(self.modi == "taktilesBCI"): | |||||
self.model = ModellTaktil(self, dll=self.dll) | |||||
elif(self.modi == "visuellesBCI"): | |||||
self.model = ModellVisuell(self, dll=self.dll) | |||||
self.model.setFunktion(self.model.trainXDawn) | |||||
self.model.start() | |||||
def filterClassic(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
wird nach dem xDawn-Training gestartet. Die Funktion erstellt ein Model (als Thread) und fuehrt die | |||||
Funktion trainClassifier aus. | |||||
''' | |||||
if(self.modi == "taktilesBCI"): | |||||
self.model = ModellTaktil(self, dll=self.dll) | |||||
elif(self.modi == "visuellesBCI"): | |||||
self.model = ModellVisuell(self, dll=self.dll) | |||||
self.model.setFunktion(self.model.trainClassifier) | |||||
self.model.start() | |||||
def commandFreeSpelling(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
startet nach Benutzeraktion das Freespelling: ie Funktion erstellt ein Model (als Thread) und fuehrt die | |||||
Funktion freespelling aus. | |||||
''' | |||||
print("freespelling") | |||||
if(self.modi == "taktilesBCI"): | |||||
self.model = ModellTaktil(self, dll=self.dll) | |||||
elif(self.modi == "visuellesBCI"): | |||||
self.model = ModellVisuell(self, dll=self.dll) | |||||
self.model.setFunktion(self.model.freeSpelling) | |||||
self.model.start() | |||||
def commandStop(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Wird nach Benutzeraktion aufgerufen und stopt, falls vorhanden, den ablaufenden Thread und schliesst | |||||
Openvibe | |||||
''' | |||||
if(self.model is not None): | |||||
self.addInfoText("Aktion: STOP-Befehl\n") | |||||
self.model.stop() | |||||
self.model.join() | |||||
self.model.killProzess() | |||||
self.mode = None | |||||
def stopAcquisitionServer(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Wird nur beim schliessen der GUI aufgerufen und stopt und schliesst den Acquisitionserver | |||||
''' | |||||
self.acquisitionServer.stop() | |||||
self.acquisitionServer.join() | |||||
self.acquisitionServer.killAcquisitionServer() | |||||
self.acquisitionServer = None | |||||
def stop(self, next=None): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Wird aus dem Modell nach Ablauf oder bei Fehler aufgerufen und stopt den aktuellen Thread. | |||||
Anschliessend wird je nach Prozess die naechste Funktion (XDawn-Training oder Classifier) aufgerufen | |||||
''' | |||||
print("Stop aus Modell") | |||||
self.model.stop() | |||||
self.model.join() | |||||
self.model.killProzess() | |||||
self.mode = None | |||||
if(next is not None): | |||||
func = self.nexts.get(next) | |||||
func() | |||||
def addInfoText(self,text): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Fuegt dem Infotext den Text hinzu und schreibt es in die Ausgabe | |||||
''' | |||||
self.infotext = self.infotext + text | |||||
self.view.setInfoText(self.infotext) | |||||
def resetInfo(self): | |||||
self.infotext = "" | |||||
def setTitle(self,text): | |||||
self.view.setTitleText(text) | |||||
def changeScreen(self, pageName): | |||||
self.view.changeFrame(pageName=pageName) | |||||
def getModi(self): | |||||
return self.modi | |||||
def getWindowSize(self): | |||||
return self.view.getWindowSize() | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Das Model steuert stellt die Modi unabhaengigen Funktionen bereit | |||||
''' | |||||
from subprocess import * | |||||
from threading import Thread | |||||
import time | |||||
class Modell(Thread): | |||||
def __init__(self,c, dll): | |||||
Thread.__init__(self) | |||||
self.controller = c | |||||
self.dll = dll | |||||
self.aktiv= True | |||||
def setFunktion(self, func, args=None, kwargs=None): | |||||
pass | |||||
def startCopySpelling(self): | |||||
pass | |||||
def trainXDawn(self): | |||||
pass | |||||
def trainClassifier(self): | |||||
pass | |||||
def freeSpelling(self): | |||||
pass | |||||
def stop(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Die Funktion stop stopt den ablaufenden Thread | |||||
''' | |||||
print("stop thread") | |||||
self.aktiv = False | |||||
def run(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Wird bei Thread.start() aus dem Controller aufgerufen und laeuft ab sobald der Thread destartet wurde | |||||
''' | |||||
print("start thread") | |||||
#self.aktiv = True | |||||
t = Thread(target=self.startAquisitionServer) | |||||
t.setDaemon(True) | |||||
t.start() | |||||
while self.aktiv: | |||||
time.sleep(0.1) | |||||
def killProzessParent(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
schliesst OpenVibe-Designer | |||||
''' | |||||
print('Parent start killing') | |||||
pidOV = 0 | |||||
items = [] | |||||
prozesse = Popen(["ps", "-e"], stdout=PIPE).communicate()[0].strip() | |||||
zeilen = prozesse.split('\n') | |||||
for z in zeilen: | |||||
if(z.find("openvibe-design") != -1): | |||||
z = z.strip() | |||||
items = z.split(' ') | |||||
pidOV = items[0] | |||||
print(pidOV) | |||||
if pidOV is not 0: | |||||
#kill -TERM veranlasst dem Prozess sich selbst zu beenden (nicht erzwungen) | |||||
try: | |||||
Popen(["kill", "-TERM", str(pidOV)], stdout=PIPE).communicate() | |||||
except: | |||||
print("kein killing") | |||||
print("killed openvibe-designer") | |||||
def killAcquisitionServer(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
schliesst den Acquisitionserver | |||||
''' | |||||
print('start killing AS') | |||||
pid = 0 | |||||
items = [] | |||||
prozesse = Popen(["ps", "-e"], stdout=PIPE).communicate()[0].strip() | |||||
zeilen = prozesse.split('\n') | |||||
for z in zeilen: | |||||
if(z.find("openvibe-acquis") != -1): | |||||
z = z.strip() | |||||
items = z.split(' ') | |||||
pid = items[0] | |||||
if pid is not 0: | |||||
#kill -TERM veranlasst dem Prozess sich selbst zu beenden (nicht erzwungen) | |||||
Popen(["kill", "-TERM", str(pid)], stdout=PIPE).communicate() | |||||
print("killed openvibe-acquisitionserver") | |||||
def startAquisitionServer(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
startet den Acquisitionserver und steuert die Benutzerausgabe | |||||
''' | |||||
print("start Aquisitionserver") | |||||
self.controller.resetInfo() | |||||
self.controller.addInfoText('Aquisitionserver -- Bitte starten sie den Server\n') | |||||
command = self.dll.getCommandStartAquisitionServer().split(" ") | |||||
process = Popen(command, stdout=PIPE, universal_newlines=True) | |||||
self.openVibeAktiv = True | |||||
while True: | |||||
output = process.stdout.readline() | |||||
print(output.strip()) | |||||
x1 = output.find("Connection succeeded") | |||||
x2 = output.find("Now acquiring") | |||||
x3 = output.find("Stopping the acquisition") | |||||
x4 = output.find("Disconnecting") | |||||
init = output.find("Loading plugin: Fiddler") | |||||
y = output.find("Error") | |||||
if(x1 != -1): | |||||
self.controller.addInfoText('Aquisitionserver ist verbunden -- Bitte starten Sie den Server\n') | |||||
elif(x2 != -1 ): | |||||
self.controller.resetInfo() | |||||
self.controller.addInfoText("Aquisitionserver gestartet\n") | |||||
self.minimizeWindow("server") | |||||
elif(x3 != -1 ): | |||||
self.controller.addInfoText("AquisitionServer gestoppt -- Bitte starten Sie den Server\n") | |||||
self.positionWindow("server") | |||||
elif(x4 != -1 ): | |||||
self.controller.addInfoText("Verbindung vom Aquisitionserver getrennt!\n") | |||||
self.positionWindow("server") | |||||
elif(init != -1 ): | |||||
time.sleep(1) | |||||
self.positionWindow("server") | |||||
elif(y != -1 ): | |||||
self.controller.addInfoText("Fehler beim Auisitionserver aufgetaucht\n") | |||||
if not self.aktiv: | |||||
print("stop") | |||||
break | |||||
def positionWindow(self, name): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
positioniert das Fenster auf der GUI | |||||
''' | |||||
print("calls positionWindow") | |||||
parameter = self.controller.getWindowSize() | |||||
positionX = int(parameter[0]+ int(parameter[3] * 0.05)) | |||||
positionY = int(parameter[1]) + int(parameter[2]*0.08) | |||||
height = int(parameter[2])*0.55 | |||||
width = int(parameter[3])*0.9 | |||||
windowID = Popen(["xdotool", "search", "--onlyvisible", "--name", name], stdout=PIPE).communicate()[0].strip() | |||||
try: | |||||
print(int(windowID)) | |||||
Popen(["xdotool", "windowsize", windowID, str(width), str(height)], stdout=PIPE).communicate() | |||||
Popen(["xdotool", "windowmove", windowID, str(positionX), str(positionY)], stdout=PIPE).communicate() | |||||
Popen(["xdotool", "windowactivate", windowID], stdout=PIPE).communicate()[0].strip() | |||||
except: | |||||
print("no window") | |||||
print(windowID) | |||||
self.controller.addInfoText("Fehler: kein Fenster gefunden!") | |||||
def minimizeWindow(self, name): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
minimiert das Fenster | |||||
''' | |||||
windowID = Popen(["xdotool", "search", "--onlyvisible", "--name", name], stdout=PIPE).communicate()[0].strip() | |||||
try: | |||||
print(int(windowID)) | |||||
Popen(["xdotool", "windowminimize", windowID], stdout=PIPE).communicate() | |||||
except: | |||||
print("no window") | |||||
print(windowID) | |||||
self.controller.addInfoText("Fehler: kein Fenster gefunden!") | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Das taktile Model steuert den Ablauf der aufrufe der Openvibe Funktionen fuer das taktile BCI | |||||
noch nicht implementiert da noch kein Programm dafuer | |||||
''' | |||||
from subprocess import * | |||||
from threading import Thread | |||||
import time | |||||
from UIModell import * | |||||
import re | |||||
class ModellTaktil(Modell): | |||||
def __init__(self,c, dll): | |||||
Thread.__init__(self) | |||||
Modell.__init__(self, c, dll) | |||||
#self.dll = dll | |||||
self.aktiv= True | |||||
self.openVibeAktiv = False | |||||
def stop(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Die Funktion stop stopt den ablaufenden Thread | |||||
''' | |||||
print("stop thread") | |||||
self.aktiv = False | |||||
def setFunktion(self, func, args=None, kwargs=None): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Mittels dieser Funktion kann die im Thread ablaufende Funktion und deren Argumente eingestellt werden | |||||
''' | |||||
self.func = func | |||||
self.args = args or [] | |||||
self.kwargs = kwargs or {} | |||||
def run(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Wird bei Thread.start() aus dem Controller aufgerufen und laeuft ab sobald der Thread destartet wurde | |||||
''' | |||||
print("start thread") | |||||
#self.aktiv = True | |||||
t = Thread(target=self.func, args=self.args, kwargs=self.kwargs) | |||||
t.setDaemon(True) | |||||
t.start() | |||||
while self.aktiv: | |||||
time.sleep(0.1) | |||||
def startCopySpelling(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Holt aus der shared Librarz die Befehle und startet das Copyspelling. Reagiert auf Error oder | |||||
beenden. Bei Beenden startet es das XDawnTraining, bei Error wird der Vorgang gestoppt. | |||||
''' | |||||
print("start copySpelling") | |||||
self.controller.resetInfo() | |||||
self.controller.addInfoText('Starten des gefuerten Buchstabierens -- ') | |||||
command = self.dll.getCommandCopyspellingTaktil().split(" ") | |||||
process = Popen(command, stdout=PIPE, universal_newlines=True) | |||||
self.openVibeAktiv = True | |||||
while True: | |||||
output = process.stdout.readline() | |||||
print(output.strip()) | |||||
x = output.find("Application terminated") | |||||
y = output.find("Error") | |||||
if(x != -1): | |||||
print("Training finished") | |||||
process.terminate() | |||||
self.controller.addInfoText('Buchstabieren beendet\n') | |||||
self.controller.stop("filterXdawn") | |||||
break | |||||
elif(y != -1 ): | |||||
print("Error occured") | |||||
self.controller.changeScreen("StartPage") | |||||
self.controller.addInfoText("Fehler beim Buchstabieren aufgetaucht\n") | |||||
process.terminate() | |||||
self.controller.stop() | |||||
break | |||||
if not self.aktiv: | |||||
print("stop") | |||||
break | |||||
#self.controller.stop() | |||||
def trainXDawn(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Holt aus der shared Library die Befehle und startet das XDawnTraining. Reagiert auf Error oder | |||||
die Ausgabe das das Training beendet wurde. Bei Beenden startet es das ClassifierTraining, bei Error wird der Vorgang gestoppt. | |||||
''' | |||||
print("Start training Xdawn") | |||||
self.controller.addInfoText('Starten des xDawn-Trainings -- ') | |||||
command = self.dll.getCommandXDawn_taktil() #.split(" ") | |||||
print("Command: " + command) | |||||
command = command.split(" ") | |||||
process = Popen(command, stdout=PIPE, universal_newlines=True) | |||||
self.openVibeAktiv = True | |||||
while self.aktiv: | |||||
output = process.stdout.readline() | |||||
print(output.strip()) | |||||
x = output.find("Training finished and saved") | |||||
y = output.find("Error") | |||||
if(x != -1): | |||||
print("Training finished") | |||||
process.terminate() | |||||
self.controller.addInfoText('Training beendet\n') | |||||
self.controller.stop("filterClassic") | |||||
break | |||||
elif(y != -1 ): | |||||
print("Error occured") | |||||
self.controller.changeScreen("StartPage") | |||||
self.controller.addInfoText("Fehler beim XDawn Training aufgetaucht\n") | |||||
self.controller.stop() | |||||
process.terminate() | |||||
break | |||||
if not self.aktiv: | |||||
print("stop") | |||||
break | |||||
#self.controller.stop() | |||||
#self.controller.filterClassic() | |||||
def trainClassifier(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Holt aus der shared Library die Befehle und startet das ClassifierTraining. Reagiert auf Error oder | |||||
die Ausgabe das das Training beendet wurde. Bei Beenden startet es das speichern der Daten, bei Error wird der Vorgang gestoppt. | |||||
''' | |||||
print("start training Classifier") | |||||
self.controller.addInfoText("Starten des Classifier-Trainings -- ") | |||||
command = self.dll.getCommandClassifier_taktil().split(" ") | |||||
process = Popen(command, stdout=PIPE, universal_newlines=True) | |||||
self.openVibeAktiv = True | |||||
counter = 0 | |||||
acc = 0 | |||||
while True: | |||||
output = process.stdout.readline() | |||||
print(output.strip()) | |||||
x = output.find("aka Classifier trainer") | |||||
accuracy = output.find("Cross-validation test accuracy is ") | |||||
y = output.find("Error") | |||||
if(x != -1): | |||||
counter = counter +1 | |||||
#counter = 18 | |||||
if(counter >= 17): | |||||
print("Training finished") | |||||
self.controller.addInfoText('Training beendet (' + acc + ')\n') | |||||
process.terminate() | |||||
self.controller.stop("save") | |||||
break | |||||
elif(y != -1 ): | |||||
print("Error occured") | |||||
self.controller.changeScreen("StartPage") | |||||
self.controller.addInfoText("Fehler beim Classifier Training aufgetaucht\n") | |||||
process.terminate() | |||||
break | |||||
if(accuracy != -1): | |||||
#print("Output:") | |||||
#print(output) | |||||
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') | |||||
test = output.encode("windows-1252").decode("utf-8") | |||||
i = len("Cross-validation test") | |||||
#print(output[accuracy:accuracy+i+12]) | |||||
#print(test[accuracy+i+12:accuracy+i+40].strip()) | |||||
acc_s = test[accuracy+i:accuracy+i+41].strip() | |||||
acc = ansi_escape.sub('', acc_s) | |||||
print(acc) | |||||
if not self.aktiv: | |||||
print("stop") | |||||
break | |||||
self.controller.changeScreen("StartPage") | |||||
self.controller.stop() | |||||
def freeSpelling(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Holt aus der shared Library die Befehle und startet das Freespelling. Reagiert auf Error oder | |||||
beenden. Daraufhin wird der Vorgang gestoppt. | |||||
''' | |||||
print("start freeSpelling") | |||||
self.controller.resetInfo() | |||||
self.controller.addInfoText('Starten des freien Buchstabierens -- ') | |||||
command = self.dll.getCommandFreespellingTaktil().split(" ") | |||||
process = Popen(command, stdout=PIPE, universal_newlines=True) | |||||
self.openVibeAktiv = True | |||||
while True: | |||||
output = process.stdout.readline() | |||||
print(output.strip()) | |||||
y = output.find("Error") | |||||
x = output.find("Application terminated") | |||||
if(x != -1): | |||||
print("End Spelling") | |||||
process.terminate() | |||||
self.controller.addInfoText('Buchstabieren beendet\n') | |||||
self.controller.changeScreen("StartPage") | |||||
self.controller.stop() | |||||
break | |||||
elif(y != -1 ): | |||||
print("Error occured") | |||||
self.controller.changeScreen("StartPage") | |||||
self.controller.addInfoText("Fehler beim Buchstabieren aufgetaucht\n") | |||||
process.terminate() | |||||
self.controller.stop() | |||||
break | |||||
if not self.aktiv: | |||||
print("stop") | |||||
break | |||||
self.controller.changeScreen("StartPage") | |||||
self.controller.stop() | |||||
def killProzess(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Ruft den Parent auf, dass Openvibe falls es noch laeuft geschlossen wird | |||||
''' | |||||
if(self.openVibeAktiv): | |||||
self.openVibeAktiv = False | |||||
self.killProzessParent() | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Das Visuelle Model steuert den Ablauf der aufrufe der Openvibe Funktionen fuer das visuelle BCI | |||||
''' | |||||
from subprocess import * | |||||
from threading import Thread | |||||
import time | |||||
import re | |||||
from UIModell import Modell | |||||
class ModellVisuell(Modell): | |||||
def __init__(self,c, dll): | |||||
Thread.__init__(self) | |||||
Modell.__init__(self, c, dll) | |||||
self.aktiv= True | |||||
self.openVibeAktiv = False | |||||
def stop(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Die Funktion stop stopt den ablaufenden Thread | |||||
''' | |||||
print("stop thread") | |||||
self.aktiv = False | |||||
def setFunktion(self, func, args=None, kwargs=None): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Mittels dieser Funktion kann die im Thread ablaufende Funktion und deren Argumente eingestellt werden | |||||
''' | |||||
self.func = func | |||||
self.args = args or [] | |||||
self.kwargs = kwargs or {} | |||||
def run(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Wird bei Thread.start() aus dem Controller aufgerufen und laeuft ab sobald der Thread destartet wurde | |||||
''' | |||||
print("start thread") | |||||
#self.aktiv = True | |||||
t = Thread(target=self.func, args=self.args, kwargs=self.kwargs) | |||||
t.setDaemon(True) | |||||
t.start() | |||||
while self.aktiv: | |||||
time.sleep(0.1) | |||||
def startCopySpelling(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Holt aus der shared Librarz die Befehle und startet das Copyspelling. Reagiert auf Error oder | |||||
beenden. Bei Beenden startet es das XDawnTraining, bei Error wird der Vorgang gestoppt. | |||||
Positioniert die visuelle Matrix. | |||||
''' | |||||
print("start copySpelling") | |||||
self.controller.resetInfo() | |||||
self.controller.addInfoText('Starten des gefuerhten Buchstabierens -- ') | |||||
command = self.dll.getCommandCopyspellingVisuell().split(" ") | |||||
process = Popen(command, stdout=PIPE, universal_newlines=True) | |||||
self.openVibeAktiv = True | |||||
while True: | |||||
output = process.stdout.readline() | |||||
print(output.strip()) | |||||
x = output.find("Application terminated") | |||||
y = output.find("Error") | |||||
z = output.find("Initialization") | |||||
if(x != -1): | |||||
print("Training finished") | |||||
process.terminate() | |||||
self.controller.addInfoText('Buchstabieren beendet\n') | |||||
self.controller.stop("filterXdawn") | |||||
break | |||||
elif(y != -1 ): | |||||
print("Error occured") | |||||
self.controller.changeScreen("StartPage") | |||||
self.controller.addInfoText("Fehler beim Buchstabieren aufgetaucht\n") | |||||
process.terminate() | |||||
self.controller.stop() | |||||
break | |||||
elif(z != -1): | |||||
print("Initiation finisched") | |||||
time.sleep(2) | |||||
self.positionWindow("p300") | |||||
if not self.aktiv: | |||||
print("stop") | |||||
break | |||||
#self.controller.stop() | |||||
def trainXDawn(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Holt aus der shared Library die Befehle und startet das XDawnTraining. Reagiert auf Error oder | |||||
die Ausgabe das das Training beendet wurde. Bei Beenden startet es das ClassifierTraining, bei Error wird der Vorgang gestoppt. | |||||
''' | |||||
print("start training Xdawn") | |||||
self.controller.addInfoText('Starten des XDawn/Trainings -- ') | |||||
command = self.dll.getCommandXDawn_visuell().split(" ") | |||||
process = Popen(command, stdout=PIPE, universal_newlines=True) | |||||
self.openVibeAktiv = True | |||||
while self.aktiv: | |||||
output = process.stdout.readline() | |||||
print(output.strip()) | |||||
x = output.find("Training finished and saved") | |||||
y = output.find("Error") | |||||
if(x != -1): | |||||
print("Training finished") | |||||
process.terminate() | |||||
self.controller.addInfoText('Beenden des Trainings\n') | |||||
self.controller.stop("filterClassic") | |||||
break | |||||
elif(y != -1 ): | |||||
print("Error occured") | |||||
self.controller.changeScreen("StartPage") | |||||
self.controller.addInfoText("Fehler beim XDawn Training aufgetaucht\n") | |||||
self.controller.stop() | |||||
process.terminate() | |||||
break | |||||
if not self.aktiv: | |||||
print("stop") | |||||
break | |||||
#self.controller.stop() | |||||
def trainClassifier(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Holt aus der shared Library die Befehle und startet das ClassifierTraining. Reagiert auf Error oder | |||||
die Ausgabe das das Training beendet wurde. Bei Beenden startet es das speichern der Daten, bei Error wird der Vorgang gestoppt. | |||||
''' | |||||
print("start training Classifier") | |||||
self.controller.addInfoText("Starten Classifier-Trainings -- ") | |||||
command = self.dll.getCommandClassifier_visuell().split(" ") | |||||
process = Popen(command, stdout=PIPE, universal_newlines=True) | |||||
self.openVibeAktiv = True | |||||
counter = 0 | |||||
acc = 0 | |||||
while True: | |||||
output = process.stdout.readline() | |||||
print(output.strip()) | |||||
x = output.find("aka Classifier trainer") | |||||
accuracy = output.find("Cross-validation test accuracy is ") | |||||
y = output.find("Error") | |||||
if(x != -1): | |||||
counter = counter +1 | |||||
#counter = 18 | |||||
if(counter >= 17): | |||||
print("Training finished") | |||||
self.controller.addInfoText('Beenden des Training (' + acc + ')\n') | |||||
process.terminate() | |||||
self.controller.stop("save") | |||||
break | |||||
elif(y != -1 ): | |||||
print("Error occured") | |||||
self.controller.changeScreen("StartPage") | |||||
self.controller.addInfoText("Fehler beim Classifier Training aufgetaucht\n") | |||||
process.terminate() | |||||
break | |||||
if(accuracy != -1): | |||||
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') | |||||
test = output.encode("windows-1252").decode("utf-8") | |||||
i = len("Cross-validation test") | |||||
acc_s = test[accuracy+i:accuracy+i+41].strip() | |||||
acc = ansi_escape.sub('', acc_s) | |||||
print(acc) | |||||
if not self.aktiv: | |||||
print("stop") | |||||
break | |||||
self.controller.changeScreen("StartPage") | |||||
self.controller.stop() | |||||
def freeSpelling(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Holt aus der shared Library die Befehle und startet das Freespelling. Reagiert auf Error oder | |||||
beenden. Daraufhin wird der Vorgang gestoppt. Positioniert die visuelle Matrix. | |||||
''' | |||||
print("start freeSpelling") | |||||
self.controller.resetInfo() | |||||
self.controller.addInfoText('Starten des freien Buchstabierens -- ') | |||||
command = self.dll.getCommandFreespellingVisuell().split(" ") | |||||
process = Popen(command, stdout=PIPE, universal_newlines=True) | |||||
self.openVibeAktiv = True | |||||
while True: | |||||
output = process.stdout.readline() | |||||
print(output.strip()) | |||||
y = output.find("Error") | |||||
x = output.find("Schlagwort") | |||||
z = output.find("Initialization") | |||||
if(x != -1): | |||||
print("End Spelling") | |||||
process.terminate() | |||||
self.controller.addInfoText('Buchstabieren beendet\n') | |||||
self.controller.changeScreen("StartPage") | |||||
self.controller.stop() | |||||
break | |||||
elif(y != -1 ): | |||||
print("Error occured") | |||||
self.controller.changeScreen("StartPage") | |||||
self.controller.addInfoText("Fehler beim Buchstabieren aufgetaucht\n") | |||||
process.terminate() | |||||
self.controller.stop() | |||||
break | |||||
elif(z != -1): | |||||
print("Initiation finisched") | |||||
time.sleep(2) | |||||
self.positionWindow("p300") | |||||
if not self.aktiv: | |||||
print("stop") | |||||
break | |||||
self.controller.changeScreen("StartPage") | |||||
self.controller.stop() | |||||
def killProzess(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Ruft den Parent auf, dass Openvibe falls es noch laeuft geschlossen wird | |||||
''' | |||||
if(self.openVibeAktiv): | |||||
self.openVibeAktiv = False | |||||
self.killProzessParent() | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Die View bildet die Oberflaeche ab. | |||||
Sie ist in 3 Sektoren Top, Main und ButtonFrame unterteilt. Der TopFrame beihnahltet die Taskleiste, | |||||
der Button Frame gibt Informationen raus. Im Main Frame sind die Buttons yu starten und stoppen der | |||||
Buchstabierungen. HIerfuer sind extra Seiten erstellt, die beliebig, je nach Situation ausgetauscht | |||||
werden koennen | |||||
''' | |||||
try: | |||||
from Tkinter import * #Fuer Python 2.7 | |||||
import tkFileDialog as fd | |||||
print("python2.7") | |||||
except ImportError: #Fuer Python >3 | |||||
from tkinter import * | |||||
from tkinter import filedialog as fd | |||||
print("python >3") | |||||
from UIController import * | |||||
class View(Tk): | |||||
def __init__(self, c, path_default, *args, **kwargs): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
In der Init wird das Lazout gesetyt und die Funktionen yur erstellung der Frames aufgerufen, | |||||
zudem wird dem schliesen des Fensters sowie den anpassen der Groesse andere Funktionen hinterlegt | |||||
''' | |||||
Tk.__init__(self, *args, **kwargs) | |||||
img = PhotoImage(file='icon.png') | |||||
self.tk.call('wm', 'iconphoto', self._w, img) | |||||
self.controller = c | |||||
self.PATH_DEFAULT = path_default | |||||
self.title("Visuelles Buchstabieren") | |||||
self.h = self.winfo_screenheight() | |||||
self.w = self.winfo_screenwidth() | |||||
self.geometry('{}x{}'.format(self.w,self.h)) | |||||
self.faktor = [(0.073),(0.727), (0.2)] | |||||
#self.resizable(height=False, width= False) | |||||
self.layout = { | |||||
"background": "#00001E", | |||||
"backgroundBtn": "#1C86EE", | |||||
"fontColor": "#FFFFFF", | |||||
"backgroundBar": "#00002E", | |||||
"font": ('Calibri', 20, 'bold'), | |||||
"fontSmall": ('Calibri', 14, 'bold'), | |||||
"fontInfo": ('Calibri', 25) | |||||
} | |||||
self.createTopFrame() | |||||
self.createMainFrame() | |||||
self.createBottomFrame() | |||||
self.bind("<Configure>", self.adjustSize) | |||||
self.protocol("WM_DELETE_WINDOW", self.onClosing) | |||||
def changeFrame(self, pageName): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Setzt dem Frame passend zum pageName in den Vordergrund | |||||
''' | |||||
frame = self.frames[pageName] | |||||
frame.tkraise() | |||||
def adjustSize(self, event): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
passt nach Faktoren die einyelnen Seitenteile der jeweiligen Frame Groesse an | |||||
''' | |||||
if(str(event.widget) =="."): | |||||
self.h = event.height | |||||
self.w = event.width | |||||
self.topFrame.configure(height=(self.h*self.faktor[0]), width=self.w) | |||||
self.mainFrame.configure(height=(self.h*self.faktor[1]), width=self.w) | |||||
self.bottomFrame.configure(height=(self.h*self.faktor[2]), width=self.w) | |||||
self.container.configure(height=(self.h*self.faktor[1]), width=self.w) | |||||
for f in self.frames: | |||||
self.frames[f].configure(height=(self.h*self.faktor[1]), width=self.w) | |||||
self.frames[f].adjustSize(height=(self.h*self.faktor[1]), width=self.w) | |||||
def onClosing(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Stoppt alle laufenden Threads und schliesst OpenVibe und den Aquisitionserver bevor sich das Fenster | |||||
schliesst | |||||
''' | |||||
try: | |||||
print("closing") | |||||
self.controller.commandStop() | |||||
self.controller.stopAcquisitionServer() | |||||
print("destroy") | |||||
self.dialog.destroy() | |||||
except: | |||||
pass | |||||
self.destroy() | |||||
def createTopFrame(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Erstellt die Buttons und Textfelder fuer die Taskleiste und positionier diese im Frame | |||||
''' | |||||
self.topFrame = Frame(self, bg=self.layout["backgroundBar"], height=50, width=500) | |||||
self.changeBtn = Button(self.topFrame, text="Wechsel zu taktilen BCI", command=lambda: self.controller.actionPerformed("wechsel"), height=3, width = 30, font=self.layout["fontSmall"], bg=self.layout["backgroundBtn"], fg=self.layout["fontColor"]) | |||||
self.saveBtn = Button(self.topFrame, text="Auswaehlen der Datei", command=lambda: self.controller.actionPerformed("speicherort"), height=3, width = 30, font=self.layout["fontSmall"], bg=self.layout["backgroundBtn"], fg=self.layout["fontColor"]) | |||||
#self.setBtn = Button(self.topFrame, text="Auswaehlen der Datei", command=lambda: self.controller.actionPerformed("setFile"), height=2, width = 25, font=self.layout["fontSmall"], bg=self.layout["backgroundBtn"], fg=self.layout["fontColor"]) | |||||
self.toplabel = Label(self.topFrame, text="visuelles BCI", font=self.layout["font"], bg=self.layout["backgroundBar"], fg=self.layout["fontColor"], ) | |||||
self.topFrame.grid(column=0, row=0) | |||||
self.topFrame.grid_propagate(0) | |||||
self.topFrame.pack_propagate(0) | |||||
self.changeBtn.grid(column=0, row=0, padx=10,pady=5) | |||||
self.saveBtn.grid(column=1, row=0, padx=10, pady=5) | |||||
#self.setBtn.grid(column=2, row=0, padx=10, pady=5) | |||||
self.toplabel.grid(column=2, row=0, padx=100, pady=2) | |||||
def createMainFrame(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Erstellt und postioniert einen Container als Platzhalter fuer die verschiedenen Seiten. Anscliessend | |||||
erstellt es die verschiedenen Seiten und speicher diese in eine Liste | |||||
''' | |||||
self.mainFrame = Frame(self, bg=self.layout["background"], height=250, width=500) | |||||
self.mainFrame.grid(column=0, row=1) | |||||
self.mainFrame.pack_propagate(0) | |||||
self.container = Frame(self.mainFrame, bg=self.layout["background"], height=250, width=500) | |||||
self.container.pack(side="top", fill="both", expand = True) | |||||
self.container.pack_propagate(0) | |||||
self.container.grid_propagate(0) | |||||
self.frames = {} | |||||
for F in (StartPage, WorkingPageTaktil, WorkingPageVisuell): | |||||
page_name = F.__name__ | |||||
frame = F(parent=self.container, controller=self.controller, layout=self.layout) | |||||
self.frames[page_name] = frame | |||||
frame.grid(row=0, column=0, sticky="nsew") | |||||
self.changeFrame("StartPage") | |||||
def createBottomFrame(self): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Erstellt und positioniert ein Label fuer die ganzen Informationen | |||||
''' | |||||
self.bottomFrame = Frame(self, bg=self.layout["backgroundBar"], height=100, width=500) | |||||
self.bottomlabel = Label(self.bottomFrame, text="Hier stehen Infos\nGanz viele", justify='left', font=self.layout["fontInfo"], bg=self.layout["backgroundBar"], fg=self.layout["fontColor"]) | |||||
self.bottomFrame.grid(column=0, row=2) | |||||
self.bottomFrame.pack_propagate(0) | |||||
self.bottomFrame.grid_propagate(0) | |||||
self.bottomlabel.grid(padx=5,pady=5) | |||||
def setInfoText(self, text): | |||||
self.bottomlabel['text'] = text | |||||
def setTitleText(self,text): | |||||
self.title(text) | |||||
self.toplabel["text"] = text | |||||
def setChangeBtnText(self,text): | |||||
self.changeBtn["text"] = text | |||||
def setDefaultPath(self, path): | |||||
self.PATH_DEFAULT = path | |||||
def openfiledialog(self): | |||||
path = self.PATH_DEFAULT | |||||
#file = fd.askopenfilename(initialdir=path) | |||||
file = fd.askdirectory(initialdir=path) | |||||
return file | |||||
def savefiledialog(self): | |||||
self.dialog = Tk() | |||||
self.dialog.title("Visuelles Buchstabieren") | |||||
h = 100 | |||||
w = 400 | |||||
self.dialog.geometry('{}x{}'.format(w,h)) | |||||
self.dialog.geometry("+{}+{}".format(100,200)) | |||||
l = Label(self.dialog, text="Bitte geben Sie einen Speichernamen ein:") | |||||
eingabefeld = Entry(self.dialog) | |||||
okBtn = Button(self.dialog, text="OK", command=lambda:self.okBtn(eingabefeld.get())) | |||||
l.pack() | |||||
eingabefeld.pack() | |||||
okBtn.pack() | |||||
self.dialog.protocol("WM_DELETE_WINDOW", self.closeDialog) | |||||
self.dialog.mainloop() | |||||
def okBtn(self, text): | |||||
path = self.PATH_DEFAULT + "/" + text | |||||
print(path) | |||||
self.controller.copyspelling(path) | |||||
self.dialog.destroy() | |||||
def closeDialog(self): | |||||
self.controller.copyspelling("-1") | |||||
self.dialog.destroy() | |||||
def getWindowSize(self): | |||||
return [self.winfo_x(), self.winfo_y(), self.winfo_height(), self.winfo_width()] | |||||
class StartPage(Frame): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Bildet die Seite in der die Aktionen ausgesucht werden koennen. Beihaltet 2 Buttons einen zum starten | |||||
des freien Buchstabierens und einen yum starten des gefuerten Buchstabierens | |||||
''' | |||||
def __init__(self, parent, controller, layout): | |||||
self.layout = layout | |||||
Frame.__init__(self, parent, height=250, width=500, bg=self.layout["background"]) | |||||
self.grid_propagate(0) | |||||
self.pack_propagate(0) | |||||
self.controller = controller | |||||
self.container = Frame(self, bg=self.layout["background"], height=250, width=500) | |||||
self.btnFrame = Frame(self.container, bg=self.layout["background"], height=250, width=250) | |||||
self.container.columnconfigure(0, weight=1) # Set weight to row and | |||||
self.container.rowconfigure(0, weight=1) # column where the widget is | |||||
self.container.grid_propagate(0) | |||||
self.container.pack_propagate(0) | |||||
self.container.pack() | |||||
self.btnFrame.grid() | |||||
testBtn = Button(self.btnFrame, text="gefuehrtes Buchstabieren", command=lambda: self.controller.actionPerformed("copySpelling"), height=6, width = 20, font=self.layout["font"], bg=self.layout["backgroundBtn"], fg=self.layout["fontColor"]) | |||||
#testBtn = Button(self.btnFrame, text="copySpelling", command=lambda: self.controller.actionPerformed("test"), height=6, width = 20, font=self.layout["font"], bg=self.layout["backgroundBtn"], fg=self.layout["fontColor"]) | |||||
testBtn.grid(row=0,column=0, padx=25,pady=25) | |||||
freeSpellingBtn = Button(self.btnFrame, text="freies Buchstabieren", command=lambda: self.controller.actionPerformed("freeSpelling"), height=6, width = 20, font=self.layout["font"], bg=self.layout["backgroundBtn"], fg=self.layout["fontColor"]) | |||||
freeSpellingBtn.grid(row=0, column=1, padx=25,pady=25) | |||||
def adjustSize(self, height, width): | |||||
self.container.configure(height=height, width= width) | |||||
self.btnFrame.configure(height=(height*4)/5, width= width/2) | |||||
class WorkingPageTaktil(Frame): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Bildet die Seite nach Auswahl einer Aktion. Beihaltet einen mittig plazierten Buttons zum stoppen | |||||
des ablaufenden Vorgang | |||||
''' | |||||
def __init__(self, parent, controller, layout): | |||||
self.layout = layout | |||||
Frame.__init__(self, parent, height=250, width=500, bg=self.layout["background"]) | |||||
self.controller = controller | |||||
self.columnconfigure(0, weight=1) # Set weight to row and | |||||
self.rowconfigure(0, weight=1) # column where the widget is | |||||
stopBtn = Button(self, text="stop", command=lambda: self.controller.actionPerformed("stop"), height=4, width = 15, font=self.layout["font"], bg=self.layout["backgroundBtn"], fg=self.layout["fontColor"]) | |||||
stopBtn.grid() | |||||
def adjustSize(self, height, width): | |||||
pass | |||||
class WorkingPageVisuell(Frame): | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
Bildet die Seite nach Auswahl einer Aktion. Beihaltet einen mittig unten plazierten Buttons zum | |||||
stoppen des ablaufenden Vorgang | |||||
''' | |||||
def __init__(self, parent, controller, layout): | |||||
self.layout = layout | |||||
Frame.__init__(self, parent, height=250, width=500, bg=self.layout["background"]) | |||||
self.controller = controller | |||||
self.grid_propagate(0) | |||||
self.pack_propagate(0) | |||||
self.windowFrame = Frame(self, height=200, width=500, bg=self.layout["background"]) | |||||
self.windowFrame.grid(column=0, row=0) | |||||
self.buttonFrame = Frame(self, height=50, width=500, bg=self.layout["background"]) | |||||
self.buttonFrame.grid(column=0, row=1) | |||||
self.buttonFrame.columnconfigure(0, weight=1) # Set weight to row and | |||||
self.buttonFrame.rowconfigure(0, weight=1) # column where the widget is | |||||
stopBtn = Button(self.buttonFrame, text="stop", command=lambda: self.controller.actionPerformed("stop"), height=1, width = 15, font=self.layout["font"], bg=self.layout["backgroundBtn"], fg=self.layout["fontColor"]) | |||||
stopBtn.grid() | |||||
def adjustSize(self, height, width): | |||||
self.windowFrame.configure(height=(height*9)/10, width= width) | |||||
self.buttonFrame.configure(height=(height)/10, width= width) |
#include <iostream> | |||||
#include <cstring> | |||||
using namespace std; | |||||
extern "C"{ | |||||
//allgemein | |||||
//------------------------------------------------------------------------------------------------------------------------------------- | |||||
string PATH_OV = "meta/dist/Release/openvibe-designer.sh"; | |||||
string PATH_AquisitionServer = "meta/dist/Release/openvibe-acquisition-server.sh"; | |||||
//visuell | |||||
//------------------------------------------------------------------------------------------------------------------------------------- | |||||
string PATH_FILES_Visual = "Projekte/OpenVibe_THN-p300-bci-main/p300-visual/"; | |||||
string pathOVFile_visuell = "Projekte/OpenVibe_THN-p300-bci-main/p300-visual/signals/p300-xdawn-train.ov"; | |||||
string pathClassifierCFG_visuell = "Projekte/OpenVibe_THN-p300-bci-main/p300-visual/cfg/p300-classifier.cfg"; | |||||
string pathSpatialCFG_visuell = "Projekte/OpenVibe_THN-p300-bci-main/p300-visual/cfg/p300-spatial-filter.cfg"; | |||||
string PATH_DEFAULT_visuell = "Projekte/OpenVibe_THN-p300-bci-main/p300-visual/datasets"; | |||||
string fileCopySpellingVisuell = "p300-xdawn-1-acquisition.xml"; | |||||
string filefreeSpellingVisuell = "p300-xdawn-4-online.xml"; | |||||
string fileXDawnTrainingVisuell = "p300-xdawn-2-train-xDAWN.xml"; | |||||
string fileClassifierTrainingVisuell = "p300-xdawn-3-train-classifier.xml"; | |||||
//taktil | |||||
//------------------------------------------------------------------------------------------------------------------------------------ | |||||
string fileCopySpellingTaktil = "p300-tactile-1-acquisition.xml"; | |||||
string filefreeSpellingTaktil = "p300-tactile-4-online.xml"; | |||||
string fileXDawnTrainingTaktil = "p300-tactile-2-train-xDAWN.xml"; | |||||
string fileClassifierTrainingTaktil = "p300-tactile-3-train-classifier.xml"; | |||||
string PATH_FILES_taktil = "Projekte/OpenVibe_THN-p300-bci-main/p300-tactile/"; | |||||
string pathOVFile_taktil = "Projekte/OpenVibe_THN-p300-bci-main/p300-tactile/signals/p300-xdawn-train.ov"; | |||||
string pathClassifierCFG_taktil = "Projekte/OpenVibe_THN-p300-bci-main/p300-tactile/cfg/p300-classifier.cfg"; | |||||
string pathSpatialCFG_taktil = "Projekte/OpenVibe_THN-p300-bci-main/p300-tactile/cfg/p300-spatial-filter.cfg"; | |||||
string PATH_DEFAULT_taktil = "Projekte/OpenVibe_THN-p300-bci-main/p300-tactile/datasets"; | |||||
string PATH_FILES_ConfigFile = "Projekte/OpenVibe_THN-p300-bci-main/cfg/OV_TACTILE_PANDA.conf"; | |||||
//Befehle | |||||
//------------------------------------------------------------------------------------------------------------------------------------- | |||||
string command = "bash"; | |||||
string nogui = "--no-gui"; | |||||
string play = "--play"; | |||||
string playFast = "--play-fast"; | |||||
string config = "--config"; | |||||
char* getCommandPS(){ | |||||
string s = ""; | |||||
s = s.append("ps").append(" ").append("-e"); | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getCommandFreespellingTaktil() | |||||
{ | |||||
string s = ""; | |||||
s = s.append(command).append(" ").append(PATH_OV).append(" ").append(config).append(" ").append(PATH_FILES_ConfigFile).append(" ").append( play).append(" ").append(PATH_FILES_taktil).append(filefreeSpellingTaktil).append(" ").append(nogui); | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getCommandFreespellingVisuell() | |||||
{ | |||||
string s = ""; | |||||
s = s.append(command).append(" ").append(PATH_OV).append(" ").append( play).append(" ").append(PATH_FILES_Visual).append(filefreeSpellingVisuell).append(" ").append(nogui); | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getCommandCopyspellingTaktil() | |||||
{ | |||||
string s = ""; | |||||
s = s.append(command).append(" ").append(PATH_OV).append(" ").append(config).append(" ").append(PATH_FILES_ConfigFile).append(" ").append( play).append(" ").append(PATH_FILES_taktil).append(fileCopySpellingTaktil).append(" ").append(nogui); | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getCommandCopyspellingVisuell() | |||||
{ | |||||
string s = ""; | |||||
s = s.append(command).append(" ").append(PATH_OV).append(" ").append( play).append(" ").append(PATH_FILES_Visual).append(fileCopySpellingVisuell).append(" ").append(nogui); | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getCommandXDawn_taktil() | |||||
{ | |||||
string s = ""; | |||||
s = s.append(command).append(" ").append(PATH_OV).append(" ").append(config).append(" ").append(PATH_FILES_ConfigFile).append(" ").append( playFast).append(" ").append(PATH_FILES_taktil).append(fileXDawnTrainingTaktil).append(" ").append(nogui); | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getCommandXDawn_visuell() | |||||
{ | |||||
string s = ""; | |||||
s = s.append(command).append(" ").append(PATH_OV).append(" ").append( playFast).append(" ").append(PATH_FILES_Visual).append(fileXDawnTrainingVisuell).append(" ").append(nogui); | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getCommandClassifier_visuell() | |||||
{ | |||||
string s = ""; | |||||
s = s.append(command).append(" ").append(PATH_OV).append(" ").append( playFast).append(" ").append(PATH_FILES_Visual).append(fileClassifierTrainingVisuell).append(" ").append(nogui); | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getCommandClassifier_taktil() | |||||
{ | |||||
string s = ""; | |||||
s = s.append(command).append(" ").append(PATH_OV).append(" ").append(config).append(" ").append(PATH_FILES_ConfigFile).append(" ").append( playFast).append(" ").append(PATH_FILES_taktil).append(fileClassifierTrainingTaktil).append(" ").append(nogui); | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getCommandStartAquisitionServer() | |||||
{ | |||||
string s = ""; | |||||
s = s.append(command).append(" ").append(PATH_AquisitionServer); | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getDefaultPath_visuell() | |||||
{ | |||||
string s = PATH_DEFAULT_visuell; | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getDefaultPath_taktil() | |||||
{ | |||||
string s = PATH_DEFAULT_taktil; | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getPathOVFile_visuell() | |||||
{ | |||||
string s = pathOVFile_visuell; | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getSpatialCFGFile_visuell() | |||||
{ | |||||
string s = pathSpatialCFG_visuell; | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getClassifierCFGFile_visuell() | |||||
{ | |||||
string s = pathClassifierCFG_visuell; | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getPathOVFile_taktil() | |||||
{ | |||||
string s = pathOVFile_taktil; | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getSpatialCFGFile_taktil() | |||||
{ | |||||
string s = pathSpatialCFG_taktil; | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
char* getClassifierCFGFile_taktil() | |||||
{ | |||||
string s = pathClassifierCFG_taktil; | |||||
char char_array[s.length()+1]; | |||||
strcpy(char_array, s.c_str()); | |||||
char* s_convert = char_array; | |||||
return s_convert; | |||||
} | |||||
} | |||||
'''-------------------------------------------------------------------------------------------------- | |||||
In dem File wird eine Instany des Controllers erstellt. Der Controller ist die Schnittstelle | |||||
zwischen GUI und Modell und koordiniert den Ablauf und die Erstellung der GUI | |||||
''' | |||||
from UIController import * | |||||
controller = Controller() |
# --------------------------------- | |||||
# Finds GUSBampCAPI | |||||
# Adds library to target | |||||
# Adds include path | |||||
# --------------------------------- | |||||
GET_PROPERTY(OV_PRINTED GLOBAL PROPERTY OV_TRIED_ThirdPartyGUSBampCAPI) | |||||
IF(WIN32) | |||||
IF("${PLATFORM_TARGET}" STREQUAL "x64") | |||||
SET(GUSBAMP_ARCH "x64") | |||||
ELSE() | |||||
SET(GUSBAMP_ARCH "Win32") | |||||
ENDIF() | |||||
FIND_PATH(PATH_GUSBampCAPI gUSBamp.h PATHS | |||||
"C:/Program Files/gtec/gUSBampCAPI/API" | |||||
"C:/Program Files (x86)/gtec/gUSBampCAPI/API" | |||||
"C:/Program Files/gtec/gUSBampCAPI/API/${GUSBAMP_ARCH}" | |||||
"C:/Program Files (x86)/gtec/gUSBampCAPI/API/${GUSBAMP_ARCH}" | |||||
${LIST_DEPENDENCIES_PATH}) | |||||
IF(PATH_GUSBampCAPI) | |||||
OV_PRINT(OV_PRINTED " Found gtec gUSBampCAPI...") | |||||
INCLUDE_DIRECTORIES(${PATH_GUSBampCAPI}) | |||||
FIND_LIBRARY(LIB_GUSBampCAPI gUSBamp PATHS ${PATH_GUSBampCAPI}) | |||||
IF(LIB_GUSBampCAPI) | |||||
OV_PRINT(OV_PRINTED " [ OK ] lib ${LIB_GUSBampCAPI}") | |||||
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LIB_GUSBampCAPI} ) | |||||
ELSE(LIB_GUSBampCAPI) | |||||
OV_PRINT(OV_PRINTED " [FAILED] lib gUSBamp") | |||||
ENDIF(LIB_GUSBampCAPI) | |||||
# Copy the DLL file at install | |||||
INSTALL(PROGRAMS "${PATH_GUSBampCAPI}/gUSBamp.dll" DESTINATION ${DIST_BINDIR}) | |||||
ADD_DEFINITIONS(-DTARGET_HAS_ThirdPartyGUSBampCAPI) | |||||
SET(OV_ThirdPartyGUSBAmp "YES") | |||||
ELSE() | |||||
OV_PRINT(OV_PRINTED " FAILED to find gtec gUSBampCAPI (optional driver)") | |||||
ENDIF() | |||||
ENDIF(WIN32) | |||||
IF(UNIX) | |||||
# To try other versions of the gtec's library, change the number below | |||||
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".so.1.16") | |||||
SET(CMAKE_FIND_LIBRARY_PREFIXES "lib") | |||||
FIND_LIBRARY(gUSBAmpLinux_LIBRARY NAMES "gUSBampAPIso" PATHS "/usr/lib" "/usr/local/lib") | |||||
IF(gUSBAmpLinux_LIBRARY) | |||||
OV_PRINT(OV_PRINTED " Found gtec gUSBAmpAPILinux...") | |||||
OV_PRINT(OV_PRINTED " [ OK ] Third party lib ${gUSBAmpLinux_LIBRARY}") | |||||
ADD_DEFINITIONS(-DTARGET_HAS_ThirdPartyGUSBampCAPI_Linux) | |||||
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${gUSBAmpLinux_LIBRARY} ) | |||||
SET(OV_ThirdPartyGUSBAmp "YES") | |||||
ELSE() | |||||
OV_PRINT(OV_PRINTED " FAILED to find gtec gUSBAmpAPI Linux... (optional)") | |||||
OV_PRINT(OV_PRINTED " : If it should be found, see that 'libgusbampapi.so.1.16' link exists on the fs, with no further nemeric suffixes in the filename.") | |||||
OV_PRINT(OV_PRINTED " : e.g. do 'cd /usr/lib/ ; ln -s libgusbampapi.so.1.16'. See gtec-bcilab/README for details.") | |||||
ENDIF() | |||||
ENDIF(UNIX) | |||||
SET_PROPERTY(GLOBAL PROPERTY OV_TRIED_ThirdPartyGUSBampCAPI "Yes") | |||||
#pragma once | |||||
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI_Linux | |||||
#include <stdint.h> | |||||
#include <string.h> | |||||
#ifndef MIN | |||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b)) | |||||
#endif | |||||
#ifndef MAX | |||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b)) | |||||
#endif | |||||
template <class T> | |||||
class Queue | |||||
{ | |||||
T* buffer = nullptr; | |||||
int head = 0, tail = 0, size = 0; | |||||
public: | |||||
Queue() { } | |||||
Queue(T* array, const int len) | |||||
{ | |||||
size = len; | |||||
buffer = array; | |||||
} | |||||
void SetBuffer(T* array, const int len) | |||||
{ | |||||
size = len; | |||||
head = tail = 0; | |||||
buffer = array; | |||||
} | |||||
int Get(T* elements, int len) | |||||
{ | |||||
// Trim the length if necessary to only as large as the number of available elements in the buffer | |||||
len = MIN(len, Avail()); | |||||
int nonwrapped = MIN((size - tail), len); | |||||
const int wrapped = len - nonwrapped; | |||||
// memcpy the data starting at the head all the way up to the last element *(storage - 1) | |||||
memcpy(elements, (buffer + tail), nonwrapped * sizeof(T)); | |||||
// If there's still data to copy memcpy whatever remains, starting at the first element *(begin) until the end of data. The first step will have ensured | |||||
// that we don't crash into the tail during this process. | |||||
memcpy((elements + nonwrapped), buffer, wrapped * sizeof(T)); | |||||
// Recalculate head | |||||
tail = (tail + nonwrapped + wrapped) % size; | |||||
return len; | |||||
} | |||||
// Returns the number of bytes actually placed in the array | |||||
int Put(const T* elements, int len) | |||||
{ | |||||
// Trim the length if necessary to only as large as the nuber of free elements in the buffer | |||||
len = MIN(len, Free()); | |||||
// Figure out how much to append to the end of the buffer and how much will overlap onto the start | |||||
int nonwrapped = MIN((size - head), len); | |||||
const int wrapped = len - nonwrapped; | |||||
// memcpy the data starting at the head all the way up to the last element *(storage - 1) | |||||
memcpy((buffer + head), elements, nonwrapped * sizeof(T)); | |||||
// If there's still data to copy memcpy whatever remains onto the beginning of the array | |||||
memcpy(buffer, (elements + nonwrapped), wrapped * sizeof(T)); | |||||
// Re-recalculate head | |||||
head = (head + nonwrapped + wrapped) % size; | |||||
return len; | |||||
} | |||||
// Expand the size of queue without actually modifying any of the contents - useful for copying directly onto the queu buffer | |||||
int Pad(int len) | |||||
{ | |||||
// Trim the length if necessary to only as large as the nuber of free elements in the buffer | |||||
len = MIN(len, Free()); | |||||
// Figure out how much to append to the end of the buffer and how much will overlap onto the start | |||||
const int nonwrapped = MIN((size - head), len), wrapped = len - nonwrapped; | |||||
// Re-recalculate head | |||||
head = (head + nonwrapped + wrapped) % size; | |||||
return len; | |||||
} | |||||
// Removes the oldest entry from the Queue | |||||
void Pop() { if (Avail()) tail = (tail + 1) % size; } | |||||
// Returns the oldest element in the array (the one added before any other) | |||||
T& Tail() { return buffer[tail]; } | |||||
// Returns the newest element in the array (the one added after every other) | |||||
T& Head() { return buffer[(head + size - 1) % size]; } | |||||
T& operator[](int n) { return buffer[tail + n % size]; } | |||||
void Clear() { head = tail = 0; } | |||||
int Avail() const { return (size + head - tail) % size; } | |||||
int Free() const { return (size - 1 - Avail()); } | |||||
// Gets the number of free elements that can be stored contiguously | |||||
int FreeContiguous() { return head < tail ? tail - head - 1 : MIN(size - head, Free()); } | |||||
// Gets a pointer to the next free address in the buffer | |||||
T* NextFreeAddress() { return buffer + head; } | |||||
}; | |||||
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI_Linux |
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI_Linux | |||||
#include "ovasCDriverGTecGUSBampLinux.h" | |||||
#include <toolkit/ovtk_all.h> | |||||
namespace OpenViBE { | |||||
namespace AcquisitionServer { | |||||
CDriverGTecGUSBampLinux::CDriverGTecGUSBampLinux(IDriverContext& ctx) | |||||
: IDriver(ctx), m_settings("AcquisitionServer_Driver_GTecGUSBampLinux", m_driverCtx.getConfigurationManager()), | |||||
m_nSamplePerSentBlock(0), m_sampleSend(nullptr), m_sampleReceive(nullptr), m_sampleBuffer(nullptr), m_currentSample(0), m_currentChannel(0) | |||||
{ | |||||
// Default values | |||||
m_header.setSamplingFrequency(512); | |||||
m_header.setChannelCount(8); | |||||
m_config.ao_config = &m_analogOutConfig; | |||||
// Configure some defaults so the settings are reasonable as soon as the driver loads and the user can tweak them from there | |||||
// Configure the analog waveform to be created by the internal signal generator | |||||
m_analogOutConfig.shape = GT_ANALOGOUT_SINE; | |||||
m_analogOutConfig.frequency = 1; | |||||
m_analogOutConfig.amplitude = 0; | |||||
m_analogOutConfig.offset = 0; | |||||
// This pretty much has to be GT_NOS_AUTOSET, don't know why, so says the documentation | |||||
m_config.number_of_scans = GT_NOS_AUTOSET; | |||||
// Disable the trigger line, digital io scan, slave mode and the shortcut | |||||
m_config.enable_trigger_line = GT_FALSE; | |||||
m_config.scan_dio = GT_FALSE; | |||||
m_config.slave_mode = GT_FALSE; | |||||
m_config.enable_sc = GT_FALSE; | |||||
// Set the mode to just take readings | |||||
m_config.mode = GT_MODE_NORMAL; | |||||
// Set all the blocks A-D to use the common ground and reference voltages | |||||
for (uint32_t i = 0; i < GT_USBAMP_NUM_GROUND; ++i) | |||||
{ | |||||
m_config.common_ground[i] = GT_TRUE; | |||||
m_config.common_reference[i] = GT_TRUE; | |||||
} | |||||
// Configure each input | |||||
for (unsigned char i = 0; i < GT_USBAMP_NUM_ANALOG_IN; ++i) | |||||
{ | |||||
// Should be from 1 - 16, specifies which channel to observe as input i | |||||
m_config.analog_in_channel[i] = i + 1; | |||||
// Don't use any of the filters on channel i | |||||
m_config.bandpass[i] = GT_FILTER_NONE; | |||||
// Don't use any of the notch filters on channel i | |||||
m_config.notch[i] = GT_FILTER_NONE; | |||||
// Don't use any of the other channels for bi-polar derivation | |||||
m_config.bipolar[i] = GT_BIPOLAR_DERIVATION_NONE; | |||||
} | |||||
// Now look for any connected devices. If any exist we'll set the name to the first one found | |||||
char** devices = nullptr; | |||||
size_t nDevice = 0; | |||||
// Refresh and get the list of currently connnected devices | |||||
GT_UpdateDevices(); | |||||
nDevice = GT_GetDeviceListSize(); | |||||
devices = GT_GetDeviceList(); | |||||
// If any devices were found at all, set the combo box to the first one listed | |||||
if (nDevice) { m_deviceName = devices[0]; } | |||||
GT_FreeDeviceList(devices, nDevice); | |||||
// Now retrieve all those configs from the settings file if they are there to be found (don't need to worry about sample rate or channel number though since they're already in the header) | |||||
/*m_settings.add("Header", &m_header); | |||||
m_settings.add("DeviceName", static_cast<std::string*>(&m_deviceName)); | |||||
m_settings.add("Mode", static_cast<int*>(&m_config.mode)); | |||||
m_settings.add("EnableTrigger", static_cast<bool*>(&m_config.enable_trigger_line)); | |||||
m_settings.add("ScanDIO", static_cast<bool*>(&m_config.scan_dio)); | |||||
m_settings.add("SlaveMode", static_cast<bool*>(&m_config.slave_mode)); | |||||
m_settings.add("EnableShortcut", static_cast<bool*>(&m_config.enable_sc)); | |||||
m_settings.add("AnalogOutShape", static_cast<int*>(&m_analogOutConfig.shape)); | |||||
m_settings.add("AnalogOutFrequency", static_cast<int*>(&m_analogOutConfig.frequency)); | |||||
m_settings.add("AnalogOutAmplitude", static_cast<int*>(&m_analogOutConfig.amplitude)); | |||||
m_settings.add("AnalogOutOffset", static_cast<int*>(&m_analogOutConfig.offset));*/ | |||||
/*// Set all the blocks A-D to use the common ground and reference voltages | |||||
for (uint32_t i = 0; i < GT_USBAMP_NUM_GROUND; ++i) | |||||
{ | |||||
std::stringstream gndConfigName, configName; | |||||
gndConfigName << "CommonGround" << i; | |||||
configName << "CommonReference" << i; | |||||
m_settings.add(gndConfigName.str().c_str(), static_cast<bool*>(&m_config.common_ground[i])); | |||||
m_settings.add(configName.str().c_str(), static_cast<bool*>(&m_config.common_reference[i])); | |||||
}*/ | |||||
/*// Configure each input | |||||
for (uint32_t i = 0; i < GT_USBAMP_NUM_ANALOG_IN; ++i) | |||||
{ | |||||
std::stringstream bandpassConfigName, notchConfigName, bipolarConfigName; | |||||
bandpassConfigName << "Bandpass" << i; | |||||
notchConfigName << "Notch" << i; | |||||
bipolarConfigName << "Bipolar" << i; | |||||
m_settings.add(bandpassConfigName.str().c_str(), static_cast<int*>(&m_config.bandpass[i])); | |||||
m_settings.add(notchConfigName.str().c_str(), static_cast<int*>(&m_config.notch[i])); | |||||
m_settings.add(bipolarConfigName.str().c_str(), static_cast<int*>(&m_config.bipolar[i])); | |||||
}*/ | |||||
// This restores saved settings if any, such as sampling rate | |||||
m_settings.load(); | |||||
// Set the sampling rate that may have been changed by load | |||||
m_config.sample_rate = m_header.getSamplingFrequency(); | |||||
// Number of channels that may have been changed by load | |||||
m_config.num_analog_in = m_header.getChannelCount(); | |||||
} | |||||
//___________________________________________________________________// | |||||
// // | |||||
bool CDriverGTecGUSBampLinux::initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback) | |||||
{ | |||||
if (m_driverCtx.isConnected()) return false; | |||||
if (!m_header.isChannelCountSet() || !m_header.isSamplingFrequencySet()) return false; | |||||
// If the scan digital inputs flag is set, the API will return one extra channel outside of the analog data requested, so we need to match that on the header | |||||
if (m_config.scan_dio == GT_TRUE) | |||||
{ | |||||
m_header.setChannelCount(m_config.num_analog_in + 1); | |||||
m_header.setChannelName(m_config.num_analog_in, "Digital"); | |||||
} | |||||
// Allocate buffers for... | |||||
// Sending to OpenViBE | |||||
m_sampleSend = new float[m_header.getChannelCount() * nSamplePerSentBlock]; | |||||
// Receiving from the hardware, | |||||
m_sampleReceive = new float[m_header.getChannelCount() * nSamplePerSentBlock]; | |||||
// Storing the data so we pass it between the two threads - we're using the recommended buffer size put out by gtec, which is enormous | |||||
m_sampleBuffer = new float[GT_USBAMP_RECOMMENDED_BUFFER_SIZE / sizeof(float)]; | |||||
// Set up the queue to help pass the data out of the hardware thread | |||||
m_sampleQueue.SetBuffer(m_sampleBuffer, m_header.getChannelCount() * m_header.getSamplingFrequency() / 8); | |||||
// If any of that allocation fails then give up. Not sure what setting it all to NULL is for, but we'll go with it. | |||||
if (!m_sampleSend || !m_sampleReceive || !m_sampleBuffer) | |||||
{ | |||||
delete[] m_sampleSend; | |||||
delete[] m_sampleReceive; | |||||
delete[] m_sampleBuffer; | |||||
m_sampleSend = m_sampleReceive = m_sampleBuffer = nullptr; | |||||
return false; | |||||
} | |||||
// Apparently this causes the API to print debug info to the console, I'm yet to see any though | |||||
GT_ShowDebugInformation(GT_TRUE); | |||||
// Try to open the device with the configured name, let the user know how it goes | |||||
if (!GT_OpenDevice(m_deviceName.c_str())) | |||||
{ | |||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not open device: " << m_deviceName << "\n"; | |||||
return false; | |||||
} | |||||
if (!GT_SetConfiguration(m_deviceName.c_str(), &m_config)) | |||||
{ | |||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Error << "Could not apply configuration to device: " << m_deviceName << "\n"; | |||||
return false; | |||||
} | |||||
GT_SetDataReadyCallBack(m_deviceName.c_str(), &OnDataReady, static_cast<void*>(this)); | |||||
// Saves parameters | |||||
m_callback = &callback; | |||||
m_nSamplePerSentBlock = nSamplePerSentBlock; | |||||
return true; | |||||
} | |||||
bool CDriverGTecGUSBampLinux::start() | |||||
{ | |||||
if (!m_driverCtx.isConnected()) return false; | |||||
if (m_driverCtx.isStarted()) return false; | |||||
// ... | |||||
// request hardware to start | |||||
// sending data | |||||
// ... | |||||
// Need to reset these in case the device is stopped mid-sample and then started again | |||||
m_currentChannel = m_currentSample = 0; | |||||
GT_StartAcquisition(m_deviceName.c_str()); | |||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Acquisition Started\n"; | |||||
return true; | |||||
} | |||||
// So when the gtec buffer grows larger than a send buffer, copy it all to a send buffer sized array, then copy it into the actual send buffer one by one. | |||||
bool CDriverGTecGUSBampLinux::loop() | |||||
{ | |||||
if (!m_driverCtx.isConnected()) return false; | |||||
if (!m_driverCtx.isStarted()) return true; | |||||
const CStimulationSet stimSet; | |||||
// while there's new data available on the queue | |||||
while (m_sampleQueue.Avail()) | |||||
{ | |||||
// take it off and put it in the appropriate element in the outgoing buffer | |||||
m_sampleQueue.Get(m_sampleSend + m_currentChannel * m_nSamplePerSentBlock + m_currentSample, 1); | |||||
// Increment the current channel | |||||
m_currentChannel++; | |||||
// If the current channel reaches the channel count then move to the next sample | |||||
if (m_currentChannel == m_header.getChannelCount()) | |||||
{ | |||||
m_currentChannel = 0; | |||||
m_currentSample++; | |||||
} | |||||
// If the sample count reaches the number per sent block, then send it and start again | |||||
if (m_currentSample == m_nSamplePerSentBlock) | |||||
{ | |||||
m_callback->setSamples(m_sampleSend); // it looks as if this copies the buffer, so we're free modify it as soon as it executes | |||||
// When your sample buffer is fully loaded, | |||||
// it is advised to ask the acquisition server | |||||
// to correct any drift in the acquisition automatically. | |||||
m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount()); | |||||
// ... | |||||
// receive events from hardware | |||||
// and put them the correct way in a CStimulationSet object | |||||
//... | |||||
m_callback->setStimulationSet(stimSet); | |||||
m_currentSample = 0; | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
bool CDriverGTecGUSBampLinux::stop() | |||||
{ | |||||
if (!m_driverCtx.isConnected()) return false; | |||||
if (!m_driverCtx.isStarted()) return false; | |||||
// ... | |||||
// request the hardware to stop | |||||
// sending data | |||||
// ... | |||||
GT_StopAcquisition(m_deviceName.c_str()); | |||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Acquisition Stopped"; | |||||
return true; | |||||
} | |||||
bool CDriverGTecGUSBampLinux::uninitialize() | |||||
{ | |||||
if (!m_driverCtx.isConnected()) return false; | |||||
if (m_driverCtx.isStarted()) return false; | |||||
GT_CloseDevice(m_deviceName.c_str()); | |||||
m_driverCtx.getLogManager() << Kernel::LogLevel_Info << "Closed Device: " << m_deviceName << "\n"; | |||||
// ... | |||||
// uninitialize hardware here | |||||
// ... | |||||
m_sampleQueue.SetBuffer(nullptr, 0); | |||||
delete[] m_sampleSend; | |||||
delete[] m_sampleBuffer; | |||||
delete[] m_sampleReceive; | |||||
m_sampleSend = m_sampleReceive = m_sampleBuffer = nullptr; | |||||
m_callback = nullptr; | |||||
return true; | |||||
} | |||||
//___________________________________________________________________// | |||||
// // | |||||
bool CDriverGTecGUSBampLinux::isConfigurable() | |||||
{ | |||||
return false; // change to false if your device is not configurable | |||||
} | |||||
bool CDriverGTecGUSBampLinux::configure() | |||||
{ | |||||
/*// Change this line if you need to specify some references to your driver attribute that need configuration, e.g. the connection ID. | |||||
CConfigurationGTecGUSBampLinux config(m_driverCtx, Directories::getDataDir() + "/applications/acquisition-server/interface-GTecGUSBampLinux.ui", &m_deviceName, &m_config); | |||||
if (!config.configure(m_header)) { return false; } | |||||
m_header.setChannelCount(m_config.num_analog_in); | |||||
m_header.setSamplingFrequency(m_config.sample_rate); | |||||
m_settings.save();*/ | |||||
return true; | |||||
} | |||||
/*void AcquisitionServer::OnDataReady(void *param) | |||||
{ | |||||
// Like the 'this' pointer, but for a friend function | |||||
CDriverGTecGUSBampLinux *that = (CDriverGTecGUSBampLinux*)param; | |||||
// This is pretty tricky to know in advance, the API decides how many values to spit out depnding on a few factors it seems. | |||||
// We'll allocate a reasonble buffer and call GT_GetData as many times as is necessary | |||||
while(size_t nSamplesToRead = GT_GetSamplesAvailable(that->m_deviceName.c_str())) | |||||
{ | |||||
// If there are more samples than will fit in the buffer, just get as many as possible and we can get the rest next iteration | |||||
if(nSamplesToRead > CDriverGTecGUSBampLinux::ReceiveBufferSize * sizeof(float)) | |||||
nSamplesToRead = CDriverGTecGUSBampLinux::ReceiveBufferSize * sizeof(float); | |||||
// Get the data -- TODO: rewrite this algorithm such that we can copy directly from GT_GetData into the buffer read in the loop() function - is this a bug?? Maybe, but probably not since the calibration mode was always perfect | |||||
GT_GetData(that->m_deviceName.c_str(), reinterpret_cast<unsigned char*>(that->m_sampleReceive), nSamplesToRead); | |||||
// Put it on the sample queue | |||||
that->m_sampleQueue.Put(that->m_sampleReceive, nSamplesToRead / sizeof(float)); | |||||
} | |||||
}*/ | |||||
void OnDataReady(void* param) | |||||
{ | |||||
// Like the 'this' pointer, but for a friend function | |||||
CDriverGTecGUSBampLinux* that = static_cast<CDriverGTecGUSBampLinux*>(param); | |||||
// This is pretty tricky to know in advance, the API decides how many values to spit out depnding on a few factors it seems. | |||||
// We'll allocate a reasonble buffer and call GT_GetData as many times as is necessary | |||||
while (size_t samplesToRead = GT_GetSamplesAvailable(that->m_deviceName.c_str())) | |||||
{ | |||||
// If there are more samples than will fit in the buffer, just get as many as possible and we can get the rest next iteration | |||||
if (samplesToRead > that->m_sampleQueue.FreeContiguous() * sizeof(float)) samplesToRead = that->m_sampleQueue.FreeContiguous() * sizeof(float); | |||||
// Get the data and put it directly onto the queue | |||||
GT_GetData(that->m_deviceName.c_str(), reinterpret_cast<unsigned char*>(that->m_sampleQueue.NextFreeAddress()), samplesToRead); | |||||
// Pad the queue so it recognises how much data was just added to it | |||||
that->m_sampleQueue.Pad(samplesToRead / sizeof(float)); | |||||
} | |||||
} | |||||
} // namespace AcquisitionServer | |||||
} // namespace OpenViBE | |||||
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI_Linux |
#pragma once | |||||
#if defined TARGET_HAS_ThirdPartyGUSBampCAPI_Linux | |||||
#include "ovasIDriver.h" | |||||
#include "../ovasCHeader.h" | |||||
#include <openvibe/ov_all.h> | |||||
#include "../ovasCSettingsHelper.h" | |||||
#include "../ovasCSettingsHelperOperators.h" | |||||
#include <gAPI.h> | |||||
#include "Queue.h" | |||||
namespace OpenViBE { | |||||
namespace AcquisitionServer { | |||||
void OnDataReady(void* param); | |||||
/** | |||||
* \class CDriverGTecGUSBampLinux | |||||
* \author Tom Stewart (University of Tsukuba) | |||||
* \date Mon Feb 9 18:59:22 2015 | |||||
* \brief The CDriverGTecGUSBampLinux allows the acquisition server to acquire data from a g.tec g.USBamp from Linux. | |||||
* | |||||
* \sa CConfigurationGTecGUSBampLinux | |||||
*/ | |||||
class CDriverGTecGUSBampLinux final : public IDriver | |||||
{ | |||||
static const int ReceiveBufferSize = 8192; | |||||
public: | |||||
friend void OnDataReady(void* param); | |||||
explicit CDriverGTecGUSBampLinux(IDriverContext& ctx); | |||||
~CDriverGTecGUSBampLinux() override {} | |||||
const char* getName() override { return "g.tec g.USBamp Linux BCI-Lab"; } | |||||
bool initialize(const uint32_t nSamplePerSentBlock, IDriverCallback& callback) override; | |||||
bool uninitialize() override; | |||||
bool start() override; | |||||
bool stop() override; | |||||
bool loop() override; | |||||
bool isConfigurable() override; | |||||
bool configure() override; | |||||
const IHeader* getHeader() override { return &m_header; } | |||||
bool isFlagSet(const EDriverFlag flag) const override { return flag == EDriverFlag::IsUnstable; } | |||||
protected: | |||||
SettingsHelper m_settings; | |||||
IDriverCallback* m_callback = nullptr; | |||||
// Replace this generic Header with any specific header you might have written | |||||
CHeader m_header; | |||||
uint32_t m_nSamplePerSentBlock; | |||||
float *m_sampleSend, *m_sampleReceive, *m_sampleBuffer; | |||||
Queue<float> m_sampleQueue; | |||||
private: | |||||
/* | |||||
* Insert here all specific attributes, such as USB port number or device ID. | |||||
*/ | |||||
std::string m_deviceName; | |||||
gt_usbamp_config m_config; | |||||
gt_usbamp_analog_out_config m_analogOutConfig; | |||||
// Keeps track of where we are with filling up the buffer | |||||
uint32_t m_currentSample, m_currentChannel; | |||||
}; | |||||
} // namespace AcquisitionServer | |||||
} // namespace OpenViBE | |||||
#endif // TARGET_HAS_ThirdPartyGUSBampCAPI_Linux |
# OpenVibe_THN-p300-bci | |||||
Die Szenarien für das Taktile und das Visuelle BCI, basieren auf dem OpenVibeSzenrio p300-speller-xDAWN. | |||||
In die jeweiligen Ordner können die aktuellen OpenVibe-Dateien für das taktile und visuelle Buchstabieren eingefügt werden. Die Dateien können im EFI-Git gefunden werden. |
Hier eigene README einfügen | |||||
# g.USBamp Linux | |||||
### **Find gUSBamp:** | |||||
Das CMake Modul FindThirdPartyGUSBampCAPI.cmake wurde für die zur Verfügung gestellte neue Version der gUSBamp C-API für Linux angepasst. Benötigt werden zur Verwendung die [Version 1.16 der API](https://www.dropbox.com/sh/5ru6suscpz9kh5a/AAAawMPWfLhPCI9EyGyyX1Jsa?dl=0) und die [Boost Library version 1.58](https://www.boost.org/users/history/version_1_58_0.html). | |||||
Das angepasste MakeFile muss vor dem Compilieren nach "/meta/extras/cmake-modules/" | |||||
### **gUSBamp_Driver:** | |||||
Anspassung für den BCILAB Treiber für das gUSBamp. | |||||
Vor dem Kompilieren nach "/meta/extras/contrib/plugins/server-drivers/gtec-bcilab/src/" kopieren. |
sudo apt-get install python-tk | |||||
sudo apt-get install xdotool | |||||
sudo apt-get install python-setuptools | |||||
sudo apt-get install python-pip | |||||
pip install pyinstaller==3.6 |
#!/bin/bash | |||||
cd ~/bci/ | |||||
> log.txt | |||||
chmod +rw log.txt | |||||
./gui >> log.txt | |||||