Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用tkinter-won';t生成多个GUI';s_Python_Python 3.x_Multithreading_Tkinter_Multiprocessing - Fatal编程技术网

Python 使用tkinter-won';t生成多个GUI';s

Python 使用tkinter-won';t生成多个GUI';s,python,python-3.x,multithreading,tkinter,multiprocessing,Python,Python 3.x,Multithreading,Tkinter,Multiprocessing,我有一个功能代码,可以在GUI中显示数据,GUI定期更新从web下载的新信息。(线程化方法的基本代码来源于)我正在使用线程化解决方案以改善阻塞IO问题(IO代码未包含在下面的简化代码示例中,因为IO似乎不是问题所在)。如果我将其作为单个实例运行,代码运行良好。然而,如果我可以使用多处理并行运行代码的多个实例,并为每个实例使用不同的输入列表,那将是最方便的。当我尝试实现多处理版本时,在尝试创建根窗口时,每个单独的进程都会挂起:“window=tk.tk()”。以下是正在工作的单实例版本: impo

我有一个功能代码,可以在GUI中显示数据,GUI定期更新从web下载的新信息。(线程化方法的基本代码来源于)我正在使用线程化解决方案以改善阻塞IO问题(IO代码未包含在下面的简化代码示例中,因为IO似乎不是问题所在)。如果我将其作为单个实例运行,代码运行良好。然而,如果我可以使用多处理并行运行代码的多个实例,并为每个实例使用不同的输入列表,那将是最方便的。当我尝试实现多处理版本时,在尝试创建根窗口时,每个单独的进程都会挂起:“window=tk.tk()”。以下是正在工作的单实例版本:

import threading
import random
import tkinter as tk
import random
import queue #Queue
import multiprocessing
import psutil

class GuiPartBase:    
    def __init__(self, master, queue, myList, endCommand):            
        self.queue = queue
        # Set up the GUI
        a = Label(master, text="Test Tkinter Display!")
        a.pack()
        ## etc
    
    def processIncoming(self):
    """Handle all messages currently in the queue, if any."""
        while self.queue.qsize():
            try:
                result = (self.queue.get(0))
                ## do stuff with incoming data...                
                print('result =', result)
            except queue.Empty:
                # just on general principles...
                pass

class ThreadedClientBase:
    """
    Launch the main part of the GUI and the worker thread. periodicCall and
    endApplication could reside in the GUI part, but putting them here
    means that you have all the thread controls in a single place.
    """
    def __init__(self, master, mylist):
        """
        Start the GUI and the asynchronous threads. We are in the main
        (original) thread of the application, which will later be used by
        the GUI as well. We spawn a new thread for the worker (I/O).
        """
        self.master = master
        self.mylist = mylist

        # Create the queue
        self.queue = queue.Queue()

        # Set up the GUI part
        self.gui = GuiPartBase(self.master, self.queue, mylist, self.endApplication)
    
        # Set up the thread to do asynchronous I/O
        # More threads can also be created and used, if necessary
        self.running = 1
        self.thread1 = threading.Thread(target=self.workerThread1)
        self.thread1.start()

        # Start the periodic call in the GUI to check if the queue contains
        # anything
        self.periodicCall()

    def periodicCall(self):
        """
        Check every 200 ms if there is something new in the queue.
        """
        self.gui.processIncoming()
        if not self.running:
            # This is the brutal stop of the system. You may want to do
            # some cleanup before actually shutting it down.
            import sys
            sys.exit(1)
        self.master.after(200, self.periodicCall)

    def workerThread1(self):
        """
        This is where we handle the asynchronous I/O. For example, it may be
        a 'select(  )'. One important thing to remember is that the thread has
        to yield control pretty regularly, by select or otherwise.
        """               
        while self.running:
            #  simulate asynchronous I/O, 
            time.sleep(rand.random() * 1.5)
            msg = rand.random()
            self.queue.put(msg)

    def endApplication(self):
        self.running = 0

def runGUIthread(threadedList2Get):
    print('entering runGUIthread...')
    print('list2Get = ', threadedList2Get)
    window = tk.Tk()    
    print('type of window = ', type(window))
    print('window = ', window)
    client = ThreadedClientBase(window, threadedList2Get)
    print('type of client = ', type(client))
    print('client = ', client)

    window.mainloop() 

if __name__ == '__main__':
    rand = random.Random()

    testList2a = ['abc','def','geh']
    testList2b = ['xyz', 'lmn', 'opq']
    allLists = [testList2a,testList2b]
    runGUIthread(testList2a)
因此,正如我所说的,上面的工作原理是——一个tkinter GUI可以正确地显示,没有错误。但是,如果我试图用下面的代码实现多处理,那么代码会产生两个进程,正如预期的那样,并且如pid打印输出所记录的那样。但是,每个进程都会打印“list2Get”(在runGUIthread中),然后就没有其他内容了。没有错误消息,python代码似乎已退出,因为系统活动监视器中没有列出任何持久进程。可能代码在“window=tk.tk()”行“挂起”/“退出”,因为从未执行过“print('type of window=',type(window))”行:

if __name__ == '__main__':
    rand = random.Random()

    testList2a = ['abc','def','geh']
    testList2b = ['xyz', 'lmn', 'opq']
    allLists = [testList2a,testList2b]
    #runGUIthread(testList2a)
    for list in allLists:
        p = multiprocessing.Process(target=runGUIthread, args=(list,))
        p.start()
        ps = psutil.Process(p.pid)
        print('pid = ', ps)

    #with multiprocessing.Pool(processes=2) as pool:
    #    pool.map(runGUIthread, allLists)
我没有多处理的经验,所以可能我没有正确地实现它。我尝试使用multiprocessing.Pool(),结果相同。 我还没有找到表明tkinter不能在同一个程序中生成多个GUI显示的信息。事实上,我发现有人意外地生成了多个GUI,尽管这似乎是使用concurrent.futures.ProcessPoolExecutor()的Python3.8。我目前使用的是Python3.7,希望不必重新安装一个新的环境来让这个多处理代码工作,尽管这可能是必要的

其他信息:使用python 3.7.6、tkinter 8.6.8、Eclipse 4.11.0、macOS10.13.6


非常感谢您的帮助。

您不能跨多个进程使用tkinter代码。至少,您不能运行相同的tkinter代码。它根本不是设计用来这样使用的。创建根窗口时,在封盖下面会创建一个tcl解释器,该解释器不能在进程之间进行pickle或共享,并且不使用python的全局解释器锁

简而言之,所有GUI代码都需要位于单个进程中的单个线程中

以下答案是一个稍微好一点的解释,由Tcl核心团队的一位开发人员编写:。以下是该答案的开头一段:

每个Tcl解释器对象(即知道如何运行Tcl过程的上下文)只能从创建它的OS线程安全地使用。这是因为Tcl不像Python那样使用全局解释器锁,而是广泛使用特定于线程的数据来减少内部所需的锁的数量。(编写良好的Tcl代码可以利用这一点在合适的硬件上进行非常大的扩展。)


不能跨多个进程使用tkinter代码。至少,您不能运行相同的tkinter代码。它根本不是设计用来这样使用的。创建根窗口时,在封盖下面会创建一个tcl解释器,该解释器不能在进程之间进行pickle或共享,并且不使用python的全局解释器锁

简而言之,所有GUI代码都需要位于单个进程中的单个线程中

以下答案是一个稍微好一点的解释,由Tcl核心团队的一位开发人员编写:。以下是该答案的开头一段:

每个Tcl解释器对象(即知道如何运行Tcl过程的上下文)只能从创建它的OS线程安全地使用。这是因为Tcl不像Python那样使用全局解释器锁,而是广泛使用特定于线程的数据来减少内部所需的锁的数量。(编写良好的Tcl代码可以利用这一点在合适的硬件上进行非常大的扩展。)


我发现这是tkinter、python和macOSX相关bug的一部分。 (python 2.7和3.6.4以及OSX 10.11.6报告了该错误,但显然python 3.7.6和OSX 10.13.6仍然存在问题。)

但是,有一个局部解决方案(也在同一网站上报告),对于我的案例来说,它似乎非常有效:

import multiprocessing
multiprocessing.set_start_method("spawn", force=True)
...
... other code same as initial ...
...

if __name__ == '__main__':
    testList2a = ['abc','def','geh']
    testList2b = ['xyz', 'lmn', 'opq']
    allLists = [testList2a,testList2b]
    with multiprocessing.Pool(processes=2) as pool:
       pool.map(runGUIthread, allLists)
结果是产生了多个GUI,每个进程一个GUI


根据bug报告,在Python3.8中,在MacOS上运行时多处理的默认启动方法已经更改,现在是“spawn”而不是“fork”,因此问题不会暴露出来(除非将启动方法更改为“fork”,在这种情况下,代码将失败).

我发现这是与tkinter、python和macOSX相关的bug的一部分:。 (python 2.7和3.6.4以及OSX 10.11.6报告了该错误,但显然python 3.7.6和OSX 10.13.6仍然存在问题。)

但是,有一个局部解决方案(也在同一网站上报告),对于我的案例来说,它似乎非常有效:

import multiprocessing
multiprocessing.set_start_method("spawn", force=True)
...
... other code same as initial ...
...

if __name__ == '__main__':
    testList2a = ['abc','def','geh']
    testList2b = ['xyz', 'lmn', 'opq']
    allLists = [testList2a,testList2b]
    with multiprocessing.Pool(processes=2) as pool:
       pool.map(runGUIthread, allLists)
结果是产生了多个GUI,每个进程一个GUI


根据错误报告,在python 3.8中,在MacOS上运行时,多处理的默认启动方法已更改,现在是“spawn”而不是“fork”,因此问题不会暴露出来(除非将启动方法更改为“fork”,否则代码将失败)。

感谢您的响应。我理解您不能“跨多个进程”运行tkinter代码。但是在我的例子中,我不想“跨多个进程”运行tkinter代码,而是希望在每个进程中运行tkinter的一个完全独立的实例(使用N