Fragen zu QGraphicsscene und QGraphicsviev

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Benutzeravatar
Arthur Dent
User
Beiträge: 23
Registriert: Montag 12. September 2011, 09:51

Hallo

Ich benötige zur Visualisierung von Daten eine skalierbare Zeichenfläche. Ich weiß allerdings nicht einmal zur Laufzeit wie groß die Grafik schlussendlich wird, bzw die Zeichnung muss sich evt. zur Laufzeit in einer nicht absehbaren Anzahl von Schritten vergrößern. Um mich in die Funktionsweise von QGraphicssene und QGraphicsview reinzudenken hab ich erstmal mit ner kleinen Spielerei angefangen.

Was das Programm eigentlich machen soll:
- Quadrate an der Curserposition zeichnen
- über das Mausrad Skalierbar sein
- Die Fenstergröße soll variabel sein

Wo liegt das Problem:
- klickt man in den Randbereich des Fensters, so werden die Quadrate nicht an der Mausposition eingefügt

Zur anschauung mal der code (hoffe das ist nicht zu Lang fürs Forum)

Code: Alles auswählen

#!usr/bin/python

import sys, math
from PyQt4 import QtGui

class example(QtGui.QMainWindow):
    def __init__(self):
        super(example, self).__init__()
        self.setupUI()

    def setupUI(self):

        view = MyGraphicsView()
        view.setScene(MyGraphicsSscene())
        self.setCentralWidget(view)
        self.show()


class MyGraphicsView(QtGui.QGraphicsView):
    def __init__(self):
        super(MyGraphicsView, self).__init__()

        self.setRenderHint(QtGui.QPainter.Antialiasing)

    def wheelEvent(self, event):
        """ skalieren """

        factor = math.pow(2.0, -event.delta() / 240.0)
        self.scale(factor, factor)


class MyGraphicsSscene(QtGui.QGraphicsScene):
    def __init__(self):
        super(MyGraphicsSscene, self).__init__()

    def mousePressEvent(self, event):
        """ zeichnen """

        point = event.scenePos()
        self.drawPoint(point.x(), point.y())

    def drawPoint(self, x, y):
        """ Zeichenmethode """

        item = QtGui.QGraphicsRectItem(x-10, y-10, 20, 20)
        self.addItem(item)


def main():

    app = QtGui.QApplication(sys.argv)
    window = example()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
Meine Vermutung dazu:
beim ersten klick wirde die Größe der Scene genau auf die Größe eines Rechtecks festgelegt. Klickt man dann erneut, liegt der Curser nicht mehr in der Scene von der ich eigentlich die Koordinaten abfrage. Aus irgendeinem Grund (versteh ich nicht genau - Erklärung wäre gewünscht) vergrößert sich die Scene jetzt. Wenn ich nun in einen Bereich klicke der mit Sicherheit in der Scene liegt macht das Programm genau das was es machen soll.

Meine Frage:
Wie löse ich diese Problem am intelligentesten, mir schwebt vor die Größe der Scene im Wheelevent anzupassen, allerdings muß dabei der aktuelle Skalierungszustand berücksichtigt werden. Außerdem wird eine Größenänderung des Fensters damit nicht berücksichtigt.

Ich freue mich über eure Ratschläge,

Gruß Arthur Dent
Optimismus ist, bei Gewitter auf dem höchsten Berg in einer Kupferrüstung zu stehen und "scheiß Götter" zu rufen

Terry Pratchett
BlackJack

@Arthur Dent: Also nach meiner Beobachtung werden die Quadrate auch im Randbereich immer an der Mausposition eingefügt. Aber dort wird dann sofort *nach* dem Zeichnen automatisch die Skalierung/Verschiebung der gesamten Zeichnung angepasst, so das auch alle Quadrate schön sichtbar sind. Diesen „Autofokus” müsstest Du abschalten.

Andererseits, wenn Du die nicht interaktiv zeichnen willst, sondern eine Datenmenge plotten möchtest, und am Ende sicher sein möchtest, dass alles angezeigt wird, egal wie gross die Koordinaten geworden sind, dann ist dieser „Autofokus” doch genau das was Du haben möchtest.
Benutzeravatar
Arthur Dent
User
Beiträge: 23
Registriert: Montag 12. September 2011, 09:51

Auf die Idee, dass der Autofocus das Problem verursacht wär ich nie gekommen. Werd mir das mal anschauen wenn ich zurück zu Hause bin.
Prinzipiell hast du recht damit, dass man die ganze Zeichnung sehen Können sollte, einige Elemente sollen aber interaktiv mit der Maus hinzugefügt werden, für diese ist es ein wenig verwirrend wenn sie nicht an der Stelle gezeichnet werden wo man hinklickt. Mann kann ja sicherlich den Fokus so einsetzen wie man es gerade braucht. Ich werd dazu nochmal die Doku konsultieren.

Bis dahin vielen Dank erstmal!
Optimismus ist, bei Gewitter auf dem höchsten Berg in einer Kupferrüstung zu stehen und "scheiß Götter" zu rufen

Terry Pratchett
lunar

@Arthur Dent: Ein "QGraphicsScene"-Objekt hat keine Größe. Die Szene selbst ist unbegrenzt, begrenzt ist lediglich der Ausschnitt aus der Szene, welche die Ansicht anzeigt. Dieser Ausschnitt wird durch die Eigenschaft "sceneRect" bestimmt. In der Dokumentation heißt es:
[…]
If unset, or if a null QRectF is set, this property has the same value as QGraphicsScene::sceneRect, and it changes with QGraphicsScene::sceneRect. Otherwise, the view's scene rect is unaffected by the scene.
[…]
"QGraphicsScene.sceneRect" ist dabei nicht die Größe der Szene (denn diese ist wie gesagt unbegrenzt), sondern lediglich der maximale Umriss (aka „bounding rectangle“) aller darin enthaltener Elemente. Diese Eigenschaft gibt also den Bereich von der oberen linken Ecke des obersten, am weitest links stehenden Element bis zur unteren rechten Ecke des untersten, am weitest rechts stehenden Element an.

Folglich wird der in einer Ansicht sichtbare Ausschnitt der Szene normalerweise automatisch angepasst, so dass alle darin enthaltenen Elemente vollständig zu sehen sind. Daraus ergibt sich das von Dir beobachtete Verhalten der Anwendung. Wenn Du mit "QGraphicsView.setSceneRect()" einen festen Ausschnitt einstellst, dann befinden sich die am Rand eingefügten Rechtecke exakt an der Position der Maus, und sind dementsprechend abgeschnitten.

Übrigens, "drawPoint()" ist ein komischer Name für eine Funktion, die Rechtecke zeichnet ;)
Benutzeravatar
Arthur Dent
User
Beiträge: 23
Registriert: Montag 12. September 2011, 09:51

Den Satz aus der Doku hatte ich auch gelesen. Zugegebenermaßen war meine Formulierung etwas schlecht. Wenn ich dich richtig Verstehe, besteht das Problem darin, das die Größe des Graphicssvie größer ist als die des scenerects. Das war jedenfalls das ungefähr das was ich meinte.
Der Naheliegendste Lösungsansatz wäre wohl die aktuelle Größe des Graphicsview als scenerect zu setzen. Ich bin mir allerdings grad nicht sicher ob das auch noch klappt wenn die Szene skaliert wird. Hab heut leider noch zuviele andere Dinge auf dem Schreibtisch die Vorgehen, werds aber am WE mal ausprobieren.

drawPoint ist wirklich n doofer Name! :) die Funktion hätte von mir aus auch das Haus vom Nikolaus malen können, es ging mir nur ums Prinzip.
Optimismus ist, bei Gewitter auf dem höchsten Berg in einer Kupferrüstung zu stehen und "scheiß Götter" zu rufen

Terry Pratchett
lunar

@Arthur Dent: Du kannst mit "fitInView()" die Szene so skalieren, dass exakt ein bestimmter Ausschnitt vollständig sichtbar ist.
Benutzeravatar
Arthur Dent
User
Beiträge: 23
Registriert: Montag 12. September 2011, 09:51

fitInView() ist auch ne tolle Sache, bewirkt aber, sofern ich es richtig benutzt habe, nicht das, was ich mir erhofft habe.
Ich habe das Problem jetzt vorerst gelöst, in dem ich im resizeEvent des GraphicsView die größe des sceneRect anpasse:

Code: Alles auswählen

def resizeEvent(self, event):
        """ sceneRect auf Fenstergroesse anpassen"""
        self.setSceneRect(0, 0, self.width(), self.height())
Das ist aber auch noch nicht so richtig befriedigend, weil sich auf diese Art die Scrollbars etwas verwirrend verhalten, ich denke weil die Größe des sceneRects nun nicht mehr mit der des „bounding rectangle“ übereinstimmt.

musss ich nochmal drüber nachdenken.
Optimismus ist, bei Gewitter auf dem höchsten Berg in einer Kupferrüstung zu stehen und "scheiß Götter" zu rufen

Terry Pratchett
lunar

@Arthur Dent: Mmh, was möchtest Du eigentlich erreichen? Willst Du, dass immer ein bestimmter Ausschnitt der Szene sichtbar ist und entsprechend skaliert wird, wenn Du die Fenstergröße veränderst, oder willst Du, dass die Größe des Ausschnitts automatisch der Fenstergröße angepasst wird und die Skalierung konstant bleibt?

Ich glaube auch, Du bist Dir noch nicht ganz im Klaren darüber, dass es sich bei den Koordinaten der Szene und denen des Bildschirms um völlig unabhängige Koordinatensysteme handelt, die je nach Transformation im View entsprechend umgerechnet werden. Lies vielleicht mal den Abschnitt The Graphics View Coordinate System in der Dokumentation.
Antworten