webview.cpp

本文介绍了一个自定义的WebView类实现,包括页面加载进度跟踪、图标加载、上下文菜单定制等功能,并实现了不同类型的浏览器窗口创建。
#include "browser.h"
#include "browserwindow.h"
#include "tabwidget.h"
#include "webpage.h"
#include "webpopupwindow.h"
#include "webview.h"
#include <QContextMenuEvent>
#include <QDebug>
#include <QMenu>
#include <QMessageBox>
#include <QNetworkReply>
#include <QTimer>

WebView::WebView(QWidget *parent)
    : QWebEngineView(parent)
    , m_loadProgress(0)
{
    connect(this, &QWebEngineView::loadProgress, [this](int progress) {
        m_loadProgress = progress;
    });
    connect(this, &QWebEngineView::loadFinished, [this](bool success) {
        if (!success) {
            qWarning() << "Could not load page: " << url();
            m_loadProgress = 0;
        }
    });
    connect(this, &QWebEngineView::iconUrlChanged, this, &WebView::handleIconUrlChanged);
    connect(this, &QWebEngineView::renderProcessTerminated,
            [this](QWebEnginePage::RenderProcessTerminationStatus termStatus, int statusCode) {
        QString status;
        switch (termStatus) {
        case QWebEnginePage::NormalTerminationStatus:
            status = tr("Render process normal exit");
            break;
        case QWebEnginePage::AbnormalTerminationStatus:
            status = tr("Render process abnormal exit");
            break;
        case QWebEnginePage::CrashedTerminationStatus:
            status = tr("Render process crashed");
            break;
        case QWebEnginePage::KilledTerminationStatus:
            status = tr("Render process killed");
            break;
        }
        QMessageBox::critical(window(), status, tr("Render process exited with code: %1").arg(statusCode));
        QTimer::singleShot(0, [this] { reload(); });
    });
}

void WebView::setPage(WebPage *page)
{
    createWebActionTrigger(page,QWebEnginePage::Forward);
    createWebActionTrigger(page,QWebEnginePage::Back);
    createWebActionTrigger(page,QWebEnginePage::Reload);
    createWebActionTrigger(page,QWebEnginePage::Stop);
    QWebEngineView::setPage(page);
}

QIcon WebView::icon() const
{
    if (!m_icon.isNull())
        return m_icon;
    return QIcon(QLatin1String(":defaulticon.png"));
}

int WebView::loadProgress() const
{
    return m_loadProgress;
}

void WebView::createWebActionTrigger(QWebEnginePage *page, QWebEnginePage::WebAction webAction)
{
    QAction *action = page->action(webAction);
    connect(action, &QAction::changed, [this, action, webAction]{
        emit webActionEnabledChanged(webAction, action->isEnabled());
    });
}

bool WebView::isWebActionEnabled(QWebEnginePage::WebAction webAction) const
{
    return page()->action(webAction)->isEnabled();
}

QNetworkAccessManager &WebView::networkAccessManager()
{
    static QNetworkAccessManager networkAccessManager;
    return networkAccessManager;
}

QWebEngineView *WebView::createWindow(QWebEnginePage::WebWindowType type)
{
    switch (type) {
    case QWebEnginePage::WebBrowserTab: {
        BrowserWindow *mainWindow = qobject_cast<BrowserWindow*>(window());
        return mainWindow->tabWidget()->createTab();
    }
    case QWebEnginePage::WebBrowserWindow: {
        BrowserWindow *mainWindow = new BrowserWindow();
        Browser::instance().addWindow(mainWindow);
        return mainWindow->currentTab();
    }
    case QWebEnginePage::WebDialog: {
        WebPopupWindow *popup = new WebPopupWindow(page()->profile());
        return popup->view();
    }
    }
    return nullptr;
}

void WebView::contextMenuEvent(QContextMenuEvent *event)
{
    QMenu *menu = page()->createStandardContextMenu();
    const QList<QAction*> actions = menu->actions();
    auto it = std::find(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::OpenLinkInThisWindow));
    if (it != actions.cend()) {
        (*it)->setText(tr("Open Link in This Tab"));
        ++it;
        QAction *before(it == actions.cend() ? nullptr : *it);
        menu->insertAction(before, page()->action(QWebEnginePage::OpenLinkInNewWindow));
        menu->insertAction(before, page()->action(QWebEnginePage::OpenLinkInNewTab));
    }
    connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater);
    menu->popup(event->globalPos());
}

void WebView::handleIconUrlChanged(const QUrl &url)
{
    QNetworkRequest iconRequest(url);
#ifndef QT_NO_OPENSSL
    QSslConfiguration conf = iconRequest.sslConfiguration();
    conf.setPeerVerifyMode(QSslSocket::VerifyNone);
    iconRequest.setSslConfiguration(conf);
#endif
    QNetworkReply *iconReply = networkAccessManager().get(iconRequest);
    iconReply->setParent(this);
    connect(iconReply, &QNetworkReply::finished, this, &WebView::handleIconLoaded);
}

void WebView::handleIconLoaded()
{
    QNetworkReply *iconReply = qobject_cast<QNetworkReply*>(sender());
    if (iconReply && iconReply->error() == QNetworkReply::NoError) {
        QByteArray data = iconReply->readAll();
        QPixmap pixmap;
        pixmap.loadFromData(data);
        m_icon.addPixmap(pixmap);
        iconReply->deleteLater();
    } else {
        m_icon = QIcon(QStringLiteral(":defaulticon.png"));
    }
    emit iconChanged(m_icon);
}
请问采用 webkit进行vscode (WSL ubuntu C++ imgui)进行在imgui控件上加载网页(html)如下为main.cpp #include "web_view.h" #include <stdlib.h> #include <GLFW/glfw3.h> #include <epoxy/gl.h> #include "imgui_impl_opengl3.h" // OpenGL3渲染器后端 #include "imgui_impl_glfw.h" // GLFW平台后端 // 定义 ImGui 使用 epoxy 作为 OpenGL 加载器 #define IMGUI_IMPL_OPENGL_LOADER_EPOXY int main() { // 设置环境变量 setenv("GDK_BACKEND", "x11", 1); setenv("WEBKIT_DISABLE_COMPOSITING_MODE", "1", 1); // 初始化GLFW if(!glfwInit()) { fprintf(stderr, "Failed to initialize GLFW\n"); return -1; } // 设置 OpenGL 版本和配置文件 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); GLFWwindow* window = glfwCreateWindow(1280, 720, "Map Viewer", NULL, NULL); if (!window) { fprintf(stderr, "Failed to create GLFW window\n"); glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSwapInterval(1); // 启用垂直同步 // 初始化ImGui IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; // 检查 ImGui OpenGL3 后端是否初始化成功 if (!ImGui_ImplGlfw_InitForOpenGL(window, true)) { fprintf(stderr, "Failed to initialize ImGui GLFW backend\n"); return -1; } if (!ImGui_ImplOpenGL3_Init("#version 130")) { fprintf(stderr, "Failed to initialize ImGui OpenGL3 backend\n"); return -1; } // 设置中文字体 io.Fonts->AddFontFromFileTTF("./Newfonts/msyh.ttc", 16.0f, nullptr, io.Fonts->GetGlyphRangesChineseFull()); // 创建Web视图 WebView mapView(1200, 700); //mapView.load_url("https://www.baidu.com"); mapView.load_local_file("resources/map.html");//("./resources/map.html"); while (!glfwWindowShouldClose(window)) { glfwPollEvents(); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); // 创建地图窗口 ImGui::Begin("Baidu Map"); mapView.render(ImGui::GetContentRegionAvail()); ImGui::End(); // 渲染 ImGui::Render(); int display_w, display_h; glfwGetFramebufferSize(window, &display_w, &display_h); glViewport(0, 0, display_w, display_h); glClearColor(0.45f, 0.55f, 0.60f, 1.00f); glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); glfwSwapBuffers(window); } // 清理资源 ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); glfwDestroyWindow(window); glfwTerminate(); return 0; } 以下为webview.cpp #include "web_view.h" #include <iostream> #include <epoxy/gl.h> #include <filesystem> // C++17 或更高版本 #include <webkit2/webkit2.h> WebView::WebView(int width, int height) :load_failed(false),texture(0),window(nullptr), webview0(nullptr) { // // 初始化GTK环境 gtk_init(nullptr, nullptr); // 创建WebKit组件 webview0 = webkit_web_view_new(); window = gtk_offscreen_window_new(); gtk_container_add(GTK_CONTAINER(window), webview0); // 设置初始尺寸 gtk_window_resize(GTK_WINDOW(window), width, height); // 显示窗口 - 这是关键步骤 gtk_widget_show_all(window); // 等待窗口映射完成 while (!gtk_widget_get_mapped(window)) { gtk_main_iteration(); } // 确保窗口已映射 gtk_widget_realize(window); // 启用JavaScript和硬件加速 WebKitSettings* settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview0)); webkit_settings_set_enable_javascript(settings, TRUE); webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_ALWAYS); // 创建OpenGL纹理 glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 连接加载失败信号 g_signal_connect(webview0, "load-failed", G_CALLBACK(on_load_failed), this); ImGui::StyleColorsLight(); // 允许加载本地资源 WebKitWebContext* context = webkit_web_view_get_context(WEBKIT_WEB_VIEW(webview0)); webkit_web_context_set_web_extensions_directory(context, ""); // 设置跨域策略 WebKitSecurityManager* security = webkit_web_context_get_security_manager(context); webkit_security_manager_register_uri_scheme_as_local(security, "file"); } WebView::~WebView() { // 清理资源 if (window) { gtk_widget_destroy(window); } if (texture) { glDeleteTextures(1, &texture); } } void WebView::load_url(const char* url) { // 异步加载URL webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview0), url); // 启用JavaScript(百度地图需要) WebKitSettings* settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview0)); webkit_settings_set_enable_javascript(settings, TRUE); } void WebView::render(const ImVec2& size) { // 验证尺寸,确保大于0 float render_width = std::max(1.0f, size.x); float render_height = std::max(1.0f, size.y); // 更新窗口尺寸 gtk_window_resize(GTK_WINDOW(window), render_width, render_height); // 确保窗口已显示 if (!gtk_widget_get_visible(window)) { gtk_widget_show_all(window); } // 强制GTK重绘和处理事件 gtk_widget_queue_draw(window); for (int i = 0; i < 10; i++) { // 多次处理事件以确保加载 while (gtk_events_pending()) { gtk_main_iteration(); } } // 获取像素数据 GdkWindow* gdk_window = gtk_widget_get_window(window); if (gdk_window) { pixbuf = gdk_pixbuf_get_from_window(gdk_window, 0, 0, size.x, size.y); if (pixbuf) { // 检查Pixbuf格式(应为RGBA) int channels = gdk_pixbuf_get_n_channels(pixbuf); if (channels != 4) { std::cerr << "Warning: Pixbuf has " << channels << " channels, expected 4 (RGBA)" << std::endl; } // 更新OpenGL纹理 glBindTexture(GL_TEXTURE_2D, texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), 0, GL_RGBA, GL_UNSIGNED_BYTE, gdk_pixbuf_get_pixels(pixbuf)); // 在ImGui中显示纹理 ImGui::Image((void*)(intptr_t)texture, size); g_object_unref(pixbuf); } else { std::cerr << "Failed to get pixbuf from window" << std::endl; } } else { std::cerr << "Failed to get GdkWindow" << std::endl; // 如果获取失败,尝试显示窗口并再次获取 gtk_widget_show_all(window); gdk_window = gtk_widget_get_window(window); if (!gdk_window) { std::cerr << "Failed to get GdkWindow even after showing window" << std::endl; return; } } // 错误处理 if (load_failed) { ImGui::TextColored(ImVec4(1,0,0,1), "Failed to load page"); if (ImGui::Button("Retry")) { load_failed = false; webkit_web_view_reload(WEBKIT_WEB_VIEW(webview0)); } } // 在render方法中添加映射检查 if (!gtk_widget_get_mapped(window)) { std::cout << "Window not mapped, forcing realization..." << std::endl; gtk_widget_realize(window); // 等待窗口映射 for (int i = 0; i < 10; i++) { while (gtk_events_pending()) { gtk_main_iteration(); } if (gtk_widget_get_mapped(window)) { break; } } } } void WebView::load_local_file(const std::string file_path) { gchar* cwd = g_get_current_dir(); if (cwd) { gchar* full_path = g_build_filename(cwd, file_path.c_str(), NULL); std::cout << "Loading file: " << full_path << std::endl; // 调试输出 if (g_file_test(full_path, G_FILE_TEST_EXISTS)) { gchar* uri = g_filename_to_uri(full_path, NULL, NULL); if (uri) { std::cout << "URI: " << uri << std::endl; // 调试输出 webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview0), uri); g_free(uri); } else { std::cerr << "无法创建URI" << std::endl; load_default_map(); } } else { std::cerr << "文件不存在: " << full_path << std::endl; load_default_map(); } g_free(full_path); g_free(cwd); } else { std::cerr << "无法获取当前工作目录" << std::endl; load_default_map(); } } void WebView::load_default_map() { const char* default_html = "<!DOCTYPE html>" "<html>" "<head>" " <meta charset='utf-8'>" " <title>本地地图</title>" " <style>" " body, html { margin: 0; padding: 0; width: 100%; height: 100%; }" " #map { width: 100%; height: 100%; background: #f0f0f0; }" " .info { position: absolute; top: 10px; left: 10px; background: white; padding: 10px; }" " </style>" "</head>" "<body>" " <div id='map'></div>" " <div class='info'>" " <h2>本地地图查看器</h2>" " <p>这是一个自动创建的地图文件</p>" " </div>" "</body>" "</html>"; webkit_web_view_load_html(WEBKIT_WEB_VIEW(webview0), default_html, nullptr); } void WebView::resize(int width, int height) { this->width = width; this->height = height; gtk_widget_set_size_request(GTK_WIDGET(webview0), width, height); } GLuint WebView::get_texture() { return texture; } void WebView::on_load_failed(WebKitWebView* webView, WebKitLoadEvent load_event, gchar* failing_uri, GError* error, gpointer user_data) { WebView* view = static_cast<WebView*>(user_data); view->load_failed = true; std::cerr << "Failed to load: " << failing_uri << ", error: " << error->message << std::endl; } 如下为CMakeList.txt cmake_minimum_required(VERSION 3.10) project(ImGuiMapViewer) set(CMAKE_CXX_STANDARD 17) find_package(PkgConfig REQUIRED) pkg_check_modules(WEBKIT2GTK REQUIRED webkit2gtk-4.0) pkg_check_modules(EPOXY REQUIRED epoxy) # 添加epoxy的pkg-config查找 add_executable(${PROJECT_NAME} main.cpp web_view.cpp imgui/imgui.cpp imgui/imgui_draw.cpp imgui/imgui_tables.cpp imgui/imgui_widgets.cpp imgui/backends/imgui_impl_glfw.cpp imgui/backends/imgui_impl_opengl3.cpp ) target_include_directories(${PROJECT_NAME} PRIVATE ${WEBKIT2GTK_INCLUDE_DIRS} ${EPOXY_INCLUDE_DIRS} imgui imgui/backends ) target_link_libraries(${PROJECT_NAME} PRIVATE ${WEBKIT2GTK_LIBRARIES} ${EPOXY_LIBRARIES} glfw GL dl pthread ) 以下为html文件:<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>本地地图</title> <style> body, html { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background-color: #f0f0f0; } #map { width: 100%; height: 100%; background: linear-gradient(45deg, #e0e0e0, #f0f0f0); position: relative; } .info { position: absolute; top: 10px; left: 10px; background: white; padding: 10px; border-radius: 5px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .marker { position: absolute; width: 20px; height: 20px; background: red; border-radius: 50%; transform: translate(-50%, -50%); } </style> </head> <body> <div id="map"> <div class="info"> <h2>本地地图查看器</h2> <p>这是一个简单的本地地图实现</p> </div> <div class="marker" style="top: 40%; left: 40%;"></div> <div class="marker" style="top: 60%; left: 60%;"></div> <div class="marker" style="top: 30%; left: 70%;"></div> </div> <script> // 简单的交互逻辑 const map = document.getElementById('map'); let scale = 1; let offsetX = 0; let offsetY = 0; let isDragging = false; let lastX, lastY; map.addEventListener('mousedown', (e) => { isDragging = true; lastX = e.clientX; lastY = e.clientY; map.style.cursor = 'grabbing'; }); map.addEventListener('mousemove', (e) => { if (isDragging) { offsetX += (e.clientX - lastX) / scale; offsetY += (e.clientY - lastY) / scale; lastX = e.clientX; lastY = e.clientY; // 更新标记位置 document.querySelectorAll('.marker').forEach(marker => { const top = parseFloat(marker.style.top); const left = parseFloat(marker.style.left); marker.style.top = `${top + (e.clientY - lastY) / scale}%`; marker.style.left = `${left + (e.clientX - lastX) / scale}%`; }); } }); map.addEventListener('mouseup', () => { isDragging = false; map.style.cursor = 'grab'; }); map.addEventListener('mouseleave', () => { isDragging = false; map.style.cursor = 'grab'; }); map.addEventListener('wheel', (e) => { e.preventDefault(); const delta = e.deltaY > 0 ? 0.9 : 1.1; scale *= delta; // 更新标记大小 document.querySelectorAll('.marker').forEach(marker => { const size = 20 * scale; marker.style.width = `${size}px`; marker.style.height = `${size}px`; }); }); // 初始光标样式 map.style.cursor = 'grab'; </script> </body> </html> 请问 为何运行时没有加载进html文件。是哪里没有渲染吗
最新发布
08-29
虽然给定引用未直接涉及在VSCode(WSL Ubuntu C++ ImGui)环境下使用WebKit运行程序无法加载HTML文件及渲染问题排查,但可从通用的编程环境和WebKit使用角度分析可能原因及排查方法。 ### 无法加载HTML文件的可能原因及排查 - **文件路径问题**:程序可能无法找到HTML文件,这可能是因为指定的文件路径不正确。需要确保在代码中使用的文件路径是准确的,可使用绝对路径进行测试。例如在C++代码中,若使用`std::ifstream`打开文件,要保证路径无误。 ```cpp #include <iostream> #include <fstream> #include <string> int main() { std::ifstream file("/path/to/your/file.html"); if (!file.is_open()) { std::cerr << "Failed to open file" << std::endl; return 1; } std::string line; while (std::getline(file, line)) { std::cout << line << std::endl; } file.close(); return 0; } ``` - **文件权限问题**:在Ubuntu系统中,若程序没有足够的权限访问HTML文件,则无法加载。可使用`ls -l`命令查看文件权限,并使用`chmod`命令修改权限。例如,若要给所有用户添加读取权限,可使用`chmod a+r /path/to/your/file.html`。 - **WebKit配置问题**:WebKit可能没有正确配置以加载HTML文件。检查WebKit初始化代码,确保其正确设置了相关参数,如用户代理、资源加载路径等。 ### 渲染问题排查 - **依赖库问题**:WebKit依赖于一些系统库来进行渲染,若这些库缺失或版本不兼容,可能导致渲染问题。可检查系统中WebKit相关依赖库的安装情况,使用`apt list --installed | grep webkit`查看已安装的WebKit库,并根据需要进行更新或重新安装。 - **图形驱动问题**:WSL的图形驱动可能存在问题,影响WebKit的渲染。可尝试更新WSL的图形驱动,或检查是否有适用于WSL的图形加速设置。 - **内存和性能问题**:若系统内存不足或性能不佳,可能导致WebKit渲染缓慢或失败。可使用`top`或`htop`命令查看系统资源使用情况,关闭不必要的程序以释放内存。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值