Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.143.0.89] |
|
Сообщ.
#1
,
|
|
|
попытался создать диалог, который при создании создает таймер и обновляет иконку.
Тоесть я хочу его запускать при длительных операциях для отображения окна действия пользователя. QDialog dialog; dialog.show(); ... // долгие расчеты, возможно в разных потоках и с синхронизацией Sleep (10000); // эмуляция ... dialog.hide(); Но таймер не срабатывает. Было предположение, что таймер работает в контексте GUI-потока. Попытался вместо таймера использовать поток. Кричит, что нельзя изменять компанент созданный в GUI-потоке из другого потока. Создал этот диалог вообще в отдельном потоке - таже фигня. Как создать такой диалог ? Добавлено P.S. Вся проблемма в том, что нужен именно диалог в отдельном поке, а не обработка данных. Т.к. при обработке данных обновляются компаненты, созданные в GUI-потоке. |
Сообщ.
#2
,
|
|
|
Цитата zss @ Создал этот диалог вообще в отдельном потоке - таже фигня. Ты в отдельном потоке метод exec, который проталкивает очередь сообщений, вызываешь? |
Сообщ.
#3
,
|
|
|
Цитата Flex Ferrum @ Ты в отдельном потоке метод exec, который проталкивает очередь сообщений, вызываешь? так exec для диалога это синхронный вызов окна (на сколько я понял) |
Сообщ.
#4
,
|
|
|
Цитата zss @ так exec для диалога это синхронный вызов окна (на сколько я понял) Да нет. У самого потока есть метод exec. |
Сообщ.
#5
,
|
|
|
Цитата Flex Ferrum @ Да нет. У самого потока есть метод exec. я не использую Qt-потоки я использую boost... |
Сообщ.
#6
,
|
|
|
Цитата zss @ я не использую Qt-потоки я использую boost... Хех. Ну тогда ищи класс QEventLoop, и дергай у его экземпляра, созданного в потоке, метод processEvents. |
Сообщ.
#7
,
|
|
|
Цитата Flex Ferrum @ Хех. Ну тогда ищи класс QEventLoop, и дергай у его экземпляра, созданного в потоке, метод processEvents. а можно ли все-таки создать поток в самом диалоге, чтою не плодить потоки из первичного потока ? |
Сообщ.
#8
,
|
|
|
Цитата zss @ а можно ли все-таки создать поток в самом диалоге, чтою не плодить потоки из первичного потока ? Если только ты во время твоих длительных вычислений будешь руками толкать сообщения. |
Сообщ.
#9
,
|
|
|
Цитата Flex Ferrum @ Если только ты во время твоих длительных вычислений будешь руками толкать сообщения. не получится - дергаеются методы библиотек, которые не возвращают управления до завершения |
Сообщ.
#10
,
|
|
|
Цитата zss @ не получится - дергаеются методы библиотек, которые не возвращают управления до завершения Тогда, если эти библиотеки не просят callback'и, только в отдельном потоке. И, кстати, элементы основного потока у тебя (при таком раскладе) врядли будут перерисовываться. |
Сообщ.
#11
,
|
|
|
Цитата Flex Ferrum @ И, кстати, элементы основного потока у тебя (при таком раскладе) врядли будут перерисовываться. при каком ? Если создаю в отдельном потоке ? З.Ы. Ну принципе элементы основного потока и не так выжны... |
Сообщ.
#12
,
|
|
|
Цитата zss @ при каком ? Если создаю в отдельном потоке ? При любом. |
Сообщ.
#13
,
|
|
|
Цитата Flex Ferrum @ При любом. ну так и при показе обычного диалога этого не происходит. Это же никому не мешает |
Сообщ.
#14
,
|
|
|
zss
я так понял, что хочешь создать диалог в не gui-потоке? или диалог создается в gui-потоке и отображается во время длительных операций в другом, не gui-потоке? Т.е. пользователь может сделать что-то с программой, пока в параллельном потоке что-то выполняется? для чего таймер? |
Сообщ.
#15
,
|
|
|
Цитата gryz @ для чего таймер? таймер у меня в диалоге для смены картинки. Но ни разу не срабатывает. Кроме того сам диалог не перетаскивается. Он не реагирует на действия пользователя вообще. Цитата gryz @ я так понял, что хочешь создать диалог в не gui-потоке? да нет. хочу как раз в GUI-потоке. но после того как dialog.show(); Sleep (10000); на Sleep замирает весь GUI и диалог не отрисовывается. Flex Ferrum, я попробовал создать QThread и вызывать exec. Не дает он создавать Widget в не GUI-потоке. Или я чего-то не понял ? void fmMain::start (void) { m_stopped = false; QThread::start (); } void fmMain::stop (void) { m_stopped = true; QThread::wait (); } void Console::run (void) { QWaitDialog dialog ("Running..."); while (!m_stopped) { QThread::exec(); QThread::msleep (100); } } void fmMain::func () { start (); Sleep (5000); stop (); } |
Сообщ.
#16
,
|
|
|
Цитата zss @ Flex Ferrum, я попробовал создать QThread и вызывать exec. Не дает он создавать Widget в не GUI-потоке. Это как? |
Сообщ.
#17
,
|
|
|
Говорит что Widget можно создать только в перыичном потоке.
Это отладочное сообщение Qt. Там еще приводятся ID потоков и показываются что они разные |
Сообщ.
#18
,
|
|
|
Вообщем, читай и думай:
Цитата QObject Reentrancy QObject is reentrant. Most of its non-GUI subclasses, such as QTimer, QTcpSocket, QUdpSocket, QHttp, QFtp, and QProcess, are also reentrant, making it possible to use these classes from multiple threads simultaneously. Note that these classes are designed to be created and used from within a single thread; creating an object in one thread and calling its functions from another thread is not guaranteed to work. There are three constraints to be aware of: The child of a QObject must always be created in the thread where the parent was created. This implies, among other things, that you should never pass the QThread object (this) as the parent of an object created in the thread (since the QThread object itself was created in another thread). Event driven objects may only be used in a single thread. Specifically, this applies to the timer mechanism and the network module. For example, you cannot start a timer or connect a socket in a thread that is not the object's thread. You must ensure that all objects created in a thread are deleted before you delete the QThread. This can be done easily by creating the objects on the stack in your run() implementation. 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. In practice, the impossibility of using GUI classes in other threads than the main thread can easily be worked around by putting time-consuming operations in a separate worker thread and displaying the results on screen in the main thread when the worker thread is finished. This is the approach used for implementing the Mandelbrot and the Blocking Fortune Client example. Добавлено Т. е., по идее, ты можешь создать диалог в другом потоке, но у этого диалога должен отсутствовать родитель. |
Сообщ.
#19
,
|
|
|
Цитата Flex Ferrum @ Вообщем, читай и думай: Цитата QObject Reentrancy QObject is reentrant. Most of its non-GUI subclasses, such as QTimer, QTcpSocket, QUdpSocket, QHttp, QFtp, and QProcess, are also reentrant, making it possible to use these classes from multiple threads simultaneously. Note that these classes are designed to be created and used from within a single thread; creating an object in one thread and calling its functions from another thread is not guaranteed to work. There are three constraints to be aware of: The child of a QObject must always be created in the thread where the parent was created. This implies, among other things, that you should never pass the QThread object (this) as the parent of an object created in the thread (since the QThread object itself was created in another thread). Event driven objects may only be used in a single thread. Specifically, this applies to the timer mechanism and the network module. For example, you cannot start a timer or connect a socket in a thread that is not the object's thread. You must ensure that all objects created in a thread are deleted before you delete the QThread. This can be done easily by creating the objects on the stack in your run() implementation. 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. In practice, the impossibility of using GUI classes in other threads than the main thread can easily be worked around by putting time-consuming operations in a separate worker thread and displaying the results on screen in the main thread when the worker thread is finished. This is the approach used for implementing the Mandelbrot and the Blocking Fortune Client example. Добавлено Т. е., по идее, ты можешь создать диалог в другом потоке, но у этого диалога должен отсутствовать родитель. Цитата Flex Ferrum @ Вообщем, читай и думай: ну я не знаю что тут написано (с английским проблемма) но вот код потока, в котором диалог не имеет родителя void fmMain::run (void) { QWaitDialog dialog; dialog.show(); while (true); dialog.hide(); } И вот сообщение QT Добавлено P.S. Кстати - почему эти диалоги показываются там где захотят. Как заставить их покацываться по центру MainWindow ? Прикреплённая картинка
|
Сообщ.
#20
,
|
|
|
Вот, собственно, существенная часть:
Цитата Flex Ferrum @ In practice, the impossibility of using GUI classes in other threads than the main thread can easily be worked around by putting time-consuming operations in a separate worker thread and displaying the results on screen in the main thread when the worker thread is finished. This is the approach used for implementing the Mandelbrot and the Blocking Fortune Client example. "На практике, невозможность использования GUI-классов в других потоках, отличных от главного, может быть несложно обойдена путем расположения сложных и ресурсоемких операций в отдельных (рабочих) потоках, а отображать результаты их работы в основном потоке, когда рабочие потоки будут завершены. Такой подход использован в примерах "Mandelbrot" и "Blocking Fortune Client". |
Сообщ.
#21
,
|
|
|
Цитата Flex Ferrum @ "На практике, невозможность использования GUI-классов в других потоках, отличных от главного, может быть несложно обойдена путем расположения сложных и ресурсоемких операций в отдельных (рабочих) потоках, а отображать результаты их работы в основном потоке, когда рабочие потоки будут завершены. Такой подход использован в примерах "Mandelbrot" и "Blocking Fortune Client". тоесть не диалон в потоке, обработка данных ? Но тогда возникает сложность. Пусть есть пара десятков методов, которые занимаются общетом данных. Тоесть каждый из них нужно запускать в отдельном потоке. Для этого нужно создать такое же количесто потокомых методов. В основнфх методах запускать диалог и из него дергать потоковый. Не слишком ли много кода ? Да и как создать такие потоки, если унаследовшись от QThread я имею только один поток (метод run). |
Сообщ.
#22
,
|
|
|
Цитата zss @ тоесть не диалон в потоке, обработка данных ? Ну да. А как ты в основном потоке обработку данных делал? Последовательно, небось? Так же и тут. Числодробилку запускай в отдельном потоке, а в основном - отображай прогрессбар, гуевые элементы обновляй и все такое прочее. Я особых проблем не вижу. |
Сообщ.
#23
,
|
|
|
Flex Ferrum, так как в отдельном потоке при помощи QThread, если там один фиртуальный метод, а у меня их десяток
|
Сообщ.
#24
,
|
|
|
Цитата zss @ если там один фиртуальный метод, а у меня их десяток Десяток чего? Потоков? Или методов? |
Сообщ.
#25
,
|
|
|
zss
Значит, диалог немодальный? Ведь модальный должен отображаться при помощи метода exec(). Каким образом привязываешь таймер к диалогу? Какого рода вычисления должны при этом выполняться? |
Сообщ.
#26
,
|
|
|
Цитата Flex Ferrum @ Десяток чего? Потоков? Или методов? методов, которые должны запускаться в отдельных потоках Цитата gryz @ Значит, диалог немодальный? Ведь модальный должен отображаться при помощи метода exec(). наверное не модальный, т.к. exec не позволит запустить поток, пока не вернется управление из него Цитата gryz @ Каким образом привязываешь таймер к диалогу? создаю в конструкторе диалога таймер и запускаю его. В деструкторе убиваю Цитата gryz @ Какого рода вычисления должны при этом выполняться? вычисления - просто вызов методов, которые долго работают |
Сообщ.
#27
,
|
|
|
Цитата zss @ методов, которые должны запускаться в отдельных потоках Передавай методы по указателям. Или, действительно, используй бустовские потоки. |
Сообщ.
#28
,
|
|
|
Цитата zss @ наверное не модальный, т.к. exec не позволит запустить поток, пока не вернется управление из него Что за поток? Для чего его запускать после показа диалога? -Added Цитата zss @ создаю в конструкторе диалога таймер и запускаю его. В деструкторе убиваю и используешь для замера времени сигнал timeout()? тогда должно все работать. |
Сообщ.
#29
,
|
|
|
Цитата Flex Ferrum @ Передавай методы по указателям. Или, действительно, используй бустовские потоки. Куда передавать ? Я так понят от Qt потоков можно только наследоваться.Просто я не работал с Qt-потоками. (может конечно и boost использовать, но хочется разобраться ) Цитата gryz @ Для чего его запускать после показа диалога? ну смотри dialog.exec(); startLongTimeFunction(); dialog.hide(); как ты думаешь - вызовется ли эта функция, пока диалог, показанный exec не закроется ? Цитата gryz @ и используешь для замера времени сигнал timeout()? ну а как еще обработать таймер. Конечно использую сигнал и изменяю в обработчике картинку. P.S. Или я что-то не понимаю. Можно тогда простой примерчик |
Сообщ.
#30
,
|
|
|
Цитата zss @ Я так понят от Qt потоков можно только наследоваться.Просто я не работал с Qt-потоками. (может конечно и boost использовать, но хочется разобраться ) Наследуешься, в конструкторе принимаешь указатель на функцию или функтор, в run его вызываешь. Какие проблемы? |
Сообщ.
#31
,
|
|
|
Цитата Flex Ferrum @ Наследуешься, в конструкторе принимаешь указатель на функцию или функтор, в run его вызываешь. Какие проблемы? Тоесть должен быть отдельный класс-поток ? Просто я думал QMainWindow от него унаследовать Тут еще проблемма. Что если сигнатура методов различна. И как поведет себя приложение если в потоковом методе произойдет исключение. Просто я его ловлю и вызываю соответствующие (разные) диалоговые сообщения. Как мне тогда из потока сказать, что ничего не получилось. Добавлено P.S. Да кстати - и как тогда обмениваться данными с потоком ? |
Сообщ.
#32
,
|
|
|
Цитата zss @ dialog.exec(); startLongTimeFunction(); dialog.hide(); функция gui-потоке выполняется? |
Сообщ.
#33
,
|
|
|
Цитата gryz @ функция gui-потоке выполняется? она библиотечная. Выполняется в GUI, но может создавать внутри себя еще потоки |
Сообщ.
#34
,
|
|
|
Цитата zss @ Просто я думал QMainWindow от него унаследовать Ни в коем случае. Цитата zss @ Тоесть должен быть отдельный класс-поток ? Ну да. Только обобщенный. Цитата zss @ Тут еще проблемма. Что если сигнатура методов различна. boost::function спасет отца русской демократии. Цитата zss @ И как поведет себя приложение если в потоковом методе произойдет исключение. Просто я его ловлю и вызываю соответствующие (разные) диалоговые сообщения. Можешь точно также - через boost::function передать указатель на функцию-обработчик исключения. |
Сообщ.
#35
,
|
|
|
Цитата Flex Ferrum @ boost::function спасет отца русской демократии. это точно Цитата Flex Ferrum @ Можешь точно также - через boost::function передать указатель на функцию-обработчик исключения а данные для обработки в конструкторе передавать ? Просто они тоже разные. И возвращаются точе разные типы. С функциями понял а как с данными быть. Например мне нужно отдать ссылку на Type1, который будет измененн при успешных вычислениях. Но для метода2 нужно отдать Type2. Как тут быть ? |
Сообщ.
#36
,
|
|
|
Цитата zss @ Например мне нужно отдать ссылку на Type1, который будет измененн при успешных вычислениях. Но для метода2 нужно отдать Type2. Как тут быть ? Прочитать доку на boost::function и boost::ref. |
Сообщ.
#37
,
|
|
|
Цитата Flex Ferrum @ Прочитать доку на boost::function и boost::ref. Цитата zss @ ну я не знаю что тут написано (с английским проблемма) только на примерах могу |
Сообщ.
#38
,
|
|
|
Сообщ.
#39
,
|
|
|
Цитата Flex Ferrum @ boost::function Добавлено Я читал данну статью. Но boost::function не решит проблемму. Например метоы voif f1 (void); bool f2 (void); voif f3 (bool); bool f4 (bool); Type1 f5 (void); voif f6 (Type1); bool f7 (Type1); врядли их все можно запихнуть в один потоковый класс |
Сообщ.
#40
,
|
|
|
Цитата zss @ врядли их все можно запихнуть в один потоковый класс Можно, используя boost::bind. |
Сообщ.
#41
,
|
|
|
Цитата Flex Ferrum @ Можно, используя boost::bind. блин... я чего-то не понимаю Вот пример бустовского потока class BoostThread : private boost::noncopyable { private : boost::thread m_thread; public : explicit BoostThread (const boost::function0<void>& func) : m_thread (func) {} ~BoostThread () { m_thread.join(); } static void sleep (unsigned msec); }; он принимает boost::function0. Хоть упрись, но он не будет принимать boost::function1 Тоесть получается что для каждого метода свой потоковый класс писать ? |
Сообщ.
#42
,
|
|
|
Цитата zss @ Тоесть получается что для каждого метода свой потоковый класс писать ? Да нет. Пишешь так: void Foo1(int) { } //... boost::thread t(boost::bind(&Foo1, 10)); //... и все. |
Сообщ.
#43
,
|
|
|
Цитата Flex Ferrum @ и все. че правда чтоли 1. А как тогда быть с обработкой исключений. Здесь нельзя зарегистрировать callback. или достатчно try{ boost::thread t(boost::bind(&Foo1, 10)); } catch (...) {...} 2. Как сбиндить unsigned Foo1(int) { } чтоб получить результат 3. и можно ли тут шаблонить ? |
Сообщ.
#44
,
|
|
|
Цитата zss @ 1. А как тогда быть с обработкой исключений. Здесь нельзя зарегистрировать callback. или достатчно Не. Лучше не так, а вот так: class WorkerThread : public QThread { public: WorkerThread(boost::function<void ()> worker, boost::function<void (const std::exception&)> xHandler) : m_Worker(worker), m_ExHandler(handler) {;} int run() { try { m_Worker(); } catch (const std::exception& ex) { m_ExHandler(ex); } } }; |
Сообщ.
#45
,
|
|
|
Цитата Flex Ferrum @ Не. Лучше не так, а вот так: понятно - спасибо. А как на счет п.2. Нужен свой поток ? Тогда получается, что для 5 функций нужно 5 обработчиков исключений (если в обработчике не возвращать код) и 5 запускающих функций, которые создают диалоги и запускают потоки N * 3 - круто... |
Сообщ.
#46
,
|
|
|
Цитата zss @ 5 обработчиков исключений (если в обработчике не возвращать код) и 5 запускающих функций, которые создают диалоги и запускают потоки Хм. Ну, если другого выхода не находишь, то да... |
Сообщ.
#47
,
|
|
|
Цитата Flex Ferrum @ Хм. Ну, если другого выхода не находишь, то да... выход есть - заставить диалог работать в другом потоке. и тогда нужен только один поток для запуска диалога. Иначе... лучше вообще без диалога, но тут могут возникнуть непонятки со стороны пользователяЮ когда приложение не отвечает в течении 1 минуты и более Добавлено идея! У меня все методы долгих обработок - это слоты QAction. А что если подписать их на один метод и в этом методе создать диалог и запустить потоковые метоты простым бустом ? Единственное - обработка исключений в потке - это нормально ? |
Сообщ.
#48
,
|
|
|
А что ты не хочешь Qt-шные потоки юзать? Создал в основном потоке диалог, после чего запускаешь поток. Можешь в Qt-шный поток добавить сигналы о том, что поток завершен, что возникла ошибка и проч...
Добавлено Я просто не могу понять - какой архитектуры ты пытаешься достигнуть? Что должно получиться в результате? |
Сообщ.
#49
,
|
|
|
Цитата Flex Ferrum @ А что ты не хочешь Qt-шные потоки юзать? Создал в основном потоке диалог, после чего запускаешь поток. Можешь в Qt-шный поток добавить сигналы о том, что поток завершен, что возникла ошибка и проч... тоесть на кждый поток 2 слота. Ведь ошибки разные Цитата Flex Ferrum @ Я просто не могу понять - какой архитектуры ты пытаешься достигнуть? Что должно получиться в результате? Да наверное мы друг-друга не понимаем... Идея проста - перед запуском долгой функции показать диалог, а по ее завершении убить диалог. Ну например struct Client { bool start (void) const; bool pause (void) const; bool stop (void) const; unsigned power (void) const; WF_STATE state (void) const; bool shutdown (void); bool reboot (void); Options options (void) const; bool setOptions (const Options& options) const; bool loadSettings (const std::string& name) const; bool setSettings (const TSettings& settings) const; TSettings settings (void) const; }; Все методы могут быть долгими. Это общение посети. QMainWindow владеет клиентом и при событии (QAction) дергает тот или иной метод MainWindow::f1 () { try { client.start(); } catch (std::exception& ex) { MessageBox (...); } } MainWindow::f2 () { try { m_options = client.options(); } catch (std::exception& ex) { MessageBox (...); } } MainWindow::f3 () { try { client.setOptions(m_options); } catch (std::exception& ex) { MessageBox (...); } } А теперь хотелось бы с диалогом MainWindow::f2 () { try { QWaitDialog dialog; dialog.show(); m_options = client.options(); dialog.hide(); } catch (std::exception& ex) { MessageBox (...); } } вот и все. впринципе слоты имеют одну сигнатуры поэтому можно использовать массив методов или boost::bind. С этим я понял. Но вот исключения в потоке ? Если для потока создать сигнал ошибки, то можно конечно создать общий обработчик и передавать в него например сообщение об ошибке. Я правильно тебя понял ? |
Сообщ.
#50
,
|
|
|
Т. е. у тебя может быть одновременно показано несколько диалогов?
Добавлено Ты, я так понимаю, что-то типа чата пишешь? |
Сообщ.
#51
,
|
|
|
Цитата Flex Ferrum @ Т. е. у тебя может быть одновременно показано несколько диалогов? нет. один диалог на каждый метод. Цитата Flex Ferrum @ Ты, я так понимаю, что-то типа чата пишешь? не - просто сетевое управление комплексами |
Сообщ.
#52
,
|
|
|
Тут смотри, какой момент. По идее (если ты потоки сделаешь на QThread), то ты можешь цеплять сигнал о возникновении ошибке сразу к диалогу. Т. е.:
MainWindow::f2 () { try { QWaitDialog dialog; QConnectThread thread(); // тут сам придумаешь, что написать. connect(&thread, SIGNAL(ThreadEnd()), &dialog, SLOT(ThreadEnded())); connect(&thread, SIGNAL(ThreadError(const QString&)), &dialog, SLOT(ThreadError(const QString&))); thread.start(); dialog.show(); thread.wait(); // или что тут должно быть? // m_options = client.options(); dialog.hide(); } catch (std::exception& ex) { MessageBox (...); } } Добавлено Цитата zss @ нет. один диалог на каждый метод. Т. е. диалоги, по сути, модальные? |
Сообщ.
#53
,
|
|
|
а зачем тогда здесь обработка исключений ?
|
Сообщ.
#54
,
|
|
|
Цитата zss @ а зачем тогда здесь обработка исключений ? Не знаю. Но исключение чужого потока ты не поймаешь. |
Сообщ.
#55
,
|
|
|
Цитата Flex Ferrum @ Не знаю. Но исключение чужого потока ты не поймаешь. а мне не нужно его ловить. оно ловится в самом потоке. Меня смущает смогу ли я из другого потока показать MessageBox ? |
Сообщ.
#56
,
|
|
|
Цитата zss @ Меня смущает смогу ли я из другого потока показать MessageBox ? Нет. Не можешь. Но ты можешь дернуть сигнал, который это сделает из основного потока. |
Сообщ.
#57
,
|
|
|
Цитата Flex Ferrum @ Нет. Не можешь. Но ты можешь дернуть сигнал, который это сделает из основного потока. ну впринципе выход. Тогда получается что сигналы потока мне вообще не нужны. Я все сделаю в самом потоке - я имею ввиду откат и т.п. |
Сообщ.
#58
,
|
|
|
Цитата zss @ Тогда получается что сигналы потока мне вообще не нужны. Я все сделаю в самом потоке - я имею ввиду откат и т.п. В смысле? Нужны. А как ты сообщишь основному потоку (т. е. диалогу) о том, что произошла нештатная ситуация? Это раз. Второй момент - механизм сигналов/слотов в Qt потокобезопасный. По сути, это единственный корректный способ обмениваться сообщениями между потоками. При этом ты (при вызове функции connect) должен передать пятый параметр - Qt::QueuedConnection. Тогда поступающие в основной поток сигналы от рабочих потоков будут корректно буферизироваться и обрабатываться. |
Сообщ.
#59
,
|
|
|
тоесть полубому 2 слота для обработки ?
Тогда не совсем понятно. 1. thread.start(); dialog.show(); thread.wait(); // или что тут должно быть? // m_options = client.options(); dialog.hide(); тоесть диалог закроется только после завершения потока. Если в потоке происходит исключение и я в обработчике сигналю для показа MessageBox, то MessageBox будет поверх моего диалога, т.к. управление может еще не вернуться. 2. Аналогично и для слота ThreadEnd. Как я сообщю MainWindow::f2 что поток успешно завершился. Веть ожидание потока не дает результат его завершения. Что-то все сложно как-то |
Сообщ.
#60
,
|
|
|
Цитата zss @ тоесть диалог закроется только после завершения потока. Если в потоке происходит исключение и я в обработчике сигналю для показа MessageBox, то MessageBox будет поверх моего диалога, т.к. управление может еще не вернуться. Зачем? Это сообщение может отобразить сам диалог. Зачем мессаджбокс еще показывать? Цитата zss @ Как я сообщю MainWindow::f2 что поток успешно завершился. Веть ожидание потока не дает результат его завершения. Можешь спросить у диалога, а диалогу - передать в соответствующем сигнале. Вообще, диалог вполне может показываться модально (при твоем раскладе). Т. е.: QWaitDialog dialog; QConnectThread thread(); // тут сам придумаешь, что написать. connect(&thread, SIGNAL(ThreadEnd(int)), &dialog, SLOT(ThreadEnded(int))); connect(&thread, SIGNAL(ThreadError(const QString&)), &dialog, SLOT(ThreadError(const QString&))); thread.start(); dialog.exec(); В таком случае, у тебя: 1) Функция не завершится до тех пор, пока не отработает поток. 2) Во время работы потока будет висеть диалог и ждать его завершения. Собственно, это именно то, что тебе нужно, на сколько я смог понять. |
Сообщ.
#61
,
|
|
|
Цитата Flex Ferrum @ Зачем? Это сообщение может отобразить сам диалог. Зачем мессаджбокс еще показывать? бр.... я совсем запутался. диалог вообще ничего ни о чем не знает. его задача - просто например крутить часики. Да и к тому же если он отобразит его, то поверх себя. А зачем - для того. чтобы GUI-поток отобразил строку ошибки. Ведь для этого нужен был ThreadError ? Цитата Flex Ferrum @ В таком случае, у тебя: 1) Функция не завершится до тех пор, пока не отработает поток. 2) Во время работы потока будет висеть диалог и ждать его завершения. Собственно, это именно то, что тебе нужно, на сколько я смог понять. так - нужен перерыв для обмозгования я окончательно запутался. Попробую что-нибудь сделать а там будет видно |
Сообщ.
#62
,
|
|
|
Цитата zss @ я совсем запутался. диалог вообще ничего ни о чем не знает. его задача - просто например крутить часики. Да и к тому же если он отобразит его, то поверх себя. Ну а теперь предположи, что он знает о том, что запущен поток. И его задача - не только крутить часики, но и дожидаться, пока поток завершится. И либо отобразить ошибку, возникшую в процессе работы потока, либо сказать, что все в порядке и схлопнуться. Тогда все получается достаточно стройно. |
Сообщ.
#63
,
|
|
|
Цитата Flex Ferrum @ Ну а теперь предположи, что он знает о том, что запущен поток. И его задача - не только крутить часики, но и дожидаться, пока поток завершится. И либо отобразить ошибку, возникшую в процессе работы потока, либо сказать, что все в порядке и схлопнуться. Тогда все получается достаточно стройно. Ва запутались в показаниях . сначала Вы говорили, что нужно диалог создавать в отдельном потоке. После что нужно в потоке обрабатывать данные. А на последнем допросе - диалог должен знать обо всем Шутка... Ну я так понял что сигналы нужны только диалогу для того. чтобы прихлопнуться или сообщить об ошибке ? Принципе логично. Попробую и скажу что получилось. Спасибо за долго общение |
Сообщ.
#64
,
|
|
|
Цитата zss @ Цитата gryz @ функция gui-потоке выполняется? она библиотечная. Выполняется в GUI, но может создавать внутри себя еще потоки Если функцию можно выполнить в другом потоке, т.е. она, например, не работает напрямую с gui, то тогда лучше так и сделать. О завершении другого потока можно узнать по его сигналу finished () (если это наследник QThread) По этому сигналу можно закрыть диалог. Если диалогу нужно знать что-то о вычиляющем потоке, то тогда можно сделать дополнительный сигнал в вычисляющем потоке, который будет информировать gui-поток (в частности - наш диалог) о текущем состоянии дел. |
Сообщ.
#65
,
|
|
|
Цитата gryz @ Если функцию можно выполнить в другом потоке, т.е. она, например, не работает напрямую с gui, то тогда лучше так и сделать. О завершении другого потока можно узнать по его сигналу finished () (если это наследник QThread) По этому сигналу можно закрыть диалог. Если диалогу нужно знать что-то о выполняемом процессе, то тогда можно сделать дополнительный сигнал в вычисляющем потоке, который будет информировать gui-поток (в частности - наш диалог) о текущем состоянии дел. я долго размышлял, но пока руки не дошли до этого. Если не сложно - простейший работающий пример со Sleep можно ? |
Сообщ.
#66
,
|
|
|
Цитата zss @ я долго размышлял, но пока руки не дошли до этого. Если не сложно - простейший работающий пример со Sleep можно ? Что за Sleep, зачем он? |
Сообщ.
#67
,
|
|
|
Цитата gryz @ Что за Sleep, зачем он? эмитация долгой функции. пусть например Sleep (50000) - 50 сек. |
Сообщ.
#68
,
|
|
|
диалог запускаем методом exec(), перед его запуском стартуем вычисляющий поток
Добавлено завтра попробую пример написать. сейчас домой - на работе засиделся. |
Сообщ.
#69
,
|
|
|
Цитата gryz @ диалог запускаем методом exec(), перед его запуском стартуем вычисляющий поток а слот диалога вешаем на сигнал завершения потока как сказал Flex Ferrum ? |
Сообщ.
#70
,
|
|
|
я имел в виду это:
#include <QApplication> #include <QThread> #include <QMessageBox> #include <QtDebug> class MyThread : public QThread { public: void run() { qDebug() << "thread started"; sleep(5); qDebug() << "thread finished"; } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); QMessageBox * d = new QMessageBox; QThread * th = new MyThread; QObject::connect(th, SIGNAL(finished()), d, SLOT(accept())); th->start(); d->exec(); th->wait();//если нажали ок, то надо дождаться завершения выполнения потока } |
Сообщ.
#71
,
|
|
|
я про это понял. Тут еще возникла сложность, что нужно создавать для каждого метода свой потоковый класс и свой слот обработки.
Много кода лишнего получается ... |
Сообщ.
#72
,
|
|
|
Цитата zss @ Тут еще возникла сложность, что нужно создавать для каждого метода свой потоковый класс и свой слот обработки. Зачем? Можно же поместить объект в поток (если объект наследник QObject), сделать его методы слотами и асинхронно вызывать их при помощи QMetaObject::invokeMethod(). Только тут надо учесть, что возвращаемыми значениями воспользоваться неполучится. Прийдется использовать сигналы для сообщении о результате, если он нужен. при старте каждого метода испускать сигнал methodStarted(QString methodName), при завершении метода испускать сигнал methodFinished(). Можно сделать сигнал methodFinished(QVarinat rez), в rez будет храниться результат выполнения метода. |
Сообщ.
#73
,
|
|
|
Цитата gryz @ Можно же поместить объект в поток (если объект наследник QObject), не наследник, но никто не мешает его хранить внутри объекта-потока. Цитата gryz @ сделать его методы слотами и асинхронно вызывать их при помощи QMetaObject::invokeMethod(). хорошая идея... Цитата gryz @ Только тут надо учесть, что возвращаемыми значениями воспользоваться неполучится. Прийдется использовать сигналы для сообщении о результате, если он нужен. Почему ? Цитата gryz @ при старте каждого метода испускать сигнал methodStarted(QString methodName), при завершении метода испускать сигнал methodFinished(). Можно сделать сигнал methodFinished(QVarinat rez), в rez будет храниться результат выполнения метода. а можно примерчик - мне кажется интересная идея. З.Ы. Только не понятно что должен делать слот потока ? |
Сообщ.
#74
,
|
|
|
Цитата zss @ Почему ? Потому что это асинхронный вызов. Он происходит не сразу, а при возврате в цикл обработки сообщений. Так что сразу результата никакого нет. А нам нужно продолжить выполнение. В Qt 4.3 появился тип вызова (точнее, это тип соединения) Qt::BlockingQueuedConnection, но он не подойдет для вызова из gui потока. Добавлено Вот примерчик //client.h #ifndef CLIENT_H #define CLIENT_H #include <QObject> #include <QVariant> #include <QPushButton> #include <QThread> #include <QMessageBox> #include <QtDebug> class Client: public QObject { Q_OBJECT public slots: bool start()const { qDebug() << "start() started"; emit methodStarted("start()"); int sum = 0;//сложнейшие вычисления for(int i = 0; i < 100000; ++i) for(int j = 0; j < 10000; ++j) sum += j * i; bool rez = true; qDebug() << "start() finished"; emit methodFinished(QByteArray("true"));//всегда последняя перед return операция. //оказалось, что QVariant нельзя передавать в //в соединениях типа Qt::QueuedConnection (qt4.2.2). //Можно упаковывать результат в QByteArray используя QDataStream. //туда же можно запихнуть имя метода start(). //Еще как вариант можно использовать различные синалы //для различных типов возвращаемых значений. return rez; } //... signals: void methodStarted(QString methodName)const; void methodFinished(QByteArray rez)const; }; class RunnableThread: public QThread { public: void run()//в QThread этот метод не определен, поэтому приходится наследоваться { exec(); } }; class DummyMainWindow: public QWidget { Q_OBJECT public: DummyMainWindow() { QPushButton * startBtn = new QPushButton(tr("Start"), this); theClient = new Client; theWorkerThread = new RunnableThread; theWorkerThread->start(); theClient->moveToThread(theWorkerThread); connect(startBtn, SIGNAL(pressed()), SLOT(invokeStart())); connect(theClient, SIGNAL(methodStarted(QString)), SLOT(showDialog(QString))); connect(theClient, SIGNAL(methodFinished(QByteArray)), SLOT(processResult(QByteArray))); theInfoDialog = new QMessageBox(this); theInfoDialog->setStandardButtons(QMessageBox::NoButton); } ~DummyMainWindow() { theWorkerThread->quit(); theWorkerThread->wait(); delete theWorkerThread; delete theClient; } private slots: void invokeStart() { //можем запомнить текущий вызываемый метод в члене класса, //тогда в методе processResult будем знать, какой метод был вызван, //чтобы правильно интепретировать результат QMetaObject::invokeMethod(theClient, "start"); } void showDialog(QString text) { theInfoDialog->setText(text + tr(" is running now")); theInfoDialog->exec(); } void processResult(QByteArray rez) { theInfoDialog->accept(); //делаем, что хотим с rez } private: Client * theClient; RunnableThread * theWorkerThread; QMessageBox * theInfoDialog; }; #endif //main.cpp #include <QtGui/QApplication> #include "client.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); DummyMainWindow mw; mw.show(); return a.exec(); } Добавлено В данной реализации в программе всегда запущено 2 потока: gui и вычисляющий. В принципе, можно сделать так, чтобы вычисляющий запускался при необходимости вычислений, а после - завершался. |
Сообщ.
#75
,
|
|
|
gryz, спасибо за код, но не все подойдет
в #49 описано почему. Разные методы, разные сигнатуры и разные возвращаемые значения... |
Сообщ.
#76
,
|
|
|
zss
что мешает добавить слоты аналогичные invokeStart() - для разных методов? для возвращаемых значений различных типов можно использовать сигналы вида: void methodFinished(int)const; void methodFinished(QString)const; void methodFinished(double)const; |
Сообщ.
#77
,
|
|
|
В общем забросил я в свое время проект, но тут опять возвращаюсь к нему.
Еще раз перечитал все сообщения. 1. gryz, если честно - не совсем понял код и как он работает и что есть moveToThread... ? 2. Flex Ferrum, ну вроде все понял, кроде некоторых моментов. Например Цитата Flex Ferrum @ Не. Лучше не так, а вот так: class WorkerThread : public QThread { public: WorkerThread(boost::function<void ()> worker, boost::function<void (const std::exception&)> xHandler) : m_Worker(worker), m_ExHandler(handler) {;} int run() { try { m_Worker(); } catch (const std::exception& ex) { m_ExHandler(ex); } } }; как сбиндить метод. Если просто метод класса, то boost::bind (&Class::function, this) а как сбиндить для WorkerThread void func (const std::exception& ex); P.S. Попробовал данный способ Цитата Flex Ferrum @ QWaitDialog dialog; QConnectThread thread(); // тут сам придумаешь, что написать. connect(&thread, SIGNAL(ThreadEnd(int)), &dialog, SLOT(ThreadEnded(int))); connect(&thread, SIGNAL(ThreadError(const QString&)), &dialog, SLOT(ThreadError(const QString&))); thread.start(); dialog.exec(); поток запускается, диалог отображается, после завершения потока окно закрывается (дергается слот диалого) Но !!! Таймер внутри диалога не отрабатывает. Почему ? Ведь диалог отрисовывается. Его можно даже перемещать |
Сообщ.
#78
,
|
|
|
Совсем не помню, чего там писал
Почитаю... moveToThread - переносит объект (QObject) из данного потока в какой-то другой. Каждый Qt object "живет" в каком-то потоке. Это означает, что обработка вызова его слотов будет происходит в этом потоке. zss а ты пробовал его запускать? |
Сообщ.
#79
,
|
|
|
zss
Так что непонятно-то? Экземпляр класса Client - это объект, методы которого нам надо исполнить. В примере его метод start() выполняет какие-то вычисления. Объект должен "жить" в не GUI потоке. DummyMainWindow - класс, который осуществляет взаимодействие с пользователем. В его конструкторе запускается параллельный "вычисляющий" поток. Создается "вычисляющий" объект. Перемещается в вычисляющий поток. Далее, при помощи сигнала кнопки clicked() вызываем слот "вычисляющего" объекта invokeStart(). Обратная связь с GUI-потоком реализуется при помощи сигналов methodStarted() и methodFinished(). Все сигнал-слот соединения имеют тип Qt::QueuedConnection. Т.е. вызовы слотов происходят асинхронно. |