• Gtk对于通常的gui程序,大家想做的事就是做一点事件处理(包括各种计算、文件操作等),然后在界面上显示出来


    以下内容全部为个人在linux下编程的经验,为个人原创。如转载请注明出处,并请保持文章完整。

    编写GTK+程序基本思想

    社团 linuxfans 作者 sagaeon

    对于通常的gui程序,大家想做的事就是做一点事件处理(包括各种计算、文件操作等),然后在界面上显示出来。以下分步骤以gtk为例地介绍一些概念上的入门知识。(因所看资料已无法找到,不对之处请大家指出)。

    一我们想完成什么功能及会碰到的问题

    我们先考虑在编写带图形界面的程序时几种情况吧。
    1.你想单击按钮button_dis后在button_dis上显示一个数字
    2. 你想每1秒在按钮button_dis上显示一个数字。
    3..你想用libpcap抓包、分析、并在界面上显示分析结果
    4.做个游戏,通过按銉控制你的动作。

    1 对于第一种情况,我们给个列子看看吧
    代码:

    //gcc pro1.c -o pro1 `pkg-config --cflags --libs gtk+-2.0`

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

    static int  count=1;
    char c[2];
    static void hello( GtkWidget *widget,
                      gpointer   data )
    {
      c[0] = (char)((*((int*)data))+48);
       
      count=(count++)%9;     
      gtk_button_set_label ((GtkButton*)widget,c);
       
    }

    static void destroy( GtkWidget *widget,
                        gpointer   data )
    {
       gtk_main_quit ();
    }

    int main( int   argc,
             char *argv[] )
    {  
       GtkWidget *window;
       GtkWidget *button;

       gtk_init (&argc, &argv);
       /* create a new window */
       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
      
       g_signal_connect (G_OBJECT (window), "destroy",
               G_CALLBACK (destroy), NULL);
       gtk_container_set_border_width (GTK_CONTAINER (window), 20);
       button = gtk_button_new_with_label ("Hello World");
       g_signal_connect (G_OBJECT (button), "clicked",
               G_CALLBACK (hello), NULL);
       gtk_container_add (GTK_CONTAINER (window), button);
       gtk_widget_show (button);
       gtk_widget_show (window);
       gtk_main ();
       return 0;
    }


    然后 gcc pro1.c -o pro1 `pkg-config --cflags --libs gtk+-2.0` 就可以啦
    很容易实现吧。大家花点时间学学基本的gtk编程,这个程序就很好理解啦。我们再来看第二种情况

    2. 大家想是不是可以这样,在button的回调函数中放以下代码: 代码:

    static void hello( GtkWidget *widget,
                      gpointer   data )
    {
       
      int i=0;
      for(;i<5;i++){
        
        c[0] = (char)(count+48);
        gtk_button_set_label ((GtkButton*)widget,c);
        count=(count++)%9;
        sleep(1);
      }
       
    }

    运行一下,结果不是如你所愿,每秒(不太精确的)增加一个数字。观察一下有什么问题?结果并不如愿每秒按钮上的数字自动刷新,而是5秒后才显示5,并且在这期间,窗口就像死了一样,不能响应鼠标事件。要解决这个问题,就要考虑gtk中的gui主循环。
    大多数gui主程序都是一个事件驱动的无限循环。这个主循环会检测一个由下层系统提供的事件队列,从队列中取出事件并处理。处理事件的函数一般叫回调函数。将事件与回调函数联系起来的机制一般叫注册。比如
    g_signal_connect ((gpointer) about1, "activate",
    G_CALLBACK (on_about1_activate),
    NULL);
    先不论第四个参数,这个函数就表示:若在小部件about1上发生activate信号,就调用on_about1_activate函数。这种注册机制其实很好理解,比如你可以用一个表,里面装的就是每个小部件的信号与回调函数对应表。而调用g_signal_connect就是填充这个表。(在gtk中事件与信号有区别。见文章底部的背景资料1)。
    这个主循环(gtk_main()函数)就这样循环检测在本窗口上有无信号和事件发生,并根据你的回调函数响应。这里有个前提就是gtk要不停地热循环,才能及时地响应外部请求。当它调用回调函数时,注意,这段时间它没有进行外部事件的循环检测,所以不能响应外部事件。如果你的回调函数处理时间特别长,自然,你就会看到我们程序中出现的问题。要解决这个问题,我们先看一个简单的方法。
    代码:

    static void hello( GtkWidget *widget,
                      gpointer   data )
    {
       
      int i=0;
      for(;i<5;i++){
        
        c[0] = (char)(count+48);
        gtk_button_set_label ((GtkButton*)widget,c);
        while (gtk_events_pending ())
        gtk_main_iteration ();
        count=(count++)%9;
        sleep(1);
      }
       
    }

    编译,如何,这此行了吧。可是我们还是有问题。
    待续。。。。。。。

    三。如何完成?----MVC结构

    解决办法就是mvc结构,即 model --- view ---controller。使用这个术语的有java、smalltalk(注1)等,《面向对象分析与设计(2e)》Grady Booch用display item object表示与model一样的含义(注2),MFC中用document代表model(《MFC程序设计》)。通过对view与model的分离,我们实现了一个良好的程序框架(注3)。


    注1:《design pattern》英 page 4《面向对象分析与设计2e》page 98
    注2: 《面向对象分析与设计2e》 page 98
    注3:如何分离view与model见《applying UML and patterns 2e》Craig Larman page471
    待补充

    四。model 与 view之间的可选通信机制
    待续.....




    背景资料:

    1.信号与事件在gtk中的区别(为个人学习总结,不具权威性,不对之处请多探讨)
    在X系统中,由下而上关系是Xlib-->GDK-->GTK。我们先看GDK,大家知道, X和GDK都是一个事件驱动系统,也就是说,它们的工作方式就是从底层系统的事件队列中不停取事件,然后进行相应的处理。当然,这个事件队列是更底层的系统产生的(比如GDK的事件队列是从X中取得,X又从....)。这里,事件是什么?是一种结构,这个结构包括许多要素,比如(鼠标单击,在窗口A上,坐标为X,Y)就是一个事件,可见,事件是自包含的,它含有你所想要的全部信息,根据这些信息,你就可以做许多事。比如,在GDK的基础上你可以设计一个主循环,假设你有两个小部件A,B,你想在当鼠标在A上单击时(一个事件)在B上显示“saga"。你可以这样做你的主循环:1.初始化,做一个表,表中有两项,分别为A,B。A为空,B中放两项clicked-->display表示当B收到clicked时display(显示saga)。2.循环:取事件,若a.单击事件发生的地点在A上(看事件这个结构中的位置项,可以是坐标,也可以是对应的窗口),则A向B发出一个"clicked"(记住这句话),B收到"clicked"就display,b.单击不发生在A上,丢掉这个事件,再取下一个事件进行判断,周而复始。看到吗?假设屏幕上有几十个小部件,每个小部件就如A,B那样做一些表,放入自己收到"clicked"(或其它的什么)时要做的事(一个函数),在主循环中查到(根据位置)在某个小部件上发生"clicked"就查表并做相应的事件(函数),它的功能就如同GTK一样强大了。把这当成一个框架,任何人用它,只要填好那个表,再写好收到"clicked"后要执行的函数就可以了,很方便吧,你都可以做系统架构师了。GTK就是这样做的!(当然GTK还做了太多的其它的事,看它的名字"Gimp ToolKit"--画好小部件是它要做的正事)
    将一些词用正式术语替换,这个过程就是这样:主循环收到一个事件(鼠标单击,在窗口A),便由窗口A向B发出一个信号"clicked",B看 (在这儿就是查表了)有没有注册了的回调函数(注册--就是那个g_connect_signal的作用了),有一个注册了的函数,就是display (...),便去执行这个函数(在B上显示saga)。

    大家看到A是向B发一个信号,其实也可以向自己发一个信号,就是这样描述:当有一个发生在A上的单击事件时,A向A发出一个"clicked"信号,A收到后在A上显示"saga"!而且大多数编程人员的实现中,A就是向自己发一个信号,也就是说,常常是经历了某个事件(A经历了单击这个事件,说经历,是因为发生在自己身上)的小部件发出一个给自己的信号并处理这个信号,有点晕?这正是常常把事件与信号混淆的原因!!!

    这样我们就可以总结一些区别了:

    1.信号总是由一个小部件发出的。而事件是下层驱动产生的(当然,大多数情况是由人发出再经过系统、X的包装,传到GDK),它自身带有事件产生的位置信息。
    2.它们之间有许多相似的函数,比如你可以用gtk_widget_set(add)_events,让一个小部件监听一个事件(比如你可以偷听在不同小部件上发生的鼠标单击),就象听一个信号一样,不同的是你可以听到在其它部件上发生的事件,却不能听到不是发给你的信号。
    3.信号有一个传递机制,比如A并没有处理这个信号的回调函数,就会把这个信号向父类传,直到有人处理或到最顶层。而事件始终在事件队列中,你可以向操作系统说(术语就是注册)我关心鼠标单击事件,有了告诉我,你不说,它就不告诉你!
    4.还有许多细节上的差异。

  • 相关阅读:
    Reducing File Size
    程序设计中的命名
    代码进阶
    如何显示当前Mipmap级别?
    何时使用泛型集合
    Using Mono DLLs in a Unity Project
    lock关键字
    VSS/RSS/PSS/USS
    AssetBundle依赖
    WWW.LoadFromCacheOrDownload
  • 原文地址:https://www.cnblogs.com/cy163/p/785341.html
Copyright © 2020-2023  润新知