问题
I have found an old thread that came close to answering this very question for me, its link can be found here QObject: Cannot create children for a parent that is in a different thread .
I know this is an old thread, but I am having a hard time following where to put the signals and slots in my situation. I am writing a program that will send out alerts via email, and I want them to be run in threads so they don't interrupt the main program and each other. I created an EmailThread class that inherits from QThread and implements the run() method. I have another class, Smtp.cpp, that successfully sends emails out but only in the main thread. I would like to create an instance of the Smtp class in each EmailThread for each address that should receive an email. I also have a namespace that handles all my database information. I want to be able to send information from my database to all the entered email addresses if something goes wrong. So which class gets the slots and which one emits the signals? And where would I place them, code examples to help me figure it out would be greatly appreciated.
//EmailThread Header
#ifndef EMAILTHREAD_H
#define EMAILTHREAD_H
#include <QThread>
#include "smtp.h"
class EmailThread : public QThread
{
public:
explicit EmailThread(QString to, QString from, QString subject, QString message);
signals:
public slots:
protected:
void run();
private:
Smtp * emailer;
QString to;
QString from;
QString subject;
QString message;
};
#endif // EMAILTHREAD_H
//EmailThread.cpp
#include "emailthread.h"
EmailThread::EmailThread(QString emailTo, QString emailFrom, QString emailSubject, QString emailMessage)
{
emailer = new Smtp("myaddress@email.com","myPassword", "secure.emailsrvr.com",465, 300000);
to = emailTo;
from = emailFrom;
subject = emailSubject;
message = emailMessage;
}
void EmailThread::run(){
emailer->sendMailNoAttachments(from,to, subject, message);
}
Called Email
EmailThread * newEmailThread = new EmailThread("myaddress@email.com","myaddress@email.com", "Subject Test", "This is the message string");
newEmailThread->start();
and I get the same error as above, where the parent and child thread are not the same. Here is the full error :
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSslSocket(0xc6dde0), parent's thread is QThread(0xf52410), current thread is QThread(0xc619c8)
QObject::connect: Cannot queue arguments of type 'QAbstractSocket::SocketState'
(Make sure 'QAbstractSocket::SocketState' is registered using qRegisterMetaType().)
Thank you for your consideration!
回答1:
The method call to sendMainNoAttachments happens in the run method of your EmailThread class, which means it is correctly happening on another thread. However, your error stems from the fact that your emailer is getting created on the main/GUI thread.
This is because a QThread should be thought of as a "thread manager" class, and as such only its run method operates on the thread it manages. This means its constructor is executed on the thread that called it.
To overcome this, you can create your emailer in your run method, if you don't have problems with creating a new one every time. However, I'd instead highly recommend changing to a different usage of QThread, which is recommended when you want to have interactions between objects on separate threads.
You subclass QObject and move the function you want to perform in a thread to a slot within it. Then you use QObject::moveToThread to move the object to the thread. If any of its slots are signalled from a queued connection (which is automatic if the connection happens with an object that has been moved to another thread), they will be executed on that thread. Note that direct function calls to your worker object will still be executed on the main thread. You can read more about this in documentation for QThread.
First, you can create a standard QThread. We can use your Smtp class if it is type QObject. If it's not, you can create a wrapper and route signals through it. Next, you'll use moveToThread to move your Smtp instance to the new thread. Then, you'll need to connect a signal with your message contents to a slot that sends your mail on to your Smtp class.
Here's some example code detailing a possible solution.
Mailer.cpp
#include "Mailer.hpp"
Mailer::Mailer(QObject* parent) :
QObject(parent), emailer(45, 300000), thread(this) {
emailer.moveToThread(&thread);
thread.start();
w.show();
connect(&w, &MainWindow::sendMail,
&emailer, &Smtp::sendMailNoAttachments);
}
Mailer.hpp
#ifndef MAILER_HPP
#define MAILER_HPP
#include "Smtp.hpp"
#include "MainWindow.hpp"
#include <QObject>
#include <QThread>
#include <memory>
class Mailer : public QObject {
Q_OBJECT
public:
explicit Mailer(QObject* parent = nullptr);
private:
Smtp emailer;
QThread thread;
MainWindow w;
};
#endif // MAILER_HPP
Smtp.cpp
#include "Smtp.hpp"
Smtp::Smtp(int port, int number, QObject* parent)
: QObject(parent) {
}
void Smtp::sendMailNoAttachments(const QString& emailTo,
const QString& emailFrom,
const QString& emailSubject,
const QString& emailMessage) {
// Send that email!
}
Smtp.hpp
#ifndef SMTP_HPP
#define SMTP_HPP
#include <QObject>
class Smtp : public QObject {
Q_OBJECT
public:
explicit Smtp(int port, int number, QObject* parent = nullptr);
public slots:
void sendMailNoAttachments(const QString& emailTo,
const QString& emailFrom,
const QString& emailSubject,
const QString& emailMessage);
};
#endif // SMTP_HPP
MainWindow.cpp
#include "MainWindow.hpp"
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent) {
QWidget* centralWidget = new QWidget(this);
QLabel* toLabel = new QLabel(QStringLiteral("To: "));
toLineEdit = new QLineEdit(centralWidget);
QLabel* fromLabel = new QLabel(QStringLiteral("From: "));
fromLineEdit = new QLineEdit(centralWidget);
QLabel* subjectLabel = new QLabel(QStringLiteral("Subject: "));
subjectLineEdit = new QLineEdit(centralWidget);
QLabel* messageLabel = new QLabel(QStringLiteral("Message: "));
messageTextEdit = new QTextEdit(centralWidget);
QPushButton* mailButton = new QPushButton(centralWidget);
mailButton->setText(QStringLiteral("Send Mail"));
QVBoxLayout* layout = new QVBoxLayout(centralWidget);
layout->addWidget(toLabel);
layout->addWidget(toLineEdit);
layout->addWidget(fromLabel);
layout->addWidget(fromLineEdit);
layout->addWidget(subjectLabel);
layout->addWidget(subjectLineEdit);
layout->addWidget(messageLabel);
layout->addWidget(messageTextEdit);
layout->addWidget(mailButton);
this->setCentralWidget(centralWidget);
connect(mailButton, &QPushButton::clicked, this, &MainWindow::prepareMail);
}
void MainWindow::prepareMail() {
emit sendMail(toLineEdit->text(), fromLineEdit->text(),
subjectLineEdit->text(), messageTextEdit->toPlainText());
}
MainWindow.hpp
#ifndef MAINWINDOW_HPP
#define MAINWINDOW_HPP
#include <QMainWindow>
#include <QLineEdit>
#include <QTextEdit>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
signals:
void sendMail(const QString& to, const QString& from,
const QString& subject, const QString& message);
public slots:
void prepareMail();
private:
QLineEdit* toLineEdit = nullptr;
QLineEdit* fromLineEdit = nullptr;
QLineEdit* subjectLineEdit = nullptr;
QTextEdit* messageTextEdit = nullptr;
};
#endif // MAINWINDOW_HPP
main.cpp
#include "Mailer.hpp"
#include <QApplication>
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
Mailer mailer;
return a.exec();
}
来源:https://stackoverflow.com/questions/25572432/qt-multithreaded-email-qobject-cannot-create-children-for-a-parent-that-is-in