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"""
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
)
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
62 m
= mBar
.addMenu("File")
63 m
.setTearOffEnabled(True)
65 self
.mConnect
=m
.addAction('Connect ...', self
.wConnect
.monitor
)
66 self
.mConnect
.setIcon(QtGui
.QIcon(appIcon
))
68 self
.mDisconnect
=m
.addAction('Disconnect', self
.mpclient
.disconnect
)
69 self
.mDisconnect
.setIcon(QtGui
.QIcon('gfx/disconnect.png'))
73 m
.addAction("Quit", self
.quit
).setIcon(QtGui
.QIcon('gfx/gtk-quit.svg'))
76 m
=mBar
.addMenu("Options")
77 m
.setTearOffEnabled(True)
79 m
.addAction("Settings", self
.showWinSettings
).setIcon(QtGui
.QIcon('gfx/gtk-preferences.svg'))
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())
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
)
117 self
.setWindowIcon(QtGui
.QIcon(appIcon
))
118 # set icon in system tray
119 self
.wConnect
.monitor()
121 self
.update_state_messages()
124 self
.showWinSettings()
129 for plugin
in self
.plugins
.loaded_plugins():
132 self
.settings
.setValue('geometry', QVariant(self
.saveGeometry()))
134 QtCore
.QCoreApplication
.exit()
137 def updateLayoutMenu(self
):
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
)
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
):
157 self
.settings
.setValue('show_titlebars', QVariant(True))
159 self
.settings
.setValue('show_titlebars', QVariant(False))
160 for dock
in self
.docks
:
162 dock
.setTitleBarWidget(None)
164 dock
.setTitleBarWidget(QtGui
.QWidget())
165 def addDock(self
, dock
):
167 self
.docks
.append(dock
)
168 self
.addDockWidget(QtCore
.Qt
.TopDockWidgetArea
, dock
)
169 self
.updateLayoutMenu()
170 def removeDock(self
, dock
):
172 if dock
in self
.docks
:
173 self
.docks
.remove(dock
)
174 self
.removeDockWidget(dock
)
175 self
.updateLayoutMenu()
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
)
185 self
.connect(a
, QtCore
.SIGNAL('toggled(bool)'), self
.switchMenubar
)
188 ret
.addAction(self
.mMenuVisible
)
190 menu
= QtGui
.QMainWindow
.createPopupMenu(self
)
192 actions
= menu
.actions()
193 for i
in xrange(len(actions
)-1):
194 ret
.addAction(actions
[i
])
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)
203 def saveLayout(self
):
204 self
.settings
.setValue('layout', QVariant(self
.saveState()))
205 def restoreLayout(self
):
206 layout
= self
.settings
.value('layout').toByteArray()
209 layout
= open(DEFAULT_LAYOUT_FILE
, 'rb').read()
211 logging
.error("Error reading default layout.")
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)
231 def enableAll(self
, value
):
232 for plugin
in self
.plugins
.loaded_plugins():
234 plugin
.o
.setEnabled(value
)
238 def initialiseData(self
):
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
):
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()))
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
282 ret
= ret
.replace('$musicdir', self
.settings
.value('MPD/music_dir').toString())