gtk使用总结

发布于 2019-04-18  92 次阅读


最近在做网络作业,要求实现五子棋,所以自学了下linux下的gui绘图,经过对比,最终选择了gtk(qt的话上机时检查作业不方便)

gtk的基本使用并不是特别复杂,感觉比当初学的windows api要简单

GtkWidget *window;
GtkWidget *fixed;
GtkWidget *drawing_area;
GtkWidget *text_view;
GtkTextBuffer *text_buffer;
GtkTextIter text_iter;
GtkTextIter text_iter2;
    gtk_init(&argc, &argv); //一些初始化操作,放在最前面

    //create window
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL); //创建一个窗体
    g_signal_connect (G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); //
    gtk_window_set_title(GTK_WINDOW(window), "五子棋"); //设置标题
    gtk_widget_set_usize(window, BOARD_WIDTH + 320, BOARD_HEIGHT); //设置窗体大小
    gtk_window_set_resizable(GTK_WINDOW(window), FALSE);//设置大小不可变

    //create drawing ares
    drawing_area = gtk_drawing_area_new();
    gtk_widget_set_size_request(drawing_area, BOARD_WIDTH, BOARD_HEIGHT);//设置大小

    //event
    g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(onDraw), NULL);//当需要重绘时执行ondraw函数
    gtk_widget_add_events(drawing_area, GDK_BUTTON_PRESS_MASK);
    g_signal_connect(GTK_OBJECT(drawing_area), "button_press_event", GTK_SIGNAL_FUNC(onClick), (gpointer) drawing_area);//按钮按下时执行onclick函数

    //textview
    text_view = gtk_text_view_new();
    text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
    gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), false);
    gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text_view), false);
    gtk_widget_set_size_request(text_view, 300, BOARD_HEIGHT);

    //fixed
    fixed = gtk_fixed_new(); //创建固定布局
    gtk_widget_set_usize(fixed, BOARD_WIDTH + 320, BOARD_HEIGHT);//设置大小
    gtk_fixed_put(GTK_FIXED(fixed), text_view, BOARD_WIDTH, 0);//将组件放在指定坐标处
    gtk_fixed_put(GTK_FIXED(fixed), drawing_area, 0, 0);
    gtk_container_add(GTK_CONTAINER(window), fixed);//将固定布局放到窗体上

    gtk_widget_show_all(window);//显示窗体上的所有组件,最好放在组件创建完后执行

    if (!g_thread_supported()) g_thread_init(NULL);
    gdk_threads_init();
    g_thread_create((GThreadFunc) receive_process, NULL, FALSE, NULL);//新开一个线程执行receive_process函数
    gdk_threads_enter();//我也不太清楚,貌似是线程同步用的
    gtk_main();//进入主事件循环
    gdk_threads_leave();

GtkWidget *可指向所有组件,调用函数传参时需要转换类型.GTK_WINDOW等为转换类型的宏,每个组件类型对应的宏就是把字母全大写,单词间加入下划线

g_signal_connect函数的四个参数分别为组件,事件,回调函数,回调函数的参数

gint onDraw(GtkWidget *widget, GdkEventExpose *event, gpointer data) {
    GdkDrawable *canvas;
    GdkGC *gc;
    canvas = widget->window;
    gc = widget->style->fg_gc[GTK_WIDGET_STATE(widget)];

    colors[0].red = 63999;
    colors[0].green = 55039;
    colors[0].blue = 23551;
    gdk_gc_set_rgb_fg_color(gc, &colors[0]);
    for (int i = 0; i < BOARD_HEIGHT; i++)
        gdk_draw_line(canvas, gc, 0, i, BOARD_WIDTH, i);

    colors[0].red = 0;
    colors[0].green = 0;
    colors[0].blue = 0;
    gdk_gc_set_rgb_fg_color(gc, &colors[0]);
    for (int i = GRID_SIZE / 2; i <= BOARD_WIDTH; i += GRID_SIZE)
        gdk_draw_line(canvas, gc, i, GRID_SIZE / 2, i, BOARD_HEIGHT - GRID_SIZE / 2 - 1);
    for (int i = GRID_SIZE / 2; i <= BOARD_HEIGHT; i += GRID_SIZE)
        gdk_draw_line(canvas, gc, GRID_SIZE / 2, i, BOARD_WIDTH - GRID_SIZE / 2 - 1, i);

    for (int i = 0; i < BOARD_ROW; i++) {
        for (int j = 0; j < BOARD_COLUMN; j++) {
            if (board[i][j] == CHEESE_SERVER) {
                gdk_gc_set_rgb_fg_color(gc, &colors[CHEESE_SERVER]);
                gdk_draw_arc(canvas, gc, TRUE, j * GRID_SIZE + 2, i * GRID_SIZE + 2, GRID_SIZE - 4, GRID_SIZE - 4, 0,
                             360 * 64);
            } else if (board[i][j] == CHEESE_CLIENT) {
                gdk_gc_set_rgb_fg_color(gc, &colors[CHEESE_CLIENT]);
                gdk_draw_arc(canvas, gc, TRUE, j * GRID_SIZE + 2, i * GRID_SIZE + 2, GRID_SIZE - 4, GRID_SIZE - 4, 0,
                             360 * 64);
            }
        }
    }
    colors[0].red = 65535;
    colors[0].green = 0;
    colors[0].blue = 0;
    gdk_gc_set_rgb_fg_color(gc, &colors[0]);
    if (last_x != -1) {
        gdk_draw_rectangle(canvas, gc, false, last_y * GRID_SIZE + 2, last_x * GRID_SIZE + 2, GRID_SIZE - 4,
                           GRID_SIZE - 4);
        // gdk_draw_arc(canvas, gc, false, last_y * GRID_SIZE + 1, last_x * GRID_SIZE + 1, GRID_SIZE - 2, GRID_SIZE - 2, 0,360 * 64);
    }


    return TRUE;
}

需要注意gdk_draw_arc的参数,第三个参数为是否填充.第4-7个参数为一个矩形的两点的坐标,而这个圆内切于这个矩形.最后两个参数为起始点和终止点的角度,每64为一度

对于多线程,注意在非界面线程中处理界面,需要将操作放在

gdk_threads_enter();
......
gdk_threads_leave();

中,还有,如果要更新界面,调用gtk_widget_queue_draw(window);函数(这个函数不能在ondraw里使用)

显示消息框的方法:

    GtkWidget *dialog;

    dialog = gtk_message_dialog_new(GTK_WINDOW(window),
                                    GTK_DIALOG_DESTROY_WITH_PARENT,
                                    GTK_MESSAGE_INFO,
                                    GTK_BUTTONS_OK,
                                    message);
    gtk_dialog_run(GTK_DIALOG (dialog));
    gtk_widget_destroy(dialog);

textview的使用方法:(详细用法见api文档https://developer.gnome.org/gtk2/stable/TextWidgetObjects.html)

    text_view = gtk_text_view_new(); //创建textview
    text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));//获取对应的textbuffer

    gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(text_buffer), &text_iter, &text_iter2);//获取起始点textiter和结束点的textiter
    gtk_text_buffer_insert(GTK_TEXT_BUFFER(text_buffer), &text_iter, message, -1);//在textiter处插入内容

编译注意事项

g++ xxx.cpp `pkg-config --cflags --libs gthread-2.0 gtk+-2.0`

clion配置

如果使用clion,需要在CMakeLists.txt下添加:

find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK2 REQUIRED gtk+-2.0)

include_directories(${GTK2_INCLUDE_DIRS})
link_directories(${GTK2_LIBRARY_DIRS})

add_definitions(${GTK2_CFLAGS_OTHER})

target_link_libraries(项目名称 ${GTK2_LIBRARIES})

项目名称即前几行project()里的内容