Mpl.cpp 8.66 KB
Newer Older
Locatelli's avatar
Locatelli committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
 * Nomad Instrument Control Software
 *
 * Copyright 2011 Institut Laue-Langevin
 *
 * Licensed under the EUPL, Version 1.1 only (the "License");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 *
 * http://joinup.ec.europa.eu/software/page/eupl
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and
 * limitations under the Licence.
 */

#include "Mpl.h"

#include <stdexcept>
#include <iostream>
#include <sstream>

#include <boost/python/call_method.hpp>
#include <boost/python/list.hpp>
#include <boost/python/import.hpp>
#include <boost/python/str.hpp>
#include <python3.5/Python.h>
30
31
32
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#define PY_ARRAY_UNIQUE_SYMBOL CORE_ARRAY_API
#include <numpy/arrayobject.h>
Locatelli's avatar
Locatelli committed
33

Locatelli's avatar
Changes    
Locatelli committed
34
#include "MplLock.h"
Locatelli's avatar
Locatelli committed
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "Trace.h"

using namespace std;
namespace bp = boost::python;

namespace view {
namespace mpl {

PyMODINIT_FUNC PyInit_module_event(void); // Definition of the PyInit of the own module event

/*
 * constructor
 */
Mpl::Mpl() throw (Error) {

Locatelli's avatar
Locatelli committed
50
51
52
53
54
55
	// Add single module event need to get gui event
	if (PyImport_AppendInittab("module_event", PyInit_module_event) < 0) {
		throw Error("Mpl", "constructor",
				"Add a single module to the existing table of built-in modules failed for module_event");
	}

Locatelli's avatar
Locatelli committed
56
57
58
59
60
61
	// Initialize the Python interpreter
	Py_Initialize();

	// Initialize and acquire the global interpreter lock for multi-thread management
	PyEval_InitThreads();

62
    // Import the matplotlib module
63
	PythonObject matplotlib;
Locatelli's avatar
Locatelli committed
64
65
66
67
68
69
70
71
72
73
74
75
76
	try {
		matplotlib = bp::import(bp::str("matplotlib"));
	} catch(...) {
		throw Error("Mpl", "constructor", "Error when importing the module matplotlib");
	}

	// Import the pyplot of matplotlib module
	try {
		m_PyPlot = bp::import(bp::str("matplotlib.pyplot"));
	} catch(...) {
		throw Error("Mpl", "constructor", "Error when importing the module matplotlib.pyplot");
	}

77
78
79
80
81
82
	try {
		m_FigureModule = bp::import(bp::str("matplotlib.figure"));
	} catch (...) {
		throw Error("Mpl", "constructor", "Error when getting the figure module");
	}

Locatelli's avatar
Changes    
Locatelli committed
83
84
85
86
	try {
		m_LinesModule = bp::import(bp::str("matplotlib.lines"));
	} catch (...) {
		throw Error("Mpl", "constructor", "Error when getting the Lines module");
Locatelli's avatar
Locatelli committed
87

Locatelli's avatar
Changes    
Locatelli committed
88
89
90
91
92
93
94
	}
	try {
		m_TextModule = bp::import(bp::str("matplotlib.text"));
	} catch (...) {
		throw Error("Mpl", "constructor", "Error when getting the Text module");
	}

Locatelli's avatar
Locatelli committed
95
96
97
98
99
100
101
102
103
104
105
106
	try {
		m_WidgetsModule = bp::import(bp::str("matplotlib.widgets"));
	} catch (...) {
		throw Error("Mpl", "constructor", "Error when getting the widgets module");
	}

	try {
		m_PatchesModule = bp::import(bp::str("matplotlib.patches"));
	} catch (...) {
		throw Error("Mpl", "constructor", "Error when getting the patches module");
	}

Locatelli's avatar
Changes    
Locatelli committed
107
	try {
108
		PythonObject widgetsModule = bp::import(bp::str("matplotlib.colors"));
Locatelli's avatar
Changes    
Locatelli committed
109
110
111
112
113
114
		m_NormalNorm = widgetsModule.attr("Normalize")();
		m_LogNorm = widgetsModule.attr("LogNorm")();
	} catch (...) {
		throw Error("Mpl", "contructor", "Error when creating the log norm object");
	}

Locatelli's avatar
Locatelli committed
115
116
117
118
119
120
121
	// Use Qt5Agg for toolbar look and feel
	auto args = bp::make_tuple("Qt5Agg");
	try {
		matplotlib.attr("use")(*args);
	} catch(...) {
		throw Error("Mpl", "constructor", "Error when activating QT5Agg");
	}
Locatelli's avatar
Changes    
Locatelli committed
122

123
	// Get Py QT5 for forcine QT5
Locatelli's avatar
Locatelli committed
124
	try {
125
		auto qt5Module = bp::import(bp::str("PyQt5"));
Locatelli's avatar
Locatelli committed
126
	} catch (...) {
127
		throw Error("Mpl", "Mpl", "Error when getting PyQt5");
Locatelli's avatar
Locatelli committed
128
129
	}

130
	// Get matplotlib BackEnd for qt5
Locatelli's avatar
Locatelli committed
131
	try {
132
		m_BackendsModule = bp::import(bp::str("matplotlib.backends.backend_qt5agg"));
Locatelli's avatar
Locatelli committed
133
	} catch (...) {
134
135
136
		throw Error("Mpl", "Mpl", "Error when getting Qt5 AGG backend");
	}

137
138
139
140
141
142
143
	// Get
	try {
		m_GarbageCollector = bp::import(bp::str("gc"));
	} catch (...) {
		throw Error("Mpl", "Mpl", "Error when getting gc");
	}

144
145
	if (_import_array() < 0) {
		throw Error("Mpl", "Mpl", "numpy.core.multiarray failed to import");
Locatelli's avatar
Locatelli committed
146
147
148
	}
}

Locatelli's avatar
Locatelli committed
149
/*
150
 * destructor
Locatelli's avatar
Locatelli committed
151
 */
152
153
Mpl::~Mpl() {
	{
154
155
156
157
158
159
160
161
162
163
		m_PyPlot.reset();
		m_FigureModule.reset();
		m_LinesModule.reset();
		m_TextModule.reset();
		m_NormalNorm.reset();
		m_LogNorm.reset();
		m_BackendsModule.reset();
		m_WidgetsModule.reset();
		m_PatchesModule.reset();
		m_GarbageCollector.reset();
Locatelli's avatar
Locatelli committed
164
	}
165
	Py_Finalize();
Locatelli's avatar
Locatelli committed
166
167
}

Locatelli's avatar
Locatelli committed
168
/*
169
 * close
Locatelli's avatar
Locatelli committed
170
 */
171
void Mpl::close(const PythonObject& figure) throw (Error) {
Locatelli's avatar
Locatelli committed
172
	MplLock lock;
173
174

	// Create matplotlib figure object
Locatelli's avatar
Locatelli committed
175
	auto args = bp::make_tuple(figure());
Locatelli's avatar
Locatelli committed
176
	try {
177
		m_PyPlot.attr("close")(*args);
Locatelli's avatar
Locatelli committed
178
	} catch (...) {
179
		throw Error("Mpl", "close", "Error when executing close method");
Locatelli's avatar
Locatelli committed
180
181
182
	}
}

183
184
185
186
187
188
189
190
191
192
193
/*
 * garbageCollect
 */
void Mpl::garbageCollect() throw (Error) {
	try {
		m_GarbageCollector.attr("collect")();
	} catch (...) {
		throw Error("Mpl", "garbageCollect", "Error when executing collect method on garbage collector");
	}
}

Locatelli's avatar
Locatelli committed
194
195
196
/*
 * xlabel
 */
197
void Mpl::xlabel(const PythonObject& axis, const std::string& name) throw (Error) {
Locatelli's avatar
Locatelli committed
198
	mpl::MplLock lock;
Locatelli's avatar
Locatelli committed
199
	try {
200
		bp::call_method<void, const char *>(axis().ptr(), "set_xlabel", name.c_str());
Locatelli's avatar
Locatelli committed
201
202
203
204
205
206
207
208
	} catch (...) {
		throw Error("Mpl", "xlabel", "Error when executing xlabel method");
	}
}

/*
 * ylabel
 */
209
void Mpl::ylabel(const PythonObject& axis, const std::string& name) throw (Error) {
Locatelli's avatar
Locatelli committed
210
	mpl::MplLock lock;
Locatelli's avatar
Locatelli committed
211
	try {
212
		bp::call_method<void, const char *>(axis().ptr(), "set_ylabel", name.c_str());
Locatelli's avatar
Locatelli committed
213
214
215
216
217
218
219
220
	} catch (...) {
		throw Error("Mpl", "ylabel", "Error when executing ylabel method");
	}
}

/*
 * grid
 */
221
void Mpl::grid(const PythonObject& axis, bool show) throw (Error) {
Locatelli's avatar
Locatelli committed
222
	mpl::MplLock lock;
Locatelli's avatar
Locatelli committed
223
224
225
226
227
228
229
230
231
232
233
234
235
236
	bp::tuple args;
	if (show == true) {
		args = bp::make_tuple(true);
	} else {
		args = bp::make_tuple(false);
	}
	try {
		axis.attr("grid")(*args);
	}
	catch(...) {
		throw Error("Mpl", "grid", "Error when executing grid method");
	}
}

Locatelli's avatar
Locatelli committed
237
238
239
/*
 * savefig
 */
240
void Mpl::savefig(const PythonObject& figure, const std::string& filename, uint32 dpi, float64 pad) throw (Error) {
Locatelli's avatar
Locatelli committed
241
	mpl::MplLock lock;
Locatelli's avatar
Locatelli committed
242
243
244
	auto args = bp::make_tuple(filename);
	bp::dict kwargs;
	kwargs["format"] = "png";
245
	kwargs["dpi"] = dpi;
246
247
248
249
	if (pad == 0) {
		kwargs["bbox_inches"] = "tight";
		kwargs["pad_inches"] = pad;
	}
Locatelli's avatar
Locatelli committed
250
251
252
253
254
255
256
	try {
		figure.attr("savefig")(*args, **kwargs);
	} catch (...) {
		throw Error("Mpl", "savefig", "Error saving png file ", filename);
	}
}

Locatelli's avatar
Locatelli committed
257
258
259
/*
 * get_xlim
 */
260
std::pair<float64, float64> Mpl::get_xlim(const PythonObject& axis) throw (Error) {
Locatelli's avatar
Locatelli committed
261
	mpl::MplLock lock;
Locatelli's avatar
Locatelli committed
262
263
264
265
266
267
268
269
270
271
272
273
274
	try {
		bp::tuple res(axis.attr("get_xlim")());
		float64 min = bp::extract<float64>(res[0]);
		float64 max = bp::extract<float64>(res[1]);
		return std::make_pair(min, max);
	} catch (...) {
		throw Error("Mpl", "get_xlim", "Error when executing the get_xlim method");
	}
}

/*
 * get_ylim
 */
275
std::pair<float64, float64> Mpl::get_ylim(const PythonObject& axis) throw (Error) {
Locatelli's avatar
Locatelli committed
276
	mpl::MplLock lock;
Locatelli's avatar
Locatelli committed
277
	try {
278
		bp::tuple res(axis.attr("get_ylim")());
Locatelli's avatar
Locatelli committed
279
280
281
282
283
284
285
286
		float64 min = bp::extract<float64>(res[0]);
		float64 max = bp::extract<float64>(res[1]);
		return std::make_pair(min, max);
	} catch (...) {
		throw Error("Mpl", "get_ylim", "Error when executing the get_ylim method");
	}
}

Locatelli's avatar
Locatelli committed
287
288
289
/*
 * set_xdata
 */
290
void Mpl::set_xdata(const PythonObject& line, const std::vector<float64> &x) throw (Error) {
Locatelli's avatar
Locatelli committed
291
	mpl::MplLock lock;
Locatelli's avatar
Locatelli committed
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
	// using numpy arrays
	boost::python::list xarray;
	for (float64 val : x) {
		xarray.append(val);
	}

	// construct positional args
	auto args = bp::make_tuple(xarray);

	// construct keyword args
	try {
		line.attr("set_xdata")(*args);
	} catch (...) {
		throw Error("Mpl", "set_xdata", "Error when executing the set_xdata method");
	}
}

/*
 * set_ydata
 */
312
void Mpl::set_ydata(const PythonObject& line, const std::vector<float64> &y) throw (Error) {
Locatelli's avatar
Locatelli committed
313
	mpl::MplLock lock;
Locatelli's avatar
Locatelli committed
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
	// using numpy arrays
	boost::python::list yarray;
	for (float64 val : y) {
		yarray.append(val);
	}

	// construct positional args
	auto args = bp::make_tuple(yarray);

	try {
		line.attr("set_ydata")(*args);
	} catch (...) {
		throw Error("Mpl", "set_ydata", "Error when executing the set_ydata method");
	}
}

/*
 * set_xlim
 */
333
void Mpl::set_xlim(const PythonObject& axis, float64 min, float64 max, float64 marge) throw (Error) {
Locatelli's avatar
Locatelli committed
334
	mpl::MplLock lock;
Locatelli's avatar
Locatelli committed
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
	float64 tot = max - min;
	min -= tot * marge;
	max += tot * marge;
	auto args = bp::make_tuple(min, max);

	try {
		axis.attr("set_xlim")(*args);
	} catch (...) {
		throw Error("Mpl", "set_xlim", "Error when executing the set_xlim method");
	}
}

/*
 * set_ylim
 */
350
void Mpl::set_ylim(const PythonObject& axis, float64 min, float64 max, float64 marge) throw (Error) {
Locatelli's avatar
Locatelli committed
351
	mpl::MplLock lock;
Locatelli's avatar
Locatelli committed
352
353
354
355
356
357
358
359
360
361
362
363
	float64 tot = max - min;
	min -= tot * marge;
	max += tot * marge;
	auto args = bp::make_tuple(min, max);

	try {
		axis.attr("set_ylim")(*args);
	} catch (...) {
		throw Error("Mpl", "set_ylim", "Error when executing the set_ylim method");
	}
}

Locatelli's avatar
Locatelli committed
364
365
}
}