QGraphisView / QGraphicsScene, Koordinaten

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
mr_egbert
User
Beiträge: 14
Registriert: Montag 13. Januar 2020, 13:33

Mittwoch 22. Januar 2020, 09:17

Guten Morgen,

ich habe ein Verständnisproblem mit QGraphicsView und QGraphicsScene.
In meinem Beispielprogramm werden mehrere Rechtecke gezeichnet. Die zwei kleinen Rechtecke sollen sich dann innerhalb des großen Rechtecks bewegen. Bei dem Rechteck, das bei 0,0 eingefügt wurde funktioniert das gut, das andere rutscht immer über die Begrenzung hinaus.

Worauf beziehen sich die Koordinaten, die ich mit punkt=self.rect.scenePos() ermittle?
Wie kann ich die Koordinaten auf QGraphicsScene ( "Modellbereich"?) umrechnen?

Viele Grüße
Martin

Code: Alles auswählen

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QGraphicsView, QGraphicsScene, \
     QVBoxLayout, QHBoxLayout, QPushButton
from PyQt5.QtCore import QTimer

class Fenster(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(800,600)

        self.btnZeichne=QPushButton("Rechtecke zeichnen", self)
        self.btnStart=QPushButton("Start", self)
        self.btnStop=QPushButton("Stop", self)
        
        self.scene = QGraphicsScene()
        self.view = QGraphicsView(self.scene)

        hbl = QHBoxLayout()
        hbl.addWidget(self.btnZeichne)
        hbl.addWidget(self.btnStart)
        hbl.addWidget(self.btnStop)
        
        vbl = QVBoxLayout()
        vbl.addWidget(self.view)
        vbl.addLayout(hbl)

        self.btnZeichne.clicked.connect(self.zeichne)
        self.btnStart.clicked.connect(self.start)
        self.btnStop.clicked.connect(self.stop)

        self.setLayout(vbl)

        self.show()

    def zeichne(self):
        self.rect1=self.scene.addRect(0,0,550,350)
        self.rect2=Rechteck(0,0,50,50)
        self.rect3=Rechteck(100,50,50,50)

    def start(self):
        try:
            self.rect2.start()
            self.rect3.start()
        except:
            pass

    def stop(self):
        try:
            self.rect2.stop()
            self.rect3.stop()
        except:
            pass

class Rechteck():
    def __init__(self, x1, y1, x2, y2):
        self.rect=w.scene.addRect(x1,y1,x2,y2)
        self.timer=QTimer()
        self.timer.timeout.connect(self.move)
        self.directionX=1
        self.directionY=1

    def move(self):
        punkt=self.rect.scenePos()
        if(punkt.x()>500 and self.directionX==1):
            self.directionX=-1
        if(punkt.x()<0 and self.directionX==-1):
            self.directionX=+1
        if(punkt.y()>300 and self.directionY==1):
            self.directionY=-1
        if(punkt.y()<0 and self.directionY==-1):
            self.directionY=+1
        self.rect.moveBy(self.directionX,self.directionY)

    def start(self):
        self.timer.start(8)

    def stop(self):
        self.timer.stop()
        
app=QApplication(sys.argv)
w=Fenster()
sys.exit(app.exec_())
Benutzeravatar
__blackjack__
User
Beiträge: 6988
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Mittwoch 22. Januar 2020, 10:54

@mr_egbert: Wirf mal einen Blick in den Style Guide for Python Code, da gibt's einiges zu verbessern was Leerzeichensetzung angeht. Und um Bedingungen bei ``if`` gehören keine Klammern.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Wenn man das macht, stellt man fest das `w` in `Rechteck.__init__()` benutzt wird, also eine globale Variable ist – das macht man nicht. Alles was Funktionen und Methoden ausser Konstanten benötigen wird als Argument(e) übergeben. Wobei ich da nicht das Fenster sondern den `QGraphicsView` übergeben würde, denn über das Fenster selbst braucht so ein Rechteck nichts zu wissen.

`w` ist auch ein ganz schlechter Name. Wofür soll der stehen? Das sollte der Leser nicht raten müssen. Das gleiche gilt für kryptische Pre- und Suffixe. Wenn man `button` meint, sollte man das auch schreiben.

`x1`, `y1`, `x2`, `y2` sind falsch benannt. Es handelt sich nicht um zwei Punkte sondern um einen Punkt und Breite und Höhe.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Attribute sollten nicht ausserhalb der `__init__()` eingeführt werden. Das ist verwirrend und fehleranfällig. Wenn man die `__init__()` durchgelesen hat, sollte man wissen welche Attribute diese Klasse hat. Dann muss man an anderer Stelle nicht Ausnahmen abfangen die nur deswegen entstehen weil es Attribute (noch) nicht gibt. Wobei *alle* Ausnahmen einfach komplett ignorieren das schlechteste ist was man machen kann.

Namen durchnummerieren ist in der Regel ein Zeichen das man sich entweder bessere Namen ausdecken will, oder gar keine einzelnen Namen sondern eine Datenstruktur verwenden möchte. Oft ist das dann eine Liste. Wie im Falle der Rechtecke. Wobei `rect1` nirgends verwendet wird, also auch keinen Namen braucht.

Die Position ist der lokale 0-Punkt des Items. Wenn Position 0 mit der oberen linken Ecke übereinstimmen soll, dann muss die obere linke Ecke vom Rechteck auch (0,0) sein. Also zum Beispiel statt (100, 50, 50, 50) müssten das Rechteck durch (0, 0, 50, 50) beschrieben werden, und dann mit `setPos()` nach (100, 50) gesetzt werden.

Code: Alles auswählen

#!/usr/bin/env python3
import sys

from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import (
    QApplication,
    QGraphicsScene,
    QGraphicsView,
    QHBoxLayout,
    QPushButton,
    QVBoxLayout,
    QWidget,
)

RECTANGLE_SIZE = 50
FIELD_SIZE = FIELD_WIDTH, FIELD_HEIGHT = (
    500 + RECTANGLE_SIZE,
    300 + RECTANGLE_SIZE,
)


class Fenster(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(800, 600)

        self.zeichne_button = QPushButton("Rechtecke zeichnen", self)
        self.start_button = QPushButton("Start", self)
        self.stop_button = QPushButton("Stop", self)

        self.scene = QGraphicsScene()
        self.view = QGraphicsView(self.scene)

        button_layout = QHBoxLayout()
        button_layout.addWidget(self.zeichne_button)
        button_layout.addWidget(self.start_button)
        button_layout.addWidget(self.stop_button)

        layout = QVBoxLayout()
        layout.addWidget(self.view)
        layout.addLayout(button_layout)
        self.setLayout(layout)

        self.zeichne_button.clicked.connect(self.zeichne)
        self.start_button.clicked.connect(self.start)
        self.stop_button.clicked.connect(self.stop)

        self.rechtecke = list()

    def zeichne(self):
        self.scene.addRect(0, 0, *FIELD_SIZE)
        for x, y in [(0, 0), (100, 50)]:
            self.rechtecke.append(Rechteck(self.scene, x, y))

    def start(self):
        for rechteck in self.rechtecke:
            rechteck.start()

    def stop(self):
        for rechteck in self.rechtecke:
            rechteck.stop()


class Rechteck:
    def __init__(self, scene, x, y):
        self.rect = scene.addRect(0, 0, RECTANGLE_SIZE, RECTANGLE_SIZE)
        self.rect.setPos(x, y)
        self.timer = QTimer()
        self.timer.timeout.connect(self.move)
        self.direction_x = 1
        self.direction_y = 1

    def move(self):
        punkt = self.rect.scenePos()
        if punkt.x() > FIELD_WIDTH - RECTANGLE_SIZE and self.direction_x == 1:
            self.direction_x = -1
        if punkt.x() < 0 and self.direction_x == -1:
            self.direction_x = +1
        if punkt.y() > FIELD_HEIGHT - RECTANGLE_SIZE and self.direction_y == 1:
            self.direction_y = -1
        if punkt.y() < 0 and self.direction_y == -1:
            self.direction_y = +1
        self.rect.moveBy(self.direction_x, self.direction_y)

    def start(self):
        self.timer.start(8)

    def stop(self):
        self.timer.stop()


def main():
    app = QApplication(sys.argv)
    fenster = Fenster()
    fenster.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
long long ago; /* in a galaxy far far away */
mr_egbert
User
Beiträge: 14
Registriert: Montag 13. Januar 2020, 13:33

Mittwoch 22. Januar 2020, 11:32

Vielen herzlichen Dank für die Antwort und die ausführliche Erklärung!
Antworten