前言
本文是基于rust和tauri,由于tauri是前、后端结合的GUI框架,既可以直接生成包含前端代码的文件,也可以在已有的前端项目上集成tauri框架,将前端页面化为桌面GUI。
环境配置
- 系统:windows 10
- 平台:visual studio code
- 语言:rust、javascript
- 库:tauri2.0
概述
本文主要说明,在tauri中如何实现多窗口以及窗口间如何实现数据传递。
1、创建前端项目
可以参考我之前的博文,本文不再赘述:
1、< tauri>< rust>< GUI>使用tauri实现一个简单的计算器程序
我们创建新的页面代码,因为我们要实现多窗口,本例以两个窗口来举例,那么我们将创建两个页面,首先是主页面:
html
<div id="tauriappdiv" class="tauriappdiv">
<div id="zone1div">
<label for="windownameinput">窗口标签(唯一):</label>
<input id="windownameinput" type="text" placeholder="请输入窗口名称">
<button id="newwindowbtn">新建窗口</button>
</div>
<div id="zone2div">
<div id="zone2div0">
<div>
<label for="windowlabelinput">窗口标签:</label>
<input id="windowlabelinput" type="text" placeholder="请输入窗口标签">
</div>
<div>
<label for="sendmsginput">消息:</label>
<input id="sendmsginput" type="text" placeholder="请输入消息">
</div>
</div>
<button id="sendmsgbtn">发送消息给子窗口</button>
</div>
<div id="zone3div">
<button id="eventbtn">触发事件</button>
<button id="channelbtn">触发通道</button>
<button id="eventtosubbtn">触发事件给子窗口</button>
</div>
<div>
<p id="showeventmsg"></p>
<p id="showchannelmsg"></p>
<p>来自子窗口:</p>
<p id="msgfromsub"></p>
</div>
</div>
页面预览如下: 然后创建一个子页面:
html
<div>
<h1>子窗口</h1>
<div>
<p>来自主窗口:</p>
<p id="mainwindowmsg"></p>
</div>
<div>
<input id="sendmsginput" type="text" placeholder="输入消息">
<button id="sendmsgtomainbtn" type="button">发送给主窗口</button>
</div>
</div>
预览如下:
2、创建子窗口
我们要实现的功能是,在主窗口,通过按钮创建子窗口,注意,子窗口必须要有一个唯一的标签名,哪怕你不给窗口输入名字,它的标签就是空字符,也是不能重复的,如果想要创建两个标签名一样的子窗口,会报错:
如何在tauri中创建子窗口,tauri官网给出了两个方法,第一个是静态创建,可以通过tauri.conf.json这个配置文件来实现:
上图是官网的示例,这个方法很简单,但是我们不使用这种方法,我们使用官网提供的第二种方法,即通过代码来创建新窗口:
rust
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.setup(|app| {
let webview_url = tauri::WebviewUrl::App("index.html".into());
// First window
tauri::WebviewWindowBuilder::new(app, "first", webview_url.clone())
.title("First")
.build()?;
// Second window
tauri::WebviewWindowBuilder::new(app, "second", webview_url)
.title("Second")
.build()?;
Ok(())
})
.run(context)
.expect("error while running tauri application");
这是官网的示例代码,在tauri程序初始化时,会加载两个窗口。这是简单的示例,我们在实际应用时,会希望当我点击一个按钮时,出现一个新的窗口。 所以,我们会希望从前端来创建新窗口,这可以通过invoke函数来实现。 具体来说,我们需要先在rust端创建一个新的指令函数:
rust
#[tauri::command]
async fn newwindow(app: tauri::AppHandle, page: &str, title: &str) -> Result<(), String> {
let webview_url = tauri::WebviewUrl::App(page.into());
match tauri::WebviewWindowBuilder::new(&app, title, webview_url)
.title(title)
.build()
{
Ok(_) => Ok(()),
Err(err) => Err(err.to_string()),
}
}
然后将此函数注册为tauri的前端可以调用:
rust
.invoke_handler(tauri::generate_handler![
newwindow,
...此处省略其他代码
])
这样,我们就可以从前端来调用此函数:
javascript
//从前端创建新窗口
newwindowbtn.addEventListener('click', async () => {
const name = windownameinput.value
invoke('newwindow',{page:'second.html',title:name})
.then(res=>{
}).catch(err=>{
console.log(err)
alert(`新建窗口错误提示:${err}`)
})
});
3、窗口间数据传递
因为tauri本身就是基于前端和后端集成的,tauri提供了前端与后端进行通信的方式,比如事件以及命令。即event和command。 如上文,我们提到的,在rust建立的函数,为其提供了添加了 #[tauri::command]
这个宏,将函数变为前端可以调用。从而实现了从前端调用rust函数。 而事件系统是专门设计来用于rust和前端通信的系统,但事件系统的限制是数据传递不能太大,事件可以是全局的,也可以指定特定webview页面。 所以,我们可以利用事件系统,来向特定窗口的页面传递数据。 与新建窗口一样,我们需要先在rust端创建注册函数:
rust
#[tauri::command]
fn eventtosomemsg(app:tauri::AppHandle,label:&str,msg:String){
let newmsg = format!("hello,{}", msg);
app.emit_to(EventTarget::labeled(label), "eventtosub", newmsg).unwrap();
}
如上,使用emit_to来传递消息,因为我们要向特定页面传递数据,所以可以使用EventTarget::labeled(label)来指定窗口标签。
我们可以在主窗口和子窗口的js代码中来调用此函数。 在主窗口中,我们调用时,指定子窗口标签:
javascript
//事件传递数据,指定窗口接收
sendmsgbtn.addEventListener('click', async () => {
let lbl = windowlabelinput.value;
let msg = sendmsginput.value;
invoke('eventtosomemsg',{label:lbl,msg:msg})
})
const window = getCurrentWebviewWindow();
window.listen("eventtosub",(event)=>{
msgfromsubp.innerText = event.payload
})
这里子窗口的标签是通过input元素输入的。 而在子窗口页面的JavaScript代码中,我们也可以调用事件函数:
javascript
const window = getCurrentWebviewWindow();
sendmsgtomainbtn.addEventListener('click',()=>{
let msg = sendmsginput.value
invoke('eventtosomemsg',{ label:'main',msg:msg})
})
window.listen('eventtosub',(event)=>{
mainwindowmsgp.innerHTML = event.payload
})
在子窗口的函数中,我们可以直接指定目标窗口的标签为main,main是默认的主窗口标签。
这样一来,我们就通过rust端的事件触发函数,实现了向指定窗口发送消息,从而实现窗口间数据传递的功能。
4、动态演示