私は、いくつかのAPIを学習することのみを目的として、ペットプロジェクトに取り組んでいます。実用的な価値があることを意図したものではなく、libpcap、gtk +、cairoを深刻な目的で使用する前に、それらを快適にするための比較的簡単な演習を目的としています。これは、Cで実装され、Gtk+2.xを使用するグラフィカルプログラムです。最終的にはpcapでフレームを読み取り(現在はハードコードされたテストフレームがあります)、cairoを使用して、生のパケットから生成された色の値を使用してきれいな画像を生成します(この段階では、cairo_show_textを使用してテキスト表現を印刷していますフレームまたはパケットの)。次に、画像はGtkDrawingAreaを継承するカスタムウィジェットに描画されます。
もちろん、私の最初のステップは、ウィジェットを実装できるように、Gtk+ランタイム環境を適切に把握することです。私はすでに、cairoを使用してカスタムウィジェットにテキストをレンダリングおよび描画することができました。今、私はウィジェットがcairo_tコンテキストポインターやGdkRegionポインターなどのプライベートストレージを本当に必要としていると思うところにいます(私はGdkを直接使用する予定はありませんでしたが、私の調査では、呼び出すために必要かもしれないことが示されていますgdk_window_invalidate_region()は、gdk_cairo_create()は言うまでもなく、フレームを描画した後、DrawingAreaを強制的に更新します。プライベートストレージをグローバル変数として設定しました(ホラー!これは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))
これが私の質問です:私のプライベートデータ構造体のポインターの初期化は、親である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への私の最初の本当の進出です。したがって、用語を誤って使用している場合は、失礼します。私の混乱の原因の1つは、Gtkにはインスタンスとクラスの初期化の概念が別々にあることだと思います。C ++は「内部」で同様のことを行う可能性がありますが、そうであれば、コーダーにはそれほど明白ではありません。
これがC++の場合、candy_draw_pane_init()に入るコードのほとんどはクラスコンストラクターにあり、コンストラクターの完了に依存する2次初期化は「Init()」メソッドに入ると思います(もちろん、これは言語の機能ではなく、一般的に使用される規則です)。Gtk +に類似した規則はありますか?あるいは、これらのウィジェットがインスタンス化されたときに、誰かが制御フローの概要を説明できるかもしれません。Gnomeの公式ドキュメントの品質にはあまり感心していません。その多くは高レベルであるか、コードにエラーやタイプミスが含まれているか、リンク切れや例が欠落しています。そしてもちろん、マクロを多用すると、自分のコードを追跡するのが少し難しくなります(この点で、Win32 GUI開発を思い出させます)。要するに、
完全を期すために、カスタムウィジェットを設定したヘッダーを次に示します。
#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ランタイムをよく理解することです。ボイラープレートを手で書くことを好みます。