Hallo zusammen, ich habe mir mit Qt eine GUI Applikation geschrieben, die auf einem Industriedisplay läuft. Es gibt eine Übersichtsseite, auf der mehrere physikalischen Größen dargestellt werden. Teilweise nur als Zahlenwert, teilweise mit Diagrammen und verschiedenen Icons/grafiken. Zusätzlich gibt es noch ein paar Softkeys, um Dinge ein/ausschalten zu können. Außerdem gibt es noch ein Menü, in dem man beispielsweise die Sprache umstellen kann. Insgesamt sind es 8 Seiten und diese sind als Widget in einem Stackedwidget abgelegt. Ein Neuzeichnen der Seite erfolgt über update(), wodurch paintEvent() aufgerufen wird. Dies erfolgt alle 50 ms. Bis hier her läuft alles prima. Nun kam aber die Anforderung auf, dass ein Neuzeichnen alle 20 ms erfolgen soll( das Projekt läuft im Rahmen des Studiums ). Ziel ist es nun, dass nur gewisse Teile eines Widgets neu gezeichnet werden. Und da fängt mein Problem an. Sobald paintEvent() aufgerufen wird, wird ja der komplette Screen gelöscht. Ist mein bisheriger Ansatz überhaupt ok mit dem Stackedwidget oder muss ich das Konzept total überarbeiten? Über einen kleinen Denkanstoß wäre ich sehr dankbar. Gruß Markus
Das QPaintEvent in paintEvent auswerten und nur diesen Teil neu zeichnen. QPainter::clipRect wäre ein erster Schritt. Dann mit einem QTimer von dem Fenster update(rect) aufrufen wobei rect das entsprechende Rechteck ist. Dieses Rechteck kommt dann bei paintEvent im QPaintEvent an.
Guest schrieb: > Das QPaintEvent in paintEvent auswerten und nur diesen Teil neu > zeichnen. > QPainter::clipRect wäre ein erster Schritt. > > Dann mit einem QTimer von dem Fenster update(rect) aufrufen wobei rect > das entsprechende Rechteck ist. Dieses Rechteck kommt dann bei > paintEvent im QPaintEvent an. Mir kam gerade ein anderer Ansatz. Aktuell ist es ja so, dass jede Seite ein eigenes Widget ist. Auf einer Seite sind halt mehrere Texte, Icosn, Diagramme aber an sich in verschiedene Bereiche oder Container aufgeteilt. Wäre es ein Ansatz nicht jede Seite als Widget zu speichern, sondern jeden Bereich/Container? Aber hier habe ich doch auch wieder das Problem, genau wie bei deinem Vorgehen, dass durch den Aufruf von paintEvent() alles gelöscht wird?!
Markus schrieb: > Nun kam aber die Anforderung auf, dass ein Neuzeichnen alle 20 ms > erfolgen soll Und was soll das bringen? Wer soll das noch visuell erfassen können? Ändern sich die dargestellten Daten so hektisch, oder geht es nur um einen reinen Performance-Test, wie schnell die Zeichenroutinen sind? Üblicherweise werden Displays mit 60 Hz Auffrischungsrate angesteuert - es gibt Ausnahmen (alte Röhrenkübel arbeiten humanerweise mit höheren Bildwechselfrequenzen, da sie sonst flackern, und in manchen Kinderzimmern finden sich schnellere Displays am Spiel-PC), aber ca. 60 Hz sind sehr weit verbreitet. Was also soll eine Änderung der Bildschirminhalte mit 50 Hz bringen? Sinnvollerweise lässt man Bildelemente sich erst dann neu zeichnen, wenn es nötig ist, also wenn sich ihr Inhalt geändert hat.
:
Bearbeitet durch User
Rufus Τ. F. schrieb: > Und was soll das bringen? Wer soll das noch visuell erfassen können? Wie gesagt, es handelt sich um ein Projekt an der Uni. Ziel ist es halt, nur Teilbereiche neu zu zeichnen und nicht immer den kompletten Screen.
Markus schrieb: > Wie gesagt, es handelt sich um ein Projekt an der Uni. Und dann ist es nicht nötig, über den Sinn von Vorgaben nachzudenken? Früher haben wir anders studiert.
Eventuell stört hier jemanden das Geflackere auf dem Schirm wenn er 20 mal pro Sekunde zuerst gelöscht und dann neu beschrieben wird und dieser eine denkt, wenn er das alle 20ms macht ist das Flackern weg... Selbst mit dieser brachialen Methode ist das trotzdem relativ flackerfrei hinzukriegen, allerding nur mit Doppelbufferung.
Rufus Τ. F. schrieb: > Und dann ist es nicht nötig, über den Sinn von Vorgaben nachzudenken? Ich habe hier ein 5" Display liegen und mit dem aktuellen Konzept klappt das auch prima. Nur soll mein Konzept später auf ein deutlich größeres Display übertragen werden, wo auch deutlich mehr Werte und Grafiken angezeigt werden. Daher ist bei dem neuem Display auch sinnvoll nur teilbereiche neu zu zeichnen. Das ist der Hintergrund.
temp schrieb: > Eventuell stört hier jemanden das Geflackere auf dem Schirm wenn er 20 > mal pro Sekunde zuerst gelöscht und dann neu beschrieben wird und dieser > eine denkt, wenn er das alle 20ms macht ist das Flackern weg... > > Selbst mit dieser brachialen Methode ist das trotzdem relativ > flackerfrei hinzukriegen, allerding nur mit Doppelbufferung. Das Qt Malsystem funktioniert nicht so, dass das flackern könnte. Da musst du schon was sehr komisches machen um das überhaupt zu erreichen. Eine konstante Repaint-Rate ist unsinnig. Repaints sollten immer nur für Teilbereiche passieren wenn die tatsächlich neue Daten darstellen müssen. Du kannst an update() ein rect übergeben und nur das neu zeichnen in deinem paintEvent().
:
Bearbeitet durch User
Sven B. schrieb: > Du kannst an update() ein rect übergeben und nur das neu > zeichnen in deinem paintEvent(). Scheint nur ein Vorschlag zu sein. http://stackoverflow.com/questions/11707768/trying-to-update-only-a-rectangle-in-a-widget-in-qt-but-the-entire-widgets-are
schotter schrieb: > Sven B. schrieb: >> Du kannst an update() ein rect übergeben und nur das neu >> zeichnen in deinem paintEvent(). > > Scheint nur ein Vorschlag zu sein. > > http://stackoverflow.com/questions/11707768/trying-to-update-only-a-rectangle-in-a-widget-in-qt-but-the-entire-widgets-are Wie da im Kommentar beschrieben m.E. ein Bug an der Stelle. Es ist nur ein Hinweis, ja, aber wenn du das paintEvent() auch selber implementierst muss es möglich sein deinem eigenen Hinweis zu folgen.
Markus schrieb: > Daher ist bei dem neuem Display auch sinnvoll nur teilbereiche neu zu > zeichnen. Das ist der Hintergrund. Es ist immer sinnvoll, nur das neu zu zeichnen was sich auch geändert hat. Daraus ergibt sich aber noch nicht die Notwendigkeit dies alle 20 Millisekunden zu tun.
Mark B. schrieb: > Es ist immer sinnvoll, nur das neu zu zeichnen was sich auch geändert > hat. Daraus ergibt sich aber noch nicht die Notwendigkeit dies alle 20 > Millisekunden zu tun. Ok dann war die Zeitangabe quatsch. Wie gesagt es geht darum nur Teilbereiche neu zu zeichnen und nicht immer die ganze Seite. Schon einmal vielen dank für die bisherigen Hinweise!
Vielen Dank für eure Beiträge. Das mit dem teilweise Neuzeichnen klappt echt gut, aber jetzt muss ich mir noch ein gescheites Handling überlegen. Aktuelle vergleiche ich den gezeichneten Wert mit dem neuen Wert und wenn ein Unterschied vorliegt, dann wird neu gezeichnet. Recht primitiv und die Region, die neu gezeichnet werden soll gebe ich noch händisch im Code an. Mal schauen, wie ich das automatisiere.
Alle 20ms neuzeichnen? Das ist doch ein geflacker und ein Anwender sieht nichts. Einmal die Sekunde, das reicht für Menschen vollkommen aus. Weiterhin könnte man schauen, was sich geändert hat, und dann nur die geänderten Informationen neu Ausgeben. Das spart Rechenzeit.
PittyJ schrieb: > Alle 20ms neuzeichnen? Das ist doch ein geflacker und ein Anwender sieht > nichts. > Einmal die Sekunde, das reicht für Menschen vollkommen aus. Das ist doch für einen Graphen nun wirklich unsinnig. Dein Oszilloskop zeichnet doch seinen Bildschirm auch nicht nur einmal die Sekunde neu, da würde man ja wahnsinnig werden.
Markus schrieb: > Vielen Dank für eure Beiträge. Das mit dem teilweise Neuzeichnen klappt > echt gut, aber jetzt muss ich mir noch ein gescheites Handling > überlegen. Aktuelle vergleiche ich den gezeichneten Wert mit dem neuen > Wert und wenn ein Unterschied vorliegt, dann wird neu gezeichnet. Recht > primitiv und die Region, die neu gezeichnet werden soll gebe ich noch > händisch im Code an. Mal schauen, wie ich das automatisiere. Ich würde es für sinnvoller erachten, die einzelnen Elemente zu eigenen Widgets zu machen. Das ist dann eine sauberere Aufteilung. Und wenn sich ein Wert geändert hat, kriegt das Widget ein Signal mit dem neuen Wert und zeichnet sich daraufhin neu. Sven B. schrieb: > PittyJ schrieb: >> Alle 20ms neuzeichnen? Das ist doch ein geflacker und ein Anwender sieht >> nichts. >> Einmal die Sekunde, das reicht für Menschen vollkommen aus. > > Das ist doch für einen Graphen nun wirklich unsinnig. Dein Oszilloskop > zeichnet doch seinen Bildschirm auch nicht nur einmal die Sekunde neu, > da würde man ja wahnsinnig werden. Und auch der Zeiger vom Tacho ändert die Position der Nadel mehr als einmal pro Sekunde, auch wenn er auf einem Display dargestellt wird.
Es gibt doch garantiert auch für Qt eine empfohlene Vorgehensweise und Codebeispiele wie man owner-drawn Controls implementiert, was wann welche Signale auslöst und welche Methoden man implementieren oder auf welche Signale man reagieren muss (und wie man das macht). Üblicherweise (bei allen GUI Toolkits die ich kenne) ist das doch so daß solange sich nichts ändert wird auch kein Paint Event ausgelöst und wenn sich was ändert wird irgendwo tief im Framework ein Flag gesetzt bzw. ein rechteckiger Bereich für ungültig erklärt. Wenn dann die Event-Loop das nächste Mal Zeit hat ruft diese die Paint-methode auf und die darf dann anhand der Rechteckkoordinaten entscheiden was neu gemalt werden muss und das dann neu malen. Und damit man nicht jedesmal mühevoll aus den Rechteckkoordinaten entscheiden muss welcher Bereich das sein soll implementiert man am besten jeden unabhängig zu ändernden Teil als eigenes Widget mit eigener Paint Methode, dann wird nur das geänderte Widget invalidiert, die anderen nicht, und nur die jeweils zuständige Paint methode wird aufgerufen und man spart sich das Rumgefummel mit den Rechtecken. Aber es ist stets die Eventloop die entscheidet wann das geschieht, niemals die Anwendung. Die Anwendung kann nur InvalidateRect() - je nach Tooolkit auch andere Namen, bei Qt heißt sie update() - aufrufen, das kann sie auch tausend Mal in der Sekunde machen wenn sie lustig ist, jedoch neu gezeichnet wird nur dann wenn die Event-Loop Zeit hat. Ich bin mir sicher daß das bei Qt im wesentlichen im gleichen Sinne funktioniert. https://doc.qt.io/qt-5/qwidget.html#update Für das Beispiel mit dem Tacho würdest Du also den neuen Wert in eine Variable schreiben und dann das ganze Tacho-Widget mit update() invalidieren. Das malt noch nichts und kehrt sofort zurück, das kannst Du zigtausend Mal in der Sekunde machen, das setzt nur ein Flag. Die Eventloop sorgt dann dafür dass bei der nächtstmöglichen Gelegenheit die paintEvent() methode des Tachos aufgerufen wird, in der sollst Du dann den Zeiger neu malen, basierend auf dem Wert der gerade in der entsprechenden Variablen steht.
:
Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.