- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在 PyQt5 窗口中有一个实时 matplotlib 图表:
您可以在此处阅读有关我如何使此代码工作的更多信息:
How to make a fast matplotlib live plot in a PyQt5 GUI
请将下面的代码复制粘贴到 python 文件中,并使用 Python 3.7 运行它:
#####################################################################################
# #
# PLOT A LIVE GRAPH IN A PYQT WINDOW #
# #
#####################################################################################
from __future__ import annotations
from typing import *
import sys
import os
from PyQt5 import QtWidgets, QtCore
from matplotlib.backends.backend_qt5agg import FigureCanvas
import matplotlib as mpl
import matplotlib.figure as mpl_fig
import matplotlib.animation as anim
import matplotlib.style as style
import numpy as np
style.use('ggplot')
class ApplicationWindow(QtWidgets.QMainWindow):
'''
The PyQt5 main window.
'''
def __init__(self):
super().__init__()
# 1. Window settings
self.setGeometry(300, 300, 800, 400)
self.setWindowTitle("Matplotlib live plot in PyQt")
self.frm = QtWidgets.QFrame(self)
self.frm.setStyleSheet("QWidget { background-color: #eeeeec; }")
self.lyt = QtWidgets.QVBoxLayout()
self.frm.setLayout(self.lyt)
self.setCentralWidget(self.frm)
# 2. Place the matplotlib figure
self.myFig = MyFigureCanvas(x_len=200, y_range=[0, 100], interval=20)
self.lyt.addWidget(self.myFig)
# 3. Show
self.show()
return
class MyFigureCanvas(FigureCanvas, anim.FuncAnimation):
'''
This is the FigureCanvas in which the live plot is drawn.
'''
def __init__(self, x_len:int, y_range:List, interval:int) -> None:
'''
:param x_len: The nr of data points shown in one plot.
:param y_range: Range on y-axis.
:param interval: Get a new datapoint every .. milliseconds.
'''
FigureCanvas.__init__(self, mpl_fig.Figure())
# Range settings
self._x_len_ = x_len
self._y_range_ = y_range
# Store two lists _x_ and _y_
x = list(range(0, x_len))
y = [0] * x_len
# Store a figure and ax
self._ax_ = self.figure.subplots()
self._ax_.set_ylim(ymin=self._y_range_[0], ymax=self._y_range_[1])
self._line_, = self._ax_.plot(x, y)
# Call superclass constructors
anim.FuncAnimation.__init__(self, self.figure, self._update_canvas_, fargs=(y,), interval=interval, blit=True)
return
def _update_canvas_(self, i, y) -> None:
'''
This function gets called regularly by the timer.
'''
y.append(round(get_next_datapoint(), 2)) # Add new datapoint
y = y[-self._x_len_:] # Truncate list _y_
self._line_.set_ydata(y)
# Print size of bounding box (in pixels)
bbox = self.figure.get_window_extent().transformed(self.figure.dpi_scale_trans.inverted())
width, height = bbox.width * self.figure.dpi, bbox.height * self.figure.dpi
print(f"bbox size in pixels = {width} x {height}")
return self._line_,
# Data source
# ------------
n = np.linspace(0, 499, 500)
d = 50 + 25 * (np.sin(n / 8.3)) + 10 * (np.sin(n / 7.5)) - 5 * (np.sin(n / 1.5))
i = 0
def get_next_datapoint():
global i
i += 1
if i > 499:
i = 0
return d[i]
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
qapp.exec_()
我需要知道从 x_min
到 x_max
的像素数:
请注意,x 轴实际上超出了 x_min
和 x_max
边界。我不需要知道总长度。只是从 x_min
到 x_max
的长度。
我已经找到了一种获取图形边界框的方法。请注意 _update_canvas_()
函数中的以下代码行:
# Print size of bounding box (in pixels)
bbox = self.figure.get_window_extent().transformed(self.figure.dpi_scale_trans.inverted())
width, height = bbox.width * self.figure.dpi, bbox.height * self.figure.dpi
print(f"bbox size in pixels = {width} x {height}")
这给了我一个尺寸为 778.0 x 378.0 像素的边界框。这是一个很好的起点,但我不知道如何从这里继续。
我还注意到这个边界框大小从第一次开始就没有正确打印出来。第一次运行 _update_canvas_()
函数会打印出 640.0 x 480.0 像素的边界框,这是完全错误的。从第二次开始,打印尺寸是正确的。为什么?
我尝试了两种解决方案。第一个基于 @ImportanceOfBeingErnes 描述的方法(请参阅 Axes class - set explicitly size (width/height) of axes in given units ),第二个基于 @Eyllanesc 的答案。
#####################################################################################
# #
# PLOT A LIVE GRAPH IN A PYQT WINDOW #
# #
#####################################################################################
from __future__ import annotations
from typing import *
import sys
import os
from PyQt5 import QtWidgets, QtCore
from matplotlib.backends.backend_qt5agg import FigureCanvas
import matplotlib as mpl
import matplotlib.figure as mpl_fig
import matplotlib.animation as anim
import matplotlib.style as style
import numpy as np
style.use('ggplot')
def get_width_method_a(ax, dpi, canvas):
l = float(ax.figure.subplotpars.left)
r = float(ax.figure.subplotpars.right)
x, y, w, h = ax.figure.get_tightbbox(renderer=canvas.get_renderer()).bounds
return float(dpi) * float(w - (l + r))
def get_width_eyllanesc(ax):
""" Based on answer from @Eyllanesc"""
""" See below """
y_fake = 0
x_min, x_max = 0, 200
x_pixel_min, _ = ax.transData.transform((x_min, y_fake))
x_pixel_max, _ = ax.transData.transform((x_max, y_fake))
return x_pixel_max - x_pixel_min
class ApplicationWindow(QtWidgets.QMainWindow):
'''
The PyQt5 main window.
'''
def __init__(self):
super().__init__()
# 1. Window settings
self.setGeometry(300, 300, 800, 400)
self.setWindowTitle("Matplotlib live plot in PyQt")
self.frm = QtWidgets.QFrame(self)
self.frm.setStyleSheet("QWidget { background-color: #eeeeec; }")
self.lyt = QtWidgets.QVBoxLayout()
self.frm.setLayout(self.lyt)
self.setCentralWidget(self.frm)
# 2. Place the matplotlib figure
self.myFig = MyFigureCanvas(x_len=200, y_range=[0, 100], interval=20)
self.lyt.addWidget(self.myFig)
# 3. Show
self.show()
return
class MyFigureCanvas(FigureCanvas, anim.FuncAnimation):
'''
This is the FigureCanvas in which the live plot is drawn.
'''
def __init__(self, x_len:int, y_range:List, interval:int) -> None:
'''
:param x_len: The nr of data points shown in one plot.
:param y_range: Range on y-axis.
:param interval: Get a new datapoint every .. milliseconds.
'''
FigureCanvas.__init__(self, mpl_fig.Figure())
# Range settings
self._x_len_ = x_len
self._y_range_ = y_range
# Store two lists _x_ and _y_
x = list(range(0, x_len))
y = [0] * x_len
# Store a figure and ax
self._ax_ = self.figure.subplots()
self._ax_.set_ylim(ymin=self._y_range_[0], ymax=self._y_range_[1])
self._line_, = self._ax_.plot(x, y)
self._line_.set_ydata(y)
print("")
print(f"width in pixels (first call, method is 'method_a') = {get_width_method_a(self._ax_, self.figure.dpi, self)}")
print(f"width in pixels (first call, method is 'eyllanesc') = {get_width_eyllanesc(self._ax_)}")
# Call superclass constructors
anim.FuncAnimation.__init__(self, self.figure, self._update_canvas_, fargs=(y,), interval=interval, blit=True)
return
def _update_canvas_(self, i, y) -> None:
'''
This function gets called regularly by the timer.
'''
y.append(round(get_next_datapoint(), 2)) # Add new datapoint
y = y[-self._x_len_:] # Truncate list _y_
self._line_.set_ydata(y)
print("")
print(f"width in pixels (method is 'method_a') = {get_width_method_a(self._ax_, self.figure.dpi, self)}")
print(f"width in pixels (method is 'eyllanesc') = {get_width_eyllanesc(self._ax_)}")
return self._line_,
# Data source
# ------------
n = np.linspace(0, 499, 500)
d = 50 + 25 * (np.sin(n / 8.3)) + 10 * (np.sin(n / 7.5)) - 5 * (np.sin(n / 1.5))
i = 0
def get_next_datapoint():
global i
i += 1
if i > 499:
i = 0
return d[i]
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
qapp.exec_()
结论:
正确答案是550 像素,这是我在打印屏幕上测量的值。现在,当我运行程序时,我得到以下输出:
width in pixels (first call, method is 'method_a') = 433.0972222222222
width in pixels (first call, method is 'eyllanesc') = 453.1749657377798
width in pixels (method is 'method_a') = 433.0972222222222
width in pixels (method is 'eyllanesc') = 453.1749657377798
width in pixels (method is 'method_a') = 540.0472222222223
width in pixels (method is 'eyllanesc') = 550.8908177249887
...
这两种方法的第一次调用给出了错误的结果。从第三次(!)调用开始,它们都给出了相当好的结果,@Eyllanesc 的方法是获胜者。
如何解决第一次调用结果错误的问题?
最佳答案
对于 old answer我必须进行计算,在你的例子中是:
y_fake = 0
x_min, x_max = 0, 200
x_pixel_min, _ = self._ax_.transData.transform((x_min, y_fake))
x_pixel_max, _ = self._ax_.transData.transform((x_max, y_fake))
print(
f"The length in pixels between x_min: {x_min} and x_max: {x_max} is: {x_pixel_max - x_pixel_min}"
)
注意:
计算考虑了绘制的内容,因此在最初的时刻它仍在绘制,因此结果是正确的,但我们的眼睛无法区分它们。如果你想在没有动画的情况下获得正确的尺寸,则必须在绘画稳定时计算该值,这很难计算,解决方法是使用 QTimer 稍后进行测量:
# ...
self._ax_ = self.figure.subplots()
self._ax_.set_ylim(ymin=self._y_range_[0], ymax=self._y_range_[1])
self._line_, = self._ax_.plot(x, y)
QtCore.QTimer.singleShot(100, self.calculate_length)
# ...
def calculate_length(self):
y_fake = 0
x_min, x_max = 0, 200
x_pixel_min, _ = self._ax_.transData.transform((x_min, y_fake))
x_pixel_max, _ = self._ax_.transData.transform((x_max, y_fake))
print(
f"The length in pixels between x_min: {x_min} and x_max: {x_max} is: {x_pixel_max - x_pixel_min}"
)
关于python - PyQt5 窗口中嵌入的 matplotlib 图形的 x 轴大小(以像素为单位),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57911383/
我无法在此图中定位轴标签。我喜欢放置顶部标签,使管道与网格对齐,并放置左右标签,以便它们不接触绘图。 我试过了 ax.tick_params(axis='both', which='both'
我使用的是 python 2,下面的代码只是使用了一些示例数据,我的实际数据可能有不同的长度,并且可能不是很细。 import numpy as np import datetime i
给定坐标 [1,5,7,3,5,10,3,6,8]为 matplotlib.pyplot ,如何突出显示或着色线条的不同部分。例如,列表中的坐标 1-3 ( [1,5,7,3] ) 表示属性 a .我
我正在matplotlib中绘制以下图像。 我的问题是,图像看起来像这样,但是,我想使背景变暗,因为当我打印该图像时,灰度部分不会出现在打印物中。有人可以告诉我API进行此更改吗? 我使用简单的API
这是关于matplotlib的一个非常基本的问题,但是我不知道该怎么做: 我想绘制多个图形,并使用绘制窗口中的箭头从一个移到另一个。 目前,我只知道如何创建多个图并将其绘制在不同的窗口中,如下所示:
在 matplotlib 中绘制小块对象时,由于显示分辨率而引入了伪影。使用抗锯齿并不能解决问题。 这个问题有解决方案吗? import matplotlib.pyplot as plt impo
对于直方图,有一个简单的内置选项 histtype='step' .如何制作相同风格的条形图? 最佳答案 [阅读评论后添加答案] 将可选关键字设置为 fill=False对于条形图: import m
我正在尝试在 (6X3) 网格上创建子图。我对图例的位置有疑问。图例对所有子图都是通用的。 lgend 现在与 y 轴标签重叠 我尝试删除 constrained_layout=True 选项。但这在
我有一个带有一些线段( LineCollection )和一些点的图表。这些线和点有一些与它们相关的值,但没有绘制出来。我希望能够添加鼠标悬停工具提示或其他方法来轻松找到点和线的关联值。这对于点或线段
我想创建一个带有对齐不同曲线文本的图例的图。这是一个最小的工作示例: import matplotlib.pyplot as plt import numpy as np x=np.linspace(
可以说我正在用matplotlib绘制一条线并添加一个图例。 在图例中,其显示为------ Label。当绘制较小的图形尺寸以进行打印时,我发现该行的默认水平长度太长。 是否存在将------ La
我正在使用 matplotlib 构建一个 3D 散点图,但无法使生成的图形具有所有 3 个轴的共同原点。我怎样才能做到这一点? 我的代码(到目前为止),我还没有为轴规范实现任何定义,因为我对 Pyt
我有一个我想使用的绘图布局,其中 9 个不同的数据簇被布置在一个方形网格上。网格中的每个框都包含 3 个并排布置的箱线图。 我最初的想法是这将适合 3x3 子图布局,每个单独的子图本身被划分为 3x1
我的图形从y=-1变为y=10 我想在任意位置写一小段文字,例如x=2000,y=5: ax.annotate('MgII', xy=(2000.0, 5.0), xycoords='data')
我想使用LateX格式来构建一个表达式,其中出现一些数字,但这些数字是用LateX表达式中的变量表示的。 实际的目标是在axes.annotate()方法中使用它,但是为了讨论起见,这里是一个原理代码
我需要比较两组的二维分布。 当我使用 matplotlib.pyplot.contourf并覆盖图,每个等高线图的背景颜色填充整个图空间。有没有办法让每个等高线图的最低等高线级别透明,以便更容易看到每
在R中,有一个locator函数,类似于Matlab的ginput,您可以用鼠标单击图形并选择任何x,y坐标。此外,还有一个名为identify(x,y)的函数,如果您给它绘制了一组绘制的点x,y,然
我想用matplotlib生成矢量图。我尽力了-但输出是光栅图像。这是我使用的: import matplotlib matplotlib.use('Agg') import matplotlib.p
我正在尝试使用 matplotlib 制作具有非常小的灰点的散点图。由于点密度的原因,点需要很小。问题是 scatter() 函数的标记似乎既有线条又有填充。当标记很小时,只有线条可见,而看不到填充,
我不太明白为什么我无法在指定的限制内创建水平和垂直线。我想用这个框绑定(bind)数据。然而,双方似乎并没有遵守我的指示。为什么是这样? # CREATING A BOUNDING BOX # BOT
我是一名优秀的程序员,十分优秀!