class ChartView(QChartView,QChart): def __init__(self, *args, **kwargs): super(ChartView, self).__init__(*args, **kwargs) self.resize(800, 600) self.setRenderHint(QPainter.Antialiasing) # 抗锯齿 self.chart_init() self.timer_init() def timer_init(self): #使用QTimer,2秒触发一次,更新数据 self.timer = QTimer(self) self.timer.timeout.connect(self.drawLine) self.timer.start(100) def chart_init(self): self.chart = QChart() self.series = QScatterSeries() #设置曲线名称 self.series.setName("实时数据") self.series.setColor(QColor(255,0,0)) self.series.setMarkerSize(20.0) self.series.setPen(QPen(QtCore.Qt.PenStyle.NoPen)) #把曲线添加到QChart的实例中 self.chart.addSeries(self.series) #声明并初始化X轴,Y轴 self.dtaxisX = QDateTimeAxis() self.vlaxisY = QValueAxis() #设置坐标轴显示范围 self.dtaxisX.setMin(QDateTime.currentDateTime().addSecs(-300*1)) self.dtaxisX.setMax(QDateTime.currentDateTime().addSecs(0)) self.vlaxisY.setMin(0) self.vlaxisY.setMax(1500) #设置X轴时间样式 self.dtaxisX.setFormat("MM月dd hh:mm:ss") #设置坐标轴上的格点 self.dtaxisX.setTickCount(6) self.vlaxisY.setTickCount(11) #设置坐标轴名称 self.dtaxisX.setTitleText("时间") self.vlaxisY.setTitleText("量程") #设置网格不显示` #把坐标轴添加到chart中 self.chart.addAxis(self.dtaxisX,Qt.AlignBottom) self.chart.addAxis(self.vlaxisY,Qt.AlignLeft) #把曲线关联到坐标轴 self.series.attachAxis(self.dtaxisX) self.series.attachAxis(self.vlaxisY) self.setChart(self.chart) def drawLine(self): #获取当前时间 bjtime = QDateTime.currentDateTime() #更新X轴坐标 self.dtaxisX.setMin(QDateTime.currentDateTime().addSecs(-300*1)) self.dtaxisX.setMax(QDateTime.currentDateTime().addSecs(0)) #当曲线上的点超出X轴的范围时,移除最早的点 if(self.series.count()>149): self.series.removePoints(0,self.series.count()-149) #产生随即数 yint = random.randint(0,1500) #添加数据到曲线末端 self.series.append(bjtime.toMSecsSinceEpoch(),yint)
chart.setTitle( "Use arrow keys to scroll, +/- to zoom, and space to switch chart type.") angularAxis = QValueAxis() angularAxis.setTickCount(9) angularAxis.setLabelFormat('%.1f') angularAxis.setShadesVisible(True) angularAxis.setShadesBrush(QBrush(QColor(249, 249, 255))) chart.addAxis(angularAxis, QPolarChart.PolarOrientationAngular) radialAxis = QValueAxis() radialAxis.setTickCount(9) radialAxis.setLabelFormat('%d') chart.addAxis(radialAxis, QPolarChart.PolarOrientationRadial) series1.attachAxis(radialAxis) series1.attachAxis(angularAxis) series2.attachAxis(radialAxis) series2.attachAxis(angularAxis) series3.attachAxis(radialAxis) series3.attachAxis(angularAxis) series4.attachAxis(radialAxis) series4.attachAxis(angularAxis) series5.attachAxis(radialAxis) series5.attachAxis(angularAxis) radialAxis.setRange(RADIAL_MIN, RADIAL_MAX) angularAxis.setRange(ANGULAR_MIN, ANGULAR_MAX) chartView = ChartView() chartView.setChart(chart)
class PriceFigure: def __init__(self, name): self.name = name self.chart_view = QChartView() self.price_time_axis = QDateTimeAxis() self.price_time_axis.setFormat('h:mm') self.price_axis = QValueAxis() self.candle_stick_series = QCandlestickSeries() self.candle_stick_series.setIncreasingColor(Qt.red) self.candle_stick_series.setDecreasingColor(Qt.blue) self.moving_average_series = QLineSeries() self.top_edge_series = QScatterSeries() self.bottom_edge_series = QScatterSeries() self.trend_lines = [] self.short_top_trend_series = QLineSeries() self.short_bottom_trend_series = QLineSeries() self.long_top_trend_series = QLineSeries() self.long_bottom_trend_series = QLineSeries() self.trend_lines.append(self.short_top_trend_series) self.trend_lines.append(self.short_bottom_trend_series) self.trend_lines.append(self.long_top_trend_series) self.trend_lines.append(self.long_bottom_trend_series) self.chart_view.chart().addSeries(self.candle_stick_series) self.chart_view.chart().addSeries(self.moving_average_series) self.chart_view.chart().addSeries(self.top_edge_series) self.chart_view.chart().addSeries(self.bottom_edge_series) self.chart_view.chart().addSeries(self.short_top_trend_series) self.chart_view.chart().addSeries(self.long_top_trend_series) self.chart_view.chart().addSeries(self.short_bottom_trend_series) self.chart_view.chart().addSeries(self.long_bottom_trend_series) self.chart_view.chart().addAxis(self.price_time_axis, Qt.AlignBottom) self.chart_view.chart().addAxis(self.price_axis, Qt.AlignLeft) self.chart_view.chart().legend().hide() self.chart_view.setRenderHint(QPainter.Antialiasing) self.set_marker_color() self.set_trend_line_pen() def set_trend_line_pen(self): brushes = [ QBrush(QColor(255, 0, 0, 90)), QBrush(QColor(0, 0, 255, 90)), QBrush(QColor(205, 56, 47, 255)), QBrush(QColor(0, 153, 213, 255)) ] for i, tl in enumerate(self.trend_lines): tl.setPen(QPen(brushes[i], 4, Qt.DotLine)) def set_marker_color(self): self.top_edge_series.setPen(Qt.black) self.top_edge_series.setBrush(QBrush(QColor(255, 0, 255, 90))) self.bottom_edge_series.setPen(Qt.black) self.bottom_edge_series.setBrush(QBrush(QColor(0, 255, 255, 90))) def set_datetime(self, d): self.chart_datetime = d self.datetime_range = (d.timestamp() * 1000, d.replace(hour=23, minute=59).timestamp() * 1000) start_time = QDateTime() until_time = QDateTime() start_time.setDate(QDate(d.year, d.month, d.day)) until_time.setDate(QDate(d.year, d.month, d.day)) start_time.setTime(QTime(9, 0)) until_time.setTime(QTime(16, 0)) self.price_time_axis.setRange(start_time, until_time) def attach(self): self.price_time_axis.setTickCount(7) self.candle_stick_series.attachAxis(self.price_time_axis) self.candle_stick_series.attachAxis(self.price_axis) self.moving_average_series.attachAxis(self.price_time_axis) self.moving_average_series.attachAxis(self.price_axis) self.top_edge_series.attachAxis(self.price_time_axis) self.top_edge_series.attachAxis(self.price_axis) self.bottom_edge_series.attachAxis(self.price_time_axis) self.bottom_edge_series.attachAxis(self.price_axis) self.short_top_trend_series.attachAxis(self.price_time_axis) self.short_top_trend_series.attachAxis(self.price_axis) self.long_top_trend_series.attachAxis(self.price_time_axis) self.long_top_trend_series.attachAxis(self.price_axis) self.short_bottom_trend_series.attachAxis(self.price_time_axis) self.short_bottom_trend_series.attachAxis(self.price_axis) self.long_bottom_trend_series.attachAxis(self.price_time_axis) self.long_bottom_trend_series.attachAxis(self.price_axis) def in_datetime_range(self, q): return self.datetime_range[0] < q < self.datetime_range[1] def clear_series_data(self): self.candle_stick_series.clear() self.moving_average_series.clear() self.top_edge_series.clear() self.bottom_edge_series.clear() self.short_top_trend_series.clear() self.long_top_trend_series.clear() self.short_bottom_trend_series.clear() self.long_bottom_trend_series.clear() def get_chart_view(self): return self.chart_view def add_moving_average(self, q, price): if self.in_datetime_range(q): self.moving_average_series.append(q, price) def add_candle_stick(self, q, o, h, l, c): if self.in_datetime_range(q): self.candle_stick_series.append(QCandlestickSet(o, h, l, c, q)) def set_price_range(self, price_min, price_max): self.price_axis.setRange(price_min, price_max) tick_count = int( math.ceil((price_max - price_min) / price_min * 100. / 2.0)) self.price_axis.setTickCount(tick_count if tick_count + 1 > 2 else 2) def add_top_edge(self, q, price): if self.in_datetime_range(q): self.top_edge_series.append(q, price) def add_bottom_edge(self, q, price): if self.in_datetime_range(q): self.bottom_edge_series.append(q, price) def add_short_top_trend(self, q, price, draw_horizontal=False): if self.in_datetime_range(q): if draw_horizontal: self.short_top_trend_series.append(q, price) if self.name == 'yesterday': self.short_top_trend_series.append(self.datetime_range[1], price) else: self.short_top_trend_series.append(self.datetime_range[0], price) else: self.short_top_trend_series.append(q, price) def add_long_top_trend(self, q, price, draw_horizontal=False): if self.in_datetime_range(q): if draw_horizontal: self.long_top_trend_series.append(q, price) if self.name == 'yesterday': self.long_top_trend_series.append(self.datetime_range[1], price) else: self.long_top_trend_series.append(self.datetime_range[0], price) else: self.long_top_trend_series.append(q, price) def add_short_bottom_trend(self, q, price, draw_horizontal=False): if self.in_datetime_range(q): if draw_horizontal: self.short_bottom_trend_series.append(q, price) if self.name == 'yesterday': self.short_bottom_trend_series.append( self.datetime_range[1], price) else: self.short_bottom_trend_series.append( self.datetime_range[0], price) else: self.short_bottom_trend_series.append(q, price) def add_long_bottom_trend(self, q, price, draw_horizontal=False): if self.in_datetime_range(q): if draw_horizontal: self.long_bottom_trend_series.append(q, price) if self.name == 'yesterday': self.long_bottom_trend_series.append( self.datetime_range[1], price) else: self.long_bottom_trend_series.append( self.datetime_range[0], price) else: self.long_bottom_trend_series.append(q, price)
class AmzHistoryChart(QWidget): """A chart that graphs the history of an AmazonListing's sales rank, price, and number of offers.""" def __init__(self, parent=None): super(AmzHistoryChart, self).__init__(parent=parent) self.dbsession = Session() self.context_menu_actions = [] self._avg_pointspan = 0 self._max_points = 100 self.source = None self.history = None layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) # Set up the chart self.chart_view = QChartView(self) self.chart_view.setRenderHint(QPainter.Antialiasing) self.chart_view.setContextMenuPolicy(Qt.CustomContextMenu) self.chart_view.customContextMenuRequested.connect(self.context_menu) self.chart = QChart() self.chart.legend().hide() self.chart.setFlags(QGraphicsItem.ItemIsFocusable | QGraphicsItem.ItemIsSelectable) self.chart.installEventFilter(self) self.chart_view.setChart(self.chart) self.layout().addWidget(self.chart_view) # Create the axes rcolor = QColor(50, 130, 220) pcolor = QColor(0, 200, 0) ocolor = QColor(255, 175, 0) self.timeAxis = QDateTimeAxis() self.timeAxis.setFormat('M/dd hh:mm') self.timeAxis.setTitleText('Date/Time') self.chart.addAxis(self.timeAxis, Qt.AlignBottom) self.timeAxis.minChanged.connect(self.on_timeaxis_min_changed) self.rankAxis = QValueAxis() self.rankAxis.setLabelFormat('%\'i') self.rankAxis.setTitleText('Sales Rank') self.rankAxis.setLinePenColor(rcolor) self.rankAxis.setLabelsColor(rcolor) self.chart.addAxis(self.rankAxis, Qt.AlignLeft) self.priceAxis = QValueAxis() self.priceAxis.setLabelFormat('$%.2f') self.priceAxis.setTitleText('Price') self.priceAxis.setLinePenColor(pcolor) self.priceAxis.setLabelsColor(pcolor) self.chart.addAxis(self.priceAxis, Qt.AlignRight) # Create the series self.rankLine = QLineSeries() self.chart.addSeries(self.rankLine) self.rankLine.attachAxis(self.timeAxis) self.rankLine.attachAxis(self.rankAxis) self.rankLine.setColor(rcolor) self.priceLine = QLineSeries() self.chart.addSeries(self.priceLine) self.priceLine.attachAxis(self.timeAxis) self.priceLine.attachAxis(self.priceAxis) self.priceLine.setColor(pcolor) self.salesPoints = QScatterSeries() self.chart.addSeries(self.salesPoints) self.salesPoints.attachAxis(self.timeAxis) self.salesPoints.attachAxis(self.rankAxis) self.salesPoints.setColor(ocolor) def add_context_action(self, action): """Add an action to the chart's context menu.""" self.context_menu_actions.append(action) def add_context_actions(self, actions): """Adds all action in an iterable.""" self.context_menu_actions.extend(actions) def remove_context_action(self, action): """Removes an action from the chart's context menu.""" self.context_menu_actions.remove(action) def context_menu(self, point): """Show a context menu on the chart.""" menu = QMenu(self) menu.addActions(self.context_menu_actions) point = self.chart_view.viewport().mapToGlobal(point) menu.popup(point) def set_source(self, source): """Set the source listing for the graph.""" self.source = source # Update the chart self.rankLine.clear() self.priceLine.clear() self.salesPoints.clear() self.history = None start_date = datetime.utcnow() - timedelta(days=5) self.load_history_from(start_date) self.reset_axes() def load_history_from(self, start_date=datetime.utcfromtimestamp(0)): """Load history data from start-present.""" if not self.source: self._avg_pointspan = 0 return # Get the earliest point already in the chart points = self.rankLine.pointsVector() if points: # The chart is drawn right-to-left, so the last point is the earliest point earliest_msecs = points[-1].x() earliest = datetime.fromtimestamp(earliest_msecs / 1000, timezone.utc) if earliest <= start_date: return else: earliest = datetime.now(timezone.utc) # Get the product history stats if we don't already have them if self.history is None: self.history = dbhelpers.ProductHistoryStats(self.dbsession, self.source.id) # Start adding points to the chart last_row = None for row in self.dbsession.query(AmzProductHistory).\ filter(AmzProductHistory.amz_listing_id == self.source.id, AmzProductHistory.timestamp > start_date.replace(tzinfo=None), AmzProductHistory.timestamp < earliest.replace(tzinfo=None)).\ order_by(AmzProductHistory.timestamp.desc()): # SqlAlchemy returns naive timestamps time = row.timestamp.replace(tzinfo=timezone.utc).timestamp() * 1000 self.rankLine.append(time, row.salesrank or 0) self.priceLine.append(time, row.price or 0) if last_row: # It's possible for salesrank to be None try: slope = (last_row.salesrank - row.salesrank) / (last_row.timestamp.timestamp() - row.timestamp.timestamp()) if slope < -0.3: self.salesPoints.append(last_row.timestamp.replace(tzinfo=timezone.utc).timestamp() * 1000, last_row.salesrank) except (TypeError, AttributeError): pass last_row = row # Calculate the average span between points spans = 0 for p1, p2 in itertools.zip_longest(itertools.islice(points, 0, None, 2), itertools.islice(points, 1, None, 2)): if p1 and p2: spans += abs(p1.x() - p2.x()) self._avg_pointspan = spans // 2 def on_timeaxis_min_changed(self, min): """Respond to a change in the time axis' minimum value.""" # toTime_t() converts to UTC automatically utc_min = datetime.fromtimestamp(min.toTime_t(), timezone.utc) self.load_history_from(start_date=utc_min - timedelta(days=1)) def reset_axes(self): """Resets the chart axes.""" r = self.rankLine.pointsVector() p = self.priceLine.pointsVector() # If there is only one data point, set the min and max to the day before and the day after if len(r) == 1: tmin = QDateTime.fromMSecsSinceEpoch(r[0].x(), Qt.LocalTime).addDays(-1) tmax = QDateTime.fromMSecsSinceEpoch(r[0].x(), Qt.LocalTime).addDays(+1) else: tmin = min(r, key=lambda pt: pt.x(), default=QPointF(QDateTime.currentDateTime().addDays(-1).toMSecsSinceEpoch(), 0)).x() tmax = max(r, key=lambda pt: pt.x(), default=QPointF(QDateTime.currentDateTime().addDays(+1).toMSecsSinceEpoch(), 0)).x() tmin = QDateTime.fromMSecsSinceEpoch(tmin, Qt.LocalTime) tmax = QDateTime.fromMSecsSinceEpoch(tmax, Qt.LocalTime) self.timeAxis.setMin(tmin) self.timeAxis.setMax(tmax) # Find the min and max values of the series min_point = lambda pts: min(pts, key=lambda pt: pt.y(), default=QPointF(0, 0)) max_point = lambda pts: max(pts, key=lambda pt: pt.y(), default=QPointF(0, 0)) rmin = min_point(r) rmax = max_point(r) pmin = min_point(p) pmax = max_point(p) # Scale the mins and maxes to 'friendly' values scalemin = lambda v, step: ((v - step / 2) // step) * step scalemax = lambda v, step: ((v + step / 2) // step + 1) * step # The the axis bounds rmin = max(scalemin(rmin.y(), 1000), 0) rmax = scalemax(rmax.y(), 1000) pmin = max(scalemin(pmin.y(), 5), 0) pmax = scalemax(pmax.y(), 5) self.rankAxis.setMin(rmin) self.rankAxis.setMax(rmax) self.priceAxis.setMin(pmin) self.priceAxis.setMax(pmax) def eventFilter(self, watched, event): """Intercept and handle mouse events.""" if event.type() == QEvent.GraphicsSceneWheel and event.orientation() == Qt.Vertical: factor = 0.95 if event.delta() < 0 else 1.05 self.chart.zoom(factor) return True if event.type() == QEvent.GraphicsSceneMouseDoubleClick: self.chart.zoomReset() self.reset_axes() return True if event.type() == QEvent.GraphicsSceneMouseMove: delta = event.pos() - event.lastPos() self.chart.scroll(-delta.x(), delta.y()) return True return False
class Window(QMainWindow): def __init__(self, landmarkPoints, allPoints, threadEvent): super().__init__() self.title = "Lidar data points" #self.queue = queue self.color = Qt.darkRed self.lmrkPoints = landmarkPoints self.allPoints = allPoints self.event = threadEvent self.left = 500 self.top = 500 self.height = 480 self.width = 640 self.count = 0 self.time = 0 self.label = QLabel(self) self.lmrkBox = QCheckBox("Landmark points", self) self.ptsBox = QCheckBox("Data points", self) self.boxArea = QWidget() self.mainLayout = QGridLayout() self.mainLayout.addWidget(self.lmrkBox, 0, 0) self.mainLayout.addWidget(self.ptsBox, 1, 0) self.mainLayout.setVerticalSpacing(5) self.boxArea.setLayout(self.mainLayout) crote = QDockWidget("Hide", self) crote.setWidget(self.boxArea) self.addDockWidget(Qt.LeftDockWidgetArea, crote) dock = QDockWidget("", self) dock.setWidget(self.label) self.addDockWidget(Qt.LeftDockWidgetArea, dock) self.chart = QChart() self.config_axis() self.series = QScatterSeries(self.chart) self.allSeries = QScatterSeries(self.chart) self.config_series() #self.update() self.timer = QTimer(self) self.view = QChartView(self.chart) self.setCentralWidget( self.view ) # It is needed to create to view because the CentralWidget needs to be a QWidget, and a QChart is not so. self.initWindow() def config_series(self): self.series.setName("Landmark Points") self.allSeries.setName("Data Points") lmrkPen = self.series.pen() pen = self.allSeries.pen() lmrkPen.setWidthF(.2) pen.setWidthF(.2) self.series.setPen(lmrkPen) self.allSeries.setPen(pen) self.series.setColor(Qt.red) self.allSeries.setColor(Qt.blue) self.series.setMarkerShape(1) # 1 - rectangle; 0 - circle # for good visualization, the landmark points should be bigger than normal points self.series.setMarkerSize(8) self.allSeries.setMarkerSize(5) self.label.move(15, 15) def config_axis(self): self.xAxis = QValueAxis() self.yAxis = QValueAxis() self.xAxis.setRange(-XRANGE, XRANGE) self.xAxis.setTitleText("Eixo x") self.yAxis.setRange(-YRANGE, YRANGE) self.yAxis.setTitleText("Eixo y") self.chart.addAxis(self.xAxis, Qt.AlignBottom) self.chart.addAxis(self.yAxis, Qt.AlignLeft) def update(self): self.event.wait() start = time.time() self.label.setText("FPS: {:.2f}".format(1 / (time.time() - self.time))) self.time = time.time() if self.count == 0 and self.lmrkPoints != []: self.series.append(self.lmrkPoints[0][:]) self.allSeries.append(self.allPoints[0][:]) del self.lmrkPoints[:] del self.allPoints[:] self.count = 1 elif self.lmrkPoints != []: self.series.replace(self.lmrkPoints[0][:]) self.allSeries.replace(self.allPoints[0][:]) del self.lmrkPoints[:] del self.allPoints[:] end = time.time() self.event.clear() def hide_show_points(self): self.series.setVisible(not self.series.isVisible()) def hide_show_all_points(self): self.allSeries.setVisible(not self.allSeries.isVisible()) def initWindow(self): print("queue inside myWindow: {}".format(self.lmrkPoints)) self.setGeometry(self.left, self.top, self.width, self.height) self.setWindowTitle(self.title) self.chart.addSeries(self.series) self.chart.addSeries(self.allSeries) self.series.attachAxis(self.xAxis) self.series.attachAxis(self.yAxis) self.allSeries.attachAxis(self.xAxis) self.allSeries.attachAxis(self.yAxis) self.timer.timeout.connect(self.update) self.lmrkBox.stateChanged.connect(self.hide_show_points) self.ptsBox.stateChanged.connect(self.hide_show_all_points) self.timer.start(0) self.show()