Я пытаюсь добавить многопроцессорность в свое приложение tkinter, но у меня возникли проблемы с ошибкой: TypeError: cannot pickle '_tkinter.tkapp' object. Я ознакомился с решением, предложенным в вопрос здесь и попытался реализовать мою собственную версию, и это, похоже, решило эту конкретную ошибку, но теперь вместо этого у меня есть постоянный OSError: [Errno 22] Invalid argument:

Я стремлюсь к тому, чтобы код выполнялся, так это то, что некоторые вычисления выполняются в фоновом режиме, и результаты этого расчета помещаются в очередь (здесь только целые числа, но в фактическом коде будут массивы Numpy). Затем приложение с графическим интерфейсом отображает некоторую статистику и результаты для пользователя.

from multiprocessing import Process, Queue
from queue import Empty
import tkinter as tk
from tkinter import Tk

class FooUI(Process):
    def __init__(self, q: Queue):
        super().__init__(target=self, args=(q,))
        self.queue = q
        self.duh = []
        self.root = Tk()
        self._create_interface()
        self.root.after(100, self._check_queue)
        self.root.mainloop()
        
    def _check_queue(self):
        try:
            out = self.queue.get_nowait()
            if out:
                self.duh.append(out)
                print(self.duh)
                return
        except Empty:
            pass
        self.root.after(100, self._check_queue) 
    
    def _create_interface(self):
        self.root.geometry("100x100")
        b = tk.Button(self.root, text='Start', command=self.calc)
        b.grid(row=0, column=0)
    
    def calc(self):
        p = Process(target=do_calc)
        p.start()     
        
def do_calc(q: Queue):
    for i in range(20):
        q.put(i**2)


If __name__ == '__main__':
    q = Queue()
    f = FooUI(q)
    f.start()

А вот и обратная связь:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\multiprocessing\spawn.py", line 116, in spawn_main
    exitcode = _main(fd, parent_sentinel)
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\multiprocessing\spawn.py", line 125, in _main
    prepare(preparation_data)
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\multiprocessing\spawn.py", line 236, in prepare
    _fixup_main_from_path(data['init_main_from_path'])
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\multiprocessing\spawn.py", line 287, in _fixup_main_from_path
    main_content = runpy.run_path(main_path,
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 264, in run_path
    code, fname = _get_code_from_file(run_name, path_name)
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 234, in _get_code_from_file
    with io.open_code(decoded_path) as f:
OSError: [Errno 22] Invalid argument: 'C:\\python\\block_model_variable_imputer\\<input>'
Traceback (most recent call last):
  File "<input>", line 3, in <module>
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\multiprocessing\process.py", line 121, in start
    self._popen = self._Popen(self)
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\multiprocessing\context.py", line 224, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\multiprocessing\context.py", line 327, in _Popen
    return Popen(process_obj)
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\multiprocessing\popen_spawn_win32.py", line 93, in __init__
    reduction.dump(process_obj, to_child)
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\multiprocessing\reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj)
TypeError: cannot pickle '_tkinter.tkapp' object
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\multiprocessing\spawn.py", line 116, in spawn_main
    exitcode = _main(fd, parent_sentinel)
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\multiprocessing\spawn.py", line 125, in _main
    prepare(preparation_data)
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\multiprocessing\spawn.py", line 236, in prepare
    _fixup_main_from_path(data['init_main_from_path'])
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\multiprocessing\spawn.py", line 287, in _fixup_main_from_path
    main_content = runpy.run_path(main_path,
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 264, in run_path
    code, fname = _get_code_from_file(run_name, path_name)
  File "C:\Users\cherp2\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 234, in _get_code_from_file
    with io.open_code(decoded_path) as f:
OSError: [Errno 22] Invalid argument: 'C:\\python\\block_model_variable_imputer\\<input>'

Какое-то время я пытался заставить его работать. Любая помощь будет оценена!

0
pavel 22 Фев 2021 в 03:57

1 ответ

Лучший ответ

Вы неправильно используете подкласс Process(). Вам нужно переопределить метод run() вместо передачи параметра target.

from multiprocessing import Process, Queue
from queue import Empty
import tkinter as tk

class FooUI(Process):
    def __init__(self, q: Queue):
        super().__init__() # don't pass target and args options
        self.queue = q
        self.duh = []
    
    # override run() method and create the Tk() inside the function
    def run(self):
        self.root = tk.Tk()
        self._create_interface()
        self.root.after(100, self._check_queue)
        self.root.mainloop()
        
    def _check_queue(self):
        try:
            out = self.queue.get_nowait()
            if out:
                self.duh.append(out)
                print(self.duh)
                #return
        except Empty:
            pass
        self.root.after(100, self._check_queue) 
    
    def _create_interface(self):
        self.root.geometry("100x100")
        b = tk.Button(self.root, text='Start', command=self.calc)
        b.grid(row=0, column=0)
    
    def calc(self):
        if self.queue.empty():
            self.duh.clear()
            p = Process(target=do_calc, args=[self.queue]) # pass self.queue to do_calc()
            p.start()     
        
def do_calc(q: Queue):
    for i in range(20):
        q.put(i**2)

if __name__ == '__main__':
    q = Queue()
    f = FooUI(q)
    f.start()
1
acw1668 22 Фев 2021 в 08:17