如何在Linux上使gtk窗口的背景透明?

8
我希望将背景设置为透明,只显示小部件。
这是我的代码:
#include <gtk/gtk.h>
int main (int argc, char *argv[])
{
    gtk_init (&argc, &argv);
    GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    // Title
    gtk_window_set_title(GTK_WINDOW (window), "Transparency");
    //gtk_window_set_opacity(GTK_WINDOW(window), 0.5);

    // CSS
    GtkCssProvider *provider = gtk_css_provider_new();
    GdkDisplay *display = gdk_display_get_default();
    GdkScreen *screen = gdk_display_get_default_screen(display);
    gtk_style_context_add_provider_for_screen(screen, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
    gtk_css_provider_load_from_data(GTK_CSS_PROVIDER (provider),
                                    "GtkWindow {\n"
                                    "   background-color: rgba(0,0,0,0);\n"
                                    "}\n",
                                     -1, NULL);
    g_object_unref (provider);

    // Window
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    gtk_window_resize(GTK_WINDOW(window), 400, 300);
    gtk_widget_show_all(window);

    gtk_main();
    return 0;
}

我使用的是gtk3。当程序执行时,它只显示为黑色。CSS(或rgba)功能不起作用。我尝试使用gtk_window_set_opacity(),但它仍然只显示为黑色。我该如何修复我的代码?


请使用cairo,详见此线程。https://dev59.com/jW865IYBdhLWcg3wQMMD - LiuLang
2个回答

16

我按照评论中提供的链接进行了尝试,但不幸的是它是为Gtk 2编写的。我重新为Gtk 3进行了调整。(我正在使用Gtk 3.8,但据我所知它在Gtk 3.10中未使用任何已弃用的内容)。该程序生成一个带有按钮的绿色半透明正方形。当然,您可以通过将cairo_set_source_rgba函数的最后一个参数更改为0来完全透明化该正方形。

注意:我使用以下命令编译它(假设文件名为transparent.c):

gcc -o transparent transparent.c `pkg-config gtk+-3.0 --libs --cflags`

以下是代码:

C语言版本

/**
 * Original code by: Mike - http://plan99.net/~mike/blog (now a dead link--unable to find it).
 * Modified by karlphillip for StackExchange:
 *     (https://dev59.com/jW865IYBdhLWcg3wQMMD)
 * Re-worked for Gtk 3 by Louis Melahn, L.C., January 30, 2014.
 */

#include <gtk/gtk.h>

static void screen_changed(GtkWidget *widget, GdkScreen *old_screen, gpointer user_data);
static gboolean draw(GtkWidget *widget, cairo_t *new_cr, 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), "draw", G_CALLBACK(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)
{
    /* To check if the display supports alpha channels, get the visual */
    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 draw(GtkWidget *widget, cairo_t *cr, gpointer userdata)
{
    cairo_save (cr);

    if (supports_alpha)
    {
        cairo_set_source_rgba (cr, 0.5, 1.0, 0.50, 0.5); /* 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_restore (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));
} 

C++版本

我包含了一个非常相似的程序,这次是用gtkmm写的。可以使用以下命令进行编译:

g++ -otransparent main.cpp transparent.cpp `pkg-config gtkmm-3.0 --cflags --libs` -std=c++11

请注意,我使用了一些新C++-11标准中的功能,因此您需要一个支持它们的编译器。(如果没有,当自动关键字出现时,您只需将其替换为适当的类型,这可以从函数定义中找到。)有三个文件:main.cpptransparent.htransparent.cpp

main.cpp


/**
     * main.cpp
     *
     * 代码改编自 Mike 的 'alphademo.c'
     * (http://plan99.net/~mike/blog--现在是死链接--无法找到它。)
     * 并由 karlphillip 修改后用于 StackExchange:
     *     (https://dev59.com/jW865IYBdhLWcg3wQMMD)
     * 由 Louis Melahn, L.C. 在 2014 年 1 月 31 日重新制作为 Gtkmm 3.0。
     */
#include "transparent.h"
#include <gtkmm/application.h>
    
int main(int argc, char *argv[])
{
    Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example.transparent");
    
    Transparent transparent;
    
    // 显示窗口并在关闭时返回。
    return app->run(transparent);
}

transparent.h

TBD
/**
 * transparent.h
 *
 * Code adapted from 'alphademo.c' by Mike
 * (http://plan99.net/~mike/blog--now a dead link--unable to find it.)
 * as modified by karlphillip for StackExchange:
 *     (https://dev59.com/jW865IYBdhLWcg3wQMMD)
 * Re-worked for Gtkmm 3.0 by Louis Melahn, L.C. January 31, 2014.
 */

#ifndef TRANSPARENT_H_
#define TRANSPARENT_H_

#include <iostream>
#include <gtk/gtk.h>
#include <gtkmm/window.h>
#include <gtkmm/button.h>
#include <gtkmm/alignment.h>

class Transparent : public Gtk::Window
{

private:
    std::string _buttonLabel;

public:
    Transparent();
    void set_visual(Glib::RefPtr<Gdk::Visual> visual);
    virtual ~Transparent();

protected:
    // Signal handlers:
    // Note that on_draw is actually overriding a virtual function
    // from the Gtk::Window class. I set it as virtual here in case
    // someone wants to override it again in a derived class.
    void on_button_clicked();
    virtual bool on_draw(const ::Cairo::RefPtr< ::Cairo::Context>& cr);
    void on_screen_changed(const Glib::RefPtr<Gdk::Screen>& previous_screen);
    bool on_window_clicked(GdkEventButton* event);

    // Member widgets:
    Gtk::Alignment _alignment;
    Gtk::Button _button;

    bool _SUPPORTS_ALPHA = false;

};

#endif /* TRANSPARENT_H_ */

transparent.cpp

/**
 * transparent.cpp
 *
 * Code adapted from 'alphademo.c' by Mike
 * (http://plan99.net/~mike/blog--now a dead link--unable to find it.)
 * as modified by karlphillip for StackExchange:
 *     (https://dev59.com/jW865IYBdhLWcg3wQMMD)
 * Re-worked for Gtkmm 3.0 by Louis Melahn, L.C. January 31, 2014.
 */
#include "transparent.h"

Transparent::Transparent() :
    _buttonLabel("Button1"),
    _alignment(Gtk::ALIGN_START, Gtk::ALIGN_START, 0.0, 0.0),    // Aligns the button.
    _button(_buttonLabel)                                        // Creates a new button with label '_buttonLabel'.
{

    // Set up the top-level window.
    set_title("Transparency test");
    set_default_size(400,400);
    set_decorated(false);
    add_events(Gdk::BUTTON_PRESS_MASK);
    set_position(Gtk::WIN_POS_CENTER);
    set_app_paintable(true);

    // Signal handlers
    signal_draw().connect(sigc::mem_fun(*this, &Transparent::on_draw));
    signal_screen_changed().connect(sigc::mem_fun(*this, &Transparent::on_screen_changed));
    signal_button_press_event().connect(sigc::mem_fun(*this, &Transparent::on_window_clicked));
    _button.signal_clicked().connect(sigc::mem_fun(*this, &Transparent::on_button_clicked));

    // Widgets

    on_screen_changed(get_screen());

    // This will add the aligner.
    add(_alignment);

    // Now pack the button into the aligner.
    _alignment.add(_button);

    // Set up the button
    _button.set_size_request(100, 100);

    // Show the window and all its children.
    show_all();
}

Transparent::~Transparent()
{
}

void Transparent::on_button_clicked()
{
    std::cout << "The button '" << _buttonLabel << "' was pressed." << std::endl;
}

bool Transparent::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
    cr->save();
    if (_SUPPORTS_ALPHA) {
        cr->set_source_rgba(0.5, 1.0, 0.5, 0.5);    // transparent
    } else {
        cr->set_source_rgb(0.5, 1.0, 0.5);          // opaque
    }
    cr->set_operator(Cairo::OPERATOR_SOURCE);
    cr->paint();
    cr->restore();

    return Gtk::Window::on_draw(cr);
}

/**
 * Checks to see if the display supports alpha channels
 */
void Transparent::on_screen_changed(const Glib::RefPtr<Gdk::Screen>& previous_screen) {
    auto screen = get_screen();
    auto visual = screen->get_rgba_visual();

    if (!visual) {
        std::cout << "Your screen does not support alpha channels!" << std::endl;
    } else {
        std::cout << "Your screen supports alpha channels!" << std::endl;
        _SUPPORTS_ALPHA = TRUE;
    }

    set_visual(visual);
}

/**
 * This simply adds a method which seems to be missing in Gtk::Widget,
 * so I had to use Gtk+ manually.
 *
 * Sets the visual for 'this' (the current widget).
 */
void Transparent::set_visual(Glib::RefPtr<Gdk::Visual> visual) {
    gtk_widget_set_visual(GTK_WIDGET(gobj()), visual->gobj());
}

/**
 * If I click somewhere other than the button, this toggles
 * between having window decorations and not having them.
 */
bool Transparent::on_window_clicked(GdkEventButton* event) {
    set_decorated(!get_decorated());
    return false;
}
希望这可以帮到你!

谢谢。我认为不需要创建新的cairo表面。 您可以使用默认表面(使用_save和_restore)。 - Treviño
以下是有关编程的内容的英语到中文的翻译。仅返回已翻译的文本:原始链接可在wayback机器上找到!https://web.archive.org/web/20121027002505/http://plan99.net/~mike/files/alphademo.c - Jamie Pate

9

在处理同样的问题时,我注意到如果在调用 show_all() 函数后再调用 gtk_window_set_opacity() 函数,将整个(部分)窗口透明化的效果对我是有效的。请尝试此方法:

gtk_widget_show_all ( window );
gtk_widget_set_opacity (GTK_WIDGET (window), 0.5);

这对您也适用吗?


在Python中,使用self.window.show_all()之后再使用self.window.set_opacity(0.5)也可以实现相同的效果。 - Fawix
很好的答案,我在C++中只需使用gtk_widget_set_opacity(widget, 0.5)就可以了。但请注意,如果您从GLADE设置不透明度级别,它将无法正常工作。在我的情况下,我尝试了GLADE的不透明度选项,但没有起作用。因此,我在GLADE中将不透明度级别设置为1,并从C++中更改它。 - Pekov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接