改变GdkPixbuf(GTK3)中像素的颜色
我正在使用 Gtk.StatusIcon
,想要改变一些像素的颜色。我有一段可以工作的代码,它加载了一个1x1像素的PNG文件,里面是我想要设置的颜色,然后把这个颜色复制到图标的Pixbuf中。
虽然这样可以实现,但明显的缺点是我必须为每种颜色都创建一个1x1像素的图像,所以我不能使用任意颜色,只能使用我事先制作好的颜色。
我该如何将一个像素设置为任意的RGBA颜色呢?
我现在使用的代码(为了演示简化了):
#!/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()
我尝试用 GdkPixbuf.PixbufLoader
创建一个新的Pixbuf对象,但这似乎只接受PNG图像,而不是字节对象,所以这并没有什么帮助:
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)
接下来我尝试使用 GdkPixbuf.Pixbuf.new_from_data
,看起来很有希望:
fill = GdkPixbuf.Pixbuf.new_from_data(b'\xff\x00\xff', GdkPixbuf.Colorspace.RGB,
False, 8, 1, 1, 3)
但是,这也不行。它不仅把像素设置成了错误的颜色,而且在多次调用 set_icon()
时颜色也会不同; print(fill.get_pixels())
给我的结果是 b'\x00\x00\x00'
... 我是不是用错了?我尝试了各种不同的参数,但结果都是一样的...
我还找到一个C语言的例子,它修改了 gdk_pixbuf_get_pixels()
的结果,因为这个函数返回的是图像数据的指针。但在Python中(据我所知)这是做不到的。
我想实现的目标背景是:
我有一个小的托盘应用程序,用来显示我笔记本电脑的电池状态;它通过填充电池图标来表示剩余的电量。当电量低于某个百分比时,颜色会从绿色变为红色(这在上面的例子中是可以实现的),但我希望在这方面有更多的灵活性;比如,允许其他人设置自己喜欢的绿色阴影,或者使用紫色,或者其他颜色。
1 个回答
结果发现其实很简单,但从文档上看并不明显:
# 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)
我最初的解决方案,留在这里作为存档;这个方法比较复杂,并且不支持透明度,但可能更适合一些高级操作
经过一番折腾,我最终使用了 GdkPixbuf.PixbufLoader.new_with_type
,来自于:
list(map(lambda x: x.get_name(), GdkPixbuf.Pixbuf.get_formats()))
我选择了最简单的图像格式,似乎是“可移植任意地图格式”或者说 pnm
。
我用 GIMP 创建了一个简单的 1x1 像素的图像,这样得到了以下数据:
>>> open('test.pnm', 'rb').read()
b'P6\n# CREATOR: GIMP PNM Filter Version 1.1\n1 1\n255\n\xff\x00\xff'
最后的 3 个字节(\xff\x00\xff
)就是我选择的颜色。
我最终使用的完整示例:
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()
然后我可以像原始示例那样使用 fill
,通过 fill.copy_area()
来实现。
这有点像变通办法,但也可以接受...
附注:
从文档来看,GdkPixbuf.Pixbuf.new_from_data
似乎是做这个的最佳选择,但它似乎有问题... 不管我怎么做都无法让它工作...