<tauri><rust><GUI>基于rust和tauri,在已有的前端框架上手动集成tauri示例

前言
本文是基于rust和tauri,由于tauri是前、后端结合的GUI框架,既可以直接生成包含前端代码的文件,也可以在已有的前端项目上集成tauri框架,将前端页面化为桌面GUI。

环境配置
系统:windows 10
平台:visual studio code
语言:rust、javascript
库:tauri2.0

概述
本文使用vite构建一个前端页面,实现简单的计算器功能,然后使用tauri集成前端,实现桌面端的计算器窗口,并使用rust后端进行计算器的功能计算,然后返回前端。

1、创建前端页面

前端框架有很多,本文是基于一个常用的vite框架,来创建一个前端项目,使用原始模板,即不使用vue3或者react这样的前端模板,而是vite框架下,原生的javascript和html。
我们打开visual studio code,如果你有现成的根文件夹,直接打开,或者创建一个新的根文件夹:

mkdir tauri
cd tauri

然后初始化一个vite项目:

npm create vite@latest

初始化时,将需要填写一个基本信息:
在这里插入图片描述
如上图,项目名称自定义,前端框架选原生,语言选js。然后会在你的根文件夹下生成一个项目:
在这里插入图片描述
我们可以先看一下这个前端页面运行起来的效果,但这之前还需要安装以下npm包:

cd vitepro
npm install
npm run dev

在这里插入图片描述
这里是vite提供的模板示例,是一个计数器,点击网页下端的按钮,计数值会改变。
但我们需要的是一个计算器界面,所以需要对代码进行修改。先看一下vite提供的示例代码的结构:
在这里插入图片描述
如上图所示,main.js是主函数入口,counter.js才是计数功能模块。

main.js源码
import './style.css'
import javascriptLogo from './javascript.svg'
import viteLogo from '/vite.svg'
import { setupCounter } from './counter.js'document.querySelector('#app').innerHTML = `<div><a href="https://vite.dev" target="_blank"><img src="${viteLogo}" class="logo" alt="Vite logo" /></a><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank"><img src="${javascriptLogo}" class="logo vanilla" alt="JavaScript logo" /></a><h1>Hello Vite!</h1><div class="card"><button id="counter" type="button"></button></div><p class="read-the-docs">Click on the Vite logo to learn more</p></div>
`setupCounter(document.querySelector('#counter'))

其中,setupCounter是由counter.js中导出的模块。官方提供的这个示例比较简单,我们希望稍作修改,首先是将app这个div的内容提取出来,写在单独的html文件中,可以将其命名为template.html

<div><a href="https://vite.dev" target="_blank"><img src="${viteLogo}" class="logo" alt="Vite logo" /></a><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank"><img src="${javascriptLogo}" class="logo vanilla" alt="JavaScript logo" /></a><h1>Hello Vite!</h1><div class="card"><button id="counter2" type="button"></button></div><p class="read-the-docs">Click on the Vite logo to learn more</p></div>

然后将template.html放在public文件夹中,方便调用。
接着修改main.js,以便于获取template.html中的内容:

import './style.css'
import javascriptLogo from './javascript.svg'
import viteLogo from '/vite.svg'
import { setupCounter } from './counter.js'async function loadTemplate(path,divid) {const response = await fetch(path);const template = await response.text();document.querySelector(divid).innerHTML = template.replace('${viteLogo}', viteLogo).replace('${javascriptLogo}', javascriptLogo);setupCounter(document.querySelector('#counter'))
}loadTemplate('./template.html','#app')

此处,使用fetch来获取本地html文件的内容,效果是一致的。接着,我们修改template.html的页面,设计一个简单的计算器布局,此处是前端程序,比较简单,代码如下,可以参考,也可以直接去使用人工智能生成。
template.html

<label for="history">历史:</label><input type="text" class="history" id="history" disabled><label for="display">实时:</label><input type="text" class="display" id="display" disabled><div class="buttons"><button class="button" id="clearbtn" >C</button><button class="button" id="squarebtn"></button><button class="button" id="backbtn"></button><button class="button operator" id="mulbtn"  >*</button><button class="button" id="sevenbtn" >7</button><button class="button" id="eightbtn" >8</button><button class="button" id="ninebtn" >9</button><button class="button operator" id="divbtn" >/</button><button class="button" id="fourbtn" >4</button><button class="button" id="fivebtn" >5</button><button class="button" id="sixbtn" >6</button><button class="button operator" id="addbtn" >+</button><button class="button" id="onebtn" >1</button><button class="button" id="twobtn" >2</button><button class="button" id="threebtn" >3</button><button class="button operator" id="subbtn" >-</button><button class="button" id="zerobtn" >0</button><button class="button" id="dotbtn" >.</button><button class="button equal" id="equalbtn" >=</button></div>

上面的代码在网页上看起来如下:
在这里插入图片描述
注意,页面布局还需要配合css样式:
global.css

body {font-family: Arial, sans-serif;display: flex;justify-content: center;align-items: center;height: 100vh;background-color: #ffffff;
}
.app {background-color: #73b4f1;padding: 20px;border-radius: 10px;box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);display: flex;flex-direction: column;
}
.display {height: 30px;text-align: right;margin-bottom: 10px;padding: 10px;font-size: 18px;border: 1px solid #e7adad;border-radius: 5px;
}
.history{height: 30px;text-align: right;margin-bottom: 10px;padding: 10px;font-size: 18px;border: 1px solid #e7adad;border-radius: 5px;
}
.buttons {display: grid;grid-template-columns: repeat(4, 1fr);gap: 10px;
}
.button {padding: 20px;font-size: 18px;border: none;border-radius: 5px;background-color: #f0f0f0;cursor: pointer;
}
.button:hover {background-color: #ddd;
}
.button.operator {background-color: #ff9500;color: white;
}
.button.operator:hover {background-color: #e08900;
}
.button.equal {background-color: #34c759;color: white;grid-column: span 2;
}
.button.equal:hover {background-color: #2da94f;
}

页面布局写好了,就需要为按钮实现功能了,这部分需要写在js中,我们将修改counter.js的代码,为其中添加以下函数:
1、清除函数
2、数字输入函数
3、计算函数(加减乘除以及平方)
4、退格函数
以上是本文将要实现的计算器功能,当然只是简单的实现,并非是一个完整的计算器功能,本文的目的是通过这个小的计算器程序来说明tauri集成前端的示例。
counter.js

let currentInput='';
let previousInput='';
let operator=null;
let predisplay=''
let calflag=false;// 导出一个函数,用于在输入框中添加数字
export function appendnumber(element,number,display,history) {// 给元素添加点击事件element.addEventListener('click',() =>{///如果连续输入,则直接拼接currentInput += number;///输入值将实时显示在输入框内display.value = currentInput;///判断是否进行了计算,如果进行了计算,则历史值清空,显示新的实时输入if (calflag){history.value = currentInput;calflag=false;} else {history.value =history.value + number;}})} 
// 导出一个函数,用于在元素上添加功能符号的点击事件
export  function appendOperator(element,op,display,history) {// 为元素添加点击事件element.addEventListener('click',() => {///如果点击了功能符号,但计算完成标记存在,则不进行任何回应if (calflag){return;}///如果多次点击功能符号而不是输入下一个数值,则切换功能符号if (currentInput === '') {operator = op;history.value =previousInput + ' ' + operator;return;};///如果前次输入值不为空,则直接计算结果if (previousInput !== '') {//calculate();insidecalculate();}operator = op;previousInput = currentInput;display.value = previousInput;history.value =currentInput + ' ' + operator;predisplay=display.value;currentInput = '';})}function insidecalculate(){if (currentInput === '' || previousInput === '') return;let result;const prev = parseFloat(previousInput);const current = parseFloat(currentInput);switch (operator) {case '+':result = prev + current;break;case '-':result = prev - current;break;case '*':result = prev * current;break;case '/':result = prev / current;break;default:return;}currentInput = result.toString();
}// 导出一个函数,用于计算
export function calculate(element,display,history) {// 给element添加点击事件监听element.addEventListener('click',()=>{// 如果currentInput或previousInput为空,则返回if (currentInput === '' || previousInput === '') return;let result;// 将previousInput和currentInput转换为浮点数const prev = parseFloat(previousInput);const current = parseFloat(currentInput);// 根据operator的值进行计算switch (operator) {case '+':result = prev + current;break;case '-':result = prev - current;break;case '*':result = prev * current;break;case '/':result = prev / current;break;default:return;}// 将计算结果转换为字符串,赋值给currentInputcurrentInput = result.toString();// 将计算结果添加到history中history.value =history.value + '=' + currentInput + '\n';// 将计算结果显示在display中display.value = currentInput;// 将display的值赋值给predisplaypredisplay=display.value;// 将operator置为nulloperator = null;// 将previousInput置为空previousInput = '';// 将currentInput置为空currentInput='';// 将calflag置为truecalflag=true;})}// 导出一个函数,用于清除显示
export function clearDisplay(element,operator,display,history) {// 为element添加点击事件监听器element.addEventListener('click',()=>{// 将currentInput设置为空字符串currentInput = '';// 将operator设置为nulloperator = null;// 将previousInput设置为空字符串previousInput = '';// 将display的值设置为空字符串display.value = '';// 将predisplay设置为空字符串predisplay='';// 将history的值设置为空字符串history.value='';})
}// 导出一个函数,用于返回上一个输入的字符
export function backLast(element,display,history) {// 给element添加点击事件element.addEventListener('click',()=>{// 如果calflag为falseif (!calflag) {// 将currentInput的最后一个字符删除currentInput = currentInput.slice(0, -1);// 将删除后的currentInput赋值给display的valuedisplay.value = currentInput;// 将history的最后一个字符删除history.value = history.value.slice(0, -1);} else {// 将currentInput的最后一个字符删除currentInput = currentInput.slice(0, -1);// 将删除后的currentInput赋值给display的valuedisplay.value = currentInput;}})
}// 导出一个函数,用于计算平方
export function squarenumber(element,display,history) {// 给element元素添加点击事件element.addEventListener('click',()=>{// 将currentInput的值平方currentInput = Math.pow(currentInput,2);// 将平方后的值显示在display元素中display.value = currentInput;// 将计算过程添加到history元素中history.value = history.value + '^2' + '=' + currentInput + '\n';// 将previousInput和currentInput的值重置为空previousInput = '';currentInput='';// 将calflag的值设置为truecalflag=true;})
}

然后在main.js中导入上述函数:

import { appendnumber,appendOperator,calculate,clearDisplay,backLast,squarenumber } from './counter.js'
2、tauri集成

完成了前端页面后,我们现在来手动集成tauri。tauri的官网提供了详细的步骤,在我们上面创建的项目文件夹vitepro下,安装Tauri 的 CLI 工具:

npm install -D @tauri-apps/cli@latest

安装完成后,node包文件夹下显示tauri库:
在这里插入图片描述
还是在当前项目文件夹的根目录下,我们使用tauri的cli工具,初始化一个tauri,使其包含在我们已经创建好的前端项目中:

npx tauri init

运行后,会让你填写几个问题:

✔ What is your app name? · vitepro
✔ What should the window title be? · vitepro
? Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri/tauri.conf.json" file that will be created? ›
✔ Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri/tauri.conf.json" file that will be created? · ..
✔ What is the url of your dev server? · http://localhost:5173
✔ What is your frontend dev command? · npm run dev    
✔ What is your frontend build command? · npm run build

由于我们是集成在vite构建的前端项目中,所以url可以使用默认的

http://localhost:5173

初始化完成后,可以看到项目文件夹下会多出一个src-tauri文件夹:
在这里插入图片描述
此时,我们可以使用:

npx tauri dev

来运行一下看看:
在这里插入图片描述
看起来还可以,不过默认的窗口尺寸并不匹配,我们可以修改一下窗口参数。
在src-tauri文件夹下,有一个配置文件:tauri.conf.json,其中有个app参数,修改其中的windows项,将窗口宽度设置为400。

 "app": {"windows": [{"title": "vitepro","width": 400,"height": 600,"resizable": true,"fullscreen": false}],"security": {"csp": null}},

修改后如下:
在这里插入图片描述
看起来要好多了,事实上,窗口还可以美化,但是本文暂不再赘述。

3、后端函数替代

现在,我们想把之前在javascript中的函数功能放到rust中实现,再通过接口在前端调用,实现前后端的数据通讯。此处,tauri提供了两种方法,一种是使用javascript API库,另一个是使用withGlobalTauri这个配置,实现预构建API。
1、如果使用javascript API,那么需要先安装@tauri-apps/api
使用

npm install @tauri-apps/api

指令安装api库,安装完成后调用invoke函数:

import { invoke } from '@tauri-apps/api/core'

2、如果使用withGlobalTauri配置,那么,需要修改tauri.conf.json文件中的build项:

{"build": {..."withGlobalTauri": true},

添加withGlobalTauri,并设置为true。这里省略了其他参数的设置。
然后在js中调用:

const invoke = window.__TAURI__.invoke

其中,invoke函数的参数有三个,第一个是要调用的函数名,第二个是函数的参数(如果有的话),第三个是一个可选项(一个headers参数,用于操作http请求和响应的接口)
如果要在前端调用rust,首先你需要在rust中编写好函数,然后才能在前端使用,在tauri初始化的src-tauri文件夹中,我们打开lib.rs文件,添加一个函数:

#[tauri::command]
fn greet(name:&str) -> String {format!("hello,this is from rust msg,your name is {}!",name)
}

注意到,这里函数被标记了,使用#[tauri::command]来表明这是一个可以被前端使用的rust函数,但是这还不够,还需要将这个函数传递给构建列表:

pub fn run() {tauri::Builder::default().invoke_handler(tauri::generate_handler![greet]).setup(|app| {if cfg!(debug_assertions) {app.handle().plugin(tauri_plugin_log::Builder::default().level(log::LevelFilter::Info).build(),)?;}Ok(())}).run(tauri::generate_context!()).expect("error while running tauri application");
}

注意上面代码中的invoke_handler,标记的函数通过它来传递。我们来测试一下,为前端添加一个文本标签,然后程序运行后,显示一个从rust函数传递过来的字符串。
我们先修改template.html,添加一个新标签:

    <div id="titlediv" class="titlediv"><p id="titlep">等待rust传递的字符:</p></div>

设置一下背景色,以做区分。先看下效果:
在这里插入图片描述
现在我们还没有调用rust的函数,所以只显示设定的内容。

然后在main.js中,我们来修改这个标签,将其内容替换为从rust函数获取的字符串:

const titlep=document.getElementById('titlep');const greeting=await invoke('greet');titlep.innerHTML +=greeting;console.log(greeting);

同时修改一下rust端的函数:

#[tauri::command]
fn greet() -> String {format!("hello,这里是rust数据!")
}

再次运行:
在这里插入图片描述
说明是正确的从前端调用了rust的函数,并获取了返回的内容。
现在,我们尝试将之前的计算函数写到rust中,然后前端这边点击相应按钮时,调用rust函数,并返回结果。
以等号的功能为例,原先的等号功能的核心逻辑是:

 // 根据operator的值进行计算switch (operator) {case '+':result = prev + current;break;case '-':result = prev - current;break;case '*':result = prev * current;break;case '/':result = prev / current;break;default:return;}

我们就将这段替换掉,先在rust中新建一个calculate_rs函数:

#[tauri::command]
fn calculate_rs(current_input:f32,previous_input:f32,operator:&str
) -> f32 {match operator {"+" => current_input + previous_input,"-" => current_input - previous_input,"*" => current_input * previous_input,"/" => current_input / previous_input,_ => 0.0,}
}

这个函数就是根据输入数字和计算符返回相应结果,当然这里比较简单,没有进行错误处理。
在js中调用此函数来计算:

 const cal=await invoke('calculate_rs',{currentInput:current,previousInput:prev,operator:operator})
4、实例演示

上面介绍了代码,下面来看一下动态演示:

tauri简单计算器程序演示

5、源代码

https://download.csdn.net/download/normer123456/90354147

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

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

相关文章

mysql 学习11 事务,事务简介,事务操作,事务四大特性,并发事务问题,事务隔离级别

一 事务简介&#xff0c; 数据库准备&#xff1a; create table account(id int auto_increment primary key comment 主键ID,name varchar(128) not null comment 姓名,backaccountnumber char(18) unique comment 银行账号,money float comment 余额 )comment 银行账号表;…

重塑生产制造企业项目管理新范式:项目模板在Tita中的卓越实践

在竞争激烈的生产制造领域&#xff0c;每一个项目的成功执行都是企业稳健前行的重要基石。然而&#xff0c;面对复杂多变的生产流程、严格的交货期限以及不断变化的客户需求&#xff0c;如何确保项目高效、有序地进行&#xff0c;成为了众多企业面临的共同挑战。此时&#xff0…

Ajax-介绍

概念: Asynchronous JavaScript And XML&#xff0c;异步的JavaScript和XML. 作用: 数据交换:通过Aiax可以给服务器发送请求&#xff0c;并获取服务器响应的数据 异步交互: 可以在不重新加载整个页面的情况下&#xff0c;与服务器交换数据并更新部分网页的技术, 如:搜索联想、…

基于可信数据空间的企业数据要素与流通体系建设(附ppt 下载)

近期&#xff0c;可信数据空间会议召开。大数据系统软件国家工程研究中心总工程师王晨发表了题为《基于可信数据空间的企业数据要素与流通体系建设》主旨演讲。 WeChat Subscription Account【智慧城市指北】&#xff0c;可搜索相关关键字“20250107”&#xff0c;可获取具体获…

idea整合deepseek实现AI辅助编程

1.File->Settings 2.安装插件codegpt 3.注册deepseek开发者账号&#xff0c;DeepSeek开放平台 4.按下图指示创建API KEY 5.回到idea配置api信息&#xff0c;File->Settings->Tools->CodeGPT->Providers->Custom OpenAI API key填写deepseek的api key Chat…

Composo:企业级AI应用的质量守门员

在当今快速发展的科技世界中,人工智能(AI)的应用已渗透到各行各业。然而,随着AI技术的普及,如何确保其可靠性和一致性成为了企业面临的一大挑战。Composo作为一家致力于为企业提供精准AI评估服务的初创公司,通过无代码和API双模式,帮助企业监测大型语言模型(LLM)驱动的…

增加工作台菜单页面,AI问答应用支持上下文设置,数据库表索引优化,zyplayer-doc 2.4.8 发布啦!

zyplayer-doc是一款适合企业和个人使用的WIKI知识库管理工具&#xff0c;支持在线编辑富文本、Markdown、表格、Office文档、API接口、思维导图、Drawio以及任意的文本文件&#xff0c;专为私有化部署而设计&#xff0c;最大程度上保证企业或个人的数据安全&#xff0c;支持以内…

C++开发(软件开发)常见面试题

目录 1、C里指针和数组的区别 2、C中空指针请使用nullptr不要使用NULL 3、http/https区别和头部结构&#xff1f; 4、有了mac地址为什么还要ip地址&#xff1f;ip地址的作用 5、有了路由器为什么还要交换机&#xff1f; 6、面向对象三大特性 7、友元函数 8、大端小端 …

智能理解 PPT 内容,快速生成讲解视频

当我们想根据一版 PPT 制作出相对应的解锁视频时&#xff0c;从撰写解锁词&#xff0c;录制音频到剪辑视频&#xff0c;每一个环节都需要投入大量的时间和精力&#xff0c;本方案将依托于阿里云函数计算 FC 和百炼模型服务&#xff0c;实现从 PPT 到视频的全自动转换&#xff0…

第八届大数据与应用统计国际学术研讨会(ISBDAS 2025)

重要信息 官网&#xff1a;www.is-bdas.org 时间&#xff1a;2025年2月28-3月2日 地点&#xff1a;中国 广州 主办单位&#xff1a;广东省高等教育学会人工智能与高等教育研究分会 协办单位&#xff1a;北京师范大学人工智能与未来网络研究院、人工智能与大数据科研基地 …

认识O(NlogN)的排序

归并排序 归并排序&#xff08;任何一个递归&#xff09;如果不懂可以画一个树状结构去帮助自己去理解。 核心排序方法为Merger public class 归并排序 {public static void main(String[] args) {int[] arr1 {3, 1, 2, 2, 5, 6};int[] arr2 Arrays.copyOf(arr1, arr1.len…

方波的基波和谐波详细推导,以及matlab验证[电路原理---2]

最近要滤波&#xff0c;从1KHZ 方波中获得正弦波&#xff0c;这让我们要对方波的频谱有具体的了解。虽然楼主一年前刚学过傅里叶。但也是忘的干干净净查阅资料后终于是整理出来。用漂亮的latex打出来了&#xff0c;为自己留存一份记录&#xff0c;也分享给大家学习。 方波的基…

计算机组成原理(3)

计算机组成原理&#xff08;3&#xff09; 存储器层次结构存储器概述存储器分类存储器性能指标 半导体随机存储SRAM和DRAM 存储器层次结构 主存-辅存&#xff1a;实现了虚拟存储系统&#xff0c;解决了主存容量不足的问题&#xff1b; Cache-主存&#xff1a;解决了主存于CPU速…

2024最新版Java面试题及答案,【来自于各大厂】

发现网上很多Java面试题都没有答案&#xff0c;所以花了很长时间搜集整理出来了这套Java面试题大全~ 篇幅限制就只能给大家展示小册部分内容了&#xff0c;需要完整版的及Java面试宝典小伙伴点赞转发&#xff0c;关注我后在【翻到最下方&#xff0c;文尾点击名片】即可免费获取…

DeepSeek-V3 论文解读:大语言模型领域的创新先锋与性能强者

论文链接&#xff1a;DeepSeek-V3 Technical Report 目录 一、引言二、模型架构&#xff1a;创新驱动性能提升&#xff08;一&#xff09;基本架构&#xff08;Basic Architecture&#xff09;&#xff08;二&#xff09;多令牌预测&#xff08;Multi-Token Prediction&#xf…

Mac 基于Ollama 本地部署DeepSeek离线模型

最近节日期间最火的除了《哪吒》就是deepseek了&#xff0c;毕竟又让西方各个层面都瑟瑟发抖的产品。DeepSeek凭借其强大的AI能力真的是在全球多个领域展现出强大的影响力。由于受到外部势力的恶意攻击倒是deepseek官方服务不稳定&#xff0c;国内其他厂家的适配版本也不是很稳…

51单片机之引脚图(详解)

8051单片机引脚分类与功能笔记 1. 电源引脚 VCC&#xff08;第40脚&#xff09;&#xff1a;接入5V电源&#xff0c;为单片机提供工作电压。GND&#xff08;第20脚&#xff09;&#xff1a;接地端&#xff0c;确保电路的电位参考点。 2.时钟引脚 XTAL1&#xff08;第19脚&a…

力扣刷题 题11,12

题目11 思路&#xff1a;设置左右指针 left和 right 指针指向数组的开始和末尾&#xff0c;max_water 用于记录最大容量初始为0。利用while循环left<right&#xff0c;移动指针比较数组元素 height[left] 和 height[right] 的大小&#xff0c;移动较短的那条线的指针&#x…

使用Python实现PDF与SVG相互转换

目录 使用工具 使用Python将SVG转换为PDF 使用Python将SVG添加到现有PDF中 使用Python将PDF转换为SVG 使用Python将PDF的特定页面转换为SVG SVG&#xff08;可缩放矢量图形&#xff09;和PDF&#xff08;便携式文档格式&#xff09;是两种常见且广泛使用的文件格式。SVG是…

爬虫工程师分享:获取京东商品详情SKU数据的技术难点与攻破方法

在电商数据领域&#xff0c;京东商品详情页的SKU数据是许多爬虫工程师的目标。这些数据包含了商品的价格、库存、规格等关键信息&#xff0c;对于市场分析、价格监控等应用场景至关重要。然而&#xff0c;获取这些数据并非易事&#xff0c;京东作为国内电商巨头&#xff0c;其反…