如何使用gtk::DrawingArea?

5

我尝试使用GTK、Cairo和Glade在Rust中构建GUI。我想使用gtk::DrawingArea来绘制一个游戏场地,但我不知道该如何使用它。我有以下代码:

extern crate cairo;
extern crate gtk;

use gtk::*;

fn main() {
    gtk::init().unwrap(); //init gtk before using it

    let glade_src = include_str!("Glade_gui.glade"); //build the glade gui
    let builder = gtk::Builder::new_from_string(glade_src);

    //get widgets from the gui
    let draw_area: gtk::DrawingArea = builder.get_object("zeichenbrett").unwrap();
    let window: gtk::Window = builder.get_object("fenster").unwrap();

    let size = (600, 600); //define the size for the image

    let style_context: gtk::StyleContext = draw_area.get_style_context().unwrap(); //get the style context from the drawing area
    let surface: cairo::ImageSurface =
        cairo::ImageSurface::create(cairo::Format::ARgb32, size.0, size.1).unwrap(); //build a new ImageSurface to draw on
    let context: cairo::Context = cairo::Context::new(&surface); //build a new cairo context from that ImageSurface to draw on

    //just a blue area
    context.set_source_rgb(0.0, 0.0, 1.0);
    context.paint();
    context.stroke();

    gtk::functions::render_background(
        &style_context,
        &context,
        0.0,
        0.0,
        size.0 as f64,
        size.1 as f64,
    ); //here I thought that I drew the context                        cairo::Context to the drawingArea but it seems to do nothing.

    window.show_all();
    gtk::main();
}

它可以编译和运行,但窗口上没有显示游戏场地。

我认为我没有正确使用render_background函数,但我不知道怎么做才对。

这里是Glade_gui.glade文件:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkWindow" id="fenster">
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Reise nach Jerusalem</property>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="spacing">5</property>
            <property name="homogeneous">True</property>
            <child>
              <object class="GtkButton" id="links">
                <property name="label" translatable="yes">Left</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="rechts">
                <property name="label" translatable="yes">Right</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="quit">
                <property name="label" translatable="yes">Quit</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">2</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkDrawingArea" id="zeichenbrett">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

1
你需要在DrawingArea的“draw”信号中进行绘图。这个答案应该会给你一个很好的起点。 - Jmb
2个回答

1
作为一条注释,我没有Rust的经验,所以我会主要使用C的例子。
您正在尝试在启动gtk::main之前渲染GUI。实现GTK+ GUI的正确方法是添加小部件,将它们的draw信号(和任何其他必要的信号)连接到您自己的绘制回调函数,然后运行gtk::main
例如,请参考文档中的简单GtkDrawingArea示例,可以在这里找到。在此示例中,使用g_signal_connectdraw信号连接到draw_callback回调函数。这样,当小部件由gtk::main实际创建时,它将在其上绘制您想要的图像。
您的draw_callback函数将获得一个Cairo上下文作为参数。您将在该上下文上进行所有绘图,因此无需创建自己的上下文。文档还通过在draw_callback的参数中使用指针cr来演示了这一点。

所有的绘图需在 draw 回调函数中完成。每当需要更新(包括创建 GtkDrawingArea 组件时)时,它都会被调用,或者您可以通过发出 queue_draw 信号来强制更新。

C docs 可以帮助您,即使使用不同的语言,特别是如果您选择的语言没有全面的文档。

此外,创建 GTK+ 应用程序的推荐现代方式是使用 GtkApplication。您可能需要了解一下。


0

我花了几个小时才找到一个使用gtk-rs和GTK4的解决方案,因为示例非常少。在GTK4中,不再连接绘制信号,而是必须使用gtk_drawing_area_set_draw_func注册绘制函数。对于Rust绑定,我们需要使用DrawingAreaExtManual::set_draw_func来设置绘制函数。在我的情况下,我正在子类化DrawingArea,结果如下:

use std::cell::Cell;
use std::f64::consts::PI;

use glib::Object;
use gtk::gdk::RGBA;
use gtk::glib;
use gtk::glib::{clone, ParamSpec, Properties, Value};
use gtk::prelude::*;
use gtk::subclass::prelude::*;

mod imp {
    use super::*;

    // Object holding the state
    #[derive(Properties)]
    #[properties(wrapper_type = super::CustomDrawingArea)]
    pub struct CustomDrawingArea {
        #[property(name = "color", get, set)]
        pub(super) color: Cell<RGBA>,
    }

    impl Default for CustomDrawingArea {
        fn default() -> Self {
            Self {
                color: Cell::new(RGBA::BLACK),
            }
        }
    }

    // The central trait for subclassing a GObject
    #[glib::object_subclass]
    impl ObjectSubclass for CustomDrawingArea {
        const NAME: &'static str = "CustomDrawingArea";
        type Type = super::CustomDrawingArea;
        type ParentType = gtk::DrawingArea;
    }

    impl ObjectImpl for CustomDrawingArea {
        fn constructed(&self) {
            self.parent_constructed();

            // Set the drawing function. This draws a circle filled with the color and a black outline.
            DrawingAreaExtManual::set_draw_func(self.obj().as_ref(), clone!(@weak self as widget => move |_, cr, w, h| {
                let r = w as f64 / 2.0 - 1.0;

                cr.arc(w as f64 / 2.0, h as f64 / 2.0, r, 0.0, 2.0 * PI);
                GdkCairoContextExt::set_source_rgba(cr, &widget.color.get());
                cr.fill().unwrap();

                cr.set_line_width(1.0);
                cr.arc(w as f64 / 2.0, h as f64 / 2.0, r, 0.0, 2.0 * PI);
                GdkCairoContextExt::set_source_rgba(cr, &RGBA::BLACK);
                cr.stroke().unwrap();
            }));

            // Make sure to redraw when the property is updated. 
            self.obj().connect_color_notify(|widget| widget.queue_draw());
        }

        // This is needed for the #[property] macro.
        fn properties() -> &'static [ParamSpec] {
            Self::derived_properties()
        }

        fn set_property(&self, id: usize, value: &Value, pspec: &ParamSpec) {
            self.derived_set_property(id, value, pspec)
        }

        fn property(&self, id: usize, pspec: &ParamSpec) -> Value {
            self.derived_property(id, pspec)
        }
    }

    impl WidgetImpl for CustomDrawingArea {}
    impl DrawingAreaImpl for CustomDrawingArea {}
}

glib::wrapper! {
    pub struct CustomDrawingArea(ObjectSubclass<imp::CustomDrawingArea>)
        @extends gtk::DrawingArea, gtk::Widget,
        @implements gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget;
}

impl CustomDrawingArea {
    pub fn new() -> Self {
        Object::builder().build()
    }
}

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