当前位置--> 首 页 --> 文 章 -->Linux Develop

※阅读文章※

GTK+ FAQ--4. 用GTK+来开发


作者:不祥 [文章出自: www.fanqiang.com]

--------------------------------------------------------------------------------

4. 开发GTK+软体
4.1 我要怎麽样开始? 
在安装GTK+後, 有些东西可以让您轻松上手. 有个GTK+入门导引. 它会介绍如何用C来写软体. 

这份入门导引尚未包含所有关於物件的资讯. 用如何使用的范例程式可看看gtk/testgtk.c (及相关程式), 它包含在GTK+ distribution里面. 看看范例会给您心里有点底子, 那些物件能做什麽事. 


4.2 GTK中的物件有那些? 
GTK+入门导引列出包含以下物件: 

  GtkObject
   +GtkData
   | +GtkAdjustment
   | `GtkTooltips
   `GtkWidget
     +GtkContainer
     | +GtkBin
     | | +GtkAlignment
     | | +GtkEventBox
     | | +GtkFrame
     | | | `GtkAspectFrame
     | | +GtkHandleBox
     | | +GtkItem
     | | | +GtkListItem
     | | | +GtkMenuItem
     | | | | `GtkCheckMenuItem
     | | | |   `GtkRadioMenuItem
     | | | `GtkTreeItem
     | | +GtkViewport
     | | `GtkWindow
     | |   +GtkColorSelectionDialog
     | |   +GtkDialog
     | |   | `GtkInputDialog
     | |   `GtkFileSelection
     | +GtkBox
     | | +GtkButtonBox
     | | | +GtkHButtonBox
     | | | `GtkVButtonBox
     | | +GtkHBox
     | | | +GtkCombo
     | | | `GtkStatusbar
     | | `GtkVBox
     | |   +GtkColorSelection
     | |   `GtkGammaCurve
     | +GtkButton
     | | +GtkOptionMenu
     | | `GtkToggleButton
     | |   `GtkCheckButton
     | |     `GtkRadioButton
     | +GtkCList
     |   `GtkCTree
     | +GtkFixed
     | +GtkList
     | +GtkMenuShell
     | | +GtkMenuBar
     | | `GtkMenu
     | +GtkNotebook
     | +GtkPaned
     | | +GtkHPaned
     | | `GtkVPaned
     | +GtkScrolledWindow
     | +GtkTable
     | +GtkToolbar
     | `GtkTree
     +GtkDrawingArea
     | `GtkCurve
     +GtkEditable
     | +GtkEntry
     | | `GtkSpinButton
     | `GtkText
     +GtkMisc
     | +GtkArrow
     | +GtkImage
     | +GtkLabel
     | | `GtkTipsQuery
     | `GtkPixmap
     +GtkPreview
     +GtkProgressBar
     +GtkRange
     | +GtkScale
     | | +GtkHScale
     | | `GtkVScale
     | `GtkScrollbar
     |   +GtkHScrollbar
     |   `GtkVScrollbar
     +GtkRuler
     | +GtkHRuler
     | `GtkVRuler
     `GtkSeparator
       +GtkHSeparator
       `GtkVSeparator


4.3 GTK+是否是thread safe? 我要如何写一个multi-threaded GTK+软体? 
虽然GTK+, 像许多X toolkits, 不是thread safe, 这并不禁止GTK+开发multi-threaded软体. 

Rob Browning (rlb@cs.utexas.edu)描述在GTK+中使用threading的技巧(稍经整理过): 

基本上有两个方法, 第一个简单, 第二个复杂. 第一个, 您确定所有GTK+ (or X)互动都是由同一个所处理, 只能有一个thread. 任何其它thread想要画什麽东西都要告诉这个"GTK+" thread, 然後让它处理实际的工作. 

第二种方法允许您呼叫GTK+ (or X)函数从许多thread, 但需要小心处理同步问题. 基本的想法是您产生一个X protection mutex, 而且没人可以在第一次要求这个mutex之前呼叫X函数. 

这会造成一点负担, 不过比起完全thread saft GTK+, 它可以让您比较有效率. 您得决定thread locking的型式. 您也必须确认该thread呼叫gtk_main时, gtk_main正好有处理lock. 

下一件事要担心的是, 当您进入gtk_main, 既然您有抓好global mutex, 那麽所有callbacks都会抓住它. 这意味著如果callback有去呼叫到会要求这个mutex时, 该callback必须释放它. 否则它会打死结. 而且, 当返回时, 您必须再去抓这个mutex. 

为了要使除了gtk_main以外其它的threads可以存取mutex, 我们也需要注册一个工作函数以让我们定期可以释放这个mutex. 

GTK+干嘛不内定thread safe? 

复杂, 难懂, 及人力资源. threaded程式比例是很小的, 要让thread safety正确会大量浪费宝贵的资源, 而这资源可用来做许多主要的工作. 在GTK+之外要让GTK+ thread safe倒是不错, 但这不是目前的工作. 而且处理不当, 它也会造成许多GTK+的东西比较没有效率. 

不管如何, 这不是个很重要的工作, 因为已经有很多很好的相关工作存在了. 


4.4 当我改变多个物件时要如何避免重画及重定大小? 
使用gtk_container_disable_resize及gtk_container_enable_resize 在您改变许多东西的部份前後加上. 这会加快许多速度, 因为它会避免重画及重定大小. 


4.5 要如何接取一个double click event (例如在list widget中)? 
Tim Janik写给gtk-list (稍经修改过): 

定义一个signal handler: 


gint
signal_handler_event(GtkWiget *widget, GdkEvenButton *event, gpointer func_data)
{
  if (GTK_IS_LIST_ITEM(widget) &&
       (event->type==GDK_2BUTTON_PRESS ||
        event->type==GDK_3BUTTON_PRESS) ) {
    printf("I feel %s clicked on button %d\",
           event->type==GDK_2BUTTON_PRESS ? "double" : "triple",
           event->button);
  }

  return FALSE;
}

然後接object到该handler: 


{
  /* list, list item init stuff */     

  gtk_signal_connect(GTK_OBJECT(list_item),
                     "button_press_event",
                     GTK_SIGNAL_FUNC(signal_handler_event),
                     NULL);

  /* and/or */

  gtk_signal_connect(GTK_OBJECT(list_item),
                     "button_release_event",
                     GTK_SIGNAL_FUNC(signal_handler_event),
                     NULL);

  /* something else */
}

Owen Taylor wrote也写: 

单一按钮按下会在按下前被收到, 如果您这样做, 将会收到一个"clicked" signal. (这在所有工具中都几乎都这样做, 因为电脑可不怎麽在行於"读心".) 


4.6 在GtkList中要如何找出selection? 

取得selection的程式像这样: 

GList *sel;
sel = GTK_LIST(list)->selection;

这是GList的定义(quoting glist.h): 

typedef struct _GList GList;

struct _GList
{
  gpointer data;
  GList *next;
  GList *prev;
};

GList结构是个很简单的doubly linked lists. 有好几个g_list_*()函数来修改linked list, 都在glib.h. GTK_LIST(MyGtkList)->selection是由gtk_list_*()函数来维护的, 而且不应该被修改. 

GtkList的selection_mode决定GtkList的selection机制, GTK_LIST(AnyGtkList)->selection: 


selection_mode          GTK_LIST()->selection contents
------------------------------------------------------

GTK_SELECTION_SINGLE)   selection is either NULL
                        or contains a GList* pointer
                        for a single selected item.

GTK_SELECTION_BROWSE)   selection is NULL if the list
                        contains no widgets, otherwise
                        it contains a GList* pointer
                        for one GList structure.
GTK_SELECTION_MULTIPLE) selection is NULL if no listitems
                        are selected or a a GList* pointer
                        for the first selected item. that
                        in turn points to a GList structure
                        for the second selected item and so
                        on

GTK_SELECTION_EXTENDED) selection is NULL.

GtkList资料栏结构GTK_LIST(MyGtkList)->selection指向GtkListItem的第一个被选物件. 所以您如果想知道那些被选起来了, 可以用这招: 

初始化设定: 

{
        gchar           *list_items[]={
                                "Item0",
                                "Item1",
                                "foo",
                                "last Item",
                        };
        guint           nlist_items=sizeof(list_items)/sizeof(list_items[0]);
        GtkWidget       *list_item;
        guint           i;

        list=gtk_list_new();
        gtk_list_set_selection_mode(GTK_LIST(list), GTK_SELECTION_MULTIPLE);
        gtk_container_add(GTK_CONTAINER(AnyGtkContainer), list);
        gtk_widget_show (list);

        for (i = 0; i < nlist_items; i++)
        {
                list_item=gtk_list_item_new_with_label(list_items[i]);
                gtk_object_set_user_data(GTK_OBJECT(list_item), (gpointer)i);
                gtk_container_add(GTK_CONTAINER(list), list_item);
                gtk_widget_show(list_item);
        }
}

要知会selection: 

{
        GList   *items;

        items=GTK_LIST(list)->selection;

        printf("Selected Items: ");
        while (items) {
                if (GTK_IS_LIST_ITEM(items->data))
                        printf("%d ", (guint) 
                gtk_object_get_user_data(items->data));
                items=items->next;
        }
        printf("\n");
}

4.7 当文字太长时是否可能换行? 
GTK的行为(no clipping)是导因於它尝试省用X的资源. Label物件没有自己的X window - 它们就是画自己的内容到其父视窗中. 当然可能在画文字前裁切一翻, 不过这会导致速度上的处罚. 

当然可以这样做, 从长期眼光来看, 最好的解决方案是把gtk label改变成有X windows. 短期来看, 要把label widget放进另一个widget, 并且有其自己的视窗 - 一个可行的候选者已经在了 - viewport widget. 


viewport = gtk_viewport (NULL, NULL);
gtk_widget_set_usize (viewport, 50, 25);
gtk_viewport_set_shadow_type (GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
gtk_widget_show(viewport);

label = gtk_label ("a really long label that won't fit");
gtk_container_add (GTK_CONTAINER(viewport), label);
gtk_widget_show (label);

如果您正在这样搞一大票物件, 可以复制gtkviewport.c, 然後拿掉adjustment及shadow功能. (您可以叫它GtkClipper). 


4.8 为什麽当按钮按下时, 内文不动? 这里有patch可以让它变成这样... 
From: Peter Mattis 

按钮按下去, 内容不动的原因, 是因为我不认为这是实际上该发生的事. 我对按钮的看法是人们是正面看下去. 这就是, 使用者介面躺在平面上, 而您是正看下去. 当按钮按下去, 它是离开您. 要完全正确, 我想子物件应该要缩小一点点. 但我看不出有任何理由要移一点点到左边. 记住, 子物件是应该接在按钮上面. 这看起来怪怪的, 好像子物件要离开按钮的表面. 

特别一书, 我的确曾一时做过这个, 但想一想不太妥当就把它移除掉了. 


4.9 要如何在选单中定义一个separation? 
看看 入门导引要怎样产生选单. 而要产生separation line, 只要插入一个空的选项: 


menuitem = gtk_menu_item_new();
gtk_menu_append(GTK_MENU(menu), menuitem);
gtk_widget_show(menuitem);


4.10 要怎麽样把选单放在右边, 像Help? 
用以下这招: 


menu_path = gtk_menu_factory_find (factory,  "/Help");
gtk_menu_item_right_justify(menu_path->widget);

4.11 我要如何使我的视窗modal? / 我要如何使一个单一视窗作用? 
在您产生您的视窗後, 用gtk_grab_add(my_window). 关闭後用gtk_grab_remove(my_window). 


4.12 我的物件怎麽不会更新(例如progressbar)? 

您可能是没有将控制权交回给gtk_main. 大部份绘图更新的动作是放在queue当中, 会被gtk_main所处理. 您可以强迫drawing queue被处理, 用这招: 


while (gtk_events_pending())
        gtk_main_iteration();


以上片段是执行所有等候判断的事件及高优先权的idle函数. 然後立即返回. (drawing是由高优先权的idle函数所完成的). 



--------------------------------------------------------------------------------

文章加入时间: 2004-11-17 14:56:30 责任编辑: w9   (2616 人次查阅)
 
Copyright © 1998-2004 中国PHP联盟 All rights reserved.