Как стать автором
Обновить

Отрисовка графиков в Chaco

Время на прочтение43 мин
Количество просмотров5.2K
Сегодня расскажу вам о замечательной программе под названием Chaco, которую разрабатывает компания Enthought.

Chaco — это кроссплатформенное приложение по созданию графиков любой сложности на языке Python. Ориентируется на отрисовку статических данных, но имеет и возможности создания анимации.



Так же, как и Mayavi умеет встраиваться в Wx и Qt (PyQt и PySide) приложения, дружит с Numpy-массивами.


Установка

Первым делом Chaco надо установить. Ставим зависимости: git, subversion, setuptools, swig, numpy, scipy, vtk, wxpython. Для Windows также потребуется установить mingw (vtk и wxpython для Win советую взять отсюда для экономии времени www.lfd.uci.edu/~gohlke/pythonlibs ). Забираем с git продукты ETS (ненужное можно будет удалить):

mkdir ets && cd ets
wget github.com/enthought/ets/raw/master/ets.py
python ets.py clone


Затем все это дело собираем:
python ets.py develop


Возможно придется еще что-то доставить, тут нужно смотреть логи сборки. Чего на хватало, устанавливаем и запускаем сборку заново.

Примеры

В папке ets/chaco/examples можно будет увидеть большой архив различных примеров. Примеры очень хорошие, так что мне что-то объяснять довольно трудно, получится копипаст кода.

Опишу лишь некоторые необычные графики, которые можно строить в Chaco:

  • Финансовые:



    Данный пример использует встраивание в виджет PySide.

    # -*- coding: utf-8 -*_<br/>
     <br/>
    # Важное установить переменную QT_API равной pyside до того, <br/>
    # как происходит импорт модулей Chaco<br/>
    import os<br/>
    os.environ['QT_API'] = 'pyside'<br/>
    os.environ['ETS_TOOLKIT'] = 'qt4'<br/>
    from PySide import QtGui, QtCore<br/>
     <br/>
    from numpy import abs, arange, cumprod, random<br/>
    from enable.example_support import DemoFrame, demo_main<br/>
    from enable.api import Window, Component, ComponentEditor<br/>
    from traits.api import HasTraits, Instance<br/>
    from traitsui.api import Item, Group, View<br/>
    from chaco.api import ArrayDataSource, BarPlot, DataRange1D, \<br/>
            LinearMapper, VPlotContainer, PlotAxis, FilledLinePlot, \<br/>
            add_default_grids, PlotLabel<br/>
    from chaco.tools.api import PanTool, ZoomTool<br/>
     <br/>
    # Функция, создающая контейнер с графиками<br/>
    def _create_plot_component():<br/>
     <br/>
        # Создадим случайные величины для графиков<br/>
        numpoints = 500<br/>
        index = arange(numpoints)<br/>
        returns = random.lognormal(0.010.1, size=numpoints)<br/>
        price = 100.0 * cumprod(returns)<br/>
        volume = abs(random.normal(1000.01500.0, size=numpoints) + 2000.0)<br/>
     <br/>
        # ArrayDataSource - это массивы, хранящие наши данные<br/>
        time_ds = ArrayDataSource(index)<br/>
        vol_ds = ArrayDataSource(volume, sort_order="none")<br/>
        price_ds = ArrayDataSource(price, sort_order="none")<br/>
     <br/>
        # LinearMapper - это массивы подписей по осям<br/>
        xmapper = LinearMapper(range=DataRange1D(time_ds))<br/>
        vol_mapper = LinearMapper(range=DataRange1D(vol_ds))<br/>
        price_mapper = LinearMapper(range=DataRange1D(price_ds))<br/>
     <br/>
        # График цены типа FilledLinePlot с заполнением области под графиком<br/>
        price_plot = FilledLinePlot(index = time_ds, value = price_ds,<br/>
                                    index_mapper = xmapper,<br/>
                                    value_mapper = price_mapper,<br/>
                                    edge_color = "blue",<br/>
                                    face_color = "paleturquoise",<br/>
                                    bgcolor = "white",<br/>
                                    border_visible = True)<br/>
     <br/>
        # Добавим сетку и оси<br/>
        add_default_grids(price_plot)<br/>
        price_plot.overlays.append(PlotAxis(price_plot, orientation='left'))<br/>
        price_plot.overlays.append(PlotAxis(price_plot, orientation='bottom'))<br/>
     <br/>
        # Добавим возможность передвигания графика<br/>
        price_plot.tools.append(PanTool(price_plot, constrain=True,<br/>
                                        constrain_direction="x"))<br/>
        # Добавим зум<br/>
        price_plot.overlays.append(ZoomTool(price_plot, drag_button="right",<br/>
                                              always_on=True,<br/>
                                              tool_mode="range",<br/>
                                              axis="index"))<br/>
     <br/>
        # BarPlot - график в виде "столбиков"<br/>
        vol_plot = BarPlot(index = time_ds, value = vol_ds,<br/>
                           index_mapper = xmapper,<br/>
                           value_mapper = vol_mapper,<br/>
                           line_color = "transparent",<br/>
                           fill_color = "black",<br/>
                           bar_width = 1.0,<br/>
                           bar_width_type = "screen",<br/>
                           antialias = False,<br/>
                           height = 100,<br/>
                           resizable = "h",<br/>
                           bgcolor = "white",<br/>
                           border_visible = True)<br/>
     <br/>
        # Добавим сетку и оси<br/>
        add_default_grids(vol_plot)<br/>
        vol_plot.underlays.append(PlotAxis(vol_plot, orientation='left'))<br/>
        vol_plot.tools.append(PanTool(vol_plot, constrain=True,<br/>
                                      constrain_direction="x"))<br/>
     <br/>
        # container - массив наших графиков, управляет их расположением<br/>
        container = VPlotContainer(bgcolor = "lightblue",<br/>
                                   spacing = 20,<br/>
                                   padding = 50,<br/>
                                   fill_padding=False)<br/>
        container.add(vol_plot)<br/>
        container.add(price_plot)<br/>
        # Добавим надпись над контейнером<br/>
        container.overlays.append(PlotLabel("Financial Plot",<br/>
                                            component=container,<br/>
                                            font="Arial 24"))<br/>
        return container<br/>
     <br/>
     <br/>
    class Demo(HasTraits):<br/>
        # HasTraits - особый класс-словарь Traits, который связывается <br/>
        # с некоторым обработчиком.<br/>
        plot = Instance(Component)<br/>
     <br/>
        # View - представление наших графиков.<br/>
        traits_view = View(<br/>
                        Group(<br/>
                            Item('plot', editor=ComponentEditor(size=(800600)),<br/>
                                 show_label=False),<br/>
                            orientation = "vertical"),<br/>
                        resizable=True<br/>
                        )<br/>
     <br/>
        def _plot_default(self):<br/>
            # Контейнер/график, который отрисовывается по умолчанию<br/>
            return _create_plot_component()<br/>
     <br/>
    # Закомментируем стандартное окно, в котором рисуются графики<br/>
    #class PlotFrame(DemoFrame):<br/>
    #<br/>
    #    def _create_window(self):<br/>
    #        # создает окно, в котором нужно отрисовать графики<br/>
    #        return Window(self, -1, component=_create_plot_component())<br/>
     <br/>
    class ChacoQWidget(QtGui.QWidget):<br/>
        def __init__(self, parent=None):<br/>
            QtGui.QWidget.__init__(self, parent)<br/>
            layout = QtGui.QVBoxLayout(self)<br/>
            frame = Demo()<br/>
            # Теперь нам нужно создать виджет, для чего вызываем функцию control,<br/>
            # без нее магия не работает :)<br/>
            ui = frame.edit_traits(parent=self, kind='subpanel').control<br/>
            layout.addWidget(ui)<br/>
            layout.addWidget(QtGui.QPushButton("Hello Habrahabr"))<br/>
     <br/>
    if __name__ == "__main__":<br/>
        #demo_main(PlotFrame, size=(800, 600), title="Financial plot example")<br/>
        app = QtGui.QApplication.instance()<br/>
        w = ChacoQWidget()<br/>
        w.resize(800600)<br/>
        w.show()<br/>
        app.exec_()


  • Выборка цветов для графиков (приложу анимацию, но на самом деле все меняется по скроллу мыши):


    from numpy import arange, exp, sort<br/>
    from numpy.random import random<br/>
    from enable.example_support import DemoFrame, demo_main<br/>
    from enable.api import Component, ComponentEditor, Window<br/>
    from traits.api import HasTraits, Instance<br/>
    from traitsui.api import Item, Group, View<br/>
    from chaco.api import ArrayPlotData, ColorBar, \<br/>
                                     ColormappedSelectionOverlay, HPlotContainer, \<br/>
                                     jet, LinearMapper, Plot, gist_earth<br/>
    from chaco.tools.api import PanTool, ZoomTool, RangeSelection, \<br/>
                                           RangeSelectionOverlay<br/>
     <br/>
    #===============================================================================<br/>
    # # Create the Chaco plot.<br/>
    #===============================================================================<br/>
    def _create_plot_component():<br/>
     <br/>
        # Create some data<br/>
        numpts = 1000<br/>
        x = sort(random(numpts))<br/>
        y = random(numpts)<br/>
        color = exp(-(x**2 + y**2))<br/>
     <br/>
        # Create a plot data obect and give it this data<br/>
        pd = ArrayPlotData()<br/>
        pd.set_data("index", x)<br/>
        pd.set_data("value", y)<br/>
        pd.set_data("color", color)<br/>
     <br/>
        # Create the plot<br/>
        plot = Plot(pd)<br/>
        plot.plot(("index""value""color"),<br/>
                  type="cmap_scatter",<br/>
                  name="my_plot",<br/>
                  color_mapper=gist_earth,<br/>
                  marker = "square",<br/>
                  fill_alpha = 0.5,<br/>
                  marker_size = 8,<br/>
                  outline_color = "black",<br/>
                  border_visible = True,<br/>
                  bgcolor = "white")<br/>
     <br/>
        # Tweak some of the plot properties<br/>
        plot.title = "Colormapped Scatter Plot with Pan/Zoom Color Bar"<br/>
        plot.padding = 50<br/>
        plot.x_grid.visible = False<br/>
        plot.y_grid.visible = False<br/>
        plot.x_axis.font = "modern 16"<br/>
        plot.y_axis.font = "modern 16"<br/>
     <br/>
        # Add pan and zoom to the plot<br/>
        plot.tools.append(PanTool(plot, constrain_key="shift"))<br/>
        zoom = ZoomTool(plot)<br/>
        plot.overlays.append(zoom)<br/>
     <br/>
        # Create the colorbar, handing in the appropriate range and colormap<br/>
        colorbar = ColorBar(index_mapper=LinearMapper(range=plot.color_mapper.range),<br/>
                            color_mapper=plot.color_mapper,<br/>
                            orientation='v',<br/>
                            resizable='v',<br/>
                            width=30,<br/>
                            padding=20)<br/>
        colorbar.plot = plot<br/>
        colorbar.padding_top = plot.padding_top<br/>
        colorbar.padding_bottom = plot.padding_bottom<br/>
     <br/>
        # Add pan and zoom tools to the colorbar<br/>
        colorbar.tools.append(PanTool(colorbar, constrain_direction="y", constrain=True))<br/>
        zoom_overlay = ZoomTool(colorbar, axis="index", tool_mode="range",<br/>
                                always_on=True, drag_button="right")<br/>
        colorbar.overlays.append(zoom_overlay)<br/>
     <br/>
        # Create a container to position the plot and the colorbar side-by-side<br/>
        container = HPlotContainer(plot, colorbar, use_backbuffer=True, bgcolor="lightgray")<br/>
     <br/>
        return container<br/>
     <br/>
    #===============================================================================<br/>
    # Attributes to use for the plot view.<br/>
    size=(650,650)<br/>
    title="Colormapped scatter plot"<br/>
     <br/>
    #===============================================================================<br/>
    # # Demo class that is used by the demo.py application.<br/>
    #===============================================================================<br/>
    class Demo(HasTraits):<br/>
        plot = Instance(Component)<br/>
     <br/>
        traits_view = View(<br/>
                        Group(<br/>
                            Item('plot', editor=ComponentEditor(size=size),<br/>
                                 show_label=False),<br/>
                            orientation = "vertical"),<br/>
                        resizable=True, title=title<br/>
                        )<br/>
     <br/>
        def _plot_default(self):<br/>
             return _create_plot_component()<br/>
     <br/>
    demo = Demo()<br/>
     <br/>
    #===============================================================================<br/>
    # Stand-alone frame to display the plot.<br/>
    #===============================================================================<br/>
    class PlotFrame(DemoFrame):<br/>
     <br/>
        def _create_window(self):<br/>
            # Return a window containing our plots<br/>
            return Window(self, -1, component=_create_plot_component())<br/>
     <br/>
    if __name__ == "__main__":<br/>
        demo_main(PlotFrame, size=size, title=title)


  • Графики в полярных координатах:


    Код:
    from numpy import arange, pi, sin, cos<br/>
    from enthought.enable.example_support import DemoFrame, demo_main<br/>
    from enthought.enable.api import Window<br/>
    from enthought.traits.api import false<br/>
    from enthought.chaco.api import create_polar_plot<br/>
     <br/>
    class MyFrame(DemoFrame):<br/>
        def _create_window(self):<br/>
            numpoints = 5000<br/>
            low = 0<br/>
            high = pi*2<br/>
            theta = arange(low, high, (high-low) / numpoints)<br/>
            radius = sin(theta*3)<br/>
     <br/>
            plot = create_polar_plot((radius,theta),color=(0.0,0.0,1.0,1), width=4.0)<br/>
            plot.bgcolor = "white"<br/>
            return Window(self, -1, component=plot)<br/>
     <br/>
    if __name__ == "__main__":<br/>
        demo_main(MyFrame, size=(600,600), title="Simple Polar Plot")


  • Различные полигоны:


    import math<br/>
    from numpy import array, transpose<br/>
    from enable.example_support import DemoFrame, demo_main<br/>
    from enable.api import Component, ComponentEditor, Window<br/>
    from traits.api import HasTraits, Instance, Enum, CArray, Dict<br/>
    from traitsui.api import Item, Group, View<br/>
    from chaco.api import ArrayPlotData, HPlotContainer, Plot<br/>
    from chaco.base import n_gon<br/>
    from chaco.tools.api import PanTool, ZoomTool, DragTool<br/>
     <br/>
    class DataspaceMoveTool(DragTool):<br/>
        """<br/>
        Modifies the data values of a plot.  Only works on instances<br/>
        of BaseXYPlot or its subclasses<br/>
        """
    <br/>
     <br/>
        event_state = Enum("normal""dragging")<br/>
        _prev_pt = CArray<br/>
     <br/>
        def is_draggable(self, x, y):<br/>
            return self.component.hittest((x,y))<br/>
     <br/>
        def drag_start(self, event):<br/>
            data_pt = self.component.map_data((event.x, event.y), all_values=True)<br/>
            self._prev_pt = data_pt<br/>
            event.handled = True<br/>
     <br/>
        def dragging(self, event):<br/>
            plot = self.component<br/>
            cur_pt = plot.map_data((event.x, event.y), all_values=True)<br/>
            dx = cur_pt[0] - self._prev_pt[0]<br/>
            dy = cur_pt[1] - self._prev_pt[1]<br/>
            index = plot.index.get_data() + dx<br/>
            value = plot.value.get_data() + dy<br/>
            plot.index.set_data(index, sort_order=plot.index.sort_order)<br/>
            plot.value.set_data(value, sort_order=plot.value.sort_order)<br/>
            self._prev_pt = cur_pt<br/>
            event.handled = True<br/>
            plot.request_redraw()<br/>
     <br/>
     <br/>
    #===============================================================================<br/>
    # # Create the Chaco plot.<br/>
    #===============================================================================<br/>
    def _create_plot_component():<br/>
     <br/>
        # Use n_gon to compute center locations for our polygons<br/>
        points = n_gon(center=(0,0), r=4, nsides=8)<br/>
     <br/>
        # Choose some colors for our polygons<br/>
        colors = {3:0xaabbcc,   4:'orange'5:'yellow',    6:'lightgreen',<br/>
                  7:'green'8:'blue',   9:'lavender'10:'purple'}<br/>
     <br/>
            # Create a PlotData object to store the polygon data<br/>
        pd = ArrayPlotData()<br/>
     <br/>
        # Create a Polygon Plot to draw the regular polygons<br/>
        polyplot = Plot(pd)<br/>
     <br/>
        # Store path data for each polygon, and plot<br/>
        nsides = 3<br/>
        for p in points:<br/>
            npoints = n_gon(center=p, r=2, nsides=nsides)<br/>
            nxarray, nyarray = transpose(npoints)<br/>
            pd.set_data("x" + str(nsides), nxarray)<br/>
            pd.set_data("y" + str(nsides), nyarray)<br/>
            plot = polyplot.plot(("x"+str(nsides)"y"+str(nsides)),<br/>
                          type="polygon",<br/>
                          face_color=colors[nsides],<br/>
                          hittest_type="poly")[0]<br/>
            plot.tools.append(DataspaceMoveTool(plot, drag_button="right"))<br/>
            nsides = nsides + 1<br/>
     <br/>
        # Tweak some of the plot properties<br/>
        polyplot.padding = 50<br/>
        polyplot.title = "Polygon Plot"<br/>
     <br/>
        # Attach some tools to the plot<br/>
        polyplot.tools.append(PanTool(polyplot))<br/>
        zoom = ZoomTool(polyplot, tool_mode="box", always_on=False)<br/>
        polyplot.overlays.append(zoom)<br/>
     <br/>
        return polyplot<br/>
     <br/>
    #===============================================================================<br/>
    # Attributes to use for the plot view.<br/>
    size=(800,800)<br/>
    title="Polygon Plot"<br/>
     <br/>
    #===============================================================================<br/>
    # # Demo class that is used by the demo.py application.<br/>
    #===============================================================================<br/>
    class Demo(HasTraits):<br/>
        plot = Instance(Component)<br/>
     <br/>
        traits_view = View(<br/>
                        Group(<br/>
                            Item('plot', editor=ComponentEditor(size=size),<br/>
                                 show_label=False),<br/>
                            orientation = "vertical"),<br/>
                        resizable=True, title=title<br/>
                        )<br/>
     <br/>
        def _plot_default(self):<br/>
             return _create_plot_component()<br/>
     <br/>
    demo = Demo()<br/>
     <br/>
    #===============================================================================<br/>
    # Stand-alone frame to display the plot.<br/>
    #===============================================================================<br/>
    class PlotFrame(DemoFrame):<br/>
     <br/>
        def _create_window(self):<br/>
            # Return a window containing our plots<br/>
            return Window(self, -1, component=_create_plot_component())<br/>
     <br/>
    if __name__ == "__main__":<br/>
        demo_main(PlotFrame, size=size, title=title)


  • Рентгеновские лучи:


    from __future__ import with_statement<br/>
    import numpy<br/>
    from traits.api import HasTraits, Instance, Enum<br/>
    from traitsui.api import View, Item<br/>
    from enable.api import ComponentEditor<br/>
    from chaco.api import Plot, ArrayPlotData, AbstractOverlay<br/>
    from enable.api import BaseTool<br/>
    from enable.markers import DOT_MARKER, DotMarker<br/>
     <br/>
    class BoxSelectTool(BaseTool):<br/>
        """ Tool for selecting all points within a box<br/>
     <br/>
            There are 2 states for this tool, normal and selecting. While the<br/>
            left mouse button is down the metadata on the datasources will be<br/>
            updated with the current selected bounds.<br/>
     <br/>
            Note that the tool does not actually store the selected point, but the<br/>
            bounds of the box.<br/>
        """
    <br/>
     <br/>
        event_state = Enum("normal""selecting")<br/>
     <br/>
        def normal_left_down(self, event):<br/>
            self.event_state = "selecting"<br/>
            self.selecting_mouse_move(event)<br/>
     <br/>
        def selecting_left_up(self, event):<br/>
            self.event_state = "normal"<br/>
     <br/>
        def selecting_mouse_move(self, event):<br/>
            x1, y1 = self.map_to_data(event.x-25, event.y-25)<br/>
            x2, y2 = self.map_to_data(event.x+25, event.y+25)<br/>
     <br/>
            index_datasource = self.component.index<br/>
            index_datasource.metadata['selections'] = (x1, x2)<br/>
     <br/>
            value_datasource = self.component.value<br/>
            value_datasource.metadata['selections'] = (y1, y2)<br/>
     <br/>
            self.component.request_redraw()<br/>
     <br/>
        def map_to_data(self, x, y):<br/>
            """ Returns the data space coordinates of the given x and y.<br/>
     <br/>
            Takes into account orientation of the plot and the axis setting.<br/>
            """
    <br/>
     <br/>
            plot = self.component<br/>
            if plot.orientation == "h":<br/>
                index = plot.x_mapper.map_data(x)<br/>
                value = plot.y_mapper.map_data(y)<br/>
            else:<br/>
                index = plot.y_mapper.map_data(y)<br/>
                value = plot.x_mapper.map_data(x)<br/>
     <br/>
            return index, value<br/>
     <br/>
     <br/>
    class XRayOverlay(AbstractOverlay):<br/>
        """ Overlay which draws scatter markers on top of plot data points.<br/>
     <br/>
            This overlay should be combined with a tool which updates the<br/>
            datasources metadata with selection bounds.<br/>
        """
    <br/>
     <br/>
        marker = DotMarker()<br/>
     <br/>
        def overlay(self, component, gc, view_bounds=None, mode='normal'):<br/>
            x_range = self._get_selection_index_screen_range()<br/>
            y_range = self._get_selection_value_screen_range()<br/>
     <br/>
            if len(x_range) == 0:<br/>
                return<br/>
     <br/>
            x1, x2 = x_range<br/>
            y1, y2 = y_range<br/>
     <br/>
            with gc:<br/>
                gc.set_alpha(0.8)<br/>
                gc.set_fill_color((1.0,1.0,1.0))<br/>
                gc.rect(x1, y1, x2-x1, y2-y1)<br/>
                gc.draw_path()<br/>
     <br/>
            pts = self._get_selected_points()<br/>
            if len(pts) == 0:<br/>
                return<br/>
            screen_pts = self.component.map_screen(pts)<br/>
            if hasattr(gc'draw_marker_at_points'):<br/>
                gc.draw_marker_at_points(screen_pts, 3, DOT_MARKER)<br/>
            else:<br/>
                gc.save_state()<br/>
                for sx,sy in screen_pts:<br/>
                    gc.translate_ctm(sx, sy)<br/>
                    gc.begin_path()<br/>
                    self.marker.add_to_path(gc3)<br/>
                    gc.draw_path(self.marker.draw_mode)<br/>
                    gc.translate_ctm(-sx, -sy)<br/>
                gc.restore_state()<br/>
     <br/>
        def _get_selected_points(self):<br/>
            """ gets all the points within the bounds defined in the datasources<br/>
                metadata<br/>
            """
    <br/>
            index_datasource = self.component.index<br/>
            index_selection = index_datasource.metadata['selections']<br/>
            index = index_datasource.get_data()<br/>
     <br/>
            value_datasource = self.component.value<br/>
            value_selection = value_datasource.metadata['selections']<br/>
            value = value_datasource.get_data()<br/>
     <br/>
            x_indices = numpy.where((index > index_selection[0]) & (index < index_selection[-1]))<br/>
            y_indices = numpy.where((value > value_selection[0]) & (value < value_selection[-1]))<br/>
     <br/>
            indices = list(set(x_indices[0]) & set(y_indices[0]))<br/>
     <br/>
            sel_index = index[indices]<br/>
            sel_value = value[indices]<br/>
     <br/>
            return zip(sel_index, sel_value)<br/>
     <br/>
        def _get_selection_index_screen_range(self):<br/>
            """ maps the selected bounds which were set by the tool into screen<br/>
                space. The screen space points can be used for drawing the overlay<br/>
            """
    <br/>
            index_datasource = self.component.index<br/>
            index_mapper = self.component.index_mapper<br/>
            index_selection = index_datasource.metadata['selections']<br/>
            return tuple(index_mapper.map_screen(numpy.array(index_selection)))<br/>
     <br/>
        def _get_selection_value_screen_range(self):<br/>
            """ maps the selected bounds which were set by the tool into screen<br/>
                space. The screen space points can be used for drawing the overlay<br/>
            """
    <br/>
            value_datasource = self.component.value<br/>
            value_mapper = self.component.value_mapper<br/>
            value_selection = value_datasource.metadata['selections']<br/>
            return tuple(value_mapper.map_screen(numpy.array(value_selection)))<br/>
     <br/>
    class PlotExample(HasTraits):<br/>
     <br/>
        plot = Instance(Plot)<br/>
     <br/>
        traits_view = View(Item('plot', editor=ComponentEditor()),<br/>
                           width=600, height=600)<br/>
     <br/>
        def __init__(self, index, value, *args, **kw):<br/>
            super(PlotExample, self).__init__(*args, **kw)<br/>
     <br/>
            plot_data = ArrayPlotData(index=index)<br/>
            plot_data.set_data('value', value)<br/>
     <br/>
            self.plot = Plot(plot_data)<br/>
            line = self.plot.plot(('index''value'))[0]<br/>
     <br/>
            line.overlays.append(XRayOverlay(line))<br/>
            line.tools.append(BoxSelectTool(line))<br/>
     <br/>
    index = numpy.arange(0250.25)<br/>
    value = numpy.sin(index) + numpy.arange(0100.1)<br/>
     <br/>
    example = PlotExample(index, value)<br/>
    example.configure_traits()


  • Различные маркеры с выделением:


    from numpy import arange, sort, compress, arange<br/>
    from numpy.random import random<br/>
    from enable.example_support import DemoFrame, demo_main<br/>
    from enable.api import Component, ComponentEditor, Window<br/>
    from traits.api import HasTraits, Instance<br/>
    from traitsui.api import Item, Group, View<br/>
    from chaco.api import AbstractDataSource, ArrayPlotData, Plot, \<br/>
                                     HPlotContainer, LassoOverlay<br/>
    from chaco.tools.api import LassoSelection, ScatterInspector<br/>
     <br/>
    #===============================================================================<br/>
    # # Create the Chaco plot.<br/>
    #===============================================================================<br/>
    def _create_plot_component():<br/>
     <br/>
        # Create some data<br/>
        npts = 2000<br/>
        x = sort(random(npts))<br/>
        y = random(npts)<br/>
     <br/>
        # Create a plot data obect and give it this data<br/>
        pd = ArrayPlotData()<br/>
        pd.set_data("index", x)<br/>
        pd.set_data("value", y)<br/>
     <br/>
        # Create the plot<br/>
        plot = Plot(pd)<br/>
        plot.plot(("index""value"),<br/>
                  type="scatter",<br/>
                  name="my_plot",<br/>
                  marker="circle",<br/>
                  index_sort="ascending",<br/>
                  color="red",<br/>
                  marker_size=4,<br/>
                  bgcolor="white")<br/>
     <br/>
        # Tweak some of the plot properties<br/>
        plot.title = "Scatter Plot With Selection"<br/>
        plot.line_width = 1<br/>
        plot.padding = 50<br/>
     <br/>
        # Right now, some of the tools are a little invasive, and we need the<br/>
        # actual ScatterPlot object to give to them<br/>
        my_plot = plot.plots["my_plot"][0]<br/>
     <br/>
        # Attach some tools to the plot<br/>
        lasso_selection = LassoSelection(component=my_plot,<br/>
                                         selection_datasource=my_plot.index)<br/>
        my_plot.active_tool = lasso_selection<br/>
        my_plot.tools.append(ScatterInspector(my_plot))<br/>
        lasso_overlay = LassoOverlay(lasso_selection=lasso_selection,<br/>
                                     component=my_plot)<br/>
        my_plot.overlays.append(lasso_overlay)<br/>
     <br/>
        # Uncomment this if you would like to see incremental updates:<br/>
        #lasso_selection.incremental_select = True<br/>
     <br/>
        return plot<br/>
     <br/>
     <br/>
    #===============================================================================<br/>
    # Attributes to use for the plot view.<br/>
    size=(650,650)<br/>
    title="Scatter plot with selection"<br/>
    bg_color="lightgray"<br/>
     <br/>
    #===============================================================================<br/>
    # # Demo class that is used by the demo.py application.<br/>
    #===============================================================================<br/>
    class Demo(HasTraits):<br/>
        plot = Instance(Component)<br/>
     <br/>
        traits_view = View(<br/>
                        Group(<br/>
                            Item('plot', editor=ComponentEditor(size=size),<br/>
                                 show_label=False),<br/>
                            orientation = "vertical"),<br/>
                        resizable=True, title=title<br/>
                        )<br/>
     <br/>
        def _selection_changed(self):<br/>
            mask = self.index_datasource.metadata['selection']<br/>
            print "New selection: "<br/>
            print compress(mask, arange(len(mask)))<br/>
            print<br/>
     <br/>
        def _plot_default(self):<br/>
             plot = _create_plot_component()<br/>
     <br/>
             # Retrieve the plot hooked to the LassoSelection tool.<br/>
             my_plot = plot.plots["my_plot"][0]<br/>
             lasso_selection = my_plot.active_tool<br/>
     <br/>
             # Set up the trait handler for the selection<br/>
             self.index_datasource = my_plot.index<br/>
             lasso_selection.on_trait_change(self._selection_changed,<br/>
                                            'selection_changed')<br/>
     <br/>
             return plot<br/>
     <br/>
    demo = Demo()<br/>
     <br/>
    #===============================================================================<br/>
    # Stand-alone frame to display the plot.<br/>
    #===============================================================================<br/>
    class PlotFrame(DemoFrame):<br/>
     <br/>
        index_datasource = Instance(AbstractDataSource)<br/>
     <br/>
        def _create_window(self):<br/>
     <br/>
            component = _create_plot_component()<br/>
     <br/>
            # Retrieve the plot hooked to the LassoSelection tool.<br/>
            my_plot = component.plots["my_plot"][0]<br/>
            lasso_selection = my_plot.active_tool<br/>
     <br/>
            # Set up the trait handler for the selection<br/>
            self.index_datasource = my_plot.index<br/>
            lasso_selection.on_trait_change(self._selection_changed,<br/>
                                            'selection_changed')<br/>
     <br/>
            # Return a window containing our plots<br/>
            return Window(self, -1, component=component, bg_color=bg_color)<br/>
     <br/>
        def _selection_changed(self):<br/>
            mask = self.index_datasource.metadata['selection']<br/>
            print "New selection: "<br/>
            print compress(mask, arange(len(mask)))<br/>
            print<br/>
     <br/>
     <br/>
    if __name__ == "__main__":<br/>
        demo_main(PlotFrame, size=size, title=title)




Чтобы посмотреть как Chaco реализует анимацию, загляните в папку ets/chaco/examples/updating_plot


Chaco в HPGL-GUI

В HPGL-GUI нужно было строить гистограммы. Для этого одинаково подходили Matplotlib и Chaco. Выбор пал на Chaco, т.к. Matplotlib не поддерживал интеграцию в PySide.
Выглядит окно статистики вот так:

Код можно посмотреть тут:
raw.github.com/Snegovikufa/HPGL-GUI/master/gui_widgets/statistics_window.py

P.S. Если нужно рассказать про встраивание в PyQt4 или PySide, то допишу.

UPD. Обновил пример финансового графика: добавил подробные комментарии и сделал встраивание в виджет PySide.
Теги:
Хабы:
Всего голосов 49: ↑45 и ↓4+41
Комментарии32

Публикации

Работа

Data Scientist
49 вакансий

Ближайшие события