AdvancedFeaturesDemo Example
import sys, os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/../shared")
from DevMachines import __pyside2__, __pyside6__
from DevMachines.QtitanBase import Qtitan
from DevMachines.QtitanChart import (Chart, ChartView2D, ChartBarSeries2D, ChartAreaSeries2D,
ChartLineSeries2D, ChartSplineSeries2D, ChartAxisTitle, ChartLegend, ChartBarSeriesLabel,
ChartHitInfo, ChartMarker)
if __pyside2__:
from PySide2 import QtCore
from PySide2.QtCore import Qt, qAbs
from PySide2.QtGui import QBrush, QIcon, QPainter, QPen, QColor
from PySide2.QtWidgets import (QApplication, QComboBox, QSlider, QLabel,
QCheckBox, QHeaderView, QTableWidget, QTableWidgetItem, QTableWidgetSelectionRange,
QToolBar, QAbstractItemView)
if __pyside6__:
from PySide6 import QtCore
from PySide6.QtCore import Qt, qAbs
from PySide6.QtGui import QBrush, QIcon, QPainter, QPen, QColor
from PySide6.QtWidgets import (QApplication, QComboBox, QSlider, QLabel,
QCheckBox, QHeaderView, QTableWidget, QTableWidgetItem, QTableWidgetSelectionRange,
QToolBar, QAbstractItemView)
from DemoChartWindow import DemoChartWindow
class AdvancedChart(Chart):
def __init__(self, parent = None):
Chart.__init__(self, parent)
self.XLine = -1
self.arMarkers = list()
self.szMarker = QtCore.QSize(5.0, 5.0)
def paintEvent(self, event):
Chart.paintEvent(self, event)
p = QPainter(self)
# Draw markers:
i = 0
for ptMarker in self.arMarkers:
series2D = self.series()[i]
color = series2D.color()
view = series2D.view()
axisY = view.axisY()
rectAxisBounds = axisY.boundingRect()
rectAxisBounds.setWidth(view.boundingRect().width())
if rectAxisBounds.contains(ptMarker):
hints = p.renderHints()
p.setRenderHints(hints, False)
p.setRenderHint(QPainter.Antialiasing)
savePen = p.pen()
p.setPen(QPen(color.darker()))
saveBrush = p.brush()
color.setAlpha(140)
p.setBrush(color)
p.drawEllipse(ptMarker, self.szMarker.width(), self.szMarker.height())
p.setBrush(saveBrush)
p.setPen(savePen)
p.setRenderHints(p.renderHints(), False)
p.setRenderHints(hints, True)
i = i + 1
# Draw vertical line:
if self.XLine != -1.0:
savePen = p.pen()
p.setPen(QPen(QBrush(QColor(Qt.red)), 1.0, Qt.DashLine))
rect = self.viewRect()
p.drawLine(self.XLine, rect.top(), self.XLine, rect.bottom())
p.setPen(savePen)
def resizeEvent(self, event):
Chart.resizeEvent(self, event)
self.arMarkers.clear()
self.XLine = -1.
class MainWindow(DemoChartWindow):
DEFAULT_INFO = "Move the mouse cursor over the chart and see information on hovered chart component"
Combined = 1
SecondaryAxis = 2
Interactive = 3
def __init__(self):
DemoChartWindow.__init__(self, "Advanced Features")
self.setChart(AdvancedChart())
self.createSeriesParametrs()
self.seriesChanged(self.seriesSwitcher.currentIndex())
self.connect(self.chart, QtCore.SIGNAL("chartMouseTrack(ChartHitInfo*)"), self,
QtCore.SLOT("mouseTrack(ChartHitInfo*)"), Qt.DirectConnection)
self.connect(self.chart, QtCore.SIGNAL("chartMouseUp(ChartHitInfo*)"), self,
QtCore.SLOT("mouseUp(ChartHitInfo*)"), Qt.DirectConnection)
def createSeriesParametrs(self):
# Option Series
seriesTypeGroup = self.createGroupParameters(self.tr("Series"))
localLayout = seriesTypeGroup.layout()
self.seriesSwitcher = QComboBox(seriesTypeGroup)
self.seriesSwitcher.addItem(self.tr("Combined"), MainWindow.Combined)
self.seriesSwitcher.addItem(self.tr("Secondary Axis"), MainWindow.SecondaryAxis)
self.seriesSwitcher.addItem(self.tr("Interactive"), MainWindow.Interactive)
self.connect(self.seriesSwitcher, QtCore.SIGNAL("currentIndexChanged(int)"), self,
QtCore.SLOT("seriesChanged(int)"))
# SecondaryAxis
self.showSecondaryAxisX = QCheckBox(self.tr("Secondary X Axis"), seriesTypeGroup)
self.showSecondaryAxisX.setCheckState(Qt.Checked)
self.showSecondaryAxisX.setVisible(False)
self.connect(self.showSecondaryAxisX, QtCore.SIGNAL("stateChanged(int)"),
self, QtCore.SLOT("showSecondaryAxisXChanged(int)"))
self.showSecondaryAxisY = QCheckBox(self.tr("Secondary Y Axis"), seriesTypeGroup)
self.showSecondaryAxisY.setCheckState(Qt.Checked)
self.showSecondaryAxisY.setVisible(False)
self.connect(self.showSecondaryAxisY, QtCore.SIGNAL("stateChanged(int)"), self,
QtCore.SLOT("showSecondaryAxisYChanged(int)"))
# Option Labels
self.createLabelsGroup()
self.dataLabelsGroup.setVisible(False)
self.connect(self.angleDataLabelsSwitcher, QtCore.SIGNAL("currentIndexChanged(int)"),
self, QtCore.SLOT("updateAngleChanged(int)"))
self.hitTestResult = QLabel(MainWindow.DEFAULT_INFO, seriesTypeGroup)
self.hitTestResult.setWordWrap(True)
self.hitTestResult.setVisible(False)
self.hitTestResult.setMinimumWidth(180)
self.hitTestResult.setMaximumWidth(180)
self.hitTestResult.setMinimumHeight(100)
self.hitTestResult.setMaximumHeight(100)
localLayout.addRow(self.seriesSwitcher)
localLayout.addRow(self.showSecondaryAxisX)
localLayout.addRow(self.showSecondaryAxisY)
localLayout.addRow(self.hitTestResult)
def createCombined(self):
self.createTitle(self.tr("Area, Bar and Line"))
self.chart.legend().setVisible(True)
self.chart.legend().setVerticalAlignment(ChartLegend.LegendCenter)
series1 = ChartAreaSeries2D()
series1.setName(self.tr("Area"))
self.chart.appendSeries(series1)
series2 = ChartBarSeries2D()
series2.setTransparency(180)
series2.setName(self.tr("Bar"))
self.chart.appendSeries(series2)
series3 = ChartLineSeries2D()
series3.setName(self.tr("Line"))
self.chart.appendSeries(series3)
arTemp = [
# 0 1 2 3 4 5 6 7 8 9 10 11
[ 28.5, 29.5, 24.5, 29.0, 32.6, 34.5, 36.5, 28.0, 34.6, 36.0, 40.0, 39.0 ], # 0 ChartAreaSeries2D
[ 23.5, 28.0, 34.0, 32.0, 31.0, 25.0, 39.0, 40.0, 42.0, 39.0, 45.0, 50.0 ], # 1 ChartBarSeries2D
[ 3.0, 5.0, 9.0, 13.0, 17.0, 21.0, 25.0, 29.0, 36.0, 42.0, 48.0, 54.0 ]] # 2 ChartLineSeries2D
for month in range(1, 13):
strMonth = QtCore.QLocale.system().monthName(month, QtCore.QLocale.ShortFormat)
series1.addAxisPointY(arTemp[0][month - 1], strMonth)
series2.addAxisPointY(arTemp[1][month - 1], strMonth)
series3.addAxisPointY(arTemp[2][month - 1], strMonth)
def createSecondaryAxis(self):
self.chart.legend().setVisible(True)
self.chart.legend().setVerticalAlignment(ChartLegend.LegendCenter)
series1 = ChartSplineSeries2D()
series1.setName(self.tr("Series1"))
self.chart.appendSeries(series1)
series1.setMarkerType(ChartMarker.Triangle)
series1.setMarkerSize(16)
series1.addXY(1, 0.0)
series1.addXY(2, 2.0)
series1.addXY(3, 5.0)
series1.addXY(4, 3.0)
series1.addXY(5, 3.5)
series1.addXY(6, 5.0)
series1.addXY(7, 8.0)
series1.addXY(8, 7.0)
series2 = ChartSplineSeries2D()
series2.setName(self.tr("Series2"))
self.chart.appendSeries(series2)
series2.setMarkerType(ChartMarker.Star)
series2.setMarkerSize(16)
series2.addXY(1, 2.0)
series2.addXY(2, 6.0)
series2.addXY(3, 12.0)
series2.addXY(4, 16.0)
series2.addXY(5, 14.0)
series2.addXY(6, 9.0)
series2.addXY(7, 6.0)
series2.addXY(8, 2.0)
def createInteractive(self):
self.createTitle(self.tr("Weather Report"))
self.chart.setAreasOrientation(Qt.Vertical)
self.chart.legend().setVisible(True)
self.chart.legend().setVerticalAlignment(ChartLegend.LegendCenter)
view1 = ChartView2D()
self.chart.appendView(view1)
self.axisY1 = view1.axisY()
titleY = self.axisY1.title()
titleY.setText(self.tr("Pressure"))
titleY.setVisible(True)
view1.axisY().setAutoRange(False)
view1.axisY().setFixedRange(15, 25)
view1.axisX().setVisible(False)
view2 = ChartView2D()
self.chart.appendView(view2)
titleY = view2.axisY().title()
titleY.setText(self.tr("Temperature"))
titleY.setVisible(True)
view2.axisY().setAutoRange(False)
view2.axisY().setFixedRange(1012, 1025)
view2.axisX().setVisible(False)
view3 = ChartView2D()
self.chart.appendView(view3)
self.axisY2 = view3.axisY()
titleY = self.axisY2.title()
titleY.setText(self.tr("Humidity,%"))
titleY.setVisible(True)
view3.axisX().title().setText(self.tr("Days"))
view3.axisX().title().setVisible(True)
# Add series and data points:
series1 = ChartLineSeries2D()
series1.setName(self.tr("Temperature"))
series1.setMarkerVisible(False)
series1.label().setVisible(True)
series1.label().setAngle(-270)
view1.appendSeries(series1)
series2 = ChartAreaSeries2D()
series2.setName(self.tr("Pressure"))
series2.setMarkerVisible(False)
series2.label().setVisible(False)
view2.appendSeries(series2)
series3 = ChartBarSeries2D()
series3.setName(self.tr("Humidity"))
series3.label().setVisible(False)
view3.appendSeries(series3)
for day in range(1, 32):
series1.addXY(day, 20.0 + self.getRandomValue(-5.0, 5.0))
series2.addXY(day, 1020.0 + self.getRandomValue(-4.0, 4.0))
series3.addXY(day, 50.0 + self.getRandomValue(-40.0, 40.0))
def updateValueParameters(self):
self.dataLabelsGroup.setVisible(False)
DemoChartWindow.updateValueParameters(self)
index = self.seriesSwitcher.currentIndex()
if index == MainWindow.SecondaryAxis:
self.showSecondaryAxisX.setVisible(True)
self.showSecondaryAxisY.setVisible(True)
if self.showSecondaryAxisX.isVisible() and self.showSecondaryAxisY.isVisible():
self.showSecondaryAxisXChanged(self.showSecondaryAxisX.checkState())
self.showSecondaryAxisYChanged(self.showSecondaryAxisY.checkState())
else:
self.showSecondaryAxisX.setVisible(False)
self.showSecondaryAxisY.setVisible(False)
if index == MainWindow.Combined:
self.dataLabelsGroup.setVisible(True)
self.showDataLabels(self.dataLabelsGroup.isChecked())
if self.angleDataLabelsSwitcher and self.angleDataLabelsSwitcher.isVisible():
self.labelsAngleChanged(self.angleDataLabelsSwitcher.currentIndex())
listSeries = self.chart.series()
for series in listSeries:
if series.inherits("Qtitan::ChartBarSeries2D"):
label = series.label()
label.setPosition(ChartBarSeriesLabel.ChartBarLabelTop)
def seriesChanged(self, index):
self.chart.XLine = -1.
self.chart.arMarkers.clear()
self.chart.clearSeries()
self.chart.clearTitles()
self.chart.setBackgroundBrush(QBrush())
v = self.seriesSwitcher.itemData(index)
if int(v) == MainWindow.Combined:
self.chart.enableMouseTrackingMode(ChartHitInfo.HitNone)
self.hitTestResult.setVisible(False)
self.createCombined()
elif int(v) == MainWindow.SecondaryAxis:
self.chart.enableMouseTrackingMode(ChartHitInfo.HitNone)
self.hitTestResult.setVisible(False)
self.createSecondaryAxis()
elif int(v) == MainWindow.Interactive:
self.chart.enableMouseTrackingMode(ChartHitInfo.HitChartArea | ChartHitInfo.HitTitle | ChartHitInfo.HitDataPoint |
ChartHitInfo.HitLegend | ChartHitInfo.HitAxis | ChartHitInfo.HitAxisName | ChartHitInfo.HitView)
self.hitTestResult.setVisible(True)
self.createInteractive()
self.updateValueParameters()
def showSecondaryAxisXChanged(self, state):
view2D = self.chart.views()[0]
if Qt.Checked != state:
view2D.secondaryAxisX().setVisible(False)
else:
self.showAxisXChanged(Qt.Checked)
titleX = view2D.axisX().title()
if Qt.Checked == state:
view2D.secondaryAxisX().title().setText(self.tr("Series 2 X"))
view2D.secondaryAxisX().title().setVisible(True)
titleX.setText(self.tr("Series 1 X"))
titleX.setVisible(True)
else:
titleX.setText(self.tr("Series 1 and 2 X"))
titleX.setVisible(True)
series = self.chart.series()[1]
series.setSecondaryAxisX(Qt.Checked == state)
def showAxisXChanged(self, state):
view2D = self.chart.views()[0]
view2D.secondaryAxisX().setVisible(Qt.Checked == state)
def showSecondaryAxisYChanged(self, state):
view2D = self.chart.views()[0]
if Qt.Checked != state:
view2D.secondaryAxisY().setVisible(False)
else:
self.showAxisYChanged(Qt.Checked)
titleY = view2D.axisY().title()
if Qt.Checked == state:
view2D.secondaryAxisY().title().setText(self.tr("Series 2 Y"))
view2D.secondaryAxisY().title().setVisible(True)
titleY.setText(self.tr("Series 1 Y"))
titleY.setVisible(True)
else:
titleY.setText(self.tr("Series 1 and 2 Y"))
titleY.setVisible(True)
series = self.chart.series()[1]
series.setSecondaryAxisX(Qt.Checked == state)
def showAxisYChanged(self, state):
view2D = self.chart.views()[0]
view2D.secondaryAxisY().setVisible(Qt.Checked == state)
def updateAngleChanged(self, angle):
self.updateValueParameters()
def mouseTrack(self, hit):
listViews = self.chart.views()
if hit == None or len(listViews) == 0:
return
view2D = listViews[0]
pt = hit.hitPoint()
axisXX = view2D.axisX()
# Get X Position on the chart
realXMarker = axisXX.valueFromPoint(pt)
# left marker index
xValue = int(realXMarker)
redraw = False
seriesCount = len(self.chart.series())
DemoChartWindow.resize_list(self.chart.arMarkers, seriesCount, QtCore.QPoint(0, 0))
i = 0
for series2D in self.chart.series():
dp1 = series2D.at(xValue)
dp2 = series2D.at(xValue + 1)
if dp1 != None and dp2 != None:
view = series2D.view()
axisX = view.axisX()
axisY = view.axisY()
x1 = axisX.valueToPoint(dp1.valueX())
x2 = axisX.valueToPoint(dp2.valueX())
y1 = axisY.valueToPoint(dp1.valueY())
y2 = axisY.valueToPoint(dp2.valueY())
pt1 = QtCore.QPoint(x1, y1)
pt2 = QtCore.QPoint(x2, y2)
ptMarker = None
if qAbs(pt.x() - pt1.x()) < qAbs(pt.x() - pt2.x()):
ptMarker = pt1
else:
ptMarker = pt2
if len(self.chart.arMarkers) < seriesCount or self.chart.arMarkers[i] != ptMarker:
self.chart.arMarkers[i] = ptMarker
if i == 0:
self.chart.XLine = ptMarker.x()
redraw = True
i = i + 1
if redraw:
self.chart.repaint()
cursor = Qt.ArrowCursor
strInfo = ""
if hit.hitInfo() == ChartHitInfo.HitDataPoint:
strInfo = "Hovered Element: Data Point\nClick left mouse button to get additional information"
cursor = Qt.PointingHandCursor
elif hit.hitInfo() == ChartHitInfo.HitChartArea:
strInfo = "Hovered Element: Chart Area"
elif hit.hitInfo() == ChartHitInfo.HitAxis or hit.hitInfo() == ChartHitInfo.HitAxisName:
axis = None
strName = "NoName"
value = 0.0
for view in listViews:
axisX = view.axisX()
axisY = view.axisY()
if axisX.axisID() == hit.axisID1():
axis = axisX
elif axisY.axisID() == hit.axisID1():
axis = axisY
elif self.axisY1.axisID() == hit.axisID1():
axis = self.axisY1
elif self.axisY2.axisID() == hit.axisID1():
axis = self.axisY2
if hit.hitInfo() == ChartHitInfo.HitAxisName:
strInfo = "Hovered Element: Axis Name\r\nAxis Name: {}".format(strName)
else:
strInfo = "Hovered Element: Axis\r\nAxis Name: {}\r\nValue = {}".format(strName, value)
if axis != None:
strName = axis.title().text()
value = axis.valueFromPoint(pt)
break
elif hit.hitInfo() == ChartHitInfo.HitTitle:
strInfo = "Hovered Element: Chart Title"
elif hit.hitInfo() == ChartHitInfo.HitView:
for view in listViews:
axisX = view.axis(hit.axisID1())
axisY = view.axis(hit.axisID2())
if axisX != None and axisY != None:
valueX = int(axisX.valueFromPoint(pt) + 1.5)
valueY = axisY.valueFromPoint(pt)
strInfo = "Hovered Element: Chart View\r\nX Value: {}\r\nY Value: {}".format(valueX, valueY)
break;
if strInfo == "":
strInfo = "Hovered Element: Chart View"
elif hit.hitInfo() == ChartHitInfo.HitLegend:
strInfo = "Hovered Element: Legend"
else:
strInfo = MainWindow.DEFAULT_INFO
self.hitTestResult.setText(strInfo)
self.setCursor(cursor)
def mouseUp(self, hit):
if hit.mouseButton == Qt.NoButton or hit.hitInfo != ChartHitInfo.HitDataPoint:
return
series = self.chart.series()[hit.index()]
strInfo = "Clicked on the Data Point:\r\n\nSeries Index: {}\r\nSeries Name: {}\r\nData Point Index: {}\r\nData Point Value: {}".format(
hit.index(), series.name(), hit.pointIndex(), series.at(hit.pointIndex()).valueY())
QMessageBox.information(self, qApp.applicationName(), strInfo)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MainWindow()
w.show()
if __pyside2__:
sys.exit(app.exec_())
if __pyside6__:
sys.exit(app.exec())