我用gtk3和cairo做了一个非常简单的动画。一秒钟它有点抽搐。它真的很烦人,看起来并不好看。为什么会发生这种情况?我该如何解决这个问题?
#include <gtk/gtk.h>
#include <cairo.h>
static int width, height,
posX = 0,
vX = 2;
gboolean draw(GtkWidget* widget, cairo_t* cr)
{
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, 100);
cairo_rectangle(cr, posX, height/2, 100, 100);
cairo_stroke(cr);
if(posX + vX >= width || posX + vX == 0)
vX = -vX;
posX += vX;
gtk_widget_queue_draw(widget);
return TRUE;
}
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(16, (GSourceFunc)draw, window);
gtk_widget_show_all(window);
gtk_main();
}
答案 0 :(得分:3)
首先要考虑您的原始程序在我的系统上完美运行,因此这可能是您系统上的一个问题。您可以尝试提高程序的优先级,但基本上您的代码容易受到此类问题的影响:请参阅g_timeout_add
了解原理。
无论如何,存在一个概念问题:您正在使用draw()
做两件事。它用作draw
信号和超时回调。这是错误的:绘制信号必须是幂等的,因为你不知道它被调用的时间和次数。
以下是原始函数拆分为move()
和draw()
的示例:
#include <gtk/gtk.h>
#include <cairo.h>
static int width, height,
posX = 0,
vX = 2;
static gboolean move(GtkWidget* widget)
{
GtkWidget* window = gtk_widget_get_toplevel(widget);
gtk_window_get_size(GTK_WINDOW(window), &width, &height);
if(posX + vX >= width || posX + vX == 0)
vX = -vX;
posX += vX;
gtk_widget_queue_draw(widget);
return TRUE;
}
static gboolean draw(GtkWidget* widget, cairo_t* cr)
{
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_set_line_width(cr, 100);
cairo_rectangle(cr, posX + 0.5, height/2 + 0.5, 100, 100);
cairo_stroke(cr);
return FALSE;
}
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(16, (GSourceFunc)move, window);
gtk_widget_show_all(window);
gtk_main();
}
不确定您的问题是否会得到解决,但无论如何都必须这样做。
答案 1 :(得分:2)
我认为您遇到的问题是您正在使用g_timeout_add
,就好像它是一个确切的时间源,而不是文档所述:
请注意,由于处理了其他事件源,超时功能可能会延迟。因此,不应依赖它们来进行精确计时。在每次调用超时函数之后,将根据当前时间和给定间隔重新计算下一次超时的时间(它不会尝试“赶上”延迟时间丢失的时间)。
这意味着在draw
回调中的代码可能会在每次调用时稍晚(或更晚)调用。由于未重新计算超时,错误加起来。你将失去同步并在错误的位置画画。这例如在帧被解码时发生在视频播放器中:如果帧需要太长时间才能被解码,它可能会被丢弃,因为可能我们已经太晚了并且需要显示下一帧。
我不确定什么是正确的解决方案,也许GTK +(或Clutter,它真的是为动画做的)开发人员可以给你一些提示,所以通过他们的IRC频道询问他们是个好主意。
然而,我在编程节拍器时遇到了这个问题。如果您尝试与g_timeout_add
同步,则会出现错误,并且您会失去同步。这就是我为我所做的工作。
首先,我在一开始就点了GTimer
,所以我有一个可靠,精确和绝对的时间参考。然后当我的回调被调用时,我:
G_SOURCE_REMOVE
以便不再调用当前事件源(基本上我将其替换为新事件源)这是我的节拍器代码供参考:https://github.com/liberforce/metrognome/blob/master/metronome.c
您也可以使用g_timeout_add_full
代替g_timeout_add
,这样您就可以使用更高的优先级。
我还建议阅读Owen Taylor关于gnome-shell中动画同步的一系列文章: