在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;
}
只是作为一个例子,实现的自定义列表比较简单。达到了能够运行的地步,但可能有一些内存管理的错误。