2

In PyQt, I have a QGraphicsScene to which I add a rectangle with one of the standard QBrush fill patterns, in this case Qt.Dense7Pattern. This shows up as expected when the scene is displayed at original 1:1 scale: the "dots" that make up the Dense7Pattern are some number of pixels apart from each other, let's say 5 pixels apart just to pick a number. Also, let's say the original rectangle is 50 pixels wide and 50 pixels tall.

When I zoom in on that scene, let's say by a factor of 2, making the filled rectangle now appear 100x100 pixels, I would like the dots of the fill pattern to still appear 5 pixels apart, but, Qt zooms in on the static fill pattern too, making the dots of the fill pattern appear 10 pixels apart.

This question looks similar. Apparently you can apply a transform (including scale) to a brush's pixmap fill pattern, but, it doesn't seem to apply to the brush's 'standard' fill pattern. I did the subclass and changed the brush's transform in the paint method, but no luck:

The entire subclass (hopefully I did everything that's necessary):

class customFillPolygonItem(QGraphicsPolygonItem):
    def paint(self,painter,option,widget=None):
        # from here, painter.setBrush affects shapes drawn directly in this function;
        #  self.setBrush affects the call to super().paint
        newBrush=QBrush(painter.brush())
        newBrush.setTransform(QTransform(painter.worldTransform().inverted()[0]))
        self.setBrush(newBrush)
        painter.setBrush(newBrush)
        painter.drawRect(QRectF(0,0,50,50)) # draw a reality-check object
        super(customFillPolygonItem,self).paint(painter,option,widget)

Then I just make an instance of it from above using

myItem=customFillPolygonItem(QPolygonF(...
myItem.setBrush(QBrush(Qt.Dense7Pattern))
myScene.addItem(myItem)

So - is it possible to apply the scale to the a brush's standard fill pattern? This would be similar to QBrush.setFlag(ItemIgnoresTransformations) but such a thing does not exist... you can only set that flag on the entire item (rectangle in this case).

Workarounds might include: - use ItemIgnoresTransformations and manually transform the actual rectangle vertices as needed - make a qpixmap out of each standard fill pattern, then use that pixmap as the brush in which case it should scale as in the question mentioned above

But of course it would be nice to find the simplest solution.

UPDATE: Schollii answers the question that was asked; his suggestion to look in to alpha values also led to the solution to the larger issue I was having in this particular case - to create different grayscales, spelled out in the solution I posted, which is not actually a solution to the question I asked. Anyway, thanks!

Community
  • 1
  • 1
Tom Grundy
  • 736
  • 5
  • 26

2 Answers2

1

I believe the goal is not reachable via a transform: the fill pattern is generated in "scene" space then a transform is applied to it based on view scale factor, clearly this is going to increase the spacing between points, no transformation can prevent that, only a different drawing of the pattern.

To get effect you're after, the brush would have to have a parameter to control the distance between points in scene coords; then in the paint() method of the rect you could set this parameter value based on current view zoom factor (via the style argument to paint() method).

Since I don't think the QBrush provides such parameter, you have to draw the dots yourself, using distance computed as described in previous paragraph. Done from Python, this might be quite slow if there are Los of dots, but then again PyQt5 has quite impressive performance in general.

Not that you will have to pick an origin for the points (a corner or the center would be typical choices), and as you zoom in you will see the points "slide" towards the origin, as more points are "added" to the opposite ends/edges. I think there is a good chance you won't like the effect and since it is not so easy to implement, you might want to consider a different visual effect that would not be based on points, like a gradient or an alpha value. There might be other approaches, like setDashPattern() or drawing a rest in view coordinates but probably not worth the effort.

Oliver
  • 27,510
  • 9
  • 72
  • 103
  • Well, it's good to hear a second opinion that it may not be possible. I had not thought of just using an alpha value, that may be the perfect solution. The overall goal of the display is to show different polygons at different levels of gray - the standard brush patterns had worked at 'normal' scales, so I had not even thought of alpha values yet. Thanks - I will post back here if I find a good result. – Tom Grundy Jan 09 '17 at 02:32
  • Yes, alpha was the best solution. Thank you for bringing up that idea. It always seems obvious in retrospect! I posted a separate answer just to narrow in on the exact solution (and will accept it when allowed to do so). – Tom Grundy Jan 09 '17 at 03:25
  • That's great to hear. Although, what I wrote got you to look at the problem from a different angle, and you ended up with a different question (to which you knew the answer). But I respect your choice, the most important is you didn't go down that rabbit hole :) – Oliver Jan 09 '17 at 06:18
  • good point, yours is an answer to the question that was asked. – Tom Grundy Jan 09 '17 at 16:30
1

Thanks Schollii for the lead: setting the alpha value provides a quick solution. I went with black, with varying alpha values to represent the various gray scales.

If you have n different items to all be shaded differently, you can just walk through the list of items:

myItem.setBrush(QBrush(QColor(0,0,0,35*n)))

Where 35 is chosen based on what looks good, so that you can get the desired amount of separation of gray levels from one item to the next.

This is massively less convoluted than subclassing and transforming!

Tom Grundy
  • 736
  • 5
  • 26