【问题标题】:Matplotlib navigation toolbar embeded in pyqt5 window - reset original view crashpyqt5窗口中嵌入的Matplotlib导航工具栏-重置原始视图崩溃
【发布时间】:2018-06-24 04:13:48
【问题描述】:

我正在尝试使用 matplotlib 的交互式绘图功能,它嵌入到具有 mpl 版本的 pyqt5 窗口中:2.1.1

我在这里提供了带有 UI 文件的最小示例:

https://ufile.io/8irs7

和python代码:

import matplotlib
matplotlib.use("Qt5Agg")
import sys

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure

import pandas as pd
import numpy as np

from PyQt5 import  QtWidgets, uic  # works for pyqt5
from PyQt5.Qt import  QVBoxLayout


# import numpy as np
qtCreatorFile = "window.ui" # Enter file here.
# qtCreatorFile = "pssga.ui"


Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)

class PlotSeries(QtWidgets.QMainWindow, Ui_MainWindow):

    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)
        Ui_MainWindow.__init__(self)
        self.setupUi(self)

                #create figure
        self.fig=Figure()

        self.axes = self.fig.add_subplot(111)
        self.axes.grid()



        self.canvas=FigureCanvas(self.fig)
        #add plot toolbar from matplotlib
        self.toolbar = NavigationToolbar(self.canvas, self)

        #create new layout
        layout = QVBoxLayout()

        layout.addWidget(self.canvas)
        layout.addWidget(self.toolbar)

        #add layout to qwidget
        self.plotlayout=self.widgetplot.setLayout(layout)

        #draw the canvas ! 
        self.canvas.draw()
        #now let's call widget 
        self.plot_basegraph()


    def plot_basegraph(self):
        #create simple time series 
        for i in range(0,5): 
            ts=pd.Series(np.random.randn(2000), index=pd.date_range('1/1/2015',freq='h', periods=2000),name="series "+str(i))
            if i==0:
                self.stackTSPD=ts.to_frame()
            else:
                self.stackTSPD=self.stackTSPD.join(ts.to_frame(),how='inner')
        print(matplotlib.__version__)

        self.axes.clear()
        self.axes.grid()

        for i in self.stackTSPD.columns.values:
            t=self.stackTSPD.index.to_pydatetime()

            y=self.stackTSPD[i].values
            self.axes.plot(t,y, label=i)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = PlotSeries()
    window.show()
    sys.exit(app.exec_())

放大到时间序列,平移按预期工作,但是当我尝试按“主页”时 - 重置原始视图时它会崩溃:

Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/backends/backend_qt5agg.py", line 155, in __draw_idle_agg
    self.draw()
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/backends/backend_qt5agg.py", line 127, in draw
    super(FigureCanvasQTAggBase, self).draw()
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/backends/backend_agg.py", line 430, in draw
    self.figure.draw(self.renderer)
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/figure.py", line 1299, in draw
    renderer, self, artists, self.suppressComposite)
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/image.py", line 138, in _draw_list_compositing_images
    a.draw(renderer)
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/axes/_base.py", line 2437, in draw
    mimage._draw_list_compositing_images(renderer, self, artists)
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/image.py", line 138, in _draw_list_compositing_images
    a.draw(renderer)
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/artist.py", line 55, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/axis.py", line 1133, in draw
    ticks_to_draw = self._update_ticks(renderer)
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/axis.py", line 974, in _update_ticks
    tick_tups = list(self.iter_ticks())
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/axis.py", line 917, in iter_ticks
    majorLocs = self.major.locator()
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/dates.py", line 1061, in __call__
    self.refresh()
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/dates.py", line 1081, in refresh
    dmin, dmax = self.viewlim_to_dt()
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/dates.py", line 839, in viewlim_to_dt
    return num2date(vmin, self.tz), num2date(vmax, self.tz)
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/dates.py", line 445, in num2date
    return _from_ordinalf(x, tz)
  File "/usr/local/lib/python3.5/dist-packages/matplotlib/dates.py", line 260, in _from_ordinalf
    dt = datetime.datetime.fromordinal(ix).replace(tzinfo=UTC)
ValueError: ordinal must be >= 1

当我尝试将此时间序列图嵌入到 pyqt5 表单中时,此行为就开始了。当绘图在新的 GTK 窗口中单独打开时,它可以正常工作。我附上了在非 pyqt 窗口中打开的工作主页按钮的示例......

'''
Created on 16. jan. 2018

@author: gregor
'''

import matplotlib as mpl
import matplotlib.pyplot as plt
# matplotlib.use("Qt5Agg")


# from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
# from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
# from matplotlib.figure import Figure

import pandas as pd
import numpy as np





class PlotSeries():

    def __init__(self):


        self.fig,self.axes = plt.subplots()

        self.axes.grid()



#         self.canvas=FigureCanvas(self.fig)
        #add plot toolbar from matplotlib
#         self.toolbar = NavigationToolbar(self.canvas, self)

        #create new layout
#         layout = QVBoxLayout()
#         
#         layout.addWidget(self.canvas)
#         layout.addWidget(self.toolbar)
#         
#         #add layout to qwidget
#         self.plotlayout=self.widgetplot.setLayout(layout)
#         
#         #draw the canvas ! 
#         self.canvas.draw()
        #now let's call widget 
        self.plot_basegraph()


    def plot_basegraph(self):
        #create simple time series 
        for i in range(0,5): 
            ts=pd.Series(np.random.randn(2000), index=pd.date_range('1/1/2015',freq='h', periods=2000),name="series "+str(i))
            if i==0:
                self.stackTSPD=ts.to_frame()
            else:
                self.stackTSPD=self.stackTSPD.join(ts.to_frame(),how='inner')
        print(mpl.__version__)

        self.axes.clear()
        self.axes.grid()

        for i in self.stackTSPD.columns.values:
            t=self.stackTSPD.index.to_pydatetime()

            y=self.stackTSPD[i].values
            self.axes.plot_date(t,y, label=i)
        plt.show()



if __name__ == "__main__":
 PlotSeries()  

谢谢!

【问题讨论】:

  • 您需要查看该问题的minimal reproducible example 才能在此处提供帮助。没有这样的例子,这个问题就没用了。
  • 当然我会尝试在一个最小的例子上重现这个......
  • 你确定这完全取决于 PyQt 的使用吗?使用 pyplot 和 plt.show() 创建的正常绘图是否显示不同的行为?
  • 我已经测试了 pyplot 和 plt.show() 示例,它可以正常工作。我附上了最小的例子来发布.....

标签: python matplotlib navigation pyqt5 interactive


【解决方案1】:

简答:删除self.canvas.draw()这一行。

说明:
在画布包含任何数据之前绘制它。但是由于此时画布已经配备了工具栏,因此“Home”状态是轴没有数据的状态。

这本身不会有问题,但一旦绘制日期,它就会变成一个问题。
空轴的默认初始限制为 (0,1)。因此,“主页”状态将引用这些限制,单击按钮后将尝试恢复这些限制。
然而,与此同时,该轴已被告知要保留日期。日期范围为(1, whatever)。因此0 不是日期轴上的有效数字。

那么解决方法就很简单了。只需从代码中删除行 self.canvas.draw() 即可。无论如何,它不能完成任何必需的任务。

【讨论】:

  • 谢谢!我没想到这么简单的解决方案。我在想这与github.com/matplotlib/matplotlib/issues/6023 这个假定的错误有关...我花了 2 天时间寻找解决方案。如果不出意外,这是一个使用 ui 将 matplotlib 嵌入到 pyqt5 的好例子。非常感谢您的帮助!!
  • 未来:如果您有工作代码和非工作代码,请寻找差异。这里除了与 qt 相关的所有内容外,区别在于 import matplotlib.pyplotcanvas.draw()。然后可以测试两者以找出问题。
猜你喜欢
  • 2018-11-03
  • 2018-07-14
  • 1970-01-01
  • 2020-10-17
  • 2019-04-14
  • 2012-09-23
  • 1970-01-01
  • 1970-01-01
  • 2019-03-23
相关资源
最近更新 更多