某内QT学习笔记

Qt(跨平台的C++ GUI应用程序开发框架) 安装与下载:

ubuntu下载 : https://mirrors.aliyun.com/ubuntu-releases/16.04/
下载地址:
http://download.qt.io/archive/qt/
ubuntu 32位下载:qt-opensource-linux-x86-5.4.1.run
ubuntu 64位下载:qt-opensource-linux-x64-5.4.1.run

ubuntu18.04建议下载最新版本:
qt-opensource-linux-x64-5.11.2.run

安装:
//添加可执行权限
chmod +x qt-opensource-linux-x64-5.x.x.run

//运行安装包,根据提示安装,默认安装在主目录下
./qt-opensource-linux-x64-5.x.x.run

一 Qt概述

1 Qt的发展历史
1)1991年诞生 Haavard Nord和Eirik Chame-Eng合作编写了最初的Qt.
2)1994年Haavard和Eirik创立TrollTech(奇趣科技)
3)2005年Qt4.0发布
4)2008年奇趣科技被诺基亚收购
5)2009年Qt源代码开放
6)2012年诺基亚将Qt业务和知识产权Digia公司
7)2013年Qt5.0发布
8)2014年Digia成立子公司The Qt Company
www.qt.io

2 Qt主要的工具软件

1)qmake(Qt构建器) 源代码构建可执行程序编译程序使用的

2)designer(Qt设计师) 界面开发编辑器 xml 产生.ui文件

在这里插入图片描述

3)uic(Qt转换器)把设计师提供的.ui文件转换成C++可以使用的.h文件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4)moc(元对象编译器) 预处理器,解决QT的语法扩展,还原成标准c++ 代码

5)rcc(资源编译器)resource cover 资源转换

6)assistant(Qt助手) 最重要

3 Qt帮助手册的使用

QTCreate 在需要函数上按下F1,直接就能跳到帮助手册内部

eg:通过Qt助手了解QApplication类的功能
1)在索引找到相应类,跳转到该类帮助手册界面
2)读一句话,了解该类功能,如果看不懂,那么可以点击"more"连接,跳转到详细说明。
3)接着往下看,了解头文件,构建选项(QT += widgets),继承关系
4)Properties(成员变量)
5)Public Functions(公有的成员函数)
6)Reimplemented Public Functions(公有虚函数)
7)Public Slots(公有的槽)//本质还是成员函数,属于Qt语法扩展

8)Signals(信号)//本质还是成员函数,属于Qt语法扩展

在这里插入图片描述
在这里插入图片描述

send,发送者,指针, 
signal  事件(信号函数,可以是自定义的信号,也可以是系统函数),可以带参数的类型,但不能带形参
 revcevier 接受者(对象,指针)  
 slot (处理函数)

在这里插入图片描述

9)Static Public Members(静态公有成员)
10)Reimplemented Protected Functions(保护虚函数)
11)Detailed Description(详细描述)

vim:

shift+ g 跳到最后一行
o 在下一行添加一个空行
: set fileencoding    查看文件编码
:set fileencoding=unicode   修改文件的编码方式  

在这里插入图片描述

二 第一个Qt程序

1 创建工程目录

mkdir Hello
注:每个Qt程序都要放在一个独立的工程目录下

2 进入工程目录,编写源代码(cpp)

cd Hello
vi main.cpp

#include <QApplication>
#include <QLabel>

int main (int argc, char** argv){


 // create QT Application  创建QT应用

        QApplication app(argc, argv);
// 创建控件
        QLabel label("hello, QtLabel");
        
 // show  label 显示控件
        label.show();
 // application program event loop进入事件循环
        return  app.exec();
        }

3 执行“qmake -project”,生成工程文件“Hello.pro”

vi Hello.pro
QT += widgets //表示需要使用widgets模块,将来会连接和该模块对应的头文件及库文件

######################################################################
# Automatically generated by qmake (3.0) ?? 12? 27 16:34:45 2023
######################################################################

TEMPLATE = app
TARGET = hello
INCLUDEPATH += .
QT += widgets
SOURCES += hello.cpp
# Directories

4 执行"qmake",根据前面的“.pro”生成Makefile

注意,第三步和第四步有错误,一般都是系统环境的问题

5 执行"make",完成编译和链接

注:正常可以得到和工程名字一致的可执行程序,如果有语法错误,修改以后再次执行make即可,不用重复3、4两步

6 测试运行

执行“./Hello”,会在系统桌面上看到Qt图形窗口

xyy@xyy-pc:~/QTstudy/01_hello$ make
g++ -c -pipe -O2 -Wall -W -D_REENTRANT -fPIE -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -I. -I../../Qt5.4.1/5.4/gcc_64/include -I../../Qt5.4.1/5.4/gcc_64/include/QtWidgets -I../../Qt5.4.1/5.4/gcc_64/include/QtGui -I../../Qt5.4.1/5.4/gcc_64/include/QtCore -I. -I../../Qt5.4.1/5.4/gcc_64/mkspecs/linux-g++ -o hello.o hello.cpp
g++ -Wl,-O1 -Wl,-rpath,/home/xyy/Qt5.4.1/5.4/gcc_64 -Wl,-rpath,/home/xyy/Qt5.4.1/5.4/gcc_64/lib -o hello hello.o   -L/home/xyy/Qt5.4.1/5.4/gcc_64/lib -lQt5Widgets -lQt5Gui -lQt5Core -lGL -lpthread 
/usr/bin/ld: 找不到 -lGL
collect2: error: ld returned 1 exit status
Makefile:201: recipe for target 'hello' failed
make: *** [hello] Error 1


出现这个错误的时候,说明缺少openGL 库,下载重新链接就好了

在这里插入图片描述
在这里插入图片描述

三 QString字符串和字符编码

1 常见编码

1)linux默认utf-8,变长的,中文一般是3个字节的
2)windows默认GBK/GB2312/GB18030
3)QString使用unicode(utf-16),并没有具体规定应该如何存取编码,双字节,默认文件是 utf8,QString默认char* 使用formutf8()函数来转换

2 编码转换 QTextCodec 文本编码转换

//类似于 Char []
QByteArray encodedString = “GBK编码中文字符串”;
//1)创建GBK编码对象
QTextCodec *codec = QTextCodec::codecForName(“GBK”);
//2)将GBK编码的字符串转换为unicode编码的字符串
QString string = codec->toUnicode(encodedString);

练习:创建Qt应用程序,里面包含一个标签(QLabel)和一个按钮(QPushButton),标签显示文本“我是标签”,按钮显示文本“我是按钮”。
1)正常构建,6步,结果可以正常显示
2)通过vi的底行命令,设置编码方式为GBK,再次构建,结果显示乱码。
:set fileencoding=gbk
3)通过QTextCodec进行编码转换,结果又可以正确显示

#include <QApplication>
#include <QLabel>
#include <QPushButton>
#include <QTextCodec>


int main(int argc, char** argv){
        QApplication app(argc, argv);
        QTextCodec* codec = QTextCodec::codecForName("GBK");
        QString qstr1= codec->toUnicode("ÎÒÊDZêÇ©");
        QString qstr2= codec->toUnicode("ÎÒÊÇ°´¼ü");
        QLabel label(qstr1);
        QPushButton pbutton(qstr2);
        label.show();
        pbutton.show();
        return app.exec();
}

//.pro:
######################################################################
# Automatically generated by qmake (3.0) ?? 12? 27 17:28:18 2023
######################################################################

TEMPLATE = app
TARGET = 02_PushButton
INCLUDEPATH += .
QT += widgets

# Input
SOURCES += PushButton.cpp


四 父窗口(容器窗口)

1 在创建控件可以指定停靠在父窗口上面,如果没有指定,该控件将飘在外面形成独立窗口.

2 常用的父窗口的类

1)QWidget//和图形控件相关的基类
2)QMainWindow(主窗口)//QWidget一个子类, 适合复杂的窗口
3)QDialog(对话框)//QWidget另一个子类,适用于简单的窗口

3 QWidget类中两个常用函数,单位是像素
1)调整窗口或控件大小:resize(int w,int h)
2)调整窗口或控件位置:move(int x,int y)

#include <QApplication>
#include <QLabel>
#include <QPushButton>
#include <QTextCodec>
#include <QWidget>
#include <QMainWindow>
#include <QDialog>


int main(int argc, char** argv){

        QApplication app(argc, argv);


//      QWidget parent; //窗口父窗口控件
//      QMainWindow parent;
        QDialog parent;
        parent.resize(480, 300);
        parent.move(100, 100);
//      parent.show();


// dock to the parent window
        QTextCodec* qtc= QTextCodec::codecForName("GBK");
        QLabel label(qtc->toUnicode("this is a label"),&parent);
        QPushButton button(qtc->toUnicode("this is a button"), &parent);
        label.resize(460, 100);
        button.resize(460, 100);
        label.move(0, 0);
        button.move(0, 100);
        parent.show();

        return app.exec();

}
// .pro:
######################################################################
# Automatically generated by qmake (3.0) ?? 12? 27 20:06:45 2023
######################################################################
QT += widgets

TEMPLATE = app
TARGET = 03_Parent
INCLUDEPATH += .

# Input
SOURCES += parent.cpp

在这里插入图片描述

#include <QApplication>
#include <QLabel>
#include <QPushButton>
#include <QTextCodec>
#include <QWidget>
#include <QMainWindow>
#include <QDialog>


int main(int argc, char** argv){

        QApplication app(argc, argv);


//      QWidget parent; //窗口父窗口控件
//      QMainWindow parent;
        QDialog parent;
        parent.resize(480, 300);
        parent.move(100, 100);
//      parent.show();


// dock to the parent window
        QTextCodec* qtc= QTextCodec::codecForName("GBK");
        QLabel label(qtc->toUnicode("this is a label"),&parent);
        QPushButton button(qtc->toUnicode("this is a button"), &parent);
        label.resize(460, 100);
        button.resize(100,50);
        label.move(0, 0);
        button.move(0, 100);
        
//new  对象如果指定父窗口, 可以不写delete, 因为在父窗口销毁的时候会自动被delete
        QPushButton* btn2= new  QPushButton(qtc->toUnicode("btn2"),&parent);
        btn2->resize(100,50);
        btn2->move(100,100);
        parent.show();

        return app.exec();

}

五 信号和槽 //重点

1 信号和槽是Qt自行定义一种通信机制,实现对象之间的交互,当某个对象状态改变时,将会发射信号,该信号可以被其它对象所接收,接收以后会执行一个指定成员函数,即为槽.

2 定义

 class QXX:public QObject{
  		Q_OBJECT //moc:将信号和槽语法扩展还原为标准C++代码
  singals:
  		void sig_func();//信号
  public slots:
  		void slot_func();//槽
  };

注:信号函数只需声明,不能定义;槽函数可以通过某个信号触发执行,也可以看做是一个普通成员函数直接调用;

3 信号和槽连接

QObject::connect(
	const QObject * sender, 
	const char * signal, 
	const QObject * receiver, 
	const char * method);
参数:
	@sender:信号的发送对象,可以是QObject任意子类对象
	@signal:要发送的信号函数
	@receiver:信号的接收对象,可以是QObject任意子类对象
	@method:要执行的槽函数
	
	SIGNAL(clicked(void)):将信号函数转换为const char*
	SLOT(close(void)):将槽函数转换为const char*

在这里插入图片描述

案例:创建Qt应用程序对象,包含一个label和一个button,实现点击按钮关闭标签功能。

#include <QApplication>

#include <QLabel>
#include <QPushButton>
#include <QDialog>
#include <QTextCodec>
int main(int argc, char**argv){
        QApplication app(argc, argv);
        QTextCodec* qCode = QTextCodec::codecForName("GBK");
        QDialog parent;
        QLabel * qLab= new QLabel(qCode->toUnicode("label"),&parent);
        QPushButton* qBtn1= new QPushButton(qCode->toUnicode("close label"),&parent);


        QPushButton* qBtn2= new QPushButton(qCode->toUnicode("open label"),&parent);
        parent.resize(400, 300);
        parent.move(100, 500);
        qLab->resize(100, 50);
        qBtn1->resize(100, 40);
        qBtn2->resize(100, 40);
        qLab->move(100,50);
        qBtn1->move(100, 100);

        qBtn2->move(200, 100);
        QObject::connect(qBtn1,SIGNAL(clicked(void)),qLab,SLOT(close(void)));
        QObject::connect(qBtn2,SIGNAL(clicked(void)),qLab,SLOT(show(void)));
        parent.show();

        return app.exec();
}

//.pro:
  ######################################################################
# Automatically generated by qmake (3.0) ?? 12? 27 21:48:49 2023
######################################################################
QT += widgets
TEMPLATE = app
TARGET = 04_SignalAndSlot
INCLUDEPATH += .

# Input
SOURCES += signalAndSlot.cpp
                                                            

在这里插入图片描述

4 信号和槽连接的语法要求

1 信号和槽参数要一致

QObject::connect(A,SIGNAL(sigfun(int)),
				B,SLOT(slotfun(int)));//ok
QObject::connect(A,SIGNAL(sigfun(void)),
				B,SLOT(slotfun(int)));//error

2 可以带有缺省值

QObject::connect(A,SIGNAL(sigfun(void)),
				B,SLOT(slotfun(int=0)));//ok

3 信号函数参数可以比槽函数多,多余参数将被忽略

QObject::connect(A,SIGNAL(sigfun(int,int)),
				B,SLOT(slotfun(int)));//ok

4 一个信号可以同时连接多个槽函数(一对多)

	QObject::connect(A,SIGNAL(sigfun(void)),
				B1,SLOT(slotfun1(void)));
	QObject::connect(A,SIGNAL(sigfun(void)),
				B2,SLOT(slotfun2(void)));
注:当A对象发送信号后,B1和B2槽函数都将被执行,但是顺序不确定.

5 多个信号连接到同一个槽函数(多对一)

QObject::connect(A1,SIGNAL(sigfun1(void)),
				B,SLOT(slotfun(void)));
	QObject::connect(A2,SIGNAL(sigfun2(void)),
				B,SLOT(slotfun(void)));
注:无论A1或A2发送信号,B的槽函数都将被执行

6 两个信号可以直接连接//了解(信号级联)

QObject::connect(A1,SIGNAL(sigfun1(void)),
				A2,SIGNAL(sigfun2(int)));
实例:事件同步 滑块和选值框同步运行

1)滑块:QSlider
QSlider(垂直/水平,父窗口)//构造函数
void setRange(最小值,最大值)//设置滑块的滑动范围
void valueChanged(int value)[signal]//滑块滑动时发送信号
void setValue(int)[slot]//设置滑块滑动位置

2)选值框:QSpinBox
QSpinBox(父窗口)//构造函数
void setRange(最小值,最大值)//设置选值框数值改变范围
void valueChanged(int value)[signal]//数值改变时发送信号
void setValue(int)[slot]//设置选值框当前数值

#include <QApplication>
#include <QLabel>
#include <QSpinBox>
#include <QSlider>
#include <QTextCodec>
#include <QMainWindow>
int main(int argc, char**argv){
        QApplication app(argc, argv);
        QTextCodec* qCode= QTextCodec::codecForName("GBK");
        QMainWindow parent;
        QLabel* pLab= new QLabel(qCode->toUnicode("please choose:"),&parent);
        QSlider * pSld= new QSlider(Qt::Horizontal, &parent);
        QSpinBox* pSpin= new QSpinBox(&parent);
        parent.resize(330, 200);
        pLab->resize(100, 50);
        pSpin->resize(300, 50);
        pSld->resize(160, 50);
        pLab->move(20,30);
        pSld->move(140, 30);
        pSpin->move(20,100);



        unsigned int offset= 0;
        pSld->setRange(0, 100);

        pSld->setValue(offset);
        //if(pslid->)


        pSpin->setRange(0, 100);





	//选值框改变的是时候 滑块随之改变
        QObject::connect(pSpin,SIGNAL(valueChanged(int)),pSld,SLOT(setValue(int)));
	//滑块滑动的时候, 选值框也随之改变
        QObject::connect(pSld,SIGNAL(valueChanged(int)), pSpin,SLOT(setValue(int)));

  parent.show();







        return app.exec();
}

//.pro:
######################################################################
# Automatically generated by qmake (3.0) ?? 12? 28 10:16:16 2023
######################################################################

QT += widgets
TEMPLATE = app
TARGET = 05_SpinAndSlider
INCLUDEPATH += .

# Input
SOURCES += spinAndSlider.cpp



面向对象的Qt编程

1 基于对象的Qt编程 //不推荐
2 面向对象的Qt编程

案例:加法计算器

需求:
1)左右操作数只能输入数字
2)等号按钮初始化禁用状态(灰色不可点击)
3)等左右操作数都输入了有效数据,在恢复按钮为使能状态
4)点击等号按钮,计算结果并显示
思路:
class CalculatorDialog:public QDialog{
Q_OBJECT //moc
public:
构造函数(){
//界面初始化
}
public slots://自定义槽函数
void 检查左右操作数并使能等号按钮的槽函数(void){…}
void 计算结果和显示(void){…}
private:
QLineEdit、QLabel、QPushButton
};
int main(int argc,char** argv){
QApplication app(argc,argv);
CalculatorDialog calc;
calc.show();
return app.exec();
}

注:构建时,如果有自定义信号或槽,会将相应头文件用moc处理为标准的C++代码
moc CalculatorDialog.h -o moc_CalculatorDialog.cpp

在这里插入图片描述

main.cpp:
#include <QApplication>
#include "CalculatorDialog.h"

int main(int argc,char** argv)
{
    QApplication app(argc,argv);
    
    CalculatorDialog calc;
    calc.show();

    return app.exec();
}



//CalculatorDialog.h:
#ifndef __CALCULATORDIALOG_H
#define __CALCULATORDIALOG_H

#include <QDialog>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>//行编辑控件
#include <QDoubleValidator>//数字验证器
#include <QHBoxLayout>//水平布局器
//继承Qt中父窗口类,当前的类就也是父窗口
class CalculatorDialog:public QDialog{
    Q_OBJECT
public:
    CalculatorDialog(void);//构造函数
public slots:
    //使能等号按钮的槽函数
    void enableButton(void);
    //计算和显示结果的槽函数
    void calcClicked(void);
private:
    QLineEdit* m_editX;//左操作数
    QLineEdit* m_editY;//右操作数
    QLineEdit* m_editZ;//显示结果
    QLabel* m_label;//“+”
    QPushButton* m_button;//“=”
    QHBoxLayout* m_layout;//水平布局器
};

#endif//__CALCULATORDIALOG_H





//CalculatorDialg.cpp:
#include "CalculatorDialog.h"
#include <QFont>
//构造函数
CalculatorDialog::CalculatorDialog(void){
    //界面初始化
    setWindowTitle("加法计算器");
    
    QFont font;
    font.setPointSize(24);
    setFont(font);//设置父窗口字体

    //左操作数,this表示父窗口指针
    m_editX = new QLineEdit(this);
    //设置文本对齐方式:文本右对齐
    m_editX->setAlignment(Qt::AlignRight);
    //设置数字验证,让其输入数字形式的内容
    m_editX->setValidator(
            new QDoubleValidator(this));
    //m_editX->setFont(font);

    //右操作数
    m_editY = new QLineEdit(this);
    m_editY->setAlignment(Qt::AlignRight);
    m_editY->setValidator(
            new QDoubleValidator(this));
    //显示结果
    m_editZ = new QLineEdit(this);
    m_editZ->setAlignment(Qt::AlignRight);
    m_editZ->setReadOnly(true);//设置只读
    //"+"
    m_label = new QLabel("+",this);
    //"="
    m_button = new QPushButton("=",this);
    m_button->setEnabled(false);//设置禁用

    //创建水平布局器:自动调整控件大小和位置
    m_layout = new QHBoxLayout(this);
    //将控件按水平方向添加到布局器中
    m_layout->addWidget(m_editX);
    m_layout->addWidget(m_label);
    m_layout->addWidget(m_editY);
    m_layout->addWidget(m_button);
    m_layout->addWidget(m_editZ);
    //设置布局器
    setLayout(m_layout);

    //信号和槽函数连接
    //左右操作数文本改变,发送信号textChanged
    //如果连接的槽函数在当前父窗口类中自定义的,那
    //么第三个参数一定是"this".
    connect(m_editX,SIGNAL(textChanged(QString)),
            this,SLOT(enableButton(void)));
    connect(m_editY,SIGNAL(textChanged(QString)),
            this,SLOT(enableButton(void)));
    //点击等号按钮时发,送信号clicked
    connect(m_button,SIGNAL(clicked(void)),
            this,SLOT(calcClicked(void)));
}
//使能等号按钮的槽函数
void CalculatorDialog::enableButton(void){
    bool bXOk;
    bool bYOk;
    //text():获取输入的文本内容(QString)
    //toDouble():QString转换为double,参数保存
    //转换是否成功结果
    m_editX->text().toDouble(&bXOk);
    m_editY->text().toDouble(&bYOk);
    //如果左右操作数都已经输入有效数据则使能等号
    m_button->setEnabled(bXOk && bYOk);
}
//计算和显示结果的槽函数
void CalculatorDialog::calcClicked(void){
    //计算结果
    double res = m_editX->text().toDouble() +
            m_editY->text().toDouble();
    //将计算结果转换为QString,再显示
    QString str = QString::number(res);
    m_editZ->setText(str);
}







六 Qt设计师使用(designer)

案例:通过设计师重构加法计算器程序
1 创建工程目录
mkdir Calculator2
2 进入工程目录,执行“designer”进入设计师界面

3 选择模板(父窗口),Dialog Without Button 没有任何控件窗口
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4 完成界面设计
1)从左边的“widget box”中找到需要的控件拖拽到父窗口上面
PushButton LineEdit Label
2)设置父窗口和每个控件属性
–》父窗口
objectName(对象名):CalculatorDialog
注:将来会根据父窗口的对象名生成相同名字的类
font(字体):点大小(20)
windowTitle(窗口标题):加法计算器
–》左操作数:
objectName(对象名):m_editX
alignment:水平(AlignRight)
–》右操作数:
objectName(对象名):m_editY
alignment:水平(AlignRight)
–》显示结果:
objectName(对象名):m_editZ
alignment:水平(AlignRight)
readOnly:勾选√
–》加号
objectName:m_label
text:“+”
–》等号
objectName:m_button
enabled:去掉勾选√
text:“=”
3)调整父窗口以及每个控件的大小和位置
方法一:鼠标拖拽
方法二:键盘,ctrl/shift+方向键
方法三:设置geometry,位置(x,y) 大小(宽度,高度)
方法四:使用布局器//推荐

4)窗体-》预览

5)保存(ctrl+s),指定文件名CalculatorDialog.ui

5 将xx.ui(xml)转换为C++的文件ui_xx.h(cpp)
uic CalculatorDialog.ui -o ui_CalculatorDialog.h

ui_CalculatorDialog.h内容:
class Ui_CalculatorDialog{
public:
图形控件相关声明;
void setupUi(父窗口指针){
//界面初始化
}
};
namespace Ui{
class CalculatorDialog:public Ui_CalculatorDialog{};
}
Ui::CalculatorDialog<=等价=>Ui_CalculatorDialog

6 编写代码

使能界面相关代码的方法:

方法一:继承//参考Calculator2

class MyClass:public Ui::CalculatorDialog{
	//将来界面相关代码继承过来直接使用
};
//ui_CalculatorDialog.h:
/********************************************************************************
** Form generated from reading UI file 'CalculatorDialog.ui'
**
** Created by: Qt User Interface Compiler version 5.4.1
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_CALCULATORDIALOG_H
#define UI_CALCULATORDIALOG_H

#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QDialog>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>

QT_BEGIN_NAMESPACE

class Ui_CalculatorDialog
{
public:
    QHBoxLayout *horizontalLayout;
    QLineEdit *m_editX;
    QLabel *m_label;
    QLineEdit *m_editY;
    QPushButton *m_button;
    QLineEdit *m_editZ;

    void setupUi(QDialog *CalculatorDialog)
    {
        if (CalculatorDialog->objectName().isEmpty())
            CalculatorDialog->setObjectName(QStringLiteral("CalculatorDialog"));
        CalculatorDialog->resize(463, 162);
        QFont font;
        font.setPointSize(20);
        CalculatorDialog->setFont(font);
        horizontalLayout = new QHBoxLayout(CalculatorDialog);
        horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
        m_editX = new QLineEdit(CalculatorDialog);
        m_editX->setObjectName(QStringLiteral("m_editX"));
        m_editX->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);

        horizontalLayout->addWidget(m_editX);

        m_label = new QLabel(CalculatorDialog);
        m_label->setObjectName(QStringLiteral("m_label"));
        QFont font1;
        font1.setBold(true);
        font1.setItalic(false);
        font1.setWeight(75);
        m_label->setFont(font1);

        horizontalLayout->addWidget(m_label);

        m_editY = new QLineEdit(CalculatorDialog);
        m_editY->setObjectName(QStringLiteral("m_editY"));
        m_editY->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);

        horizontalLayout->addWidget(m_editY);

        m_button = new QPushButton(CalculatorDialog);
        m_button->setObjectName(QStringLiteral("m_button"));
        m_button->setEnabled(false);

        horizontalLayout->addWidget(m_button);

        m_editZ = new QLineEdit(CalculatorDialog);
        m_editZ->setObjectName(QStringLiteral("m_editZ"));
        m_editZ->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
        m_editZ->setReadOnly(true);

        horizontalLayout->addWidget(m_editZ);


        retranslateUi(CalculatorDialog);

        QMetaObject::connectSlotsByName(CalculatorDialog);
    } // setupUi

    void retranslateUi(QDialog *CalculatorDialog)
    {
        CalculatorDialog->setWindowTitle(QApplication::translate("CalculatorDialog", "\345\212\240\346\263\225\350\256\241\347\256\227\345\231\250", 0));
        m_label->setText(QApplication::translate("CalculatorDialog", "+", 0));
        m_button->setText(QApplication::translate("CalculatorDialog", "=", 0));
    } // retranslateUi

};

namespace Ui {
    class CalculatorDialog: public Ui_CalculatorDialog {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_CALCULATORDIALOG_H



//CalculatorDialog.h:
#ifndef __CALCULATORDIALOG_H
#define __CALCULATORDIALOG_H
#include "ui_CalculatorDialog.h"

//继承Qt中父窗口类,当前的类就也是父窗口
class CalculatorDialog
    :public QDialog,public Ui::CalculatorDialog{
    Q_OBJECT
public:
    CalculatorDialog(void);//构造函数
public slots:
    //使能等号按钮的槽函数
    void enableButton(void);
    //计算和显示结果的槽函数
    void calcClicked(void);
/*private:
    QLineEdit* m_editX;//左操作数
    QLineEdit* m_editY;//右操作数
    QLineEdit* m_editZ;//显示结果
    QLabel* m_label;//“+”
    QPushButton* m_button;//“=”
    QHBoxLayout* m_layout;//水平布局器
*/
};

#endif//__CALCULATORDIALOG_H





//CalculatorDialog.cpp:
#include "CalculatorDialog.h"
#include <QFont>
//构造函数
CalculatorDialog::CalculatorDialog(void){
    //界面初始化
    setupUi(this);
    //设置数字验证,让其输入数字形式的内容
    m_editX->setValidator(
            new QDoubleValidator(this));
    m_editY->setValidator(
            new QDoubleValidator(this));

    //信号和槽函数连接
    //左右操作数文本改变,发送信号textChanged
    //如果连接的槽函数在当前父窗口类中自定义的,那
    //么第三个参数一定是"this".
    connect(m_editX,SIGNAL(textChanged(QString)),
            this,SLOT(enableButton(void)));
    connect(m_editY,SIGNAL(textChanged(QString)),
            this,SLOT(enableButton(void)));
    //点击等号按钮时发,送信号clicked
    connect(m_button,SIGNAL(clicked(void)),
            this,SLOT(calcClicked(void)));
}
//使能等号按钮的槽函数
void CalculatorDialog::enableButton(void){
    bool bXOk;
    bool bYOk;
    //text():获取输入的文本内容(QString)
    //toDouble():QString转换为double,参数保存
    //转换是否成功结果
    m_editX->text().toDouble(&bXOk);
    m_editY->text().toDouble(&bYOk);
    //如果左右操作数都已经输入有效数据则使能等号
    m_button->setEnabled(bXOk && bYOk);
}
//计算和显示结果的槽函数
void CalculatorDialog::calcClicked(void){
    //计算结果
    double res = m_editX->text().toDouble() +
            m_editY->text().toDouble();
    //将计算结果转换为QString,再显示
    QString str = QString::number(res);
    m_editZ->setText(str);
}

//main.cpp:
#include <QApplication>
#include "CalculatorDialog.h"

int main(int argc,char** argv)
{
    QApplication app(argc,argv);
    
    CalculatorDialog calc;
    calc.show();

    return app.exec();
}


方法二:组合//参考Calculator2_2

class MyClass{
public:
	MyClass(void):ui(new Ui::CalculatorDialog){}
private:
	//将来可以通过"ui->"访问和界面相关代码
	Ui::CalculatorDialog* ui;
};
//ui_CalculatorDialog.h:
/********************************************************************************
** Form generated from reading UI file 'CalculatorDialog.ui'
**
** Created by: Qt User Interface Compiler version 5.4.1
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_CALCULATORDIALOG_H
#define UI_CALCULATORDIALOG_H

#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QDialog>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>

QT_BEGIN_NAMESPACE

class Ui_CalculatorDialog
{
public:
    QHBoxLayout *horizontalLayout;
    QLineEdit *m_editX;
    QLabel *m_label;
    QLineEdit *m_editY;
    QPushButton *m_button;
    QLineEdit *m_editZ;

    void setupUi(QDialog *CalculatorDialog)
    {
        if (CalculatorDialog->objectName().isEmpty())
            CalculatorDialog->setObjectName(QStringLiteral("CalculatorDialog"));
        CalculatorDialog->resize(463, 162);
        QFont font;
        font.setPointSize(20);
        CalculatorDialog->setFont(font);
        horizontalLayout = new QHBoxLayout(CalculatorDialog);
        horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
        m_editX = new QLineEdit(CalculatorDialog);
        m_editX->setObjectName(QStringLiteral("m_editX"));
        m_editX->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);

        horizontalLayout->addWidget(m_editX);

        m_label = new QLabel(CalculatorDialog);
        m_label->setObjectName(QStringLiteral("m_label"));
        QFont font1;
        font1.setBold(true);
        font1.setItalic(false);
        font1.setWeight(75);
        m_label->setFont(font1);

        horizontalLayout->addWidget(m_label);

        m_editY = new QLineEdit(CalculatorDialog);
        m_editY->setObjectName(QStringLiteral("m_editY"));
        m_editY->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);

        horizontalLayout->addWidget(m_editY);

        m_button = new QPushButton(CalculatorDialog);
        m_button->setObjectName(QStringLiteral("m_button"));
        m_button->setEnabled(false);

        horizontalLayout->addWidget(m_button);

        m_editZ = new QLineEdit(CalculatorDialog);
        m_editZ->setObjectName(QStringLiteral("m_editZ"));
        m_editZ->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
        m_editZ->setReadOnly(true);

        horizontalLayout->addWidget(m_editZ);


        retranslateUi(CalculatorDialog);

        QMetaObject::connectSlotsByName(CalculatorDialog);
    } // setupUi

    void retranslateUi(QDialog *CalculatorDialog)
    {
        CalculatorDialog->setWindowTitle(QApplication::translate("CalculatorDialog", "\345\212\240\346\263\225\350\256\241\347\256\227\345\231\250", 0));
        m_label->setText(QApplication::translate("CalculatorDialog", "+", 0));
        m_button->setText(QApplication::translate("CalculatorDialog", "=", 0));
    } // retranslateUi

};

namespace Ui {
    class CalculatorDialog: public Ui_CalculatorDialog {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_CALCULATORDIALOG_H




//CalculatorDialog.h:
#ifndef __CALCULATORDIALOG_H
#define __CALCULATORDIALOG_H
#include "ui_CalculatorDialog.h"

//继承Qt中父窗口类,当前的类就也是父窗口
class CalculatorDialog
    :public QDialog{
    Q_OBJECT
public:
    CalculatorDialog(void);//构造函数
public slots:
    //使能等号按钮的槽函数
    void enableButton(void);
    //计算和显示结果的槽函数
    void calcClicked(void);
private:
    Ui::CalculatorDialog* ui;
/*private:
    QLineEdit* m_editX;//左操作数
    QLineEdit* m_editY;//右操作数
    QLineEdit* m_editZ;//显示结果
    QLabel* m_label;//“+”
    QPushButton* m_button;//“=”
    QHBoxLayout* m_layout;//水平布局器
*/
};

#endif//__CALCULATORDIALOG_H

//CalculatorDialog.cpp:
#include "CalculatorDialog.h"
#include <QFont>
//构造函数
CalculatorDialog::CalculatorDialog(void)
    :ui(new Ui::CalculatorDialog){
    //界面初始化
    ui->setupUi(this);
    //设置数字验证,让其输入数字形式的内容
    ui->m_editX->setValidator(
            new QDoubleValidator(this));
    ui->m_editY->setValidator(
            new QDoubleValidator(this));

    //信号和槽函数连接
    //左右操作数文本改变,发送信号textChanged
    //如果连接的槽函数在当前父窗口类中自定义的,那
    //么第三个参数一定是"this".
    connect(ui->m_editX,SIGNAL(textChanged(QString)),
            this,SLOT(enableButton(void)));
    connect(ui->m_editY,SIGNAL(textChanged(QString)),
            this,SLOT(enableButton(void)));
    //点击等号按钮时发,送信号clicked
    connect(ui->m_button,SIGNAL(clicked(void)),
            this,SLOT(calcClicked(void)));
}
//使能等号按钮的槽函数
void CalculatorDialog::enableButton(void){
    bool bXOk;
    bool bYOk;
    //text():获取输入的文本内容(QString)
    //toDouble():QString转换为double,参数保存
    //转换是否成功结果
    ui->m_editX->text().toDouble(&bXOk);
    ui->m_editY->text().toDouble(&bYOk);
    //如果左右操作数都已经输入有效数据则使能等号
    ui->m_button->setEnabled(bXOk && bYOk);
}
//计算和显示结果的槽函数
void CalculatorDialog::calcClicked(void){
    //计算结果
    double res = ui->m_editX->text().toDouble() +
            ui->m_editY->text().toDouble();
    //将计算结果转换为QString,再显示
    QString str = QString::number(res);
    ui->m_editZ->setText(str);
}

//main.cpp:
#include <QApplication>
#include "CalculatorDialog.h"

int main(int argc,char** argv)
{
    QApplication app(argc,argv);
    
    CalculatorDialog calc;
    calc.show();

    return app.exec();
}









7 构建、测试…

案例:获取系统时间

1)界面设计:TimeDialog.ui
–》父窗口(TimeDialog)
–》m_label
frameShape:Panel
frameShadow:Sunken
alignment:水平(AlignHCenter)
styleSheet(样式表):可以设置颜色
添加颜色->color(选择文本颜色)
添加颜色->background-color(选择背景颜色)
–》m_button

继承的方法:
// ui_Currenttime.h:
/********************************************************************************
** Form generated from reading UI file 'CurrentTime.ui'
**
** Created by: Qt User Interface Compiler version 5.4.1
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_CURRENTTIME_H
#define UI_CURRENTTIME_H

#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QDialog>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>

QT_BEGIN_NAMESPACE

class Ui_CurrentTime
{
public:
    QPushButton *m_PusBtn;
    QLabel *m_lab;

    void setupUi(QDialog *CurrentTime)
    {
        if (CurrentTime->objectName().isEmpty())
            CurrentTime->setObjectName(QStringLiteral("CurrentTime"));
        CurrentTime->resize(197, 123);
        m_PusBtn = new QPushButton(CurrentTime);
        m_PusBtn->setObjectName(QStringLiteral("m_PusBtn"));
        m_PusBtn->setGeometry(QRect(41, 86, 99, 27));
        m_lab = new QLabel(CurrentTime);
        m_lab->setObjectName(QStringLiteral("m_lab"));
        m_lab->setGeometry(QRect(36, 30, 111, 31));
        QFont font;
        font.setPointSize(24);
        m_lab->setFont(font);

        retranslateUi(CurrentTime);

        QMetaObject::connectSlotsByName(CurrentTime);
    } // setupUi

    void retranslateUi(QDialog *CurrentTime)
    {
        CurrentTime->setWindowTitle(QApplication::translate("CurrentTime", "Dialog", 0));
        m_PusBtn->setText(QApplication::translate("CurrentTime", "getTime", 0));
        m_lab->setText(QString());
    } // retranslateUi

};

namespace Ui {
    class CurrentTime: public Ui_CurrentTime {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_CURRENTTIME_H


//main.cpp:
#include <CurrentTimeDialog.h>
#include <QApplication>

int main (int argc, char** argv){
	QApplication app(argc, argv);

	CurrentTimeDialog  ct;
	ct.show();
	return app.exec();
}


//Current

组合的方法:

//ui_TimeDialog.h:
/********************************************************************************
** Form generated from reading UI file 'TimeDialog.ui'
**
** Created by: Qt User Interface Compiler version 5.4.1
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_TIMEDIALOG_H
#define UI_TIMEDIALOG_H

#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QDialog>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QVBoxLayout>

QT_BEGIN_NAMESPACE

class Ui_TimeDialog
{
public:
    QVBoxLayout *verticalLayout;
    QLabel *m_label;
    QPushButton *m_button;

    void setupUi(QDialog *TimeDialog)
    {
        if (TimeDialog->objectName().isEmpty())
            TimeDialog->setObjectName(QStringLiteral("TimeDialog"));
        TimeDialog->resize(236, 219);
        QFont font;
        font.setPointSize(20);
        TimeDialog->setFont(font);
        verticalLayout = new QVBoxLayout(TimeDialog);
        verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
        m_label = new QLabel(TimeDialog);
        m_label->setObjectName(QStringLiteral("m_label"));
        QFont font1;
        font1.setPointSize(22);
        m_label->setFont(font1);
        m_label->setStyleSheet(QLatin1String("background-color: rgb(185, 226, 239);\n"
"color: rgb(62, 12, 12);"));
        m_label->setFrameShape(QFrame::Panel);
        m_label->setFrameShadow(QFrame::Sunken);
        m_label->setAlignment(Qt::AlignCenter);

        verticalLayout->addWidget(m_label);

        m_button = new QPushButton(TimeDialog);
        m_button->setObjectName(QStringLiteral("m_button"));

        verticalLayout->addWidget(m_button);


        retranslateUi(TimeDialog);

        QMetaObject::connectSlotsByName(TimeDialog);
    } // setupUi

    void retranslateUi(QDialog *TimeDialog)
    {
        TimeDialog->setWindowTitle(QApplication::translate("TimeDialog", "\346\227\266\351\227\264", 0));
        m_label->setText(QString());
        m_button->setText(QApplication::translate("TimeDialog", "\350\216\267\345\217\226\346\227\266\351\227\264", 0));
    } // retranslateUi

};

namespace Ui {
    class TimeDialog: public Ui_TimeDialog {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_TIMEDIALOG_H

//TimeDialog.h:
#ifndef __TIMEDIALOG_H
#define __TIMEDIALOG_H

#include "ui_TimeDialog.h"
#include <QTime>

class TimeDialog
    :public QDialog{
    Q_OBJECT
public:  
    TimeDialog(void);
public slots:
    //自定义槽函数获取系统时间
    void getTime(void);
private:
    Ui::TimeDialog* ui;
};


#endif//__TIMEDIALOG_H

// TimeDialog.CPP;
#include "TimeDialog.h"
//构造函数
TimeDialog::TimeDialog(void)
    :ui(new Ui::TimeDialog){
    //ui = new Ui::TimeDialog;
    ui->setupUi(this);
    connect(ui->m_button,SIGNAL(clicked(void)),
            this,SLOT(getTime(void)));
}
//获取系统时间
void TimeDialog::getTime(void){
    QTime time = QTime::currentTime();
    QString str = time.toString("hh:mm:ss");
    ui->m_label->setText(str);
}


//main.cpp:
#include <QApplication>
#include "TimeDialog.h"

int main(int argc,char** argv)
{
    QApplication app(argc,argv);

    TimeDialog time;
    time.show();

    return app.exec();
}


//Time.pro:

######################################################################
# Automatically generated by qmake (3.0) Mon Jun 3 09:34:25 2019
######################################################################
QT += widgets
TEMPLATE = app
TARGET = Time
INCLUDEPATH += .

# Input
HEADERS += TimeDialog.h ui_TimeDialog.h
FORMS += TimeDialog.ui
SOURCES += main.cpp TimeDialog.cpp





案例:登录对话框

1)创建工程目录
mkdir Login
2)进入工程目录,启动设计师,完成界面设计
–》父窗口:LoginDialog
font:点大小(20)
windowTitle:登录
–》输入用户名:m_editUsername
echoMode:password
–》输入密码:m_editPassword
–》按钮框:m_btnBox
layoutDirection:RightToLeft
–》布局调整大小位置
–》保存(ctrl+s),LoginDialog.ui
3)编码…

继承的方式
//ui.h:
/********************************************************************************
** Form generated from reading UI file 'LoginDialog.ui'
**
** Created by: Qt User Interface Compiler version 5.4.1
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_LOGINDIALOG_H
#define UI_LOGINDIALOG_H

#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QDialog>
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QSpacerItem>
#include <QtWidgets/QVBoxLayout>

QT_BEGIN_NAMESPACE

class Ui_LoginDialog
{
public:
    QVBoxLayout *verticalLayout;
    QSpacerItem *verticalSpacer;
    QGridLayout *gridLayout;
    QLabel *label;
    QLineEdit *m_editUsername;
    QLabel *label_2;
    QLineEdit *m_editPassword;
    QHBoxLayout *horizontalLayout;
    QSpacerItem *horizontalSpacer;
    QDialogButtonBox *m_btnBox;
    QSpacerItem *horizontalSpacer_2;
    QSpacerItem *verticalSpacer_2;

    void setupUi(QDialog *LoginDialog)
    {
        if (LoginDialog->objectName().isEmpty())
            LoginDialog->setObjectName(QStringLiteral("LoginDialog"));
        LoginDialog->resize(403, 230);
        QFont font;
        font.setPointSize(20);
        LoginDialog->setFont(font);
        verticalLayout = new QVBoxLayout(LoginDialog);
        verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
        verticalSpacer = new QSpacerItem(20, 28, QSizePolicy::Minimum, QSizePolicy::Expanding);

        verticalLayout->addItem(verticalSpacer);

        gridLayout = new QGridLayout();
        gridLayout->setObjectName(QStringLiteral("gridLayout"));
        label = new QLabel(LoginDialog);
        label->setObjectName(QStringLiteral("label"));

        gridLayout->addWidget(label, 0, 0, 1, 1);

        m_editUsername = new QLineEdit(LoginDialog);
        m_editUsername->setObjectName(QStringLiteral("m_editUsername"));

        gridLayout->addWidget(m_editUsername, 0, 1, 1, 1);

        label_2 = new QLabel(LoginDialog);
        label_2->setObjectName(QStringLiteral("label_2"));

        gridLayout->addWidget(label_2, 1, 0, 1, 1);

        m_editPassword = new QLineEdit(LoginDialog);
        m_editPassword->setObjectName(QStringLiteral("m_editPassword"));
        m_editPassword->setEchoMode(QLineEdit::Password);

        gridLayout->addWidget(m_editPassword, 1, 1, 1, 1);


        verticalLayout->addLayout(gridLayout);

        horizontalLayout = new QHBoxLayout();
        horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
        horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);

        horizontalLayout->addItem(horizontalSpacer);

        m_btnBox = new QDialogButtonBox(LoginDialog);
        m_btnBox->setObjectName(QStringLiteral("m_btnBox"));
        m_btnBox->setLayoutDirection(Qt::RightToLeft);
        m_btnBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);

        horizontalLayout->addWidget(m_btnBox);

        horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);

        horizontalLayout->addItem(horizontalSpacer_2);


        verticalLayout->addLayout(horizontalLayout);

        verticalSpacer_2 = new QSpacerItem(20, 28, QSizePolicy::Minimum, QSizePolicy::Expanding);

        verticalLayout->addItem(verticalSpacer_2);


        retranslateUi(LoginDialog);

        QMetaObject::connectSlotsByName(LoginDialog);
    } // setupUi

    void retranslateUi(QDialog *LoginDialog)
    {
        LoginDialog->setWindowTitle(QApplication::translate("LoginDialog", "\347\231\273\345\275\225", 0));
        label->setText(QApplication::translate("LoginDialog", "\347\224\250\346\210\267\345\220\215\357\274\232", 0));
        label_2->setText(QApplication::translate("LoginDialog", "\345\257\206   \347\240\201\357\274\232", 0));
    } // retranslateUi

};

namespace Ui {
    class LoginDialog: public Ui_LoginDialog {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_LOGINDIALOG_H

//LoginDialog.h:
#ifndef __LOGINDIALOG_H
#define __LOGINDIALOG_H
#include "ui_LoginDialog.h"
#include <QMessageBox>//消息提示框
#include <QDebug>//打印调试
class LoginDialog
    :public QDialog,public Ui::LoginDialog{
    Q_OBJECT
public:
    LoginDialog(void);
public slots:
    //处理ok按钮的槽函数
    void onAccepted(void);
    //处理Cancel按钮的函数
    void onRejected(void);
};
#endif//__LOGINDIALOG_H




//Login.cpp:
#include "LoginDialog.h"
//构造函数
LoginDialog::LoginDialog(void){
    setupUi(this);//界面初始化
    //信号和槽函数
    //点击Ok按钮,发送accepted
    connect(m_btnBox,SIGNAL(accepted(void)),
            this,SLOT(onAccepted(void)));
    //点击Cancel,发送rejected
    connect(m_btnBox,SIGNAL(rejected(void)),
            this,SLOT(onRejected(void)));
}
//处理ok按钮的槽函数 
void LoginDialog::onAccepted(void){
    //如果输入tarena/123456打印登录成功提示
    //否则弹出表示错误的消息提示框
    if(m_editUsername->text() == "tarena" &&
        m_editPassword->text() == "123456"){
        qDebug("登录成功!");
        qDebug() << "Login Successs!";
        close();//关闭登录窗口
    }
    else{
        //创建消息提示框
        //参数:图标,标题,提示消息,按钮,父窗口
        QMessageBox msgBox(
            QMessageBox::Critical,
            "Error",
            "用户名或密码错误!",
            QMessageBox::Ok,
            this);
        //显示消息提示框,并进入事件循环
        msgBox.exec();
    }
}
//处理Cancel按钮的槽函数
void LoginDialog::onRejected(void){
    //创建一个询问用户是否要确定退出的提示框
    QMessageBox msgBox(
        QMessageBox::Question,
        windowTitle(),
        "确定要取消登录吗?",
        QMessageBox::Yes|QMessageBox::No,
        this);
    //显示消息提示框,点击上面的Yes和No都会导致
    //消息框关闭,但是返回结果不同,但点击Yes是
    //让登录窗口也关闭.
    if(msgBox.exec()==QMessageBox::Yes){
        close();//关闭登录窗口
    }
}


//main.cpp:
#include <QApplication>
#include "LoginDialog.h"

int main(int argc,char** argv)
{
    QApplication app(argc,argv);

    LoginDialog login;
    login.show();

    return app.exec();
}

//login.pro:

######################################################################
# Automatically generated by qmake (3.0) Mon Jun 3 11:37:50 2019
######################################################################
QT += widgets
TEMPLATE = app
TARGET = Login
INCLUDEPATH += .

# Input
HEADERS += LoginDialog.h
FORMS += LoginDialog.ui
SOURCES += LoginDialog.cpp main.cpp









组合的方式:
//.pro:
QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++17

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    userlogin.cpp

HEADERS += \
    userlogin.h

FORMS += \
    userlogin.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target


//main.cpp:
#include "userlogin.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    UserLogin w;
    w.show();
    return a.exec();
}


//LoginDialog.h:
#ifndef USERLOGIN_H
#define USERLOGIN_H

#include <QMainWindow>
#include <QMessageBox>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui {
class UserLogin;
}
QT_END_NAMESPACE

class UserLogin : public QMainWindow
{
    Q_OBJECT

public:
    UserLogin(QWidget *parent = nullptr);
    ~UserLogin();

private:
    Ui::UserLogin *ui;
public slots:
    void onAccepted();
    void onRejected();
};
#endif // USERLOGIN_H



//LoginDialog.cPP;
#include "userlogin.h"
#include "ui_userlogin.h"

UserLogin::UserLogin(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::UserLogin)
{
    ui->setupUi(this);
    this->connect(ui->m_btnBox,SIGNAL(accepted(void)),this, SLOT(onAccepted(void)));
    this->connect(ui->m_btnBox,SIGNAL(rejected(void)),this, SLOT(onRejected(void)));
}

UserLogin::~UserLogin()
{
    delete ui;
}

void UserLogin::onAccepted(){
    qDebug("onRejected");
    QString str1= ui->m_login ->text();
    QString str2= ui->m_pwd_2->text();
    if(str1.isEmpty() || str2.isEmpty()){
        qDebug()<< "args is empty" ;
    }else{
        if(str1== "xyy" &&(str2 == "123")){
            qDebug() << str1;
           QMessageBox box(QMessageBox::Information,windowTitle(),"login success !",QMessageBox::Ok, this);
            box.exec();
            close();
        }else{
         QMessageBox box(QMessageBox::Critical,windowTitle(),"args error !",QMessageBox::Ok, this);
        box.exec();

        }
    }
}
void UserLogin::onRejected(){
     qDebug("onRejected");
    QMessageBox box(QMessageBox::Question,windowTitle(),"是否退出 ?",QMessageBox::Yes |QMessageBox::No, this);
     if(box.exec()== QMessageBox::Yes){
        close();
    }

}
ui_LoginDialog.h:
/********************************************************************************
** Form generated from reading UI file 'userlogin.ui'
**
** Created by: Qt User Interface Compiler version 5.15.2
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_USERLOGIN_H
#define UI_USERLOGIN_H

#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QSpacerItem>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_UserLogin
{
public:
    QWidget *centralwidget;
    QVBoxLayout *verticalLayout;
    QSpacerItem *verticalSpacer;
    QGridLayout *gridLayout;
    QLabel *label;
    QLineEdit *m_login;
    QLabel *m_pwd;
    QLineEdit *m_pwd_2;
    QHBoxLayout *horizontalLayout;
    QSpacerItem *horizontalSpacer;
    QDialogButtonBox *m_btnBox;
    QSpacerItem *horizontalSpacer_2;
    QSpacerItem *verticalSpacer_2;
    QMenuBar *menubar;
    QStatusBar *statusbar;

    void setupUi(QMainWindow *UserLogin)
    {
        if (UserLogin->objectName().isEmpty())
            UserLogin->setObjectName(QString::fromUtf8("UserLogin"));
        UserLogin->resize(486, 267);
        QFont font;
        font.setPointSize(20);
        UserLogin->setFont(font);
        centralwidget = new QWidget(UserLogin);
        centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
        verticalLayout = new QVBoxLayout(centralwidget);
        verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
        verticalSpacer = new QSpacerItem(20, 7, QSizePolicy::Minimum, QSizePolicy::Expanding);

        verticalLayout->addItem(verticalSpacer);

        gridLayout = new QGridLayout();
        gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
        label = new QLabel(centralwidget);
        label->setObjectName(QString::fromUtf8("label"));

        gridLayout->addWidget(label, 0, 0, 1, 1);

        m_login = new QLineEdit(centralwidget);
        m_login->setObjectName(QString::fromUtf8("m_login"));

        gridLayout->addWidget(m_login, 0, 1, 1, 1);

        m_pwd = new QLabel(centralwidget);
        m_pwd->setObjectName(QString::fromUtf8("m_pwd"));

        gridLayout->addWidget(m_pwd, 1, 0, 1, 1);

        m_pwd_2 = new QLineEdit(centralwidget);
        m_pwd_2->setObjectName(QString::fromUtf8("m_pwd_2"));
        m_pwd_2->setEchoMode(QLineEdit::Password);

        gridLayout->addWidget(m_pwd_2, 1, 1, 1, 1);


        verticalLayout->addLayout(gridLayout);

        horizontalLayout = new QHBoxLayout();
        horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
        horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);

        horizontalLayout->addItem(horizontalSpacer);

        m_btnBox = new QDialogButtonBox(centralwidget);
        m_btnBox->setObjectName(QString::fromUtf8("m_btnBox"));
        m_btnBox->setLayoutDirection(Qt::RightToLeft);
        m_btnBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);

        horizontalLayout->addWidget(m_btnBox);

        horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);

        horizontalLayout->addItem(horizontalSpacer_2);


        verticalLayout->addLayout(horizontalLayout);

        verticalSpacer_2 = new QSpacerItem(20, 7, QSizePolicy::Minimum, QSizePolicy::Expanding);

        verticalLayout->addItem(verticalSpacer_2);

        UserLogin->setCentralWidget(centralwidget);
        menubar = new QMenuBar(UserLogin);
        menubar->setObjectName(QString::fromUtf8("menubar"));
        menubar->setGeometry(QRect(0, 0, 486, 40));
        UserLogin->setMenuBar(menubar);
        statusbar = new QStatusBar(UserLogin);
        statusbar->setObjectName(QString::fromUtf8("statusbar"));
        UserLogin->setStatusBar(statusbar);

        retranslateUi(UserLogin);

        QMetaObject::connectSlotsByName(UserLogin);
    } // setupUi

    void retranslateUi(QMainWindow *UserLogin)
    {
        UserLogin->setWindowTitle(QCoreApplication::translate("UserLogin", "UserLogin", nullptr));
        label->setText(QCoreApplication::translate("UserLogin", "username:", nullptr));
        m_pwd->setText(QCoreApplication::translate("UserLogin", "password:", nullptr));
    } // retranslateUi

};

namespace Ui {
    class UserLogin: public Ui_UserLogin {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_USERLOGIN_H

在这里插入图片描述

七 Qt创造器的使用(qtcreator)//IDE F4切换 .cpp和.h

案例:使用qt创造器再次重构加法计算器
1 在任意目录下执行“qtcreator”进入Qt创造器界面
2 在欢迎模式,点击“new Project”创建工程
1)选择模板,Application->Qt Widgets Applicaiton
在这里插入图片描述

2)项目介绍和位置
名字:Calculator3
路径:/home/tarena/qt/day03
注:将来会在制定路径下创建名字一致的工程目录
3)kit Selection(默认)
4)类信息
基类(父窗口):QDialog
类名:CalculatorDialog
注:将来会根据类型生成相应的源文件
CalculatorDialog.h
CalculatorDialog.cpp
CalculatorDialog.ui
在这里插入图片描述
在这里插入图片描述

5)项目管理(忽略)
6)完成,默认切换到编辑模式

3 双击“ui”,进入设计模式,完成界面设计
1)从左边的“widget box”中找到需要的控件拖拽到父窗口上面
PushButton LineEdit Label
2)设置父窗口和每个控件属性
–》父窗口
objectName(对象名):CalculatorDialog(默认)
注:父窗口对象名一定不要修改!!!
font(字体):点大小(20)
windowTitle(窗口标题):加法计算器
–》左操作数:
objectName(对象名):m_editX
alignment:水平(AlignRight)
–》右操作数:
objectName(对象名):m_editY
alignment:水平(AlignRight)
–》显示结果:
objectName(对象名):m_editZ
alignment:水平(AlignRight)
readOnly:勾选√
–》加号
objectName:m_label
text:“+”
–》等号
objectName:m_button
enabled:去掉勾选√
text:“=”
3)调整父窗口以及每个控件的大小和位置
方法一:鼠标拖拽
方法二:键盘,ctrl/shift+方向键
方法三:设置geometry,位置(x,y) 大小(宽度,高度)
方法四:使用布局器//推荐
4)运行(ctrl+R),查看效果
4 编写代码…

八 资源和图像

A以资源的形式添加图片

1 资源编译器(rcc)(手动模拟)

1)编写资源脚本(xml)

vi test.qrc
1
2
3 0.jpg
4 1.jpg
5
6

2)将其转换为C++文件,这里转换后其实就是一个静态的数组

rcc test.qrc -o qrc_test.cpp
在这里插入图片描述
在这里插入图片描述

2 绘图事件和画家(QPainter)

1)当应用开始运行或者窗口发生改变时,绘图事件处理函数将被执行,paintEvent()
void QWidget::paintEvent(QPaintEvent* event)[virtual];

注:update()/repaint()也可以直接出发绘制事件

2)绘图事件处理函数是虚函数,如果希望在自己窗口中绘制指定的图像,可以通过重写绘图事件函数,在其中通过"画家"完成图像绘制。
3)使用QPainter绘制图片的方法(QT的二位图片引擎)写在void QWidget::paintEvent(QPaintEvent* event)[virtual];里面
QPainter painter(this);	
painter.drawImage(QRect,QImage);
QRect:要绘制图像所在的矩形区域
QImage:要绘制图像,要使用转换后的qrc_test.cpp
案例:图图秀

1)使用qtcreator创建工程
2)工程名:ShowImage
3)类名:ShowImageDialog
4)双击“ui”文件,进入设计师,完成界面设计
–>Frame(显示框架)
frameShape:Box
sizePolicy:垂直策略(Expanding)
–>m_btnPrev(上一张)
–>m_btnNext(下一张)
5)添加资源文件
–>模板:Qt->Qt Resource File
–>文件名:showimage,可以随便起
–>下一步->完成,默认切换到资源编辑界面xxx.qrc
–>添加->添加前缀,程序是通过这个虚拟路径信息来访问的
–>将从ftp下载的images目录拷贝到工程目录下
–>添加->添加文件,选择images目录下10个图片并打开
–> 点击构建,将会转换成C++文件
6)使用资源:

//文件头:
#ifndef SHOWIMAGEDIALOG_H
#define SHOWIMAGEDIALOG_H

#include <QDialog>
#include <QPainter>
#include <QImage>
#include <QRect>
#include <QDebug>

namespace Ui {
class ShowImageDialog;
}

class ShowImageDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ShowImageDialog(QWidget *parent = 0);
    ~ShowImageDialog();

private slots:
    //上一张按钮对应的槽函数
    void on_m_btnPrev_clicked();
    //下一张按钮对应的槽函数
    void on_m_btnNext_clicked();
private:
    //绘图事件处理函数
    //窗口改变、update()/repaint()将被触发执行
    void paintEvent(QPaintEvent *);
private:
    Ui::ShowImageDialog *ui;
    int m_index;//图片索引
};

#endif // SHOWIMAGEDIALOG_H



//showimage.cpp:
#include "showimagedialog.h"
#include "ui_showimagedialog.h"

ShowImageDialog::ShowImageDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ShowImageDialog)
{
    ui->setupUi(this);
    m_index = 0;
}

ShowImageDialog::~ShowImageDialog()
{
    delete ui;
}
//上一张按钮对应的槽函数
void ShowImageDialog::on_m_btnPrev_clicked()
{
    if(--m_index < 0){
        m_index = 9;
    }
    update();//触发绘图事件,绘制和索引对应的图片
}
//下一张按钮对应的槽函数
void ShowImageDialog::on_m_btnNext_clicked()
{
    if(++m_index > 9){
        m_index = 0;
    }
    update();//触发绘图事件
}
//绘图事件处理函数
//窗口改变、update()/repaint()将被触发执行
void ShowImageDialog::paintEvent(QPaintEvent *)
{
    //qDebug() << "绘图事件被触发了!";
    //1)创建用于绘图的画家(Qt二维图形引擎)对象
    QPainter painter(this);
    //2)获取绘图所在矩形区域
    QRect rect = ui->frame->frameRect();

    //qDebug() << "rect:" << rect;
    //qDebug() << "frame:" << ui->frame->pos();

    //平移rect坐标值,让和painter使用相同坐标
    rect.translate(ui->frame->pos());

    //qDebug() << "rect2:" << rect;

    //3)根据索引构建要绘制的图片
    QImage image(":/new/prefix1/images/"+
            QString::number(m_index)+".jpg");
    //4)使用painter将images画到rect矩形区域中
    painter.drawImage(rect,image);
}


在这里插入图片描述

B 动态加载图片和目录操作:

九目录和定时器

1 目录操作(QDir)

1)创建目录对象

QDir(const QString & path)
path:表示要访问的路径
	绝对路径:
		QDir("/home/user/Documents")
		QDir("C:/Documents and Settings")
	相对路径:
		QDir("./images/landscape.png")
		windows 的路径分隔符是相反的

2)遍历目录下内容

QDir dir(...);
QStringList	list = dir.entryList(Filters filters)
filters:过滤器,指定要访问哪些内容
	QDir::Files //只访问普通文件
	QDir::Dirs //只访问子目录
	QDir::NoDotAndDotDot //不包含"." ".."

//打印遍历结果
for(int i=0;i<list.size();i++){
	qDebug() << list.at(i);
}

2 定时器

1)定时器事件:timerEvent() 使用QT定义好的定时器, 当定时器使用较少的情况下使用这个

//开启定时器,每隔interval毫秒触发一个定时器事件
//返回表示当前定时器的ID
int startTimer(int interval);
//关闭和id相匹配的定时器
void killTimer(int id);

2)定时器类:QTimer当定时器使用较多的情况下使用这个

QTimer timer;
//开启定时器
timer.start(int msec);
//每次定时器到时,发送信号timeout
connect(&timer,SIGNAL(timeout(void)),this,SLOT(mySLOT()))
//关闭定时器
timer.stop();

案例:摇奖机

工程名:Ernie
类名:ErnieDialog
注:在项目模式中去掉“Shadow Build”选项,然后将photos目录拷贝工程目录下。

//main.cpp:
#include "erniedialog.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ErnieDialog w;
    w.show();

    return a.exec();
}


//erniedialog.h:
#ifndef ERNIEDIALOG_H
#define ERNIEDIALOG_H

#include <QDialog>
#include <QDir>//目录操作
#include <QTimer>//定时器
#include <QTime>//用于设置随机种子
#include <QVector>//向量容器
#include <QPainter>//"画家"
#include <QDebug>//打印调试

namespace Ui {
class ErnieDialog;
}

class ErnieDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ErnieDialog(QWidget *parent = 0);
    ~ErnieDialog();

private slots:
    //开始按钮对应的槽函数
    void on_pushButton_clicked();
private:
    //将path目录的图片加载到容器中
    void loadPhotos(const QString& path);
    //定时器事件处理函数
    void timerEvent(QTimerEvent* );
    //绘图事件处理函数
    void paintEvent(QPaintEvent *);
private:
    Ui::ErnieDialog *ui;
    bool isStarted;//状态标示:开始摇奖/停止摇奖
    QVector<QImage> m_vecPhotos;//保存所有图片的容器
    int m_index;//图片在容器中索引
    int m_timer;//定时器
};

#endif // ERNIEDIALOG_H

//erniedialog.cpp:
#include "erniedialog.h"
#include "ui_erniedialog.h"

ErnieDialog::ErnieDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ErnieDialog)
{
    ui->setupUi(this);
    isStarted = false;//初始化状态标记:未开始摇奖
    loadPhotos("./photos");//加载photos目录下所有图片
    qDebug() << "加载的图片个数:" << m_vecPhotos.size();

    qsrand(QTime::currentTime().msec());//设置随机种子
    m_index = 0;//初始化索引
}

ErnieDialog::~ErnieDialog()
{
    delete ui;
}
//开始按钮对应的槽函数
void ErnieDialog::on_pushButton_clicked()
{
    if(isStarted == false){
        isStarted = true;//开始摇奖
        //开启定时器,每隔50毫秒触发一次定时器事件
        m_timer = startTimer(50);
        ui->pushButton->setText("停止");
    }
    else{
        isStarted = false;//停止
        //关闭定时器
        killTimer(m_timer);
        ui->pushButton->setText("开始");
    }
}
//将path目录的图片加载到容器中
void ErnieDialog::loadPhotos(const QString& path)
{
    //创建目录对象
    QDir dir(path);

    //遍历当前目录的所有图片
    QStringList l1 = dir.entryList(QDir::Files);
    for(int i=0; i<l1.size(); i++){
        //根据文件名创建图片对象
        //eg:QImage image("./photos/xx.jpg")
        QImage image(path+"/"+l1.at(i));
        //保存图片对象到容器中
        m_vecPhotos << image;
    }
    //递归遍历子目录(不包括. ..)的所有图片
    QStringList l2 = dir.entryList(
                QDir::Dirs | QDir::NoDotAndDotDot);
    for(int i=0;i<l2.size();i++){
        loadPhotos(path+"/"+l2.at(i));
    }
}
//定时器事件处理函数
void ErnieDialog::timerEvent(QTimerEvent* )
{
    //qDebug("timerEvent");
    //随机获取一个图片在容器中索引
    m_index = qrand() % m_vecPhotos.size();
    //触发绘图事件
    update();
}
//绘图事件处理函数
void ErnieDialog::paintEvent(QPaintEvent *)
{
    //qDebug("paintEvent");
    //创建画家对象
    QPainter painter(this);
    //获取绘图所在矩形区域
    QRect rect = ui->frame->frameRect();
    //坐标平移,让rect对象和painter使用相同坐标系
    rect.translate(ui->frame->pos());
    //将索引对应的图片画到rect矩形区域中
    painter.drawImage(rect,m_vecPhotos[m_index]);
}


//xxx.pro:
#-------------------------------------------------
#
# Project created by QtCreator 2019-06-04T09:48:54
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = Ernie
TEMPLATE = app


SOURCES += main.cpp\
        erniedialog.cpp

HEADERS  += erniedialog.h

FORMS    += erniedialog.ui






在这里插入图片描述
在这里插入图片描述

通过定时器对象和自定义参函数来设置开奖程序:
//.h:
#ifndef ERNIEDIALOG_H
#define ERNIEDIALOG_H

#include <QDialog>
#include <QDir>//目录操作
#include <QTimer>//定时器
#include <QTime>//用于设置随机种子
#include <QVector>//向量容器
#include <QPainter>//"画家"
#include <QDebug>//打印调试

namespace Ui {
class ErnieDialog;
}

class ErnieDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ErnieDialog(QWidget *parent = 0);
    ~ErnieDialog();

private slots:
    //开始按钮对应的槽函数
    void on_pushButton_clicked();
private:
    //将path目录的图片加载到容器中
    void loadPhotos(const QString& path);

private slots:
    
    //处理定时器的槽函数
    void onTimeout(void);
private:
    //绘图事件处理函数
    void paintEvent(QPaintEvent *);
private:
    Ui::ErnieDialog *ui;
    bool isStarted;//状态标示:开始摇奖/停止摇奖
    QVector<QImage> m_vecPhotos;//保存所有图片的容器
    int m_index;//图片在容器中索引
   
    QTimer m_timer;//定时器对象
};

#endif // ERNIEDIALOG_H





//.cpp:
#include "erniedialog.h"
#include "ui_erniedialog.h"

ErnieDialog::ErnieDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ErnieDialog)
{
    ui->setupUi(this);
    isStarted = false;//初始化状态标记:未开始摇奖
    loadPhotos("./photos");//加载photos目录下所有图片
    qDebug() << "加载的图片个数:" << m_vecPhotos.size();

    qsrand(QTime::currentTime().msec());//设置随机种子
    m_index = 0;//初始化索引

    //QTimer对象每次到时将发送信号:timeout
    connect(&m_timer,SIGNAL(timeout()),
            this,SLOT(onTimeout()));
}

ErnieDialog::~ErnieDialog()
{
    delete ui;
}
//开始按钮对应的槽函数
void ErnieDialog::on_pushButton_clicked()
{
    if(isStarted == false){
        isStarted = true;//开始摇奖
        //开启定时器,每隔50毫秒触发一次定时器事件
        //m_timer = startTimer(50);
        m_timer.start(50);
        ui->pushButton->setText("停止");
    }
    else{
        isStarted = false;//停止
        //关闭定时器
        //killTimer(m_timer);
        m_timer.stop();
        ui->pushButton->setText("开始");
    }
}
//将path目录的图片加载到容器中
void ErnieDialog::loadPhotos(const QString& path)
{
    //创建目录对象
    QDir dir(path);

    //遍历当前目录的所有图片
    QStringList l1 = dir.entryList(QDir::Files);
    for(int i=0; i<l1.size(); i++){
        //根据文件名创建图片对象
        //eg:QImage image("./photos/xx.jpg")
        QImage image(path+"/"+l1.at(i));
        //保存图片对象到容器中
        m_vecPhotos << image;
    }
    //递归遍历子目录(不包括. ..)的所有图片
    QStringList l2 = dir.entryList(
                QDir::Dirs | QDir::NoDotAndDotDot);
    for(int i=0;i<l2.size();i++){
        loadPhotos(path+"/"+l2.at(i));
    }
}
//定时器事件处理函数
/*void ErnieDialog::timerEvent(QTimerEvent* )
{
    //qDebug("timerEvent");
    //随机获取一个图片在容器中索引
    m_index = qrand() % m_vecPhotos.size();
    //触发绘图事件
    update();
}*/
//处理定时器的槽函数
void ErnieDialog::onTimeout(void)
{
    //随机获取一个图片在容器中索引
    m_index = qrand() % m_vecPhotos.size();
    //触发绘图事件
    update();
}
//绘图事件处理函数
void ErnieDialog::paintEvent(QPaintEvent *)
{
    //qDebug("paintEvent");
    //创建画家对象
    QPainter painter(this);
    //获取绘图所在矩形区域
    QRect rect = ui->frame->frameRect();
    //坐标平移,让rect对象和painter使用相同坐标系
    rect.translate(ui->frame->pos());
    //将索引对应的图片画到rect矩形区域中
    painter.drawImage(rect,m_vecPhotos[m_index]);
}



十 鼠标和键盘

1 鼠标事件

#include
//鼠标按下时执行的事件处理函数
void mousePressEvent(QMouseEvent*)
//鼠标抬起时执行的事件处理函数
void mouseReleaseEvent(QMouseEvent*)
//鼠标移动时执行的事件处理函数
void mouseMoveEvent(QMouseEvent*)
//鼠标双击时执行的事件处理函数
void mouseDoubleClickEvent(QMouseEvent*)


QRect(int x,int y,int w, int h);//矩形区域
QPoint(int x,int y);//位置
QSize(int w,int h);//大小

案例:鼠标测试,鼠标拖拽label控件移动

1)工程名:Mouse
2)类名:MouseDialog
3)界面设计(label)
设置背景颜色方法1:样式表
styleSheet:添加颜色,background-color
设置背景颜色方法2:调色板
autoFillBackground:勾选√
palette:点击“继承”->“改变调色板”->选择颜色

//mouse.h:
#ifndef MOUSEDIALOG_H
#define MOUSEDIALOG_H

#include <QDialog>
#include <QMouseEvent>

namespace Ui {
class MouseDialog;
}

class MouseDialog : public QDialog
{
    Q_OBJECT

public:
    explicit MouseDialog(QWidget *parent = 0);
    ~MouseDialog();
private:
    //鼠标按下时执行的事件处理函数
    void mousePressEvent(QMouseEvent *);
    //鼠标抬起时执行的事件处理函数
    void mouseReleaseEvent(QMouseEvent *);
    //鼠标移动时执行的事件处理函数
    void mouseMoveEvent(QMouseEvent *);
private:
    Ui::MouseDialog *ui;
    bool m_drag;//拖拽标记:鼠标左键选中label
    QPoint m_pos;//记录鼠标和label相对位置
};

#endif // MOUSEDIALOG_H



//Mouse.cpp:
#include "mousedialog.h"
#include "ui_mousedialog.h"

MouseDialog::MouseDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::MouseDialog)
{
    ui->setupUi(this);
    m_drag = false;
}
MouseDialog::~MouseDialog()
{
    delete ui;
}
//鼠标按下时执行的事件处理函数
void MouseDialog::mousePressEvent(
        QMouseEvent *event){
    //判断是否为鼠标左键
    if(event->button() == Qt::LeftButton){
        //获取label所在矩形区域
        QRect rect = ui->label->frameRect();
        //坐标平移:让rect和窗口坐标系一致
        rect.translate(ui->label->pos());
        //判断鼠标点击位置是否在rect矩形区域中
        if(rect.contains(event->pos())){
            m_drag = true;
            //记录label和鼠标的相对位置
            m_pos=ui->label->pos()-event->pos();
        }
    }
}
//鼠标抬起时执行的事件处理函数
void MouseDialog::mouseReleaseEvent(
        QMouseEvent *event){
    if(event->button() == Qt::LeftButton){
        m_drag = false;
    }
}
//鼠标移动时执行的事件处理函数
void MouseDialog::mouseMoveEvent(
        QMouseEvent *event){
    if(m_drag){
        //计算label的新位置
        QPoint newPos=event->pos() + m_pos;

        //设置label移动限制:不能超出父窗口
        //x:0 --- (窗口宽-label宽)
        //y:0 --- (窗口高-label高)
        QSize s1 = size();//获取窗口大小
        QSize s2 = ui->label->size();//获取label大小
        if(newPos.x() < 0){
            newPos.setX(0);
        }
        else if(newPos.x() >
                s1.width()-s2.width()){
            newPos.setX(s1.width()-s2.width());
        }
        if(newPos.y() < 0){
            newPos.setY(0);
        }
        else if(newPos.y() >
                s1.height()-s2.height()){
            newPos.setY(s1.height()-s2.height());
        }

        //移动label到新位置
        ui->label->move(newPos);
    }
}





2 键盘事件处理

#include
//按键按下时执行的事件处理函数
void keyPressEvent(QKeyEvent*);
//按键抬起时执行的事件处理函数
void keyReleaseEvent(QKeyEvent*);

案例:按键测试,通过键盘方向键控制label移动

工程名:Keyboard
类名:KeyboardDialog

xxx.h:
#ifndef KEYBOARDDIALOG_H
#define KEYBOARDDIALOG_H

#include <QDialog>
#include <QKeyEvent>
#include <QDebug>

namespace Ui {
class KeyboardDialog;
}

class KeyboardDialog : public QDialog
{
    Q_OBJECT

public:
    explicit KeyboardDialog(QWidget *parent = 0);
    ~KeyboardDialog();
private:
    //按键按下:通过方向键控制label移动
    void keyPressEvent(QKeyEvent *);
    //按键抬起:打印按键码值
    void keyReleaseEvent(QKeyEvent *);
private:
    Ui::KeyboardDialog *ui;
};

#endif // KEYBOARDDIALOG_H


//xxx.cpp:
#include "keyboarddialog.h"
#include "ui_keyboarddialog.h"

KeyboardDialog::KeyboardDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::KeyboardDialog)
{
    ui->setupUi(this);
}

KeyboardDialog::~KeyboardDialog()
{
    delete ui;
}
//按键按下:通过方向键控制label移动
void KeyboardDialog::keyPressEvent(
        QKeyEvent *event)
{
    //获取label当前位置
    int x=ui->label->pos().x();
    int y=ui->label->pos().y();
    if(event->key()==Qt::Key_Up){//↑
        ui->label->move(x,y-10);
    }
    else if(event->key()==Qt::Key_Down){//↓
        ui->label->move(x,y+10);
    }
    else if(event->key()==Qt::Key_Left){//←
        ui->label->move(x-10,y);
    }
    else if(event->key()==Qt::Key_Right){//→
        ui->label->move(x+10,y);
    }
}
//按键抬起:打印按键码值
void KeyboardDialog::keyReleaseEvent(
        QKeyEvent *event)
{
    qDebug() << "按键代码:" << event->key();
    qDebug() << "扫描键码:" <<
                event->nativeScanCode();
    qDebug() << "虚拟键码:" <<
                event->nativeVirtualKey();
}






十一 Qt多线程(QThread)

常用函数

1 开启线程//类似pthread_create
void start();//线程入口函数run函数将被执行

2 获取线程句柄(ID) //类似pthread_seft
Qt::HANDLE currentThreadId()[static]

3 线程退出//类似pthread_exit
void exit();
void quit()[slots];

4 线程等待//类似pthread_join
void wait();

5 线程终止//类似pthread_cancel
void terminate();

void run(){
new 内存;
//…被终止,内存泄漏
delete 内存;
return;
}
void run(){
加锁:lock
//访问临界资源
//…被终止,死锁
//访问临界资源结束
解锁:unlock
}
6 设置线程是否允许被终止//类似pthread_setcancelstate
void setTerminationEnabled(bool enable=true);
void run(){
setTerminationEnabled(false);//禁止当前线程被终止
关键代码:动态内存,临时资源,数据库…
setTerminationEnabled(true);//当前线程可以被终止
}

创建线程的具体方法一 :QObject::moveToThread

1必须是槽函数,所以必须写在类中
2必须是QObject的子类,所以必须继承 QObject
/某个对象的doword将作为子线程函数执行
class Worker : public QObject{
	    Q_OBJECT
	public slots:
		 //将来要把该函数放到子线程中执行
	    void doWork(){
	        /* 耗时或阻塞操作 */
	    }
	};


//class xxx要开启线程的对象:
   //创建子线程对象
   QThread workerThread;
   //创建worker对象,将来要放在子线程中执行
	Worker *worker = new Worker;
   //将worker对象移动到子线程中
   worker->moveToThread(&workerThread);
   //通过某个信号,触发worker槽函数执行,槽函数将在子线程中,啥信号都可以
   connect(xx,SIGNAL(xx),worker,SLOT(dowork()));    
   //开启子线程
   workerThread.start(); 

案例:多线程打印消息//Thread1

//子线程类:

//worker.h:
#ifndef WORKER_H
#define WORKER_H

#include <QObject>
#include <QDebug>

class Worker:public QObject
{
    Q_OBJECT
public:
    Worker();
    ~Worker();
public slots:
    void doWork(void);
};

#endif // WORKER_H



//worker.cpp:
#include "worker.h"
#include <QThread>
Worker::Worker()
{
}

Worker::~Worker()
{
}

void Worker::doWork(void){
    while(1){
        qDebug() << "子线程:" <<
            QThread::currentThreadId();
        QThread::sleep(1);
    }
}




//主线程类:

//threaddialog.h:
#ifndef THREADDIALOG_H
#define THREADDIALOG_H

#include <QDialog>
#include "worker.h"
#include <QThread>

namespace Ui {
class ThreadDialog;
}

class ThreadDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ThreadDialog(QWidget *parent = 0);
    ~ThreadDialog();

private:
    Ui::ThreadDialog *ui;
    QThread workerThread;//子线程
};

#endif // THREADDIALOG_H





//threaddialog.cpp:
#include "threaddialog.h"
#include "ui_threaddialog.h"

ThreadDialog::ThreadDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ThreadDialog)
{
    ui->setupUi(this);
    qDebug() << "主线程:" <<
                QThread::currentThreadId();
    //创建Worker对象
    Worker* worker = new Worker;
    //将Worker对象移动到子线程中
    worker->moveToThread(&workerThread);

    //当点击按钮时,发送clicked
    //触发worker的槽函数dowork在子线程中执行
    connect(ui->pushButton,SIGNAL(clicked()),
            worker,SLOT(doWork()));
    //开启子线程
    workerThread.start();
}

ThreadDialog::~ThreadDialog()
{
    delete ui;
}

创建线程的具体方法二:继承QThread,重写run

class WorkerThread : public QThread{
    Q_OBJECT
    void run(){//线程入口函数
        //耗时或阻塞操作
    }
};

WorkerThread thread;
thread.start();//run将在子线程中被执行

案例:多线程打印消息//Thread2

//子线程类:

//worker.h:
#ifndef WORKERTHREAD_H
#define WORKERTHREAD_H

#include <QThread>
#include <QDebug>

//1)继承QThread
class WorkerThread:public QThread
{
public:
    WorkerThread();
    ~WorkerThread();
//2)重写线程入口函数
protected:
    void run(void);
};

#endif // WORKERTHREAD_H


//worker.cpp:
#include "workerthread.h"

WorkerThread::WorkerThread()
{
}
WorkerThread::~WorkerThread()
{
}
void WorkerThread::run()
{
    while(1){
        qDebug() << "子线程:" <<
                    currentThreadId();
        sleep(1);
    }
}



//主线程类:

//threaddialog.h:
#ifndef THREADDIALOG_H
#define THREADDIALOG_H

#include <QDialog>
#include "workerthread.h"

namespace Ui {
class ThreadDialog;
}

class ThreadDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ThreadDialog(QWidget *parent = 0);
    ~ThreadDialog();

private:
    Ui::ThreadDialog *ui;
    WorkerThread thread;//子线程
};

#endif // THREADDIALOG_H


//threadDialog.cppp:
#include "threaddialog.h"
#include "ui_threaddialog.h"

ThreadDialog::ThreadDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ThreadDialog)
{
    ui->setupUi(this);
    qDebug() << "主线程:" <<
                QThread::currentThreadId();
    //点击按钮发送clicked信号,开启子线程
    //利用多态语法实际执行到是子类中重写的run函数
    connect(ui->pushButton,SIGNAL(clicked()),
        &thread,SLOT(start()));
//也可以直接调用子线程:
	//thread.start();

}

ThreadDialog::~ThreadDialog()
{
    delete ui;
}

十二 网络编程基础

1 网络模型(OSI七层、TCP/IP四层)

1)应用层:HTTP、FTP、SMTP、POP3、TFTP…
2)表示层
3)会话层
4)传输层:UDP、TCP
5)网络层:IP
6)数据链路层
7)物理层

2 IP地址

1)互联网唯一的地址标识
2)IPV4(32位整数)、IPV6(128位整数)
3)表示方式
–》点分十进制 “xx.xx.xx.xx”
–》十六进制数:struct sockaddr{IP};
eg:192.168.15.100–>0xC0A80F64
4)查看IP地址指令
linux:ifconfig
windows:ipconfig
5)通过IP判断两台主机是否能够通信
ping 对方IP
6)特殊IP地址
任意地址:“0.0.0.0"对应本任意网卡
本地环回地址:“127.0.0.1”
广播地址:“255.255.255.255”

十三 Qt中和网络编程相关类

QT += network
1 QHostAddres//IP地址
2 QAbstractSocket//套接字基类
3 QUdpSocket//UDP套接字
4 QTcpSocket//TCP套接字
5 QTcpServer//TCP服务器

案例:UDP网络广播

在这里插入图片描述

1)发送端//Sender
–》创建UDP通信套接字
–》广播地址/端口:255.255.255.255/8888
–》输入广播消息,定时发送
在这里插入图片描述

//sender.h:
#ifndef SENDERDIALOG_H
#define SENDERDIALOG_H

#include <QDialog>
#include <QUdpSocket>//QT += network
#include <QTimer>
#include <QDebug>

namespace Ui {
class SenderDialog;
}

class SenderDialog : public QDialog
{
    Q_OBJECT

public:
    explicit SenderDialog(QWidget *parent = 0);
    ~SenderDialog();

private slots:
    //开始广播按钮对应的槽函数
    void on_startButton_clicked();
    //定时发送广播消息的槽函数
    void sendMessage();
private:
    Ui::SenderDialog *ui;
    bool isStarted;//标记是否已经开始广播
    QUdpSocket* udpSocket;//UDP通信套接字
    QTimer* timer;//定时器
};

#endif // SENDERDIALOG_H

//sender.cpp:
#include "senderdialog.h"
#include "ui_senderdialog.h"

SenderDialog::SenderDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::SenderDialog)
{
    ui->setupUi(this);
    isStarted = false;//标记未开始广播
    udpSocket = new QUdpSocket(this);
    timer = new QTimer(this);
    //定时器到时后发送信号:timeout
    connect(timer,SIGNAL(timeout()),
            this,SLOT(sendMessage()));
}
SenderDialog::~SenderDialog()
{
    delete ui;
}
//开始广播按钮对应的槽函数
void SenderDialog::on_startButton_clicked()
{
    if(isStarted == false){
        isStarted = true;//开始广播
        timer->start(1000);//每隔1秒广播一次
        //修改按钮文本:停止广播
        ui->startButton->setText("停止广播");
        //禁用端口和消息的输入
        ui->portEdit->setEnabled(false);
        ui->messageEdit->setEnabled(false);
    }
    else{
        isStarted = false;//停止广播
        timer->stop();//关闭定时器
        //修改按钮文本:开始广播
        ui->startButton->setText("开始广播");
        //恢复端口和消息的输入
        ui->portEdit->setEnabled(true);
        ui->messageEdit->setEnabled(true);
    }
}
//定时发送广播消息的槽函数
void SenderDialog::sendMessage()
{
    //获取端口号
    quint16 port=ui->portEdit->text().toShort();
    //获取广播消息
    QString msg = ui->messageEdit->text();
    if(msg == ""){
        return;
    }
    //通过udp套接字发送广播消息
    //函数功能:发送udp消息,类似sendto()
    //参数:消息内容(QByteArray)、IP地址、端口号
    //toUtf8():QString转换为QByteArray
    udpSocket->writeDatagram(msg.toUtf8(),
        QHostAddress::Broadcast,port);
    qDebug() << "send:" << msg;
}

2)接收端//Reveiver
–》创建UDP通信套接字
–》绑定IP和端口
–》接收广播消息并显示

///recver.h:
#ifndef RECEIVERDIALOG_H
#define RECEIVERDIALOG_H

#include <QDialog>
#include <QUdpSocket>
#include <QDebug>

namespace Ui {
class ReceiverDialog;
}

class ReceiverDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ReceiverDialog(QWidget *parent = 0);
    ~ReceiverDialog();

private slots:
    //开始接收按钮对应的槽函数
    void on_startButton_clicked();
    //接收广播消息的槽函数
    void messageReceived();
private:
    Ui::ReceiverDialog *ui;
    bool isStarted;//标记开始接收或停止接收
    QUdpSocket* udpSocket;//通信的UDP套接字
    quint16 port;//接收消息的端口
};

#endif // RECEIVERDIALOG_H

//recver.cpp:

#include "receiverdialog.h"
#include "ui_receiverdialog.h"

ReceiverDialog::ReceiverDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ReceiverDialog)
{
    ui->setupUi(this);
    isStarted = false;
    udpSocket = new QUdpSocket(this);
}

ReceiverDialog::~ReceiverDialog()
{
    delete ui;
}
//开始接收按钮对应的槽函数
void ReceiverDialog::on_startButton_clicked()
{
    if(isStarted == false){
        isStarted = true;//开始接收
        //获取端口号
        port = ui->portEdit->text().toShort();
        //绑定IP(默认Any)和端口
        if(udpSocket->bind(port)==false){
            qDebug() << "绑定端口失败";
        }
        //当套接字有广播消息到来,发送信号readyread
        connect(udpSocket,SIGNAL(readyRead()),
                this,SLOT(messageReceived()));
        //修改按钮文本:"停止接收"
        ui->startButton->setText("停止接收");
        //禁用端口输入
        ui->portEdit->setEnabled(false);
    }
    else{
        isStarted = false;//停止接收
        udpSocket->close();//关闭套接字
        //修改按钮文本:"开始接收"
        ui->startButton->setText("开始接收");
        //恢复端口输入
        ui->startButton->setEnabled(true);
    }
}
//接收广播消息的槽函数
void ReceiverDialog::messageReceived()
{
    //判断udp套接字是否有等待读取的数据包
    if(udpSocket->hasPendingDatagrams()){
        //准备接收数据包的缓冲区
        QByteArray buf;
        //设置缓冲区大小和数据包大小一致
        buf.resize(
            //获取等待读取数据包的字节数
            udpSocket->pendingDatagramSize());
        //读取数据包
        //参数:接收数据缓冲区的首地址(char*)/大小
        //data():QByteArray转换为char*
        udpSocket->readDatagram(
            buf.data(),buf.size());
        //显示所接收到的广播消息
        ui->listWidget->addItem(buf);
        //回滚显示最底部的消息(最新)
        //ui->listWidget->scrollToBottom();
    }
}

在这里插入图片描述

案例:TCP网络聊天室

在这里插入图片描述

1)服务器//Server
–》使用QTcpServer创建Tcp服务器
–》响应和客户端连接请求,保存和客户端通信的套接字
–》接收客户端发来的聊天消息
–》转发聊天消息给其它客户端

//server.h:

#ifndef SERVERDIALOG_H
#define SERVERDIALOG_H

#include <QDialog>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>

namespace Ui {
class ServerDialog;
}

class ServerDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ServerDialog(QWidget *parent = 0);
    ~ServerDialog();

private slots:
    //创建服务器按钮对应的槽函数
    void on_createButton_clicked();
    //响应客户端连接请求的槽函数
    void onNewConnection();
    //接收聊天消息的槽函数
    void onReayRead();
private:
    //转发消息给客户端
    void sendMessage(const QByteArray&);
private:
    Ui::ServerDialog *ui;
    QTcpServer tcpServer;//TCP服务器
    quint16 port;//服务器端口
    //容器:保存和客户端通信的套接字
    QList<QTcpSocket*> tcpClientList;

};

#endif // SERVERDIALOG_H




//Server.cpp:
#include "serverdialog.h"
#include "ui_serverdialog.h"

ServerDialog::ServerDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ServerDialog)
{
    ui->setupUi(this);
}
ServerDialog::~ServerDialog()
{
    delete ui;
}
//创建服务器按钮对应的槽函数
void ServerDialog::on_createButton_clicked()
{
    //获取服务器端口
    port = ui->portEdit->text().toShort();
    //设置监听IP和端口
    if(tcpServer.listen(
        QHostAddress::Any,port)==false){
        qDebug() << "创建服务器失败";
        return;
    }
    qDebug() << "服务器创建成功!";
    //禁用创建服务器按钮和端口输入
    ui->createButton->setEnabled(false);
    ui->portEdit->setEnabled(false);
    //当客户端建立连接时,服务器发送信号:newConnection
    connect(&tcpServer,SIGNAL(newConnection()),
            this,SLOT(onNewConnection()));
}
//响应客户端连接请求的槽函数
void ServerDialog::onNewConnection()
{
    //获取和客户端通信的套接字
    QTcpSocket* tcpClient =
        tcpServer.nextPendingConnection();
    //保存客户端套接字到容器
    tcpClientList.append(tcpClient);
    //当客户端给服务器发送消息时,发送信号readyRead
    connect(tcpClient,SIGNAL(readyRead()),
            this,SLOT(onReayRead()));
}
//接收聊天消息的槽函数
void ServerDialog::onReayRead()
{
    //遍历检查容器中是否存在断开连接的客户端,如果有则删除
    for(int i=0;i<tcpClientList.size();i++){
        //state():获取套接字连接状态
        if(tcpClientList.at(i)->state() ==
            QAbstractSocket::UnconnectedState){
            tcpClientList.removeAt(i);
        }
    }

    //遍历检查容器中哪个客户端发来的消息
    for(int i=0;i<tcpClientList.size();i++){
        //bytesAvailable():当前套接字等待读取消息的
        //字节数,如果没有消息返回0;
        if(tcpClientList.at(i)
                ->bytesAvailable()){
            QByteArray buf=
                tcpClientList.at(i)->readAll();
            //显示(保存)聊天消息
            ui->listWidget->addItem(buf);
            ui->listWidget->scrollToBottom();
            //转发消息给其它客户端
            sendMessage(buf);
        }
    }
}
//转发消息给客户端
void ServerDialog::sendMessage(
        const QByteArray& buf){
    for(int i=0;i<tcpClientList.size();i++){
        tcpClientList.at(i)->write(buf);
    }
}





2)客户端//Client
–》使用QTcpSocket建立和服务器通信的套接字
–》向服务器发送连接请求
–》获取用户输入的聊天消息发送到服务器
–》接收服务器转发的聊天消息并显示

client.h:
#ifndef CLIENTDIALOG_H
#define CLIENTDIALOG_H

#include <QDialog>
#include <QHostAddress>
#include <QTcpSocket>
#include <QMessageBox>

namespace Ui {
class ClientDialog;
}

class ClientDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ClientDialog(QWidget *parent = 0);
    ~ClientDialog();

private slots:
    //发送按钮对应的槽函数
    void on_sendButton_clicked();
    //连接服务器按钮对应的槽函数
    void on_connectButton_clicked();
    //和服务器连接成功时执行的槽函数
    void onConnected(void);
    //和服务器断开连接时执行的槽函数
    void onDisconnected(void);
    //接收聊天消息消息的槽函数
    void onReadyRead(void);
    //处理网络通信异常的槽函数
    void onError(void);
private:
    Ui::ClientDialog *ui;
    bool status;//标记客户端连接状态:在线/离线
    QTcpSocket tcpSocket;//和服务器通信的套接字
    QHostAddress serverIp;//服务器IP地址
    quint16 serverPort;//服务器端口号
    QString username;//聊天室昵称
};

#endif // CLIENTDIALOG_H



//client.cpp:
#include "clientdialog.h"
#include "ui_clientdialog.h"

ClientDialog::ClientDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ClientDialog)
{
    ui->setupUi(this);
    status = false;//标记客户端为离线状态
    //和服务器连接成功时发送信号connected
    connect(&tcpSocket,SIGNAL(connected()),
            this,SLOT(onConnected()));
    //和服务器断开连接时发送信号disconnected
    connect(&tcpSocket,SIGNAL(disconnected()),
            this,SLOT(onDisconnected()));
    //当收到服务器发来的消息时,发送信号readyRead
    connect(&tcpSocket,SIGNAL(readyRead()),
            this,SLOT(onReadyRead()));
    //网络通信异常时,发送信号error
    connect(&tcpSocket,SIGNAL(error(
            QAbstractSocket::SocketError)),
                this,SLOT(onError()));
}

ClientDialog::~ClientDialog()
{
    delete ui;
}
//发送按钮对应的槽函数
void ClientDialog::on_sendButton_clicked()
{
    //获取用户输入的聊天消息
    QString msg = ui->messageEdit->text();
    if(msg == ""){
        return;
    }
    msg = username + ":" + msg;
    //发送消息
    tcpSocket.write(msg.toUtf8());
    //清空已输入的消息
    ui->messageEdit->clear();
}
//连接服务器按钮对应的槽函数
void ClientDialog::on_connectButton_clicked()
{
    //如果当前是离线状态则建立和服务器连接
    if(status == false){
        //获取服务器IP
        QString ip=ui->serverIpEdit->text();
        //设置服务器的IP地址
        if(serverIp.setAddress(ip)==false){
            QMessageBox::critical(
                this,"Error","IP地址格式错误!");
            return;
        }
        //获取服务器端口号
        serverPort=
          ui->serverPortEdit->text().toShort();
        if(serverPort < 1024){
            QMessageBox::critical(
                this,"Error","端口号格式错误!");
            return;
        }
        //获取聊天室昵称
        username = ui->usernameEdit->text();
        if(username == ""){
            QMessageBox::critical(
                this,"Error","聊天室昵称不能为空!");
            return;
        }
        //向服务器发送连接请求,参数:服务器IP/服务器端口
        //如果连接成功:发送信号connected
        //如果连接失败:发送信号error
        tcpSocket.connectToHost(
                    serverIp,serverPort);
    }
    //如果当前是在线状态则断开连接
    else{
        //向服务器发送离开聊天室的消息
        QString msg = username+":离开了聊天室";
        tcpSocket.write(msg.toUtf8());
        //关闭服务器的连接,
        //断开后将发送信号:disconnected
        tcpSocket.disconnectFromHost();
    }
}
//和服务器连接成功时执行的槽函数
void ClientDialog::onConnected(void)
{
    status = true;//标记在线状态
    //修改连接服务按钮文本:"离开聊天室"
    ui->connectButton->setText("离开聊天室");
    //禁用IP,Port,username输入
    ui->serverIpEdit->setEnabled(false);
    ui->serverPortEdit->setEnabled(false);
    ui->usernameEdit->setEnabled(false);
    //恢复"发送"按钮
    ui->sendButton->setEnabled(true);

    //向服务器发送进入聊天室的提示消息
    QString msg = username + ":进入了聊天室!";
    //toUtf8():将QString转换为QByteArray
    tcpSocket.write(msg.toUtf8());
}
//和服务器断开连接时执行的槽函数
void ClientDialog::onDisconnected(void)
{
    status = false;//标记离线状态
    //修改连接服务按钮文本:"连接服务器"
    ui->connectButton->setText("连接服务器");
    //恢复IP,Port,username输入
    ui->serverIpEdit->setEnabled(true);
    ui->serverPortEdit->setEnabled(true);
    ui->usernameEdit->setEnabled(true);
    //禁用"发送"按钮
    ui->sendButton->setEnabled(false);
}
//接收聊天消息消息的槽函数
void ClientDialog::onReadyRead(void)
{
    if(tcpSocket.bytesAvailable()){
        //读取服务器转发的消息
        QByteArray buf = tcpSocket.readAll();
        //显示聊天消息
        ui->listWidget->addItem(buf);
        ui->listWidget->scrollToBottom();
    }
}
//处理网络通信异常的槽函数
void ClientDialog::onError(void)
{
    //errorString:获取网络异常的原因
    QMessageBox::critical(this,"ERROR",
            tcpSocket.errorString());
}








在这里插入图片描述
在这里插入图片描述

十四 多窗口编程

1 先显示子窗口(登录),再显示主界面//windows1

在这里插入图片描述

在这里插入图片描述

main.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}
//子窗口.h:
#ifndef LOGINDIALOG_H
#define LOGINDIALOG_H

#include <QDialog>

namespace Ui {
class LoginDialog;
}

class LoginDialog : public QDialog
{
    Q_OBJECT

public:
    explicit LoginDialog(QWidget *parent = 0);
    ~LoginDialog();

private slots:
    void on_m_btnBox_accepted();

    void on_m_btnBox_rejected();

private:
    Ui::LoginDialog *ui;
};

#endif // LOGINDIALOG_H
//子窗口.cpp:
#include "logindialog.h"
#include "ui_logindialog.h"

LoginDialog::LoginDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::LoginDialog)
{
    ui->setupUi(this);
}

LoginDialog::~LoginDialog()
{
    delete ui;
}
//Ok
void LoginDialog::on_m_btnBox_accepted()
{
    if("用户名/密码:ok"){
        accept();//导致登录窗口事件循环结束,返回1
    }
}
//Cancel
void LoginDialog::on_m_btnBox_rejected()
{
    reject();//导致登录窗口事件循环结束,返回0
}



2 先显示主界面,再弹出子窗口//windows2

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

mainwindows.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H
mainwindows.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "dialog.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    //弹出子窗口,并进入事件循环,在子窗口退出前,
    //主界面不能在响应事件循环
    //Dialog dialog;
    //dialog.exec();

    //弹出子窗口,并显示,但不影响主界面事件循环
    Dialog* pd = new Dialog(this);
    pd->show();

}




3 同时显示多个窗口//windows3

在这里插入图片描述

十五 http编程

项目《Http客户端》
1)需求分析
实现从“http://code.tarena.com.cn/”下载代码
–》实现代码显示的主界面,参考HttpClient1.tar.gz
–》处理子目录链接,参考HttpClient2.tar.gz
–》实现文件下载,参考HttpClient3.tar.gz
2)概要设计
3)详细设计
4)代码编写
5)测试运行
6)代码重构
7)产品发布
*8)升级维护

HTTP协议简介:
1)请求(request):客户端给服务器发送消息
2)响应(response):服务器给客户端返回的消息

QT相关类:
1)QNetworkAccessManager //管理通信流程
2)QNetworkRequest //请求
3)QNetworkReply //响应
4)QUrl //网络地址“http://code.tarena.com.cn/”
5)QDir/QFile/QFileInfo //目录或文件操作
6)QTextBrowser //文本浏览器

工程名:HttpClient
类名:MainWindow(默认)
注:去掉textBrowser的“openLinks”属性

注:忽略下面提示
qt.network.ssl: QSslSocket: cannot resolve SSLv2_client_method
qt.network.ssl: QSslSocket: cannot resolve SSLv2_server_method

当前currentUrl:
http://code.tarena.com.cn/CSDCode/
newUrl:
http://code.tarena.com.cn/

//查找倒数第二次出现“/”位置
int pos = currentUrl.toString().lastIndexOf("/",-2)
//截断:去掉最后一级路径
newUrl = currentUrl.toString().mid(0,pos+1);

任务:
1)在认证登录槽函数,增加登录对话框,从登录对话框获取用户输入的用户名和密码再认证。
2)将下载文件的任务放入子线程,实现多文件同时下载
3)指定将下载的文件放在“/home/tarena/Downloads”

//参考,refer.tar.gz
mainwindows.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QDebug>
#include <QAuthenticator>//登录认证
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
private slots:
    //处理菜单栏的槽函数
    void onTriggered(QAction*);
    //向服务器发送请求
    void sendRequest();
    //验证登录的槽函数
    void onAuthenticationRequired(
        QNetworkReply*,QAuthenticator*);
    //接收响应数据的槽函数
    void onReadyRead();
    //接收响应数据结束执行的槽函数
    void onFinished();
private:
    Ui::MainWindow *ui;
    QNetworkAccessManager* manager;//管理通信过程
    QNetworkRequest request;//请求
    QNetworkReply* reply;//响应
    QUrl currentUrl;//记录当前URL地址
    QByteArray buf;//保存响应数据的缓冲区
};

#endif // MAINWINDOW_H

//mainwindows.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //点击菜单选项时发送信号triggered
    //QAction对应具体的菜单选项
    connect(ui->menuBar,SIGNAL(
                triggered(QAction*)),
            this,SLOT(onTriggered(QAction*)));
    //向工具栏添加Full/Normal工具按钮
    ui->mainToolBar->addAction("Full");
    ui->mainToolBar->addAction("Normal");
    //点击工具栏按钮时,发送信号actionTriggered
    //QAction对应具体的工具按钮
    connect(ui->mainToolBar,SIGNAL(
                actionTriggered(QAction*)),
            this,SLOT(onTriggered(QAction*)));


    //创建管理通信过程的manager对象
    manager = new QNetworkAccessManager(this);
    //初始化请求
    request.setUrl(
        QUrl("http://code.tarena.com.cn/"));
    //发送请求
    sendRequest();
}

MainWindow::~MainWindow()
{
    delete ui;
}
//处理菜单栏的槽函数
void MainWindow::onTriggered(QAction* action)
{
    //在状态栏显示菜单选项
    ui->statusBar->showMessage(action->text());
    //点击Windows下面的Full实现窗口全屏
    if(action->text() == "Full"){
        showFullScreen();
    }
    //点击Windows下面的Normal恢复窗口大小
    else if(action->text() == "Normal"){
        showNormal();
    }
}
//向服务器发送请求
void MainWindow::sendRequest()
{
    //通过manager发送请求,返回接收响应数据reply对象
    reply = manager->get(request);
    //发送请求后,如果服务器需要认证,将会发送信号:
    //authenticationRequired
    connect(manager,SIGNAL(
        authenticationRequired(
            QNetworkReply*,QAuthenticator*)),
        this,SLOT(onAuthenticationRequired(
            QNetworkReply*,QAuthenticator*)));
    //当认证成功,服务器将会返回响应数据,reply发送信号
    //readyread
    connect(reply,SIGNAL(readyRead()),
        this,SLOT(onReadyRead()));
    //响应数据接收结束,reply发送finished
    connect(reply,SIGNAL(finished()),
        this,SLOT(onFinished()));
}
//验证登录的槽函数
void MainWindow::onAuthenticationRequired(
    QNetworkReply*,QAuthenticator* a)
{
    qDebug("正在验证登录...");
    a->setUser("tarenacode");
    a->setPassword("code_2013");
}
//接收响应数据的槽函数
void MainWindow::onReadyRead()
{
    qDebug("登录成功,开始接收响应数据...");
    //读取响应数据并保存
    buf += reply->readAll();
    //保存当前URL地址
    currentUrl = reply->url();
}
//接收响应数据结束执行的槽函数
void MainWindow::onFinished()
{
    qDebug("接收响应数据完成,本次通信结束!");
    //显示结果
    ui->textBrowser->setText(buf);
    //清空buf
    buf.clear();
    //销毁reply对象
    reply->deleteLater();
}

//实例2:

//mainwindows.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QDebug>
#include <QAuthenticator>//登录认证
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
private slots:
    //处理菜单栏的槽函数
    void onTriggered(QAction*);
    //向服务器发送请求
    void sendRequest();
    //验证登录的槽函数
    void onAuthenticationRequired(
        QNetworkReply*,QAuthenticator*);
    //接收响应数据的槽函数
    void onReadyRead();
    //接收响应数据结束执行的槽函数
    void onFinished();

    //处理目录链接的槽函数
    void onAnchorClicked(const QUrl&);
private:
    Ui::MainWindow *ui;
    QNetworkAccessManager* manager;//管理通信过程
    QNetworkRequest request;//请求
    QNetworkReply* reply;//响应
    QUrl currentUrl;//记录当前URL地址
    QByteArray buf;//保存响应数据的缓冲区
};

#endif // MAINWINDOW_H


#mainwindows.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //点击菜单选项时发送信号triggered
    //QAction对应具体的菜单选项
    connect(ui->menuBar,SIGNAL(
                triggered(QAction*)),
            this,SLOT(onTriggered(QAction*)));
    //向工具栏添加Full/Normal工具按钮
    ui->mainToolBar->addAction("Full");
    ui->mainToolBar->addAction("Normal");
    //点击工具栏按钮时,发送信号actionTriggered
    //QAction对应具体的工具按钮
    connect(ui->mainToolBar,SIGNAL(
                actionTriggered(QAction*)),
            this,SLOT(onTriggered(QAction*)));


    //创建管理通信过程的manager对象
    manager = new QNetworkAccessManager(this);
    //初始化请求
    request.setUrl(
        QUrl("http://code.tarena.com.cn/"));
    //发送请求
    sendRequest();


    //点击目录链接时发送信号anchorClicked,
    //参数表示当前点击的Url地址
    connect(ui->textBrowser,SIGNAL(
               anchorClicked(QUrl)),
        this,SLOT(onAnchorClicked(QUrl)));

}

MainWindow::~MainWindow()
{
    delete ui;
}
//处理菜单栏的槽函数
void MainWindow::onTriggered(QAction* action)
{
    //在状态栏显示菜单选项
    ui->statusBar->showMessage(action->text());
    //点击Windows下面的Full实现窗口全屏
    if(action->text() == "Full"){
        showFullScreen();
    }
    //点击Windows下面的Normal恢复窗口大小
    else if(action->text() == "Normal"){
        showNormal();
    }
}
//向服务器发送请求
void MainWindow::sendRequest()
{
    //通过manager发送请求,返回接收响应数据reply对象
    reply = manager->get(request);
    //发送请求后,如果服务器需要认证,将会发送信号:
    //authenticationRequired
    connect(manager,SIGNAL(
        authenticationRequired(
            QNetworkReply*,QAuthenticator*)),
        this,SLOT(onAuthenticationRequired(
            QNetworkReply*,QAuthenticator*)));
    //当认证成功,服务器将会返回响应数据,reply发送信号
    //readyread
    connect(reply,SIGNAL(readyRead()),
        this,SLOT(onReadyRead()));
    //响应数据接收结束,reply发送finished
    connect(reply,SIGNAL(finished()),
        this,SLOT(onFinished()));
}
//验证登录的槽函数
void MainWindow::onAuthenticationRequired(
    QNetworkReply*,QAuthenticator* a)
{
    //qDebug("正在验证登录...");
    a->setUser("tarenacode");
    a->setPassword("code_2013");
}
//接收响应数据的槽函数
void MainWindow::onReadyRead()
{
    //qDebug("登录成功,开始接收响应数据...");
    //读取响应数据并保存
    buf += reply->readAll();
    //保存当前URL地址
    currentUrl = reply->url();
}
//接收响应数据结束执行的槽函数
void MainWindow::onFinished()
{
    //qDebug("接收响应数据完成,本次通信结束!");
    //显示结果
    ui->textBrowser->setText(buf);
    //清空buf
    buf.clear();
    //销毁reply对象
    reply->deleteLater();
}
//处理目录链接的槽函数
void MainWindow::onAnchorClicked(
        const QUrl& url)
{
    //CSDCode/
    //qDebug() << "点击url:" << url;
    //http://code.tarena.com.cn/
    //qDebug() << "当前url:" << currentUrl;

    QUrl newUrl;
    //http://code.tarena.com.cn/CSDCode/
    //如果点击不是"../":newUrl=当前url+点击url
    if(url.toString() != "../"){
        newUrl = currentUrl.toString() +
                url.toString();
    }
    else{//处理"../"
        //如果当前在顶层目录链接,什么也不错
        if(currentUrl.toString() ==
            "http://code.tarena.com.cn/"){
            return;
        }
        //如果不在顶层目录链接,则去掉最后一级链接路径
        //查找倒数第二次出现“/”位置
        int pos = currentUrl.toString(
                    ).lastIndexOf("/",-2);
        //截断:去掉最后一级路径
        newUrl = currentUrl.toString().mid(
                    0,pos+1);
    }
    //判断newUrl是否为要下载的文件链接,是则执行文件下载操作
    //判断方法:如果点击url不是目录就是文件
    if(url.toString().lastIndexOf("/")==-1){
        qDebug() << "当前是一个文件链接:" <<
                    newUrl.toString();
        return;
    }

    //设置请求为newUrl
    request.setUrl(newUrl);
    sendRequest();

}



参考代码:

main.cpp:
#include "mainwindow.h"
#include <QApplication>
#include <QDebug>
#include <QThread>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    //qDebug() << "main:" << QThread::currentThreadId();
    return a.exec();
}
//mainwindows.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QAuthenticator>
#include <QFile>
#include <QFileInfo>
#include <QDebug>
#include <download.h>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
private slots:
    //处理菜单选项的槽函数
    void onTriggered(QAction* action);
private:
    //向服务器发送请求
    void sendRequest();
private slots:
    //处理登录认证的槽函数
    void onAuthenticationRequired(
            QNetworkReply*,QAuthenticator*);
    //接收响应数据的槽函数
    void onReadyRead();
    //接收响应数据完成时执行的槽函数
    void onFinished();
    //处理目录链接的槽函数
    void onAnchorClicked(const QUrl& url);

private:
    //下载文件
    void downloadFile(const QUrl& fileUrl);

private:
    Ui::MainWindow *ui;
public:
    QNetworkAccessManager* manager;//管理通信
private:
    QNetworkRequest request;//请求
    QNetworkReply* reply;//响应
    QUrl currentUrl;//记录当前的Url地址
    QByteArray buf;//保存接收的响应数据
    //容器,保存要支持下载的文件类型
    QList<QString> fileType;

};

#endif // MAINWINDOW_H



//mainwindows.cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "logindialog.h"
#include "download.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //点击菜单选项时,发送信号triggered
    connect(ui->menuBar,
        SIGNAL(triggered(QAction*)),
        this,SLOT(onTriggered(QAction*)));
    //向工具栏添加工具按钮
    ui->mainToolBar->addAction("Full");
    ui->mainToolBar->addAction("Normal");
    //点击工具栏的按钮时,发送信号actiontriggered
    connect(ui->mainToolBar,
        SIGNAL(actionTriggered(QAction*)),
        this,SLOT(onTriggered(QAction*)));

    manager = new QNetworkAccessManager(this);
    request.setUrl(QUrl("http://code.tarena.com.cn/"));
    //向服务器发送请求
    sendRequest();

    //点击界面的链接时,发送信号anchorClicked,
    //参数表示点击链接的URL地址
    connect(ui->textBrowser,SIGNAL(
            anchorClicked(QUrl)),
        this,SLOT(onAnchorClicked(QUrl)));
}
MainWindow::~MainWindow()
{
    delete ui;
}
//处理菜单选项的槽函数
void MainWindow::onTriggered(QAction* action)
{
    //在状态栏显示菜单选项的文本内容
    ui->statusBar->showMessage(action->text());
    if(action->text() == "Full"){//全屏
        showFullScreen();
    }
    else if(action->text() == "Normal"){//恢复
        showNormal();
    }
}
//向服务器发送请求
void MainWindow::sendRequest()
{
    //发送请求时,禁用界面,避免连续发送请求而导致的异常结束
    ui->textBrowser->setEnabled(false);
    //向服务器发送请求
    reply = manager->get(request);
    //如果服务器需要进行登录认证,manager会发送认证
    //信号:authenticationRequired
    connect(manager,SIGNAL(
        authenticationRequired(
            QNetworkReply*,QAuthenticator*)),
        this,SLOT(onAuthenticationRequired(
            QNetworkReply*,QAuthenticator*)));
    //如果认证成功,响应数据到来,发送信号readyRead
    connect(reply,SIGNAL(readyRead()),
        this,SLOT(onReadyRead()));
    //响应数据接收结束,发送信号finished
    connect(reply,SIGNAL(finished()),
        this,SLOT(onFinished()));
}
//处理登录认证的槽函数
void MainWindow::onAuthenticationRequired(
        QNetworkReply*,QAuthenticator* authenticator)
{
    //qDebug("onAuthenticationRequired");
    //从登录认证子窗口中,获取用户名和密码在进行认证
    LoginDialog login(this);
    //显示登录窗口,并进入事件循环,点击上面Ok/Cancel
    //时都会退出登录窗口,但是返回值不同.
    //如果点击Ok按钮退出,返回QDialog::Accepted
    if(login.exec() == QDialog::Accepted){
        authenticator->setUser(login.getUsername());
        authenticator->setPassword(login.getPassword());
    }
}
//接收响应数据的槽函数
void MainWindow::onReadyRead()
{
    //qDebug("onReadyRead");
    //读取响应数据,并保存
    buf += reply->readAll();
    //保存当前URl地址
    currentUrl = reply->url();
}
//接收响应数据完成时执行的槽函数
void MainWindow::onFinished()
{
    //qDebug("onFinished");
    //显示响应数据
    ui->textBrowser->setText(buf);
    //清空buf
    buf.clear();
    //销毁reply对象
    reply->deleteLater();
    //恢复界面
    ui->textBrowser->setEnabled(true);
}

//处理目录链接的槽函数
void MainWindow::onAnchorClicked(
        const QUrl &url){
    //qDebug()<<"当前的URL:"<<currentUrl.toString();
    //qDebug()<<"点击的URL:"<<url.toString();

    QUrl newUrl;
    //如果点击是不是"../",新的URL=当前URL+点击URL
    if(url.toString() != "../"){
        newUrl = currentUrl.toString() +
                    url.toString();
    }
    //如果点击是"../"
    else{
        //如果当前在首页,什么也不做
        if(currentUrl.toString() ==
            "http://code.tarena.com.cn/"){
            return;
        }
        //如果不再首页,去掉最后一级链接路径
        //查找目录路径中倒数第二次出现"/"位置
        int pos = currentUrl.toString(
                    ).lastIndexOf("/",-2);
        //字符串截断,去掉后面的路径
        newUrl =
            currentUrl.toString().mid(0,pos+1);
    }
    //判断点击URL如果不是目录则执行文件下载操作.
    if(url.toString().lastIndexOf("/")==-1){
        downloadFile(newUrl);
        return;
    }
    //设置新的请求URL
    request.setUrl(newUrl);
    //发送新的请求
    sendRequest();
}

//下载文件
void MainWindow::downloadFile(const QUrl& fileUrl)
{
    //创建下载文件线程
    QThread *thread = new QThread;
    //创建下载文件对象
    Download* download = new Download(fileUrl);
    //将下载文件对象移动到子线程中
    download->moveToThread(thread);
    //下载文件完成时,让线程退出
    connect(download,SIGNAL(downloadFinished()),
            thread,SLOT(quit()));
    //线程结束时,删除下载文件的对象
    connect(thread,SIGNAL(finished()),
            download,SLOT(deleteLater()));
    //线程结束时,删除线程对象
    connect(thread,SIGNAL(finished()),
            thread,SLOT(deleteLater()));
    //设置下载文件请求的URL
    download->request.setUrl(fileUrl);

    //发送获取下载文件的请求
    download->reply = manager->get(download->request);

    //响应数据到来,在子线程中完成下载操作
    //download已经移动到子线程中,通过信号出发里面槽将在子线程中运行
    connect(download->reply,SIGNAL(readyRead()),
            download,SLOT(ReceiveFile()));
    connect(download->reply,SIGNAL(finished()),
            download,SLOT(ReceiveFileFinished()));
    connect(download->reply,SIGNAL(downloadProgress(qint64,qint64)),
            download,SLOT(onDownloadProgress(qint64,qint64)));
    //开启子线程
    thread->start();
}


//logindialob.h:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "logindialog.h"
#include "download.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //点击菜单选项时,发送信号triggered
    connect(ui->menuBar,
        SIGNAL(triggered(QAction*)),
        this,SLOT(onTriggered(QAction*)));
    //向工具栏添加工具按钮
    ui->mainToolBar->addAction("Full");
    ui->mainToolBar->addAction("Normal");
    //点击工具栏的按钮时,发送信号actiontriggered
    connect(ui->mainToolBar,
        SIGNAL(actionTriggered(QAction*)),
        this,SLOT(onTriggered(QAction*)));

    manager = new QNetworkAccessManager(this);
    request.setUrl(QUrl("http://code.tarena.com.cn/"));
    //向服务器发送请求
    sendRequest();

    //点击界面的链接时,发送信号anchorClicked,
    //参数表示点击链接的URL地址
    connect(ui->textBrowser,SIGNAL(
            anchorClicked(QUrl)),
        this,SLOT(onAnchorClicked(QUrl)));
}
MainWindow::~MainWindow()
{
    delete ui;
}
//处理菜单选项的槽函数
void MainWindow::onTriggered(QAction* action)
{
    //在状态栏显示菜单选项的文本内容
    ui->statusBar->showMessage(action->text());
    if(action->text() == "Full"){//全屏
        showFullScreen();
    }
    else if(action->text() == "Normal"){//恢复
        showNormal();
    }
}
//向服务器发送请求
void MainWindow::sendRequest()
{
    //发送请求时,禁用界面,避免连续发送请求而导致的异常结束
    ui->textBrowser->setEnabled(false);
    //向服务器发送请求
    reply = manager->get(request);
    //如果服务器需要进行登录认证,manager会发送认证
    //信号:authenticationRequired
    connect(manager,SIGNAL(
        authenticationRequired(
            QNetworkReply*,QAuthenticator*)),
        this,SLOT(onAuthenticationRequired(
            QNetworkReply*,QAuthenticator*)));
    //如果认证成功,响应数据到来,发送信号readyRead
    connect(reply,SIGNAL(readyRead()),
        this,SLOT(onReadyRead()));
    //响应数据接收结束,发送信号finished
    connect(reply,SIGNAL(finished()),
        this,SLOT(onFinished()));
}
//处理登录认证的槽函数
void MainWindow::onAuthenticationRequired(
        QNetworkReply*,QAuthenticator* authenticator)
{
    //qDebug("onAuthenticationRequired");
    //从登录认证子窗口中,获取用户名和密码在进行认证
    LoginDialog login(this);
    //显示登录窗口,并进入事件循环,点击上面Ok/Cancel
    //时都会退出登录窗口,但是返回值不同.
    //如果点击Ok按钮退出,返回QDialog::Accepted
    if(login.exec() == QDialog::Accepted){
        authenticator->setUser(login.getUsername());
        authenticator->setPassword(login.getPassword());
    }
}
//接收响应数据的槽函数
void MainWindow::onReadyRead()
{
    //qDebug("onReadyRead");
    //读取响应数据,并保存
    buf += reply->readAll();
    //保存当前URl地址
    currentUrl = reply->url();
}
//接收响应数据完成时执行的槽函数
void MainWindow::onFinished()
{
    //qDebug("onFinished");
    //显示响应数据
    ui->textBrowser->setText(buf);
    //清空buf
    buf.clear();
    //销毁reply对象
    reply->deleteLater();
    //恢复界面
    ui->textBrowser->setEnabled(true);
}

//处理目录链接的槽函数
void MainWindow::onAnchorClicked(
        const QUrl &url){
    //qDebug()<<"当前的URL:"<<currentUrl.toString();
    //qDebug()<<"点击的URL:"<<url.toString();

    QUrl newUrl;
    //如果点击是不是"../",新的URL=当前URL+点击URL
    if(url.toString() != "../"){
        newUrl = currentUrl.toString() +
                    url.toString();
    }
    //如果点击是"../"
    else{
        //如果当前在首页,什么也不做
        if(currentUrl.toString() ==
            "http://code.tarena.com.cn/"){
            return;
        }
        //如果不再首页,去掉最后一级链接路径
        //查找目录路径中倒数第二次出现"/"位置
        int pos = currentUrl.toString(
                    ).lastIndexOf("/",-2);
        //字符串截断,去掉后面的路径
        newUrl =
            currentUrl.toString().mid(0,pos+1);
    }
    //判断点击URL如果不是目录则执行文件下载操作.
    if(url.toString().lastIndexOf("/")==-1){
        downloadFile(newUrl);
        return;
    }
    //设置新的请求URL
    request.setUrl(newUrl);
    //发送新的请求
    sendRequest();
}

//下载文件
void MainWindow::downloadFile(const QUrl& fileUrl)
{
    //创建下载文件线程
    QThread *thread = new QThread;
    //创建下载文件对象
    Download* download = new Download(fileUrl);
    //将下载文件对象移动到子线程中
    download->moveToThread(thread);
    //下载文件完成时,让线程退出
    connect(download,SIGNAL(downloadFinished()),
            thread,SLOT(quit()));
    //线程结束时,删除下载文件的对象
    connect(thread,SIGNAL(finished()),
            download,SLOT(deleteLater()));
    //线程结束时,删除线程对象
    connect(thread,SIGNAL(finished()),
            thread,SLOT(deleteLater()));
    //设置下载文件请求的URL
    download->request.setUrl(fileUrl);

    //发送获取下载文件的请求
    download->reply = manager->get(download->request);

    //响应数据到来,在子线程中完成下载操作
    //download已经移动到子线程中,通过信号出发里面槽将在子线程中运行
    connect(download->reply,SIGNAL(readyRead()),
            download,SLOT(ReceiveFile()));
    connect(download->reply,SIGNAL(finished()),
            download,SLOT(ReceiveFileFinished()));
    connect(download->reply,SIGNAL(downloadProgress(qint64,qint64)),
            download,SLOT(onDownloadProgress(qint64,qint64)));
    //开启子线程
    thread->start();
}






//loginDialob.cpp:
#include "logindialog.h"
#include "ui_logindialog.h"

LoginDialog::LoginDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::LoginDialog)
{
    ui->setupUi(this);
}

LoginDialog::~LoginDialog()
{
    delete ui;
}
//Ok按钮对应的槽函数
void LoginDialog::on_buttonBox_accepted()
{
    username = ui->usernameEdit->text();
    password = ui->passwordEdit->text();
    accept();//退出,返回QDialog::Accepted
}
//Cancel按钮对应的槽函数
void LoginDialog::on_buttonBox_rejected()
{
    reject();//退出,返回QDialog::Rejected
}
//获取用户名
const QString& LoginDialog::getUsername()
{
    return username;
}
//获取密码
const QString& LoginDialog::getPassword()
{
    return password;
}




//download.h:

#ifndef Download_H
#define Download_H

#include <QThread>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <mainwindow.h>

class Download : public QObject
{
    Q_OBJECT
public:
    //构造函数,参数表示要下载的文件连接
    Download(const QUrl& url);
    ~Download();
private slots:
    //接收文件的槽函数
    void ReceiveFile();
    //更新显示文件下载进度的槽函数
    //参数:已收到数据的字节数/总字节数
    void onDownloadProgress(qint64,qint64);
    //接收文件完成的槽函数
    void ReceiveFileFinished();

signals:
    //自定义信号,文件下载完成时发送
    void downloadFinished();
private:
    QNetworkRequest request;//请求
    QNetworkReply* reply;//响应
    QUrl fileUrl;//下载文件的URL地址
    QFile* file;

    //将MainWindow类声明为当前类的友元,友元类可以访问当前类的任何成员
    friend class MainWindow;
};

#endif // Download_H


//download.cpp:
#include "download.h"
#include "mainwindow.h"

Download::Download(const QUrl& url):fileUrl(url){
    //根据URL获取文件名
    QFileInfo fileInfo = fileUrl.path();
    QString filename = fileInfo.fileName();

    //设置要下载文件的路径
    QDir dir = QDir::home();
    //判断是否存在Download目录
    if(dir.exists("Downloads")==false){
        //如果该目录不存在则创建
        if(dir.mkdir("Downloads") == false){
            qDebug("创建目录失败");
            return;
        }
    }
    //指定下载文件放到主目录的Download下面
    QString path = QDir::homePath() + "/Downloads/"+ filename;
    //在本地创建同名的文件
    file = new QFile(path);
    //以写的方式打开文件
    file->open(QIODevice::WriteOnly);
}
Download::~Download()
{
    //qDebug() << "~Download";
}
//接收文件的槽函数
void Download::Download::ReceiveFile()
{
    if(reply->bytesAvailable()){
        file->write(reply->readAll());
    }
}
//参数:已收到数据的字节数/总字节数
void Download::onDownloadProgress(
        qint64 readBytes,qint64 totalBytes)
{
    qint64 progress=readBytes*100/totalBytes;//百分比
    qDebug() << file->fileName() << ":" << progress << "%....";
}
//接收文件完成的槽函数
void Download::Download::ReceiveFileFinished()
{
    qDebug() << file->fileName() << "文件下载完成";
    file->flush();//刷新文件流
    file->close();//关闭文件
    delete file;//销毁文件对象
    reply->deleteLater();//销毁响应对象
    emit downloadFinished();//发送信号,表示文件下载完成
}

模块总结

1)Qt图像控件相关类,QT+=widgets
QWidget
–>QDialog–>QMessageBox(消息提示框)
–>QMainWindow
–>QFrame(显示框架)->QLabel(标签)
–>QAbstractButton->QPushButton(按钮)
–>QAbstractSlider->QSlider(滑块)
–>QAbstractSpinBox->QSpinBox(选值框)
–>QLineEdit(行编辑)
2)Qt核心类,QT+= core gui
QObject、QString、QTextCodec
QPainter、QImage
QRect、QPoint、QSize
QDir、QTimer、QTime…

  • 17
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
很抱歉,我没有找到关于"Qtcreate"的相关信息。请问您是指"Qt Creator"吗?Qt Creator是一个集成开发环境(IDE),用于开发Qt应用程序。它支持多种平台,包括iOS、Android和WinRT。通过Qt Creator,您可以编写和调试代码,并使用Qt的字符串类,如QString和QByteArray,来处理字符串。QString是Qt最常用的字符串类,而QByteArray适用于处理以'\0'结尾的传统字符串。QByteArray可以处理多字节编码的字符串,如UTF-8、GBK和Big5。在使用QByteArray时,程序员需要确定所使用的字符串编码格式。此外,QByteArray还使用了隐式共享机制来提高运行效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Qt Create 笔记 (一) 1.QString 2.QByteArray](https://blog.csdn.net/linbounconstraint/article/details/51497782)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [Game Programming Using QT(PACKT,2016)](https://download.csdn.net/download/vanridin/9431499)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孔二他老表

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值