Skip to content

Commit 3d0bd7f

Browse files
committed
Use pyplot functions into their own submodule (as matplotlib)
1 parent f7e4795 commit 3d0bd7f

7 files changed

Lines changed: 132 additions & 74 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ A basic example
3535
---------------
3636

3737
```rust
38-
use matplotlib as plt;
38+
use matplotlib::pyplot as plt;
3939

4040
fn main() -> Result<(), Box<dyn std::error::Error>> {
4141
let (fig, [[mut ax]]) = plt::subplots()?;

examples/a_simple_example.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/// https://matplotlib.org/stable/tutorials/introductory/quick_start.html#a-simple-example
22
33
use std::error::Error;
4-
use matplotlib as plt;
4+
use matplotlib::pyplot as plt;
55

66
fn main() -> Result<(), Box<dyn Error>> {
77
let (fig, [[mut ax]]) = plt::subplots()?;

examples/basic_example.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Example for the README
22

3-
use matplotlib as plt;
3+
use matplotlib::pyplot as plt;
44

55
fn main() -> Result<(), Box<dyn std::error::Error>> {
66
let (fig, [[mut ax]]) = plt::subplots()?;

examples/flower.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use polars_core::prelude::*;
2-
use matplotlib as plt;
2+
use matplotlib::pyplot as plt;
33

44
// Yellow Flag Iris, attributed to Iris pseudacorus flowers were
55
// measured in Norfolk marshes in 2018 by LCrossman.

src/axes.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ impl Axes {
3737
/// # Example
3838
///
3939
/// ```
40-
/// use matplotlib::{self as plt, colors};
40+
/// use matplotlib::{pyplot as plt, colors};
4141
/// let (fig, [[mut ax]]) = plt::subplots()?;
4242
/// let x = [1., 2., 3., 4.];
4343
/// let y = [1., 4., 2., 3.];
@@ -68,7 +68,7 @@ impl Axes {
6868
/// # Example
6969
///
7070
/// ```
71-
/// use matplotlib as plt;
71+
/// use matplotlib::pyplot as plt;
7272
/// let (fig, [[mut ax]]) = plt::subplots()?;
7373
/// ax.y(&[1., 4., 2., 3.]).plot();
7474
/// fig.save().to_file("target/Y_plot.pdf")?;
@@ -90,13 +90,15 @@ impl Axes {
9090
/// # Example
9191
///
9292
/// ```
93-
/// use matplotlib as plt;
93+
/// use matplotlib::pyplot as plt;
9494
/// let (fig, [[mut ax]]) = plt::subplots()?;
9595
/// ax.xy_from(&[(1., 2.), (4., 2.), (2., 3.), (3., 4.)]).plot();
9696
/// ax.xy_from([(1., 0.), (2., 3.), (3., 1.), (4., 3.)]).plot();
9797
/// fig.save().to_file("target/XY_from_plot.pdf")?;
9898
/// # Ok::<(), matplotlib::Error>(())
9999
/// ```
100+
// FIXME: show an example combining iterators using `zip`. The
101+
// `covid` research project may serve as a source of inspiration.
100102
pub fn xy_from<'a, I>(&'a mut self, xy: I) -> XYFrom<'a, I>
101103
where
102104
I: IntoIterator,
@@ -114,7 +116,7 @@ impl Axes {
114116
///
115117
/// # Example
116118
/// ```
117-
/// use matplotlib as plt;
119+
/// use matplotlib::pyplot as plt;
118120
/// let (fig, [[mut ax]]) = plt::subplots()?;
119121
/// ax.fun(|x| x * x, 0., 1.).plot();
120122
/// fig.save().to_file("target/Fun_plot.pdf")?;
@@ -142,7 +144,7 @@ impl Axes {
142144
/// # Example
143145
///
144146
/// ```
145-
/// use matplotlib::{self as plt, colors::Tab};
147+
/// use matplotlib::{pyplot as plt, colors::Tab};
146148
/// use ndarray::{Array1, Array2};
147149
/// let x: Array1<f64> = Array1::linspace(-1., 1., 30);
148150
/// let y: Array1<f64> = Array1::linspace(-1., 1., 30);
@@ -180,7 +182,7 @@ impl Axes {
180182
/// # Example
181183
///
182184
/// ```
183-
/// use matplotlib as plt;
185+
/// use matplotlib::pyplot as plt;
184186
/// let (fig, [[mut ax]]) = plt::subplots()?;
185187
/// ax.contour_fun([-1., 1.], [-1., 1.], |x, y| {
186188
/// (0.5 * x).powi(2) + y.powi(2)

src/lib.rs

Lines changed: 32 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,9 @@ use pyo3::{
1919

2020
pub mod colors;
2121
pub mod figure;
22-
use figure::Figure;
2322
pub mod axes;
24-
use axes::Axes;
2523
pub mod lines;
24+
pub mod pyplot;
2625
pub mod text;
2726
include!("macros.rs");
2827

@@ -93,76 +92,45 @@ impl From<&ImportError> for Error {
9392
}
9493
}
9594

96-
// RuntimeWarning: More than 20 figures have been opened. Figures
97-
// created through the pyplot interface (`matplotlib.pyplot.figure`)
98-
// are retained until explicitly closed and may consume too much
99-
// memory. […] Consider using `matplotlib.pyplot.close()`.
100-
//
101-
// => Do not use pyplot interface (since we need handles anyway).
102-
103-
fn pyplot<'a, 'py>(
104-
py: Python<'py>,
105-
f: &'a PyOnceLock<Py<PyAny>>,
106-
attr_name: &str
107-
) -> Result<&'a Bound<'py, PyAny>, Error> {
108-
f.get_or_try_init(py, || {
109-
Ok(py.import("matplotlib.pyplot").into_error(py)?
110-
.getattr(attr_name).into_error(py)?
111-
.unbind())
112-
})
113-
.map(|f| f.bind(py))
114-
}
115-
116-
/// Return a new figure.
117-
/// This figure is tracked by Matplotlib so [`show()`] displays it.
118-
/// This implies it must be explicitly deallocated using [`close()`].
119-
pub fn figure() -> Result<Figure, Error> {
120-
static FIG: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
121-
Python::attach(|py| {
122-
let fig = pyplot(py, &FIG, "figure")?;
123-
Ok(Figure { fig: fig.call0().into_error(py)?.unbind() })
124-
})
95+
/// A dict-like key-value store for config parameters, including
96+
/// validation.
97+
///
98+
/// Validating functions are defined and associated with rc parameters
99+
/// in [`rcsetup`].
100+
#[derive(Debug)]
101+
pub struct RcParams {
102+
dict: Py<PyAny>,
125103
}
126104

127-
/// Return a figure and a grid of subplots with `R` rows and `C` columns.
128-
pub fn subplots<const R: usize, const C: usize>(
129-
) -> Result<(Figure, [[Axes; C]; R]), Error> {
130-
let fig = figure()?;
131-
let ax = fig.subplots()?;
132-
Ok((fig, ax))
105+
impl RcParams {
106+
/// Return the subset of the `self` dictionary whose keys match
107+
/// ([`using
108+
/// re.search()`](https://docs.python.org/3/library/re.html#re.search)) the
109+
/// given pattern.
110+
pub fn find_all(&self, pat: &str) -> Vec<String> {
111+
Python::attach(|py| {
112+
self.dict.bind(py)
113+
.call_method1(intern!(py, "find_all"), (pat,)).unwrap()
114+
.cast().unwrap()
115+
.extract().unwrap()
116+
})
117+
}
133118
}
134119

135-
/// Display all open figures created with [`figure()`] or [`subplots()`].
136-
pub fn show() {
137-
static SHOW: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
138-
Python::attach(|py| {
139-
pyplot(py, &SHOW, "show")
140-
.expect("Cannot get matplotlib.pyplot.show")
141-
.call0()
142-
.expect("matplotlib.pyplot.show did not succeed");
143-
})
120+
pub fn rcsetup() {
121+
todo!()
144122
}
145123

146-
/// Close the figure `fig` (created with [`figure()`] or [`subplots()`]).
147-
pub fn close(fig: Figure) {
148-
static CLOSE: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
149-
Python::attach(|py| {
150-
pyplot(py, &CLOSE, "close")
151-
.expect("Cannot find matplotlib.pyplot.close")
152-
.call1((fig.fig,))
153-
.expect("matplotlib.pyplot.close did not succeed");
154-
})
124+
#[derive(Debug)]
125+
pub struct RcParamsRef<'a> {
126+
dict: &'a Bound<'a, PyAny>,
155127
}
156128

157-
/// Close all figures created with [`figure()`] or [`subplots()`].
158-
pub fn close_all() {
159-
static CLOSE: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
160-
Python::attach(|py| {
161-
pyplot(py, &CLOSE, "close")
162-
.expect("Cannot get matplotlib.pyplot.close")
163-
.call1(("all",))
164-
.expect("matplotlib.pyplot.close('all') did not succeed");
165-
})
129+
fn rc_params(py: Python<'_>) -> RcParamsRef<'_> {
130+
static RCPARAMS: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
131+
let dict = RCPARAMS.import(py, "matplotlib", "rcParams")
132+
.expect("Cannot find matplotlib.rcParams");
133+
RcParamsRef { dict }
166134
}
167135

168136

src/pyplot.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//! State-based interface to matplotlib. It provides an implicit,
2+
//! MATLAB-like, way of plotting. It also opens figures on your
3+
//! screen, and acts as the figure GUI manager.
4+
//!
5+
//! pyplot is mainly intended for interactive plots and simple cases
6+
//! of programmatic plot generation:
7+
//!
8+
//! ```
9+
//! use matplotlib::pyplot as plt;
10+
//!
11+
//! ```
12+
13+
use pyo3::{
14+
prelude::*, sync::PyOnceLock,
15+
};
16+
use crate::{axes::Axes, Error, figure::Figure, IntoError};
17+
18+
// RuntimeWarning: More than 20 figures have been opened. Figures
19+
// created through the pyplot interface (`matplotlib.pyplot.figure`)
20+
// are retained until explicitly closed and may consume too much
21+
// memory. […] Consider using `matplotlib.pyplot.close()`.
22+
//
23+
// => Do not use pyplot interface (since we need handles anyway).
24+
25+
fn pyplot<'a, 'py>(
26+
py: Python<'py>,
27+
f: &'a PyOnceLock<Py<PyAny>>,
28+
attr_name: &str
29+
) -> Result<&'a Bound<'py, PyAny>, Error> {
30+
f.get_or_try_init(py, || {
31+
Ok(py.import("matplotlib.pyplot").into_error(py)?
32+
.getattr(attr_name).into_error(py)?
33+
.unbind())
34+
})
35+
.map(|f| f.bind(py))
36+
}
37+
38+
/// Return a new figure.
39+
/// This figure is tracked by Matplotlib so [`show()`] displays it.
40+
/// This implies it must be explicitly deallocated using [`close()`].
41+
pub fn figure() -> Result<Figure, Error> {
42+
static FIG: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
43+
Python::attach(|py| {
44+
let fig = pyplot(py, &FIG, "figure")?;
45+
Ok(Figure { fig: fig.call0().into_error(py)?.unbind() })
46+
})
47+
}
48+
49+
/// Return a figure and a grid of subplots with `R` rows and `C` columns.
50+
pub fn subplots<const R: usize, const C: usize>(
51+
) -> Result<(Figure, [[Axes; C]; R]), Error> {
52+
let fig = figure()?;
53+
let ax = fig.subplots()?;
54+
Ok((fig, ax))
55+
}
56+
57+
/// Display all open figures created with [`figure()`] or [`subplots()`].
58+
pub fn show() {
59+
static SHOW: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
60+
Python::attach(|py| {
61+
pyplot(py, &SHOW, "show")
62+
.expect("Cannot get matplotlib.pyplot.show")
63+
.call0()
64+
.expect("matplotlib.pyplot.show did not succeed");
65+
})
66+
}
67+
68+
/// Close the figure `fig` (created with [`figure()`] or [`subplots()`]).
69+
pub fn close(fig: Figure) {
70+
static CLOSE: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
71+
Python::attach(|py| {
72+
pyplot(py, &CLOSE, "close")
73+
.expect("Cannot find matplotlib.pyplot.close")
74+
.call1((fig.fig,))
75+
.expect("matplotlib.pyplot.close did not succeed");
76+
})
77+
}
78+
79+
/// Close all figures created with [`figure()`] or [`subplots()`].
80+
pub fn close_all() {
81+
static CLOSE: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
82+
Python::attach(|py| {
83+
pyplot(py, &CLOSE, "close")
84+
.expect("Cannot get matplotlib.pyplot.close")
85+
.call1(("all",))
86+
.expect("matplotlib.pyplot.close('all') did not succeed");
87+
})
88+
}

0 commit comments

Comments
 (0)