如何从并行进程更新主窗口/画布。 Python 3.12

问题描述 投票:0回答:1

有一个程序可以实时模拟图的构建。为了更新时间表并加快整个程序的工作,决定使用多处理。

问题是无法从并行进程更新主窗口/画布。

那么,也许有人遇到过这样的事情或者知道如何解决它?

import time
from tkinter import *
from tkinter import ttk
from tkinter import filedialog
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import pandas as pd
import os
import sys
import inspect
import multiprocessing


def get_script_dir(follow_symlinks=True):
    if getattr(sys, 'frozen', False):
        path = os.path.abspath(sys.executable)
    else:
        path = inspect.getabsfile(get_script_dir)
    if follow_symlinks:
        path = os.path.realpath(path)
    return os.path.dirname(path)


def open_file():
    data_frame = ns.daf
    filepath = filedialog.askopenfilename(initialdir=get_script_dir())
    if filepath != "":
        data_frame = pd.read_csv(filepath, sep="\t")
    ns.daf = data_frame


def set_graf_data(nas, curva):
    # window = ns.window
    graf_value = [[] for _ in range(9)]
    x_value = [[] for _ in range(9)]
    data_frame = nas.daf
    axes = nas.axes
    delay = nas.delay

    line_object = [axes.plot([], [])[0] for _ in range(9)]
    print(data_frame)
    for i in range(len(data_frame[data_frame.columns.tolist()[1]])):
        if len(x_value[1]) == 1000:
            del x_value[1][0]
        x_value[1].append(float(data_frame[data_frame.columns.tolist()[1]].values[i]) * 86400 - float(data_frame[data_frame.columns.tolist()[1]].values[0]) * 86400)
        for j in range(9):
            if len(graf_value[j]) == 1000:
                del graf_value[j][0]

            graf_value[j].append(float(data_frame[data_frame.columns.tolist()[j + 2]].values[i]))

            line_object[j].set_data(x_value[1], graf_value[j])
        axes.relim()
        axes.autoscale()
        axes.set_xlim(x_value[1][-1] - 1000, x_value[1][-1] + 100)
        curva.update()
        time.sleep(1/delay)


def choose_click():
    data_frame = ns.daf
    choose_window = Tk()
    choose_window.columnconfigure(index=0, weight=1)
    choose_window.rowconfigure(index=0, weight=50)
    choose_window.rowconfigure(index=1, weight=1)
    choose_window.title("Choose")
    grafes = data_frame.columns.tolist()[2:]
    grafes_var = Variable(choose_window, value=grafes)
    grafes_listbox = Listbox(choose_window, listvariable=grafes_var)
    grafes_listbox.grid(row=0, column=0, sticky=NSEW)
    btn_choose = ttk.Button(choose_window, text="Confirm", cursor="hand2",
                            command=lambda: (choose_window.destroy()))
    btn_choose.grid(row=1, column=0, sticky=NSEW)
    ns.daf = data_frame


def confirm_time():
    d = float(entry_time.get())
    ns.delay = d


if __name__ == '__main__':

    root = Tk()
    root.title("Emulator")
    # root.geometry("250x200")

    fig, ax = plt.subplots(1, 1)
    plt.ion()

    canvas = FigureCanvasTkAgg(fig, master=root)
    canvas.get_tk_widget().grid(row=2, column=2, columnspan=3, rowspan=40, sticky=NSEW)

    delay_time = 10

    df = pd.DataFrame()

    mgr = multiprocessing.Manager()
    ns = mgr.Namespace()
    ns.daf = df
    ns.axes = ax
    ns.delay = delay_time
    # ns.window = root
    ns.can = canvas

    p1 = multiprocessing.Process(target=set_graf_data, args=(ns, canvas, ))

    btn_open = ttk.Button(text='Open file', command=open_file)
    btn_open.grid(column=0, row=0, sticky=NSEW)

    btn_check = ttk.Button(text='Check', command=choose_click)
    btn_check.grid(column=1, row=0, sticky=NSEW)

    btn_confirm_time = ttk.Button(text='Confirm time', command=confirm_time)
    btn_confirm_time.grid(row=1, column=1, sticky=NSEW)

    entry_time = ttk.Entry()
    entry_time.insert(0, str(1))
    entry_time.grid(row=1, column=0, sticky=E)

    btn_set_data = ttk.Button(text='Start', command=lambda: p1.start())
    btn_set_data.grid(column=2, row=0, sticky=NSEW)

    root.mainloop()

Были попытки передать в параллельный процесс само окно или отдельно 画布,но выскакивает ошибка:

Traceback (most recent call last):
  File "C:\Users\egorov22\PycharmProjects\emulator\main.py", line 104, in <module>
    ns.can = canvas
    ^^^^^^
  File "C:\Users\egorov22\AppData\Local\Programs\Python\Python312\Lib\multiprocessing\managers.py", line 1129, in __setattr__
    return callmethod('__setattr__', (key, value))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\egorov22\AppData\Local\Programs\Python\Python312\Lib\multiprocessing\managers.py", line 820, in _callmethod
    conn.send((self._id, methodname, args, kwds))
  File "C:\Users\egorov22\AppData\Local\Programs\Python\Python312\Lib\multiprocessing\connection.py", line 206, in send
    self._send_bytes(_ForkingPickler.dumps(obj))
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\egorov22\AppData\Local\Programs\Python\Python312\Lib\multiprocessing\reduction.py", line 51, in dumps
    cls(buf, protocol).dump(obj)
TypeError: cannot pickle '_tkinter.tkapp' object
python tkinter python-3.12
1个回答
0
投票

您无法跨进程使用 tkinter 小部件或与之交互。这根本不可能。

最简单的解决方案是设置一个多处理队列。当有数据需要更新时,工作进程可以写入队列,并且 UI 进程可以使用

after
轮询该队列,以从队列中提取数据并更新 UI。

您必须注意队列的轮询不会阻塞 UI 线程超过 100 毫秒左右,否则 UI 会显得迟缓。

© www.soinside.com 2019 - 2024. All rights reserved.