You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

draw信号触发时如何保留Gtk::DrawingArea的Cairo绘图内容

解决GTK DrawingArea绘制内容丢失的问题

这问题我太熟了!本质是GTK的DrawingArea每次触发draw信号时,都会给你一个全新的Cairo上下文,默认不会保留之前的绘制内容——所以你每次queue_draw()后,之前画的东西就没了,还会露出主题的灰色背景。

解决的核心思路是自己维护一个离线的Cairo Surface,把所有绘制操作都先画到这个离线surface上,每次draw信号触发时,只需要把这个离线surface完整复制到窗口的上下文里就行。下面是具体的实现步骤:

1. 添加离线Surface成员变量

在你的Gtk::DrawingArea子类里,加一个成员变量用来保存离线绘制内容:

class MyDrawingArea : public Gtk::DrawingArea
{
private:
    Cairo::RefPtr<Cairo::Surface> m_offscreen_surface;
    // 其他成员和方法...
};

2. 初始化离线Surface

我们需要在窗口大小确定/变化时,创建和窗口尺寸匹配的离线Surface,同时初始化白色背景。可以通过on_size_allocate信号来处理:

void MyDrawingArea::on_size_allocate(Gtk::Allocation& allocation)
{
    // 先调用父类的默认处理逻辑
    Gtk::DrawingArea::on_size_allocate(allocation);

    auto window = get_window();
    if (window)
    {
        // 创建和窗口同类型、同尺寸的离线Surface
        m_offscreen_surface = window->create_similar_surface(
            Cairo::CONTENT_COLOR_ALPHA,
            allocation.get_width(),
            allocation.get_height()
        );

        // 初始化Surface为白色背景,避免一开始就显示主题灰色
        auto cr = Cairo::Context::create(m_offscreen_surface);
        cr->set_source_rgb(1.0, 1.0, 1.0); // 白色
        cr->paint();
    }
}

3. 在鼠标事件中绘制到离线Surface

比如处理鼠标点击事件,在离线Surface上画方块,之后触发重绘:

bool MyDrawingArea::on_button_press_event(GdkEventButton* event)
{
    // 只处理左键点击,且离线Surface已初始化
    if (event->button == GDK_BUTTON_PRIMARY && m_offscreen_surface)
    {
        auto cr = Cairo::Context::create(m_offscreen_surface);
        // 在鼠标位置画一个10x10的黑色方块(中心对齐鼠标)
        cr->set_source_rgb(0.0, 0.0, 0.0);
        cr->rectangle(event->x - 5, event->y - 5, 10, 10);
        cr->fill();

        queue_draw(); // 触发draw信号,把离线Surface画到窗口
        return true; // 标记事件已处理
    }
    // 其他情况交给父类处理
    return Gtk::DrawingArea::on_button_press_event(event);
}

4. 处理draw信号,复制离线Surface到窗口

最后在on_draw方法里,只需要把离线Surface完整绘制到当前上下文即可:

bool MyDrawingArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
    if (m_offscreen_surface)
    {
        // 将离线Surface设置为绘制源,然后填充整个上下文
        cr->set_source(m_offscreen_surface, 0, 0);
        cr->paint();
    }
    return true;
}

关键说明

  • 为什么之前的方法不行?因为你直接在draw的上下文里绘制,每次draw都会重置上下文,之前的内容自然就没了;而离线Surface是持久化的,所有绘制操作都存在这里,每次重绘只是把它“复制”到窗口上。
  • 窗口大小变化时必须重新创建离线Surface,不然会出现内容拉伸或空白的问题,on_size_allocate是处理这个场景的最佳时机。

内容的提问来源于stack exchange,提问作者SuperPrower

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:37:43