1 from PyQt4
import QtGui
, QtCore
2 from PyQt4
.QtCore
import QVariant
5 from misc
import ORGNAME
, APPNAME
, sec2min
, appIcon
6 from mpclient
import MPClient
7 from winConnect
import winConnect
8 from winSettings
import winSettings
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"""
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
)
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
56 m
= mBar
.addMenu("File")
57 m
.setTearOffEnabled(True)
59 self
.mConnect
=m
.addAction('Connect ...', self
.wConnect
.monitor
)
60 self
.mConnect
.setIcon(QtGui
.QIcon(appIcon
))
62 self
.mDisconnect
=m
.addAction('Disconnect', self
.mpclient
.disconnect_mpd
)
63 self
.mDisconnect
.setIcon(QtGui
.QIcon('gfx/disconnect.png'))
67 m
.addAction("Quit", self
.quit
).setIcon(QtGui
.QIcon('gfx/gtk-quit.svg'))
70 m
=mBar
.addMenu("Options")
71 m
.setTearOffEnabled(True)
73 m
.addAction("Settings", self
.showWinSettings
).setIcon(QtGui
.QIcon('gfx/gtk-preferences.svg'))
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())
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
)
110 self
.setWindowIcon(QtGui
.QIcon(appIcon
))
111 # set icon in system tray
112 self
.wConnect
.monitor()
114 self
.update_state_messages()
117 self
.showWinSettings()
121 for plugin
in self
.plugins
.loaded_plugins():
124 self
.settings
.setValue('geometry', QVariant(self
.saveGeometry()))
126 QtCore
.QCoreApplication
.exit()
128 def updateLayoutMenu(self
):
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
)
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
):
148 self
.settings
.setValue('show_titlebars', QVariant(True))
150 self
.settings
.setValue('show_titlebars', QVariant(False))
151 for dock
in self
.docks
:
153 dock
.setTitleBarWidget(None)
155 dock
.setTitleBarWidget(QtGui
.QWidget())
156 def addDock(self
, dock
):
158 self
.docks
.append(dock
)
159 self
.addDockWidget(QtCore
.Qt
.TopDockWidgetArea
, dock
)
160 self
.updateLayoutMenu()
161 def removeDock(self
, dock
):
163 if dock
in self
.docks
:
164 self
.docks
.remove(dock
)
165 self
.removeDockWidget(dock
)
166 self
.updateLayoutMenu()
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
)
176 self
.connect(a
, QtCore
.SIGNAL('toggled(bool)'), self
.switchMenubar
)
179 ret
.addAction(self
.mMenuVisible
)
181 menu
= QtGui
.QMainWindow
.createPopupMenu(self
)
183 actions
= menu
.actions()
184 for i
in xrange(len(actions
)-1):
185 ret
.addAction(actions
[i
])
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)
194 def saveLayout(self
):
195 self
.settings
.setValue('layout', QVariant(self
.saveState()))
196 def restoreLayout(self
):
197 layout
= self
.settings
.value('layout').toByteArray()
200 layout
= open(DEFAULT_LAYOUT_FILE
, 'rb').read()
202 logging
.error("Error reading default layout.")
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():
220 plugin
.o
.setEnabled(value
)
224 def initialiseData(self
):
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
):
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()))
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
267 ret
= ret
.replace('$musicdir', self
.settings
.value('MPD/music_dir').toString())