本文章原創於 www.yafeilinux.com 轉載請註明出處。
前面講述了一個最簡單的FTP用戶端程式的編寫,這一節我們將這個程式進行擴充,使其可以瀏覽並能下載伺服器上的所有檔案。
1.更改widget.ui檔案如下。
我們刪除了Text Browser ,加入了幾個Label ,Line Edit ,Push Button組件,一個Tree Widget及一個Progress Bar組件。然後我們對其中幾個組件做如下更改。
(1)將“FTP伺服器”標籤後的Line Edit的objectName屬性改為“ftpServerLineEdit”,其text 屬性改為“ftp.qt.nokia.com”。
(2)將“使用者名稱”標籤後的Line Edit的objectName屬性改為“userNameLineEdit”,其text屬性改為“anonymous”,將其toolTip屬性改為“預設使用者名請使用:anonymous ,此時密碼任意。”
(3)將“密碼”標籤後的Line Edit的objectName屬性改為“passWordLineEdit”,其text屬性改為“123456”,將其echoMode屬性改為“Password”。
(4)將“串連”按鈕的objectName屬性改為“connectButton”。
(5)將“返回上一級目錄”按鈕的objectName屬性改為“cdToParentButton”。
(6)將“下載”按鈕的objectName屬性改為“downloadButton”。
(7)將Tree Widget的objectName屬性改為“fileList”,然後在Tree Widget組件上單擊滑鼠右鍵,選擇Edit Items菜單,添加列屬性如下。
最終的介面如下。
下面我們的程式中,就是實現在使用者填寫完相關資訊後,按下“串連”按鈕,就可以串連到FTP伺服器,並在Tree Widget中顯示伺服器上的所有檔案,我們可以按下“下載”按鈕來下載選中的檔案,並使用進度條顯示下載進度。
2.更改widget.h檔案。
(1)添加標頭檔#include <QtGui>
(2)在private中添加變數:
QHash<QString, bool> isDirectory;
//用來儲存一個路徑是否為目錄的資訊
QString currentPath;
//用來儲存現在的路徑
(3)添加槽函數:
private slots:
void on_downloadButton_clicked();
void on_cdToParentButton_clicked();
void on_connectButton_clicked();
void ftpCommandFinished(int,bool);
void ftpCommandStarted(int);
void updateDataTransferProgress(qint64,qint64 );
//更新進度條
void addToList(const QUrlInfo &urlInfo);
//將伺服器上的檔案添加到Tree Widget中
void processItem(QTreeWidgetItem*,int);
//雙擊一個目錄時顯示其內容
3.更改widget.cpp的內容。
(1)實現“串連”按鈕的單擊事件槽函數。
void Widget::on_connectButton_clicked() //連線按鍵
{
ui->fileList->clear();
currentPath.clear();
isDirectory.clear();
ftp = new QFtp(this);
connect(ftp,SIGNAL(commandStarted(int)),this,SLOT(ftpCommandStarted(int)));
connect(ftp,SIGNAL(commandFinished(int,bool)),
this,SLOT(ftpCommandFinished(int,bool)));
connect(ftp,SIGNAL(listInfo(QUrlInfo)),this,SLOT(addToList(QUrlInfo)));
connect(ftp,SIGNAL(dataTransferProgress(qint64,qint64)),
this,SLOT(updateDataTransferProgress(qint64,qint64)));
QString ftpServer = ui->ftpServerLineEdit->text();
QString userName = ui->userNameLineEdit->text();
QString passWord = ui->passWordLineEdit->text();
ftp->connectToHost(ftpServer,21); //串連到伺服器,預設連接埠號碼是21
ftp->login(userName,passWord); //登入
}
我們在“串連”按鈕的單擊事件槽函數中建立了ftp對象,然後關聯了相關的訊號和槽。這裡的listInfo()訊號由ftp->list()函數發射,它將在登入命令完成時調用,下面我們提到。而dataTransferProgress()訊號在資料轉送時自動發射。最後我們從介面上獲得伺服器位址,使用者名稱和密碼等資訊,並以它們為參數執行串連和登入命令。
(2)更改ftpCommandFinished()函數。
我們在相應位置做更改。
首先,在登入命令完成時,我們調用list()函數:
ui->label->setText(tr(“登入成功”));
ftp->list(); //發射listInfo()訊號,顯示檔案清單
然後,在下載命令完成時,我們使下載按鈕可用:
ui->label->setText(tr(“已經完成下載”));
ui->downloadButton->setEnabled(true);
最後再添加一個if語句,處理list命令完成時的情況:
if (ftp->currentCommand() == QFtp::List){
if (isDirectory.isEmpty())
{ //如果目錄為空白,顯示“empty”
ui->fileList->addTopLevelItem(
new QTreeWidgetItem(QStringList()<< tr(“<empty>”)));
ui->fileList->setEnabled(false);
ui->label->setText(tr(“該目錄為空白”));
}
}
我們在list命令完成時,判斷檔案清單是否為空白,如果為空白,就讓Tree Widget不可用,並顯示“empty”條目。
(3)添加檔案清單函數的內容如下。
void Widget::addToList(const QUrlInfo &urlInfo) //添加檔案清單
{
QTreeWidgetItem *item = new QTreeWidgetItem;
item->setText(0, urlInfo.name());
item->setText(1, QString::number(urlInfo.size()));
item->setText(2, urlInfo.owner());
item->setText(3, urlInfo.group());
item->setText(4, urlInfo.lastModified().toString(“MMM dd yyyy”));
QPixmap pixmap(urlInfo.isDir() ? “../dir.png” : “../file.png”);
item->setIcon(0, pixmap);
isDirectory[urlInfo.name()] = urlInfo.isDir();
//儲存該路徑是否為目錄的資訊
ui->fileList->addTopLevelItem(item);
if (!ui->fileList->currentItem()) {
ui->fileList->setCurrentItem(ui->fileList->topLevelItem(0));
ui->fileList->setEnabled(true);
}
}
當ftp->list()函數執行時會發射listInfo()訊號,此時就會執行addToList()函數,在這裡我們將檔案資訊顯示在Tree Widget上,並在isDirectory中儲存該檔案的路徑及其是否為目錄的資訊。為了使檔案與目錄進行區分,我們使用了不同的表徵圖file.png 和dir.png來表示它們,這兩個表徵圖放在了工程檔案夾中。
(4)將建構函式的內容更改如下。
{
ui->setupUi(this);
ui->progressBar->setValue(0);
connect(ui->fileList,SIGNAL(itemActivated(QTreeWidgetItem*,int)),
this,SLOT(processItem(QTreeWidgetItem*,int)));
//滑鼠雙擊列表中的目錄時,我們進入該目錄
}
這裡我們只是讓進度條的值為0,然後關聯了Tree Widget的一個訊號itemActivated()。當滑鼠雙擊一個條目時,發射該訊號,我們在槽函數中判斷該條目是否為目錄,如果是則進入該目錄。
(5)processItem()函數的實現如下。
void Widget::processItem(QTreeWidgetItem* item,int) //開啟一個目錄
{
QString name = item->text(0);
if (isDirectory.value(name)) { //如果這個檔案是個目錄,則開啟
ui->fileList->clear();
isDirectory.clear();
currentPath += ‘/’;
currentPath += name;
ftp->cd(name);
ftp->list();
ui->cdToParentButton->setEnabled(true);
}
}
(6)“返回上一級目錄”按鈕的單擊事件槽函數如下。
void Widget::on_cdToParentButton_clicked() //返回上級目錄按鈕
{
ui->fileList->clear();
isDirectory.clear();
currentPath = currentPath.left(currentPath.lastIndexOf(‘/’));
if (currentPath.isEmpty()) {
ui->cdToParentButton->setEnabled(false);
ftp->cd(“/”);
} else {
ftp->cd(currentPath);
}
ftp->list();
}
在返回上一級目錄時,我們取當前路徑的最後一個“/”之前的部分,如果此時路徑為空白了,我們就讓“返回上一級目錄”按鈕不可用。
(7)“下載”按鈕單擊事件槽函數如下。
void Widget::on_downloadButton_clicked() //下載按鈕
{
QString fileName = ui->fileList->currentItem()->text(0);
QFile *file = new QFile(fileName);
if (!file->open(QIODevice::WriteOnly))
{
delete file;
return;
}
ui->downloadButton->setEnabled(false); //下載按鈕不可用,等下載完成後才可用
ftp->get(ui->fileList->currentItem()->text(0), file);
}
在這裡我們擷取了當前項目的檔案名稱,然後建立檔案,使用get()命令下載伺服器上的檔案到我們建立的檔案中。
(8)更新進度條函數內容如下。
void Widget::updateDataTransferProgress( //進度條
qint64 readBytes,qint64 totalBytes)
{
ui->progressBar->setMaximum(totalBytes);
ui->progressBar->setValue(readBytes);
}
4.運行程式,效果如下。
開始介面如下。
登入成功時介面如下。
下載檔案時介面如下。
當一個目錄為空白時介面如下。
4.流程說明。
整個程式的流程就和我們實現函數的順序一樣。使用者在介面上輸入伺服器的相關資訊 ,然後我們利用這些資訊進行串連並登入伺服器,等登入伺服器成功時,我們列出伺服器上所有的檔案。對於一個目錄,我們可以進入其中,並返回上一級目錄,我們可以下載檔案,並顯示下載的進度。
對於ftp的操作,全部由那些命令和訊號來完成,我們只需要調用相應的命令,並在其發出訊號時,進行對應的處理就可以了。而對於檔案的顯示,則是視圖部分的知識了。
5.其他說明。
最後需要說明的是,因為為了更好的講解知識,使得程式簡單化,所以我們省去了很多細節上的處理,如果需要,你可以自己添加。比如中斷連線和取消下載,你都可以使用
ftp->abort()函數。你也可以參考Qt內建的Ftp Example例子。對於其他動作,比如上傳等,你可以根據需要添加。
FTP的相關編程就講到到這裡。