GUI 在 cmd 运行 上冻结,但不会通过 Pycharm
GUI freezes on cmd run but not through Pycharm
所以我已经研究了很长时间了,但无法解决这个问题。
这里的目标是让它从 cmd/ 终端逐行显示实时结果到 tkinter ScrolledText/Text 小部件,除了 运行 通过 [=44] 上的 cmd =].
我运行在 Linux (Kali) 和 Windows 10 上使用 运行 代码。如果我 运行 在 [=] 上使用 Pycharm 44=] 10 然后它会 运行 顺利并且 GUI 不会冻结等待代码 运行 使用 Pexpect。
如果我 运行 通过 cmd (python tmp_stack.py
) 的代码,那么它会冻结 GUI,直到 Pexpect 完成 运行 我要求的文件。
在 Linux 终端上,代码 运行 很好并且不会冻结(针对 Linux 调整后)。
我将多个文件组合成 tmp_stack.py
以防止无缘无故需要创建更多文件。
我已经测试过 运行 在 Windows 和 Linux 上的配置相同。
旁注:我不介意更改为 subprocess.Popen
并且我不介意使用 threading
如果它不会抱怨主循环并且可以工作。
requirements.txt - pexpect==4.6.0
op.py:
import time
print('This is op.py')
for i in range(10):
time.sleep(1)
print(i)
oscheck.py:
import platform
MYPLATFORM = platform.system()
tmp_stack.py:
from tkinter import *
from tkinter.ttk import *
from tkinter.scrolledtext import ScrolledText
import oscheck
if oscheck.MYPLATFORM == 'Windows':
from pexpect import popen_spawn
elif oscheck.MYPLATFORM == 'Linux':
from pexpect import spawn
class TextFrame(Frame):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.textarea = ScrolledText(master=self, wrap=WORD, bg='black', fg='white')
self.textarea.pack(side=TOP, fill=BOTH, expand=True)
def insert(self, text):
self.textarea['state'] = 'normal'
# Insert at the end of the TextArea.
self.textarea.insert(END, text)
self.textarea['state'] = 'disabled'
self.update()
class TheFrame(TextFrame):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.btn = Button(master=self, text="Run op", command=self.run_op)
self.btn.pack(fill=X)
# Ignore first child of pexpect on Linux because it is the bin bash prefix.
self.linux_flag = True
def run_op(self):
filename = 'op.py'
cmd = ['python', filename]
self.cmdstdout = RunCommand(cmd)
# Get root.
root_name = self._nametowidget(self.winfo_parent()).winfo_parent()
root = self._nametowidget(root_name)
root.after(0, self.updateLines())
def updateLines(self):
for line in self.cmdstdout.get_child():
if oscheck.MYPLATFORM == 'Linux' and self.linux_flag:
self.linux_flag = False
continue
try:
self.insert(line.decode('utf-8'))
except TclError as e:
print("Window has been closed.\n", e)
self.close()
break
def close(self):
self.cmdstdout.close()
class RunCommand:
def __init__(self, command):
command = ' '.join(command)
if oscheck.MYPLATFORM == 'Windows':
print('You are on Windows.')
self.child = popen_spawn.PopenSpawn(command, timeout=None)
elif oscheck.MYPLATFORM == 'Linux':
print('You are on Linux.')
self.child = spawn("/bin/bash", timeout=None)
self.child.sendline(command)
else:
print('Not Linux or Windows, probably Mac')
self.child = spawn(command, timeout=None)
def get_child(self):
return self.child
def close(self):
if oscheck.MYPLATFORM == 'Linux':
self.child.terminate(True)
def on_close(root):
root.quit()
root.destroy()
root = Tk()
if oscheck.MYPLATFORM == 'Windows':
root.state('zoomed')
elif oscheck.MYPLATFORM == 'Linux':
root.attributes('-zoomed', True)
the_frame = TheFrame(root, padding="1")
the_frame.grid(column=0, row=0, sticky=N+W+S+E)
root.protocol("WM_DELETE_WINDOW", lambda: on_close(root))
mainloop()
尝试 运行 禁用输出缓冲的子 Python 进程。
换句话说,尝试替换行
cmd = ['python', filename]
和
cmd = ['python', '-u', filename]
所以我已经研究了很长时间了,但无法解决这个问题。
这里的目标是让它从 cmd/ 终端逐行显示实时结果到 tkinter ScrolledText/Text 小部件,除了 运行 通过 [=44] 上的 cmd =].
我运行在 Linux (Kali) 和 Windows 10 上使用 运行 代码。如果我 运行 在 [=] 上使用 Pycharm 44=] 10 然后它会 运行 顺利并且 GUI 不会冻结等待代码 运行 使用 Pexpect。
如果我 运行 通过 cmd (python tmp_stack.py
) 的代码,那么它会冻结 GUI,直到 Pexpect 完成 运行 我要求的文件。
在 Linux 终端上,代码 运行 很好并且不会冻结(针对 Linux 调整后)。
我将多个文件组合成 tmp_stack.py
以防止无缘无故需要创建更多文件。
我已经测试过 运行 在 Windows 和 Linux 上的配置相同。
旁注:我不介意更改为 subprocess.Popen
并且我不介意使用 threading
如果它不会抱怨主循环并且可以工作。
requirements.txt - pexpect==4.6.0
op.py:
import time
print('This is op.py')
for i in range(10):
time.sleep(1)
print(i)
oscheck.py:
import platform
MYPLATFORM = platform.system()
tmp_stack.py:
from tkinter import *
from tkinter.ttk import *
from tkinter.scrolledtext import ScrolledText
import oscheck
if oscheck.MYPLATFORM == 'Windows':
from pexpect import popen_spawn
elif oscheck.MYPLATFORM == 'Linux':
from pexpect import spawn
class TextFrame(Frame):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.textarea = ScrolledText(master=self, wrap=WORD, bg='black', fg='white')
self.textarea.pack(side=TOP, fill=BOTH, expand=True)
def insert(self, text):
self.textarea['state'] = 'normal'
# Insert at the end of the TextArea.
self.textarea.insert(END, text)
self.textarea['state'] = 'disabled'
self.update()
class TheFrame(TextFrame):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.btn = Button(master=self, text="Run op", command=self.run_op)
self.btn.pack(fill=X)
# Ignore first child of pexpect on Linux because it is the bin bash prefix.
self.linux_flag = True
def run_op(self):
filename = 'op.py'
cmd = ['python', filename]
self.cmdstdout = RunCommand(cmd)
# Get root.
root_name = self._nametowidget(self.winfo_parent()).winfo_parent()
root = self._nametowidget(root_name)
root.after(0, self.updateLines())
def updateLines(self):
for line in self.cmdstdout.get_child():
if oscheck.MYPLATFORM == 'Linux' and self.linux_flag:
self.linux_flag = False
continue
try:
self.insert(line.decode('utf-8'))
except TclError as e:
print("Window has been closed.\n", e)
self.close()
break
def close(self):
self.cmdstdout.close()
class RunCommand:
def __init__(self, command):
command = ' '.join(command)
if oscheck.MYPLATFORM == 'Windows':
print('You are on Windows.')
self.child = popen_spawn.PopenSpawn(command, timeout=None)
elif oscheck.MYPLATFORM == 'Linux':
print('You are on Linux.')
self.child = spawn("/bin/bash", timeout=None)
self.child.sendline(command)
else:
print('Not Linux or Windows, probably Mac')
self.child = spawn(command, timeout=None)
def get_child(self):
return self.child
def close(self):
if oscheck.MYPLATFORM == 'Linux':
self.child.terminate(True)
def on_close(root):
root.quit()
root.destroy()
root = Tk()
if oscheck.MYPLATFORM == 'Windows':
root.state('zoomed')
elif oscheck.MYPLATFORM == 'Linux':
root.attributes('-zoomed', True)
the_frame = TheFrame(root, padding="1")
the_frame.grid(column=0, row=0, sticky=N+W+S+E)
root.protocol("WM_DELETE_WINDOW", lambda: on_close(root))
mainloop()
尝试 运行 禁用输出缓冲的子 Python 进程。
换句话说,尝试替换行
cmd = ['python', filename]
和
cmd = ['python', '-u', filename]