Seite 1 von 2
Zeichenroutine optimieren
Verfasst: 3. April 2011 22:44
von marvel
hallo,
ich verwende ein Tabwidget mit mehreren Reitern. Einer der Reiter stellt eine Map dar. Diese wird jedesmal neu gezeichnet sobald sich etwas an der Map verändert. Leider wird das paintEvent auch immer dann ausgelöst wenn ich die Reiter wechsel. Dies ist jedoch nicht nötig, weil dich an der Kartendarstellung nichts ändert. Kann ich das irgendwie umgehen bzw dafür sorgen das in diesem Fall das paintEvent nicht ausgelöst wird ?
Verfasst: 4. April 2011 09:13
von upsala
Ohne paintEvent gibt es gar keine Darstellung. Bau dir ein QPixmap als Puffer ein.
Verfasst: 9. Mai 2011 03:33
von marvel
Hallo nochmal,
ich weiß der Beitrag ist schon etwas älter, jedoch sitze ich seit kurzem wieder an meiner App. Ich konnte das mit der QPixmap nicht ganz nachvollziehen, meinst Du ich soll das Widget zwischenspeichern ? Für eine Erläuterung wäre ich dankbar.
Darüberhinaus habe ich noch andere Probleme...
Ich habe ein QMainWindow mit einem QTabWidget. In das QTabWidget schmeisse ich mehrere QWidgets rein. in einem der QWidgets benutze ich die QPaintEvent-Methode. Das QWidget besitzt zusätzlich einen QPushButton. Der QPushbutton ist mit keinem Slot verbunden, er wurde lediglich einmal durch die setupUI() Methode ins Leben gerufen. Jetzt verhält sich der Button etwas mysteriös, wenn ich auf ihn klicke dann löst er direkt zweimal die PaintEvent-Methode aus. Ich dachte eigentlich das die PaintEvent Methode erst bei Veränderung ausgelöst wird, doch scheinbar reicht schon ein Klick auf einen leblosen Button aus. Wäre nur die Frage zu klären warum er direkt zweimal auslöst anstatt einmal ?
Das einzige was ich mir vorstellen kann ist das zwischen press & release Button unterschieden wird und so zweimal die PaintEvent Methode aufgerufen wird.
Verfasst: 9. Mai 2011 08:24
von franzf
Was Upsala wohl indirekt annahm: Du willst das paintEvent unterdrücken, weil das Zeichnen lange dauert und dadurch deine Anwendung beim Tabwechsel laggt. Ein Pixmap ist schnell gezeichnet, deshalb an geeigneten Stellen (resizeEvent, zoom, ...) auf ein Pixmap malen und dieses dann im paintevent malen.
Sollte dein Wunsch nicht aus einem Performanceproblem entstanden sein: lass es, mach dir keine Gedanken, ist halt so.
Zu deinem neuen Problem: Liegt der Button auf dem Widget? Malt der nen Holo oder anderen Effekt beim press? Das sollte einen repaint triggern.
Verfasst: 9. Mai 2011 13:34
von marvel
Der Button befindet sich im selben Fenster wie die Zeichenfläche, jedoch ist dieser mit keinerlei Funktionalität verbunden. Ich habe ihn nur im Designer erstellt und via setupUI() mit dem QWidget verbunden. Kann mir einfach dieses Verhalten nicht erklären
Verfasst: 9. Mai 2011 21:20
von franzf
Und wie bereits angedeutet: Das Drücken des Buttons wird wohl bestimmte Bereiche invalidieren, und damit im Widget darunter ein repaint auslösen. Was es genau ist weiß ich nicht, es hat jedenfalls nichts mit einem angeschlossenen SLOT zu tun.
Verfasst: 10. Mai 2011 03:55
von marvel
Hallo franzf,
ich bin Dir sehr dankbar das Du mir Rede und Antwort stehst, ich bin nämlich langsam am verzweifeln. Wie Du am Post erkennen kannst schlage ich mir die Nächte um die Ohren, weil ich einfach nicht weiterkomme. Ich habe die Buttons jetzt erstmal komplett rausgenommen, weil ich damit nur eine Verschiebung in der Karte simulieren wollte. Ich habe leider immer noch einige Probleme die ich durch unzählige Testerei nicht bewältigen konnte. Im Grunde soll das ganze wie folgt ablaufen: Ich klicke im TabWidget auf den Reiter Files und lese eine Datei ein.Die Datei enthält mehrere unterschiedliche Objekte. Ein Objekt hat mehrere Koordinaten. Wenn die Datei abgearbeitet wurde werden die Objekte in einem QPainterPath vorbereitet und in einer Liste abgelegt. Sobald ich nun auf den Reiter Map klicke soll anhand der Liste von QPainterPath die Karte gezeichnet werden. Ich zeichne direkt ins Widget. Wenn ich in der Karte mit der Maus klicke wird die Karte entsprechend zentriert bzw die Zeichenroutine neu angestoßen. Bei einer großen Anzahl an Objekten kann das Zeichnen der Karte bis zu 20 Sekunden und länger dauern. Gibt es eine Möglichkeit diesen Vorgang zu optimieren oder sollte ich einen anderen Ansatz wählen ?
Verfasst: 19. Mai 2011 04:05
von marvel
Ich habe nochmals meine gesamte Zeichenroutine durchleuchtet und bin mit meinem Latein wirklich am Ende. Bei einer gewissen Anzahl an Objekten macht mein Programm einfach nicht mehr mit. Ich bekomme folgende Fehlermeldung :
Code: Alles auswählen
Zu zeichnendes Objekt: "rail"
Anzahl aller "rail" Objekte: 4498
Anzahl der zu zeichnenden "rail" Objekte: 2342
RaiseException: Thread=9cb24964 Proc=80097280 'oac.exe'
AKY=00010001 PC=03f8eb28(coredll.dll+0x00044b28) RA=88037900(NK.EXE+0x00007900) BVA=00000000 FSR=00000000
Unhandled exception at 0x03f8eb28 in oac.exe: Microsoft C++ exception: std::bad_alloc at memory location 0x2364fe24..
Das Programm "[0xBC5E8806] oac.exe" wurde mit Code 1067 (0x42b) beendet.
Auch mein Versuch erst in eine QPixmap zu schreiben und dann das ganze aufs QWidget zu übertragen hat leider nichts gebracht. Ich bekomme folgende Meldung:
Code: Alles auswählen
QImage: out of memory, returning null image
libpng warning: Image width is zero in IHDR
libpng warning: Image height is zero in IHDR
libpng error: Invalid IHDR data
Ich habe versucht an allen Stellen zu optimieren, jedoch ohne Erfolg. Ich programmiere wie schon erwähnt für Windows Mobile. Muss ich mich eventuell damit geschlagen geben das es einfach nicht umsetzbar ist aufgrund fehlender Ressourcen oder kann ich noch irgendwas optimieren oder verändern am Zeichnen ?
Verfasst: 19. Mai 2011 09:53
von UweS
Hallo marvel,
marvel hat geschrieben:
Code: Alles auswählen
QImage: out of memory, returning null image
libpng warning: Image width is zero in IHDR
libpng warning: Image height is zero in IHDR
libpng error: Invalid IHDR data
ich weiss nicht wieviel Speicher deiner Anwendung unter Windows Mobile zur Verfügung steht, aber an der Stelle hast du versucht ein QImage zu alloziieren und das Ergebnis nicht geprüft. Der Speicher scheint voll zu sein.
Unter 32bit Windows habe ich auch das Problem, das das Laden großer Daten meine Anwendung an die 2GB Speichergrenze eines Prozesses bringt. Wenn man da nicht überall prüft, oder mit try/catch die Exceptions fängt, rumsts.
Verfasst: 19. Mai 2011 16:06
von marvel
Hallo,
ich wüsste nicht an welcher Stelle bzw wie ich überpürfen kann wann und wieviel Speicher alloziert wird. Ich habe folgendes Konstrukt:
Code: Alles auswählen
void GUI_Map::paintEvent(QPaintEvent* event)
{
qDebug() << "PaintEvent";
QPixmap pixmap(width(),height());
QPainter painter(&pixmap);
// Maßstab setzen
double x = ((double)width() /(double)physicalDpiX()) * 2.54 * 5;
double y = ((double)height() /(double)physicalDpiY()) * 2.54 * 5;
// Skala für den sichtbaren Bereich auf- oder abrunden.
int xRange = (x*1+0.5)/1.0;
int yRange = (y*1+0.5)/1.0;
_oac_map->createMap(&painter,this->width(),this->height(),_xMousePos,_yMousePos,xRange,yRange);
pixmap.save("path.png");
}
Jedes Mal wenn ich im TabWidget auf den Reiter Map klicke wird die paintEvent Methode aufgerufen. In der paintEvent Methode wird die Methode createMap aufgerufen und dieser sowohl der painter als auch verschiedene Paramter für die Kartendarstellung weitergegeben. Das Zeichnen wird dann hier vorbereitet:
Code: Alles auswählen
void OAC_Map::createMap(QPainter *painter, int physicalXRange, int physicalYRange, int xMousePos, int yMousePos, int logicalXRange, int logicalYRange)
{
// Die ermittelten Kacheln zeichnen.
for(int i=0; i<_tileList.size(); i++)
{
for(int k=0; k<_drawOrder.size(); k++)
{
if(_tileList.at(i)->getHashLayer().contains(_drawOrder.at(k)))
{
qDebug() << _drawOrder.at(k);
OAC_Layer *layer = _tileList.at(i)->getHashLayer().value(_drawOrder.at(k));
if(layer->getViewable())
{
qDebug() << "Anzahl aller Objekte: " << layer->getObjectList().size();
for(int x=0; x< layer->getObjectList().size(); x++)
{
OAC_Object *object = layer->getObjectList().at(x);
QRectF objectBounding = object->getPainterPath().boundingRect();
QRectF viewBounding(_viewMinLon,_viewMaxLat,logicalXRange,-logicalYRange);
if(objectBounding.intersects(viewBounding))
{
layer->getObjectsToDraw().append(object);
}
}
qDebug() << "Anzahl der zu zeichnende Objekte: " << layer->getObjectsToDraw().size();
layer->drawObjects(painter);
layer->wait();
}
}
}
}
Am Ende wird hier gezeichnet:
Code: Alles auswählen
void OAC_Layer::drawObjects(QPainter *p)
{
_painter = p;
_painter->setBrush(_brush);
_painter->setPen(_pen);
start();
}
void OAC_Layer::run()
{
while(!_objectsToDraw.isEmpty())
{
_painter->drawPath(_objectsToDraw.takeLast()->getPainterPath());
}
}
Es können teilweise bis zu 7000 PainterPath Objekte in einem SChleifendurchlauf abgearbeitet werden, meistens knallt es dann auch. Kostet jede drawPath() Aufruf zu viel Speicher oder muss ich vorher den Speicher wieder freigeben ? Wo und wie kann ich überprüfen ob noch genug SPeicher vorhanden ist bzw Exceptions abfangen ?
Verfasst: 19. Mai 2011 16:44
von Christian81
Das das überhaupt funktioniert ist ein Wunder... ich gehe mal davon aus dass OAC_Layer ein separater Thread ist.
Er würde also bei jedem GUI_Map::paintEvent() neu gestartet. Außerdem übergibst Du ein QPainter-Pointer an den Thread aber der Pointer (bzw. das Objekt lebt nicht wirklich lang... und dann sind QPixmaps außerhalb des Main-Threads nicht zu verwenden (auch wenns kein Bug ist, siehe z.B.
http://bugreports.qt.nokia.com/browse/QTBUG-7848 )
Verfasst: 19. Mai 2011 17:20
von marvel
Das das überhaupt funktioniert ist ein Wunder... ich gehe mal davon aus dass OAC_Layer ein separater Thread ist.
Er würde also bei jedem GUI_Map::paintEvent() neu gestartet. Außerdem übergibst Du ein QPainter-Pointer an den Thread aber der Pointer (bzw. das Objekt lebt nicht wirklich lang... und dann sind QPixmaps außerhalb des Main-Threads nicht zu verwenden (auch wenns kein Bug ist, siehe z.B.
http://bugreports.qt.nokia.com/browse/QTBUG-7848 )
OAC_Layer ist ein seperater Thread. Den Thread habe ich eingebaut in der Hoffnung das beim klicken auf mein Widget das Fenster nicht blockiert, also erst das Fenster angezeigt wird und im Hintergrund der Thread abgearbeitet wird. Auch ohne den Thread bricht das Programm irgendwann ab. Ich möchte auf das QPainter Objekt eingehen. Dieses stirbt doch erst wenn die paintEvent Methode abgearbeitet ist oder ? Ich sehe nämlich kein Problem mit der Lebensdauer...erkennst Du einen Fehler ?
Lassen wir mal die QPixmap Geschichte erstmal weg. Es soll also direkt ins Widget gezeichnet werden.
Code: Alles auswählen
void GUI_Map::paintEvent(QPaintEvent* event)
{
qDebug() << "PaintEvent";
QPainter painter(this);
// Maßstab setzen
double x = ((double)width() /(double)physicalDpiX()) * 2.54 * 5;
double y = ((double)height() /(double)physicalDpiY()) * 2.54 * 5;
// Skala für den sichtbaren Bereich auf- oder abrunden.
int xRange = (x*1+0.5)/1.0;
int yRange = (y*1+0.5)/1.0;
_oac_map->createMap(&painter,this->width(),this->height(),_xMousePos,_yMousePos,xRange,yRange);
}
Verfasst: 19. Mai 2011 17:57
von marvel
okay, ich konnte folgendes Verhalten feststellen. Wenn ich die Punkte nicht vorher im QPainterpath vorbereite sondern direkt mit den Punkten via drawLine, drawPolygon zeichne funktioniert es ohne Probleme. Scheinbar kostet ein drawPath zu viele Resourcen
Verfasst: 19. Mai 2011 18:30
von Christian81
Ich habe layer->wait(); im Code übersehen. Aber da nützt ein Thread ja erst recht nichts wenn der Hauptthread eh auf den anderen Thread wartet... abgesehen davon wäre immer noch das Problem mit QPixmap/QWidget zeichnen in einem separaten Thread was nicht erlaubt ist.
Und wenn das Programm crasht gibt es einen Debugger und/oder valgrind mit denen man nachschauen kann wo und warum es crasht.
Verfasst: 18. Juni 2011 08:25
von marvel
Hallo liebe Community,
ich neige mich langsam dem Ende meiner Diplomarbeit zu und habe noch ein paar kleine Bugs die ich gerne beseitigen würde. Ich hatte schon damals ein bestimmtes Problem angesprochen, jedoch keine Lösung gefunden und erstmal das ganze bis heute aufgeschoben.
Problemstellung:
Ich habe ein QTabWidget mit mehreren Tabs. In einem der Tabs habe ich die paintEvent Methode reimplementiert. Sobald der Tab aufgerufen wird löst die paintEvent Methode aus. Zusätzlich habe ich im Tab bzw im Widget einen QToolButton eingebaut. Dieser Button ist mit keinem Slot verbunden, er wurde lediglich einmal durch die setupUI() Methode ins Leben gerufen und ist vorerst leblos. Dennoch wird bei einem Klick auf den Button gleich zweimal die paintevent Methode aufgerufen. Dies möchte ich irgendwie unterbinden. Gibt es eine Möglichkeit dieses Verhalten zu ändern oder kennt jmd eine Alternative ?
Für eine Lösung wäre ich sehr dankbar, da meine Diplomarbeit davon abhängt und ich nur noch sehr wenig Zeit habe das Problem zu lösen. Danke im vorraus für die Hilfe