如何在PyQt小部件中呈现Altair / Vega

3

能否像Matplotlib支持多个后端一样,让Altair或Vega(-Lite)渲染到PyQt小部件中?我知道我可以使用Qt WebView小部件来呈现带有Vega-embed的网页,但我想避免即使在本地也必须提供此服务的开销。

3个回答

2

使用QWebEngineView是可视化Altair图表的最佳选择,因为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_())

0

目前Altair的唯一渲染后端是Vega-Embed,因此在PyQt小部件中呈现它需要运行一些Javascript引擎。我认为Qt WebView可能是最好的选择。

如果您可以接受失去图表的交互性,您也可以使用altair_saver作为后端来存储图表的静态PNG、SVG或PDF,并在QtWidget中显示它。


0

eyllanesc的绝佳回答的基础上进行改进,这是适用于PyQt6的版本:

from io import StringIO
from typing import Optional

from PyQt6 import QtCore
from PyQt6 import QtWidgets
from PyQt6.QtWebEngineCore import QWebEngineDownloadRequest
from PyQt6.QtWebEngineCore import QWebEnginePage
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import QWidget


class WebEngineView(QWebEngineView):
    def __init__(self, parent: Optional[QWidget] = None):
        super().__init__(parent)
        self.page().profile().downloadRequested.connect(self.onDownloadRequested)
        self.windows = []

    @QtCore.pyqtSlot(QWebEngineDownloadRequest)
    def onDownloadRequested(self, download: QWebEngineDownloadRequest) -> None:
        if (
            download.state()
            == QWebEngineDownloadRequest.DownloadState.DownloadRequested
        ):
            path, _ = QtWidgets.QFileDialog.getSaveFileName(
                self, self.tr("Save as"), download.downloadFileName()
            )
            if path:
                download.setDownloadFileName(path)
                download.accept()

    def createWindow(
        self, web_window_type: QWebEnginePage.WebWindowType
    ) -> Optional[QWebEngineView]:
        if web_window_type == QWebEnginePage.WebWindowType.WebBrowserTab:
            window = QtWidgets.QMainWindow(self)
            view = QWebEngineView(window)
            window.resize(640, 480)
            window.setCentralWidget(view)
            window.show()
            return view

    def set_chart(self, chart, **kwargs) -> None:
        output = StringIO()
        chart.save(output, "html", **kwargs)
        self.setHtml(output.getvalue())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接