1 from PyQt4
import QtGui
, QtCore
2 from PyQt4
.QtCore
import QVariant
3 from traceback
import print_exc
6 from mpclient
import MPClient
10 from winConnect
import winConnect
11 from winSettings
import winSettings
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"""
38 def __init__(self
, parent
=None):
39 QtGui
.QWidget
.__init
__(self
, parent
)
40 self
.settings
= QtCore
.QSettings(ORGNAME
, APPNAME
)
41 self
.mpclient
= MPClient()
43 self
.wConnect
=winConnect(self
)
47 self
.statuslabel
= QtGui
.QLabel()
48 self
.time_slider
= QtGui
.QSlider(QtCore
.Qt
.Horizontal
, self
)
49 self
.time_slider
.setMaximumWidth(self
.width()/4)
50 self
.connect(self
.time_slider
, QtCore
.SIGNAL('sliderReleased()'), self
.on_time_slider_change
)
51 self
.time_label
= QtGui
.QLabel()
52 self
.time_label
.duration
= '0:00'
54 self
.statusBar().addWidget(self
.statuslabel
)
55 self
.statusBar().addPermanentWidget(self
.time_label
)
56 self
.statusBar().addPermanentWidget(self
.time_slider
)
58 mBar
= QtGui
.QMenuBar() # create a menubar
60 m
= mBar
.addMenu("File")
61 m
.setTearOffEnabled(True)
63 self
.mConnect
=m
.addAction('Connect ...', self
.wConnect
.monitor
)
64 self
.mConnect
.setIcon(QtGui
.QIcon(appIcon
))
66 self
.mDisconnect
=m
.addAction('Disconnect', self
.mpclient
.disconnect
)
67 self
.mDisconnect
.setIcon(QtGui
.QIcon('gfx/disconnect.png'))
71 m
.addAction("Quit", self
.quit
).setIcon(QtGui
.QIcon('gfx/gtk-quit.svg'))
74 m
=mBar
.addMenu("Options")
75 m
.setTearOffEnabled(True)
77 m
.addAction("Settings", self
.showWinSettings
).setIcon(QtGui
.QIcon('gfx/gtk-preferences.svg'))
80 self
.mLayout
=mBar
.addMenu("Layout")
81 self
.mLayout
.setTearOffEnabled(True)
83 # create a toolbar for the main menu
84 menu_toolbar
= QtGui
.QToolBar()
85 menu_toolbar
.addWidget(mBar
)
86 self
.addToolBar(QtCore
.Qt
.TopToolBarArea
, menu_toolbar
)
88 showWinSettings
= False # are there new plugins?
89 for k
, entry
in plugins
.listPlugins().iteritems():
91 plugin
=plugins
.loadPlugin(entry
[plugins
.PLUGIN_CLASS
], self
)
93 if self
.settings
.value(plugin
.getName() + '/load') == None:
94 showWinSettings
= True
95 if self
.settings
.value(plugin
.getName() + '/load', QVariant(True)).toBool():
96 # load new plugins by default
100 plugins
.setPluginMessage(plugin
.getName(), "Exception while loading %s: %s"%(plugin
.getName(), str(e
)))
103 self
.updateLayoutMenu()
104 self
.setDockOptions(QtGui
.QMainWindow
.AllowNestedDocks \
105 |QtGui
.QMainWindow
.AllowTabbedDocks \
106 |QtGui
.QMainWindow
.VerticalTabs
)
107 self
.setDockNestingEnabled(True)
108 self
.restoreGeometry(self
.settings
.value('geometry').toByteArray())
111 " add event handlers"
112 self
.mpclient
.add_listener('onReady', self
.onReady
)
113 self
.mpclient
.add_listener('onConnect', self
.onConnect
)
114 self
.mpclient
.add_listener('onDisconnect', self
.onDisconnect
)
115 self
.mpclient
.add_listener('onUpdateDBStart', self
.onUpdateDBStart
)
116 self
.mpclient
.add_listener('onUpdateDBFinish', self
.onUpdateDBFinish
)
117 self
.mpclient
.add_listener('onSongChange', self
.on_song_change
)
118 self
.mpclient
.add_listener('onStateChange', self
.update_state_messages
)
119 self
.mpclient
.add_listener('onTimeChange', self
.on_time_change
)
122 self
.setWindowIcon(QtGui
.QIcon(appIcon
))
123 # set icon in system tray
124 self
.wConnect
.monitor()
126 self
.update_state_messages()
129 self
.showWinSettings()
134 for entry
in plugins
.listPlugins().values():
135 p
=entry
[plugins
.PLUGIN_INSTANCE
]
136 if p
and p
.isLoaded():
139 self
.settings
.setValue('geometry', QVariant(self
.saveGeometry()))
141 QtCore
.QCoreApplication
.exit()
144 def updateLayoutMenu(self
):
146 self
.mLayout
.addAction('Save layout', self
.saveLayout
)
147 self
.mLayout
.addAction('Restore layout', self
.restoreLayout
)
148 self
.mLayout
.addSeparator()
149 # create checkable menu
150 a
=QtGui
.QAction('Show titlebars', self
)
152 a
.setChecked(self
.settings
.value('show_titlebars', QVariant(True)).toBool())
153 self
.toggleTitleBars(a
.isChecked())
154 self
.connect(a
, QtCore
.SIGNAL('toggled(bool)'), self
.toggleTitleBars
)
155 self
.mLayout
.addAction(a
)
156 self
.mLayout
.addSeparator()
157 # can not use iterators, as that gives some creepy error 'bout c++
158 actions
=self
.createPopupMenu().actions()
159 for i
in xrange(len(actions
)):
160 self
.mLayout
.addAction(actions
[i
])
162 def toggleTitleBars(self
, val
):
164 self
.settings
.setValue('show_titlebars', QVariant(True))
166 self
.settings
.setValue('show_titlebars', QVariant(False))
167 for dock
in self
.docks
:
169 dock
.setTitleBarWidget(None)
171 dock
.setTitleBarWidget(QtGui
.QWidget())
172 def addDock(self
, dock
):
174 self
.docks
.append(dock
)
175 self
.addDockWidget(QtCore
.Qt
.TopDockWidgetArea
, dock
)
176 self
.updateLayoutMenu()
177 def removeDock(self
, dock
):
179 if dock
in self
.docks
:
180 self
.docks
.remove(dock
)
181 self
.removeDockWidget(dock
)
182 self
.updateLayoutMenu()
185 def createPopupMenu(self
):
186 ret
=QtGui
.QMenu('Test', self
)
187 if self
.mMenuVisible
==None:
188 # create checkable menu
189 a
=QtGui
.QAction('Menubar', self
)
192 self
.connect(a
, QtCore
.SIGNAL('toggled(bool)'), self
.switchMenubar
)
195 ret
.addAction(self
.mMenuVisible
)
197 menu
= QtGui
.QMainWindow
.createPopupMenu(self
)
199 actions
= menu
.actions()
200 for i
in xrange(len(actions
)-1):
201 ret
.addAction(actions
[i
])
203 def switchMenubar(self
, val
):
204 self
.menuBar().setVisible(val
)
205 def setStatus(self
, status
):
206 """Set the text of the statusbar."""
207 self
.statusBar().showMessage(status
, 5000)
210 def saveLayout(self
):
211 self
.settings
.setValue('layout', QVariant(self
.saveState()))
212 def restoreLayout(self
):
213 layout
= self
.settings
.value('layout').toByteArray()
216 layout
= open(DEFAULT_LAYOUT_FILE
, 'rb').read()
218 logging
.error("Error reading default layout.")
220 self
.restoreState(layout
)
222 def showWinSettings(self
):
223 if not self
.wSettings
:
224 self
.wSettings
=winSettings(self
)
225 self
.wSettings
.show()
226 self
.wSettings
.raise_()
228 def onReady(self
, params
):
229 self
.initialiseData()
231 def onConnect(self
, params
):
232 logging
.info("Connected to MPD")
233 self
.setStatus('Restoring library and playlist ...')
234 self
.mDisconnect
.setEnabled(True)
235 self
.mConnect
.setEnabled(False)
238 def enableAll(self
, value
):
239 for k
,entry
in plugins
.listPlugins().iteritems():
241 plugin
=entry
[plugins
.PLUGIN_INSTANCE
]
242 plugin
.o
.setEnabled(value
)
246 def initialiseData(self
):
251 def onDisconnect(self
, params
):
252 logging
.info("Disconnected from MPD")
253 self
.mDisconnect
.setEnabled(False)
254 self
.mConnect
.setEnabled(True)
255 self
.enableAll(False)
256 self
.setStatus("You are disconnected. Choose File->Connect to reconnect!")
258 def onUpdateDBFinish(self
, params
):
260 def onUpdateDBStart(self
, params
):
261 self
.setStatus('Updating the database. Please wait ...')
263 def update_state_messages(self
, params
= None):
264 song
= self
.mpclient
.getCurrentSong()
265 if song
and self
.mpclient
.isPlaying():
266 self
.setWindowTitle(song
.getTitle() + " by " + song
.getArtist())
267 self
.statuslabel
.setText("Now playing " + song
.getTitle() + " by " + song
.getArtist() + " on " + song
.getAlbum())
269 self
.setWindowTitle(APPNAME
)
270 self
.statuslabel
.setText("")
272 def on_time_slider_change(self
):
273 self
.mpclient
.seek(self
.time_slider
.value())
275 def on_song_change(self
, params
):
276 status
= self
.mpclient
.getStatus()
277 self
.time_slider
.setMaximum(status
['length'])
278 self
.time_slider
.setEnabled(True)
279 self
.time_label
.duration
= sec2min(status
['length'])
280 self
.update_state_messages(params
)
282 def on_time_change(self
, params
):
283 if not self
.time_slider
.isSliderDown():
284 self
.time_slider
.setValue(params
['newTime'])
285 self
.time_label
.setText(sec2min(params
['newTime']) + '/' + self
.time_label
.duration
)