2021-11-19 13:49:25 +00:00
|
|
|
'''--------------------------------------------------------------------------------------------------
|
|
|
|
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()
|
|
|
|
|
2021-11-19 14:32:57 +00:00
|
|
|
self.view = viewTkinter.View(self, self.dll.getDefaultPath_visuell())
|
2021-11-19 13:49:25 +00:00
|
|
|
self.modi = "visuellesBCI"
|
2021-11-19 14:32:57 +00:00
|
|
|
self.pathOVFile = self.dll.getPathOVFile_visuell()
|
|
|
|
self.pathSpatialCfg = self.dll.getSpatialCFGFile_visuell()
|
|
|
|
self.pathClassifierCfg = self.dll.getClassifierCFGFile_visuell()
|
2021-11-19 13:49:25 +00:00
|
|
|
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
|
|
|
|
|
2021-11-19 14:32:57 +00:00
|
|
|
self.dll.getCommandXDawn_taktil.argtypes = []
|
|
|
|
self.dll.getCommandXDawn_taktil.restype = ctypes.c_char_p
|
2021-11-19 13:49:25 +00:00
|
|
|
|
2021-11-19 14:32:57 +00:00
|
|
|
self.dll.getCommandXDawn_visuell.argtypes = []
|
|
|
|
self.dll.getCommandXDawn_visuell.restype = ctypes.c_char_p
|
2021-11-19 13:49:25 +00:00
|
|
|
|
2021-11-19 14:32:57 +00:00
|
|
|
self.dll.getCommandClassifier_visuell.argtypes = []
|
|
|
|
self.dll.getCommandClassifier_visuell.restype = ctypes.c_char_p
|
2021-11-19 13:49:25 +00:00
|
|
|
|
2021-11-19 14:32:57 +00:00
|
|
|
self.dll.getCommandClassifier_taktil.argtypes = []
|
|
|
|
self.dll.getCommandClassifier_taktil.restype = ctypes.c_char_p
|
2021-11-19 13:49:25 +00:00
|
|
|
|
2021-11-19 14:32:57 +00:00
|
|
|
self.dll.getDefaultPath_visuell.argtypes = []
|
|
|
|
self.dll.getDefaultPath_visuell.restype = ctypes.c_char_p
|
2021-11-19 13:49:25 +00:00
|
|
|
|
2021-11-19 14:32:57 +00:00
|
|
|
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
|
2021-11-19 13:49:25 +00:00
|
|
|
|
|
|
|
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")
|
2021-11-19 14:32:57 +00:00
|
|
|
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()
|
2021-11-19 13:49:25 +00:00
|
|
|
self.modi = "visuellesBCI"
|
|
|
|
elif(self.modi == "visuellesBCI"):
|
|
|
|
self.setTitle("taktiles BCI")
|
|
|
|
self.view.setChangeBtnText("Wechsel zu visuellen BCI")
|
2021-11-19 14:32:57 +00:00
|
|
|
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()
|
2021-11-19 13:49:25 +00:00
|
|
|
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)
|
2021-11-23 09:37:56 +00:00
|
|
|
if not os.path.exists(self.file):
|
|
|
|
os.mkdir(self.file)
|
2021-11-19 13:49:25 +00:00
|
|
|
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"))
|
|
|
|
|
2021-11-23 09:37:56 +00:00
|
|
|
self.changeScreen("StartPage")
|
|
|
|
|
2021-11-19 13:49:25 +00:00
|
|
|
#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
|
2021-11-19 14:32:57 +00:00
|
|
|
print(path)
|
|
|
|
if self.file == "-1":
|
|
|
|
print("kein gueltiger Pfad")
|
2021-11-19 13:49:25 +00:00
|
|
|
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.
|
|
|
|
'''
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|