Найти - Пользователи
Полная версия: Не вызывается деструктор самописного класса при работе с Tkinter
Начало » Python для новичков » Не вызывается деструктор самописного класса при работе с Tkinter
1 2 3 4
Dwarf
Есть самописный класс (config), отвечающий за хранение кое-каких настроек. В его деструкторе (метод __del__()) реализовано сохранение в файл.

Также есть класс, рисующий GUI на tkinter (gui). В нём на событие закрытия окна забинден отдельный метод, который выполняет сохранение в объект класса настроек с последующим выходом из программы.

Так метод забиден
parent.protocol("WM_DELETE_WINDOW", self._update_config_and_close)
А так в функции self._update_config_and_close() происходит выход из программы:
self.root.after(1000, self.root.quit)
(почему с задержкой 1000 - потому что это костыль ради обхода ещё одной непонятной мне проблемы)

Я создаю объект вышеупомянутого config и передаю его в конструктор gui, чтобы тот считывал и записывал в него настройки.
Но когда я закрываю окно tkinter'а (стандартно, в Windows, путём нажатия на красный крестик) и программа завершается с кодом 0 (т.е. всё в порядке) - сохранения конфига в файл не происходит.
Но если я вставляю в самом конце программы вызов деструктора вручную, то настройки нормально сохраняются:
config.__del__()

Похоже что без ручного вызова, деструктор не вызывается. Как такое может быть? Я всегда полагал, что деструктор обязательно должен вызваться до конца программы, чтобы подчищать ресурсы и т.д. Или интерпретатор python'а может в некоторых случаях игнорировать вызов деструкторов? (типа “а зачем, всё равно программа закрывается, а память и операционка подчистить может”)
4kpt
Где Вы взяли такую конструкцию???
self.root.after(1000, self.root.quit)
Зачем она нужна?
Каким образом можно еще выполнить выход из программы или закрытие базового окна?
Во вторых. Каким образом Вы передаете свой config в GUI? Возможно в этом кроется ошибка… Очень сумбурно. Как функция закрытия знает о существовании config?

P.S. Возможно это поможет Вам ответить на Ваш вопрос.
import Tkinter
class now:
    def __del__(self):
        print "object deleted"
def del_me_please():
    root.destroy()
a = now()
root = Tkinter.Tk()
root.geometry("200x200+100+100")
root.protocol("WM_DELETE_WINDOW", del_me_please)
root.mainloop()
Т.е. python при отсутствии ссылок на объект его удаляет еще до начала отрисовки окна, т.е. создал - увидел, что по тексту объект не используется - и удалил. Он не ждет удаления корневого окна :) Поэтому в Вашем случае нужно явно вызывать деструктор, чтобы программа держала объект для Вас а не фигачила его сразу :) Забыл сказать. Так работает метод подсчета ссылок. Сборщик мусора работает по-другому…
Dwarf
Где Вы взяли такую конструкцию??? Зачем она нужна?
Повторюсь, это для обхода другой проблемы. Хорошо, давайте для простоты заменим
self.root.after(1000, self.root.quit)
на
self.root.quit()
Всё равно, проблема с сохранением конфига остаётся.

Каким образом можно еще выполнить выход из программы или закрытие базового окна?
Не совсем понял к чему это. Или вы не знаете тонкостей tkinter? По умолчанию там закрытие работает само собой. Но мне вместо с закрытием нужно провести определенные действия, поэтому я написал свой обработчик.

Во вторых. Каким образом Вы передаете свой config в GUI?
Очень просто.

class Gui:
    def __init__(self, config, root, "другие параметры"):
        self.config = config
        self.parent.protocol("WM_DELETE_WINDOW", self._update_config_and_close)
    def _update_config(self):
        config.update("новые параметры")
    def _update_config_and_close(self):
        self._update_config()
        self.root.quit()
if __name__ == '__main__'
    root = Tk()
    config = Config('main.cfg')
    gui = Gui(config, root, "другие параметры")
    root.mainloop()
    # если не вызвать вручную - сохранения не произойдёт
    # config.__del__() 

Хочу добавить, что основной блок (if __name__ == ‘main’ …) вместе с класом конфига находится в одном файле, а класс Gui - в другом, если это важно.
4kpt
Во первых. Если Вы в программе прикручиваете такие костыли - это Вы не знаете не то что тонкостей, а и основ работы Tkinter. То что Вы написали используется крайне редко и со значениями далекими от 1000 :). И я практически уверен, что у Вас не тот случай.
Во-вторых. По вашему вопросу. Если код структурно соответствует тому, что Вы написали, то тогда все работает…
import Tkinter
class now:
    def __del__(self):
        print "object deleted"
    def update(self):
        print "object_updated"
class Gui:
    def __init__(self, config, root):
        self.config = config
        root.protocol("WM_DELETE_WINDOW", self._update_config_and_close)
    def _update_config(self):
        self.config.update()
    def _update_config_and_close(self):
        self._update_config()
        root.destroy()
if __name__ == '__main__':
    root = Tkinter.Tk()
    config = now()
    gui = Gui(config, root)
    root.mainloop()
Выдает на выходе
object_updated
object_deleted
Проблема в том, что вместо конструкции root.quit() необходимо использовать конструкцию root.destroy(). Так будет работать.
P.S. Зачем знать основы, если знаешь тонкости :)
Dwarf
Если Вы в программе прикручиваете такие костыли
Стараюсь без них. Пока у меня в программе только две странности (и соответственно 2 костыля). Одна из них - указана в этом топике.

А костыль Вы прикрутили, так как окно не хотело закрываться после нажатия на крестик…
Если вам интересно зачем я использую, могу пояснить. Вообще-то я хотел это оставить для отдельной темы, но если вам интересно - могу расписать и тут.
self.root.after(1000, self.root.quit)
Поясняю - у меня есть ещё класс Grid, наследующий Frame. В этом классе я компоную Entry и Canvas'ы в самодельную таблицу, а потом эту таблицу (объект класса Grid) вставляю в главное окно. Это происходит в классе Gui.

Так вот в Grid у меня есть функция, в которой работает бесконечный цикл, занимающийся приёмом сообщений по очереди (стандартный питоновский Queue) с последующим отражением информации, извлеченной из этих сообщений, в таблице.

По умолчанию, если этот бесконечный цикл работает - программа на нажатие красного крестика вообще не реагирует, несмотря на то, что в цикле стоят
self.root.update_idletasks()
self.root.update()
Благодаря им, понятное дело, интерфейс остаётся отзывчивым, вот только не закрывается.
Тогда я классе Grid реализовал внутреннюю переменную-флаг, который означает, что из цикла нужно выйти, и на каждой итерации проверяю его. Для установки этой переменной в Grid реализована функция total_exit(), которая ставит её в True.

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

Проблема в том, выход из цикла происходит не мгновенно (пока очередная итерация дойдёт до проверки переменной-флага, пока выйдет), и поэтому в _update_config_and_close() нужна пауза между вызовом total_exit() и непосредственно закрытием окна через root.quit(). Если эту паузу не реализовать - на кнопку закрытия окна приходится жать дважды, т.к. пока выход из цикла не произведен - вызов root.quit() игнорируется.
4kpt
У меня возникает подозрение, что Вы взяли число 1000 из этого кода…
import Tkinter
import ScrolledText
import time
def sound(event = None):
    time.sleep(1)
    print u"Выполняем запрос к микрофону..."
    if event%5 != 0: print u"Пользователь молчит...\n"
    else: return (event)
def get_data(event = None):
    i = 0
    while 1:
        i += 1
        print u"Посылаем запрос на снятие данных..."
        x = sound(i)
        if x != None and x%5 == 0 and x < 20 :
            print u"Пользователь сказал слово...\n"
            text.insert("current", u"Пользователь сказал слово через %s вызов" %x + u"\nГоворите\n")
            text.update()
        elif x == 20:
            break
root=Tkinter.Tk()
text = ScrolledText.ScrolledText(root)
text["font"] = "Times New Romain", 14
text.insert("0.end", u"Говорите\n")
text.after(1000, get_data)
text.focus_force()
text.pack()
root.mainloop()
Dwarf
4kpt боюсь вы не правы, этот код я вижу впервые, а 1000 я подобрал более или менее эмпирическим путём.
4kpt
Просто цифры совпали :) Я и подумал…
4kpt
Не думали сделать через потоки?
Dwarf
Не думали сделать через потоки?
Что именно?
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB