class Friture(QMainWindow, ): def __init__(self, logger): QMainWindow.__init__(self) # logger self.logger = logger # Setup the user interface self.ui = Ui_MainWindow() self.ui.setupUi(self) # Initialize the audio data ring buffer self.audiobuffer = AudioBuffer(self.logger) # Initialize the audio backend self.audiobackend = AudioBackend(self.logger) # signal containing new data from the audio callback thread, processed as numpy array self.audiobackend.new_data_available.connect(self.audiobuffer.handle_new_data) # this timer is used to update widgets that just need to display as fast as they can self.display_timer = QtCore.QTimer() self.display_timer.setInterval(SMOOTH_DISPLAY_TIMER_PERIOD_MS) # constant timing # slow timer self.slow_timer = QtCore.QTimer() self.slow_timer.setInterval(SLOW_TIMER_PERIOD_MS) # constant timing self.about_dialog = About_Dialog(self, self.logger, self.audiobackend, self.slow_timer) self.settings_dialog = Settings_Dialog(self, self.logger, self.audiobackend) self.centralwidget = CentralWidget(self.ui.centralwidget, self.logger, "central_widget", 0) self.centralLayout = QVBoxLayout(self.ui.centralwidget) self.centralLayout.setContentsMargins(0, 0, 0, 0) self.centralLayout.addWidget(self.centralwidget) self.dockmanager = DockManager(self, self.logger) # timer ticks self.display_timer.timeout.connect(self.centralwidget.canvasUpdate) self.display_timer.timeout.connect(self.dockmanager.canvasUpdate) # toolbar clicks self.ui.actionStart.triggered.connect(self.timer_toggle) self.ui.actionSettings.triggered.connect(self.settings_called) self.ui.actionAbout.triggered.connect(self.about_called) self.ui.actionNew_dock.triggered.connect(self.dockmanager.new_dock) # restore the settings and widgets geometries self.restoreAppState() # start timers self.timer_toggle() self.slow_timer.start() self.logger.push("Init finished, entering the main loop") # slot def settings_called(self): self.settings_dialog.show() # slot def about_called(self): self.about_dialog.show() # event handler def closeEvent(self, event): self.audiobackend.close() self.saveAppState() event.accept() # method def saveAppState(self): settings = QtCore.QSettings("Friture", "Friture") settings.beginGroup("Docks") self.dockmanager.saveState(settings) settings.endGroup() settings.beginGroup("CentralWidget") self.centralwidget.saveState(settings) settings.endGroup() settings.beginGroup("MainWindow") windowGeometry = self.saveGeometry() settings.setValue("windowGeometry", windowGeometry) windowState = self.saveState() settings.setValue("windowState", windowState) settings.endGroup() settings.beginGroup("AudioBackend") self.settings_dialog.saveState(settings) settings.endGroup() # method def restoreAppState(self): settings = QtCore.QSettings("Friture", "Friture") settings.beginGroup("Docks") self.dockmanager.restoreState(settings) settings.endGroup() settings.beginGroup("CentralWidget") self.centralwidget.restoreState(settings) settings.endGroup() settings.beginGroup("MainWindow") self.restoreGeometry(settings.value("windowGeometry", type=QtCore.QByteArray)) self.restoreState(settings.value("windowState", type=QtCore.QByteArray)) settings.endGroup() settings.beginGroup("AudioBackend") self.settings_dialog.restoreState(settings) settings.endGroup() # slot def timer_toggle(self): if self.display_timer.isActive(): self.logger.push("Timer stop") self.display_timer.stop() self.ui.actionStart.setText("Start") self.audiobackend.pause() self.centralwidget.pause() self.dockmanager.pause() else: self.logger.push("Timer start") self.display_timer.start() self.ui.actionStart.setText("Stop") self.audiobackend.restart() self.centralwidget.restart() self.dockmanager.restart()
class Friture( QMainWindow, ): def __init__(self, logger): QMainWindow.__init__(self) # exception hook that logs to console, file, and display a message box self.errorDialogOpened = False sys.excepthook = self.excepthook # logger self.logger = logger # Setup the user interface self.ui = Ui_MainWindow() self.ui.setupUi(self) # Initialize the audio data ring buffer self.audiobuffer = AudioBuffer(self.logger) # Initialize the audio backend self.audiobackend = AudioBackend(self.logger) # signal containing new data from the audio callback thread, processed as numpy array self.audiobackend.new_data_available.connect( self.audiobuffer.handle_new_data) # this timer is used to update widgets that just need to display as fast as they can self.display_timer = QtCore.QTimer() self.display_timer.setInterval( SMOOTH_DISPLAY_TIMER_PERIOD_MS) # constant timing # slow timer self.slow_timer = QtCore.QTimer() self.slow_timer.setInterval(SLOW_TIMER_PERIOD_MS) # constant timing self.about_dialog = About_Dialog(self, self.logger, self.audiobackend, self.slow_timer) self.settings_dialog = Settings_Dialog(self, self.logger, self.audiobackend) self.centralwidget = CentralWidget(self.ui.centralwidget, self.logger, "central_widget", 0) self.centralLayout = QVBoxLayout(self.ui.centralwidget) self.centralLayout.setContentsMargins(0, 0, 0, 0) self.centralLayout.addWidget(self.centralwidget) self.dockmanager = DockManager(self, self.logger) # timer ticks self.display_timer.timeout.connect(self.centralwidget.canvasUpdate) self.display_timer.timeout.connect(self.dockmanager.canvasUpdate) # toolbar clicks self.ui.actionStart.triggered.connect(self.timer_toggle) self.ui.actionSettings.triggered.connect(self.settings_called) self.ui.actionAbout.triggered.connect(self.about_called) self.ui.actionNew_dock.triggered.connect(self.dockmanager.new_dock) # restore the settings and widgets geometries self.restoreAppState() # start timers self.timer_toggle() self.slow_timer.start() self.logger.push("Init finished, entering the main loop") # exception hook that logs to console, file, and display a message box def excepthook(self, exception_type, exception_value, traceback_object): gui_message = fileexcepthook(exception_type, exception_value, traceback_object) # we do not want to flood the user with message boxes when the error happens repeatedly on each timer event if not self.errorDialogOpened: self.errorDialogOpened = True errorBox(gui_message) self.errorDialogOpened = False # slot def settings_called(self): self.settings_dialog.show() # slot def about_called(self): self.about_dialog.show() # event handler def closeEvent(self, event): self.audiobackend.close() self.saveAppState() event.accept() # method def saveAppState(self): settings = QtCore.QSettings("Friture", "Friture") settings.beginGroup("Docks") self.dockmanager.saveState(settings) settings.endGroup() settings.beginGroup("CentralWidget") self.centralwidget.saveState(settings) settings.endGroup() settings.beginGroup("MainWindow") windowGeometry = self.saveGeometry() settings.setValue("windowGeometry", windowGeometry) windowState = self.saveState() settings.setValue("windowState", windowState) settings.endGroup() settings.beginGroup("AudioBackend") self.settings_dialog.saveState(settings) settings.endGroup() # method def restoreAppState(self): settings = QtCore.QSettings("Friture", "Friture") settings.beginGroup("Docks") self.dockmanager.restoreState(settings) settings.endGroup() settings.beginGroup("CentralWidget") self.centralwidget.restoreState(settings) settings.endGroup() settings.beginGroup("MainWindow") self.restoreGeometry( settings.value("windowGeometry", type=QtCore.QByteArray)) self.restoreState(settings.value("windowState", type=QtCore.QByteArray)) settings.endGroup() settings.beginGroup("AudioBackend") self.settings_dialog.restoreState(settings) settings.endGroup() # slot def timer_toggle(self): if self.display_timer.isActive(): self.logger.push("Timer stop") self.display_timer.stop() self.ui.actionStart.setText("Start") self.audiobackend.pause() self.centralwidget.pause() self.dockmanager.pause() else: self.logger.push("Timer start") self.display_timer.start() self.ui.actionStart.setText("Stop") self.audiobackend.restart() self.centralwidget.restart() self.dockmanager.restart()
class Friture( QMainWindow, ): def __init__(self, logger): QMainWindow.__init__(self) # logger self.logger = logger # Setup the user interface self.ui = Ui_MainWindow() self.ui.setupUi(self) self.settings_dialog = Settings_Dialog(self) self.about_dialog = About_Dialog(self) self.chunk_number = 0 self.buffer_timer_time = 0. self.cpu_percent = 0. # Initialize the audio data ring buffer self.audiobuffer = AudioBuffer() # Initialize the audio backend self.audiobackend = AudioBackend(self.logger) devices = self.audiobackend.get_readable_devices_list() for device in devices: self.settings_dialog.comboBox_inputDevice.addItem(device) channels = self.audiobackend.get_readable_current_channels() for channel in channels: self.settings_dialog.comboBox_firstChannel.addItem(channel) self.settings_dialog.comboBox_secondChannel.addItem(channel) current_device = self.audiobackend.get_readable_current_device() self.settings_dialog.comboBox_inputDevice.setCurrentIndex( current_device) first_channel = self.audiobackend.get_current_first_channel() self.settings_dialog.comboBox_firstChannel.setCurrentIndex( first_channel) second_channel = self.audiobackend.get_current_second_channel() self.settings_dialog.comboBox_secondChannel.setCurrentIndex( second_channel) # this timer is used to update widgets that just need to display as fast as they can self.display_timer = QtCore.QTimer() self.display_timer.setInterval( SMOOTH_DISPLAY_TIMER_PERIOD_MS) # constant timing # slow timer self.slow_timer = QtCore.QTimer() self.slow_timer.setInterval(1000) # constant timing self.centralwidget = CentralWidget(self.ui.centralwidget, self.logger, "central_widget", 0) self.centralLayout = QVBoxLayout(self.ui.centralwidget) self.centralLayout.setContentsMargins(0, 0, 0, 0) self.centralLayout.addWidget(self.centralwidget) # timer ticks self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.update_buffer) self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.statistics) # timer ticks self.connect(self.slow_timer, QtCore.SIGNAL('timeout()'), self.get_cpu_percent) # toolbar clicks self.connect(self.ui.actionStart, QtCore.SIGNAL('triggered()'), self.timer_toggle) self.connect(self.ui.actionSettings, QtCore.SIGNAL('triggered()'), self.settings_called) self.connect(self.ui.actionAbout, QtCore.SIGNAL('triggered()'), self.about_called) self.connect(self.ui.actionNew_dock, QtCore.SIGNAL('triggered()'), self.new_dock_called) # settings signals self.connect(self.settings_dialog.comboBox_inputDevice, QtCore.SIGNAL('currentIndexChanged(int)'), self.input_device_changed) self.connect(self.settings_dialog.comboBox_firstChannel, QtCore.SIGNAL('currentIndexChanged(int)'), self.first_channel_changed) self.connect(self.settings_dialog.comboBox_secondChannel, QtCore.SIGNAL('currentIndexChanged(int)'), self.second_channel_changed) self.connect(self.settings_dialog.radioButton_single, QtCore.SIGNAL('toggled(bool)'), self.single_input_type_selected) self.connect(self.settings_dialog.radioButton_duo, QtCore.SIGNAL('toggled(bool)'), self.duo_input_type_selected) self.connect(self.settings_dialog.doubleSpinBox_delay, QtCore.SIGNAL('valueChanged(double)'), self.delay_changed) # log change self.connect(self.logger, QtCore.SIGNAL('logChanged'), self.log_changed) self.connect(self.about_dialog.log_scrollarea.verticalScrollBar(), QtCore.SIGNAL('rangeChanged(int,int)'), self.log_scroll_range_changed) # restore the settings and widgets geometries self.restoreAppState() # start timers self.timer_toggle() self.slow_timer.start() self.logger.push("Init finished, entering the main loop") # slot # update the log widget with the new log content def log_changed(self): self.about_dialog.LabelLog.setText(self.logger.text()) # slot # scroll the log widget so that the last line is visible def log_scroll_range_changed(self, min, max): scrollbar = self.about_dialog.log_scrollarea.verticalScrollBar() scrollbar.setValue(max) # slot def settings_called(self): self.settings_dialog.show() # slot def about_called(self): self.about_dialog.show() # slot def new_dock_called(self): # the dock objectName is unique docknames = [dock.objectName() for dock in self.docks] dockindexes = [int(str(name).partition(' ')[-1]) for name in docknames] if len(dockindexes) == 0: index = 1 else: index = max(dockindexes) + 1 name = "Dock %d" % index new_dock = Dock(self, self.logger, name) self.addDockWidget(QtCore.Qt.TopDockWidgetArea, new_dock) self.docks += [new_dock] #slot def dock_closed(self, dock): self.docks.remove(dock) # event handler def closeEvent(self, event): self.saveAppState() event.accept() # method def saveAppState(self): settings = QtCore.QSettings("Friture", "Friture") settings.beginGroup("Docks") docknames = [dock.objectName() for dock in self.docks] settings.setValue("dockNames", docknames) for dock in self.docks: settings.beginGroup(dock.objectName()) dock.saveState(settings) settings.endGroup() settings.endGroup() settings.beginGroup("CentralWidget") self.centralwidget.saveState(settings) settings.endGroup() settings.beginGroup("MainWindow") windowGeometry = self.saveGeometry() settings.setValue("windowGeometry", windowGeometry) windowState = self.saveState() settings.setValue("windowState", windowState) settings.endGroup() settings.beginGroup("AudioBackend") self.settings_dialog.saveState(settings) settings.endGroup() # method def restoreAppState(self): settings = QtCore.QSettings("Friture", "Friture") settings.beginGroup("Docks") if settings.contains("dockNames"): docknames = settings.value("dockNames", []).toList() docknames = [dockname.toString() for dockname in docknames] # list of docks self.docks = [Dock(self, self.logger, name) for name in docknames] for dock in self.docks: settings.beginGroup(dock.objectName()) dock.restoreState(settings) settings.endGroup() else: self.logger.push("First launch, display a default set of docks") self.docks = [] self.docks += [Dock(self, self.logger, "Dock 0", type=3)] #spectrogram self.docks += [Dock(self, self.logger, "Dock 1", type=4)] #octave spectrum #self.docks += [Dock(self, self.logger, "Dock 2", type = 1)] #scope #self.docks += [Dock(self, self.logger, "Dock 3", type = 0)] #level for dock in self.docks: self.addDockWidget(QtCore.Qt.TopDockWidgetArea, dock) settings.endGroup() settings.beginGroup("CentralWidget") self.centralwidget.restoreState(settings) settings.endGroup() settings.beginGroup("MainWindow") self.restoreGeometry(settings.value("windowGeometry").toByteArray()) self.restoreState(settings.value("windowState").toByteArray()) settings.endGroup() settings.beginGroup("AudioBackend") self.settings_dialog.restoreState(settings) settings.endGroup() # slot def timer_toggle(self): if self.display_timer.isActive(): self.logger.push("Timer stop") self.display_timer.stop() self.ui.actionStart.setText("Start") for dock in self.docks: dock.custom_timer_stop() self.centralwidget.custom_timer_stop() else: self.logger.push("Timer start") self.display_timer.start() self.ui.actionStart.setText("Stop") for dock in self.docks: dock.custom_timer_start() self.centralwidget.custom_timer_start() # slot def update_buffer(self): (chunks, t, newpoints) = self.audiobackend.update(self.audiobuffer.ringbuffer) self.audiobuffer.set_newdata(newpoints) self.chunk_number += chunks self.buffer_timer_time = (95. * self.buffer_timer_time + 5. * t) / 100. def get_cpu_percent(self): self.cpu_percent = psutil.cpu_percent() # method def statistics(self): if not self.about_dialog.LabelStats.isVisible(): return label = "Chunk #%d\n"\ "Audio buffer retrieval: %.02f ms\n"\ "Global CPU usage: %d %%"\ % (self.chunk_number, self.buffer_timer_time, self.cpu_percent) self.about_dialog.LabelStats.setText(label) # slot def input_device_changed(self, index): self.ui.actionStart.setChecked(False) success, index = self.audiobackend.select_input_device(index) self.settings_dialog.comboBox_inputDevice.setCurrentIndex(index) if not success: # Note: the error message is a child of the settings dialog, so that # that dialog remains on top when the error message is closed error_message = QErrorMessage(self.settings_dialog) error_message.setWindowTitle("Input device error") error_message.showMessage( "Impossible to use the selected input device, reverting to the previous one" ) # reset the channels first_channel = self.audiobackend.get_current_first_channel() self.settings_dialog.comboBox_firstChannel.setCurrentIndex( first_channel) second_channel = self.audiobackend.get_current_second_channel() self.settings_dialog.comboBox_secondChannel.setCurrentIndex( second_channel) self.ui.actionStart.setChecked(True) # slot def first_channel_changed(self, index): self.ui.actionStart.setChecked(False) success, index = self.audiobackend.select_first_channel(index) self.settings_dialog.comboBox_firstChannel.setCurrentIndex(index) if not success: # Note: the error message is a child of the settings dialog, so that # that dialog remains on top when the error message is closed error_message = QErrorMessage(self.settings_dialog) error_message.setWindowTitle("Input device error") error_message.showMessage( "Impossible to use the selected channel as the first channel, reverting to the previous one" ) self.ui.actionStart.setChecked(True) # slot def second_channel_changed(self, index): self.ui.actionStart.setChecked(False) success, index = self.audiobackend.select_second_channel(index) self.settings_dialog.comboBox_secondChannel.setCurrentIndex(index) if not success: # Note: the error message is a child of the settings dialog, so that # that dialog remains on top when the error message is closed error_message = QErrorMessage(self.settings_dialog) error_message.setWindowTitle("Input device error") error_message.showMessage( "Impossible to use the selected channel as the second channel, reverting to the previous one" ) self.ui.actionStart.setChecked(True) # slot def single_input_type_selected(self, checked): if checked: self.settings_dialog.groupBox_second.setEnabled(False) self.settings_dialog.label_delay.setEnabled(False) self.settings_dialog.doubleSpinBox_delay.setEnabled(False) self.audiobackend.set_single_input() self.logger.push("Switching to single input") # slot def duo_input_type_selected(self, checked): if checked: self.settings_dialog.groupBox_second.setEnabled(True) self.settings_dialog.label_delay.setEnabled(True) self.settings_dialog.doubleSpinBox_delay.setEnabled(True) self.audiobackend.set_duo_input() self.logger.push("Switching to difference between two inputs") # slot def delay_changed(self, delay_ms): self.delay_ms = delay_ms self.logger.push("Delay changed to %f" % delay_ms) self.audiobuffer.set_delay_ms(delay_ms)
class Friture(QMainWindow, ): def __init__(self, logger): QMainWindow.__init__(self) # logger self.logger = logger # Setup the user interface self.ui = Ui_MainWindow() self.ui.setupUi(self) # sharedGLWidget is a hidden GL widget that will be used as a parent # to all QGLWidgets so that they have the same GL context, and can # share display lists, etc. self.sharedGLWidget = QtOpenGL.QGLWidget(self) self.sharedGLWidget.hide() # Initialize the audio data ring buffer self.audiobuffer = AudioBuffer(self.logger) # Initialize the audio backend self.audiobackend = AudioBackend(self.logger) # this timer is used to update widgets that just need to display as fast as they can self.display_timer = QtCore.QTimer() self.display_timer.setInterval(SMOOTH_DISPLAY_TIMER_PERIOD_MS) # constant timing # slow timer self.slow_timer = QtCore.QTimer() self.slow_timer.setInterval(SLOW_TIMER_PERIOD_MS) # constant timing self.about_dialog = About_Dialog(self, self.logger, self.audiobackend, self.slow_timer) self.settings_dialog = Settings_Dialog(self, self.logger, self.audiobackend) self.centralwidget = CentralWidget(self.ui.centralwidget, self.sharedGLWidget, self.logger, "central_widget", 0) self.centralLayout = QVBoxLayout(self.ui.centralwidget) self.centralLayout.setContentsMargins(0, 0, 0, 0) self.centralLayout.addWidget(self.centralwidget) self.dockmanager = DockManager(self, self.sharedGLWidget, self.logger) # timer ticks self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.update_buffer) self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.centralwidget.update) self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.dockmanager.update) # toolbar clicks self.connect(self.ui.actionStart, QtCore.SIGNAL('triggered()'), self.timer_toggle) self.connect(self.ui.actionSettings, QtCore.SIGNAL('triggered()'), self.settings_called) self.connect(self.ui.actionAbout, QtCore.SIGNAL('triggered()'), self.about_called) self.connect(self.ui.actionNew_dock, QtCore.SIGNAL('triggered()'), self.dockmanager.new_dock) # restore the settings and widgets geometries self.restoreAppState() # start timers self.timer_toggle() self.slow_timer.start() self.logger.push("Init finished, entering the main loop") # slot def settings_called(self): self.settings_dialog.show() # slot def about_called(self): self.about_dialog.show() # event handler def closeEvent(self, event): self.saveAppState() event.accept() # method def saveAppState(self): settings = QtCore.QSettings("Friture", "Friture") settings.beginGroup("Docks") self.dockmanager.saveState(settings) settings.endGroup() settings.beginGroup("CentralWidget") self.centralwidget.saveState(settings) settings.endGroup() settings.beginGroup("MainWindow") windowGeometry = self.saveGeometry() settings.setValue("windowGeometry", windowGeometry) windowState = self.saveState() settings.setValue("windowState", windowState) settings.endGroup() settings.beginGroup("AudioBackend") self.settings_dialog.saveState(settings) settings.endGroup() # method def restoreAppState(self): settings = QtCore.QSettings("Friture", "Friture") settings.beginGroup("Docks") self.dockmanager.restoreState(settings) settings.endGroup() settings.beginGroup("CentralWidget") self.centralwidget.restoreState(settings) settings.endGroup() settings.beginGroup("MainWindow") self.restoreGeometry(settings.value("windowGeometry").toByteArray()) self.restoreState(settings.value("windowState").toByteArray()) settings.endGroup() settings.beginGroup("AudioBackend") self.settings_dialog.restoreState(settings) settings.endGroup() # slot def timer_toggle(self): if self.display_timer.isActive(): self.logger.push("Timer stop") self.display_timer.stop() self.ui.actionStart.setText("Start") self.centralwidget.pause() self.dockmanager.pause() else: self.logger.push("Timer start") self.display_timer.start() self.ui.actionStart.setText("Stop") self.centralwidget.restart() self.dockmanager.restart() # slot def update_buffer(self): newpoints = self.audiobackend.update(self.audiobuffer.ringbuffer) self.audiobuffer.set_newdata(newpoints)
class Friture(QMainWindow, ): def __init__(self, logger): QMainWindow.__init__(self) # logger self.logger = logger # Setup the user interface self.ui = Ui_MainWindow() self.ui.setupUi(self) self.settings_dialog = Settings_Dialog(self) self.about_dialog = About_Dialog(self) self.chunk_number = 0 self.buffer_timer_time = 0. self.cpu_percent = 0. # Initialize the audio data ring buffer self.audiobuffer = AudioBuffer() # Initialize the audio backend self.audiobackend = AudioBackend(self.logger) devices = self.audiobackend.get_readable_devices_list() for device in devices: self.settings_dialog.comboBox_inputDevice.addItem(device) channels = self.audiobackend.get_readable_current_channels() for channel in channels: self.settings_dialog.comboBox_firstChannel.addItem(channel) self.settings_dialog.comboBox_secondChannel.addItem(channel) current_device = self.audiobackend.get_readable_current_device() self.settings_dialog.comboBox_inputDevice.setCurrentIndex(current_device) first_channel = self.audiobackend.get_current_first_channel() self.settings_dialog.comboBox_firstChannel.setCurrentIndex(first_channel) second_channel = self.audiobackend.get_current_second_channel() self.settings_dialog.comboBox_secondChannel.setCurrentIndex(second_channel) # this timer is used to update widgets that just need to display as fast as they can self.display_timer = QtCore.QTimer() self.display_timer.setInterval(SMOOTH_DISPLAY_TIMER_PERIOD_MS) # constant timing # slow timer self.slow_timer = QtCore.QTimer() self.slow_timer.setInterval(1000) # constant timing self.centralwidget = CentralWidget(self.ui.centralwidget, self.logger, "central_widget", 0) self.centralLayout = QVBoxLayout(self.ui.centralwidget) self.centralLayout.setContentsMargins(0, 0, 0, 0) self.centralLayout.addWidget(self.centralwidget) # timer ticks self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.update_buffer) self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.statistics) # timer ticks self.connect(self.slow_timer, QtCore.SIGNAL('timeout()'), self.get_cpu_percent) # toolbar clicks self.connect(self.ui.actionStart, QtCore.SIGNAL('triggered()'), self.timer_toggle) self.connect(self.ui.actionSettings, QtCore.SIGNAL('triggered()'), self.settings_called) self.connect(self.ui.actionAbout, QtCore.SIGNAL('triggered()'), self.about_called) self.connect(self.ui.actionNew_dock, QtCore.SIGNAL('triggered()'), self.new_dock_called) # settings signals self.connect(self.settings_dialog.comboBox_inputDevice, QtCore.SIGNAL('currentIndexChanged(int)'), self.input_device_changed) self.connect(self.settings_dialog.comboBox_firstChannel, QtCore.SIGNAL('currentIndexChanged(int)'), self.first_channel_changed) self.connect(self.settings_dialog.comboBox_secondChannel, QtCore.SIGNAL('currentIndexChanged(int)'), self.second_channel_changed) self.connect(self.settings_dialog.radioButton_single, QtCore.SIGNAL('toggled(bool)'), self.single_input_type_selected) self.connect(self.settings_dialog.radioButton_duo, QtCore.SIGNAL('toggled(bool)'), self.duo_input_type_selected) self.connect(self.settings_dialog.doubleSpinBox_delay, QtCore.SIGNAL('valueChanged(double)'), self.delay_changed) # log change self.connect(self.logger, QtCore.SIGNAL('logChanged'), self.log_changed) self.connect(self.about_dialog.log_scrollarea.verticalScrollBar(), QtCore.SIGNAL('rangeChanged(int,int)'), self.log_scroll_range_changed) # restore the settings and widgets geometries self.restoreAppState() # start timers self.timer_toggle() self.slow_timer.start() self.logger.push("Init finished, entering the main loop") # slot # update the log widget with the new log content def log_changed(self): self.about_dialog.LabelLog.setText(self.logger.text()) # slot # scroll the log widget so that the last line is visible def log_scroll_range_changed(self, min, max): scrollbar = self.about_dialog.log_scrollarea.verticalScrollBar() scrollbar.setValue(max) # slot def settings_called(self): self.settings_dialog.show() # slot def about_called(self): self.about_dialog.show() # slot def new_dock_called(self): # the dock objectName is unique docknames = [dock.objectName() for dock in self.docks] dockindexes = [int(str(name).partition(' ')[-1]) for name in docknames] if len(dockindexes) == 0: index = 1 else: index = max(dockindexes)+1 name = "Dock %d" %index new_dock = Dock(self, self.logger, name) self.addDockWidget(QtCore.Qt.TopDockWidgetArea, new_dock) self.docks += [new_dock] #slot def dock_closed(self, dock): self.docks.remove(dock) # event handler def closeEvent(self, event): self.saveAppState() event.accept() # method def saveAppState(self): settings = QtCore.QSettings("Friture", "Friture") settings.beginGroup("Docks") docknames = [dock.objectName() for dock in self.docks] settings.setValue("dockNames", docknames) for dock in self.docks: settings.beginGroup(dock.objectName()) dock.saveState(settings) settings.endGroup() settings.endGroup() settings.beginGroup("CentralWidget") self.centralwidget.saveState(settings) settings.endGroup() settings.beginGroup("MainWindow") windowGeometry = self.saveGeometry() settings.setValue("windowGeometry", windowGeometry) windowState = self.saveState() settings.setValue("windowState", windowState) settings.endGroup() settings.beginGroup("AudioBackend") self.settings_dialog.saveState(settings) settings.endGroup() # method def restoreAppState(self): settings = QtCore.QSettings("Friture", "Friture") settings.beginGroup("Docks") if settings.contains("dockNames"): docknames = settings.value("dockNames", []).toList() docknames = [dockname.toString() for dockname in docknames] # list of docks self.docks = [Dock(self, self.logger, name) for name in docknames] for dock in self.docks: settings.beginGroup(dock.objectName()) dock.restoreState(settings) settings.endGroup() else: self.logger.push("First launch, display a default set of docks") self.docks = [] self.docks += [Dock(self, self.logger, "Dock 0", type = 3)] #spectrogram self.docks += [Dock(self, self.logger, "Dock 1", type = 4)] #octave spectrum #self.docks += [Dock(self, self.logger, "Dock 2", type = 1)] #scope #self.docks += [Dock(self, self.logger, "Dock 3", type = 0)] #level for dock in self.docks: self.addDockWidget(QtCore.Qt.TopDockWidgetArea, dock) settings.endGroup() settings.beginGroup("CentralWidget") self.centralwidget.restoreState(settings) settings.endGroup() settings.beginGroup("MainWindow") self.restoreGeometry(settings.value("windowGeometry").toByteArray()) self.restoreState(settings.value("windowState").toByteArray()) settings.endGroup() settings.beginGroup("AudioBackend") self.settings_dialog.restoreState(settings) settings.endGroup() # slot def timer_toggle(self): if self.display_timer.isActive(): self.logger.push("Timer stop") self.display_timer.stop() self.ui.actionStart.setText("Start") for dock in self.docks: dock.custom_timer_stop() self.centralwidget.custom_timer_stop() else: self.logger.push("Timer start") self.display_timer.start() self.ui.actionStart.setText("Stop") for dock in self.docks: dock.custom_timer_start() self.centralwidget.custom_timer_start() # slot def update_buffer(self): (chunks, t, newpoints) = self.audiobackend.update(self.audiobuffer.ringbuffer) self.audiobuffer.set_newdata(newpoints) self.chunk_number += chunks self.buffer_timer_time = (95.*self.buffer_timer_time + 5.*t)/100. def get_cpu_percent(self): self.cpu_percent = psutil.cpu_percent() # method def statistics(self): if not self.about_dialog.LabelStats.isVisible(): return label = "Chunk #%d\n"\ "Audio buffer retrieval: %.02f ms\n"\ "Global CPU usage: %d %%"\ % (self.chunk_number, self.buffer_timer_time, self.cpu_percent) self.about_dialog.LabelStats.setText(label) # slot def input_device_changed(self, index): self.ui.actionStart.setChecked(False) success, index = self.audiobackend.select_input_device(index) self.settings_dialog.comboBox_inputDevice.setCurrentIndex(index) if not success: # Note: the error message is a child of the settings dialog, so that # that dialog remains on top when the error message is closed error_message = QErrorMessage(self.settings_dialog) error_message.setWindowTitle("Input device error") error_message.showMessage("Impossible to use the selected input device, reverting to the previous one") # reset the channels first_channel = self.audiobackend.get_current_first_channel() self.settings_dialog.comboBox_firstChannel.setCurrentIndex(first_channel) second_channel = self.audiobackend.get_current_second_channel() self.settings_dialog.comboBox_secondChannel.setCurrentIndex(second_channel) self.ui.actionStart.setChecked(True) # slot def first_channel_changed(self, index): self.ui.actionStart.setChecked(False) success, index = self.audiobackend.select_first_channel(index) self.settings_dialog.comboBox_firstChannel.setCurrentIndex(index) if not success: # Note: the error message is a child of the settings dialog, so that # that dialog remains on top when the error message is closed error_message = QErrorMessage(self.settings_dialog) error_message.setWindowTitle("Input device error") error_message.showMessage("Impossible to use the selected channel as the first channel, reverting to the previous one") self.ui.actionStart.setChecked(True) # slot def second_channel_changed(self, index): self.ui.actionStart.setChecked(False) success, index = self.audiobackend.select_second_channel(index) self.settings_dialog.comboBox_secondChannel.setCurrentIndex(index) if not success: # Note: the error message is a child of the settings dialog, so that # that dialog remains on top when the error message is closed error_message = QErrorMessage(self.settings_dialog) error_message.setWindowTitle("Input device error") error_message.showMessage("Impossible to use the selected channel as the second channel, reverting to the previous one") self.ui.actionStart.setChecked(True) # slot def single_input_type_selected(self, checked): if checked: self.settings_dialog.groupBox_second.setEnabled(False) self.settings_dialog.label_delay.setEnabled(False) self.settings_dialog.doubleSpinBox_delay.setEnabled(False) self.audiobackend.set_single_input() self.logger.push("Switching to single input") # slot def duo_input_type_selected(self, checked): if checked: self.settings_dialog.groupBox_second.setEnabled(True) self.settings_dialog.label_delay.setEnabled(True) self.settings_dialog.doubleSpinBox_delay.setEnabled(True) self.audiobackend.set_duo_input() self.logger.push("Switching to difference between two inputs") # slot def delay_changed(self, delay_ms): self.delay_ms = delay_ms self.logger.push("Delay changed to %f" %delay_ms) self.audiobuffer.set_delay_ms(delay_ms)
class Friture( QMainWindow, ): def __init__(self, logger): QMainWindow.__init__(self) # logger self.logger = logger # Setup the user interface self.ui = Ui_MainWindow() self.ui.setupUi(self) # Initialize the audio data ring buffer self.audiobuffer = AudioBuffer(self.logger) # Initialize the audio backend self.audiobackend = AudioBackend(self.logger) # this timer is used to update widgets that just need to display as fast as they can self.display_timer = QtCore.QTimer() self.display_timer.setInterval( SMOOTH_DISPLAY_TIMER_PERIOD_MS) # constant timing # slow timer self.slow_timer = QtCore.QTimer() self.slow_timer.setInterval(SLOW_TIMER_PERIOD_MS) # constant timing self.about_dialog = About_Dialog(self, self.logger, self.audiobackend, self.slow_timer) self.settings_dialog = Settings_Dialog(self, self.logger, self.audiobackend) self.centralwidget = CentralWidget(self.ui.centralwidget, self.logger, "central_widget", 0) self.centralLayout = QVBoxLayout(self.ui.centralwidget) self.centralLayout.setContentsMargins(0, 0, 0, 0) self.centralLayout.addWidget(self.centralwidget) self.dockmanager = DockManager(self, self.logger) # timer ticks self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.update_buffer) self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.centralwidget.update) self.connect(self.display_timer, QtCore.SIGNAL('timeout()'), self.dockmanager.update) # toolbar clicks self.connect(self.ui.actionStart, QtCore.SIGNAL('triggered()'), self.timer_toggle) self.connect(self.ui.actionSettings, QtCore.SIGNAL('triggered()'), self.settings_called) self.connect(self.ui.actionAbout, QtCore.SIGNAL('triggered()'), self.about_called) self.connect(self.ui.actionNew_dock, QtCore.SIGNAL('triggered()'), self.dockmanager.new_dock) # restore the settings and widgets geometries self.restoreAppState() # start timers self.timer_toggle() self.slow_timer.start() self.logger.push("Init finished, entering the main loop") # slot def settings_called(self): self.settings_dialog.show() # slot def about_called(self): self.about_dialog.show() # event handler def closeEvent(self, event): self.saveAppState() event.accept() # method def saveAppState(self): settings = QtCore.QSettings("Friture", "Friture") settings.beginGroup("Docks") self.dockmanager.saveState(settings) settings.endGroup() settings.beginGroup("CentralWidget") self.centralwidget.saveState(settings) settings.endGroup() settings.beginGroup("MainWindow") windowGeometry = self.saveGeometry() settings.setValue("windowGeometry", windowGeometry) windowState = self.saveState() settings.setValue("windowState", windowState) settings.endGroup() settings.beginGroup("AudioBackend") self.settings_dialog.saveState(settings) settings.endGroup() # method def restoreAppState(self): settings = QtCore.QSettings("Friture", "Friture") settings.beginGroup("Docks") self.dockmanager.restoreState(settings) settings.endGroup() settings.beginGroup("CentralWidget") self.centralwidget.restoreState(settings) settings.endGroup() settings.beginGroup("MainWindow") self.restoreGeometry(settings.value("windowGeometry").toByteArray()) self.restoreState(settings.value("windowState").toByteArray()) settings.endGroup() settings.beginGroup("AudioBackend") self.settings_dialog.restoreState(settings) settings.endGroup() # slot def timer_toggle(self): if self.display_timer.isActive(): self.logger.push("Timer stop") self.display_timer.stop() self.ui.actionStart.setText("Start") else: self.logger.push("Timer start") self.display_timer.start() self.ui.actionStart.setText("Stop") # slot def update_buffer(self): newpoints = self.audiobackend.update(self.audiobuffer.ringbuffer) self.audiobuffer.set_newdata(newpoints)