Dans mon programme, j'essaie de mapper les coordonnées d'une souris aux coordonnées d'une image. J'utilise PyQt4 en Python. Le programme ci-dessous illustre le problème. J'ai un widget qui fait quelques transformations d'image. Après ces transformations d'image, l'image est affichée au centre du widget, tout en conservant le format d'origine de l'image. Puisque l'image est mise à l'échelle et traduite, une coordonnée d'un MouseEvent doit être remappée sur le système de coordonnées de l'image. Le programme ci-dessous a une classe "ScalingWidget", qui devrait être capable de faire ces transformations et devrait également être en mesure de remapper la coordonnée dans un mouseReleaseEvent de retour au système de coordonnées de l'image. Cela fonctionne parfaitement comme prévu quand je montre le widget en dehors d'une mise en page et d'une fenêtre principale, mais cela devient mal quand j'intègre le widget dans un plus grand GUI. Ensuite, les coordonnées après les avoir mises en correspondance avec les coordonnées de l'image affichent soudainement un décalage.PyQt mappant les coordonnées d'une presse de la souris au système de coordonnées d'une image
Le programme minimal ci-dessous peut être démarré avec ou sans le bogue en spécifiant le flag -b au démarrage du programme. L'option -n peut placer l'instance de ScalingWidget plus profondément et plus profondément dans un "gui", et plus elle sera intégrée dans les mises en page, plus le bogue sera visible. La chose stupide est, bien que le dessin indique que les transformations sont correctes, les coordonnées mappées (imprimées dans le titre de la fenêtre et la console) indiquent que les reconfigurer aux coordonnées de l'image est foutu lorsque le drapeau -b est présent. Donc, ma question est: Qu'est-ce que je fais de mal à remapper les coordonnées de la souris aux dimensions de l'image lorsque mon ScalingWidget est intégré dans une mise en page?
Je ne m'attends pas à ce que le remappage soit parfait au pixel près, mais tout aussi précis que l'utilisateur final peut positionner la souris. Il y a deux points x = 20, y = 20 et à x = 380 et y = 380 ceux-ci peuvent être utilisés comme point de référence.
Toute aide est la bienvenue!
#!/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()