Skip to content

Commit ff55d61

Browse files
committed
Squashed commit of the following:
commit 9047863bb0c1a34bc6473c2dadee720073db631e Author: Claire Guilbaud <[email protected]> Date: Thu Mar 26 15:46:34 2020 +0100 @enh figure with variadic template args commit a8a3dd018c14a5e5c98b084f566865ba936c4fec Author: Claire Guilbaud <[email protected]> Date: Thu Mar 26 14:55:36 2020 +0100 @enh close commit 6440609657de0f47b6fd4c1a8c213704f2810a4f Author: Claire Guilbaud <[email protected]> Date: Thu Mar 26 13:41:18 2020 +0100 @enh generic form for scatter
1 parent 771b7ed commit ff55d61

File tree

5 files changed

+215
-29
lines changed

5 files changed

+215
-29
lines changed

.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,11 @@
3333

3434
# Build
3535
/examples/build/*
36+
/contrib/unittests_catch2/build/*
37+
/contrib/unittests_catch2/cmake-
38+
/contrib/unittests_catch2/build/*
39+
/contrib/unittests_catch2/cmake-*
40+
41+
# IDE directories
42+
.idea
43+

contrib/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Note: platforms folder is necessary to make qt works.
3535

3636
```cmd
3737
> cd contrib/unittests_catch2
38-
> cmake -S -B "./build"
38+
> cmake -S . -B "./build"
3939
> cd build
4040
> make
4141
> ./run_MatplotlibcppTests

contrib/unittests_catch2/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ target_compile_features(${TU_EXE}
7676
)
7777
target_compile_definitions(${TU_EXE}
7878
PRIVATE
79+
USE_VARIADIC_TEMPLATES_ARGS
7980
CATCH_UNIT_TESTS)
8081
target_compile_options(${TU_EXE}
8182
PRIVATE

contrib/unittests_catch2/tu_matplotlibcpp.cpp

+24-12
Original file line numberDiff line numberDiff line change
@@ -304,15 +304,14 @@ TEST_CASE("matplotlib", "[matplotlibcpp][matplotlib]") {
304304
REQUIRE_NOTHROW(plt::scatter(x_scatter, y_scatter, 1.0, kw_2));
305305
plt::show(true);
306306

307-
// @FIXME
308-
// plt::clf();
309-
// REQUIRE_NOTHROW(plt::scatter(x_scatter, y_scatter, make_tuple(
310-
// "cmap", "twilight",
311-
// "c", z_scatter,
312-
// "s", 46.,
313-
// "marker", "o"
314-
// )));
315-
// plt::show(true);
307+
plt::clf();
308+
REQUIRE_NOTHROW(plt::scatter(x_scatter, y_scatter, make_tuple(
309+
"cmap", "twilight",
310+
"c", z_scatter,
311+
"s", 46.,
312+
"marker", "o"
313+
)));
314+
plt::show(true);
316315
}
317316

318317
SECTION("quiver") {
@@ -534,12 +533,25 @@ TEST_CASE("matplotlib_ending", "[matplotlibcpp][matplotlib]") {
534533
plt::clf();
535534
plt::named_plot("Courbe", x, y);
536535
plt::show(true);
537-
REQUIRE_NOTHROW(plt::close());
536+
REQUIRE_NOTHROW(plt::close(fig_num));
538537
}
539538

540539
TEST_CASE("matplotlib_figure", "[matplotlibcpp]") {
541540
plt::ion();
542-
REQUIRE_NOTHROW(plt::figure_size(100, 100));
543-
plt::show(true);
541+
542+
SECTION("figure_size") {
543+
REQUIRE_NOTHROW(plt::figure_size(100, 100));
544+
plt::show(true);
545+
REQUIRE_NOTHROW(plt::close());
546+
}
547+
548+
SECTION("figure_template") {
549+
vector<double> fsize {6., 4.};
550+
REQUIRE_NOTHROW(plt::figure(111, make_tuple(
551+
"dpi", 50,
552+
"edgecolor", "green",
553+
"figsize", fsize
554+
)));
555+
}
544556
}
545557
#endif // CATCH_UNIT_TESTS

matplotlibcpp.h

+181-16
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,102 @@ PyObject* get_array(const std::vector<Numeric>& v)
417417

418418
#endif // WITHOUT_NUMPY
419419

420+
#ifdef USE_VARIADIC_TEMPLATES_ARGS
421+
422+
// ---------------------------------------------
423+
// Analyse des keywords dans un tuple
424+
//
425+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const bool value) {
426+
return {key, value ? Py_True : Py_False};
427+
}
428+
429+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const int value) {
430+
return {key, PyLong_FromLong(value)};
431+
}
432+
433+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const long value) {
434+
return {key, PyLong_FromLong(value)};
435+
}
436+
437+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const double value) {
438+
return {key, PyFloat_FromDouble(value)};
439+
}
440+
441+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const char* value) {
442+
return {key, PyString_FromString(value)};
443+
}
444+
445+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const std::string& value) {
446+
PyObject* str_value = PyString_FromString(value.c_str());
447+
return {key, str_value};
448+
}
449+
450+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const std::vector<double>& value) {
451+
// PyObject* py_levels = PyList_New(value.size());
452+
// for (size_t i = 0; i < value.size(); ++i) {
453+
// PyList_SetItem(py_levels, i, PyFloat_FromDouble(value.at(i)));
454+
// }
455+
return {key, get_array(value)};
456+
}
457+
458+
std::pair<std::string, PyObject*> analyze_key_value(const char* key, const std::vector<int>& value) {
459+
PyObject* args = PyTuple_New(value.size());
460+
for(size_t i = 0; i < value.size(); ++i) {
461+
PyTuple_SetItem(args, i, PyLong_FromLong(value.at(i)));
462+
}
463+
return {key, get_array(value)};
464+
}
465+
466+
template<class Tuple, std::size_t N>
467+
struct AnalyzeKeywordsHelper {
468+
static void analyze_keywords(const Tuple& kw, PyObject* keywords) {
469+
AnalyzeKeywordsHelper<Tuple, N-2>::analyze_keywords(kw, keywords);
470+
std::string key;
471+
PyObject* value;
472+
std::tie(key, value) = analyze_key_value(std::get<N-2>(kw), std::get<N-1>(kw));
473+
PyDict_SetItemString(keywords, key.c_str(), value);
474+
Py_DECREF(value); // @TST
475+
}
476+
};
477+
478+
template<class Tuple>
479+
struct AnalyzeKeywordsHelper<Tuple, 2> {
480+
static void analyze_keywords(const Tuple& kw, PyObject* keywords) {
481+
std::string key;
482+
PyObject* value;
483+
std::tie(key, value) = analyze_key_value(std::get<0>(kw), std::get<1>(kw));
484+
PyDict_SetItemString(keywords, key.c_str(), value);
485+
Py_DECREF(value); // @TST
486+
}
487+
};
488+
489+
template<class... Args>
490+
PyObject* analyze_keywords(const std::tuple<Args...>& kw) {
491+
// Inutile de vérifier que la longueur du tuple est un multiple de 2,
492+
// car comme on avance de 2 en 2, l'instanciation du template ne peut pas se faire pour
493+
// un tuple qui n'a pas un nombre pair d'items.
494+
PyObject* keywords = PyDict_New();
495+
AnalyzeKeywordsHelper<decltype(kw), sizeof...(Args)>::analyze_keywords(kw, keywords);
496+
return keywords;
497+
}
498+
499+
// ----------
500+
// Identity type conversion to PyObject* (for functions `figure(Identity, tuple<Args...>&)` and `close(Identity)`)
501+
//
502+
PyObject* get_pyobject_from(const int value) {
503+
return PyLong_FromLong(value);
504+
}
505+
506+
PyObject* get_pyobject_from(const char* value) {
507+
return PyString_FromString(value);
508+
}
509+
510+
PyObject* get_pyobject_from(const std::string& value) {
511+
return PyString_FromString(value.c_str());
512+
}
513+
#endif // USE_VARIADIC_TEMPLATES_ARGS
514+
515+
420516
template<typename Numeric>
421517
bool plot(const std::vector<Numeric> &x, const std::vector<Numeric> &y, const std::map<std::string, std::string>& keywords)
422518
{
@@ -739,35 +835,57 @@ bool hist(const std::vector<Numeric>& y, long bins=10,std::string color="b",
739835
#endif // WITH_OPENCV
740836
#endif // WITHOUT_NUMPY
741837

838+
template<typename NumericX, typename NumericY>
839+
bool __scatter(const std::vector<NumericX>& x,
840+
const std::vector<NumericY>& y,
841+
PyObject* kwargs)
842+
{
843+
assert(x.size() == y.size());
844+
845+
PyObject* xarray = get_array(x);
846+
PyObject* yarray = get_array(y);
847+
848+
PyObject* args = PyTuple_New(2);
849+
PyTuple_SetItem(args, 0, xarray);
850+
PyTuple_SetItem(args, 1, yarray);
851+
852+
PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_scatter, args, kwargs);
853+
854+
Py_DECREF(args);
855+
Py_DECREF(kwargs);
856+
857+
if(!res) {
858+
throw std::runtime_error("Call to scatter(x, y [, marker]) failed.");
859+
}
860+
861+
Py_DECREF(res);
862+
863+
return res;
864+
}
865+
866+
// generic form
867+
template <typename Numeric, class... Args>
868+
inline bool scatter(const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::tuple<Args...>& keywords)
869+
{
870+
PyObject* kwargs = analyze_keywords(keywords);
871+
return __scatter(x, y, kwargs);
872+
}
873+
874+
// specialized form
742875
template<typename NumericX, typename NumericY>
743876
bool scatter(const std::vector<NumericX>& x,
744877
const std::vector<NumericY>& y,
745878
const double s=1.0, // The marker size in points**2
746879
const std::unordered_map<std::string, std::string> & keywords = {})
747880
{
748-
assert(x.size() == y.size());
749-
750-
PyObject* xarray = get_array(x);
751-
PyObject* yarray = get_array(y);
752-
753881
PyObject* kwargs = PyDict_New();
754882
PyDict_SetItemString(kwargs, "s", PyLong_FromLong(s));
755883
for (const auto& it : keywords)
756884
{
757885
PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str()));
758886
}
759887

760-
PyObject* plot_args = PyTuple_New(2);
761-
PyTuple_SetItem(plot_args, 0, xarray);
762-
PyTuple_SetItem(plot_args, 1, yarray);
763-
764-
PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_scatter, plot_args, kwargs);
765-
766-
Py_DECREF(plot_args);
767-
Py_DECREF(kwargs);
768-
if(res) Py_DECREF(res);
769-
770-
return res;
888+
return __scatter(x, y, kwargs);
771889
}
772890

773891
template <typename Numeric>
@@ -1246,6 +1364,36 @@ inline long figure(long number = -1)
12461364
return figureNumber;
12471365
}
12481366

1367+
template <typename Identity, class... Args>
1368+
inline Identity figure(Identity number, const std::tuple<Args...>& keywords)
1369+
{
1370+
// Identity est de type int ou string
1371+
PyObject* args = PyTuple_New(1);
1372+
PyTuple_SetItem(args, 0, get_pyobject_from(number));
1373+
1374+
PyObject* kwargs = analyze_keywords(keywords);
1375+
1376+
PyObject* res = nullptr;
1377+
res = PyObject_Call(detail::_interpreter::get().s_python_function_figure, args, kwargs);
1378+
1379+
Py_DECREF(args);
1380+
Py_DECREF(kwargs);
1381+
1382+
if(!res) {
1383+
PyErr_Print();
1384+
throw std::runtime_error("Call to pyplot.figure(num) failed.");
1385+
}
1386+
1387+
PyObject* num = PyObject_GetAttrString(res, "number");
1388+
if (!num) throw std::runtime_error("Could not get number attribute of figure object");
1389+
auto figureNumber = static_cast<Identity>(PyLong_AsLong(num));
1390+
1391+
Py_DECREF(num);
1392+
Py_DECREF(res);
1393+
1394+
return figureNumber;
1395+
}
1396+
12491397
inline bool fignum_exists(long number)
12501398
{
12511399
// Make sure interpreter is initialised
@@ -1660,6 +1808,23 @@ inline void close()
16601808
Py_DECREF(res);
16611809
}
16621810

1811+
template <typename Identity>
1812+
inline void close(Identity num)
1813+
{
1814+
PyObject* args = PyTuple_New(1);
1815+
PyTuple_SetItem(args, 0, get_pyobject_from(num));
1816+
1817+
PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_close, args);
1818+
1819+
Py_DECREF(args);
1820+
1821+
if (!res) {
1822+
throw std::runtime_error("Call to close(num) failed.");
1823+
}
1824+
1825+
Py_DECREF(res);
1826+
}
1827+
16631828
inline void xkcd() {
16641829
PyObject* res;
16651830
PyObject *kwargs = PyDict_New();

0 commit comments

Comments
 (0)