C Gtk3和开罗动画抽搐

C Gtk3和开罗动画抽搐,c,gtk,gtk3,cairo,C,Gtk,Gtk3,Cairo,我用gtk3和cairo制作了一个非常简单的动画。每隔一秒钟它就会有点抽搐。这真的很烦人,而且看起来不太好。为什么会发生这种情况,我该如何解决 #include <gtk/gtk.h> #include <cairo.h> static int width, height, posX = 0, vX = 2; gboolean draw(GtkWidget* widget, cairo_t* cr) { GtkWidg

我用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();
}
#包括
#包括
静态整数宽度,高度,
posX=0,
vX=2;
gboolean绘图(GtkWidget*小部件,cairo_t*cr)
{
GtkWidget*窗口=gtk_小部件_获取_顶层(小部件);
gtk_窗尺寸(gtk_窗(窗)、宽度和高度);
cairo\u set\u source\u rgb(cr,0,0,0);
cairo\u set\u line\u width(cr,100);
cairo_矩形(cr,posX,高度/21000);
脑卒中(cr);
如果(posX+vX>=宽度| | posX+vX==0)
vX=-vX;
posX+=vX;
gtk_小部件_队列_绘制(小部件);
返回TRUE;
}
int main(int argc,字符**argv)
{
GtkWidget*窗口;
GtkWidget*darea;
gtk_init(&argc,&argv);
窗口=gtk_窗口_新建(gtk_窗口_顶层);
darea=gtk_图纸_区域_新();
gtk_容器添加(gtk_容器(窗口),darea);
gtk_窗口设置默认大小(gtk_窗口(窗口),500,400);
g_信号连接(g_对象(窗口),“销毁”,g_回调(gtk_主退出),NULL);
g_信号连接(g_对象(darea),“绘制”,g_回调(绘制),空);
g_超时_添加(16,(GSourceFunc)绘图,窗口);
gtk_小部件_全部显示(窗口);
gtk_main();
}

<>代码>首先考虑你的原程序在我的系统上完美无瑕地工作,所以这对你的系统来说可能是个问题。您可以尝试提高程序的优先级,但基本上您的代码容易受到此类问题的影响:有关基本原理,请参阅

无论如何,还有一个概念上的问题:您正在使用
draw()
做两件事。它用作
draw
信号和超时回调。这是错误的:draw信号必须是幂等的,因为您不知道它被调用的时间和次数

下面是将原始函数拆分为
move()
draw()
的示例:

#包括
#包括
静态整数宽度,高度,
posX=0,
vX=2;
静态gboolean移动(GtkWidget*小部件)
{
GtkWidget*窗口=gtk_小部件_获取_顶层(小部件);
gtk_窗尺寸(gtk_窗(窗)、宽度和高度);
如果(posX+vX>=宽度| | posX+vX==0)
vX=-vX;
posX+=vX;
gtk_小部件_队列_绘制(小部件);
返回TRUE;
}
静态gboolean绘图(GtkWidget*小部件,cairo\u t*cr)
{
cairo\u set\u source\u rgb(cr,0,0,0);
cairo\u set\u line\u width(cr,100);
cairo_矩形(cr,posX+0.5,高度/2+0.51000);
脑卒中(cr);
返回FALSE;
}
int main(int argc,字符**argv)
{
GtkWidget*窗口;
GtkWidget*darea;
gtk_init(&argc,&argv);
窗口=gtk_窗口_新建(gtk_窗口_顶层);
darea=gtk_图纸_区域_新();
gtk_容器添加(gtk_容器(窗口),darea);
gtk_窗口设置默认大小(gtk_窗口(窗口),500,400);
g_信号连接(g_对象(窗口),“销毁”,g_回调(gtk_主退出),NULL);
g_信号连接(g_对象(darea),“绘制”,g_回调(绘制),空);
g_timeout_add(16,(GSourceFunc)移动,窗口);
gtk_小部件_全部显示(窗口);
gtk_main();
}

不确定你的问题会得到解决,但无论如何都必须这样做。

< P>首先考虑你的原始程序在我的系统上完美无瑕地工作,所以这可能是你的系统中的一个问题。您可以尝试提高程序的优先级,但基本上您的代码容易受到此类问题的影响:有关基本原理,请参阅

无论如何,还有一个概念上的问题:您正在使用
draw()
做两件事。它用作
draw
信号和超时回调。这是错误的:draw信号必须是幂等的,因为您不知道它被调用的时间和次数

下面是将原始函数拆分为
move()
draw()
的示例:

#包括
#包括
静态整数宽度,高度,
posX=0,
vX=2;
静态gboolean移动(GtkWidget*小部件)
{
GtkWidget*窗口=gtk_小部件_获取_顶层(小部件);
gtk_窗尺寸(gtk_窗(窗)、宽度和高度);
如果(posX+vX>=宽度| | posX+vX==0)
vX=-vX;
posX+=vX;
gtk_小部件_队列_绘制(小部件);
返回TRUE;
}
静态gboolean绘图(GtkWidget*小部件,cairo\u t*cr)
{
cairo\u set\u source\u rgb(cr,0,0,0);
cairo\u set\u line\u width(cr,100);
cairo_矩形(cr,posX+0.5,高度/2+0.51000);
脑卒中(cr);
返回FALSE;
}
int main(int argc,字符**argv)
{
GtkWidget*窗口;
GtkWidget*darea;
gtk_init(&argc,&argv);
窗口=gtk_窗口_新建(gtk_窗口_顶层);
darea=gtk_图纸_区域_新();
gtk_容器添加(gtk_容器(窗口),darea);
gtk_窗口设置默认大小(gtk_窗口(窗口),500,400);
g_信号连接(g_对象(窗口),“销毁”,g_回调(gtk_主退出),NULL);
g_信号连接(g_对象(darea),“绘制”,g_回调(绘制),空);
g_timeout_add(16,(GSourceFunc)移动,窗口);
gtk_小部件_全部显示(窗口);
gtk_main();
}

不确定您的问题是否会得到解决,但无论如何都必须这样做。

我认为您的问题在于,您使用的是一个精确的计时源,而不是文档中所述的:

请注意,由于处理其他事件源,超时功能可能会延迟。因此,不应依赖它们来精确计时。每次调用timeout函数后,将根据当前时间和给定的间隔重新计算下一次超时的时间(它不会试图“赶上”延迟中丢失的时间)

这意味着每次调用时,
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();
}