Почему изображения в формате PNG, отображаемые из QGraphicsScene, неправильно смещены? - PullRequest
2 голосов
/ 04 декабря 2011

У меня есть программа, которая рисует содержимое QGraphicsScene в изображение PNG. Я обнаружил, что в некоторых случаях изображение PNG создается неправильно. Сгенерированное изображение, по-видимому, «смещено» на некоторую величину, так как верхний левый угол QGraphicsScene не равен (0,0) на изображении PNG.

Код ниже демонстрирует проблему. Функция createImage создает QGraphicsScene заданной ширины и высоты, рисует в координатах (0, 0) сплошной красный прямоугольник той же ширины и высоты, что и сцена, отображает эту сцену в изображение и записывает изображение в файл. Я полагаю, что эта функция должна всегда записывать изображение, которое является сплошным красным, независимо от ширины и высоты, которое ему дано. Однако здесь является одним из примеров выходного изображения, которое демонстрирует, что это не так.

Функция testImage (которая опирается на PIL ) считывает изображение и проверяет крайний правый столбец и нижний ряд. Если изображение было неправильно «смещено», оно найдет белый пиксель где-то в нижней строке или крайнем правом столбце. Если изображение было сгенерировано правильно, нижний ряд и крайний правый столбец будут содержать только красные пиксели. (PIL здесь не является частью проблемы; я просто использую его для автоматизации тестирования выходных изображений.)

#!/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]))

Вот несколько примеров выполнения:

$ ./qgraphicssceneimagetest.py 200 200
No red pixels found on bottom row
No red pixels found in rightmost column

В этом случае эффект «смещения» настолько радикальный, что все выходное изображение становится белым.

$ ./qgraphicssceneimagetest.py 400 400
First red pixel on bottom row is at x = 117
First red pixel in rightmost column is at y = 37

Это тот случай, когда я создал образец изображения, на которое я ссылался выше.

$ ./qgraphicssceneimagetest.py 500 500
First red pixel on bottom row is at x = 55

В этот момент изображение теперь корректно смещено по вертикали, но красный прямоугольник все еще отрисовывается на 55 пикселей слишком далеко вправо.

$ ./qgraphicssceneimagetest.py 600 600
First red pixel on bottom row is at x = 5

Почти правильно сейчас ...

$ ./qgraphicssceneimagetest.py 700 700
Image OK

Так что кажется, что код может правильно генерировать выходное изображение, если изображение достаточно большое.

Как это происходит, у меня есть обходной путь для этой проблемы. Я могу получить функцию createImage для правильного создания изображений, если я заменю строку

    sourcerect = QRect(0, 0, width, height)

с

    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)

Я не знаю, каково значение «магических» чисел в этом коде. Однако я выполнил многочисленные тесты с этим обходным путем и убедился, что все они генерировали правильное сплошное красное изображение.

С этим обходным путем все в порядке, но я бы хотел понять, почему эти изображения не создаются правильно. Есть что-то, что я пропустил или забыл сделать?

1 Ответ

2 голосов
/ 04 декабря 2011

Что происходит, так это то, что представление автоматически вычисляет прямоугольник для сцены, а затем центрируется на добавленных графических элементах. В зависимости от размеров элементов это может иногда оставлять пустые области по краям.

Решение состоит в том, чтобы явно установить прямоугольник для сцены:

view = QGraphicsView()
view.setScene(scene)
view.setSceneRect(QRectF(view.viewport().rect()))
...