gpt4 book ai didi

python - PyQt5 窗口中嵌入的 matplotlib 图形的 x 轴大小(以像素为单位)

转载 作者:行者123 更新时间:2023-12-01 07:16:54 24 4
gpt4 key购买 nike

我在 PyQt5 窗口中有一个实时 matplotlib 图表:

enter image description here

您可以在此处阅读有关我如何使此代码工作的更多信息:
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_()

 

1。问题

我需要知道从 x_minx_max 的像素数:

enter image description here

请注意,x 轴实际上超出了 x_minx_max 边界。我不需要知道总长度。只是从 x_minx_max 的长度。

 

2。到目前为止我尝试过的内容

我已经找到了一种获取图形边界框的方法。请注意 _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 像素的边界框。这是一个很好的起点,但我不知道如何从这里继续。

enter image description here

我还注意到这个边界框大小从第一次开始就没有正确打印出来。第一次运行 _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/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com