Skip to content

Commit e6b4559

Browse files
committed
Implement label rotation
1 parent 40c23ef commit e6b4559

3 files changed

Lines changed: 130 additions & 0 deletions

File tree

src/axes.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use crate::{
88
colors::{self, Color},
99
lines::Line2D,
10+
text::Text,
1011
};
1112
use numpy::convert::ToPyArray;
1213
use pyo3::{
@@ -302,6 +303,17 @@ impl Axes {
302303
pub fn xaxis_date(&mut self) {
303304
meth!(self.ax, xaxis_date, ()).unwrap();
304305
}
306+
307+
pub fn get_xticklabels(&mut self) -> Vec<Text> {
308+
Python::attach(|py| {
309+
let labels: Vec<_> = self.ax.bind(py)
310+
.call_method1(intern!(py, "get_xticklabels"), ())
311+
.unwrap()
312+
.extract()
313+
.unwrap();
314+
labels
315+
})
316+
}
305317
}
306318

307319
enum PlotData<D> {
@@ -741,3 +753,21 @@ where
741753
})
742754
}
743755
}
756+
757+
758+
#[cfg(test)]
759+
mod test {
760+
use crate::figure::Figure;
761+
762+
#[test]
763+
fn test_get_xticklabels() -> Result<(), crate::Error> {
764+
let fig = Figure::new()?;
765+
let [[mut ax]] = fig.subplots()?;
766+
ax.xy(&[0., 1.], &[0., 1.]).plot();
767+
for l in ax.get_xticklabels() {
768+
l.set_rotation(45.);
769+
}
770+
fig.save().to_file("target/test_get_xticklabels.pdf")?;
771+
Ok(())
772+
}
773+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use figure::Figure;
2424
pub mod axes;
2525
use axes::Axes;
2626
pub mod lines;
27+
pub mod text;
2728
include!("macros.rs");
2829

2930
/// Possible errors of matplotlib functions.

src/text.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//! Structs for including text in a figure.
2+
3+
use std::convert::Infallible;
4+
5+
use pyo3::{intern, prelude::*, sync::PyOnceLock};
6+
7+
pub struct Text {
8+
text: Py<PyAny>,
9+
}
10+
11+
impl FromPyObject<'_, '_> for Text {
12+
type Error = Infallible;
13+
14+
fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
15+
Ok(Self {
16+
text: obj.to_owned().unbind(),
17+
})
18+
}
19+
}
20+
21+
/// Text alignment specification.
22+
#[derive(Clone, Copy, Debug)]
23+
pub enum Align {
24+
Left,
25+
Center,
26+
Right,
27+
}
28+
29+
impl Align {
30+
#[inline]
31+
fn to_str(&self) -> &str {
32+
match self {
33+
Align::Left => "left",
34+
Align::Center => "center",
35+
Align::Right => "right",
36+
}
37+
}
38+
}
39+
40+
macro_rules! set {
41+
($(#[$doc: meta])* $name: ident $(, $v: ident, $t: ty, $conv: expr)*) => {
42+
$(#[$doc])*
43+
pub fn $name(&self, $($v: $t)*) -> &Self {
44+
Python::attach(|py| {
45+
self.text.bind(py)
46+
.call_method1(stringify!($name), ($($conv,)*))
47+
.unwrap();
48+
});
49+
self
50+
}}}
51+
52+
impl Text {
53+
fn cls(py: Python<'_>) -> &Bound<'_, PyAny> {
54+
static TEXT: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
55+
TEXT.import(py, "matplotlib.text", "Text")
56+
.expect("Cannot find matplotlib.text.Text")
57+
}
58+
59+
// FIXME: x: impl Coord
60+
pub fn new(x: f64, y: f64, text: &str) -> Self {
61+
Python::attach(|py| {
62+
Self::cls(py)
63+
.call1((x, y, text))
64+
.expect("New Text")
65+
.unbind()
66+
.extract(py)
67+
.unwrap()
68+
})
69+
}
70+
71+
/// Return whether antialiased rendering is used.
72+
pub fn get_antialiased(&self) -> bool {
73+
Python::attach(|py| {
74+
self.text
75+
.call_method1(py, intern!(py, "get_antialiased"), ())
76+
.unwrap()
77+
.extract(py)
78+
.unwrap()
79+
})
80+
}
81+
82+
set!(
83+
/// Set the horizontal alignment relative to the anchor point.
84+
set_horizontalalignment, align, Align, align.to_str());
85+
86+
set!(
87+
/// Set the rotation of the text.
88+
set_rotation, s, f64, s);
89+
}
90+
91+
#[cfg(test)]
92+
mod test {
93+
use super::*;
94+
95+
#[test]
96+
fn test_antialiased() {
97+
let _ = Text::new(0., 0., "Hello").get_antialiased();
98+
}
99+
}

0 commit comments

Comments
 (0)