QGraphicsView Translation

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Guten Tag Python Community,

ich hab mich ein wenig mit QGraphicsView beschäftigt und wollte nun einige Fragen los werden.

Ich hab einen Satz Lininen, die in einem self.scene = QGraphicsScene(self)
Objekt gespeichert sind.

Nun habe ich ein code snippet geschrieben, das es ermöglicht die via MouseEvents das QGraphicsView zu translatieren. :) Auf Englisch: Pan
Auf Deutsch: Man kann sich im Fenster hin und her bewegen.

Das Problem ist nur, dass ich beim Skalieren und Rotieren keine Probleme bekomme. Beim Translatieren (Bewegen) scheint es so als ob sich nichts ändern würde.

Ein Blick in die Doku:
QGraphicsView keeps the center of the view fixed during a transformation.
Und das kann ich nun wirklich nicht gebrauchen.

Eine Workearound ist jede Linie einzeln zu translatieren! Diese sind in meinem Fall in einer Liste self.objects gespeichert. Das funktioniert auch, ist aber sehr langsam!

Code: Alles auswählen

            for x in self.objects:
                x.translate(newpoint.x(),newpoint.y())

Nun, gibt es eine Möglichkeit den QGraphicsView zu bewegen, dass sich der Inhalt auch bewegt?

Wäre nett wenn mir jemand helfen könnte!
Danke!
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Um die Darstellung der Verschiebung zu beschleunigen, könntest Du den Zentrierpunkt verschieben mit centerOn(). Brauchst Du allerdings die geänderten Koordinaten für weitere Berechnungen, muß Du das anders lösen, da die hierbei gleich bleiben, da der Viewport "nur" gescrollt wird.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Geht irgendwie nicht,
hab ein bisschen gegooglet. Das Problem haben scheinbar andere Leute auch, leider postet keiner eine Lösung für das Problem.

http://www.nabble.com/Various-problems- ... 27902.html

Bin verzweifelt.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Kannst Du vllt. ein wenig mehr Code zu Verfügung stellen? So könnte man das Problem eher nachvollziehen.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

natürlich:

Code: Alles auswählen

from __future__ import division
import functools
import random
import sys
import wave, struct, numpy
from PyQt4.QtCore import *
from PyQt4.QtGui import *

class GraphicsView(QGraphicsView):
    
    def __init__(self,parent=None):
 
        super(GraphicsView, self).__init__(parent)
        self.setDragMode(QGraphicsView.RubberBandDrag)
        #self.setDragMode(QGraphicsView.ScrollHandDrag)
        self.point = QPoint()
        self.setRenderHint(QPainter.Antialiasing)
        self.setRenderHint(QPainter.TextAntialiasing)
    
    def wheelEvent(self, event):
        factor = 1.41 ** (-event.delta() / 240.0)
        self.scale(factor, factor)  

    def mousePressEvent(self,event):
        if event.button() == 4:
            self.setDragMode(QGraphicsView.ScrollHandDrag)
            x = event.pos().x()
            y = event.pos().y()
            self.point = QPoint(x,y)
            
    def mouseReleaseEvent(self,event):
        if event.button() == 4:
            self.setDragMode(QGraphicsView.RubberBandDrag)
            # Calc difference
            newpoint = self.point - event.pos()
            newpoint = newpoint * -20
            
            #for x in self.objects:
                #x.translate(newpoint.x(),newpoint.y())
                

            self.point = QPoint()

            



class MainForm(QDialog):

    def __init__(self, parent=None):
        
        super(MainForm, self).__init__(parent)
        
        self.wavefile = readwave("test.wav")
        self.scene = QGraphicsScene(self)
        self.view = GraphicsView(self)
        self.scene.setSceneRect(0, 0, 500, 500)
        #self.drawLine()
        self.drawWave()
        self.view.setScene(self.scene)

        buttonLayout = QVBoxLayout()
        buttonLayout.addStretch(1)
        button = QPushButton("Rotate")
        tbutton = QPushButton("Translate")
        self.connect(button, SIGNAL("clicked()"),  self.rotate)
        self.connect(tbutton, SIGNAL("clicked()"),  self.translate)
        buttonLayout.addWidget(button)
        buttonLayout.addWidget(tbutton)

        layout = QHBoxLayout()
        layout.addWidget(self.view, 1)
        layout.addLayout(buttonLayout)
        self.setLayout(layout)
        self.setWindowTitle("Simple Wave Viewer")

        

    def rotate(self):
            for x in self.view.objects:
                x.rotate(10)

    def translate(self):
            for x in self.view.objects:
                x.translate(0,2000)

    def drawWave(self):
        self.view.objects =[]
        array = self.wavefile
        pieces = 200
        dx = 500/2
        dy = -int(self.wavefile[0][0])
        
        for x in range(0,len(array),int(len(array)/pieces)):      
            self.view.objects.append(self.scene.addLine(dx, dy, dx+1, -int(array[x][0])))
            dx += int(float(len(array))/float(pieces))
            dy = int(array[x][0])
            
        
            
def readwave(wavfilename):
	w=wave.open(wavfilename,'rb')
	(nchannel, width, rate, length, comptype, compname) = w.getparams()
	frames = w.readframes(length)
	data = numpy.array(struct.unpack("%sh" %length*nchannel, frames)).reshape(length,nchannel)
	return data

app = QApplication(sys.argv)
form = MainForm()
form.show()
app.exec_()
Damit das ding funktioniert benötigt man eine wave Datei zum visualisieren (mit Namen test.wav)
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

In den von Dir geposteten Link findest Du die Antwort:
Du mußt das SceneRect entsprechend groß wählen und self.setTransformationAnchor(QGraphicsView.NoAnchor) setzen, so daß das translate() die Verschiebung vornehmen kann. Stören Dich die so entstehenden Scrollbars, kannst Du diese mit self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) ausschalten.

Um die Sache performant zu halten, ist es besser, die Modifikationen am GraphicsView vorzunehmen und nicht separat für alle Einzellinien:

Code: Alles auswählen

def rotate(self):
  #  for x in self.view.objects:
  #     x.rotate(10)
    self.view.rotate(10)
bzw.

Code: Alles auswählen

def mouseReleaseEvent(self,event):
....
  #  for x in self.objects: 
  #      x.translate(newpoint.x(),newpoint.y())
    self.translate(newpoint.x(), newpoint.y())
Mit mapToScene() und mapFromScene() lassen sich die transformierten Punkte dann wieder umrechnen.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Hallo!

Ja die Tipps die er vorgeschlagen hat, hab ich auch probiert umzusetzten!
Wäre nett wenn du oder jemand anderes mal einen Blick drüber werfen könnte! Habe die Sachen geändert die du geändert haben wolltest :P

Es funktioniert ja auch schon halbwegs, nur kann man das Fenster ab einem gewissen Punkt nicht mehr nach oben verschieben.

Ich werd noch ein bissl rumspielen. Hier der Code:

Code: Alles auswählen

from __future__ import division
import functools
import random
import sys
import wave, struct, numpy
from PyQt4.QtCore import *
from PyQt4.QtGui import *

class GraphicsView(QGraphicsView):
   
    def __init__(self,parent=None):
 
        super(GraphicsView, self).__init__(parent)
        self.setTransformationAnchor(QGraphicsView.NoAnchor)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) 
        self.point = QPoint()
        self.setRenderHint(QPainter.Antialiasing)
        self.setRenderHint(QPainter.TextAntialiasing)
   
    def wheelEvent(self, event):
        factor = 1.41 ** (-event.delta() / 240.0)
        self.scale(factor, factor) 

    def mousePressEvent(self,event):
        if event.button() == 4:
            x = event.pos().x()
            y = event.pos().y()
            self.point = self.mapToScene(QPoint(x,y))
           
    def mouseReleaseEvent(self,event):
        if event.button() == 4:
            # Calc difference
            newpoint = self.point - self.mapToScene(event.pos())        
            self.translate(newpoint.x(),newpoint.y())
            self.point = QPoint()
            
            
class MainForm(QDialog):

    def __init__(self, parent=None):
       
        super(MainForm, self).__init__(parent)
       
        self.wavefile = readwave("test2.wav")
        self.scene = QGraphicsScene(self)
        self.view = GraphicsView(self)
        self.scene.setSceneRect(0, 0, 90000, 90000)
        self.drawWave()
        self.view.setScene(self.scene)

        buttonLayout = QVBoxLayout()
        buttonLayout.addStretch(1)
        button = QPushButton("Rotate")
        tbutton = QPushButton("Translate")
        self.connect(button, SIGNAL("clicked()"),  self.rotate)
        self.connect(tbutton, SIGNAL("clicked()"),  self.translate)
        buttonLayout.addWidget(button)
        buttonLayout.addWidget(tbutton)

        layout = QHBoxLayout()
        layout.addWidget(self.view, 1)
        layout.addLayout(buttonLayout)
        self.setLayout(layout)
        self.setWindowTitle("Simple Wave Viewer")

       

    def rotate(self):
            for x in self.view.objects:
                x.rotate(10)

    def translate(self):
            for x in self.view.objects:
                x.translate(0,2000)

    def drawWave(self):
        self.view.objects =[]
        array = self.wavefile
        pieces = 200
        dx = 500/2
        dy = -int(self.wavefile[0][0])
       
        for x in range(0,len(array),int(len(array)/pieces)):     
            self.view.objects.append(self.scene.addLine(dx, dy, dx+1, -int(array[x][0])))
            dx += int(float(len(array))/float(pieces))
            dy = int(array[x][0])
           
       
           
def readwave(wavfilename):
    w=wave.open(wavfilename,'rb')
    (nchannel, width, rate, length, comptype, compname) = w.getparams()
    frames = w.readframes(length)
    data = numpy.array(struct.unpack("%sh" %length*nchannel, frames)).reshape(length,nchannel)
    return data

app = QApplication(sys.argv)
form = MainForm()
form.show()
app.exec_() 
Wenn der Code das Forum sprengt kann ich auch gerne auslagern :P


Vielen Dank für eure Hilfe!
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Wahrscheinlich ist Dir die Funktionweise des GraphicsViews noch nicht so richtig klar. Hier mal eine kurze schematische Erklärung dazu:

- Es gibt 3 "Darstellungsebenen", die Du unterscheiden mußt:
1.) Scene - Ist im Prinzip unbegrenzt, bei Dir sind das die gezeichneten Linien.
2.) sceneRect - Dient zur Darstellung eines Ausschnittes aus Scene in einem View.
3.) GraphicsView - Zeigt Scene-Auschnitte an, ist das sceneRect größer als der View, wird gescrollt.
- Der View zeigt nur an, was ihm als anzuzeigen gegeben wird (sceneRect), ist sceneRect nicht gesetzt gilt die komplette Scene.
- Transformationen werden mittels einer Transformationsmatrix am View vorgenommen, als Datenbasis dient das Scene.

Was ist jetzt mit dem sceneRect und der Viewgröße? Stell Dir beides wie Bilderrahmen vor, die die Ausgabe beschränken: die Viewgröße als Grenze des tatsächlich Sichtbaren und die sceneRect-Größe als Grenze des im View Scrollbaren. Das hat dann Auswirkungen auf die Transformationen:
translate() übersetzt Offsets nur zwischen diesen beiden "Bilderahmen", ist die zugrundeliegende Scene größer als Dein SceneRect, gibt es Bereiche, in die Du nicht translatieren kannst. Abhilfe wäre hier, das sceneRect zu vergrößern oder bei Bedarf zu verschieben (ersteres kann bei großer Scene rechenaufwendig werden).

Vllt. bringt das ja etwas Licht in die Scene ;)

Ach ja: Codeauslagern wäre angebracht, der Editier- und Vorschaumodus braucht jetzt schon sehr lange.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Hi!

Vielen Dank für deine einleuchtende Erklärung!

Werde also noch ein bisschen rumwerkeln müssen! :P
Antworten