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()
83 def create_actions(self
):
85 self
.action_add_media
= QtGui
.QAction(QtGui
.QIcon(''), _("Add Media").decode("utf-8"), self
.view
, shortcut
="Ctrl+A",
86 statusTip
="Add file to playlist", triggered
=self
.add_media_callback
)
87 self
.context
.setContextProperty('action_add_media', self
.action_add_media
)
88 self
.action_play_one
= QtGui
.QAction(QtGui
.QIcon(''), _("Play One").decode("utf-8"), self
.view
, shortcut
="Ctrl+O",
89 statusTip
="Play one file", triggered
=self
.play_one_callback
)
90 self
.context
.setContextProperty('action_play_one', self
.action_play_one
)
91 self
.action_save_playlist
= QtGui
.QAction(QtGui
.QIcon(':/images/save.png'), _("Save Playlist").decode("utf-8"), self
.view
,
92 shortcut
="Ctrl+W", statusTip
="Save current playlist as m3u", triggered
=self
.save_playlist_callback
)
93 self
.context
.setContextProperty('action_save_playlist', self
.action_save_playlist
)
94 self
.action_clear_playlist
= QtGui
.QAction(QtGui
.QIcon(':/images/trashcan.png'), _("Clear Playlist").decode("utf-8"), self
.view
,
95 shortcut
="Ctrl+H", statusTip
="Clear current playlist", triggered
=self
.clear_playlist_callback
)
96 self
.context
.setContextProperty('action_clear_playlist', self
.action_clear_playlist
)
97 self
.action_delete_bookmarks
= QtGui
.QAction(QtGui
.QIcon(':/images/trashcan.png'), _("Delete All Bookmarks").decode("utf-8"), self
.view
,
98 shortcut
="Ctrl+K", statusTip
="Delete all bookmarks", triggered
=self
.delete_bookmarks_callback
)
99 self
.context
.setContextProperty('action_delete_bookmarks', self
.action_delete_bookmarks
)
100 self
.action_quit
= QtGui
.QAction(QtGui
.QIcon('/usr/share/icons/gnome/16x16/actions/exit.png'), "Quit", self
.view
, shortcut
="Ctrl+Q",
101 statusTip
="Exit the application", triggered
=self
.quit_panucci
)
102 self
.context
.setContextProperty('action_quit', self
.action_quit
)
104 self
.action_playlist
= QtGui
.QAction(_("Playlist").decode("utf-8"), self
.view
, shortcut
="Ctrl+P",
105 statusTip
=_("Open playlist"), triggered
=self
.playlist_callback
)
106 self
.context
.setContextProperty('action_playlist', self
.action_playlist
)
107 self
.action_settings
= QtGui
.QAction(_("Settings").decode("utf-8"), self
.view
, shortcut
="Ctrl+C",
108 statusTip
=_("Open settings dialog"), triggered
=self
.settings_callback
)
109 self
.context
.setContextProperty('action_settings', self
.action_settings
)
110 self
.action_timer
= QtGui
.QAction(_("Sleep Timer").decode("utf-8"), self
.view
, shortcut
="Ctrl+T",
111 statusTip
=_("Start a timed shutdown"), triggered
=self
.sleep_timer_callback
)
112 self
.context
.setContextProperty('action_timer', self
.action_timer
)
113 self
.shutdown_str
= _("Shutdown time in minutes").decode("utf-8")
114 self
.context
.setContextProperty('shutdown_str', self
.shutdown_str
)
116 self
.main_window_str
= _("Main Window").decode("utf-8")
117 self
.context
.setContextProperty('main_window_str', self
.main_window_str
)
118 self
.action_lock_progress
= QtGui
.QAction(_("Lock Progress Bar").decode("utf-8"), self
.view
, shortcut
="Ctrl+L",
119 statusTip
="Lock progress bar", triggered
=self
.lock_progress_callback
)
120 self
.action_lock_progress
.setCheckable(True)
121 self
.action_lock_progress
.setChecked(self
.config
.getboolean("options", "lock_progress"))
122 self
.context
.setContextProperty('action_lock_progress', self
.action_lock_progress
)
123 self
.action_dual_action
= QtGui
.QAction(_("Dual Action Button").decode("utf-8"), self
.view
, shortcut
="Ctrl+B",
124 statusTip
="Set dual action button", triggered
=self
.dual_action_callback
)
125 self
.action_dual_action
.setCheckable(True)
126 self
.action_dual_action
.setChecked(self
.config
.getboolean("options", "dual_action_button"))
127 self
.context
.setContextProperty('action_dual_action', self
.action_dual_action
)
128 self
.action_scrolling_labels
= QtGui
.QAction(_("Scrolling Labels").decode("utf-8"), self
.view
, shortcut
="Ctrl+V",
129 statusTip
="Scroll title labels when too long", triggered
=self
.scrolling_labels_callback
)
130 self
.action_scrolling_labels
.setCheckable(True)
131 self
.action_scrolling_labels
.setChecked(self
.config
.getboolean("options", "scrolling_labels"))
132 self
.context
.setContextProperty('action_scrolling_labels', self
.action_scrolling_labels
)
133 self
.playback_str
= _("Playback").decode("utf-8")
134 self
.context
.setContextProperty('playback_str', self
.playback_str
)
135 self
.action_stay_at_end
= QtGui
.QAction(_("Stay at End").decode("utf-8"), self
.view
, shortcut
="Ctrl+E",
136 statusTip
="Stay at file end", triggered
=self
.stay_at_end_callback
)
137 self
.action_stay_at_end
.setCheckable(True)
138 self
.action_stay_at_end
.setChecked(self
.config
.getboolean("options", "stay_at_end"))
139 self
.context
.setContextProperty('action_stay_at_end', self
.action_stay_at_end
)
140 self
.action_seek_back
= QtGui
.QAction(_("Seek Back").decode("utf-8"), self
.view
, shortcut
="Ctrl+S",
141 statusTip
="Seek back to previous file", triggered
=self
.seek_back_callback
)
142 self
.action_seek_back
.setCheckable(True)
143 self
.action_seek_back
.setChecked(self
.config
.getboolean("options", "seek_back"))
144 self
.context
.setContextProperty('action_seek_back', self
.action_seek_back
)
145 self
.action_resume_all
= QtGui
.QAction(_("Resume All").decode("utf-8"), self
.view
, shortcut
="Ctrl+R",
146 statusTip
="Resume all files automatically", triggered
=self
.resume_all_callback
)
147 self
.action_resume_all
.setCheckable(True)
148 self
.action_resume_all
.setChecked(self
.config
.getboolean("options", "resume_all"))
149 self
.context
.setContextProperty('action_resume_all', self
.action_resume_all
)
150 self
.play_mode_str
= _("Play Mode").decode("utf-8")
151 self
.context
.setContextProperty('play_mode_str', self
.play_mode_str
)
152 self
.action_play_mode_all
= QtGui
.QAction(_("All").decode("utf-8"), self
.view
, statusTip
="Set play mode",
153 triggered
=self
.play_mode_all_callback
)
154 self
.action_play_mode_all
.setCheckable(True)
155 self
.context
.setContextProperty('action_play_mode_all', self
.action_play_mode_all
)
156 self
.action_play_mode_single
= QtGui
.QAction(_("Single").decode("utf-8"), self
.view
, statusTip
="Set play mode",
157 triggered
=self
.play_mode_single_callback
)
158 self
.action_play_mode_single
.setCheckable(True)
159 self
.context
.setContextProperty('action_play_mode_single', self
.action_play_mode_single
)
160 self
.action_play_mode_random
= QtGui
.QAction(_("Random").decode("utf-8"), self
.view
, statusTip
="Set play mode",
161 triggered
=self
.play_mode_random_callback
)
162 self
.action_play_mode_random
.setCheckable(True)
163 self
.context
.setContextProperty('action_play_mode_random', self
.action_play_mode_random
)
164 self
.action_play_mode_repeat
= QtGui
.QAction(_("Repeat").decode("utf-8"), self
.view
, statusTip
="Set play mode",
165 triggered
=self
.play_mode_repeat_callback
)
166 self
.action_play_mode_repeat
.setCheckable(True)
167 self
.context
.setContextProperty('action_play_mode_repeat', self
.action_play_mode_repeat
)
168 actiongroup
= QtGui
.QActionGroup(self
.view
)
169 actiongroup
.setExclusive(True)
170 self
.action_play_mode_all
.setActionGroup(actiongroup
)
171 self
.action_play_mode_single
.setActionGroup(actiongroup
)
172 self
.action_play_mode_random
.setActionGroup(actiongroup
)
173 self
.action_play_mode_repeat
.setActionGroup(actiongroup
)
174 if self
.config
.get("options", "play_mode") == "single":
175 self
.action_play_mode_single
.setChecked(True)
176 elif self
.config
.get("options", "play_mode") == "random":
177 self
.action_play_mode_random
.setChecked(True)
178 elif self
.config
.get("options", "play_mode") == "repeat":
179 self
.action_play_mode_repeat
.setChecked(True)
181 self
.action_play_mode_all
.setChecked(True)
182 self
.theme_str
= _('Theme').decode("utf-8")
183 self
.context
.setContextProperty('theme_str', self
.theme_str
)
185 self
.action_about
= QtGui
.QAction(QtGui
.QIcon('about.png'), _("About").decode("utf-8"), self
.view
,
186 statusTip
="Show about dialog", triggered
=self
.about_callback
)
187 self
.context
.setContextProperty('action_about', self
.action_about
)
189 self
.action_player_rrewind
= QtGui
.QAction(QtGui
.QIcon(''), _("").decode("utf-8"), self
.view
,
190 triggered
=self
.player_rrewind_callback
)
191 self
.context
.setContextProperty('action_player_rrewind', self
.action_player_rrewind
)
192 self
.action_player_rewind
= QtGui
.QAction(QtGui
.QIcon(''), _("").decode("utf-8"), self
.view
,
193 triggered
=self
.player_rewind_callback
)
194 self
.context
.setContextProperty('action_player_rewind', self
.action_player_rewind
)
195 self
.action_player_play
= QtGui
.QAction(QtGui
.QIcon(''), _("").decode("utf-8"), self
.view
,
196 triggered
=self
.player_play_callback
)
197 self
.action_player_play
.setCheckable(True)
198 self
.context
.setContextProperty('action_player_play', self
.action_player_play
)
199 self
.action_player_forward
= QtGui
.QAction(QtGui
.QIcon(''), _("").decode("utf-8"), self
.view
,
200 triggered
=self
.player_forward_callback
)
201 self
.context
.setContextProperty('action_player_forward', self
.action_player_forward
)
202 self
.action_player_fforward
= QtGui
.QAction(QtGui
.QIcon(''), _("").decode("utf-8"), self
.view
,
203 triggered
=self
.player_fforward_callback
)
204 self
.context
.setContextProperty('action_player_fforward', self
.action_player_fforward
)
205 self
.action_player_skip_back
= QtGui
.QAction(QtGui
.QIcon(''), _("").decode("utf-8"), self
.view
,
206 triggered
=self
.player_skip_back_callback
)
207 self
.context
.setContextProperty('action_player_skip_back', self
.action_player_skip_back
)
208 self
.action_player_skip_forward
= QtGui
.QAction(QtGui
.QIcon(''), _("").decode("utf-8"), self
.view
,
209 triggered
=self
.player_skip_forward_callback
)
210 self
.context
.setContextProperty('action_player_skip_forward', self
.action_player_skip_forward
)
212 self
.info_header_str
= _('Playlist item details').decode("utf-8")
213 self
.context
.setContextProperty('info_header_str', self
.info_header_str
)
214 self
.info_title_str
= _('Title:').decode("utf-8")
215 self
.context
.setContextProperty('info_title_str', self
.info_title_str
)
216 self
.info_length_str
= _('Length:').decode("utf-8")
217 self
.context
.setContextProperty('info_length_str', self
.info_length_str
)
218 self
.info_artist_str
= _('Artist:').decode("utf-8")
219 self
.context
.setContextProperty('info_artist_str', self
.info_artist_str
)
220 self
.info_album_str
= _('Album:').decode("utf-8")
221 self
.context
.setContextProperty('info_album_str', self
.info_album_str
)
222 self
.info_filepath_str
= _('Filepath:').decode("utf-8")
223 self
.context
.setContextProperty('info_filepath_str', self
.info_filepath_str
)
225 def make_config(self
):
227 self
.config_qml
["main_width"] = self
.config
.getint("options", "main_width")
228 self
.config_qml
["main_height"] = self
.config
.getint("options", "main_height")
229 self
.config_qml
["button_height"] = self
.config
.getint("options", "button_height")
230 self
.config_qml
["button_border_width"] = self
.config
.getint("options", "button_border_width")
231 self
.config_qml
["button_radius"] = self
.config
.getint("options", "button_radius")
232 self
.config_qml
["cover_height"] = self
.config
.getint("options", "cover_height")
233 self
.config_qml
["progress_height"] = self
.config
.getint("options", "progress_height")
234 self
.config_qml
["font_size"] = self
.config
.getint("options", "font_size")
235 self
.config_qml
["dual_delay"] = self
.config
.getfloat("options", "dual_action_button_delay") * 1000
236 self
.config_qml
["scrolling"] = self
.config
.getboolean("options", "scrolling_labels") * 1000
237 self
.config_qml
["button_width"] = (self
.config_qml
["main_width"] / 6) - 7
238 self
.config_qml
["theme"] = self
.config
.get("options", "theme")
239 return self
.config_qml
241 def quit_panucci(self
):
244 util
.write_config(self
.config
)
247 def close_main_window_callback(self
, event
):
250 def show_main_window(self
):
251 self
.view
.activateWindow()
253 def add_media_callback(self
):
254 self
.view
.rootObject().openFilechooser(self
.get_filechooser_items(self
.config
.get("options", "default_folder")),
255 self
.config
.get("options", "default_folder").decode('utf-8'), "add")
257 @QtCore.Slot(str, str)
258 def filechooser_callback(self
, action
, value
):
259 value
= value
.encode('utf-8')
261 if os
.path
.isdir(os
.path
.expanduser(value
)):
262 self
.config
.set("options", "default_folder", value
)
263 self
.view
.rootObject().openFilechooser(self
.get_filechooser_items(value
), value
.decode('utf-8'), False)
265 value
= value
.rsplit("/", 1)[0]
266 self
.config
.set("options", "default_folder", value
)
267 self
.view
.rootObject().openFilechooser(self
.get_filechooser_items(value
), value
.decode('utf-8'), False)
268 elif action
== "add":
269 if os
.path
.exists(os
.path
.expanduser(value
)):
270 self
.playlist
.load(os
.path
.abspath(os
.path
.expanduser(value
)))
271 self
.view
.rootObject().openPlaylist(False, self
.get_playlist_items())
272 elif action
== "save":
273 ext
= util
.detect_filetype(os
.path
.expanduser(value
))
274 if not self
.playlist
.save_to_new_playlist(os
.path
.expanduser(value
), ext
):
276 #self.notify(_('Error saving playlist...'))
277 print _('Error saving playlist...')
278 elif action
== "play_one":
279 if os
.path
.exists(os
.path
.expanduser(value
)):
280 self
.clear_playlist_callback()
281 self
.playlist
.load(os
.path
.abspath(os
.path
.expanduser(value
)))
283 def get_filechooser_items(self
, folder
):
284 _dir
= os
.path
.expanduser(folder
)
285 dirlist
= os
.listdir(_dir
)
293 _path
= _dir
+ "/" + i
294 if not i
.startswith("."):
295 if os
.path
.isdir(_path
):
300 dir_list
.sort(lambda x
,y
: cmp (x
.lower(), y
.lower()))
301 file_list
.sort(lambda x
,y
: cmp (x
.lower(), y
.lower()))
302 self
.filechooser_items
= []
304 self
.filechooser_items
.append(FilechooserItem(i
, folder
, True))
306 self
.filechooser_items
.append(FilechooserItem(i
, folder
, False))
308 return self
.filechooser_items
310 def play_one_callback(self
):
311 self
.view
.rootObject().openFilechooser(self
.get_filechooser_items(self
.config
.get("options", "default_folder")),
312 self
.config
.get("options", "default_folder").decode('utf-8'), "play_one")
314 def save_playlist_callback(self
):
315 self
.view
.rootObject().openFilechooser(self
.get_filechooser_items(self
.config
.get("options", "default_folder")),
316 self
.config
.get("options", "default_folder").decode('utf-8'), "save")
318 def clear_playlist_callback(self
):
319 self
.playlist
.reset_playlist()
321 def sleep_timer_callback(self
):
322 self
.view
.rootObject().openSleepTimer()
325 def start_timed_shutdown(self
, _minutes
):
326 QtCore
.QTimer
.singleShot(60000*int(_minutes
), self
.quit_panucci
)
328 @QtCore.Slot(str, str)
329 def remove_callback(self
, _id
, _bid
):
330 self
.playlist
.remove_bookmark(_id
, _bid
)
331 self
.playlist_callback()
333 @QtCore.Slot(str, str)
334 def jump_to_callback(self
, _id
, _bid
):
335 self
.playlist
.load_from_bookmark_id(_id
, _bid
)
337 def delete_bookmarks_callback(self
):
338 self
.playlist
.delete_all_bookmarks()
340 def playlist_callback(self
):
341 self
.view
.rootObject().openPlaylist(True, self
.get_playlist_items())
343 def get_playlist_items(self
):
344 self
.playlist_items
= []
345 for item
, data
in self
.playlist
.get_playlist_item_ids():
346 self
.playlist_items
.append(PlaylistItem(item
, data
.get('title'), "", ""))
348 for bid
, bname
, bpos
in self
.playlist
.get_bookmarks_from_item_id( item
):
349 self
.playlist_items
.append(PlaylistItem(item
, bname
, bid
, util
.convert_ns(bpos
)))
351 return self
.playlist_items
354 def playlist_item_info_callback(self
, item_id
):
355 playlist_item
= self
.playlist
.get_item_by_id(item_id
)
356 metadata
= playlist_item
.metadata
357 metadata
["length"] = util
.convert_ns(metadata
["length"])
358 metadata
["path"] = playlist_item
.filepath
.decode("utf-8")
359 for i
in ["title", "artist", "album"]:
361 metadata
[i
] = metadata
[i
].decode("utf-8")
364 self
.view
.rootObject().openPlaylistItemInfo(metadata
)
366 def settings_callback(self
):
367 self
.view
.rootObject().openSettings()
368 #from panucci.qtui.qtsettingsdialog import SettingsDialog
369 #SettingsDialog(self)
371 def lock_progress_callback(self
):
372 self
.set_config_option("lock_progress", str(self
.action_lock_progress
.isChecked()).lower())
374 def dual_action_callback(self
):
375 self
.set_config_option("dual_action_button", str(self
.action_dual_action
.isChecked()).lower())
377 def stay_at_end_callback(self
):
378 self
.set_config_option("stay_at_end", str(self
.action_stay_at_end
.isChecked()).lower())
380 def seek_back_callback(self
):
381 self
.set_config_option("seek_back", str(self
.action_seek_back
.isChecked()).lower())
383 def scrolling_labels_callback(self
):
384 self
.set_config_option("scrolling_labels", str(self
.action_scrolling_labels
.isChecked()).lower())
385 self
.view
.rootObject().start_scrolling_timer(self
.config
.getboolean("options", "scrolling_labels"))
387 def resume_all_callback(self
):
388 self
.set_config_option("resume_all", str(self
.action_resume_all
.isChecked()).lower())
389 if not self
.action_resume_all
.isChecked():
390 self
.playlist
.reset_all_seek_to()
392 def play_mode_all_callback(self
):
393 self
.set_config_option("play_mode", "all")
395 def play_mode_single_callback(self
):
396 self
.set_config_option("play_mode", "single")
398 def play_mode_random_callback(self
):
399 self
.set_config_option("play_mode", "random")
401 def play_mode_repeat_callback(self
):
402 self
.set_config_option("play_mode", "repeat")
404 def about_callback(self
):
405 from panucci
import about
406 self
.view
.rootObject().openAboutDialog([about
.about_name
+" "+panucci
.__version
__, about
.about_text
,
407 about
.about_copyright
, about
.about_website
])
409 def set_config_option(self
, option
, value
):
410 self
.config
.set("options", option
, value
)
412 def _play_file(self
, filename
, pause_on_load
=False):
413 self
.playlist
.load( os
.path
.abspath(filename
) )
415 if self
.playlist
.is_empty
:
418 def player_rrewind_callback(self
):
419 self
.do_seek(-1*self
.config
.getint("options", "seek_long"))
421 def player_rewind_callback(self
):
422 self
.do_seek(-1*self
.config
.getint("options", "seek_short"))
424 def player_forward_callback(self
):
425 self
.do_seek(self
.config
.getint("options", "seek_short"))
427 def player_fforward_callback(self
):
428 self
.do_seek(self
.config
.getint("options", "seek_long"))
430 def player_skip_back_callback(self
):
433 def player_skip_forward_callback(self
):
436 def player_play_callback(self
):
437 self
.playlist
.play_pause_toggle()
440 def bookmark_callback(self
):
441 self
.playlist
.add_bookmark_at_current_position()
443 def do_seek(self
, seek_amount
):
444 resp
= self
.playlist
.do_seek(from_current
=seek_amount
*10**9)
446 self
.set_progress_callback( *resp
)
447 self
.on_set_progress
.emit()
449 def set_progress_callback(self
, time_elapsed
, total_time
):
450 self
.time_str
= "%s / %s" %(util
.convert_ns(time_elapsed
), util
.convert_ns(total_time
))
451 self
.progress_fraction
= float(time_elapsed
) / float(total_time
) if total_time
else 0
452 self
.on_set_progress
.emit()
455 def on_progress_clicked(self
, new_fraction
):
456 if not self
.config
.getboolean("options", "lock_progress"):
457 resp
= self
.playlist
.do_seek(percent
=new_fraction
)
459 self
.set_progress_callback( *resp
)
461 def timer_callback( self
):
462 if self
.playlist
.playing
and not self
.playlist
.seeking
:
463 pos_int
, dur_int
= self
.playlist
.get_position_duration()
464 # This prevents bogus values from being set while seeking
465 if pos_int
>= 0 and dur_int
>= 0:
466 self
.set_progress_callback( pos_int
, dur_int
)
469 def get_play_pause_icon_path(self
):
470 if self
.action_player_play
.isChecked():
471 _path
= "media-playback-pause.png"
473 _path
= "media-playback-start.png"
476 def get_artist_str(self
):
478 return self
.metadata
.get('artist', 0).decode('utf-8')
482 def get_album_str(self
):
484 return self
.metadata
.get('album', 0).decode('utf-8')
488 def get_title_str(self
):
490 return self
.metadata
.get('title', 0).decode('utf-8')
494 def set_text_x(self
):
496 self
.view
.rootObject().set_text_x()
498 def get_cover_str(self
):
499 if self
.metadata
and self
.metadata
.has_key('image') and self
.metadata
['image']:
500 return "image://cover/" + os
.urandom(10)
504 def get_time_str(self
):
507 def get_progress(self
):
508 return self
.progress_fraction
510 on_play_pause
= QtCore
.Signal()
511 on_set_progress
= QtCore
.Signal()
512 on_set_metadata
= QtCore
.Signal()
513 play_pause_icon_path
= QtCore
.Property(str, get_play_pause_icon_path
, notify
=on_play_pause
)
514 time_string
= QtCore
.Property(str, get_time_str
, notify
=on_set_progress
)
515 progress
= QtCore
.Property(float, get_progress
, notify
=on_set_progress
)
516 artist_string
= QtCore
.Property(str, get_artist_str
, notify
=on_set_metadata
)
517 album_string
= QtCore
.Property(str, get_album_str
, notify
=on_set_metadata
)
518 title_string
= QtCore
.Property(str, get_title_str
, notify
=on_set_metadata
)
519 cover_string
= QtCore
.Property(str, get_cover_str
, notify
=on_set_metadata
)
521 def on_player_stopped(self
):
523 self
.action_player_play
.setChecked(False)
524 self
.on_play_pause
.emit()
526 estimated_length
= self
.metadata
.get('length', 0)
527 self
.set_progress_callback( 0, estimated_length
)
528 #self.set_controls_sensitivity(False)
530 def on_player_playing(self
):
531 self
.timer_callback()
533 self
.action_player_play
.setChecked(True)
534 self
.on_play_pause
.emit()
535 #self.set_controls_sensitivity(True)
537 def on_player_paused( self
, position
, duration
):
539 self
.action_player_play
.setChecked(False)
540 self
.on_play_pause
.emit()
541 #self.set_progress_callback( position, duration )
543 def on_player_new_track(self
):
544 self
.time_str
= "00:00 / 00:00"
545 self
.progress_fraction
= 0
547 self
.on_set_progress
.emit()
548 self
.on_set_metadata
.emit()
549 self
.view
.setWindowTitle("Panucci")
551 def on_player_new_metadata(self
):
552 self
.metadata
= self
.playlist
.get_file_metadata()
553 position
= self
.playlist
.get_current_position()
554 estimated_length
= self
.metadata
.get('length', 0)
555 self
.set_progress_callback(position
, estimated_length
)
556 self
.on_set_progress
.emit()
557 self
.on_set_metadata
.emit()
559 _title
= self
.metadata
["title"]
561 _title
= _title
[:24] + '...'
562 self
.view
.setWindowTitle(_title
.decode('utf-8'))
564 def on_player_end_of_playlist(self
, loop
):
566 estimated_length
= self
.metadata
.get('length', 0)
567 self
.set_progress_callback( 0, estimated_length
)
569 def on_player_reset_playlist(self
):
570 self
.on_player_stopped()
571 self
.on_player_new_track()
574 def open_external_url(self
, url
):
575 os
.system("xdg-open " + url
)
577 class ImageProvider(QtDeclarative
.QDeclarativeImageProvider
):
578 def __init__(self
, main
):
579 QtDeclarative
.QDeclarativeImageProvider
.__init
__(self
, QtDeclarative
.QDeclarativeImageProvider
.Pixmap
)
582 def requestPixmap(self
, id, size
, requestedSize
):
583 size
= requestedSize
.width()
584 pixmap
= QtGui
.QPixmap()
585 pixmap
.loadFromData(self
.__main
.metadata
['image'])
586 pixmap
= pixmap
.scaled(size
, size
, mode
=QtCore
.Qt
.SmoothTransformation
)
589 class PlaylistItem(QtCore
.QObject
):
590 def __init__(self
, _id
, _caption
, _bookmark
, _position
):
591 QtCore
.QObject
.__init
__(self
)
592 if isinstance(_caption
, str):
593 _caption
= _caption
.decode('utf-8')
594 self
._caption
= _caption
596 self
._bookmark
= _bookmark
597 self
._position
= _position
599 changed
= QtCore
.Signal()
604 def _get_caption(self
):
607 def _get_bookmark(self
):
608 return self
._bookmark
610 def _get_position(self
):
611 return self
._position
613 item_id
= QtCore
.Property(str, _get_id
, notify
=changed
)
614 caption
= QtCore
.Property(unicode, _get_caption
, notify
=changed
)
615 bookmark_id
= QtCore
.Property(str, _get_bookmark
, notify
=changed
)
616 position
= QtCore
.Property(str, _get_position
, notify
=changed
)
618 class FilechooserItem(QtCore
.QObject
):
619 def __init__(self
, _caption
, _path
, _directory
):
620 QtCore
.QObject
.__init
__(self
)
621 if isinstance(_caption
, str):
622 _caption
= _caption
.decode('utf-8')
623 if isinstance(_path
, str):
624 _path
= _path
.decode('utf-8')
626 self
._caption
= _caption
628 self
._directory
= _directory
630 changed
= QtCore
.Signal()
632 def _get_caption(self
):
638 def _get_directory(self
):
639 return self
._directory
641 caption
= QtCore
.Property(unicode, _get_caption
, notify
=changed
)
642 path
= QtCore
.Property(unicode, _get_path
, notify
=changed
)
643 directory
= QtCore
.Property(bool, _get_directory
, notify
=changed
)
645 class ThemeController(QtCore
.QObject
):
646 def __init__(self
, config
):
647 QtCore
.QObject
.__init
__(self
)
650 self
.config_theme
= ConfigParser
.SafeConfigParser()
651 #_file = open(util.find_data_file("theme-all.conf"))
652 #self.config.readfp(_file)
654 _file
= open(panucci
.THEME_FILE
)
655 self
.config_theme
.readfp(_file
)
659 def set_theme(self
, theme
):
660 self
.config
.set("options", "theme", theme
.strip().lower())
663 def get_themes(self
):
664 self
.sections
= self
.config_theme
.sections()
665 self
.sections
.sort(lambda x
,y
: cmp (x
.lower(), y
.lower()))
668 def _get_background(self
):
669 return "#" + self
.config_theme
.get(self
.config
.get("options", "theme"), "background")
671 def _get_foreground(self
):
672 return "#" + self
.config_theme
.get(self
.config
.get("options", "theme"), "foreground")
674 def _get_highlight(self
):
675 return "#" + self
.config_theme
.get(self
.config
.get("options", "theme"), "highlight")
677 def _get_button_color(self
):
678 return "#" + self
.config_theme
.get(self
.config
.get("options", "theme"), "button_color")
680 def _get_button_border_color(self
):
681 return "#" + self
.config_theme
.get(self
.config
.get("options", "theme"), "button_border_color")
683 def _get_progress_color(self
):
684 return "#" + self
.config_theme
.get(self
.config
.get("options", "theme"), "progress_color")
686 def _get_progress_bg_color(self
):
687 return "#" + self
.config_theme
.get(self
.config
.get("options", "theme"), "progress_background_color")
689 changed
= QtCore
.Signal()
690 background
= QtCore
.Property(str, _get_background
, notify
=changed
)
691 foreground
= QtCore
.Property(str, _get_foreground
, notify
=changed
)
692 highlight
= QtCore
.Property(str, _get_highlight
, notify
=changed
)
693 button_color
= QtCore
.Property(str, _get_button_color
, notify
=changed
)
694 button_border_color
= QtCore
.Property(str, _get_button_border_color
, notify
=changed
)
695 progress_color
= QtCore
.Property(str, _get_progress_color
, notify
=changed
)
696 progress_bg_color
= QtCore
.Property(str, _get_progress_bg_color
, notify
=changed
)