diff --git a/README.md b/README.md
index a3baa69..8d35959 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
# SigVisualizer
-SigVisualizer is a PyQt5 GUI application that visualizes electroencephalogram signals streamed from [__lab streaming layer__ (LSL)](https://github.com/sccn/labstreaminglayer "LabStreamingLayer (LSL)") in real time.
+SigVisualizer is a Qt GUI application that visualizes electroencephalogram signals streamed from [__lab streaming layer__ (LSL)](https://github.com/sccn/labstreaminglayer "LabStreamingLayer (LSL)") in real time.
## Requirements
-* Python 3.x
-* PyQt5
+* Python 3.9+
+* One of: PyQt5, PyQt6, PySide2, PySide6
-
+
diff --git a/createUI.bat b/createUI.bat
deleted file mode 100644
index fd22c46..0000000
--- a/createUI.bat
+++ /dev/null
@@ -1 +0,0 @@
-"C:\Users\Yida Lin\AppData\Local\Programs\Python\Python37\Scripts\pyuic5.exe" -x ui_sigvisualizer.ui -o ui_sigvisualizer.py
\ No newline at end of file
diff --git a/SigVisualizer_demo.gif b/docs/SigVisualizer_demo.gif
similarity index 100%
rename from SigVisualizer_demo.gif
rename to docs/SigVisualizer_demo.gif
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..678b331
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,36 @@
+[build-system]
+requires = ["setuptools>=68"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "sigvisualizer"
+version = "0.1.0"
+description = "Real-time Qt GUI for visualizing EEG signals streamed via Lab Streaming Layer (LSL)"
+readme = "README.md"
+license = { text = "GPL-3.0-or-later" }
+requires-python = ">=3.9"
+dependencies = [
+ "qtpy>=2.4",
+ "pylsl",
+]
+
+[project.optional-dependencies]
+pyqt5 = ["PyQt5"]
+pyqt6 = ["PyQt6"]
+pyside2 = ["PySide2"]
+pyside6 = ["PySide6"]
+
+[project.urls]
+Homepage = "https://github.com/labstreaminglayer/App-SigVisualizer"
+
+[project.scripts]
+sigvisualizer = "sigvisualizer.app:main"
+
+[tool.setuptools]
+package-dir = { "" = "src" }
+
+[tool.setuptools.packages.find]
+where = ["src"]
+
+[tool.setuptools.package-data]
+sigvisualizer = ["resources/*.ico", "resources/*.png", "resources/*.ui", "resources/icons/*.svg"]
diff --git a/LSL_sender.py b/scripts/LSL_sender.py
similarity index 100%
rename from LSL_sender.py
rename to scripts/LSL_sender.py
diff --git a/sigvisualizer.py b/sigvisualizer.py
deleted file mode 100644
index e31062b..0000000
--- a/sigvisualizer.py
+++ /dev/null
@@ -1,82 +0,0 @@
-import sys
-
-from PyQt5.QtCore import QSize, Qt, pyqtSignal
-from PyQt5.QtGui import QIcon
-from PyQt5.QtWidgets import (QApplication, QMainWindow, QStatusBar,
- QTreeWidgetItem)
-
-from ui_sigvisualizer import Ui_MainWindow
-
-
-class SigVisualizer(QMainWindow):
- stream_expanded = pyqtSignal(str)
-
- def __init__(self):
- super().__init__()
- self.ui = Ui_MainWindow()
- self.ui.setupUi(self)
- self.setWindowTitle('Real Time Signal Visualizer')
- self.ui.treeWidget.setHeaderLabel('Streams')
- self.setWindowIcon(QIcon('sigvisualizer.ico'))
-
- self.statusBar = QStatusBar()
- self.setStatusBar(self.statusBar)
-
- self.ui.toggleButton.setIcon(QIcon("icons/chevron_left.svg"))
- self.ui.toggleButton.setIconSize(QSize(30, 30))
-
- self.ui.toggleButton.clicked.connect(self.toggle_panel)
- self.ui.updateButton.clicked.connect(
- self.ui.widget.dataTr.update_streams)
- self.ui.widget.dataTr.updateStreamNames.connect(
- self.update_metadata_widget)
- self.panelHidden = False
-
- self.ui.treeWidget.itemExpanded.connect(self.tree_item_expanded)
- self.stream_expanded.connect(self.ui.widget.dataTr.handle_stream_expanded)
-
- def tree_item_expanded(self, widget_item):
- name = widget_item.text(0)
- for it_ix in range(self.ui.treeWidget.topLevelItemCount()):
- item = self.ui.treeWidget.topLevelItem(it_ix)
- if item.text(0) != name:
- item.setExpanded(False)
- self.stream_expanded.emit(name)
-
- def update_metadata_widget(self, metadata, default_idx):
- for s_ix, s_meta in enumerate(metadata):
- item = QTreeWidgetItem(self.ui.treeWidget)
- item.setText(0, s_meta["name"])
-
- for m in range(s_meta["ch_count"]):
- channel_item = QTreeWidgetItem(item)
- channel_item.setText(0, 'Channel {}'.format(m+1))
- channel_item.setCheckState(0, Qt.Checked)
-
- item.setExpanded(True if s_ix == default_idx else False)
- self.ui.treeWidget.addTopLevelItem(item)
-
- self.ui.treeWidget.setAnimated(True)
- self.statusBar.showMessage(
- "Sampling rate: {}Hz".format(metadata[default_idx]["srate"]))
-
- def toggle_panel(self):
- if self.panelHidden:
- self.panelHidden = False
- self.ui.treeWidget.show()
- self.ui.updateButton.show()
- self.ui.toggleButton.setIcon(QIcon("icons/chevron_left.svg"))
- self.ui.toggleButton.setIconSize(QSize(30, 30))
- else:
- self.panelHidden = True
- self.ui.treeWidget.hide()
- self.ui.updateButton.hide()
- self.ui.toggleButton.setIcon(QIcon("icons/chevron_right.svg"))
- self.ui.toggleButton.setIconSize(QSize(30, 30))
-
-
-if __name__ == '__main__':
- app = QApplication(sys.argv)
- window = SigVisualizer()
- window.show()
- sys.exit(app.exec_())
diff --git a/src/sigvisualizer/__init__.py b/src/sigvisualizer/__init__.py
new file mode 100644
index 0000000..26ceeab
--- /dev/null
+++ b/src/sigvisualizer/__init__.py
@@ -0,0 +1,6 @@
+from importlib.metadata import version, PackageNotFoundError
+
+try:
+ __version__ = version("sigvisualizer")
+except PackageNotFoundError:
+ __version__ = "0.0.0"
diff --git a/src/sigvisualizer/app.py b/src/sigvisualizer/app.py
new file mode 100644
index 0000000..2c7aa01
--- /dev/null
+++ b/src/sigvisualizer/app.py
@@ -0,0 +1,118 @@
+import atexit
+import sys
+from contextlib import ExitStack
+from importlib import resources
+
+from qtpy import uic
+from qtpy.QtCore import QSize, Qt, Signal
+from qtpy.QtGui import QIcon
+from qtpy.QtWidgets import QApplication, QMainWindow, QStatusBar, QTreeWidgetItem
+
+from .paintwidget import PaintWidget
+
+_resource_stack = ExitStack()
+atexit.register(_resource_stack.close)
+
+
+def _resource_path(*parts):
+ ref = resources.files("sigvisualizer").joinpath("resources", *parts)
+ return str(_resource_stack.enter_context(resources.as_file(ref)))
+
+
+class SigVisualizer(QMainWindow):
+ stream_expanded = Signal(str)
+
+ def __init__(self):
+ super().__init__()
+ uic.loadUi(_resource_path("ui_sigvisualizer.ui"), self)
+ self._install_paint_widget()
+
+ self.setWindowTitle('Real Time Signal Visualizer')
+ self.treeWidget.setHeaderLabel('Streams')
+ self.setWindowIcon(QIcon(_resource_path("sigvisualizer.ico")))
+
+ self.statusBar = QStatusBar()
+ self.setStatusBar(self.statusBar)
+
+ self.toggleButton.setIcon(QIcon(_resource_path("icons", "chevron_left.svg")))
+ self.toggleButton.setIconSize(QSize(30, 30))
+
+ self.toggleButton.clicked.connect(self.toggle_panel)
+ self.updateButton.clicked.connect(self.widget.dataTr.update_streams)
+ self.widget.dataTr.updateStreamNames.connect(self.update_metadata_widget)
+ self.panelHidden = False
+
+ self.treeWidget.itemExpanded.connect(self.tree_item_expanded)
+ self.stream_expanded.connect(self.widget.dataTr.handle_stream_expanded)
+
+ def closeEvent(self, event):
+ self.widget.dataTr.request_stop()
+ self.widget.dataTr.wait(2000)
+ super().closeEvent(event)
+
+ def _install_paint_widget(self):
+ """Replace the plain QWidget placeholder loaded from the .ui file with a real
+ PaintWidget. Kept as a placeholder in the .ui file (rather than a Designer
+ "promoted widget") since promoted-widget resolution isn't portable across
+ PyQt5/PyQt6/PySide2/PySide6."""
+ layout = self.centralwidget.layout()
+ index = layout.indexOf(self.widget)
+ row, col, rowspan, colspan = layout.getItemPosition(index)
+ layout.removeWidget(self.widget)
+ self.widget.deleteLater()
+ self.widget = PaintWidget(self.centralwidget)
+ self.widget.setObjectName("widget")
+ layout.addWidget(self.widget, row, col, rowspan, colspan)
+
+ def tree_item_expanded(self, widget_item):
+ name = widget_item.text(0)
+ for it_ix in range(self.treeWidget.topLevelItemCount()):
+ item = self.treeWidget.topLevelItem(it_ix)
+ if item.text(0) != name:
+ item.setExpanded(False)
+ self.stream_expanded.emit(name)
+
+ def update_metadata_widget(self, metadata, default_idx):
+ for s_ix, s_meta in enumerate(metadata):
+ item = QTreeWidgetItem(self.treeWidget)
+ item.setText(0, s_meta["name"])
+
+ for m in range(s_meta["ch_count"]):
+ channel_item = QTreeWidgetItem(item)
+ channel_item.setText(0, 'Channel {}'.format(m+1))
+ channel_item.setCheckState(0, Qt.CheckState.Checked)
+
+ item.setExpanded(True if s_ix == default_idx else False)
+ self.treeWidget.addTopLevelItem(item)
+
+ self.treeWidget.setAnimated(True)
+ if default_idx >= 0:
+ self.statusBar.showMessage(
+ "Sampling rate: {}Hz".format(metadata[default_idx]["srate"]))
+ else:
+ self.statusBar.showMessage("No signal streams found")
+
+ def toggle_panel(self):
+ if self.panelHidden:
+ self.panelHidden = False
+ self.treeWidget.show()
+ self.updateButton.show()
+ self.toggleButton.setIcon(QIcon(_resource_path("icons", "chevron_left.svg")))
+ self.toggleButton.setIconSize(QSize(30, 30))
+ else:
+ self.panelHidden = True
+ self.treeWidget.hide()
+ self.updateButton.hide()
+ self.toggleButton.setIcon(QIcon(_resource_path("icons", "chevron_right.svg")))
+ self.toggleButton.setIconSize(QSize(30, 30))
+
+
+def main():
+ app = QApplication(sys.argv)
+ window = SigVisualizer()
+ window.show()
+ sys.exit(app.exec())
+
+
+if __name__ == '__main__':
+ main()
diff --git a/paintwidget.py b/src/sigvisualizer/paintwidget.py
similarity index 62%
rename from paintwidget.py
rename to src/sigvisualizer/paintwidget.py
index 10a5967..1820deb 100644
--- a/paintwidget.py
+++ b/src/sigvisualizer/paintwidget.py
@@ -1,6 +1,6 @@
-from PyQt5.QtCore import QThread, Qt, pyqtSignal
-from PyQt5.QtGui import QPalette, QPainter, QPen
-from PyQt5.QtWidgets import QWidget
+from qtpy.QtCore import QThread, Qt, Signal
+from qtpy.QtGui import QPalette, QPainter, QPen
+from qtpy.QtWidgets import QWidget
import pylsl
import math
import copy
@@ -10,12 +10,11 @@
class DataThread(QThread):
- updateStreamNames = pyqtSignal(list, int)
- sendData = pyqtSignal(list, list, list, list)
- changedStream = pyqtSignal()
+ updateStreamNames = Signal(list, int)
+ sendData = Signal(list, list, list, list)
+ changedStream = Signal()
def_stream_parms = {'chunk_idx': 0, 'metadata': {}, 'srate': None, 'chunkSize': None,
- 'downSampling': None, 'downSamplingFactor': None, 'downSamplingBuffer': None,
'inlet': None, 'stream_idx': None, 'is_marker': False}
def __init__(self, parent):
@@ -25,6 +24,10 @@ def __init__(self, parent):
self.streams = []
self.stream_params = []
self.sig_strm_idx = -1
+ self._stop_requested = False
+
+ def request_stop(self):
+ self._stop_requested = True
def handle_stream_expanded(self, name):
stream_names = [_['metadata']['name'] for _ in self.stream_params]
@@ -55,12 +58,7 @@ def update_streams(self):
if self.sig_strm_idx < 0:
self.sig_strm_idx = k
srate = stream.nominal_srate()
- stream_params['downSampling'] = srate > 1000
stream_params['chunkSize'] = round(srate / self.chunksPerScreen * self.seconds_per_screen)
- if stream_params['downSampling']:
- stream_params['downSamplingFactor'] = round(srate / 1000)
- n_buff = round(stream_params['chunkSize'] / stream_params['downSamplingFactor'])
- stream_params['downSamplingBuffer'] = [[0] * int(stream.channel_count())] * n_buff
self.stream_params.append(stream_params)
self.updateStreamNames.emit([_['metadata'] for _ in self.stream_params], self.sig_strm_idx)
@@ -68,7 +66,7 @@ def update_streams(self):
def run(self):
if self.streams:
- while True:
+ while not self._stop_requested:
send_ts, send_data = [], []
if self.sig_strm_idx >= 0:
params = self.stream_params[self.sig_strm_idx]
@@ -77,13 +75,6 @@ def run(self):
if params['chunkSize']:
pull_kwargs['max_samples'] = params['chunkSize']
send_data, send_ts = inlet.pull_chunk(**pull_kwargs)
- if send_ts and params['downSampling']:
- for m in range(round(params['chunkSize'] / params['downSamplingFactor'])):
- end_idx = min((m + 1) * params['downSamplingFactor'], len(send_data))
- for ch_idx in range(int(self.streams[self.sig_strm_idx].channel_count())):
- buf = [send_data[n][ch_idx] for n in range(m * params['downSamplingFactor'], end_idx)]
- params['downSamplingBuffer'][m][ch_idx] = sum(buf) / len(buf)
- send_data = params['downSamplingBuffer']
send_mrk_ts, send_mrk_data = [], []
is_marker = [_['is_marker'] for _ in self.stream_params]
if any(is_marker):
@@ -102,41 +93,70 @@ class PaintWidget(QWidget):
def __init__(self, widget):
super().__init__()
self.reset()
- pal = QPalette()
- pal.setColor(QPalette.Background, Qt.white)
+ pal = self.palette()
self.setAutoFillBackground(True)
+ self.setBackgroundRole(QPalette.ColorRole.Base)
self.setPalette(pal)
self.dataTr = DataThread(self)
self.dataTr.sendData.connect(self.get_data)
self.dataTr.changedStream.connect(self.reset)
+ def _theme_colors(self):
+ pal = self.palette()
+ signal_color = pal.color(QPalette.ColorRole.Highlight)
+ marker_color = pal.color(QPalette.ColorRole.Accent)
+ if not marker_color.isValid():
+ marker_color = signal_color
+ return {
+ 'signal': signal_color,
+ 'marker': marker_color,
+ }
+
def reset(self):
- self.chunk_idx = 0
self.channelHeight = 0
self.px_per_samp = 0
self.dataBuffer = None
+ self.tsBuffer = None
self.markerBuffer = None
- self.lastY = []
- self.scaling = []
- self.mean = []
- self.t0 = 0
+ # Per-stream sweep/timestamp state, keyed by stream index. Streams at the
+ # same rate can arrive out of phase, so each keeps its own t0 and sweep
+ # position rather than sharing a single timeline.
+ self.stream_states = {}
+ self._paint_state = None
+
+ def _stream_state(self, idx):
+ st = self.stream_states.get(idx)
+ if st is None:
+ st = {'t0': 0, 'chunk_idx': 0, 'lastX': None, 'lastY': [], 'mean': [], 'scaling': []}
+ self.stream_states[idx] = st
+ return st
def get_data(self, sig_ts, sig_buffer, marker_ts, marker_buffer):
update_x0 = float(self.width())
update_width = 0.
+ st = self._stream_state(self.dataTr.sig_strm_idx)
# buffer should have exactly self.dataTr.chunkSize samples or be empty
if any(sig_ts):
- if not self.mean:
- self.mean = [0 for _ in range(len(sig_buffer[0]))]
- self.scaling = [1 for _ in range(len(sig_buffer[0]))]
- if self.chunk_idx == 0:
- self.t0 = sig_ts[0]
+ if not st['mean']:
+ st['mean'] = [0 for _ in range(len(sig_buffer[0]))]
+ st['scaling'] = [1 for _ in range(len(sig_buffer[0]))]
+ # Start a new sweep once a full screen of time has elapsed.
+ if st['chunk_idx'] == 0 or (sig_ts[0] - st['t0']) >= self.dataTr.seconds_per_screen:
+ st['t0'] = sig_ts[0]
+ st['chunk_idx'] = 0
+ st['lastX'] = None
+ st['lastY'] = []
self.dataBuffer = copy.deepcopy(sig_buffer)
- px_per_chunk = self.width() / self.dataTr.chunksPerScreen
- update_x0 = self.chunk_idx * px_per_chunk
- update_width = px_per_chunk
+ self.tsBuffer = list(sig_ts)
+ self._paint_state = st
+ px_per_sec = self.width() / self.dataTr.seconds_per_screen
+ x_start = (sig_ts[0] - st['t0']) * px_per_sec
+ x_end = (sig_ts[-1] - st['t0']) * px_per_sec
+ # Start the repaint region at the previous sample so the connecting line is included.
+ update_x0 = st['lastX'] if st['lastX'] is not None else x_start
+ update_width = x_end - update_x0
if any(marker_ts):
px_out = []
@@ -152,8 +172,8 @@ def get_data(self, sig_ts, sig_buffer, marker_ts, marker_buffer):
# TODO: Check samples vs pixels for both data stream and marker stream.
# I think there is some rounding error.
- if self.t0 <= ts <= (self.t0 + self.dataTr.seconds_per_screen):
- px_out.append((ts - self.t0) * px_per_sec)
+ if st['t0'] <= ts <= (st['t0'] + self.dataTr.seconds_per_screen):
+ px_out.append((ts - st['t0']) * px_per_sec)
ms_out.append(','.join(ms))
if any(px_out):
# Sometimes the marker might happen just off screen so we lose it.
@@ -161,74 +181,81 @@ def get_data(self, sig_ts, sig_buffer, marker_ts, marker_buffer):
update_x0 = min(update_x0, min(px_out))
update_width = max(update_width, max([_ - update_x0 for _ in px_out]))
- if any(sig_ts) and update_x0 == sig_ts[0]:
- update_x0 -= self.px_per_samp # Offset to connect with previous sample
-
# Repaint only the region of the screen containing this data chunk.
if update_width > 0:
self.update(int(update_x0), 0, int(update_width + 1), self.height())
def paintEvent(self, event):
painter = QPainter(self)
+ colors = self._theme_colors()
if self.dataBuffer is not None:
- painter.setPen(QPen(Qt.blue))
+ painter.setPen(QPen(colors['signal']))
+ st = self._paint_state
n_samps = len(self.dataBuffer)
n_chans = len(self.dataBuffer[0])
self.channelHeight = self.height() / n_chans
- self.px_per_samp = self.width() / self.dataTr.chunksPerScreen / n_samps
+ px_per_sec = self.width() / self.dataTr.seconds_per_screen
+ xs = [(ts - st['t0']) * px_per_sec for ts in self.tsBuffer]
# ======================================================================================================
# Calculate Trend and Scaling
# ======================================================================================================
- if self.chunk_idx == 0 or not self.mean:
+ if st['chunk_idx'] == 0 or not st['mean']:
for chan_idx in range(n_chans):
samps_for_chan = [frame[chan_idx] for frame in self.dataBuffer]
- self.mean[chan_idx] = sum(samps_for_chan) / len(samps_for_chan)
+ st['mean'][chan_idx] = sum(samps_for_chan) / len(samps_for_chan)
for m in range(len(samps_for_chan)):
- samps_for_chan[m] -= self.mean[chan_idx]
-
- data_range = (max(samps_for_chan) - min(samps_for_chan) + 0.0000000000001)
- self.scaling[chan_idx] = self.channelHeight * CHANNEL_Y_FILL / data_range
+ samps_for_chan[m] -= st['mean'][chan_idx]
+
+ # A flat channel (common with HD-EEG reference/disconnected channels)
+ # has ~zero range; scale it to 0 so it draws centered instead of
+ # exploding to a huge coordinate and overflowing drawLine (int32).
+ # See https://github.com/labstreaminglayer/App-SigVisualizer/issues/18,
+ # https://github.com/labstreaminglayer/App-SigVisualizer/issues/20
+ data_range = max(samps_for_chan) - min(samps_for_chan)
+ if data_range > 0:
+ st['scaling'][chan_idx] = self.channelHeight * CHANNEL_Y_FILL / data_range
+ else:
+ st['scaling'][chan_idx] = 0
# ======================================================================================================
# Trend Removal and Scaling
# ======================================================================================================
for samp_idx in range(n_samps):
for chan_idx in range(n_chans):
- self.dataBuffer[samp_idx][chan_idx] -= self.mean[chan_idx]
- self.dataBuffer[samp_idx][chan_idx] *= self.scaling[chan_idx]
+ self.dataBuffer[samp_idx][chan_idx] -= st['mean'][chan_idx]
+ self.dataBuffer[samp_idx][chan_idx] *= st['scaling'][chan_idx]
# ======================================================================================================
# Plot
# ======================================================================================================
- px_per_chunk = self.width() / self.dataTr.chunksPerScreen
- x0 = self.chunk_idx * px_per_chunk
for ch_idx in range(n_chans):
chan_offset = (ch_idx + 0.5) * self.channelHeight
- if self.lastY:
- if not math.isnan(self.lastY[ch_idx]) and not math.isnan(self.dataBuffer[0][ch_idx]):
- painter.drawLine(x0 - self.px_per_samp,
- -self.lastY[ch_idx] + chan_offset,
- x0,
+ if st['lastY']:
+ if not math.isnan(st['lastY'][ch_idx]) and not math.isnan(self.dataBuffer[0][ch_idx]):
+ painter.drawLine(st['lastX'],
+ -st['lastY'][ch_idx] + chan_offset,
+ xs[0],
-self.dataBuffer[0][ch_idx] + chan_offset)
for m in range(n_samps - 1):
if not math.isnan(self.dataBuffer[m][ch_idx]) and not math.isnan(self.dataBuffer[m+1][ch_idx]):
- painter.drawLine(x0 + m * self.px_per_samp,
+ painter.drawLine(xs[m],
-self.dataBuffer[m][ch_idx] + chan_offset,
- x0 + (m + 1) * self.px_per_samp,
+ xs[m + 1],
-self.dataBuffer[m+1][ch_idx] + chan_offset)
# Reset for next iteration
- self.chunk_idx = (self.chunk_idx + 1) % self.dataTr.chunksPerScreen # For next iteration
- self.lastY = self.dataBuffer[-1]
+ st['chunk_idx'] = (st['chunk_idx'] + 1) % self.dataTr.chunksPerScreen # For next iteration
+ st['lastY'] = self.dataBuffer[-1]
+ st['lastX'] = xs[-1]
self.dataBuffer = None
if self.markerBuffer is not None:
- painter.setPen(QPen(Qt.red))
+ painter.setPen(QPen(colors['marker']))
for px, mrk in self.markerBuffer:
painter.drawLine(px, 0, px, self.height())
painter.drawText(px - 2 * self.px_per_samp, 0.95 * self.height(), mrk)
diff --git a/icons/chevron_left.svg b/src/sigvisualizer/resources/icons/chevron_left.svg
similarity index 100%
rename from icons/chevron_left.svg
rename to src/sigvisualizer/resources/icons/chevron_left.svg
diff --git a/icons/chevron_right.svg b/src/sigvisualizer/resources/icons/chevron_right.svg
similarity index 100%
rename from icons/chevron_right.svg
rename to src/sigvisualizer/resources/icons/chevron_right.svg
diff --git a/sigvisualizer.ico b/src/sigvisualizer/resources/sigvisualizer.ico
similarity index 100%
rename from sigvisualizer.ico
rename to src/sigvisualizer/resources/sigvisualizer.ico
diff --git a/sigvisualizer.png b/src/sigvisualizer/resources/sigvisualizer.png
similarity index 100%
rename from sigvisualizer.png
rename to src/sigvisualizer/resources/sigvisualizer.png
diff --git a/ui_sigvisualizer.ui b/src/sigvisualizer/resources/ui_sigvisualizer.ui
similarity index 89%
rename from ui_sigvisualizer.ui
rename to src/sigvisualizer/resources/ui_sigvisualizer.ui
index 095ae28..aeb7be1 100644
--- a/ui_sigvisualizer.ui
+++ b/src/sigvisualizer/resources/ui_sigvisualizer.ui
@@ -56,7 +56,7 @@
-
-
+
-
@@ -79,14 +79,6 @@
-
-
- PaintWidget
- QWidget
-
- 1
-
-
diff --git a/ui_sigvisualizer.py b/ui_sigvisualizer.py
deleted file mode 100644
index 530c9b1..0000000
--- a/ui_sigvisualizer.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Form implementation generated from reading ui file 'ui_sigvisualizer.ui'
-#
-# Created by: PyQt5 UI code generator 5.11.2
-#
-# WARNING! All changes made in this file will be lost!
-
-from PyQt5 import QtCore, QtGui, QtWidgets
-
-class Ui_MainWindow(object):
- def setupUi(self, MainWindow):
- MainWindow.setObjectName("MainWindow")
- MainWindow.resize(1123, 759)
- self.centralwidget = QtWidgets.QWidget(MainWindow)
- self.centralwidget.setObjectName("centralwidget")
- self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
- self.gridLayout.setObjectName("gridLayout")
- self.toggleButton = QtWidgets.QPushButton(self.centralwidget)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.toggleButton.sizePolicy().hasHeightForWidth())
- self.toggleButton.setSizePolicy(sizePolicy)
- self.toggleButton.setMaximumSize(QtCore.QSize(20, 16777215))
- self.toggleButton.setText("")
- self.toggleButton.setObjectName("toggleButton")
- self.gridLayout.addWidget(self.toggleButton, 0, 0, 2, 1)
- self.treeWidget = QtWidgets.QTreeWidget(self.centralwidget)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.treeWidget.sizePolicy().hasHeightForWidth())
- self.treeWidget.setSizePolicy(sizePolicy)
- self.treeWidget.setMaximumSize(QtCore.QSize(180, 16777215))
- self.treeWidget.setObjectName("treeWidget")
- self.treeWidget.headerItem().setText(0, "1")
- self.gridLayout.addWidget(self.treeWidget, 0, 1, 1, 1)
- self.widget = PaintWidget(self.centralwidget)
- self.widget.setObjectName("widget")
- self.gridLayout.addWidget(self.widget, 0, 2, 2, 1)
- self.updateButton = QtWidgets.QPushButton(self.centralwidget)
- self.updateButton.setObjectName("updateButton")
- self.gridLayout.addWidget(self.updateButton, 1, 1, 1, 1)
- MainWindow.setCentralWidget(self.centralwidget)
- self.menubar = QtWidgets.QMenuBar(MainWindow)
- self.menubar.setGeometry(QtCore.QRect(0, 0, 1123, 26))
- self.menubar.setObjectName("menubar")
- MainWindow.setMenuBar(self.menubar)
- self.statusbar = QtWidgets.QStatusBar(MainWindow)
- self.statusbar.setObjectName("statusbar")
- MainWindow.setStatusBar(self.statusbar)
-
- self.retranslateUi(MainWindow)
- QtCore.QMetaObject.connectSlotsByName(MainWindow)
-
- def retranslateUi(self, MainWindow):
- _translate = QtCore.QCoreApplication.translate
- MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
- self.updateButton.setText(_translate("MainWindow", "Update Streams"))
-
-from paintwidget import PaintWidget
-
-if __name__ == "__main__":
- import sys
- app = QtWidgets.QApplication(sys.argv)
- MainWindow = QtWidgets.QMainWindow()
- ui = Ui_MainWindow()
- ui.setupUi(MainWindow)
- MainWindow.show()
- sys.exit(app.exec_())
-