GTK3实现自定义列表

⌚Time: 2023-08-21 15:33:30

👨‍💻Author: Jack Ge

在gtk中,使用GtkTreeView控件和GtkCellRenderer完全可以实现在列表中显示图片、按钮、进度条等各种内容。实现自定义的列表。

自己定义列表还有更加麻烦的办法,就是自己设计一个列表容器,将每个列表项作为一个hbox,整个列表是一个vbox。通过对容器动态的添加删除,实现列表操作,同时添加任何自己所需要的控件。

下面的例子是实现一个显示图片、按钮和进度条的列表,并且进行上移下移,具有添加和删除列表项功能但没有演示

首先定义一个滚动窗口、全局链表用于储存列表项的容器,一个vbox代表列表容器,一个记录当前选择的列表项变量




GtkWidget *scrolled = NULL;

static GSList *g_itemList = NULL;

static GtkWidget *g_vBox = NULL;

static GtkWidget *selectedItem = NULL;

初始化容器、各种变量实例化


//初始化

void item_list_init(){



    g_vBox = gtk_vbox_new(true,0);

    g_itemList = g_slist_alloc();

    scrolled = gtk_scrolled_window_new(NULL,NULL);

    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);

    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled),g_vBox);

}

列表项界面的清空函数


//清空界面上的列表显示

void item_list_clear(){

    //移除列表

    GList *iter = gtk_container_get_children(GTK_CONTAINER(g_vBox));

    for(;iter;iter=iter->next){

        gtk_container_remove(GTK_CONTAINER(g_vBox),(GtkWidget*)iter->data);

    }

}


重新载入列表功能






void item_list_reload(){

    item_list_clear();//清空界面

    GSList *iter = NULL;

    for(iter = g_itemList;iter;iter=iter->next){

        gtk_container_add(GTK_CONTAINER(g_vBox),(GtkWidget*)iter->data);

    }

}

新建列表项的功能函数,返回一个GtkWidget,实际上是一个水平容器,包含用户指定的图片路径和进度条进度、和按钮




//新建列表项

GtkWidget *new_item(gchar *imagePath, float process){

    GtkWidget *hBox = gtk_hbox_new(false,0);

    GtkWidget *image;

    GdkPixbuf *pixbuf,*dstPixbuf;

    GtkWidget *progressbar;



    pixbuf = gdk_pixbuf_new_from_file(imagePath,NULL);

    //调整图像大小

    dstPixbuf = gdk_pixbuf_scale_simple(pixbuf,60,60,GDK_INTERP_BILINEAR);

    //从像素缓冲区中建立图像

    image = gtk_image_new_from_pixbuf(dstPixbuf);

    //释放缓冲区

    g_object_unref(pixbuf);

    g_object_unref(dstPixbuf);



    gtk_box_pack_start(GTK_BOX(hBox),image,false,false,5);

    GtkWidget *button = gtk_button_new_with_label(imagePath);

    g_signal_connect(button,"clicked",G_CALLBACK(item_selected),NULL);

    gtk_box_pack_start(GTK_BOX(hBox),button,true,true,3);



    progressbar = gtk_progress_bar_new();

    // 设置进度条的值

    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressbar), process);

    gtk_box_pack_start(GTK_BOX(hBox),progressbar,true,true,3);





    g_object_ref(hBox);//增加部件引用计数,防止被移除容器后销毁



    gtk_widget_show_all(hBox);

    return hBox;

}

动态的新增列表项的方法




//添加列表项到列表末尾

void item_list_insert_end(GtkWidget *widget){



    if(g_itemList->data == NULL){//判断第一个元素是不是被使用(第一次添加到第一个元素)



        g_itemList->data = widget;

    }else{

        g_slist_append(g_itemList, widget);

    }

    item_list_reload();

}

彻底清空界面列表和数据


void item_list_remove_all(){

    //移除列表

    GList *iter = gtk_container_get_children(GTK_CONTAINER(g_vBox));

    for(;iter;iter=iter->next){

        gtk_container_remove(GTK_CONTAINER(g_vBox),(GtkWidget*)iter->data);

        g_object_unref(iter->data);//销毁列表项

    }

    //清空数据

    g_slist_free(g_itemList);

    g_itemList=NULL;

    //g_slist_free_full(g_itemList,(GDestroyNotify)gtk_widget_destroy);

    item_list_reload();

}

实现列表项上移下移的办法,原理很简单,就是将要上移或者下移的,上下两个链表节点的data数据,也就是指向不同hbox容器的指针互相交换值就可以了。之后刷新界面显示




//列表项上移

void item_list_move_up(){

    g_return_if_fail(GTK_IS_WIDGET(selectedItem));//检查部件有效

    gint index = g_slist_index(g_itemList,selectedItem);

    if(index == 0){

        return;//已是最上层

    }

    gpointer ptemp = g_slist_nth(g_itemList,index)->data;

    g_slist_nth(g_itemList,index)->data = g_slist_nth(g_itemList,index-1)->data;

    g_slist_nth(g_itemList,index-1)->data = ptemp;



    //刷新列表显示

    item_list_reload();

}

//列表项下移

void item_list_move_down(){

    g_return_if_fail(GTK_IS_WIDGET(selectedItem));//检查部件有效

    gint index = g_slist_index(g_itemList,selectedItem);

    if(index >= g_slist_length(g_itemList)-1){

        return;//已是最下层

    }

    gpointer ptemp = g_slist_nth(g_itemList,index)->data;

    g_slist_nth(g_itemList,index)->data = g_slist_nth(g_itemList,index+1)->data;

    g_slist_nth(g_itemList,index+1)->data = ptemp;



    //刷新列表显示

    item_list_reload();

}

移除选中项




//彻底移除选中项

void item_list_remove_selected(){

    g_return_if_fail(GTK_IS_WIDGET(selectedItem));//检查部件有效

    g_slist_remove(g_itemList,selectedItem);

    item_list_reload();

}

整体的源文件

a.cpp


#include <gtk/gtk.h>

#include "a.h"

GtkWidget *scrolled = NULL;

static GSList *g_itemList = NULL;

static GtkWidget *g_vBox = NULL;

static GtkWidget *selectedItem = NULL;

//初始化

void item_list_init(){



    g_vBox = gtk_vbox_new(true,0);

    g_itemList = g_slist_alloc();

    scrolled = gtk_scrolled_window_new(NULL,NULL);

    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);

    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled),g_vBox);

}

//清空界面上的列表显示

void item_list_clear(){

    //移除列表

    GList *iter = gtk_container_get_children(GTK_CONTAINER(g_vBox));

    for(;iter;iter=iter->next){

        gtk_container_remove(GTK_CONTAINER(g_vBox),(GtkWidget*)iter->data);

    }

}



//重新载入列表

void item_list_reload(){

    item_list_clear();//清空界面

    GSList *iter = NULL;

    for(iter = g_itemList;iter;iter=iter->next){

        gtk_container_add(GTK_CONTAINER(g_vBox),(GtkWidget*)iter->data);

    }

}

//call back

void item_selected(GtkWidget* widget,gpointer data){

    GtkWidget* item = gtk_widget_get_parent(widget);

    selectedItem = item;

}



//新建列表项

GtkWidget *new_item(gchar *imagePath, float process){

    GtkWidget *hBox = gtk_hbox_new(false,0);

    GtkWidget *image;

    GdkPixbuf *pixbuf,*dstPixbuf;

    GtkWidget *progressbar;



    pixbuf = gdk_pixbuf_new_from_file(imagePath,NULL);

    //调整图像大小

    dstPixbuf = gdk_pixbuf_scale_simple(pixbuf,60,60,GDK_INTERP_BILINEAR);

    //从像素缓冲区中建立图像

    image = gtk_image_new_from_pixbuf(dstPixbuf);

    //释放缓冲区

    g_object_unref(pixbuf);

    g_object_unref(dstPixbuf);



    gtk_box_pack_start(GTK_BOX(hBox),image,false,false,5);

    GtkWidget *button = gtk_button_new_with_label(imagePath);

    g_signal_connect(button,"clicked",G_CALLBACK(item_selected),NULL);

    gtk_box_pack_start(GTK_BOX(hBox),button,true,true,3);



    progressbar = gtk_progress_bar_new();

    // 设置进度条的值

    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progressbar), process);

    gtk_box_pack_start(GTK_BOX(hBox),progressbar,true,true,3);





    g_object_ref(hBox);//增加部件引用计数,防止被移除容器后销毁



    gtk_widget_show_all(hBox);

    return hBox;

}



//添加列表项到列表末尾

void item_list_insert_end(GtkWidget *widget){



    if(g_itemList->data == NULL){//判断第一个元素是不是被使用(第一次添加到第一个元素)



        g_itemList->data = widget;

    }else{

        g_slist_append(g_itemList, widget);

    }

    item_list_reload();

}





//彻底清空界面列表和数据

void item_list_remove_all(){

    //移除列表

    GList *iter = gtk_container_get_children(GTK_CONTAINER(g_vBox));

    for(;iter;iter=iter->next){

        gtk_container_remove(GTK_CONTAINER(g_vBox),(GtkWidget*)iter->data);

        g_object_unref(iter->data);//销毁列表项

    }

    //清空数据

    g_slist_free(g_itemList);

    g_itemList=NULL;

    //g_slist_free_full(g_itemList,(GDestroyNotify)gtk_widget_destroy);

    item_list_reload();

}

//列表项上移

void item_list_move_up(){

    g_return_if_fail(GTK_IS_WIDGET(selectedItem));//检查部件有效

    gint index = g_slist_index(g_itemList,selectedItem);

    if(index == 0){

        return;//已是最上层

    }

    gpointer ptemp = g_slist_nth(g_itemList,index)->data;

    g_slist_nth(g_itemList,index)->data = g_slist_nth(g_itemList,index-1)->data;

    g_slist_nth(g_itemList,index-1)->data = ptemp;



    //刷新列表显示

    item_list_reload();

}

//列表项下移

void item_list_move_down(){

    g_return_if_fail(GTK_IS_WIDGET(selectedItem));//检查部件有效

    gint index = g_slist_index(g_itemList,selectedItem);

    if(index >= g_slist_length(g_itemList)-1){

        return;//已是最下层

    }

    gpointer ptemp = g_slist_nth(g_itemList,index)->data;

    g_slist_nth(g_itemList,index)->data = g_slist_nth(g_itemList,index+1)->data;

    g_slist_nth(g_itemList,index+1)->data = ptemp;



    //刷新列表显示

    item_list_reload();

}

//彻底移除选中项

void item_list_remove_selected(){

    g_return_if_fail(GTK_IS_WIDGET(selectedItem));//检查部件有效

    g_slist_remove(g_itemList,selectedItem);

    item_list_reload();

}


封装的头文件

a.h


#ifndef _A_H_

#define _A_H_

extern GtkWidget *scrolled;

GtkWidget *new_item(gchar *imagePath,float);

void item_list_init();

void item_list_insert_end(GtkWidget *widget);

void item_list_move_up();

void item_list_move_down();

#endif // _A_H_




测试源文件

main.cpp




#include <gtk/gtk.h>

#include "a.h"



void button_up(GtkWidget *button, gpointer data)

{

    item_list_move_up();

}

void button_down(GtkWidget *button, gpointer data)

{

    item_list_move_down();

}



int main(int argc, char *argv[])

{

    GtkWidget *window;

    GtkWidget *vBox,*hBox;

    GtkWidget *buttonUp;

    GtkWidget *buttonDown;

    gtk_init(&argc, &argv);



    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    gtk_window_set_title(GTK_WINDOW(window), "Demo");

    gtk_window_set_default_size(GTK_WINDOW(window), 600, 400);

    gtk_container_set_border_width(GTK_CONTAINER(window), 10);



    vBox = gtk_vbox_new(false,5);

    hBox = gtk_hbox_new(true,5);

    gtk_container_add(GTK_CONTAINER(window),vBox);



    item_list_init();

    GtkWidget *item;

    item = new_item("image.jpg",0.5);

    item_list_insert_end(item);

    item = new_item("image2.jpg",0.2);

    item_list_insert_end(item);

    item = new_item("image1.jpg",0.1);

    item_list_insert_end(item);

    item = new_item("image.jpg",0.3);

    item_list_insert_end(item);

    item = new_item("image2.jpg",0.7);

    item_list_insert_end(item);

    item = new_item("image1.jpg",0.8);

    item_list_insert_end(item);

    item = new_item("image.jpg",0.23);

    item_list_insert_end(item);

    item = new_item("image2.jpg",0.13);

    item_list_insert_end(item);

    gtk_box_pack_start(GTK_BOX(vBox),scrolled,true,true,3);



    buttonUp = gtk_button_new_with_label("up");

    buttonDown = gtk_button_new_with_label("down");

    g_signal_connect(G_OBJECT(buttonUp),"clicked",G_CALLBACK(button_up),NULL);

    g_signal_connect(G_OBJECT(buttonDown),"clicked",G_CALLBACK(button_down),NULL);

    gtk_box_pack_start(GTK_BOX(hBox),buttonUp,false,true,3);

    gtk_box_pack_start(GTK_BOX(hBox),buttonDown,false,true,3);

    gtk_box_pack_start(GTK_BOX(vBox),hBox,false,false,3);



    gtk_widget_show_all(window);

    gtk_main();



    return 0;

}


只是作为一个例子,实现的自定义列表比较简单。达到了能够运行的地步,但可能有一些内存管理的错误。