Update Rust version: fix console window, add API test, update README

This commit is contained in:
2026-04-07 17:27:38 +08:00
parent e065c41d6b
commit 3ae0eaa9c1
7 changed files with 612 additions and 212 deletions

View File

@@ -1,13 +1,15 @@
use egui::{Color32, RichText, Stroke, Vec2};
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use egui::{Color32, FontId, Stroke, Vec2};
use std::sync::atomic::{AtomicBool, AtomicI32};
use std::sync::Arc;
use parking_lot::Mutex;
pub struct AppState {
pub running: Arc<AtomicBool>,
pub current_score: Arc<AtomicI32>,
pub status_text: Arc<parking_lot::Mutex<String>>,
pub status_text: Arc<Mutex<String>>,
pub fetch_count: Arc<AtomicI32>,
pub analysis_count: Arc<AtomicI32>,
pub no_content_count: Arc<AtomicI32>,
}
impl AppState {
@@ -15,9 +17,10 @@ impl AppState {
Self {
running: Arc::new(AtomicBool::new(false)),
current_score: Arc::new(AtomicI32::new(50)),
status_text: Arc::new(parking_lot::Mutex::new("就绪".to_string())),
status_text: Arc::new(Mutex::new("就绪".to_string())),
fetch_count: Arc::new(AtomicI32::new(0)),
analysis_count: Arc::new(AtomicI32::new(0)),
no_content_count: Arc::new(AtomicI32::new(0)),
}
}
}
@@ -30,10 +33,13 @@ impl Default for AppState {
pub fn get_score_color(score: i32) -> Color32 {
match score {
0..=30 => Color32::from_rgb(0, 100, 200),
31..=50 => Color32::from_rgb(100, 200, 100),
51..=70 => Color32::from_rgb(255, 200, 0),
71..=100 => Color32::from_rgb(255, 50, 50),
0..=30 => Color32::from_rgb(21, 101, 192), // 深蓝
31..=38 => Color32::from_rgb(25, 118, 210), // 蓝色
39..=44 => Color32::from_rgb(66, 165, 245), // 浅蓝
45..=55 => Color32::from_rgb(102, 187, 106), // 绿色
56..=64 => Color32::from_rgb(255, 167, 38), // 橙色
65..=69 => Color32::from_rgb(251, 140, 0), // 深橙
70..=100 => Color32::from_rgb(229, 57, 53), // 红色
_ => Color32::GRAY,
}
}
@@ -48,54 +54,88 @@ pub fn get_score_label(score: i32, cold: i32, warm: i32) -> &'static str {
}
}
pub fn get_score_description(score: i32) -> &'static str {
match score {
0..=29 => "极度悲观",
30..=38 => "悲观",
39..=44 => "偏悲观",
45..=55 => "中立",
56..=64 => "偏乐观",
65..=69 => "乐观",
70..=100 => "极度乐观",
_ => "无法判断",
}
}
pub fn draw_indicator(ui: &mut egui::Ui, score: i32, cold: i32, warm: i32) {
let color = get_score_color(score);
let label = get_score_label(score, cold, warm);
let size = Vec2::new(120.0, 120.0);
let description = get_score_description(score);
let size = Vec2::new(140.0, 160.0);
let (rect, _response) = ui.allocate_exact_size(size, egui::Sense::hover());
let painter = ui.painter();
let center = rect.center();
painter.circle_filled(rect.center(), 55.0, Color32::from_rgba_unmultiplied(30, 30, 30, 200));
painter.circle_stroke(rect.center(), 55.0, Stroke::new(3.0, color));
// 外圈背景 - 白色主题
painter.circle_filled(center, 65.0, Color32::from_rgb(240, 240, 245));
painter.circle_stroke(center, 65.0, Stroke::new(3.0, Color32::from_rgb(200, 200, 210)));
let inner_radius = 40.0;
let angle = (score as f32 / 100.0) * std::f32::consts::TAU - std::f32::consts::FRAC_PI_2;
let indicator_pos = rect.center() + Vec2::new(
angle.cos() * inner_radius,
angle.sin() * inner_radius,
);
painter.circle_filled(indicator_pos, 8.0, color);
// 内圈颜色
painter.circle_filled(center, 58.0, color);
// 发光效果
for i in 1..=3 {
let alpha = (60 / i) as u8;
let glow_color = Color32::from_rgba_unmultiplied(color.r(), color.g(), color.b(), alpha);
painter.circle_filled(center, 58.0 - (i as f32) * 5.0, glow_color);
}
// 中心白色背景
painter.circle_filled(center, 45.0, Color32::from_rgb(255, 255, 255));
let text = RichText::new(format!("{}", score))
.heading()
.size(36.0)
.color(color);
// 分数文本 - 深色
let score_text = format!("{}", score);
painter.text(
rect.center() - Vec2::new(0.0, 10.0),
center - Vec2::new(0.0, 5.0),
egui::Align2::CENTER_CENTER,
text,
score_text,
FontId::proportional(42.0),
color,
);
let label_text = RichText::new(label).size(16.0).color(Color32::WHITE);
// 标签文本 - 深色
painter.text(
rect.center() + Vec2::new(0.0, 25.0),
center + Vec2::new(0.0, 28.0),
egui::Align2::CENTER_CENTER,
label_text,
label,
FontId::proportional(16.0),
Color32::from_rgb(50, 50, 55),
);
// 描述文本(在指示灯下方)
ui.add_space(10.0);
ui.label(egui::RichText::new(description).size(14.0).color(Color32::from_rgb(100, 100, 110)));
}
pub fn draw_waveform(ui: &mut egui::Ui, data: &[(String, f64)], width: f32, height: f32) {
pub fn draw_waveform(ui: &mut egui::Ui, data: &[(String, f64)], _width: f32, _height: f32) {
let rect = ui.available_rect_before_wrap();
let painter = ui.painter();
painter.rect_filled(rect, 0.0, Color32::from_rgba_unmultiplied(20, 20, 30, 180));
painter.rect_stroke(rect, 0.0, Stroke::new(1.0, Color32::GRAY));
// 白色背景
painter.rect_filled(rect, 8.0, Color32::from_rgb(255, 255, 255));
painter.rect_stroke(rect, 8.0, Stroke::new(1.0, Color32::from_rgb(220, 220, 230)));
if data.is_empty() {
let text = RichText::new("暂无数据").small().color(Color32::GRAY);
painter.text(rect.center(), egui::Align2::CENTER_CENTER, text);
let text = "暂无数据";
painter.text(
rect.center(),
egui::Align2::CENTER_CENTER,
text,
FontId::proportional(14.0),
Color32::from_rgb(150, 150, 160),
);
return;
}
@@ -104,12 +144,22 @@ pub fn draw_waveform(ui: &mut egui::Ui, data: &[(String, f64)], width: f32, heig
let max_val = values.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
let range = if (max_val - min_val).abs() < 0.01 { 1.0 } else { max_val - min_val };
let padding = 10.0;
let padding = 15.0;
let draw_width = rect.width() - padding * 2.0;
let draw_height = rect.height() - padding * 2.0;
let step_x = if data.len() > 1 { draw_width / (data.len() - 1) as f32 } else { 0.0 };
// 绘制网格线
for i in 0..=4 {
let y = rect.max.y - padding - (i as f32) * (draw_height / 4.0);
painter.line_segment(
[egui::pos2(rect.min.x + padding, y), egui::pos2(rect.max.x - padding, y)],
Stroke::new(1.0, Color32::from_rgba_unmultiplied(200, 200, 210, 100)),
);
}
// 绘制数据线
for i in 0..data.len().saturating_sub(1) {
let x1 = rect.min.x + padding + (i as f32) * step_x;
let x2 = rect.min.x + padding + ((i + 1) as f32) * step_x;
@@ -119,7 +169,15 @@ pub fn draw_waveform(ui: &mut egui::Ui, data: &[(String, f64)], width: f32, heig
painter.line_segment(
[egui::pos2(x1, y1), egui::pos2(x2, y2)],
Stroke::new(2.0, Color32::from_rgb(0, 150, 255)),
Stroke::new(2.0, Color32::from_rgb(0, 123, 255)),
);
}
// 绘制数据点
for i in 0..data.len() {
let x = rect.min.x + padding + (i as f32) * step_x;
let y = rect.max.y - padding - ((values[i] - min_val) / range * draw_height as f64) as f32;
painter.circle_filled(egui::pos2(x, y), 3.0, Color32::from_rgb(0, 150, 255));
}
}