In meinem Programm versuche ich, die Koordinaten einer Mausdruck zurück auf die Koordinaten eines Bildes abzubilden. Ich benutze PyQt4 in Python. Das folgende Programm zeigt das Problem. Ich habe ein Widget, das ein paar Bildtransformationen macht. Nach diesen Bildtransformationen wird das Bild in der Mitte des Widgets angezeigt, während das ursprüngliche Seitenverhältnis des Bildes beibehalten wird. Da das Bild skaliert und übersetzt wird, muss eine Koordinate eines MouseEvent dem Koordinatensystem des Bildes zugeordnet werden.PyQt mapping die Koordinaten einer Maus drücken Sie auf das Koordinatensystem eines Bildes
Das folgende Programm hat eine Klasse "ScalingWidget", die in der Lage sein sollte, diese Transformationen durchzuführen und die Koordinate in einem MouseReleaseEvent wieder in das Koordinatensystem des Bildes neu zuordnen sollte. Dies funktioniert perfekt wie erwartet, wenn ich das Widget außerhalb eines Layouts und Hauptfensters zeige, aber es wird böse, wenn ich das Widget in eine größere GUI einbette. Dann zeigen die Koordinaten nach dem Zuordnen zu den Bildkoordinaten plötzlich einen Offset an.
Das unten stehende Minimalprogramm kann mit und ohne den Fehler gestartet werden, indem das Flag -b beim Start des Programms angegeben wird. Die Option -n können in einem „gui“, legen Sie die Instanz von ScalingWidget tief und tiefer und je tiefer sie in den Layouts desto starker der Fehler sichtbar eingebettet sein wird. Die blöde Sache ist, obwohl die Zeichnung anzeigt, dass die Transformationen korrekt sind, zeigen die gemappten Koordinaten (gedruckt in dem Fenstertitel und der Konsole) an, dass eine Neuabbildung an die Bildkoordinaten vermasselt wird, wenn das Flag -b vorhanden ist.
Also meine Frage ist: Was soll ich mit Remapping die Maus falsch zu machen Koordinaten zurück auf die Bildabmessungen, wenn mein ScalingWidget in einem Layout eingebettet ist?
Ich erwarte nicht, die Neuzuordnung Pixel, perfekt zu sein, aber nur so genau wie der End-Benutzer mit der Maus positionieren kann. Es gibt zwei Punkte x = 20, y = 20 und bei x = 380 und y = 380 können diese als Bezugspunkte verwendet werden.
Jede Hilfe ist herzlich willkommen!
#!/usr/bin/env python
from PyQt4 import QtGui
from PyQt4 import QtCore
import sys
import argparse
class ScalingWidget (QtGui.QWidget):
''' Displays a pixmap optimally in the center of the widget, in such way
the pixmap is shown in the middle
'''
white = QtGui.QColor(255,255,255)
black = QtGui.QColor( 0, 0, 0)
arcrect = QtCore.QRect(-10, -10, 20, 20)
def __init__(self):
super(ScalingWidget, self).__init__()
self.pixmap = QtGui.QPixmap(400, 400)
painter = QtGui.QPainter(self.pixmap)
painter.fillRect(self.pixmap.rect(), self.white)
self.point1 = QtCore.QPoint(20, 20)
self.point2 = QtCore.QPoint(380, 380)
painter.setPen(self.black)
painter.drawRect(QtCore.QRect(self.point1, self.point2))
painter.end()
self.matrix = None
def sizeHint(self):
return QtCore.QSize(500,400)
##
# Applies the default transformations
#
def _default_img_transform(self, painter):
#size of widget
winheight = float(self.height())
winwidth = float(self.width())
#size of pixmap
scrwidth = float(self.pixmap.width())
scrheight = float(self.pixmap.height())
assert(painter.transform().isIdentity())
if scrheight <= 0 or scrwidth <= 0:
raise RuntimeError(repr(self) + "Unable to determine Screensize")
widthr = winwidth/scrwidth
heightr = winheight/scrheight
if widthr > heightr:
translate = (winwidth - heightr * scrwidth) /2
painter.translate(translate, 0)
painter.scale(heightr, heightr)
else:
translate = (winheight - widthr * scrheight)/2
painter.translate(0, translate)
painter.scale(widthr, widthr)
# now store the matrix used to map the mouse coordinates back to the
# coordinates of the pixmap
self.matrix = painter.deviceTransform()
def paintEvent(self, e):
painter = QtGui.QPainter(self)
painter.setClipRegion(e.region())
# fill the background of the entire widget.
painter.fillRect(self.rect(), QtGui.QColor(0,0,0))
# transform to place the image nicely in the center of the widget.
self._default_img_transform(painter)
painter.drawPixmap(self.pixmap.rect(), self.pixmap, self.pixmap.rect())
pen = QtGui.QPen(QtGui.QColor(255,0,0))
# Just draw on the points used to make the black rectangle of the pix map
# drawing is not affected, be remapping those coordinates with the "same"
# matrix is.
pen.setWidth(4)
painter.setPen(pen)
painter.save()
painter.translate(self.point1)
painter.drawPoint(0,0)
painter.restore()
painter.save()
painter.translate(self.point2)
painter.drawPoint(0,0)
painter.restore()
painter.end()
def mouseReleaseEvent(self, event):
x, y = float(event.x()), float(event.y())
inverted, invsucces = self.matrix.inverted()
assert(invsucces)
xmapped, ymapped = inverted.map(x,y)
print x, y
print xmapped, ymapped
self.setWindowTitle("mouse x,y = {}, {}, mapped x, y = {},{} "
.format(x, y, xmapped, ymapped)
)
def start_bug():
''' Displays the mouse press mapping bug.
This is a bit contrived, but in the real world
a widget is embedded in deeper in a gui
than a single widget, besides the problem
grows with the depth of embedding.
'''
app = QtGui.QApplication(sys.argv)
win = QtGui.QWidget()
layout = QtGui.QVBoxLayout()
win.setLayout(layout)
widget = None
for i in range(0, args.increase_bug):
if i < args.increase_bug-1:
widget = QtGui.QWidget()
layout.addWidget(widget)
layout= QtGui.QVBoxLayout()
widget.setLayout(layout)
else:
layout.addWidget(ScalingWidget())
win.show()
sys.exit(app.exec_())
def start_no_bug():
''' Does not show the mapping bug, the mouse event.x() and .y() map nicely back to
the coordinate system of the pixmap
'''
app = QtGui.QApplication(sys.argv)
win = ScalingWidget()
win.show()
sys.exit(app.exec_())
# parsing arguments
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('-b', '--display-bug', action='store_true',
help="Toggle this option to get the bugged version"
)
parser.add_argument('-n', '--increase-bug', type=int, default=1,
help="Increase the bug by n times."
)
if __name__ == "__main__":
args = parser.parse_args()
if args.display_bug:
start_bug()
else:
start_no_bug()