⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gtk_tut-20.html

📁 gtk 开发手册和参考文档。 包括gtk glib gdk等
💻 HTML
📖 第 1 页 / 共 3 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Draft//EN"><HTML><HEAD><meta http-equiv="pragma" content="no-cache"><TITLE>GTK导引: 写出属於您自己的物件</TITLE></HEAD><BODY><A HREF="gtk_tut-19.html" tppabs="http://extend.hk.hi.cn/%7ehusuyu/http/beginner/gtk/gtk_tut-19.html"><IMG SRC="prev.gif" tppabs="http://extend.hk.hi.cn/%7ehusuyu/http/beginner/gtk/prev.gif" ALT="Previous"></A><A HREF="gtk_tut-21.html" tppabs="http://extend.hk.hi.cn/%7ehusuyu/http/beginner/gtk/gtk_tut-21.html"><IMG SRC="next.gif" tppabs="http://extend.hk.hi.cn/%7ehusuyu/http/beginner/gtk/next.gif" ALT="Next"></A><A HREF="gtk_tut.html#toc20" tppabs="http://extend.hk.hi.cn/%7ehusuyu/http/beginner/gtk/gtk_tut.html#toc20"><IMG SRC="toc.gif" tppabs="http://extend.hk.hi.cn/%7ehusuyu/http/beginner/gtk/toc.gif" ALT="Contents"></A><HR><H2><A NAME="s20">20. 写出属於您自己的物件</A></H2><P><H2><A NAME="ss20.1">20.1 概说</A></H2><P>虽然GTK的物件基本上是够用了,但有时您还是需要产生自己所需要的物件型态.如果已经有一个既存的物件很接近您的需求,那麽您可以把程式改个几行就可以达到您的需求了.但在您决定要写一个新的物件之前,先确认是否有人已经写过了.这会避免重复浪费资源,并保持物件数量达到最少,这会使程式及介面比较统一一点.另一方面, 一旦您写好您的物件,要向全世界公告,这样其它人才会受益.最好的公告地点大概就是<CODE>gtk-list</CODE>了.<P><H2><A NAME="ss20.2">20.2 物件的解析</A></H2><P>为了要产生一个新的物件, 了解GTK的运作是很重要的.这里只简单的说一下.详细请参照reference documentation.<P>GTK物件是以流行的物件导件的观念来设计的.不过, 依然是以C来写的.比起用C++来说, 这可以大大改善可移植性及稳定性.但同时, 这也意味著widget writer需要小心许多实作上的问题.所有同一类别的物件的一般资讯(例如所有的按钮物件)是放在<EM>class structure</EM>. 只有一份这样的结构.在这份结构中储存类别信号的资讯.要支撑这样的继承,第一栏的资料结构必须是其父类别的资料结构.例如GtkButton的类别宣告看起来像这样:<P><BLOCKQUOTE><CODE><PRE>struct _GtkButtonClass{  GtkContainerClass parent_class;  void (* pressed)  (GtkButton *button);  void (* released) (GtkButton *button);  void (* clicked)  (GtkButton *button);  void (* enter)    (GtkButton *button);  void (* leave)    (GtkButton *button);};</PRE></CODE></BLOCKQUOTE><P><P>当一个按钮被看成是个container时(例如, 当它被缩放时),其类别结构可被传到GtkContainerClass,而其相关的栏位被用来处理信号.<P><P>对每个物件结构来说, 都有一些状况上的不同.该结构都有一些资讯是不太一样的.我们称此结构为<EM>object structure</EM>. 如按钮一类, 看起来像这样:<P><BLOCKQUOTE><CODE><PRE>struct _GtkButton{  GtkContainer container;  GtkWidget *child;  guint in_button : 1;  guint button_down : 1;};</PRE></CODE></BLOCKQUOTE><P><P>可以看到, 第一栏是其父类别的物件资料结构,因此该结构可以传到其父类别的物件结构来处理.<P><H2><A NAME="ss20.3">20.3 产生一个组合物件</A></H2><!--<H3>介绍</H3><P>One type of widget that you might be interested in creating is awidget that is merely an aggregate of other GTK widgets. This type ofwidget does nothing that couldn't be done without creating newwidgets, but provides a convenient way of packaging user interfaceelements for reuse. The FileSelection and ColorSelection widgets inthe standard distribution are examples of this type of widget.<P><P>The example widget that we'll create in this section is the Tictactoewidget, a 3x3 array of toggle buttons which triggers a signal when allthree buttons in a row, column, or on one of the diagonals aredepressed. <P><H3>Choosing a parent class</H3><P>The parent class for a composite widget is typically the containerclass that holds all of the elements of the composite widget. Forexample, the parent class of the FileSelection widget is theDialog class. Since our buttons will be arranged in a table, itmight seem natural to make our parent class the GtkTableclass. Unfortunately, this turns out not to work. The creation of awidget is divided among two functions - a <CODE>WIDGETNAME_new()</CODE>function that the user calls, and a <CODE>WIDGETNAME_init()</CODE> functionwhich does the basic work of initializing the widget which isindependent of the arguments passed to the <CODE>_new()</CODE>function. Descendent widgets only call the <CODE>_init</CODE> function oftheir parent widget. But this division of labor doesn't work well fortables, which when created, need to know the number of rows andcolumns in the table. Unless we want to duplicate most of thefunctionality of <CODE>gtk_table_new()</CODE> in our Tictactoe widget, we hadbest avoid deriving it from GtkTable. For that reason, we derive itfrom GtkVBox instead, and stick our table inside the VBox.//--><P><H3>标头档</H3><P>每个物件类别都有一个标头档来宣告其物件, 类别结构及其函数.有些特性是值得指出的.要避免重复宣告, 我们将整个标头档包成:<P><BLOCKQUOTE><CODE><PRE>#ifndef __TICTACTOE_H__#define __TICTACTOE_H__...#endif /* __TICTACTOE_H__ */</PRE></CODE></BLOCKQUOTE><P>而且加入让C++程式不会抓狂的定义码:<P><BLOCKQUOTE><CODE><PRE>#ifdef __cplusplusextern &quot;C&quot; {#endif /* __cplusplus */...#ifdef __cplusplus}#endif /* __cplusplus */</PRE></CODE></BLOCKQUOTE><P>除了函数及结构外, 我们宣告了三个标准巨集在标头档中<CODE>TICTACTOE(obj)</CODE>,<CODE>TICTACTOE_CLASS(klass)</CODE>, 及<CODE>IS_TICTACTOE(obj)</CODE>, 当我们传入一个指标到物件或类别结构中,它会检查是否是我们的tictactoe物件.<P><P>这里是完整的标头档:<P><BLOCKQUOTE><CODE><PRE>#ifndef __TICTACTOE_H__#define __TICTACTOE_H__#include &lt;gdk/gdk.h&gt;#include &lt;gtk/gtkvbox.h&gt;#ifdef __cplusplusextern &quot;C&quot; {#endif /* __cplusplus */#define TICTACTOE(obj)          GTK_CHECK_CAST (obj, tictactoe_get_type (), Tictactoe)#define TICTACTOE_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, tictactoe_get_type (), TictactoeClass)#define IS_TICTACTOE(obj)       GTK_CHECK_TYPE (obj, tictactoe_get_type ())typedef struct _Tictactoe       Tictactoe;typedef struct _TictactoeClass  TictactoeClass;struct _Tictactoe{  GtkVBox vbox;    GtkWidget *buttons[3][3];};struct _TictactoeClass{  GtkVBoxClass parent_class;  void (* tictactoe) (Tictactoe *ttt);};guint          tictactoe_get_type        (void);GtkWidget*     tictactoe_new             (void);void           tictactoe_clear           (Tictactoe *ttt);#ifdef __cplusplus}#endif /* __cplusplus */#endif /* __TICTACTOE_H__ */</PRE></CODE></BLOCKQUOTE><P><H3><CODE>_get_type()</CODE>函数.</H3><P>我们现在来继续做我们的物件.对每个物件来说, 都有一个重要的核心函数<CODE>WIDGETNAME_get_type()</CODE>. 这个函数, 当第一次被呼叫的时候,会告诉GTK有关该物件类别,并取得一个ID来辨视其物件类别.在其後的呼叫中, 它会返回该ID.<P><BLOCKQUOTE><CODE><PRE>guinttictactoe_get_type (){  static guint ttt_type = 0;  if (!ttt_type)    {      GtkTypeInfo ttt_info =      {        &quot;Tictactoe&quot;,        sizeof (Tictactoe),        sizeof (TictactoeClass),        (GtkClassInitFunc) tictactoe_class_init,        (GtkObjectInitFunc) tictactoe_init,        (GtkArgFunc) NULL,      };      ttt_type = gtk_type_unique (gtk_vbox_get_type (), &amp;ttt_info);    }  return ttt_type;}</PRE></CODE></BLOCKQUOTE><P><P>GtkTypeInfo结构有以下定义:<P><BLOCKQUOTE><CODE><PRE>struct _GtkTypeInfo{  gchar *type_name;  guint object_size;  guint class_size;  GtkClassInitFunc class_init_func;  GtkObjectInitFunc object_init_func;  GtkArgFunc arg_func;};</PRE></CODE></BLOCKQUOTE><P><P>这资料结构自我解释的很好.在此, 我们将会忽略掉<CODE>arg_func</CODE>这一栏:它很重要, 可以允许用来给设定解译式语言来设定,但大部份相关工作都还没有完成.一旦GTK被正确的填入该资料结构,它会知道如何产生某一个特别的物件类别.<P><H3>The <CODE>_class_init()</CODE> function</H3><P><CODE>WIDGETNAME_class_init()</CODE>函数启始设定该物件类别的资料,并设定给该类别信号.<P><BLOCKQUOTE><CODE><PRE>enum {  TICTACTOE_SIGNAL,  LAST_SIGNAL};static gint tictactoe_signals[LAST_SIGNAL] = { 0 };static voidtictactoe_class_init (TictactoeClass *class){  GtkObjectClass *object_class;  object_class = (GtkObjectClass*) class;    tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new (&quot;tictactoe&quot;,                                         GTK_RUN_FIRST,                                         object_class-&gt;type,                                         GTK_SIGNAL_OFFSET (TictactoeClass, tictactoe),                                         gtk_signal_default_marshaller, GTK_ARG_NONE, 0);  gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);  class-&gt;tictactoe = NULL;}</PRE></CODE></BLOCKQUOTE><P><P>该函数只有一个信号, ``tictactoe''信号.并非所有组合式物件都需要信号,所以如果这是您第一次读这里,您可以跳到下一个, 因为这里有点复杂.<P><BLOCKQUOTE><CODE><PRE>gint   gtk_signal_new                     (gchar               *name,                                           GtkSignalRunType     run_type,                                           gint                 object_type,                                           gint                 function_offset,                                           GtkSignalMarshaller  marshaller,                                           GtkArgType           return_val,                                           gint                 nparams,                                           ...);</PRE></CODE></BLOCKQUOTE><P>产生新讯号, 参数包含:<P><UL><LI> <CODE>name</CODE>: 信号名称.</LI><LI> <CODE>run_type</CODE>: 决定内定的处理器要在使用者的处理器之前处理或之後处理.一般可以是<CODE>GTK_RUN_FIRST</CODE>, or <CODE>GTK_RUN_LAST</CODE>.</LI><LI> <CODE>object_type</CODE>: 物件的ID.</LI><LI> <CODE>function_offset</CODE>:在类别结构中内定处理器函数位址值在记忆体中的偏移值.</LI><LI> <CODE>marshaller</CODE>: 用来触发信号处理器的函数.对除了使用者资料外, 没有额外参数的的信号处理器来说,我们可以用内定的marshaller函数<CODE>gtk_signal_default_marshaller</CODE>.</LI><LI> <CODE>return_val</CODE>: 返回值的型态.</LI><LI> <CODE>nparams</CODE>: 信号处理器的参数数量.(不同於以上所提的两个)</LI><LI> <CODE>...</CODE>: 参数型态.</LI></UL><P>当指定型态时, 可用<CODE>GtkArgType</CODE>:<P><BLOCKQUOTE><CODE><PRE>typedef enum{  GTK_ARG_INVALID,  GTK_ARG_NONE,  GTK_ARG_CHAR,  GTK_ARG_SHORT,  GTK_ARG_INT,  GTK_ARG_LONG,  GTK_ARG_POINTER,  GTK_ARG_OBJECT,  GTK_ARG_FUNCTION,  GTK_ARG_SIGNAL} GtkArgType;</PRE></CODE></BLOCKQUOTE><!--<P><P><CODE>gtk_signal_new()</CODE> returns a unique integer identifier for thesignal, that we store in the <CODE>tictactoe_signals</CODE> array, which weindex using an enumeration. (Conventionally, the enumeration elementsare the signal name, uppercased, but here there would be a conflictwith the <CODE>TICTACTOE()</CODE> macro, so we called it <CODE>TICTACTOE_SIGNAL</CODE>instead.<P>After creating our signals, we need to tell GTK to associate oursignals with the Tictactoe class. We do that by calling<CODE>gtk_object_class_add_signals()</CODE>. We then set the pointer whichpoints to the default handler for the ``tictactoe'' signal to NULL,indicating that there is no default action.//--><P><H3>The <CODE>_init()</CODE> function.</H3><!--<P><P>Each widget class also needs a function to initialize the objectstructure. Usually, this function has the fairly limited role ofsetting the fields of the structure to default values. For compositewidgets, however, this function also creates the component widgets.--><P><BLOCKQUOTE><CODE><PRE>static voidtictactoe_init (Tictactoe *ttt){  GtkWidget *table;  gint i,j;    table = gtk_table_new (3, 3, TRUE);  gtk_container_add (GTK_CONTAINER(ttt), table);  gtk_widget_show (table);  for (i=0;i&lt;3; i++)    for (j=0;j&lt;3; j++)      {        ttt-&gt;buttons[i][j] = gtk_toggle_button_new ();        gtk_table_attach_defaults (GTK_TABLE(table), ttt-&gt;buttons[i][j],                                    i, i+1, j, j+1);        gtk_signal_connect (GTK_OBJECT (ttt-&gt;buttons[i][j]), &quot;toggled&quot;,                            GTK_SIGNAL_FUNC (tictactoe_toggle), ttt);        gtk_widget_set_usize (ttt-&gt;buttons[i][j], 20, 20);        gtk_widget_show (ttt-&gt;buttons[i][j]);      }}</PRE></CODE></BLOCKQUOTE><P><!--<H3>And the rest...</H3><P><P>There is one more function that every widget (except for base widgettypes like GtkBin that cannot be instantiated) needs to have - thefunction that the user calls to create an object of that type. This isconventionally called <CODE>WIDGETNAME_new()</CODE>In somewidgets, thought not for the Tictactoe widgets, this function takesarguments, and does some setup based on the arguments. The other twofunctions are specific to the Tictactoe widget. <P><P><CODE>tictactoe_clear()</CODE> is a public function that resets all thebuttons in the widget to the up position. Note the use of<CODE>gtk_signal_handler_block_by_data()</CODE> to keep our signal handler forbutton toggles from being triggered unnecessarily.<P><P><CODE>tictactoe_toggle()</CODE> is the signal handler that is invoked when theuser clicks on a button. It checks to see if there are any winningcombinations that involve the toggled button, and if so, emitsthe "tictactoe" signal.//--><P><BLOCKQUOTE><CODE><PRE>GtkWidget*tictactoe_new (){  return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ()));}void           tictactoe_clear (Tictactoe *ttt){  int i,j;  for (i=0;i&lt;3;i++)    for (j=0;j&lt;3;j++)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -