IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Qt Discussion :

Opacité, paintEvent et problèmes d'appels récursifs


Sujet :

Qt

  1. #1
    Membre chevronné Avatar de Jbx 2.0b
    Homme Profil pro
    Développeur C++/3D
    Inscrit en
    Septembre 2002
    Messages
    476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur C++/3D
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2002
    Messages : 476
    Points : 1 787
    Points
    1 787
    Par défaut
    Bonjour à tous,

    J'essaye de redessiner un QPushButton en gardant son aspect original mais changeant l'opacité. Après pas mal de recherche sur le web la solution qui m'a parue la plus correcte est celle qui consiste à faire dériver QPushButton et à redéfinir la méthode paintEvent().
    Malgré tout un problème d'appel récursif se pose (que je comprends bien, mais auquel je ne trouve pas d'alternative). Voici le code incriminé :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    void TranslucentButton::paintEvent(QPaintEvent **event)
    {
        QPainter painter(this);
        painter.setOpacity(0.5);
        this->render(&painter);
    }
    en sortie j'obtiens alors :
    QWidget::repaint: Recursive repaint detected
    Le résultat à l'écran est pourtant lui celui attendu.[EDIT] en fait non, pas du tout.
    Une suggestion ?

    PS : En fait, pour essayer d'expliquer un peu plus ce que je cherche à faire, j'aimerais mettre au point une classe générique "GenericTranslucentWidget" descendant de QWidget afin de pouvoir avoir tout les widgets héritant de cette classe translucides sans avoir à redéfinir constamment paintEvent()

  2. #2
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2009
    Messages
    26
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 26
    Points : 31
    Points
    31
    Par défaut
    Pt que je me trompe mais si dans le créateur tu met dans le css de ton widget principal
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    background: transparent;
    Tous ce qui sera en dessous (tous ce que ce widget contiendra) sera transparent par défaut.

  3. #3
    Membre chevronné Avatar de Jbx 2.0b
    Homme Profil pro
    Développeur C++/3D
    Inscrit en
    Septembre 2002
    Messages
    476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur C++/3D
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2002
    Messages : 476
    Points : 1 787
    Points
    1 787
    Par défaut
    Oui, mais le widget deviendra alors totalement transparent ( avec background-color: transparent; très exactement).
    Or je veux pouvoir régler l'opacité et aussi pouvoir appliquer une image sur certains widgets.

  4. #4
    Membre habitué
    Avatar de bobti89
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    86
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Janvier 2005
    Messages : 86
    Points : 150
    Points
    150
    Par défaut
    Et un simple QWidget::render à la place de this->render donne quoi ?

  5. #5
    Membre chevronné Avatar de Jbx 2.0b
    Homme Profil pro
    Développeur C++/3D
    Inscrit en
    Septembre 2002
    Messages
    476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur C++/3D
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2002
    Messages : 476
    Points : 1 787
    Points
    1 787
    Par défaut
    Même problème. Je donne le code de test pour ceux que ça intéresse. Ici dans le cas d'un bouton :

    translucentboutton.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
     
    #ifndef TRANSLUCENTBUTTON_H
    #define TRANSLUCENTBUTTON_H
     
    #include <QPushButton>
     
    #define WIDGETS_OPACITY 0.8
     
    class TranslucentButton : public QPushButton
    {
    	Q_OBJECT
     
    public:
    	TranslucentButton(QWidget *parent);
    	~TranslucentButton();
     
    protected:
    	virtual void paintEvent (QPaintEvent  *event);
     
     
    };
     
    #endif // TRANSLUCENTBUTTON_H
    translucentboutton.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    #include "translucentbutton.h"
    #include <QPainter>
    #include <QPaintEvent>
     
     
    TranslucentButton::TranslucentButton(QWidget *parent)
    	: QPushButton(parent)
    {
    	setGeometry(50,50,120,120);
    }
     
     
    void TranslucentButton::paintEvent(QPaintEvent  *event)
    {
    	QPainter painter(this);
    	painter.setOpacity(WIDGETS_OPACITY);
    	QWidget::render(&painter);
     
    }

  6. #6
    Membre éprouvé

    Profil pro
    Inscrit en
    Mai 2007
    Messages
    774
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France, Finistère (Bretagne)

    Informations forums :
    Inscription : Mai 2007
    Messages : 774
    Points : 969
    Points
    969
    Par défaut
    Il y a une autre piste à explorer, c'est celle de la gestion des paintEvent() :
    updatesEnabled : bool

    This property holds whether updates are enabled.

    An updates enabled widget receives paint events and has a system background; a disabled widget does not. This also implies that calling update() and repaint() has no effect if updates are disabled.

    By default, this property is true.

    setUpdatesEnabled() is normally used to disable updates for a short period of time, for instance to avoid screen flicker during large changes. In Qt, widgets normally do not generate screen flicker, but on X11 the server might erase regions on the screen when widgets get hidden before they can be replaced by other widgets. Disabling updates solves this.

    Example:

    setUpdatesEnabled(false);
    bigVisualChanges();
    setUpdatesEnabled(true);
    Disabling a widget implicitly disables all its children. Enabling a widget enables all child widgets except top-level widgets or those that have been explicitly disabled. Re-enabling updates implicitly calls update() on the widget.

    Access functions:

    bool updatesEnabled () const
    void setUpdatesEnabled ( bool enable )
    See also paintEvent().
    Peut être qu'en redéfinissant le render(), update() ou repaint() dans ta classe, tu pourras faire ce que tu veux.

    C'est une solution un peu jetée au hasard, sans garantie.

    G.

  7. #7
    Membre chevronné Avatar de Jbx 2.0b
    Homme Profil pro
    Développeur C++/3D
    Inscrit en
    Septembre 2002
    Messages
    476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur C++/3D
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2002
    Messages : 476
    Points : 1 787
    Points
    1 787
    Par défaut
    C'est peut-être une piste, mais je vois pas vraiment comment l'implémenter.
    Plus je creuse et plus j'ai l'impression que ce que je cherche n'est pas possible.
    Je parcours le net depuis 3 jours et j'ai déjà essayé pas mal de chose ( entre autre : CSS, redef de paintEvent(), setWindowOpacity(), setAttribute(Qt::WA_TranslucentBackground, true); ) et chacunes a ses soucis ( en particulier ne pas transmettre l'opacité aux enfants, ou rendre la fenêtre complètement transparente, alors que je veux pouvoir gérer individuellement chaque widget).

    La seule solution que j'entrevoie est celle de redessiner complétement mes Widgets (simple dans le cas d'un QPushButton, un peu plus complexe pour un QMenu et littéralement folle dans le cas d'une QTableWidget).

  8. #8
    Membre chevronné Avatar de Jbx 2.0b
    Homme Profil pro
    Développeur C++/3D
    Inscrit en
    Septembre 2002
    Messages
    476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur C++/3D
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2002
    Messages : 476
    Points : 1 787
    Points
    1 787
    Par défaut
    Il semblerait que c'est toujours quand je perds espoir que je finis par trouver une solution..

    En fait ça semble fonctionner en utilisant QStyleOption. Par exemple, pour reprendre l'exemple vu plus haut:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    void TranslucentButton::paintEvent(QPaintEvent  *event)
    {
    	QStyleOptionButton option;
    	option.initFrom(this);
     
    	option.text = text();
    	option.icon = icon();
     
    	QPainter painter(this);
    	painter.setOpacity(0.5);
    	style()->drawControl(QStyle::CE_PushButton, &option, &painter, this);
     
    }
    Si ça se confirme et que j'arrive à mettre au point une solution générique, je reviendrais la poster sur ce forum. En attendant, tout vos conseils sont bienvenus

  9. #9
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 617
    Points
    15 617
    Par défaut
    Bonjour jbx2004

    Tu devrais regarder du côté de QProxyStyle (http://qt.developpez.com/doc/4.7-snapshot/qproxystyle/) pour faire quelque chose de similaire à ce que j'avais présenté dans ce post : http://www.developpez.net/forums/d90...s/#post5110243


    Tu crées une classe héritant de QProxyStyle qui ajoute surcharge la fonction drawControl en ajoutant la transparence pour les QPushButton :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class TranslucentStyle : public QProxyStyle
     {
     public:
         TranslucentStyle() : QProxyStyle(QStyleFactory::create("TranslucentStyle")) {}
     
         void drawControl(
                 ControlElement element,
                 const QStyleOption  option,
                 QPainter  painter,
                 const QWidget  widget = 0) const
         {
             if(element == QStyle::CE_PushButton)
                 painter->setOpacity(0.5);
     
             QProxyStyle::drawControl(element, option, painter, widget);
         }
     };
    puis tu ajoutes ce style dans ton QApplication :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    QApplication a(argc, argv);
     a.setStyle(new TranslucentStyle());
    Par example, avec ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    MainWindow::MainWindow(QWidget  parent)
         : QMainWindow(parent)
     {
         QLabel   c = new QLabel("bla blabla blabla\nblabla blabla\nblabla blablabla\nblabla");
         setCentralWidget(c);
     
         QPushButton   b = new QPushButton("Mon bouton", this);
         b->move(10,10);
         b->resize(100,40);
     }
    j'obtiens l'image en copie.

    Ce code fonctionne si tu veux que tous tes boutons soient transparents. Sinon, il faut regarder la doc de QStyle pour voir comment créer un type spécifique.


    EDIT : En me relisant, la solution pour tester si tu dois dessiner une classe particulière : il suffit de tester le retour d'un qobject_cast sur le paramètre widget :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class MyTypeDeWidget : public QPushButton
     {
         Q_OBJECT
     public:
         MyTypeDeWidget( const QString & text, QWidget   parent = 0 ) : QPushButton(text, parent) {}
     };
     
     class TranslucentStyle : public QProxyStyle
     {
     public:
         TranslucentStyle() : QProxyStyle(QStyleFactory::create("TranslucentStyle")) {}
     
         void drawControl(
                 ControlElement element,
                 const QStyleOption  option,
                 QPainter  painter,
                 const QWidget  widget = 0) const
         {
             if(qobject_cast<MyTypeDeWidget >(widget))
                 painter->setOpacity(0.5);
     
             QProxyStyle::drawControl(element, option, painter, widget);
         }
     };
    le résultat dans translucide-2.png


    EDIT 2 : la même chose, sans être obligé de créer une classe enfant, en utilisant les propriétés des QObject :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    QPushButton   b = new QPushButton("Mon bouton", this);
     b->setProperty("Translucid", QVariant(0.5));
     
     class TranslucentStyle : public QProxyStyle
     {
     public:
         TranslucentStyle() : QProxyStyle(QStyleFactory::create("TranslucentStyle")) {}
     
         void drawControl(
                 ControlElement element,
                 const QStyleOption  option,
                 QPainter  painter,
                 const QWidget  widget = 0) const
         {
             if(widget->property("Translucid").isValid())
                 painter->setOpacity(widget->property("Translucid").toReal());
     
             QProxyStyle::drawControl(element, option, painter, widget);
         }
     };
    donne le même résultat que translucide-2.png
    Images attachées Images attachées   

  10. #10
    Membre chevronné Avatar de Jbx 2.0b
    Homme Profil pro
    Développeur C++/3D
    Inscrit en
    Septembre 2002
    Messages
    476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur C++/3D
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2002
    Messages : 476
    Points : 1 787
    Points
    1 787
    Par défaut
    Merci beaucoup En fait je commençais déjà à trouver des limites à ma "solution" ^^'

    Je vais étudier tout ça, mais avant j'ai une question : es-ce que l'utilisation de QProxyStyle va entrer en concurrence avec les style sheet ? ou es-ce qu'on peut utiliser les deux simultanément sans aucun soucis ?

  11. #11
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 617
    Points
    15 617
    Par défaut
    Les propriétés que tu vas modifier dans le stylesheet seront perdu pour la transparence. Par exemple, si tu ajoutes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    b->setStyleSheet("QPushButton { color:yellow;}");
    alors le texte ne sera pas transparent mais le reste oui.

    Pour corriger cela (mais je ne suis pas sur que ce soit une bonne idée de mélanger les 2), tu peux :
    - créer un stylesheet conditionnel (voir le post que je t'ai indiqué au dessus)
    - modifier la chaine de caractères définissant le stylesheet en ajoutant la transparence
    - récupérer le stylesheet dans QProxyStyle et modifier en fonction de cela.
    - laisser tomber les stylessheet (les stylesheet sont plus simple que QStyle à utiliser mais à partir du moment où tu définies un QStyle, autant travailler qu'avec ça)

  12. #12
    Membre chevronné Avatar de Jbx 2.0b
    Homme Profil pro
    Développeur C++/3D
    Inscrit en
    Septembre 2002
    Messages
    476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur C++/3D
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2002
    Messages : 476
    Points : 1 787
    Points
    1 787
    Par défaut
    Les propriétés que tu vas modifier dans le stylesheet seront perdu pour la transparence.
    Effectivement, je viens de m'en rendre compte à l'instant.

    - laisser tomber les stylessheet (les stylesheet sont plus simple que QStyle à utiliser mais à partir du moment où tu définies un QStyle, autant travailler qu'avec ça)
    Le but étant de faire quelque chose de propre autant que possible, je vais plutôt m'engager dans cette voie. Dans ce cas je suppose qu'il faut que j'use d'un QPixmap pour habiller mon Widget ?
    Je m'imagine créer une classe qui hérite de chacun des widgets que j'ai à définir, qui prend en paramètre un degré d'opacité ainsi qu'un fichier de conf XML donnant l'image associée ( pour les widgets qui en en ont besoin, en l'occurrence, les boutons).

    Je m'étonne quand même de la complexité que ça peut prendre de vouloir ajouter une opacité paramétrable sur un widget "stylisé". Si le cahier des charges ne spécifiait pas que cette opacité devait être paramétrable, une simple collection de .png supplémentaire aurait réglé le problème :/

    En tout cas merci pour tes réponses, j'apprends beaucoup sur les méandres de Qt :p

  13. #13
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Points : 15 617
    Points
    15 617
    Par défaut
    Je pense que le choix a été la performance : utiliser des stylesheet est plus couteux (en terme de temps et probablement de ressources) que QStyle. Et ajouter la transparence par défaut également (il faut tester pour chaque objet enfant par récurrence, ce qui augmente exponentiellement le temps).

    Les dev ont choisit de transférer la responsabilité de dessiner par défaut les widgets dans des QStyles (et donc de créer plusieurs QStyle en fonction de la plateforme : linux, win, mac)

    Si tu veux voir un "gros" QStyle (et te faire un peu mal aux yeux), tu peux regarder le QStyle utilisé par Qt Creator :
    - http://qt.gitorious.org/qt-creator/q...nhattanstyle.h
    - http://qt.gitorious.org/qt-creator/q...attanstyle.cpp

Discussions similaires

  1. [Lex/Yacc] Problème de typage / appel récursif du parseur de yacc
    Par hitman981 dans le forum Autres langages
    Réponses: 0
    Dernier message: 14/11/2012, 12h01
  2. Problème d'appel de module.
    Par TomPad dans le forum Access
    Réponses: 2
    Dernier message: 23/06/2005, 10h24
  3. Autre contexte mais tjs problème d'appel fct interne
    Par Neilos dans le forum C++Builder
    Réponses: 1
    Dernier message: 28/08/2004, 13h51
  4. [DLL] problème pour appeler une fonction d'une DLL
    Par bigboomshakala dans le forum MFC
    Réponses: 34
    Dernier message: 19/07/2004, 11h30
  5. Réponses: 4
    Dernier message: 19/04/2004, 13h41

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo