class RectZoomMoveView(QChartView): """ Filter data to be displayed in rectangular body """ rangeSig = pyqtSignal(list) def __init__(self, parent=None): super(RectZoomMoveView, self).__init__(parent) self.setChart(QChart()) self.chart().setMargins(QMargins(5, 5, 5, 5)) self.chart().setContentsMargins(-10, -10, -10, -10) self.chart().setTitle(" ") self.relationState = True # Define two rectangles for background and drawing respectively self.parentRect = QGraphicsRectItem(self.chart()) self.parentRect.setFlag(QGraphicsItem.ItemClipsChildrenToShape, True) self.parentRect.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) self.RangeItem = RectRangeItem(parent=self.parentRect) self.RangeItem.setZValue(998) pen = QPen(Qt.gray) pen.setWidth(1) self.parentRect.setPen(pen) self.parentRect.setZValue(997) self.scene().addItem(self.parentRect) self.scene().addItem(self.RangeItem) self.RangeItem.hide() self.m_chartRectF = QRectF() self.m_rubberBandOrigin = QPointF(0, 0) self.dataLength = 0 self.RangeItem.selectedChange.connect(self.changeFromRectItem) self.BtnsWidget = ViewButtonsWidget(self) self.BtnsWidget.refreshBtn.clicked.connect(self.updateView) self.BtnsWidget.RelationSig.connect(self.setRelationState) self.BtnsWidget.dateRangeEdit.dateRangeSig.connect(self.changDateRect) def changDateRect(self, daterange): if self.chartTypes == "Bar": v = 3 else: v = 2 l = len(self.RangeItem.rangePoints) if l > 2: try: num = self.mintimeData.date().daysTo(daterange[0]) left = self.RangeItem.rangePoints[num] num = self.mintimeData.date().daysTo(daterange[1]) right = self.RangeItem.rangePoints[num] rect = self.chart().plotArea() rect.setLeft(left) rect.setRight(right) except: rect = self.chart().plotArea() self.RangeItem.setRect(rect) self.RangeItem.updateHandlesPos() else: try: num = self.mintimeData.date().daysTo(daterange[0]) left = self.RangeItem.rangePoints[num] num = self.mintimeData.date().daysTo(daterange[0]) right = self.RangeItem.rangePoints[num] rect = self.chart().plotArea() rect.setLeft(left) rect.setRight(right) except: rect = self.chart().plotArea() self.RangeItem.setRect(rect) self.RangeItem.updateHandlesPos() def lineSpace(self, start, end, num): res = [] if self.chartTypes == "Bar": step = (end - start) / (num) for i in range(num + 2): res.append(start + i * step) else: step = (end - start) / (num - 1) for i in range(num + 1): res.append(start + i * step) return res def getRangePoints(self): count = self.zoomSeries.count() rect = self.chart().plotArea() left = rect.left() right = rect.right() if count == 0: self.RangeItem.rangePoints = [left, right] else: # Get coordinate position for each node self.RangeItem.rangePoints = self.lineSpace(left, right, count) def setRangeColor(self, color): self.RangeItem.setRangeColor(color) def setRelationState(self, state): self.relationState = state def initSeries(self, chartTypes="Bar"): self.chartTypes = chartTypes axisX = QDateTimeAxis() axisX.setFormat("yyyy-MM-dd") self.zoomSeries = QLineSeries(self.chart()) self.chart().addSeries(self.zoomSeries) self.chart().setAxisY(QValueAxis(), self.zoomSeries) self.chart().setAxisX(axisX, self.zoomSeries) self.initView() def clearAll(self): # Clear all series and axes self.chart().removeAllSeries() axess = self.chart().axes() for axes in axess: self.chart().removeAxis(axes) def setData(self, timeData, valueData, chartTypes="Bar"): axisX = QDateTimeAxis() axisX.setFormat("yyyy-MM-dd") if self.chartTypes == "Bar": # Clear all series self.clearAll() self.zoomSeries = QLineSeries(self.chart()) barSeries = QBarSeries(self.chart()) barset = QBarSet("data") barSeries.setBarWidth(0.8) barSeries.append(barset) for td, vd in zip(timeData, valueData): self.zoomSeries.append(td.toMSecsSinceEpoch(), vd) barset.append(valueData) self.zoomSeries.hide() self.chart().addSeries(self.zoomSeries) self.chart().addSeries(barSeries) self.chart().setAxisY(QValueAxis(), self.zoomSeries) axisX.setRange(min(timeData), max(timeData)) self.chart().setAxisX(axisX, self.zoomSeries) elif self.chartTypes == "Scatter": # Clear all series self.clearAll() self.zoomSeries = QLineSeries(self.chart()) scattSeries = QScatterSeries(self.chart()) scattSeries.setMarkerSize(8) for td, vd in zip(timeData, valueData): self.zoomSeries.append(td.toMSecsSinceEpoch(), vd) scattSeries.append(td.toMSecsSinceEpoch(), vd) self.zoomSeries.hide() self.chart().addSeries(self.zoomSeries) self.chart().addSeries(scattSeries) self.chart().setAxisY(QValueAxis(), self.zoomSeries) axisX.setRange(min(timeData), max(timeData)) self.chart().setAxisX(axisX, self.zoomSeries) elif self.chartTypes in ["Line", "PLine"]: self.clearAll() if self.chartTypes == "Line": self.zoomSeries = QLineSeries(self.chart()) else: self.zoomSeries = QSplineSeries(self.chart()) for td, vd in zip(timeData, valueData): self.zoomSeries.append(td.toMSecsSinceEpoch(), vd) self.chart().addSeries(self.zoomSeries) self.chart().setAxisY(QValueAxis(), self.zoomSeries) axisX.setRange(min(timeData), max(timeData)) self.chart().setAxisX(axisX, self.zoomSeries) elif self.chartTypes == "Area": self.clearAll() self.zoomSeries = QLineSeries() self.zoomSeries.setColor(QColor("#666666")) for td, vd in zip(timeData, valueData): self.zoomSeries.append(td.toMSecsSinceEpoch(), vd) areaSeries = QAreaSeries(self.zoomSeries, None) self.chart().addSeries(self.zoomSeries) self.chart().addSeries(areaSeries) self.chart().setAxisY(QValueAxis(), areaSeries) axisX.setRange(min(timeData), max(timeData)) self.chart().setAxisX(axisX, areaSeries) self.zoomSeries.hide() self.mintimeData = min(timeData) self.maxtimeData = max(timeData) self.BtnsWidget.dateRangeEdit.setDateRange([ self.mintimeData.toString("yyyy-MM-dd"), self.maxtimeData.toString("yyyy-MM-dd"), ]) self.updateView() def resetView(self): rect = self.chart().plotArea() self.parentRect.setRect(rect) topRight = self.chart().plotArea().topRight() x = int(topRight.x()) y = int(topRight.y()) self.BtnsWidget.setGeometry(QRect(x - 420, 0, 420, 23)) self.RangeItem.setRect(rect) self.RangeItem.show() self.save_current_rubber_band() self.RangeItem.updateHandlesPos() self.apply_nice_numbers() self.getRangePoints() self.sendRang() def initView(self): self.RangeItem.hide() # Hide y-axis if self.chart().axisY(): self.chart().axisY().setVisible(False) if self.chart().axisX(): self.chart().axisX().setGridLineVisible(False) self.m_chartRectF = QRectF() self.m_rubberBandOrigin = QPointF(0, 0) self.getRangePoints() def updateView(self): self.RangeItem.hide() # Hide y-axis if self.chart().axisY(): self.chart().axisY().setVisible(False) if self.chart().axisX(): self.chart().axisX().setGridLineVisible(False) self.m_chartRectF = QRectF() self.m_rubberBandOrigin = QPointF(0, 0) self.resetView() # Map points to chart def point_to_chart(self, pnt): scene_point = self.mapToScene(pnt) chart_point = self.chart().mapToValue(scene_point) return chart_point # Map chart to points def chart_to_view_point(self, char_coord): scene_point = self.chart().mapToPosition(char_coord) view_point = self.mapFromScene(scene_point) return view_point # Save positions of rectangles def save_current_rubber_band(self): rect = self.RangeItem.rect() chart_top_left = self.point_to_chart(rect.topLeft().toPoint()) self.m_chartRectF.setTopLeft(chart_top_left) chart_bottom_right = self.point_to_chart(rect.bottomRight().toPoint()) self.m_chartRectF.setBottomRight(chart_bottom_right) # Respond to change in positions of rectangles def changeFromRectItem(self, rectIndex): self.save_current_rubber_band() self.sendRang(rectIndex) def sendRang(self, rectIndex=[]): if self.RangeItem.rect() != self.parentRect.rect(): self.BtnsWidget.setPalActive() else: self.BtnsWidget.setPalDisActive() if self.chartTypes == "Bar": v = 3 else: v = 2 if rectIndex == []: maxData = QDateTime.fromMSecsSinceEpoch( self.zoomSeries.at(len(self.RangeItem.rangePoints) - v).x()) minData = QDateTime.fromMSecsSinceEpoch(self.zoomSeries.at(0).x()) else: minData = max(rectIndex[0], 0) maxData = min(rectIndex[1], len(self.RangeItem.rangePoints) - v) minData = QDateTime.fromMSecsSinceEpoch( self.zoomSeries.at(minData).x()) maxData = QDateTime.fromMSecsSinceEpoch( self.zoomSeries.at(maxData).x()) if minData > maxData: if self.RangeItem.handleSelected is None: self.resetView() else: self.BtnsWidget.dateRangeEdit.setDate([ minData.toString("yyyy-MM-dd"), maxData.toString("yyyy-MM-dd") ]) if self.relationState: self.rangeSig.emit([ minData.toString("yyyy-MM-dd HH:mm:ss"), maxData.toString("yyyy-MM-dd HH:mm:ss"), ]) # Change positions of rectangles in scaling def resizeEvent(self, event): super().resizeEvent(event) rect = self.chart().plotArea() self.parentRect.setRect(rect) self.getRangePoints() topRight = self.chart().plotArea().topRight() x = int(topRight.x()) y = int(topRight.y()) self.BtnsWidget.setGeometry(QRect(x - 420, 0, 420, 23)) if self.RangeItem.isVisible(): self.restore_rubber_band() self.save_current_rubber_band() self.RangeItem.updateHandlesPos() else: self.RangeItem.setRect(self.parentRect.rect()) self.RangeItem.show() self.RangeItem.setRect(self.parentRect.rect()) self.save_current_rubber_band() self.RangeItem.updateHandlesPos() self.apply_nice_numbers() # Restore to original positions of rectangles def restore_rubber_band(self): view_top_left = self.chart_to_view_point(self.m_chartRectF.topLeft()) view_bottom_right = self.chart_to_view_point( self.m_chartRectF.bottomRight()) self.m_rubberBandOrigin = view_top_left height = self.chart().plotArea().height() rect = QRectF() rect.setTopLeft(view_top_left) rect.setBottomRight(view_bottom_right) rect.setHeight(height) self.RangeItem.setRect(rect) # Adjust display coordinates of axes automatically def apply_nice_numbers(self): axes_list = self.chart().axes() for value_axis in axes_list: if value_axis: pass
class Graph(QChartView): def __init__(self, parent=None): super().__init__(parent=parent) self.setpoint_temperature = None self.chart = QChart() self.chart.legend().hide() self.setChart( self.chart ) self.setRenderHint(QPainter.Antialiasing) self.chart.setPlotAreaBackgroundBrush( QBrush(Qt.black) ) self.chart.setPlotAreaBackgroundVisible( True ) self.setpointTemperatureSeries = QLineSeries( self.chart ) pen = self.setpointTemperatureSeries.pen() pen.setWidthF(2.) pen.setColor( Qt.green ) self.setpointTemperatureSeries.setPen( pen ) #self.setpointTemperatureSeries.setUseOpenGL( True ) self.chart.addSeries( self.setpointTemperatureSeries ) self.temperatureSeries = QLineSeries( self.chart ) pen = self.temperatureSeries.pen() pen.setWidthF(2.) pen.setColor( Qt.red ) self.temperatureSeries.setPen( pen ) #self.temperatureSeries.setUseOpenGL( True ) self.chart.addSeries( self.temperatureSeries ) self.number_of_samples_to_keep = 2 * 5 * 60 self.xMin = QDateTime.currentDateTime().toMSecsSinceEpoch() self.xMax = QDateTime.currentDateTime().toMSecsSinceEpoch() self.yMin = 400 self.yMax = 0 #self.chart.createDefaultAxes() #x_axis = QValueAxis() x_axis = QDateTimeAxis() x_axis.setTitleText( "Time" ) x_axis.setFormat("HH:mm:ss") self.chart.addAxis( x_axis, Qt.AlignBottom ) self.temperatureSeries.attachAxis( x_axis ) self.setpointTemperatureSeries.attachAxis( x_axis ) startDate = QDateTime.currentDateTime().addSecs( -5 * 60 ) endDate = QDateTime.currentDateTime().addSecs( 5 * 60 ) #startDate = QDateTime(QDate(2017, 1, 9), QTime(17, 25, 0)) #endDate = QDateTime(QDate(2017, 1, 9), QTime(17, 50, 0)) #self.chart.axisX().setRange( startDate, endDate ) #self.chart.axisX().setRange( 0, 100 ) y_axis = QValueAxis() y_axis.setTitleText( "Temperature (K)" ) self.chart.addAxis( y_axis, Qt.AlignLeft ) self.temperatureSeries.attachAxis( y_axis ) self.setpointTemperatureSeries.attachAxis( y_axis ) self.chart.axisY().setRange( 0, 400 ) #self.chart.axisY().setRange( 260., 290. ) self.temperatureSeries.pointAdded.connect( self.Rescale_Axes ) #self.setpointTemperatureSeries.pointAdded.connect( self.Rescale_Axes ) self.setRubberBand( QChartView.HorizontalRubberBand ) # Customize chart title font = QFont() font.setPixelSize(24); self.chart.setTitleFont(font); self.chart.setTitleBrush(QBrush(Qt.white)); ## Customize chart background #backgroundGradient = QLinearGradient() #backgroundGradient.setStart(QPointF(0, 0)); #backgroundGradient.setFinalStop(QPointF(0, 1)); #backgroundGradient.setColorAt(0.0, QColor(0x000147)); #backgroundGradient.setColorAt(1.0, QColor(0x000117)); #backgroundGradient.setCoordinateMode(QGradient.ObjectBoundingMode); #self.chart.setBackgroundBrush(backgroundGradient); transparent_background = QBrush(QColor(0,0,0,0)) self.chart.setBackgroundBrush( transparent_background ) # Customize axis label font labelsFont = QFont() labelsFont.setPixelSize(16); x_axis.setLabelsFont(labelsFont) y_axis.setLabelsFont(labelsFont) x_axis.setTitleFont(labelsFont) y_axis.setTitleFont(labelsFont) # Customize axis colors axisPen = QPen(QColor(0xd18952)) axisPen.setWidth(2) x_axis.setLinePen(axisPen) y_axis.setLinePen(axisPen) # Customize axis label colors axisBrush = QBrush(Qt.white) x_axis.setLabelsBrush(axisBrush) y_axis.setLabelsBrush(axisBrush) x_axis.setTitleBrush(axisBrush) y_axis.setTitleBrush(axisBrush) def set_title(self, title): self.chart.setTitle(title) def add_new_data_point( self, x, y ): x_as_millisecs = x.toMSecsSinceEpoch() self.temperatureSeries.append( x_as_millisecs, y ) if( self.setpoint_temperature ): self.setpointTemperatureSeries.append( x_as_millisecs, self.setpoint_temperature ) num_of_datapoints = self.temperatureSeries.count() #if( num_of_datapoints > self.number_of_samples_to_keep ): # self.number_of_samples_to_keep. #print( x_as_millisecs, y ) #self.chart.scroll( x_as_millisecs - 5 * 60 * 1000, x_as_millisecs ) #self.temperatureSeries.append( x, float(y) ) #self.repaint() def Rescale_Axes( self, index ): x = self.temperatureSeries.at( index ).x() x_rescaled = False if( x < self.xMin ): self.xMin = x x_rescaled = True if( x > self.xMax ): self.xMax = x x_rescaled = True if( x_rescaled ): full_range = min( self.xMax - self.xMin, 5 * 60 * 1000 ) margin = full_range * 0.05 self.chart.axisX().setRange( QDateTime.fromMSecsSinceEpoch(self.xMax - full_range - margin), QDateTime.fromMSecsSinceEpoch(self.xMax + margin) ) y = self.temperatureSeries.at( index ).y() y_rescaled = False if( y < self.yMin ): self.yMin = y y_rescaled = True if( y > self.yMax ): self.yMax = y y_rescaled = True if( y_rescaled ): full_range = self.yMax - self.yMin margin = full_range * 0.05 self.chart.axisY().setRange( self.yMin - margin, self.yMax + margin )