__deets__ hat geschrieben: ↑Dienstag 1. Januar 2019, 11:07
Denn du könntest ein Countdown-Objekt analog zu einem Star einführen. Und dann die _stars in _things oder so umbennennen. Am Anfang erzeugst du dann nur ein Countdown-Objekt. Dessen Update Methode wird dann immer mit der verflossenen Zeit aufgerufen & modifiziert sein Text-item (oder entfernt es und legt ein neues an). Mit scene clear darf dann natürlich nicht gearbeitet werden.
Ohne `scene.clear()` schaffe ich es nicht den Countdown-Text zu aktualisieren. Wie geht das ohne `scene.clear()`?
__deets__ hat geschrieben: ↑Dienstag 1. Januar 2019, 11:07
Wenn der Countdown abgelaufen ist, entfernt man das Objekt aus der Liste der Dinge, und fügt die Sterne ein. Dazu muss man ggf das Protokoll etwas erweitern: Update muss zurück geben, ob das Objekt weiter leben soll, oder entfernt werden muss. Und es muss eine neue Liste von Objekten zurück geben können. Man könnte also ein Tupel (True, []) zurück geben in update.
Was meinst du mit "Protokoll"?
__deets__ hat geschrieben: ↑Dienstag 1. Januar 2019, 11:07
Auf die Art kannst du zb auch einen StarSpawner machen, der die Sterne nach und nach einführt.
Der entscheidende Aspekt aber ist, dass die “Engine” generisch bleibt.
Meinst du mit "generisch", dass die Methode `_update_all()` sowohl den Countdown als auch die Sterne aktualisieren kann?
__deets__ hat geschrieben: ↑Dienstag 1. Januar 2019, 23:09
Auf dem Weg ja, die spawn_stars Methode sollte natürlich uns Countdown Objekt. Und üblicherweise sammelt man die zu entfernenden Objekte auf, und macht das dann am Ende in einem Rutsch. Genauso fügt man dann neue erst am Ende wirklich hinzu & erst in nächsten Zyklus werden die gerechnet.
Warum sollte die Methode `spawn_stars()` ins `countdown`-Objekt? Das `graphics_window`-Objekt habe ich eigentlich als Ablaufsteuerung angesehen, die Objekte erstellt, zur Liste hinzufügt, laufend aktualisiert, usw. Ist das nicht so?
@Sirius3: Deine Empfehlungen habe ich so gut wie mir möglich umgesetzt.
Aktueller Code (zumindest der Countdown funktioniert
):
Code: Alles auswählen
import sys
import time
from random import random
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtWidgets import QApplication, QGraphicsView, QGraphicsScene
from PyQt5.QtGui import QPainterPath
START_COUNTDOWN = 3
UPDATE_INTERVAL = 100
SPEED = 5000
MIN_DISTANCE = 30000
DISTANCE_VARIANCE = 20000
D = 1000
class Countdown:
def __init__(self, scene, remaining_time):
self.scene = scene
self.remaining_time = remaining_time
self.painterpath = QPainterPath()
self._path = scene.addPath(self.painterpath)
def update(self, elapsed_time):
self.scene.clear() # Ohne diese Anweisung wird der Text nicht mehr aktualisiert.
self.remaining_time = (START_COUNTDOWN - elapsed_time)
self.scene.addText(f"In {self.remaining_time} Sekunden gehts los!")
return self.remaining_time > 0
class Star:
def __init__(self, scene):
painterpath = QPainterPath()
painterpath.addRect(-10, -10, 20, 20)
self._x = random() * 20000 - 10000
self._y = random() * 20000 - 10000
self._z = random() * 20000 + MIN_DISTANCE
self._speed = random() * SPEED
self._path = scene.addPath(painterpath)
self.draw_pen()
def draw_pen(self):
self._path.setPen(Qt.red)
self._path.setBrush(Qt.transparent)
def draw_brush(self):
self._path.setBrush(Qt.blue)
def update(self, elapsed):
self._z -= elapsed * self._speed
if self._z < 0:
self._z = 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 random() > .5 == 0:
self.draw_pen()
else:
self.draw_brush()
class GraphicsWindow(QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
self._last = time.monotonic()
self.scene = QGraphicsScene(self)
self.setScene(self.scene)
self.scene.setSceneRect(-400, -400, 800, 800)
self.showFullScreen()
self.countdown = Countdown(self.scene, START_COUNTDOWN)
self._things_on_screen = list()
self._things_on_screen.append(self.countdown)
self._timer = QTimer()
self._timer.setTimerType(Qt.PreciseTimer)
self._timer.setInterval(100)
self._timer.timeout.connect(self._update_all)
self._timer.start()
self._start = time.monotonic()
def spawn_star(self):
self._things_on_screen.append(Star(self.scene))
def _update_all(self):
now = time.monotonic()
elapsed = now - self._start
for thing in self._things_on_screen:
if not thing.update(elapsed):
self._things_on_screen.clear() # Liste leeren, wenn Startcountdown abgelaufen.
def keyPressEvent(self, event):
if event.key() == 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