最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python結(jié)合matplotlib實現(xiàn)圖表的基本交互

 更新時間:2025年12月15日 09:17:10   作者:MC皮蛋俠客  
這篇文章主要為大家詳細(xì)介紹了Python如何結(jié)合matplotlib實現(xiàn)圖表的基本交互,可以實現(xiàn)圖表的放大縮小和移動光標(biāo)注釋,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

前言

最近在使用pyqt結(jié)合matplotlib開發(fā)一款內(nèi)部使用的數(shù)據(jù)分析軟件,發(fā)現(xiàn)matplotlib庫在處理大數(shù)據(jù),出圖性能方面還是很不錯的,但是就是圖表的交互性上差了一點,比如說圖像的放大和縮小,移動的光標(biāo)線,顯示注釋等等,很多還是需要自己造輪子,本人通過五一假期的一番研究,從中也頗有收獲,現(xiàn)在把下面的這些研究成果分享給大家。

使用Matplotlib庫完成基本的圖表交互

初始化基本的曲線配置

import sys

import numpy as np
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure

class MatplotlibWidget(FigureCanvas):
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)

        self.compute_initial_figure()

        FigureCanvas.__init__(self, fig)
        self.setParent(parent)
        self.lef_mouse_pressed = False  # 鼠標(biāo)左鍵是否按下
       	self.connect_event()
	
    def connect_event(self):
        return	#添加鼠標(biāo)事件,后續(xù)在這里添加
    
    def compute_initial_figure(self):
        x = np.linspace(0, 10, 100)
        y1 = np.sin(x)
        y2 = np.cos(x)
        y3 = np.sin(x) * 2
        self.line1, = self.axes.plot(x, y1, 'b-', label='sin(x)')
        self.line2, = self.axes.plot(x, y2, 'r-', label='cos(x)')
        self.line3, = self.axes.plot(x, y3, 'g-', label='2*sin(x)')
        self.axes.legend()  # 顯示右上角標(biāo)簽
	

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.widget = QWidget()
        self.setMinimumHeight(600)
        self.setMinimumWidth(800)
        self.showMaximized() # 設(shè)置全屏
        self.setCentralWidget(self.widget)

        layout = QVBoxLayout(self.widget)

        self.mpl_widget = MatplotlibWidget(self.widget, width=5, height=4, dpi=100)
        layout.addWidget(self.mpl_widget)

        self.show()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    mainWin = MainWindow()
    sys.exit(app.exec_())

現(xiàn)在的圖像應(yīng)該是這個效果

添加鼠標(biāo)滾動放大和縮小效果,比例可以自己設(shè)置,我這里設(shè)置的是最大值和最小值差值的10分之1

    def on_mouse_wheel(self, event):
        if self.axes is not None:
            x_min, x_max = self.axes.get_xlim()
            x_delta = (x_max - x_min) / 10		# 控制縮放X軸的比例
            y_min, y_max = self.axes.get_ylim()
            y_delta = (y_max - y_min) / 10		# 控制縮放X軸的比例
            if event.button == "up":
                self.axes.set(xlim=(x_min + x_delta, x_max - x_delta))
                self.axes.set(ylim=(y_min + y_delta, y_max - y_delta))
            elif event.button == "down":
                self.axes.set(xlim=(x_min - x_delta, x_max + x_delta))
                self.axes.set(ylim=(y_min - y_delta, y_max + y_delta))

            self.draw_idle()

添加鼠標(biāo)滾動事件到connect_event()函數(shù)里面

    def connect_event(self):
        self.mpl_connect("scroll_event", self.on_mouse_wheel)	#鼠標(biāo)滾動事件

實現(xiàn)按住鼠標(biāo)向上下左右拖動的效果,拖動的距離同理可以自己控制

    def on_button_press(self, event):
        if event.inaxes is not None:  # 判斷是否在坐標(biāo)軸內(nèi)
            if event.button == 1:
                self.lef_mouse_pressed = True
                self.pre_x = event.xdata
                self.pre_y = event.ydata

    def on_button_release(self, event):
        self.lef_mouse_pressed = False
        
    def on_mouse_move(self, event):
        if event.inaxes is not None and event.button == 1:
            if self.lef_mouse_pressed:	#鼠標(biāo)左鍵按下時才計算
                x_delta = event.xdata - self.pre_x
                y_delta = event.ydata - self.pre_y
                # 獲取當(dāng)前原點和最大點的4個位置
                x_min, x_max = self.axes.get_xlim()
                y_min, y_max = self.axes.get_ylim()
				
                # 控制一次移動鼠標(biāo)拖動的距離
                x_min = x_min - x_delta
                x_max = x_max - x_delta
                y_min = y_min - y_delta
                y_max = y_max - y_delta

                self.axes.set_xlim(x_min, x_max)
                self.axes.set_ylim(y_min, y_max)
                self.draw_idle()

添加鼠標(biāo)按住和松開事件到connect_event()函數(shù)里面

    def connect_event(self):
        self.mpl_connect("scroll_event", self.on_mouse_wheel)
        self.mpl_connect("button_press_event", self.on_button_press)
        self.mpl_connect("button_release_event", self.on_button_release)
        self.mpl_connect("motion_notify_event", self.on_mouse_move)

現(xiàn)在圖像可以實現(xiàn)如下效果

可以看到鼠標(biāo)按住后把圖像拖動了,同時鼠標(biāo)滾動也放大了圖像

實現(xiàn)圖表的高階交互

我們最終想達(dá)到的目標(biāo)是能夠?qū)崿F(xiàn)類似echarts庫的功能,能夠隨著鼠標(biāo)移動顯示一條豎的光標(biāo)線,光標(biāo)線旁邊能夠顯示詳細(xì)信息,效果跟下圖所示差不多

? 接下來我們使用matplotlib實現(xiàn)如圖效果

給圖表添加一個豎光標(biāo)線,在圖表中可以隨著鼠標(biāo)移動而移動,在光標(biāo)線旁邊顯示相應(yīng)的曲線信息

    def init_annotation(self):
        # 初始化光標(biāo)線和注釋
        self.vertline, = self.ax.plot([], [], 'c-', lw=2)
        # 預(yù)置一個空文本顯示橫坐標(biāo)值
        hPackerList = [HPacker(children=[TextArea("", textprops=dict(size=10))])]
        for line in self.axes.get_lines():
            if line == self.vertline:  # 跳過光標(biāo)線
                continue
            line_color = line.get_color()   # 獲取每條曲線的顏色
            text_area = TextArea(line.get_label(), textprops=dict(size=10, color=line_color))   #根據(jù)曲線顏色設(shè)置文字顏色
            hPacker = HPacker(children=[text_area])
            hPackerList.append(hPacker)
        self.text_box = VPacker(children=hPackerList, pad=1, sep=3) # 豎值布局,設(shè)置padding和文字之間上下的間距
        self.annotation_bbox = AnnotationBbox(self.text_box, (0, 0),
                                              xybox=(100, 0),
                                              xycoords='data',
                                              boxcoords="offset points")
        if self.axes is not None:
            self.axes.add_artist(self.annotation_bbox)

將init_annotation函數(shù)放到初始化init函數(shù)里面

添加hover函數(shù),使豎光標(biāo)線和注釋隨著鼠標(biāo)的移動能夠動態(tài)地做出改變,這里使用了annocation_bbox,這個工具網(wǎng)上資料很少,我也是臨時翻英文文檔看的,gpt生成的錯誤代碼用不了,如果需要詳細(xì)了解可以翻閱matlibplot的官方文檔,hPacker和vPacker類似于qt的hBoxlayout和vBoxLayout,還是比較好理解的,還有textarea是可以設(shè)置顏色的

    def hover(self, event):
        if event.inaxes == self.axes:
            x = event.xdata
            if x is not None:
                text = f"x: {x}" #顯示橫坐標(biāo)值
                hPacker_list = self.text_box.get_children()
                time_hPacker = hPacker_list[0]
                time_text_area: TextArea = time_hPacker.get_children()[0]
                time_text_area.set_text(text)   # 更新橫坐標(biāo)值
                for index,line in enumerate(self.axes.get_lines()):
                    if line == self.vertline:  # 跳過光標(biāo)線
                        continue
                    x_data = line.get_xdata()
                    y_data = line.get_ydata()
                    y = np.interp(x, x_data, y_data)
                    # 更新光標(biāo)線的位置
                    self.vertline.set_xdata([x, x])
                    self.vertline.set_ydata([self.axes.get_ylim()[0], self.axes.get_ylim()[1]])
                    # 顯示每條曲線的詳細(xì)信息
                    line_text = f"{line.get_label()}: {y:.3f}"
                    hPacker = hPacker_list[index+1] # 因為橫坐標(biāo)值放在了第一個hPacker中,所以從第二個開始
                    text_area: TextArea = hPacker.get_children()[0]
                    text_area.set_text(line_text)

                # 更新AnnotationBbox的位置
                self.annotation_bbox.xy = (x, event.ydata)
                self.annotation_bbox.set_visible(True)
                self.draw_idle()
            else:
                # 隱藏AnnotationBbox和光標(biāo)線
                self.annotation_bbox.set_visible(False)
                self.vertline.set_xdata([])
                self.vertline.set_ydata([])
                self.draw_idle()

將hover函數(shù)添加到connect_event()函數(shù)中

    def connect_event(self):
        self.mpl_connect("scroll_event", self.on_mouse_wheel)
        self.mpl_connect("button_press_event", self.on_button_press)
        self.mpl_connect("button_release_event", self.on_button_release)
        self.mpl_connect("motion_notify_event", self.on_mouse_move)
        self.mpl_connect("motion_notify_event", self.hover)

最終實現(xiàn)效果

全部代碼

import sys

import numpy as np
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.offsetbox import HPacker, TextArea, VPacker, AnnotationBbox


class MatplotlibWidget(FigureCanvas):
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        self.lef_mouse_pressed = False  # 鼠標(biāo)左鍵是否按下

        self.compute_initial_figure()

        FigureCanvas.__init__(self, fig)
        self.setParent(parent)

        self.connect_event()

        self.init_annotation()

    def connect_event(self):
        self.mpl_connect("scroll_event", self.on_mouse_wheel)
        self.mpl_connect("button_press_event", self.on_button_press)
        self.mpl_connect("button_release_event", self.on_button_release)
        self.mpl_connect("motion_notify_event", self.on_mouse_move)
        self.mpl_connect("motion_notify_event", self.hover)

    def compute_initial_figure(self):
        x = np.linspace(0, 10, 100)
        y1 = np.sin(x)
        y2 = np.cos(x)
        y3 = np.sin(x) * 2
        self.line1, = self.axes.plot(x, y1, 'b-', label='sin(x)')
        self.line2, = self.axes.plot(x, y2, 'r-', label='cos(x)')
        self.line3, = self.axes.plot(x, y3, 'g-', label='2*sin(x)')
        self.axes.legend()  # 顯示右上角標(biāo)簽

    def init_annotation(self):
        # 初始化光標(biāo)線和注釋
        self.vertline, = self.axes.plot([], [], 'c-', lw=2)
        hPackerList = [HPacker(children=[TextArea("", textprops=dict(size=10))])]  # 預(yù)置一個空文本顯示橫坐標(biāo)值
        for line in self.axes.get_lines():
            if line == self.vertline:  # 跳過光標(biāo)線
                continue
            line_color = line.get_color()   # 獲取每條曲線的顏色
            text_area = TextArea(line.get_label(), textprops=dict(size=10, color=line_color))   #根據(jù)曲線顏色設(shè)置文字顏色
            hPacker = HPacker(children=[text_area])
            hPackerList.append(hPacker)
        self.text_box = VPacker(children=hPackerList, pad=1, sep=3) # 豎值布局,設(shè)置padding和文字之間上下的間距
        self.annotation_bbox = AnnotationBbox(self.text_box, (0, 0),
                                              xybox=(100, 0),
                                              xycoords='data',
                                              boxcoords="offset points")
        if self.axes is not None:
            self.axes.add_artist(self.annotation_bbox)

    def on_mouse_wheel(self, event):
        if self.axes is not None:
            x_min, x_max = self.axes.get_xlim()
            x_delta = (x_max - x_min) / 10
            y_min, y_max = self.axes.get_ylim()
            y_delta = (y_max - y_min) / 10
            if event.button == "up":
                self.axes.set(xlim=(x_min + x_delta, x_max - x_delta))
                self.axes.set(ylim=(y_min + y_delta, y_max - y_delta))
            elif event.button == "down":
                self.axes.set(xlim=(x_min - x_delta, x_max + x_delta))
                self.axes.set(ylim=(y_min - y_delta, y_max + y_delta))

            self.draw_idle()

    def on_button_press(self, event):
        if event.inaxes is not None:  # 判斷是否在坐標(biāo)軸內(nèi)
            if event.button == 1:
                self.lef_mouse_pressed = True
                self.pre_x = event.xdata
                self.pre_y = event.ydata

    def on_button_release(self, event):
        self.lef_mouse_pressed = False

    def on_mouse_move(self, event):
        if event.inaxes is not None and event.button == 1:
            if self.lef_mouse_pressed:
                x_delta = event.xdata - self.pre_x
                y_delta = event.ydata - self.pre_y
                # 獲取當(dāng)前原點和最大點的4個位置
                x_min, x_max = self.axes.get_xlim()
                y_min, y_max = self.axes.get_ylim()

                x_min = x_min - x_delta
                x_max = x_max - x_delta
                y_min = y_min - y_delta
                y_max = y_max - y_delta

                self.axes.set_xlim(x_min, x_max)
                self.axes.set_ylim(y_min, y_max)
                self.draw_idle()

    def hover(self, event):
        if event.inaxes == self.axes:
            x = event.xdata
            if x is not None:
                text = f"x: {x}" #顯示橫坐標(biāo)值
                hPacker_list = self.text_box.get_children()
                time_hPacker = hPacker_list[0]
                time_text_area: TextArea = time_hPacker.get_children()[0]
                time_text_area.set_text(text)   # 更新橫坐標(biāo)值
                for index,line in enumerate(self.axes.get_lines()):
                    if line == self.vertline:  # 跳過光標(biāo)線
                        continue
                    x_data = line.get_xdata()
                    y_data = line.get_ydata()
                    y = np.interp(x, x_data, y_data)
                    # 更新光標(biāo)線的位置
                    self.vertline.set_xdata([x, x])
                    self.vertline.set_ydata([self.axes.get_ylim()[0], self.axes.get_ylim()[1]])
                    # 顯示每條曲線的詳細(xì)信息
                    line_text = f"{line.get_label()}: {y:.3f}"
                    hPacker = hPacker_list[index+1] # 因為橫坐標(biāo)值放在了第一個hPacker中,所以從第二個開始
                    text_area: TextArea = hPacker.get_children()[0]
                    text_area.set_text(line_text)

                # 更新AnnotationBbox的位置
                self.annotation_bbox.xy = (x, event.ydata)
                self.annotation_bbox.set_visible(True)
                self.draw_idle()
            else:
                # 隱藏AnnotationBbox和光標(biāo)線
                self.annotation_bbox.set_visible(False)
                self.vertline.set_xdata([])
                self.vertline.set_ydata([])
                self.draw_idle()

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.widget = QWidget()
        self.setMinimumHeight(600)
        self.setMinimumWidth(800)
        self.showMaximized() # 設(shè)置全屏
        self.setCentralWidget(self.widget)

        layout = QVBoxLayout(self.widget)

        self.mpl_widget = MatplotlibWidget(self.widget, width=5, height=4, dpi=100)
        layout.addWidget(self.mpl_widget)

        self.show()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    mainWin = MainWindow()
    sys.exit(app.exec_())

總結(jié)

感覺matplotlib庫更適用于靜態(tài)圖表的分析,即使自己造了這些輪子,但總感覺還是不如echats好用,不過echarts性能方面還是不如matplotlib的,畢竟兩者的應(yīng)用場景確實不一樣。

到此這篇關(guān)于Python結(jié)合matplotlib實現(xiàn)圖表的基本交互的文章就介紹到這了,更多相關(guān)Python matplotlib圖表交互內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python中非常實用的Math模塊函數(shù)教程詳解

    Python中非常實用的Math模塊函數(shù)教程詳解

    Math模塊中,有很多基礎(chǔ)的數(shù)學(xué)知識,我們必須要掌握的,例如:指數(shù)、對數(shù)、三角或冪函數(shù)等。因此,特意借著這篇文章,為大家講解一些該庫
    2021-10-10
  • 詳解Python的整數(shù)是如何實現(xiàn)的

    詳解Python的整數(shù)是如何實現(xiàn)的

    本文我們來聊一聊Python的整數(shù),我們知道Python的整數(shù)是不會溢出的,換句話說,它可以計算無窮大的數(shù),只要你的內(nèi)存足夠,它就能計算。但問題是,Python底層又是C實現(xiàn)的,那么它是怎么做到整數(shù)不溢出的呢?本文就來詳細(xì)說說
    2022-11-11
  • Python可視化單詞統(tǒng)計詞頻統(tǒng)計中文分詞的實現(xiàn)步驟

    Python可視化單詞統(tǒng)計詞頻統(tǒng)計中文分詞的實現(xiàn)步驟

    這篇文章主要介紹了Python可視化單詞統(tǒng)計詞頻統(tǒng)計中文分詞,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-11-11
  • python中取絕對值簡單方法總結(jié)

    python中取絕對值簡單方法總結(jié)

    在本篇內(nèi)容里小編給大家整理的是關(guān)于python中取絕對值簡單方法,需要的朋友們可以學(xué)習(xí)下。
    2020-07-07
  • Python生成器generator用法示例

    Python生成器generator用法示例

    這篇文章主要介紹了Python生成器generator用法,結(jié)合實例形式分析了Python生成器generator常見操作技巧與相關(guān)注意事項,需要的朋友可以參考下
    2018-08-08
  • Python中g(shù)etattr函數(shù)和hasattr函數(shù)作用詳解

    Python中g(shù)etattr函數(shù)和hasattr函數(shù)作用詳解

    這篇文章主要介紹了Python中g(shù)etattr函數(shù)和hasattr函數(shù)作用的相關(guān)知識,非常不錯具有參考借鑒價值,需要的朋友可以參考下
    2016-06-06
  • 深入了解Django中間件及其方法

    深入了解Django中間件及其方法

    這篇文章主要介紹了簡單了解Django中間件及其方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-07-07
  • Python可變參數(shù)*args和**kwargs

    Python可變參數(shù)*args和**kwargs

    本文我們將通過示例了解 Python函數(shù)的可變參數(shù)*args和?**kwargs的用法,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Linux下為不同版本python安裝第三方庫

    Linux下為不同版本python安裝第三方庫

    本文給大家分享了下作者是如何在linux下為python2.x以及python3.x安裝第三方庫的方法,十分的實用,有需要的小伙伴可以參考下
    2016-08-08
  • django自帶調(diào)試服務(wù)器的使用詳解

    django自帶調(diào)試服務(wù)器的使用詳解

    今天小編就為大家分享一篇django自帶調(diào)試服務(wù)器的使用詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-08-08

最新評論

井陉县| 宁化县| 砀山县| 汉寿县| 新田县| 林甸县| 菏泽市| 滦南县| 固原市| 海南省| 普格县| 沂南县| 恩平市| 长子县| 军事| 讷河市| 鄂伦春自治旗| 沙湾县| 饶平县| 营口市| 新闻| 平昌县| 西安市| 汪清县| 上蔡县| 壶关县| 东山县| 台安县| 大竹县| 武山县| 鹤庆县| 井冈山市| 武陟县| 阿拉善右旗| 高台县| 渭南市| 台州市| 改则县| 永康市| 清徐县| 南皮县|