PyQt6基础_pyqtgraph_双Y轴不同周期数据叠加

发布于:2025-07-26 ⋅ 阅读:(23) ⋅ 点赞:(0)

效果:

双Y轴,左轴对应曲线总市值;右轴对应柱状图总营收。

市值数据为月数据,营收数据为季度数据,两者时间区间一致。

代码:

# -*- coding: utf-8-*-
import pandas as pd
import numpy as np
from PyQt6.QtWidgets import (
    QApplication
)
import pyqtgraph as pg

class StrAxisItem(pg.AxisItem):
    def __init__(self,ticks,*args,**kwargs):
        pg.AxisItem.__init__(self,*args,**kwargs)
        self.x_values = [x[0] for x in ticks]
        self.x_strings = [x[1] for x in ticks]
        pass
    def tickStrings(self, values, scale, spacing):
        strings = []
        for v in values:
            vs = v*scale
            if vs in self.x_values:
                vstr = self.x_strings[self.x_values.index(vs)]
            else:
                vstr = ''
            strings.append(vstr)
        return strings

class Graph2YBarAndLineWidget(pg.PlotWidget):
    def __init__(self):
        super().__init__()
        self.init_data()
    def init_data(self):
        self.color_bar = (0, 0, 255)
        self.color_line = (178, 34, 34)

        self.x = None
        self.xTicks:list = []
        self.p2 = pg.ViewBox()
        self.whole_left_df = pd.DataFrame() # lien
        self.whole_right_df = pd.DataFrame() # bar
        self.whole_left_name:str = ''
        self.whole_right_name:str = ''
        pass
    def set_data(self,left_df:pd.DataFrame,right_df:pd.DataFrame,left_name:str,right_name:str,xTicks:list):
        '''
        dateStr 字符型
        timestamp 长整型
        a1 line 总市值 a2 bar 总营收
        :return:
        '''
        self.clear()
        if self.p2:
            self.p2.clear()
            pass
        if left_df.empty or right_df.empty:
            return
        self.x = range(len(xTicks))
        self.whole_right_df = right_df
        self.whole_left_df = left_df
        self.whole_left_name = left_name
        self.whole_right_name = right_name
        self.xTicks = xTicks

        self.addLegend()
        self.setLabel('left',left_name)

        y_left = left_df['a1'].to_list()
        x_left = left_df['x'].to_list()

        right_df = right_df.fillna(0)
        y_right = right_df['a2'].to_list()
        x_right = right_df['x'].to_list()

        curve_left = pg.PlotCurveItem(x=x_left,y=y_left,pen=pg.mkPen({'color': self.color_line, 'width': 4}), connect='finite',name=left_name)
        self.addItem(curve_left)

        self.setLabel('right',right_name)
        self.p2.enableAutoRange()
        self.scene().addItem(self.p2)
        self.getAxis('right').linkToView(self.p2)
        self.p2.setXLink(self)

        bars = pg.BarGraphItem(x=x_right,height=y_right,width=0.8,brush=pg.mkBrush(color=self.color_bar))
        self.p2.addItem(bars)

        horAxis = StrAxisItem(ticks=xTicks, orientation='bottom')
        self.setAxisItems({'bottom': horAxis})

        self.vLine = pg.InfiniteLine(angle=90, movable=False)
        self.hLine = pg.InfiniteLine(angle=0, movable=False)
        self.addItem(self.vLine, ignoreBounds=True)
        self.addItem(self.hLine, ignoreBounds=True)

        self.label = pg.TextItem()
        self.addItem(self.label)

        self.vb = self.getViewBox()
        self.vb.sigResized.connect(self.updateViews)

        self.scene().sigMouseMoved.connect(self.mouseMoved)

        self.enableAutoRange()
        pass
    def updateViews(self):
        self.p2.setGeometry(self.getViewBox().sceneBoundingRect())
        self.p2.linkedViewChanged(self.getViewBox(), self.p2.XAxis)
        pass
    def mouseMoved(self,evt):
        pos = evt
        if self.sceneBoundingRect().contains(pos):
            mousePoint = self.vb.mapSceneToView(pos)
            index = int(mousePoint.x())
            if index > 0 and index < len(self.x):
                l_df = self.whole_left_df.loc[self.whole_left_df['x']==index]
                r_df = self.whole_right_df.loc[self.whole_right_df['x']==index]
                dateStr = self.xTicks[index][1]
                html_str = f"<span style='font-size:20pt;color:#ffff00;'>日期:{dateStr} <br/>"
                if not l_df.empty:
                    html_str += f"{self.whole_left_name}:{trans_float_to_money(l_df.iloc[0]['a1'])} <br/>"
                    pass
                if not r_df.empty:
                    html_str += f"{self.whole_right_name}:{trans_float_to_money(r_df.iloc[0]['a2'])} "
                    pass
                html_str += '</span>'
                self.label.setHtml(html_str)
                self.label.setPos(mousePoint.x(),mousePoint.y())
                self.vLine.setPos(mousePoint.x())
                self.hLine.setPos(mousePoint.y())
                pass
            pass
        pass
    pass

def trans_float_to_money(val):
    try:
        val = float(val)
        if abs(val) // 1000000000000 >= 1:
            return f"{val / 1000000000000:,.2f}万亿"
        elif abs(val) // 100000000 >= 1:
            return f"{val / 100000000:,.2f}亿"
        elif abs(val) // 10000 >= 1:
            return f"{val / 10000:,.2f}万"
        else:
            return f"{val:,.2f}"
    except:
        return f"{val}"

if __name__ == '__main__':
    dateStr = ['2021-01-31', '2021-02-28', '2021-03-31', '2021-04-30', '2021-05-31', '2021-06-30', '2021-07-31', '2021-08-31', '2021-09-30', '2021-10-31', '2021-11-30', '2021-12-31', '2022-01-31', '2022-02-28', '2022-03-31', '2022-04-30', '2022-05-31', '2022-06-30', '2022-07-31', '2022-08-31', '2022-09-30', '2022-10-31', '2022-11-30', '2022-12-31', '2023-01-31', '2023-02-28', '2023-03-31', '2023-04-30', '2023-05-31', '2023-06-30', '2023-07-31', '2023-08-31', '2023-09-30', '2023-10-31', '2023-11-30', '2023-12-31', '2024-01-31', '2024-02-29', '2024-03-31', '2024-04-30', '2024-05-31', '2024-06-30', '2024-07-31', '2024-08-31', '2024-09-30', '2024-10-31', '2024-11-30', '2024-12-31', '2025-01-31', '2025-02-28', '2025-03-31', '2025-04-30', '2025-05-31', '2025-06-30', '2025-07-31']
    a1 = [3366454261851.7603, 3085759494953.95, 2915166338295.36, 3062518461207.14, 3282010044186.26, 3704755450707.67, 3819034032030.23, 4151137992229.7, 3731726058531.34, 4220140547358.65, 4395348959207.6, 4134718310549.7695, 3622885627493.38, 3638523139149.04, 3188729955826.0195, 2949100387509.55, 3533200939973.0, 4028306230193.0703, 4230437911317.54, 3777311881572.21, 3371730187564.07, 4775148842141.6, 5147063106507.54, 4839222735880.72, 3713661759474.92, 3709103595200.17, 3508159000487.1, 3319952875737.66, 3281394558675.9004, 3555277221999.0703, 3770568023843.5, 3482966320754.82, 3491462068127.8105, 3586476527336.13, 3647520125533.5303, 3424088400362.96, 2886662947012.88, 3285235429188.3203, 3438771995769.73, 3545558269681.92, 3450330288454.79, 3375797478167.6, 3421568335798.04, 3232236836706.6396, 3977065566968.01, 4013214016479.33, 4059133933833.29, 4178900418689.1104, 4157633137555.46, 4543013367676.15, 4703947321683.3, 4571493143958.17, 4632381147891.07, 4589724848135.59, 4680715665617.9]
    a2 = [np.nan, np.nan, 3032052010652.64, np.nan, np.nan, 3122744032107.75, np.nan, np.nan, 3095421730935.44, np.nan, np.nan, 3070683024038.89, np.nan, np.nan, 3039483894149.17, np.nan, np.nan, 2915294618426.64, np.nan, np.nan, 3093223235167.2803, np.nan, np.nan, 3201963125816.03, np.nan, np.nan, 3253459877515.63, np.nan, np.nan, 3494836097005.8496, np.nan, np.nan, 3536010599250.05, np.nan, np.nan, 3635214337405.83, np.nan, np.nan, 3665836883287.5, np.nan, np.nan, 3674413007760.9, np.nan, np.nan, 3617732527099.52, np.nan, np.nan, 3721610095247.51, np.nan, np.nan, 3766992158253.9995, np.nan, np.nan, np.nan, np.nan]

    df = pd.DataFrame(data={
        'dateStr':dateStr,
        'a1':a1,
        'a2':a2
    })

    df['x'] = range(len(df))

    df_l = df.loc[:, ['x', 'dateStr', 'a1']]
    df_r = df.loc[:, ['x', 'dateStr', 'a2']]
    df_l.dropna(subset=['a1'], inplace=True)
    df_r.dropna(subset=['a2'], inplace=True)
    xTicks = df.loc[:, ['x', 'dateStr']].values

    app = QApplication([])
    mv = Graph2YBarAndLineWidget()
    # mv.set_data(df)
    mv.set_data(df_l, df_r, '总市值', '总营收', xTicks)
    mv.show()
    app.exec()
    pass


网站公告

今日签到

点亮在社区的每一天
去签到