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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Блокируется интерфейс, не понятно почему  (Прочитано 6227 раз)
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« : Август 16, 2011, 18:48 »

Есть plugin1, у которого есть GUI. Есть plugin2, у которого тоже есть GUI, и вызывается функция библиотеки, написанной на C, которая что-то считает. Из интерфейса plugin1 пользователь дает команду (кнопочкой) на запуск вычислений в plugin2, для чего тот вызывает функцию из библиотеки. Иногда библиотека плюется сообщениями, которые надо поймать и показать в GUI plugin1. Сообщения она выдает через функцию-хук, который присутствует в plugin2. Этот хук эмитирует сигнал, содержащий сообщение. Сигнал попадает в слот plugin1 и тот показывает текст. Все сигналы типа Qt::AutoConnection. Библиотека считает долго и нудно, разумеется, если все сделать влоб в одном потоке, то весь GUI будет блокирован. Поэтому библиотека помещена в отдельный QThread, откуда и вызывается. Так было сделано сразу, давно, и тогда в plugin2 использовался класс, наследовавший QMainWindow, в нем и был реализован сигнал с сообщением. Там все работает, как надо, ничего не блокируется. Но возникла необходимость сделать по-другому, и QMainWindow в новой версии plugin2 уже не наследуется. Поток создается, запускается, сообщения идут - но весь GUI (и plugin1, и plugin2, и остальных плагинов главного потока) оказывается блокирован. Предположил, что QMainWindow создает автоматически цикл обработки сообщений, благодаря чему с ним GUI работает. Попытался сделать тоже самое для plugin2, причем чтобы запустить в правильном порядке библиотеку и цикл сообщений, не придумал ничего лучше, как запустить счет по сигналу таймера после запуска цикла. То есть, вот так примерно (код не полный, явно незначимое выброшено, и plugin1 для простоты опущен, с ним нет проблем, поскольку со старым вариантом plugin2 отлично работает):

Код:
class RunThread : public QThread
{
    Q_OBJECT
public:
    explicit RunThread( QObject *parent = 0 );
    void run();

private slots:
    void execute();

private:
    QTimer timer;
};

RunThread::RunThread(QObject *parent) :
    QThread(parent)
{
    //moveToThread( this ); не влияет
}

void RunThread::run()
{
    timer.singleShot( 10, this, SLOT(execute()) );
    qDebug() << "starting loop";
    exec();
}

extern "C" int c_library();

void RunThread::execute()
{
    qDebug() << "starting processor";
    c_library();
    quit();
}

class plugin2 : public QObject, public plugininterface
{
    Q_OBJECT
    Q_INTERFACES( plugininterface )

public:
    virtual ~plugin2(){}
    virtual void startPlugin();
    void lib_message( QString );

signals:
    void sigRelayMessage( QString );
public slots:
    run_c_library(); // сигнал на запуск попадает сюда
    void threadFinished();

private:
    RunThread* runner;
}

static plugin2* p;

void plugin2::startPlugin()
{
    runner = new RunThread(); // без родителя
    connect( runner, SIGNAL(finished()), SLOT(threadFinished()) );
    p = this;
}

extern "C" void lib_message( char* msg ) // этот хук вызывает C-библиотека
{
    p->lib_message( QObject::tr(msg) );
}

void plugin2::lib_message( QString msg )
{
    emit sigRelayMessage( msg );
}

void plugin2::run_c_library()
{
    runner->start();
}

в отладчике, как и ожидалось, появляется сначала starting loop, потом практически сразу starting processor. Библиотека считает, сообщения от нее идут, появляются в GUI plugin1. Однако весь GUI всё равно заблокирован... И вот тут я пока не вижу вариантов, куда копать. Непонимающий
« Последнее редактирование: Август 16, 2011, 19:02 от Гурман » Записан

2^7-1 == 127, задумайтесь...
LisandreL
Птица говорун
*****
Offline Offline

Сообщений: 984


Надо улыбаться


Просмотр профиля
« Ответ #1 : Август 16, 2011, 19:42 »

Для начала проверьте в каком потоке у вас реально идёт обсчёт ( qDebug() << QThread::currentThread(); из функции обсчёта и из гуя ) .
Записан
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #2 : Август 16, 2011, 19:56 »

о, это хорошая идея... проверил

в считалке:

Код:
void RunThread::execute()
{
    qDebug() << "starting processor";
    qDebug() << currentThread();
    c_library();
    quit();
}

и в запускалке

Код:
void plugin2::run_c_library()
{
    runner->start();
    qDebug() << QThread::currentThread();
}

результат неожиданный - если в конструкторе RunThread нет вызова moveToThread(this) то нити одинаковые

а если есть, то RunThread работает в своем потоке, но... интерфейс всё равно блокирован

кстати, Qt:AutoConnection динамически в какой момент определяет как передается сигнал? в момент connect, или в момент передачи самого сигнала? мне что-то помнилось, что вроде в момент передачи...

« Последнее редактирование: Август 16, 2011, 20:05 от Гурман » Записан

2^7-1 == 127, задумайтесь...
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #3 : Август 16, 2011, 20:55 »

кажется, нащупал, в чем проблема... но пока еще не 100% уверен
Записан

2^7-1 == 127, задумайтесь...
LisandreL
Птица говорун
*****
Offline Offline

Сообщений: 984


Надо улыбаться


Просмотр профиля
« Ответ #4 : Август 16, 2011, 20:59 »

Вроде бы в момент вызова поток, в котором вызывается emit сравнивается с потоком принимающего потока: http://habrahabr.ru/blogs/qt_software/115835/

Попробуйте явно во всех коннектах прописать Qt::QueuedConnection, хотя бы для проверки.
Записан
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #5 : Август 16, 2011, 21:11 »

если сравнивается в момент вызова, значит это именно подстройка под разные потоки, то есть, соединения на потоки не влияют

на самом деле, пробовал, прописывал QueuedConnection у тех сигналов, которые эмитятся из потока, не помогает, да и не должно - в том варианте, который работает, соединения AutoConnection

тут не совсем понятное поведение обнаружил - GUI таки откликается, но очень плохо, можно если долго и часто кликать, то один раз переключить окно, первая мысль, разумеется, что застревают события, но вставка processEvents в функцию вывода текста не помогла, хотя должна была бы, если дело в этом, и опять же - в работающем варианте вызова processEvents нет
Записан

2^7-1 == 127, задумайтесь...
LisandreL
Птица говорун
*****
Offline Offline

Сообщений: 984


Надо улыбаться


Просмотр профиля
« Ответ #6 : Август 16, 2011, 21:39 »

тут не совсем понятное поведение обнаружил - GUI таки откликается, но очень плохо
Такое наблюдал, когда в главном потоке очень часто вызывался слот из очереди, изменяющий содержимое окна. Может у вас тоже в новом алгоритме emit'ы из обработчика слишком частые?
Записан
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #7 : Август 16, 2011, 21:48 »

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

Цитировать
Может у вас тоже в новом алгоритме emit'ы из обработчика слишком частые?

Алгоритм счета тот же, что и в старом варианте, С-библиотека просто та же самая. И эмиты точно такие же, ровно с той же частотой. Но там все без проблем работает, а тут нет. Причем, блокируется не одно окно, плагины приложения создают свои окна - события перестают проходить ко всем окнам. И опять же - processEvents не помогает ни в какую. Специально протащил указатель на главное QApplication до вывода строк, вставил его прямо перед emit:

Код:
void plugin2::lib_message( QString msg )
{
    _a->processEvents();    
    emit sigRelayMessage( msg );
}

не помогает. На самом деле, трансляция сигналов внутри там сама делает processEvents, поскольку сигналы попадают в event loop. То есть, и не удивительно, что не помогает. Удивительно, что сигналы не проходят.

Сейчас попробую еще один тест...

О! Действительно, проблема похоже в частой передаче сигналов. Отключил выдачу сообщений из библиотеки - все стало работать. Теперь еще более не понятно, поскольку в старой версии все работает без этого.
« Последнее редактирование: Август 16, 2011, 21:52 от Гурман » Записан

2^7-1 == 127, задумайтесь...
LisandreL
Птица говорун
*****
Offline Offline

Сообщений: 984


Надо улыбаться


Просмотр профиля
« Ответ #8 : Август 16, 2011, 22:19 »

В слоте изменяете GUI? Вот у вас вся очередь событий и забита вызовом вашего слота и перерисовкой.
В слоте аккумулируйте изменения в каком-нибудь массиве/списке и с некоторой периодичностью вызывайте слот, который все эти изменения будет применять скопом.

Я писал примерно так:
Слот, соединённый с обработчиком:
Код
C++ (Qt)
void Widget :: addLog( ... )
{
   MessageRecord record;
   record.text = text;
   ...
   newRecords.append( record );
}

Вывод в гуй:
Код:
void Widget :: updateLog()
{
    if ( !newRecords.isEmpty() )
    {
      ui->log->setUpdatesEnabled( false );
      while ( !newRecords.isEmpty() )
      {
         MessageRecord message = newRecords.takeLast();
         MyItem* item = new MyItem();
         item->setText( 0, ... );
         ui->log->insert( 0, item );
      }
      ui->log->setUpdatesEnabled( true );
    }
    QCoreApplication::processEvents();
    QTimer::singleShot( 1, this, SLOT( updateLog() ) );
}

Записан
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #9 : Август 16, 2011, 22:41 »

в слоте сообщения валится в QPlainTextEdit, типа консоль

но, черт побери, в СТАРОЙ ВЕРСИИ ТОЖЕ САМОЕ, и все без проблем работает... причем там даже QTextEdit, а не QPlainTextEdit, он вроде еще медленнее

а, не, вру - в обоих случаях QTextEdit, видимо у QPlainTextEdit чего-то не хватало, не помню уже точно

ну старая версия немного медленнее выводит, но всего лишь в 2 раза

чудеса какие-то...
« Последнее редактирование: Август 16, 2011, 22:50 от Гурман » Записан

2^7-1 == 127, задумайтесь...
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #10 : Август 17, 2011, 00:14 »

Все, победил. Немного подумав, сделал это соединение для выдачи сообщений типа Qt::BlockingQueuedConnection. И все стало, как надо. Никакие дополнительные буфера не нужны, поскольку очередь - это и так буфер, а при этом типе поток приостанавливается, пока сигнал не будет передан.

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

BTW: когда я обнаружил, что дело в передаче сигналов, то переделал класс RunThread на вот такой:

Код:
class RunThread : public QThread
{
    Q_OBJECT
public:
    explicit RunThread( QObject *parent = 0 );
    void run();
};

RunThread::RunThread(QObject *parent) :
    QThread(parent)
{
}

void RunThread::run()
{
    c_library();
}


и все прекрасно работает, без всяких таймеров и exec()
Записан

2^7-1 == 127, задумайтесь...
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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