使用 pyqt5 和 matplotlib 进行图形滚动

Posted

技术标签:

【中文标题】使用 pyqt5 和 matplotlib 进行图形滚动【英文标题】:Graph scrolling using pyqt5 and malplotlib 【发布时间】:2019-01-15 11:51:59 【问题描述】:

previous question (link)

在这里,我希望当我使用鼠标移动滚动条时,它不会滚动图形,但是当我离开鼠标时,图形将被更新。这里每次轴限制都在更新,但我希望当我将鼠标移动滚动条到某个位置时它会更新轴限制。注意:这里 y 轴技巧标签位置将是固定的,它会是可见的,但是当我滚动图表时值会更新。我还希望滚动条从正确的位置开始。

这是我的程序:

import sys
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from PyQt5.QtWidgets import QMainWindow,QVBoxLayout
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore, QtGui, QtWidgets
import datetime
from matplotlib.dates import num2date, date2num
from mpl_finance import candlestick_ochl as candlestick
import numpy as np
import matplotlib.ticker as ticker
import matplotlib.dates as mdates
import pylab as pl
class MainWindow_code_serarch(object):

    def setup_code_serarch(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1070, 680)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayoutWidget1 = QtWidgets.QWidget(self.centralwidget)
        self.verticalLayoutWidget1.setGeometry(QtCore.QRect(17, 30, 741, 13))
        self.verticalLayoutWidget1.setObjectName("verticalLayoutWidget")
        self.verticalLayout1 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget1)
        self.verticalLayout1.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout1.setObjectName("verticalLayout1")
        self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.verticalLayoutWidget.setGeometry(QtCore.QRect(17, 10, 1040, 603))
        self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setObjectName("verticalLayout")
        self.figure = Figure(figsize=(100,7.2), dpi=80, facecolor='k')
        self.canvas = FigureCanvas(self.figure)
        self.canvas.draw()
        # self.widget = QtWidgets.QWidget()
        # self.scroll_area = QtWidgets.QScrollArea(self.widget)
        # self.scroll_area.setWidget(self.canvas)
        # self.verticalLayout.addWidget(self.scroll_area)
        self.scroll = QtWidgets.QScrollBar(QtCore.Qt.Horizontal)
        self.axes, self.axes2 = self.figure.subplots(nrows=2, sharex=True)


        self.verticalLayout.addWidget(self.canvas)
        self.verticalLayout.addWidget(self.scroll)


        data = 
            'date': ['2018/10/29','2018/10/30', '2018/11/03', '2018/11/04', '2018/11/05', '2018/11/07', '2018/11/10', '2018/11/11','2018/11/12','2018/11/13'],
            'open': [8824,8824, 8726.31, 8642.14, 8531.51, 8630.25, 8602.50, 8640.22,8202.50, 8610.22],
            'high': [8858,8858, 8748.60, 8551.36, 8653.16, 8476.69, 8630, 8570.56 ,8602.50, 8640.22],
            'low': [8688,8688, 8743.67, 8550.76, 8449.50, 8631.83, 8602.18, 8743.22 ,8502.50, 8540.22],
            'close': [8820,8820, 8747.17, 8550.52, 8553., 8517.10, 8628.78, 8588.52 ,8602.50, 8640.22],
            'volume': [17759.56,17759.56, 120000.17, 18739.52, 38599.50, 16517.10, 17723.78, 15588.52 ,28602.50, 28640.22]
        
        x = date2num([datetime.datetime.strptime(d, '%Y/%m/%d').date() for d in data['date']])
        t= np.arange(len(data['date']))
        candle_trace = zip(t, data['open'], data['high'], data['low'], data['close'], data['volume'])
        candlestick(self.axes, candle_trace, width=.75, colorup='g', colordown='r')
        self.axes2.plot(t, [1, 2, 3, 4, 7, 8, 9,6,6,9])
        self.axes.set_position([0.02, 0.37, 0.88, 0.6])
        self.axes2.set_position([0.02, 0.15, 0.88, 0.22])
        self.axes.tick_params(axis='both', color='#ffffff', labelcolor='#ffffff')
        self.axes.yaxis.tick_right()
        self.axes2.tick_params(axis='both', color='#ffffff', labelcolor='#ffffff')
        self.axes2.grid(color='lightgray', linewidth=.5, linestyle=':')
        self.axes.grid(color='lightgray', linewidth=.5, linestyle=':')
        self.axes2.yaxis.tick_right()
        self.axes.autoscale_view()
        self.axes2.autoscale_view()
        self.axes.set_facecolor('#041105')
        self.axes2.set_facecolor('#041105')
        # N = len(dates)
        self.axes.set_xticks(range(0, len((x)), 1))
        self.axes.set_xticklabels([mdates.num2date(d).strftime('%b-%d') for d in x])
        self.axes.set_xticklabels([mdates.num2date(d).strftime('%Y-%m-%d') for d in x])
        self.axes2.set_xticklabels([mdates.num2date(d).strftime('%Y-%m-%d') for d in x])
        self.canvas.draw()
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 246, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        # self.pushButton.clicked.connect(self.graphShowCode)
        self.step = .1
        self.setupSlider()

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        # self.pushButton.setText(_translate("MainWindow", "OK"))

    def setupSlider(self):
        self.lims = np.array(self.axes2.get_xlim())
        print("limit"+str(self.lims))
        self.scroll.setPageStep(self.step * 100)
        self.scroll.actionTriggered.connect(self.update)
        self.update()

    def update(self, evt=None):
        r = self.scroll.value() / ((1 + self.step) * 100)
        l1 = self.lims[0] + r * np.diff(self.lims)
        l2 = l1 + np.diff(self.lims) * self.step
        self.axes2.set_xlim(l1, l2)
        self.axes.set_xlim(l1, l2)
        print(self.scroll.value(), l1, l2)
        self.figure.canvas.draw_idle()
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = MainWindow_code_serarch()
    ui.setup_code_serarch(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

【问题讨论】:

QScrollBar 提供了您可以使用的其他事件,而不是此处使用的actionTriggered。查看您要将呼叫栏连接到哪个。在setupSlider,您可以根据自己的喜好设置初始限制。 谢谢先生。通过采取 sliderReleased() action 解决了我的问题的一半。现在我如何设置我的图形滚动的初始限制。我如何选择图表的最后一部分? 您可以通过self.axes2.set_xlim 自己设置限制,只需将它们设置为您想要的范围即可。 我想最初加载图表的最后一部分。 self.axes2.set_xlim 在哪里设置。 【参考方案1】:

QScrollBar 从 QAbstractSlider 继承了一组全面的信号。 我使用了sliderReleased()。因为当用户释放滑块时它会发出。从正确的位置开始滚动条。我启动滚动条值 (99) 因为在这里我发现滚动条最小值是 (0) 而最大值是 (99)。

我的程序:

import sys
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from PyQt5.QtWidgets import QMainWindow,QVBoxLayout
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore, QtGui, QtWidgets
import datetime
from matplotlib.dates import num2date, date2num
from mpl_finance import candlestick_ochl as candlestick
import numpy as np
import matplotlib.ticker as ticker
import matplotlib.dates as mdates
import pylab as pl
class MainWindow_code_serarch(object):

    def setup_code_serarch(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1070, 680)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayoutWidget1 = QtWidgets.QWidget(self.centralwidget)
        self.verticalLayoutWidget1.setGeometry(QtCore.QRect(17, 30, 741, 13))
        self.verticalLayoutWidget1.setObjectName("verticalLayoutWidget")
        self.verticalLayout1 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget1)
        self.verticalLayout1.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout1.setObjectName("verticalLayout1")
        self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.verticalLayoutWidget.setGeometry(QtCore.QRect(17, 10, 1040, 603))
        self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setObjectName("verticalLayout")
        self.figure = Figure(figsize=(100,7.2), dpi=80, facecolor='k')
        self.canvas = FigureCanvas(self.figure)
        self.canvas.draw()
        # self.widget = QtWidgets.QWidget()
        # self.scroll_area = QtWidgets.QScrollArea(self.widget)
        # self.scroll_area.setWidget(self.canvas)
        # self.verticalLayout.addWidget(self.scroll_area)
        self.scroll = QtWidgets.QScrollBar(QtCore.Qt.Horizontal)
        self.axes, self.axes2 = self.figure.subplots(nrows=2, sharex=True)


        self.verticalLayout.addWidget(self.canvas)
        self.verticalLayout.addWidget(self.scroll)
        self.scroll.setValue(99)


        data = 
            'date': ['2018/10/29','2018/10/30', '2018/11/03', '2018/11/04', '2018/11/05', '2018/11/07', '2018/11/10', '2018/11/11','2018/11/12','2018/11/13'],
            'open': [8824,8824, 8726.31, 8642.14, 8531.51, 8630.25, 8602.50, 8640.22,8202.50, 8610.22],
            'high': [8858,8858, 8748.60, 8551.36, 8653.16, 8476.69, 8630, 8570.56 ,8602.50, 8640.22],
            'low': [8688,8688, 8743.67, 8550.76, 8449.50, 8631.83, 8602.18, 8743.22 ,8502.50, 8540.22],
            'close': [8820,8820, 8747.17, 8550.52, 8553., 8517.10, 8628.78, 8588.52 ,8602.50, 8640.22],
            'volume': [17759.56,17759.56, 120000.17, 18739.52, 38599.50, 16517.10, 17723.78, 15588.52 ,28602.50, 28640.22]
        
        x = date2num([datetime.datetime.strptime(d, '%Y/%m/%d').date() for d in data['date']])
        t= np.arange(len(data['date']))
        candle_trace = zip(t, data['open'], data['high'], data['low'], data['close'], data['volume'])
        candlestick(self.axes, candle_trace, width=.75, colorup='g', colordown='r')
        self.axes2.plot(t, [1, 2, 3, 4, 7, 8, 9,6,6,9])
        self.axes.set_position([0.02, 0.37, 0.88, 0.6])
        self.axes2.set_position([0.02, 0.15, 0.88, 0.22])
        self.axes.tick_params(axis='both', color='#ffffff', labelcolor='#ffffff')
        self.axes.yaxis.tick_right()
        self.axes2.tick_params(axis='both', color='#ffffff', labelcolor='#ffffff')
        self.axes2.grid(color='lightgray', linewidth=.5, linestyle=':')
        self.axes.grid(color='lightgray', linewidth=.5, linestyle=':')
        self.axes2.yaxis.tick_right()
        self.axes.autoscale_view()
        self.axes2.autoscale_view()
        self.axes.set_facecolor('#041105')
        self.axes2.set_facecolor('#041105')
        # N = len(dates)
        self.axes.set_xticks(range(0, len((x)), 1))
        self.axes.set_xticklabels([mdates.num2date(d).strftime('%b-%d') for d in x])
        self.axes.set_xticklabels([mdates.num2date(d).strftime('%Y-%m-%d') for d in x])
        self.axes2.set_xticklabels([mdates.num2date(d).strftime('%Y-%m-%d') for d in x])
        self.canvas.draw()
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 246, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        # self.pushButton.clicked.connect(self.graphShowCode)
        self.step = .1
        self.setupSlider()

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        # self.pushButton.setText(_translate("MainWindow", "OK"))

    def setupSlider(self):
        self.lims = np.array(self.axes2.get_xlim())
        print("limit"+str(self.lims))
        self.scroll.setPageStep(self.step * 100)
        # self.scroll.actionTriggered.connect(self.update)
        self.scroll.sliderReleased.connect(self.update)

        self.update()

    def update(self, evt=None):
        r = self.scroll.value() / ((1 + self.step) * 100)
        l1 = self.lims[0] + r * np.diff(self.lims)
        l2 = l1 + np.diff(self.lims) * self.step
        self.axes2.set_xlim(l1, l2)
        self.axes.set_xlim(l1, l2)
        print(self.scroll.value(), l1, l2)
        self.figure.canvas.draw_idle()
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = MainWindow_code_serarch()
    ui.setup_code_serarch(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

【讨论】:

以上是关于使用 pyqt5 和 matplotlib 进行图形滚动的主要内容,如果未能解决你的问题,请参考以下文章

在 pyqt5 中绘制 Matplotlib 图

将数据从散点图传输到 lineEdit - Matplotlib 和 Pyqt5

如何在 Pyqt5 环境中使用 Pickle 保存 Matplotlib 图?

Python 3.6.3:PyQt5 Matplotlib 条 x 最大面积。 10 项

PyQT5 与 matplotlib 图,事件循环已经在运行

在 pyqt5 gui 中显示 matplotlib 动画图并同时保存在 mp4 中的问题