如何在python的不同终端窗口中运行函数

如何在python的不同终端窗口中运行函数

本文介绍了如何在python的不同终端窗口中运行函数/线程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个这样的程序:

from threading import Thread
def foo1(arg):
    print("foo1 >>> Something")
    input("foo1 >>> Enter Something")
    ...

def foo2(arg):
    print("foo2 >>> Something")
    input("foo2 >>> Enter Something")
    ...

def main():
    th1 = Thread(target= foo1)
    th1.start()

    th2 = Thread(target= foo2)
    th2.start()

这个程序在同一个终端窗口中运行这两个函数(foo1foo2).我可以以某种方式在不同的终端窗口中运行它们吗?我不希望重新运行程序.原因是它们在同一地点和同一时间打印和输入.我不要.有什么方法吗?

This program runs both the functions(foo1 and foo2) in the same terminal window. Can I in some way run them in a different terminal window. What I don't wish is to re-run the program. The reason is that they print and take input at the same place and same time. I don't want. Any method?

推荐答案

仅使用线程是无法实现的,当您创建一个新的 Thread 时,它会共享所有变量程序中的其他线程,包括 sys.stdout/sys.stdin.

What you are trying to accomplish isn't possible with just threads, when you create a new Thread it shares all the variables with other threads in your program, including sys.stdout / sys.stdin.

通常您不必担心 Python 程序中的 PIPES,因为它会为您处理.print 将文本发送到 sys.stdout 并且 inputsys.stdin 获取文本(并发送错误消息sys.stderr)

Normally you don't have to worry about PIPES in python programs because it takes care of it for you. print sends the text to sys.stdout and input grabs text from sys.stdin (and error messages are sent to sys.stderr)

因此在两个终端窗口中运行一个程序意味着您将不得不拥有多个输入/输出流,对此有两种解决方案:

So running one program in two terminal windows would mean you would have to have more then one input/output streams, to which there are two solutions:

  1. 像其他人描述的那样,使用 subprocess.Popen 运行一个完全独立的程序,并且弄清楚如何在两者之间架起信息,这真的很痛苦.
  1. run a completely separate program with subprocess.Popen like the other fellow described and figure out how to bridge information across the two which is a real pain.

或 2. 使用 tkinter 之类的工具创建自己的终端窗口,这从头开始很难,但幸运的是,IDLE 拥有标准库中的大部分代码.

or 2. create your own terminal window with something like tkinter, which is difficult from scratch but luckily IDLE has the majority of the code available in the standard library.

这是来自 idlelib.PyShell 的 PyShell 的改编版本,用于运行线程而不是交互式解释:

Here is an adapted version of PyShell from idlelib.PyShell to run a Thread instead of the interactive interpretive:

from idlelib import PyShell,EditorWindow
import threading,sys
try:
    import tkinter as tk #python 3.X
except ImportError:
    import Tkinter as tk #python 2
    import tkMessageBox as messagebox
    tk.messagebox = messagebox

class ThreadShell(PyShell.PyShell):
    """mostly copied from idlelib.PyShell module but adapted to work with threads"""
    #__adapted_by__ = "Tadhg McDonald-Jensen"
    def __init__(self, tk_root,target=None):
        #not sure exactly what the FileList object is for but it is required by the shell
        flist = PyShell.PyShellFileList(tk_root)
        super(ThreadShell,self).__init__(flist)

        #internal event flag for input, allows thread waiting for input to wait until a tk event handles it
        self.__input_flag = threading.Event()
        #target is stored and called in .run_command() which also deals with finishing the shell
        self.target = target
        self.thread = threading.Thread(target=self.run_command)
        #tk_root.after makes the .start method call when the program starts (after 0 miliseconds)
        tk_root.after(0,self.start)

    def start(self):
        """starts executing the Thread"""
        super(ThreadShell,self).beginexecuting()
        try:
            self.thread.start()
        except RuntimeError:
            self.executing = 0
            self.canceled = 0
            #self.top.quit() #this causes double deletion warnings with better Implementation of mainloop

    beginexecuting = start

    def run_command(self):
        """calls target from constructor with self as argument then cleans up shell"""
        if self.target:
            self.target(self)
        self.prompt_exit()
        self.executing = 0
        self.canceled = 0
        try:
            self.text.after(1,self.close)
        except RuntimeError:
            pass #tkinter has issues with changing threads so often after closing one shell others will throw this error

    def printf(self,*stuff,**kw):
        """works just like python 3.x print function but writes to shell's .stdout file"""
        if self.executing:
##            if USING_OLD_METHOD:       #Pretty sure this would do exact same thing
##                kw.setdefault("file",self.stdout)
##                print(*stuff,**kw), self.resetoutput()
##                return
            sep = kw.get("sep"," ")
            end = kw.get("end","\n")
            text = sep.join(stuff) + end
            self.stdout.write(text)
            self.resetoutput()

    def input(self,prompt="",timeout=None):
        """python 2 equivelent to raw_input or py 3+ input
Prompts user for input and freezes thread until input is given
Will return "" if .executing is False or it timed out from optional timeout argument"""
        if self.executing or self.closing:
            if prompt:
                self.stdout.write(prompt)
            self.__in_buffer = ""
            self.__input_flag.clear()
            self.reading=True
            self.__input_flag.wait(timeout)
            #input is inserted into .__in_buffer by other events
            #then set __input_flag so that it can be delivered to thread
            self.reading = False
            return self.__in_buffer.strip("\n")
        else:
            raise RuntimeError("cannot take input after finished")

    def prompt_exit(self):
        """writes press enter to quit" to the console colour then waits for input"""
        self.executing = False
        self.closing = True
        self.console.write("\n press enter to quit")
        self.input()

    def join_thread(self,timeout=None):
        """sets .executing label to False then waits to join thead,
returns True if thread finished or False if timeout activated"""
        self.executing = False
        self.closing = True
        if self.thread:
            self.thread.join(timeout)
        return not self.thread.is_alive()

    def _close(self):
        "Extend EditorWindow._close(), joins thread to close it"

        # Restore std streams
        sys.stdout = self.save_stdout
        sys.stderr = self.save_stderr
        sys.stdin = self.save_stdin
        # Break cycles
        self.interp = None
        self.console = None
        self.flist.pyshell = None
        self.history = None
        EditorWindow.EditorWindow._close(self)
        self.join_thread()

    def stop_readline(self):
        self.__in_buffer = ""
        self.__input_flag.set()

    def update_in(self):
        """updates input from user, I think some of the labels are probably unnecessary but it is easier to leave it alone"""
        line = self.text.get("iomark", "end-1c")
        if len(line) == 0:  # may be EOF if we quit our mainloop with Ctrl-C
            line = "\n"
        self.resetoutput()
        if self.canceled:
            self.canceled = 0
        if self.endoffile:
            self.endoffile = 0
            line = ""
        self.__in_buffer = line
        self.__input_flag.set()

    def cancel_callback(self, event=None):
        try:
            if self.text.compare("sel.first", "!=", "sel.last"):
                return # Active selection -- always use default binding
        except:
            pass
        if not (self.executing or self.reading):
            return "break"
        self.endoffile = 0
        self.canceled = 1
        if self.reading:
            self.update_in()
        return "break"

    def eof_callback(self, event):
        if self.executing and not self.reading:
            return # Let the default binding (delete next char) take over
        if not (self.text.compare("iomark", "==", "insert") and
                self.text.compare("insert", "==", "end-1c")):
            return # Let the default binding (delete next char) take over
        if not self.executing:
            self.resetoutput()
            self.close()
        else:
            self.canceled = 0
            self.endoffile = 1
            self.update_in()
        return "break"

    def enter_callback(self, event):
        """called when the enter/return key is pressed,
only the recursive self.top.mainloop() / self.top.quit() had to be changed for support"""
        # it is very long to copy/paste for the one line change, so I override the method temporarily
        save = self.top.quit
        self.top.quit = self.update_in
        super(ThreadShell,self).enter_callback(event)
        self.top.quit = save


#stupid module depends on this being set from the main function, so it needs to be done manually
PyShell.use_subprocess = True

#this defines the root tkinter window and sets it up
root = tk.Tk()
EditorWindow.fixwordbreaks(root)
root.withdraw()
#I need this to work on my mac, not sure if there are other OS specific stuff that should be included
try:
    from idlelib import macosxSupport
    macosxSupport.setupApp(root, None)
except (ImportError,AttributeError):
    pass





##!!!!!!!!!!!!!!!!!!!! And This Is The Part You Need To Worry About !!!!!!!!!!!!!!!!!!!!##


switch = threading.Event()
switch.clear()

def foo(shell):
    global x
    x = shell.input("enter a message: ")
    switch.set()
    shell.printf("message sent")

def foo2(shell):
    shell.printf("waiting for message...")
    while shell.executing and not switch.is_set():
        switch.wait(2)   # by using shell.executing in the loop it will occasionally check
                         # if the program should quit because the window was closed
    if shell.executing:
        shell.printf("message recieved: ",x)

shell1 = ThreadShell(root,foo)
shell2 = ThreadShell(root,foo2)
first_time = True
while shell1.executing or shell2.executing or first_time:
    first_time = False
    root.mainloop()
root.destroy()

这篇关于如何在python的不同终端窗口中运行函数/线程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 06:08