如何在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
相关产品推荐
相关产品推荐

