使用Quasar结合python进行跨端应用开发

2024/1/3 QuasarElectron桌面开发

python语言作为一门使用非常广泛的语言,在客户端的开发上面有基本的tk,还有pyQT等,这些工具都比较重,在多平台支持上或多或少存在不足。现在web技术发展日新月异,Electron可以基于web技术开发出各种桌面应用,如VSCode等。python也有类似的库提供基于web的界面开发,比如nicegui。网上也有大神使用electron结合thrift、zerorpc,使用python作为后端进行界面开发。

本文基于Quasar框架进行桌面端开发,使用electron启动python restfull子进程,由python实现具体业务逻辑。

# 框架流程

start
 |
 V
+--------------------+
|                    | start
| quasar electron    +-------------> +------------------+
|                    | sub process   |                  |
| (browser)          |               | python server    |
|                    |               |                  |
| (all html/css/js)  |               | (business logic) |
|                    |   restfull    |                  |
| (node.js runtime,  | <-----------> | (restfull)       |
|  axio)             | communication |                  |
|                    |               |                  |
+--------------------+               +------------------+

本文主要介绍基于electron的端开发,quasar项目的初始化与electron编译可参考官方文档 (opens new window)进行配置。

# 后端脚本

  1. 在src同级目录下创建backhaul目录;
  2. 在目录下新建api.py文件,输入如下内容;
import tornado.ioloop
import pyrestful.rest

from pyrestful import mediatypes
from pyrestful.rest import get

class EchoService(pyrestful.rest.RestHandler):
     @get(_path="/echo/{name}", _produces=mediatypes.APPLICATION_JSON)
     def sayHello(self, name):
          return {"Hello":name}

if __name__ == '__main__':
     try:
          print("Start the echo service")
          app = pyrestful.rest.RestService([EchoService])
          app.listen(58997)
          tornado.ioloop.IOLoop.instance().start()
     except KeyboardInterrupt:
          print("\nStop the echo service")

需要安装tornado与pyrestful两个依赖库

# 前端适配

# 启动后端

在"electron-main.js"中使用child_process启动后端进程,参考代码如下:

* import { app, BrowserWindow } from "electron"; *
import path from "path";
* import os from "os"; *

// needed in case process is undefined under Linux
* const platform = process.platform || os.platform(); *

* process.env["ELECTRON_DISABLE_SECURITY_WARNINGS"] = "true"; *

* let mainWindow; *
let pyProc = null;
let pyPort = null;

// 启动后端进程
const createPyProc = () => {
  let port = "58997";

  if (process.env.DEV) {
    // if on DEV or Production with debug enabled
    let script = path.join(__dirname, "../..", "backhaul", "api.py");
    console.log("script path: " + script);
    pyProc = require("child_process").spawn("python", [script, "-P", port]);
  } else {
    // we're on production; no access to devtools pls
    let script = path.join(
      __dirname + ".unpacked",
      process.env.QUASAR_PUBLIC_FOLDER,
      "api",
      "api"
    );
    console.log("script path: " + script);
    pyProc = require("child_process").spawn(script, ["-P", port]);
  }

  if (pyProc != null) {
    console.log("child process success");
  }
  console.log("Spawned child pid: " + pyProc.pid);
};

const exitPyProc = () => {
  pyProc.kill();
  pyProc = null;
  pyPort = null;
};

// 原本的代码这里省略

// 注册调用开启后端和关闭机制
app.on("ready", createPyProc);
app.on("will-quit", exitPyProc);

主要是在原文件中加入createPyProc和exitPyProc并注册调用时机。

启动app时会根据当前的运行环境决定运行编译打包文件还是源文件,此处在打包发布章详细介绍。

# 添加axios与API

  • 参考官方文档 (opens new window)添加axios;
  • 设置axios的baseURL与上一步的一致,如http://127.0.0.1:58997
  • 在src文件夹中添加api目录,新增backhaul.js文件,参考如下:
import { api } from "boot/axios";

export function echo(name = "test") {
  let url = "/echo/" + name;
  return api.get(url);
}

# 与后端通讯实例

本例以src/layouts/MainView.vue页面为例,是在app启动时访问后端echo接口。

  • 按照下述代码引入相关依赖
import { ref, onBeforeMount, onMounted } from "vue";
import { echo } from "../api/backhaul";

  • 在setup中添加如下代码
    const $q = useQuasar();

    function call_backhaul() {
      echo("isfront")
        .then((resp) => {
          console.log("echo with backhaul: ", resp);
        })
        .catch(() => {
          $q.notify({
            color: "negative",
            position: "center",
            message: "后端连接错误",
            icon: "report_problem",
          });
        });
    }

    onMounted(() => {
      call_backhaul();
    });
  • 使用yarn dev -m electron启动测试环境,页面不会弹出错误提示,在开发者工具上可以看到控制台打印出接口输出。

# 打包

  • 在quasar.config.js中修改electron中的bundler为“builder”;
  • 在quasar.config.js中electron -> bundler项目中添加asarUnpack字段,值为["api"];
  • 在package.json文件scripts中添加如下编译选项:
    "build-backhaul": "pyinstaller ./backhaul/api.py --clean --add-data ./backhaul/resource/:./resource --distpath ./public --noconfirm",
    "desktop:dev": "quasar dev -m electron",
    "desktop:publish:debug": "yarn build-backhaul && quasar build -m electron -d",
    "desktop:publish": "yarn build-backhaul && quasar build -m electron"
  • builder 默认会将资源打包为asar格式文件,需要在配置中设置unpack,否则打包时无法执行文件;
  • 基于上述原因,在生产环节的启动路径添加了".unpacked";
  • Quasar 打包会默认将public中的文件放入资源文件夹,所以python打包的目标路径设置为public,生成的最终在public/api路径下;
  • python打包建议使用纯净的虚拟环境,否则pyinstaller可能会把不需要的包打包,造成app体积增大;

# 参考