14

在我的 Gtk-Gdk-Cairo-Pango 应用程序的开头,我创建了窗口:

GtkWidget   *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

首先,有GtkWindow,但gtk_create_window返回GtkWidget,没有GtkWindow,为什么?

然后,一些功能,如gdk_window_process_updates(..)require GdkWindow*

gtk_window_set_geometry_hints()另一方面需要GtkWindow*.

在文档中也GdkWindow* gdk_window_new()有返回GdkWindow

当然有文件说

GdkWindow 是屏幕上的一个矩形区域。它是一个低级对象,用于在 GTK+ 级别上实现 GtkWidget 和 GtkWindow 等高级对象。GtkWindow 是一个顶层窗口,用户可能会认为它是一个带有标题栏等的“窗口”;一个 GtkWindow 可能包含许多 GdkWindow。

但它仍然没有告诉我,何时以及为什么应该创建 Gtk 或 Gdk 窗口?这里要遵循的模式是什么?

现在你问,我要解决什么特别的问题?当然,我尝试在鼠标移动之后在 gtk+gdk 之上使用 cairo+pango 绘制文本。问题是,虽然实际绘图似乎执行得很快,但我无法让它在鼠标移动时完全发生。在我motion_notify_event的调用gtk_widget_queue_draw(GtkWidget)中,但实际鼠标在屏幕上的移动明显滞后,即使我绘制单个字符,它在移动阶段也没有与鼠标指针对齐,只有在鼠标停止后才捕捉到它。

我尝试的是通过调用来加速更新gdk_window_process_updates(GDK_WINDOW(window), false);,编译器吃掉它,但我得到了运行时断言:Gdk-CRITICAL **: gdk_window_process_updates: assertion 'GDK_IS_WINDOW (window)' failed. 我找不到有关此宏以及如何/何时使用它的任何信息。

#include <cairo.h>

#include <gtk/gtk.h>
#define TXT "1234567890"
int X = 0, Y = 0;
static void do_drawing(cairo_t *);
GtkWidget *window;
PangoLayout *layout = 0;

static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, 
              gpointer user_data) {
  do_drawing(cr);
  return FALSE;
}

static void do_drawing(cairo_t *cr) {
    if (layout == 0) {
        layout = pango_cairo_create_layout (cr);
        pango_layout_set_text (layout, TXT, -1);
    }
    for (int y = 0; y < 2; y++) {
        cairo_set_source_rgb (cr, 1, 0, 1);
        cairo_move_to (cr, 0+X, 0 + y * 20 + Y);
        pango_cairo_show_layout (cr, layout);
    }
    gtk_widget_queue_draw(window);
}

static gint onmouse(GtkWidget *widget, GdkEventMotion *event) {
    X = event->x; Y = event->y;
    gtk_widget_queue_draw(widget);
    gdk_window_process_updates(GDK_WINDOW(widget), false);
}


int main(int argc, char *argv[]) {
    GtkWidget *darea;
    gtk_init(&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    darea = gtk_drawing_area_new();
    gtk_container_add(GTK_CONTAINER(window), darea);
    gtk_widget_set_events (window, GDK_EXPOSURE_MASK
          | GDK_LEAVE_NOTIFY_MASK   | GDK_POINTER_MOTION_MASK);
    g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); 
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);  
    g_signal_connect(window, "motion_notify_event", G_CALLBACK(onmouse), NULL);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    gtk_window_set_default_size(GTK_WINDOW(window), 5000, 5000); 
    gtk_window_set_title(GTK_WINDOW(window), "Lines");
    gtk_widget_show_all(window);
    gtk_main();
    return 0;
}
4

2 回答 2

29

窗口管理器(X11、Wayland、Windows 的 user32.dll 以及我不记得名字的 Mac OS X 中的那个)本身并没有(必然)提供很多功能。他们给你的是:

  • 创建屏幕区域的能力,称为windows,您可以在这些区域上绘制、移动、调整大小、重塑、最小化、隐藏在其他窗口后面,以及以其他基本方式进行控制——但对于我们的讨论来说,其中最重要的是“画在”
  • 窗口的鼠标事件处理:当鼠标移入或移出窗口、在窗口内周围或当鼠标光标位于窗口上方时单击鼠标按钮时的通知
  • 接收键盘输入(焦点窗口)和此窗口的键盘事件的窗口的概念:当用户键入窗口时
  • 杂项其他功能(例如,绘制鼠标光标)

当与将矢量图形和文本渲染到窗口中的工具(通常由其他库提供,例如 cairo 和 pango)结合使用时,GUI 工具包就会发挥作用。这就是窗口管理器窗口并将其划分为您熟悉的所有小控件:按钮、文本字段、列表、选项卡、网页渲染器等。

在这种情况下,GTK+ 是 GUI 工具包。它提供了您在程序中使用的大量控件。

当您使用 GUI 工具包时,您通常不会直接与窗口管理器交互。因此,GUI 工具包提供了自己的窗口。当您创建 GUI 工具包窗口时,GUI 工具包会创建底层窗口管理器窗口,然后控制所有绘图和事件,以便它可以处理在该窗口中为您提供所有这些简洁控件的工作。

对于 GTK+,这是 GtkWindow。

GTK+ 的设计者不希望在 GTK+ 本身中拥有 GTK+ 支持的每个单独平台的所有窗口管理器交互代码。相反,他们创建了一个单独的库(包含在 GTK+ 源代码中),称为 GDK。GDK 围绕底层平台特定的窗口管理器函数提供一致的可移植 API。

所以 GdkWindow 是一种环绕窗口管理器窗口并提供 GTK+ 使用的可移植界面的类型。当您创建 GdkWindow 时,您正在创建这些低级窗口管理器窗口之一,而不是您放置控件的更丰富的 GtkWindow。

X11 在历史上一直非常资源受限。GTK+ 不会为每个控件创建一个窗口管理器窗口。它只为 GtkWindow、GtkPopover 和其他类似的控件创建这些控件,这些控件就像我们作为用户认为的窗口一样。

掌握了所有这些知识,您现在可以找到问题的答案:您几乎总是想使用 GtkWindow,而几乎从不想使用 GdkWindow。GdkWindow 仅对某些 GTK+ 控件的实现真正有用。

并且 GdkWindow 和 GtkWindow 不可互换。

(这仍然是对正在发生的事情的合理准确的过度简化。它并不适用于所有环境。例如,编写本机 Windows 程序的人通常为每个控件创建窗口管理器窗口,并且窗口管理器提供了一些基本的控件,例如按钮。我可能在上面的解释中也弄错了一些细节。)

GDK 和 GTK+ 之间的分离还有其他几个优点。例如,添加 Wayland 支持并没有(据我所知;我很可能错了)需要对 GTK+ 本身进行许多更改,并且有一个称为Broadway的 GDK 层,它可以让普通的 GTK+ 程序在 Web 浏览器中呈现.


更新,因为我似乎链接了很多:

  • 曾经有一段时间,大多数 GtkWidgets 实际上都有自己的 GdkWindows。这就是发生的事情。现在,我描述的情况就是这样。
于 2014-12-16T23:19:15.383 回答
1

里面的问题太多了,我就不一一解答了。

关于绘图延迟:最有可能的选择是您的实现中存在错误或未优化的代码:绘图周期在应用程序代码中非常独特,因为它确实非常需要快速......

注意事项:

  • gtk_widget_queue_draw(window)从你的绘图事件处理程序调用:这似乎没有必要
  • 你总是在鼠标事件上重绘而不检查它是否真的有必要(可能你确实想在所有运动事件上​​排队绘制:请确保这一点)
  • 您总是重绘整个小部件:这可能非常昂贵,如果您一次只需要更改一个小区域并且像您一样频繁重绘,您不应该这样做。见gtk_widget_queue_draw_region ()
  • 绘制文本可能很昂贵:我不熟悉您使用的功能,但您可能想从绘制一个框或其他东西开始,看看问题出在哪里
于 2014-12-17T07:48:47.730 回答