Geometrische Objekte werden generiert, aber nicht angezeigt.

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Hallo

Heute wollte ich mit Qt geometrische Formen zeichnen. Folgender Code zum Zeichnen eines Rechtecks funktioniert noch.

Rechteck.py:

Code: Alles auswählen

import sys
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtGui import QPainter, QBrush, QPen
from PyQt5 import QtCore


class MainWindow(QWidget):

    def __init__(self, parent=None):
        QWidget.__init__(self)
        self.pen = QPen(QtCore.Qt.red) # Außenliniefarbe
        self.pen.setWidth(5) # Außenliniendicke
        self.brush = QBrush(QtCore.Qt.blue) # Füllfarbe


    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setPen(self.pen)
        painter.setBrush(self.brush)
        painter.drawRect(50, 50, 200, 200)


def main():
    app = QApplication(sys.argv)
    main_window = MainWindow(app)
    main_window.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

Im nächsten Schwierigkeitsgrad wollte ich dann Punktobjekte, die auf einer Kreisbahn angeordnet sind, mittels einer for-Schleife erstellen und zeichnen. Punktobjekte werden laut `print(points)` mit dem folgenden Code zwar erstellt, aber es erscheinen keine Punkte im `MainWindow`.

Punkte.py

Code: Alles auswählen

from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPainter, QBrush
from PyQt5 import QtCore
import math
import sys

points = []

circle_points = 30
point_color = QtCore.Qt.red
point_diameter = 20
circle_diameter = 500


class MainWindow(QWidget):

    def __init__(self, parent=None):
        QWidget.__init__(self)
        self.setGeometry(0,0,800,800)
        for i in range(circle_points):
            angle = (360 / circle_points * i)
            point = Point(self, angle, point_color, point_diameter, circle_diameter)
            points.append(point)

        print(points)


class Point(QWidget):

    def __init__(self, main_window, angle, point_color, point_diameter, circle_diameter):
        super().__init__()
        self.main_window = main_window
        self.x_center = self.width() / 2
        self.y_center = self.height() / 2
        self.point_color = point_color
        self.point_diameter = point_diameter
        self.circle_diameter = circle_diameter
        self.angle = angle
        self.brush = QBrush(self.point_color)

    def paintEvent(self, event):
        painter = QPainter(self.main_window)
        painter.setBrush(self.brush)

        x = self.x_center + (self.circle_diameter / 2 * math.cos(math.radians(self.angle)))
        y = self.y_center + (self.circle_diameter / 2 * math.sin(math.radians(self.angle)))

        x1 = int(x - self.point_diameter)
        y1 = int(y - self.point_diameter)
        x2 = int(x + self.point_diameter)
        y2 = int(y + self.point_diameter)
        print("Test")

        painter.drawEllipse(x1, y1, x2, y2)


def main():
    app = QApplication(sys.argv)
    main_window = MainWindow(app)
    main_window.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
  1. Die Anweisung `print("Test")` wird gar nicht ausgeführt. Wo liegt der oder die Fehler?
  2. Warum braucht man zum Zeichnen eigentlich die Methode `paintEvent(self, event)`? Von den über Google gefundenen Erklärungen habe ich nur verstanden, dass damit wohl die Methode `paintEvent` der Klasse `QWidget` überschrieben wird.
Gruß
Atalanttore
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da ist vieles im argen.

- Du benutzt widgets. Die wollen ziemlich sicher ueber ihre parent-Eigenschaft miteinander verbunden werden. Du uebergibst aber kein parent. Das widget haengt also "im leeren". Und das, obwohl du schon main_window mit reinreichst. Und darum wird wohl auch dein print nicht aufgerufen
- widgets sind nicht gedacht fuer so etwas. Die sind per Definition immer rechteckig, und eine Art Spielfeld oder Grafik daraus aufzubauen ist ungewoehnlich bis unmoeglich. Wenn du viele graphische Objekte darstellen willst, schau dir den QGraphicsView an, der ist dazu gedacht.
- das das mainwindow sich seine geometry selbst setzt macht es als allgemeines Objekt unnutzbar. Das sollte eigentlich gar nicht, bestenfalls von aussen, geschehen.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@__deets__: Soweit ich deine Vorschläge verstanden habe und mithilfe des Codebeispiels hier habe ich den Code jetzt umgebaut (siehe unten). Leider werden auch mit dem umgebauten Code noch keine Punkte gezeichnet. Als Fehlermeldung erscheint nun `QPainter::setBrush: Painter not active` in der Konsole.

__deets__ hat geschrieben: Sonntag 2. Dezember 2018, 17:20 - Du benutzt widgets. Die wollen ziemlich sicher ueber ihre parent-Eigenschaft miteinander verbunden werden. Du uebergibst aber kein parent. Das widget haengt also "im leeren". Und das, obwohl du schon main_window mit reinreichst. Und darum wird wohl auch dein print nicht aufgerufen
Ein `parent` wird jetzt übergeben und `print("Test")` wird aufgerufen.


__deets__ hat geschrieben: Sonntag 2. Dezember 2018, 17:20 - widgets sind nicht gedacht fuer so etwas. Die sind per Definition immer rechteckig, und eine Art Spielfeld oder Grafik daraus aufzubauen ist ungewoehnlich bis unmoeglich. Wenn du viele graphische Objekte darstellen willst, schau dir den QGraphicsView an, der ist dazu gedacht.
- das das mainwindow sich seine geometry selbst setzt macht es als allgemeines Objekt unnutzbar. Das sollte eigentlich gar nicht, bestenfalls von aussen, geschehen.
`MainWindow()` wurde zu `GraphicsWindow()` und erbt von `QGraphicsView`. Hat sich damit auch das Problem mit der `geometry` gelöst?


Der umgebaute Code:

Code: Alles auswählen

from PyQt5.QtCore import QRectF
from PyQt5 import QtCore

from PyQt5.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGraphicsItem
from PyQt5.QtGui import QPainter, QBrush
import math

points = []

circle_points = 30
point_color = QtCore.Qt.red
point_diameter = 20
circle_diameter = 500


class Point(QGraphicsItem):
    def __init__(self, angle, point_color, point_diameter, circle_diameter, x_center, y_center):
        super(Point, self).__init__()
        self.x_center = x_center
        self.y_center = y_center
        self.point_color = point_color
        self.point_diameter = point_diameter
        self.circle_diameter = circle_diameter
        self.angle = angle
        self.brush = QBrush(self.point_color)


    def paint(self, painter, option, widget):
        painter = QPainter()
        painter.setBrush(self.brush)

        x = self.x_center + (self.circle_diameter / 2 * math.cos(math.radians(self.angle)))
        y = self.y_center + (self.circle_diameter / 2 * math.sin(math.radians(self.angle)))

        x1 = int(x - self.point_diameter)
        y1 = int(y - self.point_diameter)
        x2 = int(x + self.point_diameter)
        y2 = int(y + self.point_diameter)
        print("Test")

        painter.drawEllipse(x1, y1, x2, y2)


    def boundingRect(self):
        return QRectF(0,0,800,800)


class GraphicsWindow(QGraphicsView):
    def __init__(self, parent=None):
        super(GraphicsWindow, self).__init__(parent)
        scene = QGraphicsScene(self)
        self.x_center = self.width() / 2
        self.y_center = self.height() / 2

        for i in range(circle_points):
            angle = (360 / circle_points * i)
            point = Point(angle, point_color, point_diameter, circle_diameter, self.x_center, self.y_center)
            points.append(point)
            scene.addItem(point)

        scene.setSceneRect(0, 0, 800, 800)
        self.setScene(scene)
        self.setCacheMode(QGraphicsView.CacheBackground)


def main():
    import sys
    app = QApplication(sys.argv)
    graphics_window = GraphicsWindow()
    graphics_window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
Bei der Methode `paint()` in der Klasse `Point(QGraphicsItem)` bemäkelt meine IDE (PyCharm) folgendes:

Code: Alles auswählen

Signature of method 'Point.paint()' does not match signature of base method in class 'QGraphicsItem'
This inspection detects inconsistencies in overriding method signatures.
In der Doku zu `paint()` werden aber genauso `painter`, `option` und `widget` als formale Parameter genannt. Was ist hier mit Signatur gemeint?

Gruß
Atalanttore
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich würde erstmal mit den eingebauten Items spielen, bevor ich ein eigenes erstellen würde. Ggf reichen die schon völlig aus.

Das geometry Ding hat ja was mit der Position und Dimension des Fensters selbst zu tun. Das Scene rect legt hingegen die Koordinaten der dargestellten Items fest - das wird dann umtransformiert. Das ist ja was komplett anderes.

Mein Punkt richtet sich gegen ersteres: entweder man bietet einen fullscreen Modus an, den der User wieder verlassen kann. Oder der User positioniert das Fenster selbst. Das vorzugeben ist ein usability-no-no. Du könntest die letzte Position dir merken und restaurieren. Das wäre ok.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Achso: warum pycharm da bockig ist - kA. Ich benutze das nicht, aber generell ist Typinferenz in Python schwierig. Das kann schon mal sein, dass das nicht klappt. Wenn der Code tut, kann es so falsch nicht sein.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Ich habe jetzt `QGraphicsItem` durch `QGraphicsEllipseItem` (QGraphicsEllipseItem → QAbstractGraphicsShapeItem → QGraphicsItem) ersetzt. Die Methode `setRect()` habe ich dann auch mal benutzt. Leider wird nach wie vor nichts gezeichnet und es erscheint immer noch folgende Fehlermeldung in Konsole:

Code: Alles auswählen

QPainter::setBrush: Painter not active

Code:

Code: Alles auswählen

from PyQt5.QtCore import QRectF
from PyQt5 import QtCore

from PyQt5.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGraphicsEllipseItem
from PyQt5.QtGui import QPainter, QBrush
import math

points = []

circle_points = 30
point_color = QtCore.Qt.red
point_diameter = 20
circle_diameter = 500



class Point(QGraphicsEllipseItem):
    def __init__(self, angle, point_color, point_diameter, circle_diameter, x_center, y_center):
        super(Point, self).__init__()
        self.x_center = x_center
        self.y_center = y_center
        self.point_color = point_color
        self.point_diameter = point_diameter
        self.circle_diameter = circle_diameter
        self.angle = angle
        self.brush = QBrush(self.point_color)


    def paint(self, painter, option, widget):
        painter = QPainter()
        painter.setBrush(self.brush)

        x = self.x_center + (self.circle_diameter / 2 * math.cos(math.radians(self.angle)))
        y = self.y_center + (self.circle_diameter / 2 * math.sin(math.radians(self.angle)))

        x1 = int(x - self.point_diameter)
        y1 = int(y - self.point_diameter)
        x2 = int(x + self.point_diameter)
        y2 = int(y + self.point_diameter)

        self.setRect(x1, y1, x2, y2)
        painter.drawEllipse(x1, y1, x2, y2)
        print("Ich werde auch ausgeführt :-)")

    def boundingRect(self):
        return QRectF(0,0,800,800)


class GraphicsWindow(QGraphicsView):
    def __init__(self, parent=None):
        super(GraphicsWindow, self).__init__(parent)
        scene = QGraphicsScene(self)
        self.x_center = self.width() / 2
        self.y_center = self.height() / 2

        for i in range(circle_points):
            angle = (360 / circle_points * i)
            point = Point(angle, point_color, point_diameter, circle_diameter, self.x_center, self.y_center)

            points.append(point)
            scene.addItem(point)

        scene.setSceneRect(0, 0, 800, 800)
        self.setScene(scene)
        self.setCacheMode(QGraphicsView.CacheBackground)


def main():
    import sys
    app = QApplication(sys.argv)
    graphics_window = GraphicsWindow()
    graphics_window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
Gruß
Atalanttore
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

So, ich habe damit mal ein bisschen rumgespielt. Du warst schon fast da, aber der entscheidende Fehler ist mir auch erst gerade aufgefallen: du baust einen eigenen Painter. Der haengt in der Luft, tut nichts und kann nichts. Du musst stattdessen einfach nur den uebergebenen nehmen, das war's.

Code: Alles auswählen

from PyQt5.QtCore import QRectF
from PyQt5 import QtCore

from PyQt5.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGraphicsEllipseItem
from PyQt5.QtGui import QPainter, QBrush
import math

points = []

circle_points = 30
point_color = QtCore.Qt.red
point_diameter = 20
circle_diameter = 500

class Point(QGraphicsEllipseItem):
    def __init__(self, angle, point_color, point_diameter, circle_diameter, x_center, y_center):
        super(Point, self).__init__()
        self.x_center = x_center
        self.y_center = y_center
        self.point_color = point_color
        self.point_diameter = point_diameter
        self.circle_diameter = circle_diameter
        self.angle = angle
        self.brush = QBrush(self.point_color)


    def paint(self, painter, option, widget):
        painter.setBrush(self.brush)

        x = self.x_center + (self.circle_diameter / 2 * math.cos(math.radians(self.angle)))
        y = self.y_center + (self.circle_diameter / 2 * math.sin(math.radians(self.angle)))

        x1 = int(x - self.point_diameter)
        y1 = int(y - self.point_diameter)
        x2 = int(x + self.point_diameter)
        y2 = int(y + self.point_diameter)

        self.setRect(x1, y1, x2, y2)
        painter.drawEllipse(x1, y1, x2, y2)
        print("Ich werde auch ausgeführt :-)")

    def boundingRect(self):
        return QRectF(0,0,800,800)


class GraphicsWindow(QGraphicsView):
    def __init__(self, parent=None):
        super(GraphicsWindow, self).__init__(parent)
        scene = QGraphicsScene(self)
        self.setScene(scene)
        self.x_center = self.width() / 2
        self.y_center = self.height() / 2

        for i in range(circle_points):
            angle = (360 / circle_points * i)
            scene.addRect(QRectF(i * 20, i * 20, 20, 20), QtCore.Qt.red )
            ellipse = QGraphicsEllipseItem((i + 1) * 20, i * 20, 20, 20)
            scene.addItem(ellipse)
            point = Point(angle, point_color, point_diameter, circle_diameter, self.x_center, self.y_center)
            scene.addItem(point)

        scene.setSceneRect(0, 0, 800, 800)
        self.setCacheMode(QGraphicsView.CacheBackground)


def main():
    import sys
    app = QApplication(sys.argv)
    graphics_window = GraphicsWindow()
    graphics_window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nachtrag: die bounding box ist natuerlich auch komplett falsch. Die muss schon die wirklichen Koordinaten und ausmasse des Items beruecksichtigen. Und da du ableitest, kannst du auch von QGraphicsItem ableiten. Das, was eine Ellipse ausmacht, benutzt du ja gar nicht. Mein Code tut das, wie man da den Pen/Brush setzt habe ich aber noch nicht rausgefummelt. Last but not least: leite NICHT ab, benutzt die addPath-Methode.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und hier noch ein Beispiel, welches illustriert, dass man nicht ableiten muss. Und welche Vorteile das Scene-Modell hat bei Koordinaten etc.

Code: Alles auswählen

import random
import time

from PyQt5.QtCore import QRectF
from PyQt5 import QtCore

from PyQt5.QtWidgets import QApplication, QGraphicsView, QGraphicsScene
from PyQt5.QtGui import QPainter, QBrush, QPainterPath

points = []

SPEED = 5000
MIN_DISTANCE = 30000
DISTANCE_VARIANCE = 20000


class Star:
    D = 1000

    def __init__(self, scene):
        path = QPainterPath()
        path.addRect(-10, -10, 20, 20)
        self._x, self._y = random.random() * 20000 - 10000, random.random() * 20000 - 10000
        self._z = random.random() * 20000 + MIN_DISTANCE
        self._speed = random.random() * SPEED
        self._path = scene.addPath(path)
        self._path.setPen(QtCore.Qt.red)

    def update(self, elapsed):
        self._z -= elapsed * self._speed
        if self._z < 0:
            self._z = random.random() * DISTANCE_VARIANCE + MIN_DISTANCE
        self._path.setX(self._x * self.D / self._z)
        self._path.setY(self._y * self.D / self._z)
        self._path.setScale(1 - self._z / (MIN_DISTANCE + DISTANCE_VARIANCE))
        self._path.setRotation(self._path.rotation() + elapsed * 360)


class GraphicsWindow(QGraphicsView):

    def __init__(self, parent=None):
        super(GraphicsWindow, self).__init__(parent)
        self._last = time.monotonic()
        scene = QGraphicsScene(self)
        self.setScene(scene)
        self.x_center = self.width() / 2
        self.y_center = self.height() / 2

        self._stars = []
        for i in range(100):
            self._stars.append(
                Star(scene)
            )
        scene.setSceneRect(-400, -400, 800, 800)

        self.setCacheMode(QGraphicsView.CacheBackground)
        self._timer = QtCore.QTimer(self)
        self._timer.setSingleShot(False)
        self._timer.setInterval(1000 / 60)
        self._timer.timeout.connect(self._update_all)
        self._timer.start()

    def _update_all(self):
        now = time.monotonic()
        elapsed = now - self._last
        self._last = now
        for star in self._stars:
            star.update(elapsed)


def main():
    import sys
    app = QApplication(sys.argv)
    graphics_window = GraphicsWindow()
    graphics_window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@__deets__: Danke für den vielen Beispielcode. Mit dem Durcharbeiten werde ich einige Zeit beschäftigt sein.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@__deets__: Ich habe mich jetzt mithilfe von Dokumentationen durchgearbeitet und mit deinem Beispielcode gespielt.

Meine Erkenntnisse und Fragen zu einzelnen Anweisungen habe ich mal als Kommentare im Beispielcode ergänzt.

Code: Alles auswählen

import random
import time

from PyQt5 import QtCore
from PyQt5.QtWidgets import QApplication, QGraphicsView, QGraphicsScene
from PyQt5.QtGui import QPainterPath

points = []

SPEED = 5000
MIN_DISTANCE = 30000
DISTANCE_VARIANCE = 20000


class Star:
    D = 1000 # Durchmesser?

    def __init__(self, scene):
        path = QPainterPath() # Pfad für Kontur erstellen
        path.addRect(-10, -10, 20, 20) # Kontur dem Stern zuweisen
        self._x, self._y = random.random() * 20000 - 10000, random.random() * 20000 - 10000 # Zufallskoordinaten für x und y berechnen
        self._z = random.random() * 20000 + MIN_DISTANCE # Zufallskoordinate für z berechnen
        self._speed = random.random() * SPEED # Zufallsgeschwindigkeit berechnen
        self._path = scene.addPath(path) # Pfad zur Szene hinzufügen
        self._path.setPen(QtCore.Qt.red) # Farbe der Außenlinie zuweisen

    def update(self, elapsed):
        self._z -= elapsed * self._speed # z-Koordinate neu berechnen
        if self._z < 0:
            self._z = random.random() * DISTANCE_VARIANCE + MIN_DISTANCE # Zufallszahl * Multiplikator + Mindestabstand
        self._path.setX(self._x * self.D / self._z) # ?
        self._path.setY(self._y * self.D / self._z) # ?
        self._path.setScale(1 - self._z / (MIN_DISTANCE + DISTANCE_VARIANCE)) # Sterne bei der Bewegung nach vorne größer skalieren
        self._path.setRotation(self._path.rotation() + elapsed * 360) # Drehbewegung der Sterne


class GraphicsWindow(QGraphicsView):

    def __init__(self, parent=None):
        super(GraphicsWindow, self).__init__(parent) # Programm läuft auch, wenn man kein parent übergibt
        self._last = time.monotonic() # Letzte Zeit zuweisen
        scene = QGraphicsScene(self) # Szene erstellen
        self.setScene(scene) # QGraphicsScene dem GraphicsWindow zuweisen
        # self.x_center = self.width() / 2
        # self.y_center = self.height() / 2

        self._stars = []
        for i in range(100):
            self._stars.append(
                Star(scene) # 100 Star-Objekte erstellen und Liste hinzufügen
            )
        scene.setSceneRect(-400, -400, 800, 800) # Größe der Szene festlegen

        self.setCacheMode(QGraphicsView.CacheBackground) # ?
        self._timer = QtCore.QTimer(self) # QTimer-Objekt erstellen
        self._timer.setSingleShot(False) # Programm läuft auch ohne diese Anweisung
        self._timer.setInterval(1000 / 60) # Timer-Intervall festlegen
        self._timer.timeout.connect(self._update_all) # Aktualisierungsmethode aufrufen, wenn Timer abgelaufen ist.
        self._timer.start() # Timer neu starten

    def _update_all(self):
        now = time.monotonic() # Aktuelle Zeit zuweisen
        elapsed = now - self._last # Vergangene Zeit berechnen
        self._last = now # Letzte Zeit durch aktuelle Zeit ersetzen
        for star in self._stars:
            star.update(elapsed) # Aktualisierungsmethode jedes Star-Objektes mit vergangener Zeit aufrufen


def main():
    import sys # Warum hier unten importieren?
    app = QApplication(sys.argv)
    graphics_window = GraphicsWindow()
    graphics_window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
Gruß
Atalanttore
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das mit dem sys in main musst du dich selbst fragen - von dir habe ich das ja ;) Ich habe das einfach uebernommen. Das ist so natuerlich Unsinn. Auch den Cache-Modus hast *du* eingefuehrt, keine Ahnung, was genau der bewirkt.

Und was deine Kommentare im allgemeinen angeht - die sind jetzt nicht so sinnvoll wie ich finde, denn sie sagen ja nur, was da steht. Zufallszahl * Multiplikator + Mindestabstand.
Viel interessanter waere zu sagen "bei negativem Z Stern wieder in der Ferne erscheinen lassen". Denn darum geht es ja.



D ist kein Durchmesser, sondern eine Konstante fuer die Perspektivische Transformation. Ich hab das so aus dem Gedaechtnis gemacht. Da koennte man jetzt mal in ein Buch zur 3D-Programmierung gucken.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@__deets__: Danke für die Antwort.
  1. Stammen die Formeln im Code alle aus einem Buch zur 3D-Programmierung?
  2. Ohne die folgenden Anweisungen läuft gar nichts mehr, aber was wird da eigentlich berechnet?

    Code: Alles auswählen

            self._path.setX(self._x * self.D / self._z)
            self._path.setY(self._y * self.D / self._z)
  3. Im Ausgabefenster erscheint sowohl ein horizontaler als auch ein vertikaler Scrollbalken. Kann man die Scrollbalken auch deaktivieren?
Gruß
Atalanttore
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

1. nein, aus meinem Kopf. So habe ich das schon auf dem AMIGA in assembler gemacht. Und da hatte ich es ggf aus einer Zeitschrift.
2. Das ist die perspektivische Transformation.
3. Bestimmt, durch Eigenschaften des Widgets. Im Designer mit rumspielen, Doku lesen. Weil aus dem Kopf weiß ich das nicht.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@__deets__:
Zu 1.: Wann hast du das letzte mal in Assembler auf dem AMIGA gearbeitet?
Zu 2.: Das ist also die Formel für die perspektivische Transformation.
Zu 3.: Welchen Codeteil in deinem Beispiel hast du mittels Qt Designer erstellt?

Gruß
Atalanttore
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

1) Keine Ahnung, 1996 oder so. Wieso ist das wichtig?
2) das ist irgendwie keine Frage, sondern eine Feststellung...
3) Keinen. Das ist hauptsaechlich DEIN Beispiel, lediglich zum laufen gebracht. Du siehst doch, dass da keine .ui-Datei vorkommt. Oder anderweitig generiert aussieht. Der Hinweis war gemeint als "nimm dir den Designer, bau einen GraphicsView ein, und manipulier den oder das parent-widget/layout (keine Ahnung was genau), lad die UI Datei, und gucks' dir an". Der Designer prasentiert einem eben all die Layout und sonstigen Optionen auf leicht erschliessbare Art & Weise, und darum benutze ich den fuer sowas - wenn ich das selbst machen wuerde, und eben nicht nur dein Beispiel aufgebohrt haette.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

@__deets__:
Zu 1.: ~22 Jahre könnte ich mir solche Details nicht im Kopf merken. :oops:
Zu 3.: Alles klar.

Gruß
Atalanttore
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Naja, das ist ja eine simple Division. Du kannst dir doch sicher Dinge merken wie zb die Fläche eines Kreises zu berechnen, etc. das ist ja nichts anderes. Und es war ein ziemliches aha-Erlebnis, weil ich vorher nur einen simpleren Algorithmus kannte, der einfach jeden Stern mit fixen Schritten bewegt. Das war eher C64-Style. Darum war das so ein “wichtiges” Ding.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Heute wollte ich die Sterne aus dem Beispiel blinken lassen. Dazu wollte ich zufällig entweder eine rote Außenlinie mit der Methode `setPen()` oder eine blaue Füllung mit der Methode `setBrush()` zeichnen lassen. Nun erscheinen selbst bei langsamer Geschwindigkeit blau gefüllte Rechtecke mit roter Außenlinie und nichts blinkt.
Anscheinend muss man die von `setPen()` oder `setBrush()` gezeichnete Außenlinie bzw. Füllung auch wieder löschen, wenn sie nicht mehr erscheinen soll. Leider habe ich in der Doku nichts passendes dazu gefunden.

Macht man das überhaupt so wie von mir gedacht?

Der Code:

Code: Alles auswählen

import sys
import random
import time
from random import randint

from PyQt5 import QtCore
from PyQt5.QtWidgets import QApplication, QGraphicsView, QGraphicsScene
from PyQt5.QtGui import QPainterPath


points = []

SPEED = 5000
MIN_DISTANCE = 30000
DISTANCE_VARIANCE = 20000


class Star:
    D = 1000

    def __init__(self, scene):
        path = QPainterPath()
        path.addRect(-10, -10, 20, 20)
        self._x = random.random() * 20000 - 10000
        self._y = random.random() * 20000 - 10000
        self._z = random.random() * 20000 + MIN_DISTANCE
        self._speed = random.random() * SPEED
        self._path = scene.addPath(path)
        self.draw_pen()

    def draw_pen(self):
        self._path.setPen(QtCore.Qt.red)

    def draw_brush(self):
        self._path.setBrush(QtCore.Qt.blue)


    def update(self, elapsed):
        self._z -= elapsed * self._speed
        if self._z < 0:
            self._z = random.random() * DISTANCE_VARIANCE + MIN_DISTANCE
        self._path.setX(self._x * self.D / self._z)
        self._path.setY(self._y * self.D / self._z)
        self._path.setScale(1 - self._z / (MIN_DISTANCE + DISTANCE_VARIANCE))
        self._path.setRotation(self._path.rotation() + elapsed * 360)

        if randint(0, 2) == 0:
            self.draw_pen()
            print("Pen")
        else:
            self.draw_brush()
            print("Brush")


class GraphicsWindow(QGraphicsView):

    def __init__(self, parent=None):
        super().__init__(parent)
        self._last = time.monotonic()
        scene = QGraphicsScene(self)
        self.setScene(scene)
        scene.setSceneRect(-400, -400, 800, 800)
        self.showFullScreen()

        self._stars = []
        for i in range(100):
            self._stars.append(Star(scene))

        self._timer = QtCore.QTimer(self)
        self._timer.setInterval(1000 / 30)
        self._timer.timeout.connect(self._update_all)
        self._timer.start()

    def _update_all(self):
        now = time.monotonic()
        elapsed = now - self._last
        self._last = now
        for star in self._stars:
            star.update(elapsed)

    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_Escape:
            self.close()



def main():
    app = QApplication(sys.argv)
    graphics_window = GraphicsWindow()
    graphics_window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
Gruß
Atalanttore
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: Man muss halt in beiden Fällen Pen und Brush setzen. Jeweils so dass man das eine sieht und das andere nicht. Zum Beispiel könntest Du die Farbe `Qt.transparent` für den jeweiligen Teil den man nicht sehen soll verwenden. Das würde ich allerdings nicht als blinken, sondern als flackern bezeichnen. :-)
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten