Seite 1 von 2
Mainthread sinnvoll ausnutzen
Verfasst: 19. Oktober 2009 14:43
von olebole
Hallo,
ich habe (wie schon früher hier erläutert) das Problem, dass ich häufig ziemlich umfangreiche (und daher langdauernde) Änderungen an GUI-Elementen vornehmen muss: insbesondere muss z.B. mit der Verschiebung eines Mauscursors ein Bild aus 100.000 Elementen angepasst werden.
Wenn ich das nun in der "konventionellen" Methode mache, also die Mausbewegungen direkt ein Neuzeichnen des Bildes kopple, dauert das viel zu lange (einige Sekunden); die Maus ruckelt und auch die anderen Oberflächenelemente erhalten keine Updates, bis das Bild neugezeichnet ist.
In einen eigenen Thread auslagern kann ich das Bild offenbar auch nicht, da (selbst wenn die Berechnung in einer QGraphicsScene erfolgt, die gar nicht unmittelbar mit der Oberfläche verbunden ist und auch nicht aus QObjects besteht) das Programm dann abstürzt. Also muss ich das Neuzeichnen "irgendwie" aufteilen, damit andere Oberflächenänderungen wie die Verschiebung des Mauscursors auch mal zum Zuge kommen.
Die Frage ist nur: wie mache ich das am besten? Woran sehe ich an einer Stelle im Thread, dass da auch andere "in der Warteschlange" sind und wie kann ich die dann "vorlassen"? Oder ist mein Ansatz generell falsch? Welches wäre dann der richtige?
Viele Grüße
olebole
Verfasst: 19. Oktober 2009 15:27
von Curtis Newton
Ich würde schauen, dass ich nicht 100000 Elemente anpasse. Wie will man die dann aufm Monitor sehen. Und ändern die sich wirklich alle?
Und bist Du Dir sicher, dass Du eine Szene nicht im einen Thread ändern kannst. Natürlich darfst Du sie nicht mit einem View verknüpfen. Hast Du mal probiert, zwei Scenes zu nehmen: in eines zu rendern, das andere darstellen. Und mit void QGraphicsView::setScene ( QGraphicsScene * scene ) zwischen beiden zu wechseln?
C.
Verfasst: 19. Oktober 2009 15:31
von olebole
Curtis Newton hat geschrieben:Ich würde schauen, dass ich nicht 100000 Elemente anpasse. Wie will man die dann aufm Monitor sehen. Und ändern die sich wirklich alle?
Das sind ca. 300x300 Elemente, die passen tatsächlich ohne weiteres auf den Schirm. Und die (bzw. deren Farben) ändern sich tatsächlich alle: es sind Kanäle eines speziellen Gerätes, deren farbliche Kodierung mit den (per Gui-Elementen: Slider, Mauszeiger usw. anpassbaren) Parametern automatisch anpassen sollen.
Verfasst: 19. Oktober 2009 15:36
von Curtis Newton
olebole hat geschrieben:Curtis Newton hat geschrieben:Ich würde schauen, dass ich nicht 100000 Elemente anpasse. Wie will man die dann aufm Monitor sehen. Und ändern die sich wirklich alle?
Das sind ca. 300x300 Elemente, die passen tatsächlich ohne weiteres auf den Schirm. Und die (bzw. deren Farben) ändern sich tatsächlich alle: es sind Kanäle eines speziellen Gerätes, deren farbliche Kodierung mit den (per Gui-Elementen: Slider, Mauszeiger usw. anpassbaren) Parametern automatisch anpassen sollen.
Und wie gross sind die?
C.
Verfasst: 19. Oktober 2009 15:40
von olebole
Curtis Newton hat geschrieben:Und wie gross sind die?
Jedes ist einige Pixel groß. Minimal eins, maximal wohl so um die 10x10 Pixel (dann sind natürlich nicht mehr alle sichtbar). Aber die Oberfläche soll ja unabhängig davon gut bedienbar bleiben, selbst wenn später noch einige "Bosheiten" dazukommen sollten (wie zusätzliche Items, transparente Grafiken im Vorder- und Hintergrund usw.)
Verfasst: 19. Oktober 2009 16:23
von AuE
Mach doch einfach ein runnable. Dieses runnable kannst du ja auf deine bisherige "zeichen"fkt ansetzen. wenn du fertig berechnet hast emittierst ein signal (qued connection!!!!!) und dann wird das bildgezeichnet sobald die e-loop desmmaithreads wieder dran kommt!
Verfasst: 19. Oktober 2009 16:26
von olebole
AuE hat geschrieben:Mach doch einfach ein runnable. Dieses runnable kannst du ja auf deine bisherige "zeichen"fkt ansetzen.
Es ist ja bereits ein eigener Thread (das ist es doch, was Du mit "runnable" meinst?).
wenn du fertig berechnet hast emittierst ein signal (qued connection!!!!!) und dann wird das bildgezeichnet sobald die e-loop desmmaithreads wieder dran kommt!
Die Berechnung ist nicht das Problem, sondern das pure Updaten der 100.000 Items mit den Ergebnissen. Das dauert relativ lange, und in dieser Zeit kann die GUI eben nicht aktualisiert werden und "friert ein".
Verfasst: 19. Oktober 2009 16:31
von AuE
Also zunächst wäre in deinem Fall ein runnable perfomanter da du threads jedesmal neu anlegen musst (es sei denn du lässt ihn durchlaufen).
Ja mhhh aber kannst du das nicht mit in den Thread packen? Ich meine es macht ja keinen Sinn was was lange dauert dann nicht in den Thread zu packen

Auf die Objekte deiner GUI kannst aus dem Thread nicht zu greifen. Aber du kannst die ja ein Objekt bauen was du dann via Signal und qued connection versendest.
Oder?
Verfasst: 19. Oktober 2009 16:36
von olebole
AuE hat geschrieben:Also zunächst wäre in deinem Fall ein runnable perfomanter da du threads jedesmal neu anlegen musst (es sei denn du lässt ihn durchlaufen).
Er läuft durch und wird nur getriggert.
Auf die Objekte deiner GUI kannst aus dem Thread nicht zu greifen.
Eben. Ich habe 100.000 Objekte in meiner Oberfläche, die aktualisiert werden müssen. Die reine Aktualisierung dauert recht lange (einige Sekunden) und in dieser Zeit ist dann die Oberfläche eben eingefroren, was für den Nutzer unannehmbar ist.
Aber du kannst die ja ein Objekt bauen was du dann via Signal und qued connection versendest.
Oder?
100.000 Objektupdates einzeln per Signal verschicken? Das hört sich auch nicht gerade nach Performance an. Zumal das bei mir aus Python heraus passiert, was pro Aufruf einen deutlichen zusätzlichen Overhead gibt.
Verfasst: 20. Oktober 2009 09:02
von RavenIV
Kannst Du mir erklären, was alle an python soooo toll finden?
Ich musste ein Projekt übernehmen, wo die GUI in python mit PyQt programmiert ist. Alles zeitkritische ist mit C++ gemacht.
Mit dem python-Teil haben wir nur Probleme, vor allem mit der Anbindung an C++. Da muss man Handstände machen, damit man ein einigermassen vernünftiges Interface mit boost.python oder so bekommt.
Nach und nach soll der python-Code bei uns durch C++ ersetzt werden, was sich aber nicht immer so einfach gestaltet.
Ich würde Dir auch empfehlen, Dich von python zu verabschieden.
Verfasst: 20. Oktober 2009 09:25
von olebole
RavenIV hat geschrieben:Kannst Du mir erklären, was alle an python soooo toll finden?
Was "alle" daran finden, weiß ich nicht.
Ich habe für mein Projekt u.a. deshalb Python gewählt, weil:
- es eine einfache Einbindung von Scripten erlaubt und Python als Scriptsprache unter den künftigen Anwendern verbreitet ist
- es viele leistungsfähige portable Bibliotheken gibt (PyQt, numpy/scipy, matplotlib, ...)
- viele Spezialbibliotheken auf dem Fachgebiet des Projektes (Astrophysik) vorhanden sind
- Python eine Konzentration auf die fachlichen Probleme erlaubt, während man bei C++ einen nicht unerheblichen Teil des Codes für das Resource Management aufwenden muss.
Ich benötige keine Anbindung an C++, mal abgesehen von PyQt, daher ist Dein Kritikpunkt für mich unerheblich.
Verfasst: 20. Oktober 2009 09:36
von AuE
Aloswenn das ding mehrfach getriggert wird kostet auch das performance..... da du ja jedesmalwieder den Thread neu zum leben erweckst.
kannst du nicht deine Objekte in ein Oberobjekt zusammenfassen? Map, ListVector? Und dann raushauen?
Verfasst: 20. Oktober 2009 09:47
von olebole
AuE hat geschrieben:kannst du nicht deine Objekte in ein Oberobjekt zusammenfassen? Map, ListVector? Und dann raushauen?
Auch dann müsste ich die Objekte erstmal erzeugen, was die Zeit kostet.
Letztlich sieht die Stelle jetzt bei mir so aus:
Code: Alles auswählen
def update(self):
brushes = [ QtGui.QBrush(QtGui.QColor(c[0], c[1], c[2]))
for c, item in zip(self.colors, self.items) ]
self.emit(QtCore.SIGNAL('newbrushes(PyQt_PyObject)'), brushes)
# is connected to self.update_items
def update_items(self, brushes):
for brush, item in zip(brushes, self.items):
item.setBrush(brush)
self.emit(QtCore.SIGNAL('changed()'))
# is connected to scene.update
Das führt dazu, dass das (prozesszeitintensive) Erzeugen der Objekte in einem eigenen Thread liegt, während das setBrush() im Mainthread ausgeführt wird.
Trotzdem dauert der 100.000-fache Aufruf von setBrush() eine Weile und blockiert damit die Oberfläche eine Weile. Es ist zwar etwas besser als vorher, aber nicht gerade das, was man von einer guten, schnellen GUIO erwarten würde.
Verfasst: 20. Oktober 2009 09:54
von RHBaum
Die frage ist auch:
wie lange dauert das zeichnen einer kompletten Szene ?
grosser 1 Sek ?
wenn ja, aendert sich wirklich fast jedes QGraphicsItem ?
wenn ja, dann hasst eh nen Problem ^^ Dann kommst mit python, QT und co eh ned weiter, dann brauchst was performanteres, wenn deine Anforderungen überhaupt aufn PC zu realisieren sind.
Aendert sich nicht alles, schau das du fuers neuzeichnen ned den ganzen bereich nimmst, sondern nur den bereich wo sich wirklich was aendert ....
was heisst das paar verwaltungsebenen noch einziehen musst, die dir bestimmten was neu gezeichnet werden muss ...
Die QT und der Signal / slot mechanismus ist Klasse, aber auch nicht die Mutter der Performance. Von Python reden wir gar ned erst
Deine Anforderung die du hier rueberbringst, klingen so als waeren sie mit deinen Mitteln (python + QT) nicht durchführbar, zumindest nicht in der geforderten performance.
Schreib fuer deine "Szene" bzw deinen View nen abstraktes "interface", ohne dem QGrafics -Zeug. In deinem fall waer ne pure c-Schnittstelle sicher ned verkehrt. lager das in ne dll aus, und roedel mit der direkt auf der winapi um !
Kommst du ueber python eigentlich auch an die windowshandles ran ? bzw gibts da sowas wie das migration kit (commerzielle qt version) ?
Ciao ....
Verfasst: 20. Oktober 2009 10:02
von olebole
RHBaum hat geschrieben:wie lange dauert das zeichnen einer kompletten Szene ? grosser 1 Sek ?
Etwa 12 Sekunde.
wenn ja, aendert sich wirklich fast jedes QGraphicsItem ?
Ja.
wenn ja, dann hasst eh nen Problem ^^ Dann kommst mit python, QT und co eh ned weiter, dann brauchst was performanteres, wenn deine Anforderungen überhaupt aufn PC zu realisieren sind.
Nein, warum denn? Ich kann schon damit leben, dass das Bild eventuell auch mal verzögert dargestellt wird, wenn man denn seine Eigenschaften (mit Slidern oder mit dem Mauscursor) zu schnell ändert.
Mein Problem heißt nicht "wie bekomme ich [Py]Qt schneller", sondern "wie erreiche ich, dass die Oberfläche trotz komplexer Änderungen ansprechbar bleibt".
Schreib fuer deine "Szene" bzw deinen View nen abstraktes "interface", ohne dem QGrafics -Zeug. In deinem fall waer ne pure c-Schnittstelle sicher ned verkehrt. lager das in ne dll aus, und roedel mit der direkt auf der winapi um !
Ich habe gar kein Win. Ich habe Linux. Und es soll natürlich auch auf dem Mac laufen. Ja, und auch unter Windows.