本帖最后由 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的回调会让控件根本不再绘制?难道是这个事件本身有默认的回调函数,在这个默认的回调中会进行控件绘制,而我关联了自己的回调函数就没有调用控件绘制的代码,是这样吗? 各位大神帮帮忙~~~ |