Node打开选择文件夹弹框
前言
用脚手架的那套东西写了一个工具,但是想要一个用Node去打开选择文件夹弹框的效果,来设置操作根目录。但是,Node本身没有这个API。
node执行python脚本
Node本身没有提供打开选择文件夹弹框的API,但是Python的tkinter
是有这个功能的。所以可以用Python写好脚本来打开选择文件夹,然后通过Node来执行python脚本。
Python脚本也是非常的简单。
1 2 3 4 5 6 7 8 9
| import tkinter as tk import tkinter.filedialog
dirPaths = tkinter.filedialog.askdirectory()
if(len(dirPaths) == 0): print('None') else: print(dirPaths)
|
Node执行Python脚本需要通过Node提供的child_process
来创建子进程(exec
),它会将紫禁城的输入以回调函数参数的形式一次性返回。
ESM里使用__dirname
因为我用的是ESM
模式写的,所以是不能直接使用__dirname
的。这里稍微吹一下下ChatGPT。
启用ESM
模式则是在package.json
中,添加type: "module"
当然,答案有点小瑕疵,实际上得到的是当前文件的绝对地址,并且前面会有文件协议。所以需要进行一些处理。


代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { exec } from 'child_process'; import { EOL } from 'os'; import path from 'path';
const dirname = import.meta.url.slice(8, import.meta.url.lastIndexOf('/'));
const p = new Promise((resolve, reject) => { exec(path.join(dirname, 'dialog.py'), (err, stdout, stderr) => { if (err) { reject(new Error(err)); } else if (stderr) { reject(new Error(stderr)); } else if (stdout) { const result = stdout.trim().split(EOL).toString(); resolve(result); } }) });
p.then(val => { console.log(val); })
|

中文路径问题
python
输出中文是会乱码的,所以当我们选择的路径有中文的话,就会出现问题。

在网上找到一些解决方案说是改环境变量。但是,本人想要的效果是只需要下载工具,就能直接使用。而不需要手动修改。所以最好的方案还是在代码上做文章。
最后,功夫不负有心人。找到一个改变标准输入输出的默认编码的方案。
1 2 3 4 5
| import io import sys
sys.stdout= io.TextIOWrapper(sys.stdout.buffer,encoding='utf8')
|
所以,需要小修改Python代码,添加上面的内容。


将python程序打包成exe
文件
上面通过Node
来执行python
脚本,实际上是需要电脑有安装Python
,但是这样子当然是不太好的,有种捆绑的感觉。和Python的耦合度过高,所以最终考虑将python
程序打包成exe
文件。
将py
打包为exe文件需要依赖pyinstaller
。
更多:如何将python程序打包成exe文件_py打包成exe_一朝乐的博客-CSDN博客
安装pyinstaller
可能会遇到的问题以及解决方案:
如何将python程序打包成exe文件_py打包成exe_一朝乐的博客-CSDN博客

除了dist
外,生成的东西都能删掉,因为其他都是都是编译的时候生成的。只有dist
是我们有我们想要的exe
文件。
直接双击生成的exe
文件,也会打开选择文件夹弹框。

代码也需要修改成执行exe
文件,而不再是python
文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { exec } from 'child_process'; import { EOL } from 'os'; import path from 'path';
const dirname = import.meta.url.slice(8, import.meta.url.lastIndexOf('/'));
const p = new Promise((resolve, reject) => { exec(path.join(dirname, 'dist', 'dialog.exe'), (err, stdout, stderr) => { if (err) { reject(new Error(err)); } else if (stderr) { reject(new Error(stderr)); } else if (stdout) { const result = stdout.trim().split(EOL).toString(); resolve(result); } }) });
p.then(val => { console.log(val); })
|
效果和前面一样。
还可以编写一个sh文件,帮我们生成exe
文件,并且删除编译中生成的一些其他文件。
run.sh
1 2 3 4
| #!/bin/bash pyinstaller -F dialog.py rm build/ -rf rm dialog.specs
|