QT(2)
二、UI入门
1、QWidget类(熟悉)
QWidget
类是Qt中所有用户界面组件的基类。窗口和控件(如按钮、标签等)都直接或间接继承自它。它提供了组件最基础的特性,如几何形状、布局、事件处理等。
常用属性与方法:
属性/方法 | 类型/签名 | 说明 |
---|---|---|
x : const int | 属性 (只读) | 组件在其父组件坐标系中的横坐标,以左上角为定位点。横轴零点在父组件客户区最左边,正方向向右。注意:位置包含窗口边框。 |
y : const int | 属性 (只读) | 组件在其父组件坐标系中的纵坐标,以左上角为定位点。纵轴零点在父组件客户区最上边,正方向向下。注意:位置包含窗口边框。 |
void move(int x, int y) | 方法 | 将组件移动到新的位置(x, y)。这是修改组件位置的常用方法。 |
width : const int | 属性 (只读) | 组件的宽度,不包含边框。 |
height : const int | 属性 (只读) | 组件的高度,不包含边框。 |
void resize(int w, int h) | 方法 | 修改组件的宽度和高度。 |
示例:在构造函数中设置窗口属性
// dialog.cpp
#include "dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent) // 调用父类构造函数
{// 移动窗口到屏幕坐标(200, 200)的位置move(200, 200);// 设置窗口客户区的宽度为200像素,高度为600像素resize(200, 600);// 使用样式表设置窗口背景颜色为红色setStyleSheet("background-color:red");
}Dialog::~Dialog()
{// 析构函数
}
2、添加子组件(掌握)
空的窗口没什么用,我们通常需要向其中添加各种组件(如按钮、文本框等)来实现交互功能。这些组件需要在父窗口的构造函数中创建,并指定父对象(通常用 this
),以确保父窗口析构时能自动销毁它们,避免内存泄漏。
示例:添加按钮并设置样式
dialog.h (头文件)
#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QDebug>
#include <QPushButton> // 包含QPushButton的头文件// 定义一个按钮的样式宏(可选,便于管理和复用)
#define QPushButton_STYLE (QString("\
QPushButton {\font-family: Microsoft Yahei;\font-size: 20pt;\color: white;\background-color: rgb(14, 150, 254);\border-radius: 8px;\
}\
QPushButton:hover {\background-color: rgb(100, 137, 112);\
}\
QPushButton:pressed {\background-color: rgb(14, 135, 10);\padding-left: 3px;\padding-top: 3px;\
}"))class Dialog : public QDialog
{Q_OBJECT // Qt元对象系统宏,必须包含private:QPushButton *btn; // 声明一个指向按钮的指针public:Dialog(QWidget *parent = nullptr);~Dialog();
};#endif // DIALOG_H
dialog.cpp (源文件)
#include "dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent)
{// 设置主窗口大小resize(500, 500);// 创建按钮对象:"你好"是按钮文本,this指定父对象为当前窗口btn = new QPushButton("你好", this);// 将按钮移动到相对父窗口(50, 250)的位置btn->move(50, 250);// 应用预定义的样式表btn->setStyleSheet(QPushButton_STYLE);
}Dialog::~Dialog()
{// 由于btn是父窗口的子组件,通常Qt会自动销毁它。// 但显式delete也是一个好习惯,尤其是在复杂项目中。delete btn;
}
配色网站推荐:
- ColorPicker: http://tools.jb51.net/static/colorpicker/
- Color Palettes: https://colors.muz.li/
三、信号槽(重点)
1、概念
信号槽是Qt的核心机制,用于对象间的通信,是一种类型安全且松耦合的“回调”。
- 信号 (Signal):由对象在特定事件发生时发射的通知(例如,按钮被点击时发射
clicked()
信号)。 - 槽 (Slot):普通的成员函数,用于响应特定的信号。
- 连接 (Connection):使用
QObject::connect()
函数建立信号与槽的关联。
两个先决条件
- 通信的对象必须直接或间接继承自
QObject
。 - 类声明中必须包含
Q_OBJECT
宏。
2、函数原型
connect
函数的基本形式如下:
// 参数1:sender,发送信号的对象指针
// 参数2:signal,发送的信号(使用 SIGNAL() 宏)
// 参数3:receiver,接收信号的对象指针
// 参数4:method,接收的槽函数(使用 SLOT() 宏)
// 返回值:QMetaObject::Connection,可用于后续断开连接
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method);
第五个参数 (Qt::ConnectionType) :
connect
函数还有一个可选的第五个参数 type
,用于指定连接方式,默认为 Qt::AutoConnection
。
连接类型 | 含义 |
---|---|
Qt::AutoConnection (默认) | 自动决定。如果发送者和接收者在同一线程,等同于 DirectConnection ;否则等同于 QueuedConnection 。 |
Qt::DirectConnection | 直接连接。信号发射后,槽函数立即在发送者线程中被调用。 |
Qt::QueuedConnection | 队列连接。信号发射后,槽函数会被放入接收者线程的事件循环中,等待稍后在接收者线程中被调用。用于跨线程通信 2 。 |
Qt::BlockingQueuedConnection | 阻塞队列连接。类似 QueuedConnection ,但发送者线程会阻塞,直到接收者线程的槽函数执行完毕。注意:发送者和接收者不能在同一线程。 |
Qt::UniqueConnection | 唯一连接。可以与其他类型按位或组合使用,确保相同的信号和槽只会被连接一次,防止重复连接。 |
3、实现
3.1 自带信号 → 自带槽
最简单的方式,使用Qt内置的信号和槽。
示例:点击按钮,关闭窗口
// dialog.cpp
#include "dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent)
{resize(500, 500);btn = new QPushButton("关闭", this);btn->move(200, 250);// 连接:按钮的clicked信号 -> 窗口的close槽connect(btn, SIGNAL(clicked()), this, SLOT(close()));
}
3.2 自带信号 → 自定义槽
最常用的方式,槽函数需要开发者自己声明和实现。
示例:点击按钮,移动窗口并打印坐标
dialog.h
// ... 其他代码同上 ...
class Dialog : public QDialog
{Q_OBJECT
// ... 其他代码同上 ...
private slots: // 声明自定义槽函数(私有槽)void mySlot(); // 槽函数
};
dialog.cpp
// ... 构造函数和按钮创建同上 ...connect(btn, SIGNAL(clicked()), this, SLOT(mySlot()));
}// 实现自定义槽函数
void Dialog::mySlot()
{int currentX = x(); // 获取当前x坐标int currentY = y(); // 获取当前y坐标move(currentX + 10, currentY + 10); // 移动窗口qDebug() << "New position:" << currentX + 10 << currentY + 10; // 打印新坐标
}
3.3 自定义信号
信号只需声明,无需实现(定义),使用 emit
关键字发射。
示例:通过自定义信号间接关闭窗口
dialog.h
// ... 其他代码同上 ...
class Dialog : public QDialog
{Q_OBJECT
// ... 其他代码同上 ...
signals: // 信号声明区域void mySignal(); // 自定义信号
private slots:void mySlot(); // 槽函数,用于响应按钮点击并发射自定义信号
};
dialog.cpp
// ... 构造函数 ...connect(btn, SIGNAL(clicked()), this, SLOT(mySlot()));// 连接:自定义信号mySignal -> 窗口的close槽connect(this, SIGNAL(mySignal()), this, SLOT(close()));
}void Dialog::mySlot()
{emit mySignal(); // 发射自定义信号
}
4、信号槽传参
信号和槽都可以带参数,参数类型必须兼容,且信号的参数个数可以多于槽的参数个数,但反之不行。
示例:点击按钮,按钮文字显示点击次数
dialog.h
// ... 其他代码同上 ...
class Dialog : public QDialog
{Q_OBJECT
// ... 其他代码同上 ...
public slots:void mySlot();void mySlot2(int count); // 带参数的槽
signals:void mySignal(int count); // 带参数的信号
};
dialog.cpp
// ... 构造函数 ...connect(btn, SIGNAL(clicked()), this, SLOT(mySlot()));connect(this, SIGNAL(mySignal(int)), this, SLOT(mySlot2(int)));
}void Dialog::mySlot()
{static int count = 0; // 静态变量记录点击次数count++;emit mySignal(count); // 发射信号,并传递count值
}void Dialog::mySlot2(int count)
{// 将整数转换为字符串并设置为按钮文本btn->setText(QString::number(count));
}
传递自定义类型:如果信号槽需要传递自定义数据类型(如结构体或类),则该类型需要使用 qRegisterMetaType()
进行注册,才能在不同线程间安全传递(同一线程内有时可以不注册,但建议总是注册)。
5、对应关系
5.1 一对多
一个信号可以连接多个槽函数。信号发射时,槽函数的调用顺序是不确定的。
connect(button, SIGNAL(clicked()), this, SLOT(slot1()));
connect(button, SIGNAL(clicked()), this, SLOT(slot2()));
connect(button, SIGNAL(clicked()), this, SLOT(slot3()));
// 点击按钮,slot1, slot2, slot3 都会被调用(顺序不确定)
5.2 多对一
多个信号可以连接同一个槽函数。在槽函数中,可以使用 sender()
函数判断是哪个对象发射的信号。
connect(button1, SIGNAL(clicked()), this, SLOT(handleButton()));
connect(button2, SIGNAL(clicked()), this, SLOT(handleButton()));// 在槽函数中:
void MyClass::handleButton()
{QPushButton *clickedButton = qobject_cast<QPushButton*>(sender());if (clickedButton) {// 根据clickedButton判断是哪个按钮被点击了}
}
断开连接
使用 disconnect
函数断开信号槽连接,参数形式与 connect
类似。
disconnect(btn, SIGNAL(clicked()), this, SLOT(mySlot()));
// 或者断开某个对象的所有连接
disconnect(btn, 0, 0, 0);