Qt邮箱程序改良版(信号和槽)
上一版代码可以正常使用,但是会报错
上一篇文章
错误信息 "QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread" 指出了一个问题,即在非主线程中尝试启用或禁用套接字通知器(QSocketNotifier)。在Qt中,与GUI相关的操作,包括网络操作,通常需要在主线程中进行,因为Qt的GUI不是线程安全的。
在你的代码中,你使用了 QtConcurrent::run 来在后台线程中运行网络操作,这是不正确的,因为Qt的网络类(如QTcpSocket)不是线程安全的。尝试在非主线程中使用它们会导致这个错误。
要解决这个问题,你需要确保所有的网络操作都在主线程中进行。这里有几种方法可以解决这个问题:
-
不使用QtConcurrent::run:将网络操作放在主线程中,不使用QtConcurrent::run。这可能不是最佳解决方案,因为它会导致UI线程阻塞,用户体验不佳。
-
使用QThread和QObject::moveToThread:创建一个新的QThread,并将网络操作的QObject移动到这个新线程中。这样可以确保网络操作在新线程中运行,而不会干扰主线程。
-
使用Qt的网络线程:如果你使用的是QTcpSocket等网络类,可以考虑使用Qt自己的网络线程,例如通过QNetworkAccessManager来处理网络请求。
改进版本(信号与槽机制)
所以,为了改善代码,使用信号与槽机制对网络操作进行改善,通过在主线程触发信号,并传递参数到新线程workerThread的networkhandler对象进行数据处理,在触发信号返回主线程更新主线程的响应信息,达到自动化发送邮件的功能
效果展示

networkhandler.h
#ifndef NETWORKHANDLER_H
#define NETWORKHANDLER_H#include <QObject>class NetworkHandler : public QObject
{Q_OBJECT
public:explicit NetworkHandler(QObject *parent = nullptr);void handleData(const QString &data);signals:void updateTextBrowser(const QString &msg);
};#endif // NETWORKHANDLER_H
networkhandler.cpp
#include "networkhandler.h"NetworkHandler::NetworkHandler(QObject *parent): QObject{parent}
{}void NetworkHandler::handleData(const QString &data){if (data.isEmpty()){return;}emit this->updateTextBrowser(data);
}
tcpmailclient.h
#ifndef TCPMAILCLIENT_H
#define TCPMAILCLIENT_H#include <QObject>
#include <QObject>
#include <QtNetwork>
#include <QSslSocket>
#include <QSslCertificate>
#include <QSslKey>
#include <QTcpSocket>
#include <QHostAddress>
#include <QIODevice>
#include <QApplication>
#include <QDebug>class TCPMailClient : public QObject
{Q_OBJECT
public:explicit TCPMailClient(const QString &host, quint16 port,QObject *parent = nullptr);void send(QString msg);QString recieve();void close();private:QSslSocket* ssl;bool isentrcyed = false;signals:void Connected();void readyRead();
};#endif // TCPMAILCLIENT_H
tcpmailclient.cpp
#include "tcpmailclient.h"TCPMailClient::TCPMailClient(const QString &host, quint16 port, QObject *parent): QObject{parent} {this->ssl = new QSslSocket(this);QObject::connect(ssl, &QSslSocket::encrypted, [=]() {this->isentrcyed = true;qDebug() << "连接成功";});QObject::connect(ssl, &QSslSocket::connected, this,[this]() {qDebug() << "已连接到SMTP服务器";emit this->Connected();});// = 是复制要传递的变量, &是引用QObject::connect(ssl,&QSslSocket::readyRead,[&](){emit this->readyRead();});QObject::connect(ssl, &QSslSocket::errorOccurred, this,[](QAbstractSocket::SocketError socketError){qDebug() << "发生错误:" << socketError;});ssl->connectToHostEncrypted(host, port);// 可以连接信号,以确认数据已经发送connect(ssl, &QSslSocket::bytesWritten, this, [](qint64 bytes) {qDebug() << bytes << "bytes were written to the socket.";});
}void TCPMailClient::send(QString msg) {if (ssl->state() == QAbstractSocket::ConnectedState) {qDebug() << "发送" << msg;this->ssl->write(msg.toUtf8());} else {qDebug() << "SMTP连接未建立";}
}QString TCPMailClient::recieve() {// 等待并读取响应// if (ssl->waitForReadyRead()) {// QByteArray data = ssl->readAll(); // 读取所有可用数据// qDebug() << "响应内容:" << data;// return QString(data);// }// return QString();QByteArray data = ssl->readAll(); // 读取所有可用数据qDebug() << "响应内容:" << QString::fromUtf8(data);// return QString(data);return QString::fromUtf8(data); // 编码成字符串
}void TCPMailClient::close()
{ssl->disconnectFromHost();this->ssl->close();
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include "networkhandler.h"
#include "tcpmailclient.h"
#include <QWidget>
#include <QtConcurrent/QtConcurrent>
#include <QDebug>
#include <QCoreApplication>
#include <QFuture>
#include <QMessageBox>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTsignals:void dataReceived(const QString& data);public:Widget(QWidget *parent = nullptr);~Widget();void startWork();void dealWithResponse(const QString& response);private slots:void on_pushButton_clicked();void on_pushButton_2_clicked();private:Ui::Widget *ui;TCPMailClient *mailclient;QThread *workerThread;NetworkHandler * networkhandler;int sendState = 0;bool hassendUsername = false;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QFuture>
#include <QtConcurrent/QtConcurrent>Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->resize(600, 400);this->setWindowIcon(QIcon(":/icon/src/mail_icon.png"));
}Widget::~Widget() {on_pushButton_2_clicked();delete ui;
}void Widget::startWork()
{this->workerThread = new QThread;this->networkhandler = new NetworkHandler();this->networkhandler->moveToThread(workerThread);// 连接信号和槽connect(this, &Widget::dataReceived, networkhandler, &NetworkHandler::handleData);// connect(worker, &Worker::finished, workerThread, &QThread::quit);// connect(worker, &Worker::finished, worker, &Worker::deleteLater);connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);// 有新的数据可读时触发readyRead信号connect(mailclient,&TCPMailClient::readyRead,[this](){qDebug() << "触发readyRead信号";QString data = this->mailclient->recieve();emit this->dataReceived(data);});// 通过触发信号回到ui线程更新connect(networkhandler, &NetworkHandler::updateTextBrowser, this,[this](const QString &msg) {// 使用 Qt::QueuedConnection 确保在主线程中执行ui->textBrowser->append(msg);// 更新之后发送对应信息dealWithResponse(msg);}, Qt::QueuedConnection);// 启动线程workerThread->start();}
void Widget::dealWithResponse(const QString& response) {if (response.startsWith("220") && sendState == 0) {// 服务器已准备好接收命令this->mailclient->send("HELO xxx\r\n");sendState = 1; // 移动到下一个状态} else if (response.startsWith("250")) {// 如果服务器回复250,通常表示前一个命令成功执行switch (sendState) {case 1:this->mailclient->send("AUTH LOGIN\r\n");sendState = 2; // 准备发送用户名break;case 5:this->mailclient->send(QString("RCPT TO:<%1>\r\n").arg(ui->lineEdit_4->text()));sendState = 6; // 准备发送邮件数据break;case 6:// 服务器准备接收邮件数据this->mailclient->send("DATA\r\n");sendState = 7; // 邮件数据发送状态case 8:// 已发送退出QMessageBox::information(this,"提示信息","发送成功");break;default:qDebug() << "Unexpected 250 response in state" << sendState;break;}} else if (response.startsWith("334")) {// 服务器要求认证信息if (sendState == 2) {// 发送Base64编码的用户名QString username = QString::fromLatin1(QString("aaaa@163.com").toUtf8().toBase64());this->mailclient->send(username + "\r\n");qDebug() << username;sendState = 3; // 准备发送密码} else if (sendState == 3) {// 发送Base64编码的密码QString password = QString::fromLatin1(QString("MfjaiokgaaLN").toUtf8().toBase64());qDebug() << password;this->mailclient->send(password + "\r\n");sendState = 4; // 完成登录,准备发送MAIL FROM}} else if (response.startsWith("235")) {// 认证成功this->mailclient->send(QString("MAIL FROM:<%1>\r\n").arg(ui->lineEdit_3->text()));sendState = 5; // 准备发送RCPT TO} else if (response.startsWith("354") && sendState == 7) {// 发送邮件数据this->mailclient->send(QString("FROM:%1\r\n").arg(ui->lineEdit_3->text()));this->mailclient->send(QString("SUBJECT:%1\r\n").arg(ui->lineEdit_5->text()));this->mailclient->send(QString("TO:%1\r\n").arg(ui->lineEdit_4->text()));// 发送空行,隔开邮件正文和内容this->mailclient->send("\r\n");this->mailclient->send(ui->textEdit->toPlainText() + "\r\n");this->mailclient->send(".\r\n");this->mailclient->send("QUIT\r\n");// 退出状态sendState = 8;} else if (response.startsWith("5")) {// 5xx表示服务器端的错误qDebug() << "服务端报错" << response;} else {// 未识别的响应qDebug() << "Unrecognized SMTP response:" << response;}
}// 退出按钮
void Widget::on_pushButton_2_clicked() {QApplication::quit();
}// 发送按钮
void Widget::on_pushButton_clicked() {mailclient = new TCPMailClient("smtp.163.com", 465);// 连接之后触发槽函数处理接下来的步骤connect(mailclient,&TCPMailClient::Connected,this,&Widget::startWork);
}
相关文章:
Qt邮箱程序改良版(信号和槽)
上一版代码可以正常使用,但是会报错 上一篇文章 错误信息 "QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread" 指出了一个问题,即在非主线程中尝试启用或禁用套接字通知器(QSocketNotifier)…...
入门到精通mysql数据(四)
5、运维篇 5.1、日志 5.1.1、错误日志 错误日志是MySQL中最重要的日志之一,它记录了当mysqld启动和停止,以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时,建议首先查看此日志。 该日志是默认开启的,默认存放目录/var/log…...
Java 设计模式 详解
在Java开发中,设计模式是一种常见的、成熟的解决方案,用于应对特定的设计问题和复杂性管理。以下是一些常用的设计模式,它们可以分为三类:创建型模式、结构型模式和行为型模式。 一、创建型模式 创建型模式主要负责对象的创建&a…...
卡尔曼滤波学习资料汇总
卡尔曼滤波学习资料汇总 其实,当初的目的,是为了写 MPU6050 的代码的,然后不知不觉学了那么多,也是因为好奇、感兴趣吧 有些还没看完,之后笔记也会同步更新的 学习原始材料 【卡尔曼滤波器】1_递归算法_Recursive P…...
linux003.在ubuntu中安装cmake的方法
1.cmake安装程序下载 https://cmake.org/files/v3.30/ 2.解压并下载包 解压cmake压缩包 tar -xvzf cmake.tar.gz进入解压目录 cd cmake-<version>编辑~/.bashrc nano ~/.bashrc在文件的末尾添加如下代码 export PATH/home/xwl/software/cmake/bin:$PATH然后运行以…...
EtherNet/IP转Profinet网关连接发那科机器人配置实例解析
本案例主要展示了如何通过Ethernet/IP转Profinet网关实现西门子1200PLC与发那科搬运机器人的连接。所需的设备有西门子1200PLC、开疆智能Ethernet/IP转Profinet网关以及Fanuc机器人。 具体配置步骤:打开西门子博图配置软件,添加PLC。这是配置的第一步&am…...
自动化运维-检测Linux服务器CPU、内存、负载、IO读写、机房带宽和服务器类型等信息脚本
前言:以上脚本为今年8月1号发布的,当时是没有任何问题,但现在脚本里网络速度测试py文件获取不了了,测速这块功能目前无法实现,后面我会抽时间来研究,大家如果有建议也可以分享下。 脚本内容: #…...
ubuntu24.04设置开机自启动Eureka
ubuntu24.04设置开机自启动Eureka 之前我们是在/root/.bashrc的文件中增加了一条命令 nohup java -jar /usr/software/eurekaServer-auth-prd-03.jar > /usr/software/log.log 2>&1 &但上面这条命令只有在登录root的用户时,才会执行,如果…...
从视频帧生成点云数据、使用PointNet++模型提取特征,并将特征保存下来的完整实现。
文件地址 https://github.com/yanx27/Pointnet_Pointnet2_pytorch?spm5176.28103460.0.0.21a95d27ollfze Pointnet_Pointnet2_pytorch\log\classification\pointnet2_ssg_wo_normals文件夹改名为Pointnet_Pointnet2_pytorch\log\classification\pointnet2_cls_ssg "E:…...
工化企业内部能源能耗过大 落实能源管理
一、精准监测与数据分析 实时准确的数据采集 企业能耗管理系统能够对企业内各种能源(如电、水、气、热等)的使用情况进行实时监测。通过安装在能源供应线路和设备上的智能传感器,可以精确地采集能源消耗的各项数据,包括瞬时流量、…...
LSTM 和 LSTMCell
1. LSTM 和 LSTMCell 的简介 LSTM (Long Short-Term Memory): 一种特殊的 RNN(循环神经网络),用于解决普通 RNN 中 梯度消失 或 梯度爆炸 的问题。能够捕获 长期依赖关系,适合处理序列数据(如自然语言、时间序列等&…...
python成长技能之正则表达式
文章目录 一、认识正则表达式二、使用正则表达式匹配单一字符三、正则表达式之重复出现数量匹配四、使用正则表达式匹配字符集五、正则表达式之边界匹配六、正则表达式之组七、正则表达式之贪婪与非贪婪 一、认识正则表达式 什么是正则表达式 正则表达式(英语&…...
解决docker报Error response from daemon Get httpsregistry-1.docker.iov2错误
解决docker报Error response from daemon: Get "https://registry-1.docker.io/v2/"错误 报错详情 首先先看一下问题报错效果,我想要拉去nacos-serve:1.1.4的镜像,报如下错误,从报错信息可以看到,用于网络的愿意&…...
【论文分享】利用多源大数据衡量街道步行环境的老年友好性:以中国上海为例
本次给大家带来一篇SCI论文的全文翻译!该论文考虑了绿化程度、可步行性、安全性、形象性、封闭性和复杂性这六个指标,提出了一种基于多源地理空间大数据的新型定量评价模型,用于从老年人和专家的角度评估街道步行环境的老年友好程度ÿ…...
说说软件工程中的“协程”
在软件工程中,协程(coroutine)是一种程序运行的方式,可以理解成“协作的线程”或“协作的函数”。以下是对协程的详细解释: 一、协程的基本概念 定义:协程是一组序列化的子过程,用户能像指挥家…...
使用IDE实现java端远程调试功能
使用IDE实现java端远程调试功能 1. 整体描述2. 前期准备3. 具体操作3.1 修改启动命令3.2 IDE配置3.3 打断点3.4 运行Debug 4. 总结 1. 整体描述 在做项目时,有些时候,需要和第三方进行调式,但是第三方不在一起,需要进行远程调试&…...
javaScript交互案例2
1、京东侧边导航条 需求: 原先侧边栏是绝对定位当页面滚动到一定位置,侧边栏改为固定定位页面继续滚动,会让返回顶部显示出来 思路: 需要用到页面滚动事件scroll,因为是页面滚动,所以事件源是document滚动…...
JavaScript 浏览器对象模型 BOM
浏览器对象模型(Browser Object Model,BOM)是指一组与浏览器进行交互的 JavaScript 对象。它允许 JavaScript 与浏览器的组件进行交互,比如窗口、文档、历史记录等。BOM 不同于 DOM(文档对象模型)ÿ…...
基于MATLAB的激光雷达与相机联合标定原理及实现方法——以标定板为例
1.为什么要进行激光雷达和相机的联合标定? 激光雷达和相机的联合标定是为了将两种传感器的数据统一到同一坐标系中,从而实现更准确的环境感知。激光雷达提供精准的三维距离信息,而相机捕捉丰富的纹理和颜色,通过联合标定可以结合两…...
React(一)
文章目录 项目地址一、创建第一个react项目二、JSX语法2.1 生成列表2.2 大括号识别JS的表达式2.3 列表循环array2.4 条件判断以及假值显示2.5 复杂条件渲染2.6 事件处理2.7 添加CSS样式2.8 添加图片2.9 使用Fregments返回多个根标签2.10多条件渲染2.11 导出子组件2.12 给子组件…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
