2016-04-25 38 views
1

私のプログラムでは、マウスの座標を画像の座標の次元に戻してマップしようとしています。私はPythonでPyQt4を使用しています。以下のプログラムは、この問題を示しています。私はいくつかの画像変換を行うウィジェットを持っています。これらの画像変換の後、イメージはウィジェットの中央に表示され、イメージの元のアスペクト比は維持されます。画像は拡大縮小されて平行移動されるため、MouseEventの座標はImageの座標系に再マッピングする必要があります。マウスの座標を画像の座標系にマッピングするPyQt

以下のプログラムは、これらの変換を行うことができるはずのクラス "ScalingWidget"を持ち、mouseReleaseEvent内の座標を画像の座標系に再マップすることもできるはずです。これは、レイアウトとメインウィンドウの外側にウィジェットを表示すると、期待通りに動作しますが、ウィジェットをより大きなGUIに埋め込むと悪くなります。その後、座標をImage座標にマッピングした後の座標は、オフセットを突然表示します。

以下の最小限のプログラムは、プログラムの起動時にフラグ-bを指定することで、バグの有無にかかわらず開始することができます。 -nオプションを指定すると、ScalingWidgetのインスタンスを "gui"の中に深く深く置くことができます。レイアウトに深く埋め込まれるほど、バグが強く表示されます。 愚かなことは、描画が正しいことを示していますが、マップされた座標(ウィンドウのタイトルとコンソールに表示されます)は、-bフラグが存在するときに、それらを画像座標に再マッピングし直すことを示しています。

私の質問は、ScalingWidgetがレイアウトに埋め込まれているときに、マウスの座標をイメージの次元に再マッピングすると間違っていますか?

私は再マッピングがピクセル完全であるとは思っていませんが、エンドユーザーがマウスを置くことができるほど正確です。 x = 20、y = 20の2つの点が存在し、x = 380およびy = 380にこれらを基準点として使用することができます。

どのようなヘルプも大歓迎です!

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

答えて

0

_default_image_transformの基本的な考え方は正しいです。エラーは関数の最後にあります。

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() ## <-- error is here 

機能_default_image_transformの最後の行は次のようになります。

self.matrix = painter.transform() 

あなたは、プラットフォームに依存ハンドルですQT :: HANDLEで作業しているとき、1だけQPainter.deviceTransform()を呼び出す必要がありますドキュメントによります。私はプラットフォームに依存するハンドルで作業していなかったので、呼び出さないでください。ウィジェットを表示すると機能しますが、ウィジェットがレイアウトに埋め込まれているときは表示されません。次に、deviceTransform行列は、通常のQPainter.transform()行列とは異なります。他を参照してくださいhttp://doc.qt.io/qt-4.8/qpainter.html#deviceTransform

関連する問題