使用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)进行配置。
# 后端脚本
- 在src同级目录下创建backhaul目录;
- 在目录下新建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体积增大;