gpt4 book ai didi

c - 在依赖于父成员的自定义 Gtk+ 小部件中初始化私有(private)数据 (C)

转载 作者:行者123 更新时间:2023-12-04 05:48:17 25 4
gpt4 key购买 nike

我正在从事一个宠物项目,只是为了学习一些 API。它并不具有实用值(value),而是在我将它们用于任何严肃的事情之前,通过相对简单的练习让我熟悉 libpcap、gtk+ 和 cairo。这是一个图形程序,用 C 语言实现,使用 Gtk+ 2.x。它最终将使用 pcap 读取帧(目前我只有一个硬编码的测试帧),然后使用 cairo 使用从原始数据包生成的颜色值生成漂亮的图片(在此阶段,我只是使用 cairo_show_text 打印文本表示帧或数据包)。然后图片将被绘制到继承自 GtkDrawingArea 的自定义小部件。

当然,我的第一步是充分掌握 Gtk+ 运行时环境,以便我可以实现我的小部件。我已经设法使用 cairo 将文本渲染和绘制到我的自定义小部件。现在我认为小部件真的需要私有(private)存储来存储 cairo_t 上下文指针和 GdkRegion 指针(我没有计划直接使用 Gdk,但我的研究表明可能有必要为了调用gdk_window_invalidate_region() 在我绘制了一个框架后强制我的 DrawingArea 刷新,更不用说 gdk_cairo_create())。我已经将私有(private)存储设置为全局变量(可怕!显然这是 Gtk+ 的惯例。如果我有多个小部件实例,我仍然不确定这将如何工作,所以也许我没有这样做部分正确。或者也许预处理器宏和运行时环境正在做一些魔术来为每个实例提供自己的这个结构的副本?):

/* private data */
typedef struct _CandyDrawPanePrivate CandyDrawPanePrivate;
struct _CandyDrawPanePrivate {
cairo_t *cr;
GdkRegion *region;
};


#define CANDY_DRAW_PANE_GET_PRIVATE(obj)\
(G_TYPE_INSTANCE_GET_PRIVATE((obj), CANDY_DRAW_PANE_TYPE, CandyDrawPanePrivate))

这是我的问题:初始化我的私有(private)数据结构中的指针取决于从父级 GtkWidget 继承的成员:
/* instance initializer */
static void candy_draw_pane_init(CandyDrawPane *pane) {
GdkWindow *win = NULL;
/*win = gtk_widget_get_window((GtkWidget *)pane);*/
win = ((GtkWidget*)pane)->window;
if (!win)
return;

/* TODO: I should probably also check this return value */
CandyDrawPanePrivate *priv = CANDY_DRAW_PANE_GET_PRIVATE(((CandyDrawPane*)pane));
priv->cr = gdk_cairo_create(win);
priv->region = gdk_drawable_get_clip_region(win);

candy_draw_pane_update(pane);
g_timeout_add(1000, candy_draw_pane_update, pane);
}

当我用在我的事件处理程序期间调用 gdk_cairo_create() 和 gdk_drawable_get_clip_region() 的旧代码替换为在 candy_draw_pane_init() 期间调用它们的代码时,应用程序将不再绘制。通过调试器,我可以看到当我们在 candy_draw_pane_init() 中时,pane->window 和 pane->parent 都是 NULL 指针。指针稍后在 Gtk 事件处理循环中有效。这让我相信,当我的派生类的“_init()”方法被调用时,继承的成员还没有被初始化。我确信这只是 Gtk+ 运行时环境的特性。

那么这种事情一般是怎么处理的呢?我可以向我的事件处理程序添加逻辑以检查 priv->cr 和 priv->region 是否为 NULL,如果它们仍然为 NULL,则调用 gdk_cairo_create() 和 gdk_drawable_get_clip_region()。或者我可以向我的 CandyDrawPane 小部件添加一个“post-init”方法,并在我调用 candy_draw_pane_new() 之后显式调用它。我相信很多其他人都遇到过这种情况,那么有没有一种干净而传统的方法来处理它呢?

这是我第一次真正涉足面向对象的 C,所以如果我使用的术语有误,请见谅。我认为让我感到困惑的一个原因是 Gtk 具有独立的实例和类初始化概念。 C++ 可能会在“幕后”做类似的事情,但如果是这样,对编码人员来说就不那么明显了。

我有一种感觉,如果这是 C++,进入 candy_draw_pane_init() 的大部分代码将在类构造函数中,任何依赖于已完成构造函数的二次初始化都将进入“Init()”方法(这当然不是语言的特性,而只是一种常用的约定)。 Gtk+ 是否有类似的约定?或者,当这些小部件被实例化时,也许有人可以很好地概述控制流。 Gnome 官方文档的质量并没有给我留下很深的印象。其中大部分内容要么太高级,要么包含代码中的错误和拼写错误,要么链接断开或缺少示例。当然,大量使用宏使得我自己的代码更难理解(在这方面它让我想起了 Win32 GUI 开发)。简而言之,我相信我可以自己解决这个问题并使其发挥作用,但我想听听有 Gtk+ 和 C 经验的人来说,什么是“正确”的方法来做到这一点。

为了完整起见,这里是我设置自定义小部件的标题:
#ifndef __GTKCAIRO_H__
#define __GTKCAIRO_H__ 1

#include <gtk/gtk.h>

/* Following tutorial; see gtkcairo.c */
/* Not sure about naming convention; may need revisiting */
G_BEGIN_DECLS
#define CANDY_DRAW_PANE_TYPE (candy_draw_pane_get_type())
#define CANDY_DRAW_PANE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CANDY_DRAW_PANE_TYPE, CandyDrawPane))
#define CANDY_DRAW_PANE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass)CANDY_DRAW_PANE_TYPE, CandyDrawPaneClass))
#define IS_CANDY_DRAW_PANE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CANDY_DRAW_PANE_TYPE))
#define IS_CANDY_DRAW_PANE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CANDY_DRAW_PANE_TYPE))
// official gtk tutorial, which seems to be of higher quality, does not use this.

// #define CANDY_DRAW_PANE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CANDY_DRAW_PANE_TYPE, CandyDrawPaneClass))

typedef struct {
GtkDrawingArea parent;

/* private */
} CandyDrawPane;

typedef struct {
GtkDrawingAreaClass parent_class;
} CandyDrawPaneClass;

/* method prototypes */
GtkWidget* candy_draw_pane_new(void);
GType candy_draw_pane_get_type(void);
void candy_draw_pane_clear(CandyDrawPane *cdp);

G_END_DECLS
#endif

任何见解都非常感谢。我确实意识到我可以使用一个代码生成 IDE 并更快地完成一些事情,并且可能避免不得不处理其中的一些东西,但是这个练习的重点是很好地掌握 Gtk 运行时,所以我更愿意手工编写样板。

最佳答案

本文,A Gentle Introduction to GObject Construction ,可以帮到你。以下是我在查看您的代码和问题时想到的一些提示:

  • 如果您的 priv->crpriv->region每当小部件的 GDK 窗口更改时,指针都必须更改,然后您也可以将该代码移动到 notify::window 的信号处理程序中。信号。 notify是一个在对象的属性发生更改时触发的信号,您可以通过将其附加到这样的信号名称来缩小信号发射范围以监听特定属性。
  • 您不需要检查 GET_PRIVATE 的返回值。宏。查看 g_type_instance_get_private() 的源代码,可以返回 NULL在出现错误的情况下,但这真的不太可能,并且会向终端打印警告。我的感觉是如果GET_PRIVATE返回 NULL那么事情就真的出错了,无论如何你都无法恢复并继续执行程序。
  • 您没有将私有(private)存储设置为全局变量。你在哪里声明这个全局变量?我只看到一个 structtypedef全局层面的宣言。您最有可能在做什么以及通常的做法是调用 g_type_class_add_private()class_init功能。这会在每个对象中为您的私有(private)结构保留空间。那么当你需要使用它时,g_type_instance_get_private()给你一个指向这个空间的指针。
  • init方法相当于 C++ 中的构造函数。 class_init方法没有等价物,因为那里完成的所有工作都是在 C++ 的幕后完成的。例如,在 class_init 中函数,您可以指定哪些函数覆盖父类的虚函数。在 C++ 中,您只需在类中定义一个与您要覆盖的虚拟方法同名的方法即可完成此操作。
  • 关于c - 在依赖于父成员的自定义 Gtk+ 小部件中初始化私有(private)数据 (C),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10407781/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com