【问题标题】:How to make Gtk+ window background transparent?如何使 Gtk+ 窗口背景透明?
【发布时间】:2011-04-23 22:34:49
【问题描述】:

我想让 Gtk+ 窗口的背景透明,以便只有窗口中的小部件可见。我找到了一些教程:

http://mikehearn.wordpress.com/2006/03/26/gtk-windows-with-alpha-channels/

http://macslow.thepimp.net/?p=26

但他们似乎都在监听“expose”事件,然后委托 Cairo 进行渲染。这意味着添加到窗口的其他小部件不会呈现(例如,我也尝试向窗口添加按钮)。

我看到GtkWidget上有一个modify-bg方法:http://library.gnome.org/devel/gtk/stable/GtkWidget.html#gtk-widget-modify-bg

但是,GdkColor 似乎不接受透明度参数:http://library.gnome.org/devel/gdk/stable/gdk-Colormaps-and-Colors.html

我也尝试过 GtkWindow.set_opacity 方法,但这也设置了窗口内容的不透明度,这不是我想要的。

如果有人能在这方面提供任何指导,我将不胜感激。

【问题讨论】:

标签: c background window gtk transparent


【解决方案1】:

我将alphademo 示例更改为绘制一个按钮而不是红色圆圈。

此应用程序在 400x400 透明窗口上绘制按钮。

当您单击窗口时,应用程序会显示/隐藏标题栏。

#include <gtk/gtk.h>
#include <gdk/gdkscreen.h>
#include <cairo.h>

static void screen_changed(GtkWidget *widget, GdkScreen *old_screen, gpointer user_data);
static gboolean expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data);
static void clicked(GtkWindow *win, GdkEventButton *event, gpointer user_data);

int main(int argc, char **argv)
{
    gtk_init(&argc, &argv);

    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 400);
    gtk_window_set_title(GTK_WINDOW(window), "Alpha Demo");
    g_signal_connect(G_OBJECT(window), "delete-event", gtk_main_quit, NULL);

    gtk_widget_set_app_paintable(window, TRUE);

    g_signal_connect(G_OBJECT(window), "expose-event", G_CALLBACK(expose), NULL);
    g_signal_connect(G_OBJECT(window), "screen-changed", G_CALLBACK(screen_changed), NULL);

    gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
    gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
    g_signal_connect(G_OBJECT(window), "button-press-event", G_CALLBACK(clicked), NULL);

    GtkWidget* fixed_container = gtk_fixed_new();
    gtk_container_add(GTK_CONTAINER(window), fixed_container);
    GtkWidget* button = gtk_button_new_with_label("button1");
    gtk_widget_set_size_request(button, 100, 100);
    gtk_container_add(GTK_CONTAINER(fixed_container), button);

    screen_changed(window, NULL, NULL);

    gtk_widget_show_all(window);
    gtk_main();

    return 0;
}


gboolean supports_alpha = FALSE;
static void screen_changed(GtkWidget *widget, GdkScreen *old_screen, gpointer userdata)
{
    /* To check if the display supports alpha channels, get the colormap */
    GdkScreen *screen = gtk_widget_get_screen(widget);
    GdkColormap *colormap = gdk_screen_get_rgba_colormap(screen);

    if (!colormap)
    {
        printf("Your screen does not support alpha channels!\n");
        colormap = gdk_screen_get_rgb_colormap(screen);
        supports_alpha = FALSE;
    }
    else
    {
        printf("Your screen supports alpha channels!\n");
        supports_alpha = TRUE;
    }

    gtk_widget_set_colormap(widget, colormap);
}

static gboolean expose(GtkWidget *widget, GdkEventExpose *event, gpointer userdata)
{
   cairo_t *cr = gdk_cairo_create(widget->window);

    if (supports_alpha)
        cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0); /* transparent */
    else
        cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* opaque white */

    /* draw the background */
    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
    cairo_paint (cr);

    cairo_destroy(cr);

    return FALSE;
}

static void clicked(GtkWindow *win, GdkEventButton *event, gpointer user_data)
{
    /* toggle window manager frames */
    gtk_window_set_decorated(win, !gtk_window_get_decorated(win));
}

在 Ubuntu 10.04 上编译:

gcc alpha.c -o alpha -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/atk-1.0 -lgtk-x11-2.0

【讨论】:

  • 请注意,我在窗口中添加了一个 fixed_container,然后将按钮附加到它。
  • 效果很好。我还尝试将它与 gtk webkit 一起使用,并将其背景设置为透明,并且效果很好。
  • @echo-flow:您能否通过示例添加您的 gtk-webkit 答案?谢谢。
  • 注意:与其一一列出所有库文件位置进行编译,不如使用pkg-config:gcc alpha.c -o alpha $( pkg-config --cflags --libs gtk+-2.0 )。如果您的系统碰巧在非标准(或新)路径中安装了某些库,这也有效。
【解决方案2】:

感谢代码,这正是我想要的,尽管它需要修改才能运行 GTK3。还需要将暴露事件更改为“绘制”事件才能使其正常工作。所以这是我为 GTK3 修改的代码贡献:

#include <gtk/gtk.h>
#include <gdk/gdkscreen.h>
#include <cairo.h>

static void screen_changed(GtkWidget *widget, GdkScreen *old_screen, gpointer user_data);
static gboolean expose_draw(GtkWidget *widget, GdkEventExpose *event, gpointer userdata);
static void clicked(GtkWindow *window, GdkEventButton *event, gpointer user_data);

int main(int argc, char **argv) {
    gtk_init(&argc, &argv);

    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 400);
    gtk_window_set_title(GTK_WINDOW(window), "Alpha Demo");
    g_signal_connect(G_OBJECT(window), "delete-event", gtk_main_quit, NULL);

    gtk_widget_set_app_paintable(window, TRUE);

    g_signal_connect(G_OBJECT(window), "draw", G_CALLBACK(expose_draw), NULL);
    g_signal_connect(G_OBJECT(window), "screen-changed", G_CALLBACK(screen_changed), NULL);

    gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
    gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
    g_signal_connect(G_OBJECT(window), "button-press-event", G_CALLBACK(clicked), NULL);

    GtkWidget* fixed_container = gtk_fixed_new();
    gtk_container_add(GTK_CONTAINER(window), fixed_container);
    GtkWidget* button = gtk_button_new_with_label("button1");
    gtk_widget_set_size_request(button, 100, 100);
    gtk_container_add(GTK_CONTAINER(fixed_container), button);

    screen_changed(window, NULL, NULL);

    gtk_widget_show_all(window);
    gtk_main();

    return 0;
}


gboolean supports_alpha = FALSE;
static void screen_changed(GtkWidget *widget, GdkScreen *old_screen, gpointer userdata) {
    GdkScreen *screen = gtk_widget_get_screen(widget);
    GdkVisual *visual = gdk_screen_get_rgba_visual(screen);

    if (!visual) {
        printf("Your screen does not support alpha channels!\n");
        visual = gdk_screen_get_system_visual(screen);
        supports_alpha = FALSE;
    } else {
        printf("Your screen supports alpha channels!\n");
        supports_alpha = TRUE;
    }

        gtk_widget_set_visual(widget, visual);
}

static gboolean expose_draw(GtkWidget *widget, GdkEventExpose *event, gpointer userdata) {
    cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(widget));

    if (supports_alpha) {
        printf("setting transparent window\n");
        cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0); 
    } else {
        printf("setting opaque window\n");
        cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); 
    }

    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
    cairo_paint (cr);

    cairo_destroy(cr);

    return FALSE;
}

static void clicked(GtkWindow *window, GdkEventButton *event, gpointer user_data) {
    /* toggle window manager frames */
    gtk_window_set_decorated(window, !gtk_window_get_decorated(window));
}

这是我在 Ubuntu 15.04 上编译它的方式:

gcc alpha.c -o alpha  `pkg-config --cflags --libs glib-2.0` `pkg-config --cflags --libs gtk+-3.0`

希望这可以帮助其他人尝试让它在 GTK3 上运行。

【讨论】:

  • 因为 gtk-3.22 gdk_cairo_create 在 expose_draw 回调中已被弃用。请改用GdkWindow *window = gtk_widget_get_window (widget); cairo_surface_t *surface = gdk_window_create_similar_surface(window, CAIRO_CONTENT_COLOR_ALPHA, WINDOW_WIDTH, WINDOW_HEIGHT); cairo_t *cr = cairo_create (surface);
  • gtk-3 的 C 语言和 gtkmm-3 的 C++ 语言还有更好的解决方案,请参阅 [stackoverflow.com/questions/16832581/…
【解决方案3】:

感谢您的回答。我用python + GTK3重写代码:

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
import cairo


supports_alpha = False


def screen_changed(widget, old_screen, userdata=None):
    global supports_alpha

    screen = widget.get_screen()
    visual = screen.get_rgba_visual()

    if visual is None:
        print("Your screen does not support alpha channels!")
        visual = screen.get_system_visual()
        supports_alpha = False
    else:
        print("Your screen supports alpha channels!")
        supports_alpha = True

    widget.set_visual(visual)


def expose_draw(widget, event, userdata=None):
    global supports_alpha

    cr = Gdk.cairo_create(widget.get_window())

    if supports_alpha:
        print("setting transparent window")
        cr.set_source_rgba(1.0, 1.0, 1.0, 0.0) 
    else:
        print("setting opaque window")
        cr.set_source_rgb(1.0, 1.0, 1.0)

    cr.set_operator(cairo.OPERATOR_SOURCE)
    cr.paint()

    return False


def clicked(window, event, userdata=None):
    # toggle window manager frames
    window.set_decorated(not window.get_decorated())


if __name__ == "__main__":
    window = Gtk.Window()
    window.set_position(Gtk.WindowPosition.CENTER)
    window.set_default_size(400, 400)
    window.set_title("Alpha Demo")
    window.connect("delete-event", Gtk.main_quit)

    window.set_app_paintable(True)

    window.connect("draw", expose_draw)
    window.connect("screen-changed", screen_changed)

    window.set_decorated(False)
    window.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
    window.connect("button-press-event", clicked)

    fixed_container = Gtk.Fixed()
    window.add(fixed_container)
    button = Gtk.Button.new_with_label("button1")
    button.set_size_request(100, 100)
    fixed_container.add(button)

    screen_changed(window, None, None)

    window.show_all()
    Gtk.main()

【讨论】:

    【解决方案4】:

    对于那些使用 golang 的人,这里有一个使用 gotk3 (https://github.com/gotk3/gotk3):

    package main
    
    import (
        "github.com/gotk3/gotk3/cairo"
        "github.com/gotk3/gotk3/gdk"
        "github.com/gotk3/gotk3/gtk"
        "log"
    )
    
    var alphaSupported = false
    
    func main() {
        gtk.Init(nil)
    
        win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
        if err != nil {
            log.Fatal("Unable to create window:", err)
        }
        win.SetTitle("Simple Example")
        win.Connect("destroy", func() {
            gtk.MainQuit()
        })
    
        // Needed for transparency
        win.SetAppPaintable(true)
    
        win.Connect("screen-changed", func (widget *gtk.Widget, oldScreen *gdk.Screen, userData ...interface{}) {
            screenChanged(widget)
        })
        win.Connect("draw", func (window *gtk.Window, context *cairo.Context) {
            exposeDraw(window, context)
        })
    
        l, err := gtk.LabelNew("I'm transparent !")
        if err != nil {
            log.Fatal("Unable to create label:", err)
        }
    
        win.Add(l)
        win.SetDefaultSize(800, 600)
    
        screenChanged(&win.Widget)
    
        win.ShowAll()
        gtk.Main()
    }
    
    func screenChanged(widget *gtk.Widget) {
        screen, _ := widget.GetScreen()
        visual, _ := screen.GetRGBAVisual()
    
        if visual != nil {
            alphaSupported = true
        } else {
            println("Alpha not supported")
            alphaSupported = false
        }
    
        widget.SetVisual(visual)
    }
    
    func exposeDraw(w *gtk.Window, ctx *cairo.Context) {
        if alphaSupported {
            ctx.SetSourceRGBA(0.0, 0.0, 0.0, 0.25)
        } else {
            ctx.SetSourceRGB(0.0, 0.0, 0.0)
        }
    
        ctx.SetOperator(cairo.OPERATOR_SOURCE)
        ctx.Paint()
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-27
      • 2022-01-24
      相关资源
      最近更新 更多