GTK 绘图时闪烁
GTK Blinks when drawing
我是 GTK 的新手,我正在努力学习如何处理我心目中的项目。
问题是,我可以设法制作一个用于将 "Generators" 放置在 window 上的应用程序......每次我点击屏幕时,该应用程序都会在 window,准备另外一个准备放...
问题是程序在绘制所有发电机时闪烁...
每个生成器都必须一直重绘自己,这就是我认为的问题……这是代码……我怎样才能让它更快? ... 提前致谢!!
// gcc main.c -o main `pkg-config gtk+-3.0 --cflags --libs`
#include <gtk/gtk.h>
#include <stdlib.h>
#include <iostream>
#include <vector>
using namespace std;
class Gen{
public:
int x;
int y;
GdkPixbuf *pix;
GtkWidget *canvas;
bool placed;
Gen(GtkWidget *canvas){
this->canvas=canvas;
GError *err = NULL;
pix = gdk_pixbuf_new_from_file("./Img/gen.png", &err);
pix= gdk_pixbuf_scale_simple(pix,50,50, GDK_INTERP_BILINEAR);
x=10;y=10;
placed=0;
}
void draw(cairo_t *cr){
gdk_cairo_set_source_pixbuf(cr, pix, x, y);
cairo_paint(cr);
}
void updatePosition(int a, int b){
if(placed==0){
x=a-25;
y=b-25;
}
}
void place(){
placed=1;
}
};
class Map{
public:
vector<Gen *> Gens;
GtkWidget *window;
GtkWidget *canvas;
int xPointer,yPointer;
Map(GtkWidget *_window, GtkWidget *_canvas){
window=_window;
canvas=_canvas;
}
void draw(){
cairo_t *cr;
cr = gdk_cairo_create (gtk_widget_get_window(canvas));
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_rectangle(cr, xPointer-35, yPointer-35, 70, 70);
cairo_paint(cr);
for(vector<Gen *>::const_iterator i=Gens.begin();i!=Gens.end();i++){
(*i)->draw(cr);
}
cairo_destroy (cr);
}
void place(){
Gen *aux=Gens.back();
aux->place();
//Gens.push_back(new Gen(canvas));
}
void moving(int x,int y){
xPointer=x;yPointer=y;
if(Gens.size()==0){
Gens.push_back(new Gen(canvas));
}
else if (Gens.back()->placed==1){
Gens.push_back(new Gen(canvas));
}
Gen *aux=Gens.back();
aux->updatePosition(x,y);
this->draw();
cout<<"Elementos -> "<<Gens.size()<<"\n";
}
};
static gboolean
moving(GtkWidget *da, GdkEvent *event, gpointer data)
{
int x, y;
GdkModifierType state;
gdk_window_get_device_position (gdk_event_get_window ((GdkEvent *) event),
gdk_event_get_device ((GdkEvent *) event),
&x, &y, &state);
/*
(void)event; (void)data;
((Gen *)da)->draw();*/
Map *g=(Map *)data;
g->moving(x,y);
}
static gboolean
placing (GtkWidget *da, GdkEvent *event, gpointer data)
{
Map *g=(Map *)data;
g->place();
}
int main ( int argc, char **argv) {
GtkWidget *window;
GtkWidget *canvas;
gtk_init (&argc , &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_size_request (window,
500, 500);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_main_quit) , NULL);
canvas = gtk_drawing_area_new ();
Map *g=new Map(window,canvas);
gtk_container_add (GTK_CONTAINER (window), canvas);
gtk_widget_set_events(window, GDK_POINTER_MOTION_MASK);
//g_signal_connect (canvas, "draw", G_CALLBACK (drawing), (gpointer *)g);
g_signal_connect (window, "motion-notify-event", G_CALLBACK (moving), (gpointer *)g);
g_signal_connect (window, "button-press-event", G_CALLBACK (placing), (gpointer *)g);
//g_signal_connect (canvas, "motion-notify-event", (GCallback) on_window_draw, NULL);
gtk_widget_set_app_paintable(canvas, TRUE);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
这里有几点说明:
每次创建 Gen
对象时都会分配 Gen::pix。它是相同的 pixbuf,但是您创建一个,然后在使用缩放函数时创建另一个(意味着您正在泄漏原始 pixbuf 的内存),并且每个 Gen
对象都是如此。这真的很低效,所以使用静态 pix
成员,加载然后缩放 pixbuf 并修复内存泄漏将允许您只执行一次。
然后:您在绘图处理程序中调用 gdk_cairo_create
,但是从 GTK 3 开始,您应该将 cairo 上下文作为 draw
信号回调中的输入参数。我看到您正在通过运动事件调用自定义 draw
方法,这不是 GTK+ 绘图堆栈的工作方式!
正确做法:
- 主要连接到GtkDrawingArea
的draw
信号
- 在您的运动回调中,只需更改
Gen
对象的位置,然后为绘图区域调用 gtk_widget_queue_draw
。这将为您触发 draw
信号。
- 在连接到
draw
信号的回调中,然后在给定的 cairo 上下文中重绘 Gen
对象。
- 要提高性能,您可以使用 cairo 裁剪函数,或者调用
gtk_widget_queue_draw_area
或 gtk_widget_queue_draw_region
而不是 gtk_widget_queue_draw
。然后,您将在 draw
回调中收到的 cairo 上下文中获得一个预先计算的剪辑区域。通过这些提示,您可以准确地确定图像的哪一部分需要重绘,并避免不必要的工作。
请阅读官方文档The GTK+ Drawing Model。
我是 GTK 的新手,我正在努力学习如何处理我心目中的项目。
问题是,我可以设法制作一个用于将 "Generators" 放置在 window 上的应用程序......每次我点击屏幕时,该应用程序都会在 window,准备另外一个准备放... 问题是程序在绘制所有发电机时闪烁... 每个生成器都必须一直重绘自己,这就是我认为的问题……这是代码……我怎样才能让它更快? ... 提前致谢!!
// gcc main.c -o main `pkg-config gtk+-3.0 --cflags --libs`
#include <gtk/gtk.h>
#include <stdlib.h>
#include <iostream>
#include <vector>
using namespace std;
class Gen{
public:
int x;
int y;
GdkPixbuf *pix;
GtkWidget *canvas;
bool placed;
Gen(GtkWidget *canvas){
this->canvas=canvas;
GError *err = NULL;
pix = gdk_pixbuf_new_from_file("./Img/gen.png", &err);
pix= gdk_pixbuf_scale_simple(pix,50,50, GDK_INTERP_BILINEAR);
x=10;y=10;
placed=0;
}
void draw(cairo_t *cr){
gdk_cairo_set_source_pixbuf(cr, pix, x, y);
cairo_paint(cr);
}
void updatePosition(int a, int b){
if(placed==0){
x=a-25;
y=b-25;
}
}
void place(){
placed=1;
}
};
class Map{
public:
vector<Gen *> Gens;
GtkWidget *window;
GtkWidget *canvas;
int xPointer,yPointer;
Map(GtkWidget *_window, GtkWidget *_canvas){
window=_window;
canvas=_canvas;
}
void draw(){
cairo_t *cr;
cr = gdk_cairo_create (gtk_widget_get_window(canvas));
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_rectangle(cr, xPointer-35, yPointer-35, 70, 70);
cairo_paint(cr);
for(vector<Gen *>::const_iterator i=Gens.begin();i!=Gens.end();i++){
(*i)->draw(cr);
}
cairo_destroy (cr);
}
void place(){
Gen *aux=Gens.back();
aux->place();
//Gens.push_back(new Gen(canvas));
}
void moving(int x,int y){
xPointer=x;yPointer=y;
if(Gens.size()==0){
Gens.push_back(new Gen(canvas));
}
else if (Gens.back()->placed==1){
Gens.push_back(new Gen(canvas));
}
Gen *aux=Gens.back();
aux->updatePosition(x,y);
this->draw();
cout<<"Elementos -> "<<Gens.size()<<"\n";
}
};
static gboolean
moving(GtkWidget *da, GdkEvent *event, gpointer data)
{
int x, y;
GdkModifierType state;
gdk_window_get_device_position (gdk_event_get_window ((GdkEvent *) event),
gdk_event_get_device ((GdkEvent *) event),
&x, &y, &state);
/*
(void)event; (void)data;
((Gen *)da)->draw();*/
Map *g=(Map *)data;
g->moving(x,y);
}
static gboolean
placing (GtkWidget *da, GdkEvent *event, gpointer data)
{
Map *g=(Map *)data;
g->place();
}
int main ( int argc, char **argv) {
GtkWidget *window;
GtkWidget *canvas;
gtk_init (&argc , &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_size_request (window,
500, 500);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_main_quit) , NULL);
canvas = gtk_drawing_area_new ();
Map *g=new Map(window,canvas);
gtk_container_add (GTK_CONTAINER (window), canvas);
gtk_widget_set_events(window, GDK_POINTER_MOTION_MASK);
//g_signal_connect (canvas, "draw", G_CALLBACK (drawing), (gpointer *)g);
g_signal_connect (window, "motion-notify-event", G_CALLBACK (moving), (gpointer *)g);
g_signal_connect (window, "button-press-event", G_CALLBACK (placing), (gpointer *)g);
//g_signal_connect (canvas, "motion-notify-event", (GCallback) on_window_draw, NULL);
gtk_widget_set_app_paintable(canvas, TRUE);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
这里有几点说明:
每次创建 Gen
对象时都会分配 Gen::pix。它是相同的 pixbuf,但是您创建一个,然后在使用缩放函数时创建另一个(意味着您正在泄漏原始 pixbuf 的内存),并且每个 Gen
对象都是如此。这真的很低效,所以使用静态 pix
成员,加载然后缩放 pixbuf 并修复内存泄漏将允许您只执行一次。
然后:您在绘图处理程序中调用 gdk_cairo_create
,但是从 GTK 3 开始,您应该将 cairo 上下文作为 draw
信号回调中的输入参数。我看到您正在通过运动事件调用自定义 draw
方法,这不是 GTK+ 绘图堆栈的工作方式!
正确做法:
- 主要连接到GtkDrawingArea 的
- 在您的运动回调中,只需更改
Gen
对象的位置,然后为绘图区域调用gtk_widget_queue_draw
。这将为您触发draw
信号。 - 在连接到
draw
信号的回调中,然后在给定的 cairo 上下文中重绘Gen
对象。 - 要提高性能,您可以使用 cairo 裁剪函数,或者调用
gtk_widget_queue_draw_area
或gtk_widget_queue_draw_region
而不是gtk_widget_queue_draw
。然后,您将在draw
回调中收到的 cairo 上下文中获得一个预先计算的剪辑区域。通过这些提示,您可以准确地确定图像的哪一部分需要重绘,并避免不必要的工作。
draw
信号
请阅读官方文档The GTK+ Drawing Model。