?? gtk_tut-22.html
字號:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
<META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.7">
<TITLE>GTK v1.2 Tutorial: Writing Your Own Widgets </TITLE>
<LINK HREF="gtk_tut-23.html" REL=next>
<LINK HREF="gtk_tut-21.html" REL=previous>
<LINK HREF="gtk_tut.html#toc22" REL=contents>
</HEAD>
<BODY TEXT="#CCCCCC" BGCOLOR="#000000" LINK="#33cc00" VLINK="#009900" ALINK="#FF0000">
<A HREF="gtk_tut-23.html">Next</A>
<A HREF="gtk_tut-21.html">Previous</A>
<A HREF="gtk_tut.html#toc22">Contents</A>
<HR>
<H2><A NAME="s22">22. Writing Your Own Widgets </A></H2>
<H2><A NAME="ss22.1">22.1 Overview</A>
</H2>
<P>Although the GTK distribution comes with many types of widgets that
should cover most basic needs, there may come a time when you need to
create your own new widget type. Since GTK uses widget inheritance
extensively, and there is already a widget that is close to what you want,
it is often possible to make a useful new widget type in
just a few lines of code. But before starting work on a new widget, check
around first to make sure that someone has not already written
it. This will prevent duplication of effort and keep the number of
GTK widgets out there to a minimum, which will help keep both the code
and the interface of different applications consistent. As a flip side
to this, once you finish your widget, announce it to the world so
other people can benefit. The best place to do this is probably the
<CODE>gtk-list</CODE>.
<P>Complete sources for the example widgets are available at the place you
got this tutorial, or from:
<P>
<A HREF="http://www.gtk.org/~otaylor/gtk/tutorial/">http://www.gtk.org/~otaylor/gtk/tutorial/</A><P>
<P>
<H2><A NAME="ss22.2">22.2 The Anatomy Of A Widget</A>
</H2>
<P>In order to create a new widget, it is important to have an
understanding of how GTK objects work. This section is just meant as a
brief overview. See the reference documentation for the details.
<P>GTK widgets are implemented in an object oriented fashion. However,
they are implemented in standard C. This greatly improves portability
and stability over using current generation C++ compilers; however,
it does mean that the widget writer has to pay attention to some of
the implementation details. The information common to all instances of
one class of widgets (e.g., to all Button widgets) is stored in the
<EM>class structure</EM>. There is only one copy of this in
which is stored information about the class's signals
(which act like virtual functions in C). To support inheritance, the
first field in the class structure must be a copy of the parent's
class structure. The declaration of the class structure of GtkButtton
looks like:
<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>When a button is treated as a container (for instance, when it is
resized), its class structure can be cast to GtkContainerClass, and
the relevant fields used to handle the signals.
<P>There is also a structure for each widget that is created on a
per-instance basis. This structure has fields to store information that
is different for each instance of the widget. We'll call this
structure the <EM>object structure</EM>. For the Button class, it looks
like:
<P>
<BLOCKQUOTE><CODE>
<PRE>
struct _GtkButton
{
GtkContainer container;
GtkWidget *child;
guint in_button : 1;
guint button_down : 1;
};
</PRE>
</CODE></BLOCKQUOTE>
<P>Note that, similar to the class structure, the first field is the
object structure of the parent class, so that this structure can be
cast to the parent class' object structure as needed.
<P>
<H2><A NAME="ss22.3">22.3 Creating a Composite widget</A>
</H2>
<H3>Introduction</H3>
<P>One type of widget that you may be interested in creating is a
widget that is merely an aggregate of other GTK widgets. This type of
widget does nothing that couldn't be done without creating new
widgets, but provides a convenient way of packaging user interface
elements for reuse. The FileSelection and ColorSelection widgets in
the standard distribution are examples of this type of widget.
<P>The example widget that we'll create in this section is the Tictactoe
widget, a 3x3 array of toggle buttons which triggers a signal when all
three buttons in a row, column, or on one of the diagonals are
depressed.
<P>
<H3>Choosing a parent class</H3>
<P>The parent class for a composite widget is typically the container
class that holds all of the elements of the composite widget. For
example, the parent class of the FileSelection widget is the
Dialog class. Since our buttons will be arranged in a table, it
might seem natural to make our parent class the Table
class. Unfortunately, this turns out not to work. The creation of a
widget is divided among two functions - a <CODE>WIDGETNAME_new()</CODE>
function that the user calls, and a <CODE>WIDGETNAME_init()</CODE> function
which does the basic work of initializing the widget which is
independent of the arguments passed to the <CODE>_new()</CODE>
function. Descendant widgets only call the <CODE>_init</CODE> function of
their parent widget. But this division of labor doesn't work well for
tables, which when created need to know the number of rows and
columns in the table. Unless we want to duplicate most of the
functionality of <CODE>gtk_table_new()</CODE> in our Tictactoe widget, we had
best avoid deriving it from Table. For that reason, we derive it
from VBox instead, and stick our table inside the VBox.
<P>
<H3>The header file</H3>
<P>Each widget class has a header file which declares the object and
class structures for that widget, along with public functions.
A couple of features are worth pointing out. To prevent duplicate
definitions, we wrap the entire header file in:
<P>
<BLOCKQUOTE><CODE>
<PRE>
#ifndef __TICTACTOE_H__
#define __TICTACTOE_H__
.
.
.
#endif /* __TICTACTOE_H__ */
</PRE>
</CODE></BLOCKQUOTE>
<P>And to keep C++ programs that include the header file happy, in:
<P>
<BLOCKQUOTE><CODE>
<PRE>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
.
.
.
#ifdef __cplusplus
}
#endif /* __cplusplus */
</PRE>
</CODE></BLOCKQUOTE>
<P>Along with the functions and structures, we declare three standard
macros in our header file, <CODE>TICTACTOE(obj)</CODE>,
<CODE>TICTACTOE_CLASS(klass)</CODE>, and <CODE>IS_TICTACTOE(obj)</CODE>, which cast a
pointer into a pointer to the object or class structure, and check
if an object is a Tictactoe widget respectively.
<P>Here is the complete header file:
<P>
<BLOCKQUOTE><CODE>
<PRE>
/* tictactoe.h */
#ifndef __TICTACTOE_H__
#define __TICTACTOE_H__
#include <gdk/gdk.h>
#include <gtk/gtkvbox.h>
#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__ */
</PRE>
</CODE></BLOCKQUOTE>
<P>
<H3>The <CODE>_get_type()</CODE> function.</H3>
<P>We now continue on to the implementation of our widget. A core
function for every widget is the function
<CODE>WIDGETNAME_get_type()</CODE>. This function, when first called, tells
GTK about the widget class, and gets an ID that uniquely identifies
the widget class. Upon subsequent calls, it just returns the ID.
<P>
<BLOCKQUOTE><CODE>
<PRE>
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,
(GtkArgSetFunc) NULL,
(GtkArgGetFunc) NULL
};
ttt_type = gtk_type_unique (gtk_vbox_get_type (), &ttt_info);
}
return ttt_type;
}
</PRE>
</CODE></BLOCKQUOTE>
<P>The GtkTypeInfo structure has the following definition:
<P>
<BLOCKQUOTE><CODE>
<PRE>
struct _GtkTypeInfo
{
gchar *type_name;
guint object_size;
guint class_size;
GtkClassInitFunc class_init_func;
GtkObjectInitFunc object_init_func;
GtkArgSetFunc arg_set_func;
GtkArgGetFunc arg_get_func;
};
</PRE>
</CODE></BLOCKQUOTE>
<P>The fields of this structure are pretty self-explanatory. We'll ignore
the <CODE>arg_set_func</CODE> and <CODE>arg_get_func</CODE> fields here: they have an important,
but as yet largely
unimplemented, role in allowing widget options to be conveniently set
from interpreted languages. Once GTK has a correctly filled in copy of
this structure, it knows how to create objects of a particular widget
type.
<P>
<H3>The <CODE>_class_init()</CODE> function</H3>
<P>The <CODE>WIDGETNAME_class_init()</CODE> function initializes the fields of
the widget's class structure, and sets up any signals for the
class. For our Tictactoe widget it looks like:
<P>
<BLOCKQUOTE><CODE>
<PRE>
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_TYPE_NONE, 0);
gtk_object_class_add_signals (object_class, tictactoe_signals, LAST_SIGNAL);
class->tictactoe = NULL;
}
</PRE>
</CODE></BLOCKQUOTE>
<P>Our widget has just one signal, the <CODE>tictactoe</CODE> signal that is
invoked when a row, column, or diagonal is completely filled in. Not
every composite widget needs signals, so if you are reading this for
the first time, you may want to skip to the next section now, as
things are going to get a bit complicated.
<P>The function:
<P>
<BLOCKQUOTE><CODE>
<PRE>
gint gtk_signal_new( const gchar *name,
GtkSignalRunType run_type,
GtkType object_type,
gint function_offset,
GtkSignalMarshaller marshaller,
GtkType return_val,
guint nparams,
...);
</PRE>
</CODE></BLOCKQUOTE>
<P>Creates a new signal. The parameters are:
<P>
<UL>
<LI> <CODE>name</CODE>: The name of the signal.</LI>
<LI> <CODE>run_type</CODE>: Whether the default handler runs before or after
user handlers. Usually this will be <CODE>GTK_RUN_FIRST</CODE>, or <CODE>GTK_RUN_LAST</CODE>,
although there are other possibilities.</LI>
<LI> <CODE>object_type</CODE>: The ID of the object that this signal applies
to. (It will also apply to that objects descendants.)</LI>
<LI> <CODE>function_offset</CODE>: The offset within the class structure of
a pointer to the default handler.</LI>
<LI> <CODE>marshaller</CODE>: A function that is used to invoke the signal
handler. For signal handlers that have no arguments other than the
object that emitted the signal and user data, we can use the
pre-supplied marshaller function <CODE>gtk_signal_default_marshaller</CODE>.</LI>
<LI> <CODE>return_val</CODE>: The type of the return val.</LI>
<LI> <CODE>nparams</CODE>: The number of parameters of the signal handler
(other than the two default ones mentioned above)</LI>
<LI> <CODE>...</CODE>: The types of the parameters.</LI>
</UL>
<P>When specifying types, the <CODE>GtkType</CODE> enumeration is used:
<P>
<BLOCKQUOTE><CODE>
<PRE>
typedef enum
{
GTK_TYPE_INVALID,
GTK_TYPE_NONE,
GTK_TYPE_CHAR,
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -