From 2b4323eca801638f0a305ff29afe815a45e103b1 Mon Sep 17 00:00:00 2001 From: Eddy Pedroni Date: Tue, 4 Jan 2022 17:55:00 +0100 Subject: Added current value to notification system, fixed Qt hack --- midi_controller_launchpad_mini.py | 16 ++++++------- notifier.py | 8 +++---- notifier_unittest.py | 40 ++++++++++++++++++++------------ solo_tool.py | 41 +++++++++++++++++++-------------- solo_tool_integrationtest.py | 48 +++++++++++++++++++++++++++++---------- solo_tool_qt.py | 6 ++--- 6 files changed, 98 insertions(+), 61 deletions(-) diff --git a/midi_controller_launchpad_mini.py b/midi_controller_launchpad_mini.py index 10013b5..6790cec 100644 --- a/midi_controller_launchpad_mini.py +++ b/midi_controller_launchpad_mini.py @@ -69,21 +69,19 @@ class MidiController: def _stop(self): self._soloTool.stop() - def _updatePlayPauseButton(self): - if self._soloTool.isPlaying(): + def _updatePlayPauseButton(self, playing): + if playing: self._setButtonLED(7, 0, MidiController.LED_GREEN) else: self._setButtonLED(7, 0, MidiController.LED_YELLOW) - def _updateVolumeRow(self): - volume = self._soloTool.getPlaybackVolume() + def _updateVolumeRow(self, volume): t1 = int(round(volume / MidiController.PLAYBACK_VOLUME_STEP, 1)) t2 = int(round(MidiController.MIN_PLAYBACK_VOLUME / MidiController.PLAYBACK_VOLUME_STEP, 1)) lastColumnLit = t1 - t2 + 1 self._lightRowUntilColumn(0, lastColumnLit, MidiController.LED_GREEN) - def _updateRateRow(self): - rate = self._soloTool.getPlaybackRate() + def _updateRateRow(self, rate): t1 = int(round(rate / MidiController.PLAYBACK_RATE_STEP, 1)) t2 = int(round(MidiController.MIN_PLAYBACK_RATE / MidiController.PLAYBACK_RATE_STEP, 1)) lastColumnLit = t1 - t2 + 1 @@ -116,14 +114,14 @@ class MidiController: self._allLEDsOff() # volume buttons - self._updateVolumeRow() + self._updateVolumeRow(self._soloTool.getPlaybackVolume()) # playback rate buttons - self._updateRateRow() + self._updateRateRow(self._soloTool.getPlaybackRate()) # playback control self._setButtonLED(6, 0, MidiController.LED_RED) - self._updatePlayPauseButton() + self._updatePlayPauseButton(self._soloTool.isPlaying()) # AB control self._setButtonLED(6, 5, MidiController.LED_YELLOW) diff --git a/notifier.py b/notifier.py index fc91e8c..06b4434 100644 --- a/notifier.py +++ b/notifier.py @@ -17,13 +17,13 @@ class Notifier: self._callbacks[event] = list() self._callbacks[event].append(callback) - def notify(self, event): + def notify(self, event, value): for callback in self._callbacks.get(event, list()): - callback() + callback(value) def _playingStateChangedCallback(self, *args): - self.notify(Notifier.PLAYING_STATE_EVENT) + self.notify(Notifier.PLAYING_STATE_EVENT, self._player.isPlaying()) def _playbackVolumeChangedCallback(self, *args): - self.notify(Notifier.PLAYBACK_VOLUME_EVENT) + self.notify(Notifier.PLAYBACK_VOLUME_EVENT, self._player.getPlaybackVolume()) diff --git a/notifier_unittest.py b/notifier_unittest.py index 13f2285..4aff8f4 100644 --- a/notifier_unittest.py +++ b/notifier_unittest.py @@ -14,20 +14,24 @@ def uut(mockPlayer): def test_allEvents(uut): def checkEvent(uut, event): callbacks = 2 - calledFlags = [False] * 2 + calledFlags = [False] * callbacks + values = [None] * callbacks def createCallback(i): - def cb(): - nonlocal calledFlags + def cb(value): + nonlocal calledFlags, values calledFlags[i] = True + values[i] = value + return cb for i in range(0, callbacks): uut.registerCallback(event, createCallback(i)) assert not any(calledFlags) - uut.notify(event) + uut.notify(event, 123) assert all(calledFlags) + assert values == [123] * callbacks checkEvent(uut, Notifier.PLAYING_STATE_EVENT) checkEvent(uut, Notifier.PLAYBACK_VOLUME_EVENT) @@ -36,33 +40,39 @@ def test_allEvents(uut): checkEvent(uut, Notifier.CURRENT_AB_EVENT) def test_eventWithoutRegisteredCallbacks(uut): - uut.notify(Notifier.PLAYING_STATE_EVENT) + uut.notify(Notifier.PLAYING_STATE_EVENT, 0) # expect no crash def test_eventsWithMockPlayer(uut, mockPlayer): - def checkEvent(eventCode, simulateEvent): + def checkEvent(eventCode, simulateEvent, expectedValue): called = False - def callback(): - nonlocal called + receivedValue = None + def callback(value): + nonlocal called, receivedValue called = True + receivedValue = value uut.registerCallback(eventCode, callback) assert not called simulateEvent() assert called + assert receivedValue == expectedValue + + mockPlayer.state = 1 + mockPlayer.volume = 75 - checkEvent(Notifier.PLAYING_STATE_EVENT, mockPlayer.simulatePlayingStateChanged) - checkEvent(Notifier.PLAYBACK_VOLUME_EVENT, mockPlayer.simulatePlaybackVolumeChanged) + checkEvent(Notifier.PLAYING_STATE_EVENT, mockPlayer.simulatePlayingStateChanged, True) + checkEvent(Notifier.PLAYBACK_VOLUME_EVENT, mockPlayer.simulatePlaybackVolumeChanged, 75) def test_singleEventNotification(uut): playingStateCalled = False - def playingStateCallback(): + def playingStateCallback(value): nonlocal playingStateCalled playingStateCalled = True volumeCalled = False - def volumeCallback(): + def volumeCallback(value): nonlocal volumeCalled volumeCalled = True @@ -72,18 +82,18 @@ def test_singleEventNotification(uut): assert not playingStateCalled assert not volumeCalled - uut.notify(Notifier.PLAYING_STATE_EVENT) + uut.notify(Notifier.PLAYING_STATE_EVENT, 0) assert playingStateCalled assert not volumeCalled playingStateCalled = False - uut.notify(Notifier.PLAYBACK_VOLUME_EVENT) + uut.notify(Notifier.PLAYBACK_VOLUME_EVENT, 0) assert not playingStateCalled assert volumeCalled volumeCalled = False - uut.notify(Notifier.PLAYBACK_RATE_EVENT) + uut.notify(Notifier.PLAYBACK_RATE_EVENT, 0) assert not playingStateCalled assert not volumeCalled diff --git a/solo_tool.py b/solo_tool.py index fee7e4a..4ea081d 100644 --- a/solo_tool.py +++ b/solo_tool.py @@ -30,22 +30,25 @@ class SoloTool: self._sessionManager.addSong(path) def setSong(self, index): - changed = index != self._playlist.getCurrentSongIndex() + previous = self._playlist.getCurrentSongIndex() self._playlist.setCurrentSong(index) - if changed: - self._notifier.notify(Notifier.CURRENT_SONG_EVENT) + new = self._playlist.getCurrentSongIndex() + if previous != new: + self._notifier.notify(Notifier.CURRENT_SONG_EVENT, new) def nextSong(self): previous = self._playlist.getCurrentSongIndex() self._playlist.nextSong() - if previous != self._playlist.getCurrentSongIndex(): - self._notifier.notify(Notifier.CURRENT_SONG_EVENT) + new = self._playlist.getCurrentSongIndex() + if previous != new: + self._notifier.notify(Notifier.CURRENT_SONG_EVENT, new) def previousSong(self): previous = self._playlist.getCurrentSongIndex() self._playlist.previousSong() - if previous != self._playlist.getCurrentSongIndex(): - self._notifier.notify(Notifier.CURRENT_SONG_EVENT) + new = self._playlist.getCurrentSongIndex() + if previous != new: + self._notifier.notify(Notifier.CURRENT_SONG_EVENT, new) def getSongs(self): return self._playlist.getSongs() @@ -54,10 +57,11 @@ class SoloTool: self._abController.storeLimits(aLimit, bLimit) def loadAbLimits(self, index): - changed = index != self._abController.getLoadedIndex() + previous = self._abController.getLoadedIndex() self._abController.loadLimits(index) - if changed: - self._notifier.notify(Notifier.CURRENT_AB_EVENT) + new = self._abController.getLoadedIndex() + if previous != new: + self._notifier.notify(Notifier.CURRENT_AB_EVENT, new) def setAbLimits(self, aLimit, bLimit): self._abController.setLimits(aLimit, bLimit) @@ -75,14 +79,16 @@ class SoloTool: def nextStoredAbLimits(self): previous = self._abController.getLoadedIndex() self._abController.nextStoredAbLimits() - if previous != self._abController.getLoadedIndex(): - self._notifier.notify(Notifier.CURRENT_AB_EVENT) + new = self._abController.getLoadedIndex() + if previous != new: + self._notifier.notify(Notifier.CURRENT_AB_EVENT, new) def previousStoredAbLimits(self): previous = self._abController.getLoadedIndex() self._abController.previousStoredAbLimits() - if previous != self._abController.getLoadedIndex(): - self._notifier.notify(Notifier.CURRENT_AB_EVENT) + new = self._abController.getLoadedIndex() + if previous != new: + self._notifier.notify(Notifier.CURRENT_AB_EVENT, new) def jumpToA(self): a = self._abController.getCurrentLimits()[0] @@ -110,10 +116,11 @@ class SoloTool: return self._player.isPlaying() def setPlaybackRate(self, rate): - previousRate = self._player.getPlaybackRate() + previous = self._player.getPlaybackRate() self._player.setPlaybackRate(rate) - if previousRate != rate: - self._notifier.notify(Notifier.PLAYBACK_RATE_EVENT) + new = self._player.getPlaybackRate() + if previous != new: + self._notifier.notify(Notifier.PLAYBACK_RATE_EVENT, new) def getPlaybackRate(self): return self._player.getPlaybackRate() diff --git a/solo_tool_integrationtest.py b/solo_tool_integrationtest.py index 386cb26..2ee4719 100644 --- a/solo_tool_integrationtest.py +++ b/solo_tool_integrationtest.py @@ -335,9 +335,11 @@ def test_playingStateNotification(uut, mockPlayer): uut.setSong(0) called = False - def callback(): - nonlocal called + receivedValue = None + def callback(value): + nonlocal called, receivedValue called = True + receivedValue = value uut.registerPlayingStateCallback(callback) @@ -346,22 +348,26 @@ def test_playingStateNotification(uut, mockPlayer): uut.play() assert called + assert receivedValue == True called = False uut.play() assert not called uut.pause() assert called + assert receivedValue == False called = False uut.pause() assert not called uut.play() assert called + assert receivedValue == True called = False uut.stop() assert called + assert receivedValue == False called = False uut.stop() assert not called @@ -372,19 +378,22 @@ def test_playbackVolumeNotification(uut, mockPlayer): uut.setSong(0) called = False - def callback(): - nonlocal called + receivedValue = None + def callback(value): + nonlocal called, receivedValue called = True + receivedValue = value uut.registerPlaybackVolumeCallback(callback) assert not called - uut.setPlaybackVolume(0.0) + uut.setPlaybackVolume(0.3) assert called + assert receivedValue == 0.3 called = False - uut.setPlaybackVolume(0.0) + uut.setPlaybackVolume(0.3) assert not called def test_playbackRateNotification(uut, mockPlayer): @@ -393,9 +402,11 @@ def test_playbackRateNotification(uut, mockPlayer): uut.setSong(0) called = False - def callback(): - nonlocal called + receivedValue = None + def callback(value): + nonlocal called, receivedValue called = True + receivedValue = value uut.registerPlaybackRateCallback(callback) @@ -403,6 +414,7 @@ def test_playbackRateNotification(uut, mockPlayer): uut.setPlaybackRate(0.5) assert called + assert receivedValue == 0.5 called = False uut.setPlaybackRate(0.5) @@ -410,9 +422,11 @@ def test_playbackRateNotification(uut, mockPlayer): def test_currentSongNotification(uut): called = False - def callback(): - nonlocal called + receivedValue = None + def callback(value): + nonlocal called, receivedValue called = True + receivedValue = value uut.registerCurrentSongCallback(callback) assert not called @@ -426,6 +440,7 @@ def test_currentSongNotification(uut): uut.setSong(0) assert called + assert receivedValue == 0 called = False uut.addSong(songs[1]) @@ -436,10 +451,12 @@ def test_currentSongNotification(uut): uut.setSong(1) assert called + assert receivedValue == 1 called = False uut.previousSong() assert called + assert receivedValue == 0 called = False uut.previousSong() @@ -447,6 +464,7 @@ def test_currentSongNotification(uut): uut.nextSong() assert called + assert receivedValue == 1 called = False uut.nextSong() @@ -454,9 +472,11 @@ def test_currentSongNotification(uut): def test_currentAbNotification(uut): called = False - def callback(): - nonlocal called + receivedValue = None + def callback(value): + nonlocal called, receivedValue called = True + receivedValue = value uut.registerCurrentAbLimitsCallback(callback) assert not called @@ -476,6 +496,7 @@ def test_currentAbNotification(uut): uut.loadAbLimits(0) assert called + assert receivedValue == 0 called = False uut.loadAbLimits(0) @@ -483,10 +504,12 @@ def test_currentAbNotification(uut): uut.loadAbLimits(1) assert called + assert receivedValue == 1 called = False uut.previousStoredAbLimits() assert called + assert receivedValue == 0 called = False uut.previousStoredAbLimits() @@ -494,6 +517,7 @@ def test_currentAbNotification(uut): uut.nextStoredAbLimits() assert called + assert receivedValue == 1 called = False uut.nextStoredAbLimits() diff --git a/solo_tool_qt.py b/solo_tool_qt.py index a6823e0..25a15fb 100644 --- a/solo_tool_qt.py +++ b/solo_tool_qt.py @@ -41,7 +41,7 @@ class ABListModel(QAbstractListModel): return len(self.soloTool.getStoredAbLimits()) class MainWindow(QMainWindow, Ui_MainWindow): - songChangeSignal = pyqtSignal() + songChangeSignal = pyqtSignal(int) def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) @@ -170,14 +170,12 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.abListModel.layoutChanged.emit() self.clearListViewSelection(self.abListView) - def currentSongChanged(self): + def currentSongChanged(self, songIndex): if self.songChangePending == CHANGE_GUI: self.songChangePending = None else: assert self.songChangePending is None self.songChangePending = CHANGE_INTERNAL - # TODO fix this hack - songIndex = self.soloTool._playlist.getCurrentSongIndex() print(f"Forcing selection to {songIndex}") i = self.playlistModel.createIndex(songIndex, 0) self.songListView.selectionModel().select(i, QItemSelectionModel.ClearAndSelect) -- cgit v1.2.3