Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: Unnamed_Hero от Июнь 23, 2008, 13:04



Название: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: Unnamed_Hero от Июнь 23, 2008, 13:04
Пытаюсь понять, как корректно работать с tabWidget'ом.
Есть у меня нарисованный в дизайнере виджет. В классе, его описывающем, ещё присутствует некоторое количество вычислений, ради чего внутри класса создаются объекты пары других классов.

По нажатию кнопки в главной программе - создаётся новый объект этого виджета и добавляется на новую вкладку tabWidget'a. 

По нажатию другой кнопки - надо грохнуть текущий виджет из текущей вкладки  (и саму вкладку) и удалить из памяти всё то, что он насоздавал.

Как это сделать наиболее корректно? В моей реализации у меня гигантские утечки памяти :(


Название: Re: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: lit-uriy от Июнь 23, 2008, 13:28
покажи как сам делаешь


Название: Re: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: lit-uriy от Июнь 23, 2008, 13:33
чтоб добавить виджет, делай так:
tab->addTab(new MyWidget(this), tr("Вкладка №1"));
чтобы удалить текущую вкладку, делай так
tab->removeTab( currentIndex ());


Название: Re: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: Unnamed_Hero от Июнь 23, 2008, 14:50
Сделал по написанному. Тоже самое.
Но если верить Assistant'у

Цитата: QT Assistant
void QTabWidget::removeTab ( int index )
Removes the tab at position index from this stack of widgets. The page widget itself is not deleted

Да если верить valdrind'у - объекты классов, которые создаются в конструкторе виджета - не удаляются :(


Название: Re: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: Пантер от Июнь 23, 2008, 16:59
А что мешает самому удалить свой виджет?


Название: Re: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: Unnamed_Hero от Июнь 23, 2008, 21:57
А как корректно удалять его?

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

Наверное, будет проще выложить сюда кусок кода, чтобы было более наглядно. Только предупреждаю сразу - я начинающий, и поэтому код может быть ужасен :)


tabWidget->addTab (new char_Sheet (&someData,"sometabname"));

в конструкторе char_Sheet
создаются объекты других классов для проведения вычислений и преобразований.
В деструкторе класса все объявленные объекты delete'аются....
Когда я просто делаю removeTab, то память не освобождается.
Если создать массив указателей char_sheet *charArray[];
и говорить delete нужному номеру, то это освобождает часть памяти (удаляется сам виджет, а порождённые им объекты - висят в памяти.) Может, стоит всем классам вычислений ещё прописывать родителя, который их рождает, чтобы они вместе с ним удалялись?



Название: Re: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: Пантер от Июнь 23, 2008, 22:00
Да, нужно указывать родителя, который и поубивает всех детей при разрушении. :-D


Название: Re: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: Alex03 от Июнь 24, 2008, 05:44
чтоб добавить виджет, делай так:
tab->addTab(new MyWidget(this), tr("Вкладка №1"));
чтобы удалить текущую вкладку, делай так
tab->removeTab( currentIndex ());
Вы б определились что у Вас не удаляется, если сама закладка то:
Код:
    QWidget * pw = widget(currentIndex ());
    tab->removeTab( currentIndex ());
    delete pw;



Название: Re: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: Red Devil от Июнь 28, 2008, 23:23
Хочу обратить внимание на то, что в QWidget не виртуальный деструктор.
Поэтому перед удалением, необходимо преобразовать указатель к тому обьекту для которого была выделена память изначально, иначе будут утечки памяти.
Неправильно :
Код:
m_pTab->addWidget(new QTextEdit, "New file");
...
QWidget * pWidget = m_pTab->currentWidget();
m_pTab->removeTab( currentIndex () );
delete pWidget;

Правильно :
Код:
m_pTab->addWidget(new QTextEdit, "New file");
...
QTextEdit * pEdit = static_cast<QTextEdit*>(m_pTab->currentWidget());
m_pTab->removeTab( currentIndex () );
delete pEdit;


Название: Re: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: Sergeich от Июнь 29, 2008, 00:07
Хочу обратить внимание на то, что в QWidget не виртуальный деструктор.
С чего ты взял что он не виртуальный? У QObject - виртуальный деструктор - следовательно он виртуальный у всех его потомков, с том числе и QWidget.


Название: Re: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: Red Devil от Июнь 29, 2008, 00:20
Да точно. Я забыл про QObject. Проверил и убедился, все правильно удаляется.

Но кстати вот еще проблема появилась. При удалении обьекта, в трейсе видно сообщение :
QObject: Do not delete object, 'unnamed', during its event handler!

Вот я где-то (на страничку эту уже не зайти, я её открыл с помощью кеша гугла) нашел обсуждение по его поводу.
Цитировать
Неоднократно замечены проблемы с перекрытием в обработке событий/сигналов
и
удаления объекта с этими обработчиками.

На QT4.4 он начал предупреждать о подобном:
QObject: Do not delete object, 'unnamed', during its event handler!

Но не понятно другое, как это возможно в одном потоке и как это разруливать?
Ситуация достаточно неочевидна и перестройкой пользовательского кода
часто не
лечится или лечится только путём потери функциональности.

Например ситуация:
- инспектор атрибутов генерирует сигнал на обновления виджета при потере
фокуса; в процессе обновления удаляется вложенный виджет и вместо него
создаётся новый;
- в тоже время фокус теряется по причине выбора этого самого виджета
(удаляемого), т.е. какие-то события пошли к нему;
- в результате генерируется "QObject: Do not delete object, 'unnamed', during
its event handler!" и происходит обрушение.

Я не понимаю как такое может вообще происходить, разве в деструкторе не
откидываются из очереди события к удаляемому виджету?

Порылся в коде QT4.4 и меня насторожил вот этот участок кода в функции
QCoreApplication::notifyInternal( ) файла qcoreapplication.cpp:
    bool returnValue;
    try {
        returnValue = notify(receiver, event);
    } catch(...) {
        --threadData->loopLevel;
        throw;
    }
Получается при возникновении исключения оно передаётся на уровень выше, а
флаг
d->inEventHandler не очищается, что и является причиной указанного сообщения, а
установка этого флага осуществляется только там. Не исключено, что не
очищается
ещё чего нибуть.

------- Comment #1 From Roman Savochenko 2008-05-26 21:45:26 -------

Created an attachment (id=2645) [details]
Программа демонстрации проблемы

Подготовил наглядный пример этой проблемы на основе примера из дистрибутива
QT4.4 - basiclayouts. Для кнопки Button1 добавил eventFilter() c целью отлова
потери фокуса. По результату потери фокуса удаляется кнопка Button2. Так вот,
если сначала нажать кнопку Button1, а затем Button3, то кнопка Button2
корректно удалится, а вот если после Button1 нажать Button2, то программа
упадёт по этой самой проблеме.

------- Comment #2 From Michael Shigorin 2008-05-31 02:43:48 -------

alterator-browser-qt - X11 Qt interface driver for alterator           
* Fri May 30 2008 Sergey V Turchin <zerg@altlinux> 2.9.83-alt1                 
                   
- fix #15792
* Fri May 30 2008 Sergey V Turchin <zerg@altlinux> 2.9.82-alt1
- fix *listbox state-rows attribute 
- force return *multi*listbox current-rows and state-rows if empty list
* Thu May 29 2008 Sergey V Turchin <zerg@altlinux> 2.9.81-alt1

------- Comment #3 From Sergey V Turchin 2008-06-02 17:26:37 -------

Я не уверен, это бага Qt или нет, но можно использовать deleteLater

------- Comment #4 From Roman Savochenko 2008-06-02 17:56:29 -------

Это не всегда возможно.
У меня был случай с QAbstractItemView::itemDelegate()
Когда я создаю в качестве элемента редактирования комбобокс(редактируемый) и
добавляю ему обработку события принятия выбора по выбору элемента.
Так вот, если я редактирую содержимое этого комбобокса руками и нажимаю Enter,
то приходит два события и он валится поскольку сама QT удалила этот элемент
после первого события.

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

------- Comment #5 From Sergey V Turchin 2008-06-10 17:27:30 -------

Похоже, в Trolltech думают, что это фича
http://trolltech.net/developer/task-tracker/index_html?method=entry&id=198093
http://trolltech.de/developer/task-tracker/index_html?method=entry&id=205903

------- Comment #6 From Roman Savochenko 2008-06-24 00:14:37 -------

Если бы оно только писало и не падало.
В приложенном примере нет моего кода, а оно устойчиво падает на банальной
ситуации.


Название: Re: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: Sergeich от Июнь 29, 2008, 00:33
Юзай QObject::deleteLater ()


Название: Re: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: Unnamed_Hero от Июнь 29, 2008, 10:51
Не знаю, насколько это корректно.... но я пока что поступаю таким образом -
Код:
int index = tabWidget->currentIndex();
QWidget* deadwidget =tabWidget-> widget(index);
deadwidget->close();
в конструкторе виджета для вкладки tabWidget
Код:
setAttribute(Qt::WA_DeleteOnClose);

всё удаляется... вроде :)
Во всяком случае - видно, что деструкторы вызываются для всех объектов.


Название: Re: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: Red Devil от Июнь 29, 2008, 12:02
Юзай QObject::deleteLater ()
Т.е. сам Qt удалит обьект, после того отработает цикл сообщений для него. И самому вручную delete использоваться получается не нужно.
Но вот хотелось бы удостоверится что это действительно так.


Название: Re: [Qt4] QTabWidget - кооректная чистка памяти
Отправлено: Sergeich от Июнь 29, 2008, 13:44
Юзай QObject::deleteLater ()
Т.е. сам Qt удалит обьект, после того отработает цикл сообщений для него. И самому вручную delete использоваться получается не нужно.
Но вот хотелось бы удостоверится что это действительно так.
Да, именно так.
Код:
void QObject::deleteLater()
{
    QCoreApplication::postEvent(this, new QEvent(QEvent::DeferredDelete));
}