Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: Vladimir от Март 20, 2012, 11:35



Название: Зависание GUI
Отправлено: Vladimir от Март 20, 2012, 11:35
Доброго дня! Есть такая проблемка: по сети, в отдельном потоке приходят данные, заполняя глобальную структуры данных, объявленную как extern! После того как вся инфа была получена отсылается сигнал, связанный со слотом в главном потоке, который отрисовывает полученную инфу из глобальной структуры! При этом сигналы невсегда доходят и интерфейс программы висит! Может нужно заменить сигналы на события? Если да, то как это сделать.. нужно реализовывать собственный класс событий??


Название: Re: Зависание GUI
Отправлено: Пантер от Март 20, 2012, 11:36
Код показывай.


Название: Re: Зависание GUI
Отправлено: Vladimir от Март 20, 2012, 11:56
код достаточно большой.. основные выдержки:
прием данных

Код:
void TSignalReceiver::run( )
{
        int ioSize = 0;

        bool timedOut = false;
        static uint azimuth = 0;

        QString strPortData  = "3111";

        while ( !isDone( ) )
        {
                if ( paused( ) )
                {
                        // если поток приостановлен - ждем...
                        currentThread( )->msleep( pausedThreadDelay );
                        continue;
                }
         
               CNetworkStd::closeSocket( socket );
               socket = CNetworkStd::connectClientTcp(settings.stationIp.toAscii( ),
                                                              strPortData.toAscii( ),
                                                                   1000, &timedOut );

                if ( socket == invalid_socket )
                {
                        // обработка...
                        continue;
                }

                 while ( !isDone( ) )
                {
                        if ( paused( ) )
                        {
                                // если поток приостановлен - ждем...
                                currentThread( )->msleep( pausedThreadDelay );
                                continue;
                        }

                        if ( CNetworkStd::recvData( socket,
                                                    reinterpret_cast< char* >( &packetFromHw ), // структура, куда записываютс данные
                                                    sizeof( packetFromHw ),
                                                    ioSize,
                                                    3000,
                                                    &timedOut ) == false )
                        {
                                if ( timedOut )
                                {
                                        // обработка
                                        continue;
                                }
             
                        }
                         qDebug() << "Az = " << packetFromHw.Az; // эта строка выводится каждый раз, когда пришли данные
                         emit onRecvSignal(); // если данные получены отослать сигнал !!!!!!


конект сигнала со слотом
Код:
connect(tSignReceiv,SIGNAL(onRecvSignal()),indWin,SLOT(slotDrawIndicat()));

// отрисовка
Код:
void COpenGlEngine::slotDrawIndicat()
{
     
       int az =  packetFromHw.Az;
       qDebug() << "Az = " << az; // а здесь уже данные выводятся не каждый раз, а через 3-4 раза..
                                            // хотя сигнал отсылается каждый раз, как данные в полном объеме получены

       ...
}


Название: Re: Зависание GUI
Отправлено: V1KT0P от Март 20, 2012, 11:59
Так и не понял проблема в том что только сигнал не доходит или GUI зависает. Или и то и другое? А как ты в отдельный поток код обработки выносишь?


Название: Re: Зависание GUI
Отправлено: Пантер от Март 20, 2012, 12:02
Что-то у тебя напутано. Отправляй структуру в сигнале и лови ее в слоте, а не используй глобальную структуру.


Название: Re: Зависание GUI
Отправлено: mutineer от Март 20, 2012, 12:03
и больше кода не помешает


Название: Re: Зависание GUI
Отправлено: Vladimir от Март 20, 2012, 12:09
Так и не понял проблема в том что только сигнал не доходит или GUI зависает. Или и то и другое? А как ты в отдельный поток код обработки выносишь?

и сигнал не доходит, и все элементы интерфейса не реагируют на нажатие, всё дико висит! "А как ты в отдельный поток код обработки выносишь?" в каком плане? из главного потока запускается поток TSignalReceiver и там принимаются данные..! с этим проблем нет, а вот в том что они не всегда читаются.. ибо сигнал не всегда доходит - проблема!


Название: Re: Зависание GUI
Отправлено: Vladimir от Март 20, 2012, 12:12
а каким образом передать структуры в сигнале и принять ее в слоте?
структура
Код:
struct dataPacket
        {
            dataPacket( ) : Az( 0 ), TpCnt( 0 ), nagcFactor( 0 ), isJammer( 0 )
            {
                    qMemSet( data, 0, sizeof( uchar ) * distancePoints );
            }

            alt_u16 Az;
            alt_u16 TpCnt;
            alt_u8  data[2400];
            alt_u8  nagcFactor;
            alt_u8  isJammer;
            alt_8   dummy[ 2 ];
            
        };

дойдет ли она корректно и всегда, как только получены данные, если даже просто сигнал не всегда доходит?


Название: Re: Зависание GUI
Отправлено: V1KT0P от Март 20, 2012, 12:22
Так и не понял проблема в том что только сигнал не доходит или GUI зависает. Или и то и другое? А как ты в отдельный поток код обработки выносишь?

и сигнал не доходит, и все элементы интерфейса не реагируют на нажатие, всё дико висит! "А как ты в отдельный поток код обработки выносишь?" в каком плане? из главного потока запускается поток TSignalReceiver и там принимаются данные..! с этим проблем нет, а вот в том что они не всегда читаются.. ибо сигнал не всегда доходит - проблема!
TSignalReceiver - наследуется от QThread-а? Если да то ты его после объявления moveToThread-ил сам в себя?


Название: Re: Зависание GUI
Отправлено: mutineer от Март 20, 2012, 12:23
Если да то ты его после объявления moveToThread-ил сам в себя?

Вот не надо так делать


Название: Re: Зависание GUI
Отправлено: V1KT0P от Март 20, 2012, 12:29
Если да то ты его после объявления moveToThread-ил сам в себя?

Вот не надо так делать
Да знаю я уже =). Но он же мог вот так сделать и наступить на грабли.


Название: Re: Зависание GUI
Отправлено: Vladimir от Март 20, 2012, 12:33
Что значит "moveToThread-ил сам в себя"??
а наследуется так
Код:
class TSignalReceiver : public TAbstractThread
        {
                ....
         }
class TAbstractThread : public QThread
        {
                  ...
        }



Название: Re: Зависание GUI
Отправлено: mutineer от Март 20, 2012, 12:34
Покажи хедеры обоих классов (прием данных и гуй), как они создаются и как связываются


Название: Re: Зависание GUI
Отправлено: Vladimir от Март 20, 2012, 12:48
class TSignalReceiver описание
Код:
class TSignalReceiver : public TAbstractThread
        {
                Q_OBJECT

        public:
                TSignalReceiver( const QString& name, QObject* parent = 0 );
                virtual ~TSignalReceiver( );
                virtual void stop( );


        signals:
                void togglePeriod( );

                void setRadiation( int );

                void onRecvSignal();

        protected:
                virtual void run( );

        private:
                networkEvents netEvt;
                network::socket_type socket;
        };
Конструктор
Код:
TSignalReceiver::TSignalReceiver(const QString& name, QObject* p ) :
                                TAbstractThread( name, p ), socket( 0 )
{
       
}

class TAbstractThread описание
Код:
class TAbstractThread : public QThread
        {
                Q_OBJECT

        public:
                TAbstractThread( const QString& name, QObject* parent = 0 );
                virtual ~TAbstractThread( ) = 0;
             
                virtual void stop( );
               
                virtual bool isDone( ) const;
                virtual void setPause( bool p );
                virtual bool waitForPaused( uint msec = -1 );

               
                virtual bool paused( ) const;
                virtual QString name( ) const;

        signals:
               
                void setGlobalStatus( int sys, int state );
             
                void updateStationStatus( const SStatus& st );
        private:
                volatile bool done__;
                volatile bool pause__;

                QString name__;

                mutable QMutex mut__;
                mutable QWaitCondition wcPause__;
        };

В главной форме окна создается 
tSignReceiv = new TSignalReceiver( "RcvSig", this );
там же и связывается

connect(tSignReceiv,SIGNAL(onRecvSignal()),indWin,SLOT(slotDrawIndicat()));

Проект достаточно большой поэтому сложно выложить весь код подробно, будет много! Да, кстати, программа под Linux на Qt 4.8!






Название: Re: Зависание GUI
Отправлено: Vladimir от Март 20, 2012, 12:55
Закомментировал отрисовку и картина вообще удивлять стала! Чередование сообщений получается очень разное:
QDebug() << " в потоке сразу после приема данных";
QDebug() << " в слоте отрисовки";

Хотелось бы видеть:
QDebug() << " в потоке сразу после приема данных";
QDebug() << " в слоте отрисовки";
QDebug() << " в потоке сразу после приема данных";
QDebug() << " в слоте отрисовки";
QDebug() << " в потоке сразу после приема данных";
QDebug() << " в слоте отрисовки";

А получается:
QDebug() << " в потоке сразу после приема данных";
QDebug() << " в слоте отрисовки";
QDebug() << " в слоте отрисовки";
QDebug() << " в слоте отрисовки";
QDebug() << " в слоте отрисовки";

QDebug() << " в потоке сразу после приема данных";
QDebug() << " в потоке сразу после приема данных";
QDebug() << " в слоте отрисовки";

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


Название: Re: Зависание GUI
Отправлено: mutineer от Март 20, 2012, 13:00
Последовательно вряд ли получится - это ж разные потоки и общение между ними идет через eventLoop


Название: Re: Зависание GUI
Отправлено: Vladimir от Март 20, 2012, 13:02
Если реализовать заполнение структуры в том же потоке, но не данными по сети, то работает все как надо! проблемы начинаются, когда инфа начинает приходить по сети.. может как-то нужно синхронизировать этот процесс??


Название: Re: Зависание GUI
Отправлено: Bepec от Март 20, 2012, 13:31
Мб минимально компилируемый пример?


Название: Re: Зависание GUI
Отправлено: mutineer от Март 20, 2012, 13:35
Последовательно вряд ли получится - это ж разные потоки и общение между ними идет через eventLoop

Пардон, скорее всего должен быть прямой вызов, а не через eventLoop


Название: Re: Зависание GUI
Отправлено: V1KT0P от Март 20, 2012, 13:43
Последовательно вряд ли получится - это ж разные потоки и общение между ними идет через eventLoop

Пардон, скорее всего должен быть прямой вызов, а не через eventLoop
Так из не главного потока в графику лезть нельзя-же или я чего-то не знал? У меня кстати есть похожая ситуация, один поток принимает данные а второй обрабатывает. Так вот раньше было через слоты, я просто создавал новую структуру и указатель на нее через слот посылал второму а уж ее обрабатывал. Но у меня структуры маленькие и их много, а задержка должна быть минимальная, пришлось от слотов отказаться.


Название: Re: Зависание GUI
Отправлено: LisandreL от Март 20, 2012, 13:46
А получается:
QDebug() << " в потоке сразу после приема данных";
QDebug() << " в слоте отрисовки";
QDebug() << " в слоте отрисовки";
QDebug() << " в слоте отрисовки";
QDebug() << " в слоте отрисовки";

QDebug() << " в потоке сразу после приема данных";
QDebug() << " в потоке сразу после приема данных";
QDebug() << " в слоте отрисовки";

В общем различные комбинации, но не последовательно одна за другой..
Возможно отрисовывать не успеваете с той скоростью, с которой получаете данные (да ещё за счёт глобальной переменной и перетираете одни другими).

Вот так же всё по очереди будет:
connect(tSignReceiv,SIGNAL(onRecvSignal()),indWin,SLOT(slotDrawIndicat()), Qt::BlockingQueuedConnection );


Название: Re: Зависание GUI
Отправлено: mutineer от Март 20, 2012, 13:53
Последовательно вряд ли получится - это ж разные потоки и общение между ними идет через eventLoop

Пардон, скорее всего должен быть прямой вызов, а не через eventLoop
Так из не главного потока в графику лезть нельзя-же или я чего-то не знал? У меня кстати есть похожая ситуация, один поток принимает данные а второй обрабатывает. Так вот раньше было через слоты, я просто создавал новую структуру и указатель на нее через слот посылал второму а уж ее обрабатывал. Но у меня структуры маленькие и их много, а задержка должна быть минимальная, пришлось от слотов отказаться.

Ну вот если рассужадть логически. Принимающий объект живет в GUI-треде. Отправляющий объект кто? Наследник QThread. По идее он тоже живет в GUI-треде. Значит Qt должен использовать прямой вызов


Название: Re: Зависание GUI
Отправлено: V1KT0P от Март 20, 2012, 13:57
Ну вот если рассужадть логически. Принимающий объект живет в GUI-треде. Отправляющий объект кто? Наследник QThread. По идее он тоже живет в GUI-треде. Значит Qt должен использовать прямой вызов
Ну так если обработчик слотов треда в главном треде то все что в нем будет выполняться будет тормозить GUI. А он хочет вынести его в отдельный.


Название: Re: Зависание GUI
Отправлено: arttr от Март 20, 2012, 14:18
Да, да, да, вот в точности такая ситуация была QTcpSocket крутится в отдельном потоке, принимает данные, в этом же потоке происходит их (довольно сложное) преобразование, а потом они эмитятся в ГУИ-поток...головняка на неделю было...

проблему решили так: отказались от переопределения метода run в классе треда. Написали свой класс Thread, в котором был указатель на QThread как приватное поле QThread* thread. Написали слот
Код:
void Thread::start()
{
    thread = new QThread();
    moveToThread(thread);
    thread->moveToThread(thread);

    connect(thread, SIGNAL( started() ),
            this,     SLOT( _threadStarted() ) );


    thread->start();

}
реализовали слот threadStarted(). В этом слоте прописали весь код который должен выполняться в нашем потоке. Так же сделали слот quit (хотя может Вам и метода достаточно будет), к котором разрываем подключение, делаем deleteLater() всем полям класса нашего потока, а потом отстанавливаем поток. Выглятит это примерно так
Код:
    this->deleteLater();
    thread->quit();
Более подробно см. http://labs.qt.nokia.com/2006/12/04/threading-without-the-headache/ (http://labs.qt.nokia.com/2006/12/04/threading-without-the-headache/)

Еще про "сеть в отдельном потоке". Cетевые сокеты (QTcpSocket точно) являются асинхронными девайсами, это значит им для нормальной работы нужен работающий цикл обработки событий, а это в свою очередь значит, что создавать/удалять, создавать подключение/разрывать подключение, считывать/записывать нужно в работающем потоке. Поэтому и пришлось делать метод threadStarted.

Теперь структуру Вашу, объявленную extern'ом, если я правильно понял, то она же живет в главном потоке, а заполняется в дочернем данными по сети, еще бы ГУЙ не повис...делайте структуру в своем дочернем потоке и эмитируйте куда хотите...никакие blockingConnection не нужны будут

Дополнение: всем полям класса Thread обязательно делать moveToThread(thread)


Название: Re: Зависание GUI
Отправлено: Vladimir от Март 20, 2012, 15:05
Вроде выше писали, что moveToThread не есть хорошее решение.. что-то я по ходу жестко тупанул) зачем мне вообще отсылать сигнал, что данные пришли.. в глобальную структуры я эти данные записываю, а когда нужно отрисовать просто считываю! без всякого сигнала.. вроде так и работает! рисуется по данным из сети, но интерфейс всеравно притормаживает.. не так жестко конечно, но есть еще..


Название: Re: Зависание GUI
Отправлено: Bepec от Март 20, 2012, 15:24
Код в студию! Тогда и тормоза уберём. Причёску сделаем, автомат выдадим и на окопы ЗА РОДИНУУУ!!!!


Название: Re: Зависание GUI
Отправлено: arttr от Март 20, 2012, 15:49
Цитировать
Вроде выше писали, что moveToThread не есть хорошее решение..
moveToThread - это хорошее решение, если понимать зачем оно нужно.
Цитировать
интерфейс всеравно притормаживает.. не так жестко конечно, но есть еще..
интерфейс и будут замораживаться, именно для этого весь ресурсоемкий код выносят в отдельный поток. Получение данных по сети всяко происходит медленнее, чем отрисовка ГУИ => ГУИ притормаживает


Название: Re: Зависание GUI
Отправлено: mutineer от Март 20, 2012, 15:50
Вроде выше писали, что moveToThread не есть хорошее решение..

moveToThread хорошее решение, moveToThread(this) нехорошее


Название: Re: Зависание GUI
Отправлено: Bepec от Март 20, 2012, 15:56
Чем? :)

mutineer - Если ты точно незнаешь, то не советуй :) А спор у нас таки пришёл к тому, что это РАВНОПРАВНОЕ решение ;)


Название: Re: Зависание GUI
Отправлено: mutineer от Март 20, 2012, 15:59
Чем? :)

mutineer - Если ты точно незнаешь, то не советуй :) А спор у нас таки пришёл к тому, что это РАВНОПРАВНОЕ решение ;)

Ваш спор пришел к тому, что "я 50 раз запускал, у меня ниче не крешится, так что способ хороший", а с потоками так нельзя. Так что ты тоже точно не знаешь что это нормальное решение


Название: Re: Зависание GUI
Отправлено: Vladimir от Март 20, 2012, 16:23
получается данные приходят по сети, каждые 12 мс и это реализовано в отдельном потоке.. а отрисовка происходит по таймеру, каждые 40 мс (вызывается updateGL()) в GUI потоке! Данные накапливаются в глобальной структуре и каждые 40 мс отрисовываются на экране средствами OpenGL! при этом при открытии диалоговых окон, нажатии на кнопки программа чуток подвисает и в этот момент данные могут не отрисоваться (в случае открытия диалоговых окон)..


Название: Re: Зависание GUI
Отправлено: Bepec от Март 20, 2012, 16:49
Помоему у вас что-то с архитектурой. Т.е. к примеру у вас каждые 15 мс полная отрисовка + приём данных в основном потоке.


Название: Re: Зависание GUI
Отправлено: Vladimir от Март 20, 2012, 17:33
может быть и с архитектурой.. ибо прога переделывается не из лучшего варианта! но сейчас реализовано так, что данные принимаются в отдельном потоке (не в главном), а отрисовываются в главном через глобальную структуру! если не использовать эту глобальную структуру, то как передать данные из дочернего потока в главный?


Название: Re: Зависание GUI
Отправлено: V1KT0P от Март 20, 2012, 17:37
может быть и с архитектурой.. ибо прога переделывается не из лучшего варианта! но сейчас реализовано так, что данные принимаются в отдельном потоке (не в главном), а отрисовываются в главном через глобальную структуру! если не использовать эту глобальную структуру, то как передать данные из дочернего потока в главный?
Пошли сигнал с указателем на структуру.


Название: Re: Зависание GUI
Отправлено: Vladimir от Март 20, 2012, 17:45
так это будет правельнее, чем использование глобальной структуры? тобишь накапливать данные сколько мне нужно, а как понадобиться отрисовать отослать сигнал с указателем на структуру и перерисовать?


Название: Re: Зависание GUI
Отправлено: V1KT0P от Март 20, 2012, 17:50
так это будет правельнее, чем использование глобальной структуры? тобишь накапливать данные сколько мне нужно, а как понадобиться отрисовать отослать сигнал с указателем на структуру и перерисовать?
Да.


Название: Re: Зависание GUI
Отправлено: arttr от Март 20, 2012, 18:11
Vladimir, возможно я не правильно объясняю, возможно Вы меня просто не хотите понять.
Давайте попробуем еще раз на пальцах(с потоками в Qt не так просто, как кажется на первый взгляд): разберем, что происходит. В ГУИ-потоке живет некая глобальная структура; в ГУИ-потоке живут все виджеты и вся графика, их отрисовка происходит в цикле событий ГУИ-шного потока. Также, у Вас есть дочерний поток, который держит некое сетевое соединение. У этот дочерний поток крутит свой цикл обработки событий и именно в нем работает сокет сетевое соединение. Из дочернего потоке Вы ломитесь в главный поток, чтобы записать данные в структуру. и на этом цикл обработки событий гуи-потока притормаживается(читайте "притормаживается графика"), т.к. главный поток в это время посто ждет. Внимательно прочитайте http://habrahabr.ru/post/115830/ (http://habrahabr.ru/post/115830/), особенно пункты "События и цикл обработки событий" и "Блокирование цикла обработки событий". Человек все подробно и просто расписал.
Насчет как передавать эту структуру для отрисовки, V1KT0P предложил:
Цитировать
Пошли сигнал с указателем на структуру.
тоже самое предлагал я раньше:
Цитировать
делайте структуру в своем дочернем потоке и эмитируйте куда хотите
добавлю только что лучше передавать константную ссылку или просто копию.


Название: Re: Зависание GUI
Отправлено: Vladimir от Март 20, 2012, 18:28
Обязательно почитаю! Спасибо за помощь, буду пробовать..