Python ProgressBar и графический интерфейс зависли, пока идет расчет графика

Может ли кто-нибудь помочь мне с потоками в python и заставить работать индикатор выполнения?

Даже исследования дают довольно много результатов, я не могу заставить их работать.

Я никогда раньше не работал с потоками, и я не могу сказать, где правильно размещать вещи.

Для тестирования я подготовил простой графический интерфейс с кнопкой и индикатором выполнения:

После нажатия на кнопку появится простой 3D-график.

Такое построение графика может занять некоторое время вычислений, и, пока пользователю нужно ждать, я хотел бы, чтобы графический интерфейс не зависал, а индикатор выполнения анимировался.

На данный момент графический интерфейс зависает, пока не появится сюжет. И после этого индикатор выполнения запускает анимацию.

Я много читал о многопоточности и о том, чтобы поместить вычисления и графический интерфейс в разные потоки? Но я просто нубиш, чтобы заставить его работать. Может ли кто-нибудь объяснить мне больше, направить меня к аналогичным проблемам или документации? Или, может быть, в случае быстрого решения, перегнать простой код и исправить его так, как он должен быть?

Заранее спасибо за любую помощь.

Скрипт Python на данный момент:

from time import sleep
from tkinter import EW
import ttkbootstrap as ttk
import numpy as np
import matplotlib.pyplot as plt

def main():

    def plot_sample():

        sleep(5) # simulate calculation times
        x = np.outer(np.linspace(-2, 2, 30), np.ones(30))
        y = x.copy().T # transpose
        z = np.cos(x ** 2 + y ** 2)

        fig = plt.figure()
        ax = plt.axes(projection='3d')

        ax.plot_surface(x, y, z,cmap='viridis', edgecolor='none')
        ax.set_title('Surface plot')
        plt.show()

    def progressbar_start():
        progressbar.grid(row=1, column=0, sticky=EW, padx=10, pady=10) # let progressbar appear in GUI
        progressbar.start(interval=10)

    def progressbar_stop():
        progressbar.stop()
        progressbar.grid_forget() # hide progressbar when not needed

    def button_clicked():
        progressbar_start()  # start progressbar before computation begins
        plot_sample() # plotting
        progressbar_stop()  # stop progressbar after plot is done



    # GUI
    # window size and settings
    root = ttk.Window()

    # Basic settings for the main window
    root.title('Test progressbar')
    root.minsize(300, 200)
    root.resizable(True, True)
    root.configure(bg='white')

    # grid settings for the main window in which LabelFrames are sitting in
    root.columnconfigure(0, weight=1)
    root.rowconfigure(0, weight=1)
    root.rowconfigure(1, weight=1)

    # Button fto show 3d-plot
    button_calc_3dplot = ttk.Button(root, text='Calculate 3D Plot', command=button_clicked)
    button_calc_3dplot.grid(row=0, column=0, padx=5, pady=5)

    progressbar = ttk.Progressbar(style='success-striped', mode='indeterminate')


    # end of GUI
    root.mainloop()


if __name__ == "__main__":
    main()
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

У меня есть одно решение. Основная проблема заключается в том, что вы не можете запустить функцию plt.show() в потоке. Из этого следует, что вы не можете запустить окно и функцию plt.show одновременно.

from time import sleep
from tkinter import EW
import ttkbootstrap as ttk
import numpy as np
import matplotlib.pyplot as plt
from threading import Thread

x, y, z = None, None, None


def main():
    def calculate_xyz():
        sleep(5)  # simulate calculation times
        global x, y, z
        x = np.outer(np.linspace(-2, 2, 30), np.ones(30))
        y = x.copy().T  # transpose
        z = np.cos(x ** 2 + y ** 2)

    def progressbar_start():
        progressbar.grid(row=1, column=0, sticky=EW, padx=10, pady=10)  # let progressbar appear in GUI
    progressbar.start(interval=10)

    def progressbar_stop():
        progressbar.stop()
        progressbar.grid_forget()  # hide progressbar when not needed

    def button_clicked():
        progressbar_start()  # start progressbar before computation begins
        calculate_xyz_thread = Thread(target=calculate_xyz)
        calculate_xyz_thread.start()  # calculate x, y and z
        root.after(500, wait_for_plotting)  # wait for plotting and stop animation after finishing

    def wait_for_plotting():
        if x is None or y is None or z is None:
            root.after(500, wait_for_plotting)  # run self again
            return  # wait for calculation
        # calculation is finished
        progressbar_stop()  # stop progressbar "after" plot is done
        ax = plt.axes(projection='3d')
        ax.plot_surface(x, y, z, cmap='viridis', edgecolor='none')
        ax.set_title('Surface plot')
        root.after(500, plot_3d)

    def plot_3d():
        plt.show()

    # GUI
    # window size and settings
    root = ttk.Window()

    # Basic settings for the main window
    root.title('Test progressbar')
    root.minsize(300, 200)
    root.resizable(True, True)
    root.configure(bg='white')

    # grid settings for the main window in which LabelFrames are sitting in
    root.columnconfigure(0, weight=1)
    root.rowconfigure(0, weight=1)
    root.rowconfigure(1, weight=1)

    # Button fto show 3d-plot
    button_calc_3dplot = ttk.Button(root, text='Calculate 3D Plot', command=button_clicked)
    button_calc_3dplot.grid(row=0, column=0, padx=5, pady=5)

    progressbar = ttk.Progressbar(style='success-striped', mode='indeterminate')

    # end of GUI
    root.mainloop()


if __name__ == "__main__":
    main()

Это хорошо работает, спасибо :) Спасибо за объяснение! Это мне очень помогло.

lilaaffe 05.01.2023 16:36

Вы можете изменить некоторые числа: 2x root.after(num, wait_for_plotting) и root_after(num, plot_3d). Первый влияет на время между обновлениями (обновление проверяет, вычислены ли все переменные [x, y и z]). Второй влияет на время после завершения расчета, подготовки графика и удаления индикатора выполнения. Вы можете уменьшить его, но если он равен нулю, индикатор выполнения все еще виден.

Elias 05.01.2023 16:44

я немного протестировал, и у меня есть весь этот процесс, работающий без метода 'plot_3d()'. Мне удалось успешно запустить индикатор выполнения с помощью plt.show() в методе «wait_for_plotting».

lilaaffe 05.01.2023 17:30

по какой причине вы добавили root.after(500, plot_3d) в «wait_for_plotting»?

lilaaffe 05.01.2023 17:38

После того, как я запустил plt.show() в функции wait_for_plotting, мой экран загрузки не обновился, и я все еще мог видеть индикатор выполнения.

Elias 06.01.2023 11:24

Другие вопросы по теме