Gtk3 和 cairo g_timeout_add 不工作

Gtk3 and cairo g_timeout_add doesn't work

我用 gtk3 和 cairo 制作了非常简单的动画。我有 g_timeout_add,它应该每 1 毫秒调用一次 draw 函数,但它调用 draw 的速度比应有的慢得多。为什么会发生,我该如何解决?

#include <gtk/gtk.h>
#include <cairo.h>

gboolean timeout(GtkWidget *widget)
{
    gtk_widget_queue_draw(widget);
    return TRUE;
}

void draw(GtkWidget* widget, cairo_t* cr)
{
    static int width, height,
               posX = 0,
               vX = 1;

    GtkWidget* window = gtk_widget_get_toplevel(widget);
    gtk_window_get_size(GTK_WINDOW(window), &width, &height);

    cairo_set_source_rgb(cr, 0, 0, 0);
    cairo_set_line_width(cr, 1);

    cairo_rectangle(cr, posX, height/2, 1, 1);
    cairo_stroke(cr);

    if(posX + vX >= width || posX + vX == 0)
        vX = -vX;
    posX += vX;
}

int main(int argc, char** argv)
{
    GtkWidget* window;
    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_window_set_default_size(GTK_WINDOW(window), 500, 400);

    g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
    g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(draw), NULL);

    g_timeout_add(1, (GSourceFunc)timeout, window);

    gtk_widget_show_all(window);
    gtk_main();
}

根据 g_timeout_add() 的 GLib 文档:

Note that timeout functions may be delayed, due to the processing of
other event sources. Thus they should not be relied on for precise
timing. After each call to the timeout function, the time of the next
timeout is recalculated based on the current time and the given
interval (it does not try to 'catch up' time lost in delays).

因为事件循环正在为您的应用程序做其他事情(包括 运行 您的 draw() 函数),超时可能无法跟上 1 毫秒的滴答声,当然不应该不能依赖精确的计时。

Why it happens and

Zrax 回答了这个问题。我只想添加一些内容:我将以下内容添加到您的绘制函数中(并添加了适当的包含)

struct timeval tv;
gettimeofday(&tv, NULL);
printf("%d.%06d\n", (int) tv.tv_sec, (int) tv.tv_usec);

我得到如下输出:

1518869317.202412
1518869317.218970
1518869317.236563
1518869317.253032
1518869317.269721
1518869317.286305

因此您的绘制函数大约每 17 毫秒被调用一次,或每秒大约 60 次。

how can I fix it?

这就提出了一个问题:为什么你想要绘制速度超过 60 fps?

如何创建一个 GTimer 并使用 g_timer_elapsed() 找出您必须绘制的位置? IE。不必假设您的绘图速度足够快,您只需在每次绘制时计算当前图形状态即可。