Seite 1 von 2

[gelöst] paintEvent löscht Widgethintergrund???

Verfasst: 13. September 2007 15:13
von qtnub
Moinmoin,
ich bastel gerade an einem Widget, das einen Balken ausgeben soll, dessen Höhe proportional zu einem Wert ist.
Nun dachte ich, man könnte einfach die paintEvent-Methode überschreiben.
Da das Zeichnen den Prozessor allerdings nicht so sehr auslasten soll, wollte ich das so lösen, dass nur der Bereich des Bildes neu gezeichnet wird, der sich verändert hat.
Das geht anscheinend aber nicht so einfach, da immer das komplette Bild neu gezeichnet wird, sprich, wenn im painEvent nichts gemacht wird, dann verschwindet auch der alte Balken!!!??

Gibts da eine elegante Lösung für?

Verfasst: 13. September 2007 15:19
von CaptnChaos
Bitte poste mal den Code. Dann schau ich mir das mal an.

Verfasst: 13. September 2007 15:35
von qtnub
Okay, in meinem Testcode sollte es erstmal so sein, dass sich der Balken nur erhöhen kann.
Allerdings wird der Balken gelöscht, wenn sich der Wert nicht erhöht. die paintEvent-Methode wird immer aufgerufen, wenn sich value geändert hat.

Code: Alles auswählen

void Anzeige::paintEvent(QPaintEvent *) {

	if (value > oldValue) {
		QPainter painter(this);
		int yNeu = back.height() - value;
		int height = value - oldValue;
		painter.drawPixmap(0, yNeu, back.width(), height,   (*imgLevel), 0, yNeu, back.width(), height);
	                oldValue = value;
		painter.end();
	}
	
}

Verfasst: 13. September 2007 15:45
von qtnub
Es kann ja irgendwo nicht sein, dass 24 kleine Widgets, die alle 30 msec aktualisiert werden, eine Prozessorlast von 40% erzeugen :(

Verfasst: 13. September 2007 16:02
von macman
Mir fehlen die Worte.

Verfasst: 13. September 2007 16:11
von qtnub
Was ich suche ist im Prinzip ein Flag, das festlegt, dass als Hintergrund beim repaint das Resultat des letzten repaintEvents verwendet wird.

Verfasst: 13. September 2007 16:49
von CaptnChaos
Bitte das ganze Widget, damit ich das auch mal ausprobieren kann.
UND! ALLE 30MSEC????? DU FRAGST DICH WARUM DAS 40% AUSLASTET...
Sry, hatte grad nen Kernel Panic...
erm, versuchs mal mit allen 1sec. Dann wirst du sehen, dass sich die Prozessorlast drastisch reduziert.

Verfasst: 13. September 2007 17:05
von qtnub
ok, der Header sieht so aus:

Code: Alles auswählen

#ifndef ANZEIGE_H
#define ANZEIGE_H

#include <QWidget>
#include <QtGui>

class Anzeige : public QWidget
{
	Q_OBJECT

public:
	Anzeige(QPixmap *imgBackground, QPixmap *imgLevel, QPixmap *imgPeak, QRect back, QWidget *parent, QPoint offset = QPoint(0, 0));
	~Anzeige();
	void setValue(float v);
	void update(int value, int peak);
	void setImage(QPixmap *imgBackground, QPixmap *imgLevel, QPixmap *imgPeak);

private:
	QPixmap *imgBackground;
	QPixmap *imgLevel;
	QPixmap *imgPeak;

	int balkenX;
	int balkenY;
	float peakY;
	QRect back;
	int value;
	int peak;
	int oldValue;
	int oldPeak;

protected:
	void paintEvent(QPaintEvent *);
	
};

#endif
peak spielt erstmal keine Rolle, es geht erstmal nur darum, diesen Balken zum Laufen zu bekommen.

Code: Alles auswählen

#include "Anzeige.h"
#include <iostream>

Anzeige::Anzeige(QPixmap *imgBackground, QPixmap *imgLevel, QPixmap *imgPeak, QRect back, QWidget *parent, QPoint offset) {
	this->imgBackground = imgBackground;
	this->imgPeak = imgPeak;
	this->imgLevel = imgLevel;
	this->back = QRect(back.x() + offset.x(), back.y() + offset.y(), back.width(), back.height());
	setGeometry(this->back);
	setParent(parent);
	//this->setAutoFillBackground(false); 
	//this->setAttribute(Qt::WA_OpaquePaintEvent, false);
	//this->setAttribute(Qt::WA_NoBackground, false);

	peakY = 30;

	value = 0;
	peak = 0;
	oldValue = 0;
}

Anzeige::~Anzeige() {

}

void Anzeige::setValue(float v) {



}

void Anzeige::paintEvent(QPaintEvent *) { 

   if (value > oldValue) { 
      QPainter painter(this); 
      int yNeu = back.height() - value; 
      int height = value - oldValue; 
      painter.drawPixmap(0, yNeu, back.width(), height,   (*imgLevel), 0, yNeu, back.width(), height); 
                   oldValue = value; 
      painter.end(); 
   } 
    
} 

void Anzeige::update(int value, int peak) {
	if ((value != this->value) || (peak != this->peak)) {
		this->value = value;
		//repaint();
		int yNeu = back.height() - value;
		int height = value;//value - oldValue;
		this->repaint(0, yNeu, back.width(), height);
	}
	
}

void Anzeige::setImage(QPixmap *imgBackground, QPixmap *imgLevel, QPixmap *imgPeak) {
	if ((this->imgBackground != imgBackground) || (this->imgLevel != imgLevel) || (this->imgPeak != imgPeak)){
		this->imgBackground = imgBackground;
		this->imgLevel = imgLevel;
		this->imgPeak = imgPeak;
	}
}
Das mit den 30 msec muss schon sein, da das ganze so ein Audiomixerprogramm werden soll. In einem alten Windowsprogramm läuft das auch mit 3 mal so vielen Elementen deutlich flüssiger.

Verfasst: 13. September 2007 17:31
von qtnub
Nach langem Stöbern in der Qt-Doku hab ich nun endlich einen eventuellen Lösungsansatz gefunden, der eventuell nicht der eleganteste ist, aber funktionieren müsste.
Mit den Zeilen:

Code: Alles auswählen

	this->setAttribute(Qt::WA_NoSystemBackground, true);
	this->setAttribute(Qt::WA_PaintOnScreen, true);
kann man das Doublebuffering deaktivieren und außerdem die Einstellung, dass der Hintergrund des Widgets gelöscht wird, bevor das paintEvent aufgerufen wird. :)

Verfasst: 13. September 2007 17:45
von CaptnChaos
ARGH!
Sry, aber was bitteschön soll ich mit den Dateien anfangen? Wenn das mehr als Klasse ist, die zu deinem Widget gehören und sogar noch Pixmaps eingebunden sind, solltest du enweder ein minimal lauffähiges Widget mit den Funktionen deiner Anwendung posten oder du lädst dein Projekt hoch.
Und worunter Programmierst du? Windows? Dann wird dir das :

Code: Alles auswählen

this->setAttribute(Qt::WA_PaintOnScreen, true); 
nicht viel bringen. Lies ma nach was in der Doku steht. Und WA_NoSystemBackground wird auch nicht viel bringen. Versuch es mit den Standart WinApi Paint Funktionen, das geht wesentlich schneller als die PaintEngine von Qt, ist aber nich so schön. Um mit WinApi zu zeichnen brauchst du ein Handle auf den DC deines Widgets. Such mal schön in der Doku, unter QWidget, da wirst du dann was finden.

Verfasst: 13. September 2007 18:23
von qtnub
Danke für die Mühe, es ja eigentlich darum, auszustellen, dass der Hintergrund des Widgets resetet wird, bevor das paintEvent ausgelöst wird.
Das ganze ist wohl nicht so einfach. Einfach auf Windows-Funktionen zurückgreifen, wäre eine Lösung, dann müsste ich aber ähnliches für alle anderen Systeme seperat machen, da das Prog am Ende auf allen Systemen lauffähig sein soll.
Ich werde mal weiterprobieren.

Warum WA_NoSystemBackground nicht so richtig funzt, weiß ich allerdings nicht.
Ich werde nochmal weiterprobieren, schließlich muss es irgendeine Lösung geben, sonst wäre Qt für umfangreiche, selbstdesignte Fenster ja kaum zu gebrauchen :( .

Vielleicht könnte man die repaint-Methode überschreiben?

Verfasst: 13. September 2007 19:46
von CaptnChaos
Und warum zeichnest du nicht einfach auf dein Hauptwidget und nicht auf unendlich viele kleine Widgets?

Verfasst: 14. September 2007 15:25
von qtnub
ähm..., es wäre eine schlechtere Strukturierung und die Frage ist ja, ob er dann nicht das Gleiche machen würde.

Verfasst: 14. September 2007 16:10
von CaptnChaos
schick mir mal den quellcode:
markus.kuenkler@gmx.de

Verfasst: 30. September 2007 17:39
von qtnub
sry, ich war zwischenzeitlich im Urlaub, habe danach nochmal ein bisschen selbst herumprobiert und das Problem gelöst.

1) Also das Doublebuffering kann man deaktivieren, indem man im hintersten Widget(das Widget ohne parent) WA_NoSystemBackground aktiviert.

2)Außerdem bringt es enorme Performancevorteile, wenn man nur ein Widget hat, das sehr oft den paintEvent aufruft, anstelle von vielen kleinen.

-> Lösung: alle Balken in ein Widget zeichnen, Doublebuffering deaktiveren und beim Zeichnen des ganzen Widgets erstmal in einen Buffer zeichnen.