Manipulate color of QImage - QT - python

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.

Related

How to remove or replace the PyVista window icon?

How can I remove or change the PyVista render window's icon? I have tried to search the issue also from the docs but didn't find any answers.
This is not currently supported directly in PyVista, but this is a great idea and I'll open a pull request to implement this once a major refactor of render windows is done.
In the meantime you can use raw VTK, the SetIcon() method on render windows. According to the docs this only works on Windows and Linux though.
As of PyVista 0.36.1 you have direct access to plotter.ren_win which is a VTK render window object. According to the docs the icon should be a vtkImageData; in practical PyVista terms this means UniformGrids with dimensions (n, m, 1).
Some experimentation suggests that the icon has to have uint8 active scalars of shape (n_points, 3) or (n_points, 4), but I could only get the icon to actually show up on my linux machine with the latter setup. It seems that non-square shaped icons get tiled to square shape, so you have to crop your image to square shape first. Finally, you need to call ren_win.Render() before setting the icon, otherwise problems arise (on my linux machine: a segmentation fault).
Here's a small example:
import numpy as np
import pyvista as pv
from pyvista import examples
# example icon: cropped puppy mesh turned from RGB to RGBA
icon = examples.download_puppy().extract_subset([0, 1199, 0, 1199, 0, 0])
data = np.empty((icon.n_points, 4), dtype=np.uint8)
data[:, :-1] = icon.point_data['JPEGImage']
data[:, -1] = 255 # pad with full opacity
icon.point_data['JPEGImage'] = data
# create a plotter with a dummy mesh and set its icon
plotter = pv.Plotter()
plotter.add_mesh(pv.Dodecahedron())
ren_win = plotter.ren_win # render window
ren_win.Render() # important against segfault
ren_win.SetIcon(icon)
plotter.show()
With this my bottom panel looks like this:
It also works for my window switcher:
(Interestingly, the window title in the title bar is "PyVista" which is the default title in pyvista.Plotter.__init__(), but in the window switcher I see "Vtk". I don't know why this is but I'll also try to see if we can fix this.)
Opacity handling seems to work too:
# add opacity in a nontrivial pattern
i, j = np.indices(icon.dimensions[:-1])
alpha = ((np.sin(2*i/icon.dimensions[0]*2*np.pi) * np.cos(j/icon.dimensions[1]*2*np.pi)) * 255).round().astype(np.uint8)
icon.point_data['JPEGImage'][:, -1] = alpha.ravel()
with this icon the window switcher looks like this:
It looks funky but that's just because the opacity pattern itself is funky. Transparency shows up as the switcher's semitransparent background colour on my system.

Incorrect hover area with QGraphicsPathItem and QPainterPathStroker

I am writing a class inheriting from QGraphicsItemGroup and its main child is a QGraphicsPathItem. The whole thing is used to draw polylines. I wanted to have a proper hover system for the lines, si I reimplemented the shape method of the object as follows:
def shape(self):
stroker = QtWidgets.QPainterPathStroker()
stroker.setWidth(10 * self.resolution) # resolution handles zoom and stuff
path = stroker.createStroke(self.__path.path()).simplified()
return path
In the above snippet, self.__path is the QGraphicsPathItem I mentioned ealier.
To make things simple, here are a few pictures. The line I drew, that I see on the screen:
The hover area I want:
The hover area I currently have with the reimplemented shape method shown above:
As you guessed, such a selection area is hardly useful for any purpose. Worst of all, I tried to use the exact same method to generate the outlines of the line, then used toFillPolygon to generate a polygon that I rendered in the same object by adding a QGraphicsPolygonItem child to my object: the shape that appears on my screen is exactly what I want, but when I use the same path to create the hover area via shape, it gives me the useless hover area (image 3) instead.
So, do you know why the path obtained with the QPainterPathStroker allows me to display a polygon that seems to exactly correspond to the hover area I want, but when I use that path in shape, the obtained over area is wonky? If so, do you know how to fix this problem?

how to change color of QGraphicsEllipseItem when it selected?

i use python 2.7 + pyqt4.8
i create many items like QGraphicsEllipseItem and QGraphicsRectItem in my scene. I made them all different colors. the allocation of the elements around them appears bounding rect, and I would like that they also changed the fill color to another color (white). When removing the selection to return to the original color.
how to change color of QGraphicsEllipseItem when it selected?
You should trigger your QGraphicsEllipseItem "clicked" signal with a handler method which we call it here ellipseClickHandler where you change the color of your ellipse item by calling its setBrush method like this for instance:
def ellipseClickHandler(self, ellipseItem)
ellipseItem.setBrush(QBrush(QtCore.Qt.red, style = QtCore.Qt.SolidPattern))

Convert picture coordinates to window coordinates

I am working on Ubuntu,using Python. I have (x,y) which are the image coordinates.I would like to convert them to screen coordinates.The image loads using Qt.Can anyone tell me how this could be done?
QWidgets have mapToGlobal() in C++. I haven't used it in python, but it should work.
So when using it, it will probably look something like this:
QWidget imgContainer
QPoint global_pt
# ... After the image is visible and loaded ...
# Map the top left corner of the image to global screen coordinates
global_pt = imgContainer.mapToGlobal(QPoint(0,0))
Hope that helps.

When saving turtle graphics to an .eps file, the background color shows on the screen but is not saved in the .eps file

I am new to Python and have been working with the turtle module as a way of learning the language.
Thanks to stackoverflow, I researched and learned how to copy the image into an encapsulated postscript file and it works great. There is one problem, however. The turtle module allows background color which shows on the screen but does not show in the .eps file. All other colors, i.e. pen color and turtle color, make it through but not the background color.
As a matter of interest, I do not believe the import of Tkinter is necessary since I do not believe I am using any of the Tkinter module here. I included it as a part of trying to diagnose the problem. I had also used bgcolor=Orange rather than the s.bgcolor="orange".
No Joy.
I am including a simple code example:
# Python 2.7.3 on a Mac
import turtle
from Tkinter import *
s=turtle.Screen()
s.bgcolor("orange")
bob = turtle.Turtle()
bob.circle(250)
ts=bob.getscreen()
ts.getcanvas().postscript(file = "turtle.eps")
I tried to post the images of the screen and the .eps file but stackoverflow will not allow me to do so as a new user. Some sort of spam prevention. Simple enough to visualize though, screen has background color of orange and the eps file is white.
I would appreciate any ideas.
Postscript was designed for making marks on some medium like paper or film, not raster graphics. As such it doesn't have a background color per se that can be set to given color because that would normally be the color of the paper or unexposed film being used.
In order to simulate this you need to draw a rectangle the size of the canvas and fill it with the color you want as the background. I didn't see anything in the turtle module to query the canvas object returned by getcanvas() and the only alternative I can think of is to read the turtle.cfg file if there is one, or just hardcode the default 300x400 size. You might be able to look at the source and figure out where the dimensions of the current canvas are stored and access them directly.
Update:
I was just playing around in the Python console with the turtle module and discovered that what the canvas getcanvas() returns has a private attribute called _canvas which is a <Tkinter.Canvas instance>. This object has winfo_width() and winfo_height() methods which seem to contain the dimensions of the current turtle graphics window. So I would try drawing a filled rectangle of that size and see if that gives you what you want.
Update 2:
Here's code showing how to do what I suggested. Note: The background must be drawn before any other graphics are because otherwise the solid filled background rectangle created will cover up everything else on the screen.
Also, the added draw_background() function makes an effort to save and later restore the graphics state to what it was. This may not be necessary depending on your exact usage case.
import turtle
def draw_background(a_turtle):
""" Draw a background rectangle. """
ts = a_turtle.getscreen()
canvas = ts.getcanvas()
height = ts.getcanvas()._canvas.winfo_height()
width = ts.getcanvas()._canvas.winfo_width()
turtleheading = a_turtle.heading()
turtlespeed = a_turtle.speed()
penposn = a_turtle.position()
penstate = a_turtle.pen()
a_turtle.penup()
a_turtle.speed(0) # fastest
a_turtle.goto(-width/2-2, -height/2+3)
a_turtle.fillcolor(turtle.Screen().bgcolor())
a_turtle.begin_fill()
a_turtle.setheading(0)
a_turtle.forward(width)
a_turtle.setheading(90)
a_turtle.forward(height)
a_turtle.setheading(180)
a_turtle.forward(width)
a_turtle.setheading(270)
a_turtle.forward(height)
a_turtle.end_fill()
a_turtle.penup()
a_turtle.setposition(*penposn)
a_turtle.pen(penstate)
a_turtle.setheading(turtleheading)
a_turtle.speed(turtlespeed)
s = turtle.Screen()
s.bgcolor("orange")
bob = turtle.Turtle()
draw_background(bob)
ts = bob.getscreen()
canvas = ts.getcanvas()
bob.circle(250)
canvas.postscript(file="turtle.eps")
s.exitonclick() # optional
And here's the actual output produced (rendered onscreen via Photoshop):
I haven't found a way to get the canvas background colour on the generated (Encapsulated) PostScript file (I suspect it isn't possible). You can however fill your circle with a colour, and then use Canvas.postscript(colormode='color') as suggested by #mgilson:
import turtle
bob = turtle.Turtle()
bob.fillcolor('orange')
bob.begin_fill()
bob.circle(250)
bob.begin_fill()
ts = bob.getscreen()
ts.getcanvas().postscript(file='turtle.eps', colormode='color')
Improving #martineau's code after a decade
import turtle as t
Screen=t.Screen()
Canvas=Screen.getcanvas()
Width, Height = Canvas.winfo_width(), Canvas.winfo_height()
HalfWidth, HalfHeight = Width//2, Height//2
Background = t.Turtle()
Background.ht()
Background.speed(0)
def BackgroundColour(Colour:str="white"):
Background.clear() # Prevents accumulation of layers
Background.penup()
Background.goto(-HalfWidth,-HalfHeight)
Background.color(Colour)
Background.begin_fill()
Background.goto(HalfWidth,-HalfHeight)
Background.goto(HalfWidth,HalfHeight)
Background.goto(-HalfWidth,HalfHeight)
Background.goto(-HalfWidth,-HalfHeight)
Background.end_fill()
Background.penup()
Background.home()
BackgroundColour("orange")
Bob=t.Turtle()
Bob.circle(250)
Canvas.postscript(file="turtle.eps")
This depends on what a person is trying to accomplish but generally, having the option to select which turtle to use to draw your background to me is unnecessary and can overcomplicate things so what one can do instead is have one specific turtle (which I named Background) to just update the background when desired.
Plus, rather than directing the turtle object via magnitude and direction with setheading() and forward(), its cleaner (and maybe faster) to simply give the direct coordinates of where the turtle should go.
Also for any newcomers: Keeping all of the constants like Canvas, Width, and Height outside the BackgroundColour() function speeds up your code since your computer doesn't have to recalculate or refetch any values every time the function is called.

Categories