Python - custom shaped user input area

So I had an idea on the back on my head that I have not made because I don’t know enough of PyQt5 to make yet. But I was talking about it to someone about the idea and I suddenly thought someone here could know, how to do it.

The thing is that I want to create a graphical interface with polygon shaped surface.
The triangle is the most easy shape that is used around.

I know how to paint a square, circle or a polygon with paint events.
But how do you do it for a selectable area so it disregards user inputs from outside the area space you painted when you click it?

info on a circle, square and or a polygon would be deeply appreciated.
I have been looking for this info for a long while now.

2 Likes

QWidgets support masks which can be defined by QRegions:
https://doc.qt.io/qt-5/qwidget.html#setMask-1
Note that it also restricts rendering to that masked area.

1 Like

For a rectangle you need to know at least the top left corner and the width and height, or the center point and the distance from it to the left or right side (width / 2) and the distance from the center to the top or bottom side (height / 2). So the algorithm is (pseudo code):

1) point_inside = test_point.x >= rect.left AND test_point.x <= rect.left + rect.width AND test_point.y >= rect.top AND test_point.x <= rect.top + rect.height
2) point_inside = test_point.x >= rect.center.x - rect.half_width AND test_point.x <= rect.center.x + rect.half_width AND test_point.center.y >= rect.center.y - rect.half_height AND test_point.x <= rect.center.y + rect.half_height

You may have to use < sign instead of <= depending on the type of the coordinates (integer, float)

For a circle you need to know the center and the radius. The algorithm is:

delta.x = test_point.x - center.x
delta.y = test_point.y - center.y
length_squared = delta.x * delta.x + delta.y * delta.y
radius_squared = radius * radius
point_inside = length_squared <= radius_squared

NOTE: this code uses squared lengths to avoid a square root call. The code with actual lengths is:
length = sqrt(delta.x * delta.x + delta.y * delta.y)
point_inside = length <= radius

So if you want to know if the test point is inside the hue circle in your image, you need to do the following:

point_in_hue_circle = point_is_inside_outter_circle AND point_is_outside_inner_circle
BONUS hue angle (between -pi and pi):
delta.x = test_point.x - center.x
delta.y = test_point.y - center.y
hue_angle = atan2(delta.y, delta.x)

For a triangle or a convex polygon the algorithm is more complex, and for a general polygon even more. You can search for “point in convex polygon test” or “point in polygon test” and you will find a lot of resources on the subject.

1 Like

@Lynx3d
QRegions then. I will sure read about that then. I have been searching for the wrong thing, I was looking for Masks before. Blocking the rendering is only a good thing for me actually.

@Deif_Lou
I see. that is quite interesting acctually. I can’t use that just yet though. I will still need to brush up my algrebra to see if I can get my result before I can do that check on the viewer logic, but I will keep it noted.

I still don’t even know if my idea works even, but i hope it does. I made a 3d model and it looks like it should work.

Well I just finished the region polygon and the initial set up for display purposes.
This is just a paint event with a painter.drawPolygon()

I noticed something curious. I have a SVG cursor on the panel with a alpha on its background and it reacts a bit strange over the paintEvent.


is there a way to go around this?
Previousley I have used StyleSeets and there was no issues but I am not sure if I will be able to keep using them with QRegions if I need to use paint events instead.

Can you use Qregions on Styles sheets?

I have been searching for QRegion information. however but for what I read it is a used with other commands to be workable? Up until now I am not able to make it work.

if you place this snippet on a stylesheet of a widget, should this not give a red polygon?

background: red;
width: 100px;
height: 100px;
clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 025%);

at least it is what is in the tutorial but I cant get it to work for some reason.
I wonder if Qt’s CSS has some limitations or some different syntax?

Which tutorial are you following? And yes, Qt’s CSS support is quite limited. See https://doc.qt.io/Qt-5/stylesheet-syntax.html

@boud
the one I was following is from this page, the portion I pasted is from the " Clipping paths in a nutshell" section:


I will read that page again more carefully to see what I can find.

however I think I found a behaviour today that might suit my needs.
Placing a setMask might not affect the background color of the widget it is set on but it affects the background color of children widgets under it. I think I will explore that for now and see how that goes.

hexagon = QPolygon([
    QPoint(50, 0),
    QPoint(100, 25),
    QPoint(100, 75),
    QPoint(50, 100),
    QPoint(0, 75),
    QPoint(0, 25)
    ])

limits = QRegion(hexagon)
self.layout.widget.setMask(limits)

there is a widget fully colored as a child of “widget” that gets masked out. I guess that is kinda the same effect as doing the weird clip command.

https://doc.qt.io/qt-5/stylesheet-reference.html