Russian Qt Forum

Qt => Общие вопросы => Тема начата: ZeBriD от Март 13, 2009, 14:31



Название: QThread && QApplication::postEvent()
Отправлено: ZeBriD от Март 13, 2009, 14:31
День добрый.
Столкнулся со следующей проблемой, и никак не могу сообразить, как её решить.
Суть проблемы такова: надо обработать большое кол-во файлов (я обрабатываю для теста чуть более 32тыс. таковых).
Дабы отменить этот процесс, а также быть в курсе прогресса, решил во время обработки файлов выводить прогрессбар с кнопкой отмены.
Первый вариант, который я попробовал - это добавить в функцию обработки файлов QApplication::postEvent();
И оно в принципе работало. Прогресс отображался, при нажатии на кнопку - останавливался.
Но! обработка пошла в десятки раз медленнее, что мне совсем не понравилось.
Тогда я решил использовать отдельный поток для отображение прогресса.
Создал поток, наследник QThread, в котором был один сигнал, на прекращение процесса, и один слот, для увелинчения прогресса на 1.
Но и тут всё пошло не чисто.
При обработке файлов стабильно сыпятся сообщения "QCoreApplication::sendPostedEvents: Cannot send posted events for objects in another thread".
Да и ко всему прочему, нажать на отмену я так и не могу, пока не вставлю в код потока обработчик событий, который, опять же, замедляет в десятки раз процесс обработки файлов.

Собственно, не подскажете, как заставить этот поток работать отдельно от основного, дабы таки была возможность отменить процесс, без добавления QApplication::postEvent() ?


Название: Re: QThread && QApplication::postEvent()
Отправлено: BRE от Март 13, 2009, 14:36
Попробуй сделать наоборот, т.е. файлы обрабатывай в отдельном потоке, а прогресс-бар рисуй и кнопку обрабатывай в основной потоке.


Название: Re: QThread && QApplication::postEvent()
Отправлено: ZeBriD от Март 13, 2009, 17:55
Часть обработки файлов заключается в формировании QTreeWidget, который лежит на главной форме.
Поэтому, перенести обработку файлов в отдельный поток не получится, ибо QTreeWidget будет лежать не в нём, а значит и обрабатываться нормально не сможет...
Какие есть ещё варианты ?


Название: Re: QThread && QApplication::postEvent()
Отправлено: BRE от Март 13, 2009, 18:02
Часть обработки файлов заключается в формировании QTreeWidget, который лежит на главной форме.
Поэтому, перенести обработку файлов в отдельный поток не получится, ибо QTreeWidget будет лежать не в нём, а значит и обрабатываться нормально не сможет...
Какие есть ещё варианты ?
Вариант тот-же.
Посылай из потока обработки сигнал addFileName( const QString &filename ), а в главном потоке добавляй в дерево.
Имя сигнала и параметры можно менять.


Название: Re: QThread && QApplication::postEvent()
Отправлено: Пантер от Март 13, 2009, 18:33
Столкнулся тут на днях с такой же проблемой. Есть модель со списком файлов, нужно поизвлекать их иконки, что есть очень долго. Сделал так: формирую модель,запускаю поток, передав ему имена файлов, в потоке выбирается иконка и делается emit(int,QIcon), что ловится в моделе на слот и устанавливается в модель. Все под масдаем шло отлично, но под Линем сказало, что с пиксмапом не будет работать не в гуи треде. Что делать ХЗ...


Название: Re: QThread && QApplication::postEvent()
Отправлено: spirit от Март 13, 2009, 18:39
юзай QImage. QPixmap нельзя юзать вне главного потока.
из ассистанта, см.Thread Support in Qt
Цитировать
Painting in Threads
...Painting onto QPixmaps and QWidgets is not supported....


Название: Re: QThread && QApplication::postEvent()
Отправлено: ZeBriD от Март 13, 2009, 18:46
Вариант тот-же.
Посылай из потока обработки сигнал addFileName( const QString &filename ), а в главном потоке добавляй в дерево.
Имя сигнала и параметры можно менять.
Уже думал об этом... Но, после прикидки, понял, что либо получится через чур перенавороченые сигналы с кучей параметров, либо ничего не получится...
Чтобы было понятнее, у меня обработка файлов тесно связана с QTreeWidget:
Код:
  QTreeWidgetItem *locwi;
  bool same[] = {false, true, true, true, true, true, true, true};
  for (int i = 0; i < filModel->rowCount(*mi); i++)
  {
    if ((filModel->fileInfo(mi->child(i,0)).isDir()) && (filModel->rowCount(mi->child(i,0)) > 0))
    {
      locwi = new QTreeWidgetItem(wi);
      locwi->setText( 0, filModel->fileName(mi->child( i, 0 )));
      locwi->setIcon( 0, filModel->fileIcon(mi->child( i, 0 )));
      locwi->setText( 8, filModel->filePath(mi->child( i, 0 )));
      locwi->setText( 9, "Folder");
      LookUp(&mi->child(i, 0), locwi);
     
    }
   
    if (filModel->fileInfo( mi->child( i, 0 )).isFile())
    {
      TagLib::FileRef fl(filModel->filePath( mi->child( i, 0) ).toAscii().data());

      locwi = new QTreeWidgetItem(wi);
      locwi->setText( 0, filModel->fileName( mi->child( i, 0) ));
      locwi->setIcon( 0, filModel->fileIcon( mi->child( i, 0) ));
      locwi->setText( 8, filModel->filePath( mi->child( i, 0) ));
      locwi->setText( 9, "File");
      locwi->setText( 1, QString::fromStdWString(fl.tag()->artist().toWString()));
      locwi->setText( 2, QString::fromStdWString(fl.tag()->title().toWString()));
      locwi->setText( 3, QString::fromStdWString(fl.tag()->album().toWString()));
      locwi->setText( 4, QString::number(fl.tag()->year()));
      locwi->setText( 5, QString::number(fl.tag()->track()));
      locwi->setText( 6, QString::fromStdWString(fl.tag()->genre().toWString()));
      locwi->setText( 7, QString::fromStdWString(fl.tag()->comment().toWString()));
     
      emit incProgVal();
     
    }
   
    for (int j = 1; j <= 8; j++)
      if ( (same[j]) && (i > 0) )
        if (wi->child(i - 1) != NULL)
          if (locwi->text(j) != wi->child(i - 1)->text(j))
            same[j] = false;
   
    if (cancel)
    {
      return;
    }
    //QApplication::processEvents();
  }
 
  for (int j = 1; j <= 8 ; j++)
    if (same[j])
      if (wi->child(0) != NULL)
        wi->setText(j, wi->child(0)->text(j));

Возможно глупый вопрос... Но может есть какой-то "локальный" postEvent(), только для одного потока/формы ?


Название: Re: QThread && QApplication::postEvent()
Отправлено: spirit от Март 13, 2009, 18:47
используй катом ивенты, в них можно запишнуть то, что тебе нужно.


Название: Re: QThread && QApplication::postEvent()
Отправлено: Пантер от Март 13, 2009, 18:49
юзай QImage. QPixmap нельзя юзать вне главного потока.
из ассистанта, см.Thread Support in Qt
Цитировать
Painting in Threads
...Painting onto QPixmaps and QWidgets is not supported....
Да хотелось через QFileIconProvider, чтобы свои костыли не лепить, а там через пиксмап. :(


Название: Re: QThread && QApplication::postEvent()
Отправлено: spirit от Март 13, 2009, 18:51
так конвертануть потом QImage в пикспам, что нельзя?  :)
Цитировать
QPixmap QPixmap::fromImage ( const QImage & image, Qt::ImageConversionFlags flags = Qt::AutoColor )   [static]


Название: Re: QThread && QApplication::postEvent()
Отправлено: Пантер от Март 13, 2009, 18:53
Да не в том дело. Загляни в исходники QFileIconProvider, там работа через QPixmap, а на выходе QIcon. Т.е. получается, что в потоке оперирую с QPixmap, что не позволено.


Название: Re: QThread && QApplication::postEvent()
Отправлено: BRE от Март 13, 2009, 18:54
Чтобы было понятнее, у меня обработка файлов тесно связана с QTreeWidget:
[offtop]
Вот это очень плохая традиция переплетать функционал и GUI (уж прости  ;) ). Представь, что завтра тебе нужно будет сделать тоже, только с использованием не Qt (в консоли!). Будешь заново программу писать? Вместо того, чтобы "морду" новую сделать, а функционал старый оставить.
[/offtop]
А в сигналах можно любые объекты передовать. Сделай класс FileMegaInfo, создавай объект этого класса, заполни данными и передавай в главный поток, а он на основании этих данных будет дерево заполнять.


Название: Re: QThread && QApplication::postEvent()
Отправлено: spirit от Март 13, 2009, 18:56
Да не в том дело. Загляни в исходники QFileIconProvider, там работа через QPixmap, а на выходе QIcon. Т.е. получается, что в потоке оперирую с QPixmap, что не позволено.
[offtop]
типа QFileIconProvider юзается в потоке? че-то не пойму :)
[/offtop]


Название: Re: QThread && QApplication::postEvent()
Отправлено: ZeBriD от Март 13, 2009, 18:58
используй катом ивенты, в них можно запишнуть то, что тебе нужно.
Простите, что ?  ::)


Да, и может кто объяснит, почему таки изначальный вариант, с отображением прогресс-бара в отдельном потоке не работает?


[offtop]
Вот это очень плохая традиция переплетать функционал и GUI (уж прости  ;) ). Представь, что завтра тебе нужно будет сделать тоже, только с использованием не Qt (в консоли!). Будешь заново программу писать? Вместо того, чтобы "морду" новую сделать, а функционал старый оставить.
[/offtop]
Тоже верно. Не подумал об этом, ибо это мой первый проект... Спасибо за совет.

А в сигналах можно любые объекты передовать. Сделай класс FileMegaInfo, создавай объект этого класса, заполни данными и передавай в главный поток, а он на основании этих данных будет дерево заполнять.
Похоже, выбор у меня не велик... Попробую так реализовать...


Название: Re: QThread && QApplication::postEvent()
Отправлено: spirit от Март 13, 2009, 18:59
Да, и может кто объяснит, почему таки изначальный вариант, с отображением прогресс-бара в отдельном потоке не работает?
нельзя юзать гуевые классы в негуевом(рабочем) потоке.
опять-таки ассистант, см.Thread Support in Qt
Код:
QObject Reentrancy
...Although QObject is reentrant, the GUI classes, notably QWidget and all its subclasses, are not reentrant.
They can only be used from the main thread. As noted earlier,
QCoreApplication::exec() must also be called from that thread...


Название: Re: QThread && QApplication::postEvent()
Отправлено: ZeBriD от Март 13, 2009, 19:11
нельзя юзать гуевые классы в негуевом(рабочем) потоке.
опять-таки ассистант, см.Thread Support in Qt
Код:
QObject Reentrancy
...Although QObject is reentrant, the GUI classes, notably QWidget and all its subclasses, are not reentrant.
They can only be used from the main thread. As noted earlier,
QCoreApplication::exec() must also be called from that thread...
Спасибо большое за пояснение.


Название: Re: QThread && QApplication::postEvent()
Отправлено: Пантер от Март 13, 2009, 19:11
Да не в том дело. Загляни в исходники QFileIconProvider, там работа через QPixmap, а на выходе QIcon. Т.е. получается, что в потоке оперирую с QPixmap, что не позволено.
[offtop]
типа QFileIconProvider юзается в потоке? че-то не пойму :)
[/offtop]
Да, в потоке получаю иконки из QFileIconProvider::icon(QFileInfo&).


Название: Re: QThread && QApplication::postEvent()
Отправлено: spirit от Март 13, 2009, 19:14
Да не в том дело. Загляни в исходники QFileIconProvider, там работа через QPixmap, а на выходе QIcon. Т.е. получается, что в потоке оперирую с QPixmap, что не позволено.
[offtop]
типа QFileIconProvider юзается в потоке? че-то не пойму :)
[/offtop]
Да, в потоке получаю иконки из QFileIconProvider::icon(QFileInfo&).
поняв :)


Название: Re: QThread && QApplication::postEvent()
Отправлено: Пантер от Март 13, 2009, 19:16
Дык можно что-нибудь сделать без велосипедов?


Название: Re: QThread && QApplication::postEvent()
Отправлено: spirit от Март 13, 2009, 19:16
Дык можно что-нибудь сделать без велосипедов?
надо подумать. :)
а какая задача собственно?


Название: Re: QThread && QApplication::postEvent()
Отправлено: Пантер от Март 13, 2009, 19:19
Ну, типа файловый менеджер пытаюсь писать для саморазвития.  ::)


Название: Re: QThread && QApplication::postEvent()
Отправлено: spirit от Март 13, 2009, 19:21
так это, а чем QFileSystemModel не подходит для этого дела?
или покриколу самому?  :)


Название: Re: QThread && QApplication::postEvent()
Отправлено: BRE от Март 13, 2009, 19:23
Да, в потоке получаю иконки из QFileIconProvider::icon(QFileInfo&).
Залез в исходники, в linux не дает даже сконструировать QPixmap в не GUI-потоке (создает null-pixmap).
Наверное от FileIconProvider в отдельном потоке прийдется отказаться.  :(


Название: Re: QThread && QApplication::postEvent()
Отправлено: Пантер от Март 13, 2009, 19:24
А как по другому? Неохота самому в винапи пока лезть.


Название: Re: QThread && QApplication::postEvent()
Отправлено: Пантер от Март 14, 2009, 14:29
так это, а чем QFileSystemModel не подходит для этого дела?
или покриколу самому?  :)
Хотя бы потому, что у pagefile.sys не показывает размер и иконку.


Название: Re: QThread && QApplication::postEvent()
Отправлено: pastor от Март 14, 2009, 14:38
Хотя бы потому, что у pagefile.sys не показывает размер и иконку.

Это давно известный баг в Qt:

#167099 - On Windows QFileInfo::exists() returns false for c:\pagefile.sys and c:\hiberfil.sys (http://www.qtsoftware.com/developer/task-tracker/index_html?id=167099&method=entry)


Название: Re: QThread && QApplication::postEvent()
Отправлено: Пантер от Март 14, 2009, 14:46
Да я знаю, что это баг, уже когда-то на форуме спрашивал. Вот из-за этого сам и решил покопаться. И еще с правами под виндой кака.