且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用多个图像GTK2应用程序内存泄漏

更新时间:2023-01-27 15:18:03

文档GtkFixed


  

对于大多数应用程序,你不应该使用这个容器!它使你
  不必了解其他GTK +容器,但它的结果
  在破碎的应用。随着GtkFixed,下面的事情会
  结果截断文字,重叠的窗口小部件,以及其他显示错误...


块引用>从以上报价

我想强调的重叠的小部件的。随着应用程序继续运行,你继续越来越多的图像添加到GtkFixed容器。因为你永远不会从容器中删除图像,它们仍然需要内存,即使你再也看不到他们。

不要把这个太细了点,但你不能看到老照片的原因是,你已经把它们与新的图像。至于GtkFixed部件而言,每次因为应用程序开始你已经添加图像仍然在那里。

要解决这个问题,您需要删除旧的图像。有很多方法,你可以做到这一点。最简单的可能是破坏整个容器,但也有一些并发症,由于还放按钮到该容器中。

更好的解决方案可能是按照从上述报价的建议,不要使用GtkFixed - 或者至少层应用程序,以便GtkFixed仅用于图像(你可以放置GtkFixed外的按钮) 。但如前所述,这可能需要您了解其他GTK +容器(除非你只需要使用第二的GtkFixed容器,而第一GtkFixed容器包含两个按钮和第二个容器来保存图像)。

This is a follow up to my last question regarding GTK2 dealing with resource usage.

The application is properly displaying images, however it now appears to be leaking some memory every time GtkImages are loaded from disk and placed onto a fixed frame for absolute positioning.

I'm using the following basic method called every few seconds to load and display different sets of images.

int DisplaySymbols( GameInfo *m )
{
    // variable declarations removed for brevity 
    // error checking needs to be added
    GtkWidget *image;

    pos_y = 150;
    for( y = 0; y < 3; y++ )
    {
    pos_x = 187;
        for( x = 0; x < 5; x++ )
        {
            image=gtk_image_new_from_file( fileName );
            gtk_fixed_put(GTK_FIXED(frame), image, pos_x, pos_y);
        pos_x += symbols[i].pixel_width;
        }
    pos_y += symbols[i].pixel_height;
    }
    gtk_widget_show_all(window);
    return( 0 );
} 

I've read parts of the GTK+ documentation regarding resource usage and I just can't quite figure out how to use the API calls to prevent memory leaks.

Some thoughts and/or questions I have:

  • Should I create some image holding widgets in the fixed frame to more easily manage the images placed in the frame?
  • What is my code doing or not doing that is causing memory to be leaked? I'm likely not releasing the GtkImage widgets?
  • It seems a bit inefficient to load the images from disk every time I need to use them. How can I read them into memory and then display them in the frame as needed?

Partial source code is as follows:

//Compile me with: gcc -o leak leak.c $(pkg-config --cflags --libs gtk+-2.0 gmodule-2.0)

// include files removed for brevity
/* GTK */
#include <gdk/gdkx.h>
#include <gtk/gtk.h>

typedef struct
{
    unsigned int pixel_width, pixel_height;
    gchar fileName[20];
}symbol_t;

static symbol_t symbols[] =
{
    /* only showing 2 of eight - brevity */
    { 118, 107, "images/LO.jpg" },
    { 118, 107, "images/L1.jpg" }
};

typedef struct
{
    char SpinResult[3][5];      // index of images pointing into symbols struct
}GameInfo;

GameInfo egm;

GtkWidget *frame;       /* for absolute positionining of widgets */
GtkWidget *window;

/**** prototypes ****/
    // remove for brevity
/********************/

// init random number generator
int Init( void )
{
    // create random number - brevity
}

// Determine spin outcome and store into egm.SpinResult array
int DoSpin( GameInfo *egm )
{
    // generate matrix of indexes into symbols structure
    // so we know what symbols to display in the frame
}

int DisplaySymbols( GameInfo *egm )
{
    // variable declarations removed - brevity
    GtkWidget *image;

    pos_y = 150;
    for( y = 0; y < 3; y++ )
    {
        pos_x = 187;
        for( x = 0; x < 5; x++ )
        {
            image = gtk_image_new_from_file( symbols[i].fileName );             
            gtk_fixed_put(GTK_FIXED(frame), image, pos_x, pos_y);
            pos_x += symbols[i].pixel_width;
        }
        pos_y += symbols[i].pixel_height;
    }
    gtk_widget_show_all(window);
    return( 0 );
} 

void btnSpin_clicked(GtkWidget *button, gpointer data)
{
    DoSpin( &egm );
    DisplaySymbols( &egm );
    return;
}

GtkWidget *SetupWindow(gchar *data, const gchar *filename)
{
    // window setup code removed for brevity    
    return(window);
}

int main (int argc, char *argv[])
{
    GtkWidget *btnSpin, *btnExit;
    float spinDelay = 2.0;     

    Init();    
    gtk_init (&argc, &argv);
    window = SetupWindow("Tournament", "images/Midway_Madness_Shell.jpg");
    g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);

    frame = gtk_fixed_new();
    gtk_container_add(GTK_CONTAINER(window), frame);

    btnSpin = gtk_button_new_with_label("Spin");
    gtk_widget_set_size_request(btnSpin, 70, 40);
    gtk_fixed_put(GTK_FIXED(frame), btnSpin, 720, 540);
    g_signal_connect(G_OBJECT( btnSpin ), "clicked", G_CALLBACK(btnSpin_clicked), NULL );

    btnExit = gtk_button_new_with_label("Exit");
    gtk_widget_set_size_request(btnExit, 70, 40);
    gtk_fixed_put(GTK_FIXED(frame), btnExit, 595, 540);
    g_signal_connect(G_OBJECT( btnExit ), "clicked", G_CALLBACK(btnExit_clicked), NULL );

    DoSpin( &egm );
    DisplaySymbols( &egm );

    gtk_widget_show_all(window);
    gtk_main ();
    return( 0 );
}

I would be very grateful for a detailed explanation of how to resolve this problem as well as some example code as I'm having a hard time understanding how to apply what I've read from the GTK2 documentation which appears to just provide the definition of objects and the function calls. A handsome bounty can be provided if needed for a very thorough response.

If it is useful to have all of the code, I've provided it here.

EDIT: The memory leak was fixed in two ways.

  1. destroy and re-create the GtkFixed container before redrawing the images as suggested by nobar.
  2. use a two-dimensional array of pointers to GtkWidget to hold the pointers to each image as they are drawn. When it's time to redraw the images the widget pointers in the two-dimensional array are first destroyed like this:

    int ReleaseImages( void ) { u_int8_t y,x;

    /* release images */
    for( y = 0; y < 3; y++ )
    {
        for( x = 0; x < 5; x++ )
        {
            gtk_widget_destroy( ptrImages[y][x] );
        }
    }
    return( 0 );
    

    }

    Now the image resources are released and new images can be redrawn.

From the documentation of GtkFixed:

For most applications, you should not use this container! It keeps you from having to learn about the other GTK+ containers, but it results in broken applications. With GtkFixed, the following things will result in truncated text, overlapping widgets, and other display bugs...

I would like to emphasize overlapping widgets from the above quote. As your application continues to run, you continue to add more and more images to the GtkFixed container. Since you never remove images from the container, they continue to require memory even though you can no longer see them.

Not to put too fine a point on this, but the reason you can't see the old images is that you have covered them up with new images. As far as the GtkFixed widget is concerned, every image you have added since the application began is still in there.

To fix this, you need to remove the old images. There are many ways that you could do this. The simplest might be to destroy the entire container, but there are some complications with that since you also put the buttons into that container.

The better solution is probably to follow the advice from the above quote and don't use GtkFixed -- or at least layer your application so that GtkFixed is only used for the images (you could place the buttons outside of the GtkFixed). But as noted, this might require you to learn about the other GTK+ containers (unless you just use a second GtkFixed container to hold the images while the first GtkFixed container holds both the buttons and the second container).