Change the colour of pixels in a GdkPixbuf (GTK3)

我的梦境 提交于 2019-12-08 03:47:59

问题


I'm using Gtk.StatusIcon, and want to change the colour of some pixels; I have a working piece of code, this loads a 1x1 pixel PNG file with the colour I want to set, and then copies that to thhe icon Pixbuf.
While this works, it has the obvious drawback of having to create a 1x1 pixel for every colour, so I can't use an arbitrary colour, only the predefined colours I created images for.

How do I set a pixel to an arbitrary RGBA colour?

The code I'm using now (simplified for demonstration purposes):

#!/usr/bin/env python

from gi.repository import Gtk, GLib, GdkPixbuf

def set_icon_cb(widget, data=None):
    set_icon(widget)

def set_icon(icon):
    fill_amount = 20
    img = GdkPixbuf.Pixbuf.new_from_file('./data/icons/battery.png')
    fill = GdkPixbuf.Pixbuf.new_from_file('./data/icons/green.png')

    for row_num, row in enumerate(zip(*(iter(img.get_pixels()),) *img.get_rowstride())):
        # Blank row
        if 255 not in row: continue

        for col_num, pixel in enumerate(zip(*(iter(row),) * 4)):
            r, g, b, a = pixel

            if col_num > 2 and col_num <= fill_amount and a == 0:
                fill.copy_area(0, 0, 1, 1, img, col_num, row_num)

    icon.set_from_pixbuf(img)


icon = Gtk.StatusIcon()
icon.connect('activate', set_icon_cb)
set_icon(icon)

Gtk.main()

I tried creating a new Pixbuf object with GdkPixbuf.PixbufLoader, but this seems to expect a PNG image, not a bytes object, so this isn't very helpful:

fill = GdkPixbuf.PixbufLoader()
fill.write(b'\x88\x88\x88\xff')
fill.close()
fill = fill.get_pixbuf()

# Gives error:
# GLib.Error: gdk-pixbuf-error-quark: Unrecognized image file format (3)

My next try was to use GdkPixbuf.Pixbuf.new_from_data, which looked promossing:

fill = GdkPixbuf.Pixbuf.new_from_data(b'\xff\x00\xff', GdkPixbuf.Colorspace.RGB,
    False, 8, 1, 1, 3)

However, this doesn't work either. It not only sets the pixels to the wrong colour, it also sets it to different colours on multiple invocations of set_icon(); print(fill.get_pixels()) gives me b'\x00\x00\x00'... Am I using this wrong? I tied various different parameters, but all give the same result...

I also found a C example which modified the result of gdk_pixbuf_get_pixels(), since this returns a pointer to the image data. But this is not something you can do in Python (AFAIK).

A little background of what I'm trying to accomplish:
I have a little tray application to show my laptop's battery status; it fills up the battery icon to give an indication of how much battery power is left. Below a certain percentage the colour changes from green to red (this works with the above example), but I would like to have more flexibility in this; eg. allowing other people to set their own shade of green, or use purple, or whatever.


回答1:


It turned out to be fairly simple, but not at all obvious from reading the docs:

# red, green, blue, alpha
color = 0xeeff2dff

# Create blank 1x1 image
fill = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, True, 8, 1, 1)

# Fill entire image with color
fill.fill(color)

My original solution, left here for archival purposes; this is more complicated & doesn't work with transparency, but it might be better suited for more advanced operations

After more mucking about I ended up using GdkPixbuf.PixbufLoader.new_with_type, from:

list(map(lambda x: x.get_name(), GdkPixbuf.Pixbuf.get_formats()))

I choose the simplest image format, which appears to be the "portable anymap format" or pnm.

I created a simple 1x1 image with GIMP, which gave me this data:

>>> open('test.pnm', 'rb').read()
b'P6\n# CREATOR: GIMP PNM Filter Version 1.1\n1 1\n255\n\xff\x00\xff'

The last 3 bytes (\xff\x00\xff) is the colour I choose.

Full example I ended up using:

color = b'\xee\xff\x2d'

px = GdkPixbuf.PixbufLoader.new_with_type('pnm')
px.write(b'P6\n\n1 1\n255\n' + color)
px.write(color)
px.close()
fill = px.get_pixbuf()

I can then use fill as in the original example with fill.copy_area()

It's a bit of a workaround, but acceptable...

PS.
From the documentation, GdkPixbuf.Pixbuf.new_from_data looks like the best option to do this, but it seems broken... I can't it to work no matter what I do...



来源:https://stackoverflow.com/questions/26713547/change-the-colour-of-pixels-in-a-gdkpixbuf-gtk3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!