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