headmaster_1 发表于 2015-1-16 12:39:23

关于gtk控件显示与截图的一个问题

本帖最后由 headmaster_1 于 2015-1-16 12:42 编辑

小弟现在在eclipse下做一个GUI开发的插件,就是通过拖拽编辑图形界面,最终生成一个GTK的图形界面应用,类似于glade的功能,但是图形编辑的代码是在eclipse下用java写的。
<P style="LINE-HEIGHT: 30px; TEXT-INDENT: 2em">
<BLOCKQUOTE>static GdkPixmap* copyPixmap(GdkPixmap *source, gint width, gint height) {
if (source) {
GdkPixmap* pixmap = gdk_pixmap_new(source, width, height, -1);
GdkGC *gc = gdk_gc_new(source);
gdk_draw_drawable(pixmap, gc, source, 0, 0, 0, 0, -1, -1);
g_object_unref(gc);
g_object_unref(source);
return pixmap;
}
return NULL;
}
JNIEnv *m_envir;
jobject m_callback;
jmethodID m_IScreenshotCallback_storeImage;
//
typedef struct _GdkWindowPaint GdkWindowPaint;
struct _GdkWindowPaint {
GdkRegion *region;
GdkPixmap *pixmap;
gint x_offset;
gint y_offset;
};
static void exposeAllWidgetsCallback(GtkWidget *widget, gpointer data);
//
#define PREPARE_EVENT \
GdkEventExpose ev;\
ev.type = GDK_EXPOSE;\
ev.send_event = TRUE;\
ev.area.x = 0;\
ev.area.y = 0;\
ev.count = 0;
#define UPDATE_EVENT \
gdk_window_get_geometry(ev.window, NULL, NULL, &ev.area.width, &ev.area.height, NULL);\
ev.region = gdk_region_rectangle(&ev.area);
static void exposeWidget(GtkWidget *widget) {
GdkWindow *window = widget->window;
if (!GTK_WIDGET_REALIZED(widget)) {
return;
}
// g_warning ("type = %s", G_OBJECT_TYPE_NAME (widget));
if (GTK_IS_SPIN_BUTTON(widget)) {
// spin button
GtkWidgetClass *clazz = (GtkWidgetClass *)GTK_SPIN_BUTTON_GET_CLASS(widget);
GtkSpinButton *spin = GTK_SPIN_BUTTON (widget);
{
PREPARE_EVENT
ev.window = spin->panel;
UPDATE_EVENT
clazz->expose_event(widget, &ev);
}
// spin button also contains GtkEntry, so give a chance to expose it too, so no 'else' statement
}
if (GTK_IS_ENTRY(widget)) {
// single text
GtkWidgetClass *clazz = (GtkWidgetClass *)GTK_ENTRY_GET_CLASS(widget);
{
PREPARE_EVENT
ev.window = window;
UPDATE_EVENT
clazz->expose_event(widget, &ev);
}
//
{
PREPARE_EVENT
ev.window = ((GtkEntry*)widget)->text_area;
UPDATE_EVENT
clazz->expose_event(widget, &ev);
}
} else if (GTK_IS_TEXT_VIEW(widget)) {
// multi-line text
{
PREPARE_EVENT
ev.window = gtk_text_view_get_window((GtkTextView *)widget, GTK_TEXT_WINDOW_TEXT);
UPDATE_EVENT
gtk_widget_send_expose(widget, (GdkEvent*)&ev);
}
} else if (GTK_IS_TREE_VIEW(widget)) {
// tree
GtkWidgetClass *clazz = (GtkWidgetClass *)GTK_TREE_VIEW_GET_CLASS(widget);
GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
{
PREPARE_EVENT
ev.window = gtk_tree_view_get_bin_window(tree_view);
UPDATE_EVENT
clazz->expose_event(widget, &ev);
}
} else {
// everything else
{
PREPARE_EVENT
ev.window = window;
UPDATE_EVENT
gtk_widget_send_expose(widget, (GdkEvent*)&ev);
}
}
}
static void exposeAllWidgets(GtkWidget *widget) {
if (!GTK_IS_WIDGET(widget)) {
return;
}
exposeWidget(widget);
if (!GTK_IS_CONTAINER(widget)) {
return;
}
GtkContainer *container = GTK_CONTAINER(widget);
gtk_container_forall(container, exposeAllWidgetsCallback, 0);
}
static GdkPixmap* getPixmap(GdkWindow *window, int shouldCallback) {
if (!gdk_window_is_visible(window)) {
// don't deal with unmapped windows
return NULL;
}
gint width, height;
gdk_window_get_geometry(window, NULL, NULL, &width, &height, NULL);
//
GdkRectangle rect;
rect.x = 0; rect.y = 0; rect.width = width; rect.height = height;
//
GdkRegion *region = gdk_region_rectangle(&rect);
gdk_window_begin_paint_region(window, region);
//
region = gdk_region_rectangle(&rect);
gdk_window_invalidate_region(window, region, TRUE);
//
gpointer widget = NULL;
gdk_window_get_user_data(window, &widget);
if (widget != NULL) {
exposeAllWidgets((GtkWidget*)widget);
}
//
gdk_window_process_updates(window, TRUE);
//
GdkWindowObject *private = (GdkWindowObject *)(window);
GdkPixmap *internalPixmap = ((GdkWindowPaint *)private->paint_stack->data)->pixmap;
if (internalPixmap == NULL) {
return NULL;
}
//
g_object_ref(internalPixmap);
GdkPixmap *pixmap = copyPixmap(internalPixmap, width, height);
gdk_window_end_paint(window);
//
if (shouldCallback) {
(*m_envir)->CallVoidMethod(m_envir, m_callback, m_IScreenshotCallback_storeImage, wrap_pointer(m_envir, widget), wrap_pointer(m_envir, pixmap));
}
//
return pixmap;
}
static GdkPixmap* traverse(GdkWindow *window, int shouldCallback){
gint depth;
gdk_window_get_geometry(window, NULL, NULL, NULL, NULL, &depth);
// strange window
if (depth == 0) {
return NULL;
}
//
GdkPixmap *pixmap = getPixmap(window, shouldCallback);
if (pixmap == NULL) {
return NULL;
}
//
GdkGC *gc = gdk_gc_new(pixmap);
GList *children = gdk_window_get_children(window);
guint length = g_list_length(children);
//
guint i;
for (i = 0; i < length; i++) {
GdkWindow *win = g_list_nth_data(children, i);
GdkPixmap* pix = traverse(win, shouldCallback);
if (pix == NULL) {
continue;
}
gint x, y, width, height;
gdk_window_get_geometry(win, &x, &y, &width, &height, NULL);
gdk_draw_drawable(pixmap, gc, pix, 0, 0, x, y, width, height);
if (!shouldCallback) {
g_object_unref(pix);
}
}
g_object_unref(gc);
return pixmap;
}
static void exposeAllWidgetsCallback(GtkWidget *widget, gpointer data) {
exposeAllWidgets(widget);
}
static GdkPixmap* makeShot(GtkWidget* shellWidget) {
GdkWindow *window = shellWidget->window;
return traverse(window, m_callback != NULL);
}
JNIEXPORT JHANDLE JNICALL OS_NATIVE(_1makeShot)(
JNIEnv *envir, jobject that, JHANDLE widgetHandle, jobject callback) {
m_envir = envir;
if (callback != NULL) {
m_callback = (*envir)->NewGlobalRef(envir, callback);
jclass clazz = (*envir)->GetObjectClass(envir, m_callback);
m_IScreenshotCallback_storeImage = (*envir)->GetMethodID(envir, clazz, "storeImage", CALLBACK_SIG);
}
// make shot
GdkPixmap* pixmap = makeShot((GtkWidget*)unwrap_pointer(envir, widgetHandle));
// clean up
if (callback != NULL) {
(*envir)->DeleteGlobalRef(envir, m_callback);
}
m_callback = NULL;
return (JHANDLE)wrap_pointer(envir, pixmap);
}


为了使图形编辑时控件的图形尽可能逼真,我采取了这样一个方案:在后台运行起一个GTK的程序,这个GTK程序是由java代码控制运行起来的(实际是java代码通过JNI调用C代码运行起来)。假设现在这个GTK程序有三个Widget:一个Window,一个Fixed,一个Button。我在创建了这个Button并调用gtk_widget_show()方法显示出这个Button后,马上调用了一段“截图”的代码,代码如下:代码最下方的_1makeShot是提供给java代码调用的C接口,这个接口再调用了倒数第二个函数 static GdkPixmap* makeShot(GtkWidget* shellWidget)开始截图。也就是说这段截图的代码是通过把指向GtkWidget的指针作为参数,最终获取的返回结果是GdkPixmap*,即这个Widget的图形数据。java代码获取到这个图形数据后,再将其显示在我们的GUI开发工具上但是现在遇到了一个问题:我调用上述截图代码截到的图有点“滞后”了,我每次在新创建一个控件后马上截到的图是空白,而过一会截到的图才是正确的图。即button = gtk_button_new();gtk_widget_show(button);makeShot(button);这样一个逻辑最终截到的控件的图是空白。如果我不将makeShot(button)紧跟在gtk_widget_show(button)的后面,过一会再调用makeShot(button)(比如在线程中睡一会)是可以截到正确的button的图的。我在网上看到有人说gtk_widget_show()这个函数本身是异步的,它只是发了一个信号,具体的控件绘制工作会在后台进行。再根据我做的实验(即上一段所说的在调用gtk_widget_show()后睡一会再截图就能截到),出现这个现象的原因似乎就是由于控件的绘制是异步的,使得我截图的逻辑在控件绘制前就完成了。但是我这个项目本身具备特殊性,需要我必须实时地去截控件的图。例如用户在编辑控件时改变了控件的大小,这时控件的图形必然发生了变化,我需要马上改变后台控件的大小,并马上截下这个改变了大小的控件的图并将其绘制在前端的窗口上。基本就是用户的操作导致了GUI编辑窗口的重绘,后台运行的GTK窗口也要发生变化,GUI应用要马上截控件的图并反映在图形编辑窗口上。我曾经试过编写expose事件的回调函数,即g_signal_connect(button, "expose_event", G_CALLBACK(callback), NULL)我以为在调用expose_event的回调时控件的绘制已经完成了,所以这时应该可以正常截图。但是我关联了这个回调之后,发现控件根本绘制不出来了。看来是我对expose_event回调理解有误。现在的问题是:1、gtk_widget_show()实现控件绘制是异步的吗?2、如果gtk_widget_show()是异步的,有没有什么方法能让控件的绘制变成同步的吗?3、为什么编写expose_event的回调会让控件根本不再绘制?难道是这个事件本身有默认的回调函数,在这个默认的回调中会进行控件绘制,而我关联了自己的回调函数就没有调用控件绘制的代码,是这样吗?各位大神帮帮忙~~~

headmaster_1 发表于 2015-1-16 12:43:40

代码这块不知道怎么格式化啊,看上去不太好看

ACTom 发表于 2015-2-23 14:02:19

gtk_widget_show() 确实是异步的。
页: [1]
查看完整版本: 关于gtk控件显示与截图的一个问题