Eines vorweg: Deine Vorlage sollte für mich nicht als Lösung dienen. Ich versuchte und versuche immer noch innerlich bis in die kleinste Ecke zu verstehen, was hier passiert. Es bringt mir nichts, wenn ich etwas eins zu eins übernehme. Ich möchte Python lernen und verstehen. Dazu dienen dann Vorlagen für die Analyse sehr hervorragend. Denn das in Worten geschriebene Text wirkt auf mich zu trocken und zu abstrakt. Für Profis ist das natürlich alles offensichtlich und klar nachvollziehbar. Nun habe ich eine gute Überleitung zu meiner Verwirrtheit gebracht. Du redest von Umformung in eine ereignisgesteuerte Variante. Den Satz kann ich lesen, verstehe jedes Wort. Aber was genau meinst du mit Umformung? Kannst du mir dazu bitte ein pseudo-Programm schreiben. Zum Beispiel in diesem Stil "Eigentlich würde man das so machen, aber dann würde man umformen". So das man eine Art »Vorher und Nachher«-Bild hat. Ich sitze nämlich hier und denke mir "Ähm, ja, und?"jerch hat geschrieben:Naja, ich hatte den Code jetzt nicht als Vorlage zur Lösung Deines Problems gedacht, aber das geht natürlich damit. Wie Du richtig bemerkt hast, arbeitet die Task-Klasse wiederum mit der `run`-Methode und zusätzlich mit dem händischen Aufruf des Eventdisptachers (dieses `processEvents`), was die Umformung des Problems in eine ereignisgesteuerte Variante erspart. Ich denke allerdings, dass Du wenigstens die Umformung mal gemacht haben solltest, um selbst abschätzen zu können, was wann besser geeignet ist.
Das verstehe ich nicht. Wenn die »QApplication()«-Klasse von der »QCoreApplication« ableitet, dann ist es doch Jacke wie Hose, ob ich nun die »QApplicationen()«-Klasse in meinem Beispiel-Programm verwende. Denn in meinem Mini-Projekt arbeite ich (natürlich einmal) mit der »QApplication()«-Klasse. Daher verstehe ich nun nicht, was daran weniger sinnvoll sein soll? Da bin ich leider nicht ganz mitgekommen.jerch hat geschrieben:QApplication leitet von QCoreApplication ab, daher ist die Umstellung nicht nötig und eigentlich auch nicht sinnvoll, da es für die TaskThread und Task-Klasse die Abhängigkeit zu den Qt GUI-Klassen einführt.
Habe ich schon - also getestet. Würde ich die Instanzt der »WorkObject()«-Klasse lokal in der »on_start()«Methode erzeugen, so würde das Programm nicht einmal anlaufen. Es sei denn, ich setze am Ende der »on_start()«-Methode ein eine »print«-Anweisung, dann beginnt die Arbeit. Daher bin ich dazu übergegangen, und habe die Instanz der »WorkObject« dialogweit erzeugt. Eine andere und bessere Lösung fiel mir nicht ein.jerch hat geschrieben:Genau. Versuch doch mal diese simple Zählschleife in eine ereignisgesteuerte Version mittels QTimer zu überführen, dann wird Dir das klarer:Ich möchte nicht mit der While-Schleife arbeiten, weil sie für mich wesentlich langsamer ist als die »xrange()«-Methode. Aber bei deiner Aufgabenstellung komme ich zum gleichen Ergebnis. Hier das Ergebnis:Code: Alles auswählen
def endless_counter(): c = 0 while True: # do something c += 1
In der »create_items()«-Funktion sollen später in meinem Projekt die Datensätze aus der Datenbank gelesen werden. Daher auch diesen Generator. Man will ja mit dem Arbeitsspeicher und mit den Ressourcen des Rechners sparsam umgehen. Damit der Generator überhaupt entsprechend angesprochen werden kann, nehme ich hier die »output_items()«-Funktion. So arbeitet man mit Generatoren, richtig? In der »main()«-Funktion erzeuge ich eine Instanz der »QAppliaction()«-Klasse. Anschließend erzeuge ich eine weitere Instanz der »QTimer()«-Klasse. Damit der »QTimer« bei jedem sogenannten »timeout()« etwas auslösen soll, wird die »output_items()«Funktion zum Inhalt der »timeout()«-Methode. Damit hätte ich eine Verbindung hergestellt. Und damit der »QTimer« auch sofort mit der Arbeit beginnt, wird der »start()«-Methode der »QTimer()«-Klasse einfach die Zalh Null übergeben. Und damit das Programm nicht sofort abbricht, wird auf der Instanz der »QApplication()«-Klasse die »exec_()«-Methode ausgeführt. So würde ich meine Aufgabe lösen. Du siehst. Im Grunde finde ich bei mir keinen anderen Weg. Eigentlich der Gleiche Weg wie in all meinen anderen Beispielen.Code: Alles auswählen
import sys from PyQt4.QtCore import QTimer from PyQt4.QtGui import QApplication def create_items(total): for item in xrange(total): yield item def output_items(): for element in create_items(10): print "element", element def main(): app = QApplication(sys.argv) app.setQuitOnLastWindowClosed(False) create_timer() timer = QTimer() timer.timeout.connect(output_items) timer.start(0) # run event loop so python doesn't exit app.exec_() if __name__ == "__main__": main()
Mir ist dieser Fehler nicht aufgefallen. Verwundert aber auch die wenigsten Ich hatte mich allerdings gefragt, wieso du die »init()«-Methode angewendet hast? Ich hätte die Variable in der »run()«-Methode auf True gesetzt und wäre damit fertig.jerch hat geschrieben: Die Ableitung war nötig, um die Task-Funktionalität nach aussen hin zu kapseln, sodass man nur noch ein Task-Exemplar erstellen muss, ggf. Slots an Signale hängt und mit `.start` die Sache ohne weiteres Zutun läuft. Auch hab ich das Ding nur schnell zusammengeschrieben. So finde ich die zusätzliche `init`-Funktion unglücklich - sie versucht, ein Stück weit der Threadaffinität von QObjects entgegen zu kommen, ist aber nach wie vor simpel aushebelbar. Auch fehlt bei der Exception der Fehlertyp. Das sind Sachen, die einem auffallen, wenn man ein zweiten Mal drüberschaut.
Generell kannst Du beliebig viele Objekte in einen Thread verschieben und dort nutzen, jepp.
Mein Blick würde jetzt sagen: "Ähm, ja... virtuelle Methode... was zum Geier....?"Sophus hat geschrieben: Das mit der GUI verstehe ich nicht, ich nutze im Bsp. nix GUI-haftes. Für eine schöne API-Spec fehlt der Task-Klasse eine virtuelle `run`-Methode als Platzhalter.
verbunden. Das `moveToThread` geht beim 2. Mal baden ausbesagten Gründen, einfach mal testen.jerch hat geschrieben: Ungünstig an dem Vorgehen ist zum einen, dass die dialogweite Verfügbarkeit suggeriert, dass dieses Objekt ein Kind des Dialoges ist und entsprechend darauf zugegriffen werden kann. Stimmt aber nicht, denn sobald das Objekt im Thread ist, ist das mit großen Einschränkung
Damit du nicht groß suchen musst, hier das ältere Beispiel: Nimm mal die »Print«-Anweisung in Zeile 119 weg, du wirst sehen. Keine Reaktion. Das war auch der Grund, weshalb ich die die Instanz der »WorkObject«-Klasse dialogweit erzeugt habe. Ich gebe zu, das ist keineswegs eine elegante Lösung. Nur damit du meinen Gang nachvollziehen kannst.
Code: Alles auswählen
import sys
from time import sleep
from PyQt4.QtCore import QThread, pyqtSignal, Qt, QStringList, QObject, QTimer
from PyQt4.QtGui import QVBoxLayout, QPushButton, QDialog, QProgressBar, QApplication, \
QMessageBox, QTreeWidget, QTreeWidgetItem, QLabel
def create_items(total):
for elem in range(total):
yield elem
class WorkObject(QObject):
notify_progress = pyqtSignal(object)
fire_label = pyqtSignal(object)
finished = pyqtSignal()
def __init__(self, parent=None):
QObject.__init__(self, parent)
def add_items(self):
total = 190000
x = (total/100*1)
a = x
counter = 0
for element in create_items(10000):
counter += 1
self.notify_progress.emit((element))
self.fire_label.emit((counter))
if counter == x:
x += a
sleep(1)
if not self.keep_running:
self.keep_running = True
break
def run(self):
self.keep_running = True
self.add_items()
def stop(self):
self.keep_running = False
class MyCustomDialog(QDialog):
finish = pyqtSignal()
def __init__(self, parent=None):
QDialog.__init__(self, parent)
layout = QVBoxLayout(self)
self.tree = QTreeWidget(self)
self.label = QLabel(self)
self.pushButton_start = QPushButton("Start", self)
self.pushButton_stopp = QPushButton("Stopp", self)
self.pushButton_close = QPushButton("Close", self)
layout.addWidget(self.label)
layout.addWidget(self.tree)
layout.addWidget(self.pushButton_start)
layout.addWidget(self.pushButton_stopp)
layout.addWidget(self.pushButton_close)
self.pushButton_start.clicked.connect(self.on_start)
self.pushButton_stopp.clicked.connect(self.on_finish)
self.pushButton_close.clicked.connect(self.close)
def fill_tree_widget(self, i):
parent = QTreeWidgetItem(self.tree)
self.tree.addTopLevelItem(parent)
parent.setText(0, unicode(i))
parent.setCheckState(0, Qt.Unchecked)
parent.setFlags(parent.flags() | Qt.ItemIsTristate | Qt.ItemIsUserCheckable)
def on_label(self, i):
self.label.setText("Result: {}".format(i))
def on_start(self):
self.tree.clear()
self.label.clear()
task_thread = QThread(self)
work_object = WorkObject()
work_object.fire_label.connect(self.on_label)
work_object.notify_progress.connect(self.fill_tree_widget)
work_object.finished.connect(task_thread.deleteLater)
self.finish.connect(work_object.stop)
work_object.moveToThread(task_thread)
task_thread.started.connect(work_object.run)
timer = QTimer()
# I set the single shot timer on False,
# because I don't want the timer to fires only once,
# it should fires every interval milliseconds
timer.setSingleShot(False)
timer.setInterval(7000)
timer.timeout.connect(work_object.stop)
timer.start()
task_thread.start()
print
def on_finish(self):
self.finish.emit()
def main():
app = QApplication(sys.argv)
window = MyCustomDialog()
window.resize(600, 400)
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()