LINUX.ORG.RU

[QT] QThread

 


0

0

Снова вопросы...)

Есть GUI и есть функция, которая очень долго считает. Хочу эту функцию запустить в отдельном потоке, чтобы можно было в GUI на кнопочки жмякать. Сделал наследника от QThread

ExtrThread::ExtrThread(curve *c, string p, QPushButton *b): QThread()
{
    cur = c;
    path = p;
    button = b;
    //run();
    moveToThread( this );
}
сделал для него метод run:
void ExtrThread::run(){
    isRuning = true;
    button->setText("Stop");
    qDebug()<<"Thread Start\n";

    core(cur, path);// моя функция, которую хочу запустить параллельно с GUI

    //system("cat /dev/random");

    button->setText("Run");
    qDebug()<<"Thread Stop\n";
    isRuning = false;
    exec();
        emit stopped();
}

так вызываю:
ExtrThread *extr;
.........
extr = new ExtrThread(&cur, "opt.conf", ui->pushButton);
                extr->start();

При запуске ф-я считает, но окошко не реагирует на нажатия кнопок и пр. Если вместо моей ф-и записать system(«cat /dev/random»), то все ОК: окно ресайзится, кнопочки нажимаются, а в консоли бегут кракозябы.
Вопросы:
1. Почему с функцией так не получается?
2. моя ф-я из себя потом вызывает через system(...) внешнюю программу. Как мне можно перехватить вывод в консоль, чтобы его направить, к примеру, в QTextEdit?

пардон!

При запуске ф-я считает, но окошко не реагирует на нажатия кнопок и пр.

Это если run сделать public и запускать через extr->run().
Если через extr->start(), то авще вылетает.

spike_by
() автор топика

> 2. моя ф-я из себя потом вызывает через system(...) внешнюю программу. Как мне можно перехватить вывод в консоль, чтобы его направить, к примеру, в QTextEdit?

#include <QProcess>
class ... {
    ...
private:
    ...
    QProcess *m_mplayer;
    ...
private slots:
    ...
    void slotMplayerData();
    void slotMplayerError(QProcess::ProcessError);
    void slotMplayerExit(int, QProcess::ExitStatus);
    ...
};

...

...::...() : m_mplayer(new QProcess(this)) {
    ...
    connect(m_mplayer, SIGNAL(readyReadStandardOutput()), SLOT(slotMplayerData()));
    connect(m_mplayer, SIGNAL(error(QProcess::ProcessError)), SLOT(slotMplayerError(QProcess::ProcessError)));
    connect(m_mplayer, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(slotMplayerExit(int,QProcess::ExitStatus)));
    ...
}

bool ...::openFile(const QString &filename) {
    ...
    QStringList args;
    ...
    m_mplayer->start("mplayer", args);
    ...
}

void ...::slotMplayerData() {
    QByteArray data = m_mplayer->readAllStandardOutput();
    ...
}

...

остальное в манах.

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

arsi ★★★★★
()

>есть функция, которая очень долго считает. Хочу эту функцию запустить в отдельном потоке

QtConcurrent::run( Function )

summatus
()
Ответ на: комментарий от spike_by

> а без использования QtConcurrent::run( Function )?

Хотелось бы стандартным средствами (через потоки).

Чем это кутред стандартнее кутеконкаррента? Второй ещё и проще в данной ситуации.

const86 ★★★★★
()
Ответ на: комментарий от const86

Note that the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting.

т.е. на сколько я понимаю, его нельзя убить?

spike_by
() автор топика

Из чужого треда нельзя рисовать GUI (это я про button->setText(). Надо подавать signal основному треду, чтобы рисовал он. В данный момент ты вызываешь ->run() в основном треде, где все и выполняется, отсюда и задержка отрисовки, и отсутствие падения. Новый тред стартует только по start().

ftor
()

Как заметили выше, у тебя функция вызывается в основном потоке, поєтому и не ожидаемый результат.
Переделай примерно так:

void ExtrThread::run()
{
QMetaObject::invokeMethod(this, SLOT(mySlot()),Qt::QueuedConnection);
exec();
}

void ExtrThread::mySlot()
{
emit MyRun();
core(cur, path);
emit MyStop();
}

Хотя я больше склоняюсь к использованию QtConcurrent.

Kristi
()
Ответ на: комментарий от Kristi

Спасибо за советы.
Сейчас разбираюсь.

spike_by
() автор топика

1. Убрать в конструкторе moveToThread(this)
2. Как уже сказали - делать button->setText() из другого потока нельзя
3. Поскольку сам обьект extr и приёмник его сигнала живут в одном потоке - QObject::connect(extr, SIGNAL(stopped()), this, SLOT(extrStopped())) создаст прямое соединение, а поскольку сигнал испустится из другого потока - то и слот выполнится в другом потоке, что приведёт к плачевным последствиям. Используйте последним аргументом в QObject::connect(..., Qt::QueuedConnection).
4. Неясно что делает функция с curve - если она её изменяет, а основная программа рисует - должны быть средства синхронизации.

Dendy ★★★★★
()
Ответ на: комментарий от Kristi
void ExtrThread::run()
{
QMetaObject::invokeMethod(this, SLOT(mySlot()),Qt::QueuedConnection);
exec();
}

QMetaObject::invokeMethod(... Qt::QueuedConnection) выполнит метод в потоке, которому принадлежит обьект, то-есть в главном потоке. Запись:

ExtrnThread::ExtrnThread()
{
  ...
  moveToThread(this);
}

Выглядит странно. Даже если это работает, получается собитыйный механизм для эклемпляра потока мёртв, пока кто-то явно его не стартует. И неясно как он будет работать, если exec() отсутствует или завершился. Поэтому рекомендую не использовать такие сомнительные конструкции, а предполагать, что экземпляр обьекта живёт в главном потоке.

Dendy ★★★★★
()
Ответ на: комментарий от Dendy

curve - это структура

struct curve{
        vector<string>  inFile;
        vector<string>  outFile;
        vector<string>  expFile;
        vector<string>  spiceParam;
        vector<string>  spiceMin;
        vector<string>  spiceInit;
        vector<string>  spiceMax;
        string AlgOpt;
        string model_path;
        double result;
        ///////////////////
        vector<string>  spiceOldVal;
    }; 
функция изменяет curve, а именно значение result на основании остальных данных.

spike_by
() автор топика
Ответ на: комментарий от Dendy

> 1. Убрать в конструкторе moveToThread(this)
Это можно и не делать, ниже по ссылке в комментариях говорится, что вроде и сами Тролли этим балуются
http://vingrad.ru/blogs/sabrog/2009/06/11/qt-45-movetothread-ili-metod-myunhg...

QMetaObject::invokeMethod(... Qt::QueuedConnection) выполнит метод в >потоке, которому принадлежит обьект, то-есть в главном потоке

Если принять в расчёт выше сказанное, то запись вполне будет корректной.

P.S. Чего-то SLOT() зря написал.
QMetaObject::invokeMethod(this, «myFunc», Qt::QueuedConnection);

Kristi
()
Ответ на: комментарий от Dendy

Это не сомнительная инструкция, а вполне нормальный подход. После вызова moveToThread() события объекта, включая вызов слота mySlot(), будут «вариться» в другом event loop'e, который в свою очередь начнет шевелиться после вызова exec().

Также, например:
http://www.linux.org.ru/jump-message.jsp?msgid=3741719&cid=3744421

mannaz
()
Ответ на: комментарий от spike_by

от такой помеси stl с qt стновится жутковато...

xydo ★★
()
Ответ на: комментарий от Dendy

Не ругайте сильно.
Вот пока что мой класс (реализация).

#include "extrthread.h"
#include <QDebug>
#include <QProcess>
//#include <QtConcurrent>
#include <qtconcurrentrun.h>



//ExtrThread::ExtrThread(curve *c, string p, QPushButton *b): QRunnable()
ExtrThread::ExtrThread(curve *c, string p, QPushButton *b): QThread()
{
    cur = c;
    path = p;
    button = b;
    //moveToThread( this );
    connect(this, SIGNAL(gostart()), this, SLOT(mySlot()),Qt::QueuedConnection); //без этого не начинает работу

}

ExtrThread::ExtrThread(): QThread()
{
}

void ExtrThread::run(){
    emit gostart(); //без этого не начинает работу
    QMetaObject::invokeMethod(this, SLOT(mySlot()),Qt::QueuedConnection);
    exec();

//    go();

}

void ExtrThread::kill(){
    endOfStream = true;
}

void ExtrThread::go(){
     core(cur, path);
}

void ExtrThread::mySlot()
{
emit started();
core(cur, path);
emit stopped();
}

Без коннекта вообще не запускается.
ф-я core создает дополнительный класс в котором используется *cur.
Вот часть инициализации этого класса:

bool SpiceExtr::Init(curve *cur){

    qDebug()<<"\nINIT\n";

    qDebug()<<"bool SpiceExtr::Init(curve *cur)\n";
    /*for(int i=0;i<cur->inFile.size();i++){
        qDebug()<<QString::fromStdString(cur->outFile.at(i))<<"\n";
        qDebug()<<QString::fromStdString(cur->outFile.at(i))<<"\n";
        qDebug()<<QString::fromStdString(cur->expFile.at(i))<<"\n";
    }*/

    qDebug()<<"SpiceExtr::Init Model Path = ";
    qDebug()<<QString::fromStdString(cur->model_path)<<"\n";

на строчке qDebug()<<QString::fromStdString(cur->model_path)<<«\n»; все падает (т.е. при перфом обращении к полям структуры).
На сколько понимаю, не получается корректно передать указатель на структуру.
Подскажие еще раз, где я туплю.
Спасибо.

spike_by
() автор топика
Ответ на: комментарий от Kristi

Получилась адская смесь всех советов =)


Именно(.

spike_by
() автор топика
Ответ на: комментарий от spike_by

Я бы делал примерно так:

class CurveData : public QSharedData
{
...
};

class Curve
{
public:
    Curve()
    {
        d_ = new CurveData;
    }

private:
    QSharedDataPointer<CurveData> d_;
};

// forget about QPushButton
ExtrThread::ExtrThread(const Curve & c, string p)
{
    // in header: Curve cur;
    cur = c;
    path = p;
    //button = b;
} 
 
void ExtrThread::run()
{
    emit started();
    core(cur, path);
    emit finished(cur.result);
} 

void ExtrThread::kill()
{
    endOfStream = true; 
}

И используйте Qt-контейнеры вместо STL в структуре CurveData, они при изменении копии Curve в главном потоке отделят каждый из контейнеров атомарно без выделения памяти. К примеру, если в эту же структуру записать результат result после того как выполнится рассчёт.

Dendy ★★★★★
()
Ответ на: комментарий от Dendy

Парень и так запутался неплохо, а ты все со своим copy-on-write'ом.

mannaz
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.