I'm trying to create a PyGTK StatusIcon with transparent background. I need to draw the contents of the StatusIcon at runtime.
StatusIcon wants a Pixbuf object (which can have transparency). No problem with that:
pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, width, height)
pixbuf.fill(0xffffffff)
The problem is, I can't draw to Pixbuf objects. My current approach is to draw to a Pixmap and then convert it to a Pixbuf, but unfortunately Pixmaps can't have transparency.
How do I get a Pixbuf with transparent background that I can draw at runtime?
Greets,
Philip
You can add transparency to Pixbuf objects with the add_alpha() method. The following line will set zero opacity for the color #ffffff:
pixbuf = pixbuf.add_alpha(True, 0xFF, 0xFF, 0xFF)
Too easy... :-|
Related
I use a menu icon with white background. When I start my program I see this white background on gray menu item. It does not look nice. How to make white background transparent for not to see white color?
Try these approaches:
self.setStyleSheet("background-color: transparent;)
self.setFlags(Qt.WA_TranslucentBackground)
You have also the option of setting up a mask
def make_transparent(self):
self.setStyleSheet("background: transparent;")
mMask = QPixmap(self.width(), self.height())
mMask.fill(Qt.transparent)
p = QPainter()
p.begin(mMask)
p.setPen(Qt.red)
p.setBrush(QBrush(Qt.red))
p.drawRect(QRect(0, 0, 1000, 1000))
p.end()
self.setMask(mMask.mask())
Just adapt to size and color you want.
You should have a transparent image to begin with, where everything except the menu are empty pixels( png format).
Your pyqt application can't differentiate between what's background and foreground.
You can try and remove the image background online here
This answer doesn't help with the current question but it's for the people who will come in the future looking for a way to set a transparent Icon.
So I wanted to get rid of the default windows Icon and I didn't have any logo for the application either so I thought that making the Icon transparent would get rid of that windows icon.
pixmap = QtGui.QPixmap(255, 255)
pixmap.fill(QtGui.QColor(0,0,0,0))
MainWindow.setWindowIcon(QtGui.QIcon(pixmap))
So I have a series of transparent pngs and append them to a new Image()
with Image() as new_gif:
for img_path in input_images:
with Image(filename=img_path) as inimg:
# create temp image with transparent background to composite
with Image(width=inimg.width, height=inimg.height, background=None) as new_img:
new_img.composite(inimg, 0, 0)
new_gif.sequence.append(new_img)
new_gif.save(filename=output_path)
unfortunately the background is not "cleared" when the new image is appended. They'll have the last image there as well:
But how do I clear the background? I though I do exactly that by compositing into a new image upfront.. `:| HALP!!
I see there is a similar thing with commandline ImageMagick but wand doesn't have anything like that. So far I have to workaround with a fitting background color.
Without seeing the source images, I can assume the -set dispose background is what's needed. For wand, you'll need to call wand.api.library.MagickSetOption method.
from wand.image import Image
from wand.api import library
with Image() as new_gif:
# Tell new gif how to manage background
library.MagickSetOption(new_gif.wand, 'dispose', 'background')
for img_path in input_images:
library.MagickReadImage(new_gif.wand, img_path)
new_gif.save(filename=output_path)
Or alternatively...
You can extent wand to manage Background Dispose behavior. This approach would give you the benefit of alter/generate each frame programmatically. But the down side would include a lot more work with ctypes. For example.
import ctypes
from wand.image import Image
from wand.api import library
# Tell python about library method
library.MagickSetImageDispose.argtypes = [ctypes.c_void_p, # Wand
ctypes.c_int] # DisposeType
# Define enum DisposeType
BackgroundDispose = ctypes.c_int(2)
with Image() as new_gif:
for img_path in input_images:
with Image(filename=img_path) as inimg:
# create temp image with transparent background to composite
with Image(width=inimg.width, height=inimg.height, background=None) as new_img:
new_img.composite(inimg, 0, 0)
library.MagickSetImageDispose(new_img.wand, BackgroundDispose)
new_gif.sequence.append(new_img)
# Also rebuild loop and delay as ``new_gif`` never had this defined.
new_gif.save(filename=output_path)
<- still needs delay correction
I have one generic icon image, which has an alpha. Lets say a black sphere placed on an square button, with transparancy.
Now I would like to change the color of the icon on the fly, without having several image of sphere_black.png, sphere_red.png etc etc.
Is there a way to colorize the pixmap, respecting the alpha and change HSV on that pixel, for all in the map?
I have something like this, but stuck:
img = QtGui.QImage(kwargs['icon_path']
pxmap = QtGui.QPixmap(img)
for x in range(img.width()):
for y in range(img.height()):
print img.pixel(1, 1), '###'
# ???? #
Any help is appreciated!
QGraphicsColorizeEffect might be what you are looking for. Sadly the QGraphicsEffect class is made to be used with the graphics view framework, it can't easily be applied to a QImage. However there are workarounds for that, as this discussion shows.
The implementation of the effect in QPixmapColorizeFilter::draw() shows how the colourization is done: A coloured rect (with the color having the alpha set to something else than fully opaque) is drawn over the image with QPainter::fillRect(), with an appropriate composition mode set.
I've got a small PyGI project which uses a Cairo image surface, which I then scale with a surface pattern and render on a Gtk.DrawingArea.
I'd like to write the scaled version to a PNG file. I've tried to write from the original surface with Surface.write_to_png(), but it only writes in the original (i.e. non-scaled) size, so I'm stuck in there.
Then I thought I could perhaps fetch the rendered image from the Gtk.DrawingArea and write that to disk, but I haven't found out how to do that in PyGI (this seems to be only possible in GTK+ 2 - save gtk.DrawingArea to file). So I'm trying to figure out how I can write my scaled image to disk.
Here's the code that creates the surface, scales it up and renders it:
def on_drawingarea1_draw (self, widget, ctx, data=None):
# 'widget' is a Gtk.DrawingArea
# 'ctx' is the Cairo context
text = self.ui.entry1.get_text()
if text == '':
return
# Get the data and encode it into the image
version, size, im = qrencode.encode(text)
im = im.convert('RGBA') # Cairo expects RGB
# Create a pixel array from the PIL image
bytearr = array.array('B', im.tostring())
height, width = im.size
# Convert the PIL image to a Cairo surface
self.surface = cairo.ImageSurface.create_for_data(bytearr,
cairo.FORMAT_ARGB32,
width, height,
width * 4)
# Scale the image
imgpat = cairo.SurfacePattern(self.surface)
scaler = cairo.Matrix()
scaler.scale(1.0/self.scale_factor, 1.0/self.scale_factor)
imgpat.set_matrix(scaler)
ctx.set_source(imgpat)
# Render the image
ctx.paint()
And here's the code to write the surface to a PNG file:
def on_toolbuttonSave_clicked(self, widget, data=None):
if not self.surface:
return
# The following two lines did not seem to work
# ctx = cairo.Context(self.surface)
# ctx.scale(self.scale_factor, self.scale_factor)
self.surface.write_to_png('/tmp/test.png')
So writing the surface creates an non-scaled image, and there is no write method in the cairo.SurfacePattern either.
My last resort is to fetch the scaled image as rendered in the gtk.DrawingArea, put it in a GtkPixbuf.Pixbuf or in a new surface, and then write that to disk. The pixbuf approach seemed to work in GTK+ 2, but not in GTK+ 3.
So does anyone know how I can write the scaled image to disk?
Ok, I found a way:
Remembering that Gtk.DrawingArea derives from Gtk.Window, I could use the Gdk.pixbuf_get_from_window() function to get the contents of the drawing area into a GdkPixbuf.Pixbuf and then use the GdkPixbuf.Pixbuf.savev() function to write the pixbuf as an image on disk.
def drawing_area_write(self):
# drawingarea1 is a Gtk.DrawingArea
window = self.ui.drawingarea1.get_window()
# Some code to get the coordinates for the image, which is centered in the
# in the drawing area. You can ignore it for the purpose of this example
src_x, src_y = self.get_centered_coordinates(self.ui.drawingarea1,
self.surface)
image_height = self.surface.get_height() * self.scale_factor
image_width = self.surface.get_width() * self.scale_factor
# Fetch what we rendered on the drawing area into a pixbuf
pixbuf = Gdk.pixbuf_get_from_window(window, src_x, src_y,
image_width, image_height)
# Write the pixbuf as a PNG image to disk
pixbuf.savev('/tmp/testimage.png', 'png', [], [])
While this works, it'd still be nice to see if someone could confirm this is the right way or to see if there is any other alternative.
I found another approach, using the Cairo context passed to the handler of draw events, but it resulted in capturing a region of the parent window that was larger than the DrawingArea.
What worked for me was to use the PixBuf as you have shown, but first calling the queue_draw() method for the DrawingArea, to force a full rendering, and waiting for the event to be processed (easy enough, I already had a draw handler). Otherwise, the resulting images can be partially undrawn.
In a PyQt based GUI program, I'm drawing a few PNG file as QPixmap after resize. So here is basically what happens:
bitmap = QPixmap( "foo.png" )
bitmap.scaleToHeight(38) # original is larger than this
scene.addItem(QGraphicsPixmapItem(bitmap)) # Add to graphics scene
The problem is: afterwards, the bitmap is rather ugly. Is there a way to do this in a antialiasing way?
See the documentation.
bitmap = bitmap.scaledToHeight(38, Qt.SmoothTransformation)