多窗口编程:
1)程序启动时,先显示子窗口,再显示主界面//windows1
2)程序启动时,先显示主界面,再弹出子窗口//windows2
3)程序启动时,显示出多个窗口//windows3
项目:Http客户端(代码下载器)
1)需求分析
–》显示下载代码界面
–》处理"子目录"链接
–》实现对文件下载
2)概要设计
3)详细设计
4)编写代码
5)运行测试
*6)代码重构
7)产品发布
8)升级维护
Http协议简介:
1)请求:客户端向服务器发送消息
2)响应:服务器给客户端返回消息
QT中相关类:
1)QNetworkAccessManger//管理通信中请求和响应
2)QNetworkRequest//请求
3)QNetworkReply//响应
4)QUrl//网址
5)QFile/QFileInfo //文件操作
6)QDir//目录
7)QTextBrowser//图形控件:文本浏览器
工程名字:HttpClient
类名:MainWindow(默认)
忽略下面提示:
qt.network.ssl: QSslSocket: cannot resolve SSLv2_client_method
qt.network.ssl: QSslSocket: cannot resolve SSLv2_server_method
任务:
1)增加登录对话框
2)将下载文件操作,放到子线程中,实现多文件的同时下载
3)下载文件时,指定保存"/home/tarena/Downloads/"目录下面
参考代码如下:
HttP代码下载器:
头文件代码:
public:
QNetworkAccessManager* manager;//管理通信//************************************************
private:
QNetworkRequest request;//请求//************************************************
QNetworkReply* reply;//响应//************************************************
QUrl currentUrl;//记录当前的Url地址//************************************************
QByteArray buf;//保存接收的响应数据//************************************************
//容器,保存要支持下载的文件类型
QList<QString> fileType;//************************************************
private:
//向服务器发送请求
void sendRequest();//************************************************1
private slots:
//处理登录认证的槽函数
void onAuthenticationRequired(QNetworkReply*,QAuthenticator*);//************************************************2
//接收响应数据的槽函数
void onReadyRead();//************************************************3
//接收响应数据完成时执行的槽函数
void onFinished();//************************************************4
//处理目录链接的槽函数
void onAnchorClicked(const QUrl& url);//************************************************5
private:
//下载文件
void downloadFile(const QUrl& fileUrl);//************************************************6
源文件代码:
①《构造函数》MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
manager = new QNetworkAccessManager(this);//************************************************1
request.setUrl(QUrl("http://code.tarena.com.cn/"));//************************************************2
//向服务器发送请求
sendRequest();//************************************************3
//点击界面的链接时,发送信号anchorClicked,
//参数表示点击链接的URL地址
connect(ui->textBrowser,SIGNAL(anchorClicked(QUrl)),
this,SLOT(onAnchorClicked(QUrl)));//************************************************4
② 《向服务器发送请求》sendRequest()
//向服务器发送请求
reply = manager->get(request);//************************************************
//如果服务器需要进行登录认证,manager会发送认证
//信号:authenticationRequired
connect(manager,SIGNAL(
authenticationRequired(
QNetworkReply*,QAuthenticator*)),
this,SLOT(onAuthenticationRequired(
QNetworkReply*,QAuthenticator*)));//************************************************
//如果认证成功,响应数据到来,发送信号readyRead
connect(reply,SIGNAL(readyRead()),
this,SLOT(onReadyRead()));//************************************************
//响应数据接收结束,发送信号finished
connect(reply,SIGNAL(finished()),
this,SLOT(onFinished()));//************************************************
③ 《处理登录认证的槽函数》onAuthenticationRequired(QNetworkReply*,QAuthenticator* authenticator)
//从登录认证子窗口中,获取用户名和密码在进行认证
LoginDialog login(this);//************************************************
//显示登录窗口,并进入事件循环,点击上面Ok/Cancel
//时都会退出登录窗口,但是返回值不同.
//如果点击Ok按钮退出,返回QDialog::Accepted
if(login.exec() == QDialog::Accepted){
authenticator->setUser(login.getUsername());//************************************************
authenticator->setPassword(login.getPassword());//************************************************
}
④ 《接收响应数据的槽函数》onReadyRead()
//读取响应数据,并保存
buf += reply->readAll();//************************************************
//保存当前URl地址
currentUrl = reply->url();//************************************************
⑤ 《接收响应数据完成时执行的槽函数》onFinished()
//显示响应数据
ui->textBrowser->setText(buf);//************************************************
⑥ 《处理目录链接的槽函数》onAnchorClicked(const QUrl &url)
QUrl newUrl;
//如果点击是不是"../",新的URL=当前URL+点击URL
if(url.toString() != "../"){
newUrl = currentUrl.toString() + url.toString();//************************************************
}
//如果点击是"../"
else{
//如果当前在首页,什么也不做
if(currentUrl.toString() =="http://code.tarena.com.cn/")//************************************************
{
return;//************************************************
}
//如果不在首页,去掉最后一级链接路径
//查找目录路径中倒数第二次出现"/"位置
int pos = currentUrl.toString(
).lastIndexOf("/",-2);//************************************************
//字符串截断,去掉后面的路径
newUrl =
currentUrl.toString().mid(0,pos+1);//************************************************
}
//设置新的请求URL
request.setUrl(newUrl);//************************************************
//发送新的请求
sendRequest();//************************************************
//判断点击URL如果不是目录则执行文件下载操作.
if(url.toString().lastIndexOf("/")==-1){//************************************************
downloadFile(newUrl);//************************************************
return;
}
⑦ 《下载文件》downloadFile(const QUrl& fileUrl)
//创建下载文件线程
QThread *thread = new QThread;//************************************************
//创建下载文件对象
Download* download = new Download(fileUrl);//************************************************
//将下载文件对象移动到子线程中
download->moveToThread(thread);//************************************************
//设置下载文件请求的URL
download->request.setUrl(fileUrl);//************************************************1(初始化)
//发送获取下载文件的请求
download->reply = manager->get(download->request);//************************************************2(初始化)
//响应数据到来,在子线程中完成下载操作
//download已经移动到子线程中,通过信号出发里面槽将在子线程中运行
connect(download->reply,SIGNAL(readyRead()),
download,SLOT(ReceiveFile()));//************************************************3
connect(download->reply,SIGNAL(downloadProgress(qint64,qint64)),
download,SLOT(onDownloadProgress(qint64,qint64)));//************************************************3
connect(download->reply,SIGNAL(finished()),
download,SLOT(ReceiveFileFinished()));//************************************************4
//开启子线程
thread->start();//************************************************
注:
Download类实现如下:
头文件:
private:
QNetworkRequest request;//请求//************************************************
QNetworkReply* reply;//响应//************************************************
QUrl fileUrl;//下载文件的URL地址//************************************************
QFile* file;//************************************************
//将MainWindow类声明为当前类的友元,友元类可以访问当前类的任何成员
friend class MainWindow;//************************************************
private slots:
//接收文件的槽函数
void ReceiveFile();//************************************************
//更新显示文件下载进度的槽函数
//参数:已收到数据的字节数/总字节数
void onDownloadProgress(qint64,qint64);//************************************************
//接收文件完成的槽函数
void ReceiveFileFinished();//************************************************
源文件:
① 《构造函数》Download(const QUrl& url):fileUrl(url)
//根据URL获取文件名
QFileInfo fileInfo = fileUrl.path();//************************************************
QString filename = fileInfo.fileName();//************************************************
//指定下载文件放到主目录的Download下面
QString path = QDir::homePath() + "/Downloads/"+ filename;//************************************************
//在本地创建同名的文件
file = new QFile(path);//************************************************
//以写的方式打开文件
file->open(QIODevice::WriteOnly);//************************************************
② 《接收文件的槽函数》ReceiveFile()
file->write(reply->readAll());//************************************************
③ 《已收到数据的字节数/总字节数》onDownloadProgress(qint64 readBytes,qint64 totalBytes)
qint64 progress=readBytes*100/totalBytes;//百分比//************************************************
qDebug() << file->fileName() << ":" << progress << "%....";//************************************************
④ 《接收文件完成的槽函数》ReceiveFileFinished()
qDebug() << file->fileName() << "文件下载完成";//************************************************