【Tauri2】046—— tauri_plugin_clipboard_manager(一)

发布于:2025-05-20 ⋅ 阅读:(20) ⋅ 点赞:(0)

目录

前言

正文

安装

Rust中的使用

对文字的操作

看看write_text的函数签名

看看read_text的函数签名

对图像的操作

对html的操作

总结


前言

这篇就来看看clipboard这个插件。

参考如下

Clipboard | Taurihttps://tauri.app/plugin/clipboard/


正文

安装

执行下面的命令

pnpm tauri add clipboard-manager

前后端同时安装这个插件,并且注册插件

        .plugin(tauri_plugin_clipboard_manager::init())

这个插件 的英文意思在这里指的是(计算机上的)剪贴板

CLIPBOARD中文(简体)翻译:剑桥词典https://dictionary.cambridge.org/zhs/%E8%AF%8D%E5%85%B8/%E8%8B%B1%E8%AF%AD-%E6%B1%89%E8%AF%AD-%E7%AE%80%E4%BD%93/clipboard


Rust中的使用

要想在Rust中使用,需要导入一个关键的trait

use tauri_plugin_clipboard_manager::ClipboardExt;

许多插件都有这个操作,比如Shell、Opener。

剪贴板上面有什么东西——有文本、图片。

对文字的操作

显而易见,需要先写文字在剪贴版上,才能粘贴文字。因此,代码如下

#[command]
fn read_text(app_handle: AppHandle)-> String {
    app_handle
        .clipboard()
        .write_text("start")
        .unwrap();
    app_handle
        .clipboard()
        .read_text()
        .unwrap()
}

看看write_text的函数签名

 pub fn write_text<'a, T: Into<Cow<'a, str>>>(&self, text: T) 
-> crate::Result<()>

需要传一个参数text,泛型是T,泛型约束是Into<Cow<'a, str>>

表示接受任何能转换为 Cow<'a, str> 的类型,比如传String、&str、Cow。

use std::borrow::Cow;    
app_handle.clipboard().write_text(Cow::Borrowed("start")).unwrap();

声明生命周期'a。返回Result。

看看read_text的函数签名

pub fn read_text(&self) -> crate::Result<String>

返回Result<String>。

注册通信并使用,不必细说。

对图像的操作

同理,先要有图像,然后才能粘贴图像。

前面写入文字使用的是write_text,那么粘贴图像——write_image

函数签名如下

 pub fn write_image(&self, image: &Image<'_>) -> crate::Result<()> 

传入一个&Image

前面读取文字使用的是read_text,那么粘贴图像——read_image

函数签名如下

pub fn read_image(&self) -> crate::Result<Image<'_>> 

返回一个Result,其中的类型是Image。

模仿前面,作出一些细微的修改,代码如下

#[command]
fn image(app_handle: AppHandle) -> Result<Image<'static>> {
    app_handle
        .clipboard()
        .write_image(&Image::from_path("./icons/icon.png").unwrap())
        .unwrap();
    let image=app_handle
        .clipboard()
        .read_image()
        .unwrap();
    Ok(image)
}

说起来,还从来没写过在通信函数中返回Image的,还没见过。

上面这段代码,首先需要添加feature ,即在Cargo.toml文件中

tauri= {version = "2.5.1", features = ["image-ico"] }

或者

tauri= {version = "2.5.1", features = ["image-png"] }

运行,然后报错

error[E0515]: cannot return value referencing function parameter `app_handle`
  --> src\main.rs:27:5
   |
27 |     app_handle.clipboard().read_image().unwrap()
   |     ----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |     |
   |     returns a value referencing data owned by the current function
   |     `app_handle` is borrowed here

 直接翻译,意思是返回了一个引用了当前函数所拥有数据的值,app_handle在此处被借用

所有权问题——返回值依赖于app_handle的内部数据,当函数结束时数据将被释放,但返回值却试图继续引用它们,这会导致悬垂指针(dangling pointer)问题。

因此,获取所有权,即

    app_handle
        .clipboard()
        .read_image()
        .unwrap()
        .to_owned()

还有其他类似方法。

后面注册通信函数,又报错了

关键报错如下

16 | pub struct Image<'a> {
   | -------------------- doesn't satisfy `tauri::image::Image<'_>: IpcResponse`
   |
   = note: the following trait bounds were not satisfied:
           `tauri::image::Image<'_>: IpcResponse`
           which is required by `&tauri::image::Image<'_>: tauri::ipc::private::ResponseKind`
   = note: this error originates in the macro `__cmd__image` which comes from the expansion of the macro `tauri::generate_handler` (in Nightly builds, run with -Z macro-backtrace for more info)

意思就是Image没有实现 IpcResponse 这个trait,如果为其实现这个trait

考虑一个关键的东西——孤儿规则

Image 是 tauri 中的类型,IpcResponse 是 tauri 中的 trait。

因为孤儿规则限制,除非在 Tauri 的源代码中修改,否则无法直接为 Image 实现 IpcRespons这个trait

无法直接实现。

Rust: trait 孤儿规则_rust孤儿原则-CSDN博客https://blog.csdn.net/wowotuo/article/details/88594502

rust学习二十.9、RUST绕过孤儿规则实现特质和包装器缺点 - 正在战斗中 - 博客园https://www.cnblogs.com/lzfhope/p/18807962可以创建一个包装类型(newtype)来绕过孤儿规则,笔者在后续试试,在这篇就不尝试了。

因此,返回Image可能比较困难,如果将其变成字节数组,比如to_bytes这样的方法。

笔者看到了Image结构体的定义

#[derive(Debug, Clone)]
pub struct Image<'a> {
  rgba: Cow<'a, [u8]>,
  width: u32,
  height: u32,
}

这个rgba好像就是字节数组。

因此,重新修改一下,就返回里面东西,代码如下。

#[command]
fn image(app_handle: AppHandle) -> Result<(Vec<u8>, u32, u32)> {
    app_handle
        .clipboard()
        .write_image(&Image::from_path("./icons/icon.png").unwrap())
        .unwrap();
    let image = app_handle
        .clipboard()
        .read_image()
        .unwrap();
    let rgba = image.rgba().to_owned();
    let w = image.width();
    let h = image.height();
    Ok((rgba, w, h))
}

注册通信函数并使用,结果如下

前端结果

可以发现,第一个参数是一个数组,既然如此,展示一下。

前端代码如下

import { useState } from "react";
import { invoke } from "@tauri-apps/api/core";

export default function Home() {
  const [imageUrl, setImageUrl] = useState("");

  // 定义后端返回的数据类型
  type ImageData = [number[], number, number];

  async function clicked() {
    setImageUrl("");
    try {
      const [rgba, width, height]: ImageData = await invoke("image");

      // 创建 Canvas
      const canvas = document.createElement("canvas");
      canvas.width = width;
      canvas.height = height;
      const ctx = canvas.getContext("2d");

      if (!ctx) {
        console.error("无法获取 Canvas 上下文");
        return;
      }

      // 将 RGBA 字节转换为 ImageData
      const imageData = new ImageData(new Uint8ClampedArray(rgba), width, height);
      ctx.putImageData(imageData, 0, 0);

      // 转换为 data URL
      const url = canvas.toDataURL("image/png");
      setImageUrl(url);
    } catch (error) {
      console.error("错误:", error);
    }
  }

  return (
    <div>
      <h1>Home</h1>
      <p>首页</p>
      <button onClick={clicked}>点击</button>
      <div>
        {imageUrl ? (
          <img src={imageUrl} alt="Generated Image" style={{ maxWidth: "100%" }} />
        ) : (
          <p>点击按钮加载图像</p>
        )}
      </div>
    </div>
  );
}

结果如下

没问题。

感觉逻辑不是很对,应该是复制了一张图片,然后粘贴就出现图片。

这就要在前端使用这个插件,后面再说。

对html的操作

看了对文字和图片的操作,接下来看看对html文本的操作。

对html的操作,只有一个方法,

    pub fn write_html<'a, T: Into<Cow<'a, str>>>(
        &self,
        html: T,
        alt_text: Option<T>,
    ) -> crate::Result<()> 

传两个参数,一个是html,另一个是Option,返回Result<()>。没有返回值。

代码如下

#[command]
fn html(app_handle: AppHandle){
    app_handle
        .clipboard()
        .write_html("<h1>hello start!</h1>",Some("hello start!"));
    let a=app_handle
        .clipboard()
        .read_text().unwrap();
    println!("a: {}", a);

}

或者写

.write_html("<h1>hello start!</h1>", None::<&str>)

都行,

注册通信函数并运行。结果如下

可以发现,打印的hello start!,如果是None,

什么都没有。


总结

这个插件在Rust的部分的使用就介绍到这,后续再看看在前端的使用。

逻辑有点怪,应该是复制,然后粘贴,而不是直接写入。


网站公告

今日签到

点亮在社区的每一天
去签到