class PlayerControl(Gtk.Box): """ Provides info and controls for any active music or other media player. It is a a direct child of the AudioPanel, and is only shown if there is an active mpris interface we can connect to. """ def __init__(self): super(PlayerControl, self).__init__(orientation=Gtk.Orientation.HORIZONTAL) self.watcher = singletons.MediaPlayerWatcher self.player = self.watcher.get_best_player() if self.player: self.build_layout() def build_layout(self): size = Gtk.IconSize.from_name("audio-button") player_status = self.player.get_playback_status() # Player buttons button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) button_box.set_homogeneous(True) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 2) vbox.pack_start(button_box, True, True, 0) vbox.set_valign(Gtk.Align.CENTER) self.previous_button = TransparentButton("media-skip-backward-symbolic", size) self.previous_button.show() trackers.con_tracker_get().connect(self.previous_button, "clicked", self.on_previous_clicked) button_box.pack_start(self.previous_button, True, True, 2) self.play_pause_button = TransparentButton(self.get_play_pause_icon_name(player_status), size) self.play_pause_button.show() trackers.con_tracker_get().connect(self.play_pause_button, "clicked", self.on_play_pause_clicked) button_box.pack_start(self.play_pause_button, True, True, 2) self.next_button = TransparentButton("media-skip-forward-symbolic", size) self.next_button.show() trackers.con_tracker_get().connect(self.next_button, "clicked", self.on_next_clicked) button_box.pack_start(self.next_button, True, True, 2) self.update_buttons(player_status) status.focusWidgets = status.focusWidgets + [self.previous_button, self.play_pause_button, self.next_button] # Track info vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 6) vbox.set_valign(Gtk.Align.CENTER) self.track_name_label = MarqueeLabel("") self.track_name_label.get_style_context().add_class("trackname") vbox.pack_start(self.track_name_label, True, True, 2) self.album_artist_label = MarqueeLabel("") self.album_artist_label.get_style_context().add_class("albumartist") vbox.pack_end(self.album_artist_label, True, True, 2) self.show_all() trackers.con_tracker_get().connect(self.player, "status-changed", self.on_playback_status_changed) trackers.con_tracker_get().connect(self.player, "metadata-changed", self.on_metadata_changed) self.on_playback_status_changed(self.player, player_status) self.on_metadata_changed(self.player) trackers.con_tracker_get().connect(self, "destroy", self.on_widget_destroy) def on_previous_clicked(self, button, data=None): self.player.go_previous() def on_next_clicked(self, button, data=None): self.player.go_next() def on_play_pause_clicked(self, button, data=None): self.player.play_pause() def get_play_pause_icon_name(self, status): if status == PlaybackStatus.Playing: icon_name = "media-playback-pause-symbolic" else: icon_name = "media-playback-start-symbolic" return icon_name def on_playback_status_changed(self, player, status, data=None): self.update_buttons(status) def on_metadata_changed(self, player): """ Update labels when the player metadata changes """ self.update_labels() def update_labels(self): """ Construct the track and artist-album labels as well as possible. """ self.track_name_label.set_text(self.player.get_track_name()) artist_name = self.player.get_artist_name() album_name = self.player.get_album_name() if artist_name != "" and album_name != "": self.album_artist_label.set_text("%s - %s" % (self.player.get_artist_name(), self.player.get_album_name())) elif artist_name != "": self.album_artist_label.set_text(artist_name) elif album_name != "": self.album_artist_label.set_text(album_name) else: self.album_artist_label.set_text("") def update_buttons(self, status): """ Updates the player buttons based on the current state """ self.play_pause_button.set_sensitive(self.player.get_can_play_pause()) self.next_button.set_sensitive(self.player.get_can_go_next()) self.previous_button.set_sensitive(self.player.get_can_go_previous()) icon_name = self.get_play_pause_icon_name(status) size = Gtk.IconSize.from_name("audio-button") image = Gtk.Image.new_from_icon_name(icon_name, size) self.play_pause_button.set_image(image) def on_widget_destroy(self, widget, data=None): trackers.con_tracker_get().disconnect(self.player, "status-changed", self.on_playback_status_changed) trackers.con_tracker_get().disconnect(self, "destroy", self.on_widget_destroy) def should_show(self): """ Checked by the AudioPanel, whether or not this widget should be displayed. """ return self.player != None
class PlayerControl(Gtk.Box): """ Provides info and controls for any active music or other media player. It is a a direct child of the AudioPanel, and is only shown if there is an active mpris interface we can connect to. """ def __init__(self): super(PlayerControl, self).__init__(orientation=Gtk.Orientation.HORIZONTAL) self.watcher = singletons.MediaPlayerWatcher self.player = self.watcher.get_best_player() if self.player: self.build_layout() def build_layout(self): size = Gtk.IconSize.from_name("audio-button") player_status = self.player.get_playback_status() # Player buttons button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) button_box.set_homogeneous(True) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 2) vbox.pack_start(button_box, True, True, 0) vbox.set_valign(Gtk.Align.CENTER) self.previous_button = TransparentButton( "media-skip-backward-symbolic", size) self.previous_button.show() trackers.con_tracker_get().connect(self.previous_button, "clicked", self.on_previous_clicked) button_box.pack_start(self.previous_button, True, True, 2) self.play_pause_button = TransparentButton( self.get_play_pause_icon_name(player_status), size) self.play_pause_button.show() trackers.con_tracker_get().connect(self.play_pause_button, "clicked", self.on_play_pause_clicked) button_box.pack_start(self.play_pause_button, True, True, 2) self.next_button = TransparentButton("media-skip-forward-symbolic", size) self.next_button.show() trackers.con_tracker_get().connect(self.next_button, "clicked", self.on_next_clicked) button_box.pack_start(self.next_button, True, True, 2) self.update_buttons(player_status) status.focusWidgets = status.focusWidgets + [ self.previous_button, self.play_pause_button, self.next_button ] # Position labels and bar vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 4) vbox.set_valign(Gtk.Align.CENTER) position_length_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) vbox.pack_start(position_length_box, True, True, 2) self.current_pos_label = BlinkingLabel("", 400) self.current_pos_label.get_style_context().add_class("positionlabel") position_length_box.pack_start(self.current_pos_label, False, False, 2) self.max_pos_label = BlinkingLabel("", 400) self.max_pos_label.get_style_context().add_class("positionlabel") position_length_box.pack_end(self.max_pos_label, False, False, 2) self.position_bar = PositionBar() vbox.pack_end(self.position_bar, True, True, 2) # Track info vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 6) vbox.set_valign(Gtk.Align.CENTER) self.track_name_label = MarqueeLabel("") self.track_name_label.get_style_context().add_class("trackname") vbox.pack_start(self.track_name_label, True, True, 2) self.album_artist_label = MarqueeLabel("") self.album_artist_label.get_style_context().add_class("albumartist") vbox.pack_end(self.album_artist_label, True, True, 2) self.show_all() trackers.con_tracker_get().connect(self.player, "position-changed", self.on_position_changed) trackers.con_tracker_get().connect(self.player, "status-changed", self.on_playback_status_changed) trackers.con_tracker_get().connect(self.player, "metadata-changed", self.on_metadata_changed) self.on_playback_status_changed(self.player, player_status) self.on_metadata_changed(self.player) trackers.con_tracker_get().connect(self, "destroy", self.on_widget_destroy) self.update_position_timer(player_status) def on_previous_clicked(self, button, data=None): self.player.go_previous() def on_next_clicked(self, button, data=None): self.player.go_next() def on_play_pause_clicked(self, button, data=None): self.player.play_pause() def get_play_pause_icon_name(self, status): if status == PlaybackStatus.Playing: icon_name = "media-playback-pause-symbolic" else: icon_name = "media-playback-start-symbolic" return icon_name def position_to_time_string(self, position): """ We receive track position and track length values in microseconds. This function formats this into readable HH:MM:SS format, and handles any invalid values. """ delta = datetime.timedelta(microseconds=position) duration = datetime.datetime.utcfromtimestamp(delta.total_seconds()) if duration.hour < 0: return _("--:--") if duration.hour < 1: return duration.strftime(_("%M:%S")) else: return duration.strftime(_("%H:%M:%S")) def on_playback_status_changed(self, player, status, data=None): self.update_buttons(status) self.update_position_timer(status) self.update_position_display() self.update_position_values_appearance(status) def update_position_values_appearance(self, status): """ When the player is paused, we blink the position/length values. """ if status == PlaybackStatus.Paused: self.max_pos_label.set_blinking(True) self.current_pos_label.set_blinking(True) else: self.max_pos_label.set_blinking(False) self.current_pos_label.set_blinking(False) def on_metadata_changed(self, player): """ Update max position and labels when the player metadata changes """ self.max_pos_label.set_text( self.position_to_time_string(self.player.get_max_position())) self.update_labels() def update_labels(self): """ Construct the track and artist-album labels as well as possible. """ self.track_name_label.set_text(self.player.get_track_name()) artist_name = self.player.get_artist_name() album_name = self.player.get_album_name() if artist_name != "" and album_name != "": self.album_artist_label.set_text( "%s - %s" % (self.player.get_artist_name(), self.player.get_album_name())) elif artist_name != "": self.album_artist_label.set_text(artist_name) elif album_name != "": self.album_artist_label.set_text(album_name) else: self.album_artist_label.set_text("") def update_buttons(self, status): """ Updates the player buttons based on the current state """ self.play_pause_button.set_sensitive(self.player.get_can_play_pause()) self.next_button.set_sensitive(self.player.get_can_go_next()) self.previous_button.set_sensitive(self.player.get_can_go_previous()) icon_name = self.get_play_pause_icon_name(status) size = Gtk.IconSize.from_name("audio-button") image = Gtk.Image.new_from_icon_name(icon_name, size) self.play_pause_button.set_image(image) def update_position_display(self): """ Updates the position values and bar to reflect the current state. """ if self.player.get_position() < self.player.get_max_position(): value = self.player.get_position() / self.player.get_max_position() else: value = 1.0 value = utils.CLAMP(value, 0.0, 1.0) self.position_bar.set_fraction(value) self.current_pos_label.set_text( self.position_to_time_string(self.player.get_position())) return True def update_position_timer(self, status): """ Starts or stops the position update timer - this is based upon the provided rate property of the player, which is defined as the recommended update frequency for position data. """ if status == PlaybackStatus.Playing: trackers.timer_tracker_get().start("position-timer", self.player.get_rate() * 1000, self.update_position_display) else: trackers.timer_tracker_get().cancel("position-timer") def on_position_changed(self, player, position, data=None): """ Callback for an explicit position change from the player. """ self.update_position_display() def on_widget_destroy(self, widget, data=None): trackers.con_tracker_get().disconnect(self.player, "position-changed", self.on_position_changed) trackers.con_tracker_get().disconnect(self.player, "status-changed", self.on_playback_status_changed) trackers.con_tracker_get().disconnect(self, "destroy", self.on_widget_destroy) trackers.timer_tracker_get().cancel("position-timer") def should_show(self): """ Checked by the AudioPanel, whether or not this widget should be displayed. """ return self.player != None
class PlayerControl(Gtk.Box): def __init__(self): super(PlayerControl, self).__init__(orientation=Gtk.Orientation.HORIZONTAL) self.watcher = singletons.MediaPlayerWatcher self.player = self.watcher.get_best_player() if self.player: self.build_layout() def build_layout(self): size = Gtk.IconSize.from_name("audio-button") # size = Gtk.IconSize.MENU player_status = self.player.get_playback_status() # Player buttons self.pack_start(Gtk.VSeparator(), True, True, 2) button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) button_box.set_homogeneous(True) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 2) vbox.pack_start(button_box, True, True, 0) vbox.set_valign(Gtk.Align.CENTER) self.previous_button = TransparentButton("media-skip-backward-symbolic", size) self.previous_button.show() trackers.con_tracker_get().connect(self.previous_button, "clicked", self.on_previous_clicked) button_box.pack_start(self.previous_button, True, True, 2) self.play_pause_button = TransparentButton(self.get_play_pause_icon_name(player_status), size) self.play_pause_button.show() trackers.con_tracker_get().connect(self.play_pause_button, "clicked", self.on_play_pause_clicked) button_box.pack_start(self.play_pause_button, True, True, 2) self.next_button = TransparentButton("media-skip-forward-symbolic", size) self.next_button.show() trackers.con_tracker_get().connect(self.next_button, "clicked", self.on_next_clicked) button_box.pack_start(self.next_button, True, True, 2) self.update_buttons(player_status) status.focusWidgets = status.focusWidgets + [self.previous_button, self.play_pause_button, self.next_button] # Position labels and bar self.pack_start(Gtk.VSeparator(), True, True, 2) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 4) vbox.set_valign(Gtk.Align.CENTER) position_length_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) vbox.pack_start(position_length_box, True, True, 2) self.current_pos_label = BlinkingLabel("", 400) self.current_pos_label.get_style_context().add_class("positionlabel") position_length_box.pack_start(self.current_pos_label, False, False, 2) self.max_pos_label = BlinkingLabel("", 400) self.max_pos_label.get_style_context().add_class("positionlabel") position_length_box.pack_end(self.max_pos_label, False, False, 2) self.position_bar = PositionBar() vbox.pack_end(self.position_bar, True, True, 2) # Track info self.pack_start(Gtk.VSeparator(), True, True, 2) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 6) vbox.set_valign(Gtk.Align.CENTER) self.track_name_label = MarqueeLabel("") self.track_name_label.get_style_context().add_class("trackname") vbox.pack_start(self.track_name_label, True, True, 2) self.album_artist_label = MarqueeLabel("") self.album_artist_label.get_style_context().add_class("albumartist") vbox.pack_end(self.album_artist_label, True, True, 2) self.show_all() trackers.con_tracker_get().connect(self.player, "position-changed", self.on_position_changed) trackers.con_tracker_get().connect(self.player, "status-changed", self.on_playback_status_changed) trackers.con_tracker_get().connect(self.player, "metadata-changed", self.on_metadata_changed) self.on_playback_status_changed(self.player, player_status) self.on_metadata_changed(self.player) trackers.con_tracker_get().connect(self, "destroy", self.on_widget_destroy) self.update_position_timer(player_status) def on_previous_clicked(self, button, data=None): self.player.go_previous() def on_next_clicked(self, button, data=None): self.player.go_next() def on_play_pause_clicked(self, button, data=None): self.player.play_pause() def get_play_pause_icon_name(self, status): if status == PlaybackStatus.Playing: icon_name = "media-playback-pause-symbolic" else: icon_name = "media-playback-start-symbolic" return icon_name def position_to_time_string(self, position): delta = datetime.timedelta(microseconds=position) duration = datetime.datetime.utcfromtimestamp(delta.total_seconds()) if duration.hour < 0: return _("--:--") if duration.hour < 1: return duration.strftime(_("%M:%S")) else: return duration.strftime(_("%H:%M:%S")) def on_playback_status_changed(self, player, status, data=None): self.update_buttons(status) self.update_position_timer(status) self.update_position_display() self.update_position_values_appearance(status) def update_position_values_appearance(self, status): if status == PlaybackStatus.Paused: self.max_pos_label.set_blinking(True) self.current_pos_label.set_blinking(True) else: self.max_pos_label.set_blinking(False) self.current_pos_label.set_blinking(False) def on_metadata_changed(self, player): self.max_pos_label.set_text(self.position_to_time_string(self.player.get_max_position())) self.update_labels() def update_labels(self): self.track_name_label.set_text(self.player.get_track_name()) artist_name = self.player.get_artist_name() album_name = self.player.get_album_name() if artist_name != "" and album_name != "": self.album_artist_label.set_text("%s - %s" % (self.player.get_artist_name(), self.player.get_album_name())) elif artist_name != "": self.album_artist_label.set_text(artist_name) elif album_name != "": self.album_artist_label.set_text(album_name) else: self.album_artist_label.set_text("") def pause_blink_step(self): self.max_pos_label.set_visible(not self.max_pos_label.get_visible()) self.current_pos_label.set_visible(not self.current_pos_label.get_visible()) return True def update_buttons(self, status): self.play_pause_button.set_sensitive(self.player.get_can_play_pause()) self.next_button.set_sensitive(self.player.get_can_go_next()) self.previous_button.set_sensitive(self.player.get_can_go_previous()) icon_name = self.get_play_pause_icon_name(status) size = Gtk.IconSize.from_name("audio-button") image = Gtk.Image.new_from_icon_name(icon_name, size) self.play_pause_button.set_image(image) def update_position_display(self): if self.player.get_position() < self.player.get_max_position(): value = self.player.get_position() / self.player.get_max_position() else: value = 1.0 value = utils.CLAMP(value, 0.0, 1.0) self.position_bar.set_fraction(value) self.current_pos_label.set_text(self.position_to_time_string(self.player.get_position())) return True def update_position_timer(self, status): if status == PlaybackStatus.Playing: trackers.timer_tracker_get().start("position-timer", self.player.get_rate() * 1000, self.update_position_display) else: trackers.timer_tracker_get().cancel("position-timer") def on_position_changed(self, player, position, data=None): self.update_position_display() def on_widget_destroy(self, widget, data=None): trackers.con_tracker_get().disconnect(self.player, "position-changed", self.on_position_changed) trackers.con_tracker_get().disconnect(self.player, "status-changed", self.on_playback_status_changed) trackers.con_tracker_get().disconnect(self, "destroy", self.on_widget_destroy) trackers.timer_tracker_get().cancel("position-timer") def should_show(self): return self.player != None
class PlayerControl(Gtk.Box): """ Provides info and controls for any active music or other media player. It is a a direct child of the AudioPanel, and is only shown if there is an active mpris interface we can connect to. """ def __init__(self): super(PlayerControl, self).__init__(orientation=Gtk.Orientation.HORIZONTAL) self.watcher = singletons.MediaPlayerWatcher self.player = self.watcher.get_best_player() if self.player: self.build_layout() def build_layout(self): size = Gtk.IconSize.from_name("audio-button") player_status = self.player.get_playback_status() # Player buttons self.pack_start(Gtk.VSeparator(), True, True, 2) button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) button_box.set_homogeneous(True) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 2) vbox.pack_start(button_box, True, True, 0) vbox.set_valign(Gtk.Align.CENTER) self.previous_button = TransparentButton("media-skip-backward-symbolic", size) self.previous_button.show() trackers.con_tracker_get().connect(self.previous_button, "clicked", self.on_previous_clicked) button_box.pack_start(self.previous_button, True, True, 2) self.play_pause_button = TransparentButton(self.get_play_pause_icon_name(player_status), size) self.play_pause_button.show() trackers.con_tracker_get().connect(self.play_pause_button, "clicked", self.on_play_pause_clicked) button_box.pack_start(self.play_pause_button, True, True, 2) self.next_button = TransparentButton("media-skip-forward-symbolic", size) self.next_button.show() trackers.con_tracker_get().connect(self.next_button, "clicked", self.on_next_clicked) button_box.pack_start(self.next_button, True, True, 2) self.update_buttons(player_status) status.focusWidgets = status.focusWidgets + [self.previous_button, self.play_pause_button, self.next_button] # Position labels and bar self.pack_start(Gtk.VSeparator(), True, True, 2) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 4) vbox.set_valign(Gtk.Align.CENTER) position_length_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) vbox.pack_start(position_length_box, True, True, 2) self.current_pos_label = BlinkingLabel("", 400) self.current_pos_label.get_style_context().add_class("positionlabel") position_length_box.pack_start(self.current_pos_label, False, False, 2) self.max_pos_label = BlinkingLabel("", 400) self.max_pos_label.get_style_context().add_class("positionlabel") position_length_box.pack_end(self.max_pos_label, False, False, 2) self.position_bar = PositionBar() vbox.pack_end(self.position_bar, True, True, 2) # Track info self.pack_start(Gtk.VSeparator(), True, True, 2) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 6) vbox.set_valign(Gtk.Align.CENTER) self.track_name_label = MarqueeLabel("") self.track_name_label.get_style_context().add_class("trackname") vbox.pack_start(self.track_name_label, True, True, 2) self.album_artist_label = MarqueeLabel("") self.album_artist_label.get_style_context().add_class("albumartist") vbox.pack_end(self.album_artist_label, True, True, 2) self.show_all() trackers.con_tracker_get().connect(self.player, "position-changed", self.on_position_changed) trackers.con_tracker_get().connect(self.player, "status-changed", self.on_playback_status_changed) trackers.con_tracker_get().connect(self.player, "metadata-changed", self.on_metadata_changed) self.on_playback_status_changed(self.player, player_status) self.on_metadata_changed(self.player) trackers.con_tracker_get().connect(self, "destroy", self.on_widget_destroy) self.update_position_timer(player_status) def on_previous_clicked(self, button, data=None): self.player.go_previous() def on_next_clicked(self, button, data=None): self.player.go_next() def on_play_pause_clicked(self, button, data=None): self.player.play_pause() def get_play_pause_icon_name(self, status): if status == PlaybackStatus.Playing: icon_name = "media-playback-pause-symbolic" else: icon_name = "media-playback-start-symbolic" return icon_name def position_to_time_string(self, position): """ We receive track position and track length values in microseconds. This function formats this into readable HH:MM:SS format, and handles any invalid values. """ delta = datetime.timedelta(microseconds=position) duration = datetime.datetime.utcfromtimestamp(delta.total_seconds()) if duration.hour < 0: return _("--:--") if duration.hour < 1: return duration.strftime(_("%M:%S")) else: return duration.strftime(_("%H:%M:%S")) def on_playback_status_changed(self, player, status, data=None): self.update_buttons(status) self.update_position_timer(status) self.update_position_display() self.update_position_values_appearance(status) def update_position_values_appearance(self, status): """ When the player is paused, we blink the position/length values. """ if status == PlaybackStatus.Paused: self.max_pos_label.set_blinking(True) self.current_pos_label.set_blinking(True) else: self.max_pos_label.set_blinking(False) self.current_pos_label.set_blinking(False) def on_metadata_changed(self, player): """ Update max position and labels when the player metadata changes """ self.max_pos_label.set_text(self.position_to_time_string(self.player.get_max_position())) self.update_labels() def update_labels(self): """ Construct the track and artist-album labels as well as possible. """ self.track_name_label.set_text(self.player.get_track_name()) artist_name = self.player.get_artist_name() album_name = self.player.get_album_name() if artist_name != "" and album_name != "": self.album_artist_label.set_text("%s - %s" % (self.player.get_artist_name(), self.player.get_album_name())) elif artist_name != "": self.album_artist_label.set_text(artist_name) elif album_name != "": self.album_artist_label.set_text(album_name) else: self.album_artist_label.set_text("") def update_buttons(self, status): """ Updates the player buttons based on the current state """ self.play_pause_button.set_sensitive(self.player.get_can_play_pause()) self.next_button.set_sensitive(self.player.get_can_go_next()) self.previous_button.set_sensitive(self.player.get_can_go_previous()) icon_name = self.get_play_pause_icon_name(status) size = Gtk.IconSize.from_name("audio-button") image = Gtk.Image.new_from_icon_name(icon_name, size) self.play_pause_button.set_image(image) def update_position_display(self): """ Updates the position values and bar to reflect the current state. """ if self.player.get_position() < self.player.get_max_position(): value = self.player.get_position() / self.player.get_max_position() else: value = 1.0 value = utils.CLAMP(value, 0.0, 1.0) self.position_bar.set_fraction(value) self.current_pos_label.set_text(self.position_to_time_string(self.player.get_position())) return True def update_position_timer(self, status): """ Starts or stops the position update timer - this is based upon the provided rate property of the player, which is defined as the recommended update frequency for position data. """ if status == PlaybackStatus.Playing: trackers.timer_tracker_get().start("position-timer", self.player.get_rate() * 1000, self.update_position_display) else: trackers.timer_tracker_get().cancel("position-timer") def on_position_changed(self, player, position, data=None): """ Callback for an explicit position change from the player. """ self.update_position_display() def on_widget_destroy(self, widget, data=None): trackers.con_tracker_get().disconnect(self.player, "position-changed", self.on_position_changed) trackers.con_tracker_get().disconnect(self.player, "status-changed", self.on_playback_status_changed) trackers.con_tracker_get().disconnect(self, "destroy", self.on_widget_destroy) trackers.timer_tracker_get().cancel("position-timer") def should_show(self): """ Checked by the AudioPanel, whether or not this widget should be displayed. """ return self.player != None
class UnlockDialog(BaseWindow): """ The main widget for the unlock dialog - this is a direct child of the Stage's GtkOverlay. It has a number of parts, namely: - The user face image. - The user's real name (or username if the real name is unavailable) - The password entry widget - Unlock and Switch User buttons - A caps lock warning label - An invalid password error label """ __gsignals__ = { 'inhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()), 'uninhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()), 'auth-success': (GObject.SignalFlags.RUN_LAST, None, ()), 'auth-failure': (GObject.SignalFlags.RUN_LAST, None, ()) } def __init__(self): super(UnlockDialog, self).__init__() self.set_halign(Gtk.Align.CENTER) self.set_valign(Gtk.Align.CENTER) self.set_size_request(350, -1) self.real_name = None self.user_name = None self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) self.box.get_style_context().add_class("unlockbox") self.add(self.box) self.face_image = FramedImage() self.face_image.set_halign(Gtk.Align.CENTER) self.face_image.get_style_context().add_class("faceimage") self.face_image.set_no_show_all(True) self.box.pack_start(self.face_image, False, False, 10) self.realname_label = Gtk.Label(None) self.realname_label.set_alignment(0, 0.5) self.realname_label.set_halign(Gtk.Align.CENTER) self.box.pack_start(self.realname_label, False, False, 10) self.entry_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.box.pack_start(self.entry_box, True, True, 2) self.password_entry = PasswordEntry() trackers.con_tracker_get().connect(self.password_entry, "changed", self.on_password_entry_text_changed) trackers.con_tracker_get().connect(self.password_entry, "button-press-event", self.on_password_entry_button_press) trackers.con_tracker_get().connect(self.password_entry, "activate", self.on_auth_enter_key) self.entry_box.pack_start(self.password_entry, True, True, 15) button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.entry_box.pack_end(button_box, False, False, 0) self.auth_unlock_button = TransparentButton( "screensaver-unlock-symbolic", Gtk.IconSize.LARGE_TOOLBAR) self.auth_unlock_button.set_sensitive(False) trackers.con_tracker_get().connect(self.auth_unlock_button, "clicked", self.on_unlock_clicked) button_box.pack_start(self.auth_unlock_button, False, False, 4) self.auth_switch_button = TransparentButton( "screensaver-switch-users-symbolic", Gtk.IconSize.LARGE_TOOLBAR) trackers.con_tracker_get().connect(self.auth_switch_button, "clicked", self.on_switch_user_clicked) button_box.pack_start(self.auth_switch_button, False, False, 4) status.focusWidgets = [ self.password_entry, self.auth_unlock_button, self.auth_switch_button ] vbox_messages = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2) self.capslock_label = Gtk.Label("") self.capslock_label.get_style_context().add_class("caps-message") self.capslock_label.set_alignment(0.5, 0.5) vbox_messages.pack_start(self.capslock_label, False, False, 2) self.auth_message_label = Gtk.Label("") self.auth_message_label.get_style_context().add_class("auth-message") self.auth_message_label.set_alignment(0.5, 0.5) vbox_messages.pack_start(self.auth_message_label, False, False, 2) self.box.pack_start(vbox_messages, False, False, 0) self.real_name = utils.get_user_display_name() self.user_name = utils.get_user_name() self.update_realname_label() self.account_client = singletons.AccountsServiceClient if self.account_client.is_loaded: self.on_account_client_loaded(self.account_client) else: trackers.con_tracker_get().connect(self.account_client, "account-loaded", self.on_account_client_loaded) self.keymap = Gdk.Keymap.get_default() trackers.con_tracker_get().connect(self.keymap, "state-changed", self.keymap_handler) trackers.con_tracker_get().connect_after(self, "notify::child-revealed", self.on_revealed) def cancel(self): """ Clears the auth message text if we have any. """ self.auth_message_label.set_text("") def on_revealed(self, widget, child): """ Updates the capslock state and ensures the password is cleared when we're first shown. """ if self.get_child_revealed(): self.keymap_handler(self.keymap) else: self.password_entry.set_text("") def queue_key_event(self, event): """ Takes a propagated key event from the stage and passes it to the entry widget, possibly queueing up the first character of the password. """ if not self.password_entry.get_realized(): self.password_entry.realize() self.password_entry.event(event) def keymap_handler(self, keymap): """ Handler for the GdkKeymap changing - updates our capslock indicator label. """ if keymap.get_caps_lock_state(): self.capslock_label.set_text(_("You have the Caps Lock key on.")) else: self.capslock_label.set_text("") def on_account_client_loaded(self, client): """ Handler for the AccountsService - requests the user real name and .face image. """ if client.get_real_name() != None: self.real_name = client.get_real_name() self.update_realname_label() if client.get_face_path() != None: self.face_image.set_from_path(client.get_face_path()) self.face_image.show() def on_password_entry_text_changed(self, editable): """ Handler for the password entry text changing - this controls the sensitivity of the unlock button, as well as returning visual focus to the entry any time a key event is received. """ if not self.password_entry.has_focus(): self.password_entry.grab_focus() self.auth_unlock_button.set_sensitive(editable.get_text() != "") def on_password_entry_button_press(self, widget, event): """ Prevents the usual copy/paste popup when right-clicking the PasswordEntry. """ if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS: return Gdk.EVENT_STOP return Gdk.EVENT_PROPAGATE def on_unlock_clicked(self, button=None): """ Callback for the unlock button. Activates the 'progress' animation in the GtkEntry, and attempts to authenticate the password. During this time, we also inhibit the unlock timeout, so we don't fade out while waiting for an authentication result (highly unlikely.) """ self.emit("inhibit-timeout") text = self.password_entry.get_text() self.password_entry.start_progress() self.password_entry.set_placeholder_text(_("Checking...")) self.authenticate(text) def on_auth_enter_key(self, widget): """ Implicitly activates the unlock button when the Enter/Return key is pressed. """ if widget.get_text() == "": return self.on_unlock_clicked() def on_switch_user_clicked(self, widget): """ Callback for the switch-user button. """ utils.do_user_switch() def clear_entry(self): """ Clear the password entry widget. """ self.password_entry.set_text("") def update_realname_label(self): """ Updates the name label to the current real_name. """ self.realname_label.set_text(self.real_name) def authenticate(self, password): """ Authenticates the entered password against the system PAM provider. """ CinnamonDesktop.desktop_check_user_password(self.user_name, password, self.authenticate_callback) def authenticate_callback(self, success, data=None): """ Callback for check_user_password. Send this back to the Stage so it can react how it needs to (destroying itself or flashing the unlock widget on failure.) """ self.password_entry.stop_progress() if success: self.clear_entry() self.emit("auth-success") else: self.authentication_failed() self.emit("auth-failure") def authentication_failed(self): """ Called upon authentication failure, clears the password, sets an error message, and refocuses the password entry. """ self.clear_entry() self.password_entry.set_placeholder_text( _("Please enter your password...")) self.auth_message_label.set_text(_("Incorrect password")) self.password_entry.grab_focus() self.emit("uninhibit-timeout")
class UnlockDialog(BaseWindow): __gsignals__ = { 'inhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()), 'uninhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()), 'auth-success': (GObject.SignalFlags.RUN_LAST, None, ()), 'auth-failure': (GObject.SignalFlags.RUN_LAST, None, ()) } def __init__(self): super(UnlockDialog, self).__init__() self.set_halign(Gtk.Align.CENTER) self.set_valign(Gtk.Align.CENTER) self.set_size_request(350, -1) self.real_name = None self.user_name = None self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) self.box.get_style_context().add_class("unlockbox") self.add(self.box) self.face_image = FramedImage() self.face_image.set_halign(Gtk.Align.CENTER) self.box.pack_start(self.face_image, False, False, 10) self.realname_label = Gtk.Label(None) self.realname_label.set_alignment(0, 0.5) self.realname_label.set_halign(Gtk.Align.CENTER) self.box.pack_start(self.realname_label, False, False, 10) self.entry_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.box.pack_start(self.entry_box, True, True, 2) self.password_entry = PasswordEntry() trackers.con_tracker_get().connect(self.password_entry, "changed", self.on_password_entry_text_changed) trackers.con_tracker_get().connect(self.password_entry, "button-press-event", self.on_password_entry_button_press) trackers.con_tracker_get().connect(self.password_entry, "activate", self.on_auth_enter_key) self.entry_box.pack_start(self.password_entry, True, True, 15) button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.entry_box.pack_end(button_box, False, False, 0) self.auth_unlock_button = TransparentButton( "screensaver-unlock-symbolic", Gtk.IconSize.LARGE_TOOLBAR) self.auth_unlock_button.set_sensitive(False) trackers.con_tracker_get().connect(self.auth_unlock_button, "clicked", self.on_unlock_clicked) button_box.pack_start(self.auth_unlock_button, False, False, 4) self.auth_switch_button = TransparentButton( "screensaver-switch-users-symbolic", Gtk.IconSize.LARGE_TOOLBAR) trackers.con_tracker_get().connect(self.auth_switch_button, "clicked", self.on_switch_user_clicked) button_box.pack_start(self.auth_switch_button, False, False, 4) status.focusWidgets = [ self.password_entry, self.auth_unlock_button, self.auth_switch_button ] vbox_messages = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2) self.capslock_label = Gtk.Label("") self.capslock_label.get_style_context().add_class("caps-message") self.capslock_label.set_alignment(0.5, 0.5) vbox_messages.pack_start(self.capslock_label, False, False, 2) self.auth_message_label = Gtk.Label("") self.auth_message_label.get_style_context().add_class("auth-message") self.auth_message_label.set_alignment(0.5, 0.5) vbox_messages.pack_start(self.auth_message_label, False, False, 2) self.box.pack_start(vbox_messages, False, False, 0) self.real_name = utils.get_user_display_name() self.user_name = utils.get_user_name() self.update_realname_label() global acc_service if acc_service is not None: self.on_accounts_service_loaded(acc_service, None) else: acc_service = AccountsService.UserManager.get_default().get_user( self.user_name) trackers.con_tracker_get().connect(acc_service, "notify::is-loaded", self.on_accounts_service_loaded) self.keymap = Gdk.Keymap.get_default() trackers.con_tracker_get().connect(self.keymap, "state-changed", self.keymap_handler) trackers.con_tracker_get().connect_after(self, "notify::child-revealed", self.on_revealed) def cancel(self): self.auth_message_label.set_text("") def on_revealed(self, widget, child): if self.get_child_revealed(): self.keymap_handler(self.keymap) else: self.password_entry.set_text("") def queue_key_event(self, event): if not self.password_entry.get_realized(): self.password_entry.realize() self.password_entry.event(event) def keymap_handler(self, keymap): if keymap.get_caps_lock_state(): self.capslock_label.set_text(_("You have the Caps Lock key on.")) else: self.capslock_label.set_text("") def on_accounts_service_loaded(self, service, param): self.real_name = service.get_real_name() self.update_realname_label() for path in [ os.path.join(service.get_home_dir(), ".face"), service.get_icon_file(), "/usr/share/cinnamon/faces/user-generic.png" ]: if os.path.exists(path): self.face_image.set_from_file(path) break def on_password_entry_text_changed(self, editable): if not self.password_entry.has_focus(): self.password_entry.grab_focus() self.auth_unlock_button.set_sensitive(editable.get_text() != "") def on_password_entry_button_press(self, widget, event): if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS: return Gdk.EVENT_STOP return Gdk.EVENT_PROPAGATE def on_unlock_clicked(self, button=None): self.emit("inhibit-timeout") text = self.password_entry.get_text() self.start_progress() self.password_entry.set_placeholder_text(_("Checking...")) self.authenticate(text) def on_auth_enter_key(self, widget): if widget.get_text() == "": return self.on_unlock_clicked() def on_switch_user_clicked(self, widget): utils.do_user_switch() def pulse(self): self.password_entry.progress_pulse() return True def start_progress(self): self.password_entry.set_progress_pulse_step(0.2) trackers.timer_tracker_get().start("auth-progress", 100, self.pulse) def stop_progress(self): trackers.timer_tracker_get().cancel("auth-progress") self.password_entry.set_progress_fraction(0.0) def clear_entry(self): self.password_entry.set_text("") def update_realname_label(self): self.realname_label.set_text(self.real_name) def authenticate(self, password): CinnamonDesktop.desktop_check_user_password(self.user_name, password, self.authenticate_callback) def authenticate_callback(self, success, data=None): self.stop_progress() if success: self.clear_entry() self.emit("auth-success") else: self.authentication_failed() self.emit("auth-failure") def authentication_failed(self): self.clear_entry() self.password_entry.set_placeholder_text(_("Enter password...")) self.auth_message_label.set_text(_("Password incorrect - try again.")) self.password_entry.grab_focus() self.emit("uninhibit-timeout")
class UnlockDialog(BaseWindow): """ The main widget for the unlock dialog - this is a direct child of the Stage's GtkOverlay. It has a number of parts, namely: - The user face image. - The user's real name (or username if the real name is unavailable) - The password entry widget - Unlock and Switch User buttons - A caps lock warning label - An invalid password error label """ __gsignals__ = { 'inhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()), 'uninhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()), 'auth-success': (GObject.SignalFlags.RUN_LAST, None, ()), 'auth-failure': (GObject.SignalFlags.RUN_LAST, None, ()) } def __init__(self): super(UnlockDialog, self).__init__() self.set_halign(Gtk.Align.CENTER) self.set_valign(Gtk.Align.CENTER) self.set_size_request(350, -1) self.real_name = None self.user_name = None self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) self.box.get_style_context().add_class("unlockbox") self.add(self.box) self.face_image = FramedImage() self.face_image.set_halign(Gtk.Align.CENTER) self.face_image.get_style_context().add_class("faceimage") self.face_image.set_no_show_all(True) self.box.pack_start(self.face_image, False, False, 10) self.realname_label = Gtk.Label(None) self.realname_label.set_alignment(0, 0.5) self.realname_label.set_halign(Gtk.Align.CENTER) self.box.pack_start(self.realname_label, False, False, 10) self.entry_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.box.pack_start(self.entry_box, True, True, 2) self.password_entry = PasswordEntry() trackers.con_tracker_get().connect(self.password_entry, "changed", self.on_password_entry_text_changed) trackers.con_tracker_get().connect(self.password_entry, "button-press-event", self.on_password_entry_button_press) trackers.con_tracker_get().connect(self.password_entry, "activate", self.on_auth_enter_key) self.entry_box.pack_start(self.password_entry, True, True, 15) button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.entry_box.pack_end(button_box, False, False, 0) self.auth_unlock_button = TransparentButton("screensaver-unlock-symbolic", Gtk.IconSize.LARGE_TOOLBAR) self.auth_unlock_button.set_sensitive(False) trackers.con_tracker_get().connect(self.auth_unlock_button, "clicked", self.on_unlock_clicked) button_box.pack_start(self.auth_unlock_button, False, False, 4) self.auth_switch_button = TransparentButton("screensaver-switch-users-symbolic", Gtk.IconSize.LARGE_TOOLBAR) trackers.con_tracker_get().connect(self.auth_switch_button, "clicked", self.on_switch_user_clicked) button_box.pack_start(self.auth_switch_button, False, False, 4) status.focusWidgets = [self.password_entry, self.auth_unlock_button, self.auth_switch_button] vbox_messages = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2) self.capslock_label = Gtk.Label("") self.capslock_label.get_style_context().add_class("caps-message") self.capslock_label.set_alignment(0.5, 0.5) vbox_messages.pack_start(self.capslock_label, False, False, 2) self.auth_message_label = Gtk.Label("") self.auth_message_label.get_style_context().add_class("auth-message") self.auth_message_label.set_alignment(0.5, 0.5) vbox_messages.pack_start(self.auth_message_label, False, False, 2) self.box.pack_start(vbox_messages, False, False, 0) self.real_name = utils.get_user_display_name() self.user_name = utils.get_user_name() self.update_realname_label() self.account_client = singletons.AccountsServiceClient if self.account_client.is_loaded: self.on_account_client_loaded(self.account_client) else: trackers.con_tracker_get().connect(self.account_client, "account-loaded", self.on_account_client_loaded) self.keymap = Gdk.Keymap.get_default() trackers.con_tracker_get().connect(self.keymap, "state-changed", self.keymap_handler) trackers.con_tracker_get().connect_after(self, "notify::child-revealed", self.on_revealed) def cancel(self): """ Clears the auth message text if we have any. """ self.auth_message_label.set_text("") def on_revealed(self, widget, child): """ Updates the capslock state and ensures the password is cleared when we're first shown. """ if self.get_child_revealed(): self.keymap_handler(self.keymap) else: self.password_entry.set_text("") def queue_key_event(self, event): """ Takes a propagated key event from the stage and passes it to the entry widget, possibly queueing up the first character of the password. """ if not self.password_entry.get_realized(): self.password_entry.realize() self.password_entry.event(event) def keymap_handler(self, keymap): """ Handler for the GdkKeymap changing - updates our capslock indicator label. """ if keymap.get_caps_lock_state(): self.capslock_label.set_text(_("You have the Caps Lock key on.")) else: self.capslock_label.set_text("") def on_account_client_loaded(self, client): """ Handler for the AccountsService - requests the user real name and .face image. """ if client.get_real_name() != None: self.real_name = client.get_real_name() self.update_realname_label() if client.get_face_path() != None: self.face_image.set_from_path(client.get_face_path()) self.face_image.show() def on_password_entry_text_changed(self, editable): """ Handler for the password entry text changing - this controls the sensitivity of the unlock button, as well as returning visual focus to the entry any time a key event is received. """ if not self.password_entry.has_focus(): self.password_entry.grab_focus() self.auth_unlock_button.set_sensitive(editable.get_text() != "") def on_password_entry_button_press(self, widget, event): """ Prevents the usual copy/paste popup when right-clicking the PasswordEntry. """ if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS: return Gdk.EVENT_STOP return Gdk.EVENT_PROPAGATE def on_unlock_clicked(self, button=None): """ Callback for the unlock button. Activates the 'progress' animation in the GtkEntry, and attempts to authenticate the password. During this time, we also inhibit the unlock timeout, so we don't fade out while waiting for an authentication result (highly unlikely.) """ self.emit("inhibit-timeout") text = self.password_entry.get_text() self.password_entry.start_progress() self.password_entry.set_placeholder_text (_("Checking...")) self.authenticate(text) def on_auth_enter_key(self, widget): """ Implicitly activates the unlock button when the Enter/Return key is pressed. """ if widget.get_text() == "": return self.on_unlock_clicked() def on_switch_user_clicked(self, widget): """ Callback for the switch-user button. """ utils.do_user_switch() def clear_entry(self): """ Clear the password entry widget. """ self.password_entry.set_text("") def update_realname_label(self): """ Updates the name label to the current real_name. """ self.realname_label.set_text(self.real_name) def authenticate(self, password): """ Authenticates the entered password against the system PAM provider. """ CinnamonDesktop.desktop_check_user_password(self.user_name, password, self.authenticate_callback) def authenticate_callback(self, success, data=None): """ Callback for check_user_password. Send this back to the Stage so it can react how it needs to (destroying itself or flashing the unlock widget on failure.) """ self.password_entry.stop_progress() if success: self.clear_entry() self.emit("auth-success") else: self.authentication_failed() self.emit("auth-failure") def authentication_failed(self): """ Called upon authentication failure, clears the password, sets an error message, and refocuses the password entry. """ self.clear_entry() self.password_entry.set_placeholder_text (_("Please enter your password...")) self.auth_message_label.set_text(_("Incorrect password")) self.password_entry.grab_focus() self.emit("uninhibit-timeout")
class PlayerControl(Gtk.Box): def __init__(self): super(PlayerControl, self).__init__(orientation=Gtk.Orientation.HORIZONTAL) self.watcher = singletons.MediaPlayerWatcher self.player = self.watcher.get_best_player() if self.player: self.build_layout() def build_layout(self): size = Gtk.IconSize.from_name("audio-button") # size = Gtk.IconSize.MENU player_status = self.player.get_playback_status() # Player buttons self.pack_start(Gtk.VSeparator(), True, True, 2) button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) button_box.set_homogeneous(True) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 2) vbox.pack_start(button_box, True, True, 0) vbox.set_valign(Gtk.Align.CENTER) self.previous_button = TransparentButton( "media-skip-backward-symbolic", size) self.previous_button.show() trackers.con_tracker_get().connect(self.previous_button, "clicked", self.on_previous_clicked) button_box.pack_start(self.previous_button, True, True, 2) self.play_pause_button = TransparentButton( self.get_play_pause_icon_name(player_status), size) self.play_pause_button.show() trackers.con_tracker_get().connect(self.play_pause_button, "clicked", self.on_play_pause_clicked) button_box.pack_start(self.play_pause_button, True, True, 2) self.next_button = TransparentButton("media-skip-forward-symbolic", size) self.next_button.show() trackers.con_tracker_get().connect(self.next_button, "clicked", self.on_next_clicked) button_box.pack_start(self.next_button, True, True, 2) self.update_buttons(player_status) status.focusWidgets = status.focusWidgets + [ self.previous_button, self.play_pause_button, self.next_button ] # Position labels and bar self.pack_start(Gtk.VSeparator(), True, True, 2) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 4) vbox.set_valign(Gtk.Align.CENTER) position_length_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) vbox.pack_start(position_length_box, True, True, 2) self.current_pos_label = BlinkingLabel("", 400) self.current_pos_label.get_style_context().add_class("positionlabel") position_length_box.pack_start(self.current_pos_label, False, False, 2) self.max_pos_label = BlinkingLabel("", 400) self.max_pos_label.get_style_context().add_class("positionlabel") position_length_box.pack_end(self.max_pos_label, False, False, 2) self.position_bar = PositionBar() vbox.pack_end(self.position_bar, True, True, 2) # Track info self.pack_start(Gtk.VSeparator(), True, True, 2) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 6) vbox.set_valign(Gtk.Align.CENTER) self.track_name_label = MarqueeLabel("") self.track_name_label.get_style_context().add_class("trackname") vbox.pack_start(self.track_name_label, True, True, 2) self.album_artist_label = MarqueeLabel("") self.album_artist_label.get_style_context().add_class("albumartist") vbox.pack_end(self.album_artist_label, True, True, 2) self.show_all() trackers.con_tracker_get().connect(self.player, "position-changed", self.on_position_changed) trackers.con_tracker_get().connect(self.player, "status-changed", self.on_playback_status_changed) trackers.con_tracker_get().connect(self.player, "metadata-changed", self.on_metadata_changed) self.on_playback_status_changed(self.player, player_status) self.on_metadata_changed(self.player) trackers.con_tracker_get().connect(self, "destroy", self.on_widget_destroy) self.update_position_timer(player_status) def on_previous_clicked(self, button, data=None): self.player.go_previous() def on_next_clicked(self, button, data=None): self.player.go_next() def on_play_pause_clicked(self, button, data=None): self.player.play_pause() def get_play_pause_icon_name(self, status): if status == PlaybackStatus.Playing: icon_name = "media-playback-pause-symbolic" else: icon_name = "media-playback-start-symbolic" return icon_name def position_to_time_string(self, position): delta = datetime.timedelta(microseconds=position) duration = datetime.datetime.utcfromtimestamp(delta.total_seconds()) if duration.hour < 0: return _("--:--") if duration.hour < 1: return duration.strftime(_("%M:%S")) else: return duration.strftime(_("%H:%M:%S")) def on_playback_status_changed(self, player, status, data=None): self.update_buttons(status) self.update_position_timer(status) self.update_position_display() self.update_position_values_appearance(status) def update_position_values_appearance(self, status): if status == PlaybackStatus.Paused: self.max_pos_label.set_blinking(True) self.current_pos_label.set_blinking(True) else: self.max_pos_label.set_blinking(False) self.current_pos_label.set_blinking(False) def on_metadata_changed(self, player): self.max_pos_label.set_text( self.position_to_time_string(self.player.get_max_position())) self.update_labels() def update_labels(self): self.track_name_label.set_text(self.player.get_track_name()) artist_name = self.player.get_artist_name() album_name = self.player.get_album_name() if artist_name != "" and album_name != "": self.album_artist_label.set_text( "%s - %s" % (self.player.get_artist_name(), self.player.get_album_name())) elif artist_name != "": self.album_artist_label.set_text(artist_name) elif album_name != "": self.album_artist_label.set_text(album_name) else: self.album_artist_label.set_text("") def pause_blink_step(self): self.max_pos_label.set_visible(not self.max_pos_label.get_visible()) self.current_pos_label.set_visible( not self.current_pos_label.get_visible()) return True def update_buttons(self, status): self.play_pause_button.set_sensitive(self.player.get_can_play_pause()) self.next_button.set_sensitive(self.player.get_can_go_next()) self.previous_button.set_sensitive(self.player.get_can_go_previous()) icon_name = self.get_play_pause_icon_name(status) size = Gtk.IconSize.from_name("audio-button") image = Gtk.Image.new_from_icon_name(icon_name, size) self.play_pause_button.set_image(image) def update_position_display(self): if self.player.get_position() < self.player.get_max_position(): value = self.player.get_position() / self.player.get_max_position() else: value = 1.0 value = utils.CLAMP(value, 0.0, 1.0) self.position_bar.set_fraction(value) self.current_pos_label.set_text( self.position_to_time_string(self.player.get_position())) return True def update_position_timer(self, status): if status == PlaybackStatus.Playing: trackers.timer_tracker_get().start("position-timer", self.player.get_rate() * 1000, self.update_position_display) else: trackers.timer_tracker_get().cancel("position-timer") def on_position_changed(self, player, position, data=None): self.update_position_display() def on_widget_destroy(self, widget, data=None): trackers.con_tracker_get().disconnect(self.player, "position-changed", self.on_position_changed) trackers.con_tracker_get().disconnect(self.player, "status-changed", self.on_playback_status_changed) trackers.con_tracker_get().disconnect(self, "destroy", self.on_widget_destroy) trackers.timer_tracker_get().cancel("position-timer") def should_show(self): return self.player != None