Уведомления

Группа в Telegram: @pythonsu

#1 Март 1, 2013 21:57:49

Dwarf
От:
Зарегистрирован: 2011-05-09
Сообщения: 34
Репутация: +  0  -
Профиль   Отправить e-mail  

Не вызывается деструктор самописного класса при работе с Tkinter

Есть самописный класс (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'а может в некоторых случаях игнорировать вызов деструкторов? (типа “а зачем, всё равно программа закрывается, а память и операционка подчистить может”)



Отредактировано Dwarf (Март 1, 2013 22:01:10)

Офлайн

#2 Март 1, 2013 22:19:53

4kpt
От: Харьков
Зарегистрирован: 2010-11-03
Сообщения: 998
Репутация: +  63  -
Профиль   Отправить e-mail  

Не вызывается деструктор самописного класса при работе с Tkinter

Где Вы взяли такую конструкцию???

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 при отсутствии ссылок на объект его удаляет еще до начала отрисовки окна, т.е. создал - увидел, что по тексту объект не используется - и удалил. Он не ждет удаления корневого окна :) Поэтому в Вашем случае нужно явно вызывать деструктор, чтобы программа держала объект для Вас а не фигачила его сразу :) Забыл сказать. Так работает метод подсчета ссылок. Сборщик мусора работает по-другому…



Отредактировано 4kpt (Март 1, 2013 22:31:30)

Офлайн

#3 Март 1, 2013 22:35:35

Dwarf
От:
Зарегистрирован: 2011-05-09
Сообщения: 34
Репутация: +  0  -
Профиль   Отправить e-mail  

Не вызывается деструктор самописного класса при работе с Tkinter

Где Вы взяли такую конструкцию??? Зачем она нужна?
Повторюсь, это для обхода другой проблемы. Хорошо, давайте для простоты заменим
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 - в другом, если это важно.



Отредактировано Dwarf (Март 1, 2013 22:38:32)

Офлайн

#4 Март 1, 2013 23:33:34

4kpt
От: Харьков
Зарегистрирован: 2010-11-03
Сообщения: 998
Репутация: +  63  -
Профиль   Отправить e-mail  

Не вызывается деструктор самописного класса при работе с Tkinter

Во первых. Если Вы в программе прикручиваете такие костыли - это Вы не знаете не то что тонкостей, а и основ работы 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. Зачем знать основы, если знаешь тонкости :)



Отредактировано 4kpt (Март 1, 2013 23:54:02)

Офлайн

#5 Март 2, 2013 00:06:36

Dwarf
От:
Зарегистрирован: 2011-05-09
Сообщения: 34
Репутация: +  0  -
Профиль   Отправить e-mail  

Не вызывается деструктор самописного класса при работе с Tkinter

Если Вы в программе прикручиваете такие костыли
Стараюсь без них. Пока у меня в программе только две странности (и соответственно 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() игнорируется.



Отредактировано Dwarf (Март 2, 2013 00:18:12)

Офлайн

#6 Март 2, 2013 00:30:22

4kpt
От: Харьков
Зарегистрирован: 2010-11-03
Сообщения: 998
Репутация: +  63  -
Профиль   Отправить e-mail  

Не вызывается деструктор самописного класса при работе с Tkinter

У меня возникает подозрение, что Вы взяли число 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()



Офлайн

#7 Март 2, 2013 00:39:20

Dwarf
От:
Зарегистрирован: 2011-05-09
Сообщения: 34
Репутация: +  0  -
Профиль   Отправить e-mail  

Не вызывается деструктор самописного класса при работе с Tkinter

4kpt боюсь вы не правы, этот код я вижу впервые, а 1000 я подобрал более или менее эмпирическим путём.



Офлайн

#8 Март 2, 2013 00:41:30

4kpt
От: Харьков
Зарегистрирован: 2010-11-03
Сообщения: 998
Репутация: +  63  -
Профиль   Отправить e-mail  

Не вызывается деструктор самописного класса при работе с Tkinter

Просто цифры совпали :) Я и подумал…



Отредактировано 4kpt (Март 2, 2013 00:54:02)

Офлайн

#9 Март 2, 2013 00:48:57

4kpt
От: Харьков
Зарегистрирован: 2010-11-03
Сообщения: 998
Репутация: +  63  -
Профиль   Отправить e-mail  

Не вызывается деструктор самописного класса при работе с Tkinter

Не думали сделать через потоки?



Офлайн

#10 Март 2, 2013 01:02:38

Dwarf
От:
Зарегистрирован: 2011-05-09
Сообщения: 34
Репутация: +  0  -
Профиль   Отправить e-mail  

Не вызывается деструктор самописного класса при работе с Tkinter

Не думали сделать через потоки?
Что именно?



Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version