def connectUi (self, player): self.player= player self.playlist= player.playlist # connect buttons! self.ui.prevButton.clicked.connect (self.player.prev) # the QPushButton.clicked() emits a bool, # and it's False on normal (non-checkable) buttons # no, it's not false, it's 0, which is indistinguishable from play(0) # so lambda the 'bool' away self.ui.playButton.clicked.connect (lambda b: self.player.play ()) self.ui.pauseButton.clicked.connect (self.player.pause) self.ui.stopButton.clicked.connect (self.player.stop) self.ui.nextButton.clicked.connect (self.player.next) self.ui.randomCheck.setChecked (self.playlist.random) self.ui.randomCheck.clicked.connect (self.playlist.toggleRandom) self.playlist.randomChanged.connect (self.ui.randomCheck.setChecked) self.ui.stopAfterCheck.setChecked (self.player.stopAfter) self.ui.stopAfterCheck.clicked.connect (self.player.toggleStopAfter) self.player.stopAfterChanged.connect (self.ui.stopAfterCheck.setChecked) self.ui.searchEntry.textChanged.connect (self.search) majV, minV, patchL= utils.phononVersion () if (majV>4) or (majV==4 and minV>3) or (majV==4 and minV==3 and patchL>1): # each second self.player.media.setTickInterval (1000); self.player.media.tick.connect (self.updateTimes) self.ui.seekSlider.setMediaObject (self.player.media) # TODO: self.ui.volumeSlider # TODO: better name? self.appModel= QPlayListModel (collaggr=self.playlist.collaggr, view=self) # TODO: connect after the collection has been scanned/populated self.appModel.dataChanged.connect (self.copyEditToSelection) self.copying= False self.setModel (self.appModel) self.renamer= Renamer (self.model.collaggr) self.playlist.songChanged.connect (self.showSong) self.playlist.queued.connect (self.appModel.dirtyRow) self.playlist.dequeued.connect (self.appModel.dirtyRow) self.ui.songsList.activated.connect (self.changeSong) self.player.songChanged.connect (self.nowPlaying) # FIXME: kinda hacky self.fontMetrics= QFontMetrics (KGlobalSettings.generalFont ()) for i, w in enumerate (self.model.columnWidths): self.ui.songsList.setColumnWidth (i, self.fontMetrics.width (w)) # this is much much slower! # self.ui.songsList.verticalHeader ().setResizeMode (QHeaderView.ResizeToContents) # FIXME: temporarily until I resolve the showSong() at boot time self.modelIndex= None self.songIndexSelectedByUser= None actions.create (self, self.actionCollection ())
class MainWindow (KXmlGuiWindow): def __init__ (self, parent=None): KXmlGuiWindow.__init__ (self, parent) # load the .ui file # !!! __file__ can end with .py[co]! uipath= __file__[:__file__.rfind ('.')]+'.ui' UIMainWindow, _= uic.loadUiType (uipath) self.ui= UIMainWindow () self.ui.setupUi (self) self.collectionsAwaited= 0 self.oldSearchText= '' self.setupGUI () def connectUi (self, player): self.player= player self.playlist= player.playlist # connect buttons! self.ui.prevButton.clicked.connect (self.player.prev) # the QPushButton.clicked() emits a bool, # and it's False on normal (non-checkable) buttons # no, it's not false, it's 0, which is indistinguishable from play(0) # so lambda the 'bool' away self.ui.playButton.clicked.connect (lambda b: self.player.play ()) self.ui.pauseButton.clicked.connect (self.player.pause) self.ui.stopButton.clicked.connect (self.player.stop) self.ui.nextButton.clicked.connect (self.player.next) self.ui.randomCheck.setChecked (self.playlist.random) self.ui.randomCheck.clicked.connect (self.playlist.toggleRandom) self.playlist.randomChanged.connect (self.ui.randomCheck.setChecked) self.ui.stopAfterCheck.setChecked (self.player.stopAfter) self.ui.stopAfterCheck.clicked.connect (self.player.toggleStopAfter) self.player.stopAfterChanged.connect (self.ui.stopAfterCheck.setChecked) self.ui.searchEntry.textChanged.connect (self.search) majV, minV, patchL= utils.phononVersion () if (majV>4) or (majV==4 and minV>3) or (majV==4 and minV==3 and patchL>1): # each second self.player.media.setTickInterval (1000); self.player.media.tick.connect (self.updateTimes) self.ui.seekSlider.setMediaObject (self.player.media) # TODO: self.ui.volumeSlider # TODO: better name? self.appModel= QPlayListModel (collaggr=self.playlist.collaggr, view=self) # TODO: connect after the collection has been scanned/populated self.appModel.dataChanged.connect (self.copyEditToSelection) self.copying= False self.setModel (self.appModel) self.renamer= Renamer (self.model.collaggr) self.playlist.songChanged.connect (self.showSong) self.playlist.queued.connect (self.appModel.dirtyRow) self.playlist.dequeued.connect (self.appModel.dirtyRow) self.ui.songsList.activated.connect (self.changeSong) self.player.songChanged.connect (self.nowPlaying) # FIXME: kinda hacky self.fontMetrics= QFontMetrics (KGlobalSettings.generalFont ()) for i, w in enumerate (self.model.columnWidths): self.ui.songsList.setColumnWidth (i, self.fontMetrics.width (w)) # this is much much slower! # self.ui.songsList.verticalHeader ().setResizeMode (QHeaderView.ResizeToContents) # FIXME: temporarily until I resolve the showSong() at boot time self.modelIndex= None self.songIndexSelectedByUser= None actions.create (self, self.actionCollection ()) def setModel (self, model): logger.debug ("complex.setModel():", model) self.model= model self.ui.songsList.setModel (self.model) self.ui.songsList.resizeRowsToContents () def log (self, *args): print args def showSong (self, index): # save the old modelIndex so we can update that row and the new one oldModelIndex= self.modelIndex if self.songIndexSelectedByUser is None: logger.debug ("complex.showSong()", index) # we use the playlist model because the index is *always* refering # to that model song= self.playlist.collaggr.songForIndex (index) # we save the new modelIndex in self so we can show it # when we come back from searching modelIndex= self.modelIndex= self.model.index (index, 0) else: (song, modelIndex)= self.songIndexSelectedByUser # I also have to save it for the same reason # but using the other model! # BUG: this is getting ugly self.modelIndex= self.appModel.index (index, 0) # we used it so we discard it # it will be set again by changeSong() self.songIndexSelectedByUser= None # mark data in old song and new song as dirty # and let the view update the hightlight # FIXME? yes, this could be moved to the model (too many self.appModel's) # FIXME: temporarily if'ed until I resolve the showSong() at boot time if oldModelIndex is not None: self.appModel.dirtyRow (oldModelIndex.row ()) self.appModel.dirtyRow (self.modelIndex.row ()) logger.debug ("default.showSong()", song) # FIXME? QAbstractItemView.EnsureVisible config? self.ui.songsList.scrollTo (modelIndex, QAbstractItemView.PositionAtCenter) # move the selection cursor too self.ui.songsList.setCurrentIndex (modelIndex) # set the window title # TODO: also update on tag edition self.setCaption (self.playlist.formatSong (song)) def changeSong (self, modelIndex): # FIXME: later we ask for the index... doesn't make sense! logger.debug ("default.changeSong()", modelIndex.row ()) song= self.model.collaggr.songForIndex (modelIndex.row ()) self.songIndexSelectedByUser= (song, modelIndex) self.player.play (song) def nowPlaying (self, index): logger.debug ("complex.nowPlaying(): %s", self.playlist.formatSong (self.playlist.song)) # event self.notif= KNotification ("nowPlaying", self) self.notif.setText ("Now Playing: %s" % self.playlist.formatSong (self.playlist.song)) self.notif.sendEvent () def scanBegins (self): # self.ui.songsList.setEnabled (False) # self.ui.songsList.setUpdatesEnabled (False) pass def scanFinished (self): # self.ui.songsList.setEnabled (True) # self.ui.songsList.setUpdatesEnabled (True) pass def search (self, text): # TODO: 23:44 < steckmqn> searching w/ the next chr cqn be done only in the qlready found set # oST>2 && t>=3 => search # (oST>=1 because it can reach that state when the user presses esc) # oST>=1 && t==0 => normal # otherwise => keep current if len (text)>=3 and len (self.oldSearchText)>=2: # QString->unicode songs= self.playlist.search (unicode (text)) # we have to keep it # otherwise it pufs into inexistence after the function ends self.setModel (QPlayListModel (songs=songs, view=self)) elif len (text)==0 and len (self.oldSearchText)>=1: self.setModel (self.appModel) # ensure the current song is shown if self.modelIndex is not None: self.showSong (self.modelIndex.row ()) else: # print text, self.oldSearchText pass self.oldSearchText= text def updateTimes (self, tick): elapsed= tick/1000 # ms to s song= self.appModel.collaggr.songForIndex (self.modelIndex.row ()) length= int (song.length) remaining= elapsed-length # print "tick! %d [%d] / %d / %d" % (elapsed, tick, length, remaining) self.ui.elapsedTime.setText (utils.secondsToTime (elapsed)) self.ui.remainingTime.setText (utils.secondsToTime (remaining)) def copyEditToSelection (self, tl, br): """copies the outcome of an edition in a cell to all the selected cells in the same column which. this lets us implement mass tag edition.""" # print "complex.copyEditToSelection()", len (self.ui.songsList.selectedIndexes ()), self.appModel.edited if len (self.ui.songsList.selectedIndexes ())>1 and self.appModel.edited: # more than one cell selected # we copy was has just been edited tho the rest of selected cells logger.debug ("complex.copyEditToSelection()", self.copying) if not self.copying: self.copying= True # data() already returns QVariant value= self.appModel.data (tl, Qt.DisplayRole) logger.debug ("complex.copyEditToSelection()", value) # just copy the column that has been edited... column= tl.column () for modelIndex in self.ui.songsList.selectedIndexes (): # ... in the selected cells in the same column if modelIndex.column ()==column: # copy the value self.appModel.setData (modelIndex, value, Qt.EditRole) # we finished copying self.copying= False def collectionAdded (self): self.collectionsAwaited+= 1 def collectionLoaded (self): # TODO: locking here, for data race's sake! # really? signals get emited from threads, # but processed from the main loop, isn't it? self.collectionsAwaited-= 1 if self.collectionsAwaited==0: self.showSong (self.playlist.index) def selectedSongs (self): return [self.model.collaggr.songForIndex (modelIndex.row ()) for modelIndex in self.ui.songsList.selectedIndexes ()] ### actions ### def queue (self): logger.debug ("complex.queue()") # so we don't keep (de)queuing if several cells of the same song are selected selectedSongs= [] for modelIndex in self.ui.songsList.selectedIndexes (): logger.debug ("complex.queue()", modelIndex.row ()) if modelIndex.row () not in selectedSongs: # self.playlist.queue (modelIndex.row ()) # not so fast, cowboy. PlayList.queue() spects 'global' indexes # TODO: this is not very efficient song= self.model.collaggr.songForIndex (modelIndex.row ()) index= self.appModel.collaggr.indexForSong (song) self.playlist.queue (index) selectedSongs.append (modelIndex.row ()) def rename (self): logger.debug ("complex.rename()") songs= self.selectedSongs () self.renamer.rename (songs) def toggleVA (self): logger.debug ("complex.toggleVA()") songs= self.selectedSongs () def delete (self): # we actually move it to a 'trash' collection logger.debug ("complex.delete()") songs= self.selectedSongs () self.renamer.delete (songs) ### session management ### def saveProperties (self, config): logger ("saveProperties():") def restoreProperties (self, config): # not automatically called, add code in main() # see http://techbase.kde.org/Development/Tutorials/Session_Management#Add_session_management_support_to_your_main.28.29_function logger.debug ("restoreProperties():") def queryClose (self): logger.debug ("queryClose():") # , KApplication.sessionSaving () self.player.quit () self.renamer.saveConfig () return True def queryExit (self): logger.debug ("queryExit():") # , KApplication.sessionSaving () return True