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 基类 窗口标题 setWindowTitle() 大小与位置 resize() / move() / setGeometry() 窗口图标 setWindowIcon() 模态 / 非模态 exec() / show() 核心关系 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,看看一个完整的应用程序窗口应该怎么搭建。

可可精品资料库