from PyQt4 import QtGui, QtSvg, QtCore from misc import * from clMonty import monty from clPlugin import * from thread import start_new_thread from random import randint # Some predefined constants. # Note that REPEAT precedes RANDOM. E.g. if repeat # is ALBUM, and random is SONG, then it'll take a random song # from the current album ... PC_RANDOM_NO=0 # no randomness PC_RANDOM_SONG=1 # choose a song random from the playlist PC_RANDOM_ALBUM=2 # choose a random album, and play that fully PC_REPEAT_NO=0 # no repeat PC_REPEAT_SONG=1 # repeat current song PC_REPEAT_ALBUM=2 # repeat current album PC_REPEAT_PLAYLIST=3 # repeat playlist class wgPlayControl(QtGui.QWidget): """Displays controls for interacting with playing, like play, volume ...""" " control buttons" btnPlayPause=None btnStop=None btnPrevious=None btnNext=None " slider for current time" slrTime=None " slider for volume" slrVolume=None " indicator for volume" svgVolume=None " all objects in this widget" objects=None cmbRepeat=None cmbShuffle=None " contains the songs of the album the current song is playing. None, if the album is not set" curAlbumSongs=None def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) class wgSvgSwitcher(QtSvg.QSvgWidget): """Widget showing an svg-image, which, when clicked, will (un)hide an element.""" # the element we wish to hide/show scroller=None def __init__(self,scroller,parent=None): QtSvg.QSvgWidget.__init__(self,parent) self.scroller=scroller def mousePressEvent(self,event): self.scroller.setVisible(not self.scroller.isVisible()) def wheelEvent(self, event): event.accept() numDegrees=event.delta() / 8 numSteps=5*numDegrees/15 self.scroller.setValue(self.scroller.value()+numSteps) self.slrTime=QtGui.QSlider(QtCore.Qt.Horizontal, self) self.slrTime.setMinimumWidth(100) self.slrVolume=QtGui.QSlider(QtCore.Qt.Vertical, self) self.slrVolume.setMaximum(100) self.slrVolume.setMinimumWidth(100) self.slrVolume.setMaximumWidth(350) # set to some value that'll never be chosen, that way onChange will be called automatically :) self.slrVolume.setValue(3.141595) self.slrVolume.setVisible(False) self.svgVolume=wgSvgSwitcher(self.slrVolume) self.svgVolume.setMaximumSize(64,64) self.svgVolume.setMinimumSize(64,64) self.btnPlayPause=Button("play", self.onBtnPlayPauseClick, 'gfx/media-playback-start.svg', True) self.btnStop=Button("stop", self.onBtnStopClick, 'gfx/media-playback-stop.svg', True) self.btnPrevious=Button("prev", self.onBtnPreviousClick, 'gfx/media-skip-backward.svg', True) self.btnNext=Button("next", self.onBtnNextClick, 'gfx/media-skip-forward.svg', True) self.cmbShuffle=QtGui.QComboBox(self) self.cmbShuffle.addItem('Don\'t play dices') self.cmbShuffle.addItem('Random song') self.cmbShuffle.addItem('Random album') self.cmbShuffle.setCurrentIndex(int(settings.get('playcontrol.shuffle', PC_RANDOM_SONG))) self.cmbRepeat=QtGui.QComboBox(self) self.cmbRepeat.addItem('No repeat') self.cmbRepeat.addItem('Repeat current song') self.cmbRepeat.addItem('Repeat album') self.cmbRepeat.addItem('Playlist') self.cmbRepeat.setCurrentIndex(int(settings.get('playcontrol.repeat', PC_REPEAT_PLAYLIST))) self.objects=[self.slrVolume, self.slrTime, self.btnStop, self.btnNext, self.btnPrevious] layout=QtGui.QHBoxLayout(parent) layout2=QtGui.QHBoxLayout(parent) layoutWidget=QtGui.QVBoxLayout(parent) layoutWidget.addLayout(layout) layoutWidget.addLayout(layout2) self.setLayout(layoutWidget) layout.addWidget(self.btnPrevious) layout.addWidget(self.btnPlayPause) layout.addWidget(self.btnStop) layout.addWidget(self.btnNext) layout.addWidget(self.slrTime) layout.addWidget(self.slrVolume) layout.addWidget(self.svgVolume) layout2.addWidget(self.cmbRepeat) layout2.addWidget(self.cmbShuffle) self.connect(self.slrVolume, QtCore.SIGNAL('valueChanged(int)'),self.onVolumeSliderChange) self.connect(self.slrTime, QtCore.SIGNAL('sliderReleased()'),self.onTimeSliderChange) self.connect(self.cmbRepeat, QtCore.SIGNAL('currentIndexChanged(int)'),self.onCmbRepeatChanged) self.connect(self.cmbShuffle, QtCore.SIGNAL('currentIndexChanged(int)'),self.onCmbShuffleChanged) monty.addListener('onStateChange', self.onStateChange) monty.addListener('beforeSongChange', self.beforeSongChange) monty.addListener('onSongChange', self.onSongChange) monty.addListener('onVolumeChange', self.onVolumeChange) monty.addListener('onReady', self.onStateChange) monty.addListener('onDisconnect', self.onDisconnect) monty.addListener('onTimeChange', self.onTimeChange) def onStateChange(self, params): newState=monty.getStatus()['state'] map(lambda o: o.setEnabled(newState!='stop'), self.objects) if newState=='play': self.btnPlayPause.changeIcon('gfx/media-playback-pause.svg') self.btnPlayPause.setText('pauze') elif newState=='pause' or newState=='stop': self.btnPlayPause.changeIcon('gfx/media-playback-start.svg') self.btnPlayPause.setText('play') def onVolumeChange(self, params): self.slrVolume.setValue(params['newVolume']) def onDisconnect(self, params): map(lambda o: o.setEnabled(False), self.objects) def onTimeChange(self, params): if not self.slrTime.isSliderDown(): self.slrTime.setValue(params['newTime']) def onSongChange(self, params): try: self.slrTime.setMaximum(monty.getStatus()['length']) self.slrTime.setEnabled(True) except: pass # look in another thread for the songs in the current album! params=() start_new_thread(self.findAlbumSongs, params) def beforeSongChange(self, params): nextID=None song=monty.getCurrentSong() # decide here what next song to play! repeat=self.cmbRepeat.currentIndex() random=self.cmbShuffle.currentIndex() # is the current song the last of the album? eofAlbum=int(song.getTrack())==int(self.curAlbumSongs[-1].getTrack()) if repeat==PC_REPEAT_NO: # no repeat, nothing to see here! Pass on! pass elif repeat==PC_REPEAT_SONG: # we must repeat the previous song! nextID=params['curSongID'] elif repeat==PC_REPEAT_ALBUM: # check if we are at the last track, if it is, we must start a new! if eofAlbum: nextID=self.curAlbumSongs[0].getID() elif repeat==PC_REPEAT_PLAYLIST: # repeating the playlist is handled by monty itself; # it is set in onCmbRepeatChanged. pass if random==PC_RANDOM_NO: # just follow our leader Monty. pass elif random==PC_RANDOM_SONG: # pick a random song! This depends on what repeat-mode we're in. if repeat==PC_REPEAT_NO or repeat==PC_REPEAT_PLAYLIST: # we don't repeat anything, so we can just let monty pick the # next random one! pass elif repeat==PC_REPEAT_SONG: # song repeat; don't choose the next one! nextID=params['curSongID'] elif repeat==PC_REPEAT_ALBUM and self.curAlbumSongs: # pick random song from current album nextID=self.curAlbumSongs[randint(0,len(self.curAlbumSongs)-1)].getID() elif random==PC_RANDOM_ALBUM: # pick a random album! This means, we pick the first song of a random # album. if eofAlbum and (repeat==PC_REPEAT_PLAYLIST or repeat==PC_REPEAT_NO): # all first songs of an album albums=filter(lambda s: s.getAlbum() and s.getTrack()==1, monty.listPlaylist()) nextID=albums[randint(0,len(albums)-1)].getID() if nextID!=None: monty.play(nextID) def findAlbumSongs(self): """This method looks for the songs in the album of current playing song.""" song=monty.getCurrentSong() self.curAlbumSongs=None if not song or not song.getAlbum(): return self.curAlbumSongs=filter(lambda s: s.getArtist()==song.getArtist() and s.getAlbum()==song.getAlbum(), monty.listPlaylist()) self.curAlbumSongs.sort(numeric_compare) def onBtnPlayPauseClick(self): status=monty.getStatus() if status['state']=='play': monty.pause() elif status['state']=='stop': monty.play(None) #if self.lstPlaylist.getSelItemID()==-1: #if nothing selected, set it on first row #self.lstPlaylist.selectRow(0) #self.onPlaylistDoubleClick() else: monty.resume() def onBtnStopClick(self): monty.stop() def onBtnPreviousClick(self): monty.previous() def onBtnNextClick(self): monty.next() def onTimeSliderChange(self): monty.seek(self.slrTime.value()) def onVolumeSliderChange(self): v=self.slrVolume.value() monty.setVolume(v) if v<=1: mode='mute' else: mode=('0', 'min', 'med', 'max')[int(3*v/100)] self.svgVolume.load('gfx/stock_volume-%s.svg'%(mode)) def onCmbRepeatChanged(self, newval): settings.set('playcontrol.repeat', newval) if newval==PC_REPEAT_PLAYLIST: monty.repeat(1) else: monty.repeat(0) def onCmbShuffleChanged(self, newval): settings.set('playcontrol.shuffle', newval) if newval==PC_RANDOM_SONG: monty.random(1) else: monty.random(0) class pluginPlayControl(Plugin): o=None def __init__(self, winMain): Plugin.__init__(self, winMain, 'PlayControl') def _load(self): self.o=wgPlayControl(None) def getInfo(self): return "Have total control over the playing!" def _getDockWidget(self): return self._createDock(self.o)