博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
QT开发之旅-Udp聊天室编程
阅读量:6676 次
发布时间:2019-06-25

本文共 13709 字,大约阅读时间需要 45 分钟。

一、概要设计

  登录对话框(继承自QDialog类)进行用户登录查询数据库用户是否存在,注册插入数据到用户表。用户表字段:

(chatid int primary key, passwd varchar(30), name varchar(30), email varchar(30), history int)

 显示好友列表(继承自QWidget类),窗体间数据传递,显示登录用户头像及昵称。轮询数据库用户表显示好友列表。点击好友跳出聊天窗口(继承自MainWindow类),窗体间数据传递,显示好友昵称,实现多用户聊天,支持中文通信,设置快捷键,保存聊天记录。

关键字:数据库、窗体传值、中文通信、udp协议、快捷键、文件操作。

二、详细设计

  1.登录对话框:

  数据库操作:

  (1)创建数据库和数据表

// 设置参数    QString select_table = "select tbl_name name from sqlite_master where type = 'table'";    QString create_sql = "create table user (chatid int primary key, passwd varchar(30), name varchar(30), email varchar(30), history int)";    QString select_max_sql = "select max(chatid) from user";    QString insert_sql = "insert into user values (?, ?, ?, ? ?)";    //QString update_sql = "update user set name = :name where chatid = :chatid";    QString select_sql = "select name from user";    //QString select_all_sql = "select * from user";    //QString delete_sql = "delete from user where chatid = ?";    //QString clear_sql = "delete from user";    QString select_nameInfo = "selcet * from user where name=";database = QSqlDatabase::addDatabase("QSQLITE");    database.setDatabaseName("database.db");    //打开数据库    if(!database.open())    {        qDebug()<

   (2)注册验证及插入数据

if(ui->passwd1LineEdit->text()==""||ui->passwd2LineEdit->text()=="")    {        passwdFlag=false;    }    else if(ui->passwd1LineEdit->text()==ui->passwd2LineEdit->text())    //两次密码相同    {        //newpasswd=ui->passwd1LineEdit->text();        passwdFlag=true;    }    else    {        QMessageBox::information(this, QString::fromLocal8Bit("提示"), QStringLiteral("密码输入不一致!"));        qDebug()<<"passwd err";        passwdFlag=false;        //return;    }    //以下为数据库的操作    QSqlQuery sql_query;    //查询最大id    max_id = 0;    sql_query.prepare(select_max_sql);    if(!sql_query.exec())    {        qDebug()<
nameLineEdit->text()==name) //用户名已经存在 { QMessageBox::information(this, QString::fromLocal8Bit("提示"), QStringLiteral("用户名已存在!")); qDebug()<<"name existed"; nameFlag=false; break; } else { //newname=ui->nameLineEdit->text(); nameFlag=true; } } else { //name列为空 nameFlag=true; break; } } } newchatid=max_id+1; if(nameFlag==true) newname=ui->nameLineEdit->text(); else return; if(passwdFlag==true) newpasswd=ui->passwd1LineEdit->text(); else return; //插入数据 sql_query.prepare(insert_sql); sql_query.addBindValue(newchatid); //chatid sql_query.addBindValue(newpasswd); //passwd sql_query.addBindValue(newname); //name sql_query.addBindValue(newemail); //email sql_query.addBindValue(0); //history if(!sql_query.exec()) { qDebug()<

   登录界面:

  (1)登录验证

if(matchFlag==false)    {        //用户名错误        QMessageBox::warning(this, tr("警告"), tr("用户不存在!"), QMessageBox::Yes);        this->ui->et_username->clear();        this->ui->et_pwd->clear();        this->ui->et_username->setFocus();    }    else    {        if(usr_passwd!=ui->et_pwd->text())        {            //密码错误            QMessageBox::warning(this, tr("警告"), tr("用户不存在!"), QMessageBox::Yes);            this->ui->et_username->clear();            this->ui->et_pwd->clear();            this->ui->et_username->setFocus();        }        else        {            //用户名和密码均正确//            ChatWindow cw(this);//            this->hide();//            cw.show();//            cw.exec();//            this->close();            LoginDialog::NICKNAME = usr_name;            accept();        }

  (2)用户头像

QSqlQuery sql_query;        //变量必须在成功打开数据库后定义才有效    //查询部分数据(name)    QString tempstring="select * from user where name='"+name+"'";    qDebug()<
userPic->width(),ui->userPic->height())); ui->userPic->setPixmap(pic); } else { QPixmap pic; ui->userPic->setPixmap(pic); }

  2.好友列表:

  数据库查询及列表显示:

database = QSqlDatabase::addDatabase("QSQLITE");    database.setDatabaseName("database.db");    QSqlQuery sql_query;        //改变量必须在成功打开数据库后定义才有效    //打开数据库    if(!database.open())    {        qDebug()<
listWidget->setMouseTracking(true); if(!query.exec(execstring)) { qDebug()<
listWidget); itemWidget->setText(QPixmap(QString(":/images/face%1").arg(usr_id)).scaled(80, 80), QString("%1").arg(usr_name), QString("127.0.0.1:800%1").arg(usr_id)); QListWidgetItem *listItem = new QListWidgetItem(ui->listWidget); // 此处的size如果不设置,界面被压缩了看不出ItemWidget的效果,高度一定要设置 listItem->setSizeHint(QSize(200, 70)); ui->listWidget->setItemWidget(listItem, itemWidget); } } } QObject::connect(ui->listWidget,SIGNAL(itemClicked(QListWidgetItem*)),this,SLOT(conChat(QListWidgetItem*))); qDebug()<<"LoginWidget:"+LoginDialog::NICKNAME; ui->nickname->setText(LoginDialog::NICKNAME); QString tempstring="select * from user where name='"+LoginDialog::NICKNAME+"'"; qDebug()<

   登录用户信息设置:

QString path=":/images/facex.jpg";        QString diff="face"+QString::number(usr_id);        path.replace("facex",diff);        qDebug()<
userPic->width(),ui->userPic->height())); ui->userPic->setPixmap(pic); ui->ipaddress->setText(QString("127.0.0.1:800%1").arg(usr_id));

   列表项:

labelIcon = new QLabel(this);    labelName = new QLabel(this);    labelName->setStyleSheet("QLabel{color: green; font: 23pt bold;}");    labelInfo = new QLabel(this);    labelInfo->setStyleSheet("QLabel{color: gray;}");    verlayout = new QVBoxLayout();    verlayout->setContentsMargins(0, 0, 0, 0);    verlayout->addWidget(labelName);    verlayout->addWidget(labelInfo);    horLayout = new QHBoxLayout(this);    horLayout->setContentsMargins(2, 2, 2, 2);    horLayout->addWidget(labelIcon, 1, Qt::AlignTop);    horLayout->addLayout(verlayout, 4);

   悬浮窗口:

this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint);    this->resize(200, 100); ;    this->setObjectName("CToolTip");    this->setStyleSheet("QWidget#CToolTip {border: 2px solid green; background-color: skyblue;}");    groupBox = new QGroupBox(this);    groupBox->setGeometry(10, 10, 180, 80);    groupBox->setTitle("用户信息");    labelIcon = new QLabel(groupBox);    labelName = new QLabel(groupBox);    labelInfo = new QLabel(groupBox);    verlayout = new QVBoxLayout();    verlayout->setContentsMargins(0, 0, 0, 0);    verlayout->addWidget(labelName);    verlayout->addWidget(labelInfo);    horLayout = new QHBoxLayout(groupBox);    horLayout->setContentsMargins(10, 10, 10, 10);    horLayout->addWidget(labelIcon, 1, Qt::AlignTop);    horLayout->addLayout(verlayout, 4);

  选择列表项:

QObject::connect(ui->listWidget,SIGNAL(itemClicked(QListWidgetItem*)),this,SLOT(conChat(QListWidgetItem*)));void LoginWidget::conChat(QListWidgetItem*){    qDebug()<
listWidget->currentRow()+1; chatWindow=new ChatWindow; chatWindow->show();}

  3.聊天窗口:

  数据库查询、信息设置及绑定端口号:

database = QSqlDatabase::addDatabase("QSQLITE");    database.setDatabaseName("database.db");    QSqlQuery chat_query;        //变量必须在成功打开数据库后定义才有效    //打开数据库    if(!database.open())    {        qDebug()<
name->setText(usr_name); } }

   UDP接收端:

senderAno = new QUdpSocket(this);        receiver = new QUdpSocket(this);        receiver->bind(port, QUdpSocket::ShareAddress);        connect(receiver, &QUdpSocket::readyRead, this, &ChatWindow::processPendingDatagram);void ChatWindow::processPendingDatagram(){    //中文支持//    QTextCodec *codec = QTextCodec::codecForName("GBK");    // 拥有等待的数据报    while(receiver->hasPendingDatagrams())    {        QDateTime time = QDateTime::currentDateTime();//获取系统现在的时间        QString str = time.toString("yyyy-MM-dd hh:mm:ss ddd"); //设置显示格式        QByteArray datagram;        // 让datagram的大小为等待处理的数据报的大小,这样才能接收到完整的数据        datagram.resize(receiver->pendingDatagramSize());        // 接收数据报,将其存放到datagram中        receiver->readDatagram(datagram.data(), datagram.size());        //ui->label->setText(datagram);        ui->listWidget->addItem(str);        chat += str+"\n";        qDebug()<
<
listWidget->addItem(usr_name+":"+QString::fromLocal8Bit(datagram)); chat += usr_name+":"+datagram+"\n"; }}

   UDP发送端:

//中文支持//    QTextCodec *codec = QTextCodec::codecForName("GBK");    QDateTime time = QDateTime::currentDateTime();//获取系统现在的时间    QString str = time.toString("yyyy-MM-dd hh:mm:ss ddd"); //设置显示格式    int port = LoginWidget::ID+8000;    qDebug()<
textEdit->toPlainText().toUtf8(); senderAno->writeDatagram(datagram.data(), datagram.size(), QHostAddress("127.0.0.1"), port); ui->listWidget->addItem(str); chat += str+"\n"; qDebug()<
textEdit->toPlainText()<
textEdit->toPlainText().toUtf8()); ui->listWidget->addItem("me:"+ui->textEdit->toPlainText()); chat += "me:"+ui->textEdit->toPlainText()+"\n"; this->ui->textEdit->clear(); this->ui->textEdit->setFocus();

   快捷键设置:

ui->pushButton->setShortcut(tr("ctrl+return"));        ui->pushButton_3->setShortcut(tr("alt+c"));        ui->pushButton_8->setShortcut(tr("ctrl+s"));

   文件操作:

/*     * 通过QFile实现数据操作     */    qDebug()<

   4.窗体传值:

  使用QT中的Signal&Slot机制进行传值:

  QT中的Signal&Slot机制相比于MFC中的消息机制简单了许多,它保证了任何对象之间均可以通过这种方式进行通信,甚至可以得到消息的sender。这里就拿一个简单的窗体间传值作为例子。

      首先看一下主窗体MainWindow:

      在设计器中拖拽一个Label和一个TextEdit控件到界面上,TextEdit用于显示传递过来的数据。

  创建一个右下有两个按键的对话框,放置一个Label和一个LineEdit。

  下面就是编码的操作了,我们需要在Dialog中声明一个信号,当用户点击OK时传递LineEdit中的内容到mainWindow中,具体的dialog.h代码为:

#ifndef DIALOG_H      #define DIALOG_H            #include 
namespace Ui { class Dialog; } class Dialog : public QDialog { Q_OBJECT public: explicit Dialog(QWidget *parent = 0); ~Dialog(); private: Ui::Dialog *ui; signals: void sendData(QString); private slots: void on_buttonBox_accepted(); }; #endif // DIALOG_H

   其中的signals:void sendData(QString)便是我们需要的信号函数,同时声明了一个槽函数

       void on_buttonBox_accepted();用于相应确定按钮的click事件。下面就是需要在该函数中产生一个信号。代码如下:

 

void Dialog::on_buttonBox_accepted()      {          emit sendData(ui->lineEdit->text());      }

 

   代码异乎寻常的简单,只需要用emit的方式调用sendData函数,将需要的参数传递进去即可。而MainWindow中则需要声明接收的槽函数,注意槽函数参数只能与信号函数少或相等,而不能多于信号函数参数个数。在MainWindow的头文件中声明槽函数:

private slots:          void receiveData(QString data);

   为了便于测试,我只在MainWindow的构造函数中创建了一个Dialog对象,并连接了信号和槽,具体为:

MainWindow::MainWindow(QWidget *parent) :          QMainWindow(parent),          ui(new Ui::MainWindow)      {          ui->setupUi(this);          //信号槽方式下父子窗体传值的测试          Dialog *dlg = new Dialog;          //关联信号和槽函数          connect(dlg,SIGNAL(sendData(QString)),this,SLOT(receiveData(QString)));         // dlg->setModal(true); 不论是模态或者非模态都可以正常传值          dlg->show();      }

   这里,我没有将父窗口的指针传递到Dialog中,如new Dialog(this),这种方式下,实际上可以归结到第三类传值方式中去。因为此时,可以使用MainWindow中的父窗口的函数进行数据的赋值和操作。

      这里,可能还有一个问题就是,父窗口如何给子窗口传值,一方面,仍然可以使用信号和槽的方式进行,但是,我感觉更便利的方式倒是使用这种public接口的方式进行传值。这种来的更直接和明显。当然,可以看出Signal&Signal方式进行此类的处理会更有通用性。

    在receiveData(QString)的槽函数中进行接收到数据的处理,这里仅仅进行了简单的显示:

void MainWindow::receiveData(QString data)      {          ui->textEdit->setText(data);      }

   最后看下结果:

  

  最终的结果,因为信号和槽可以是多对多的,所以,在类似多个窗体广播信息时,这种方式就很有用,当然也可以使用全局变量的形式。

   使用全局变量;

     使用public形式的函数接口;

     使用QT中的Event机制(这种没有把握,但是感觉应该是可以的),但是实现起来应该比前几种复杂,这里不做讨论。

   5.中文支持:

  网上搜索一下,找到的都是这种:

#include < QTextCodec >int main(int argc, char **argv){....................QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF8"));QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF8"));QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF8"));..........................}

   Qt5中, 取消了QTextCodec::setCodecForTr()和QTextCodec::setCodecForCString()这两个函数,而且网上很多都是不推荐这种写法。

有一下几种转换方法:

1、

 

QTextCodec * BianMa = QTextCodec::codecForName ( "GBK" );    QMessageBox::information(this, "提示", BianMa->toUnicode("中文显示!"));

 

 2、也可以通过QString定义的静态函数,先转换成Unicode类型:

QString::fromLocal8Bit("提示")

 3、在Qt5中,提供了一个专门的处理宏,来支持中文常量,那就是QStringLiteral,但它只能处理常量。

QMessageBox::information(this, QString::fromLocal8Bit("提示"), QStringLiteral("中文显示"));const char* info = "中文显示";    //不支持    QString strInfo = QStringLiteral(info);    //支持    QString strInfo = QString::fromLocal8Bit(info);

QByteArray QString::toLatin1() const

  Latin1是ISO-8859-1的别名,有些环境下写作Latin-1。

  ISO-8859-1编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。

  toLatin1压缩掉了QString自动给每个英文字符加上的那些00字节.

QString与QByteArray互相转换的方法

  QString转QByteArray方法

//Qt5.3.2QString str("hello");  QByteArray bytes = str.toUtf8(); // QString转QByteArray方法1 QString str("hello");  QByteArray bytes = str.toLatin1();  // QString转QByteArray方法2

   QByteArray转QString方法

//Qt5.3.2    QByteArray bytes("hello world");    QString string = bytes;   // QByteArray转QString方法1    QByteArray bytes("hello world");    QString string;    string.prepend(bytes);// QByteArray转QString方法2    qDebug() << string;

   QByteArray类同样不以’\0’为结尾:如

QByteArray bytes;  bytes.resize(5);  bytes[0] = '1';  bytes[1] = '2';  bytes[2] = '3';  bytes[3] = '\0';  bytes[4] = 'a';  cout << bytes << endl;

三、结果图

登录界面:

好友列表:

注册界面:

聊天窗口:

聊天记录:

 

 

转载于:https://www.cnblogs.com/sirius-swu/p/6919924.html

你可能感兴趣的文章
快速大规模无光驱安装Linux操作系统就选“PXE自动安装”
查看>>
我的友情链接
查看>>
switch&router-四层模式
查看>>
新博安卓培训的第一天
查看>>
游戏中常用到的碰撞检测帮助类
查看>>
访问默认共享
查看>>
01262015要看的blog——oracle tuning
查看>>
[信息图]电子商务营销的6大步骤
查看>>
Webdriver(selenium2.0)+NUnit+C# (一)
查看>>
C语言的基本输入输出
查看>>
Hibernate注释大全收藏
查看>>
通过openfiler模拟存储
查看>>
java学习笔记 --- String类
查看>>
实时检查MySQL数据库延迟状况复制中断数据延迟
查看>>
Windows Server 2012 网络负载平衡(NLB)
查看>>
使用JSOM创建一个SharePoint网站计数器
查看>>
1.5-cut命令
查看>>
我的友情链接
查看>>
从技术角度看人与人的沟通
查看>>
加速sshd
查看>>