diff --git a/.gitignore b/.gitignore index 2072e27a74f7..43c7c5ef6bda 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,5 @@ lib/matplotlib/mpl-data/matplotlibrc ################################# doc/examples doc/_templates/gallery.html +doc/users/installing.rst doc/_static/matplotlibrc diff --git a/CHANGELOG b/CHANGELOG index b1c9694d7e25..a71d84863ad2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +2011-04-01 The plot directive Sphinx extension now supports all of the + features in the Numpy fork of that extension. These + include doctest formatting, an 'include-source' option, and + a number of new configuration options. - MGD + 2011-03-29 Wrapped ViewVCCachedServer definition in a factory function. This class now inherits from urllib2.HTTPSHandler in order to fetch data from github, but HTTPSHandler is not defined @@ -12,10 +17,43 @@ not responding to the c kwarg. The symbols have no face area, so only the edgecolor is visible. - EF +2011-02-27 Support libpng version 1.5.x; suggestion by Michael + Albert. Changed installation specification to a + minimum of libpng version 1.2. - EF + +2011-02-20 clabel accepts a callable as an fmt kwarg; modified + patch by Daniel Hyams. - EF + +2011-02-18 scatter([], []) is now valid. Also fixed issues + with empty collections - BVR + 2011-02-07 Quick workaround for dviread bug #3175113 - JKS +2011-02-05 Add cbook memory monitoring for Windows, using + tasklist. - EF + +2011-02-05 Speed up Normalize and LogNorm by using in-place + operations and by using float32 for float32 inputs + and for ints of 2 bytes or shorter; based on + patch by Christoph Gohlke. - EF + +2011-02-04 Changed imshow to use rgba as uint8 from start to + finish, instead of going through an intermediate + step as double precision; thanks to Christoph Gohlke. - EF + +2011-01-13 Added zdir and offset arguments to contourf3d to + bring contourf3d in feature parity with contour3d. - BVR + 2011-01-04 Tag 1.0.1 for release at r8896 +2011-01-03 Added display of ticker offset to 3d plots. - BVR + +2011-01-03 Turn off tick labeling on interior subplots for + pyplots.subplots when sharex/sharey is True. - JDH + +2010-12-29 Implement axes_divider.HBox and VBox. -JJL + + 2010-11-22 Fixed error with Hammer projection. - BVR 2010-11-12 Fixed the placement and angle of axis labels in 3D plots. - BVR @@ -24,6 +62,18 @@ allow bypassing the download mechanism in get_sample_data. - JKS +2010-10-04 Fix JPEG saving bug: only accept the kwargs documented + by PIL for JPEG files. - JKS + +2010-09-15 Remove unused _wxagg extension and numerix.h. - EF + +2010-08-25 Add new framework for doing animations with examples.- RM + +2010-08-21 Remove unused and inappropriate methods from Tick classes: + set_view_interval, get_minpos, and get_data_interval are + properly found in the Axis class and don't need to be + duplicated in XTick and YTick. - EF + 2010-08-21 Change Axis.set_view_interval() so that when updating an existing interval, it respects the orientation of that interval, and can enlarge but not reduce the interval. @@ -38,6 +88,23 @@ 2010-08-14 Fix bug in patch alpha handling, and in bar color kwarg - EF +2010-08-12 Removed all traces of numerix module after 17 months of + deprecation warnings. - EF + +2010-08-05 Added keyword arguments 'thetaunits' and 'runits' for polar + plots. Fixed PolarAxes so that when it set default + Formatters, it marked them as such. Fixed semilogx and + semilogy to no longer blindly reset the ticker information + on the non-log axis. Axes.arrow can now accept unitized + data. - JRE + +2010-08-03 Add support for MPLSETUPCFG variable for custom setup.cfg + filename. Used by sage buildbot to build an mpl w/ no gui + support - JDH + +2010-08-01 Create directory specified by MPLCONFIGDIR if it does + not exist. - ADS + 2010-07-20 Return Qt4's default cursor when leaving the canvas - DSD 2010-07-06 Tagging for mpl 1.0 at r8502 diff --git a/CXX5/Config.hxx b/CXX5/Config.hxx deleted file mode 100644 index 2e3c70e23aa4..000000000000 --- a/CXX5/Config.hxx +++ /dev/null @@ -1,134 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (c) 1998 - 2007, The Regents of the University of California -// Produced at the Lawrence Livermore National Laboratory -// All rights reserved. -// -// This file is part of PyCXX. For details,see http://cxx.sourceforge.net/. The -// full copyright notice is contained in the file COPYRIGHT located at the root -// of the PyCXX distribution. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the disclaimer below. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the disclaimer (as noted below) in the -// documentation and/or materials provided with the distribution. -// - Neither the name of the UC/LLNL nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF -// CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. -// -//----------------------------------------------------------------------------- - -#ifndef __PyCXX_config_hh__ -#define __PyCXX_config_hh__ - -// -// Microsoft VC++ 6.0 has no traits -// -#if defined( _MSC_VER ) - -# define STANDARD_LIBRARY_HAS_ITERATOR_TRAITS 1 - -#elif defined( __GNUC__ ) -# if __GNUC__ >= 3 -# define STANDARD_LIBRARY_HAS_ITERATOR_TRAITS 1 -# else -# define STANDARD_LIBRARY_HAS_ITERATOR_TRAITS 0 -#endif - -// -// Assume all other compilers do -// -#else - -// Macros to deal with deficiencies in compilers -# define STANDARD_LIBRARY_HAS_ITERATOR_TRAITS 1 -#endif - -#if STANDARD_LIBRARY_HAS_ITERATOR_TRAITS -# define random_access_iterator_parent(itemtype) std::iterator -#else -# define random_access_iterator_parent(itemtype) std::random_access_iterator -#endif - -// -// Which C++ standard is in use? -// -#if defined( _MSC_VER ) -# if _MSC_VER <= 1200 -// MSVC++ 6.0 -# define PYCXX_ISO_CPP_LIB 0 -# define STR_STREAM -# define TEMPLATE_TYPENAME class -# else -# define PYCXX_ISO_CPP_LIB 1 -# define STR_STREAM -# define TEMPLATE_TYPENAME typename -# endif -#elif defined( __GNUC__ ) -# if __GNUC__ >= 3 -# define PYCXX_ISO_CPP_LIB 1 -# define STR_STREAM -# define TEMPLATE_TYPENAME typename -# else -# define PYCXX_ISO_CPP_LIB 0 -# define STR_STREAM -# define TEMPLATE_TYPENAME class -# endif -#endif - -#if PYCXX_ISO_CPP_LIB -# define STR_STREAM -# define OSTRSTREAM ostringstream -# define EXPLICIT_TYPENAME typename -# define EXPLICIT_CLASS class -# define TEMPLATE_TYPENAME typename -#else -# define STR_STREAM -# define OSTRSTREAM ostrstream -# define EXPLICIT_TYPENAME -# define EXPLICIT_CLASS -# define TEMPLATE_TYPENAME class -#endif - -// before 2.5 Py_ssize_t was missing -#ifndef PY_MAJOR_VERSION -#error not defined PY_MAJOR_VERSION -#endif -#if PY_MAJOR_VERSION < 2 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 5) -typedef int Py_ssize_t; -#endif - -// hash_map container usage selection -// 1) if PYCXX_USING_STD_MAP is defined PyCXX will be using std::map<> container -// implementation only. -// 2) if compilers are used other than MS Visual Studio (7.1+) or GCC 3.x -// STANDARD_LIBRARY_HAS_HASH_MAP must be defined before compilation to -// make PyCXX using hash_map container. -#if !defined( PYCXX_USING_STD_MAP ) - #if defined( _MSC_VER ) || defined( __INTEL_COMPILER ) || defined ( __ICC ) || (defined( __GNUC__ ) && ( __GNUC__ > 3 )) - # define PYCXX_USING_HASH_MAP - #else - # if defined( STANDARD_LIBRARY_HAS_HASH_MAP ) && !defined( PYCXX_USING_HASH_MAP ) - # define PYCXX_USING_HASH_MAP - # endif - #endif -#endif - -#endif // __PyCXX_config_hh__ diff --git a/CXX5/Exception.hxx b/CXX5/Exception.hxx deleted file mode 100644 index 1ef087cac9fe..000000000000 --- a/CXX5/Exception.hxx +++ /dev/null @@ -1,249 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (c) 1998 - 2007, The Regents of the University of California -// Produced at the Lawrence Livermore National Laboratory -// All rights reserved. -// -// This file is part of PyCXX. For details,see http://cxx.sourceforge.net/. The -// full copyright notice is contained in the file COPYRIGHT located at the root -// of the PyCXX distribution. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the disclaimer below. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the disclaimer (as noted below) in the -// documentation and/or materials provided with the distribution. -// - Neither the name of the UC/LLNL nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF -// CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. -// -//----------------------------------------------------------------------------- - -#ifndef __CXX_Exception_h -#define __CXX_Exception_h - -#include "CXX/WrapPython.h" -#include "CXX/Version.hxx" -#include "CXX/Config.hxx" -#include "CXX/IndirectPythonInterface.hxx" - -#include -#include - -// This mimics the Python structure, in order to minimize confusion -namespace Py -{ - class ExtensionExceptionType; - - class Object; - - class Exception - { - public: - Exception( ExtensionExceptionType &exception, const std::string& reason ); - Exception( ExtensionExceptionType &exception, Object &reason ); - - explicit Exception () - {} - - Exception (const std::string& reason) - { - PyErr_SetString (Py::_Exc_RuntimeError(), reason.c_str()); - } - - Exception (PyObject* exception, const std::string& reason) - { - PyErr_SetString (exception, reason.c_str()); - } - - Exception (PyObject* exception, Object &reason); - - void clear() // clear the error - // technically but not philosophically const - { - PyErr_Clear(); - } - }; - - - // Abstract - class StandardError: public Exception - { - protected: - explicit StandardError() - {} - }; - - class LookupError: public StandardError - { - protected: - explicit LookupError() - {} - }; - - class ArithmeticError: public StandardError - { - protected: - explicit ArithmeticError() - {} - }; - - class EnvironmentError: public StandardError - { - protected: - explicit EnvironmentError() - {} - }; - - // Concrete - - class TypeError: public StandardError - { - public: - TypeError (const std::string& reason) - : StandardError() - { - PyErr_SetString (Py::_Exc_TypeError(),reason.c_str()); - } - }; - - class IndexError: public LookupError - { - public: - IndexError (const std::string& reason) - : LookupError() - { - PyErr_SetString (Py::_Exc_IndexError(), reason.c_str()); - } - }; - - class AttributeError: public StandardError - { - public: - AttributeError (const std::string& reason) - : StandardError() - { - PyErr_SetString (Py::_Exc_AttributeError(), reason.c_str()); - } - }; - - class NameError: public StandardError - { - public: - NameError (const std::string& reason) - : StandardError() - { - PyErr_SetString (Py::_Exc_NameError(), reason.c_str()); - } - }; - - class RuntimeError: public StandardError - { - public: - RuntimeError (const std::string& reason) - : StandardError() - { - PyErr_SetString (Py::_Exc_RuntimeError(), reason.c_str()); - } - }; - - class SystemError: public StandardError - { - public: - SystemError (const std::string& reason) - : StandardError() - { - PyErr_SetString (Py::_Exc_SystemError(),reason.c_str()); - } - }; - - class KeyError: public LookupError - { - public: - KeyError (const std::string& reason) - : LookupError() - { - PyErr_SetString (Py::_Exc_KeyError(),reason.c_str()); - } - }; - - - class ValueError: public StandardError - { - public: - ValueError (const std::string& reason) - : StandardError() - { - PyErr_SetString (Py::_Exc_ValueError(), reason.c_str()); - } - }; - - class OverflowError: public ArithmeticError - { - public: - OverflowError (const std::string& reason) - : ArithmeticError() - { - PyErr_SetString (Py::_Exc_OverflowError(), reason.c_str()); - } - }; - - class ZeroDivisionError: public ArithmeticError - { - public: - ZeroDivisionError (const std::string& reason) - : ArithmeticError() - { - PyErr_SetString (Py::_Exc_ZeroDivisionError(), reason.c_str()); - } - }; - - class FloatingPointError: public ArithmeticError - { - public: - FloatingPointError (const std::string& reason) - : ArithmeticError() - { - PyErr_SetString (Py::_Exc_FloatingPointError(), reason.c_str()); - } - }; - - class MemoryError: public StandardError - { - public: - MemoryError (const std::string& reason) - : StandardError() - { - PyErr_SetString (Py::_Exc_MemoryError(), reason.c_str()); - } - }; - - class SystemExit: public StandardError - { - public: - SystemExit (const std::string& reason) - : StandardError() - { - PyErr_SetString (Py::_Exc_SystemExit(),reason.c_str()); - } - }; - -}// Py - -#endif diff --git a/CXX5/Extensions.hxx b/CXX5/Extensions.hxx deleted file mode 100644 index 815d17b1d1da..000000000000 --- a/CXX5/Extensions.hxx +++ /dev/null @@ -1,928 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (c) 1998 - 2007, The Regents of the University of California -// Produced at the Lawrence Livermore National Laboratory -// All rights reserved. -// -// This file is part of PyCXX. For details,see http://cxx.sourceforge.net/. The -// full copyright notice is contained in the file COPYRIGHT located at the root -// of the PyCXX distribution. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the disclaimer below. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the disclaimer (as noted below) in the -// documentation and/or materials provided with the distribution. -// - Neither the name of the UC/LLNL nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF -// CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. -// -//----------------------------------------------------------------------------- - -#ifndef __CXX_Extensions__h -#define __CXX_Extensions__h - - -#ifdef _MSC_VER -// disable warning C4786: symbol greater than 255 character, -// okay to ignore -#pragma warning(disable: 4786) -#endif - -#include "CXX/WrapPython.h" -#include "CXX/Version.hxx" -#include "CXX/Config.hxx" -#include "CXX/Objects.hxx" - -extern "C" -{ - extern PyObject py_object_initializer; -} - -#include - -// std::map / hash_map selection and declarations ---------------------------- -#if !defined( PYCXX_USING_HASH_MAP ) - -#include - -#else - -#if defined( __GNUC__) && !defined( _STLPORT_VERSION ) - #include -#else - #include -#endif -#if defined( _STLPORT_VERSION ) - #define __PYCXX_HASHMAP_NAMESPACE std - using namespace std; -#elif defined ( _MSC_VER ) && !defined( __INTEL_COMPILER ) && !defined( __ICC ) && !defined( __ICL ) && !defined( __ECC ) - #define __PYCXX_HASHMAP_NAMESPACE stdext - using namespace stdext; -#elif defined( __INTEL_COMPILER ) || defined( __ICC ) || defined( __ICL ) || defined( __ECC ) - #define __PYCXX_HASHMAP_NAMESPACE stdext - using namespace stdext; -#elif defined( __GNUC__ ) - #define __PYCXX_HASHMAP_NAMESPACE __gnu_cxx - using namespace __gnu_cxx; -#else - #define __PYCXX_HASHMAP_NAMESPACE std - using namespace std; -#endif - -class __pycxx_str_hash_func -{ -public: - enum - { - // parameters for hash table - bucket_size = 4, // 0 < bucket_size - min_buckets = 8 // min_buckets = 2 ^^ N, 0 < N - }; - - // http://www.azillionmonkeys.com/qed/hash.html - size_t operator()( const std::string &str ) const - { - const unsigned char * data = reinterpret_cast( str.c_str() ); - int len = (int)str.length(); - unsigned int hash = len; - unsigned int tmp; - int rem; - - if (len <= 0 || data == NULL) - return 0; - - rem = len & 3; - len >>= 2; - - /* Main loop */ - for (;len > 0; len--) - { - hash += (data[1] << 8) | data[0]; - tmp = (((data[3] << 8) | data[2]) << 11) ^ hash; - hash = (hash << 16) ^ tmp; - data += 2*sizeof (unsigned short); - hash += hash >> 11; - } - - /* Handle end cases */ - switch (rem) - { - case 3: hash += (data[1] << 8) | data[0]; - hash ^= hash << 16; - hash ^= data[sizeof (unsigned short)] << 18; - hash += hash >> 11; - break; - case 2: hash += (data[1] << 8) | data[0]; - hash ^= hash << 11; - hash += hash >> 17; - break; - case 1: hash += *data; - hash ^= hash << 10; - hash += hash >> 1; - } - - /* Force "avalanching" of final 127 bits */ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - - return hash; - } - - bool operator()(const std::string &str_1, const std::string &str_2) const - { - // test if str_1 ordered before str_2 - return str_1 < str_2; - } -}; -#endif // PYCXX_USING_HASH_MAP -// ---------------------------------------------------------------------- - -namespace Py -{ - class ExtensionModuleBase; - - // Make an Exception Type for use in raising custom exceptions - class ExtensionExceptionType : public Object - { - public: - ExtensionExceptionType(); - virtual ~ExtensionExceptionType(); - - // call init to create the type - void init( ExtensionModuleBase &module, const std::string& name, ExtensionExceptionType &parent ); - void init( ExtensionModuleBase &module, const std::string& name ); - }; - - - class MethodTable - { - public: - MethodTable(); - virtual ~MethodTable(); - - void add(const char* method_name, PyCFunction f, const char* doc="", int flag=1); - PyMethodDef* table(); - - protected: - std::vector t; // accumulator of PyMethodDef's - PyMethodDef *mt; // Actual method table produced when full - - static PyMethodDef method (const char* method_name, PyCFunction f, int flags = 1, const char* doc=""); - - private: - // - // prevent the compiler generating these unwanted functions - // - MethodTable(const MethodTable& m); //unimplemented - void operator=(const MethodTable& m); //unimplemented - - }; // end class MethodTable - - extern "C" - { - typedef PyObject *(*method_varargs_call_handler_t)( PyObject *_self, PyObject *_args ); - typedef PyObject *(*method_keyword_call_handler_t)( PyObject *_self, PyObject *_args, PyObject *_dict ); - } - - template - class MethodDefExt : public PyMethodDef - { - public: - typedef Object (T::*method_varargs_function_t)( const Tuple &args ); - typedef Object (T::*method_keyword_function_t)( const Tuple &args, const Dict &kws ); - - MethodDefExt - ( - const char *_name, - method_varargs_function_t _function, - method_varargs_call_handler_t _handler, - const char *_doc - ) - { - ext_meth_def.ml_name = const_cast(_name); - ext_meth_def.ml_meth = _handler; - ext_meth_def.ml_flags = METH_VARARGS; - ext_meth_def.ml_doc = const_cast(_doc); - - ext_varargs_function = _function; - ext_keyword_function = NULL; - } - - MethodDefExt - ( - const char *_name, - method_keyword_function_t _function, - method_keyword_call_handler_t _handler, - const char *_doc - ) - { - ext_meth_def.ml_name = const_cast(_name); - ext_meth_def.ml_meth = method_varargs_call_handler_t( _handler ); - ext_meth_def.ml_flags = METH_VARARGS|METH_KEYWORDS; - ext_meth_def.ml_doc = const_cast(_doc); - - ext_varargs_function = NULL; - ext_keyword_function = _function; - } - - ~MethodDefExt() - {} - - PyMethodDef ext_meth_def; - method_varargs_function_t ext_varargs_function; - method_keyword_function_t ext_keyword_function; - }; - - class ExtensionModuleBase - { - public: - ExtensionModuleBase( const char *name ); - virtual ~ExtensionModuleBase(); - - Module module(void) const; // only valid after initialize() has been called - Dict moduleDictionary(void) const; // only valid after initialize() has been called - - virtual Object invoke_method_keyword( const std::string &_name, const Tuple &_args, const Dict &_keywords ) = 0; - virtual Object invoke_method_varargs( const std::string &_name, const Tuple &_args ) = 0; - - const std::string &name() const; - const std::string &fullName() const; - - protected: - // Initialize the module - void initialize( const char *module_doc ); - - const std::string module_name; - const std::string full_module_name; - MethodTable method_table; - - private: - - // - // prevent the compiler generating these unwanted functions - // - ExtensionModuleBase( const ExtensionModuleBase & ); //unimplemented - void operator=( const ExtensionModuleBase & ); //unimplemented - - }; - - extern "C" PyObject *method_keyword_call_handler( PyObject *_self_and_name_tuple, PyObject *_args, PyObject *_keywords ); - extern "C" PyObject *method_varargs_call_handler( PyObject *_self_and_name_tuple, PyObject *_args ); - extern "C" void do_not_dealloc( void * ); - - - template - class ExtensionModule : public ExtensionModuleBase - { - public: - ExtensionModule( const char *name ) - : ExtensionModuleBase( name ) - {} - virtual ~ExtensionModule() - {} - - protected: - typedef Object (T::*method_varargs_function_t)( const Tuple &args ); - typedef Object (T::*method_keyword_function_t)( const Tuple &args, const Dict &kws ); - -#if defined( PYCXX_USING_HASH_MAP ) - typedef __PYCXX_HASHMAP_NAMESPACE::hash_map *, __pycxx_str_hash_func> method_map_t; -#else - typedef std::map *> method_map_t; -#endif - - static void add_varargs_method( const char *name, method_varargs_function_t function, const char *doc="" ) - { - method_map_t &mm = methods(); - - MethodDefExt *method_definition = new MethodDefExt - ( - name, - function, - method_varargs_call_handler, - doc - ); - - mm[std::string( name )] = method_definition; - } - - static void add_keyword_method( const char *name, method_keyword_function_t function, const char *doc="" ) - { - method_map_t &mm = methods(); - - MethodDefExt *method_definition = new MethodDefExt - ( - name, - function, - method_keyword_call_handler, - doc - ); - - mm[std::string( name )] = method_definition; - } - - void initialize( const char *module_doc="" ) - { - ExtensionModuleBase::initialize( module_doc ); - Dict dict( moduleDictionary() ); - - // - // put each of the methods into the modules dictionary - // so that we get called back at the function in T. - // - method_map_t &mm = methods(); - EXPLICIT_TYPENAME method_map_t::const_iterator i; - - for( i=mm.begin(); i != mm.end(); ++i ) - { - MethodDefExt *method_definition = (*i).second; - - static PyObject *self = PyCObject_FromVoidPtr( this, do_not_dealloc ); - - Tuple args( 2 ); - args[0] = Object( self ); - args[1] = String( (*i).first ); - - PyObject *func = PyCFunction_New - ( - &method_definition->ext_meth_def, - new_reference_to( args ) - ); - - dict[ (*i).first ] = Object( func ); - } - } - - protected: // Tom Malcolmson reports that derived classes need access to these - - static method_map_t &methods(void) - { - static method_map_t *map_of_methods = NULL; - if( map_of_methods == NULL ) - map_of_methods = new method_map_t; - - return *map_of_methods; - } - - - // this invoke function must be called from within a try catch block - virtual Object invoke_method_keyword( const std::string &name, const Tuple &args, const Dict &keywords ) - { - method_map_t &mm = methods(); - MethodDefExt *meth_def = mm[ name ]; - if( meth_def == NULL ) - { - std::string error_msg( "CXX - cannot invoke keyword method named " ); - error_msg += name; - throw RuntimeError( error_msg ); - } - - // cast up to the derived class - T *self = static_cast(this); - - return (self->*meth_def->ext_keyword_function)( args, keywords ); - } - - // this invoke function must be called from within a try catch block - virtual Object invoke_method_varargs( const std::string &name, const Tuple &args ) - { - method_map_t &mm = methods(); - MethodDefExt *meth_def = mm[ name ]; - if( meth_def == NULL ) - { - std::string error_msg( "CXX - cannot invoke varargs method named " ); - error_msg += name; - throw RuntimeError( error_msg ); - } - - // cast up to the derived class - T *self = static_cast(this); - - return (self->*meth_def->ext_varargs_function)( args ); - } - - private: - // - // prevent the compiler generating these unwanted functions - // - ExtensionModule( const ExtensionModule & ); //unimplemented - void operator=( const ExtensionModule & ); //unimplemented - }; - - - class PythonType - { - public: - // if you define one sequence method you must define - // all of them except the assigns - - PythonType (size_t base_size, int itemsize, const char *default_name ); - virtual ~PythonType (); - - const char *getName () const; - const char *getDoc () const; - - PyTypeObject* type_object () const; - PythonType & name (const char* nam); - PythonType & doc (const char* d); - PythonType & dealloc(void (*f)(PyObject*)); - - PythonType & supportPrint(void); - PythonType & supportGetattr(void); - PythonType & supportSetattr(void); - PythonType & supportGetattro(void); - PythonType & supportSetattro(void); - PythonType & supportCompare(void); -#if PY_MAJOR_VERSION > 2 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 1) - PythonType & supportRichCompare(void); -#endif - PythonType & supportRepr(void); - PythonType & supportStr(void); - PythonType & supportHash(void); - PythonType & supportCall(void); - PythonType & supportIter(void); - - PythonType & supportSequenceType(void); - PythonType & supportMappingType(void); - PythonType & supportNumberType(void); - PythonType & supportBufferType(void); - - protected: - PyTypeObject *table; - PySequenceMethods *sequence_table; - PyMappingMethods *mapping_table; - PyNumberMethods *number_table; - PyBufferProcs *buffer_table; - - void init_sequence(); - void init_mapping(); - void init_number(); - void init_buffer(); - - private: - // - // prevent the compiler generating these unwanted functions - // - PythonType (const PythonType& tb); // unimplemented - void operator=(const PythonType& t); // unimplemented - - }; // end of PythonType - - - - // Class PythonExtension is what you inherit from to create - // a new Python extension type. You give your class itself - // as the template paramter. - - // There are two ways that extension objects can get destroyed. - // 1. Their reference count goes to zero - // 2. Someone does an explicit delete on a pointer. - // In (1) the problem is to get the destructor called - // We register a special deallocator in the Python type object - // (see behaviors()) to do this. - // In (2) there is no problem, the dtor gets called. - - // PythonExtension does not use the usual Python heap allocator, - // instead using new/delete. We do the setting of the type object - // and reference count, usually done by PyObject_New, in the - // base class ctor. - - // This special deallocator does a delete on the pointer. - - - class PythonExtensionBase : public PyObject - { - public: - PythonExtensionBase(); - virtual ~PythonExtensionBase(); - - public: - virtual int print( FILE *, int ); - virtual Object getattr( const char * ) = 0; - virtual int setattr( const char *, const Object & ); - virtual Object getattro( const Object & ); - virtual int setattro( const Object &, const Object & ); - virtual int compare( const Object & ); - virtual Object rich_compare( const Object &, int op ); - virtual Object repr(); - virtual Object str(); - virtual long hash(); - virtual Object call( const Object &, const Object & ); - virtual Object iter(); - virtual PyObject* iternext(); - - // Sequence methods - virtual int sequence_length(); - virtual Object sequence_concat( const Object & ); - virtual Object sequence_repeat( Py_ssize_t ); - virtual Object sequence_item( Py_ssize_t ); - virtual Object sequence_slice( Py_ssize_t, Py_ssize_t ); - virtual int sequence_ass_item( Py_ssize_t, const Object & ); - virtual int sequence_ass_slice( Py_ssize_t, Py_ssize_t, const Object & ); - - // Mapping - virtual int mapping_length(); - virtual Object mapping_subscript( const Object & ); - virtual int mapping_ass_subscript( const Object &, const Object & ); - - // Number - virtual int number_nonzero(); - virtual Object number_negative(); - virtual Object number_positive(); - virtual Object number_absolute(); - virtual Object number_invert(); - virtual Object number_int(); - virtual Object number_float(); - virtual Object number_long(); - virtual Object number_oct(); - virtual Object number_hex(); - virtual Object number_add( const Object & ); - virtual Object number_subtract( const Object & ); - virtual Object number_multiply( const Object & ); - virtual Object number_divide( const Object & ); - virtual Object number_remainder( const Object & ); - virtual Object number_divmod( const Object & ); - virtual Object number_lshift( const Object & ); - virtual Object number_rshift( const Object & ); - virtual Object number_and( const Object & ); - virtual Object number_xor( const Object & ); - virtual Object number_or( const Object & ); - virtual Object number_power( const Object &, const Object & ); - - // Buffer - virtual Py_ssize_t buffer_getreadbuffer( Py_ssize_t, void** ); - virtual Py_ssize_t buffer_getwritebuffer( Py_ssize_t, void** ); - virtual Py_ssize_t buffer_getsegcount( Py_ssize_t* ); - - private: - void missing_method( void ); - static PyObject *method_call_handler( PyObject *self, PyObject *args ); - }; - - template - class PythonExtension: public PythonExtensionBase - { - public: - static PyTypeObject* type_object() - { - return behaviors().type_object(); - } - - static int check( PyObject *p ) - { - // is p like me? - return p->ob_type == type_object(); - } - - static int check( const Object& ob ) - { - return check( ob.ptr()); - } - - - // - // every object needs getattr implemented - // to support methods - // - virtual Object getattr( const char *name ) - { - return getattr_methods( name ); - } - - protected: - explicit PythonExtension() - : PythonExtensionBase() - { - #ifdef PyObject_INIT - (void)PyObject_INIT( this, type_object() ); - #else - ob_refcnt = 1; - ob_type = type_object(); - #endif - - // every object must support getattr - behaviors().supportGetattr(); - } - - virtual ~PythonExtension() - {} - - static PythonType &behaviors() - { - static PythonType* p; - if( p == NULL ) - { -#if defined( _CPPRTTI ) || defined(__GNUG__) - const char *default_name = (typeid ( T )).name(); -#else - const char *default_name = "unknown"; -#endif - p = new PythonType( sizeof( T ), 0, default_name ); - p->dealloc( extension_object_deallocator ); - } - - return *p; - } - - - typedef Object (T::*method_varargs_function_t)( const Tuple &args ); - typedef Object (T::*method_keyword_function_t)( const Tuple &args, const Dict &kws ); - -#if defined( PYCXX_USING_HASH_MAP ) - typedef __PYCXX_HASHMAP_NAMESPACE::hash_map *, __pycxx_str_hash_func> method_map_t; -#else - typedef std::map *> method_map_t; -#endif - - // support the default attributes, __name__, __doc__ and methods - virtual Object getattr_default( const char *_name ) - { - std::string name( _name ); - - if( name == "__name__" && type_object()->tp_name != NULL ) - { - return Py::String( type_object()->tp_name ); - } - if( name == "__doc__" && type_object()->tp_doc != NULL ) - { - return Py::String( type_object()->tp_doc ); - } - -// trying to fake out being a class for help() -// else if( name == "__bases__" ) -// { -// return Py::Tuple(0); -// } -// else if( name == "__module__" ) -// { -// return Py::Nothing(); -// } -// else if( name == "__dict__" ) -// { -// return Py::Dict(); -// } - - return getattr_methods( _name ); - } - - // turn a name into function object - virtual Object getattr_methods( const char *_name ) - { - std::string name( _name ); - - method_map_t &mm = methods(); - - EXPLICIT_TYPENAME method_map_t::const_iterator i; - - if( name == "__methods__" ) - { - List methods; - - for( i = mm.begin(); i != mm.end(); ++i ) - methods.append( String( (*i).first ) ); - - return methods; - } - - // see if name exists and get entry with method - i = mm.find( name ); - if( i == mm.end() ) - throw AttributeError( name ); - - Tuple self( 2 ); - - self[0] = Object( this ); - self[1] = String( name ); - - MethodDefExt *method_definition = i->second; - - PyObject *func = PyCFunction_New( &method_definition->ext_meth_def, self.ptr() ); - - return Object(func, true); - } - - static void add_varargs_method( const char *name, method_varargs_function_t function, const char *doc="" ) - { - method_map_t &mm = methods(); - - // check that all methods added are unique - EXPLICIT_TYPENAME method_map_t::const_iterator i; - i = mm.find( name ); - if( i != mm.end() ) - throw AttributeError( name ); - - MethodDefExt *method_definition = new MethodDefExt - ( - name, - function, - method_varargs_call_handler, - doc - ); - - mm[std::string( name )] = method_definition; - } - - static void add_keyword_method( const char *name, method_keyword_function_t function, const char *doc="" ) - { - method_map_t &mm = methods(); - - // check that all methods added are unique - EXPLICIT_TYPENAME method_map_t::const_iterator i; - i = mm.find( name ); - if( i != mm.end() ) - throw AttributeError( name ); - - MethodDefExt *method_definition = new MethodDefExt - ( - name, - function, - method_keyword_call_handler, - doc - ); - - mm[std::string( name )] = method_definition; - } - - private: - static method_map_t &methods(void) - { - static method_map_t *map_of_methods = NULL; - if( map_of_methods == NULL ) - map_of_methods = new method_map_t; - - return *map_of_methods; - } - - static PyObject *method_keyword_call_handler( PyObject *_self_and_name_tuple, PyObject *_args, PyObject *_keywords ) - { - try - { - Tuple self_and_name_tuple( _self_and_name_tuple ); - - PyObject *self_in_cobject = self_and_name_tuple[0].ptr(); - T *self = static_cast( self_in_cobject ); - - String name( self_and_name_tuple[1] ); - - method_map_t &mm = methods(); - - EXPLICIT_TYPENAME method_map_t::const_iterator i; - i = mm.find( name ); - if( i == mm.end() ) - return 0; - - MethodDefExt *meth_def = i->second; - - Tuple args( _args ); - - // _keywords may be NULL so be careful about the way the dict is created - Dict keywords; - if( _keywords != NULL ) - keywords = Dict( _keywords ); - - Object result( (self->*meth_def->ext_keyword_function)( args, keywords ) ); - - return new_reference_to( result.ptr() ); - } - catch( Exception & ) - { - return 0; - } - } - - static PyObject *method_varargs_call_handler( PyObject *_self_and_name_tuple, PyObject *_args ) - { - try - { - Tuple self_and_name_tuple( _self_and_name_tuple ); - - PyObject *self_in_cobject = self_and_name_tuple[0].ptr(); - T *self = static_cast( self_in_cobject ); - - String name( self_and_name_tuple[1] ); - - method_map_t &mm = methods(); - - EXPLICIT_TYPENAME method_map_t::const_iterator i; - i = mm.find( name ); - if( i == mm.end() ) - return 0; - - MethodDefExt *meth_def = i->second; - - Tuple args( _args ); - - Object result; - - // TMM: 7Jun'01 - Adding try & catch in case of STL debug-mode exceptions. - #ifdef _STLP_DEBUG - try - { - result = (self->*meth_def->ext_varargs_function)( args ); - } - catch (std::__stl_debug_exception) - { - // throw cxx::RuntimeError( sErrMsg ); - throw cxx::RuntimeError( "Error message not set yet." ); - } - #else - result = (self->*meth_def->ext_varargs_function)( args ); - #endif // _STLP_DEBUG - - return new_reference_to( result.ptr() ); - } - catch( Exception & ) - { - return 0; - } - } - - static void extension_object_deallocator ( PyObject* t ) - { - delete (T *)( t ); - } - - // - // prevent the compiler generating these unwanted functions - // - explicit PythonExtension( const PythonExtension& other ); - void operator=( const PythonExtension& rhs ); - }; - - // - // ExtensionObject is an Object that will accept only T's. - // - template - class ExtensionObject: public Object - { - public: - - explicit ExtensionObject ( PyObject *pyob ) - : Object( pyob ) - { - validate(); - } - - ExtensionObject( const ExtensionObject& other ) - : Object( *other ) - { - validate(); - } - - ExtensionObject( const Object& other ) - : Object( *other ) - { - validate(); - } - - ExtensionObject& operator= ( const Object& rhs ) - { - return (*this = *rhs ); - } - - ExtensionObject& operator= ( PyObject* rhsp ) - { - if( ptr() == rhsp ) - return *this; - set( rhsp ); - return *this; - } - - virtual bool accepts ( PyObject *pyob ) const - { - return ( pyob && T::check( pyob )); - } - - // - // Obtain a pointer to the PythonExtension object - // - T *extensionObject(void) - { - return static_cast( ptr() ); - } - }; - -} // Namespace Py -// End of CXX_Extensions.h -#endif diff --git a/CXX5/IndirectPythonInterface.cxx b/CXX5/IndirectPythonInterface.cxx deleted file mode 100644 index d36d1b52612d..000000000000 --- a/CXX5/IndirectPythonInterface.cxx +++ /dev/null @@ -1,597 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (c) 1998 - 2007, The Regents of the University of California -// Produced at the Lawrence Livermore National Laboratory -// All rights reserved. -// -// This file is part of PyCXX. For details,see http://cxx.sourceforge.net/. The -// full copyright notice is contained in the file COPYRIGHT located at the root -// of the PyCXX distribution. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the disclaimer below. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the disclaimer (as noted below) in the -// documentation and/or materials provided with the distribution. -// - Neither the name of the UC/LLNL nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF -// CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. -// -//----------------------------------------------------------------------------- - -#include "CXX/IndirectPythonInterface.hxx" - -namespace Py -{ -bool _Buffer_Check( PyObject *op ) { return (op)->ob_type == _Buffer_Type(); } -bool _CFunction_Check( PyObject *op ) { return (op)->ob_type == _CFunction_Type(); } -bool _Class_Check( PyObject *op ) { return (op)->ob_type == _Class_Type(); } -bool _CObject_Check( PyObject *op ) { return (op)->ob_type == _CObject_Type(); } -bool _Complex_Check( PyObject *op ) { return (op)->ob_type == _Complex_Type(); } -bool _Dict_Check( PyObject *op ) { return (op)->ob_type == _Dict_Type(); } -bool _File_Check( PyObject *op ) { return (op)->ob_type == _File_Type(); } -bool _Float_Check( PyObject *op ) { return (op)->ob_type == _Float_Type(); } -bool _Function_Check( PyObject *op ) { return (op)->ob_type == _Function_Type(); } -bool _Instance_Check( PyObject *op ) { return (op)->ob_type == _Instance_Type(); } -bool _Boolean_Check( PyObject *op ) { return (op)->ob_type == _Bool_Type(); } -bool _Int_Check( PyObject *op ) { return (op)->ob_type == _Int_Type(); } -bool _List_Check( PyObject *o ) { return o->ob_type == _List_Type(); } -bool _Long_Check( PyObject *op ) { return (op)->ob_type == _Long_Type(); } -bool _Method_Check( PyObject *op ) { return (op)->ob_type == _Method_Type(); } -bool _Module_Check( PyObject *op ) { return (op)->ob_type == _Module_Type(); } -bool _Range_Check( PyObject *op ) { return (op)->ob_type == _Range_Type(); } -bool _Slice_Check( PyObject *op ) { return (op)->ob_type == _Slice_Type(); } -bool _String_Check( PyObject *o ) { return o->ob_type == _String_Type(); } -bool _TraceBack_Check( PyObject *v ) { return (v)->ob_type == _TraceBack_Type(); } -bool _Tuple_Check( PyObject *op ) { return (op)->ob_type == _Tuple_Type(); } -bool _Type_Check( PyObject *op ) { return (op)->ob_type == _Type_Type(); } - -#if PY_MAJOR_VERSION >= 2 -bool _Unicode_Check( PyObject *op ) { return (op)->ob_type == _Unicode_Type(); } -#endif - - - -#if defined(PY_WIN32_DELAYLOAD_PYTHON_DLL) - -#if defined(MS_WINDOWS) -#include - - -static HMODULE python_dll; - -static PyObject *ptr__Exc_ArithmeticError = NULL; -static PyObject *ptr__Exc_AssertionError = NULL; -static PyObject *ptr__Exc_AttributeError = NULL; -static PyObject *ptr__Exc_EnvironmentError = NULL; -static PyObject *ptr__Exc_EOFError = NULL; -static PyObject *ptr__Exc_Exception = NULL; -static PyObject *ptr__Exc_FloatingPointError = NULL; -static PyObject *ptr__Exc_ImportError = NULL; -static PyObject *ptr__Exc_IndexError = NULL; -static PyObject *ptr__Exc_IOError = NULL; -static PyObject *ptr__Exc_KeyboardInterrupt = NULL; -static PyObject *ptr__Exc_KeyError = NULL; -static PyObject *ptr__Exc_LookupError = NULL; -static PyObject *ptr__Exc_MemoryError = NULL; -static PyObject *ptr__Exc_MemoryErrorInst = NULL; -static PyObject *ptr__Exc_NameError = NULL; -static PyObject *ptr__Exc_NotImplementedError = NULL; -static PyObject *ptr__Exc_OSError = NULL; -static PyObject *ptr__Exc_OverflowError = NULL; -static PyObject *ptr__Exc_RuntimeError = NULL; -static PyObject *ptr__Exc_StandardError = NULL; -static PyObject *ptr__Exc_SyntaxError = NULL; -static PyObject *ptr__Exc_SystemError = NULL; -static PyObject *ptr__Exc_SystemExit = NULL; -static PyObject *ptr__Exc_TypeError = NULL; -static PyObject *ptr__Exc_ValueError = NULL; -static PyObject *ptr__Exc_ZeroDivisionError = NULL; - -#ifdef MS_WINDOWS -static PyObject *ptr__Exc_WindowsError = NULL; -#endif - -#if PY_MAJOR_VERSION >= 2 -static PyObject *ptr__Exc_IndentationError = NULL; -static PyObject *ptr__Exc_TabError = NULL; -static PyObject *ptr__Exc_UnboundLocalError = NULL; -static PyObject *ptr__Exc_UnicodeError = NULL; -#endif - -static PyObject *ptr__PyNone = NULL; - -static PyObject *ptr__PyFalse = NULL; -static PyObject *ptr__PyTrue = NULL; - -static PyTypeObject *ptr__Buffer_Type = NULL; -static PyTypeObject *ptr__CFunction_Type = NULL; -static PyTypeObject *ptr__Class_Type = NULL; -static PyTypeObject *ptr__CObject_Type = NULL; -static PyTypeObject *ptr__Complex_Type = NULL; -static PyTypeObject *ptr__Dict_Type = NULL; -static PyTypeObject *ptr__File_Type = NULL; -static PyTypeObject *ptr__Float_Type = NULL; -static PyTypeObject *ptr__Function_Type = NULL; -static PyTypeObject *ptr__Instance_Type = NULL; -static PyTypeObject *ptr__Int_Type = NULL; -static PyTypeObject *ptr__List_Type = NULL; -static PyTypeObject *ptr__Long_Type = NULL; -static PyTypeObject *ptr__Method_Type = NULL; -static PyTypeObject *ptr__Module_Type = NULL; -static PyTypeObject *ptr__Range_Type = NULL; -static PyTypeObject *ptr__Slice_Type = NULL; -static PyTypeObject *ptr__String_Type = NULL; -static PyTypeObject *ptr__TraceBack_Type = NULL; -static PyTypeObject *ptr__Tuple_Type = NULL; -static PyTypeObject *ptr__Type_Type = NULL; - -#if PY_MAJOR_VERSION >= 2 -static PyTypeObject *ptr__Unicode_Type = NULL; -#endif - -static int *ptr_Py_DebugFlag = NULL; -static int *ptr_Py_InteractiveFlag = NULL; -static int *ptr_Py_OptimizeFlag = NULL; -static int *ptr_Py_NoSiteFlag = NULL; -static int *ptr_Py_TabcheckFlag = NULL; -static int *ptr_Py_VerboseFlag = NULL; - -#if PY_MAJOR_VERSION >= 2 -static int *ptr_Py_UnicodeFlag = NULL; -#endif - -static char **ptr__Py_PackageContext = NULL; - -#ifdef Py_REF_DEBUG -int *ptr_Py_RefTotal; -#endif - - -//-------------------------------------------------------------------------------- -class GetAddressException -{ -public: - GetAddressException( const char *_name ) - : name( _name ) - {} - virtual ~GetAddressException() {} - const char *name; -}; - - -//-------------------------------------------------------------------------------- -static PyObject *GetPyObjectPointer_As_PyObjectPointer( const char *name ) -{ - FARPROC addr = GetProcAddress( python_dll, name ); - if( addr == NULL ) - throw GetAddressException( name ); - - return *(PyObject **)addr; -} - -static PyObject *GetPyObject_As_PyObjectPointer( const char *name ) -{ - FARPROC addr = GetProcAddress( python_dll, name ); - if( addr == NULL ) - throw GetAddressException( name ); - - return (PyObject *)addr; -} - -static PyTypeObject *GetPyTypeObjectPointer_As_PyTypeObjectPointer( const char *name ) -{ - FARPROC addr = GetProcAddress( python_dll, name ); - if( addr == NULL ) - throw GetAddressException( name ); - - return *(PyTypeObject **)addr; -} - -static PyTypeObject *GetPyTypeObject_As_PyTypeObjectPointer( const char *name ) -{ - FARPROC addr = GetProcAddress( python_dll, name ); - if( addr == NULL ) - throw GetAddressException( name ); - - return (PyTypeObject *)addr; -} - -static int *GetInt_as_IntPointer( const char *name ) -{ - FARPROC addr = GetProcAddress( python_dll, name ); - if( addr == NULL ) - throw GetAddressException( name ); - - return (int *)addr; -} - -static char **GetCharPointer_as_CharPointerPointer( const char *name ) -{ - FARPROC addr = GetProcAddress( python_dll, name ); - if( addr == NULL ) - throw GetAddressException( name ); - - return (char **)addr; -} - - -#ifdef _DEBUG -static const char python_dll_name_format[] = "PYTHON%1.1d%1.1d_D.DLL"; -#else -static const char python_dll_name_format[] = "PYTHON%1.1d%1.1d.DLL"; -#endif - -//-------------------------------------------------------------------------------- -bool InitialisePythonIndirectInterface() -{ - char python_dll_name[sizeof(python_dll_name_format)]; - - sprintf( python_dll_name, python_dll_name_format, PY_MAJOR_VERSION, PY_MINOR_VERSION ); - - python_dll = LoadLibrary( python_dll_name ); - if( python_dll == NULL ) - return false; - - try -{ -#ifdef Py_REF_DEBUG - ptr_Py_RefTotal = GetInt_as_IntPointer( "_Py_RefTotal" ); -#endif - ptr_Py_DebugFlag = GetInt_as_IntPointer( "Py_DebugFlag" ); - ptr_Py_InteractiveFlag = GetInt_as_IntPointer( "Py_InteractiveFlag" ); - ptr_Py_OptimizeFlag = GetInt_as_IntPointer( "Py_OptimizeFlag" ); - ptr_Py_NoSiteFlag = GetInt_as_IntPointer( "Py_NoSiteFlag" ); - ptr_Py_TabcheckFlag = GetInt_as_IntPointer( "Py_TabcheckFlag" ); - ptr_Py_VerboseFlag = GetInt_as_IntPointer( "Py_VerboseFlag" ); -#if PY_MAJOR_VERSION >= 2 - ptr_Py_UnicodeFlag = GetInt_as_IntPointer( "Py_UnicodeFlag" ); -#endif - ptr__Py_PackageContext = GetCharPointer_as_CharPointerPointer( "_Py_PackageContext" ); - - ptr__Exc_ArithmeticError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_ArithmeticError" ); - ptr__Exc_AssertionError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_AssertionError" ); - ptr__Exc_AttributeError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_AttributeError" ); - ptr__Exc_EnvironmentError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_EnvironmentError" ); - ptr__Exc_EOFError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_EOFError" ); - ptr__Exc_Exception = GetPyObjectPointer_As_PyObjectPointer( "PyExc_Exception" ); - ptr__Exc_FloatingPointError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_FloatingPointError" ); - ptr__Exc_ImportError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_ImportError" ); - ptr__Exc_IndexError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_IndexError" ); - ptr__Exc_IOError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_IOError" ); - ptr__Exc_KeyboardInterrupt = GetPyObjectPointer_As_PyObjectPointer( "PyExc_KeyboardInterrupt" ); - ptr__Exc_KeyError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_KeyError" ); - ptr__Exc_LookupError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_LookupError" ); - ptr__Exc_MemoryError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_MemoryError" ); - ptr__Exc_MemoryErrorInst = GetPyObjectPointer_As_PyObjectPointer( "PyExc_MemoryErrorInst" ); - ptr__Exc_NameError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_NameError" ); - ptr__Exc_NotImplementedError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_NotImplementedError" ); - ptr__Exc_OSError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_OSError" ); - ptr__Exc_OverflowError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_OverflowError" ); - ptr__Exc_RuntimeError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_RuntimeError" ); - ptr__Exc_StandardError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_StandardError" ); - ptr__Exc_SyntaxError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_SyntaxError" ); - ptr__Exc_SystemError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_SystemError" ); - ptr__Exc_SystemExit = GetPyObjectPointer_As_PyObjectPointer( "PyExc_SystemExit" ); - ptr__Exc_TypeError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_TypeError" ); - ptr__Exc_ValueError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_ValueError" ); -#ifdef MS_WINDOWS - ptr__Exc_WindowsError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_WindowsError" ); -#endif - ptr__Exc_ZeroDivisionError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_ZeroDivisionError" ); - -#if PY_MAJOR_VERSION >= 2 - ptr__Exc_IndentationError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_IndentationError" ); - ptr__Exc_TabError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_TabError" ); - ptr__Exc_UnboundLocalError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_UnboundLocalError" ); - ptr__Exc_UnicodeError = GetPyObjectPointer_As_PyObjectPointer( "PyExc_UnicodeError" ); -#endif - ptr__PyNone = GetPyObject_As_PyObjectPointer( "_Py_NoneStruct" ); - - ptr__PyFalse = GetPyObject_As_PyObjectPointer( "_Py_ZeroStruct" ); - ptr__PyTrue = GetPyObject_As_PyObjectPointer( "_Py_TrueStruct" ); - - ptr__Buffer_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyBuffer_Type" ); - ptr__CFunction_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyCFunction_Type" ); - ptr__Class_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyClass_Type" ); - ptr__CObject_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyCObject_Type" ); - ptr__Complex_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyComplex_Type" ); - ptr__Dict_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyDict_Type" ); - ptr__File_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyFile_Type" ); - ptr__Float_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyFloat_Type" ); - ptr__Function_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyFunction_Type" ); - ptr__Instance_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyInstance_Type" ); - ptr__Int_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyInt_Type" ); - ptr__List_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyList_Type" ); - ptr__Long_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyLong_Type" ); - ptr__Method_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyMethod_Type" ); - ptr__Module_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyModule_Type" ); - ptr__Range_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyRange_Type" ); - ptr__Slice_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PySlice_Type" ); - ptr__String_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyString_Type" ); - ptr__TraceBack_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyTraceBack_Type" ); - ptr__Tuple_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyTuple_Type" ); - ptr__Type_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyType_Type" ); - -#if PY_MAJOR_VERSION >= 2 - ptr__Unicode_Type = GetPyTypeObject_As_PyTypeObjectPointer( "PyUnicode_Type" ); -#endif -} - catch( GetAddressException &e ) - { - OutputDebugString( python_dll_name ); - OutputDebugString( " does not contain symbol "); - OutputDebugString( e.name ); - OutputDebugString( "\n" ); - - return false; - } - - return true; -} - -// -// Wrap variables as function calls -// -PyObject * _Exc_ArithmeticError(){ return ptr__Exc_ArithmeticError; } -PyObject * _Exc_AssertionError(){ return ptr__Exc_AssertionError; } -PyObject * _Exc_AttributeError(){ return ptr__Exc_AttributeError; } -PyObject * _Exc_EnvironmentError(){ return ptr__Exc_EnvironmentError; } -PyObject * _Exc_EOFError() { return ptr__Exc_EOFError; } -PyObject * _Exc_Exception() { return ptr__Exc_Exception; } -PyObject * _Exc_FloatingPointError(){ return ptr__Exc_FloatingPointError; } -PyObject * _Exc_ImportError() { return ptr__Exc_ImportError; } -PyObject * _Exc_IndexError() { return ptr__Exc_IndexError; } -PyObject * _Exc_IOError() { return ptr__Exc_IOError; } -PyObject * _Exc_KeyboardInterrupt(){ return ptr__Exc_KeyboardInterrupt; } -PyObject * _Exc_KeyError() { return ptr__Exc_KeyError; } -PyObject * _Exc_LookupError() { return ptr__Exc_LookupError; } -PyObject * _Exc_MemoryError() { return ptr__Exc_MemoryError; } -PyObject * _Exc_MemoryErrorInst(){ return ptr__Exc_MemoryErrorInst; } -PyObject * _Exc_NameError() { return ptr__Exc_NameError; } -PyObject * _Exc_NotImplementedError(){ return ptr__Exc_NotImplementedError; } -PyObject * _Exc_OSError() { return ptr__Exc_OSError; } -PyObject * _Exc_OverflowError() { return ptr__Exc_OverflowError; } -PyObject * _Exc_RuntimeError() { return ptr__Exc_RuntimeError; } -PyObject * _Exc_StandardError() { return ptr__Exc_StandardError; } -PyObject * _Exc_SyntaxError() { return ptr__Exc_SyntaxError; } -PyObject * _Exc_SystemError() { return ptr__Exc_SystemError; } -PyObject * _Exc_SystemExit() { return ptr__Exc_SystemExit; } -PyObject * _Exc_TypeError() { return ptr__Exc_TypeError; } -PyObject * _Exc_ValueError() { return ptr__Exc_ValueError; } -#ifdef MS_WINDOWS -PyObject * _Exc_WindowsError() { return ptr__Exc_WindowsError; } -#endif -PyObject * _Exc_ZeroDivisionError(){ return ptr__Exc_ZeroDivisionError; } - -#if PY_MAJOR_VERSION >= 2 -PyObject * _Exc_IndentationError(){ return ptr__Exc_IndentationError; } -PyObject * _Exc_TabError() { return ptr__Exc_TabError; } -PyObject * _Exc_UnboundLocalError(){ return ptr__Exc_UnboundLocalError; } -PyObject * _Exc_UnicodeError() { return ptr__Exc_UnicodeError; } -#endif - -// -// wrap items in Object.h -// -PyObject * _None() { return ptr__PyNone; } - -PyObject * _False() { return ptr__PyFalse; } -PyObject * _True() { return ptr__PyTrue; } - -PyTypeObject * _Buffer_Type() { return ptr__Buffer_Type; } -PyTypeObject * _CFunction_Type(){ return ptr__CFunction_Type; } -PyTypeObject * _Class_Type() { return ptr__Class_Type; } -PyTypeObject * _CObject_Type() { return ptr__CObject_Type; } -PyTypeObject * _Complex_Type() { return ptr__Complex_Type; } -PyTypeObject * _Dict_Type() { return ptr__Dict_Type; } -PyTypeObject * _File_Type() { return ptr__File_Type; } -PyTypeObject * _Float_Type() { return ptr__Float_Type; } -PyTypeObject * _Function_Type() { return ptr__Function_Type; } -PyTypeObject * _Instance_Type() { return ptr__Instance_Type; } -PyTypeObject * _Bool_Type() { return ptr__Bool_Type; } -PyTypeObject * _Int_Type() { return ptr__Int_Type; } -PyTypeObject * _List_Type() { return ptr__List_Type; } -PyTypeObject * _Long_Type() { return ptr__Long_Type; } -PyTypeObject * _Method_Type() { return ptr__Method_Type; } -PyTypeObject * _Module_Type() { return ptr__Module_Type; } -PyTypeObject * _Range_Type() { return ptr__Range_Type; } -PyTypeObject * _Slice_Type() { return ptr__Slice_Type; } -PyTypeObject * _String_Type() { return ptr__String_Type; } -PyTypeObject * _TraceBack_Type(){ return ptr__TraceBack_Type; } -PyTypeObject * _Tuple_Type() { return ptr__Tuple_Type; } -PyTypeObject * _Type_Type() { return ptr__Type_Type; } - -#if PY_MAJOR_VERSION >= 2 -PyTypeObject * _Unicode_Type() { return ptr__Unicode_Type; } -#endif - -char *__Py_PackageContext() { return *ptr__Py_PackageContext; } - - -// -// wrap the Python Flag variables -// -int &_Py_DebugFlag() { return *ptr_Py_DebugFlag; } -int &_Py_InteractiveFlag() { return *ptr_Py_InteractiveFlag; } -int &_Py_OptimizeFlag() { return *ptr_Py_OptimizeFlag; } -int &_Py_NoSiteFlag() { return *ptr_Py_NoSiteFlag; } -int &_Py_TabcheckFlag() { return *ptr_Py_TabcheckFlag; } -int &_Py_VerboseFlag() { return *ptr_Py_VerboseFlag; } -#if PY_MAJOR_VERSION >= 2 -int &_Py_UnicodeFlag() { return *ptr_Py_UnicodeFlag; } -#endif - -void _XINCREF( PyObject *op ) -{ - // This function must match the contents of Py_XINCREF(op) - if( op == NULL ) - return; - -#ifdef Py_REF_DEBUG - (*ptr_Py_RefTotal)++; -#endif - (op)->ob_refcnt++; - -} - -void _XDECREF( PyObject *op ) -{ - // This function must match the contents of Py_XDECREF(op); - if( op == NULL ) - return; - -#ifdef Py_REF_DEBUG - (*ptr_Py_RefTotal)--; -#endif - - if (--(op)->ob_refcnt == 0) - _Py_Dealloc((PyObject *)(op)); -} - - -#else -#error "Can only delay load under Win32" -#endif - -#else - -// -// Duplicated these declarations from rangeobject.h which is missing the -// extern "C". This has been reported as a bug upto and include 2.1 -// -extern "C" DL_IMPORT(PyTypeObject) PyRange_Type; -extern "C" DL_IMPORT(PyObject *) PyRange_New(long, long, long, int); - - -//================================================================================ -// -// Map onto Macros -// -//================================================================================ - -// -// Wrap variables as function calls -// - -PyObject * _Exc_ArithmeticError() { return ::PyExc_ArithmeticError; } -PyObject * _Exc_AssertionError() { return ::PyExc_AssertionError; } -PyObject * _Exc_AttributeError() { return ::PyExc_AttributeError; } -PyObject * _Exc_EnvironmentError() { return ::PyExc_EnvironmentError; } -PyObject * _Exc_EOFError() { return ::PyExc_EOFError; } -PyObject * _Exc_Exception() { return ::PyExc_Exception; } -PyObject * _Exc_FloatingPointError() { return ::PyExc_FloatingPointError; } -PyObject * _Exc_ImportError() { return ::PyExc_ImportError; } -PyObject * _Exc_IndexError() { return ::PyExc_IndexError; } -PyObject * _Exc_IOError() { return ::PyExc_IOError; } -PyObject * _Exc_KeyboardInterrupt() { return ::PyExc_KeyboardInterrupt; } -PyObject * _Exc_KeyError() { return ::PyExc_KeyError; } -PyObject * _Exc_LookupError() { return ::PyExc_LookupError; } -PyObject * _Exc_MemoryError() { return ::PyExc_MemoryError; } -PyObject * _Exc_MemoryErrorInst() { return ::PyExc_MemoryErrorInst; } -PyObject * _Exc_NameError() { return ::PyExc_NameError; } -PyObject * _Exc_NotImplementedError() { return ::PyExc_NotImplementedError; } -PyObject * _Exc_OSError() { return ::PyExc_OSError; } -PyObject * _Exc_OverflowError() { return ::PyExc_OverflowError; } -PyObject * _Exc_RuntimeError() { return ::PyExc_RuntimeError; } -PyObject * _Exc_StandardError() { return ::PyExc_StandardError; } -PyObject * _Exc_SyntaxError() { return ::PyExc_SyntaxError; } -PyObject * _Exc_SystemError() { return ::PyExc_SystemError; } -PyObject * _Exc_SystemExit() { return ::PyExc_SystemExit; } -PyObject * _Exc_TypeError() { return ::PyExc_TypeError; } -PyObject * _Exc_ValueError() { return ::PyExc_ValueError; } -PyObject * _Exc_ZeroDivisionError() { return ::PyExc_ZeroDivisionError; } - -#ifdef MS_WINDOWS -PyObject * _Exc_WindowsError() { return ::PyExc_WindowsError; } -#endif - - -#if PY_MAJOR_VERSION >= 2 -PyObject * _Exc_IndentationError() { return ::PyExc_IndentationError; } -PyObject * _Exc_TabError() { return ::PyExc_TabError; } -PyObject * _Exc_UnboundLocalError() { return ::PyExc_UnboundLocalError; } -PyObject * _Exc_UnicodeError() { return ::PyExc_UnicodeError; } -#endif - - -// -// wrap items in Object.h -// -PyObject * _None() { return &::_Py_NoneStruct; } - -PyObject * _False() { return Py_False; } -PyObject * _True() { return Py_True; } - -PyTypeObject * _Buffer_Type() { return &PyBuffer_Type; } -PyTypeObject * _CFunction_Type() { return &PyCFunction_Type; } -PyTypeObject * _Class_Type() { return &PyClass_Type; } -PyTypeObject * _CObject_Type() { return &PyCObject_Type; } -PyTypeObject * _Complex_Type() { return &PyComplex_Type; } -PyTypeObject * _Dict_Type() { return &PyDict_Type; } -PyTypeObject * _File_Type() { return &PyFile_Type; } -PyTypeObject * _Float_Type() { return &PyFloat_Type; } -PyTypeObject * _Function_Type() { return &PyFunction_Type; } -PyTypeObject * _Instance_Type() { return &PyInstance_Type; } -PyTypeObject * _Bool_Type() { return &PyBool_Type; } -PyTypeObject * _Int_Type() { return &PyInt_Type; } -PyTypeObject * _List_Type() { return &PyList_Type; } -PyTypeObject * _Long_Type() { return &PyLong_Type; } -PyTypeObject * _Method_Type() { return &PyMethod_Type; } -PyTypeObject * _Module_Type() { return &PyModule_Type; } -PyTypeObject * _Range_Type() { return &PyRange_Type; } -PyTypeObject * _Slice_Type() { return &PySlice_Type; } -PyTypeObject * _String_Type() { return &PyString_Type; } -PyTypeObject * _TraceBack_Type() { return &PyTraceBack_Type; } -PyTypeObject * _Tuple_Type() { return &PyTuple_Type; } -PyTypeObject * _Type_Type() { return &PyType_Type; } - -#if PY_MAJOR_VERSION >= 2 -PyTypeObject * _Unicode_Type() { return &PyUnicode_Type; } -#endif - -// -// wrap flags -// -int &_Py_DebugFlag() { return Py_DebugFlag; } -int &_Py_InteractiveFlag(){ return Py_InteractiveFlag; } -int &_Py_OptimizeFlag() { return Py_OptimizeFlag; } -int &_Py_NoSiteFlag() { return Py_NoSiteFlag; } -int &_Py_TabcheckFlag() { return Py_TabcheckFlag; } -int &_Py_VerboseFlag() { return Py_VerboseFlag; } -#if PY_MAJOR_VERSION >= 2 -int &_Py_UnicodeFlag() { return Py_UnicodeFlag; } -#endif -char *__Py_PackageContext(){ return _Py_PackageContext; } - -// -// Needed to keep the abstactions for delayload interface -// -void _XINCREF( PyObject *op ) -{ - Py_XINCREF(op); -} - -void _XDECREF( PyObject *op ) -{ - Py_XDECREF(op); -} - -#endif -} diff --git a/CXX5/IndirectPythonInterface.hxx b/CXX5/IndirectPythonInterface.hxx deleted file mode 100644 index a29a394c6c6a..000000000000 --- a/CXX5/IndirectPythonInterface.hxx +++ /dev/null @@ -1,198 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (c) 1998 - 2007, The Regents of the University of California -// Produced at the Lawrence Livermore National Laboratory -// All rights reserved. -// -// This file is part of PyCXX. For details,see http://cxx.sourceforge.net/. The -// full copyright notice is contained in the file COPYRIGHT located at the root -// of the PyCXX distribution. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the disclaimer below. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the disclaimer (as noted below) in the -// documentation and/or materials provided with the distribution. -// - Neither the name of the UC/LLNL nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF -// CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. -// -//----------------------------------------------------------------------------- - -#ifndef __CXX_INDIRECT_PYTHON_INTERFACE__HXX__ -#define __CXX_INDIRECT_PYTHON_INTERFACE__HXX__ - -#include "CXX/WrapPython.h" - -namespace Py -{ -bool InitialisePythonIndirectInterface(); - -// -// Wrap Exception variables as function calls -// -PyObject * _Exc_Exception(); -PyObject * _Exc_StandardError(); -PyObject * _Exc_ArithmeticError(); -PyObject * _Exc_LookupError(); - -PyObject * _Exc_AssertionError(); -PyObject * _Exc_AttributeError(); -PyObject * _Exc_EOFError(); -PyObject * _Exc_FloatingPointError(); -PyObject * _Exc_EnvironmentError(); -PyObject * _Exc_IOError(); -PyObject * _Exc_OSError(); -PyObject * _Exc_ImportError(); -PyObject * _Exc_IndexError(); -PyObject * _Exc_KeyError(); -PyObject * _Exc_KeyboardInterrupt(); -PyObject * _Exc_MemoryError(); -PyObject * _Exc_NameError(); -PyObject * _Exc_OverflowError(); -PyObject * _Exc_RuntimeError(); -PyObject * _Exc_NotImplementedError(); -PyObject * _Exc_SyntaxError(); -PyObject * _Exc_SystemError(); -PyObject * _Exc_SystemExit(); -PyObject * _Exc_TypeError(); -PyObject * _Exc_ValueError(); -PyObject * _Exc_ZeroDivisionError(); -#ifdef MS_WINDOWS -PyObject * _Exc_WindowsError(); -#endif - -PyObject * _Exc_MemoryErrorInst(); - -#if PY_MAJOR_VERSION >= 2 -PyObject * _Exc_IndentationError(); -PyObject * _Exc_TabError(); -PyObject * _Exc_UnboundLocalError(); -PyObject * _Exc_UnicodeError(); -#endif - -// -// Wrap Object variables as function calls -// -PyObject * _None(); - -PyObject * _False(); -PyObject * _True(); - -// -// Wrap Type variables as function calls -// -PyTypeObject * _List_Type(); -bool _List_Check( PyObject *o ); - -PyTypeObject * _Buffer_Type(); -bool _Buffer_Check( PyObject *op ); - -PyTypeObject * _Class_Type(); -bool _Class_Check( PyObject *op ); - -PyTypeObject * _Instance_Type(); -bool _Instance_Check( PyObject *op ); - -PyTypeObject * _Method_Type(); -bool _Method_Check( PyObject *op ); - -PyTypeObject * _CObject_Type(); -bool _CObject_Check( PyObject *op ); - -PyTypeObject * _Complex_Type(); -bool _Complex_Check( PyObject *op ); - -PyTypeObject * _Dict_Type(); -bool _Dict_Check( PyObject *op ); - -PyTypeObject * _File_Type(); -bool _File_Check( PyObject *op ); - -PyTypeObject * _Float_Type(); -bool _Float_Check( PyObject *op ); - -PyTypeObject * _Frame_Type(); -bool _Frame_Check( PyObject *op ); - -PyTypeObject * _Function_Type(); -bool _Function_Check( PyObject *op ); - -PyTypeObject * _Bool_Type(); -bool _Boolean_Check( PyObject *op ); - -PyTypeObject * _Int_Type(); -bool _Int_Check( PyObject *op ); - -PyTypeObject * _List_Type(); -bool _List_Check( PyObject *op ); - -PyTypeObject * _Long_Type(); -bool _Long_Check( PyObject *op ); - -PyTypeObject * _CFunction_Type(); -bool _CFunction_Check( PyObject *op ); - -PyTypeObject * _Module_Type(); -bool _Module_Check( PyObject *op ); - -PyTypeObject * _Type_Type(); -bool _Type_Check( PyObject *op ); - -PyTypeObject * _Range_Type(); -bool _Range_Check( PyObject *op ); - -PyTypeObject * _Slice_Type(); -bool _Slice_Check( PyObject *op ); - -PyTypeObject * _String_Type(); -bool _String_Check( PyObject *op ); - -PyTypeObject * _Unicode_Type(); -bool _Unicode_Check( PyObject *op ); - -PyTypeObject * _TraceBack_Type(); -bool _TraceBack_Check( PyObject *v ); - -PyTypeObject * _Tuple_Type(); -bool _Tuple_Check( PyObject *op ); - -#if PY_MAJOR_VERSION >= 2 -PyTypeObject * _Unicode_Type(); -bool _Unicode_Check( PyObject *op ); -#endif - -int &_Py_DebugFlag(); -int &_Py_InteractiveFlag(); -int &_Py_OptimizeFlag(); -int &_Py_NoSiteFlag(); -int &_Py_TabcheckFlag(); -int &_Py_VerboseFlag(); - -#if PY_MAJOR_VERSION >= 2 -int &_Py_UnicodeFlag(); -#endif - -void _XINCREF( PyObject *op ); -void _XDECREF( PyObject *op ); - -char *__Py_PackageContext(); -} - -#endif // __CXX_INDIRECT_PYTHON_INTERFACE__HXX__ diff --git a/CXX5/Objects.hxx b/CXX5/Objects.hxx deleted file mode 100644 index e9d59a44e0b0..000000000000 --- a/CXX5/Objects.hxx +++ /dev/null @@ -1,3040 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (c) 1998 - 2007, The Regents of the University of California -// Produced at the Lawrence Livermore National Laboratory -// All rights reserved. -// -// This file is part of PyCXX. For details,see http://cxx.sourceforge.net/. The -// full copyright notice is contained in the file COPYRIGHT located at the root -// of the PyCXX distribution. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the disclaimer below. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the disclaimer (as noted below) in the -// documentation and/or materials provided with the distribution. -// - Neither the name of the UC/LLNL nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF -// CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. -// -//----------------------------------------------------------------------------- - -#ifndef __CXX_Objects__h -#define __CXX_Objects__h - -#include "CXX/WrapPython.h" -#include "CXX/Version.hxx" -#include "CXX/Config.hxx" -#include "CXX/Exception.hxx" - -#include -#include STR_STREAM -#include -#include -#include -#include - -namespace Py -{ - typedef int sequence_index_type; // type of an index into a sequence - - // Forward declarations - class Object; - class Type; - template class SeqBase; - class String; - class List; - template class MapBase; - - // new_reference_to also overloaded below on Object - inline PyObject* new_reference_to(PyObject* p) - { - Py::_XINCREF(p); - return p; - } - - // returning Null() from an extension method triggers a - // Python exception - inline PyObject* Null() - { - return (static_cast(0)); - } - - //===========================================================================// - // class Object - // The purpose of this class is to serve as the most general kind of - // Python object, for the purpose of writing C++ extensions in Python - // Objects hold a PyObject* which they own. This pointer is always a - // valid pointer to a Python object. In children we must maintain this behavior. - // - // Instructions on how to make your own class MyType descended from Object: - // (0) Pick a base class, either Object or perhaps SeqBase or MapBase. - // This example assumes Object. - - // (1) Write a routine int MyType_Check (PyObject *) modeled after PyInt_Check, - // PyFloat_Check, etc. - - // (2) Add method accepts: - // virtual bool accepts (PyObject *pyob) const { - // return pyob && MyType_Check (pyob); - // } - - // (3) Include the following constructor and copy constructor - // - /* - explicit MyType (PyObject *pyob): Object(pyob) { - validate(); -} - - MyType(const Object& other): Object(other.ptr()) { - validate(); -} - */ - - // Alernate version for the constructor to allow for construction from owned pointers: - /* - explicit MyType (PyObject *pyob): Object(pyob) { - validate(); -} - */ - - // You may wish to add other constructors; see the classes below for examples. - // Each constructor must use "set" to set the pointer - // and end by validating the pointer you have created. - - // (4) Each class needs at least these two assignment operators: - /* - MyType& operator= (const Object& rhs) { - return (*this = *rhs); -} - - Mytype& operator= (PyObject* rhsp) { - if(ptr() == rhsp) return *this; - set(rhsp); - return *this; -} - */ - // Note on accepts: constructors call the base class - // version of a virtual when calling the base class constructor, - // so the test has to be done explicitly in a descendent. - - // If you are inheriting from PythonExtension to define an object - // note that it contains PythonExtension::check - // which you can use in accepts when writing a wrapper class. - // See Demo/range.h and Demo/range.cxx for an example. - - class Object - { - private: - // the pointer to the Python object - // Only Object sets this directly. - // The default constructor for Object sets it to Py_None and - // child classes must use "set" to set it - // - PyObject* p; - - protected: - - void set (PyObject* pyob, bool owned = false) - { - release(); - p = pyob; - if (!owned) - { - Py::_XINCREF (p); - } - validate(); - } - - void release () - { - Py::_XDECREF (p); - p = 0; - } - - void validate(); - - public: - // Constructor acquires new ownership of pointer unless explicitly told not to. - explicit Object (PyObject* pyob=Py::_None(), bool owned = false): p (pyob) - { - if(!owned) - { - Py::_XINCREF (p); - } - validate(); - } - - // Copy constructor acquires new ownership of pointer - Object (const Object& ob): p(ob.p) - { - Py::_XINCREF (p); - validate(); - } - - // Assignment acquires new ownership of pointer - Object& operator= (const Object& rhs) - { - set(rhs.p); - return *this; - } - - Object& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set (rhsp); - return *this; - } - - // Destructor - virtual ~Object () - { - release (); - } - - // Loaning the pointer to others, retain ownership - PyObject* operator* () const - { - return p; - } - - // Explicit reference_counting changes - void increment_reference_count() - { - Py::_XINCREF(p); - } - - void decrement_reference_count() - { - // not allowed to commit suicide, however - if(reference_count() == 1) - throw RuntimeError("Object::decrement_reference_count error."); - Py::_XDECREF(p); - } - // Would like to call this pointer() but messes up STL in SeqBase - PyObject* ptr () const - { - return p; - } - - // - // Queries - // - - // Can pyob be used in this object's constructor? - virtual bool accepts (PyObject *pyob) const - { - return (pyob != 0); - } - - Py_ssize_t reference_count () const - { // the reference count - return p ? p->ob_refcnt : 0; - } - - Type type () const; // the type object associated with this one - - String str () const; // the str() representation - - std::string as_string() const; - - String repr () const; // the repr () representation - - List dir () const; // the dir() list - - bool hasAttr (const std::string& s) const - { - return PyObject_HasAttrString (p, const_cast(s.c_str())) ? true: false; - } - - Object getAttr (const std::string& s) const - { - return Object (PyObject_GetAttrString (p, const_cast(s.c_str())), true); - } - - Object getItem (const Object& key) const - { - return Object (PyObject_GetItem(p, *key), true); - } - - long hashValue () const - { - return PyObject_Hash (p); - } - - // - // int print (FILE* fp, int flags=Py_Print_RAW) - //{ - // return PyObject_Print (p, fp, flags); - //} - // - bool is(PyObject *pother) const - { // identity test - return p == pother; - } - - bool is(const Object& other) const - { // identity test - return p == other.p; - } - - bool isNone() const - { - return p == _None(); - } - - bool isCallable () const - { - return PyCallable_Check (p) != 0; - } - - bool isInstance () const - { - return PyInstance_Check (p) != 0; - } - - bool isDict () const - { - return Py::_Dict_Check (p); - } - - bool isList () const - { - return Py::_List_Check (p); - } - - bool isMapping () const - { - return PyMapping_Check (p) != 0; - } - - bool isNumeric () const - { - return PyNumber_Check (p) != 0; - } - - bool isSequence () const - { - return PySequence_Check (p) != 0; - } - - bool isTrue () const - { - return PyObject_IsTrue (p) != 0; - } - - bool isType (const Type& t) const; - - bool isTuple() const - { - return Py::_Tuple_Check(p); - } - - bool isString() const - { - return Py::_String_Check(p) || Py::_Unicode_Check(p); - } - - bool isUnicode() const - { - return Py::_Unicode_Check(p); - } - - // Commands - void setAttr (const std::string& s, const Object& value) - { - if(PyObject_SetAttrString (p, const_cast(s.c_str()), *value) == -1) - throw AttributeError ("getAttr failed."); - } - - void delAttr (const std::string& s) - { - if(PyObject_DelAttrString (p, const_cast(s.c_str())) == -1) - throw AttributeError ("delAttr failed."); - } - - // PyObject_SetItem is too weird to be using from C++ - // so it is intentionally omitted. - - void delItem (const Object& key) - { - //if(PyObject_DelItem(p, *key) == -1) - // failed to link on Windows? - throw KeyError("delItem failed."); - } - - // Equality and comparison use PyObject_RichCompareBool - - bool operator==(const Object& o2) const - { - int k = PyObject_RichCompareBool (p, *o2, Py_EQ); - if (PyErr_Occurred()) throw Exception(); - return k != 0; - } - - bool operator!=(const Object& o2) const - { - int k = PyObject_RichCompareBool (p, *o2, Py_NE); - if (PyErr_Occurred()) throw Exception(); - return k != 0; - - } - - bool operator>=(const Object& o2) const - { - int k = PyObject_RichCompareBool (p, *o2, Py_GE); - if (PyErr_Occurred()) throw Exception(); - return k != 0; - } - - bool operator<=(const Object& o2) const - { - int k = PyObject_RichCompareBool (p, *o2, Py_LE); - if (PyErr_Occurred()) throw Exception(); - return k != 0; - } - - bool operator<(const Object& o2) const - { - int k = PyObject_RichCompareBool (p, *o2, Py_LT); - if (PyErr_Occurred()) throw Exception(); - return k != 0; - } - - bool operator>(const Object& o2) const - { - int k = PyObject_RichCompareBool (p, *o2, Py_GT); - if (PyErr_Occurred()) throw Exception(); - return k != 0; - } - }; - // End of class Object - inline PyObject* new_reference_to(const Object& g) - { - PyObject* p = g.ptr(); - Py::_XINCREF(p); - return p; - } - - // Nothing() is what an extension method returns if - // there is no other return value. - inline Object Nothing() - { - return Object(Py::_None()); - } - - // Python special None value - inline Object None() - { - return Object(Py::_None()); - } - - // Python special Boolean values - inline Object False() - { - return Object(Py::_False()); - } - - inline Object True() - { - return Object(Py::_True()); - } - - // TMM: 31May'01 - Added the #ifndef so I can exlude iostreams. -#ifndef CXX_NO_IOSTREAMS - std::ostream& operator<< (std::ostream& os, const Object& ob); -#endif - - // Class Type - class Type: public Object - { - public: - explicit Type (PyObject* pyob, bool owned = false): Object(pyob, owned) - { - validate(); - } - - Type (const Object& ob): Object(*ob) - { - validate(); - } - - Type(const Type& t): Object(t) - { - validate(); - } - - Type& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - Type& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set (rhsp); - return *this; - } - virtual bool accepts (PyObject *pyob) const - { - return pyob && Py::_Type_Check (pyob); - } - }; - - - // - // Convert an owned Python pointer into a CXX Object - // - inline Object asObject (PyObject *p) - { - return Object(p, true); - } - - // =============================================== - // class boolean - class Boolean: public Object - { - public: - // Constructor - Boolean (PyObject *pyob, bool owned = false): Object (pyob, owned) - { - validate(); - } - - Boolean (const Boolean& ob): Object(*ob) - { - validate(); - } - - // create from bool - Boolean (bool v=false) - { - set(PyBool_FromLong(v ? 1 : 0), true); - validate(); - } - - explicit Boolean (const Object& ob) - { - set(*ob, true); - validate(); - } - - // Assignment acquires new ownership of pointer - - Boolean& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - Boolean& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set (rhsp, true); - return *this; - } - - // Membership - virtual bool accepts (PyObject *pyob) const - { - return pyob && Py::_Boolean_Check (pyob); - } - - // convert to long - operator bool() const - { - return PyObject_IsTrue (ptr()) != 0; - } - - Boolean& operator= (bool v) - { - set (PyBool_FromLong (v ? 1 : 0), true); - return *this; - } - }; - - // =============================================== - // class Int - class Int: public Object - { - public: - // Constructor - Int (PyObject *pyob, bool owned = false): Object (pyob, owned) - { - validate(); - } - - Int (const Int& ob): Object(*ob) - { - validate(); - } - - // create from long - Int (long v = 0L): Object(PyInt_FromLong(v), true) - { - validate(); - } - - // create from int - Int (int v) - { - long w = v; - set(PyInt_FromLong(w), true); - validate(); - } - - // create from bool - Int (bool v) - { - long w = v ? 1 : 0; - set(PyInt_FromLong(w), true); - validate(); - } - - explicit Int (const Object& ob) - { - set(PyNumber_Int(*ob), true); - validate(); - } - - // Assignment acquires new ownership of pointer - - Int& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - Int& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set (PyNumber_Int(rhsp), true); - return *this; - } - - // Membership - virtual bool accepts (PyObject *pyob) const - { - return pyob && Py::_Int_Check (pyob); - } - - // convert to long - operator long() const - { - return PyInt_AsLong (ptr()); - } - -#ifdef HAVE_LONG_LONG - // convert to long long - PY_LONG_LONG asLongLong() const - { - return PyLong_AsLongLong (ptr()); - } - // convert to unsigned long long - unsigned PY_LONG_LONG asUnsignedLongLong() const - { - return PyLong_AsUnsignedLongLong (ptr()); - } -#endif - - // assign from an int - Int& operator= (int v) - { - set (PyInt_FromLong (long(v)), true); - return *this; - } - - // assign from long - Int& operator= (long v) - { - set (PyInt_FromLong (v), true); - return *this; - } - -#ifdef HAVE_LONG_LONG - // assign from long long - Int& operator= (PY_LONG_LONG v) - { - set (PyLong_FromLongLong (v), true); - return *this; - } - // assign from unsigned long long - Int& operator= (unsigned PY_LONG_LONG v) - { - set (PyLong_FromUnsignedLongLong (v), true); - return *this; - } -#endif - }; - - // =============================================== - // class Long - class Long: public Object - { - public: - // Constructor - explicit Long (PyObject *pyob, bool owned = false): Object (pyob, owned) - { - validate(); - } - - Long (const Long& ob): Object(ob.ptr()) - { - validate(); - } - - // create from long - explicit Long (long v = 0L) - : Object(PyLong_FromLong(v), true) - { - validate(); - } - // create from unsigned long - explicit Long (unsigned long v) - : Object(PyLong_FromUnsignedLong(v), true) - { - validate(); - } - // create from int - explicit Long (int v) - : Object(PyLong_FromLong(static_cast(v)), true) - { - validate(); - } - - // try to create from any object - Long (const Object& ob) - : Object(PyNumber_Long(*ob), true) - { - validate(); - } - - // Assignment acquires new ownership of pointer - - Long& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - Long& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set (PyNumber_Long(rhsp), true); - return *this; - } - // Membership - virtual bool accepts (PyObject *pyob) const - { - return pyob && Py::_Long_Check (pyob); - } - // convert to long - operator long() const - { - return PyLong_AsLong (ptr()); - } - // convert to unsigned - operator unsigned long() const - { - return PyLong_AsUnsignedLong (ptr()); - } - operator double() const - { - return PyLong_AsDouble (ptr()); - } - // assign from an int - Long& operator= (int v) - { - set(PyLong_FromLong (long(v)), true); - return *this; - } - // assign from long - Long& operator= (long v) - { - set(PyLong_FromLong (v), true); - return *this; - } - // assign from unsigned long - Long& operator= (unsigned long v) - { - set(PyLong_FromUnsignedLong (v), true); - return *this; - } - }; - -#ifdef HAVE_LONG_LONG - // =============================================== - // class LongLong - class LongLong: public Object - { - public: - // Constructor - explicit LongLong (PyObject *pyob, bool owned = false): Object (pyob, owned) - { - validate(); - } - - LongLong (const LongLong& ob): Object(ob.ptr()) - { - validate(); - } - // create from long long - explicit LongLong (PY_LONG_LONG v = 0L) - : Object(PyLong_FromLongLong(v), true) - { - validate(); - } - // create from unsigned long long - explicit LongLong (unsigned PY_LONG_LONG v) - : Object(PyLong_FromUnsignedLongLong(v), true) - { - validate(); - } - // create from long - explicit LongLong (long v) - : Object(PyLong_FromLongLong(v), true) - { - validate(); - } - // create from unsigned long - explicit LongLong (unsigned long v) - : Object(PyLong_FromUnsignedLongLong(v), true) - { - validate(); - } - // create from int - explicit LongLong (int v) - : Object(PyLong_FromLongLong(static_cast(v)), true) - { - validate(); - } - - // try to create from any object - LongLong (const Object& ob) - : Object(PyNumber_Long(*ob), true) - { - validate(); - } - - // Assignment acquires new ownership of pointer - - LongLong& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - LongLong& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set (PyNumber_Long(rhsp), true); - return *this; - } - // Membership - virtual bool accepts (PyObject *pyob) const - { - return pyob && Py::_Long_Check (pyob); - } - // convert to long long - operator PY_LONG_LONG() const - { - return PyLong_AsLongLong (ptr()); - } - // convert to unsigned long - operator unsigned PY_LONG_LONG() const - { - return PyLong_AsUnsignedLongLong (ptr()); - } - // convert to long - operator long() const - { - return PyLong_AsLong (ptr()); - } - // convert to unsigned - operator unsigned long() const - { - return PyLong_AsUnsignedLong (ptr()); - } - operator double() const - { - return PyLong_AsDouble (ptr()); - } - // assign from an int - LongLong& operator= (int v) - { - set(PyLong_FromLongLong (long(v)), true); - return *this; - } - // assign from long long - LongLong& operator= (PY_LONG_LONG v) - { - set(PyLong_FromLongLong (v), true); - return *this; - } - // assign from unsigned long long - LongLong& operator= (unsigned PY_LONG_LONG v) - { - set(PyLong_FromUnsignedLongLong (v), true); - return *this; - } - // assign from long - LongLong& operator= (long v) - { - set(PyLong_FromLongLong (v), true); - return *this; - } - // assign from unsigned long - LongLong& operator= (unsigned long v) - { - set(PyLong_FromUnsignedLongLong (v), true); - return *this; - } - }; -#endif - - // =============================================== - // class Float - // - class Float: public Object - { - public: - // Constructor - explicit Float (PyObject *pyob, bool owned = false): Object(pyob, owned) - { - validate(); - } - - Float (const Float& f): Object(f) - { - validate(); - } - - // make from double - explicit Float (double v=0.0) - : Object(PyFloat_FromDouble (v), true) - { - validate(); - } - - // try to make from any object - Float (const Object& ob) - : Object(PyNumber_Float(*ob), true) - { - validate(); - } - - Float& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - Float& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set (PyNumber_Float(rhsp), true); - return *this; - } - // Membership - virtual bool accepts (PyObject *pyob) const - { - return pyob && Py::_Float_Check (pyob); - } - // convert to double - operator double () const - { - return PyFloat_AsDouble (ptr()); - } - // assign from a double - Float& operator= (double v) - { - set(PyFloat_FromDouble (v), true); - return *this; - } - // assign from an int - Float& operator= (int v) - { - set(PyFloat_FromDouble (double(v)), true); - return *this; - } - // assign from long - Float& operator= (long v) - { - set(PyFloat_FromDouble (double(v)), true); - return *this; - } - // assign from an Int - Float& operator= (const Int& iob) - { - set(PyFloat_FromDouble (double(long(iob))), true); - return *this; - } - }; - - // =============================================== - // class Complex - class Complex: public Object - { - public: - // Constructor - explicit Complex (PyObject *pyob, bool owned = false): Object(pyob, owned) - { - validate(); - } - - Complex (const Complex& f): Object(f) - { - validate(); - } - - // make from double - explicit Complex (double v=0.0, double w=0.0) - :Object(PyComplex_FromDoubles (v, w), true) - { - validate(); - } - - Complex& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - Complex& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set (rhsp); - return *this; - } - // Membership - virtual bool accepts (PyObject *pyob) const - { - return pyob && Py::_Complex_Check (pyob); - } - // convert to Py_complex - operator Py_complex () const - { - return PyComplex_AsCComplex (ptr()); - } - // assign from a Py_complex - Complex& operator= (const Py_complex& v) - { - set(PyComplex_FromCComplex (v), true); - return *this; - } - // assign from a double - Complex& operator= (double v) - { - set(PyComplex_FromDoubles (v, 0.0), true); - return *this; - } - // assign from an int - Complex& operator= (int v) - { - set(PyComplex_FromDoubles (double(v), 0.0), true); - return *this; - } - // assign from long - Complex& operator= (long v) - { - set(PyComplex_FromDoubles (double(v), 0.0), true); - return *this; - } - // assign from an Int - Complex& operator= (const Int& iob) - { - set(PyComplex_FromDoubles (double(long(iob)), 0.0), true); - return *this; - } - - double real() const - { - return PyComplex_RealAsDouble(ptr()); - } - - double imag() const - { - return PyComplex_ImagAsDouble(ptr()); - } - }; - // Sequences - // Sequences are here represented as sequences of items of type T. - // The base class SeqBase represents that. - // In basic Python T is always "Object". - - // seqref is what you get if you get elements from a non-const SeqBase. - // Note: seqref could probably be a nested class in SeqBase but that might stress - // some compilers needlessly. Simlarly for mapref later. - - // While this class is not intended for enduser use, it needs some public - // constructors for the benefit of the STL. - - // See Scott Meyer's More Essential C++ for a description of proxies. - // This application is even more complicated. We are doing an unusual thing - // in having a double proxy. If we want the STL to work - // properly we have to compromise by storing the rvalue inside. The - // entire Object API is repeated so that things like s[i].isList() will - // work properly. - - // Still, once in a while a weird compiler message may occur using expressions like x[i] - // Changing them to Object(x[i]) helps the compiler to understand that the - // conversion of a seqref to an Object is wanted. - - template - class seqref - { - protected: - SeqBase& s; // the sequence - int offset; // item number - T the_item; // lvalue - public: - - seqref (SeqBase& seq, sequence_index_type j) - : s(seq), offset(j), the_item (s.getItem(j)) - {} - - seqref (const seqref& range) - : s(range.s), offset(range.offset), the_item(range.the_item) - {} - - // TMM: added this seqref ctor for use with STL algorithms - seqref (Object& obj) - : s(dynamic_cast< SeqBase&>(obj)) - , offset( NULL ) - , the_item(s.getItem(offset)) - {} - ~seqref() - {} - - operator T() const - { // rvalue - return the_item; - } - - seqref& operator=(const seqref& rhs) - { //used as lvalue - the_item = rhs.the_item; - s.setItem(offset, the_item); - return *this; - } - - seqref& operator=(const T& ob) - { // used as lvalue - the_item = ob; - s.setItem(offset, ob); - return *this; - } - - // forward everything else to the item - PyObject* ptr () const - { - return the_item.ptr(); - } - - int reference_count () const - { // the reference count - return the_item.reference_count(); - } - - Type type () const - { - return the_item.type(); - } - - String str () const; - - String repr () const; - - bool hasAttr (const std::string& attr_name) const - { - return the_item.hasAttr(attr_name); - } - - Object getAttr (const std::string& attr_name) const - { - return the_item.getAttr(attr_name); - } - - Object getItem (const Object& key) const - { - return the_item.getItem(key); - } - - long hashValue () const - { - return the_item.hashValue(); - } - - bool isCallable () const - { - return the_item.isCallable(); - } - - bool isInstance () const - { - return the_item.isInstance(); - } - - bool isDict () const - { - return the_item.isDict(); - } - - bool isList () const - { - return the_item.isList(); - } - - bool isMapping () const - { - return the_item.isMapping(); - } - - bool isNumeric () const - { - return the_item.isNumeric(); - } - - bool isSequence () const - { - return the_item.isSequence(); - } - - bool isTrue () const - { - return the_item.isTrue(); - } - - bool isType (const Type& t) const - { - return the_item.isType (t); - } - - bool isTuple() const - { - return the_item.isTuple(); - } - - bool isString() const - { - return the_item.isString(); - } - // Commands - void setAttr (const std::string& attr_name, const Object& value) - { - the_item.setAttr(attr_name, value); - } - - void delAttr (const std::string& attr_name) - { - the_item.delAttr(attr_name); - } - - void delItem (const Object& key) - { - the_item.delItem(key); - } - - bool operator==(const Object& o2) const - { - return the_item == o2; - } - - bool operator!=(const Object& o2) const - { - return the_item != o2; - } - - bool operator>=(const Object& o2) const - { - return the_item >= o2; - } - - bool operator<=(const Object& o2) const - { - return the_item <= o2; - } - - bool operator<(const Object& o2) const - { - return the_item < o2; - } - - bool operator>(const Object& o2) const - { - return the_item > o2; - } - }; // end of seqref - - - // class SeqBase - // ...the base class for all sequence types - - template - class SeqBase: public Object - { - public: - // STL definitions - typedef size_t size_type; - typedef seqref reference; - typedef T const_reference; - typedef seqref* pointer; - typedef int difference_type; - typedef T value_type; // TMM: 26Jun'01 - - virtual size_type max_size() const - { - return std::string::npos; // ? - } - - virtual size_type capacity() const - { - return size(); - } - - virtual void swap(SeqBase& c) - { - SeqBase temp = c; - c = ptr(); - set(temp.ptr()); - } - - virtual size_type size () const - { - return PySequence_Length (ptr()); - } - - explicit SeqBase () - :Object(PyTuple_New(0), true) - { - validate(); - } - - explicit SeqBase (PyObject* pyob, bool owned=false) - : Object(pyob, owned) - { - validate(); - } - - SeqBase (const Object& ob): Object(ob) - { - validate(); - } - - // Assignment acquires new ownership of pointer - - SeqBase& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - SeqBase& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set (rhsp); - return *this; - } - - virtual bool accepts (PyObject *pyob) const - { - return pyob && PySequence_Check (pyob); - } - - size_type length () const - { - return PySequence_Length (ptr()); - } - - // Element access - const T operator[](sequence_index_type index) const - { - return getItem(index); - } - - seqref operator[](sequence_index_type index) - { - return seqref(*this, index); - } - - virtual T getItem (sequence_index_type i) const - { - return T(asObject(PySequence_GetItem (ptr(), i))); - } - - virtual void setItem (sequence_index_type i, const T& ob) - { - if (PySequence_SetItem (ptr(), i, *ob) == -1) - { - throw Exception(); - } - } - - SeqBase repeat (int count) const - { - return SeqBase (PySequence_Repeat (ptr(), count), true); - } - - SeqBase concat (const SeqBase& other) const - { - return SeqBase (PySequence_Concat(ptr(), *other), true); - } - - // more STL compatability - const T front () const - { - return getItem(0); - } - - seqref front() - { - return seqref(this, 0); - } - - const T back () const - { - return getItem(size()-1); - } - - seqref back() - { - return seqref(this, size()-1); - } - - void verify_length(size_type required_size) const - { - if (size() != required_size) - throw IndexError ("Unexpected SeqBase length."); - } - - void verify_length(size_type min_size, size_type max_size) const - { - size_type n = size(); - if (n < min_size || n > max_size) - throw IndexError ("Unexpected SeqBase length."); - } - - class iterator - : public random_access_iterator_parent(seqref) - { - protected: - friend class SeqBase; - SeqBase* seq; - int count; - - public: - ~iterator () - {} - - iterator () - : seq( 0 ) - , count( 0 ) - {} - - iterator (SeqBase* s, int where) - : seq( s ) - , count( where ) - {} - - iterator (const iterator& other) - : seq( other.seq ) - , count( other.count ) - {} - - bool eql (const iterator& other) const - { - return (seq->ptr() == other.seq->ptr()) && (count == other.count); - } - - bool neq (const iterator& other) const - { - return (seq->ptr() != other.seq->ptr()) || (count != other.count); - } - - bool lss (const iterator& other) const - { - return (count < other.count); - } - - bool gtr (const iterator& other) const - { - return (count > other.count); - } - - bool leq (const iterator& other) const - { - return (count <= other.count); - } - - bool geq (const iterator& other) const - { - return (count >= other.count); - } - - seqref operator*() - { - return seqref(*seq, count); - } - - seqref operator[] (sequence_index_type i) - { - return seqref(*seq, count + i); - } - - iterator& operator=(const iterator& other) - { - if (this == &other) return *this; - seq = other.seq; - count = other.count; - return *this; - } - - iterator operator+(int n) const - { - return iterator(seq, count + n); - } - - iterator operator-(int n) const - { - return iterator(seq, count - n); - } - - iterator& operator+=(int n) - { - count = count + n; - return *this; - } - - iterator& operator-=(int n) - { - count = count - n; - return *this; - } - - int operator-(const iterator& other) const - { - if (*seq != *other.seq) - throw RuntimeError ("SeqBase::iterator comparison error"); - return count - other.count; - } - - // prefix ++ - iterator& operator++ () - { count++; return *this;} - // postfix ++ - iterator operator++ (int) - { return iterator(seq, count++);} - // prefix -- - iterator& operator-- () - { count--; return *this;} - // postfix -- - iterator operator-- (int) - { return iterator(seq, count--);} - - std::string diagnose() const - { - std::OSTRSTREAM oss; - oss << "iterator diagnosis " << seq << ", " << count << std::ends; - return std::string(oss.str()); - } - }; // end of class SeqBase::iterator - - iterator begin () - { - return iterator(this, 0); - } - - iterator end () - { - return iterator(this, length()); - } - - class const_iterator - : public random_access_iterator_parent(const Object) - { - protected: - friend class SeqBase; - const SeqBase* seq; - sequence_index_type count; - - private: - const_iterator (const SeqBase* s, int where) - : seq( s ) - , count( where ) - {} - - public: - ~const_iterator () - {} - - const_iterator () - : seq( 0 ) - , count( 0 ) - {} - - const_iterator(const const_iterator& other) - : seq( other.seq ) - , count( other.count ) - {} - - const T operator*() const - { - return seq->getItem(count); - } - - const T operator[] (sequence_index_type i) const - { - return seq->getItem(count + i); - } - - const_iterator& operator=(const const_iterator& other) - { - if (this == &other) return *this; - seq = other.seq; - count = other.count; - return *this; - } - - const_iterator operator+(int n) const - { - return const_iterator(seq, count + n); - } - - bool eql (const const_iterator& other) const - { - return (seq->ptr() == other.seq->ptr()) && (count == other.count); - } - - bool neq (const const_iterator& other) const - { - return (seq->ptr() != other.seq->ptr()) || (count != other.count); - } - - bool lss (const const_iterator& other) const - { - return (count < other.count); - } - - bool gtr (const const_iterator& other) const - { - return (count > other.count); - } - - bool leq (const const_iterator& other) const - { - return (count <= other.count); - } - - bool geq (const const_iterator& other) const - { - return (count >= other.count); - } - - const_iterator operator-(int n) - { - return const_iterator(seq, count - n); - } - - const_iterator& operator+=(int n) - { - count = count + n; - return *this; - } - - const_iterator& operator-=(int n) - { - count = count - n; - return *this; - } - - int operator-(const const_iterator& other) const - { - if (*seq != *other.seq) - throw RuntimeError ("SeqBase::const_iterator::- error"); - return count - other.count; - } - // prefix ++ - const_iterator& operator++ () - { count++; return *this;} - // postfix ++ - const_iterator operator++ (int) - { return const_iterator(seq, count++);} - // prefix -- - const_iterator& operator-- () - { count--; return *this;} - // postfix -- - const_iterator operator-- (int) - { return const_iterator(seq, count--);} - }; // end of class SeqBase::const_iterator - - const_iterator begin () const - { - return const_iterator(this, 0); - } - - const_iterator end () const - { - return const_iterator(this, length()); - } - }; - - // Here's an important typedef you might miss if reading too fast... - typedef SeqBase Sequence; - - template bool operator==(const EXPLICIT_TYPENAME SeqBase::iterator& left, const EXPLICIT_TYPENAME SeqBase::iterator& right); - template bool operator!=(const EXPLICIT_TYPENAME SeqBase::iterator& left, const EXPLICIT_TYPENAME SeqBase::iterator& right); - template bool operator< (const EXPLICIT_TYPENAME SeqBase::iterator& left, const EXPLICIT_TYPENAME SeqBase::iterator& right); - template bool operator> (const EXPLICIT_TYPENAME SeqBase::iterator& left, const EXPLICIT_TYPENAME SeqBase::iterator& right); - template bool operator<=(const EXPLICIT_TYPENAME SeqBase::iterator& left, const EXPLICIT_TYPENAME SeqBase::iterator& right); - template bool operator>=(const EXPLICIT_TYPENAME SeqBase::iterator& left, const EXPLICIT_TYPENAME SeqBase::iterator& right); - - template bool operator==(const EXPLICIT_TYPENAME SeqBase::const_iterator& left, const EXPLICIT_TYPENAME SeqBase::const_iterator& right); - template bool operator!=(const EXPLICIT_TYPENAME SeqBase::const_iterator& left, const EXPLICIT_TYPENAME SeqBase::const_iterator& right); - template bool operator< (const EXPLICIT_TYPENAME SeqBase::const_iterator& left, const EXPLICIT_TYPENAME SeqBase::const_iterator& right); - template bool operator> (const EXPLICIT_TYPENAME SeqBase::const_iterator& left, const EXPLICIT_TYPENAME SeqBase::const_iterator& right); - template bool operator<=(const EXPLICIT_TYPENAME SeqBase::const_iterator& left, const EXPLICIT_TYPENAME SeqBase::const_iterator& right); - template bool operator>=(const EXPLICIT_TYPENAME SeqBase::const_iterator& left, const EXPLICIT_TYPENAME SeqBase::const_iterator& right); - - - extern bool operator==(const Sequence::iterator& left, const Sequence::iterator& right); - extern bool operator!=(const Sequence::iterator& left, const Sequence::iterator& right); - extern bool operator< (const Sequence::iterator& left, const Sequence::iterator& right); - extern bool operator> (const Sequence::iterator& left, const Sequence::iterator& right); - extern bool operator<=(const Sequence::iterator& left, const Sequence::iterator& right); - extern bool operator>=(const Sequence::iterator& left, const Sequence::iterator& right); - - extern bool operator==(const Sequence::const_iterator& left, const Sequence::const_iterator& right); - extern bool operator!=(const Sequence::const_iterator& left, const Sequence::const_iterator& right); - extern bool operator< (const Sequence::const_iterator& left, const Sequence::const_iterator& right); - extern bool operator> (const Sequence::const_iterator& left, const Sequence::const_iterator& right); - extern bool operator<=(const Sequence::const_iterator& left, const Sequence::const_iterator& right); - extern bool operator>=(const Sequence::const_iterator& left, const Sequence::const_iterator& right); - - // ================================================== - // class Char - // Python strings return strings as individual elements. - // I'll try having a class Char which is a String of length 1 - // - typedef std::basic_string unicodestring; - extern Py_UNICODE unicode_null_string[1]; - - class Char: public Object - { - public: - explicit Char (PyObject *pyob, bool owned = false): Object(pyob, owned) - { - validate(); - } - - Char (const Object& ob): Object(ob) - { - validate(); - } - - Char (const std::string& v = "") - :Object(PyString_FromStringAndSize (const_cast(v.c_str()),1), true) - { - validate(); - } - - Char (char v) - : Object(PyString_FromStringAndSize (&v, 1), true) - { - validate(); - } - - Char (Py_UNICODE v) - : Object(PyUnicode_FromUnicode (&v, 1), true) - { - validate(); - } - // Assignment acquires new ownership of pointer - Char& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - Char& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set (rhsp); - return *this; - } - - // Membership - virtual bool accepts (PyObject *pyob) const - { - return pyob && (Py::_String_Check(pyob) || Py::_Unicode_Check(pyob)) && PySequence_Length (pyob) == 1; - } - - // Assignment from C string - Char& operator= (const std::string& v) - { - set(PyString_FromStringAndSize (const_cast(v.c_str()),1), true); - return *this; - } - - Char& operator= (char v) - { - set(PyString_FromStringAndSize (&v, 1), true); - return *this; - } - - Char& operator= (const unicodestring& v) - { - set(PyUnicode_FromUnicode (const_cast(v.data()),1), true); - return *this; - } - - Char& operator= (Py_UNICODE v) - { - set(PyUnicode_FromUnicode (&v, 1), true); - return *this; - } - - // Conversion - operator String() const; - - operator std::string () const - { - return std::string(PyString_AsString (ptr())); - } - }; - - class String: public SeqBase - { - public: - virtual size_type capacity() const - { - return max_size(); - } - - explicit String (PyObject *pyob, bool owned = false): SeqBase(pyob, owned) - { - validate(); - } - - String (const Object& ob): SeqBase(ob) - { - validate(); - } - - String() - : SeqBase( PyString_FromStringAndSize( "", 0 ), true ) - { - validate(); - } - - String( const std::string& v ) - : SeqBase( PyString_FromStringAndSize( const_cast(v.data()), - static_cast( v.length() ) ), true ) - { - validate(); - } - - String( const char *s, const char *encoding, const char *error="strict" ) - : SeqBase( PyUnicode_Decode( s, strlen( s ), encoding, error ), true ) - { - validate(); - } - - String( const char *s, int len, const char *encoding, const char *error="strict" ) - : SeqBase( PyUnicode_Decode( s, len, encoding, error ), true ) - { - validate(); - } - - String( const std::string &s, const char *encoding, const char *error="strict" ) - : SeqBase( PyUnicode_Decode( s.c_str(), s.length(), encoding, error ), true ) - { - validate(); - } - - String( const std::string& v, std::string::size_type vsize ) - : SeqBase(PyString_FromStringAndSize( const_cast(v.data()), - static_cast( vsize ) ), true) - { - validate(); - } - - String( const char *v, int vsize ) - : SeqBase(PyString_FromStringAndSize( const_cast(v), vsize ), true ) - { - validate(); - } - - String( const char* v ) - : SeqBase( PyString_FromString( v ), true ) - { - validate(); - } - - // Assignment acquires new ownership of pointer - String& operator= ( const Object& rhs ) - { - return *this = *rhs; - } - - String& operator= (PyObject* rhsp) - { - if( ptr() == rhsp ) - return *this; - set (rhsp); - return *this; - } - // Membership - virtual bool accepts (PyObject *pyob) const - { - return pyob && (Py::_String_Check(pyob) || Py::_Unicode_Check(pyob)); - } - - // Assignment from C string - String& operator= (const std::string& v) - { - set( PyString_FromStringAndSize( const_cast( v.data() ), - static_cast( v.length() ) ), true ); - return *this; - } - String& operator= (const unicodestring& v) - { - set( PyUnicode_FromUnicode( const_cast( v.data() ), - static_cast( v.length() ) ), true ); - return *this; - } - - - // Encode - String encode( const char *encoding, const char *error="strict" ) - { - if( isUnicode() ) - { - return String( PyUnicode_AsEncodedString( ptr(), encoding, error ) ); - } - else - { - return String( PyString_AsEncodedObject( ptr(), encoding, error ) ); - } - } - - String decode( const char *encoding, const char *error="strict" ) - { - return Object( PyString_AsDecodedObject( ptr(), encoding, error ) ); - } - - // Queries - virtual size_type size () const - { - if( isUnicode() ) - { - return static_cast( PyUnicode_GET_SIZE (ptr()) ); - } - else - { - return static_cast( PyString_Size (ptr()) ); - } - } - - operator std::string () const - { - return as_std_string(); - } - - std::string as_std_string() const - { - if( isUnicode() ) - { - throw TypeError("cannot return std::string from Unicode object"); - } - else - { - return std::string( PyString_AsString( ptr() ), static_cast( PyString_Size( ptr() ) ) ); - } - } - - unicodestring as_unicodestring() const - { - if( isUnicode() ) - { - return unicodestring( PyUnicode_AS_UNICODE( ptr() ), - static_cast( PyUnicode_GET_SIZE( ptr() ) ) ); - } - else - { - throw TypeError("can only return unicodestring from Unicode object"); - } - } - }; - - // ================================================== - // class Tuple - class Tuple: public Sequence - { - public: - virtual void setItem (sequence_index_type offset, const Object&ob) - { - // note PyTuple_SetItem is a thief... - if(PyTuple_SetItem (ptr(), offset, new_reference_to(ob)) == -1) - { - throw Exception(); - } - } - - // Constructor - explicit Tuple (PyObject *pyob, bool owned = false): Sequence (pyob, owned) - { - validate(); - } - - Tuple (const Object& ob): Sequence(ob) - { - validate(); - } - - // New tuple of a given size - explicit Tuple (int size = 0) - { - set(PyTuple_New (size), true); - validate (); - for (sequence_index_type i=0; i < size; i++) - { - if(PyTuple_SetItem (ptr(), i, new_reference_to(Py::_None())) == -1) - { - throw Exception(); - } - } - } - // Tuple from any sequence - explicit Tuple (const Sequence& s) - { - sequence_index_type limit( sequence_index_type( s.length() ) ); - - set(PyTuple_New (limit), true); - validate(); - - for(sequence_index_type i=0; i < limit; i++) - { - if(PyTuple_SetItem (ptr(), i, new_reference_to(s[i])) == -1) - { - throw Exception(); - } - } - } - // Assignment acquires new ownership of pointer - - Tuple& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - Tuple& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set (rhsp); - return *this; - } - // Membership - virtual bool accepts (PyObject *pyob) const - { - return pyob && Py::_Tuple_Check (pyob); - } - - Tuple getSlice (int i, int j) const - { - return Tuple (PySequence_GetSlice (ptr(), i, j), true); - } - - }; - - // ================================================== - // class List - - class List: public Sequence - { - public: - // Constructor - explicit List (PyObject *pyob, bool owned = false): Sequence(pyob, owned) - { - validate(); - } - List (const Object& ob): Sequence(ob) - { - validate(); - } - // Creation at a fixed size - List (int size = 0) - { - set(PyList_New (size), true); - validate(); - for (sequence_index_type i=0; i < size; i++) - { - if(PyList_SetItem (ptr(), i, new_reference_to(Py::_None())) == -1) - { - throw Exception(); - } - } - } - - // List from a sequence - List (const Sequence& s): Sequence() - { - int n = (int)s.length(); - set(PyList_New (n), true); - validate(); - for (sequence_index_type i=0; i < n; i++) - { - if(PyList_SetItem (ptr(), i, new_reference_to(s[i])) == -1) - { - throw Exception(); - } - } - } - - virtual size_type capacity() const - { - return max_size(); - } - // Assignment acquires new ownership of pointer - - List& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - List& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set (rhsp); - return *this; - } - // Membership - virtual bool accepts (PyObject *pyob) const - { - return pyob && Py::_List_Check (pyob); - } - - List getSlice (int i, int j) const - { - return List (PyList_GetSlice (ptr(), i, j), true); - } - - void setSlice (int i, int j, const Object& v) - { - if(PyList_SetSlice (ptr(), i, j, *v) == -1) - { - throw Exception(); - } - } - - void append (const Object& ob) - { - if(PyList_Append (ptr(), *ob) == -1) - { - throw Exception(); - } - } - - void insert (int i, const Object& ob) - { - if(PyList_Insert (ptr(), i, *ob) == -1) - { - throw Exception(); - } - } - - void sort () - { - if(PyList_Sort(ptr()) == -1) - { - throw Exception(); - } - } - - void reverse () - { - if(PyList_Reverse(ptr()) == -1) - { - throw Exception(); - } - } - }; - - - // Mappings - // ================================================== - template - class mapref - { - protected: - MapBase& s; // the map - Object key; // item key - T the_item; - - public: - mapref (MapBase& map, const std::string& k) - : s(map), the_item() - { - key = String(k); - if(map.hasKey(key)) the_item = map.getItem(key); - } - - mapref (MapBase& map, const Object& k) - : s(map), key(k), the_item() - { - if(map.hasKey(key)) the_item = map.getItem(key); - } - - virtual ~mapref() - {} - - // MapBase stuff - // lvalue - mapref& operator=(const mapref& other) - { - if(this == &other) return *this; - the_item = other.the_item; - s.setItem(key, other.the_item); - return *this; - } - - mapref& operator= (const T& ob) - { - the_item = ob; - s.setItem (key, ob); - return *this; - } - - // rvalue - operator T() const - { - return the_item; - } - - // forward everything else to the_item - PyObject* ptr () const - { - return the_item.ptr(); - } - - int reference_count () const - { // the mapref count - return the_item.reference_count(); - } - - Type type () const - { - return the_item.type(); - } - - String str () const - { - return the_item.str(); - } - - String repr () const - { - return the_item.repr(); - } - - bool hasAttr (const std::string& attr_name) const - { - return the_item.hasAttr(attr_name); - } - - Object getAttr (const std::string& attr_name) const - { - return the_item.getAttr(attr_name); - } - - Object getItem (const Object& k) const - { - return the_item.getItem(k); - } - - long hashValue () const - { - return the_item.hashValue(); - } - - bool isCallable () const - { - return the_item.isCallable(); - } - - bool isInstance () const - { - return the_item.isInstance(); - } - - bool isList () const - { - return the_item.isList(); - } - - bool isMapping () const - { - return the_item.isMapping(); - } - - bool isNumeric () const - { - return the_item.isNumeric(); - } - - bool isSequence () const - { - return the_item.isSequence(); - } - - bool isTrue () const - { - return the_item.isTrue(); - } - - bool isType (const Type& t) const - { - return the_item.isType (t); - } - - bool isTuple() const - { - return the_item.isTuple(); - } - - bool isString() const - { - return the_item.isString(); - } - - // Commands - void setAttr (const std::string& attr_name, const Object& value) - { - the_item.setAttr(attr_name, value); - } - - void delAttr (const std::string& attr_name) - { - the_item.delAttr(attr_name); - } - - void delItem (const Object& k) - { - the_item.delItem(k); - } - }; // end of mapref - - // TMM: now for mapref - template< class T > - bool operator==(const mapref& left, const mapref& right) - { - return true; // NOT completed. - } - - template< class T > - bool operator!=(const mapref& left, const mapref& right) - { - return true; // not completed. - } - - template - class MapBase: public Object - { - protected: - explicit MapBase() - {} - public: - // reference: proxy class for implementing [] - // TMM: 26Jun'01 - the types - // If you assume that Python mapping is a hash_map... - // hash_map::value_type is not assignable, but - // (*it).second = data must be a valid expression - typedef size_t size_type; - typedef Object key_type; - typedef mapref data_type; - typedef std::pair< const T, T > value_type; - typedef std::pair< const T, mapref > reference; - typedef const std::pair< const T, const T > const_reference; - typedef std::pair< const T, mapref > pointer; - - // Constructor - explicit MapBase (PyObject *pyob, bool owned = false): Object(pyob, owned) - { - validate(); - } - - // TMM: 02Jul'01 - changed MapBase to Object in next line - MapBase (const Object& ob): Object(ob) - { - validate(); - } - - // Assignment acquires new ownership of pointer - MapBase& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - MapBase& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set (rhsp); - return *this; - } - // Membership - virtual bool accepts (PyObject *pyob) const - { - return pyob && PyMapping_Check(pyob); - } - - // Clear -- PyMapping Clear is missing - // - - void clear () - { - List k = keys(); - for(List::iterator i = k.begin(); i != k.end(); i++) - { - delItem(*i); - } - } - - virtual size_type size() const - { - return PyMapping_Length (ptr()); - } - - // Element Access - T operator[](const std::string& key) const - { - return getItem(key); - } - - T operator[](const Object& key) const - { - return getItem(key); - } - - mapref operator[](const std::string& key) - { - return mapref(*this, key); - } - - mapref operator[](const Object& key) - { - return mapref(*this, key); - } - - int length () const - { - return PyMapping_Length (ptr()); - } - - bool hasKey (const std::string& s) const - { - return PyMapping_HasKeyString (ptr(),const_cast(s.c_str())) != 0; - } - - bool hasKey (const Object& s) const - { - return PyMapping_HasKey (ptr(), s.ptr()) != 0; - } - - T getItem (const std::string& s) const - { - return T( - asObject(PyMapping_GetItemString (ptr(),const_cast(s.c_str()))) - ); - } - - T getItem (const Object& s) const - { - return T( - asObject(PyObject_GetItem (ptr(), s.ptr())) - ); - } - - virtual void setItem (const char *s, const Object& ob) - { - if (PyMapping_SetItemString (ptr(), const_cast(s), *ob) == -1) - { - throw Exception(); - } - } - - virtual void setItem (const std::string& s, const Object& ob) - { - if (PyMapping_SetItemString (ptr(), const_cast(s.c_str()), *ob) == -1) - { - throw Exception(); - } - } - - virtual void setItem (const Object& s, const Object& ob) - { - if (PyObject_SetItem (ptr(), s.ptr(), ob.ptr()) == -1) - { - throw Exception(); - } - } - - void delItem (const std::string& s) - { - if (PyMapping_DelItemString (ptr(), const_cast(s.c_str())) == -1) - { - throw Exception(); - } - } - - void delItem (const Object& s) - { - if (PyMapping_DelItem (ptr(), *s) == -1) - { - throw Exception(); - } - } - // Queries - List keys () const - { - return List(PyMapping_Keys(ptr()), true); - } - - List values () const - { // each returned item is a (key, value) pair - return List(PyMapping_Values(ptr()), true); - } - - List items () const - { - return List(PyMapping_Items(ptr()), true); - } - - class iterator - { - // : public forward_iterator_parent( std::pair ) { - protected: - typedef std::forward_iterator_tag iterator_category; - typedef std::pair< const T, T > value_type; - typedef int difference_type; - typedef std::pair< const T, mapref > pointer; - typedef std::pair< const T, mapref > reference; - - friend class MapBase; - // - MapBase* map; - List keys; // for iterating over the map - int pos; // index into the keys - - private: - iterator( MapBase* m, List k, int p ) - : map( m ) - , keys( k ) - , pos( p ) - {} - - public: - ~iterator () - {} - - iterator () - : map( 0 ) - , keys() - , pos() - {} - - iterator (MapBase* m, bool end = false ) - : map( m ) - , keys( m->keys() ) - , pos( end ? keys.length() : 0 ) - {} - - iterator (const iterator& other) - : map( other.map ) - , keys( other.keys ) - , pos( other.pos ) - {} - - reference operator*() - { - Object key = keys[ pos ]; - return std::make_pair(key, mapref(*map,key)); - } - - iterator& operator=(const iterator& other) - { - if (this == &other) - return *this; - map = other.map; - keys = other.keys; - pos = other.pos; - return *this; - } - - bool eql(const iterator& right) const - { - return map->ptr() == right.map->ptr() && pos == right.pos; - } - bool neq( const iterator& right ) const - { - return map->ptr() != right.map->ptr() || pos != right.pos; - } - - // pointer operator->() { - // return ; - // } - - // prefix ++ - iterator& operator++ () - { pos++; return *this;} - // postfix ++ - iterator operator++ (int) - { return iterator(map, keys, pos++);} - // prefix -- - iterator& operator-- () - { pos--; return *this;} - // postfix -- - iterator operator-- (int) - { return iterator(map, keys, pos--);} - - std::string diagnose() const - { - std::OSTRSTREAM oss; - oss << "iterator diagnosis " << map << ", " << pos << std::ends; - return std::string(oss.str()); - } - }; // end of class MapBase::iterator - - iterator begin () - { - return iterator(this); - } - - iterator end () - { - return iterator(this, true); - } - - class const_iterator - { - protected: - typedef std::forward_iterator_tag iterator_category; - typedef const std::pair< const T, T > value_type; - typedef int difference_type; - typedef const std::pair< const T, T > pointer; - typedef const std::pair< const T, T > reference; - - friend class MapBase; - const MapBase* map; - List keys; // for iterating over the map - int pos; // index into the keys - - private: - const_iterator( MapBase* m, List k, int p ) - : map( m ) - , keys( k ) - , pos( p ) - {} - - public: - ~const_iterator () - {} - - const_iterator () - : map( 0 ) - , keys() - , pos() - {} - - const_iterator (MapBase* m, bool end = false ) - : map( m ) - , keys( m->keys() ) - , pos( end ? keys.length() : 0 ) - {} - - const_iterator(const const_iterator& other) - : map( other.map ) - , keys( other.keys ) - , pos( other.pos ) - {} - - bool eql(const const_iterator& right) const - { - return map->ptr() == right.map->ptr() && pos == right.pos; - } - - bool neq( const const_iterator& right ) const - { - return map->ptr() != right.map->ptr() || pos != right.pos; - } - - const_reference operator*() - { - Object key = keys[ pos ]; - return std::make_pair( key, mapref( *map, key ) ); - } - - const_iterator& operator=(const const_iterator& other) - { - if (this == &other) return *this; - map = other.map; - keys = other.keys; - pos = other.pos; - return *this; - } - - // prefix ++ - const_iterator& operator++ () - { pos++; return *this;} - // postfix ++ - const_iterator operator++ (int) - { return const_iterator(map, keys, pos++);} - // prefix -- - const_iterator& operator-- () - { pos--; return *this;} - // postfix -- - const_iterator operator-- (int) - { return const_iterator(map, keys, pos--);} - }; // end of class MapBase::const_iterator - - const_iterator begin () const - { - return const_iterator(this); - } - - const_iterator end () const - { - return const_iterator(this, true); - } - - }; // end of MapBase - - typedef MapBase Mapping; - - template bool operator==(const EXPLICIT_TYPENAME MapBase::iterator& left, const EXPLICIT_TYPENAME MapBase::iterator& right); - template bool operator!=(const EXPLICIT_TYPENAME MapBase::iterator& left, const EXPLICIT_TYPENAME MapBase::iterator& right); - template bool operator==(const EXPLICIT_TYPENAME MapBase::const_iterator& left, const EXPLICIT_TYPENAME MapBase::const_iterator& right); - template bool operator!=(const EXPLICIT_TYPENAME MapBase::const_iterator& left, const EXPLICIT_TYPENAME MapBase::const_iterator& right); - - extern bool operator==(const Mapping::iterator& left, const Mapping::iterator& right); - extern bool operator!=(const Mapping::iterator& left, const Mapping::iterator& right); - extern bool operator==(const Mapping::const_iterator& left, const Mapping::const_iterator& right); - extern bool operator!=(const Mapping::const_iterator& left, const Mapping::const_iterator& right); - - - // ================================================== - // class Dict - class Dict: public Mapping - { - public: - // Constructor - explicit Dict (PyObject *pyob, bool owned=false): Mapping (pyob, owned) - { - validate(); - } - Dict (const Object& ob): Mapping(ob) - { - validate(); - } - // Creation - Dict () - { - set(PyDict_New (), true); - validate(); - } - // Assignment acquires new ownership of pointer - - Dict& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - Dict& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set(rhsp); - return *this; - } - // Membership - virtual bool accepts (PyObject *pyob) const - { - return pyob && Py::_Dict_Check (pyob); - } - }; - - class Callable: public Object - { - public: - // Constructor - explicit Callable (): Object() {} - explicit Callable (PyObject *pyob, bool owned = false): Object (pyob, owned) - { - validate(); - } - - Callable (const Object& ob): Object(ob) - { - validate(); - } - - // Assignment acquires new ownership of pointer - - Callable& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - Callable& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set (rhsp); - return *this; - } - - // Membership - virtual bool accepts (PyObject *pyob) const - { - return pyob && PyCallable_Check (pyob); - } - - // Call - Object apply(const Tuple& args) const - { - return asObject(PyObject_CallObject(ptr(), args.ptr())); - } - - // Call with keywords - Object apply(const Tuple& args, const Dict& kw) const - { - return asObject( PyEval_CallObjectWithKeywords( ptr(), args.ptr(), kw.ptr() ) ); - } - - Object apply(PyObject* pargs = 0) const - { - return apply (Tuple(pargs)); - } - }; - - class Module: public Object - { - public: - explicit Module (PyObject* pyob, bool owned = false): Object (pyob, owned) - { - validate(); - } - - // Construct from module name - explicit Module (const std::string&s): Object() - { - PyObject *m = PyImport_AddModule( const_cast(s.c_str()) ); - set( m, false ); - validate (); - } - - // Copy constructor acquires new ownership of pointer - Module (const Module& ob): Object(*ob) - { - validate(); - } - - Module& operator= (const Object& rhs) - { - return (*this = *rhs); - } - - Module& operator= (PyObject* rhsp) - { - if(ptr() == rhsp) return *this; - set(rhsp); - return *this; - } - - Dict getDict() - { - return Dict(PyModule_GetDict(ptr())); - // Caution -- PyModule_GetDict returns borrowed reference! - } - }; - - // Numeric interface - inline Object operator+ (const Object& a) - { - return asObject(PyNumber_Positive(*a)); - } - inline Object operator- (const Object& a) - { - return asObject(PyNumber_Negative(*a)); - } - - inline Object abs(const Object& a) - { - return asObject(PyNumber_Absolute(*a)); - } - - inline std::pair coerce(const Object& a, const Object& b) - { - PyObject *p1, *p2; - p1 = *a; - p2 = *b; - if(PyNumber_Coerce(&p1,&p2) == -1) - { - throw Exception(); - } - return std::pair(asObject(p1), asObject(p2)); - } - - inline Object operator+ (const Object& a, const Object& b) - { - return asObject(PyNumber_Add(*a, *b)); - } - inline Object operator+ (const Object& a, int j) - { - return asObject(PyNumber_Add(*a, *Int(j))); - } - inline Object operator+ (const Object& a, double v) - { - return asObject(PyNumber_Add(*a, *Float(v))); - } - inline Object operator+ (int j, const Object& b) - { - return asObject(PyNumber_Add(*Int(j), *b)); - } - inline Object operator+ (double v, const Object& b) - { - return asObject(PyNumber_Add(*Float(v), *b)); - } - - inline Object operator- (const Object& a, const Object& b) - { - return asObject(PyNumber_Subtract(*a, *b)); - } - inline Object operator- (const Object& a, int j) - { - return asObject(PyNumber_Subtract(*a, *Int(j))); - } - inline Object operator- (const Object& a, double v) - { - return asObject(PyNumber_Subtract(*a, *Float(v))); - } - inline Object operator- (int j, const Object& b) - { - return asObject(PyNumber_Subtract(*Int(j), *b)); - } - inline Object operator- (double v, const Object& b) - { - return asObject(PyNumber_Subtract(*Float(v), *b)); - } - - inline Object operator* (const Object& a, const Object& b) - { - return asObject(PyNumber_Multiply(*a, *b)); - } - inline Object operator* (const Object& a, int j) - { - return asObject(PyNumber_Multiply(*a, *Int(j))); - } - inline Object operator* (const Object& a, double v) - { - return asObject(PyNumber_Multiply(*a, *Float(v))); - } - inline Object operator* (int j, const Object& b) - { - return asObject(PyNumber_Multiply(*Int(j), *b)); - } - inline Object operator* (double v, const Object& b) - { - return asObject(PyNumber_Multiply(*Float(v), *b)); - } - - inline Object operator/ (const Object& a, const Object& b) - { - return asObject(PyNumber_Divide(*a, *b)); - } - inline Object operator/ (const Object& a, int j) - { - return asObject(PyNumber_Divide(*a, *Int(j))); - } - inline Object operator/ (const Object& a, double v) - { - return asObject(PyNumber_Divide(*a, *Float(v))); - } - inline Object operator/ (int j, const Object& b) - { - return asObject(PyNumber_Divide(*Int(j), *b)); - } - inline Object operator/ (double v, const Object& b) - { - return asObject(PyNumber_Divide(*Float(v), *b)); - } - - inline Object operator% (const Object& a, const Object& b) - { - return asObject(PyNumber_Remainder(*a, *b)); - } - inline Object operator% (const Object& a, int j) - { - return asObject(PyNumber_Remainder(*a, *Int(j))); - } - inline Object operator% (const Object& a, double v) - { - return asObject(PyNumber_Remainder(*a, *Float(v))); - } - inline Object operator% (int j, const Object& b) - { - return asObject(PyNumber_Remainder(*Int(j), *b)); - } - inline Object operator% (double v, const Object& b) - { - return asObject(PyNumber_Remainder(*Float(v), *b)); - } - - inline Object type(const Exception&) // return the type of the error - { - PyObject *ptype, *pvalue, *ptrace; - PyErr_Fetch(&ptype, &pvalue, &ptrace); - Object result; - if(ptype) result = ptype; - PyErr_Restore(ptype, pvalue, ptrace); - return result; - } - - inline Object value(const Exception&) // return the value of the error - { - PyObject *ptype, *pvalue, *ptrace; - PyErr_Fetch(&ptype, &pvalue, &ptrace); - Object result; - if(pvalue) result = pvalue; - PyErr_Restore(ptype, pvalue, ptrace); - return result; - } - - inline Object trace(const Exception&) // return the traceback of the error - { - PyObject *ptype, *pvalue, *ptrace; - PyErr_Fetch(&ptype, &pvalue, &ptrace); - Object result; - if(ptrace) result = ptrace; - PyErr_Restore(ptype, pvalue, ptrace); - return result; - } - -template -String seqref::str () const -{ - return the_item.str(); -} - -template -String seqref::repr () const -{ - return the_item.repr(); -} - -} // namespace Py -#endif // __CXX_Objects__h diff --git a/CXX5/Version.hxx b/CXX5/Version.hxx deleted file mode 100644 index 96d911e118f9..000000000000 --- a/CXX5/Version.hxx +++ /dev/null @@ -1,46 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (c) 1998 - 2007, The Regents of the University of California -// Produced at the Lawrence Livermore National Laboratory -// All rights reserved. -// -// This file is part of PyCXX. For details,see http://cxx.sourceforge.net/. The -// full copyright notice is contained in the file COPYRIGHT located at the root -// of the PyCXX distribution. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the disclaimer below. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the disclaimer (as noted below) in the -// documentation and/or materials provided with the distribution. -// - Neither the name of the UC/LLNL nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF -// CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. -// -//----------------------------------------------------------------------------- - -#ifndef __PyCXX_version_hxx__ -#define __PyCXX_version_hxx__ - -#define PYCXX_VERSION_MAJOR 5 -#define PYCXX_VERSION_MINOR 4 -#define PYCXX_VERSION_PATCH 2 -#define PYCXX_MAKEVERSION( major, minor, patch ) ((major<<16)|(minor<<8)|(patch)) -#define PYCXX_VERSION PYCXX_MAKEVERSION( PYCXX_VERSION_MAJOR, PYCXX_VERSION_MINOR, PYCXX_VERSION_PATCH ) -#endif diff --git a/CXX5/WrapPython.h b/CXX5/WrapPython.h deleted file mode 100644 index b7ac19658a36..000000000000 --- a/CXX5/WrapPython.h +++ /dev/null @@ -1,49 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (c) 1998 - 2007, The Regents of the University of California -// Produced at the Lawrence Livermore National Laboratory -// All rights reserved. -// -// This file is part of PyCXX. For details,see http://cxx.sourceforge.net/. The -// full copyright notice is contained in the file COPYRIGHT located at the root -// of the PyCXX distribution. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the disclaimer below. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the disclaimer (as noted below) in the -// documentation and/or materials provided with the distribution. -// - Neither the name of the UC/LLNL nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF -// CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. -// -//----------------------------------------------------------------------------- - -#ifndef __PyCXX_wrap_python_hxx__ -#define __PyCXX_wrap_python_hxx__ - -// pull in python definitions -#include - -// On some platforms we have to include time.h to get select defined -#if !defined(__WIN32__) && !defined(WIN32) && !defined(_WIN32) && !defined(_WIN64) -#include -#endif - -#endif diff --git a/CXX5/cxx_extensions.cxx b/CXX5/cxx_extensions.cxx deleted file mode 100644 index 65fa21cf230e..000000000000 --- a/CXX5/cxx_extensions.cxx +++ /dev/null @@ -1,1464 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (c) 1998 - 2007, The Regents of the University of California -// Produced at the Lawrence Livermore National Laboratory -// All rights reserved. -// -// This file is part of PyCXX. For details,see http://cxx.sourceforge.net/. The -// full copyright notice is contained in the file COPYRIGHT located at the root -// of the PyCXX distribution. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the disclaimer below. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the disclaimer (as noted below) in the -// documentation and/or materials provided with the distribution. -// - Neither the name of the UC/LLNL nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF -// CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. -// -//----------------------------------------------------------------------------- -#include "CXX/Extensions.hxx" -#include "CXX/Exception.hxx" - -#include - -namespace Py -{ - -void Object::validate() -{ - // release pointer if not the right type - if( !accepts( p ) ) - { -#if defined( _CPPRTTI ) || defined( __GNUG__ ) - std::string s( "PyCXX: Error creating object of type " ); - s += (typeid( *this )).name(); - - if( p != 0 ) - { - String from_repr = repr(); - s += " from "; - s += from_repr.as_std_string(); - } - else - { - s += " from (nil)"; - } -#endif - release(); - if( PyErr_Occurred() ) - { // Error message already set - throw Exception(); - } - // Better error message if RTTI available -#if defined( _CPPRTTI ) || defined( __GNUG__ ) - throw TypeError( s ); -#else - throw TypeError( "PyCXX: type error." ); -#endif - } -} - -//================================================================================ -// -// Implementation of MethodTable -// -//================================================================================ - -PyMethodDef MethodTable::method( const char* method_name, PyCFunction f, int flags, const char* doc ) -{ - PyMethodDef m; - m.ml_name = const_cast( method_name ); - m.ml_meth = f; - m.ml_flags = flags; - m.ml_doc = const_cast( doc ); - return m; -} - -MethodTable::MethodTable() -{ - t.push_back( method( 0, 0, 0, 0 ) ); - mt = 0; -} - -MethodTable::~MethodTable() -{ - delete [] mt; -} - -void MethodTable::add( const char* method_name, PyCFunction f, const char* doc, int flag ) -{ - if( !mt ) - { - t.insert( t.end()-1, method( method_name, f, flag, doc ) ); - } - else - { - throw RuntimeError( "Too late to add a module method!" ); - } -} - -PyMethodDef* MethodTable::table() -{ - if( !mt ) - { - Py_ssize_t t1size = t.size(); - mt = new PyMethodDef[t1size]; - int j = 0; - for( std::vector::iterator i = t.begin(); i != t.end(); i++ ) - { - mt[j++] = *i; - } - } - return mt; -} - -//================================================================================ -// -// Implementation of ExtensionModule -// -//================================================================================ -ExtensionModuleBase::ExtensionModuleBase( const char *name ) - : module_name( name ) - , full_module_name( __Py_PackageContext() != NULL ? std::string( __Py_PackageContext() ) : module_name ) - , method_table() -{} - -ExtensionModuleBase::~ExtensionModuleBase() -{} - -const std::string &ExtensionModuleBase::name() const -{ - return module_name; -} - -const std::string &ExtensionModuleBase::fullName() const -{ - return full_module_name; -} - -class ExtensionModuleBasePtr : public PythonExtension -{ -public: - ExtensionModuleBasePtr( ExtensionModuleBase *_module ) - : module( _module ) - {} - virtual ~ExtensionModuleBasePtr() - {} - - ExtensionModuleBase *module; -}; - - -void ExtensionModuleBase::initialize( const char *module_doc ) -{ - PyObject *module_ptr = new ExtensionModuleBasePtr( this ); - - Py_InitModule4 - ( - const_cast( module_name.c_str() ), // name - method_table.table(), // methods - const_cast( module_doc ), // docs - module_ptr, // pass to functions as "self" - PYTHON_API_VERSION // API version - ); -} - -Py::Module ExtensionModuleBase::module(void) const -{ - return Module( full_module_name ); -} - -Py::Dict ExtensionModuleBase::moduleDictionary(void) const -{ - return module().getDict(); -} - -//-------------------------------------------------------------------------------- - -//================================================================================ -// -// Implementation of PythonType -// -//================================================================================ - -extern "C" -{ - static void standard_dealloc(PyObject* p); - // - // All the following functions redirect the call from Python - // onto the matching virtual function in PythonExtensionBase - // - static int print_handler (PyObject*, FILE *, int); - static PyObject* getattr_handler (PyObject*, char*); - static int setattr_handler (PyObject*, char*, PyObject*); - static PyObject* getattro_handler (PyObject*, PyObject*); - static int setattro_handler (PyObject*, PyObject*, PyObject*); - static int compare_handler (PyObject*, PyObject*); - static PyObject* richcompare_handler (PyObject*, PyObject*, int op); - static PyObject* repr_handler (PyObject*); - static PyObject* str_handler (PyObject*); - static long hash_handler (PyObject*); - static PyObject* call_handler (PyObject*, PyObject*, PyObject*); - static PyObject* iter_handler (PyObject*); - static PyObject* iternext_handler (PyObject*); - - // Sequence methods - static Py_ssize_t sequence_length_handler(PyObject*); - static PyObject* sequence_concat_handler(PyObject*,PyObject*); - static PyObject* sequence_repeat_handler(PyObject*, Py_ssize_t); - static PyObject* sequence_item_handler(PyObject*, Py_ssize_t); - static PyObject* sequence_slice_handler(PyObject*, Py_ssize_t, Py_ssize_t); - static int sequence_ass_item_handler(PyObject*, Py_ssize_t, PyObject*); - static int sequence_ass_slice_handler(PyObject*, Py_ssize_t, Py_ssize_t, PyObject*); - // Mapping - static Py_ssize_t mapping_length_handler(PyObject*); - static PyObject* mapping_subscript_handler(PyObject*, PyObject*); - static int mapping_ass_subscript_handler(PyObject*, PyObject*, PyObject*); - - // Numeric methods - static int number_nonzero_handler (PyObject*); - static PyObject* number_negative_handler (PyObject*); - static PyObject* number_positive_handler (PyObject*); - static PyObject* number_absolute_handler (PyObject*); - static PyObject* number_invert_handler (PyObject*); - static PyObject* number_int_handler (PyObject*); - static PyObject* number_float_handler (PyObject*); - static PyObject* number_long_handler (PyObject*); - static PyObject* number_oct_handler (PyObject*); - static PyObject* number_hex_handler (PyObject*); - static PyObject* number_add_handler (PyObject*, PyObject*); - static PyObject* number_subtract_handler (PyObject*, PyObject*); - static PyObject* number_multiply_handler (PyObject*, PyObject*); - static PyObject* number_divide_handler (PyObject*, PyObject*); - static PyObject* number_remainder_handler (PyObject*, PyObject*); - static PyObject* number_divmod_handler (PyObject*, PyObject*); - static PyObject* number_lshift_handler (PyObject*, PyObject*); - static PyObject* number_rshift_handler (PyObject*, PyObject*); - static PyObject* number_and_handler (PyObject*, PyObject*); - static PyObject* number_xor_handler (PyObject*, PyObject*); - static PyObject* number_or_handler (PyObject*, PyObject*); - static PyObject* number_power_handler(PyObject*, PyObject*, PyObject*); - - // Buffer - static Py_ssize_t buffer_getreadbuffer_handler (PyObject*, Py_ssize_t, void**); - static Py_ssize_t buffer_getwritebuffer_handler (PyObject*, Py_ssize_t, void**); - static Py_ssize_t buffer_getsegcount_handler (PyObject*, Py_ssize_t*); -} - - -extern "C" void standard_dealloc( PyObject* p ) -{ - PyMem_DEL( p ); -} - -PythonType & PythonType::supportSequenceType() -{ - if( !sequence_table ) - { - sequence_table = new PySequenceMethods; - memset( sequence_table, 0, sizeof( PySequenceMethods ) ); // ensure new fields are 0 - table->tp_as_sequence = sequence_table; - sequence_table->sq_length = sequence_length_handler; - sequence_table->sq_concat = sequence_concat_handler; - sequence_table->sq_repeat = sequence_repeat_handler; - sequence_table->sq_item = sequence_item_handler; - sequence_table->sq_slice = sequence_slice_handler; - - sequence_table->sq_ass_item = sequence_ass_item_handler; // BAS setup seperately? - sequence_table->sq_ass_slice = sequence_ass_slice_handler; // BAS setup seperately? - } - return *this; -} - -PythonType & PythonType::supportMappingType() -{ - if( !mapping_table ) - { - mapping_table = new PyMappingMethods; - memset( mapping_table, 0, sizeof( PyMappingMethods ) ); // ensure new fields are 0 - table->tp_as_mapping = mapping_table; - mapping_table->mp_length = mapping_length_handler; - mapping_table->mp_subscript = mapping_subscript_handler; - mapping_table->mp_ass_subscript = mapping_ass_subscript_handler; // BAS setup seperately? - } - return *this; -} - -PythonType & PythonType::supportNumberType() -{ - if( !number_table ) - { - number_table = new PyNumberMethods; - memset( number_table, 0, sizeof( PyNumberMethods ) ); // ensure new fields are 0 - table->tp_as_number = number_table; - number_table->nb_add = number_add_handler; - number_table->nb_subtract = number_subtract_handler; - number_table->nb_multiply = number_multiply_handler; - number_table->nb_divide = number_divide_handler; - number_table->nb_remainder = number_remainder_handler; - number_table->nb_divmod = number_divmod_handler; - number_table->nb_power = number_power_handler; - number_table->nb_negative = number_negative_handler; - number_table->nb_positive = number_positive_handler; - number_table->nb_absolute = number_absolute_handler; - number_table->nb_nonzero = number_nonzero_handler; - number_table->nb_invert = number_invert_handler; - number_table->nb_lshift = number_lshift_handler; - number_table->nb_rshift = number_rshift_handler; - number_table->nb_and = number_and_handler; - number_table->nb_xor = number_xor_handler; - number_table->nb_or = number_or_handler; - number_table->nb_coerce = 0; - number_table->nb_int = number_int_handler; - number_table->nb_long = number_long_handler; - number_table->nb_float = number_float_handler; - number_table->nb_oct = number_oct_handler; - number_table->nb_hex = number_hex_handler; - } - return *this; -} - -PythonType & PythonType::supportBufferType() -{ - if( !buffer_table ) - { - buffer_table = new PyBufferProcs; - memset( buffer_table, 0, sizeof( PyBufferProcs ) ); // ensure new fields are 0 - table->tp_as_buffer = buffer_table; - buffer_table->bf_getreadbuffer = buffer_getreadbuffer_handler; - buffer_table->bf_getwritebuffer = buffer_getwritebuffer_handler; - buffer_table->bf_getsegcount = buffer_getsegcount_handler; - } - return *this; -} - -// if you define one sequence method you must define -// all of them except the assigns - -PythonType::PythonType( size_t basic_size, int itemsize, const char *default_name ) - : table( new PyTypeObject ) - , sequence_table( NULL ) - , mapping_table( NULL ) - , number_table( NULL ) - , buffer_table( NULL ) -{ - memset( table, 0, sizeof( PyTypeObject ) ); // ensure new fields are 0 - *reinterpret_cast( table ) = py_object_initializer; - table->ob_type = _Type_Type(); - table->ob_size = 0; - table->tp_name = const_cast( default_name ); - table->tp_basicsize = basic_size; - table->tp_itemsize = itemsize; - table->tp_dealloc = ( destructor ) standard_dealloc; - table->tp_print = 0; - table->tp_getattr = 0; - table->tp_setattr = 0; - table->tp_compare = 0; - table->tp_repr = 0; - table->tp_as_number = 0; - table->tp_as_sequence = 0; - table->tp_as_mapping = 0; - table->tp_hash = 0; - table->tp_call = 0; - table->tp_str = 0; - table->tp_getattro = 0; - table->tp_setattro = 0; - table->tp_as_buffer = 0; - table->tp_flags = Py_TPFLAGS_DEFAULT; - table->tp_doc = 0; -#if PY_MAJOR_VERSION > 2 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 0) - // first use in 2.0 - table->tp_traverse = 0L; - table->tp_clear = 0L; -#else - table->tp_xxx5 = 0L; - table->tp_xxx6 = 0L; -#endif -#if PY_MAJOR_VERSION > 2 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 1) - // first defined in 2.1 - table->tp_richcompare = 0L; - table->tp_weaklistoffset = 0L; -#else - table->tp_xxx7 = 0L; - table->tp_xxx8 = 0L; -#endif - -#if PY_MAJOR_VERSION > 2 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 2) - // first defined in 2.3 - table->tp_iter = 0L; - table->tp_iternext = 0L; -#endif - -#ifdef COUNT_ALLOCS - table->tp_alloc = 0; - table->tp_free = 0; - table->tp_maxalloc = 0; - table->tp_next = 0; -#endif -} - -PythonType::~PythonType( ) -{ - delete table; - delete sequence_table; - delete mapping_table; - delete number_table; - delete buffer_table; -} - -PyTypeObject* PythonType::type_object( ) const -{return table;} - -PythonType & PythonType::name( const char* nam ) -{ - table->tp_name = const_cast( nam ); - return *this; -} - -const char *PythonType::getName() const -{ - return table->tp_name; -} - -PythonType & PythonType::doc( const char* d ) -{ - table->tp_doc = const_cast( d ); - return *this; -} - -const char *PythonType::getDoc() const -{ - return table->tp_doc; -} - -PythonType & PythonType::dealloc( void( *f )( PyObject* )) -{ - table->tp_dealloc = f; - return *this; -} - -PythonType & PythonType::supportPrint() -{ - table->tp_print = print_handler; - return *this; -} - -PythonType & PythonType::supportGetattr() -{ - table->tp_getattr = getattr_handler; - return *this; -} - -PythonType & PythonType::supportSetattr() -{ - table->tp_setattr = setattr_handler; - return *this; -} - -PythonType & PythonType::supportGetattro() -{ - table->tp_getattro = getattro_handler; - return *this; -} - -PythonType & PythonType::supportSetattro() -{ - table->tp_setattro = setattro_handler; - return *this; -} - -PythonType & PythonType::supportCompare() -{ - table->tp_compare = compare_handler; - return *this; -} - -#if PY_MAJOR_VERSION > 2 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 1) -PythonType & PythonType::supportRichCompare() -{ - table->tp_richcompare = richcompare_handler; - return *this; -} -#endif - -PythonType & PythonType::supportRepr() -{ - table->tp_repr = repr_handler; - return *this; -} - -PythonType & PythonType::supportStr() -{ - table->tp_str = str_handler; - return *this; -} - -PythonType & PythonType::supportHash() -{ - table->tp_hash = hash_handler; - return *this; -} - -PythonType & PythonType::supportCall() -{ - table->tp_call = call_handler; - return *this; -} - -PythonType & PythonType::supportIter() -{ - table->tp_iter = iter_handler; - table->tp_iternext = iternext_handler; - return *this; -} - -//-------------------------------------------------------------------------------- -// -// Handlers -// -//-------------------------------------------------------------------------------- -extern "C" int print_handler( PyObject *self, FILE *fp, int flags ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->print( fp, flags ); - } - catch( Py::Exception & ) - { - return -1; // indicate error - } -} - -extern "C" PyObject* getattr_handler( PyObject *self, char *name ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->getattr( name ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" int setattr_handler( PyObject *self, char *name, PyObject *value ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->setattr( name, Py::Object( value ) ); - } - catch( Py::Exception & ) - { - return -1; // indicate error - } -} - -extern "C" PyObject* getattro_handler( PyObject *self, PyObject *name ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->getattro( Py::Object( name ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" int setattro_handler( PyObject *self, PyObject *name, PyObject *value ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->setattro( Py::Object( name ), Py::Object( value ) ); - } - catch( Py::Exception & ) - { - return -1; // indicate error - } -} - -extern "C" int compare_handler( PyObject *self, PyObject *other ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->compare( Py::Object( other ) ); - } - catch( Py::Exception & ) - { - return -1; // indicate error - } -} - -#if PY_MAJOR_VERSION > 2 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 1) -extern "C" PyObject* richcompare_handler( PyObject *self, PyObject *other, int op ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->rich_compare( Py::Object( other ), op ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} -#endif - -extern "C" PyObject* repr_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->repr() ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* str_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->str() ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" long hash_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->hash(); - } - catch( Py::Exception & ) - { - return -1; // indicate error - } -} - -extern "C" PyObject* call_handler( PyObject *self, PyObject *args, PyObject *kw ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - if( kw != NULL ) - return new_reference_to( p->call( Py::Object( args ), Py::Object( kw ) ) ); - else - return new_reference_to( p->call( Py::Object( args ), Py::Object() ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* iter_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->iter() ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* iternext_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->iternext(); // might be a NULL ptr on end of iteration - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - - -// Sequence methods -extern "C" Py_ssize_t sequence_length_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->sequence_length(); - } - catch( Py::Exception & ) - { - return -1; // indicate error - } -} - -extern "C" PyObject* sequence_concat_handler( PyObject *self, PyObject *other ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->sequence_concat( Py::Object( other ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* sequence_repeat_handler( PyObject *self, Py_ssize_t count ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->sequence_repeat( count ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* sequence_item_handler( PyObject *self, Py_ssize_t index ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->sequence_item( index ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* sequence_slice_handler( PyObject *self, Py_ssize_t first, Py_ssize_t last ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->sequence_slice( first, last ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" int sequence_ass_item_handler( PyObject *self, Py_ssize_t index, PyObject *value ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->sequence_ass_item( index, Py::Object( value ) ); - } - catch( Py::Exception & ) - { - return -1; // indicate error - } -} - -extern "C" int sequence_ass_slice_handler( PyObject *self, Py_ssize_t first, Py_ssize_t last, PyObject *value ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->sequence_ass_slice( first, last, Py::Object( value ) ); - } - catch( Py::Exception & ) - { - return -1; // indicate error - } -} - -// Mapping -extern "C" Py_ssize_t mapping_length_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->mapping_length(); - } - catch( Py::Exception & ) - { - return -1; // indicate error - } -} - -extern "C" PyObject* mapping_subscript_handler( PyObject *self, PyObject *key ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->mapping_subscript( Py::Object( key ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" int mapping_ass_subscript_handler( PyObject *self, PyObject *key, PyObject *value ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->mapping_ass_subscript( Py::Object( key ), Py::Object( value ) ); - } - catch( Py::Exception & ) - { - return -1; // indicate error - } -} - -// Number -extern "C" int number_nonzero_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->number_nonzero(); - } - catch( Py::Exception & ) - { - return -1; // indicate error - } -} - -extern "C" PyObject* number_negative_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_negative() ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_positive_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_positive() ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_absolute_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_absolute() ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_invert_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_invert() ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_int_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_int() ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_float_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_float() ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_long_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_long() ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_oct_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_oct() ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_hex_handler( PyObject *self ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_hex() ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_add_handler( PyObject *self, PyObject *other ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_add( Py::Object( other ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_subtract_handler( PyObject *self, PyObject *other ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_subtract( Py::Object( other ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_multiply_handler( PyObject *self, PyObject *other ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_multiply( Py::Object( other ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_divide_handler( PyObject *self, PyObject *other ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_divide( Py::Object( other ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_remainder_handler( PyObject *self, PyObject *other ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_remainder( Py::Object( other ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_divmod_handler( PyObject *self, PyObject *other ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_divmod( Py::Object( other ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_lshift_handler( PyObject *self, PyObject *other ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_lshift( Py::Object( other ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_rshift_handler( PyObject *self, PyObject *other ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_rshift( Py::Object( other ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_and_handler( PyObject *self, PyObject *other ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_and( Py::Object( other ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_xor_handler( PyObject *self, PyObject *other ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_xor( Py::Object( other ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_or_handler( PyObject *self, PyObject *other ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_or( Py::Object( other ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -extern "C" PyObject* number_power_handler( PyObject *self, PyObject *x1, PyObject *x2 ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return new_reference_to( p->number_power( Py::Object( x1 ), Py::Object( x2 ) ) ); - } - catch( Py::Exception & ) - { - return NULL; // indicate error - } -} - -// Buffer -extern "C" Py_ssize_t buffer_getreadbuffer_handler( PyObject *self, Py_ssize_t index, void **pp ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->buffer_getreadbuffer( index, pp ); - } - catch( Py::Exception & ) - { - return -1; // indicate error - } -} - -extern "C" Py_ssize_t buffer_getwritebuffer_handler( PyObject *self, Py_ssize_t index, void **pp ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->buffer_getwritebuffer( index, pp ); - } - catch( Py::Exception & ) - { - return -1; // indicate error - } -} - -extern "C" Py_ssize_t buffer_getsegcount_handler( PyObject *self, Py_ssize_t *count ) -{ - try - { - PythonExtensionBase *p = static_cast( self ); - return p->buffer_getsegcount( count ); - } - catch( Py::Exception & ) - { - return -1; // indicate error - } -} - - -//================================================================================ -// -// Implementation of PythonExtensionBase -// -//================================================================================ -#define missing_method( method ) \ -throw RuntimeError( "Extension object does not support method " #method ); - -PythonExtensionBase::PythonExtensionBase() -{ -} - -PythonExtensionBase::~PythonExtensionBase() -{ - assert( ob_refcnt == 0 ); -} - -int PythonExtensionBase::print( FILE *, int ) -{ missing_method( print ); return -1; } - -int PythonExtensionBase::setattr( const char*, const Py::Object & ) -{ missing_method( setattr ); return -1; } - -Py::Object PythonExtensionBase::getattro( const Py::Object & ) -{ missing_method( getattro ); return Py::Nothing(); } - -int PythonExtensionBase::setattro( const Py::Object &, const Py::Object & ) -{ missing_method( setattro ); return -1; } - -int PythonExtensionBase::compare( const Py::Object & ) -{ missing_method( compare ); return -1; } - -#if PY_MAJOR_VERSION > 2 || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 1) -Py::Object PythonExtensionBase::rich_compare( const Py::Object &, int op ) -{ missing_method( rich_compare ); return Py::None(); } -#endif - -Py::Object PythonExtensionBase::repr() -{ missing_method( repr ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::str() -{ missing_method( str ); return Py::Nothing(); } - -long PythonExtensionBase::hash() -{ missing_method( hash ); return -1; } - -Py::Object PythonExtensionBase::call( const Py::Object &, const Py::Object & ) -{ missing_method( call ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::iter() -{ missing_method( iter ); return Py::Nothing(); } - -PyObject* PythonExtensionBase::iternext() -{ missing_method( iternext ); return NULL; } - - -// Sequence methods -int PythonExtensionBase::sequence_length() -{ missing_method( sequence_length ); return -1; } - -Py::Object PythonExtensionBase::sequence_concat( const Py::Object & ) -{ missing_method( sequence_concat ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::sequence_repeat( Py_ssize_t ) -{ missing_method( sequence_repeat ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::sequence_item( Py_ssize_t ) -{ missing_method( sequence_item ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::sequence_slice( Py_ssize_t, Py_ssize_t ) -{ missing_method( sequence_slice ); return Py::Nothing(); } - -int PythonExtensionBase::sequence_ass_item( Py_ssize_t, const Py::Object & ) -{ missing_method( sequence_ass_item ); return -1; } - -int PythonExtensionBase::sequence_ass_slice( Py_ssize_t, Py_ssize_t, const Py::Object & ) -{ missing_method( sequence_ass_slice ); return -1; } - - -// Mapping -int PythonExtensionBase::mapping_length() -{ missing_method( mapping_length ); return -1; } - -Py::Object PythonExtensionBase::mapping_subscript( const Py::Object & ) -{ missing_method( mapping_subscript ); return Py::Nothing(); } - -int PythonExtensionBase::mapping_ass_subscript( const Py::Object &, const Py::Object & ) -{ missing_method( mapping_ass_subscript ); return -1; } - - -// Number -int PythonExtensionBase::number_nonzero() -{ missing_method( number_nonzero ); return -1; } - -Py::Object PythonExtensionBase::number_negative() -{ missing_method( number_negative ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_positive() -{ missing_method( number_positive ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_absolute() -{ missing_method( number_absolute ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_invert() -{ missing_method( number_invert ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_int() -{ missing_method( number_int ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_float() -{ missing_method( number_float ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_long() -{ missing_method( number_long ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_oct() -{ missing_method( number_oct ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_hex() -{ missing_method( number_hex ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_add( const Py::Object & ) -{ missing_method( number_add ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_subtract( const Py::Object & ) -{ missing_method( number_subtract ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_multiply( const Py::Object & ) -{ missing_method( number_multiply ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_divide( const Py::Object & ) -{ missing_method( number_divide ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_remainder( const Py::Object & ) -{ missing_method( number_remainder ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_divmod( const Py::Object & ) -{ missing_method( number_divmod ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_lshift( const Py::Object & ) -{ missing_method( number_lshift ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_rshift( const Py::Object & ) -{ missing_method( number_rshift ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_and( const Py::Object & ) -{ missing_method( number_and ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_xor( const Py::Object & ) -{ missing_method( number_xor ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_or( const Py::Object & ) -{ missing_method( number_or ); return Py::Nothing(); } - -Py::Object PythonExtensionBase::number_power( const Py::Object &, const Py::Object & ) -{ missing_method( number_power ); return Py::Nothing(); } - - -// Buffer -Py_ssize_t PythonExtensionBase::buffer_getreadbuffer( Py_ssize_t, void** ) -{ missing_method( buffer_getreadbuffer ); return -1; } - -Py_ssize_t PythonExtensionBase::buffer_getwritebuffer( Py_ssize_t, void** ) -{ missing_method( buffer_getwritebuffer ); return -1; } - -Py_ssize_t PythonExtensionBase::buffer_getsegcount( Py_ssize_t* ) -{ missing_method( buffer_getsegcount ); return -1; } - -//-------------------------------------------------------------------------------- -// -// Method call handlers for -// PythonExtensionBase -// ExtensionModuleBase -// -//-------------------------------------------------------------------------------- - -extern "C" PyObject *method_keyword_call_handler( PyObject *_self_and_name_tuple, PyObject *_args, PyObject *_keywords ) -{ - try - { - Tuple self_and_name_tuple( _self_and_name_tuple ); - - PyObject *self_in_cobject = self_and_name_tuple[0].ptr(); - void *self_as_void = PyCObject_AsVoidPtr( self_in_cobject ); - if( self_as_void == NULL ) - return NULL; - - ExtensionModuleBase *self = static_cast( self_as_void ); - - String py_name( self_and_name_tuple[1] ); - std::string name( py_name.as_std_string() ); - - Tuple args( _args ); - if( _keywords == NULL ) - { - Dict keywords; // pass an empty dict - - Object result( self->invoke_method_keyword( name, args, keywords ) ); - return new_reference_to( result.ptr() ); - } - - Dict keywords( _keywords ); - - Object result( self->invoke_method_keyword( name, args, keywords ) ); - return new_reference_to( result.ptr() ); - } - catch( Exception & ) - { - return 0; - } -} - -extern "C" PyObject *method_varargs_call_handler( PyObject *_self_and_name_tuple, PyObject *_args ) -{ - try - { - Tuple self_and_name_tuple( _self_and_name_tuple ); - - PyObject *self_in_cobject = self_and_name_tuple[0].ptr(); - void *self_as_void = PyCObject_AsVoidPtr( self_in_cobject ); - if( self_as_void == NULL ) - return NULL; - - ExtensionModuleBase *self = static_cast( self_as_void ); - - String py_name( self_and_name_tuple[1] ); - std::string name( py_name.as_std_string() ); - - Tuple args( _args ); - - Object result( self->invoke_method_varargs( name, args ) ); - - return new_reference_to( result.ptr() ); - } - catch( Exception & ) - { - return 0; - } -} - -extern "C" void do_not_dealloc( void * ) -{} - - -//-------------------------------------------------------------------------------- -// -// ExtensionExceptionType -// -//-------------------------------------------------------------------------------- -ExtensionExceptionType::ExtensionExceptionType() - : Py::Object() -{ -} - -void ExtensionExceptionType::init( ExtensionModuleBase &module, const std::string& name ) -{ - std::string module_name( module.fullName() ); - module_name += "."; - module_name += name; - - set( PyErr_NewException( const_cast( module_name.c_str() ), NULL, NULL ), true ); -} - -void ExtensionExceptionType::init( ExtensionModuleBase &module, const std::string& name, ExtensionExceptionType &parent) - { - std::string module_name( module.fullName() ); - module_name += "."; - module_name += name; - - set( PyErr_NewException( const_cast( module_name.c_str() ), parent.ptr(), NULL ), true ); -} - -ExtensionExceptionType::~ExtensionExceptionType() -{ -} - -Exception::Exception( ExtensionExceptionType &exception, const std::string& reason ) -{ - PyErr_SetString (exception.ptr(), reason.c_str()); -} - -Exception::Exception( ExtensionExceptionType &exception, Object &reason ) -{ - PyErr_SetObject (exception.ptr(), reason.ptr()); -} - -Exception::Exception( PyObject* exception, Object &reason ) -{ - PyErr_SetObject (exception, reason.ptr()); -} - -} // end of namespace Py diff --git a/CXX5/cxxextensions.c b/CXX5/cxxextensions.c deleted file mode 100644 index edae3be6bce9..000000000000 --- a/CXX5/cxxextensions.c +++ /dev/null @@ -1,49 +0,0 @@ -/*---------------------------------------------------------------------------- -// -// Copyright (c) 1998 - 2007, The Regents of the University of California -// Produced at the Lawrence Livermore National Laboratory -// All rights reserved. -// -// This file is part of PyCXX. For details,see http://cxx.sourceforge.net/. The -// full copyright notice is contained in the file COPYRIGHT located at the root -// of the PyCXX distribution. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the disclaimer below. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the disclaimer (as noted below) in the -// documentation and/or materials provided with the distribution. -// - Neither the name of the UC/LLNL nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF -// CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. -// -//---------------------------------------------------------------------------*/ - -#include "CXX/WrapPython.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - -PyObject py_object_initializer = {PyObject_HEAD_INIT(0)}; - -#ifdef __cplusplus -} -#endif diff --git a/CXX5/cxxsupport.cxx b/CXX5/cxxsupport.cxx deleted file mode 100644 index 1dfba5a1908b..000000000000 --- a/CXX5/cxxsupport.cxx +++ /dev/null @@ -1,174 +0,0 @@ -//----------------------------------------------------------------------------- -// -// Copyright (c) 1998 - 2007, The Regents of the University of California -// Produced at the Lawrence Livermore National Laboratory -// All rights reserved. -// -// This file is part of PyCXX. For details,see http://cxx.sourceforge.net/. The -// full copyright notice is contained in the file COPYRIGHT located at the root -// of the PyCXX distribution. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, -// this list of conditions and the disclaimer below. -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the disclaimer (as noted below) in the -// documentation and/or materials provided with the distribution. -// - Neither the name of the UC/LLNL nor the names of its contributors may be -// used to endorse or promote products derived from this software without -// specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF -// CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. -// -//----------------------------------------------------------------------------- - -#include "CXX/Objects.hxx" -namespace Py { - -Py_UNICODE unicode_null_string[1] = { 0 }; - -Type Object::type () const -{ - return Type (PyObject_Type (p), true); -} - -String Object::str () const -{ - return String (PyObject_Str (p), true); -} - -String Object::repr () const -{ - return String (PyObject_Repr (p), true); -} - -std::string Object::as_string() const -{ - return static_cast(str()); -} - -List Object::dir () const - { - return List (PyObject_Dir (p), true); - } - -bool Object::isType (const Type& t) const -{ - return type ().ptr() == t.ptr(); -} - -Char::operator String() const -{ - return String(ptr()); -} - -// TMM: non-member operaters for iterators - see above -// I've also made a bug fix in respect to the cxx code -// (dereffed the left.seq and right.seq comparison) -bool operator==(const Sequence::iterator& left, const Sequence::iterator& right) -{ - return left.eql( right ); -} - -bool operator!=(const Sequence::iterator& left, const Sequence::iterator& right) -{ - return left.neq( right ); -} - -bool operator< (const Sequence::iterator& left, const Sequence::iterator& right) -{ - return left.lss( right ); -} - -bool operator> (const Sequence::iterator& left, const Sequence::iterator& right) -{ - return left.gtr( right ); -} - -bool operator<=(const Sequence::iterator& left, const Sequence::iterator& right) -{ - return left.leq( right ); -} - -bool operator>=(const Sequence::iterator& left, const Sequence::iterator& right) -{ - return left.geq( right ); -} - -// now for const_iterator -bool operator==(const Sequence::const_iterator& left, const Sequence::const_iterator& right) -{ - return left.eql( right ); -} - -bool operator!=(const Sequence::const_iterator& left, const Sequence::const_iterator& right) -{ - return left.neq( right ); -} - -bool operator< (const Sequence::const_iterator& left, const Sequence::const_iterator& right) -{ - return left.lss( right ); -} - -bool operator> (const Sequence::const_iterator& left, const Sequence::const_iterator& right) -{ - return left.gtr( right ); -} - -bool operator<=(const Sequence::const_iterator& left, const Sequence::const_iterator& right) -{ - return left.leq( right ); -} - -bool operator>=(const Sequence::const_iterator& left, const Sequence::const_iterator& right) -{ - return left.geq( right ); -} - -// For mappings: -bool operator==(const Mapping::iterator& left, const Mapping::iterator& right) -{ - return left.eql( right ); -} - -bool operator!=(const Mapping::iterator& left, const Mapping::iterator& right) -{ - return left.neq( right ); -} - -// now for const_iterator -bool operator==(const Mapping::const_iterator& left, const Mapping::const_iterator& right) -{ - return left.eql( right ); -} - -bool operator!=(const Mapping::const_iterator& left, const Mapping::const_iterator& right) -{ - return left.neq( right ); -} - -// TMM: 31May'01 - Added the #ifndef so I can exclude iostreams. -#ifndef CXX_NO_IOSTREAMS -// output - -std::ostream& operator<< (std::ostream& os, const Object& ob) -{ - return (os << static_cast(ob.str())); -} -#endif - -} // Py diff --git a/DEVNOTES b/DEVNOTES deleted file mode 100644 index 0df74feb9c9a..000000000000 --- a/DEVNOTES +++ /dev/null @@ -1,103 +0,0 @@ - -# html pages -scp index.html jdh2358@matplotlib.sf.net:/home/groups/m/ma/matplotlib/htdocs - -# project page -http://sourceforge.net/projects/matplotlib - -# home page -http://matplotlib.sourceforge.net - -### updating the website - - # build the docs - mother> cd ~/python/projects/matplotlib - mother> make htmldocs - mother> cd htdocs - mother> python process_docs.py - mother> python convert.py - mother> tar cvfz site.tar.gz *.html screenshots tut examples - mother> scp site.tar.gz jdh2358@matplotlib.sf.net:/home/groups/m/ma/matplotlib/htdocs/ - - mother> ssh -l jdh2358 shell.sf.net - sf> cd /home/groups/m/ma/matplotlib/htdocs - sf> tar xvfz site.tar.gz - - -### Doing a release - -0) Turn off all the build flags - -0) Testing: remove font cache and test w/ and w/o font cache on some - example script - -0) Testing: Run examples/backend_driver.py w/o errors; run the backend specific - demos (object_picker, anim , embedding*). Do an explicity numarray - and python2.2 test. - -0) Remove MANIFEST so it will be rebuilt by MANIFEST.in - -0) Make sure sdist builds setuptools-compatible release: Remove - setup.cfg (or, if more than the [egg_info] section is in - this file, remove the [egg_info] section). See - http://mail.python.org/pipermail/distutils-sig/2006-July/006561.html - for more info. - -1) Comment out all the savefig commands in the examples dir - > rfr -f savefig -r '#savefig' *.py - > rfr -f '##savefig' -r '#savefig' *.py - -2) edit __init__.py, setup.py license.html.template to the correct version number - local> clearbak - local> make htmldocs - local> scp site.tar.gz jdh2358@matplotlib.sf.net:/home/groups/m/ma/matplotlib/htdocs/ - local> ssh -l jdh2358 shell.sf.net - sf> cd /home/groups/m/ma/matplotlib/htdocs - sf> tar xvfz site.tar.gz - - > make release - - > build on win32 (set the backend to tkagg, set the 'auto' flags in - setup.py to 1, set tk windowing) - - > cvs tag the sucker - -4) anon ftp the tar.gz and exe file to SF - local> cd dist - local> ncftp upload.sourceforge.net - ncftp> cd incoming - ncftp> put tar.gz, zip exe - -5) go to https://sourceforge.net/project/admin/?group_id=80706 and do - a file release. Click on the "Admin" tab to log in as an admin, - and then the "File Releases" tab. Go to the bottom and click "add - release" and enter the package name but not the version number in - the "Package Name" box. You will then be prompted for the "New - release name" at which point you can add the version number, eg - somepackage-0.1 and click "Create this release". - - You will then be taken to a fairly self explanatory page where you - can enter the Change notes, the release notes, and select which - packages from the incoming ftp archive you want to include in this - release. For each binary, you will need to select the platform and - file type, and when you are done you click on the "notify users who - are monitoring this package link" - - -Standard reminders for numarray build - - - change numerix in matplotlibrc *and* setup.py - - - change backend to tkagg in rc - - - email users monitoring the package and me when you are done and - I'll send the announcement - -Trove categorization is under Public info on the admin page - -standard reminders for win32 - - - setup flags to 1 - - backend tkagg in rc - - unix2dos the rc file - diff --git a/FILETYPES b/FILETYPES deleted file mode 100644 index 5378f6c4a2dc..000000000000 --- a/FILETYPES +++ /dev/null @@ -1,61 +0,0 @@ -This is a table of the output formats supported by each backend. - -You may need to expand your terminal window to read this table -correctly. It may be edited with emacs' table mode. - -Each cell specifies the backend that actually handles the file format. -A cell with a '+' in it denotes the rasterizer and the file writing -infrastructure as separate pieces. - -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -| |bmp |emf |eps |jpeg |pcx |pdf |png |ps |raw |svg |svgz |tiff |xpm | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|Agg | |emf |ps | | |pdf |agg * |ps |agg |svg |svg | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|Cairo | |emf |ps | | |cairo |cairo |cairo|agg |cairo|cairo| | | -|[1] | | |[2] | | | |* | | | | | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|CocoaAgg| |emf |ps | | |pdf |agg * |ps |agg |svg |svg | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|Emf | |emf *| | | | | | | | | | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|FltkAgg | |emf |ps | | |pdf |agg * |ps |agg |svg |svg | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|Gd | | | | | | |gd * | | | | | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|Gtk | |emf |ps |gdk + | |pdf |gdk + |ps |agg |svg |svg | | | -|(gdk) | | | |pixbuf | | |pixbuf| | | | | | | -| | | | | | | |* | | | | | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|GtkAgg | |emf |ps |agg + | |pdf |agg + |ps |agg |svg |svg | | | -| | | | |pixbuf | | |pixbuf| | | | | | | -| | | | | | | |* | | | | | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|GtkCairo| |emf |ps |cairo +| |cairo |cairo |cairo|agg |cairo|cairo| | | -| | | | |pixbuf | | |* | | | | | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|Paint | | | | | | |libart| | | | | | | -|(libart)| | | | | | |* | | | | | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|Pdf | | | | | |pdf * | | | | | | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|Ps | | |ps | | | | |ps * | | | | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|QtAgg | |emf |ps | | |pdf |agg * |ps |agg |svg |svg | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|Qt4Agg | |emf |ps | | |pdf |agg * |ps |agg |svg |svg | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|Svg | | | | | | | | | |svg *|svg | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|TkAgg | |emf |ps | | |pdf |agg * |ps |agg |svg |svg | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|Wx |wx + |emf |ps |wx + wx|wx + |pdf |wx + |ps |agg |svg |svg |wx + |wx + | -| |wx | | | |wx | |wx * | | | | |wx |wx | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ -|WxAgg | |emf |ps | | |pdf |agg * |ps |agg |svg |svg | | | -+--------+-----+-----+-----+-------+-----+------+------+-----+-----+-----+-----+-----+-----+ - -* Default filetype for the backend -[1] Cairo's default filetype is specified in rcParams['cairo.format'] -[2] Cairo does not produce .eps files, and instead falls back on -backend_ps.py diff --git a/INSTALL b/INSTALL index d8b85425587d..35f9b420af64 100644 --- a/INSTALL +++ b/INSTALL @@ -1,141 +1,258 @@ +********** +Installing +********** + +There are lots of different ways to install matplotlib, and the best +way depends on what operating system you are using, what you already +have installed, and how you want to use it. To avoid wading through +all the details (and potential complications) on this page, the +easiest thing for you to do is use one of the pre-packaged python +distributions that already provide matplotlib built in. The Enthought +Python Distribution `(EPD) +`_ for Windows, OS X or +Redhat is an excellent choice that "just works" out of the box. +Another excellent alternative for Windows users is `Python (x, y) +`_ which tends to be updated a +bit more frequently. Both of these packages include matplotlib and +pylab, and *lots* of other useful tools. matplotlib is also packaged +for pretty much every major linux distribution, so if you are on linux +your package manager will probably provide matplotlib prebuilt. + +One single click installer and you are done. + +Manually installing pre-built packages +====================================== + +For some people, the prepackaged pythons discussed above are not an +option. That's OK, it's usually pretty easy to get a custom install +working. You will first need to find out if you have python installed +on your machine, and if not, install it. The official python builds +are available for download `here `_, +but OS X users please read :ref:`which-python-for-osx`. + +Once you have python up and running, you will need to install `numpy +`_. +numpy provides high performance array data structures and mathematical +functions, and is a requirement for matplotlib. You can test your +progress:: + + >>> import numpy + >>> print numpy.__version__ + +matplotlib requires numpy version 1.1 or later. Although it is not a +requirement to use matplotlib, we strongly encourage you to install +`ipython `_, which is an interactive +shell for python that is matplotlib aware. + +Next we need to get matplotlib installed. We provide prebuilt +binaries for OS X and Windows on the matplotlib `download +`_ page. Click on +the latest release of the "matplotlib" package, choose your python +version (2.5 or 2.6) and your platform (macosx or win32) and you +should be good to go. If you have any problems, please check the +:ref:`installing-faq`, google around a little bit, and post a question +the `mailing list +`_. If +you are on debian/unbuntu linux, it suffices to do:: + + > sudo apt-get install python-matplotlib + +Instructions for installing our OSX binaries are found in the FAQ +:ref:`install_osx_binaries`. + + +Once you have ipython, numpy and matplotlib installed, in ipython's +"pylab" mode you have a MATLAB-like environment that automatically +handles most of the configuration details for you, so you can get up +and running quickly:: + + johnh@flag:~> ipython -pylab + Python 2.4.5 (#4, Apr 12 2008, 09:09:16) + IPython 0.9.0 -- An enhanced Interactive Python. + + Welcome to pylab, a matplotlib-based Python environment. + For more information, type 'help(pylab)'. + + In [1]: x = randn(10000) + + In [2]: hist(x, 100) + +Instructions for installing our OSX binaries are found in the FAQ +ref:`install_osx_binaries`. + +Note that when testing matplotlib installations from the interactive +python console, there are some issues relating to user interface +toolkits and interactive settings that are discussed in +:ref:`mpl-shell`. + +Installing on Windows +--------------------- + +If you don't already have python installed, you may want to consider +using the enthought edition of python, which has scipy, numpy, and +wxpython, plus a lot of other goodies, preinstalled - `Enthought +Python `_. With the enthought +edition of python + matplotlib installer, the following backends +should work out of the box: agg, wx, wxagg, tkagg, ps, pdf and svg. + +For standard python installations, you will also need to install numpy +in addition to the matplotlib installer. On some systems you will +also need to download msvcp71.dll library, which you can download from +http://www.dll-files.com/dllindex/dll-files.shtml?msvcp71 or other +sites. You will need to unzip the archive and drag the dll into +:file:`c:\windows\system32`. + +All of the GUI backends run on windows, but TkAgg is probably the +best for interactive use from the standard python shell or ipython. +The windows installer (:file:`*.exe`) on the download page contains all the +code you need to get up and running. However, there are many +examples that are not included in the windows installer. If you +want to try the many demos that come in the matplotlib src +distribution, download the zip file and look in the examples subdir. + +.. _install_from_source: + +Installing from source +====================== + +If you are interested perhaps in contributing to matplotlib +development, running the latest greatest code, or just like to +build everything yourself, it is not difficult to build matplotlib +from source. Grab the latest *tar.gz* release file from `sourceforge +`_, or if +you want to develop matplotlib or just need the latest bugfixed +version, grab the latest git version :ref:`install-from-git`. + +Once you have satisfied the requirements detailed below (mainly +python, numpy, libpng and freetype), you build matplotlib in the usual +way:: + + cd matplotlib + python setup.py build + python setup.py install + +We provide a `setup.cfg +`_ +file that lives along :file:`setup.py` which you can use to customize +the build process, for example, which default backend to use, whether +some of the optional libraries that matplotlib ships with are +installed, and so on. This file will be particularly useful to those +packaging matplotlib. + +If you have installed prerequisites to nonstandard places and need to +inform matplotlib where they are, edit ``setupext.py`` an add the base +dirs to the ``basedir`` dictionary entry for your ``sys.platform``. +e.g., if the header to some required library is in +``/some/path/include/someheader.h``, put ``/some/path`` in the +``basedir`` list for your platform. + +.. _install_requirements: + +Build requirements +================== + +These are external packages which you will need to install before +installing matplotlib. Windows users only need the first two (python +and numpy) since the others are built into the matplotlib windows +installers available for download at the sourceforge site. If you are +building on OSX, see :ref:`build_osx`. If you are installing +dependencies with a package manager, you may need to install the +development packages (look for a "-dev" postfix) in addition to the +libraries themselves. + +.. note:: + + If you are on debian/ubuntu, you can get all the dependencies + required to build matplotlib with:: + + sudo apt-get build_dep python-matplotlib + + This does not build matplotlib, but it does get the install the + build dependencies, which will make building from svn easy. + + If you are on Fedora/RedHat, you can get all the dependencies + required to build matplotlib by first installing ``yum-builddep`` + and then running:: + + su -c "yum-builddep python-matplotlib" + +:term:`python` 2.4 (or later but not python3) + matplotlib requires python 2.4 or later (`download `__) + +:term:`numpy` 1.1 (or later) + array support for python (`download + `__) + +libpng 1.2 (or later) + library for loading and saving :term:`PNG` files (`download + `__). libpng requires + zlib. If you are a windows user, you can ignore this since we + build support into the matplotlib single click installer + +:term:`freetype` 1.4 (or later) + library for reading true type font files. If you are a windows + user, you can ignore this since we build support into the + matplotlib single click installer. + +**Optional** + +These are optional packages which you may want to install to use +matplotlib with a user interface toolkit. See +:ref:`what-is-a-backend` for more details on the optional matplotlib +backends and the capabilities they provide + +:term:`tk` 8.3 or later + The TCL/Tk widgets library used by the TkAgg backend + +:term:`pyqt` 3.1 or later + The Qt3 widgets library python wrappers for the QtAgg backend + +:term:`pyqt` 4.0 or later + The Qt4 widgets library python wrappers for the Qt4Agg backend + +:term:`pygtk` 2.4 or later + The python wrappers for the GTK widgets library for use with the + GTK or GTKAgg backend + +:term:`wxpython` 2.8 or later + The python wrappers for the wx widgets library for use with the + WX backend + +:term:`pyfltk` 1.0 or later + The python wrappers of the FLTK widgets library for use with FLTKAgg + +**Required libraries that ship with matplotlib** + +:term:`agg` 2.4 + The antigrain C++ rendering engine. matplotlib links against the + agg template source statically, so it will not affect anything on + your system outside of matplotlib. + +:term:`pytz` 2007g or later + timezone handling for python datetime objects. By default, + matplotlib will install pytz if it isn't already installed on your + system. To override the default, use :file:`setup.cfg` to force or + prevent installation of pytz. + +:term:`dateutil` 1.1 or later + provides extensions to python datetime handling. By default, matplotlib + will install dateutil if it isn't already installed on your + system. To override the default, use :file:`setup.cfg` to force + or prevent installation of dateutil. + +.. _build_osx: -INTRODUCTION - - matplotlib requires at a minimum python 2.4, numpy, libpng and - freetype. To get the most out of matplotlib, you will want to build - some of the optional GUI and image extensions, discussed below. - Matplotlib is known to work on linux, unix, win32 and OS X - platforms. - - There are two kinds of matplotlib backends: vector based and raster - based. The vector based backends, SVG, PDF and PS, produce ASCII - text output files *.svg, *.pdf and *.ps. The core raster based - renderer is the http://antigrain.com (agg) backend. This is a high - quality 2D library that supports fast antialiasing, alpha blending - and much more. If you want to produce PNGs or GUI images that - support all of matplotlib's features, you should compile matplotlib - with agg support and use one of the GUI agg backends: GTKAgg, WXAgg, - TkAgg or FLTKAgg. - -COMPILING - - You will need to have recent versions of freetype, libpng and zlib - installed on your system. If you are using a package manager, also - make sure the devel versions of these packages are also installed - (eg freetype-devel). - - matplotlib ships with a setup.cfg.template which you can use to - customize the build process. Copy it to setup.cfg if you need to - customize something. See that files for details of the parameters - you can set. - - If you want to use a GUI backend, you will need either Tkinter, - pygtk , wxPython or Qt installed on your system, from src or from a - package manager including the devel packages. You can choose which - backends to enable by setting the flags in setup.cfg, but the default - is to automatically detect your installed GUIs and build support for - them. If you later find that you did not have a GUI toolkit like - pygtk installed when you built matplotlib, but now want it, you will - need to install the toolkit and rebuild matplotlib. - - If you have installed prerequisites to nonstandard places and need - to inform matplotlib where they are, edit setupext.py an add the - base dirs to the 'basedir' dictionary entry for your sys.platform. - Eg, if the header to some required library is in - /some/path/include/somheader.h, put /some/path in the basedir list - for your platform. - - - Once you have everything above set to your liking, just do the usual - thing - - python setup.py build - python setup.py install - -WINDOWS - - If you don't already have python installed, you may want to consider - using the enthought edition of python, which has scipy, numpy, and - wxpython, plus a lot of other goodies, preinstalled - - http://www.enthought.com/python . With the enthought edition of - python + matplotlib installer, the following backends should work - out of the box: agg, wx, wxagg, tkagg, ps, pdf and svg. - - For standard python installations, you will also need to install - either numpy, in addition to the matplotlib installer. On some - systems you will also need to download msvcp71.dll library, which - you can download from - http://www.dll-files.com/dllindex/dll-files.shtml?msvcp71 or other - sites. You will need to unzip the archive and drag the dll into - c:\windows\system32 - - All of the GUI backends run on windows, but TkAgg is probably the - best for interactive use from the standard python shell or ipython. - The windows installer (*.exe) on the download page contains all the - code you need to get up and running. However, there are many - examples that are not included in the windows installer. If you - want to try the many demos that come in the matplotlib src - distribution, download the zip file and look in the examples subdir. - - Important: There are known conflicts with some of the backends with - some python IDEs such as pycrust, idle. If you want to - use matplotlib from an IDE, please consult - http://matplotlib.sf.net/backends.html for compatibility - information. You will have the greatest likelihood of - success if you run the examples from the command shell or - by double clicking on them, rather than from an IDE. If - you are interactively generating plots, your best bet is - TkAgg from the standard python shell or ipython. - -DISTROS: (rpms, apt, fink, osx, freebsd, gentoo, etc) - - RPMS - - To build all the backends on a binary linux distro such as redhat, - you need to install a number of the devel libs (and whatever - dependencies they require), I suggest - - matplotlib core: zlib, zlib-devel, libpng, libpng-devel, - freetype, freetype-devel, freetype-utils - - gtk backend: gtk2-devel, gtk+-devel, pygtk2, glib-devel, - pygtk2-devel, gnome-libs-devel, pygtk2-libglade - - tk backend: tcl, tk, tkinter - - DEBIAN - - matplotlib is part of debian (and ubuntu) so you shoule be able to - apt-get install it. - - FREEBSD - - http://www.freshports.org/math/py-matplotlib/ - - Gentoo - - http://www.gentoo-portage.com/dev-python/matplotlib - - OS X - - All of the backends run on OS X. You will need to install - freetype2, libpng and zlib via fink or from src. You will also - need the base libraries for a given backend. Eg, if you want to - run TkAgg, you will need a python with Tkinter; if you want to use - WxAgg, install wxpython. See - http://matplotlib.sf.net/backends.html for a more comprehensive - discussion of the various backend requirements. Edit setup.py to - configure the backends you want to build as described above. - - Note when running a GUI backend in OSX, you should launch your - programs with pythonw rather than python, or you may get - nonresponsive GUIs. - - FINK - - fink users should use Jeffrey Whitaker's fink distribution, which - supports the GTK, TkAgg, GTKAgg, PS, WX, WXAgg and Agg backends. - http://fink.sourceforge.net/pdb/package.php/matplotlib-py23 - +Building on OSX +=============== +The build situation on OSX is complicated by the various places one +can get the png and freetype requirements from (darwinports, fink, +/usr/X11R6) and the different architectures (x86, ppc, universal) and +the different OSX version (10.4 and 10.5). We recommend that you build +the way we do for the OSX release: by grabbing the tarbar or git +repository, cd-ing into the release/osx dir, and following the +instruction in the README. This directory has a Makefile which will +automatically grab the zlib, png and freetype dependencies from the +web, build them with the right flags to make universal libraries, and +then build the matplotlib source and binary installers. diff --git a/INTERACTIVE b/INTERACTIVE deleted file mode 100644 index 83694f731ea4..000000000000 --- a/INTERACTIVE +++ /dev/null @@ -1,74 +0,0 @@ -To use matplotlib interactively from the prompt, you need a special -interpreter that doesn't enter the gtk mainloop. One is provided in -examples/interactive.py. The coew functionality of this interpreter -was provided by Brian McErlean and John Finlay. See the header for -more info. - -This will only work if you have pygtk compiled with threading. See -instrubtions below. - -WIN32 Users - - I haven't tested the interactive shell on win32. It is apparently - possible, but not easy, to get threading to work on win32. See - http://www.async.com.br/faq/pygtk/index.py?req=show&file=faq21.003.htp - -LINUX/UNIX Users: - - To compile pygtk with threading, I recommend you make a backup of your - existing pygtk install tree and completely remove it - - eg - > cd /your/path/to/python/site-packages - > cp -a gtk-2.0 /some/backup/dir - > rm -rf gtk-2.0 - - This is because I have found that recompiling pygtk with threads on top - of an existing installation w/o threads does not enable threads. Your - milage may vary. - - Then from a clean pygtk src tree - - > ./configure --enable-thread # and any other flags - > make install - -TESTING threading - - To test whether this install worked, try running this script - - import pygtk - pygtk.require('2.0') - import gtk - - import threading, time - - def func(): - n = 0 - while 1: - print n - n += 1 - time.sleep(0.1) - - gtk.threads_init() - threading.Thread(target=func).start() - gtk.mainloop() - - - - If it spits numbers back at you, you're golden. - -TESTING with matplotlib - - You should be able to make a simple plot with - - > cd examples - > ./interactive.py - Welcome to matplotlib. - - help(matlab) -- shows a list of all matlab compatible commands provided - help(plotting) -- shows a list of plot specific commands - - >> plot([1,2,3]) - - - diff --git a/KNOWN_BUGS b/KNOWN_BUGS deleted file mode 100644 index 30e085ce82ab..000000000000 --- a/KNOWN_BUGS +++ /dev/null @@ -1,50 +0,0 @@ - -KNOWN BUGS - - - DONE - Circle needs to provide window extent -- see scatter demo - - - DONE - axes patch edge on gtk backend not complete - - - DONE - autoscale on y not satisfactory -- see histogram demo - - - DONE with multiple subplots there is a small gap between the - bottom of the xtick1 line and the axes patch edge. see - subplot_demo - - - DONE 2003-11-14 - lowest line position is off in legend_demo - - - DONE the markers for circ and square in the gtk backend do not have the - same center. See the stock_demo for example. - - - DONE 2003-11-16 - GD port of new API not done - - - DONE 2003-11-16 - xticklabels in PS backend need clip - - - x and y ticklabels overlap at origin - - - DONE parse error on lines like plot(bins, mu, bins, mu+sigma, '--') - - - DONE 2004-02-11 mri with eeg offsets whacked - - - add backend method draw ellipse -GD - - - FIXED right tickline is off - - - dashes not scaling with dpi properly - rounding error or other? - - - GD not allocating colors on high res figs - - - I don't know how to fill arcs in a way that will guarantee the - ellipse will be filled. gd fills till it meets a different border - color, which makes it difficult to fill arcs. expose gdEllipse - and gdFilledEllipse - -FT2 - - - chars are truncated on the right, eg, -0.8 in simple_plot - yticklabels - - - no rotated text - - - error handling diff --git a/LICENSE/LICENSE b/LICENSE/LICENSE index 9274210639e6..10850d6753ba 100644 --- a/LICENSE/LICENSE +++ b/LICENSE/LICENSE @@ -1,4 +1,4 @@ -LICENSE AGREEMENT FOR MATPLOTLIB 0.91 +LICENSE AGREEMENT FOR MATPLOTLIB 1.1.0 -------------------------------------- 1. This LICENSE AGREEMENT is between John D. Hunter ("JDH"), and the @@ -9,30 +9,30 @@ documentation. 2. Subject to the terms and conditions of this License Agreement, JDH hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare -derivative works, distribute, and otherwise use matplotlib 0.91 +derivative works, distribute, and otherwise use matplotlib 1.1.0 alone or in any derivative version, provided, however, that JDH's License Agreement and JDH's notice of copyright, i.e., "Copyright (c) -2002-2007 John D. Hunter; All Rights Reserved" are retained in -matplotlib 0.91 alone or in any derivative version prepared by +2002-2011 John D. Hunter; All Rights Reserved" are retained in +matplotlib 1.1.0 alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or -incorporates matplotlib 0.91 or any part thereof, and wants to +incorporates matplotlib 1.1.0 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of -the changes made to matplotlib 0.91. +the changes made to matplotlib 1.1.0. -4. JDH is making matplotlib 0.91 available to Licensee on an "AS +4. JDH is making matplotlib 1.1.0 available to Licensee on an "AS IS" basis. JDH MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, JDH MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB 0.91 +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB 1.1.0 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. JDH SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB -0.91 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR +1.1.0 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING -MATPLOTLIB 0.91, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF +MATPLOTLIB 1.1.0, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material @@ -44,6 +44,6 @@ Licensee. This License Agreement does not grant permission to use JDH trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. -8. By copying, installing or otherwise using matplotlib 0.91, +8. By copying, installing or otherwise using matplotlib 1.1.0, Licensee agrees to be bound by the terms and conditions of this License Agreement. diff --git a/LICENSE/LICENSE_PAINT b/LICENSE/LICENSE_PAINT deleted file mode 100644 index 9544d2ebb779..000000000000 --- a/LICENSE/LICENSE_PAINT +++ /dev/null @@ -1,23 +0,0 @@ -/****************************************************************** -Copyright 2000 by Object Craft P/L, Melbourne, Australia. - - All Rights Reserved - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appear in all copies and that -both that copyright notice and this permission notice appear in -supporting documentation, and that the name of Object Craft -is not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior -permission. - -OBJECT CRAFT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, -INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO -EVENT SHALL OBJECT CRAFT BE LIABLE FOR ANY SPECIAL, INDIRECT OR -CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF -USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. - -******************************************************************/ diff --git a/LICENSE/LICENSE_PIL b/LICENSE/LICENSE_PIL deleted file mode 100644 index 93841ca72195..000000000000 --- a/LICENSE/LICENSE_PIL +++ /dev/null @@ -1,25 +0,0 @@ -The Python Imaging Library is - -Copyright (c) 1997-2002 by Secret Labs AB -Copyright (c) 1995-2002 by Fredrik Lundh - -By obtaining, using, and/or copying this software and/or its -associated documentation, you agree that you have read, understood, -and will comply with the following terms and conditions: - -Permission to use, copy, modify, and distribute this software and its -associated documentation for any purpose and without fee is hereby -granted, provided that the above copyright notice appears in all -copies, and that both that copyright notice and this permission notice -appear in supporting documentation, and that the name of Secret Labs -AB or the author not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior -permission. - -SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO -THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR -ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.osx b/README.osx index dac81f47b90f..b0f4ad45838b 100644 --- a/README.osx +++ b/README.osx @@ -2,29 +2,12 @@ Build mpl on OSX has proven to be a nightmare because of all the different types of zlib, png and freetype that may be on your system. The recommended and supported way to build is to use the make.osx file in this directory. This script requires you to set a PREFIX -environment variable where you want the install to go. It will -fetch the dependencies from a remote server and install them into your +environment variable where you want to the install to go. It will +fetch the dependencies from a remove server and install them into your PREFIX dir, and then build mpl against them. It should ignore any lib, png or freetype on your system Example usage:: - make -f make.osx PREFIX=/Users/jdhunter/dev PYVERSION=2.6 \ - fetch deps mpl_install_std + PREFIX=/Users/jdhunter/dev make -f make.osx fetch deps mpl_install -Variables: - PREFIX (required): where to install the dependencies - PYVERSION (optional): which python version to use - (default=python, e.g. PYVERSION=2.6 uses python2.6) - -Targets: - - clean: remove compiled files - fetch: download dependencies - deps: build all dependencies (zlib, png, freetype) - mpl_build: compile matplotlib - mpl_install: install matplotlib in $PREFIX/lib/pythonX.Y/site-packages - mpl_install_std: install matplotlib in standard site-packages directory - mpl_install_egg: install matplotlib as an egg - mpl_install_develop: set up egg link to working directory (for developers) - binaries: create the dmg file for distribution diff --git a/README.txt b/README.txt index 318ee55f43c0..760b580af87a 100644 --- a/README.txt +++ b/README.txt @@ -43,3 +43,4 @@ interactive : False # see http://matplotlib.sourceforge.net/interactive.html See also + diff --git a/SEGFAULTS b/SEGFAULTS deleted file mode 100644 index a89b368d65a4..000000000000 --- a/SEGFAULTS +++ /dev/null @@ -1,30 +0,0 @@ -# How to diagnose where a segfault is occurring - -First thing to try is simply rm -rf the site-packages/matplotlib and -build subdirs and get a clean install. Installing a new version over -a pretty old version has been known to cause trouble, segfault, etc. - -Try importing these packages individually - - import matplotlib._image - import matplotlib._transforms - - #one of these three depending on which numerix package you are using - import matplotlib.backends._na_backend_agg # for numarray - import matplotlib.backends._nc_backend_agg # for Numeric - import matplotlib.backends._ns_backend_agg # for numpy - - import matplotlib.backends._tkagg - import matplotlib._agg - - -If the last two work and the others don't, it is likely you need to -upgrade your gcc, because on some platforms (OS X for sure) old -versions of gcc cannot compile new versions of pycxx, which matplotlib -uses for building some but not all of it's extensions. Report back -which if any work or segfault or raise tracebacks, - -If that shed additional light, again flush the build and install dirs, -and try setting VERBOSE=True in setup.py before doing a clean install. -The VERBOSE setting will generate lots of extra output and may help -indicate where the segfault is occurring diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/doc/README.txt b/doc/README.txt index 40bc64901ef0..7d0ca4b68378 100644 --- a/doc/README.txt +++ b/doc/README.txt @@ -15,6 +15,9 @@ python documentation system built on top of ReST. This directory contains * api - placeholders to automatically generate the api documentation +* mpl_toolkits - documentation of individual toolkits that ship with + matplotlib + * make.py - the build script to build the html or PDF docs * index.rst - the top level include document for matplotlib docs @@ -30,7 +33,7 @@ python documentation system built on top of ReST. This directory contains * mpl_examples - a link to the matplotlib examples in case any documentation wants to literal include them -To build the HTML documentation, install sphinx (0.5 or greater +To build the HTML documentation, install sphinx (1.0 or greater required), then type "python make.py html" in this directory. Wait for the initial run (which builds the example gallery) to be done, then run "python make.py html" again. The top file of the results will diff --git a/doc/conf.py b/doc/conf.py index 0c7beb0bbf67..89f74a04f250 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -25,7 +25,7 @@ # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['matplotlib.sphinxext.mathmpl', 'math_symbol_table', 'sphinx.ext.autodoc', 'matplotlib.sphinxext.only_directives', - 'matplotlib.sphinxext.plot_directive', 'inheritance_diagram', + 'matplotlib.sphinxext.plot_directive', 'sphinx.ext.inheritance_diagram', 'gen_gallery', 'gen_rst', 'matplotlib.sphinxext.ipython_console_highlighting'] diff --git a/doc/contents.rst b/doc/contents.rst index de48268ff0c1..8fed2cb8e10e 100644 --- a/doc/contents.rst +++ b/doc/contents.rst @@ -17,6 +17,7 @@ Overview users/index.rst faq/index.rst devel/index.rst + mpl_toolkits/index.rst api/index.rst glossary/index.rst diff --git a/doc/devel/documenting_mpl.rst b/doc/devel/documenting_mpl.rst index e46c1784d62f..bf256910cd4f 100644 --- a/doc/devel/documenting_mpl.rst +++ b/doc/devel/documenting_mpl.rst @@ -8,20 +8,12 @@ Getting started =============== The documentation for matplotlib is generated from ReStructured Text -using the Sphinx_ documentation generation tool. Sphinx-0.5 or later -is required. You might still run into problems, so most developers -work from the sphinx source repository (Mercurial based) because it -is a rapidly evolving project:: +using the Sphinx_ documentation generation tool. Sphinx-1.0 or later +is required. - hg clone http://bitbucket.org/birkenfeld/sphinx/ - cd sphinx - python setup.py install - -.. _Sphinx: http://sphinx.pocoo.org/ - -The documentation sources are found in the :file:`doc/` directory in the trunk. -To build the users guide in html format, cd into :file:`doc/` and -do:: +The documentation sources are found in the :file:`doc/` directory in +the trunk. To build the users guide in html format, cd into +:file:`doc/` and do:: python make.py html @@ -184,6 +176,7 @@ working with Sphinx in general. Here are a few additional things to keep in mind * The autodoc extension will handle index entries for the API, but additional entries in the index_ need to be explicitly added. +.. _Sphinx: http://sphinx.pocoo.org .. _documentation: http://sphinx.pocoo.org/contents.html .. _`inline markup`: http://sphinx.pocoo.org/markup/inline.html .. _index: http://sphinx.pocoo.org/markup/para.html#index-generating-markup @@ -213,19 +206,6 @@ Figures can be automatically generated from scripts and included in the docs. It is not necessary to explicitly save the figure in the script, this will be done automatically at build time to ensure that the code that is included runs and produces the advertised figure. -Several figures will be saved with the same basename as the filename -when the documentation is generated (low and high res PNGs, a PDF). -Matplotlib includes a Sphinx extension -(:file:`sphinxext/plot_directive.py`) for generating the images from -the python script and including either a png copy for html or a pdf -for latex:: - - .. plot:: pyplots/pyplot_simple.py - :include-source: - -If the script produces multiple figures (through multiple calls to -:func:`pyplot.figure`), each will be given a numbered file name and -included. The path should be relative to the ``doc`` directory. Any plots specific to the documentation should be added to the ``doc/pyplots`` @@ -238,8 +218,12 @@ directory. e.g.:: The ``:scale:`` directive rescales the image to some percentage of the original size, though we don't recommend using this in most cases since it is probably better to choose the correct figure size and dpi -in mpl and let it handle the scaling. ``:include-source:`` will -present the contents of the file, marked up as source code. +in mpl and let it handle the scaling. + +Plot directive documentation +'''''''''''''''''''''''''''' + +.. automodule:: matplotlib.sphinxext.plot_directive Static figures -------------- @@ -406,3 +390,4 @@ Some helpful functions:: Shift region to the right +.. TODO: Add section about uploading docs diff --git a/doc/devel/release_guide.rst b/doc/devel/release_guide.rst index 179b8f72a38f..faf2714b60f8 100644 --- a/doc/devel/release_guide.rst +++ b/doc/devel/release_guide.rst @@ -17,19 +17,20 @@ When doing a release Testing ======= -* Make sure :file:`examples/tests/backend_driver.py` runs without errors - and check the output of the PNG, PDF, PS and SVG backends +* Run all of the regression tests by running the `tests.py` script at + the root of the source tree. * Run :file:`unit/memleak_hawaii3.py` and make sure there are no memory leaks -* Run :file:`unit/nose_tests.py` and make sure all the unit tests are passing - * try some GUI examples, eg :file:`simple_plot.py` with GTKAgg, TkAgg, etc... * remove font cache and tex cache from :file:`.matplotlib` and test with and without cache on some example script +* Optionally, make sure :file:`examples/tests/backend_driver.py` runs + without errors and check the output of the PNG, PDF, PS and SVG + backends .. _release-branching: @@ -100,7 +101,6 @@ the release branch and merged into the trunk. Uploading ========= - * Post the win32 and OS-X binaries for testing and make a request on matplotlib-devel for testing. Pester us if we don't respond diff --git a/doc/faq/installing_faq.rst b/doc/faq/installing_faq.rst index 24f929711d48..6ccf40fe199e 100644 --- a/doc/faq/installing_faq.rst +++ b/doc/faq/installing_faq.rst @@ -276,9 +276,9 @@ macosx Cocoa rendering in OSX windows Compile matplotlib with PyGTK-2.4 ------------------------------------------- -There is a `bug in PyGTK-2.4`_. You need to edit -:file:`pygobject.h` to add the :c:macro:`G_BEGIN_DECLS` and :c:macro:`G_END_DECLS` -macros, and rename :c:data:`typename` parameter to :c:data:`typename_`:: +There is a `bug in PyGTK-2.4`_. You need to edit :file:`pygobject.h` +to add the :c:macro:`G_BEGIN_DECLS` and :c:macro:`G_END_DECLS` macros, +and rename :c:data:`typename` parameter to :c:data:`typename_`:: - const char *typename, + const char *typename_, diff --git a/doc/make.py b/doc/make.py index 121c10f57d26..2d368dd65eec 100755 --- a/doc/make.py +++ b/doc/make.py @@ -5,6 +5,11 @@ import shutil import sys +def copy_if_out_of_date(original, derived): + if (not os.path.exists(derived) or + os.stat(derived).st_mtime < os.stat(original).st_mtime): + shutil.copyfile(original, derived) + def check_build(): build_dirs = ['build', 'build/doctrees', 'build/html', 'build/latex', '_static', '_templates'] @@ -28,7 +33,7 @@ def figs(): def html(): check_build() - shutil.copy('../lib/matplotlib/mpl-data/matplotlibrc', '_static/matplotlibrc') + copy_if_out_of_date('../lib/matplotlib/mpl-data/matplotlibrc', '_static/matplotlibrc') if small_docs: options = "-D plot_formats=\"[('png', 80)]\"" else: @@ -102,6 +107,8 @@ def all(): # Change directory to the one containing this file current_dir = os.getcwd() os.chdir(os.path.dirname(os.path.join(current_dir, __file__))) +copy_if_out_of_date('../INSTALL', 'users/installing.rst') + if len(sys.argv)>1: if '--small' in sys.argv[1:]: diff --git a/doc/mpl_toolkits/axes_grid/index.rst b/doc/mpl_toolkits/axes_grid/index.rst index cf0df536393b..90f223c8436a 100644 --- a/doc/mpl_toolkits/axes_grid/index.rst +++ b/doc/mpl_toolkits/axes_grid/index.rst @@ -13,26 +13,21 @@ multiple axes according to their aspects. .. image:: ../../_static/demo_axes_grid.png -.. note:: +.. note:: AxesGrid toolkit has been a part of matplotlib since v - 0.99. Originally, the toolkit had a single namespace of - *axes_grid*. In more recent version (since svn r8226), the toolkit + 0.99. Originally, the toolkit had a single namespace of + *axes_grid*. In more recent version (since svn r8226), the toolkit has divided into two separate namespace (*axes_grid1* and *axisartist*). - While *axes_grid* namespace is maintained for he backward compatibility, + While *axes_grid* namespace is maintained for the backward compatibility, use of *axes_grid1* and *axisartist* is recommended. -.. warning:: +.. warning:: *axes_grid* and *axisartist* (but not *axes_grid1*) uses - a custome Axes class (derived from the mpl's original Axes class). - As a sideeffect, some commands (mostly tick-related) do not work. + a custom Axes class (derived from the mpl's original Axes class). + As a side effect, some commands (mostly tick-related) do not work. Use *axes_grid1* to avoid this, or see how things are different in *axes_grid* and *axisartist* (LINK needed) - - -Documentation -============= - .. toctree:: :maxdepth: 2 diff --git a/doc/mpl_toolkits/axes_grid/users/axes_divider.rst b/doc/mpl_toolkits/axes_grid/users/axes_divider.rst index 50d01827ad71..fe0b782306c5 100644 --- a/doc/mpl_toolkits/axes_grid/users/axes_divider.rst +++ b/doc/mpl_toolkits/axes_grid/users/axes_divider.rst @@ -4,20 +4,21 @@ AxesDivider The axes_divider module provide helper classes to adjust the axes positions of set of images in the drawing time. -* :mod:`~mpl_toolkits.axes_grid.axes_size` provides a classese of +* :mod:`~mpl_toolkits.axes_grid.axes_size` provides a classes of units that the size of each axes will be determined. For example, you can specify a fixed size * :class:`~mpl_toolkits.axes_grid.axes_size.Divider` this is the class that is used calculates the axes position. It divides the given - renctangular area into several areas. You intialize the divider by + rectangular area into several areas. You initialize the divider by setting the horizontal and vertical list of sizes that the division will be based on. You then use the new_locator method, whose return value is a callable object that can be used to set the axes_locator of the axes. -You first initialize the divider by specifying its grids, i.e., horiz and vert. +You first initialize the divider by specifying its grids, i.e., +horizontal and vertical. for example,:: @@ -56,8 +57,8 @@ be adjusted accordingly. The :mod:`mpl_toolkits.axes_grid.axes_size` contains several classes -that can be used to set the horiz and vert. For example, for the -vertical configuration above will be:: +that can be used to set the horizontal and vertical configurations. For +example, for the vertical configuration above will be:: from mpl_toolkits.axes_grid.axes_size import Fixed, Scaled vert = [Fixed(2), Scaled(2), Scaled(3)] @@ -84,7 +85,7 @@ See the example, .. plot:: mpl_toolkits/axes_grid/figures/simple_axes_divider2.py :include-source: -You can adjust the size of the each axes accroding to their x or y +You can adjust the size of the each axes according to their x or y data limits (AxesX and AxesY), similar to the axes aspect parameter. .. plot:: mpl_toolkits/axes_grid/figures/simple_axes_divider3.py diff --git a/doc/mpl_toolkits/axes_grid/users/axisartist.rst b/doc/mpl_toolkits/axes_grid/users/axisartist.rst index b25aed8eb97e..67cdf64ecfa1 100644 --- a/doc/mpl_toolkits/axes_grid/users/axisartist.rst +++ b/doc/mpl_toolkits/axes_grid/users/axisartist.rst @@ -4,11 +4,11 @@ AXISARTIST namespace ==================== -The AxisArtist namesapce includes a derived Axes implementation. The +The AxisArtist namespace includes a derived Axes implementation. The biggest difference is that the artists responsible to draw axis line, ticks, ticklabel and axis labels are separated out from the mpl's Axis class, which are much more than artists in the original mpl. This -change was strongly motivated to support curvlinear grid. Here are a +change was strongly motivated to support curvilinear grid. Here are a few things that mpl_tootlkits.axisartist.Axes is different from original Axes from mpl. @@ -18,7 +18,7 @@ Axes from mpl. have different tick location and different tick labels. * gridlines are drawn by a Gridlines instance. The change was - motivated that in curvelinear coordinate, a gridline may not cross + motivated that in curvilinear coordinate, a gridline may not cross axis-lines (i.e., no associated ticks). In the original Axes class, gridlines are tied to ticks. @@ -26,7 +26,7 @@ Axes from mpl. In summary, all these changes was to support -* a curvelinear grid. +* a curvilinear grid. * a floating axis .. plot:: mpl_toolkits/axes_grid/examples/demo_floating_axis.py @@ -111,7 +111,8 @@ Similarly, to make ticklabels invisible :: ax.axis["bottom"].major_ticklabels.set_visible(False) -AxisAritst provides a helper method to control the visibility of ticks, ticklabels, and label. To make ticklabel invisible, :: +AxisAritst provides a helper method to control the visibility of ticks, +ticklabels, and label. To make ticklabel invisible, :: ax.axis["bottom"].toggle(ticklabels=False) @@ -138,7 +139,7 @@ Note that 'ax.axis["top","right"]' returns a simple proxy object that translate for n in ["top","right"]: ax.axis[n].toggle(ticklabels=True)) -So, any return values in the for loop are ignored. And you shoud not +So, any return values in the for loop are ignored. And you should not use it anything more than a simple method. Like the list indexing ":" means all items, i.e., :: @@ -184,8 +185,8 @@ HowTo axis.label.set_pad method. -Rotaion and Alignment of TickLabels -=================================== +Rotation and Alignment of TickLabels +==================================== This is also quite different from the original mpl and can be confusing. When you want to rotate the ticklabels, first consider @@ -252,7 +253,7 @@ direction can be more clear with curved axis. .. plot:: mpl_toolkits/axes_grid/figures/demo_axis_direction.py -The axis_drection can be adjusted in the AxisArtist level, or in the +The axis_direction can be adjusted in the AxisArtist level, or in the level of its child arists, i.e., ticks, ticklabels, and axis-label. :: ax1.axis["left"].set_axis_direction("top") @@ -264,7 +265,7 @@ axis, while :: changes the axis_direction of only the major_ticklabels. Note that set_axis_direction in the AxisArtist level changes the -ticklabel_direction and label_direction, while chainging the +ticklabel_direction and label_direction, while changing the axis_direction of ticks, ticklabels, and axis-label does not affect them. @@ -295,11 +296,11 @@ So, in summary, * set_ticksize : size in points * TickLabels' methods (major_ticklabels and minor_ticklabels) * set_axis_direction : "left", "right", "bottom", or "top" - * set_rotation : angle with respect to the renference direction + * set_rotation : angle with respect to the reference direction * set_ha and set_va : see below * AxisLabels' methods (label) * set_axis_direction : "left", "right", "bottom", or "top" - * set_rotation : angle with respect to the renference direction + * set_rotation : angle with respect to the reference direction * set_ha and set_va @@ -329,13 +330,13 @@ Or ticklabels and axis-label :: GridHelper ========== -To actually define a curvelinear coordinate, you have to use your own +To actually define a curvilinear coordinate, you have to use your own grid helper. A generalised version of grid helper class is supplied and this class should suffice in most of cases. A user may provide two functions which defines a transformation (and its inverse pair) -from the curved coordinate to (rectlinear) image coordinate. Note that +from the curved coordinate to (rectilinear) image coordinate. Note that while ticks and grids are drawn for curved coordinate, the data -transform of the axes itself (ax.transData) is still rectlinear +transform of the axes itself (ax.transData) is still rectilinear (image) coordinate. :: @@ -404,7 +405,7 @@ required. :: ) -Again, the *transData* of the axes is still a rectlinear coordinate +Again, the *transData* of the axes is still a rectilinear coordinate (image coordinate). You may manually do conversion between two coordinates, or you may use Parasite Axes for convenience.:: @@ -447,7 +448,7 @@ Current Limitations and TODO's The code need more refinement. Here is a incomplete list of issues and TODO's * No easy way to support a user customized tick location (for - curvelinear grid). A new Locator class needs to be created. + curvilinear grid). A new Locator class needs to be created. * FloatingAxis may have coordinate limits, e.g., a floating axis of x = 0, but y only spans from 0 to 1. diff --git a/doc/mpl_toolkits/axes_grid/users/overview.rst b/doc/mpl_toolkits/axes_grid/users/overview.rst index b4d76d58a661..c2cef96d2084 100644 --- a/doc/mpl_toolkits/axes_grid/users/overview.rst +++ b/doc/mpl_toolkits/axes_grid/users/overview.rst @@ -17,19 +17,19 @@ mainly to ease displaying (multiple) images in matplotlib. 0.99. Originally, the toolkit had a single namespace of *axes_grid*. In more recent version (since svn r8226), the toolkit has divided into two separate namespace (*axes_grid1* and *axisartist*). - While *axes_grid* namespace is maintained for he backward compatibility, + While *axes_grid* namespace is maintained for the backward compatibility, use of *axes_grid1* and *axisartist* is recommended. .. warning:: *axes_grid* and *axisartist* (but not *axes_grid1*) uses - a custome Axes class (derived from the mpl's original Axes class). - As a sideeffect, some commands (mostly tick-related) do not work. + a custom Axes class (derived from the mpl's original Axes class). + As a side effect, some commands (mostly tick-related) do not work. Use *axes_grid1* to avoid this, or see how things are different in *axes_grid* and *axisartist* (LINK needed) AxesGrid toolkit has two namespaces (*axes_grid1* and *axisartist*). -*axisartist* contains custome Axes class that is meant to support for +*axisartist* contains custom Axes class that is meant to support for curvilinear grids (e.g., the world coordinate system in astronomy). Unlike mpl's original Axes class which uses Axes.xaxis and Axes.yaxis to draw ticks, ticklines and etc., Axes in axisartist uses special @@ -46,8 +46,8 @@ Axes.xaxis and Axes.yaxis may not work. See LINK for more detail. (multiple) images with matplotlib. In matplotlib, the axes location (and size) is specified in the normalized figure coordinates, which may not be ideal for displaying images that needs to have a given -aspect ratio. For example, it helps you to have a colobar whose -height always matches that of the image. `AxesGrid`_, `RGB Axes`_ and +aspect ratio. For example, it helps you to have a colorbar whose +height always matches that of the image. `ImageGrid`_, `RGB Axes`_ and `AxesDivider`_ are helper classes that deals with adjusting the location of (multiple) Axes. They provides a framework to adjust the position of multiple axes at the drawing time. `ParasiteAxes`_ @@ -76,9 +76,9 @@ used in such case. .. plot:: mpl_toolkits/axes_grid/examples/simple_axesgrid.py :include-source: -* The postion of each axes is determined at the drawing time (see +* The position of each axes is determined at the drawing time (see `AxesDivider`_), so that the size of the entire grid fits in the - given rectangle (like the aspec of axes). Note that in this example, + given rectangle (like the aspect of axes). Note that in this example, the paddings between axes are fixed even if you changes the figure size. @@ -141,7 +141,7 @@ AxesGrid takes following arguments, means the increasing direction of the axes number. *aspect* - By default (False), widths and heigths of axes in the grid are + By default (False), widths and heights of axes in the grid are scaled independently. If True, they are scaled according to their data limits (similar to aspect parameter in mpl). @@ -165,7 +165,7 @@ AxesGrid takes following arguments, | grid[1] | grid[3] | +---------+---------+ -You can also create a colorbar (or colobars). You can have colorbar +You can also create a colorbar (or colorbars). You can have colorbar for each axes (cbar_mode="each"), or you can have a single colorbar for the grid (cbar_mode="single"). The colorbar can be placed on your right, or top. The axes for each colorbar is stored as a *cbar_axes* @@ -187,7 +187,7 @@ at drawing time. While a more about the AxesDivider is (will be) explained in (yet to be written) AxesDividerGuide, direct use of the AxesDivider class will not be necessary for most users. The axes_divider module provides a helper function make_axes_locatable, -which can be useful. It takes a exisitng axes instance and create a +which can be useful. It takes a existing axes instance and create a divider for it. :: ax = subplot(1,1,1) @@ -196,7 +196,7 @@ divider for it. :: -*make_axes_locatable* returns an isntance of the AxesLocator class, +*make_axes_locatable* returns an instance of the AxesLocator class, derived from the Locator. It provides *append_axes* method that creates a new axes on the given side of ("top", "right", "bottom" and "left") of the original axes. @@ -255,7 +255,7 @@ if the host change its location (e.g., images). In most cases, you first create a host axes, which provides a few method that can be used to create parasite axes. They are *twinx*, *twiny* (which are similar to twinx and twiny in the matplotlib) and -*twin*. *twin* takes an arbitraty tranfromation that maps between the +*twin*. *twin* takes an arbitrary transformation that maps between the data coordinates of the host axes and the parasite axes. *draw* method of the parasite axes are never called. Instead, host axes collects artists in parasite axes and draw them as if they belong to @@ -292,7 +292,7 @@ tick-formatter for bottom(or left)-axis. :: -A more sophiscated example using twin. Note that if you change the +A more sophisticated example using twin. Note that if you change the x-limit in the host axes, the x-limit of the parasite axes will change accordingly. @@ -319,7 +319,7 @@ InsetLocator :mod:`mpl_toolkits.axes_grid.inset_locator` provides helper classes and functions to place your (inset) axes at the anchored position of -the parent axes, similarly to AnchoredArtis. +the parent axes, similarly to AnchoredArtist. Using :func:`mpl_toolkits.axes_grid.inset_locator.inset_axes`, you can have inset axes whose size is either fixed, or a fixed proportion @@ -359,7 +359,7 @@ represented by the inset axes. RGB Axes ~~~~~~~~ -RGBAxes is a helper clase to conveniently show RGB composite +RGBAxes is a helper class to conveniently show RGB composite images. Like ImageGrid, the location of axes are adjusted so that the area occupied by them fits in a given rectangle. Also, the xaxis and yaxis of each axes are shared. :: @@ -386,7 +386,7 @@ AxisArtist AxisArtist module provides a custom (and very experimental) Axes class, where each axis (left, right, top and bottom) have a separate -artist associated which is resposible to draw axis-line, ticks, +artist associated which is responsible to draw axis-line, ticks, ticklabels, label. Also, you can create your own axis, which can pass through a fixed position in the axes coordinate, or a fixed position in the data coordinate (i.e., the axis floats around when viewlimit @@ -407,7 +407,7 @@ To create an axes, :: ax = AA.Axes(fig, [0.1, 0.1, 0.8, 0.8]) fig.add_axes(ax) -or to creat a subplot :: +or to create a subplot :: ax = AA.Subplot(fig, 111) fig.add_subplot(ax) @@ -441,7 +441,7 @@ Or a fixed axis with some offset :: AxisArtist with ParasiteAxes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Most commands in the axes_grid toolkit1 can take a axes_class keyword +Most commands in the axes_grid1 toolkit can take a axes_class keyword argument, and the commands creates an axes of the given class. For example, to create a host subplot with axisartist.Axes, :: @@ -458,10 +458,10 @@ Here is an example that uses parasiteAxes. -Curvelinear Grid +Curvilinear Grid ---------------- -The motivation behind the AxisArtist module is to support cuvelinear grid +The motivation behind the AxisArtist module is to support curvilinear grid and ticks. .. plot:: mpl_toolkits/axes_grid/examples/demo_floating_axis.py @@ -472,7 +472,7 @@ See :ref:`axisartist-manual` for more details. Floating Axes ------------- -This also suppport a Floating Axes whose outer axis are defined as +This also support a Floating Axes whose outer axis are defined as floating axis. .. plot:: mpl_toolkits/axes_grid/examples/demo_floating_axes.py diff --git a/doc/users/toolkits.rst b/doc/mpl_toolkits/index.rst similarity index 94% rename from doc/users/toolkits.rst rename to doc/mpl_toolkits/index.rst index 5bbc7eebf70f..a09ab7e2923e 100644 --- a/doc/users/toolkits.rst +++ b/doc/mpl_toolkits/index.rst @@ -1,8 +1,15 @@ -.. _toolkits: +.. _toolkits-index: -******************* +######## Toolkits -******************* +######## + +.. htmlonly:: + + :Release: |version| + :Date: |today| + +.. _toolkits: Toolkits are collections of application-specific functions that extend matplotlib. @@ -66,5 +73,5 @@ ease displaying multiple images in matplotlib. The AxesGrid toolkit is distributed with matplotlib source. .. image:: ../_static/demo_axes_grid.png - + See :ref:`toolkit_axesgrid-index` for documentations. diff --git a/doc/pyplots/plotmap.py b/doc/pyplots/plotmap.py index 7d3267da7abc..02f12b7a4d41 100644 --- a/doc/pyplots/plotmap.py +++ b/doc/pyplots/plotmap.py @@ -1,62 +1,45 @@ -# make plot of etopo bathymetry/topography data on -# lambert conformal conic map projection, drawing coastlines, state and -# country boundaries, and parallels/meridians. +try: + from mpl_toolkits.basemap import Basemap + import matplotlib.pyplot as plt + import numpy as np -# the data is interpolated to the native projection grid. -import os -from mpl_toolkits.basemap import Basemap, shiftgrid -import numpy as np - -from pylab import title, colorbar, show, axes, cm, arange, figure, \ - text -from matplotlib.cbook import get_sample_data - -# read in topo data (on a regular lat/lon grid) -# longitudes go from 20 to 380. -topoin = np.loadtxt(get_sample_data('screenshots/etopo20data.gz').name) -lons = np.loadtxt(get_sample_data('screenshots/etopo20lons.gz').name) -lats = np.loadtxt(get_sample_data('screenshots/etopo20lats.gz').name) -# shift data so lons go from -180 to 180 instead of 20 to 380. -topoin,lons = shiftgrid(180.,topoin,lons,start=False) - -# setup of basemap ('lcc' = lambert conformal conic). -# use major and minor sphere radii from WGS84 ellipsoid. -m = Basemap(llcrnrlon=-145.5,llcrnrlat=1.,urcrnrlon=-2.566,urcrnrlat=46.352,\ - rsphere=(6378137.00,6356752.3142),\ - resolution='l',area_thresh=1000.,projection='lcc',\ - lat_1=50.,lon_0=-107.) -# transform to nx x ny regularly spaced native projection grid -nx = int((m.xmax-m.xmin)/40000.)+1; ny = int((m.ymax-m.ymin)/40000.)+1 -topodat,x,y = m.transform_scalar(topoin,lons,lats,nx,ny,returnxy=True) -# create the figure. -fig=figure(figsize=(6,6)) -# add an axes, leaving room for colorbar on the right. -ax = fig.add_axes([0.1,0.1,0.7,0.7]) -# plot image over map with imshow. -im = m.imshow(topodat,cm.jet) -# setup colorbar axes instance. -# for matplotlib 0.91 and earlier, could do l,b,w,h = ax.get_position() -# for post 0.91, pos = ax.get_position(); l,b,w,h = pos.bounds -# this works for both. -pos = ax.get_position() -l, b, w, h = getattr(pos, 'bounds', pos) -cax = axes([l+w+0.075, b, 0.05, h]) -colorbar(cax=cax) # draw colorbar -axes(ax) # make the original axes current again -# plot blue dot on boulder, colorado and label it as such. -xpt,ypt = m(-104.237,40.125) -m.plot([xpt],[ypt],'bo') -text(xpt+100000,ypt+100000,'Boulder') -# draw coastlines and political boundaries. -m.drawcoastlines() -m.drawcountries() -m.drawstates() -# draw parallels and meridians. -# label on left, right and bottom of map. -parallels = arange(0.,80,20.) -m.drawparallels(parallels,labels=[1,1,0,1]) -meridians = arange(10.,360.,30.) -m.drawmeridians(meridians,labels=[1,1,0,1]) -# set title. -title('ETOPO Topography - Lambert Conformal Conic') -show() + # create figure + fig = plt.figure(figsize=(8,8)) + # set up orthographic map projection with + # perspective of satellite looking down at 50N, 100W. + # use low resolution coastlines. + map = Basemap(projection='ortho',lat_0=50,lon_0=-100,resolution='l') + # lat/lon coordinates of five cities. + lats=[40.02,32.73,38.55,48.25,17.29] + lons=[-105.16,-117.16,-77.00,-114.21,-88.10] + cities=['Boulder, CO','San Diego, CA', + 'Washington, DC','Whitefish, MT','Belize City, Belize'] + # compute the native map projection coordinates for cities. + xc,yc = map(lons,lats) + # make up some data on a regular lat/lon grid. + nlats = 73; nlons = 145; delta = 2.*np.pi/(nlons-1) + lats = (0.5*np.pi-delta*np.indices((nlats,nlons))[0,:,:]) + lons = (delta*np.indices((nlats,nlons))[1,:,:]) + wave = 0.75*(np.sin(2.*lats)**8*np.cos(4.*lons)) + mean = 0.5*np.cos(2.*lats)*((np.sin(2.*lats))**2 + 2.) + # compute native map projection coordinates of lat/lon grid. + # (convert lons and lats to degrees first) + x, y = map(lons*180./np.pi, lats*180./np.pi) + # draw map boundary + map.drawmapboundary(color="0.9") + # draw graticule (latitude and longitude grid lines) + map.drawmeridians(np.arange(0,360,30),color="0.9") + map.drawparallels(np.arange(-90,90,30),color="0.9") + # plot filled circles at the locations of the cities. + map.plot(xc,yc,'wo') + # plot the names of five cities. + for name,xpt,ypt in zip(cities,xc,yc): + plt.text(xpt+100000,ypt+100000,name,fontsize=9,color='w') + # contour data over the map. + cs = map.contour(x,y,wave+mean,15,linewidths=1.5) + # draw blue marble image in background. + # (downsample the image by 50% for speed) + map.bluemarble(scale=0.5) + plt.show() +except ImportError: + pass diff --git a/doc/sphinxext/inheritance_diagram.py b/doc/sphinxext/inheritance_diagram.py deleted file mode 100644 index 407fc13ffb1b..000000000000 --- a/doc/sphinxext/inheritance_diagram.py +++ /dev/null @@ -1,407 +0,0 @@ -""" -Defines a docutils directive for inserting inheritance diagrams. - -Provide the directive with one or more classes or modules (separated -by whitespace). For modules, all of the classes in that module will -be used. - -Example:: - - Given the following classes: - - class A: pass - class B(A): pass - class C(A): pass - class D(B, C): pass - class E(B): pass - - .. inheritance-diagram: D E - - Produces a graph like the following: - - A - / \ - B C - / \ / - E D - -The graph is inserted as a PNG+image map into HTML and a PDF in -LaTeX. -""" - -import inspect -import os -import re -import subprocess -try: - from hashlib import md5 -except ImportError: - from md5 import md5 - -from docutils.nodes import Body, Element -from docutils.parsers.rst import directives -from sphinx.roles import xfileref_role - -def my_import(name): - """Module importer - taken from the python documentation. - - This function allows importing names with dots in them.""" - - mod = __import__(name) - components = name.split('.') - for comp in components[1:]: - mod = getattr(mod, comp) - return mod - -class DotException(Exception): - pass - -class InheritanceGraph(object): - """ - Given a list of classes, determines the set of classes that - they inherit from all the way to the root "object", and then - is able to generate a graphviz dot graph from them. - """ - def __init__(self, class_names, show_builtins=False): - """ - *class_names* is a list of child classes to show bases from. - - If *show_builtins* is True, then Python builtins will be shown - in the graph. - """ - self.class_names = class_names - self.classes = self._import_classes(class_names) - self.all_classes = self._all_classes(self.classes) - if len(self.all_classes) == 0: - raise ValueError("No classes found for inheritance diagram") - self.show_builtins = show_builtins - - py_sig_re = re.compile(r'''^([\w.]*\.)? # class names - (\w+) \s* $ # optionally arguments - ''', re.VERBOSE) - - def _import_class_or_module(self, name): - """ - Import a class using its fully-qualified *name*. - """ - try: - path, base = self.py_sig_re.match(name).groups() - except: - raise ValueError( - "Invalid class or module '%s' specified for inheritance diagram" % name) - fullname = (path or '') + base - path = (path and path.rstrip('.')) - if not path: - path = base - try: - module = __import__(path, None, None, []) - # We must do an import of the fully qualified name. Otherwise if a - # subpackage 'a.b' is requested where 'import a' does NOT provide - # 'a.b' automatically, then 'a.b' will not be found below. This - # second call will force the equivalent of 'import a.b' to happen - # after the top-level import above. - my_import(fullname) - - except ImportError: - raise ValueError( - "Could not import class or module '%s' specified for inheritance diagram" % name) - - try: - todoc = module - for comp in fullname.split('.')[1:]: - todoc = getattr(todoc, comp) - except AttributeError: - raise ValueError( - "Could not find class or module '%s' specified for inheritance diagram" % name) - - # If a class, just return it - if inspect.isclass(todoc): - return [todoc] - elif inspect.ismodule(todoc): - classes = [] - for cls in todoc.__dict__.values(): - if inspect.isclass(cls) and cls.__module__ == todoc.__name__: - classes.append(cls) - return classes - raise ValueError( - "'%s' does not resolve to a class or module" % name) - - def _import_classes(self, class_names): - """ - Import a list of classes. - """ - classes = [] - for name in class_names: - classes.extend(self._import_class_or_module(name)) - return classes - - def _all_classes(self, classes): - """ - Return a list of all classes that are ancestors of *classes*. - """ - all_classes = {} - - def recurse(cls): - all_classes[cls] = None - for c in cls.__bases__: - if c not in all_classes: - recurse(c) - - for cls in classes: - recurse(cls) - - return all_classes.keys() - - def class_name(self, cls, parts=0): - """ - Given a class object, return a fully-qualified name. This - works for things I've tested in matplotlib so far, but may not - be completely general. - """ - module = cls.__module__ - if module == '__builtin__': - fullname = cls.__name__ - else: - fullname = "%s.%s" % (module, cls.__name__) - if parts == 0: - return fullname - name_parts = fullname.split('.') - return '.'.join(name_parts[-parts:]) - - def get_all_class_names(self): - """ - Get all of the class names involved in the graph. - """ - return [self.class_name(x) for x in self.all_classes] - - # These are the default options for graphviz - default_graph_options = { - "rankdir": "LR", - "size": '"8.0, 12.0"' - } - default_node_options = { - "shape": "box", - "fontsize": 10, - "height": 0.25, - "fontname": "Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans", - "style": '"setlinewidth(0.5)"' - } - default_edge_options = { - "arrowsize": 0.5, - "style": '"setlinewidth(0.5)"' - } - - def _format_node_options(self, options): - return ','.join(["%s=%s" % x for x in options.items()]) - def _format_graph_options(self, options): - return ''.join(["%s=%s;\n" % x for x in options.items()]) - - def generate_dot(self, fd, name, parts=0, urls={}, - graph_options={}, node_options={}, - edge_options={}): - """ - Generate a graphviz dot graph from the classes that - were passed in to __init__. - - *fd* is a Python file-like object to write to. - - *name* is the name of the graph - - *urls* is a dictionary mapping class names to http urls - - *graph_options*, *node_options*, *edge_options* are - dictionaries containing key/value pairs to pass on as graphviz - properties. - """ - g_options = self.default_graph_options.copy() - g_options.update(graph_options) - n_options = self.default_node_options.copy() - n_options.update(node_options) - e_options = self.default_edge_options.copy() - e_options.update(edge_options) - - fd.write('digraph %s {\n' % name) - fd.write(self._format_graph_options(g_options)) - - for cls in self.all_classes: - if not self.show_builtins and cls in __builtins__.values(): - continue - - name = self.class_name(cls, parts) - - # Write the node - this_node_options = n_options.copy() - url = urls.get(self.class_name(cls)) - if url is not None: - this_node_options['URL'] = '"%s"' % url - fd.write(' "%s" [%s];\n' % - (name, self._format_node_options(this_node_options))) - - # Write the edges - for base in cls.__bases__: - if not self.show_builtins and base in __builtins__.values(): - continue - - base_name = self.class_name(base, parts) - fd.write(' "%s" -> "%s" [%s];\n' % - (base_name, name, - self._format_node_options(e_options))) - fd.write('}\n') - - def run_dot(self, args, name, parts=0, urls={}, - graph_options={}, node_options={}, edge_options={}): - """ - Run graphviz 'dot' over this graph, returning whatever 'dot' - writes to stdout. - - *args* will be passed along as commandline arguments. - - *name* is the name of the graph - - *urls* is a dictionary mapping class names to http urls - - Raises DotException for any of the many os and - installation-related errors that may occur. - """ - try: - dot = subprocess.Popen(['dot'] + list(args), - stdin=subprocess.PIPE, stdout=subprocess.PIPE, - close_fds=True) - except OSError: - raise DotException("Could not execute 'dot'. Are you sure you have 'graphviz' installed?") - except ValueError: - raise DotException("'dot' called with invalid arguments") - except: - raise DotException("Unexpected error calling 'dot'") - - self.generate_dot(dot.stdin, name, parts, urls, graph_options, - node_options, edge_options) - dot.stdin.close() - result = dot.stdout.read() - returncode = dot.wait() - if returncode != 0: - raise DotException("'dot' returned the errorcode %d" % returncode) - return result - -class inheritance_diagram(Body, Element): - """ - A docutils node to use as a placeholder for the inheritance - diagram. - """ - pass - -def inheritance_diagram_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, - state_machine): - """ - Run when the inheritance_diagram directive is first encountered. - """ - node = inheritance_diagram() - - class_names = arguments - - # Create a graph starting with the list of classes - graph = InheritanceGraph(class_names) - - # Create xref nodes for each target of the graph's image map and - # add them to the doc tree so that Sphinx can resolve the - # references to real URLs later. These nodes will eventually be - # removed from the doctree after we're done with them. - for name in graph.get_all_class_names(): - refnodes, x = xfileref_role( - 'class', ':class:`%s`' % name, name, 0, state) - node.extend(refnodes) - # Store the graph object so we can use it to generate the - # dot file later - node['graph'] = graph - # Store the original content for use as a hash - node['parts'] = options.get('parts', 0) - node['content'] = " ".join(class_names) - return [node] - -def get_graph_hash(node): - return md5(node['content'] + str(node['parts'])).hexdigest()[-10:] - -def html_output_graph(self, node): - """ - Output the graph for HTML. This will insert a PNG with clickable - image map. - """ - graph = node['graph'] - parts = node['parts'] - - graph_hash = get_graph_hash(node) - name = "inheritance%s" % graph_hash - path = '_images' - dest_path = os.path.join(setup.app.builder.outdir, path) - if not os.path.exists(dest_path): - os.makedirs(dest_path) - png_path = os.path.join(dest_path, name + ".png") - path = setup.app.builder.imgpath - - # Create a mapping from fully-qualified class names to URLs. - urls = {} - for child in node: - if child.get('refuri') is not None: - urls[child['reftitle']] = child.get('refuri') - elif child.get('refid') is not None: - urls[child['reftitle']] = '#' + child.get('refid') - - # These arguments to dot will save a PNG file to disk and write - # an HTML image map to stdout. - image_map = graph.run_dot(['-Tpng', '-o%s' % png_path, '-Tcmapx'], - name, parts, urls) - return ('%s' % - (path, name, name, image_map)) - -def latex_output_graph(self, node): - """ - Output the graph for LaTeX. This will insert a PDF. - """ - graph = node['graph'] - parts = node['parts'] - - graph_hash = get_graph_hash(node) - name = "inheritance%s" % graph_hash - dest_path = os.path.abspath(os.path.join(setup.app.builder.outdir, '_images')) - if not os.path.exists(dest_path): - os.makedirs(dest_path) - pdf_path = os.path.abspath(os.path.join(dest_path, name + ".pdf")) - - graph.run_dot(['-Tpdf', '-o%s' % pdf_path], - name, parts, graph_options={'size': '"6.0,6.0"'}) - return '\n\\includegraphics{%s}\n\n' % pdf_path - -def visit_inheritance_diagram(inner_func): - """ - This is just a wrapper around html/latex_output_graph to make it - easier to handle errors and insert warnings. - """ - def visitor(self, node): - try: - content = inner_func(self, node) - except DotException, e: - # Insert the exception as a warning in the document - warning = self.document.reporter.warning(str(e), line=node.line) - warning.parent = node - node.children = [warning] - else: - source = self.document.attributes['source'] - self.body.append(content) - node.children = [] - return visitor - -def do_nothing(self, node): - pass - -def setup(app): - setup.app = app - setup.confdir = app.confdir - - app.add_node( - inheritance_diagram, - latex=(visit_inheritance_diagram(latex_output_graph), do_nothing), - html=(visit_inheritance_diagram(html_output_graph), do_nothing)) - app.add_directive( - 'inheritance-diagram', inheritance_diagram_directive, - False, (1, 100, 0), parts = directives.nonnegative_int) diff --git a/doc/sphinxext/mathml.py b/doc/sphinxext/mathml.py deleted file mode 100644 index 098abd4024c6..000000000000 --- a/doc/sphinxext/mathml.py +++ /dev/null @@ -1,552 +0,0 @@ -from docutils import nodes -from docutils.writers.html4css1 import HTMLTranslator -from sphinx.latexwriter import LaTeXTranslator - -# Define LaTeX math node: -class latex_math(nodes.General, nodes.Element): - pass - -def math_role(role, rawtext, text, lineno, inliner, - options={}, content=[]): - i = rawtext.find('`') - latex = rawtext[i+1:-1] - try: - mathml_tree = parse_latex_math(latex, inline=True) - except SyntaxError, msg: - msg = inliner.reporter.error(msg, line=lineno) - prb = inliner.problematic(rawtext, rawtext, msg) - return [prb], [msg] - node = latex_math(rawtext) - node['latex'] = latex - node['mathml_tree'] = mathml_tree - return [node], [] - - -try: - from docutils.parsers.rst import Directive -except ImportError: - # Register directive the old way: - from docutils.parsers.rst.directives import _directives - def math_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - latex = ''.join(content) - try: - mathml_tree = parse_latex_math(latex, inline=False) - except SyntaxError, msg: - error = state_machine.reporter.error( - msg, nodes.literal_block(block_text, block_text), line=lineno) - return [error] - node = latex_math(block_text) - node['latex'] = latex - node['mathml_tree'] = mathml_tree - return [node] - math_directive.arguments = None - math_directive.options = {} - math_directive.content = 1 - _directives['math'] = math_directive -else: - class math_directive(Directive): - has_content = True - def run(self): - latex = ' '.join(self.content) - try: - mathml_tree = parse_latex_math(latex, inline=False) - except SyntaxError, msg: - error = self.state_machine.reporter.error( - msg, nodes.literal_block(self.block_text, self.block_text), - line=self.lineno) - return [error] - node = latex_math(self.block_text) - node['latex'] = latex - node['mathml_tree'] = mathml_tree - return [node] - from docutils.parsers.rst import directives - directives.register_directive('math', math_directive) - -def setup(app): - app.add_node(latex_math) - app.add_role('math', math_role) - - # Add visit/depart methods to HTML-Translator: - def visit_latex_math_html(self, node): - mathml = ''.join(node['mathml_tree'].xml()) - self.body.append(mathml) - def depart_latex_math_html(self, node): - pass - HTMLTranslator.visit_latex_math = visit_latex_math_html - HTMLTranslator.depart_latex_math = depart_latex_math_html - - # Add visit/depart methods to LaTeX-Translator: - def visit_latex_math_latex(self, node): - inline = isinstance(node.parent, nodes.TextElement) - if inline: - self.body.append('$%s$' % node['latex']) - else: - self.body.extend(['\\begin{equation}', - node['latex'], - '\\end{equation}']) - def depart_latex_math_latex(self, node): - pass - LaTeXTranslator.visit_latex_math = visit_latex_math_latex - LaTeXTranslator.depart_latex_math = depart_latex_math_latex - - -# LaTeX to MathML translation stuff: -class math: - """Base class for MathML elements.""" - - nchildren = 1000000 - """Required number of children""" - - def __init__(self, children=None, inline=None): - """math([children]) -> MathML element - - children can be one child or a list of children.""" - - self.children = [] - if children is not None: - if type(children) is list: - for child in children: - self.append(child) - else: - # Only one child: - self.append(children) - - if inline is not None: - self.inline = inline - - def __repr__(self): - if hasattr(self, 'children'): - return self.__class__.__name__ + '(%s)' % \ - ','.join([repr(child) for child in self.children]) - else: - return self.__class__.__name__ - - def full(self): - """Room for more children?""" - - return len(self.children) >= self.nchildren - - def append(self, child): - """append(child) -> element - - Appends child and returns self if self is not full or first - non-full parent.""" - - assert not self.full() - self.children.append(child) - child.parent = self - node = self - while node.full(): - node = node.parent - return node - - def delete_child(self): - """delete_child() -> child - - Delete last child and return it.""" - - child = self.children[-1] - del self.children[-1] - return child - - def close(self): - """close() -> parent - - Close element and return first non-full element.""" - - parent = self.parent - while parent.full(): - parent = parent.parent - return parent - - def xml(self): - """xml() -> xml-string""" - - return self.xml_start() + self.xml_body() + self.xml_end() - - def xml_start(self): - if not hasattr(self, 'inline'): - return ['<%s>' % self.__class__.__name__] - xmlns = 'http://www.w3.org/1998/Math/MathML' - if self.inline: - return ['' % xmlns] - else: - return ['' % xmlns] - - def xml_end(self): - return ['' % self.__class__.__name__] - - def xml_body(self): - xml = [] - for child in self.children: - xml.extend(child.xml()) - return xml - -class mrow(math): pass -class mtable(math): pass -class mtr(mrow): pass -class mtd(mrow): pass - -class mx(math): - """Base class for mo, mi, and mn""" - - nchildren = 0 - def __init__(self, data): - self.data = data - - def xml_body(self): - return [self.data] - -class mo(mx): - translation = {'<': '<', '>': '>'} - def xml_body(self): - return [self.translation.get(self.data, self.data)] - -class mi(mx): pass -class mn(mx): pass - -class msub(math): - nchildren = 2 - -class msup(math): - nchildren = 2 - -class msqrt(math): - nchildren = 1 - -class mroot(math): - nchildren = 2 - -class mfrac(math): - nchildren = 2 - -class msubsup(math): - nchildren = 3 - def __init__(self, children=None, reversed=False): - self.reversed = reversed - math.__init__(self, children) - - def xml(self): - if self.reversed: -## self.children[1:3] = self.children[2:0:-1] - self.children[1:3] = [self.children[2], self.children[1]] - self.reversed = False - return math.xml(self) - -class mfenced(math): - translation = {'\\{': '{', '\\langle': u'\u2329', - '\\}': '}', '\\rangle': u'\u232A', - '.': ''} - def __init__(self, par): - self.openpar = par - math.__init__(self) - - def xml_start(self): - open = self.translation.get(self.openpar, self.openpar) - close = self.translation.get(self.closepar, self.closepar) - return ['' % (open, close)] - -class mspace(math): - nchildren = 0 - -class mstyle(math): - def __init__(self, children=None, nchildren=None, **kwargs): - if nchildren is not None: - self.nchildren = nchildren - math.__init__(self, children) - self.attrs = kwargs - - def xml_start(self): - return [''] - -class mover(math): - nchildren = 2 - def __init__(self, children=None, reversed=False): - self.reversed = reversed - math.__init__(self, children) - - def xml(self): - if self.reversed: - self.children.reverse() - self.reversed = False - return math.xml(self) - -class munder(math): - nchildren = 2 - -class munderover(math): - nchildren = 3 - def __init__(self, children=None): - math.__init__(self, children) - -class mtext(math): - nchildren = 0 - def __init__(self, text): - self.text = text - - def xml_body(self): - return [self.text] - - -over = {'tilde': '~', - 'hat': '^', - 'bar': '_', - 'vec': u'\u2192'} - -Greek = { - # Upper case greek letters: - 'Phi': u'\u03a6', 'Xi': u'\u039e', 'Sigma': u'\u03a3', 'Psi': u'\u03a8', 'Delta': u'\u0394', 'Theta': u'\u0398', 'Upsilon': u'\u03d2', 'Pi': u'\u03a0', 'Omega': u'\u03a9', 'Gamma': u'\u0393', 'Lambda': u'\u039b'} -greek = { - # Lower case greek letters: - 'tau': u'\u03c4', 'phi': u'\u03d5', 'xi': u'\u03be', 'iota': u'\u03b9', 'epsilon': u'\u03f5', 'varrho': u'\u03f1', 'varsigma': u'\u03c2', 'beta': u'\u03b2', 'psi': u'\u03c8', 'rho': u'\u03c1', 'delta': u'\u03b4', 'alpha': u'\u03b1', 'zeta': u'\u03b6', 'omega': u'\u03c9', 'varepsilon': u'\u03b5', 'kappa': u'\u03ba', 'vartheta': u'\u03d1', 'chi': u'\u03c7', 'upsilon': u'\u03c5', 'sigma': u'\u03c3', 'varphi': u'\u03c6', 'varpi': u'\u03d6', 'mu': u'\u03bc', 'eta': u'\u03b7', 'theta': u'\u03b8', 'pi': u'\u03c0', 'varkappa': u'\u03f0', 'nu': u'\u03bd', 'gamma': u'\u03b3', 'lambda': u'\u03bb'} - -special = { - # Binary operation symbols: - 'wedge': u'\u2227', 'diamond': u'\u22c4', 'star': u'\u22c6', 'amalg': u'\u2a3f', 'ast': u'\u2217', 'odot': u'\u2299', 'triangleleft': u'\u25c1', 'bigtriangleup': u'\u25b3', 'ominus': u'\u2296', 'ddagger': u'\u2021', 'wr': u'\u2240', 'otimes': u'\u2297', 'sqcup': u'\u2294', 'oplus': u'\u2295', 'bigcirc': u'\u25cb', 'oslash': u'\u2298', 'sqcap': u'\u2293', 'bullet': u'\u2219', 'cup': u'\u222a', 'cdot': u'\u22c5', 'cap': u'\u2229', 'bigtriangledown': u'\u25bd', 'times': u'\xd7', 'setminus': u'\u2216', 'circ': u'\u2218', 'vee': u'\u2228', 'uplus': u'\u228e', 'mp': u'\u2213', 'dagger': u'\u2020', 'triangleright': u'\u25b7', 'div': u'\xf7', 'pm': u'\xb1', - # Relation symbols: - 'subset': u'\u2282', 'propto': u'\u221d', 'geq': u'\u2265', 'ge': u'\u2265', 'sqsubset': u'\u228f', 'Join': u'\u2a1d', 'frown': u'\u2322', 'models': u'\u22a7', 'supset': u'\u2283', 'in': u'\u2208', 'doteq': u'\u2250', 'dashv': u'\u22a3', 'gg': u'\u226b', 'leq': u'\u2264', 'succ': u'\u227b', 'vdash': u'\u22a2', 'cong': u'\u2245', 'simeq': u'\u2243', 'subseteq': u'\u2286', 'parallel': u'\u2225', 'equiv': u'\u2261', 'ni': u'\u220b', 'le': u'\u2264', 'approx': u'\u2248', 'precsim': u'\u227e', 'sqsupset': u'\u2290', 'll': u'\u226a', 'sqsupseteq': u'\u2292', 'mid': u'\u2223', 'prec': u'\u227a', 'succsim': u'\u227f', 'bowtie': u'\u22c8', 'perp': u'\u27c2', 'sqsubseteq': u'\u2291', 'asymp': u'\u224d', 'smile': u'\u2323', 'supseteq': u'\u2287', 'sim': u'\u223c', 'neq': u'\u2260', - # Arrow symbols: - 'searrow': u'\u2198', 'updownarrow': u'\u2195', 'Uparrow': u'\u21d1', 'longleftrightarrow': u'\u27f7', 'Leftarrow': u'\u21d0', 'longmapsto': u'\u27fc', 'Longleftarrow': u'\u27f8', 'nearrow': u'\u2197', 'hookleftarrow': u'\u21a9', 'downarrow': u'\u2193', 'Leftrightarrow': u'\u21d4', 'longrightarrow': u'\u27f6', 'rightharpoondown': u'\u21c1', 'longleftarrow': u'\u27f5', 'rightarrow': u'\u2192', 'Updownarrow': u'\u21d5', 'rightharpoonup': u'\u21c0', 'Longleftrightarrow': u'\u27fa', 'leftarrow': u'\u2190', 'mapsto': u'\u21a6', 'nwarrow': u'\u2196', 'uparrow': u'\u2191', 'leftharpoonup': u'\u21bc', 'leftharpoondown': u'\u21bd', 'Downarrow': u'\u21d3', 'leftrightarrow': u'\u2194', 'Longrightarrow': u'\u27f9', 'swarrow': u'\u2199', 'hookrightarrow': u'\u21aa', 'Rightarrow': u'\u21d2', - # Miscellaneous symbols: - 'infty': u'\u221e', 'surd': u'\u221a', 'partial': u'\u2202', 'ddots': u'\u22f1', 'exists': u'\u2203', 'flat': u'\u266d', 'diamondsuit': u'\u2662', 'wp': u'\u2118', 'spadesuit': u'\u2660', 'Re': u'\u211c', 'vdots': u'\u22ee', 'aleph': u'\u2135', 'clubsuit': u'\u2663', 'sharp': u'\u266f', 'angle': u'\u2220', 'prime': u'\u2032', 'natural': u'\u266e', 'ell': u'\u2113', 'neg': u'\xac', 'top': u'\u22a4', 'nabla': u'\u2207', 'bot': u'\u22a5', 'heartsuit': u'\u2661', 'cdots': u'\u22ef', 'Im': u'\u2111', 'forall': u'\u2200', 'imath': u'\u0131', 'hbar': u'\u210f', 'emptyset': u'\u2205', - # Variable-sized symbols: - 'bigotimes': u'\u2a02', 'coprod': u'\u2210', 'int': u'\u222b', 'sum': u'\u2211', 'bigodot': u'\u2a00', 'bigcup': u'\u22c3', 'biguplus': u'\u2a04', 'bigcap': u'\u22c2', 'bigoplus': u'\u2a01', 'oint': u'\u222e', 'bigvee': u'\u22c1', 'bigwedge': u'\u22c0', 'prod': u'\u220f', - # Braces: - 'langle': u'\u2329', 'rangle': u'\u232A'} - -sumintprod = ''.join([special[symbol] for symbol in - ['sum', 'int', 'oint', 'prod']]) - -functions = ['arccos', 'arcsin', 'arctan', 'arg', 'cos', 'cosh', - 'cot', 'coth', 'csc', 'deg', 'det', 'dim', - 'exp', 'gcd', 'hom', 'inf', 'ker', 'lg', - 'lim', 'liminf', 'limsup', 'ln', 'log', 'max', - 'min', 'Pr', 'sec', 'sin', 'sinh', 'sup', - 'tan', 'tanh', - 'injlim', 'varinjlim', 'varlimsup', - 'projlim', 'varliminf', 'varprojlim'] - - -def parse_latex_math(string, inline=True): - """parse_latex_math(string [,inline]) -> MathML-tree - - Returns a MathML-tree parsed from string. inline=True is for - inline math and inline=False is for displayed math. - - tree is the whole tree and node is the current element.""" - - # Normalize white-space: - string = ' '.join(string.split()) - - if inline: - node = mrow() - tree = math(node, inline=True) - else: - node = mtd() - tree = math(mtable(mtr(node)), inline=False) - - while len(string) > 0: - n = len(string) - c = string[0] - skip = 1 # number of characters consumed - if n > 1: - c2 = string[1] - else: - c2 = '' -## print n, string, c, c2, node.__class__.__name__ - if c == ' ': - pass - elif c == '\\': - if c2 in '{}': - node = node.append(mo(c2)) - skip = 2 - elif c2 == ' ': - node = node.append(mspace()) - skip = 2 - elif c2.isalpha(): - # We have a LaTeX-name: - i = 2 - while i < n and string[i].isalpha(): - i += 1 - name = string[1:i] - node, skip = handle_keyword(name, node, string[i:]) - skip += i - elif c2 == '\\': - # End of a row: - entry = mtd() - row = mtr(entry) - node.close().close().append(row) - node = entry - skip = 2 - else: - raise SyntaxError('Syntax error: "%s%s"' % (c, c2)) - elif c.isalpha(): - node = node.append(mi(c)) - elif c.isdigit(): - node = node.append(mn(c)) - elif c in "+-/()[]|=<>,.!'": - node = node.append(mo(c)) - elif c == '_': - child = node.delete_child() - if isinstance(child, msup): - sub = msubsup(child.children, reversed=True) - elif isinstance(child, mo) and child.data in sumintprod: - sub = munder(child) - else: - sub = msub(child) - node.append(sub) - node = sub - elif c == '^': - child = node.delete_child() - if isinstance(child, msub): - sup = msubsup(child.children) - elif isinstance(child, mo) and child.data in sumintprod: - sup = mover(child) - elif (isinstance(child, munder) and - child.children[0].data in sumintprod): - sup = munderover(child.children) - else: - sup = msup(child) - node.append(sup) - node = sup - elif c == '{': - row = mrow() - node.append(row) - node = row - elif c == '}': - node = node.close() - elif c == '&': - entry = mtd() - node.close().append(entry) - node = entry - else: - raise SyntaxError('Illegal character: "%s"' % c) - string = string[skip:] - return tree - - -mathbb = {'A': u'\U0001D538', - 'B': u'\U0001D539', - 'C': u'\u2102', - 'D': u'\U0001D53B', - 'E': u'\U0001D53C', - 'F': u'\U0001D53D', - 'G': u'\U0001D53E', - 'H': u'\u210D', - 'I': u'\U0001D540', - 'J': u'\U0001D541', - 'K': u'\U0001D542', - 'L': u'\U0001D543', - 'M': u'\U0001D544', - 'N': u'\u2115', - 'O': u'\U0001D546', - 'P': u'\u2119', - 'Q': u'\u211A', - 'R': u'\u211D', - 'S': u'\U0001D54A', - 'T': u'\U0001D54B', - 'U': u'\U0001D54C', - 'V': u'\U0001D54D', - 'W': u'\U0001D54E', - 'X': u'\U0001D54F', - 'Y': u'\U0001D550', - 'Z': u'\u2124'} - -negatables = {'=': u'\u2260', - '\in': u'\u2209', - '\equiv': u'\u2262'} - - -def handle_keyword(name, node, string): - skip = 0 - if len(string) > 0 and string[0] == ' ': - string = string[1:] - skip = 1 - if name == 'begin': - if not string.startswith('{matrix}'): - raise SyntaxError('Expected "\begin{matrix}"!') - skip += 8 - entry = mtd() - table = mtable(mtr(entry)) - node.append(table) - node = entry - elif name == 'end': - if not string.startswith('{matrix}'): - raise SyntaxError('Expected "\end{matrix}"!') - skip += 8 - node = node.close().close().close() - elif name == 'text': - if string[0] != '{': - raise SyntaxError('Expected "\text{...}"!') - i = string.find('}') - if i == -1: - raise SyntaxError('Expected "\text{...}"!') - node = node.append(mtext(string[1:i])) - skip += i + 1 - elif name == 'sqrt': - sqrt = msqrt() - node.append(sqrt) - node = sqrt - elif name == 'frac': - frac = mfrac() - node.append(frac) - node = frac - elif name == 'left': - for par in ['(', '[', '|', '\\{', '\\langle', '.']: - if string.startswith(par): - break - else: - raise SyntaxError('Missing left-brace!') - fenced = mfenced(par) - node.append(fenced) - row = mrow() - fenced.append(row) - node = row - skip += len(par) - elif name == 'right': - for par in [')', ']', '|', '\\}', '\\rangle', '.']: - if string.startswith(par): - break - else: - raise SyntaxError('Missing right-brace!') - node = node.close() - node.closepar = par - node = node.close() - skip += len(par) - elif name == 'not': - for operator in negatables: - if string.startswith(operator): - break - else: - raise SyntaxError('Expected something to negate: "\\not ..."!') - node = node.append(mo(negatables[operator])) - skip += len(operator) - elif name == 'mathbf': - style = mstyle(nchildren=1, fontweight='bold') - node.append(style) - node = style - elif name == 'mathbb': - if string[0] != '{' or not string[1].isupper() or string[2] != '}': - raise SyntaxError('Expected something like "\mathbb{A}"!') - node = node.append(mi(mathbb[string[1]])) - skip += 3 - elif name in greek: - node = node.append(mi(greek[name])) - elif name in Greek: - node = node.append(mo(Greek[name])) - elif name in special: - node = node.append(mo(special[name])) - elif name in functions: - node = node.append(mo(name)) - else: - chr = over.get(name) - if chr is not None: - ovr = mover(mo(chr), reversed=True) - node.append(ovr) - node = ovr - else: - raise SyntaxError('Unknown LaTeX command: ' + name) - - return node, skip diff --git a/doc/users/gridspec.rst b/doc/users/gridspec.rst index 3c71587e6431..0271d0fc3671 100644 --- a/doc/users/gridspec.rst +++ b/doc/users/gridspec.rst @@ -129,6 +129,16 @@ parameters are set to that of the location of the given SubplotSpec. :: .. plot:: users/plotting/examples/demo_gridspec04.py +A Complex Nested GridSpec using SubplotSpec +=========================================== + +Here's a more sophisticated example of nested gridspec where we put +a box around each cell of the outer 4x4 grid, by hiding appropriate +spines in each of the inner 3x3 grids. :: + +.. plot:: users/plotting/examples/demo_gridspec06.py + + GridSpec with Varying Cell Sizes ================================ diff --git a/doc/users/image_tutorial.rst b/doc/users/image_tutorial.rst index ab5fe00703fa..1bcdab9b1aad 100644 --- a/doc/users/image_tutorial.rst +++ b/doc/users/image_tutorial.rst @@ -152,7 +152,7 @@ plot from the prompt. import matplotlib.pyplot as plt import matplotlib.image as mpimg import numpy as np - img = mpimg.imread('_static/stinkbug.png') + img = mpimg.imread('../_static/stinkbug.png') imgplot = plt.imshow(img) You can also plot any numpy array - just remember that the datatype @@ -188,7 +188,7 @@ This is array slicing. You can read more in the `Numpy tutorial `_ for Windows, OS X or -Redhat is an excellent choice that "just works" out of the box. -Another excellent alternative for Windows users is `Python (x, y) -`_ which tends to be updated a -bit more frequently. Both of these packages include matplotlib and -pylab, and *lots* of other useful tools. matplotlib is also packaged -for pretty much every major linux distribution, so if you are on linux -your package manager will probably provide matplotlib prebuilt. - -One single click installer and you are done. - -OK, so you want to do it the hard way? -====================================== - -For some people, the prepackaged pythons discussed above are not an -option. That's OK, it's usually pretty easy to get a custom install -working. You will first need to find out if you have python installed -on your machine, and if not, install it. The official python builds -are available for download `here `_, -but OS X users please read :ref:`which-python-for-osx`. - -Once you have python up and running, you will need to install `numpy -`_. -numpy provides high performance array data structures and mathematical -functions, and is a requirement for matplotlib. You can test your -progress:: - - >>> import numpy - >>> print numpy.__version__ - -matplotlib requires numpy version 1.1 or later. Although it is not a -requirement to use matplotlib, we strongly encourage you to install -`ipython `_, which is an interactive -shell for python that is matplotlib aware. - -Next we need to get matplotlib installed. We provide prebuilt -binaries for OS X and Windows on the matplotlib `download -`_ page. Click on -the latest release of the "matplotlib" package, choose your python -version (2.5 or 2.6) and your platform (macosx or win32) and you -should be good to go. If you have any problems, please check the -:ref:`installing-faq`, google around a little bit, and post a question -the `mailing list -`_. If -you are on debian/unbuntu linux, it suffices to do:: - - > sudo apt-get install python-matplotlib - -Instructions for installing our OSX binaries are found in the FAQ -:ref:`install_osx_binaries`. - - -Once you have ipython, numpy and matplotlib installed, in ipython's -"pylab" mode you have a MATLAB-like environment that automatically -handles most of the configuration details for you, so you can get up -and running quickly:: - - johnh@flag:~> ipython -pylab - Python 2.4.5 (#4, Apr 12 2008, 09:09:16) - IPython 0.9.0 -- An enhanced Interactive Python. - - Welcome to pylab, a matplotlib-based Python environment. - For more information, type 'help(pylab)'. - - In [1]: x = randn(10000) - - In [2]: hist(x, 100) - -Instructions for installing our OSX binaries are found in the FAQ -ref:`install_osx_binaries`. - -Note that when testing matplotlib installations from the interactive -python console, there are some issues relating to user interface -toolkits and interactive settings that are discussed in -:ref:`mpl-shell`. - -.. _install_from_source: - -Installing from source -====================== - -If you are interested perhaps in contributing to matplotlib -development, running the latest greatest code, or just like to -build everything yourself, it is not difficult to build matplotlib -from source. Grab the latest *tar.gz* release file from `sourceforge -`_, or if -you want to develop matplotlib or just need the latest bugfixed -version, grab the latest git version :ref:`install-from-git`. - -Once you have satisfied the requirements detailed below (mainly -python, numpy, libpng and freetype), you build matplotlib in the usual -way:: - - cd matplotlib - python setup.py build - python setup.py install - -We provide a `setup.cfg -`_ -file that lives along :file:`setup.py` which you can use to customize -the build process, for example, which default backend to use, whether -some of the optional libraries that matplotlib ships with are -installed, and so on. This file will be particularly useful to those -packaging matplotlib. - - -.. _install_requirements: - -Build requirements -================== - -These are external packages which you will need to install before -installing matplotlib. Windows users only need the first two (python -and numpy) since the others are built into the matplotlib windows -installers available for download at the sourceforge site. If you are -building on OSX, see :ref:`build_osx`. If you are installing -dependencies with a package manager, you may need to install the -development packages (look for a "-dev" postfix) in addition to the -libraries themselves. - -:term:`python` 2.4 (or later but not python3) - matplotlib requires python 2.4 or later (`download `__) - -:term:`numpy` 1.1 (or later) - array support for python (`download - `__) - -libpng 1.1 (or later) - library for loading and saving :term:`PNG` files (`download - `__). libpng requires - zlib. If you are a windows user, you can ignore this since we - build support into the matplotlib single click installer - -:term:`freetype` 1.4 (or later) - library for reading true type font files. If you are a windows - user, you can ignore this since we build support into the - matplotlib single click installer. - -**Optional** - -These are optional packages which you may want to install to use -matplotlib with a user interface toolkit. See -:ref:`what-is-a-backend` for more details on the optional matplotlib -backends and the capabilities they provide - -:term:`tk` 8.3 or later - The TCL/Tk widgets library used by the TkAgg backend - -:term:`pyqt` 3.1 or later - The Qt3 widgets library python wrappers for the QtAgg backend - -:term:`pyqt` 4.0 or later - The Qt4 widgets library python wrappers for the Qt4Agg backend - -:term:`pygtk` 2.4 or later - The python wrappers for the GTK widgets library for use with the - GTK or GTKAgg backend - -:term:`wxpython` 2.6 or later - The python wrappers for the wx widgets library for use with the - WXAgg backend - -:term:`wxpython` 2.8 or later - The python wrappers for the wx widgets library for use with the - WX backend - -:term:`pyfltk` 1.0 or later - The python wrappers of the FLTK widgets library for use with FLTKAgg - -**Required libraries that ship with matplotlib** - -:term:`agg` 2.4 - The antigrain C++ rendering engine. matplotlib links against the - agg template source statically, so it will not affect anything on - your system outside of matplotlib. - -:term:`pytz` 2007g or later - timezone handling for python datetime objects. By default, - matplotlib will install pytz if it isn't already installed on your - system. To override the default, use :file:`setup.cfg` to force or - prevent installation of pytz. - -:term:`dateutil` 1.1 or later - provides extensions to python datetime handling. By default, matplotlib - will install dateutil if it isn't already installed on your - system. To override the default, use :file:`setup.cfg` to force - or prevent installation of dateutil. - - - -.. _build_osx: - -Building on OSX -================== - -The build situation on OSX is complicated by the various places one -can get the png and freetype requirements from (darwinports, fink, -/usr/X11R6) and the different architectures (x86, ppc, universal) and -the different OSX version (10.4 and 10.5). We recommend that you build -the way we do for the OSX release: by grabbing the tarbar or git -repository, cd-ing into the release/osx dir, and following the -instruction in the README. This directory has a Makefile which will -automatically grab the zlib, png and freetype dependencies from the -web, build them with the right flags to make universal libraries, and -then build the matplotlib source and binary installers. - diff --git a/doc/users/legend_guide.rst b/doc/users/legend_guide.rst index 7afcf0bd1cb6..1f23e7e0fa10 100644 --- a/doc/users/legend_guide.rst +++ b/doc/users/legend_guide.rst @@ -28,39 +28,43 @@ For example, *ax.legend()* is equivalent to:: The :meth:`~matplotlib.axes.Axes.get_legend_handles_labels` method returns a tuple of two lists, i.e., list of artists and list of labels (python string). However, it does not return all of its child -artists. It returns all artists in *ax.lines* and *ax.patches* and -some artists in *ax.collection* which are instance of +artists. It returns artists that are currently supported by matplotlib. + +For matplotlib v1.0 and before, the supported artists are as follows. + + * :class:`~matplotlib.lines.Line2D` + * :class:`~matplotlib.patches.Patch` + * :class:`~matplotlib.collections.LineCollection` + * :class:`~matplotlib.collections.RegularPolyCollection` + +And, :meth:`~matplotlib.axes.Axes.get_legend_handles_labels` returns +all artists in *ax.lines*, *ax.patches* and +artists in *ax.collection* which are instance of :class:`~matplotlib.collections.LineCollection` or :class:`~matplotlib.collections.RegularPolyCollection`. The label attributes (returned by get_label() method) of collected artists are used as text labels. If label attribute is empty string or starts with -"_", that artist will be ignored. +"_", those artists will be ignored. - * Note that not all kind of artists are supported by the legend. The - following is the list of artists that are currently supported. +Therefore, plots drawn by some *pyplot* commands are not supported by +legend. For example, :func:`~matplotlib.pyplot.fill_between` creates +:class:`~matplotlib.collections.PolyCollection` that is not +supported. Also support is limted for some commands that creat +multiple artists. For example, :func:`~matplotlib.pyplot.errorbar` +creates multiples :class:`~matplotlib.lines.Line2D` instances. - * :class:`~matplotlib.lines.Line2D` - * :class:`~matplotlib.patches.Patch` - * :class:`~matplotlib.collections.LineCollection` - * :class:`~matplotlib.collections.RegularPolyCollection` +Unfortunately, there is no easy workaround when you need legend for an +artist not supported by matplotlib (You may use one of the supported +artist as a proxy. See below) - Unfortunately, there is no easy workaround when you need legend for - an artist not in the above list (You may use one of the supported - artist as a proxy. See below), or customize it beyond what is - supported by :class:`matplotlib.legend.Legend`. +In newer version of matplotlib (v1.1 and later), the matplotlib +internals are revised to support - * Remember that some *pyplot* commands return artist not supported by - legend, e.g., :func:`~matplotlib.pyplot.fill_between` returns - :class:`~matplotlib.collections.PolyCollection` that is not - supported. Or some return multiple artists. For example, - :func:`~matplotlib.pyplot.plot` returns list of - :class:`~matplotlib.lines.Line2D` instances, and - :func:`~matplotlib.pyplot.errorbar` returns a length 3 tuple of - :class:`~matplotlib.lines.Line2D` instances. + * complex plots that creates multiple artists (e.g., bar, errorbar, etc) + * custom legend handles - * The legend does not care about the axes that given artists belongs, - i.e., the artists may belong to other axes or even none. +See below for details of new functionality. Adjusting the Order of Legend items @@ -180,3 +184,132 @@ legend. .. plot:: users/plotting/examples/simple_legend02.py :include-source: + + +Legend of Complex Plots +======================= + +In matplotlib v1.1 (FIXME when released) and later, the legend is +improved to support more plot commands and ease the customization. + +Artist Container +---------------- + +The Artist Container is simple class (derived from tuple) that +contains multiple artists. This is introduced primarily to support +legends for complex plot commands that create multiple artists. + +Axes instances now have a "containers" attribute (which is a list, and +this is only intended to be used for generating a legend). The items +in this attribute are also returned by +:meth:`~matplotlib.axes.Axes.get_legend_handles_labels`. + +For example, "bar" command creates a series of Rectangle +patches. Previously, it returned a list of these patches. With the +current change, it creates a container object of these rectangle +patches (and these patches are added to Axes.patches attribute as +before) and return it instead. As the container class is derived from +a tuple, it should be backward-compatible. Furthermore, the container +object is added to the Axes.containers attributes so that legend +command can properly create a legend for the bar. Thus, you may do :: + + b1 = bar([0, 1, 2], [0.2, 0.3, 0.1], width=0.4, + label="Bar 1", align="center") + legend() + +or :: + + b1 = bar([0, 1, 2], [0.2, 0.3, 0.1], width=0.4, align="center") + legend([b1], ["Bar 1"]) + + +At this time of writing, however, "bar" and "errorbar" are only +supported (hopefully the list will increase). Here is an example. + +.. plot:: mpl_examples/pylab_examples/legend_demo4.py + +Legend Handler +-------------- + +One of the change is that drawing of legend handles is delegated to +legend handlers. For example, :class:`~matplotlib.lines.Line2D` +instances are handled by +:class:`~matplotlib.legend_handler.HandlerLine2D`. The mapping +between the artists and their corresponding handlers are defined in a +handler_map of the legend. The handler_map is a dictionary of +key-handler pair, where key can be an artist instance or its +class. And the handler is a Handler instance. + +Let's consider the following sample code, :: + + legend([p_1, p_2,..., p_i, ...], ["Test 1", "Test 2", ..., "Test i",...]) + +For each *p_i*, matplotlib + + 1. check if *p_i* itself is in the handler_map + 2. if not, iterate over type(p_i).mro() until a matching key is found in the handler_map + + +Unless specified, the defaul handler_map is used. Below is a partial +list of key-handler pairs included in the default handler map. + + * Line2D : legend_handler.HandlerLine2D() + * Patch : legend_handler.HandlerPatch() + * LineCollection : legend_handler.HandlerLineCollection() + * ... + + +The legend command takes an optional argument of "handler_map". When +provided, the default handler map will be updated (using dict.update +method) with the provided one. :: + + p1, = plot(x, "ro", label="test1") + p2, = plot(y, "b+", ms=10, label="test2") + + my_handler = HandlerLine2D(numpoints=1) + + legend(handler_map={Line2D:my_handler}) + +The above example will use *my_handler* for any Line2D +instances (p1 and p2). :: + + legend(handler_map={p1:HandlerLine2D(numpoints=1)}) + +In the above example, only *p1* will be handled by *my_handler*, while +others will be handled by default handlers. + +The curent default handler_map has handlers for errobar and bar +plots. Also, it includes an entry for `tuple` which is mapped to +`HandlerTuple`. It simply overplots all the handles for items in the +given tuple. For example, + + +.. plot:: + :include-source: + + z = np.random.randn(10) + + p1a, = plt.plot(z, "ro", ms=10, mfc="r", mew=2, mec="r") # red filled circle + p1b, = plt.plot(z[:5], "w+", ms=10, mec="w", mew=2) # white cross + + plt.legend([p1a, (p1a, p1b)], ["Attr A", "Attr A+B"]) + + + +Implement a Custom Handler +-------------------------- + +Handler can be any callable object with following signature. :: + + def __call__(self, legend, orig_handle, + fontsize, + handlebox): + +Where *legend* is the legend itself, *orig_handle* is the original +plot (*p_i* in the above example), *fontsize* is the fontsize in +pixles, and *handlebox* is a OffsetBox instance. Within the call, you +create relevant artists (using relevant properties from the *legend* +and/or *orig_handle*) and add them into the handlebox. The artists +needs to be scaled according to the fontsize (note that the size is in +pixel, i.e., this is dpi-scaled value). See legend_handler.py for more +details. diff --git a/doc/users/mathtext.rst b/doc/users/mathtext.rst index 790114d66945..8fd934c6d598 100644 --- a/doc/users/mathtext.rst +++ b/doc/users/mathtext.rst @@ -299,6 +299,7 @@ There are long and short forms for some of them. ``\hat a`` or ``\^a`` :math:`\hat a` ``\tilde a`` or ``\~a`` :math:`\tilde a` ``\vec a`` :math:`\vec a` + ``\overline{abc}`` :math:`\overline{abc}` ============================== ================================= In addition, there are two special accents that automatically adjust diff --git a/doc/users/plotting/examples/demo_gridspec06.py b/doc/users/plotting/examples/demo_gridspec06.py new file mode 100644 index 000000000000..261668b30168 --- /dev/null +++ b/doc/users/plotting/examples/demo_gridspec06.py @@ -0,0 +1,53 @@ +import matplotlib.pyplot as plt +import matplotlib.gridspec as gridspec +import numpy as np + +try: + from itertools import product +except ImportError: + # product is new in v 2.6 + def product(*args, **kwds): + pools = map(tuple, args) * kwds.get('repeat', 1) + result = [[]] + for pool in pools: + result = [x+[y] for x in result for y in pool] + for prod in result: + yield tuple(prod) + + +def squiggle_xy(a, b, c, d, i=np.arange(0.0, 2*np.pi, 0.05)): + return np.sin(i*a)*np.cos(i*b), np.sin(i*c)*np.cos(i*d) + +f = plt.figure(figsize=(8, 8)) + +# gridspec inside gridspec +outer_grid = gridspec.GridSpec(4, 4, wspace=0.0, hspace=0.0) + +for i in xrange(16): + inner_grid = gridspec.GridSpecFromSubplotSpec(3, 3, + subplot_spec=outer_grid[i], wspace=0.0, hspace=0.0) + a, b = int(i/4)+1,i%4+1 + for j, (c, d) in enumerate(product(range(1, 4), repeat=2)): + ax = plt.Subplot(f, inner_grid[j]) + ax.plot(*squiggle_xy(a, b, c, d)) + ax.set_xticks([]) + ax.set_yticks([]) + f.add_subplot(ax) + +all_axes = f.get_axes() + +#show only the outside spines +for ax in all_axes: + for sp in ax.spines.values(): + sp.set_visible(False) + if ax.is_first_row(): + ax.spines['top'].set_visible(True) + if ax.is_last_row(): + ax.spines['bottom'].set_visible(True) + if ax.is_first_col(): + ax.spines['left'].set_visible(True) + if ax.is_last_col(): + ax.spines['right'].set_visible(True) + +plt.show() + diff --git a/doc/users/recipes.rst b/doc/users/recipes.rst index 6194af095a3d..0efb97995b90 100644 --- a/doc/users/recipes.rst +++ b/doc/users/recipes.rst @@ -101,7 +101,7 @@ objects in a numpy record array:: array([2004-08-19, 2004-08-20, 2004-08-23, ..., 2008-10-10, 2008-10-13, 2008-10-14], dtype=object) -The dtype of the numpy record array for the field 'date' is '\|O4' +The dtype of the numpy record array for the field ``date`` is ``|O4`` which means it is a 4-byte python object pointer; in this case the objects are datetime.date instances, which we can see when we print some samples in the ipython terminal window. @@ -130,7 +130,7 @@ the x locations are formatted the same way the tick labels are, eg "Dec 2004". What we'd like is for the location in the toolbar to have a higher degree of precision, eg giving us the exact date out mouse is hovering over. To fix the first problem, we can use -method:`matplotlib.figure.Figure.autofmt_xdate` and to fix the second +:func:`matplotlib.figure.Figure.autofmt_xdate` and to fix the second problem we can use the ``ax.fmt_xdata`` attribute which can be set to any function that takes a scalar and returns a string. matplotlib has a number of date formatters built in, so we'll use one of those. @@ -285,7 +285,7 @@ sigma boundary, and shade that region blue. ax.plot(t, X, lw=2, label='walker position', color='blue') ax.plot(t, mu*t, lw=1, label='population mean', color='black', ls='--') ax.fill_between(t, lower_bound, upper_bound, facecolor='yellow', alpha=0.5, - label='1 sigma range') + label='1 sigma range') ax.legend(loc='upper left') # here we use the where argument to only fill the region where the @@ -363,5 +363,5 @@ argument takes a dictionary with keys that are Patch properties. # place a text box in upper left in axes coords ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=14, - verticalalignment='top', bbox=props) + verticalalignment='top', bbox=props) diff --git a/doc/users/screenshots.rst b/doc/users/screenshots.rst index e3ecdcaac00a..f3853d88e930 100644 --- a/doc/users/screenshots.rst +++ b/doc/users/screenshots.rst @@ -188,13 +188,9 @@ techniques, not market analysis! Basemap demo ============ -Jeff Whitaker provided this example showing how to efficiently plot a -collection of lines over a colormap image using the -:ref:`toolkit_basemap` . Many map projections are handled via the -proj4 library: cylindrical equidistant, mercator, lambert conformal -conic, lambert azimuthal equal area, albers equal area conic and -stereographic. See the `tutorial -`_ entry on the wiki. +Jeff Whitaker's :ref:`toolkit_basemap` add-on toolkit makes it possible to plot data on many +different map projections. This example shows how to plot contours, markers and text +on an orthographic projection, with NASA's "blue marble" satellite image as a background. .. plot:: pyplots/plotmap.py diff --git a/examples/README.txt b/examples/README.txt index 649e0f1ef6ce..40a8e71aad9c 100644 --- a/examples/README.txt +++ b/examples/README.txt @@ -21,7 +21,7 @@ directories found here: doc/artist_api_tut.txt in the matplotlib src directory ((PDF at http://matplotlib.sf.net/pycon) - * data - some data files we use in the examples + * axes_grid - Examples related to the AxesGrid toolkit * event_handling - how to interact with your figure, mouse presses, key presses, object picking, etc. See the event handling tutorial @@ -31,12 +31,17 @@ directories found here: * misc - some miscellaneous examples. some demos for loading and working with record arrays + * mplot3d - 3D examples + * pylab_examples - the interface to matplotlib similar to matlab * tests - tests used by matplotlib developers to check functionality + (These tests are still sometimes useful, but mostly developers should + use the nosetests which perform automatic image comparison.) * units - working with unit data an custom types in matplotlib * user_interfaces - using matplotlib in a GUI application, eg TkInter, WXPython, pygtk, pyqt or FLTK widgets + * widgets - Examples using interactive widgets diff --git a/examples/animation/animate_decay.py b/examples/animation/animate_decay.py new file mode 100644 index 000000000000..90267c605032 --- /dev/null +++ b/examples/animation/animate_decay.py @@ -0,0 +1,37 @@ +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.animation as animation + +def data_gen(): + t = data_gen.t + cnt = 0 + while cnt < 1000: + cnt+=1 + t += 0.05 + yield t, np.sin(2*np.pi*t) * np.exp(-t/10.) +data_gen.t = 0 + +fig = plt.figure() +ax = fig.add_subplot(111) +line, = ax.plot([], [], lw=2) +ax.set_ylim(-1.1, 1.1) +ax.set_xlim(0, 5) +ax.grid() +xdata, ydata = [], [] +def run(data): + # update the data + t,y = data + xdata.append(t) + ydata.append(y) + xmin, xmax = ax.get_xlim() + + if t >= xmax: + ax.set_xlim(xmin, 2*xmax) + ax.figure.canvas.draw() + line.set_data(xdata, ydata) + + return line, + +ani = animation.FuncAnimation(fig, run, data_gen, blit=True, interval=10, + repeat=False) +plt.show() diff --git a/examples/animation/basic_example.py b/examples/animation/basic_example.py new file mode 100644 index 000000000000..72c95eb95682 --- /dev/null +++ b/examples/animation/basic_example.py @@ -0,0 +1,34 @@ +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.animation as animation + +def update_line(num, data, line): + line.set_data(data[...,:num]) + return line, + +fig1 = plt.figure() + +data = np.random.rand(2, 25) +l, = plt.plot([], [], 'r-') +plt.xlim(0, 1) +plt.ylim(0, 1) +plt.xlabel('x') +plt.title('test') +line_ani = animation.FuncAnimation(fig1, update_line, 25, fargs=(data, l), + interval=50, blit=True) +#line_ani.save('lines.mp4') + +fig2 = plt.figure() + +x = np.arange(-9, 10) +y = np.arange(-9, 10).reshape(-1, 1) +base = np.hypot(x, y) +ims = [] +for add in np.arange(15): + ims.append((plt.pcolor(x, y, base + add, norm=plt.Normalize(0, 30)),)) + +im_ani = animation.ArtistAnimation(fig2, ims, interval=50, repeat_delay=3000, + blit=True) +#im_ani.save('im.mp4') + +plt.show() diff --git a/examples/animation/dynamic_image.py b/examples/animation/dynamic_image.py new file mode 100644 index 000000000000..e25e66b0eb33 --- /dev/null +++ b/examples/animation/dynamic_image.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +""" +An animated image +""" +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.animation as animation + +fig = plt.figure() + +def f(x, y): + return np.sin(x) + np.cos(y) + +x = np.linspace(0, 2 * np.pi, 120) +y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1) + +im = plt.imshow(f(x, y), cmap=plt.get_cmap('jet')) + +def updatefig(*args): + global x,y + x += np.pi / 15. + y += np.pi / 20. + im.set_array(f(x,y)) + return im, + +ani = animation.FuncAnimation(fig, updatefig, interval=50, blit=True) +plt.show() diff --git a/examples/animation/dynamic_image2.py b/examples/animation/dynamic_image2.py new file mode 100644 index 000000000000..29fb4295dc82 --- /dev/null +++ b/examples/animation/dynamic_image2.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +""" +An animated image +""" +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.animation as animation + +fig = plt.figure() + +def f(x, y): + return np.sin(x) + np.cos(y) + +x = np.linspace(0, 2 * np.pi, 120) +y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1) +# ims is a list of lists, each row is a list of artists to draw in the +# current frame; here we are just animating one artist, the image, in +# each frame +ims = [] +for i in range(60): + x += np.pi / 15. + y += np.pi / 20. + im = plt.imshow(f(x, y)) + ims.append([im]) + +ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True, + repeat_delay=1000) + +ani.save('dynamic_images.mp4') + + +plt.show() diff --git a/examples/animation/histogram.py b/examples/animation/histogram.py new file mode 100644 index 000000000000..b1aa9249dff7 --- /dev/null +++ b/examples/animation/histogram.py @@ -0,0 +1,62 @@ +""" +This example shows how to use a path patch to draw a bunch of +rectangles for an animated histogram +""" +import numpy as np + +import matplotlib.pyplot as plt +import matplotlib.patches as patches +import matplotlib.path as path +import matplotlib.animation as animation + +fig = plt.figure() +ax = fig.add_subplot(111) + +# histogram our data with numpy +data = np.random.randn(1000) +n, bins = np.histogram(data, 100) + +# get the corners of the rectangles for the histogram +left = np.array(bins[:-1]) +right = np.array(bins[1:]) +bottom = np.zeros(len(left)) +top = bottom + n +nrects = len(left) + +# here comes the tricky part -- we have to set up the vertex and path +# codes arrays using moveto, lineto and closepoly + +# for each rect: 1 for the MOVETO, 3 for the LINETO, 1 for the +# CLOSEPOLY; the vert for the closepoly is ignored but we still need +# it to keep the codes aligned with the vertices +nverts = nrects*(1+3+1) +verts = np.zeros((nverts, 2)) +codes = np.ones(nverts, int) * path.Path.LINETO +codes[0::5] = path.Path.MOVETO +codes[4::5] = path.Path.CLOSEPOLY +verts[0::5,0] = left +verts[0::5,1] = bottom +verts[1::5,0] = left +verts[1::5,1] = top +verts[2::5,0] = right +verts[2::5,1] = top +verts[3::5,0] = right +verts[3::5,1] = bottom + +barpath = path.Path(verts, codes) +patch = patches.PathPatch(barpath, facecolor='green', edgecolor='yellow', alpha=0.5) +ax.add_patch(patch) + +ax.set_xlim(left[0], right[-1]) +ax.set_ylim(bottom.min(), top.max()) + +def animate(i): + # simulate new data coming in + data = np.random.randn(1000) + n, bins = np.histogram(data, 100) + top = bottom + n + verts[1::5,1] = top + verts[2::5,1] = top + +ani = animation.FuncAnimation(fig, animate, 100, repeat=False) +plt.show() diff --git a/examples/animation/README.txt b/examples/animation/old_animation/README.txt similarity index 100% rename from examples/animation/README.txt rename to examples/animation/old_animation/README.txt diff --git a/examples/animation/animate_decay_tk_blit.py b/examples/animation/old_animation/animate_decay_tk_blit.py similarity index 100% rename from examples/animation/animate_decay_tk_blit.py rename to examples/animation/old_animation/animate_decay_tk_blit.py diff --git a/examples/animation/animation_blit_fltk.py b/examples/animation/old_animation/animation_blit_fltk.py similarity index 100% rename from examples/animation/animation_blit_fltk.py rename to examples/animation/old_animation/animation_blit_fltk.py diff --git a/examples/animation/animation_blit_gtk.py b/examples/animation/old_animation/animation_blit_gtk.py similarity index 74% rename from examples/animation/animation_blit_gtk.py rename to examples/animation/old_animation/animation_blit_gtk.py index 30b193ca182a..ea9bca3aa916 100644 --- a/examples/animation/animation_blit_gtk.py +++ b/examples/animation/old_animation/animation_blit_gtk.py @@ -29,28 +29,15 @@ # for profiling tstart = time.time() -def on_draw(event): - background = canvas.copy_from_bbox(ax.bbox) - if on_draw.background is None: - gobject.idle_add(update_line) - - on_draw.background = background - -on_draw.background = None - -fig.canvas.mpl_connect('draw_event', on_draw) - def update_line(*args): - if on_draw.background is None: - return True - - print 'frame', update_line.cnt + print 'you are here', update_line.cnt + if update_line.background is None: + update_line.background = canvas.copy_from_bbox(ax.bbox) # restore the clean slate background - canvas.restore_region(on_draw.background) + canvas.restore_region(update_line.background) # update the data line.set_ydata(np.sin(x+update_line.cnt/10.0)) - # just draw the animated artist ax.draw_artist(line) @@ -67,6 +54,14 @@ def update_line(*args): return True update_line.cnt = 0 +update_line.background = None + + +def start_anim(event): + gobject.idle_add(update_line) + canvas.mpl_disconnect(start_anim.cid) + +start_anim.cid = canvas.mpl_connect('draw_event', start_anim) diff --git a/examples/animation/animation_blit_gtk2.py b/examples/animation/old_animation/animation_blit_gtk2.py similarity index 97% rename from examples/animation/animation_blit_gtk2.py rename to examples/animation/old_animation/animation_blit_gtk2.py index 8d6393c2f2b2..24d389a3821f 100644 --- a/examples/animation/animation_blit_gtk2.py +++ b/examples/animation/old_animation/animation_blit_gtk2.py @@ -20,9 +20,9 @@ class UpdateLine(object): def get_bg_bbox(self): - + return self.ax.bbox.padded(-3) - + def __init__(self, canvas, ax): self.cnt = 0 self.canvas = canvas @@ -31,7 +31,7 @@ def __init__(self, canvas, ax): self.prev_time = time.time() self.start_time = self.prev_time self.prev_pixel_offset = 0. - + self.x0 = 0 self.phases = np.random.random_sample((20,)) * np.pi * 2 @@ -70,7 +70,7 @@ def restore_background_shifted(self, dx_pixel): # restore the clean slate background self.canvas.restore_region(self.background1) - # restore subregion (x1+dx, y1, x2, y2) of the second bg + # restore subregion (x1+dx, y1, x2, y2) of the second bg # in a offset position (x1-dx, y1) x1, y1, x2, y2 = self.background2.get_extents() self.canvas.restore_region(self.background2, @@ -82,18 +82,18 @@ def restore_background_shifted(self, dx_pixel): def on_draw(self, *args): self.save_bg() return False - + def update_line(self, *args): if self.background1 is None: return True - + cur_time = time.time() pixel_offset = int((cur_time - self.start_time)*100.) dx_pixel = pixel_offset - self.prev_pixel_offset self.prev_pixel_offset = pixel_offset dx_data = self.get_dx_data(dx_pixel) #cur_time - self.prev_time) - + x0 = self.x0 self.x0 += dx_data self.prev_time = cur_time @@ -109,7 +109,7 @@ def update_line(self, *args): self.line.set_color(self.color_cycle.next()) - # now plot line segment within [x0, x0+dx_data], + # now plot line segment within [x0, x0+dx_data], # Note that we're only plotting a line between [x0, x0+dx_data]. xx = np.array([x0, self.x0]) self.line.set_xdata(xx) diff --git a/examples/animation/animation_blit_qt.py b/examples/animation/old_animation/animation_blit_qt.py similarity index 100% rename from examples/animation/animation_blit_qt.py rename to examples/animation/old_animation/animation_blit_qt.py diff --git a/examples/animation/animation_blit_qt4.py b/examples/animation/old_animation/animation_blit_qt4.py similarity index 100% rename from examples/animation/animation_blit_qt4.py rename to examples/animation/old_animation/animation_blit_qt4.py diff --git a/examples/animation/animation_blit_tk.py b/examples/animation/old_animation/animation_blit_tk.py similarity index 100% rename from examples/animation/animation_blit_tk.py rename to examples/animation/old_animation/animation_blit_tk.py diff --git a/examples/animation/animation_blit_wx.py b/examples/animation/old_animation/animation_blit_wx.py similarity index 100% rename from examples/animation/animation_blit_wx.py rename to examples/animation/old_animation/animation_blit_wx.py diff --git a/examples/animation/draggable_legend.py b/examples/animation/old_animation/draggable_legend.py similarity index 100% rename from examples/animation/draggable_legend.py rename to examples/animation/old_animation/draggable_legend.py diff --git a/examples/animation/dynamic_collection.py b/examples/animation/old_animation/dynamic_collection.py similarity index 100% rename from examples/animation/dynamic_collection.py rename to examples/animation/old_animation/dynamic_collection.py diff --git a/examples/animation/dynamic_image_gtkagg.py b/examples/animation/old_animation/dynamic_image_gtkagg.py similarity index 100% rename from examples/animation/dynamic_image_gtkagg.py rename to examples/animation/old_animation/dynamic_image_gtkagg.py diff --git a/examples/animation/dynamic_image_wxagg2.py b/examples/animation/old_animation/dynamic_image_wxagg2.py similarity index 100% rename from examples/animation/dynamic_image_wxagg2.py rename to examples/animation/old_animation/dynamic_image_wxagg2.py diff --git a/examples/animation/gtk_timeout.py b/examples/animation/old_animation/gtk_timeout.py similarity index 100% rename from examples/animation/gtk_timeout.py rename to examples/animation/old_animation/gtk_timeout.py diff --git a/examples/animation/histogram_tkagg.py b/examples/animation/old_animation/histogram_tkagg.py similarity index 100% rename from examples/animation/histogram_tkagg.py rename to examples/animation/old_animation/histogram_tkagg.py diff --git a/examples/animation/movie_demo.py b/examples/animation/old_animation/movie_demo.py similarity index 100% rename from examples/animation/movie_demo.py rename to examples/animation/old_animation/movie_demo.py diff --git a/examples/animation/simple_anim_gtk.py b/examples/animation/old_animation/simple_anim_gtk.py similarity index 100% rename from examples/animation/simple_anim_gtk.py rename to examples/animation/old_animation/simple_anim_gtk.py diff --git a/examples/animation/simple_anim_tkagg.py b/examples/animation/old_animation/simple_anim_tkagg.py similarity index 100% rename from examples/animation/simple_anim_tkagg.py rename to examples/animation/old_animation/simple_anim_tkagg.py diff --git a/examples/animation/simple_idle_wx.py b/examples/animation/old_animation/simple_idle_wx.py similarity index 100% rename from examples/animation/simple_idle_wx.py rename to examples/animation/old_animation/simple_idle_wx.py diff --git a/examples/animation/simple_timer_wx.py b/examples/animation/old_animation/simple_timer_wx.py similarity index 100% rename from examples/animation/simple_timer_wx.py rename to examples/animation/old_animation/simple_timer_wx.py diff --git a/examples/animation/old_animation/strip_chart_demo.py b/examples/animation/old_animation/strip_chart_demo.py new file mode 100644 index 000000000000..ac7587a261d3 --- /dev/null +++ b/examples/animation/old_animation/strip_chart_demo.py @@ -0,0 +1,72 @@ +""" +Emulate an oscilloscope. Requires the animation API introduced in +matplotlib 0.84. See +http://www.scipy.org/wikis/topical_software/Animations for an +explanation. + +This example uses gtk but does not depend on it intimately. It just +uses the idle handler to trigger events. You can plug this into a +different GUI that supports animation (GTKAgg, TkAgg, WXAgg) and use +your toolkits idle/timer functions. +""" +import gobject +import matplotlib +matplotlib.use('GTKAgg') +import numpy as np +from matplotlib.lines import Line2D + + +class Scope: + def __init__(self, ax, maxt=10, dt=0.01): + self.ax = ax + self.canvas = ax.figure.canvas + self.dt = dt + self.maxt = maxt + self.tdata = [0] + self.ydata = [0] + self.line = Line2D(self.tdata, self.ydata, animated=True) + self.ax.add_line(self.line) + self.background = None + self.canvas.mpl_connect('draw_event', self.update_background) + self.ax.set_ylim(-.1, 1.1) + self.ax.set_xlim(0, self.maxt) + + def update_background(self, event): + self.background = self.canvas.copy_from_bbox(self.ax.bbox) + + def emitter(self, p=0.01): + 'return a random value with probability p, else 0' + v = np.random.rand(1) + if v>p: return 0. + else: return np.random.rand(1) + + def update(self, *args): + if self.background is None: return True + y = self.emitter() + lastt = self.tdata[-1] + if lastt>self.tdata[0]+self.maxt: # reset the arrays + self.tdata = [self.tdata[-1]] + self.ydata = [self.ydata[-1]] + self.ax.set_xlim(self.tdata[0], self.tdata[0]+self.maxt) + self.ax.figure.canvas.draw() + + self.canvas.restore_region(self.background) + + t = self.tdata[-1] + self.dt + self.tdata.append(t) + self.ydata.append(y) + self.line.set_data(self.tdata, self.ydata) + self.ax.draw_artist(self.line) + + self.canvas.blit(self.ax.bbox) + return True + + +from pylab import figure, show + +fig = figure() +ax = fig.add_subplot(111) +scope = Scope(ax) +gobject.idle_add(scope.update) + +show() diff --git a/examples/animation/random_data.py b/examples/animation/random_data.py new file mode 100644 index 000000000000..1a31e963a246 --- /dev/null +++ b/examples/animation/random_data.py @@ -0,0 +1,18 @@ +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.animation as animation + +fig = plt.figure() +ax = fig.add_subplot(111) +line, = ax.plot(np.random.rand(10)) +ax.set_ylim(0, 1) + +def update(data): + line.set_ydata(data) + return line, + +def data_gen(): + while True: yield np.random.rand(10) + +ani = animation.FuncAnimation(fig, update, data_gen, interval=100) +plt.show() diff --git a/examples/animation/simple_3danim.py b/examples/animation/simple_3danim.py new file mode 100644 index 000000000000..c1514b33b64a --- /dev/null +++ b/examples/animation/simple_3danim.py @@ -0,0 +1,62 @@ +""" +A simple example of an animated plot... In 3D! +""" +import numpy as np +import matplotlib.pyplot as plt +import mpl_toolkits.mplot3d.axes3d as p3 +import matplotlib.animation as animation + +def Gen_RandLine(length, dims=2) : + """ + Create a line using a random walk algorithm + + length is the number of points for the line. + dims is the number of dimensions the line has. + """ + lineData = np.empty((dims, length)) + lineData[:, 0] = np.random.rand(dims) + for index in xrange(1, length) : + # scaling the random numbers by 0.1 so + # movement is small compared to position. + # subtraction by 0.5 is to change the range to [-0.5, 0.5] + # to allow a line to move backwards. + step = ((np.random.rand(dims) - 0.5) * 0.1) + lineData[:, index] = lineData[:, index-1] + step + + return lineData + +def update_lines(num, dataLines, lines) : + for line, data in zip(lines, dataLines) : + # NOTE: there is no .set_data() for 3 dim data... + line.set_data(data[0:2, :num]) + line.set_3d_properties(data[2,:num]) + return lines + +# Attaching 3D axis to the figure +fig = plt.figure() +ax = p3.Axes3D(fig) + +# Fifty lines of random 3-D lines +data = [Gen_RandLine(25, 3) for index in xrange(50)] + +# Creating fifty line objects. +# NOTE: Can't pass empty arrays into 3d version of plot() +lines = [ax.plot(dat[0, 0:1], dat[1, 0:1], dat[2, 0:1])[0] for dat in data] + +# Setting the axes properties +ax.set_xlim3d([0.0, 1.0]) +ax.set_xlabel('X') + +ax.set_ylim3d([0.0, 1.0]) +ax.set_ylabel('Y') + +ax.set_zlim3d([0.0, 1.0]) +ax.set_zlabel('Z') + +ax.set_title('3D Test') + +# Creating the Animation object +line_ani = animation.FuncAnimation(fig, update_lines, 25, fargs=(data, lines), + interval=50, blit=False) + +plt.show() diff --git a/examples/animation/simple_anim.py b/examples/animation/simple_anim.py new file mode 100644 index 000000000000..78c9cdfc3ae2 --- /dev/null +++ b/examples/animation/simple_anim.py @@ -0,0 +1,25 @@ +""" +A simple example of an animated plot +""" +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.animation as animation + +fig = plt.figure() +ax = fig.add_subplot(111) + +x = np.arange(0, 2*np.pi, 0.01) # x-array +line, = ax.plot(x, np.sin(x)) + +def animate(i): + line.set_ydata(np.sin(x+i/10.0)) # update the data + return line, + +#Init only required for blitting to give a clean slate. +def init(): + line.set_ydata(np.ma.array(x, mask=True)) + return line, + +ani = animation.FuncAnimation(fig, animate, np.arange(1, 200), init_func=init, + interval=25, blit=True) +plt.show() diff --git a/examples/animation/strip_chart_demo.py b/examples/animation/strip_chart_demo.py index ac7587a261d3..119fac9f6719 100644 --- a/examples/animation/strip_chart_demo.py +++ b/examples/animation/strip_chart_demo.py @@ -1,72 +1,56 @@ """ Emulate an oscilloscope. Requires the animation API introduced in -matplotlib 0.84. See -http://www.scipy.org/wikis/topical_software/Animations for an -explanation. - -This example uses gtk but does not depend on it intimately. It just -uses the idle handler to trigger events. You can plug this into a -different GUI that supports animation (GTKAgg, TkAgg, WXAgg) and use -your toolkits idle/timer functions. +matplotlib 1.0 SVN. """ -import gobject import matplotlib -matplotlib.use('GTKAgg') import numpy as np from matplotlib.lines import Line2D - +import matplotlib.pyplot as plt +import matplotlib.animation as animation class Scope: - def __init__(self, ax, maxt=10, dt=0.01): + def __init__(self, ax, maxt=2, dt=0.02): self.ax = ax - self.canvas = ax.figure.canvas self.dt = dt self.maxt = maxt self.tdata = [0] self.ydata = [0] - self.line = Line2D(self.tdata, self.ydata, animated=True) + self.line = Line2D(self.tdata, self.ydata) self.ax.add_line(self.line) - self.background = None - self.canvas.mpl_connect('draw_event', self.update_background) self.ax.set_ylim(-.1, 1.1) self.ax.set_xlim(0, self.maxt) - def update_background(self, event): - self.background = self.canvas.copy_from_bbox(self.ax.bbox) - - def emitter(self, p=0.01): - 'return a random value with probability p, else 0' - v = np.random.rand(1) - if v>p: return 0. - else: return np.random.rand(1) - - def update(self, *args): - if self.background is None: return True - y = self.emitter() + def update(self, y): lastt = self.tdata[-1] - if lastt>self.tdata[0]+self.maxt: # reset the arrays + if lastt > self.tdata[0] + self.maxt: # reset the arrays self.tdata = [self.tdata[-1]] self.ydata = [self.ydata[-1]] - self.ax.set_xlim(self.tdata[0], self.tdata[0]+self.maxt) + self.ax.set_xlim(self.tdata[0], self.tdata[0] + self.maxt) self.ax.figure.canvas.draw() - self.canvas.restore_region(self.background) - t = self.tdata[-1] + self.dt self.tdata.append(t) self.ydata.append(y) self.line.set_data(self.tdata, self.ydata) - self.ax.draw_artist(self.line) - - self.canvas.blit(self.ax.bbox) - return True + return self.line, -from pylab import figure, show +def emitter(p=0.03): + 'return a random value with probability p, else 0' + while True: + v = np.random.rand(1) + if v > p: + yield 0. + else: + yield np.random.rand(1) -fig = figure() +fig = plt.figure() ax = fig.add_subplot(111) scope = Scope(ax) -gobject.idle_add(scope.update) -show() +# pass a generator in "emitter" to produce data for the update func +ani = animation.FuncAnimation(fig, scope.update, emitter, interval=10, + blit=True) + + +plt.show() diff --git a/examples/animation/subplots.py b/examples/animation/subplots.py new file mode 100644 index 000000000000..462549e658f8 --- /dev/null +++ b/examples/animation/subplots.py @@ -0,0 +1,93 @@ +import numpy as np +import matplotlib.pyplot as plt +from matplotlib.lines import Line2D +import matplotlib.animation as animation + +# This example uses subclassing, but there is no reason that the proper function +# couldn't be set up and then use FuncAnimation. The code is long, but not +# really complex. The length is due solely to the fact that there are a total +# of 9 lines that need to be changed for the animation as well as 3 subplots +# that need initial set up. +class SubplotAnimation(animation.TimedAnimation): + def __init__(self): + fig = plt.figure() + ax1 = fig.add_subplot(1, 2, 1) + ax2 = fig.add_subplot(2, 2, 2) + ax3 = fig.add_subplot(2, 2, 4) + + self.t = np.linspace(0, 80, 400) + self.x = np.cos(2 * np.pi * self.t / 10.) + self.y = np.sin(2 * np.pi * self.t / 10.) + self.z = 10 * self.t + + ax1.set_xlabel('x') + ax1.set_ylabel('y') + self.line1 = Line2D([], [], color='black') + self.line1a = Line2D([], [], color='red', linewidth=2) + self.line1e = Line2D([], [], color='red', marker='o', markeredgecolor='r') + ax1.add_line(self.line1) + ax1.add_line(self.line1a) + ax1.add_line(self.line1e) + ax1.set_xlim(-1, 1) + ax1.set_ylim(-2, 2) + ax1.set_aspect('equal', 'datalim') + + ax2.set_xlabel('y') + ax2.set_ylabel('z') + self.line2 = Line2D([], [], color='black') + self.line2a = Line2D([], [], color='red', linewidth=2) + self.line2e = Line2D([], [], color='red', marker='o', markeredgecolor='r') + ax2.add_line(self.line2) + ax2.add_line(self.line2a) + ax2.add_line(self.line2e) + ax2.set_xlim(-1, 1) + ax2.set_ylim(0, 800) + + ax3.set_xlabel('x') + ax3.set_ylabel('z') + self.line3 = Line2D([], [], color='black') + self.line3a = Line2D([], [], color='red', linewidth=2) + self.line3e = Line2D([], [], color='red', marker='o', markeredgecolor='r') + ax3.add_line(self.line3) + ax3.add_line(self.line3a) + ax3.add_line(self.line3e) + ax3.set_xlim(-1, 1) + ax3.set_ylim(0, 800) + + animation.TimedAnimation.__init__(self, fig, interval=50, blit=True) + + def _draw_frame(self, framedata): + i = framedata + head = i - 1 + head_len = 10 + head_slice = (self.t > self.t[i] - 1.0) & (self.t < self.t[i]) + + self.line1.set_data(self.x[:i], self.y[:i]) + self.line1a.set_data(self.x[head_slice], self.y[head_slice]) + self.line1e.set_data(self.x[head], self.y[head]) + + self.line2.set_data(self.y[:i], self.z[:i]) + self.line2a.set_data(self.y[head_slice], self.z[head_slice]) + self.line2e.set_data(self.y[head], self.z[head]) + + self.line3.set_data(self.x[:i], self.z[:i]) + self.line3a.set_data(self.x[head_slice], self.z[head_slice]) + self.line3e.set_data(self.x[head], self.z[head]) + + self._drawn_artists = [self.line1, self.line1a, self.line1e, + self.line2, self.line2a, self.line2e, + self.line3, self.line3a, self.line3e] + + def new_frame_seq(self): + return iter(range(self.t.size)) + + def _init_draw(self): + lines = [self.line1, self.line1a, self.line1e, + self.line2, self.line2a, self.line2e, + self.line3, self.line3a, self.line3e] + for l in lines: + l.set_data([], []) + +ani = SubplotAnimation() +#ani.save('test_sub.mp4') +plt.show() diff --git a/examples/api/quad_bezier.py b/examples/api/quad_bezier.py index f1ab74eea2a0..5314dfb936b2 100644 --- a/examples/api/quad_bezier.py +++ b/examples/api/quad_bezier.py @@ -16,4 +16,5 @@ ax.plot([0.75], [0.25], "ro") ax.set_title('The red point should be on the path') -plt.show() \ No newline at end of file +plt.show() + diff --git a/examples/axes_grid/demo_axes_divider.py b/examples/axes_grid/demo_axes_divider.py index c40d8dac3e00..5a0652cf1052 100644 --- a/examples/axes_grid/demo_axes_divider.py +++ b/examples/axes_grid/demo_axes_divider.py @@ -27,7 +27,7 @@ def demo_locatable_axes_hard(fig1): # axes for image ax = LocatableAxes(fig1, divider.get_position()) - # axes for coloarbar + # axes for colorbar ax_cb = LocatableAxes(fig1, divider.get_position()) h = [Size.AxesX(ax), # main axes @@ -117,7 +117,7 @@ def demo(): ## PLOT 4 - # two images side by sied with fixed padding. + # two images side by side with fixed padding. ax = fig1.add_subplot(2, 2, 4) demo_images_side_by_sied(ax) diff --git a/examples/axes_grid/demo_axes_grid.py b/examples/axes_grid/demo_axes_grid.py index f6232d5ef3c8..d28f50efbd40 100644 --- a/examples/axes_grid/demo_axes_grid.py +++ b/examples/axes_grid/demo_axes_grid.py @@ -12,7 +12,7 @@ def get_demo_image(): def demo_simple_grid(fig): """ A grid of 2x2 images with 0.05 inch pad between images and only - the lower-left axes is labeld. + the lower-left axes is labeled. """ grid = AxesGrid(fig, 131, # similar to subplot(131) nrows_ncols = (2, 2), @@ -31,7 +31,7 @@ def demo_simple_grid(fig): def demo_grid_with_single_cbar(fig): """ - A grid of 2x2 images with a single colobar + A grid of 2x2 images with a single colorbar """ grid = AxesGrid(fig, 132, # similar to subplot(132) nrows_ncols = (2, 2), @@ -58,7 +58,7 @@ def demo_grid_with_single_cbar(fig): def demo_grid_with_each_cbar(fig): """ - A grid of 2x2 images. Each image has its own colobar. + A grid of 2x2 images. Each image has its own colorbar. """ grid = AxesGrid(F, 133, # similar to subplot(122) diff --git a/examples/axes_grid/demo_axes_hbox_divider.py b/examples/axes_grid/demo_axes_hbox_divider.py new file mode 100644 index 000000000000..fe72bdc14195 --- /dev/null +++ b/examples/axes_grid/demo_axes_hbox_divider.py @@ -0,0 +1,52 @@ +import numpy as np +import matplotlib.pyplot as plt +from mpl_toolkits.axes_grid1.axes_divider import HBoxDivider +import mpl_toolkits.axes_grid1.axes_size as Size + +def make_heights_equal(fig, rect, ax1, ax2, pad): + # pad in inches + + h1, v1 = Size.AxesX(ax1), Size.AxesY(ax1) + h2, v2 = Size.AxesX(ax2), Size.AxesY(ax2) + + pad_v = Size.Scaled(1) + pad_h = Size.Fixed(pad) + + my_divider = HBoxDivider(fig, rect, + horizontal=[h1, pad_h, h2], + vertical=[v1, pad_v, v2]) + + + ax1.set_axes_locator(my_divider.new_locator(0)) + ax2.set_axes_locator(my_divider.new_locator(2)) + + +if __name__ == "__main__": + + fig1 = plt.figure() + + arr1 = np.arange(20).reshape((4,5)) + arr2 = np.arange(20).reshape((5,4)) + + ax1 = plt.subplot(121) + ax2 = plt.subplot(122) + + ax1.imshow(arr1, interpolation="nearest") + ax2.imshow(arr2, interpolation="nearest") + + rect = 111 # subplot param for combined axes + make_heights_equal(fig1, rect, ax1, ax2, pad=0.5) # pad in inches + + for ax in [ax1, ax2]: + ax.locator_params(nbins=4) + + # annotate + ax3 = plt.axes([0.5, 0.5, 0.001, 0.001], frameon=False) + ax3.xaxis.set_visible(False) + ax3.yaxis.set_visible(False) + ax3.annotate("Location of two axes are adjusted\n so that they have equal heights\n while maintaining their aspect ratios", (0.5, 0.5), + xycoords="axes fraction", va="center", ha="center", + bbox=dict(boxstyle="round, pad=1", fc="w")) + + plt.show() + diff --git a/examples/axes_grid/demo_floating_axes.py b/examples/axes_grid/demo_floating_axes.py index 9eda9e4a2ab2..e6712ac01363 100644 --- a/examples/axes_grid/demo_floating_axes.py +++ b/examples/axes_grid/demo_floating_axes.py @@ -146,7 +146,7 @@ def setup_axes3(fig, rect): ax3, aux_ax3 = setup_axes3(fig, 133) - theta = (8 + np.random.rand(10)*(14-8))*15. # indegree + theta = (8 + np.random.rand(10)*(14-8))*15. # in degrees radius = np.random.rand(10)*14000. aux_ax3.scatter(theta, radius) diff --git a/examples/axes_grid/demo_floating_axis.py b/examples/axes_grid/demo_floating_axis.py index dc9ed3be94b3..bc8dea6b2729 100644 --- a/examples/axes_grid/demo_floating_axis.py +++ b/examples/axes_grid/demo_floating_axis.py @@ -1,5 +1,5 @@ """ -An experimental support for curvelinear grid. +An experimental support for curvilinear grid. """ diff --git a/examples/axes_grid/make_room_for_ylabel_using_axesgrid.py b/examples/axes_grid/make_room_for_ylabel_using_axesgrid.py new file mode 100644 index 000000000000..6a269a5c8f32 --- /dev/null +++ b/examples/axes_grid/make_room_for_ylabel_using_axesgrid.py @@ -0,0 +1,62 @@ +from mpl_toolkits.axes_grid1 import make_axes_locatable +from mpl_toolkits.axes_grid1.axes_divider import make_axes_area_auto_adjustable + + + +if __name__ == "__main__": + + import matplotlib.pyplot as plt + def ex1(): + plt.figure(1) + ax = plt.axes([0,0,1,1]) + # ax = plt.subplot(111) + + ax.set_yticks([0.5]) + ax.set_yticklabels(["very long label"]) + + make_axes_area_auto_adjustable(ax) + + + def ex2(): + + plt.figure(2) + ax1 = plt.axes([0,0,1,0.5]) + ax2 = plt.axes([0,0.5,1,0.5]) + + ax1.set_yticks([0.5]) + ax1.set_yticklabels(["very long label"]) + ax1.set_ylabel("Y label") + + ax2.set_title("Title") + + make_axes_area_auto_adjustable(ax1, pad=0.1, use_axes=[ax1, ax2]) + make_axes_area_auto_adjustable(ax2, pad=0.1, use_axes=[ax1, ax2]) + + def ex3(): + + fig = plt.figure(3) + ax1 = plt.axes([0,0,1,1]) + divider = make_axes_locatable(ax1) + + ax2 = divider.new_horizontal("100%", pad=0.3, sharey=ax1) + ax2.tick_params(labelleft="off") + fig.add_axes(ax2) + + divider.add_auto_adjustable_area(use_axes=[ax1], pad=0.1, + adjust_dirs=["left"]) + divider.add_auto_adjustable_area(use_axes=[ax2], pad=0.1, + adjust_dirs=["right"]) + divider.add_auto_adjustable_area(use_axes=[ax1, ax2], pad=0.1, + adjust_dirs=["top", "bottom"]) + + ax1.set_yticks([0.5]) + ax1.set_yticklabels(["very long label"]) + + ax2.set_title("Title") + ax2.set_xlabel("X - Label") + + ex1() + ex2() + ex3() + + plt.show() diff --git a/examples/misc/developer_commit_history.py b/examples/misc/developer_commit_history.py new file mode 100644 index 000000000000..759fea87a804 --- /dev/null +++ b/examples/misc/developer_commit_history.py @@ -0,0 +1,44 @@ +""" +report how many days it has been since each developer committed. You +must do an + +svn log > log.txt + +and place the output next to this file before running + +""" +import os, datetime + +import matplotlib.cbook as cbook + +todate = cbook.todate('%Y-%m-%d') +today = datetime.date.today() +if not os.path.exists('log.txt'): + print('You must place the "svn log" output into a file "log.txt"') + raise SystemExit + +parse = False + +lastd = dict() +for line in file('log.txt'): + if line.startswith('--------'): + parse = True + continue + + if parse: + parts = [part.strip() for part in line.split('|')] + developer = parts[1] + dateparts = parts[2].split(' ') + ymd = todate(dateparts[0]) + + + if developer not in lastd: + lastd[developer] = ymd + + parse = False + +dsu = [((today - lastdate).days, developer) for developer, lastdate in lastd.items()] + +dsu.sort() +for timedelta, developer in dsu: + print('%s : %d'%(developer, timedelta)) diff --git a/examples/mplot3d/contour3d_demo2.py b/examples/mplot3d/contour3d_demo2.py index 94bc66531c80..be9ab883dbe4 100644 --- a/examples/mplot3d/contour3d_demo2.py +++ b/examples/mplot3d/contour3d_demo2.py @@ -4,7 +4,7 @@ fig = plt.figure() ax = fig.gca(projection='3d') X, Y, Z = axes3d.get_test_data(0.05) -cset = ax.contour(X, Y, Z, 16, extend3d=True) +cset = ax.contour(X, Y, Z, extend3d=True) ax.clabel(cset, fontsize=9, inline=1) plt.show() diff --git a/examples/mplot3d/contourf3d_demo2.py b/examples/mplot3d/contourf3d_demo2.py new file mode 100644 index 000000000000..eb8d23776da3 --- /dev/null +++ b/examples/mplot3d/contourf3d_demo2.py @@ -0,0 +1,20 @@ +from mpl_toolkits.mplot3d import axes3d +import matplotlib.pyplot as plt + +fig = plt.figure() +ax = fig.gca(projection='3d') +X, Y, Z = axes3d.get_test_data(0.05) +ax.plot_surface(X, Y, Z, rstride=8, cstride=8, alpha=0.3) +cset = ax.contourf(X, Y, Z, zdir='z', offset=-100) +cset = ax.contourf(X, Y, Z, zdir='x', offset=-40) +cset = ax.contourf(X, Y, Z, zdir='y', offset=40) + +ax.set_xlabel('X') +ax.set_xlim3d(-40, 40) +ax.set_ylabel('Y') +ax.set_ylim3d(-40, 40) +ax.set_zlabel('Z') +ax.set_zlim3d(-100, 100) + +plt.show() + diff --git a/examples/mplot3d/surface3d_radial_demo.py b/examples/mplot3d/surface3d_radial_demo.py index 8e8a3c84bd3d..938866cd07a1 100644 --- a/examples/mplot3d/surface3d_radial_demo.py +++ b/examples/mplot3d/surface3d_radial_demo.py @@ -23,5 +23,4 @@ ax.set_xlabel(r'$\phi_\mathrm{real}$') ax.set_ylabel(r'$\phi_\mathrm{im}$') ax.set_zlabel(r'$V(\phi)$') -ax.set_xticks([]) plt.show() diff --git a/examples/pylab_examples/__init__.py b/examples/pylab_examples/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/examples/pylab_examples/axes_zoom_effect.py b/examples/pylab_examples/axes_zoom_effect.py index b01e2d91472a..7a4d10d0c271 100644 --- a/examples/pylab_examples/axes_zoom_effect.py +++ b/examples/pylab_examples/axes_zoom_effect.py @@ -100,21 +100,19 @@ def zoom_effect02(ax1, ax2, **kwargs): return c1, c2, bbox_patch1, bbox_patch2, p -if __name__ == "__main__": - import matplotlib.pyplot as plt +import matplotlib.pyplot as plt - plt.figure(1, figsize=(5,5)) - ax1 = plt.subplot(221) - ax2 = plt.subplot(212) - ax2.set_xlim(0, 1) - ax2.set_xlim(0, 5) - zoom_effect01(ax1, ax2, 0.2, 0.8) +plt.figure(1, figsize=(5,5)) +ax1 = plt.subplot(221) +ax2 = plt.subplot(212) +ax2.set_xlim(0, 1) +ax2.set_xlim(0, 5) +zoom_effect01(ax1, ax2, 0.2, 0.8) - ax1 = plt.subplot(222) - ax1.set_xlim(2, 3) - ax2.set_xlim(0, 5) - zoom_effect02(ax1, ax2) - - plt.show() +ax1 = plt.subplot(222) +ax1.set_xlim(2, 3) +ax2.set_xlim(0, 5) +zoom_effect02(ax1, ax2) +plt.show() diff --git a/examples/pylab_examples/broken_axis.py b/examples/pylab_examples/broken_axis.py new file mode 100644 index 000000000000..a5927d9132cd --- /dev/null +++ b/examples/pylab_examples/broken_axis.py @@ -0,0 +1,61 @@ +""" +Broken axis example, where the y-axis will have a portion cut out. +""" +import matplotlib.pylab as plt +import numpy as np + + +# 30 points between 0 0.2] originally made using np.random.rand(30)*.2 +pts = np.array([ 0.015, 0.166, 0.133, 0.159, 0.041, 0.024, 0.195, + 0.039, 0.161, 0.018, 0.143, 0.056, 0.125, 0.096, 0.094, 0.051, + 0.043, 0.021, 0.138, 0.075, 0.109, 0.195, 0.05 , 0.074, 0.079, + 0.155, 0.02 , 0.01 , 0.061, 0.008]) + +# Now let's make two outlier points which are far away from everything. +pts[[3,14]] += .8 + +# If we were to simply plot pts, we'd lose most of the interesting +# details due to the outliers. So let's 'break' or 'cut-out' the y-axis +# into two portions - use the top (ax) for the outliers, and the bottom +# (ax2) for the details of the majority of our data +f,(ax,ax2) = plt.subplots(2,1,sharex=True) + +# plot the same data on both axes +ax.plot(pts) +ax2.plot(pts) + +# zoom-in / limit the view to different portions of the data +ax.set_ylim(.78,1.) # outliers only +ax2.set_ylim(0,.22) # most of the data + +# hide the spines between ax and ax2 +ax.spines['bottom'].set_visible(False) +ax2.spines['top'].set_visible(False) +ax.xaxis.tick_top() +ax.tick_params(labeltop='off') # don't put tick labels at the top +ax2.xaxis.tick_bottom() + +# This looks pretty good, and was fairly painless, but you can get that +# cut-out diagonal lines look with just a bit more work. The important +# thing to know here is that in axes coordinates, which are always +# between 0-1, spine endpoints are at these locations (0,0), (0,1), +# (1,0), and (1,1). Thus, we just need to put the diagonals in the +# appropriate corners of each of our axes, and so long as we use the +# right transform and disable clipping. + +d = .015 # how big to make the diagonal lines in axes coordinates +# arguments to pass plot, just so we don't keep repeating them +kwargs = dict(transform=ax.transAxes, color='k', clip_on=False) +ax.plot((-d,+d),(-d,+d), **kwargs) # top-left diagonal +ax.plot((1-d,1+d),(-d,+d), **kwargs) # top-right diagonal + +kwargs.update(transform=ax2.transAxes) # switch to the bottom axes +ax2.plot((-d,+d),(1-d,1+d), **kwargs) # bottom-left diagonal +ax2.plot((1-d,1+d),(1-d,1+d), **kwargs) # bottom-right diagonal + +# What's cool about this is that now if we vary the distance between +# ax and ax2 via f.subplots_adjust(hspace=...) or plt.subplot_tool(), +# the diagonal lines will move accordingly, and stay right at the tips +# of the spines they are 'breaking' + +plt.show() diff --git a/examples/pylab_examples/contour_label_demo.py b/examples/pylab_examples/contour_label_demo.py index 8181b792ab2d..d900192f648c 100644 --- a/examples/pylab_examples/contour_label_demo.py +++ b/examples/pylab_examples/contour_label_demo.py @@ -9,6 +9,7 @@ import numpy as np import matplotlib.cm as cm import matplotlib.mlab as mlab +import matplotlib.ticker as ticker import matplotlib.pyplot as plt matplotlib.rcParams['xtick.direction'] = 'out' @@ -68,7 +69,15 @@ def __repr__(self): # Label every other level using strings plt.clabel(CS,CS.levels[::2],inline=True,fmt=fmt,fontsize=10) -################################################## -# Show the hole thing -################################################## +# Use a Formatter + +plt.figure() + +CS = plt.contour(X, Y, 100**Z, locator=plt.LogLocator()) +fmt = ticker.LogFormatterMathtext() +fmt.create_dummy_axis() +plt.clabel(CS, CS.levels, fmt=fmt) +plt.title("$100^Z$") + plt.show() + diff --git a/examples/pylab_examples/image_slices_viewer.py b/examples/pylab_examples/image_slices_viewer.py index c476c3dd6287..a9c1b78f472b 100644 --- a/examples/pylab_examples/image_slices_viewer.py +++ b/examples/pylab_examples/image_slices_viewer.py @@ -1,5 +1,5 @@ import numpy -from pylab import figure, show +from matplotlib.pyplot import figure, show @@ -7,7 +7,7 @@ class IndexTracker: def __init__(self, ax, X): self.ax = ax - ax.set_title('use scroll wheen to navigate images') + ax.set_title('use scroll wheel to navigate images') self.X = X rows,cols,self.slices = X.shape @@ -28,7 +28,7 @@ def onscroll(self, event): def update(self): self.im.set_data(self.X[:,:,self.ind]) - ax.set_ylabel('slice %s'%self.ind) + ax.set_ylabel('slice %s'%self.ind) self.im.axes.figure.canvas.draw() diff --git a/examples/pylab_examples/legend_demo4.py b/examples/pylab_examples/legend_demo4.py new file mode 100644 index 000000000000..a6a5e958506a --- /dev/null +++ b/examples/pylab_examples/legend_demo4.py @@ -0,0 +1,22 @@ +import matplotlib.pyplot as plt + +ax = plt.subplot(111) + +b1 = ax.bar([0, 1, 2], [0.2, 0.3, 0.1], width=0.4, + label="Bar 1", align="center") + +b2 = ax.bar([0.5, 1.5, 2.5], [0.3, 0.2, 0.2], color="red", width=0.4, + label="Bar 2", align="center") + +err1 = ax.errorbar([0, 1, 2], [2, 3, 1], xerr=0.4, fmt="s", + label="test 1") +err2 = ax.errorbar([0, 1, 2], [3, 2, 4], yerr=0.3, fmt="o", + label="test 2") +err3 = ax.errorbar([0, 1, 2], [1, 1, 3], xerr=0.4, yerr=0.3, fmt="^", + label="test 3") + +# legend +leg1 = plt.legend(loc=1) + +plt.show() + diff --git a/examples/pylab_examples/legend_demo_custom_handler.py b/examples/pylab_examples/legend_demo_custom_handler.py new file mode 100644 index 000000000000..4d5e9e59def3 --- /dev/null +++ b/examples/pylab_examples/legend_demo_custom_handler.py @@ -0,0 +1,30 @@ +import matplotlib.pyplot as plt +import numpy as np + +ax = plt.subplot(111, aspect=1) +x, y = np.random.randn(2, 20) +#[1.1, 2, 2.8], [1.1, 2, 1.8] +l1, = ax.plot(x,y, "k+", mew=3, ms=12) +l2, = ax.plot(x,y, "w+", mew=1, ms=10) + +import matplotlib.patches as mpatches +c = mpatches.Circle((0, 0), 1, fc="g", ec="r", lw=3) +ax.add_patch(c) + + + +from matplotlib.legend_handler import HandlerPatch + +def make_legend_ellipse(legend, orig_handle, + xdescent, ydescent, + width, height, fontsize): + p = mpatches.Ellipse(xy=(0.5*width-0.5*xdescent, 0.5*height-0.5*ydescent), + width = width+xdescent, height=(height+ydescent)) + + return p + +plt.legend([c, (l1, l2)], ["Label 1", "Label 2"], + handler_map={mpatches.Circle:HandlerPatch(patch_func=make_legend_ellipse), + }) + +plt.show() diff --git a/examples/pylab_examples/loadrec.py b/examples/pylab_examples/loadrec.py index 74fa34a5785e..7d43dbbc58f7 100644 --- a/examples/pylab_examples/loadrec.py +++ b/examples/pylab_examples/loadrec.py @@ -14,6 +14,9 @@ fig.autofmt_xdate() # if you have xlwt installed, you can output excel -import mpl_toolkits.exceltools as exceltools -exceltools.rec2excel(a, 'test.xls') +try: + import mpl_toolkits.exceltools as exceltools + exceltools.rec2excel(a, 'test.xls') +except ImportError: + pass show() diff --git a/examples/pylab_examples/multiple_yaxis_with_spines.py b/examples/pylab_examples/multiple_yaxis_with_spines.py index 66da6241ccfa..a0051246b202 100644 --- a/examples/pylab_examples/multiple_yaxis_with_spines.py +++ b/examples/pylab_examples/multiple_yaxis_with_spines.py @@ -1,66 +1,54 @@ import matplotlib.pyplot as plt def make_patch_spines_invisible(ax): - par2.set_frame_on(True) - par2.patch.set_visible(False) - for sp in par2.spines.itervalues(): + ax.set_frame_on(True) + ax.patch.set_visible(False) + for sp in ax.spines.itervalues(): sp.set_visible(False) -def make_spine_invisible(ax, direction): - if direction in ["right", "left"]: - ax.yaxis.set_ticks_position(direction) - ax.yaxis.set_label_position(direction) - elif direction in ["top", "bottom"]: - ax.xaxis.set_ticks_position(direction) - ax.xaxis.set_label_position(direction) - else: - raise ValueError("Unknown Direction : %s" % (direction,)) +fig = plt.figure() +fig.subplots_adjust(right=0.75) - ax.spines[direction].set_visible(True) +host = fig.add_subplot(111) +par1 = host.twinx() +par2 = host.twinx() +# Offset the right spine of par2. The ticks and label have already been +# placed on the right by twinx above. +par2.spines["right"].set_position(("axes", 1.2)) +# Having been created by twinx, par2 has its frame off, so the line of its +# detached spine is invisible. First, activate the frame but make the patch +# and spines invisible. +make_patch_spines_invisible(par2) +# Second, show the right spine. +par2.spines["right"].set_visible(True) -if 1: - fig = plt.figure(1) +p1, = host.plot([0, 1, 2], [0, 1, 2], "b-", label="Density") +p2, = par1.plot([0, 1, 2], [0, 3, 2], "r-", label="Temperature") +p3, = par2.plot([0, 1, 2], [50, 30, 15], "g-", label="Velocity") - host = fig.add_subplot(111) +host.set_xlim(0, 2) +host.set_ylim(0, 2) +par1.set_ylim(0, 4) +par2.set_ylim(1, 65) - host.set_xlabel("Distance") +host.set_xlabel("Distance") +host.set_ylabel("Density") +par1.set_ylabel("Temperature") +par2.set_ylabel("Velocity") - par1 = host.twinx() - par2 = host.twinx() +host.yaxis.label.set_color(p1.get_color()) +par1.yaxis.label.set_color(p2.get_color()) +par2.yaxis.label.set_color(p3.get_color()) - par2.spines["right"].set_position(("axes", 1.2)) - make_patch_spines_invisible(par2) - make_spine_invisible(par2, "right") +tkw = dict(size=4, width=1.5) +host.tick_params(axis='y', colors=p1.get_color(), **tkw) +par1.tick_params(axis='y', colors=p2.get_color(), **tkw) +par2.tick_params(axis='y', colors=p3.get_color(), **tkw) +host.tick_params(axis='x', **tkw) - plt.subplots_adjust(right=0.75) +lines = [p1, p2, p3] +host.legend(lines, [l.get_label() for l in lines]) - p1, = host.plot([0, 1, 2], [0, 1, 2], "b-", label="Density") - p2, = par1.plot([0, 1, 2], [0, 3, 2], "r-", label="Temperature") - p3, = par2.plot([0, 1, 2], [50, 30, 15], "g-", label="Velocity") - - host.set_xlim(0, 2) - host.set_ylim(0, 2) - par1.set_ylim(0, 4) - par2.set_ylim(1, 65) - - host.set_xlabel("Distance") - host.set_ylabel("Density") - par1.set_ylabel("Temperature") - par2.set_ylabel("Velocity") - - host.yaxis.label.set_color(p1.get_color()) - par1.yaxis.label.set_color(p2.get_color()) - par2.yaxis.label.set_color(p3.get_color()) - - tkw = dict(size=4, width=1.5) - host.tick_params(axis='y', colors=p1.get_color(), **tkw) - par1.tick_params(axis='y', colors=p2.get_color(), **tkw) - par2.tick_params(axis='y', colors=p3.get_color(), **tkw) - host.tick_params(axis='x', **tkw) - - lines = [p1, p2, p3] - host.legend(lines, [l.get_label() for l in lines]) - plt.show() - +plt.show() diff --git a/examples/pylab_examples/toggle_images.py b/examples/pylab_examples/toggle_images.py index 327a91391d66..14173edb953a 100644 --- a/examples/pylab_examples/toggle_images.py +++ b/examples/pylab_examples/toggle_images.py @@ -5,7 +5,7 @@ them to the same axes with hold "on". Then, toggle the visible property of them using keypress event handling -If you want two images with sifferent shapes to be plotted with the same +If you want two images with different shapes to be plotted with the same extent, they must have the same "extent" property As usual, we'll define some random images for demo. Real data is much more diff --git a/examples/tests/pngsuite/pngsuite.py b/examples/tests/pngsuite/pngsuite.py deleted file mode 100644 index ca8d162bc265..000000000000 --- a/examples/tests/pngsuite/pngsuite.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -This test loads a subset of the files in Willem van Schaik's PNG test -suite available here: - - http://libpng.org/pub/png/pngsuite.html - -The result should look like truth.png. -""" - -from matplotlib import pyplot as plt -import matplotlib.cm as cm -import glob - -files = glob.glob("basn*.png") -files.sort() - -plt.figure(figsize=(len(files), 2)) - -for i, fname in enumerate(files): - data = plt.imread(fname) - cmap = None # use default colormap - if data.ndim==2: - # keep grayscale images gray - cmap = cm.gray - plt.imshow(data, extent=[i,i+1,0,1], cmap=cmap) - -plt.gca().get_frame().set_facecolor("#ddffff") -plt.gca().set_xlim(0, len(files)) -plt.show() diff --git a/examples/tests/pngsuite/truth.png b/examples/tests/pngsuite/truth.png deleted file mode 100644 index 49610ba02daf..000000000000 Binary files a/examples/tests/pngsuite/truth.png and /dev/null differ diff --git a/examples/units/__init__.py b/examples/units/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/examples/widgets/radio_buttons.py b/examples/widgets/radio_buttons.py index d18cc5ec3ac5..cf14c155d68a 100644 --- a/examples/widgets/radio_buttons.py +++ b/examples/widgets/radio_buttons.py @@ -20,17 +20,17 @@ def hzfunc(label): radio.on_clicked(hzfunc) rax = axes([0.05, 0.4, 0.15, 0.15], axisbg=axcolor) -radio = RadioButtons(rax, ('red', 'blue', 'green')) +radio2 = RadioButtons(rax, ('red', 'blue', 'green')) def colorfunc(label): l.set_color(label) draw() -radio.on_clicked(colorfunc) +radio2.on_clicked(colorfunc) rax = axes([0.05, 0.1, 0.15, 0.15], axisbg=axcolor) -radio = RadioButtons(rax, ('-', '--', '-.', 'steps', ':')) +radio3 = RadioButtons(rax, ('-', '--', '-.', 'steps', ':')) def stylefunc(label): l.set_linestyle(label) draw() -radio.on_clicked(stylefunc) +radio3.on_clicked(stylefunc) show() diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 60c7f343e505..649e4d8462ac 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -99,7 +99,7 @@ """ from __future__ import generators -__version__ = '1.0.1' +__version__ = '1.1.0' import os, re, shutil, subprocess, sys, warnings import distutils.sysconfig @@ -454,6 +454,8 @@ def _get_configdir(): configdir = os.environ.get('MPLCONFIGDIR') if configdir is not None: + if not os.path.exists(configdir): + os.makedirs(configdir) if not _is_writable_dir(configdir): raise RuntimeError('Could not write to MPLCONFIGDIR="%s"'%configdir) return configdir diff --git a/lib/matplotlib/animation.py b/lib/matplotlib/animation.py new file mode 100644 index 000000000000..9ff014d26423 --- /dev/null +++ b/lib/matplotlib/animation.py @@ -0,0 +1,473 @@ +# TODO: +# * Loop Delay is broken on GTKAgg. This is because source_remove() is not +# working as we want. PyGTK bug? +# * Documentation -- this will need a new section of the User's Guide. +# Both for Animations and just timers. +# - Also need to update http://www.scipy.org/Cookbook/Matplotlib/Animations +# * Blit +# * Currently broken with Qt4 for widgets that don't start on screen +# * Still a few edge cases that aren't working correctly +# * Can this integrate better with existing matplotlib animation artist flag? +# - If animated removes from default draw(), perhaps we could use this to +# simplify initial draw. +# * Example +# * Frameless animation - pure procedural with no loop +# * Need example that uses something like inotify or subprocess +# * Complex syncing examples +# * Movies +# * Library to make movies? +# * RC parameter for config? +# * Can blit be enabled for movies? +# * Need to consider event sources to allow clicking through multiple figures +import itertools +from matplotlib.cbook import iterable + +class Animation(object): + ''' + This class wraps the creation of an animation using matplotlib. It is + only a base class which should be subclassed to provide needed behavior. + + *fig* is the figure object that is used to get draw, resize, and any + other needed events. + + *event_source* is a class that can run a callback when desired events + are generated, as well as be stopped and started. Examples include timers + (see :class:`TimedAnimation`) and file system notifications. + + *blit* is a boolean that controls whether blitting is used to optimize + drawing. + ''' + def __init__(self, fig, event_source=None, blit=False): + self._fig = fig + self._blit = blit + + # These are the basics of the animation. The frame sequence represents + # information for each frame of the animation and depends on how the + # drawing is handled by the subclasses. The event source fires events + # that cause the frame sequence to be iterated. + self.frame_seq = self.new_frame_seq() + self.event_source = event_source + + # Clear the initial frame + self._init_draw() + + # Instead of starting the event source now, we connect to the figure's + # draw_event, so that we only start once the figure has been drawn. + self._first_draw_id = fig.canvas.mpl_connect('draw_event', self._start) + + # Connect to the figure's close_event so that we don't continue to + # fire events and try to draw to a deleted figure. + self._close_id = self._fig.canvas.mpl_connect('close_event', self._stop) + if blit: + self._setup_blit() + + def _start(self, *args): + ''' + Starts interactive animation. Adds the draw frame command to the GUI + handler, calls show to start the event loop. + ''' + # On start, we add our callback for stepping the animation and + # actually start the event_source. We also disconnect _start + # from the draw_events + self.event_source.add_callback(self._step) + self.event_source.start() + self._fig.canvas.mpl_disconnect(self._first_draw_id) + self._first_draw_id = None # So we can check on save + + def _stop(self, *args): + # On stop we disconnect all of our events. + if self._blit: + self._fig.canvas.mpl_disconnect(self._resize_id) + self._fig.canvas.mpl_disconnect(self._close_id) + self.event_source.remove_callback(self._step) + self.event_source = None + + def save(self, filename, fps=5, codec='mpeg4', clear_temp=True, + frame_prefix='_tmp'): + ''' + Saves a movie file by drawing every frame. + + *fps* is the frames per second in the movie + + *codec* is the codec to be used,if it is supported by the output method. + + *clear_temp* specifies whether the temporary image files should be + deleted. + + *frame_prefix* gives the prefix that should be used for individual + image files. This prefix will have a frame number (i.e. 0001) appended + when saving individual frames. + ''' + # Need to disconnect the first draw callback, since we'll be doing + # draws. Otherwise, we'll end up starting the animation. + if self._first_draw_id is not None: + self._fig.canvas.mpl_disconnect(self._first_draw_id) + reconnect_first_draw = True + else: + reconnect_first_draw = False + + fnames = [] + # Create a new sequence of frames for saved data. This is different + # from new_frame_seq() to give the ability to save 'live' generated + # frame information to be saved later. + # TODO: Right now, after closing the figure, saving a movie won't + # work since GUI widgets are gone. Either need to remove extra code + # to allow for this non-existant use case or find a way to make it work. + for idx,data in enumerate(self.new_saved_frame_seq()): + #TODO: Need to see if turning off blit is really necessary + self._draw_next_frame(data, blit=False) + fname = '%s%04d.png' % (frame_prefix, idx) + fnames.append(fname) + self._fig.savefig(fname) + + self._make_movie(filename, fps, codec, frame_prefix) + + #Delete temporary files + if clear_temp: + import os + for fname in fnames: + os.remove(fname) + + # Reconnect signal for first draw if necessary + if reconnect_first_draw: + self._first_draw_id = self._fig.canvas.mpl_connect('draw_event', + self._start) + + def ffmpeg_cmd(self, fname, fps, codec, frame_prefix): + # Returns the command line parameters for subprocess to use + # ffmpeg to create a movie + return ['ffmpeg', '-y', '-r', str(fps), '-b', '1800k', '-i', + '%s%%04d.png' % frame_prefix, fname] + + def mencoder_cmd(self, fname, fps, codec, frame_prefix): + # Returns the command line parameters for subprocess to use + # mencoder to create a movie + return ['mencoder', 'mf://%s*.png' % frame_prefix, '-mf', + 'type=png:fps=%d' % fps, '-ovc', 'lavc', '-lavcopts', + 'vcodec=%s' % codec, '-oac', 'copy', '-o', fname] + + def _make_movie(self, fname, fps, codec, frame_prefix, cmd_gen=None): + # Uses subprocess to call the program for assembling frames into a + # movie file. *cmd_gen* is a callable that generates the sequence + # of command line arguments from a few configuration options. + from subprocess import Popen, PIPE + if cmd_gen is None: + cmd_gen = self.ffmpeg_cmd + proc = Popen(cmd_gen(fname, fps, codec, frame_prefix), shell=False, + stdout=PIPE, stderr=PIPE) + proc.wait() + + def _step(self, *args): + ''' + Handler for getting events. By default, gets the next frame in the + sequence and hands the data off to be drawn. + ''' + # Returns True to indicate that the event source should continue to + # call _step, until the frame sequence reaches the end of iteration, + # at which point False will be returned. + try: + framedata = self.frame_seq.next() + self._draw_next_frame(framedata, self._blit) + return True + except StopIteration: + return False + + def new_frame_seq(self): + 'Creates a new sequence of frame information.' + # Default implementation is just an iterator over self._framedata + return iter(self._framedata) + + def new_saved_frame_seq(self): + 'Creates a new sequence of saved/cached frame information.' + # Default is the same as the regular frame sequence + return self.new_frame_seq() + + def _draw_next_frame(self, framedata, blit): + # Breaks down the drawing of the next frame into steps of pre- and + # post- draw, as well as the drawing of the frame itself. + self._pre_draw(framedata, blit) + self._draw_frame(framedata) + self._post_draw(framedata, blit) + + def _init_draw(self): + # Initial draw to clear the frame. Also used by the blitting code + # when a clean base is required. + pass + + def _pre_draw(self, framedata, blit): + # Perform any cleaning or whatnot before the drawing of the frame. + # This default implementation allows blit to clear the frame. + if blit: + self._blit_clear(self._drawn_artists, self._blit_cache) + + def _draw_frame(self, framedata): + # Performs actual drawing of the frame. + raise NotImplementedError('Needs to be implemented by subclasses to' + ' actually make an animation.') + + def _post_draw(self, framedata, blit): + # After the frame is rendered, this handles the actual flushing of + # the draw, which can be a direct draw_idle() or make use of the + # blitting. + if blit and self._drawn_artists: + self._blit_draw(self._drawn_artists, self._blit_cache) + else: + self._fig.canvas.draw_idle() + + # The rest of the code in this class is to facilitate easy blitting + def _blit_draw(self, artists, bg_cache): + # Handles blitted drawing, which renders only the artists given instead + # of the entire figure. + updated_ax = [] + for a in artists: + # If we haven't cached the background for this axes object, do + # so now. This might not always be reliable, but it's an attempt + # to automate the process. + if a.axes not in bg_cache: + bg_cache[a.axes] = a.figure.canvas.copy_from_bbox(a.axes.bbox) + a.axes.draw_artist(a) + updated_ax.append(a.axes) + + # After rendering all the needed artists, blit each axes individually. + for ax in set(updated_ax): + ax.figure.canvas.blit(ax.bbox) + + def _blit_clear(self, artists, bg_cache): + # Get a list of the axes that need clearing from the artists that + # have been drawn. Grab the appropriate saved background from the + # cache and restore. + axes = set(a.axes for a in artists) + for a in axes: + a.figure.canvas.restore_region(bg_cache[a]) + + def _setup_blit(self): + # Setting up the blit requires: a cache of the background for the + # axes + self._blit_cache = dict() + self._drawn_artists = [] + self._resize_id = self._fig.canvas.mpl_connect('resize_event', + self._handle_resize) + self._post_draw(None, self._blit) + + def _handle_resize(self, *args): + # On resize, we need to disable the resize event handling so we don't + # get too many events. Also stop the animation events, so that + # we're paused. Reset the cache and re-init. Set up an event handler + # to catch once the draw has actually taken place. + self._fig.canvas.mpl_disconnect(self._resize_id) + self.event_source.stop() + self._blit_cache.clear() + self._init_draw() + self._resize_id = self._fig.canvas.mpl_connect('draw_event', self._end_redraw) + + def _end_redraw(self, evt): + # Now that the redraw has happened, do the post draw flushing and + # blit handling. Then re-enable all of the original events. + self._post_draw(None, self._blit) + self.event_source.start() + self._fig.canvas.mpl_disconnect(self._resize_id) + self._resize_id = self._fig.canvas.mpl_connect('resize_event', + self._handle_resize) + + +class TimedAnimation(Animation): + ''' + :class:`Animation` subclass that supports time-based animation, drawing + a new frame every *interval* milliseconds. + + *repeat* controls whether the animation should repeat when the sequence + of frames is completed. + + *repeat_delay* optionally adds a delay in milliseconds before repeating + the animation. + ''' + def __init__(self, fig, interval=200, repeat_delay=None, repeat=True, + event_source=None, *args, **kwargs): + # Store the timing information + self._interval = interval + self._repeat_delay = repeat_delay + self.repeat = repeat + + # If we're not given an event source, create a new timer. This permits + # sharing timers between animation objects for syncing animations. + if event_source is None: + event_source = fig.canvas.new_timer() + event_source.interval = self._interval + + Animation.__init__(self, fig, event_source=event_source, *args, **kwargs) + + def _step(self, *args): + ''' + Handler for getting events. + ''' + # Extends the _step() method for the Animation class. If + # Animation._step signals that it reached the end and we want to repeat, + # we refresh the frame sequence and return True. If _repeat_delay is + # set, change the event_source's interval to our loop delay and set the + # callback to one which will then set the interval back. + still_going = Animation._step(self, *args) + if not still_going and self.repeat: + if self._repeat_delay: + self.event_source.remove_callback(self._step) + self.event_source.add_callback(self._loop_delay) + self.event_source.interval = self._repeat_delay + self.frame_seq = self.new_frame_seq() + return True + else: + return still_going + + def _stop(self, *args): + # If we stop in the middle of a loop delay (which is relatively likely + # given the potential pause here, remove the loop_delay callback as + # well. + self.event_source.remove_callback(self._loop_delay) + Animation._stop(self) + + def _loop_delay(self, *args): + # Reset the interval and change callbacks after the delay. + self.event_source.remove_callback(self._loop_delay) + self.event_source.interval = self._interval + self.event_source.add_callback(self._step) + + +class ArtistAnimation(TimedAnimation): + ''' + Before calling this function, all plotting should have taken place + and the relevant artists saved. + + frame_info is a list, with each list entry a collection of artists that + represent what needs to be enabled on each frame. These will be disabled + for other frames. + ''' + def __init__(self, fig, artists, *args, **kwargs): + # Internal list of artists drawn in the most recent frame. + self._drawn_artists = [] + + # Use the list of artists as the framedata, which will be iterated + # over by the machinery. + self._framedata = artists + TimedAnimation.__init__(self, fig, *args, **kwargs) + + def _init_draw(self): + # Make all the artists involved in *any* frame invisible + axes = [] + for f in self.new_frame_seq(): + for artist in f: + artist.set_visible(False) + # Assemble a list of unique axes that need flushing + if artist.axes not in axes: + axes.append(artist.axes) + + # Flush the needed axes + for ax in axes: + ax.figure.canvas.draw() + + def _pre_draw(self, framedata, blit): + ''' + Clears artists from the last frame. + ''' + if blit: + # Let blit handle clearing + self._blit_clear(self._drawn_artists, self._blit_cache) + else: + # Otherwise, make all the artists from the previous frame invisible + for artist in self._drawn_artists: + artist.set_visible(False) + + def _draw_frame(self, artists): + # Save the artists that were passed in as framedata for the other + # steps (esp. blitting) to use. + self._drawn_artists = artists + + # Make all the artists from the current frame visible + for artist in artists: + artist.set_visible(True) + +class FuncAnimation(TimedAnimation): + ''' + Makes an animation by repeatedly calling a function *func*, passing in + (optional) arguments in *fargs*. + + *frames* can be a generator, an iterable, or a number of frames. + + *init_func* is a function used to draw a clear frame. If not given, the + results of drawing from the first item in the frames sequence will be + used. + ''' + def __init__(self, fig, func, frames=None ,init_func=None, fargs=None, + save_count=None, **kwargs): + if fargs: + self._args = fargs + else: + self._args = () + self._func = func + + # Amount of framedata to keep around for saving movies. This is only + # used if we don't know how many frames there will be: in the case + # of no generator or in the case of a callable. + self.save_count = save_count + + # Set up a function that creates a new iterable when needed. If nothing + # is passed in for frames, just use itertools.count, which will just + # keep counting from 0. A callable passed in for frames is assumed to + # be a generator. An iterable will be used as is, and anything else + # will be treated as a number of frames. + if frames is None: + self._iter_gen = itertools.count + elif callable(frames): + self._iter_gen = frames + elif iterable(frames): + self._iter_gen = lambda: iter(frames) + self.save_count = len(frames) + else: + self._iter_gen = lambda: iter(range(frames)) + self.save_count = frames + + # If we're passed in and using the default, set it to 100. + if self.save_count is None: + self.save_count = 100 + + self._init_func = init_func + + # Needs to be initialized so the draw functions work without checking + self._save_seq = [] + + TimedAnimation.__init__(self, fig, **kwargs) + + # Need to reset the saved seq, since right now it will contain data + # for a single frame from init, which is not what we want. + self._save_seq = [] + + def new_frame_seq(self): + # Use the generating function to generate a new frame sequence + return self._iter_gen() + + def new_saved_frame_seq(self): + # Generate an iterator for the sequence of saved data. If there are + # no saved frames, generate a new frame sequence and take the first + # save_count entries in it. + if self._save_seq: + return iter(self._save_seq) + else: + return itertools.islice(self.new_frame_seq(), self.save_count) + + def _init_draw(self): + # Initialize the drawing either using the given init_func or by + # calling the draw function with the first item of the frame sequence. + # For blitting, the init_func should return a sequence of modified + # artists. + if self._init_func is None: + self._draw_frame(self.new_frame_seq().next()) + else: + self._drawn_artists = self._init_func() + + def _draw_frame(self, framedata): + # Save the data for potential saving of movies. + self._save_seq.append(framedata) + + # Make sure to respect save_count (keep only the last save_count around) + self._save_seq = self._save_seq[-self.save_count:] + + # Call the func with framedata and args. If blitting is desired, + # func needs to return a sequence of any artists that were modified. + self._drawn_artists = self._func(framedata, *self._args) diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index ef05e9200273..17cc94d83835 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -92,7 +92,11 @@ def __init__(self): self.eventson = False # fire events only if eventson self._oid = 0 # an observer id self._propobservers = {} # a dict from oids to funcs - self.axes = None + try: + self.axes = None + except AttributeError: + # Handle self.axes as a read-only property, as in Figure. + pass self._remove_method = None self._url = None self._gid = None @@ -235,13 +239,13 @@ def hitlist(self, event): """ List the children of the artist which contain the mouse event *event*. """ - import traceback L = [] try: hascursor,info = self.contains(event) if hascursor: L.append(self) except: + import traceback traceback.print_exc() print "while checking",self.__class__ diff --git a/lib/matplotlib/axes.py b/lib/matplotlib/axes.py index 9cf7715aa600..4a0849ab56f9 100644 --- a/lib/matplotlib/axes.py +++ b/lib/matplotlib/axes.py @@ -34,6 +34,7 @@ import matplotlib.transforms as mtransforms import matplotlib.tri as mtri +from matplotlib.container import BarContainer, ErrorbarContainer iterable = cbook.iterable is_string_like = cbook.is_string_like @@ -178,7 +179,11 @@ def __call__(self, *args, **kwargs): if self.axes.xaxis is not None and self.axes.yaxis is not None: xunits = kwargs.pop( 'xunits', self.axes.xaxis.units) + if self.axes.name == 'polar': + xunits = kwargs.pop( 'thetaunits', xunits ) yunits = kwargs.pop( 'yunits', self.axes.yaxis.units) + if self.axes.name == 'polar': + yunits = kwargs.pop( 'runits', yunits ) if xunits!=self.axes.xaxis.units: self.axes.xaxis.set_units(xunits) if yunits!=self.axes.yaxis.units: @@ -243,7 +248,7 @@ def _xy_from_xy(self, x, y): def _makeline(self, x, y, kw, kwargs): kw = kw.copy() # Don't modify the original kw. - if not 'color' in kw: + if not 'color' in kw and not 'color' in kwargs.keys(): kw['color'] = self.color_cycle.next() # (can't use setdefault because it always evaluates # its second argument) @@ -864,6 +869,7 @@ def cla(self): self._current_image = None # strictly for pyplot via _sci, _gci self.legend_ = None self.collections = [] # collection.Collection instances + self.containers = [] # self.grid(self._gridOn) props = font_manager.FontProperties(size=rcParams['axes.titlesize']) @@ -1490,6 +1496,21 @@ def add_table(self, tab): tab._remove_method = lambda h: self.tables.remove(h) return tab + def add_container(self, container): + ''' + Add a :class:`~matplotlib.container.Container` instance + to the axes. + + Returns the collection. + ''' + label = container.get_label() + if not label: + container.set_label('_container%d'%len(self.containers)) + self.containers.append(container) + container.set_remove_method(lambda h: self.containers.remove(container)) + return container + + def relim(self): """ Recompute the data limits based on current artists. @@ -1559,6 +1580,8 @@ def _process_unit_info(self, xdata=None, ydata=None, kwargs=None): # process kwargs 2nd since these will override default units if kwargs is not None: xunits = kwargs.pop( 'xunits', self.xaxis.units) + if self.name == 'polar': + xunits = kwargs.pop( 'thetaunits', xunits ) if xunits!=self.xaxis.units: #print '\tkw setting xunits', xunits self.xaxis.set_units(xunits) @@ -1568,6 +1591,8 @@ def _process_unit_info(self, xdata=None, ydata=None, kwargs=None): self.xaxis.update_units(xdata) yunits = kwargs.pop('yunits', self.yaxis.units) + if self.name == 'polar': + yunits = kwargs.pop( 'runits', yunits ) if yunits!=self.yaxis.units: #print '\tkw setting yunits', yunits self.yaxis.set_units(yunits) @@ -2015,7 +2040,7 @@ def grid(self, b=None, which='major', **kwargs): """ call signature:: - grid(self, b=None, which='major', **kwargs) + grid(self, b=None, which='major', **kwargs) Set the axes grids on or off; *b* is a boolean. (For MATLAB compatibility, *b* may also be a string, 'on' or 'off'.) @@ -2029,11 +2054,12 @@ def grid(self, b=None, which='major', **kwargs): *kawrgs* are used to set the grid line properties, eg:: - ax.grid(color='r', linestyle='-', linewidth=2) + ax.grid(color='r', linestyle='-', linewidth=2) Valid :class:`~matplotlib.lines.Line2D` kwargs are %(Line2D)s + """ if len(kwargs): b = True @@ -3277,8 +3303,7 @@ def axhline(self, y=0, xmin=0, xmax=1, **kwargs): >>> axhline(y=.5, xmin=0.25, xmax=0.75) - Valid kwargs are :class:`~matplotlib.lines.Line2D` properties, - with the exception of 'transform': + Valid kwargs are :class:`~matplotlib.lines.Line2D` properties: %(Line2D)s @@ -3288,21 +3313,21 @@ def axhline(self, y=0, xmin=0, xmax=1, **kwargs): for example plot and source code """ - if "transform" in kwargs: - raise ValueError( - "'transform' is not allowed as a kwarg;" - + "axhline generates its own transform.") - ymin, ymax = self.get_ybound() - # We need to strip away the units for comparison with # non-unitized bounds + ymin, ymax = self.get_ybound() self._process_unit_info( ydata=y, kwargs=kwargs ) yy = self.convert_yunits( y ) scaley = (yyymax) + # the default transform trans = mtransforms.blended_transform_factory( self.transAxes, self.transData) - l = mlines.Line2D([xmin,xmax], [y,y], transform=trans, **kwargs) + + # use kwargs `transform` if given, otherwise use the default transform + kwargs['transform'] = kwargs.pop('transform', trans) + + l = mlines.Line2D([xmin,xmax], [y,y], **kwargs) l.x_isdata = False self.add_line(l) self.autoscale_view(scalex=False, scaley=scaley) @@ -3342,8 +3367,7 @@ def axvline(self, x=0, ymin=0, ymax=1, **kwargs): >>> axvline(x=.5, ymin=0.25, ymax=0.75) - Valid kwargs are :class:`~matplotlib.lines.Line2D` properties, - with the exception of 'transform': + Valid kwargs are :class:`~matplotlib.lines.Line2D` properties: %(Line2D)s @@ -3353,21 +3377,22 @@ def axvline(self, x=0, ymin=0, ymax=1, **kwargs): for example plot and source code """ - if "transform" in kwargs: - raise ValueError( - "'transform' is not allowed as a kwarg;" - + "axvline generates its own transform.") - xmin, xmax = self.get_xbound() # We need to strip away the units for comparison with # non-unitized bounds + xmin, xmax = self.get_xbound() self._process_unit_info( xdata=x, kwargs=kwargs ) xx = self.convert_xunits( x ) scalex = (xxxmax) + # the default transform trans = mtransforms.blended_transform_factory( self.transData, self.transAxes) - l = mlines.Line2D([x,x], [ymin,ymax] , transform=trans, **kwargs) + + # use kwargs `transform` if given, otherwise use the default transform + kwargs['transform'] = kwargs.pop('transform', trans) + + l = mlines.Line2D([x,x], [ymin,ymax], **kwargs) l.y_isdata = False self.add_line(l) self.autoscale_view(scalex=scalex, scaley=False) @@ -3412,9 +3437,14 @@ def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs): .. plot:: mpl_examples/pylab_examples/axhspan_demo.py """ + + # the default transform trans = mtransforms.blended_transform_factory( self.transAxes, self.transData) + # use kwargs `transform` if given, otherwise use the default transform + kwargs['transform'] = kwargs.pop('transform', trans) + # process the unit information self._process_unit_info( [xmin, xmax], [ymin, ymax], kwargs=kwargs ) @@ -3424,7 +3454,6 @@ def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs): verts = (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin) p = mpatches.Polygon(verts, **kwargs) - p.set_transform(trans) p.x_isdata = False self.add_patch(p) self.autoscale_view(scalex=False) @@ -3469,9 +3498,14 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): :meth:`axhspan` for example plot and source code """ + + # the default transform trans = mtransforms.blended_transform_factory( self.transData, self.transAxes) + # use kwargs `transform` if given, otherwise use the default transform + kwargs['transform'] = kwargs.pop('transform', trans) + # process the unit information self._process_unit_info( [xmin, xmax], [ymin, ymax], kwargs=kwargs ) @@ -3481,7 +3515,6 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): verts = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)] p = mpatches.Polygon(verts, **kwargs) - p.set_transform(trans) p.y_isdata = False self.add_patch(p) self.autoscale_view(scaley=False) @@ -3794,7 +3827,7 @@ def plot_date(self, x, y, fmt='bo', tz=None, xdate=True, ydate=False, """ call signature:: - plot_date(x, y, fmt='bo', tz=None, xdate=True, ydate=False, **kwargs) + plot_date(x, y, fmt='bo', tz=None, xdate=True, ydate=False, **kwargs) Similar to the :func:`~matplotlib.pyplot.plot` command, except the *x* or *y* (or both) data is considered to be dates, and the @@ -3805,18 +3838,18 @@ def plot_date(self, x, y, fmt='bo', tz=None, xdate=True, ydate=False, Keyword arguments: - *fmt*: string - The plot format string. + *fmt*: string + The plot format string. - *tz*: [ None | timezone string ] - The time zone to use in labeling dates. If *None*, defaults to rc - value. + *tz*: [ None | timezone string ] + The time zone to use in labeling dates. If *None*, defaults to rc + value. - *xdate*: [ True | False ] - If *True*, the *x*-axis will be labeled with dates. + *xdate*: [ True | False ] + If *True*, the *x*-axis will be labeled with dates. - *ydate*: [ False | True ] - If *True*, the *y*-axis will be labeled with dates. + *ydate*: [ False | True ] + If *True*, the *y*-axis will be labeled with dates. Note if you are using custom date tickers and formatters, it may be necessary to set the formatters/locators after the call @@ -3836,14 +3869,12 @@ def plot_date(self, x, y, fmt='bo', tz=None, xdate=True, ydate=False, .. seealso:: - :mod:`~matplotlib.dates` - for helper functions + :mod:`~matplotlib.dates` for helper functions - :func:`~matplotlib.dates.date2num`, - :func:`~matplotlib.dates.num2date` and - :func:`~matplotlib.dates.drange` - for help on creating the required floating point - dates. + :func:`~matplotlib.dates.date2num`, + :func:`~matplotlib.dates.num2date` and + :func:`~matplotlib.dates.drange` for help on creating the required + floating point dates. """ if not self._hold: self.cla() @@ -3965,7 +3996,6 @@ def semilogx(self, *args, **kwargs): } self.set_xscale('log', **d) - self.set_yscale('linear') b = self._hold self._hold = True # we've already processed the hold l = self.plot(*args, **kwargs) @@ -4016,7 +4046,6 @@ def semilogy(self, *args, **kwargs): 'nonposy': kwargs.pop('nonposy', 'mask'), } self.set_yscale('log', **d) - self.set_xscale('linear') b = self._hold self._hold = True # we've already processed the hold l = self.plot(*args, **kwargs) @@ -4176,20 +4205,29 @@ def xcorr(self, x, y, normed=True, detrend=mlab.detrend_none, return lags, c, a, b - def _get_legend_handles(self): + def _get_legend_handles(self, legend_handler_map=None): "return artists that will be used as handles for legend" - handles = self.lines[:] - handles.extend(self.patches) - handles.extend([c for c in self.collections - if isinstance(c, mcoll.LineCollection)]) - handles.extend([c for c in self.collections - if isinstance(c, mcoll.RegularPolyCollection)]) - handles.extend([c for c in self.collections - if isinstance(c, mcoll.CircleCollection)]) + handles_original = self.lines + self.patches + \ + self.collections + self.containers + + # collections + handler_map = mlegend.Legend.get_default_handler_map() + + if legend_handler_map is not None: + handler_map = handler_map.copy() + handler_map.update(legend_handler_map) + + handles = [] + for h in handles_original: + if h.get_label() == "_nolegend_": #.startswith('_'): + continue + if mlegend.Legend.get_legend_handler(handler_map, h): + handles.append(h) + return handles - def get_legend_handles_labels(self): + def get_legend_handles_labels(self, legend_handler_map=None): """ return handles and labels for legend @@ -4202,10 +4240,10 @@ def get_legend_handles_labels(self): handles = [] labels = [] - for handle in self._get_legend_handles(): + for handle in self._get_legend_handles(legend_handler_map): label = handle.get_label() - if (label is not None and - label != '' and not label.startswith('_')): + #if (label is not None and label != '' and not label.startswith('_')): + if label and not label.startswith('_'): handles.append(handle) labels.append(label) @@ -4353,6 +4391,9 @@ def legend(self, *args, **kwargs): columnspacing the spacing between columns ================ ================================================================== + .. Note:: Not all kinds of artist are supported by the legend command. + See LINK (FIXME) for details. + **Example:** @@ -4394,7 +4435,10 @@ def legend(self, *args, **kwargs): raise TypeError('Invalid arguments to legend') - handles = cbook.flatten(handles) + # Why do we need to call "flatten" here? -JJL + # handles = cbook.flatten(handles) + + self.legend_ = mlegend.Legend(self, handles, labels, **kwargs) return self.legend_ @@ -4655,9 +4699,8 @@ def make_iterable(x): facecolor=c, edgecolor=e, linewidth=lw, - label=label + label='_nolegend_' ) - label = '_nolegend_' r.update(kwargs) r.get_path()._interpolation_steps = 100 #print r.get_label(), label, 'label' in kwargs @@ -4678,10 +4721,14 @@ def make_iterable(x): x = [l+w for l,w in zip(left, width)] y = [b+0.5*h for b,h in zip(bottom, height)] - self.errorbar( - x, y, - yerr=yerr, xerr=xerr, - fmt=None, **error_kw) + if "label" not in error_kw: + error_kw["label"] = '_nolegend_' + + errorbar = self.errorbar(x, y, + yerr=yerr, xerr=xerr, + fmt=None, **error_kw) + else: + errorbar = None self.hold(holdstate) # restore previous hold state @@ -4701,7 +4748,11 @@ def make_iterable(x): ymin = max(ymin*0.9, 1e-100) self.dataLim.intervaly = (ymin, ymax) self.autoscale_view() - return patches + + bar_container = BarContainer(patches, errorbar, label=label) + self.add_container(bar_container) + + return bar_container @docstring.dedent_interpd def barh(self, bottom, width, height=0.8, left=None, **kwargs): @@ -5103,6 +5154,8 @@ def errorbar(self, x, y, yerr=None, xerr=None, holdstate = self._hold self._hold = True + label = kwargs.pop("label", None) + # make sure all the args are iterable; use lists not arrays to # preserve units if not iterable(x): @@ -5122,7 +5175,7 @@ def errorbar(self, x, y, yerr=None, xerr=None, l0 = None if barsabove and fmt is not None: - l0, = self.plot(x,y,fmt,**kwargs) + l0, = self.plot(x,y,fmt,label="_nolegend_", **kwargs) barcols = [] caplines = [] @@ -5275,7 +5328,14 @@ def xywhere(xs, ys, mask): self.autoscale_view() self._hold = holdstate - return (l0, caplines, barcols) + + errorbar_container = ErrorbarContainer((l0, tuple(caplines), tuple(barcols)), + has_xerr=(xerr is not None), + has_yerr=(yerr is not None), + label=label) + self.containers.append(errorbar_container) + + return errorbar_container # (l0, caplines, barcols) def boxplot(self, x, notch=0, sym='b+', vert=1, whis=1.5, positions=None, widths=None, patch_artist=False, @@ -5826,24 +5886,17 @@ def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None, else: collection.autoscale_None() - temp_x = x - temp_y = y - - minx = np.amin(temp_x) - maxx = np.amax(temp_x) - miny = np.amin(temp_y) - maxy = np.amax(temp_y) - - w = maxx-minx - h = maxy-miny - # the pad is a little hack to deal with the fact that we don't # want to transform all the symbols whose scales are in points # to data coords to get the exact bounding box for efficiency # reasons. It can be done right if this is deemed important - padx, pady = 0.05*w, 0.05*h - corners = (minx-padx, miny-pady), (maxx+padx, maxy+pady) - self.update_datalim( corners) + # Also, only bother with this padding if there is anything to draw. + if self._xmargin < 0.05 and x.size > 0 : + self.set_xmargin(0.05) + + if self._ymargin < 0.05 and x.size > 0 : + self.set_ymargin(0.05) + self.autoscale_view() # add the collection last @@ -5860,12 +5913,12 @@ def hexbin(self, x, y, C = None, gridsize = 100, bins = None, """ call signature:: - hexbin(x, y, C = None, gridsize = 100, bins = None, - xscale = 'linear', yscale = 'linear', - cmap=None, norm=None, vmin=None, vmax=None, - alpha=None, linewidths=None, edgecolors='none' - reduce_C_function = np.mean, mincnt=None, marginals=True - **kwargs) + hexbin(x, y, C = None, gridsize = 100, bins = None, + xscale = 'linear', yscale = 'linear', + cmap=None, norm=None, vmin=None, vmax=None, + alpha=None, linewidths=None, edgecolors='none' + reduce_C_function = np.mean, mincnt=None, marginals=True + **kwargs) Make a hexagonal binning plot of *x* versus *y*, where *x*, *y* are 1-D sequences of the same length, *N*. If *C* is None @@ -5884,84 +5937,84 @@ def hexbin(self, x, y, C = None, gridsize = 100, bins = None, Optional keyword arguments: - *gridsize*: [ 100 | integer ] - The number of hexagons in the *x*-direction, default is - 100. The corresponding number of hexagons in the - *y*-direction is chosen such that the hexagons are - approximately regular. Alternatively, gridsize can be a - tuple with two elements specifying the number of hexagons - in the *x*-direction and the *y*-direction. + *gridsize*: [ 100 | integer ] + The number of hexagons in the *x*-direction, default is + 100. The corresponding number of hexagons in the + *y*-direction is chosen such that the hexagons are + approximately regular. Alternatively, gridsize can be a + tuple with two elements specifying the number of hexagons + in the *x*-direction and the *y*-direction. - *bins*: [ None | 'log' | integer | sequence ] - If *None*, no binning is applied; the color of each hexagon - directly corresponds to its count value. + *bins*: [ None | 'log' | integer | sequence ] + If *None*, no binning is applied; the color of each hexagon + directly corresponds to its count value. - If 'log', use a logarithmic scale for the color - map. Internally, :math:`log_{10}(i+1)` is used to - determine the hexagon color. + If 'log', use a logarithmic scale for the color + map. Internally, :math:`log_{10}(i+1)` is used to + determine the hexagon color. - If an integer, divide the counts in the specified number - of bins, and color the hexagons accordingly. + If an integer, divide the counts in the specified number + of bins, and color the hexagons accordingly. - If a sequence of values, the values of the lower bound of - the bins to be used. + If a sequence of values, the values of the lower bound of + the bins to be used. - *xscale*: [ 'linear' | 'log' ] - Use a linear or log10 scale on the horizontal axis. + *xscale*: [ 'linear' | 'log' ] + Use a linear or log10 scale on the horizontal axis. - *scale*: [ 'linear' | 'log' ] - Use a linear or log10 scale on the vertical axis. + *scale*: [ 'linear' | 'log' ] + Use a linear or log10 scale on the vertical axis. - *mincnt*: None | a positive integer - If not None, only display cells with more than *mincnt* - number of points in the cell + *mincnt*: None | a positive integer + If not None, only display cells with more than *mincnt* + number of points in the cell - *marginals*: True|False - if marginals is True, plot the marginal density as - colormapped rectagles along the bottom of the x-axis and - left of the y-axis + *marginals*: True|False + if marginals is True, plot the marginal density as + colormapped rectagles along the bottom of the x-axis and + left of the y-axis - *extent*: [ None | scalars (left, right, bottom, top) ] - The limits of the bins. The default assigns the limits - based on gridsize, x, y, xscale and yscale. + *extent*: [ None | scalars (left, right, bottom, top) ] + The limits of the bins. The default assigns the limits + based on gridsize, x, y, xscale and yscale. Other keyword arguments controlling color mapping and normalization arguments: - *cmap*: [ None | Colormap ] - a :class:`matplotlib.cm.Colormap` instance. If *None*, - defaults to rc ``image.cmap``. + *cmap*: [ None | Colormap ] + a :class:`matplotlib.cm.Colormap` instance. If *None*, + defaults to rc ``image.cmap``. - *norm*: [ None | Normalize ] - :class:`matplotlib.colors.Normalize` instance is used to - scale luminance data to 0,1. + *norm*: [ None | Normalize ] + :class:`matplotlib.colors.Normalize` instance is used to + scale luminance data to 0,1. - *vmin*/*vmax*: scalar - *vmin* and *vmax* are used in conjunction with *norm* to normalize - luminance data. If either are *None*, the min and max of the color - array *C* is used. Note if you pass a norm instance, your settings - for *vmin* and *vmax* will be ignored. + *vmin*/*vmax*: scalar + *vmin* and *vmax* are used in conjunction with *norm* to normalize + luminance data. If either are *None*, the min and max of the color + array *C* is used. Note if you pass a norm instance, your settings + for *vmin* and *vmax* will be ignored. - *alpha*: scalar between 0 and 1, or None - the alpha value for the patches + *alpha*: scalar between 0 and 1, or None + the alpha value for the patches - *linewidths*: [ None | scalar ] - If *None*, defaults to rc lines.linewidth. Note that this - is a tuple, and if you set the linewidths argument you - must set it as a sequence of floats, as required by - :class:`~matplotlib.collections.RegularPolyCollection`. + *linewidths*: [ None | scalar ] + If *None*, defaults to rc lines.linewidth. Note that this + is a tuple, and if you set the linewidths argument you + must set it as a sequence of floats, as required by + :class:`~matplotlib.collections.RegularPolyCollection`. Other keyword arguments controlling the Collection properties: - *edgecolors*: [ None | mpl color | color sequence ] - If 'none', draws the edges in the same color as the fill color. - This is the default, as it avoids unsightly unpainted pixels - between the hexagons. + *edgecolors*: [ None | mpl color | color sequence ] + If 'none', draws the edges in the same color as the fill color. + This is the default, as it avoids unsightly unpainted pixels + between the hexagons. - If *None*, draws the outlines in the default color. + If *None*, draws the outlines in the default color. - If a matplotlib color arg or sequence of rgba tuples, draws the - outlines in the specified color. + If a matplotlib color arg or sequence of rgba tuples, draws the + outlines in the specified color. Here are the standard descriptions of all the :class:`~matplotlib.collections.Collection` kwargs: @@ -6305,6 +6358,13 @@ def arrow(self, x, y, dx, dy, **kwargs): .. plot:: mpl_examples/pylab_examples/arrow_demo.py """ + # Strip away units for the underlying patch since units + # do not make sense to most patch-like code + x = self.convert_xunits(x) + y = self.convert_yunits(y) + dx = self.convert_xunits(dx) + dy = self.convert_yunits(dy) + a = mpatches.FancyArrow(x, y, dx, dy, **kwargs) self.add_artist(a) return a @@ -7429,9 +7489,11 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, """ call signature:: - hist(x, bins=10, range=None, normed=False, cumulative=False, - bottom=None, histtype='bar', align='mid', - orientation='vertical', rwidth=None, log=False, **kwargs) + def hist(x, bins=10, range=None, normed=False, weights=None, + cumulative=False, bottom=None, histtype='bar', align='mid', + orientation='vertical', rwidth=None, log=False, + color=None, label=None, + **kwargs): Compute and draw the histogram of *x*. The return value is a tuple (*n*, *bins*, *patches*) or ([*n0*, *n1*, ...], *bins*, @@ -7600,7 +7662,7 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, 'this looks transposed (shape is %d x %d)' % x.shape[::-1]) else: # multiple hist with data of different length - x = [np.array(xi) for xi in x] + x = [np.asarray(xi) for xi in x] nx = len(x) # number of datasets @@ -7623,7 +7685,7 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, else: raise ValueError("weights must be 1D or 2D") else: - w = [np.array(wi) for wi in weights] + w = [np.asarray(wi) for wi in weights] if len(w) != nx: raise ValueError('weights should have the same shape as x') @@ -7800,22 +7862,26 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, self.dataLim.intervaly = (ymin, ymax) if label is None: - labels = ['_nolegend_'] + labels = [None] elif is_string_like(label): labels = [label] elif is_sequence_of_strings(label): labels = list(label) else: - raise ValueError( - 'invalid label: must be string or sequence of strings') + raise ValueError('invalid label: must be string or sequence of strings') + if len(labels) < nx: - labels += ['_nolegend_'] * (nx - len(labels)) + labels += [None] * (nx - len(labels)) for (patch, lbl) in zip(patches, labels): - for p in patch: + if patch: + p = patch[0] p.update(kwargs) - p.set_label(lbl) - lbl = '_nolegend_' + if lbl is not None: p.set_label(lbl) + + for p in patch[1:]: + p.update(kwargs) + p.set_label('_nolegend_') if binsgiven: if orientation == 'vertical': @@ -8286,10 +8352,12 @@ def get_tightbbox(self, renderer): if self.xaxis.get_visible(): artists.append(self.xaxis.label) bbx1, bbx2 = self.xaxis.get_ticklabel_extents(renderer) + self.xaxis._update_label_position([bbx1], [bbx2]) bb.extend([bbx1, bbx2]) if self.yaxis.get_visible(): artists.append(self.yaxis.label) bby1, bby2 = self.yaxis.get_ticklabel_extents(renderer) + self.yaxis._update_label_position([bby1], [bby2]) bb.extend([bby1, bby2]) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 85e078c93332..585c1a29405b 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -262,9 +262,6 @@ def get_view_interval(self): 'return the view Interval instance for the axis this tick is ticking' raise NotImplementedError('Derived must override') - def set_view_interval(self, vmin, vmax, ignore=False): - raise NotImplementedError('Derived must override') - def _apply_params(self, **kw): switchkw = ['gridOn', 'tick1On', 'tick2On', 'label1On', 'label2On'] switches = [k for k in kw if k in switchkw] @@ -445,20 +442,6 @@ def get_view_interval(self): 'return the Interval instance for this axis view limits' return self.axes.viewLim.intervalx - def set_view_interval(self, vmin, vmax, ignore = False): - if ignore: - self.axes.viewLim.intervalx = vmin, vmax - else: - Vmin, Vmax = self.get_view_interval() - self.axes.viewLim.intervalx = min(vmin, Vmin), max(vmax, Vmax) - - def get_minpos(self): - return self.axes.dataLim.minposx - - def get_data_interval(self): - 'return the Interval instance for this axis data limits' - return self.axes.dataLim.intervalx - class YTick(Tick): """ @@ -595,20 +578,6 @@ def get_view_interval(self): 'return the Interval instance for this axis view limits' return self.axes.viewLim.intervaly - def set_view_interval(self, vmin, vmax, ignore = False): - if ignore: - self.axes.viewLim.intervaly = vmin, vmax - else: - Vmin, Vmax = self.get_view_interval() - self.axes.viewLim.intervaly = min(vmin, Vmin), max(vmax, Vmax) - - def get_minpos(self): - return self.axes.dataLim.minposy - - def get_data_interval(self): - 'return the Interval instance for this axis data limits' - return self.axes.dataLim.intervaly - class Ticker: locator = None diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 3b64bd1d080d..44fd11713701 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -48,7 +48,11 @@ import matplotlib.textpath as textpath from matplotlib.path import Path - +try: + from PIL import Image + _has_pil = True +except ImportError: + _has_pil = False _backend_d = {} @@ -912,28 +916,42 @@ class TimerBase(object): event loops. Mandatory functions that must be implemented: - * _timer_start: Contains backend-specific code for starting the timer - * _timer_stop: Contains backend-specific code for stopping the timer + + * `_timer_start`: Contains backend-specific code for starting + the timer + + * `_timer_stop`: Contains backend-specific code for stopping + the timer Optional overrides: - * _timer_set_single_shot: Code for setting the timer to single shot - operating mode, if supported by the timer object. If not, the Timer - class itself will store the flag and the _on_timer method should - be overridden to support such behavior. - * _timer_set_interval: Code for setting the interval on the timer, if - there is a method for doing so on the timer object. - * _on_timer: This is the internal function that any timer object should - call, which will handle the task of running all callbacks that have - been set. + + * `_timer_set_single_shot`: Code for setting the timer to + single shot operating mode, if supported by the timer + object. If not, the `Timer` class itself will store the flag + and the `_on_timer` method should be overridden to support + such behavior. + + * `_timer_set_interval`: Code for setting the interval on the + timer, if there is a method for doing so on the timer + object. + + * `_on_timer`: This is the internal function that any timer + object should call, which will handle the task of running + all callbacks that have been set. Attributes: - * interval: The time between timer events in milliseconds. Default - is 1000 ms. - * single_shot: Boolean flag indicating whether this timer should - operate as single shot (run once and then stop). Defaults to False. - * callbacks: Stores list of (func, args) tuples that will be called - upon timer events. This list can be manipulated directly, or the - functions add_callback and remove_callback can be used. + + * `interval`: The time between timer events in + milliseconds. Default is 1000 ms. + + * `single_shot`: Boolean flag indicating whether this timer + should operate as single shot (run once and then + stop). Defaults to `False`. + + * `callbacks`: Stores list of (func, args) tuples that will be + called upon timer events. This list can be manipulated + directly, or the functions `add_callback` and + `remove_callback` can be used. ''' def __init__(self, interval=None, callbacks=None): #Initialize empty callbacks list and setup default settings if necssary @@ -1758,6 +1776,42 @@ def print_svgz(self, *args, **kwargs): svg = self.switch_backends(FigureCanvasSVG) return svg.print_svgz(*args, **kwargs) + if _has_pil: + filetypes['jpg'] = filetypes['jpeg'] = 'Joint Photographic Experts Group' + def print_jpg(self, filename_or_obj, *args, **kwargs): + """ + Supported kwargs: + + *quality*: The image quality, on a scale from 1 (worst) to + 95 (best). The default is 75. Values above 95 should + be avoided; 100 completely disables the JPEG + quantization stage. + + *optimize*: If present, indicates that the encoder should + make an extra pass over the image in order to select + optimal encoder settings. + + *progressive*: If present, indicates that this image + should be stored as a progressive JPEG file. + """ + from backends.backend_agg import FigureCanvasAgg # lazy import + agg = self.switch_backends(FigureCanvasAgg) + buf, size = agg.print_to_buffer() + image = Image.frombuffer('RGBA', size, buf, 'raw', 'RGBA', 0, 1) + options = cbook.restrict_dict(kwargs, ['quality', 'optimize', + 'progressive']) + return image.save(filename_or_obj, **options) + print_jpeg = print_jpg + + filetypes['tif'] = filetypes['tiff'] = 'Tagged Image File Format' + def print_tif(self, filename_or_obj, *args, **kwargs): + from backends.backend_agg import FigureCanvasAgg # lazy import + agg = self.switch_backends(FigureCanvasAgg) + buf, size = agg.print_to_buffer() + image = Image.frombuffer('RGBA', size, buf, 'raw', 'RGBA', 0, 1) + return image.save(filename_or_obj) + print_tiff = print_tif + def get_supported_filetypes(self): return self.filetypes diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index 77887243bb6c..b41f4aaee105 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -75,15 +75,18 @@ def _get_hinting_flag(self): else: return LOAD_NO_HINTING + # for filtering to work with rasterization, methods needs to be wrapped. + # maybe there is better way to do it. def draw_markers(self, *kl, **kw): - # for filtering to work with rastrization, methods needs to be wrapped. - # maybe there is better way to do it. return self._renderer.draw_markers(*kl, **kw) + def draw_path_collection(self, *kl, **kw): + return self._renderer.draw_path_collection(*kl, **kw) + def _update_methods(self): #self.draw_path = self._renderer.draw_path # see below #self.draw_markers = self._renderer.draw_markers - self.draw_path_collection = self._renderer.draw_path_collection + #self.draw_path_collection = self._renderer.draw_path_collection self.draw_quad_mesh = self._renderer.draw_quad_mesh self.draw_gouraud_triangle = self._renderer.draw_gouraud_triangle self.draw_gouraud_triangles = self._renderer.draw_gouraud_triangles @@ -326,6 +329,10 @@ def post_processing(image, dpi): post_processing is plotted (using draw_image) on it. """ + # WARNING. + # For agg_filter to work, the rendere's method need + # to overridden in the class. See draw_markers, and draw_path_collections + from matplotlib._image import fromarray width, height = int(self.width), int(self.height) @@ -445,3 +452,13 @@ def print_png(self, filename_or_obj, *args, **kwargs): renderer.width, renderer.height, filename_or_obj, self.figure.dpi) renderer.dpi = original_dpi + + def print_to_buffer(self): + FigureCanvasAgg.draw(self) + renderer = self.get_renderer() + original_dpi = renderer.dpi + renderer.dpi = self.figure.dpi + result = (renderer._renderer.buffer_rgba(0, 0), + (int(renderer.width), int(renderer.height))) + renderer.dpi = original_dpi + return result diff --git a/lib/matplotlib/backends/backend_macosx.py b/lib/matplotlib/backends/backend_macosx.py index 1fc24a58b672..b39676a7c74f 100644 --- a/lib/matplotlib/backends/backend_macosx.py +++ b/lib/matplotlib/backends/backend_macosx.py @@ -5,7 +5,7 @@ from matplotlib._pylab_helpers import Gcf from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\ - FigureManagerBase, FigureCanvasBase, NavigationToolbar2 + FigureManagerBase, FigureCanvasBase, NavigationToolbar2, TimerBase from matplotlib.backend_bases import ShowBase from matplotlib.cbook import maxdict @@ -15,7 +15,6 @@ from matplotlib.colors import colorConverter - from matplotlib.widgets import SubplotTool import matplotlib @@ -104,6 +103,10 @@ def new_gc(self): self.gc.set_hatch(None) return self.gc + def draw_gouraud_triangle(self, gc, points, colors, transform): + points = transform.transform(points) + gc.draw_gouraud_triangle(points, colors) + def draw_image(self, gc, x, y, im): im.flipud_out() nrows, ncols, data = im.as_rgba_str() @@ -237,6 +240,24 @@ def new_figure_manager(num, *args, **kwargs): manager = FigureManagerMac(canvas, num) return manager +class TimerMac(_macosx.Timer, TimerBase): + ''' + Subclass of :class:`backend_bases.TimerBase` that uses CoreFoundation + run loops for timer events. + + Attributes: + * interval: The time between timer events in milliseconds. Default + is 1000 ms. + * single_shot: Boolean flag indicating whether this timer should + operate as single shot (run once and then stop). Defaults to False. + * callbacks: Stores list of (func, args) tuples that will be called + upon timer events. This list can be manipulated directly, or the + functions add_callback and remove_callback can be used. + ''' + # completely implemented at the C-level (in _macosx.Timer) + + + class FigureCanvasMac(_macosx.FigureCanvas, FigureCanvasBase): """ The canvas the figure renders into. Calls the draw and print fig @@ -307,6 +328,21 @@ def print_gif(self, filename, *args, **kwargs): def get_default_filetype(self): return 'png' + def new_timer(self, *args, **kwargs): + """ + Creates a new backend-specific subclass of :class:`backend_bases.Timer`. + This is useful for getting periodic events through the backend's native + event loop. Implemented only for backends with GUIs. + + optional arguments: + + *interval* + Timer interval in milliseconds + *callbacks* + Sequence of (func, args, kwargs) where func(*args, **kwargs) will + be executed by the timer every *interval*. + """ + return TimerMac(*args, **kwargs) class FigureManagerMac(_macosx.FigureManager, FigureManagerBase): """ diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 84ba120dbf37..29d4433bae8f 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -1382,14 +1382,41 @@ def merge_used_characters(self, other): def get_image_magnification(self): return self.image_dpi/72.0 - def draw_image(self, gc, x, y, im): + def option_scale_image(self): + """ + pdf backend support arbitrary scaling of image. + """ + return True + + def draw_image(self, gc, x, y, im, dx=None, dy=None, transform=None): self.check_gc(gc) h, w = im.get_size_out() - h, w = 72.0*h/self.image_dpi, 72.0*w/self.image_dpi + + if dx is None: + w = 72.0*w/self.image_dpi + else: + w = dx + + if dy is None: + h = 72.0*h/self.image_dpi + else: + h = dy + imob = self.file.imageObject(im) - self.file.output(Op.gsave, w, 0, 0, h, x, y, Op.concat_matrix, - imob, Op.use_xobject, Op.grestore) + + if transform is None: + self.file.output(Op.gsave, + w, 0, 0, h, x, y, Op.concat_matrix, + imob, Op.use_xobject, Op.grestore) + else: + tr1, tr2, tr3, tr4, tr5, tr6 = transform.to_values() + + self.file.output(Op.gsave, + tr1, tr2, tr3, tr4, tr5, tr6, Op.concat_matrix, + w, 0, 0, h, x, y, Op.concat_matrix, + imob, Op.use_xobject, Op.grestore) + def draw_path(self, gc, path, transform, rgbFace=None): self.check_gc(gc, rgbFace) diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index fba529b9c655..8bbddb914203 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -1544,6 +1544,8 @@ def pstoeps(tmpfile, bbox, rotated=False): if rcParams['ps.usedistiller'] == 'xpdf': # remove extraneous "end" operator: line = tmph.readline() + elif line.startswith('%%PageBoundingBox'): + pass else: epsh.write(line) line = tmph.readline() diff --git a/lib/matplotlib/backends/backend_qt4.py b/lib/matplotlib/backends/backend_qt4.py index dab72fc85caa..bc5888369296 100644 --- a/lib/matplotlib/backends/backend_qt4.py +++ b/lib/matplotlib/backends/backend_qt4.py @@ -21,7 +21,7 @@ figureoptions = None try: - from PyQt4 import QtCore, QtGui, Qt + from PyQt4 import QtCore, QtGui except ImportError: raise ImportError("Qt4 backend requires that PyQt4 is installed.") @@ -69,11 +69,16 @@ def _create_qApp(): if QtGui.QApplication.startingUp(): if DEBUG: print "Starting up QApplication" global qApp - qApp = QtGui.QApplication( [" "] ) - QtCore.QObject.connect( qApp, QtCore.SIGNAL( "lastWindowClosed()" ), - qApp, QtCore.SLOT( "quit()" ) ) - #remember that matplotlib created the qApp - will be used by show() - _create_qApp.qAppCreatedHere = True + app = QtGui.QApplication.instance() + if app is None: + qApp = QtGui.QApplication( [" "] ) + QtCore.QObject.connect( qApp, QtCore.SIGNAL( "lastWindowClosed()" ), + qApp, QtCore.SLOT( "quit()" ) ) + #remember that matplotlib created the qApp - will be used by show() + _create_qApp.qAppCreatedHere = True + else: + qApp = app + _create_qApp.qAppCreatedHere = False _create_qApp.qAppCreatedHere = False @@ -262,7 +267,7 @@ def new_timer(self, *args, **kwargs): return TimerQT(*args, **kwargs) def flush_events(self): - Qt.qApp.processEvents() + QtGui.qApp.processEvents() def start_event_loop(self,timeout): FigureCanvasBase.start_event_loop_default(self,timeout) @@ -286,13 +291,13 @@ def idle_draw(*args): # http://old.nabble.com/Qt4-backend:-critical-bug-with-PyQt4-v4.6%2B-td26205716.html # Once a release of Qt/PyQt is available without the bug, the version check # below can be tightened further to only be applied in the necessary versions. -if Qt.PYQT_VERSION_STR.startswith('4.6'): +if QtCore.PYQT_VERSION_STR.startswith('4.6'): class FigureWindow(QtGui.QMainWindow): def __init__(self): super(FigureWindow, self).__init__() def closeEvent(self, event): super(FigureWindow, self).closeEvent(event) - self.emit(Qt.SIGNAL('destroyed()')) + self.emit(QtCore.SIGNAL('destroyed()')) else: FigureWindow = QtGui.QMainWindow # /end pyqt hackish bugfix diff --git a/lib/matplotlib/backends/backend_qt4agg.py b/lib/matplotlib/backends/backend_qt4agg.py index 89d9f4e63a4d..d9f5090e5d44 100644 --- a/lib/matplotlib/backends/backend_qt4agg.py +++ b/lib/matplotlib/backends/backend_qt4agg.py @@ -110,7 +110,7 @@ def paintEvent( self, e ): w = int(r) - int(l) h = int(t) - int(b) t = int(b) + h - reg = self.copy_from_bbox(bbox) + reg = FigureCanvasAgg.copy_from_bbox(self, bbox) stringBuffer = reg.to_string_argb() qImage = QtGui.QImage(stringBuffer, w, h, QtGui.QImage.Format_ARGB32) pixmap = QtGui.QPixmap.fromImage(qImage) @@ -127,7 +127,6 @@ def draw( self ): if DEBUG: print "FigureCanvasQtAgg.draw", self self.replot = True - FigureCanvasAgg.draw(self) self.update() def blit(self, bbox=None): @@ -140,6 +139,16 @@ def blit(self, bbox=None): t = b + h self.repaint(l, self.renderer.height-t, w, h) + def copy_from_bbox(self, *args): + """ + If a draw() has been called but the update() has not + occurred, draw into the agg canvas before copying. + """ + if self.replot: + FigureCanvasAgg.draw(self) + self.replot = False + return FigureCanvasAgg.copy_from_bbox(self, *args) + def print_figure(self, *args, **kwargs): FigureCanvasAgg.print_figure(self, *args, **kwargs) self.draw() diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index 69e556b3bc83..61d27f6506d2 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -603,7 +603,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath): if rcParams['svg.embed_char_paths']: new_chars = [] for c in s: - path = self._add_char_def(prop, c) + path = self._add_char_def(prop, ord(c)) if path is not None: new_chars.append(path) if len(new_chars): @@ -628,7 +628,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath): lastgind = None currx = 0 for c in s: - charnum = self._get_char_def_id(prop, c) + charnum = self._get_char_def_id(prop, ord(c)) ccode = ord(c) gind = cmap.get(ccode) if gind is None: @@ -680,13 +680,13 @@ def _add_char_def(self, prop, char): font = prop font.set_size(self.FONT_SCALE, 72) ps_name = font.get_sfnt()[(1,0,0,6)] - char_id = urllib.quote('%s-%d' % (ps_name, ord(char))) + char_id = urllib.quote('%s-%d' % (ps_name, char)) char_num = self._char_defs.get(char_id, None) if char_num is not None: return None path_data = [] - glyph = font.load_char(ord(char), flags=LOAD_NO_HINTING) + glyph = font.load_char(char, flags=LOAD_NO_HINTING) currx, curry = 0.0, 0.0 for step in glyph.path: if step[0] == 0: # MOVE_TO @@ -724,7 +724,7 @@ def _get_char_def_id(self, prop, char): font = prop font.set_size(self.FONT_SCALE, 72) ps_name = font.get_sfnt()[(1,0,0,6)] - char_id = urllib.quote('%s-%d' % (ps_name, ord(char))) + char_id = urllib.quote('%s-%d' % (ps_name, char)) return self._char_defs[char_id] def _draw_mathtext(self, gc, x, y, s, prop, angle): @@ -742,8 +742,8 @@ def _draw_mathtext(self, gc, x, y, s, prop, angle): if rcParams['svg.embed_char_paths']: new_chars = [] - for font, fontsize, thetext, new_x, new_y_mtc, metrics in svg_glyphs: - path = self._add_char_def(font, thetext) + for font, fontsize, char, new_x, new_y_mtc, metrics in svg_glyphs: + path = self._add_char_def(font, char) if path is not None: new_chars.append(path) if len(new_chars): @@ -760,8 +760,8 @@ def _draw_mathtext(self, gc, x, y, s, prop, angle): svg.append('translate(%f,%f)' % (x, y)) svg.append('">\n') - for font, fontsize, thetext, new_x, new_y_mtc, metrics in svg_glyphs: - charid = self._get_char_def_id(font, thetext) + for font, fontsize, char, new_x, new_y_mtc, metrics in svg_glyphs: + charid = self._get_char_def_id(font, char) svg.append('\n' % (charid, new_x, -new_y_mtc, fontsize / self.FONT_SCALE)) diff --git a/lib/matplotlib/backends/backend_tkagg.py b/lib/matplotlib/backends/backend_tkagg.py index 9c07cbc954af..1f6d5d8dc7cc 100644 --- a/lib/matplotlib/backends/backend_tkagg.py +++ b/lib/matplotlib/backends/backend_tkagg.py @@ -101,8 +101,10 @@ class TimerTk(TimerBase): def __init__(self, parent, *args, **kwargs): TimerBase.__init__(self, *args, **kwargs) self.parent = parent + self._timer = None def _timer_start(self): + self._timer_stop() self._timer = self.parent.after(self._interval, self._on_timer) def _timer_stop(self): @@ -213,18 +215,6 @@ def filter_destroy(evt): self._master = master self._tkcanvas.focus_set() - # a dict from func-> cbook.Scheduler threads - self.sourced = dict() - - # call the idle handler - def on_idle(*ignore): - self.idle_event() - return True - - # disable until you figure out how to handle threads and interrupts - #t = cbook.Idle(on_idle) - #self._tkcanvas.after_idle(lambda *ignore: t.start()) - def resize(self, event): width, height = event.width, event.height if self._resize_callback is not None: diff --git a/lib/matplotlib/backends/backend_wxagg.py b/lib/matplotlib/backends/backend_wxagg.py index a1f406f0fb74..de2b016bb319 100644 --- a/lib/matplotlib/backends/backend_wxagg.py +++ b/lib/matplotlib/backends/backend_wxagg.py @@ -128,78 +128,12 @@ def new_figure_manager(num, *args, **kwargs): return figmgr -# -# agg/wxPython image conversion functions (wxPython <= 2.6) -# - -def _py_convert_agg_to_wx_image(agg, bbox): - """ - Convert the region of the agg buffer bounded by bbox to a wx.Image. If - bbox is None, the entire buffer is converted. - - Note: agg must be a backend_agg.RendererAgg instance. - """ - image = wx.EmptyImage(int(agg.width), int(agg.height)) - image.SetData(agg.tostring_rgb()) - - if bbox is None: - # agg => rgb -> image - return image - else: - # agg => rgb -> image => bitmap => clipped bitmap => image - return wx.ImageFromBitmap(_clipped_image_as_bitmap(image, bbox)) - - -def _py_convert_agg_to_wx_bitmap(agg, bbox): - """ - Convert the region of the agg buffer bounded by bbox to a wx.Bitmap. If - bbox is None, the entire buffer is converted. - - Note: agg must be a backend_agg.RendererAgg instance. - """ - if bbox is None: - # agg => rgb -> image => bitmap - return wx.BitmapFromImage(_py_convert_agg_to_wx_image(agg, None)) - else: - # agg => rgb -> image => bitmap => clipped bitmap - return _clipped_image_as_bitmap( - _py_convert_agg_to_wx_image(agg, None), - bbox) - - -def _clipped_image_as_bitmap(image, bbox): - """ - Convert the region of a wx.Image bounded by bbox to a wx.Bitmap. - """ - l, b, width, height = bbox.bounds - r = l + width - t = b + height - - srcBmp = wx.BitmapFromImage(image) - srcDC = wx.MemoryDC() - srcDC.SelectObject(srcBmp) - - destBmp = wx.EmptyBitmap(int(width), int(height)) - destDC = wx.MemoryDC() - destDC.SelectObject(destBmp) - - destDC.BeginDrawing() - x = int(l) - y = int(image.GetHeight() - t) - destDC.Blit(0, 0, int(width), int(height), srcDC, x, y) - destDC.EndDrawing() - - srcDC.SelectObject(wx.NullBitmap) - destDC.SelectObject(wx.NullBitmap) - - return destBmp - # # agg/wxPython image conversion functions (wxPython >= 2.8) # -def _py_WX28_convert_agg_to_wx_image(agg, bbox): +def _convert_agg_to_wx_image(agg, bbox): """ Convert the region of the agg buffer bounded by bbox to a wx.Image. If bbox is None, the entire buffer is converted. @@ -216,7 +150,7 @@ def _py_WX28_convert_agg_to_wx_image(agg, bbox): return wx.ImageFromBitmap(_WX28_clipped_agg_as_bitmap(agg, bbox)) -def _py_WX28_convert_agg_to_wx_bitmap(agg, bbox): +def _convert_agg_to_wx_bitmap(agg, bbox): """ Convert the region of the agg buffer bounded by bbox to a wx.Bitmap. If bbox is None, the entire buffer is converted. @@ -262,34 +196,3 @@ def _WX28_clipped_agg_as_bitmap(agg, bbox): return destBmp - -def _use_accelerator(state): - """ - Enable or disable the WXAgg accelerator, if it is present and is also - compatible with whatever version of wxPython is in use. - """ - global _convert_agg_to_wx_image - global _convert_agg_to_wx_bitmap - - if getattr(wx, '__version__', '0.0')[0:3] < '2.8': - # wxPython < 2.8, so use the C++ accelerator or the Python routines - if state and _wxagg is not None: - _convert_agg_to_wx_image = _wxagg.convert_agg_to_wx_image - _convert_agg_to_wx_bitmap = _wxagg.convert_agg_to_wx_bitmap - else: - _convert_agg_to_wx_image = _py_convert_agg_to_wx_image - _convert_agg_to_wx_bitmap = _py_convert_agg_to_wx_bitmap - else: - # wxPython >= 2.8, so use the accelerated Python routines - _convert_agg_to_wx_image = _py_WX28_convert_agg_to_wx_image - _convert_agg_to_wx_bitmap = _py_WX28_convert_agg_to_wx_bitmap - - -# try to load the WXAgg accelerator -try: - import _wxagg -except ImportError: - _wxagg = None - -# if it's present, use it -_use_accelerator(True) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 5d138e29ff02..9335bf9ec4d5 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -373,8 +373,10 @@ def unique(x): def iterable(obj): 'return true if *obj* is iterable' - try: len(obj) - except: return False + try: + iter(obj) + except TypeError: + return False return True @@ -1243,6 +1245,12 @@ def reverse_dict(d): 'reverse the dictionary -- may lose data if values are not unique!' return dict([(v,k) for k,v in d.items()]) +def restrict_dict(d, keys): + """ + Return a dictionary that contains those keys that appear in both + d and keys, with values from d. + """ + return dict([(k,v) for (k,v) in d.iteritems() if k in keys]) def report_memory(i=0): # argument may go away 'return the memory consumed by process' @@ -1260,7 +1268,18 @@ def report_memory(i=0): # argument may go away a2 = Popen('ps -p %d -o rss,vsz' % pid, shell=True, stdout=PIPE).stdout.readlines() mem = int(a2[1].split()[0]) - + elif sys.platform.startswith('win'): + try: + a2 = Popen(["tasklist", "/nh", "/fi", "pid eq %d" % pid], + stdout=PIPE).stdout.read() + except OSError: + raise NotImplementedError( + "report_memory works on Windows only if " + "the 'tasklist' program is found") + mem = int(a2.strip().split()[-2].replace(',','')) + else: + raise NotImplementedError( + "We don't have a memory monitor for %s" % sys.platform) return mem _safezip_msg = 'In safezip, len(args[0])=%d but len(args[%d])=%d' diff --git a/lib/matplotlib/cm.py b/lib/matplotlib/cm.py index 4a1762be6991..d89bbeb587fb 100644 --- a/lib/matplotlib/cm.py +++ b/lib/matplotlib/cm.py @@ -43,7 +43,7 @@ def revcmap(data): def _reverse_cmap_spec(spec): """Reverses cmap specification *spec*, can handle both dict and tuple type specs.""" - + if 'red' in spec: return revcmap(spec) else: @@ -53,9 +53,9 @@ def _reverse_cmap_spec(spec): return revspec def _generate_cmap(name, lutsize): - """Generates the requested cmap from it's name *name*. The lut size is + """Generates the requested cmap from it's name *name*. The lut size is *lutsize*.""" - + spec = datad[name] # Generate the colormap object. diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 6a93ccbc27d9..e67d6402bacf 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -59,6 +59,8 @@ class Collection(artist.Artist, cm.ScalarMappable): scalar mappable will be made to set the face colors. """ _offsets = np.array([], np.float_) + # _offsets must be a Nx2 array! + _offsets.shape = (0, 2) _transOffset = transforms.IdentityTransform() _transforms = [] @@ -95,10 +97,11 @@ def __init__(self, self._uniform_offsets = None self._offsets = np.array([], np.float_) + # Force _offsets to be Nx2 + self._offsets.shape = (0, 2) if offsets is not None: offsets = np.asarray(offsets) - if len(offsets.shape) == 1: - offsets = offsets[np.newaxis,:] # Make it Nx2. + offsets.shape = (-1, 2) # Make it Nx2 if transOffset is not None: self._offsets = offsets self._transOffset = transOffset @@ -148,13 +151,17 @@ def get_datalim(self, transData): transOffset = self._transOffset offsets = self._offsets paths = self.get_paths() + + if not transform.is_affine: paths = [transform.transform_path_non_affine(p) for p in paths] transform = transform.get_affine() if not transOffset.is_affine: offsets = transOffset.transform_non_affine(offsets) transOffset = transOffset.get_affine() + offsets = np.asarray(offsets, np.float_) + offsets.shape = (-1, 2) # Make it Nx2 result = mpath.get_path_collection_extents( transform.frozen(), paths, self.get_transforms(), @@ -176,6 +183,7 @@ def _prepare_points(self): offsets = self._offsets paths = self.get_paths() + if self.have_units(): paths = [] for path in self.get_paths(): @@ -184,17 +192,19 @@ def _prepare_points(self): xs = self.convert_xunits(xs) ys = self.convert_yunits(ys) paths.append(mpath.Path(zip(xs, ys), path.codes)) - if len(self._offsets): - xs = self.convert_xunits(self._offsets[:,0]) - ys = self.convert_yunits(self._offsets[:,1]) + + if offsets.size > 0: + xs = self.convert_xunits(offsets[:,0]) + ys = self.convert_yunits(offsets[:,1]) offsets = zip(xs, ys) offsets = np.asarray(offsets, np.float_) + offsets.shape = (-1, 2) # Make it Nx2 if not transform.is_affine: paths = [transform.transform_path_non_affine(path) for path in paths] transform = transform.get_affine() - if not transOffset.is_affine: + if not transOffset.is_affine : offsets = transOffset.transform_non_affine(offsets) transOffset = transOffset.get_affine() @@ -258,8 +268,7 @@ def set_offsets(self, offsets): ACCEPTS: float or sequence of floats """ offsets = np.asarray(offsets, np.float_) - if len(offsets.shape) == 1: - offsets = offsets[np.newaxis,:] # Make it Nx2. + offsets.shape = (-1, 2) # Make it Nx2 #This decision is based on how they are initialized above if self._uniform_offsets is None: self._offsets = offsets @@ -428,13 +437,17 @@ def set_edgecolor(self, c): self._is_stroked = False except AttributeError: pass - if c == 'face': - self._edgecolors = 'face' - self._edgecolors_original = 'face' - else: - if c is None: c = mpl.rcParams['patch.edgecolor'] - self._edgecolors_original = c - self._edgecolors = mcolors.colorConverter.to_rgba_array(c, self._alpha) + try: + if c.lower() == 'face': + self._edgecolors = 'face' + self._edgecolors_original = 'face' + return + except AttributeError: + pass + if c is None: + c = mpl.rcParams['patch.edgecolor'] + self._edgecolors_original = c + self._edgecolors = mcolors.colorConverter.to_rgba_array(c, self._alpha) def set_edgecolors(self, c): @@ -479,9 +492,12 @@ def update_scalarmappable(self): If the scalar mappable array is not none, update colors from scalar data """ - if self._A is None: return + if self._A is None: + return if self._A.ndim > 1: raise ValueError('Collections can only map rank 1 arrays') + if not self.check_update("array"): + return if self._is_filled: self._facecolors = self.to_rgba(self._A, self._alpha) elif self._is_stroked: @@ -807,7 +823,7 @@ def __init__(self, segments, # Can be None. The default is 5 pt. The use of :class:`~matplotlib.cm.ScalarMappable` is optional. - If the :class:`~matplotlib.cm.ScalarMappable` matrix + If the :class:`~matplotlib.cm.ScalarMappable` array :attr:`~matplotlib.cm.ScalarMappable._A` is not None (ie a call to :meth:`~matplotlib.cm.ScalarMappable.set_array` has been made), at draw time a call to scalar mappable will be made to set the colors. @@ -940,14 +956,15 @@ def __init__(self, widths, heights, angles, units='points', **kwargs): angles of first axes, degrees CCW from the X-axis *units*: ['points' | 'inches' | 'dots' | 'width' | 'height' - | 'x' | 'y' | 'xy'] - units in which majors and minors are given; 'width' and 'height' - refer to the dimensions of the axes, while 'x' and 'y' - refer to the *offsets* data units. 'xy' differs from all - others in that the angle as plotted varies with the - aspect ratio, and equals the specified angle only when - the aspect ratio is unity. Hence it behaves the same - as the :class:`~matplotlib.patches.Ellipse` with + | 'x' | 'y' | 'xy'] + + units in which majors and minors are given; 'width' and + 'height' refer to the dimensions of the axes, while 'x' + and 'y' refer to the *offsets* data units. 'xy' differs + from all others in that the angle as plotted varies with + the aspect ratio, and equals the specified angle only when + the aspect ratio is unity. Hence it behaves the same as + the :class:`~matplotlib.patches.Ellipse` with axes.transData as its transform. Additional kwargs inherited from the base :class:`Collection`: @@ -1214,9 +1231,9 @@ def draw(self, renderer): offsets = zip(xs, ys) offsets = np.asarray(offsets, np.float_) + offsets.shape = (-1, 2) # Make it Nx2 - if self.check_update('array'): - self.update_scalarmappable() + self.update_scalarmappable() if not transform.is_affine: coordinates = self._coordinates.reshape( diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 2c35c04a5207..2ea65f153a84 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -509,20 +509,22 @@ def __call__(self, X, alpha=None, bytes=False): xa = np.array([X]) else: vtype = 'array' - # force a copy here -- the ma.array and filled functions - # do force a cop of the data by default - JDH - xma = ma.array(X, copy=True) - xa = xma.filled(0) - mask_bad = ma.getmask(xma) + xma = ma.array(X, copy=False) + mask_bad = xma.mask + xa = xma.data.copy() # Copy here to avoid side effects. + del xma + # masked values are substituted below; no need to fill them here + if xa.dtype.char in np.typecodes['Float']: np.putmask(xa, xa==1.0, 0.9999999) #Treat 1.0 as slightly less than 1. # The following clip is fast, and prevents possible # conversion of large positive values to negative integers. + xa *= self.N if NP_CLIP_OUT: - np.clip(xa * self.N, -1, self.N, out=xa) + np.clip(xa, -1, self.N, out=xa) else: - xa = np.clip(xa * self.N, -1, self.N) + xa = np.clip(xa, -1, self.N) # ensure that all 'under' values will still have negative # value after casting to int @@ -532,8 +534,11 @@ def __call__(self, X, alpha=None, bytes=False): # otherwise the under-range values get converted to over-range. np.putmask(xa, xa>self.N-1, self._i_over) np.putmask(xa, xa<0, self._i_under) - if mask_bad is not None and mask_bad.shape == xa.shape: - np.putmask(xa, mask_bad, self._i_bad) + if mask_bad is not None: + if mask_bad.shape == xa.shape: + np.putmask(xa, mask_bad, self._i_bad) + elif mask_bad: + xa.fill(self._i_bad) if bytes: lut = (self._lut * 255).astype(np.uint8) else: @@ -542,6 +547,8 @@ def __call__(self, X, alpha=None, bytes=False): if alpha is not None: alpha = min(alpha, 1.0) # alpha must be between 0 and 1 alpha = max(alpha, 0.0) + if bytes: + alpha = int(alpha * 255) if (lut[-1] == 0).all(): lut[:-1, -1] = alpha # All zeros is taken as a flag for the default bad @@ -797,32 +804,63 @@ def __init__(self, vmin=None, vmax=None, clip=False): self.vmax = vmax self.clip = clip + @staticmethod + def process_value(value): + """ + Homogenize the input *value* for easy and efficient normalization. + + *value* can be a scalar or sequence. + + Returns *result*, *is_scalar*, where *result* is a + masked array matching *value*. Float dtypes are preserved; + integer types with two bytes or smaller are converted to + np.float32, and larger types are converted to np.float. + Preserving float32 when possible, and using in-place operations, + can greatly improve speed for large arrays. + + Experimental; we may want to add an option to force the + use of float32. + """ + if cbook.iterable(value): + is_scalar = False + result = ma.asarray(value) + if result.dtype.kind == 'f': + if isinstance(value, np.ndarray): + result = result.copy() + elif result.dtype.itemsize > 2: + result = result.astype(np.float) + else: + result = result.astype(np.float32) + else: + is_scalar = True + result = ma.array([value]).astype(np.float) + return result, is_scalar + def __call__(self, value, clip=None): if clip is None: clip = self.clip - if cbook.iterable(value): - vtype = 'array' - val = ma.asarray(value).astype(np.float) - else: - vtype = 'scalar' - val = ma.array([value]).astype(np.float) + result, is_scalar = self.process_value(value) - self.autoscale_None(val) + self.autoscale_None(result) vmin, vmax = self.vmin, self.vmax if vmin > vmax: raise ValueError("minvalue must be less than or equal to maxvalue") - elif vmin==vmax: - result = 0.0 * val + elif vmin == vmax: + result.fill(0) # Or should it be all masked? Or 0.5? else: vmin = float(vmin) vmax = float(vmax) if clip: - mask = ma.getmask(val) - val = ma.array(np.clip(val.filled(vmax), vmin, vmax), - mask=mask) - result = (val-vmin) / (vmax-vmin) - if vtype == 'scalar': + mask = ma.getmask(result) + result = ma.array(np.clip(result.filled(vmax), vmin, vmax), + mask=mask) + # ma division is very slow; we can take a shortcut + resdat = result.data + resdat -= vmin + resdat /= (vmax - vmin) + result = np.ma.array(resdat, mask=result.mask, copy=False) + if is_scalar: result = result[0] return result @@ -847,8 +885,10 @@ def autoscale(self, A): def autoscale_None(self, A): ' autoscale only None-valued vmin or vmax' - if self.vmin is None: self.vmin = ma.min(A) - if self.vmax is None: self.vmax = ma.max(A) + if self.vmin is None: + self.vmin = ma.min(A) + if self.vmax is None: + self.vmax = ma.max(A) def scaled(self): 'return true if vmin and vmax set' @@ -862,30 +902,37 @@ def __call__(self, value, clip=None): if clip is None: clip = self.clip - if cbook.iterable(value): - vtype = 'array' - val = ma.asarray(value).astype(np.float) - else: - vtype = 'scalar' - val = ma.array([value]).astype(np.float) + result, is_scalar = self.process_value(value) - val = ma.masked_less_equal(val, 0, copy=False) + result = ma.masked_less_equal(result, 0, copy=False) - self.autoscale_None(val) + self.autoscale_None(result) vmin, vmax = self.vmin, self.vmax if vmin > vmax: raise ValueError("minvalue must be less than or equal to maxvalue") elif vmin<=0: raise ValueError("values must all be positive") elif vmin==vmax: - result = 0.0 * val + result.fill(0) else: if clip: - mask = ma.getmask(val) - val = ma.array(np.clip(val.filled(vmax), vmin, vmax), + mask = ma.getmask(result) + val = ma.array(np.clip(result.filled(vmax), vmin, vmax), mask=mask) - result = (ma.log(val)-np.log(vmin))/(np.log(vmax)-np.log(vmin)) - if vtype == 'scalar': + #result = (ma.log(result)-np.log(vmin))/(np.log(vmax)-np.log(vmin)) + # in-place equivalent of above can be much faster + resdat = result.data + mask = result.mask + if mask is np.ma.nomask: + mask = (resdat <= 0) + else: + mask |= resdat <= 0 + np.putmask(resdat, mask, 1) + np.log(resdat, resdat) + resdat -= np.log(vmin) + resdat /= (np.log(vmax) - np.log(vmin)) + result = np.ma.array(resdat, mask=mask, copy=False) + if is_scalar: result = result[0] return result diff --git a/lib/matplotlib/container.py b/lib/matplotlib/container.py new file mode 100644 index 000000000000..0993fa236d51 --- /dev/null +++ b/lib/matplotlib/container.py @@ -0,0 +1,103 @@ +import matplotlib.cbook as cbook + +class Container(tuple): + """ + Base class for containers. + """ + + def __repr__(self): + return "" % (len(self)) + + def __new__(cls, *kl, **kwargs): + return tuple.__new__(cls, kl[0]) + + def __init__(self, kl, label=None): + + self.eventson = False # fire events only if eventson + self._oid = 0 # an observer id + self._propobservers = {} # a dict from oids to funcs + + self._remove_method = None + + self.set_label(label) + + def set_remove_method(self, f): + self._remove_method = f + + def remove(self): + for c in self: + c.remove() + + if self._remove_method: + self._remove_method() + + def get_label(self): + """ + Get the label used for this artist in the legend. + """ + return self._label + + def set_label(self, s): + """ + Set the label to *s* for auto legend. + + ACCEPTS: any string + """ + self._label = s + self.pchanged() + + def add_callback(self, func): + """ + Adds a callback function that will be called whenever one of + the :class:`Artist`'s properties changes. + + Returns an *id* that is useful for removing the callback with + :meth:`remove_callback` later. + """ + oid = self._oid + self._propobservers[oid] = func + self._oid += 1 + return oid + + def remove_callback(self, oid): + """ + Remove a callback based on its *id*. + + .. seealso:: + + :meth:`add_callback` + For adding callbacks + + """ + try: del self._propobservers[oid] + except KeyError: pass + + def pchanged(self): + """ + Fire an event when property changed, calling all of the + registered callbacks. + """ + for oid, func in self._propobservers.items(): + func(self) + + def get_children(self): + return list(cbook.flatten(self)) + + +class BarContainer(Container): + + def __init__(self, patches, errorbar=None, **kwargs): + self.patches = patches + self.errorbar = errorbar + Container.__init__(self, patches, **kwargs) + + +class ErrorbarContainer(Container): + + def __init__(self, lines, has_xerr=False, has_yerr=False, **kwargs): + self.lines = lines + self.has_xerr = has_xerr + self.has_yerr = has_yerr + Container.__init__(self, lines, **kwargs) + + diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index 9c8ef0e34b30..2c31c20fc8f4 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -95,7 +95,9 @@ def clabel(self, *args, **kwargs): a format string for the label. Default is '%1.3f' Alternatively, this can be a dictionary matching contour levels with arbitrary strings to use for each contour level - (i.e., fmt[level]=string) + (i.e., fmt[level]=string), or it can be any callable, such + as a :class:`~matplotlib.ticker.Formatter` instance, that + returns a string when called with a numeric contour level. *manual*: if *True*, contour labels will be placed manually using @@ -326,6 +328,8 @@ def get_text(self, lev, fmt): else: if isinstance(fmt,dict): return fmt[lev] + elif callable(fmt): + return fmt(lev) else: return fmt%lev diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 4cf42e702cfe..b96423ec95e3 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -38,6 +38,83 @@ docstring.interpd.update(projection_names = get_projection_names()) +class AxesStack(Stack): + """ + Specialization of the Stack to handle all + tracking of Axes in a Figure. This requires storing + key, (ind, axes) pairs. The key is based on the args and kwargs + used in generating the Axes. ind is a serial number for tracking + the order in which axes were added. + """ + def __init__(self): + Stack.__init__(self) + self._ind = 0 + + def as_list(self): + """ + Return a list of the Axes instances that have been added to the figure + """ + ia_list = [a for k, a in self._elements] + ia_list.sort() + return [a for i, a in ia_list] + + def get(self, key): + """ + Return the Axes instance that was added with *key*. + If it is not present, return None. + """ + item = dict(self._elements).get(key) + if item is None: + return None + return item[1] + + def _entry_from_axes(self, e): + ind, k = dict([(a, (ind, k)) for (k, (ind, a)) in self._elements])[e] + return (k, (ind, e)) + + def remove(self, a): + Stack.remove(self, self._entry_from_axes(a)) + + def bubble(self, a): + return Stack.bubble(self, self._entry_from_axes(a)) + + def add(self, key, a): + """ + Add Axes *a*, with key *key*, to the stack, and return the stack. + + If *a* is already on the stack, don't add it again, but + return *None*. + """ + # All the error checking may be unnecessary; but this method + # is called so seldom that the overhead is negligible. + if not isinstance(a, Axes): + raise ValueError("second argument, %s, is not an Axes" % a) + try: + hash(key) + except TypeError: + raise ValueError("first argument, %s, is not a valid key" % key) + + a_existing = self.get(key) + if a_existing is not None: + Stack.remove(self, (key, a_existing)) + warnings.Warn( + "key %s already existed; Axes is being replaced" % key) + # I don't think the above should ever happen. + + if a in self: + return None + self._ind += 1 + return Stack.push(self, (key, (self._ind, a))) + + def __call__(self): + if not len(self._elements): + return self._default + else: + return self._elements[self._pos][1][1] + + def __contains__(self, a): + return a in self.as_list() + class SubplotParams: """ A class to hold the parameters for a subplot @@ -203,11 +280,15 @@ def __init__(self, self.subplotpars = subplotpars - self._axstack = Stack() # maintain the current axes - self.axes = [] + self._axstack = AxesStack() # track all figure axes and current axes self.clf() self._cachedRenderer = None + def _get_axes(self): + return self._axstack.as_list() + + axes = property(fget=_get_axes, doc="Read-only: list of axes in Figure") + def _get_dpi(self): return self._dpi def _set_dpi(self, dpi): @@ -524,15 +605,9 @@ def set_frameon(self, b): def delaxes(self, a): 'remove a from the figure and update the current axes' - self.axes.remove(a) self._axstack.remove(a) - keys = [] - for key, thisax in self._seen.items(): - if a==thisax: del self._seen[key] for func in self._axobservers: func(self) - - def _make_key(self, *args, **kwargs): 'make a hashable key out of args and kwargs' @@ -593,15 +668,14 @@ def add_axes(self, *args, **kwargs): %(Axes)s """ + if not len(args): return key = self._make_key(*args, **kwargs) - - if key in self._seen: - ax = self._seen[key] + ax = self._axstack.get(key) + if ax is not None: self.sca(ax) return ax - if not len(args): return if isinstance(args[0], Axes): a = args[0] assert(a.get_figure() is self) @@ -619,10 +693,8 @@ def add_axes(self, *args, **kwargs): a = projection_factory(projection, self, rect, **kwargs) - self.axes.append(a) - self._axstack.push(a) + self._axstack.add(key, a) self.sca(a) - self._seen[key] = a return a @docstring.dedent_interpd @@ -654,15 +726,17 @@ def add_subplot(self, *args, **kwargs): %(Axes)s """ - - kwargs = kwargs.copy() - if not len(args): return + if len(args) == 1 and isinstance(args[0], int): + args = tuple([int(c) for c in str(args[0])]) + if isinstance(args[0], SubplotBase): a = args[0] assert(a.get_figure() is self) + key = self._make_key(*args, **kwargs) else: + kwargs = kwargs.copy() ispolar = kwargs.pop('polar', False) projection = kwargs.pop('projection', None) if ispolar: @@ -675,20 +749,23 @@ def add_subplot(self, *args, **kwargs): projection_class = get_projection_class(projection) + # Remake the key without projection kwargs: key = self._make_key(*args, **kwargs) - if key in self._seen: - ax = self._seen[key] + ax = self._axstack.get(key) + if ax is not None: if isinstance(ax, projection_class): self.sca(ax) return ax else: - self.axes.remove(ax) self._axstack.remove(ax) + # Undocumented convenience behavior: + # subplot(111); subplot(111, projection='polar') + # will replace the first with the second. + # Without this, add_subplot would be simpler and + # more similar to add_axes. a = subplot_class_factory(projection_class)(self, *args, **kwargs) - self._seen[key] = a - self.axes.append(a) - self._axstack.push(a) + self._axstack.add(key, a) self.sca(a) return a @@ -704,13 +781,12 @@ def clf(self, keep_observers=False): for ax in tuple(self.axes): # Iterate over the copy. ax.cla() - self.delaxes(ax) # removes ax from self.axes + self.delaxes(ax) # removes ax from self._axstack toolbar = getattr(self.canvas, 'toolbar', None) if toolbar is not None: toolbar.update() self._axstack.clear() - self._seen = {} self.artists = [] self.lines = [] self.patches = [] @@ -732,7 +808,6 @@ def draw(self, renderer): Render the figure using :class:`matplotlib.backend_bases.RendererBase` instance renderer """ # draw the figure bounding box, perhaps none for white figure - #print 'figure draw' if not self.get_visible(): return renderer.open_group('figure') @@ -899,6 +974,8 @@ def legend(self, handles, labels, *args, **kwargs): columnspacing the spacing between columns ================ ================================================================== + .. Note:: Not all kinds of artist are supported by the legend. + See LINK (FIXME) for details. **Example:** @@ -976,7 +1053,7 @@ def _gci(self): helper for :func:`~matplotlib.pyplot.gci`; do not use elsewhere. """ - for ax in reversed(self._axstack): + for ax in reversed(self.axes): im = ax._gci() if im is not None: return im diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 49f7674f8d38..9fb055102dbc 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -124,6 +124,7 @@ X11FontDirectories = [ # an old standard installation point "/usr/X11R6/lib/X11/fonts/TTF/", + "/usr/X11/lib/X11/fonts", # here is the new standard location for fonts "/usr/share/fonts/", # documented as a good place to install new fonts @@ -135,7 +136,10 @@ OSXFontDirectories = [ "/Library/Fonts/", "/Network/Library/Fonts/", - "/System/Library/Fonts/" + "/System/Library/Fonts/", + # fonts installed via MacPorts + "/opt/local/share/fonts" + "" ] if not USE_FONTCONFIG: diff --git a/lib/matplotlib/gridspec.py b/lib/matplotlib/gridspec.py index d6e1b58e8bc0..73932d016e49 100644 --- a/lib/matplotlib/gridspec.py +++ b/lib/matplotlib/gridspec.py @@ -127,7 +127,6 @@ def get_grid_positions(self, fig): return figBottoms, figTops, figLefts, figRights - def __getitem__(self, key): """ create and return a SuplotSpec instance. @@ -146,6 +145,8 @@ def __getitem__(self, key): else: if k1 < 0: k1 += nrows + if k1 >= nrows or k1 < 0 : + raise IndexError("index out of range") row1, row2 = k1, k1+1 @@ -154,6 +155,8 @@ def __getitem__(self, key): else: if k2 < 0: k2 += ncols + if k2 >= ncols or k2 < 0 : + raise IndexError("index out of range") col1, col2 = k2, k2+1 @@ -168,6 +171,8 @@ def __getitem__(self, key): else: if key < 0: key += total + if key >= total or key < 0 : + raise IndexError("index out of range") num1, num2 = key, None diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index e628d99b97d7..624e9dd04c38 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -97,7 +97,7 @@ def __init__(self, ax, self._imcache = None - # this is an expetimental attribute, if True, unsampled image + # this is an experimental attribute, if True, unsampled image # will be drawn using the affine transform that are # appropriately skewed so that the given postition # corresponds to the actual position in the coordinate. -JJL @@ -138,7 +138,7 @@ def make_image(self, magnification=1.0): def _get_unsampled_image(self, A, image_extents, viewlim): """ convert numpy array A with given extents ([x1, x2, y1, y2] in - data coordinate) into the Image, given the vielim (should be a + data coordinate) into the Image, given the viewlim (should be a bbox instance). Image will be clipped if the extents is significantly larger than the viewlim. """ @@ -193,17 +193,17 @@ def _get_unsampled_image(self, A, image_extents, viewlim): self._oldyslice = yslice if self._imcache is None: - if self._A.dtype == np.uint8 and len(self._A.shape) == 3: + if self._A.dtype == np.uint8 and self._A.ndim == 3: im = _image.frombyte(self._A[yslice,xslice,:], 0) im.is_grayscale = False else: if self._rgbacache is None: - x = self.to_rgba(self._A, self._alpha) + x = self.to_rgba(self._A, self._alpha, bytes=True) self._rgbacache = x else: x = self._rgbacache - im = _image.fromarray(x[yslice,xslice], 0) - if len(self._A.shape) == 2: + im = _image.frombyte(x[yslice,xslice,:], 0) + if self._A.ndim == 2: im.is_grayscale = self.cmap.is_gray() else: im.is_grayscale = False @@ -654,7 +654,8 @@ class NonUniformImage(AxesImage): def __init__(self, ax, **kwargs): """ kwargs are identical to those for AxesImage, except - that 'interpolation' defaults to 'nearest' + that 'interpolation' defaults to 'nearest', and 'bilinear' + is the only alternative. """ interp = kwargs.pop('interpolation', 'nearest') AxesImage.__init__(self, ax, @@ -712,7 +713,7 @@ def set_data(self, x, y, A): A.shape = A.shape[0:2] if len(A.shape) == 2: if A.dtype != np.uint8: - A = (self.cmap(self.norm(A))*255).astype(np.uint8) + A = self.to_rgba(A, alpha=self._alpha, bytes=True) self.is_grayscale = self.cmap.is_gray() else: A = np.repeat(A[:,:,np.newaxis], 4, 2) @@ -796,6 +797,9 @@ def __init__(self, ax, cm.ScalarMappable.__init__(self, norm, cmap) self.axes = ax self._rgbacache = None + # There is little point in caching the image itself because + # it needs to be remade if the bbox or viewlim change, + # so caching does help with zoom/pan/resize. self.update(kwargs) self.set_data(x, y, A) @@ -810,7 +814,7 @@ def make_image(self, magnification=1.0): height = (round(t) + 0.5) - (round(b) - 0.5) width = width * magnification height = height * magnification - if self.check_update('array'): + if self._rgbacache is None: A = self.to_rgba(self._A, alpha=self._alpha, bytes=True) self._rgbacache = A if self._A.ndim == 2: @@ -826,9 +830,14 @@ def make_image(self, magnification=1.0): im.is_grayscale = self.is_grayscale return im + def changed(self): + self._rgbacache = None + cm.ScalarMappable.changed(self) + @allow_rasterization def draw(self, renderer, *args, **kwargs): - if not self.get_visible(): return + if not self.get_visible(): + return im = self.make_image(renderer.get_image_magnification()) gc = renderer.new_gc() gc.set_clip_rectangle(self.axes.bbox.frozen()) @@ -870,7 +879,7 @@ def set_data(self, x, y, A): self._A = A self._Ax = x self._Ay = y - self.update_dict['array'] = True + self._rgbacache = None def set_array(self, *args): raise NotImplementedError('Method not supported') @@ -956,7 +965,7 @@ def make_image(self, magnification=1.0): if self._A is None: raise RuntimeError('You must first set the image array') - x = self.to_rgba(self._A, self._alpha) + x = self.to_rgba(self._A, self._alpha, bytes=True) self.magnification = magnification # if magnification is not one, we need to resize ismag = magnification!=1 @@ -965,7 +974,7 @@ def make_image(self, magnification=1.0): isoutput = 0 else: isoutput = 1 - im = _image.fromarray(x, isoutput) + im = _image.frombyte(x, isoutput) fc = self.figure.get_facecolor() im.set_bg( *mcolors.colorConverter.to_rgba(fc, 0) ) im.is_grayscale = (self.cmap.name == "gray" and @@ -1078,11 +1087,11 @@ def make_image(self, renderer, magnification=1.0): im.is_grayscale = False else: if self._rgbacache is None: - x = self.to_rgba(self._A, self._alpha) + x = self.to_rgba(self._A, self._alpha, bytes=True) self._rgbacache = x else: x = self._rgbacache - im = _image.fromarray(x, 0) + im = _image.frombyte(x, 0) if len(self._A.shape) == 2: im.is_grayscale = self.cmap.is_gray() else: diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index 3d9886d34144..f8a272e9e546 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -1,24 +1,15 @@ """ -Place a legend on the axes at location loc. Labels are a -sequence of strings and loc can be a string or an integer -specifying the legend location - -The location codes are - - 'best' : 0, (only implemented for axis legends) - 'upper right' : 1, - 'upper left' : 2, - 'lower left' : 3, - 'lower right' : 4, - 'right' : 5, - 'center left' : 6, - 'center right' : 7, - 'lower center' : 8, - 'upper center' : 9, - 'center' : 10, - -Return value is a sequence of text, line instances that make -up the legend +The legend module defines the Legend class, which is responsible for +drawing legends associated with axes and/or figures. + +The Legend class can be considered as a container of legend handles +and legend texts. Creation of corresponding legend handles from the +plot elements in the axes or figures (e.g., lines, patches, etc.) are +specified by the handler map, which defines the mapping between the +plot elements and the legend handlers to be used (the default legend +handlers are defined in the matplotlib.legend_handler module). Note +that not all kinds of artist are supported by the legend yet (See LINK +(FIXME) for details). """ from __future__ import division import warnings @@ -37,10 +28,24 @@ from matplotlib.offsetbox import HPacker, VPacker, TextArea, DrawingArea, DraggableOffsetBox +from matplotlib.container import ErrorbarContainer, BarContainer +import legend_handler + class DraggableLegend(DraggableOffsetBox): - def __init__(self, legend, use_blit=False): + def __init__(self, legend, use_blit=False, update="loc"): + """ + update : If "loc", update *loc* parameter of + legend upon finalizing. If "bbox", update + *bbox_to_anchor* parameter. + """ self.legend=legend + + if update in ["loc", "bbox"]: + self._update = update + else: + raise ValueError("update parameter '%s' is not supported." % update) + DraggableOffsetBox.__init__(self, legend, legend._legend_box, use_blit=use_blit) @@ -50,6 +55,14 @@ def artist_picker(self, legend, evt): def finalize_offset(self): loc_in_canvas = self.get_loc_in_canvas() + if self._update == "loc": + self._update_loc(loc_in_canvas) + elif self._update == "bbox": + self._update_bbox_to_anchor(loc_in_canvas) + else: + raise RuntimeError("update parameter '%s' is not supported." % self.update) + + def _update_loc(self, loc_in_canvas): bbox = self.legend.get_bbox_to_anchor() # if bbox has zero width or height, the transformation is @@ -57,11 +70,19 @@ def finalize_offset(self): if bbox.width ==0 or bbox.height ==0: self.legend.set_bbox_to_anchor(None) bbox = self.legend.get_bbox_to_anchor() - + _bbox_transform = BboxTransformFrom(bbox) self.legend._loc = tuple(_bbox_transform.transform_point(loc_in_canvas)) + def _update_bbox_to_anchor(self, loc_in_canvas): + + tr = self.legend.axes.transAxes + loc_in_bbox = tr.transform_point(loc_in_canvas) + + self.legend.set_bbox_to_anchor(loc_in_bbox) + + class Legend(Artist): """ Place a legend on the axes at location loc. Labels are a @@ -85,8 +106,6 @@ class Legend(Artist): loc can be a tuple of the noramilzed coordinate values with respect its parent. - Return value is a sequence of text, line instances that make - up the legend """ @@ -140,10 +159,11 @@ def __init__(self, parent, handles, labels, bbox_to_anchor = None, # bbox that the legend will be anchored. bbox_transform = None, # transform for the bbox frameon = True, # draw frame + handler_map = None, ): """ - *parent* : the artist that contains the legend - - *handles* : a list of artists (lines, patches) to add to the legend + - *handles* : a list of artists (lines, patches) to be added to the legend - *labels* : a list of strings to label the legend Optional keyword arguments: @@ -173,7 +193,7 @@ def __init__(self, parent, handles, labels, ================ ================================================================== -The pad and spacing parameters are measure in font-size units. E.g., +The pad and spacing parameters are measured in font-size units. E.g., a fontsize of 10 points and a handlelength=5 implies a handlelength of 50 points. Values from rcParams will be used if None. @@ -209,6 +229,9 @@ def __init__(self, parent, handles, labels, self.legendHandles = [] self._legend_title_box = None + + self._handler_map = handler_map + localdict = locals() for name in propnames: @@ -443,6 +466,88 @@ def _approx_text_height(self, renderer=None): return renderer.points_to_pixels(self._fontsize) + # _default_handler_map defines the default mapping between plot + # elements and the legend handlers. + + _default_handler_map = { + ErrorbarContainer:legend_handler.HandlerErrorbar(), + Line2D:legend_handler.HandlerLine2D(), + Patch:legend_handler.HandlerPatch(), + LineCollection:legend_handler.HandlerLineCollection(), + RegularPolyCollection:legend_handler.HandlerRegularPolyCollection(), + CircleCollection:legend_handler.HandlerCircleCollection(), + BarContainer:legend_handler.HandlerPatch(update_func=legend_handler.update_from_first_child), + tuple:legend_handler.HandlerTuple(), + } + + # (get|set|update)_default_handler_maps are public interfaces to + # modify the defalut handler map. + + @classmethod + def get_default_handler_map(cls): + """ + A class method that returns the default handler map. + """ + return cls._default_handler_map + + @classmethod + def set_default_handler_map(cls, handler_map): + """ + A class method to set the default handler map. + """ + cls._default_handler_map = handler_map + + @classmethod + def update_default_handler_map(cls, handler_map): + """ + A class method to update the default handler map. + """ + cls._default_handler_map.update(handler_map) + + def get_legend_handler_map(self): + """ + return the handler map. + """ + + default_handler_map = self.get_default_handler_map() + + if self._handler_map: + hm = default_handler_map.copy() + hm.update(self._handler_map) + return hm + else: + return default_handler_map + + @staticmethod + def get_legend_handler(legend_handler_map, orig_handle): + """ + return a legend handler from *legend_handler_map* that + corresponds to *orig_handler*. + + *legend_handler_map* should be a dictionary object (that is + returned by the get_legend_handler_map method). + + It first checks if the *orig_handle* itself is a key in the + *legend_hanler_map* and return the associated value. + Otherwised, it checks for each of the classes in its + method-resolution-order. If no matching key is found, it + returns None. + """ + legend_handler_keys = legend_handler_map.keys() + if orig_handle in legend_handler_keys: + handler = legend_handler_map[orig_handle] + else: + + for handle_type in type(orig_handle).mro(): + if handle_type in legend_handler_map: + handler = legend_handler_map[handle_type] + break + else: + handler = None + + return handler + + def _init_legend_box(self, handles, labels): """ Initiallize the legend_box. The legend_box is an instance of @@ -489,154 +594,37 @@ def _init_legend_box(self, handles, labels): # manually set their transform to the self.get_transform(). - for handle, lab in zip(handles, labels): - if isinstance(handle, RegularPolyCollection) or \ - isinstance(handle, CircleCollection): - npoints = self.scatterpoints - else: - npoints = self.numpoints - if npoints > 1: - # we put some pad here to compensate the size of the - # marker - xdata = np.linspace(0.3*fontsize, - (self.handlelength-0.3)*fontsize, - npoints) - xdata_marker = xdata - elif npoints == 1: - xdata = np.linspace(0, self.handlelength*fontsize, 2) - xdata_marker = [0.5*self.handlelength*fontsize] - - if isinstance(handle, Line2D): - ydata = ((height-descent)/2.)*np.ones(xdata.shape, float) - legline = Line2D(xdata, ydata) - - legline.update_from(handle) - self._set_artist_props(legline) # after update - legline.set_clip_box(None) - legline.set_clip_path(None) - legline.set_drawstyle('default') - legline.set_marker('None') - - handle_list.append(legline) - - legline_marker = Line2D(xdata_marker, ydata[:len(xdata_marker)]) - legline_marker.update_from(handle) - self._set_artist_props(legline_marker) - legline_marker.set_clip_box(None) - legline_marker.set_clip_path(None) - legline_marker.set_linestyle('None') - if self.markerscale !=1: - newsz = legline_marker.get_markersize()*self.markerscale - legline_marker.set_markersize(newsz) - # we don't want to add this to the return list because - # the texts and handles are assumed to be in one-to-one - # correpondence. - legline._legmarker = legline_marker - - elif isinstance(handle, Patch): - p = Rectangle(xy=(0., 0.), - width = self.handlelength*fontsize, - height=(height-descent), - ) - p.update_from(handle) - self._set_artist_props(p) - p.set_clip_box(None) - p.set_clip_path(None) - handle_list.append(p) - elif isinstance(handle, LineCollection): - ydata = ((height-descent)/2.)*np.ones(xdata.shape, float) - legline = Line2D(xdata, ydata) - self._set_artist_props(legline) - legline.set_clip_box(None) - legline.set_clip_path(None) - lw = handle.get_linewidth()[0] - dashes = handle.get_dashes()[0] - color = handle.get_colors()[0] - legline.set_color(color) - legline.set_linewidth(lw) - if dashes[0] is not None: # dashed line - legline.set_dashes(dashes[1]) - handle_list.append(legline) - - elif isinstance(handle, RegularPolyCollection): - - #ydata = self._scatteryoffsets - ydata = height*self._scatteryoffsets - - size_max, size_min = max(handle.get_sizes())*self.markerscale**2,\ - min(handle.get_sizes())*self.markerscale**2 - if self.scatterpoints < 4: - sizes = [.5*(size_max+size_min), size_max, - size_min] - else: - sizes = (size_max-size_min)*np.linspace(0,1,self.scatterpoints)+size_min - - p = type(handle)(handle.get_numsides(), - rotation=handle.get_rotation(), - sizes=sizes, - offsets=zip(xdata_marker,ydata), - transOffset=self.get_transform(), - ) - - p.update_from(handle) - p.set_figure(self.figure) - p.set_clip_box(None) - p.set_clip_path(None) - handle_list.append(p) - - elif isinstance(handle, CircleCollection): - - ydata = height*self._scatteryoffsets - - size_max, size_min = max(handle.get_sizes())*self.markerscale**2,\ - min(handle.get_sizes())*self.markerscale**2 - if self.scatterpoints < 4: - sizes = [.5*(size_max+size_min), size_max, - size_min] - else: - sizes = (size_max-size_min)*np.linspace(0,1,self.scatterpoints)+size_min - - p = type(handle)(sizes, - offsets=zip(xdata_marker,ydata), - transOffset=self.get_transform(), - ) - - p.update_from(handle) - p.set_figure(self.figure) - p.set_clip_box(None) - p.set_clip_path(None) - handle_list.append(p) - else: - handle_type = type(handle) - warnings.warn("Legend does not support %s\nUse proxy artist instead.\n\nhttp://matplotlib.sourceforge.net/users/legend_guide.html#using-proxy-artist\n" % (str(handle_type),)) - handle_list.append(None) + legend_handler_map = self.get_legend_handler_map() + + for orig_handle, lab in zip(handles, labels): + + handler = self.get_legend_handler(legend_handler_map, orig_handle) + if handler is None: + warnings.warn("Legend does not support %s\nUse proxy artist instead.\n\nhttp://matplotlib.sourceforge.net/users/legend_guide.html#using-proxy-artist\n" % (str(orig_handle),)) + handle_list.append(None) + continue + textbox = TextArea(lab, textprops=label_prop, + multilinebaseline=True, minimumdescent=True) + text_list.append(textbox._text) - handle = handle_list[-1] - if handle is not None: # handle is None is the artist is not supproted - textbox = TextArea(lab, textprops=label_prop, - multilinebaseline=True, minimumdescent=True) - text_list.append(textbox._text) + labelboxes.append(textbox) - labelboxes.append(textbox) + handlebox = DrawingArea(width=self.handlelength*fontsize, + height=height, + xdescent=0., ydescent=descent) - handlebox = DrawingArea(width=self.handlelength*fontsize, - height=height, - xdescent=0., ydescent=descent) + handle = handler(self, orig_handle, \ + #xdescent, ydescent, width, height, + fontsize, + handlebox) + handle_list.append(handle) - handlebox.add_artist(handle) - # special case for collection instances - if isinstance(handle, RegularPolyCollection) or \ - isinstance(handle, CircleCollection): - handle._transOffset = handlebox.get_transform() - handle.set_transform(None) - if hasattr(handle, "_legmarker"): - handlebox.add_artist(handle._legmarker) - handleboxes.append(handlebox) + handleboxes.append(handlebox) if len(handleboxes) > 0: @@ -931,7 +919,7 @@ def _find_best_position(self, width, height, renderer, consider=None): return ox, oy - def draggable(self, state=None, use_blit=False): + def draggable(self, state=None, use_blit=False, update="loc"): """ Set the draggable state -- if state is @@ -944,6 +932,10 @@ def draggable(self, state=None, use_blit=False): If draggable is on, you can drag the legend on the canvas with the mouse. The DraggableLegend helper instance is returned if draggable is on. + + The update parameter control which parameter of the legend changes + when dragged. If update is "loc", the *loc* paramter of the legend + is changed. If "bbox", the *bbox_to_anchor* parameter is changed. """ is_draggable = self._draggable is not None @@ -953,7 +945,7 @@ def draggable(self, state=None, use_blit=False): if state: if self._draggable is None: - self._draggable = DraggableLegend(self, use_blit) + self._draggable = DraggableLegend(self, use_blit, update=update) else: if self._draggable is not None: self._draggable.disconnect() diff --git a/lib/matplotlib/legend_handler.py b/lib/matplotlib/legend_handler.py new file mode 100644 index 000000000000..3556b48a22d1 --- /dev/null +++ b/lib/matplotlib/legend_handler.py @@ -0,0 +1,493 @@ +""" +This module defines default legend handlers. + +Legend handlers are expected to be a callable object with a following +signature. :: + + legend_handler(legend, orig_handle, fontsize, handlebox) + +Where *legend* is the legend itself, *orig_handle* is the original +plot, *fontsize* is the fontsize in pixles, and *handlebox* is a +OffsetBox instance. Within the call, you should create relevant +artists (using relevant properties from the *legend* and/or +*orig_handle*) and add them into the handlebox. The artists needs to +be scaled according to the fontsize (note that the size is in pixel, +i.e., this is dpi-scaled value). + +This module includes definition of several legend handler classes +derived from the base class (HandlerBase) with a following method. + + def __call__(self, legend, orig_handle, + fontsize, + handlebox): + + +""" + +import numpy as np + +from matplotlib.lines import Line2D +from matplotlib.patches import Rectangle +import matplotlib.collections as mcoll +# from matplotlib.collections import LineCollection, RegularPolyCollection, \ +# CircleCollection + +def update_from_first_child(tgt, src): + tgt.update_from(src.get_children()[0]) + + +class HandlerBase(object): + """ + A Base class for default legend handlers. + + The derived classes are meant to override *create_artists* method, which + has a following signatture.:: + + def create_artists(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize, + trans): + + The overriden method needs to create artists of the given + transform that fits in the given dimension (xdescent, ydescemt, + width, height) that are scaled by fontsize if necessary. + + """ + def __init__(self, xpad=0., ypad=0., update_func=None): + self._xpad, self._ypad = xpad, ypad + self._update_prop_func = update_func + + def _update_prop(self, legend_handle, orig_handle): + if self._update_prop_func is None: + self._default_update_prop(legend_handle, orig_handle) + else: + self._update_prop_func(legend_handle, orig_handle) + + def _default_update_prop(self, legend_handle, orig_handle): + legend_handle.update_from(orig_handle) + + + def update_prop(self, legend_handle, orig_handle, legend): + + self._update_prop(legend_handle, orig_handle) + + legend._set_artist_props(legend_handle) + legend_handle.set_clip_box(None) + legend_handle.set_clip_path(None) + + # make usre that transform is not set since they will be set + # when added to an handlerbox. + legend_handle._transformSet = False + + def adjust_drawing_area(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize, + ): + xdescent = xdescent-self._xpad*fontsize + ydescent = ydescent-self._ypad*fontsize + width = width-self._xpad*fontsize + height = height-self._ypad*fontsize + return xdescent, ydescent, width, height + + def __call__(self, legend, orig_handle, + fontsize, + handlebox): + """ + x, y, w, h in display coordinate w/ default dpi (72) + fontsize in points + """ + + width, height, xdescent, ydescent = handlebox.width, \ + handlebox.height, \ + handlebox.xdescent, \ + handlebox.ydescent + + xdescent, ydescent, width, height = \ + self.adjust_drawing_area(legend, orig_handle, + xdescent, ydescent, width, height, + fontsize) + + a_list = self.create_artists(legend, orig_handle, + xdescent, ydescent, width, height, fontsize, + handlebox.get_transform()) + + # create_artists will return a list of artists. + for a in a_list: + handlebox.add_artist(a) + + # we only return the first artist + return a_list[0] + + + def create_artists(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize, + trans): + + raise NotImplementedError('Derived must override') + + +class HandlerLine2D(HandlerBase): + """ + Handler for Line2D instances + """ + def __init__(self, marker_pad=0.3, numpoints=None, **kw): + HandlerBase.__init__(self, **kw) + self._marker_pad = marker_pad + self._numpoints = numpoints + + def get_numpoints(self, legend): + if self._numpoints is None: + return legend.numpoints + else: + return self._numpoints + + def get_xdata(self, legend, xdescent, ydescent, width, height, fontsize): + numpoints = self.get_numpoints(legend) + + if numpoints > 1: + # we put some pad here to compensate the size of the + # marker + xdata = np.linspace(-xdescent+self._marker_pad*fontsize, + width-self._marker_pad*fontsize, + numpoints) + xdata_marker = xdata + elif numpoints == 1: + xdata = np.linspace(-xdescent, width, 2) + xdata_marker = [0.5*width-0.5*xdescent] + + return xdata, xdata_marker + + + + def create_artists(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize, + trans): + + xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent, + width, height, fontsize) + + ydata = ((height-ydescent)/2.)*np.ones(xdata.shape, float) + legline = Line2D(xdata, ydata) + + self.update_prop(legline, orig_handle, legend) + #legline.update_from(orig_handle) + #legend._set_artist_props(legline) # after update + #legline.set_clip_box(None) + #legline.set_clip_path(None) + legline.set_drawstyle('default') + legline.set_marker('None') + + + legline_marker = Line2D(xdata_marker, ydata[:len(xdata_marker)]) + self.update_prop(legline_marker, orig_handle, legend) + #legline_marker.update_from(orig_handle) + #legend._set_artist_props(legline_marker) + #legline_marker.set_clip_box(None) + #legline_marker.set_clip_path(None) + legline_marker.set_linestyle('None') + if legend.markerscale !=1: + newsz = legline_marker.get_markersize()*legend.markerscale + legline_marker.set_markersize(newsz) + # we don't want to add this to the return list because + # the texts and handles are assumed to be in one-to-one + # correpondence. + legline._legmarker = legline_marker + + return [legline, legline_marker] + + + +class HandlerPatch(HandlerBase): + """ + Handler for Patches + """ + def __init__(self, patch_func=None, **kw): + HandlerBase.__init__(self, **kw) + + self._patch_func = patch_func + + def _create_patch(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize): + if self._patch_func is None: + p = Rectangle(xy=(-xdescent, -ydescent), + width = width+xdescent, height=(height+ydescent)) + else: + p = self._patch_func(legend=legend, orig_handle=orig_handle, + xdescent=xdescent, ydescent=ydescent, + width=width, height=height, fontsize=fontsize) + + return p + + def create_artists(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize, trans): + + p = self._create_patch(legend, orig_handle, + xdescent, ydescent, width, height, fontsize) + + self.update_prop(p, orig_handle, legend) + + return [p] + + + +class HandlerLineCollection(HandlerLine2D): + """ + Handler for LineCollections + """ + + def get_numpoints(self, legend): + if self._numpoints is None: + return legend.scatterpoints + else: + return self._numpoints + + def _default_update_prop(self, legend_handle, orig_handle): + lw = orig_handle.get_linewidth()[0] + dashes = orig_handle.get_dashes()[0] + color = orig_handle.get_colors()[0] + legend_handle.set_color(color) + legend_handle.set_linewidth(lw) + if dashes[0] is not None: # dashed line + legend_handle.set_dashes(dashes[1]) + + + def create_artists(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize, trans): + + xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent, + width, height, fontsize) + ydata = ((height-ydescent)/2.)*np.ones(xdata.shape, float) + legline = Line2D(xdata, ydata) + + self.update_prop(legline, orig_handle, legend) + + return [legline] + + + +class HandlerRegularPolyCollection(HandlerLine2D): + """ + Handler for RegularPolyCollections. + """ + def __init__(self, scatteryoffsets=None, sizes=None, **kw): + HandlerLine2D.__init__(self, **kw) + + self._scatteryoffsets = scatteryoffsets + self._sizes = sizes + + def get_numpoints(self, legend): + if self._numpoints is None: + return legend.scatterpoints + else: + return self._numpoints + + def get_ydata(self, legend, xdescent, ydescent, width, height, fontsize): + if self._scatteryoffsets is None: + ydata = height*legend._scatteryoffsets + else: + ydata = height*np.asarray(self._scatteryoffsets) + + return ydata + + def get_sizes(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize): + if self._sizes is None: + size_max = max(orig_handle.get_sizes())*legend.markerscale**2 + size_min = min(orig_handle.get_sizes())*legend.markerscale**2 + + numpoints = self.get_numpoints(legend) + if numpoints < 4: + sizes = [.5*(size_max+size_min), size_max, + size_min] + else: + sizes = (size_max-size_min)*np.linspace(0,1,numpoints)+size_min + else: + sizes = self._sizes #[:legend.scatterpoints] + + return sizes + + def update_prop(self, legend_handle, orig_handle, legend): + + self._update_prop(legend_handle, orig_handle) + + legend_handle.set_figure(legend.figure) + #legend._set_artist_props(legend_handle) + legend_handle.set_clip_box(None) + legend_handle.set_clip_path(None) + + def create_collection(self, orig_handle, sizes, offsets, transOffset): + p = type(orig_handle)(orig_handle.get_numsides(), + rotation=orig_handle.get_rotation(), + sizes=sizes, + offsets=offsets, + transOffset=transOffset, + ) + return p + + def create_artists(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize, + trans): + + + xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent, + width, height, fontsize) + + + ydata = self.get_ydata(legend, xdescent, ydescent, + width, height, fontsize) + + sizes = self.get_sizes(legend, orig_handle, xdescent, ydescent, + width, height, fontsize) + + p = self.create_collection(orig_handle, sizes, + offsets=zip(xdata_marker,ydata), + transOffset=trans) + + self.update_prop(p, orig_handle, legend) + + p._transOffset = trans + p.set_transform(None) + + return [p] + + +class HandlerCircleCollection(HandlerRegularPolyCollection): + """ + Handler for CircleCollections + """ + def create_collection(self, orig_handle, sizes, offsets, transOffset): + p = type(orig_handle)(sizes, + offsets=offsets, + transOffset=transOffset, + ) + return p + + +class HandlerErrorbar(HandlerLine2D): + """ + Handler for Errorbars + """ + def __init__(self, xerr_size=0.5, yerr_size=None, + marker_pad=0.3, numpoints=None, **kw): + + self._xerr_size = xerr_size + self._yerr_size = yerr_size + + HandlerLine2D.__init__(self, marker_pad=marker_pad, numpoints=numpoints, + **kw) + + def get_err_size(self, legend, xdescent, ydescent, width, height, fontsize): + xerr_size = self._xerr_size*fontsize + + if self._yerr_size is None: + yerr_size = xerr_size + else: + yerr_size = self._yerr_size*fontsize + + return xerr_size, yerr_size + + + + def create_artists(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize, + trans): + + plotlines, caplines, barlinecols = orig_handle + + xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent, + width, height, fontsize) + + ydata = ((height-ydescent)/2.)*np.ones(xdata.shape, float) + legline = Line2D(xdata, ydata) + + self.update_prop(legline, plotlines, legend) + legline.set_drawstyle('default') + legline.set_marker('None') + + + xdata_marker = np.asarray(xdata_marker) + ydata_marker = np.asarray(ydata[:len(xdata_marker)]) + + xerr_size, yerr_size = self.get_err_size(legend, xdescent, ydescent, + width, height, fontsize) + + + legline_marker = Line2D(xdata_marker, ydata_marker) + self.update_prop(legline_marker, plotlines, legend) + legline_marker.set_linestyle('None') + if legend.markerscale !=1: + newsz = legline_marker.get_markersize()*legend.markerscale + legline_marker.set_markersize(newsz) + + + handle_barlinecols = [] + handle_caplines = [] + + if orig_handle.has_xerr: + verts = [ ((x-xerr_size, y), (x+xerr_size, y)) + for x,y in zip(xdata_marker, ydata_marker)] + coll = mcoll.LineCollection(verts) + self.update_prop(coll, barlinecols[0], legend) + handle_barlinecols.append(coll) + + if caplines: + capline_left = Line2D(xdata_marker-xerr_size, ydata_marker) + capline_right = Line2D(xdata_marker+xerr_size, ydata_marker) + self.update_prop(capline_left, caplines[0], legend) + self.update_prop(capline_right, caplines[0], legend) + capline_left.set_marker("|") + capline_right.set_marker("|") + + handle_caplines.append(capline_left) + handle_caplines.append(capline_right) + + if orig_handle.has_yerr: + verts = [ ((x, y-yerr_size), (x, y+yerr_size)) + for x,y in zip(xdata_marker, ydata_marker)] + coll = mcoll.LineCollection(verts) + self.update_prop(coll, barlinecols[0], legend) + handle_barlinecols.append(coll) + + if caplines: + capline_left = Line2D(xdata_marker, ydata_marker-yerr_size) + capline_right = Line2D(xdata_marker, ydata_marker+yerr_size) + self.update_prop(capline_left, caplines[0], legend) + self.update_prop(capline_right, caplines[0], legend) + capline_left.set_marker("_") + capline_right.set_marker("_") + + handle_caplines.append(capline_left) + handle_caplines.append(capline_right) + + artists = [] + artists.extend(handle_barlinecols) + artists.extend(handle_caplines) + artists.append(legline_marker) + artists.append(legline) + + return artists + + + +class HandlerTuple(HandlerBase): + """ + Handler for Tuple + """ + def __init__(self, **kwargs): + HandlerBase.__init__(self, **kwargs) + + #self._handle_list = handle_list + + def create_artists(self, legend, orig_handle, + xdescent, ydescent, width, height, fontsize, + trans): + + handler_map = legend.get_legend_handler_map() + a_list = [] + for handle1 in orig_handle: + handler = legend.get_legend_handler(handler_map, handle1) + _a_list = handler.create_artists(legend, handle1, + xdescent, ydescent, width, height, + fontsize, + trans) + a_list.extend(_a_list) + + return a_list diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 203e29ffcaa4..75ed92702632 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -248,6 +248,7 @@ def __init__(self, xdata, ydata, # chance to init axes (and hence unit support) self.update(kwargs) self.pickradius = pickradius + self.ind_offset = 0 if is_numlike(self._picker): self.pickradius = self._picker @@ -283,6 +284,8 @@ def contains(self, mouseevent): if len(self._xy)==0: return False,{} # Convert points to pixels + if self._transformed_path is None: + self._transform_path() path, affine = self._transformed_path.get_transformed_path_and_affine() path = affine.transform_path(path) xy = path.vertices @@ -756,44 +759,44 @@ def set_marker(self, marker): """ Set the line marker - ========== ========================== - marker description - ========== ========================== - ``'.'`` point - ``','`` pixel - ``'o'`` circle - ``'v'`` triangle_down - ``'^'`` triangle_up - ``'<'`` triangle_left - ``'>'`` triangle_right - ``'1'`` tri_down - ``'2'`` tri_up - ``'3'`` tri_left - ``'4'`` tri_right - ``'s'`` square - ``'p'`` pentagon - ``'*'`` star - ``'h'`` hexagon1 - ``'H'`` hexagon2 - ``'+'`` plus - ``'x'`` x - ``'D'`` diamond - ``'d'`` thin_diamond - ``'|'`` vline - ``'_'`` hline - TICKLEFT tickleft - TICKRIGHT tickright - TICKUP tickup - TICKDOWN tickdown - CARETLEFT caretleft - CARETRIGHT caretright - CARETUP caretup - CARETDOWN caretdown - ``'None'`` nothing - ``' '`` nothing - ``''`` nothing - '$...$' render the string using mathtext - ========== ========================== + =========== =================================== + marker description + =========== =================================== + ``'.'`` point + ``','`` pixel + ``'o'`` circle + ``'v'`` triangle_down + ``'^'`` triangle_up + ``'<'`` triangle_left + ``'>'`` triangle_right + ``'1'`` tri_down + ``'2'`` tri_up + ``'3'`` tri_left + ``'4'`` tri_right + ``'s'`` square + ``'p'`` pentagon + ``'*'`` star + ``'h'`` hexagon1 + ``'H'`` hexagon2 + ``'+'`` plus + ``'x'`` x + ``'D'`` diamond + ``'d'`` thin_diamond + ``'|'`` vline + ``'_'`` hline + TICKLEFT tickleft + TICKRIGHT tickright + TICKUP tickup + TICKDOWN tickdown + CARETLEFT caretleft + CARETRIGHT caretright + CARETUP caretup + CARETDOWN caretdown + ``'None'`` nothing + ``' '`` nothing + ``''`` nothing + ``'$...$'`` render the string using mathtext + =========== =================================== @@ -805,7 +808,7 @@ def set_marker(self, marker): | ``'x'`` | ``'|'`` | TICKUP | TICKDOWN | TICKLEFT | TICKRIGHT | CARETUP | CARETDOWN | CARETLEFT | CARETRIGHT - | ``'None'`` | ``' '`` | ``''`` | '$...$'] + | ``'None'`` | ``' '`` | ``''`` | ``'$...$'`` ] """ if marker in self._markers: diff --git a/lib/matplotlib/mathtext.py b/lib/matplotlib/mathtext.py index b7b7a7a5d609..4fe2b3f87d1a 100644 --- a/lib/matplotlib/mathtext.py +++ b/lib/matplotlib/mathtext.py @@ -329,10 +329,9 @@ def __init__(self): def render_glyph(self, ox, oy, info): oy = self.height - oy + info.offset - thetext = unichr_safe(info.num) self.svg_glyphs.append( - (info.font, info.fontsize, thetext, ox, oy, info.metrics)) + (info.font, info.fontsize, info.num, ox, oy, info.metrics)) def render_rect_filled(self, x1, y1, x2, y2): self.svg_rects.append( @@ -2254,7 +2253,6 @@ def __init__(self): | Error(r"Expected \genfrac{ldelim}{rdelim}{rulesize}{style}{num}{den}")) ).setParseAction(self.genfrac).setName("genfrac") - sqrt = Group( Suppress(Literal(r"\sqrt")) + Optional( @@ -2266,6 +2264,11 @@ def __init__(self): + (group | Error("Expected \sqrt{value}")) ).setParseAction(self.sqrt).setName("sqrt") + overline = Group( + Suppress(Literal(r"\overline")) + + (group | Error("Expected \overline{value}")) + ).setParseAction(self.overline).setName("overline") + placeable <<(function ^ (c_over_c | symbol) ^ accent @@ -2275,6 +2278,7 @@ def __init__(self): ^ binom ^ genfrac ^ sqrt + ^ overline ) simple <<(space @@ -2850,6 +2854,31 @@ def sqrt(self, s, loc, toks): rightside]) # Body return [hlist] + def overline(self, s, loc, toks): + assert(len(toks)==1) + assert(len(toks[0])==1) + + body = toks[0][0] + + state = self.get_state() + thickness = state.font_output.get_underline_thickness( + state.font, state.fontsize, state.dpi) + + height = body.height - body.shift_amount + thickness * 3.0 + depth = body.depth + body.shift_amount + + # Place overline above body + rightside = Vlist([Hrule(state), + Fill(), + Hlist([body])]) + + # Stretch the glue between the hrule and the body + rightside.vpack(height + (state.fontsize * state.dpi) / (100.0 * 12.0), + depth, 'exactly') + + hlist = Hlist([rightside]) + return [hlist] + def _auto_sized_delimiter(self, front, middle, back): state = self.get_state() height = max([x.height for x in middle]) diff --git a/lib/matplotlib/mlab.py b/lib/matplotlib/mlab.py index 5c757bd5a667..305ec6356e2d 100644 --- a/lib/matplotlib/mlab.py +++ b/lib/matplotlib/mlab.py @@ -212,10 +212,11 @@ def _spectral_helper(x, y, NFFT=256, Fs=2, detrend=detrend_none, #Make sure we're dealing with a numpy array. If y and x were the same #object to start with, keep them that way - x = np.asarray(x) if not same_data: y = np.asarray(y) + else: + y = x # zero pad x and y up to NFFT if they are shorter than NFFT if len(x)1 and M>1 are returned as a 2d array. + + - if only one subplot is constructed (nrows=ncols=1), the + resulting single Axis object is returned as a scalar. + + - for Nx1 or 1xN subplots, the returned object is a 1-d numpy + object array of Axis objects are returned as numpy 1-d + arrays. + + - for NxM subplots with N>1 and M>1 are returned as a 2d + array. If False, no squeezing at all is done: the returned axis object is always a 2-d array contaning Axis instances, even if it ends up being 1x1. @@ -712,10 +720,13 @@ def subplots(nrows=1, ncols=1, sharex=False, sharey=False, squeeze=True, Returns: fig, ax : tuple + - fig is the Matplotlib Figure object - - ax can be either a single axis object or an array of axis objects if - more than one supblot was created. The dimensions of the resulting array - can be controlled with the squeeze keyword, see above. + + - ax can be either a single axis object or an array of axis + objects if more than one supblot was created. The dimensions + of the resulting array can be controlled with the squeeze + keyword, see above. **Examples:** @@ -760,17 +771,37 @@ def subplots(nrows=1, ncols=1, sharex=False, sharey=False, squeeze=True, for i in range(1, nplots): axarr[i] = fig.add_subplot(nrows, ncols, i+1, **subplot_kw) + + + # returned axis array will be always 2-d, even if nrows=ncols=1 + axarr = axarr.reshape(nrows, ncols) + + + # turn off redundant tick labeling + if sharex and nrows>1: + # turn off all but the bottom row + for ax in axarr[:-1,:].flat: + for label in ax.get_xticklabels(): + label.set_visible(False) + + + if sharey and ncols>1: + # turn off all but the first column + for ax in axarr[:,1:].flat: + for label in ax.get_yticklabels(): + label.set_visible(False) + if squeeze: # Reshape the array to have the final desired dimension (nrow,ncol), # though discarding unneeded dimensions that equal 1. If we only have # one subplot, just return it instead of a 1-element array. if nplots==1: - return fig, axarr[0] + ret = fig, axarr[0,0] else: - return fig, axarr.reshape(nrows, ncols).squeeze() - else: - # returned axis array will be always 2-d, even if nrows=ncols=1 - return fig, axarr.reshape(nrows, ncols) + ret = fig, axarr.squeeze() + + + return ret from gridspec import GridSpec @@ -1107,9 +1138,8 @@ def xscale(*args, **kwargs): %(scale_docs)s """ ax = gca() - ret = ax.set_xscale(*args, **kwargs) + ax.set_xscale(*args, **kwargs) draw_if_interactive() - return ret @docstring.dedent_interpd def yscale(*args, **kwargs): @@ -1125,9 +1155,8 @@ def yscale(*args, **kwargs): %(scale_docs)s """ ax = gca() - ret = ax.set_yscale(*args, **kwargs) + ax.set_yscale(*args, **kwargs) draw_if_interactive() - return ret def xticks(*args, **kwargs): """ diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index f318882653cb..f3198d862c7f 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -107,17 +107,6 @@ def validate_backend(s): else: return _validate_standard_backends(s) -def validate_numerix(v): - # 2009/02/24: start warning; later, remove all traces - try: - if v == 'obsolete': - return v - except ValueError: - pass - warnings.warn('rcParams key "numerix" is obsolete and has no effect;\n' - ' please delete it from your matplotlibrc file') - - validate_toolbar = ValidateInStrings('toolbar',[ 'None','classic','toolbar2', ], ignorecase=True) @@ -347,8 +336,6 @@ def __call__(self, s): defaultParams = { 'backend' : ['Agg', validate_backend], # agg is certainly present 'backend_fallback' : [True, validate_bool], # agg is certainly present - #'numerix' : ['obsolete', validate_numerix], - #'maskedarray' : ['obsolete', validate_maskedarray], #to be removed 'toolbar' : ['toolbar2', validate_toolbar], 'datapath' : [None, validate_path_exists], # handled by _get_data_path_cached 'units' : [False, validate_bool], diff --git a/lib/matplotlib/scale.py b/lib/matplotlib/scale.py index 4e85632061b7..adbbf20e3209 100644 --- a/lib/matplotlib/scale.py +++ b/lib/matplotlib/scale.py @@ -6,6 +6,7 @@ from cbook import dedent from ticker import NullFormatter, ScalarFormatter, LogFormatterMathtext, Formatter from ticker import NullLocator, LogLocator, AutoLocator, SymmetricalLogLocator, FixedLocator +from ticker import is_decade from transforms import Transform, IdentityTransform from matplotlib import docstring @@ -318,19 +319,22 @@ class SymmetricalLogTransform(Transform): def __init__(self, base, linthresh): Transform.__init__(self) self.base = base - self.linthresh = linthresh + self.linthresh = abs(linthresh) self._log_base = np.log(base) - self._linadjust = (np.log(linthresh) / self._log_base) / linthresh + self._logb_linthresh = np.log(linthresh) / self._log_base + self._logb_minlog = np.floor(self._logb_linthresh - 1e-10) + self._linadjust = np.abs((np.log(linthresh) - self._logb_minlog) / + linthresh) def transform(self, a): a = np.asarray(a) sign = np.sign(a) masked = ma.masked_inside(a, -self.linthresh, self.linthresh, copy=False) - log = sign * ma.log(np.abs(masked)) / self._log_base + log = sign * (ma.log(np.abs(masked)) / self._log_base - self._logb_minlog) if masked.mask.any(): return np.asarray(ma.where(masked.mask, - a * self._linadjust, - log)) + a * self._linadjust, + log)) else: return np.asarray(log) diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index cd23a1c2de15..9da6c795202d 100644 --- a/lib/matplotlib/sphinxext/plot_directive.py +++ b/lib/matplotlib/sphinxext/plot_directive.py @@ -1,8 +1,13 @@ -"""A special directive for including a matplotlib plot. +""" +A directive for including a matplotlib plot in a Sphinx document. + +By default, in HTML output, `plot` will include a .png file with a +link to a high-res .png and .pdf. In LaTeX output, it will include a +.pdf. -The source code for the plot may be included in one of two ways: +The source code for the plot may be included in one of three ways: - 1. A path to a source file as the argument to the directive:: + 1. **A path to a source file** as the argument to the directive:: .. plot:: path/to/plot.py @@ -18,69 +23,97 @@ .. plot:: path/to/plot.py plot_function1 - 2. Included as inline content to the directive:: + 2. Included as **inline content** to the directive:: + + .. plot:: + + import matplotlib.pyplot as plt + import matplotlib.image as mpimg + import numpy as np + img = mpimg.imread('_static/stinkbug.png') + imgplot = plt.imshow(img) + + 3. Using **doctest** syntax:: + + .. plot:: + A plotting example: + >>> import matplotlib.pyplot as plt + >>> plt.plot([1,2,3], [4,5,6]) + +Options +------- + +The ``plot`` directive supports the following options: + + format : {'python', 'doctest'} + Specify the format of the input + + include-source : bool + Whether to display the source code. The default can be changed + using the `plot_include_source` variable in conf.py + + encoding : str + If this source file is in a non-UTF8 or non-ASCII encoding, + the encoding must be specified using the `:encoding:` option. + The encoding will not be inferred using the ``-*- coding -*-`` + metacomment. + + context : bool + If provided, the code will be run in the context of all + previous plot directives for which the `:context:` option was + specified. This only applies to inline code plot directives, + not those run from files. + + nofigs : bool + If specified, the code block will be run, but no figures will + be inserted. This is usually useful with the ``:context:`` + option. - .. plot:: +Additionally, this directive supports all of the options of the +`image` directive, except for `target` (since plot will add its own +target). These include `alt`, `height`, `width`, `scale`, `align` and +`class`. - import matplotlib.pyplot as plt - import matplotlib.image as mpimg - import numpy as np - img = mpimg.imread('_static/stinkbug.png') - imgplot = plt.imshow(img) +Configuration options +--------------------- -In HTML output, `plot` will include a .png file with a link to a high-res -.png and .pdf. In LaTeX output, it will include a .pdf. +The plot directive has the following configuration options: -To customize the size of the plot, this directive supports all of the -options of the `image` directive, except for `target` (since plot will -add its own target). These include `alt`, `height`, `width`, `scale`, -`align` and `class`. + plot_include_source + Default value for the include-source option -Additionally, if the `:include-source:` option is provided, the -literal source will be displayed inline in the text, (as well as a -link to the source in HTML). If this source file is in a non-UTF8 or -non-ASCII encoding, the encoding must be specified using the -`:encoding:` option. + plot_pre_code + Code that should be executed before each plot. -If the `:context:` option is plotted, the code will be run in the -context of all previous plot directives for which the context option -was specified. This only applies to inline code plot directives, not -those run from files. + plot_basedir + Base directory, to which ``plot::`` file names are relative + to. (If None or empty, file names are relative to the + directoly where the file containing the directive is.) -If the ``:nofigs:`` option is specified, the code block will be run, -but no figures will be inserted. This is usually useful with the -``:context:`` option. + plot_formats + File formats to generate. List of tuples or strings:: -The set of file formats to generate can be specified with the -`plot_formats` configuration variable. + [(suffix, dpi), suffix, ...] + that determine the file format and the DPI. For entries whose + DPI was omitted, sensible defaults are chosen. -Error handling: + plot_html_show_formats + Whether to show links to the files in HTML. -Any errors generated during the running of the code are emitted as warnings -using the Python `warnings` module, using a custom category called -`PlotWarning`. To turn the warnings into fatal errors that stop the -documentation build, after adjusting your `sys.path` in your `conf.py` Sphinx -configuration file, use:: + plot_rcparams + A dictionary containing any non-standard rcParams that should + be applied before each plot. - import plot_directive - warnings.simplefilter('error', plot_directive.PlotWarning) """ -import sys, os, shutil, imp, warnings, cStringIO, re -try: - from hashlib import md5 -except ImportError: - from md5 import md5 +import sys, os, glob, shutil, imp, warnings, cStringIO, re, textwrap, \ + traceback, exceptions from docutils.parsers.rst import directives -try: - # docutils 0.4 - from docutils.parsers.rst.directives.images import align -except ImportError: - # docutils 0.5 - from docutils.parsers.rst.directives.images import Image - align = Image.align +from docutils import nodes +from docutils.parsers.rst.directives.images import Image +align = Image.align import sphinx sphinx_version = sphinx.__version__.split(".") @@ -89,88 +122,276 @@ sphinx_version = tuple([int(re.split('[a-z]', x)[0]) for x in sphinx_version[:2]]) +try: + # Sphinx depends on either Jinja or Jinja2 + import jinja2 + def format_template(template, **kw): + return jinja2.Template(template).render(**kw) +except ImportError: + import jinja + def format_template(template, **kw): + return jinja.from_string(template, **kw) + import matplotlib import matplotlib.cbook as cbook matplotlib.use('Agg') import matplotlib.pyplot as plt -import matplotlib.image as image from matplotlib import _pylab_helpers -from matplotlib.sphinxext import only_directives +__version__ = 2 + +#------------------------------------------------------------------------------ +# Relative pathnames +#------------------------------------------------------------------------------ + +# os.path.relpath is new in Python 2.6 +try: + from os.path import relpath +except ImportError: + # Copied from Python 2.7 + if 'posix' in sys.builtin_module_names: + def relpath(path, start=os.path.curdir): + """Return a relative version of a path""" + from os.path import sep, curdir, join, abspath, commonprefix, \ + pardir + + if not path: + raise ValueError("no path specified") + + start_list = abspath(start).split(sep) + path_list = abspath(path).split(sep) + + # Work out how much of the filepath is shared by start and path. + i = len(commonprefix([start_list, path_list])) + + rel_list = [pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return curdir + return join(*rel_list) + elif 'nt' in sys.builtin_module_names: + def relpath(path, start=os.path.curdir): + """Return a relative version of a path""" + from os.path import sep, curdir, join, abspath, commonprefix, \ + pardir, splitunc + + if not path: + raise ValueError("no path specified") + start_list = abspath(start).split(sep) + path_list = abspath(path).split(sep) + if start_list[0].lower() != path_list[0].lower(): + unc_path, rest = splitunc(path) + unc_start, rest = splitunc(start) + if bool(unc_path) ^ bool(unc_start): + raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" + % (path, start)) + else: + raise ValueError("path is on drive %s, start on drive %s" + % (path_list[0], start_list[0])) + # Work out how much of the filepath is shared by start and path. + for i in range(min(len(start_list), len(path_list))): + if start_list[i].lower() != path_list[i].lower(): + break + else: + i += 1 + + rel_list = [pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return curdir + return join(*rel_list) + else: + raise RuntimeError("Unsupported platform (no relpath available!)") -class PlotWarning(Warning): - """Warning category for all warnings generated by this directive. +#------------------------------------------------------------------------------ +# Registration hook +#------------------------------------------------------------------------------ - By printing our warnings with this category, it becomes possible to turn - them into errors by using in your conf.py:: +def plot_directive(name, arguments, options, content, lineno, + content_offset, block_text, state, state_machine): + return run(arguments, content, options, state_machine, state, lineno) +plot_directive.__doc__ = __doc__ + +def _option_boolean(arg): + if not arg or not arg.strip(): + # no argument given, assume used as a flag + return True + elif arg.strip().lower() in ('no', '0', 'false'): + return False + elif arg.strip().lower() in ('yes', '1', 'true'): + return True + else: + raise ValueError('"%s" unknown boolean' % arg) + +def _option_format(arg): + return directives.choice(arg, ('python', 'doctest')) - warnings.simplefilter('error', plot_directive.PlotWarning) +def _option_align(arg): + return directives.choice(arg, ("top", "middle", "bottom", "left", "center", + "right")) - This way, you can ensure that your docs only build if all your examples - actually run successfully. +def mark_plot_labels(app, document): """ - pass + To make plots referenceable, we need to move the reference from + the "htmlonly" (or "latexonly") node to the actual figure node + itself. + """ + for name, explicit in document.nametypes.iteritems(): + if not explicit: + continue + labelid = document.nameids[name] + if labelid is None: + continue + node = document.ids[labelid] + if node.tagname in ('html_only', 'latex_only'): + for n in node: + if n.tagname == 'figure': + sectname = name + for c in n: + if c.tagname == 'caption': + sectname = c.astext() + break + node['ids'].remove(labelid) + node['names'].remove(name) + n['ids'].append(labelid) + n['names'].append(name) + document.settings.env.labels[name] = \ + document.settings.env.docname, labelid, sectname + break -# os.path.relpath is new in Python 2.6 -if hasattr(os.path, 'relpath'): - relpath = os.path.relpath -else: - # This code is snagged from Python 2.6 - - def relpath(target, base=os.curdir): - """ - Return a relative path to the target from either the current dir or an optional base dir. - Base can be a directory specified either as absolute or relative to current dir. - """ - - if not os.path.exists(target): - raise OSError, 'Target does not exist: '+target - - if not os.path.isdir(base): - raise OSError, 'Base is not a directory or does not exist: '+base - - base_list = (os.path.abspath(base)).split(os.sep) - target_list = (os.path.abspath(target)).split(os.sep) - - # On the windows platform the target may be on a completely - # different drive from the base. - if os.name in ['nt','dos','os2'] and base_list[0] <> target_list[0]: - raise OSError, 'Target is on a different drive to base. Target: '+target_list[0].upper()+', base: '+base_list[0].upper() - - # Starting from the filepath root, work out how much of the - # filepath is shared by base and target. - for i in range(min(len(base_list), len(target_list))): - if base_list[i] <> target_list[i]: break - else: - # If we broke out of the loop, i is pointing to the first - # differing path elements. If we didn't break out of the - # loop, i is pointing to identical path elements. - # Increment i so that in all cases it points to the first - # differing path elements. - i+=1 - - rel_list = [os.pardir] * (len(base_list)-i) + target_list[i:] - if rel_list: - return os.path.join(*rel_list) - else: - return "" +def setup(app): + setup.app = app + setup.config = app.config + setup.confdir = app.confdir -template = """ -.. htmlonly:: + options = {'alt': directives.unchanged, + 'height': directives.length_or_unitless, + 'width': directives.length_or_percentage_or_unitless, + 'scale': directives.nonnegative_int, + 'align': _option_align, + 'class': directives.class_option, + 'include-source': _option_boolean, + 'format': _option_format, + 'context': directives.flag, + 'nofigs': directives.flag, + 'encoding': directives.encoding + } + + app.add_directive('plot', plot_directive, True, (0, 2, False), **options) + app.add_config_value('plot_pre_code', None, True) + app.add_config_value('plot_include_source', False, True) + app.add_config_value('plot_formats', ['png', 'hires.png', 'pdf'], True) + app.add_config_value('plot_basedir', None, True) + app.add_config_value('plot_html_show_formats', True, True) + app.add_config_value('plot_rcparams', {}, True) + + app.connect('doctree-read', mark_plot_labels) + +#------------------------------------------------------------------------------ +# Doctest handling +#------------------------------------------------------------------------------ - %(links)s +def contains_doctest(text): + try: + # check if it's valid Python as-is + compile(text, '', 'exec') + return False + except SyntaxError: + pass + r = re.compile(r'^\s*>>>', re.M) + m = r.search(text) + return bool(m) + +def unescape_doctest(text): + """ + Extract code from a piece of text, which contains either Python code + or doctests. + + """ + if not contains_doctest(text): + return text + + code = "" + for line in text.split("\n"): + m = re.match(r'^\s*(>>>|\.\.\.) (.*)$', line) + if m: + code += m.group(2) + "\n" + elif line.strip(): + code += "# " + line.strip() + "\n" + else: + code += "\n" + return code - .. figure:: %(prefix)s%(tmpdir)s/%(outname)s.png -%(options)s +def split_code_at_show(text): + """ + Split code at plt.show() -%(caption)s + """ -.. latexonly:: - .. figure:: %(prefix)s%(tmpdir)s/%(outname)s.pdf -%(options)s + parts = [] + is_doctest = contains_doctest(text) -%(caption)s + part = [] + for line in text.split("\n"): + if (not is_doctest and line.strip() == 'plt.show()') or \ + (is_doctest and line.strip() == '>>> plt.show()'): + part.append(line) + parts.append("\n".join(part)) + part = [] + else: + part.append(line) + if "\n".join(part).strip(): + parts.append("\n".join(part)) + return parts + +#------------------------------------------------------------------------------ +# Template +#------------------------------------------------------------------------------ + + +TEMPLATE = """ +{{ source_code }} + +{{ only_html }} + + {% if source_link or (html_show_formats and not multi_image) %} + ( + {%- if source_link -%} + `Source code <{{ source_link }}>`__ + {%- endif -%} + {%- if html_show_formats and not multi_image -%} + {%- for img in images -%} + {%- for fmt in img.formats -%} + {%- if source_link or not loop.first -%}, {% endif -%} + `{{ fmt }} <{{ dest_dir }}/{{ img.basename }}.{{ fmt }}>`__ + {%- endfor -%} + {%- endfor -%} + {%- endif -%} + ) + {% endif %} + + {% for img in images %} + .. figure:: {{ build_dir }}/{{ img.basename }}.png + {%- for option in options %} + {{ option }} + {% endfor %} + + {% if html_show_formats and multi_image -%} + ( + {%- for fmt in img.formats -%} + {%- if not loop.first -%}, {% endif -%} + `{{ fmt }} <{{ dest_dir }}/{{ img.basename }}.{{ fmt }}>`__ + {%- endfor -%} + ) + {%- endif -%} + + {{ caption }} + {% endfor %} + +{{ only_latex }} + + {% for img in images %} + .. image:: {{ build_dir }}/{{ img.basename }}.pdf + {% endfor %} """ @@ -183,12 +404,22 @@ def relpath(target, base=os.curdir): """ -template_content_indent = ' ' - # the context of the plot for all directives specified with the # :context: option plot_context = dict() +class ImageFile(object): + def __init__(self, basename, dirname): + self.basename = basename + self.dirname = dirname + self.formats = [] + + def filename(self, format): + return os.path.join(self.dirname, "%s.%s" % (self.basename, format)) + + def filenames(self): + return [self.filename(fmt) for fmt in self.formats] + def out_of_date(original, derived): """ Returns True if derivative is out-of-date wrt original, @@ -198,7 +429,10 @@ def out_of_date(original, derived): (os.path.exists(original) and os.stat(derived).st_mtime < os.stat(original).st_mtime)) -def run_code(plot_path, function_name, plot_code, context=False): +class PlotError(RuntimeError): + pass + +def run_code(code, code_path, ns=None, function_name=None): """ Import a Python module from a path, and run the function given by name, if function_name is not None. @@ -208,269 +442,172 @@ def run_code(plot_path, function_name, plot_code, context=False): # it can get at its data files, if any. Add its path to sys.path # so it can import any helper modules sitting beside it. - if plot_code is not None: - exec_code = 'import numpy as np; import matplotlib.pyplot as plt\n%s'%plot_code - #print 'CONTEXT', context, plot_context, exec_code - if context: - exec(exec_code, None, plot_context) - else: - exec(exec_code) + pwd = os.getcwd() + old_sys_path = list(sys.path) + if code_path is not None: + dirname = os.path.abspath(os.path.dirname(code_path)) + os.chdir(dirname) + sys.path.insert(0, dirname) - else: - pwd = os.getcwd() - path, fname = os.path.split(plot_path) - sys.path.insert(0, os.path.abspath(path)) - stdout = sys.stdout - sys.stdout = cStringIO.StringIO() - os.chdir(path) - fd = None + # Redirect stdout + stdout = sys.stdout + sys.stdout = cStringIO.StringIO() + + # Reset sys.argv + old_sys_argv = sys.argv + sys.argv = [code_path] + + try: try: - fd = open(fname) - module = imp.load_module( - "__plot__", fd, fname, ('py', 'r', imp.PY_SOURCE)) - finally: - del sys.path[0] - os.chdir(pwd) - sys.stdout = stdout - if fd is not None: - fd.close() - - if function_name is not None: - getattr(module, function_name)() - -def run_savefig(plot_path, basename, tmpdir, destdir, formats): - """ - Once a plot script has been imported, this function runs savefig - on all of the figures in all of the desired formats. - """ - fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() - for i, figman in enumerate(fig_managers): - for j, (format, dpi) in enumerate(formats): - if len(fig_managers) == 1: - outname = basename - else: - outname = "%s_%02d" % (basename, i) - outname = outname + "." + format - outpath = os.path.join(tmpdir, outname) - try: - figman.canvas.figure.savefig(outpath, dpi=dpi) - except: - s = cbook.exception_to_str("Exception saving plot %s" % plot_path) - warnings.warn(s, PlotWarning) - return 0 - if j > 0: - shutil.copyfile(outpath, os.path.join(destdir, outname)) - - return len(fig_managers) - - -def clear_state(): + code = unescape_doctest(code) + if ns is None: + ns = {} + if not ns: + if setup.config.plot_pre_code is None: + exec "import numpy as np\nfrom matplotlib import pyplot as plt\n" in ns + else: + exec setup.config.plot_pre_code in ns + exec code in ns + if function_name is not None: + exec function_name + "()" in ns + except (Exception, SystemExit), err: + raise PlotError(traceback.format_exc()) + finally: + os.chdir(pwd) + sys.argv = old_sys_argv + sys.path[:] = old_sys_path + sys.stdout = stdout + return ns + +def clear_state(plot_rcparams): plt.close('all') matplotlib.rc_file_defaults() + matplotlib.rcParams.update(plot_rcparams) -def render_figures(plot_path, function_name, plot_code, tmpdir, destdir, - formats, context=False): +def render_figures(code, code_path, output_dir, output_base, context, + function_name, config): """ Run a pyplot script and save the low and high res PNGs and a PDF in outdir. - """ - plot_path = str(plot_path) # todo, why is unicode breaking this - basedir, fname = os.path.split(plot_path) - basename, ext = os.path.splitext(fname) + Save the images under *output_dir* with file names derived from + *output_base* + """ + # -- Parse format list + default_dpi = {'png': 80, 'hires.png': 200, 'pdf': 200} + formats = [] + for fmt in config.plot_formats: + if isinstance(fmt, str): + formats.append((fmt, default_dpi.get(fmt, 80))) + elif type(fmt) in (tuple, list) and len(fmt)==2: + formats.append((str(fmt[0]), int(fmt[1]))) + else: + raise PlotError('invalid image format "%r" in plot_formats' % fmt) + # -- Try to determine if all images already exist - all_exists = True + code_pieces = split_code_at_show(code) # Look for single-figure output files first + # Look for single-figure output files first + all_exists = True + img = ImageFile(output_base, output_dir) for format, dpi in formats: - outname = os.path.join(tmpdir, '%s.%s' % (basename, format)) - if out_of_date(plot_path, outname): + if out_of_date(code_path, img.filename(format)): all_exists = False break + img.formats.append(format) + + if all_exists: + return [(code, [img])] - if not context and all_exists: - return 1 - - # Then look for multi-figure output files, assuming - # if we have some we have all... - i = 0 - while True: - all_exists = True - for format, dpi in formats: - outname = os.path.join( - tmpdir, '%s_%02d.%s' % (basename, i, format)) - if out_of_date(plot_path, outname): - all_exists = False + # Then look for multi-figure output files + results = [] + all_exists = True + for i, code_piece in enumerate(code_pieces): + images = [] + for j in xrange(1000): + if len(code_pieces) > 1: + img = ImageFile('%s_%02d_%02d' % (output_base, i, j), output_dir) + else: + img = ImageFile('%s_%02d' % (output_base, j), output_dir) + for format, dpi in formats: + if out_of_date(code_path, img.filename(format)): + all_exists = False + break + img.formats.append(format) + + # assume that if we have one, we have them all + if not all_exists: + all_exists = (j > 0) break - if all_exists: - i += 1 - else: + images.append(img) + if not all_exists: break + results.append((code_piece, images)) - if not context and i != 0: - return i + if all_exists: + return results # We didn't find the files, so build them - if not context: - clear_state() - try: - run_code(plot_path, function_name, plot_code, context=context) - except: - s = cbook.exception_to_str("Exception running plot %s" % plot_path) - warnings.warn(s, PlotWarning) - return 0 - - if not all_exists: - num_figs = run_savefig(plot_path, basename, tmpdir, destdir, formats) - - if '__plot__' in sys.modules: - del sys.modules['__plot__'] - - return num_figs - else: - return 1 - - -def _plot_directive(plot_path, basedir, function_name, plot_code, caption, - options, state_machine): - context = options.has_key('context') + results = [] if context: - # remove for figure directive - del options['context'] - - nofigs = options.has_key('nofigs') - if nofigs: - # remove for figure directive - del options['nofigs'] - - formats = setup.config.plot_formats - if isinstance(formats, basestring): - formats = eval(formats) - - fname = os.path.basename(plot_path) - basename, ext = os.path.splitext(fname) - - # Get the directory of the rst file, and determine the relative - # path from the resulting html file to the plot_directive links - # (linkdir). This relative path is used for html links *only*, - # and not the embedded image. That is given an absolute path to - # the temporary directory, and then sphinx moves the file to - # build/html/_images for us later. - rstdir, rstfile = os.path.split(state_machine.document.attributes['source']) - outdir = os.path.join('plot_directive', basedir) - reldir = relpath(setup.confdir, rstdir) - linkdir = os.path.join(reldir, outdir) - - # tmpdir is where we build all the output files. This way the - # plots won't have to be redone when generating latex after html. - - # Prior to Sphinx 0.6, absolute image paths were treated as - # relative to the root of the filesystem. 0.6 and after, they are - # treated as relative to the root of the documentation tree. We - # need to support both methods here. - tmpdir = os.path.join('build', outdir) - tmpdir = os.path.abspath(tmpdir) - if sphinx_version < (0, 6): - prefix = '' + ns = plot_context else: - prefix = '/' - if not os.path.exists(tmpdir): - cbook.mkdirs(tmpdir) - - # destdir is the directory within the output to store files - # that we'll be linking to -- not the embedded images. - destdir = os.path.abspath(os.path.join(setup.app.builder.outdir, outdir)) - if not os.path.exists(destdir): - cbook.mkdirs(destdir) - - # Properly indent the caption - caption = '\n'.join(template_content_indent + line.strip() - for line in caption.split('\n')) - - # Generate the figures, and return the number of them - num_figs = render_figures(plot_path, function_name, plot_code, tmpdir, - destdir, formats, context=context) - - # Now start generating the lines of output - lines = [] - - if plot_code is None: - shutil.copyfile(plot_path, os.path.join(destdir, fname)) - - if options.has_key('include-source'): - if plot_code is None: - if sphinx_version > (1,): - include_prefix = '/' + ns = {} + + for i, code_piece in enumerate(code_pieces): + if not context: + clear_state(config.plot_rcparams) + run_code(code_piece, code_path, ns, function_name) + + images = [] + fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() + for j, figman in enumerate(fig_managers): + if len(fig_managers) == 1 and len(code_pieces) == 1: + img = ImageFile(output_base, output_dir) + elif len(code_pieces) == 1: + img = ImageFile("%s_%02d" % (output_base, j), output_dir) else: - include_prefix = setup.app.builder.srcdir - - lines.extend( - ['.. include:: %s' % os.path.join(include_prefix, plot_path), - ' :literal:']) - if options.has_key('encoding'): - lines.append(' :encoding: %s' % options['encoding']) - del options['encoding'] - else: - lines.extend(['::', '']) - lines.extend([' %s' % row.rstrip() - for row in plot_code.split('\n')]) - lines.append('') - del options['include-source'] - else: - lines = [] + img = ImageFile("%s_%02d_%02d" % (output_base, i, j), + output_dir) + images.append(img) + for format, dpi in formats: + try: + figman.canvas.figure.savefig(img.filename(format), dpi=dpi) + except exceptions.BaseException as err: + raise PlotError(traceback.format_exc()) + img.formats.append(format) - if not nofigs: - if num_figs > 0: - options = ['%s:%s: %s' % (template_content_indent, key, val) - for key, val in options.items()] - options = "\n".join(options) + results.append((code_piece, images)) - for i in range(num_figs): - if num_figs == 1: - outname = basename - else: - outname = "%s_%02d" % (basename, i) - - # Copy the linked-to files to the destination within the build tree, - # and add a link for them - links = [] - if plot_code is None: - links.append('`source code <%(linkdir)s/%(basename)s.py>`__') - for format, dpi in formats[1:]: - links.append('`%s <%s/%s.%s>`__' % (format, linkdir, outname, format)) - if len(links): - links = '[%s]' % (', '.join(links) % locals()) - else: - links = '' + return results - lines.extend((template % locals()).split('\n')) - else: - lines.extend((exception_template % locals()).split('\n')) +def run(arguments, content, options, state_machine, state, lineno): + # The user may provide a filename *or* Python code content, but not both + if arguments and content: + raise RuntimeError("plot:: directive can't have both args and content") - if len(lines): - state_machine.insert_input( - lines, state_machine.input_lines.source(0)) + document = state_machine.document + config = document.settings.env.config + nofigs = options.has_key('nofigs') - return [] + options.setdefault('include-source', config.plot_include_source) + context = options.has_key('context') + + rst_file = document.attributes['source'] + rst_dir = os.path.dirname(rst_file) -def plot_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, state_machine): - """ - Handle the arguments to the plot directive. The real work happens - in _plot_directive. - """ - # The user may provide a filename *or* Python code content, but not both if len(arguments): - plot_path = directives.uri(arguments[0]) - basedir = relpath(os.path.dirname(plot_path), setup.app.builder.srcdir) + if not config.plot_basedir: + source_file_name = os.path.join(setup.app.builder.srcdir, + directives.uri(arguments[0])) + else: + source_file_name = os.path.join(setup.confdir, config.plot_basedir, + directives.uri(arguments[0])) # If there is content, it will be passed as a caption. - - # Indent to match expansion below. XXX - The number of spaces matches - # that of the 'options' expansion further down. This should be moved - # to common code to prevent them from diverging accidentally. caption = '\n'.join(content) # If the optional function name is provided, use it @@ -479,67 +616,143 @@ def plot_directive(name, arguments, options, content, lineno, else: function_name = None - return _plot_directive(plot_path, basedir, function_name, None, caption, - options, state_machine) + fd = open(source_file_name, 'r') + code = fd.read() + fd.close() + output_base = os.path.basename(source_file_name) + else: + source_file_name = rst_file + code = textwrap.dedent("\n".join(map(str, content))) + counter = document.attributes.get('_plot_counter', 0) + 1 + document.attributes['_plot_counter'] = counter + base, ext = os.path.splitext(os.path.basename(source_file_name)) + output_base = '%s-%d.py' % (base, counter) + function_name = None + caption = '' + + base, source_ext = os.path.splitext(output_base) + if source_ext in ('.py', '.rst', '.txt'): + output_base = base else: - plot_code = '\n'.join(content) + source_ext = '' - # Since we don't have a filename, use a hash based on the content - plot_path = md5(plot_code).hexdigest()[-10:] + # ensure that LaTeX includegraphics doesn't choke in foo.bar.pdf filenames + output_base = output_base.replace('.', '-') - return _plot_directive(plot_path, 'inline', None, plot_code, '', options, - state_machine) + # is it in doctest format? + is_doctest = contains_doctest(code) + if options.has_key('format'): + if options['format'] == 'python': + is_doctest = False + else: + is_doctest = True + + # determine output directory name fragment + source_rel_name = relpath(source_file_name, setup.confdir) + source_rel_dir = os.path.dirname(source_rel_name) + while source_rel_dir.startswith(os.path.sep): + source_rel_dir = source_rel_dir[1:] + + # build_dir: where to place output files (temporarily) + build_dir = os.path.join(os.path.dirname(setup.app.doctreedir), + 'plot_directive', + source_rel_dir) + if not os.path.exists(build_dir): + cbook.mkdirs(build_dir) + + # output_dir: final location in the builder's directory + dest_dir = os.path.abspath(os.path.join(setup.app.builder.outdir, + source_rel_dir)) + if not os.path.exists(dest_dir): + cbook.mkdirs(dest_dir) + + # how to link to files from the RST file + dest_dir_link = os.path.join(relpath(setup.confdir, rst_dir), + source_rel_dir).replace(os.path.sep, '/') + build_dir_link = relpath(build_dir, rst_dir).replace(os.path.sep, '/') + source_link = dest_dir_link + '/' + output_base + source_ext + + # make figures + try: + results = render_figures(code, source_file_name, build_dir, output_base, + context, function_name, config) + errors = [] + except PlotError, err: + reporter = state.memo.reporter + sm = reporter.system_message( + 2, "Exception occurred in plotting %s: %s" % (output_base, err), + line=lineno) + results = [(code, [])] + errors = [sm] -def mark_plot_labels(app, document): - """ - To make plots referenceable, we need to move the reference from - the "htmlonly" (or "latexonly") node to the actual figure node - itself. - """ - for name, explicit in document.nametypes.iteritems(): - if not explicit: - continue - labelid = document.nameids[name] - if labelid is None: - continue - node = document.ids[labelid] - if node.tagname in ('html_only', 'latex_only'): - for n in node: - if n.tagname == 'figure': - sectname = name - for c in n: - if c.tagname == 'caption': - sectname = c.astext() - break + # Properly indent the caption + caption = '\n'.join(' ' + line.strip() + for line in caption.split('\n')) - node['ids'].remove(labelid) - node['names'].remove(name) - n['ids'].append(labelid) - n['names'].append(name) - document.settings.env.labels[name] = \ - document.settings.env.docname, labelid, sectname - break + # generate output restructuredtext + total_lines = [] + for j, (code_piece, images) in enumerate(results): + if options['include-source']: + if is_doctest: + lines = [''] + lines += [row.rstrip() for row in code_piece.split('\n')] + else: + lines = ['.. code-block:: python', ''] + lines += [' %s' % row.rstrip() + for row in code_piece.split('\n')] + source_code = "\n".join(lines) + else: + source_code = "" -def setup(app): - setup.app = app - setup.config = app.config - setup.confdir = app.confdir + if nofigs: + images = [] - options = {'alt': directives.unchanged, - 'height': directives.length_or_unitless, - 'width': directives.length_or_percentage_or_unitless, - 'scale': directives.nonnegative_int, - 'align': align, - 'class': directives.class_option, - 'include-source': directives.flag, - 'context': directives.flag, - 'nofigs': directives.flag, - 'encoding': directives.encoding } + opts = [':%s: %s' % (key, val) for key, val in options.items() + if key in ('alt', 'height', 'width', 'scale', 'align', 'class')] - app.add_directive('plot', plot_directive, True, (0, 2, 0), **options) - app.add_config_value( - 'plot_formats', - [('png', 80), ('hires.png', 200), ('pdf', 50)], - True) + only_html = ".. only:: html" + only_latex = ".. only:: latex" - app.connect('doctree-read', mark_plot_labels) + if j == 0: + src_link = source_link + else: + src_link = None + + result = format_template( + TEMPLATE, + dest_dir=dest_dir_link, + build_dir=build_dir_link, + source_link=src_link, + multi_image=len(images) > 1, + only_html=only_html, + only_latex=only_latex, + options=opts, + images=images, + source_code=source_code, + html_show_formats=config.plot_html_show_formats, + caption=caption) + + total_lines.extend(result.split("\n")) + total_lines.extend("\n") + + if total_lines: + state_machine.insert_input(total_lines, source=source_file_name) + + # copy image files to builder's output directory + if not os.path.exists(dest_dir): + cbook.mkdirs(dest_dir) + + for code_piece, images in results: + for img in images: + for fn in img.filenames(): + shutil.copyfile(fn, os.path.join(dest_dir, + os.path.basename(fn))) + + # copy script (if necessary) + if source_file_name == rst_file: + target_name = os.path.join(dest_dir, output_base + source_ext) + f = open(target_name, 'w') + f.write(unescape_doctest(code)) + f.close() + + return errors diff --git a/lib/matplotlib/testing/jpl_units/UnitDblConverter.py b/lib/matplotlib/testing/jpl_units/UnitDblConverter.py index 18218412c093..8d88f464cd7d 100644 --- a/lib/matplotlib/testing/jpl_units/UnitDblConverter.py +++ b/lib/matplotlib/testing/jpl_units/UnitDblConverter.py @@ -79,11 +79,7 @@ def axisinfo( unit, axis ): else: label = None - if ( label == "rad" ): - # If the axis units are in radians, then use a special function for - # applying format control. - majfmt = ticker.FuncFormatter( rad_fn ) - elif ( label == "deg" ) and isinstance( axis.axes, polar.PolarAxes ): + if ( label == "deg" ) and isinstance( axis.axes, polar.PolarAxes ): # If we want degrees for a polar plot, use the PolarPlotFormatter majfmt = polar.PolarAxes.ThetaFormatter() else: diff --git a/examples/tests/pngsuite/basn0g01.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn0g01.png similarity index 100% rename from examples/tests/pngsuite/basn0g01.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn0g01.png diff --git a/examples/tests/pngsuite/basn0g02.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn0g02.png similarity index 100% rename from examples/tests/pngsuite/basn0g02.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn0g02.png diff --git a/examples/tests/pngsuite/basn0g04.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn0g04.png similarity index 100% rename from examples/tests/pngsuite/basn0g04.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn0g04.png diff --git a/examples/tests/pngsuite/basn0g08.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn0g08.png similarity index 100% rename from examples/tests/pngsuite/basn0g08.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn0g08.png diff --git a/examples/tests/pngsuite/basn0g16.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn0g16.png similarity index 100% rename from examples/tests/pngsuite/basn0g16.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn0g16.png diff --git a/examples/tests/pngsuite/basn2c08.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn2c08.png similarity index 100% rename from examples/tests/pngsuite/basn2c08.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn2c08.png diff --git a/examples/tests/pngsuite/basn2c16.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn2c16.png similarity index 100% rename from examples/tests/pngsuite/basn2c16.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn2c16.png diff --git a/examples/tests/pngsuite/basn3p01.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn3p01.png similarity index 100% rename from examples/tests/pngsuite/basn3p01.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn3p01.png diff --git a/examples/tests/pngsuite/basn3p02.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn3p02.png similarity index 100% rename from examples/tests/pngsuite/basn3p02.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn3p02.png diff --git a/examples/tests/pngsuite/basn3p04.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn3p04.png similarity index 100% rename from examples/tests/pngsuite/basn3p04.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn3p04.png diff --git a/examples/tests/pngsuite/basn3p08.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn3p08.png similarity index 100% rename from examples/tests/pngsuite/basn3p08.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn3p08.png diff --git a/examples/tests/pngsuite/basn4a08.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn4a08.png similarity index 100% rename from examples/tests/pngsuite/basn4a08.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn4a08.png diff --git a/examples/tests/pngsuite/basn4a16.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn4a16.png similarity index 100% rename from examples/tests/pngsuite/basn4a16.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn4a16.png diff --git a/examples/tests/pngsuite/basn6a08.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn6a08.png similarity index 100% rename from examples/tests/pngsuite/basn6a08.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn6a08.png diff --git a/examples/tests/pngsuite/basn6a16.png b/lib/matplotlib/tests/baseline_images/pngsuite/basn6a16.png similarity index 100% rename from examples/tests/pngsuite/basn6a16.png rename to lib/matplotlib/tests/baseline_images/pngsuite/basn6a16.png diff --git a/lib/matplotlib/tests/baseline_images/test_axes/canonical.pdf b/lib/matplotlib/tests/baseline_images/test_axes/canonical.pdf new file mode 100644 index 000000000000..c8d6ad974738 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/canonical.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/canonical.png b/lib/matplotlib/tests/baseline_images/test_axes/canonical.png new file mode 100644 index 000000000000..76782c967ae4 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/canonical.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/canonical.svg b/lib/matplotlib/tests/baseline_images/test_axes/canonical.svg new file mode 100644 index 000000000000..1ce403f08515 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_axes/canonical.svg @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.pdf b/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.pdf index 91c9d94c9a1a..1acf60be40fa 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.png b/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.png index 050c6e43914c..7084e4390771 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.png and b/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.svg b/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.svg index 729b40345fb4..94a14767fa31 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/polar_axes.svg @@ -11,7 +11,7 @@ +L0.000000 0.000000z"/> @@ -132,7 +132,7 @@ C-0.795609 -3.000000 -1.558740 -2.683901 -2.121320 -2.121320 C-2.683901 -1.558740 -3.000000 -0.795609 -3.000000 0.000000 C-3.000000 0.795609 -2.683901 1.558740 -2.121320 2.121320 C-1.558740 2.683901 -0.795609 3.000000 0.000000 3.000000z"/> - + @@ -580,9 +580,9 @@ C122.400000 261.827096 140.607298 305.783402 173.011948 338.188052 C205.416598 370.592702 249.372904 388.800000 295.200000 388.800000z"/> - + diff --git a/lib/matplotlib/tests/baseline_images/test_axes/symlog.pdf b/lib/matplotlib/tests/baseline_images/test_axes/symlog.pdf index 8137e3b9ad5e..822c5cdfb03f 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/symlog.pdf and b/lib/matplotlib/tests/baseline_images/test_axes/symlog.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/symlog.png b/lib/matplotlib/tests/baseline_images/test_axes/symlog.png index 32c6189bf58c..737aa236e78e 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/symlog.png and b/lib/matplotlib/tests/baseline_images/test_axes/symlog.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/symlog.svg b/lib/matplotlib/tests/baseline_images/test_axes/symlog.svg index b5169fb78d99..4b177e8548b0 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/symlog.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/symlog.svg @@ -23,9 +23,9 @@ L72.000000 43.200000z"/> - + @@ -57,7 +57,7 @@ L286.272000 381.525282L500.544000 381.525282"/> - + @@ -129,30 +129,33 @@ L286.272000 381.525282L500.544000 381.525282"/> - + - + + + + - - + + - + - + - + @@ -162,14 +165,14 @@ L286.272000 381.525282L500.544000 381.525282"/> - + - + - + @@ -179,17 +182,17 @@ L286.272000 381.525282L500.544000 381.525282"/> - + - + - + @@ -199,17 +202,17 @@ L286.272000 381.525282L500.544000 381.525282"/> - + - + - + @@ -219,14 +222,14 @@ L286.272000 381.525282L500.544000 381.525282"/> - + - + - + @@ -236,17 +239,17 @@ L286.272000 381.525282L500.544000 381.525282"/> - + - + - + @@ -277,227 +280,227 @@ L286.272000 381.525282L500.544000 381.525282"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/lib/matplotlib/tests/baseline_images/test_image/image_interps.pdf b/lib/matplotlib/tests/baseline_images/test_image/image_interps.pdf index 848e15fe9099..42ea689625a3 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_image/image_interps.pdf and b/lib/matplotlib/tests/baseline_images/test_image/image_interps.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_png/pngsuite.pdf b/lib/matplotlib/tests/baseline_images/test_png/pngsuite.pdf new file mode 100644 index 000000000000..11355aa62bd8 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_png/pngsuite.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_png/pngsuite.png b/lib/matplotlib/tests/baseline_images/test_png/pngsuite.png new file mode 100644 index 000000000000..5c3dced973c7 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_png/pngsuite.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_png/pngsuite.svg b/lib/matplotlib/tests/baseline_images/test_png/pngsuite.svg new file mode 100644 index 000000000000..8317c4efd22b --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_png/pngsuite.svg @@ -0,0 +1,638 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index eaeee01b1975..cceb110b5eaf 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -279,10 +279,12 @@ def test_polar_wrap(): @image_comparison(baseline_images=['polar_units']) def test_polar_units(): import matplotlib.testing.jpl_units as units + from nose.tools import assert_true units.register() pi = np.pi deg = units.UnitDbl( 1.0, "deg" ) + km = units.UnitDbl( 1.0, "km" ) x1 = [ pi/6.0, pi/4.0, pi/3.0, pi/2.0 ] x2 = [ 30.0*deg, 45.0*deg, 60.0*deg, 90.0*deg ] @@ -299,6 +301,12 @@ def test_polar_units(): fig.savefig( 'polar_units' ) + # make sure runits and theta units work + y1 = [ y*km for y in y1 ] + plt.polar( x2, y1, color = "blue", thetaunits="rad", runits="km" ) + assert_true( isinstance(plt.gca().get_xaxis().get_major_formatter(), units.UnitDblFormatter) ) + + @image_comparison(baseline_images=['polar_rmin']) def test_polar_rmin(): r = np.arange(0, 3.0, 0.01) @@ -504,6 +512,13 @@ def test_pcolormesh(): fig.savefig('pcolormesh') + +@image_comparison(baseline_images=['canonical']) +def test_canonical(): + fig, ax = plt.subplots() + ax.plot([1,2,3]) + fig.savefig('canonical') + if __name__=='__main__': import nose nose.runmodule(argv=['-s','--with-doctest'], exit=False) diff --git a/lib/matplotlib/tests/test_cbook.py b/lib/matplotlib/tests/test_cbook.py index 848686804d21..227f6eab1fc0 100644 --- a/lib/matplotlib/tests/test_cbook.py +++ b/lib/matplotlib/tests/test_cbook.py @@ -12,3 +12,18 @@ def test_is_string_like(): assert cbook.is_string_like( "hello world" ) assert_equal( cbook.is_string_like(10), False ) + +def test_restrict_dict(): + d = {'foo': 'bar', 1: 2} + d1 = cbook.restrict_dict(d, ['foo', 1]) + assert_equal(d1, d) + d2 = cbook.restrict_dict(d, ['bar', 2]) + assert_equal(d2, {}) + d3 = cbook.restrict_dict(d, {'foo': 1}) + assert_equal(d3, {'foo': 'bar'}) + d4 = cbook.restrict_dict(d, {}) + assert_equal(d4, {}) + d5 = cbook.restrict_dict(d, set(['foo',2])) + assert_equal(d5, {'foo': 'bar'}) + # check that d was not modified + assert_equal(d, {'foo': 'bar', 1: 2}) diff --git a/lib/matplotlib/tests/test_png.py b/lib/matplotlib/tests/test_png.py new file mode 100644 index 000000000000..58213c4aeb28 --- /dev/null +++ b/lib/matplotlib/tests/test_png.py @@ -0,0 +1,29 @@ +from matplotlib.testing.decorators import image_comparison, knownfailureif +from matplotlib import pyplot as plt +import matplotlib.cm as cm +import glob +import os + +@image_comparison(baseline_images=['pngsuite']) +def test_pngsuite(): + dirname = os.path.join( + os.path.dirname(__file__), + 'baseline_images', + 'pngsuite') + files = glob.glob(os.path.join(dirname, 'basn*.png')) + files.sort() + + fig = plt.figure(figsize=(len(files), 2)) + + for i, fname in enumerate(files): + data = plt.imread(fname) + cmap = None # use default colormap + if data.ndim==2: + # keep grayscale images gray + cmap = cm.gray + plt.imshow(data, extent=[i,i+1,0,1], cmap=cmap) + + plt.gca().get_frame().set_facecolor("#ddffff") + plt.gca().set_xlim(0, len(files)) + + fig.savefig('pngsuite') diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index dbcb70a8a84e..ba5aa2b8d6e9 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -603,7 +603,7 @@ def get_fontproperties(self): def get_font_properties(self): 'alias for get_fontproperties' - return self.get_fontproperties + return self.get_fontproperties() def get_family(self): "Return the list of font families used for font lookup" diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 83b84ed18bcc..96607ced29c3 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -127,7 +127,6 @@ - class TickHelper: axis = None class DummyAxis: @@ -1174,14 +1173,14 @@ def decade_down(x, base=10): 'floor x to the nearest lower decade' if x == 0.0: return -base - lx = math.floor(math.log(x)/math.log(base)) + lx = np.floor(np.log(x)/np.log(base)) return base**lx def decade_up(x, base=10): 'ceil x to the nearest higher decade' if x == 0.0: return base - lx = math.ceil(math.log(x)/math.log(base)) + lx = np.ceil(np.log(x)/np.log(base)) return base**lx def nearest_long(x): @@ -1194,7 +1193,7 @@ def is_decade(x, base=10): return False if x == 0.0: return True - lx = math.log(abs(x))/math.log(base) + lx = np.log(np.abs(x))/np.log(base) return is_close_to_int(lx) def is_close_to_int(x): @@ -1274,15 +1273,20 @@ def __call__(self): stride += 1 decades = np.arange(math.floor(vmin), - math.ceil(vmax)+stride, stride) - if len(subs) > 1 or (len(subs == 1) and subs[0] != 1.0): - ticklocs = [] - for decadeStart in b**decades: - ticklocs.extend( subs*decadeStart ) + math.ceil(vmax)+stride, stride) + if hasattr(self, '_transform'): + ticklocs = self._transform.inverted().transform(decades) + if len(subs) > 1 or (len(subs == 1) and subs[0] != 1.0): + ticklocs = np.ravel(np.outer(subs, ticklocs)) else: - ticklocs = b**decades + if len(subs) > 1 or (len(subs == 1) and subs[0] != 1.0): + ticklocs = [] + for decadeStart in b**decades: + ticklocs.extend( subs*decadeStart ) + else: + ticklocs = b**decades - return self.raise_if_exceeds(np.array(ticklocs)) + return self.raise_if_exceeds(np.asarray(ticklocs)) def view_limits(self, vmin, vmax): 'Try to choose the view limits intelligently' diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index 3165e6629daa..d76214f3eafc 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -1506,6 +1506,7 @@ def get_matrix(self): a c e b d f 0 0 1 + """ self._invalid = 0 return self._mtx diff --git a/lib/mpl_toolkits/axes_grid/colorbar.py b/lib/mpl_toolkits/axes_grid/colorbar.py index b1f06799c2d0..7ab7593a2ecd 100644 --- a/lib/mpl_toolkits/axes_grid/colorbar.py +++ b/lib/mpl_toolkits/axes_grid/colorbar.py @@ -162,7 +162,7 @@ class CbarAxesLocator(object): """ - CbarAxesLocator is a axes_locator for colobar axes. It adjust the + CbarAxesLocator is a axes_locator for colorbar axes. It adjust the position of the axes to make a room for extended ends, i.e., the extended ends are located outside the axes area. """ @@ -194,7 +194,7 @@ def get_original_position(self, axes, renderer): def get_end_vertices(self): """ return a tuple of two vertices for the colorbar extended ends. - The first vertives is for min. end, and the second is for + The first vertices is for min. end, and the second is for max. end. """ # Note that concatenating two vertices needs to make a @@ -390,7 +390,7 @@ def __init__(self, ax, cmap=None, def _get_colorbar_limits(self): """ - initial limits for colorbar range. The returne min, max values + initial limits for colorbar range. The returned min, max values will be used to create colorbar solid(?) and etc. """ if self.boundaries is not None: @@ -407,7 +407,7 @@ def _get_colorbar_limits(self): def _config_axes(self): ''' - Adjust the properties of the axes to be adquate for colorbar display. + Adjust the properties of the axes to be adequate for colorbar display. ''' ax = self.ax @@ -416,7 +416,7 @@ def _config_axes(self): orientation=self.orientation) ax.set_axes_locator(axes_locator) - # overide the get_data_ratio for the aspect works. + # override the get_data_ratio for the aspect works. def _f(): return 1. ax.get_data_ratio = _f @@ -557,7 +557,7 @@ def _add_solids(self, X, Y, C): def add_lines(self, levels, colors, linewidths): ''' - Draw lines on the colorbar. It deletes preexting lines. + Draw lines on the colorbar. It deletes preexisting lines. ''' del self.lines @@ -735,7 +735,7 @@ def add_lines(self, CS): def update_bruteforce(self, mappable): """ Update the colorbar artists to reflect the change of the - assocaited mappable. + associated mappable. """ self.update_artists() diff --git a/lib/mpl_toolkits/axes_grid1/anchored_artists.py b/lib/mpl_toolkits/axes_grid1/anchored_artists.py index 045a4834de8c..1a077553f3a8 100644 --- a/lib/mpl_toolkits/axes_grid1/anchored_artists.py +++ b/lib/mpl_toolkits/axes_grid1/anchored_artists.py @@ -17,7 +17,7 @@ def __init__(self, width, height, xdescent, ydescent, **kwargs): """ *width*, *height*, *xdescent*, *ydescent* : the dimensions of the DrawingArea. - *prop* : font property. this is only used for scaling the paddings. + *prop* : font property. This is only used for scaling the paddings. """ self.da = DrawingArea(width, height, xdescent, ydescent, clip=True) @@ -69,7 +69,7 @@ def __init__(self, transform, size, label, loc, **kwargs): """ Draw a horizontal bar with the size in data coordinate of the give axes. - A label will be drawn underneath (center-alinged). + A label will be drawn underneath (center-aligned). pad, borderpad in fraction of the legend font size (or prop) sep in points. diff --git a/lib/mpl_toolkits/axes_grid1/axes_divider.py b/lib/mpl_toolkits/axes_grid1/axes_divider.py index 69463eff8c5a..54eb01727473 100644 --- a/lib/mpl_toolkits/axes_grid1/axes_divider.py +++ b/lib/mpl_toolkits/axes_grid1/axes_divider.py @@ -3,8 +3,8 @@ multiple axes at the drawing time. Divider: this is the class that is used calculates the axes - position. It divides the given renctangular area into several sub - rectangles. You intialize the divider by setting the horizontal + position. It divides the given rectangular area into several sub + rectangles. You initialize the divider by setting the horizontal and vertical list of sizes that the division will be based on. You then use the new_locator method, whose return value is a callable object that can be used to set the axes_locator of the axes. @@ -23,8 +23,8 @@ class Divider(object): """ This is the class that is used calculates the axes position. It - divides the given renctangular area into several - sub-rectangles. You intialize the divider by setting the + divides the given rectangular area into several + sub-rectangles. You initialize the divider by setting the horizontal and vertical lists of sizes (:mod:`mpl_toolkits.axes_grid.axes_size`) that the division will be based on. You then use the new_locator method to create a @@ -44,11 +44,11 @@ def __init__(self, fig, pos, horizontal, vertical, aspect=None, anchor="C"): :param vertical: list of sizes (:mod:`~mpl_toolkits.axes_grid.axes_size`) for vertical division - :param aspect: if True, the overall rectalngular area is reduced + :param aspect: if True, the overall rectangular area is reduced so that the relative part of the horizontal and vertical scales have same scale. - :param anchor: Detrmine how the reduced rectangle is placed - when aspect is True, + :param anchor: Determine how the reduced rectangle is placed + when aspect is True. """ self._fig = fig @@ -59,15 +59,30 @@ def __init__(self, fig, pos, horizontal, vertical, aspect=None, anchor="C"): self._aspect = aspect self._xrefindex = 0 self._yrefindex = 0 + self._locator = None + + def get_horizontal_sizes(self, renderer): + return [s.get_size(renderer) for s in self.get_horizontal()] + + def get_vertical_sizes(self, renderer): + return [s.get_size(renderer) for s in self.get_vertical()] + + def get_vsize_hsize(self): + + from axes_size import AddList + + vsize = AddList(self.get_vertical()) + hsize = AddList(self.get_horizontal()) + + return vsize, hsize @staticmethod - def _calc_k(l, total_size, renderer): + def _calc_k(l, total_size): rs_sum, as_sum = 0., 0. - for s in l: - _rs, _as = s.get_size(renderer) + for _rs, _as in l: rs_sum += _rs as_sum += _as @@ -79,12 +94,13 @@ def _calc_k(l, total_size, renderer): @staticmethod - def _calc_offsets(l, k, renderer): + def _calc_offsets(l, k): offsets = [0.] - for s in l: - _rs, _as = s.get_size(renderer) + #for s in l: + for _rs, _as in l: + #_rs, _as = s.get_size(renderer) offsets.append(offsets[-1] + _rs*k + _as) return offsets @@ -168,8 +184,19 @@ def get_aspect(self): "return aspect" return self._aspect + def set_locator(self, _locator): + self._locator = _locator + + def get_locator(self): + return self._locator + + def get_position_runtime(self, ax, renderer): + if self._locator is None: + return self.get_position() + else: + return self._locator(ax, renderer).bounds - def locate(self, nx, ny, nx1=None, ny1=None, renderer=None): + def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None): """ :param nx, nx1: Integers specifying the column-position of the @@ -182,15 +209,17 @@ def locate(self, nx, ny, nx1=None, ny1=None, renderer=None): figW,figH = self._fig.get_size_inches() - x, y, w, h = self.get_position() + x, y, w, h = self.get_position_runtime(axes, renderer) - k_h = self._calc_k(self._horizontal, figW*w, renderer) - k_v = self._calc_k(self._vertical, figH*h, renderer) + hsizes = self.get_horizontal_sizes(renderer) + vsizes = self.get_vertical_sizes(renderer) + k_h = self._calc_k(hsizes, figW*w) + k_v = self._calc_k(vsizes, figH*h) if self.get_aspect(): k = min(k_h, k_v) - ox = self._calc_offsets(self._horizontal, k, renderer) - oy = self._calc_offsets(self._vertical, k, renderer) + ox = self._calc_offsets(hsizes, k) + oy = self._calc_offsets(vsizes, k) ww = (ox[-1] - ox[0])/figW hh = (oy[-1] - oy[0])/figH @@ -200,8 +229,8 @@ def locate(self, nx, ny, nx1=None, ny1=None, renderer=None): x0, y0 = pb1_anchored.x0, pb1_anchored.y0 else: - ox = self._calc_offsets(self._horizontal, k_h, renderer) - oy = self._calc_offsets(self._vertical, k_v, renderer) + ox = self._calc_offsets(hsizes, k_h) + oy = self._calc_offsets(vsizes, k_v) x0, y0 = x, y @@ -231,11 +260,37 @@ def new_locator(self, nx, ny, nx1=None, ny1=None): """ return AxesLocator(self, nx, ny, nx1, ny1) + def append_size(self, position, size): + + if position == "left": + self._horizontal.insert(0, size) + self._xrefindex += 1 + elif position == "right": + self._horizontal.append(size) + elif position == "bottom": + self._vertical.insert(0, size) + self._yrefindex += 1 + elif position == "top": + self._vertical.append(size) + else: + raise ValueError("the position must be one of left, right, bottom, or top") + + + def add_auto_adjustable_area(self, + use_axes, pad=0.1, + adjust_dirs=["left", "right", "bottom", "top"], + ): + from axes_size import Padded, SizeFromFunc, GetExtentHelper + for d in adjust_dirs: + helper = GetExtentHelper(use_axes, d) + size = SizeFromFunc(helper) + padded_size = Padded(size, pad) # pad in inch + self.append_size(d, padded_size) class AxesLocator(object): """ - A simple callable object, initiallized with AxesDivider class, + A simple callable object, initialized with AxesDivider class, returns the position and size of the given cell. """ def __init__(self, axes_divider, nx, ny, nx1=None, ny1=None): @@ -274,13 +329,14 @@ def __call__(self, axes, renderer): self._ny + _yrefindex, self._nx1 + _xrefindex, self._ny1 + _yrefindex, + axes, renderer) class SubplotDivider(Divider): """ - The Divider class whose rectangle area is specified as a subplot grometry. + The Divider class whose rectangle area is specified as a subplot geometry. """ @@ -378,13 +434,20 @@ class AxesDivider(Divider): Divider based on the pre-existing axes. """ - def __init__(self, axes): + def __init__(self, axes, xref=None, yref=None): """ :param axes: axes """ self._axes = axes - self._xref = Size.AxesX(axes) - self._yref = Size.AxesY(axes) + if xref==None: + self._xref = Size.AxesX(axes) + else: + self._xref = xref + if yref==None: + self._yref = Size.AxesY(axes) + else: + self._yref = yref + Divider.__init__(self, fig=axes.get_figure(), pos=None, horizontal=[self._xref], vertical=[self._yref], aspect=None, anchor="C") @@ -412,18 +475,17 @@ def new_horizontal(self, size, pad=None, pack_start=False, **kwargs): :param size: A width of the axes. A :mod:`~mpl_toolkits.axes_grid.axes_size` instance or if float or string is given, *from_any* - fucntion is used to create one, with *ref_size* set to AxesX instance + function is used to create one, with *ref_size* set to AxesX instance of the current axes. :param pad: pad between the axes. It takes same argument as *size*. :param pack_start: If False, the new axes is appended at the end of the list, i.e., it became the right-most axes. If True, it is - inseted at the start of the list, and becomes the left-most axes. + inserted at the start of the list, and becomes the left-most axes. - All extra keywords argument is passed to when creating a axes. - if *axes_class* is given, the new axes will be created as an + All extra keywords arguments are passed to the created axes. + If *axes_class* is given, the new axes will be created as an instance of the given class. Otherwise, the same class of the - main axes will be used. if Not provided - + main axes will be used. """ if pad: @@ -459,18 +521,17 @@ def new_vertical(self, size, pad=None, pack_start=False, **kwargs): :param size: A height of the axes. A :mod:`~mpl_toolkits.axes_grid.axes_size` instance or if float or string is given, *from_any* - fucntion is used to create one, with *ref_size* set to AxesX instance + function is used to create one, with *ref_size* set to AxesX instance of the current axes. :param pad: pad between the axes. It takes same argument as *size*. :param pack_start: If False, the new axes is appended at the end of the list, i.e., it became the top-most axes. If True, it is - inseted at the start of the list, and becomes the bottom-most axes. + inserted at the start of the list, and becomes the bottom-most axes. - All extra keywords argument is passed to when creating a axes. - if *axes_class* is given, the new axes will be created as an + All extra keywords arguments are passed to the created axes. + If *axes_class* is given, the new axes will be created as an instance of the given class. Otherwise, the same class of the - main axes will be used. if Not provided - + main axes will be used. """ if pad: @@ -553,203 +614,266 @@ def get_anchor(self): -class LocatableAxesBase: - def __init__(self, *kl, **kw): - self._axes_class.__init__(self, *kl, **kw) - self._locator = None - self._locator_renderer = None - def set_axes_locator(self, locator): - self._locator = locator +class HBoxDivider(SubplotDivider): - def get_axes_locator(self): - return self._locator - def apply_aspect(self, position=None): + def __init__(self, fig, *args, **kwargs): + SubplotDivider.__init__(self, fig, *args, **kwargs) - if self.get_axes_locator() is None: - self._axes_class.apply_aspect(self, position) - else: - pos = self.get_axes_locator()(self, self._locator_renderer) - self._axes_class.apply_aspect(self, position=pos) + @staticmethod + def _determine_karray(equivalent_sizes, appended_sizes, + max_equivalent_size, + total_appended_size): - def draw(self, renderer=None, inframe=False): - self._locator_renderer = renderer + n = len(equivalent_sizes) + import numpy as np + A = np.mat(np.zeros((n+1, n+1), dtype="d")) + B = np.zeros((n+1), dtype="d") + # AxK = B - self._axes_class.draw(self, renderer, inframe) + # populated A + for i, (r, a) in enumerate(equivalent_sizes): + A[i,i] = r + A[i,-1] = -1 + B[i] = -a + A[-1,:-1] = [r for r, a in appended_sizes] + B[-1] = total_appended_size - sum([a for rs, a in appended_sizes]) + karray_H = (A.I*np.mat(B).T).A1 + karray = karray_H[:-1] + H = karray_H[-1] + if H > max_equivalent_size: + karray = (max_equivalent_size - \ + np.array([a for r, a in equivalent_sizes])) \ + / np.array([r for r, a in equivalent_sizes]) + return karray -_locatableaxes_classes = {} -def locatable_axes_factory(axes_class): - new_class = _locatableaxes_classes.get(axes_class) - if new_class is None: - new_class = new.classobj("Locatable%s" % (axes_class.__name__), - (LocatableAxesBase, axes_class), - {'_axes_class': axes_class}) - _locatableaxes_classes[axes_class] = new_class + @staticmethod + def _calc_offsets(appended_sizes, karray): + offsets = [0.] - return new_class + #for s in l: + for (r, a), k in zip(appended_sizes, karray): + offsets.append(offsets[-1] + r*k + a) -#if hasattr(maxes.Axes, "get_axes_locator"): -# LocatableAxes = maxes.Axes -#else: + return offsets -def make_axes_locatable(axes): - if not hasattr(axes, "set_axes_locator"): - new_class = locatable_axes_factory(type(axes)) - axes.__class__ = new_class - divider = AxesDivider(axes) - locator = divider.new_locator(nx=0, ny=0) - axes.set_axes_locator(locator) + def new_locator(self, nx, nx1=None): + """ + returns a new locator + (:class:`mpl_toolkits.axes_grid.axes_divider.AxesLocator`) for + specified cell. - return divider + :param nx, nx1: Integers specifying the column-position of the + cell. When nx1 is None, a single nx-th column is + specified. Otherwise location of columns spanning between nx + to nx1 (but excluding nx1-th column) is specified. + :param ny, ny1: same as nx and nx1, but for row positions. + """ + return AxesLocator(self, nx, 0, nx1, None) -#from matplotlib.axes import Axes -from mpl_axes import Axes -LocatableAxes = locatable_axes_factory(Axes) + + def _locate(self, x, y, w, h, + y_equivalent_sizes, x_appended_sizes, + figW, figH): + """ + + :param nx, nx1: Integers specifying the column-position of the + cell. When nx1 is None, a single nx-th column is + specified. Otherwise location of columns spanning between nx + to nx1 (but excluding nx1-th column) is specified. + + :param ny, ny1: same as nx and nx1, but for row positions. + """ + equivalent_sizes = y_equivalent_sizes + appended_sizes = x_appended_sizes -def get_demo_image(): - # prepare image - delta = 0.5 + max_equivalent_size = figH*h + total_appended_size = figW*w + karray = self._determine_karray(equivalent_sizes, appended_sizes, + max_equivalent_size, + total_appended_size) - extent = (-3,4,-4,3) - import numpy as np - x = np.arange(-3.0, 4.001, delta) - y = np.arange(-4.0, 3.001, delta) - X, Y = np.meshgrid(x, y) - import matplotlib.mlab as mlab - Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) - Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) - Z = (Z1 - Z2) * 10 + ox = self._calc_offsets(appended_sizes, karray) - return Z, extent + ww = (ox[-1] - ox[0])/figW + ref_h = equivalent_sizes[0] + hh = (karray[0]*ref_h[0] + ref_h[1])/figH + pb = mtransforms.Bbox.from_bounds(x, y, w, h) + pb1 = mtransforms.Bbox.from_bounds(x, y, ww, hh) + pb1_anchored = pb1.anchored(self.get_anchor(), pb) + x0, y0 = pb1_anchored.x0, pb1_anchored.y0 -def demo_locatable_axes(): - import matplotlib.pyplot as plt + return x0, y0, ox, hh - fig1 = plt.figure(1, (6, 6)) - fig1.clf() + def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None): + """ - ## PLOT 1 - # simple image & colorbar - ax = fig1.add_subplot(2, 2, 1) + :param nx, nx1: Integers specifying the column-position of the + cell. When nx1 is None, a single nx-th column is + specified. Otherwise location of columns spanning between nx + to nx1 (but excluding nx1-th column) is specified. - Z, extent = get_demo_image() + :param ny, ny1: same as nx and nx1, but for row positions. + """ - im = ax.imshow(Z, extent=extent, interpolation="nearest") - cb = plt.colorbar(im) - plt.setp(cb.ax.get_yticklabels(), visible=False) + figW,figH = self._fig.get_size_inches() + x, y, w, h = self.get_position_runtime(axes, renderer) - ## PLOT 2 - # image and colorbar whose location is adjusted in the drawing time. - # a hard way + y_equivalent_sizes = self.get_vertical_sizes(renderer) + x_appended_sizes = self.get_horizontal_sizes(renderer) + x0, y0, ox, hh = self._locate(x, y, w, h, + y_equivalent_sizes, x_appended_sizes, + figW, figH) + if nx1 is None: + nx1=nx+1 - divider = SubplotDivider(fig1, 2, 2, 2, aspect=True) + x1, w1 = x0 + ox[nx]/figW, (ox[nx1] - ox[nx])/figW + y1, h1 = y0, hh - # axes for image - ax = LocatableAxes(fig1, divider.get_position()) + return mtransforms.Bbox.from_bounds(x1, y1, w1, h1) - # axes for coloarbar - ax_cb = LocatableAxes(fig1, divider.get_position()) - h = [Size.AxesX(ax), # main axes - Size.Fixed(0.05), # padding, 0.1 inch - Size.Fixed(0.2), # colorbar, 0.3 inch - ] - v = [Size.AxesY(ax)] +class VBoxDivider(HBoxDivider): + """ + The Divider class whose rectangle area is specified as a subplot geometry. + """ - divider.set_horizontal(h) - divider.set_vertical(v) - ax.set_axes_locator(divider.new_locator(nx=0, ny=0)) - ax_cb.set_axes_locator(divider.new_locator(nx=2, ny=0)) + def new_locator(self, ny, ny1=None): + """ + returns a new locator + (:class:`mpl_toolkits.axes_grid.axes_divider.AxesLocator`) for + specified cell. - fig1.add_axes(ax) - fig1.add_axes(ax_cb) + :param nx, nx1: Integers specifying the column-position of the + cell. When nx1 is None, a single nx-th column is + specified. Otherwise location of columns spanning between nx + to nx1 (but excluding nx1-th column) is specified. - ax_cb.yaxis.set_ticks_position("right") + :param ny, ny1: same as nx and nx1, but for row positions. + """ + return AxesLocator(self, 0, ny, None, ny1) - Z, extent = get_demo_image() - im = ax.imshow(Z, extent=extent, interpolation="nearest") - plt.colorbar(im, cax=ax_cb) - plt.setp(ax_cb.get_yticklabels(), visible=False) + def locate(self, nx, ny, nx1=None, ny1=None, axes=None, renderer=None): + """ - plt.draw() - #plt.colorbar(im, cax=ax_cb) + :param nx, nx1: Integers specifying the column-position of the + cell. When nx1 is None, a single nx-th column is + specified. Otherwise location of columns spanning between nx + to nx1 (but excluding nx1-th column) is specified. + :param ny, ny1: same as nx and nx1, but for row positions. + """ - ## PLOT 3 - # image and colorbar whose location is adjusted in the drawing time. - # a easy way - ax = fig1.add_subplot(2, 2, 3) - divider = make_axes_locatable(ax) + figW,figH = self._fig.get_size_inches() + x, y, w, h = self.get_position_runtime(axes, renderer) - ax_cb = divider.new_horizontal(size="5%", pad=0.05) - fig1.add_axes(ax_cb) + x_equivalent_sizes = self.get_horizontal_sizes(renderer) + y_appended_sizes = self.get_vertical_sizes(renderer) - im = ax.imshow(Z, extent=extent, interpolation="nearest") - plt.colorbar(im, cax=ax_cb) - plt.setp(ax_cb.get_yticklabels(), visible=False) + y0, x0, oy, ww = self._locate(y, x, h, w, + x_equivalent_sizes, y_appended_sizes, + figH, figW) + if ny1 is None: + ny1=ny+1 + x1, w1 = x0, ww + y1, h1 = y0 + oy[ny]/figH, (oy[ny1] - oy[ny])/figH - ## PLOT 4 - # two images side by sied with fixed padding. + return mtransforms.Bbox.from_bounds(x1, y1, w1, h1) - ax = fig1.add_subplot(2, 2, 4) - divider = make_axes_locatable(ax) - ax2 = divider.new_horizontal(size="100%", pad=0.05) - fig1.add_axes(ax2) - ax.imshow(Z, extent=extent, interpolation="nearest") - ax2.imshow(Z, extent=extent, interpolation="nearest") - plt.setp(ax2.get_yticklabels(), visible=False) - plt.draw() - plt.show() -def demo_fixed_size_axes(): - import matplotlib.pyplot as plt +class LocatableAxesBase: + def __init__(self, *kl, **kw): - fig2 = plt.figure(2, (6, 6)) + self._axes_class.__init__(self, *kl, **kw) - # The first items are for padding and the second items are for the axes. - # sizes are in inch. - h = [Size.Fixed(1.0), Size.Fixed(4.5)] - v = [Size.Fixed(0.7), Size.Fixed(5.)] + self._locator = None + self._locator_renderer = None - divider = Divider(fig2, (0.0, 0.0, 1., 1.), h, v, aspect=False) - # the width and height of the rectangle is ignored. + def set_axes_locator(self, locator): + self._locator = locator - ax = LocatableAxes(fig2, divider.get_position()) - ax.set_axes_locator(divider.new_locator(nx=1, ny=1)) + def get_axes_locator(self): + return self._locator + + def apply_aspect(self, position=None): + + if self.get_axes_locator() is None: + self._axes_class.apply_aspect(self, position) + else: + pos = self.get_axes_locator()(self, self._locator_renderer) + self._axes_class.apply_aspect(self, position=pos) + + + def draw(self, renderer=None, inframe=False): - fig2.add_axes(ax) + self._locator_renderer = renderer + + self._axes_class.draw(self, renderer, inframe) - ax.plot([1,2,3]) - plt.draw() - plt.show() - #plt.colorbar(im, cax=ax_cb) +_locatableaxes_classes = {} +def locatable_axes_factory(axes_class): + new_class = _locatableaxes_classes.get(axes_class) + if new_class is None: + new_class = new.classobj("Locatable%s" % (axes_class.__name__), + (LocatableAxesBase, axes_class), + {'_axes_class': axes_class}) + _locatableaxes_classes[axes_class] = new_class + return new_class + +#if hasattr(maxes.Axes, "get_axes_locator"): +# LocatableAxes = maxes.Axes +#else: + +def make_axes_locatable(axes): + if not hasattr(axes, "set_axes_locator"): + new_class = locatable_axes_factory(type(axes)) + axes.__class__ = new_class + + divider = AxesDivider(axes) + locator = divider.new_locator(nx=0, ny=0) + axes.set_axes_locator(locator) + + return divider + +def make_axes_area_auto_adjustable(ax, + use_axes=None, pad=0.1, + adjust_dirs=["left", "right", "bottom", "top"]): + + divider = make_axes_locatable(ax) + + if use_axes is None: + use_axes = ax + + divider.add_auto_adjustable_area(use_axes=use_axes, pad=pad, + adjust_dirs=adjust_dirs) + +#from matplotlib.axes import Axes +from mpl_axes import Axes +LocatableAxes = locatable_axes_factory(Axes) -if __name__ == "__main__": - demo_locatable_axes() - demo_fixed_size_axes() diff --git a/lib/mpl_toolkits/axes_grid1/axes_grid.py b/lib/mpl_toolkits/axes_grid1/axes_grid.py index 5f8c464d76b0..bd5c2469ddf3 100644 --- a/lib/mpl_toolkits/axes_grid1/axes_grid.py +++ b/lib/mpl_toolkits/axes_grid1/axes_grid.py @@ -187,7 +187,7 @@ def __init__(self, fig, Keyword Default Description ================ ======== ========================================= direction "row" [ "row" | "column" ] - axes_pad 0.02 float| pad betweein axes given in inches + axes_pad 0.02 float| pad between axes given in inches add_all True [ True | False ] share_all False [ True | False ] share_x True [ True | False ] @@ -413,6 +413,24 @@ def set_label_mode(self, mode): ax = self.axes_llc _tick_only(ax, bottom_on=False, left_on=False) + def set_axes_locator(self, locator): + self._divider.set_locator(locator) + + def get_axes_locator(self): + return self._divider.get_locator() + + def get_vsize_hsize(self): + + return self._divider.get_vsize_hsize() +# from axes_size import AddList + +# vsize = AddList(self._divider.get_vertical()) +# hsize = AddList(self._divider.get_horizontal()) + +# return vsize, hsize + + + class ImageGrid(Grid): """ @@ -457,7 +475,7 @@ def __init__(self, fig, Keyword Default Description ================ ======== ========================================= direction "row" [ "row" | "column" ] - axes_pad 0.02 float| pad betweein axes given in inches + axes_pad 0.02 float| pad between axes given in inches add_all True [ True | False ] share_all False [ True | False ] aspect True [ True | False ] diff --git a/lib/mpl_toolkits/axes_grid1/axes_size.py b/lib/mpl_toolkits/axes_grid1/axes_size.py index 82dc26fa3e77..0eda2c822335 100644 --- a/lib/mpl_toolkits/axes_grid1/axes_size.py +++ b/lib/mpl_toolkits/axes_grid1/axes_size.py @@ -1,6 +1,6 @@ """ -provides a classese of simlpe units that will be used with AxesDivider +provides a classes of simple units that will be used with AxesDivider class (or others) to determine the size of each axes. The unit classes define `get_size` method that returns a tuple of two floats, meaning relative and absolute sizes, respectively. @@ -12,11 +12,43 @@ class (or others) to determine the size of each axes. The unit """ import matplotlib.cbook as cbook - +from matplotlib.axes import Axes class _Base(object): "Base class" - pass + + def __rmul__(self, other): + float(other) # just to check if number if given + return Fraction(other, self) + + def __add__(self, other): + if isinstance(other, _Base): + return Add(self, other) + else: + float(other) + other = Fixed(other) + return Add(self, other) + + +class Add(_Base): + def __init__(self, a, b): + self._a = a + self._b = b + + def get_size(self, renderer): + a_rel_size, a_abs_size = self._a.get_size(renderer) + b_rel_size, b_abs_size = self._b.get_size(renderer) + return a_rel_size + b_rel_size, a_abs_size + b_abs_size + +class AddList(_Base): + def __init__(self, add_list): + self._list = add_list + + def get_size(self, renderer): + sum_rel_size = sum([a.get_size(renderer)[0] for a in self._list]) + sum_abs_size = sum([a.get_size(renderer)[1] for a in self._list]) + return sum_rel_size, sum_abs_size + class Fixed(_Base): "Simple fixed size with absolute part = *fixed_size* and relative part = 0" @@ -206,3 +238,54 @@ def from_any(size, fraction_ref=None): raise ValueError("Unknown format") +class SizeFromFunc(_Base): + def __init__(self, func): + self._func = func + + def get_size(self, renderer): + rel_size = 0. + + bb = self._func(renderer) + dpi = renderer.points_to_pixels(72.) + abs_size = bb/dpi + + return rel_size, abs_size + +class GetExtentHelper(object): + def _get_left(tight_bbox, axes_bbox): + return axes_bbox.xmin - tight_bbox.xmin + + def _get_right(tight_bbox, axes_bbox): + return tight_bbox.xmax - axes_bbox.xmax + + def _get_bottom(tight_bbox, axes_bbox): + return axes_bbox.ymin - tight_bbox.ymin + + def _get_top(tight_bbox, axes_bbox): + return tight_bbox.ymax - axes_bbox.ymax + + _get_func_map = dict(left=_get_left, + right=_get_right, + bottom=_get_bottom, + top=_get_top) + + del _get_left, _get_right, _get_bottom, _get_top + + def __init__(self, ax, direction): + if isinstance(ax, Axes): + self._ax_list = [ax] + else: + self._ax_list = ax + + try: + self._get_func = self._get_func_map[direction] + except KeyError: + print "direction must be one of left, right, bottom, top" + raise + + def __call__(self, renderer): + vl = [self._get_func(ax.get_tightbbox(renderer), + ax.bbox) for ax in self._ax_list] + return max(vl) + + diff --git a/lib/mpl_toolkits/axes_grid1/colorbar.py b/lib/mpl_toolkits/axes_grid1/colorbar.py index 36c9d26e5ecc..ba56af317864 100644 --- a/lib/mpl_toolkits/axes_grid1/colorbar.py +++ b/lib/mpl_toolkits/axes_grid1/colorbar.py @@ -162,7 +162,7 @@ class CbarAxesLocator(object): """ - CbarAxesLocator is a axes_locator for colobar axes. It adjust the + CbarAxesLocator is a axes_locator for colorbar axes. It adjust the position of the axes to make a room for extended ends, i.e., the extended ends are located outside the axes area. """ @@ -194,8 +194,8 @@ def get_original_position(self, axes, renderer): def get_end_vertices(self): """ return a tuple of two vertices for the colorbar extended ends. - The first vertives is for min. end, and the second is for - max. end. + The first vertices is for the minimum end, and the second is for + the maximum end. """ # Note that concatenating two vertices needs to make a # vertices for the frame. @@ -390,7 +390,7 @@ def __init__(self, ax, cmap=None, def _get_colorbar_limits(self): """ - initial limits for colorbar range. The returne min, max values + initial limits for colorbar range. The returned min, max values will be used to create colorbar solid(?) and etc. """ if self.boundaries is not None: @@ -407,7 +407,7 @@ def _get_colorbar_limits(self): def _config_axes(self): ''' - Adjust the properties of the axes to be adquate for colorbar display. + Adjust the properties of the axes to be adequate for colorbar display. ''' ax = self.ax @@ -416,7 +416,7 @@ def _config_axes(self): orientation=self.orientation) ax.set_axes_locator(axes_locator) - # overide the get_data_ratio for the aspect works. + # override the get_data_ratio for the aspect works. def _f(): return 1. ax.get_data_ratio = _f @@ -559,7 +559,7 @@ def _add_solids(self, X, Y, C): def add_lines(self, levels, colors, linewidths): ''' - Draw lines on the colorbar. It deletes preexting lines. + Draw lines on the colorbar. It deletes preexisting lines. ''' del self.lines @@ -737,7 +737,7 @@ def add_lines(self, CS): def update_bruteforce(self, mappable): """ Update the colorbar artists to reflect the change of the - assocaited mappable. + associated mappable. """ self.update_artists() diff --git a/lib/mpl_toolkits/axes_grid1/inset_locator.py b/lib/mpl_toolkits/axes_grid1/inset_locator.py index 8fc4491d645e..488d9f2b00a0 100644 --- a/lib/mpl_toolkits/axes_grid1/inset_locator.py +++ b/lib/mpl_toolkits/axes_grid1/inset_locator.py @@ -143,7 +143,7 @@ def __call__(self, ax, renderer): class BboxPatch(Patch): def __init__(self, bbox, **kwargs): if "transform" in kwargs: - raise ValueError("trnasform should nt be set") + raise ValueError("transform should not be set") kwargs["transform"] = IdentityTransform() Patch.__init__(self, **kwargs) @@ -225,7 +225,7 @@ def __init__(self, bbox1, bbox2, loc1, loc2=None, **kwargs): """ if "transform" in kwargs: - raise ValueError("trnasform should nt be set") + raise ValueError("transform should not be set") kwargs["transform"] = IdentityTransform() Patch.__init__(self, **kwargs) diff --git a/lib/mpl_toolkits/axisartist/angle_helper.py b/lib/mpl_toolkits/axisartist/angle_helper.py index f3ff8bfd7a82..0d72a78542af 100644 --- a/lib/mpl_toolkits/axisartist/angle_helper.py +++ b/lib/mpl_toolkits/axisartist/angle_helper.py @@ -5,7 +5,7 @@ A = np.array -from grid_finder import ExtremeFinderSimple +from mpl_toolkits.axisartist.grid_finder import ExtremeFinderSimple def select_step_degree(dv): @@ -80,9 +80,6 @@ def select_step_sub(dv): # subarcsec or degree tmp = 10.**(int(math.log10(dv))-1.) - dv2 = dv/tmp - substep_limits_ = [1.5, 3., 7.] - substep_steps_ = [1. , 2., 5.] factor = 1./tmp @@ -99,13 +96,11 @@ def select_step_sub(dv): return step, factor -def select_step(v1, v2, nv, hour=False): +def select_step(v1, v2, nv, hour=False, include_last=True): if v1 > v2: v1, v2 = v2, v1 - A = np.array - dv = float(v2 - v1) / nv if hour: @@ -130,8 +125,8 @@ def select_step(v1, v2, nv, hour=False): levs = np.arange(math.floor(f1/step), math.ceil(f2/step)+0.5, 1, dtype="i") * step - # n : number valid levels. If there is a cycle, e.g., [0, 90, 180, - # 270, 360], the a grid line needs to be extend from 0 to 360, so + # n : number of valid levels. If there is a cycle, e.g., [0, 90, 180, + # 270, 360], the grid line needs to be extended from 0 to 360, so # we need to return the whole array. However, the last level (360) # needs to be ignored often. In this case, so we return n=4. @@ -141,38 +136,45 @@ def select_step(v1, v2, nv, hour=False): # we need to check the range of values # for example, -90 to 90, 0 to 360, - if factor == 1. and (levs[-1] >= levs[0]+cycle): # check for cycle nv = int(cycle / step) - levs = np.arange(0, nv, 1) * step + if include_last: + levs = levs[0] + np.arange(0, nv+1, 1) * step + else: + levs = levs[0] + np.arange(0, nv, 1) * step + n = len(levs) return np.array(levs), n, factor -def select_step24(v1, v2, nv): +def select_step24(v1, v2, nv, include_last=True): v1, v2 = v1/15., v2/15. - levs, n, factor = select_step(v1, v2, nv, hour=True) + levs, n, factor = select_step(v1, v2, nv, hour=True, + include_last=include_last) return levs*15., n, factor -def select_step360(v1, v2, nv): - return select_step(v1, v2, nv, hour=False) +def select_step360(v1, v2, nv, include_last=True): + return select_step(v1, v2, nv, hour=False, + include_last=include_last) class LocatorHMS(object): - def __init__(self, den): + def __init__(self, den, include_last=True): self.den = den + self._include_last = include_last def __call__(self, v1, v2): - return select_step24(v1, v2, self.den) + return select_step24(v1, v2, self.den, self._include_last) class LocatorDMS(object): - def __init__(self, den): + def __init__(self, den, include_last=True): self.den = den + self._include_last = include_last def __call__(self, v1, v2): - return select_step360(v1, v2, self.den) + return select_step360(v1, v2, self.den, self._include_last) class FormatterHMS(object): @@ -181,11 +183,12 @@ def __call__(self, direction, factor, values): # hour return [] #ss = [[-1, 1][v>0] for v in values] #not py24 compliant values = np.asarray(values) - ss = np.where(values>0, 1, -1) + ss = np.where(values>=0, 1, -1) values = np.abs(values)/15. if factor == 1: - return ["$%d^{\mathrm{h}}$" % (int(v),) for v in values] + return ["$%s%d^{\mathrm{h}}$" % ({1:"",-1:"-"}[s], int(v),) \ + for s, v in zip(ss, values)] elif factor == 60: return ["$%d^{\mathrm{h}}\,%02d^{\mathrm{m}}$" % (s*floor(v/60.), v%60) \ for s, v in zip(ss, values)] @@ -288,7 +291,7 @@ def __call__(self, transform_xy, x1, y1, x2, y2): get extreme values. x1, y1, x2, y2 in image coordinates (0-based) - nx, ny : number of dvision in each axis + nx, ny : number of divisions in each axis """ x_, y_ = np.linspace(x1, x2, self.nx), np.linspace(y1, y2, self.ny) x, y = np.meshgrid(x_, y_) @@ -347,3 +350,6 @@ def _adjust_extremes(self, lon_min, lon_max, lat_min, lat_max): print select_step360(20+21.2/60., 21+33.3/60., 5) print select_step360(20.5+21.2/3600., 20.5+33.3/3600., 5) print select_step360(20+21.2/60., 20+53.3/60., 5) + + print select_step(-180, 180, 10, hour=False) + print select_step(-12, 12, 10, hour=True) diff --git a/lib/mpl_toolkits/axisartist/axis_artist.py b/lib/mpl_toolkits/axisartist/axis_artist.py index 3b0204624e20..2e31d5bda138 100644 --- a/lib/mpl_toolkits/axisartist/axis_artist.py +++ b/lib/mpl_toolkits/axisartist/axis_artist.py @@ -10,7 +10,7 @@ The main artist class is a AxisArtist and a GridlinesCollection. The GridlinesCollection is responsible for drawing grid lines and the AxisArtist is responsible for all other artists. The AxisArtist class -has attributest that are associated with each type of artists. +has attributes that are associated with each type of artists. * line : axis line * major_ticks : major tick lines @@ -25,11 +25,14 @@ ax.axis["bottom"] -where *ax* is an instance of axes (mpl_toolkits.axislines.Axes). -Thus, ax.axis["bottom"].line is an artist associated with the axis line, and ax.axis["bottom"].major_ticks is an artist associated with the major tick lines. +where *ax* is an instance of axes (mpl_toolkits.axislines.Axes). Thus, +ax.axis["bottom"].line is an artist associated with the axis line, and +ax.axis["bottom"].major_ticks is an artist associated with the major tick +lines. You can change the colors, fonts, line widths, etc. of these artists -by calling sutable set method. For example, to change the color of the major ticks of the bottom axis to red, +by calling suitable set method. For example, to change the color of the major +ticks of the bottom axis to red, ax.axis["bottom"].major_ticks.set_color("r") @@ -39,7 +42,7 @@ axis_direction -------------- -AxisArtist, AxisLabel, TickLabels have *axis_drection* attribute, +AxisArtist, AxisLabel, TickLabels have *axis_direction* attribute, which adjusts the location, angle, etc.,. The *axis_direction* must be one of [left, right, bottom, top] and they follow the matplotlib convention for the rectangle axis. @@ -62,7 +65,7 @@ ticklabels angle 90 0 -90 180 axislabel angle 180 0 0 180 ticklabel va center baseline center baseline - axislabel va center top center btoom + axislabel va center top center bottom ticklabel ha right center right center axislabel ha right center right center @@ -177,7 +180,7 @@ def set_ref_artist(self, artist): self._ref_artist = artist def get_ref_artist(self): - raise RuntimeError("get_ref_artist must overriden") + raise RuntimeError("get_ref_artist must overridden") #return self._ref_artist def get_attribute_from_ref_artist(self, attr_name, default_value): @@ -257,7 +260,7 @@ def get_tick_out(self): def set_ticksize(self, ticksize): """ - set lenth of the ticks in points. + set length of the ticks in points. """ self._ticksize = ticksize @@ -490,7 +493,7 @@ def test_labelbase(): class AxisLabel(LabelBase, AttributeCopier): """ Axis Label. Derived from Text. The position of the text is updated - in the fly, so chaning text position has no effect. Otherwise, the + in the fly, so changing text position has no effect. Otherwise, the properties can be changed as a normal Text. To change the pad between ticklabels and axis label, use set_pad. @@ -587,7 +590,7 @@ def set_axis_direction(self, d): property left bottom right top ===================== ========== ========= ========== ========== axislabel angle 180 0 0 180 - axislabel va center top center btoom + axislabel va center top center bottom axislabel ha right center right center ===================== ========== ========= ========== ========== @@ -808,7 +811,7 @@ def get_window_extents(self, renderer): def get_texts_widths_heights_descents(self, renderer): """ - return a list of width, height, descent for ticklaels. + return a list of width, height, descent for ticklabels. """ whd_list = [] for (x, y), a, l in self._locs_angles_labels: @@ -981,7 +984,7 @@ def set_axis_direction(self, axis_direction): ticklabel va center baseline center baseline ticklabel ha right center right center axislabel angle 180 0 0 180 - axislabel va center top center btoom + axislabel va center top center bottom axislabel ha right center right center ===================== ========== ========= ========== ========== diff --git a/lib/mpl_toolkits/axisartist/axisline_style.py b/lib/mpl_toolkits/axisartist/axisline_style.py index 13c4fb76b592..8bf67d66c8e5 100644 --- a/lib/mpl_toolkits/axisartist/axisline_style.py +++ b/lib/mpl_toolkits/axisartist/axisline_style.py @@ -6,7 +6,7 @@ class _FancyAxislineStyle: class SimpleArrow(FancyArrowPatch): """ - The artist class that will be returend for SimpleArrow style. + The artist class that will be returned for SimpleArrow style. """ _ARROW_STYLE = "->" @@ -60,7 +60,7 @@ def set_path(self, path): def draw(self, renderer): """ Draw the axis line. - 1) transform the path to the display cooridnate. + 1) transform the path to the display coordinate. 2) extend the path to make a room for arrow 3) update the path of the FancyArrowPatch. 4) draw @@ -75,7 +75,7 @@ def draw(self, renderer): class FilledArrow(SimpleArrow): """ - The artist class that will be returend for SimpleArrow style. + The artist class that will be returned for SimpleArrow style. """ _ARROW_STYLE = "-|>" @@ -115,7 +115,7 @@ class _Base(object): def __init__(self): """ - initializtion. + initialization. """ super(AxislineStyle._Base, self).__init__() diff --git a/lib/mpl_toolkits/axisartist/axislines.py b/lib/mpl_toolkits/axisartist/axislines.py index cf09a550d24b..ef8ca0ef6e95 100644 --- a/lib/mpl_toolkits/axisartist/axislines.py +++ b/lib/mpl_toolkits/axisartist/axislines.py @@ -3,7 +3,7 @@ biggest difference is that the artists responsible to draw axis line, ticks, ticklabel and axis labels are separated out from the mpl's Axis class, which are much more than artists in the original -mpl. Originally, this change was motivated to support curvlinear +mpl. Originally, this change was motivated to support curvilinear grid. Here are a few reasons that I came up with new axes class. @@ -11,7 +11,7 @@ different ticks (tick locations and labels). This is not possible with the current mpl, although some twin axes trick can help. - * Curvelinear grid. + * Curvilinear grid. * angled ticks. @@ -109,7 +109,7 @@ def get_tick_transform(self, axes): return trans def get_tick_iterators(self, axes): - # iter : iteratoable object that yields (c, angle, l) where + # iter : iteratable object that yields (c, angle, l) where # c, angle, l is position, tick angle, and label return iter_major, iter_minor @@ -639,7 +639,7 @@ def _init_gridlines(self, grid_helper=None): self.axes._set_artist_props(gridlines) # gridlines.set_clip_path(self.axes.patch) - # set_clip_path need to be defered after Axes.cla is completed. + # set_clip_path need to be deferred after Axes.cla is completed. # It is done inside the cla. self.gridlines = gridlines @@ -662,7 +662,7 @@ def get_grid_helper(self): def grid(self, b=None, which='major', **kwargs): """ - Toggel the gridlines, and optionally set the properties of the lines. + Toggle the gridlines, and optionally set the properties of the lines. """ # their are some discrepancy between the behavior of grid in # axes_grid and the original mpl's grid, because axes_grid diff --git a/lib/mpl_toolkits/axisartist/clip_path.py b/lib/mpl_toolkits/axisartist/clip_path.py index bf3b06dd0821..f863468ce468 100644 --- a/lib/mpl_toolkits/axisartist/clip_path.py +++ b/lib/mpl_toolkits/axisartist/clip_path.py @@ -10,7 +10,7 @@ def atan2(dy, dx): return math.atan2(dy, dx) # FIXME : The current algorithm seems to return incorrect angle when the line -# ends at the boudnary. +# ends at the boundary. def clip(xlines, ylines, x0, clip="right"): diff --git a/lib/mpl_toolkits/axisartist/floating_axes.py b/lib/mpl_toolkits/axisartist/floating_axes.py index 111495bd34a4..e8d9afc5d3ab 100644 --- a/lib/mpl_toolkits/axisartist/floating_axes.py +++ b/lib/mpl_toolkits/axisartist/floating_axes.py @@ -1,5 +1,5 @@ """ -An experimental support for curvelinear grid. +An experimental support for curvilinear grid. """ @@ -225,7 +225,7 @@ def __call__(self, transform_xy, x1, y1, x2, y2): get extreme values. x1, y1, x2, y2 in image coordinates (0-based) - nx, ny : number of dvision in each axis + nx, ny : number of division in each axis """ #lon_min, lon_max, lat_min, lat_max = self._extremes return self._extremes @@ -241,7 +241,7 @@ def __init__(self, aux_trans, extremes, tick_formatter2=None): """ aux_trans : a transform from the source (curved) coordinate to - target (rectlinear) coordinate. An instance of MPL's Transform + target (rectilinear) coordinate. An instance of MPL's Transform (inverse transform should be defined) or a tuple of two callable objects which defines the transform and its inverse. The callables need take two arguments of array of source coordinates and @@ -320,7 +320,7 @@ def new_fixed_axis(self, loc, return axisline - # new_floating_axis will inheirt the grid_helper's extremes. + # new_floating_axis will inherit the grid_helper's extremes. # def new_floating_axis(self, nth_coord, # value, diff --git a/lib/mpl_toolkits/axisartist/grid_finder.py b/lib/mpl_toolkits/axisartist/grid_finder.py index c052e619331d..5a693811ee66 100644 --- a/lib/mpl_toolkits/axisartist/grid_finder.py +++ b/lib/mpl_toolkits/axisartist/grid_finder.py @@ -19,7 +19,7 @@ def __call__(self, transform_xy, x1, y1, x2, y2): get extreme values. x1, y1, x2, y2 in image coordinates (0-based) - nx, ny : number of dvision in each axis + nx, ny : number of division in each axis """ x_, y_ = np.linspace(x1, x2, self.nx), np.linspace(y1, y2, self.ny) x, y = np.meshgrid(x_, y_) @@ -31,9 +31,10 @@ def __call__(self, transform_xy, x1, y1, x2, y2): return self._add_pad(lon_min, lon_max, lat_min, lat_max) def _add_pad(self, lon_min, lon_max, lat_min, lat_max): - # a small amound of padding is added because the current - # clipping algorithms seems to fail when the gidline ends at - # the bbox boundary. + """ a small amount of padding is added because the current + clipping algorithms seems to fail when the gridline ends at + the bbox boundary. + """ dlon = (lon_max - lon_min) / self.nx dlat = (lat_max - lat_min) / self.ny @@ -205,7 +206,7 @@ def update(self, **kw): "tick_formatter2"]: setattr(self, k, kw[k]) else: - raise ValueError("unknwonw update property") + raise ValueError("unknown update property '%s'" % k) @@ -220,7 +221,7 @@ def __init__(self, tick_formatter1=None, tick_formatter2=None): """ - transform : transfrom from the image coordinate (which will be + transform : transform from the image coordinate (which will be the transData of the axes to the world coordinate. or transform = (transform_xy, inv_transform_xy) @@ -279,21 +280,27 @@ def set_factor(self, f): class FixedLocator(object): def __init__(self, locs): self._locs = locs + self._factor = None def __call__(self, v1, v2): - v1, v2 = sorted([v1, v2]) + if self._factor is None: + v1, v2 = sorted([v1, v2]) + else: + v1, v2 = sorted([v1*self._factor, v2*self._factor]) locs = np.array([l for l in self._locs if ((v1 <= l) and (l <= v2))]) - return locs, len(locs), None + return locs, len(locs), self._factor + def set_factor(self, f): + self._factor = f # Tick Formatter class FormatterPrettyPrint(object): - def __init__(self): - self._fmt = mticker.ScalarFormatter() + def __init__(self, useMathText=True): + self._fmt = mticker.ScalarFormatter(useMathText=useMathText) self._fmt.create_dummy_axis() self._ignore_factor = True diff --git a/lib/mpl_toolkits/axisartist/grid_helper_curvelinear.py b/lib/mpl_toolkits/axisartist/grid_helper_curvelinear.py index f05bedcd3cb0..3330f52be00c 100644 --- a/lib/mpl_toolkits/axisartist/grid_helper_curvelinear.py +++ b/lib/mpl_toolkits/axisartist/grid_helper_curvelinear.py @@ -1,5 +1,5 @@ """ -An experimental support for curvelinear grid. +An experimental support for curvilinear grid. """ from itertools import chain @@ -11,6 +11,8 @@ from matplotlib.transforms import Affine2D, IdentityTransform import numpy as np +from matplotlib.path import Path + class FixedAxisArtistHelper(AxisArtistHelper.Fixed): """ Helper class for a fixed axis. @@ -76,6 +78,8 @@ def __init__(self, grid_helper, nth_coord, value, axis_direction=None): self.grid_helper = grid_helper self._extremes = None, None + self._get_line_path = None # a method that returns a Path. + self._line_num_points = 100 # number of points to create a line def set_extremes(self, e1, e2): self._extremes = e1, e2 @@ -125,12 +129,12 @@ def update_lim(self, axes): #e1, e2 = self._extremes # ranges of other coordinates if self.nth_coord == 0: - xx0 = np.linspace(self.value, self.value, 100) - yy0 = np.linspace(extremes[2], extremes[3], 100) + xx0 = np.linspace(self.value, self.value, self._line_num_points) + yy0 = np.linspace(extremes[2], extremes[3], self._line_num_points) xx, yy = grid_finder.transform_xy(xx0, yy0) elif self.nth_coord == 1: - xx0 = np.linspace(extremes[0], extremes[1], 100) - yy0 = np.linspace(self.value, self.value, 100) + xx0 = np.linspace(extremes[0], extremes[1], self._line_num_points) + yy0 = np.linspace(self.value, self.value, self._line_num_points) xx, yy = grid_finder.transform_xy(xx0, yy0) grid_info["line_xy"] = xx, yy @@ -285,10 +289,12 @@ def get_line_transform(self, axes): def get_line(self, axes): self.update_lim(axes) - from matplotlib.path import Path - xx, yy = self.grid_info["line_xy"] + x, y = self.grid_info["line_xy"] - return Path(zip(xx, yy)) + if self._get_line_path is None: + return Path(zip(x, y)) + else: + return self._get_line_path(axes, x, y) @@ -303,7 +309,7 @@ def __init__(self, aux_trans, tick_formatter2=None): """ aux_trans : a transform from the source (curved) coordinate to - target (rectlinear) coordinate. An instance of MPL's Transform + target (rectilinear) coordinate. An instance of MPL's Transform (inverse transform should be defined) or a tuple of two callable objects which defines the transform and its inverse. The callables need take two arguments of array of source coordinates and diff --git a/lib/mpl_toolkits/gtktools.py b/lib/mpl_toolkits/gtktools.py index 7d7d646e08d6..b0daf60a99fe 100644 --- a/lib/mpl_toolkits/gtktools.py +++ b/lib/mpl_toolkits/gtktools.py @@ -158,7 +158,7 @@ def __call__(self, column): val = model.get_value(thisiter, self.i) try: val = float(val.strip().rstrip('%')) except ValueError: pass - if npy.isnan(val): val = npy.inf # force nan to sort uniquely + if mlab.safe_isnan(val): val = npy.inf # force nan to sort uniquely dsu.append((val, rownum)) dsu.sort() if not self.num%2: dsu.reverse() diff --git a/lib/mpl_toolkits/mplot3d/art3d.py b/lib/mpl_toolkits/mplot3d/art3d.py index 0b25c3976990..a355dd3514df 100644 --- a/lib/mpl_toolkits/mplot3d/art3d.py +++ b/lib/mpl_toolkits/mplot3d/art3d.py @@ -291,8 +291,17 @@ def __init__(self, *args, **kwargs): PatchCollection.__init__(self, *args, **kwargs) self._old_draw = lambda x: PatchCollection.draw(self, x) + def set_sort_zpos(self,val): + '''Set the position to use for z-sorting.''' + self._sort_zpos = val + def set_3d_properties(self, zs, zdir): - xs, ys = zip(*self.get_offsets()) + offsets = self.get_offsets() + if len(offsets) > 0: + xs, ys = zip(*self.get_offsets()) + else: + xs = [0] * len(zs) + ys = [0] * len(zs) self._offsets3d = juggle_axes(xs, ys, zs, zdir) self._facecolor3d = self.get_facecolor() self._edgecolor3d = self.get_edgecolor() diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index 9aef760b0e10..531182b3811e 100644 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -58,7 +58,6 @@ def __init__(self, fig, rect=None, *args, **kwargs): if rect is None: rect = [0.0, 0.0, 1.0, 1.0] - self.fig = fig self._cids = [] self.initial_azim = kwargs.pop('azim', -60) @@ -72,19 +71,27 @@ def __init__(self, fig, rect=None, *args, **kwargs): # they can't be defined until Axes.__init__ has been called self.view_init(self.initial_elev, self.initial_azim) self._ready = 0 - Axes.__init__(self, self.fig, rect, - frameon=True, - xticks=[], yticks=[], *args, **kwargs) + Axes.__init__(self, fig, rect, + frameon=True, + *args, **kwargs) + # Disable drawing of axes by base class + Axes.set_axis_off(self) + self._axis3don = True self.M = None self._ready = 1 self.mouse_init() - self.create_axes() self.set_top_view() self.axesPatch.set_linewidth(0) - self.fig.add_axes(self) + self.figure.add_axes(self) + + def set_axis_off(self): + self._axis3don = False + + def set_axis_on(self): + self._axis3don = True def set_top_view(self): # this happens to be the right view for the viewing coordinates @@ -97,13 +104,23 @@ def set_top_view(self): Axes.set_xlim(self, -xdwl, xdw, auto=None) Axes.set_ylim(self, -ydwl, ydw, auto=None) - def create_axes(self): + def _init_axis(self): + '''Init 3d axes; overrides creation of regular X/Y axes''' self.w_xaxis = axis3d.XAxis('x', self.xy_viewLim.intervalx, self.xy_dataLim.intervalx, self) + self.xaxis = self.w_xaxis self.w_yaxis = axis3d.YAxis('y', self.xy_viewLim.intervaly, self.xy_dataLim.intervaly, self) + self.yaxis = self.w_yaxis self.w_zaxis = axis3d.ZAxis('z', self.zz_viewLim.intervalx, self.zz_dataLim.intervalx, self) + self.zaxis = self.w_zaxis + + for ax in self.xaxis, self.yaxis, self.zaxis: + ax.init3d() + + def get_children(self): + return [self.zaxis,] + Axes.get_children(self) def unit_cube(self, vals=None): minx, maxx, miny, maxy, minz, maxz = vals or self.get_w_lims() @@ -165,12 +182,16 @@ def draw(self, renderer): for i, (z, patch) in enumerate(zlist): patch.zorder = i - axes = (self.w_xaxis, self.w_yaxis, self.w_zaxis) - for ax in axes: - ax.draw_pane(renderer) - for ax in axes: - ax.draw(renderer) + if self._axis3don: + axes = (self.w_xaxis, self.w_yaxis, self.w_zaxis) + # Draw panes first + for ax in axes: + ax.draw_pane(renderer) + # Then axes + for ax in axes: + ax.draw(renderer) + # Then rest Axes.draw(self, renderer) def get_axis_position(self): @@ -204,7 +225,7 @@ def auto_scale_xyz(self, X, Y, Z=None, had_data=None): self.autoscale_view() def autoscale_view(self, scalex=True, scaley=True, scalez=True, **kw): - # This method looks at the rectanglular volume (see above) + # This method looks at the rectangular volume (see above) # of data and decides how to scale the view portal to fit it. self.set_top_view() @@ -240,18 +261,21 @@ def set_xlim3d(self, *args, **kwargs): lims = self._determine_lims(*args, **kwargs) self.xy_viewLim.intervalx = lims return lims + set_xlim = set_xlim3d def set_ylim3d(self, *args, **kwargs): '''Set 3D y limits.''' lims = self._determine_lims(*args, **kwargs) self.xy_viewLim.intervaly = lims return lims + set_ylim = set_ylim3d def set_zlim3d(self, *args, **kwargs): '''Set 3D z limits.''' lims = self._determine_lims(*args, **kwargs) self.zz_viewLim.intervalx = lims return lims + set_zlim = set_zlim3d def get_xlim3d(self): '''Get 3D x limits.''' @@ -264,6 +288,51 @@ def get_ylim3d(self): def get_zlim3d(self): '''Get 3D z limits.''' return self.zz_viewLim.intervalx + get_zlim = get_zlim3d + + def set_zticks(self, *args, **kwargs): + """ + Set z-axis tick locations. + See set_xticks for more details. + + Note that minor ticks are not supported at this time. + """ + return self.w_zaxis.set_ticks(*args, **kwargs) + + def get_zticks(self, *args, **kwargs): + """ + Get the z-axis tick objects. + See get_xticks for more details. + + Note that minor ticks are not supported at this time. + """ + return self.w_zaxis.get_ticks(*args, **kwargs) + + def set_zticklabels(self, *args, **kwargs) : + """ + Set z-axis tick labels. + See set_xticklabels for more details. + + Note that minor ticks are not supported at this time. + """ + return self.w_zaxis.set_ticklabels(*args, **kwargs) + + def get_zticklabels(self, *args, **kwargs) : + """ + Get ztick labels as a list of Text instances. + Set get_xticklabels for more details. + + Note that minor ticks are not supported at this time. + """ + return self.w_zaxis.get_ticklabels(*args, **kwargs) + + def get_zticklines(self) : + """ + Get ztick lines as a list of Line2D instances. + Note that this function is provided merely for completeness. + These lines are re-calculated as the display changes. + """ + return self.w_zaxis.get_ticklines() def clabel(self, *args, **kwargs): return None @@ -661,7 +730,6 @@ def plot_surface(self, X, Y, Z, *args, **kwargs): had_data = self.has_data() rows, cols = Z.shape - tX, tY, tZ = np.transpose(X), np.transpose(Y), np.transpose(Z) rstride = kwargs.pop('rstride', 10) cstride = kwargs.pop('cstride', 10) @@ -684,21 +752,27 @@ def plot_surface(self, X, Y, Z, *args, **kwargs): fcolors = self._shade_colors_lightsource(Z, cmap, lightsource) polys = [] - normals = [] + # Only need these vectors to shade if there is no cmap + if cmap is None and shade : + totpts = int(np.ceil(float(rows - 1) / rstride) * + np.ceil(float(cols - 1) / cstride)) + v1 = np.empty((totpts, 3)) + v2 = np.empty((totpts, 3)) + # This indexes the vertex points + which_pt = 0 + + #colset contains the data for coloring: either average z or the facecolor colset = [] - for rs in np.arange(0, rows-1, rstride): - for cs in np.arange(0, cols-1, cstride): + for rs in xrange(0, rows-1, rstride): + for cs in xrange(0, cols-1, cstride): ps = [] - corners = [] - for a, ta in [(X, tX), (Y, tY), (Z, tZ)]: - ztop = a[rs][cs:min(cols, cs+cstride+1)] - zleft = ta[min(cols-1, cs+cstride)][rs:min(rows, rs+rstride+1)] - zbase = a[min(rows-1, rs+rstride)][cs:min(cols, cs+cstride+1):] - zbase = zbase[::-1] - zright = ta[cs][rs:min(rows, rs+rstride+1):] - zright = zright[::-1] - corners.append([ztop[0], ztop[-1], zbase[0], zbase[-1]]) + for a in (X, Y, Z) : + ztop = a[rs,cs:min(cols, cs+cstride+1)] + zleft = a[rs+1:min(rows, rs+rstride+1), + min(cols-1, cs+cstride)] + zbase = a[min(rows-1, rs+rstride), cs:min(cols, cs+cstride+1):][::-1] + zright = a[rs:min(rows-1, rs+rstride):, cs][::-1] z = np.concatenate((ztop, zleft, zbase, zright)) ps.append(z) @@ -706,13 +780,8 @@ def plot_surface(self, X, Y, Z, *args, **kwargs): # are removed here. ps = zip(*ps) lastp = np.array([]) - ps2 = [] - avgzsum = 0.0 - for p in ps: - if p != lastp: - ps2.append(p) - lastp = p - avgzsum += p[2] + ps2 = [ps[0]] + [ps[i] for i in xrange(1, len(ps)) if ps[i] != ps[i-1]] + avgzsum = sum(p[2] for p in ps2) polys.append(ps2) if fcolors is not None: @@ -722,9 +791,14 @@ def plot_surface(self, X, Y, Z, *args, **kwargs): # Only need vectors to shade if no cmap if cmap is None and shade: - v1 = np.array(ps2[0]) - np.array(ps2[1]) - v2 = np.array(ps2[2]) - np.array(ps2[0]) - normals.append(np.cross(v1, v2)) + i1, i2, i3 = 0, int(len(ps2)/3), int(2*len(ps2)/3) + v1[which_pt] = np.array(ps2[i1]) - np.array(ps2[i2]) + v2[which_pt] = np.array(ps2[i2]) - np.array(ps2[i3]) + which_pt += 1 + if cmap is None and shade: + normals = np.cross(v1, v2) + else : + normals = [] polyc = art3d.Poly3DCollection(polys, *args, **kwargs) @@ -772,24 +846,19 @@ def _shade_colors(self, color, normals): *color* can also be an array of the same length as *normals*. ''' - shade = [] - for n in normals: - n = n / proj3d.mod(n) - shade.append(np.dot(n, [-1, -1, 0.5])) - - shade = np.array(shade) + shade = np.array([np.dot(n / proj3d.mod(n), [-1, -1, 0.5]) + for n in normals]) mask = ~np.isnan(shade) if len(shade[mask]) > 0: norm = Normalize(min(shade[mask]), max(shade[mask])) - if art3d.iscolor(color): - color = color.copy() - color[3] = 1 - colors = [color * (0.5 + norm(v) * 0.5) for v in shade] - else: - colors = [np.array(colorConverter.to_rgba(c)) * \ - (0.5 + norm(v) * 0.5) \ - for c, v in zip(color, shade)] + color = colorConverter.to_rgba_array(color) + # shape of color should be (M, 4) (where M is number of faces) + # shape of shade should be (M,) + # colors should have final shape of (M, 4) + alpha = color[:, 3] + colors = (0.5 + norm(shade)[:, np.newaxis] * 0.5) * color + colors[:, 3] = alpha else: colors = color.copy() @@ -896,6 +965,24 @@ def _3d_extend_contour(self, cset, stride=5): for col in colls: self.collections.remove(col) + def add_contour_set(self, cset, extend3d=False, stride=5, zdir='z', offset=None): + zdir = '-' + zdir + if extend3d: + self._3d_extend_contour(cset, stride) + else: + for z, linec in zip(cset.levels, cset.collections): + if offset is not None: + z = offset + art3d.line_collection_2d_to_3d(linec, z, zdir=zdir) + + def add_contourf_set(self, cset, zdir='z', offset=None) : + zdir = '-' + zdir + for z, linec in zip(cset.levels, cset.collections) : + if offset is not None : + z = offset + art3d.poly_collection_2d_to_3d(linec, z, zdir=zdir) + linec.set_sort_zpos(z) + def contour(self, X, Y, Z, *args, **kwargs): ''' Create a 3D contour plot. @@ -927,26 +1014,62 @@ def contour(self, X, Y, Z, *args, **kwargs): jX, jY, jZ = art3d.rotate_axes(X, Y, Z, zdir) cset = Axes.contour(self, jX, jY, jZ, *args, **kwargs) - - zdir = '-' + zdir - if extend3d: - self._3d_extend_contour(cset, stride) - else: - for z, linec in zip(cset.levels, cset.collections): - if offset is not None: - z = offset - art3d.line_collection_2d_to_3d(linec, z, zdir=zdir) + self.add_contour_set(cset, extend3d, stride, zdir, offset) self.auto_scale_xyz(X, Y, Z, had_data) return cset contour3D = contour + def tricontour(self, X, Y, Z, *args, **kwargs): + ''' + Create a 3D contour plot. + + ========== ================================================ + Argument Description + ========== ================================================ + *X*, *Y*, Data values as numpy.arrays + *Z* + *extend3d* Whether to extend contour in 3D (default: False) + *stride* Stride (step size) for extending contour + *zdir* The direction to use: x, y or z (default) + *offset* If specified plot a projection of the contour + lines on this position in plane normal to zdir + ========== ================================================ + + Other keyword arguments are passed on to + :func:`~matplotlib.axes.Axes.tricontour` + + Returns a :class:`~matplotlib.axes.Axes.contour` + ''' + + extend3d = kwargs.pop('extend3d', False) + stride = kwargs.pop('stride', 5) + zdir = kwargs.pop('zdir', 'z') + offset = kwargs.pop('offset', None) + + had_data = self.has_data() + + jX, jY, jZ = art3d.rotate_axes(X, Y, Z, zdir) + cset = Axes.tricontour(self, jX, jY, jZ, *args, **kwargs) + self.add_contour_set(cset, extend3d, stride, zdir, offset) + + self.auto_scale_xyz(X, Y, Z, had_data) + return cset + def contourf(self, X, Y, Z, *args, **kwargs): ''' - Plot filled 3D contours. + Create a 3D contourf plot. - *X*, *Y*, *Z*: data points. + ========== ================================================ + Argument Description + ========== ================================================ + *X*, *Y*, Data values as numpy.arrays + *Z* + *zdir* The direction to use: x, y or z (default) + *offset* If specified plot a projection of the filled contour + on this position in plane normal to zdir + ========== ================================================ The positional and keyword arguments are passed on to :func:`~matplotlib.axes.Axes.contourf` @@ -954,20 +1077,50 @@ def contourf(self, X, Y, Z, *args, **kwargs): Returns a :class:`~matplotlib.axes.Axes.contourf` ''' + zdir = kwargs.pop('zdir', 'z') + offset = kwargs.pop('offset', None) + had_data = self.has_data() - cset = Axes.contourf(self, X, Y, Z, *args, **kwargs) - levels = cset.levels - colls = cset.collections - for z1, z2, linec in zip(levels, levels[1:], colls): - art3d.poly_collection_2d_to_3d(linec, z1) - linec.set_sort_zpos(z1) + jX, jY, jZ = art3d.rotate_axes(X, Y, Z, zdir) + cset = Axes.contourf(self, jX, jY, jZ, *args, **kwargs) + self.add_contourf_set(cset, zdir, offset) self.auto_scale_xyz(X, Y, Z, had_data) return cset contourf3D = contourf + def tricontourf(self, X, Y, Z, offset=None, zdir='z', *args, **kwargs): + ''' + Create a 3D contourf plot. + + ========== ================================================ + Argument Description + ========== ================================================ + *X*, *Y*, Data values as numpy.arrays + *Z* + *extend3d* Whether to extend contour in 3D (default: False) + *stride* Stride (step size) for extending contour + *zdir* The direction to use: x, y or z (default) + *offset* If specified plot a projection of the contour + lines on this position in plane normal to zdir + ========== ================================================ + + Other keyword arguments are passed on to + :func:`~matplotlib.axes.Axes.tricontour` + + Returns a :class:`~matplotlib.axes.Axes.contour` + ''' + + had_data = self.has_data() + + cset = Axes.tricontourf(self, X, Y, Z, *args, **kwargs) + self.add_contourf_set(cset, zdir, offset) + + self.auto_scale_xyz(X, Y, Z, had_data) + return cset + def add_collection3d(self, col, zs=0, zdir='z'): ''' Add a 3d collection object to the plot. @@ -993,20 +1146,31 @@ def add_collection3d(self, col, zs=0, zdir='z'): Axes.add_collection(self, col) - def scatter(self, xs, ys, zs=0, zdir='z', *args, **kwargs): + def scatter(self, xs, ys, zs=0, zdir='z', s=20, c='b', *args, **kwargs): ''' Create a scatter plot. - ========== ================================================ + ========== ========================================================== Argument Description - ========== ================================================ + ========== ========================================================== *xs*, *ys* Positions of data points. *zs* Either an array of the same length as *xs* and *ys* or a single value to place all points in the same plane. Default is 0. *zdir* Which direction to use as z ('x', 'y' or 'z') when plotting a 2d set. - ========== ================================================ + *s* size in points^2. It is a scalar or an array of the same + length as *x* and *y*. + + *c* a color. *c* can be a single color format string, or a + sequence of color specifications of length *N*, or a + sequence of *N* numbers to be mapped to colors using the + *cmap* and *norm* specified via kwargs (see below). Note + that *c* should not be a single numeric RGB or RGBA + sequence because that is indistinguishable from an array + of values to be colormapped. *c* can be a 2-D array in + which the rows are RGB or RGBA, however. + ========== ========================================================== Keyword arguments are passed on to :func:`~matplotlib.axes.Axes.scatter`. @@ -1016,7 +1180,25 @@ def scatter(self, xs, ys, zs=0, zdir='z', *args, **kwargs): had_data = self.has_data() - patches = Axes.scatter(self, xs, ys, *args, **kwargs) + xs = np.ma.ravel(xs) + ys = np.ma.ravel(ys) + zs = np.ma.ravel(zs) + if xs.size != ys.size: + raise ValueError("x and y must be the same size") + if xs.size != zs.size and zs.size == 1: + zs = np.array(zs[0] * xs.size) + + s = np.ma.ravel(s) # This doesn't have to match x, y in size. + + cstr = cbook.is_string_like(c) or cbook.is_sequence_of_strings(c) + if not cstr: + c = np.asanyarray(c) + if c.size == xs.size: + c = np.ma.ravel(c) + + xs, ys, zs, s, c = cbook.delete_masked_points(xs, ys, zs, s, c) + + patches = Axes.scatter(self, xs, ys, s=s, c=c, *args, **kwargs) if not cbook.iterable(zs): is_2d = True zs = np.ones(len(xs)) * zs diff --git a/lib/mpl_toolkits/mplot3d/axis3d.py b/lib/mpl_toolkits/mplot3d/axis3d.py index 35e4f0eaeb2e..033b939ec1d6 100644 --- a/lib/mpl_toolkits/mplot3d/axis3d.py +++ b/lib/mpl_toolkits/mplot3d/axis3d.py @@ -73,6 +73,11 @@ def __init__(self, adir, v_intervalx, d_intervalx, axes, *args, **kwargs): self.v_interval = v_intervalx maxis.XAxis.__init__(self, axes, *args, **kwargs) + + self.set_rotate_label(kwargs.get('rotate_label', None)) + + + def init3d(self): self.line = mlines.Line2D(xdata=(0, 0), ydata=(0, 0), linewidth=0.75, color=(0, 0, 0, 1), @@ -80,20 +85,21 @@ def __init__(self, adir, v_intervalx, d_intervalx, axes, *args, **kwargs): ) # Store dummy data in Polygon object - self.has_pane = True self.pane = mpatches.Polygon(np.array([[0,0], [0,1], [1,0], [0,0]]), alpha=0.8, facecolor=(1,1,1,0), edgecolor=(1,1,1,0)) + self.set_pane_color(self._AXINFO[self.adir]['color']) self.axes._set_artist_props(self.line) self.axes._set_artist_props(self.pane) self.gridlines = art3d.Line3DCollection([], ) self.axes._set_artist_props(self.gridlines) self.axes._set_artist_props(self.label) + self.axes._set_artist_props(self.offsetText) # Need to be able to place the label at the correct location self.label._transform = self.axes.transData - self.set_rotate_label(kwargs.get('rotate_label', None)) + self.offsetText._transform = self.axes.transData def get_tick_positions(self): majorLocs = self.major.locator() @@ -111,14 +117,16 @@ def get_major_ticks(self, numticks=None): t.label2.set_transform(self.axes.transData) return ticks - def set_pane(self, xys, color): - if self.has_pane: - xys = np.asarray(xys) - xys = xys[:,:2] - self.pane.xy = xys - self.pane.set_edgecolor(color) - self.pane.set_facecolor(color) - self.pane.set_alpha(color[-1]) + def set_pane_pos(self, xys): + xys = np.asarray(xys) + xys = xys[:,:2] + self.pane.xy = xys + + def set_pane_color(self, color): + '''Set pane color to a RGBA tuple''' + self.pane.set_edgecolor(color) + self.pane.set_facecolor(color) + self.pane.set_alpha(color[-1]) def set_rotate_label(self, val): ''' @@ -162,7 +170,7 @@ def draw_pane(self, renderer): else: plane = self._PLANES[2 * index + 1] xys = [tc[p] for p in plane] - self.set_pane(xys, info['color']) + self.set_pane_pos(xys) self.pane.draw(renderer) renderer.close_group('pane3d') @@ -199,6 +207,7 @@ def draw(self, renderer): edgep2 = edgep1.copy() edgep2[juggled[1]] = get_flip_min_max(edgep2, juggled[1], mins, maxs) pep = proj3d.proj_trans_points([edgep1, edgep2], renderer.M) + centpt = proj3d.proj_transform(centers[0], centers[1], centers[2], renderer.M) self.line.set_data((pep[0][0], pep[0][1]), (pep[1][0], pep[1][1])) self.line.draw(renderer) @@ -237,25 +246,95 @@ def draw(self, renderer): self.label.set_ha('center') self.label.draw(renderer) - # Grid points at end of one plane - xyz1 = copy.deepcopy(xyz0) - newindex = (index + 1) % 3 - newval = get_flip_min_max(xyz1[0], newindex, mins, maxs) - for i in range(len(majorLocs)): - xyz1[i][newindex] = newval - - # Grid points at end of the other plane - xyz2 = copy.deepcopy(xyz0) - newindex = (index + 2) % 3 - newval = get_flip_min_max(xyz2[0], newindex, mins, maxs) - for i in range(len(majorLocs)): - xyz2[i][newindex] = newval - - lines = zip(xyz1, xyz0, xyz2) - if self.axes._draw_grid: - self.gridlines.set_segments(lines) - self.gridlines.set_color([(0.9,0.9,0.9,1)] * len(lines)) - self.gridlines.draw(renderer, project=True) + + # Draw Offset text + + # Which of the two edge points do we want to + # use for locating the offset text? + if juggled[2] == 2 : + outeredgep = edgep1 + outerindex = 0 + else : + outeredgep = edgep2 + outerindex = 1 + + pos = copy.copy(outeredgep) + pos = move_from_center(pos, centers, labeldeltas, axmask) + olx, oly, olz = proj3d.proj_transform(pos[0], pos[1], pos[2], renderer.M) + self.offsetText.set_text( self.major.formatter.get_offset() ) + self.offsetText.set_position( (olx, oly) ) + angle = art3d.norm_text_angle(math.degrees(math.atan2(dy, dx))) + self.offsetText.set_rotation(angle) + # Must set rotation mode to "anchor" so that + # the alignment point is used as the "fulcrum" for rotation. + self.offsetText.set_rotation_mode('anchor') + + #----------------------------------------------------------------------- + # Note: the following statement for determining the proper alignment of + # the offset text. This was determined entirely by trial-and-error + # and should not be in any way considered as "the way". There are + # still some edge cases where alignment is not quite right, but + # this seems to be more of a geometry issue (in other words, I + # might be using the wrong reference points). + # + # (TT, FF, TF, FT) are the shorthand for the tuple of + # (centpt[info['tickdir']] <= peparray[info['tickdir'], outerindex], + # centpt[index] <= peparray[index, outerindex]) + # + # Three-letters (e.g., TFT, FTT) are short-hand for the array + # of bools from the variable 'highs'. + # --------------------------------------------------------------------- + if centpt[info['tickdir']] > peparray[info['tickdir'], outerindex] : + # if FT and if highs has an even number of Trues + if (centpt[index] <= peparray[index, outerindex] + and ((len(highs.nonzero()[0]) % 2) == 0)) : + # Usually, this means align right, except for the FTT case, + # in which offset for axis 1 and 2 are aligned left. + if highs.tolist() == [False, True, True] and index in (1, 2) : + align = 'left' + else : + align = 'right' + else : + # The FF case + align = 'left' + else : + # if TF and if highs has an even number of Trues + if (centpt[index] > peparray[index, outerindex] + and ((len(highs.nonzero()[0]) % 2) == 0)) : + # Usually mean align left, except if it is axis 2 + if index == 2 : + align = 'right' + else : + align = 'left' + else : + # The TT case + align = 'right' + + self.offsetText.set_va('center') + self.offsetText.set_ha(align) + self.offsetText.draw(renderer) + + # Draw grid lines + if len(xyz0) > 0: + # Grid points at end of one plane + xyz1 = copy.deepcopy(xyz0) + newindex = (index + 1) % 3 + newval = get_flip_min_max(xyz1[0], newindex, mins, maxs) + for i in range(len(majorLocs)): + xyz1[i][newindex] = newval + + # Grid points at end of the other plane + xyz2 = copy.deepcopy(xyz0) + newindex = (index + 2) % 3 + newval = get_flip_min_max(xyz2[0], newindex, mins, maxs) + for i in range(len(majorLocs)): + xyz2[i][newindex] = newval + + lines = zip(xyz1, xyz0, xyz2) + if self.axes._draw_grid: + self.gridlines.set_segments(lines) + self.gridlines.set_color([(0.9,0.9,0.9,1)] * len(lines)) + self.gridlines.draw(renderer, project=True) # Draw ticks tickdir = info['tickdir'] @@ -296,19 +375,23 @@ def draw(self, renderer): renderer.close_group('axis3d') def get_view_interval(self): - """return the Interval instance for this axis view limits""" + """return the Interval instance for this 3d axis view limits""" return self.v_interval + + def set_view_interval(self, vmin, vmax, ignore=False): + if ignore: + self.v_interval = vmin, vmax + else: + Vmin, Vmax = self.get_view_interval() + self.v_interval = min(vmin, Vmin), max(vmax, Vmax) -# Each type of axis should be looking in a different place for its -# current data limits so we do this with classes. I think there is -# a lot more that I can and should move down into these classes also. +# Use classes to look at different data limits class XAxis(Axis): def get_data_interval(self): 'return the Interval instance for this axis data limits' return self.axes.xy_dataLim.intervalx - class YAxis(Axis): def get_data_interval(self): 'return the Interval instance for this axis data limits' diff --git a/lib/mpl_toolkits/mplot3d/proj3d.py b/lib/mpl_toolkits/mplot3d/proj3d.py index ce4341f769b7..f4d8935b8cee 100644 --- a/lib/mpl_toolkits/mplot3d/proj3d.py +++ b/lib/mpl_toolkits/mplot3d/proj3d.py @@ -51,7 +51,7 @@ def line2d_seg_dist(p1, p2, p0): p0[1] = y(s) intersection point p = p1 + u*(p2-p1) - and intersection point lies within segement if u is between 0 and 1 + and intersection point lies within segment if u is between 0 and 1 """ x21 = p2[0] - p1[0] diff --git a/make.osx b/make.osx index 7d29974aab14..d3093a731244 100644 --- a/make.osx +++ b/make.osx @@ -1,98 +1,110 @@ -# -*- makefile -*- # build mpl into a local install dir with -# make -f make.osx PREFIX=/Users/jdhunter/dev PYVERSION=2.6 fetch deps mpl_install -# (see README.osx for more details) - +# PREFIX=/Users/jdhunter/dev make -f make.osx fetch deps mpl_install +MPLVERSION=1.0rc1 +PYVERSION=2.6 PYTHON=python${PYVERSION} ZLIBVERSION=1.2.3 PNGVERSION=1.2.39 FREETYPEVERSION=2.3.11 MACOSX_DEPLOYMENT_TARGET=10.6 OSX_SDK_VER=10.6 -ARCH_FLAGS=-arch i386 -arch x86_64 +ARCH_FLAGS="-arch i386-arch x86_64" ## You shouldn't need to configure past this point -export PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig -export CFLAGS=${ARCH_FLAGS} -I${PREFIX}/include -I${PREFIX}/include/freetype2 -isysroot /Developer/SDKs/MacOSX${OSX_SDK_VER}.sdk -export LDFLAGS=${ARCH_FLAGS} -L${PREFIX}/lib -syslibroot,/Developer/SDKs/MacOSX${OSX_SDK_VER}.sdk -export MACOSX_DEPLOYMENT_TARGET +#PKG_CONFIG_PATH="${PREFIX}/lib/pkgconfig" +#CFLAGS="${ARCH_FLAGS} -I${PREFIX}/include -I${PREFIX}/include/freetype2 -isysroot /Developer/SDKs/MacOSX${OSX_SDK_VER}.sdk" +#LDFLAGS="${ARCH_FLAGS} -L${PREFIX}/lib -syslibroot,/Developer/SDKs/MacOSX${OSX_SDK_VER}.sdk" + +PKG_CONFIG_PATH="${PREFIX}/lib/pkgconfig" +CFLAGS="-arch i386 -arch x86_64 -I${PREFIX}/include -I${PREFIX}/include/freetype2 -isysroot /Developer/SDKs/MacOSX${OSX_SDK_VER}.sdk" +LDFLAGS="-arch i386 -arch x86_64 -L${PREFIX}/lib -syslibroot,/Developer/SDKs/MacOSX${OSX_SDK_VER}.sdk" +FFLAGS="-arch i386 -arch x86_64" -help: - @echo "Interesting targets: clean, fetch, deps, mpl_build, mpl_install_std" - @echo "See README.osx for details" +check-prefix: + @if [ ! -d "$(PREFIX)" ]; then echo Set PREFIX to a directory - see README.osx; exit 1; fi clean: - rm -rf zlib-${ZLIBVERSION}.tar.gz libpng-${PNGVERSION}.tar.gz \ + rm -rf zlib-${ZLIBVERSION}.tar.gz libpng-${PNGVERSION}.tar.bz2 \ freetype-${FREETYPEVERSION}.tar.bz2 bdist_mpkg-${BDISTMPKGVERSION}.tar.gz \ - bdist_mpkg-${BDISTMPKGVERSION} setupext.pyc \ - lib/matplotlib/mpl-data/matplotlib.conf lib/matplotlib/mpl-data/matplotlibrc \ + bdist_mpkg-${BDISTMPKGVERSION} \ zlib-${ZLIBVERSION} libpng-${PNGVERSION} freetype-${FREETYPEVERSION} \ build fetch: - ${PYTHON} -c 'import urllib; urllib.urlretrieve("http://sourceforge.net/projects/libpng/files/zlib/${ZLIBVERSION}/zlib-${ZLIBVERSION}.tar.gz/download", "zlib-${ZLIBVERSION}.tar.gz")' &&\ - ${PYTHON} -c 'import urllib; urllib.urlretrieve("http://sourceforge.net/projects/libpng/files/libpng12/older-releases/${PNGVERSION}/libpng-${PNGVERSION}.tar.gz/download", "libpng-${PNGVERSION}.tar.gz")' &&\ + ${PYTHON} -c 'import urllib; urllib.urlretrieve("http://superb-sea2.dl.sourceforge.net/project/libpng/zlib/${ZLIBVERSION}/zlib-${ZLIBVERSION}.tar.gz", "zlib-${ZLIBVERSION}.tar.gz")' &&\ + ${PYTHON} -c 'import urllib; urllib.urlretrieve("http://sourceforge.net/projects/libpng/files/libpng-stable/${PNGVERSION}/libpng-${PNGVERSION}.tar.gz/download", "libpng-${PNGVERSION}.tar.gz")' &&\ ${PYTHON} -c 'import urllib; urllib.urlretrieve("http://download.savannah.gnu.org/releases/freetype/freetype-${FREETYPEVERSION}.tar.bz2", "freetype-${FREETYPEVERSION}.tar.bz2")' -check-prefix: - @if [ ! -d "$(PREFIX)" ]; then echo Set PREFIX to a directory - see README.osx; exit 1; fi + + zlib: check-prefix + export PKG_CONFIG_PATH=${PKG_CONFIG_PATH} &&\ rm -rf zlib-${ZLIBVERSION} &&\ tar xvfz zlib-${ZLIBVERSION}.tar.gz &&\ cd zlib-${ZLIBVERSION} &&\ + export MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} &&\ + export CFLAGS=${CFLAGS} &&\ + export LDFLAGS=${LDFLAGS} &&\ ./configure --prefix=${PREFIX}&&\ - ${MAKE} -j3 install + MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} CFLAGS=${CFLAGS} LDFLAGS=${LDFLAGS} make -j3 install&& \ + unset MACOSX_DEPLOYMENT_TARGET -png: check-prefix zlib +png: zlib check-prefix + export PKG_CONFIG_PATH=${PKG_CONFIG_PATH} &&\ rm -rf libpng-${PNGVERSION} &&\ tar xvfz libpng-${PNGVERSION}.tar.gz && \ cd libpng-${PNGVERSION} &&\ + export MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} &&\ + export CFLAGS=${CFLAGS} &&\ + export LDFLAGS=${LDFLAGS} &&\ ./configure --disable-dependency-tracking --prefix=${PREFIX} &&\ - ${MAKE} -j3 install&&\ - cp .libs/libpng.a . + make -j3 install&&\ + cp .libs/libpng.a . &&\ + unset MACOSX_DEPLOYMENT_TARGET + -freetype: check-prefix zlib +freetype: zlib check-prefix + export PKG_CONFIG_PATH=${PKG_CONFIG_PATH} &&\ rm -rf ${FREETYPEVERSION} &&\ tar xvfj freetype-${FREETYPEVERSION}.tar.bz2 &&\ cd freetype-${FREETYPEVERSION} &&\ + export MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} &&\ + export CFLAGS=${CFLAGS} &&\ + export LDFLAGS=${LDFLAGS} &&\ ./configure --prefix=${PREFIX} &&\ - ${MAKE} -j3 install &&\ - cp objs/.libs/libfreetype.a . + make -j3 install &&\ + cp objs/.libs/libfreetype.a . &&\ + unset MACOSX_DEPLOYMENT_TARGET + deps: zlib png freetype echo 'all done' -# Various build and install targets -# -# The values of PKG_CONFIG_PATH, MACOSX_DEPLOYMENT_TARGET, CFLAGS, -# LDFLAGS affect the compilation; note that variables are -# automatically put into the environment by make. - -mpl_build: check-prefix +mpl_build: + export PKG_CONFIG_PATH=${PKG_CONFIG_PATH} &&\ + export MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} &&\ + export CFLAGS=${CFLAGS} &&\ + export LDFLAGS=${LDFLAGS} &&\ ${PYTHON} setup.py build mpl_install: check-prefix + export PKG_CONFIG_PATH=${PKG_CONFIG_PATH} &&\ + export MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} &&\ + export CFLAGS=${CFLAGS} &&\ + export LDFLAGS=${LDFLAGS} &&\ ${PYTHON} setup.py install --prefix=${PREFIX} -mpl_install_std: check-prefix - ${PYTHON} setup.py install - -mpl_install_egg: check-prefix - ${PYTHON} setupegg.py install - -mpl_install_develop: check-prefix - ${PYTHON} setupegg.py develop - binaries: check-prefix - @if [ -z "$(PYVERSION)" ]; then echo Must set PYVERSION; exit 1; fi unset PKG_CONFIG_PATH &&\ cp release/osx/data/setup.cfg release/osx/data/ReadMe.txt . &&\ + export CFLAGS=${CFLAGS} &&\ + export LDFLAGS=${LDFLAGS} &&\ rm -f ${PREFIX}/lib/*.dylib &&\ - /Library/Frameworks/Python.framework/Versions/${PYVERSION}/bin/bdist_mpkg --readme=ReadMe.txt &&\ - hdiutil create -srcdir dist/matplotlib-${MPLVERSION}-py${PYVERSION}-macosx10.5.mpkg dist/matplotlib-${MPLVERSION}-py${PYVERSION}-macosx10.5.dmg &&\ - ${PYTHON} setupegg.py bdist_egg + VERSIONER_PYTHON_PREFER_32_BIT=yes bdist_mpkg --readme=ReadMe.txt &&\ + hdiutil create -srcdir dist/matplotlib-${MPLVERSION}-py${PYVERSION}-macosx${OSX_SDK_VER}.mpkg dist/matplotlib-${MPLVERSION}-py${PYVERSION}-macosx${OSX_SDK_VER}.dmg &&\ + VERSIONER_PYTHON_PREFER_32_BIT=yes ${PYTHON} setupegg.py bdist_egg diff --git a/setup.cfg.template b/setup.cfg.template index 0e55d9741c4c..8af8b6d3049d 100644 --- a/setup.cfg.template +++ b/setup.cfg.template @@ -57,7 +57,6 @@ #gtk = False #gtkagg = False #tkagg = False -#wxagg = False #macosx = False [rc_options] @@ -73,10 +72,3 @@ # #backend = Agg # -# The numerix module was historically used to provide -# compatibility between the Numeric, numarray, and NumPy array -# packages. Now that NumPy has emerge as the universal array -# package for python, numerix is not really necessary and is -# maintained to provide backward compatibility. Do not change -# this unless you have a compelling reason to do so. -#numerix = numpy diff --git a/setup.py b/setup.py index 0feb7ea22f0c..2f138218881d 100644 --- a/setup.py +++ b/setup.py @@ -33,12 +33,12 @@ import glob from distutils.core import setup -from setupext import build_agg, build_gtkagg, build_tkagg, build_wxagg,\ +from setupext import build_agg, build_gtkagg, build_tkagg,\ build_macosx, build_ft2font, build_image, build_windowing, build_path, \ build_contour, build_delaunay, build_nxutils, build_gdk, \ build_ttconv, print_line, print_status, print_message, \ print_raw, check_for_freetype, check_for_libpng, check_for_gtk, \ - check_for_tk, check_for_wx, check_for_macosx, check_for_numpy, \ + check_for_tk, check_for_macosx, check_for_numpy, \ check_for_qt, check_for_qt4, check_for_cairo, \ check_provide_pytz, check_provide_dateutil,\ check_for_dvipng, check_for_ghostscript, check_for_latex, \ @@ -53,20 +53,12 @@ 'matplotlib.testing', 'matplotlib.testing.jpl_units', 'matplotlib.tests', -# 'matplotlib.toolkits', 'mpl_toolkits', 'mpl_toolkits.mplot3d', 'mpl_toolkits.axes_grid', 'mpl_toolkits.axes_grid1', 'mpl_toolkits.axisartist', 'matplotlib.sphinxext', - # The following are deprecated and will be removed. - 'matplotlib.numerix', - 'matplotlib.numerix.mlab', - 'matplotlib.numerix.ma', - 'matplotlib.numerix.linear_algebra', - 'matplotlib.numerix.random_array', - 'matplotlib.numerix.fft', 'matplotlib.tri', ] @@ -75,7 +67,7 @@ ext_modules = [] -for line in file('lib/matplotlib/__init__.py').readlines(): +for line in open('lib/matplotlib/__init__.py').readlines(): if (line.startswith('__version__')): exec(line.strip()) @@ -163,17 +155,6 @@ def chop_package(fname): build_tkagg(ext_modules, packages) rc['backend'] = 'TkAgg' -if options['build_wxagg']: - if check_for_wx() or (options['build_wxagg'] is True): - options['build_agg'] = 1 - import wx - if getattr(wx, '__version__', '0.0')[0:3] < '2.8' : - build_wxagg(ext_modules, packages) - wxagg_backend_status = "yes" - else: - print_message("WxAgg extension not required for wxPython >= 2.8") - rc['backend'] = 'WXAgg' - hasgtk = check_for_gtk() if options['build_gtk']: if hasgtk or (options['build_gtk'] is True): @@ -235,7 +216,7 @@ def add_dateutil(): # only add them if we need them if provide_pytz: add_pytz() - print 'adding pytz' + print_raw("adding pytz") if provide_dateutil: add_dateutil() print_raw("") @@ -251,14 +232,14 @@ def add_dateutil(): # Write the default matplotlibrc file if options['backend']: rc['backend'] = options['backend'] -template = file('matplotlibrc.template').read() -file('lib/matplotlib/mpl-data/matplotlibrc', 'w').write(template%rc) +template = open('matplotlibrc.template').read() +open('lib/matplotlib/mpl-data/matplotlibrc', 'w').write(template%rc) # Write the default matplotlib.conf file -template = file('lib/matplotlib/mpl-data/matplotlib.conf.template').read() +template = open('lib/matplotlib/mpl-data/matplotlib.conf.template').read() template = template.replace("datapath = ", "#datapath = ") template = template.replace(" use = 'Agg'", " use = '%s'"%rc['backend']) -file('lib/matplotlib/mpl-data/matplotlib.conf', 'w').write(template) +open('lib/matplotlib/mpl-data/matplotlib.conf', 'w').write(template) try: additional_params # has setupegg.py provided except NameError: additional_params = {} @@ -267,8 +248,8 @@ def add_dateutil(): if options['verbose']: mod.extra_compile_args.append('-DVERBOSE') -print 'pymods', py_modules -print 'packages', packages +print_raw("pymods %s" % py_modules) +print_raw("packages %s" % packages) distrib = setup(name="matplotlib", version= __version__, description = "Python plotting package", diff --git a/setupegg.py b/setupegg.py index 56a373ae3a8b..0d51b3eeb47e 100644 --- a/setupegg.py +++ b/setupegg.py @@ -6,8 +6,5 @@ execfile('setup.py', {'additional_params' : {'namespace_packages' : ['mpl_toolkits'], - 'entry_points': {'nose.plugins': - [ - 'KnownFailure = matplotlib.testing.noseclasses:KnownFailure', - ] - }}}) + #'entry_points': {'nose.plugins': ['KnownFailure = matplotlib.testing.noseclasses:KnownFailure', ] } + }}) diff --git a/setupext.py b/setupext.py index f6c6388f1983..eed934244140 100644 --- a/setupext.py +++ b/setupext.py @@ -79,13 +79,21 @@ } import sys, os, stat -if sys.platform != 'win32': - import commands + from textwrap import fill from distutils.core import Extension import glob -import ConfigParser -import cStringIO + +if sys.version_info[0] < 3: + import ConfigParser as configparser + from cStringIO import StringIO + if sys.platform != 'win32': + from commands import getstatusoutput +else: + import configparser + from io import StringIO + if sys.platform != 'win32': + from subprocess import getstatusoutput BUILT_PNG = False BUILT_AGG = False @@ -95,7 +103,6 @@ BUILT_IMAGE = False BUILT_MACOSX = False BUILT_TKAGG = False -BUILT_WXAGG = False BUILT_WINDOWING = False BUILT_CONTOUR = False BUILT_DELAUNAY = False @@ -120,7 +127,6 @@ 'build_gtk': 'auto', 'build_gtkagg': 'auto', 'build_tkagg': 'auto', - 'build_wxagg': 'auto', 'build_macosx': 'auto', 'build_image': True, 'build_windowing': True, @@ -131,10 +137,11 @@ ('PY_ARRAY_UNIQUE_SYMBOL', 'MPL_ARRAY_API'), ('PYCXX_ISO_CPP_LIB', '1')] +setup_cfg = os.environ.get('MPLSETUPCFG', 'setup.cfg') # Based on the contents of setup.cfg, determine the build options -if os.path.exists("setup.cfg"): - config = ConfigParser.SafeConfigParser() - config.read("setup.cfg") +if os.path.exists(setup_cfg): + config = configparser.SafeConfigParser() + config.read(setup_cfg) try: options['display_status'] = not config.getboolean("status", "suppress") except: pass @@ -158,9 +165,6 @@ try: options['build_tkagg'] = config.getboolean("gui_support", "tkagg") except: options['build_tkagg'] = 'auto' - try: options['build_wxagg'] = config.getboolean("gui_support", "wxagg") - except: options['build_wxagg'] = 'auto' - try: options['build_macosx'] = config.getboolean("gui_support", "macosx") except: options['build_macosx'] = 'auto' @@ -175,27 +179,27 @@ basedirlist = options['basedirlist'].split() else: basedirlist = basedir[sys.platform] -print "basedirlist is:", basedirlist +print("basedirlist is: %s" % basedirlist) if options['display_status']: def print_line(char='='): - print char * 76 + print(char * 76) def print_status(package, status): initial_indent = "%22s: " % package indent = ' ' * 24 - print fill(str(status), width=76, + print(fill(str(status), width=76, initial_indent=initial_indent, - subsequent_indent=indent) + subsequent_indent=indent)) def print_message(message): indent = ' ' * 24 + "* " - print fill(str(message), width=76, + print(fill(str(message), width=76, initial_indent=indent, - subsequent_indent=indent) + subsequent_indent=indent)) def print_raw(section): - print section + print(section) else: def print_line(*args, **kwargs): pass @@ -263,7 +267,7 @@ def has_pkgconfig(): has_pkgconfig.cache = False else: #print 'environ', os.environ['PKG_CONFIG_PATH'] - status, output = commands.getstatusoutput("pkg-config --help") + status, output = getstatusoutput("pkg-config --help") has_pkgconfig.cache = (status == 0) return has_pkgconfig.cache has_pkgconfig.cache = None @@ -285,7 +289,7 @@ def get_pkgconfig(module, '-U': 'undef_macros'} cmd = "%s %s %s" % (pkg_config_exec, flags, packages) - status, output = commands.getstatusoutput(cmd) + status, output = getstatusoutput(cmd) if status == 0: for token in output.split(): attr = _flags.get(token[:2], None) @@ -313,7 +317,7 @@ def get_pkgconfig_version(package): if not has_pkgconfig(): return default - status, output = commands.getstatusoutput( + status, output = getstatusoutput( "pkg-config %s --modversion" % (package)) if status == 0: return output @@ -481,7 +485,7 @@ def check_provide_dateutil(hasdatetime=True): def check_for_dvipng(): try: stdin, stdout = run_child_process('dvipng -version') - print_status("dvipng", stdout.readlines()[1].split()[-1]) + print_status("dvipng", stdout.readlines()[1].decode().split()[-1]) return True except (IndexError, ValueError): print_status("dvipng", "no") @@ -494,7 +498,7 @@ def check_for_ghostscript(): else: command = 'gs --version' stdin, stdout = run_child_process(command) - print_status("ghostscript", stdout.read()[:-1]) + print_status("ghostscript", stdout.read().decode()[:-1]) return True except (IndexError, ValueError): print_status("ghostscript", "no") @@ -503,7 +507,7 @@ def check_for_ghostscript(): def check_for_latex(): try: stdin, stdout = run_child_process('latex -version') - line = stdout.readlines()[0] + line = stdout.readlines()[0].decode() pattern = '(3\.1\d+)|(MiKTeX \d+.\d+)' match = re.search(pattern, line) print_status("latex", match.group(0)) @@ -516,6 +520,7 @@ def check_for_pdftops(): try: stdin, stdout = run_child_process('pdftops -v') for line in stdout.readlines(): + line = line.decode() if 'version' in line: print_status("pdftops", line.split()[-1]) return True @@ -715,101 +720,9 @@ def add_pygtk_flags(module): if sys.platform == 'win32' and win32_compiler == 'msvc' and 'm' in module.libraries: module.libraries.remove('m') - -def check_for_wx(): - gotit = False - explanation = None - try: - import wx - except ImportError: - explanation = 'wxPython not found' - else: - if getattr(wx, '__version__', '0.0')[0:3] >= '2.8': - print_status("wxPython", wx.__version__) - return True - elif sys.platform == 'win32' and win32_compiler == 'mingw32': - explanation = "The wxAgg extension can not be built using the mingw32 compiler on Windows, since the default wxPython binary is built using MS Visual Studio" - else: - wxconfig = find_wx_config() - if wxconfig is None: - explanation = """ -WXAgg's accelerator requires `wx-config'. - -The `wx-config\' executable could not be located in any directory of the -PATH environment variable. If you want to build WXAgg, and wx-config is -in some other location or has some other name, set the WX_CONFIG -environment variable to the full path of the executable like so: - -export WX_CONFIG=/usr/lib/wxPython-2.6.1.0-gtk2-unicode/bin/wx-config -""" - elif not check_wxpython_broken_macosx104_version(wxconfig): - explanation = 'WXAgg\'s accelerator not building because a broken wxPython (installed by Apple\'s Mac OS X) was found.' - else: - gotit = True - - if gotit: - module = Extension("test", []) - add_wx_flags(module, wxconfig) - if not find_include_file( - module.include_dirs, - os.path.join("wx", "wxPython", "wxPython.h")): - explanation = ("Could not find wxPython headers in any of %s" % - ", ".join(["'%s'" % x for x in module.include_dirs])) - - if gotit: - print_status("wxPython", wx.__version__) - else: - print_status("wxPython", "no") - if explanation is not None: - print_message(explanation) - return gotit - -def find_wx_config(): - """If the WX_CONFIG environment variable has been set, returns it value. - Otherwise, search for `wx-config' in the PATH directories and return the - first match found. Failing that, return None. - """ - - wxconfig = os.getenv('WX_CONFIG') - if wxconfig is not None: - return wxconfig - - path = os.getenv('PATH') or '' - for dir in path.split(':'): - wxconfig = os.path.join(dir, 'wx-config') - if os.path.exists(wxconfig): - return wxconfig - - return None - -def check_wxpython_broken_macosx104_version(wxconfig): - """Determines if we're using a broken wxPython installed by Mac OS X 10.4""" - if sys.platform == 'darwin': - if wxconfig == '/usr/bin/wx-config': - version_full = getoutput(wxconfig + ' --version-full') - if version_full == '2.5.3.1': - return False - return True - -def add_wx_flags(module, wxconfig): - """ - Add the module flags to build extensions which use wxPython. - """ - - if sys.platform == 'win32': # just added manually - wxlibs = ['wxexpath', 'wxjpegh', 'wxmsw26uh', - 'wxmsw26uh_animate', 'wxmsw26uh_gizmos', 'wxmsw26uh_gizmos_xrc', - 'wxmsw26uh_gl', 'wxmsw26uh_stc', 'wxpngh', 'wxregexuh', 'wxtiffh', 'wxzlibh'] - module.libraries.extend(wxlibs) - module.libraries.extend(wxlibs) - return - - get_pkgconfig(module, '', flags='--cppflags --libs', pkg_config_exec='wx-config') - # Make sure you use the Tk version given by Tkinter.TkVersion # or else you'll build for a wrong version of the Tcl # interpreter (leading to nasty segfaults). - def check_for_tk(): gotit = False explanation = None @@ -829,9 +742,15 @@ def check_for_tk(): module = Extension('test', []) try: explanation = add_tk_flags(module) - except RuntimeError, e: - explanation = str(e) + except RuntimeError: + # This deals with the change in exception handling syntax in + # python 3. If we only need to support >= 2.6, we can just use the + # commented out lines below. + exc_type,exc,tb = sys.exc_info() + explanation = str(exc) gotit = False +# except RuntimeError, e: +# explanation = str(e) else: if not find_include_file(module.include_dirs, "tk.h"): message = 'Tkinter present, but header files are not found. ' + \ @@ -925,23 +844,21 @@ def parse_tcl_config(tcl_lib_dir, tk_lib_dir): # So, we push a "[default]" section to a copy of the # file in a StringIO object. try: - tcl_vars_str = cStringIO.StringIO( - "[default]\n" + open(tcl_config, "r").read()) - tk_vars_str = cStringIO.StringIO( - "[default]\n" + open(tk_config, "r").read()) + tcl_vars_str = StringIO("[default]\n" + open(tcl_config, "r").read()) + tk_vars_str = StringIO("[default]\n" + open(tk_config, "r").read()) except IOError: # if we can't read the file, that's ok, we'll try # to guess instead return None tcl_vars_str.seek(0) - tcl_vars = ConfigParser.RawConfigParser() + tcl_vars = configparser.RawConfigParser() tk_vars_str.seek(0) - tk_vars = ConfigParser.RawConfigParser() + tk_vars = configparser.RawConfigParser() try: tcl_vars.readfp(tcl_vars_str) tk_vars.readfp(tk_vars_str) - except ConfigParser.ParsingError: + except configparser.ParsingError: # if we can't read the file, that's ok, we'll try # to guess instead return None @@ -957,7 +874,7 @@ def parse_tcl_config(tcl_lib_dir, tk_lib_dir): else: # On RHEL4 tk_inc = tcl_inc - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): + except (configparser.NoSectionError, configparser.NoOptionError): return None if not os.path.exists(os.path.join(tk_inc, 'tk.h')): @@ -1058,8 +975,8 @@ def add_tk_flags(module): # tk_include_dirs = [ join(F, fw + '.framework', H) - for fw in 'Tcl', 'Tk' - for H in 'Headers', 'Versions/Current/PrivateHeaders' + for fw in ('Tcl', 'Tk') + for H in ('Headers', 'Versions/Current/PrivateHeaders') ] # For 8.4a2, the X11 headers are not included. Rather than include a @@ -1202,25 +1119,6 @@ def build_tkagg(ext_modules, packages): BUILT_TKAGG = True -def build_wxagg(ext_modules, packages): - global BUILT_WXAGG - if BUILT_WXAGG: - return - - deps = ['src/_wxagg.cpp', 'src/mplutils.cpp'] - deps.extend(glob.glob('CXX/*.cxx')) - deps.extend(glob.glob('CXX/*.c')) - - module = Extension('matplotlib.backends._wxagg', deps) - - add_agg_flags(module) - add_ft2font_flags(module) - wxconfig = find_wx_config() - add_wx_flags(module, wxconfig) - - ext_modules.append(module) - BUILT_WXAGG = True - def build_macosx(ext_modules, packages): global BUILT_MACOSX if BUILT_MACOSX: return # only build it if you you haven't already diff --git a/src/_backend_agg.cpp b/src/_backend_agg.cpp index 00f2eb5cf4e0..37aa1f70cab2 100644 --- a/src/_backend_agg.cpp +++ b/src/_backend_agg.cpp @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + /* A rewrite of _backend_agg using PyCXX to handle ref counting, etc.. */ @@ -1916,13 +1918,15 @@ RendererAgg::draw_gouraud_triangles(const Py::Tuple& args) Py::Object points_obj = args[1]; Py::Object colors_obj = args[2]; agg::trans_affine trans = py_to_agg_transformation_matrix(args[3].ptr()); + double c_points[6]; + double c_colors[12]; theRasterizer.reset_clipping(); rendererBase.reset_clipping(true); set_clipbox(gc.cliprect, theRasterizer); bool has_clippath = render_clippath(gc.clippath, gc.clippath_trans); - PyArrayObject* points = (PyArrayObject*)PyArray_ContiguousFromAny + PyArrayObject* points = (PyArrayObject*)PyArray_FromObject (points_obj.ptr(), PyArray_DOUBLE, 3, 3); if (!points || PyArray_DIM(points, 1) != 3 || PyArray_DIM(points, 2) != 2) @@ -1932,7 +1936,7 @@ RendererAgg::draw_gouraud_triangles(const Py::Tuple& args) } points_obj = Py::Object((PyObject*)points, true); - PyArrayObject* colors = (PyArrayObject*)PyArray_ContiguousFromAny + PyArrayObject* colors = (PyArrayObject*)PyArray_FromObject (colors_obj.ptr(), PyArray_DOUBLE, 3, 3); if (!colors || PyArray_DIM(colors, 1) != 3 || PyArray_DIM(colors, 2) != 4) @@ -1949,9 +1953,20 @@ RendererAgg::draw_gouraud_triangles(const Py::Tuple& args) for (int i = 0; i < PyArray_DIM(points, 0); ++i) { + for (int j = 0; j < 3; ++j) { + for (int k = 0; k < 2; ++k) { + c_points[j*2+k] = *(double *)PyArray_GETPTR3(points, i, j, k); + } + } + + for (int j = 0; j < 3; ++j) { + for (int k = 0; k < 4; ++k) { + c_colors[j*4+k] = *(double *)PyArray_GETPTR3(colors, i, j, k); + } + } + _draw_gouraud_triangle( - (double*)PyArray_GETPTR1(points, i), - (double*)PyArray_GETPTR1(colors, i), trans, has_clippath); + c_points, c_colors, trans, has_clippath); } return Py::Object(); diff --git a/src/_backend_agg.h b/src/_backend_agg.h index 538a1f54ce3b..633f3e7c8bc9 100644 --- a/src/_backend_agg.h +++ b/src/_backend_agg.h @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + /* _backend_agg.h - A rewrite of _backend_agg using PyCXX to handle ref counting, etc.. */ diff --git a/src/_gtkagg.cpp b/src/_gtkagg.cpp index 0ad0e999cbfe..5a0b4613fe10 100644 --- a/src/_gtkagg.cpp +++ b/src/_gtkagg.cpp @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + #include #include diff --git a/src/_image.cpp b/src/_image.cpp index ca641c6f2743..3278b6c382b0 100644 --- a/src/_image.cpp +++ b/src/_image.cpp @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + /* Python API mandates Python.h is included *first* */ #include "Python.h" #include @@ -1083,7 +1085,7 @@ _image_module::frombyte(const Py::Tuple& args) Py::Object x = args[0]; int isoutput = Py::Int(args[1]); - PyArrayObject *A = (PyArrayObject *) PyArray_ContiguousFromObject(x.ptr(), PyArray_UBYTE, 3, 3); + PyArrayObject *A = (PyArrayObject *) PyArray_FromObject(x.ptr(), PyArray_UBYTE, 3, 3); if (A == NULL) { throw Py::ValueError("Array must have 3 dimensions"); @@ -1102,35 +1104,86 @@ _image_module::frombyte(const Py::Tuple& args) agg::int8u *arrbuf; agg::int8u *buffer; + agg::int8u *dstbuf; arrbuf = reinterpret_cast(A->data); size_t NUMBYTES(imo->colsIn * imo->rowsIn * imo->BPP); - buffer = new agg::int8u[NUMBYTES]; + buffer = dstbuf = new agg::int8u[NUMBYTES]; if (buffer == NULL) //todo: also handle allocation throw { throw Py::MemoryError("_image_module::frombyte could not allocate memory"); } - const size_t N = imo->rowsIn * imo->colsIn * imo->BPP; - size_t i = 0; - if (A->dimensions[2] == 4) + if PyArray_ISCONTIGUOUS(A) + { + if (A->dimensions[2] == 4) + { + memmove(dstbuf, arrbuf, imo->rowsIn * imo->colsIn * 4); + } + else + { + size_t i = imo->rowsIn * imo->colsIn; + while (i--) + { + *dstbuf++ = *arrbuf++; + *dstbuf++ = *arrbuf++; + *dstbuf++ = *arrbuf++; + *dstbuf++ = 255; + } + } + } + else if ((A->strides[1] == 4) && (A->strides[2] == 1)) { - memmove(buffer, arrbuf, N); + const size_t N = imo->colsIn * 4; + const size_t stride = A->strides[0]; + for (size_t rownum = 0; rownum < imo->rowsIn; rownum++) + { + memmove(dstbuf, arrbuf, N); + arrbuf += stride; + dstbuf += N; + } + } + else if ((A->strides[1] == 3) && (A->strides[2] == 1)) + { + const size_t stride = A->strides[0] - imo->colsIn * 3; + for (size_t rownum = 0; rownum < imo->rowsIn; rownum++) + { + for (size_t colnum = 0; colnum < imo->colsIn; colnum++) + { + *dstbuf++ = *arrbuf++; + *dstbuf++ = *arrbuf++; + *dstbuf++ = *arrbuf++; + *dstbuf++ = 255; + } + arrbuf += stride; + } } else { - while (i < N) + PyArrayIterObject *iter; + iter = (PyArrayIterObject *)PyArray_IterNew((PyObject *)A); + if (A->dimensions[2] == 4) + { + while (iter->index < iter->size) { + *dstbuf++ = *((unsigned char *)iter->dataptr); + PyArray_ITER_NEXT(iter); + } + } + else { - memmove(buffer, arrbuf, 3); - buffer += 3; - arrbuf += 3; - *buffer++ = 255; - i += 4; + while (iter->index < iter->size) { + *dstbuf++ = *((unsigned char *)iter->dataptr); + PyArray_ITER_NEXT(iter); + *dstbuf++ = *((unsigned char *)iter->dataptr); + PyArray_ITER_NEXT(iter); + *dstbuf++ = *((unsigned char *)iter->dataptr); + PyArray_ITER_NEXT(iter); + *dstbuf++ = 255; + } } - buffer -= N; - arrbuf -= imo->rowsIn * imo->colsIn; + Py_DECREF(iter); } if (isoutput) diff --git a/src/_image.h b/src/_image.h index eb36fbf64109..8a3be548dcb2 100644 --- a/src/_image.h +++ b/src/_image.h @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + /* image.h * */ @@ -76,7 +78,7 @@ class Image : public Py::PythonExtension }; //enum { BICUBIC=0, BILINEAR, BLACKMAN100, BLACKMAN256, BLACKMAN64, - // NEAREST, SINC144, SINC256, SINC64, SPLINE16, SPLINE36}; + // NEAREST, SINC144, SINC256, SINC64, SPLINE16, SPLINE36}; enum { ASPECT_PRESERVE = 0, ASPECT_FREE}; agg::int8u *bufferIn; diff --git a/src/_macosx.m b/src/_macosx.m index 83e7346e7643..ad78787df340 100644 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -5,6 +5,17 @@ #include "numpy/arrayobject.h" #include "path_cleanup.h" +/* Must define Py_TYPE for Python 2.5 or older */ +#ifndef Py_TYPE +# define Py_TYPE(o) ((o)->ob_type) +#endif + +/* Must define PyVarObject_HEAD_INIT for Python 2.5 or older */ +#ifndef PyVarObject_HEAD_INIT +#define PyVarObject_HEAD_INIT(type, size) \ + PyObject_HEAD_INIT(type) size, +#endif + /* Proper way to check for the OS X version we are compiling for, from http://developer.apple.com/documentation/DeveloperTools/Conceptual/cross_development */ #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 @@ -346,6 +357,7 @@ - (void)drawRect:(NSRect)rect; - (void)windowDidResize:(NSNotification*)notification; - (View*)initWithFrame:(NSRect)rect; - (void)setCanvas: (PyObject*)newCanvas; +- (void)windowWillClose:(NSNotification*)notification; - (BOOL)windowShouldClose:(NSNotification*)notification; - (BOOL)isFlipped; - (void)mouseEntered:(NSEvent*)event; @@ -487,14 +499,18 @@ static int _get_snap(GraphicsContext* self, enum e_snap_mode* mode) ngc--; if (ngc==0) _dealloc_atsui(); - self->ob_type->tp_free((PyObject*)self); + Py_TYPE(self)->tp_free((PyObject*)self); } #endif static PyObject* GraphicsContext_repr(GraphicsContext* self) { +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromFormat("GraphicsContext object %p wrapping the Quartz 2D graphics context %p", (void*)self, (void*)(self->cr)); +#else return PyString_FromFormat("GraphicsContext object %p wrapping the Quartz 2D graphics context %p", (void*)self, (void*)(self->cr)); +#endif } static PyObject* @@ -1758,8 +1774,6 @@ static BOOL _clip(CGContextRef cr, PyObject* object) /* Preset graphics context properties if possible */ CGContextSetShouldAntialias(cr, antialiased); - CGContextSetLineWidth(cr, 0.0); - if (Nfacecolors==1) { const double r = *(double*)PyArray_GETPTR2(facecolors, 0, 0); @@ -1822,6 +1836,7 @@ static BOOL _clip(CGContextRef cr, PyObject* object) CGContextMoveToPoint(cr, points[3].x, points[3].y); CGContextAddLines(cr, points, 4); + CGContextClosePath(cr); if (Nfacecolors > 1) { @@ -1876,6 +1891,346 @@ static BOOL _clip(CGContextRef cr, PyObject* object) return Py_None; } +static int _find_minimum(CGFloat values[3]) +{ + int i = 0; + CGFloat minimum = values[0]; + if (values[1] < minimum) + { + minimum = values[1]; + i = 1; + } + if (values[2] < minimum) + i = 2; + return i; +} + +static int _find_maximum(CGFloat values[3]) +{ + int i = 0; + CGFloat maximum = values[0]; + if (values[1] > maximum) + { + maximum = values[1]; + i = 1; + } + if (values[2] > maximum) + i = 2; + return i; +} + +static void +_rgba_color_evaluator(void* info, const CGFloat input[], CGFloat outputs[]) +{ + const CGFloat c1 = input[0]; + const CGFloat c0 = 1.0 - c1; + CGFloat(* color)[4] = info; + outputs[0] = c0 * color[0][0] + c1 * color[1][0]; + outputs[1] = c0 * color[0][1] + c1 * color[1][1]; + outputs[2] = c0 * color[0][2] + c1 * color[1][2]; + outputs[3] = c0 * color[0][3] + c1 * color[1][3]; +} + +static void +_gray_color_evaluator(void* info, const CGFloat input[], CGFloat outputs[]) +{ + const CGFloat c1 = input[0]; + const CGFloat c0 = 1.0 - c1; + CGFloat(* color)[2] = info; + outputs[0] = c0 * color[0][0] + c1 * color[1][0]; + outputs[1] = c0 * color[0][1] + c1 * color[1][1]; +} + +static int +_shade_one_color(CGContextRef cr, CGFloat colors[3], CGPoint points[3], int icolor) +{ + const int imin = _find_minimum(colors); + const int imax = _find_maximum(colors); + + float numerator; + float denominator; + float ac; + float as; + float phi; + float distance; + CGPoint start; + CGPoint end; + static CGFunctionCallbacks callbacks = {0, &_rgba_color_evaluator, free}; + CGFloat domain[2] = {0.0, 1.0}; + CGFloat range[8] = {0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0}; + CGFunctionRef function; + + CGFloat(* rgba)[4] = malloc(2*sizeof(CGFloat[4])); + if (!rgba) return -1; + else { + rgba[0][0] = 0.0; + rgba[0][1] = 0.0; + rgba[0][2] = 0.0; + rgba[0][3] = 1.0; + rgba[1][0] = 0.0; + rgba[1][1] = 0.0; + rgba[1][2] = 0.0; + rgba[1][3] = 1.0; + } + + denominator = (points[1].x-points[0].x)*(points[2].y-points[0].y) + - (points[2].x-points[0].x)*(points[1].y-points[0].y); + numerator = (colors[1]-colors[0])*(points[2].y-points[0].y) + - (colors[2]-colors[0])*(points[1].y-points[0].y); + ac = numerator / denominator; + numerator = (colors[2]-colors[0])*(points[1].x-points[0].x) + - (colors[1]-colors[0])*(points[2].x-points[0].x); + as = numerator / denominator; + phi = atan2(as, ac); + + start.x = points[imin].x; + start.y = points[imin].y; + + rgba[0][icolor] = colors[imin]; + rgba[1][icolor] = colors[imax]; + + distance = (points[imax].x-points[imin].x) * cos(phi) + (points[imax].y-points[imin].y) * sin(phi); + + end.x = start.x + distance * cos(phi); + end.y = start.y + distance * sin(phi); + + function = CGFunctionCreate(rgba, + 1, /* one input (position) */ + domain, + 4, /* rgba output */ + range, + &callbacks); + if (function) + { + CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + CGShadingRef shading = CGShadingCreateAxial(colorspace, + start, + end, + function, + true, + true); + CGFunctionRelease(function); + if (shading) + { + CGContextDrawShading(cr, shading); + CGShadingRelease(shading); + return 1; + } + } + free(rgba); + return -1; +} + +static CGRect _find_enclosing_rect(CGPoint points[3]) +{ + CGFloat left = points[0].x; + CGFloat right = points[0].x; + CGFloat bottom = points[0].y; + CGFloat top = points[0].y; + if (points[1].x < left) left = points[1].x; + if (points[1].x > right) right = points[1].x; + if (points[2].x < left) left = points[2].x; + if (points[2].x > right) right = points[2].x; + if (points[1].y < bottom) bottom = points[1].y; + if (points[1].y > top) top = points[1].y; + if (points[2].y < bottom) bottom = points[2].y; + if (points[2].y > top) top = points[2].y; + return CGRectMake(left,bottom,right-left,top-bottom); +} + +static int +_shade_alpha(CGContextRef cr, CGFloat alphas[3], CGPoint points[3]) +{ + const int imin = _find_minimum(alphas); + const int imax = _find_maximum(alphas); + + if (alphas[imin]==1.0) return 0; + + CGRect rect = _find_enclosing_rect(points); + const size_t width = (size_t)rect.size.width; + const size_t height = (size_t)rect.size.height; + if (width==0 || height==0) return 0; + + void* data = malloc(width*height); + + CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray(); + CGContextRef bitmap = CGBitmapContextCreate(data, + width, + height, + 8, + width, + colorspace, + 0); + CGColorSpaceRelease(colorspace); + + if (imin==imax) + { + CGRect bitmap_rect = rect; + bitmap_rect.origin = CGPointZero; + CGContextSetGrayFillColor(bitmap, alphas[0], 1.0); + CGContextFillRect(bitmap, bitmap_rect); + } + else + { + float numerator; + float denominator; + float ac; + float as; + float phi; + float distance; + CGPoint start; + CGPoint end; + CGFloat(*gray)[2] = malloc(2*sizeof(CGFloat[2])); + + static CGFunctionCallbacks callbacks = {0, &_gray_color_evaluator, free}; + CGFloat domain[2] = {0.0, 1.0}; + CGFloat range[2] = {0.0, 1.0}; + CGShadingRef shading = NULL; + CGFunctionRef function; + + gray[0][1] = 1.0; + gray[1][1] = 1.0; + + denominator = (points[1].x-points[0].x)*(points[2].y-points[0].y) + - (points[2].x-points[0].x)*(points[1].y-points[0].y); + numerator = (alphas[1]-alphas[0])*(points[2].y-points[0].y) + - (alphas[2]-alphas[0])*(points[1].y-points[0].y); + ac = numerator / denominator; + numerator = (alphas[2]-alphas[0])*(points[1].x-points[0].x) + - (alphas[1]-alphas[0])*(points[2].x-points[0].x); + as = numerator / denominator; + phi = atan2(as, ac); + + start.x = points[imin].x - rect.origin.x; + start.y = points[imin].y - rect.origin.y; + + gray[0][0] = alphas[imin]; + gray[1][0] = alphas[imax]; + + distance = (points[imax].x-points[imin].x) * cos(phi) + (points[imax].y-points[imin].y) * sin(phi); + + end.x = start.x + distance * cos(phi); + end.y = start.y + distance * sin(phi); + + function = CGFunctionCreate(gray, + 1, /* one input (position) */ + domain, + 1, /* one output (gray level) */ + range, + &callbacks); + if (function) + { + shading = CGShadingCreateAxial(colorspace, + start, + end, + function, + true, + true); + CGFunctionRelease(function); + } + if (shading) + { + CGContextDrawShading(bitmap, shading); + CGShadingRelease(shading); + } + else + { + free(gray); + } + } + + CGImageRef mask = CGBitmapContextCreateImage(bitmap); + CGContextClipToMask(cr, rect, mask); + CGImageRelease(mask); + free(data); + return 0; +} + +static PyObject* +GraphicsContext_draw_gouraud_triangle (GraphicsContext* self, PyObject* args) + +{ + PyObject* coordinates; + PyObject* colors; + + CGPoint points[3]; + CGFloat intensity[3]; + + int i = 0; + + CGContextRef cr = self->cr; + if (!cr) + { + PyErr_SetString(PyExc_RuntimeError, "CGContextRef is NULL"); + return NULL; + } + + if(!PyArg_ParseTuple(args, "OO", &coordinates, &colors)) return NULL; + + /* ------------------- Check coordinates array ------------------------ */ + + coordinates = PyArray_FromObject(coordinates, NPY_DOUBLE, 2, 2); + if (!coordinates || + PyArray_DIM(coordinates, 0) != 3 || PyArray_DIM(coordinates, 1) != 2) + { + PyErr_SetString(PyExc_ValueError, "Invalid coordinates array"); + Py_XDECREF(coordinates); + return NULL; + } + points[0].x = *((double*)(PyArray_GETPTR2(coordinates, 0, 0))); + points[0].y = *((double*)(PyArray_GETPTR2(coordinates, 0, 1))); + points[1].x = *((double*)(PyArray_GETPTR2(coordinates, 1, 0))); + points[1].y = *((double*)(PyArray_GETPTR2(coordinates, 1, 1))); + points[2].x = *((double*)(PyArray_GETPTR2(coordinates, 2, 0))); + points[2].y = *((double*)(PyArray_GETPTR2(coordinates, 2, 1))); + + /* ------------------- Check colors array ----------------------------- */ + + colors = PyArray_FromObject(colors, NPY_DOUBLE, 2, 2); + if (!colors || + PyArray_DIM(colors, 0) != 3 || PyArray_DIM(colors, 1) != 4) + { + PyErr_SetString(PyExc_ValueError, "colors must by a 3x4 array"); + Py_DECREF(coordinates); + Py_XDECREF(colors); + return NULL; + } + + /* ----- Draw the gradients separately for each color component ------- */ + CGContextSaveGState(cr); + CGContextMoveToPoint(cr, points[0].x, points[0].y); + CGContextAddLineToPoint(cr, points[1].x, points[1].y); + CGContextAddLineToPoint(cr, points[2].x, points[2].y); + CGContextClip(cr); + intensity[0] = *((double*)(PyArray_GETPTR2(colors, 0, 3))); + intensity[1] = *((double*)(PyArray_GETPTR2(colors, 1, 3))); + intensity[2] = *((double*)(PyArray_GETPTR2(colors, 2, 3))); + if (_shade_alpha(cr, intensity, points)!=-1) { + CGContextBeginTransparencyLayer(cr, NULL); + CGContextSetBlendMode(cr, kCGBlendModeScreen); + for (i = 0; i < 3; i++) + { + intensity[0] = *((double*)(PyArray_GETPTR2(colors, 0, i))); + intensity[1] = *((double*)(PyArray_GETPTR2(colors, 1, i))); + intensity[2] = *((double*)(PyArray_GETPTR2(colors, 2, i))); + if (!_shade_one_color(cr, intensity, points, i)) break; + } + CGContextEndTransparencyLayer(cr); + } + CGContextRestoreGState(cr); + + Py_DECREF(coordinates); + Py_DECREF(colors); + + if (i < 3) /* break encountered */ + { + PyErr_SetString(PyExc_MemoryError, "insufficient memory in draw_gouraud_triangle"); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} #ifdef COMPILING_FOR_10_5 static CTFontRef @@ -1896,6 +2251,9 @@ static BOOL _clip(CGContextRef cr, PyObject* object) #else ATSFontRef font = 0; #endif +#if PY_MAJOR_VERSION >= 3 + PyObject* ascii = NULL; +#endif const int k = (strcmp(italic, "italic") ? 0 : 2) + (strcmp(weight, "bold") ? 0 : 1); @@ -2076,8 +2434,14 @@ static BOOL _clip(CGContextRef cr, PyObject* object) for (i = 0; i < n; i++) { PyObject* item = PyList_GET_ITEM(family, i); +#if PY_MAJOR_VERSION >= 3 + ascii = PyUnicode_AsASCIIString(item); + if(!ascii) return 0; + temp = PyBytes_AS_STRING(ascii); +#else if(!PyString_Check(item)) return 0; temp = PyString_AS_STRING(item); +#endif for (j = 0; j < NMAP; j++) { if (!strcmp(map[j].name, temp)) { temp = psnames[map[j].index][k]; @@ -2104,6 +2468,10 @@ static BOOL _clip(CGContextRef cr, PyObject* object) name = temp; break; } +#if PY_MAJOR_VERSION >= 3 + Py_DECREF(ascii); + ascii = NULL; +#endif } if(!font) { string = CFStringCreateWithCString(kCFAllocatorDefault, @@ -2118,6 +2486,9 @@ static BOOL _clip(CGContextRef cr, PyObject* object) } #ifndef COMPILING_FOR_10_5 CGContextSelectFont(cr, name, size, kCGEncodingMacRoman); +#endif +#if PY_MAJOR_VERSION >= 3 + Py_XDECREF(ascii); #endif return font; } @@ -2618,11 +2989,19 @@ static void _data_provider_release(void* info, const void* data, size_t size) CGDataProviderRef provider; double rect[4] = {0.0, 0.0, self->size.width, self->size.height}; +#if PY_MAJOR_VERSION >= 3 + if (!PyBytes_Check(image)) + { + PyErr_SetString(PyExc_RuntimeError, "image is not a byte array"); + return NULL; + } +#else if (!PyString_Check(image)) { PyErr_SetString(PyExc_RuntimeError, "image is not a string"); return NULL; } +#endif const size_t bytesPerComponent = 1; const size_t bitsPerComponent = 8 * bytesPerComponent; @@ -2638,8 +3017,13 @@ static void _data_provider_release(void* info, const void* data, size_t size) } Py_INCREF(image); +#if PY_MAJOR_VERSION >= 3 + n = PyByteArray_GET_SIZE(image); + data = PyByteArray_AS_STRING(image); +#else n = PyString_GET_SIZE(image); data = PyString_AsString(image); +#endif provider = CGDataProviderCreateWithData(image, data, @@ -2791,6 +3175,11 @@ static void _data_provider_release(void* info, const void* data, size_t size) METH_VARARGS, "Draws a mesh in the graphics context." }, + {"draw_gouraud_triangle", + (PyCFunction)GraphicsContext_draw_gouraud_triangle, + METH_VARARGS, + "Draws a Gouraud-shaded triangle in the graphics context." + }, {"draw_text", (PyCFunction)GraphicsContext_draw_text, METH_VARARGS, @@ -2816,8 +3205,7 @@ static void _data_provider_release(void* info, const void* data, size_t size) "set_joinstyle, etc.).\n"; static PyTypeObject GraphicsContextType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ + PyVarObject_HEAD_INIT(NULL, 0) "_macosx.GraphicsContext", /*tp_name*/ sizeof(GraphicsContext), /*tp_basicsize*/ 0, /*tp_itemsize*/ @@ -2902,14 +3290,19 @@ static void _data_provider_release(void* info, const void* data, size_t size) [self->view setCanvas: NULL]; [self->view release]; } - self->ob_type->tp_free((PyObject*)self); + Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject* FigureCanvas_repr(FigureCanvas* self) { +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromFormat("FigureCanvas object %p wrapping NSView %p", + (void*)self, (void*)(self->view)); +#else return PyString_FromFormat("FigureCanvas object %p wrapping NSView %p", (void*)self, (void*)(self->view)); +#endif } static PyObject* @@ -3243,8 +3636,7 @@ static void _data_provider_release(void* info, const void* data, size_t size) "A FigureCanvas object wraps a Cocoa NSView object.\n"; static PyTypeObject FigureCanvasType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ + PyVarObject_HEAD_INIT(NULL, 0) "_macosx.FigureCanvas", /*tp_name*/ sizeof(FigureCanvas), /*tp_basicsize*/ 0, /*tp_itemsize*/ @@ -3374,8 +3766,13 @@ static void _data_provider_release(void* info, const void* data, size_t size) static PyObject* FigureManager_repr(FigureManager* self) { +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromFormat("FigureManager object %p wrapping NSWindow %p", + (void*) self, (void*)(self->window)); +#else return PyString_FromFormat("FigureManager object %p wrapping NSWindow %p", (void*) self, (void*)(self->window)); +#endif } static void @@ -3388,7 +3785,7 @@ static void _data_provider_release(void* info, const void* data, size_t size) [window close]; [pool release]; } - self->ob_type->tp_free((PyObject*)self); + Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject* @@ -3419,8 +3816,7 @@ static void _data_provider_release(void* info, const void* data, size_t size) "A FigureManager object wraps a Cocoa NSWindow object.\n"; static PyTypeObject FigureManagerType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ + PyVarObject_HEAD_INIT(NULL, 0) "_macosx.FigureManager", /*tp_name*/ sizeof(FigureManager), /*tp_basicsize*/ 0, /*tp_itemsize*/ @@ -3755,13 +4151,17 @@ -(void)save_figure:(id)sender NavigationToolbar_dealloc(NavigationToolbar *self) { [self->handler release]; - self->ob_type->tp_free((PyObject*)self); + Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject* NavigationToolbar_repr(NavigationToolbar* self) { +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromFormat("NavigationToolbar object %p", (void*)self); +#else return PyString_FromFormat("NavigationToolbar object %p", (void*)self); +#endif } static char NavigationToolbar_doc[] = @@ -3868,7 +4268,11 @@ -(void)save_figure:(id)sender { if(states[i]==1) { +#if PY_MAJOR_VERSION >= 3 + PyList_SET_ITEM(list, j, PyLong_FromLong(i)); +#else PyList_SET_ITEM(list, j, PyInt_FromLong(i)); +#endif j++; } } @@ -3891,8 +4295,7 @@ -(void)save_figure:(id)sender }; static PyTypeObject NavigationToolbarType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ + PyVarObject_HEAD_INIT(NULL, 0) "_macosx.NavigationToolbar", /*tp_name*/ sizeof(NavigationToolbar), /*tp_basicsize*/ 0, /*tp_itemsize*/ @@ -4277,13 +4680,17 @@ -(void)save_figure:(id)sender NavigationToolbar2_dealloc(NavigationToolbar2 *self) { [self->handler release]; - self->ob_type->tp_free((PyObject*)self); + Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject* NavigationToolbar2_repr(NavigationToolbar2* self) { +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromFormat("NavigationToolbar2 object %p", (void*)self); +#else return PyString_FromFormat("NavigationToolbar2 object %p", (void*)self); +#endif } static char NavigationToolbar2_doc[] = @@ -4298,8 +4705,10 @@ -(void)save_figure:(id)sender NSText* messagebox = self->messagebox; if (messagebox) - { NSString* text = [NSString stringWithUTF8String: message]; + { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSString* text = [NSString stringWithUTF8String: message]; [messagebox setString: text]; + [pool release]; } Py_INCREF(Py_None); @@ -4316,8 +4725,7 @@ -(void)save_figure:(id)sender }; static PyTypeObject NavigationToolbar2Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ + PyVarObject_HEAD_INIT(NULL, 0) "_macosx.NavigationToolbar2", /*tp_name*/ sizeof(NavigationToolbar2), /*tp_basicsize*/ 0, /*tp_itemsize*/ @@ -4447,10 +4855,10 @@ - (void)dealloc gstate = PyGILState_Ensure(); Py_DECREF(manager); PyGILState_Release(gstate); - /* The reference count of the view that was added as a subview to the - * content view of this window was increased during the call to addSubview, - * and is decreased during the call to [super dealloc]. - */ + /* The reference count of the view that was added as a subview to the + * content view of this window was increased during the call to addSubview, + * and is decreased during the call to [super dealloc]. + */ [super dealloc]; } @end @@ -4594,6 +5002,20 @@ - (void)windowDidResize: (NSNotification*)notification [self setNeedsDisplay: YES]; } +- (void)windowWillClose:(NSNotification*)notification +{ + PyGILState_STATE gstate; + PyObject* result; + + gstate = PyGILState_Ensure(); + result = PyObject_CallMethod(canvas, "close_event", ""); + if(result) + Py_DECREF(result); + else + PyErr_Print(); + PyGILState_Release(gstate); +} + - (BOOL)windowShouldClose:(NSNotification*)notification { NSWindow* window = [self window]; @@ -5153,6 +5575,199 @@ - (int)index return Py_True; } +typedef struct { + PyObject_HEAD + CFRunLoopTimerRef timer; +} Timer; + +static PyObject* +Timer_new(PyTypeObject* type, PyObject *args, PyObject *kwds) +{ + Timer* self = (Timer*)type->tp_alloc(type, 0); + if (!self) return NULL; + self->timer = NULL; + return (PyObject*) self; +} + +static void +Timer_dealloc(Timer* self) +{ + if (self->timer) { + PyObject* attribute; + CFRunLoopTimerContext context; + CFRunLoopTimerGetContext(self->timer, &context); + attribute = context.info; + Py_DECREF(attribute); + CFRunLoopRef runloop = CFRunLoopGetCurrent(); + if (runloop) { + CFRunLoopRemoveTimer(runloop, self->timer, kCFRunLoopCommonModes); + } + CFRelease(self->timer); + self->timer = NULL; + } + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static PyObject* +Timer_repr(Timer* self) +{ +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_FromFormat("Timer object %p wrapping CFRunLoopTimerRef %p", + (void*) self, (void*)(self->timer)); +#else + return PyString_FromFormat("Timer object %p wrapping CFRunLoopTimerRef %p", + (void*) self, (void*)(self->timer)); +#endif +} + +static char Timer_doc[] = +"A Timer object wraps a CFRunLoopTimerRef and can add it to the event loop.\n"; + +static void timer_callback(CFRunLoopTimerRef timer, void* info) +{ + PyObject* method = info; + PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject* result = PyObject_CallFunction(method, NULL); + if (result==NULL) PyErr_Print(); + PyGILState_Release(gstate); +} + +static PyObject* +Timer__timer_start(Timer* self, PyObject* args) +{ + CFRunLoopRef runloop; + CFRunLoopTimerRef timer; + CFRunLoopTimerContext context; + double milliseconds; + CFTimeInterval interval; + PyObject* attribute; + PyObject* failure; + runloop = CFRunLoopGetCurrent(); + if (!runloop) { + PyErr_SetString(PyExc_RuntimeError, "Failed to obtain run loop"); + return NULL; + } + context.version = 0; + context.retain = 0; + context.release = 0; + context.copyDescription = 0; + attribute = PyObject_GetAttrString((PyObject*)self, "_interval"); + if (attribute==NULL) + { + PyErr_SetString(PyExc_AttributeError, "Timer has no attribute '_interval'"); + return NULL; + } + milliseconds = PyFloat_AsDouble(attribute); + failure = PyErr_Occurred(); + Py_DECREF(attribute); + if (failure) return NULL; + attribute = PyObject_GetAttrString((PyObject*)self, "_single"); + if (attribute==NULL) + { + PyErr_SetString(PyExc_AttributeError, "Timer has no attribute '_single'"); + return NULL; + } + switch (PyObject_IsTrue(attribute)) { + case 1: + interval = 0; + break; + case 0: + interval = milliseconds / 1000.0; + break; + case -1: + default: + PyErr_SetString(PyExc_ValueError, "Cannot interpret _single attribute as True of False"); + return NULL; + } + attribute = PyObject_GetAttrString((PyObject*)self, "_on_timer"); + if (attribute==NULL) + { + PyErr_SetString(PyExc_AttributeError, "Timer has no attribute '_on_timer'"); + return NULL; + } + if (!PyMethod_Check(attribute)) { + PyErr_SetString(PyExc_RuntimeError, "_on_timer should be a Python method"); + return NULL; + } + context.info = attribute; + timer = CFRunLoopTimerCreate(kCFAllocatorDefault, + 0, + interval, + 0, + 0, + timer_callback, + &context); + if (!timer) { + PyErr_SetString(PyExc_RuntimeError, "Failed to create timer"); + return NULL; + } + Py_INCREF(attribute); + if (self->timer) { + CFRunLoopTimerGetContext(self->timer, &context); + attribute = context.info; + Py_DECREF(attribute); + CFRunLoopRemoveTimer(runloop, self->timer, kCFRunLoopCommonModes); + CFRelease(self->timer); + } + CFRunLoopAddTimer(runloop, timer, kCFRunLoopCommonModes); + /* Don't release the timer here, since the run loop may be destroyed and + * the timer lost before we have a chance to decrease the reference count + * of the attribute */ + self->timer = timer; + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef Timer_methods[] = { + {"_timer_start", + (PyCFunction)Timer__timer_start, + METH_VARARGS, + "Initialize and start the timer." + }, + {NULL} /* Sentinel */ +}; + +static PyTypeObject TimerType = { + PyVarObject_HEAD_INIT(NULL, 0) + "_macosx.Timer", /*tp_name*/ + sizeof(Timer), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)Timer_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + (reprfunc)Timer_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Timer_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Timer_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + Timer_new, /* tp_new */ +}; + static struct PyMethodDef methods[] = { {"show", (PyCFunction)show, @@ -5177,33 +5792,65 @@ - (int)index {NULL, NULL, 0, NULL}/* sentinel */ }; +#if PY_MAJOR_VERSION >= 3 + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_macosx", + "Mac OS X native backend", + -1, + methods, + NULL, + NULL, + NULL, + NULL +}; + +PyObject* PyInit__macosx(void) + +#else + void init_macosx(void) -{ PyObject *m; +#endif +{ PyObject *module; import_array(); - if (PyType_Ready(&GraphicsContextType) < 0) return; - if (PyType_Ready(&FigureCanvasType) < 0) return; - if (PyType_Ready(&FigureManagerType) < 0) return; - if (PyType_Ready(&NavigationToolbarType) < 0) return; - if (PyType_Ready(&NavigationToolbar2Type) < 0) return; + if (PyType_Ready(&GraphicsContextType) < 0 + || PyType_Ready(&FigureCanvasType) < 0 + || PyType_Ready(&FigureManagerType) < 0 + || PyType_Ready(&NavigationToolbarType) < 0 + || PyType_Ready(&NavigationToolbar2Type) < 0 + || PyType_Ready(&TimerType) < 0) +#if PY_MAJOR_VERSION >= 3 + return NULL; +#else + return; +#endif - m = Py_InitModule4("_macosx", - methods, - "Mac OS X native backend", - NULL, - PYTHON_API_VERSION); +#if PY_MAJOR_VERSION >= 3 + module = PyModule_Create(&moduledef); + if (module==NULL) return NULL; +#else + module = Py_InitModule4("_macosx", + methods, + "Mac OS X native backend", + NULL, + PYTHON_API_VERSION); +#endif Py_INCREF(&GraphicsContextType); Py_INCREF(&FigureCanvasType); Py_INCREF(&FigureManagerType); Py_INCREF(&NavigationToolbarType); Py_INCREF(&NavigationToolbar2Type); - PyModule_AddObject(m, "GraphicsContext", (PyObject*) &GraphicsContextType); - PyModule_AddObject(m, "FigureCanvas", (PyObject*) &FigureCanvasType); - PyModule_AddObject(m, "FigureManager", (PyObject*) &FigureManagerType); - PyModule_AddObject(m, "NavigationToolbar", (PyObject*) &NavigationToolbarType); - PyModule_AddObject(m, "NavigationToolbar2", (PyObject*) &NavigationToolbar2Type); + Py_INCREF(&TimerType); + PyModule_AddObject(module, "GraphicsContext", (PyObject*) &GraphicsContextType); + PyModule_AddObject(module, "FigureCanvas", (PyObject*) &FigureCanvasType); + PyModule_AddObject(module, "FigureManager", (PyObject*) &FigureManagerType); + PyModule_AddObject(module, "NavigationToolbar", (PyObject*) &NavigationToolbarType); + PyModule_AddObject(module, "NavigationToolbar2", (PyObject*) &NavigationToolbar2Type); + PyModule_AddObject(module, "Timer", (PyObject*) &TimerType); PyOS_InputHook = wait_for_stdin; } diff --git a/src/_path.cpp b/src/_path.cpp index e53611c5aff4..54c4492a7d6e 100644 --- a/src/_path.cpp +++ b/src/_path.cpp @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + #include "agg_py_path_iterator.h" #include "agg_py_transforms.h" #include "path_converters.h" diff --git a/src/_png.cpp b/src/_png.cpp index 9f12389166a4..a2342dc8ebd3 100644 --- a/src/_png.cpp +++ b/src/_png.cpp @@ -1,3 +1,4 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ /* For linux, png.h must be imported before Python.h because png.h needs to be the one to define setjmp. @@ -350,10 +351,10 @@ _png_module::read_png(const Py::Tuple& args) png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); - png_uint_32 width = info_ptr->width; - png_uint_32 height = info_ptr->height; + png_uint_32 width = png_get_image_width(png_ptr, info_ptr); + png_uint_32 height = png_get_image_height(png_ptr, info_ptr); - int bit_depth = info_ptr->bit_depth; + int bit_depth = png_get_bit_depth(png_ptr, info_ptr); // Unpack 1, 2, and 4-bit images if (bit_depth < 8) @@ -361,7 +362,7 @@ _png_module::read_png(const Py::Tuple& args) // If sig bits are set, shift data png_color_8p sig_bit; - if ((info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) && + if ((png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_PALETTE) && png_get_sBIT(png_ptr, info_ptr, &sig_bit)) { png_set_shift(png_ptr, sig_bit); @@ -374,13 +375,13 @@ _png_module::read_png(const Py::Tuple& args) } // Convert palletes to full RGB - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); } // If there's an alpha channel convert gray to RGB - if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); } @@ -408,11 +409,11 @@ _png_module::read_png(const Py::Tuple& args) npy_intp dimensions[3]; dimensions[0] = height; //numrows dimensions[1] = width; //numcols - if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA) { dimensions[2] = 4; //RGBA images } - else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + else if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_COLOR) { dimensions[2] = 3; //RGB images } @@ -421,7 +422,8 @@ _png_module::read_png(const Py::Tuple& args) dimensions[2] = 1; //Greyscale images } //For gray, return an x by y array, not an x by y by 1 - int num_dims = (info_ptr->color_type & PNG_COLOR_MASK_COLOR) ? 3 : 2; + int num_dims = (png_get_color_type(png_ptr, info_ptr) + & PNG_COLOR_MASK_COLOR) ? 3 : 2; double max_value = (1 << ((bit_depth < 8) ? 8 : bit_depth)) - 1; PyArrayObject *A = (PyArrayObject *) PyArray_SimpleNew( diff --git a/src/_subprocess.c b/src/_subprocess.c deleted file mode 100644 index c93f84bd1fe7..000000000000 --- a/src/_subprocess.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * support routines for subprocess module - * - * Currently, this extension module is only required when using the - * subprocess module on Windows, but in the future, stubs for other - * platforms might be added here as well. - * - * Copyright (c) 2004 by Fredrik Lundh - * Copyright (c) 2004 by Secret Labs AB, http://www.pythonware.com - * Copyright (c) 2004 by Peter Astrand - * - * By obtaining, using, and/or copying this software and/or its - * associated documentation, you agree that you have read, understood, - * and will comply with the following terms and conditions: - * - * Permission to use, copy, modify, and distribute this software and - * its associated documentation for any purpose and without fee is - * hereby granted, provided that the above copyright notice appears in - * all copies, and that both that copyright notice and this permission - * notice appear in supporting documentation, and that the name of the - * authors not be used in advertising or publicity pertaining to - * distribution of the software without specific, written prior - * permission. - * - * THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. - * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS - * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -/* Licensed to PSF under a Contributor Agreement. */ -/* See http://www.python.org/2.4/license for licensing details. */ - -/* TODO: handle unicode command lines? */ -/* TODO: handle unicode environment? */ - -#include "Python.h" - -#define WINDOWS_LEAN_AND_MEAN -#include "windows.h" - -/* -------------------------------------------------------------------- */ -/* handle wrapper. note that this library uses integers when passing - handles to a function, and handle wrappers when returning handles. - the wrapper is used to provide Detach and Close methods */ - -typedef struct { - PyObject_HEAD - HANDLE handle; -} sp_handle_object; - -staticforward PyTypeObject sp_handle_type; - -static PyObject* -sp_handle_new(HANDLE handle) -{ - sp_handle_object* self; - - self = PyObject_NEW(sp_handle_object, &sp_handle_type); - if (self == NULL) - return NULL; - - self->handle = handle; - - return (PyObject*) self; -} - -static PyObject* -sp_handle_detach(sp_handle_object* self, PyObject* args) -{ - HANDLE handle; - - if (! PyArg_ParseTuple(args, ":Detach")) - return NULL; - - handle = self->handle; - - self->handle = NULL; - - /* note: return the current handle, as an integer */ - return PyInt_FromLong((long) handle); -} - -static PyObject* -sp_handle_close(sp_handle_object* self, PyObject* args) -{ - if (! PyArg_ParseTuple(args, ":Close")) - return NULL; - - if (self->handle != INVALID_HANDLE_VALUE) { - CloseHandle(self->handle); - self->handle = INVALID_HANDLE_VALUE; - } - Py_INCREF(Py_None); - return Py_None; -} - -static void -sp_handle_dealloc(sp_handle_object* self) -{ - if (self->handle != INVALID_HANDLE_VALUE) - CloseHandle(self->handle); - PyObject_FREE(self); -} - -static PyMethodDef sp_handle_methods[] = { - {"Detach", (PyCFunction) sp_handle_detach, METH_VARARGS}, - {"Close", (PyCFunction) sp_handle_close, METH_VARARGS}, - {NULL, NULL} -}; - -static PyObject* -sp_handle_getattr(sp_handle_object* self, char* name) -{ - return Py_FindMethod(sp_handle_methods, (PyObject*) self, name); -} - -static PyObject* -sp_handle_as_int(sp_handle_object* self) -{ - return PyInt_FromLong((long) self->handle); -} - -static PyNumberMethods sp_handle_as_number; - -statichere PyTypeObject sp_handle_type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "_subprocess_handle", sizeof(sp_handle_object), 0, - (destructor) sp_handle_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc) sp_handle_getattr,/*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - &sp_handle_as_number, /*tp_as_number */ - 0, /*tp_as_sequence */ - 0, /*tp_as_mapping */ - 0 /*tp_hash*/ -}; - -/* -------------------------------------------------------------------- */ -/* windows API functions */ - -static PyObject * -sp_GetStdHandle(PyObject* self, PyObject* args) -{ - HANDLE handle; - int std_handle; - - if (! PyArg_ParseTuple(args, "i:GetStdHandle", &std_handle)) - return NULL; - - Py_BEGIN_ALLOW_THREADS - handle = GetStdHandle((DWORD) std_handle); - Py_END_ALLOW_THREADS - - if (handle == INVALID_HANDLE_VALUE) - return PyErr_SetFromWindowsErr(GetLastError()); - - if (! handle) { - Py_INCREF(Py_None); - return Py_None; - } - - /* note: returns integer, not handle object */ - return PyInt_FromLong((long) handle); -} - -static PyObject * -sp_GetCurrentProcess(PyObject* self, PyObject* args) -{ - if (! PyArg_ParseTuple(args, ":GetCurrentProcess")) - return NULL; - - return sp_handle_new(GetCurrentProcess()); -} - -static PyObject * -sp_DuplicateHandle(PyObject* self, PyObject* args) -{ - HANDLE target_handle; - BOOL result; - - long source_process_handle; - long source_handle; - long target_process_handle; - int desired_access; - int inherit_handle; - int options = 0; - - if (! PyArg_ParseTuple(args, "lllii|i:DuplicateHandle", - &source_process_handle, - &source_handle, - &target_process_handle, - &desired_access, - &inherit_handle, - &options)) - return NULL; - - Py_BEGIN_ALLOW_THREADS - result = DuplicateHandle( - (HANDLE) source_process_handle, - (HANDLE) source_handle, - (HANDLE) target_process_handle, - &target_handle, - desired_access, - inherit_handle, - options - ); - Py_END_ALLOW_THREADS - - if (! result) - return PyErr_SetFromWindowsErr(GetLastError()); - - return sp_handle_new(target_handle); -} - -static PyObject * -sp_CreatePipe(PyObject* self, PyObject* args) -{ - HANDLE read_pipe; - HANDLE write_pipe; - BOOL result; - - PyObject* pipe_attributes; /* ignored */ - int size; - - if (! PyArg_ParseTuple(args, "Oi:CreatePipe", &pipe_attributes, &size)) - return NULL; - - Py_BEGIN_ALLOW_THREADS - result = CreatePipe(&read_pipe, &write_pipe, NULL, size); - Py_END_ALLOW_THREADS - - if (! result) - return PyErr_SetFromWindowsErr(GetLastError()); - - return Py_BuildValue( - "NN", sp_handle_new(read_pipe), sp_handle_new(write_pipe)); -} - -/* helpers for createprocess */ - -static int -getint(PyObject* obj, char* name) -{ - PyObject* value; - int ret; - - value = PyObject_GetAttrString(obj, name); - if (! value) { - PyErr_Clear(); /* FIXME: propagate error? */ - return 0; - } - ret = (int) PyInt_AsLong(value); - Py_DECREF(value); - return ret; -} - -static HANDLE -gethandle(PyObject* obj, char* name) -{ - sp_handle_object* value; - HANDLE ret; - - value = (sp_handle_object*) PyObject_GetAttrString(obj, name); - if (! value) { - PyErr_Clear(); /* FIXME: propagate error? */ - return NULL; - } - if (value->ob_type != &sp_handle_type) - ret = NULL; - else - ret = value->handle; - Py_DECREF(value); - return ret; -} - -static PyObject* -getenvironment(PyObject* environment) -{ - int i, envsize; - PyObject* out = NULL; - PyObject* keys; - PyObject* values; - char* p; - - /* convert environment dictionary to windows enviroment string */ - if (! PyMapping_Check(environment)) { - PyErr_SetString( - PyExc_TypeError, "environment must be dictionary or None"); - return NULL; - } - - envsize = PyMapping_Length(environment); - - keys = PyMapping_Keys(environment); - values = PyMapping_Values(environment); - if (!keys || !values) - goto error; - - out = PyString_FromStringAndSize(NULL, 2048); - if (! out) - goto error; - - p = PyString_AS_STRING(out); - - for (i = 0; i < envsize; i++) { - int ksize, vsize, totalsize; - PyObject* key = PyList_GET_ITEM(keys, i); - PyObject* value = PyList_GET_ITEM(values, i); - - if (! PyString_Check(key) || ! PyString_Check(value)) { - PyErr_SetString(PyExc_TypeError, - "environment can only contain strings"); - goto error; - } - ksize = PyString_GET_SIZE(key); - vsize = PyString_GET_SIZE(value); - totalsize = (p - PyString_AS_STRING(out)) + ksize + 1 + - vsize + 1 + 1; - if (totalsize > PyString_GET_SIZE(out)) { - int offset = p - PyString_AS_STRING(out); - _PyString_Resize(&out, totalsize + 1024); - p = PyString_AS_STRING(out) + offset; - } - memcpy(p, PyString_AS_STRING(key), ksize); - p += ksize; - *p++ = '='; - memcpy(p, PyString_AS_STRING(value), vsize); - p += vsize; - *p++ = '\0'; - } - - /* add trailing null byte */ - *p++ = '\0'; - _PyString_Resize(&out, p - PyString_AS_STRING(out)); - - /* PyObject_Print(out, stdout, 0); */ - - Py_XDECREF(keys); - Py_XDECREF(values); - - return out; - - error: - Py_XDECREF(out); - Py_XDECREF(keys); - Py_XDECREF(values); - return NULL; -} - -static PyObject * -sp_CreateProcess(PyObject* self, PyObject* args) -{ - BOOL result; - PROCESS_INFORMATION pi; - STARTUPINFO si; - PyObject* environment; - - char* application_name; - char* command_line; - PyObject* process_attributes; /* ignored */ - PyObject* thread_attributes; /* ignored */ - int inherit_handles; - int creation_flags; - PyObject* env_mapping; - char* current_directory; - PyObject* startup_info; - - if (! PyArg_ParseTuple(args, "zzOOiiOzO:CreateProcess", - &application_name, - &command_line, - &process_attributes, - &thread_attributes, - &inherit_handles, - &creation_flags, - &env_mapping, - ¤t_directory, - &startup_info)) - return NULL; - - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - - /* note: we only support a small subset of all SI attributes */ - si.dwFlags = getint(startup_info, "dwFlags"); - si.wShowWindow = getint(startup_info, "wShowWindow"); - si.hStdInput = gethandle(startup_info, "hStdInput"); - si.hStdOutput = gethandle(startup_info, "hStdOutput"); - si.hStdError = gethandle(startup_info, "hStdError"); - - if (PyErr_Occurred()) - return NULL; - - if (env_mapping == Py_None) - environment = NULL; - else { - environment = getenvironment(env_mapping); - if (! environment) - return NULL; - } - - Py_BEGIN_ALLOW_THREADS - result = CreateProcess(application_name, - command_line, - NULL, - NULL, - inherit_handles, - creation_flags, - environment ? PyString_AS_STRING(environment) : NULL, - current_directory, - &si, - &pi); - Py_END_ALLOW_THREADS - - Py_XDECREF(environment); - - if (! result) - return PyErr_SetFromWindowsErr(GetLastError()); - - return Py_BuildValue("NNii", - sp_handle_new(pi.hProcess), - sp_handle_new(pi.hThread), - pi.dwProcessId, - pi.dwThreadId); -} - -static PyObject * -sp_TerminateProcess(PyObject* self, PyObject* args) -{ - BOOL result; - - long process; - int exit_code; - if (! PyArg_ParseTuple(args, "li:TerminateProcess", &process, - &exit_code)) - return NULL; - - result = TerminateProcess((HANDLE) process, exit_code); - - if (! result) - return PyErr_SetFromWindowsErr(GetLastError()); - - Py_INCREF(Py_None); - return Py_None; -} - -static PyObject * -sp_GetExitCodeProcess(PyObject* self, PyObject* args) -{ - DWORD exit_code; - BOOL result; - - long process; - if (! PyArg_ParseTuple(args, "l:GetExitCodeProcess", &process)) - return NULL; - - result = GetExitCodeProcess((HANDLE) process, &exit_code); - - if (! result) - return PyErr_SetFromWindowsErr(GetLastError()); - - return PyInt_FromLong(exit_code); -} - -static PyObject * -sp_WaitForSingleObject(PyObject* self, PyObject* args) -{ - DWORD result; - - long handle; - int milliseconds; - if (! PyArg_ParseTuple(args, "li:WaitForSingleObject", - &handle, - &milliseconds)) - return NULL; - - Py_BEGIN_ALLOW_THREADS - result = WaitForSingleObject((HANDLE) handle, (DWORD) milliseconds); - Py_END_ALLOW_THREADS - - if (result == WAIT_FAILED) - return PyErr_SetFromWindowsErr(GetLastError()); - - return PyInt_FromLong((int) result); -} - -static PyObject * -sp_GetVersion(PyObject* self, PyObject* args) -{ - if (! PyArg_ParseTuple(args, ":GetVersion")) - return NULL; - - return PyInt_FromLong((int) GetVersion()); -} - -static PyObject * -sp_GetModuleFileName(PyObject* self, PyObject* args) -{ - BOOL result; - long module; - TCHAR filename[MAX_PATH]; - - if (! PyArg_ParseTuple(args, "l:GetModuleFileName", &module)) - return NULL; - - result = GetModuleFileName((HMODULE)module, filename, MAX_PATH); - filename[MAX_PATH-1] = '\0'; - - if (! result) - return PyErr_SetFromWindowsErr(GetLastError()); - - return PyString_FromString(filename); -} - -static PyMethodDef sp_functions[] = { - {"GetStdHandle", sp_GetStdHandle, METH_VARARGS}, - {"GetCurrentProcess", sp_GetCurrentProcess, METH_VARARGS}, - {"DuplicateHandle", sp_DuplicateHandle, METH_VARARGS}, - {"CreatePipe", sp_CreatePipe, METH_VARARGS}, - {"CreateProcess", sp_CreateProcess, METH_VARARGS}, - {"TerminateProcess", sp_TerminateProcess, METH_VARARGS}, - {"GetExitCodeProcess", sp_GetExitCodeProcess, METH_VARARGS}, - {"WaitForSingleObject", sp_WaitForSingleObject, METH_VARARGS}, - {"GetVersion", sp_GetVersion, METH_VARARGS}, - {"GetModuleFileName", sp_GetModuleFileName, METH_VARARGS}, - {NULL, NULL} -}; - -/* -------------------------------------------------------------------- */ - -static void -defint(PyObject* d, const char* name, int value) -{ - PyObject* v = PyInt_FromLong((long) value); - if (v) { - PyDict_SetItemString(d, (char*) name, v); - Py_DECREF(v); - } -} - -#if PY_VERSION_HEX >= 0x02030000 -PyMODINIT_FUNC -#else -DL_EXPORT(void) -#endif -init_subprocess() -{ - PyObject *d; - PyObject *m; - - /* patch up object descriptors */ - sp_handle_type.ob_type = &PyType_Type; - sp_handle_as_number.nb_int = (unaryfunc) sp_handle_as_int; - - m = Py_InitModule("_subprocess", sp_functions); - if (m == NULL) - return; - d = PyModule_GetDict(m); - - /* constants */ - defint(d, "STD_INPUT_HANDLE", STD_INPUT_HANDLE); - defint(d, "STD_OUTPUT_HANDLE", STD_OUTPUT_HANDLE); - defint(d, "STD_ERROR_HANDLE", STD_ERROR_HANDLE); - defint(d, "DUPLICATE_SAME_ACCESS", DUPLICATE_SAME_ACCESS); - defint(d, "STARTF_USESTDHANDLES", STARTF_USESTDHANDLES); - defint(d, "STARTF_USESHOWWINDOW", STARTF_USESHOWWINDOW); - defint(d, "SW_HIDE", SW_HIDE); - defint(d, "INFINITE", INFINITE); - defint(d, "WAIT_OBJECT_0", WAIT_OBJECT_0); - defint(d, "CREATE_NEW_CONSOLE", CREATE_NEW_CONSOLE); -} diff --git a/src/_tkagg.cpp b/src/_tkagg.cpp index f8074aab97b2..925c2b585c34 100644 --- a/src/_tkagg.cpp +++ b/src/_tkagg.cpp @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + /* * The Python Imaging Library. * $Id$ diff --git a/src/_ttconv.cpp b/src/_ttconv.cpp index 5753de5b7956..b203a138a442 100644 --- a/src/_ttconv.cpp +++ b/src/_ttconv.cpp @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + /* _ttconv.c diff --git a/src/_windowing.cpp b/src/_windowing.cpp index 1b4083c76dbc..949944c0539a 100644 --- a/src/_windowing.cpp +++ b/src/_windowing.cpp @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + #include "Python.h" #include diff --git a/src/_wxagg.cpp b/src/_wxagg.cpp deleted file mode 100644 index 1586c4f96081..000000000000 --- a/src/_wxagg.cpp +++ /dev/null @@ -1,273 +0,0 @@ -// File: _wxagg.cpp -// Purpose: Accelerate WXAgg by doing the agg->wxWidgets conversions in C++. -// Author: Ken McIvor -// -// Copyright 2005 Illinois Institute of Technology -// Derived from `_gtkagg.cpp', Copyright 2004-2005 John Hunter -// -// See the file "LICENSE" for information on usage and redistribution -// of this file, and for a DISCLAIMER OF ALL WARRANTIES. - - -// TODO: -// * Better type checking. -// -// * Make the `bbox' argument optional. -// -// * Determine if there are any thread-safety issues with this implementation. -// -// * Perform some AGG kung-fu to let us slice a region out of a -// rendering_buffer and convert it from RGBA to RGB on the fly, rather than -// making itermediate copies. This could be of use in _gtkagg and _tkagg as -// well. -// -// * Write an agg_to_wx_bitmap() that works more like agg_to_gtk_drawable(), -// drawing directly to a bitmap. -// -// This was the initial plan, except that I had not idea how to take a -// wx.Bitmap Python shadow class and turn it into a wxBitmap pointer. -// -// It appears that this is the way to do it: -// bool success = wxPyConvertSwigPtr(pyBitmap, (void**)&bitmap, -// _T("wxBitmap")); -// -// I'm not sure this will speed things up much, since wxWidgets requires you -// to go AGG->wx.Image->wx.Bitmap before you can blit using a MemoryDC. - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "agg_basics.h" -#include "_backend_agg.h" -#include "agg_pixfmt_rgba.h" -#include "util/agg_color_conv_rgb8.h" -#include "agg_py_transforms.h" - -#include -#include -#include - - -// forward declarations -static wxImage *convert_agg2image(RendererAgg *aggRenderer, Py::Object clipbox); -static wxBitmap *convert_agg2bitmap(RendererAgg *aggRenderer, Py::Object clipbox); - - -// the extension module -class _wxagg_module : public Py::ExtensionModule<_wxagg_module> -{ -public: - - _wxagg_module() - : Py::ExtensionModule<_wxagg_module>("_wxkagg") - { - add_varargs_method("convert_agg_to_wx_image", - &_wxagg_module::convert_agg_to_wx_image, - "Convert the region of the agg buffer bounded by bbox to a wx.Image." - " If bbox\nis None, the entire buffer is converted.\n\nNote: agg must" - " be a backend_agg.RendererAgg instance."); - - add_varargs_method("convert_agg_to_wx_bitmap", - &_wxagg_module::convert_agg_to_wx_bitmap, - "Convert the region of the agg buffer bounded by bbox to a wx.Bitmap." - " If bbox\nis None, the entire buffer is converted.\n\nNote: agg must" - " be a backend_agg.RendererAgg instance."); - - initialize("The _wxagg module"); - } - - virtual ~_wxagg_module() {} - -private: - - Py::Object convert_agg_to_wx_image(const Py::Tuple &args) - { - args.verify_length(2); - - RendererAgg* aggRenderer = static_cast( - args[0].getAttr("_renderer").ptr()); - - Py::Object clipbox = args[1]; - - // convert the buffer - wxImage *image = convert_agg2image(aggRenderer, clipbox); - - // wrap a wx.Image around the result and return it - PyObject *pyWxImage = wxPyConstructObject(image, _T("wxImage"), 1); - if (pyWxImage == NULL) - { - throw Py::MemoryError( - "_wxagg.convert_agg_to_wx_image(): could not create the wx.Image"); - } - - return Py::asObject(pyWxImage); - } - - - Py::Object convert_agg_to_wx_bitmap(const Py::Tuple &args) - { - args.verify_length(2); - - RendererAgg* aggRenderer = static_cast( - args[0].getAttr("_renderer").ptr()); - - Py::Object clipbox = args[1]; - - // convert the buffer - wxBitmap *bitmap = convert_agg2bitmap(aggRenderer, clipbox); - - // wrap a wx.Bitmap around the result and return it - PyObject *pyWxBitmap = wxPyConstructObject(bitmap, _T("wxBitmap"), 1); - if (pyWxBitmap == NULL) - { - throw Py::MemoryError( - "_wxagg.convert_agg_to_wx_bitmap(): could not create the wx.Bitmap"); - } - - return Py::asObject(pyWxBitmap); - } -}; - - -// -// Implementation Functions -// - -static wxImage *convert_agg2image(RendererAgg *aggRenderer, Py::Object clipbox) -{ - int srcWidth = 1; - int srcHeight = 1; - int srcStride = 1; - - bool deleteSrcBuffer = false; - agg::int8u *srcBuffer = NULL; - - double l, b, r, t; - - if (!py_convert_bbox(clipbox.ptr(), l, b, r, t)) - { - // Convert everything: rgba => rgb -> image - srcBuffer = aggRenderer->pixBuffer; - srcWidth = (int) aggRenderer->get_width(); - srcHeight = (int) aggRenderer->get_height(); - srcStride = (int) aggRenderer->get_width() * 4; - } - else - { - // Convert a region: rgba => clipped rgba => rgb -> image - srcWidth = (int)(r - l); - srcHeight = (int)(t - b); - srcStride = srcWidth * 4; - - deleteSrcBuffer = true; - srcBuffer = new agg::int8u[srcStride*srcHeight]; - if (srcBuffer == NULL) - { - throw Py::MemoryError( - "_wxagg::convert_agg2image(): could not allocate `srcBuffer'"); - } - - int h = (int) aggRenderer->get_height(); - agg::rect_base region( - (int) l, // x1 - h - (int) t, // y1 - (int) r, // x2 - h - (int) b); // y2 - - agg::rendering_buffer rbuf; - rbuf.attach(srcBuffer, srcWidth, srcHeight, srcStride); - pixfmt pf(rbuf); - renderer_base rndr(pf); - rndr.copy_from(aggRenderer->renderingBuffer, ®ion, - (int) - l, (int)(t - h)); - } - - // allocate the RGB data array - - // use malloc(3) because wxImage will use free(3) - agg::int8u *destBuffer = (agg::int8u *) malloc( - sizeof(agg::int8u) * srcWidth * 3 * srcHeight); - - if (destBuffer == NULL) - { - if (deleteSrcBuffer) - { - delete [] srcBuffer; - } - - throw Py::MemoryError( - "_wxagg::convert_agg2image(): could not allocate `destBuffer'"); - } - - // convert from RGBA to RGB - agg::rendering_buffer rbSource; - rbSource.attach(srcBuffer, srcWidth, srcHeight, srcStride); - - agg::rendering_buffer rbDest; - rbDest.attach(destBuffer, srcWidth, srcHeight, srcWidth*3); - - agg::color_conv(&rbDest, &rbSource, agg::color_conv_rgba32_to_rgb24()); - - // Create a wxImage using the RGB data - wxImage *image = new wxImage(srcWidth, srcHeight, destBuffer); - if (image == NULL) - { - if (deleteSrcBuffer) - { - delete [] srcBuffer; - } - - free(destBuffer); - throw Py::MemoryError( - "_wxagg::convert_agg2image(): could not allocate `image'"); - } - - if (deleteSrcBuffer) - { - delete [] srcBuffer; - } - - return image; -} - - -static wxBitmap *convert_agg2bitmap(RendererAgg *aggRenderer, Py::Object clipbox) -{ - // Convert everything: rgba => rgb -> image => bitmap - // Convert a region: rgba => clipped rgba => rgb -> image => bitmap - wxImage *image = convert_agg2image(aggRenderer, clipbox); - wxBitmap *bitmap = new wxBitmap(*image); - - image->Destroy(); - delete image; - - if (bitmap == NULL) - { - throw Py::MemoryError( - "_wxagg::convert_agg2bitmap(): could not allocate `bitmap'"); - } - - return bitmap; -} - - -// -// Module Initialization -// - -extern "C" - DL_EXPORT(void) - init_wxagg(void) -{ - wxPyCoreAPI_IMPORT(); - //suppress an unused variable warning by creating _wxagg_module in two lines - static _wxagg_module* _wxagg = NULL; - _wxagg = new _wxagg_module; -}; diff --git a/src/agg_py_path_iterator.h b/src/agg_py_path_iterator.h index b014739ae89a..8449dd2054d8 100644 --- a/src/agg_py_path_iterator.h +++ b/src/agg_py_path_iterator.h @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + #ifndef __AGG_PY_PATH_ITERATOR_H__ #define __AGG_PY_PATH_ITERATOR_H__ diff --git a/src/agg_py_transforms.cpp b/src/agg_py_transforms.cpp index bee369cfb0a7..457d5e7f602c 100644 --- a/src/agg_py_transforms.cpp +++ b/src/agg_py_transforms.cpp @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + #include #define NO_IMPORT_ARRAY @@ -75,7 +77,7 @@ py_convert_bbox(PyObject* bbox_obj, double& l, double& b, double& r, double& t) if (!bbox || PyArray_NDIM(bbox) != 2 || PyArray_DIM(bbox, 0) != 2 || PyArray_DIM(bbox, 1) != 2) { throw Py::TypeError - ("Argument 3 to agg_to_gtk_drawable must be a Bbox object."); + ("Expected a bbox array"); } l = *(double*)PyArray_GETPTR2(bbox, 0, 0); diff --git a/src/agg_py_transforms.h b/src/agg_py_transforms.h index 4b5abdd657a5..db66a2ff4d79 100644 --- a/src/agg_py_transforms.h +++ b/src/agg_py_transforms.h @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + #ifndef __AGG_PY_TRANSFORMS_H__ #define __AGG_PY_TRANSFORMS_H__ diff --git a/src/cntr.c b/src/cntr.c index b1118aa84673..631471f89542 100644 --- a/src/cntr.c +++ b/src/cntr.c @@ -1,3 +1,5 @@ +/* -*- mode: c; c-basic-offset: 4 -*- */ + /* cntr.c General purpose contour tracer for quadrilateral meshes. @@ -18,7 +20,7 @@ #include "structmember.h" #include #include -#include "numerix.h" +#include "numpy/arrayobject.h" /* Note that all arrays in these routines are Fortran-style, in the sense that the "i" index varies fastest; the dimensions @@ -1804,7 +1806,7 @@ Cntr_init(Cntr *self, PyObject *args, PyObject *kwds) PyArray_DOUBLE, 2, 2); if (marg) mpa = (PyArrayObject *) PyArray_ContiguousFromObject(marg, - PyArray_SBYTE, 2, 2); + PyArray_BYTE, 2, 2); else mpa = NULL; diff --git a/src/ft2font.cpp b/src/ft2font.cpp index ae522fe2aaf1..b4228058ce41 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + #include "ft2font.h" #include "mplutils.h" #include diff --git a/src/ft2font.h b/src/ft2font.h index 86b5a130ef8d..827622c19b9a 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + /* A python interface to freetype2 */ #ifndef _FT2FONT_H #define _FT2FONT_H diff --git a/src/mplutils.cpp b/src/mplutils.cpp index 766173b189ba..febb97ac81a0 100644 --- a/src/mplutils.cpp +++ b/src/mplutils.cpp @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + #include #include #include diff --git a/src/mplutils.h b/src/mplutils.h index d3c269f3d1d4..e0305f142f50 100644 --- a/src/mplutils.h +++ b/src/mplutils.h @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + /* mplutils.h -- * * $Header$ diff --git a/src/numerix.h b/src/numerix.h deleted file mode 100644 index d4aa83aa757e..000000000000 --- a/src/numerix.h +++ /dev/null @@ -1,11 +0,0 @@ -/* numerix.h -- John Hunter - */ - -#ifndef _NUMERIX_H -#define _NUMERIX_H -#include "numpy/arrayobject.h" -#if (NDARRAY_VERSION >= 0x00090908) -#include "numpy/oldnumeric.h" -#endif - -#endif diff --git a/src/nxutils.c b/src/nxutils.c index bf8b35374b62..c88bdf40c6df 100644 --- a/src/nxutils.c +++ b/src/nxutils.c @@ -1,3 +1,5 @@ +/* -*- mode: c; c-basic-offset: 4 -*- */ + #include #include "structmember.h" #include @@ -24,8 +26,8 @@ int pnpoly_api(int npol, double *xp, double *yp, double x, double y) int i, j, c = 0; for (i = 0, j = npol-1; i < npol; j = i++) { if ((((yp[i]<=y) && (ydimensions[1]!=2) { PyErr_SetString(PyExc_ValueError, - "Arguments verts must be a Nx2 array."); + "Arguments verts must be a Nx2 array."); Py_XDECREF(verts); return NULL; @@ -118,7 +120,7 @@ points_inside_poly(PyObject *self, PyObject *args) if (verts == NULL) { PyErr_SetString(PyExc_ValueError, - "Argument verts must be a Nx2 array."); + "Argument verts must be a Nx2 array."); Py_XDECREF(verts); return NULL; @@ -129,7 +131,7 @@ points_inside_poly(PyObject *self, PyObject *args) if (verts->dimensions[1]!=2) { PyErr_SetString(PyExc_ValueError, - "Arguments verts must be a Nx2 array."); + "Arguments verts must be a Nx2 array."); Py_XDECREF(verts); return NULL; @@ -163,7 +165,7 @@ points_inside_poly(PyObject *self, PyObject *args) if (xypoints == NULL) { PyErr_SetString(PyExc_ValueError, - "Arguments xypoints must an Nx2 array."); + "Arguments xypoints must an Nx2 array."); Py_XDECREF(verts); Py_XDECREF(xypoints); PyMem_Free(xv); @@ -175,7 +177,7 @@ points_inside_poly(PyObject *self, PyObject *args) if (xypoints->dimensions[1]!=2) { PyErr_SetString(PyExc_ValueError, - "Arguments xypoints must be a Nx2 array."); + "Arguments xypoints must be a Nx2 array."); Py_XDECREF(verts); Py_XDECREF(xypoints); diff --git a/src/path_cleanup.cpp b/src/path_cleanup.cpp index 708509390799..7e9bee6367aa 100644 --- a/src/path_cleanup.cpp +++ b/src/path_cleanup.cpp @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + #include #define NO_IMPORT_ARRAY #include "numpy/arrayobject.h" diff --git a/src/path_cleanup.h b/src/path_cleanup.h index 43bdcb983230..e5f781f9d9a0 100644 --- a/src/path_cleanup.h +++ b/src/path_cleanup.h @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + #ifndef PATH_CLEANUP_H #define PATH_CLEANUP_H diff --git a/src/path_converters.h b/src/path_converters.h index 7d6c1ea4eb98..c45ff862c6f6 100644 --- a/src/path_converters.h +++ b/src/path_converters.h @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + #ifndef __PATH_CONVERTERS_H__ #define __PATH_CONVERTERS_H__ diff --git a/test/_buildbot_doc.sh b/test/_buildbot_doc.sh index 71feaded6b1f..6551ee0d2d3b 100755 --- a/test/_buildbot_doc.sh +++ b/test/_buildbot_doc.sh @@ -8,8 +8,8 @@ source $TARGET/bin/activate echo "removing MPL config dir" python -c "import shutil,matplotlib; x=matplotlib.get_configdir(); shutil.rmtree(x)" -echo "calling 'easy_install sphinx'" -easy_install sphinx +echo "calling 'easy_install Sphinx'" +easy_install Sphinx echo "calling 'cd doc'" cd doc diff --git a/test/_buildbot_mac_sage.sh b/test/_buildbot_mac_sage.sh index 63ee5e7fb876..31235d5d8122 100755 --- a/test/_buildbot_mac_sage.sh +++ b/test/_buildbot_mac_sage.sh @@ -1,12 +1,19 @@ #!/bin/bash set -e -rm -rf ${HOME}/.matplotlib/* rm -rf build +export MPLCONFIGDIR=${HOME}/.matplotlib_buildbot export PATH=${HOME}/dev/bin:$PATH -export PYTHON=${HOME}/dev/bin/python -export PREFIX=${HOME}/devbb +export PYTHON=/usr/bin/python2.6 +export PREFIX=${HOME}/devbb export PYTHONPATH=${PREFIX}/lib/python2.6/site-packages:${HOME}/dev/lib/python2.6/site-packages +export LD_LIBRARY_PATH=${PREFIX}/lib +export MPLSETUPCFG=test/setup.sageosx.cfg +rm -rf ${MPLCONFIGDIR}/* +rm -rf ${PREFIX}/lib/python2.6/site-packages/matplotlib* +echo 'backend : Agg' > $MPLCONFIGDIR/matplotlibrc + + make -f make.osx mpl_install echo ${PYTHONPATH} diff --git a/test/setup.sageosx.cfg b/test/setup.sageosx.cfg new file mode 100644 index 000000000000..9e6b7a48ca32 --- /dev/null +++ b/test/setup.sageosx.cfg @@ -0,0 +1,83 @@ +# Rename this file to setup.cfg to modify matplotlib's +# build options. + +[egg_info] +tag_svn_revision = 1 + +[directories] +# Uncomment to override the default basedir in setupext.py. +# This can be a single directory or a space-delimited list of directories. +#basedirlist = /usr + +[status] +# To suppress display of the dependencies and their versions +# at the top of the build log, uncomment the following line: +#suppress = True +# +# Uncomment to insert lots of diagnostic prints in extension code +#verbose = True + +[provide_packages] +# By default, matplotlib checks for a few dependencies and +# installs them if missing. This feature can be turned off +# by uncommenting the following lines. Acceptible values are: +# True: install, overwrite an existing installation +# False: do not install +# auto: install only if the package is unavailable. This +# is the default behavior +# +## Date/timezone support: +#pytz = False +#dateutil = False + +[gui_support] +# Matplotlib supports multiple GUI toolkits, including Cocoa, +# GTK, Fltk, MacOSX, Qt, Qt4, Tk, and WX. Support for many of +# these toolkits requires AGG, the Anti-Grain Geometry library, +# which is provided by matplotlib and built by default. +# +# Some backends are written in pure Python, and others require +# extension code to be compiled. By default, matplotlib checks +# for these GUI toolkits during installation and, if present, +# compiles the required extensions to support the toolkit. GTK +# support requires the GTK runtime environment and PyGTK. Wx +# support requires wxWidgets and wxPython. Tk support requires +# Tk and Tkinter. The other GUI toolkits do not require any +# extension code, and can be used as long as the libraries are +# installed on your system. +# +# You can uncomment any the following lines if you know you do +# not want to use the GUI toolkit. Acceptible values are: +# True: build the extension. Exits with a warning if the +# required dependencies are not available +# False: do not build the extension +# auto: build if the required dependencies are available, +# otherwise skip silently. This is the default +# behavior +# +gtk = False +gtkagg = False +tkagg = False +wxagg = False +macosx = False + +[rc_options] +# User-configurable options +# +# Default backend, one of: Agg, Cairo, CocoaAgg, GTK, GTKAgg, GTKCairo, +# FltkAgg, MacOSX, Pdf, Ps, QtAgg, Qt4Agg, SVG, TkAgg, WX, WXAgg. +# +# The Agg, Ps, Pdf and SVG backends do not require external +# dependencies. Do not choose GTK, GTKAgg, GTKCairo, MacOSX, TkAgg or WXAgg +# if you have disabled the relevent extension modules. Agg will be used +# by default. +# +backend = Agg +# +# The numerix module was historically used to provide +# compatibility between the Numeric, numarray, and NumPy array +# packages. Now that NumPy has emerge as the universal array +# package for python, numerix is not really necessary and is +# maintained to provide backward compatibility. Do not change +# this unless you have a compelling reason to do so. +#numerix = numpy diff --git a/ttconv/global_defines.h b/ttconv/global_defines.h index 0a6f84d314b7..04dd41e02ac1 100644 --- a/ttconv/global_defines.h +++ b/ttconv/global_defines.h @@ -1,3 +1,5 @@ +/* -*- mode: c; c-basic-offset: 4 -*- */ + /* * Modified for use within matplotlib * 5 July 2007 @@ -18,8 +20,8 @@ ** ** The PPR project was begun 28 December 1992. ** -** There are many things in this file you may want to change. This file -** should be the first include file. It is the header file for the whole +** There are many things in this file you may want to change. This file +** should be the first include file. It is the header file for the whole ** project. ** ** This file was last modified 22 December 1995. diff --git a/ttconv/truetype.h b/ttconv/truetype.h index 283163b30c2e..86be14fe3705 100644 --- a/ttconv/truetype.h +++ b/ttconv/truetype.h @@ -1,3 +1,5 @@ +/* -*- mode: c; c-basic-offset: 4 -*- */ + /* * Modified for use within matplotlib * 5 July 2007 @@ -48,37 +50,37 @@ struct TTFONT TTFONT(); ~TTFONT(); - const char *filename; /* Name of TT file */ - FILE *file; /* the open TT file */ - font_type_enum target_type; /* 42 or 3 for PS, or -3 for PDF */ + const char *filename; /* Name of TT file */ + FILE *file; /* the open TT file */ + font_type_enum target_type; /* 42 or 3 for PS, or -3 for PDF */ - ULONG numTables; /* number of tables present */ - char *PostName; /* Font's PostScript name */ - char *FullName; /* Font's full name */ - char *FamilyName; /* Font's family name */ - char *Style; /* Font's style string */ - char *Copyright; /* Font's copyright string */ - char *Version; /* Font's version string */ - char *Trademark; /* Font's trademark string */ - int llx,lly,urx,ury; /* bounding box */ + ULONG numTables; /* number of tables present */ + char *PostName; /* Font's PostScript name */ + char *FullName; /* Font's full name */ + char *FamilyName; /* Font's family name */ + char *Style; /* Font's style string */ + char *Copyright; /* Font's copyright string */ + char *Version; /* Font's version string */ + char *Trademark; /* Font's trademark string */ + int llx,lly,urx,ury; /* bounding box */ - Fixed TTVersion; /* Truetype version number from offset table */ - Fixed MfrRevision; /* Revision number of this font */ + Fixed TTVersion; /* Truetype version number from offset table */ + Fixed MfrRevision; /* Revision number of this font */ - BYTE *offset_table; /* Offset table in memory */ - BYTE *post_table; /* 'post' table in memory */ + BYTE *offset_table; /* Offset table in memory */ + BYTE *post_table; /* 'post' table in memory */ - BYTE *loca_table; /* 'loca' table in memory */ - BYTE *glyf_table; /* 'glyf' table in memory */ - BYTE *hmtx_table; /* 'hmtx' table in memory */ + BYTE *loca_table; /* 'loca' table in memory */ + BYTE *glyf_table; /* 'glyf' table in memory */ + BYTE *hmtx_table; /* 'hmtx' table in memory */ USHORT numberOfHMetrics; - int unitsPerEm; /* unitsPerEm converted to int */ - int HUPM; /* half of above */ + int unitsPerEm; /* unitsPerEm converted to int */ + int HUPM; /* half of above */ - int numGlyphs; /* from 'post' table */ + int numGlyphs; /* from 'post' table */ - int indexToLocFormat; /* short or long offsets */ + int indexToLocFormat; /* short or long offsets */ }; ULONG getULONG(BYTE *p); diff --git a/ttconv/ttutil.cpp b/ttconv/ttutil.cpp index e9976d5af0a5..52c3c8bf75c5 100644 --- a/ttconv/ttutil.cpp +++ b/ttconv/ttutil.cpp @@ -1,3 +1,5 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + /* * Modified for use within matplotlib * 5 July 2007 diff --git a/unit/__init__.py b/unit/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000