Search code examples
pythonplotenthoughtchaco

Minor ticks in Chaco plot?


The minor ticks in Chaco are always omitted: enter image description here

This is not always convinient. Is it possible to have minor ticks in Chaco like ones in matplotlib:

enter image description here

Haven't found anything related.. Thanks.


Solution

  • Edit: This feature has now been added to Chaco 4.6, so if you're using this version or later, use the following similar code. If not see the original post below. Also see the documentation here and another example here.

    if __name__ == "__main__":
        from traits.etsconfig.api import ETSConfig
        ETSConfig.toolkit = 'qt4'
        #
        import warnings
        warnings.filterwarnings(action = "ignore", category = FutureWarning, module="chaco")
        warnings.filterwarnings(action = "ignore", category = FutureWarning, module="traits")
    #
    from PySide import QtGui, QtCore
    import numpy as np
    from enable.api import Component, Container, Window
    from chaco.api import *
    import sys
    #
    class ChacoPlot(QtGui.QWidget):
        def __init__(self, parent=None):
            super(ChacoPlot, self).__init__(parent)
            #
            self.container = OverlayPlotContainer(padding=40)
            #
            self.enableWindow = Window(self, -1, component=self.container)
            windowLayout = QtGui.QVBoxLayout(self)
            windowLayout.addWidget(self.enableWindow.control)
            #
            self.xRange = DataRange1D()
            self.yRange = DataRange1D()
            #
            self.xMapper = LinearMapper(range=self.xRange)
            self.yMapper = LinearMapper(range=self.yRange)
            #
            self.plots = {}
            # keep a list of plots added to the container
        #
        def setMinimumSize(self, width, height):
            self.enableWindow.control.setMinimumSize(width, height)
        #
        def addLine(self, name, plotType):
            xSource = ArrayDataSource([0])
            ySource = ArrayDataSource([0])
            #
            self.xRange.add(xSource)
            self.yRange.add(ySource)
            #
            index_mapper = self.xMapper
            value_mapper = self.yMapper
            #
            # plotType is a class name
            plot = plotType(    index = xSource,
                                value = ySource,
                                index_mapper = index_mapper,
                                value_mapper = value_mapper,
                                visible = False
                           )
            #
            self.container.add(plot)
            #
            self.plots[name] = {'plot':plot, 'xSource':xSource, 'ySource':ySource}
        #
        def updateLine(self, name, xData, yData):
            plot = self.plots[name]
            #
            if np.array(xData).size != 0:
                plot['plot'].visible = True
            else:
                plot['plot'].visible = False
                xData = [0]
                yData = [0]
            #
            plot['xSource'].set_data(xData)
            plot['ySource'].set_data(yData)
        #
        def addAxis(self, plotName, orientation):
            plot = self.plots[plotName]['plot']
            #
            if orientation == 'top' or orientation == 'bottom':
                mapper = self.xMapper
            if orientation == 'left' or orientation == 'right':
                mapper = self.yMapper
            #
            axis = PlotAxis(plot, orientation=orientation, mapper=mapper)
            plot.overlays.append(axis)
            #
            return axis
        #
        def addMinorAxis(self, plotName, orientation):
            plot = self.plots[plotName]['plot']
            #
            if orientation == 'top' or orientation == 'bottom':
                mapper = self.xMapper
                range = self.xRange
            if orientation == 'left' or orientation == 'right':
                mapper = self.yMapper
                range = self.yRange
            #
            newAxis = MinorPlotAxis(plot, orientation=orientation, mapper=mapper)
            plot.overlays.append(newAxis)
            #
            return axis
        #
    #
    if __name__ == "__main__":
        appQT = QtGui.QApplication.instance()
        #
        x1 = np.arange(300)/18.0
        y1 = np.sin(x1)
        x2 = np.arange(300)/18.0
        y2 = 2.0*np.cos(x2)
        #
        plot = ChacoPlot()
        plot.setMinimumSize(400,300)
        #
        plot.addLine('line1', LinePlot)
        plot.addLine('line2', LinePlot)
        plot.updateLine('line1', x1, y1)
        plot.updateLine('line2', x2, y2)
        #
        plot.addAxis('line1', 'bottom')
        plot.addAxis('line1', 'left')
        plot.addMinorAxis('line1', 'bottom')
        plot.addMinorAxis('line1', 'left')
        #
        plot.show()
        appQT.exec_()
    

    Original: Chaco doesn't have this feature specifically, but you can add minor ticks by adding an extra PlotAxis. You need to modify a few properties of the axis:

    • tick_generator - This object defines the position of the ticks
    • tick_label_formatter - This function returns the tick label string for a given tick label value
    • tick_in and tick_out - These numbers define the size of the ticks (in and out of the axis)

    Here's an example. It's a lot of code, but it's fairly straightforward. Although it is common for people to make plots using the Plot helper class, I like to create plots manually since it's much easier to customize. Anyways hope it helps.

    if __name__ == "__main__":
        from traits.etsconfig.api import ETSConfig
        ETSConfig.toolkit = 'qt4'
        #
        import warnings
        warnings.filterwarnings(action = "ignore", category = FutureWarning, module="chaco")
        warnings.filterwarnings(action = "ignore", category = FutureWarning, module="traits")
    #
    from PySide import QtGui, QtCore
    import numpy as np
    from enable.api import Component, Container, Window
    from chaco.api import *
    import sys
    #
    class ChacoPlot(QtGui.QWidget):
        def __init__(self, parent=None):
            super(ChacoPlot, self).__init__(parent)
            #
            self.container = OverlayPlotContainer(padding=40)
            #
            self.enableWindow = Window(self, -1, component=self.container)
            windowLayout = QtGui.QVBoxLayout(self)
            windowLayout.addWidget(self.enableWindow.control)
            #
            self.xRange = DataRange1D()
            self.yRange = DataRange1D()
            #
            self.xMapper = LinearMapper(range=self.xRange)
            self.yMapper = LinearMapper(range=self.yRange)
            #
            self.plots = {}
            # keep a list of plots added to the container
        #
        def setMinimumSize(self, width, height):
            self.enableWindow.control.setMinimumSize(width, height)
        #
        def addLine(self, name, plotType):
            xSource = ArrayDataSource([0])
            ySource = ArrayDataSource([0])
            #
            self.xRange.add(xSource)
            self.yRange.add(ySource)
            #
            index_mapper = self.xMapper
            value_mapper = self.yMapper
            #
            # plotType is a class name
            plot = plotType(    index = xSource,
                                value = ySource,
                                index_mapper = index_mapper,
                                value_mapper = value_mapper,
                                visible = False
                           )
            #
            self.container.add(plot)
            #
            self.plots[name] = {'plot':plot, 'xSource':xSource, 'ySource':ySource}
        #
        def updateLine(self, name, xData, yData):
            plot = self.plots[name]
            #
            if np.array(xData).size != 0:
                plot['plot'].visible = True
            else:
                plot['plot'].visible = False
                xData = [0]
                yData = [0]
            #
            plot['xSource'].set_data(xData)
            plot['ySource'].set_data(yData)
        #
        def addAxis(self, plotName, orientation):
            plot = self.plots[plotName]['plot']
            #
            if orientation == 'top' or orientation == 'bottom':
                mapper = self.xMapper
            if orientation == 'left' or orientation == 'right':
                mapper = self.yMapper
            #
            axis = PlotAxis(plot, orientation=orientation, mapper=mapper)
            plot.overlays.append(axis)
            #
            return axis
        #
        def addMinorAxis(self, plotName, orientation):
            plot = self.plots[plotName]['plot']
            #
            if orientation == 'top' or orientation == 'bottom':
                mapper = self.xMapper
                range = self.xRange
            if orientation == 'left' or orientation == 'right':
                mapper = self.yMapper
                range = self.yRange
            #
            newAxis = PlotAxis(plot, orientation=orientation, mapper=mapper)
            plot.overlays.append(newAxis)
            #
            newAxis.tick_generator = MinorTickGenerator()
            #
            newAxis.tick_label_formatter  = lambda x: ''
            newAxis.tick_in  = 2
            newAxis.tick_out = 2
        #
    #
    class MinorTickGenerator(AbstractTickGenerator):
        def __init__(self):
            super(MinorTickGenerator, self).__init__()
        #
        def get_ticks(self, data_low, data_high, bounds_low, bounds_high, interval, use_endpoints=False, scale='linear'):
            interval = interval
            #
            if interval == 'auto':
                interval = auto_interval(data_low, data_high)/5.0
            #
            return auto_ticks(data_low, data_high, bounds_low, bounds_high, interval, use_endpoints)
        #
    #
    if __name__ == "__main__":
        appQT = QtGui.QApplication.instance()
        #
        x1 = np.arange(300)/18.0
        y1 = np.sin(x1)
        x2 = np.arange(300)/18.0
        y2 = 2.0*np.cos(x2)
        #
        plot = ChacoPlot()
        plot.setMinimumSize(400,300)
        #
        plot.addLine('line1', LinePlot)
        plot.addLine('line2', LinePlot)
        plot.updateLine('line1', x1, y1)
        plot.updateLine('line2', x2, y2)
        #
        plot.addAxis('line1', 'bottom')
        plot.addAxis('line1', 'left')
        plot.addMinorAxis('line1', 'bottom')
        plot.addMinorAxis('line1', 'left')
        #
        plot.show()
        appQT.exec_()
    

    enter image description here