Skip to content

Commit bff24dd

Browse files
committed
bugfix: keywords can handle numeric arguments
e.g. {{"borderpad", "0.2"}} was not possible before bc Py*_FromString couldn't recognise that "0.2" was a number. resulted in uncaught exception
1 parent b568e87 commit bff24dd

File tree

2 files changed

+77
-48
lines changed

2 files changed

+77
-48
lines changed

examples/legend.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,34 @@ void basic() {
1010
plt::show();
1111
}
1212

13+
void loc() {
14+
plt::figure();
15+
plt::plot({1, 2, 3}, {{"label", "a line"}});
16+
plt::plot({1, 3, 5}, {{"label", "also a line"}});
17+
plt::legend("lower left");
18+
plt::show();
19+
}
20+
1321
void bbox() {
1422
plt::figure();
1523
plt::plot({1, 2, 3}, {{"label", "a line"}});
1624
plt::plot({1, 3, 5}, {{"label", "also a line"}});
1725
plt::legend(std::vector<double>{0.5, 0.7});
1826
plt::show();
1927
}
20-
/*
28+
2129
void keywords() {
2230
plt::figure();
2331
plt::plot({1, 2, 3}, {{"label", "a line"}});
2432
plt::plot({1, 3, 5}, {{"label", "also a line"}});
2533
plt::legend("best", {{"borderpad", "0.2"}});
2634
plt::show();
2735
}
28-
*/
36+
2937
int main() {
3038
basic();
39+
loc();
3140
bbox();
32-
// keywords();
41+
keywords();
3342
return 0;
3443
}

matplotlibcpp.h

Lines changed: 65 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <numeric>
2525
#include <stdexcept>
2626
#include <vector>
27+
#include <sstream>
2728

2829
#include <Python.h>
2930

@@ -231,24 +232,23 @@ struct _interpreter {
231232
!s_python_function_draw || !s_python_function_pause ||
232233
!s_python_function_figure || !s_python_function_fignum_exists ||
233234
!s_python_function_plot || !s_python_function_quiver ||
234-
!s_python_function_contour ||
235-
!s_python_function_semilogx || !s_python_function_semilogy ||
236-
!s_python_function_loglog || !s_python_function_fill ||
237-
!s_python_function_fill_between || !s_python_function_subplot ||
238-
!s_python_function_legend || !s_python_function_ylim ||
239-
!s_python_function_title || !s_python_function_axis ||
240-
!s_python_function_xlabel || !s_python_function_ylabel ||
241-
!s_python_function_xticks || !s_python_function_yticks ||
242-
!s_python_function_xscale || !s_python_function_yscale ||
243-
!s_python_function_grid || !s_python_function_xlim ||
244-
!s_python_function_ion || !s_python_function_ginput ||
245-
!s_python_function_save || !s_python_function_clf ||
246-
!s_python_function_annotate || !s_python_function_errorbar ||
247-
!s_python_function_errorbar || !s_python_function_tight_layout ||
248-
!s_python_function_stem || !s_python_function_xkcd ||
249-
!s_python_function_text || !s_python_function_suptitle ||
250-
!s_python_function_bar || !s_python_function_subplots_adjust ||
251-
!s_python_function_spy) {
235+
!s_python_function_contour || !s_python_function_semilogx ||
236+
!s_python_function_semilogy || !s_python_function_loglog ||
237+
!s_python_function_fill || !s_python_function_fill_between ||
238+
!s_python_function_subplot || !s_python_function_legend ||
239+
!s_python_function_ylim || !s_python_function_title ||
240+
!s_python_function_axis || !s_python_function_xlabel ||
241+
!s_python_function_ylabel || !s_python_function_xticks ||
242+
!s_python_function_yticks || !s_python_function_xscale ||
243+
!s_python_function_yscale || !s_python_function_grid ||
244+
!s_python_function_xlim || !s_python_function_ion ||
245+
!s_python_function_ginput || !s_python_function_save ||
246+
!s_python_function_clf || !s_python_function_annotate ||
247+
!s_python_function_errorbar || !s_python_function_errorbar ||
248+
!s_python_function_tight_layout || !s_python_function_stem ||
249+
!s_python_function_xkcd || !s_python_function_text ||
250+
!s_python_function_suptitle || !s_python_function_bar ||
251+
!s_python_function_subplots_adjust || !s_python_function_spy) {
252252
throw std::runtime_error("Couldn't find required function!");
253253
}
254254

@@ -303,6 +303,24 @@ struct _interpreter {
303303
~_interpreter() { Py_Finalize(); }
304304
};
305305

306+
void process_keywords(PyObject *kwargs,
307+
const std::map<std::string, std::string> &keywords) {
308+
for (auto const &item : keywords) {
309+
// check if the keyword is a number
310+
try {
311+
std::stringstream ss(item.second);
312+
double d;
313+
ss >> d;
314+
PyDict_SetItemString(kwargs, item.first.c_str(), PyFloat_FromDouble(d));
315+
}
316+
// if its not, then leave it as string
317+
catch (std::exception& e) {
318+
PyDict_SetItemString(kwargs, item.first.c_str(),
319+
PyString_FromString(item.second.c_str()));
320+
}
321+
}
322+
}
323+
306324
} // end namespace detail
307325

308326
// must be called before the first regular call to matplotlib to have any effect
@@ -429,8 +447,7 @@ PyObject *get_2darray(const std::vector<::std::vector<Numeric>> &v) {
429447
}
430448

431449
// suitable for more general matrices (especially Eigen matrices)
432-
template <typename Matrix>
433-
PyObject *get_2darray(const Matrix &A) {
450+
template <typename Matrix> PyObject *get_2darray(const Matrix &A) {
434451
detail::_interpreter::get(); // interpreter needs to be initialized for the
435452
// numpy commands to work
436453
if (A.size() < 1)
@@ -455,8 +472,7 @@ PyObject *get_2darray(const Matrix &A) {
455472

456473
#else // fallback if we don't have numpy: copy every element of the given vector
457474

458-
template <typename Vector>
459-
PyObject *get_array(const Vector &v) {
475+
template <typename Vector> PyObject *get_array(const Vector &v) {
460476
detail::_interpreter::get();
461477
PyObject *list = PyList_New(v.size());
462478
for (size_t i = 0; i < v.size(); ++i) {
@@ -666,7 +682,7 @@ bool semilogy(const VectorY &y,
666682
// @param z The function value of the datapoints in a matrix
667683
// @param keywords Additional keywords
668684
template <typename Matrix>
669-
void plot_surface(const Matrix &x, const Matrix& y, const Matrix& z,
685+
void plot_surface(const Matrix &x, const Matrix &y, const Matrix &z,
670686
const std::map<std::string, std::string> &keywords =
671687
std::map<std::string, std::string>()) {
672688
// We lazily load the modules here the first time this function is called
@@ -765,14 +781,13 @@ void plot_surface(const Matrix &x, const Matrix& y, const Matrix& z,
765781
Py_DECREF(res);
766782
}
767783

768-
769784
// @brief plot_surface for datapoints (x_ij, y_ij, z_ij) with i,j = 0..n
770785
// @param x The x values of the datapoints in a matrix
771786
// @param y The y values of the datapoints in a matrix
772787
// @param z The function value of the datapoints in a matrix
773788
// @param keywords Additional keywords
774789
template <typename Matrix>
775-
void contour(const Matrix &x, const Matrix& y, const Matrix& z,
790+
void contour(const Matrix &x, const Matrix &y, const Matrix &z,
776791
const std::map<std::string, std::string> &keywords = {}) {
777792
detail::_interpreter::get();
778793

@@ -801,8 +816,8 @@ void contour(const Matrix &x, const Matrix& y, const Matrix& z,
801816
PyString_FromString(it->second.c_str()));
802817
}
803818

804-
PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_contour,
805-
args, kwargs);
819+
PyObject *res = PyObject_Call(
820+
detail::_interpreter::get().s_python_function_contour, args, kwargs);
806821
if (!res)
807822
throw std::runtime_error("failed surface");
808823

@@ -980,18 +995,17 @@ bool scatter(const VectorX &x, const VectorY &y, const double s = 1.0,
980995

981996
template <typename VectorX, typename VectorY>
982997
bool scatter(const VectorX &x, const VectorY &y,
983-
const std::map<std::string, std::string>& keywords) {
998+
const std::map<std::string, std::string> &keywords) {
984999
return scatter(x, y, 1.0, keywords);
9851000
}
9861001

987-
9881002
// @brief Spy plot
9891003
// @param A the matrix
9901004
// @param precision Plot all elements above `|precision|`
9911005
// @param keywords Additional keywords
9921006
template <typename Matrix>
9931007
bool spy(const Matrix &A,
994-
const std::map<std::string, std::string>& keywords = {}) {
1008+
const std::map<std::string, std::string> &keywords = {}) {
9951009
PyObject *Aarray = get_2darray(A);
9961010

9971011
PyObject *kwargs = PyDict_New();
@@ -1048,7 +1062,8 @@ bool bar(const std::vector<Numeric> &y, std::string ec = "black",
10481062
return res;
10491063
}
10501064

1051-
inline bool subplots_adjust(const std::map<std::string, double> &keywords = {}) {
1065+
inline bool
1066+
subplots_adjust(const std::map<std::string, double> &keywords = {}) {
10521067

10531068
PyObject *kwargs = PyDict_New();
10541069
for (std::map<std::string, double>::const_iterator it = keywords.begin();
@@ -1217,8 +1232,7 @@ bool stem(const std::vector<NumericX> &x, const std::vector<NumericY> &y,
12171232
}
12181233

12191234
template <typename VectorX, typename VectorY>
1220-
bool errorbar(const VectorX &x, const VectorY &y,
1221-
const VectorY &yerr,
1235+
bool errorbar(const VectorX &x, const VectorY &y, const VectorY &yerr,
12221236
const std::map<std::string, std::string> &keywords = {}) {
12231237
assert(x.size() == y.size());
12241238

@@ -1359,7 +1373,7 @@ inline void figure_size(size_t w, size_t h) {
13591373
template <typename Vector = std::vector<double>>
13601374
inline void legend(const std::string &loc = "best",
13611375
const Vector &bbox_to_anchor = Vector(),
1362-
const std::map<std::string, std::string>& keywords = {}) {
1376+
const std::map<std::string, std::string> &keywords = {}) {
13631377
detail::_interpreter::get();
13641378

13651379
PyObject *kwargs = PyDict_New();
@@ -1375,11 +1389,7 @@ inline void legend(const std::string &loc = "best",
13751389
}
13761390

13771391
// add other keywords
1378-
for (std::map<std::string, std::string>::const_iterator it = keywords.begin();
1379-
it != keywords.end(); ++it) {
1380-
PyDict_SetItemString(kwargs, it->first.c_str(),
1381-
PyUnicode_FromString(it->second.c_str()));
1382-
}
1392+
detail::process_keywords(kwargs, keywords);
13831393

13841394
PyObject *res =
13851395
PyObject_Call(detail::_interpreter::get().s_python_function_legend,
@@ -1394,11 +1404,23 @@ inline void legend(const std::string &loc = "best",
13941404
}
13951405

13961406
template <typename Vector>
1397-
inline void legend(const Vector& bbox_to_anchor,
1398-
const std::map<std::string, std::string>& keywords = {}) {
1407+
inline void legend(const Vector &bbox_to_anchor,
1408+
const std::map<std::string, std::string> &keywords = {}) {
13991409
legend("", bbox_to_anchor, keywords);
14001410
}
14011411

1412+
inline void legend(const std::string &loc,
1413+
const std::map<std::string, std::string> &keywords = {}) {
1414+
legend(loc, std::vector<double>(), keywords);
1415+
}
1416+
1417+
// to support C-style strings we also need const char[], std::string only
1418+
// does not capture calls of style legend("lower left")
1419+
inline void legend(const char loc[],
1420+
const std::map<std::string, std::string> &keywords = {}) {
1421+
legend(loc, std::vector<double>(), keywords);
1422+
}
1423+
14021424
/*
14031425
inline void legend(const std::string& loc,
14041426
const std::map<std::string, std::string>& keywords = {}) {
@@ -1410,8 +1432,7 @@ inline void legend(const std::map<std::string, std::string>& keywords) {
14101432
}
14111433
*/
14121434

1413-
template <typename Numeric>
1414-
void ylim(const Numeric bottom, const Numeric top) {
1435+
template <typename Numeric> void ylim(const Numeric bottom, const Numeric top) {
14151436
detail::_interpreter::get();
14161437

14171438
PyObject *list = PyList_New(2);
@@ -1430,8 +1451,7 @@ void ylim(const Numeric bottom, const Numeric top) {
14301451
Py_DECREF(res);
14311452
}
14321453

1433-
template <typename Numeric>
1434-
void xlim(const Numeric left, const Numeric right) {
1454+
template <typename Numeric> void xlim(const Numeric left, const Numeric right) {
14351455
detail::_interpreter::get();
14361456

14371457
PyObject *list = PyList_New(2);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy