Issue
I have a program that draws the contents of a QGraphicsScene
to a PNG image. I have found that in some circumstances the PNG image is not being created correctly. The image generated appears to be 'offset' by some amount, in that the top-left corner of the QGraphicsScene
isn't at (0,0) in the PNG image.
The code below demonstrates the problem. The createImage
function creates a QGraphicsScene
of a given width and height, draws at coordinates (0, 0) a solid red rectangle of the same width and height as the scene, renders this scene to an image and writes the image out to a file. I believe that this function should always write out an image that is solid red, regardless of the width and height it is given. However, here is one example output image which demonstrates that this is not the case.
The testImage
function (which relies on PIL) reads in the image and inspects the rightmost column and bottom row. If the image has been incorrectly 'offset', it will find a white pixel somewhere within the bottom row or rightmost column. If the image has been generated correctly, the bottom row and rightmost column will contain only red pixels. (PIL isn't part of the problem here; I'm just using it to automate the testing of the output images.)
#!/usr/bin/env python
import sys
import Image # Requires PIL
from PyQt4.QtCore import Qt, QRect, QRectF
from PyQt4.QtGui import QPen, QBrush, QGraphicsScene, QGraphicsView, QGraphicsRectItem, QPixmap, QPainter, QApplication
whitebrush = QBrush(Qt.white)
redbrush = QBrush(Qt.red)
RED = (255, 0, 0)
def createImage(width, height):
scene = QGraphicsScene(0, 0, width, height)
scene.setBackgroundBrush(whitebrush)
rectangle = QGraphicsRectItem(0, 0, width, height)
rectangle.setPen(QPen(Qt.NoPen))
rectangle.setBrush(redbrush)
rectangle.show()
scene.addItem(rectangle)
view = QGraphicsView()
view.setScene(scene)
outputimg = QPixmap(width, height)
painter = QPainter(outputimg)
targetrect = QRectF(0, 0, width, height)
sourcerect = QRect(0, 0, width, height)
view.render(painter, targetrect, sourcerect)
outputimg.save("output.png", "PNG")
painter.end()
def testImage(width, height):
image = Image.open("output.png")
ok = True
for x in range(width):
if image.getpixel((x, height - 1)) == RED:
if x > 0:
print "First red pixel on bottom row is at x = %d" % x
ok = False
break
else:
print "No red pixels found on bottom row"
ok = False
for y in range(height):
if image.getpixel((width - 1, y)) == RED:
if y > 0:
print "First red pixel in rightmost column is at y = %d" % y
ok = False
break
else:
print "No red pixels found in rightmost column"
ok = False
if ok:
print "Image OK"
def doTest(width, height):
createImage(width, height)
testImage(width, height)
if __name__ == "__main__":
app = QApplication(sys.argv)
doTest(int(sys.argv[1]), int(sys.argv[2]))
Here are a few example runs:
$ ./qgraphicssceneimagetest.py 200 200 No red pixels found on bottom row No red pixels found in rightmost column
In this case the effect of the 'offsetting' is so drastic that the entire output image is white.
$ ./qgraphicssceneimagetest.py 400 400 First red pixel on bottom row is at x = 117 First red pixel in rightmost column is at y = 37
This is the case that generated the sample image I linked to above.
$ ./qgraphicssceneimagetest.py 500 500 First red pixel on bottom row is at x = 55
At this point, the image is now correctly offset vertically, but the red rectangle is still being drawn 55 pixels too far to the right.
$ ./qgraphicssceneimagetest.py 600 600 First red pixel on bottom row is at x = 5
Nearly correct now...
$ ./qgraphicssceneimagetest.py 700 700 Image OK
So it seems that the code can generate an output image correctly if the image is large enough.
As it happens, I have a workaround for this problem. I can get the createImage
function to create the images correctly if I replace the line
sourcerect = QRect(0, 0, width, height)
with
xoff = 0
yoff = 0
if width <= 634 and height <= 474:
xoff = (634 - width) // 2
yoff = (474 - height) // 2
elif width < 610:
xoff = (610 - width) // 2
elif height < 450:
yoff = (450 - height) // 2
sourcerect = QRect(xoff, yoff, width, height)
I do not know what the significance of the 'magic' numbers in this code is. However, I have run numerous tests with this workaround in place and have verified that they all generated a correct solid red image.
Having this workaround is all right, but I'd like to understand why these images aren't being created correctly. Is there something I've missed or forgotten to do?
Solution
What is happening, is that the view is automatically calculating a rect for the scene and then centring itself on the graphics items that have been added. Depending on the dimensions of the items, this may sometimes leave blank areas around the edges.
The solution is to explicitly set a rect for the scene:
view = QGraphicsView()
view.setScene(scene)
view.setSceneRect(QRectF(view.viewport().rect()))
Answered By - ekhumoro
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.