Mainthread sinnvoll ausnutzen

Alles rund um die Programmierung mit Qt
olebole
Beiträge: 38
Registriert: 20. März 2009 16:39

Mainthread sinnvoll ausnutzen

Beitrag 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
Curtis Newton
Beiträge: 122
Registriert: 11. Juni 2008 18:39

Beitrag 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.
olebole
Beiträge: 38
Registriert: 20. März 2009 16:39

Beitrag 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.
Curtis Newton
Beiträge: 122
Registriert: 11. Juni 2008 18:39

Beitrag 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.
olebole
Beiträge: 38
Registriert: 20. März 2009 16:39

Beitrag 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.)
AuE
Beiträge: 918
Registriert: 5. August 2008 10:58

Beitrag 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!
olebole
Beiträge: 38
Registriert: 20. März 2009 16:39

Beitrag 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".
AuE
Beiträge: 918
Registriert: 5. August 2008 10:58

Beitrag 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?
olebole
Beiträge: 38
Registriert: 20. März 2009 16:39

Beitrag 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.
RavenIV
Beiträge: 267
Registriert: 21. Januar 2009 14:24
Wohnort: Waldshut

Beitrag 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.
Linux, das längste Text-Adventure aller Zeiten
olebole
Beiträge: 38
Registriert: 20. März 2009 16:39

Beitrag 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.
AuE
Beiträge: 918
Registriert: 5. August 2008 10:58

Beitrag 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?
olebole
Beiträge: 38
Registriert: 20. März 2009 16:39

Beitrag 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.
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag 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 ....
olebole
Beiträge: 38
Registriert: 20. März 2009 16:39

Beitrag 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.
Antworten