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

GTK+ Discussion :

Problème "assertion GTK_IS_WIDGET (widget) failed" avec gtk_widget_queue_draw_area()


Sujet :

GTK+

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Homme Profil pro
    chercheur
    Inscrit en
    Décembre 2012
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : chercheur

    Informations forums :
    Inscription : Décembre 2012
    Messages : 195
    Par défaut Problème "assertion GTK_IS_WIDGET (widget) failed" avec gtk_widget_queue_draw_area()
    Bonjour à tous,

    Je bute sur une problème que je ne sais pas résoudre (GTK+2).

    J'ai bati un code exemple minimal qui déclanche le problème. Le voici:
    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
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    #include <stdio.h>
    #include <cairo.h>
    #include <gtk/gtk.h>
    #include <glib.h>
     
    gint pos_x=500,pos_y=500;
    GtkWidget *window;
    GtkWidget *drawarea; /* drawing area */
    GRand *tirage; /* a random generator */
     
    gint grid[1001][1001]; /* content of the grid  */
     
    int main(int argc, char *argv[])
    {
        gboolean on_expose_event(GtkWidget *widget, GdkEvent *event, gpointer userdata);
        gboolean update(gpointer);
        void OnDestroy(GtkWidget *pWidget, gpointer pData); /* function call back destroy */
        void initialise(int);
     
        gtk_init(&argc, &argv);
     
        /* main window creation */
        window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
        gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
     
        /* random generator creation */
        tirage=g_rand_new();
     
        /* drawing points in the grid */
        initialise(200);
     
        /* drawing area creation */
        drawarea=gtk_drawing_area_new();
        gtk_widget_set_size_request(drawarea, (gint)1200, (gint)1200);
     
        /* adding the drawarea to the window */
        gtk_container_add (GTK_CONTAINER(window), drawarea);
     
        g_signal_connect(G_OBJECT(drawarea), "expose_event", G_CALLBACK(on_expose_event), NULL);
        g_signal_connect(window, "destroy", G_CALLBACK(OnDestroy), NULL);
        g_timeout_add((guint)10, update, NULL); /* called function at regular intervals */
        gtk_widget_show_all(window);
        gtk_main();
        return 0;
    }
     
    void OnDestroy(GtkWidget *pWidget, gpointer pData)
    {
        g_rand_free(tirage);
        gtk_main_quit();
    }
     
    void initialise(int food)
    {
        int i,j,flag,tmp_x,tmp_y;
        for (i=1;i<=1000;i++)
        {
            for (j=1;j<=1000;j++)
            {
                grid[i][j]=0;
            }
        }
     
        /* drawing randomly locations in the grid */
        for (i=1;i<=food;i++)
        {
            flag=1;
            while (flag)
            {
                 /* drawing an empty cell in the grid */
                tmp_x=(int)(g_rand_double(tirage)*1000.)+1;
                tmp_y=(int)(g_rand_double(tirage)*1000.)+1;
                if (grid[tmp_x][tmp_y]==0)
                    flag=0;
            }
            grid[tmp_x][tmp_y]=1;
        }
    }
    gboolean on_expose_event(GtkWidget *widget, GdkEvent *event, gpointer userdata)
    {
        int i, j;
        cairo_t *cr=NULL;
        cr=gdk_cairo_create(drawarea->window);
        cairo_set_source_rgb(cr, .0, .0, .0);
        cairo_rectangle(cr, 10.,10.,1000.,1000.);
     
        /* drawing  items in the grid  */
        for (i=1;i<=1000;i++)
        {
            for (j=1;j<=1000;j++)
            {
                if (grid[i][j]==1)
                {
                    cairo_move_to(cr,(double)i+5.+10.,(double)j+10.);
                    cairo_arc(cr,(double)i+10.,(double)j+10., 5., 0.,6.28);
                }
            }
        }
        cairo_stroke(cr);
        cairo_destroy(cr);
        return FALSE;
    }
     
    gboolean update(gpointer pData)
    {
        /* invalidating a zone around pos_x, pos-y to force redrawing by the callback function link to expose-event */
        gtk_widget_queue_draw_area(drawarea, pos_x-40,pos_y-40,80,80);
        return TRUE;
    }
    Ce code tourne bien, sauf que en sortie, j'ai le message suivant :
    Gtk-CRITICAL **: gtk_widget_queue_draw_area: assertion 'GTK_IS_WIDGET (widget)' failed

    Je précise que ce code en soit ne présente aucun intérêt. Il fait un update toutes les 10 millisecondes, alors que rien ne change. Cependant, dans le véritable code que je développe, il y aura des choses qui changeront, d'où la nécessité de faire un rafraichissement rapide.

    Enfin, ce code tire au hasard 200 points sur une grille (voir ligne 30). En revanche, si je ne tire que 100 points, le problème disparait. Ca ressemble à un problème d'allocation de taille de tableau, mais je ne vois pas où, et surtout pourquoi ça serait gtk_widget_queue_draw_area() qui poserait problème dans ce cas, comme le message d'erreur l'indique ? C'est peut-être une erreur d'étourderie de ma part, mais je ne vois pas où..

    Merci pour toute aide sur ce point.

    Cordialement, Eric.

  2. #2
    Membre confirmé
    Homme Profil pro
    chercheur
    Inscrit en
    Décembre 2012
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : chercheur

    Informations forums :
    Inscription : Décembre 2012
    Messages : 195
    Par défaut
    Bon,

    A force de chercher, j'ai fini par trouver le problème. Il suffit de rajouter un cairo_stroke() de manière régulière dans la construction de la chaine d'instructions de dessin. J'imagine que ceci vider égulièrement la pile d'instructions, et que - sans ça - cette pile devient trop grosse et va "empiéter" dans la pile des données..

    Le fait que le message d'erreur indiquait gtk_widget_queue_draw_area() comme la source du problème était vraiment trompeur..

    Problème résolu, donc,

    Désolé pour le dérangement,

    Eric.

  3. #3
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Par défaut
    Bonjour,

    je viens de voir ton code, et tu n'es quand même pas venu pour rien, parce qu'il y a quand même plusieurs soucis.

    1. Pourquoi utiliser GTK+ 2, alors que GTK+ 3 est sorti en 2011 et est stable depuis des années.
    2. Si tu veux dessiner avec cairo, je te conseille aussi GTK+ 3 car il te fournit directement le contexte cairo. Tu n'as plus à le recréer/détruire sans cesse. En GTK+ 3, on dessine en réponse au signal "draw", "expose-event" a disparu.
    3. Tu utilises gtk_widget_queue_draw_area, mais dans ton expose-event, tu redessines tout à chaque image. Vu la quantité de cases (1 million), c'est complètement sous-optimal, surtout si tu comptes raffraîchir ça toutes les 10ms. Je te conseille de réécrire ta calback on_expose_event en regardant dans le GdkEvent la zone qui a été invalidée. Tu peux alors te restreindre à ne redessiner que le strict nécessaire.
    4. "GTK_IS_WIDGET (widget) failed", ça te dit que le pointeur passé ne pointe pas vers un widget, ce qui signifie souvent que la mémoire en question a été écrasée ou que le widget a déjà été détruit.
    5. N'utilise pas g_timeout_add toutes les 10ms pour forcer un rafraichissement. À la place, tu dois avoir une API qui te permet de manipuler ta grille (modifier des éléments dedans), et après chaque modification de la grille, tu appelles gtk_widget_queue_draw_area (voire éventuellement gtk_widget_queue_draw_region) avec juste la zone correspondant à la case à redessiner. Actuellement tu utilises massivement le CPU pour redessiner des choses qui ne changent pas.

  4. #4
    Membre confirmé
    Homme Profil pro
    chercheur
    Inscrit en
    Décembre 2012
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : chercheur

    Informations forums :
    Inscription : Décembre 2012
    Messages : 195
    Par défaut
    Citation Envoyé par liberforce Voir le message
    Bonjour,

    je viens de voir ton code, et tu n'es quand même pas venu pour rien, parce qu'il y a quand même plusieurs soucis.

    [*]Pourquoi utiliser GTK+ 2, alors que GTK+ 3 est sorti en 2011 et est stable depuis des années.
    C'est une bonne question et nous avons déjà discuté plusieurs fois de ceci sur ce forum. La raison est que j'ai la flemme (1) d'investir du temps pour apprendre ce qui a changé entre GTK+2 et GTK+3; et surtout (2) de reprendre et corriger tous mes codes pour que ca refonctionne bien (et j'en ai un paquet, et certains sont vraiment gros).. Mais bon, je sais que je suis criticable ici.

    Citation Envoyé par liberforce Voir le message
    Si tu veux dessiner avec cairo, je te conseille aussi GTK+ 3 car il te fournit directement le contexte cairo. Tu n'as plus à le recréer/détruire sans cesse. En GTK+ 3, on dessine en réponse au signal "draw", "expose-event" a disparu.
    Je crois que la réponse à ce commentaire est la même qu'au commentaire précédent.
    Citation Envoyé par liberforce Voir le message
    Tu utilises gtk_widget_queue_draw_area, mais dans ton expose-event, tu redessines tout à chaque image. Vu la quantité de cases (1 million), c'est complètement sous-optimal, surtout si tu comptes raffraîchir ça toutes les 10ms. Je te conseille de réécrire ta calback on_expose_event en regardant dans le GdkEvent la zone qui a été invalidée. Tu peux alors te restreindre à ne redessiner que le strict nécessaire.
    Là c'est une vraie question, et qui montre qu'il y a quelque chose que je n'ai pas compris, peut-être. gtk_widget_queue_draw_area() utilise explicitement la zone (rectangluaire) qui doit être ré-argumentée. J'imagine que seulement cette zone est donc redessinée, sans quoi pourquoi doit-on définir cette zone ici si tout est redessiné à chaque fois ? Il y a t'il un point ici (important) qui m'échappe ?
    Citation Envoyé par liberforce Voir le message
    "GTK_IS_WIDGET (widget) failed", ça te dit que le pointeur passé ne pointe pas vers un widget, ce qui signifie souvent que la mémoire en question a été écrasée ou que le widget a déjà été détruit.
    Oui, c'est ce que j'avais donc bien compris, comme je l'ai expliqué dans ma réponse précédente.
    Citation Envoyé par liberforce Voir le message
    [*]N'utilise pas g_timeout_add toutes les 10ms pour forcer un rafraichissement. À la place, tu dois avoir une API qui te permet de manipuler ta grille (modifier des éléments dedans), et après chaque modification de la grille, tu appelles gtk_widget_queue_draw_area (voire éventuellement gtk_widget_queue_draw_region) avec juste la zone correspondant à la case à redessiner. Actuellement tu utilises massivement le CPU pour redessiner des choses qui ne changent pas.
    Oui, mais dans l'application que je suis en train de développer, il y modification vraiment tout le temps. J'ai besoin d'une telle cadence (énorme je sais) de rafraichissement.

    Dans tous les cas, merci pour tes avis et ta disponibilité.

    Eric.

  5. #5
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Par défaut
    Citation Envoyé par eric1708 Voir le message
    C'est une bonne question et nous avons déjà discuté plusieurs fois de ceci sur ce forum. La raison est que j'ai la flemme (1) d'investir du temps pour apprendre ce qui a changé entre GTK+2 et GTK+3; et surtout (2) de reprendre et corriger tous mes codes pour que ca refonctionne bien (et j'en ai un paquet, et certains sont vraiment gros).. Mais bon, je sais que je suis criticable ici.
    Désolé, je radote, mais bon, écrire du code pour une plateforme obsolète... Les fonctionnalités de dessin "à la main" comme tu fais sont aussi juste plus simple à gérer dans GTK+ 3 puisque l'intégration de cairo est nettement meilleure... Plus besoin de faire des aller-retours entre contextes GDK et cairo.

    Citation Envoyé par eric1708 Voir le message
    Là c'est une vraie question, et qui montre qu'il y a quelque chose que je n'ai pas compris, peut-être. gtk_widget_queue_draw_area() utilise explicitement la zone (rectangluaire) qui doit être ré-argumentée. J'imagine que seulement cette zone est donc redessinée, sans quoi pourquoi doit-on définir cette zone ici si tout est redessiné à chaque fois ? Il y a t'il un point ici (important) qui m'échappe ?
    Clariement oui, le toolkit ne fait pas de magie. Tu es responsable de l'image que tu dessines dans une GtkDrawingArea. Dans un toolkit graphique (j'ai fait des MFC sous Windows il y a longtemps, c'était pareil), quand tu dessines toi même à l'écran, tu indiques les zones à metre à jour. Cette information ne sert qu'à être transmise dans l'expose-event, pour que tu puisses récupérer les dans l'événement Gdk qui t'est passé (le GdkEvent que tu reçois en paramètre dans ta callback est un GdkEventExpose). Dans cet événement, le membre 'area' est un GdkRectangle qui correspond à la zone à redessiner, et créé à partir des informations que tu as toi même passé à gtk_widget_queue_draw_area. C'est cela que tu dois utiliser pour déduire quelles cases de ta grille doivent être mises à jour à l'écran, et tu ne redessines que celles là. Je te conseille de lire les tutoriels de dessin en GTK+ 2 et surtout de lancer gtk-demo et de regarder le code des exemples de dessin associé.

    Citation Envoyé par eric1708 Voir le message
    Oui, mais dans l'application que je suis en train de développer, il y modification vraiment tout le temps. J'ai besoin d'une telle cadence (énorme je sais) de rafraichissement.
    Je t'assure que non, tu n'as pas besoin de cette cadence . Tu en veux la preuve ? Un raffraichissement toutes les 10ms correspond à une fréquence de 100 images par secondes (100fps) . Ton écran doit sans doute rafraîchir à 60Hz, donc au mieux tu gaspilles de la puissance de calcul pour rien puisque tu ne pourrais pas afficher toutes les images que tu as calculées, au pire tu charges ton système tellement que tu auras 3 images par seconde... Ce n'est pas du temps réel, si tu écroule le système, tu as beau demander à g_timeout de te réveiller dans 10ms, il ne te le garantit pas. Je le sais, j'ai déjà fait une appli de ce genre en python + GTK+ 2 il y a 10 ans pour un projet de recherche: mise à jour de points dans une grille pour affichage sur une interface graphique déportée. C'était pour de la détection de forme dans un passage RATP. Les fonction de clipping de cairo m'avaient été très utile aussi dans ce contexte pour ne dessiner que le strict nécessaire. Tout ce que tu fais en trop, c'est du temps processeur en moins pour ton algo.

  6. #6
    Membre confirmé
    Homme Profil pro
    chercheur
    Inscrit en
    Décembre 2012
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : chercheur

    Informations forums :
    Inscription : Décembre 2012
    Messages : 195
    Par défaut
    Citation Envoyé par liberforce Voir le message
    Désolé, je radote, mais bon, écrire du code pour une plateforme obsolète... Les fonctionnalités de dessin "à la main" comme tu fais sont aussi juste plus simple à gérer dans GTK+ 3 puisque l'intégration de cairo est nettement meilleure... Plus besoin de faire des aller-retours entre contextes GDK et cairo.
    Non, tu ne radotes pas et tu as raison. Mais - comme je l'ai expliqué - je n'ai juste pas le temps pour investir dans l'apprentissage des nouvelles caractéristiques de GTK+3 par rapport à GTK+2, et surtout pour reprendre et modifier tous mes codes. J'ai mon sujet de recherche à développer. Coder est un moyen, pas un but pour moi.
    Citation Envoyé par liberforce Voir le message
    Clariement oui, le toolkit ne fait pas de magie. Tu es responsable de l'image que tu dessines dans une GtkDrawingArea. Dans un toolkit graphique (j'ai fait des MFC sous Windows il y a longtemps, c'était pareil), quand tu dessines toi même à l'écran, tu indiques les zones à metre à jour. Cette information ne sert qu'à être transmise dans l'expose-event, pour que tu puisses récupérer les dans l'événement Gdk qui t'est passé (le GdkEvent que tu reçois en paramètre dans ta callback est un GdkEventExpose). Dans cet événement, le membre 'area' est un GdkRectangle qui correspond à la zone à redessiner, et créé à partir des informations que tu as toi même passé à gtk_widget_queue_draw_area. C'est cela que tu dois utiliser pour déduire quelles cases de ta grille doivent être mises à jour à l'écran, et tu ne redessines que celles là. Je te conseille de lire les tutoriels de dessin en GTK+ 2 et surtout de lancer gtk-demo et de regarder le code des exemples de dessin associé.
    Ok, je comprends. Je vais tacher d'améliorer ceci. Deux remarques, cependant:
    1) Dans l'exemple que je donne au début de ce post, certes il y a une grille de 1000x1000 à argumenter (donc effectivement un million de cases), mais seulement 200 objets sont à dessiner, ce qui n'est pas énorme, et ca tourne de manière fluide sur ma machine.
    2) J'ai pas mal bataillé pour apprendre à me servir de cairo, et la raison est que les tutoriels sont assez rares sur la toile, je trouve. Des exemples seraient effectivement bienvenus..
    Citation Envoyé par liberforce Voir le message
    Je t'assure que non, tu n'as pas besoin de cette cadence . Tu en veux la preuve ? Un raffraichissement toutes les 10ms correspond à une fréquence de 100 images par secondes (100fps) . Ton écran doit sans doute rafraîchir à 60Hz, donc au mieux tu gaspilles de la puissance de calcul pour rien puisque tu ne pourrais pas afficher toutes les images que tu as calculées, au pire tu charges ton système tellement que tu auras 3 images par seconde... Ce n'est pas du temps réel, si tu écroule le système, tu as beau demander à g_timeout de te réveiller dans 10ms, il ne te le garantit pas. Je le sais, j'ai déjà fait une appli de ce genre en python + GTK+ 2 il y a 10 ans pour un projet de recherche: mise à jour de points dans une grille pour affichage sur une interface graphique déportée. C'était pour de la détection de forme dans un passage RATP. Les fonction de clipping de cairo m'avaient été très utile aussi dans ce contexte pour ne dessiner que le strict nécessaire. Tout ce que tu fais en trop, c'est du temps processeur en moins pour ton algo.
    Oui, bon point. 10 millisecondes est une cadence trop élevée. Cette remarque est pertinente. Je vais réduire, ce qui déchargera la cpu de ma machine.

    Encore merci pour ton temps et tes avis, très utiles.

    Cordialement, Eric.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. problème d' "assertion" avec GTK+
    Par poutch dans le forum Langage
    Réponses: 5
    Dernier message: 02/05/2011, 21h42
  2. Problème "LoadModule ssl_module modules/mod_ssl.so"
    Par ldcarpathes dans le forum Apache
    Réponses: 9
    Dernier message: 24/01/2008, 11h07
  3. [Problème] Accès aux propriétés de listes nommées avec []
    Par VincentL dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 05/10/2005, 16h13
  4. Problème unit CRT pour Faire du Pascal avec Delphi
    Par alexmorel dans le forum Débuter
    Réponses: 4
    Dernier message: 01/06/2004, 17h13

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