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

GTK绘图区域坐标因其他部件显隐偏移,居中线条绘制异常求助

GTK绘图区域坐标偏移问题

我把GTK绘图区域和其他部件一起打包布局,当这些部件显示时,绘图区域的坐标会偏移,导致居中线条没法居中绘制。

截图显示:菜单栏显示时,绘图区域的居中线条向下偏移,不在绘图区域中心;隐藏菜单栏后,线条回到正确位置。

绘图相关代码

//Let's translate to origin of the drawing area allocation to avoid
//the cross lines to be rotated too
cairo_save(cr);
cairo_identity_matrix(cr);
cairo_translate(cr, allocation.x, allocation.y);

//This to compensate the allocation menubar height gap
//which prevents the horizontal line to be drawn at the center
//I wonder why the coordinates in the drawing area are shifted
//when the blessed menubar is hidden by the TAB key, shouldn't
//they always be 0,0 at the top left?

int menubar_height = gtk_widget_get_allocated_height(img->menubar);
if (img->textbox->draw_horizontal_line)
{
    if (gtk_widget_is_visible(img->menubar))
        center_y = menubar_height + center_y;
    else
        center_y = allocation.height / 2;

//Draw the horizontal centering line
    cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
    cairo_set_line_width(cr, 1.0);
    cairo_move_to(cr, 0, center_y - 2);
    cairo_line_to(cr, allocation.width, center_y - 2);
    cairo_stroke(cr);
    cairo_set_source_rgb(cr, 0.8, 0.7, 0.3);
    cairo_move_to(cr, 0, center_y);
    cairo_line_to(cr, allocation.width, center_y);
    cairo_stroke(cr);
    cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
    cairo_move_to(cr, 0, center_y + 2);
    cairo_line_to(cr, allocation.width, center_y + 2);
    cairo_stroke(cr);
}

// Draw the vertical centering line
if (img->textbox->draw_vertical_line)
{
    cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
    cairo_set_line_width(cr, 1.0);
    cairo_move_to(cr, center_x - 2, 0);
    cairo_line_to(cr, center_x - 2, allocation.height + menubar_height);
    cairo_stroke(cr);
    cairo_set_source_rgb(cr, 0.8, 0.7, 0.3);
    cairo_move_to(cr, center_x, 0);
    cairo_line_to(cr, center_x, allocation.height + menubar_height);
    cairo_stroke(cr);
    cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
    cairo_move_to(cr, center_x + 2, 0);
    cairo_line_to(cr, center_x + 2, allocation.height + menubar_height);
    cairo_stroke(cr);
}
cairo_restore(cr);

布局打包代码

/* Create the image area */
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
g_object_set(G_OBJECT(vbox), "valign", GTK_ALIGN_CENTER);
gtk_box_pack_start (GTK_BOX (right_horizontal_box), vbox, TRUE, TRUE, 0);

img_struct->image_area = gtk_drawing_area_new();
gtk_box_pack_start(GTK_BOX(vbox), img_struct->image_area, FALSE, FALSE, 0);
gtk_widget_set_hexpand(img_struct->image_area, FALSE);
gtk_widget_set_vexpand(img_struct->image_area, FALSE);

gtk_widget_set_halign(img_struct->image_area, GTK_ALIGN_CENTER);
gtk_widget_set_valign(img_struct->image_area, GTK_ALIGN_CENTER);
gtk_widget_set_events(img_struct->image_area, 
                                  GDK_KEY_PRESS_MASK
                                | GDK_BUTTON_PRESS_MASK
                                | GDK_BUTTON_RELEASE_MASK
                                | GDK_POINTER_MOTION_MASK);

gtk_widget_set_can_focus(img_struct->image_area, TRUE);
gtk_widget_grab_focus(img_struct->image_area);

疑问

为什么和绘图区域一同打包的其他部件显示时,绘图区域的(0,0)坐标会发生偏移?


原因分析

问题核心是混用了全局坐标与绘图区域局部坐标,再加上布局对齐设置放大了这个错误:

  1. Cairo坐标矩阵的错误操作
    GTK传给绘图回调的Cairo上下文,默认已经把原点设在了绘图区域的左上角(局部坐标(0,0))。但你调用cairo_identity_matrix(cr)重置矩阵后,又用cairo_translate(cr, allocation.x, allocation.y)把原点移到了绘图区域在父容器里的全局位置——这相当于把绘图坐标从「局部」切换成了「全局窗口坐标」,自然会受外部布局变化影响。

  2. 布局对齐的间接影响
    你给绘图区域和父容器都设置了GTK_ALIGN_CENTER对齐,当菜单栏显示/隐藏时,父容器会重新计算布局,绘图区域在父容器里的全局位置(allocation.x/allocation.y)会改变。而你用这个全局偏移来平移Cairo上下文,就导致内部绘图跟着全局位置跑,线条自然偏移。

  3. 手动补偿逻辑冗余且错误
    绘图区域的allocation.height是它自身的高度,和菜单栏完全无关。你用菜单栏高度修正center_y的操作,本质是在局部坐标里叠加了全局偏移,进一步打乱了绘图位置。

修复方案

去掉多余的坐标变换和手动补偿,直接用绘图区域的局部坐标绘制:

cairo_save(cr);
// 移除cairo_identity_matrix和cairo_translate,使用默认的局部坐标

if (img->textbox->draw_horizontal_line)
{
    // 直接基于绘图区域自身高度计算中心Y
    int center_y = allocation.height / 2;

    // 绘制水平居中线条
    cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
    cairo_set_line_width(cr, 1.0);
    cairo_move_to(cr, 0, center_y - 2);
    cairo_line_to(cr, allocation.width, center_y - 2);
    cairo_stroke(cr);
    cairo_set_source_rgb(cr, 0.8, 0.7, 0.3);
    cairo_move_to(cr, 0, center_y);
    cairo_line_to(cr, allocation.width, center_y);
    cairo_stroke(cr);
    cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
    cairo_move_to(cr, 0, center_y + 2);
    cairo_line_to(cr, allocation.width, center_y + 2);
    cairo_stroke(cr);
}

// 绘制垂直居中线条
if (img->textbox->draw_vertical_line)
{
    // 直接基于绘图区域自身宽度计算中心X
    int center_x = allocation.width / 2;

    cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
    cairo_set_line_width(cr, 1.0);
    cairo_move_to(cr, center_x - 2, 0);
    cairo_line_to(cr, center_x - 2, allocation.height);
    cairo_stroke(cr);
    cairo_set_source_rgb(cr, 0.8, 0.7, 0.3);
    cairo_move_to(cr, center_x, 0);
    cairo_line_to(cr, center_x, allocation.height);
    cairo_stroke(cr);
    cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
    cairo_move_to(cr, center_x + 2, 0);
    cairo_line_to(cr, center_x + 2, allocation.height);
    cairo_stroke(cr);
}
cairo_restore(cr);

修改后,绘图区域内部的(0,0)始终是自身左上角,居中线条基于自身尺寸计算,不会再受外部布局变化影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 15:22:36