rust - 如何在 Rust 中为终端设置动画
问题描述
我需要分析几个变量,例如在我的应用程序中呈现的每秒帧数。因此,我需要一种简单的方法来更新终端中的变量。
我搜索并找到ascii_table
了生成表和termion
更新终端。但我怀疑termion
这里只是用来清除终端。
无论如何,我能够绘制一个简单的表格并每 200 毫秒更新一次其内容:
use ascii_table::{Align, AsciiTable, Column};
extern crate termion;
use termion::{clear, color, cursor};
use std::fmt::Display;
use std::{thread, time};
fn main() {
let mut ascii_table = AsciiTable::default();
ascii_table.max_width = 40;
let mut column = Column::default();
column.header = "H1".into();
column.align = Align::Left;
ascii_table.columns.insert(0, column);
let mut column = Column::default();
column.header = "H2".into();
column.align = Align::Center;
ascii_table.columns.insert(1, column);
let mut column = Column::default();
column.header = "H3".into();
column.align = Align::Right;
ascii_table.columns.insert(2, column);
let mut i = 0;
while (true) {
let data: Vec<Vec<&dyn Display>> = vec![
vec![&i, &"hello", &789],
];
let s = ascii_table.format(data.clone());
println!(
"\n{}{}{}{}",
cursor::Hide,
clear::All,
cursor::Goto(1, 1),
s
);
println!("Hello");//couldn't make this appear on top.
i = i+1;
std::thread::sleep(std::time::Duration::from_millis(200));
}
}
这是程序top
在终端上更新数据的方式吗?或者,还有更好的方法?有更复杂的结构会很好。
解决方案
在终端上格式化复杂数据没有比您现在做的更好的方法了。可以进行一些单独的改进以提高显示质量。
特别是,为了减少闪烁,最好覆盖文本而不是先清除整个终端,只清除需要变为空白或已经为空白的部分,使用更窄的清除操作,例如清除到行尾,当你替换一行时你会使用它,它可能会变短——通过在文本的末尾清楚地放置它,这样如果文本没有改变,它就不会短暂消失。
由于您从生成多行文本的代码开始,因此您需要编辑字符串:
// Insert clear-to-end-of-line into the table
let s = ascii_table.format(data.clone())
.replace("\n", &format!("{}\n", clear::UntilNewline));
println!(
"\n{}{}{}{}",
cursor::Hide,
cursor::Goto(1, 1),
s,
clear::AfterCursor,
);
请注意,我已经对操作进行了重新排序:首先,我们转到 (1, 1) 并绘制文本(在进行时清除到行尾)。然后当一切都完成后,我们从光标清除到屏幕末尾。这样,我们永远不会清除任何我们希望仍然存在的文本,因此不会有闪烁。
我注意到你还有一个愿望:
println!("Hello");//couldn't make this appear on top.
你需要做的就是在 goto 之后和 table 之前做,包括清理,它会按照你的意愿工作。
// Move cursor to top. This always goes first.
// Note print!, not println!, since we don't want to move down
// after the goto.
print!("{}{}", cursor::Hide, cursor::Goto(1, 1));
// Use clear::UntilNewline on intermediate things
println!("Hello{}", clear::UntilNewline);
// ...even if they are blank lines
println!("{}", clear::UntilNewline);
// Use clear::AfterCursor on the *last* thing printed
// Note print!, not println!, since if we are filling the entire terminal
// we don't want to cause it to scroll down.
print!("{}{}", s, clear::AfterCursor);
我没有涉及的一件事是,您还可以使用termion::cursor::Goto
移动到终端上的特定区域来更新它们,而不是从上到下编写整行。这当然更复杂,因为您的程序必须理解整个布局才能知道要转到哪个光标位置,并知道需要重绘哪些部分。在实际串行终端和调制解调器的数据速率非常低的时代,这是一项非常重要的优化,可以避免在相同字符上浪费传输时间——今天,它已经不那么重要了。
推荐阅读
- spring - Kotlin:在 @ActiveProfiles 注释中保留多个配置文件?
- c# - 计数频率使用Dictionary C#时如何清除未输入的项目
- java - 检查 URL 是否有效
- amazon-web-services - 如何将 localhost 更改为 ec2 公共 dns
- java - 一种禁用在动态加载的 JAR 之间共享 Gradle 依赖项的方法
- javascript - 框架增强现实。来自自定义流而不是网络摄像头的场景输入源流
- java - finally 子句中的信号量操作
- python - 在 html 模板中同时显示烧瓶变量
- python-3.x - Gzip 从一个 s3 存储桶解压缩到另一个
- java - 如何通过 sqoop-import 更改临时使用的目录?