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

如何在GTK-rs绘图区域捕获鼠标悬停时的光标位置?

问题:GTK-rs绘图区域无法捕获鼠标移动事件

我正在开发一个包含绘图区域的Rust GTK-rs应用,希望在用户鼠标悬停绘图区域时,将光标位置打印到命令行。尝试使用motion_notify_event事件,通过回调函数调用position()方法获取事件的x、y坐标,再用println!()打印到控制台,但运行应用后悬停绘图区域时无任何输出。

已尝试过button_press_event信号、motion_notify_event以及drawing.grab_focus()来捕获鼠标事件,但这些方法都无效。期望鼠标悬停绘图区域时,程序能将光标位置打印到控制台,但实际无任何输出。

原代码

Rust代码

use cairo::{ImageSurface, Context, Format};
use gtk::prelude::*;
use gtk::{Builder, Window, DrawingArea};

fn main() {
    if let Err(err) = gtk::init() {
        println!("Failed to initialize GTK: {}", err);
        return;
    }

    let glade_src = include_str!("interface.ui");
    let builder = Builder::from_string(glade_src);

    let window: Window = builder.object("main").unwrap();
    let drawing: DrawingArea = builder.object("canvas").unwrap();    
    
    let surface = ImageSurface::create(Format::ARgb32, 600, 700).unwrap();
    drawing.connect_draw(move |_,cr| {
        let surface_ctx = Context::new(&surface).unwrap();
        surface_ctx.set_source_rgb(1.0,1.0,1.0);
        surface_ctx.paint().expect("unable to paint surface_ctx");

        cr.set_source_surface(&surface, 0.0, 0.0).expect("unable to set source surface");
        cr.paint().expect("unable to paint context");
        Inhibit(false)
    });
    
    drawing.connect_motion_notify_event(|_widget, event| {
        let x = event.position().0;
        let y = event.position().1;
        println!("Mouse click position: ({}, {})", x, y);
        Inhibit(false)
    });

    window.show_all();

    gtk::main();
}

interface.ui文件

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
  <requires lib="gtk+" version="3.24"/>
  <object class="GtkWindow" id="main">
    <property name="can-focus">False</property>
    <child>
      <object class="GtkPaned">
        <property name="visible">True</property>
        <property name="can-focus">True</property>
        <property name="position">400</property>
        <child>
          <object class="GtkDrawingArea" id="canvas">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
          </object>
          <packing>
            <property name="resize">False</property>
            <property name="shrink">True</property>
          </packing>
        </child>
        <child>
          <object class="GtkLabel">
            <property name="visible">True</property>
            <property name="can-focus">False</property>
            <property name="label" translatable="yes">A scaled down version
 of the interface.ui</property>
          </object>
          <packing>
            <property name="resize">True</property>
            <property name="shrink">True</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>
解决方案

核心问题

GtkDrawingArea默认不会监听鼠标移动事件,需要手动添加事件掩码告知GTK要捕获这类事件。

修复步骤

  • 添加事件掩码:在获取DrawingArea实例后,调用add_events方法启用POINTER_MOTION_MASK事件监听
  • (可选)补充其他掩码:如果需要捕获鼠标进入/离开事件,可以额外添加ENTER_NOTIFY_MASK或LEAVE_NOTIFY_MASK
  • 修正打印描述:将回调中的Mouse click position改为Mouse position,更符合事件类型

修改后的完整代码

use cairo::{ImageSurface, Context, Format};
use gtk::prelude::*;
use gtk::{Builder, Window, DrawingArea};
use gdk::EventMask; // 引入事件掩码枚举

fn main() {
    if let Err(err) = gtk::init() {
        println!("Failed to initialize GTK: {}", err);
        return;
    }

    let glade_src = include_str!("interface.ui");
    let builder = Builder::from_string(glade_src);

    let window: Window = builder.object("main").unwrap();
    let drawing: DrawingArea = builder.object("canvas").unwrap();    
    
    // 关键:启用鼠标移动事件监听
    drawing.add_events(EventMask::POINTER_MOTION_MASK);
    
    let surface = ImageSurface::create(Format::ARgb32, 600, 700).unwrap();
    drawing.connect_draw(move |_,cr| {
        let surface_ctx = Context::new(&surface).unwrap();
        surface_ctx.set_source_rgb(1.0,1.0,1.0);
        surface_ctx.paint().expect("unable to paint surface_ctx");

        cr.set_source_surface(&surface, 0.0, 0.0).expect("unable to set source surface");
        cr.paint().expect("unable to paint context");
        Inhibit(false)
    });
    
    drawing.connect_motion_notify_event(|_widget, event| {
        let (x, y) = event.position();
        println!("Mouse position: ({}, {})", x, y);
        Inhibit(false)
    });

    window.show_all();

    gtk::main();
}

补充说明

  • 确保Cargo.toml中的依赖版本匹配,示例配置:
    [dependencies]
    gtk = { version = "0.15", features = ["v3_24"] }
    cairo = "0.15"
    
  • grab_focus()对鼠标事件捕获无效,该方法主要用于获取键盘输入焦点,与鼠标事件监听无关

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 17:53:14