1 # -*- coding: utf-8 -*-
3 # This file is part of Panucci.
4 # Copyright (c) 2008-2011 The Panucci Project
6 # Panucci is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # Panucci is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with Panucci. If not, see <http://www.gnu.org/licenses/>.
24 from PySide
import QtCore
25 from PySide
import QtGui
26 from PySide
import QtDeclarative
29 from panucci
import util
30 from panucci
import platform
31 from panucci
import playlist
32 from panucci
.dbusinterface
import interface
33 from panucci
.services
import ObservableService
35 class PanucciGUI(QtCore
.QObject
, ObservableService
):
36 def __init__(self
, settings
, filename
=None):
37 self
.__log
= logging
.getLogger('panucci.panucci.PanucciGUI')
38 QtCore
.QObject
.__init
__(self
)
39 ObservableService
.__init
__(self
, [], self
.__log
)
40 self
.config
= settings
.config
41 interface
.register_gui(self
)
42 self
.playlist
= playlist
.Playlist(self
.config
)
43 self
.time_str
= "00:00 / 00:00"
44 self
.progress_fraction
= 0
47 self
.app
= QtGui
.QApplication(["Panucci"])
48 self
.app
.setWindowIcon(QtGui
.QIcon(util
.find_data_file('panucci.png')))
49 self
.view
= QtDeclarative
.QDeclarativeView()
50 self
.view
.closeEvent
= self
.close_main_window_callback
51 self
.context
= self
.view
.rootContext()
52 self
.context
.setContextProperty('main', self
)
53 self
.context
.setContextProperty('config', self
.make_config())
54 self
.theme_controller
= ThemeController(self
.config
)
55 self
.context
.setContextProperty('themeController', self
.theme_controller
)
56 self
.context
.setContextProperty('themes', self
.theme_controller
.get_themes())
58 engine
= self
.context
.engine()
59 self
.image_provider
= ImageProvider(self
)
60 engine
.addImageProvider("cover", self
.image_provider
)
61 self
.view
.setSource(util
.find_data_file("main.qml"))
63 self
.playlist
.register( 'stopped', self
.on_player_stopped
)
64 self
.playlist
.register( 'playing', self
.on_player_playing
)
65 self
.playlist
.register( 'paused', self
.on_player_paused
)
66 self
.playlist
.register( 'end-of-playlist', self
.on_player_end_of_playlist
)
67 self
.playlist
.register( 'new-track-loaded', self
.on_player_new_track
)
68 self
.playlist
.register( 'new-metadata-available', self
.on_player_new_metadata
)
69 self
.playlist
.register( 'reset-playlist', self
.on_player_reset_playlist
)
71 self
.playlist
.init(filepath
=filename
)
72 self
.view
.rootObject().start_scrolling_timer(self
.config
.getboolean("options", "scrolling_labels"))
73 self
.timer
= QtCore
.QTimer()
74 self
.timer
.setInterval(1000)
75 self
.timer
.timeout
.connect(self
.timer_callback
)
76 self
.view
.setAttribute(QtCore
.Qt
.WA_LockLandscapeOrientation
)
78 self
.view
.showFullScreen()
84 # Enable play/pause with headset button
85 #interface.headset_device.connect_to_signal('Condition', \
86 # self.handle_headset_button)
88 system_bus
= dbus
.SystemBus()
89 if platform
.HARMATTAN
:
90 self
.headset_path
= '/org/freedesktop/Hal/devices/computer_logicaldev_input_0'
91 elif platform
.FREMANTLE
:
92 self
.headset_path
= '/org/freedesktop/Hal/devices/computer_logicaldev_input_1'
93 # Monitor connection state of BT headset
94 system_bus
.add_signal_receiver(self
.handle_connection_state
, 'DeviceAdded', \
95 'org.freedesktop.Hal.Manager', None, '/org/freedesktop/Hal/Manager')
97 # Monitor BT headset buttons
98 system_bus
.add_signal_receiver(self
.handle_bt_button
, 'Condition', \
99 'org.freedesktop.Hal.Device', None, self
.headset_path
)
103 def create_actions(self
):
105 self
.action_add_media
= QtGui
.QAction(QtGui
.QIcon(''), _("Add Media").decode("utf-8"), self
.view
, shortcut
="Ctrl+A",
106 statusTip
="Add file to playlist", triggered
=self
.add_media_callback
)
107 self
.context
.setContextProperty('action_add_media', self
.action_add_media
)
108 self
.action_play_one
= QtGui
.QAction(QtGui
.QIcon(''), _("Play One").decode("utf-8"), self
.view
, shortcut
="Ctrl+O",
109 statusTip
="Play one file", triggered
=self
.play_one_callback
)
110 self
.context
.setContextProperty('action_play_one', self
.action_play_one
)
111 self
.action_save_playlist
= QtGui
.QAction(QtGui
.QIcon(':/images/save.png'), _("Save Playlist").decode("utf-8"), self
.view
,
112 shortcut
="Ctrl+W", statusTip
="Save current playlist as m3u", triggered
=self
.save_playlist_callback
)
113 self
.context
.setContextProperty('action_save_playlist', self
.action_save_playlist
)
114 self
.action_clear_playlist
= QtGui
.QAction(QtGui
.QIcon(':/images/trashcan.png'), _("Clear Playlist").decode("utf-8"), self
.view
,
115 shortcut
="Ctrl+H", statusTip
="Clear current playlist", triggered
=self
.clear_playlist_callback
)
116 self
.context
.setContextProperty('action_clear_playlist', self
.action_clear_playlist
)
117 self
.action_delete_bookmarks
= QtGui
.QAction(QtGui
.QIcon(':/images/trashcan.png'), _("Delete All Bookmarks").decode("utf-8"), self
.view
,
118 shortcut
="Ctrl+K", statusTip
="Delete all bookmarks", triggered
=self
.delete_bookmarks_callback
)
119 self
.context
.setContextProperty('action_delete_bookmarks', self
.action_delete_bookmarks
)
120 self
.action_quit
= QtGui
.QAction(QtGui
.QIcon('/usr/share/icons/gnome/16x16/actions/exit.png'), "Quit", self
.view
, shortcut
="Ctrl+Q",
121 statusTip
="Exit the application", triggered
=self
.quit_panucci
)
122 self
.context
.setContextProperty('action_quit', self
.action_quit
)
124 self
.action_playlist
= QtGui
.QAction(_("Playlist").decode("utf-8"), self
.view
, shortcut
="Ctrl+P",
125 statusTip
=_("Open playlist"), triggered
=self
.playlist_callback
)
126 self
.context
.setContextProperty('action_playlist', self
.action_playlist
)
127 self
.action_settings
= QtGui
.QAction(_("Settings").decode("utf-8"), self
.view
, shortcut
="Ctrl+C",
128 statusTip
=_("Open settings dialog"), triggered
=self
.settings_callback
)
129 self
.context
.setContextProperty('action_settings', self
.action_settings
)
130 self
.action_timer
= QtGui
.QAction(_("Sleep Timer").decode("utf-8"), self
.view
, shortcut
="Ctrl+T",
131 statusTip
=_("Start a timed shutdown"), triggered
=self
.sleep_timer_callback
)
132 self
.context
.setContextProperty('action_timer', self
.action_timer
)
133 self
.shutdown_str
= _("Shutdown time in minutes").decode("utf-8")
134 self
.context
.setContextProperty('shutdown_str', self
.shutdown_str
)
135 self
.action_volume_control
= QtGui
.QAction(_("Volume Control").decode("utf-8"), self
.view
, shortcut
="Ctrl+V",
136 statusTip
=_(""), triggered
=self
.volume_control_callback
)
137 self
.context
.setContextProperty('action_volume_control', self
.action_volume_control
)
138 self
.volume_level_str
= _('Volume level in percent').decode("utf-8")
139 self
.context
.setContextProperty('volume_level_str', self
.volume_level_str
)
141 self
.main_window_str
= _("Main Window").decode("utf-8")
142 self
.context
.setContextProperty('main_window_str', self
.main_window_str
)
143 self
.action_lock_progress
= QtGui
.QAction(_("Lock Progress Bar").decode("utf-8"), self
.view
, shortcut
="Ctrl+L",
144 statusTip
="Lock progress bar", triggered
=self
.lock_progress_callback
)
145 self
.action_lock_progress
.setCheckable(True)
146 self
.action_lock_progress
.setChecked(self
.config
.getboolean("options", "lock_progress"))
147 self
.context
.setContextProperty('action_lock_progress', self
.action_lock_progress
)
148 self
.action_dual_action
= QtGui
.QAction(_("Dual Action Button").decode("utf-8"), self
.view
, shortcut
="Ctrl+B",
149 statusTip
="Set dual action button", triggered
=self
.dual_action_callback
)
150 self
.action_dual_action
.setCheckable(True)
151 self
.action_dual_action
.setChecked(self
.config
.getboolean("options", "dual_action_button"))
152 self
.context
.setContextProperty('action_dual_action', self
.action_dual_action
)
153 self
.action_scrolling_labels
= QtGui
.QAction(_("Scrolling Labels").decode("utf-8"), self
.view
, shortcut
="Ctrl+D",
154 statusTip
="Scroll title labels when too long", triggered
=self
.scrolling_labels_callback
)
155 self
.action_scrolling_labels
.setCheckable(True)
156 self
.action_scrolling_labels
.setChecked(self
.config
.getboolean("options", "scrolling_labels"))
157 self
.context
.setContextProperty('action_scrolling_labels', self
.action_scrolling_labels
)
158 self
.playback_str
= _("Playback").decode("utf-8")
159 self
.context
.setContextProperty('playback_str', self
.playback_str
)
160 self
.action_stay_at_end
= QtGui
.QAction(_("Stay at End").decode("utf-8"), self
.view
, shortcut
="Ctrl+E",
161 statusTip
="Stay at file end", triggered
=self
.stay_at_end_callback
)
162 self
.action_stay_at_end
.setCheckable(True)
163 self
.action_stay_at_end
.setChecked(self
.config
.getboolean("options", "stay_at_end"))
164 self
.context
.setContextProperty('action_stay_at_end', self
.action_stay_at_end
)
165 self
.action_seek_back
= QtGui
.QAction(_("Seek Back").decode("utf-8"), self
.view
, shortcut
="Ctrl+S",
166 statusTip
="Seek back to previous file", triggered
=self
.seek_back_callback
)
167 self
.action_seek_back
.setCheckable(True)
168 self
.action_seek_back
.setChecked(self
.config
.getboolean("options", "seek_back"))
169 self
.context
.setContextProperty('action_seek_back', self
.action_seek_back
)
170 self
.action_resume_all
= QtGui
.QAction(_("Resume All").decode("utf-8"), self
.view
, shortcut
="Ctrl+R",
171 statusTip
="Resume all files automatically", triggered
=self
.resume_all_callback
)
172 self
.action_resume_all
.setCheckable(True)
173 self
.action_resume_all
.setChecked(self
.config
.getboolean("options", "resume_all"))
174 self
.context
.setContextProperty('action_resume_all', self
.action_resume_all
)
175 self
.play_mode_str
= _("Play Mode").decode("utf-8")
176 self
.context
.setContextProperty('play_mode_str', self
.play_mode_str
)
177 self
.action_play_mode_all
= QtGui
.QAction(_("All").decode("utf-8"), self
.view
, statusTip
="Set play mode",
178 triggered
=self
.play_mode_all_callback
)
179 self
.action_play_mode_all
.setCheckable(True)
180 self
.context
.setContextProperty('action_play_mode_all', self
.action_play_mode_all
)
181 self
.action_play_mode_single
= QtGui
.QAction(_("Single").decode("utf-8"), self
.view
, statusTip
="Set play mode",
182 triggered
=self
.play_mode_single_callback
)
183 self
.action_play_mode_single
.setCheckable(True)
184 self
.context
.setContextProperty('action_play_mode_single', self
.action_play_mode_single
)
185 self
.action_play_mode_random
= QtGui
.QAction(_("Random").decode("utf-8"), self
.view
, statusTip
="Set play mode",
186 triggered
=self
.play_mode_random_callback
)
187 self
.action_play_mode_random
.setCheckable(True)
188 self
.context
.setContextProperty('action_play_mode_random', self
.action_play_mode_random
)
189 self
.action_play_mode_repeat
= QtGui
.QAction(_("Repeat").decode("utf-8"), self
.view
, statusTip
="Set play mode",
190 triggered
=self
.play_mode_repeat_callback
)
191 self
.action_play_mode_repeat
.setCheckable(True)
192 self
.context
.setContextProperty('action_play_mode_repeat', self
.action_play_mode_repeat
)
193 actiongroup
= QtGui
.QActionGroup(self
.view
)
194 actiongroup
.setExclusive(True)
195 self
.action_play_mode_all
.setActionGroup(actiongroup
)
196 self
.action_play_mode_single
.setActionGroup(actiongroup
)
197 self
.action_play_mode_random
.setActionGroup(actiongroup
)
198 self
.action_play_mode_repeat
.setActionGroup(actiongroup
)
199 if self
.config
.get("options", "play_mode") == "single":
200 self
.action_play_mode_single
.setChecked(True)
201 elif self
.config
.get("options", "play_mode") == "random":
202 self
.action_play_mode_random
.setChecked(True)
203 elif self
.config
.get("options", "play_mode") == "repeat":
204 self
.action_play_mode_repeat
.setChecked(True)
206 self
.action_play_mode_all
.setChecked(True)
207 self
.theme_str
= _('Theme').decode("utf-8")
208 self
.context
.setContextProperty('theme_str', self
.theme_str
)
210 self
.action_about
= QtGui
.QAction(QtGui
.QIcon('about.png'), _("About").decode("utf-8"), self
.view
,
211 statusTip
="Show about dialog", triggered
=self
.about_callback
)
212 self
.context
.setContextProperty('action_about', self
.action_about
)
214 self
.action_player_rrewind
= QtGui
.QAction(QtGui
.QIcon(''), _("").decode("utf-8"), self
.view
,
215 triggered
=self
.player_rrewind_callback
)
216 self
.context
.setContextProperty('action_player_rrewind', self
.action_player_rrewind
)
217 self
.action_player_rewind
= QtGui
.QAction(QtGui
.QIcon(''), _("").decode("utf-8"), self
.view
,
218 triggered
=self
.player_rewind_callback
)
219 self
.context
.setContextProperty('action_player_rewind', self
.action_player_rewind
)
220 self
.action_player_play
= QtGui
.QAction(QtGui
.QIcon(''), _("").decode("utf-8"), self
.view
,
221 triggered
=self
.player_play_callback
)
222 self
.action_player_play
.setCheckable(True)
223 self
.context
.setContextProperty('action_player_play', self
.action_player_play
)
224 self
.action_player_forward
= QtGui
.QAction(QtGui
.QIcon(''), _("").decode("utf-8"), self
.view
,
225 triggered
=self
.player_forward_callback
)
226 self
.context
.setContextProperty('action_player_forward', self
.action_player_forward
)
227 self
.action_player_fforward
= QtGui
.QAction(QtGui
.QIcon(''), _("").decode("utf-8"), self
.view
,
228 triggered
=self
.player_fforward_callback
)
229 self
.context
.setContextProperty('action_player_fforward', self
.action_player_fforward
)
230 self
.action_player_skip_back
= QtGui
.QAction(QtGui
.QIcon(''), _("").decode("utf-8"), self
.view
,
231 triggered
=self
.player_skip_back_callback
)
232 self
.context
.setContextProperty('action_player_skip_back', self
.action_player_skip_back
)
233 self
.action_player_skip_forward
= QtGui
.QAction(QtGui
.QIcon(''), _("").decode("utf-8"), self
.view
,
234 triggered
=self
.player_skip_forward_callback
)
235 self
.context
.setContextProperty('action_player_skip_forward', self
.action_player_skip_forward
)
237 self
.info_header_str
= _('Playlist item details').decode("utf-8")
238 self
.context
.setContextProperty('info_header_str', self
.info_header_str
)
239 self
.info_title_str
= _('Title:').decode("utf-8")
240 self
.context
.setContextProperty('info_title_str', self
.info_title_str
)
241 self
.info_length_str
= _('Length:').decode("utf-8")
242 self
.context
.setContextProperty('info_length_str', self
.info_length_str
)
243 self
.info_artist_str
= _('Artist:').decode("utf-8")
244 self
.context
.setContextProperty('info_artist_str', self
.info_artist_str
)
245 self
.info_album_str
= _('Album:').decode("utf-8")
246 self
.context
.setContextProperty('info_album_str', self
.info_album_str
)
247 self
.info_filepath_str
= _('Filepath:').decode("utf-8")
248 self
.context
.setContextProperty('info_filepath_str', self
.info_filepath_str
)
250 self
.disabled_str
= _('Disabled').decode("utf-8")
251 self
.context
.setContextProperty('disabled_str', self
.disabled_str
)
253 def make_config(self
):
255 self
.config_qml
["main_width"] = self
.config
.getint("options", "main_width")
256 self
.config_qml
["main_height"] = self
.config
.getint("options", "main_height")
257 self
.config_qml
["button_height"] = self
.config
.getint("options", "button_height")
258 self
.config_qml
["button_border_width"] = self
.config
.getint("options", "button_border_width")
259 self
.config_qml
["button_radius"] = self
.config
.getint("options", "button_radius")
260 self
.config_qml
["cover_height"] = self
.config
.getint("options", "cover_height")
261 self
.config_qml
["progress_height"] = self
.config
.getint("options", "progress_height")
262 self
.config_qml
["font_size"] = self
.config
.getint("options", "font_size")
263 self
.config_qml
["dual_delay"] = self
.config
.getfloat("options", "dual_action_button_delay") * 1000
264 self
.config_qml
["scrolling"] = self
.config
.getboolean("options", "scrolling_labels") * 1000
265 self
.config_qml
["button_width"] = int(float((self
.config_qml
["main_width"]) / 6.0)) - self
.config_qml
["button_border_width"] - 1
266 self
.config_qml
["theme"] = self
.config
.get("options", "theme")
267 return self
.config_qml
269 def quit_panucci(self
):
272 util
.write_config(self
.config
)
275 def close_main_window_callback(self
, event
):
278 def show_main_window(self
):
279 self
.view
.activateWindow()
281 def add_media_callback(self
):
282 self
.view
.rootObject().openFilechooser(self
.get_filechooser_items(self
.config
.get("options", "default_folder")),
283 self
.config
.get("options", "default_folder").decode('utf-8'), "add")
285 @QtCore.Slot(str, str)
286 def filechooser_callback(self
, action
, value
):
287 value
= value
.encode('utf-8')
289 if os
.path
.isdir(os
.path
.expanduser(value
)):
290 self
.config
.set("options", "default_folder", value
)
291 self
.view
.rootObject().openFilechooser(self
.get_filechooser_items(value
), value
.decode('utf-8'), False)
293 value
= value
.rsplit("/", 1)[0]
294 self
.config
.set("options", "default_folder", value
)
295 self
.view
.rootObject().openFilechooser(self
.get_filechooser_items(value
), value
.decode('utf-8'), False)
296 elif action
== "add":
297 if os
.path
.exists(os
.path
.expanduser(value
)):
298 self
.playlist
.load(os
.path
.abspath(os
.path
.expanduser(value
)))
299 self
.view
.rootObject().openPlaylist(False, self
.get_playlist_items())
300 elif action
== "save":
301 ext
= util
.detect_filetype(os
.path
.expanduser(value
))
302 if not self
.playlist
.save_to_new_playlist(os
.path
.expanduser(value
), ext
):
304 #self.notify(_('Error saving playlist...'))
305 print _('Error saving playlist...')
306 elif action
== "play_one":
307 if os
.path
.exists(os
.path
.expanduser(value
)):
308 self
.clear_playlist_callback()
309 self
.playlist
.load(os
.path
.abspath(os
.path
.expanduser(value
)))
311 def get_filechooser_items(self
, folder
):
312 _dir
= os
.path
.expanduser(folder
)
313 dirlist
= os
.listdir(_dir
)
321 _path
= _dir
+ "/" + i
322 if not i
.startswith("."):
323 if os
.path
.isdir(_path
):
328 dir_list
.sort(lambda x
,y
: cmp (x
.lower(), y
.lower()))
329 file_list
.sort(lambda x
,y
: cmp (x
.lower(), y
.lower()))
330 self
.filechooser_items
= []
332 self
.filechooser_items
.append(FilechooserItem(i
, folder
, True))
334 self
.filechooser_items
.append(FilechooserItem(i
, folder
, False))
336 return self
.filechooser_items
338 def play_one_callback(self
):
339 self
.view
.rootObject().openFilechooser(self
.get_filechooser_items(self
.config
.get("options", "default_folder")),
340 self
.config
.get("options", "default_folder").decode('utf-8'), "play_one")
342 def save_playlist_callback(self
):
343 self
.view
.rootObject().openFilechooser(self
.get_filechooser_items(self
.config
.get("options", "default_folder")),
344 self
.config
.get("options", "default_folder").decode('utf-8'), "save")
346 def clear_playlist_callback(self
):
347 self
.playlist
.reset_playlist()
349 def sleep_timer_callback(self
):
350 self
.view
.rootObject().openSleepTimer()
353 def start_timed_shutdown(self
, _minutes
):
354 QtCore
.QTimer
.singleShot(60000*int(_minutes
), self
.quit_panucci
)
356 def volume_control_callback(self
):
357 self
.view
.rootObject().openVolumeControl(str(self
.playlist
.get_volume_level()))
360 def set_volume_level(self
, _percent
):
361 self
.playlist
.set_volume_level(int(_percent
))
363 @QtCore.Slot(str, str)
364 def remove_callback(self
, _id
, _bid
):
365 self
.playlist
.remove_bookmark(_id
, _bid
)
366 self
.playlist_callback()
368 @QtCore.Slot(str, str)
369 def jump_to_callback(self
, _id
, _bid
):
370 self
.playlist
.load_from_bookmark_id(_id
, _bid
)
372 def delete_bookmarks_callback(self
):
373 self
.playlist
.delete_all_bookmarks()
375 def playlist_callback(self
):
376 self
.view
.rootObject().openPlaylist(True, self
.get_playlist_items())
378 def get_playlist_items(self
):
379 self
.playlist_items
= []
380 for item
, data
in self
.playlist
.get_playlist_item_ids():
381 self
.playlist_items
.append(PlaylistItem(item
, data
.get('title'), "", ""))
383 for bid
, bname
, bpos
in self
.playlist
.get_bookmarks_from_item_id( item
):
384 self
.playlist_items
.append(PlaylistItem(item
, bname
, bid
, util
.convert_ns(bpos
)))
386 return self
.playlist_items
389 def playlist_item_info_callback(self
, item_id
):
390 playlist_item
= self
.playlist
.get_item_by_id(item_id
)
391 metadata
= playlist_item
.metadata
392 metadata
["length"] = util
.convert_ns(metadata
["length"])
393 metadata
["path"] = playlist_item
.filepath
.decode("utf-8")
394 for i
in ["title", "artist", "album"]:
396 metadata
[i
] = metadata
[i
].decode("utf-8")
399 self
.view
.rootObject().openPlaylistItemInfo(metadata
)
401 def settings_callback(self
):
402 self
.view
.rootObject().openSettings()
403 #from panucci.qtui.qtsettingsdialog import SettingsDialog
404 #SettingsDialog(self)
406 def lock_progress_callback(self
):
407 self
.set_config_option("lock_progress", str(self
.action_lock_progress
.isChecked()).lower())
409 def dual_action_callback(self
):
410 self
.set_config_option("dual_action_button", str(self
.action_dual_action
.isChecked()).lower())
412 def stay_at_end_callback(self
):
413 self
.set_config_option("stay_at_end", str(self
.action_stay_at_end
.isChecked()).lower())
415 def seek_back_callback(self
):
416 self
.set_config_option("seek_back", str(self
.action_seek_back
.isChecked()).lower())
418 def scrolling_labels_callback(self
):
419 self
.set_config_option("scrolling_labels", str(self
.action_scrolling_labels
.isChecked()).lower())
420 self
.view
.rootObject().start_scrolling_timer(self
.config
.getboolean("options", "scrolling_labels"))
422 def resume_all_callback(self
):
423 self
.set_config_option("resume_all", str(self
.action_resume_all
.isChecked()).lower())
424 if not self
.action_resume_all
.isChecked():
425 self
.playlist
.reset_all_seek_to()
427 def play_mode_all_callback(self
):
428 self
.set_config_option("play_mode", "all")
430 def play_mode_single_callback(self
):
431 self
.set_config_option("play_mode", "single")
433 def play_mode_random_callback(self
):
434 self
.set_config_option("play_mode", "random")
436 def play_mode_repeat_callback(self
):
437 self
.set_config_option("play_mode", "repeat")
439 def about_callback(self
):
440 from panucci
import about
441 self
.view
.rootObject().openAboutDialog([about
.about_name
+" "+panucci
.__version
__, about
.about_text
,
442 about
.about_copyright
, about
.about_website
])
444 def set_config_option(self
, option
, value
):
445 self
.config
.set("options", option
, value
)
447 def _play_file(self
, filename
, pause_on_load
=False):
448 self
.playlist
.load( os
.path
.abspath(filename
) )
450 if self
.playlist
.is_empty
:
453 def player_rrewind_callback(self
):
454 self
.do_seek(-1*self
.config
.getint("options", "seek_long"))
456 def player_rewind_callback(self
):
457 self
.do_seek(-1*self
.config
.getint("options", "seek_short"))
459 def player_forward_callback(self
):
460 self
.do_seek(self
.config
.getint("options", "seek_short"))
462 def player_fforward_callback(self
):
463 self
.do_seek(self
.config
.getint("options", "seek_long"))
465 def player_skip_back_callback(self
):
468 def player_skip_forward_callback(self
):
471 def player_play_callback(self
):
472 self
.playlist
.play_pause_toggle()
475 def bookmark_callback(self
):
476 self
.playlist
.add_bookmark_at_current_position()
478 def do_seek(self
, seek_amount
):
479 resp
= self
.playlist
.do_seek(from_current
=seek_amount
*10**9)
481 self
.set_progress_callback( *resp
)
482 self
.on_set_progress
.emit()
484 def set_progress_callback(self
, time_elapsed
, total_time
):
485 self
.time_str
= "%s / %s" %(util
.convert_ns(time_elapsed
), util
.convert_ns(total_time
))
486 self
.progress_fraction
= float(time_elapsed
) / float(total_time
) if total_time
else 0
487 self
.on_set_progress
.emit()
490 def on_progress_clicked(self
, new_fraction
):
491 if not self
.config
.getboolean("options", "lock_progress"):
492 resp
= self
.playlist
.do_seek(percent
=new_fraction
)
494 self
.set_progress_callback( *resp
)
496 def timer_callback( self
):
497 if self
.playlist
.playing
and not self
.playlist
.seeking
:
498 pos_int
, dur_int
= self
.playlist
.get_position_duration()
499 # This prevents bogus values from being set while seeking
500 if pos_int
>= 0 and dur_int
>= 0:
501 self
.set_progress_callback( pos_int
, dur_int
)
504 def get_play_pause_icon_path(self
):
505 if self
.action_player_play
.isChecked():
506 _path
= "media-playback-pause.png"
508 _path
= "media-playback-start.png"
511 def get_artist_str(self
):
513 return self
.metadata
.get('artist', 0).decode('utf-8')
517 def get_album_str(self
):
519 return self
.metadata
.get('album', 0).decode('utf-8')
523 def get_title_str(self
):
525 return self
.metadata
.get('title', 0).decode('utf-8')
529 def set_text_x(self
):
531 self
.view
.rootObject().set_text_x()
533 def get_cover_str(self
):
534 if self
.metadata
and self
.metadata
.has_key('image') and self
.metadata
['image']:
535 return "image://cover/" + os
.urandom(10)
539 def get_time_str(self
):
542 def get_progress(self
):
543 return self
.progress_fraction
545 on_play_pause
= QtCore
.Signal()
546 on_set_progress
= QtCore
.Signal()
547 on_set_metadata
= QtCore
.Signal()
548 play_pause_icon_path
= QtCore
.Property(str, get_play_pause_icon_path
, notify
=on_play_pause
)
549 time_string
= QtCore
.Property(str, get_time_str
, notify
=on_set_progress
)
550 progress
= QtCore
.Property(float, get_progress
, notify
=on_set_progress
)
551 artist_string
= QtCore
.Property(str, get_artist_str
, notify
=on_set_metadata
)
552 album_string
= QtCore
.Property(str, get_album_str
, notify
=on_set_metadata
)
553 title_string
= QtCore
.Property(str, get_title_str
, notify
=on_set_metadata
)
554 cover_string
= QtCore
.Property(str, get_cover_str
, notify
=on_set_metadata
)
556 def on_player_stopped(self
):
558 self
.action_player_play
.setChecked(False)
559 self
.on_play_pause
.emit()
561 estimated_length
= self
.metadata
.get('length', 0)
562 self
.set_progress_callback( 0, estimated_length
)
563 #self.set_controls_sensitivity(False)
565 def on_player_playing(self
):
566 self
.timer_callback()
568 self
.action_player_play
.setChecked(True)
569 self
.on_play_pause
.emit()
570 #self.set_controls_sensitivity(True)
572 def on_player_paused( self
, position
, duration
):
574 self
.action_player_play
.setChecked(False)
575 self
.on_play_pause
.emit()
576 #self.set_progress_callback( position, duration )
578 def on_player_new_track(self
):
579 self
.time_str
= "00:00 / 00:00"
580 self
.progress_fraction
= 0
582 self
.on_set_progress
.emit()
583 self
.on_set_metadata
.emit()
584 self
.view
.setWindowTitle("Panucci")
586 def on_player_new_metadata(self
):
587 self
.metadata
= self
.playlist
.get_file_metadata()
588 position
= self
.playlist
.get_current_position()
589 estimated_length
= self
.metadata
.get('length', 0)
590 self
.set_progress_callback(position
, estimated_length
)
591 self
.on_set_progress
.emit()
592 self
.on_set_metadata
.emit()
594 _title
= self
.metadata
["title"]
596 _title
= _title
[:24] + '...'
597 self
.view
.setWindowTitle(_title
.decode('utf-8'))
599 def on_player_end_of_playlist(self
, loop
):
601 estimated_length
= self
.metadata
.get('length', 0)
602 self
.set_progress_callback( 0, estimated_length
)
604 def on_player_reset_playlist(self
):
605 self
.on_player_stopped()
606 self
.on_player_new_track()
609 def open_external_url(self
, url
):
610 os
.system("xdg-open " + url
)
612 def handle_connection_state(self
, device_path
):
613 if device_path
== self
.headset_path
and self
.config
.getboolean("options", "play_on_headset") and not self
.playlist
.playing
:
614 self
.playlist
.play_pause_toggle()
616 def handle_bt_button(self
, signal
, button
):
617 if signal
== 'ButtonPressed':
618 if button
== 'play-cd':
619 self
.playlist
.play_pause_toggle()
620 elif button
== 'pause-cd':
621 self
.playlist
.play_pause_toggle()
622 elif button
== 'previous-song':
623 if self
.config
.get("options", "headset_button") == "short":
624 self
.do_seek(-1*self
.config
.getint("options", "seek_short"))
625 elif self
.config
.get("options", "headset_button") == "long":
626 self
.do_seek(-1*self
.config
.getint("options", "seek_long"))
629 elif button
== 'next-song':
630 if self
.config
.get("options", "headset_button") == "short":
631 self
.do_seek(self
.config
.getint("options", "seek_short"))
632 elif self
.config
.get("options", "headset_button") == "long":
633 self
.do_seek(self
.config
.getint("options", "seek_long"))
637 class ImageProvider(QtDeclarative
.QDeclarativeImageProvider
):
638 def __init__(self
, main
):
639 QtDeclarative
.QDeclarativeImageProvider
.__init
__(self
, QtDeclarative
.QDeclarativeImageProvider
.Pixmap
)
642 def requestPixmap(self
, id, size
, requestedSize
):
643 size
= requestedSize
.width()
644 pixmap
= QtGui
.QPixmap()
645 pixmap
.loadFromData(self
.__main
.metadata
['image'])
646 pixmap
= pixmap
.scaled(size
, size
, mode
=QtCore
.Qt
.SmoothTransformation
)
649 class PlaylistItem(QtCore
.QObject
):
650 def __init__(self
, _id
, _caption
, _bookmark
, _position
):
651 QtCore
.QObject
.__init
__(self
)
652 if isinstance(_caption
, str):
653 _caption
= _caption
.decode('utf-8')
654 self
._caption
= _caption
656 self
._bookmark
= _bookmark
657 self
._position
= _position
659 changed
= QtCore
.Signal()
664 def _get_caption(self
):
667 def _get_bookmark(self
):
668 return self
._bookmark
670 def _get_position(self
):
671 return self
._position
673 item_id
= QtCore
.Property(str, _get_id
, notify
=changed
)
674 caption
= QtCore
.Property(unicode, _get_caption
, notify
=changed
)
675 bookmark_id
= QtCore
.Property(str, _get_bookmark
, notify
=changed
)
676 position
= QtCore
.Property(str, _get_position
, notify
=changed
)
678 class FilechooserItem(QtCore
.QObject
):
679 def __init__(self
, _caption
, _path
, _directory
):
680 QtCore
.QObject
.__init
__(self
)
681 if isinstance(_caption
, str):
682 _caption
= _caption
.decode('utf-8')
683 if isinstance(_path
, str):
684 _path
= _path
.decode('utf-8')
686 self
._caption
= _caption
688 self
._directory
= _directory
690 changed
= QtCore
.Signal()
692 def _get_caption(self
):
698 def _get_directory(self
):
699 return self
._directory
701 caption
= QtCore
.Property(unicode, _get_caption
, notify
=changed
)
702 path
= QtCore
.Property(unicode, _get_path
, notify
=changed
)
703 directory
= QtCore
.Property(bool, _get_directory
, notify
=changed
)
705 class ThemeController(QtCore
.QObject
):
706 def __init__(self
, config
):
707 QtCore
.QObject
.__init
__(self
)
710 self
.config_theme
= ConfigParser
.SafeConfigParser()
711 #_file = open(util.find_data_file("theme-all.conf"))
712 #self.config.readfp(_file)
714 _file
= open(panucci
.THEME_FILE
)
715 self
.config_theme
.readfp(_file
)
719 def set_theme(self
, theme
):
720 self
.config
.set("options", "theme", theme
.strip().lower())
723 def get_themes(self
):
724 self
.sections
= self
.config_theme
.sections()
725 self
.sections
.sort(lambda x
,y
: cmp (x
.lower(), y
.lower()))
728 def _get_background(self
):
729 return "#" + self
.config_theme
.get(self
.config
.get("options", "theme"), "background")
731 def _get_foreground(self
):
732 return "#" + self
.config_theme
.get(self
.config
.get("options", "theme"), "foreground")
734 def _get_highlight(self
):
735 return "#" + self
.config_theme
.get(self
.config
.get("options", "theme"), "highlight")
737 def _get_button_color(self
):
738 return "#" + self
.config_theme
.get(self
.config
.get("options", "theme"), "button_color")
740 def _get_button_border_color(self
):
741 return "#" + self
.config_theme
.get(self
.config
.get("options", "theme"), "button_border_color")
743 def _get_progress_color(self
):
744 return "#" + self
.config_theme
.get(self
.config
.get("options", "theme"), "progress_color")
746 def _get_progress_bg_color(self
):
747 return "#" + self
.config_theme
.get(self
.config
.get("options", "theme"), "progress_background_color")
749 changed
= QtCore
.Signal()
750 background
= QtCore
.Property(str, _get_background
, notify
=changed
)
751 foreground
= QtCore
.Property(str, _get_foreground
, notify
=changed
)
752 highlight
= QtCore
.Property(str, _get_highlight
, notify
=changed
)
753 button_color
= QtCore
.Property(str, _get_button_color
, notify
=changed
)
754 button_border_color
= QtCore
.Property(str, _get_button_border_color
, notify
=changed
)
755 progress_color
= QtCore
.Property(str, _get_progress_color
, notify
=changed
)
756 progress_bg_color
= QtCore
.Property(str, _get_progress_bg_color
, notify
=changed
)