"Улетают" виджеты после 10 минутной работы tkinter

Разрабатываю приложение для вывода информации с плк на пк. После 10 минутной работы программы, все читаемые переменные улетают в один левый верхний угол (показано на скриншоте).Что может повлиять на поведение программы? Немного пояснения: пошагово до появление этой проблемы разбирался с методом расположения grid(). Все отображалось и находилось в заданных местах. Но примерно через 10 минут работы они "улетают". 2. Программа разрабатывается под конкретное железо для вывода информации, поэтому было сделано так (добавил скрин с расположением как только запускаю программу, все на своих местах находится). Первый раз занимаюсь разработкой приложений на python.

import tkinter as tk
from tkinter import ttk
from tkinter import *
from threading import Timer
from pymodbus.client import ModbusTcpClient as ModbusClient
import multiprocessing


class App(tk.Tk):  # Главное окно
    def __init__(self):  # Инициализация главного окна
        super().__init__()
        self.title('Тандем 3')  # Заголовок окна
        self.geometry('1920x1000')  # Размер в пикселях окна
        self.resizable(False, False)  # Запрет на растягивания окна
        self['background'] = 'lightskyblue1'
        self.Connect_to_PLC()
        self.create_widgets()  # Отображение фреймов

    def Connect_to_PLC(self):  # Подключение к ПЛК в качестве клиета
        print('Start Modbus Client')  # Вывод надписи в консоль для проверки работы функции
        global client  # Присваивание глобального типа к переменной для доступа других функций
        global reg  # Присваивание глобального типа к переменной для доступа других функций
        global address  # Присваивание глобального типа к переменной для доступа других функций
        client = ModbusClient(host='192.168.100.50', port=502, auto_open=TRUE)  # ПЛК выступает в роли сервера
        reg = 0
        address = 0

    def create_widgets(self):  # Отображение фреймов
        for i in range(4):  # Создание в цикле фреймов экструдеров
            FrameExtr(self, i + 1).grid(row=0, column=0 + i, columnspan=1, padx=50, pady=10, ipadx=20, ipady=10,
                                        sticky=tk.N)  # Вызов класса экструдера и привязки их по сетке


class FrameExtr(ttk.Frame):  # Отображение фреймов
    def __init__(self, container, i):
        super().__init__(container)
        style = ttk.Style()
        style.configure('My.TFrame', relief='solid', background='gray94')
        self.configure(style='My.TFrame')
        self.choice_extr(i)
        self.create_widgets_static()
        self.process_temp = multiprocessing.Process(target=self.repeater(5, self.update_temp_fields))
        self.process_extr = multiprocessing.Process(target=self.repeater(2, self.update_extr_fields))

    def choice_extr(self, i):
        if i == 1:
            self.numberExtr = 1
            self.nameExtr = '120/1'
            self.quantity_zone = 11
            self.offset = 0
        if i == 2:
            self.numberExtr = 2
            self.nameExtr = '120/2'
            self.quantity_zone = 10
            self.offset = 26
        if i == 3:
            self.numberExtr = 3
            self.nameExtr = '90/1'
            self.quantity_zone = 11
            self.offset = 52
        if i == 4:
            self.numberExtr = 4
            self.nameExtr = '45'
            self.quantity_zone = 10
            self.offset = 78

    def create_widgets_static(self):
        ttk.Label(self, text=f'Экструдер {self.nameExtr}', font=("Times", 32), anchor='center').grid(row=0, column=0,
                                                                                                     columnspan=4)
        ttk.Label(self, text='Текущие обороты', font=("Times", 18)).grid(row=1, column=0, columnspan=2)
        ttk.Label(self, text='Текущий ток', font=("Times", 18)).grid(row=2, column=0, columnspan=2)
        ttk.Label(self, text='Текущая', font=("Times", 18)).grid(row=3, column=1)
        ttk.Label(self, text='Заданная', font=("Times", 18)).grid(row=3, column=2)

        for i in range(self.quantity_zone):
            ttk.Label(self, text=f'Зона {i + 1}', font=("Times", 24)).grid(row=4 + 2 * i, column=0, padx=5, pady=10)

    def repeater(self, interval, function):
        Timer(interval, self.repeater, [interval, function]).start()
        function()

    def update_extr_fields(self):
        self.text_extr_vars = [StringVar() for _ in range(2)]
        for i in range(2):
            if i == 0:
                ttk.Label(self, textvariable=self.text_extr_vars[i], font=("Times", 30), background='green', width=5,
                          anchor='center', relief='ridge', padding=3).grid(row=1, column=2)
            else:
                ttk.Label(self, textvariable=self.text_extr_vars[i], font=("Times", 30), background='blue', width=5,
                          anchor='center', relief='ridge', padding=3).grid(row=2, column=2)
        for i in range(2):
            self.text_extr_vars[i].set(client.read_holding_registers(reg + self.offset + 22 + i, 1).registers)

    def update_temp_fields(self):
        # Создаем список для хранения объектов StringVar
        self.text_vars = [StringVar() for _ in range(self.quantity_zone * 2)]
        # Создаем текстовые поля с привязкой к соответствующим StringVar
        for i in range(self.quantity_zone * 2):
            # self.text_vars[i].set(client.read_holding_registers(reg + i, 1).registers)
            if i % 2 == 0:
                ttk.Label(self, textvariable=self.text_vars[i], font=("Times", 30), background='yellow', width=5,
                          anchor='center', relief='ridge', padding=3).grid(row=4 + i, column=1)
            else:
                ttk.Label(self, textvariable=self.text_vars[i], font=("Times", 30), background='red', width=5,
                          borderwidth=8, anchor='center').grid(row=3 + i, column=2)
        # Обновляем текст в текстовых полях
        for i in range(self.quantity_zone * 2):
            self.text_vars[i].set(client.read_holding_registers(reg + self.offset + i, 1).registers)


if __name__ == "__main__":
    app = App()
    app.mainloop()

введите сюда описание изображения улетают значения из красных, желтых, синих и зеленых виджетов в левый верхний угол (на экран с pycharm)


Ответы (1 шт):

Автор решения: Stanislav Volodarskiy

Функция update_temp_fields вызывается по таймеру каждые пять секунд. На каждый вызов она создаёт пару десятков объектов StringVar и пару десятков полей ttk.Label. Предыдущие переменные и поля не удаляются. Новые поля рисуются поверх старых. Число полей во окне растёт неограниченно, а tkinter использует шестнадцатибитные переменные для индексов полей. В конце концов счётчик переполняется, портятся внутренние структуры и программа падает.

Поправить можно так:

  • код создания переменных и полей перенести в create_widgets_static:
    def create_widgets_static(self):
        ...
  
        self.text_vars = [StringVar() for _ in range(self.quantity_zone * 2)]

        for i in range(self.quantity_zone * 2):
            if i % 2 == 0:
                ttk.Label(self, ...).grid(row=4 + i, column=1)
            else:
                ttk.Label(self, ...).grid(row=3 + i, column=2)
        ...
  • в update_temp_fields оставить только обновление переменных:
    def update_temp_fields(self):
        for i in range(self.quantity_zone * 2):
            self.text_vars[i].set(...)

Аналогичная проблема есть в функции update_extr_fields, её тоже надо исправить.

P.S. Код реально ужасен: неправильно использованы библиотеки, неправильно использованы средства языка. Наплачетесь вы с этой программой, её надо переписать полностью.

→ Ссылка