Maemo Linux手机平台系列分析:(16) Maemo应用开发: GTK+基础

C语言中的图形用户界面开发:使用库如GTK+或ncurses创建GUI应用程序(一) C语言开发者可以充分利用GTK+的强大功能,开发出界面美观、功能丰富、具有良好用户体验的跨平台GUI应用程序。 阅读详情

本部分内容:

  • Glib基本概念
  • 信号系统
  • GTK+术语
  • Hello world程序如何结束自己
  • Gobject接口
  • 增加菜单和布局
  • Hildon 构件
  • 使用附件
  • 处理动态内存
  • 避免使用废弃不用的函数

 

Glib基本概念                  

在Maemo中,所有的GTK程序都使用Glib工具库。这个工具库提供一组可移植的类型,这些类型可以为用C语言写的程序提供一定的可移植性。便利的工具函数和可移植的数据类型可以很容易写出具有移植性的软件。

 

 

从现在开始,我们将会用Glib类型,而不用C标准的类型了:

gboolean      Either TRUE or FALSE. FALSE is equal to zero 

gint8         8-bit signed integer 

gint16        16-bit signed integer 

gint32        32-bit signed integer 

gint64        64-bit signed integer (there are no >64-bit ones) 

gpointer      Untyped pointer ('void *') (32/64-bit) 

gconstpointer R/O untyped pointer('const void *') (32/64-bit) 

gchar         Compiler's 'char' (8-bit in gcc) 

guchar        Compiler's 'unsigned char' 

gshort        Compiler's 'short' (16-bit in gcc) 

gushort       Compiler's 'unsigned short' 

gint          Compiler's 'int' (32-bit normally in gcc) 

guint         Compiler's 'unsigned int' 

glong         Compiler's 'long' (32/64-bit in gcc) 

gulong        Compiler's 'unsigned long' (32/64-bit in gcc) 

gfloat        Compiler's 'float' (32-bits in gcc) 

gdouble       Compiler's 'double'(64/80/81-bits in gcc) 

[ Glib 类型的名字及其含义]

所有符号整型以2S方式存储。对于每一个符号整型都有一个对应的无符号整型。

你应该试着坚持使用这些Glib类型,一般情况下你可以不必计较类型的大小。当然,也有例外:当你想优化内存时,或者数据来自于外部的数据(比如,原始位图数据,网络协议,硬件编程等等)。

GLib 也提供一些限制上述类型最大值的宏、字节序和字节序修改宏函数,在写可移植程序时,这些宏是非常有用的。在Glib API 文档中有详细的描述,可以参考如下:

http://maemo.org/api_refs/4.0/glib/index.html.

下面,我们将会使用少量函数写一个能跑的程序,同时,随着时间的推移,你将会欣赏用Glib写的代码(也有一些人放弃了Glib,因为Glib作为一个工具库实在太大、太多了)。由于Glib是共享库,所有Nokia的Internet Tablet 通常只有一个GUI程序在运行,所以Glib一般情况下不会耗费多少内存。

除了数据类型外,Glib同时也提供了如下的比较有用的函数集:

  • 内存分配
  • 消息输出、调试和日志函数
  • 字符串处理 (UTF-8 and UCS-4)
  • 日期和时间处理以及计数定时器
  • 数据结构:
    • 动态字符串
    • 双向链表 (单向和双向)
    • Hash 表
    • 动态数组(也提供指针数组)
    • 二叉树和N叉树
    • 数据缓冲支持
  • 其它一些功能

正如前面提到的,Glib的API可以在线浏览: http://maemo.org/api_refs/4.0/glib/index.html. 对于Maemo中使用的其他的库函数的参考,你可以到这里找找看: http://maemo.org/development/documentation/apis/4-x/.

如果在你的开发系统上已经安装了合适的文档包,你也应当试试 devhelp,这是一个非常好的程序,可以让你浏览GNOME库文档,并且提供超文本和search功能。

 

信号机制

为了处理不同组件之间交互,不同的库有不同的方法去实现事件和变化时的通知机制。在设计和限制方面各有千秋。在GTK中使用的设计模型是注册/自动激发回调函数的这么一个机制。这种机制称之为Gsignal, 在Gobject库中实现的。Gobject是一个框架或者架构,使用它你可以用C语言编写出面向对象的结构,就像Java和C++那样。Gobject并不需要特殊的工具函数,因此它的可移植性非常好,当然,Gobject中使用了大量的Glib数据结构。

 

关于如何使用Gobject(实现自己的类、扩展已有的类,等等)这里并不准备介绍,因为这是个比较复杂的问题,而且在开始用gtk+编程时也没有必要非要把Gobject搞的很透。呵呵。

GTK+ widget使用GSignal机制去实现通知:当有什么事情发生时。然后这些消息就传递给那些关心这些事件的监听者(通过回调函数的形式)。在GTK+中,并没有所谓的共享事件总线,但是你可以发挥想象:在widget和回调函数之间是建立了“链接”的。GSignal还支持优先级的处理。

 

每一个类可以定义其型号类型,而类的实例可以发射这些信号。Signal是通过一个文本字符串和一个信号源标示的。

 

我们链接一个信号:指定一个具备发射信号的对象并且指定信号的名称。你可以把名字认为是类型,实际上它仅仅是用于区分不同的信号,比如“clicked”和“selected” 。我们链接的对象是谁?就是用C语言写的回调函数。请注意:发射信号的对象并不直接调用回调函数,而是使用GObject提供的普遍的信号发送框架。这个框架使得发射者和接收者仅仅提供相同的API就可以了。

 

假设在面向对象编程成熟之前就设计了C语言,那么,GObject和signal看起来就顺眼多了。如果你仅仅使用过面向对象语言编程,GObject对你来说,看起来就比较奇怪了。习惯了就好了。

 


你可以通过阅读API或者很多与GObject相关的数据去熟悉它,推荐一本书:【The Official Gnome 2 Developer’s Guide】。直接从代码中理清GObject的架构并不是一个好的主意,因为代码比较复杂。不过你早晚要熟悉GObject的代码的。J  

 

GTK+ 术语

在开始GTK+之前,我们需要看看有哪些术语:

  • Widget: 能在屏幕显示并且允许用户和它交互的这么一个控件。比如:scrollbar,Button,Menu,等等。  
  • Container: 一个特殊的容器控件,能够给其它的一些控件提供容身之所,并且把那些控件以一定的方式组织在一块,显示在屏幕上。另外容器可以套容器,就是大鱼吃小鱼,小鱼吃虾米。比如: Window, VBox, HBox, Toolbar.
  • Packing: 填充,这是一个动作。就是把一个widget放到一个容器控件中,这么一个动作就叫做填充。一般情况下,我们这样讲:把一个widget填充到一个容器中。填充这个动作将会做些空间分配和布局方面的工作。如果一个widget暂时不可见,这个填充动作暂时是不做的。  
  • Child Widget: 子控件,就是被填充到一个容器中的控件.反过来讲,一个控件的父控件就是其容器。一个控件不能有多个父控件。
  • Widget Tree: 所有的控件和其容器都是一个root控件的子控件。对于每一个window都有一个widget tree。
  • Event: 事件,就是进程外发生什么事情后给出的通知。这些通知一般来自于HID-sysem,并且由GTK+转为信号。信号处理机制就是处理这些事件的。
  • Visibility:每一个widget都可以设置可见或者不可见。只有父控件(容器)可见,才能在屏幕上看到图像。为了让widget tree中的每一个控件可见,我们需要告诉这些控件,显示与否。通常我们在创建完widget tree后,统一处理这种告知的事情。
  • Property: 属性,这实际是和Gobject相关的东西,不过在GTK+中一样得到了应用。一般来讲,property就是可以设置/取得的具有名字的数据或者值。当我们设置属性时,对象会执行一些操作来反映值的变化。读取一个属性也会触发一些代码执行。

 

 

Hello World 示例程序如何结束自己呢?

/**

 * gtk_helloworld-2.c

 *

 * This maemo code example is licensed under a MIT-style license,

 * that can be found in the file called "License" in the same

 * directory as this file.

 * Copyright (c) 2007 Nokia Corporation. All rights reserved.

 *

 * This version adds proper mechanisms to end the program.

 *

 * Look for lines with "NEW" or "MODIFIED" in them.

 */

 

#include <stdlib.h>

#include <gtk/gtk.h>

 

/**

 * NEW

 *

 * Callback function (event handler) for the "delete" event.

 * This event is emitted by GTK+ main loop after a window manager has

 * requested this window to be closed.

 *

 * NOTES:

 * - This is really a signal handler.

 * - The first parameter to a signal handler is a pointer to the

 *   object that caused the signal to be emitted (when using

 *   gobject_signal_connect).

 * - There are zero or more parameters in between the first one and

 *   the last one. The count of parameters depends on the signal

 *   type.

 * - The last parameter is always an untyped pointer (again, when

 *   using gobject_signal_connect).

 *

 * Parameters:

 * - widget: Pointer to the widget that emitted the delete-event.

 *           // 发射delete事件的控件

 *

 * - event:  GdkEvent-structure describing what kind of event this

 *           is. We'll register this handler to handle only the

 *           "destroy-event", so we'll know what kind of event this

 *           is without checking the "event"-parameter.

 *           //

 * - data:   Untyped pointer for passing data between the main loop

 *           and handlers. We ignore it for now.

 *           // 在主循环和事件handler之间传递数据

 * Returns:

 * - gboolean: Whether the event has been consumed by this handler or

 *             not.

 * NOTE:

 *   Signal handlers may exit with an return value. Whether this

 *   value will be used by anyone, depends on the signal type. For

 *   events, we must always return a boolean value.

 */

static gboolean delete_event(GtkWidget* widget, GdkEvent event,

                                                gpointer data) {

  /* Print out an informational message. */

  g_print("CB:delete_event/n");

 

  /* We want to tell the gtk_main that we're done with this widget.

     Since it's the top-level widget in our window (it is the

     window), returning FALSE to our caller signifies that the event

     has not been processed by us, and this in turn means that it

     will be propagated to the default window delete-event handler

     which will terminate the program. This is done in by the caller

     so that it will issue a "destroy"-signal next. If we would

     return TRUE, the program wouldn't stop. This behavior is

     specific to the "delete-event" signal. */

  return FALSE;

}

 

/**

 * NEW

 *

 * Our callback function for the "destroy"-signal which is issued

 * when the Widget is going to be destroyed.

 * NOTE:

 *   In this case we don't need to return anything, since we're just

 *   processing a simple signal.

 *

 * This is used by widgets that are going away that want to notify us

 * that they will not be around for much longer. They will remove

 * themselves eventually.

 *

 * We call gtk_main_quit() which is a routine that will tell gtk_main

 * that it's time to terminate.

 *

 * Note, you must not terminate your program here! (do not use the

 * exit()-library function). This would leave the GTK+ library in a

 * slightly confused state and might leave other threads hanging

 * around. In this material we won't be using threads, but consider

 * yourself warned.

 *

 * Parameters:

 * - widget: Same as with event handler callbacks.

 * - data:   Same as with event handler callbacks.

 *

 * Returns:

 * void

 */

static void end_program(GtkWidget* widget, gpointer data) {

  /* Print out an informational message to stdout. */

  g_print("CB:end_program: calling gtk_main_quit()/n");

 

  /* Tell gtk_main to terminate the application. */

  gtk_main_quit();

 

  /* Display message about what is happening. */

  g_print("CB:end_program: back from gtk_main_quit & returning/n");

}

 

/**

 * MODIFIED

 *

 * We connect the signals to the handlers so that the application may

 * be terminated properly from the GUI.

 */

int main(int argc, char** argv) {

 

  /* We'll have two references to two GTK+ widgets. */

  GtkWindow* window;

  GtkLabel* label;

 

  /* Initialize the GTK+ library. */

  gtk_init(&argc, &argv);

 

  /* Create a window with window border width of 12 pixels and a

     title text. */

  window = g_object_new(GTK_TYPE_WINDOW,

    "border-width", 12,

    "title", "Hello GTK+",

    NULL);

 

  /* Create the label widget. */

  label = g_object_new(GTK_TYPE_LABEL,

    "label", "Hello World!",

    NULL);

 

  /* Pack the label into the window layout. */

  gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(label));

 

  /**

   * NEW

   *

   * Here we "install" event and signal handlers.

   *

   * Events are handled by the GTK+ as signals. Their difference is

   * explained in the text that follows.

   *

   * g_signal_connect(GtkWidget*, gchar*, CB*, gpointer*) is the API

   * function to connect signals from some object to some signal

   * receiver (callback function when using C).

   *

   * The second parameter should point to a string giving the event

   * or signal name. GLib treats underscores and dashes in signal

   * names as they would be the same character. The API documentation

   * uses the dash (-) and so will we. Needless to say, different

   * kind of widgets are capable of emitting different events.

   *

   * Third parameter is the address of the callback function that

   * should receive the signal. We use G_CALLBACK()-macros to force a

   * typecast into the type specification that g_signal_connect

   * expects. Depending on the signal the callback will receive

   * different number of parameters and is expected to return

   * something in some cases. There is no hard and set rule.

   *

   * THIS MEANS THAT YOU HAVE TO BE SURE ABOUT THE CAST VALIDITY.

   *

   * It is possible to pass an extra pointer (the last parameter)

   * that will be carried with the signal to the signal receiver. We

   * will see uses for this later, but in real applications it is

   * quite important. This last parameter is defined as a gpointer

   * (untyped pointer, void*), and if one needs to pass other data

   * in the pointer's place, one will need to do additional

   * typecasting (we'll see this later).

   */

  g_signal_connect(window, "delete-event",

                   G_CALLBACK(delete_event), NULL);

  g_signal_connect(window, "destroy",

                   G_CALLBACK(end_program), NULL);

 

  /* Show all widgets that are contained by the window. */

  gtk_widget_show_all(GTK_WIDGET(window));

 

  /* Start the main event loop. */

  g_print("main: calling gtk_main/n");

  gtk_main();

 

  /* Display a message to the standard output and exit. */

  g_print("main: returned from gtk_main and exiting with success/n");

 

  /* Return success as exit code. */

  return EXIT_SUCCESS;

}

[ 带有基本信号的Hello World ]

 

你们看到,上面写了很多的注释,其实很烦的。不过后面就不这样写了,第一次这样写,以后就可以直接看这个注释的含义了。

 

下面编译,运行这个例子,并关闭程序:

[sbox-CHINOOK_X86: ~/appdev] > gcc -Wall `pkg-config --cflags gtk+-2.0` /

 gtk_helloworld-2.c -o gtk_helloworld-2 `pkg-config --libs gtk+-2.0`

[sbox-CHINOOK_X86: ~/appdev] > ./gtk_helloworld-2

main: calling gtk_main

Window closing button engaged by the user

CB:delete_event

CB:end_program: calling gtk_main_quit()

CB:end_program: back from gtk_main_quit & returning

main: returned from gtk_main and exiting with success

[ Running the program ]

可以看到,在Xserver上面并没有看到图形输出,这里我们其实只是关心g_print()打印出来的信息。上面你们看到的序列是正常的信息。

 

 

GObject 接口

下面我接着探讨:我们看看GObject类必需要实现的基本功能,以及在GTK+中如何使用这些看似怪异的功能。

 

下面我们使用accessor接口(就是能直接创建控件的API)写个例子,然后与用property接口实现相同功能的例子做比较。

 

创建一个窗口:窗口边宽12像素,同时有title:

window = (GtkWindow*)gtk_window_new(GTK_WINDOW_TOPLEVEL);

gtk_window_set_title(window, "Hello GTK+");

gtk_container_set_border_width((GtkContainer*)window, 12);

下面我们换种方法:不用现存的控件API,而是使用GObject一般的创建对象的一套方法来实现上面相同的功能。

 

我们通过指定GType的值为:GTK_TYPE_WINDOW,来创建一个GtkWindow对象。同时设定对象的两个属性:属性的名字为“border-width”和“title”。我们用NULL来结束属性列表的设置。

 

window = g_object_new(GTK_TYPE_WINDOW,

  "border-width", 12,

  "title", "Hello GTK+",

  NULL);

后面,我们需要决定冻结窗口大小的变化。为此,我们需要设置一个属性:“resizable”:

/* Set the value of one property. */

g_object_set_property(window, "resizable", FALSE);

假设现在我们需要设置多个属性,可以很方便的使用g_object_set()函数来做,而如果用widget的API来做这样的事情,就显得比较繁琐了。

gchar* data = "Some random data";

 

g_object_set(window,

  "resizable", FALSE,/* GtkWindow-property */

  "has-focus", TRUE, /* GtkWidget-property */

  "width", 300,      /* GtkWidget-property */

  "height", 150,     /* GtkWidget-property */

  "user-data", data, /* GObject-property */

  NULL);

 

下面的代码片断中,你会注意到GObject匹配的宏,它们主要是做类型匹配的。

例如,gtk_container_add() 有两个参数:

  • GtkContainer* : 容器指针,用来指向一个可以添加widget的容器。
  • GtkWidget* :任何控件.

 

因为window也是一个container,所以我们可以很安全地把它匹配成一个container。又因为GtkLabel是一个控件,所以把它转换为一个GtkWidget是没有任何问题的。这样做的目的就是为了编译的顺利通过,不必出现N多的warnings。

 

显然你需要知道各个对象指针对应的宏定义,其实转化规则很简单:每一个“word”单独写成大写,中间用下划线分开就行。举个例子:GtkWidget变成GTK_WIDGET; GtkRadioMenuItem变成GTK_RADIO_MENU_ITEM。

 

如果仅仅为了讨好编译器,其实这种转化是意义不大的。这种宏的另外一个作用就是类型检查(以及检查实例是否有悖于类的层次关系)。

/* Pack the label into the window layout. */

gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(label));

前面我们用设置属性的方式,设置了一些对象,如果使每一个都可视的话,要一个一个设置,有时比较繁琐的。有时候,控件接口函数可以帮我们分担一些工作,呵呵。.

/* Show all the widgets that are contained by the window. */

gtk_widget_show_all(GTK_WIDGET(window));

gtk_widget_show_all这个函数就是使得整个widget树所包含的所有widget都可见。与可见/不可见相关的函数还有: gtk_widget_show(GtkWidget*), gtk_widget_hide(GtkWidget*) and gtk_widget_hide_all(GtkWidget*).

 

 

增加菜单和layout

下面我们为我们的程序实现一个菜单。这个例子程序会使用信号和属性。

 

我们会尽量使用属性,借以证明压缩代码的可能性。请注意:每一个属性的查找将会包含一个GObject的hash table的lookup。 在代码尺寸和速度方面寻求一个平衡点是比较难的事情。

/**

 * gtk_helloworld-3.c

 *

 * This maemo code example is licensed under a MIT-style license,

 * that can be found in the file called "License" in the same

 * directory as this file.

 * Copyright (c) 2007 Nokia Corporation. All rights reserved.

 *

 * We add a menu and callbacks to process menu selections.

 *

 * Look for lines with "NEW" or "MODIFIED" in them.

 */

 

#include <stdlib.h>

#include <gtk/gtk.h>

 

/**

 * NEW

 * 使用一个回调函数去处理所有的菜单项

 * We will use a single callback function to process the activation

 * of all of the menu items.

 *

 * 我们可以使用字符串来识别菜单项,不过这里我们使用一个枚举量。

 * We could also use strings to identify which menu item was

 * selected, or if our callback would have a list of pointers to the

 * menu items themselves, we could compare against that. Instead,

 * we'll define an enumeration that will contain all the possible

 * menu items that our application will have. This allows use to use

 * a simple switch statement to determine what to do.

 */

typedef enum {

  MENU_FILE_OPEN = 1,

  MENU_FILE_SAVE = 2,

  MENU_FILE_QUIT = 3

} MenuActionCode;

 

/**

 * Callback function (event handler) for the "delete" event.

 */

static gboolean delete_event(GtkWidget* widget, GdkEvent event,

                                                gpointer data) {

  /* Print out an informational message. */

  g_print("CB:delete_event/n");

 

  /* Done with the widget. */

  return FALSE;

}

 

/**

 * Our callback function for the "destroy"-signal which is issued

 * when the Widget is going to be destroyed.

 */

static void end_program(GtkWidget* widget, gpointer data) {

  /* Print out an informational message to stdout. */

  g_print("CB:end_program: calling gtk_main_quit()/n");

 

  /* Tell gtk_main to terminate the application. */

  gtk_main_quit();

 

  /* Display message about what is happening. */

  g_print("CB:end_program: back from gtk_main_quit & returning/n");

}

 

/**

 * NEW

 *

 * Signal handler for the menu item selections.

 *

 * Gets the menu action code as a parameter but since we'll cast it

 * into an pointer on callback registration, we'll need to cast it

 * back internally.

 */

static void cbActivation(GtkMenuItem* mi, gpointer data) {

 

  /* Convert the pointer into an integer, irrespective of the pointer

     size. */

  MenuActionCode aCode = GPOINTER_TO_INT(data);

 

  switch(aCode) {

    case MENU_FILE_OPEN:

      g_print("Selected open/n");

      break;

    case MENU_FILE_SAVE:

      g_print("Selected save/n");

      break;

    case MENU_FILE_QUIT:

      g_print("Selected quit/n");

      gtk_main_quit();

      break;

    default:

      /* Besides g_print, there are two other output functions in

         GLib: g_warning and g_error. g_error will stop our program

         (using abort() internally), so we'll choose to use g_warning

         instead since we want our program to continue running. The

         situation is not lethal, but should be investigated.

 

         g_warning prints out a warning message in the same format as

         g_print and printf. */

      g_warning("cbActivation: Unknown menu action code %d./n", aCode);

  }

}

 

/**

 * NEW

 *

 * A convenience function to create menu items with internal label

 * objects.

 *

 * NOTE:

 *   Most existing programs use a convenience function that GTK+

 *   provides (gtk_menu_item_new_with_label).

 *

 * We want to use the GObject API for this example to demonstrate its

 * compactness.

 *

 * Parameters:

 * - const gchar*: string to display in the label inside the item.

 *

 * Returns:

 * - A pointer to a new MenuItem that holds the label

 */

static GtkMenuItem* buildMenuItem(const gchar* labelText) {

 

  GtkLabel* label;

  GtkMenuItem* mi;

 

  /* Create the Label object first. We set the text using its "label"

     property. We also set its alignment using a property that its

     superclass (GtkMisc) has. Namely "xalign". xalign with 0.0 is

     left and 1.0 is right extreme. Default value for xalign is 0.5

     which would align the text using center justification. */

  label = g_object_new(GTK_TYPE_LABEL,

    "label", labelText,     /* GtkLabel property */

    "xalign", (gfloat)0.0,  /* GtkMisc property */

    NULL);

 

  /* Next we create the GtkMenuItem.

 

     A MenuItem is a container that can hold one child (this

     restriction comes from GtkBin-class, which is an abstract class

     for containers that are capable of holding only one one child).

     We use the "child" property of the Container class to set its

     child on creation. Normally a Container would allow us to add

     multiple children in a similar way (by setting the "child"-

     property multiple times). */

  mi = g_object_new(GTK_TYPE_MENU_ITEM, "child", label, NULL);

 

  /* Return the GtkMenuItem to caller. */

  return mi;

}

 

/**

 * NEW

 *

 * Utility function to build a menubar and the menu.

 *

 * Uses the buildMenuItem-function from above and will connect the

 * signals that can be generated by MenuItems when they're selected

 * (the "activate"-signal).

 *

 * Returns:

 * - the completed MenuBar

 */

static GtkMenuBar* buildMenubar(void) {

 

  /* The visible menubar widget that will hold one submenu called

     "File". */

  GtkMenuBar* menubar;

  /* The menu item representing the menu-name in the menubar. It is

     necessary in order to attach the menu to the menubar. Will open

     the submenu when activated. */

  GtkMenuItem* miFile;

 

  /* A container implementing a menu that holds multiple items. */

  GtkMenu* fileMenu;

  /* The individual menu items and a separator item. */

  GtkMenuItem* miOpen;

  GtkMenuItem* miSave;

  GtkMenuItem* miSep;

  GtkMenuItem* miQuit;

 

  /* Create the container for items. */

  fileMenu = g_object_new(GTK_TYPE_MENU, NULL);

 

  /* Create the menu items. */

  miOpen = buildMenuItem("Open");

  miSave = buildMenuItem("Save");

  miQuit = buildMenuItem("Quit");

  miSep = g_object_new(GTK_TYPE_SEPARATOR_MENU_ITEM, NULL);

 

  /* Add the items into the container.

 

     Here we again use the "child"-property. But since Menu is a

     proper container that can hold multiple children, we can use a

     list of widgets to add. */

  g_object_set(fileMenu,

    "child", miOpen,

    "child", miSave,

    "child", miSep,

    "child", miQuit,

    NULL);

 

  /* Connect the signals from the individual menu items.

     NOTE:

       We need to cast the CB data into a gpointer since that is what

       the prototype of g_signal_connect tells the compiler to

       enforce. Luckily for us there is a macro to do exactly this

       (in GLib). */

  g_signal_connect(G_OBJECT(miOpen), "activate",

    G_CALLBACK(cbActivation), GINT_TO_POINTER(MENU_FILE_OPEN));

  g_signal_connect(G_OBJECT(miSave), "activate",

    G_CALLBACK(cbActivation), GINT_TO_POINTER(MENU_FILE_SAVE));

  g_signal_connect(G_OBJECT(miQuit), "activate",

    G_CALLBACK(cbActivation), GINT_TO_POINTER(MENU_FILE_QUIT));

 

  /* Create the menu item that will be added to the menubar. */

  miFile = buildMenuItem("File");

 

  /* Associate the "File" menu item to the Menu container so that it

     will open it as a submenu when activated.

 

     There is no property to set this, so we use an accessor

     function. */

  gtk_menu_item_set_submenu(miFile, GTK_WIDGET(fileMenu));

 

  /* Create the menubar. */

  menubar = g_object_new(GTK_TYPE_MENU_BAR, NULL);

 

  /* Add the "File" menu item to the menubar.

 

     We want to add from left to right, so again, the "child"

     property will be used. */

  g_object_set(menubar, "child", miFile, NULL);

 

  /* Return the completed menubar to the caller. */

  return menubar;

}

 

/**

 * MODIFIED

 *

 * We add a container that can hold more than one widget since

 * GtkWindow can hold only one at a time. We need to put our menubar

 * somewhere, and display a label. We select a container that will

 * split the area that it will get into horizontal sections according

 * to the needs to its children (GtkVBox).

 */

int main(int argc, char** argv) {

 

  GtkWindow* window;

  GtkLabel* label;

  GtkMenuBar* menubar;

  GtkVBox* vbox;

 

  /* Initialize the GTK+ library. */

  gtk_init(&argc, &argv);

 

  /* Create a window with window border width of 12 pixels and a

     title text. */

  window = g_object_new(GTK_TYPE_WINDOW,

    "border-width", 12,

    "title", "Hello GTK+",

    NULL);

 

  /* Create the label widget. */

  label = g_object_new(GTK_TYPE_LABEL,

    "label", "Hello World! (with menus)",

    NULL);

 

  /* Build the menubar (NEW). */

  menubar = buildMenubar();

 

  /* Create a layout box for the window since it can only hold one

     widget (NEW). */

  vbox = g_object_new(GTK_TYPE_VBOX, NULL);

 

  /* Add the vbox as the only child for the window (NEW). */

  g_object_set(window, "child", vbox, NULL);

 

  /* NEW

 

     Add it to the container that now controls the area inside the

     window.

 

     Both VBox and HBox implement an abstract class called GtkBox.

     Adding child widgets to GtkBoxes is possible by using an

     old-style API (gtk_box_pack_start and _end), or we can use the

     context based one with properties.

 

     We need to control three properties (besides the "child", GtkBox

     is also a container):

     - "pack-type": selects whether to add the next child to the

       start or end of box. Default is GTK_PACK_START.

       For VBox start is top and end is bottom.

     - "expand": Controls whether the child will receive extra space

       when the Box will grow (if it get's new space itself). Default

       is TRUE.

     - "fill": If "expand" is set, controls whether extra new space

       should be given to the child or used as padding. Default is

       TRUE.

 

    Based on this information we now must select a sequence that will

    create a split between the menubar and the label.

 

    We want the menubar to take only the amount of space that it

    requires, and for the label to fill up the rest. Should the

    window grow, we want the area allocated to label to grow as well.

    We'll see a growing window later on.

 

    We have two choices (A or B):

    A) Insert the menubar first:

       1) Set "expand" to FALSE.

       2) Use "child" to insert menubar.

       3) Set "pack-type" to GTK_PACK_END so that label will be added

          from bottom-up.

       4) Set "expand" back to TRUE.

       5) Use "child" to insert the label.

    B) Insert the Label first:

       1) Set "pack-type" to GTK_PACK_END.

       2) Insert label using "child".

       3) Set "expand" to FALSE.

       4) Set "pack-type" to GTK_PACK_START.

       5) Use "child" to insert the menubar.

 

    As you can see, both approaches will require an equal amount of

    property operations, so we can choose either one. There are other

    possible orders as well, but all of them require at least the

    five operations. We'll choose the first approach (A):

 

    g_object_set(vbox,

      "fill", FALSE,

      "child", menubar,

      "pack-type", GTK_PACK_END,

      "expand", TRUE,

      "child", label,

      NULL);

 

    NOTE:

      This won't work currently since the VBox implementation does

      not implement the properties of the abstract GtkBox class.

 

      This means that we'll need to use the accessor interface which

      requires us to give both fill and expand each time we add a

      widget.

 

      The order of parameters for the accessor function:

      - GtkBox into which we're adding the widget.

      - The widget that we're adding.

      - "fill".

      - "expand".

      - "padding", which is how many pixels are used around the

        widget. */

  gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(menubar), FALSE,

                     FALSE, 0);

  gtk_box_pack_end(GTK_BOX(vbox), GTK_WIDGET(label), TRUE, TRUE, 0);

 

  /* Connect the termination signals */

  g_signal_connect(window, "delete-event",

                   G_CALLBACK(delete_event), NULL);

  g_signal_connect(window, "destroy",

                   G_CALLBACK(end_program), NULL);

 

  /* Show all widgets that are contained by the window. This will

     also include the menubar, and its widget tree. */

  gtk_widget_show_all(GTK_WIDGET(window));

 

  /* Start the main event loop. */

  g_print("main: calling gtk_main/n");

  gtk_main();

 

  /* Display a message to the standard output and exit. */

  g_print("main: returned from gtk_main and exiting with success/n");

 

  /* Return success as exit code. */

  return EXIT_SUCCESS;

}

 

[上面的代码,仔细读读,还是能理解一些GObject的东西的。 ]

[没有运行run-standalone.sh的情况 ]

[ 运行 run-standalone.sh 后的效果]

 

Hildon widgets

Maemo平台中包含一些经过优化的widgets。相对于桌面环境来讲,Maemo的显示屏比较小。因此就需要考虑在传统的窗口管理器和运行GUI程序之间做合理的切换。一般情况下,对GTK要做些修改,比如该尺寸等等。

 

为了很好的集成,下面我们开始把前面的程序模式切换到Hildon下面。Hildon主要提供两类widgets: HildonProgram和HildonWindow,HildonWindow主要是完成GtkWindow的一些功能。

HildonProgram是一个“超级window”对象,它提供一个shell,通过shell我们可以把我们的图形view集成到runtime 环境中。View和window很类似,不过在某个时间内,只有一个view是可见的。你可以这样想象:Hildon 应用程序就是一个带有多个tab的dialog,但是只有tab是可见的。View是用HildonWindow这么一个控件实现的,这个控件是我们放置其它控件的容器控件。

 

当你想把现有的GUI程序porting到Hildon下面时,你需要考虑一些UI布局方面的东东,以及如下的一些内容:

  • 每一个试图只有一个GtkMenu可以使用。这和传统的桌面环境是不同的。
  • 每一个窗口都有一个容器来包含toolbar.
  • 输入区域获取焦点后,会激活虚拟键盘。这样的话,就需要程序能够调整尺寸。如果你的程序不能很容易的调整尺寸,你可能需要使用GtkScrolledWindow来包容它。 (http://maemo.org/api_refs/4.0/gtk/GtkScrolledWindow.html).
  • 避免很深的子菜单。
  • 要把你的程序设计成只有一个主window的。如果对于有若干个分离窗口的程序,你需要重新设计。
  • 一次不要显示太多的信息。

 

下面我们使用HildonProgram和HildonWindow控件来改造Hello world例子程序。同时也使用HildonWinow提供的菜单控件:

/**

 * hildon_helloworld-1.c

 *

 * This maemo code example is licensed under a MIT-style license,

 * that can be found in the file called "License" in the same

 * directory as this file.

 * Copyright (c) 2007 Nokia Corporation. All rights reserved.

 *

 * Let's hildonize our application. Most of the application stays

 * unchanged, but the menu building utility needs some changes and

 * 'main' will need to be modified.

 *

 * Look for lines with "NEW" or "MODIFIED" in them.

 */

 

#include <stdlib.h>

#include <gtk/gtk.h>

 

/**

 * NEW

 *

 * When using widgets from the Hildon library, we need to use

 * additional include files. We could just pull in <hildon/hildon.h>,

 * but that is unnecessary since we only use a small number of Hildon

 * widgets. Instead we'll opt to pull in only the Hildon header files

 * that are necessary for the widgets that we use.

 *

 * For our first Hildon application, we'll need the Hildon Program

 * framework widgets whose API and types are declared in

 * hildon-program.h

 */

#include <hildon/hildon-program.h>

 

/* Menu codes. */

typedef enum {

  MENU_FILE_OPEN = 1,

  MENU_FILE_SAVE = 2,

  MENU_FILE_QUIT = 3

} MenuActionCode;

 

/**

 * Callback function (event handler) for the "delete" event.

 */

static gboolean delete_event(GtkWidget* widget, GdkEvent event,

                                                gpointer data) {

  return FALSE;

}

 

/**

 * Our callback function for the "destroy"-signal which is issued

 * when the Widget is going to be destroyed.

 */

static void end_program(GtkWidget* widget, gpointer data) {

  gtk_main_quit();

}

 

/**

 * Signal handler for the menu item selections.

 */

static void cbActivation(GtkMenuItem* mi, gpointer data) {

 

  MenuActionCode aCode = GPOINTER_TO_INT(data);

 

  switch(aCode) {

    case MENU_FILE_OPEN:

      g_print("Selected open/n");

      break;

    case MENU_FILE_SAVE:

      g_print("Selected save/n");

      break;

    case MENU_FILE_QUIT:

      g_print("Selected quit/n");

      gtk_main_quit();

      break;

    default:

      g_warning("cbActivation: Unknown menu action code %d./n", aCode);

  }

}

 

/**

 * A convenience function to create menu items with internal label

 * objects.

 */

static GtkMenuItem* buildMenuItem(const gchar* labelText) {

 

  GtkLabel* label;

  GtkMenuItem* mi;

 

  /* Create the Label object. */

  label = g_object_new(GTK_TYPE_LABEL,

    "label", labelText,     /* GtkLabel property */

    "xalign", (gfloat)0.0,  /* GtkMisc property */

    NULL);

 

  /* Create the GtkMenuItem and add the Label as its child. */

  mi = g_object_new(GTK_TYPE_MENU_ITEM, "child", label, NULL);

 

  /* Return the GtkMenuItem to caller. */

  return mi;

}

 

/**

 * MODIFIED

 *

 * This utility adds the menu items into the given HildonProgram.

 * It uses the buildMenuItem-function internally to build each menu

 * item. It will connect the signals that can be generated by

 * MenuItems when they're selected ("activate"-signal).

 *

 * Since there is no menubar, so this becomes quite simple.

 */

static void buildMenu(HildonProgram* program) {

 

  GtkMenu* menu;

  GtkMenuItem* miOpen;

  GtkMenuItem* miSave;

  GtkMenuItem* miSep;

  GtkMenuItem* miQuit;

 

  /* Create the menu items. */

  miOpen = buildMenuItem("Open");

  miSave = buildMenuItem("Save");

  miQuit = buildMenuItem("Quit");

  miSep = g_object_new(GTK_TYPE_SEPARATOR_MENU_ITEM, NULL);

 

  /* Create a new menu. */

  menu = g_object_new(GTK_TYPE_MENU, NULL);

 

  /* Add the items to the container. */

  g_object_set(menu,

    "child", miOpen,

    "child", miSave,

    "child", miSep,

    "child", miQuit,

    NULL);

 

  /* Set the top-level menu for Hildon program (NEW). */

  /* 设置顶层菜单 *、

  hildon_program_set_common_menu(program, menu);

 

  /* Connect the signals from the individual menu items. */

  g_signal_connect(G_OBJECT(miOpen), "activate",

    G_CALLBACK(cbActivation), GINT_TO_POINTER(MENU_FILE_OPEN));

  g_signal_connect(G_OBJECT(miSave), "activate",

    G_CALLBACK(cbActivation), GINT_TO_POINTER(MENU_FILE_SAVE));

  g_signal_connect(G_OBJECT(miQuit), "activate",

    G_CALLBACK(cbActivation), GINT_TO_POINTER(MENU_FILE_QUIT));

 

  /**

   * NEW

   *

   * We need to explicitly set the visibility for the menu since by

   * default it's not visible when we get it from window. Safest bet

   * is to show the widget tree that is inside the menu.

   */

  gtk_widget_show_all(GTK_WIDGET(menu));

}

 

/**

 * MODIFIED

 *

 * We switch to the application framework that Hildon provides.

 */

int main(int argc, char** argv) {

 

  /* Our GtkWindow has been replaced with HildonProgram (NEW). */

  HildonProgram* program;

  /* And we'll have one window, which will act as the container for

     our needs (NEW). */

  HildonWindow* window;

  GtkWidget* label;

  GtkWidget* vbox;

 

  /* Initialize the GTK+. */

  gtk_init(&argc, &argv);

 

  /**

   * NEW

   *

   * Create the Hildon provided window and controller widgets.

   */

  Hildon来提供window和控制控件

  program = HILDON_PROGRAM(hildon_program_get_instance());

  /* Set the application title using an accessor function. */

  g_set_application_name("Hello Hildon!");

  /* Create a window that will handle our layout and menu. */

  window = HILDON_WINDOW(hildon_window_new());

  /* Bind the HildonWindow to HildonProgram. */

  把创建的HildonWindow绑定到超级window(HildonProgram上)

  hildon_program_add_window(program, HILDON_WINDOW(window));

 

  /* Create the label widget. */

  label = g_object_new(GTK_TYPE_LABEL,

    "label", "Hello Hildon (with menus)!",

    NULL);

 

  /* Build the menu. We'll create the menu for the HildonProgram, so

     we pass the pointer to our revised buildMenu (NEW). */

  buildMenu(program);

 

  /* Create a layout box for the window since it can only hold one

     widget. */

  vbox = g_object_new(GTK_TYPE_VBOX, NULL);

 

  /* Add the vbox as a child to the Window. */

  g_object_set(window, "child", vbox, NULL);

 

  /* Pack the label into the VBox. */

  gtk_box_pack_end(GTK_BOX(vbox), GTK_WIDGET(label), TRUE, TRUE, 0);

 

  /* Connect the termination signals. Note how the HildonWindow

     object has taken the responsibilities that a GtkWindow normally

     would have (NEW). */

  g_signal_connect(G_OBJECT(window), "delete-event",

                   G_CALLBACK(delete_event), NULL);

  g_signal_connect(G_OBJECT(window), "destroy",

                   G_CALLBACK(end_program), NULL);

 

  /* Show all widgets that are contained by the window. This also

     includes the menu (if it has been setup by this point) (NEW). */

  gtk_widget_show_all(GTK_WIDGET(window));

 

  /* Start the main event loop. */

  g_print("main: calling gtk_main/n");

  gtk_main();

 

  g_print("main: returned from gtk_main and exiting with success/n");

 

  return EXIT_SUCCESS;

}

编辑:

[sbox-CHINOOK_X86: ~/appdev] > gcc -Wall `pkg-config --cflags gtk+-2.0  /

 hildon-1` hildon_helloworld-1.c -o hildon_helloworld-1 /

 `pkg-config --libs gtk+-2.0 hildon-1`

运行效果,还不错吧,呵呵:

上面的效果和Meamo的正常视图布局一致:

上面视图中每个组件的像素大小:

  • 左边的任务导航栏占用:80x480.
  • 最上边的状态栏占用720x60的大小。这一个区域有时候也用来显示title或者菜单。
  • 给程序显示区域的大小是: 672×396 像素.

不过这个尺寸并不是一成不变的。

 

使用封装过的函数API

现在,你已经具有基本的概念了,并且也用GObject实现了一些小的例子程序,不过仍然有很多东西需要进一步的学习和了解。

 

如果一直用GObject的属性做GTK编成,也不是不行,而是不方便。大家还是使用GTK+提供的函数直接进行编成吧。GTK+函数会自己做些严格的类型检查的,所以你如果传入了不合适的参数,编译时就会给你提示,而不是等到运行时再报错。

 

下面我们用GTK+的接口函数实现,不用GObject的property了。

/**

 * hildon_helloworld-2.c

 *

 * This maemo code example is licensed under a MIT-style license,

 * that can be found in the file called "License" in the same

 * directory as this file.

 * Copyright (c) 2007 Nokia Corporation. All rights reserved.

 *

 * We now convert into accessor functions. Program logic is same as

 * in hildon_helloworld-1.c.

 *

 * Look for lines with "NEW" or "MODIFIED" in them.

 */

 

#include <stdlib.h>

#include <gtk/gtk.h>

#include <hildon/hildon-program.h>

 

/* Menu codes. */

typedef enum {

  MENU_FILE_OPEN = 1,

  MENU_FILE_SAVE = 2,

  MENU_FILE_QUIT = 3

} MenuActionCode;

 

/**

 * Callback function (event handler) for the "delete" event.

 */

static gboolean delete_event(GtkWidget* widget, GdkEvent event,

                                                gpointer data) {

  return FALSE;

}

 

/**

 * Our callback function for the "destroy"-signal which is issued

 * when the Widget is going to be destroyed.

 */

static void end_program(GtkWidget* widget, gpointer data) {

  gtk_main_quit();

}

 

/**

 * Signal handler for the menu item selections.

 */

static void cbActivation(GtkMenuItem* mi, gpointer data) {

 

  MenuActionCode aCode = GPOINTER_TO_INT(data);

 

  switch(aCode) {

    case MENU_FILE_OPEN:

      g_print("Selected open/n");

      break;

    case MENU_FILE_SAVE:

      g_print("Selected save/n");

      break;

    case MENU_FILE_QUIT:

      g_print("Selected quit/n");

      gtk_main_quit();

      break;

    default:

      g_warning("cbActivation: Unknown menu action code %d./n", aCode);

  }

}

 

/**

 * MODIFIED

 *

 * This utility creates the menu for the HildonProgram.

 * GTK+ offers a convenience function to create MenuItems with

 * GtkLabels with alignment already included. We'll use the

 * convenience function instead of the GObject properties.

 */

static void buildMenu(HildonProgram* program) {

 

  GtkMenu* menu;

  GtkWidget* miOpen;

  GtkWidget* miSave;

  GtkWidget* miSep;

  GtkWidget* miQuit;

 

  /* Create the menu items. */

  //这里直接使用GTK+接口函数,

  miOpen = gtk_menu_item_new_with_label("Open");

  miSave = gtk_menu_item_new_with_label("Save");

  miQuit = gtk_menu_item_new_with_label("Quit");

  miSep =  gtk_separator_menu_item_new();

 

  /* Create a new menu. */

  menu = GTK_MENU(gtk_menu_new());

 

  /* Add the items to the container.

 

     Now that we're using accessor functions, things look a bit

     different. We're still doing the same logic though.

 

     The prototype for adding children to a container is:

     gtk_container_add(GtkContainer*, GtkWidget*)

 

     This is why we don't need to cast the menu items. */

  gtk_container_add(GTK_CONTAINER(menu), miOpen);

  gtk_container_add(GTK_CONTAINER(menu), miSave);

  gtk_container_add(GTK_CONTAINER(menu), miSep);

  gtk_container_add(GTK_CONTAINER(menu), miQuit);

 

  /* Connect the signals from individual menu items. */

  g_signal_connect(G_OBJECT(miOpen), "activate",

    G_CALLBACK(cbActivation), GINT_TO_POINTER(MENU_FILE_OPEN));

  g_signal_connect(G_OBJECT(miSave), "activate",

    G_CALLBACK(cbActivation), GINT_TO_POINTER(MENU_FILE_SAVE));

  g_signal_connect(G_OBJECT(miQuit), "activate",

    G_CALLBACK(cbActivation), GINT_TO_POINTER(MENU_FILE_QUIT));

 

  /* Set the top level menu for Hildon program. */

  hildon_program_set_common_menu(program, menu);

}

 

/**

 * MODIFIED

 *

 * We use the accessor functions instead of GObject-properties.

 * Because Hildon API creator functions return GtkWidget pointers,

 * we'll change our pointer types accordingly. This is done for GTK+

 * widgets as well.

 */

int main(int argc, char** argv) {

 

  HildonProgram* program;

  HildonWindow* window;

  GtkWidget* label;

  GtkWidget* vbox;

 

  /* Initialize the GTK+. */

  gtk_init(&argc, &argv);

 

  /* Create the Hildon program. */

  program = HILDON_PROGRAM(hildon_program_get_instance());

  /* Set the application title using an accessor function. */

  g_set_application_name("Hello Hildon!");

  /* Create a window that will handle our layout and menu. */

  window = HILDON_WINDOW(hildon_window_new());

  /* Bind the HildonWindow to HildonProgram. */

  hildon_program_add_window(program, HILDON_WINDOW(window));

 

  /* Create the label widget (NEW). */

  label = gtk_label_new("Hello Hildon (with accessors)!");

 

  /* Build the menu and attach it to the HildonProgram. */

  buildMenu(program);

 

  /* Create a layout box for the window since it can only hold one

     widget (NEW).

 

     Using the creator function, we need to pass two parameters to

     it:

     - gboolean: should all children be given equal amount of space?

     - gint:     pixels to leave between the child widgets. */

  vbox = gtk_vbox_new(FALSE, 0);

 

  /* Add the vbox as a child to the Window (NEW). */

  gtk_container_add(GTK_CONTAINER(window), vbox);

 

  /* Pack the label into the VBox. */

  gtk_box_pack_end(GTK_BOX(vbox), GTK_WIDGET(label), TRUE, TRUE, 0);

 

  /* Connect the termination signals. */

  g_signal_connect(G_OBJECT(window), "delete-event",

                   G_CALLBACK(delete_event), NULL);

  g_signal_connect(G_OBJECT(window), "destroy",

                   G_CALLBACK(end_program), NULL);

 

  /* Show all widgets that are contained by the Window. */

  gtk_widget_show_all(GTK_WIDGET(window));

 

  /* Start the main event loop. */

  g_print("main: calling gtk_main/n");

  gtk_main();

 

  g_print("main: returned from gtk_main and exiting with success/n");

 

  return EXIT_SUCCESS;

}

你可能想知道使用两种API写程序的不同点在哪?用GObject property模型写的代码易于和别的语言绑定。不过现在可以不用关心这个。

 

处理内存

GTK+对内存的处理,都放在内部了,你就放心大胆地使用把。在实际使用过程中,如果使用GTK+函数创建一个widget或者数据结构,将会从堆(heap,动态内存区)分配一些内存,并且使用GObject函数去增加reference count。当这个widget是添加到一个container中,container会增加一个reference count。 反之,从一个container中移除一个widget,container会减少一个reference count. 在减少reference count时,会检查这个reference是否到0了,如果到0了,则释放内存。

 

如果一个widget准备销毁自己,它会发射一个“destroy”信号出去,这个信号会送给所有包含这个widget的容器,然后这些容器就不得不忍痛割爱:减少reference count. 在所有的容器做完这个动作后,这个widget就寿终正寝了。

 

更多的信息参考文档: http://maemo.org/api_refs/4.0/gtk/GtkObject.html http://maemo.org/api_refs/4.0/gobject/gobject-memory.html.

 

避免使用废弃的函数

GTK+也是一个不断开发的项目,其中代码会随着时间的推移,会有些废代码。在编译时有些开关可以不编译这些废弃代码。

  • GTK_DISABLE_DEPRECATED: 禁用GTK的废弃函数
  • GDK_DISABLE_DEPRECATED: 禁用GDK的废弃函数
  • GDK_PIXBUF_DISABLE_DEPRECATED: 禁用GDK-Pixbuf的废弃函数  
  • G_DISABLE_DEPRECATED: 禁用GLib的废弃函数
  • GTK_MULTIHEAD_SAFE: 这个并不是废弃的函数,主要把可能在多线程系统中引起问题的GTK函数禁用。

 

GTK+笔记之入门(一) 1. GTK概念 GTK+是一种跨平台的图形工具包,它可以很方便的制作图形交互界面(GUI)。 2. Linux安装 阅读详情

相关推荐

全面深入的GTK+开发工具套件指南

在GTK+中,开发者也可以创建自己的信号。创建信号通常涉及定义信号名称、默认处理程序等。一个简单的示例为创建一个按钮点击信号:/* 信号连接示例 *//* 自定义信号创建示例 */FALSE,在创建自定义信号的代码中,class_init函数用于初始化类属性,其中函数用于安装一个布尔型的样式属性,该属性可以作为自定义信号的一种表现形式。颜色方案为应用程序提供了视觉层次感和吸引力。GTK+允许开发者通过CSS来定义和应用颜色方案,包括主题色和系统颜色。

weixin_30476025的博客 1315

GTK+:GTK+的简介、安装、使用方法之详细攻略

​ GTK+:GTK+的简介、安装、使用方法之详细攻略 目录 GTK+的简介 GTK+的安装 GTK+的使用方法 GTK+的简介 GTK+(GIMP Toolkit)是一套源码以LGPL许可协议分发、跨平台的图形工具包。最初是为GIMP写的,已成为一个功能强大、设计灵活的一个通用图形库,是GNU/Linux下开发图形界面的应用程序的主流开发工具之一。并且,GTK+也有Windows版本和Mac OS X版。 GTK+ 是一种图形用户界面(GUI)工

头部AI社区如有邀博主AI主题演讲请私信—心比天高,仗剑走天涯,保持热爱,奔赴向梦想!低调,专注,谦虚,自律,反思,成长,还算比较正能量的博主,公益免费传播…内心特别想在AI界做出一些可以推进历史进程影响力的技术(兴趣使然,有点小情怀,也有点使命感呀 6万+

GTK+GNOME程序设计PDF(高清)2

本书详细介绍了两种Linux图形用户界面开发技术:GTK+/GNOME。主要内容包括:GUI的基本概念,GLib,GTK+及GNOME的简介,各种GUI元素的创建与使用,bIDE,Glade等等。最后通过两个综合实例来巩固所学知识。本书实例丰富,理论联系实际,是一本实践性很强的编程参考书。本书虽然适合GTK+和GNOME编程的初学者学习,但不管是程序设计新手还是编程专家,都能从本书中获益。

GTK+(GIMP Toolkit)介绍

GTK+(GIMP Toolkit)是一种开源的跨平台图形用户界面(GUI)工具包,最初作为GNU图像处理程序(GIMP)的工具包而开发。它提供了一套丰富的组件和工具,使开发者能够创建直观且功能强大的应用程序界面。本文将介绍GTK+的特点、优势以及它在应用程序开发中的应用。GTK+(GIMP Toolkit)是一种开源的跨平台图形用户界面(GUI)工具包,可以用于游戏开发。虽然GTK+主要是为桌面应用程序设计的,但也可以用于构建简单的2D游戏。下面将介绍GTK+游戏开发的基本过程。

牛肉胡辣汤 1411

泛微E9开发 明细表字段赋值到主表字段,将各类型合计赋值到主表类型合计中

①绑定明细表类型、预计金额字段的值改变事件,每当有值发生改变,就需要重新计算供应商1、供应商2、供应商3的合计,如果不绑定这两个字段的话,会出现计算不及时或计算错误的问题。②当有明细行被删除时,也需要重新计算供应商1、供应商2、供应商3的合计,比如删除了供应商1的明细行,如果不重新计算,那么供应商1的值还是被删之前的值,合计错误。将各类型合计赋值到主表类型合计中。

a_waste_only的博客 3836

LAMMPS in文件详解:从零开始手把手教你配置金属分子动力学模拟

本文详细解析了LAMMPS in文件的配置方法,手把手教你完成金属分子动力学模拟的全流程。从软件环境准备、势函数选择,到使用Materials Studio构建模型、编写in文件进行能量最小化与系统弛豫,再到实施单轴拉伸生产模拟并提取应力应变数据,最后通过Python和OVITO进行结果分析与可视化,提供了完整的实战指南和常见问题排查技巧。

weixin_28475533的博客 528

Maemo Linux手机平台系列分析:8 Maemo平台开发之 使用Glib绑定的D-Bus

  使用Glib封装过的D-Bus这部分的内容: GObject介绍 使用XML文件定义D-Bus接口 自动生成proxy/stub代码 创建一个简单的D-Bus对象 通过D-Bus发布一个GType类型 客户端如何使用Glib封装过的D-Bus D-Bus的自省功能 GObject介绍为了在运行时

Linux智能手机--cuijpus的专栏 2764

Unity面试题总结

Unity面试题

sinat_34014668的博客 1万+

GTK+系列---关于GTK+

什么是GTK+? GTK+或GIMP工具包是用于创建图形用户界面的多平台工具包。GTK+提供了一套完整的构件(widget),适用于从小型一次性工具到完整的应用程序套件等项目。GTK+是跨平台的,并且拥有易于使用的API,从而加快了您的开发时间。GTK+是用C语言编写的,但它是从底层设计的,支持各种各样的语言,而不仅仅是C/C++。使用来自Perl和Python等语言(尤其是与Glade GUI...

drsonxu的博客 2312

嵌入式系统存储体系

主要分为三种:高速缓存(cache)、主存和外存。

weixin_43586667的博客 2265

ADG同步性能优化:如何正确配置standby redolog避免WAIT_FOR_LOG状态

本文深入剖析了Oracle ADG(Active Data Guard)同步中常见的WAIT_FOR_LOG状态,指出其根源在于Standby Redo Log(SRL)配置不当。文章提供了从诊断到优化的完整实践指南,重点阐述了SRL大小匹配、组数充足等黄金配置法则,帮助架构师从根本上避免同步延迟,实现稳定高效的实时数据同步。

weixin_29200485的博客 335

GTK+基本操作加图形化计算器实现

1.GTK+简介 Linux下大多数的开发都是基于字符界面的,但是在Linux下也可以开发出美观大方的图形界面,其中较为常用的是Qt和GTK+; 为什么我们要在这里说GTK+呢? 因为GTK+使用C语言作为开发语言,GTK+是开放源代码而且免费的,简单易用,执行效率高,Linux的桌面环境GNOME就是建立在GTK+的基础上; 2.GTK+的简单操作 2.1.Deeping安装GTK+ sudo ...

qq_44049351的博客 1362

【AI编程】Qoder AI 编程工具从部署到深度使用实战详解

Qoder AI 编程工具从部署到深度使用实战详解

congge 8343

gtk_overviewGTK入门

数据类型,由类来定义对象)称为。

实力派,无需多言! 2325

AutoSAR EcuM系列01- EcuM模块的功能概述和变体类型

在【ECU的软硬件架构】文章中我们了解ECU的功能和组成,那么ECU具体是怎么启动的呢?我们知道,AutoSAR标准架构有三层,即APPL应用层、RTE层和BSW层。而这些层又被进一步划分为更多单独的组件,如BSW层可能有80多个组件。在ECU启动时,这些组件必须相互交互,有时也必须以某种顺序交互。这些组件的启动顺序由ECU管理器模块或EcuM处理。EcuM负责整个ECU的初始化和去初始化,包括对一些系统模块如。......

1767

【YOLOv11】087、YOLOv11多任务学习:检测、分割、分类联合学习

摘要: 工业质检场景中,多任务模型(检测、分类、分割)的需求日益增长。针对边缘设备部署的挑战,本文提出基于YOLOv11的多任务学习方案。通过共享主干网络和设计分支头(检测、分割、分类),实现高效推理;采用动态权重平衡策略解决多任务损失不平衡问题;并优化数据流处理(支持部分标注)和推理剪裁(按需启用分支)。实验表明,该方法在Jetson Orin上帧率提升3倍,内存占用减少40%,为工业质检提供了轻量高效的解决方案。

企鹅码农的专属播客,分享10多年的实战案例和经验解读,包含YOLO、RT-DETR、AI等各个工作领域。 446

GTK+入门教程

GTK+入门教程(一)1. 显示一个窗口#include &lt;gtk/gtk.h&gt; int main(int argc, char *argv[]) { GtkWidget *window; gtk_init(&amp;argc, &amp;argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk...

ygyoe的专栏 2万+

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Gtk+概述 Linux环境 按钮 窗口 布局 信号 回调函数 行编辑

Gtk+概述 GUI GUI 含义: (Graphics User Interface) 图形用户界面, 是计算机与使用者之间的对话接口, 是计算机重要的组成部分, 比如说咱们使用电脑或手机看到的 Windows 的桌面或 wps 软件显示的窗口界面等都是 GUI, 都是图形界面开发出来的图形界面的软件。 GUI 组成 : 桌面、 视窗、 菜单、 按钮、 图标等等 GUI 特点 : 可以使操作更加简单, 更加快捷、 更加人性化。 八十岁的老奶奶也会使用智能手机 早期的操作系统比如 DOS, 属于...

记录点点滴滴 1542

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

【GTK+-编程指南*】

GTK+-编程指南■■■■■ ■ ■ ■ ■ ■

sinat_23896491的博客 915

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

GTK+ 简介

GTK

qq_33172851的博客 415

0018__GTK+:GTK+的简介、安装、使用方法之详细攻略

GTK+:GTK+的简介、安装、使用方法之详细攻略-优快云博客

行潇 503

GTK+简介

GTK+ 是一套在 GIMP 的基础上发展而来的高级的、可伸缩的现代化、跨平台图形工具包,提供一整套完备的图形构件,适用于大大小小各种软件工程项目,不论是小到只需要一个窗口,还是复杂得如桌面环境。简单来说,GTK+ 是一种函数库是用来帮助制作图形交互界面的。 GTK+ 整个函数库都是由C 语言来编写的。这对 C 爱好者来说是极大的诱惑!你也无需担心因为是 C 写的而担心好用不好用,一旦你熟悉了这

秋叶原 && Mike || 麦克 1万+

GTK+ 中的第一个程序

gtk_window_new()函数创建一个新的GtkWindow,这是一个可以包含其他窗口小部件的顶层窗口。我们在屏幕上居中放置一个窗口,在标题栏中显示一个图标,显示一个小的工具提示,并为按钮小部件创建一个助记符。当我们处理多个窗口小部件时,在容器上调用gtk_widget_show_all()比单独显示所有窗口小部件容易。gtk_window_set_icon()显示窗口的图标。将GTK_WIN_POS_CENTER常量传递给gtk_window_set_position()函数,使程序在屏幕上居中。

qq_33172851的博客 426

C语言中的图形用户界面开发:使用库如GTK+或ncurses创建GUI应用程序(三)

GTK+主要用于开发桌面应用程序,尤其是在Linux环境中,与GNOME桌面环境紧密关联。尽管GTK+最初是为Linux/GNOME设计,但经过长期发展,它已经具备良好的跨平台能力,支持Windows、macOS等主流桌面操作系统。

weixin_56154577的博客 2774

学习使用GTK+

学习使用GTK+ 0.概述 原文地址:http://blog.programet.org/2010/08/gtk-0.html GTK+是当下流行的图形界面库之一,使用GTK+可以方便地构造出应用程序界面。叶子觉得GTK+很好用,在这里推荐给大家,并留下自己的一些使用心得。希望你也喜欢~ 在Programet这个系列中,你可以看到GTK+通俗的介绍,了解使用方法和常用API,

1万+
上一篇: LiMo又一可喜成果:LiMo平台发布!
下一篇: TD-SCDMA开始表现一般,要多支持理解!
testhw
博客等级 码龄19年 172粉丝 183原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值