为何GestureMultiPress无法识别我的鼠标点击操作?
问题描述
我正在用gtk-rs(GTK+ 3)开发小型应用,遇到了鼠标点击无法被识别的问题。我有一个DrawingArea,希望每次在该区域内点击鼠标时执行特定代码,选择用GestureMultiPress(因为这是更现代的方式,而非直接处理事件),但配置后完全没有输出。
我的配置代码片段:
let drw_area : DrawingArea = ...; ... window.add(&drw_area); ... window.add_events(EventMask::BUTTON_PRESS_MASK | EventMask::BUTTON_RELEASE_MASK); let press = GestureMultiPress::builder() .button(0) .widget(&drw_area) .build(); let _press_handler_id = { press.connect_pressed(|g, npress, x, y| { println!("({}, {npress}, {x}, {y})", g.current_button()); }) };
完整代码:
use gtk::gdk::EventMask; use gtk::prelude::*; use gtk::{DrawingArea, GestureMultiPress}; const APP_NAME : &'static str = "com.example.appname"; const APP_TITLE : &'static str = "Example App"; const APP_WIDTH : i32 = 600; const APP_HEIGHT : i32 = 600; const APP_BORDER_WIDTH : u32 = 20; fn build_ui(application : >k::Application) { let window = gtk::ApplicationWindow::new(application); window.set_title(APP_TITLE); window.set_border_width(APP_BORDER_WIDTH); window.set_position(gtk::WindowPosition::Center); window.set_default_size(APP_WIDTH, APP_HEIGHT); let drw_area = { DrawingArea::builder() .width_request(APP_WIDTH - 2*APP_BORDER_WIDTH as i32) .height_request(APP_HEIGHT - 2*APP_BORDER_WIDTH as i32) .build() }; drw_area.connect_draw(|a, c| { c.set_source_rgb(0.0, 0.0, 0.0); c.rectangle(0.0, 0.0, a.allocated_width() as f64, a.allocated_height() as f64); let _ = c.fill(); Inhibit(false) }); window.add(&drw_area); window.add_events(EventMask::BUTTON_PRESS_MASK | EventMask::BUTTON_RELEASE_MASK); let press = GestureMultiPress::builder() .button(0) .widget(&drw_area) .build(); let _press_handler_id = { press.connect_pressed(|g, npress, x, y| { println!("({}, {npress}, {x}, {y})", g.current_button()); }) }; window.show_all(); } fn main() { let application = gtk::Application::new(Some(APP_NAME), Default::default()); application.connect_activate(build_ui); application.run(); }
问题原因
- 手势对象生命周期过短:创建的
GestureMultiPress是局部变量,build_ui函数执行完毕后,该变量会被销毁,导致手势无法继续监听事件。 - 多余的事件掩码设置:给
window添加BUTTON_PRESS_MASK和BUTTON_RELEASE_MASK是不必要的,GTK手势机制会自动处理事件的捕获,手动添加反而可能引发冲突。
修复方案
步骤1:持久化手势对象
将手势对象附加到DrawingArea上,利用GTK的对象数据存储机制让其随DrawingArea的生命周期存在,使用set_data方法绑定即可。
步骤2:移除多余的事件掩码设置
删除window.add_events(...)这一行代码。
修复后的完整代码:
use gtk::prelude::*; use gtk::{DrawingArea, GestureMultiPress}; const APP_NAME : &'static str = "com.example.appname"; const APP_TITLE : &'static str = "Example App"; const APP_WIDTH : i32 = 600; const APP_HEIGHT : i32 = 600; const APP_BORDER_WIDTH : u32 = 20; fn build_ui(application : >k::Application) { let window = gtk::ApplicationWindow::new(application); window.set_title(APP_TITLE); window.set_border_width(APP_BORDER_WIDTH); window.set_position(gtk::WindowPosition::Center); window.set_default_size(APP_WIDTH, APP_HEIGHT); let drw_area = { DrawingArea::builder() .width_request(APP_WIDTH - 2*APP_BORDER_WIDTH as i32) .height_request(APP_HEIGHT - 2*APP_BORDER_WIDTH as i32) .build() }; drw_area.connect_draw(|a, c| { c.set_source_rgb(0.0, 0.0, 0.0); c.rectangle(0.0, 0.0, a.allocated_width() as f64, a.allocated_height() as f64); let _ = c.fill(); Inhibit(false) }); window.add(&drw_area); // 创建手势并绑定到DrawingArea let press = GestureMultiPress::builder() .button(0) .widget(&drw_area) .build(); press.connect_pressed(|g, npress, x, y| { println!("({}, {npress}, {x}, {y})", g.current_button()); }); // 将手势对象附加到DrawingArea,保证其生命周期 drw_area.set_data("press_gesture", press); window.show_all(); } fn main() { let application = gtk::Application::new(Some(APP_NAME), Default::default()); application.connect_activate(build_ui); application.run(); }
额外说明
.button(0)表示监听所有鼠标按钮,如果你只想监听特定按钮(比如左键是1,右键是3),可以修改为对应数字。- 默认情况下
DrawingArea可以接收鼠标事件,若有特殊需求,可添加drw_area.set_can_focus(true)和drw_area.set_receives_default(true)确保交互性。
内容的提问来源于stack exchange,提问作者V. Jackson
相关产品推荐
相关产品推荐

