def get_cfg(option): cfg_option = "%s_%s" % (_PLUGIN_ID, option) default = _SETTINGS[option][2] if option == "threshold": return config.getfloat("plugins", cfg_option, default) elif option == "ratio": return config.getfloat("plugins", cfg_option, default)
def get_cfg(option): cfg_option = "%s_%s" % (_PLUGIN_ID, option) default = _SETTINGS[option][2] if option == "seekto": return config.getfloat("plugins", cfg_option, default) elif option == "skipat": return config.getfloat("plugins", cfg_option, default)
def get_cfg(option): cfg_option = "%s_%s" % (_PLUGIN_ID, option) default = _SETTINGS[option][1] if option == "rate": return config.getfloat("plugins", cfg_option, default) elif option == "tempo": return config.getfloat("plugins", cfg_option, default) elif option == "pitch": return config.getfloat("plugins", cfg_option, default)
def do_set_property(self, property, v): if property.name == 'volume': self._volume = v if self.song and config.getboolean("player", "replaygain"): profiles = filter(None, self.replaygain_profiles)[0] fb_gain = config.getfloat("player", "fallback_gain") pa_gain = config.getfloat("player", "pre_amp_gain") scale = self.song.replay_gain(profiles, pa_gain, fb_gain) v = min(10.0, max(0.0, v * scale)) # volume supports 0..10 if self.bin: self._vol_element.set_property('volume', v) else: raise AttributeError
def do_set_property(self, property, v): if property.name == 'volume': self._volume = v if self.song and config.getboolean("player", "replaygain"): profiles = filter(None, self.replaygain_profiles)[0] fb_gain = config.getfloat("player", "fallback_gain") pa_gain = config.getfloat("player", "pre_amp_gain") scale = self.song.replay_gain(profiles, pa_gain, fb_gain) v = max(0.0, v * scale) v = min(100, int(v * 100)) xine_set_param(self._stream, XINE_PARAM_AUDIO_AMP_LEVEL, v) else: raise AttributeError
def bayesian_average(nums, c=None, m=None): """Returns the Bayesian average of an iterable of numbers, with parameters defaulting to config specific to ~#rating.""" m = m or config.RATINGS.default c = c or config.getfloat("settings", "bayesian_rating_factor", 0.0) ret = float(m * c + sum(nums)) / (c + len(nums)) return ret
def __refresh_album(self, menuitem, view): albums = self.__get_selected_albums() mag = config.getfloat("browsers", "covergrid_magnification", 3.) scale_factor = self.get_scale_factor() * mag for album in albums: album.scan_cover(True, scale_factor=scale_factor) self._refresh_albums(albums)
def slider_config(section, option, label, tooltip, lower=0, upper=1, on_change_callback=None, label_value_callback=None): def on_reverted(*args): config.reset(section, option) scale.set_value(config.getfloat(section, option)) def on_change(scale): value = scale.get_value() if on_change_callback: value = on_change_callback(value) scale.set_value(value) config.set(section, option, value) default = config.getfloat(section, option) scale = Gtk.HScale.new(Gtk.Adjustment( value=default, lower=lower, upper=upper)) scale.set_value_pos(Gtk.PositionType.LEFT) scale.set_show_fill_level(True) scale.set_tooltip_text(_(tooltip)) if label_value_callback: scale.connect('format-value', lambda _, value: label_value_callback(value)) scale.connect('value-changed', on_change) revert = Gtk.Button() revert.add(Gtk.Image.new_from_icon_name(Icons.DOCUMENT_REVERT, Gtk.IconSize.BUTTON)) revert.connect("clicked", on_reverted) lbl = Gtk.Label(label=label, use_underline=True) lbl.set_mnemonic_widget(scale) return lbl, scale, revert
def select_browser(self, activator, current, library, player, restore=False): if isinstance(current, Gtk.RadioAction): current = current.get_current_value() Browser = browsers.get(current) config.set("memory", "browser", Browser.__name__) if self.browser: container = self.browser.__container self.browser.unpack(container, self.songpane) if self.browser.accelerators: self.remove_accel_group(self.browser.accelerators) container.destroy() self.browser.destroy() self.browser = Browser(library, True) self.browser.connect('songs-selected', self.__browser_cb) self.browser.connect('activated', self.__browser_activate) if restore: self.browser.restore() self.browser.activate() self.browser.finalize(restore) if self.browser.reordered: self.songlist.enable_drop() elif self.browser.dropped: self.songlist.enable_drop(False) else: self.songlist.disable_drop() if self.browser.accelerators: self.add_accel_group(self.browser.accelerators) container = self.browser.__container = self.browser.pack(self.songpane) # find a paned and save the position paned = None for widget in qltk.find_widgets(container, RPaned): if widget is not self.songpane: paned = widget break if paned: try: key = "%s_pos" % self.browser.__class__.__name__ val = config.getfloat("browsers", key) # Use a minimum restore size val = max(val, 0.1) except: val = 0.4 paned.connect( 'notify::position', self.__browser_configure, self.browser) paned.set_relative(val) player.replaygain_profiles[1] = self.browser.replaygain_profiles player.volume = player.volume self.__browserbox.add(container) container.show() self.__hide_menus() self.__hide_headers() self.__refresh_size()
def _no_cover(self): """Returns a cairo surface representing a missing cover""" mag = config.getfloat("browsers", "covergrid_magnification", 3.) cover_size = get_cover_size() scale_factor = self.get_scale_factor() * mag pb = get_no_cover_pixbuf(cover_size, cover_size, scale_factor) return get_surface_for_pixbuf(self, pb)
def update_mag(klass): mag = config.getfloat("browsers", "covergrid_magnification", 3.) for covergrid in klass.instances(): covergrid.__cover.set_property('width', get_cover_size() * mag + 8) covergrid.__cover.set_property('height', get_cover_size() * mag + 8) covergrid.view.set_item_width(get_cover_size() * mag + 8) covergrid.view.queue_resize() covergrid.redraw()
def test_get(self): config.set("foo", "int", "1") config.set("foo", "float", "1.25") config.set("foo", "str", "foobar") config.set("foo", "bool", "True") self.failUnlessEqual(config.getint("foo", "int"), 1) self.failUnlessEqual(config.getfloat("foo", "float"), 1.25) self.failUnlessEqual(config.get("foo", "str"), "foobar") self.failUnlessEqual(config.getboolean("foo", "bool"), True)
def __init__(self): for (key, text, func) in self.keys: val = config.getfloat("plugins", "randomalbum_%s" % key, 0.0) self.weights[key] = val use = config.getint("plugins", "randomalbum_use_weights", 0) self.use_weights = use delay = config.getint("plugins", "randomalbum_delay", 0) self.delay = delay
def _no_cover(self) -> Optional[cairo.Surface]: """Returns a cairo surface representing a missing cover""" mag = config.getfloat("browsers", "covergrid_magnification", 3.) cover_size = get_cover_size() scale_factor = self.get_scale_factor() * mag pb = get_no_cover_pixbuf(cover_size, cover_size, scale_factor) return get_surface_for_pixbuf(self, pb)
def __init__(self, parent, player, library): super(TopBar, self).__init__() # play controls control_item = Gtk.ToolItem() self.insert(control_item, 0) t = PlayControls(player, library.librarian) self.volume = t.volume # only restore the volume in case it is managed locally, otherwise # this could affect the system volume if not player.has_external_volume: player.volume = config.getfloat("memory", "volume") connect_destroy(player, "notify::volume", self._on_volume_changed) control_item.add(t) self.insert(Gtk.SeparatorToolItem(), 1) info_item = Gtk.ToolItem() self.insert(info_item, 2) info_item.set_expand(True) box = Gtk.Box(spacing=6) info_item.add(box) qltk.add_css(self, "GtkToolbar {padding: 3px;}") self._pattern_box = Gtk.VBox() # song text info_pattern_path = os.path.join(quodlibet.get_user_dir(), "songinfo") text = SongInfo(library.librarian, player, info_pattern_path) self._pattern_box.pack_start(Align(text, border=3), True, True, 0) box.pack_start(self._pattern_box, True, True, 0) # cover image self.image = CoverImage(resize=True) connect_destroy(player, 'song-started', self.__new_song) # FIXME: makes testing easier if app.cover_manager: connect_destroy( app.cover_manager, 'cover-changed', self.__song_art_changed, library) box.pack_start(Align(self.image, border=2), False, True, 0) # On older Gtk+ (3.4, at least) # setting a margin on CoverImage leads to errors and result in the # QL window not being visible for some reason. assert self.image.props.margin == 0 for child in self.get_children(): child.show_all() context = self.get_style_context() context.add_class("primary-toolbar")
def __set_pane_size(self): widgets = qltk.find_widgets(self.__container, RPaned) if widgets: paned = widgets[0] try: key = "%s_pos" % self.browser.__class__.__name__ val = config.getfloat("browsers", key) except: val = 0.4 paned.set_relative(val)
def mag_changed(mag): if self.mag_lock: return newmag = mag.get_value() oldmag = config.getfloat("browsers", "covergrid_magnification", 3.0) if newmag == oldmag: print_d("Covergrid magnification haven't changed: {0}".format(newmag)) return print_d("Covergrid magnification update from {0} to {1}".format(oldmag, newmag)) config.set("browsers", "covergrid_magnification", mag.get_value()) browser.update_mag()
def calc_replaygain_volume(self, volume): """Returns a new float volume for the given volume. Takes into account the global active replaygain profile list, the user specified replaygain settings and the tags available for that song. Args: volume (float): 0.0..1.0 Returns: float: adjusted volume, can be outside of 0.0..0.1 """ if self.song and config.getboolean("player", "replaygain"): profiles = list(filter(None, self.replaygain_profiles))[0] fb_gain = config.getfloat("player", "fallback_gain") pa_gain = config.getfloat("player", "pre_amp_gain") scale = self.song.replay_gain(profiles, pa_gain, fb_gain) else: scale = 1 return volume * scale
def calc_replaygain_volume(self, volume): """Returns a new float volume for the given volume. Takes into account the global active replaygain profile list, the user specified replaygain settings and the tags available for that song. Args: volume (float): 0.0..1.0 Returns: float: adjusted volume, can be outside of 0.0..0.1 """ if self.song and config.getboolean("player", "replaygain"): profiles = listfilter(None, self.replaygain_profiles)[0] fb_gain = config.getfloat("player", "fallback_gain") pa_gain = config.getfloat("player", "pre_amp_gain") scale = self.song.replay_gain(profiles, pa_gain, fb_gain) else: scale = 1 return volume * scale
def test_bayesian_multiple_ratings(s): # separated from above to avoid caching c, r1, r2 = 5, 1.0, 0.5 songs = [Fakesong({"~#rating": r1}), Fakesong({"~#rating": r2})] album = Album(songs[0]) album.songs = set(songs) config.set("settings", "bayesian_rating_factor", float(c)) s.failUnlessEqual(config.getfloat("settings", "bayesian_rating_factor"), float(c)) expected = avg(c * [config.RATINGS.default] + [r1, r2]) s.failUnlessEqual(album("~#rating:bav"), expected) s.failUnlessEqual(album("~#rating"), expected)
def __init__(self, songs, library): super(WeightedPlaylist, self).__init__(songs, library) for key,_ in self.options["weights"]: val = config.getfloat("plugins", "weightedlibrary_%s" % key, 0.0) self.weights[key] = val for key,_,min_value,max_value in self.options["values"]: val = config.getfloat("plugins", "weightedlibrary_%s" % key, (min_value+max_value)/2.) self.weights[key] = val for key,val in self.weights.items(): print "%s = %s" % (key,val) rater = ModifiedAveragedRater() # Raters are a weighted sum rater.add_rater(weight=self.weights["rating"], rater=SongRatingRater()) rater.add_rater(weight=self.weights["tempo"], rater=BpmRater(target_bpm=self.weights["tempo_target"], spread=self.weights["tempo_spread"])) # Variety is between 0 and 50 # Modifiers work multiplicatively rater.add_modifier(weight=3., rater=RepeaterRater()) self.rater = rater
def test_bayesian_multiple_ratings(s): # separated from above to avoid caching c, r1, r2 = 5, 1.0, 0.5 songs = [Fakesong({"~#rating": r1}), Fakesong({"~#rating": r2})] album = Album(songs[0]) album.songs = set(songs) config.set("settings", "bayesian_rating_factor", float(c)) s.failUnlessEqual( config.getfloat("settings", "bayesian_rating_factor"), float(c)) expected = avg(c * [config.RATINGS.default] + [r1, r2]) s.failUnlessEqual(album("~#rating:bav"), expected) s.failUnlessEqual(album("~#rating"), expected)
def mag_changed(mag): if self.mag_lock: return newmag = mag.get_value() oldmag = config.getfloat("browsers", "covergrid_magnification", 3.) if newmag == oldmag: print_d("Covergrid magnification haven't changed: {0}".format( newmag)) return print_d('Covergrid magnification update from {0} to {1}'.format( oldmag, newmag)) config.set("browsers", "covergrid_magnification", mag.get_value()) browser.update_mag()
def __init__(self, parent, player, library): super(TopBar, self).__init__() # play controls control_item = Gtk.ToolItem() self.insert(control_item, 0) t = PlayControls(player, library.librarian) self.volume = t.volume # only restore the volume in case it is managed locally, otherwise # this could affect the system volume if not player.has_external_volume: player.volume = config.getfloat("memory", "volume") self.volume.connect("value-changed", self._on_volume_changed) control_item.add(t) self.insert(Gtk.SeparatorToolItem(), 1) info_item = Gtk.ToolItem() self.insert(info_item, 2) info_item.set_expand(True) box = Gtk.Box(spacing=6) info_item.add(box) qltk.add_css(self, "GtkToolbar {padding: 3px;}") # song text info_pattern_path = os.path.join(const.USERDIR, "songinfo") text = SongInfo(library.librarian, player, info_pattern_path) box.pack_start(Align(text, border=3), True, True, 0) # cover image self.image = CoverImage(resize=True) connect_destroy(player, 'song-started', self.__new_song) # FIXME: makes testing easier if app.cover_manager: connect_destroy( app.cover_manager, 'cover-changed', self.__song_art_changed, library) self.image.props.margin = 2 box.pack_start(self.image, False, True, 0) for child in self.get_children(): child.show_all() context = self.get_style_context() context.add_class("primary-toolbar")
def __set_pane_size(self): sub = self.__container if not isinstance(self.__container, RPaned): for child in self.__container.get_children(): if isinstance(child, RPaned): sub = child if isinstance(sub, RPaned): try: key = "%s_pos" % self.browser.__class__.__name__ val = config.getfloat("browsers", key) except: val = 0.4 sub.set_relative(val)
def test_basic(self): self.failUnless(config.get("memory", "foobar", None) is None) p = x.ConfigRVPaned("memory", "foobar", 0.75) p.pack1(Gtk.Button()) p.pack2(Gtk.Button()) with visible(p, width=200, height=200) as p: self.failUnlessAlmostEqual(p.get_relative(), 0.75, 2) p.props.position = 20 self.failUnlessAlmostEqual(p.get_relative(), 0.10, 2) config_value = config.getfloat("memory", "foobar") self.failUnlessAlmostEqual(config_value, 0.10, 2)
def __init__(self, device): super(Volume, self).__init__(size=Gtk.IconSize.MENU, use_symbolic=True) self.set_relief(Gtk.ReliefStyle.NORMAL) self.set_adjustment(Gtk.Adjustment(0, 0, 1, 0.05, 0.1, 0)) self.connect('value-changed', self.__volume_changed, device) device.connect('notify::volume', self.__volume_notify) self.set_value(config.getfloat("memory", "volume")) replaygain_menu = ReplayGainMenu(device) self.connect('popup-menu', self.__popup, replaygain_menu) self.connect_object('button-press-event', self.__volume_button_press, replaygain_menu)
def test_basic(self): self.failUnless(config.get("memory", "foobar", None) is None) p = ConfigRVPaned("memory", "foobar", 0.75) p.pack1(Gtk.Button()) p.pack2(Gtk.Button()) with visible(p, width=200, height=200) as p: self.failUnlessAlmostEqual(p.get_relative(), 0.75, 2) p.props.position = 20 self.failUnlessAlmostEqual(p.get_relative(), 0.10, 2) config_value = config.getfloat("memory", "foobar") self.failUnlessAlmostEqual(config_value, 0.10, 2)
def __init__(self, parent, player, library): super(TopBar, self).__init__() # play controls control_item = Gtk.ToolItem() self.insert(control_item, 0) t = PlayControls(player, library.librarian) self.volume = t.volume # only restore the volume in case it is managed locally, otherwise # this could affect the system volume if not player.has_external_volume: player.volume = config.getfloat("memory", "volume") self.volume.connect("value-changed", self._on_volume_changed) control_item.add(t) self.insert(Gtk.SeparatorToolItem(), 1) info_item = Gtk.ToolItem() self.insert(info_item, 2) info_item.set_expand(True) box = Gtk.Box(spacing=6) info_item.add(box) qltk.add_css(self, "GtkToolbar {padding: 3px;}") # song text info_pattern_path = os.path.join(const.USERDIR, "songinfo") text = SongInfo(library.librarian, player, info_pattern_path) box.pack_start(Align(text, border=3), True, True, 0) # cover image self.image = CoverImage(resize=True) connect_destroy(player, 'song-started', self.__new_song) # FIXME: makes testing easier if app.cover_manager: connect_destroy(app.cover_manager, 'cover-changed', self.__song_art_changed, library) self.image.props.margin = 2 box.pack_start(self.image, False, True, 0) for child in self.get_children(): child.show_all() context = self.get_style_context() context.add_class("primary-toolbar")
def _set_color(self, _widget, validator): value = validator(self.get_text()) default = self._default_color() amount = config.getfloat("settings", "validator_colorise") if value is True: color = mix(default, self.VALID, amount) elif value is False: color = mix(default, self.INVALID, amount) else: color = Gdk.RGBA(default.red, default.green, default.blue, self.ALPHA) if color and self.get_property('sensitive'): self.override_color(Gtk.StateType.NORMAL, color) else: self.override_color(Gtk.StateType.NORMAL, None)
def __init__(self, device): super(Volume, self).__init__(size=Gtk.IconSize.MENU, use_symbolic=True) self.set_relief(Gtk.ReliefStyle.NORMAL) self.set_adjustment(Gtk.Adjustment.new(0, 0, 1, 0.05, 0.1, 0)) self.connect('value-changed', self.__volume_changed, device) device.connect('notify::volume', self.__volume_notify) self.set_value(config.getfloat("memory", "volume")) replaygain_menu = ReplayGainMenu(device) replaygain_menu.attach_to_widget(self, None) self.connect('popup-menu', self.__popup, replaygain_menu) connect_obj(self, 'button-press-event', self.__volume_button_press, replaygain_menu)
def __init__(self, song_scroller, qexpander): super(SongListPaned, self).__init__() self.pack1(song_scroller, resize=True, shrink=False) self.pack2(qexpander, resize=True, shrink=False) self.set_relative(config.getfloat("memory", "queue_position", 0.75)) self.connect( 'notify::position', self._changed, "memory", "queue_position") self._handle_position = self.get_relative() qexpander.connect('notify::visible', self._expand_or) qexpander.connect('notify::expanded', self._expand_or) qexpander.connect('draw', self._check_minimize) self.connect("button-press-event", self._on_button_press) self.connect('notify', self._moved_pane_handle)
def __init__(self, song_scroller, qexpander): super().__init__() self.pack1(song_scroller, resize=True, shrink=False) self.pack2(qexpander, resize=True, shrink=False) self.set_relative(config.getfloat("memory", "queue_position", 0.75)) self.connect('notify::position', self._changed, "memory", "queue_position") self._handle_position = self.get_relative() qexpander.connect('notify::visible', self._expand_or) qexpander.connect('notify::expanded', self._expand_or) qexpander.connect('draw', self._check_minimize) self.connect("button-press-event", self._on_button_press) self.connect('notify', self._moved_pane_handle)
def __init__(self, device): super(Volume, self).__init__() self.props.size = SUBSIZE self.set_relief(gtk.RELIEF_NORMAL) self.set_adjustment(gtk.Adjustment(0, 0, 1, 0.05, 0.1, 0)) self.connect('value-changed', self.__volume_changed, device) device.connect('notify::volume', self.__volume_notify) self.set_value(config.getfloat("memory", "volume")) self.show_all() replaygain_menu = ReplayGainMenu(device) self.connect('popup-menu', self.__popup, replaygain_menu) self.connect_object('button-press-event', self.__volume_button_press, replaygain_menu)
def _update_row(self, filter_model, iter_): sort_model = filter_model.get_model() model = sort_model.get_model() iter_ = filter_model.convert_iter_to_child_iter(iter_) iter_ = sort_model.convert_iter_to_child_iter(iter_) tref = Gtk.TreeRowReference.new(model, model.get_path(iter_)) mag = config.getfloat("browsers", "covergrid_magnification", 3.) def callback(): path = tref.get_path() if path is not None: model.row_changed(path, model.get_iter(path)) # XXX: icon view seems to ignore row_changed signals for pixbufs.. self.queue_resize() item = model.get_value(iter_) scale_factor = self.get_scale_factor() * mag item.scan_cover(scale_factor=scale_factor, callback=callback, cancel=self._cover_cancel)
def _update_row(self, filter_model, iter_): sort_model = filter_model.get_model() model = sort_model.get_model() iter_ = filter_model.convert_iter_to_child_iter(iter_) iter_ = sort_model.convert_iter_to_child_iter(iter_) tref = Gtk.TreeRowReference.new(model, model.get_path(iter_)) mag = config.getfloat("browsers", "covergrid_magnification", 3.) def callback(): path = tref.get_path() if path is not None: model.row_changed(path, model.get_iter(path)) # XXX: icon view seems to ignore row_changed signals for pixbufs.. self.queue_draw() item = model.get_value(iter_) scale_factor = self.get_scale_factor() * mag item.scan_cover(scale_factor=scale_factor, callback=callback, cancel=self._cover_cancel)
def __end(self, player, song, ended, librarian, pl): if song is not None and not song.multisong: if ended: config.set("memory", "seek", player.get_position()) else: config.set("memory", "seek", 0) playcount_minimum_length = config.getfloat( "player", "playcount_minimum_length_proportion") * int(song.get( "~#length", 1)) if self.elapsed >= playcount_minimum_length: song["~#lastplayed"] = int(time.time()) song["~#playcount"] = song.get("~#playcount", 0) + 1 self.__changed(librarian, song) elif pl.current is not song: if not player.error: song["~#skipcount"] = song.get("~#skipcount", 0) + 1 self.__changed(librarian, song) else: config.set("memory", "seek", 0)
def __browser_cb(self, browser, songs, sorted, library, player): if browser.background: bg = background_filter() if bg: songs = filter(bg, songs) self.songlist.set_songs(songs, sorted) # After the first time the browser activates, which should always # happen if we start up and restore, restore the playing song. # Because the browser has send us songs we can be sure it has # registered all its libraries. if self.__first_browser_set: self.__first_browser_set = False song = library.librarian.get(config.get("memory", "song")) seek_pos = config.getfloat("memory", "seek", 0) config.set("memory", "seek", 0) if song is not None: player.setup(self.playlist, song, seek_pos) if self.__restore_cb: self.__restore_cb() self.__restore_cb = None
def get_cfg(option): cfg_option = "%s_%s" % (_PLUGIN_ID, option) default = _SETTINGS[option][2] return config.getfloat("plugins", cfg_option, default)
def __init__(self, section, option, default, *args, **kwargs): super(ConfigRPaned, self).__init__(*args, **kwargs) self.set_relative(config.getfloat(section, option, default)) self.connect('notify::position', self.__changed, section, option)
def __init__(self): super(PreferencesWindow.Player, self).__init__(spacing=12) self.set_border_width(12) self.title = _("Playback") # player backend if app.player and hasattr(app.player, 'PlayerPreferences'): player_prefs = app.player.PlayerPreferences() f = qltk.Frame(_("Output Configuration"), child=player_prefs) self.pack_start(f, False, True, 0) # replaygain fallback_gain = config.getfloat("player", "fallback_gain", 0.0) adj = Gtk.Adjustment.new(fallback_gain, -12.0, 12.0, 0.5, 0.5, 0.0) fb_spin = Gtk.SpinButton(adjustment=adj) fb_spin.set_digits(1) fb_spin.connect('changed', self.__changed, 'player', 'fallback_gain') fb_spin.set_tooltip_text( _("If no Replay Gain information is available " "for a song, scale the volume by this value")) fb_label = Gtk.Label(label=_("_Fall-back gain (dB):")) fb_label.set_use_underline(True) fb_label.set_mnemonic_widget(fb_spin) pre_amp_gain = config.getfloat("player", "pre_amp_gain", 0.0) adj = Gtk.Adjustment.new(pre_amp_gain, -6, 6, 0.5, 0.5, 0.0) adj.connect('value-changed', self.__changed, 'player', 'pre_amp_gain') pre_spin = Gtk.SpinButton(adjustment=adj) pre_spin.set_digits(1) pre_spin.set_tooltip_text( _("Scale volume for all songs by this value, " "as long as the result will not clip")) pre_label = Gtk.Label(label=_("_Pre-amp gain (dB):")) pre_label.set_use_underline(True) pre_label.set_mnemonic_widget(pre_spin) widgets = [pre_label, pre_spin, fb_label, fb_spin] c = CCB(_("_Enable Replay Gain volume adjustment"), "player", "replaygain", populate=True) c.connect('toggled', self.__toggled_gain, widgets) # packing table = Gtk.Table.new(3, 2, False) table.set_col_spacings(6) table.set_row_spacings(6) table.attach(c, 0, 2, 0, 1) fb_label.set_alignment(0, 0.5) table.attach(fb_label, 0, 1, 1, 2, xoptions=Gtk.AttachOptions.FILL) pre_label.set_alignment(0, 0.5) table.attach(pre_label, 0, 1, 2, 3, xoptions=Gtk.AttachOptions.FILL) fb_align = Align(halign=Gtk.Align.START) fb_align.add(fb_spin) table.attach(fb_align, 1, 2, 1, 2) pre_align = Align(halign=Gtk.Align.START) pre_align.add(pre_spin) table.attach(pre_align, 1, 2, 2, 3) f = qltk.Frame(_("Replay Gain Volume Adjustment"), child=table) c.emit('toggled') self.pack_start(f, False, True, 0) for child in self.get_children(): child.show_all()
def __init_pipeline(self): """Creates a gstreamer pipeline. Returns True on success.""" if self.bin: return True # reset error state self.error = False pipeline = config.get("player", "gst_pipeline") try: pipeline, self._pipeline_desc = GStreamerSink(pipeline) except PlayerError as e: self._error(e) return False if self._use_eq and Gst.ElementFactory.find('equalizer-10bands'): # The equalizer only operates on 16-bit ints or floats, and # will only pass these types through even when inactive. # We push floats through to this point, then let the second # audioconvert handle pushing to whatever the rest of the # pipeline supports. As a bonus, this seems to automatically # select the highest-precision format supported by the # rest of the chain. filt = Gst.ElementFactory.make('capsfilter', None) filt.set_property('caps', Gst.Caps.from_string('audio/x-raw,format=F32LE')) eq = Gst.ElementFactory.make('equalizer-10bands', None) self._eq_element = eq self.update_eq_values() conv = Gst.ElementFactory.make('audioconvert', None) resample = Gst.ElementFactory.make('audioresample', None) pipeline = [filt, eq, conv, resample] + pipeline # playbin2 has started to control the volume through pulseaudio, # which means the volume property can change without us noticing. # Use our own volume element for now until this works with PA. self._int_vol_element = Gst.ElementFactory.make('volume', None) pipeline.insert(0, self._int_vol_element) # Get all plugin elements and append audio converters. # playbin already includes one at the end plugin_pipeline = [] for plugin in self._get_plugin_elements(): plugin_pipeline.append(plugin) plugin_pipeline.append( Gst.ElementFactory.make('audioconvert', None)) plugin_pipeline.append( Gst.ElementFactory.make('audioresample', None)) pipeline = plugin_pipeline + pipeline bufbin = Gst.Bin() for element in pipeline: assert element is not None, pipeline bufbin.add(element) if len(pipeline) > 1: if not link_many(pipeline): print_w("Linking the GStreamer pipeline failed") self._error( PlayerError(_("Could not create GStreamer pipeline"))) return False # see if the sink provides a volume property, if yes, use it sink_element = pipeline[-1] if isinstance(sink_element, Gst.Bin): sink_element = iter_to_list(sink_element.iterate_recurse)[-1] self._ext_vol_element = None if hasattr(sink_element.props, "volume"): self._ext_vol_element = sink_element # In case we use the sink volume directly we can increase buffering # without affecting the volume change delay too much and safe some # CPU time... (2x default for now). if hasattr(sink_element.props, "buffer_time"): sink_element.set_property("buffer-time", 400000) def ext_volume_notify(*args): # gets called from a thread GLib.idle_add(self.notify, "volume") self._ext_vol_element.connect("notify::volume", ext_volume_notify) self._ext_mute_element = None if hasattr(sink_element.props, "mute") and \ sink_element.get_factory().get_name() != "directsoundsink": # directsoundsink has a mute property but it doesn't work # https://bugzilla.gnome.org/show_bug.cgi?id=755106 self._ext_mute_element = sink_element def mute_notify(*args): # gets called from a thread GLib.idle_add(self.notify, "mute") self._ext_mute_element.connect("notify::mute", mute_notify) # Make the sink of the first element the sink of the bin gpad = Gst.GhostPad.new('sink', pipeline[0].get_static_pad('sink')) bufbin.add_pad(gpad) bin_ = Gst.ElementFactory.make('playbin', None) assert bin_ self.bin = BufferingWrapper(bin_, self) self._seeker = Seeker(self.bin, self) bus = bin_.get_bus() bus.add_signal_watch() self.__bus_id = bus.connect('message', self.__message, self._librarian) self.__atf_id = self.bin.connect('about-to-finish', self.__about_to_finish) # set buffer duration duration = config.getfloat("player", "gst_buffer") self._set_buffer_duration(int(duration * 1000)) # connect playbin to our pluing/volume/eq pipeline self.bin.set_property('audio-sink', bufbin) # by default playbin will render video -> suppress using fakesink fakesink = Gst.ElementFactory.make('fakesink', None) self.bin.set_property('video-sink', fakesink) # disable all video/text decoding in playbin GST_PLAY_FLAG_VIDEO = 1 << 0 GST_PLAY_FLAG_TEXT = 1 << 2 flags = self.bin.get_property("flags") flags &= ~(GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT) self.bin.set_property("flags", flags) if not self.has_external_volume: # Restore volume/ReplayGain and mute state self.volume = self._volume self.mute = self._mute # ReplayGain information gets lost when destroying self._reset_replaygain() if self.song: self.bin.set_property('uri', self.song("~uri")) return True
def ratings_vbox(self): """Returns a new VBox containing all ratings widgets""" vb = Gtk.VBox(spacing=6) # Default Rating model = Gtk.ListStore(float) default_combo = Gtk.ComboBox(model=model) default_lab = Gtk.Label(label=_("_Default rating:")) default_lab.set_use_underline(True) default_lab.set_alignment(0, 0.5) def draw_rating(column, cell, model, it, data): num = model[it][0] text = "%0.2f: %s" % (num, util.format_rating(num)) cell.set_property('text', text) def default_rating_changed(combo, model): it = combo.get_active_iter() if it is None: return RATINGS.default = model[it][0] qltk.redraw_all_toplevels() def populate_default_rating_model(combo, num): model = combo.get_model() model.clear() deltas = [] default = RATINGS.default precision = RATINGS.precision for i in range(0, num + 1): r = i * precision model.append(row=[r]) deltas.append((abs(default - r), i)) active = sorted(deltas)[0][1] print_d("Choosing #%d (%.2f), closest to current %.2f" % (active, precision * active, default)) combo.set_active(active) cell = Gtk.CellRendererText() default_combo.pack_start(cell, True) default_combo.set_cell_data_func(cell, draw_rating, None) default_combo.connect('changed', default_rating_changed, model) default_lab.set_mnemonic_widget(default_combo) def refresh_default_combo(num): populate_default_rating_model(default_combo, num) # Rating Scale model = Gtk.ListStore(int) scale_combo = Gtk.ComboBox(model=model) scale_lab = Gtk.Label(label=_("Rating _scale:")) scale_lab.set_use_underline(True) scale_lab.set_mnemonic_widget(scale_combo) cell = Gtk.CellRendererText() scale_combo.pack_start(cell, False) num = RATINGS.number for i in [1, 2, 3, 4, 5, 6, 8, 10]: it = model.append(row=[i]) if i == num: scale_combo.set_active_iter(it) def draw_rating_scale(column, cell, model, it, data): num_stars = model[it][0] text = "%d: %s" % (num_stars, RATINGS.full_symbol * num_stars) cell.set_property('text', text) def rating_scale_changed(combo, model): it = combo.get_active_iter() if it is None: return RATINGS.number = num = model[it][0] refresh_default_combo(num) refresh_default_combo(RATINGS.number) scale_combo.set_cell_data_func(cell, draw_rating_scale, None) scale_combo.connect('changed', rating_scale_changed, model) default_align = Align(halign=Gtk.Align.START) default_align.add(default_lab) scale_align = Align(halign=Gtk.Align.START) scale_align.add(scale_lab) grid = Gtk.Grid(column_spacing=6, row_spacing=6) grid.add(scale_align) grid.add(scale_combo) grid.attach(default_align, 0, 1, 1, 1) grid.attach(default_combo, 1, 1, 1, 1) vb.pack_start(grid, False, False, 6) # Bayesian Factor bayesian_factor = config.getfloat("settings", "bayesian_rating_factor", 0.0) adj = Gtk.Adjustment.new(bayesian_factor, 0.0, 10.0, 0.5, 0.5, 0.0) bayes_spin = Gtk.SpinButton(adjustment=adj, numeric=True) bayes_spin.set_digits(1) bayes_spin.connect('changed', self.__changed_and_signal_library, 'settings', 'bayesian_rating_factor') bayes_spin.set_tooltip_text( _("Bayesian Average factor (C) for aggregated ratings.\n" "0 means a conventional average, higher values mean that " "albums with few tracks will have less extreme ratings. " "Changing this value triggers a re-calculation for all " "albums.")) bayes_label = Gtk.Label(label=_("_Bayesian averaging amount:")) bayes_label.set_use_underline(True) bayes_label.set_mnemonic_widget(bayes_spin) # Save Ratings hb = Gtk.HBox(spacing=6) hb.pack_start(bayes_label, False, True, 0) hb.pack_start(bayes_spin, False, True, 0) vb.pack_start(hb, True, True, 0) cb = CCB(_("Save ratings and play _counts"), "editing", "save_to_songs", populate=True) vb.pack_start(cb, True, True, 0) hb = Gtk.HBox(spacing=6) lab = Gtk.Label(label=_("_Email:")) entry = UndoEntry() entry.set_tooltip_text(_("Ratings and play counts will be set " "for this email address")) entry.set_text(config.get("editing", "save_email")) entry.connect('changed', self.__changed, 'editing', 'save_email') hb.pack_start(lab, False, True, 0) hb.pack_start(entry, True, True, 0) lab.set_mnemonic_widget(entry) lab.set_use_underline(True) vb.pack_start(hb, True, True, 0) return vb
def __init_pipeline(self): """Creates a gstreamer pipeline. Returns True on success.""" if self.bin: return True # reset error state self.error = False pipeline = config.get("player", "gst_pipeline") try: pipeline, self._pipeline_desc = GStreamerSink(pipeline) except PlayerError as e: self._error(e) return False if self._use_eq and Gst.ElementFactory.find('equalizer-10bands'): # The equalizer only operates on 16-bit ints or floats, and # will only pass these types through even when inactive. # We push floats through to this point, then let the second # audioconvert handle pushing to whatever the rest of the # pipeline supports. As a bonus, this seems to automatically # select the highest-precision format supported by the # rest of the chain. filt = Gst.ElementFactory.make('capsfilter', None) filt.set_property('caps', Gst.Caps.from_string('audio/x-raw,format=F32LE')) eq = Gst.ElementFactory.make('equalizer-10bands', None) self._eq_element = eq self.update_eq_values() conv = Gst.ElementFactory.make('audioconvert', None) resample = Gst.ElementFactory.make('audioresample', None) pipeline = [filt, eq, conv, resample] + pipeline # playbin2 has started to control the volume through pulseaudio, # which means the volume property can change without us noticing. # Use our own volume element for now until this works with PA. self._int_vol_element = Gst.ElementFactory.make('volume', None) pipeline.insert(0, self._int_vol_element) # Get all plugin elements and append audio converters. # playbin already includes one at the end plugin_pipeline = [] for plugin in self._get_plugin_elements(): plugin_pipeline.append(plugin) plugin_pipeline.append( Gst.ElementFactory.make('audioconvert', None)) plugin_pipeline.append( Gst.ElementFactory.make('audioresample', None)) pipeline = plugin_pipeline + pipeline bufbin = Gst.Bin() for element in pipeline: assert element is not None, pipeline bufbin.add(element) if len(pipeline) > 1: if not link_many(pipeline): print_w("Linking the GStreamer pipeline failed") self._error( PlayerError(_("Could not create GStreamer pipeline"))) return False # see if the sink provides a volume property, if yes, use it sink_element = pipeline[-1] if isinstance(sink_element, Gst.Bin): sink_element = iter_to_list(sink_element.iterate_recurse)[-1] self._ext_vol_element = None if hasattr(sink_element.props, "volume"): self._ext_vol_element = sink_element # In case we use the sink volume directly we can increase buffering # without affecting the volume change delay too much and safe some # CPU time... (2x default for now). if hasattr(sink_element.props, "buffer_time"): sink_element.set_property("buffer-time", 400000) def ext_volume_notify(*args): # gets called from a thread GLib.idle_add(self.notify, "volume") self._ext_vol_element.connect("notify::volume", ext_volume_notify) self._ext_mute_element = None if hasattr(sink_element.props, "mute") and \ sink_element.get_factory().get_name() != "directsoundsink": # directsoundsink has a mute property but it doesn't work # https://bugzilla.gnome.org/show_bug.cgi?id=755106 self._ext_mute_element = sink_element def mute_notify(*args): # gets called from a thread GLib.idle_add(self.notify, "mute") self._ext_mute_element.connect("notify::mute", mute_notify) # Make the sink of the first element the sink of the bin gpad = Gst.GhostPad.new('sink', pipeline[0].get_static_pad('sink')) bufbin.add_pad(gpad) bin_ = Gst.ElementFactory.make('playbin', None) assert bin_ self.bin = BufferingWrapper(bin_, self) self._seeker = Seeker(self.bin, self) bus = bin_.get_bus() bus.add_signal_watch() self.__bus_id = bus.connect('message', self.__message, self._librarian) self.__atf_id = self.bin.connect('about-to-finish', self.__about_to_finish) # set buffer duration duration = config.getfloat("player", "gst_buffer") self._set_buffer_duration(int(duration * 1000)) # connect playbin to our pluing/volume/eq pipeline self.bin.set_property('audio-sink', bufbin) # by default playbin will render video -> suppress using fakesink fakesink = Gst.ElementFactory.make('fakesink', None) self.bin.set_property('video-sink', fakesink) # disable all video/text decoding in playbin GST_PLAY_FLAG_VIDEO = 1 << 0 GST_PLAY_FLAG_TEXT = 1 << 2 flags = self.bin.get_property("flags") flags &= ~(GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT) self.bin.set_property("flags", flags) # find the (uri)decodebin after setup and use autoplug-sort # to sort elements like decoders def source_setup(*args): def autoplug_sort(decode, pad, caps, factories): def set_prio(x): i, f = x i = {"mad": -1, "mpg123audiodec": -2}.get(f.get_name(), i) return (i, f) return list( zip(*sorted(map(set_prio, enumerate(factories)))))[1] for e in iter_to_list(self.bin.iterate_recurse): try: e.connect("autoplug-sort", autoplug_sort) except TypeError: pass else: break self.bin.connect("source-setup", source_setup) if not self.has_external_volume: # Restore volume/ReplayGain and mute state self.volume = self._volume self.mute = self._mute # ReplayGain information gets lost when destroying self._reset_replaygain() if self.song: self.bin.set_property('uri', self.song("~uri")) return True
def __init__(self, player, debug=False): super(GstPlayerPreferences, self).__init__(spacing=6) e = UndoEntry() e.set_tooltip_text(_("The GStreamer output pipeline used for " "playback. Leave blank for the default pipeline. " "In case the pipeline contains a sink, " "it will be used instead of the default one.")) e.set_text(config.get('player', 'gst_pipeline')) def changed(entry): config.set('player', 'gst_pipeline', entry.get_text()) e.connect('changed', changed) pipe_label = Gtk.Label(label=_('_Output pipeline:')) pipe_label.set_use_underline(True) pipe_label.set_mnemonic_widget(e) apply_button = Gtk.Button(stock=Gtk.STOCK_APPLY) def format_buffer(scale, value): return _("%.1f seconds") % value def scale_changed(scale): duration_msec = int(scale.get_value() * 1000) player._set_buffer_duration(duration_msec) duration = config.getfloat("player", "gst_buffer") scale = Gtk.HScale.new( Gtk.Adjustment(value=duration, lower=0.2, upper=10)) scale.set_value_pos(Gtk.PositionType.RIGHT) scale.set_show_fill_level(True) scale.connect('format-value', format_buffer) scale.connect('value-changed', scale_changed) buffer_label = Gtk.Label(label=_('_Buffer duration:')) buffer_label.set_use_underline(True) buffer_label.set_mnemonic_widget(scale) def rebuild_pipeline(*args): player._rebuild_pipeline() apply_button.connect('clicked', rebuild_pipeline) gapless_button = ConfigCheckButton( _('Disable _gapless playback'), "player", "gst_disable_gapless", populate=True) gapless_button.set_alignment(0.0, 0.5) gapless_button.set_tooltip_text( _("Disabling gapless playback can avoid track changing problems " "with some GStreamer versions.")) widgets = [(pipe_label, e, apply_button), (buffer_label, scale, None), ] table = Gtk.Table(n_rows=len(widgets), n_columns=3) table.set_col_spacings(6) table.set_row_spacings(6) for i, (left, middle, right) in enumerate(widgets): left.set_alignment(0.0, 0.5) table.attach(left, 0, 1, i, i + 1, xoptions=Gtk.AttachOptions.FILL | Gtk.AttachOptions.SHRINK) if right: table.attach(middle, 1, 2, i, i + 1) table.attach(right, 2, 3, i, i + 1, xoptions=Gtk.AttachOptions.FILL | Gtk.AttachOptions.SHRINK) else: table.attach(middle, 1, 3, i, i + 1) table.attach(gapless_button, 0, 3, 2, 3) self.pack_start(table, True, True, 0) if debug: def print_bin(player): player._print_pipeline() b = Button("Print Pipeline", Gtk.STOCK_DIALOG_INFO) connect_obj(b, 'clicked', print_bin, player) self.pack_start(b, True, True, 0)
def __init__(self, player, debug=False): super().__init__(spacing=6) e = UndoEntry() e.set_tooltip_text( _("The GStreamer output pipeline used for " "playback. Leave blank for the default pipeline. " "In case the pipeline contains a sink, " "it will be used instead of the default one.")) e.set_text(config.get('player', 'gst_pipeline')) def changed(entry): config.set('player', 'gst_pipeline', entry.get_text()) e.connect('changed', changed) pipe_label = Gtk.Label(label=_('_Output pipeline:')) pipe_label.set_use_underline(True) pipe_label.set_mnemonic_widget(e) apply_button = Button(_("_Apply")) def format_buffer(scale, value): return _("%.1f seconds") % value def scale_changed(scale): duration_msec = int(scale.get_value() * 1000) player._set_buffer_duration(duration_msec) duration = config.getfloat("player", "gst_buffer") scale = Gtk.HScale.new( Gtk.Adjustment(value=duration, lower=0.2, upper=10)) scale.set_value_pos(Gtk.PositionType.RIGHT) scale.set_show_fill_level(True) scale.connect('format-value', format_buffer) scale.connect('value-changed', scale_changed) buffer_label = Gtk.Label(label=_('_Buffer duration:')) buffer_label.set_use_underline(True) buffer_label.set_mnemonic_widget(scale) def rebuild_pipeline(*args): player._rebuild_pipeline() apply_button.connect('clicked', rebuild_pipeline) gapless_button = ConfigCheckButton( _('Disable _gapless playback'), "player", "gst_disable_gapless", populate=True, tooltip=_( "Disabling gapless playback can avoid track changing problems " "with some GStreamer versions")) jack_button = ConfigCheckButton( _('Use JACK for playback if available'), "player", "gst_use_jack", populate=True, tooltip=_( "Uses `jackaudiosink` for playbin sink if it can be detected")) jack_connect = ConfigCheckButton( _('Auto-connect to JACK output devices'), "player", "gst_jack_auto_connect", populate=True, tooltip=_("Tells `jackaudiosink` to auto-connect")) def _jack_toggled(widget: ConfigCheckButton) -> None: jack_connect.set_sensitive(widget.get_active()) jack_button.connect("clicked", _jack_toggled) _jack_toggled(jack_button) widgets = [(pipe_label, e, apply_button), (buffer_label, scale, None)] table = Gtk.Table(n_rows=len(widgets) + 3, n_columns=3) table.set_col_spacings(6) table.set_row_spacings(6) for i, (left, middle, right) in enumerate(widgets): left.set_alignment(0.0, 0.5) table.attach(left, 0, 1, i, i + 1, xoptions=Gtk.AttachOptions.FILL | Gtk.AttachOptions.SHRINK) if right: table.attach(middle, 1, 2, i, i + 1) table.attach(right, 2, 3, i, i + 1, xoptions=Gtk.AttachOptions.FILL | Gtk.AttachOptions.SHRINK) else: table.attach(middle, 1, 3, i, i + 1) table.attach(gapless_button, 0, 3, 2, 3) table.attach(jack_button, 0, 3, 3, 4) table.attach(jack_connect, 0, 3, 4, 5) self.pack_start(table, True, True, 0) if debug: def print_bin(player): player._print_pipeline() b = Button("Print Pipeline", Icons.DIALOG_INFORMATION) connect_obj(b, 'clicked', print_bin, player) self.pack_start(b, True, True, 0)
def ratings_vbox(self): """Returns a new VBox containing all ratings widgets""" vb = Gtk.VBox(spacing=6) # Default Rating model = Gtk.ListStore(float) default_combo = Gtk.ComboBox(model=model) default_lab = Gtk.Label(label=_("_Default rating:")) default_lab.set_use_underline(True) default_lab.set_alignment(0, 0.5) def draw_rating(column, cell, model, it, data): num = model[it][0] text = "%0.2f: %s" % (num, util.format_rating(num)) cell.set_property('text', text) def default_rating_changed(combo, model): it = combo.get_active_iter() if it is None: return RATINGS.default = model[it][0] def populate_default_rating_model(combo, num): model = combo.get_model() model.clear() deltas = [] default = RATINGS.default precision = RATINGS.precision for i in range(0, num + 1): r = i * precision model.append(row=[r]) deltas.append((abs(default - r), i)) active = sorted(deltas)[0][1] print_d("Choosing #%d (%.2f), closest to current %.2f" % (active, precision * active, default)) combo.set_active(active) cell = Gtk.CellRendererText() default_combo.pack_start(cell, True) default_combo.set_cell_data_func(cell, draw_rating, None) default_combo.connect('changed', default_rating_changed, model) default_lab.set_mnemonic_widget(default_combo) def refresh_default_combo(num): populate_default_rating_model(default_combo, num) # Rating Scale model = Gtk.ListStore(int) scale_combo = Gtk.ComboBox(model=model) scale_lab = Gtk.Label(label=_("Rating _Scale:")) scale_lab.set_use_underline(True) scale_lab.set_mnemonic_widget(scale_combo) cell = Gtk.CellRendererText() scale_combo.pack_start(cell, False) num = RATINGS.number for i in [1, 2, 3, 4, 5, 6, 8, 10]: it = model.append(row=[i]) if i == num: scale_combo.set_active_iter(it) def draw_rating_scale(column, cell, model, it, data): num_stars = model[it][0] text = "%d: %s" % (num_stars, RATINGS.full_symbol * num_stars) cell.set_property('text', text) def rating_scale_changed(combo, model): it = combo.get_active_iter() if it is None: return RATINGS.number = num = model[it][0] refresh_default_combo(num) refresh_default_combo(RATINGS.number) scale_combo.set_cell_data_func(cell, draw_rating_scale, None) scale_combo.connect('changed', rating_scale_changed, model) default_align = Gtk.Alignment(xalign=0, xscale=0) default_align.add(default_lab) default_combo_align = Gtk.Alignment(xalign=0, xscale=0) default_combo_align.add(default_combo) scale_align = Gtk.Alignment(xalign=0, xscale=0) scale_align.add(scale_lab) grid = Gtk.Grid(column_spacing=6, row_spacing=6) grid.add(scale_align) grid.add(scale_combo) grid.attach(default_align, 0, 1, 1, 1) grid.attach(default_combo_align, 1, 1, 1, 1) vb.pack_start(grid, False, False, 6) # Bayesian Factor bayesian_factor = config.getfloat("settings", "bayesian_rating_factor", 0.0) adj = Gtk.Adjustment.new(bayesian_factor, 0.0, 10.0, 0.5, 0.5, 0.0) bayes_spin = Gtk.SpinButton(adjustment=adj, numeric=True) bayes_spin.set_digits(1) bayes_spin.connect('changed', self.__changed_and_signal_library, 'settings', 'bayesian_rating_factor') bayes_spin.set_tooltip_text( _("Bayesian Average factor (C) for aggregated ratings.\n" "0 means a conventional average, higher values mean that " "albums with few tracks will have less extreme ratings. " "Changing this value triggers a re-calculation for all " "albums.")) bayes_label = Gtk.Label(label=_("_Bayesian averaging amount:")) bayes_label.set_use_underline(True) bayes_label.set_mnemonic_widget(bayes_spin) # Save Ratings hb = Gtk.HBox(spacing=6) hb.pack_start(bayes_label, False, True, 0) hb.pack_start(bayes_spin, False, True, 0) vb.pack_start(hb, True, True, 0) cb = CCB(_("Save ratings and play _counts"), "editing", "save_to_songs", populate=True) vb.pack_start(cb, True, True, 0) hb = Gtk.HBox(spacing=6) lab = Gtk.Label(label=_("_Email:")) entry = UndoEntry() entry.set_tooltip_text(_("Ratings and play counts will be set " "for this email address")) entry.set_text(config.get("editing", "save_email")) entry.connect('changed', self.__changed, 'editing', 'save_email') hb.pack_start(lab, False, True, 0) hb.pack_start(entry, True, True, 0) lab.set_mnemonic_widget(entry) lab.set_use_underline(True) vb.pack_start(hb, True, True, 0) return vb
def __init__(self): super(PreferencesWindow.Player, self).__init__(spacing=12) self.set_border_width(12) self.title = _("Playback") # player backend if app.player and hasattr(app.player, 'PlayerPreferences'): player_prefs = app.player.PlayerPreferences() f = qltk.Frame(_("Output Configuration"), child=player_prefs) self.pack_start(f, False, True, 0) # replaygain fallback_gain = config.getfloat("player", "fallback_gain", 0.0) adj = Gtk.Adjustment.new(fallback_gain, -12.0, 12.0, 0.5, 0.5, 0.0) fb_spin = Gtk.SpinButton(adjustment=adj) fb_spin.set_digits(1) fb_spin.connect('changed', self.__changed, 'player', 'fallback_gain') fb_spin.set_tooltip_text( _("If no Replay Gain information is available " "for a song, scale the volume by this value")) fb_label = Gtk.Label(label=_("_Fall-back gain (dB):")) fb_label.set_use_underline(True) fb_label.set_mnemonic_widget(fb_spin) pre_amp_gain = config.getfloat("player", "pre_amp_gain", 0.0) adj = Gtk.Adjustment.new(pre_amp_gain, -6, 6, 0.5, 0.5, 0.0) adj.connect('value-changed', self.__changed, 'player', 'pre_amp_gain') pre_spin = Gtk.SpinButton(adjustment=adj) pre_spin.set_digits(1) pre_spin.set_tooltip_text( _("Scale volume for all songs by this value, " "as long as the result will not clip")) pre_label = Gtk.Label(label=_("_Pre-amp gain (dB):")) pre_label.set_use_underline(True) pre_label.set_mnemonic_widget(pre_spin) widgets = [pre_label, pre_spin, fb_label, fb_spin] c = CCB(_("_Enable Replay Gain volume adjustment"), "player", "replaygain", populate=True) c.connect('toggled', self.__toggled_gain, widgets) # packing table = Gtk.Table.new(3, 2, False) table.set_col_spacings(6) table.set_row_spacings(6) table.attach(c, 0, 2, 0, 1) fb_label.set_alignment(0, 0.5) table.attach(fb_label, 0, 1, 1, 2, xoptions=0) pre_label.set_alignment(0, 0.5) table.attach(pre_label, 0, 1, 2, 3, xoptions=0) fb_align = Gtk.Alignment.new(0, 0.5, 0, 1) fb_align.add(fb_spin) table.attach(fb_align, 1, 2, 1, 2) pre_align = Gtk.Alignment.new(0, 0.5, 0, 1) pre_align.add(pre_spin) table.attach(pre_align, 1, 2, 2, 3) f = qltk.Frame(_("Replay Gain Volume Adjustment"), child=table) c.emit('toggled') self.pack_start(f, False, True, 0) for child in self.get_children(): child.show_all()
def __init__(self, browser): if self.is_not_unique(): return super(Preferences, self).__init__() self.set_border_width(12) self.set_title(_("Cover Grid Preferences")) self.set_default_size(420, 380) self.set_transient_for(qltk.get_top_parent(browser)) # Do this config-driven setup at instance-time self._PREVIEW_ITEM["~rating"] = format_rating(0.75) self.mag_lock = False box = Gtk.VBox(spacing=6) vbox = Gtk.VBox(spacing=6) cb = ConfigCheckButton( _("Show album _text"), "browsers", "album_text") cb.set_active(config.getboolean("browsers", "album_text")) cb.connect('toggled', lambda s: browser.toggle_text()) vbox.pack_start(cb, False, True, 0) cb2 = ConfigCheckButton( _("Show \"All Albums\" Item"), "browsers", "covergrid_all") cb2.set_active(config.getboolean("browsers", "covergrid_all", False)) cb2.connect('toggled', lambda s: browser.view.get_model().refilter()) vbox.pack_start(cb2, False, True, 0) cb3 = ConfigCheckButton( _("Wide Mode"), "browsers", "covergrid_wide") cb3.set_active(config.getboolean("browsers", "covergrid_wide", False)) cb3.connect('toggled', lambda s: browser.toggle_wide()) vbox.pack_start(cb3, False, True, 0) # Redraws the covers only when the user releases the slider def mag_button_press(*_): self.mag_lock = True def mag_button_release(mag, _): self.mag_lock = False mag_changed(mag) def mag_changed(mag): if self.mag_lock: return newmag = mag.get_value() oldmag = config.getfloat("browsers", "covergrid_magnification", 3.) if newmag == oldmag: print_d("Covergrid magnification haven't changed: {0}" .format(newmag)) return print_d('Covergrid magnification update from {0} to {1}' .format(oldmag, newmag)) config.set("browsers", "covergrid_magnification", mag.get_value()) browser.update_mag() mag_scale = Gtk.HScale( adjustment=Gtk.Adjustment.new(config.getfloat("browsers", "covergrid_magnification", 3), 0., 10., .5, .5, 0)) mag_scale.set_tooltip_text(_("Cover Magnification")) l = Gtk.Label(label=_("Cover Magnification")) mag_scale.set_value_pos(Gtk.PositionType.RIGHT) mag_scale.connect('button-press-event', mag_button_press) mag_scale.connect('button-release-event', mag_button_release) mag_scale.connect('value-changed', mag_changed) vbox.pack_start(l, False, True, 0) vbox.pack_start(mag_scale, False, True, 0) f = qltk.Frame(_("Options"), child=vbox) box.pack_start(f, False, True, 12) display_frame = self.edit_display_pane(browser, _("Album Display")) box.pack_start(display_frame, True, True, 0) main_box = Gtk.VBox(spacing=12) close = Button(_("_Close"), Icons.WINDOW_CLOSE) close.connect('clicked', lambda *x: self.destroy()) b = Gtk.HButtonBox() b.set_layout(Gtk.ButtonBoxStyle.END) b.pack_start(close, True, True, 0) main_box.pack_start(box, True, True, 0) self.use_header_bar() if not self.has_close_button(): main_box.pack_start(b, False, True, 0) self.add(main_box) close.grab_focus() self.show_all()
def on_reverted(*args): config.reset(section, option) scale.set_value(config.getfloat(section, option))