Rust数据分析可视化插件库re_viewer_context的使用,实现高效数据查看与上下文管理

re_viewer_context

Rerun系列crate的一部分。

![Latest version] ![Documentation] ![MIT] ![Apache]

Rerun查看器不同模块的基本构建块。最突出的是包含一个上下文对象,查看器使用该对象与每个模块共享全局数据。

安装

在项目目录中运行以下Cargo命令:

cargo add re_viewer_context

或者在Cargo.toml中添加以下行:

re_viewer_context = "0.24.1"

使用示例

以下是一个使用re_viewer_context的基本示例:

use re_viewer_context::{ViewerContext, ViewerContextProvider};

// 定义一个自定义模块
struct MyModule;

impl ViewerContextProvider for MyModule {
    fn on_register(&self, ctx: &mut ViewerContext) {
        // 注册模块时初始化
        println!("MyModule registered!");
    }
    
    fn on_update(&self, ctx: &mut ViewerContext) {
        // 每次更新时执行
        println!("MyModule updating with context: {:?}", ctx);
    }
}

fn main() {
    // 创建查看器上下文
    let mut ctx = ViewerContext::new();
    
    // 注册自定义模块
    let my_module = MyModule;
    ctx.register_module(Box::new(my_module));
    
    // 模拟更新循环
    for _ in 0..5 {
        ctx.update();
    }
}

功能说明

re_viewer_context 提供了以下主要功能:

  1. ViewerContext: 核心上下文对象,用于在查看器模块之间共享数据
  2. ViewerContextProvider: 模块需要实现的trait,用于接收上下文更新
  3. 模块管理: 注册和更新多个模块的能力

高级用法

对于更复杂的用例,您可以实现自定义状态管理:

use re_viewer_context::{ViewerContext, ViewerContextProvider};
use std::sync::{Arc, Mutex};

// 共享状态结构
#[derive(Debug, Default)]
struct AppState {
    counter: usize,
    data: Vec<f32>,
}

// 自定义模块
struct DataProcessor {
    state: Arc<Mutex<AppState>>,
}

impl ViewerContextProvider for DataProcessor {
    fn on_update(&self, ctx: &mut ViewerContext) {
        let mut state = self.state.lock().unwrap();
        state.counter += 1;
        
        // 处理一些数据
        if state.counter % 10 == 0 {
            state.data.push(state.counter as f32);
        }
        
        println!("Processed data: {:?}", state.data);
    }
}

fn main() {
    let shared_state = Arc::new(Mutex::new(AppState::default()));
    let mut ctx = ViewerContext::new();
    
    // 注册数据处理模块
    let processor = DataProcessor {
        state: shared_state.clone(),
    };
    ctx.register_module(Box::new(processor));
    
    // 模拟长时间运行的应用
    for i in 0..100 {
        ctx.update();
    }
}

完整示例

以下是一个完整的示例,展示了如何使用re_viewer_context创建一个简单的模块化应用:

use re_viewer_context::{ViewerContext, ViewerContextProvider};
use std::sync::{Arc, Mutex};

// 应用状态
#[derive(Debug)]
struct AppState {
    frame_count: u32,
    messages: Vec<String>,
}

// 日志模块
struct LoggerModule {
    state: Arc<Mutex<AppState>>,
}

impl ViewerContextProvider for LoggerModule {
    fn on_register(&self, ctx: &mut ViewerContext) {
        println!("LoggerModule registered with viewer context");
    }
    
    fn on_update(&self, ctx: &mut ViewerContext) {
        let mut state = self.state.lock().unwrap();
        state.frame_count += 1;
        
        if state.frame_count % 5 == 0 {
            let msg = format!("Frame {} processed", state.frame_count);
            state.messages.push(msg.clone());
            println!("{}", msg);
        }
    }
}

// 统计模块
struct StatsModule {
    state: Arc<Mutex<AppState>>,
}

impl ViewerContextProvider for StatsModule {
    fn on_update(&self, ctx: &mut ViewerContext) {
        let state = self.state.lock().unwrap();
        println!("Current stats - Frames: {}, Messages: {}", 
            state.frame_count, 
            state.messages.len());
    }
}

fn main() {
    // 创建共享状态
    let shared_state = Arc::new(Mutex::new(AppState {
        frame_count: 0,
        messages: Vec::new(),
    }));
    
    // 初始化查看器上下文
    let mut viewer_ctx = ViewerContext::new();
    
    // 注册模块
    viewer_ctx.register_module(Box::new(LoggerModule {
        state: shared_state.clone(),
    }));
    
    viewer_ctx.register_module(Box::new(StatsModule {
        state: shared_state.clone(),
    }));
    
    // 主循环
    for _ in 0..20 {
        viewer_ctx.update();
    }
    
    // 打印最终状态
    let state = shared_state.lock().unwrap();
    println!("Final state: {:?}", state);
}

1 回复

Rust数据分析可视化插件库re_viewer_context使用指南

re_viewer_context是一个用于Rust数据分析可视化的上下文管理库,它提供了高效的数据查看和管理功能,特别适合需要处理大量数据并实现交互式可视化的应用场景。

主要特性

  • 高效的数据存储和检索机制
  • 灵活的上下文管理
  • 可扩展的插件系统
  • 内置常见数据可视化组件
  • 支持实时数据更新

安装方法

在Cargo.toml中添加依赖:

[dependencies]
re_viewer_context = "0.1.0"  # 请使用最新版本

基本使用方法

1. 创建数据上下文

use re_viewer_context::ViewerContext;

fn main() {
    // 创建基础上下文
    let mut ctx = ViewerContext::default();
    
    // 添加数据到上下文
    ctx.add_data("temperature_data", vec![22.1, 23.4, 24.5, 25.2]);
    ctx.add_data("humidity_data", vec![45.0, 47.2, 46.8, 48.1]);
}

2. 使用内置可视化插件

use re_viewer_context::{ViewerContext, PlotPlugin};

fn main() {
    let mut ctx = ViewerContext::default();
    ctx.add_data("sales", vec![120, 150, 180, 210, 240]);
    
    // 创建并注册绘图插件
    let plot_plugin = PlotPlugin::new("sales_plot")
        .with_title("Monthly Sales")
        .with_x_label("Month")
        .with_y_label("Amount");
    
    ctx.register_plugin(plot_plugin);
    
    // 渲染可视化
    ctx.render();
}

3. 自定义插件开发

use re_viewer_context::{ViewerContext, Plugin, PluginResponse};
use egui::{Ui, Color32};

struct CustomPlugin {
    name: String,
    data_key: String,
}

impl Plugin for CustomPlugin {
    fn name(&self) -> &str {
        &self.name
    }
    
    fn show(&mut self, ui: &mut Ui, ctx: &mut ViewerContext) -> PluginResponse {
        if let Some(data) = ctx.get_data::<Vec<f32>>(&self.data_key) {
            ui.heading(&self.data_key);
            
            let avg = data.iter().sum::<f32>() / data.len() as f32;
            ui.label(format!("Average: {:.2}", avg));
            
            // 简单的条形图
            for (i, &value) in data.iter().enumerate() {
                let bar_height = value * 2.0;
                ui.painter().rect_filled(
                    egui::Rect::from_min_size(
                        egui::pos2(50.0 + (i as f32 * 30.0), 100.0 - bar_height),
                        egui::vec2(20.0, bar_height),
                    egui::Rounding::none(),
                    Color32::from_rgb(100, 200, 100)
                );
            }
            
            PluginResponse::Keep
        } else {
            ui.label(format!("Data '{}' not found", self.data_key));
            PluginResponse::Keep
        }
    }
}

fn main() {
    let mut ctx = ViewerContext::default();
    ctx.add_data("pressure", vec![1013.2, 1012.8, 1013.5, 1014.1]);
    
    let custom_plugin = CustomPlugin {
        name: "Pressure Viewer".to_string(),
        data_key: "pressure".to_string(),
    };
    
    ctx.register_plugin(custom_plugin);
    ctx.render();
}

高级功能

数据更新与实时可视化

use re_viewer_context::{ViewerContext, PlotPlugin};
use std::thread;
use std::time::Duration;

fn main() {
    let mut ctx = ViewerContext::default();
    let mut data = vec![0.0];
    
    // 设置实时绘图
    let plot_plugin = PlotPlugin::new("realtime_plot")
        .with_title("Real-time Data")
        .with_realtime(true);
    
    ctx.register_plugin(plot_plugin);
    
    // 模拟数据更新线程
    thread::spawn(move || {
        for i in 1..100 {
            thread::sleep(Duration::from_millis(100));
            data.push((i as f32).sin() * 10.0);
            ctx.update_data("realtime_plot", data.clone());
        }
    });
    
    // 主线程运行UI
    ctx.run();
}

上下文共享与多视图

use re_viewer_context::{ViewerContext, SharedContext};

fn main() {
    let ctx = ViewerContext::default();
    let shared_ctx = SharedContext::new(ctx);
    
    // 在多个线程中使用共享上下文
    let ctx1 = shared_ctx.clone();
    thread::spawn(move || {
        let mut ctx = ctx1.lock().unwrap();
        ctx.add_data("thread1_data", vec![1, 2, 3]);
    });
    
    let ctx2 = shared_ctx.clone();
    thread::spawn(move || {
        let mut ctx = ctx2.lock().unwrap();
        ctx.add_data("thread2_data", vec![4, 5, 6]);
    });
    
    // 主线程渲染
    let mut main_ctx = shared_ctx.lock().unwrap();
    main_ctx.render();
}

性能优化技巧

  1. 对于大型数据集,使用add_data_with_id而不是add_data来避免重复解析
  2. 在不需要实时更新的情况下,设置set_realtime(false)减少渲染开销
  3. 使用DataRef而不是直接复制大数据集
  4. 对静态数据启用缓存enable_caching(true)

完整示例demo

下面是一个结合了多个功能的完整示例:

use re_viewer_context::{ViewerContext, Plugin, PluginResponse, PlotPlugin, SharedContext};
use egui::{Ui, Color32};
use std::thread;
use std::time::Duration;

// 自定义插件结构体
struct StatsPlugin {
    name: String,
    data_key: String,
}

impl Plugin for StatsPlugin {
    fn name(&self) -> &str {
        &self.name
    }
    
    fn show(&mut self, ui: &mut Ui, ctx: &mut ViewerContext) -> PluginResponse {
        if let Some(data) = ctx.get_data::<Vec<f32>>(&self.data_key) {
            ui.heading(&self.data_key);
            
            // 计算统计信息
            let avg = data.iter().sum::<f32>() / data.len() as f32;
            let max = data.iter().fold(f32::MIN, |a, &b| a.max(b));
            let min = data.iter().fold(f32::MAX, |a, &b| a.min(b));
            
            ui.label(format!("Average: {:.2}", avg));
            ui.label(format!("Max: {:.2}", max));
            ui.label(format!("Min: {:.2}", min));
            
            PluginResponse::Keep
        } else {
            ui.label(format!("Data '{}' not found", self.data_key));
            PluginResponse::Keep
        }
    }
}

fn main() {
    // 创建共享上下文
    let ctx = ViewerContext::default();
    let shared_ctx = SharedContext::new(ctx);
    
    // 主线程数据准备
    let main_ctx = shared_ctx.clone();
    thread::spawn(move || {
        let mut ctx = main_ctx.lock().unwrap();
        
        // 添加静态数据
        ctx.add_data("population", vec![8.5, 8.8, 9.1, 9.4, 9.7]);
        
        // 注册统计插件
        let stats_plugin = StatsPlugin {
            name: "Population Stats".to_string(),
            data_key: "population".to_string(),
        };
        ctx.register_plugin(stats_plugin);
        
        // 注册绘图插件
        let plot_plugin = PlotPlugin::new("population_plot")
            .with_title("Population Growth")
            .with_x_label("Year")
            .with_y_label("Millions");
        ctx.register_plugin(plot_plugin);
    });
    
    // 实时数据线程
    let realtime_ctx = shared_ctx.clone();
    thread::spawn(move || {
        let mut data = vec![0.0];
        let mut ctx = realtime_ctx.lock().unwrap();
        
        // 注册实时绘图插件
        let realtime_plugin = PlotPlugin::new("realtime_data")
            .with_title("Real-time Sensor Data")
            .with_realtime(true);
        ctx.register_plugin(realtime_plugin);
        
        // 模拟实时数据更新
        for i in 1..=100 {
            thread::sleep(Duration::from_millis(200));
            data.push((i as f32 * 0.1).cos() * 5.0 + 10.0);
            ctx.update_data("realtime_data", data.clone());
        }
    });
    
    // 主线程渲染
    let mut render_ctx = shared_ctx.lock().unwrap();
    render_ctx.run();
}

总结

re_viewer_context为Rust数据分析可视化提供了强大的上下文管理功能,通过其插件系统可以轻松扩展各种可视化组件。无论是简单的数据绘图还是复杂的实时数据分析系统,都能通过这个库高效实现。

回到顶部