GObject, Glib, GDK, GTK+...

原本只是打算学习怎么使用GTK+进行图形界面的开发,不过在看了tigersoldier写的控件之后,对GTK+的内部机制产生了强烈的好奇心,于是下载了源码来看。

结果,越看越迷糊,涉及到的内容好多:

GObject:

“GObject provides the object system used for Pango and GTK+.”

Glib:

"GLib provides the core application building blocks for libraries and applications written in C. It provides the core object system used in GNOME, the main loop implementation, and a large set of utility functions for strings and common data structures."

GDK:

"An intermediate layer which isolates GTK+ from the details of the windowing system."

GTK+

"GTK+ is the primary library used to construct user interfaces in GNOME applications. It provides user interface controls and signal callbacks to control user interfaces."

 

目标是彻底理解下面这段最简单的GTK+代码的工作原理及实现:

 

#include<gtk/gtk.h>

static gint delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
{
    gtk_main_quit();
}

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

        gtk_init(&argc, &argv);

        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

        g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);

        gtk_widget_show(window);

        gtk_main();

        return 0;
}

 

 

加油,加油...

 

GLib的一些规范

来源:

imtx.cn/archives/167.html

 

在一些面向对象的程序语言中,有一些内置方法,是用来取得类的所属关系或者类型。比如Python中,用type来取得实例的类型。

GLib中规定了一些规范来实现这个。 这是在学用GObject定义自己的类型前需要注意的。

当用户在头文件中创建新类型时,有一些规范用户需要注意:

  • 使用object_method的形式来定义函数名称:例如在一个bar类中定义一个名为foo的函数,则用bar_foo。
  • 使用前缀来避免与其他工程的命名空间冲突。如果你的库(或应用程序)名为Marman,那么所有的函数名称前缀为maman_。举例:maman_object_method。
  • 创建一个宏命为PREFIX_OBJECT_TYPE用来返回GType关联的对象类型。比如,Bar这个类在一个以maman前缀的库中,则使用 MANMAN_BAR_TYPE。另有一个不成文的规定是,定义一个使用全局静态变或一个名为prefix_object_get_type的函数来实现 这个宏。我们将在后面的章节中讨论这个函数。
  • 创建一个宏命名为PREFIX_OBJECT(obj)来返回一个指向 PrefixObject类型的指针。这个宏用于必要时安全地强制转换一个静态类型。运行环境检查时,同样也是安全地执行动态类型。在处理过程中禁用动态 类型检查是可行的。例如,我们可以创建MAMAN_BAR(obj)来保持先前的例子。
  • 如果类型是类化的,那么创建一个命令为 PREFIX_OBJECT_CLASS(klass)的宏。这个宏与前面那个是非常相似的:它以类结构的动态类型检查来进行静态转换,并返回一个指向 PrefixObjectClass这个类型的类结构的指针。同样,例子为:MAMAN_BAR_CLASS。
  • 创建一个宏命名为PREFIX_IS_BAR (obj):这个宏用于判断输入的对象实例是否是BAR类型的。
  • 如果类型是类化的,创建一个名为PREFIX_IS_OBJECT_CLASS (klass)的宏,与上面的类似,返回输入的类型指针是否是OBJECT类型。
  • 如果类型是类化的,创建一个名为PREFIX_OBJECT_GET_CLASS,返回一个实例所属的类的类型指针。这个宏因为安全的原因,被静态和动态类型所使用,就像上面的转换宏一样。

至于这些宏的实现是非常直观的:一些数量的简单使用的宏由gtype.h提供。针对上面我们兴趣的例子,我们写了下面的代码来声明这些宏:

 

#define MAMAN_BAR_TYPE                  (maman_bar_get_type ())

#define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_BAR_TYPE, MamanBar))

#define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_BAR_TYPE, MamanBarClass))

#define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_BAR_TYPE))

#define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_BAR_TYPE))

#define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_BAR_TYPE, MamanBarClass))

 
GType maman_bar_get_type (void)

{

static GType type = 0;

if (type == 0) {

static const GTypeInfo info = {

/* You fill this structure. */

};

type = g_type_register_static (G_TYPE_OBJECT,

"MamanBarType",

&info, 0);

}

return type;

}

 

例子(GtkWindow):

 

#define GTK_TYPE_WINDOW            (gtk_window_get_type ())
#define GTK_WINDOW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_WINDOW, GtkWindow))
#define GTK_WINDOW_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_WINDOW, GtkWindowClass))
#define GTK_IS_WINDOW(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_WINDOW))
#define GTK_IS_WINDOW_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_WINDOW))
#define GTK_WINDOW_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_WINDOW, GtkWindowClass))

 

 

GTK系统添加建立一种新的“类型”的过程

来源:

blog.csai.cn/user1/265/archives/2005/786.html

 

GTK对象系统运行时,为支持对象的动态创建和销毁,系统中必定会拥有所有对象“类型”之间的 关联表,也即所有_GtkTypeNode实体组织而成的“类继承树”,每个_GtkTypeNode代表一种“类型”,它包含了指向 _GtkTypeInfo,以及从_GtkObjectClass派生出的结构的数据信息,所以,我们可以轻松通过GtkType来遍历系统中所有的类 型。

为系统添加建立一种新的“类型”的过程很简单,它一般由gtk_xxx_get_type()触发,例如 gtk_window_get_type()函数。注意,一种新“类型”被加入时,它会创建一个_GtkTypeNode的数据结构,并添入到系统的“类 继承树”中,具体的过程参阅下面的源代码:

Realizing, Mapping, and Showing

From: developer.gnome.org/doc/GGAD/z57.html

 

Fully understanding GTK+ requires some minimal understanding of the X Window System. This book assumes you have a user-level understanding---you know what an X server is, that X is network transparent, what a window manager does, and so on. A few more details are needed to write programs, however.

One detail is particularly important: the X Window System maintains a tree of windows. "Window" in this sense refers to an X window, not a GtkWindow---GtkWindow is a GTK+-specific concept, a widget that corresponds to an application's toplevel X window. An X window is not the user-visible concept "window" represented by GtkWindow; rather, it's an abstraction used by the X server to partition the screen. The "background" displayed by your X server is the root window; the root window has no parent. Application windows are typically near-children of the root window; most window managers create a child of the root window to hold the window's titlebar and other decorations, and place the application window inside. Window managers have total control over application windows---they can reposition them, reparent them, and iconify them at will. Application windows can in turn contain subwindows, which are controlled by the application. Note that GTK+ uses the GDK library, rather than using X directly; in GDK, there is a thin X window wrapper called GdkWindow. Don't confuse GdkWindow and GtkWindow.

An X window, or a GdkWindow, gives the X server hints about the structure of the graphics being displayed. Since X is network transparent, this helps reduce network traffic. The X server knows how to show windows on the screen; hide them; move them around (keeping children in position relative to their parents); capture events such as mouse movements on a per-window basis; and so on. A GdkWindow is also the fundamental unit for drawing graphics---you can't draw to "the screen" as a whole, you must draw on a GdkWindow.

Most GTK+ widgets have a corresponding GdkWindow. There are exceptions, such as GtkLabel; these are referred to as "no window widgets," and are relatively lightweight. Widgets with no associated GdkWindow draw into their parent's GdkWindow. Some operations, such as capturing events, require a GdkWindow; thus they are impossible on no-window widgets.

Widgets pass through a number of states related to their GdkWindow:

  • A widget is said to be realized if its corresponding GdkWindow has been created. Widgets are realized via gtk_widget_realize(), and unrealized via gtk_widget_unrealize(). Since an X window must have a parent, if a widget is realized its parent must also be.

  • A widget is mapped if gdk_window_show() has been called on its GdkWindow. This means the server has been asked to display the window on the screen; obviously the GdkWindow must exist, implying that the widget is realized.

  • A widget is visible if it will automatically be mapped when its parent is mapped. This means that gtk_widget_show() has been called on the widget. A widget can be rendered invisible by calling gtk_widget_hide(); this will either unschedule the pending map, or unmap the widget (hide its GdkWindow). Since toplevel widgets have no parent, they are mapped as soon as they are shown.

In typical user code, you only need to call gtk_widget_show(); this implies realizing and mapping the widget as soon as its parent is realized and mapped. It's important to understand that gtk_widget_show() has no immediate effect, it merely schedules the widget to be shown. This means you don't have to worry about showing widgets in any particular order; it also means that you can't immediately access the GdkWindow of a widget. Sometimes you need to access the GdkWindow; in those cases you'll want to manually call gtk_widget_realize() to create it. gtk_widget_realize() will also realize a widget's parents, if appropriate. It's uncommon to need gtk_widget_realize(); if you find that you do, perhaps you are approaching the problem incorrectly.

Destroying a widget automatically reverses the entire sequence of events, recursively unrealizing the widget's children and the widget itself.

Figure 23 summarizes the functions discussed in this section.

#include <gtk/gtkwidget.h>

void gtk_widget_realize(GtkWidget* widget);

void gtk_widget_unrealize(GtkWidget* widget);

void gtk_widget_map(GtkWidget* widget);

void gtk_widget_unmap(GtkWidget* widget);

void gtk_widget_show(GtkWidget* widget);

void gtk_widget_hide(GtkWidget* widget);

Figure 23. Showing/Realizing Widgets

Figure 24 summarizes macros for querying the states discussed in this section.

#include <gtk/gtkwidget.h>

GTK_WIDGET_NO_WINDOW(widget);

GTK_WIDGET_REALIZED(widget);

GTK_WIDGET_MAPPED(widget);

GTK_WIDGET_VISIBLE(widget);

Figure 24. Widget Predicates

在屏幕上显示构件

在屏幕上显示需要几个相关步骤。在调用 WIDGETNAME_new() 创建构件之后,如下几个函数需要用到:

你可能注意到后面的两个函数十分相似,都是负责在屏幕上绘制构件。实际上许多构件并不真正关心它们之间的不同。构件类里的默认 draw() 函数只是简单的为重绘区域产生一个暴露事件。然而,一些构件通过区分这两个函数可以减少操作。例如,如果一个构件有多个 X 窗口,因为暴露事件标识了暴露的窗口,它可以只重绘受影响的窗口,调用 draw() 是不可能这样的。

容器构件,即使它们自身并不关心这个差别,也不能简单的使用默认 draw() 函数,因为它的子构件可能需要注意这个差别。然而,在两个函数里重复绘制代码是一种浪费的。按惯例,构件有一个名为 WIDGETNAME_paint() 的函数做实际的绘制构件的工作,draw()expose() 函数再调用它。