from PyQt4 import QtGui, QtCore from PyQt4.QtCore import QVariant from traceback import print_exc from misc import * from clMonty import Monty import plugins from winConnect import winConnect from winSettings import winSettings import logging DEFAULT_LAYOUT_FILE = 'default_layout' class winMain(QtGui.QMainWindow): """The winMain class is mpc's main window, showing the playlists and control-interface""" docks=[] " menus" mConnect=None mDisconnect=None mLayout=None " connection window" wConnect=None wSettings=None " MPD object" monty = None " Statusbar objects" statuslabel = None time_slider = None time_label = None settings = None def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self.settings = QtCore.QSettings(ORGNAME, APPNAME) self.monty = Monty() self.wConnect=winConnect(self) # statusbar self.statusBar() self.statuslabel = QtGui.QLabel() self.time_slider = QtGui.QSlider(QtCore.Qt.Horizontal, self) self.time_slider.setMaximumWidth(self.width()/4) self.connect(self.time_slider, QtCore.SIGNAL('sliderReleased()'), self.on_time_slider_change) self.time_label = QtGui.QLabel() self.time_label.duration = '0:00' self.statusBar().addWidget(self.statuslabel) self.statusBar().addPermanentWidget(self.time_label) self.statusBar().addPermanentWidget(self.time_slider) mBar = QtGui.QMenuBar() # create a menubar # File menu m = mBar.addMenu("File") m.setTearOffEnabled(True) # connect self.mConnect=m.addAction('Connect ...', self.wConnect.monitor) self.mConnect.setIcon(QtGui.QIcon(appIcon)) # disconnect self.mDisconnect=m.addAction('Disconnect', self.monty.disconnect) self.mDisconnect.setIcon(QtGui.QIcon('gfx/disconnect.png')) # separator m.addSeparator() # quit m.addAction("Quit", self.quit).setIcon(QtGui.QIcon('gfx/gtk-quit.svg')) # menu options m=mBar.addMenu("Options") m.setTearOffEnabled(True) # settings m.addAction("Settings", self.showWinSettings).setIcon(QtGui.QIcon('gfx/gtk-preferences.svg')) # menu layout self.mLayout=mBar.addMenu("Layout") self.mLayout.setTearOffEnabled(True) # create a toolbar for the main menu menu_toolbar = QtGui.QToolBar() menu_toolbar.addWidget(mBar) self.addToolBar(QtCore.Qt.TopToolBarArea, menu_toolbar) showWinSettings = False # are there new plugins? for k, entry in plugins.listPlugins().iteritems(): # load the plugin plugin=plugins.loadPlugin(entry[plugins.PLUGIN_CLASS], self) if plugin: if self.settings.value(plugin.getName() + '/load') == None: showWinSettings = True if self.settings.value(plugin.getName() + '/load', QVariant(True)).toBool(): # load new plugins by default try: plugin.load() except Exception, e: plugins.setPluginMessage(plugin.getName(), "Exception while loading %s: %s"%(plugin.getName(), str(e))) showWinSettings=True self.updateLayoutMenu() self.setDockOptions(QtGui.QMainWindow.AllowNestedDocks \ |QtGui.QMainWindow.AllowTabbedDocks \ |QtGui.QMainWindow.VerticalTabs) self.setDockNestingEnabled(True) self.restoreGeometry(self.settings.value('geometry').toByteArray()) self.restoreLayout() " add event handlers" self.monty.add_listener('onReady', self.onReady) self.monty.add_listener('onConnect', self.onConnect) self.monty.add_listener('onDisconnect', self.onDisconnect) self.monty.add_listener('onUpdateDBStart', self.onUpdateDBStart) self.monty.add_listener('onUpdateDBFinish', self.onUpdateDBFinish) self.monty.add_listener('onSongChange', self.on_song_change) self.monty.add_listener('onStateChange', self.update_state_messages) self.monty.add_listener('onTimeChange', self.on_time_change) self.enableAll(True) self.setWindowIcon(QtGui.QIcon(appIcon)) # set icon in system tray self.wConnect.monitor() self.update_state_messages() self.show() if showWinSettings: self.showWinSettings() doEvents def quit(self): # unload all plugins for entry in plugins.listPlugins().values(): p=entry[plugins.PLUGIN_INSTANCE] if p and p.isLoaded(): p.unload() self.settings.setValue('geometry', QVariant(self.saveGeometry())) self.settings.sync() QtCore.QCoreApplication.exit() def updateLayoutMenu(self): self.mLayout.clear() self.mLayout.addAction('Save layout', self.saveLayout) self.mLayout.addAction('Restore layout', self.restoreLayout) self.mLayout.addSeparator() # create checkable menu a=QtGui.QAction('Show titlebars', self) a.setCheckable(True) a.setChecked(self.settings.value('show_titlebars', QVariant(True)).toBool()) self.toggleTitleBars(a.isChecked()) self.connect(a, QtCore.SIGNAL('toggled(bool)'), self.toggleTitleBars) self.mLayout.addAction(a) self.mLayout.addSeparator() # can not use iterators, as that gives some creepy error 'bout c++ actions=self.createPopupMenu().actions() for i in xrange(len(actions)): self.mLayout.addAction(actions[i]) def toggleTitleBars(self, val): if val: self.settings.setValue('show_titlebars', QVariant(True)) else: self.settings.setValue('show_titlebars', QVariant(False)) for dock in self.docks: if val: dock.setTitleBarWidget(None) else: dock.setTitleBarWidget(QtGui.QWidget()) def addDock(self, dock): if dock: self.docks.append(dock) self.addDockWidget(QtCore.Qt.TopDockWidgetArea, dock) self.updateLayoutMenu() def removeDock(self, dock): if dock: if dock in self.docks: self.docks.remove(dock) self.removeDockWidget(dock) self.updateLayoutMenu() mMenuVisible=None def createPopupMenu(self): ret=QtGui.QMenu('Test', self) if self.mMenuVisible==None: # create checkable menu a=QtGui.QAction('Menubar', self) a.setCheckable(True) a.setChecked(True) self.connect(a, QtCore.SIGNAL('toggled(bool)'), self.switchMenubar) self.mMenuVisible=a ret.addAction(self.mMenuVisible) ret.addSeparator() menu = QtGui.QMainWindow.createPopupMenu(self) if menu: actions = menu.actions() for i in xrange(len(actions)-1): ret.addAction(actions[i]) return ret def switchMenubar(self, val): self.menuBar().setVisible(val) def setStatus(self, status): """Set the text of the statusbar.""" self.statusBar().showMessage(status, 5000) logging.info(status) def saveLayout(self): self.settings.setValue('layout', QVariant(self.saveState())) def restoreLayout(self): layout = self.settings.value('layout').toByteArray() if not layout: try: layout = open(DEFAULT_LAYOUT_FILE, 'rb').read() except IOError: logging.error("Error reading default layout.") return self.restoreState(layout) def showWinSettings(self): if not self.wSettings: self.wSettings=winSettings(self) self.wSettings.show() self.wSettings.raise_() def onReady(self, params): self.initialiseData() def onConnect(self, params): logging.info("Connected to MPD") self.setStatus('Restoring library and playlist ...') self.mDisconnect.setEnabled(True) self.mConnect.setEnabled(False) doEvents def enableAll(self, value): for k,entry in plugins.listPlugins().iteritems(): try: plugin=entry[plugins.PLUGIN_INSTANCE] plugin.o.setEnabled(value) except: pass def initialiseData(self): self.enableAll(True) self.setStatus("") doEvents def onDisconnect(self, params): logging.info("Disconnected from MPD") self.mDisconnect.setEnabled(False) self.mConnect.setEnabled(True) self.enableAll(False) self.setStatus("You are disconnected. Choose File->Connect to reconnect!") def onUpdateDBFinish(self, params): self.setStatus('') def onUpdateDBStart(self, params): self.setStatus('Updating the database. Please wait ...') def update_state_messages(self, params = None): song = self.monty.getCurrentSong() if song and self.monty.isPlaying(): self.setWindowTitle(song.getTitle() + " by " + song.getArtist()) self.statuslabel.setText("Now playing " + song.getTitle() + " by " + song.getArtist() + " on " + song.getAlbum()) else: self.setWindowTitle(APPNAME) self.statuslabel.setText("") def on_time_slider_change(self): self.monty.seek(self.time_slider.value()) def on_song_change(self, params): status = self.monty.getStatus() self.time_slider.setMaximum(status['length']) self.time_slider.setEnabled(True) self.time_label.duration = sec2min(status['length']) self.update_state_messages(params) def on_time_change(self, params): if not self.time_slider.isSliderDown(): self.time_slider.setValue(params['newTime']) self.time_label.setText(sec2min(params['newTime']) + '/' + self.time_label.duration)