egui_dataframe/
lib.rs

1#![doc(html_root_url = "https://docs.rs/egui-dataframe/0.3.3")]
2//! egui dataframe
3//!
4
5use eframe::{self, egui::*};
6use egui_grid::GridBuilder;
7use egui_extras::{Size, TableBuilder, Column};
8use polars::prelude::{DataFrame, Schema}; // , AnyValue, Field, DataType
9
10/// Decorator
11#[derive(Debug, Clone)]
12pub struct Decorator {
13  /// sz: Vec2
14  pub sz: Vec2,
15  /// sense: Sense
16  pub sense: Sense,
17  /// cols: vec![bgr, bgc, fgc]
18  pub cols: Vec<Option<Color32>>,
19  /// align: Align2
20  pub align: Align2,
21  /// ofs: Vec2
22  pub ofs: Vec2,
23  /// fontid: FontId
24  pub fontid: FontId
25}
26
27/// Decorator
28impl Decorator {
29  /// slice Color32 to Vec Option Color32
30  pub fn opt(v: &[Color32]) -> Vec<Option<Color32>> {
31    v.iter().map(|c| Some(*c)).collect::<Vec<_>>()
32  }
33
34  /// constructor
35  pub fn new(sz: Vec2, sense: Sense, cols: Vec<Option<Color32>>,
36    align: Align2, ofs: Vec2, fontid: FontId) -> Self {
37    Decorator{sz, sense, cols, align, ofs, fontid}
38  }
39
40  /// paint text as painter.text allocated from ui
41  /// - ui: &amp;mut Ui
42  /// - txt: &str
43  /// - result: (Response, Painter)
44  pub fn disp(&self, ui: &mut Ui, txt: &str) -> Option<(Response, Painter)> {
45    let (bgr, bgc, fgc) = (self.cols[0], self.cols[1], self.cols[2]);
46    if let Some(fgc) = fgc {
47      let (resp, painter) = ui.allocate_painter(self.sz, self.sense);
48      let rc = resp.rect;
49      // rc.max = rc.min + sz; // when calc mut rc (same with resp.rect)
50      if let Some(bgr) = bgr {
51        if let Some(bgc) = bgc {
52          painter.rect(rc, 0.0, bgc, Stroke{width: 1.0, color: bgr});
53        }
54      }
55      painter.text(rc.min + self.ofs, self.align, txt,
56        self.fontid.clone(), fgc);
57      Some((resp, painter))
58    }else{
59      ui.label(RichText::new(txt)
60        .size(self.fontid.size).family(self.fontid.family.clone()));
61      None
62    }
63  }
64}
65
66/// DecoFunc
67pub type DecoFunc<'a> = &'a mut dyn FnMut(&Decorator, &mut Ui, &str,
68  usize, usize) -> bool;
69
70/// DecoFs
71pub struct DecoFs<'a> {
72  /// fncs
73  pub fncs: (DecoFunc<'a>, DecoFunc<'a>)
74}
75
76/// DecoFs
77impl<'a> DecoFs<'a> {
78  /// default
79  pub fn default(d: &Decorator, ui: &mut Ui, tx: &str,
80    _ri: usize, _ci: usize) -> bool {
81    let _resp_painter = d.disp(ui, tx);
82    true
83  }
84}
85
86/// DFDesc
87#[derive(Debug, Clone)]
88pub struct DFDesc {
89  /// default deco
90  pub default_deco: (Decorator, Decorator),
91  /// deco (head, body=column)
92  pub deco: Vec<(Option<Decorator>, Option<Decorator>)>,
93  /// schema
94  pub schema: Schema
95}
96
97/// DFDesc
98impl DFDesc {
99  /// constructor
100  pub fn new(default_deco: (Decorator, Decorator), schema: Schema) -> Self {
101    let n = schema.len();
102    DFDesc{default_deco,
103      deco: Vec::<(Option<Decorator>, Option<Decorator>)>::with_capacity(n),
104      schema}
105  }
106
107  /// all from (pipeline)
108  pub fn all_from(mut self,
109    deco: Vec<(Option<Decorator>, Option<Decorator>)>) -> Self {
110    self.deco = deco; // deco.into_iter().map(|hb| self.push(hb));
111    self
112  }
113
114  /// all default (pipeline)
115  pub fn all_default(mut self) -> Self {
116    for _i in 0..self.schema.len() { self.push((None, None)) }
117    self
118  }
119
120  /// push deco (head, body=column)
121  pub fn push(&mut self, deco: (Option<Decorator>, Option<Decorator>)) {
122    self.deco.push(deco);
123  }
124
125  /// display dataframe to ui (TableBuilder)
126  pub fn disp<'a>(&'a self, ui: &'a mut Ui, f: &mut DecoFs, df: &DataFrame,
127    height_head: f32, height_body: f32, resizable: bool, striped: bool,
128    vscroll: bool) {
129    let (nrows, ncols) = (df.height(), df.width());
130    let cols = df.get_column_names();
131    TableBuilder::new(ui).columns(Column::auto().resizable(resizable), ncols)
132    .resizable(resizable)
133    .striped(striped)
134    .vscroll(vscroll)
135    .header(height_head, |mut header| {
136      for (i, head) in cols.iter().enumerate() {
137        header.col(|ui| {
138          let tx = format!("{}", head);
139          let d = if self.deco.len() == 0 { &self.default_deco.0 } else {
140            match &self.deco[i] {
141            (None, _) => &self.default_deco.0,
142            (Some(deco_head), _) => deco_head
143            }
144          };
145          f.fncs.0(d, ui, &tx, 0, i);
146          // ui.label(&tx); // ui.heading(&tx);
147        });
148      }
149    })
150    .body(|body| {
151      body.rows(height_body, nrows, |ri, mut row| {
152        for (i, col) in cols.iter().enumerate() {
153          row.col(|ui| {
154            if let Ok(column) = &df.column(col) {
155//              if let Ok(value) = column.get(ri) {
156              let value = column.get(ri);
157              let tx = format!("{}", value);
158              let d = if self.deco.len() == 0 { &self.default_deco.1 } else {
159                match &self.deco[i] {
160                (_, None) => &self.default_deco.1,
161                (_, Some(deco_body)) => deco_body
162                }
163              };
164              f.fncs.1(d, ui, &tx, ri, i);
165              // ui.label(&tx);
166//              }
167            }
168          });
169        }
170      });
171    });
172  }
173
174  /// display grid to ui (GridBuilder)
175  /// - ma: &amp;style::Margin::[same(f32) or symmetric(f32, f32)]
176  pub fn grid<'a>(&'a self, ui: &'a mut Ui, f: &mut DecoFs, df: &DataFrame,
177    width: f32, height: f32, ts: &TextStyle,
178    sp: &(f32, f32), ma: &style::Margin) {
179    let (nrows, ncols) = (df.height(), df.width());
180    ui.style_mut().override_text_style = Some(ts.clone());
181    let mut gb = GridBuilder::new().spacing(sp.0, sp.1).clip(true);
182    gb = (0..nrows).into_iter().fold(gb, |gb, _i|
183      (0..ncols).into_iter().fold(gb.new_row(Size::exact(height)), |gb, _j|
184        gb.cell(Size::exact(width)).with_margin(ma.clone())
185        // gb.cell(Size::remainder()).with_margin(ma.clone())
186      )
187    );
188    gb.show(ui, |mut grid| {
189      let cols = df.get_column_names();
190      for j in 0..nrows {
191        for (i, col) in cols.iter().enumerate() {
192          // grid.empty();
193          grid.cell(|ui| {
194            if let Ok(column) = &df.column(col) {
195//              if let Ok(value) = column.get(j) {
196              let value = column.get(j);
197              let tx = format!("{}", value);
198              let d = if self.deco.len() == 0 { &self.default_deco.1 } else {
199                match &self.deco[i] {
200                (_, None) => &self.default_deco.1,
201                (_, Some(deco_body)) => deco_body
202                }
203              };
204              f.fncs.1(d, ui, &tx, j, i);
205              // ui.label(&tx);
206//              }
207            }
208          });
209        }
210      }
211    });
212  }
213}
214
215/// tests
216#[cfg(test)]
217mod tests {
218  use super::*;
219
220  /// [-- --nocapture] [-- --show-output]
221  /// https://github.com/rust-lang/rust/pull/103681
222  /// https://github.com/rust-lang/rust/issues/104053
223  /// -- --test-threads=[0|1]
224  /// RUST_TEST_THREADS=[0|1]
225  /// https://github.com/rust-lang/rust/pull/107396
226  /// fn dispatch_to_ui_thread&<lt;R, F&gt;>(f: F) -&gt;> R where
227  ///   F: FnOnce() -&gt;> R + Send, R: Send
228  /// #[test(flavour = "static_thread")]
229  #[test]
230  fn test_dataframe() {
231    assert_eq!(Decorator::opt(&[Color32::RED, Color32::GREEN]),
232      [Some(Color32::RED), Some(Color32::GREEN)]);
233  }
234}