Seite 1 von 1
QThread - Anzahl der Instanzen limitieren
Verfasst: Donnerstag 28. November 2019, 12:41
von sev
Hallo zusammen,
ich habe neuerdings das erste mal QThread verwendet um eine ProgressBar zu füllen, wenn ein mouseover-event von einem Button ausgelöst wird.
Mein Problem ist nun, dass theoretisch (wenn der Nutzer seine Maus wild über die Buttons fährt), so viele Threads erstellt werden, dass das Programm abschmiert.
Ich habe versucht herauszufinden, wie ich die Anzahl an Instanzen eines QThreads limitieren kann, oder einen anderen Thread beenden kann, wenn ein neuer erstellt wird, aber leider ohne Erfolg.
Wie kann ich laufende Threads beenden oder die maximal Anzahl an parallel laufenden Threads begrenzen?
Hier ein Auszug:
Code: Alles auswählen
#Thread
class External(QThread):
#Die Anzahl an laufenden Threads begrenzen?
countChanged = pyqtSignal(int)
finished = pyqtSignal()
def run(self):
count = 0
while count < 100:
count += 1
time.sleep(0.005)
self.countChanged.emit(count)
self.finished.emit()
#EventFitler für den mouseover Effekt
def eventFilter(self, object, event):
if event.type() == QEvent.Enter:
#Den alten Thread hier beenden?
self.calc = External()
self.calc.start()
self.calc.countChanged.connect(self.onCountChanged)
self.calc.finished.connect(lambda: self.onFinish(object))
return True
return False
def onCountChanged(self, value):
self.progressBar.setValue(value)
def onFinish(self, object):
self.progressBar.setValue(0)
.
.
.
if __name__ == '__main__':
app = QApplication(sys.argv)
MainPage().show()
sys.exit(app.exec_())
`
Danke und Gruß,
Sev
Re: QThread - Anzahl der Instanzen limitieren
Verfasst: Donnerstag 28. November 2019, 12:54
von __blackjack__
@sev: Du könntest beispielsweise einfach keinen neuen Thread starten wenn schon einer existiert. Dazu vorher prüfen ob `self.calc` an `None` gebunden ist und bei `onFinish()` das Attribut auf `None` setzen.
Warum erstellst Du selbst ein `finished`-Signal und sendest das selbst aus? Was gefällt Dir an `QThread.finished` nicht?
Die ``while``-Schleife in `run()` ist eigentlich eine ``for``-Schleife. Und zumindest für dieses Beispiel braucht man keine(n) Thread(s), da tut's auch ein `QTimer`.
Re: QThread - Anzahl der Instanzen limitieren
Verfasst: Donnerstag 28. November 2019, 12:56
von __deets__
Statt alte Threads zu beenden bevor man neue startet, macht man das ganz einfach so, dass man einen (oder mehrere, wenn das denn sinnvoll ist) Threads startet, die jeweils eine Queue mit Arbeitsauftraegen haben. Das kann man explizit machen, oder wenn man die Mechanismen von Qt hier korrekt einsetzt implizit durch die sogenannten "queued connections" mit abfruehstuecken. Wenn der Thread gerade nichts zu tun haben sollte, macht das nichts - der wartet dann rum und kostet (fast) nichts.
Re: QThread - Anzahl der Instanzen limitieren
Verfasst: Donnerstag 28. November 2019, 13:58
von sev
Hallo blackjack,
ich hab das QThread.finished eingefügt, funktioniert super. Danke für den Hinweis.
Wie genau kann ich denn prüfen ob self.calc None ist, wenn self.calc noch gar nicht erstellt wurde?
Code: Alles auswählen
class External(QThread):
countChanged = pyqtSignal(int)
def run(self):
count = 0
while count < 100:
count += 1
time.sleep(0.005)
self.countChanged.emit(count)
#EventFitler für den mouseover Effekt
def eventFilter(self, object, event):
if event.type() == QEvent.Enter:
self.calc = External()
self.calc.start()
self.calc.countChanged.connect(self.onCountChanged)
self.calc.finished.connect(lambda: self.onFinish(object))
return True
return False
def onCountChanged(self, value):
self.progressBar.setValue(value)
def onFinish(self, object):
self.calc = None
self.progressBar.setValue(0)
.
.
.
if __name__ == '__main__':
app = QApplication(sys.argv)
MainPage().show()
sys.exit(app.exec_())
Re: QThread - Anzahl der Instanzen limitieren
Verfasst: Donnerstag 28. November 2019, 14:06
von __deets__
Um sowas pruefen zu koennen soll man eben jedes Attribut einer Klasse schon im Konstruktor anlegen. Dann muss man da nicht komisch drumrumarbeiten.
Aber nochmal: man macht das nicht so. Man legt einfach IMMER einen Thread schon am Anfang an, und wenn es was zu tun gibt, schubst man dem das rueber. Das loest dann so ein Problem von "ist der schon gestartet oder nicht?!?" gleich mit.
Re: QThread - Anzahl der Instanzen limitieren
Verfasst: Donnerstag 28. November 2019, 14:23
von sev
Ich hab den Thread jetzt an den Anfang gelegt.
Ein Problem habe ich nun aber noch.
Momentan muss ein Thread immer erst fertig sein, wenn ich einen neuen aufmache, sprich wenn der Nutzer schnell über Button 1 und dann auf Button 2 mit der Maus geht, wird nur der Thread von Button 1 abgearbeitet.
Wenn ich aber mehrere Threads gleichzeitig zulasse, werden die Threads von Button 1 und Button 2 gleichzeitig ausgeführt und der Wert von der ProgressBar springt immer zwischen den ausgaben der beiden laufenden Threads.
Eigentlich möchte ich in diesem Beispiel, dass der Thread von Button 1 stoppt, wenn der Nutzer auf Button zwei fährt, bevor der Thread von Button 1 fertig ist.
Mit einer queue oder ähnlichem kann ich das doch nicht erreichen, da ja alle Threads nacheinander abgearbeitet werden?
Re: QThread - Anzahl der Instanzen limitieren
Verfasst: Donnerstag 28. November 2019, 14:30
von __deets__
Sowas geht nicht so ohne weiteres. Du kannst Threads nicht stoppen. Du kannst bestenfalls die anfallende Arbeit in lauter kleine Haeppchen teilen, und die nacheinander abfackeln. Und dann die Haeppchen (oder den einen Happen) von Button 2 priorisiert ausfuehren. Alternativ kann die Aufgabe in Thread 1 permanent pruefen, ob ein angelegter 2ter Thread gerade etwas tut, oder nur wartet. Das ist im Grunde semantisch dann dasselbe wie kleine Haeppchen.
Aber warum soll das so sein? Warum kann das nicht nacheinander abgearbeitet werden? Warum ist Button 2 wichtiger als Button 1?
Re: QThread - Anzahl der Instanzen limitieren
Verfasst: Donnerstag 28. November 2019, 14:39
von sev
Da der Thread nicht anderes macht als eine (immer die selbe) ProgressBar von 0 auf 100 aufzufüllen, ist es problematisch, wenn ich zwei Threads am laufen habe, die auf die gleiche ProgressBar zugreifen.
Thread 1 gibt value = 30 weiter --> ProgressBar springt auf 30%
Thread 2 startet und gibt value = 1 weiter --> ProgressBar springt auf 1%
Thread 1 ist wieder dran und gibt value = 31 weiter --> ProgressBar springt auf 31%
Thread 2 ist dran und gibt value = 2 weiter --> ProgressBar springt auf 2%
Immer wenn der Nutzer über einen neuen Button fährt, sollte die ProgressBar eigentlich zurückgesetzt werden und sich langsam von 0 auf 100 auffüllen.
Erst wenn die ProgressBar 100 erreicht, wird eine Funktion ausgelöst, daher sollte die ProgressBar auch immer wieder zurückgesetzt werden wenn sich ein Nutzer "umentscheided".
Ich hoffe das ist verständlich

Re: QThread - Anzahl der Instanzen limitieren
Verfasst: Donnerstag 28. November 2019, 14:43
von __deets__
Das Problem ist ja nur, das du die Gesamtheit der Aufgabe falsch berechnest. Wenn du eine neue Aufgabe hinzufuegst, dann hast du zwei Moeglichkeiten:
- du fuegst einen eigenen Progressbar hinzu. macOS zB macht das, wenn der Finder mehrere Kopierarbeiten oder so erledigt. Dann entsteht darunter ein zweiter, dritter etc. Fortschrittsbalken.
- du definierst die Gesamtheit der zu leistenden Arbeit dann eben als doppelt so gross, und jeder Thread addiert einfach nur nach jedem Arbeitsschritt einen entsprechenden Wert. Nehmen wir mal an Thread 1 kopiert 20 Dateien, erledigt also immer 5% mit einem Schritt, und Thread zwei kopiert 4 Dateien, also 25% pro Schritt. Dann startet der erste Auftrag, und der Fortschrittsbalken geht von 0-100. Dann kommt der zweite hinzu, und jetzt muss der Balken von 0-200 gehen, und jeder Thread addiert jeweils seine 5 oder 25 auf die geleistete Arbeit drauf.
Re: QThread - Anzahl der Instanzen limitieren
Verfasst: Donnerstag 28. November 2019, 14:57
von sev
Mein Anwendungsbeispiel ist etwas anders.
Die Progress Bar stellt keinen wirklichen Arbeitsfortschritt dar, sondern soll nur eine sich aufbauende Verbindung darstellen.
Sprich wenn der Nutzer auf Button1 fährt, füllt sich eine ProgressBar die in der Mitte von zwei Bildern steht. Bild1 ist fest und Bild2 ändert sich je nachdem über welchen Button der Nutzer fährt.
Wenn der Nutzer also auf Button1 fährt, ändert sich Bild2 erst, wenn die ProgressBar 100 erreicht hat (voll ist). Wenn der Nutzer davor jedoch auf Button2 fährt, soll die ProgressBar in diesem Moment wieder von 0 beginnen.
Wenn ich das Limit der ProgressBar einfach nur auf 200 setzen würde und die Fortschritte der beiden Threads addiere, springt zwar die ProgessBar nicht mehr zwischen verschiedenen Werten, aber somit kann ich auch kein "neu aufbauen" der Verbindung darstellen.
Re: QThread - Anzahl der Instanzen limitieren
Verfasst: Donnerstag 28. November 2019, 14:59
von __deets__
Dann benutzt du die falsche visuelle Abstraktion fuer die Darstellung. Dann musst du eben pro Verbindung etwas darstellen, wenn's sein muss einen Progressbar - aber der kann nunmal nicht schluessig zwei Dinge die parallel vorgehen darstellen, ausser sie lassen sich eben so kombinieren wie von mir beschrieben.
Re: QThread - Anzahl der Instanzen limitieren
Verfasst: Donnerstag 28. November 2019, 15:03
von sev
Ok, danke für deine Hilfe.
Re: QThread - Anzahl der Instanzen limitieren
Verfasst: Donnerstag 28. November 2019, 15:30
von __deets__
Noch ein Nachtrag: ich halte dein Vorgehen immer noch nicht fuer ideal, aber dein Problem kommt ja eher daher, dass du direkt aus dem Thread den Progressbar aenderst. Das ist halt falsch. Du musst abhaengig vom Button eben die Bilder und den aktuellen Fortschritt darstellen. Um das dann auch noch dynamisch zu machen (also den Balken zu bewegen waehrend man ueber dem entsprechenden Button schwebt) sollte die Abfrage des Ist-Zustandes einfach mit einem Timer erfolgen. Dadurch ist es dann auch einfach, das sauber zu wechseln, denn wenn man mit dem Hover/Enter/wasauchimmer Event den Bereich wechselt, kann der Timer gar nicht zuschlagen, weil man gerade ja ein Event verarbeitet. Und dann hievt man einfach alles um. Was *nicht* geht, ist die Threads die Signale senden zu lassen und die umzuhaengen. Theoretisch geht das, aber weil das Signal wirklich asynchron durch den Thread kommt, kann es sein, das man dann glitches bekommt, weil waehrend des umkonfigurierens ein Event gekommen ist. Das will man ja nicht.
Re: QThread - Anzahl der Instanzen limitieren
Verfasst: Donnerstag 28. November 2019, 15:50
von sparrow
Soll der erste laufende Thread eigentlich ohne Ergebnis beendet werden, wenn der zweite gestartet wird?
Re: QThread - Anzahl der Instanzen limitieren
Verfasst: Freitag 29. November 2019, 08:46
von sev
@deets: ja ich denke du hast recht, Threads scheinen hier nicht der richtige bzw. schöne weg zu sein.
@sparrow: Ja, damit müsste man jedenfalls einen Reset simulieren können.