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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: QNetworkConfigurationManager и QThread  (Прочитано 11013 раз)
virtual_root
Гость
« : Апрель 23, 2012, 13:43 »

Здравствуйте! Ребята, помогите пожалуйста разобраться.
Мне в приложении понадобилось создать 2 класса в которых я создаю объект QNetworkConfigurationManager.
Первый класс авторизуется на сервере, а второй получает данные с сервера. Второй класс является потоком.
Приложение компилируется,но в процессе работы вываливается следующая ошибка:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNetworkAccessManager(0x8c8d808), parent's thread is MyThread(0x8da5980), current thread is QThread(0x8a9dfd0)
Я уже кучу статей перечитала как разрешить эту проблему, пересмотрела много примеров на форуме, но мне не помогло.
Привожу код класса потока, так как эта ошибка от него идет:
Код:
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTimer>
#include <QNetworkConfigurationManager>
#include <Parser>
#include <Serializer>
#include <QObjectHelper>
#include <QTime>
#include "macroconfig.h"

class MyThread : public QThread
{
    Q_OBJECT
public:
    enum Status{
        PointsPay,
        FullInfo
    };
    explicit MyThread(QObject *parent = 0);
    void getPointsPay(int);
    QString getPlatform() ;
    void setServerTime(QString);
protected:
    void run();
private:
    QString m_skey;
    QNetworkAccessManager *m_manager;
    QNetworkReply *m_reply;
    QNetworkConfigurationManager m_config;
    Status m_flag;
    QTimer m_timer;
    QTimer m_TimerWating;
    QTime m_lastTimeInfo;
    QTime m_lastTimePay;
    QString m_serverTime;
    QString m_os;
    QMap<int,QVariantMap> m_pointsPay;
   
signals:
    QVariantMap succes(QVariantMap);
    QMap<int,QVariantMap> succes(QMap<int,QVariantMap>);
    QString Error(QString);
   
public slots:
        void getFullInfo();
        void finish(QNetworkReply*);
        void Error(QNetworkReply::NetworkError);
        void isOnlain();
        void setParamAndStart(QString skey);
   
};

#endif // MYTHREAD_H


Код:
#include "mythread.h"
#include <QDebug>

MyThread::MyThread(QObject *parent) :
    QThread(parent)
{
    //moveToThread(this);   если раскоментировать то функция run вообще не вызывается....
    m_manager = new QNetworkAccessManager();
    m_manager->moveToThread(this);
    connect(m_manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(finish(QNetworkReply*)));
    connect(&m_TimerWating,SIGNAL(timeout()),this,SLOT(isOnlain()));
    m_lastTimeInfo.addSecs(-60);
    m_lastTimePay.addSecs(-60);
    m_os = this->getPlatform();
    m_timer.setInterval(60000);
}

void MyThread::setParamAndStart(QString skey){  // устанавливаем ключ и запускаем поток
    if (!skey.isEmpty()) {
        m_skey = skey;
        start();   // запускаем поток. Пробовала и из main.cpp но ничего не изменилось
    }
}

void MyThread::run(){
    qDebug()<<"run";
    getPointsPay(1);
    //getFullInfo();
    m_timer.start();
    exec();             // использовать собственную очередь событий
}

void MyThread::getPointsPay(int type){  // поработать с объектом QTime
   qDebug()<<__FUNCTION__<<type;
    m_flag = PointsPay;
    //if (m_lastTimePay.addSecs(30) <= QTime::currentTime()){
        QString Responce = "/?r=client/paymentplacesinfo&place_type_id=%1";
        Responce = HOST + Responce.arg(QString::number(type));
        if (m_config.isOnline()){
            m_lastTimePay = QTime::currentTime();
            m_reply = m_manager->get(QNetworkRequest(Responce));
            m_reply->moveToThread(this);
            m_reply->ignoreSslErrors();
            connect(m_reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(Error(QNetworkReply::NetworkError)));
        }
        else  m_TimerWating.start(30000);
   // }

}

void MyThread::getFullInfo(){
  m_flag = FullInfo;
  if (m_lastTimePay.addSecs(30) <= QTime::currentTime()){   // проверяем как давно мы запрашивали эту информацию
        QString param = "/?r=client/getfullInfo&skey=%1&platform=%2&date=%3";
        param = HOST+param.arg(m_skey,m_os,m_serverTime);
        if (m_config.isOnline()){
            m_lastTimeInfo = QTime::currentTime();
            m_reply = m_manager->get(QNetworkRequest(param));
            connect(m_reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(Error(QNetworkReply::NetworkError)));
            m_reply->ignoreSslErrors();
        }
        else  m_TimerWating.start(30000);

   }

}

void MyThread::finish(QNetworkReply* reply){
    QByteArray json = reply->readAll();
    QJson::Parser parser;
    bool ok;
    QVariantMap buffer = parser.parse (json, &ok).toMap();
    if (ok){
        if (m_flag == PointsPay){
            m_pointsPay[buffer["current"].toInt()] = buffer;
            if (buffer["current"].toInt()!=3)
                this->getPointsPay(buffer["current"].toInt()+1); // запрашиваем следующую точку оплаты
            else this->getFullInfo(); // получаем пользовательскую информацию
        }
       else {
            emit succes(buffer);  // возвращаем пользовательскую информацию
            QVariantMap news = buffer["news"].toMap();
            QList<QVariant> created = news.value("created").toList();
            QList<QVariant> modified = news.value("modified").toList();
            QList<QVariant> deleted = news.value("deleted").toList();
            if (created.isEmpty() || modified.isEmpty() || deleted.isEmpty()){
                this->getPointsPay(1);
            }
            m_timer.start();
        }
    }

}

void MyThread::Error(QNetworkReply::NetworkError error){
    switch(error)
       {

       case QNetworkReply::ConnectionRefusedError:{
           emit Error(QString::fromLocal8Bit("Сервер перегружен."));
           break;
       }

       case QNetworkReply::ContentNotFoundError:{
               emit Error(QString::fromLocal8Bit("Ошибка сервера 404."));
               break;
           }
       case QNetworkReply::HostNotFoundError:{
               emit Error( QString::fromLocal8Bit("Не найден удаленный сервер."));
               break;
           }

       default: {
               emit Error(m_reply->errorString());
               break;
           }
       }
    m_TimerWating.start(60000); // спустя минуту попытаемся вновь соедениться с сервером
}

void MyThread::isOnlain(){
    if (m_config.isOnline()){
        m_TimerWating.stop();
        if (m_flag == PointsPay)
            this->getPointsPay(m_pointsPay.count());
        else this->getFullInfo();
    }
}

void MyThread::setServerTime(QString time){
    this->m_serverTime = time;
}

QString MyThread::getPlatform() {
#if defined(Q_OS_WIN)
    return "winxp";
#elif defined(Q_OS_LINUX)
    return "linux";
#elif defined(Q_OS_MACOSX)
    return "macos";
#endif
}

Подскажите,пожалуйста,что я не так делаю???
Записан
V1KT0P
Гость
« Ответ #1 : Апрель 23, 2012, 14:00 »

Здравствуйте! Ребята, помогите пожалуйста разобраться.
Мне в приложении понадобилось создать 2 класса в которых я создаю объект QNetworkConfigurationManager.
Первый класс авторизуется на сервере, а второй получает данные с сервера. Второй класс является потоком.
Приложение компилируется,но в процессе работы вываливается следующая ошибка:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNetworkAccessManager(0x8c8d808), parent's thread is MyThread(0x8da5980), current thread is QThread(0x8a9dfd0)
Я уже кучу статей перечитала как разрешить эту проблему, пересмотрела много примеров на форуме, но мне не помогло.
Привожу код класса потока, так как эта ошибка от него идет:
Код:
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTimer>
#include <QNetworkConfigurationManager>
#include <Parser>
#include <Serializer>
#include <QObjectHelper>
#include <QTime>
#include "macroconfig.h"

class MyThread : public QThread
{
    Q_OBJECT
public:
    enum Status{
        PointsPay,
        FullInfo
    };
    explicit MyThread(QObject *parent = 0);
    void getPointsPay(int);
    QString getPlatform() ;
    void setServerTime(QString);
protected:
    void run();
private:
    QString m_skey;
    QNetworkAccessManager *m_manager;
    QNetworkReply *m_reply;
    QNetworkConfigurationManager m_config;
    Status m_flag;
    QTimer m_timer;
    QTimer m_TimerWating;
    QTime m_lastTimeInfo;
    QTime m_lastTimePay;
    QString m_serverTime;
    QString m_os;
    QMap<int,QVariantMap> m_pointsPay;
   
signals:
    QVariantMap succes(QVariantMap);
    QMap<int,QVariantMap> succes(QMap<int,QVariantMap>);
    QString Error(QString);
   
public slots:
        void getFullInfo();
        void finish(QNetworkReply*);
        void Error(QNetworkReply::NetworkError);
        void isOnlain();
        void setParamAndStart(QString skey);
   
};

#endif // MYTHREAD_H


Код:
#include "mythread.h"
#include <QDebug>

MyThread::MyThread(QObject *parent) :
    QThread(parent)
{
    //moveToThread(this);   если раскоментировать то функция run вообще не вызывается....
    m_manager = new QNetworkAccessManager();
    m_manager->moveToThread(this);
    connect(m_manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(finish(QNetworkReply*)));
    connect(&m_TimerWating,SIGNAL(timeout()),this,SLOT(isOnlain()));
    m_lastTimeInfo.addSecs(-60);
    m_lastTimePay.addSecs(-60);
    m_os = this->getPlatform();
    m_timer.setInterval(60000);
}

void MyThread::setParamAndStart(QString skey){  // устанавливаем ключ и запускаем поток
    if (!skey.isEmpty()) {
        m_skey = skey;
        start();   // запускаем поток. Пробовала и из main.cpp но ничего не изменилось
    }
}

void MyThread::run(){
    qDebug()<<"run";
    getPointsPay(1);
    //getFullInfo();
    m_timer.start();
    exec();             // использовать собственную очередь событий
}

void MyThread::getPointsPay(int type){  // поработать с объектом QTime
   qDebug()<<__FUNCTION__<<type;
    m_flag = PointsPay;
    //if (m_lastTimePay.addSecs(30) <= QTime::currentTime()){
        QString Responce = "/?r=client/paymentplacesinfo&place_type_id=%1";
        Responce = HOST + Responce.arg(QString::number(type));
        if (m_config.isOnline()){
            m_lastTimePay = QTime::currentTime();
            m_reply = m_manager->get(QNetworkRequest(Responce));
            m_reply->moveToThread(this);
            m_reply->ignoreSslErrors();
            connect(m_reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(Error(QNetworkReply::NetworkError)));
        }
        else  m_TimerWating.start(30000);
   // }

}

void MyThread::getFullInfo(){
  m_flag = FullInfo;
  if (m_lastTimePay.addSecs(30) <= QTime::currentTime()){   // проверяем как давно мы запрашивали эту информацию
        QString param = "/?r=client/getfullInfo&skey=%1&platform=%2&date=%3";
        param = HOST+param.arg(m_skey,m_os,m_serverTime);
        if (m_config.isOnline()){
            m_lastTimeInfo = QTime::currentTime();
            m_reply = m_manager->get(QNetworkRequest(param));
            connect(m_reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(Error(QNetworkReply::NetworkError)));
            m_reply->ignoreSslErrors();
        }
        else  m_TimerWating.start(30000);

   }

}

void MyThread::finish(QNetworkReply* reply){
    QByteArray json = reply->readAll();
    QJson::Parser parser;
    bool ok;
    QVariantMap buffer = parser.parse (json, &ok).toMap();
    if (ok){
        if (m_flag == PointsPay){
            m_pointsPay[buffer["current"].toInt()] = buffer;
            if (buffer["current"].toInt()!=3)
                this->getPointsPay(buffer["current"].toInt()+1); // запрашиваем следующую точку оплаты
            else this->getFullInfo(); // получаем пользовательскую информацию
        }
       else {
            emit succes(buffer);  // возвращаем пользовательскую информацию
            QVariantMap news = buffer["news"].toMap();
            QList<QVariant> created = news.value("created").toList();
            QList<QVariant> modified = news.value("modified").toList();
            QList<QVariant> deleted = news.value("deleted").toList();
            if (created.isEmpty() || modified.isEmpty() || deleted.isEmpty()){
                this->getPointsPay(1);
            }
            m_timer.start();
        }
    }

}

void MyThread::Error(QNetworkReply::NetworkError error){
    switch(error)
       {

       case QNetworkReply::ConnectionRefusedError:{
           emit Error(QString::fromLocal8Bit("Сервер перегружен."));
           break;
       }

       case QNetworkReply::ContentNotFoundError:{
               emit Error(QString::fromLocal8Bit("Ошибка сервера 404."));
               break;
           }
       case QNetworkReply::HostNotFoundError:{
               emit Error( QString::fromLocal8Bit("Не найден удаленный сервер."));
               break;
           }

       default: {
               emit Error(m_reply->errorString());
               break;
           }
       }
    m_TimerWating.start(60000); // спустя минуту попытаемся вновь соедениться с сервером
}

void MyThread::isOnlain(){
    if (m_config.isOnline()){
        m_TimerWating.stop();
        if (m_flag == PointsPay)
            this->getPointsPay(m_pointsPay.count());
        else this->getFullInfo();
    }
}

void MyThread::setServerTime(QString time){
    this->m_serverTime = time;
}

QString MyThread::getPlatform() {
#if defined(Q_OS_WIN)
    return "winxp";
#elif defined(Q_OS_LINUX)
    return "linux";
#elif defined(Q_OS_MACOSX)
    return "macos";
#endif
}

Подскажите,пожалуйста,что я не так делаю???

Твоя проблема в том что надо сперва почитать справку и разобраться с особенностями многопоточного программирования. Тот код который записан в конструкторе выполняется в том потоке в котором вызван а не из того потока который создается. И есть некоторые классы которые нельзя перекидывать из потока в поток. Таким классом является QNetworkAccessManager. Ты его создаешь в конструкторе и он привязывается к тому потоку который создает новый тред. А затем пытаешься его запихать в новый тред. Здесь уже давно говорилось что наследоваться от треда нежелательно, особенно когда не понимаешь что делаешь.
Записан
Bepec
Гость
« Ответ #2 : Апрель 23, 2012, 14:04 »

ВИКТОР!!! Изыди!

Наследоваться от QThread можно и это решение не имеет никаких отрицательных сторон. Уже разбирали это.

по теме:

Проблема у вас в том, что создавать QNAM(менегер), надо либо в функции run потока, либо использовать moveToThread.

PS к тому же moveToThread вроде никак на run() не может повлиять Подмигивающий
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Апрель 23, 2012, 14:06 »

MyThread::MyThread(QObject *parent) :
    QThread(parent)
Уберите parent или подавайте NULL. Раскомментите moveToThread(this);
Записан
V1KT0P
Гость
« Ответ #4 : Апрель 23, 2012, 14:09 »

ВИКТОР!!! Изыди!
Чего это ты так завелся =).
Записан
alexis031182
Гость
« Ответ #5 : Апрель 23, 2012, 14:16 »

Проблема в том, что объект QThread, а соответственно и все переменные и объекты, которые он содержит, принадлежат основному потоку приложения. Но при этом осуществляется попытка создания дочернего объекта в потоке, который реализует QThread.

Я бы посоветовал сменить концепцию использования многопоточности в Вашем приложении, поскольку объект класса QThread не является потоком как таковым, он лишь предоставляет возможность выполнения пользовательских операций в отдельном потоке.

На мой взгляд, использовать многопоточность лучше посредством реализации отдельного класса, не унаследованного от QThread:
Код:
class MyClass : public QObject
{
   Q_OBJECT
signals:
   //Сигнал о завершении работы "void process()"
   void finished();

public:
   //Обращаем внимание, что в конструкторе не указываем родителя,
   //но при этом можем внести какие-либо другие параметры.
   MyClass() : QObject() {}

   virtual ~MyClass() {}

public slots:
   void process() {
      //Здесь наш код, который должен выполниться в отдельном потоке.
      ...

      //Отсылаем сигнал о завершении выполнения.
      emit finished();
   }

private:
   //Здесь указываем наши приватные объекты и переменные.

};
Ну а теперь, собственно метод использования:
Код:
QThread *thread = new QThread;
MyClass *myclass = new MyClass();
myclass->moveToThread(thread);
connect(thread, SIGNAL(started()), myclass, SLOT(process()));
connect(myclass, SIGNAL(finished()), thread, SLOT(quit()));
connect(myclass, SIGNAL(finished()), myclass, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
Здесь мы создаём объект MyClass, перемещаем его в отдельный поток, и соединяем необходимые к обработке сигналы со слотами. Думаю, что из этого кода очевидна идея.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Апрель 23, 2012, 14:18 »

И есть некоторые классы которые нельзя перекидывать из потока в поток. Таким классом является QNetworkAccessManager. Ты его создаешь в конструкторе и он привязывается к тому потоку который создает новый тред. А затем пытаешься его запихать в новый тред. Здесь уже давно говорилось что наследоваться от треда нежелательно,
Ну переброс QNetworkAccessManager не имеет отношения к наследованию. Я не работаю с вебом и могу ошибаться, но мне интересно узнать почему QNetworkAccessManager нельзя перекидывать - вроде никаких оснований не видно. Ну хочу чтобы он крутился через eventLoop нитки, почему нельзя?
Записан
V1KT0P
Гость
« Ответ #7 : Апрель 23, 2012, 14:25 »

И есть некоторые классы которые нельзя перекидывать из потока в поток. Таким классом является QNetworkAccessManager. Ты его создаешь в конструкторе и он привязывается к тому потоку который создает новый тред. А затем пытаешься его запихать в новый тред. Здесь уже давно говорилось что наследоваться от треда нежелательно,
Ну переброс QNetworkAccessManager не имеет отношения к наследованию. Я не работаю с вебом и могу ошибаться, но мне интересно узнать почему QNetworkAccessManager нельзя перекидывать - вроде никаких оснований не видно. Ну хочу чтобы он крутился через eventLoop нитки, почему нельзя?
Ну дык справку почитай:
Цитировать
Changes the thread affinity for this object and its children. The object cannot be moved if it has a parent. Event processing will continue in the targetThread.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Апрель 23, 2012, 14:33 »

Ну дык справку почитай:
Цитировать
Changes the thread affinity for this object and its children. The object cannot be moved if it has a parent. Event processing will continue in the targetThread.
Мне это известно, так для любого объекта с парентом. А конкретно QNetworkAccessManager данный случай-то здесь причем?
Код
C++ (Qt)
m_manager = new QNetworkAccessManager();
 
Никакого парента я здесь не наблюдаю
Записан
V1KT0P
Гость
« Ответ #9 : Апрель 23, 2012, 14:48 »

Ну дык справку почитай:
Цитировать
Changes the thread affinity for this object and its children. The object cannot be moved if it has a parent. Event processing will continue in the targetThread.
Мне это известно, так для любого объекта с парентом. А конкретно QNetworkAccessManager данный случай-то здесь причем?
Код
C++ (Qt)
m_manager = new QNetworkAccessManager();
 
Никакого парента я здесь не наблюдаю
Я вот например удаление m_manager не вижу. Если он его вручную не удаляет, значит должен ему родителя назначить.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Апрель 23, 2012, 15:08 »

Я вот например удаление m_manager не вижу. Если он его вручную не удаляет, значит должен ему родителя назначить.
И что, проблема в деструкторе нитки его удалить? Или подать парентом MyThread. То есть выходит что QNetworkAccessManager можно перекидывать с тем же успехом как и любой др объект. А по-Вашему не так
И есть некоторые классы которые нельзя перекидывать из потока в поток. Таким классом является QNetworkAccessManager.
Чего наводить тень на плетень?  Улыбающийся

Записан
V1KT0P
Гость
« Ответ #11 : Апрель 23, 2012, 15:13 »

Я вот например удаление m_manager не вижу. Если он его вручную не удаляет, значит должен ему родителя назначить.
И что, проблема в деструкторе нитки его удалить? Или подать парентом MyThread. То есть выходит что QNetworkAccessManager можно перекидывать с тем же успехом как и любой др объект. А по-Вашему не так
И есть некоторые классы которые нельзя перекидывать из потока в поток. Таким классом является QNetworkAccessManager.
Чего наводить тень на плетень?  Улыбающийся
Да немного ошибся, бывает. Плохо выспался, голова не так соображает =(.
Записан
virtual_root
Гость
« Ответ #12 : Апрель 23, 2012, 22:29 »

Ребята, спасибо всем большое!!! Ваши советы помогли разобраться. Очень приятно, что столько откликнулись.
Записан
Alex_C
Гость
« Ответ #13 : Апрель 24, 2012, 08:25 »

На мой взгляд, использовать многопоточность лучше посредством реализации отдельного класса, не унаследованного от QThread:

Хотелось бы кстати уточнить - а почему? В чем разница наследования QThread от moveToThread. Как мне кажется - это как бы 2 варианта реализации одного и того же в итоге. Просто moveToThread проще что ли в записи, чем класс от QThread наследовать. Или все же есть какие то принципиальные отличия?
Записан
alexis031182
Гость
« Ответ #14 : Апрель 24, 2012, 10:29 »

Хотелось бы кстати уточнить - а почему? В чем разница наследования QThread от moveToThread. Как мне кажется - это как бы 2 варианта реализации одного и того же в итоге. Просто moveToThread проще что ли в записи, чем класс от QThread наследовать. Или все же есть какие то принципиальные отличия?
Оба варианта рабочие, однако наследование от QThread идёт врозь с идеологией использования этого класса. QThread - это не поток, а лишь инструмент управления потоком. По сути, в варианте с наследованием мы получаем монстрообразную форму, которая при выполнении программы может привести ко всяким неожиданностям, на вроде указанной в данном топике проблемы.

Не используя наследование, можно не заботиться о многих вещах, в т.ч. и о иерархии отношений объектов класса, выполняющегося в отдельном потоке. Все parent'ы указываем как обычно. И при этом можем легко перемещать выполняющийся класс из главного потока в параллельный и обратно даже в то время, когда оба из них выполняются. Например:
Код:
QThread *thread = new QThread(this); //Сам QThread может иметь parent'а
thread->start();
...
MyClass *myclass = new MyClass();
myclass->moveToThread(thread);
connect(this, SIGNAL(my_signal()), myclass, SLOT(process()));
connect(myclass, SIGNAL(finished()), this, SLOT(my_slot()));
Код:
void MyClass::process() {
   //Здесь наш код, который должен выполниться в отдельном потоке.
   ...

   //Перемещаем объект обратно в главный поток.
   moveToThread(QCoreApplication::instance()->thread());

   //Отсылаем сигнал о завершении выполнения.
   emit finished();
}
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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