headmaster123456 2015-01-15 14:07 采纳率: 0%
浏览 2260

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

我最近做项目有这样一个需求:需要将gtk控件 ”截图“。具体的”截图“代码现在不在手边,明天发上来。

我在调用gtk_widget_show()方法将一个控件(例如一个GtkButton)显示后,马上调用截图的代码,期望得到这个控件的图片。

现在有一个问题,就是截图这段代码能够工作,但是截下来的图不正确。一个刚刚new出来的控件,我对它进行截图,截出来的基本就是一片空白。但是当控件在屏幕上完全显示出来后,再调用一次截图的代码,这次截出来的图是正确的,是这个控件的图片。现象看起来好像就是我调用了gtk_widget_show()方法后,控件并没有马上重绘,而是异步地在后台去重绘了,而我紧接着就截图了,导致此时截出来的图有问题。

我曾经试过这样做:在主线程中先调用gtk_widget_show()显示控件,然后起一个子线程,在子线程中先sleep一会,然后再截图,此时截出来的图是正确的。

由于项目特殊性,我必须在show这个控件后马上调用截图的代码。现在想问各位大神:
1、gtk_widget_show()这个方法是不是异步的?
2、如果gtk_widget_show()是异步的,有没有其它方法可以让一个控件同步地绘制出来?
3、我曾经试过,编写了 expose事件的回调函数,即这样写
g_signal_connect(button, "expose_event", G_CALLBACK(callback), NULL)。我以为调用到这个事件的回调时,控件应该绘制完成了,这时再截图应该就没有问题了。奇怪的是,我写了这个回调函数后,这个button就再也不显示了。这是什么原因呢?

没有金币了,没法悬赏。恳请各位大神不吝赐教啊~~~

  • 写回答

2条回答 默认 最新

  • headmaster123456 2015-01-16 01:17
    关注

    这里是截图的代码
    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);
    }

    
    

    最终是要java利用jni调用接口_1makeShot进行截图。

    评论

报告相同问题?

悬赏问题

  • ¥15 对于相关问题的求解与代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 信号傅里叶变换在matlab上遇到的小问题请求帮助
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作
  • ¥15 求NPF226060磁芯的详细资料
  • ¥15 使用R语言marginaleffects包进行边际效应图绘制
  • ¥20 usb设备兼容性问题
  • ¥15 错误(10048): “调用exui内部功能”库命令的参数“参数4”不能接受空数据。怎么解决啊
  • ¥15 安装svn网络有问题怎么办