Как отобразить и обновить сообщение или метку, связывающую результаты, используя TopLevel Tkinter внутри a для l oop на каждой итерации в Python? - PullRequest
0 голосов
/ 13 марта 2020

Я строю код GUI Python, используя tkinter. У меня есть модель CSTR, которая решается внутри цикла for; значения обновляются внутри для l oop. У меня есть четыре параметра, которые обновляются T, Ca, T c, q. Я использую TopLevel () для отображения результатов КАЖДОЙ итерации внутри для l oop. Другими словами, я хочу, чтобы результаты на каждой итерации отображались после того, как результаты отображаются на дисплее, и как только я нахожусь в другом взаимодействии, я хочу снова открыть новый файл окна и обновить значения новыми вычисленными значениями в для l oop. Я использовал функцию уничтожения, используя то же время итераций, что и для l oop.

. Пожалуйста, найдите мой код, прикрепленный ниже (я приложил часть кода только для простоты):

 # Time Interval (min)
t = np.linspace(0,800,401) # every 2 seconds
duration=8000 # I also tried 2000,4000,6000,1000,etc.

    # Simulate CSTR
    for i in range(len(t)-1):
        # simulate one time period (0.02 sec each loop)
        ts = [t[i],t[i+1]]
        #read CSTR Process T and Ca (to be controlled)

        y = odeint(cstr,x0,ts,args=(u[i],u2[i],Tf,Caf))
        # retrieve measurements
        Ca[i+1] = y[-1][0]
        T[i+1] = y[-1][1]
        # insert measurement
        m.T.MEAS = T[i+1]
        m.Ca.MEAS=Ca[i+1]

        #run MPC controller

        # solve MPC
        m.solve(disp=True)

        # retrieve new Tc and q values

        u[i+1] = m.Tc.NEWVAL
        u2[i+1]=m.q.NEWVAL

        #run optimization

        x=optimizer(numa,numb,numc,numd,Tsp[i+1],Casp[i+1],u[i+1],u2[i+1],numi,numj,nume,numf,numg,numh,numk,numl) # or put T and Ca (not sp)


        #updating the setpoints of the controller from the optimizer

        #Set the setpoint temperature equal to the optimized Temperature

        Tsp[i+1]=x[1]

        m.T.SPHI = Tsp[i+1] + DT
        m.T.SPLO = Tsp[i+1] - DT

        #Set the setpoint conc. equal to the optimized conc.
        Casp[i+1]=x[4]

        m.Ca.SPHI = Casp[i+1] + DT
        m.Ca.SPLO = Casp[i+1] - DT

        # retrieve new Tc and q values

        #update Tc and q values from optimizer as targets


        f[i]=m.Tc.NEWVAL #MPC
        f2[i]=x[2] #Optimizer (target)

        z[i]=m.q.NEWVAL #MPC
        z2[i]=x[3] #Optimizer (target)



        x2=optimize(f[i],f2[i],nume,numf)# MINIMIZES THE ERROR DIFFERENCE BETWEEN MPC AND OPTIMIZER Tc value
        x3=optimize2(z[i],z2[i],numg,numh)# MINIMIZES THE ERROR DIFFERENCE BETWEEN MPC AND OPTIMIZER q value

        #updating the Tc and q MV values

        u[i+1] = x2
        u2[i+1]=x3

        # update initial conditions
        x0[0] = Ca[i+1]
        x0[1] = T[i+1]
        #to show the results on GUI

        top1=Toplevel()

        output1=" q optimum= %d m^3/s \n"%(z[i]) #takes the output q of MPC
        output1+="q target= %d m^3/s \n"%(z2[i])#takes the output of the optimizer
        output1+="Tc optimum= %d K \n"%(f[i])#takes the output Tc of MPC
        output1+="Tc target= %d K \n"%(f2[i])#takes the output of the optimizer
        top1.title('Manipulated Variables')

        Message(top1,text=output1,padx=30,pady=30).pack()
        top1.after(duration,top1.destroy)


На самом деле происходит то, что окно открывается только после того, как выполнены все циклы цикла for (это занимает много времени), а не на каждой итерации / расчете в for l oop! Как я могу отобразить окно внутри для l oop на каждой итерации, а НЕ после того, как все l oop сделано? Есть ли какая-то функция или что-то, где я могу вручную заставить окно отображаться? Например, я смог показать графики с использованием модуля plt, которые обновляются на каждой итерации внутри для l oop, я хочу сделать то же самое с помощью toplevel ().

Есть предложения?

Спасибо.

1 Ответ

1 голос
/ 14 марта 2020

В GUI ваш код в основном guest в событии GUI toolkit l oop (mainloop в tkinter).

Так что вы не можете иметь длительное выполнение l oop в программе GUI; Вы должны структурировать свой код по-другому. Например, скажем, что у вас есть oop, как это;

# *Long* list of data
data = [ ... ]
# storage for results
result = []
for item in data:
    rv = process(item)
    result.append(rv)

Если бы вы запускали такое al oop в GUI программе, это прервало бы поток событий. По сути, он замораживает GUI и рендеринг не отвечает.

Так что в tkinter GUI вам придется выполнять вычисления в обработчике тайм-аута. Ниже приведен набросок принципа разделения длительной работы на маленькие кусочки, которые можно обработать внутри события GUI -l oop.

import tkinter as tk
from tkinter import messagebox

# *Long* list of data
data = [ ... ]
# storage for results
result = []
index = 0

def iteration():
    '''Handles one iteration in the event loop.'''
    rv = process(data[index])
    result.append(rv)
    index += 1
    if index < len(data):
        root.after(100, iteration)
    else:
        messagebox.showinfo('Calculations', 'Finished!')


def create_UI():
    # Create and place the widgets here.
    pass


if __name__ == '__main__':
    root = tk.Tk(None)
    widgets = create_UI(root)
    root.after(100, iteration)
    root.mainloop()

Как видите, ваш В этом случае код должен иметь другую структуру.

Обратите внимание, что на CPython только один поток может одновременно выполнять Python байт-код, поэтому использование потоков не является действительно хорошее решение для этого. Тем более что многие GUI наборы инструментов не являются поточно-ориентированными, поэтому вы должны только вызывать GUI функций из одного потока.

Другим решением было бы запустить вычисление в другая программа. Эта программа может затем связаться с GUI с помощью Queue. Вы должны будете использовать функцию тайм-аута в GUI для регулярного отслеживания очереди и отображения результатов. Это не простое решение, но может предложить более высокую производительность, если вы можете разделить вычисления на несколько процессов.

Редактировать :

Обратите внимание, что в первом решении, каждая итерация должна занимать относительно небольшое количество времени, например, 100 мс. Если бы это заняло намного больше времени, GUI чувствовал бы, что он не отвечает. Если это невозможно, используйте второе решение (отдельную программу).

У меня нет удобной ссылки на эту проблему. Но простой шаблон выглядит следующим образом:

Вы пишете две программы.

Первая программа - это простой Python скрипт, который выполняет свои вычисления в обычном порядке. л oop. Единственная особенность заключается в том, что в конце каждой итерации записывает в файл значения T, Ca, T c и q.

Вторая программа - это Python Программа tkinter, использующая метод after для проверки времени модификации вышеупомянутого файла. Если он обнаруживает, что файл изменился, он читает файл и обновляет его отображение.

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

Общее название этого решения, я думаю, называется «наблюдателем за файлами». Такое решение встречается настолько часто, что я не могу вспомнить, где я это говорю первым.

...