基于Electron,Python,从零开始打造一款免费的PDF桌面工具
效果展示
动机
一直在使用 Python
的命令行操作PDF文件,想有一个 GUI
界面,这样操作更方便,也便于其他不懂技术的同事使用。
现在市面上完全免费的PDF工具较少,要么收费,要么功能有诸多限制,在线的pdf工具也是一样的情况,有的在线是免费的,但是有些文档不便于上传到其他的服务器(安全问题)
主要是想学习相关的技术,关于 Electron, Vue, Python
等技术。
功能
- PDF合并
- PDF分割
- PDF提取图片
- PDF提取文本
- PDF转图片
- PDF加密
- PDF解密
- PDF添加水印
- PDF删除页面
- PDF重排
- PDF压缩
- 图片转PDF
技术选型
前端GUI页面主要使用 Electron
+ Vue3
实现,前端和后端的通信主要使用 Node
中的 child_process
实现, 前端页面的通信使用 IPC
实现
主要使用技术有
-
nodejs
-
electron
-
vue
-
vue-router
-
pinia
-
naiveui
-
python
-
pymupdf
-
nuitkia 打包工具
IPC通信
在 Electron
中为了安全问题,不可以直接在渲染进程中调用 Nodejs
中的相关操作。 默认情况下,渲染器进程没有权限访问 Node.js 和 Electron 模块。 作为应用开发者,您需要使用 contextBridge
API 来选择要从预加载脚本中暴露哪些 API。
例:通过系统的默认应用打开浏览PDF文件
渲染器进程到主进程(单向)
单向通信只是渲染进程发消息到主进程,不需要主进程的返回信息。
- 在主线程中通过
ipcMain.on
监听事件
index.ts 主进程中加载脚本,设置事件监听
import { app, dialog, ipcMain, shell } from 'electron';
function createWindow () {
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
mainWindow.loadFile('index.html')
}
const openLocalPath = async (path:string) => {
shell.openPath(path);
};
app.whenReady().then(() => {
ipcMain.on('shell:openPath', openLocalPath)
createWindow()
})
- 通过预加载脚本暴露
ipcRenderer.send
要将消息发送到上面创建的监听器,您可以使用 ipcRenderer.send
API。在您的预加载脚本中添加以下代码,向渲染器进程暴露一个全局的 window.electronAPI
变量。
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
openLocalPath: (path) => ipcRenderer.send('openLocalPath', path)
})
- 在渲染进程中调用
const setButton = document.getElementById('btn')
setButton.addEventListener('click', () => {
window.electronAPI.openLocalPath(path)
})
例:打开文件夹获取里面的文件
渲染器进程到主进程(双向)
- 在主线程中通过
ipcMain.handle
监听事件
const openDirectory = async (): Promise<string> => {
const { canceled, filePaths } = await dialog.showOpenDialog()
if (!canceled) {
return filePaths[0]
}
};
ipcMain.handle(IPC_EVENT.EVENT_DIALOG_OPENFILE, async () => openDirectory(type));
- 通过预加载脚本暴露
ipcRenderer.invoke
contextBridge.exposeInMainWorld('electronAPI', {
openDirectory: async (): Promise<string> => ipcRenderer.invoke(IPC_EVENT.EVENT_DIALOG_OPENFILE)
})
- 在渲染进程中调用
const handleOpenFile = async () => {
const res = await window.electronAPI.openDirectory()
// 获取打开的文件夹路径
}
例:主进程的错误信息发送给渲染进程,通过界面显示出来
主进程到渲染器进程
将消息从主进程发送到渲染器进程时,消息需要通过其 WebContents
实例发送到渲染器进程。 此 WebContents
实例包含一个 send
方法,其使用方式与 ipcRenderer.send
相同。
- 在主线程中通过
mainWindow.webContents.send
发送事件
// 发送主进程的错误信息给渲染进程
mainWindow.webContents.send(IPC_EVENT.EVENT_PROCESS_ERROR, result)
- 通过预加载脚本暴露
ipcRenderer.on
export const listenError = (callback: (e: IpcRendererEvent, result: ProcessResult) => void) =>
ipcRenderer.on(IPC_EVENT.EVENT_PROCESS_ERROR, callback);
- 在渲染进程中设置监听
onBeforeMount(() => {
listenSuccess((__: IpcRendererEvent, _: ProcessResult) => {
store.updateLoading(false);
notification.success({ duration: 1500, content: '操作成功' });
});
})
后端 和 前端 通信
通信的格式主要使用 json
字符串,通过 Nodejs
中的 child_process
调用 命令行,监听命令行的控制台的输出信息
import { spawn } from 'child_process';
//获取命令行的路径
const resourceUrl = join(dirname(app.getPath('exe')), '/resources/toolkit/');
//调用命令,传递相关的参数
child = spawn('toolkit', [cmd, config_json], { cwd: resourceUrl });
//设置监听
child.stdout.on('data', (data) => {
// 处理返回的数据
})
child.stderr.on('data', (data) => {
// 错误信息
})
child.on('exit', (code) => {
// 退出信息
})
在 python
中主要向控制台输出信息
def process_done(cmd):
print(json.dumps({'cmd': cmd, 'status': 'done'}))
打包可执行文件
主要是使用 nuitka
工具把 Python
文件打包成一个可执行文件
nuitka --standalone --output-dir=static toolkit.pyt
自动更新
软件的自动更新主要使用 electron-updater
,主要的逻辑代码,在打包的配置文件中设置自己的更新服务器,将打包之后的文件放在自己的服务器中。
publish: [
{
provider: 'generic',
url: 'https://www.examle.com/apps/pdf-toolkit',
},
],
自动更新主要的监听事件
export const initUpdate = (win: BrowserWindow) => {
autoUpdater.autoDownload = false;
autoUpdater.autoInstallOnAppQuit = false;
// 主进程监听检查更新事件
ipcMain.on(IPC_EVENT.EVENT_UPDATE_CHECKFORUPDATE, () => {
autoUpdater.checkForUpdates();
});
// 主进程监听开始下载事件
ipcMain.on(IPC_EVENT.EVENT_UPDATE_DOWNLOADUPDATE, () => {
autoUpdater.downloadUpdate();
});
// 检测到有可用的更新
autoUpdater.on(IPC_EVENT.EVENT_UPDATE_UPDATEAVAILABLE, (info: UpdateInfo) => {
win.webContents.send(IPC_EVENT.EVENT_UPDATE_UPDATEAVAILABLE, info);
});
// 下载更新进度
autoUpdater.on(IPC_EVENT.EVENT_UPDATE_DOWNLOADPROGRESS, (progressObj: ProgressInfo) => {
win.webContents.send(IPC_EVENT.EVENT_UPDATE_DOWNLOADPROGRESS, progressObj);
});
// 下载完成并安装
autoUpdater.on(IPC_EVENT.EVENT_UPDATE_UPDATEDOWNLOADED, () => {
autoUpdater.quitAndInstall();
win.webContents.send(IPC_EVENT.EVENT_UPDATE_UPDATEDOWNLOADED);
});
};
软件下载
待后续功能完善之后,会开放源码,敬请期待。
可以自行下载试用,还没有经过完整测试,有任何问题欢迎反馈交流。
PdfToolkit 提取码:
链接: pan.baidu.com/s/1aQ2Ztj-q… 提取码: 3j2y
转载自:https://juejin.cn/post/7259633069460914236