Основная проблема: метод QGraphicsView.mapToScene
возвращает разные ответы в зависимости от того, отображается ли графический интерфейс. Почему, и могу ли я обойти это?
В контексте я пытаюсь написать модульные тесты, но я не хочу показывать инструменты для тестов.
Маленький пример ниже иллюстрирует поведение. Я использую подклассифицированное представление, которое печатает позиции события щелчка мыши в координатах сцены с началом в левом нижнем углу (он имеет шкалу -1 по вертикали), вызывая mapToScene
. Тем не менее, mapToScene
не возвращает то, что я ожидаю, прежде чем будет показано диалоговое окно. Если я запустил основной раздел внизу, я получаю следующий вывод:
Size is (150, 200)
Putting in (50, 125) - This point should return (50.0, 75.0)
Before show(): PyQt5.QtCore.QPointF(84.0, -20.0)
After show() : PyQt5.QtCore.QPointF(50.0, 75.0)
До show()
существует согласованное смещение 34 пикселей в x и 105 в y (и в y смещение перемещается в обратном порядке, как если бы масштаб не применялся). Эти смещения кажутся довольно случайными, я понятия не имею, откуда они.
Вот пример кода:
import numpy as np
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QPointF, QPoint
from PyQt5.QtWidgets import (QDialog, QGraphicsView, QGraphicsScene,
QVBoxLayout, QPushButton, QApplication,
QSizePolicy)
from PyQt5.QtGui import QPixmap, QImage
class MyView(QGraphicsView):
"""View subclass that emits mouse events in the scene coordinates."""
mousedown = pyqtSignal(QPointF)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setSizePolicy(QSizePolicy.Fixed,
QSizePolicy.Fixed)
# This is the key thing I need
self.scale(1, -1)
def mousePressEvent(self, event):
return self.mousedown.emit(self.mapToScene(event.pos()))
class SimplePicker(QDialog):
def __init__(self, data, parent=None):
super().__init__(parent=parent)
# Get a grayscale image
bdata = ((data - data.min()) / (data.max() - data.min()) * 255).astype(np.uint8)
wid, hgt = bdata.shape
img = QImage(bdata.T.copy(), wid, hgt, wid,
QImage.Format_Indexed8)
# Construct a scene with pixmap
self.scene = QGraphicsScene(0, 0, wid, hgt, self)
self.scene.setSceneRect(0, 0, wid, hgt)
self.px = self.scene.addPixmap(QPixmap.fromImage(img))
# Construct the view and connect mouse clicks
self.view = MyView(self.scene, self)
self.view.mousedown.connect(self.mouse_click)
# End button
self.doneb = QPushButton('Done', self)
self.doneb.clicked.connect(self.accept)
# Layout
layout = QVBoxLayout(self)
layout.addWidget(self.view)
layout.addWidget(self.doneb)
@pyqtSlot(QPointF)
def mouse_click(self, xy):
print((xy.x(), xy.y()))
if __name__ == "__main__":
# Fake data
x, y = np.mgrid[0:4*np.pi:150j, 0:4*np.pi:200j]
z = np.sin(x) * np.sin(y)
qapp = QApplication.instance()
if qapp is None:
qapp = QApplication(['python'])
pick = SimplePicker(z)
print("Size is (150, 200)")
print("Putting in (50, 125) - This point should return (50.0, 75.0)")
p0 = QPoint(50, 125)
print("Before show():", pick.view.mapToScene(p0))
pick.show()
print("After show() :", pick.view.mapToScene(p0))
qapp.exec_()
Этот пример находится в PyQt5 в Windows, но PyQt4 в Linux делает то же самое.