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…