From 5e2efdd12590f2ddf884a0fc6420ea214bc8e632 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 18:58:29 -0400 Subject: [PATCH 01/23] DOC : move MEP10 --- doc/devel/MEP/MEP10.rst | 192 ++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 193 insertions(+) create mode 100644 doc/devel/MEP/MEP10.rst diff --git a/doc/devel/MEP/MEP10.rst b/doc/devel/MEP/MEP10.rst new file mode 100644 index 000000000000..8604e4fd7e36 --- /dev/null +++ b/doc/devel/MEP/MEP10.rst @@ -0,0 +1,192 @@ +MEP10: Docstring consistency +============================ +.. contents:: + :local: + +Status +------ + +**Progress** + +Targeted for 1.3 + +Branches and Pull requests +-------------------------- + +#1665 +#1757 +#1795 + +Abstract +-------- + +matplotlib has a great deal of inconsistency between docstrings. This +not only makes the docs harder to read, but it is harder on +contributors, because they don't know which specifications to follow. +There should be a clear docstring convention that is followed +consistently. + +The organization of the API documentation is difficult to follow. +Some pages, such as pyplot and axes, are enormous and hard to browse. +There should instead be short summary tables that link to detailed +documentation. In addition, some of the docstrings themselves are +quite long and contain redundant information. + +Building the documentation takes a long time and uses a `make.py` +script rather than a Makefile. + +Detailed description +-------------------- + +There are number of new tools and conventions available since +matplotlib started using Sphinx that make life easier. The following +is a list of proposed changes to docstrings, most of which involve +these new features. + +Numpy docstring format +'''''''''''''''''''''' + +`Numpy docstring format +`_: +This format divides the docstring into clear sections, each having +different parsing rules that make the docstring easy to read both as +raw text and as HTML. We could consider alternatives, or invent our +own, but this is a strong choice, as it's well used and understood in +the Numpy/Scipy community. + +Cross references +'''''''''''''''' + +Most of the docstrings in matplotlib use explicit "roles" when linking +to other items, for example: ``:func:`myfunction```. As of Sphinx +0.4, there is a "default_role" that can be set to "obj", which will +polymorphically link to a Python object of any type. This allows one +to write ```myfunction``` instead. This makes docstrings much easier +to read and edit as raw text. Additionally, Sphinx allows for setting +a current module, so links like ```~matplotlib.axes.Axes.set_xlim``` +could be written as ```~axes.Axes.set_xlim```. + +Overriding signatures +''''''''''''''''''''' + +Many methods in matplotlib use the ``*args`` and ``**kwargs`` syntax +to dynamically handle the keyword arguments that are accepted by the +function, or to delegate on to another function. This, however, is +often not useful as a signature in the documentation. For this +reason, many matplotlib methods include something like:: + + def annotate(self, *args, **kwargs): + """ + Create an annotation: a piece of text referring to a data + point. + + Call signature:: + + annotate(s, xy, xytext=None, xycoords='data', + textcoords='data', arrowprops=None, **kwargs) + """ + +This can't be parsed by Sphinx, and is rather verbose in raw text. As +of Sphinx 1.1, if the `autodoc_docstring_signature` config value is +set to True, Sphinx will extract a replacement signature from the +first line of the docstring, allowing this:: + + def annotate(self, *args, **kwargs): + """ + annotate(s, xy, xytext=None, xycoords='data', + textcoords='data', arrowprops=None, **kwargs) + + Create an annotation: a piece of text referring to a data + point. + """ + +The explicit signature will replace the actual Python one in the +generated documentation. + +Linking rather than duplicating +''''''''''''''''''''''''''''''' + +Many of the docstrings include long lists of accepted keywords by +interpolating things into the docstring at load time. This makes the +docstrings very long. Also, since these tables are the same across +many docstrings, it inserts a lot of redundant information in the docs +-- particularly a problem in the printed version. + +These tables should be moved to docstrings on functions whose only +purpose is for help. The docstrings that refer to these tables should +link to them, rather than including them verbatim. + +autosummary extension +''''''''''''''''''''' + +The Sphinx autosummary extension should be used to generate summary +tables, that link to separate pages of documentation. Some classes +that have many methods (e.g. `Axes.axes`) should be documented with +one method per page, whereas smaller classes should have all of their +methods together. + +Examples linking to relevant documentation +'''''''''''''''''''''''''''''''''''''''''' + +The examples, while helpful at illustrating how to use a feature, do +not link back to the relevant docstrings. This could be addressed by +adding module-level docstrings to the examples, and then including +that docstring in the parsed content on the example page. These +docstrings could easily include references to any other part of the +documentation. + +Documentation using help() vs a browser +'''''''''''''''''''''' + +Using Sphinx markup in the source allows for good-looking docs in your +browser, but the markup also makes the raw text returned using help() +look terrible. One of the aims of improving the docstrings should be +to make both methods of accessing the docs look good. + +Implementation +-------------- + +1. The numpydoc extensions should be turned on for matplotlib. There + is an important question as to whether these should be included in + the matplotlib source tree, or used as a dependency. Installing + Numpy is not sufficient to get the numpydoc extensions -- it's a + separate install procedure. In any case, to the extent that they + require customization for our needs, we should endeavor to submit + those changes upstream and not fork them. + +2. Manually go through all of the docstrings and update them to the + new format and conventions. Updating the cross references (from + ```:func:`myfunc``` to ```func```) may be able to be + semi-automated. This is a lot of busy work, and perhaps this labor + should be divided on a per-module basis so no single developer is + over-burdened by it. + +3. Reorganize the API docs using autosummary and `sphinx-autogen`. + This should hopefully have minimal impact on the narrative + documentation. + +4. Modify the example page generator (`gen_rst.py`) so that it + extracts the module docstring from the example and includes it in a + non-literal part of the example page. + +5. Use `sphinx-quickstart` to generate a new-style Sphinx Makefile. + The following features in the current `make.py` will have to be + addressed in some other way: + + - Copying of some static content + + - Specifying a "small" build (only low-resolution PNG files for examples) + +Steps 1, 2, and 3 are interdependent. 4 and 5 may be done +independently, though 5 has some dependency on 3. + +Backward compatibility +---------------------- + +As this mainly involves docstrings, there should be minimal impact on +backward compatibility. + +Alternatives +------------ + +None yet discussed. diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index 3c76f68f1033..913ed7d9496c 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -13,4 +13,5 @@ Matplotlib Enhancement Proposals :maxdepth: 1 template + MEP10 MEP25 From e67f8d17f7fc2ab27116cce650633b36d3888b75 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 19:03:39 -0400 Subject: [PATCH 02/23] DOC : move MEP11 --- doc/devel/MEP/MEP11.rst | 169 ++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 170 insertions(+) create mode 100644 doc/devel/MEP/MEP11.rst diff --git a/doc/devel/MEP/MEP11.rst b/doc/devel/MEP/MEP11.rst new file mode 100644 index 000000000000..786311c85d68 --- /dev/null +++ b/doc/devel/MEP/MEP11.rst @@ -0,0 +1,169 @@ +MEP11: Third-party dependencies +=============================== + +.. contents:: + :local: + +This MEP attempts to improve the way in which third-party dependencies +in matplotlib are handled. + +Status +------ + +**Completed** -- needs to be merged + +Branches and Pull requests +-------------------------- + +#1157: Use automatic dependency resolution + +#1290: Debundle pyparsing + +#1261: Update six to 1.2 + +Abstract +-------- + +One of the goals of matplotlib has been to keep it as easy to install +as possible. To that end, some third-party dependencies are included +in the source tree and, under certain circumstances, installed +alongside matplotlib. This MEP aims to resolve some problems with +that approach, bring some consistency, while continuing to make +installation convenient. + +At the time that was initially done, `setuptools`, `easy_install` and +`PyPI` were not mature enough to be relied on. However, at present, +we should be able to safely leverage the "modern" versions of those +tools, `distribute` and `pip`. + +While matplotlib has dependencies on both Python libraries and C/C++ +libraries, this MEP addresses only the Python libraries so as to not +confuse the issue. C libraries represent a larger and mostly +orthogonal set of problems. + +Detailed description +-------------------- + +matplotlib depends on the following third-party Python libraries: + + - Numpy + - dateutil (pure Python) + - pytz (pure Python) + - six -- required by dateutil (pure Python) + - pyparsing (pure Python) + - PIL (optional) + - GUI frameworks: pygtk, gobject, tkinter, PySide, PyQt4, wx (all + optional, but one is required for an interactive GUI) + +Current behavior +```````````````` + +When installing from source, a `git` checkout or `pip`: + + - `setup.py` attempts to `import numpy`. If this fails, the + installation fails. + + - For each of `dateutil`, `pytz` and `six`, `setup.py` attempts to + import them (from the top-level namespace). If that fails, + matplotlib installs its local copy of the library into the + top-level namespace. + + - `pyparsing` is always installed inside of the matplotlib + namespace. + +This behavior is most surprising when used with `pip`, because no +`pip` dependency resolution is performed, even though it is likely to +work for all of these packages. + +The fact that `pyparsing` is installed in the matplotlib namespace has +reportedly (#1290) confused some users into thinking it is a +matplotlib-related module and import it from there rather than the +top-level. + +When installing using the Windows installer, `dateutil`, `pytz` and +`six` are installed at the top-level *always*, potentially overwriting +already installed copies of those libraries. + +TODO: Describe behavior with the OS-X installer. + +When installing using a package manager (Debian, RedHat, MacPorts +etc.), this behavior actually does the right thing, and there are no +special patches in the matplotlib packages to deal with the fact that +we handle `dateutil`, `pytz` and `six` in this way. However, care +should be taken that whatever approach we move to continues to work in +that context. + +Maintaining these packages in the matplotlib tree and making sure they +are up-to-date is a maintenance burden. Advanced new features that +may require a third-party pure Python library have a higher barrier to +inclusion because of this burden. + + +Desired behavior +```````````````` + +Third-party dependencies are downloaded and installed from their +canonical locations by leveraging `pip`, `distribute` and `PyPI`. + +`dateutil`, `pytz`, and `pyparsing` should be made into optional +dependencies -- though obviously some features would fail if they +aren't installed. This will allow the user to decide whether they +want to bother installing a particular feature. + +Implementation +-------------- + +For installing from source, and assuming the user has all of the +C-level compilers and dependencies, this can be accomplished fairly +easily using `distribute` and following the instructions `here +`_. The only anticipated +change to the matplotlib library code will be to import `pyparsing` +from the top-level namespace rather than from within matplotlib. Note +that `distribute` will also allow us to remove the direct dependency +on `six`, since it is, strictly speaking, only a direct dependency of +`dateutil`. + +For binary installations, there are a number of alternatives (here +ordered from best/hardest to worst/easiest): + + 1. The distutils wininst installer allows a post-install script to + run. It might be possible to get this script to run `pip` to + install the other dependencies. (See `this thread + `_ + for someone who has trod that ground before). + + 2. Continue to ship `dateutil`, `pytz`, `six` and `pyparsing` in + our installer, but use the post-install-script to install them + *only* if they can not already be found. + + 3. Move all of these packages inside a (new) `matplotlib.extern` + namespace so it is clear for outside users that these are + external packages. Add some conditional imports in the core + matplotlib codebase so `dateutil` (at the top-level) is tried + first, and failing that `matplotlib.extern.dateutil` is used. + +2 and 3 are undesirable as they still require maintaining copies of +these packages in our tree -- and this is exacerbated by the fact that +they are used less -- only in the binary installers. None of these 3 +approaches address Numpy, which will still have to be manually +installed using an installer. + +TODO: How does this relate to the Mac OS-X installer? + +Backward compatibility +---------------------- + +At present, matplotlib can be installed from source on a machine +without the third party dependencies and without an internet +connection. After this change, an internet connection (and a working +PyPI) will be required to install matplotlib for the first time. +(Subsequent matplotlib updates or development work will run without +accessing the network). + +Alternatives +------------ + +Distributing binary `eggs` doesn't feel like a usable solution. That +requires getting `easy_install` installed first, and Windows users +generally prefer the well known `.exe` or `.msi` installer that works +out of the box. diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index 913ed7d9496c..f03b9e26bcae 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -14,4 +14,5 @@ Matplotlib Enhancement Proposals template MEP10 + MEP11 MEP25 From 71039859e6afc38d89d765a7dabe6481c7e7e63c Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 19:18:31 -0400 Subject: [PATCH 03/23] DOC : clean up MEP10, MEP11 headings --- doc/devel/MEP/MEP10.rst | 33 +++++++++++++++++---------------- doc/devel/MEP/MEP11.rst | 23 ++++++++++++----------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/doc/devel/MEP/MEP10.rst b/doc/devel/MEP/MEP10.rst index 8604e4fd7e36..dc00e0fb9c0b 100644 --- a/doc/devel/MEP/MEP10.rst +++ b/doc/devel/MEP/MEP10.rst @@ -1,24 +1,25 @@ -MEP10: Docstring consistency -============================ +============================== + MEP10: Docstring consistency +============================== .. contents:: :local: Status ------- +====== **Progress** Targeted for 1.3 Branches and Pull requests --------------------------- +========================== #1665 #1757 #1795 Abstract --------- +======== matplotlib has a great deal of inconsistency between docstrings. This not only makes the docs harder to read, but it is harder on @@ -36,7 +37,7 @@ Building the documentation takes a long time and uses a `make.py` script rather than a Makefile. Detailed description --------------------- +==================== There are number of new tools and conventions available since matplotlib started using Sphinx that make life easier. The following @@ -44,7 +45,7 @@ is a list of proposed changes to docstrings, most of which involve these new features. Numpy docstring format -'''''''''''''''''''''' +---------------------- `Numpy docstring format `_: @@ -55,7 +56,7 @@ own, but this is a strong choice, as it's well used and understood in the Numpy/Scipy community. Cross references -'''''''''''''''' +---------------- Most of the docstrings in matplotlib use explicit "roles" when linking to other items, for example: ``:func:`myfunction```. As of Sphinx @@ -67,7 +68,7 @@ a current module, so links like ```~matplotlib.axes.Axes.set_xlim``` could be written as ```~axes.Axes.set_xlim```. Overriding signatures -''''''''''''''''''''' +--------------------- Many methods in matplotlib use the ``*args`` and ``**kwargs`` syntax to dynamically handle the keyword arguments that are accepted by the @@ -104,7 +105,7 @@ The explicit signature will replace the actual Python one in the generated documentation. Linking rather than duplicating -''''''''''''''''''''''''''''''' +------------------------------- Many of the docstrings include long lists of accepted keywords by interpolating things into the docstring at load time. This makes the @@ -117,7 +118,7 @@ purpose is for help. The docstrings that refer to these tables should link to them, rather than including them verbatim. autosummary extension -''''''''''''''''''''' +--------------------- The Sphinx autosummary extension should be used to generate summary tables, that link to separate pages of documentation. Some classes @@ -126,7 +127,7 @@ one method per page, whereas smaller classes should have all of their methods together. Examples linking to relevant documentation -'''''''''''''''''''''''''''''''''''''''''' +------------------------------------------ The examples, while helpful at illustrating how to use a feature, do not link back to the relevant docstrings. This could be addressed by @@ -136,7 +137,7 @@ docstrings could easily include references to any other part of the documentation. Documentation using help() vs a browser -'''''''''''''''''''''' +--------------------------------------- Using Sphinx markup in the source allows for good-looking docs in your browser, but the markup also makes the raw text returned using help() @@ -144,7 +145,7 @@ look terrible. One of the aims of improving the docstrings should be to make both methods of accessing the docs look good. Implementation --------------- +============== 1. The numpydoc extensions should be turned on for matplotlib. There is an important question as to whether these should be included in @@ -181,12 +182,12 @@ Steps 1, 2, and 3 are interdependent. 4 and 5 may be done independently, though 5 has some dependency on 3. Backward compatibility ----------------------- +====================== As this mainly involves docstrings, there should be minimal impact on backward compatibility. Alternatives ------------- +============ None yet discussed. diff --git a/doc/devel/MEP/MEP11.rst b/doc/devel/MEP/MEP11.rst index 786311c85d68..67274038877e 100644 --- a/doc/devel/MEP/MEP11.rst +++ b/doc/devel/MEP/MEP11.rst @@ -1,5 +1,6 @@ -MEP11: Third-party dependencies -=============================== +================================= + MEP11: Third-party dependencies +================================= .. contents:: :local: @@ -8,12 +9,12 @@ This MEP attempts to improve the way in which third-party dependencies in matplotlib are handled. Status ------- +====== **Completed** -- needs to be merged Branches and Pull requests --------------------------- +========================== #1157: Use automatic dependency resolution @@ -22,7 +23,7 @@ Branches and Pull requests #1261: Update six to 1.2 Abstract --------- +======== One of the goals of matplotlib has been to keep it as easy to install as possible. To that end, some third-party dependencies are included @@ -42,7 +43,7 @@ confuse the issue. C libraries represent a larger and mostly orthogonal set of problems. Detailed description --------------------- +==================== matplotlib depends on the following third-party Python libraries: @@ -56,7 +57,7 @@ matplotlib depends on the following third-party Python libraries: optional, but one is required for an interactive GUI) Current behavior -```````````````` +---------------- When installing from source, a `git` checkout or `pip`: @@ -100,7 +101,7 @@ inclusion because of this burden. Desired behavior -```````````````` +---------------- Third-party dependencies are downloaded and installed from their canonical locations by leveraging `pip`, `distribute` and `PyPI`. @@ -111,7 +112,7 @@ aren't installed. This will allow the user to decide whether they want to bother installing a particular feature. Implementation --------------- +============== For installing from source, and assuming the user has all of the C-level compilers and dependencies, this can be accomplished fairly @@ -151,7 +152,7 @@ installed using an installer. TODO: How does this relate to the Mac OS-X installer? Backward compatibility ----------------------- +====================== At present, matplotlib can be installed from source on a machine without the third party dependencies and without an internet @@ -161,7 +162,7 @@ PyPI) will be required to install matplotlib for the first time. accessing the network). Alternatives ------------- +============ Distributing binary `eggs` doesn't feel like a usable solution. That requires getting `easy_install` installed first, and Windows users From 579ecb8366465c3356248839d3bfb24019476182 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 19:23:32 -0400 Subject: [PATCH 04/23] DOC : move MEP12 --- doc/devel/MEP/MEP12.rst | 187 ++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 188 insertions(+) create mode 100644 doc/devel/MEP/MEP12.rst diff --git a/doc/devel/MEP/MEP12.rst b/doc/devel/MEP/MEP12.rst new file mode 100644 index 000000000000..c021f1faa6d7 --- /dev/null +++ b/doc/devel/MEP/MEP12.rst @@ -0,0 +1,187 @@ +===================================== + MEP12: Improve Gallery and Examples +===================================== +.. contents:: + :local: + + +Status +====== + +**Progress** + +Initial changes added in 1.3. Conversion of the gallery is on-going. + +Branches and Pull requests +========================== + +#1623, #1924, #2181 + +PR `#2474 _ +demonstrates a single example being cleaned up and moved to the +appropriate section. + +Abstract +======== + +Reorganizing the matplotlib plot gallery would greatly simplify +navigation of the gallery. In addition, examples should be cleaned-up +and simplified for clarity. + + +Detailed description +==================== + +The matplotlib gallery was recently set up to split examples up into +sections. As discussed in that PR [1]_, the current example sections +(``api``, ``pylab_examples``) aren't terribly useful to users: New +sections in the gallery would help users find relevant examples. + +These sections would also guide a cleanup of the examples: Initially, +all the current examples would remain and be listed under their +current directories. Over time, these examples could be cleaned up +and moved into one of the new sections. + +This process allows users to easily identify examples that need to be +cleaned up; i.e. anything in the ``api`` and ``pylab_examples`` +directories. + + +Implementation +============== + +1. Create new gallery sections. [Done] +2. Clean up examples and move them to the new gallery sections (over the course + of many PRs and with the help of many users/developers). [In progress] + +Gallery sections +---------------- + +The naming of sections is critical and will guide the clean-up +effort. The current sections are: + +* Lines, bars, and markers (more-or-less 1D data) +* Shapes and collections +* Statistical plots +* Images, contours, and fields +* Pie and polar charts: Round things +* Color +* Text, labels, and annotations +* Ticks and spines +* Subplots, axes, and figures +* Specialty plots (e.g., sankey, radar, tornado) +* Showcase (plots with tweaks to make them publication-quality) +* separate sections for toolboxes (already exists: 'mplot3d', + 'axes_grid', 'units', 'widgets') + +These names are certainly up for debate. As these sections grow, we +should reevaluate them and split them up as necessary. + + +Clean up guidelines +------------------- + +The current examples in the ``api`` and ``pylab_examples`` sections of +the gallery would remain in those directories until they are cleaned +up. After clean-up, they would be moved to one of the new gallery +sections described above. "Clean-up" should involve: + +* PEP8_ clean-ups (running `flake8 + `_, or a similar checker, is + highly recommended) +* Commented-out code should be removed. +* Add introductory sentence or paragraph in the main docstring. See + `6d1b8a2 + `_. +* Replace uses of ``pylab`` interface with ``pyplot`` (+ ``numpy``, + etc.). See `c25ef1e + `_ +* Remove shebang line, e.g.: + + #!/usr/bin/env python + +* Use consistent imports. In particular: + + import numpy as np + + import matplotlib.pyplot as plt + + Avoid importing specific functions from these modules (e.g. ``from + numpy import sin``) + +* Each example should focus on a specific feature (excluding + ``showcase`` examples, which will show more "polished" + plots). Tweaking unrelated to that feature should be removed. See + `f7b2217 + `_, + `e57b5fc + `_, + and `1458aa8 + `_ + +Use of ``pylab`` should be demonstrated/discussed on a dedicated help +page instead of the gallery examples. + +**Note:** When moving an existing example, you should search for +references to that example. For example, the API documentation for +`axes.py` and `pyplot.py` may use these examples to generate +plots. Use your favorite search tool (e.g., grep, ack, `grin +`_, `pss +`_) to search the matplotlib +package. See `2dc9a46 +`_ +and `aa6b410 +`_ + + +Additional suggestions +~~~~~~~~~~~~~~~~~~~~~~ + +* Provide links (both ways) between examples and API docs for the + methods/objects used. (issue `#2222 + `_) +* Use ``plt.subplots`` (note trailing "s") in preference over + ``plt.subplot``. +* Rename the example to clarify it's purpose. For example, the most + basic demo of ``imshow`` might be ``imshow_demo.py``, and one + demonstrating different interpolation settings would be + ``imshow_demo_interpolation.py`` (*not* ``imshow_demo2.py``). +* Split up examples that try to do too much. See `5099675 + `_ + and `fc2ab07 + `_ +* Delete examples that don't show anything new. +* Some examples exercise esoteric features for unit testing. These + tweaks should be moved out of the gallery to an example in the + ``unit`` directory located in the root directory of the package. +* Add plot titles to clarify intent of the example. See `bd2b13c + `_ + + +Backward compatibility +====================== + +The website for each Matplotlib version is readily accessible, so +users who want to refer to old examples can still do so. + + +Alternatives +============ + +Tags +---- + +Tagging examples will also help users search the example gallery. Although tags +would be a big win for users with specific goals, the plot gallery will remain +the entry point to these examples, and sections could really help users +navigate the gallery. Thus, tags are complementary to this reorganization. + + +.. _PEP8: http://www.python.org/dev/peps/pep-0008/ + +.. [1] http://github.com/matplotlib/matplotlib/pull/714 +.. [2] http://github.com/matplotlib/matplotlib/issues/524 +.. [3] http://matplotlib.1069221.n5.nabble.com/Matplotlib-gallery-td762.html#a33379091 +.. [4] http://www.loria.fr/~rougier/teaching/matplotlib/ +.. [5] http://www.gigawiz.com/aagraphs.html +.. [6] http://www.loria.fr/~rougier/coding/gallery/ diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index f03b9e26bcae..8bf56919c3dd 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -15,4 +15,5 @@ Matplotlib Enhancement Proposals template MEP10 MEP11 + MEP12 MEP25 From 9bfedc83bcb3bd9576898be293458b7dccb113d3 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 19:27:21 -0400 Subject: [PATCH 05/23] DOC : move MEP13 --- doc/devel/MEP/MEP13.rst | 199 ++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 200 insertions(+) create mode 100644 doc/devel/MEP/MEP13.rst diff --git a/doc/devel/MEP/MEP13.rst b/doc/devel/MEP/MEP13.rst new file mode 100644 index 000000000000..9230dfe2805e --- /dev/null +++ b/doc/devel/MEP/MEP13.rst @@ -0,0 +1,199 @@ +================================= +MEP12: Use properties for Artists +================================= + +.. contents:: + :local: + +Status +====== + +- **Discussion** + +Branches and Pull requests +========================== + +None + +Abstract +======== + +Wrap all of the matplotlib getter and setter methods with python +`properties +`_, allowing +them to be read and written like class attributes. + +Detailed description +==================== + +Currently matplotlib uses getter and setter functions (usually +prefixed with get\_ and set\_, respectively) for reading and writing +data related to classes. However, since 2.6 python supports +properties, which allow such setter and getter functions to be +accessed as though they were attributes. This proposal would +implement all existing setter and getter methods as properties. + +Implementation +============== + +1. All existing getter and setter methods will need to have two + aliases, one with the get\_ or set\_ prefix and one without. + Getter methods that currently lack prefixes should be recording in + a text file. +2. Classes should be reorganized so setter and getter methods are + sequential in the code, with getter methods first. +3. Getter and setter methods the provide additional optional optional + arguments should have those arguments accessible in another manner, + either as additional getter or setter methods or attributes of + other classes. If those classes are not accessible, getters for + them should be added. +4. Property decorators will be added to the setter and getter methods + without the prefix. Those with the prefix will be marked as + deprecated. +5. Docstrings will need to be rewritten so the getter with the prefix + has the current docstring and the getter without the prefix has a + generic docstring appropriate for an attribute. +6. Automatic alias generation will need to be modified so it will also + create aliases for the properties. +7. All instances of getter and setter method calls will need to be + changed to attribute access. +8. All setter and getter aliases with prefixes will be removed + +The following steps can be done simultaneously: 1, 2, and 3; 4 and 5; +6 and 7. + +Only the following steps must be done in the same release: 4, 5, +and 6. All other changes can be done in separate releases. 8 should +be done several major releases after everything else. + +Backward compatibility +====================== + +All existing getter methods that do not have a prefix (such as get\_) +will need to be changed from function calls to attribute access. In +most cases this will only require removing the parenthesis. + +setter and getter methods that have additional optional arguments will +need to have those arguments implemented in another way, either as a +separate property in the same class or as attributes or properties of +another class. + +Cases where the setter returns a value will need to be changed to +using the setter followed by the getter. + +Cases where there are set_ATTR_on() and set_ATTR_off() methods will be +changed to ATTR_on properties. + +Examples +======== + +axes.Axes.set_axis_off/set_axis_on +---------------------------------- + +Current implementation: :: + + axes.Axes.set_axis_off() + axes.Axes.set_axis_on() + +New implementation: :: + + True = axes.Axes.axis_on + False = axes.Axes.axis_on + axes.Axes.axis_on = True + axes.Axes.axis_on = False + +axes.Axes.get_xlim/set_xlim and get_autoscalex_on/set_autoscalex_on +------------------------------------------------------------------- + +Current implementation: :: + + [left, right] = axes.Axes.get_xlim() + auto = axes.Axes.get_autoscalex_on() + + [left, right] = axes.Axes.set_xlim(left=left, right=right, emit=emit, auto=auto) + [left, right] = axes.Axes.set_xlim(left=left, right=None, emit=emit, auto=auto) + [left, right] = axes.Axes.set_xlim(left=None, right=right, emit=emit, auto=auto) + [left, right] = axes.Axes.set_xlim(left=left, emit=emit, auto=auto) + [left, right] = axes.Axes.set_xlim(right=right, emit=emit, auto=auto) + + axes.Axes.set_autoscalex_on(auto) + +New implementation: :: + + [left, right] = axes.Axes.axes_xlim + auto = axes.Axes.autoscalex_on + + axes.Axes.axes_xlim = [left, right] + axes.Axes.axes_xlim = [left, None] + axes.Axes.axes_xlim = [None, right] + axes.Axes.axes_xlim[0] = left + axes.Axes.axes_xlim[1] = right + + axes.Axes.autoscalex_on = auto + + axes.Axes.emit_xlim = emit + +axes.Axes.get_title/set_title +----------------------------- + +Current implementation: :: + + string = axes.Axes.get_title() + axes.Axes.set_title(string, fontdict=fontdict, **kwargs) + +New implementation: :: + + string = axes.Axes.title + string = axes.Axes.title_text.text + + text.Text = axes.Axes.title_text + text.Text. = attribute + text.Text.fontdict = fontdict + + axes.Axes.title = string + axes.Axes.title = text.Text + axes.Axes.title_text = string + axes.Axes.title_text = text.Text + +axes.Axes.get_xticklabels/set_xticklabels +----------------------------------------- + +Current implementation: :: + + [text.Text] = axes.Axes.get_xticklabels() + [text.Text] = axes.Axes.get_xticklabels(minor=False) + [text.Text] = axes.Axes.get_xticklabels(minor=True) + [text.Text] = axes.Axes.([string], fontdict=None, **kwargs) + [text.Text] = axes.Axes.([string], fontdict=None, minor=False, **kwargs) + [text.Text] = axes.Axes.([string], fontdict=None, minor=True, **kwargs) + +New implementation: :: + + [text.Text] = axes.Axes.xticklabels + [text.Text] = axes.Axes.xminorticklabels + axes.Axes.xticklabels = [string] + axes.Axes.xminorticklabels = [string] + axes.Axes.xticklabels = [text.Text] + axes.Axes.xminorticklabels = [text.Text] + +Alternatives +============ + +Instead of using decorators, it is also possible to use the property +function. This would change the procedure so that all getter methods +that lack a prefix will need to be renamed or removed. This makes +handling docstrings more difficult and harder to read. + +It is not necessary to deprecate the setter and getter methods, but +leaving them in will complicate the code. + +This could also serve as an opportunity to rewrite or even remove +automatic alias generation. + +Another alternate proposal: + +Convert ``set_xlim``, ``set_xlabel``, ``set_title``, etc. to ``xlim``, +``xlabel``, ``title``,... to make the transition from ``plt`` +functions to ``axes`` methods significantly simpler. These would still +be methods, not properties, but it's still a great usability +enhancement while retaining the interface. diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index 8bf56919c3dd..a3ed7455f01f 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -16,4 +16,5 @@ Matplotlib Enhancement Proposals MEP10 MEP11 MEP12 + MEP13 MEP25 From f7b8db1e7ef309a215678ee80ba4dfe2182b5fec Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 19:43:11 -0400 Subject: [PATCH 06/23] DOC : move MEP14 --- doc/devel/MEP/MEP14.rst | 420 ++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 421 insertions(+) create mode 100644 doc/devel/MEP/MEP14.rst diff --git a/doc/devel/MEP/MEP14.rst b/doc/devel/MEP/MEP14.rst new file mode 100644 index 000000000000..eb0ebd34f381 --- /dev/null +++ b/doc/devel/MEP/MEP14.rst @@ -0,0 +1,420 @@ +==================== +MEP13: Text handling +==================== + +.. contents:: + :local: + + +Status +====== + +- **Discussion** + +Branches and Pull requests +========================== + +Issue #253 demonstrates a bug where using the bounding box rather than +the advance width of text results in misaligned text. This is a minor +point in the grand scheme of things, but it should be addressed as +part of this MEP. + +Abstract +======== + +By reorganizing how text is handled, this MEP aims to: + +- improve support for Unicode and non-ltr languages +- improve text layout (especially multi-line text) +- allow support for more fonts, especially non-Apple-format TrueType + fonts and OpenType fonts. +- make the font configuration easier and more transparent + +Detailed description +==================== + +**Text layout** + +At present, matplotlib has two different ways to render text: +"built-in" (based on FreeType and our own Python code), and "usetex" +(based on calling out to a TeX installation). Adjunct to the +"built-in" renderer there is also the Python-based "mathtext" system +for rendering mathematical equations using a subset of the TeX +language without having a TeX installation available. Support for +these two engines in strewn about many source files, including every +backend, where one finds clauses like :: + + if rcParams['text.usetex']: # do one thing else: # do another + +Adding a third text rendering approach (more on that later) would +require editing all of these places as well, and therefore doesn't +scale. + +Instead, this MEP proposes adding a concept of "text engines", where +the user could select one of many different approaches for rendering +text. The implementations of each of these would be localized to +their own set of modules, and not have little pieces around the whole +source tree. + +Why add more text rendering engines? The "built-in" text rendering +has a number of shortcomings. +- It only handles right-to-left languages, and doesn't handle many + special features of Unicode, such as combining diacriticals. +- The multiline support is imperfect and only supports manual + line-breaking -- it can not break up a paragraph into lines of a + certain length. +- It also does not handle inline formatting changes in order to + support something like Markdown, reStructuredText or HTML. (Though + rich-text formatting is contemplated in this MEP, since we want to + make sure this design allows it, the specifics of a rich-text + formatting implementation is outside of the scope of this MEP.) + +Supporting these things is difficult, and is the "full-time job" of a +number of other projects: + + - `pango `_/`harfbuzz + `_ + - `QtTextLayout + `_ + - `Microsoft DirectWrite + `_ + - `Apple Core Text + `_ + +Of the above options, it should be noted that `harfbuzz` is designed +from the start as a cross platform option with minimal dependencies, +so therefore is a good candidate for a single option to support. + +Additionally, for supporting rich text, we could consider using +`WebKit `_, and possibly whether than +represents a good single cross-platform option. Again, however, rich +text formatting is outside of the scope of this project. + +Rather than trying to reinvent the wheel and add these features to +matplotlib's "built-in" text renderer, we should provide a way to +leverage these projects to get more powerful text layout. The +"built-in" renderer will still need to exist for reasons of ease of +installation, but its feature set will be more limited compared to the +others. [TODO: This MEP should clearly decide what those limited +features are, and fix any bugs to bring the implementation into a +state of working correctly in all cases that we want it to work. I +know @leejjoon has some thoughts on this.] + +**Font selection** + +Going from an abstract description of a font to a file on disk is the +task of the font selection algorithm -- it turns out to be much more +complicated than it seems at first. + +The "built-in" and "usetex" renderers have very different ways of +handling font selection, given their different technologies. TeX +requires the installation of TeX-specific font packages, for example, +and can not use TrueType fonts directly. Unfortunately, despite the +different semantics for font selection, the same set of font +properties are used for each. This is true of both the +`FontProperties` class and the font-related `rcParams` (which +basically share the same code underneath). Instead, we should define +a core set of font selection parameters that will work across all text +engines, and have engine-specific configuration to allow the user to +do engine-specific things when required. For example, it is possible +to directly select a font by name in the "built-in" using +`font.family`, but the same is not possible with "usetex". It may be +possible to make it easier to use TrueType fonts by using XeTeX, but +users will still want to use the traditional metafonts through TeX +font packages. So the issue still stands that different text engines +will need engine-specific configuration, and it should be more obvious +to the user which configuration will work across text engines and +which are engine-specific. + +Note that even excluding "usetex", there are different ways to find +fonts. The default is to use the font list cache in `font_manager.py` +which matches fonts using our own algorithm based on the `CSS font +matching algorithm `_. +It doesn't always do the same thing as the native font selection +algorithms on Linux (`fontconfig +`_), Mac and +Windows, and it doesn't always find all of the fonts on the system +that the OS would normally pick up. However, it is cross-platform, +and always finds the fonts that ship with matplotlib. The Cairo and +MacOSX backends (and presumably a future HTML5-based backend) +currently bypass this mechanism and use the OS-native ones. The same +is true when not embedding fonts in SVG, PS or PDF files and opening +them in a third-party viewer. A downside there is that (at least with +Cairo, need to confirm with MacOSX) they don't always find the fonts +we ship with matplotlib. (It may be possible to add the fonts to +their search path, though, or we may need to find a way to install our +fonts to a location the OS expects to find them). + +There are also special modes in the PS and PDF to only use the core +fonts that are always available to those formats. There, the font +lookup mechanism must only match against those fonts. It is unclear +whether the OS-native font lookup systems can handle this case. + +There is also experimental support for using `fontconfig +`_ for font +selection in matplotlib, turned off by default. fontconfig is the +native font selection algorithm on Linux, but is also cross platform +and works well on the other platforms (though obviously is an +additional dependency there). + +Many of the text layout libraries proposed above (pango, QtTextLayout, +DirectWrite and CoreText etc.) insist on using the font selection +library from their own ecosystem. + +All of the above seems to suggest that we should move away from our +self-written font selection algorithm and use the native APIs where +possible. That's what Cairo and MacOSX backends already want to use, +and it will be a requirement of any complex text layout library. On +Linux, we already have the bones of a `fontconfig` implementation +(which could also be accessed through pango). On Windows and Mac we +may need to write custom wrappers. The nice thing is that the API for +font lookup is relatively small, and essentially consist of "given a +dictionary of font properties, give me a matching font file". + +**Font subsetting** + +Font subsetting is currently handled using ttconv. ttconv was a +standalone commandline utility for converting TrueType fonts to +subsetted Type 3 fonts (among other features) written in 1995, which +matplotlib (well, I) forked in order to make it work as a library. It +only handles Apple-style TrueType fonts, not ones with the Microsoft +(or other vendor) encodings. It doesn't handle OpenType fonts at all. +This means that even though the STIX fonts come as .otf files, we have +to convert them to .ttf files to ship them with matplotlib. The Linux +packagers hate this -- they'd rather just depend on the upstream STIX +fonts. ttconv has also been shown to have a few bugs that have been +difficult to fix over time. + +Instead, we should be able to use FreeType to get the font outlines +and write our own code (probably in Python) to output subsetted fonts +(Type 3 on PS and PDF and SVGFonts or paths on SVG). Freetype, as a +popular and well-maintained project, handles a wide variety of fonts +in the wild. This would remove a lot of custom C code, and remove +some code duplication between backends. + +Note that subsetting fonts this way, while the easiest route, does +lose the hinting in the font, so we will need to continue, as we do +now, provide a way to embed the entire font in the file where +possible. + +Alternative font subsetting options include using the subsetting +built-in to Cairo (not clear if it can be used without the rest of +Cairo), or using `fontforge` (which is a heavy and not terribly +cross-platform dependency). + +**Freetype wrappers** + +Our FreeType wrapper could really use a reworking. It defines its own +image buffer class (when a Numpy array would be easier). While +FreeType can handle a huge diversity of font files, there are +limitations to our wrapper that make it much harder to support +non-Apple-vendor TrueType files, and certain features of OpenType +files. (See #2088 for a terrible result of this, just to support the +fonts that ship with Windows 7 and 8). I think a fresh rewrite of +this wrapper would go a long way. + +**Text anchoring and alignment and rotation** + +The handling of baselines was changed in 1.3.0 such that the backends +are now given the location of the baseline of the text, not the bottom +of the text. This is probably the correct behavior, and the MEP +refactoring should also follow this convention. + +In order to support alignment on multi-line text, it should be the +responsibility of the (proposed) text engine to handle text alignment. +For a given chunk of text, each engine calculates a bounding box for +that text and the offset of the anchor point within that box. +Therefore, if the va of a block was "top", the anchor point would be +at the top of the box. + +Rotating of text should always be around the anchor point. I'm not +sure that lines up with current behavior in matplotlib, but it seems +like the sanest/least surprising choice. [This could be revisited +once we have something working]. Rotation of text should not be +handled by the text engine -- that should be handled by a layer +between the text engine and the rendering backend so it can be handled +in a uniform way. [I don't see any advantage to rotation being +handled by the text engines individually...] + +There are other problems with text alignment and anchoring that should +be resolved as part of this work. [TODO: enumerate these]. + +**Other minor problems to fix** + +The mathtext code has backend-specific code -- it should instead +provide its output as just another text engine. However, it's still +desirable to have mathtext layout inserted as part of a larger layout +performed by another text engine, so it should be possible to do this. +It's an open question whether embedding the text layout of an +arbitrary text engine in another should be possible. + +The text mode is currently set by a global rcParam ("text.usetex") so +it's either all on or all off. We should continue to have a global +rcParam to choose the text engine ("text.layout_engine"), but it +should under the hood be an overridable property on the `Text` object, +so the same figure can combine the results of multiple text layout +engines if necessary. + + +Implementation +============== + +A concept of a "text engine" will be introduced. Each text engine +will implement a number of abstract classes. The `TextFont` interface +will represent text for a given set of font properties. It isn't +necessarily limited to a single font file -- if the layout engine +supports rich text, it may handle a number of font files in a family. +Given a `TextFont` instance, the user can get a `TextLayout` instance, +which represents the layout for a given string of text in a given +font. From a `TextLayout`, an iterator over `TextSpans` is returned +so the engine can output raw editable text using as few spans as +possible. If the engine would rather get individual characters, they +can be obtained from the `TextSpan` instance:: + + + class TextFont(TextFontBase): + def __init__(self, font_properties): + """ + Create a new object for rendering text using the given font properties. + """ + pass + + def get_layout(self, s, ha, va): + """ + Get the TextLayout for the given string in the given font and + the horizontal (left, center, right) and verticalalignment (top, + center, baseline, bottom) + """ + pass + + class TextLayout(TextLayoutBase): + def get_metrics(self): + """ + Return the bounding box of the layout, anchored at (0, 0). + """ + pass + + def get_spans(self): + """ + Returns an iterator over the spans of different in the layout. + This is useful for backends that want to editable raw text as + individual lines. For rich text where the font may change, + each span of different font type will have its own span. + """ + pass + + def get_image(self): + """ + Returns a rasterized image of the text. Useful for raster backends, + like Agg. + + In all likelihood, this will be overridden in the backend, as it can + be created from get_layout(), but certain backends may want to + override it if their library provides it (as freetype does). + """ + pass + + def get_rectangles(self): + """ + Returns an iterator over the filled black rectangles in the layout. + Used by TeX and mathtext for drawing, for example, fraction lines. + """ + pass + + def get_path(self): + """ + Returns a single Path object of the entire layed out text. + + [Not strictly necessary, but might be useful for textpath + functionality] + """ + pass + + class TextSpan(TextSpanBase): + x, y # Position of the span -- relative to the text layout as a whole + # where (0, 0) is the anchor. y is the baseline of the span. + fontfile # The font file to use for the span + text # The text content of the span + + def get_path(self): + pass # See TextLayout.get_path + + def get_chars(self): + """ + Returns an iterator over the characters in the span. + """ + pass + + class TextChar(TextCharBase): + x, y # Position of the character -- relative to the text layout as + # a whole, where (0, 0) is the anchor. y is in the baseline + # of the character. + codepoint # The unicode code point of the character -- only for informational + # purposes, since the mapping of codepoint to glyph_id may have been + # handled in a complex way by the layout engine. This is an int + # to avoid problems on narrow Unicode builds. + glyph_id # The index of the glyph within the font + fontfile # The font file to use for the char + + def get_path(self): + """ + Get the path for the character. + """ + pass + + +Graphic backends that want to output subset of fonts would likely +build up a file-global dictionary of characters where the keys are +(fontname, glyph_id) and the values are the paths so that only one +copy of the path for each character will be stored in the file. + +Special casing: The "usetex" functionality currently is able to get +Postscript directly from TeX to insert directly in a Postscript file, +but for other backends, parses a DVI file and generates something more +abstract. For a case like this, `TextLayout` would implement +`get_spans` for most backends, but add `get_ps` for the Postscript +backend, which would look for the presence of this method and use it +if available, or fall back to `get_spans`. This kind of special +casing may also be necessary, for example, when the graphics backend +and text engine belong to the same ecosystem, e.g. Cairo and Pango, or +MacOSX and CoreText. + +There are three main pieces to the implementation: + +1) Rewriting the freetype wrapper, and removing ttconv. + + a) Once (1) is done, as a proof of concept, we can move to the + upstream STIX .otf fonts + + b) Add support for web fonts loaded from a remote URL. (Enabled by using freetype for font subsetting). + +2) Refactoring the existing "builtin" and "usetex" code into separate text engines and to follow the API outlined above. + +3) Implementing support for advanced text layout libraries. + + +(1) and (2) are fairly independent, though having (1) done first will +allow (2) to be simpler. (3) is dependent on (1) and (2), but even if +it doesn't get done (or is postponed), completing (1) and (2) will +make it easier to move forward with improving the "builtin" text +engine. + +Backward compatibility +====================== + +The layout of text with respect to its anchor and rotation will change +in hopefully small, but improved, ways. The layout of multiline text +will be much better, as it will respect horizontal alignment. The +layout of bidirectional text or other advanced Unicode features will +now work inherently, which may break some things if users are +currently using their own workarounds. + +Fonts will be selected differently. Hacks that used to sort of work +between the "builtin" and "usetex" text rendering engines may no +longer work. Fonts found by the OS that weren't previously found by +matplotlib may be selected. + +Alternatives +============ + +TBD diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index a3ed7455f01f..a65966b37dd1 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -17,4 +17,5 @@ Matplotlib Enhancement Proposals MEP11 MEP12 MEP13 + MEP14 MEP25 From c2cbbc94a38c6f850ae1ede3fae9ed916083cc10 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 19:46:22 -0400 Subject: [PATCH 07/23] DOC : move MEP15 --- doc/devel/MEP/MEP15.rst | 58 +++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 59 insertions(+) create mode 100644 doc/devel/MEP/MEP15.rst diff --git a/doc/devel/MEP/MEP15.rst b/doc/devel/MEP/MEP15.rst new file mode 100644 index 000000000000..7c9b2b776120 --- /dev/null +++ b/doc/devel/MEP/MEP15.rst @@ -0,0 +1,58 @@ +========================================================================== + MEP15 - Fix axis autoscaling when limits are specified for one axis only +========================================================================== + +.. contents:: + :local: + +Status +====== + +**Discussion** + +Branches and Pull requests +========================== + +None so far. + +Abstract +======== + +When one axis of a 2-dimensional plot if overridden via `xlim` or `ylim`, +automatic scaling of the remaining axis should be based on the data that falls +within the specified limits of the first axis. + +Detailed description +==================== + +When axis limits for a 2-D plot are specified for one axis only (via `xlim` or +`ylim`), matplotlib currently does not currently rescale the other axis. The +result is that the displayed curves or symbols may be compressed into a tiny +portion of the available area, so that the final plot conveys much less +information than it would with appropriate axis scaling. An example of such a +plot can be found at the following URL: + +http://phillipmfeldman.org/Python/MEP15.png + +The proposed change of behavior would make matplotlib choose the scale for the +remaining axis using only the data that falls within the limits for the axis +where limits were specified. + +Implementation +============== + +I don't know enough about the internals of matplotlib to be able to suggest an +implementation. + +Backward compatibility +====================== + +From the standpoint of software interfaces, there would be no break in +backward compatibility. Some outputs would be different, but if the user +truly desires the previous behavior, he/she can achieve this by overriding +the axis scaling for both axes. + +Alternatives +============ + +The only alternative that I can see is to maintain the status quo. diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index a65966b37dd1..afb8f662e265 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -18,4 +18,5 @@ Matplotlib Enhancement Proposals MEP12 MEP13 MEP14 + MEP15 MEP25 From e07dc2d2843417e71eac02407600ff9dd2775fbe Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 19:55:29 -0400 Subject: [PATCH 08/23] DOC : move MEP8 --- doc/devel/MEP/MEP08.rst | 58 +++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 59 insertions(+) create mode 100644 doc/devel/MEP/MEP08.rst diff --git a/doc/devel/MEP/MEP08.rst b/doc/devel/MEP/MEP08.rst new file mode 100644 index 000000000000..072a279eacf9 --- /dev/null +++ b/doc/devel/MEP/MEP08.rst @@ -0,0 +1,58 @@ +============ + MEP8: PEP8 +============ + +.. contents:: + :local: + + +Status +====== + +**Discussion** + +Branches and Pull requests +========================== + +None so far. + +Abstract +======== + +The matplotlib codebase predates PEP8, and therefore is less than +consistent style-wise in some areas. Bringing the codebase into +compliance with PEP8 would go a long way to improving its legibility. + +Detailed description +==================== + +Some files use four space indentation, some use three. Some use +different levels in the same file. + +For the most part, class/function/variable naming follows PEP8, but it +wouldn't hurt to fix where necessary. + +Implementation +============== + +The implementation should be fairly mechanical: running the pep8 tool +over the code and fixing where appropriate. + +This should be merged in after the 2.0 release, since the changes will +likely make merging any pending pull requests more difficult. + +Additionally, and optionally, PEP8 compliance could be tracked by an +automated build system. + +Backward compatibility +====================== + +Public names of classes and functions that require change (there +shouldn't be many of these) should first be deprecated and then +removed in the next release cycle. + +Alternatives +============ + +PEP8 is a popular standard for Python code style, blessed by the +Python core developers, making any alternatives less desirable. diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index afb8f662e265..b0dbcd5b6d88 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -13,6 +13,7 @@ Matplotlib Enhancement Proposals :maxdepth: 1 template + MEP08 MEP10 MEP11 MEP12 From 7884afc641d610a366ccb97563d52aee91107dde Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 19:58:17 -0400 Subject: [PATCH 09/23] DOC : move MEP9 --- doc/devel/MEP/MEP09.rst | 215 ++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 216 insertions(+) create mode 100644 doc/devel/MEP/MEP09.rst diff --git a/doc/devel/MEP/MEP09.rst b/doc/devel/MEP/MEP09.rst new file mode 100644 index 000000000000..12176a7a8662 --- /dev/null +++ b/doc/devel/MEP/MEP09.rst @@ -0,0 +1,215 @@ +================================== + MEP9: Global interaction manager +================================== + +Add a global manager for all user interactivity with artists; make any +artist resizeable, moveable, highlightable, and selectable as desired +by the user. + +Status +====== +**Discussion** + +Branches and Pull requests +========================== +https://github.com/dhyams/matplotlib/tree/MEP9 + +Abstract +======== + +The goal is to be able to interact with matplotlib artists in a very +similar way as drawing programs do. When appropriate, the user should +be able to move, resize, or select an artist that is already on the +canvas. Of course, the script writer is ultimately in control of +whether an artist is able to be interacted with, or whether it is +static. + +This code to do this has already been privately implemented and +tested, and would need to be migrated from its current "mixin" +implementation, to a bona-fide part of matplotlib. + +The end result would be to have four new keywords available to +matplotlib.artist.Artist: _moveable_, _resizeable_, _selectable_, and +_highlightable_. Setting any one of these keywords to True would +activate interactivity for that artist. + +In effect, this MEP is a logical extension of event handling in +matplotlib; matplotlib already supports "low level" interactions like +left mouse presses, a key press, or similar. The MEP extends the +support to the logical level, where callbacks are performed on the +artists when certain interactive gestures from the user are detected. + +Detailed description +==================== + +This new functionality would be used to allow the end-user to better +interact with the graph. Many times, a graph is almost what the user +wants, but a small repositioning and/or resizing of components is +necessary. Rather than force the user to go back to the script to +trial-and-error the location, and simple drag and drop would be +appropriate. + +Also, this would better support applications that use matplotlib; +here, the end-user has no reasonable access or desire to edit the +underlying source in order to fine-tune a plot. Here, if matplotlib +offered the capability, one could move or resize artists on the canvas +to suit their needs. Also, the user should be able to highlight (with +a mouse over) an artist, and select it with a double-click, if the +application supports that sort of thing. In this MEP, we also want to +support the highlighting and selection natively; it is up to +application to handle what happens when the artist is selected. A +typical handling would be to display a dialog to edit the properties +of the artist. + +In the future, as well (this is not part of this MEP), matplotlib +could offer backend-specific property dialogs for each artist, which +are raised on artist selection. This MEP would be a necessary +stepping stone for that sort of capability. + +There are currently a few interactive capabilities in matplotlib +(e.g. legend.draggable()), but they tend to be scattered and are not +available for all artists. This MEP seeks to unify the interactive +interface and make it work for all artists. + +The current MEP also includes grab handles for resizing artists, and +appropriate boxes drawn when artists are moved or resized. + +Implementation +============== +* Add appropriate methods to the "tree" of artists so that the + interactivity manager has a consistent interface for the + interactivity manager to deal with. The proposed methods to add to + the artists, if they are to support interactivity, are: + * get_pixel_position_ll(self): get the pixel position of the lower + left corner of the artist's bounding box + * get_pixel_size(self): get the size of the artist's bounding box, + in pixels + * set_pixel_position_and_size(self,x,y,dx,dy): set the new size of + the artist, such that it fits within the specified bounding box. +* add capability to the backends to 1) provide cursors, since these + are needed for visual indication of moving/resizing, and 2) provide + a function that gets the current mouse position +* Implement the manager. This has already been done privately (by + dhyams) as a mixin, and has been tested quite a bit. The goal would + be to move the functionality of the manager into the artists so that + it is in matplotlib properly, and not as a "monkey patch" as I + currently have it coded. + + + +Current summary of the mixin +============================ + +(Note that this mixin is for now just private code, but can be added +to a branch obviously) + +InteractiveArtistMixin: + +Mixin class to make any generic object that is drawn on a matplotlib +canvas moveable and possibly resizeable. The Powerpoint model is +followed as closely as possible; not because I'm enamoured with +Powerpoint, but because that's what most people understand. An artist +can also be selectable, which means that the artist will receive the +on_activated() callback when double clicked. Finally, an artist can +be highlightable, which means that a highlight is drawn on the artist +whenever the mouse passes over. Typically, highlightable artists will +also be selectable, but that is left up to the user. So, basically +there are four attributes that can be set by the user on a per-artist +basis: + +* highlightable +* selectable +* moveable +* resizeable + +To be moveable (draggable) or resizeable, the object that is the +target of the mixin must support the following protocols: + +* get_pixel_position_ll(self) +* get_pixel_size(self) +* set_pixel_position_and_size(self,x,y,sx,sy) + +Note that nonresizeable objects are free to ignore the sx and sy +parameters. To be highlightable, the object that is the target of the +mixin must also support the following protocol: + +* get_highlight(self) + +Which returns a list of artists that will be used to draw the highlight. + +If the object that is the target of the mixin is not an matplotlib +artist, the following protocols must also be implemented. Doing so is +usually fairly trivial, as there has to be an artist *somewhere* that +is being drawn. Typically your object would just route these calls to +that artist. + +* get_figure(self) +* get_axes(self) +* contains(self,event) +* set_animated(self,flag) +* draw(self,renderer) +* get_visible(self) + +The following notifications are called on the artist, and the artist +can optionally implement these. + +* on_select_begin(self) +* on_select_end(self) +* on_drag_begin(self) +* on_drag_end(self) +* on_activated(self) +* on_highlight(self) +* on_right_click(self,event) +* on_left_click(self,event) +* on_middle_click(self,event) +* on_context_click(self,event) +* on_key_up(self,event) +* on_key_down(self,event) + +The following notifications are called on the canvas, if no +interactive artist handles the event: + +* on_press(self,event) +* on_left_click(self,event) +* on_middle_click(self,event) +* on_right_click(self,event) +* on_context_click(self,event) +* on_key_up(self,event) +* on_key_down(self,event) + +The following functions, if present, can be used to modify the +behavior of the interactive object: + +* press_filter(self,event) # determines if the object wants to have + the press event routed to it +* handle_unpicked_cursor() # can be used by the object to set a cursor + as the cursor passes over the object when it is unpicked. + +Supports multiple canvases, maintaining a drag lock, motion notifier, +and a global "enabled" flag per canvas. Supports fixed aspect ratio +resizings by holding the shift key during the resize. + +Known problems: + +* Zorder is not obeyed during the selection/drag operations. Because + of the blit technique used, I do not believe this can be fixed. The + only way I can think of is to search for all artists that have a + zorder greater then me, set them all to animated, and then redraw + them all on top during each drag refresh. This might be very slow; + need to try. +* the mixin only works for wx backends because of two things: 1) the + cursors are hardcoded, and 2) there is a call to + wx.GetMousePosition() Both of these shortcomings are reasonably + fixed by having each backend supply these things. + +Backward compatibility +====================== + +No problems with backward compatibility, although once this is in +place, it would be appropriate to obsolete some of the existing +interactive functions (like legend.draggable()) + +Alternatives +============ + +None that I know of. diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index b0dbcd5b6d88..7eeeac714203 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -14,6 +14,7 @@ Matplotlib Enhancement Proposals template MEP08 + MEP09 MEP10 MEP11 MEP12 From f5f0c2ca9db756684c2297991b9b61a1d4caee58 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 20:06:20 -0400 Subject: [PATCH 10/23] DOC : move MEP19 - updated section on auto-building docs - updated section on S3 uploads - correctly credit Thomas Kluyver (takluyver), not me, for launchpad builds --- doc/devel/MEP/MEP19.rst | 193 ++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 194 insertions(+) create mode 100644 doc/devel/MEP/MEP19.rst diff --git a/doc/devel/MEP/MEP19.rst b/doc/devel/MEP/MEP19.rst new file mode 100644 index 000000000000..e96ebf74d8cd --- /dev/null +++ b/doc/devel/MEP/MEP19.rst @@ -0,0 +1,193 @@ +=============================== + MEP19: Continuous Integration +=============================== + +Status +====== + +**Discussion** + +Branches and Pull requests +========================== + +Abstract +======== + +matplotlib could benefit from better and more reliable continuous +integration, both for testing and building installers and +documentation. + +Detailed description +==================== + +Current state-of-the-art +------------------------ + +**Testing** + +matplotlib currently uses Travis-CI for automated tests. While +Travis-CI should be praised for how much it does as a free service, it +has a number of shortcomings: + +- It often fails due to network timeouts when installing dependencies. + +- It often fails for inexplicable reasons. + +- build or test products can only be saved from build off of branches + on the main repo, not pull requsts, so it is often difficult to + "post mortem" analyse what went wrong. This is particularly + frustrating when the failure can not be subsequently reproduced + locally. + +- It is not extremely fast. matplotlib's cpu and memory requirements + for testing are much higher than the average Python project. + +- It only tests on Ubuntu Linux, and we have only minimal control over + the specifics of the platform. It can be upgraded at any time + outside of our control, causing unexpected delays at times that may + not be convenient in our release schedule. + +On the plus side, Travis-CI's integration with github -- automatically +testing all pending pull requests -- is exceptional. + +**Builds** + +There is no centralized effort for automated binary builds for +matplotlib. However, the following disparate things are being done +[If the authors mentioned here could fill in detail, that would be +great!]: + +- @sandrotosi: builds Debian packages + +- @takluyver: Has automated Ubuntu builds on Launchpad + +- @cgohlke: Makes Windows builds (don't know how automated that is) + +- @r-owen: Makes OS-X builds (don't know how automated that is) + +**Documentation** + +Documentation of master is now built by travis and uploaded to http://matplotlib.org/devdocs/index.html + +@NelleV, I believe, generates the docs automatically and posts them on +the web to chart MEP10 progress. + +Peculiarities of matplotlib +--------------------------- + +matplotlib has complex requirements that make testing and building +more taxing than many other Python projects. + +- The CPU time to run the tests is quite high. It puts us beyond the + free accounts of many CI services (e.g. ShiningPanda) + +- It has a large number of dependencies, and testing the full matrix + of all combinations is impractical. We need to be clever about what + space we test and guarantee to support. + +Requirements +------------ + +This section outlines the requirements that we would like to have. + +#. Testing all pull requests by hooking into the Github API, as + Travis-CI does + +#. Testing on all major platforms: Linux, Mac OS-X, MS Windows (in + that order of priority, based on user survey) + +#. Retain the last n days worth of build and test products, to aid in + post-mortem debugging. + +#. Automated nightly binary builds, so that users can test the + bleeding edge without installing a complete compilation + environment. + +#. Automated benchmarking. It would be nice to have a standard + benchmark suite (separate from the tests) whose performance could + be tracked over time, in different backends and platforms. While + this is separate from building and testing, ideally it would run on + the same infrastructure. + +#. Automated nightly building and publishing of documentation (or as + part of testing, to ensure PRs don't introduce documentation bugs). + (This would not replace the static documentation for stable + releases as a default). + +#. The test systems should be managable by multiple developers, so + that no single person becomes a bottleneck. (Travis-CI's design + does this well -- storing build configuration in the git + repository, rather than elsewhere, is a very good design.) + +#. Make it easy to test a large but sparse matrix of different + versions of matplotlib's dependencies. The matplotlib user survey + provides some good data as to where to focus our efforts: + https://docs.google.com/spreadsheet/ccc?key=0AjrPjlTMRTwTdHpQS25pcTZIRWdqX0pNckNSU01sMHc#gid=0 + +#. Nice to have: A decentralized design so that those with more + obscure platforms can publish build results to a central dashboard. + +Implementation +============== + +This part is yet-to-be-written. + +However, ideally, the implementation would be a third-party service, +to avoid adding system administration to our already stretched time. +As we have some donated funds, this service may be a paid one if it +offers significant time-saving advantages over free offerings. + +Backward compatibility +====================== + +Backward compatibility is not a major concern for this MEP. We will +replace current tools and procedures with something better and throw +out the old. + +Alternatives +============ + + +Hangout Notes +============= + +CI Infrastructure +----------------- + +- We like Travis and it will probably remain part of our arsenal in + any event. The reliability issues are being looked into. + +- Enable Amazon S3 uploads of testing products on Travis. This will + help with post-mortem of failures (@mdboom is looking into this + now). + +- We want Mac coverage. The best bet is probably to push Travis to + enable it for our project by paying them for a Pro account (since + they don't otherwise allow testing on both Linux and Mac). + +- We want Windows coverage. Shining Panda is an option there. + +- Investigate finding or building a tool that would collect and + synthesize test results from a number of sources and post it to + Github using the Github API. This may be of general use to the + Scipy community. + +- For both Windows and Mac, we should document (or better yet, script) + the process of setting up the machine for a build, and how to build + binaries and installers. This may require getting information from + Russel Owen and Christoph Gohlke. This is a necessary step for + doing automated builds, but would also be valuable for a number of + other reasons. + +The test framework itself +------------------------- + +- We should investigate ways to make it take less time + + - Eliminating redundant tests, if possible + + - General performance improvements to matplotlib will help + +- We should be covering more things, particularly more backends + +- We should have more unit tests, fewer integration tests, if possible diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index 7eeeac714203..c01045ae1775 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -21,4 +21,5 @@ Matplotlib Enhancement Proposals MEP13 MEP14 MEP15 + MEP19 MEP25 From 1e07323cbb57c8c33819b605d1891054246be7d8 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 22:24:06 -0400 Subject: [PATCH 11/23] DOC : clean up template --- doc/devel/MEP/template.rst | 56 ++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/doc/devel/MEP/template.rst b/doc/devel/MEP/template.rst index a36b7c9865d2..fae4a5132275 100644 --- a/doc/devel/MEP/template.rst +++ b/doc/devel/MEP/template.rst @@ -1,50 +1,72 @@ -MEP Template -============ +============== + MEP Template +============== + +.. contents:: + :local: -This MEP template is a guideline of the sections that a MEP should contain. Extra sections may be added if appropriate, and unnecessary sections may be noted as such. +This MEP template is a guideline of the sections that a MEP should +contain. Extra sections may be added if appropriate, and unnecessary +sections may be noted as such. Status ------- +====== MEPs go through a number of phases in their lifetime: -- **Discussion**: The MEP is being actively discussed on the mailing list and it is being improved by its author. The mailing list discussion of the MEP should include the MEP number (MEPxxx) in the subject line so they can be easily related to the MEP. +- **Discussion**: The MEP is being actively discussed on the mailing + list and it is being improved by its author. The mailing list + discussion of the MEP should include the MEP number (MEPxxx) in the + subject line so they can be easily related to the MEP. -- **Progress**: Consensus was reached on the mailing list and implementation work has begun. +- **Progress**: Consensus was reached on the mailing list and + implementation work has begun. - **Completed**: The implementation has been merged into master. -- **Superseded**: This MEP has been abandoned in favor of another approach. +- **Superseded**: This MEP has been abandoned in favor of another + approach. Branches and Pull requests --------------------------- +========================== All development branches containing work on this MEP should be linked to from here. -All pull requests submitted relating to this MEP should be linked to from here. (A MEP does not need to be implemented in a single pull request if it makes sense to implement it in discrete phases). +All pull requests submitted relating to this MEP should be linked to +from here. (A MEP does not need to be implemented in a single pull +request if it makes sense to implement it in discrete phases). Abstract --------- +======== The abstract should be a short description of what the MEP will achieve. Detailed description --------------------- +==================== -This section describes the need for the MEP. It should describe the existing problem that it is trying to solve and why this MEP makes the situation better. It should include examples of how the new functionality would be used and perhaps some use cases. +This section describes the need for the MEP. It should describe the +existing problem that it is trying to solve and why this MEP makes the +situation better. It should include examples of how the new +functionality would be used and perhaps some use cases. Implementation --------------- +============== -This section lists the major steps required to implement the MEP. Where possible, it should be noted where one step is dependent on another, and which steps may be optionally omitted. Where it makes sense, each step should include a link related pull requests as the implementation progresses. +This section lists the major steps required to implement the MEP. +Where possible, it should be noted where one step is dependent on +another, and which steps may be optionally omitted. Where it makes +sense, each step should include a link related pull requests as the +implementation progresses. Backward compatibility ----------------------- +====================== This section describes the ways in which the MEP breaks backward incompatibility. Alternatives ------------- +============ -If there were any alternative solutions to solving the same problem, they should be discussed here, along with a justification for the chosen approach. +If there were any alternative solutions to solving the same problem, +they should be discussed here, along with a justification for the +chosen approach. From 30c1da6613a0800d42e9552a62d780b7042a8c78 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 22:28:35 -0400 Subject: [PATCH 12/23] DOC : move MEP21 --- doc/devel/MEP/MEP21.rst | 56 +++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 57 insertions(+) create mode 100644 doc/devel/MEP/MEP21.rst diff --git a/doc/devel/MEP/MEP21.rst b/doc/devel/MEP/MEP21.rst new file mode 100644 index 000000000000..a5c2663e070c --- /dev/null +++ b/doc/devel/MEP/MEP21.rst @@ -0,0 +1,56 @@ +============================== + MEP21: color and cm refactor +============================== +.. contents:: + :local: + + +Status +====== + +- **Discussion**: This MEP has not commenced yet, but here are some + ongoing ideas which may become a part of this MEP: + + + +Branches and Pull requests +========================== + + + +Abstract +======== + + + * color + * tidy up the namespace + * Define a "Color" class + * make it easy to convert from one color type to another ```hex -> RGB```, ```RGB -> hex```, ```HSV -> RGB``` etc. + * improve the construction of a colormap - the dictionary approach is archaic and overly complex (though incredibly powerful) + * make it possible to interpolate between two or more color types + in different modes, especially useful for construction of + colormaps in HSV space for instance + * cm + * rename the module to something more descriptive - mappables? + + +Overall, there are a lot of improvements that can be made with +matplotlib color handling - managing backwards compatibility will be +difficult as there are some badly named variables/modules which really +shouldn't exist - but a clear path and message for migration should be +available, with a large amount of focus on this in the API changes +documentation. + + +Detailed description +==================== + +Implementation +============== + + +Backward compatibility +====================== + +Alternatives +============ diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index c01045ae1775..68de43f12a19 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -22,4 +22,5 @@ Matplotlib Enhancement Proposals MEP14 MEP15 MEP19 + MEP21 MEP25 From 06a0e8eb97b14b452b3ce6592b178d67e4687862 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 22:28:50 -0400 Subject: [PATCH 13/23] DOC : fix markup in MEP09 --- doc/devel/MEP/MEP09.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/devel/MEP/MEP09.rst b/doc/devel/MEP/MEP09.rst index 12176a7a8662..627187c363fd 100644 --- a/doc/devel/MEP/MEP09.rst +++ b/doc/devel/MEP/MEP09.rst @@ -2,6 +2,9 @@ MEP9: Global interaction manager ================================== +.. contents:: + :local: + Add a global manager for all user interactivity with artists; make any artist resizeable, moveable, highlightable, and selectable as desired by the user. @@ -80,11 +83,11 @@ Implementation interactivity manager has a consistent interface for the interactivity manager to deal with. The proposed methods to add to the artists, if they are to support interactivity, are: - * get_pixel_position_ll(self): get the pixel position of the lower + * get_pixel_position_ll(self): get the pixel position of the lower left corner of the artist's bounding box - * get_pixel_size(self): get the size of the artist's bounding box, + * get_pixel_size(self): get the size of the artist's bounding box, in pixels - * set_pixel_position_and_size(self,x,y,dx,dy): set the new size of + * set_pixel_position_and_size(self,x,y,dx,dy): set the new size of the artist, such that it fits within the specified bounding box. * add capability to the backends to 1) provide cursors, since these are needed for visual indication of moving/resizing, and 2) provide From 34263c1dd2958e58031a18d77cdff9e8669bf0a8 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 22:33:23 -0400 Subject: [PATCH 14/23] DOC : move MEP22 --- doc/devel/MEP/MEP22.rst | 193 ++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 194 insertions(+) create mode 100644 doc/devel/MEP/MEP22.rst diff --git a/doc/devel/MEP/MEP22.rst b/doc/devel/MEP/MEP22.rst new file mode 100644 index 000000000000..91786104d9b7 --- /dev/null +++ b/doc/devel/MEP/MEP22.rst @@ -0,0 +1,193 @@ +======================== + MEP22: Toolbar rewrite +======================== + +.. contents:: + :local: + +Status +====== +**Progress** + + +Branches and Pull requests +========================== + +Previous work + * https://github.com/matplotlib/matplotlib/pull/1849 + * https://github.com/matplotlib/matplotlib/pull/2557 + * https://github.com/matplotlib/matplotlib/pull/2465 + +Pull Requests: + * Removing the NavigationToolbar classes + https://github.com/matplotlib/matplotlib/pull/2740 **CLOSED** + * Keeping the NavigationToolbar classes https://github.com/matplotlib/matplotlib/pull/2759 **CLOSED** + * Navigation by events: https://github.com/matplotlib/matplotlib/pull/3652 + +Abstract +======== + +The main goal of this MEP is to make it easier to modify (add, change, +remove) the way the user interacts with the figures. + +The user interaction with the figure is deeply integrated within the +Canvas and Toolbar. Making extremely difficult to do any modification. + +This MEP proposes the separation of this interaction into Toolbar, +Navigation and Tools to provide independent access and +reconfiguration. + +This approach will make easier to create and share tools among +users. In the far future, we can even foresee a kind of Marketplace +for `Tools` where the most popular can be added into the main +distribution. + +Detailed description +==================== + +The reconfiguration of the Toolbar is complex, most of the time it +requires a custom backend. + +The creation of custom Tools sometimes interferes with the Toolbar, as +example see https://github.com/matplotlib/matplotlib/issues/2694 also +the shortcuts are hardcoded and again not easily modifiable +https://github.com/matplotlib/matplotlib/issues/2699 + +The proposed solution is to take the actions out of the `Toolbar` and +the shortcuts out of the `Canvas`. This actions and shortcuts will be +in the form of `Tools`. + +A new class `Navigation` will be the bridge between the events from +the `Canvas` and `Toolbar` and redirect them to the appropiate `Tool`. + +At the end the user interaction will be divided into three classes: + + * NavigationBase: This class is instantiated for each FigureManager + and connect the all user interactions with the Tools + * ToolbarBase: This existing class is relegated only as a GUI access + to Tools. + * ToolBase: Is the basic definition of Tools. + + +Implementation +============== + +ToolBase(object) +---------------- + +Tools can have a graphical representation as the `SubplotTool` or not even be present in the Toolbar as `Quit` + +The `ToolBase` has the following class attributes for configuration at definition time + + * keymap = None: Key(s) to be used to trigger the tool + * description = '': Small description of the tool + * image = None: Image that is used in the toolbar + +The following instance attributes are set at instantiation: + * name + * navigation + +**Methods** + * trigger(self, event): This is the main method of the Tool, it is called when the Tool is triggered by: + * Toolbar button click + * keypress associated with the Tool Keymap + * Call to navigation.trigger_tool(name) + * set_figure(self, figure): Set the figure and navigation attributes + * destroy(self, *args): Destroy the `Tool` graphical interface (if exists) + +**Available Tools** + * ToolQuit + * ToolEnableAllNavigation + * ToolEnableNavigation + * ToolToggleGrid + * ToolToggleFullScreen + * ToolToggleYScale + * ToolToggleXScale + * ToolHome + * ToolBack + * ToolForward + * SaveFigureBase + * ConfigureSubplotsBase + + +ToolToggleBase(ToolBase) +------------------------ + +The `ToolToggleBase` has the following class attributes for +configuration at definition time + + * radio_group = None: Attribute to group 'radio' like tools (mutually + exclusive) + * cursor = None: Cursor to use when the tool is active + +The **Toggleable** Tools, can capture keypress, mouse moves, and mouse +button press + +It defines the following methods + * enable(self, event): Called by `ToolToggleBase.trigger` method + * disable(self, event): Called when the tool is untoggled + * toggled : **Property** True or False + +**Available Tools** + * ToolZoom + * ToolPan + +NavigationBase +-------------- + +Defines the following attributes + * canvas: + * keypresslock: Lock to know if the `canvas` key_press_event` is + available and process it + * messagelock: Lock to know if the message is available to write + +Public methods for **User use**: + * nav_connect(self, s, func): Connect to to navigation for events + * nav_disconnect(self, cid): Disconnect from navigation event + * message_event(self, message, sender=None): Emit a + tool_message_event event + * active_toggle(self): **Property** The currently toggled tools or + None + * get_tool_keymap(self, name): Return a list of keys that are + associated with the tool + * set_tool_keymap(self, name, *keys): Set the keys for the given tool + * remove_tool(self, name): Removes tool from the navigation control. + * add_tools(self, tools): Add multiple tools to `Navigation` + * add_tool(self, name, tool, group=None, position=None): Add a tool + to the Navigation + * tool_trigger_event(self, name, sender=None, canvasevent=None, + data=None): Trigger a tool and fire the event + + * tools(self) **Property**: Return a dict with available tools with + corresponding keymaps, descriptions and objects + * get_tool(self, name): Return the tool object + + + +ToolbarBase +----------- + +Methods for **Backend implementation** + * add_toolitem(self, name, group, position, image, description, + toggle): Add a toolitem to the toolbar. This method is a callback + from `tool_added_event` (emited by navigation) + * set_message(self, s): Display a message on toolbar or in status bar + * toggle_toolitem(self, name): Toggle the toolitem without firing + event. + * remove_toolitem(self, name): Remove a toolitem from the `Toolbar` + + +Backward compatibility +====================== + +For backward compatibility added a 'navigation' key to +`rcsetup.validate_toolbar`, that is used for Navigation classes +instantiation instead of the NavigationToolbar classes + +With this parameter, it makes it transparent to anyone using the +existing backends. + +[@pelson comment: This also gives us an opportunity to avoid needing +to implement all of this in the same PR - some backends can +potentially exist without the new functionality for a short while (but +it must be done at some point).] diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index 68de43f12a19..f3462a04870d 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -23,4 +23,5 @@ Matplotlib Enhancement Proposals MEP15 MEP19 MEP21 + MEP22 MEP25 From f5bb16b0f60ee5669fb1e5d0c17913512103b730 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 22:45:26 -0400 Subject: [PATCH 15/23] DOC : move MEP23 --- doc/devel/MEP/MEP23.rst | 116 ++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 117 insertions(+) create mode 100644 doc/devel/MEP/MEP23.rst diff --git a/doc/devel/MEP/MEP23.rst b/doc/devel/MEP/MEP23.rst new file mode 100644 index 000000000000..ae51294f017a --- /dev/null +++ b/doc/devel/MEP/MEP23.rst @@ -0,0 +1,116 @@ +======================================== + MEP23: Multiple Figures per GUI window +======================================== + +.. contents:: + :local: + + + +Status +====== + +**Discussion** + +Branches and Pull requests +========================== + +**Previous work** +- https://github.com/matplotlib/matplotlib/pull/2465 **To-delete** + + +Abstract +======== + +Add the possibility to have multiple figures grouped under the same +`FigureManager` + +Detailed description +==================== + +Under the current structure, every canvas has its own window. + +This is and may continue to be the desired method of operation for +most use cases. + +Sometimes when there are too many figures open at the same time, it is +desirable to be able to group these under the same window +[see](https://github.com/matplotlib/matplotlib/issues/2194). + +The proposed solution modifies `FigureManagerBase` to contain and +manage more than one `canvas`. The settings parameter +`rcParams['backend.multifigure']` control when the **MultiFigure** +behaviour is desired. + +**Note** + +It is important to note, that the proposed solution, assumes that the +[MEP22](https://github.com/matplotlib/matplotlib/wiki/Mep22) is +already in place. This is simply because the actual implementation of +the `Toolbar` makes it pretty hard to switch between canvases. + +Implementation +============== + +The first implementation will be done in `GTK3` using a Notebook as +canvas container. + +`FigureManagerBase` +------------------- + +will add the following new methods + +* `add_canvas`: To add a canvas to an existing `FigureManager` object +* `remove_canvas`: To remove a canvas from a `FigureManager` object, + if it is the last one, it will be destroyed +* `move_canvas`: To move a canvas from one `FigureManager` to another. +* `set_canvas_title`: To change the title associated with a specific + canvas container +* `get_canvas_title`: To get the title associated with a specific + canvas container +* `get_active_canvas`: To get the canvas that is in the foreground and + is subject to the gui events. There is no `set_active_canvas` + because the active canvas, is defined when `show` is called on a + `Canvas` object. + +`new_figure_manager` +-------------------- + +To control which `FigureManager` will contain the new figures, an +extra optional parameter `figuremanager` will be added, this parameter +value will be passed to `new_figure_manager_given_figure` + +`new_figure_manager_given_figure` +--------------------------------- + +* If `figuremanager` parameter is give, this `FigureManager` object + will be used instead of creating a new one. +* If `rcParams['backend.multifigure'] == True`: The last + `FigureManager` object will be used instead of creating a new one. + +`NavigationBase` +---------------- + +Modifies the `NavigationBase` to keep a list of canvases, directing +the actions to the active one + +Backward compatibility +====================== + +For the **MultiFigure** properties to be visible, the user has to +activate them directly setting `rcParams['backend.multifigure'] = +True` + +It should be backwards compatible for backends that adhere to the +current `FigureManagerBase` structure even if they have not +implemented the **MultiFigure** magic yet. + + +Alternatives +============ + +Insted of modifing the `FigureManagerBase` it could be possible to add +a parallel class, that handles the cases where +`rcParams['backend.multifigure'] = True`. This will warranty that +there won't be any problems with custom made backends, but also makes +bigger the code, and more things to mantain. diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index f3462a04870d..522a46a6ac6c 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -24,4 +24,5 @@ Matplotlib Enhancement Proposals MEP19 MEP21 MEP22 + MEP23 MEP25 From 292a9a23080d86afd0cc73cecf3dfefd65ad3690 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 22:53:59 -0400 Subject: [PATCH 16/23] DOC : really fix MEP09 formatting --- doc/devel/MEP/MEP09.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/devel/MEP/MEP09.rst b/doc/devel/MEP/MEP09.rst index 627187c363fd..b7e0ca7a39b7 100644 --- a/doc/devel/MEP/MEP09.rst +++ b/doc/devel/MEP/MEP09.rst @@ -83,12 +83,14 @@ Implementation interactivity manager has a consistent interface for the interactivity manager to deal with. The proposed methods to add to the artists, if they are to support interactivity, are: + * get_pixel_position_ll(self): get the pixel position of the lower - left corner of the artist's bounding box + left corner of the artist's bounding box * get_pixel_size(self): get the size of the artist's bounding box, - in pixels + in pixels * set_pixel_position_and_size(self,x,y,dx,dy): set the new size of - the artist, such that it fits within the specified bounding box. + the artist, such that it fits within the specified bounding box. + * add capability to the backends to 1) provide cursors, since these are needed for visual indication of moving/resizing, and 2) provide a function that gets the current mouse position From a295d540c9e72179dd2f832e6e22bc9713984c9c Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 22:55:34 -0400 Subject: [PATCH 17/23] DOC : move MEP24 --- doc/devel/MEP/MEP24.rst | 51 +++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 52 insertions(+) create mode 100644 doc/devel/MEP/MEP24.rst diff --git a/doc/devel/MEP/MEP24.rst b/doc/devel/MEP/MEP24.rst new file mode 100644 index 000000000000..89132cc7cd0e --- /dev/null +++ b/doc/devel/MEP/MEP24.rst @@ -0,0 +1,51 @@ +======================================= + MEP24: negative radius in polar plots +======================================= + +.. contents:: + :local: + + + +Status +====== +*Discussion* + +Branches and Pull requests +========================== + +None + +Abstract +======== + +It is clear that polar plots need to be able to gracefully handle +negative r values (not by clipping or reflection). + +Detailed description +==================== + +One obvious application that we should support is bB plots (see +https://github.com/matplotlib/matplotlib/issues/1730#issuecomment-40815837), +but this seems more generally useful (for example growth rate as a +function of angle). The assumption in the current code (as I +understand it) is that the center of the graph is `r==0`, however it +would be good to be able to set the center to be at any `r` (with any +value less than the off set clipped). + +Implementation +============== + + +Related Issues +============== +#1730, #1603, #2203, #2133 + + + +Backward compatibility +====================== + + +Alternatives +============ diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index 522a46a6ac6c..7564cabd0222 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -25,4 +25,5 @@ Matplotlib Enhancement Proposals MEP21 MEP22 MEP23 + MEP24 MEP25 From f4b589b82207165e005d612b2aab4d2f6085281c Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 23:06:11 -0400 Subject: [PATCH 18/23] DOC : move MEP26 --- doc/devel/MEP/MEP26.rst | 232 ++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 233 insertions(+) create mode 100644 doc/devel/MEP/MEP26.rst diff --git a/doc/devel/MEP/MEP26.rst b/doc/devel/MEP/MEP26.rst new file mode 100644 index 000000000000..01e539c74304 --- /dev/null +++ b/doc/devel/MEP/MEP26.rst @@ -0,0 +1,232 @@ +======================= + MEP26: Artist styling +======================= + +.. contents:: + :local: + + +Status +====== + +**Proposed** + +Branches and Pull requests +========================== + +Abstract +======== + +This MEP proposes a new stylesheet implementation to allow more +comprehensive and dynamic styling of artists. + +The current version of matplotlib (1.4.0) allows stylesheets based on +the rcParams syntax to be applied before creation of a plot. The +methodology below proposes a new syntax, based on CSS, which would +allow styling of individual artists and properties, which can be +applied dynamically to existing objects. + +This is related to (and makes steps toward) the overall goal of moving +to a DOM/tree-like architecture. + + +Detailed description +==================== + +Currently, the look and appearance of existing artist objects (figure, +axes, Line2D etc...) can only be updated via `set_` and `get_` methods +on the artist object, which is quite laborious, especially if no +reference to the artist(s) has been stored. The new style sheets +introduced in 1.4 allow styling before a plot is created, but do not +offer any means to dynamically update plots or distinguish between +artists of the same type (i.e. to specifiy the `line color` and `line +style` separately for differing `Line2D` objects). + +The initial development should concentrate on allowing styling of +artist primitives (those `artists` that do not contain other +`artists`), and further development could expand the CSS syntax rules +and parser to allow more complex styling. See the appendix for a list +of primitives. + +The new methodology would require development of a number of steps: + +- A new stylesheet syntax (likely based on CSS) to allow selection of + artists by type, class, id etc... +- A mechanism by which to parse a stylesheet into a tree +- A mechanism by which to translate the parse-tree into something + which can be used to update the properties of relevant + artists. Ideally this would implement a method by which to traverse + the artists in a tree-like structure. +- A mechanism by which to generate a stylesheet from existing artist + properties. This would be useful to allow a user to export a + stylesheet from an existing figure (where the appearance may have + been set using the matplotlib API)... + +Implementation +============== + +It will be easiest to allow a '3rd party' to modify/set the style of +an artist if the 'style' is created as a separate class and store +against the artist as a property. The `GraphicsContext` class already +provides a the basis of a `Style` class and an artists `draw` method can +be refactored to use the `Style` class rather than setting up it's own +`GraphicsContext` and transferring it's style-related properties to +it. A minimal example of how this could be implemented is shown here: +https://github.com/JamesRamm/mpl_experiment + +IMO, this will also make the API and code base much neater as +individual get/set methods for artist style properties are now +redundant... Indirectly related would be a general drive to replace +get/set methods with properties. Implementing the style class with +properties would be a big stride toward this... + +For initial development, I suggest developing a syntax based on a much +(much much) simplified version of CSS. I am in favour of dubbing this +Artist Style Sheets :+1: : + +BNF Grammar +----------- + +I propose a very simple syntax to implement initially (like a proof of +concept), which can be expanded upon in the future. The BNF form of +the syntax is given below and then explained :: + + RuleSet ::= SelectorSequence "{"Declaration"}" + + SelectorSequence :: = Selector {"," Selector} + + Declaration ::= propName":" propValue";" + + Selector ::= ArtistIdent{"#"Ident} + + propName ::= Ident + + propValue ::= Ident | Number | Colour | "None" + +`ArtistIdent`, `Ident`, `Number` and `Colour` are tokens (the basic +building blocks of the expression) which are defined by regular +expressions. + +Syntax +------ + +A CSS stylesheet consists of a series of **rule sets** in hierarchical +order (rules are applied from top to bottom). Each rule follows the +syntax :: + + selector {attribute: value;} + +Each rule can have any number of `attribute`:`value` pairs, and a +stylesheet can have any number of rules. + +The initial syntax is designed only for `artist` primitives. It does +not address the question of how to set properties on `container` types +(whose properties may themselves be `artists` with settable +properties), however, a future solution to this could simply be nested +`RuleSet`s + +Selectors +~~~~~~~~~ + + +Selectors define the object to which the attribute updates should be +applied. As a starting point, I propose just 2 selectors to use in +initial development: + + + +Artist Type Selector + + +Select an `artist` by it's type. E.g `Line2D` or `Text`:: + + Line2D {attribute: value} + +The regex for matching the artist type selector (`ArtistIdent` in the BNF grammar) would be:: + + ArtistIdent = r'(?P\bLine2D\b|\bText\b|\bAxesImage\b|\bFigureImage\b|\bPatch\b)' + +GID selector +~~~~~~~~~~~~ + +Select an `artist` by its `gid`:: + + Line2D#myGID {attribute: value} + +A `gid` can be any string, so the regex could be as follows:: + + Ident = r'(?P[a-zA-Z_][a-zA-Z_0-9]*)' + + +The above selectors roughly correspond to their CSS counterparts +(http://www.w3.org/TR/CSS21/selector.html) + +Attributes and values +~~~~~~~~~~~~~~~~~~~~~ + +- `Attributes` are any valid (settable) property for the `artist` in question. +- `Values` are any valid value for the property (Usually a string, or number). + +Parsing +------- + +Parsing would consist of breaking the stylesheet into tokens (the +python cookbook gives a nice tokenizing recipe on page 66), applying +the syntax rules and constructing a `Tree`. This requires defining the +grammar of the stylesheet (again, we can borrow from CSS) and writing +a parser. Happily, there is a recipe for this in the python cookbook +aswell. + + +Visitor pattern for matplotlib figure +------------------------------------- + +In order to apply the stylesheet rules to the relevant artists, we +need to 'visit' each artist in a figure and apply the relevant rule. +Here is a visitor class (again, thanks to python cookbook), where each +`node` would be an artist in the figure. A `visit_` method would need +to be implemented for each mpl artist, to handle the different +properties for each :: + + class Visitor: + def visit(self, node): + name = 'visit_' + type(node).__name__ + meth = getattr(self, name, None) + if meth is None: + raise NotImplementedError + return meth(node) + +An `evaluator` class would then take the stylesheet rules and +implement the visitor on each one of them. + + + +Backward compatibility +====================== + +Implementing a separate `Style` class would break backward +compatibility as many get/set methods on an artist would become +redundant. While it would be possible to alter these methods to hook +into the `Style` class (stored as a property against the artist), I +would be in favor of simply removing them to both neaten/simplify the +codebase and to provide a simple, uncluttered API... + +Alternatives +============ + +No alternatives, but some of the ground covered here overlaps with +MEP25, which may assist in this development + +Appendix +======== + +Matplotlib primitives +~~~~~~~~~~~~~~~~~~~~~ + +This will form the initial selectors which stylesheets can use. + +* Line2D +* Text +* AxesImage +* FigureImage +* Patch diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index 7564cabd0222..4e710d2eda25 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -27,3 +27,4 @@ Matplotlib Enhancement Proposals MEP23 MEP24 MEP25 + MEP26 From 936c80ae8a2d20107b74665beb116e673555c670 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 23:31:01 -0400 Subject: [PATCH 19/23] DOC : move MEP27 --- doc/devel/MEP/MEP27.rst | 225 ++++++++++++++++++++++++++++++++++++++++ doc/devel/MEP/index.rst | 1 + 2 files changed, 226 insertions(+) create mode 100644 doc/devel/MEP/MEP27.rst diff --git a/doc/devel/MEP/MEP27.rst b/doc/devel/MEP/MEP27.rst new file mode 100644 index 000000000000..1995851d9147 --- /dev/null +++ b/doc/devel/MEP/MEP27.rst @@ -0,0 +1,225 @@ +====================================== + MEP27: decouple pyplot from backends +====================================== + +.. contents:: + :local: + + +Status +====== +**Discussion** + +Branches and Pull requests +========================== +Main PR (including GTK3): ++ https://github.com/matplotlib/matplotlib/pull/4143 + +Backend specific branch diffs: ++ https://github.com/OceanWolf/matplotlib/compare/backend-refactor...OceanWolf:backend-refactor-tkagg ++ https://github.com/OceanWolf/matplotlib/compare/backend-refactor...OceanWolf:backend-refactor-qt ++ https://github.com/OceanWolf/matplotlib/compare/backend-refactor...backend-refactor-wx + +Abstract +======== + +This MEP refactors the backends to give a more structured and +consistent API, removing generic code and consolidate existing code. +To do this we propose splitting: + +1. ``FigureManagerBase`` and its derived classes into the core + functionality class ``FigureManager`` and a backend specific class + ``WindowBase`` and +2. ``ShowBase`` and its derived classes into ``Gcf.show_all`` and ``MainLoopBase``. + +Detailed description +==================== + +This MEP aims to consolidate the backends API into one single uniform +API, removing generic code out of the backend (which includes +``_pylab_helpers`` and ``Gcf``), and push code to a more appropriate +level in matplotlib. With this we automatically remove +inconsistencies that appear in the backends, such as +``FigureManagerBase.resize(w, h)`` which sometimes sets the canvas, +and other times set the entire window to the dimensions given, +depending on the backend. + +Two main places for generic code appear in the classes derived from +``FigureManagerBase`` and ``ShowBase``. + +1. ``FigureManagerBase`` has **three** jobs at the moment: + 1. The documentation describes it as a *``Helper class for pyplot + mode, wraps everything up into a neat bundle''* + 2. But it doesn't just wrap the canvas and toolbar, it also does + all of the windowing tasks itself. The conflation of these two + tasks gets seen the best in the following line: ```python + self.set_window_title("Figure %d" % num) ``` This combines + backend specific code ``self.set_window_title(title)`` with + matplotlib generic code ``title = "Figure %d" % num``. + + 3. Currently the backend specific subclass of ``FigureManager`` + decides when to end the mainloop. This also seems very wrong + as the figure should have no control over the other figures. + + +2. ``ShowBase`` has two jobs: + 1. It has the job of going through all figure managers registered + in ``_pylab_helpers.Gcf`` and telling them to show themselves. + 2. And secondly it has the job of performing the backend specific + ``mainloop`` to block the main programme and thus keep the + figures from dying. + +Implementation +============== + +The description of this MEP gives us most of the solution: + +1. To remove the windowing aspect out of ``FigureManagerBase`` letting + it simply wrap this new class along with the other backend classes. + Create a new ``WindowBase`` class that can handle this + functionality, with pass-through methods (:arrow_right:) to + ``WindowBase``. Classes that subclass ``WindowBase`` should also + subclass the GUI specific window class to ensure backward + compatibility (``manager.window == manager.window``). +2. Refactor the mainloop of ``ShowBase`` into ``MainLoopBase``, which + encapsulates the end of the loop as well. We give an instance of + ``MainLoop`` to ``FigureManager`` as a key unlock the exit method + (requiring all keys returned before the loop can die). Note this + opens the possibility for multiple backends to run concurrently. +3. Now that ``FigureManagerBase`` has no backend specifics in it, to + rename it to ``FigureManager``, and move to a new file + ``backend_managers.py`` noting that: + 1. This allows us to break up the conversion of backends into + separate PRs as we can keep the existing ``FigureManagerBase`` + class and its dependencies intact. + 2. and this also anticipates MEP22 where the new + ``NavigationBase`` has morphed into a backend independent + ``ToolManager``. + ++--------------------------------------+------------------------------+---------------------+--------------------------------+ +|FigureManagerBase(canvas, num) |FigureManager(figure, num) |``WindowBase(title)``|Notes | +| | | | | ++======================================+==============================+=====================+================================+ +|show |:arrow_right: |show | | ++--------------------------------------+------------------------------+---------------------+--------------------------------+ +|destroy |calls destroy on all |destroy | | +| |components | | | ++--------------------------------------+------------------------------+---------------------+--------------------------------+ +|full_screen_toggle |handles logic |set_fullscreen | | ++--------------------------------------+------------------------------+---------------------+--------------------------------+ +|resize |:arrow_right: |resize | | ++--------------------------------------+------------------------------+---------------------+--------------------------------+ +|key_press |key_press |:no_entry: | | ++--------------------------------------+------------------------------+---------------------+--------------------------------+ +|show_popup |show_poup |:no_entry: |Not used anywhere in mpl, and | +| | | |does nothing. | +| | | | | +| | | | | +| | | | | ++--------------------------------------+------------------------------+---------------------+--------------------------------+ +|get_window_title |:arrow_right: |get_window_title | | ++--------------------------------------+------------------------------+---------------------+--------------------------------+ +|set_window_title |:arrow_right: |set_window_title | | ++--------------------------------------+------------------------------+---------------------+--------------------------------+ +|:no_entry: |_get_toolbar | |A common method to all | +| | | |subclasses of FigureManagerBase | +| | | | | +| | | | | ++--------------------------------------+------------------------------+---------------------+--------------------------------+ +|:no_entry: |:no_entry: |set_default_size | | ++--------------------------------------+------------------------------+---------------------+--------------------------------+ +|:no_entry: |:no_entry: |add_element_to_window| | ++--------------------------------------+------------------------------+---------------------+--------------------------------+ + + ++----------+------------+-------------+ +|ShowBase |MainLoopBase|Notes | ++==========+============+=============+ +|mainloop |begin | | ++----------+------------+-------------+ +|:no_entry:|end |Gets called | +| | |automagically| +| | |when no more | +| | |instances of | +| | |the subclass | +| | |exist | ++----------+------------+-------------+ +|__call__ |:no_entry: |Method moved | +| | |to | +| | |Gcf.show_all | ++----------+------------+-------------+ + +Future compatibility +==================== + +As eluded to above when discussing MEP 22, this refactor makes it easy +to add in new generic features. At the moment, MEP 22 has to make +ugly hacks to each class extending from ``FigureManagerBase``. With +this code, this only needs to get made in the single ``FigureManager`` +class. This also makes the later deprecation of +``NavigationToolbar2`` very straightforward, only needing to touch the +single ``FigureManager`` class + +MEP 23 makes for another use case where this refactored code will come +in very handy. + +Backward compatibility +====================== + +As we leave all backend code intact, only adding missing methods to +existing classes, this should work seamlessly for all use cases. The +only difference will lie for backends that used +``FigureManager.resize`` to resize the canvas and not the window, due +to the standardisation of the API. + +I would envision that the classes made obsolete by this refactor get +deprecated and removed on the same timetable as +``NavigationToolbar2``, also note that the change in call signature to +the ``FigureCanvasWx`` constructor, while backward compatible, I think +the old (imho ugly style) signature should get deprecated and removed +in the same manner as everything else. + ++-------------------------+-------------------------+-------------------------+ +|backend |manager.resize(w,h) |Extra | ++=========================+=========================+=========================+ +|gtk3 |window | | ++-------------------------+-------------------------+-------------------------+ +|Tk |canvas | | ++-------------------------+-------------------------+-------------------------+ +|Qt |window | | ++-------------------------+-------------------------+-------------------------+ +|Wx |canvas |FigureManagerWx had | +| | |``frame`` as an alias to | +| | |window, so this also | +| | |breaks BC. | ++-------------------------+-------------------------+-------------------------+ + + +Alternatives +============ + +If there were any alternative solutions to solving the same problem, +they should be discussed here, along with a justification for the +chosen approach. + +Questions +========= + +Mdehoon: Can you elaborate on how to run multiple backends +concurrently? + +OceanWolf: @mdehoon, as I say, not for this MEP, but I see this MEP +opens it up as a future possibility. Basically the ``MainLoopBase`` +class acts a per backend Gcf, in this MEP it tracks the number of +figures open per backend, and manages the mainloops for those +backends. It closes the backend specific mainloop when it detects +that no figures remain open for that backend. Because of this I +imagine that with only a small amount of tweaking that we can do +full-multi-backend matplotlib. No idea yet why one would want to, but +I leave the possibility there in MainLoopBase. With all the +backend-code specifics refactored out of ``FigureManager`` also aids +in this, one manager to rule them (the backends) all. + +Mdehoon: @OceanWolf, OK, thanks for the explanation. Having a uniform +API for the backends is very important for the maintainability of +matplotlib. I think this MEP is a step in the right direction. diff --git a/doc/devel/MEP/index.rst b/doc/devel/MEP/index.rst index 4e710d2eda25..1a14f65de440 100644 --- a/doc/devel/MEP/index.rst +++ b/doc/devel/MEP/index.rst @@ -28,3 +28,4 @@ Matplotlib Enhancement Proposals MEP24 MEP25 MEP26 + MEP27 From 1d140ebebcc982510a34a2471f88a701fd05f021 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 28 Mar 2015 23:38:37 -0400 Subject: [PATCH 20/23] DOC : maybe fix MEP14 --- doc/devel/MEP/MEP14.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/devel/MEP/MEP14.rst b/doc/devel/MEP/MEP14.rst index eb0ebd34f381..31d03190b5cd 100644 --- a/doc/devel/MEP/MEP14.rst +++ b/doc/devel/MEP/MEP14.rst @@ -58,6 +58,7 @@ source tree. Why add more text rendering engines? The "built-in" text rendering has a number of shortcomings. + - It only handles right-to-left languages, and doesn't handle many special features of Unicode, such as combining diacriticals. - The multiline support is imperfect and only supports manual From 5839ab2b03a9a45dd868df1766de88e2771eb7ad Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 29 Mar 2015 22:21:50 -0400 Subject: [PATCH 21/23] DOC : fix indention in MEP21 --- doc/devel/MEP/MEP21.rst | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/doc/devel/MEP/MEP21.rst b/doc/devel/MEP/MEP21.rst index a5c2663e070c..84744e7d6706 100644 --- a/doc/devel/MEP/MEP21.rst +++ b/doc/devel/MEP/MEP21.rst @@ -1,6 +1,7 @@ ============================== MEP21: color and cm refactor ============================== + .. contents:: :local: @@ -22,16 +23,21 @@ Abstract ======== - * color - * tidy up the namespace - * Define a "Color" class - * make it easy to convert from one color type to another ```hex -> RGB```, ```RGB -> hex```, ```HSV -> RGB``` etc. - * improve the construction of a colormap - the dictionary approach is archaic and overly complex (though incredibly powerful) - * make it possible to interpolate between two or more color types - in different modes, especially useful for construction of - colormaps in HSV space for instance - * cm - * rename the module to something more descriptive - mappables? +* color + + * tidy up the namespace + * Define a "Color" class + * make it easy to convert from one color type to another ```hex -> + RGB```, ```RGB -> hex```, ```HSV -> RGB``` etc. + * improve the construction of a colormap - the dictionary approach + is archaic and overly complex (though incredibly powerful) + * make it possible to interpolate between two or more color types + in different modes, especially useful for construction of + colormaps in HSV space for instance + +* cm + + * rename the module to something more descriptive - mappables? Overall, there are a lot of improvements that can be made with From e680cd27246bd5394a34aa7c7f0516261fad41e5 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 29 Mar 2015 23:16:53 -0400 Subject: [PATCH 22/23] DOC : fix MEP22 markup --- doc/devel/MEP/MEP22.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/devel/MEP/MEP22.rst b/doc/devel/MEP/MEP22.rst index 91786104d9b7..a72ca0764e04 100644 --- a/doc/devel/MEP/MEP22.rst +++ b/doc/devel/MEP/MEP22.rst @@ -93,7 +93,7 @@ The following instance attributes are set at instantiation: * keypress associated with the Tool Keymap * Call to navigation.trigger_tool(name) * set_figure(self, figure): Set the figure and navigation attributes - * destroy(self, *args): Destroy the `Tool` graphical interface (if exists) + * ``destroy(self, *args)``: Destroy the `Tool` graphical interface (if exists) **Available Tools** * ToolQuit @@ -150,7 +150,7 @@ Public methods for **User use**: None * get_tool_keymap(self, name): Return a list of keys that are associated with the tool - * set_tool_keymap(self, name, *keys): Set the keys for the given tool + * set_tool_keymap(self, name, ``*keys``): Set the keys for the given tool * remove_tool(self, name): Removes tool from the navigation control. * add_tools(self, tools): Add multiple tools to `Navigation` * add_tool(self, name, tool, group=None, position=None): Add a tool From dac829312a4dc94a1ac63a3773e6e5be7f647fd2 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 30 Mar 2015 00:23:31 -0400 Subject: [PATCH 23/23] DOC : fix markup in MEP26 and MEP27 --- doc/devel/MEP/MEP26.rst | 6 +++--- doc/devel/MEP/MEP27.rst | 42 ++++++++++++++++++++--------------------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/doc/devel/MEP/MEP26.rst b/doc/devel/MEP/MEP26.rst index 01e539c74304..c282735d8a46 100644 --- a/doc/devel/MEP/MEP26.rst +++ b/doc/devel/MEP/MEP26.rst @@ -116,14 +116,14 @@ syntax :: selector {attribute: value;} -Each rule can have any number of `attribute`:`value` pairs, and a +Each rule can have any number of `attribute`: `value` pairs, and a stylesheet can have any number of rules. The initial syntax is designed only for `artist` primitives. It does not address the question of how to set properties on `container` types (whose properties may themselves be `artists` with settable properties), however, a future solution to this could simply be nested -`RuleSet`s +`RuleSet` s Selectors ~~~~~~~~~ @@ -221,7 +221,7 @@ Appendix ======== Matplotlib primitives -~~~~~~~~~~~~~~~~~~~~~ +--------------------- This will form the initial selectors which stylesheets can use. diff --git a/doc/devel/MEP/MEP27.rst b/doc/devel/MEP/MEP27.rst index 1995851d9147..57b0540a4c91 100644 --- a/doc/devel/MEP/MEP27.rst +++ b/doc/devel/MEP/MEP27.rst @@ -48,6 +48,7 @@ Two main places for generic code appear in the classes derived from ``FigureManagerBase`` and ``ShowBase``. 1. ``FigureManagerBase`` has **three** jobs at the moment: + 1. The documentation describes it as a *``Helper class for pyplot mode, wraps everything up into a neat bundle''* 2. But it doesn't just wrap the canvas and toolbar, it also does @@ -63,6 +64,7 @@ Two main places for generic code appear in the classes derived from 2. ``ShowBase`` has two jobs: + 1. It has the job of going through all figure managers registered in ``_pylab_helpers.Gcf`` and telling them to show themselves. 2. And secondly it has the job of performing the backend specific @@ -89,46 +91,42 @@ The description of this MEP gives us most of the solution: 3. Now that ``FigureManagerBase`` has no backend specifics in it, to rename it to ``FigureManager``, and move to a new file ``backend_managers.py`` noting that: - 1. This allows us to break up the conversion of backends into - separate PRs as we can keep the existing ``FigureManagerBase`` - class and its dependencies intact. - 2. and this also anticipates MEP22 where the new - ``NavigationBase`` has morphed into a backend independent - ``ToolManager``. + + 1. This allows us to break up the conversion of backends into + separate PRs as we can keep the existing ``FigureManagerBase`` + class and its dependencies intact. + 2. and this also anticipates MEP22 where the new + ``NavigationBase`` has morphed into a backend independent + ``ToolManager``. +--------------------------------------+------------------------------+---------------------+--------------------------------+ |FigureManagerBase(canvas, num) |FigureManager(figure, num) |``WindowBase(title)``|Notes | | | | | | +======================================+==============================+=====================+================================+ -|show |:arrow_right: |show | | +|show | |show | | +--------------------------------------+------------------------------+---------------------+--------------------------------+ |destroy |calls destroy on all |destroy | | | |components | | | +--------------------------------------+------------------------------+---------------------+--------------------------------+ |full_screen_toggle |handles logic |set_fullscreen | | +--------------------------------------+------------------------------+---------------------+--------------------------------+ -|resize |:arrow_right: |resize | | +|resize | |resize | | +--------------------------------------+------------------------------+---------------------+--------------------------------+ -|key_press |key_press |:no_entry: | | +|key_press |key_press | | | +--------------------------------------+------------------------------+---------------------+--------------------------------+ -|show_popup |show_poup |:no_entry: |Not used anywhere in mpl, and | +|show_popup |show_poup | |Not used anywhere in mpl, and | | | | |does nothing. | -| | | | | -| | | | | -| | | | | +--------------------------------------+------------------------------+---------------------+--------------------------------+ -|get_window_title |:arrow_right: |get_window_title | | +|get_window_title | |get_window_title | | +--------------------------------------+------------------------------+---------------------+--------------------------------+ -|set_window_title |:arrow_right: |set_window_title | | +|set_window_title | |set_window_title | | +--------------------------------------+------------------------------+---------------------+--------------------------------+ -|:no_entry: |_get_toolbar | |A common method to all | +| |_get_toolbar | |A common method to all | | | | |subclasses of FigureManagerBase | -| | | | | -| | | | | +--------------------------------------+------------------------------+---------------------+--------------------------------+ -|:no_entry: |:no_entry: |set_default_size | | +| | |set_default_size | | +--------------------------------------+------------------------------+---------------------+--------------------------------+ -|:no_entry: |:no_entry: |add_element_to_window| | +| | |add_element_to_window| | +--------------------------------------+------------------------------+---------------------+--------------------------------+ @@ -137,14 +135,14 @@ The description of this MEP gives us most of the solution: +==========+============+=============+ |mainloop |begin | | +----------+------------+-------------+ -|:no_entry:|end |Gets called | +| |end |Gets called | | | |automagically| | | |when no more | | | |instances of | | | |the subclass | | | |exist | +----------+------------+-------------+ -|__call__ |:no_entry: |Method moved | +|__call__ | |Method moved | | | |to | | | |Gcf.show_all | +----------+------------+-------------+