Ganz einfacher Startcountdown

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

Hallo

Ich habe mir heute einen ganz einfachen Startcountdown geschrieben. Nach dem Ablauf der Zeit wird der Startcountdown gelöscht und eine andere Methode mit weiteren Anweisungen (hier beispielhaft zum Zeichnen eines roten Rechtecks) aufgerufen. Wie findet ihr das?

Code:

Code: Alles auswählen

import time
import sys

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


class Countdown:
    COUNTDOWN = 5

    def __init__(self, scene):
        self.scene = scene
        self._time = 0
        self._text = None

    def update(self, elapsed):
        self._time += elapsed

        if self._text:
            self.scene.removeItem(self._text)
            self._time = self.COUNTDOWN - elapsed

        self._text = self.scene.addText(f"Noch {self._time:.2f} Sekunden")
        return self._time <= 0

class Rectangle:

    def __init__(self, scene):
        self.scene = scene
        path = QPainterPath()
        path.addRect(0,0,100,100)
        self._path = scene.addPath(path)
        self._path.setBrush(QtCore.Qt.red)


class GraphicsWindow(QGraphicsView):

    def __init__(self, parent=None):
        super(GraphicsWindow, self).__init__(parent)

        self.scene = QGraphicsScene(self)
        self.setScene(self.scene)
        self.scene.setSceneRect(-400, -400, 800, 800)

        self.start_countdown = Countdown(self.scene)
        self._last = time.monotonic()
        self.setCacheMode(QGraphicsView.CacheBackground)
        self._timer = QtCore.QTimer(self)
        self._timer.setSingleShot(False)
        self._timer.setInterval(1000 / 60)
        self._timer.timeout.connect(self._update_countdown)
        self._timer.start()

    def _update_countdown(self):
        _now = time.monotonic()
        _elapsed = _now - self._last
        countdown_expired = self.start_countdown.update(_elapsed)
        if countdown_expired:
            self._timer.stop()
            self.scene.clear()
            del self.start_countdown
            self._next_instruction()

    def _next_instruction(self):
        self.rectangle = Rectangle(self.scene)
        # Weitere Anweisungen


def main():
    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

Nicht so gut, weil du stark koppelst zwischen deinem Window und der Logik zur Darstellung von Countdown & nachfolgenden Objekten. Wir diskutieren doch den Entwurf in einem anderen Thread.

Was kann dieser Ansatz, das der andere nicht konnte?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Noch eine Anmerkung: der Unterstrich vor Variablennamen ist nur für Instanzvariablen gedacht. Also self._irgendwas. Um zu erklären, dass diese Variable/Attribut nicht zur öffentlichen Schnittstelle gehört.

Für lokale Variablen (wie bei dir _now) macht man das nicht. Im Gegenteil: es bedeutet, dass diese Variable eigentlich nicht genutzt wird. Was ja nicht stimmt.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich würde auch die Finger davon lassen Attribute zu löschen. Objekte die ein Attribut haben und dann plötzlich wieder nicht sind komisch. Was man an der Stelle machen würde, wäre das an den Wert `None` zu binden, anstatt den Attributnamen zu löschen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

__deets__ hat geschrieben: Sonntag 6. Januar 2019, 03:30 Nicht so gut, weil du stark koppelst zwischen deinem Window und der Logik zur Darstellung von Countdown & nachfolgenden Objekten. Wir diskutieren doch den Entwurf in einem anderen Thread.
Ich habe nun die Klasse `Countdown` entfernt und deren Logik in `GraphicsWindow` integriert, damit es keine Kopplung zwischen 2 Klassen mehr gibt. Ist der Code (siehe unten) jetzt besser?

__deets__ hat geschrieben: Sonntag 6. Januar 2019, 03:30 Was kann dieser Ansatz, das der andere nicht konnte?
Ich kann den Ablauf noch nachvollziehen. :P Für meinen Anwendungsfall "einfachen Countdown anzeigen und nach Ablauf eigentliches Programm starten" darf es gerne einfacher sein.

__deets__ hat geschrieben: Sonntag 6. Januar 2019, 13:07 Noch eine Anmerkung: der Unterstrich vor Variablennamen ist nur für Instanzvariablen gedacht. Also self._irgendwas. Um zu erklären, dass diese Variable/Attribut nicht zur öffentlichen Schnittstelle gehört.

Für lokale Variablen (wie bei dir _now) macht man das nicht. Im Gegenteil: es bedeutet, dass diese Variable eigentlich nicht genutzt wird. Was ja nicht stimmt.
Okay. Ist geändert. Meinst du mit "öffentlicher Schnittstelle" den Zugriff auf Variablen und Methoden einer Klasse von außerhalb der Klasse?

__blackjack__ hat geschrieben: Sonntag 6. Januar 2019, 13:38 Ich würde auch die Finger davon lassen Attribute zu löschen. Objekte die ein Attribut haben und dann plötzlich wieder nicht sind komisch. Was man an der Stelle machen würde, wäre das an den Wert `None` zu binden, anstatt den Attributnamen zu löschen.
Alles klar. Auch geändert.


Code:

Code: Alles auswählen

import time
import sys

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


class Rectangle:

    def __init__(self, scene):
        self.scene = scene
        path = QPainterPath()
        path.addRect(0,0,100,100)
        self._path = scene.addPath(path)
        self._path.setBrush(QtCore.Qt.red)


class GraphicsWindow(QGraphicsView):

    def __init__(self, parent=None):
        super(GraphicsWindow, self).__init__(parent)
        self.scene = QGraphicsScene(self)
        self.setScene(self.scene)
        self.scene.setSceneRect(-400, -400, 800, 800)
        self.INTERVAL = 5
        self._time = 0
        self._text = None

        self._start_countdown(self.INTERVAL, self._update_countdown)


    def _start_countdown(self, interval, f):
        self.last = time.monotonic()
        self._timer = QtCore.QTimer(self)
        self._timer.setInterval(interval)
        self._timer.timeout.connect(f)
        self._timer.start()


    def _update_countdown(self):
        now = time.monotonic()
        elapsed = now - self.last

        self._time += elapsed

        if self._text:
            self.scene.removeItem(self._text)
            self._time = self.INTERVAL - elapsed

        self._text = self.scene.addText(f"Noch {self._time:.2f} Sekunden")

        if self._time <= 0:
            self._timer.stop()
            self.scene.clear()
            self.start_countdown = None
            self._next_instruction()


    def _next_instruction(self):
        self.rectangle = Rectangle(self.scene)
        # Weitere Anweisungen


def main():
    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

Ja, Eigenschaften die ohne unterstrich sind gehören zum öffentlichen API der Klasse.

Du hast da ziemlich Kraut und Rüben mit was du öffentlich und was privat machst. Und self.INTERVAL zuzuweisen ist auch unglücklich - GROSsGESCHRIEBEN meint Konstante. Da weist du nichts zu.

Und was das Grundprinzip angeht: es ist dein Code. Ich finde das rumhantieren mit Timern und callbacks verquast, weil man es für jede Sache individuell durchsteigen muss, statt einmal zu verstehen, wie es geht, und das dann beliebig zu benutzen. Also wird das in meinen Augen auch nicht besser. Jetzt hast du halt eine Gottklasse, die alles macht. Natürlich ist das alles pillepalle bei so einem Beispieplrogramm. Aber wenn man es nicht im kleinen übt, endet man halt irgendwann mit einem fettigen Klumpen Spaghetti 🤷🏼‍♂️
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

__deets__ hat geschrieben: Montag 7. Januar 2019, 00:41 Aber wenn man es nicht im kleinen übt, endet man halt irgendwann mit einem fettigen Klumpen Spaghetti 🤷🏼‍♂️
Obwohl ich Spaghetti mit der richtigen Soße sehr gerne mag, habe ich den Debugger noch einmal bemüht. :mrgreen:

Gruß
Atalanttore
Antworten