4. QWidget与窗口属性:一切UI的起点
说实话,Qt里几乎所有你能看到的东西,都是从QWidget派生出来的。按钮、标签、输入框、甚至整个对话框……它们的祖宗都是QWidget。我刚开始学Qt时,总觉得这玩意儿就是个“空壳子”,后来才发现,恰恰是它定义了窗口最核心的行为。
这一节,我们就来彻底搞懂QWidget这个基类,以及窗口的那些基本属性——标题、大小、位置、图标,还有模态与非模态的区别。
4.1 QWidget:一切UI组件的“根”
QWidget是Qt中所有用户界面对象的基类。它本身就是一个空白矩形区域,可以接收鼠标、键盘事件,也可以绘制内容。你想想看,一个按钮本质上就是一个QWidget,只不过它额外绘制了按钮的外观,并处理了点击事件。
我个人习惯把QWidget理解为“画布”。你可以在上面放子控件,也可以直接在上面绘图。它提供了最基础的窗口管理功能:
- 设置标题、大小、位置
- 控制显示/隐藏
- 处理事件(鼠标、键盘、绘制等)
- 管理子控件(布局、父子关系)
核心要点:任何窗口或控件,最终都继承自QWidget。理解它的属性,就等于理解了所有UI组件的基础行为。
4.2 窗口标题:给窗口一个“名字”
窗口标题就是顶部标题栏显示的那行文字。设置它很简单:
// 方式一:构造函数中设置
QWidget *w = new QWidget();
w->setWindowTitle("我的第一个窗口");
// 方式二:直接调用setter
w->setWindowTitle("标题可以随时改");
// 方式三:获取当前标题
QString title = w->windowTitle();
我在项目中遇到过一个问题:有些窗口明明调用了setWindowTitle,但标题没变。后来发现,是因为窗口还没创建(还没show),某些平台下setWindowTitle会被忽略。嗯,这里要注意——最好在show之前设置标题,或者show之后再设置一次。
4.3 窗口大小与位置:控制窗口的“身材”和“站位”
窗口的大小和位置,是UI开发中最常打交道的事情。Qt提供了两套API:一套是直接设置像素值,另一套是通过QRect统一设置。
设置大小
// 设置固定大小(不包含边框)
w->resize(800, 600);
// 设置最小/最大尺寸
w->setMinimumSize(400, 300);
w->setMaximumSize(1920, 1080);
// 设置固定尺寸(用户不能调整大小)
w->setFixedSize(400, 300);
设置位置
// 移动窗口到屏幕坐标 (100, 100)
w->move(100, 100);
// 同时设置位置和大小
w->setGeometry(100, 100, 800, 600);
小技巧:setGeometry设置的是窗口的“客户区”位置和大小,不包括标题栏和边框。如果你想让窗口居中显示,可以这样:
// 获取屏幕尺寸
QScreen *screen = QGuiApplication::primaryScreen();
QRect screenRect = screen->availableGeometry();
// 计算居中位置
int x = (screenRect.width() - w->width()) / 2;
int y = (screenRect.height() - w->height()) / 2;
w->move(x, y);
我曾经在做一个多屏应用时,发现窗口总是跑到主显示器上。后来才意识到,move和setGeometry都是相对于当前屏幕的,如果你要跨屏操作,得用QDesktopWidget或者QScreen来获取目标屏幕的几何信息。
4.4 窗口图标:让窗口更有“辨识度”
窗口图标就是标题栏左侧那个小图标,也出现在任务栏里。设置它需要用到QIcon:
// 从资源文件加载
w->setWindowIcon(QIcon(":/icons/app.png"));
// 从文件系统加载
w->setWindowIcon(QIcon("C:/path/to/icon.ico"));
// 从内置图标加载(Qt自带)
w->setWindowIcon(QIcon::fromTheme("application-x-executable"));
注意:Linux下用QIcon::fromTheme需要系统安装对应的图标主题。Windows下建议用.ico文件,否则可能显示不正常。我踩过这个坑——在Ubuntu上好好的图标,到了Windows上就变成默认的白色方块了。
4.5 模态与非模态对话框:谁“霸占”了用户操作权?
这是初学者最容易搞混的概念。说白了,模态对话框会阻塞用户对其他窗口的操作,而非模态则不会。
模态对话框
模态对话框弹出后,用户必须先处理它,才能操作其他窗口。典型的例子就是“文件保存”对话框:
QDialog *dialog = new QDialog(this);
dialog->setWindowTitle("重要提示");
dialog->setModal(true); // 设置为模态
dialog->exec(); // 阻塞,直到对话框关闭
// 或者更简洁的方式
QMessageBox::information(this, "提示", "操作成功!");
exec()会启动一个事件循环,直到对话框关闭才返回。这意味着exec()后面的代码不会立即执行。
非模态对话框
非模态对话框弹出后,用户仍然可以操作其他窗口。比如“查找替换”对话框:
QDialog *dialog = new QDialog(this);
dialog->setWindowTitle("查找");
dialog->setModal(false); // 非模态(默认就是false)
dialog->show(); // 不阻塞,立即返回
这里有个关键区别:非模态对话框用show(),模态对话框用exec()。show()会立即返回,程序继续执行。
| 特性 | 模态对话框 | 非模态对话框 |
|---|---|---|
| 阻塞用户操作 | 是 | 否 |
| 显示方法 | exec() | show() |
| 典型用途 | 警告、确认、文件选择 | 查找、属性、工具面板 |
| 内存管理 | 通常栈上创建或deleteLater | 注意防止内存泄漏 |
避坑指南:我曾经在非模态对话框里忘记设置setAttribute(Qt::WA_DeleteOnClose),结果对话框关闭后,对象还在内存里。每次打开都new一个,最后内存暴涨。所以非模态对话框,要么设置WA_DeleteOnClose,要么在关闭时手动delete。
4.6 知识体系总览
下面这张图,把QWidget和窗口属性的核心关系梳理清楚了:
这张图把QWidget的四个核心属性串起来了。你想想看,一个窗口无非就是:给它一个标题,设定它的大小和位置,放个图标,再决定它是模态还是非模态。就这么简单。
4.7 实战小例子:组装一个完整的窗口
最后,我们把这些知识点串起来,写一个完整的例子:
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QIcon>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// 创建主窗口
QWidget window;
window.setWindowTitle("我的第一个Qt窗口");
window.resize(600, 400);
window.setWindowIcon(QIcon(":/icons/app.png"));
// 添加一个按钮,点击弹出模态对话框
QPushButton *btn = new QPushButton("点击我", &window);
btn->move(250, 180);
// 连接信号槽
QObject::connect(btn, &QPushButton::clicked, [&]() {
QMessageBox::information(&window, "提示", "你点击了按钮!");
});
window.show();
return app.exec();
}
这个例子虽然简单,但涵盖了窗口标题、大小、位置、图标、模态对话框这几个核心知识点。你可以在Qt Creator里跑一下试试。
个人建议:刚开始学的时候,别急着搞复杂布局。先玩熟QWidget的这些基本属性,后面学QMainWindow、QDialog这些子类时,你会发现它们本质上就是QWidget的“增强版”。
好了,这一节的内容就到这里。QWidget作为Qt UI的基石,掌握它的属性设置,你就已经迈出了Qt开发的第一步。下一节我们会深入QMainWindow,看看一个完整的应用程序窗口应该怎么搭建。
可可精品资料库