在TypeScript中引入QtWebChannel.js

如何在TypeScript中引入,Qt框架的webchannel:

我的工程环境为:Vite React TypeScript

第一步:引入 qtwebchannel.js,并将你的qtwebchannel.js放在public下,假如是将public作为资源目录

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/x-icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- <meta
      http-equiv="Content-Security-Policy"
      content="script-src 'self' 'unsafe-inline';"
    /> -->
    <title>SCAN NEO</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="/qwebchannel.js"></script>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

第二步:定义全局 types,并且导出

declare namespace QWebChannel {
  export interface MessageTypes {
    signal: 1;
    propertyUpdate: 2;
    init: 3;
    idle: 4;
    debug: 5;
    invokeMethod: 6;
    connectToSignal: 7;
    disconnectFromSignal: 8;
    setProperty: 9;
    response: 10;
  }

  export interface Transport {
    send(data: any): void;
    onmessage?: (message: { data: any }) => void;
  }

  export class QObject {
    constructor(name: string, data: any, webChannel: QWebChannel);
    __id__?: string;
    __objectSignals__?: { [key: string]: Function[] };
    __propertyCache__?: { [key: string]: any };
    unwrapQObject?(response: any): any;
    unwrapProperties?(): void;
    propertyUpdate?(signals: any, propertyMap: any): void;
    signalEmitted?(signalName: string, signalArgs: any[]): void;
  }
}

declare class QWebChannel {
  constructor(
    transport: QWebChannel.Transport,
    initCallback?: (channel: QWebChannel) => void,
  );
  send(data: any): void;
  exec(data: any, callback?: (data: any) => void): void;
  objects: { [key: string]: QWebChannel.QObject };
  debug: boolean;
  static init(transport: QWebChannel.Transport): Promise<QWebChannel>;
}

// 扩展全局 Window 接口
declare global {
  interface Window {
    userClick?: (userID?: string, groupID?: string) => void;
    editRevoke?: (clientMsgID: string) => void;
    screenshotPreview?: (results: string) => void;

    // Qt WebChannel 相关
    // qt 对象
    qt: {
      webChannelTransport: QWebChannel.Transport; // qt传递的对象
    };
    coreRef?: IQWebChannelCore; // 回调函数中传递的对象

    // qtwebchannel.js 没有 types 结构,自行定义
    // 直接挂载在 window 上的 QWebChannel
    QWebChannel: typeof QWebChannel; // qt webchannel 核心
    QObject?: typeof QWebChannel.QObject; // qt webchannel 基本对象结构
    QWebChannelMessageTypes?: QWebChannel.MessageTypes; // qt webchannel 消息类型
  }
}

第三步:关于初始化组件,在顶级路由增加逻辑勾子,此组件将使用在路由配置当中

import { Outlet } from "react-router-dom";

import { useQtWebChannel } from "@/hooks/useQtWebChannel";

export const MainContentWrap = () => {
  useQtWebChannel();
  return <Outlet />;
};

第四步:完成勾子逻辑

import { useEffect } from "react";

import { useQtStore } from "@/store/qtStore";
import { QtGroupItem, QtStore } from "@/store/type";

export function useQtWebChannel() {
  // store 更新 qt 消息部分实现
  const { updateGroupItems, setLoading, setError } = useQtStore((state: any) => ({
    updateGroupItems: state.updateGroupItems,
    setLoading: state.setLoading,
    setError: state.setError,
  }));

  useEffect(() => {
    // qt初始化实现
    // qt开启9222端口,启用webview调试
    const initQtWebChannel = () => {
      if (typeof window !== "undefined" && !window.qt) {
        setError("Qt 对象未获取到!");
        return;
      }
      setLoading(true);

      try {
        // 确保 QWebChannel 可用
        if (!window.QWebChannel && !window.qt.webChannelTransport) {
          throw new Error("Qt WebChannel 初始化失败");
        }

        // 初始化 WebChannel
        new window.QWebChannel(window.qt.webChannelTransport, (channel: any) => {
          // 获取 Qt 注册的对象
          window.coreRef = channel.objects.QtInitObject;

          // 连接各种信号
          if (window.coreRef) {
            // 这里与 Qt 端核对 函数功能,在window.coreRef可以展示所有qt注册函数
            console.log(window.coreRef)
            // 在这里调用你的 updateGroupItems ;更新store逻辑
            // 与Qt通信在这里初始化
            window.coreRef.XXX((QtMsg) => {
                // 获取qt消息
                console.log(QtMsg)
            })

            // 触发qt消息,类似于激活websocket客户端,建立双向通信
            setInterval(() => {
                // 心跳函数
                window.coreRef.SendSinglToQt()...
            }, 500)
          }
        });
      } catch (error) {
        setError(error instanceof Error ? error.message : "Qt WebChannel 初始化失败");
        console.error("QtWebChannel 初始化错误:", error);
      } finally {
        setLoading(false);
      }
    };

    // 检查是否在浏览器环境中
    if (typeof window !== "undefined") {
      // 确保 DOM 已加载
      if (document.readyState === "complete") {
        initQtWebChannel();
      } else {
        window.addEventListener("load", initQtWebChannel);
        return () => window.removeEventListener("load", initQtWebChannel);
      }
    }

    // 清理函数
    return () => {
      // 清空qt资源
      window.qt.webChannelTransport = null;
      // 如果有需要断开连接信号,可以在这里处理
      window.coreRef.XXX(()=>{
         // 断开qt链接
      })
    };
  }, [setLoading, setError]);

  // 调用 Qt 方法的通用函数
  const callQtMethod = (methodName: string, ...args: any[]) => {
    return new Promise((resolve, reject) => {
      if (!window.qt || !window.qt.webChannelTransport) {
        reject(new Error("Qt WebChannel not initialized"));
        return;
      }

      try {
        // Qt 对象是全局的
        const qtObject = window.coreRef; // 使用我们初始化的 core 对象
        if (!qtObject || !qtObject[methodName]) {
          reject(new Error(`Qt method "${methodName}" not found`));
          return;
        }

        // 调用 Qt 方法
        qtObject[methodName](...args, (result: any) => {
          resolve(result);
        });
      } catch (error) {
        reject(error);
      }
    });
  };

  return {
    isLoading: useQtStore((state) => state.isLoading),
    error: useQtStore((state) => state.error),
  };
}

第五步:路由中引入组件,触发initWebChannel初始化函数

import { createHashRouter, Navigate } from "react-router-dom";

import { MainContentLayout } from "@/layout/MainContentLayout";
import { MainContentWrap } from "@/layout/MainContentWrap";

import GlobalErrorElement from "./GlobalErrorElement";

const router = createHashRouter([
  {
    path: "/",
    element: <MainContentWrap />,
    errorElement: <GlobalErrorElement />,
    children: [
      {
        element: <MainContentLayout />,
        children: [
          {
            async lazy() {
              const { Xxx } = await import("@/pages/xxx");
              return { Component: Xxx };
            },
            index: true,
          },
          ... // 你其余的路由
        ],
      },
    ],
  },
]);

export default router;

End

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值