LINUX.ORG.RU

Бегущая строка на GTK

 


0

2

Здравствуйте! Необходимо реализовать бегущую строку текста на gtk. Для примера вот движущаяся окружность:

#include <gtk/gtk.h>

#define WIDTH 	1920
#define HEIGHT 	360

//gcc `pkg-config --cflags gtk+-3.0` -o test test.c `pkg-config --libs gtk+-3.0`

gboolean pos = 0.0;

gboolean timer_func (gpointer data) {
	
  	GtkWidget *widget = (GtkWidget *)data;
  	gtk_widget_queue_draw(widget);

  	return TRUE;
}

gboolean draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data) {

	guint width, height;
  	GdkRGBA color;
	pos += 5.0;
  	width = gtk_widget_get_allocated_width (widget);
  	height = gtk_widget_get_allocated_height (widget);
  	cairo_arc (cr,
             pos, height / 2.0,
             MIN (width, height) / 2.0,
             0, 2 * G_PI);

  	gtk_style_context_get_color (gtk_widget_get_style_context (widget),
                               0,
                               &color);
  	gdk_cairo_set_source_rgba (cr, &color);
  	cairo_fill (cr);

	if(pos >= WIDTH) pos =0;

 	return TRUE;
}

void activate (GtkApplication* app, gpointer        user_data) {
  	
  	GtkWidget *window;
  	window = gtk_application_window_new (app);
  	gtk_window_set_title (GTK_WINDOW (window), "Window");
  	gtk_window_set_default_size (GTK_WINDOW (window), WIDTH, HEIGHT);

  	GtkWidget *drawing_area = gtk_drawing_area_new ();

  	gtk_widget_set_size_request (drawing_area, WIDTH, HEIGHT);
  	g_signal_connect (G_OBJECT (drawing_area), "draw",
                    G_CALLBACK (draw_callback), NULL);
  	gtk_container_add(GTK_CONTAINER(window),drawing_area);

  	gtk_widget_show_all (window);

  	g_timeout_add(1, timer_func, (gpointer) drawing_area);
}

int main (int argc, char **argv) {

  	GtkApplication *app;
  	int status;
  	app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
  	g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  	status = g_application_run (G_APPLICATION (app), argc, argv);
  	g_object_unref (app);

  	return status;
}
При перемещении возникают «фризы» и нет плавности. Возможно ли это исправить? Складывается ощущене, что не работает двойная буферизация.


gtk_widget_set_double_buffered (widget, TRUE);

Оно вроде по дефолту включено должно быть. Но все равно попробуй.

Как вариант на время перемещения можно отключать анимацию.

Cactus64k
()
Ответ на: комментарий от Cactus64k

Как вариант на время перемещения можно отключать анимацию.

Это как?
gtk_widget_set_double_buffered - устарела, но я пробовал ее использовать, без изменений

Ya-NET
() автор топика

У меня только еле заметные 1-2 поддёргивания за цикл, а так плавно. Mate 1.12 (gtk2), никакого композитинга, встроенная intel (но драйвер от xserver).

gag ★★★★★
()
g_timeout_add(1, timer_func, (gpointer) drawing_area);

А draw_callback точно за меньше 1 миллисекунды успевает сработать? 1000 кадров в секунду не слишком много? Тут бы синхронизироваться с vsync.

gag ★★★★★
()
Ответ на: комментарий от gag

попробовал 16 мс, подергивания тоже есть. Тестирую на conga-QMX6 (yocto2.1) + Mitsubishi aa192aa01, но и на моем ПК (Intel HD XXXX) есть подергивания, но без фризов. Таймер ровно срабатывает, проблемы на уровне отрисовки думаю

Ya-NET
() автор топика

Сделай таймер не периодическим, а одноразовым и перезапускай его в своей timer_func после отрисовки. Движение станет намного плавнее.

alexku
()

При перемещении возникают «фризы» и нет плавности. Возможно ли это исправить? Складывается ощущене, что не работает двойная буферизация.

да, это очень просто исправить — вместо таймера просто делай gtk_widget_queue_draw в конце draw_callback.

это позволяет достичь максимальной плавности, которую позволяет текущее окружение.

в системах с композитным WM это будет автоматически делать 60fps+vsync.

waker ★★★★★
()

с таймером получается фигня, потому что на 1000Hz ты порождаешь 16 draw events за то время, когда должен происходить 1, да еще и не дожидаясь окончания отрисовки предыдущего кадра. это работать нормально не может в принципе.

waker ★★★★★
()
Ответ на: комментарий от waker

да, это очень просто исправить — вместо таймера просто делай gtk_widget_queue_draw в конце draw_callback.

крутое и простое решение, но проблемы (у меня) это не исправило(

Ya-NET
() автор топика
Ответ на: комментарий от waker

Спасибо. Вот:

#include <gtk/gtk.h>

#define WIDTH 	1920
#define HEIGHT 	360

//gcc `pkg-config --cflags gtk+-3.0` -o test test.c `pkg-config --libs gtk+-3.0`

gboolean pos = 0.0;

gboolean draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data) {

	guint width, height;
  	GdkRGBA color;
	pos += 3;
  	width = gtk_widget_get_allocated_width (widget);
  	height = gtk_widget_get_allocated_height (widget);
  	cairo_arc (cr,
             pos, height / 2.0,
             MIN (width, height) / 2.0,
             0, 2 * G_PI);

  	gtk_style_context_get_color (gtk_widget_get_style_context (widget),
                               0,
                               &color);
  	gdk_cairo_set_source_rgba (cr, &color);
  	cairo_fill (cr);

	if(pos >= width) 
		pos =0.0;

  	gtk_widget_queue_draw(widget);

 	return TRUE;
}

void activate (GtkApplication* app, gpointer  user_data) {
  	
	GtkWidget *window;
  	window = gtk_application_window_new (app);
  	gtk_window_set_title (GTK_WINDOW (window), "Window");
  	gtk_window_set_default_size (GTK_WINDOW (window), WIDTH, HEIGHT);

  	GtkWidget *drawing_area = gtk_drawing_area_new ();

	g_object_set(drawing_area,"double-buffered",FALSE,"app-paintable",FALSE,NULL);
  	gtk_widget_set_size_request (drawing_area, WIDTH, HEIGHT);
  	g_signal_connect (G_OBJECT (drawing_area), "draw",
                    G_CALLBACK (draw_callback), NULL);
  	gtk_container_add(GTK_CONTAINER(window),drawing_area);

  	gtk_widget_show_all (window);
}

int main (int argc, char **argv) {

	GtkApplication *app;
  	int status;

  	app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
  	g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  	status = g_application_run (G_APPLICATION (app), argc, argv);
  	g_object_unref (app);

  	return status;
}

Ya-NET
() автор топика
Ответ на: комментарий от Ya-NET

Есть у меня подозрение, что тебе ничего не помогает потому, что ты пихаешь отрисовку в очередь. Тут ни периодичность таймера, ни перезапуск работать не будут. Потому что нет гарантии, что отрисовка произойдёт до следующего срабатывания таймера. Попробуй отрисовывать непосредственно в timer_proc, вызывая gtk_widget_draw() (ну, или какая-то другая, которая делает немедленную отрисовку, я не знаток gtk). Этим ты гарантируешь, что твои отрисовки не будут накапливаться в очереди и отрисовываться одним скопом, когда решит WM.

alexku
()
Ответ на: комментарий от alexku

Попробуй отрисовывать непосредственно в timer_proc, вызывая gtk_widget_draw()

это не будет работать корректно вообще. в GTK (и большинстве других тулкитов) рисовать можно только из обработчика draw event, в противном случае не гарантируется что что-либо нарисуется.

waker ★★★★★
()
Ответ на: комментарий от Ya-NET

попробуй так:

#include <gtk/gtk.h>

#define WIDTH 1920
#define HEIGHT 360

//gcc `pkg-config --cflags gtk+-3.0` -o test test.c `pkg-config --libs gtk+-3.0`

gboolean pos = 0.0;

gboolean draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data) {

    guint width, height;
    GdkRGBA color;
    pos += 3;
    width = gtk_widget_get_allocated_width (widget);
    height = gtk_widget_get_allocated_height (widget);
    cairo_set_source_rgba (cr, 1,1,1,1);
    cairo_paint (cr);
    cairo_set_source_rgba (cr, 0,0,0,1);
    cairo_arc (cr,
            pos, height / 2.0,
            MIN (width, height) / 2.0,
            0, 2 * G_PI);
    cairo_fill (cr);

    if(pos >= width) 
        pos =0.0;

    gtk_widget_queue_draw(widget);

    return TRUE;
}

void activate (GtkApplication* app, gpointer  user_data) {

    GtkWidget *window;
    window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "Window");
    gtk_window_set_default_size (GTK_WINDOW (window), WIDTH, HEIGHT);

    GtkWidget *drawing_area = gtk_drawing_area_new ();

    //g_object_set(drawing_area,"double-buffered",FALSE,"app-paintable",FALSE,NULL);
    gtk_widget_set_size_request (drawing_area, WIDTH, HEIGHT);
    g_signal_connect (G_OBJECT (drawing_area), "draw",
            G_CALLBACK (draw_callback), NULL);
    gtk_container_add(GTK_CONTAINER(window),drawing_area);

    gtk_widget_show_all (window);
}

int main (int argc, char **argv) {

    GtkApplication *app;
    int status;

    app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);

    return status;
}
waker ★★★★★
()
Ответ на: комментарий от waker

это не будет работать корректно вообще

Это почему это? Чем прямой вызов отрисовки отличается от инициированного эвентом?

alexku
()
Ответ на: комментарий от alexku

Это почему это? Чем прямой вызов отрисовки отличается от инициированного эвентом?

тем, что он срабатывает через раз, или вообще ни разу.

waker ★★★★★
()
Ответ на: комментарий от Ya-NET

ну я пробовал в виртуалке, плыл не очень плавно, пока я не уменьшил размер окна, иначе тормозит потому что cairo не может его нарисовать за 16.6ms.

если окно поменьше — то довольно плавно, но по виртуалке трудно определить.

добавь измерение времени исполнения функции draw_callback

если занимает >16ms — значит надо что-то оптимизировать.

(сразу скажу — с cairo это будет очень тяжело)

ps замени круг на прямоугольник, лучше если меньшего размера — они пошустрее рисуются.

waker ★★★★★
()
Последнее исправление: waker (всего исправлений: 1)

gboolean pos = 0.0; ересь

Deleted
()
Ответ на: комментарий от waker

Круг, прямоугольник, не в этом суть. Нужен «бегущий» текст, и текс большого размера (для инфо. табло), а в этом случае ториоза проявляются много сильнее. Оптимизировать? Думаю, можно, но результата не хватит. Попробую QT, а затем, если поможет, openGL. Посмотрим как там)
Всем спасибо за ответы, советы и помощь!

Ya-NET
() автор топика
Ответ на: комментарий от Ya-NET

я не удивлюсь, если большой текст будет рисоваться быстрее такого круга

пробуй

начни рисовать что-то что работает быстро, добейся плавности.

воткни то что медленно.

если невозможно оптимизнуть — рендерни весь большой текст в большой cairo_surface, и скролль готовую картинку — это работает быстро.

waker ★★★★★
()
Ответ на: комментарий от Ya-NET

самое главное: для плавности нужен VSYNC. удостоверься что он на твоем окружении корректно работает. для этого нужен композитный WM, нормальные дрова, и возможно дополнительные твики в настройках иксов.

это же линукс..

waker ★★★★★
()
Ответ на: комментарий от waker

Скролить пробовал и это ад) исходники удалил, но плавностью не пахло)
gtk_adjustment_set_value (adj, pos++); - такой смыысл, верно?

Ya-NET
() автор топика
Ответ на: комментарий от Ya-NET

Скролить пробовал и это ад) исходники удалил, но плавностью не пахло)

gtk_adjustment_set_value (adj, pos++); - такой смыысл, верно?

не знаю, надо остальной код увидеть чтобы понять что там за смысл :)

рисовать готовую картинку — это самое быстрое.

waker ★★★★★
()
Ответ на: комментарий от Ya-NET

под IMX6 сложно найти нормальные дрова! vivante это боль

ну я имел ввиду нормальные исключительно в плане VSYNC. остальное неважно, если ты просто картинку готовую рисуешь.

waker ★★★★★
()

слушай, а ты не пробовал не таймер ставить, а IDLE job?

я давно в gtk не тыкал, это должно быть что-то «add_idle» + время меряет по какому-нибудь таймеру.

вообще таймер для таких задач как-то криво использовать.

i36_zubov
()
Ответ на: комментарий от waker

тем, что он срабатывает через раз, или вообще ни разу.

Аргументация бронебойная. По каким причинам «через раз или вообще ниразу»? И, можно не отвечать. ТС бороться за жизнь прекратил тему бросил, ему уже не надо. А мне и подавно.

alexku
()
Ответ на: комментарий от alexku

Аргументация бронебойная. По каким причинам «через раз или вообще ниразу»? И, можно не отвечать.

это не аргументация, а факт. к сожалению, я не смог нагуглить, где это поведение описано в документации, но проверить очень просто. с этим сталкивается практически каждый, когда что-то гуйное начинает кодить.

косвенно, все это описано здесь, хоть и не говорится прямо: https://developer.gnome.org/gtk3/stable/chap-drawing-model.html

например, если ты что-то нарисуешь по таймеру не используя draw signal — после этого может дернуться дефолтный обработчик draw signal у виджета или родительского окна, и затереть то, что ты нарисовал.

waker ★★★★★
()
Ответ на: комментарий от waker

косвенно, все это описано здесь, хоть и не говорится прямо: https://developer.gnome.org/gtk3/stable/chap-drawing-model.html

Так туда и надо было послать топик-стартера! Там упоминаются 2 интересные вещи:

If you are doing an animation, you can use gtk_widget_add_tick_callback()

и

GtkWindow and GtkEventBox are the two widgets that allow turning off drawing of default contents by calling gtk_widget_set_app_paintable()

Вероятно, это решило бы проблему. Но ТС почил...

alexku
()
Ответ на: комментарий от alexku

If you are doing an animation, you can use gtk_widget_add_tick_callback()

он делает почти в точности то же самое что я предложил — из этого tick callback все равно надо дергать queue_draw.

а еще он 3.8+ (в моем проекте есть поддержка и GTK2, и GTK<3.8)

waker ★★★★★
()
Ответ на: комментарий от alexku

ТС тему бросил, ему уже не надо

Вышел с работы, доехал домой, кушает, скоро ответит... ;)

Оффтоп: неужели в GTK так сложно делается плавная анимация? Или просто ТС не умеет ее готовить?

I-Love-Microsoft ★★★★★
()

если я правильно понимаю, что тебе нужно, смотри на gtk_widget_add_tick_callback(). пример - в gtk3-demo, сниппет pixbuf

ananas ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Оффтоп: неужели в GTK так сложно делается плавная анимация? Или просто ТС не умеет ее готовить?

скорее в линуксе, чем в GTK. она делается ровно так, как я написал в своем 1м посте. естественно, при выполнении остальных условий.

я думаю, что у ТС оно не работает, потому что кадр рисуется слишком медленно (каира аццки тормозная, аппаратно ускоряется только для некоторых простых примитивов, и по-моему только на опенсорс дровах, и то не всех), и потому что он не меряет время между кадрами, а просто инкрементирует позицию по 3px.

кстати

gboolean pos = 0.0;

LOL

waker ★★★★★
()
Последнее исправление: waker (всего исправлений: 1)
Ответ на: комментарий от Ya-NET

Скролить пробовал и это ад) исходники удалил, но плавностью не пахло)

Быстрее отрисовки в буфер полезной части и его скроллинга ничего быть не может. Если он у тебя тормозит, значит что-то делаешь совсем не так.

mashina ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.