Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 49 additions & 43 deletions paintwidget.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from PyQt5.QtCore import QThread, Qt, pyqtSignal
from PyQt5.QtGui import QPalette, QPainter, QPen
from PyQt5.QtWidgets import QWidget
from pylsl import StreamInlet, resolve_streams
from pylsl import StreamInlet, resolve_streams, cf_string
import math

class dataThread(QThread):

class DataThread(QThread):
updateStreamNames = pyqtSignal(list, int)
sendSignalChunk = pyqtSignal(int, list)

Expand All @@ -14,37 +15,42 @@ def __init__(self, parent):
self.streams = []
self.chunk_idx = 0
self.seconds_per_screen = 2

def updateStreams(self):
self.metadata = []
self.srate = None
self.chunkSize = None
self.downSampling = None
self.downSamplingFactor = None
self.downSamplingBuffer = None
self.inlet = None
self.sig_strm_idx = None

def update_streams(self):
if not self.streams:
self.streams = resolve_streams(wait_time=1.0)

if self.streams:
self.stream_idx = -1
self.metadata = [None] * len(self.streams)

for k in range(len(self.streams)):
self.metadata[k] = {
"name": self.streams[k].name(),
"ch_count": self.streams[k].channel_count(),
"ch_format": self.streams[k].channel_format(),
"srate": self.streams[k].nominal_srate()
}

if self.streams[k].channel_format() != "String" and self.stream_idx == -1:
self.stream_idx = k

self.srate = int(self.streams[self.stream_idx].nominal_srate())
self.sig_strm_idx = -1
for k, stream in enumerate(self.streams):
self.metadata.append({
"name": stream.name(),
"ch_count": stream.channel_count(),
"ch_format": stream.channel_format(),
"srate": stream.nominal_srate()
})
if self.sig_strm_idx == -1 and stream.channel_format() not in ["String", cf_string]:
self.sig_strm_idx = k

if self.sig_strm_idx != -1:
sig_stream = self.streams[self.sig_strm_idx]
self.srate = int(sig_stream.nominal_srate())
self.downSampling = False if self.srate <= 1000 else True
self.chunkSize = round(self.srate / self.chunksPerScreen * self.seconds_per_screen)

if self.downSampling:
self.downSamplingFactor = round(self.srate / 1000)
self.downSamplingBuffer = [[0 for m in range(int(self.streams[self.stream_idx].channel_count()))]
for n in range(round(self.chunkSize/self.downSamplingFactor))]
self.downSamplingBuffer = [[0 for m in range(int(sig_stream.channel_count()))]
for n in range(round(self.chunkSize/self.downSamplingFactor))]

self.inlet = StreamInlet(self.streams[self.stream_idx])
self.updateStreamNames.emit(self.metadata, self.stream_idx)
self.inlet = StreamInlet(sig_stream)
self.updateStreamNames.emit(self.metadata, self.sig_strm_idx)
self.start()

def run(self):
Expand All @@ -54,12 +60,11 @@ def run(self):
if timestamps:

if self.downSampling:
for k in range(int(self.streams[self.stream_idx].channel_count())):
for k in range(int(self.streams[self.sig_strm_idx].channel_count())):
for m in range(round(self.chunkSize/self.downSamplingFactor)):
endIdx = min((m+1) * self.downSamplingFactor, len(chunk))
buf = [chunk[n][k] for n in range(m * self.downSamplingFactor, endIdx)]
end_idx = min((m+1) * self.downSamplingFactor, len(chunk))
buf = [chunk[n][k] for n in range(m * self.downSamplingFactor, end_idx)]
self.downSamplingBuffer[m][k] = sum(buf) / len(buf)

self.sendSignalChunk.emit(self.chunk_idx, self.downSamplingBuffer)
else:
self.sendSignalChunk.emit(self.chunk_idx, chunk)
Expand All @@ -69,6 +74,7 @@ def run(self):
else:
self.chunk_idx = 0


class PaintWidget(QWidget):

def __init__(self, widget):
Expand All @@ -86,20 +92,20 @@ def __init__(self, widget):
self.setAutoFillBackground(True)
self.setPalette(pal)

self.dataTr = dataThread(self)
self.dataTr.sendSignalChunk.connect(self.getDataChunk)
self.dataTr = DataThread(self)
self.dataTr.sendSignalChunk.connect(self.get_data_chunk)

def getDataChunk(self, chunkIdx, buffer):
def get_data_chunk(self, chunk_idx, buffer):
if not self.mean:
self.mean= [0 for k in range(len(buffer[0]))]
self.mean = [0 for k in range(len(buffer[0]))]
self.scaling = [0 for k in range(len(buffer[0]))]
self.dataBuffer = buffer

self.idx = chunkIdx
self.idx = chunk_idx
self.update(self.idx * (self.width() / self.dataTr.chunksPerScreen) - self.interval,
0,
self.width() / self.dataTr.chunksPerScreen,
self.height())
0,
self.width() / self.dataTr.chunksPerScreen,
self.height())

def paintEvent(self, event):
if self.dataBuffer:
Expand Down Expand Up @@ -137,15 +143,15 @@ def paintEvent(self, event):
if self.lastY:
if not math.isnan(self.lastY[k]) and not math.isnan(self.dataBuffer[0][k]):
painter.drawLine(self.idx * (self.width() / self.dataTr.chunksPerScreen) - self.interval,
-self.lastY[k] + (k + 0.5) * self.channelHeight,
self.idx * (self.width() / self.dataTr.chunksPerScreen),
-self.dataBuffer[0][k] + (k + 0.5) * self.channelHeight)
-self.lastY[k] + (k + 0.5) * self.channelHeight,
self.idx * (self.width() / self.dataTr.chunksPerScreen),
-self.dataBuffer[0][k] + (k + 0.5) * self.channelHeight)

for m in range(len(self.dataBuffer) - 1):
if not math.isnan(self.dataBuffer[m][k]) and not math.isnan(self.dataBuffer[m+1][k]):
painter.drawLine(m * self.interval + self.idx * (self.width() / self.dataTr.chunksPerScreen),
-self.dataBuffer[m][k] + (k + 0.5) * self.channelHeight,
(m + 1) * self.interval + self.idx * (self.width() / self.dataTr.chunksPerScreen),
-self.dataBuffer[m+1][k] + (k + 0.5) * self.channelHeight)
-self.dataBuffer[m][k] + (k + 0.5) * self.channelHeight,
(m + 1)*self.interval + self.idx*(self.width() / self.dataTr.chunksPerScreen),
-self.dataBuffer[m+1][k] + (k + 0.5) * self.channelHeight)

self.lastY = self.dataBuffer[-1]
20 changes: 10 additions & 10 deletions sigvisualizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,31 @@ def __init__(self):
self.ui.toggleButton.setIcon(QIcon("icons/chevron_left.svg"))
self.ui.toggleButton.setIconSize(QSize(30, 30))

self.ui.toggleButton.clicked.connect(self.togglePanel)
self.ui.toggleButton.clicked.connect(self.toggle_panel)
self.ui.updateButton.clicked.connect(
self.ui.widget.dataTr.updateStreams)
self.ui.widget.dataTr.update_streams)
self.ui.widget.dataTr.updateStreamNames.connect(
self.updateMetadataWidget)
self.update_metadata_widget)
self.panelHidden = False

def updateMetadataWidget(self, metadata, defaultIdx):
def update_metadata_widget(self, metadata, default_idx):
for k in range(len(metadata)):
item = QTreeWidgetItem(self.ui.treeWidget)
item.setText(0, metadata[k]["name"])

for m in range(metadata[k]["ch_count"]):
channelItem = QTreeWidgetItem(item)
channelItem.setText(0, 'Channel {}'.format(m+1))
channelItem.setCheckState(0, Qt.Checked)
channel_item = QTreeWidgetItem(item)
channel_item.setText(0, 'Channel {}'.format(m+1))
channel_item.setCheckState(0, Qt.Checked)

item.setExpanded(True if k == defaultIdx else False)
item.setExpanded(True if k == default_idx else False)
self.ui.treeWidget.addTopLevelItem(item)

self.ui.treeWidget.setAnimated(True)
self.statusBar.showMessage(
"Sampling rate: {}Hz".format(metadata[defaultIdx]["srate"]))
"Sampling rate: {}Hz".format(metadata[default_idx]["srate"]))

def togglePanel(self):
def toggle_panel(self):
if self.panelHidden:
self.panelHidden = False
self.ui.treeWidget.show()
Expand Down