绘制事件
1> 对于QT窗口而言,最先接触的是绘制事件,整个窗口的内容都是有绘制事件绘制出来的
2> 触发条件:当窗口第一次展示出来、由最小化重新展示出来、窗口最大化、由最大化转变正常、改变窗口大小、使用updata函数进行更新界面时
3> 在绘制事件处理函数中,可以使用画家类对界面进行相关绘制工作
4> 实现实例
头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
//重写自己的绘制事件处理函数
void paintEvent(QPaintEvent *event) override;
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
源文件
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include<QPainter> //画家类
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
//绘制事件处理函数的定义
void Widget::paintEvent(QPaintEvent *event)
{
//static int num = 0;
//qDebug()<<num++;
QPainter painter(this); //实例化一个画家
//准备画笔
QPen pen;
pen.setStyle(Qt::DashDotLine); //使用的是虚点线
pen.setColor(QColor("red")); //设置画笔颜色
pen.setWidth(10); //设置画笔的粗细
//准备字体
QFont font;
font.setFamily("楷体"); //设置字体族
font.setStyle(QFont::StyleItalic); //设置字体倾斜
font.setPointSize(40); //设置字号
//给画家安排上画笔
painter.setPen(pen);
//给画家安排字体
painter.setFont(font);
//绘制文本
painter.drawText(this->rect(), //绘制的区域矩形框
Qt::AlignCenter, //绘制的对齐方式
"好好学习,天天向上"); //绘制的文本内容
//绘制矩形框
painter.drawRect(50,50,this->width()-100, this->height()-100);
//设置画家的坐标起点
painter.translate(this->width()/2,this->height()/2);
//设置绘制坐标正方向
painter.rotate(45); //参数 为旋转的坐标方向
//绘制一个椭圆
painter.drawEllipse(QPoint(0,0), 300, 200);
}
6> 使用绘制事件完成画图软件
头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QMouseEvent> //鼠标事件
#include<QPaintEvent> //绘制事件
#include<QPainter> //画家类
#include<QPixmap>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
//重写相关事件处理函数
void mouseMoveEvent(QMouseEvent *event) override; //鼠标移动事件
void mousePressEvent(QMouseEvent *event) override; //鼠标按下事件
void paintEvent(QPaintEvent *event) override; //绘制事件
private:
Ui::Widget *ui;
QPoint startPoint; //绘制的起始点
QPixmap *pix; //膜
};
#endif // WIDGET_H
源文件
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//实例化屏幕膜
pix = new QPixmap(this->size()); //实例化一个膜大小跟当前界面一致
pix->fill(Qt::white); //使用白色进行填充
}
Widget::~Widget()
{
delete ui;
}
//鼠标移动事件
void Widget::mouseMoveEvent(QMouseEvent *event)
{
//在该事件处理函数中,要在膜上绘制一条直线
//1、实例化一个画家
QPainter p1(pix); //父组件应该是膜的地址
//2、对画家进行属性设置
QPen pen;
pen.setStyle(Qt::DashDotLine); //使用的是虚点线
pen.setColor(QColor("green")); //设置画笔颜色
pen.setWidth(5); //设置画笔的粗细
p1.setPen(pen); //设置画笔
//2、使用画家在两点之间画直线
p1.drawLine(startPoint, event->pos()); //起始点到鼠标当前位置绘制直线
startPoint = event->pos(); //更新起始点,以便于下一次的绘制
//3、更新界面内容
this->update();
}
//鼠标点击事件实现
void Widget::mousePressEvent(QMouseEvent *event)
{
startPoint = event->pos(); //获取画笔的起始点
}
//绘制事件
void Widget::paintEvent(QPaintEvent *event)
{
//在该事件处理函数中,将膜上面的内容绘制到界面上
//1、准备一个画家,在界面上进行绘制
QPainter p2(this);
//2、将pix绘制到界面上
p2.drawPixmap(QPoint(0,0), *pix);
}
一、回顾TCP网络通信步骤
二、QT中的TCP网络通信
2.1 服务器端(QTcpServer)
1> 使用QTcpServer类实例化一个对象,就得到了一个服务器端
2> 调用该类对象的成员函数 listen 将服务器启动监听,该函数会进行绑定ip和端口号
ip地址可以指定也可以由系统自动绑定,端口号也可以自己指定和由系统自动指定
3> 当有客户端发来连接请求后,该服务器就会自动发射一个newConnection的信号
我们可以将该信号绑定到自定义的槽函数中完成相关逻辑
4> 可以使用类中的成员函数 nextPenddingConnection 可以获取最新连接的客户端套接字
5> 可以使用该客户端套接字进行数据收发
read、readLine、readAll读取数据
write发送数据
6> 当服务器收到客户端的消息后,该服务器会自动发射一个readyRead的信号
可以将该信号连接到对应的槽函数中,处理客户端发来的消息
7> 调用成员函数close关闭监听
ui界面
头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QTcpServer> //tcp服务器类
#include<QTcpSocket> //tcp客户端类
#include<QList> //链表类
#include<QMessageBox> //消息对话框类
#include<QDebug> //信息调试类
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_startSerBtn_clicked();
void newConnection_slot(); //自定义处理newConnection信号的槽函数
void readyRead_slot(); //自定义处理readyRead信号的槽函数
private:
Ui::Widget *ui;
QTcpServer *server; //定义服务器指针
QList<QTcpSocket *> socketList; //定义存放客户端信息的容器
};
#endif // WIDGET_H
源文件
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//实例化一个服务器对象
server = new QTcpServer(this);
}
Widget::~Widget()
{
delete ui;
}
//启动服务器按钮对应的槽函数
void Widget::on_startSerBtn_clicked()
{
if(ui->startSerBtn->text() == "启动服务器")
{
//执行启动服务器的动作
//获取ui界面上的端口号
quint16 port = ui->portEdit->text().toUInt();
//启动监听:bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
//参数1:要监听的ip地址,如果写Any,表示监听该主机上的所有网络接口
//参数2:要监听的端口号,如果不指定,系统会默认给绑定一个随机端口号
//返回值:成功监听返回真,失败返回假
if(!server->listen(QHostAddress::Any, port))
{
QMessageBox::critical(this, "错误", "服务器启动失败");
return;
}
//程序执行至此,表示服务器启动成功
QMessageBox::information(this, "成功", "服务器启动成功");
//将航编辑器设置为不可用
ui->portEdit->setEnabled(false);
//将按钮文本内容设置为关闭服务器
ui->startSerBtn->setText("关闭服务器");
//此时,如果有客户端发来连接请求,那么该服务器就会自动发射一个newConnection的信号
//我们可以将该信号连接到自定义的槽函数中,处理后续操作
connect(server, &QTcpServer::newConnection, this, &Widget::newConnection_slot);
}else
{
//执行关闭服务器的动作
server->close();
//将行编辑器设置成可用状态
ui->portEdit->setEnabled(true);
//将按钮文本内容设置为启动服务器
ui->startSerBtn->setText("启动服务器");
}
}
//自定义处理newConnection信号的槽函数的实现
void Widget::newConnection_slot()
{
qDebug()<<"有新的客户端发来连接请求了";
//可以通过成员函数 nextPendingConnection函数获取最新连接的客户端套接字的地址
//函数原型:QTcpSocket *nextPendingConnection();
//无参函数
//返回值:最新的一个连接的套接字地址
QTcpSocket * s = server->nextPendingConnection();
//将该套接字放入客户端链表中
socketList.push_back(s);
//程序执行至此,一个服务器可以对应多个客户端,已经建立了连接
//此时,如果有某个客户端发来数据,那么该客户端套接字就会自动发射一个readyRead的信号
//我们可以将该信号连接到自定义的槽函数中处理相关逻辑
connect(s, &QTcpSocket::readyRead, this, &Widget::readyRead_slot);
}
//自定义处理readyRead信号的槽函数的实现
void Widget::readyRead_slot()
{
//1、遍历链表中的所有客户端,如果客户端的状态为未连接,则直接移除出链表
for(int i=0; i<socketList.size(); i++)
{
//判断当前套接字 socketList[i] 是否实效
//函数原型:SocketState state() const;
//功能:返回当前套接字的状态
//返回结果为0时,表示该套接字是未连接状态
if(socketList[i]->state()==0)
{
//将该套接字移除出链表
socketList.removeAt(i);
}
}
//2、遍历所有客户端,判断客户端中是否有数据可读,如果有数据可读,则表示是该客户端发来的消息、
for(int i=0; i<socketList.count(); i++)
{
//判断当前 客户端中是否有数据可读
//函数原型:qint64 bytesAvailable() const override;
//参数无
//返回值:返回当前客户端套接字中的待读数据,如果没有数据,则返回0
if(socketList[i]->bytesAvailable() != 0)
{
//读取当前套接字的内容
QByteArray msg = socketList[i]->readAll();
//将接受的消息展示到ui界面上
ui->msgListWidget->addItem( QString::fromLocal8Bit(msg) );
//将收到的消息,全部发给其他客户端
for(int j=0; j<socketList.length(); j++)
{
if(i!=j) //防止自己发给自己
{
socketList[j]->write(msg);
}
}
}
}
}
2.2 客户端(QTcpSocket)
1> 使用该类实例化一个对象,就创建了一个客户端
2> 使用该类对象的成员函数 connectToHost 向服务器发送连接请求
如果连接服务器成功,那么该客户端套接字就会自动发射一个 connected 的信号
可以将该信号连接到自定义的槽函数中处理相关逻辑
3> 可以使用该客户端套接字进行数据收发
read、readLine、readAll读取数据
write发送数据
4> 如果客户端收到服务器发来的消息后,该客户端就会自动发射一个readyRead的信号
可以将该信号连接到对应的槽函数中,处理客户端发来的消息
5> 调用成员函数 disconnectFromHost 断开跟服务器的连接
ui界面
头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QTcpSocket> //客户端套接字类
#include<QMessageBox> //消息对话框类
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_connectBtn_clicked();
void connected_slot(); //自定义处理connected信号的槽函数
void readyRead_slot(); //自定义处理readyRead信号的槽函数
void disconnected_slot(); //自定义处理disconnected信号的槽函数
void on_sendBtn_clicked();
void on_disConnectBtn_clicked();
private:
Ui::Widget *ui;
//定义通信用的变量
QTcpSocket *client; //定义套接字指针
QString userName; //用户名
};
#endif // WIDGET_H
源文件
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//实例化客户端对象
client = new QTcpSocket(this);
//当客户端与服务器建立联系后,如果客户端接受到服务器发来的消息
//客户端自身就会自动发射一个 readyRead的信号,我们可以将该信号连接到自定义的槽函数中执行相关逻辑
connect(client, &QTcpSocket::readyRead, this, &Widget::readyRead_slot);
//如果当前客户端成功连接的服务器,那么该客户端就会自动发射一个connected的信号
//我们可以将该信号连接到自定义的槽函数中处理相关逻辑
connect(client, &QTcpSocket::connected, this, &Widget::connected_slot);
//当客户端断开了与服务器的连接后,该客户端就会自动发射一个disconnected的信号
//我们可以将该信号连接到自定义的槽函数中处理相关逻辑
connect(client, &QTcpSocket::disconnected, this, &Widget::disconnected_slot);
}
Widget::~Widget()
{
delete ui;
}
//连接服务器按钮对应的槽函数
void Widget::on_connectBtn_clicked()
{
//获取ui界面上的数据
QString ip = ui->ipEdit->text(); //ip地址
quint16 port = ui->portEdit->text().toUInt(); //端口号
userName = ui->nameEdit->text(); //用户名
//调用套接字成员函数,连接服务器
//函数原型:void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);
//参数1:要被连接的服务器ip地址
//参数2:服务器的端口号
//参数3:默认为可读可写
//返回值:无
client->connectToHost(ip, port);
}
//处理connected信号的槽函数的定义
void Widget::connected_slot()
{
QMessageBox::information(this,"成功","连接成功");
//将相关组件禁用
ui->ipEdit->setEnabled(false);
ui->nameEdit->setEnabled(false);
ui->portEdit->setEnabled(false);
ui->connectBtn->setEnabled(false);
//向服务器发送一条消息
QString msg = userName + ": 进入聊天室";
client->write( msg.toLocal8Bit() );
}
//自定义处理readyRead信号的槽函数
void Widget::readyRead_slot()
{
//从套接字中读取数据
QByteArray msg = client->readAll();
//将读取下来的数据展示到ui界面上
ui->msgListWidget->addItem( QString::fromLocal8Bit(msg) );
}
//消息发送按钮对应的槽函数
void Widget::on_sendBtn_clicked()
{
//组织要发送的消息
QString msg = userName + ": " + ui->msgEdit->text();
//将消息发送给服务器
client->write(msg.toLocal8Bit());
//将消息展示到自己界面上
//准备一个QListWidgetItem类的对象
QListWidgetItem *item = new QListWidgetItem(msg);
item->setTextAlignment(Qt::AlignRight); //将文本右对齐
ui->msgListWidget->addItem(item);
//清空消息发送框的内容
ui->msgEdit->clear();
}
//断开连接按钮对应的槽函数
void Widget::on_disConnectBtn_clicked()
{
//执行断开连接的操作
//准备发送消息给服务器
QString msg = userName + ": 离开聊天室";
client->write(msg.toLocal8Bit());
//断开连接
client->disconnectFromHost();
}
//自定义处理disconnected信号的槽函数的定义
void Widget::disconnected_slot()
{
QMessageBox::information(this, "提示", "成功断开与服务器的连接");
//将相关组件启用
ui->ipEdit->setEnabled(true);
ui->nameEdit->setEnabled(true);
ui->portEdit->setEnabled(true);
ui->connectBtn->setEnabled(true);
}
时钟
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPaintEvent>
#include <QTimer>
#include <QPainter>
#include <QPen>
#include <QBrush>
#include <QTime>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
//重写自己的绘制事件处理函数
void paintEvent(QPaintEvent *event) override;
public slots:
void slot();
private:
Ui::Widget *ui;
QTimer *timer;
int hour;
int minute;
int second;
};
#endif // WIDGET_H
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 初始化定时器
QTimer *timer = new QTimer(this);
// 设置定时器每秒钟触发一次
timer->setInterval(1000);
// 将定时器的timeout信号连接到slot槽函数
connect(timer, &QTimer::timeout, this, &Widget::slot);
// 启动定时器
timer->start();
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);//实例化一个画家
QPen pen;// 准备画笔
pen.setStyle(Qt::SolidLine);// 设置画笔样式为直线
pen.setColor(QColor("red"));// 设置画笔颜色为红色
pen.setWidth(10);// 设置画笔粗细为10像素
//准备字体
QFont font;
font.setFamily("楷体"); //设置字体族
font.setStyle(QFont::StyleNormal); //设置字体为正常
font.setPointSize(40); //设置字号
//给画家安排上画笔
painter.setPen(pen);
//设置画家的坐标起点
painter.translate(this->width()/2,this->height()/2);
//绘制一个圆
painter.drawEllipse(QPoint(0,0), 200, 200);
pen.setColor(QColor("black"));
painter.setPen(pen);
for(int i=0;i<60;i++) //绘制分钟线
{
painter.rotate(6);
painter.drawLine(QPoint(200,0),QPoint(195,0));
}
pen.setWidth(10);
painter.setPen(pen);
for(int i=1;i<=12;i++) //绘制时钟线
{
painter.drawLine(QPoint(200,0),QPoint(190,0));
painter.rotate(30);
painter.drawText(QPoint(0,-170),QString("%1").arg(i));
}
//时针
pen.setWidth(15);
pen.setColor(QColor("red"));
painter.setPen(pen);
painter.rotate(hour*30+30/60*minute+30/60/60*second);
painter.drawLine(QPoint(0,-50),QPoint(0,5));
//分针
QPainter p1(this);
p1.translate(this->width()/2,this->height()/2);
pen.setWidth(10);
pen.setColor(QColor("blue"));
p1.setPen(pen);
p1.rotate(minute*6+6*second/60);
p1.drawLine(QPoint(0,-80),QPoint(0,8));
//秒针
QPainter p2(this);
p2.translate(this->width()/2,this->height()/2);
pen.setWidth(5);
pen.setColor(QColor("green"));
p2.setPen(pen);
p2.rotate(6*second);
p2.drawLine(QPoint(0,-120),QPoint(0,12));
}
void Widget::slot()
{
QDateTime currentDateTime = QDateTime::currentDateTime();
QString currentTimeStr = currentDateTime.toString("hh:mm:ss");
hour=currentDateTime.time().hour();
minute=currentDateTime.time().minute();
second=currentDateTime.time().second();
this->update();
}