使用Python+Flask开发桌面应用的新选择-flaskwebgui

前言

之前分别用过了eel, pywebview进行桌面应用开发, 但是都有不太理想的地方, eel没有对flask的原生支持, 而pywebview虽然可以直接将flask的实例对象进行传递, 用起来也挺方便, 不过在调用浏览器引擎方面有点难受, 要么用cef, 就得带进去一大堆依赖, 如果用pyinstaller打包后还会出现各种跑不起来的坑, 要么就是用IE内核, 那简直要了亲命的, 还不能指定调用外部的Chrome浏览器进行渲染, 很是尴尬…要是能有一个轮子结合eelpywebview的优点, 可以直接用flask+Chrome跑的话那就完美了, 嘿嘿~还真让我给翻出来了: flaskwebgui, 下面开始实例记录一个小demo

官网

https://github.com/ClimenteA/flaskwebgui

是个19年开始做的项目, 更新还是蛮频繁的, 感谢作者ClimenteA

pip直接安装就行

pip install flaskwebgui

效果演示

auto-py-to-exe打包后扔到纯净的Win7虚拟机中进行测试, 除了需要单独安装一下Chrome以外, 不需要任何运行环境即可直接跑起来.
在这里插入图片描述

项目文件结构

就是正常的一个flask应用的结构, 这里我用了CDN, 没有加static文件夹

.
├── main.py				# 入口程序
└── templates
    └── index.html		# 页面模板

main.py代码

from flask import Flask, render_template
from flaskwebgui import FlaskUI

# 正常实例化flask对象
app = Flask(__name__)
# 将app对象传入FlaskUI的构造函数中, 并设置要打开的窗口尺寸
ui = FlaskUI(app, width=800, height=600)


@app.route('/')
def index():
    return render_template('index.html')


if __name__ == '__main__':
	# 注意这里调用的是ui的run
    ui.run()

index.html代码

纯前端的代码, 其实没啥好贴的了, 从layui文档里面抄了一段后台模板测试下. 主要就是一个需要注意的地方, 记得设置页面的语言<html lang="zh">, 否则调用Chrome的时候会提示是否需要进行翻译, 比较讨厌.

<!DOCTYPE html>
<html lang="zh">    <!--注意设置语言, 否则调用Chrome打开会出现是否翻译的提示-->
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flaskwebgui演示程序</title>    <!--这里将会是打开的Chrome标题-->
</head>

<body>
    <div class="layui-layout layui-layout-admin">
    中间省略了, 没啥参考价值
    </div>
</body>
</html>

打包成EXE

这里还是用auto-py-to-exe, 比手工配置pyinstaller更方便, ps: 这货是eel写的

pip install auto-py-to-exe
auto-py-to-exe

注意把templates文件夹添加到打包的选项里就行
在这里插入图片描述
默认参数打包后的文件夹大小22MB多点
在这里插入图片描述
开UPX压缩试试:
在这里插入图片描述
直接干掉一半大小, 变成10MB了, 哇哈哈哈哈
在这里插入图片描述
然而…启动upx压缩后的程序报错:
在这里插入图片描述
在这里插入图片描述
本着这种问题我肯定不会是第一个发现的原则, 果然在stackoverflow上找到了病友:
https://stackoverflow.com/questions/38811966/error-when-creating-executable-file-with-pyinstaller

upx压缩的时候把VCRUNTIME140.dll给干残了, 单独设置一下不压缩这个文件, 注意上面设置的--upx-dir参数不变, 额外添加手动参数: --upx-exclude=vcruntime140.dll, 这样完整的命令:

pyinstaller --noconfirm --onedir --windowed --upx-dir "D:/tools/upx-3.96-win64" --add-data "C:/Users/Lian/Desktop/flaskgui/templates;templates/" --upx-exclude=vcruntime140.dll "C:/Users/Lian/Desktop/flaskgui/main.py"

再打包, 文件夹就大了0.1MB而已, 这次运行正常! 整个文件夹再打个zip, 只有9MB了, 好吧, 承认是我强迫症犯了…
在这里插入图片描述
进行应用分发的时候可以直接把这个压缩包+Chrome的standalone安装包扔给用户, 先装Chrome, 然后再解压执行main.exe就可以了.

结束语

这么一来的话, Chrome居然变成了运行环境RUMTIME, 不过相对于之前用.Net Framework做Winform类的桌面应用而言, 这种方式算是完美结合了WEB技术出炫酷的UI界面+Python各种轮子快速实现业务逻辑的优势, 当然缺点嘛, 可能就是第一次启动程序调用Chrome的时候会有点略慢, 不过关掉后再启动就挺快了.

BUG处理

这貌似是当前版本存在的bug, 就是在关闭浏览器之后, 后台的flask进程并没有自动退出, 看了下GitHub上的issue, 果然我又不是第一个哈哈哈哈:

https://github.com/ClimenteA/flaskwebgui/issues/58

根据热心网友提出的方案, 改一下flaskwebgui.py源代码313行的while True:开始的内容, 加一层try except来捕获异常强制退出flask就行了, 修改后的这部分代码:


        while True:
            try:	# 增加的内容
                gui_running = psutil.Process(
                    self.BROWSER_PROCESS.pid).is_running()
                gui_memory_usage = psutil.Process(
                    self.BROWSER_PROCESS.pid).memory_percent()

                if (
                    gui_running == False
                    or
                    gui_memory_usage == 0
                ):
                    break

                time.sleep(5)
            except Exception as e:	# 增加的内容
                logging.info('Forced Shutdown Browser Closed')
                break

        if isfunction(self.on_exit):
            logging.info(f"Executing {self.on_exit.__name__} function...")
            self.on_exit()
        else:
            logging.info("No 'on_exit' function provided.")

        logging.info("Closing connections...")
        FlaskUI.kill_pids_by_ports(self.port)

估计后续的更新可能会修复这个bug吧, 可以跟进一下这个项目了, 真的很不错!

更新: 使用pyinstaller打包为单个EXE文件需要注意的问题

又尝试了一下在auto-py-to-exe中打包成单个可执行文件, 对于额外引入的templates文件夹, 需要在主程序中修改一下访问路径的逻辑, 不能直接用默认的, 看代码吧:

import os
import sys
from flask import Flask, render_template
from flaskwebgui import FlaskUI

# 使用pyinstaller打包成单文件需要处理路径问题
app_path = ''
if hasattr(sys, '_MEIPASS'):	# 如果是单个EXE文件执行的时候sys中会存在这个_MEIPASS变量作为当前的工作根路径
    app_path = os.path.join(sys._MEIPASS)

# 拼接一下获得模板文件夹所在的绝对路径, 这样写不会影响打包前对源代码的调试
template_folder = os.path.join(app_path, 'templates')

# 实例化Flask对象的时候就得指定一下template_folder的位置了, static文件夹同理.
app = Flask(__name__, template_folder=template_folder)

附上打包成单个EXE文件后的运行效果, 只有9MB哦! 估计是用CDN的原因, 启动的前两秒空白等待了一会儿, 还是把静态资源打包到程序里面比较好.
在这里插入图片描述

Electron 是一个基于 Node.js 和 Chromium 的桌面应用程序开发框架,可以使用 HTML、CSS 和 JavaScript 来构建跨平台的桌面应用程序。在本文中,我们将介绍如何使用 PythonFlask 结合 Electron 编写一个简单的桌面应用程序。 1. 安装 Electron 首先,我们需要安装 Electron。可以通过 npm 来安装它: ``` npm install electron --save-dev ``` 2. 创建 Electron 应用程序 接下来,我们需要创建一个基本的 Electron 应用程序。在项目根目录下创建一个名为 main.js 的文件,并添加以下代码: ``` const { app, BrowserWindow } = require('electron') const path = require('path') function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true } }) win.loadFile('index.html') win.webContents.openDevTools() } app.whenReady().then(() => { createWindow() app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow() } }) }) app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() } }) ``` 这段代码创建了一个基本的窗口,加载了一个名为 index.html 的文件,并打开了开发者工具。 3. 创建 Flask 应用程序 然后,我们需要创建一个 Flask 应用程序。在项目根目录下创建一个名为 server.py 的文件,并添加以下代码: ``` from flask import Flask, jsonify app = Flask(__name__) @app.route('/api') def api(): return jsonify({'message': 'Hello, World!'}) if __name__ == '__main__': app.run(debug=True) ``` 这段代码创建了一个简单的 Flask 应用程序,具有一个名为 /api 的路由,返回了一个 JSON 响应。 4. 集成 Flask 应用程序 现在,我们需要将 Flask 应用程序集成到 Electron 应用程序中。在 main.js 文件中添加以下代码: ``` const { app, BrowserWindow } = require('electron') const path = require('path') const { spawn } = require('child_process') let flaskProcess = null const flaskPath = path.join(__dirname, 'server.py') function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true } }) win.loadFile('index.html') win.webContents.openDevTools() } app.whenReady().then(() => { flaskProcess = spawn('python', [flaskPath]) createWindow() app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow() } }) }) app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() } }) app.on('before-quit', () => { flaskProcess.kill() }) ``` 这段代码启动了一个 Python 进程来运行 Flask 应用程序,并在应用程序关闭之前杀死该进程。 5. 发起请求 最后,我们需要在渲染进程中发起请求。在项目根目录下创建一个名为 index.html 的文件,并添加以下代码: ``` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Hello, World!</title> </head> <body> <h1 id="message"></h1> <script> const { ipcRenderer } = require('electron') ipcRenderer.on('message', (event, message) => { document.getElementById('message').textContent = message }) fetch('http://localhost:5000/api') .then(response => response.json()) .then(data => { ipcRenderer.send('message', data.message) }) .catch(error => console.error(error)) </script> </body> </html> ``` 这段代码使用 IPC 通信来从 Python 进程接收消息,并使用 fetch API 发起一个请求来获取 Flask 应用程序的响应。 6. 运行应用程序 现在,我们可以通过运行以下命令来启动应用程序: ``` npm start ``` 这将同时启动 Electron 应用程序和 Flask 应用程序,并打开一个窗口,显示来自 Flask 应用程序的消息。 至此,我们已经成功地构建了一个使用 PythonFlask 和 Electron 框架的桌面应用程序。
评论 41
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DexterLien

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值