# # Copyright (C) 2008 jerous # Copyright (C) 2009 Anton Khirnov # # Nephilim is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Nephilim is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Nephilim. If not, see . # from PyQt4 import QtGui, QtCore from PyQt4.QtCore import QVariant import logging from misc import APPNAME, sec2min, appIcon from mpclient import MPClient from connect_wg import ConnectWidget 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 wConnect = None mpclient = None settings = None # Statusbar objects statuslabel = None time_slider = None time_label = None def __init__(self): QtGui.QWidget.__init__(self) self.settings = QtCore.QSettings() self.mpclient = QtGui.QApplication.instance().mpclient self.wConnect = ConnectWidget(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.mpclient.disconnect_mpd) self.mDisconnect.setIcon(QtGui.QIcon('gfx/disconnect.png')) # separator m.addSeparator() # quit m.addAction("Quit", QtGui.QApplication.instance().quit).setIcon(QtGui.QIcon('gfx/gtk-quit.svg')) # menu options m=mBar.addMenu("Options") m.setTearOffEnabled(True) # settings m.addAction("Settings", QtGui.QApplication.instance().show_settings_win).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('Main menu', self) menu_toolbar.addWidget(mBar) self.addToolBar(QtCore.Qt.TopToolBarArea, menu_toolbar) self.updateLayoutMenu() self.setDockOptions(QtGui.QMainWindow.AllowNestedDocks \ |QtGui.QMainWindow.AllowTabbedDocks \ |QtGui.QMainWindow.VerticalTabs) self.setDockNestingEnabled(True) self.restoreGeometry(self.settings.value('geometry').toByteArray()) " add event handlers" self.connect(self.mpclient, QtCore.SIGNAL('connected'), self.onConnected) self.connect(self.mpclient, QtCore.SIGNAL('disconnected'), self.onDisconnect) self.connect(self.mpclient, QtCore.SIGNAL('song_changed'), self.on_song_change) self.connect(self.mpclient, QtCore.SIGNAL('state_changed'), self.update_state_messages) self.connect(self.mpclient, QtCore.SIGNAL('time_changed'), self.on_time_change) self.wConnect.monitor() self.update_state_messages() self.show() def on_quit(self): self.settings.setValue('geometry', QVariant(self.saveGeometry())) def updateLayoutMenu(self): self.mLayout.clear() self.mLayout.addAction('Save layout', self.saveLayout) self.mLayout.addAction('Restore layout', self.restore_layout) 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 restore_layout(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 onConnected(self): self.mDisconnect.setEnabled(True) self.mConnect.setEnabled(False) def onDisconnect(self): logging.info("Disconnected from MPD") self.mDisconnect.setEnabled(False) self.mConnect.setEnabled(True) self.setStatus("You are disconnected. Choose File->Connect to reconnect!") def update_state_messages(self): song = self.mpclient.current_song() state = self.mpclient.status()['state'] state = 'playing' if state == 'play' else 'paused' if state == 'pause' else 'stopped' if song: self.setWindowTitle('%s by %s - %s [%s]'%(song.title(), song.artist(), APPNAME, state)) self.statuslabel.setText('%s by %s on %s [%s]'%(song.title(), song.artist(),song.album(), state)) else: self.setWindowTitle(APPNAME) self.statuslabel.setText('') def on_time_slider_change(self): self.mpclient.seek(self.time_slider.value()) def on_song_change(self): status = self.mpclient.status() self.time_slider.setMaximum(status['length']) self.time_slider.setEnabled(True) self.time_label.duration = sec2min(status['length']) self.update_state_messages() def on_time_change(self, new_time): if not self.time_slider.isSliderDown(): self.time_slider.setValue(new_time) self.time_label.setText(sec2min(new_time) + '/' + self.time_label.duration) def expand_tags(self, str): # maybe there's a better place for this function ret = str ret = ret.replace('$musicdir', self.settings.value('MPD/music_dir').toString()) return ret