JavaFX WebView与cefpython显示ECharts图对比

本文对比了JavaFXWebView与cefpython在显示ECharts图表时的性能与效果,发现cefpython基于ChromiumEmbeddedFramework,显示效果更佳,支持WebGL,内存使用与加载速度优于JavaFX。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概况

JavaFX WebViewcefpython都提供了在桌面程序中显示网页的功能,但是cefpython显示效果更优。其中cefpython基于Chromium Embedded Framework (CEF)提供了更好的网页显示支持,并能嵌入到Qt / GTK / wxPython构建的窗体中。

作者从Echart实例网站(https://gallery.echartsjs.com/)上找了四个例子,分别用JavaFX WebView和cefpython去加载,得到的效果图见对比。两种库显示效果对比如下表:

示例显示对象JavaFX WebViewcefpython内存 MB (JavaFX WebView : cefpython : chrome)
案例1极坐标柱状图有些效果显示不了,分辨率低正常(同chrome对比)620 : 234 : 390
案例2城堡不支持WebGL正常909 : 715 : 635
案例3立体柱状图显示不全正常704 : 240 : 430
案例4出租车立体流动热图不显示正常665 : 1681 : 1510

从上表可以看出,JavaFX WebView不支持一些显示效果和WebGL,且在有显示时耗费的内除大约时cefpython的1.2~3倍。在所示案例中cefpython都能正常显示效果图,且加载速度远快于JavaFX WebView。需要指出cefpython后端采用c++实现与chrome内存消耗类似。

示例代码

JavaFX WebView

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.StackPane;

import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
//  w  w  w.j av a2s  . c  o m
public class Main extends Application {
    @Override
    public void start(final Stage stage) {
        stage.setWidth(400);
        stage.setHeight(500);
        Scene scene = new Scene(new Group());


        final WebView browser = new WebView();
        final WebEngine webEngine = browser.getEngine();

        ScrollPane scrollPane = new ScrollPane(); //ScrollPane scrollPane = new ScrollPane();
        scrollPane.setContent(browser);

        browser.prefHeightProperty().bind(stage.heightProperty());
        browser.prefWidthProperty().bind(stage.widthProperty());

        webEngine.getLoadWorker().stateProperty()
                .addListener(new ChangeListener<State>() {
                    @Override
                    public void changed(ObservableValue ov, State oldState, State newState) {

                        if (newState == Worker.State.SUCCEEDED) {
                            stage.setTitle(webEngine.getLocation());
                        }

                    }
                });
        webEngine.load("https://gallery.echartsjs.com/");

        scene.setRoot(scrollPane);

        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

cefpython


from cefpython3 import cefpython as cef
import ctypes
import os
import platform
import sys

# GLOBALS
PYQT4 = False
PYQT5 = False
PYSIDE = False
PYSIDE2 = True # False
sys.argv.append("pyside2")

if "pyqt4" in sys.argv:
    PYQT4 = True
    # noinspection PyUnresolvedReferences
    from PyQt4.QtGui import *
    # noinspection PyUnresolvedReferences
    from PyQt4.QtCore import *
elif "pyqt5" in sys.argv:
    PYQT5 = True
    # noinspection PyUnresolvedReferences
    from PyQt5.QtGui import *
    # noinspection PyUnresolvedReferences
    from PyQt5.QtCore import *
    # noinspection PyUnresolvedReferences
    from PyQt5.QtWidgets import *
elif "pyside" in sys.argv:
    PYSIDE = True
    # noinspection PyUnresolvedReferences
    import PySide
    # noinspection PyUnresolvedReferences
    from PySide import QtCore
    # noinspection PyUnresolvedReferences
    from PySide.QtGui import *
    # noinspection PyUnresolvedReferences
    from PySide.QtCore import *
elif "pyside2" in sys.argv:
    PYSIDE2 = True
    # noinspection PyUnresolvedReferences
    import PySide2
    # noinspection PyUnresolvedReferences
    from PySide2 import QtCore
    # noinspection PyUnresolvedReferences
    from PySide2.QtGui import *
    # noinspection PyUnresolvedReferences
    from PySide2.QtCore import *
    # noinspection PyUnresolvedReferences
    from PySide2.QtWidgets import *
else:
    print("USAGE:")
    print("  qt.py pyqt4")
    print("  qt.py pyqt5")
    print("  qt.py pyside")
    print("  qt.py pyside2")
    sys.exit(1)

# Fix for PyCharm hints warnings when using static methods
WindowUtils = cef.WindowUtils()

# Platforms
WINDOWS = (platform.system() == "Windows")
LINUX = (platform.system() == "Linux")
MAC = (platform.system() == "Darwin")

# Configuration
WIDTH = 800
HEIGHT = 600

# OS differences
CefWidgetParent = QWidget
if LINUX and (PYQT4 or PYSIDE):
    # noinspection PyUnresolvedReferences
    CefWidgetParent = QX11EmbedContainer


def main():
    check_versions()
    sys.excepthook = cef.ExceptHook  # To shutdown all CEF processes on error
    settings = {}
    if MAC:
        # Issue #442 requires enabling message pump on Mac
        # in Qt example. Calling cef.DoMessageLoopWork in a timer
        # doesn't work anymore.
        settings["external_message_pump"] = True

    cef.Initialize(settings)
    app = CefApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    main_window.activateWindow()
    main_window.raise_()
    app.exec_()
    if not cef.GetAppSetting("external_message_pump"):
        app.stopTimer()
    del main_window  # Just to be safe, similarly to "del app"
    del app  # Must destroy app object before calling Shutdown
    cef.Shutdown()


def check_versions():
    print("[qt.py] CEF Python {ver}".format(ver=cef.__version__))
    print("[qt.py] Python {ver} {arch}".format(
            ver=platform.python_version(), arch=platform.architecture()[0]))
    if PYQT4 or PYQT5:
        print("[qt.py] PyQt {v1} (qt {v2})".format(
              v1=PYQT_VERSION_STR, v2=qVersion()))
    elif PYSIDE:
        print("[qt.py] PySide {v1} (qt {v2})".format(
              v1=PySide.__version__, v2=QtCore.__version__))
    elif PYSIDE2:
        print("[qt.py] PySide2 {v1} (qt {v2})".format(
              v1=PySide2.__version__, v2=QtCore.__version__))
    # CEF Python version requirement
    assert cef.__version__ >= "55.4", "CEF Python v55.4+ required to run this"


class MainWindow(QMainWindow):
    def __init__(self):
        # noinspection PyArgumentList
        super(MainWindow, self).__init__(None)
        # Avoids crash when shutting down CEF (issue #360)
        if PYSIDE:
            self.setAttribute(Qt.WA_DeleteOnClose, True)
        self.cef_widget = None
        self.navigation_bar = None
        if PYQT4:
            self.setWindowTitle("PyQt4 example")
        elif PYQT5:
            self.setWindowTitle("PyQt5 example")
        elif PYSIDE:
            self.setWindowTitle("PySide example")
        elif PYSIDE2:
            self.setWindowTitle("PySide2 example")
        self.setFocusPolicy(Qt.StrongFocus)
        self.setupLayout()

    def setupLayout(self):
        self.resize(WIDTH, HEIGHT)
        self.cef_widget = CefWidget(self)
        self.navigation_bar = NavigationBar(self.cef_widget)
        layout = QGridLayout()
        # noinspection PyArgumentList
        layout.addWidget(self.navigation_bar, 0, 0)
        # noinspection PyArgumentList
        layout.addWidget(self.cef_widget, 1, 0)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.setRowStretch(0, 0)
        layout.setRowStretch(1, 1)
        # noinspection PyArgumentList
        frame = QFrame()
        frame.setLayout(layout)
        self.setCentralWidget(frame)

        if (PYSIDE2 or PYQT5) and WINDOWS:
            # On Windows with PyQt5 main window must be shown first
            # before CEF browser is embedded, otherwise window is
            # not resized and application hangs during resize.
            self.show()

        # Browser can be embedded only after layout was set up
        self.cef_widget.embedBrowser()

        if (PYSIDE2 or PYQT5) and LINUX:
            # On Linux with PyQt5 the QX11EmbedContainer widget is
            # no more available. An equivalent in Qt5 is to create
            # a hidden window, embed CEF browser in it and then
            # create a container for that hidden window and replace
            # cef widget in the layout with the container.
            # noinspection PyUnresolvedReferences, PyArgumentList
            self.container = QWidget.createWindowContainer(
                    self.cef_widget.hidden_window, parent=self)
            # noinspection PyArgumentList
            layout.addWidget(self.container, 1, 0)

    def closeEvent(self, event):
        # Close browser (force=True) and free CEF reference
        if self.cef_widget.browser:
            self.cef_widget.browser.CloseBrowser(True)
            self.clear_browser_references()

    def clear_browser_references(self):
        # Clear browser references that you keep anywhere in your
        # code. All references must be cleared for CEF to shutdown cleanly.
        self.cef_widget.browser = None


class CefWidget(CefWidgetParent):
    def __init__(self, parent=None):
        # noinspection PyArgumentList
        super(CefWidget, self).__init__(parent)
        self.parent = parent
        self.browser = None
        self.hidden_window = None  # Required for PyQt5 on Linux
        self.show()

    def focusInEvent(self, event):
        # This event seems to never get called on Linux, as CEF is
        # stealing all focus due to Issue #284.
        if cef.GetAppSetting("debug"):
            print("[qt.py] CefWidget.focusInEvent")
        if self.browser:
            if WINDOWS:
                WindowUtils.OnSetFocus(self.getHandle(), 0, 0, 0)
            self.browser.SetFocus(True)

    def focusOutEvent(self, event):
        # This event seems to never get called on Linux, as CEF is
        # stealing all focus due to Issue #284.
        if cef.GetAppSetting("debug"):
            print("[qt.py] CefWidget.focusOutEvent")
        if self.browser:
            self.browser.SetFocus(False)

    def embedBrowser(self):
        if (PYSIDE2 or PYQT5) and LINUX:
            # noinspection PyUnresolvedReferences
            self.hidden_window = QWindow()
        window_info = cef.WindowInfo()
        rect = [0, 0, self.width(), self.height()]
        window_info.SetAsChild(self.getHandle(), rect)
        self.browser = cef.CreateBrowserSync(window_info,
                                             url="https://gallery.echartsjs.com") #file:///F:/programming/python/TileMapBase/TileMapBase-master/tutorial/html/echart-test.html
                                            #  url="https://www.google.com/")
        self.browser.SetClientHandler(LoadHandler(self.parent.navigation_bar))
        self.browser.SetClientHandler(FocusHandler(self))

    def getHandle(self):
        if self.hidden_window:
            # PyQt5 on Linux
            return int(self.hidden_window.winId())
        try:
            # PyQt4 and PyQt5
            return int(self.winId())
        except:
            # PySide:
            # | QWidget.winId() returns <PyCObject object at 0x02FD8788>
            # | Converting it to int using ctypes.
            if sys.version_info[0] == 2:
                # Python 2
                ctypes.pythonapi.PyCObject_AsVoidPtr.restype = (
                        ctypes.c_void_p)
                ctypes.pythonapi.PyCObject_AsVoidPtr.argtypes = (
                        [ctypes.py_object])
                return ctypes.pythonapi.PyCObject_AsVoidPtr(self.winId())
            else:
                # Python 3
                ctypes.pythonapi.PyCapsule_GetPointer.restype = (
                        ctypes.c_void_p)
                ctypes.pythonapi.PyCapsule_GetPointer.argtypes = (
                        [ctypes.py_object])
                return ctypes.pythonapi.PyCapsule_GetPointer(
                        self.winId(), None)

    def moveEvent(self, _):
        self.x = 0
        self.y = 0
        if self.browser:
            if WINDOWS:
                WindowUtils.OnSize(self.getHandle(), 0, 0, 0)
            elif LINUX:
                self.browser.SetBounds(self.x, self.y,
                                       self.width(), self.height())
            self.browser.NotifyMoveOrResizeStarted()

    def resizeEvent(self, event):
        size = event.size()
        if self.browser:
            if WINDOWS:
                WindowUtils.OnSize(self.getHandle(), 0, 0, 0)
            elif LINUX:
                self.browser.SetBounds(self.x, self.y,
                                       size.width(), size.height())
            self.browser.NotifyMoveOrResizeStarted()


class CefApplication(QApplication):
    def __init__(self, args):
        super(CefApplication, self).__init__(args)
        if not cef.GetAppSetting("external_message_pump"):
            self.timer = self.createTimer()
        self.setupIcon()

    def createTimer(self):
        timer = QTimer()
        # noinspection PyUnresolvedReferences
        timer.timeout.connect(self.onTimer)
        timer.start(10)
        return timer

    def onTimer(self):
        cef.MessageLoopWork()

    def stopTimer(self):
        # Stop the timer after Qt's message loop has ended
        self.timer.stop()

    def setupIcon(self):
        icon_file = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                                 "resources", "{0}.png".format(sys.argv[1]))
        if os.path.exists(icon_file):
            self.setWindowIcon(QIcon(icon_file))


class LoadHandler(object):
    def __init__(self, navigation_bar):
        self.initial_app_loading = True
        self.navigation_bar = navigation_bar

    def OnLoadingStateChange(self, **_):
        self.navigation_bar.updateState()

    def OnLoadStart(self, browser, **_):
        self.navigation_bar.url.setText(browser.GetUrl())
        if self.initial_app_loading:
            self.navigation_bar.cef_widget.setFocus()
            # Temporary fix no. 2 for focus issue on Linux (Issue #284)
            if LINUX:
                print("[qt.py] LoadHandler.OnLoadStart:"
                      " keyboard focus fix no. 2 (Issue #284)")
                browser.SetFocus(True)
            self.initial_app_loading = False


class FocusHandler(object):
    def __init__(self, cef_widget):
        self.cef_widget = cef_widget

    def OnTakeFocus(self, **_):
        if cef.GetAppSetting("debug"):
            print("[qt.py] FocusHandler.OnTakeFocus")

    def OnSetFocus(self, **_):
        if cef.GetAppSetting("debug"):
            print("[qt.py] FocusHandler.OnSetFocus")

    def OnGotFocus(self, browser, **_):
        if cef.GetAppSetting("debug"):
            print("[qt.py] FocusHandler.OnGotFocus")
        self.cef_widget.setFocus()
        # Temporary fix no. 1 for focus issues on Linux (Issue #284)
        if LINUX:
            browser.SetFocus(True)


class NavigationBar(QFrame):
    def __init__(self, cef_widget):
        # noinspection PyArgumentList
        super(NavigationBar, self).__init__()
        self.cef_widget = cef_widget

        # Init layout
        layout = QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        # Back button
        self.back = self.createButton("back")
        # noinspection PyUnresolvedReferences
        self.back.clicked.connect(self.onBack)
        # noinspection PyArgumentList
        layout.addWidget(self.back, 0, 0)

        # Forward button
        self.forward = self.createButton("forward")
        # noinspection PyUnresolvedReferences
        self.forward.clicked.connect(self.onForward)
        # noinspection PyArgumentList
        layout.addWidget(self.forward, 0, 1)

        # Reload button
        self.reload = self.createButton("reload")
        # noinspection PyUnresolvedReferences
        self.reload.clicked.connect(self.onReload)
        # noinspection PyArgumentList
        layout.addWidget(self.reload, 0, 2)

        # Url input
        self.url = QLineEdit("")
        # noinspection PyUnresolvedReferences
        self.url.returnPressed.connect(self.onGoUrl)
        # noinspection PyArgumentList
        layout.addWidget(self.url, 0, 3)

        # Layout
        self.setLayout(layout)
        self.updateState()

    def onBack(self):
        if self.cef_widget.browser:
            self.cef_widget.browser.GoBack()

    def onForward(self):
        if self.cef_widget.browser:
            self.cef_widget.browser.GoForward()

    def onReload(self):
        if self.cef_widget.browser:
            self.cef_widget.browser.Reload()

    def onGoUrl(self):
        if self.cef_widget.browser:
            self.cef_widget.browser.LoadUrl(self.url.text())

    def updateState(self):
        browser = self.cef_widget.browser
        if not browser:
            self.back.setEnabled(False)
            self.forward.setEnabled(False)
            self.reload.setEnabled(False)
            self.url.setEnabled(False)
            return
        self.back.setEnabled(browser.CanGoBack())
        self.forward.setEnabled(browser.CanGoForward())
        self.reload.setEnabled(True)
        self.url.setEnabled(True)
        self.url.setText(browser.GetUrl())

    def createButton(self, name):
        resources = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                                 "resources")
        pixmap = QPixmap(os.path.join(resources, "{0}.png".format(name)))
        icon = QIcon(pixmap)
        button = QPushButton()
        button.setIcon(icon)
        button.setIconSize(pixmap.rect().size())
        return button

if __name__ == '__main__':
    main()

对比

案例1

JavaFX WebView 显示

显示不完整,且分辨率较差
JavaFX WebView显示图1
内存使用量620MB

cefpython 显示

在这里插入图片描述
内存使用量约234MB
内存使用量

案例2

JavaFX WebView 显示

JavaFX WebView 不支持WebGL

cefpython 显示

cefpython支持WebGL
内存使用量

案例3

JavaFX WebView 显示

JavaFX WebView立体柱状图显示不全,且分辨率低

cefpython 显示

cefpython显示正常
在这里插入图片描述

案例4

JavaFX WebView 显示

在这里插入图片描述
在这里插入图片描述

cefpython 显示

在这里插入图片描述
在这里插入图片描述

chrome 显示

在这里插入图片描述
在这里插入图片描述

此外JavaFX WebView的加载速度远慢于cefpython

### 使用Transformer模型进行图像分类的方法 #### 方法概述 为了使Transformer能够应用于图像分类任务,一种有效的方式是将图像分割成固定大小的小块(patches),这些小块被线性映射为向量,并加上位置编码以保留空间信息[^2]。 #### 数据预处理 在准备输入数据的过程中,原始图片会被切分成多个不重叠的patch。假设一张尺寸为\(H \times W\)的RGB图像是要处理的对象,则可以按照设定好的宽度和高度参数来划分该图像。例如,对于分辨率为\(224\times 224\)像素的图像,如果选择每边切成16个部分的话,那么最终会得到\((224/16)^2=196\)个小方格作为单独的特征表示单元。之后,每一个这样的补丁都会通过一个简单的全连接层转换成为维度固定的嵌入向量。 ```python import torch from torchvision import transforms def preprocess_image(image_path, patch_size=16): transform = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), # 假设目标分辨率是224x224 transforms.ToTensor(), ]) image = Image.open(image_path).convert('RGB') tensor = transform(image) patches = [] for i in range(tensor.shape[-2] // patch_size): # 高度方向上的循环 row_patches = [] for j in range(tensor.shape[-1] // patch_size): # 宽度方向上的循环 patch = tensor[:, :, i*patch_size:(i+1)*patch_size, j*patch_size:(j+1)*patch_size].flatten() row_patches.append(patch) patches.extend(row_patches) return torch.stack(patches) ``` #### 构建Transformer架构 构建Vision Transformer (ViT),通常包括以下几个组成部分: - **Patch Embedding Layer**: 将每个图像块转化为低维向量; - **Positional Encoding Layer**: 添加绝对或相对位置信息给上述获得的向量序列; - **Multiple Layers of Self-Attention and Feed Forward Networks**: 多层自注意机制与前馈神经网络交替堆叠而成的核心模块; 最后,在顶层附加一个全局平均池化层(Global Average Pooling)以及一个多类别Softmax回归器用于预测类标签。 ```python class VisionTransformer(nn.Module): def __init__(self, num_classes=1000, embed_dim=768, depth=12, num_heads=12, mlp_ratio=4., qkv_bias=False, drop_rate=0.): super().__init__() self.patch_embed = PatchEmbed(embed_dim=embed_dim) self.pos_embed = nn.Parameter(torch.zeros(1, self.patch_embed.num_patches + 1, embed_dim)) self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) dpr = [drop_rate for _ in range(depth)] self.blocks = nn.Sequential(*[ Block( dim=embed_dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, drop=dpr[i], ) for i in range(depth)]) self.norm = nn.LayerNorm(embed_dim) self.head = nn.Linear(embed_dim, num_classes) def forward(self, x): B = x.shape[0] cls_tokens = self.cls_token.expand(B, -1, -1) x = self.patch_embed(x) x = torch.cat((cls_tokens, x), dim=1) x += self.pos_embed x = self.blocks(x) x = self.norm(x) return self.head(x[:, 0]) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值