8000 bugfix: keywords can handle numeric arguments · lindanxia/matplotlib-cpp@bff24dd · GitHub
[go: up one dir, main page]

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)
0