Kelvin
Kelvin

Reputation: 33

Is it possible to multiprocess tkinter?

I am trying to multiprocess multiple tkinter windows where threading (for long running processes with while loops) in each process will be required. Is this possible?

I have tried to work around this by doing a pure multithread implementation - The entire application starts to turn 'laggy'; hence the attempt now to do multiprocessing. Please see code for a very simplified, basic version of what I am trying to do. There are 2 scripts:

Main file (to run)

from tkinter import *
from tkinter import ttk
import threading
import multiprocessing
import multiprocessing_import_worker

def hello():
    if __name__ == '__main__':
        p_list = ["Hello", "success"]
        p = multiprocessing.Process(target=multiprocessing_import_worker.worker, args=p_list)
        p.start()
        root.destroy()

root = Tk()
root.title("Window 1")

statusFrame = Frame(root)
statusFrame.pack()
statusLabelFrame = LabelFrame(statusFrame, text='TEST FRAME', font="Helvetica 8 bold")
statusLabelFrame.pack(fill="both", expand="yes", padx="7")
firstButtonFrame = Frame(root)
firstButtonFrame.pack()
statusbar = Label(statusLabelFrame, text="TEST", width=40, font="Helvetica 8 bold")
statusbar.pack()
b1=ttk.Button(firstButtonFrame, text="START SESSION", width=15, command=hello)
b1.pack(pady=(10,0))

root.mainloop()

Secondary file (to be run as a separate process)

def worker(word, word2):
    import threading
    import tkinter as tk
    from tkinter import ttk
    import os

    print(word)
    print(word2)

    def testThread():
        global word
        global word2
        print(word)
        print(word2)

    root = tk.Tk()
    root.withdraw()
    rootWindow = tk.Toplevel()
    rootWindow.title("Window 2")

    statusFrame = tk.Frame(rootWindow)
    statusFrame.pack()
    statusLabelFrame = tk.LabelFrame(statusFrame, text='TEST FRAME', font="Helvetica 8 bold")
    statusLabelFrame.pack(fill="both", expand="yes", padx="7")
    firstButtonFrame = tk.Frame(rootWindow)
    firstButtonFrame.pack()
    statusbar = tk.Label(statusLabelFrame, text="TEST", width=40, font="Helvetica 8 bold")
    statusbar.pack()
    b1=ttk.Button(firstButtonFrame, text="START SESSION", width=15, state='disabled')
    b1.pack(pady=(10,0))

    child_thread1 = threading.Thread(target=testThread)
    child_thread1.start()

    rootWindow.protocol("WM_DELETE_WINDOW", os._exit(0))
    rootWindow.mainloop()

Expected results:

Actual results:

Upvotes: 3

Views: 3667

Answers (2)

user3757614
user3757614

Reputation: 1806

The standard way to do this would be for all GUI manipulation to take place in a single process, while other processes will handle any computationally-heavy tasks. As long as there's no heavy computation going on in the GUI process, the application should remain responsive, and you can use cross-process communication to update the GUI with results of the heavy process as needed.

Upvotes: 2

Bryan Oakley
Bryan Oakley

Reputation: 385920

It's possible, but all interaction with the GUI must be in a single process. Tkinter itself cannot span processes, nor threads. Your other processes must put work on a queue that the tkinter process periodically polls and acts upon (of course, you could also communicate with any other form of IPC).

The reason for this is that each root window is associated with an embedded tcl interpreter, and this interpreter itself cannot span processes.

Upvotes: 3

Related Questions