Qt学习之路_5(Qt TCP的初步使用)

原文地址为: Qt学习之路_5(Qt TCP的初步使用)

       在上一篇博文Qt学习之路_4(Qt UDP的初步使用) 中,初步了解了Qt下UDP的使用,这一节就学习下TCP的使用。2者其实流程都差不多。当然了,本文还是参考的《Qt及Qt Quick开发实战精解》一书中的第5个例子,即局域网聊天工具中的UDP聊天和TCP文件传送部分。另外http://www.yafeilinux.com/ 上有其源码和相关教程下载。

         其发送端界面如下:

          

         接收端界面如下:

  

 

         发送端,也即承担服务器角色的操作:

         在主界面程序右侧选择一个需要发送文件的用户,弹出发送端界面后,点击打开按钮,在本地计算机中选择需要发送的文件,点击发送按钮,则进度条上会显示当前文件传送的信息,有已传送文件大小信息,传送速度等信息。如果想关闭发送过程,则单击关闭按钮。

         其流程图如下:

  

 

         接收端,也即承担客户端角色的操作:

         当在主界面中突然弹出一个对话框,问是否接自某个用户名和IP地址的文件传送信息,如果接受则单击yes按钮,否则就单击no按钮。当接收文件时,选择好接收文件所存目录和文件名后就开始接收文件了,其过程也会显示已接收文件的大小,接收速度和剩余时间的大小等信息。

         其流程图如下:

  

 

         TCP部分程序代码和注释如下:

 Widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include
<QWidget>

class QUdpSocket;

class TcpServer;//可以这样定义类?不用保护头文件的?

namespace Ui {
class Widget;
}

// 枚举变量标志信息的类型,分别为消息,新用户加入,用户退出,文件名,拒绝接受文件
enum MessageType{Message, NewParticipant, ParticipantLeft, FileName, Refuse};


class Widget : public QWidget
{
Q_OBJECT

public:
explicit Widget(QWidget *parent = 0);
~Widget();

protected:
void newParticipant(QString userName,
QString localHostName, QString ipAddress);
void participantLeft(QString userName,
QString localHostName, QString time);
void sendMessage(MessageType type, QString serverAddress="");

QString getIP();
QString getUserName();
QString getMessage();


void hasPendingFile(QString userName, QString serverAddress,
QString clientAddress, QString fileName);

private:
Ui::Widget
*ui;
QUdpSocket
*udpSocket;
qint16 port;

QString fileName;
TcpServer
*server;

private slots:
void processPendingDatagrams();

void on_sendButton_clicked();

void getFileName(QString);
void on_sendToolBtn_clicked();
};

#endif // WIDGET_H

 

Widget.cpp:

#include "widget.h"
#include
"ui_widget.h"
#include
<QUdpSocket>
#include
<QHostInfo>
#include
<QMessageBox>
#include
<QScrollBar>
#include
<QDateTime>
#include
<QNetworkInterface>
#include
<QProcess>

#include
"tcpserver.h"
#include
"tcpclient.h"
#include
<QFileDialog>

Widget::Widget(QWidget
*parent) :
QWidget(parent),
ui(
new Ui::Widget)
{
ui
->setupUi(this);

udpSocket
= new QUdpSocket(this);
port
= 45454;
udpSocket
->bind(port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
connect(udpSocket, SIGNAL(readyRead()),
this, SLOT(processPendingDatagrams()));
sendMessage(NewParticipant);

//TcpServer是tcpserver.ui对应的类,上面直接用QUdpSocket是因为没有单独的udpserver.ui类
server = new TcpServer(this);
//sendFileName()函数一发送,则触发槽函数getFileName()
connect(server, SIGNAL(sendFileName(QString)), this, SLOT(getFileName(QString)));
}

Widget::
~Widget()
{
delete ui;
}

// 使用UDP广播发送信息
void Widget::sendMessage(MessageType type, QString serverAddress)
{
QByteArray data;
QDataStream
out(&data, QIODevice::WriteOnly);
QString localHostName
= QHostInfo::localHostName();
QString address
= getIP();
out << type << getUserName() << localHostName;

switch(type)
{
case Message :
if (ui->messageTextEdit->toPlainText() == "") {
QMessageBox::warning(
0,tr("警告"),tr("发送内容不能为空"),QMessageBox::Ok);
return;
}
out << address << getMessage();
ui
->messageBrowser->verticalScrollBar()
->setValue(ui->messageBrowser->verticalScrollBar()->maximum());
break;

case NewParticipant :
out << address;
break;

case ParticipantLeft :
break;

case FileName : {
int row = ui->userTableWidget->currentRow();//必须选中需要发送的给谁才可以发送
QString clientAddress = ui->userTableWidget->item(row, 2)->text();//(row,,2)为ip地址
out << address << clientAddress << fileName;//发送本地ip,对方ip,所发送的文件名
break;
}

case Refuse :
out << serverAddress;
break;
}
udpSocket
->writeDatagram(data,data.length(),QHostAddress::Broadcast, port);
}

// 接收UDP信息
void Widget::processPendingDatagrams()
{
while(udpSocket->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(udpSocket
->pendingDatagramSize());
udpSocket
->readDatagram(datagram.data(), datagram.size());
QDataStream
in(&datagram, QIODevice::ReadOnly);
int messageType;
in >> messageType;
QString userName,localHostName,ipAddress,message;
QString time
= QDateTime::currentDateTime()
.toString(
"yyyy-MM-dd hh:mm:ss");

switch(messageType)
{
case Message:
in >> userName >> localHostName >> ipAddress >> message;
ui
->messageBrowser->setTextColor(Qt::blue);
ui
->messageBrowser->setCurrentFont(QFont("Times New Roman",12));
ui
->messageBrowser->append("[ " +userName+" ] "+ time);
ui
->messageBrowser->append(message);
break;

case NewParticipant:
in >>userName >>localHostName >>ipAddress;
newParticipant(userName,localHostName,ipAddress);
break;

case ParticipantLeft:
in >>userName >>localHostName;
participantLeft(userName,localHostName,time);
break;

case FileName: {
in >> userName >> localHostName >> ipAddress;
QString clientAddress, fileName;
in >> clientAddress >> fileName;
hasPendingFile(userName, ipAddress, clientAddress, fileName);
break;
}

case Refuse: {
in >> userName >> localHostName;
QString serverAddress;
in >> serverAddress;
QString ipAddress
= getIP();

if(ipAddress == serverAddress)
{
server
->refused();
}
break;
}
}
}
}

// 处理新用户加入
void Widget::newParticipant(QString userName, QString localHostName, QString ipAddress)
{
bool isEmpty = ui->userTableWidget->findItems(localHostName, Qt::MatchExactly).isEmpty();
if (isEmpty) {
QTableWidgetItem
*user = new QTableWidgetItem(userName);
QTableWidgetItem
*host = new QTableWidgetItem(localHostName);
QTableWidgetItem
*ip = new QTableWidgetItem(ipAddress);

ui
->userTableWidget->insertRow(0);
ui
->userTableWidget->setItem(0,0,user);
ui
->userTableWidget->setItem(0,1,host);
ui
->userTableWidget->setItem(0,2,ip);
ui
->messageBrowser->setTextColor(Qt::gray);
ui
->messageBrowser->setCurrentFont(QFont("Times New Roman",10));
ui
->messageBrowser->append(tr("%1 在线!").arg(userName));
ui
->userNumLabel->setText(tr("在线人数:%1").arg(ui->userTableWidget->rowCount()));

sendMessage(NewParticipant);
}
}

// 处理用户离开
void Widget::participantLeft(QString userName, QString localHostName, QString time)
{
int rowNum = ui->userTableWidget->findItems(localHostName, Qt::MatchExactly).first()->row();
ui
->userTableWidget->removeRow(rowNum);
ui
->messageBrowser->setTextColor(Qt::gray);
ui
->messageBrowser->setCurrentFont(QFont("Times New Roman", 10));
ui
->messageBrowser->append(tr("%1 于 %2 离开!").arg(userName).arg(time));
ui
->userNumLabel->setText(tr("在线人数:%1").arg(ui->userTableWidget->rowCount()));
}

// 获取ip地址
QString Widget::getIP()
{
QList
<QHostAddress> list = QNetworkInterface::allAddresses();
foreach (QHostAddress address, list) {
if(address.protocol() == QAbstractSocket::IPv4Protocol)
return address.toString();
}
return 0;
}

// 获取用户名
QString Widget::getUserName()
{
QStringList envVariables;
envVariables
<< "USERNAME.*" << "USER.*" << "USERDOMAIN.*"
<< "HOSTNAME.*" << "DOMAINNAME.*";
QStringList environment
= QProcess::systemEnvironment();
foreach (QString string, envVariables) {
int index = environment.indexOf(QRegExp(string));
if (index != -1) {
QStringList stringList
= environment.at(index).split('=');
if (stringList.size() == 2) {
return stringList.at(1);
break;
}
}
}
return "unknown";
}

// 获得要发送的消息
QString Widget::getMessage()
{
QString msg
= ui->messageTextEdit->toHtml();

ui
->messageTextEdit->clear();
ui
->messageTextEdit->setFocus();
return msg;
}


// 发送消息
void Widget::on_sendButton_clicked()
{
sendMessage(Message);
}




// 获取要发送的文件名
void Widget::getFileName(QString name)
{
fileName
= name;
sendMessage(FileName);
}

// 传输文件按钮
void Widget::on_sendToolBtn_clicked()
{
if(ui->userTableWidget->selectedItems().isEmpty())//传送文件前需选择用户
{
QMessageBox::warning(
0, tr("选择用户"),
tr(
"请先从用户列表选择要传送的用户!"), QMessageBox::Ok);
return;
}
server
->show();
server
->initServer();
}

// 是否接收文件,客户端的显示
void Widget::hasPendingFile(QString userName, QString serverAddress,
QString clientAddress, QString fileName)
{
QString ipAddress
= getIP();
if(ipAddress == clientAddress)
{
int btn = QMessageBox::information(this,tr("接受文件"),
tr(
"来自%1(%2)的文件:%3,是否接收?")
.arg(userName).arg(serverAddress).arg(fileName),
QMessageBox::Yes,QMessageBox::No);
//弹出一个窗口
if (btn == QMessageBox::Yes) {
QString name
= QFileDialog::getSaveFileName(0,tr("保存文件"),fileName);//name为另存为的文件名
if(!name.isEmpty())
{
TcpClient
*client = new TcpClient(this);
client
->setFileName(name); //客户端设置文件名
client->setHostAddress(QHostAddress(serverAddress)); //客户端设置服务器地址
client->show();
}
}
else {
sendMessage(Refuse, serverAddress);
}
}
}

 

Tcpserver.h:

#ifndef TCPSERVER_H
#define TCPSERVER_H

#include
<QDialog>

#include
<QTime>
class QFile;
class QTcpServer;
class QTcpSocket;

namespace Ui {
class TcpServer;
}

class TcpServer : public QDialog
{
Q_OBJECT

public:
explicit TcpServer(QWidget *parent = 0);
~TcpServer();

void initServer();
void refused();

protected:
void closeEvent(QCloseEvent *);

private:
Ui::TcpServer
*ui;

qint16 tcpPort;
QTcpServer
*tcpServer;
QString fileName;
QString theFileName;
QFile
*localFile;

qint64 TotalBytes;
qint64 bytesWritten;
qint64 bytesToWrite;
qint64 payloadSize;
QByteArray outBlock;

QTcpSocket
*clientConnection;

QTime time;

private slots:
void sendMessage();
void updateClientProgress(qint64 numBytes);

void on_serverOpenBtn_clicked();

void on_serverSendBtn_clicked();

void on_serverCloseBtn_clicked();

signals:
void sendFileName(QString fileName);
};

#endif // TCPSERVER_H

 

Tcpserver.cpp:

#include "tcpserver.h"
#include
"ui_tcpserver.h"

#include
<QFile>
#include
<QTcpServer>
#include
<QTcpSocket>
#include
<QMessageBox>
#include
<QFileDialog>
#include
<QDebug>

TcpServer::TcpServer(QWidget
*parent) :
QDialog(parent),
ui(
new Ui::TcpServer)
{
ui
->setupUi(this); //每一个新类都有一个自己的ui

setFixedSize(
350,180); //初始化时窗口显示固定大小

tcpPort
= 6666; //tcp通信端口
tcpServer = new QTcpServer(this);
//newConnection表示当tcp有新连接时就发送信号
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(sendMessage()));

initServer();
}

TcpServer::
~TcpServer()
{
delete ui;
}

// 初始化
void TcpServer::initServer()
{
payloadSize
= 64*1024;
TotalBytes
= 0;
bytesWritten
= 0;
bytesToWrite
= 0;

ui
->serverStatusLabel->setText(tr("请选择要传送的文件"));
ui
->progressBar->reset();//进度条复位
ui->serverOpenBtn->setEnabled(true);//open按钮可用
ui->serverSendBtn->setEnabled(false);//发送按钮不可用

tcpServer
->close();//tcp传送文件窗口不显示
}

// 开始发送数据
void TcpServer::sendMessage() //是connect中的槽函数
{
ui
->serverSendBtn->setEnabled(false); //当在传送文件的过程中,发送按钮不可用
clientConnection = tcpServer->nextPendingConnection(); //用来获取一个已连接的TcpSocket
//bytesWritten为qint64类型,即长整型
connect(clientConnection, SIGNAL(bytesWritten(qint64)), //?
this, SLOT(updateClientProgress(qint64)));

ui
->serverStatusLabel->setText(tr("开始传送文件 %1 !").arg(theFileName));

localFile
= new QFile(fileName); //localFile代表的是文件内容本身
if(!localFile->open((QFile::ReadOnly))){
QMessageBox::warning(
this, tr("应用程序"), tr("无法读取文件 %1:\n%2")
.arg(fileName).arg(localFile
->errorString()));//errorString是系统自带的信息
return;
}
TotalBytes
= localFile->size();//文件总大小
//头文件中的定义QByteArray outBlock;
QDataStream sendOut(&outBlock, QIODevice::WriteOnly);//设置输出流属性
sendOut.setVersion(QDataStream::Qt_4_7);//设置Qt版本,不同版本的数据流格式不同
time.start(); // 开始计时
QString currentFile = fileName.right(fileName.size() //currentFile代表所选文件的文件名
- fileName.lastIndexOf('/')-1);
//qint64(0)表示将0转换成qint64类型,与(qint64)0等价
//如果是,则此处为依次写入总大小信息空间,文件名大小信息空间,文件名
sendOut << qint64(0) << qint64(0) << currentFile;
TotalBytes
+= outBlock.size();//文件名大小等信息+实际文件大小
//sendOut.device()为返回io设备的当前设置,seek(0)表示设置当前pos为0
sendOut.device()->seek(0);//返回到outBlock的开始,执行覆盖操作
//发送总大小空间和文件名大小空间
sendOut << TotalBytes << qint64((outBlock.size() - sizeof(qint64)*2));
//qint64 bytesWritten;bytesToWrite表示还剩下的没发送完的数据
//clientConnection->write(outBlock)为套接字将内容发送出去,返回实际发送出去的字节数
bytesToWrite = TotalBytes - clientConnection->write(outBlock);
outBlock.resize(
0);//why??
}

// 更新进度条,有数据发送时触发
void TcpServer::updateClientProgress(qint64 numBytes)
{
//qApp为指向一个应用对象的全局指针
qApp->processEvents();//processEvents为处理所有的事件?
bytesWritten += (int)numBytes;
if (bytesToWrite > 0) { //没发送完毕
//初始化时payloadSize = 64*1024;qMin为返回参数中较小的值,每次最多发送64K的大小
outBlock = localFile->read(qMin(bytesToWrite, payloadSize));
bytesToWrite
-= (int)clientConnection->write(outBlock);
outBlock.resize(
0);//清空发送缓冲区
} else {
localFile
->close();
}
ui
->progressBar->setMaximum(TotalBytes);//进度条的最大值为所发送信息的所有长度(包括附加信息)
ui->progressBar->setValue(bytesWritten);//进度条显示的进度长度为bytesWritten实时的长度

float useTime = time.elapsed();//从time.start()还是到当前所用的时间记录在useTime中
double speed = bytesWritten / useTime;
ui
->serverStatusLabel->setText(tr("已发送 %1MB (%2MB/s) "
"\n共%3MB 已用时:%4秒\n估计剩余时间:%5秒")
.arg(bytesWritten
/ (1024*1024)) //转化成MB
.arg(speed*1000 / (1024*1024), 0, 'f', 2)
.arg(TotalBytes
/ (1024 * 1024))
.arg(useTime
/1000, 0, 'f', 0) //0,‘f’,0是什么意思啊?
.arg(TotalBytes/speed/1000 - useTime/1000, 0, 'f', 0));

if(bytesWritten == TotalBytes) { //当需发送文件的总长度等于已发送长度时,表示发送完毕!
localFile->close();
tcpServer
->close();
ui
->serverStatusLabel->setText(tr("传送文件 %1 成功").arg(theFileName));
}
}

// 打开按钮
void TcpServer::on_serverOpenBtn_clicked()
{
//QString fileName;QFileDialog是一个提供给用户选择文件或目录的对话框
fileName = QFileDialog::getOpenFileName(this); //filename为所选择的文件名(包含了路径名)
if(!fileName.isEmpty())
{
//fileName.right为返回filename最右边参数大小个字文件名,theFileName为所选真正的文件名
theFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/')-1);
ui
->serverStatusLabel->setText(tr("要传送的文件为:%1 ").arg(theFileName));
ui
->serverSendBtn->setEnabled(true);//发送按钮可用
ui->serverOpenBtn->setEnabled(false);//open按钮禁用
}
}

// 发送按钮
void TcpServer::on_serverSendBtn_clicked()
{
//tcpServer->listen函数如果监听到有连接,则返回1,否则返回0
if(!tcpServer->listen(QHostAddress::Any,tcpPort))//开始监听6666端口
{
qDebug()
<< tcpServer->errorString();//此处的errorString是指?
close();
return;
}

ui
->serverStatusLabel->setText(tr("等待对方接收... ..."));
emit sendFileName(theFileName);
//发送已传送文件的信号,在widget.cpp构造函数中的connect()触发槽函数
}

// 关闭按钮,服务器端的关闭按钮
void TcpServer::on_serverCloseBtn_clicked()
{
if(tcpServer->isListening())
{
//当tcp正在监听时,关闭tcp服务器端应用,即按下close键时就不监听tcp请求了
tcpServer->close();
if (localFile->isOpen())//如果所选择的文件已经打开,则关闭掉
localFile->close();
clientConnection
->abort();//clientConnection为下一个连接?怎么理解
}
close();
//关闭本ui,即本对话框
}

// 被对方拒绝
void TcpServer::refused()
{
tcpServer
->close();
ui
->serverStatusLabel->setText(tr("对方拒绝接收!!!"));
}

// 关闭事件
void TcpServer::closeEvent(QCloseEvent *)
{
on_serverCloseBtn_clicked();
}

 

Tcpclient.h:

#ifndef TCPCLIENT_H
#define TCPCLIENT_H

#include
<QDialog>
#include
<QHostAddress>
#include
<QFile>
#include
<QTime>
class QTcpSocket;

namespace Ui {
class TcpClient;
}

class TcpClient : public QDialog
{
Q_OBJECT

public:
explicit TcpClient(QWidget *parent = 0);
~TcpClient();

void setHostAddress(QHostAddress address);
void setFileName(QString fileName);

protected:
void closeEvent(QCloseEvent *);

private:
Ui::TcpClient
*ui;

QTcpSocket
*tcpClient;
quint16 blockSize;
QHostAddress hostAddress;
qint16 tcpPort;

qint64 TotalBytes;
qint64 bytesReceived;
qint64 bytesToReceive;
qint64 fileNameSize;
QString fileName;
QFile
*localFile;
QByteArray inBlock;

QTime time;

private slots:
void on_tcpClientCancleBtn_clicked();
void on_tcpClientCloseBtn_clicked();

void newConnect();
void readMessage();
void displayError(QAbstractSocket::SocketError);
};

#endif // TCPCLIENT_H

 

Tcpclient.cpp:

#include "tcpclient.h"
#include
"ui_tcpclient.h"

#include
<QTcpSocket>
#include
<QDebug>
#include
<QMessageBox>

TcpClient::TcpClient(QWidget
*parent) :
QDialog(parent),
ui(
new Ui::TcpClient)
{
ui
->setupUi(this);

setFixedSize(
350,180);

TotalBytes
= 0;
bytesReceived
= 0;
fileNameSize
= 0;

tcpClient
= new QTcpSocket(this);
tcpPort
= 6666;
connect(tcpClient, SIGNAL(readyRead()),
this, SLOT(readMessage()));
connect(tcpClient, SIGNAL(error(QAbstractSocket::SocketError)),
this,
SLOT(displayError(QAbstractSocket::SocketError)));
}

TcpClient::
~TcpClient()
{
delete ui;
}

// 设置文件名
void TcpClient::setFileName(QString fileName)
{
localFile
= new QFile(fileName);
}

// 设置地址
void TcpClient::setHostAddress(QHostAddress address)
{
hostAddress
= address;
newConnect();
}

// 创建新连接
void TcpClient::newConnect()
{
blockSize
= 0;
tcpClient
->abort(); //取消已有的连接
tcpClient->connectToHost(hostAddress, tcpPort);//连接到指定ip地址和端口的主机
time.start();
}

// 读取数据
void TcpClient::readMessage()
{
QDataStream
in(tcpClient); //这里的QDataStream可以直接用QTcpSocket对象做参数
in.setVersion(QDataStream::Qt_4_7);

float useTime = time.elapsed();

if (bytesReceived <= sizeof(qint64)*2) { //说明刚开始接受数据
if ((tcpClient->bytesAvailable() //bytesAvailable为返回将要被读取的字节数
>= sizeof(qint64)*2) && (fileNameSize == 0))
{
//接受数据总大小信息和文件名大小信息
in>>TotalBytes>>fileNameSize;
bytesReceived
+= sizeof(qint64)*2;
}
if((tcpClient->bytesAvailable() >= fileNameSize) && (fileNameSize != 0)){
//开始接受文件,并建立文件
in>>fileName;
bytesReceived
+=fileNameSize;

if(!localFile->open(QFile::WriteOnly)){
QMessageBox::warning(
this,tr("应用程序"),tr("无法读取文件 %1:\n%2.")
.arg(fileName).arg(localFile
->errorString()));
return;
}
}
else {
return;
}
}
if (bytesReceived < TotalBytes) {
bytesReceived
+= tcpClient->bytesAvailable();//返回tcpClient中字节的总数
inBlock = tcpClient->readAll(); //返回读到的所有数据
localFile->write(inBlock);
inBlock.resize(
0);
}
ui
->progressBar->setMaximum(TotalBytes);
ui
->progressBar->setValue(bytesReceived);

double speed = bytesReceived / useTime;
ui
->tcpClientStatusLabel->setText(tr("已接收 %1MB (%2MB/s) "
"\n共%3MB 已用时:%4秒\n估计剩余时间:%5秒")
.arg(bytesReceived
/ (1024*1024))
.arg(speed
*1000/(1024*1024),0,'f',2)
.arg(TotalBytes
/ (1024 * 1024))
.arg(useTime
/1000,0,'f',0)
.arg(TotalBytes
/speed/1000 - useTime/1000,0,'f',0));

if(bytesReceived == TotalBytes)
{
localFile
->close();
tcpClient
->close();
ui
->tcpClientStatusLabel->setText(tr("接收文件 %1 完毕")
.arg(fileName));
}
}

// 错误处理
//QAbstractSocket类提供了所有scoket的通用功能,socketError为枚举型
void TcpClient::displayError(QAbstractSocket::SocketError socketError)
{
switch(socketError)
{
//RemoteHostClosedError为远处主机关闭了连接时发出的错误信号
case QAbstractSocket::RemoteHostClosedError : break;
default : qDebug() << tcpClient->errorString();
}
}

// 取消按钮
void TcpClient::on_tcpClientCancleBtn_clicked()
{
tcpClient
->abort();
if (localFile->isOpen())
localFile
->close();
}

// 关闭按钮
void TcpClient::on_tcpClientCloseBtn_clicked()
{
tcpClient
->abort();
if (localFile->isOpen())
localFile
->close();
close();
}

// 关闭事件
void TcpClient::closeEvent(QCloseEvent *)
{
on_tcpClientCloseBtn_clicked();
}

 

Main.cpp:

#include <QtGui/QApplication>
#include
"widget.h"
#include
<QTextCodec>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTextCodec::setCodecForTr(QTextCodec::codecForLocale());
Widget w;
w.show();

return a.exec();
}

 

 

 

 

 


转载请注明本文地址: Qt学习之路_5(Qt TCP的初步使用)