类继承关系图
(上面为Base类,下面为Derived类)
窗口与子部件
窗口:没有父部件的部件,称为顶级部件
子部件:非窗口部件
新建空的qmake项目mywidget1
//mywidget1.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS
QT += widgets
SOURCES += \
main.cpp
//main.cpp
#include<QTWidgets>
int main(int argc,char *argv[])
{
QApplication a(argc,argv);
QWidget *widget=new QWidget();
widget->setWindowTitle(QObject::tr("I am widget"));
QLabel *label=new QLabel();
label->setWindowTitle(QObject::tr("I am label"));
label->setText(QObject::tr("label:I am a window"));
label->resize(350,20);
QLabel *label2=new QLabel(widget);
label2->setText(QObject::tr("label2:不是独立窗口,只是widget的子部件"));
label2->resize(350,20);
label->show();
widget->show();
int ret=a.exec();
delete label;
delete widget;
return ret;
}
运行结果
窗口类型
窗口一般有边框和标题栏,但不必需
QWidget的构造函数有两个参数:QWidget *parent = 0 和 Qt::WindowFlags f = 0
其中,parent是父窗口部件,默认为0即没有父窗口
f是Qt::WindowType枚举类型,默认为0即Qt::Widget窗口类型
更改新建对象的两行:
QWidget *widget=new QWidget(0,Qt::Dialog);
QLabel *label=new QLabel(0,Qt::SplashScreen);
运行结果:
再次更改:
QWidget *widget=new QWidget(0,Qt::Dialog|Qt::FramelessWindowHint);
QLabel *label=new QLabel(0,Qt::SplashScreen|Qt::WindowStaysOnTopHint);
label2(widget)窗口隐藏到后面,label窗口停留在所有窗口之上
窗口几何布局
帮助文档索引Window and Dialog Widgets
QWidget提供了几个处理小组件的几何形状的函数。其中一些函数在纯客户区域(即不包括窗口框架的窗口)上操作,另一些则包括窗口框架。这种区分是以一种透明地涵盖最常见的用法的方式进行的。
包括窗口框架:x(), y(), frameGeometry(), pos(), move()
不包括窗口框架:geometry(), width(), height(), rect(), size()
断点调试
//main.cpp
#include<QApplication>
#include<QWidget>
int main(int argc,char *argv[])
{
QApplication a(argc,argv);
QWidget widget;
int x=widget.x();
int y=widget.y();
QRect geometry=widget.geometry();
QRect frame=widget.frameGeometry();
return a.exec();
}
直接debug无反应
使用qDebug()调试
//main.cpp
#include<QApplication>
#include<QWidget>
#include<QDebug>
int main(int argc,char *argv[])
{
QApplication a(argc,argv);
QWidget widget;
widget.resize(400,300);
widget.move(200,100);
widget.show();
int x=widget.x();
qDebug("x:%d",x);
int y=widget.y();
qDebug("y:%d",y);
QRect geometry=widget.geometry();
QRect frame=widget.frameGeometry();
qDebug()<<"geometry:"<<geometry<<"frame:"<<frame;
qDebug()<<"pos:"<<widget.pos()<<endl<<"rect:"<<widget.rect()<<endl
<<"size:"<<widget.size()<<endl<<"width:"<<widget.width()<<endl
<<"height:"<<widget.height()<<endl;
return a.exec();
}
运行结果:
对话框QDialog
模态和非模态对话框
模态对话框:关闭它之前,不能再与同一个应用程序的其他窗口交互,使用exec()
非模态对话框:既可以与它交互,也可以与同一程序的其他窗口交互,使用new和show()
新建项目mywidget1
//mywidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
#include<QDialog>
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MyWidget)
{
ui->setupUi(this);
QDialog dialog(this);
dialog.show();
}
MyWidget::~MyWidget()
{
delete ui;
}
运行结果:小的窗口一闪而过(dialog对象在构造函数执行结束后自动释放)
dialog创建两行改为:
QDialog *dialog=new QDialog(this);
dialog->show();
运行结果:一大一小 (大的是MyDialog窗口,小的是对话框)
不用指针,再将这两行改为:
QDialog dialog(this);
dialog.exec();
运行结果:只弹出小的,关闭后,弹出大的。称为模态对话框
多窗口切换
继续使用mydialog1项目
信号和槽
进入mywidget.ui,拖用Push Button和Label,Push Button的objectName改为showChildButton,文本改为“显示子窗口”,Label的文本改为“我是主页面”
保存后在mywidget.h的MyWidget类最后添加:
public slots:
void showChildDialog();
点击showChildDialog,按alt+enter,点击"在mywidget.cpp中添加定义"
//mywidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
#include<QDialog>
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MyWidget)
{
ui->setupUi(this);
connect(ui->showChildButton,&QPushButton::clicked,this,&MyWidget::showChildDialog);
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::showChildDialog()
{
QDialog *dialog=new QDialog(this);
dialog->show();
}
已经解决 no member named ‘showChildButton’ in ‘Ui::MyWidget’
connect行出现该报错,原因为未构建项目,点击左下角的“锤子”即可
运行结果:
点击“显示子窗口”:
关联信号和槽的connect函数的四个参数:
发射信号的对象、发射的信号、接收信号的对象,要执行的槽
自动关联
在mywidget.cpp中,将showChildDialog()槽命名为on_showChildButton_clicked(),即on_发射信号的对象_信号名
运行结果同上。自动关联方式在Qt设计器中更简便,但在其他设计器中还需要其他操作(书P53)
自定义对话框
继续使用项目mydialog1
新建MyDialog类
Debug without Buttons,类名MyDialog,设计模式中添加两个Push Button,文本为“进入主界面”和“退出程序”
点击第二个图标,进入信号和槽的编辑模式
关联如下,若要取消,可点击clicked()文字,文本框为红色时按delete,或右键
点击第一个图标,返回部件编辑模式
“进入主界面”右键,转到槽
选择clicked()
点击OK后跳转到mydialog.cpp代码
//mydialog.cpp的改动部分
void MyDialog::on_pushButton_clicked()
{
accept();
//对于一个使用exec()实现的模态对话框,执行该槽会隐藏模态对话框
//并返回QDialog::Accepted值,可用于判断哪个按钮被按下
//类似还有reject()槽,返回QDialog::Rejected值,可以关联“退出程序”
}
//main.cpp
#include "mywidget.h"
#include <QApplication>
#include"mydialog.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyWidget w;
MyDialog dialog;
//若按下“进入主界面”,则显示主界面,否则退出程序
if(dialog.exec()==QDialog::Accepted){
w.show();
return a.exec();
}
else return 0;
}
运行结果:
进入mywidget.ui,再添加两个Push Button,“退出”同样“牵红线”关联close,“重新登陆”同样转到槽
//mywidget.cpp改动部分
//添加头文件,否则MyDialog不识别!!!
#include"mydialog.h"
//代码正文...
void MyWidget::on_pushButton_clicked()
{
//先隐藏主界面
close();
//若按下“进入主界面”,则再次显示主界面,否则退出
MyDialog dlg;
if(dlg.exec()==QDialog::Accepted) show();
}
运行结果:
点击“重新登录”后,回到Dialog页面,无限循环
标准对话框(末尾附8button完整代码)
新建Qt Widget项目mydialog2
mywidget.ui拖入Push Button,修改文本如下:
Tips:书中的顺序是按行拖用,即1是颜色,2是文件…但本博客是按列拖用,唯一的影响是槽的编号
显示不全的button框(比如“错误信息对话框”),可以修改宽度
颜色对话框
在.ui界面右键button,转到槽
//mywidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
#include<QDebug>
#include<QColorDialog>
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MyWidget)
{
ui->setupUi(this);
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::on_pushButton_clicked()
{
//getColor的3个参数:设置初始颜色、指定父窗口、设置对话框标题
QColor color=QColorDialog::getColor(Qt::red,this,tr("颜色对话框"));
qDebug()<<"color:"<<color;
}
已经解决报错“常量中有换行符”
对应行改为:
QColor color=QColorDialog::getColor(Qt::red,this,tr("颜色"));
//建议直接用英语
color: QColor(ARGB 1, 0, 1, 0.498039)
4个值代表:透明度(alpha)、R、G、B
其中透明度为1则完全不透明,0.498039为127/255
添加对透明度的设置:
对应行代码改为
QColor color=QColorDialog::getColor(Qt::red,this,tr("颜色"),QColorDialog::ShowAlphaChannel);
运行结果:
color: QColor(ARGB 0.392157, 0, 1, 0.498039)
0.392157=100/255
前面是静态函数方法,不用创建对象
更灵活的方式:先创建对象,再设置各项,结果相同
//mywidget.cpp改动部分
void MyWidget::on_pushButton_clicked()
{
QColorDialog dialog(Qt::red,this);
dialog.setOption(QColorDialog::ShowAlphaChannel);
dialog.exec();
QColor color=dialog.currentColor();
qDebug()<<"color:"<<color;
}
文件对话框
在.ui界面右键button,转到槽
//mywidget.cpp改动部分
#include<QFileDialog>
//代码正文
void MyWidget::on_pushButton_5_clicked()
{
//4个参数:指定父窗口、设置对话框标题、指定默认打开路径、设置文件类型过滤器
QString filename=QFileDialog::getOpenFileName(this,tr("文件"),"D:",tr("图片文件(*png *jpg)"));
qDebug()<<"filename:"<<filename;
}
运行后,出现8个button,点击“文件对话框”后,选一张图片打开后,qDebug输出图片路径
我的图片路径为 filename: “D:/wj.png”
改动一行代码,添加文件类型:
QString filename=QFileDialog::getOpenFileName(this,tr("文件"),"D:",tr("图片文件(*png *jpg);;文本文件(*txt)"));
运行结果:
再改动这行代码,同时选择多个文件:
QStringList filenames=QFileDialog::getOpenFileNames(this,tr("文件"),"D:",tr("图片文件(*png *jpg)"));
运行结果:
字体对话框
在.ui界面右键button,转到槽
//mywidget.cpp改动部分
#include<QFontDialog>
void MyWidget::on_pushButton_2_clicked()
{
bool ok;
QFont font=QFontDialog::getFont(&ok,this);
if(ok) ui->pushButton_2->setFont(font);
else qDebug()<<tr("未选择");
//同理,这里中文字数太多也会报错“常量中有换行符”
}
运行结果:
“字体对话框”字号变小
不选择字体,直接cancel:
输入对话框
在.ui界面右键button,转到槽
//mywidget.cpp改动部分
#include<QInputDialog>
void MyWidget::on_pushButton_6_clicked()
{
bool ok;
//getText()的参数:指定父窗口、设置窗口标题、设置标签显示文本、字符串的显示模式、默认输入字符串、按下按钮信息的bool变量
QString string=QInputDialog::getText(this,tr("stringDialog"),tr("userName"),QLineEdit::Normal,tr("admin"),&ok);
if(ok) qDebug()<<"string:"<<string;
//用箭头调整大小时,每次变化10
int value1=QInputDialog::getInt(this,tr("intDialog"),tr("-1000~1000"),100,-1000,1000,10,&ok);
if(ok) qDebug()<<"value1:"<<value1;
//小数的位数是2
double value2=QInputDialog::getDouble(this,tr("doubleDialog"),tr("-1000~1000"),0.00,-1000,1000,2,&ok);
if(ok) qDebug()<<"value2:"<<value2;
QStringList items;
items<<tr("item1")<<tr("item2");
//true是设置条目可以被更改
QString item=QInputDialog::getItem(this,tr("item"),tr("chooseOrInput"),items,0,true,&ok);
if(ok) qDebug()<<"item:"<<item;
}
消息对话框
在.ui界面右键button,转到槽
Tips:运行时提示音不同,由操作系统设置
//mywidget.cpp改动部分
#include<QMessageBox>
void MyWidget::on_pushButton_3_clicked()
{
int ret1=QMessageBox::question(this,tr("questionDialog"),tr("Do you know Qt?"),QMessageBox::Yes,QMessageBox::No);
if(ret1==QMessageBox::Yes) qDebug()<<tr("Question!");
int ret2=QMessageBox::information(this,tr("informationDialog"),tr("Qt book."),QMessageBox::Ok);
if(ret2==QMessageBox::Ok) qDebug()<<tr("Information!");
int ret3=QMessageBox::warning(this,tr("warningDialog"),tr("cannot stop in advance!"),QMessageBox::Abort);
if(ret3==QMessageBox::Abort) qDebug()<<tr("Warning!");
int ret4=QMessageBox::critical(this,tr("criticalDialog"),tr("Error!Close all!"),QMessageBox::YesAll);
if(ret4==QMessageBox::YesAll) qDebug()<<tr("Error!");
QMessageBox::about(this,tr("aboutDialog"),tr("Welcome!"));
}
进度对话框
在.ui界面右键button,转到槽
//mywidget.cpp改动部分
#include<QProgressDialog>
void MyWidget::on_pushButton_7_clicked()
{
//参数:对话框标签内容、取消按钮的显示文本、最小值、最大值、父窗口
QProgressDialog dialog(tr("copyProgress"),tr("cancel"),0,50000,this);
dialog.setWindowTitle(tr("progressDialog"));
dialog.setWindowModality(Qt::WindowModal);
dialog.show();
for(int i=0;i<50000;i++){
dialog.setValue(i);
QCoreApplication::processEvents();
if(dialog.wasCanceled()) break;
}
dialog.setValue(50000);
qDebug()<<tr("Completed!");
}
错误信息对话框
//本段是有问题的示例代码,解决见下文
//mywidget.h添加类前置声明和私有对象
class QErrorMessage;
QErrorMessage *errordlg;
//mywidget.cpp改动部分
#include<QErrorMessage>
//构造函数
MyWidget::MyWidget(QWidget *parent)
: QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
// 初始化 errordlg 对象
errordlg=new QErrorMessage(this);
}
//析构函数
MyWidget::~MyWidget()
{
delete ui;
// 清理 errordlg 对象
delete errordlg;
}
//在.ui界面右键button,转到槽
void MyWidget::on_pushButton_4_clicked()
{
errordlg->setWindowTitle(tr("errormessageDialog"));
errordlg->showMessage(tr("errorMessage!"));
}
已经解决:报错“重复定义”
//mywidget.h添加类前置声明和私有对象
// 前置声明 QErrorMessage 类
class QErrorMessage;
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = nullptr);
~MyWidget();
private slots:
//若干个槽,此处省略
void on_pushButton_clicked();
private:
Ui::MyWidget *ui;
// 在这里添加私有对象指针!!!
QErrorMessage *errordlg;
};
运行结果:
默认有一个’Show this message again’复选框,勾选后点击OK返回8个button界面,再次点击“错误信息对话框”,仍会弹出这个框;取消勾选后,点击“错误信息对话框”无反应
向导对话框
//mywidget.h
#include<QWizard>
private:
Ui::MyWidget *ui;
QWizardPage *createPage1();
QWizardPage *createPage2();
QWizardPage *createPage3();
//mywidget.cpp改动部分
QWizardPage * MyWidget::createPage1()
{
QWizardPage *page=new QWizardPage;
page->setTitle(tr("Introduction"));
return page;
}
QWizardPage * MyWidget::createPage2()
{
QWizardPage *page=new QWizardPage;
page->setTitle(tr("Choose"));
return page;
}
QWizardPage * MyWidget::createPage3()
{
QWizardPage *page=new QWizardPage;
page->setTitle(tr("Result"));
return page;
}
//在.ui界面右键button,转到槽
void MyWidget::on_pushButton_8_clicked()
{
QWizard wizard(this);
wizard.setWindowTitle(tr("guideDialog"));
wizard.addPage(createPage1());
wizard.addPage(createPage2());
wizard.addPage(createPage3());
wizard.exec();
}
其他窗口部件
QFrame类族
QFrame是带边框的部件的基类,主要通过边框形状和阴影的组合,实现边框效果
帮助文档的效果图:
其中,lineWidth是边框边界线的宽度,midLineWidth是在边框额外插入的线的宽度(为了形成3D效果)
书中的表格:
QLabel
新建Qt Widget项目myframe,选择基类QWidget,类名MyWidget
打开mywidget.ui,拖用Frame,右下角属性栏更改如下:
此时Frame如图:
等效的方式:
//mywisget构造函数内部
ui->frame->setFrameShape(QFrame::Box);
ui->frame->setFrameShadow(QFrame::Sunken);
ui->frame->setLineWidth(5);
ui->frame->setMidLineWidth(10);
拖用Label,将其拖大些,右下角属性栏更改如下:
此时界面如图:
//mywisget构造函数内部添加
QFont font;
font.setFamily("华文行楷");
font.setPointSize(20);
font.setBold(true);
font.setItalic(true);
ui->label->setFont(font);
QString string =tr("too long,need omitting");
//elidedText()的参数:要省略的文本、省略号出现的位置、文本的长度(单位是像素)
QString str=ui->label->fontMetrics().elidedText(string,Qt::ElideRight,200);
ui->label->setText(str);
运行结果:
再添加一行:
ui->label->setPixmap(QPixmap("D:/wj.png"));
运行结果:图片覆盖原来的文本
QLCDNumber(液晶数字)
.ui界面拖用LCDNumber,右下角属性栏改为:
构建后运行,多了一个液晶显示屏如下:
QtackedWidget(部件栈)
.ui拖用List Wisget,右键选择“编辑项目”,左下角“绿色加号”添加如下:
拖用Stacked Widget,再拖用Label到Stacked Widget里面,编辑文本为firstPage,点击Stacked Widget右上角箭头,再拖用Label到里面,编辑文本为secondPage(若结果对应关系相反,见下文)
右下角更改Stacked Widget属性栏如下:
在“编辑信号与槽”模式牵红线,更改如下:
已经解决:运行结果对应关系相反
Stacked Widget具有栈的特性,编辑Label时,先写secondPage,再写firstPage
运行结果:
QToolBox(层叠窗口抽屉)
.ui拖用Tool Box,右键选择“插入页”,每个页的右下角属性栏更改如下:
frameShape为Box;currentItemText分别为“好友”、“黑名单”、“陌生人”
运行结果:
按钮部件
QPushButton
新建Qt Widget项目mybutton,选择基类QWidget,类名MyWidget
.ui拖用3个Push Button,右下角属性栏更改如图,数字分别为1,2,3
继续更改属性栏:Button1的checkable勾选,Button2的flat勾选
其中checkable属性具有两个状态:选择or未选中;flat属性按钮不显示边框
Button1转到槽,选择toggled(bool)如下:
//mywidget.cpp添加如下:
#include<QDebug>
void MyWidget::on_pushButton_1_toggled(bool checked)
{
qDebug()<<tr("Push or not:")<<checked;
}
运行时,按下Button1,会显示:“Push or not:” true,但再按一下会显示:“Push or not:” false,无限循环
//mywidget.cpp继续在构造函数中添加如下:
#include<QMenu>
ui->pushButton_1->setText(tr("&nihao"));
ui->pushButton_2->setText(tr("help(&H)"));
ui->pushButton_2->setIcon(QIcon("D:/wx.png"));
ui->pushButton_3->setText(tr("z&oom"));
QMenu *menu=new QMenu(this);
menu->addAction(QIcon("D:/txqq.png"),tr("Enlarge"));
ui->pushButton_3->setMenu(menu);
其中字母前加“&”符号,可以设置按下该按钮的快捷键为alt+该字母,如案例中分别为alt+n、alt+h、alt+o
运行结果:
QGroupBox、QCheckBox、QRadioButton
.ui拖用两个Group Box,文本改为“复选框”、“单选框”
“复选框”中拖入3个Check Box,文本改为“跑步”、“踢球”、“游泳”;“游泳”的右下角属性栏tristate勾选
tristate勾选后,具有3个状态:“不改变”、“选中”、“未选中”
“单选框”中拖入3个Radio Button,文本改为“优秀”、“一般”、“不好”
运行结果:
QLineEdit
新建Qt Widget项目mylineedit,选择基类QWidget,类名MyWidget
.ui拖用4个Label和Line Edit如图,右下角属性栏Line Edit的objectName为lineEdit_1、2、3、4
显式模式
lineEdit_1继续更改属性的显示模式为Password,其中Normal为显式文本显示、NoEcho为不显示文本、Password显示为小黑点/星号、PasswordRchoOnEdit在编辑时为显式,其他情况为密码模式
运行结果:
输入掩码
lineEdit_2的属性inputMask中编辑如下:
AA-90-bb! aa#H;*
书中掩码字符表:
转到returnPressed()槽
//mywidget.cpp添加部分
#include<QDebug>
void MyWidget::on_lineEdit_2_returnPressed()
{
ui->lineEdit_3->setFocus();
qDebug()<<ui->lineEdit_2->text();
qDebug()<<ui->lineEdit_2->displayText();
}
运行结果:
qDebug()输出:
“DD-10-01 dd#d”
“DD-10-01 dd#d”
输入验证
//mywidget.cpp构造函数添加部分
#include<QIntValidator>
//添加验证器,只能输入100~999
QValidator *validator=new QIntValidator(100,999,this);
ui->lineEdit_3->setValidator(validator);
运行结果:
若输入大于999,则只取前3位;若输入小于100,则qDebug()不显示;只有100~999之间正常
自动补全
//mywidget.cpp构造函数添加部分
#include<QCompleter>
QStringList wordList;
wordList<<"Qt"<<"Qt Creator"<<tr("hello");
QCompleter *completer=new QCompleter(wordList,this);
completer->setCaseSensitivity(Qt::CaseInsensitive);
ui->lineEdit_4->setCompleter(completer);
运行结果:输入Q,自动提示补全
QAbstractSpinBox
新建Qt Widget项目myspinbox,选择基类QWidget,类名MyWidget
.ui拖用Time Edit、Date Edit、Date/Time Edit
Time Edit 的 displayFormat属性改为如下,即12小时制
Date Edit 勾选 calendarPopup,即使用弹出的日历设置日期
hh:mm:ssA
//mywidget.cpp构造函数添加部分
ui->dateTimeEdit->setDateTime(QDateTime::currentDateTime());
ui->dateTimeEdit->setDisplayFormat(tr("yyyy.MM.dd.ddd HH:mm:ss"));
运行结果:
QSpinBox、QDoublrSpinBox
.ui拖用Spin Box和Double Spin Box
属性栏,前缀prefix可以是¥,后缀suffix可以是%,decimals是小数点后的位数
运行结果:
QAbstractSlider
新建Qt Widget项目myslider,选择基类QWidget,类名MyWidget
拖用刻度表盘Dial(中圆)、Horizontal Scroll Bar(左)、Vertical Scroll Bar(上)、Horizontal Slider(右)、Vertical Slider(下)、Spin Box(左下)
Dial属性栏最下面的wrapping勾选为首尾相连,notchesVisible勾选为显式刻度
牵红线Dial的sliderMoved(int)与其他5个部件的setValue(int)
运行结果:转动中间的刻度表盘Dial,其他部件都随之变化
本篇所有项目的代码压缩包
链接:本篇所有项目的代码压缩包
提取码:ldwo
参考书目
链接:Qt Creator快速入门第三版
提取码:3ryu