前言
最近做一个python项目,项目中调用了NodeJS中的一段代码,用tkinter做GUI然后pyintaller打包成无命令行窗口后运行过程中显示NodeJS黑框,客户不接受。于是网上查找各种办法,但是都只有pyintaller打包时用-w/–noconsole类似的回答,该方法只能取消python程序的黑框,NodeJS的无法取消。于是查看execjs的源代码,终于解决此问题。
测试代码
from tkinter import *
import os
import execjs
def get_js(fileNme):
# jscript = execjs.get("Node")
# jscript = execjs.get()
with open(fileNme, 'r', encoding='utf-8')as f:
content = f.read()
js = execjs.compile(content)
return js
def get_data(entry):
js = get_js("测试.js")
var.set(str(js.call("getData", "1")))
root = Tk()
button = Button(root, text="点击", command=lambda: get_data(entry))
button.pack()
var = StringVar()
entry = Entry(root, textvariable=var)
entry.pack()
root.mainloop()
测试运行
改成 .pyw 直接运行,发现 node 弹窗一闪即逝。如下图:
反查源代码
- 经测试发现每次调用 .call 的时候都会弹窗
- 找到.call打断点,一步步调试,最终定位到 execjs库中的_external_runtime.py文件下的函数_exec_with_pipe
def _exec_with_pipe(self, source):
cmd = self._runtime._binary()
p = None
try:
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=self._cwd, universal_newlines=True)
# 不显示调用黑框
# p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=self._cwd, universal_newlines=True, shell=True)
input = self._compile(source)
if six.PY2:
input = input.encode(sys.getfilesystemencoding())
stdoutdata, stderrdata = p.communicate(input=input)
ret = p.wait()
finally:
del p
self._fail_on_non_zero_status(ret, stdoutdata, stderrdata)
return stdoutdata
-
网上一查发现Popen为python调用cmd的函数,应该就是他了,想办法让调用的时候不显示命令行就行了,Popen有没有不显示黑框的参数呢,结果还真有。
参考博文:python中的subprocess.Popen()使用
-
很明显,只要将 Popen 创建对象的时候加上 shell=True 就可以了,即
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=self._cwd, universal_newlines=True)
改为
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, cwd=self._cwd, universal_newlines=True, shell=True)
总结
- 在python中只要调用了cmd的地方,在用pyinstaller打包成无控制台显示(-w/–noconsole)的时候都会出现黑框
- 不用(-w/–noconsole)打包的时候不出现黑框是因为程序自身就在控制台中运行
- 凡是通过Popen调用cmd的所有代码,都可以这样修改取消控制台输出。