[gelöst] QGraphicsItem - Opacity in paint Funktion ->QPix

Alles rund um die Programmierung mit Qt
Antworten
helmuth
Beiträge: 15
Registriert: 11. Dezember 2010 01:26

[gelöst] QGraphicsItem - Opacity in paint Funktion ->QPix

Beitrag von helmuth »

Hallo Leute,

Zusammenfassung:
Wenn ich im paint event innerhalb einer QGraphicsItem Klasse, die den Opacity Wert 0.5 hat 2 Pixmaps mit Opacity 1.0 und 0.2 zeichne - bekommen dann beide Pixmaps 0.5 aufgedrückt oder bleiben diese was ihre Opacity angeht eigenständig?

Nach meinem Verständnis müsste es eigentlich so sein das Pixmap 1 beim Darstellen im QWidget den Wert 0.5 bekommt und Pixmap 2 den Wert 0.1.

Aber nach meinen Versuchen letztes Wochenende werden beide Pixmaps grundsätzlich mit dem Opacity Wert der übergeordneten Klasse dargestellt. Was eigentlich ja nicht richtig sein kann, oder?


Details meines Problems:
Ich habe eine Klasse programmiert die mir abhängig vom Status 2 unterschiedliche QPixmap's darstellen soll.

Dies funktioniert auch prima. Mit .setOpacity(0.5); auf die gesammtklasse angewendet kann ich die gezeichneten QPixmaps auch wunderbar halbtransparent darstellen bzw. mit hilfe eines QTimers sanft ein oder ausblenden.

Nun möchte ich aber zusätzlich innerhalb meiner Klasse im paint event die beiden Bilder sanft in einander überblenden sobald sich der Status ändert.

Ich habe inzwischen mehrere Ansätze durchprobiert und dabei einige stunden und viele Haare verloren. :wink:

Was prima funktioniert ist dies hier:

Code: Alles auswählen

    QPixmap PixAlpha = PixStatus1;
    QPainter p(&PixAlpha);
    p.fillRect(PixAlpha.rect(), QColor(LocBlende, LocBlende, LocBlende));
    p.end();
    PixStatus1.setAlphaChannel(PixAlpha);
LocBlende ist im Bereich von 0 bis 255.

Nachteil: SEHR Rechenintensiv. Wird sogar in der Qt Dokumentation von abgeraten. Keine Praktikable lösung.

Viel lieber wäre mir ebenso wie für das gesammte Objekt für die beiden Pixmaps ganz bequem den Opacity wert zu setzen. Da diese Eigenschaft von QPixmap nicht unterstützt wird habe ich es über QPainter gelöst:

Code: Alles auswählen

            QPainter p(&PixStatus1);
            p.setOpacity (1.0/255*LocBlende);
            p.end ();
Bedauerlicherweise scheint dies völlig ignoriert zu werden sobald ich den Opacity Wert der gesammten Klasse setze. Ist dies richtig?
Oder ist der umweg über QPainter grundsätzlich falsch? :?
Zuletzt geändert von helmuth am 7. März 2011 17:15, insgesamt 2-mal geändert.
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Hast du dir die Doku zu QGraphicsItem::setOpacity durchgelesen? Da steht einiges interessantes dabei. U.A., wie es sich mit child-Items verhält. Das im Hinterkopf könntest du entsprechend QGraphicsPixmapItems als child deines Objekt hinzufügen (je nach Status), da sollte das mit der Opacity automatisch richtig klappen.

Alternativ:
Aus der Doku siehst du, dass der QPainter, den du in paint() bekommst, je nach this->opacity() schon passend vorbereitet ist. Du kannst also das Pixmap direkt Opaque aus der Datei laden, und nur die opacity des Painters in paint() anpassen (schau dir hierzu auch QPainter::save() und restore() an).
helmuth
Beiträge: 15
Registriert: 11. Dezember 2010 01:26

Beitrag von helmuth »

// Originalpost durch franzf gelöscht. Dickes Sry
// Der Text sollte als Antwort rausgehen...

Mit child items hab ich Samstagnacht etwas rumexperimentiert. Mir gelang es nur nicht diese der selben scene zuzuordnen wie ihr parent.
So schwer?
* QGraphicsItem::QGraphicsItem ( QGraphicsItem * parent = 0 )
* void QGraphicsItem::setParentItem ( QGraphicsItem * newParent )
Den passenden Konstruktor bieten alle offiziellen QGraphicsItem-Subclasses in der Qt-Library an.
Die Doku zu setParentItem sagt dir auch, was mit der scene() passiert.
Den opacity Wert des painters zu verändern bringt da natürlich nichts da ich dies dann ja für beide PixMaps tun würde.
Wieso? Opacity ist ein State von QPainter. Einmal gesetzt malt er alles mit dieser Opacity.
Ungetestet:

Code: Alles auswählen

painter->save();
painter->setOpacity(painter->opacity()*0.5);
painter->drawPixmap(0, 0, PixStatus1);
painter->drawPixmap(0, 0, PixStatus2);
painter->restore();
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Oh Mann, Sorry!!!!! Ich hab das schon manchmal bei Andere gesehen, hab dann aber immer geschmunzelt...
Ich bin statt auf "zitat" auf den "edit"-button (den wir Mods sehen) gekommen, und hab deinen ganzen Post unwiederruflich überschrieben... :oops: peinlich :oops:
Ich hoffe trotzdem, die Antwort hilft dir.
helmuth
Beiträge: 15
Registriert: 11. Dezember 2010 01:26

Beitrag von helmuth »

franzf hat geschrieben:Oh Mann, Sorry!!!!! Ich hab das schon manchmal bei Andere gesehen, hab dann aber immer geschmunzelt...
Ich bin statt auf "zitat" auf den "edit"-button (den wir Mods sehen) gekommen, und hab deinen ganzen Post unwiederruflich überschrieben... :oops: peinlich :oops:
Ich hoffe trotzdem, die Antwort hilft dir.
Ich versuchs... Danke für deine Antwort. Auch wenn es jetzt etwas missverständlich aussieht. ;)

Den Post hab ich au nimmer. Hab mir eigentlich angewohnt den in die Zwischenablage zu kopieren bevor ich auf senden drücke falls dabei was schief geht und ich mit dem zurück button ein leeren Formular vor mir habe. (sehr frustrierend wenn man sich 20 min gedanken macht)

Da ich nix schlimmes darin sah meine Ahnungslosigkeit hier zu offenbaren rechnete ich eigentlich auch nicht damit das ihn einer der Mods löscht. :wink:
Jetzt sieht es natürlich etwas missverständlich aus. Vielleicht solltest Du den Text in ein neues Post von Dir kopieren und wir ersetzen den Text bei mir oben durch [von Mod versehentlich gelöscht]

Sonst sieht es so aus als würde ich als fragenstellenden denjenigen der hilfsbereit antwortet direkt im ersten Satz fragen: "So schwer?" ;-)
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

So, als kleine Entschädigung hab ich ein kleines Beispielprogramm geschrieben.
pix1.png und pix2.png musst du natürlich selber beisteuern (ich hab kurzerhand zwei 48x48-Icons genommen).

items.hpp

Code: Alles auswählen

#ifndef MY_ITEMS_TEST
#define MY_ITEMS_TEST

#include <QGraphicsItem>
#include <QPainter>
#include <QGraphicsPixmapItem>
#include <QPixmap>
#include <QRectF>

class Item_v1 : public QGraphicsItem
{
    QGraphicsPixmapItem *p1, *p2;
    
    void paint(QPainter* p, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 ) {
        p->drawRect(boundingRect());
    }
public:
    Item_v1()
     : p1(new QGraphicsPixmapItem(QPixmap("pix1.png"), this)),
       p2(new QGraphicsPixmapItem(QPixmap("pix2.png"), this))
    {
        p1->setOpacity(0.5);
        p2->setOpacity(0.5);
        p2->setPos(50, 0);
    }
    
    QRectF boundingRect() const {
        return QRectF(0,0,200,200);
    }
};

class Item_v2 : public QGraphicsItem
{
    QPixmap p1, p2;
    void paint(QPainter* p, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 ) {
        p->drawRect(boundingRect());
        p->save();
        p->setOpacity(p->opacity()*0.5);
        p->drawPixmap(0,  0, p1);
        p->drawPixmap(50, 0, p2);
        p->restore();
    }
public:
    Item_v2()
     : p1("pix1.png"),
       p2("pix2.png")
    {}
    
    QRectF boundingRect() const {
        return QRectF(0,0,200,200);
    }
};

#endif
main.cpp

Code: Alles auswählen

#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>

#include "items.hpp"

int main(int argc, char** argv) {
    QApplication app(argc, argv);
    QGraphicsView view;
    QGraphicsScene scene;
    view.setScene(&scene);
    
    QGraphicsItem* i1 = new Item_v1;
    QGraphicsItem* i2 = new Item_v2;
    
    i1->setOpacity(0.6);
    i2->setOpacity(0.3);
    i2->setPos(250,0);
    
    scene.addItem(i1);
    scene.addItem(i2);
    
    view.show();
    return app.exec();
}
Hoffe das hilft. Bei Fragen einfach - fragen :P
Ich pass extra auf, dass ich den richtigen Button erwisch ^^
helmuth
Beiträge: 15
Registriert: 11. Dezember 2010 01:26

Beitrag von helmuth »

franzf hat geschrieben:
Den opacity Wert des painters zu verändern bringt da natürlich nichts da ich dies dann ja für beide PixMaps tun würde.
Wieso? Opacity ist ein State von QPainter. Einmal gesetzt malt er alles mit dieser Opacity.
Ungetestet:

Code: Alles auswählen

painter->save();
painter->setOpacity(painter->opacity()*0.5);
painter->drawPixmap(0, 0, PixStatus1);
painter->drawPixmap(0, 0, PixStatus2);
painter->restore();
Richtig. Führt aber nicht zu den Ergebnis das ich wollte. :)

Ich bräuchte für die beiden Pixmaps PixStatus1 und PixStatus2 zwei unterschiedliche opacity werte um sie zu überblenden. (war ursprünglich in meinem verschwundenen Posting #3 nochmal genauer erläutert) :wink:

Aber Ich fürchte Du hast mich eben auf die richtige Spur gebracht! Und ich könnt mir grad ein ESEL Schild um den Hals hängen... :oops:

Ich ging davon aus das der opacity wert auswirkungen auf das gesammte Objekt hat. Sprich, wenn ich den Opacity wert verändere wird das Objekt mit entsprechendem Wert gezeichnet ohne die paint routine nochmal durchlaufen zu müssen. Sich das ganze quasi nur in einem FrameBuffer oder sowas abspielt.
Somit wäre das unabhängig davon wann ich ihn einsetze.

ABER, dem ist nicht so. Dein Code in dieser Reihenfolge hat tatsächlich den für mich gewünschten Effekt:

Code: Alles auswählen

painter->drawPixmap(0, 0, PixStatus1);
painter->setOpacity(painter->opacity()*0.5);
painter->drawPixmap(0, 0, PixStatus2);
[seufz]
PixStatus1 wird so mit der ursprünglichen Opacity gezeichnet. Danach wird die Opacity halbiert um damit PixStatus2 zu zeichnen... ohne auswirkung auf die schon gezeichnete PixStatus2 zu haben. OHMAN!

Setze ich den Opacity wert meines Objekts auf 0.4 wird PixStatus1 mit 0.4 gezeichnet und PixStatus2 mit 0.2 - wie gewünscht.

Und deshalb bastel ich die halbe nacht mit Alpha kanälen rum. :evil:
Hab mich sogar schon daran geübt ein Feld mit 16 QPixmaps beim Initialisieren des Objekts mit allen gewünschten überblendungsstufen errechnen zu lassen damit ich beim darstellen unabhängig von der Prozessorlast bin. (ging mir mit 16MB aber entschieden zu sehr zu lasten des Arbeitsspeichers)

Jetzt hab ich nurnoch das Problem das PixStatus2 bei Opacity 0.0 immernoch sichtbar ist wenn die globale Opacity mit der PixStatus1 gezeichnet wird noch nicht 1.0 ist.
Hört sich ein wenig nach diesem verhalten an, da unter Linux mit Opacity 0.0 PixStauts2 grundsätzlich nicht sichtbar ist:
Bug in Qt 4.5.1 QGraphicsItem setOpacity?

Das wiederum macht mir aber nix. Wenn die Opacity für PixStatus2 0.0 ist spare ich mir einfach das zeichnen. :)

DANKE DIR!

Werd nun mal meinen Code entrümpeln gehen. Nach meiner ganzen bastelei und geteste ist der überfüllt mit // und viel zu kurzen und uneindeutigen Variablennamen.

Dein Beispiel mit Item_v1 werd ich auch mal ausprobieren. Wenn ich das gerade richtig abschätze bringt es mir aber keine weiteren vorteile bzw. den nachteil das QPixmap2 grundsätzlich gezeichnet wird, auch wenn dieser für dieses Pixmap 0.0 wäre.
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Sehr schön dass dass es jetzt geht :)
Dein Beispiel mit Item_v1 werd ich auch mal ausprobieren. Wenn ich das gerade richtig abschätze bringt es mir aber keine weiteren vorteile bzw. den nachteil das QPixmap2 grundsätzlich gezeichnet wird, auch wenn dieser für dieses Pixmap 0.0 wäre.
Wann kann es denn 0.0 werden? Es kann sich annähern, aber ein double/0.5 ergibt einen double ungleich 0.0. Und pass auf, dass du nich nach einer Berechnung mit festen Werten vergleichst. Durch die interne Darstellung von Fließkommazahlen kann im Programm durchaus passieren

Code: Alles auswählen

double d=1.0;
double d2 = d/0.5;
assert(d2==0.5);
dass das assert dein Programm abwürgt!

Als Lösung könntest du ab einem bestimmten opacity-Wert das Item auf invisible setzen:
void QGraphicsItem::setVisible ( bool visible )
If visible is true, the item is made visible. Otherwise, the item is made invisible. Invisible items are not painted, nor do they receive any events.
helmuth
Beiträge: 15
Registriert: 11. Dezember 2010 01:26

Beitrag von helmuth »

franzf hat geschrieben:
Dein Beispiel mit Item_v1 werd ich auch mal ausprobieren. Wenn ich das gerade richtig abschätze bringt es mir aber keine weiteren vorteile bzw. den nachteil das QPixmap2 grundsätzlich gezeichnet wird, auch wenn dieser für dieses Pixmap 0.0 wäre.
Wann kann es denn 0.0 werden? Es kann sich annähern, aber ein double/0.5 ergibt einen double ungleich 0.0.
Ja, das ist ja eigentlich nur die stark vereinfachte darstellung meines Codes.
Ich wollte dem hier folgen: Ein erfolgreicher Beitrag und nicht seitenweise Code posten der mit dem eigentlichen Problem nichts zu tun hat. ;)
Siehe mein 1. Post:
Nun möchte ich aber zusätzlich innerhalb meiner Klasse im paint event die beiden Bilder sanft in einander überblenden sobald sich der Status ändert.
Sprich, sobald sich ein localer bool Wert in meiner Klasse ändert startet ein QTimer der den Opacity Wert des 2. QPixmap ändert bis dieses das 1. überlagert. Es bleibt also nicht bei

Code: Alles auswählen

painter->setOpacity(painter->opacity()*0.5);


sondern sieht aktuell so aus:

Code: Alles auswählen

painter->setOpacity (painter->opacity()*(1.0/255.0*LocIntBlende));
Dies aber auch nur weil ich das ganze aus verzweiflung für 255 Blendenstufen des Alphakanals geschrieben habe. Bin noch am überlegen ob ich das umschreibe...

Zudem ist der qreal wert von opacity bei mir nicht double, sondern float. (ARM Platform) ;)
Aber danke für den Hinweis. Mit Berechnungen von Flieskommazahlen bin ich schon zu Basic zeiten mehrfach so auf die Nase gefallen das ich grundsätzlich alles gerne in int mache und das ganze für die von mir benötigte genauigkeit um x nachkommastellen verschiebe um erst zur ausgabe in double umzurechnen. (mDM statt DM war damals die Währungseinheit schlechthin) Aber jetzt wirds doch offtopic. :)
helmuth
Beiträge: 15
Registriert: 11. Dezember 2010 01:26

Beitrag von helmuth »

Hmm... ich fürchte mein titel ist zu lang. Bekomme kein [gelöst] davor... :(

Gibts hier irgendwo ein häkchen das ich setzen kann oder ist das bearbeiten des Titels die einzige Möglichkeit den Status eines Threads auf "problem gelöst" abzuändern?
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

tjo, da merkt man, dass ich heut etwas neben mir steh...
"ineinander überblenden" war für mich nicht das dynamische überblenden, sondern ein überlappendes überblenden, bei dem beide sichtbar sein sollen.

Ansonsten hätte ich dich auch gleich auf die QPropertyAnimation aufmerksam gemacht...
Von QPixmapItem und QObject ableiten, opacity als Property exportieren. Davon zwei Member setzen. Fürs Überblenden dann zwei Animationen laufen lassen p1 von max nach min (fadeout), p2 von min nach max (fade in). Ist die fadeout-Animation fertig, einfach das Item auf invisible setzen.

// edit
Wg. Gelöst gibts hier leider keinen Haken (wie bei forum.kde.org z.B.).
Wenn der Titel zu lang ist, kannst du ihn ja kürzen ;)
"GraphicsItem, opacity und paint" o.Ä.
helmuth
Beiträge: 15
Registriert: 11. Dezember 2010 01:26

Beitrag von helmuth »

franzf hat geschrieben:tjo, da merkt man, dass ich heut etwas neben mir steh...
Da heute erst Montag ist hoffe ich das Du kein Wochenende dazwischen brauchst um Dich wieder zu finden. :)
franzf hat geschrieben: Ansonsten hätte ich dich auch gleich auf die QPropertyAnimation aufmerksam gemacht...
Von QPixmapItem und QObject ableiten, opacity als Property exportieren. Davon zwei Member setzen. Fürs Überblenden dann zwei Animationen laufen lassen p1 von max nach min (fadeout), p2 von min nach max (fade in). Ist die fadeout-Animation fertig, einfach das Item auf invisible setzen.
Hätte mir auch nichts genützt da ich nicht gewusst hätte was animieren. Die Opacity meiner Klasse hab ich ja schon zum einblenden selbst verwendet. Ich brauchte es aber innerhalb der Paint funktion. :)
franzf hat geschrieben: // edit
Wg. Gelöst gibts hier leider keinen Haken (wie bei forum.kde.org z.B.).
Wenn der Titel zu lang ist, kannst du ihn ja kürzen ;)
"GraphicsItem, opacity und paint" o.Ä.
Hab ich gemacht, anzumerken ist das da n kleiner Bug zu sein scheint. Ich habe ganz normal "QPixmap" eingegeben... die Zeichen schienen gereicht zu haben. Trotzdem schneidet er die 3 letzten ab.

Bug? Unicode problem? - Für meinen 1600pixel breiten Monitor sind die Titel eh n bissl kurz... :roll: Hab probleme mit den augen in der richtigen Zeile zu bleiben wenn ich nach rechts rübergucke. Aber als neuling hab ich heut glaub genug unruhe verbreitet. :roll: :D
Antworten