当前位置--> 首 页 --> 文 章 -->Linux Develop
|
|
※阅读文章※ |
GTK入门导引--20. 写出属於您自己的物件作者:不祥 [文章出自: www.fanqiang.com] -------------------------------------------------------------------------------- 20. 写出属於您自己的物件 20.1 概说 虽然GTK的物件基本上是够用了, 但有时您还是需要产生自己所需要的物件型态. 如果已经有一个既存的物件很接近您的需求, 那麽您可以把程式改个几行就可以达到您的需求了. 但在您决定要写一个新的物件之前, 先确认是否有人已经写过了. 这会避免重复浪费资源, 并保持物件数量达到最少, 这会使程式及介面比较统一一点. 另一方面, 一旦您写好您的物件, 要向全世界公告, 这样其它人才会受益. 最好的公告地点大概就是gtk-list了. 20.2 物件的解析 为了要产生一个新的物件, 了解GTK的运作是很重要的. 这里只简单的说一下. 详细请参照reference documentation. GTK物件是以流行的物件导件的观念来设计的. 不过, 依然是以C来写的. 比起用C++来说, 这可以大大改善可移植性及稳定性. 但同时, 这也意味著widget writer需要小心许多实作上的问题. 所有同一类别的物件的一般资讯 (例如所有的按钮物件)是放在 class structure. 只有一份这样的结构. 在这份结构中储存类别信号的资讯. 要支撑这样的继承, 第一栏的资料结构必须是其父类别的资料结构. 例如GtkButton的类别宣告看起来像这样: 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); }; 当一个按钮被看成是个container时(例如, 当它被缩放时), 其类别结构可被传到GtkContainerClass, 而其相关的栏位被用来处理信号. 对每个物件结构来说, 都有一些状况上的不同. 该结构都有一些资讯是不太一样的. 我们称此结构为object structure. 如按钮一类, 看起来像这样: struct _GtkButton { GtkContainer container; GtkWidget *child; guint in_button : 1; guint button_down : 1; }; 可以看到, 第一栏是其父类别的物件资料结构, 因此该结构可以传到其父类别的物件结构来处理. 20.3 产生一个组合物件 标头档 每个物件类别都有一个标头档来宣告其物件, 类别结构及其函数. 有些特性是值得指出的. 要避免重复宣告, 我们将整个标头档包成: #ifndef __TICTACTOE_H__ #define __TICTACTOE_H__ . . . #endif /* __TICTACTOE_H__ */ 而且加入让C++程式不会抓狂的定义码: #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ . . . #ifdef __cplusplus } #endif /* __cplusplus */ 除了函数及结构外, 我们宣告了三个标准巨集在标头档中 TICTACTOE(obj), TICTACTOE_CLASS(klass), 及IS_TICTACTOE(obj), 当我们传入一个指标到物件或类别结构中, 它会检查是否是我们的tictactoe物件. 这里是完整的标头档: #ifndef __TICTACTOE_H__ #define __TICTACTOE_H__ #include #include #ifdef __cplusplus extern "C" { #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__ */ _get_type()函数. 我们现在来继续做我们的物件. 对每个物件来说, 都有一个重要的核心函数 WIDGETNAME_get_type(). 这个函数, 当第一次被呼叫的时候, 会告诉GTK有关该物件类别, 并取得一个ID来辨视其物件类别. 在其後的呼叫中, 它会返回该ID. guint tictactoe_get_type () { static guint ttt_type = 0; if (!ttt_type) { GtkTypeInfo ttt_info = { "Tictactoe", sizeof (Tictactoe), sizeof (TictactoeClass), (GtkClassInitFunc) tictactoe_class_init, (GtkObjectInitFunc) tictactoe_init, (GtkArgFunc) NULL, }; ttt_type = gtk_type_unique (gtk_vbox_get_type (), &ttt_info); } return ttt_type; } GtkTypeInfo结构有以下定义: struct _GtkTypeInfo { gchar *type_name; guint object_size; guint class_size; GtkClassInitFunc class_init_func; GtkObjectInitFunc object_init_func; GtkArgFunc arg_func; }; 这资料结构自我解释的很好. 在此, 我们将会忽略掉arg_func这一栏: 它很重要, 可以允许用来给设定解译式语言来设定, 但大部份相关工作都还没有完成. 一旦GTK被正确的填入该资料结构, 它会知道如何产生某一个特别的物件类别. The _class_init() function WIDGETNAME_class_init()函数启始设定该物件类别的资料, 并设定给该类别信号. enum { TICTACTOE_SIGNAL, LAST_SIGNAL }; static gint tictactoe_signals[LAST_SIGNAL] = { 0 }; static void tictactoe_class_init (TictactoeClass *class) { GtkObjectClass *object_class; object_class = (GtkObjectClass*) class; tictactoe_signals[TICTACTOE_SIGNAL] = gtk_signal_new ("tictactoe", GTK_RUN_FIRST, object_class->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->tictactoe = NULL; } 该函数只有一个信号, ``tictactoe''信号. 并非所有组合式物件都需要信号, 所以如果这是您第一次读这里, 您可以跳到下一个, 因为这里有点复杂. gint gtk_signal_new (gchar *name, GtkSignalRunType run_type, gint object_type, gint function_offset, GtkSignalMarshaller marshaller, GtkArgType return_val, gint nparams, ...); 产生新讯号, 参数包含: name: 信号名称. run_type: 决定内定的处理器要在使用者的处理器之前处理或之後处理. 一般可以是GTK_RUN_FIRST, or GTK_RUN_LAST. object_type: 物件的ID. function_offset: 在类别结构中内定处理器函数位址值在记忆体中的偏移值. marshaller: 用来触发信号处理器的函数. 对除了使用者资料外, 没有额外参数的的信号处理器来说, 我们可以用内定的marshaller函数 gtk_signal_default_marshaller. return_val: 返回值的型态. nparams: 信号处理器的参数数量. (不同於以上所提的两个) ...: 参数型态. 当指定型态时, 可用GtkArgType: 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; The _init() function. static void tictactoe_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<3; i++) for (j=0;j<3; j++) { ttt->buttons[i][j] = gtk_toggle_button_new (); gtk_table_attach_defaults (GTK_TABLE(table), ttt->buttons[i][j], i, i+1, j, j+1); gtk_signal_connect (GTK_OBJECT (ttt->buttons[i][j]), "toggled", GTK_SIGNAL_FUNC (tictactoe_toggle), ttt); gtk_widget_set_usize (ttt->buttons[i][j], 20, 20); gtk_widget_show (ttt->buttons[i][j]); } } GtkWidget* tictactoe_new () { return GTK_WIDGET ( gtk_type_new (tictactoe_get_type ())); } void tictactoe_clear (Tictactoe *ttt) { int i,j; for (i=0;i<3;i++) for (j=0;j<3;j++) { gtk_signal_handler_block_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt); gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (ttt->buttons[i][j]), FALSE); gtk_signal_handler_unblock_by_data (GTK_OBJECT(ttt->buttons[i][j]), ttt); } } static void tictactoe_toggle (GtkWidget *widget, Tictactoe *ttt) { int i,k; static int rwins[8][3] = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 } }; static int cwins[8][3] = { { 0, 1, 2 }, { 0, 1, 2 }, { 0, 1, 2 }, { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 }, { 0, 1, 2 }, { 2, 1, 0 } }; int success, found; for (k=0; k<8; k++) { success = TRUE; found = FALSE; for (i=0;i<3;i++) { success = success && GTK_TOGGLE_BUTTON(ttt->buttons[rwins[k][i]][cwins[k][i]])->active; found = found || ttt->buttons[rwins[k][i]][cwins[k][i]] == widget; } if (success && found) { gtk_signal_emit (GTK_OBJECT (ttt), tictactoe_signals[TICTACTOE_SIGNAL]); break; } } } 最後, 使用Tictactoe widget的范例程式: #include #include "tictactoe.h" /* Invoked when a row, column or diagonal is completed */ void win (GtkWidget *widget, gpointer data) { g_print ("Yay!\n"); tictactoe_clear (TICTACTOE (widget)); } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *ttt; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "Aspect Frame"); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC (gtk_exit), NULL); gtk_container_border_width (GTK_CONTAINER (window), 10); /* Create a new Tictactoe widget */ ttt = tictactoe_new (); gtk_container_add (GTK_CONTAINER (window), ttt); gtk_widget_show (ttt); /* And attach to its "tictactoe" signal */ gtk_signal_connect (GTK_OBJECT (ttt), "tictactoe", GTK_SIGNAL_FUNC (win), NULL); gtk_widget_show (window); gtk_main (); return 0; } 20.4 从草稿中产生物件. 基本 我们的物件看起来会有点像Tictactoe物件. /* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __GTK_DIAL_H__ #define __GTK_DIAL_H__ #include #include #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #define GTK_DIAL(obj) GTK_CHECK_CAST (obj, gtk_dial_get_type (), GtkDial) #define GTK_DIAL_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_dial_get_type (), GtkDialClass) #define GTK_IS_DIAL(obj) GTK_CHECK_TYPE (obj, gtk_dial_get_type ()) typedef struct _GtkDial GtkDial; typedef struct _GtkDialClass GtkDialClass; struct _GtkDial { GtkWidget widget; /* update policy (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */ guint policy : 2; /* Button currently pressed or 0 if none */ guint8 button; /* Dimensions of dial components */ gint radius; gint pointer_width; /* ID of update timer, or 0 if none */ guint32 timer; /* Current angle */ gfloat angle; /* Old values from adjustment stored so we know when something changes */ gfloat old_value; gfloat old_lower; gfloat old_upper; /* The adjustment object that stores the data for this dial */ GtkAdjustment *adjustment; }; struct _GtkDialClass { GtkWidgetClass parent_class; }; GtkWidget* gtk_dial_new (GtkAdjustment *adjustment); guint gtk_dial_get_type (void); GtkAdjustment* gtk_dial_get_adjustment (GtkDial *dial); void gtk_dial_set_update_policy (GtkDial *dial, GtkUpdateType policy); void gtk_dial_set_adjustment (GtkDial *dial, GtkAdjustment *adjustment); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __GTK_DIAL_H__ */ 在您产生视窗後, 我们设定其型态及背景, 并放指标到物件的GdkWindow使用者资料栏上 最後一步允许GTK来分派事件给各别的物件. static void gtk_dial_realize (GtkWidget *widget) { GtkDial *dial; GdkWindowAttr attributes; gint attributes_mask; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_DIAL (widget)); GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); dial = GTK_DIAL (widget); attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.window_type = GDK_WINDOW_CHILD; attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK; attributes.visual = gtk_widget_get_visual (widget); attributes.colormap = gtk_widget_get_colormap (widget); attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask); widget->style = gtk_style_attach (widget->style, widget->window); gdk_window_set_user_data (widget->window, widget); gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE); } 大小的设定 在所有视窗被显示出来之前, GTK会先问每个子物件的大小. 该事件是由gtk_dial_size_request()所处理的. 既然我们的物件不是container物件, 而且没什麽大小约束, 就用个合理的数字就行了. static void gtk_dial_size_request (GtkWidget *widget, GtkRequisition *requisition) { requisition->width = DIAL_DEFAULT_SIZE; requisition->height = DIAL_DEFAULT_SIZE; } 最後所有物件都有理想的大小. 一般会尽可能用原定大小, 但使用者会改变它的大小. 大小的改变是由gtk_dial_size_allocate(). static void gtk_dial_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GtkDial *dial; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_DIAL (widget)); g_return_if_fail (allocation != NULL); widget->allocation = *allocation; if (GTK_WIDGET_REALIZED (widget)) { dial = GTK_DIAL (widget); gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height); dial->radius = MAX(allocation->width,allocation->height) * 0.45; dial->pointer_width = dial->radius / 5; } } . gtk_dial_expose() 就如之前所提到的一样, 所有物件的绘出都是由expose事件来处理. 没什麽可多提的, 除了用gtk_draw_polygon 来画出三维阴影. static gint gtk_dial_expose (GtkWidget *widget, GdkEventExpose *event) { GtkDial *dial; GdkPoint points[3]; gdouble s,c; gdouble theta; gint xc, yc; gint tick_length; gint i; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); if (event->count > 0) return FALSE; dial = GTK_DIAL (widget); gdk_window_clear_area (widget->window, 0, 0, widget->allocation.width, widget->allocation.height); xc = widget->allocation.width/2; yc = widget->allocation.height/2; /* Draw ticks */ for (i=0; i<25; i++) { theta = (i*M_PI/18. - M_PI/6.); s = sin(theta); c = cos(theta); tick_length = (i%6 == 0) ? dial->pointer_width : dial->pointer_width/2; gdk_draw_line (widget->window, widget->style->fg_gc[widget->state], xc + c*(dial->radius - tick_length), yc - s*(dial->radius - tick_length), xc + c*dial->radius, yc - s*dial->radius); } /* Draw pointer */ s = sin(dial->angle); c = cos(dial->angle); points[0].x = xc + s*dial->pointer_width/2; points[0].y = yc + c*dial->pointer_width/2; points[1].x = xc + c*dial->radius; points[1].y = yc - s*dial->radius; points[2].x = xc - s*dial->pointer_width/2; points[2].y = yc - c*dial->pointer_width/2; gtk_draw_polygon (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, points, 3, TRUE); return FALSE; } 事件处理 最後一段程式处理各种事件, 跟我们之前所做的没有什麽太大的不同. 有两种事件会发生, 使用者滑鼠的动作及其它因素所造成的物件参数调整. 当使用者在物件上按钮时, 我们检查是否靠近我们的指标, 如果是, 将资料存到button一栏, 并用gtk_grab_add()将所有滑鼠事件抓住. 接下来的滑鼠的动作将会被gtk_dial_update_mouse所接管.. 接下来就看我们是如何做的, "value_changed"事件可以用(GTK_UPDATE_CONTINUOUS)来产生, 或用gtk_timeout_add()来延迟一下(GTK_UPDATE_DELAYED), 或仅在按钮按下时反应(GTK_UPDATE_DISCONTINUOUS). static gint gtk_dial_button_press (GtkWidget *widget, GdkEventButton *event) { GtkDial *dial; gint dx, dy; double s, c; double d_parallel; double d_perpendicular; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); dial = GTK_DIAL (widget); /* Determine if button press was within pointer region - we do this by computing the parallel and perpendicular distance of the point where the mouse was pressed from the line passing through the pointer */ dx = event->x - widget->allocation.width / 2; dy = widget->allocation.height / 2 - event->y; s = sin(dial->angle); c = cos(dial->angle); d_parallel = s*dy + c*dx; d_perpendicular = fabs(s*dx - c*dy); if (!dial->button && (d_perpendicular < dial->pointer_width/2) && (d_parallel > - dial->pointer_width)) { gtk_grab_add (widget); dial->button = event->button; gtk_dial_update_mouse (dial, event->x, event->y); } return FALSE; } static gint gtk_dial_button_release (GtkWidget *widget, GdkEventButton *event) { GtkDial *dial; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); dial = GTK_DIAL (widget); if (dial->button == event->button) { gtk_grab_remove (widget); dial->button = 0; if (dial->policy == GTK_UPDATE_DELAYED) gtk_timeout_remove (dial->timer); if ((dial->policy != GTK_UPDATE_CONTINUOUS) && (dial->old_value != dial->adjustment->value)) gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); } return FALSE; } static gint gtk_dial_motion_notify (GtkWidget *widget, GdkEventMotion *event) { GtkDial *dial; GdkModifierType mods; gint x, y, mask; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_DIAL (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); dial = GTK_DIAL (widget); if (dial->button != 0) { x = event->x; y = event->y; if (event->is_hint || (event->window != widget->window)) gdk_window_get_pointer (widget->window, &x, &y, &mods); switch (dial->button) { case 1: mask = GDK_BUTTON1_MASK; break; case 2: mask = GDK_BUTTON2_MASK; break; case 3: mask = GDK_BUTTON3_MASK; break; default: mask = 0; break; } if (mods & mask) gtk_dial_update_mouse (dial, x,y); } return FALSE; } static gint gtk_dial_timer (GtkDial *dial) { g_return_val_if_fail (dial != NULL, FALSE); g_return_val_if_fail (GTK_IS_DIAL (dial), FALSE); if (dial->policy == GTK_UPDATE_DELAYED) gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); return FALSE; } static void gtk_dial_update_mouse (GtkDial *dial, gint x, gint y) { gint xc, yc; gfloat old_value; g_return_if_fail (dial != NULL); g_return_if_fail (GTK_IS_DIAL (dial)); xc = GTK_WIDGET(dial)->allocation.width / 2; yc = GTK_WIDGET(dial)->allocation.height / 2; old_value = dial->adjustment->value; dial->angle = atan2(yc-y, x-xc); if (dial->angle < -M_PI/2.) dial->angle += 2*M_PI; if (dial->angle < -M_PI/6) dial->angle = -M_PI/6; if (dial->angle > 7.*M_PI/6.) dial->angle = 7.*M_PI/6.; dial->adjustment->value = dial->adjustment->lower + (7.*M_PI/6 - dial->angle) * (dial->adjustment->upper - dial->adjustment->lower) / (4.*M_PI/3.); if (dial->adjustment->value != old_value) { if (dial->policy == GTK_UPDATE_CONTINUOUS) { gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); } else { gtk_widget_draw (GTK_WIDGET(dial), NULL); if (dial->policy == GTK_UPDATE_DELAYED) { if (dial->timer) gtk_timeout_remove (dial->timer); dial->timer = gtk_timeout_add (SCROLL_DELAY_LENGTH, (GtkFunction) gtk_dial_timer, (gpointer) dial); } } } } static void gtk_dial_update (GtkDial *dial) { gfloat new_value; g_return_if_fail (dial != NULL); g_return_if_fail (GTK_IS_DIAL (dial)); new_value = dial->adjustment->value; if (new_value < dial->adjustment->lower) new_value = dial->adjustment->lower; if (new_value > dial->adjustment->upper) new_value = dial->adjustment->upper; if (new_value != dial->adjustment->value) { dial->adjustment->value = new_value; gtk_signal_emit_by_name (GTK_OBJECT (dial->adjustment), "value_changed"); } dial->angle = 7.*M_PI/6. - (new_value - dial->adjustment->lower) * 4.*M_PI/3. / (dial->adjustment->upper - dial->adjustment->lower); gtk_widget_draw (GTK_WIDGET(dial), NULL); } static void gtk_dial_adjustment_changed (GtkAdjustment *adjustment, gpointer data) { GtkDial *dial; g_return_if_fail (adjustment != NULL); g_return_if_fail (data != NULL); dial = GTK_DIAL (data); if ((dial->old_value != adjustment->value) || (dial->old_lower != adjustment->lower) || (dial->old_upper != adjustment->upper)) { gtk_dial_update (dial); dial->old_value = adjustment->value; dial->old_lower = adjustment->lower; dial->old_upper = adjustment->upper; } } static void gtk_dial_adjustment_value_changed (GtkAdjustment *adjustment, gpointer data) { GtkDial *dial; g_return_if_fail (adjustment != NULL); g_return_if_fail (data != NULL); dial = GTK_DIAL (data); if (dial->old_value != adjustment->value) { gtk_dial_update (dial); dial->old_value = adjustment->value; } } 有可能的增强之处 这个Dial物件到目前为止有670行. 这看起来好像有不少了, 不过我们真正完成的只有一点点, 因为大部份都是标头及模子. 还是有许多可以加强的地方: 如果您试过这个物件, 您会发现滑鼠指标会一闪一闪的. 这是因为整个物件每次都重画一次. 当然了最好的方式是在offscreen pixmap上画完以後, 然後整个复制到萤幕上. 使用者应该可以用up及down按键来增加或减少其值. 如果有个按钮来增加或减少其值, 那是再好不过的了. 虽然可也以用embedded Button widgets来做, 但我们会想要按钮有auto-repeat的功能. 所有要做这一类功能的程式可以在GtkRange物件中发现. 这个Dial物件可再做进一个container物件, 带有一个子物件, 位於按钮与最下面之间. 使用者可以增加一个标签或整个物件来显示目前的值. 20.5 更多一点 关於产生一个新的物件的细部资讯在以上被提供出来. 如果您想要写一个属於自己的物件, 我想最好的范例就是GTK本身了. 问问您自己一些关於您想要写的物件: 它是否是个Container物件? 它是否有自己的视窗? 是否是个现有物件的修改? 找出一个相近的物件, 然後开始动工. 祝好运! 20.6 版权 This section on of the tutorial on writing widgets is Copyright (C) 1997 Owen Taylor This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -------------------------------------------------------------------------------- 文章加入时间: 2004-11-17 14:56:29 责任编辑: w9 (2571 人次查阅) |