Electron + Django(第 1 部分),桌面应用集成 JavaScript & Python
简介与概念 从 Electron 和 Django 构建桌面应用程序实际上是构建一个 Web 应用程序,它使用 Electron 作为本地浏览器的前端,Django 作为后端。 那么,为什么我们需要构建桌面应用而不是 Web 应用呢?🤔 因为: 桌面应用可以离线运行 桌面应用可以访问客户端PC底层API(例如文件系统📁) POC(概念验证) 我们将使用 Electron、TypeScript、
简介与概念
从 Electron 和 Django 构建桌面应用程序实际上是构建一个 Web 应用程序,它使用 Electron 作为本地浏览器的前端,Django 作为后端。
那么,为什么我们需要构建桌面应用而不是 Web 应用呢?🤔
因为:
-
桌面应用可以离线运行
-
桌面应用可以访问客户端PC底层API(例如文件系统📁)
POC(概念验证)
我们将使用 Electron、TypeScript、Webpack 和 Django 构建一个简单的桌面应用程序。
如上图所示,
-
用户首先输入一个文本。
-
输入被传递到 Django Web 服务器并返回输出,输出结合了输入、日期和时间以及其他一些文本。
-
最后会在窗口中显示输出
0.先决条件
假设您已经安装:
-
节点js,v14.19.1
-
蟒蛇,3.9.10
-
虚拟环境,20.13.1
1.设置电子
我们将使用 Electron ForgeTypeScript + Webpack模板来创建我们的桌面应用程序。
- 在命令窗口中运行以下命令:
npx create-electron-app edtwExample --templateu003dtypescript-webpack
我们将示例命名为edtw
,whick 代表 Electron、Django、TypScript、Webpack
- 运行命令后,您应该看到命令窗口的输出:
文件结构:
- 在
edtwExample
文件夹内运行npm run start
,应该会弹出如下窗口
2.设置 Django
- 在
edtwExample
文件夹中创建一个名为python
的文件夹
mkdir python
光盘蟒蛇
- 创建虚拟环境并激活
virtualenv edtwExampleEnv
edtwExampleEnv\Scripts 激活
- 安装Django和Django REST框架(附版本)
pip install djangou003du003d4.0.3 djangorestframeworku003du003d3.13.1
- 启动Django项目
django-admin startproject edtw示例
这是结果文件结构:image.png
- 通过以下命令运行 Django 应用
python manage.py 运行服务器
- 在浏览器中打开 127.0.0.1:8000 应该会看到以下内容:
3.电子启动时启动 Django 应用程序(使用 spawn)
为此,我们在index.ts
中创建了一个startDjangoServer
方法,该方法使用spawn
运行 djangorunserver
命令
import { spawn } from 'child_process';
const startDjangoServer = () =>
{
const djangoBackend = spawn(`python\\edtwExampleEnv\\Scripts\\python.exe`,
['python\\edtwExample\\manage.py', 'runserver', '--noreload']);
djangoBackend.stdout.on('data', data =>
{
console.log(`stdout:\n${data}`);
});
djangoBackend.stderr.on('data', data =>
{
console.log(`stderr: ${data}`);
});
djangoBackend.on('error', (error) =>
{
console.log(`error: ${error.message}`);
});
djangoBackend.on('close', (code) =>
{
console.log(`child process exited with code ${code}`);
});
djangoBackend.on('message', (message) =>
{
console.log(`message:\n${message}`);
});
return djangoBackend;
}
以下脚本调用 cmd 以使用带有参数['python\\edtwExample\\manage.py', 'runserver', '--noreload']
的命令python\edtwExampleEnv\Scripts\python.exe
运行新进程。
const djangoBackend = spawn(`python\\edtwExampleEnv\\Scripts\\python.exe`,
['python\\edtwExample\\manage.py', 'runserver', '--noreload']);
以下脚本记录 django 进程的输出
djangoBackend.stdout.on('data', data =>
{
log.info(`stdout:\n${data}`);
});
djangoBackend.stderr.on('data', data =>
{
log.error(`stderr: ${data}`);
});
djangoBackend.on('error', (error) =>
{
log.error(`error: ${error.message}`);
});
djangoBackend.on('close', (code) =>
{
log.info(`child process exited with code ${code}`);
});
djangoBackend.on('message', (message) =>
{
log.info(`stdout:\n${message}`);
});
我们在createWindow
方法中调用startDjangoServer
方法。
const createWindow = (): void => {
startDjangoServer();
// Create the browser window.
const mainWindow = new BrowserWindow({
height: 600,
width: 800,
});
// and load the index.html of the app.
mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
// Open the DevTools.
mainWindow.webContents.openDevTools();
};
-
运行
npm run start
,打开任务管理器,应该可以看到python进程了 -
如果关闭app窗口,python进程会停止
注意事项
必须包含['python\\edtwExample\\manage.py', 'runserver', '--noreload']
中的参数--noreload
以防止 django 应用程序启动两次。
如果省略--noreload
,您将在后台运行 4 个 python 实例。
即使你关闭了应用程序窗口,还剩下 2 个 python 实例,你仍然可以访问 django 站点。
4.在 Django 中构造 API 方法
- 在
INSTALLED_APPS
中添加rest_framework
在settings.py
- 在命令窗口中运行以下命令来创建一个名为
edtwExampleAPI
的应用程序
python manage.py startapp edtwExampleAPI
您应该看到以下文件结构:
- 在
edtwExample\urls.py
中添加path('', include('edtwExampleAPI.urls')),
- 在文件夹
edtwExampleAPI
下创建urls.py
并粘贴以下内容
from django.urls import include, path
from rest_framework import routers
from . import views
router = routers.DefaultRouter()
router.register( 'edtwExampleAPI', views.EdtwViewSet, basename='edtwExampleAPI' )
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
path('', include(router.urls)),
]
- 在
views.py
中,复制粘贴以下内容
from datetime import datetime
from rest_framework import viewsets
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.response import Response
class EdtwViewSet(viewsets.ViewSet):
# Create your views here.
@action(methods=['GET'], detail=False, name='Get Value from input' )
def get_val_from( self, request ):
input = request.GET[ 'input' ]
return Response( status=status.HTTP_200_OK,
data=f"[{ datetime.now() }] input= { input }, value from Django" )
- 重新启动 Django web 服务器并转到
http://127.0.0.1:8000/edtwExampleAPI/get_val_from/?input=This+is+an+input+text
。
将显示以下内容:
5.从 Electron 调用 Django API
- 将以下代码复制粘贴到
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello World!</title>
</head>
<body>
<h1>💖 Hello World!</h1>
<p>Welcome to your Electron application.</p>
<input id="input_text" type="text"></body>
<button id="btn_get_val_from_django" >Get Value From Django</button>
<h2>Output</h2>
<p id="p_output"></p>
</body>
</html>
- 将以下代码复制粘贴到
renderer.ts
import axios from 'axios';
import './index.css';
const btnGetValFromDjango = document.getElementById('btn_get_val_from_django');
btnGetValFromDjango.onclick = async () => {
const res = await axios.get("http://127.0.0.1:8000/edtwExampleAPI/get_val_from/", { params: {
input: ( document.getElementById('input_text') as HTMLInputElement ).value
} });
const result = res.data;
document.getElementById('p_output').innerHTML = result;
};
console.log('👋 This message is being logged by "renderer.js", included via webpack');
- 我们的逻辑完成了。在这里,将出现2个错误。别担心,我会告诉你如何解决它。 😊
如果您测试该应用程序,它将显示以下错误。
上述错误是由于内容安全策略造成的。我们将通过在package.json
中添加devContentSecurityPolicy
并重新启动应用程序来修复它。 (有关更多信息,请参阅这个。)
"@electron-forge/plugin-webpack",
{
"mainConfig": "./webpack.main.config.js",
"devContentSecurityPolicy": "connect-src 'self' http://127.0.0.1:8000 'unsafe-eval'",
"renderer": {
"config": "./webpack.renderer.config.js",
"entryPoints": [
{
"html": "./src/index.html",
"js": "./src/renderer.ts",
"name": "main_window"
}
]
}
}
- 之后再尝试应用,又会报错。
这是由于共同的 CORS 政策。我们在这里选择中引入的修复。
这个概念是在浏览器检查来源之前替换标题。
在index.ts
中添加如下方法
const UpsertKeyValue = (obj : any, keyToChange : string, value : string[]) => {
const keyToChangeLower = keyToChange.toLowerCase();
for (const key of Object.keys(obj)) {
if (key.toLowerCase() === keyToChangeLower) {
// Reassign old key
obj[key] = value;
// Done
return;
}
}
// Insert at end instead
obj[keyToChange] = value;
}
在index.ts
中改变createWindow
方法如下
const createWindow = (): void => {
startDjangoServer();
// Create the browser window.
const mainWindow = new BrowserWindow({
height: 600,
width: 800,
});
mainWindow.webContents.session.webRequest.onBeforeSendHeaders(
(details, callback) => {
const { requestHeaders } = details;
UpsertKeyValue(requestHeaders, 'Access-Control-Allow-Origin', ['*']);
callback({ requestHeaders });
},
);
mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => {
const { responseHeaders } = details;
UpsertKeyValue(responseHeaders, 'Access-Control-Allow-Origin', ['*']);
UpsertKeyValue(responseHeaders, 'Access-Control-Allow-Headers', ['*']);
callback({
responseHeaders,
});
});
// and load the index.html of the app.
mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
// Open the DevTools.
mainWindow.webContents.openDevTools();
};
- 重新启动应用程序并完成! 🎉👏
源码
您可以查看源代码以获取更多信息。
使用 Electron 和 Django 构建桌面应用程序的原因
最初,我想构建一个桌面应用程序并专注于使用 Python 构建后端或业务逻辑,因此我首先搜索 Python 的桌面框架。然而,他们要么
-
不是很人性化
-
缺少好看的UI框架
-
不是免费的
由于以上三个原因,我预计如果我选择它们可能需要花费大量时间来开发。 (这是一个很好的参考。)
作为一名网络开发人员,我问自己,我可以使用我已经知道的东西(例如 JavaScript 和 Python)来构建桌面应用程序吗?
这就是电子进入我视线的原因。
-
Electron + Django 是一个很好的方法,如果
-
您已经有一个使用 Django 作为后端并使用 JavaScript 作为前端的 Web 应用程序,并且您希望将其转换为桌面应用程序
-
你想在 Django 上更出色,并使用你最喜欢的前端库(例如 React、Angular 或 vue 等)开发前端
-
如果
-
您从无到有构建桌面应用程序。 (Electron + Django 需要相对更多的时间来设置,并且您需要维护 2 种编程语言)。
对于这种情况,我建议将 Electron 本身与 TypeScript 结合使用,因为已经存在模板来处理这个问题。此外,Electron 本身可以访问 PC 低级 API 以满足您的需求
下一篇
Electron + Django(第 2 部分),打包到生产环境
更多推荐
所有评论(0)