Issue
I made the PlotlyViewer
class shown below to display a Plotly graph, and it works correctly but shows this warning when I close it: Release of profile requested but WebEnginePage still not deleted. Expect troubles !
The warning started happening when I created my own QWebEngineProfile
instance in __init__
so I could connect to the downloadRequested
signal for showing a save file dialog.
I made my PlotlyViewer
a parent of the QWebEnginePage
but it sounds like it's not getting cleaned up when the parent is closed? I can't understand why.
import os
import tempfile
from plotly.io import to_html
import plotly.graph_objs as go
import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets, sip, QtWebEngineWidgets
from PyQt5.QtCore import Qt
class PlotlyViewer(QtWebEngineWidgets.QWebEngineView):
def __init__(self, fig=None):
super().__init__()
# https://stackoverflow.com/a/48142651/3620725
self.profile = QtWebEngineWidgets.QWebEngineProfile(self)
self.page = QtWebEngineWidgets.QWebEnginePage(self.profile, self)
self.setPage(self.page)
self.profile.downloadRequested.connect(self.on_downloadRequested)
# https://stackoverflow.com/a/8577226/3620725
self.temp_file = tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False)
self.set_figure(fig)
self.resize(700, 600)
self.setWindowTitle("Plotly Viewer")
def set_figure(self, fig=None):
self.temp_file.seek(0)
if fig:
self.temp_file.write(to_html(fig, config={"responsive": True}))
else:
self.temp_file.write("")
self.temp_file.truncate()
self.temp_file.seek(0)
self.load(QtCore.QUrl.fromLocalFile(self.temp_file.name))
def closeEvent(self, event: QtGui.QCloseEvent) -> None:
self.temp_file.close()
os.unlink(self.temp_file.name)
def sizeHint(self) -> QtCore.QSize:
return QtCore.QSize(400, 400)
# https://stackoverflow.com/questions/55963931/how-to-download-csv-file-with-qwebengineview-and-qurl
def on_downloadRequested(self, download):
dialog = QtWidgets.QFileDialog()
dialog.setDefaultSuffix(".png")
path, _ = dialog.getSaveFileName(self, "Save File", os.path.join(os.getcwd(), "newplot.png"), "*.png")
if path:
download.setPath(path)
download.accept()
if __name__ == "__main__":
app = QtWidgets.QApplication([])
fig = go.Figure()
fig.add_scatter(
x=np.random.rand(100),
y=np.random.rand(100),
mode="markers",
marker={
"size": 30,
"color": np.random.rand(100),
"opacity": 0.6,
"colorscale": "Viridis",
},
)
pv = PlotlyViewer(fig)
pv.show()
app.exec_()
Solution
The problem is that python's way of working with memory does not comply with the pre-established rules by Qt (that's the bindings problems), that is, Qt wants QWebEnginePage to be removed first but python removes QWebEngineProfile first.
In your case, I don't see the need to create a QWebEnginePage or a QWebEngineProfile different from the one that comes by default, but to obtain the QWebEngineProfile by default:
class PlotlyViewer(QtWebEngineWidgets.QWebEngineView):
def __init__(self, fig=None):
super().__init__()
self.page().profile().downloadRequested.connect(self.on_downloadRequested)
# https://stackoverflow.com/a/8577226/3620725
self.temp_file = tempfile.NamedTemporaryFile(
mode="w", suffix=".html", delete=False
)
self.set_figure(fig)
self.resize(700, 600)
self.setWindowTitle("Plotly Viewer")
Answered By - eyllanesc
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.