diff --git a/chaco/plot_component.py b/chaco/plot_component.py index 833af6c4e..bbb4aa6b7 100644 --- a/chaco/plot_component.py +++ b/chaco/plot_component.py @@ -46,12 +46,12 @@ class PlotComponent(Component): #: #: 1. 'background': Background image, shading #: 2. 'image': A special layer for plots that render as images. This is in - #: a separate layer since these plots must all render before non-image - #: plots. + #: a separate layer since these plots must all render before non-image + #: plots. #: 3. 'underlay': Axes and grids #: 4. 'plot': The main plot area itself #: 5. 'selection': Selected content are rendered above normal plot elements - #: to make them stand out + #: to make them stand out #: 6. 'border': Plot borders #: 7. 'annotation': Lines and text that are conceptually part of the "plot" #: but need to be rendered on top of everything else in the plot diff --git a/docs/source/index.rst b/docs/source/index.rst index 0b93470dc..ebae76b28 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -6,7 +6,7 @@ visualizations. Chaco facilitates writing plotting applications at all levels of complexity, from simple scripts with hard-coded data to large plotting programs with complex data interrelationships and a multitude of interactive tools. While Chaco generates attractive static plots for publication and -presentation, Chaco differs from tools like MatPlotLib in that it also works +presentation, Chaco differs from tools like Matplotlib in that it also works well for dynamic interactive data visualization and exploration. Chaco is part of the Enthought Tool Suite. diff --git a/docs/source/user_manual/basic_elements/data_sources.rst b/docs/source/user_manual/basic_elements/data_sources.rst index 59f106506..274c7f64a 100644 --- a/docs/source/user_manual/basic_elements/data_sources.rst +++ b/docs/source/user_manual/basic_elements/data_sources.rst @@ -263,7 +263,7 @@ This is a list of all concrete implementations of data sources in Chaco: :attr:`~chaco.function_data_source.FunctionDataSource.data_range`). -:class:`~chaco.function_data_source.FunctionImageData` +:class:`~chaco.function_image_data.FunctionImageData` A subclass of :class:`~chaco.array_data_source.ImageData` that sets the values of the underlying data array based on a 2D function (defined in the callable attribute diff --git a/docs/source/user_manual/basic_elements/overlays.rst b/docs/source/user_manual/basic_elements/overlays.rst index 5008e1af5..89589de91 100644 --- a/docs/source/user_manual/basic_elements/overlays.rst +++ b/docs/source/user_manual/basic_elements/overlays.rst @@ -11,13 +11,13 @@ of their interface with :ref:`plot renderers ` In addition, they have a lightweight interface defined in :class:`chaco.abstract_overlay.AbstractOverlay`: the additional -features are that 1) they keep a reference to the plot they are decorating in -:attr:`~chaco.abstract_overlay.AbstractOverlay.component`; -2) the background color -:attr:`~chaco.abstract_overlay.AbstractOverlay.bgcolor` -is 'transparent' by default; -3) they plot :ref:`on the 'overlay' layer ` by default. +features are that +1. they keep a reference to the plot they are decorating in + :attr:`~chaco.abstract_overlay.AbstractOverlay.component`; +2. the background color :attr:`~chaco.abstract_overlay.AbstractOverlay.bgcolor` + is 'transparent' by default; +3. they plot :ref:`on the 'overlay' layer ` by default. .. TODO: explain how to attach an overlay to an existing plot renderer @@ -49,11 +49,13 @@ given an X-Y plot renderer, ``plot``, we can define a new x-axis as: :: 'title_font': 'modern 20', } - x_axis = PlotAxis(orientation='bottom', - title='My x axis', - mapper=plot.x_mapper, - component=plot, - **AXIS_DEFAULTS) + x_axis = PlotAxis( + orientation='bottom', + title='My x axis', + mapper=plot.x_mapper, + component=plot, + **AXIS_DEFAULTS, + ) The newly created axis can then be attached to the plot renderer by appending it to its underlays layer: :: @@ -71,15 +73,19 @@ A :class:`~chaco.axis.MinorPlotAxis` should be added along with a :class:`~chaco.axis.PlotAxis`. For example, minor axis tick marks can be added with: :: - x_major_axis = PlotAxis(orientation='bottom', - title='My x axis', - mapper=plot.x_mapper, - component=plot) + x_major_axis = PlotAxis( + orientation='bottom', + title='My x axis', + mapper=plot.x_mapper, + component=plot, + ) plot.underlays.append(x_major_axis) - x_minor_axis = MinorPlotAxis(orientation='bottom', - mapper=plot.x_mapper, - component=plot) + x_minor_axis = MinorPlotAxis( + orientation='bottom', + mapper=plot.x_mapper, + component=plot, + ) plot.underlays.append(x_minor_axis) Attributes @@ -178,13 +184,10 @@ designed to draw a text box over the plots to display custom information. The rendering of the text can be customized with the following attributes: - * :attr:`bgcolor` and :attr:`border_visible` to control the styling of the - box, - * :attr:`alpha` to control the transparency of the text box, - * :attr:`text_color` and :attr:`font` to control how the text looks like, - * :attr:`align` to control what corner of the plot the text box should - appear, - * ... +* :attr:`bgcolor` and :attr:`border_visible` to control the styling of the box, +* :attr:`alpha` to control the transparency of the text box, +* :attr:`text_color` and :attr:`font` to control how the text looks like, +* :attr:`align` to control what corner of the plot the text box should appear, .. note:: The overlay can also be used directly by any custom tool that needs to display information upon an event. It should be done by diff --git a/docs/source/user_manual/basic_elements/plot_renderers.rst b/docs/source/user_manual/basic_elements/plot_renderers.rst index 784cf3ee8..fc6428271 100644 --- a/docs/source/user_manual/basic_elements/plot_renderers.rst +++ b/docs/source/user_manual/basic_elements/plot_renderers.rst @@ -168,19 +168,16 @@ of the names of the layers. The definition of the layers is as follows: 4. **plot**: The main plot area itself -5. **annotation**: Lines and text that are conceptually part of the "plot" but - need to be rendered on top of everything else in the plot. - -6. **selection**: Selected content are rendered above normal plot elements to +5. **selection**: Selected content are rendered above normal plot elements to make them stand out. This can be disabled by setting :attr:`use_selection` to False (default). -7. **border**: Plot borders +6. **border**: Plot borders -8. **annotation**: Lines and text that are conceptually part of the "plot" but +7. **annotation**: Lines and text that are conceptually part of the "plot" but need to be rendered on top of everything else in the plot -9. **overlay**: Legends, selection regions, and other tool-drawn visual +8. **overlay**: Legends, selection regions, and other tool-drawn visual elements Concrete plot renderers set their default draw layer in diff --git a/docs/source/user_manual/basic_elements/tools.rst b/docs/source/user_manual/basic_elements/tools.rst index edc0ad356..ddf7b5657 100644 --- a/docs/source/user_manual/basic_elements/tools.rst +++ b/docs/source/user_manual/basic_elements/tools.rst @@ -75,9 +75,9 @@ and the overlay catches these events and displays the data as an overlay. To use it, as for other tools, you need to: - 1. create a tool object and append it to the **renderer**'s list of tools, - 2. create an overlay object and append it to the **renderer**'s list of - overlays. +1. create a tool object and append it to the **renderer**'s list of tools, +2. create an overlay object and append it to the **renderer**'s list of + overlays. For example, a method to build a :class:`chaco.api.Plot` object with that tool could look like:: @@ -95,9 +95,12 @@ will need it. The tool code to be inserted would look something like this:: imgtool = ImageInspectorTool(component=img_plot) img_plot.tools.append(imgtool) - overlay = ImageInspectorOverlay(component=img_plot, image_inspector=imgtool, - bgcolor="white", border_visible=True) - + overlay = ImageInspectorOverlay( + component=img_plot, + image_inspector=imgtool, + bgcolor="white", + border_visible=True + ) img_plot.overlays.append(overlay) Note the two important connections that are made for the tool/overlay to work diff --git a/docs/source/user_manual/chaco_tutorial.rst b/docs/source/user_manual/chaco_tutorial.rst index b0f019542..48724cdab 100644 --- a/docs/source/user_manual/chaco_tutorial.rst +++ b/docs/source/user_manual/chaco_tutorial.rst @@ -162,8 +162,12 @@ things, as you will see later on:: plot = Instance(Plot) traits_view = View( - Item('plot',editor=ComponentEditor(), show_label=False), - width=500, height=500, resizable=True, title="Chaco Plot") + Item('plot', editor=ComponentEditor(), show_label=False), + width=500, + height=500, + resizable=True, + title="Chaco Plot", + ) def _plot_default(self): x = linspace(-14, 14, 100) @@ -204,10 +208,14 @@ from :class:`HasTraits`. Next, we declare a Traits UI View for this class:: traits_view = View( - Item('plot',editor=ComponentEditor(), show_label=False), - width=500, height=500, resizable=True, title="Chaco Plot") - -Inside this view, we are placing a reference to the :attr:`plot` trait and + Item('plot',editor=ComponentEditor(), show_label=False), + width=500, + height=500, + resizable=True, + title="Chaco Plot", + ) + +Inside this view, we are placing a reference to the ``plot`` trait and telling Traits UI to use the :class:`ComponentEditor` (imported from :mod:`enable.api`) to display it. If the trait were an Int or Str or Float, Traits could automatically pick an @@ -435,7 +443,7 @@ Horizontal containers (:class:`HPlotContainer`) place components horizontally: .. image:: images/hplotcontainer.png :height: 350pt -Vertical containers (:class:`VPlotContainer`) array component vertically: +Vertical containers (:class:`VPlotContainer`) place components vertically: .. image:: images/vplotcontainer.png :height: 350pt @@ -478,8 +486,13 @@ plot, and adds them both to the HPlotContainer object:: plot = Instance(HPlotContainer) - traits_view = View(Item('plot', editor=ComponentEditor(), show_label=False), - width=1000, height=600, resizable=True, title="Chaco Plot") + traits_view = View( + Item('plot', editor=ComponentEditor(), show_label=False), + width=1000, + height=600, + resizable=True, + title="Chaco Plot", + ) def _plot_default(self): x = linspace(-14, 14, 100) @@ -506,9 +519,9 @@ This produces the following plot: There are many parameters you can configure on a container, like background -color, border thickness, spacing, and padding. We insert some more -lines between lines 25 and 26 of the previous example to make the two plots -touch in the middle: +color, border thickness, spacing, and padding. We add additional code between +creating the ``HPlotContainer`` instance and returning the container to make +the two plots touch in the middle: .. code-block:: python @@ -561,12 +574,18 @@ of these capabilities. Here is the full listing of the modified code:: marker_size = Int(4) traits_view = View( - Group(Item('color', label="Color", style="custom"), - Item('marker', label="Marker"), - Item('marker_size', label="Size"), - Item('plot', editor=ComponentEditor(), show_label=False), - orientation = "vertical"), - width=800, height=600, resizable=True, title="Chaco Plot") + Group( + Item('color', label="Color", style="custom"), + Item('marker', label="Marker"), + Item('marker_size', label="Size"), + Item('plot', editor=ComponentEditor(), show_label=False), + orientation = "vertical", + ), + width=800, + height=600, + resizable=True, + title="Chaco Plot", + ) def _plot_default(self): x = linspace(-14, 14, 100) @@ -606,15 +625,19 @@ new traits. We put them in a Traits UI :class:`Group` so that we can control the layout in the dialog a little better --- here, we're setting the layout orientation of the elements in the dialog to "vertical". :: - traits_view = View( - Group( - Item('color', label="Color", style="custom"), - Item('marker', label="Marker"), - Item('marker_size', label="Size"), - Item('plot', editor=ComponentEditor(), show_label=False), - orientation = "vertical" ), - width=500, height=500, resizable=True, - title="Chaco Plot") + traits_view = View( + Group( + Item('color', label="Color", style="custom"), + Item('marker', label="Marker"), + Item('marker_size', label="Size"), + Item('plot', editor=ComponentEditor(), show_label=False), + orientation = "vertical", + ), + width=800, + height=600, + resizable=True, + title="Chaco Plot", + ) Now we have to do something with those traits. We modify the constructor so that we grab a handle to the renderer that is created by @@ -692,18 +715,23 @@ the modifications: :: traits_view = View( Item('data_name', label="Y data"), Item('plot', editor=ComponentEditor(), show_label=False), - width=800, height=600, resizable=True, - title="Data Chooser") + width=800, + height=600, + resizable=True, + title="Data Chooser", + ) def _plot_default(self): x = linspace(-5, 10, 100) # jn is the Bessel function or order n - self.data = {"jn0": jn(0, x), - "jn1": jn(1, x), - "jn2": jn(2, x)} + self.data = { + "jn0": jn(0, x), + "jn1": jn(1, x), + "jn2": jn(2, x), + } - self.plotdata = ArrayPlotData(x = x, y = self.data["jn0"]) + self.plotdata = ArrayPlotData(x=x, y=self.data["jn0"]) plot = Plot(self.plotdata) plot.plot(("x", "y"), type="line", color="blue") @@ -727,10 +755,12 @@ By default, an ``Enum`` trait will be displayed as a drop-down. In the constructor, we create a dictionary that maps the data names to actual numpy arrays:: - # jn is the Bessel function - self.data = {"jn0": jn(0, x), - "jn1": jn(1, x), - "jn2": jn(2, x)} + # jn is the Bessel function of order n + self.data = { + "jn0": jn(0, x), + "jn1": jn(1, x), + "jn2": jn(2, x), + } When we initialize the ArrayPlotData, we’ll set ``y`` to the ``jn0`` array:: @@ -780,10 +810,13 @@ space region. This is the full code:: container = Instance(HPlotContainer) - traits_view = View(Item('container', editor=ComponentEditor(), - show_label=False), - width=1000, height=600, resizable=True, - title="Connected Range") + traits_view = View( + Item('container', editor=ComponentEditor(), show_label=False), + width=1000, + height=600, + resizable=True, + title="Connected Range", + ) def _container_default(self): x = linspace(-14, 14, 100) @@ -815,11 +848,13 @@ to side:: container = Instance(HPlotContainer) - traits_view = View(Item('container', editor=ComponentEditor(), - show_label=False), - width=1000, height=600, resizable=True, - title="Connected Range") - + traits_view = View( + Item('container', editor=ComponentEditor(), show_label=False), + width=1000, + height=600, + resizable=True, + title="Connected Range", + ) In the constructor, we define some data and create two plots of it, a line plot and a scatter plot, insert them in the container, and add @@ -916,11 +951,14 @@ This is the full code that we will analyze step by step below :: orientation = Enum("horizontal", "vertical") - traits_view = View(Item('orientation', label="Orientation"), - Item('plot', editor=ComponentEditor(), - show_label=False), - width=500, height=500, resizable=True, - title="Chaco Plot") + traits_view = View( + Item('orientation', label="Orientation"), + Item('plot', editor=ComponentEditor(), show_label=False), + width=500, + height=500, + resizable=True, + title="Chaco Plot", + ) def _plot_default(self): x = linspace(-14, 14, 100) @@ -941,7 +979,7 @@ This is the full code that we will analyze step by step below :: self.plot.orientation = "h" -The plot defines two traits, one for the plot type (scatter of line plot) :: +The plot defines two traits, one for the plot type (scatter or line plot) :: plot_type = Enum("scatter", "line") @@ -1011,7 +1049,7 @@ Plot tools: adding interactions An important feature of Chaco is that it is possible to write re-usable tools to interact directly with the plots. -Chaco takes a modular approach to interactivity. Instead of begin hard-coded +Chaco takes a modular approach to interactivity. Instead of being hard-coded into specific plot types or plot renderers, the interaction logic is factored out into classes we call *tools*. An advantage of this approach is that we can add new plot types @@ -1035,9 +1073,11 @@ the :ref:`LinePlot example ` so that we can pan and zoom. :: traits_view = View( Item('plot',editor=ComponentEditor(), show_label=False), - width=500, height=500, + width=500, + height=500, resizable=True, - title="Chaco Plot") + title="Chaco Plot", + ) def _plot_default(self): x = linspace(-14, 14, 100) @@ -1099,8 +1139,11 @@ strings, and not the tool classes themselves. plot = Instance(Plot) - tools = List(editor=CheckListEditor(values = ["PanTool", - "SimpleZoom", "DragZoom"])) + tools = List( + editor=CheckListEditor( + values = ["PanTool", "SimpleZoom", "DragZoom"], + ) + ) In the constructor, we do not add the interactive tools: @@ -1138,12 +1181,12 @@ The first line, :: classes = [eval(class_name) for class_name in self.tools] converts the value of the ``tools`` trait (a string) to a Tool class. In the -of the method, we remove all the existing tools from the plot :: +next part of the method, we remove all the existing tools from the plot :: # Remove all tools from the plot plot_tools = self.plot.tools for tool in plot_tools: - plot_tools.remove(tool) + self.plot.tools.remove(tool) and create new ones for the selected items: :: diff --git a/docs/source/user_manual/containers.rst b/docs/source/user_manual/containers.rst index 7d0b1f06b..66b965606 100644 --- a/docs/source/user_manual/containers.rst +++ b/docs/source/user_manual/containers.rst @@ -54,8 +54,9 @@ E.g., this code:: # Create a vertical container containing two horizontal containers h_container1 = HPlotContainer() h_container2 = HPlotContainer() - outer_container = VPlotContainer(h_container1, h_container2, - stack_order="top_to_bottom") + outer_container = VPlotContainer( + h_container1, h_container2, stack_order="top_to_bottom" + ) # Add the three plots to the first container h_container1.add(scatter_plot, line_plot1, line_plot2) @@ -120,17 +121,20 @@ horizontal container:: # Create the plot plot = Plot(data) - plot.plot(("index", "value", "color"), type="cmap_scatter", - color_mapper=jet) + plot.plot( + ("index", "value", "color"), type="cmap_scatter", color_mapper=jet + ) # Create the colorbar, handing in the appropriate range and colormap colormap = plot.color_mapper - colorbar = ColorBar(index_mapper=LinearMapper(range=colormap.range), - color_mapper=colormap, - orientation='v', - resizable='v', - width=30, - padding=20) + colorbar = ColorBar( + index_mapper=LinearMapper(range=colormap.range), + color_mapper=colormap, + orientation='v', + resizable='v', + width=30, + padding=20, + ) colorbar.padding_top = plot.padding_top colorbar.padding_bottom = plot.padding_bottom @@ -226,15 +230,19 @@ The complete code looks like this: traits_view = View( Item('plot', editor=ComponentEditor(), show_label=False), - width=1000, height=600, resizable=True + width=1000, + height=600, + resizable=True, ) def _plot_default(self): # Create a GridContainer to hold all of our plots: 2 rows, 3 columns - container = GridPlotContainer(shape=(2,3), - spacing=(10,5), - valign='top', - bgcolor='lightgray') + container = GridPlotContainer( + shape=(2,3), + spacing=(10,5), + valign='top', + bgcolor='lightgray', + ) # Create x data x = linspace(-5, 15.0, 100) @@ -246,13 +254,14 @@ The complete code looks like this: pd.set_data(data_name, jn(i,x)) plot = Plot(pd) - plot.plot(('index', data_name), - color=COLOR_PALETTE[i], - line_width=3.0) + plot.plot( + ('index', data_name), + color=COLOR_PALETTE[i], + line_width=3.0, + ) # Set each plot's aspect based on its position in the grid - plot.set(height=((i % 3) + 1)*50, - resizable='h') + plot.set(height=((i % 3) + 1)*50, resizable='h') # Add to the grid container container.add(plot) @@ -310,7 +319,9 @@ full data: :: traits_view = View( Item('plot', editor=ComponentEditor(), show_label=False), - width=800, height=600, resizable=True + width=800, + height=600, + resizable=True, ) def _plot_default(self): @@ -320,23 +331,31 @@ full data: :: pd = ArrayPlotData(index=x, value=y) zoomable_plot = Plot(pd) - zoomable_plot.plot(('index', 'value'), - name='external', color='red', line_width=3) + zoomable_plot.plot( + ('index', 'value'), + name='external', + color='red', + line_width=3, + ) # Attach tools to the plot - zoom = ZoomTool(component=zoomable_plot, - tool_mode="box", always_on=False) + zoom = ZoomTool( + component=zoomable_plot, + tool_mode="box", + always_on=False, + ) zoomable_plot.overlays.append(zoom) zoomable_plot.tools.append(PanTool(zoomable_plot)) # Create a second inset plot, not resizable, not zoom-able inset_plot = Plot(pd) inset_plot.plot(('index', 'value'), color='blue') - inset_plot.set(resizable = '', - bounds = [250, 150], - position = [450, 350], - border_visible = True - ) + inset_plot.set( + resizable='', + bounds=[250, 150], + position=[450, 350], + border_visible=True, + ) # Create a container and add our plots container = OverlayPlotContainer() @@ -434,16 +453,26 @@ Rendering order Every plot component has several layers: -1. :attr:`background`: Background image, shading, and borders -2. :attr:`underlay`: Axes and grids -3. :attr:`image`: A special layer for plots that render as images. This is in - a separate layer since these plots must all render before non-image - plots. -4. :attr:`plot`: The main plot area -5. :attr:`annotation`: Lines and text that are conceptually part of the "plot" but - need to be rendered on top of everything else in the plot. -6. :attr:`overlay`: Legends, selection regions, and other tool-drawn visual - elements +1. **background**: Background image, shading, and borders + +2. **image**: A special layer for plots that render as images. This is in a + separate layer since these plots must all render before non-image plots + +3. **underlay**: Axes and grids + +4. **plot**: The main plot area itself + +5. **selection**: Selected content are rendered above normal plot elements to + make them stand out. This can be disabled by setting :attr:`use_selection` + to False (default). + +6. **border**: Plot borders + +7. **annotation**: Lines and text that are conceptually part of the "plot" but + need to be rendered on top of everything else in the plot + +8. **overlay**: Legends, selection regions, and other tool-drawn visual + elements These are defined by :attr:`~chaco.plot_component.DEFAULT_DRAWING_ORDER`, and stored in the :attr:`drawing_order` trait. diff --git a/docs/source/user_manual/faq.rst b/docs/source/user_manual/faq.rst index 4e8c25261..6b198bf71 100644 --- a/docs/source/user_manual/faq.rst +++ b/docs/source/user_manual/faq.rst @@ -11,7 +11,7 @@ Where does the name "Chaco" come from? It is named after `Chaco Canyon `_, which had astronomical markings that served as an observatory for Native Americans. The original version of Chaco was built as part of a project for the `Space -Telescope Science Institute `_. This is also +Telescope Science Institute `_. This is also the origin of the name "Kiva" for our vector graphics layer that Chaco uses for rendering. diff --git a/docs/source/user_manual/plot_types.rst b/docs/source/user_manual/plot_types.rst index 00102a695..2ef803529 100644 --- a/docs/source/user_manual/plot_types.rst +++ b/docs/source/user_manual/plot_types.rst @@ -381,8 +381,12 @@ The preferred way to do this is using the factory method w, h = image_source.get_width(), image_source.get_height() index = GridDataSource(np.arange(w), np.arange(h)) - index_mapper = GridMapper(range=DataRange2D(low=(0, 0), - high=(w-1, h-1))) + index_mapper = GridMapper( + range=DataRange2D( + low=(0, 0), + high=(w-1, h-1), + ) + ) image_plot = ImagePlot( index=index, diff --git a/docs/source/user_manual/quickstart.rst b/docs/source/user_manual/quickstart.rst index cbd830375..3593e25e8 100644 --- a/docs/source/user_manual/quickstart.rst +++ b/docs/source/user_manual/quickstart.rst @@ -180,7 +180,7 @@ Chaco has been written for. First, some imports to bring in necessary components:: from chaco.api import ArrayPlotData, Plot - from enable.component_editor import ComponentEditor + from enable.api import ComponentEditor from traits.api import HasTraits, Instance from traitsui.api import View, Item diff --git a/docs/source/user_manual/tutorial_hyetograph.rst b/docs/source/user_manual/tutorial_hyetograph.rst index b357e2064..09850fbe5 100644 --- a/docs/source/user_manual/tutorial_hyetograph.rst +++ b/docs/source/user_manual/tutorial_hyetograph.rst @@ -27,11 +27,11 @@ the soil) a plot shows the intensity vs. time hyetograph plots. Development Setup ================= -To run this demo you must have Chaco and its dependencies installed, +To run this demo you must have Chaco and its dependencies installed: - * Traits - * TraitsGUI - * Enable +* Traits +* TraitsUI +* Enable Why use Traits for this application? @@ -60,8 +60,14 @@ In calling your function you want to specify where the function is and then import it. The following code snippet imports all the names that will be used for our application. :: - from traits.api \ - import HasTraits, Int, Range, Array, Enum, on_trait_change + from traits.api import ( + HasTraits, + Int, + Range, + Array, + Enum, + on_trait_change, + ) from traitsui.api import View, Item from chaco.chaco_plot_editor import ChacoPlotItem @@ -71,45 +77,52 @@ Trait Definitions This application only requires one class that will contain the Traits and mathematical calculations together. Classes that contain Traits -must inherit from the HasTraits function. Python's multiple +must inherit from the HasTraits class. Python's multiple inheritance allows for mixing HasTraits objects with other class hierarchies if needed. -Within this class we define all the class variables using Traits types +Within this class we define all the variables using Traits types which will later be used in the UI. These traits are set to equal their type similar to many typed languages. :: class Hyetograph(HasTraits): """ Creates a simple hyetograph demo. """ + timeline = Array + intensity = Array + nrcs = Array + duration = Int(12, desc='In Hours') + year_storm = Enum(2, 10, 25, 100) + county = Enum('Brazos', 'Dallas', 'El Paso', 'Harris') + curve_number = Range(70, 100) + plot_type = Enum('line', 'scatter') - The above code snippet shows a number of Traits features, - 1. The naming convention with traits is that types are capitalized. +1. The naming convention with traits is that types are capitalized. - 2. An Array is an array, an Int is an integer, an Enum is a single - value from a list of options, and a Range is a value between - two numbers. - - 3. All traits get a default value, such as whats done in the - Arrays, or they can be assigned an initial value as is done in - the duration trait. - - 4. Descriptions can be added to traits, such as is done in - duration. This description is not visible except when viewing - the trait in a TraitsUI view, and then the description is seen - when the mouse hovers over the variable. - - 5. Traits are always contained within the class definition, and - each instance of the class will have a unique copy of the traits. +2. An Array is an array, an Int is an integer, an Enum is a single + value from a list of options, and a Range is a value between + two numbers. + +3. All traits get a default value, such as whats done in the + Arrays, or they can be assigned an initial value as is done in + the duration trait. + +4. Descriptions can be added to traits, such as is done in + duration. This description is not visible except when viewing + the trait in a TraitsUI view, and then the description is seen + when the mouse hovers over the variable. + +5. Traits are always contained within the class definition, and + each instance of the class will have a unique copy of the traits. The Traits API Reference contains more information about the standard Trait types; see the :mod:`trait_types` module in the `Traits API Reference @@ -131,37 +144,45 @@ Continuing with our application, here is the View definition. :: class Hyetograph(HasTraits): - <... snip ...> - - view1 = View(Item('plot_type'), - ChacoPlotItem('timeline', 'intensity', - type_trait='plot_type', - resizable=True, - x_label='Time (hr)', - y_label='Intensity (in/hr)', - color='blue', - bgcolor='white', - border_visible=True, - border_width=1, - padding_bg_color='lightgray'), - Item(name='duration'), - Item(name='year_storm'), - Item(name='county'), - - # After infiltration using the nrcs curve number method. - ChacoPlotItem('timeline', 'nrcs', - type_trait='plot_type', - resizable=True, - x_label='Time', - y_label='Intensity', - color='blue', - bgcolor='white', - border_visible=True, - border_width=1, - padding_bg_color='lightgray'), - Item('curve_number'), - resizable = True, - width=800, height=800) + ... + + view = View( + Item('plot_type'), + ChacoPlotItem( + 'timeline', + 'intensity', + type_trait='plot_type', + resizable=True, + x_label='Time (hr)', + y_label='Intensity (in/hr)', + color='blue', + bgcolor='white', + border_visible=True, + border_width=1, + padding_bg_color='lightgray' + ), + Item(name='duration'), + Item(name='year_storm'), + Item(name='county'), + # After infiltration using the nrcs curve number method. + ChacoPlotItem( + 'timeline', + 'nrcs', + type_trait='plot_type', + resizable=True, + x_label='Time', + y_label='Intensity', + color='blue', + bgcolor='white', + border_visible=True, + border_width=1, + padding_bg_color='lightgray' + ), + Item('curve_number'), + resizable = True, + width=800, + height=800, + ) Views generally contain Item objects and named parameters. Views can @@ -213,14 +234,14 @@ Array traits. :: self.timeline=range(2, self.duration + 1, 2) intensity=a / (self.timeline * 60 + b)**c - cumdepth=intensity * self.timeline + cumulative_depth=intensity * self.timeline - temp=cumdepth[0] + temp=cumulative_depth[0] result=[] - for i in cumdepth[1:]: + for i in cumulative_depth[1:]: result.append(i-temp) temp=i - result.insert(0,cumdepth[0]) + result.insert(0,cumulative_depth[0]) # Alternating block method implementation. result.reverse() @@ -275,13 +296,13 @@ any of the values within the list of traits change. :: self.calculate_runoff() So now when the application is run, when the ``duration`` trait is - changed or any of the four listed traits change, the calculation - functions are automatically called and the data changes. And these - traits will automatically change when the user adjusts the widgets - in the UI. So when the user changes the ``duration`` in the UI - from 12 hours to 24 hours this will automatically effect both of - the plots since the listeners force a recalculation of both of the - functions. +changed or any of the four listed traits change, the calculation +functions are automatically called and the data changes. And these +traits will automatically change when the user adjusts the widgets +in the UI. So when the user changes the ``duration`` in the UI +from 12 hours to 24 hours this will automatically effect both of +the plots since the listeners force a recalculation of both of the +functions. Showing the Display @@ -297,8 +318,9 @@ to initialize the data arrays. Here's the last piece of the program. :: self.configure_traits() - f=Hyetograph() - f.start() + if __name__ == "__main__": + hyetograph=Hyetograph() + hyetograph.start() start() performs the calculations needed for the Arrays used to plot, and then triggers the UI. The application is complete, and if you now @@ -315,79 +337,100 @@ Source Code The final version of the program, `hyetograph.py`. :: - from traits.api \ - import HasTraits, Int, Range, Array, Enum, on_trait_change + from traits.api import ( + HasTraits, + Int, + Range, + Array, + Enum, + on_trait_change, + ) from traitsui.api import View, Item from chaco.chaco_plot_editor import ChacoPlotItem - - + + COUNTIES = {'Brazos': 0, 'Dallas': 3, 'El Paso': 6, 'Harris': 9} + YEARS = { + 2 : [65, 8, .806, 54, 8.3, .791, 24, 9.5, .797, 68, 7.9, .800], + 10: [80, 8.5, .763, 78, 8.7, .777, 42, 12., .795,81, 7.7, .753], + 25: [89, 8.5, .754, 90, 8.7, .774, 60, 12.,.843, 81, 7.7, .724], + 100: [96, 8., .730, 106, 8.3, .762, 65, 9.5, .825, 91, 7.9, .706] + } + class Hyetograph(HasTraits): """ Creates a simple hyetograph demo. """ + timeline = Array + intensity = Array + nrcs = Array + duration = Int(12, desc='In Hours') + year_storm = Enum(2, 10, 25, 100) + county = Enum('Brazos', 'Dallas', 'El Paso', 'Harris') + curve_number = Range(70, 100) + plot_type = Enum('line', 'scatter') - - view1 = View(Item('plot_type'), - ChacoPlotItem('timeline', 'intensity', - type_trait='plot_type', - resizable=True, - x_label='Time (hr)', - y_label='Intensity (in/hr)', - color='blue', - bgcolor='white', - border_visible=True, - border_width=1, - padding_bg_color='lightgray'), - Item(name='duration'), - Item(name='year_storm'), - Item(name='county'), - - # After infiltration using the nrcs curve number method. - ChacoPlotItem('timeline', 'nrcs', - type_trait='plot_type', - resizable=True, - x_label='Time', - y_label='Intensity', - color='blue', - bgcolor='white', - border_visible=True, - border_width=1, - padding_bg_color='lightgray'), - Item('curve_number'), - resizable = True, - width=800, height=800) - - + + view1 = View( + Item('plot_type'), + ChacoPlotItem( + 'timeline', + 'intensity', + type_trait='plot_type', + resizable=True, + x_label='Time (hr)', + y_label='Intensity (in/hr)', + color='blue', + bgcolor='white', + border_visible=True, + border_width=1, + padding_bg_color='lightgray', + ), + Item(name='duration'), + Item(name='year_storm'), + Item(name='county'), + # After infiltration using the nrcs curve number method. + ChacoPlotItem( + 'timeline', + 'nrcs', + type_trait='plot_type', + resizable=True, + x_label='Time', + y_label='Intensity', + color='blue', + bgcolor='white', + border_visible=True, + border_width=1, + padding_bg_color='lightgray', + ), + Item('curve_number'), + resizable=True, + width=800, + height=800, + ) + def calculate_intensity(self): """ The Hyetograph calculations. """ # Assigning A, B, and C values based on year, storm, and county - counties = {'Brazos': 0, 'Dallas': 3, 'El Paso': 6, 'Harris': 9} - years = { - 2 : [65, 8, .806, 54, 8.3, .791, 24, 9.5, .797, 68, 7.9, .800], - 10: [80, 8.5, .763, 78, 8.7, .777, 42, 12., .795,81, 7.7, .753], - 25: [89, 8.5, .754, 90, 8.7, .774, 60, 12.,.843, 81, 7.7, .724], - 100: [96, 8., .730, 106, 8.3, .762, 65, 9.5, .825, 91, 7.9, .706] - } - year = years[self.year_storm] - value = counties[self.county] + year = YEARS[self.year_storm] + value = COUNTIES[self.county] a, b, c = year[value], year[value+1], year[value+2] - + self.timeline=range(2, self.duration + 1, 2) intensity=a / (self.timeline * 60 + b)**c - cumdepth=intensity * self.timeline - - temp=cumdepth[0] + cumulative_depth=intensity * self.timeline + + temp=cumulative_depth[0] result=[] - for i in cumdepth[1:]: + for i in cumulative_depth[1:]: result.append(i-temp) temp=i - result.insert(0,cumdepth[0]) - + result.insert(0,cumulative_depth[0]) + # Alternating block method implementation. result.reverse() switch = True @@ -401,8 +444,7 @@ The final version of the program, `hyetograph.py`. :: e.reverse() result = o + e self.intensity = result - - + def calculate_runoff(self): """ NRCS method to get run-off based on permeability of ground. """ s = (1000 / self.curve_number) - 10 @@ -413,18 +455,17 @@ The final version of the program, `hyetograph.py`. :: if a[i] <= 0: vr[i] = 0 self.nrcs = vr - - + @on_trait_change('duration, year_storm, county, curve_number') def _perform_calculations(self): self.calculate_intensity() self.calculate_runoff() - - + def start(self): self._perform_calculations() self.configure_traits() - - - f=Hyetograph() - f.start() + + + if __name__ == "__main__": + hyetograph=Hyetograph() + hyetograph.start() diff --git a/docs/source/user_manual/tutorial_van_der_waal.rst b/docs/source/user_manual/tutorial_van_der_waal.rst index 52b2a65c0..1ab315343 100644 --- a/docs/source/user_manual/tutorial_van_der_waal.rst +++ b/docs/source/user_manual/tutorial_van_der_waal.rst @@ -10,7 +10,7 @@ Overview ======== This tutorial walks through the creation of an example program that plots a -scientific equation. In particular, we will model `Van Der Waal's Equation +scientific equation. In particular, we will model `Van der Waal's Equation `_, which is a modification to the ideal gas law that takes into account the nonzero size of molecules and the attraction to each other that they experience. @@ -28,41 +28,57 @@ resources for learning about the packages. You must have Chaco and its dependencies installed: * Traits -* TraitsGUI +* TraitsUI * Enable +i.e. `edm install chaco` should install the above dependencies. + Writing the Program =================== First, define a Traits class and the elements necessary need to model -the task. The following Traits class is made for the Van Der Waal +the task. The following Traits class is made for the Van der Waal equation, whose variables can be viewed on `this wiki page `_. The :attr:`volume` and :attr:`pressure` attributes hold lists of our X- and Y-coordinates, respectively, and are defined as arrays. The attributes -:attr:`attraction` and :attr:`totVolume` are input parameters specified by the +:attr:`attraction` and :attr:`tot_volume` are input parameters specified by the user. The type of the variables dictates their appearance in the GUI. For -example, :attr:`attraction` and :attr:`totVolume` are defined as Ranges, so they +example, :attr:`attraction` and :attr:`tot_volume` are defined as Ranges, so they show up as slider bars. Likewise, :attr:`plot_type` is shown as a drop-down list, since it is defined as an Enum. :: # We'll also import a few things to be used later. - from traits.api \ - import HasTraits, Array, Range, Float, Enum, on_trait_change, Property + from traits.api import ( + HasTraits, + Array, + Range, + Float, + Enum, + on_trait_change, + Property, + ) from traitsui.api import View, Item from chaco.chaco_plot_editor import ChacoPlotItem from numpy import arange class Data(HasTraits): + volume = Array + pressure = Array - attraction = Range(low=-50.0,high=50.0,value=0.0) - totVolume = Range(low=.01,high=100.0,value=0.01) + + attraction = Range(low=-50.0, high=50.0, value=0.0) + + tot_volume = Range(low=.01, high=100.0, value=0.01) + temperature = Range(low=-50.0,high=50.0,value=50.0) + r_constant= Float(8.314472) + plot_type = Enum("line", "scatter") .... @@ -83,36 +99,42 @@ embed a Chaco plot into a Traits View, you need to import the like the Item objects. The first two arguments to ChacoPlotItem are the lists of X- and Y-coordinates for the graph. The attributes :attr:`volume` and :attr:`pressure` hold the lists of X- and Y-coordinates, and therefore are the -first two arguments to Chaco2PlotItem. Other parameters have been +first two arguments to ChacoPlotItem. Other parameters have been provided to the plot for additional customization:: class Data(HasTraits): .... - traits_view = View(ChacoPlotItem("volume", "pressure", - type_trait="plot_type", - resizable=True, - x_label="Volume", - y_label="Pressure", - x_bounds=(-10,120), - x_auto=False, - y_bounds=(-2000,4000), - y_auto=False, - color="blue", - bgcolor="white", - border_visible=True, - border_width=1, - title='Pressure vs. Volume', - padding_bg_color="lightgray"), - Item(name='attraction'), - Item(name='totVolume'), - Item(name='temperature'), - Item(name='r_constant', style='readonly'), - Item(name='plot_type'), - resizable = True, - buttons = ["OK"], - title='Van der Waal Equation', - width=900, height=800) + traits_view = View( + ChacoPlotItem( + "volume", + "pressure", + type_trait="plot_type", + resizable=True, + x_label="Volume", + y_label="Pressure", + x_bounds=(-10,120), + x_auto=False, + y_bounds=(-2000,4000), + y_auto=False, + color="blue", + bgcolor="white", + border_visible=True, + border_width=1, + title='Pressure vs. Volume', + padding_bg_color="lightgray" + ), + Item(name='attraction'), + Item(name='tot_volume'), + Item(name='temperature'), + Item(name='r_constant', style='readonly'), + Item(name='plot_type'), + resizable=True, + buttons=["OK"], + title='Van der Waal Equation', + width=900, + height=800, + ) .... @@ -128,9 +150,9 @@ The :attr:`volume` attribute is the independent variable and :attr:`pressure` is the dependent variable. The relationship between pressure and volume, as derived from the equation found on the wiki page, is:: - r_constant * Temperature attraction - Pressure = ------------------------ - ---------- - Volume - totVolume Volume**2 + r_constant * temperature attraction + pressure = ------------------------ - ---------- + volume - tot_volume volume**2 Next, there are two programing tasks to complete: @@ -144,20 +166,21 @@ Next, there are two programing tasks to complete: The following is the code for these two needs:: - # Re-calculate when attraction, totVolume, or temperature are changed. - @on_trait_change('attraction, totVolume, temperature') + # Re-calculate when attraction, tot_volume, or temperature are changed. + @on_trait_change('attraction, tot_volume, temperature') def calc(self): """ Update the data based on the numbers specified by the user. """ self.volume = arange(.1, 100) - self.pressure = ((self.r_constant*self.temperature) - /(self.volume - self.totVolume) - -(self.attraction/(self.volume*self.volume))) + self.pressure = ( + (self.r_constant*self.temperature)/(self.volume - self.tot_volume) + - self.attraction/(self.volume*self.volume) + ) The :func:`calc` function computes the :attr:`pressure` array using the current values of the independent variables. Meanwhile, the :func:`@on_trait_change` decorator (provided by Traits) tells Python to call :func:`calc` whenever any of the attributes :attr:`attraction`, -:attr:`totVolume`, or :attr:`temperature` changes. +:attr:`tot_volume`, or :attr:`temperature` changes. Testing your Program @@ -236,9 +259,9 @@ The new pieces of code to add to the Data class are:: class Data(HasTraits): ... - pressure = Property(Array, depends_on=['temperature', - 'attraction', - 'totVolume']) + pressure = Property( + Array, depends_on=['temperature', 'attraction', 'tot_volume'] + ) ... def _volume_default(self): @@ -247,9 +270,10 @@ The new pieces of code to add to the Data class are:: # Pressure is recalculated whenever one of the elements the property # depends on changes. No need to use @on_trait_change. def _get_pressure(self): - return ((self.r_constant*self.temperature) - /(self.volume - self.totVolume) - -(self.attraction/(self.volume*self.volume))) + return ( + (self.r_constant*self.temperature)/(self.volume - self.tot_volume) + -self.attraction/(self.volume*self.volume) + ) You now no longer have to call an inconvenient calculation function before the first call to :meth:`configure_traits`! @@ -260,47 +284,67 @@ Source Code The final version on the program, `vanderwaals.py` :: - from traits.api \ - import HasTraits, Array, Range, Float, Enum, on_trait_change, Property + from traits.api import ( + HasTraits, + Array, + Range, + Float, + Enum, + on_trait_change, + Property, + ) from traitsui.api import View, Item from chaco.chaco_plot_editor import ChacoPlotItem from numpy import arange class Data(HasTraits): + volume = Array - pressure = Property(Array, depends_on=['temperature', 'attraction', - 'totVolume']) + + pressure = Property( + Array, depends_on=['temperature', 'attraction', 'tot_volume'] + ) + attraction = Range(low=-50.0,high=50.0,value=0.0) - totVolume = Range(low=.01,high=100.0,value=0.01) + + tot_volume = Range(low=.01,high=100.0,value=0.01) + temperature = Range(low=-50.0,high=50.0,value=50.0) + r_constant= Float(8.314472) - plot_type = Enum("line", "scatter") - traits_view = View(ChacoPlotItem("volume", "pressure", - type_trait="plot_type", - resizable=True, - x_label="Volume", - y_label="Pressure", - x_bounds=(-10,120), - x_auto=False, - y_bounds=(-2000,4000), - y_auto=False, - color="blue", - bgcolor="white", - border_visible=True, - border_width=1, - title='Pressure vs. Volume', - padding_bg_color="lightgray"), - Item(name='attraction'), - Item(name='totVolume'), - Item(name='temperature'), - Item(name='r_constant', style='readonly'), - Item(name='plot_type'), - resizable = True, - buttons = ["OK"], - title='Van der Waal Equation', - width=900, height=800) + plot_type = Enum("line", "scatter") + traits_view = View( + ChacoPlotItem( + "volume", + "pressure", + type_trait="plot_type", + resizable=True, + x_label="Volume", + y_label="Pressure", + x_bounds=(-10,120), + x_auto=False, + y_bounds=(-2000,4000), + y_auto=False, + color="blue", + bgcolor="white", + border_visible=True, + border_width=1, + title='Pressure vs. Volume', + padding_bg_color="lightgray", + ), + Item(name='attraction'), + Item(name='tot_volume'), + Item(name='temperature'), + Item(name='r_constant', style='readonly'), + Item(name='plot_type'), + resizable=True, + buttons=["OK"], + title='Van der Waal Equation', + width=900, + height=800, + ) def _volume_default(self): """ Default handler for volume Trait Array. """ @@ -308,9 +352,11 @@ The final version on the program, `vanderwaals.py` :: def _get_pressure(self): """Recalculate when one a trait the property depends on changes.""" - return ((self.r_constant*self.temperature) - /(self.volume - self.totVolume) - -(self.attraction/(self.volume*self.volume))) + return ( + (self.r_constant*self.temperature)/(self.volume - self.tot_volume) + -self.attraction/(self.volume*self.volume) + ) + if __name__ == '__main__': viewer = Data()