GTK-rs中鼠标按下修改Pixbuf无效,求解决方法及合适事件方案
问题:左键点击修改Pixbuf不生效,Gesture是否合适?
我尝试在左键点击时修改Pixbuf,但修改似乎并未生效。modify_pixel函数用于获取Pixbuf的指定像素并对其进行修改。代码如下:
fn build_ui(application: &Application) { let pixbuf = Pixbuf::from_file("assets/map.png").expect("msg"); let pixbuf = Rc::new(RefCell::new(pixbuf)); let gesture = GestureClick::new(); let pixbuf_clone = Rc::clone(&pixbuf); gesture.connect_pressed(move |_gesture, _n_press, x, y| { let mut pixbuf = pixbuf_clone.borrow_mut(); let position = [x as usize, y as usize]; if position[0] < pixbuf.width() as usize && position[1] < pixbuf.height() as usize { modify_pixel(&mut pixbuf, position); } else { println!("Out of bounds"); } }); let picture = Picture::for_pixbuf(Some(&pixbuf.borrow()).expect("No se pudo crear el Picture")); let window = ApplicationWindow::builder() .application(application) .default_height(800) .default_width(800) .title("My GTK App") .build(); window.set_child(Some(&picture)); window.add_controller(gesture); window.present(); }
fn modify_pixel(pixbuf: &mut Pixbuf, position: [usize;2]){ let pixels = unsafe {pixbuf.pixels()}; let rowstride = pixbuf.rowstride() as usize; let n_channels = pixbuf.n_channels() as usize; let offset = position[1] * rowstride + position[0] * n_channels; let mut pixels = pixels.to_vec(); pixels[offset] = 255; pixels[offset + 1] = 0; pixels[offset + 2] = 0; *pixbuf = Pixbuf::from_mut_slice( pixels, Colorspace::Rgb, pixbuf.has_alpha(), 8, pixbuf.width(), pixbuf.height(), rowstride as i32, ); }
另外,我不确定使用Gesture是否是最合适的选择。我也曾尝试在事件处理闭包内修改Picture,但遇到了同样的问题。
解决方案
1. 核心问题:Picture未同步Pixbuf的更新
你创建Picture时用的是初始的Pixbuf实例,之后修改Rc<RefCell<Pixbuf>>内部的Pixbuf,Picture不会自动感知到这个变化,必须手动调用picture.set_pixbuf()来更新显示。
2. 优化modify_pixel函数
原函数中pixels.to_vec()会复制整个像素缓冲区,效率很低,而且重新创建Pixbuf的操作没必要。可以直接修改原Pixbuf的像素数据(注意unsafe块的使用要确保安全):
fn modify_pixel(pixbuf: &Pixbuf, position: [usize;2]){ let rowstride = pixbuf.rowstride() as usize; let n_channels = pixbuf.n_channels() as usize; let offset = position[1] * rowstride + position[0] * n_channels; // 直接修改原像素缓冲区,无需复制 unsafe { let pixels = pixbuf.pixels(); pixels[offset] = 255; pixels[offset + 1] = 0; pixels[offset + 2] = 0; // 如果有Alpha通道,可按需设置:pixels[offset +3] = 255; } }
3. 修改build_ui函数,确保Picture更新
需要把picture也用Rc<RefCell>包裹,在回调中持有它的引用,修改完Pixbuf后手动更新:
fn build_ui(application: &Application) { let pixbuf = Pixbuf::from_file("assets/map.png").expect("msg"); let pixbuf = Rc::new(RefCell::new(pixbuf)); let gesture = GestureClick::new(); // 同时克隆pixbuf和包裹picture的Rc let pixbuf_clone = Rc::clone(&pixbuf); let picture = Picture::for_pixbuf(Some(&pixbuf.borrow()).expect("No se pudo crear el Picture")); let picture_clone = Rc::new(RefCell::new(picture)); gesture.connect_pressed(move |_gesture, _n_press, x, y| { let mut pixbuf = pixbuf_clone.borrow_mut(); let position = [x as usize, y as usize]; if position[0] < pixbuf.width() as usize && position[1] < pixbuf.height() as usize { modify_pixel(&pixbuf, position); // 修改后更新Picture的Pixbuf picture_clone.borrow_mut().set_pixbuf(Some(&pixbuf)); } else { println!("Out of bounds"); } }); let window = ApplicationWindow::builder() .application(application) .default_height(800) .default_width(800) .title("My GTK App") .build(); window.set_child(Some(&picture_clone.borrow())); window.add_controller(gesture); window.present(); }
关于Gesture的选择
GestureClick完全适合你的场景:它是GTK专门为点击类交互设计的高级手势控制器,相比直接监听button-press-event,它能自动处理双击、多点击等逻辑,还能过滤掉误触,是更推荐的做法。
内容的提问来源于stack exchange,提问作者zerowanderer
相关产品推荐
相关产品推荐

