audio-reactive-led-strip/python/lib/gui.py

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()