QBrush - scale a standard fill stipple pattern?

2019-07-14 22:24发布

问题:

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!

回答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.



回答2:

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!