golang调用webview,webview2,go-webview2

news2024/12/26 9:18:46

go version go1.20 windows/amd64

先要了解一些第三方库

1、webview/webview

它是一个跨平台的轻量级的webview库,面向的是C/C++,使用它可以构建跨平台的GUI。webview就是浏览器内核,在不同操作系统上是不同的库,比如在windows上为webview2,所以webview与webview2不要搞混了。从功能上,它实现了Javascript与C/C++之间的相互调用,即bindings。具体实现原理应该与wails差不多。

在不同平台上的支持如下

PlatformTechnologies
LinuxGTK, WebKitGTK
macOSCocoa, WebKit
WindowsWindows API, WebView2

windows api 是windows系统提供的DLL(user32.dll),以此来构建windows上原生的GUI程序。

webview2是微软的Edge浏览器内核,用它来渲染前端页面,下载地址,所以这是两种不同的实现方式。而且webview2只支持windows系统。如果你的系统是Windows 10+,那么 WebView2 Runtime会默认安装了。

2、webview/webview_go

它使用Golang将webview/webview包装了一下,但是使用的是CGO,即webview/webview_go依赖于webview/webview,并使用CGO将依赖引入。

项目还不完善,我的本地系统x86_64-w64-mingw32下面找不到EventToken.h头文件,所以代码无法运行,Support for EventToken.h on mingw64 #45 依然没有解决。

3、jchv/go-webview2

它借鉴了webview/webview,使用Golang包装了webview2,不需要使用CGO,这是与webview_go的不同,当然此包只支持windows,因为它只是包装了webview2。在wails中使用的就是 jchv/go-webview2

它提供了一个示例:cmd/demo/main.go

import (
	"log"

	"github.com/jchv/go-webview2"
)

func main() {
	w := webview2.NewWithOptions(webview2.WebViewOptions{
		Debug:     true,
		AutoFocus: true,
		WindowOptions: webview2.WindowOptions{
			Title:  "Minimal webview example",
			Width:  800,
			Height: 600,
			IconId: 2, // icon resource id
			Center: true,
		},
	})
	if w == nil {
		log.Fatalln("Failed to load webview.")
	}
	defer w.Destroy()
	w.SetSize(800, 600, webview2.HintFixed)
	w.Navigate("https://en.m.wikipedia.org/wiki/Main_Page")
	w.Run()
}

在这里插入图片描述

在这里插入图片描述

go-webview2简要说明

webviewloader\module.go

先使用windows.NewLazyDLL("WebView2Loader")加载WebView2Loader.dll,如果没有找到,它会使用go-winloader库来加载go-webview2自带的WebView2Loader.dll文件(webviewloader/sdk/x64下)。

webview2的参考文档:https://learn.microsoft.com/zh-cn/microsoft-edge/webview2/

go-webview2基本已经封装的很好了,下面是一个WebView对象提供的功能。

// WebView is the interface for the webview.
type WebView interface {

	// Run runs the main loop until it's terminated. After this function exits -
	// you must destroy the webview.
	Run()

	// Terminate stops the main loop. It is safe to call this function from
	// a background thread.
	Terminate()

	// Dispatch posts a function to be executed on the main thread. You normally
	// do not need to call this function, unless you want to tweak the native
	// window.
	Dispatch(f func())

	// Destroy destroys a webview and closes the native window.
	Destroy()

	// Window returns a native window handle pointer. When using GTK backend the
	// pointer is GtkWindow pointer, when using Cocoa backend the pointer is
	// NSWindow pointer, when using Win32 backend the pointer is HWND pointer.
	Window() unsafe.Pointer

	// SetTitle updates the title of the native window. Must be called from the UI
	// thread.
	SetTitle(title string)

	// SetSize updates native window size. See Hint constants.
	SetSize(w int, h int, hint Hint)

	// Navigate navigates webview to the given URL. URL may be a data URI, i.e.
	// "data:text/text,<html>...</html>". It is often ok not to url-encode it
	// properly, webview will re-encode it for you.
	Navigate(url string)

	// SetHtml sets the webview HTML directly.
	// The origin of the page is `about:blank`.
	SetHtml(html string)

	// Init injects JavaScript code at the initialization of the new page. Every
	// time the webview will open a the new page - this initialization code will
	// be executed. It is guaranteed that code is executed before window.onload.
	Init(js string)

	// Eval evaluates arbitrary JavaScript code. Evaluation happens asynchronously,
	// also the result of the expression is ignored. Use RPC bindings if you want
	// to receive notifications about the results of the evaluation.
	Eval(js string)

	// Bind binds a callback function so that it will appear under the given name
	// as a global JavaScript function. Internally it uses webview_init().
	// Callback receives a request string and a user-provided argument pointer.
	// Request string is a JSON array of all the arguments passed to the
	// JavaScript function.
	//
	// f must be a function
	// f must return either value and error or just error
	Bind(name string, f interface{}) error
}

Navigate方法的参数

  • 网络地址:https://en.m.wikipedia.org/wiki/Main_Page
  • 文件系统地址:file:///D:/dev/php/magook/trunk/server/go-webview/test.html,要使用绝对地址。当然,只要是浏览器能识别的文件都行,比如图片,网页,TXT文件等等。
  • 直接展示内容:就是将文件的内容复制进去,此时就需要指定内容是什么形式的,如果不指定就无法展示出来。格式为mimetype,content,其中 mimetype的常用的有data:image/gif, data:image/webp, data:image/jpeg, data:image/png, data:text/html, data:text/text,它会严格按照mimetype渲染,所以类型一定要对。
    但是不推荐这种用法,我发现它会将本行中#后面的内容当做注释舍弃掉,比如
......
let option_CrGrDQTOQuKK = {"color":["#5470c6","#91cc75","#fac858","#ee6666","#73c0de","#3ba272","#fc8452","#9a60b4","#ea7ccc"],"legend":{},"series":[{"name":"Category A","type":"bar","data":[{"value":235},{"value":41},{"value":90},{"value":256},{"value":202},{"value":40},{"value":11}]},{"name":"Category B","type":"bar","data":[{"value":277},{"value":69},{"value":158},{"value":274},{"value":116},{"value":43},{"value":233}]}],"title":{"text":"My first bar chart generated by go-echarts","subtext":"It's extremely easy to use, right?"},"toolbox":{},"tooltip":{},"xAxis":[{"data":["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]}],"yAxis":[{}]}
......

你会发现这一行只剩下了let option_CrGrDQTOQuKK = {"color":[",肯定会影响结果的。所以,最好使用前两种方式。

Bind 方法,进行了绑定之后,js就可以调用name方法,而这个name方法在go中的实现逻辑就是 f。

func (w *webview) Bind(name string, f interface{}) error {
	v := reflect.ValueOf(f)
	if v.Kind() != reflect.Func {
		return errors.New("only functions can be bound")
	}
	if n := v.Type().NumOut(); n > 2 {
		return errors.New("function may only return a value or a value+error")
	}
	w.m.Lock()
	w.bindings[name] = f
	w.m.Unlock()

	w.Init("(function() { var name = " + jsString(name) + ";" + `
		var RPC = window._rpc = (window._rpc || {nextSeq: 1});
		window[name] = function() {
		  var seq = RPC.nextSeq++;
		  var promise = new Promise(function(resolve, reject) {
			RPC[seq] = {
			  resolve: resolve,
			  reject: reject,
			};
		  });
		  window.external.invoke(JSON.stringify({
			id: seq,
			method: name,
			params: Array.prototype.slice.call(arguments),
		  }));
		  return promise;
		}
	})()`)

	return nil
}

它这里使用的是window.external.invoke()方法,即调用外部方法,因为它包装的是new Promise,所以使用 Promise 的机制来处理返回值。

在RPC调用方面,其内部机制还是postMessage()消息事件,更具体的可以阅读我关于wails的文章。

如果 Debug = true,那么按F12可以打开调试。

我的代码如下:

package main

import (
	"errors"
	"log"

	webview2 "github.com/jchv/go-webview2"
)

func main() {
	w := webview2.NewWithOptions(webview2.WebViewOptions{
		Debug:     true,
		AutoFocus: true,
		WindowOptions: webview2.WindowOptions{
			Title:  "Minimal webview example",
			Width:  800,
			Height: 600,
			IconId: 2, // icon resource id
			Center: true,
		},
	})
	if w == nil {
		log.Fatalln("Failed to load webview.")
	}
	defer w.Destroy()
	
	w.Bind("test_love", Great)

	w.Navigate("file:///D:/dev/php/magook/trunk/server/go-webview/test.html")
	
	w.Run()
}

func Great(param string) (result string, err error) {
	if param == "I love you" {
		return "I love you too", nil
	} else if param == "I hate you" {
		return "", errors.New("break up")
	} else {
		return "I don't know", nil
	}
}

特别注意: Bind() 一定要在 Navigate() 之前调用,否则就是无效的!!

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>爱吗?</title>
</head>

<body>
    <div class="box">
        <input type="button" value="爱你" class="btn" onclick="love()">
        <input type="button" value="不爱了" class="btn" onclick="nolove()">
    </div>
</body>

</html>
<style>
    .box {
        margin-top: 100px;
        text-align: center;
    }

    .btn {
        display: inline-block;
        width: 100px;
        height: 50px;
        border-radius: 10px;
        border: 2px solid green;
        text-align: center;
    }
</style>
<script>

    function love() {
        window["test_love"]("I love you").then(result => {
            alert(result);
        }).catch(err => {
            alert(err);
        });
    }
    function nolove() {
        window["test_love"]("I hate you").then(result => {
            alert(result);
        }).catch(err => {
            alert(err);
        });
    }
</script>

打开调试,console.log(window),或者 console.log(window.test_love),就能看到你定义的方法已经被注册到了window对象上。

运行效果:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

由于wails引入了go-webview2,并做了扩展,所以直接使用wails框架即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2245727.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【初阶数据结构篇】双向链表的实现(赋源码)

文章目录 须知 &#x1f4ac; 欢迎讨论&#xff1a;如果你在学习过程中有任何问题或想法&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习。你的支持是我继续创作的动力&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;觉得这篇文章对你有帮助吗&#xff1…

结合第三方模块requests,文件IO、正则表达式,通过函数封装爬虫应用采集数据

#引用BeautifulSoup更方便提取html信息&#xff1b;requests模块&#xff0c;发生http请求&#xff1b;os模块&#xff0c;文件写入import requests from bs4 import BeautifulSoup import os#当使用requests库发送请求时&#xff0c;如果不设置User - Agent&#xff0c;默认的…

开源许可协议

何同学推动了开源协议的认识&#xff0c;功不可没&#xff0c;第一次对开源有了清晰的认识&#xff0c;最宽松的MIT开源协议 源自OSC开源社区&#xff1a;何同学使用开源软件“翻车”&#xff0c;都别吵了&#xff01;扯什么违反MIT

【达梦数据库】主备集群改造成读写分离

目录 背景步骤1、确认授权2、参数修改3、确认驱动版本和数据库版本匹配 背景 客户在双机主备集群手动切换的环境下&#xff0c;发现主库压力较大&#xff0c;需要改成读写分离的模式&#xff0c;将主库的压力分担到备库。 步骤 1、确认授权 select * from v$license;授权使…

docker安装zabbix +grafana

安装zabbix grafana 1、部署 mkdir -p /opt/zabbix/{data,backups}mkdir -p /opt/grafanasudo chown -R 472:472 /opt/grafanasudo chmod -R 755 /opt/grafanacat > docker-compose.yml <<-EOF version: 3.3services:mysql-server:image: mysql:8.1container_name: m…

RocketMQ学习记录

服务器操作系统版本&#xff1a;Ubuntu 24.04 Java版本&#xff1a;21 Spring Boot版本&#xff1a;3.3.5 如果打算用GUI&#xff0c;虚拟机安装Ubuntu 24.04&#xff0c;见虚拟机安装Ubuntu 24.04及其常用软件(2024.7)_ubuntu24.04-CSDN博客https://blog.csdn.net/weixin_4…

网络安全之接入控制

身份鉴别 ​ 定义:验证主题真实身份与其所声称的身份是否符合的过程&#xff0c;主体可以是用户、进程、主机。同时也可实现防重放&#xff0c;防假冒。 ​ 分类:单向鉴别、双向鉴别、三向鉴别。 ​ 主题身份标识信息:密钥、用户名和口令、证书和私钥 Internet接入控制过程 …

Spring 框架七大模块(Java EE 学习笔记03)

​ ​核心容器模块&#xff08;Core Container&#xff09; 核心容器模块在Spring的功能体系中起着支撑性作用&#xff0c;是其他模块的基石。核心容器层主要由Beans模块、Core模块、Contex模块和SpEL模块组成。 &#xff08;1&#xff09;Beans模块。它提供了BeanFactory类&…

IPv6基础知识

IPv6是由IEIF提出的互聯網協議第六版&#xff0c;用來替代IPv4的下一代協議&#xff0c;它的提出不僅解決了網絡地址資源匱乏問題&#xff0c;也解決了多種接入設備接入互聯網的障礙。IPv6的地址長度為128位&#xff0c;可支持340多萬億個地址。如下圖&#xff0c;3ffe:1900:fe…

旷世yolox自定义数据训练和验证和onnx导出推理

目录 1.前言 2.代码 3.环境 4.自定义数据形态 5.配置文件 6.训练 7.验证 8.评估混淆矩阵 9.导出onnx 10.onnx推理 -- 补充&#xff1a;docker环境 1.前言 旷世科技的yolox比较清爽&#xff0c;效果也不错&#xff0c;简单总结主要有三点创新比较高&#xff1a;deco…

Electron开发构建工具electron-vite(alex8088)添加VueDevTools(VitePlugin)

零、介绍 本文章的electron-vite指的是这个项目&#x1f449;electron-vite仓库&#xff0c;electron-vite网站 本文章的VueDevTools指的是VueDevTools的Vite插件版&#x1f449;https://devtools.vuejs.org/guide/vite-plugin 一、有一个用electron-vite创建的项目 略 二、…

软件测试—— Selenium 常用函数(一)

前一篇文章&#xff1a;软件测试 —— 自动化基础-CSDN博客 目录 前言 一、窗口 1.屏幕截图 2.切换窗口 3.窗口设置大小 4.关闭窗口 二、等待 1.等待意义 2.强制等待 3.隐式等待 4.显式等待 总结 前言 在前一篇文章中&#xff0c;我们介绍了自动化的一些基础知识&a…

UE5 腿部IK 解决方案 footplacement

UE5系列文章目录 文章目录 UE5系列文章目录前言一、FootPlacement 是什么&#xff1f;二、具体实现 前言 在Unreal Engine 5 (UE5) 中&#xff0c;腿部IK&#xff08;Inverse Kinematics&#xff0c;逆向运动学&#xff09;是一个重要的动画技术&#xff0c;用于实现角色脚部准…

私有化部署视频平台EasyCVR宇视设备视频平台如何构建视频联网平台及升级视频转码业务?

在当今数字化、网络化的时代背景下&#xff0c;视频监控技术已广泛应用于各行各业&#xff0c;成为保障安全、提升效率的重要工具。然而&#xff0c;面对复杂多变的监控需求和跨区域、网络化的管理挑战&#xff0c;传统的视频监控解决方案往往显得力不从心。 EasyCVR视频融合云…

山东春季高考-C语言-综合应用题

&#xff08;2018年&#xff09;3.按要求编写以下C语言程序&#xff1a; &#xff08;1&#xff09;从键盘上输入三个整数a、b、c&#xff0c;判断能否以这三个数为边构成三角形&#xff0c;若可以则计算机三角形的面积且保留两位小数&#xff1b;若不可以则输出“不能构成三角…

Linux移植IMX6ULL记录 一:编译源码并支持能顺利进入linux

目录 前言 一、不修改文件进行编译 二、修改设备树文件进行编译 前言 我用的开发板是100_ask_imx6ull_pro&#xff0c;其自带的linux内核版本linux-4.9.88&#xff0c;然后从linux官网下载过来的linux-4.9.88版本的arch/arm/configs/defconfig和dts设备树文件并没有对imx6ull…

从Stream的 toList() 和 collect(Collectors.toList()) 方法看Java的不可变流

环境 JDK 21Windows 11 专业版IntelliJ IDEA 2024.1.6 背景 在使用Java的Stream的时候&#xff0c;常常会把流收集为List。 假设有List list1 如下&#xff1a; var list1 List.of("aaa", "bbbbbb", "cccc", "d", "eeeee&qu…

大语言模型---LoRA简介;LoRA的优势;LoRA训练步骤;总结

文章目录 1. 介绍2. LoRA的优势3. LoRA训练步骤&#xff1a;4.总结 1. 介绍 LoRA&#xff08;Low-Rank Adaptation&#xff09;是一种用于高效微调大模型的技术&#xff0c;它通过在已有模型的基础上引入低秩矩阵来减少训练模型时所需的参数量和计算量。具体来说&#xff0c;L…

Debug-031-近期功能实现小结

由于时间原因&#xff0c;没办法对每个小的功能点进行比较细致的总结&#xff0c;这里统一去记录一下最近的实现了的功能&#xff0c;算是存档备份&#xff0c;为今后开发带来便利和参考。 一、ACEeditor ACEeditor使用手册&#xff08;一&#xff09;_ace editor-CSDN博客 AC…

深度学习中的mAP

在深度学习中&#xff0c;mAP是指平均精度均值(mean Average Precision)&#xff0c;它是深度学习中评价模型好坏的一种指标(metric)&#xff0c;特别是在目标检测中。 精确率和召回率的概念&#xff1a; (1).精确率(Precision)&#xff1a;预测阳性结果中实际正确的比例(TP / …