Russian Qt Forum
Ноябрь 27, 2024, 07:54 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Удаление QTreeWidgetItem из вторичного потока  (Прочитано 6541 раз)
astramax57
Гость
« : Январь 06, 2009, 15:03 »

Доброго дня!

Итак, Qt 4.4.3, XP, 9-я студия.

Есть QtreeWidget. В него по мере поступления сообщений из дочернего потока добавляются item-ы:

Код:

        quint32 module_id;
quint32 message_status;
QStringList m_;

if(the_message_keeper->pop_message(module_id, message_status, m_))
{
QTreeWidgetItem *the_m = new QTreeWidgetItem();
the_m->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);

//date_time
tm the_moment = to_tm(posix_time::second_clock::local_time());
QString qstr_time(QString("[%1.%2 %3:%4:%5]").arg(the_moment.tm_mday, 2, 10, QLatin1Char('0'))
.arg(the_moment.tm_mon + 1, 2, 10, QLatin1Char('0'))
.arg(the_moment.tm_hour, 2, 10, QLatin1Char('0'))
.arg(the_moment.tm_min, 2, 10, QLatin1Char('0'))
.arg(the_moment.tm_sec, 2, 10, QLatin1Char('0')));

the_m->setText(0, qstr_time);

//message source icon
the_m->setIcon(1, modules_icons[module_id ].get_icon());

//message type icon
the_m->setIcon(2, message_types_icons[message_status].get_icon());

QStringList::const_iterator the_string = m_.begin();
//message 1st string
the_m->setText(3, *the_string);

//message other strings
for(++the_string; the_string != m_.end(); ++the_string)
{
QTreeWidgetItem *current_child = new QTreeWidgetItem();
current_child->setText(3, *the_string);
the_m->addChild(current_child);
}
               //messages_from_topTV это и есть QTreeWidget
messages_from_topTV->addTopLevelItem(the_m);

Задача:
необходимо, чтобы количество итемов было не больше N. Если итемов N и приходит новое сообщение, то первый итем удаляется и новое сообщение записывается в конец списка. Удалять собираюсь так:
Код:
delete messages_from_topTV->TakeTopLevelItem(0);

Если пытаться что-то удалять из вторичного потока (т.е. из функции, добавляющей сообщения) то вылетает исключение.

Пытался так
Код:
QMetaObject::invokeMethod(messages_from_topTV, "takeTopLevelItem",Qt::QueuedConnection, Q_ARG(int, 0));

не работает, возвращает false. Да и память будет теряться, т.к. я не вызываю delete

Пытался блокировать сигналы (blockSignals) на время удаления - тоже исключения.

Может кто-нибудь подскажет решение.
Записан
Dendy
Гость
« Ответ #1 : Январь 06, 2009, 16:05 »

Гуёвые классы лучше использовать только из главного потока. QueuedConnection отрабатывает мгновенно и ничего не возвращает. Используйте BlockingQueuedConnection для синхронного вызова. Только убедитесь, что вызываемый метод обьявлен как слот.
Записан
Dendy
Гость
« Ответ #2 : Январь 06, 2009, 16:09 »

Да и память будет теряться, т.к. я не вызываю delete

BlockingQueuedConnection может возвращать значение, которое вы сможете удалить через delete. Главное убедиться что этот указатель после take() оказался для остальных потерян.
Записан
astramax57
Гость
« Ответ #3 : Январь 06, 2009, 17:11 »

Скорее всего дело в том, что taketoplevelitem это не слот, а обычный метод.
 Поэтому invokemethod и возвращает false. Как быть?

Я пробовал вариант, когда удаление "лишних" итемов производится в слоте, навешенном на какой-нибудь сигнал QTreeWidget.
Но проблема осталась: например я использую слот, подключенный к currentItemChanged.

Если я меняю currentitem не программно (щелкаю мышкой), все работает, т.к. слот работает в основном ГУИ-потоке.
А вот если я программно вызываю messages_from_topTV->setCurrentItem, то сыпятся исключения, т.к. слот работает во вторичном потоке.
Записан
Dendy
Гость
« Ответ #4 : Январь 06, 2009, 17:47 »

Всё верно сыпется, если в наследнике QWidget явно не указано, что метод потокобезопастный - значит его можно вызывать только из основного потока. Попытайтесь отвязать внешний вид программы от её данных. Если вы завязались с потоками, то вам сам Бог велел использовать QTreeView вместо QTreeWidget.
Записан
astramax57
Гость
« Ответ #5 : Январь 06, 2009, 18:44 »

Пробую Qtreeview + своя модель, унаследованная от QAbstractItemModel.

Приведенный в первом посте код меняется на
Код:
        quint32 module_id;
quint32 message_status;
QStringList m_;
if(the_message_keeper->pop_message(module_id, message_status, m_))
{
            //здесь уже messages_from_topTV это QTreeView
message_model *model = reinterpret_cast<message_model*>(messages_from_topTV->model());
message_model_item *message_item = new message_model_item();


//date_time
tm the_moment = to_tm(posix_time::second_clock::local_time());
QString qstr_time(QString("[%1.%2 %3:%4:%5]").arg(the_moment.tm_mon + 1, 2, 10, QLatin1Char('0'))
.arg(the_moment.tm_mday, 2, 10, QLatin1Char('0'))
.arg(the_moment.tm_hour, 2, 10, QLatin1Char('0'))
.arg(the_moment.tm_min, 2, 10, QLatin1Char('0'))
.arg(the_moment.tm_sec, 2, 10, QLatin1Char('0')));
message_item->setItemTime(QVariant(qstr_time));

//message source icon
message_item->setItemModuleIcon(QVariant(modules_icons[module_id].get_icon()));

//message type icon
message_item->setItemStatusIcon(QVariant(message_types_icons[message_status].get_icon()));

QStringList::const_iterator the_string = m_.begin();
//message 1st string
message_item->setItemMessage(QVariant(*the_string));

//message other strings
for(++the_string; the_string != m_.end(); ++the_string)
{
message_model_item *current_child = new message_model_item();
current_child->setItemMessage(QVariant(*the_string));
message_item->insertChild(message_item->childCount(), current_child);
}

model->insertTopLevelRow(message_item);
int item_count = model->topLevelItemCount();

if( item_count > count_lines)
{
model->removeRow(0);
}




removeRow принадлежит QAbstractItemModel
Код:
inline bool QAbstractItemModel::removeRow(int arow, const QModelIndex &aparent)
{ return removeRows(arow, 1, aparent); }

а removeRows реализована у меня в модели как
Код:
bool message_model::removeRows(int position, int rows, const QModelIndex &parent)
{
    message_model_item *parentItem = getItem(parent);
    bool success = true;

    beginRemoveRows(parent, position, position + rows - 1);
    success = parentItem->removeChildren(position, rows);
    endRemoveRows();

    return success;
}

и опять исключения в функции size контейнера qlist.
Поскольку все методы qlist реентерабельны, мне нужно защищать сам список мьютексом, так?
Записан
Dendy
Гость
« Ответ #6 : Январь 06, 2009, 19:54 »

Что-то мне подсказывает что так использовать модель нельзя. Модель - это интерфейс для доступа к данным из GUI классов. Она тоже должна работать в основном потоке. В другой поток можно вынести операции по работе с самими данными. Модель же должна только информировать о том что данные изменились.
Записан
SABROG
Гость
« Ответ #7 : Январь 06, 2009, 20:38 »

А можно позырить метод QThread::run() и участок где он вызывается ?
Записан
astramax57
Гость
« Ответ #8 : Январь 06, 2009, 21:22 »

А можно позырить метод QThread::run() и участок где он вызывается ?

я использую boost::thread.
Может быть дело в этом?

А как грамотно использовать модель? Я в кьюте полный чайник. Ткните в пример плз
« Последнее редактирование: Январь 06, 2009, 21:23 от astramax57 » Записан
SABROG
Гость
« Ответ #9 : Январь 06, 2009, 22:57 »

я использую boost::thread.
Может быть дело в этом?

Попробуй на QThread протести и посмотри в сторону QApplication::postEvent().
Записан
vaprele07
Гость
« Ответ #10 : Январь 07, 2009, 09:22 »

QThreadStorage + QCache в ассистенте даже пример есть!
Записан
astramax57
Гость
« Ответ #11 : Апрель 08, 2009, 09:32 »

Проблему давно решил, надо и отписаться для приличия.

Я пытался удалять QTreeWidgetItem из вторичного потока, но это полбеды. Я еще их и добавлял в QTreeWidget не из гуи-потока.

Проблему решил с помощью CustomEvent, то есть при поступлении нового сообщения из вторичного потока я ставлю в очередь обработки новое событие (postEvent), а в гуи потоке, когда вызовется CustomEvent, я добавляю новый QTreeWidgetItem и при необходимости удаляю старые.

Спасибо всем ответившим!
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.051 секунд. Запросов: 21.