简体   繁体   English

GUI 在 cmd 运行时冻结,但不是通过 Pycharm

[英]GUI freezes on cmd run but not through Pycharm

So I've been looking at this for quite some time and can't figure this problem out.所以我一直在看这个很长一段时间,无法解决这个问题。

The goal here is to have it display realtime results line by line from the cmd/ terminal into the tkinter ScrolledText/Text widget, which it does except when ran through the cmd on Windows.这里的目标是让它从 cmd/ 终端逐行显示实时结果到 tkinter ScrolledText/Text 小部件,除非在 Windows 上通过 cmd 运行,否则它会这样做。

I am running the code on both Linux (Kali) and Windows 10. If I run it using Pycharm on Windows 10 then it will run smoothly and the GUI won't freeze waiting for the code run using Pexpect.我在 Linux (Kali) 和 Windows 10 上运行代码。如果我在 Windows 10 上使用 Pycharm 运行它,那么它将运行顺利,并且 GUI 不会冻结等待使用 Pexpect 运行的代码。 If I run the code through the cmd ( python tmp_stack.py ) then it will freeze the GUI until Pexpect finishes running the file I asked it to.如果我通过 cmd ( python tmp_stack.py ) 运行代码,那么它将冻结 GUI,直到 Pexpect 完成我要求它运行的文件。
On Linux terminal the code runs fine and doesn't freeze (after adjusting it for Linux).在 Linux 终端上,代码运行良好并且不会冻结(在针对 Linux 进行调整之后)。
I combined multiple files into tmp_stack.py to prevent the need to create more files for no real reason.我将多个文件合并到tmp_stack.py中,以防止无缘无故创建更多文件。
I have tested that the configuration runs the same on both Windows and Linux.我已经测试了配置在 Windows 和 Linux 上的运行方式相同。

Side note: I don't mind changing to subprocess.Popen and I don't mind using threading if it won't complain about main loop and will work.旁注:我不介意更改为subprocess.Popen并且如果它不会抱怨主循环并且可以工作,我不介意使用threading

requirements.txt - pexpect==4.6.0 requirements.txt - pexpect==4.6.0

op.py:操作.py:

import time

print('This is op.py')
for i in range(10):
    time.sleep(1)
    print(i)

oscheck.py: oscheck.py:

import platform

MYPLATFORM = platform.system()

tmp_stack.py: 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()

Try running the child Python process with output buffering disabled.尝试在禁用输出缓冲的情况下运行子 Python 进程。

In other words, try replacing the line换句话说,尝试替换该行

        cmd = ['python', filename]

with

        cmd = ['python', '-u', filename]

See also the Python documentation for the -u option .另请参阅-u选项的 Python 文档

I had the same thing - a process spawned from my main script read the stdout (pexpect) in PyCharm, but not when launched from a shell.我有同样的事情 - 从我的主脚本产生的一个进程读取 PyCharm 中的标准输出(pexpect),但不是从 shell 启动时。

The -u option fixed this, thanks. -u 选项解决了这个问题,谢谢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM