qt学习笔记

学习视频链接: blibli QT.

qt的优点:1、跨平台 2、接口简单,容易上手 3、一定程度上简化了内存回收
案例:linux桌面环境KDE、谷歌地图、WPS

QT学习笔记

day01

    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;
		});

day02

常用控件的使用

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、基本布局

qt学习笔记_第1张图片

布局组件 功能
Vertical Layout 垂直方向布局,组件自动在垂直方向上分布
Horizontal Layout 水平方向布局,组件自动在水平方向上分布
Grid Layout 网格状布局,网状布局大小改变时,每个网格的大小都改变
Form Layout 窗体布局,与网格状布局类似,但是只有最右侧的一列网格会改变大小
Horizontal Spacer 一个用于水平分隔的空格
Vertical Spacer 一个用于垂直分隔的空格

在使用布局组件时,面对多个控件,可以将多个控件放在同一个界面(容器中)视为整体。

容器(container)可以使用widget,容器用不了弹簧居中,但可以铺满整列

Qt事件

事件的派发顺序是先进入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类,添加信号即可

基本事件(函数都是protected virtual的)

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; } }

event函数

	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);//点击产生焦点
2、bool eventFilter(QObject *obj, QEvent *event);重写事件过滤函数
	{
		//不想让它继续转发,就返回 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);
	}

day03

Qt绘图

1、基本绘图设备

绘图设备是指继承QPainterDevice的子类。Qt一共提供了四个这样的类,分别是QPixmap、QBitmap、QImage和 QPicture。

QPixmap专门为图像在屏幕上的显示做了优化
QBitmap是QPixmap的一个子类,它的色深限定为1(黑白两色),可以使用 QPixmap的isQBitmap()函数来确定这个QPixmap是不是一个QBitmap。
QImage专门为图像的像素级访问做了优化。(可使用独立线程)
QPicture则可以记录和重现QPainter的各条命令。(二进制文件,其他不是)
QPixmap和QImage的相互转化:fromImage()和toImage()

2、简单绘图

直接使用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");


}

程序主界面

qt学习笔记_第2张图片

image.png

qt学习笔记_第3张图片

3、不规则窗体

示例:设计一个可以拖动的不规则控件
创建一个类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();
}

QFile读写文件

读文件

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写入的二进制文件同样也需要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流操作只能读取一整个不包含空格的字符串,下一个字符串要重新读取一次。
//或者可以直接用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(缓存区类,与QFile类似的IO设备)

//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;
    }

day04

TCP和UDP网络编程

QTCP

示例:简单的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("未连接");
    }

}

QUDP

不分客户端和服务端(代码都是一样的)
UDP广播和组播(广播地址:255.255.255.255)

UDP广播和组播只能在局域网范围内使用,广播发给所有用户,组播发给部分用户

qt学习笔记_第4张图片

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<<"套接字已经关闭";
    }
}

TCP传文件

服务端

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("断开连接");
}

day05

线程

注意:线程不参与图像界面的操作,只用于数据处理

两种方式
1、新建一个类直接继承QThread类,重写run函数
2、新建一个类,通过moveToThread将该类添加到子线程里,并执行其中的函数。

重写run函数的方式

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();
}

2、moveToThread方式

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);
}

你可能感兴趣的:(qt,学习,开发语言)