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

Alles rund um die Programmierung mit Qt
qtnub
Beiträge: 17
Registriert: 10. Juli 2007 15:27

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

Beitrag 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?
Zuletzt geändert von qtnub am 30. September 2007 17:44, insgesamt 1-mal geändert.
CaptnChaos
Beiträge: 605
Registriert: 28. Juni 2007 15:01
Kontaktdaten:

Beitrag von CaptnChaos »

Bitte poste mal den Code. Dann schau ich mir das mal an.
qtnub
Beiträge: 17
Registriert: 10. Juli 2007 15:27

Beitrag 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();
	}
	
}
qtnub
Beiträge: 17
Registriert: 10. Juli 2007 15:27

Beitrag von qtnub »

Es kann ja irgendwo nicht sein, dass 24 kleine Widgets, die alle 30 msec aktualisiert werden, eine Prozessorlast von 40% erzeugen :(
macman
Beiträge: 1738
Registriert: 15. Juni 2005 13:33
Wohnort: Gütersloh
Kontaktdaten:

Beitrag von macman »

Mir fehlen die Worte.
Die deutsche Schriftsprache ist case-sensitive. Außerdem gibt es eine Interpunktionsnorm. Wenn manch einer seine Programme genauso schlampig schreibt, wie sein Posting hier, dann sollte er es lieber bleiben lassen.
qtnub
Beiträge: 17
Registriert: 10. Juli 2007 15:27

Beitrag von qtnub »

Was ich suche ist im Prinzip ein Flag, das festlegt, dass als Hintergrund beim repaint das Resultat des letzten repaintEvents verwendet wird.
CaptnChaos
Beiträge: 605
Registriert: 28. Juni 2007 15:01
Kontaktdaten:

Beitrag 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.
qtnub
Beiträge: 17
Registriert: 10. Juli 2007 15:27

Beitrag 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.
qtnub
Beiträge: 17
Registriert: 10. Juli 2007 15:27

Beitrag 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. :)
CaptnChaos
Beiträge: 605
Registriert: 28. Juni 2007 15:01
Kontaktdaten:

Beitrag 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.
qtnub
Beiträge: 17
Registriert: 10. Juli 2007 15:27

Beitrag 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?
CaptnChaos
Beiträge: 605
Registriert: 28. Juni 2007 15:01
Kontaktdaten:

Beitrag von CaptnChaos »

Und warum zeichnest du nicht einfach auf dein Hauptwidget und nicht auf unendlich viele kleine Widgets?
qtnub
Beiträge: 17
Registriert: 10. Juli 2007 15:27

Beitrag von qtnub »

ähm..., es wäre eine schlechtere Strukturierung und die Frage ist ja, ob er dann nicht das Gleiche machen würde.
CaptnChaos
Beiträge: 605
Registriert: 28. Juni 2007 15:01
Kontaktdaten:

Beitrag von CaptnChaos »

schick mir mal den quellcode:
markus.kuenkler@gmx.de
qtnub
Beiträge: 17
Registriert: 10. Juli 2007 15:27

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