diff options
Diffstat (limited to 'examples/charts')
-rw-r--r-- | examples/charts/donutbreakdown.py | 181 | ||||
-rw-r--r-- | examples/charts/legend.py | 252 | ||||
-rw-r--r-- | examples/charts/modeldata.py | 183 | ||||
-rw-r--r-- | examples/charts/nesteddonuts.py | 137 | ||||
-rw-r--r-- | examples/charts/percentbarchart.py | 99 |
5 files changed, 852 insertions, 0 deletions
diff --git a/examples/charts/donutbreakdown.py b/examples/charts/donutbreakdown.py new file mode 100644 index 000000000..28a48b8ac --- /dev/null +++ b/examples/charts/donutbreakdown.py @@ -0,0 +1,181 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +"""PySide2 port of the Donut Chart Breakdown example from Qt v5.x""" + + +import sys +from PySide2.QtCore import Qt +from PySide2.QtGui import QColor, QFont, QPainter +from PySide2.QtWidgets import QApplication, QMainWindow +from PySide2.QtCharts import QtCharts + +class MainSlice(QtCharts.QPieSlice): + def __init__(self, breakdown_series, parent=None): + super(MainSlice, self).__init__(parent) + + self.breakdown_series = breakdown_series + self.name = None + + self.percentageChanged.connect(self.update_label) + + def get_breakdown_series(self): + return self.breakdown_series + + def setName(self, name): + self.name = name + + def name(self): + return self.name + + def update_label(self): + self.setLabel("{} {:.2f}%".format(self.name, + self.percentage() * 100)) + + +class DonutBreakdownChart(QtCharts.QChart): + def __init__(self, parent=None): + super(DonutBreakdownChart, self).__init__(QtCharts.QChart.ChartTypeCartesian, parent, Qt.WindowFlags()) + self.main_series = QtCharts.QPieSeries() + self.main_series.setPieSize(0.7) + self.addSeries(self.main_series) + + def add_breakdown_series(self, breakdown_series, color): + font = QFont("Arial", 8) + + # add breakdown series as a slice to center pie + main_slice = MainSlice(breakdown_series) + main_slice.setName(breakdown_series.name()) + main_slice.setValue(breakdown_series.sum()) + self.main_series.append(main_slice) + + # customize the slice + main_slice.setBrush(color) + main_slice.setLabelVisible() + main_slice.setLabelColor(Qt.white) + main_slice.setLabelPosition(QtCharts.QPieSlice.LabelInsideHorizontal) + main_slice.setLabelFont(font) + + # position and customize the breakdown series + breakdown_series.setPieSize(0.8) + breakdown_series.setHoleSize(0.7) + breakdown_series.setLabelsVisible() + + for pie_slice in breakdown_series.slices(): + color = QColor(color).lighter(115) + pie_slice.setBrush(color) + pie_slice.setLabelFont(font) + + # add the series to the chart + self.addSeries(breakdown_series) + + # recalculate breakdown donut segments + self.recalculate_angles() + + # update customize legend markers + self.update_legend_markers() + + def recalculate_angles(self): + angle = 0 + slices = self.main_series.slices(); + for pie_slice in slices: + breakdown_series = pie_slice.get_breakdown_series() + breakdown_series.setPieStartAngle(angle) + angle += pie_slice.percentage() * 360.0 # full pie is 360.0 + breakdown_series.setPieEndAngle(angle) + + def update_legend_markers(self): + # go through all markers + for series in self.series(): + markers = self.legend().markers(series) + for marker in markers: + if series == self.main_series: + # hide markers from main series + marker.setVisible(False) + else: + # modify markers from breakdown series + marker.setLabel("{} {:.2f}%".format( + marker.slice().label(), + marker.slice().percentage() * 100, 0)) + marker.setFont(QFont("Arial", 8)) + +if __name__ == "__main__": + app = QApplication(sys.argv) + # Graph is based on data of: + # 'Total consumption of energy increased by 10 per cent in 2010' + # Statistics Finland, 13 December 2011 + # http://www.stat.fi/til/ekul/2010/ekul_2010_2011-12-13_tie_001_en.html + series1 = QtCharts.QPieSeries() + series1.setName("Fossil fuels") + series1.append("Oil", 353295) + series1.append("Coal", 188500) + series1.append("Natural gas", 148680) + series1.append("Peat", 94545) + + series2 = QtCharts.QPieSeries() + series2.setName("Renewables") + series2.append("Wood fuels", 319663) + series2.append("Hydro power", 45875) + series2.append("Wind power", 1060) + + series3 = QtCharts.QPieSeries() + series3.setName("Others") + series3.append("Nuclear energy", 238789) + series3.append("Import energy", 37802) + series3.append("Other", 32441) + + donut_breakdown = DonutBreakdownChart() + donut_breakdown.setAnimationOptions(QtCharts.QChart.AllAnimations) + donut_breakdown.setTitle("Total consumption of energy in Finland 2010") + donut_breakdown.legend().setAlignment(Qt.AlignRight) + donut_breakdown.add_breakdown_series(series1, Qt.red) + donut_breakdown.add_breakdown_series(series2, Qt.darkGreen) + donut_breakdown.add_breakdown_series(series3, Qt.darkBlue) + + window = QMainWindow() + chart_view = QtCharts.QChartView(donut_breakdown) + chart_view.setRenderHint(QPainter.Antialiasing) + window.setCentralWidget(chart_view) + available_geometry = app.desktop().availableGeometry(window) + size = available_geometry.height() * 0.75 + window.resize(size, size * 0.8) + window.show() + + sys.exit(app.exec_()) diff --git a/examples/charts/legend.py b/examples/charts/legend.py new file mode 100644 index 000000000..47099e729 --- /dev/null +++ b/examples/charts/legend.py @@ -0,0 +1,252 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +"""PySide2 port of the Legend example from Qt v5.x""" + +import sys +from PySide2.QtCore import Qt, QRectF +from PySide2.QtGui import QBrush, QColor, QPainter, QPen +from PySide2.QtWidgets import (QApplication, QDoubleSpinBox, + QFormLayout, QGridLayout, QGroupBox, QPushButton, QWidget) +from PySide2.QtCharts import QtCharts + +class MainWidget(QWidget): + def __init__(self, parent=None): + super(MainWidget, self).__init__(parent) + self.chart = QtCharts.QChart() + self.series = QtCharts.QBarSeries() + + self.main_layout = QGridLayout() + self.button_layout = QGridLayout() + self.font_layout = QFormLayout() + + self.font_size = QDoubleSpinBox() + + self.legend_posx = QDoubleSpinBox() + self.legend_posy = QDoubleSpinBox() + self.legend_width = QDoubleSpinBox() + self.legend_height = QDoubleSpinBox() + + self.detach_legend_button = QPushButton("Toggle attached") + self.detach_legend_button.clicked.connect(self.toggle_attached) + self.button_layout.addWidget(self.detach_legend_button, 0, 0) + + self.add_set_button = QPushButton("add barset") + self.add_set_button.clicked.connect(self.add_barset) + self.button_layout.addWidget(self.add_set_button, 2, 0) + + self.remove_barset_button = QPushButton("remove barset") + self.remove_barset_button.clicked.connect(self.remove_barset) + self.button_layout.addWidget(self.remove_barset_button, 3, 0) + + self.align_button = QPushButton("Align (Bottom)") + self.align_button.clicked.connect(self.set_legend_alignment) + self.button_layout.addWidget(self.align_button, 4, 0) + + self.bold_button = QPushButton("Toggle bold") + self.bold_button.clicked.connect(self.toggle_bold) + self.button_layout.addWidget(self.bold_button, 8, 0) + + self.italic_button = QPushButton("Toggle italic") + self.italic_button.clicked.connect(self.toggle_italic) + self.button_layout.addWidget(self.italic_button, 9, 0) + + self.legend_posx.valueChanged.connect(self.update_legend_layout) + self.legend_posy.valueChanged.connect(self.update_legend_layout) + self.legend_width.valueChanged.connect(self.update_legend_layout) + self.legend_height.valueChanged.connect(self.update_legend_layout) + + legend_layout = QFormLayout() + legend_layout.addRow("HPos", self.legend_posx) + legend_layout.addRow("VPos", self.legend_posy) + legend_layout.addRow("Width", self.legend_width) + legend_layout.addRow("Height", self.legend_height) + + self.legend_settings = QGroupBox("Detached legend") + self.legend_settings.setLayout(legend_layout) + self.button_layout.addWidget(self.legend_settings) + self.legend_settings.setVisible(False) + + # Create chart view with the chart + self.chart_view = QtCharts.QChartView(self.chart, self) + + # Create spinbox to modify font size + self.font_size.setValue(self.chart.legend().font().pointSizeF()) + self.font_size.valueChanged.connect(self.font_size_changed) + + self.font_layout.addRow("Legend font size", self.font_size) + + # Create layout for grid and detached legend + self.main_layout.addLayout(self.button_layout, 0, 0) + self.main_layout.addLayout(self.font_layout, 1, 0) + self.main_layout.addWidget(self.chart_view, 0, 1, 3, 1) + self.setLayout(self.main_layout) + + self.create_series() + + def create_series(self): + self.add_barset() + self.add_barset() + self.add_barset() + self.add_barset() + + self.chart.addSeries(self.series) + self.chart.setTitle("Legend detach example") + self.chart.createDefaultAxes() + + self.chart.legend().setVisible(True) + self.chart.legend().setAlignment(Qt.AlignBottom) + + self.chart_view.setRenderHint(QPainter.Antialiasing) + + def show_legend_spinbox(self): + self.legend_settings.setVisible(True) + chart_viewrect = self.chart_view.rect() + + self.legend_posx.setMinimum(0) + self.legend_posx.setMaximum(chart_viewrect.width()) + self.legend_posx.setValue(150) + + self.legend_posy.setMinimum(0) + self.legend_posy.setMaximum(chart_viewrect.height()) + self.legend_posy.setValue(150) + + self.legend_width.setMinimum(0) + self.legend_width.setMaximum(chart_viewrect.width()) + self.legend_width.setValue(150) + + self.legend_height.setMinimum(0) + self.legend_height.setMaximum(chart_viewrect.height()) + self.legend_height.setValue(75) + + def hideLegendSpinbox(self): + self.legend_settings.setVisible(False) + + def toggle_attached(self): + legend = self.chart.legend() + if legend.isAttachedToChart(): + legend.detachFromChart() + legend.setBackgroundVisible(True) + legend.setBrush(QBrush(QColor(128, 128, 128, 128))) + legend.setPen(QPen(QColor(192, 192, 192, 192))) + + self.show_legend_spinbox() + self.update_legend_layout() + else: + legend.attachToChart() + legend.setBackgroundVisible(False) + self.hideLegendSpinbox() + self.update() + + def add_barset(self): + series_count = self.series.count() + bar_set = QtCharts.QBarSet("set {}".format(series_count)) + delta = series_count * 0.1 + bar_set.append([1 + delta, 2 + delta, 3 + delta, 4 + delta]) + self.series.append(bar_set) + + def remove_barset(self): + sets = self.series.barSets() + len_sets = len(sets) + if len_sets > 0: + self.series.remove(sets[len_sets - 1]) + + def set_legend_alignment(self): + button = self.sender() + legend = self.chart.legend() + alignment = legend.alignment() + + if alignment == Qt.AlignTop: + legend.setAlignment(Qt.AlignLeft) + if button: + button.setText("Align (Left)") + elif alignment == Qt.AlignLeft: + legend.setAlignment(Qt.AlignBottom) + if button: + button.setText("Align (Bottom)") + elif alignment == Qt.AlignBottom: + legend.setAlignment(Qt.AlignRight) + if button: + button.setText("Align (Right)") + else: + if button: + button.setText("Align (Top)") + legend.setAlignment(Qt.AlignTop) + + def toggle_bold(self): + legend = self.chart.legend() + font = legend.font() + font.setBold(not font.bold()) + legend.setFont(font) + + def toggle_italic(self): + legend = self.chart.legend() + font = legend.font() + font.setItalic(not font.italic()) + legend.setFont(font) + + def font_size_changed(self): + legend = self.chart.legend() + font = legend.font() + font_size = self.font_size.value() + if font_size < 1: + font_size = 1 + font.setPointSizeF(font_size) + legend.setFont(font) + + def update_legend_layout(self): + legend = self.chart.legend() + + rect = QRectF(self.legend_posx.value(), + self.legend_posy.value(), + self.legend_width.value(), + self.legend_height.value()) + legend.setGeometry(rect) + + legend.update() + +if __name__ == "__main__": + app = QApplication(sys.argv) + w = MainWidget() + available_geometry = app.desktop().availableGeometry(w) + size = available_geometry.height() * 0.75 + w.setFixedSize(size, size) + w.show() + sys.exit(app.exec_()) diff --git a/examples/charts/modeldata.py b/examples/charts/modeldata.py new file mode 100644 index 000000000..018e05345 --- /dev/null +++ b/examples/charts/modeldata.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python + +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +"""PySide2 port of the Model Data example from Qt v5.x""" + +import sys +from random import randrange + +from PySide2.QtCore import QAbstractTableModel, QModelIndex, QRect, Qt +from PySide2.QtGui import QColor, QPainter +from PySide2.QtWidgets import (QApplication, QGridLayout, QHeaderView, + QTableView, QWidget) +from PySide2.QtCharts import QtCharts + +class CustomTableModel(QAbstractTableModel): + def __init__(self): + QAbstractTableModel.__init__(self) + self.input_data = [] + self.mapping = {} + self.column_count = 4 + self.row_count = 15 + + for i in range(self.row_count): + data_vec = [0]*self.column_count + for k in range(len(data_vec)): + if k % 2 == 0: + data_vec[k] = i * 50 + randrange(30) + else: + data_vec[k] = randrange(100) + self.input_data.append(data_vec) + + def rowCount(self, parent=QModelIndex()): + return len(self.input_data) + + def columnCount(self, parent=QModelIndex()): + return self.column_count + + def headerData(self, section, orientation, role): + if role != Qt.DisplayRole: + return None + + if orientation == Qt.Horizontal: + if section % 2 == 0: + return "x" + else: + return "y" + else: + return "{}".format(section + 1) + + def data(self, index, role=Qt.DisplayRole): + if role == Qt.DisplayRole: + return self.input_data[index.row()][index.column()] + elif role == Qt.EditRole: + return self.input_data[index.row()][index.column()] + elif role == Qt.BackgroundRole: + for color, rect in self.mapping.items(): + if rect.contains(index.column(), index.row()): + return QColor(color) + # cell not mapped return white color + return QColor(Qt.white); + return None + + def setData(self, index, value, role=Qt.EditRole): + if index.isValid() and role == Qt.EditRole: + self.input_data[index.row()][index.column()] = float(value) + self.dataChanged.emit(index, index) + return True + return False + + def flags(self, index): + return Qt.ItemIsEnabled | Qt.ItemIsEditable | Qt.ItemIsSelectable + + def add_mapping(self, color, area): + self.mapping[color] = area + + def clear_mapping(self): + self.mapping = {} + + + +class TableWidget(QWidget): + def __init__(self): + QWidget.__init__(self) + + self.model = CustomTableModel() + + self.table_view = QTableView() + self.table_view.setModel(self.model) + self.table_view.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + self.table_view.verticalHeader().setSectionResizeMode(QHeaderView.Stretch) + + self.chart = QtCharts.QChart() + self.chart.setAnimationOptions(QtCharts.QChart.AllAnimations) + + self.series = QtCharts.QLineSeries() + self.series.setName("Line 1") + self.mapper = QtCharts.QVXYModelMapper(self) + self.mapper.setXColumn(0) + self.mapper.setYColumn(1) + self.mapper.setSeries(self.series) + self.mapper.setModel(self.model) + self.chart.addSeries(self.series) + + # for storing color hex from the series + seriesColorHex = "#000000" + + # get the color of the series and use it for showing the mapped area + seriesColorHex = "{}".format(self.series.pen().color().name()) + self.model.add_mapping(seriesColorHex, QRect(0, 0, 2, self.model.rowCount())) + + # series 2 + self.series = QtCharts.QLineSeries() + self.series.setName("Line 2") + + self.mapper = QtCharts.QVXYModelMapper(self) + self.mapper.setXColumn(2) + self.mapper.setYColumn(3) + self.mapper.setSeries(self.series) + self.mapper.setModel(self.model) + self.chart.addSeries(self.series) + + # get the color of the series and use it for showing the mapped area + seriesColorHex = "{}".format(self.series.pen().color().name()) + self.model.add_mapping(seriesColorHex, QRect(2, 0, 2, self.model.rowCount())); + + self.chart.createDefaultAxes() + self.chart_view = QtCharts.QChartView(self.chart) + self.chart_view.setRenderHint(QPainter.Antialiasing) + self.chart_view.setMinimumSize(640, 480) + + # create main layout + self.main_layout = QGridLayout() + self.main_layout.addWidget(self.table_view, 1, 0) + self.main_layout.addWidget(self.chart_view, 1, 1) + self.main_layout.setColumnStretch(1, 1) + self.main_layout.setColumnStretch(0, 0) + self.setLayout(self.main_layout) + + +if __name__ == "__main__": + app = QApplication(sys.argv) + w = TableWidget() + w.show() + sys.exit(app.exec_()) diff --git a/examples/charts/nesteddonuts.py b/examples/charts/nesteddonuts.py new file mode 100644 index 000000000..bee3c1c79 --- /dev/null +++ b/examples/charts/nesteddonuts.py @@ -0,0 +1,137 @@ +#!/usr/bin/python + +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +"""PySide2 port of the Nested Donuts example from Qt v5.x""" + +import sys + +from PySide2.QtCore import Qt, QTimer +from PySide2.QtGui import QPainter +from PySide2.QtWidgets import QApplication, QGridLayout, QWidget +from PySide2.QtCharts import QtCharts + +from random import randrange +from functools import partial + +class Widget(QWidget): + def __init__(self): + QWidget.__init__(self) + self.setMinimumSize(800, 600) + self.donuts = [] + self.chart_view = QtCharts.QChartView() + self.chart_view.setRenderHint(QPainter.Antialiasing) + self.chart = self.chart_view.chart() + self.chart.legend().setVisible(False) + self.chart.setTitle("Nested donuts demo") + self.chart.setAnimationOptions(QtCharts.QChart.AllAnimations) + + self.min_size = 0.1 + self.max_size = 0.9 + self.donut_count = 5 + + self.setup_donuts() + + # create main layout + self.main_layout = QGridLayout(self) + self.main_layout.addWidget(self.chart_view, 1, 1) + self.setLayout(self.main_layout) + + self.update_timer = QTimer(self) + self.update_timer.timeout.connect(self.update_rotation) + self.update_timer.start(1250) + + def setup_donuts(self): + for i in range(self.donut_count): + donut = QtCharts.QPieSeries() + slccount = randrange(3, 6) + for j in range(slccount): + value = randrange(100, 200) + + slc = QtCharts.QPieSlice(str(value), value) + slc.setLabelVisible(True) + slc.setLabelColor(Qt.white) + slc.setLabelPosition(QtCharts.QPieSlice.LabelInsideTangential) + + # Connection using an extra parameter for the slot + slc.hovered[bool].connect(partial(self.explode_slice, slc=slc)) + + donut.append(slc) + size = (self.max_size - self.min_size)/self.donut_count + donut.setHoleSize(self.min_size + i * size) + donut.setPieSize(self.min_size + (i + 1) * size) + + self.donuts.append(donut) + self.chart_view.chart().addSeries(donut) + + + + def update_rotation(self): + for donut in self.donuts: + phase_shift = randrange(-50, 100) + donut.setPieStartAngle(donut.pieStartAngle() + phase_shift) + donut.setPieEndAngle(donut.pieEndAngle() + phase_shift) + + def explode_slice(self, exploded, slc): + if exploded: + self.update_timer.stop() + slice_startangle = slc.startAngle() + slice_endangle = slc.startAngle() + slc.angleSpan() + + donut = slc.series() + idx = self.donuts.index(donut) + for i in range(idx + 1, len(self.donuts)): + self.donuts[i].setPieStartAngle(slice_endangle) + self.donuts[i].setPieEndAngle(360 + slice_startangle) + else: + for donut in self.donuts: + donut.setPieStartAngle(0) + donut.setPieEndAngle(360) + + self.update_timer.start() + + slc.setExploded(exploded) + +if __name__ == "__main__": + app = QApplication(sys.argv) + w = Widget() + w.show() + sys.exit(app.exec_()) diff --git a/examples/charts/percentbarchart.py b/examples/charts/percentbarchart.py new file mode 100644 index 000000000..f92920602 --- /dev/null +++ b/examples/charts/percentbarchart.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python + +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +"""PySide2 port of the Percent Bar Chart example from Qt v5.x""" + +import sys +from PySide2.QtCore import Qt +from PySide2.QtGui import QPainter +from PySide2.QtWidgets import (QMainWindow, QApplication) +from PySide2.QtCharts import QtCharts + +class MainWindow(QMainWindow): + def __init__(self): + QMainWindow.__init__(self) + + set0 = QtCharts.QBarSet("Jane") + set1 = QtCharts.QBarSet("John") + set2 = QtCharts.QBarSet("Axel") + set3 = QtCharts.QBarSet("Mary") + set4 = QtCharts.QBarSet("Samantha") + + set0.append([1, 2, 3, 4, 5, 6]) + set1.append([5, 0, 0, 4, 0, 7]) + set2.append([3, 5, 8, 13, 8, 5]) + set3.append([5, 6, 7, 3, 4, 5]) + set4.append([9, 7, 5, 3, 1, 2]) + + series = QtCharts.QPercentBarSeries() + series.append(set0) + series.append(set1) + series.append(set2) + series.append(set3) + series.append(set4) + + chart = QtCharts.QChart() + chart.addSeries(series) + chart.setTitle("Simple percentbarchart example") + chart.setAnimationOptions(QtCharts.QChart.SeriesAnimations) + + categories = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"] + axis = QtCharts.QBarCategoryAxis() + axis.append(categories) + chart.createDefaultAxes() + chart.setAxisX(axis, series) + + chart.legend().setVisible(True) + chart.legend().setAlignment(Qt.AlignBottom) + + chart_view = QtCharts.QChartView(chart) + chart_view.setRenderHint(QPainter.Antialiasing) + + self.setCentralWidget(chart_view) + + +if __name__ == "__main__": + app = QApplication(sys.argv) + w = MainWindow() + w.resize(420, 300) + w.show() + sys.exit(app.exec_()) |