繁体   English   中英

GTK3和Cairo动画抽搐

[英]Gtk3 and cairo animation twitch

我用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();
}

首先,请考虑您的原始程序在我的系统上可以完美运行,因此这可能是您系统上的问题。 您可以尝试提高程序的优先级,但是基本上您的代码容易受到此类问题的影响:有关g_timeout_add ,请参见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();
}

不确定您的问题是否会解决,但是还是必须这样做。

我认为您遇到的问题是,您正在使用g_timeout_add ,就好像它是确切的计时源一样,而不是文档所述:

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

这意味着在每次调用时, draw回调中的代码可能会稍后(或稍后)被调用。 由于未重新计算超时, 因此错误总计 您将失去同步并在错误的位置绘制。 例如,在视频播放器中,当解码一帧时会发生这种情况:如果帧花费的时间太长而无法解码,则可能会丢弃该帧,因为也许我们已经来不及了,需要显示下一帧。

我不确定什么是正确的解决方案,也许GTK +(或Clutter,实际上是为动画制作的)开发人员可以给您一些提示,所以最好通过IRC频道询问他们。

但是,我在编程节拍器时遇到了这个问题。 如果您尝试与g_timeout_add同步,则错误加总,您将不同步。 这是我为我所做的工作。

首先,我从一开始就启动了GTimer ,所以我有一个可靠,准确和绝对的时间参考。 然后,当我的回调被调用时,我:

  • 计算自上次滴答以来的经过时间(自您上一帧以来)
  • 播放我的刻度(在您的情况下,将对象显示在正确的位置)
  • 计算下一个刻度(下一帧)之前还剩下多少时间
  • 用该值调用g_timeout_add(它将创建一个新的事件源)
  • 返回G_SOURCE_REMOVE以便不再调用当前事件源(基本上我将其替换为新事件源)

这是我的节拍器代码以供参考: https : //github.com/liberforce/metrognome/blob/master/metronome.c

您也可以使用g_timeout_add_full代替g_timeout_add以便可以使用更高的优先级。

我还建议阅读Owen Taylor撰写的有关gnome-shell中的动画同步的一系列文章:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM