winMain: alway show APPNAME in window title.
[nephilim.git] / nephilim / winMain.py
blob6ba35959557379fa7503cea518b9971cd9d7a16e
1 from PyQt4 import QtGui, QtCore
2 from PyQt4.QtCore import QVariant
3 from traceback import print_exc
5 from misc import *
6 from mpclient import MPClient
8 import plugins
10 from winConnect import winConnect
11 from winSettings import winSettings
12 import logging
14 DEFAULT_LAYOUT_FILE = 'default_layout'
16 class winMain(QtGui.QMainWindow):
17 """The winMain class is mpc's main window, showing the playlists and control-interface"""
18 docks=[]
20 " menus"
21 mConnect=None
22 mDisconnect=None
23 mLayout=None
25 " connection window"
26 wConnect=None
27 wSettings=None
29 " MPD object"
30 mpclient = None
31 " plugins interface"
32 plugins = None
34 " Statusbar objects"
35 statuslabel = None
36 time_slider = None
37 time_label = None
39 settings = None
40 def __init__(self, parent=None):
41 QtGui.QWidget.__init__(self, parent)
42 self.settings = QtCore.QSettings(ORGNAME, APPNAME)
43 self.mpclient = MPClient()
45 self.wConnect=winConnect(self)
47 # statusbar
48 self.statusBar()
49 self.statuslabel = QtGui.QLabel()
50 self.time_slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
51 self.time_slider.setMaximumWidth(self.width()/4)
52 self.connect(self.time_slider, QtCore.SIGNAL('sliderReleased()'), self.on_time_slider_change)
53 self.time_label = QtGui.QLabel()
54 self.time_label.duration = '0:00'
56 self.statusBar().addWidget(self.statuslabel)
57 self.statusBar().addPermanentWidget(self.time_label)
58 self.statusBar().addPermanentWidget(self.time_slider)
60 mBar = QtGui.QMenuBar() # create a menubar
61 # File menu
62 m = mBar.addMenu("File")
63 m.setTearOffEnabled(True)
64 # connect
65 self.mConnect=m.addAction('Connect ...', self.wConnect.monitor)
66 self.mConnect.setIcon(QtGui.QIcon(appIcon))
67 # disconnect
68 self.mDisconnect=m.addAction('Disconnect', self.mpclient.disconnect)
69 self.mDisconnect.setIcon(QtGui.QIcon('gfx/disconnect.png'))
70 # separator
71 m.addSeparator()
72 # quit
73 m.addAction("Quit", self.quit).setIcon(QtGui.QIcon('gfx/gtk-quit.svg'))
75 # menu options
76 m=mBar.addMenu("Options")
77 m.setTearOffEnabled(True)
78 # settings
79 m.addAction("Settings", self.showWinSettings).setIcon(QtGui.QIcon('gfx/gtk-preferences.svg'))
81 # menu layout
82 self.mLayout=mBar.addMenu("Layout")
83 self.mLayout.setTearOffEnabled(True)
85 # create a toolbar for the main menu
86 menu_toolbar = QtGui.QToolBar('Main menu', self)
87 menu_toolbar.addWidget(mBar)
88 self.addToolBar(QtCore.Qt.TopToolBarArea, menu_toolbar)
90 showWinSettings = False # are there new plugins?
91 self.plugins = plugins.Plugins(self)
92 for plugin in self.plugins.plugins():
93 if self.settings.value(plugin.getName() + '/load') == None:
94 showWinSettings = True
95 if self.settings.value(plugin.getName() + '/load', QVariant(True)).toBool():
96 self.plugins.load(plugin.getName())
98 self.updateLayoutMenu()
99 self.setDockOptions(QtGui.QMainWindow.AllowNestedDocks \
100 |QtGui.QMainWindow.AllowTabbedDocks \
101 |QtGui.QMainWindow.VerticalTabs)
102 self.setDockNestingEnabled(True)
103 self.restoreGeometry(self.settings.value('geometry').toByteArray())
104 self.restoreLayout()
106 " add event handlers"
107 self.mpclient.add_listener('onReady', self.onReady)
108 self.mpclient.add_listener('onConnect', self.onConnect)
109 self.mpclient.add_listener('onDisconnect', self.onDisconnect)
110 self.mpclient.add_listener('onUpdateDBStart', self.onUpdateDBStart)
111 self.mpclient.add_listener('onUpdateDBFinish', self.onUpdateDBFinish)
112 self.mpclient.add_listener('onSongChange', self.on_song_change)
113 self.mpclient.add_listener('onStateChange', self.update_state_messages)
114 self.mpclient.add_listener('onTimeChange', self.on_time_change)
116 self.enableAll(True)
117 self.setWindowIcon(QtGui.QIcon(appIcon))
118 # set icon in system tray
119 self.wConnect.monitor()
121 self.update_state_messages()
122 self.show()
123 if showWinSettings:
124 self.showWinSettings()
125 doEvents
127 def quit(self):
128 # unload all plugins
129 for plugin in self.plugins.loaded_plugins():
130 plugin.unload()
132 self.settings.setValue('geometry', QVariant(self.saveGeometry()))
133 self.settings.sync()
134 QtCore.QCoreApplication.exit()
137 def updateLayoutMenu(self):
138 self.mLayout.clear()
139 self.mLayout.addAction('Save layout', self.saveLayout)
140 self.mLayout.addAction('Restore layout', self.restoreLayout)
141 self.mLayout.addSeparator()
142 # create checkable menu
143 a=QtGui.QAction('Show titlebars', self)
144 a.setCheckable(True)
145 a.setChecked(self.settings.value('show_titlebars', QVariant(True)).toBool())
146 self.toggleTitleBars(a.isChecked())
147 self.connect(a, QtCore.SIGNAL('toggled(bool)'), self.toggleTitleBars)
148 self.mLayout.addAction(a)
149 self.mLayout.addSeparator()
150 # can not use iterators, as that gives some creepy error 'bout c++
151 actions=self.createPopupMenu().actions()
152 for i in xrange(len(actions)):
153 self.mLayout.addAction(actions[i])
155 def toggleTitleBars(self, val):
156 if val:
157 self.settings.setValue('show_titlebars', QVariant(True))
158 else:
159 self.settings.setValue('show_titlebars', QVariant(False))
160 for dock in self.docks:
161 if val:
162 dock.setTitleBarWidget(None)
163 else:
164 dock.setTitleBarWidget(QtGui.QWidget())
165 def addDock(self, dock):
166 if dock:
167 self.docks.append(dock)
168 self.addDockWidget(QtCore.Qt.TopDockWidgetArea, dock)
169 self.updateLayoutMenu()
170 def removeDock(self, dock):
171 if dock:
172 if dock in self.docks:
173 self.docks.remove(dock)
174 self.removeDockWidget(dock)
175 self.updateLayoutMenu()
177 mMenuVisible=None
178 def createPopupMenu(self):
179 ret=QtGui.QMenu('Test', self)
180 if self.mMenuVisible==None:
181 # create checkable menu
182 a=QtGui.QAction('Menubar', self)
183 a.setCheckable(True)
184 a.setChecked(True)
185 self.connect(a, QtCore.SIGNAL('toggled(bool)'), self.switchMenubar)
187 self.mMenuVisible=a
188 ret.addAction(self.mMenuVisible)
189 ret.addSeparator()
190 menu = QtGui.QMainWindow.createPopupMenu(self)
191 if menu:
192 actions = menu.actions()
193 for i in xrange(len(actions)-1):
194 ret.addAction(actions[i])
195 return ret
196 def switchMenubar(self, val):
197 self.menuBar().setVisible(val)
198 def setStatus(self, status):
199 """Set the text of the statusbar."""
200 self.statusBar().showMessage(status, 5000)
201 logging.info(status)
203 def saveLayout(self):
204 self.settings.setValue('layout', QVariant(self.saveState()))
205 def restoreLayout(self):
206 layout = self.settings.value('layout').toByteArray()
207 if not layout:
208 try:
209 layout = open(DEFAULT_LAYOUT_FILE, 'rb').read()
210 except IOError:
211 logging.error("Error reading default layout.")
212 return
213 self.restoreState(layout)
215 def showWinSettings(self):
216 if not self.wSettings:
217 self.wSettings=winSettings(self)
218 self.wSettings.show()
219 self.wSettings.raise_()
221 def onReady(self, params):
222 self.initialiseData()
224 def onConnect(self, params):
225 logging.info("Connected to MPD")
226 self.setStatus('Restoring library and playlist ...')
227 self.mDisconnect.setEnabled(True)
228 self.mConnect.setEnabled(False)
229 doEvents
231 def enableAll(self, value):
232 for plugin in self.plugins.loaded_plugins():
233 try:
234 plugin.o.setEnabled(value)
235 except:
236 pass
238 def initialiseData(self):
239 self.enableAll(True)
240 self.setStatus("")
241 doEvents
243 def onDisconnect(self, params):
244 logging.info("Disconnected from MPD")
245 self.mDisconnect.setEnabled(False)
246 self.mConnect.setEnabled(True)
247 self.enableAll(False)
248 self.setStatus("You are disconnected. Choose File->Connect to reconnect!")
250 def onUpdateDBFinish(self, params):
251 self.setStatus('')
252 def onUpdateDBStart(self, params):
253 self.setStatus('Updating the database. Please wait ...')
255 def update_state_messages(self, params = None):
256 song = self.mpclient.getCurrentSong()
257 if song and self.mpclient.isPlaying():
258 self.setWindowTitle('%s by %s - %s'%(song.getTitle(), song.getArtist(), APPNAME))
259 self.statuslabel.setText('Now playing %s by %s on %s'%(song.getTitle(), song.getArtist(),song.getAlbum()))
260 else:
261 self.setWindowTitle(APPNAME)
262 self.statuslabel.setText('')
264 def on_time_slider_change(self):
265 self.mpclient.seek(self.time_slider.value())
267 def on_song_change(self, params):
268 status = self.mpclient.getStatus()
269 self.time_slider.setMaximum(status['length'])
270 self.time_slider.setEnabled(True)
271 self.time_label.duration = sec2min(status['length'])
272 self.update_state_messages(params)
274 def on_time_change(self, params):
275 if not self.time_slider.isSliderDown():
276 self.time_slider.setValue(params['newTime'])
277 self.time_label.setText(sec2min(params['newTime']) + '/' + self.time_label.duration)
279 def expand_tags(self, str):
280 # maybe there's a better place for this function
281 ret = str
282 ret = ret.replace('$musicdir', self.settings.value('MPD/music_dir').toString())
283 return ret