Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I set QChart axis ticks explicitly?

Tags:

qt

pyqt

Using the Zoom Line Example I have made a Python QChartView class that can scroll with the arrow keys and zoom with the plus and minus keys. (see my code below).

When I scroll left I would expect that the grid lines and axis ticks scroll the same amount as the data. However, only the data (the QLineSeries) scrolls to the left. The 5 grid lines remain at the same positions but their tick values are updated. This is undesirable as the new tick values can be anything.

I have looked in the documentation but could not find how to make the grid scroll together with the data. Am I missing something?

I would also like to be able to set the ticks to explicit values (so that I can perhaps implement the scrolling behavior myself). Is it possible to set the axis tick values to specific values?

My example code:

import sys
from math import pi, sin, sqrt

from PyQt5.QtChart import QLineSeries, QChart, QChartView
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication

class ZoomPanChartView(QChartView):
    """ QChartView that can zoom/pan with the keys
    """
    def __init__(self, chart):
        super().__init__(chart)

        self.zoomFactor = sqrt(2) # QCharts default is 2
        self.panPixels = 10

    def keyPressEvent(self, keyEvent):
        """ Panning (scrolling) is done with the arrow keys. 
            Zooming goes with the plus and minus keys.
            The '=' key resets.
        """
        key = keyEvent.key()

        if key == Qt.Key_Equal:
            self.chart().zoomReset()
        if key == Qt.Key_Plus:
            self.chart().zoom(self.zoomFactor)
        elif key == Qt.Key_Minus:
            self.chart().zoom(1/self.zoomFactor)
        elif key == Qt.Key_Left:
            self.chart().scroll(-self.panPixels, 0)
        elif key == Qt.Key_Right:
            self.chart().scroll(+self.panPixels, 0)
        elif key == Qt.Key_Up:
            self.chart().scroll(0, +self.panPixels)
        elif key == Qt.Key_Down:
            self.chart().scroll(0, -self.panPixels)
        elif key == Qt.Key_0:
            self.chart().axisX().applyNiceNumbers() # changes the range
        else:
            super().keyPressEvent(keyEvent)


def main():
    app = QApplication(sys.argv)
    chart = QChart()

    series = QLineSeries()
    for i in range(0, 100):
        x = i * pi / 20
        y = sin(x)
        series.append(x, y)

    chart.addSeries(series)
    chart.createDefaultAxes()
    chart.axisY().setRange(-1, 1)
    chart.legend().hide()

    chartView = ZoomPanChartView(chart)
    chartView.show()
    chartView.resize(400, 300)
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()    
like image 758
titusjan Avatar asked Mar 24 '17 17:03

titusjan


2 Answers

You can use QCategoryAxis to place ticks where you want:

initialize:

ch = self.chView.chart()
self.chartAxisX = QCategoryAxis(labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue, startValue=0.0)
ch.setAxisX(self.chartAxisX)
self.chartAxisY = QCategoryAxis(labelsPosition=QCategoryAxis.AxisLabelsPositionOnValue, startValue=0.0)
ch.setAxisY(self.chartAxisY)

add series:

        ch.addSeries(s)
        s.attachAxis(self.chartAxisX)
        s.attachAxis(self.chartAxisY)

set ticks at multiples of 5:

        for s in self.chartAxisX.categoriesLabels():
            self.chartAxisX.remove(s)
        for i in range(0, int(max_x_value) + 1, 5):
            self.chartAxisX.append(str(i), i)
        self.chartAxisX.setRange(0.0, max_x_value)

or use this generic function for any interval:

def format_axis(axis, min_value, max_value, step):
    for s in axis.categoriesLabels():
        axis.remove(s)
    axis.setStartValue(min_value)
    for i in range(ceil(min_value / step), floor(max_value / step) + 1):
        v = i * step
        axis.append('%g' % v, v)
    axis.setRange(min_value, max_value)

format_axis(self.chartAxisX, -1.1, 0.98, 0.25)
like image 54
panda-34 Avatar answered Oct 12 '22 23:10

panda-34


The best I could find is setting a QValueAxis as the axis on QChart and calling QValueAxis::applyNiceNumbers() to adjust the range, i.e. max and min of the current scale, so that the numbers shown are a bit more human readable. But this will alter data's position instead of gridlines' positions. You can check the function's behaviour on the horizontalBarChart example.

I thought of using a QLineSeries data-set to make the grid myself, but I would need to change the tick's positions on the axis, which, as far as I was able to determine, is not easily made with current QChart.

Short answer: you can't do it with QCharts..

I've been working with Qwt library for some time and I can attest that the grid there behaves as expected and other behaviors are a bit more mature as well. Panning moves the grip around and zooming makes the grid resize in steps to stay human-readable. Maybe it's worth checking.

like image 20
A. Vieira Avatar answered Oct 12 '22 23:10

A. Vieira