def do_draw(self, cairo_context): pixbuf = self._get_pixbuf() if not pixbuf: return alloc = self.get_allocation() width, height = alloc.width, alloc.height scale_factor = get_scale_factor(self) width *= scale_factor height *= scale_factor if self._path: if width < (2 * scale_factor) or height < (2 * scale_factor): return pixbuf = scale( pixbuf, (width - 2 * scale_factor, height - 2 * scale_factor)) pixbuf = add_border_widget(pixbuf, self) else: pixbuf = scale(pixbuf, (width, height)) style_context = self.get_style_context() pbosf = get_pbosf_for_pixbuf(self, pixbuf) pbosf_render(style_context, cairo_context, pbosf, 0, 0)
def do_draw(self, cairo_context): pixbuf = self._get_pixbuf() if not pixbuf: return alloc = self.get_allocation() width, height = alloc.width, alloc.height scale_factor = get_scale_factor(self) width *= scale_factor height *= scale_factor if self._path: if width < 2 or height < 2: return round_thumbs = config.getboolean("albumart", "round") pixbuf = scale( pixbuf, (width - 2 * scale_factor, height - 2 * scale_factor)) pixbuf = add_border_widget(pixbuf, self, None, round_thumbs) else: pixbuf = scale(pixbuf, (width, height)) style_context = self.get_style_context() pbosf = get_pbosf_for_pixbuf(self, pixbuf) pbosf_render(style_context, cairo_context, pbosf, 0, 0)
def _no_cover(self): """Returns a cairo surface of pixbuf representing a missing cover""" cover_size = Album.COVER_SIZE scale_factor = get_scale_factor(self) pb = get_no_cover_pixbuf(cover_size, cover_size, scale_factor) return get_pbosf_for_pixbuf(self, pb)
def do_draw(self, cairo_context): pixbuf = self._get_pixbuf() if not pixbuf: return alloc = self.get_allocation() width, height = alloc.width, alloc.height scale_factor = get_scale_factor(self) width *= scale_factor height *= scale_factor if self._path: if width < (2 * scale_factor) or height < (2 * scale_factor): return round_thumbs = config.getboolean("albumart", "round") pixbuf = scale( pixbuf, (width - 2 * scale_factor, height - 2 * scale_factor)) pixbuf = add_border_widget(pixbuf, self, round_thumbs) else: pixbuf = scale(pixbuf, (width, height)) style_context = self.get_style_context() pbosf = get_pbosf_for_pixbuf(self, pixbuf) pbosf_render(style_context, cairo_context, pbosf, 0, 0)
def test_pbosf_get_width_height(self): w = Gtk.Button() rgb = GdkPixbuf.Colorspace.RGB s = get_scale_factor(w) newpb = GdkPixbuf.Pixbuf.new(rgb, True, 8, 10 * s, 15 * s) pbosf = get_pbosf_for_pixbuf(w, newpb) self.assertEqual(pbosf_get_width(pbosf), 10) self.assertEqual(pbosf_get_height(pbosf), 15)
def add_border_widget(pixbuf, widget, round=False): """Like add_border() but uses the widget to get a border color and a border width. """ from quodlibet.qltk.image import get_scale_factor context = widget.get_style_context() color = context.get_color(context.get_state()) scale_factor = get_scale_factor(widget) return add_border(pixbuf, color, round=round, width=scale_factor)
def get_scaled_cover(album): # XXX: Cache this somewhere else cover = None if not hasattr(album, "_scaled_cover"): scale_factor = get_scale_factor(self) album.scan_cover(scale_factor=scale_factor) if album.cover: s = 25 * scale_factor cover = scale(album.cover, (s, s)) album._scaled_cover = cover else: cover = album._scaled_cover return cover
def add_border_widget(pixbuf, widget): """Like add_border() but uses the widget to get a border color and a border width. """ from quodlibet.qltk.image import get_scale_factor context = widget.get_style_context() color = context.get_color(context.get_state()) scale_factor = get_scale_factor(widget) border_radius = get_border_radius() * scale_factor return add_border(pixbuf, color, width=scale_factor, radius=border_radius)
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_)) def callback(): path = tref.get_path() if path is not None: model.row_changed(path, model.get_iter(path)) album = model.get_album(iter_) scale_factor = get_scale_factor(self) album.scan_cover(scale_factor=scale_factor, callback=callback, cancel=self._cover_cancel)
def __init__(self, title, fileobj, parent): super(BigCenteredImage, self).__init__(type=Gtk.WindowType.POPUP) self.set_type_hint(Gdk.WindowTypeHint.TOOLTIP) assert parent parent = qltk.get_top_parent(parent) self.set_transient_for(parent) if qltk.is_wayland(): # no screen size with wayland, the parent window is # the next best thing.. width, height = parent.get_size() width = int(width / 1.1) height = int(height / 1.1) else: width = int(Gdk.Screen.width() / 1.75) height = int(Gdk.Screen.height() / 1.75) self.set_position(Gtk.WindowPosition.CENTER_ON_PARENT) scale_factor = get_scale_factor(self) pixbuf = None try: pixbuf = pixbuf_from_file(fileobj, (width, height), scale_factor) except GLib.GError: pass # failed to load, abort if not pixbuf: self.destroy() return image = Gtk.Image() set_image_from_pbosf(image, get_pbosf_for_pixbuf(self, pixbuf)) event_box = Gtk.EventBox() event_box.add(image) frame = Gtk.Frame() frame.set_shadow_type(Gtk.ShadowType.OUT) frame.add(event_box) self.add(frame) event_box.connect('button-press-event', self.__destroy) event_box.connect('key-press-event', self.__destroy) self.get_child().show_all()
def _get_pixbuf(self): if not self._dirty: return self._pixbuf self._dirty = False max_size = 256 * get_scale_factor(self) self._pixbuf = None if self._file: self._pixbuf = thumbnails.get_thumbnail_from_file( self._file, (max_size, max_size)) if not self._pixbuf: self._pixbuf = get_no_cover_pixbuf(max_size, max_size) return self._pixbuf
def add_border_widget(pixbuf, widget, cell=None, round=False): """Like add_border() but uses the widget to get a border color and a border width. """ from quodlibet.qltk.image import get_scale_factor context = widget.get_style_context() if cell is not None: state = cell.get_state(widget, 0) else: state = widget.get_state_flags() color = context.get_color(state) scale_factor = get_scale_factor(widget) return add_border(pixbuf, color, round=round, width=scale_factor)
def __scale_pixbuf(self, *data): if not self.current_pixbuf: return pixbuf = self.current_pixbuf if not self.window_fit.get_active(): pbosf = pixbuf else: alloc = self.scrolled.get_allocation() width = alloc.width height = alloc.height scale_factor = get_scale_factor(self) boundary = (width * scale_factor, height * scale_factor) pixbuf = scale(pixbuf, boundary, scale_up=False) pbosf = get_pbosf_for_pixbuf(self, pixbuf) set_image_from_pbosf(self.image, pbosf)
def __add_cover_to_list(self, cover): try: pbloader = GdkPixbuf.PixbufLoader() pbloader.write(get_url(cover['thumbnail'])[0]) pbloader.close() scale_factor = get_scale_factor(self) size = self.THUMB_SIZE * scale_factor - scale_factor * 2 pixbuf = pbloader.get_pixbuf().scale_simple(size, size, GdkPixbuf.InterpType.BILINEAR) pixbuf = add_border_widget(pixbuf, self, None, round=True) thumb = get_pbosf_for_pixbuf(self, pixbuf) except (GLib.GError, IOError): pass else: def append(data): self.liststore.append(data) GLib.idle_add(append, [thumb, cover])
def __add_cover_to_list(self, cover): try: pbloader = GdkPixbuf.PixbufLoader() pbloader.write(get_url(cover['thumbnail'])[0]) pbloader.close() scale_factor = get_scale_factor(self) size = self.THUMB_SIZE * scale_factor - scale_factor * 2 pixbuf = pbloader.get_pixbuf().scale_simple(size, size, GdkPixbuf.InterpType.BILINEAR) pixbuf = thumbnails.add_border( pixbuf, 80, round=True, width=scale_factor) thumb = get_pbosf_for_pixbuf(self, pixbuf) except (GLib.GError, IOError): pass else: def append(data): self.liststore.append(data) GLib.idle_add(append, [thumb, cover])
def pbosf_get_height(widget, pbosf): """The scale independent height""" return pbosf.get_height() / get_scale_factor(widget)
def pbosf_get_width(widget, pbosf): """The scale independent width""" return pbosf.get_width() / get_scale_factor(widget)
def test_scale_factor(self): w = Gtk.Button() self.assertTrue(get_scale_factor(w) in range(10))
def _update_row(self, row): album = row[0] scale_factor = get_scale_factor(self) album.scan_cover(scale_factor=scale_factor) self._refresh_albums([album])
def __init__(self, conf, song): Gtk.Window.__init__(self, Gtk.WindowType.POPUP) self.set_type_hint(Gdk.WindowTypeHint.NOTIFICATION) screen = self.get_screen() rgba = screen.get_rgba_visual() if rgba is not None: self.set_visual(rgba) self.conf = conf self.iteration_source = None self.fading_in = False self.fade_start_time = 0 mgeo = screen.get_monitor_geometry(conf.monitor) textwidth = mgeo.width - 2 * (self.BORDER + self.MARGIN) scale_factor = get_scale_factor(self) self.cover_pixbuf = app.cover_manager.get_pixbuf( song, conf.coversize * scale_factor, conf.coversize * scale_factor) coverheight = 0 coverwidth = 0 if self.cover_pixbuf: self.cover_pixbuf = get_pbosf_for_pixbuf(self, self.cover_pixbuf) coverwidth = self.cover_pixbuf.get_width() // scale_factor coverheight = self.cover_pixbuf.get_height() // scale_factor textwidth -= coverwidth + self.BORDER layout = self.create_pango_layout('') layout.set_alignment((Pango.Alignment.LEFT, Pango.Alignment.CENTER, Pango.Alignment.RIGHT)[conf.align]) layout.set_spacing(Pango.SCALE * 7) layout.set_font_description(Pango.FontDescription(conf.font)) try: layout.set_markup(pattern.XMLFromMarkupPattern(conf.string) % song) except pattern.error: layout.set_markup("") layout.set_width(Pango.SCALE * textwidth) layoutsize = layout.get_pixel_size() if layoutsize[0] < textwidth: layout.set_width(Pango.SCALE * layoutsize[0]) layoutsize = layout.get_pixel_size() self.title_layout = layout winw = layoutsize[0] + 2 * self.BORDER if coverwidth: winw += coverwidth + self.BORDER winh = max(coverheight, layoutsize[1]) + 2 * self.BORDER self.set_default_size(winw, winh) rect = namedtuple("Rect", ["x", "y", "width", "height"]) rect.x = self.BORDER rect.y = (winh - coverheight) // 2 rect.width = coverwidth rect.height = coverheight self.cover_rectangle = rect winx = int((mgeo.width - winw) * self.POS_X) winx = max(self.MARGIN, min(mgeo.width - self.MARGIN - winw, winx)) winy = int((mgeo.height - winh) * conf.pos_y) winy = max(self.MARGIN, min(mgeo.height - self.MARGIN - winh, winy)) self.move(winx + mgeo.x, winy + mgeo.y)
def __init__(self, songs): super(AlbumArtWindow, self).__init__() self.image_cache = [] self.image_cache_size = 10 self.search_lock = False self.set_title(_('Album Art Downloader')) self.set_icon_name(Gtk.STOCK_FIND) self.set_default_size(800, 550) image = CoverArea(self, songs[0]) self.liststore = Gtk.ListStore(object, object) self.treeview = treeview = AllTreeView(model=self.liststore) self.treeview.set_headers_visible(False) self.treeview.set_rules_hint(True) targets = [("text/uri-list", 0, 0)] targets = [Gtk.TargetEntry.new(*t) for t in targets] treeview.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, targets, Gdk.DragAction.COPY) treeselection = self.treeview.get_selection() treeselection.set_mode(Gtk.SelectionMode.SINGLE) treeselection.connect('changed', self.__select_callback, image) self.treeview.connect("drag-data-get", self.__drag_data_get, treeselection) rend_pix = Gtk.CellRendererPixbuf() img_col = Gtk.TreeViewColumn('Thumb') img_col.pack_start(rend_pix, False) def cell_data_pb(column, cell, model, iter_, *args): pbosf = model[iter_][0] set_renderer_from_pbosf(cell, pbosf) img_col.set_cell_data_func(rend_pix, cell_data_pb, None) treeview.append_column(img_col) rend_pix.set_property('xpad', 2) rend_pix.set_property('ypad', 2) border_width = get_scale_factor(self) * 2 rend_pix.set_property('width', self.THUMB_SIZE + 4 + border_width) rend_pix.set_property('height', self.THUMB_SIZE + 4 + border_width) def escape_data(data): for rep in ('\n', '\t', '\r', '\v'): data = data.replace(rep, ' ') return util.escape(' '.join(data.split())) def cell_data(column, cell, model, iter, data): cover = model[iter][1] esc = escape_data txt = '<b><i>%s</i></b>' % esc(cover['name']) txt += "\n<small>%s</small>" % ( _('from %(source)s') % { "source": util.italic(esc(cover['source'])) }) if 'resolution' in cover: txt += "\n" + _('Resolution: %s') % util.italic( esc(cover['resolution'])) if 'size' in cover: txt += "\n" + _('Size: %s') % util.italic(esc(cover['size'])) cell.markup = txt cell.set_property('markup', cell.markup) rend = Gtk.CellRendererText() rend.set_property('ellipsize', Pango.EllipsizeMode.END) info_col = Gtk.TreeViewColumn('Info', rend) info_col.set_cell_data_func(rend, cell_data) treeview.append_column(info_col) sw_list = Gtk.ScrolledWindow() sw_list.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) sw_list.set_shadow_type(Gtk.ShadowType.IN) sw_list.add(treeview) self.search_field = Gtk.Entry() self.search_button = Gtk.Button(stock=Gtk.STOCK_FIND) self.search_button.connect('clicked', self.start_search) self.search_field.connect('activate', self.start_search) widget_space = 5 search_hbox = Gtk.HBox(spacing=widget_space) search_hbox.pack_start(self.search_field, True, True, 0) search_hbox.pack_start(self.search_button, False, True, 0) self.progress = Gtk.ProgressBar() left_vbox = Gtk.VBox(spacing=widget_space) left_vbox.pack_start(search_hbox, False, True, 0) left_vbox.pack_start(sw_list, True, True, 0) hpaned = Paned() hpaned.set_border_width(widget_space) hpaned.pack1(left_vbox, shrink=False) hpaned.pack2(image, shrink=False) hpaned.set_position(275) self.add(hpaned) self.show_all() left_vbox.pack_start(self.progress, False, True, 0) if songs[0]('albumartist'): text = songs[0]('albumartist') else: text = songs[0]('artist') text += ' - ' + songs[0]('album') self.set_text(text) self.start_search()
def __init__(self, songs): super(AlbumArtWindow, self).__init__() self.image_cache = [] self.image_cache_size = 10 self.search_lock = False self.set_title(_('Album Art Downloader')) self.set_icon_name(Icons.EDIT_FIND) self.set_default_size(800, 550) image = CoverArea(self, songs[0]) self.liststore = Gtk.ListStore(object, object) self.treeview = treeview = AllTreeView(model=self.liststore) self.treeview.set_headers_visible(False) self.treeview.set_rules_hint(True) targets = [("text/uri-list", 0, 0)] targets = [Gtk.TargetEntry.new(*t) for t in targets] treeview.drag_source_set( Gdk.ModifierType.BUTTON1_MASK, targets, Gdk.DragAction.COPY) treeselection = self.treeview.get_selection() treeselection.set_mode(Gtk.SelectionMode.SINGLE) treeselection.connect('changed', self.__select_callback, image) self.treeview.connect("drag-data-get", self.__drag_data_get, treeselection) rend_pix = Gtk.CellRendererPixbuf() img_col = Gtk.TreeViewColumn('Thumb') img_col.pack_start(rend_pix, False) def cell_data_pb(column, cell, model, iter_, *args): pbosf = model[iter_][0] set_renderer_from_pbosf(cell, pbosf) img_col.set_cell_data_func(rend_pix, cell_data_pb, None) treeview.append_column(img_col) rend_pix.set_property('xpad', 2) rend_pix.set_property('ypad', 2) border_width = get_scale_factor(self) * 2 rend_pix.set_property('width', self.THUMB_SIZE + 4 + border_width) rend_pix.set_property('height', self.THUMB_SIZE + 4 + border_width) def escape_data(data): for rep in ('\n', '\t', '\r', '\v'): data = data.replace(rep, ' ') return util.escape(' '.join(data.split())) def cell_data(column, cell, model, iter, data): cover = model[iter][1] esc = escape_data txt = '<b><i>%s</i></b>' % esc(cover['name']) txt += "\n<small>%s</small>" % ( _('from %(source)s') % { "source": util.italic(esc(cover['source']))}) if 'resolution' in cover: txt += "\n" + _('Resolution: %s') % util.italic( esc(cover['resolution'])) if 'size' in cover: txt += "\n" + _('Size: %s') % util.italic(esc(cover['size'])) cell.markup = txt cell.set_property('markup', cell.markup) rend = Gtk.CellRendererText() rend.set_property('ellipsize', Pango.EllipsizeMode.END) info_col = Gtk.TreeViewColumn('Info', rend) info_col.set_cell_data_func(rend, cell_data) treeview.append_column(info_col) sw_list = Gtk.ScrolledWindow() sw_list.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) sw_list.set_shadow_type(Gtk.ShadowType.IN) sw_list.add(treeview) self.search_field = Gtk.Entry() self.search_button = Button(_("_Search"), Icons.EDIT_FIND) self.search_button.connect('clicked', self.start_search) self.search_field.connect('activate', self.start_search) widget_space = 5 search_hbox = Gtk.HBox(spacing=widget_space) search_hbox.pack_start(self.search_field, True, True, 0) search_hbox.pack_start(self.search_button, False, True, 0) self.progress = Gtk.ProgressBar() left_vbox = Gtk.VBox(spacing=widget_space) left_vbox.pack_start(search_hbox, False, True, 0) left_vbox.pack_start(sw_list, True, True, 0) hpaned = Paned() hpaned.set_border_width(widget_space) hpaned.pack1(left_vbox, shrink=False) hpaned.pack2(image, shrink=False) hpaned.set_position(275) self.add(hpaned) self.show_all() left_vbox.pack_start(self.progress, False, True, 0) if songs[0]('albumartist'): text = songs[0]('albumartist') else: text = songs[0]('artist') text += ' - ' + songs[0]('album') self.set_text(text) self.start_search()
def create_multi_row_drag_icon(self, paths, max_rows): """Similar to create_row_drag_icon() but creates a drag icon for multiple paths or None. The resulting surface will draw max_rows rows and point out if there are more rows selected. """ if not paths: return if len(paths) == 1: return self.create_row_drag_icon(paths[0]) # create_row_drag_icon can return None icons = [self.create_row_drag_icon(p) for p in paths[:max_rows]] icons = [i for i in icons if i is not None] if not icons: return scale_factor = get_scale_factor(self) sizes = [_get_surface_size(s, scale_factor) for s in icons] if None in sizes: return width = max([s[0] for s in sizes]) height = sum([s[1] for s in sizes]) layout = None if len(paths) > max_rows: more = _(u"and %d more…") % (len(paths) - max_rows) more = "<i>%s</i>" % more layout = self.create_pango_layout("") layout.set_markup(more) layout.set_alignment(Pango.Alignment.CENTER) layout.set_width(Pango.SCALE * (width - 2)) lw, lh = layout.get_pixel_size() height += lh height += 6 # padding surface = icons[0].create_similar(cairo.CONTENT_COLOR_ALPHA, width, height) ctx = cairo.Context(surface) # render background style_ctx = self.get_style_context() Gtk.render_background(style_ctx, ctx, 0, 0, width, height) # render rows count_y = 0 for icon, (icon_width, icon_height) in zip(icons, sizes): ctx.save() ctx.set_source_surface(icon, 2, count_y + 2) ctx.rectangle(2, count_y + 2, icon_width - 4, icon_height - 4) ctx.clip() ctx.paint() ctx.restore() count_y += icon_height if layout: Gtk.render_layout(style_ctx, ctx, 1, count_y, layout) # render border Gtk.render_line(style_ctx, ctx, 0, 0, 0, height - 1) Gtk.render_line(style_ctx, ctx, 0, height - 1, width - 1, height - 1) Gtk.render_line(style_ctx, ctx, width - 1, height - 1, width - 1, 0) Gtk.render_line(style_ctx, ctx, width - 1, 0, 0, 0) return surface
def create_multi_row_drag_icon(self, paths, max_rows): """Similar to create_row_drag_icon() but creates a drag icon for multiple paths or None. The resulting surface will draw max_rows rows and point out if there are more rows selected. """ if not paths: return if len(paths) == 1: return self.create_row_drag_icon(paths[0]) # create_row_drag_icon can return None icons = [self.create_row_drag_icon(p) for p in paths[:max_rows]] icons = [i for i in icons if i is not None] if not icons: return scale_factor = get_scale_factor(self) sizes = [_get_surface_size(s, scale_factor) for s in icons] if None in sizes: return width = max([s[0] for s in sizes]) height = sum([s[1] for s in sizes]) layout = None if len(paths) > max_rows: more = _(u"and %d more…") % (len(paths) - max_rows) more = "<i>%s</i>" % more layout = self.create_pango_layout("") layout.set_markup(more) layout.set_alignment(Pango.Alignment.CENTER) layout.set_width(Pango.SCALE * (width - 2)) lw, lh = layout.get_pixel_size() height += lh height += 6 # padding surface = icons[0].create_similar( cairo.CONTENT_COLOR_ALPHA, width, height) ctx = cairo.Context(surface) # render background style_ctx = self.get_style_context() Gtk.render_background(style_ctx, ctx, 0, 0, width, height) # render rows count_y = 0 for icon, (icon_width, icon_height) in zip(icons, sizes): ctx.save() ctx.set_source_surface(icon, 2, count_y + 2) ctx.rectangle(2, count_y + 2, icon_width - 4, icon_height - 4) ctx.clip() ctx.paint() ctx.restore() count_y += icon_height if layout: Gtk.render_layout(style_ctx, ctx, 1, count_y, layout) # render border Gtk.render_line(style_ctx, ctx, 0, 0, 0, height - 1) Gtk.render_line(style_ctx, ctx, 0, height - 1, width - 1, height - 1) Gtk.render_line(style_ctx, ctx, width - 1, height - 1, width - 1, 0) Gtk.render_line(style_ctx, ctx, width - 1, 0, 0, 0) return surface