分享一个之前用qt实现的tftp客户端,希望能给有需要的同学一些帮助。
完整的项目(可执行程序及源代码)下载地址:完整项目tftp v1.0
源代码文件各自内容如下:
- mythread.cpp:实现tftp的主逻辑。
- widget.cpp:实现操作页面。
运行界面如下,支持put和get功能。
下面列出所有的代码片段和项目配置文件。
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QtNetwork>
#include <QUdpSocket>
#include <QTime>
#define DATA_RECIVE_FLAG 0x80
#define ACK_RECIVE_FLAG 0x82
#define ERR_RECIVE_FLAG 0x84
#define DATA_SEND_FLAG 0x81
#define ACK_SEND_FLAG 0x83
#define RRQ_SEND_FLAG 0x85
#define WRQ_SEND_FLAG 0x87
#define LOBYTE(w) (w & 0xFF)
#define HIBYTE(w) ((w >> 8)&0xFF)
#define MAKEWORD(l,h) ((h << 8)|l)
class QUdpSocket;
enum {
RRQ = 1,
WRQ,
DATA,
ACK,
ERROR
};
struct code{
quint16 Opcode;
QString path;
QString fileName;
quint16 blocks;
};
struct msg{
quint8 flag;
QString messageBuf;
};
class MyThread : public QThread
{
Q_OBJECT
public:
QUdpSocket *tftpclient;
quint8 Eof;
quint8 timeOutCount;
quint8 maxRetryCount;
quint16 port;
quint16 peerPort;
quint16 currentPort;
quint16 errcode;
quint16 cBlocks;
quint64 cLen;
quint64 filesize;
QTimer timer;
QByteArray Datagram;
int timId;
int trMod;
QHostAddress serverIp;
struct code Code;
QString mode;
QFile *file;
struct msg Msg;
MyThread();
void run();
void timerReset();
void timerStop();
void GetErrMsg();
void sendMsg(quint8 flag);
void DoReciveData(char *pct,quint64);
void DoReciveAck(char *pct);
void DoReciveErr(char *pct);
void SendFile(QByteArray *Datagram);
void SendData(quint16 port);
void SendReadReq();
void SendWriteReq();
void SendAck();
void Clear();
QHostAddress getServerIp();
signals:
void timeOut();
void showMsg();
void transOver();
private slots:
void on_timeOut();
void ReciveData();
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QDebug>
#include <QFile>
#include <QDataStream>
MyThread::MyThread()
{
mode = "octet";
port = 69;
cBlocks = 0;
cLen = 0;
Eof=0;
timeOutCount=0;
currentPort=0;
maxRetryCount=10;
Code.blocks =0;
Code.Opcode =0;
tftpclient = new QUdpSocket;
tftpclient->bind(6666/*,QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint*/);
connect(tftpclient,SIGNAL(readyRead()),this,SLOT(ReciveData()));
connect(&timer,SIGNAL(timeout()), this, SLOT(on_timeOut()) );
}
void MyThread::Clear()
{
cBlocks = 0;
cLen = 0;
Eof=0;
Code.blocks =0;
Code.Opcode =0;
}
void MyThread::SendWriteReq()
{
Code.Opcode = WRQ;
file = new QFile(Code.path+"/"+Code.fileName);
if (!file->exists()) {
qDebug()<<"file not exit!";
return;
}
Eof=0;
filesize = file->size();
SendData(port);
}
void MyThread::SendReadReq()
{
Code.Opcode = RRQ;
file = new QFile(Code.path+"/"+Code.fileName);
if (file->exists()) {
file->remove();
file->setFileName(Code.path+"/"+Code.fileName);
qDebug()<<"file exit & rm";
}
SendData(port);
}
void MyThread::SendAck()
{
Code.Opcode = ACK;
if (Code.blocks-1 == cBlocks) {
cBlocks = Code.blocks;
sendMsg(ACK_SEND_FLAG);
SendData(peerPort);
}
}
void MyThread::run()
{
while(1){
qDebug()<< "start thread";
sleep(5);
}
}
void MyThread::SendFile(QByteArray *Datagram)
{
quint8 fun =0;
quint8 hi = HIBYTE(Code.blocks);
quint8 lo = LOBYTE(Code.blocks);
quint8 val;
quint16 i;
Datagram->clear();
Datagram->append(fun);
Datagram->append(DATA);
Datagram->append(hi);
Datagram->append(lo);
file->open(QIODevice::ReadOnly);
file->seek(cLen);
QDataStream in(file);
for(i=0;(i < 512)&&(i<(filesize-cLen));i++){
in >> val;
Datagram->append(val);
}
file->close();
cLen += i;
sendMsg(DATA_SEND_FLAG);
if (i < 512){
Eof=1;//文件传输结束
cLen=0;
Code.blocks=0;
}
}
void MyThread::GetErrMsg()
{
switch (errcode) {
case 1:
Msg.messageBuf.append("文件未找到!");
break;
case 2:
Msg.messageBuf.append("非法操作!");
break;
case 3:
Msg.messageBuf.append("磁盘满!");
break;
case 4:
Msg.messageBuf.append("非法操作码!");
break;
case 5:
Msg.messageBuf.append("未知发送ID!");
break;
case 6:
Msg.messageBuf.append("文件已经存在!");
break;
case 7:
Msg.messageBuf.append("没有当前用户!");
break;
default:
Msg.messageBuf.append("未定义错误!");
break;
}
}
void MyThread::DoReciveErr(char *pct)
{
errcode = MAKEWORD((quint8)pct[3],(quint8)pct[2]);
sendMsg(ERR_RECIVE_FLAG);
}
void MyThread::DoReciveAck(char *pct)
{
quint16 blocks = MAKEWORD((quint8)pct[3],(quint8)pct[2]);
if(Code.blocks == blocks) {
Code.blocks += 1;
}
Code.Opcode=DATA;
sendMsg(ACK_RECIVE_FLAG);
if (Eof) {
emit transOver();
timerStop();
return;
}
SendData(peerPort);
}
void MyThread::sendMsg(quint8 flag)
{
char temval[5]={0};
Msg.flag = flag;
if ((flag & 0x81) == 0x81)
Msg.messageBuf.append("发送:");
else
Msg.messageBuf.append("接收:");
switch(flag) {
case DATA_RECIVE_FLAG:
Msg.messageBuf.append("数据包 ");
sprintf(temval,"%d",Code.blocks);
Msg.messageBuf.append(temval);
break;
case DATA_SEND_FLAG:
Msg.messageBuf.append("数据包 ");
sprintf(temval,"%d",Code.blocks);
Msg.messageBuf.append(temval);
break;
case ACK_SEND_FLAG:
Msg.messageBuf.append("响应 ");
sprintf(temval,"%d",Code.blocks);
Msg.messageBuf.append(temval);
break;
case ACK_RECIVE_FLAG:
Msg.messageBuf.append("响应 ");
sprintf(temval,"%d",Code.blocks);
Msg.messageBuf.append(temval);
break;
case RRQ_SEND_FLAG:
Msg.messageBuf.append("读文件请求");
break;
case WRQ_SEND_FLAG:
Msg.messageBuf.append("写文件请求");
break;
case ERR_RECIVE_FLAG:
GetErrMsg();
break;
default:
return;
}
Msg.messageBuf.append("\n");
emit showMsg();
}
void MyThread::DoReciveData(char *pct,quint64 len)
{
if(file->open(QIODevice::WriteOnly|QIODevice::Append))
qDebug()<< "openfile ok";
else
return;
QDataStream out(file);
Code.blocks = MAKEWORD((quint8)pct[3],(quint8)pct[2]);
pct +=4;
for (quint16 i=0;i<len-4;i++)
out << *(uchar *)pct++;
file->close();
sendMsg(DATA_RECIVE_FLAG);
SendAck();
if (len < 516){
timerStop();
Code.blocks=0;
cBlocks =0;
emit transOver();
delete file;
}
}
void MyThread::ReciveData()
{
while(tftpclient->hasPendingDatagrams())
{
QByteArray datagram;
QHostAddress addr;
datagram.resize(tftpclient->pendingDatagramSize());
tftpclient->readDatagram(datagram.data(), datagram.size(),&addr,&peerPort);
char *pct = (char*)datagram.data();
quint16 opcode = MAKEWORD((quint8)pct[1],(quint8)pct[0]);
quint64 len = datagram.length();
switch(opcode) {
case RRQ:
case WRQ:
break;
case DATA:
timerReset();
DoReciveData(pct,len);
break;
case ACK:
timerReset();
DoReciveAck(pct);
break;
case ERROR:
DoReciveErr(pct);
timerStop();
break;
default:
break;
}
}
}
void MyThread::SendData(quint16 port)
{
quint8 fun =0;
quint8 hi = HIBYTE(Code.blocks);
quint8 lo = LOBYTE(Code.blocks);
currentPort = port;
switch(Code.Opcode) {
case RRQ:
Datagram.clear();
Datagram.append(fun);
Datagram.append(RRQ);
Datagram.append(Code.fileName);
Datagram.append(fun);
Datagram.append(mode);
Datagram.append(fun);
sendMsg(RRQ_SEND_FLAG);
break;
case WRQ:
Datagram.clear();
Datagram.append(fun);
Datagram.append(WRQ);
Datagram.append(Code.fileName);
Datagram.append(fun);
Datagram.append(mode);
Datagram.append(fun);
sendMsg(WRQ_SEND_FLAG);
break;
case DATA:
SendFile(&Datagram);
break;
case ACK:
Datagram.clear();
Datagram.append(fun);
Datagram.append(ACK);
Datagram.append(hi);
Datagram.append(lo);
break;
case ERROR:
Datagram.clear();
Datagram.append(fun);
Datagram.append(ERROR);
break;
default:
break;
}
tftpclient->writeDatagram(Datagram,Datagram.length(),getServerIp(),port);
}
void MyThread::timerReset()
{
timeOutCount = 0;
timer.start(1000);
}
void MyThread::timerStop()
{
timeOutCount=0;
timer.stop();
}
void MyThread::on_timeOut()
{
SendData(currentPort);
timeOutCount ++;
if(timeOutCount > maxRetryCount) {
timerStop();
emit timeOut();
}
}
QHostAddress MyThread::getServerIp()
{
return serverIp;
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QLineEdit>
#include "mythread.h"
namespace Ui {
class Widget;
}
enum {
get_mod=2,
put_mod=3
};
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
quint8 parOkFlag;
quint8 modflag;
MyThread *thread;
private slots:
void DoTimeOut();
void on_showMsg();
void on_transOver();
void on_lineEdit_returnPressed();
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_toolButton_clicked();
void on_comboBox_currentIndexChanged(int index);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMessageBox>
#include <QFileDialog>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
parOkFlag =0;//参数正确标志
modflag =get_mod;//初始化为get模式
thread = new MyThread();
ui->setupUi(this);
connect(thread,SIGNAL(transOver()),this,SLOT(on_transOver()),Qt::QueuedConnection);
connect(thread,SIGNAL(showMsg()),this,SLOT(on_showMsg()));
connect(thread,SIGNAL(timeOut()),this,SLOT(DoTimeOut()));
ui->textBrowser->append("Version 1.0");
ui->textBrowser->append("Author: dhm@zxe-china.com");
}
Widget::~Widget()
{
delete ui;
}
void Widget::DoTimeOut()
{
QMessageBox::warning(this,"错误","传输失败 请求超时!",QMessageBox::Abort);
}
void Widget::on_lineEdit_returnPressed()
{
qDebug() << ui->lineEdit->text().toUInt();
}
void Widget::on_pushButton_clicked()
{
thread->serverIp = ui->lineEdit->text();
thread->trMod = ui->comboBox->currentIndex();
if(modflag==get_mod) {
thread->Code.path = ui->lineEdit_3->text();
thread->Code.fileName = ui->lineEdit_2->text();
} else if (modflag == put_mod) {
QFileInfo info = ui->lineEdit_3->text();
thread->Code.path = info.absolutePath();
thread->Code.fileName = info.fileName();
}
if (thread->serverIp.isNull()) {
QMessageBox::warning(this,"警告","IP地址不能为空!",QMessageBox::Abort);
parOkFlag=0;
}
else if (thread->Code.fileName.isEmpty()) {
QMessageBox::warning(this,"警告","文件名不能为空!",QMessageBox::Abort);
parOkFlag=0;
}
else if (thread->Code.path.isEmpty()) {
if(modflag == get_mod)
QMessageBox::warning(this,"警告","请选择保存位置!",QMessageBox::Abort);
if(modflag == put_mod)
QMessageBox::warning(this,"警告","请选择文件!",QMessageBox::Abort);
parOkFlag=0;
}
else {
parOkFlag =1;
}
qDebug()<<"currentindex"<<ui->comboBox->currentIndex();
qDebug() << thread->serverIp;
qDebug() << thread->Code.fileName;
}
void Widget::on_pushButton_2_clicked()
{
if (parOkFlag) {
thread->Clear();
thread->timer.start(1000);
if (thread->trMod == 0)
thread->SendReadReq();
else
thread->SendWriteReq();
parOkFlag =0;
}
}
void Widget::on_showMsg()
{
ui->textBrowser->append(thread->Msg.messageBuf);
thread->Msg.messageBuf.clear();
thread->Msg.flag=0;
}
void Widget::on_transOver()
{
QMessageBox::information(this,"提示","文件传输完成!",QMessageBox::Ok);
}
void Widget::on_toolButton_clicked()
{
if (modflag==get_mod){
ui->lineEdit_3->setText(QFileDialog::getExistingDirectory(this));
} else if (modflag==put_mod){
ui->lineEdit_3->setText(QFileDialog::getOpenFileName(this));
}
}
void Widget::on_comboBox_currentIndexChanged(int index)
{
if (index == 0) {//get模式
ui->label_4->setText("存储位置:");
ui->lineEdit_2->clear();
ui->lineEdit_3->clear();
ui->lineEdit_2->setEnabled(true);
modflag =get_mod;
}
else if (index == 1) {
ui->label_4->setText("选择文件:");
ui->lineEdit_2->clear();
ui->lineEdit_3->clear();
ui->lineEdit_2->setDisabled(true);
modflag =put_mod;
}
}
main.cpp
#include "widget.h"
#include <QApplication>
#include <QTextCodec>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
项目文件tftpclient.pro
#-------------------------------------------------
#
# Project created by QtCreator 2017-04-08T19:47:47
#
#-------------------------------------------------
QT += core gui
QT += network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = tftpclient
TEMPLATE = app
SOURCES += main.cpp\
widget.cpp \
mythread.cpp
HEADERS += widget.h \
mythread.h
FORMS += widget.ui
RC_FILE += myico.rc
ui_widget.h
/********************************************************************************
** Form generated from reading UI file 'widget.ui'
**
** Created by: Qt User Interface Compiler version 5.8.0
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_WIDGET_H
#define UI_WIDGET_H
#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QTextBrowser>
#include <QtWidgets/QToolButton>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_Widget
{
public:
QLineEdit *lineEdit;
QLabel *label;
QLineEdit *lineEdit_2;
QLabel *label_2;
QPushButton *pushButton;
QPushButton *pushButton_2;
QComboBox *comboBox;
QLabel *label_3;
QTextBrowser *textBrowser;
QToolButton *toolButton;
QLabel *label_4;
QLineEdit *lineEdit_3;
void setupUi(QWidget *Widget)
{
if (Widget->objectName().isEmpty())
Widget->setObjectName(QStringLiteral("Widget"));
Widget->resize(447, 380);
lineEdit = new QLineEdit(Widget);
lineEdit->setObjectName(QStringLiteral("lineEdit"));
lineEdit->setGeometry(QRect(100, 38, 101, 20));
lineEdit->setContextMenuPolicy(Qt::ActionsContextMenu);
lineEdit->setAutoFillBackground(false);
lineEdit->setInputMethodHints(Qt::ImhNone);
lineEdit->setCursorPosition(0);
lineEdit->setCursorMoveStyle(Qt::LogicalMoveStyle);
label = new QLabel(Widget);
label->setObjectName(QStringLiteral("label"));
label->setGeometry(QRect(41, 38, 48, 16));
label->setTextFormat(Qt::AutoText);
lineEdit_2 = new QLineEdit(Widget);
lineEdit_2->setObjectName(QStringLiteral("lineEdit_2"));
lineEdit_2->setGeometry(QRect(100, 70, 101, 20));
label_2 = new QLabel(Widget);
label_2->setObjectName(QStringLiteral("label_2"));
label_2->setGeometry(QRect(41, 70, 48, 16));
pushButton = new QPushButton(Widget);
pushButton->setObjectName(QStringLiteral("pushButton"));
pushButton->setGeometry(QRect(280, 50, 91, 23));
pushButton_2 = new QPushButton(Widget);
pushButton_2->setObjectName(QStringLiteral("pushButton_2"));
pushButton_2->setGeometry(QRect(280, 90, 91, 23));
comboBox = new QComboBox(Widget);
comboBox->setObjectName(QStringLiteral("comboBox"));
comboBox->setGeometry(QRect(100, 100, 44, 20));
label_3 = new QLabel(Widget);
label_3->setObjectName(QStringLiteral("label_3"));
label_3->setGeometry(QRect(41, 100, 36, 16));
textBrowser = new QTextBrowser(Widget);
textBrowser->setObjectName(QStringLiteral("textBrowser"));
textBrowser->setGeometry(QRect(41, 189, 371, 181));
toolButton = new QToolButton(Widget);
toolButton->setObjectName(QStringLiteral("toolButton"));
toolButton->setGeometry(QRect(210, 130, 21, 18));
label_4 = new QLabel(Widget);
label_4->setObjectName(QStringLiteral("label_4"));
label_4->setGeometry(QRect(40, 130, 54, 12));
lineEdit_3 = new QLineEdit(Widget);
lineEdit_3->setObjectName(QStringLiteral("lineEdit_3"));
lineEdit_3->setGeometry(QRect(100, 130, 101, 20));
#ifndef QT_NO_SHORTCUT
label->setBuddy(lineEdit);
label_2->setBuddy(lineEdit_2);
label_3->setBuddy(comboBox);
label_4->setBuddy(lineEdit_3);
#endif // QT_NO_SHORTCUT
retranslateUi(Widget);
QMetaObject::connectSlotsByName(Widget);
} // setupUi
void retranslateUi(QWidget *Widget)
{
Widget->setWindowTitle(QApplication::translate("Widget", "TftpTools", Q_NULLPTR));
lineEdit->setInputMask(QApplication::translate("Widget", "000.000.000.000", Q_NULLPTR));
lineEdit->setPlaceholderText(QString());
label->setText(QApplication::translate("Widget", "IP\345\234\260\345\235\200\357\274\232", Q_NULLPTR));
label_2->setText(QApplication::translate("Widget", "\346\226\207\344\273\266\345\220\215\357\274\232", Q_NULLPTR));
pushButton->setText(QApplication::translate("Widget", "\347\241\256\345\256\232", Q_NULLPTR));
pushButton_2->setText(QApplication::translate("Widget", "\345\220\257\345\212\250", Q_NULLPTR));
comboBox->clear();
comboBox->insertItems(0, QStringList()
<< QApplication::translate("Widget", "Get", Q_NULLPTR)
<< QApplication::translate("Widget", "Put", Q_NULLPTR)
);
label_3->setText(QApplication::translate("Widget", "\346\250\241\345\274\217\357\274\232", Q_NULLPTR));
toolButton->setText(QApplication::translate("Widget", "...", Q_NULLPTR));
label_4->setText(QApplication::translate("Widget", "\345\255\230\345\202\250\344\275\215\347\275\256\357\274\232", Q_NULLPTR));
} // retranslateUi
};
namespace Ui {
class Widget: public Ui_Widget {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_WIDGET_H
widget.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Widget</class>
<widget class="QWidget" name="Widget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>447</width>
<height>380</height>
</rect>
</property>
<property name="windowTitle">
<string>TftpTools</string>
</property>
<widget class="QLineEdit" name="lineEdit">
<property name="geometry">
<rect>
<x>100</x>
<y>38</y>
<width>101</width>
<height>20</height>
</rect>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="inputMethodHints">
<set>Qt::ImhNone</set>
</property>
<property name="inputMask">
<string>000.000.000.000</string>
</property>
<property name="cursorPosition">
<number>0</number>
</property>
<property name="placeholderText">
<string extracomment="IP地址"/>
</property>
<property name="cursorMoveStyle">
<enum>Qt::LogicalMoveStyle</enum>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>41</x>
<y>38</y>
<width>48</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>IP地址:</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
<property name="buddy">
<cstring>lineEdit</cstring>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit_2">
<property name="geometry">
<rect>
<x>100</x>
<y>70</y>
<width>101</width>
<height>20</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>41</x>
<y>70</y>
<width>48</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>文件名:</string>
</property>
<property name="buddy">
<cstring>lineEdit_2</cstring>
</property>
</widget>
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>280</x>
<y>50</y>
<width>91</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>确定</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton_2">
<property name="geometry">
<rect>
<x>280</x>
<y>90</y>
<width>91</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>启动</string>
</property>
</widget>
<widget class="QComboBox" name="comboBox">
<property name="geometry">
<rect>
<x>100</x>
<y>100</y>
<width>44</width>
<height>20</height>
</rect>
</property>
<item>
<property name="text">
<string>Get</string>
</property>
</item>
<item>
<property name="text">
<string>Put</string>
</property>
</item>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>41</x>
<y>100</y>
<width>36</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>模式:</string>
</property>
<property name="buddy">
<cstring>comboBox</cstring>
</property>
</widget>
<widget class="QTextBrowser" name="textBrowser">
<property name="geometry">
<rect>
<x>41</x>
<y>189</y>
<width>371</width>
<height>181</height>
</rect>
</property>
</widget>
<widget class="QToolButton" name="toolButton">
<property name="geometry">
<rect>
<x>210</x>
<y>130</y>
<width>21</width>
<height>18</height>
</rect>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>40</x>
<y>130</y>
<width>54</width>
<height>12</height>
</rect>
</property>
<property name="text">
<string>存储位置:</string>
</property>
<property name="buddy">
<cstring>lineEdit_3</cstring>
</property>
</widget>
<widget class="QLineEdit" name="lineEdit_3">
<property name="geometry">
<rect>
<x>100</x>
<y>130</y>
<width>101</width>
<height>20</height>
</rect>
</property>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>