277 lines
13 KiB
Python
277 lines
13 KiB
Python
#from __future__ import print_function
|
|
#from __future__ import division
|
|
#from scipy.ndimage.filters import gaussian_filter1d
|
|
#from collections import deque
|
|
#import time
|
|
#import sys
|
|
import numpy as np
|
|
import lib.config as config
|
|
#import microphone
|
|
#import dsp
|
|
#import led
|
|
#import random
|
|
|
|
from lib.qrangeslider import QRangeSlider
|
|
from lib.qfloatslider import QFloatSlider
|
|
import pyqtgraph as pg
|
|
from PyQt5.QtCore import *
|
|
from PyQt5.QtWidgets import *
|
|
|
|
class GUI(QMainWindow):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.settings = QSettings('settings.ini', QSettings.IniFormat)
|
|
self.settings.setFallbacksEnabled(False) # File only, no fallback to registry or or.
|
|
self.initUI()
|
|
|
|
def hideGraphs(self):
|
|
print("Blah")
|
|
|
|
def hideOpts(self):
|
|
print("Bleh")
|
|
|
|
def config.settings["configuration"]["configDialogue"](self):
|
|
self.d = QDialog(None, Qt.WindowSystemMenuHint | Qt.WindowCloseButtonHint)
|
|
b1 = QPushButton("ok",self.d)
|
|
b1.move(50,50)
|
|
self.d.setWindowTitle("Dialog")
|
|
self.d.setWindowModality(Qt.ApplicationModal)
|
|
self.d.show()
|
|
|
|
def initUI(self):
|
|
# ==================================== Set up window and wrapping layout
|
|
self.setWindowTitle("Visualization")
|
|
wrapper = QVBoxLayout()
|
|
|
|
# ======================================================= Set up toolbar
|
|
#toolbar_hideGraphs.setShortcut('Ctrl+H')
|
|
toolbar_hideGraphs = QAction('GUI Properties', self)
|
|
toolbar_hideGraphs.triggered.connect(self.config.settings["configuration"]["configDialogue"])
|
|
toolbar_hideOpts = QAction('Hide Opts', self)
|
|
toolbar_hideOpts.triggered.connect(self.hideOpts)
|
|
|
|
self.toolbar = self.addToolBar('Toolbar')
|
|
self.toolbar.addAction(toolbar_hideGraphs)
|
|
self.toolbar.addAction(toolbar_hideOpts)
|
|
|
|
# ========================================== Set up FPS and error labels
|
|
labels_layout = QHBoxLayout()
|
|
self.label_error = QLabel("")
|
|
self.label_fps = QLabel("")
|
|
self.label_latency = QLabel("")
|
|
self.label_fps.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
|
self.label_latency.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
|
labels_layout.addWidget(self.label_error)
|
|
labels_layout.addStretch()
|
|
labels_layout.addWidget(self.label_latency)
|
|
labels_layout.addWidget(self.label_fps)
|
|
|
|
# ================================================== Set up graph layout
|
|
graph_view = pg.GraphicsView()
|
|
graph_layout = pg.GraphicsLayout(border=(100,100,100))
|
|
graph_view.setCentralItem(graph_layout)
|
|
# Mel filterbank plot
|
|
fft_plot = graph_layout.addPlot(title='Filterbank Output', colspan=3)
|
|
fft_plot.setRange(yRange=[-0.1, 1.2])
|
|
fft_plot.disableAutoRange(axis=pg.ViewBox.YAxis)
|
|
x_data = np.array(range(1, config.settings["configuration"]["N_FFT_BINS"] + 1))
|
|
self.mel_curve = pg.PlotCurveItem()
|
|
self.mel_curve.setData(x=x_data, y=x_data*0)
|
|
fft_plot.addItem(self.mel_curve)
|
|
# Visualization plot
|
|
graph_layout.nextRow()
|
|
led_plot = graph_layout.addPlot(title='Visualization Output', colspan=3)
|
|
led_plot.setRange(yRange=[-5, 260])
|
|
led_plot.disableAutoRange(axis=pg.ViewBox.YAxis)
|
|
# Pen for each of the color channel curves
|
|
r_pen = pg.mkPen((255, 30, 30, 200), width=4)
|
|
g_pen = pg.mkPen((30, 255, 30, 200), width=4)
|
|
b_pen = pg.mkPen((30, 30, 255, 200), width=4)
|
|
# Color channel curves
|
|
self.r_curve = pg.PlotCurveItem(pen=r_pen)
|
|
self.g_curve = pg.PlotCurveItem(pen=g_pen)
|
|
self.b_curve = pg.PlotCurveItem(pen=b_pen)
|
|
# Define x data
|
|
x_data = np.array(range(1, config.settings["configuration"]["N_PIXELS"] + 1))
|
|
self.r_curve.setData(x=x_data, y=x_data*0)
|
|
self.g_curve.setData(x=x_data, y=x_data*0)
|
|
self.b_curve.setData(x=x_data, y=x_data*0)
|
|
# Add curves to plot
|
|
led_plot.addItem(self.r_curve)
|
|
led_plot.addItem(self.g_curve)
|
|
led_plot.addItem(self.b_curve)
|
|
|
|
# ================================================= Set up button layout
|
|
label_reactive = QLabel("Audio Reactive Effects")
|
|
label_non_reactive = QLabel("Non Reactive Effects")
|
|
reactive_button_grid = QGridLayout()
|
|
non_reactive_button_grid = QGridLayout()
|
|
buttons = {}
|
|
connecting_funcs = {}
|
|
grid_width = 4
|
|
i = 0
|
|
j = 0
|
|
k = 0
|
|
l = 0
|
|
# Dynamically layout reactive_buttons and connect them to the visualisation effects
|
|
def connect_generator(effect):
|
|
def func():
|
|
visualizer.current_effect = effect
|
|
buttons[effect].setDown(True)
|
|
func.__name__ = effect
|
|
return func
|
|
# Where the magic happens
|
|
for effect in visualizer.effects:
|
|
if not effect in visualizer.non_reactive_effects:
|
|
connecting_funcs[effect] = connect_generator(effect)
|
|
buttons[effect] = QPushButton(effect)
|
|
buttons[effect].clicked.connect(connecting_funcs[effect])
|
|
reactive_button_grid.addWidget(buttons[effect], j, i)
|
|
i += 1
|
|
if i % grid_width == 0:
|
|
i = 0
|
|
j += 1
|
|
else:
|
|
connecting_funcs[effect] = connect_generator(effect)
|
|
buttons[effect] = QPushButton(effect)
|
|
buttons[effect].clicked.connect(connecting_funcs[effect])
|
|
non_reactive_button_grid.addWidget(buttons[effect], l, k)
|
|
k += 1
|
|
if k % grid_width == 0:
|
|
k = 0
|
|
l += 1
|
|
|
|
# ============================================== Set up frequency slider
|
|
# Frequency range label
|
|
label_slider = QLabel("Frequency Range")
|
|
# Frequency slider
|
|
def freq_slider_change(tick):
|
|
minf = freq_slider.tickValue(0)**2.0 * (config.settings["configuration"]["MIC_RATE"] / 2.0)
|
|
maxf = freq_slider.tickValue(1)**2.0 * (config.settings["configuration"]["MIC_RATE"] / 2.0)
|
|
t = 'Frequency range: {:.0f} - {:.0f} Hz'.format(minf, maxf)
|
|
freq_label.setText(t)
|
|
config.settings["configuration"]["MIN_FREQUENCY"] = minf
|
|
config.settings["configuration"]["MAX_FREQUENCY"] = maxf
|
|
dsp.create_mel_bank()
|
|
def set_freq_min():
|
|
config.settings["configuration"]["MIN_FREQUENCY"] = freq_slider.start()
|
|
dsp.create_mel_bank()
|
|
def set_freq_max():
|
|
config.settings["configuration"]["MAX_FREQUENCY"] = freq_slider.end()
|
|
dsp.create_mel_bank()
|
|
freq_slider = QRangeSlider()
|
|
freq_slider.show()
|
|
freq_slider.setMin(0)
|
|
freq_slider.setMax(20000)
|
|
freq_slider.setRange(config.settings["configuration"]["MIN_FREQUENCY"], config.settings["configuration"]["MAX_FREQUENCY"])
|
|
freq_slider.setBackgroundStyle('background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #222, stop:1 #333);')
|
|
freq_slider.setSpanStyle('background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #282, stop:1 #393);')
|
|
freq_slider.setDrawValues(True)
|
|
freq_slider.endValueChanged.connect(set_freq_max)
|
|
freq_slider.startValueChanged.connect(set_freq_min)
|
|
freq_slider.setStyleSheet("""
|
|
QRangeSlider * {
|
|
border: 0px;
|
|
padding: 0px;
|
|
}
|
|
QRangeSlider > QSplitter::handle {
|
|
background: #fff;
|
|
}
|
|
QRangeSlider > QSplitter::handle:vertical {
|
|
height: 3px;
|
|
}
|
|
QRangeSlider > QSplitter::handle:pressed {
|
|
background: #ca5;
|
|
}
|
|
""")
|
|
|
|
# ============================================ Set up option tabs layout
|
|
label_options = QLabel("Effect Options")
|
|
opts_tabs = QTabWidget()
|
|
# Dynamically set up tabs
|
|
tabs = {}
|
|
grid_layouts = {}
|
|
self.grid_layout_widgets = {}
|
|
options = visualizer.effect_opts.keys()
|
|
for effect in visualizer.effects:
|
|
# Make the tab
|
|
self.grid_layout_widgets[effect] = {}
|
|
tabs[effect] = QWidget()
|
|
grid_layouts[effect] = QGridLayout()
|
|
tabs[effect].setLayout(grid_layouts[effect])
|
|
opts_tabs.addTab(tabs[effect],effect)
|
|
# These functions make functions for the dynamic ui generation
|
|
# YOU WANT-A DYNAMIC I GIVE-A YOU DYNAMIC!
|
|
def gen_slider_valuechanger(effect, key):
|
|
def func():
|
|
visualizer.effect_opts[effect][key] = self.grid_layout_widgets[effect][key].value()
|
|
return func
|
|
def gen_float_slider_valuechanger(effect, key):
|
|
def func():
|
|
visualizer.effect_opts[effect][key] = self.grid_layout_widgets[effect][key].slider_value
|
|
return func
|
|
def gen_combobox_valuechanger(effect, key):
|
|
def func():
|
|
visualizer.effect_opts[effect][key] = self.grid_layout_widgets[effect][key].currentText()
|
|
return func
|
|
def gen_checkbox_valuechanger(effect, key):
|
|
def func():
|
|
visualizer.effect_opts[effect][key] = self.grid_layout_widgets[effect][key].isChecked()
|
|
return func
|
|
# Dynamically generate ui for settings
|
|
if effect in visualizer.dynamic_effects_config.settings["configuration"]["config:"]
|
|
i = 0
|
|
connecting_funcs[effect] = {}
|
|
for key, label, ui_element, *opts in visualizer.dynamic_effects_config.settings["configuration"]["config[effect"]]:
|
|
if opts: # neatest way ^^^^^ i could think of to unpack and handle an unknown number of opts (if any)
|
|
opts = opts[0]
|
|
if ui_element == "slider":
|
|
connecting_funcs[effect][key] = gen_slider_valuechanger(effect, key)
|
|
self.grid_layout_widgets[effect][key] = QSlider(Qt.Horizontal)
|
|
self.grid_layout_widgets[effect][key].setMinimum(opts[0])
|
|
self.grid_layout_widgets[effect][key].setMaximum(opts[1])
|
|
self.grid_layout_widgets[effect][key].setValue(visualizer.effect_opts[effect][key])
|
|
self.grid_layout_widgets[effect][key].valueChanged.connect(
|
|
connecting_funcs[effect][key])
|
|
elif ui_element == "float_slider":
|
|
connecting_funcs[effect][key] = gen_float_slider_valuechanger(effect, key)
|
|
self.grid_layout_widgets[effect][key] = QFloatSlider(*opts, visualizer.effect_opts[effect][key])
|
|
self.grid_layout_widgets[effect][key].setValue(visualizer.effect_opts[effect][key])
|
|
self.grid_layout_widgets[effect][key].valueChanged.connect(
|
|
connecting_funcs[effect][key])
|
|
elif ui_element == "dropdown":
|
|
connecting_funcs[effect][key] = gen_combobox_valuechanger(effect, key)
|
|
self.grid_layout_widgets[effect][key] = QComboBox()
|
|
self.grid_layout_widgets[effect][key].addItems(opts)
|
|
self.grid_layout_widgets[effect][key].currentIndexChanged.connect(
|
|
connecting_funcs[effect][key])
|
|
elif ui_element == "checkbox":
|
|
connecting_funcs[effect][key] = gen_checkbox_valuechanger(effect, key)
|
|
self.grid_layout_widgets[effect][key] = QCheckBox()
|
|
self.grid_layout_widgets[effect][key].setCheckState(visualizer.effect_opts[effect][key])
|
|
self.grid_layout_widgets[effect][key].stateChanged.connect(
|
|
connecting_funcs[effect][key])
|
|
grid_layouts[effect].addWidget(QLabel(label),i,0)
|
|
grid_layouts[effect].addWidget(self.grid_layout_widgets[effect][key],i,1)
|
|
i += 1
|
|
#visualizer.effect_settings[effect]
|
|
else:
|
|
grid_layouts[effect].addWidget(QLabel("No customisable options for this effect :("),0,0)
|
|
|
|
|
|
|
|
# ============================================= Add layouts into wrapper
|
|
self.setCentralWidget(QWidget(self))
|
|
self.centralWidget().setLayout(wrapper)
|
|
wrapper.addLayout(labels_layout)
|
|
wrapper.addWidget(graph_view)
|
|
wrapper.addWidget(label_reactive)
|
|
wrapper.addLayout(reactive_button_grid)
|
|
wrapper.addWidget(label_non_reactive)
|
|
wrapper.addLayout(non_reactive_button_grid)
|
|
wrapper.addWidget(label_slider)
|
|
wrapper.addWidget(freq_slider)
|
|
wrapper.addWidget(label_options)
|
|
wrapper.addWidget(opts_tabs)
|
|
#self.show()
|