如何在 PyQt 小部件中渲染 Altair/Vega
How to render Altair / Vega in a PyQt widget
是否可以将 Altair 或 Vega(-Lite) 渲染到 PyQt 小部件,类似于支持多个后端的 Matplotlib?我知道我可以使用 Qt WebView 小部件来呈现带有 Vega 嵌入的网页,但我想防止必须提供此服务的开销,即使是在本地也是如此。
目前 Altair 唯一的渲染后端是 Vega-Embed,因此在 PyQt 小部件中渲染它需要 运行 一些 Javascript 引擎。我怀疑 Qt WebView 可能是最好的选择。
如果您不介意失去图表的交互性,您还可以使用 altair_saver 作为后端来存储图表的静态 PNG、SVG 或 PDF 并在 QtWidget 中显示它。
使用 Altair 可视化绘图的最佳选择是使用 QWebEngineView,因为 altair 会根据您设置的指令创建 javascript 代码。恕我直言,最好的解决方案是获取图表的 html 并将其设置在 QWebEngineView 中。在下面的示例中,除了启用将图像保存为 svg 或 png 等特性外,我还展示了如何执行上述操作
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
from io import StringIO
class WebEngineView(QtWebEngineWidgets.QWebEngineView):
def __init__(self, parent=None):
super().__init__(parent)
self.page().profile().downloadRequested.connect(self.onDownloadRequested)
self.windows = []
@QtCore.pyqtSlot(QtWebEngineWidgets.QWebEngineDownloadItem)
def onDownloadRequested(self, download):
if (
download.state()
== QtWebEngineWidgets.QWebEngineDownloadItem.DownloadRequested
):
path, _ = QtWidgets.QFileDialog.getSaveFileName(
self, self.tr("Save as"), download.path()
)
if path:
download.setPath(path)
download.accept()
def createWindow(self, type_):
if type_ == QtWebEngineWidgets.QWebEnginePage.WebBrowserTab:
window = QtWidgets.QMainWindow(self)
view = QtWebEngineWidgets.QWebEngineView(window)
window.resize(640, 480)
window.setCentralWidget(view)
window.show()
return view
def updateChart(self, chart, **kwargs):
output = StringIO()
chart.save(output, "html", **kwargs)
self.setHtml(output.getvalue())
if __name__ == "__main__":
import sys
import altair as alt
from vega_datasets import data
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QMainWindow()
cars = data.cars()
chart = (
alt.Chart(cars)
.mark_bar()
.encode(x=alt.X("Miles_per_Gallon", bin=True), y="count()",)
.properties(title="A bar chart")
.configure_title(anchor="start")
)
view = WebEngineView()
view.updateChart(chart)
w.setCentralWidget(view)
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
是否可以将 Altair 或 Vega(-Lite) 渲染到 PyQt 小部件,类似于支持多个后端的 Matplotlib?我知道我可以使用 Qt WebView 小部件来呈现带有 Vega 嵌入的网页,但我想防止必须提供此服务的开销,即使是在本地也是如此。
目前 Altair 唯一的渲染后端是 Vega-Embed,因此在 PyQt 小部件中渲染它需要 运行 一些 Javascript 引擎。我怀疑 Qt WebView 可能是最好的选择。
如果您不介意失去图表的交互性,您还可以使用 altair_saver 作为后端来存储图表的静态 PNG、SVG 或 PDF 并在 QtWidget 中显示它。
使用 Altair 可视化绘图的最佳选择是使用 QWebEngineView,因为 altair 会根据您设置的指令创建 javascript 代码。恕我直言,最好的解决方案是获取图表的 html 并将其设置在 QWebEngineView 中。在下面的示例中,除了启用将图像保存为 svg 或 png 等特性外,我还展示了如何执行上述操作
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
from io import StringIO
class WebEngineView(QtWebEngineWidgets.QWebEngineView):
def __init__(self, parent=None):
super().__init__(parent)
self.page().profile().downloadRequested.connect(self.onDownloadRequested)
self.windows = []
@QtCore.pyqtSlot(QtWebEngineWidgets.QWebEngineDownloadItem)
def onDownloadRequested(self, download):
if (
download.state()
== QtWebEngineWidgets.QWebEngineDownloadItem.DownloadRequested
):
path, _ = QtWidgets.QFileDialog.getSaveFileName(
self, self.tr("Save as"), download.path()
)
if path:
download.setPath(path)
download.accept()
def createWindow(self, type_):
if type_ == QtWebEngineWidgets.QWebEnginePage.WebBrowserTab:
window = QtWidgets.QMainWindow(self)
view = QtWebEngineWidgets.QWebEngineView(window)
window.resize(640, 480)
window.setCentralWidget(view)
window.show()
return view
def updateChart(self, chart, **kwargs):
output = StringIO()
chart.save(output, "html", **kwargs)
self.setHtml(output.getvalue())
if __name__ == "__main__":
import sys
import altair as alt
from vega_datasets import data
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QMainWindow()
cars = data.cars()
chart = (
alt.Chart(cars)
.mark_bar()
.encode(x=alt.X("Miles_per_Gallon", bin=True), y="count()",)
.properties(title="A bar chart")
.configure_title(anchor="start")
)
view = WebEngineView()
view.updateChart(chart)
w.setCentralWidget(view)
w.resize(640, 480)
w.show()
sys.exit(app.exec_())