24#include <boost/preprocessor/list/for_each.hpp>
25#include <boost/preprocessor/tuple/to_list.hpp>
26#include <boost/python/class.hpp>
27#include <boost/python/converter/builtin_converters.hpp>
28#include <boost/python/dict.hpp>
29#include <boost/python/list.hpp>
30#include <boost/python/make_constructor.hpp>
31#include <boost/python/overloads.hpp>
38#define PY_ARRAY_UNIQUE_SYMBOL API_ARRAY_API
39#define NO_IMPORT_ARRAY
40#include <numpy/arrayobject.h>
54#define TO_LONG PyLong_AsLong
55#define STR_CHECK PyUnicode_Check
56#if NPY_API_VERSION < 0x0000000a
57#define IS_ARRAY_INTEGER(obj) (PyLong_Check(obj) || PyArray_IsScalar((obj), Integer))
59#define IS_ARRAY_INTEGER PyArray_IsIntegerScalar
63#define BUILTIN_TYPES BOOST_PP_TUPLE_TO_LIST(8, (double, std::string, int, size_t, uint32_t, int64_t, float, uint64_t))
64#define USER_TYPES BOOST_PP_TUPLE_TO_LIST(1, (Mantid::Kernel::V3D))
65#define ARRAY_TYPES BOOST_PP_TUPLE_TO_LIST(2, (std::vector<int>, std::vector<double>))
78 return to_python_value<const bool &>()(res);
81#define GET_BUILTIN(R, _, T) \
82 else if (typeID.hash_code() == typeid(T).hash_code()) { \
83 result = to_python_value<const T &>()(column->cell<T>(row)); \
85#define GET_USER(R, _, T) \
86 else if (typeID.hash_code() == typeid(T).hash_code()) { \
87 const converter::registration *entry = converter::registry::query(typeid(T)); \
89 throw std::invalid_argument("Cannot find converter from C++ type."); \
90 result = entry->to_python((const void *)&column->cell<T>(row)); \
92#define GET_ARRAY(R, _, T) \
93 else if (typeID.hash_code() == typeid(T).hash_code()) { \
94 result = Converters::Clone::apply<T::value_type>::create1D(column->cell<T>(row)); \
99 PyObject *result(
nullptr);
106 throw std::invalid_argument(
"Cannot convert C++ type to Python: " + column->type());
117void setValue(
const Column_sptr &column,
const int row,
const object &
value) {
118 const auto &typeID = column->get_type_info();
129 column->cell<
int>(row) =
static_cast<int>(
TO_LONG(
value.ptr()));
134#define SET_CELL(R, _, T) \
135 else if (typeID.hash_code() == typeid(T).hash_code()) { \
136 column->cell<T>(row) = extract<T>(value)(); \
138#define SET_VECTOR_CELL(R, _, T) \
139 else if (typeID.hash_code() == typeid(T).hash_code()) { \
140 if (!NDArray::check(value)) { \
141 column->cell<T>(row) = Converters::PySequenceToVector<T::value_type>(value)(); \
143 column->cell<T>(row) = Converters::NDArrayToVector<T::value_type>(value)(); \
155 throw std::invalid_argument(
"Cannot convert Python type to C++: " + column->type());
167bool addColumnPlotType(
ITableWorkspace &self,
const std::string &type,
const std::string &name,
int plottype) {
168 auto newColumn = self.
addColumn(type, name);
171 newColumn->setPlotType(plottype);
173 return newColumn !=
nullptr;
186bool addColumnSimple(
ITableWorkspace &self,
const std::string &type,
const std::string &name) {
187 return self.
addColumn(type, name) !=
nullptr;
200bool addReadOnlyColumn(
ITableWorkspace &self,
const std::string &type,
const std::string &name) {
201 auto newColumn = self.
addColumn(type, name);
202 newColumn->setReadOnly(
true);
216 colptr = self.
getColumn(extract<std::string>(column)());
218 colptr = self.
getColumn(extract<int>(column)());
221 return colptr->getPlotType();
232void setPlotType(
ITableWorkspace &self,
const object &column,
int ptype,
int linkedCol = -1) {
236 colptr = self.
getColumn(extract<std::string>(column)());
238 colptr = self.
getColumn(extract<int>(column)());
240 colptr->setPlotType(ptype);
241 if (linkedCol >= 0) {
242 colptr->setLinkedYCol(linkedCol);
251BOOST_PYTHON_FUNCTION_OVERLOADS(setPlotType_overloads, setPlotType, 3, 4)
265 colptr = self.getColumn(extract<std::string>(column)());
267 colptr = self.getColumn(extract<int>(column)());
270 return colptr->getLinkedYCol();
279void setLinkedYCol(
ITableWorkspace &self,
const object &errColumn,
const int dataColomn) {
283 colptr = self.
getColumn(extract<std::string>(errColumn)());
285 colptr = self.
getColumn(extract<int>(errColumn)());
288 colptr->setLinkedYCol(dataColomn);
305 const std::type_info &typeID = column->get_type_info();
306 const auto numRows =
static_cast<int>(column->size());
308 PyObject *result = PyList_New(numRows);
309 for (
int i = 0; i < numRows; i++) {
310 if (PyList_SetItem(result, i, getValue(column, typeID, i)))
311 throw std::runtime_error(
"Error while building list");
325 throw std::invalid_argument(
"Cannot specify negative row number");
326 if (row >=
static_cast<int>(self.
rowCount()))
327 throw std::invalid_argument(
"Cannot specify row larger than number of rows");
329 auto numCols =
static_cast<int>(self.
columnCount());
331 PyObject *result = PyDict_New();
333 for (
int columnIndex = 0; columnIndex < numCols; columnIndex++) {
335 const std::type_info &typeID = col->get_type_info();
337 if (PyDict_SetItemString(result, col->name().c_str(), getValue(col, typeID, row)))
338 throw std::runtime_error(
"Error while building dict");
350 auto numCols =
static_cast<int>(self.
columnCount());
352 boost::python::list types;
354 for (
int colIndex = 0; colIndex < numCols; colIndex++) {
355 const auto col = self.
getColumn(colIndex);
356 const auto &type = col->type();
373 auto nitems = boost::python::len(rowItems);
374 if (nitems !=
static_cast<decltype(nitems)
>(self.
columnCount())) {
375 throw std::invalid_argument(
"Number of values given does not match the number of columns. "
381 const auto rowIndex =
static_cast<int>(self.
rowCount());
391 for (
auto &iter : columns) {
393 value = rowItems[iter];
394 setValue(col, rowIndex,
value);
396 }
catch (error_already_set &) {
398 if (PyErr_ExceptionMatches(PyExc_KeyError)) {
399 std::ostringstream msg;
400 msg <<
"Missing key-value entry for column ";
401 msg <<
"<" << col->name() <<
">";
402 PyErr_SetString(PyExc_KeyError, msg.str().c_str());
406 if (PyErr_ExceptionMatches(PyExc_TypeError)) {
407 std::ostringstream msg;
408 msg <<
"Wrong datatype for column <" << col->name() <<
"> ";
409 msg <<
"(expected <" << col->type() <<
">)";
410 PyErr_SetString(PyExc_TypeError, msg.str().c_str());
428void addRowFromSequence(
ITableWorkspace &self,
const object &rowItems) {
430 auto nitems = boost::python::len(rowItems);
431 if (nitems !=
static_cast<decltype(nitems)
>(self.
columnCount())) {
432 throw std::invalid_argument(
"Number of values given does not match the number of columns. "
438 const auto rowIndex =
static_cast<int>(self.
rowCount());
442 for (
decltype(nitems) i = 0; i < nitems; ++i) {
444 auto value = rowItems[i];
447 setValue(col, rowIndex,
value);
448 }
catch (error_already_set &) {
450 if (PyErr_ExceptionMatches(PyExc_TypeError)) {
451 std::ostringstream msg;
452 msg <<
"Wrong datatype for column <" << col->name() <<
"> ";
453 msg <<
"(expected <" << col->type() <<
">)";
454 PyErr_SetString(PyExc_TypeError, msg.str().c_str());
477 column = self.
getColumn(extract<std::string>(col_or_row)());
478 rowIndex = row_or_col;
480 rowIndex = extract<int>(col_or_row)();
497 getCellLoc(self,
value, row_or_col, col, rowIndex);
498 const std::type_info &typeID = col->get_type_info();
499 return getValue(col, typeID, rowIndex);
510void setCell(
ITableWorkspace &self,
const object &col_or_row,
const int row_or_col,
const object &
value,
511 const bool ¬ify_replace) {
514 getCellLoc(self, col_or_row, row_or_col, col, rowIndex);
515 setValue(col, rowIndex,
value);
517 if (notify_replace) {
532 colptr = self.
getColumn(extract<std::string>(column)());
534 colptr = self.
getColumn(extract<int>(column)());
536 return colptr->getReadOnly();
545void setColumnReadOnly(
ITableWorkspace &self,
const object &column,
const bool readOnly) {
549 colptr = self.
getColumn(extract<std::string>(column)());
551 colptr = self.
getColumn(extract<int>(column)());
553 colptr->setReadOnly(readOnly);
570 handle<> handle(column(self,
object(name)));
571 object values(handle);
572 result[name] = values;
582 data[
"data"] =
toDict(ws);
602 for (
const auto &name : names) {
604 columnNames.append(name);
605 columnTypes.append(column->type());
609 metaData[
"column_names"] = columnNames;
610 metaData[
"column_types"] = columnTypes;
623 const auto &metaData = state[
"meta_data"];
624 const auto &columnNames = metaData[
"column_names"];
625 const auto &columnTypes = metaData[
"column_types"];
627 auto numColumns = len(columnNames);
628 for (
decltype(numColumns) colIndex = 0; colIndex < numColumns; ++colIndex) {
629 const auto &key = columnNames[colIndex];
630 const auto &
value = columnTypes[colIndex];
631 const auto &name = extract<std::string>(key);
632 const auto &type = extract<std::string>(
value);
643 const auto &data = state[
"data"];
650 auto numRows = len(data[names[0]]);
651 for (
int rowIndex = 0; rowIndex < numRows; ++rowIndex) {
653 for (
const auto &name : names) {
654 setValue(ws.
getColumn(name),
static_cast<int>(rowIndex), data[name][rowIndex]);
663 std::string iTableWorkspace_docstring =
"Most of the information from a table workspace is returned ";
664 iTableWorkspace_docstring +=
"as native copies. All of the column accessors return lists while the ";
665 iTableWorkspace_docstring +=
"rows return dicts. This object does support the idom 'for row in ";
666 iTableWorkspace_docstring +=
"ITableWorkspace'.";
668 class_<ITableWorkspace, bases<Workspace>, boost::noncopyable>(
"ITableWorkspace", iTableWorkspace_docstring.c_str(),
671 .def(
"addColumn", &addColumnSimple, (arg(
"self"), arg(
"type"), arg(
"name")),
672 "Add a named column with the given type. Recognized types are: "
673 "int,float,double,bool,str,V3D,long64")
675 .def(
"addColumn", &addColumnPlotType, (arg(
"self"), arg(
"type"), arg(
"name"), arg(
"plottype")),
676 "Add a named column with the given datatype "
677 "(int,float,double,bool,str,V3D,long64) "
679 "(0 = None, 1 = X, 2 = Y, 3 = Z, 4 = xErr, 5 = yErr, 6 = Label).")
681 .def(
"addReadOnlyColumn", &addReadOnlyColumn, (arg(
"self"), arg(
"type"), arg(
"name")),
682 "Add a read-only, named column with the given type. Recognized types are: "
683 "int,float,double,bool,str,V3D,long64")
685 .def(
"getPlotType", &getPlotType, (arg(
"self"), arg(
"column")),
686 "Get the plot type of given column as an integer. "
687 "Accepts column name or index. \nPossible return values: "
688 "(0 = None, 1 = X, 2 = Y, 3 = Z, 4 = xErr, 5 = yErr, 6 = Label).")
690 .def(
"setPlotType", setPlotType,
691 setPlotType_overloads((arg(
"self"), arg(
"column"), arg(
"ptype"), arg(
"linkedCol") = -1),
692 "Set the plot type of given column. "
693 "Accepts column name or index. \nPossible type values: "
694 "(0 = None, 1 = X, 2 = Y, 3 = Z, 4 = xErr, 5 = yErr, 6 = "
697 .def(
"getLinkedYCol", &getLinkedYCol, (arg(
"self"), arg(
"column")),
698 "Get the data column associated with a given error column. ")
700 .def(
"setLinkedYCol", &setLinkedYCol, (arg(
"self"), arg(
"errColumn"), arg(
"dataColumn")),
701 "Set the data column associated with a given error column. ")
710 "Resize the table to contain count rows.")
715 boost::python::return_value_policy<VectorToNumpy>(),
"Return a list of the column names.")
718 "Return a list of the column names.")
720 .def(
"column", &column, (arg(
"self"), arg(
"column")),
"Return all values of a specific column as a list.")
722 .def(
"row", &row, (arg(
"self"), arg(
"row")),
"Return all values of a specific row as a dict.")
724 .def(
"columnTypes", &columnTypes, arg(
"self"),
"Return the types of the columns as a list")
728 .def(
"addRow", &addRowFromSequence, (arg(
"self"), arg(
"row_items_seq")),
729 "Appends a row with the values from the given sequence. "
730 "It it assumed that the items are in the correct order for the "
733 .def(
"addRow", &addRowFromDict, (arg(
"self"), arg(
"row_items_dict")),
734 "Appends a row with the values from the dictionary.")
736 .def(
"cell", &cell, (arg(
"self"), arg(
"value"), arg(
"row_or_column")),
737 "Return the value in the given cell. If the value argument is a "
738 "number then it is interpreted as a row otherwise it "
739 "is interpreted as a column name.")
741 .def(
"setCell", &setCell,
742 (arg(
"self"), arg(
"row_or_column"), arg(
"column_or_row"), arg(
"value"), arg(
"notify_replace") =
true),
743 "Sets the value of a given cell. If the row_or_column argument is a "
744 "number then it is interpreted as a row otherwise it "
745 "is interpreted as a column name. If notify replace is false, then "
746 "the replace workspace event is not triggered.")
748 .def(
"toDict", &
toDict, (arg(
"self")),
749 "Gets the values of this workspace as a dictionary. The keys of the "
750 "dictionary will be the names of the columns of the table. The "
751 "values of the entries will be lists of values for each column.")
753 .def(
"setColumnReadOnly", &setColumnReadOnly, (arg(
"self"), arg(
"column"), arg(
"read_only")),
754 "Sets whether or not a given column of this workspace should be read-only. Columns can be "
755 "selected by name or by index")
757 .def(
"isColumnReadOnly", &isColumnReadOnly, (arg(
"self"), arg(
"column")),
758 "Gets whether or not a given column of this workspace is be read-only. Columns can be "
759 "selected by name or by index");
double value
The value of the point.
#define GET_POINTER_SPECIALIZATION(TYPE)
void export_ITableWorkspace()
#define SET_VECTOR_CELL(R, _, T)
#define GET_BUILTIN(R, _, T)
#define GET_ARRAY(R, _, T)
dict toDict(const ITableWorkspace &self)
Get the contents of the workspace as a python dictionary.
#define GET_USER(R, _, T)
#define SET_CELL(R, _, T)
#define BUILTIN_TYPES
Boost macro for "looping" over builtin types.
#define IS_ARRAY_INTEGER(obj)
#define GNU_DIAG_OFF(x)
This is a collection of macros for turning compiler warnings off in a controlled manner.
static void readMetaData(ITableWorkspace &ws, const dict &state)
Read the meta data from a python dict into the table workspace.
static void setstate(ITableWorkspace &ws, const dict &state)
static dict writeMetaData(const ITableWorkspace &ws)
Write the meta data from a table workspace to a python dict.
static dict getstate(const ITableWorkspace &ws)
static void readData(ITableWorkspace &ws, const dict &state)
Read the data from a python dict into the table workspace.
ITableWorkspace is an implementation of Workspace in which the data are organised in columns of same ...
virtual void removeColumn(const std::string &name)=0
Removes a column.
void modified()
If the workspace is the AnalysisDataService sends AfterReplaceNotification.
virtual void setRowCount(size_t count)=0
Resizes the workspace.
virtual Column_sptr getColumn(const std::string &name)=0
Gets the shared pointer to a column by name.
TableRowHelper appendRow()
Appends a row.
virtual void removeRow(size_t index)=0
Delets a row if it exists.
virtual Column_sptr addColumn(const std::string &type, const std::string &name)=0
Creates a new column.
virtual size_t columnCount() const =0
Number of columns in the workspace.
virtual std::vector< std::string > getColumnNames() const =0
Returns a vector of all column names.
virtual size_t rowCount() const =0
Number of rows in the workspace.
Thin object wrapper around a numpy array.
std::shared_ptr< Column > Column_sptr
std::shared_ptr< const Column > Column_const_sptr
std::string to_string(const wide_integer< Bits, Signed > &n)
As TableColumn stores its data in a std::vector bool type cannot be used in the same way as the other...
Implements a return value policy that returns a numpy array from a function returning a std::vector b...
Encapsulates the registration required for an interface type T that sits on top of a Kernel::DataItem...