├── ChatRoom.pro ├── README.md ├── Windows版本_编译完成 ├── ChatRoom.exe ├── Qt5Core.dll ├── Qt5Gui.dll ├── Qt5Network.dll ├── Qt5Widgets.dll ├── libgcc_s_seh-1.dll ├── libstdc++-6.dll ├── libwinpthread-1.dll └── platforms │ ├── qdirect2d.dll │ ├── qminimal.dll │ ├── qoffscreen.dll │ └── qwindows.dll ├── basewindow.cpp ├── basewindow.h ├── chatIcon.ico ├── chaticon.qrc ├── chatroom.cpp ├── chatroom.h ├── chatroom.ui ├── icon.qrc ├── icon ├── IP.png ├── bargroud.jpg ├── close.png ├── head.jpg ├── max.png ├── min.png └── name.png ├── image ├── bold.png ├── clear.png ├── color.png ├── italic.png ├── save.png ├── send.png └── under.png ├── images ├── Demonstrate.jpg ├── Demonstrate2.jpg ├── data_flow.png ├── data_flow2.png └── software_module.jpg ├── login.cpp ├── login.h ├── login.ui ├── main.cpp ├── mytitlebar.cpp ├── mytitlebar.h ├── tcpclient.cpp ├── tcpclient.h ├── tcpclient.ui ├── tcpserver.cpp ├── tcpserver.h └── tcpserver.ui /ChatRoom.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2019-01-31T22:40:37 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui network 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = ChatRoom 12 | TEMPLATE = app 13 | 14 | # The following define makes your compiler emit warnings if you use 15 | # any feature of Qt which has been marked as deprecated (the exact warnings 16 | # depend on your compiler). Please consult the documentation of the 17 | # deprecated API in order to know how to port your code away from it. 18 | DEFINES += QT_DEPRECATED_WARNINGS 19 | 20 | # You can also make your code fail to compile if you use deprecated APIs. 21 | # In order to do so, uncomment the following line. 22 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 23 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 24 | 25 | CONFIG += c++11 26 | 27 | SOURCES += \ 28 | main.cpp \ 29 | login.cpp \ 30 | basewindow.cpp \ 31 | mytitlebar.cpp \ 32 | tcpclient.cpp \ 33 | tcpserver.cpp \ 34 | chatroom.cpp 35 | 36 | HEADERS += \ 37 | login.h \ 38 | basewindow.h \ 39 | mytitlebar.h \ 40 | tcpclient.h \ 41 | tcpserver.h \ 42 | chatroom.h 43 | 44 | FORMS += \ 45 | login.ui \ 46 | tcpclient.ui \ 47 | tcpserver.ui \ 48 | chatroom.ui 49 | 50 | # Default rules for deployment. 51 | qnx: target.path = /tmp/$${TARGET}/bin 52 | else: unix:!android: target.path = /opt/$${TARGET}/bin 53 | !isEmpty(target.path): INSTALLS += target 54 | 55 | RESOURCES += \ 56 | icon.qrc 57 | 58 | #set icon 59 | RC_ICONS = chatIcon.ico 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChatRoom 2 | qt_局域网聊天室,修改自《Qt开发实战》中的例子 3 | 4 | ## 运行软件 5 | Windows:进入 目录,点击ChatRoom.exe
6 | Linux:使用Qt Creator打开ChatRoom.pro编译运行 7 | 8 | ## 依赖环境 9 | + Qt5.12 10 | + C++11 11 | 12 | ## 效果 13 | #### 登录界面 14 | ![image](./images/Demonstrate.jpg) 15 | #### 聊天界面 16 | ![image](./images/Demonstrate2.jpg) 17 | 18 | ## 各模块功能 19 | ![image](./images/software_module.jpg) 20 | 21 | 登录模块: 22 | 自定义标题栏 23 | 获取用户输入的名字 24 | 获取本机IP 25 | 登录按钮:按下后将用户名和IP地址传送到聊天模块,并切换界面到聊天模块 26 | 聊天界面模块: 27 | 广播该用户已在线 28 | 显示所有在线的用户与对应的主机名和IP 29 | 输入框可更改字体样式,如字体种类、大小、颜色等 30 | 发送消息的功能 31 | 退出则向所有用户广播该用户已离线 32 | 文件传输功能 33 | 显示文件传输百分比与需要传输的时间 34 | 35 | ## 数据流程 36 | #### 主要通过UDP与TCP协议来分别实现消息的传播和文件传输的功能 37 | ![image](./images/data_flow.png) 38 | #### 文件传输模块流程图 39 | ![image](./images/data_flow2.png) 40 | -------------------------------------------------------------------------------- /Windows版本_编译完成/ChatRoom.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/Windows版本_编译完成/ChatRoom.exe -------------------------------------------------------------------------------- /Windows版本_编译完成/Qt5Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/Windows版本_编译完成/Qt5Core.dll -------------------------------------------------------------------------------- /Windows版本_编译完成/Qt5Gui.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/Windows版本_编译完成/Qt5Gui.dll -------------------------------------------------------------------------------- /Windows版本_编译完成/Qt5Network.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/Windows版本_编译完成/Qt5Network.dll -------------------------------------------------------------------------------- /Windows版本_编译完成/Qt5Widgets.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/Windows版本_编译完成/Qt5Widgets.dll -------------------------------------------------------------------------------- /Windows版本_编译完成/libgcc_s_seh-1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/Windows版本_编译完成/libgcc_s_seh-1.dll -------------------------------------------------------------------------------- /Windows版本_编译完成/libstdc++-6.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/Windows版本_编译完成/libstdc++-6.dll -------------------------------------------------------------------------------- /Windows版本_编译完成/libwinpthread-1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/Windows版本_编译完成/libwinpthread-1.dll -------------------------------------------------------------------------------- /Windows版本_编译完成/platforms/qdirect2d.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/Windows版本_编译完成/platforms/qdirect2d.dll -------------------------------------------------------------------------------- /Windows版本_编译完成/platforms/qminimal.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/Windows版本_编译完成/platforms/qminimal.dll -------------------------------------------------------------------------------- /Windows版本_编译完成/platforms/qoffscreen.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/Windows版本_编译完成/platforms/qoffscreen.dll -------------------------------------------------------------------------------- /Windows版本_编译完成/platforms/qwindows.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/Windows版本_编译完成/platforms/qwindows.dll -------------------------------------------------------------------------------- /basewindow.cpp: -------------------------------------------------------------------------------- 1 | #include "basewindow.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | BaseWindow::BaseWindow(QWidget *parent) : QWidget(parent) 8 | { 9 | // FramelessWindowHint属性设置窗口去除边框; 10 | // WindowMinimizeButtonHint 属性设置在窗口最小化时,点击任务栏窗口可以显示出原窗口; 11 | this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint); 12 | // 设置窗口背景透明; 13 | setAttribute(Qt::WA_TranslucentBackground); 14 | 15 | // 注意!!!如果是主窗口不要设置WA_DeleteOnClose属性; 16 | // 关闭窗口时释放资源; 17 | setAttribute(Qt::WA_DeleteOnClose); 18 | // 初始化标题栏; 19 | initTitleBar(); 20 | } 21 | 22 | BaseWindow::~BaseWindow() 23 | { 24 | 25 | } 26 | 27 | void BaseWindow::initTitleBar() 28 | { 29 | m_titleBar = new MyTitleBar(this); 30 | m_titleBar->move(0, 0); 31 | 32 | connect(m_titleBar, SIGNAL(signalButtonMinClicked()), this, SLOT(onButtonMinClicked())); 33 | connect(m_titleBar, SIGNAL(signalButtonRestoreClicked()), this, SLOT(onButtonRestoreClicked())); 34 | connect(m_titleBar, SIGNAL(signalButtonMaxClicked()), this, SLOT(onButtonMaxClicked())); 35 | connect(m_titleBar, SIGNAL(signalButtonCloseClicked()), this, SLOT(onButtonCloseClicked())); 36 | 37 | } 38 | 39 | void BaseWindow::paintEvent(QPaintEvent* event) 40 | { 41 | //设置背景色; 42 | QPainter painter(this); 43 | QPainterPath pathBack; 44 | pathBack.setFillRule(Qt::WindingFill); 45 | pathBack.addRoundedRect(QRect(0, 0, this->width(), this->height()), 3, 3); 46 | painter.setRenderHint(QPainter::SmoothPixmapTransform, true); 47 | painter.fillPath(pathBack, QBrush(QColor(238, 223, 204))); 48 | 49 | return QWidget::paintEvent(event); 50 | } 51 | #if 0 52 | void BaseWindow::loadStyleSheet(const QString &sheetName) 53 | { 54 | 55 | 56 | QFile file(":/Resources/" + sheetName + ".css"); 57 | file.open(QFile::ReadOnly); 58 | if (file.isOpen()) 59 | { 60 | QString styleSheet = this->styleSheet(); 61 | styleSheet += QLatin1String(file.readAll()); 62 | this->setStyleSheet(styleSheet); 63 | } 64 | } 65 | #endif 66 | 67 | void BaseWindow::onButtonMinClicked() 68 | { 69 | showMinimized(); 70 | } 71 | 72 | void BaseWindow::onButtonRestoreClicked() 73 | { 74 | QPoint windowPos; 75 | QSize windowSize; 76 | m_titleBar->getRestoreInfo(windowPos, windowSize); 77 | this->setGeometry(QRect(windowPos, windowSize)); 78 | } 79 | 80 | void BaseWindow::onButtonMaxClicked() 81 | { 82 | m_titleBar->saveRestoreInfo(this->pos(), QSize(this->width(), this->height())); 83 | QRect desktopRect = QApplication::desktop()->availableGeometry(); 84 | QRect FactRect = QRect(desktopRect.x() - 3, desktopRect.y() - 3, desktopRect.width() + 6, desktopRect.height() + 6); 85 | setGeometry(FactRect); 86 | } 87 | 88 | void BaseWindow::onButtonCloseClicked() 89 | { 90 | this->close(); 91 | } 92 | -------------------------------------------------------------------------------- /basewindow.h: -------------------------------------------------------------------------------- 1 | #ifndef BASEWINDOW_H 2 | #define BASEWINDOW_H 3 | 4 | #include 5 | #include "mytitlebar.h" 6 | 7 | /* 8 | * 自定义标题栏 9 | */ 10 | 11 | class BaseWindow : public QWidget 12 | { 13 | Q_OBJECT 14 | public: 15 | explicit BaseWindow(QWidget *parent = nullptr); 16 | ~BaseWindow(); 17 | 18 | private: 19 | void initTitleBar(); 20 | void paintEvent(QPaintEvent *event); 21 | void loadStyleSheet(const QString &sheetName); 22 | 23 | private slots: 24 | void onButtonMinClicked(); 25 | void onButtonRestoreClicked(); 26 | void onButtonMaxClicked(); 27 | void onButtonCloseClicked(); 28 | 29 | protected: 30 | MyTitleBar* m_titleBar; 31 | 32 | }; 33 | 34 | #endif // BASEWINDOW_H 35 | -------------------------------------------------------------------------------- /chatIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/chatIcon.ico -------------------------------------------------------------------------------- /chaticon.qrc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON "chatIcon.ico" 2 | -------------------------------------------------------------------------------- /chatroom.cpp: -------------------------------------------------------------------------------- 1 | #include "chatroom.h" 2 | #include "ui_chatroom.h" 3 | #include "tcpserver.h" 4 | #include "tcpclient.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | chatRoom::chatRoom(QWidget *parent,User u) : 18 | QWidget(parent), 19 | ui(new Ui::chatRoom) 20 | { 21 | ui->setupUi(this); 22 | setUser(u); 23 | setWindowTitle("聊天室 "+user.address); 24 | 25 | udpSocket = new QUdpSocket(this); 26 | port = 45454; //设置端口号 27 | udpSocket->bind(port,QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint); //绑定端口 28 | connect(udpSocket,SIGNAL(readyRead()),this,SLOT(processPendingDatagrams())); 29 | sendMessage(NewParticipant); 30 | 31 | 32 | 33 | server = new TcpServer(this); 34 | connect(server,SIGNAL(sendFileName(QString)), 35 | this,SLOT(getFileName(QString))); 36 | connect(ui->messageTextEdit,SIGNAL(currentCharFormatChanged(QTextCharFormat)), 37 | this,SLOT(currentFormatChanged(QTextCharFormat))); 38 | 39 | //设置消息过滤器 40 | ui->messageTextEdit->installEventFilter(this); 41 | } 42 | 43 | chatRoom::~chatRoom() 44 | { 45 | delete ui; 46 | } 47 | 48 | //成员加入处理函数 49 | void chatRoom::newParticipant(QString userName, QString localHostName, QString ipAddress) 50 | { 51 | //使用主机名判断用户是否已经加入 52 | bool isEmpty = ui->userTableWidget 53 | ->findItems(localHostName,Qt::MatchExactly).isEmpty(); 54 | 55 | //如果用户未加入则向界面右侧的用户列表加入新用户的信息 56 | if(isEmpty 57 | ) { 58 | QTableWidgetItem *user = new QTableWidgetItem(userName); 59 | QTableWidgetItem *host = new QTableWidgetItem(localHostName); 60 | QTableWidgetItem *ip = new QTableWidgetItem(ipAddress); 61 | 62 | ui->userTableWidget->insertRow(0); 63 | ui->userTableWidget->setItem(0,0,user); 64 | ui->userTableWidget->setItem(0,1,host); 65 | ui->userTableWidget->setItem(0,2,ip); 66 | 67 | ui->messageBrowser->setTextColor(Qt::gray); 68 | ui->messageBrowser->setCurrentFont(QFont("Times New Roman",10)); 69 | ui->messageBrowser->append(tr("%1 在线").arg(userName)); 70 | ui->userNumLabel->setText(tr("在线人数: %1") 71 | .arg(ui->userTableWidget->rowCount())); 72 | //发送新用户登录消息 73 | sendMessage(NewParticipant); 74 | } 75 | } 76 | 77 | //成员离开处理函数 78 | void chatRoom::participantLeft(QString username, QString localHostName, QString time) 79 | { 80 | int rowNum = ui->userTableWidget->findItems(localHostName, 81 | Qt::MatchExactly).first()->row(); 82 | ui->userTableWidget->removeRow(rowNum); 83 | ui->messageBrowser->setTextColor(Qt::gray); 84 | ui->messageBrowser->setCurrentFont(QFont("Times New Roman",10)); 85 | ui->messageBrowser->append(tr("%1 于 %2 离开!").arg(username).arg(time)); 86 | ui->userNumLabel->setText(tr("在线人数: %1") 87 | .arg(ui->userTableWidget->rowCount())); 88 | } 89 | 90 | //广播消息,type用于接收端区分消息类型 91 | void chatRoom::sendMessage(MessageType type, QString serverAddress) 92 | { 93 | QByteArray data; 94 | QDataStream out(&data,QIODevice::WriteOnly); 95 | QString localHostName = QHostInfo::localHostName(); //获取用户名、主机名 96 | QString address = getIP(); 97 | out << type << getUserName() << localHostName; 98 | 99 | QString clientAddress; 100 | int row; 101 | 102 | //判断发送消息的类型 103 | switch(type) 104 | { 105 | case Message: 106 | //发送的是消息 107 | if(ui->messageTextEdit->toPlainText() == "") 108 | { 109 | QMessageBox::warning(0,tr("警告"), 110 | tr("发送内容不能为空"),QMessageBox::Ok); 111 | return; 112 | } 113 | out << address << getMessage(); 114 | ui->messageBrowser->verticalScrollBar() 115 | ->setValue(ui->messageBrowser->verticalScrollBar()->maximum()); 116 | break; 117 | 118 | case NewParticipant: 119 | //广播新用户加入的消息 120 | out << address; 121 | break; 122 | 123 | case ParticipantLeft: 124 | //广播用户离开的消息 125 | break; 126 | 127 | case FileName: 128 | //发送传输的文件名 129 | row = ui->userTableWidget->currentRow(); 130 | clientAddress= ui->userTableWidget->item(row,2)->text(); 131 | out << address << clientAddress << fileName; 132 | break; 133 | 134 | case Refuse: 135 | //发送拒绝接受文件消息 136 | out << serverAddress; 137 | break; 138 | } 139 | //进行udp广播 140 | udpSocket->writeDatagram(data,data.length(),QHostAddress::Broadcast,port); 141 | } 142 | 143 | //判断是否接收、发送文件 144 | void chatRoom::hasPendingFile(QString userName, QString serverAddress, 145 | QString clientAddress, QString fileName) 146 | { 147 | 148 | QString ipAddress = getIP(); 149 | if(ipAddress == clientAddress) 150 | { 151 | int btn = QMessageBox::information(this,tr("接收文件"), 152 | tr("来自%1(%2)的文件:%3,是否接收?") 153 | .arg(userName).arg(serverAddress) 154 | .arg(fileName),QMessageBox::Yes,QMessageBox::No); 155 | if(btn == QMessageBox::Yes) { 156 | QString name = QFileDialog::getSaveFileName(0,tr("保存文件"),fileName); 157 | if(!name.isEmpty()) 158 | { 159 | TcpClient *client = new TcpClient(this); 160 | client->setFileName(name); 161 | client->setHostAddress(QHostAddress(serverAddress)); 162 | client->show(); 163 | } 164 | } 165 | else { 166 | sendMessage(Refuse,serverAddress); 167 | } 168 | } 169 | } 170 | 171 | //保存接受的文件 172 | bool chatRoom::saveFile(const QString &fileName) 173 | { 174 | QFile file(fileName); 175 | if(!file.open(QFile::WriteOnly|QFile::Text)) { 176 | QMessageBox::warning(this,tr("保存文件"), 177 | tr("无法保存文件 %1:\n %2").arg(fileName) 178 | .arg(file.errorString())); 179 | return false; 180 | } 181 | QTextStream out(&file); 182 | out << ui->messageBrowser->toPlainText(); 183 | return true; 184 | } 185 | 186 | 187 | //关闭事件:在关闭程序时发送用户离开的广播,让其他端点在其用户列表中删除用户 188 | void chatRoom::closeEvent(QCloseEvent *event) 189 | { 190 | sendMessage(ParticipantLeft); 191 | QWidget::closeEvent(event); 192 | } 193 | 194 | //快捷键ctrl+enter发送消息 195 | bool chatRoom::eventFilter(QObject *obj, QEvent *event) 196 | { 197 | #if 1 198 | if(obj == ui->messageTextEdit){ 199 | 200 | if(event->type() == QEvent::KeyPress){ 201 | QKeyEvent *k = static_cast(event); 202 | //按下ctrl+enter发消息 203 | if(k->key() == Qt::Key_Return &&(k->modifiers() & Qt::ControlModifier)) 204 | { 205 | on_sendButton_clicked(); 206 | return true; 207 | } 208 | } 209 | } 210 | return false; 211 | #endif 212 | } 213 | 214 | //获取IP地址 215 | QString chatRoom::getIP() 216 | { 217 | #if 0 218 | QListlist = QNetworkInterface::allAddresses(); 219 | foreach(QHostAddress address,list) { 220 | if(address.protocol() == QAbstractSocket::IPv4Protocol) 221 | return address.toString(); 222 | } 223 | return 0; 224 | #endif 225 | return user.address; 226 | } 227 | 228 | //获取用户名 229 | QString chatRoom::getUserName() 230 | { 231 | #if 0 232 | QStringList envVariables; 233 | envVariables << "USERNAME.*" << "USER.*" << "USERDOMAIN.*" 234 | << "HOSTNAME.*" << "DOMAINNAME.*"; 235 | QStringList environment = QProcess::systemEnvironment(); 236 | foreach(QString string,envVariables) 237 | { 238 | int index = environment.indexOf(QRegExp(string)); 239 | if(index != -1) { 240 | QStringList stringList = environment.at(index).split('='); 241 | if(stringList.size() == 2) 242 | { 243 | qDebug() << stringList.at(1); 244 | return stringList.at(1); 245 | break; 246 | } 247 | } 248 | } 249 | return "unKnow"; 250 | #endif 251 | return user.name; 252 | } 253 | 254 | //获取用户输入的消息并设置 255 | QString chatRoom::getMessage() 256 | { 257 | QString msg = ui->messageTextEdit->toHtml(); 258 | ui->messageTextEdit->clear(); //将文本编辑器内容清空 259 | ui->messageTextEdit->setFocus(); 260 | return msg; 261 | } 262 | 263 | //处理接收到的广播消息 264 | void chatRoom::processPendingDatagrams() 265 | { 266 | while(udpSocket->hasPendingDatagrams()) 267 | { 268 | QByteArray datagram; 269 | datagram.resize(udpSocket->pendingDatagramSize()); //调整为接收到数据的大小 270 | udpSocket->readDatagram(datagram.data(),datagram.size()); 271 | QDataStream in(&datagram,QIODevice::ReadOnly); 272 | int messageType; 273 | in >> messageType; 274 | QString clientAddress,fileName; 275 | QString userName,localHostName,ipAddress,message; 276 | QString time = QDateTime::currentDateTime() 277 | .toString("yyyy-MM-dd hh:mm:ss"); 278 | switch(messageType) 279 | { 280 | //显示发送的消息 281 | case Message: 282 | in >> userName >> localHostName >> ipAddress >> message; 283 | ui->messageBrowser->setTextColor(Qt::blue); 284 | ui->messageBrowser->setCurrentFont(QFont("Times New Roman",12)); 285 | ui->messageBrowser->append("[" + userName + "]" + time); 286 | ui->messageBrowser->append(message); 287 | break; 288 | 289 | //有新成员加入 290 | case NewParticipant: 291 | in >> userName >> localHostName >> ipAddress; 292 | newParticipant(userName,localHostName,ipAddress); //加入处理 293 | break; 294 | 295 | //有成员离开 296 | case ParticipantLeft: 297 | in >> userName >> localHostName; 298 | participantLeft(userName,localHostName,time); //进行离开处理 299 | 300 | //要发送的文件名 301 | case FileName: 302 | in >> userName >> localHostName >> ipAddress; 303 | in >> clientAddress >> fileName; 304 | hasPendingFile(userName,ipAddress,clientAddress,fileName); 305 | break; 306 | 307 | //拒绝接受的文件 308 | case Refuse: 309 | in >> userName >> localHostName; 310 | QString serverAddress; 311 | in >> serverAddress; 312 | QString ipAddress = getIP(); 313 | if(ipAddress == serverAddress) 314 | { 315 | server->refused(); 316 | } 317 | break; 318 | } 319 | } 320 | } 321 | 322 | //发送按钮--槽 323 | void chatRoom::on_sendButton_clicked() 324 | { 325 | sendMessage(Message); 326 | } 327 | 328 | //给出文件名 329 | void chatRoom::getFileName(QString name) 330 | { 331 | fileName = name; 332 | sendMessage(FileName); //upd广播要发送的文件名 333 | } 334 | 335 | //传输文件按钮--槽 336 | void chatRoom::on_sendToolBtn_clicked() 337 | { 338 | if(ui->userTableWidget->selectedItems().isEmpty()) 339 | { 340 | QMessageBox::warning(0,tr("选择用户"), 341 | tr("请先从用户列表选择要传送的用户!"),QMessageBox::Ok); 342 | return; 343 | } 344 | server->show(); 345 | server->initServer(); 346 | } 347 | 348 | //更改字体大小 349 | void chatRoom::on_SizeComboBox_currentIndexChanged(const QString &arg1) 350 | { 351 | ui->messageTextEdit->setFontPointSize(arg1.toDouble()); 352 | ui->messageTextEdit->setFocus(); 353 | } 354 | 355 | //加粗 356 | void chatRoom::on_boldToolBtn_clicked(bool checked) 357 | { 358 | if(checked) 359 | { 360 | ui->messageTextEdit->setFontWeight(QFont::Bold); 361 | } 362 | else 363 | { 364 | ui->messageTextEdit->setFontWeight(QFont::Normal); 365 | } 366 | ui->messageTextEdit->setFocus(); 367 | } 368 | 369 | 370 | //斜体 371 | void chatRoom::on_italicToolBtn_clicked(bool checked) 372 | { 373 | ui->messageTextEdit->setFontItalic(checked); 374 | ui->messageTextEdit->setFocus(); 375 | } 376 | 377 | //下划线 378 | void chatRoom::on_underlineToolBtn_clicked(bool checked) 379 | { 380 | ui->messageTextEdit->setFontUnderline(checked); 381 | ui->messageTextEdit->setFocus(); 382 | } 383 | 384 | //字体颜色 385 | void chatRoom::on_colorToolBtn_clicked() 386 | { 387 | color = QColorDialog::getColor(color,this); 388 | if(color.isValid()) { 389 | ui->messageTextEdit->setTextColor(color); 390 | ui->messageTextEdit->setFocus(); 391 | } 392 | } 393 | 394 | //设置字体时可切换到相应状态 395 | void chatRoom::currentFormatChanged(const QTextCharFormat &format) 396 | { 397 | ui->fontComboBox->setCurrentFont(format.font()); 398 | if(format.fontPointSize() < 9) { 399 | ui->SizeComboBox->setCurrentIndex(3); 400 | } 401 | else { 402 | ui->SizeComboBox->setCurrentIndex(ui->SizeComboBox 403 | ->findText(QString::number(format.fontPointSize()))); 404 | } 405 | ui->boldToolBtn->setChecked(format.font().bold()); 406 | ui->italicToolBtn->setChecked(format.font().italic()); 407 | ui->underlineToolBtn->setChecked(format.font().underline()); 408 | color = format.foreground().color(); 409 | } 410 | 411 | //保存聊天记录 412 | void chatRoom::on_saveToolBtn_clicked() 413 | { 414 | if(ui->messageBrowser->document()->isEmpty()) { 415 | QMessageBox::warning(0,tr("警告"), 416 | tr("聊天记录为空,无法保存!"),QMessageBox::Ok); 417 | }else { 418 | QString fileName = QFileDialog::getSaveFileName(this,tr("保存聊天记录"), 419 | tr("聊天记录"),tr("文本(*.txt);;All File(*.*)")); 420 | if(!fileName.isEmpty()) 421 | saveFile(fileName); 422 | } 423 | } 424 | 425 | //清空聊天记录 426 | void chatRoom::on_clearToolBtn_clicked() 427 | { 428 | ui->messageBrowser->clear(); 429 | } 430 | 431 | //退出按钮 432 | void chatRoom::on_exitButton_clicked() 433 | { 434 | close(); 435 | } 436 | 437 | //改变字体 438 | void chatRoom::on_fontComboBox_currentFontChanged(const QFont &f) 439 | { 440 | ui->messageTextEdit->setCurrentFont(f); 441 | ui->messageTextEdit->setFocus(); 442 | } 443 | 444 | 445 | void chatRoom::setUser(User u) 446 | { 447 | user.name = u.name; 448 | user.address = u.address; 449 | user.hostaddress = u.hostaddress; 450 | qDebug() << user.name< 5 | 6 | /* 7 | * 使用UDP协议进行聊天 8 | * 使用TCP协议进行文件传输 9 | */ 10 | 11 | namespace Ui { 12 | class chatRoom; 13 | } 14 | 15 | class QUdpSocket; 16 | class TcpServer; 17 | class QTextCharFormat; 18 | 19 | //枚举变量分别表示消息类型、新用户加入、用户退出、文件名、拒绝接受文件 20 | enum MessageType {Message,NewParticipant,ParticipantLeft,FileName,Refuse}; 21 | 22 | struct User{ 23 | QString name; 24 | QString hostaddress; 25 | QString address; 26 | }; 27 | 28 | class chatRoom : public QWidget 29 | { 30 | Q_OBJECT 31 | 32 | public: 33 | explicit chatRoom(QWidget *parent = 0,User u = {"","",""}); 34 | ~chatRoom(); 35 | //用于从登陆端接收用户信息 36 | void setUser(User u); 37 | 38 | protected: 39 | //新成员加入 40 | void newParticipant(QString userName, 41 | QString localHostName,QString ipAddress); 42 | //成员离开 43 | void participantLeft(QString username, 44 | QString localHostName,QString time); 45 | //广播用户登录信息 46 | void sendMessage(MessageType type,QString serverAddress = ""); 47 | //判断是否接收、发送文件 48 | void hasPendingFile(QString userName, QString serverAddress, 49 | QString clientAddress,QString fileName); 50 | //保存接受的文件 51 | bool saveFile(const QString &fileName); 52 | //关闭事件 53 | void closeEvent(QCloseEvent *event); 54 | //事件过滤器,按下回车发送信息 55 | bool eventFilter(QObject *obj, QEvent *event); 56 | 57 | //获取IP地址 58 | QString getIP(); 59 | //获取用户名 60 | QString getUserName(); 61 | //获取消息 62 | QString getMessage(); 63 | private: 64 | Ui::chatRoom *ui; 65 | QUdpSocket *udpSocket; 66 | qint16 port; 67 | QString fileName; 68 | TcpServer *server; 69 | QColor color; 70 | User user; 71 | 72 | private slots: 73 | //接收广播消息——槽 74 | void processPendingDatagrams(); 75 | //发送按钮--槽 76 | void on_sendButton_clicked(); 77 | //获取发送过来的文件名 78 | void getFileName(QString); 79 | //传输文件按钮--槽 80 | void on_sendToolBtn_clicked(); 81 | //设置字体 82 | void on_fontComboBox_currentFontChanged(const QFont &f); 83 | //更改字体大小 84 | void on_SizeComboBox_currentIndexChanged(const QString &arg1); 85 | //字体加粗 86 | void on_boldToolBtn_clicked(bool checked); 87 | //斜体 88 | void on_italicToolBtn_clicked(bool checked); 89 | //下划线 90 | void on_underlineToolBtn_clicked(bool checked); 91 | //字体颜色 92 | void on_colorToolBtn_clicked(); 93 | //设置字体时可切换到相应状态 94 | void currentFormatChanged(const QTextCharFormat &format); 95 | //保存聊天记录 96 | void on_saveToolBtn_clicked(); 97 | //清空聊天记录--槽 98 | void on_clearToolBtn_clicked(); 99 | //退出按钮--槽 100 | void on_exitButton_clicked(); 101 | 102 | }; 103 | 104 | #endif // WIDGET_H 105 | -------------------------------------------------------------------------------- /chatroom.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | chatRoom 4 | 5 | 6 | 7 | 0 8 | 0 9 | 900 10 | 460 11 | 12 | 13 | 14 | 聊天室 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 3 35 | 36 | 37 | 38 | 9 39 | 40 | 41 | 42 | 43 | 10 44 | 45 | 46 | 47 | 48 | 11 49 | 50 | 51 | 52 | 53 | 12 54 | 55 | 56 | 57 | 58 | 13 59 | 60 | 61 | 62 | 63 | 14 64 | 65 | 66 | 67 | 68 | 15 69 | 70 | 71 | 72 | 73 | 16 74 | 75 | 76 | 77 | 78 | 17 79 | 80 | 81 | 82 | 83 | 18 84 | 85 | 86 | 87 | 88 | 19 89 | 90 | 91 | 92 | 93 | 20 94 | 95 | 96 | 97 | 98 | 21 99 | 100 | 101 | 102 | 103 | 22 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | Qt::DefaultContextMenu 112 | 113 | 114 | 加粗 115 | 116 | 117 | ... 118 | 119 | 120 | 121 | :/new/chatroom/image/bold.png:/new/chatroom/image/bold.png 122 | 123 | 124 | 125 | 22 126 | 22 127 | 128 | 129 | 130 | true 131 | 132 | 133 | true 134 | 135 | 136 | 137 | 138 | 139 | 140 | 倾斜 141 | 142 | 143 | ... 144 | 145 | 146 | 147 | :/new/chatroom/image/italic.png:/new/chatroom/image/italic.png 148 | 149 | 150 | 151 | 22 152 | 22 153 | 154 | 155 | 156 | true 157 | 158 | 159 | true 160 | 161 | 162 | 163 | 164 | 165 | 166 | 下划线 167 | 168 | 169 | ... 170 | 171 | 172 | 173 | :/new/chatroom/image/under.png:/new/chatroom/image/under.png 174 | 175 | 176 | 177 | 22 178 | 22 179 | 180 | 181 | 182 | true 183 | 184 | 185 | true 186 | 187 | 188 | 189 | 190 | 191 | 192 | 更改字体颜色 193 | 194 | 195 | ... 196 | 197 | 198 | 199 | :/new/chatroom/image/color.png:/new/chatroom/image/color.png 200 | 201 | 202 | 203 | 22 204 | 22 205 | 206 | 207 | 208 | true 209 | 210 | 211 | 212 | 213 | 214 | 215 | 传输文件 216 | 217 | 218 | ... 219 | 220 | 221 | 222 | :/new/chatroom/image/send.png:/new/chatroom/image/send.png 223 | 224 | 225 | 226 | 22 227 | 22 228 | 229 | 230 | 231 | true 232 | 233 | 234 | 235 | 236 | 237 | 238 | 保存聊天记录 239 | 240 | 241 | ... 242 | 243 | 244 | 245 | :/new/chatroom/image/save.png:/new/chatroom/image/save.png 246 | 247 | 248 | 249 | 22 250 | 22 251 | 252 | 253 | 254 | true 255 | 256 | 257 | 258 | 259 | 260 | 261 | 清空聊天记录 262 | 263 | 264 | ... 265 | 266 | 267 | 268 | :/new/chatroom/image/clear.png:/new/chatroom/image/clear.png 269 | 270 | 271 | 272 | 22 273 | 22 274 | 275 | 276 | 277 | true 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | QAbstractItemView::SingleSelection 292 | 293 | 294 | QAbstractItemView::SelectRows 295 | 296 | 297 | false 298 | 299 | 300 | 301 | 用户名 302 | 303 | 304 | 305 | 306 | 主机名 307 | 308 | 309 | 310 | 311 | IP地址 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | Qt::Horizontal 324 | 325 | 326 | 327 | 108 328 | 20 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 发送 337 | 338 | 339 | 340 | 341 | 342 | 343 | Qt::Horizontal 344 | 345 | 346 | 347 | 178 348 | 20 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 在线用户:0人 357 | 358 | 359 | 360 | 361 | 362 | 363 | Qt::Horizontal 364 | 365 | 366 | 367 | 248 368 | 20 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 退出 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | -------------------------------------------------------------------------------- /icon.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icon/IP.png 4 | icon/name.png 5 | icon/max.png 6 | icon/min.png 7 | icon/close.png 8 | icon/bargroud.jpg 9 | icon/head.jpg 10 | 11 | 12 | image/bold.png 13 | image/clear.png 14 | image/color.png 15 | image/italic.png 16 | image/save.png 17 | image/send.png 18 | image/under.png 19 | 20 | 21 | -------------------------------------------------------------------------------- /icon/IP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/icon/IP.png -------------------------------------------------------------------------------- /icon/bargroud.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/icon/bargroud.jpg -------------------------------------------------------------------------------- /icon/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/icon/close.png -------------------------------------------------------------------------------- /icon/head.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/icon/head.jpg -------------------------------------------------------------------------------- /icon/max.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/icon/max.png -------------------------------------------------------------------------------- /icon/min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/icon/min.png -------------------------------------------------------------------------------- /icon/name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/icon/name.png -------------------------------------------------------------------------------- /image/bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/image/bold.png -------------------------------------------------------------------------------- /image/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/image/clear.png -------------------------------------------------------------------------------- /image/color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/image/color.png -------------------------------------------------------------------------------- /image/italic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/image/italic.png -------------------------------------------------------------------------------- /image/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/image/save.png -------------------------------------------------------------------------------- /image/send.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/image/send.png -------------------------------------------------------------------------------- /image/under.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/image/under.png -------------------------------------------------------------------------------- /images/Demonstrate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/images/Demonstrate.jpg -------------------------------------------------------------------------------- /images/Demonstrate2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/images/Demonstrate2.jpg -------------------------------------------------------------------------------- /images/data_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/images/data_flow.png -------------------------------------------------------------------------------- /images/data_flow2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/images/data_flow2.png -------------------------------------------------------------------------------- /images/software_module.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhengYYZZ/chatRoom/9aceee379802e872283f1227824af71ba8b16021/images/software_module.jpg -------------------------------------------------------------------------------- /login.cpp: -------------------------------------------------------------------------------- 1 | #include "login.h" 2 | #include "ui_login.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | login::login(QWidget *parent) : 13 | BaseWindow(parent), 14 | ui(new Ui::login) 15 | { 16 | titleBar = new BaseWindow; 17 | ui->setupUi(this); 18 | 19 | 20 | initTitleBar(); 21 | initControl(); 22 | 23 | } 24 | 25 | login::~login() 26 | { 27 | delete ui; 28 | } 29 | 30 | #if 1 31 | void login::initTitleBar() 32 | { 33 | // 设置标题栏跑马灯效果,可以不设置; 34 | //m_titleBar->setTitleRoll(); 35 | 36 | m_titleBar->move(0,0); 37 | m_titleBar->raise(); 38 | m_titleBar->setBackgroundColor(0,0,0,true); 39 | m_titleBar->setButtonType(MIN_MAX_BUTTON); 40 | m_titleBar->setTitleWidth(this->width()); 41 | 42 | } 43 | #endif 44 | 45 | void login::initControl() 46 | { 47 | #if 0 48 | //设置上半角背景图片 49 | QPixmap backicon(tr("bargroud.jpg")); 50 | backicon = backicon.scaled(ui->backgroundLabel->width(),ui->backgroundLabel->height()); 51 | ui->backgroundLabel->setPixmap(backicon); 52 | 53 | //设置头像 54 | int wid = ui->headPortraitLabel->width(); 55 | int hei = ui->headPortraitLabel->height(); 56 | QPixmap headicon("head.jpg"); 57 | /*-----设置圆形头像----- 58 | QPixmap pixmap(wid,hei); 59 | pixmap.fill(Qt::transparent); 60 | QPainter painter(&pixmap); 61 | painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); 62 | QPainterPath path; 63 | path.addEllipse(0, 0, wid, hei); 64 | painter.setClipPath(path); 65 | painter.drawPixmap(0, 0, wid, hei, headicon); */ 66 | 67 | headicon = headicon.scaled(wid,hei); 68 | ui->headPortraitLabel->setPixmap(headicon); 69 | #endif 70 | 71 | 72 | 73 | //暗注释 74 | ui->nameLineEdit->setPlaceholderText(QStringLiteral(" 昵称")); 75 | ui->ipLineEdit->setPlaceholderText(tr(" IP地址")); 76 | 77 | ui->ipLineEdit->setText(getIP()); //将ip显示到地址栏上 78 | 79 | } 80 | 81 | QString login::getIP() 82 | { 83 | QListlist = QNetworkInterface::allAddresses(); 84 | foreach(QHostAddress address,list) { 85 | if(address.protocol() == QAbstractSocket::IPv4Protocol) 86 | return address.toString(); 87 | } 88 | return 0; 89 | } 90 | 91 | 92 | void login::on_createPushButton_clicked() 93 | { 94 | users.name = ui->nameLineEdit->text(); 95 | if(ui->nameLineEdit->text() == "") 96 | { 97 | QMessageBox::warning(0,tr("警告"), 98 | tr("发送内容不能为空"),QMessageBox::Ok); 99 | return; 100 | } 101 | users.hostaddress = getIP(); 102 | users.address = getIP(); 103 | //qDebug() << users.address << users.name << users.hostaddress; 104 | chat = new chatRoom(0,users); 105 | //chat->setUser(users); 106 | chat->show(); 107 | 108 | this->hide(); 109 | } 110 | 111 | 112 | -------------------------------------------------------------------------------- /login.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGIN_H 2 | #define LOGIN_H 3 | 4 | #include 5 | #include 6 | #include "basewindow.h" 7 | #include "chatroom.h" 8 | 9 | /* 10 | * 登录界面--继承自baseWindow类 11 | * 1)控件图标 12 | * 2)自定义状态栏 13 | * 3)头像、背景 14 | * 4)窗口固定大小 15 | * 功能 ---获取本地ip 16 | */ 17 | 18 | namespace Ui { 19 | class login; 20 | } 21 | 22 | 23 | class login : public BaseWindow 24 | { 25 | Q_OBJECT 26 | 27 | public: 28 | explicit login(QWidget *parent = nullptr); 29 | ~login(); 30 | 31 | //初始化标题栏 32 | void initTitleBar(); 33 | //初始化控件 34 | void initControl(); 35 | //获取ip地址 36 | QString getIP(); 37 | 38 | 39 | private slots: 40 | void on_createPushButton_clicked(); 41 | 42 | private: 43 | Ui::login *ui; 44 | BaseWindow *titleBar; 45 | User users; 46 | chatRoom *chat; 47 | }; 48 | 49 | #endif // LOGIN_H 50 | -------------------------------------------------------------------------------- /login.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | login 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 327 11 | 12 | 13 | 14 | login 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 70 23 | 210 24 | 41 25 | 31 26 | 27 | 28 | 29 | 30 | 11 31 | 32 | 33 | 34 | image: url(:/new/login/icon/name.png); 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 70 44 | 250 45 | 41 46 | 31 47 | 48 | 49 | 50 | 51 | 11 52 | 53 | 54 | 55 | image: url(:/new/login/icon/IP.png); 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 130 65 | 290 66 | 161 67 | 31 68 | 69 | 70 | 71 | 创建房间 72 | 73 | 74 | 75 | 76 | 77 | 160 78 | 120 79 | 91 80 | 81 81 | 82 | 83 | 84 | border-image: url(:/new/login/icon/head.jpg); 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 0 94 | 0 95 | 401 96 | 201 97 | 98 | 99 | 100 | border-image: url(:/new/login/icon/bargroud.jpg); 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 120 110 | 210 111 | 221 112 | 31 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 120 | 250 121 | 221 122 | 31 123 | 124 | 125 | 126 | backgroundLabel 127 | nameLable 128 | ipLabel 129 | createPushButton 130 | nameLineEdit 131 | ipLineEdit 132 | headPortraitLabel 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "login.h" 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QApplication a(argc, argv); 8 | QTextCodec::setCodecForLocale(QTextCodec::codecForLocale()); 9 | login w; 10 | w.show(); 11 | return a.exec(); 12 | } 13 | -------------------------------------------------------------------------------- /mytitlebar.cpp: -------------------------------------------------------------------------------- 1 | #include "mytitlebar.h" 2 | #include "mytitlebar.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define BUTTON_HEIGHT 30 // 按钮高度; 9 | #define BUTTON_WIDTH 30 // 按钮宽度; 10 | #define TITLE_HEIGHT 30 // 标题栏高度; 11 | 12 | 13 | MyTitleBar::MyTitleBar(QWidget *parent) : QWidget(parent) 14 | , m_colorR(153) 15 | , m_colorG(153) 16 | , m_colorB(153) 17 | , m_isPressed(false) 18 | , m_buttonType(MIN_MAX_BUTTON) 19 | , m_windowBorderWidth(0) 20 | , m_isTransparent(false) 21 | { 22 | // 初始化; 23 | initControl(); 24 | initConnections(); 25 | // 加载本地样式 MyTitle.css文件; 26 | //loadStyleSheet("MyTitle"); 27 | } 28 | 29 | MyTitleBar::~MyTitleBar() 30 | { 31 | 32 | } 33 | 34 | // 初始化控件; 35 | void MyTitleBar::initControl() 36 | { 37 | m_pIcon = new QLabel; 38 | m_pTitleContent = new QLabel; 39 | 40 | m_pButtonMin = new QPushButton; 41 | m_pButtonRestore = new QPushButton; 42 | m_pButtonMax = new QPushButton; 43 | m_pButtonClose = new QPushButton; 44 | 45 | m_pButtonMin->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT)); 46 | m_pButtonRestore->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT)); 47 | m_pButtonMax->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT)); 48 | m_pButtonClose->setFixedSize(QSize(BUTTON_WIDTH, BUTTON_HEIGHT)); 49 | 50 | m_pTitleContent->setObjectName("TitleContent"); 51 | m_pButtonMin->setObjectName("ButtonMin"); 52 | m_pButtonRestore->setObjectName("ButtonRestore"); 53 | m_pButtonMax->setObjectName("ButtonMax"); 54 | m_pButtonClose->setObjectName("ButtonClose"); 55 | 56 | m_pButtonMin->setToolTip(QStringLiteral("最小化")); 57 | m_pButtonRestore->setToolTip(QStringLiteral("还原")); 58 | m_pButtonMax->setToolTip(QStringLiteral("最大化")); 59 | m_pButtonClose->setToolTip(QStringLiteral("关闭")); 60 | 61 | 62 | m_pButtonMax->setStyleSheet(tr("border-image: url(:/new/login/icon/max.png);")); 63 | m_pButtonMin->setStyleSheet(tr("border-image: url(:/new/login/icon/min.png);")); 64 | m_pButtonClose->setStyleSheet(tr("border-image: url(:/new/login/icon/close.png);")); 65 | 66 | QHBoxLayout* mylayout = new QHBoxLayout(this); 67 | mylayout->addWidget(m_pIcon); 68 | mylayout->addWidget(m_pTitleContent); 69 | 70 | mylayout->addWidget(m_pButtonMin); 71 | mylayout->addWidget(m_pButtonRestore); 72 | mylayout->addWidget(m_pButtonMax); 73 | mylayout->addWidget(m_pButtonClose); 74 | 75 | mylayout->setContentsMargins(5, 0, 0, 0); 76 | mylayout->setSpacing(0); 77 | 78 | m_pTitleContent->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 79 | this->setFixedHeight(TITLE_HEIGHT); 80 | this->setWindowFlags(Qt::FramelessWindowHint); 81 | } 82 | 83 | // 信号槽的绑定; 84 | void MyTitleBar::initConnections() 85 | { 86 | connect(m_pButtonMin, SIGNAL(clicked()), this, SLOT(onButtonMinClicked())); 87 | connect(m_pButtonRestore, SIGNAL(clicked()), this, SLOT(onButtonRestoreClicked())); 88 | connect(m_pButtonMax, SIGNAL(clicked()), this, SLOT(onButtonMaxClicked())); 89 | connect(m_pButtonClose, SIGNAL(clicked()), this, SLOT(onButtonCloseClicked())); 90 | } 91 | 92 | // 设置标题栏背景色,在paintEvent事件中进行绘制标题栏背景色; 93 | // 在构造函数中给了默认值,可以外部设置颜色值改变标题栏背景色; 94 | void MyTitleBar::setBackgroundColor(int r, int g, int b, bool isTransparent) 95 | { 96 | m_colorR = r; 97 | m_colorG = g; 98 | m_colorB = b; 99 | m_isTransparent = isTransparent; 100 | // 重新绘制(调用paintEvent事件); 101 | update(); 102 | } 103 | 104 | // 设置标题栏图标; 105 | void MyTitleBar::setTitleIcon(QString filePath, QSize IconSize) 106 | { 107 | QPixmap titleIcon(filePath); 108 | m_pIcon->setPixmap(titleIcon.scaled(IconSize)); 109 | } 110 | 111 | // 设置标题内容; 112 | void MyTitleBar::setTitleContent(QString titleContent, int titleFontSize) 113 | { 114 | // 设置标题字体大小; 115 | QFont font = m_pTitleContent->font(); 116 | font.setPointSize(titleFontSize); 117 | m_pTitleContent->setFont(font); 118 | // 设置标题内容; 119 | m_pTitleContent->setText(titleContent); 120 | m_titleContent = titleContent; 121 | } 122 | 123 | // 设置标题栏长度; 124 | void MyTitleBar::setTitleWidth(int width) 125 | { 126 | this->setFixedWidth(width); 127 | } 128 | 129 | // 设置标题栏上按钮类型; 130 | // 由于不同窗口标题栏上的按钮都不一样,所以可以自定义标题栏中的按钮; 131 | // 这里提供了四个按钮,分别为最小化、还原、最大化、关闭按钮,如果需要其他按钮可自行添加设置; 132 | void MyTitleBar::setButtonType(ButtonType buttonType) 133 | { 134 | m_buttonType = buttonType; 135 | 136 | switch (buttonType) 137 | { 138 | case MIN_BUTTON: 139 | { 140 | m_pButtonRestore->setVisible(false); 141 | m_pButtonMax->setVisible(false); 142 | } 143 | break; 144 | case MIN_MAX_BUTTON: 145 | { 146 | m_pButtonRestore->setVisible(false); 147 | } 148 | break; 149 | case ONLY_CLOSE_BUTTON: 150 | { 151 | m_pButtonMin->setVisible(false); 152 | m_pButtonRestore->setVisible(false); 153 | m_pButtonMax->setVisible(false); 154 | } 155 | break; 156 | default: 157 | break; 158 | } 159 | } 160 | 161 | // 设置标题栏中的标题是否会自动滚动,跑马灯的效果; 162 | // 一般情况下标题栏中的标题内容是不滚动的,但是既然自定义就看自己需要嘛,想怎么设计就怎么搞O(∩_∩)O! 163 | void MyTitleBar::setTitleRoll() 164 | { 165 | connect(&m_titleRollTimer, SIGNAL(timeout()), this, SLOT(onRollTitle())); 166 | m_titleRollTimer.start(200); 167 | } 168 | 169 | // 设置窗口边框宽度; 170 | void MyTitleBar::setWindowBorderWidth(int borderWidth) 171 | { 172 | m_windowBorderWidth = borderWidth; 173 | } 174 | 175 | // 保存窗口最大化前窗口的位置以及大小; 176 | void MyTitleBar::saveRestoreInfo(const QPoint point, const QSize size) 177 | { 178 | m_restorePos = point; 179 | m_restoreSize = size; 180 | } 181 | 182 | // 获取窗口最大化前窗口的位置以及大小; 183 | void MyTitleBar::getRestoreInfo(QPoint& point, QSize& size) 184 | { 185 | point = m_restorePos; 186 | size = m_restoreSize; 187 | } 188 | 189 | // 绘制标题栏背景色; 190 | void MyTitleBar::paintEvent(QPaintEvent *event) 191 | { 192 | // 是否设置标题透明; 193 | if (!m_isTransparent) 194 | { 195 | //设置背景色; 196 | QPainter painter(this); 197 | QPainterPath pathBack; 198 | pathBack.setFillRule(Qt::WindingFill); 199 | pathBack.addRoundedRect(QRect(0, 0, this->width(), this->height()), 3, 3); 200 | painter.setRenderHint(QPainter::Antialiasing, true); 201 | painter.fillPath(pathBack, QBrush(QColor(m_colorR, m_colorG, m_colorB))); 202 | } 203 | 204 | // 当窗口最大化或者还原后,窗口长度变了,标题栏的长度应当一起改变; 205 | // 这里减去m_windowBorderWidth ,是因为窗口可能设置了不同宽度的边框; 206 | // 如果窗口有边框则需要设置m_windowBorderWidth的值,否则m_windowBorderWidth默认为0; 207 | if (this->width() != (this->parentWidget()->width() - m_windowBorderWidth)) 208 | { 209 | this->setFixedWidth(this->parentWidget()->width() - m_windowBorderWidth); 210 | } 211 | QWidget::paintEvent(event); 212 | } 213 | 214 | // 双击响应事件,主要是实现双击标题栏进行最大化和最小化操作; 215 | void MyTitleBar::mouseDoubleClickEvent(QMouseEvent *event) 216 | { 217 | // 只有存在最大化、还原按钮时双击才有效; 218 | #if 0 219 | if (m_buttonType == MIN_MAX_BUTTON) 220 | { 221 | // 通过最大化按钮的状态判断当前窗口是处于最大化还是原始大小状态; 222 | // 或者通过单独设置变量来表示当前窗口状态; 223 | if (m_pButtonMax->isVisible()) 224 | { 225 | onButtonMaxClicked(); 226 | } 227 | else 228 | { 229 | onButtonRestoreClicked(); 230 | } 231 | } 232 | #endif 233 | 234 | return QWidget::mouseDoubleClickEvent(event); 235 | } 236 | 237 | // 以下通过mousePressEvent、mouseMoveEvent、mouseReleaseEvent三个事件实现了鼠标拖动标题栏移动窗口的效果; 238 | void MyTitleBar::mousePressEvent(QMouseEvent *event) 239 | { 240 | if (m_buttonType == MIN_MAX_BUTTON) 241 | { 242 | // 在窗口最大化时禁止拖动窗口; 243 | if (m_pButtonMax->isVisible()) 244 | { 245 | m_isPressed = true; 246 | m_startMovePos = event->globalPos(); 247 | } 248 | } 249 | else 250 | { 251 | m_isPressed = true; 252 | m_startMovePos = event->globalPos(); 253 | } 254 | 255 | return QWidget::mousePressEvent(event); 256 | } 257 | 258 | void MyTitleBar::mouseMoveEvent(QMouseEvent *event) 259 | { 260 | if (m_isPressed) 261 | { 262 | QPoint movePoint = event->globalPos() - m_startMovePos; 263 | QPoint widgetPos = this->parentWidget()->pos(); 264 | m_startMovePos = event->globalPos(); 265 | this->parentWidget()->move(widgetPos.x() + movePoint.x(), widgetPos.y() + movePoint.y()); 266 | } 267 | return QWidget::mouseMoveEvent(event); 268 | } 269 | 270 | void MyTitleBar::mouseReleaseEvent(QMouseEvent *event) 271 | { 272 | m_isPressed = false; 273 | return QWidget::mouseReleaseEvent(event); 274 | } 275 | 276 | #if 0 277 | // 加载本地样式文件; 278 | // 可以将样式直接写在文件中,程序运行时直接加载进来; 279 | void MyTitleBar::loadStyleSheet(const QString &sheetName) 280 | { 281 | 282 | 283 | QFile file(":/Resources/" + sheetName + ".css"); 284 | file.open(QFile::ReadOnly); 285 | if (file.isOpen()) 286 | { 287 | QString styleSheet = this->styleSheet(); 288 | styleSheet += QLatin1String(file.readAll()); 289 | this->setStyleSheet(styleSheet); 290 | } 291 | } 292 | #endif 293 | 294 | // 以下为按钮操作响应的槽; 295 | void MyTitleBar::onButtonMinClicked() 296 | { 297 | emit signalButtonMinClicked(); 298 | } 299 | 300 | void MyTitleBar::onButtonRestoreClicked() 301 | { 302 | m_pButtonRestore->setVisible(false); 303 | m_pButtonMax->setVisible(true); 304 | emit signalButtonRestoreClicked(); 305 | } 306 | 307 | void MyTitleBar::onButtonMaxClicked() 308 | { 309 | #if 0 310 | m_pButtonMax->setVisible(false); 311 | m_pButtonRestore->setVisible(true); 312 | emit signalButtonMaxClicked(); 313 | #endif 314 | } 315 | 316 | void MyTitleBar::onButtonCloseClicked() 317 | { 318 | emit signalButtonCloseClicked(); 319 | } 320 | 321 | // 该方法主要是让标题栏中的标题显示为滚动的效果; 322 | void MyTitleBar::onRollTitle() 323 | { 324 | static int nPos = 0; 325 | QString titleContent = m_titleContent; 326 | // 当截取的位置比字符串长时,从头开始; 327 | if (nPos > titleContent.length()) 328 | nPos = 0; 329 | 330 | m_pTitleContent->setText(titleContent.mid(nPos)); 331 | nPos++; 332 | } 333 | -------------------------------------------------------------------------------- /mytitlebar.h: -------------------------------------------------------------------------------- 1 | #ifndef MYTITLEBAR_H 2 | #define MYTITLEBAR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* 10 | * 自定义标题栏按钮宽度属性 11 | */ 12 | 13 | enum ButtonType 14 | { 15 | MIN_BUTTON = 0, // 最小化和关闭按钮; 16 | MIN_MAX_BUTTON , // 最小化、最大化和关闭按钮; 17 | ONLY_CLOSE_BUTTON // 只有关闭按钮; 18 | }; 19 | 20 | 21 | class MyTitleBar : public QWidget 22 | { 23 | Q_OBJECT 24 | public: 25 | explicit MyTitleBar(QWidget *parent = nullptr); 26 | //这里parent没有给默认值NULL,保证在创建MyTitleBar对象时父指针必须得赋值;且赋值不为NULL; 27 | ~MyTitleBar(); 28 | 29 | // 设置标题栏背景色及是否设置标题栏背景色透明; 30 | void setBackgroundColor(int r, int g, int b , bool isTransparent = false); 31 | // 设置标题栏图标; 32 | void setTitleIcon(QString filePath , QSize IconSize = QSize(25 , 25)); 33 | // 设置标题内容; 34 | void setTitleContent(QString titleContent , int titleFontSize = 9); 35 | // 设置标题栏长度; 36 | void setTitleWidth(int width); 37 | // 设置标题栏上按钮类型; 38 | void setButtonType(ButtonType buttonType); 39 | // 设置标题栏中的标题是否会滚动;具体可以看效果; 40 | void setTitleRoll(); 41 | // 设置窗口边框宽度; 42 | void setWindowBorderWidth(int borderWidth); 43 | 44 | // 保存/获取 最大化前窗口的位置及大小; 45 | void saveRestoreInfo(const QPoint point, const QSize size); 46 | void getRestoreInfo(QPoint& point, QSize& size); 47 | 48 | private: 49 | void paintEvent(QPaintEvent *event); 50 | void mouseDoubleClickEvent(QMouseEvent *event); 51 | void mousePressEvent(QMouseEvent *event); 52 | void mouseMoveEvent(QMouseEvent *event); 53 | void mouseReleaseEvent(QMouseEvent *event); 54 | 55 | // 初始化控件; 56 | void initControl(); 57 | // 信号槽的绑定; 58 | void initConnections(); 59 | // 加载样式文件; 60 | //void loadStyleSheet(const QString &sheetName); 61 | 62 | signals: 63 | // 按钮触发的信号; 64 | void signalButtonMinClicked(); 65 | void signalButtonRestoreClicked(); 66 | void signalButtonMaxClicked(); 67 | void signalButtonCloseClicked(); 68 | 69 | private slots: 70 | // 按钮触发的槽; 71 | void onButtonMinClicked(); 72 | void onButtonRestoreClicked(); 73 | void onButtonMaxClicked(); 74 | void onButtonCloseClicked(); 75 | void onRollTitle(); 76 | 77 | private: 78 | QLabel* m_pIcon; // 标题栏图标; 79 | QLabel* m_pTitleContent; // 标题栏内容; 80 | QPushButton* m_pButtonMin; // 最小化按钮; 81 | QPushButton* m_pButtonRestore; // 最大化还原按钮; 82 | QPushButton* m_pButtonMax; // 最大化按钮; 83 | QPushButton* m_pButtonClose; // 关闭按钮; 84 | 85 | // 标题栏背景色; 86 | int m_colorR; 87 | int m_colorG; 88 | int m_colorB; 89 | 90 | // 最大化,最小化变量; 91 | QPoint m_restorePos; 92 | QSize m_restoreSize; 93 | // 移动窗口的变量; 94 | bool m_isPressed; 95 | QPoint m_startMovePos; 96 | // 标题栏跑马灯效果时钟; 97 | QTimer m_titleRollTimer; 98 | // 标题栏内容; 99 | QString m_titleContent; 100 | // 按钮类型; 101 | ButtonType m_buttonType; 102 | // 窗口边框宽度; 103 | int m_windowBorderWidth; 104 | // 标题栏是否透明; 105 | bool m_isTransparent; 106 | 107 | 108 | }; 109 | 110 | #endif // MYTITLEBAR_H 111 | -------------------------------------------------------------------------------- /tcpclient.cpp: -------------------------------------------------------------------------------- 1 | #include "tcpclient.h" 2 | #include "ui_tcpclient.h" 3 | #include 4 | #include 5 | 6 | TcpClient::TcpClient(QWidget *parent) : 7 | QDialog(parent), 8 | ui(new Ui::TcpClient) 9 | { 10 | ui->setupUi(this); 11 | //设置界面大小 12 | setFixedSize(350,180); 13 | 14 | //初始化 15 | TotalBytes = 0; 16 | bytesReceive = 0; 17 | fileNameSize = 0; 18 | tcpClient = new QTcpSocket(this); 19 | tcpPort = 6666; 20 | 21 | connect(tcpClient,SIGNAL(readyRead()),this,SLOT(readMessage())); 22 | connect(tcpClient,SIGNAL(error(QAbstractSocket::SocketError)), 23 | this,SLOT(displayError(QAbstractSocket::SocketError))); 24 | } 25 | 26 | TcpClient::~TcpClient() 27 | { 28 | delete ui; 29 | } 30 | 31 | //获取发送端的IP地址 32 | void TcpClient::setHostAddress(QHostAddress address) 33 | { 34 | hostAddress = address; 35 | newConnect(); 36 | } 37 | 38 | //文件保存路径 39 | void TcpClient::setFileName(QString fileName) 40 | { 41 | localFile = new QFile(fileName); 42 | } 43 | 44 | //关闭事件 45 | void TcpClient::closeEvent(QCloseEvent *) 46 | { 47 | on_tcpClientCloseBtn_clicked(); 48 | } 49 | 50 | //与服务端连接 51 | void TcpClient::newConnect() 52 | { 53 | blockSize = 0; 54 | tcpClient->abort(); 55 | tcpClient->connectToHost(hostAddress,tcpPort); 56 | time.start(); 57 | } 58 | 59 | //接收文件、数据 60 | void TcpClient::readMessage() 61 | { 62 | QDataStream in(tcpClient); 63 | in.setVersion(QDataStream::Qt_5_6); 64 | 65 | float useTime = time.elapsed(); 66 | 67 | qDebug() << bytesReceive << endl; 68 | 69 | if(bytesReceive <= sizeof(qint64)*2) { 70 | if((tcpClient->bytesAvailable()>=sizeof(qint64) * 2) && (fileNameSize == 0)) 71 | { 72 | in >> TotalBytes >> fileNameSize; 73 | bytesReceive += sizeof(qint64) * 2; 74 | } 75 | if((tcpClient->bytesAvailable() >= fileNameSize) && (fileNameSize != 0)) { 76 | in >> fileName; 77 | bytesReceive += fileNameSize; 78 | if(!localFile->open(QFile::WriteOnly)) { 79 | QMessageBox::warning(this,tr("应用程序"),tr("无法读取文件 %1:\n %2") 80 | .arg(fileName).arg(localFile->errorString())); 81 | return; 82 | } 83 | 84 | } 85 | else 86 | { 87 | return; 88 | } 89 | } 90 | 91 | if(bytesToReceive < TotalBytes) { 92 | bytesReceive += tcpClient->bytesAvailable(); 93 | inBlock = tcpClient->readAll(); 94 | localFile->write(inBlock); 95 | inBlock.resize(0); 96 | } 97 | 98 | ui->progressBar->setMaximum(TotalBytes); 99 | ui->progressBar->setValue(bytesReceive); 100 | double speed = bytesReceive / useTime; 101 | ui->tcpClientStatuslabel->setText(tr("已接收 %1 MB (%2MB/s)" 102 | "\n共%3MB.已用时:%4秒\n估计剩余时间:%5秒") 103 | .arg(bytesReceive / (1024*1024)) 104 | .arg(speed * 1000/(1024 * 1024),0,'f',2) 105 | .arg(TotalBytes / (1024*1024)) 106 | .arg(useTime/1000,0,'f',0) 107 | .arg(TotalBytes/speed/1000 - useTime/1000,0,'f',0)); 108 | if(bytesReceive == TotalBytes) { 109 | localFile->close(); 110 | tcpClient->close(); 111 | ui->tcpClientStatuslabel->setText(tr("接收文件%1完毕").arg(fileName)); 112 | } 113 | } 114 | 115 | //显示错误 116 | void TcpClient::displayError(QAbstractSocket::SocketError socketError) 117 | { 118 | switch(socketError) 119 | { 120 | case QAbstractSocket::RemoteHostClosedError: break; 121 | default : 122 | qDebug() << tcpClient->errorString(); 123 | } 124 | } 125 | 126 | //取消按钮 127 | void TcpClient::on_tcpClientBtn_clicked() 128 | { 129 | tcpClient->abort(); 130 | if(localFile->isOpen()) 131 | localFile->close(); 132 | } 133 | 134 | //关闭按钮 135 | void TcpClient::on_tcpClientCloseBtn_clicked() 136 | { 137 | tcpClient->abort(); 138 | if(localFile->isOpen()) 139 | localFile->close(); 140 | close(); 141 | } 142 | 143 | -------------------------------------------------------------------------------- /tcpclient.h: -------------------------------------------------------------------------------- 1 | #ifndef TCPCLIENT_H 2 | #define TCPCLIENT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* 10 | * 文件传输客户端 11 | */ 12 | 13 | class QTcpSocket; 14 | 15 | namespace Ui { 16 | class TcpClient; 17 | } 18 | 19 | class TcpClient : public QDialog 20 | { 21 | Q_OBJECT 22 | 23 | public: 24 | explicit TcpClient(QWidget *parent = 0); 25 | ~TcpClient(); 26 | 27 | //设置主机地址 28 | void setHostAddress(QHostAddress address); 29 | //设置要传输的文件名 30 | void setFileName(QString fileName); 31 | 32 | protected: 33 | //关闭事件 34 | void closeEvent(QCloseEvent *); 35 | 36 | private: 37 | Ui::TcpClient *ui; 38 | 39 | QTcpSocket *tcpClient; 40 | quint16 blockSize; 41 | QHostAddress hostAddress; 42 | qint16 tcpPort; 43 | 44 | qint64 TotalBytes; 45 | qint64 bytesReceive; 46 | qint64 bytesToReceive; 47 | qint64 fileNameSize; 48 | QString fileName; 49 | QFile *localFile; 50 | QByteArray inBlock; 51 | 52 | QTime time; 53 | 54 | private slots: 55 | //与服务端连接 56 | void newConnect(); 57 | //保存接收文件 58 | void readMessage(); 59 | //显示错误 60 | void displayError(QAbstractSocket::SocketError); 61 | //取消按钮--槽 62 | void on_tcpClientBtn_clicked(); 63 | //关闭按钮--槽 64 | void on_tcpClientCloseBtn_clicked(); 65 | }; 66 | 67 | #endif // TCPCLIENT_H 68 | -------------------------------------------------------------------------------- /tcpclient.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | TcpClient 4 | 5 | 6 | 7 | 0 8 | 0 9 | 350 10 | 180 11 | 12 | 13 | 14 | 接收端 15 | 16 | 17 | 18 | 19 | 20 20 | 9 21 | 321 22 | 61 23 | 24 | 25 | 26 | 27 | 12 28 | 75 29 | true 30 | 31 | 32 | 33 | 等待接收文件...... 34 | 35 | 36 | Qt::AlignCenter 37 | 38 | 39 | 40 | 41 | 42 | 30 43 | 80 44 | 331 45 | 23 46 | 47 | 48 | 49 | 0 50 | 51 | 52 | 53 | 54 | 55 | 70 56 | 120 57 | 75 58 | 23 59 | 60 | 61 | 62 | 取消 63 | 64 | 65 | 66 | 67 | 68 | 200 69 | 120 70 | 75 71 | 23 72 | 73 | 74 | 75 | 关闭 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /tcpserver.cpp: -------------------------------------------------------------------------------- 1 | #include "tcpserver.h" 2 | #include "ui_tcpserver.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | TcpServer::TcpServer(QWidget *parent) : 11 | QDialog(parent), 12 | ui(new Ui::TcpServer) 13 | { 14 | ui->setupUi(this); 15 | 16 | //设置界面大小 17 | setFixedSize(350,180); 18 | 19 | tcpPort = 6666; 20 | tcpServer = new QTcpServer(this); 21 | connect(tcpServer,SIGNAL(newConnection()),this,SLOT(sendMessage())); 22 | 23 | initServer(); 24 | } 25 | 26 | TcpServer::~TcpServer() 27 | { 28 | delete ui; 29 | } 30 | 31 | //初始化 32 | void TcpServer::initServer() 33 | { 34 | payloadSize = 64*1024; 35 | totalBytes = 0; 36 | bytesWritten = 0; 37 | bytesToWrite = 0; 38 | ui->serverStatuslabel->setText(tr("请选择要传送的文件")); 39 | ui->progressBar->reset(); 40 | //设置按钮状态 41 | ui->serverOpenBtn->setEnabled(true); 42 | ui->serverSendBtn->setEnabled(false); 43 | 44 | tcpServer->close(); 45 | } 46 | 47 | //接收端拒绝接收 48 | void TcpServer::refused() 49 | { 50 | //关闭文件传输服务 51 | tcpServer->close(); 52 | ui->serverStatuslabel->setText(tr("对方拒绝接收")); 53 | } 54 | 55 | //关闭事件 56 | void TcpServer::closeEvent(QCloseEvent *) 57 | { 58 | on_serverCloseBtn_clicked(); 59 | } 60 | 61 | //发送数据 62 | void TcpServer::sendMessage() 63 | { 64 | ui->serverSendBtn->setEnabled(false); 65 | clientConnection = tcpServer->nextPendingConnection(); 66 | connect(clientConnection,SIGNAL(bytesWritten(qint64)), 67 | this,SLOT(updateClientProgress(qint64))); 68 | ui->serverStatuslabel->setText(tr("开始传送文件 %1 !").arg(theFileName)); 69 | 70 | //打开文件 71 | localFile = new QFile(fileName); 72 | if(!localFile->open((QFile::ReadOnly))) 73 | { 74 | QMessageBox::warning(this,tr("应用程序"),tr("无法读取文件 %1:\n %2") 75 | .arg(fileName).arg(localFile->errorString())); 76 | return; 77 | } 78 | totalBytes = localFile->size(); 79 | 80 | QDataStream sendOut(&outBlock,QIODevice::WriteOnly); 81 | sendOut.setVersion(QDataStream::Qt_5_4); 82 | time.start(); //启动计时器 83 | QString currentFile = fileName.right(fileName.size() 84 | - fileName.lastIndexOf('/')-1); 85 | sendOut << qint64(0) << qint64(0) << currentFile; 86 | totalBytes += outBlock.size(); 87 | sendOut.device()->seek(0); 88 | sendOut << totalBytes << qint64((outBlock.size() - sizeof(qint64) * 2)); 89 | bytesToWrite = totalBytes - clientConnection->write(outBlock); 90 | outBlock.resize(0); 91 | } 92 | 93 | //更新进度条--槽 94 | void TcpServer::updateClientProgress(qint64 numBytes) 95 | { 96 | qApp->processEvents(); //使界面不会冻结 97 | bytesWritten += (int)numBytes; 98 | if(bytesWritten > 0) { 99 | outBlock = localFile->read(qMin(bytesToWrite,payloadSize)); 100 | bytesToWrite -= (int)clientConnection->write(outBlock); 101 | outBlock.resize(0); 102 | } 103 | else 104 | { 105 | localFile->close(); 106 | } 107 | ui->progressBar->setMaximum(totalBytes); 108 | ui->progressBar->setValue(bytesWritten); 109 | 110 | float userTime = time.elapsed(); //获取耗费的时间 111 | double speed = bytesWritten / userTime; 112 | ui->serverStatuslabel->setText(tr("已发送 %1MB(%2MB/s)" 113 | "\n共%3MB 已用时:%4秒\n 估计剩余时间:%5秒") 114 | .arg(bytesWritten / (1024*1024)) 115 | .arg(speed * 1000 / (1024*1024),0,'f',2) 116 | .arg(totalBytes / (1024*1024)) 117 | .arg(userTime/1000,0,'f',0)); 118 | 119 | if(bytesToWrite == totalBytes) { 120 | localFile->close(); 121 | tcpServer->close(); 122 | ui->serverStatuslabel->setText(tr("传送文件 %1 成功") 123 | .arg(theFileName)); 124 | } 125 | } 126 | 127 | //打开按钮--槽 128 | void TcpServer::on_serverOpenBtn_clicked() 129 | { 130 | //文件对话框 131 | fileName = QFileDialog::getOpenFileName(this); 132 | if(!fileName.isEmpty()) 133 | { 134 | theFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/') - 1); 135 | ui->serverStatuslabel->setText(tr("要传送的文件为:%1").arg(theFileName)); 136 | ui->serverSendBtn->setEnabled(true); 137 | ui->serverOpenBtn->setEnabled(false); 138 | } 139 | } 140 | 141 | //发送按钮--槽 142 | void TcpServer::on_serverSendBtn_clicked() 143 | { 144 | if(!tcpServer->listen(QHostAddress::Any,tcpPort)) 145 | { 146 | qDebug() << tcpServer->errorString(); 147 | close(); 148 | return; 149 | } 150 | ui->serverStatuslabel->setText(tr("等待对方接收... ...")); 151 | ui->serverSendBtn->setEnabled(false); 152 | emit sendFileName(theFileName); 153 | } 154 | 155 | //关闭按钮--槽 156 | void TcpServer::on_serverCloseBtn_clicked() 157 | { 158 | if(tcpServer->isListening()) 159 | { 160 | tcpServer->close(); 161 | if(localFile->isOpen()) 162 | localFile->close(); 163 | clientConnection->abort(); 164 | } 165 | close(); 166 | } 167 | -------------------------------------------------------------------------------- /tcpserver.h: -------------------------------------------------------------------------------- 1 | #ifndef TCPSERVER_H 2 | #define TCPSERVER_H 3 | 4 | #include 5 | #include 6 | 7 | 8 | /* 9 | * 文件传输服务端 10 | */ 11 | 12 | namespace Ui { 13 | class TcpServer; 14 | } 15 | 16 | class QTcpServer; 17 | class QTcpSocket; 18 | class QFile; 19 | 20 | class TcpServer : public QDialog 21 | { 22 | Q_OBJECT 23 | 24 | public: 25 | explicit TcpServer(QWidget *parent = 0); 26 | ~TcpServer(); 27 | 28 | //用于初始化 29 | void initServer(); 30 | //拒绝接收文件 31 | void refused(); 32 | 33 | protected: 34 | //关闭事件 35 | void closeEvent(QCloseEvent *); 36 | 37 | private: 38 | Ui::TcpServer *ui; 39 | qint16 tcpPort; 40 | QTcpServer *tcpServer; 41 | QString fileName; 42 | QString theFileName; 43 | QFile *localFile; 44 | qint64 payloadSize; 45 | qint64 totalBytes; 46 | qint64 bytesWritten; 47 | qint64 bytesToWrite; 48 | QByteArray outBlock; 49 | 50 | QTcpSocket *clientConnection; 51 | 52 | QTime time; 53 | 54 | private slots: 55 | //发送数据 56 | void sendMessage(); 57 | //更新进度条--槽 58 | void updateClientProgress(qint64 numBytes); 59 | //打开按钮--槽 60 | void on_serverOpenBtn_clicked(); 61 | //发送按钮--槽 62 | void on_serverSendBtn_clicked(); 63 | //关闭按钮--槽 64 | void on_serverCloseBtn_clicked(); 65 | 66 | signals: 67 | void sendFileName(QString fileName); 68 | }; 69 | 70 | #endif // TCPSERVER_H 71 | -------------------------------------------------------------------------------- /tcpserver.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | TcpServer 4 | 5 | 6 | 7 | 0 8 | 0 9 | 350 10 | 180 11 | 12 | 13 | 14 | 发送端 15 | 16 | 17 | 18 | 19 | 19 20 | 9 21 | 311 22 | 61 23 | 24 | 25 | 26 | 27 | Agency FB 28 | 12 29 | 75 30 | true 31 | 32 | 33 | 34 | 请选择要发送的文件: 35 | 36 | 37 | Qt::AlignCenter 38 | 39 | 40 | 41 | 42 | 43 | 40 44 | 90 45 | 271 46 | 20 47 | 48 | 49 | 50 | 0 51 | 52 | 53 | Qt::AlignCenter 54 | 55 | 56 | 57 | 58 | 59 | 30 60 | 150 61 | 75 62 | 23 63 | 64 | 65 | 66 | 打开 67 | 68 | 69 | 70 | 71 | 72 | 130 73 | 150 74 | 75 75 | 23 76 | 77 | 78 | 79 | 发送 80 | 81 | 82 | 83 | 84 | 85 | 240 86 | 150 87 | 75 88 | 23 89 | 90 | 91 | 92 | 关闭 93 | 94 | 95 | 96 | 97 | 98 | 99 | --------------------------------------------------------------------------------