Over here I am using multiprocessing to run multiple algorithms in tkinter. At first I tried using threading, but it can't work properly in my program. Below is an idea of my program workflow, it works something like this, but just different functions:
from tkinter import *
from multiprocessing import Process
def SquarFunc(Square):
for i in range(1,1000):
Square.set(str(i**2))
def CubeFunc(Cube):
for i in range(1,1000):
Cube.set(str(i**3))
if __name__ == "__main__":
window= Tk()
Square= StringVar()
Cube= StringVar()
window.geometry("500x500")
A= Label(window, textvariable= Square)
A.place(x=200, y=200)
B= Label(window, textvariable= Cube)
B.place(x=300, y=300)
Squaring= Process(target=SquarFunc, args=(Square, ))
Cubing= Process(target=CubeFunc, args=(Cube, ))
Squaring.start()#Error originates here
Cubing.start()
Squaring.join()
Cubing.join()
window.mainloop()
The error produced is this:
TypeError: cannot pickle '_tkinter.tkapp' object
Anybody knows how to fix this?? thanks in advance!
CodePudding user response:
Here is an example of how to communicate with other processes if using multiprocessing
(explanation is in comments, time.sleep
is used just for the example because otherwise those loops will complete in a few microseconds):
from tkinter import Tk, StringVar, Label
from multiprocessing import Process, Manager
import time
def square_func(d, name):
for i in range(1, 1000):
# update data in the shared dict
d[name] = i
time.sleep(0.1)
def cube_func(d, name):
for i in range(1, 1000):
# update data in the shared dict
d[name] = i
time.sleep(0.1)
def update_string_vars(d, *variables):
for var in variables:
# get the value from shared dict
value = d[str(var)]
if value is not None:
# set string var to the value
var.set(str(value))
# schedule this to run again
window.after(100, update_string_vars, d, *variables)
# cleanup process upon closing the window in case
# processes haven't finished
def terminate_processes(*processes):
for p in processes:
p.terminate()
if __name__ == "__main__":
window = Tk()
window.geometry("500x500")
# bind the terminator to closing the window
window.bind('<Destroy>', lambda _: terminate_processes(
square_process, cube_process))
square_var = StringVar()
cube_var = StringVar()
Label(window, text='Square:').pack()
Label(window, textvariable=square_var).pack()
Label(window, text='Cube:').pack()
Label(window, textvariable=cube_var).pack()
# create the manager to have a shared memory space
manager = Manager()
# shared dict with preset values as to not raise a KeyError
process_dict = manager.dict({str(square_var): None, str(cube_var): None})
square_process = Process(
target=square_func, args=(process_dict, str(square_var))
)
cube_process = Process(
target=cube_func, args=(process_dict, str(cube_var))
)
square_process.start()
cube_process.start()
# start the updater
update_string_vars(process_dict, square_var, cube_var)
window.mainloop()
Useful:
See also:
I strongly advise against using wildcard (*
) when importing something, You should either import what You need, e.g. from module import Class1, func_1, var_2
and so on or import the whole module: import module
then You can also use an alias: import module as md
or sth like that, the point is that don't import everything unless You actually know what You are doing; name clashes are the issue.
I strongly suggest following PEP 8 - Style Guide for Python Code. Function and variable names should be in snake_case
, class names in CapitalCase
. Don't have space around =
if it is used as a part of keyword argument (func(arg='value')
) but have space around =
if it is used for assigning a value (variable = 'some value'
). Have space around operators ( -/
etc.: value = x y
(except here value = x y
)). Have two blank lines around function and class declarations. Object method definitions have one blank line around them.