QCustomPlot Bindings for Python

    Good afternoon, habrozhiteli!

    Introduction


    In his spare time, he became interested in writing applications in PyQt5. And at the end of May, MyWallet decided to rewrite its long-standing project on conducting home accounting from Python pluses, as in the previous version a number of architectural errors were made that we wanted to correct. Therefore, collecting PyQt5 from source for Fedora 21, somewhere in two weeks I realized all the functionality that was previously. And now the question arises in the visualization of data on expenses / income on a monthly basis. Since I had experience visualizing data using QCustomPlot , I wanted to do the visualization using this lib. But unfortunately, I did not find the binds.

    Assembly


    After looking at the source of PyQt5, it was found out that the generation of binds was implemented using SIP ). SIP receives something like a stripped-down header of class methods (naturally, with its so-called annotations), and generates C ++ code to create a ready-made python module.

    So, to build the QCustomPlot module for Python, we need:
    1. Qt 5.x.
    2. SIP is the most recent version.
    3. PyQt 5.x.
    4. Assembled as a dynamic link library qcustomplot, assembled under Qt 5.x.
    5. A special kind of file with a description of the library class interface.


    Rummaging through github in search of a ready-made interface file for this lib, I came across the qcustomplot-python repository , the owner of which collected binders, though for PyQt4. By analogy, we get the qcustomplot.sip interface file.

    You can also find configure.py in the same repository, which, as you know, is necessary for building and installing Python modules. This file had to be adapted to the new version of PyQt.

    Well, then standard:

    $ python3 configure.py build
    $ make
    $ sudo make install 


    Make sure that everything worked out for us, run IPy:
    $ python3
    >>> import qcustomplot
    >>> dir(qcustomplot)
    ['QCP', 'QCPAbstractItem', 'QCPAbstractLegendItem', 'QCPAbstractPlottable', 'QCPAxis', 'QCPAxisRect', 'QCPBarData', 'QCPBars', 'QCPBarsGroup', 'QCPColorGradient', 'QCPColorMap', 'QCPColorMapData', 'QCPColorScale', 'QCPColorScaleAxisRectPrivate', 'QCPCurve', 'QCPCurveData', 'QCPData', 'QCPFinancial', 'QCPFinancialData', 'QCPGraph', 'QCPGrid', 'QCPItemAnchor', 'QCPItemBracket', 'QCPItemCurve', 'QCPItemEllipse', 'QCPItemLine', 'QCPItemPixmap', 'QCPItemPosition', 'QCPItemRect', 'QCPItemStraightLine', 'QCPItemText', 'QCPItemTracer', 'QCPLayer', 'QCPLayerable', 'QCPLayout', 'QCPLayoutElement', 'QCPLayoutGrid', 'QCPLayoutInset', 'QCPLegend', 'QCPLineEnding', 'QCPMarginGroup', 'QCPPainter', 'QCPPlotTitle', 'QCPPlottableLegendItem', 'QCPRange', 'QCPScatterStyle', 'QCPStatisticalBox', 'QCustomPlot', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
    >>> 
    


    Well, in order to be completely beautiful, I give the code of one of the BarsDemo examples :
    Example code
    import sys
    from PyQt5.QtCore import Qt
    from PyQt5.QtWidgets import QApplication
    from PyQt5.QtGui import QColor, QPen
    from qcustomplot import QCustomPlot, QCPBars, QCP
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = QCustomPlot()
        regen = QCPBars(w.xAxis, w.yAxis)
        nuclear = QCPBars(w.xAxis, w.yAxis)
        fossil = QCPBars(w.xAxis, w.yAxis)
        w.addPlottable(regen)
        w.addPlottable(nuclear)
        w.addPlottable(fossil)
        pen = QPen()
        pen.setWidthF(1.2)
        fossil.setName('Fossil fuels')
        pen.setColor(QColor(255, 131, 0))
        fossil.setPen(pen)
        fossil.setBrush(QColor(255, 131, 0, 50))
        nuclear.setName('Nuclear')
        pen.setColor(QColor(1, 92, 192))
        nuclear.setPen(pen)
        nuclear.setBrush(QColor(1, 92, 191, 50))
        regen.setName('Regenerative')
        pen.setColor(QColor(150, 222, 0))
        regen.setPen(pen)
        regen.setBrush(QColor(150, 222, 0, 70))
        nuclear.moveAbove(fossil)
        regen.moveAbove(nuclear)
        ticks = [1, 2, 3, 4, 5, 6, 7]
        labels = ['USA', 'Japan', 'Germany', 'France', 'UK', 'Italy', 'Canada']
        w.xAxis.setAutoTicks(False)
        w.xAxis.setAutoTickLabels(False)
        w.xAxis.setTickVector(ticks)
        w.xAxis.setTickVectorLabels(labels)
        w.xAxis.setTickLabelRotation(60)
        w.xAxis.setSubTickCount(0)
        w.xAxis.grid().setVisible(True)
        w.xAxis.setRange(0, 8)
        w.yAxis.setRange(0, 12.1)
        w.yAxis.setPadding(5)
        w.yAxis.setLabel('Power Consumption in\nKilowatts per Capita (2007)')
        w.yAxis.grid().setSubGridVisible(True)
        grid_pen = QPen()
        grid_pen.setStyle(Qt.SolidLine)
        grid_pen.setColor(QColor(0, 0, 0, 25))
        w.yAxis.grid().setSubGridPen(grid_pen)
        fossil_data = [0.86 * 10.5, 0.83 * 5.5, 0.84 * 5.5, 0.52 * 5.8, 0.89 * 5.2, 0.90 * 4.2, 0.67 * 11.2]
        nuclear_data = [0.08 * 10.5, 0.12 * 5.5, 0.12 * 5.5, 0.40 * 5.8, 0.09 * 5.2, 0.00 * 4.2, 0.07 * 11.2]
        regen_data = [0.06 * 10.5, 0.05 * 5.5, 0.04 * 5.5, 0.06 * 5.8, 0.02 * 5.2, 0.07 * 4.2, 0.25 * 11.2]
        fossil.setData(ticks, fossil_data)
        nuclear.setData(ticks, nuclear_data)
        regen.setData(ticks, regen_data)
        w.legend.setVisible(True)
        w.axisRect().insetLayout().setInsetAlignment(0, Qt.AlignTop|Qt.AlignHCenter)
        w.legend.setBrush(QColor(255, 255, 255, 200))
        legendPen = QPen()
        legendPen.setColor(QColor(130, 130, 130, 200))
        w.legend.setBorderPen(legendPen)
        w.setInteractions(QCP.iRangeDrag or QCP.iRangeZoom)
        w.show()
        sys.exit(app.exec())
    


    Here's what happened:
    Result
    image


    PS


    Link to the source repository: QCustomPlot-PyQt5 . The repositories in the RPMS directory contain SRPM and RPM for Fedora21 (PyQt5, qcustomplot 1.3.1 and python3-qcustomplot).

    All comments and suggestions are welcome. I hope this module comes in handy. Thanks for attention!

    Also popular now: