【问题标题】:Gtk - draw event fired for wrong widget, and widget not redrawnGtk - 为错误的小部件触发绘制事件,并且小部件未重绘
【发布时间】:2017-06-07 12:45:40
【问题描述】:

我正在尝试创建一个自定义的可滚动文本区域。我在Grid 中创建了一个DrawingArea 和一个ScrollBar。我已将DrawingAreadraw 事件附加到this.on_draw 方法,该方法仅查看ScrollBar 的值并在绘制Pango.Layout 之前适当地移动Cairo.Context

第一个问题是this.on_drawScrollBar 被触摸时被调用,即使我没有向ScrollBar 注册任何事件。我该如何预防或检查?

第二个问题是即使调用了this.on_draw,对Context所做的更改也不会显示,除非ScrollBar值接近0100(100是调整的上限值)。为什么会这样?

我确实发现如果我将connectScrollBarvalue_changed 事件调用到调用DrawingAreaqueue_redraw 的方法,它将调用this.on_draw 并在其后正确显示它。但是由于第二个问题,我认为this.on_draw 被不必要地调用了太多次。那么,实现这一点的“正确”方式是什么?

using Cairo;
using Gdk;
using Gtk;
using Pango;

public class Texter : Gtk.Window {
    private Gtk.DrawingArea darea;
    private Gtk.Scrollbar scroll;

    private string text = "Hello\nWorld!";

    public Texter () {
        GLib.Object (type: Gtk.WindowType.TOPLEVEL);

        Gtk.Grid grid = new Gtk.Grid();
        this.add (grid);

        var drawing_area = new Gtk.DrawingArea ();
        drawing_area.set_size_request (200, 200);
        drawing_area.expand = true;
        drawing_area.draw.connect (this.on_draw);

        grid.attach (drawing_area, 0, 0);

        var scrollbar = new Gtk.Scrollbar (Gtk.Orientation.VERTICAL,
                                           new Gtk.Adjustment(0, 0, 100, 0, 0, 1));

        grid.attach (scrollbar, 1, 0);

        this.darea = drawing_area;
        this.scroll = scrollbar;

        this.destroy.connect (Gtk.main_quit);
    }

    private bool on_draw (Gtk.Widget sender, Cairo.Context ctx) {
        ctx.set_source_rgb (0.9, 0.9, 0.9);
        ctx.paint ();

        var y_offset = this.scroll.get_value();
        stdout.printf("%f\n", y_offset);

        ctx.set_source_rgb (0.25, 0.25, 0.25);
        ctx.move_to(0, 100 - y_offset);

        var layout = Pango.cairo_create_layout(ctx);
        layout.set_font_description(Pango.FontDescription.from_string("Sans 12"));
        layout.set_auto_dir(false);
        layout.set_text(this.text, this.text.length);
        Pango.cairo_show_layout(ctx, layout);

        return false;
    }

    static int main (string[] args) {
        Gtk.init (ref args);

        var window = new Texter ();
        window.show_all ();

        Gtk.main ();

        return 0;
    }
}

另外,如果您在上述代码中发现任何错误(可能不相关),请指出。

【问题讨论】:

  • 这是什么版本的 GTK+?我怀疑第一个问题是由覆盖滚动条引起的,但这取决于您是否使用具有覆盖滚动条的 GTK+。
  • @andlabs 我用--pkg gtk+-3.0选项编译,安装的gtk3版本是3.22。通过“覆盖滚动条”,如果您的意思是悬停在其上时会展开的滚动条,那么它不是,至少在视觉上是这样。它“看起来”像普通的旧 3.0 滚动条。 imgur.com/a/NpCui(但它确实表现得像一个,因为即使将鼠标悬停在它上面也会发出重绘事件。)那么我怎样才能禁用这种行为呢?

标签: gtk gtk3 vala


【解决方案1】:

您缺少的部分是 draw 信号确实意味着“重绘所有内容”。相反,GTK+ 将 cairo 上下文的剪辑区域设置为需要重绘的部分,因此您所做的一切都没有任何效果。 cairo 函数cairo_clip_extents() 将告诉您该区域是什么。 GtkWidget 上的queue_draw_area() 方法将允许您显式标记某个区域进行绘制,而不是整个小部件。

但无论如何,您使用滚动条的方法是错误的:您正试图从头开始构建整个基础架构!考虑改用 GtkScrolledWindow。这会自动为您处理滚动的所有细节,并为您提供我提到的覆盖滚动条。您需要做的就是将 GtkDrawingArea 的大小设置为您想要的大小,其余的由 GtkScrolledWindow 完成。最好的方法是继承 GtkDrawingArea 并覆盖 get_preferred_height() 和/或 get_preferred_width() 虚函数(确保将最小和自然尺寸都设置为您想要的特定尺寸的尺寸)。如果您以后需要更改此大小,请调用 GtkWidget 的queue_resize() 方法。 (您可能可以只使用set_size_request(),但我所描述的是这样做的首选方式。)这样做还可以让您不必担心转换您的开罗坐标; GtkScrolledWindow 会为你做这件事。

【讨论】:

  • 我自己现在就遇到了这个问题,由于担心内存使用,我还打算“从头开始”完成所有工作:如果我保留屏幕上可见大小的绘图区域,它'大概会比覆盖整个滚动区域的绘图区域使用更少的内存?还是 scrolledwindow+drawingarea 足够聪明,可以只分配将要显示的内容?我想滚动一个非常大的“虚拟”显示...
猜你喜欢
  • 1970-01-01
  • 2021-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多