将 html 中的本地文件用于 PyQt5 网络引擎
Using a local file in html for a PyQt5 webengine
我正在尝试将绘图嵌入到 PyQt5 网络引擎视图中。我能够使用以下方法做到这一点:
如果您阅读它,文章解释说您不能在使用 webengine 视图时直接将 javascript 包含在 HTML 中(加载超过 2 MB 的文件时会出现问题)。但是,我正在尝试使 javascript 的源代码是 plotly-min.js 的本地副本(保存在项目文件夹中),这样使用该程序的任何人都不需要互联网连接以查看它生成的图表。我尝试遵循 html 上的一些示例,但无济于事。
适用于互联网连接的原始代码是:
raw_html = '<html><head><meta charset="utf-8" />'
raw_html += '<script src="https://cdn.plot.ly/plotly-latest.min.js"></script></head>'
raw_html += '<body>'
raw_html += plot(fig, include_plotlyjs=False, output_type='div')
raw_html += '</body></html>'
temp_view = QWebEngineView()
temp_view.setHtml(graph)
如果我理解正确的话,我需要修改这部分:
<script src="https://cdn.plot.ly/plotly-latest.min.js">
我已经试过了:
<script type="text/javascript" src="file:///C:/Users/UserName/PycharmProjects/ProjectFolder/plotly-latest.min.js"></script>
老实说我不知道HTML。这是代码中唯一的HTML(其余在Python 3)。有谁知道为什么以上可能不起作用以及我需要更改什么?我怀疑我可能以某种方式 运行 进入上述问题引用的 2 MB 限制,因为我在网上找到的关于如何在 HTML 中引用本地文件的不同变体似乎没有改变任何东西。
出于安全原因,许多浏览器禁止加载本地文件,但如下文所述forum您可以通过以下方式启用该功能:
sys.argv.append("--disable-web-security")
假设 .js 在您的 .py 文件旁边:
.
├── main.py
└── plotly-latest.min.js
您可以使用以下示例:
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import QDir, QUrl
import plotly
import plotly.graph_objs as go
sys.argv.append("--disable-web-security")
app = QApplication(sys.argv)
x1 = [10, 3, 4, 5, 20, 4, 3]
trace1 = go.Box(x = x1)
layout = go.Layout(showlegend = True)
data = [trace1]
fig = go.Figure(data=data, layout = layout)
path = QDir.current().filePath('plotly-latest.min.js')
local = QUrl.fromLocalFile(path).toString()
raw_html = '<html><head><meta charset="utf-8" />'
raw_html += '<script src="{}"></script></head>'.format(local)
raw_html += '<body>'
raw_html += plotly.offline.plot(fig, include_plotlyjs=False, output_type='div')
raw_html += '</body></html>'
view = QWebEngineView()
view.setHtml(raw_html)
view.show()
sys.exit(app.exec_())
我有问题 运行 @eyllanesc 的示例在 Ubuntu 16.04 / 18.04 下使用 NVIDIA 驱动程序。
首先我必须将 PyQt5
5.10.1
降级到 5.10.0
以避免:
[0525/114545.216138:WARNING:stack_trace_posix.cc(648)] Failed to open file: /home/tmp/.gl5Qb0Tf (deleted)
Error: Datei oder Verzeichnis nicht gefunden
Could not find QtWebEngineProcess
[3032:3032:0525/114545.495351:FATAL:zygote_host_impl_linux.cc(182)] Check failed: ReceiveFixedMessage(fds[0], kZygoteBootMessage, sizeof(kZygoteBootMessage), &boot_pid).
...
但这应该在 PyQt5 5.11
中得到解决
第二个问题是着色器的链接(ubuntu 16.04
):
QOpenGLShaderProgram::uniformLocation(qt_Matrix): shader program is not linked
QOpenGLShaderProgram: could not create shader program
QOpenGLShader: could not create shader
QOpenGLShader: could not create shader
shader compilation failed:
在 VirtualBox
中的 ubuntu 18.04
下:
Received signal 11 SEGV_MAPERR 000000000000
#0 0x7faa83f029a5 <unknown>
#1 0x7faa82c42501 <unknown>
#2 0x7faa83f02d3d <unknown>
#3 0x7faa9228a890 <unknown>
r8: 0000000000000001 r9: 0000000002d5d3d0 r10: 0000000000000002 r11: 00007faa8ec4a000
r12: 0000000002d386b0 r13: 0000000002d59b50 r14: 0000000000000000 r15: 00007faa8ed3b280
di: 0000000000001f01 si: 00007faa8ed3d210 bp: 0000000002d52e40 bx: 0000000000000001
dx: 0000000002e52220 ax: 0000000002e52220 cx: 0000000000000000 sp: 00007ffce38c2b78
ip: 0000000000000000 efl: 0000000000010202 cgf: 002b000000000033 erf: 0000000000000014
trp: 000000000000000e msk: 0000000000000000 cr2: 0000000000000000
[end of stack trace]
Calling _exit(1). Core file will not be generated.
可以按照 dcortesi 修复一个 4 年前的 Ubuntu bug 的建议来解决:
if sys.platform.startswith( 'linux' ) :
from OpenGL import GL
综合起来:
requirements.txt
PyQt5 == 5.10.0
PyOpenGL
plotly
webview.py
import sys
if sys.platform.startswith( 'linux' ) :
from OpenGL import GL
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import QDir, QUrl
import plotly
import plotly.graph_objs as go
sys.argv.append("--disable-web-security")
app = QApplication(sys.argv)
x1 = [10, 3, 4, 5, 20, 4, 3]
trace1 = go.Box(x = x1)
layout = go.Layout(showlegend = True)
data = [trace1]
fig = go.Figure(data=data, layout = layout)
path = QDir.current().filePath('plotly-latest.min.js')
local = QUrl.fromLocalFile(path).toString()
raw_html = '<html><head><meta charset="utf-8" />'
raw_html += '<script src="{}"></script></head>'.format(local)
raw_html += '<body>'
raw_html += plotly.offline.plot(fig, include_plotlyjs=False, output_type='div')
raw_html += '</body></html>'
view = QWebEngineView()
view.setHtml(raw_html)
view.show()
sys.exit(app.exec_())
可以下载最新的 plotly.js here。我在这个例子中使用了 plotly.js v1.38.0
。
为了防止对任何人有帮助,我采用了不同的解决方案。我决定不依赖 QWebEngineView.setHTML
方法,而是使用一个命名的临时文件将 html 写入磁盘,然后将其加载到 QUrl
以加载到 QWebEngineView
使用 QUrl.fromLocalFile
和 QWebEngineView.setUrl
方法。
总而言之,我将其应用到自定义 PlotlyWidget
class 中,所以这是完整的内容,以防对任何人有帮助。
必须特别小心以确保在 PlotlyWiget
超出范围并被垃圾收集(即 __del__
覆盖)时删除临时文件。
import os
import tempfile
import plotly
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import QUrl
class PlotlyWidget(QWebEngineView):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.plotly_fig = None
self.html_file = None
def load_fig(self, plotly_fig):
self.plotly_fig = plotly_fig
self.plotly_fig.update_layout(font={'size':18})
self.update_fig()
def update_fig(self):
html = '<html><body>'
html += plotly.offline.plot(self.plotly_fig, output_type='div', include_plotlyjs=True)
html += '</body></html>'
if self.html_file is not None: # close previous tempfiles so that they delete from disk properly
self.html_file.close()
os.unlink(self.html_file.name)
self.html_file = tempfile.NamedTemporaryFile(suffix='.html', delete=False)
with open(self.html_file.name, 'w') as file:
file.write(html)
url = QUrl.fromLocalFile(self.html_file.name)
self.setUrl(url)
def closeEvent(self, event):
"""Delete the temp file on close event."""
super().closeEvent(event)
if self.html_file is not None:
self.html_file.close()
os.unlink(self.html_file.name)
self.html_file = None
def __del__(self):
"""Handle temp file close (and deletion) if QWebEngineView is garbage collected instead of closed."""
if self.html_file is not None:
self.html_file.close()
os.unlink(self.html_file.name)
典型的用法是生成一个绘图图形,然后将其发送到 load_fig
方法,如:
# from within QMainWindow or other QWidget instance
fig = go.Figure()
fig.add_scatter(y=ydata, x=xdata)
self.ploty_widget = PlotlyWidget()
self.ploty_widget.load_fig(fig)
self.ploty_widget.show()
我正在尝试将绘图嵌入到 PyQt5 网络引擎视图中。我能够使用以下方法做到这一点:
如果您阅读它,文章解释说您不能在使用 webengine 视图时直接将 javascript 包含在 HTML 中(加载超过 2 MB 的文件时会出现问题)。但是,我正在尝试使 javascript 的源代码是 plotly-min.js 的本地副本(保存在项目文件夹中),这样使用该程序的任何人都不需要互联网连接以查看它生成的图表。我尝试遵循 html 上的一些示例,但无济于事。
适用于互联网连接的原始代码是:
raw_html = '<html><head><meta charset="utf-8" />'
raw_html += '<script src="https://cdn.plot.ly/plotly-latest.min.js"></script></head>'
raw_html += '<body>'
raw_html += plot(fig, include_plotlyjs=False, output_type='div')
raw_html += '</body></html>'
temp_view = QWebEngineView()
temp_view.setHtml(graph)
如果我理解正确的话,我需要修改这部分:
<script src="https://cdn.plot.ly/plotly-latest.min.js">
我已经试过了:
<script type="text/javascript" src="file:///C:/Users/UserName/PycharmProjects/ProjectFolder/plotly-latest.min.js"></script>
老实说我不知道HTML。这是代码中唯一的HTML(其余在Python 3)。有谁知道为什么以上可能不起作用以及我需要更改什么?我怀疑我可能以某种方式 运行 进入上述问题引用的 2 MB 限制,因为我在网上找到的关于如何在 HTML 中引用本地文件的不同变体似乎没有改变任何东西。
出于安全原因,许多浏览器禁止加载本地文件,但如下文所述forum您可以通过以下方式启用该功能:
sys.argv.append("--disable-web-security")
假设 .js 在您的 .py 文件旁边:
.
├── main.py
└── plotly-latest.min.js
您可以使用以下示例:
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import QDir, QUrl
import plotly
import plotly.graph_objs as go
sys.argv.append("--disable-web-security")
app = QApplication(sys.argv)
x1 = [10, 3, 4, 5, 20, 4, 3]
trace1 = go.Box(x = x1)
layout = go.Layout(showlegend = True)
data = [trace1]
fig = go.Figure(data=data, layout = layout)
path = QDir.current().filePath('plotly-latest.min.js')
local = QUrl.fromLocalFile(path).toString()
raw_html = '<html><head><meta charset="utf-8" />'
raw_html += '<script src="{}"></script></head>'.format(local)
raw_html += '<body>'
raw_html += plotly.offline.plot(fig, include_plotlyjs=False, output_type='div')
raw_html += '</body></html>'
view = QWebEngineView()
view.setHtml(raw_html)
view.show()
sys.exit(app.exec_())
我有问题 运行 @eyllanesc 的示例在 Ubuntu 16.04 / 18.04 下使用 NVIDIA 驱动程序。
首先我必须将 PyQt5
5.10.1
降级到 5.10.0
以避免:
[0525/114545.216138:WARNING:stack_trace_posix.cc(648)] Failed to open file: /home/tmp/.gl5Qb0Tf (deleted)
Error: Datei oder Verzeichnis nicht gefunden
Could not find QtWebEngineProcess
[3032:3032:0525/114545.495351:FATAL:zygote_host_impl_linux.cc(182)] Check failed: ReceiveFixedMessage(fds[0], kZygoteBootMessage, sizeof(kZygoteBootMessage), &boot_pid).
...
但这应该在 PyQt5 5.11
第二个问题是着色器的链接(ubuntu 16.04
):
QOpenGLShaderProgram::uniformLocation(qt_Matrix): shader program is not linked
QOpenGLShaderProgram: could not create shader program
QOpenGLShader: could not create shader
QOpenGLShader: could not create shader
shader compilation failed:
在 VirtualBox
中的 ubuntu 18.04
下:
Received signal 11 SEGV_MAPERR 000000000000
#0 0x7faa83f029a5 <unknown>
#1 0x7faa82c42501 <unknown>
#2 0x7faa83f02d3d <unknown>
#3 0x7faa9228a890 <unknown>
r8: 0000000000000001 r9: 0000000002d5d3d0 r10: 0000000000000002 r11: 00007faa8ec4a000
r12: 0000000002d386b0 r13: 0000000002d59b50 r14: 0000000000000000 r15: 00007faa8ed3b280
di: 0000000000001f01 si: 00007faa8ed3d210 bp: 0000000002d52e40 bx: 0000000000000001
dx: 0000000002e52220 ax: 0000000002e52220 cx: 0000000000000000 sp: 00007ffce38c2b78
ip: 0000000000000000 efl: 0000000000010202 cgf: 002b000000000033 erf: 0000000000000014
trp: 000000000000000e msk: 0000000000000000 cr2: 0000000000000000
[end of stack trace]
Calling _exit(1). Core file will not be generated.
可以按照 dcortesi 修复一个 4 年前的 Ubuntu bug 的建议来解决:
if sys.platform.startswith( 'linux' ) :
from OpenGL import GL
综合起来:
requirements.txt
PyQt5 == 5.10.0
PyOpenGL
plotly
webview.py
import sys
if sys.platform.startswith( 'linux' ) :
from OpenGL import GL
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import QDir, QUrl
import plotly
import plotly.graph_objs as go
sys.argv.append("--disable-web-security")
app = QApplication(sys.argv)
x1 = [10, 3, 4, 5, 20, 4, 3]
trace1 = go.Box(x = x1)
layout = go.Layout(showlegend = True)
data = [trace1]
fig = go.Figure(data=data, layout = layout)
path = QDir.current().filePath('plotly-latest.min.js')
local = QUrl.fromLocalFile(path).toString()
raw_html = '<html><head><meta charset="utf-8" />'
raw_html += '<script src="{}"></script></head>'.format(local)
raw_html += '<body>'
raw_html += plotly.offline.plot(fig, include_plotlyjs=False, output_type='div')
raw_html += '</body></html>'
view = QWebEngineView()
view.setHtml(raw_html)
view.show()
sys.exit(app.exec_())
可以下载最新的 plotly.js here。我在这个例子中使用了 plotly.js v1.38.0
。
为了防止对任何人有帮助,我采用了不同的解决方案。我决定不依赖 QWebEngineView.setHTML
方法,而是使用一个命名的临时文件将 html 写入磁盘,然后将其加载到 QUrl
以加载到 QWebEngineView
使用 QUrl.fromLocalFile
和 QWebEngineView.setUrl
方法。
总而言之,我将其应用到自定义 PlotlyWidget
class 中,所以这是完整的内容,以防对任何人有帮助。
必须特别小心以确保在 PlotlyWiget
超出范围并被垃圾收集(即 __del__
覆盖)时删除临时文件。
import os
import tempfile
import plotly
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import QUrl
class PlotlyWidget(QWebEngineView):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.plotly_fig = None
self.html_file = None
def load_fig(self, plotly_fig):
self.plotly_fig = plotly_fig
self.plotly_fig.update_layout(font={'size':18})
self.update_fig()
def update_fig(self):
html = '<html><body>'
html += plotly.offline.plot(self.plotly_fig, output_type='div', include_plotlyjs=True)
html += '</body></html>'
if self.html_file is not None: # close previous tempfiles so that they delete from disk properly
self.html_file.close()
os.unlink(self.html_file.name)
self.html_file = tempfile.NamedTemporaryFile(suffix='.html', delete=False)
with open(self.html_file.name, 'w') as file:
file.write(html)
url = QUrl.fromLocalFile(self.html_file.name)
self.setUrl(url)
def closeEvent(self, event):
"""Delete the temp file on close event."""
super().closeEvent(event)
if self.html_file is not None:
self.html_file.close()
os.unlink(self.html_file.name)
self.html_file = None
def __del__(self):
"""Handle temp file close (and deletion) if QWebEngineView is garbage collected instead of closed."""
if self.html_file is not None:
self.html_file.close()
os.unlink(self.html_file.name)
典型的用法是生成一个绘图图形,然后将其发送到 load_fig
方法,如:
# from within QMainWindow or other QWidget instance
fig = go.Figure()
fig.add_scatter(y=ydata, x=xdata)
self.ploty_widget = PlotlyWidget()
self.ploty_widget.load_fig(fig)
self.ploty_widget.show()