├── .gitignore ├── LICENSE.md ├── README.md ├── UI ├── FromFile.py ├── FromFile.pyc ├── HeadStatusWidget.py ├── HeadStatusWidget.pyc ├── HeatMapWidget.py ├── HeatMapWidget.pyc ├── Interface.py ├── PlottingWidget.py ├── PlottingWidget.pyc ├── Record.py ├── Record.pyc ├── __init__.py └── __init__.pyc ├── assets ├── headset.png ├── mockup.png ├── screenshot-1.png ├── screenshot-2.png ├── screenshot-3.png ├── screenshot-4.png └── screenshot-5.png ├── requirements.txt ├── sample ├── __init__.py ├── __init__.pyc ├── render.py ├── render.pyc ├── tests.py └── tests2.py ├── setup.py ├── tests ├── __pycache__ │ └── test_emokit.cpython-27-PYTEST.pyc └── test_emokit.py └── util ├── PacketParser.py └── PacketParser.pyc /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | venv/ -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 - [Universidad Nacional de Colombia](http://www.unal.edu.co) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EmokitVisualizer 2 | 3 | This is an Open Source signal visualizer project for the Emotiv EPOC+ Dongle based on the open source project [Emokit](https://github.com/openyou/emokit) 4 | 5 | ## Screenshots 6 | 7 | ![alt text](https://github.com/EmokitAlife/EmokitVisualizer/blob/CreacionUI/assets/screenshot-1.png "Main Interface tab 1") 8 | ![alt text](https://github.com/EmokitAlife/EmokitVisualizer/blob/CreacionUI/assets/screenshot-2.png "Main Interface tab 2") 9 | ![alt text](https://github.com/EmokitAlife/EmokitVisualizer/blob/CreacionUI/assets/screenshot-3.png "Running a recorded file plots") 10 | ![alt text](https://github.com/EmokitAlife/EmokitVisualizer/blob/CreacionUI/assets/screenshot-4.png "Running a recorded file heatmap") 11 | ![alt text](https://github.com/EmokitAlife/EmokitVisualizer/blob/CreacionUI/assets/screenshot-5.png "Running a recorded file selected sensors") 12 | 13 | 14 | ## Getting Started 15 | 16 | These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system. 17 | 18 | ### Prerequisites 19 | 20 | First, if you are in a Windows machine, you should install the Microsoft Visual C++ Compiler for Python 21 | It depends on the version of Python of your preference, for example, here is the [link](https://www.microsoft.com/en-us/download/details.aspx?id=44266) for Python 2.7 22 | 23 | 24 | ### Installing 25 | 26 | #### Windows 27 | 28 | After you have installed the Visual C++ Compiler, you have to start a cmd console in the folder that holds your project and type: 29 | 30 | ``` 31 | pip install -r requirements.txt 32 | ``` 33 | 34 | This command will install all the Python modules needed for running the Emokit driver package 35 | 36 | ### Linux ( Debian based ) 37 | 38 | First, you have to install the Hidapi driver so your OS can read the USB-HID that comes with the dongle, so you have to install this libraries, open your terminal and run: 39 | 40 | ``` 41 | sudo apt-get install libudev-dev libusb-1.0-0-dev libtool dh-autoreconf 42 | ``` 43 | 44 | After that, you got to download and install the 'Hidapi' driver itself, download the last version here: 45 | 46 | ``` 47 | https://github.com/signal11/hidapi/archive/master.zip 48 | ``` 49 | 50 | Now that you have the driver, you have to get a C++ compiler like 'g++' to compile and install the driver, after that, we have to unzip, compile and install the driver, open a Terminal at your Downloads folder: 51 | 52 | ``` 53 | unzip hidapi-master.zip 54 | cd hidapi-master 55 | ./bootstrap 56 | ./configure 57 | make 58 | sudo make install 59 | ``` 60 | 61 | Now we have to install a Python library that connects the 'Hidapi' driver with your Python project, that can be made with the library 'pyhidapi', go to this link and download it: 62 | 63 | ``` 64 | https://pypi.python.org/pypi/hid/0.1.1 65 | ``` 66 | 67 | Or, you can download it from it's Main Github project in: 68 | 69 | ``` 70 | https://github.com/NF6X/pyhidapi 71 | ``` 72 | 73 | In this example, we download it from it's Github project, open a Terminal in your Downloads folder again and type: 74 | 75 | ``` 76 | unzip pyhidapi-master.zip 77 | cd pyhidapi-master 78 | python setup.py install 79 | ``` 80 | 81 | After installing the 'pyhidapi', we can run the main requirements setup, download it, we have to make a little fix in the 'requirements.txt' file first, open it in your favorite text editor and remove the line that says: 82 | 83 | ``` 84 | pywinusb>=0.4.2 85 | ``` 86 | 87 | This is the USB-HID driver for Windows, we are removing it because, in the Linux case, we are using the 'Hidapi' driver instead 88 | 89 | After that, open a Terminal in the folder of the project and run: 90 | 91 | ``` 92 | pip install -r requirements.txt 93 | ``` 94 | 95 | And that's it for Linux. 96 | 97 | ## Authors 98 | 99 | * **Dorian Tovar** - *developer* - [datovard](https://github.com/datovard) 100 | 101 | See also the list of [contributors](https://github.com/orgs/EmokitAlife/people) who participated in this project. 102 | 103 | ## License 104 | 105 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 106 | -------------------------------------------------------------------------------- /UI/FromFile.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import pyqtgraph as pg 6 | from PySide.QtGui import * 7 | import csv 8 | from PlottingWidget import PlottingWidget 9 | from HeadStatusWidget import HeadStatusWidget 10 | from HeatMapWidget import HeatMapWidget 11 | 12 | sys.path.append('../util') 13 | from PacketParser import PacketParser 14 | 15 | class FromFile: 16 | def __init__(self, parent=None): 17 | self.parser = PacketParser() 18 | self.timer = pg.QtCore.QTimer() 19 | 20 | self.electrodePairing = {\ 21 | "AF3": {'pair': "AF4", 'order': 1}, 22 | "AF4": {'pair': "AF3", 'order': 0}, 23 | "F3": {'pair': "F4", 'order': 1}, 24 | "F4": {'pair': "F3", 'order': 0}, 25 | "F7": {'pair': "F8", 'order': 1}, 26 | "F8": {'pair': "F7", 'order': 0}, 27 | "FC5": {'pair': "FC6", 'order': 1}, 28 | "FC6": {'pair': "FC5", 'order': 0}, 29 | "T7": {'pair': "T8", 'order': 1}, 30 | "T8": {'pair': "T7", 'order': 0}, 31 | "P7": {'pair': "P8", 'order': 1}, 32 | "P8": {'pair': "P7", 'order': 0}, 33 | "O1": {'pair': "O2", 'order': 1}, 34 | "O2": {'pair': "O1", 'order': 0}, 35 | } 36 | 37 | def setFromFileTab(self): 38 | self.setLeftSidedBox() 39 | self.setCenterBox() 40 | 41 | # Main grid layout 42 | self.gridLayout = QGridLayout() 43 | self.gridLayout.addLayout( self.leftBox, 0, 0) 44 | self.gridLayout.addLayout( self.centerBox, 0, 1) 45 | 46 | self.gridLayout.setColumnStretch(0, 1) 47 | self.gridLayout.setColumnStretch(1, 3) 48 | 49 | return self.gridLayout 50 | 51 | def setLeftSidedBox(self): 52 | # Left sided box for controls 53 | self.leftBox = QFormLayout() 54 | 55 | # Folder selection buttons 56 | self.route = QLineEdit() 57 | self.route.setReadOnly(True) 58 | self.examine = QPushButton("Examinar") 59 | self.examine.clicked.connect(self.getFilename) 60 | 61 | folderButtons = QGridLayout() 62 | folderButtons.addWidget(self.route, 0, 0) 63 | folderButtons.addWidget(self.examine, 0, 1) 64 | self.leftBox.addRow(QLabel("Archivo escogido")) 65 | self.leftBox.addRow(folderButtons) 66 | 67 | # Action selection buttons 68 | self.startBtn = QPushButton("Iniciar") 69 | self.startBtn.setEnabled(False) 70 | self.startBtn.clicked.connect(self.startReading) 71 | self.stopBtn = QPushButton("Pausar") 72 | self.stopBtn.setEnabled(False) 73 | self.stopBtn.clicked.connect(self.stopReading) 74 | self.restartBtn = QPushButton("Reiniciar") 75 | self.restartBtn.setEnabled(False) 76 | self.restartBtn.clicked.connect(self.restartReading) 77 | 78 | actionsButtons = QGridLayout() 79 | actionsButtons.addWidget(self.startBtn, 0, 0) 80 | actionsButtons.addWidget(self.stopBtn, 0, 1) 81 | actionsButtons.addWidget(self.restartBtn, 0, 2) 82 | self.leftBox.addRow(QLabel("Control")) 83 | self.leftBox.addRow(actionsButtons) 84 | 85 | # Sensors status 86 | self.leftBox.addRow(QLabel("Estado de los sensores")) 87 | 88 | self.headsetState = HeadStatusWidget(self.setPlotGraphBySensor) 89 | self.leftBox.addRow(self.headsetState) 90 | self.headsetState.updateHeadsetStatus(None) 91 | 92 | def setPlotGraphBySensor(self, sensor): 93 | self.plots.setVisible(False) 94 | self.heatmap.setVisible(False) 95 | self.altPlots.setVisible(True) 96 | 97 | if self.electrodePairing[sensor]["order"]: 98 | self.altPlots.restartSensors( [ sensor, self.electrodePairing[sensor]["pair"]] ) 99 | else: 100 | self.altPlots.restartSensors([ self.electrodePairing[sensor]["pair"], sensor ]) 101 | 102 | self.toggleGraph.setVisible(False) 103 | self.returnToGraphs.setVisible(True) 104 | 105 | def setCenterBox(self): 106 | # Center sided box for signals 107 | self.centerBox = QFormLayout() 108 | self.centerBox.addRow(QLabel("Estado de las senales")) 109 | 110 | self.activeGraph = True 111 | 112 | self.toggleGraph = QPushButton("Mapa de calor") 113 | self.toggleGraph.setEnabled( False ) 114 | self.toggleGraph.clicked.connect( self.toggleGraphics ) 115 | self.centerBox.addRow( self.toggleGraph ) 116 | 117 | self.returnToGraphs = QPushButton("Regresar") 118 | self.returnToGraphs.setVisible(False) 119 | self.returnToGraphs.clicked.connect(self.returnToGraphics) 120 | self.centerBox.addRow(self.returnToGraphs) 121 | 122 | self.plots = PlottingWidget() 123 | self.centerBox.addRow( self.plots ) 124 | 125 | self.heatmap = HeatMapWidget() 126 | self.centerBox.addRow(self.heatmap) 127 | self.toggleGraphics() 128 | 129 | self.altPlots = PlottingWidget([]) 130 | self.centerBox.addRow(self.altPlots) 131 | self.altPlots.setVisible(False) 132 | 133 | def toggleGraphics(self): 134 | if not self.activeGraph: 135 | self.plots.setVisible(False) 136 | self.heatmap.setVisible(True) 137 | self.toggleGraph.setText("Graficas") 138 | self.activeGraph = True 139 | else: 140 | self.plots.setVisible(True) 141 | self.heatmap.setVisible(False) 142 | self.toggleGraph.setText("Mapa de calor") 143 | self.activeGraph = False 144 | 145 | def returnToGraphics(self): 146 | self.altPlots.setVisible(False) 147 | self.returnToGraphs.setVisible(False) 148 | self.plots.setVisible(True) 149 | self.toggleGraph.setVisible(True) 150 | 151 | def startReading(self): 152 | self.startBtn.setEnabled(False) 153 | self.stopBtn.setEnabled(True) 154 | self.toggleGraph.setEnabled(True) 155 | 156 | self.timer.timeout.connect( self.setupNewPacket ) 157 | self.timer.start(10) 158 | 159 | def stopReading(self): 160 | self.timer.stop() 161 | 162 | self.stopBtn.setEnabled(False) 163 | self.startBtn.setEnabled(True) 164 | 165 | def restartReading(self): 166 | self.altPlots.setVisible(False) 167 | self.returnToGraphs.setVisible(False) 168 | 169 | self.startBtn.setEnabled(False) 170 | self.stopBtn.setEnabled(False) 171 | self.restartBtn.setEnabled(False) 172 | self.toggleGraph.setEnabled(False) 173 | 174 | self.timer.stop() 175 | 176 | self.fileRoute = "" 177 | self.route.setText(self.fileRoute) 178 | 179 | self.csvfile = None 180 | self.file = None 181 | self.headers = None 182 | 183 | self.plots.restartPlotting() 184 | self.plots.setVisible(True) 185 | self.activeGraph = True 186 | self.heatmap.updateHeatMapStatus(None) 187 | if self.activeGraph: 188 | self.toggleGraphics() 189 | self.headsetState.updateHeadsetStatus(None) 190 | 191 | def setupNewPacket(self): 192 | nextPacket = next(self.file) 193 | parsed = self.parser.fromCSVToPacket( list(self.headers), nextPacket ) 194 | self.plots.updater( parsed ) 195 | self.heatmap.updateHeatMapStatus(parsed) 196 | self.headsetState.updateHeadsetStatus(parsed) 197 | if self.altPlots != None: 198 | self.altPlots.updater(parsed) 199 | 200 | def getFilename(self): 201 | filename = QFileDialog.getOpenFileName(self.examine, 'Open file', 202 | 'c:\\', "CSV (*.csv)") 203 | self.fileRoute = filename[0] 204 | self.route.setText( self.fileRoute ) 205 | 206 | self.csvfile = open(self.fileRoute, 'rb') 207 | self.file = csv.reader(self.csvfile, delimiter=',', quotechar='|') 208 | self.headers = next(self.file) 209 | 210 | self.startBtn.setEnabled(True) 211 | self.restartBtn.setEnabled(True) -------------------------------------------------------------------------------- /UI/FromFile.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/UI/FromFile.pyc -------------------------------------------------------------------------------- /UI/HeadStatusWidget.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | from PySide.QtGui import * 5 | from PySide.QtCore import Qt 6 | 7 | class HeadStatusWidget(QWidget): 8 | def __init__(self, callback, parent=None): 9 | super(HeadStatusWidget, self).__init__(parent) 10 | self.callback= callback 11 | self.electrodesPosition = \ 12 | { 13 | "AF3": {"x": 82, "y": 57}, 14 | "AF4": {"x": 221, "y": 57}, 15 | "F7" : {"x": 35, "y": 104}, 16 | "F3" : {"x": 114, "y": 107}, 17 | "F4" : {"x": 190, "y": 107}, 18 | "F8" : {"x": 269, "y": 104}, 19 | "FC5": {"x": 67, "y": 149}, 20 | "FC6": {"x": 236, "y": 149}, 21 | "T7" : {"x": 18, "y": 197}, 22 | "T8" : {"x": 286, "y": 197}, 23 | "P7" : {"x": 67, "y": 317}, 24 | "P8" : {"x": 236, "y": 317}, 25 | "O1" : {"x": 113, "y": 375}, 26 | "O2" : {"x": 192, "y": 375} 27 | } 28 | 29 | self.headsetColors = \ 30 | [ 31 | (0, 0, 0), # Black 32 | (255, 0, 0), # Red 33 | (230, 255, 0), # Yellow 34 | (102, 175, 54), # Green 35 | ] 36 | 37 | layout = QHBoxLayout() 38 | self.headsetState = QLabel() 39 | layout.addWidget(self.headsetState) 40 | self.setLayout(layout) 41 | self.pixmap = QPixmap("../assets/headset.png") 42 | 43 | def updateHeadsetStatus(self, packet): 44 | painter = QPainter() 45 | if painter.begin(self.pixmap): 46 | painter.setFont(QFont('Decorative', 12)) 47 | for key in self.electrodesPosition: 48 | if key[0] == "O": 49 | painter.drawText(self.electrodesPosition[key]["x"] - 1, self.electrodesPosition[key]["y"] - 20, 30, 15, 50 | Qt.AlignCenter, key) 51 | elif key == "T7": 52 | painter.drawText(self.electrodesPosition[key]["x"] + 7, self.electrodesPosition[key]["y"] + 32, 30, 15, 53 | Qt.AlignCenter, key) 54 | elif key == "T8": 55 | painter.drawText(self.electrodesPosition[key]["x"] - 9, self.electrodesPosition[key]["y"] + 32, 30, 15, 56 | Qt.AlignCenter, key) 57 | else: 58 | painter.drawText( self.electrodesPosition[key]["x"] - 1, self.electrodesPosition[key]["y"] + 32, 30, 15, Qt.AlignCenter, key) 59 | 60 | if packet == None: 61 | color = self.headsetColors[0] 62 | painter.setBrush(QColor(color[0], color[1], color[2])) 63 | for item in self.electrodesPosition: 64 | painter.drawEllipse(self.electrodesPosition[item]["x"], self.electrodesPosition[item]["y"], 28, 28) 65 | painter.drawPoint(self.electrodesPosition[item]["x"], self.electrodesPosition[item]["y"]) 66 | else: 67 | for sensor in packet.sensors: 68 | if sensor in self.electrodesPosition: 69 | quality = packet.sensors[sensor]['quality'] // 540 70 | color = self.headsetColors[ quality if quality <= 3 else 3 ] 71 | painter.setBrush(QColor(color[0], color[1], color[2])) 72 | painter.drawEllipse( self.electrodesPosition[sensor]["x"], self.electrodesPosition[sensor]["y"], 28, 28) 73 | painter.drawPoint(self.electrodesPosition[sensor]["x"], self.electrodesPosition[sensor]["y"]) 74 | painter.end() 75 | 76 | self.headsetState.setPixmap(self.pixmap) 77 | 78 | def callParentCallback(self, sensor): 79 | self.callback(sensor) 80 | 81 | def mousePressEvent(self, QMouseEvent): 82 | x, y = QMouseEvent.pos().x(), QMouseEvent.pos().y() 83 | for sensor in self.electrodesPosition: 84 | #print self.electrodesPosition[sensor] 85 | if ( x >= self.electrodesPosition[sensor]["x"] and x <= self.electrodesPosition[sensor]["x"] + 35 ) and \ 86 | ( y >= self.electrodesPosition[sensor]["y"] and y <= self.electrodesPosition[sensor]["y"] + 35 ): 87 | self.callParentCallback( sensor ) -------------------------------------------------------------------------------- /UI/HeadStatusWidget.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/UI/HeadStatusWidget.pyc -------------------------------------------------------------------------------- /UI/HeatMapWidget.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | from PySide.QtGui import * 5 | from PySide.QtCore import Qt 6 | 7 | class HeatMapWidget(QWidget): 8 | def __init__(self, parent=None): 9 | super(HeatMapWidget, self).__init__(parent) 10 | 11 | self.electrodesPosition = \ 12 | { 13 | "AF3": {"x": 82, "y": 57}, 14 | "AF4": {"x": 221, "y": 57}, 15 | "F7" : {"x": 35, "y": 104}, 16 | "F3" : {"x": 114, "y": 107}, 17 | "F4" : {"x": 190, "y": 107}, 18 | "F8" : {"x": 269, "y": 104}, 19 | "FC5": {"x": 67, "y": 149}, 20 | "FC6": {"x": 236, "y": 149}, 21 | "T7" : {"x": 18, "y": 197}, 22 | "T8" : {"x": 286, "y": 197}, 23 | "P7" : {"x": 67, "y": 317}, 24 | "P8" : {"x": 236, "y": 317}, 25 | "O1" : {"x": 113, "y": 375}, 26 | "O2" : {"x": 192, "y": 375} 27 | } 28 | 29 | self.headsetColors = \ 30 | [ 31 | (0, 0, 255), # Black 32 | (21, 0, 234), # Red 33 | (43, 0, 212), # Yellow 34 | (64, 0, 191), # Green 35 | (106, 0, 149), 36 | (128, 0, 128), 37 | (149, 0, 106), 38 | (170, 0, 85), 39 | (191, 0, 64), 40 | (212, 0, 43), 41 | (234, 0, 21), 42 | (255, 0, 0) 43 | ] 44 | 45 | layout = QHBoxLayout() 46 | self.headsetState = QLabel() 47 | layout.addWidget(self.headsetState) 48 | self.setLayout(layout) 49 | self.minValue = -607 50 | self.maxValue = 3075 51 | layout.setAlignment(Qt.AlignHCenter ) 52 | 53 | self.pixmap = QPixmap("../assets/headset.png") 54 | 55 | def updateHeatMapStatus(self, packet): 56 | painter = QPainter() 57 | if painter.begin(self.pixmap): 58 | 59 | painter.setFont(QFont('Decorative', 15)) 60 | painter.drawText(self.pixmap.rect(), Qt.AlignCenter, "Mapa de calor") 61 | 62 | painter.setFont(QFont('Decorative', 8)) 63 | 64 | for key in self.electrodesPosition: 65 | if key[0] == "O": 66 | painter.drawText(self.electrodesPosition[key]["x"] - 1, self.electrodesPosition[key]["y"] - 20, 30, 15, 67 | Qt.AlignCenter, key) 68 | elif key == "T7": 69 | painter.drawText(self.electrodesPosition[key]["x"] + 7, self.electrodesPosition[key]["y"] + 32, 30, 15, 70 | Qt.AlignCenter, key) 71 | elif key == "T8": 72 | painter.drawText(self.electrodesPosition[key]["x"] - 9, self.electrodesPosition[key]["y"] + 32, 30, 15, 73 | Qt.AlignCenter, key) 74 | else: 75 | painter.drawText( self.electrodesPosition[key]["x"] - 1, self.electrodesPosition[key]["y"] + 32, 30, 15, Qt.AlignCenter, key) 76 | 77 | if packet == None: 78 | color = self.headsetColors[0] 79 | painter.setBrush(QColor(0,0,0)) 80 | for item in self.electrodesPosition: 81 | painter.drawEllipse(self.electrodesPosition[item]["x"], self.electrodesPosition[item]["y"], 28, 28) 82 | else: 83 | for sensor in packet.sensors: 84 | if sensor in self.electrodesPosition: 85 | quality = ( packet.sensors[sensor]['value'] + -1*self.minValue ) // 307 86 | color = self.headsetColors[ quality ] 87 | painter.setBrush(QColor(color[0], color[1], color[2])) 88 | painter.drawEllipse( self.electrodesPosition[sensor]["x"], self.electrodesPosition[sensor]["y"], 28, 28) 89 | painter.end() 90 | 91 | self.headsetState.setPixmap(self.pixmap) 92 | -------------------------------------------------------------------------------- /UI/HeatMapWidget.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/UI/HeatMapWidget.pyc -------------------------------------------------------------------------------- /UI/Interface.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import pyqtgraph as pg 6 | from PySide.QtGui import * 7 | from Record import Record 8 | from FromFile import FromFile 9 | 10 | class Interface ( QTabWidget ): 11 | def __init__(self, parent=None): 12 | super(Interface, self).__init__(parent) 13 | 14 | self.recordTab = Record() 15 | self.fromFileTab = FromFile() 16 | 17 | self.tab1 = QWidget() 18 | self.tab2 = QWidget() 19 | 20 | self.addTab(self.tab1, "Tab 1") 21 | self.addTab(self.tab2, "Tab 2") 22 | 23 | self.tab1UI() 24 | self.tab2UI() 25 | self.setWindowTitle("Emokit Visualizer") 26 | self.showMaximized() 27 | 28 | def tab1UI(self): 29 | self.setTabText(0, "Grabar") 30 | self.tab1.setLayout( self.recordTab.setRecordTab() ) 31 | 32 | self.timer = pg.QtCore.QTimer() 33 | self.timer.timeout.connect(self.recordTab.update) 34 | self.timer.start(10) 35 | 36 | def tab2UI(self): 37 | self.setTabText(1, "Desde archivo") 38 | self.tab2.setLayout( self.fromFileTab.setFromFileTab() ) 39 | 40 | def main(): 41 | app = QApplication(sys.argv) 42 | ex = Interface() 43 | ex.show() 44 | sys.exit(app.exec_()) 45 | 46 | if __name__ == '__main__': 47 | main() -------------------------------------------------------------------------------- /UI/PlottingWidget.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | from PySide import QtCore, QtGui 5 | import pyqtgraph as pg 6 | 7 | class PlottingWidget(QtGui.QWidget): 8 | def __init__(self, sensors=None, parent=None): 9 | super(PlottingWidget, self).__init__(parent) 10 | 11 | self.electrodes = ( 12 | ('AF3', (15, 150, 255)), ('AF4', (150, 255, 15)), ('F3', (255, 15, 150)), ('F4', (15, 150, 255)), 13 | ('F7', (150, 255, 15)), ('F8', (255, 15, 150)), 14 | ('FC5', (15, 150, 255)), ('FC6', (150, 255, 15)), ('T7', (255, 15, 150)), ('T8', (15, 150, 255)), 15 | ('P7', (150, 255, 15)), ('P8', (255, 15, 150)), 16 | ('O1', (15, 150, 255)), ('O2', (150, 255, 15)) 17 | ) 18 | 19 | layout = QtGui.QHBoxLayout() 20 | pg.setConfigOptions(antialias=True) 21 | self.plots = pg.GraphicsWindow() 22 | layout.addWidget(self.plots) 23 | self.setLayout(layout) 24 | self.sensors = sensors 25 | self.startTime = pg.ptime.time() 26 | self.plotter() 27 | 28 | def restartSensors(self, sensors): 29 | self.sensors = sensors 30 | self.startTime = pg.ptime.time() 31 | self.restartPlotting() 32 | 33 | def plotter(self): 34 | self.allwaves = [] 35 | if self.sensors == None: 36 | for i in xrange(14): 37 | if i: 38 | self.plots.nextRow() 39 | p = self.plots.addPlot() 40 | if i == 13: 41 | p.setLabel('bottom', 'Time', 's') 42 | else: 43 | p.showAxis('bottom', False) 44 | p.setLabel('left', self.electrodes[i][0]) 45 | dataX = [0] 46 | dataY = [0] 47 | curve = p.plot() 48 | self.allwaves.append( [p, dataX, dataY, curve] ) 49 | else: 50 | for i, sensor in enumerate(self.sensors): 51 | if i: 52 | self.plots.nextRow() 53 | p = self.plots.addPlot() 54 | if i == len(self.sensors)-1: 55 | p.setLabel('bottom', 'Time', 's') 56 | else: 57 | p.showAxis('bottom', False) 58 | p.setLabel('left', sensor) 59 | dataX = [0] 60 | dataY = [0] 61 | curve = p.plot() 62 | self.allwaves.append([p, dataX, dataY, curve]) 63 | 64 | def updater(self, packet): 65 | now = pg.ptime.time() 66 | if self.sensors == None: 67 | for i in xrange(len(self.allwaves)): 68 | plot, dataX, dataY, curve = self.allwaves[i] 69 | 70 | if (len(dataY) >= 300): 71 | dataY.pop(0) 72 | dataY[0] = 0 73 | dataX.pop(0) 74 | 75 | dataY.append( packet.sensors[self.electrodes[i][0]]["value"] ) 76 | dataX.append( now - self.startTime ) 77 | curve.setPen(self.electrodes[i][1]) 78 | plot.setLabel('right', packet.sensors[self.electrodes[i][0]]["value"]) 79 | curve.setData( x = dataX, y = dataY ) 80 | else: 81 | for i, sensor in enumerate(self.sensors): 82 | plot, dataX, dataY, curve = self.allwaves[i] 83 | 84 | if (len(dataY) >= 300): 85 | dataY.pop(0) 86 | dataY[0] = 0 87 | dataX.pop(0) 88 | 89 | dataY.append(packet.sensors[sensor]["value"]) 90 | dataX.append(now - self.startTime) 91 | curve.setPen(self.electrodes[i][1]) 92 | plot.setLabel('right', packet.sensors[sensor]["value"]) 93 | curve.setData(x=dataX, y=dataY) 94 | 95 | 96 | def restartPlotting(self): 97 | self.plots.clear() 98 | self.plots.nextRow() 99 | self.plotter() -------------------------------------------------------------------------------- /UI/PlottingWidget.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/UI/PlottingWidget.pyc -------------------------------------------------------------------------------- /UI/Record.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | from PySide.QtGui import * 6 | from emokit.emotiv import Emotiv 7 | from PlottingWidget import PlottingWidget 8 | from HeadStatusWidget import HeadStatusWidget 9 | import pyqtgraph as pg 10 | import datetime 11 | import time 12 | 13 | sys.path.append('../util') 14 | from PacketParser import PacketParser 15 | 16 | class Record: 17 | def __init__(self, parent=None): 18 | self.headset = Emotiv() 19 | self.electrodePairing = { \ 20 | "AF3": {'pair': "AF4", 'order': 1}, 21 | "AF4": {'pair': "AF3", 'order': 0}, 22 | "F3": {'pair': "F4", 'order': 1}, 23 | "F4": {'pair': "F3", 'order': 0}, 24 | "F7": {'pair': "F8", 'order': 1}, 25 | "F8": {'pair': "F7", 'order': 0}, 26 | "FC5": {'pair': "FC6", 'order': 1}, 27 | "FC6": {'pair': "FC5", 'order': 0}, 28 | "T7": {'pair': "T8", 'order': 1}, 29 | "T8": {'pair': "T7", 'order': 0}, 30 | "P7": {'pair': "P8", 'order': 1}, 31 | "P8": {'pair': "P7", 'order': 0}, 32 | "O1": {'pair': "O2", 'order': 1}, 33 | "O2": {'pair': "O1", 'order': 0}, 34 | } 35 | self.timer = pg.QtCore.QTimer() 36 | self.recording = False 37 | self.parser = PacketParser() 38 | self.header_text = "Timestamp,F3 Value,F3 Quality,FC5 Value,FC5 Quality,F7 Value,F7 Quality,T7 Value,T7 Quality,P7 Value,P7 Quality,O1 Value,O1 Quality,O2 Value,O2 Quality,P8 Value,P8 Quality,T8 Value,T8 Quality,F8 Value,F8 Quality,AF4 Value,AF4 Quality,FC6 Value,FC6 Quality,F4 Value,F4 Quality,AF3 Value,AF3 Quality,X Value,Y Value,Z Value" 39 | 40 | def setPlotGraphBySensor(self, sensor): 41 | self.plots.setVisible(False) 42 | self.altPlots.setVisible(True) 43 | 44 | if self.electrodePairing[sensor]["order"]: 45 | self.altPlots.restartSensors( [ sensor, self.electrodePairing[sensor]["pair"]] ) 46 | else: 47 | self.altPlots.restartSensors([ self.electrodePairing[sensor]["pair"], sensor ]) 48 | 49 | self.returnToGraphs.setVisible(True) 50 | 51 | def update(self): 52 | packet = self.headset.dequeue() 53 | if packet != None: 54 | self.plots.updater(packet) 55 | self.headsetState.updateHeadsetStatus(packet) 56 | 57 | if self.recording: 58 | row = self.parser.fromPacketToCSV( packet ) 59 | self.output_file.write(row + "\n") 60 | 61 | def setRecordTab(self): 62 | self.setLeftSidedBox() 63 | self.setCenterBox() 64 | 65 | # Main grid layout 66 | self.gridLayout = QGridLayout() 67 | self.gridLayout.addLayout( self.leftBox, 0, 0) 68 | self.gridLayout.addLayout( self.centerBox, 0, 1) 69 | 70 | self.gridLayout.setColumnStretch(0, 1) 71 | self.gridLayout.setColumnStretch(1, 3) 72 | 73 | return self.gridLayout 74 | 75 | def setLeftSidedBox(self): 76 | # Left sided box for controls 77 | self.leftBox = QFormLayout() 78 | 79 | self.recordButton = QPushButton("Grabar") 80 | self.recordButton.setEnabled(False) 81 | self.recordButton.clicked.connect(self.startRecord) 82 | 83 | self.stopButton = QPushButton("Detener") 84 | self.stopButton.setEnabled(False) 85 | self.stopButton.clicked.connect(self.stopRecord) 86 | 87 | self.recordButtons = QGridLayout() 88 | self.recordButtons.addWidget( self.recordButton, 0, 0) 89 | self.recordButtons.addWidget( self.stopButton, 0, 1) 90 | self.leftBox.addRow(QLabel("Controles de grabacion")) 91 | self.leftBox.addRow(self.recordButtons) 92 | 93 | self.route = QLineEdit() 94 | self.route.setReadOnly(True) 95 | self.examine = QPushButton("Examinar") 96 | self.examine.clicked.connect(self.getFilename) 97 | folderButtons = QGridLayout() 98 | folderButtons.addWidget(self.route, 0, 0) 99 | folderButtons.addWidget(self.examine, 0, 1) 100 | self.leftBox.addRow(QLabel("Carpeta de guardado")) 101 | self.leftBox.addRow(folderButtons) 102 | 103 | # Sensors status 104 | self.leftBox.addRow(QLabel("Estado de los sensores")) 105 | self.headsetState = HeadStatusWidget(self.setPlotGraphBySensor) 106 | self.leftBox.addRow(self.headsetState) 107 | self.headsetState.updateHeadsetStatus(None) 108 | 109 | def setCenterBox(self): 110 | # Center sided box for signals 111 | self.centerBox = QFormLayout() 112 | 113 | self.centerBox.addRow(QLabel("Estado de las senales")) 114 | 115 | self.returnToGraphs = QPushButton("Regresar") 116 | self.returnToGraphs.setVisible(False) 117 | self.returnToGraphs.clicked.connect(self.returnToGraphics) 118 | self.centerBox.addRow(self.returnToGraphs) 119 | 120 | self.plots = PlottingWidget() 121 | self.centerBox.addRow(self.plots) 122 | 123 | self.altPlots = PlottingWidget([]) 124 | self.centerBox.addRow(self.altPlots) 125 | self.altPlots.setVisible(False) 126 | 127 | def returnToGraphics(self): 128 | self.altPlots.setVisible(False) 129 | self.returnToGraphs.setVisible(False) 130 | self.plots.setVisible(True) 131 | 132 | def getFilename(self): 133 | filename = QFileDialog.getExistingDirectory(self.examine, "Open Directory", 134 | "/home", 135 | QFileDialog.ShowDirsOnly 136 | | QFileDialog.DontResolveSymlinks); 137 | self.fileRoute = filename 138 | self.route.setText(self.fileRoute) 139 | 140 | self.recordButton.setEnabled(True) 141 | 142 | def startRecord(self): 143 | self.stopButton.setEnabled(True) 144 | self.recordButton.setEnabled(False) 145 | 146 | self.recording = True 147 | self.file_name += datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d_%H-%M-%S') + ".csv" 148 | self.output_file = open(self.file_name, 'w') 149 | self.output_file.write(self.header_text + "\n") 150 | 151 | def stopRecord(self): 152 | self.output_file.close() -------------------------------------------------------------------------------- /UI/Record.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/UI/Record.pyc -------------------------------------------------------------------------------- /UI/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/UI/__init__.py -------------------------------------------------------------------------------- /UI/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/UI/__init__.pyc -------------------------------------------------------------------------------- /assets/headset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/assets/headset.png -------------------------------------------------------------------------------- /assets/mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/assets/mockup.png -------------------------------------------------------------------------------- /assets/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/assets/screenshot-1.png -------------------------------------------------------------------------------- /assets/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/assets/screenshot-2.png -------------------------------------------------------------------------------- /assets/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/assets/screenshot-3.png -------------------------------------------------------------------------------- /assets/screenshot-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/assets/screenshot-4.png -------------------------------------------------------------------------------- /assets/screenshot-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/assets/screenshot-5.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pycrypto>=2.6.1 2 | future 3 | pytest 4 | pygame 5 | pywinusb>=0.4.2 6 | wxPython 7 | emokit 8 | pyside 9 | pyqtgraph -------------------------------------------------------------------------------- /sample/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/sample/__init__.py -------------------------------------------------------------------------------- /sample/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/sample/__init__.pyc -------------------------------------------------------------------------------- /sample/render.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Renders a window with graph values for each sensor and a box for gyro values. 3 | try: 4 | import psyco 5 | psyco.full() 6 | except ImportError: 7 | print('') 8 | import platform 9 | import sys 10 | import os 11 | import time 12 | import datetime 13 | 14 | import pygame 15 | from pygame import FULLSCREEN 16 | 17 | if platform.system() == "Windows": 18 | pass 19 | from emokit.emotiv import Emotiv 20 | from emokit.util import get_quality_scale_level_color 21 | 22 | 23 | class Grapher(object): 24 | """ 25 | Worker that draws a line for the sensor value. 26 | """ 27 | 28 | def __init__(self, screen, name, i, old_model=False): 29 | """ 30 | Initializes graph worker 31 | """ 32 | self.screen = screen 33 | self.name = name 34 | self.range = float(1 << 13) 35 | self.x_offset = 40 36 | self.y = i * gheight 37 | self.buffer = [] 38 | font = pygame.font.Font(None, 24) 39 | self.text = font.render(self.name, 1, (255, 0, 0)) 40 | self.text_pos = self.text.get_rect() 41 | self.text_pos.centery = self.y + gheight 42 | self.first_packet = True 43 | self.y_offset = 0 44 | self.old_model = old_model 45 | 46 | def update(self, packet): 47 | """ 48 | Appends value and quality values to drawing buffer. 49 | """ 50 | if len(self.buffer) == 800 - self.x_offset: 51 | self.buffer = self.buffer[1:] 52 | self.buffer.append([packet.sensors[self.name]['value'], packet.sensors[self.name]['quality']]) 53 | 54 | def calc_y(self, val): 55 | """ 56 | Calculates line height from value. 57 | """ 58 | return val - self.y_offset + gheight 59 | 60 | def draw(self): 61 | """ 62 | Draws a line from values stored in buffer. 63 | """ 64 | if len(self.buffer) == 0: 65 | return 66 | 67 | if self.first_packet: 68 | self.y_offset = self.buffer[0][0] 69 | self.first_packet = False 70 | pos = self.x_offset, self.calc_y(self.buffer[0][0]) + self.y 71 | for i, (value, quality) in enumerate(self.buffer): 72 | y = self.calc_y(value) + self.y 73 | if self.old_model: 74 | color = str(get_quality_scale_level_color(quality, True)) 75 | else: 76 | color = str(get_quality_scale_level_color(quality, False)) 77 | pygame.draw.line(self.screen, (10,20,30), pos, (self.x_offset + i, y)) 78 | pos = (self.x_offset + i, y) 79 | self.screen.blit(self.text, self.text_pos) 80 | 81 | def save_recordings( recordings, file_name, header_text ): 82 | for record in recordings: 83 | file_name += datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d_%H-%M-%S') + ".csv" 84 | output_file = open(file_name, 'w') 85 | output_file.write(header_text + "\n") 86 | for packet in record: 87 | row_file = "" 88 | row_file += str(packet.timestamp) + "," 89 | row_file += str(packet.sensors['F3']['value']) + "," 90 | row_file += str(packet.sensors['F3']['quality']) + "," 91 | row_file += str(packet.sensors['FC5']['value']) + "," 92 | row_file += str(packet.sensors['FC5']['quality']) + "," 93 | row_file += str(packet.sensors['F7']['value']) + "," 94 | row_file += str(packet.sensors['F7']['quality']) + "," 95 | row_file += str(packet.sensors['T7']['value']) + "," 96 | row_file += str(packet.sensors['T7']['quality']) + "," 97 | row_file += str(packet.sensors['P7']['value']) + "," 98 | row_file += str(packet.sensors['P7']['quality']) + "," 99 | row_file += str(packet.sensors['O1']['value']) + "," 100 | row_file += str(packet.sensors['O1']['quality']) + "," 101 | row_file += str(packet.sensors['O2']['value']) + "," 102 | row_file += str(packet.sensors['O2']['quality']) + "," 103 | row_file += str(packet.sensors['P8']['value']) + "," 104 | row_file += str(packet.sensors['P8']['quality']) + "," 105 | row_file += str(packet.sensors['T8']['value']) + "," 106 | row_file += str(packet.sensors['T8']['quality']) + "," 107 | row_file += str(packet.sensors['F8']['value']) + "," 108 | row_file += str(packet.sensors['F8']['quality']) + "," 109 | row_file += str(packet.sensors['AF4']['value']) + "," 110 | row_file += str(packet.sensors['AF4']['quality']) + "," 111 | row_file += str(packet.sensors['FC6']['value']) + "," 112 | row_file += str(packet.sensors['FC6']['quality']) + "," 113 | row_file += str(packet.sensors['F4']['value']) + "," 114 | row_file += str(packet.sensors['F4']['quality']) + "," 115 | row_file += str(packet.sensors['AF3']['value']) + "," 116 | row_file += str(packet.sensors['AF3']['quality']) + "," 117 | row_file += str(packet.sensors['X']['value']) + "," 118 | row_file += str(packet.sensors['Y']['value']) + "," 119 | row_file += str(packet.sensors['Z']['value']) 120 | output_file.write(row_file + "\n") 121 | output_file.close() 122 | 123 | file_name += datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d_%H-%M-%S') + ".in" 124 | output_file = open(file_name, 'w') 125 | for packet in record: 126 | for sensor in packet.sensors: 127 | if sensor != "X" and sensor != "Y" and sensor != "Z" and sensor != "Unknown": 128 | row_file = "" 129 | row_file += sensor+" "+str(packet.sensors[sensor]['value']) 130 | output_file.write(row_file + "\n") 131 | output_file.close() 132 | 133 | 134 | class PacketLine: 135 | 136 | def __init__(self, line): 137 | self.sensors = {} 138 | self.timestamp = time.mktime(datetime.datetime.strptime( line[0], "%Y-%m-%d %H:%M:%S.%f").timetuple()) 139 | index = 1 140 | 141 | for name in 'F3 FC5 F7 T7 P7 O1 O2 P8 T8 F8 AF4 FC6 F4 AF3'.split(' '): 142 | self.sensors[name] = {} 143 | if line[index] == '?': self.sensors[name]['value'] = line[index] 144 | else: self.sensors[name]['value'] = int(line[index]) 145 | 146 | if line[index+1] == '?': self.sensors[name]['quality'] = line[index+1] 147 | else: self.sensors[name]['quality'] = int(line[index+1]) 148 | 149 | index += 2 150 | 151 | self.sensors['X'] = {} 152 | if line[index] == '?': self.sensors['X']['value'] = line[index] 153 | else: self.sensors['X']['value'] = int(line[index]) 154 | 155 | self.sensors['Y'] = {} 156 | if line[index+1] == '?': self.sensors['Y']['value'] = line[index+1] 157 | else: self.sensors['Y']['value'] = int(line[index+1]) 158 | 159 | self.sensors['Z'] = {} 160 | if line[index+2] == '?': self.sensors['Z']['value'] = line[index+2] 161 | else: self.sensors['Z']['value'] = int(line[index+2]) 162 | 163 | def main(): 164 | """ 165 | Creates pygame window and graph drawing workers for each sensor. 166 | """ 167 | global gheight 168 | graphers = [] 169 | recordings = [] 170 | recording = False 171 | record_packets = [] 172 | updated = False 173 | cursor_x, cursor_y = 400, 300 174 | fullscreen = False 175 | file_name = "output_data_"; 176 | header_text = "Timestamp,F3 Value,F3 Quality,FC5 Value,FC5 Quality,F7 Value,F7 Quality,T7 Value,T7 Quality,P7 Value,P7 Quality,O1 Value,O1 Quality,O2 Value,O2 Quality,P8 Value,P8 Quality,T8 Value,T8 Quality,F8 Value,F8 Quality,AF4 Value,AF4 Quality,FC6 Value,FC6 Quality,F4 Value,F4 Quality,AF3 Value,AF3 Quality,X Value,Y Value,Z Value" 177 | default_line = [ "", '0', '0','0', '0','0', '0','0', '0','0', '0','0', '0','0', '0','0', '0','0', '0','0', '0','0', '0','0', '0','0', '0','0', '0','0', '0', '0'] 178 | 179 | print "Ingrese opcion:" 180 | opcion = int(sys.stdin.readline().strip()) 181 | 182 | if opcion == 1: 183 | file_name = sys.stdin.readline().strip() 184 | #read_file = open( file_name, 'r' ) 185 | with open(file_name, 'r') as file: 186 | lines = file.readlines() 187 | 188 | pygame.init() 189 | screen = pygame.display.set_mode((800, 600)) 190 | 191 | for name in 'AF3 F7 F3 FC5 T7 P7 O1 O2 P8 T8 FC6 F4 F8 AF4'.split(' '): 192 | graphers.append( Grapher(screen, name, len(graphers) ) ) 193 | 194 | packets_in_queue = 0 195 | lines.pop(0) 196 | #for line in lines: 197 | while True: 198 | if( len(lines) != 0 ): 199 | line = lines.pop(0).strip().split(",") 200 | else: 201 | line =default_line 202 | line[0] = datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S.%f') 203 | try: 204 | while packets_in_queue < 8: 205 | packet = PacketLine(line) 206 | if packet is not None: 207 | #print packet.sensors 208 | if abs(packet.sensors['X']['value']) > 1: 209 | cursor_x = max(0, min(cursor_x, 800)) 210 | cursor_x -= packet.sensors['X']['value'] 211 | if abs(packet.sensors['Y']['value']) > 1: 212 | cursor_y += packet.sensors['Y']['value'] 213 | cursor_y = max(0, min(cursor_y, 600)) 214 | map(lambda x: x.update(packet), graphers) 215 | if recording: 216 | record_packets.append(packet) 217 | updated = True 218 | packets_in_queue += 1 219 | time.sleep(0.001) 220 | except Exception as ex: 221 | print("EmotivRender DequeuePlotError ", sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2], 222 | " : ", ex) 223 | 224 | if updated: 225 | screen.fill((75, 75, 75)) 226 | map(lambda x: x.draw(), graphers) 227 | pygame.draw.rect(screen, (255, 255, 255), (cursor_x - 5, cursor_y - 5, 10, 10), 0) 228 | pygame.display.flip() 229 | updated = False 230 | else: 231 | pygame.init() 232 | screen = pygame.display.set_mode((800, 600)) 233 | with Emotiv(display_output=False, verbose=True ) as emotiv: 234 | for name in 'AF3 F7 F3 FC5 T7 P7 O1 O2 P8 T8 FC6 F4 F8 AF4'.split(' '): 235 | graphers.append(Grapher(screen, name, len(graphers), emotiv.old_model)) 236 | while emotiv.running: 237 | for event in pygame.event.get(): 238 | if event.type == pygame.QUIT: 239 | save_recordings(recordings, file_name, header_text) 240 | emotiv.stop() 241 | return 242 | if event.type == pygame.KEYDOWN: 243 | if event.key == pygame.K_ESCAPE: 244 | save_recordings(recordings, file_name, header_text) 245 | emotiv.stop() 246 | return 247 | elif event.key == pygame.K_f: 248 | if fullscreen: 249 | screen = pygame.display.set_mode((800, 600)) 250 | fullscreen = False 251 | else: 252 | screen = pygame.display.set_mode((800, 600), FULLSCREEN, 16) 253 | fullscreen = True 254 | elif event.key == pygame.K_r: 255 | if not recording: 256 | record_packets = [] 257 | recording = True 258 | else: 259 | recording = False 260 | recordings.append(list(record_packets)) 261 | record_packets = None 262 | packets_in_queue = 0 263 | try: 264 | while packets_in_queue < 8: 265 | packet = emotiv.dequeue() 266 | if packet is not None: 267 | #print packet.sensors 268 | if abs(packet.sensors['X']['value']) > 1: 269 | cursor_x = max(0, min(cursor_x, 800)) 270 | cursor_x -= packet.sensors['X']['value'] 271 | if abs(packet.sensors['Y']['value']) > 1: 272 | cursor_y += packet.sensors['Y']['value'] 273 | cursor_y = max(0, min(cursor_y, 600)) 274 | map(lambda x: x.update(packet), graphers) 275 | if recording: 276 | record_packets.append(packet) 277 | updated = True 278 | packets_in_queue += 1 279 | time.sleep(0.001) 280 | except Exception as ex: 281 | print("EmotivRender DequeuePlotError ", sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2], 282 | " : ", ex) 283 | if updated: 284 | screen.fill((75, 75, 75)) 285 | map(lambda x: x.draw(), graphers) 286 | pygame.draw.rect(screen, (255, 255, 255), (cursor_x - 5, cursor_y - 5, 10, 10), 0) 287 | pygame.display.flip() 288 | updated = False 289 | 290 | if __name__ == "__main__": 291 | try: 292 | gheight = 580 // 14 293 | main() 294 | except Exception as ex: 295 | print("EmotivRender ", sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2], " : ", ex) 296 | -------------------------------------------------------------------------------- /sample/render.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/sample/render.pyc -------------------------------------------------------------------------------- /sample/tests.py: -------------------------------------------------------------------------------- 1 | """ ps_QPainter_drawRect101.py 2 | explore the PySide GUI toolkit to draw rectangles in different colors 3 | there are a number of ways colors can be specified 4 | fill colors are set with the brush 5 | perimeter colors are set with the pen 6 | QColor can be given a transparency value 7 | (PySide is the official LGPL-licensed version of PyQT) 8 | for Python33 you can use the Windows self-extracting installer 9 | PySide-1.1.2.win32-py3.3.exe 10 | (PyQT483 equivalent) from: 11 | http://qt-project.org/wiki/PySide 12 | or: 13 | http://www.lfd.uci.edu/~gohlke/pythonlibs/ 14 | for Qpainter methods see: 15 | http://srinikom.github.com/pyside-docs/PySide/QtGui/ 16 | QPainter.html?highlight=qpainter#PySide.QtGui.PySide.QtGui.QPainter 17 | tested with Python27 and Python33 by vegaseat 14jan2013 18 | """ 19 | from PySide.QtCore import * 20 | from PySide.QtGui import * 21 | class MyWindow(QWidget): 22 | def __init__(self): 23 | QWidget.__init__(self) 24 | # setGeometry(x_pos, y_pos, width, height) 25 | # upper left corner coordinates (x_pos, y_pos) 26 | self.setGeometry(300, 300, 370, 100) 27 | self.setWindowTitle('Colors set with brush and pen') 28 | def paintEvent(self, e): 29 | ''' 30 | the method paintEvent() is called automatically 31 | the QPainter class does all the low-level drawing 32 | coded between its methods begin() and end() 33 | ''' 34 | qp = QPainter() 35 | qp.begin(self) 36 | self.drawRectangles(qp) 37 | qp.end() 38 | def drawRectangles(self, qp): 39 | '''use QPainter (instance qp) methods to do drawings''' 40 | # there are several different ways to reference colors 41 | # use HTML style color string #RRGGBB with values 00 to FF 42 | black = "#000000" 43 | # QPen(color, width, style) 44 | qp.setPen(black) 45 | # use QColor(r, g, b) with values 0 to 255 46 | qp.setBrush(QColor(255, 0, 0)) 47 | # drawRect(int x, int y, int width, int height) 48 | # upper left corner coordinates (x, y) 49 | qp.drawRect(10, 15, 90, 60) 50 | # there are some preset named colors 51 | qp.setBrush(QColor(Qt.green)) 52 | qp.drawRect(160, 25, 90, 60) 53 | # this rectangle will overlap the previous one 54 | # you can give it some transparency alpha 0 to 255 55 | # QColor(int r, int g, int b, int alpha=255) 56 | qp.setBrush(QColor(0, 0, 255, 100)) 57 | qp.drawRect(130, 15, 90, 60) 58 | # some colors can be given as strings 59 | qp.setBrush(QColor('yellow')) 60 | qp.drawRect(265, 25, 90, 60) 61 | app = QApplication([]) 62 | win = MyWindow() 63 | win.show() 64 | # run the application event loop 65 | app.exec_() -------------------------------------------------------------------------------- /sample/tests2.py: -------------------------------------------------------------------------------- 1 | import pyqtgraph as pg 2 | from pyqtgraph.Qt import QtCore, QtGui 3 | import numpy as np 4 | 5 | import pyqtgraph.examples 6 | 7 | pyqtgraph.examples.run() -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | try: 2 | from setuptools import setup 3 | except ImportError: 4 | from distutils.core import setup 5 | import os 6 | import sys 7 | import re 8 | 9 | setup( 10 | name = 'emokitvisualizer', # temporal 11 | version = "0.1", 12 | description = "An Open Source signal visualizer for the Emotiv EPOC+ Dongle", 13 | author = "Universidad Nacional de Colombia", 14 | author_email = "datovard@unal.edu.co", 15 | py_modules=['sample', 'UI'], 16 | license = "MIT License", 17 | url = "https://github.com/EmokitAlife/EmokitVisualizer" 18 | ) -------------------------------------------------------------------------------- /tests/__pycache__/test_emokit.cpython-27-PYTEST.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/tests/__pycache__/test_emokit.cpython-27-PYTEST.pyc -------------------------------------------------------------------------------- /tests/test_emokit.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | from emokit.emotiv import Emotiv 4 | 5 | 6 | def test_emotiv_no_headset(): 7 | try: 8 | emo_test = Emotiv() 9 | except Exception as ex: 10 | assert (ex.message == "Device not found") 11 | 12 | 13 | def test_import_hidapi(): 14 | hidapi_found = False 15 | try: 16 | import hidapi 17 | hidapi_found = True 18 | except Exception as ex: 19 | pass 20 | try: 21 | import pywinusb.hid 22 | hidapi_found = True 23 | except Exception as ex: 24 | pass 25 | assert hidapi_found 26 | -------------------------------------------------------------------------------- /util/PacketParser.py: -------------------------------------------------------------------------------- 1 | class PacketParser: 2 | def fromCSVToPacket(self, headers, row): 3 | return ParsedPacket( headers, row ) 4 | 5 | def fromPacketToCSV(self, packet): 6 | row_file = "" 7 | row_file += str(packet.timestamp) + "," 8 | row_file += str(packet.sensors['F3']['value']) + "," 9 | row_file += str(packet.sensors['F3']['quality']) + "," 10 | row_file += str(packet.sensors['FC5']['value']) + "," 11 | row_file += str(packet.sensors['FC5']['quality']) + "," 12 | row_file += str(packet.sensors['F7']['value']) + "," 13 | row_file += str(packet.sensors['F7']['quality']) + "," 14 | row_file += str(packet.sensors['T7']['value']) + "," 15 | row_file += str(packet.sensors['T7']['quality']) + "," 16 | row_file += str(packet.sensors['P7']['value']) + "," 17 | row_file += str(packet.sensors['P7']['quality']) + "," 18 | row_file += str(packet.sensors['O1']['value']) + "," 19 | row_file += str(packet.sensors['O1']['quality']) + "," 20 | row_file += str(packet.sensors['O2']['value']) + "," 21 | row_file += str(packet.sensors['O2']['quality']) + "," 22 | row_file += str(packet.sensors['P8']['value']) + "," 23 | row_file += str(packet.sensors['P8']['quality']) + "," 24 | row_file += str(packet.sensors['T8']['value']) + "," 25 | row_file += str(packet.sensors['T8']['quality']) + "," 26 | row_file += str(packet.sensors['F8']['value']) + "," 27 | row_file += str(packet.sensors['F8']['quality']) + "," 28 | row_file += str(packet.sensors['AF4']['value']) + "," 29 | row_file += str(packet.sensors['AF4']['quality']) + "," 30 | row_file += str(packet.sensors['FC6']['value']) + "," 31 | row_file += str(packet.sensors['FC6']['quality']) + "," 32 | row_file += str(packet.sensors['F4']['value']) + "," 33 | row_file += str(packet.sensors['F4']['quality']) + "," 34 | row_file += str(packet.sensors['AF3']['value']) + "," 35 | row_file += str(packet.sensors['AF3']['quality']) + "," 36 | row_file += str(packet.sensors['X']['value']) + "," 37 | row_file += str(packet.sensors['Y']['value']) + "," 38 | row_file += str(packet.sensors['Z']['value']) 39 | return row_file 40 | 41 | 42 | class ParsedPacket: 43 | def __init__(self, headers, row): 44 | self.sensors = {} 45 | self.timestamp = row.pop(0) 46 | headers.pop(0) 47 | for header in headers: 48 | header = header.split() 49 | if header[0] not in self.sensors: 50 | self.sensors[header[0]] = {} 51 | value = row.pop(0) 52 | self.sensors[header[0]][header[1].lower()] = int(value) if value != "?" else None -------------------------------------------------------------------------------- /util/PacketParser.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EmokitAlife/EmokitVisualizer/f388f7b68a24f7aaa9ad1af435e603c2ee027f1f/util/PacketParser.pyc --------------------------------------------------------------------------------