gtk当中一些好玩的小例子

之前有人问我,为什么要学习gtk?

原因很简单:

首先, GTK是C语言实现的。我们需要明白,对于C程序员而言,大部分都没有接触过图形界面编程,但是对于以后的发展,我们很有必要了解一下图形界面编程。那么问题又来了,我们是学C语言的,我们可能不太乐意学习面向对象语言(如 C++, Java, C#, Objective-C 等),现在市场上流行的图形界面工具库基本上都是用面向对象语言开发的。
再者,对于我们 C 程序员,我们可能只需了解一下图形界面开发过程,并不一定以后从事图形界面开发。所以,GTK是一个很好的选择。
实际上,用C语言开发的图形库还有一个MiniGUI,在国内医疗设备应用非常广泛,相反,GTK在国内基本上没人用。但是,我们学习到的个别知识,以后未必一定能用上,对于我们而言, 重要的是学习方法。如果觉得 MiniGUI 比 GTK 在国内应用广,就想学习 MiniGUI,那我们是否应该学Android,因为它应用更广。技术是不断的更新的,但是万变不离其宗。
其实,学习 MiniGUI 和 GTK 都差不多。区别在于 MiniGUI 是国内开发, GTK 国外开发的。
学习GTK,我们可以了解到图形界面开发的流程是怎么一个过程,这和 Android 应用开发差不多,假如,有那么一天我们真想做Android开发的工作,我们转过去也容易,因为我们有 C 语言的基础,也有图形界面的基础。

接下来补充几个gtk中好玩的一些效果:

1、gtk窗口移动动画

/* $Id: app12.c $
 Re: animating position of a top-level Gtk window
 jiw July 2011 -- Offered without warranty under GPL v3
 terms per http://www.gnu.org/licenses/gpl.html  */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <gtk/gtk.h>
typedef struct DATA { GTimer *timer; GtkWidget *window; int w, h; }
  DataStruct;

gboolean timerEvent(void *dataset) {
  enum { HalfTime=8, CycTime=2*HalfTime };
  gulong micros;
  DataStruct *data =dataset;
  double t = fabs(fmod (g_timer_elapsed (data->timer, &micros),CycTime));
  int x = (t*data->w)/HalfTime, y = (t*data->h)/HalfTime;
  gtk_window_move (GTK_WINDOW(data->window),t<HalfTime? x : 2*data->w-x, t<HalfTime? y : 2*data->h-y);
  return TRUE; /* Keep timeout running */
}

int main(int argc, char **argv) {
  GtkWidget *vbox, *b;
  GdkScreen *gds;
  DataStruct data;
  data.timer = g_timer_new();
  gtk_init (&argc, &argv);
  data.window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_default_size (GTK_WINDOW(data.window), 200, 150);
  g_signal_connect (G_OBJECT(data.window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
  vbox = gtk_vbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER(data.window), vbox);
  b = gtk_button_new_with_label ("Click to Exit");
  gtk_box_pack_start (GTK_BOX(vbox), b, TRUE, TRUE, TRUE);
  g_signal_connect (b, "clicked", G_CALLBACK(gtk_main_quit), NULL);
  gtk_widget_show_all (data.window);

  gds = gdk_screen_get_default ();  /* Get pointer to screen  */
  data.w = gdk_screen_get_width (gds);  /* Find out screen width  */
  data.h = gdk_screen_get_height (gds); /* Find out screen height */
  printf ("Screen size = %d by %d", data.w, data.h); fflush(stdout);
  g_timeout_add(3, timerEvent, &data);  /* Create .003 sec timer   */
  gtk_main();
  return (0);
}

2、gtk窗口中添加动画(动图)

方法1:

#include <gtk/gtk.h>
static GtkWidget *ourgif;
int main(int argc, char **argv)
{
    GtkWidget *window;
    GtkWidget *vbox;
    GtkWidget *hbox;
    GtkWidget *label;
    GtkWidget *image;
    gint num = 0;
    gchar *filename;
    gtk_init(&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(gtk_main_quit), NULL);
 
    vbox = gtk_vbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(window), vbox);
    label = gtk_label_new("直接引用GIF动画");
    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
    image = gtk_image_new_from_file("hh.gif");
    gtk_box_pack_start(GTK_BOX(vbox), image, FALSE, FALSE, 5);
    gtk_widget_show_all(window);
    gtk_main();
    return TRUE;
}

效果:

方法2:参考本人上一篇文章

3、GTK +的多线程动画

#include <gtk/gtk.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>

//the global pixmap that will serve as our buffer
static GdkPixmap *pixmap = NULL;

gboolean on_window_configure_event(GtkWidget * da, GdkEventConfigure * event, gpointer user_data){
    static int oldw = 0;
    static int oldh = 0;
    //make our selves a properly sized pixmap if our window has been resized
    if (oldw != event->width || oldh != event->height){
        //create our new pixmap with the correct size.
        GdkPixmap *tmppixmap = gdk_pixmap_new(da->window, event->width,  event->height, -1);
        //copy the contents of the old pixmap to the new pixmap.  This keeps ugly uninitialized
        //pixmaps from being painted upon resize
        int minw = oldw, minh = oldh;
        if( event->width < minw ){ minw =  event->width; }
        if( event->height < minh ){ minh =  event->height; }
        gdk_draw_drawable(tmppixmap, da->style->fg_gc[GTK_WIDGET_STATE(da)], pixmap, 0, 0, 0, 0, minw, minh);
        //we're done with our old pixmap, so we can get rid of it and replace it with our properly-sized one.
        g_object_unref(pixmap);
        pixmap = tmppixmap;
    }
    oldw = event->width;
    oldh = event->height;
    return TRUE;
}

gboolean on_window_expose_event(GtkWidget * da, GdkEventExpose * event, gpointer user_data){
    gdk_draw_drawable(da->window,
        da->style->fg_gc[GTK_WIDGET_STATE(da)], pixmap,
        // Only copy the area that was exposed.
        event->area.x, event->area.y,
        event->area.x, event->area.y,
        event->area.width, event->area.height);
    return TRUE;
}


static int currently_drawing = 0;
//do_draw will be executed in a separate thread whenever we would like to update
//our animation
void *do_draw(void *ptr){

    //prepare to trap our SIGALRM so we can draw when we recieve it!
    siginfo_t info;
    sigset_t sigset;

    sigemptyset(&sigset);
    sigaddset(&sigset, SIGALRM);

    while(1){
        //wait for our SIGALRM.  Upon receipt, draw our stuff.  Then, do it again!
        while (sigwaitinfo(&sigset, &info) > 0) {

            currently_drawing = 1;

            int width, height;
            gdk_threads_enter();
            gdk_drawable_get_size(pixmap, &width, &height);
            gdk_threads_leave();

            //create a gtk-independant surface to draw on
            cairo_surface_t *cst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
            cairo_t *cr = cairo_create(cst);

            //do some time-consuming drawing
            static int i = 0;
            ++i; i = i % 300;   //give a little movement to our animation
            cairo_set_source_rgb (cr, .9, .9, .9);
            cairo_paint(cr);
            int j,k;
            for(k=0; k<100; ++k){   //lets just redraw lots of times to use a lot of proc power
                for(j=0; j < 1000; ++j){
                    cairo_set_source_rgb (cr, (double)j/1000.0, (double)j/1000.0, 1.0 - (double)j/1000.0);
                    cairo_move_to(cr, i,j/2);
                    cairo_line_to(cr, i+100,j/2);
                    cairo_stroke(cr);
                }
            }
            cairo_destroy(cr);


            //When dealing with gdkPixmap's, we need to make sure not to
            //access them from outside gtk_main().
            gdk_threads_enter();

            cairo_t *cr_pixmap = gdk_cairo_create(pixmap);
            cairo_set_source_surface (cr_pixmap, cst, 0, 0);
            cairo_paint(cr_pixmap);
            cairo_destroy(cr_pixmap);

            gdk_threads_leave();

            cairo_surface_destroy(cst);

            currently_drawing = 0;

        }
    }
}

gboolean timer_exe(GtkWidget * window){
    static int first_time = 1;
    //use a safe function to get the value of currently_drawing so
    //we don't run into the usual multithreading issues
    int drawing_status = g_atomic_int_get(&currently_drawing);

    //if this is the first time, create the drawing thread
    static pthread_t thread_info;
    if(first_time == 1){
        int  iret;
        iret = pthread_create( &thread_info, NULL, do_draw, NULL);
    }

    //if we are not currently drawing anything, send a SIGALRM signal
    //to our thread and tell it to update our pixmap
    if(drawing_status == 0){
        pthread_kill(thread_info, SIGALRM);
    }

    //tell our window it is time to draw our animation.
    int width, height;
    gdk_drawable_get_size(pixmap, &width, &height);
    gtk_widget_queue_draw_area(window, 0, 0, width, height);


    first_time = 0;
    return TRUE;

}


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

    //Block SIGALRM in the main thread
    sigset_t sigset;
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGALRM);
    pthread_sigmask(SIG_BLOCK, &sigset, NULL);

    //we need to initialize all these functions so that gtk knows
    //to be thread-aware
    if (!g_thread_supported ()){ g_thread_init(NULL); }
    gdk_threads_init();
    gdk_threads_enter();

    gtk_init(&argc, &argv);

    GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
    g_signal_connect(G_OBJECT(window), "expose_event", G_CALLBACK(on_window_expose_event), NULL);
    g_signal_connect(G_OBJECT(window), "configure_event", G_CALLBACK(on_window_configure_event), NULL);

    //this must be done before we define our pixmap so that it can reference
    //the colour depth and such
    gtk_widget_show_all(window);

    //set up our pixmap so it is ready for drawing
    pixmap = gdk_pixmap_new(window->window,500,500,-1);
    //because we will be painting our pixmap manually during expose events
    //we can turn off gtk's automatic painting and double buffering routines.
    gtk_widget_set_app_paintable(window, TRUE);
    gtk_widget_set_double_buffered(window, FALSE);

    (void)g_timeout_add(33, (GSourceFunc)timer_exe, window);


    gtk_main();
    gdk_threads_leave();

    return 0;
}

效果:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值