class VLineChartView(QChartView): def __init__(self): super(VLineChartView, self).__init__() self.stocks = read_tick_data() self.category = [ trade_date[4:] for trade_date in self.stocks['trade_date'] ] self.resize(800, 300) self.initChart() def initChart(self): self._chart = QChart(title='数量') self._chart.setAnimationOptions(QChart.SeriesAnimations) series = QStackedBarSeries() series.setName('数量') bar_red = QBarSet('red') bar_red.setColor(Qt.red) bar_green = QBarSet('green') bar_green.setColor(Qt.green) for _, stock in self.stocks.iterrows(): if stock['open'] < stock['close']: bar_red.append(stock['vol'] / 100) bar_green.append(0) else: bar_red.append(0) bar_green.append(stock['vol'] / 100) series.append(bar_red) series.append(bar_green) self._chart.addSeries(series) self._chart.createDefaultAxes() self._chart.setLocalizeNumbers(True) axis_x = self._chart.axisX() axis_y = self._chart.axisY() axis_x.setGridLineVisible(False) axis_y.setGridLineVisible(False) axis_y.setLabelFormat("%.2f") axis_x.setCategories(self.category) max_p = self.stocks[[ 'vol', ]].stack().max() / 100 + 10 min_p = self.stocks[[ 'vol', ]].stack().min() / 100 - 10 axis_y.setRange(min_p, max_p) # chart的图例 legend = self._chart.legend() legend.hide() # 设置图例由Series来决定样式 # legend.setMarkerShape(QLegend.MarkerShapeFromSeries) self.setChart(self._chart) self._chart.layout().setContentsMargins(0, 0, 0, 0) # self._chart.setMargins(QMargins(0, 0, 0, 0)) self._chart.setBackgroundRoundness(0)
class KLineChartView(QChartView): # QCandlestickSeries的hovered的信号响应后传递日期出去 candles_hovered = pyqtSignal(bool, str) def __init__(self, data: pd.DataFrame): super(KLineChartView, self).__init__() self.setRenderHint(QPainter.Antialiasing) # 抗锯齿 self._chart = QChart() self._series = QCandlestickSeries() self._stocks = data self._category = list() self._count = None self.init_chart() self._zero_value = (0, self._chart.axisY().min()) self._max_value = (len(self._chart.axisX().categories()), self._chart.axisY().max()) self._zero_point = self._chart.mapToPosition( QPointF(self._zero_value[0], self._zero_value[1])) self._max_point = self._chart.mapToPosition( QPointF(self._max_value[0], self._max_value[1])) # 计算x轴单个cate的宽度,用来处理横线不能画到边界 self._cate_width = (self._max_point.x() - self._zero_point.x()) / len( self._category) self._series.hovered.connect(self.on_series_hovered) def on_series_hovered(self, status, candles_set): trade_date = time.strftime('%Y%m%d', time.localtime(candles_set.timestamp())) self.candles_hovered.emit(status, trade_date) def set_name(self, name): self._series.setName(name) def clear_series_values(self): self._series.clear() self._chart.axisY().setRange(0, 10) self._chart.axisX().setCategories(list()) self._stocks = None def add_series_values(self, data: pd.DataFrame, is_init=False): self._stocks = data self._category = self._stocks['trade_date'] self._count = len(self._category) for _, stock in self._stocks.iterrows(): time_p = datetime.datetime.strptime(stock['trade_date'], '%Y%m%d') time_p = float(time.mktime(time_p.timetuple())) _set = QCandlestickSet(float(stock['open']), float(stock['high']), float(stock['low']), float(stock['close']), time_p, self._series) self._series.append(_set) if not is_init: self._stocks = data self._category = self._stocks['trade_date'] axis_x = self._chart.axisX() axis_y = self._chart.axisY() axis_x.setCategories(self._category) max_p = self._stocks[['high', 'low']].stack().max() min_p = self._stocks[['high', 'low']].stack().min() axis_y.setRange(min_p * 0.99, max_p * 1.01) self._zero_value = (0, self._chart.axisY().min()) self._max_value = (len(self._chart.axisX().categories()), self._chart.axisY().max()) # 计算x轴单个cate的宽度,用来处理横线不能画到边界 self._cate_width = (self._max_point.x() - self._zero_point.x()) / len(self._category) def resizeEvent(self, event): super(KLineChartView, self).resizeEvent(event) self._zero_point = self._chart.mapToPosition( QPointF(self._zero_value[0], self._zero_value[1])) self._max_point = self._chart.mapToPosition( QPointF(self._max_value[0], self._max_value[1])) self._cate_width = (self._max_point.x() - self._zero_point.x()) / len( self._category) def max_point(self): return QPointF(self._max_point.x() + self._cate_width / 2, self._max_point.y()) def min_point(self): return QPointF(self._zero_point.x() - self._cate_width / 2, self._zero_point.y()) def init_chart(self): self._chart.setAnimationOptions(QChart.SeriesAnimations) self._series.setIncreasingColor(QColor(Qt.red)) self._series.setDecreasingColor(QColor(Qt.green)) self._series.setName(self._stocks['name'].iloc[0]) self.add_series_values(self._stocks, True) self._chart.addSeries(self._series) self._chart.createDefaultAxes() self._chart.setLocalizeNumbers(True) axis_x = self._chart.axisX() axis_y = self._chart.axisY() axis_x.setGridLineVisible(False) axis_y.setGridLineVisible(False) axis_x.setCategories(self._category) axis_x.setLabelsVisible(False) axis_x.setVisible(False) max_p = self._stocks[['high', 'low']].stack().max() min_p = self._stocks[['high', 'low']].stack().min() axis_y.setRange(min_p * 0.99, max_p * 1.01) # chart的图例 legend = self._chart.legend() # 设置图例由Series来决定样式 legend.setMarkerShape(QLegend.MarkerShapeFromSeries) self.setChart(self._chart) # 设置外边界全部为0 self._chart.layout().setContentsMargins(0, 0, 0, 0) # 设置内边界的bottom为0 margins = self._chart.margins() self._chart.setMargins(QMargins(margins.left(), 0, margins.right(), 0)) # 设置背景区域无圆角 self._chart.setBackgroundRoundness(0)
class VLineChartView(QChartView): bar_hovered = pyqtSignal(bool, str) def __init__(self, data: pd.DataFrame): super(VLineChartView, self).__init__() self._stocks = data self._category = self._stocks['trade_date'] self._chart = QChart() self._chart.setAnimationOptions(QChart.SeriesAnimations) self._series = QStackedBarSeries() # 成交量以万股为单位 self._vol_multiple = 10000 self.init_chart() self._zero_value = (0, self._chart.axisY().min()) self._max_value = (len(self._chart.axisX().categories()), self._chart.axisY().max()) self._zero_point = self._chart.mapToPosition( QPointF(self._zero_value[0], self._zero_value[1])) self._max_point = self._chart.mapToPosition( QPointF(self._max_value[0], self._max_value[1])) # 计算x轴单个cate的宽度,用来处理横线不能画到边界 self._cate_width = (self._max_point.x() - self._zero_point.x()) / len( self._category) self._series.hovered.connect(self.on_series_hovered) x_index_list = np.percentile(range(len(self._category)), [0, 25, 50, 75, 100]) self._x_axis_list = [ QGraphicsSimpleTextItem(self._category[int(index)], self._chart) for index in x_index_list ] [axis.setText(axis.text()[4:]) for axis in self._x_axis_list[1:]] self._v_b = QGraphicsSimpleTextItem('B', self._chart) self._v_b.setZValue(100) def on_series_hovered(self, status, index): self.bar_hovered.emit(status, self._category[index]) def clear_series_value(self): self._series.clear() self._stocks = None self._chart.axisY().setRange(0, 10) self._chart.axisX().setCategories(list()) def add_series_values(self, data: pd.DataFrame, is_init=False): self._stocks = data bar_red = QBarSet('red') bar_red.setColor(Qt.red) bar_green = QBarSet('green') bar_green.setColor(Qt.green) for _, stock in self._stocks.iterrows(): if stock['open'] < stock['close']: bar_red.append(stock['vol'] / self._vol_multiple) bar_green.append(0) else: bar_red.append(0) bar_green.append(stock['vol'] / self._vol_multiple) self._series.append(bar_red) self._series.append(bar_green) if not is_init: self._stocks = data self._category = self._stocks['trade_date'] axis_x = self._chart.axisX() axis_y = self._chart.axisY() axis_x.setCategories(self._category) max_p = self._stocks[[ 'vol', ]].stack().max() min_p = self._stocks[[ 'vol', ]].stack().min() axis_y.setRange(min_p / self._vol_multiple * 0.9, max_p / self._vol_multiple * 1.1) self._zero_value = (0, self._chart.axisY().min()) self._max_value = (len(self._chart.axisX().categories()), self._chart.axisY().max()) # 计算x轴单个cate的宽度,用来处理横线不能画到边界 self._cate_width = (self._max_point.x() - self._zero_point.x()) / len(self._category) def resizeEvent(self, event): super(VLineChartView, self).resizeEvent(event) self._zero_point = self._chart.mapToPosition( QPointF(self._zero_value[0], self._zero_value[1])) self._max_point = self._chart.mapToPosition( QPointF(self._max_value[0], self._max_value[1])) self._cate_width = (self._max_point.x() - self._zero_point.x()) / len( self._category) # 绘制自定义X轴 self._x_axis_list[0].setPos(self._zero_point.x() - self._cate_width, self._zero_point.y() + 10) self._x_axis_list[1].setPos(self._max_point.x() * 0.25, self._zero_point.y() + 10) self._x_axis_list[2].setPos(self._max_point.x() * 0.5, self._zero_point.y() + 10) self._x_axis_list[3].setPos(self._max_point.x() * 0.75, self._zero_point.y() + 10) self._x_axis_list[4].setPos( self._max_point.x() - self._x_axis_list[-1].boundingRect().width(), self._zero_point.y() + 10) # 20180207 这个日期的柱形图上面画一个字母b vol = self._stocks[self._stocks['trade_date'] == '20180207']['vol'] / self._vol_multiple print('vol:', vol, ' trade_date:', '20180207') pos = self._chart.mapToPosition( QPointF(list(self._category).index('20180207'), vol)) pos = QPointF(pos.x() - self._cate_width / 2, pos.y() - self._v_b.boundingRect().height()) self._v_b.setPos(pos) def max_point(self): return QPointF(self._max_point.x() + self._cate_width / 2, self._max_point.y()) def min_point(self): return QPointF(self._zero_point.x() - self._cate_width / 2, self._zero_point.y()) def init_chart(self): self.add_series_values(self._stocks, True) self._chart.addSeries(self._series) self._chart.createDefaultAxes() self._chart.setLocalizeNumbers(True) axis_x = self._chart.axisX() axis_y = self._chart.axisY() axis_x.setGridLineVisible(False) axis_y.setGridLineVisible(False) axis_y.setLabelFormat("%.2f") axis_x.setCategories(self._category) axis_x.setLabelsVisible(False) max_p = self._stocks[[ 'vol', ]].stack().max() min_p = self._stocks[[ 'vol', ]].stack().min() axis_y.setRange(min_p / self._vol_multiple * 0.9, max_p / self._vol_multiple * 1.1) # chart的图例 legend = self._chart.legend() legend.hide() # 设置图例由Series来决定样式 # legend.setMarkerShape(QLegend.MarkerShapeFromSeries) self.setChart(self._chart) self._chart.layout().setContentsMargins(0, 0, 0, 0) # 设置内边界的bottom为0 # margins = self._chart.margins() # self._chart.setMargins(QMargins(margins.left(), 0, margins.right(), 0)) self._chart.setBackgroundRoundness(0)
class KLineChartView(QChartView): def __init__(self): super(KLineChartView, self).__init__() self.setRenderHint(QPainter.Antialiasing) # 抗锯齿 self._chart = QChart(title='蜡烛图悬浮提示') self.stocks = read_tick_data() self.category = [ trade_date[4:] for trade_date in self.stocks['trade_date'] ] self._count = len(self.category) self.resize(800, 300) self.init_chart() self.toolTipWidget = GraphicsProxyWidget(self._chart) # 鼠标跟踪的十字线 self.lineItem_h = QGraphicsLineItem(self._chart) self.lineItem_v = QGraphicsLineItem(self._chart) pen = QPen() pen.setStyle(Qt.DotLine) pen.setColor(QColor(Qt.gray)) pen.setWidth(2) self.lineItem_h.setPen(pen) self.lineItem_v.setPen(pen) self.lineItem_h.setZValue(100) self.lineItem_v.setZValue(100) self.lineItem_h.hide() self.lineItem_v.hide() # 坐标轴上最大最小的值 # x 轴是 self.min_x, self.max_x = 0, len(self._chart.axisX().categories()) self.min_y, self.max_y = self._chart.axisY().min(), self._chart.axisY( ).max() # y 轴最高点坐标 self.point_y_max = self._chart.mapToPosition( QPointF(self.min_x, self.max_y)) # x 轴最高点坐标 self.point_x_max = self._chart.mapToPosition( QPointF(self.max_x, self.min_y)) # self.point_x_min = self._chart.mapToPosition(QPointF(self.min_x, self.min_y)) # 计算x轴单个cate的宽度,用来处理横线不能画到边界 self.x_width = (self.point_x_max.x() - self.point_y_max.x()) / len( self.category) self.x_x_min = self.point_y_max.x() - self.x_width / 2 self.x_x_max = self.point_x_max.x() - self.x_width / 2 # 中间位置,用来判断TipWidget放在哪里 mid_date = self.stocks['trade_date'].iloc[len( self.stocks['trade_date']) // 2] self.mid_x = float( time.mktime( datetime.datetime.strptime(str(mid_date), '%Y%m%d').timetuple())) self.left_pos = self.point_y_max self.right_pos = self._chart.mapToPosition( QPointF(self.max_x, self.max_y)) def mouseMoveEvent(self, event): super(KLineChartView, self).mouseMoveEvent(event) pos = event.pos() if self.x_x_min < pos.x() < self.x_x_max \ and self.point_x_max.y() > pos.y() > self.point_y_max.y(): self.lineItem_h.setLine(self.x_x_min, pos.y(), self.x_x_max, pos.y()) self.lineItem_v.setLine(pos.x(), self.point_y_max.y(), pos.x(), self.point_x_max.y()) self.lineItem_h.show() self.lineItem_v.show() else: self.lineItem_h.hide() self.lineItem_v.hide() def resizeEvent(self, event): super(KLineChartView, self).resizeEvent(event) # y 轴最高点坐标 self.point_y_max = self._chart.mapToPosition( QPointF(self.min_x, self.max_y)) # x 轴最高点坐标 self.point_x_max = self._chart.mapToPosition( QPointF(self.max_x, self.min_y)) # 计算x轴单个cate的宽度,用来处理横线不能画到边界 self.x_width = (self.point_x_max.x() - self.point_y_max.x()) / len( self.category) self.x_x_min = self.point_y_max.x() - self.x_width / 2 self.x_x_max = self.point_x_max.x() - self.x_width / 2 self.left_pos = self.point_y_max self.right_pos = self._chart.mapToPosition( QPointF(self.max_x, self.max_y)) def init_chart(self): self._chart.setAnimationOptions(QChart.SeriesAnimations) series = QCandlestickSeries() series.setIncreasingColor(QColor(Qt.red)) series.setDecreasingColor(QColor(Qt.green)) series.setName(self.stocks['name'].iloc[0]) for _, stock in self.stocks.iterrows(): time_p = datetime.datetime.strptime(stock['trade_date'], '%Y%m%d') time_p = float(time.mktime(time_p.timetuple())) _set = QCandlestickSet(float(stock['open']), float(stock['high']), float(stock['low']), float(stock['close']), time_p, series) _set.hovered.connect(self.handleBarHoverd) # 鼠标悬停 series.append(_set) self._chart.addSeries(series) self._chart.createDefaultAxes() self._chart.setLocalizeNumbers(True) axis_x = self._chart.axisX() axis_y = self._chart.axisY() axis_x.setGridLineVisible(False) axis_y.setGridLineVisible(False) axis_x.setCategories(self.category) max_p = self.stocks[['high', 'low']].stack().max() + 10 min_p = self.stocks[['high', 'low']].stack().min() - 10 axis_y.setRange(min_p, max_p) # chart的图例 legend = self._chart.legend() # 设置图例由Series来决定样式 legend.setMarkerShape(QLegend.MarkerShapeFromSeries) self.setChart(self._chart) # 设置外边界全部为0 self._chart.layout().setContentsMargins(0, 0, 0, 0) # 设置内边界都为0 # self._chart.setMargins(QMargins(0, 0, 0, 0)) # 设置背景区域无圆角 self._chart.setBackgroundRoundness(0) def handleBarHoverd(self, status): """ 改变画笔的风格 """ bar = self.sender() # 信号发送者 pen = bar.pen() if not pen: return pen.setStyle(Qt.DotLine if status else Qt.SolidLine) bar.setPen(pen) if status: # 通过 bar 可以获取横轴坐标(timestamp)和纵轴坐标(high) # 然后将坐标值转换为位置,显示 TipWidget 的位置 right_pos = QPointF( self.right_pos.x() - self.toolTipWidget.width() - self.x_width, self.right_pos.y()) pos = self.left_pos if bar.timestamp() > self.mid_x else right_pos trade_date = time.strftime('%Y%m%d', time.localtime(bar.timestamp())) self.toolTipWidget.show(str(trade_date), str(bar.open()), str(bar.close()), str(bar.high()), str(bar.low()), pos) else: self.toolTipWidget.hide()