学习视频链接: blibli QT.
qt的优点:1、跨平台 2、接口简单,容易上手 3、一定程度上简化了内存回收
案例:linux桌面环境KDE、谷歌地图、WPS
QMainWindow和QDialog都是QWidget的子类
//将qDebug()宏定义成cout,并且在输出文件名和代码所在行数
#define cout qDebug() <<"[name:"<<__FILE__<<"Line:"<<__LINE__<<"]"
main.cpp
#include "mainwindow.h"
#include
//argc命令行变量数量 argv命令行变量数组
int main(int argc, char *argv[])
{
//a应用程序对象 在Qt中有且只有一个
QApplication a(argc, argv);
//窗口对象实例化 父类为QMainWindow
MainWindow w;
//窗口对象的显示
w.show();
//让应用程序对象进入消息循环,进行各种操作,除非按下×
//代码阻塞到这
return a.exec();
}
类名:首字母大写,单词和单词之间首字母大写
函数名:变量名称 首字母小写 单词和单词之间首字母大写
注释: ctrl +/
运行: ctrl +r
编译:ctrl +b
查找: ctrl+f
整行移动: ctrl+shift+上下方向键
自动对齐: ctrl+i
同名文件的切换(.h 和.cpp文件的切换):F4
帮助文档:F1(非独立界面)或者直接找到Assistant打开(独立界面)
1、包含控件类 #include<类名>
2、new一个
3、对象->setParent(this)//将控件置于窗口内
=new 类名(另一个类名);//依赖在另一个类名下
4、resize(w,h);//设置窗口大小,也可以设置控件大小
5、setFixedSize(w,h);//设置固定窗口大小,不让缩放
6、setText();//设置文本
7、setWindowTitle();//设置窗口标题
在QT中选中类名,按F1进入帮助文档可以查看signal,如果该类没有signal,可以看基类有没有signal
connect(sender, signal, receiver, slot);
connect(bt_1,QPushButton::clicked,this,MyWidget::close);
sender:发出信号的对象
signal:发送对象发出的信号(只要函数名不要括号)发送类名::槽函数名字
receiver:接收信号的对象
slot:接收对象在接收到信号之后所需要调用的函数(只要函数名不要括号)接收类名::槽函数名字
槽函数需要和信号一致(参数,返回值),信号没有返回值,所以槽函数没有返回值
!!第二个参数写的信号,不是发送信号的参数!!
connect(对象,信号,对象,槽函数);
信号的创建需要signals关键字,信号是一个无返回值的函数(只需声明,无需定义,可有参数)
发送信号要使用槽函数,在槽函数里定义: emit 信号函数;
有参数的信号,要定义拥有相同参数的槽函数
重载的信号和槽函数(即有相同函数名的多个函数)要声明函数指针才能connect
声明函数指针:
示例:(void*)(类名::*指针名)(参数列表)=&类名::函数名;
Lambda表达式(匿名函数对象):C++新特性
static int flag=0;
//lambda表达式
//C++11新增特性 匿名函数对象 若要改变具体对象,需要传入对象在[]。
//直接写入不能使用类成员
//()写入信号参数
//可以在[]中加入=,
//[=]代表把所有外部局部变量、类中所有成员以值传递方式传入,只能显示,不能改变对象本身(能settext)
//[&]引用传递,但对局部变量地址捕捉混乱,可以将局部变量定义为静态变量
connect(qBt2,QPushButton::clicked,
[=]()
{
qDebug()<<"fuck----";
qDebug()<<flag;
flag=!flag;
qBt1->setText("11");
}
);
1、使用QMainWindow类
2、创建QMenuBar类:QMenuBar *mBar=menuBar();//创建菜单栏
3、创建QMenu类:QMenu* menu=mBar->addMenu("菜单名字");//创建菜单
4、创建QAction类:QAction* qAction=menu->addAction("行为名字")//创建菜单的项
5、connect(qAction,QAction::triggered,
[]()
{
//写入点击行为要实现的功能
}
);
//lambda表达式,[]中写入要调用的对象,可以用“=”或“&”;()中写入信号对应的参数
//若要改变值传递的值可以在()后加入mutable
6、menu->addSeparator();//为菜单项与项之间添加分割线,每个项之间都要重新打一次
7、创建QToolBar类:QToolBar* my_ToolBar=addToolBar("toolbar");//创建工具栏
8、关联菜单行为在工具栏上新建快捷方式:my_ToolBar->addAction(qAction);//关联qAction的行为
9、创建QStatusBar类:
QStatusBar* myStatusBar=statusBar();//创建状态栏在左下角
myStatusBar->addWidget(new QLabel("名字",this));//状态栏需要控件显示状态,从左往右添加状态内容addWidget
myStatusBar->addPermanentWidget(new QLabel("2",this));//从右往作添加addPermanentWidget
10、添加QTextEdit类:
QTextEdit* qTextEdit=new QTextEdit(this);//创建核心控件,文本编辑窗口
setCentralWidget(qTextEdit);//依赖于主窗口
11、添加QDockWidget类:
//添加浮动窗口,可在浮动窗口添加文本编辑
QDockWidget* qDockWidget=new QDockWidget(this);
addDockWidget(Qt::RightDockWidgetArea,qDockWidget);//在主窗口右端添加浮动窗口
qDockWidget->setWidget(要添加的控件);
1、模态对话框(只能操作当前对话框)
QAction *qAction=menu->addAction("模态对话框");
//模态对话框,不用show,show了变成非模态的了
//使用exec函数阻塞,只能在当前对话框操作,不能在其他界面操作
connect(qAction,QAction::triggered,
[=]()
{
//connect结束后对象会自动释放
QDialog* qDlg=new QDialog(this);
qDlg->setWindowTitle("模态对话框");
qDlg->resize(512,300);
//qDlg->show();//不用show,show了变成非模态的了
qDlg->exec();
qDebug()<<"模态对话框";
}
);
2、非模态对话框(可以操作其他窗口)
connect(qAction1,QAction::triggered,
[=]()
{
//局部变量直接new占用太多内存(如果常用的话)
//在全部窗口关闭后才释放内存
// QDialog* qDlg=new QDialog(this);
// qDlg->resize(512,300);
// qDlg->setWindowTitle("非模态对话框");
// qDlg->show();
// qDebug()<<"非模态对话框";
//在关闭对话框就释放内存
QDialog *qDlg=new QDialog();//不指定父对象
qDlg->setWindowTitle("非模态对话框");
//当对话框关闭时,qDlg释放内存
qDlg->setAttribute(Qt::WA_DeleteOnClose);
qDlg->show();
qDebug()<<"非模态对话框";
}
);
3、标准对话框
connect(qAction2,QAction::triggered,
[=]()
{
QMessageBox::about(this,"标准对话框","welcome");
});
4、问题对话框
connect(qAction3,QAction::triggered,
[=]()
{
//使用flag接受返回值
//在对话框添加按钮,|为按钮和按钮之间的分隔符号.
int flag=
QMessageBox::question(this,"问题对话框","Are you ok?",
QMessageBox::Yes|QMessageBox::No
|QMessageBox::Cancel);
switch(flag)
{
case QMessageBox::Yes:
qDebug()<<"I am fine!";
break;
case QMessageBox::No:
qDebug()<<"I am not good";
break;
case QMessageBox::Cancel:
qDebug()<<"Nothing!";
break;
default:
break;
}
});
5、文件对话框
connect(qAction4,QAction::triggered,
[=]()
{
//source,image等为文件过滤器,文件格式之间用空格分隔
QString path=QFileDialog::getOpenFileName(this,"openfile","../",
"source(*.cpp *.h);;"
"images(*.jpg *.png *JPG *PNG);;"
"text(*.txt *.doc);;"
"all(*.*)");
qDebug()<<"文件路径"<<path;
});
1、stackedWidget类:分层界面
//跳转界面,界面从0开始
ui->stackedWidget->setCurrentIndex(0);
2、LineEdit:单行文本编辑
//文本格式设置,设置密码不可见
ui->lineEdit->setEchoMode(QLineEdit::Password);
//使用QCompleter类存储常用搜索
//QStringList存储字符串
//QCompletr作为存储QStringList的容器
//QCompleter依赖于其他控件上,如QLabel上
QStringList qSlist;
qSlist<<"hello"<<"How are you?"<<"thank you";
QCompleter *com=new QCompleter(qSlist,this);
//搜索不区分大小
com->setCaseSensitivity(Qt::CaseInsensitive);
ui->lineEdit_2->setCompleter(com);
3、QLabel类:可显示文本、图片、动画、视频、链接等
//插入图片
ui->labelView->setPixmap(QPixmap("://icon/icon-上传.png"));
//自适用大小
ui->labelView->setScaledContents(true);
//创建动画
QMovie* myMovie=new QMovie("://icon/转圈.gif");
//插入动画
ui->labelView->setMovie(myMovie);
//启动动画
myMovie->start();
//自适应界面
ui->labelView->setScaledContents(true);
//设置链接,可插入HTML语言
...setText("baidu.com
");
//激活链接,点击跳转网站
ui->labelLink->setOpenExternalLinks(true);
1、常用快捷键
ctrl+H:水平布局
ctrl+L:垂直布局
ctrl+J:自动调整所选组件的大小
2、基本布局
布局组件 | 功能 |
---|---|
Vertical Layout | 垂直方向布局,组件自动在垂直方向上分布 |
Horizontal Layout | 水平方向布局,组件自动在水平方向上分布 |
Grid Layout | 网格状布局,网状布局大小改变时,每个网格的大小都改变 |
Form Layout | 窗体布局,与网格状布局类似,但是只有最右侧的一列网格会改变大小 |
Horizontal Spacer | 一个用于水平分隔的空格 |
Vertical Spacer | 一个用于垂直分隔的空格 |
容器(container)可以使用widget,容器用不了弹簧居中,但可以铺满整列
事件的派发顺序是先进入eventFilter中,看是否过滤掉此事件,
然后进入到bool WLabel::event(QEvent *e)事件中,由event去分发事件,进入到各个独立事件
例子:为QLabel增加点击和双击事件
新建一个类:MyLabel 继承QLabel
signals:
void clicked();
void doubleClicked();
protected:
bool MyLabel::event(QEvent *ev)
{
switch(ev->type())
{
case QEvent::MouseButtonPress:
emit clicked();
ev->ignore();
break;
case QEvent::MouseButtonDblClick:
emit doubleClicked();
ev->ignore();
break;
case QEvent::MouseMove:
ev->ignore();//将事件传递到容器
break;
default:
break;
}
return QLabel::event(ev);
}
然后在UI界面新建一个QLabel将其提升为MyLabel类,添加信号即可
keyPressEvent()
keyReleaseEvent()
mouseDoubleClickEvent()
mouseMoveEvent() //在构造函数中写入QWidget::setMouseTracking(true);//鼠标追踪无需点下
mousePressEvent()
mouseReleaseEvent() 等。
示例:
void Widget::mousePressEvent(QMouseEvent *event)
{
QString str;
if(event->buttons()==Qt::LeftButton)
str="left";
else if(event->buttons()==Qt::RightButton)
str="right";
else if(event->buttons()==Qt::MidButton)
str="mid";
else
{}
//x,y代表坐标,%1和%2代表arg函数要加入的数据
ui->label_2->setText(QString("clicked:(%1,%2) %3
")
.arg(event->x()).arg(event->y()).arg(str));
}
---
void Widget::keyPressEvent(QKeyEvent *event)
{
//shift、alt、ctrl键都要用此函数
if(event->modifiers() == Qt::ControlModifier)// 是否按下Ctrl键
{
if(event->key() == Qt::Key_M) // 是否按下M键
{
setWindowState(Qt::WindowMaximized); // 窗口最大化
ui->label_4->setText("ctrl+m 窗口最大化");
}
}
}
---
//时间事件需要在构造函数中设定时钟频率:timerId=startTimer(1000);//定时器事件每1000毫秒一次
//只能实现定时器的开启和关闭(不能暂停)
//QTimer可以实现开始和暂停和关闭
//QTimer的使用:QTimer *timer = new QTimer(this);
//connect(timer, SIGNAL(timeout()), this, SLOT(update()));
//timer->start(1000);//开始
//timer->stop();//暂停
void Widget::timerEvent(QTimerEvent *event)
{
static int sec=0;
ui->label_5->setText(QString("time:%1
")
.arg(++sec));
//到某个时间,关闭定时器,timerId需要在启动定时器时获取
if(sec==20)
this->killTimer(timerId);
}
---
void Widget::closeEvent(QCloseEvent *event)
{
int flag=QMessageBox::question(this,"对话框",
"Are you want to close?",
QMessageBox::Yes|QMessageBox::No);
switch(flag)
{
case QMessageBox::Yes:
//accept代表接受此次事件的处理
//event->accept();
break;
case QMessageBox::No:
//ignore代表忽略此类的该事件处理,并将该事件传给容器
event->ignore();
break;
default:
break;
}
}
bool Widget::event(QEvent *ev)//事件的先行处理
{
//事件分发前处理键盘事件
if(ev->type()==QEvent::KeyPress)
{
//强制类型转换
QKeyEvent* keyEvent=static_cast<QKeyEvent*>(ev);
if(keyEvent->key()==Qt::Key_M)
qDebug()<<"M is pressed!";
if(keyEvent->modifiers()==Qt::ControlModifier)
qDebug()<<"Ctrl is pressed";
//return true; //直接返回 true,表示我们已经对此事件进行了处理
}
//返回到
return QWidget::event(ev);//必要的,重新处理其他事件
}
1、ui->label->installEventFilter(this)//给label安装事件过滤器
若键盘没有响应,则可能是被减持了。可改变响应控件的焦点策略
ui->label->setFocusPolicy(Qt::ClickFocus);//点击产生焦点
{
//不想让它继续转发,就返回 true,否则返回 false,默认return false
if(watched==ui->label)
{
if(event->type()==QEvent::Enter)
{
ui->label->setStyleSheet("color: rgb(85, 255, 0);background-color: rgb(255, 216, 249);");
return true;
}
else if(event->type()==QEvent::Leave)
{
ui->label->setStyleSheet("color: rgb(0, 0, 255);background-color: rgb(255, 216, 249);");
return true;
}
else if(event->type()==QEvent::KeyPress)
{
QKeyEvent* ev=static_cast<QKeyEvent *>(event);
if(ev->modifiers()==Qt::ShiftModifier)
{
if(ev->key()==Qt::Key_F)
qDebug()<<"f";
}
//qDebug()<<"key";
return true;
}
//return false;//别的事件会传给label对象 默认return false
}
return QWidget::eventFilter(watched,event);
}
绘图设备是指继承QPainterDevice的子类。Qt一共提供了四个这样的类,分别是QPixmap、QBitmap、QImage和 QPicture。
QPixmap专门为图像在屏幕上的显示做了优化
QBitmap是QPixmap的一个子类,它的色深限定为1(黑白两色),可以使用 QPixmap的isQBitmap()函数来确定这个QPixmap是不是一个QBitmap。
QImage专门为图像的像素级访问做了优化。(可使用独立线程)
QPicture则可以记录和重现QPainter的各条命令。(二进制文件,其他不是)
QPixmap和QImage的相互转化:fromImage()和toImage()
直接使用QPainter* qPainter=new QPainter(this);可以不用begin,但要用end
或QPainter* qPainter=new QPainter();然后在begin(this)和end()之间画画;
//手动调用paintEvent----QWidget::update()
void Widget::paintEvent(QPaintEvent *event)
{
static int sec=0;
QPainter* qPainter=new QPainter(this);
// QPainter* qPainter=new QPainter();
// qPainter->begin(this);
//画图,从(0,0)开始,宽度高度,图片
//qPainter->drawPixmap(0,0,width(),height(),QPixmap("../icon/网络认证.png"));
//绘图设备QPixmap
qPainter->drawPixmap(rect(),QPixmap("../icon/网络认证.png"));
//创建绘图设备QImage,300*300
QImage* image=new QImage(300,300,QImage::Format_ARGB32);
QRgb value;
//填充白色
//image->fill(Qt::white);
for(int i=50; i<100; ++i)
{
for(int j=50; j<100; ++j)
{
value = qRgb(255, 0, 0); // 红色
image->setPixel(i, j, value);
}
}
qPainter->drawImage(QPoint(0, 0), *image);
//设置画笔
QPen* pen=new QPen();
pen->setWidth(5);
pen->setColor(Qt::blue);
pen->setStyle(Qt::SolidLine);
//把画笔交给画家
qPainter->setPen(*pen);
//画直线
qPainter->drawLine(100,100,100,0);
qPainter->drawLine(100,100,0,100);
//设置画刷。填充
QBrush* brush=new QBrush();
brush->setColor(Qt::red);
brush->setStyle(Qt::HorPattern);
//把画刷交给画家
qPainter->setBrush(*brush);
//画椭圆
qPainter->drawEllipse(200,200,50,50);
qDebug()<<sec++;
qPainter->end();
//保存QImage
image->save("image.png");
}
示例:设计一个可以拖动的不规则控件
创建一个类BuGuiZe
buguize.h
protected:
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void paintEvent(QPaintEvent *event);
private:
QPointe m_point;
buguize.cpp
BuGuiZe::BuGuiZe(QWidget *parent) : QWidget(parent)
{
QPixmap* pix=new QPixmap();
pix->load("../icon/icon-提示.png");
this->setFixedSize(pix->width(),pix->height());
//给窗口去掉边框,设置窗口的flags
setWindowFlags(Qt::FramelessWindowHint | windowFlags());
//设置透明背景, On Windows the widget also needs the Qt::FramelessWindowHint window flag to be set.
setAttribute(Qt::WA_TranslucentBackground);
}
void BuGuiZe::mouseMoveEvent(QMouseEvent *event)
{
//按住鼠标左键移动,移动窗口
if(event->buttons()&Qt::LeftButton)
{
move(event->globalPos()-m_point);
}
}
void BuGuiZe::mousePressEvent(QMouseEvent *event)
{
if(event->button()==Qt::LeftButton)
{
m_point=event->globalPos()-frameGeometry().topLeft();
}
if(event->button()==Qt::RightButton)
{
this->close();
}
}
void BuGuiZe::paintEvent(QPaintEvent *event)
{
//向编译器指示具有指定名称的参数未在函数体中使用。这可以用于抑制编译器警告,同时允许在其签名中使用有意义的参数名定义函数。
Q_UNUSED(event);
QPainter* qPainter=new QPainter(this);
qPainter->drawPixmap(0,0,QPixmap("../icon/icon-提示.png"));
qPainter->end();
}
void Widget::on_buttonRead_clicked()
{
QString path=QFileDialog::getOpenFileName(this,
"open","../","TXT(*.txt *.cpp)");
if(!path.isEmpty())
{
QFile* file=new QFile(path);
if(file->open(QIODevice::ReadOnly))
{
#if 0
//默认只识别 UTF-8
//读取整个文件
QByteArray arr=file->readAll();
ui->textEdit->setText(arr);
#endif
//一行一行读取
QByteArray arr;
//检测是否到文件结尾
while(!file->atEnd())
{
arr+=file->readLine();
}
ui->textEdit->setText(arr);
//QFileInfo获取文件信息
QFileInfo* info=new QFileInfo(*file);
qDebug()<<"文件名字:"<<info->fileName();
qDebug()<<"文件后缀:"<<info->suffix();
qDebug()<<"文件大小:"<<info->size();
qDebug()<<"文件创建时间;"<<info->created().toString("yyyy-MM-dd hh:mm:ss");
}
file->close();
}
}
void Widget::on_buttonWrite_clicked()
{
QString path=QFileDialog::getSaveFileName(this,"save","../",
"Text(*.txt)");
if(!path.isEmpty())
{
QFile* file=new QFile();
file->setFileName(path);
if(file->open(QIODevice::WriteOnly))
{
QString str=ui->textEdit->toPlainText();
//QString类型转化
//file->write(str.toUtf8());//QString-->QByteArray
//file->write(str.toStdString().data());//QString->string->char*
// 转化为本地编码(Windows)
file->write(str.toLocal8Bit());
}
file->close();
}
}
//写文件,以QDataStream写入的二进制文件同样也需要QDataStream读取,并且需要按照写入顺序和数据类型读取
QString path=QFileDialog::getSaveFileName(this,"write","../","TEXT(*.txt)");
if(!path.isEmpty())
{
QFile* file=new QFile(path);
if(file->open(QIODevice::WriteOnly))
{
QDataStream stream(file);
QString str=ui->textEdit->toPlainText();
stream<<str;
cout<<str;
}
file->close();
}
//QTextStream流操作只能读取一整个不包含空格的字符串,下一个字符串要重新读取一次。
//或者可以直接用QTextStream::readAll()读取整个文件
QString path=QFileDialog::getOpenFileName(this,"read",
"../","TEXT(*.txt)");
if(!path.isEmpty())
{
QFile* file=new QFile(path);
if(file->open(QIODevice::ReadOnly))
{
QTextStream stream(file);
char* str1=new char[100];
stream.setCodec("UTF-8");
QString str;
//stream>>str;
str=stream.readAll();
ui->textEdit->setText(str.toStdString().data());
cout<<str.toStdString().data();
}
file->close();
}
//QBuffer类的直接读写操作
QBuffer* buff=new QBuffer();
if(buff->open(QIODevice::WriteOnly))
{
buff->write("I love you!");
buff->write("It's impolite!槽");
buff->close();
cout<<buff->buffer().data();
}
//在QBuffer中使用QDataStream流对缓冲区进行读写操作
QBuffer* buff1=new QBuffer();
if(buff1->open(QIODevice::WriteOnly))
{
QDataStream stream(buff1);
stream<<QString("sorry!")<<25041;
buff1->close();
}
if(buff1->open(QIODevice::ReadOnly))
{
QDataStream stream(buff1);
QString str;
int num;
stream>>str>>num;
buff1->close();
cout<<str<<num;
}
示例:简单的TCP服务端和客户端
服务端:
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//创建客户端界面
ClientWindow* clientWin=new ClientWindow();
clientWin->show();
//界面设置
this->setWindowTitle("server--port:8008");
//监听套接字
server=new QTcpServer(this);
server->listen(QHostAddress::Any,8008);
//通信套接字
socket=new QTcpSocket(this);
//接受客户端请求
connect(server,QTcpServer::newConnection,
[=]()
{
socket=server->nextPendingConnection();
QString ip=socket->peerAddress().toString();
quint16 port=socket->peerPort();
QString str=QString("[%1:%2]--成功连接").arg(ip).arg(port);
ui->textEditRead->setText(str);
//由于socket在此连接中更新,所有所有有关socket的连接都要写在此连接中
//接受客户端发送的数据
connect(socket,QTcpSocket::readyRead,
[=]()
{
QByteArray arr=socket->readAll();
ui->textEditRead->append(arr);
});
connect(socket,QTcpSocket::disconnected,
[=]()
{
ui->textEditRead->append("连接断开");
if(socket->isOpen())
{
socket->close();
}
});
});
}
Widget::~Widget()
{
//手动释放内存最佳
delete socket;
delete server;
delete ui;
}
void Widget::on_buttonSend_clicked()
{
if(socket->isOpen())
{
QString str=ui->textEditSend->toPlainText();
socket->write(str.toUtf8());
}
else
{
ui->textEditRead->append("没有可发送的客户端");
}
}
void Widget::on_buttonClose_clicked()
{
if(socket->isOpen())
{
socket->disconnectFromHost();
socket->close();
}
else
{
ui->textEditRead->append("没有可断开的连接");
}
}
客户端:
ClientWindow::ClientWindow(QWidget *parent) :
QWidget(parent),
ui(new Ui::ClientWindow)
{
ui->setupUi(this);
this->setWindowTitle("clinet--");
//客户端通信套接字
socket=new QTcpSocket(this);
connect(socket,QTcpSocket::connected,
[=]()
{
QString ip=ui->lineEditIP->text();
QString port=ui->lineEditPort->text();
ui->textEditRead->setText(QString("[ip:%1-%2]连接成功!").arg(ip).arg(port));
});
//接受服务器端发送的数据
connect(socket,QTcpSocket::readyRead,
[=]()
{
QByteArray arr=socket->readAll();
ui->textEditRead->append(arr);
});
//若TCP连接断开
connect(socket,QTcpSocket::disconnected,
[=]()
{
ui->textEditRead->append("连接断开");
if(socket->isOpen())
{
socket->close();
}
});
}
ClientWindow::~ClientWindow()
{
//手动释放,可以避免部分问题
delete socket;
delete ui;
}
void ClientWindow::on_buttonConnect_clicked()
{
QString ip=ui->lineEditIP->text();
quint16 port=ui->lineEditPort->text().toInt();
socket->connectToHost(QHostAddress(ip),port);
}
void ClientWindow::on_buttonClose_clicked()
{
if(socket->isOpen())
{
socket->disconnectFromHost();
socket->close();
}
else
{
ui->textEditRead->append("没有可断的连接");
}
}
void ClientWindow::on_buttonSend_clicked()
{
if(socket->isOpen())
{
QString str=ui->textEditSend->toPlainText();
socket->write(str.toUtf8());
}
else
{
ui->textEditRead->append("未连接");
}
}
不分客户端和服务端(代码都是一样的)
UDP广播和组播(广播地址:255.255.255.255)
UDP广播和组播只能在局域网范围内使用,广播发给所有用户,组播发给部分用户
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//客户端界面
MyClient* myClient=new MyClient();
myClient->show();
myClient->setWindowTitle("客户端8008");
//服务器端
udpSocket=new QUdpSocket(this);
//绑定本机通信端口
//udpSocket->bind(8080);
//绑定组播端口
client->bind(QHostAddress::AnyIPv4,8008);
client->joinMulticastGroup(QHostAddress("224.0.0.2"));
this->setWindowTitle("服务器:8080");
//读取数据
connect(udpSocket,QUdpSocket::readyRead,
[=]()
{
char str[1024]={0};
QHostAddress add;//获取对方IP
quint16 port;//获取对方port
//sizeof加数组名可以得到数组大小,若是指针只能知道指针类型得大小
quint64 len=udpSocket->readDatagram(str,sizeof(str),&add,&port);
if(len>0)
{
QString qStr=QString("[%1:%2] %3").arg(add.toString()).arg(port).arg(str);
ui->textEditRead->append(qStr);
}
});
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_buttonSend_clicked()
{
//如果套接字有效并且可以使用,则返回true
if(udpSocket->isValid())
{
QHostAddress add=QHostAddress(ui->lineEditIP->text());
quint16 port=ui->lineEditPort->text().toInt();
QString str=ui->textEditSend->toPlainText();
if(!add.isNull())
udpSocket->writeDatagram(str.toUtf8(),add,port);
}
else
{
cout<<"套接字已经关闭,无法发送";
}
}
void Widget::on_buttonClose_clicked()
{
if(udpSocket->isValid())
{
cout<<"套接字关闭";
udpSocket->close();
}
else
{
cout<<"套接字已经关闭";
}
}
服务端
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
client=new TcpClient();
client->show();
ui->buttonOpen->setEnabled(true);
ui->buttonSend->setEnabled(false);
server=new QTcpServer(this);
server->listen(QHostAddress::Any,8008);
connect(server,QTcpServer::newConnection,
[=]()
{
socket=server->nextPendingConnection();
QString str=QString("[%1:%2]--连接成功").arg(socket->peerAddress().toString()).arg(socket->peerPort());
ui->textEdit->setText(str);
connect(socket,QTcpSocket::disconnected,
[=]()
{
ui->textEdit->append("连接断开");
ui->buttonSend->setEnabled(false);
});
});
timer=new QTimer(this);
connect(timer,QTimer::timeout,
[=]()
{
//关闭定时器
timer->stop();
//发送文件
sendFile();
});
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_buttonOpen_clicked()
{
//QString path=QFileDialog::getOpenFileName(this,"open","../",
//"TXT(*.txt *cpp *.h);; ZIP(*.zip *.7z)");
QString path=QFileDialog::getOpenFileName(this,"open","../");
if(!path.isEmpty())
{
//获取文件信息
QFileInfo* fileInfo=new QFileInfo(path);
if(fileInfo->exists())
{
fileName=fileInfo->fileName();
fileSize=fileInfo->size();
}
file=new QFile(path);
if(file->open(QIODevice::ReadOnly))
{
ui->buttonSend->setEnabled(true);
ui->textEdit->append(QString("[文件打开成功!]--%1--%2byte").arg(fileName).arg(fileSize));
}
}
}
void Widget::on_buttonSend_clicked()
{
QString head=QString("%1--%2").arg(fileName).arg(fileSize);
quint64 len=socket->write(head.toUtf8());
//开始发送真正的文件
if(len>0)
{
//20毫秒
timer->start(20);
}
else
{
file->close();
//重新打开文件再发送
ui->buttonSend->setEnabled(false);
}
}
void Widget::sendFile()
{
ui->textEdit->append("正在发送-----");
sendSize=0;
quint64 len=0;
do
{
char buf[4*1024]={0};
len=0;
//从文件中读取数据
len=file->read(buf,sizeof(buf));
cout<<"len="<<len;
len=socket->write(buf,len);
sendSize+=len;
cout<<sendSize;
}while(len>0);
//发送完毕
if(sendSize==fileSize)
{
ui->textEdit->append("发送完毕!!!");
file->close();
//socket->disconnectFromHost();
delete file;
file=NULL;
//socket->close();
//socket=NULL;
ui->buttonSend->setEnabled(false);
}
}
客户端
TcpClient::TcpClient(QWidget *parent) :
QWidget(parent),
ui(new Ui::TcpClient)
{
ui->setupUi(this);
socket=new QTcpSocket(this);
connect(socket,QTcpSocket::connected,
[=]()
{
ui->textEdit->setText(QString("[ip:%1-%2]连接成功!").arg(ui->lineEditIP->text()).arg(ui->lineEditPort->text()));
});
//接受数据
connect(socket,QTcpSocket::readyRead,
[=]()
{
QByteArray buf=socket->readAll();
//接收头部文件信息
if(flag)
{
flag=false;
//头部信息自定义格式:filename--filesize;
//字符串分节
fileName=QString(buf).section("--",0,0);
fileSize=QString(buf).section("--",1,1).toInt();
ui->textEdit->append(QString("文件接收中---[%1,%2Byte]").arg(fileName).arg(fileSize));
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(fileSize);
ui->progressBar->setValue(0);
}
//接收文件
else
{
file=new QFile(fileName,this);
if(file->open(QIODevice::WriteOnly))
{
qDebug()<<"111111";
qDebug()<<"1111111";
quint64 len=file->write(buf);
recieveSize+=len;
ui->progressBar->setValue(recieveSize);
}
if(recieveSize==fileSize)
{
recieveSize=0;
flag=true;
file->close();
ui->textEdit->append("文件接收成功!!!");
}
}
});
}
TcpClient::~TcpClient()
{
delete ui;
}
void TcpClient::on_buttonConnect_clicked()
{
QString ip=ui->lineEditIP->text();
QString port=ui->lineEditPort->text();
socket->connectToHost(QHostAddress(ip),port.toInt());
}
void TcpClient::on_buttonDisconnect_clicked()
{
socket->disconnectFromHost();
socket->close();
ui->textEdit->append("断开连接");
}
两种方式
1、新建一个类直接继承QThread类,重写run函数
2、新建一个类,通过moveToThread将该类添加到子线程里,并执行其中的函数。
ui类Widget
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include
#include"mythread.h"
#include
#include
#define cout qDebug() <<"["<<"Line:"<<__LINE__<<"]"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
signals:
void timerstop();
private slots:
void on_pushButton_clicked();
private:
Ui::Widget *ui;
QTimer* timer;
MyThread* myThread;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
timer=new QTimer(this);
myThread=new MyThread(this);
connect(timer,QTimer::timeout,
[=]()
{
static int sec=0;
ui->lcdNumber->display(++sec);
});
connect(myThread,MyThread::threadOver,
[=]()
{
//关闭子线程和定时器
myThread->quit();
myThread->wait();
timer->stop();
});
}
Widget::~Widget()
{
delete ui;
delete myThread;
delete timer;
}
void Widget::on_pushButton_clicked()
{
if(!timer->isActive())
{
//开启线程和定时器
timer->start(100);
myThread->start();//间接调用run
}
else
cout<<"已开始,切勿点击";
}
线程类MyThread
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include
#include
#include
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);
~MyThread();
protected:
void run();//重写QThread的run函数
signals:
void threadOver();
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
MyThread::MyThread(QObject *parent) : QThread(parent)
{
}
MyThread::~MyThread()
{
}
void MyThread::run()
{
//线程创建定时器要在run函数里面创建
QTimer* timer1=new QTimer();
timer1->moveToThread(this);
if(!timer1->isActive())
{
timer1->start(100);
}
connect(timer1,QTimer::timeout,
[=]()
{
static int i=0;
i++;
//5秒停止一次
if((i%50)==0)
{
emit threadOver();
}
});
//阻塞后才能使用定时器
this->exec();
delete timer1;
//QThread::sleep(5);//睡眠5秒
//emit threadOver();
}
ui主界面类Widget
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include
#include"workclass.h"
#include
#include
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
signals:
void worksignal();
private slots:
void on_buttonStart_clicked();
void on_buttonStop_clicked();
void procecsssignal1();
private:
Ui::Widget *ui;
QTimer* timer;
WorkClass* workclass;
QThread* myThread;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//不要指定父对象,不然无法加入到子线程
workclass=new WorkClass();
myThread=new QThread();
timer=new QTimer(this);
qDebug()<<"主线程号:"<<QThread::currentThread();
//将workClass加入到子线程
workclass->moveToThread(myThread);
//不能直接调用工作函数,只能通过connect调用
//第五个参数的调用
//Qt::DirectConnection //发送和接受者线程相同一样时使用
//QueuedConnection //发送者和接收者不同线程时使用
//Qt::BlockingQueuedConnection //于上个模式相似,
//但会阻塞至接收者的槽函数执行完毕,适用于槽函数中有大量数据要处理的情况
//Qt::UniqueConnection //判断作用:防止重复连接
//使用方式:(Qt::AutoConnection | Qt::UniqueConnection)
connect(this,Widget::worksignal,workclass,WorkClass::work);
//接收workclass的信号并处理
connect(workclass,WorkClass::signal1,this,Widget::procecsssignal1);
}
Widget::~Widget()
{
delete ui;
delete workclass;
delete timer;
delete myThread;
}
void Widget::on_buttonStart_clicked()
{
//设置标志位开始工作
if(!timer->isActive())
{
qDebug()<<"开始工作";
workclass->setFlag(true);
timer->start(1000);
//发送信号调用work函数
emit worksignal();
myThread->start();
}
}
void Widget::on_buttonStop_clicked()
{
//设置标志停止工作
if(timer->isActive())
{
workclass->setFlag(false);
timer->stop();
myThread->quit();
myThread->wait();
}
}
//每次LCD显示数字+1
void Widget::procecsssignal1()
{
static int i=0;
i++;
ui->lcdNumber->display(i);
}
工作类WorkClass:用于加入子进程
workclass.h
#ifndef WORKCLASS_H
#define WORKCLASS_H
#include
#include
#include
class WorkClass : public QObject
{
Q_OBJECT
public:
explicit WorkClass(QObject *parent = nullptr);
~WorkClass();
//工作函数
void work();
//设置标志位判断是否工作
void setFlag(bool f);
signals:
void signal1();
public slots:
private:
bool flag;
};
#endif // WORKCLASS_H
workclass.cpp
#include "workclass.h"
WorkClass::WorkClass(QObject *parent) : QObject(parent)
{
}
WorkClass::~WorkClass()
{
}
void WorkClass::work()
{
while(flag)
{
QThread::sleep(1);
emit signal1();
qDebug()<<"子线程号;"<<QThread::currentThread();
}
if(!flag)
qDebug()<<"停止工作";
}
void WorkClass::setFlag(bool f)
{
flag=f;
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include"drawwork.h"
#include
#include
#include
#include
#include
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
void getImage(QImage* im);
~Widget();
protected:
void paintEvent(QPaintEvent *event);
private slots:
private:
Ui::Widget *ui;
DrawWork* draw_work;
QThread* thread;
QImage* image;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
image=new QImage();
thread=new QThread(this);
thread->start();
draw_work=new DrawWork();
draw_work->moveToThread(thread);
//接受线程的图片
connect(ui->buttonUpdate,QPushButton::clicked,draw_work,DrawWork::drawWork);
connect(draw_work,DrawWork::sendImage,this,Widget::getImage);
}
Widget::~Widget()
{
delete ui;
thread->quit();
thread->wait();
delete image;
delete draw_work;
delete thread;
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter* painter=new QPainter(this);
painter->drawImage(QPoint(200,200),*image);
painter->end();
}
void Widget::getImage(QImage *im)
{
image=im;
update();
}
drawwork.h
#ifndef DRAWWORK_H
#define DRAWWORK_H
#include
#include
#include
#include
#include
#include
#include
class DrawWork : public QObject
{
Q_OBJECT
public:
explicit DrawWork(QObject *parent = nullptr);
~DrawWork();
void drawWork();
signals:
void sendImage(QImage* image);
public slots:
};
#endif // DRAWWORK_H
drawwork.cpp
#include "drawwork.h"
DrawWork::DrawWork(QObject *parent) : QObject(parent)
{
}
DrawWork::~DrawWork()
{
}
void DrawWork::drawWork()
{
QImage* image=new QImage(200,200,QImage::Format_ARGB32);
QPainter* painter=new QPainter(image);
//画笔
QPen* pen=new QPen();
pen->setColor(Qt::blue);
pen->setWidth(5);
pen->setStyle(Qt::DashLine);
//画刷
QBrush* brush=new QBrush;
brush->setColor(Qt::red);
brush->setStyle(Qt::Dense5Pattern);
//将画笔和画刷交给画家
painter->setPen(*pen);
painter->setBrush(*brush);
QPoint point[5]=
{
QPoint(qrand()%200,qrand()%200),
QPoint(qrand()%200,qrand()%200),
QPoint(qrand()%200,qrand()%200),
QPoint(qrand()%200,qrand()%200),
QPoint(qrand()%200,qrand()%200)
};
//根据五个点画出多边形
painter->drawPolygon(point,5);
painter->end();
emit sendImage(image);
}