Qt 是一个跨平台的 C++图形用户界面应用程序框架。它为应用程序开发者提供建立艺术级图形界面所需的所有功能。它是完全面向对象的,很容易扩展,并且允许真正的组件编程。
Qt 按照不同的版本发行,分为商业版和开源版
商业版
为商业软件提供开发,他们提供传统商业软件发行版,并且提供在商业有效期内的免费升级和技术支持服务
开源的 LGPL版本:
为了开发自有而设计的开放源码软件,它提供了和商业版本同样的功能,在GNU 通用公共许可下,它是免费的。
官网:https://www.qt.io/download-open-source
完成
crtl+R创建
在使用 Qt 向导生成的应用程序.pro 文件格式如下:
QT += core gui //包含的模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets //大于 Qt4 版本 才包含 widget 模块,低于包含在上面gui
TARGET = QtFirst //应用程序名 生成的.exe 程序名称
TEMPLATE = app //模板类型 应用程序模板
SOURCES += main.cpp\ //源文件
mywidget.cpp
HEADERS += mywidget.h //头文件
.pro 就是工程文件(project),它是 qmake 自动生成的用于生产 makefile 的配置文件。
.pro 文件的写法如下:
注释 :从“#”开始,到这一行结束。
模板变量告诉 qmake 为这个应用程序生成哪种 makefile。下面是可供使用的 选择:TEMPLATE = app
app -建立一个应用程序的 makefile。这是默认值,所以如果模板没有被指定,这个将被使用。
lib - 建立一个库的 makefile。
vcapp - 建立一个应用程序的 VisualStudio 项目文件。
vclib - 建立一个库的 VisualStudio 项目文件。
subdirs -这是一个特殊的模板,它可以创建一个能够进入特定目录并且 为一个项目文件生成 makefile 并且为它调用make 的 makefile。
#指定生成的应用程序名: TARGET = QtDemo
#工程中包含的头文件 HEADERS += include/painter.h
#工程中包含的.ui 设计文件 FORMS += forms/painter.ui
#工程中包含的源文件 SOURCES += sources/main.cpp sources
#工程中包含的资源文件 RESOURCES += qrc/painter.qrc
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
这条语句的含义是,如果 QT_MAJOR_VERSION 大于 4(也就是当前使用的 Qt5 及更高版本)需要增加 widgets 模块。如果项目仅需支持 Qt5,也可以直接 添加“QT += widgets”一句。不过为了保持代码兼容,最好还是按照 QtCreator 生成的语句编写。
#配置信息
CONFIG 用来告诉 qmake 关于应用程序的配置信息。
CONFIG += c++11 //使用 c++11 的特性 。在这里使用“+=”,是因为我们添加我们的配置选项到任何一个已经存在中。这样做比使用“=”那样替换已经指定的所有选项更安全。
例如:QPushButton
Public Functions //公共函数
Public Slots //公共槽函数
Header: #include //头文件
qmake: QT += widgets //所在模块
Inherits: QAbstractButton //父类
Inherited By: QCommandLinkButton //子类
首先,对于main函数
#include "widget.h"
#include
int main(int argc, char *argv[])
{
//应用程序类,初始化我们的应用程序
QApplication a(argc, argv);
//创建一个主窗口控件
Widget w;
//显示一个窗口,hide隐藏窗口(窗口默认隐藏)
w.show();
//主事件循环,带阻塞,等待用户操作界面
return a.exec();
}
解释:
Qt 系统提供的标准类名声明头文件没有.h 后缀 ,即系统头文件
Qt 一个类对应一个头文件,类名就是头文件名
QApplication 应用程序类
管理图形用户界面应用程序的控制流和主要设置
是 Qt 的整个后台管理的命脉它包含主事件循环,在其中来自窗口系统和其它资源的所有事件处理和调度。它也处理应用程序的初始化和结束, 并且提供对话管理
对于任何一个使用 Qt 的图形用户界面应用程序,都正好存在一个 QApplication 对象,而不论这个应用程序在同一时间内是不是有 0、1、 2 或更多个窗口
a.exec() 程序:主事件循环:进入消息循环,等待对用户输入进行响应。这里 main()把控制权转交给 Qt,Qt 完成事件处理工作,当应用程序退出的时候 exec()的值就会返回。 在 exec()中,Qt 接受并处理用户和系统的事件并且把它们传递给适当的窗口部件
在实际设计界面时,我们只需要在窗口控件的构造函数中设计即可,而不在main函数中设计
首先我们知道,窗口控件widget继承于QWidget
因此我们创建按钮时,要在QWidget里寻找对应的函数
setWindowTitle(const QTstring &)是在槽函数
同时这里也可以看到,函数左边有个带凹槽的图标,即说明是槽函数
this->setWindowTitle("The first window");
运行
在输出为中文时,显示可能为乱码,将文件编码改为UTF-8即可
void setFixedSize(const QSize &s)
void setFixedSize(int w, int h)
有两个重载,我们使用第二个,指定宽高
this->setFixedSize(400,300);
此时,鼠标在边缘无法再拖动修改大小
使用QPushButton
The QPushButton widget provides a command button. More...
Header: #include
qmake: QT += widgets
Inherits: QAbstractButton
Inherited By:QCommandLinkButton
构造函数:3个重载
QPushButton(const QIcon &icon, const QString &text, QWidget *parent = nullptr)
QPushButton(const QString &text, QWidget *parent = nullptr)
//文本 //要放置在哪里,即哪个窗口
QPushButton(QWidget *parent = nullptr)
包含头文件后,我使用第二个
QPushButton *button= new QPushButton("button1",this);
new完后,不需要手动 delete,关闭后将会被父类结束所有窗口
接下来创建button2
QPushButton *button= new QPushButton("button1",this);
QPushButton *button2= new QPushButton("button2",this);
发现button1消失,因为2个都是默认控件,会自动显示到窗口的左上方
这时就需要将button2移动到其他地方
经过查询,是在QWidget类中才有move移动函数
void move(const QPoint &)
void move(int x, int y)
button2->move(200,150);
// button3
QPushButton *button3= new QPushButton;
button3->setParent(this); //设置父亲,即将展示在哪个窗口
button3->setText("button3");//设置文字
button3->move(100,75); //移动位置
上面代码中,一个按钮其实就是一个 QPushButton 类下的对象,如果只是创建出对象, 是无法显示到窗口中的,所以我们需要依赖一个父窗口,也就是指定一个父亲利用 setParent 函数即可,如果想设置按钮上显示的文字利用 setText,移动按钮位置用 move
// 重设大小
this->resize(800,600);
注意:前面有setFixedSize则必须将其注释,否则大小固定
在 Qt 中创建对象的时候会提供一个 Parent 对象指针,下面来解释这个 parent到底是干什么的。
QObject 是以对象树的形式组织起来的。
QWidget 是能够在屏幕上显示的一切组件的父类。
Qt 引入对象树的概念,在一定程度上解决了内存问题,
如果 QObject 在栈上创建,Qt 保持同样的行为。正常情况下,这也不会发生什么问题。来看下下面的代码片段:
{
QWidget window;
QPushButton quit("Quit", &window);
}
作为父组件的 window 和作为子组件的 quit 都是 QObject 的子类(事实上,它 们都是 QWidget 的子类,而 QWidget 是 QObject 的子类)。这段代码是正确的, quit 的析构函数不会被调用两次,因为标准 C++要求,局部对象的析构顺序应 该按照其创建顺序的相反过程。因此,这段代码在超出作用域时,会先调用 quit的析构函数,将其从父对象 window 的子对象列表中删除,然后才会再调用 window 的析构函数。 但是,如果我们使用下面的代码
{
QPushButton quit("Quit");
QWidget window;
quit.setParent(&window);
}
情况又有所不同,析构顺序就有了问题。我们看到,在上面的代码中,作为父对象的 window 会首先被析构,因为它是最后一个创建的对象。在析构过程中,它会调用子对象列表中每一个对象的析构函数,也就是说,quit 此时就被析构了。然后,代码继续执行,在 window 析构之后,quit 也会被析构,因为 quit 也是一个局部变量,在超出作用域的时候当然也需要析构。但是,这时候已经是第二次调用 quit 的析构函数了,C++ 不允许调用两次析构函数,因此,程序崩溃了。
由此我们看到,Qt 的对象树机制虽然帮助我们在一定程度上解决了内存问题,但是也引入了一些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰一下,所以,我们最好从开始就养成良好习惯,在 Qt 中,尽量在构造的时候就指定 parent 对象,并且大胆在堆上创建
坐标体系: 以左上角为原点(0,0),X 向右增加,Y 向下增加
对于嵌套窗口,其坐标是相对于父窗口来说的
概述:
信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当 某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号 (signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣, 它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函 数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。
void clicked(bool checked = false) // 发出信号
bool close() //槽函数,执行关闭动作
connect(sender, signal, receiver, slot);
connect(信号发起者,信号,信号接收者,行为函数);
这里有小喇叭,即信号
// 信号:单击关闭
connect(button2,&QPushButton::clicked,this,&QWidget::close);
使用 connect()可以让我们连接系统提供的信号和槽。但是,Qt 的信号槽机制并不仅仅是使用系统提供的那部分,还允许我们自己设计自己的信号和槽。 下面我们看看使用 Qt 的信号槽
首先创建新的工程,然后创建学生类student和老师类teacher
右击工程,点击Add new
创建
class studnet : public QObject
{
Q_OBJECT
public:
explicit studnet(QObject *parent = nullptr);
signals:
};
注意:
定义信号:
定义槽函数:
用户使用emit可以发出信号
student.h里:
public slots:
void treat();
student.cpp里
void student::treat()
{
qDebug()<<"hello T"<
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
this->resize(600,400);
teacher *tea = new teacher(this); //实例化一个老师
student *stu = new student(this); //实例化一个学生
// 建立二者之间关系
connect(tea,&teacher::send,stu,&student::treat);
// 此时无法触发条件
QPushButton *btn = new QPushButton("Click",this);
connect(btn,&QPushButton::clicked,[=](){
// 单击后,tea发出信号
emit tea->send();
});
}
student.h里:
public slots:
void treat();
void treat(QString foodname);
student.cpp里
void student::treat(QString foodname)
{
qDebug()<<"hello T:"<
widget:
#define n 0 //决定调用哪一块函数
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
this->resize(600,400);
teacher *tea = new teacher(this); //实例化一个老师
student *stu = new student(this); //实例化一个学生
// 建立二者之间关系
//connect(tea,&teacher::send,stu,&student::treat);
#if n // 重载,调用无参的信号和槽
void (teacher::*p1)() = &teacher::send;
void (student::*p2)() = &student::treat;
connect(tea,p1,stu,p2);
QPushButton *btn = new QPushButton("Click",this);
connect(btn,&QPushButton::clicked,[=](){
// 单击后,tea发出信号
emit tea->send();
});
#else // 重载,调用有参的信号和槽
void (teacher::*p1)(QString foodname) = &teacher::send;
void (student::*p2)(QString foodname) = &student::treat;
connect(tea,p1,stu,p2);
QPushButton *btn = new QPushButton("Click",this);
connect(btn,&QPushButton::clicked,[=](){
// 单击后,tea发出信号
emit tea->send("joyce");
});
#endif
}
在Qt5中,我们刚才是这么写信号槽:
connect(tea,&teacher::send,stu,&student::treat)
而在Qt4是这样:
connect(tea,SIGNAL(send()),stu,SLOT(treat()))
这里使用了 SIGNAL 和 SLOT 这两个宏,将两个函数名转换成了字符串。注意到 connect()函数的 signal 和 slot 都是接受字符串,一旦出现连接不成功的情 况,Qt4 是没有编译错误的(因为一切都是字符串,编译期是不检查字符串是否匹配),而是在运行时给出错误。这无疑会增加程序的不稳定性。
Qt5 在语法上完全兼容 Qt4,而反之是不可以的
Qt5 因为不接收参数,因此不能重载
例:创建2个窗口,第一个窗口有next按钮,点击进入下一个L窗口,下一个窗口有back按钮,点击回到上一个窗口
首先新建LWidget窗口类
方法1
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
this->resize(800,600);
this->setWindowTitle("登陆界面");
LWidget *win1=new LWidget(); //创建第二个窗口
{ // 创建按钮1并设置属性
QPushButton *btn1 =new QPushButton(this);
btn1->move(400,300);
btn1->setText("next");
// 设置按钮1的作用
connect(btn1,&QPushButton::clicked,[=]()
{
this->hide();
win1->show();
});
}
{ // 创建按钮2并设置属性
QPushButton *btn2 =new QPushButton(win1);
btn2->move(500,200);
btn2->setText("back");
// 设置按钮2的作用
connect(btn2,&QPushButton::clicked,[=]()
{
win1->hide();
this->show();
});
}
}
LWidget::LWidget(QWidget *parent) : QWidget(parent)
{ // 设置第二个窗口的属性
this->resize(800,600);
this->setWindowTitle("确认界面");
}
方法2
lwidget.h:
signals:
void back();
lwidget.cpp:
LWidget::LWidget(QWidget *parent) : QWidget(parent)
{ // 设置第二个窗口的属性
this->resize(800,600);
this->setWindowTitle("确认界面");
QPushButton *btn =new QPushButton("back",this);
btn->move(300,400);
connect(btn,&QPushButton::clicked,[=]()
{ // 如有点击,则发出back信号
emit this->back();
});
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
this->resize(800,600);
this->setWindowTitle("登陆界面");
LWidget *win1=new LWidget(); //创建第二个窗口,不在当前窗口上,因此不加this
{ // 创建按钮1并设置属性
QPushButton *btn1 =new QPushButton(this);
btn1->move(400,300);
btn1->setText("next");
// 设置按钮1的作用
connect(btn1,&QPushButton::clicked,[=]()
{
this->hide();
win1->show();
});
}
// 检测第二个窗口的行为
connect(win1,&LWidget::back,[=](){
win1->hide();
this->show();
});
}
C++11 中的Lambda 表达式用于定义并创建匿名的函数对象,以简化编程工作。 首先看一下 Lambda表达式的基本构成:
[函数对象参数](mutable) ->返回值{函数体)
[capture](parameters) mutable ->return-type
{
statement
}
函数对象参数:[],标识一个 Lambda 的开始,这部分必须存在,不能省略。函数对象参数 是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义 Lambda 为止时 Lambda 所在作用范围内可见的局部变量(包括 Lambda 所在类的 this)。
函数对象参数有以下形式:
形式 | 作用 |
---|---|
空 | 没有使用任何函数对象参数 |
= | 函数体内可以使用 Lambda 所在作用范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量) |
& | 函数体内可以使用 Lambda 所在作用范围内所有可见的局部变量(包 括 Lambda 所在类的 this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量) |
this | 函数体内可以使用 Lambda 所在类中的成员变量 |
a | 将 a 按值进行传递。按值进行传递时,函数体内不能修改传递进来的 a 的拷贝,因为默认情况下函数是 const 的。要修改传递进来的 a 的拷 贝,可以添加 mutable 修饰符 |
&a | 将 a 按引用进行传递 |
a,&b | 将 a 按值进行传递,b 按引用进行传递。 |
=,&a,&b | 除 a 和 b 按引用进行传递外,其他参数都按值进行传递。 |
&,a,b | 除 a 和 b 按值进行传递外,其他参数都按引用进行传递。 |
操作符重载函数参数: 标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过 按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递
可修改标示符: mutable 声明,这部分可以省略。按值传递函数对象参数时,加上 mutable 修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)
函数返回值: ->返回值类型,标识函数返回值的类型,当返回值为 void,或者函数体中只 有一处 return 的地方(此时编译器可以自动推断出返回值类型)时,这部 分可以省略
是函数体: {},标识函数的实现,这部分不能省略,但函数体可以为空
QMainWindow 是一个为用户提供主窗口程序的类,包含一个菜单栏(menu bar)、 多个工具栏(tool bars)、多个锚接部件(dock widgets)、一个状态栏(status bar) 及一个中心部件(central widget),是许多应用程序的基础,如文本编辑器,图片编辑器等
Qt 并没有专门的菜单项类,只是使用一个 QAction 类,抽象出公共的动作。 当我们把 QAction 对象添加到菜单,就显示成一个菜单项,添加到工具栏, 就显示成一个工具按钮。用户可以通过点击菜单项、点击工具栏按钮、点击 快捷键来激活这个动作。
创建工程
注意不要选择QWidget,而是QMainWindow
一个主窗口最多只有一个菜单栏。位于主窗口顶部、主窗口标题栏下面。
如果这么写
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
this->setWindowTitle("test");
this->resize(600,400);
// 创建一个菜单栏
QMenuBar *menuBar=new QMenuBar(this);
}
菜单栏会被视为普通控件添加到主窗口的正文部分,可以被move随便移动
所以我们需要将菜单栏单独列出,添加到主窗口中
且由于是主窗口添加,所以在主窗口QMainWindow中查找对应函数
//原函数:
void QMainWindow::setMenuBar(QMenuBar *menuBar)
//添加
// 将菜单栏添加到主窗口
this->setMenuBar(menuBar);
同理,在Qmenu里找对应函数
//原函数:
QAction *addMenu(QMenu *menu)
QMenu *addMenu(const QString &title)
QMenu *addMenu(const QIcon &icon, const QString &title)
// 这里我使用第2个
// 在菜单栏中添加菜单
// 1、定义菜单
QMenu *file = new QMenu("文件",this);
QMenu *edit = new QMenu("编辑",this);
QMenu *tool = new QMenu("工具",this);
QMenu *setting = new QMenu("设置",this);
QMenu *help = new QMenu("帮助",this);
// 2、将定义的菜单加入到菜单栏
menuBar->addMenu(file);
menuBar->addMenu(edit);
menuBar->addMenu(tool);
menuBar->addMenu(setting);
menuBar->addMenu(help);
在Qmenu里找对应函数
QAction *addAction(const QString &text)
QAction *addAction(const QIcon &icon, const QString &text)
QAction *addAction(const QString &text, const QObject *receiver, const char *member, const QKeySequence &shortcut = 0)
QAction *addAction(const QIcon &icon, const QString &text, const QObject *receiver, const char *member, const QKeySequence &shortcut = 0)
QAction *addAction(const QString &text, Functor functor, const QKeySequence &shortcut = ...)
QAction *addAction(const QString &text, const QObject *context, Functor functor, const QKeySequence &shortcut = 0)
QAction *addAction(const QIcon &icon, const QString &text, Functor functor, const QKeySequence &shortcut = ...)
QAction *addAction(const QIcon &icon, const QString &text, const QObject *context, Functor functor, const QKeySequence &shortcut = 0)
例:
// 在菜单里添加菜单项
// 1、定义菜单项
QAction *newfile = new QAction("新建",this);
QAction *save = new QAction("保存",this);
QAction *option = new QAction("选项",this);
QAction *test = new QAction("测试",this);
QAction *dialog = new QAction("对话",this);
// 2、将定义的菜单项添加到菜单
file->addAction(newfile);
edit->addAction(save);
tool->addAction(test);
setting->addAction(option);
help->addAction(dialog);
在QAction里找对应函数
//原函数
void setShortcut(const QKeySequence &shortcut)
void setShortcutContext(Qt::ShortcutContext context)
void setShortcutVisibleInContextMenu(bool show)
void setShortcuts(const QList &shortcuts)
void setShortcuts(QKeySequence::StandardKey key)
我们使用第一个
帮助里的例子:
#include
QKeySequence(QKeySequence::Print);
QKeySequence(tr("Ctrl+P"));
QKeySequence(tr("Ctrl+p"));
QKeySequence(Qt::CTRL + Qt::Key_P);
例:
// 给菜单项里添加快捷键
newfile->setShortcut(QKeySequence(Qt::CTRL+Qt::Key_N));
save->setShortcut(QKeySequence(tr("Ctrl+s")));
在QAction里找对应函数
//原函数:发出信号
void triggered(bool checked = false)
例:
// 给菜单项添加行为
connect(newfile,&QAction::triggered,[=](){
qDebug()<<"新建文件"<
file->addAction(recent);
file->addSeparator(); // 在同一个菜单的相邻菜单项之间添加分隔符
file->addAction(exit);
主窗口的工具栏上可以有多个工具条,通常采用一个菜单对应一个工具条的的方 式,也可根据需要进行工具条的划分
头文件:
#include
**创建工具栏原函数:**在主窗口函数QMainWindow中查找
void addToolBar(Qt::ToolBarArea area, QToolBar *toolbar)
void addToolBar(QToolBar *toolbar)
QToolBar *addToolBar(const QString &title)
例:
// 创建一个工具栏
QToolBar *toolBar = new QToolBar(this);
// 将工具栏添加到主窗口中,并默认出现在左侧
this->addToolBar(Qt::LeftToolBarArea, toolBar);
在QToolBar中查找
原函数:
QAction *addAction(const QString &text)
QAction *addAction(const QIcon &icon, const QString &text)
QAction *addAction(const QString &text, const QObject *receiver, const char *member)
QAction *addAction(const QIcon &icon, const QString &text, const QObject *receiver, const char *member)
QAction *addAction(const QString &text, Functor functor)
QAction *addAction(const QString &text, const QObject *context, Functor functor)
QAction *addAction(const QIcon &icon, const QString &text, Functor functor)
QAction *addAction(const QIcon &icon, const QString &text, const QObject *context, Functor functor)
将上面创建的几个菜单项添加到工具栏中
// 将菜单项添加到工具栏中
toolBar->addAction(newfile);
toolBar->addAction(save);
toolBar->addSeparator();
toolBar->addAction(test);
toolBar->addAction(option);
toolBar->addAction(recent);
toolBar->addAction(exit);
同时,由于我们刚才有设置菜单项的动作,因此点击对应的菜单项,仍可以触发动作
void setFloatable(bool floatable) //true可以浮动,false不可浮动
例:
// 设置工具栏不可浮动
toolBar->setFloatable(false);
包括:
allowAreas | 停靠区域 |
---|---|
Qt::LeftToolBarArea | 停靠在左侧 |
Qt::RightToolBarArea | 停靠在右侧 |
Qt::TopToolBarArea | 停靠在顶部 |
Qt::BottomToolBarArea | 停靠在底部 |
Qt::AllToolBarAreas | 以上四个位置都可以停靠 |
使用 setAllowedAreas()函数指定停靠区域:
原函数:
void setAllowedAreas(Qt::ToolBarAreas areas)
例:
// 设置工具栏停靠的位置
toolBar->setAllowedAreas(Qt::LeftToolBarArea |Qt::RightToolBarArea);
注意:由于默认是停靠上面,因此当我们移动工具栏后,将无法停靠在除设置外的其他位置
使用 setMoveable()函数设定工具栏的可移动性:
setMoveable(false) //工具条不可移动, 只能停靠在初始化的位置上
一般下面的是状态栏
头文件
#include
void setStatusBar(QStatusBar *statusbar)
例:
// 创建一个状态栏
QStatusBar *statusBar = new QStatusBar(this);
// 将状态栏添加到主窗口
this->setStatusBar(statusBar);
头文件
#include
添加左右控件
void addPermanentWidget(QWidget *widget, int stretch = 0) //添加右侧控件
void addWidget(QWidget *widget, int stretch = 0) //添加左侧控件
例:通过控件添加到状态栏:
// 创建2个标签lable
QLabel *label1 = new QLabel("左侧",this);
QLabel *label2 = new QLabel("右侧",this);
// 将标签添加到状态栏
statusBar->addWidget(label1);
statusBar->addPermanentWidget(label2);
//添加小部件
void addWidget(QWidget * widget, int stretch = 0)
//插入小部件
int insertWidget(int index, QWidget * widget, int stretch = 0)
//删除小部件
void removeWidget(QWidget * widget)
中心显示的部件可以作为核心部件,例如一个记事本文件,可以利用 QTextEdit 做核心部件
void setCentralWidget(QWidget *widget)
需要另一个部件
这里使用文本域:QTextEdit
头文件
#include
例:
// 创建一个核心部件
// 创建一个文本域
QTextEdit *text = new QTextEdit(this);
text->setText("如果阳光永远\n都炽热");//设置内容
this->setCentralWidget(text);
铆接部件 QDockWidget,也称浮动窗口,可以有多个,分布在中心内容的四周
头文件
#include
原函数,在QMainWindow里
void addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget)
void addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget, Qt::Orientation orientation)
铆接部件的区域
Qt::LeftDockWidgetArea
Qt::RightDockWidgetArea
Qt::TopDockWidgetArea
Qt::BottomDockWidgetArea
Qt::AllDockWidgetAreas
Qt::NoDockWidgetArea
例:
// 创建一个铆接部件
QDockWidget *DockWidget = new QDockWidget("铆接部件",this);
// 将铆接部件添加到主窗口
this->addDockWidget(Qt::TopDockWidgetArea,DockWidget);
// 设置允许铆接部件停靠的位置 上 下
DockWidget->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
Qt 资源系统是一个跨平台的资源机制,用于将程序运行时所需要的资源以二进 制的形式存储于可执行文件内部。
如果你的程序需要加载特定的资源(图标、文本翻译等),那么,将其放置在资源文件中,就再也不需要担心这些文件的丢失。 也就是说,如果你将资源以资源文件形式存储,它是会编译到可执行文件内部。 使用 Qt Creator 可以很方便地创建资源文件。
我们可以在工程上点右键,选择 “添加新文件…”,可以在 Qt 分类下找到“Qt 资源文件”
将资源文件放入工程目录(非必要)
添加新文件
选择资源文件,一直下一步就行
然后点击添加文件,并选择所有要添加的资源(必须先添加前缀)
最后点击左下角的构建,才算添加成功
如果后续想要继续添加,右击资源并点击open in edit打开刚才的窗口继续编辑
给菜单项添加图标:
void setIcon(const QIcon &icon) //添加图标
void setIconText(const QString &text) //添加图标文本
这里需要使用图片控件
头文件
#include
添加函数load
bool load(const QString &fileName, const char *format = nullptr, Qt::ImageConversionFlags flags = Qt::AutoColor)
例:
//给菜单项添加图标:
// 创建图片控件
QPixmap pix;
// 给图片控件添加资源图片
// :+资源的路径
pix.load(":/logo/qq.png");
// 将控件添加到菜单项
newfile->setIcon(QIcon(pix));
可以辅助用户设计文件
创建工程
在右下角这里
geometry //可以修改窗口尺寸
minimumsize可 //以修改允许缩放到的最小尺寸
windowTitle //可以修改窗口标题
windowIcon //可以修改窗口图标
实现添加资源后
点击添加资源即可选择标题
然后点击某一菜单,点击输入并输入内容,即可添加菜单项
填加分隔符即可添加分割符
由于UI已经给我们生成了各个控件,因此我们不需要再生成控件,只需要对其动作做出设定即可,即设计控件的行为
//通过UI指针成员访问UI文件上的控件,而不需要再new
connect(ui->actionnew,&QAction::triggered,[=]()
{
qDebug()<<"new a file."<
对话框是 GUI 程序中不可或缺的组成部分。很多不能或者不适合放入主窗口的功能组件都必须放在对话框中设置。对话框通常会是一个顶层窗口,出现在程序最上层,用于实现短期任务或者简洁的用户交互。
Qt 中使用 QDialog 类实现对话框。就像主窗口一样,我们通常会设计一个类继 承 QDialog。QDialog(及其子类,以及所有 Qt::Dialog 类型的类)的对于其 parent 指针都有额外的解释:如果 parent 为 NULL,则该对话框会作为一个顶 层窗口,否则则作为其父组件的子对话框(此时,其默认出现的位置是 parent 的中心)。顶层窗口与非顶层窗口的区别在于,顶层窗口在任务栏会有自己的位置,而非顶层窗口则会共享其父组件的位置。
对话框分为模态对话框和非模态对话框。
头文件
#include
所谓标准对话框,是 Qt 内置的一系列对话框,用于简化开发。事实上,有很多对话框都是通用的,比如打开文件、设置颜色、打印设置等。这些对话框在所有程序中几乎相同,因此没有必要在每一个程序中都自己实现这么一个对话框。
Qt 的内置对话框大致分为以下几类:
类型 | 作用 |
---|---|
QColorDialog | 选择颜色 |
QFileDialog | 选择文件或者目录 |
QFontDialog | 选择字体 |
QInputDialog | 允许用户输入一个值,并将其值返回 |
QMessageBox | 模态对话框,用于显示信息、询问问题等 |
QPageSetupDialog | 为打印机提供纸张相关的选项 |
QPrintDialog | 打印机配置 |
QPrintPreviewDialog | 打印预览 |
QProgressDialog | 显示操作过程 |
Qt 支持模态对话框和非模态对话框。
模态与非模态的实现:
Qt 有两种级别的模态对话框:
一般默认是应用程序级别的模态
例:将模态对话框连接菜单项copy
#if n //使对话框运行模式为模态对话框
connect(ui->actioncopy,&QAction::triggered,[=]()
{
dlg->exec();
qDebug()<<"模态对话框"<
show()函数不会阻塞当前线程,对话框会显示出来,然后函数立即返回,代码继续执行。注意,dialog 是建立在栈上的,show()函数返回,MainWindow::open()函数结束,dialog 超出作用域被析构,因此对话框消失了,我们将 dialog 改成堆上建立,就没有这个问题
例:非模态对话框连接菜单项paste
#else //使对话框运行模式为非模态对话框
connect(ui->actionpaste,&QAction::triggered,[=]()
{
dlg->show();
qDebug()<<"非模态对话框"<
QMessageBox 用于显示消息提示。我们一般会使用其提供的几个 static 函数
头文件
#include
void about(QWidget * parent, const QString & title, const QString & text)
这是一个最简单的对话框,其标题是 title,内容是 text,父窗口是 parent。对话框只有一个 OK 按钮。
void aboutQt(QWidget * parent, const QString & title = QString());
StandardButton warning(QWidget * parent, const QString & title, const QString & text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton)
StandardButton critical(QWidget * parent, const QString & title, const QString & text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton);
这个对话框将显示一个红色的错误符号。我们可以通过 buttons 参数指明 其 显 示 的 按 钮 。 默 认 情 况 下 只 有 一 个 Ok 按 钮 , 我 们 可 以 使 用 StandardButtons 类型指定多种按钮。
例:先显示错误对话框,再显示模态对话框
#if n //使对话框运行模式为模态对话框
connect(ui->actioncopy,&QAction::triggered,[=]()
{
QMessageBox::critical(this,"error","there has been a huge error!");
dlg->exec();
qDebug()<<"模态对话框"<
提供一个普通信息图标
StandardButton information(QWidget * parent, const QString & title, const QString & text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton)
例:同时显示非模态对话框和信息对话框
#else //使对话框运行模式为非模态对话框
connect(ui->actionpaste,&QAction::triggered,[=]()
{
dlg->show();
qDebug()<<"非模态对话框"<
提供一个问号 图标,并且其显示的按钮是“是”和“否”。
StandardButton question(QWidget * parent, const QString & title, const QString & text, StandardButtons buttons = StandardButtons( Yes | No ), StandardButton defaultButton = NoButton)
例:
QMessageBox::question(this,"ques","do u want to exit?");
同时,提问对话框的button可以修改:
Constant | Value | Description |
---|---|---|
OMessageBox::Ok | 0x00000400 | An “OK” button defined with the AcceptRole. |
OMessageBox::Open | 0x00002000 | An “Open” button defined with the AcceptRole. |
OMessageBox: :Save | 0x00000800 | An “Save” button defined with the AcceptRole. |
QMessageBox::Cancel | 0x00400000 | A “Cancel” button defined with the RejectRole. |
QMessageBox::Close | 0x00200000 | A "Close button defined with the RejectRole. |
QMessageBox::Discard | 0x00800000 | A Discard" or “Don’t Save” button, depending on the platformdefined with the DestructiveRole. |
OMessageBox: :Apply | 0x02000000 | An “Apply” button defined with the ApplyRole |
OMessageBox: :Reset | 0x04000000 | A “Reset” button defined with the ResetRole. |
QMessageBox::RestoreDefaults | 0x08000000 | A “Restore” Defaults" button defined with the ResetRole. |
OMessageBox::Help | 0x01000000 | A “Help” button defined with the HelpRole. |
QMessageBox: : SaveA11 | 0x00001000 | A “Save All” button defined with the AcceptRole. |
OMessageBox: :Yes | 0x00004000 | A “Yes” button defined with the YesRole. |
QMessageBox::YesToA11 | 0x00008000 | A “Yes to All” button defined with the YesRole. |
OMessageBox::No | 0x00010000 | A “No” button defined with the NoRole. |
QMessageBox::NoToA1] | 0x00020000 | A “No to All” button defined with the NoRole. |
QMessageBox: :Abort | 0x00040000 | An “Abort” button defined with the RejectRole. |
OMessageBox: :Retry | 0x00080000 | A “Retry” button defined with the AcceptRole |
OMessageBox::Ignore | 0x00100000 | An “Ignore” button defined with the AcceptRole. |
OMessageBox: :NoButton | 0x00000000 | An invalid button. |
例:修改button为save和cancel
QMessageBox::question(this,"ques","tdo u want to exit?",QMessageBox::Save | QMessageBox::Cancel);
注意,button默认是左边的,有点蓝色
例:将默认修改为右边的cancel
QMessageBox::question(this,"ques","tdo u want to exit?",QMessageBox::Save | QMessageBox::Cancel,QMessageBox::Cancel);
QMessageBox::StandardButton ret ;
ret = QMessageBox::question(this,"ques","tdo u want to exit?",
QMessageBox::Save | QMessageBox::Cancel,QMessageBox::Cancel);
if (ret == QMessageBox::Save)
qDebug()<<"保存数据!"<
头文件
#include
原函数
//获得字体
QFont getFont(bool *ok, const QFont &initial, QWidget *parent = nullptr, const QString &title = QString(), QFontDialog::FontDialogOptions options = FontDialogOptions())
QFont getFont(bool *ok, QWidget *parent = nullptr)
例:
bool yes;
QFontDialog::getFont(&yes,QFont("宋体"),this);
其他函数:
QString family() const //查看字体类型
int pointSize() const //查看字体大小
例:
bool yes;
QFont font;
font = QFontDialog::getFont(&yes,QFont("宋体"),this);
if(yes)
{
qDebug()<<"字体为:"<
然后点击OK
头文件
#include
QColor getColor(const QColor &initial = Qt::white, QWidget *parent = nullptr, const QString &title = QString(), QColorDialog::ColorDialogOptions options = ColorDialogOptions())
例:显示颜色对话框
QColorDialog::getColor();
QColor color;
color = QColorDialog::getColor();
qDebug()<<"red:"<
或
QFileDialog,也就是文件对话框。在本节中,我们将尝试编写一个简单的文本文件编辑器,我们将使用 QFileDialog 来打开一个文本文件,并将修改过的文件保存到硬盘。
头文件
#include
QString getOpenFileName(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = nullptr, QFileDialog::Options options = Options())
QStringList getOpenFileNames(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const Q String &filter = QString(), QString *selectedFilter = nullptr, QFileDialog::Options options = Options())
不过注意,它的所有参数都是可选的,因此在一定程度上说,这个函数也是简单的。这六个参数分别是:
QFileDialog::getOpenFileName()返回值是选择的文件路径。我们将其赋值给filename。通过判断 filename 是否为空,可以确定用户是否选择了某一文件。只有当用户选择了一个文件时,我们才执行下面的操作
在 saveFile()中使用的 QFileDialog::getSaveFileName()也是类似的。使用这 种静态函数,在 Windows、Mac OS 上面都是直接调用本地对话框,但是 Linux 上 则是 QFileDialog 自己的模拟。这暗示了,如果你不使用这些静态函数,而是直 接使用 QFileDialog 进行设置,那么得到的对话框很可能与系统对话框的外观不 一致。这一点是需要注意的
例:随便打开个文件
QString filename;
filename = QFileDialog::getOpenFileName();
qDebug()<
成功显示
QString filename;
filename = QFileDialog::getOpenFileName(this,"file","C:\\Users\\HUIO\\Desktop\\Me\\QT\\00\\03\\logo");
//注意:要用双斜线\\
qDebug()<
则会自动打开该路径
QString filename;
filename = QFileDialog::getOpenFileName(this,"file",
"C:\\Users\\HUIO\\Desktop\\Me\\QT\\00\\03\\logo","(*.txt)");
qDebug()<
QString filename;
filename = QFileDialog::getOpenFileName(this,"file",
"C:\\Users\\HUIO\\Desktop\\Me\\QT\\00\\03\\logo","(*.txt *.png)");
qDebug()<
同样的,双击可以添加内容
头文件
#include
添加项目函数
void addItem(const QString &text, const QVariant &userData = QVariant())
void addItem(const QIcon &icon, const QString &text, const QVariant &userData = QVariant())
void addItems(const QStringList &texts) //链表,只能是string
这里我使用第三个
//给下拉列表框添加选项
QStringList list;
list<<"梅赛德斯"<<"帕拉梅拉"<<"科尼赛克";
ui->comboBox->addItems(list);
函数
void setCurrentIndex(int index)
ui->comboBox->setCurrentIndex(2);//设置默认索引
信号
void currentIndexChanged(const QString &text)
void currentIndexChanged(int index)
//重载
void currentTextChanged(const QString &text)
例:
//添加行为
void ( QComboBox::*p )(int) = &QComboBox::currentIndexChanged;
// 返回值 作用域 希望他的类型
connect(ui->comboBox,p,[=](int index)
{
qDebug()<comboBox->currentIndex()<comboBox->currentText()<
注意:信号发生重载,要用函数指针匹配
所谓 GUI 界面,归根结底,就是一堆组件的叠加。我们创建一个窗口,把按钮 放上面,把图标放上面,这样就成了一个界面。在放置时,组件的位置尤其重要。 我们必须要指定组件放在哪里,以便窗口能够按照我们需要的方式进行渲染。这就涉及到组件定位的机制。
Qt 提供了两种组件定位机制:绝对定位和布局定位。
Qt 提供的布局中以下三种是我们最常用的:
分别是垂直布局、水平布局、栅格布局,表单布局
同时这里有水平布局、垂直布局和栅格布局
这 4 个为系统给我们提供的布局的控件,但是使用起来不是非常的灵活,这里不再详细介绍。
接下来设计一个登录窗口
直接拖动过去即可,并输入内容
运行
比较丑,接下来优化
将水平弹簧分别放在用户名、密码两侧,以及下面的三处
然后选中一整行,点击上面的水平布局将其重新划分,下面同理
然后随便点击空白处,再点击上面的垂直布局
重新分布后,运行
随着我们拖动边缘,它的布局仍然规整
点击某一弹簧后,可以设置其宽高
如果要解除布局,可以点击空白处或某一控件,再点击上面的打破布局
接下来,双击输入框,并修改名字,以方便我们识别与使用
同理,我们把登录和注册账号按钮也制定一下
完成后运行,我们输入用户名和密码,会发现密码可以被看到
因此需要修改密码输入框的模式
双击密码框,右下角在echoMode里选择password
获取user和passwd
点击登录,获取用户名和密码
QString text() const
因此我们需要QString类型的字段接收
//点击登录,获取用户名和密码
connect(ui->pushButton_login,&QPushButton::clicked,[=](){
QString username = ui->lineEdit_user->text(); // 获取用户名
QString passwd = ui->lineEdit_passwd->text(); // 获取密码
qDebug()<<"用户名为:"<
拖动Radio Button按钮,并输入内容,创建4个按钮
一次性只能从4个选择一个
接下来我们使用Group Box组,拖动出2个组,并将2组年龄和性别加入(注意:button需要先放在外面,否则会压住)
此时可以选择2组
双击修改一下各个组件的名字,方便后面识别与使用
选中male则输出男
//选中则输出对应的内容
connect(ui->radioButton_male,&QRadioButton::clicked,[=]()
{
qDebug()<<"男"<
打开程序时默认的选择
void setChecked(bool)
//默认选择
ui->radioButton_female->setChecked(true);
Qt 为我们应用程序界面开发提供的一系列的控件,所有控件的使用方法我们都可以通过帮助文档获取
通过 QLabel 类的 setText 函数设置显示的内容:
void setText(const QString &)
可以显示普通文本字符串
QLable *label = new QLable;
label->setText(“Hello, World!”);
可以显示 HTML 格式的字符串 比如显示一个链接:
QLabel * label = new QLabel(this);
label ->setText("Hello, World");
label ->setText("百度一下
");
label ->setOpenExternalLinks(true);
其中 setOpenExternalLinks()函数是用来设置用户点击链接之后是否自动 打开链接,如果参数指定为 true 则会自动打开
例:
QString text() const //获取文本函数
//添加文本
ui->label->setText("babe hello");
qDebug()<label->text()<
可以使用 QLabel 的成员函数 setPixmap 设置图片
头文件
#include
void setPixmap(const QPixmap &)
首先添加资源,然后拖动一个新的label_2进入画布
定义 QPixmap 对象
QPixmap pixmap;
bool load(const QString &fileName, const char *format = nullptr, Qt::ImageConversionFlags flags = Qt::AutoColor)
pixmap.load(":/logo/anime.png");
void setPixmap(const QPixmap &)
ui->label_2->setPixmap(pixmap);
头文件
#include
QMovie *movie = new QMovie(":/game.gif");
void setMovie(QMovie * movie)
例:
ui->label->setMovie(movie);
signal:
bool jumpToNextFrame() //跳到下一个动画
void setPaused(bool paused) //暂停
void setSpeed(int percentSpeed) //设置速度
void start() //开始
void stop() //停止
例:
//开始播放
connect(ui->pushButton,&QPushButton::clicked,[=]()
{
movie->start();
});
//停止播放
connect(ui->pushButton_2,&QPushButton::clicked,[=]()
{
movie->stop();
});
//开始播放
connect(ui->pushButton_3,&QPushButton::clicked,[=]()
{
movie->setPaused(1);
});
Qt 提供的单行文本编辑框。
设置/获取内容
QString text() const
void setText(const QString &)
设置显示模式
void setEchoMode(EchoMode mode)
EchoMode 是一个枚举类型,一共定义了四种显示模式:
另外,我们再使用 QLineEdit 显示文本的时候,希望在左侧留出一段空白的区域, 那么,就可以使用 QLineEdit 给我们提供的 setTextMargins 函数:
void setTextMargins(int left, int top, int right, int bottom)
用此函数可以指定显示的文本与输入框上下左右边界的间隔的像素数。
这里接着容器9.2.2的部分
注意不要选错
头文件
#include
#include
拖动进主窗口(注意:拖进去了后面才有listwidget,否则没有)
添加项目函数
void addItem(const QString &label)
void addItem(QListWidgetItem *item)
void addItems(const QStringList &labels)
例:使用第二个函数
//添加列表控件:方法2
QListWidgetItem *item = new QListWidgetItem("多远都要在一起");
ui->listWidget->addItem(item);
例:使用第三个函数:链表list
//添加列表控件:方法3
QStringList list; // 使用链表
list<<"就让我独自守着回忆"<<"如果阳光永远都炽热"<<"如果彩虹不会掉颜色";
ui->listWidget->addItems(list);
扩展:点击项目输出内容
这里使用
void itemClicked(QListWidgetItem *item)
例:参数保持一致
//点击项目输出内容
connect(ui->listWidget,&QListWidget::itemClicked,[=](QListWidgetItem *item)
{
qDebug()<text()<
双击树控件最上面
双击列以修改内容,下面±可以增加或减少列
点击项目,可以双击列下面的位置,以输入内容
紫色:可以设置子项目
运行
这种方法好处是操作方便简单,缺点是运行起来后,无法再修改
因此需要使用代码添加
创建新项目,先把树控件拖入主窗口
头文件
#include
添加头信息函数
void setHeaderItem(QTreeWidgetItem *item)
void setHeaderLabel(const QString &label) //只能1个
void setHeaderLabels(const QStringList &labels) // 多个
这里使用第三个
//设置树控件头信息
//创建链表以存入各个头信息
QStringList list;
list<<"语言"<<"介绍";
ui->treeWidget->setHeaderLabels(list);
接下来添加顶层控件
原函数
void addTopLevelItem(QTreeWidgetItem *item) //单个
void addTopLevelItems(const QList &items) //多个
由于我们等下会对顶层控件操作,因此使用第一个逐个添加
而他需要QTreeWidgetItem类型的参数
QTreeWidgetItem(const QTreeWidgetItem &other)
QTreeWidgetItem(QTreeWidgetItem *parent, QTreeWidgetItem *preceding, int type = Type)
QTreeWidgetItem(QTreeWidgetItem *parent, const QStringList &strings, int type = Type)
QTreeWidgetItem(QTreeWidgetItem *parent, int type = Type)
QTreeWidgetItem(QTreeWidget *parent, QTreeWidgetItem *preceding, int type = Type)
QTreeWidgetItem(QTreeWidget *parent, const QStringList &strings, int type = Type)
QTreeWidgetItem(QTreeWidget *parent, int type = Type)
QTreeWidgetItem(const QStringList &strings, int type = Type)
QTreeWidgetItem(int type = Type)
我们使用匿名函数对象,注意只能写一个内容
//添加顶层控件
QTreeWidgetItem *item1 = new QTreeWidgetItem(QStringList()<<"Chinese");
ui->treeWidget->addTopLevelItem(item1);
QTreeWidgetItem *item2 = new QTreeWidgetItem(QStringList()<<"mandarin"<<"普通话");
ui->treeWidget->addTopLevelItem(item1);
QTreeWidgetItem *item3 = new QTreeWidgetItem(QStringList()<<"English");
ui->treeWidget->addTopLevelItem(item1);
接下来添加顶层控件的子控件
来自于QTreeWidgetItem
void addChild(QTreeWidgetItem *child)
void addChildren(const QList &children)
//添加子控件
//方法1:先创建再传入
QTreeWidgetItem *child1 = new QTreeWidgetItem(QStringList()<<"简体"<<"中文(简体)");
item1->addChild(child1);
//方法2:创建时直接作为参数传入
item1->addChild(new QTreeWidgetItem(QStringList()<<"繁体"<<"中文(繁体)"));
item3->addChild(new QTreeWidgetItem(QStringList()<<"英式"<<"英语(英式)"));
item3->addChild(new QTreeWidgetItem(QStringList()<<"美式"<<"英语(美式)"));
当点击某个项目时,我们为其添加输出内容的行为
信号
void itemClicked(QTreeWidgetItem *item, int column) //单击信号
void itemDoubleClicked(QTreeWidgetItem *item, int column) //双击信号
注意:这里参数column相当于一个数组,0代表第0列子空控件,1代表第1列的子控件
输出内容
QString text(int column) const
//添加行为
connect(ui->treeWidget,&QTreeWidget::itemClicked,[](QTreeWidgetItem *item, int column)
{
qDebug()<text(column)<
去掉双引号"",以utf8格式输出
//添加行为
connect(ui->treeWidget,&QTreeWidget::itemClicked,[](QTreeWidgetItem *item, int column)
{
qDebug()<text(column).toUtf8().data()<
拖入表格控件table widget
也可以双击空白处添加内容
运行
紫色:项目
运行
重新创建工程,拖出一个表格控件
头文件
#include
设置列/行数函数
void setColumnCount(int columns)
void setRowCount(int rows)
//设置3列
ui->tableWidget->setColumnCount(3);
//设置4行
ui->tableWidget->setRowCount(4);
设置水平/垂直表头信息函数
void setHorizontalHeaderItem(int column, QTableWidgetItem *item)
void setHorizontalHeaderLabels(const QStringList &labels)
void setVerticalHeaderItem(int row, QTableWidgetItem *item)
void setVerticalHeaderLabels(const QStringList &labels)
例:
//添加表头信息
ui->tableWidget->setHorizontalHeaderLabels(QStringList()<<"国家"<<"语言"<<"介绍");
为表格添加项目
函数
void setItem(int row, int column, QTableWidgetItem *item)
//行 列 item
首先,向表中添加控件有2中方式:
即一列一列添加,创建一列的数据,再循环将这一列的0行数据,1行数据,2行数据…添加进去
例:
//向表中添加控件
//创建一列控件
QStringList country;
country<<"China"<<"USA"<<"Japan"<<"UK";
QStringList language;
language<<"Chinese"<<"English"<<"Japanese"<<"English";
QStringList introduce;
introduce<<"good"<<"nice"<<"shit"<<"nice";
//将控件按列添加进去
for(int i=0;i<4;i++)
{
ui->tableWidget->setItem(i,0,new QTableWidgetItem(country[i]));
ui->tableWidget->setItem(i,1,new QTableWidgetItem(language[i]));
ui->tableWidget->setItem(i,2,new QTableWidgetItem(introduce[i]));
}
即一行数据一行数据添加,原理相同,不再演示
点击表格中的某个数据,将其内容输出,来自于QTableWidget
发出信号函数
void cellActivated(int row, int column)
void cellChanged(int row, int column)
void cellClicked(int row, int column)
void cellDoubleClicked(int row, int column)
void cellEntered(int row, int column)
void cellPressed(int row, int column)
void currentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn)
void currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous)
void itemActivated(QTableWidgetItem *item)
void itemChanged(QTableWidgetItem *item)
void itemClicked(QTableWidgetItem *item)
void itemDoubleClicked(QTableWidgetItem *item)
void itemEntered(QTableWidgetItem *item)
void itemPressed(QTableWidgetItem *item)
void itemSelectionChanged()
1、这里我们使用单击信号itemClicked
例:
//添加动作
connect(ui->tableWidget,&QTableWidget::itemClicked,[=](QTableWidgetItem *item)
{
qDebug()<text()<
2、使用cellClicked信号以返回行列
connect(ui->tableWidget,&QTableWidget::cellClicked,[=](int row,int col)
{
qDebug()<<"row:"<|
Qt 中控件的使用方法可参考 Qt 提供的帮助文档
在搭建 Qt 窗口界面的时候,在一个项目中很多窗口,或者是窗口中的某个模块会被经常性的重复使用。一般遇到这种情况我们都会将这个窗口或者模块拿出来做成一个独立的窗口类,以备以后重复使用。
双击进入我们创建的ui文件,添加常用控件
这里我拖入spin box和水平进度条控件
然后到其他ui里,准备使用我们的自定义控件:
在widget里存放控件
右击widget控件,选择提升
此时它属于我们的自定义控件类
1、设置spinbox改变,导致slider滑动
spinbox:
头文件
#include
发出信号
void textChanged(const QString &text)
void valueChanged(int i)
void valueChanged(const QString &text) // 重载,需要用函数指针匹配
slider:
头文件
#include
父的槽函数
void setValue(int)
在构造函数实现:且ui属于mywidget,所以要在mywidget.cpp中实现
//重载valueChanged
void (QSpinBox:: *p)(int) = &QSpinBox::valueChanged;
//设置spinbox改变,导致slider滑动
connect(ui->spinBox,p,ui->horizontalSlider,&QSlider::setValue);
2、设置滑动slider,导致spinbox改变
spinbox:
槽函数
void setValue(int val)
slider:
发出信号
void valueChanged(int value)
//设置滑动slider,导致spinbox改变
connect(ui->horizontalSlider,&QSlider::valueChanged,ui->spinBox,&QSpinBox::setValue);
在主窗口中设置2个按钮:setHalf、getValue
不过,由于这两个ui分别是不同的文件,无法直接调用
因此需要在自定义控件里设置对外接口
public:
explicit MyWidget(QWidget *parent = nullptr);
~MyWidget();
//提供setHalf接口
void mysetHalf(int value);
//提供getValue接口
int mygetValue(void);
void MyWidget::mysetHalf(int value)
{
ui->horizontalSlider->setValue(value);
}
int MyWidget::mygetValue()
{
return ui->horizontalSlider->value();
}
//调用2个接口
connect(ui->button_setHalf,&QPushButton::clicked,[=]()
{
ui->widget->mysetHalf(50);
});
connect(ui->button_getValue,&QPushButton::clicked,[=]()
{
qDebug()<widget->mygetValue()<
事件(event)是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、 敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发 出,如计时器事件。
在前面我们也曾经简单提到,Qt 程序需要在 main()函数创建一个 QApplication 对象,然后调用它的 exec()函数。这个函数就是开始 Qt 的主事件循环。在执行 exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时, Qt 将创建一个事件对象。Qt 中所有事件类都继承于 QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给 QObject 的 event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler),关于这一点,会在后边详细说明。
在所有组件的父类 QWidget 中,定义了很多事件处理的回调函数,如
virtual void keyPressEvent(QKeyEvent *event)
virtual void keyReleaseEvent(QKeyEvent *event)
virtual void leaveEvent(QEvent *event) //鼠标离开事件
virtual void mouseDoubleClickEvent(QMouseEvent *event)
virtual void mouseMoveEvent(QMouseEvent *event)
virtual void mousePressEvent(QMouseEvent *event)
virtual void mouseReleaseEvent(QMouseEvent *event)
virtual void enterEvent(QEvent *event) //鼠标进入事件
这些函数都是 virtual 的,所以我们必须在子类中重新实现这些函数。
例:重写label控件的事件,如上面的几个事件
注意:label属于系统QLbel类,无法修改;因此如果重写QLabel事件,需要在自定义label控件中实现事件的重写,即自定义一个类,令其继承于该类
#include
class mylabel : public QLabel
{
Q_OBJECT
public:
explicit mylabel(QWidget *parent = nullptr);
signals:
};
这时,我们制定的类mylabel继承于QLabel,这样我们才可以在mylabel中重写QLabel的事件
mylabel::mylabel(QWidget *parent) : QLabel(parent)
{
}
因此需要将label的类型提升成mylabel,这样label的控件才能使用mylabel重写的事件
添加内容后,添加并提升即可
总结:如果想重写某个类的事件,一般情况下需要自定义一个类,继承于该控件的类型,然后将控件的类提升成自定义的类,这样我们就可以在自定义类中重写控件类型事件函数
QLabel的一些事件
virtual void keyPressEvent(QKeyEvent *ev) override //键盘事件
virtual void mouseMoveEvent(QMouseEvent *ev) override //鼠标移动事件
virtual void mousePressEvent(QMouseEvent *ev) override//鼠标按压事件
virtual void mouseReleaseEvent(QMouseEvent *ev) override //鼠标释放事件
virtual void paintEvent(QPaintEvent *) override //绘画事件
QWidget的一些事件
virtual void leaveEvent(QEvent *event) //鼠标离开事件
virtual void enterEvent(QEvent *event) //鼠标进入事件
virtual void leaveEvent(QEvent *event); //鼠标离开事件
virtual void enterEvent(QEvent *event); //鼠标进入事件
void mylabel::leaveEvent(QEvent *event)
{
qDebug()<<"mouseLeave"<
virtual void mouseMoveEvent(QMouseEvent *ev) override //鼠标移动事件
virtual void mousePressEvent(QMouseEvent *ev) override//鼠标按压事件
virtual void mouseMoveEvent(QMouseEvent *ev) override; //鼠标移动事件
virtual void mousePressEvent(QMouseEvent *ev) override;//鼠标按压事件
void mylabel::mouseMoveEvent(QMouseEvent *ev)
{
qDebug()<<"鼠标移动"<
查看QMouseEvent类
头文件
#include
公共函数
//判断是鼠标哪个按键
Qt::MouseButton button() const
Qt::MouseButtons buttons() const
而Qt::MouseButton里
例:
void mylabel::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
qDebug()<<"左键按下"<button() == Qt::RightButton)
qDebug()<<"右键按下"<
//全局坐标
int globalX() const
int globalY() const
//局部坐标
int x() const
int y() const
例:
void mylabel::mouseMoveEvent(QMouseEvent *ev)
{
//默认情况鼠标按下才可移动
qDebug()<<"鼠标移动 x="<x()<<"y="<y()<button() == Qt::LeftButton)
qDebug()<<"左键按下 x="<x()<<"y="<y()<button() == Qt::RightButton)
qDebug()<<"右键按下 x="<x()<<"y="<y()<
不过默认情况下,只有按下才会触发移动函数
设置鼠标跟踪函数
void setMouseTracking(bool enable)
mylabel::mylabel(QWidget *parent) : QLabel(parent)
{
this->setMouseTracking(true);
}
此时不需要按下鼠标就能显示坐标
事件对象创建完毕后,Qt 将这个事件对象传递给 QObject 的 event()函数。 event()函数并不直接处理事件,而是将这些事件对象按照它们不同的类型,分发给不同的事件处理器(event handler)。
如上所述,event()函数主要用于事件的分发
因此,如果希望在事件分发之前做一些操作,可以重写这个 event()函数,这个函数有一个 QEvent 对象作为参数,也就是需要转发的事件对象。函数返回值是 bool 类型
如果传入的事件已被识别并且处理,则需要返回 true,否则返回 false。 如果返回值是 true,那么 Qt 会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。
在 event()函数中,调用事件对象的 accept()和 ignore()函数是没有作用的,不会影响到事件的传播。
我们处理过自己感兴趣的事件之后,可以直接返回 true,表示我们已经对此事件进行了处理;对于其它我们不关心的事件,则需要调用父类的event()函数继续转发,否则这个组件就只能处理我们定义的事件
//QLabel:
virtual bool event(QEvent *e) override
//QEvent
Public Types
enum Type { None, ActionAdded, ActionChanged, ActionRemoved, ActivationChange, …, MaxUser }
点击
例:在事件分发器中处理鼠标按压事件
void mylabel::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
qDebug()<<"mousePressEvent左键按下 x="<x()<<"y="<y()<button() == Qt::RightButton)
qDebug()<<"mousePressEvent右键按下 x="<x()<<"y="<y()<
virtual bool event(QEvent *e) override;
bool mylabel::event(QEvent *e)
{
//只关心mouseButtonPress事件
if(e->type() == QEvent::MouseButtonPress)
{
qDebug()<<"事件分发器鼠标按下"<
注意,Event中没有对应的函数,在QMouseEvent中有,因此我们在分发器运行后将其类型转化为QMouseEvent
if(e->type() == QEvent::MouseButtonPress)
{
qDebug()<<"事件分发器鼠标按下"<(e);
qDebug()<<"x = "<x()<<"y = "<y()<
有时候,对象需要查看、甚至要拦截发送到另外对象的事件。例如,对话框可能想要拦截按键事件,不让别的组件接收到;或者要修改回车键的默认处理。
通过前面的章节,我们已经知道,Qt 创建了 QEvent 事件对象之后,会调用 QObject 的 event()函数处理事件的分发。显然,我们可以在 event()函数中实现拦截的操作。由于 event()函数是 protected 的,因此,需要继承已有类。 如果组件很多,就需要重写很多个 event()函数。这当然相当麻烦,更不用说重 写 event()函数还得小心一堆问题。好在 Qt 提供了另外一种机制来达到这一目 的:事件过滤器。
步骤:
QObject 有一个 eventFilter()函数,用于建立事件过滤器。函数原型如下:
Public Functions:
virtual bool eventFilter(QObject *watched, QEvent *event) //重写事件过滤器
// 事件触发的控件 控件产生的具体事件(鼠标按下、移动等)
void installEventFilter(QObject *filterObj) //加载事件过滤器
这个函数正如其名字显示的那样,是一个“事件过滤器”。所谓事件过滤器,可以理解成一种过滤代码。事件过滤器会检查接收到的事件。如果这个事件是我们感兴趣的类型,就进行我们自己的处理;如果不是,就继续转发。这个函数返回一个 bool 类型,如果将参数 event 过滤出来,不想让它继续转发, 就返回 true,否则返回 false。事件过滤器的调用时间是目标对象(也就是参数里面的 watched 对象)接收到事件对象之前。也就是说,如果你在事件过滤器中停止了某个事件,那么,watched 对象以及以后所有的事件过滤器根本不会知道这么一个事件。
例:
mylabel::mylabel(QWidget *parent) : QLabel(parent)
{
this->setMouseTracking(true);//鼠标跟踪
//加载事件过滤器
this->installEventFilter(this);
}
//声明事件过滤器
virtual bool eventFilter(QObject *watched, QEvent *event);
//重写事件过滤器
bool mylabel::eventFilter(QObject *watched, QEvent *event)
{
if(watched == this)
{
if(event->type() == QEvent::MouseButtonPress)
{
qDebug()<<"事件过滤器鼠标按下"<(event);
qDebug()<<"x = "<x()<<"y = "<y()<
注意:
事件过滤器的强大之处在于,我们可以为整个应用程序添加一个事件过滤器。 installEventFilter() 函 数 是 QObject 的 函 数 , QApplication 或 者 QCoreApplication 对象都是 QObject 的子类,因此,我们可以向 QApplication 或者 QCoreApplication 添加事件过滤器。
Qt 的事件是整个 Qt 框架的核心机制之一,也比较复杂。说它复杂,更多是因为它涉及到的函数众多,而处理方法也很多,有时候让人难以选择。现在我们简单总结一下 Qt 中的事件机制。
Qt 中有很多种事件:鼠标事件、键盘事件、大小改变的事件、位置移动的事件 等等。如何处理这些事件,实际有两种选择:
LRESULT CALLBACK WndProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
在这个函数中,我们需要使用 switch 语句,选择 message 参数的类型进行处理,典型代码是:
switch(message)
{
case WM_PAINT:
// ...
break;
case WM_DESTROY:
// ...
break;
...
}
每一种事件对应一个事件处理函数。Qt 就是使用的这么一种机制:
mouseEvent()
keyPressEvent()
…
Qt 具有这么多种事件处理函数,肯定有一个地方对其进行分发,否则,Qt 怎么知道哪一种事件调用哪一个事件处理函数呢?这个分发的函数,就是 event(),显然,当 QMouseEvent 产生之后,event()函数将其分发给 mouseEvent()事件处理器进行处理。
而event()函数会有两个问题:
这两个问题是 event()函数无法处理的。于是,Qt 提供了另外一种解决方案: 事件过滤器。
事件过滤器给我们一种能力,让我们能够完全移除某种事件。事件过滤器可以安装到任意 QObject 类型上面,并且可以安装多个。如果要实现全局的事件过滤器,则可以安装到 QApplication 或者 QCoreApplication 上面。这里需要注意的是,如果使用 installEventFilter()函数给一个对象安装事件过滤器,那么该事件过滤器只对该对象有效,只有这个对象的事件需要先传递给事件过滤器的 eventFilter() 函数进行过滤, 其它对象不受影响。如果给QApplication 对象安装事件过滤器,那么该过滤器对程序中的每一个对象都有效,任何对象的事件都是先传给 eventFilter()函数。
事件过滤器可以解决刚刚我们提出的 event()函数的两点不足:
事实上,还有一种方法,我们没有介绍。Qt 事件的调用最终都会追溯到QCoreApplication::notify() 函 数 ,因此,最大的控制权实际上是重写QCoreApplication::notify()。这个函数的声明是:
virtual bool QCoreApplication::notify ( QObject * receiver, QEvent * event );
该函数会将 event 发送给 receiver,也就是调用 receiver->event(event),其返回值就是来自 receiver 的事件处理器。注意,这个函数为任意线程的任意对象的任意事件调用,因此,它不存在事件过滤器的线程的问题。不过我们并不推荐这么做,因为notify()函数只有一个,而事件过滤器要灵活得多。
现在我们可以总结一下 Qt 的事件处理,实际上是有五个层次:
定时器事件函数
QTimer Class:Reimplemented Protected Functions
virtual void timerEvent(QTimerEvent *e) override
启动计时器函数
QObject:Public Functions
int startTimer(int interval, Qt::TimerType timerType = Qt::CoarseTimer)
int startTimer(std::chrono::milliseconds time, Qt::TimerType timerType = Qt::CoarseTimer)
注意:这里返回值是int类型,是定时器的ID,即一个进程可以有多个计时器
创建工程
先在widget.h中声明定时器事件
//声明定时器事件
virtual void timerEvent(QTimerEvent *e) override;
//重写定时器事件
void Widget::timerEvent(QTimerEvent *e)
{
static int time =0;
ui->label->setText(QString::number(time++));
}
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//启动定时器事件
this->startTimer(1000); //1000ms
}
返回定时器ID函数
QTimerEvent Class:Public Functions
int QTimerEvent::timerId() const
private:
Ui::Widget *ui;
//定时器id
int id1;
int id2;
//声明定时器事件
virtual void timerEvent(QTimerEvent *e) override;
在widget.c中重写
先开启2个定时器
//开始2个定时器
this->id1 = this->startTimer(1000);
this->id2 = this->startTimer(2000);
//实现定时器事件
void Widget::timerEvent(QTimerEvent *e)
{
static int num1 = 0 ;
static int num2 = 0;
//使用timerID函数
if(e->timerId() == id1)
ui->label->setText(QString::number(num1++));
else if (e->timerId() == id2)
ui->label_2->setText(QString::number(num2++));
}
头文件
#include
QTimer Class:Public Slots
void start()
void start(int msec)
void stop()
Signals
void timeout()
// 计时器对象创建
QTimer *time = new QTimer(this);
// 给定时器添加行为:超时记录时间
connect(time,&QTimer::timeout,[=]()
{
static int num =0 ;
ui->label_3->setText(QString::number(num++));
});
//启动定时器
connect(ui->button_start,&QPushButton::clicked,[=]()
{
time->start(1000);
});
//暂停定时器
connect(ui->button_stop,&QPushButton::clicked,[=]()
{
time->stop();
});
通过singleShot实现延时功能,做完即结束,而非重复循环某事件
先向UI中再拖入一个label_4
在构造函数中实现
由于是静态成员函数,所以可以通过类名称访问
//静态成员函数触发
QTimer::singleShot(3000,[=](){
ui->label_4->setText("Hello,my pricess.");
});
Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制。整个绘图系统基于 QPainter,QPainterDevice 和 QPaintEngine 三个类。
QPaintEngine 类应用于 QPainter 和 QPaintDevice 之间,通常对开发人员是透明的。除非你需要自定义一个设备,否则你是不需要关心 QPaintEngine 这个类的。我们可以把 QPainter 理解成画笔;把 QPaintDevice 理解成使用画笔的地方, 比如纸张、屏幕等;而对于纸张、屏幕而言,肯定要使用不同的画笔绘制,为了统一使用一种画笔,我们设计了 QPaintEngine 类,这个类让不同的纸张、屏幕都能使用一种画笔
下图给出了这三个类之间的层次结构:
即使用 QPainter 在 QPainterDevice 上进行绘制,它们之间使用 QPaintEngine 进行通讯(也就是翻译 QPainter 的指令)。
注意:如果在主窗口上绘画,必须在绘画事件(paintEvent)中完成绘图
[virtual protected] void QWidget::paintEvent(QPaintEvent *event)
头文件
#include
#include
绘画事件调用的时机:
绘图函数:
QPainter Class:Public Functions
// Arc弧
void drawArc(const QRectF &rectangle, int startAngle, int spanAngle)
void drawArc(const QRect &rectangle, int startAngle, int spanAngle)
void drawArc(int x, int y, int width, int height, int startAngle, int spanAngle)
//chord弦
void drawChord(const QRectF &rectangle, int startAngle, int spanAngle)
void drawChord(int x, int y, int width, int height, int startAngle, int spanAngle)
void drawChord(const QRect &rectangle, int startAngle, int spanAngle)
//ConvexPolygon凸多边形
void drawConvexPolygon(const QPointF *points, int pointCount)
void drawConvexPolygon(const QPolygonF &polygon)
void drawConvexPolygon(const QPoint *points, int pointCount)
void drawConvexPolygon(const QPolygon &polygon)
//Ellipse椭圆
void drawEllipse(const QRectF &rectangle)
void drawEllipse(const QRect &rectangle)
void drawEllipse(int x, int y, int width, int height)
void drawEllipse(const QPointF ¢er, qreal rx, qreal ry)
void drawEllipse(const QPoint ¢er, int rx, int ry)
//GlyphRun字形运行
void drawGlyphRun(const QPointF &position, const QGlyphRun &glyphs)
//图像
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, Qt::ImageConversionFlags flags = Qt::AutoColor)
void drawImage(const QRect &target, const QImage &image, const QRect &source, Qt::ImageConversionFlags flags = Qt::AutoColor)
void drawImage(const QPointF &point, const QImage &image, const QRectF &source, Qt::ImageConversionFlags flags = ...)
void drawImage(const QPoint &point, const QImage &image, const QRect &source, Qt::ImageConversionFlags flags = ...)
void drawImage(const QRectF &rectangle, const QImage &image)
void drawImage(const QRect &rectangle, const QImage &image)
void drawImage(const QPointF &point, const QImage &image)
void drawImage(const QPoint &point, const QImage &image)
void drawImage(int x, int y, const QImage &image, int sx = 0, int sy = 0, int sw = -1, int sh = -1, Qt::ImageConversionFlags flags = Qt::AutoColor)
//单条线
void drawLine(const QLineF &line)
void drawLine(const QLine &line)
void drawLine(int x1, int y1, int x2, int y2)
void drawLine(const QPoint &p1, const QPoint &p2)
void drawLine(const QPointF &p1, const QPointF &p2)
//线
void drawLines(const QLineF *lines, int lineCount)
void drawLines(const QVector &lines)
void drawLines(const QPointF *pointPairs, int lineCount)
void drawLines(const QVector &pointPairs)
void drawLines(const QLine *lines, int lineCount)
void drawLines(const QVector &lines)
void drawLines(const QPoint *pointPairs, int lineCount)
void drawLines(const QVector &pointPairs)
//路径
void drawPath(const QPainterPath &path)
//图片
void drawPicture(const QPointF &point, const QPicture &picture)
void drawPicture(int x, int y, const QPicture &picture)
void drawPicture(const QPoint &point, const QPicture &picture)
//饼图,圆
void drawPie(const QRectF &rectangle, int startAngle, int spanAngle)
void drawPie(int x, int y, int width, int height, int startAngle, int spanAngle)
void drawPie(const QRect &rectangle, int startAngle, int spanAngle)
//像素图
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
void drawPixmap(const QRect &target, const QPixmap &pixmap, const QRect &source)
void drawPixmap(int x, int y, int w, int h, const QPixmap &pixmap, int sx, int sy, int sw, int sh)
void drawPixmap(int x, int y, const QPixmap &pixmap, int sx, int sy, int sw, int sh)
void drawPixmap(const QPointF &point, const QPixmap &pixmap, const QRectF &source)
void drawPixmap(const QPoint &point, const QPixmap &pixmap, const QRect &source)
void drawPixmap(const QPointF &point, const QPixmap &pixmap)
void drawPixmap(const QPoint &point, const QPixmap &pixmap)
void drawPixmap(int x, int y, const QPixmap &pixmap)
void drawPixmap(const QRect &rectangle, const QPixmap &pixmap)
void drawPixmap(int x, int y, int width, int height, const QPixmap &pixmap)
void drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, QPainter::PixmapFragmentHints hints = PixmapFragmentHints())
//点
void drawPoint(const QPointF &position)
void drawPoint(const QPoint &position)
void drawPoint(int x, int y)
void drawPoints(const QPointF *points, int pointCount)
void drawPoints(const QPolygonF &points)
void drawPoints(const QPoint *points, int pointCount)
void drawPoints(const QPolygon &points)
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule = Qt::OddEvenFill)
//Polygon多边形
void drawPolygon(const QPolygonF &points, Qt::FillRule fillRule = Qt::OddEvenFill)
void drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule = Qt::OddEvenFill)
void drawPolygon(const QPolygon &points, Qt::FillRule fillRule = Qt::OddEvenFill)
//Polyline折线
void drawPolyline(const QPointF *points, int pointCount)
void drawPolyline(const QPolygonF &points)
void drawPolyline(const QPoint *points, int pointCount)
void drawPolyline(const QPolygon &points)
//Rect矩形
void drawRect(const QRectF &rectangle)
void drawRect(int x, int y, int width, int height)
void drawRect(const QRect &rectangle)
void drawRects(const QRectF *rectangles, int rectCount)
void drawRects(const QVector &rectangles)
void drawRects(const QRect *rectangles, int rectCount)
void drawRects(const QVector &rectangles)
//圆角矩形
void drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode = Qt::AbsoluteSize)
void drawRoundedRect(int x, int y, int w, int h, qreal xRadius, qreal yRadius, Qt::SizeMode mode = ...)
void drawRoundedRect(const QRect &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode = ...)
//静态文本
void drawStaticText(const QPointF &topLeftPosition, const QStaticText &staticText)
void drawStaticText(const QPoint &topLeftPosition, const QStaticText &staticText)
void drawStaticText(int left, int top, const QStaticText &staticText)
//文本
void drawText(const QRectF &rectangle, int flags, const QString &text, QRectF *boundingRect = nullptr)
void drawText(const QPointF &position, const QString &text)
void drawText(const QPoint &position, const QString &text)
void drawText(int x, int y, const QString &text)
void drawText(const QRect &rectangle, int flags, const QString &text, QRect *boundingRect = nullptr)
void drawText(int x, int y, int width, int height, int flags, const QString &text, QRect *boundingRect = nullptr)
void drawText(const QRectF &rectangle, const QString &text, const QTextOption &option = QTextOption())
//TiledPixmap平铺像素图
void drawTiledPixmap(const QRectF &rectangle, const QPixmap &pixmap, const QPointF &position = QPointF())
void drawTiledPixmap(int x, int y, int width, int height, const QPixmap &pixmap, int sx = 0, int sy = 0)
void drawTiledPixmap(const QRect &rectangle, const QPixmap &pixmap, const QPoint &position = ...)
设置画笔函数
QPainter Class:Public Functions
void setPen(const QPen &pen)
void setPen(const QColor &color) //颜色
void setPen(Qt::PenStyle style) //样式
例:画一个背景图
//声明绘图事件
virtual void paintEvent(QPaintEvent *event);
在widget.cpp实现绘图事件
定义画笔painter
QPainter *painter = new QPainter(this);
//定义图片控件
QPixmap pix;
pix.load(":/benz.jpg");
//修改图片大小
pix.scaled(this->size());
修改大小函数
QPixmap Class:Public Functions
QPixmap scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio, Qt::TransformationMode transformMode = Qt::FastTransformation) const
QPixmap scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio, Qt::TransformationMode transformMode = Qt::FastTransformation) const
画笔在主窗口绘画
//画笔在主窗口绘画
painter->drawPixmap(0,0,this->width(),this->height(),pix);
新建工程
头文件
#include
#include
首先,在widget.h声明绘图事件
//声明绘图事件
virtual void paintEvent(QPaintEvent *event);
添加资源图片
在widget.cpp实现绘图事件
定义画笔painter
QPainter *painter = new QPainter(this);
//定义图片控件
QPixmap pix;
pix.load(":/benz.jpg");
//修改图片大小
pix.scaled(this->size());
//画笔在主窗口绘画
painter->drawPixmap(0,0,this->width()*0.75,this->height()*0.75,pix);
//注意:高和长都缩小到原来的0.75倍
下面添加一个button
首先我们要知道窗口和按钮的宽高
this->width();this->height(); //窗口的宽高
ui->pushButton->width();ui->pushButton->height(); //按钮的宽高
计算图
//修改button位置到中间的下方
ui->pushButton->move((this->width()-ui->pushButton->width())*0.5,\
this->height()-ui->pushButton->height());
添加button的动作,使其被点击时图片向右移动10单位,更新显示移动后的图片
static int x =0 ;//静态成员,不会被释放;如果是普通的每次使用都是0
//画笔在主窗口绘画
painter->drawPixmap(x,0,this->width()*0.85,this->height()*0.85,pix);
if(x >= this->width())//超出窗口宽度
x=0;
x+=10;
connect(ui->pushButton,&QPushButton::clicked,[=]()
{
//按下按钮,update重新绘图
this->update();
});
//声明绘图事件
virtual void paintEvent(QPaintEvent *event);
在widget.cpp实现绘图事件
定义画笔painter
QPainter *painter = new QPainter(this);
//画线函数
painter->drawLine(0,0,400,400);
void drawRect(int x, int y, int width, int height)
//画矩形rectangle
painter->drawRect(0,0,300,300);
//修改画笔颜色
painter->setPen(Qt::red);
painter->drawRect(100,100,300,300);//改完再画一次
//修改画笔样式
painter->setPen(Qt::DashLine);
painter->drawRect(200,200,200,200);
不过只能设置颜色或样式,无法同时设置
void drawEllipse(int x, int y, int width, int height)
//画圆
painter->drawEllipse(300,300,300,300);
void drawEllipse(int x, int y, int width, int height)
//画椭圆
painter->drawEllipse(200,200,300,150);
void drawPie(int x, int y, int width, int height, int startAngle, int spanAngle)
//画扇形
painter->setPen(Qt::red);
painter->drawPie(200,200,400,400,0,2000);
绘图设备是指继承 QPaintDevice 的子类。Qt 一共提供了四个这样的类,分别 是 QPixmap、QBitmap、QImage 和 QPicture。其中,
QPixmap 继承了 QPaintDevice,因此,你可以使用 QPainter 直接在上面绘制图 形。QPixmap 也可以接受一个字符串作为一个文件的路径来显示这个文件,比如你想在程序之中打开 png、jpeg 之类的文件,就可以使用 QPixmap。使用 QPainter 的 drawPixmap()函数可以把这个文件绘制到一个 QLabel、QPushButton 或者其他的设备上面。QPixmap 是针对屏幕进行特殊优化的,因此,它与实际的底层显示设备息息相关。注意,这里说的显示设备并不是硬件,而是操作系统提供的原 生的绘图引擎。所以,在不同的操作系统平台下,QPixmap 的显示可能会有所差别。
QBitmap 继承自 QPixmap,因此具有 QPixmap 的所有特性,提供单色图像。QBitmap 的色深始终为 1。色深这个概念来自计算机图形学,是指用于表现颜色的二进制 的位数。我们知道,计算机里面的数据都是使用二进制表示的。为了表示一种颜 色,我们也会使用二进制。比如我们要表示 8 种颜色,需要用 3 个二进制位,这 时我们就说色深是 3。因此,所谓色深为 1,也就是使用 1 个二进制位表示颜色。 1 个位只有两种状态:0 和 1,因此它所表示的颜色就有两种,黑和白。所以说, QBitmap 实际上是只有黑白两色的图像数据。
由于 QBitmap 色深小,因此只占用很少的存储空间,所以适合做光标文件和笔刷
下面我们来看同一个图像文件在 QPixmap 和 QBitmap 下的不同表现:
void PaintWidget::paintEvent(QPaintEvent *)
{
QPixmap pixmap(":/Image/butterfly.png");
QPixmap pixmap1(":/Image/butterfly1.png");
QBitmap bitmap(":/Image/butterfly.png");
QBitmap bitmap1(":/Image/butterfly1.png");
QPainter painter(this);
painter.drawPixmap(0, 0, pixmap);
painter.drawPixmap(200, 0, pixmap1);
painter.drawPixmap(0, 130, bitmap);
painter.drawPixmap(200, 130, bitmap1);
}
这里我们给出了两张 png 图片。butterfly1.png 是没有透明色的纯白背景,而 butterfly.png 是具有透明色的背景。我们分别使用 QPixmap 和 QBitmap 来加载它们。注意看它们的区别:白色的背景在 QBitmap 中消失了,而透明色在 QBitmap 中转换成了黑色;其他颜色则是使用点的疏密程度来体现的。
QPixmap 使用底层平台的绘制系统进行绘制,无法提供像素级别的操作,而 QImage 则是使用独立于硬件的绘制系统,实际上是自己绘制自己,因此提供了像素级别的操作,并且能够在不同系统之上提供一个一致的显示形式。
QImage 与 QPixmap 的区别
QImage 与 QPixmap 之间的转换:
QImage 转 QPixmap :使用 QPixmap 的静态成员函数: fromImage()
QPixmap fromImage(const QImage & image, Qt::ImageConversionFlags flags = Qt::AutoColor)
QPixmap 转 QImage: 使用 QPixmap 类的成员函数: toImage()
QImage toImage() const
//定义一个绘图设备QBitmap
QBitmap bit(800,800);
//定义画笔
QPainter painter(&bit);
painter.drawEllipse(QPoint(400,400),bit.width()/3,bit.height()/3);
//保存图片
bit.save("C:\\Users\\HUIO\\Desktop\\Me\\QT\\00\\11_painter\\bit01.png");
//定义一个绘图设备QImage,并加载图片(提前添加资源)
QImage img;
img.load(":/benz_2.jpg");
//定义画笔
QPainter painter2(&img);
painter2.drawEllipse(QPoint(800,800),bit.width()/2,bit.height()/2);
//保存图片
img.save("C:\\Users\\HUIO\\Desktop\\Me\\QT\\00\\11_painter\\img_01.png");
注意观察可以看到圆的边缘
接下来,我们根据QImage像素级别的操作的特性,更改图片RBG颜色
//定义一个绘图设备QImage
QImage img;
img.load(":/benz_2.jpg");
//由于圆心在(400,400),这里我将颜色起点设为(450,450),以便观察
for(int i=450;i<600;i++)
{
for(int j=450;j<600;j++)
{
int val = qRgb(233,0,0);
img.setPixel(i,j,val);
}
}
可以记录和重现 QPainter 命令的绘图设备。 QPicture 将 QPainter 的命令序列化到一个 IO 设备,保存为一个平台独立的文件格式。这种格式有时候会是“元文件(meta- files)”。Qt 的这种格式是二进制的,不同于某些本地的元文件,Qt 的 pictures 文件没有内容上的限制,只要是能够被 QPainter 绘制的元素,不论是字体还是 pixmap,或者是变换, 都可以保存进一个 picture 中。
QPicture 是平台无关的,因此它可以使用在多种设备之上,比如 svg、pdf、ps、 打印机或者屏幕。回忆下我们这里所说的 QPaintDevice,实际上是说可以有 QPainter 绘制的对象。QPicture 使用系统的分辨率,并且可以调整 QPainter 来消除不同设备之间的显示差异。
如果我们要记录下 QPainter 的命令,首先要使用 QPainter::begin()函数,将 QPicture 实例作为参数传递进去,以便告诉系统开始记录,记录完毕后使用 QPainter::end()命令终止。
例:
//定义一个绘图设备QPicture
QPicture pic;
//定义画笔
QPainter painter3;
//记录绘图指令
painter3.begin(&pic); //开始指令
painter3.drawEllipse(100,100,100,100);//画图
painter3.end(); //结束指令
//保存指令
pic.save("C:\\Users\\HUIO\\Desktop\\Me\\QT\\00\\11_painter\\pic01.cmd");
接下来,重现绘图指令到主窗口(在绘图事件完成)
void Widget::paintEvent(QPaintEvent *event)
{
//重现画图指令:
//创建绘图设备
QPicture pic;
QPainter painter(this);
//绘图设备pic加载绘图指令
pic.load("C:\\Users\\HUIO\\Desktop\\Me\\QT\\00\\11_painter\\pic01.cmd");
//画笔根据绘图指令绘图
painter.drawPicture(10,10,pic);
}