Tui-rs:绘制多个小部件时闪烁
Tui-rs: flickering when drawing multiple widgets
晚上好!
我正在尝试编写一个非常简单的终端应用程序,它使用 Rust 和 tui-rs 在屏幕上绘制两个文本框,一个接受输入,另一个显示输出。第一部分完美运行,但是当我尝试同时绘制两个块时出现了问题:出于某种原因,它只显示第二个块(按绘制顺序),如果我移动鼠标,它会在两个块之间闪烁以一种奇怪的方式。我最好的猜测是,这是由于我的绘图实现,当它需要绘制某些东西时,它会以某种方式“清除”屏幕,但如果是这样的话,我找不到任何文档,而且我不知道如何着手解决这个问题。我提供了一些代码,应该足以在较小规模上重现该问题。
#![allow(unused_imports)]
#![allow(unused_variables)]
use crossterm::{
event::{self, DisableMouseCapture, EnableMouseCapture, Event},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use std::io
use tui::{
backend::CrosstermBackend,
layout::Rect,
widgets::{Block, Borders},
Terminal,
};
struct FirstStruct {}
impl FirstStruct {
pub fn draw(&self, term: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::Result<()> {
term.draw(|f| {
let size = f.size();
let (w, h) = (size.width / 2, size.height);
let (x, y) = (size.x, size.y);
let rect = Rect::new(x, y, w, h);
let block = Block::default()
.title("One")
.borders(Borders::ALL);
f.render_widget(block, rect)
})?;
Ok(())
}
}
struct SecondStruct { }
impl SecondStruct {
pub fn draw(&self, term: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::Result<()> {
term.draw(|f| {
let size = f.size();
let (w, h) = (size.width / 2, size.height);
let (x, y) = (size.x + w, size.y);
let rect = Rect::new(x, y, w, h);
let block = Block::default()
.title("Two")
.borders(Borders::ALL);
f.render_widget(block, rect)
})?;
Ok(())
}
}
fn main() -> io::Result<()>{
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let first = FirstStruct {};
let second = SecondStruct {};
let mut running = true;
while running {
if let Event::Key(key) = event::read()? {
running = false;
}
second.draw(&mut terminal)?;
first.draw(&mut terminal)?;
}
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
Ok(())
}
有人知道我该如何解决这个问题吗?提前致谢!!
每次调用 Terminal::draw()
时,您必须立即绘制所有 想要显示的内容。不要将 Terminal
传递给您自己的 draw
函数,而是传递您从 Terminal::draw()
获得的 Frame
。即替换
second.draw(&mut terminal)?;
first.draw(&mut terminal)?;
和
terminal.draw(|f| {
first.draw(f)?;
second.draw(f)?;
});
并更改 FirstStruct
和 SecondStruct
的签名以匹配。
此外,更常见的做法是,不是在各个函数中计算每个小部件的矩形,而是在顶层决定(可能使用 Layout
)并传递 Rect
s直到绘图功能。这样,他们可以在不同的情况下以不同的方式定位。您拥有的东西会起作用,但要改变它并不那么容易。
文档示例中的布局代码,已根据您的情况进行调整:
terminal.draw(|f| {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Percentage(50),
Constraint::Percentage(50),
].as_ref()
)
.split(f.size());
first.draw(f, chunks[0])?;
second.draw(f, chunks[1])?;
});
晚上好!
我正在尝试编写一个非常简单的终端应用程序,它使用 Rust 和 tui-rs 在屏幕上绘制两个文本框,一个接受输入,另一个显示输出。第一部分完美运行,但是当我尝试同时绘制两个块时出现了问题:出于某种原因,它只显示第二个块(按绘制顺序),如果我移动鼠标,它会在两个块之间闪烁以一种奇怪的方式。我最好的猜测是,这是由于我的绘图实现,当它需要绘制某些东西时,它会以某种方式“清除”屏幕,但如果是这样的话,我找不到任何文档,而且我不知道如何着手解决这个问题。我提供了一些代码,应该足以在较小规模上重现该问题。
#![allow(unused_imports)]
#![allow(unused_variables)]
use crossterm::{
event::{self, DisableMouseCapture, EnableMouseCapture, Event},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use std::io
use tui::{
backend::CrosstermBackend,
layout::Rect,
widgets::{Block, Borders},
Terminal,
};
struct FirstStruct {}
impl FirstStruct {
pub fn draw(&self, term: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::Result<()> {
term.draw(|f| {
let size = f.size();
let (w, h) = (size.width / 2, size.height);
let (x, y) = (size.x, size.y);
let rect = Rect::new(x, y, w, h);
let block = Block::default()
.title("One")
.borders(Borders::ALL);
f.render_widget(block, rect)
})?;
Ok(())
}
}
struct SecondStruct { }
impl SecondStruct {
pub fn draw(&self, term: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::Result<()> {
term.draw(|f| {
let size = f.size();
let (w, h) = (size.width / 2, size.height);
let (x, y) = (size.x + w, size.y);
let rect = Rect::new(x, y, w, h);
let block = Block::default()
.title("Two")
.borders(Borders::ALL);
f.render_widget(block, rect)
})?;
Ok(())
}
}
fn main() -> io::Result<()>{
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let first = FirstStruct {};
let second = SecondStruct {};
let mut running = true;
while running {
if let Event::Key(key) = event::read()? {
running = false;
}
second.draw(&mut terminal)?;
first.draw(&mut terminal)?;
}
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
Ok(())
}
有人知道我该如何解决这个问题吗?提前致谢!!
每次调用 Terminal::draw()
时,您必须立即绘制所有 想要显示的内容。不要将 Terminal
传递给您自己的 draw
函数,而是传递您从 Terminal::draw()
获得的 Frame
。即替换
second.draw(&mut terminal)?;
first.draw(&mut terminal)?;
和
terminal.draw(|f| {
first.draw(f)?;
second.draw(f)?;
});
并更改 FirstStruct
和 SecondStruct
的签名以匹配。
此外,更常见的做法是,不是在各个函数中计算每个小部件的矩形,而是在顶层决定(可能使用 Layout
)并传递 Rect
s直到绘图功能。这样,他们可以在不同的情况下以不同的方式定位。您拥有的东西会起作用,但要改变它并不那么容易。
文档示例中的布局代码,已根据您的情况进行调整:
terminal.draw(|f| {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Percentage(50),
Constraint::Percentage(50),
].as_ref()
)
.split(f.size());
first.draw(f, chunks[0])?;
second.draw(f, chunks[1])?;
});