窗口标志示例

窗口标志示例展示了如何在Qt中使用窗口标志。

窗口标志可以是类型或提示。类型用于指定小部件的窗口系统属性。一个小部件只能有一个类型,默认是 Qt::Widget。然而,一个小部件可以有零个或多个提示。提示用于自定义顶级窗口的外观。

小部件的标志存储在一个 Qt::WindowFlags 类型中,该类型存储标志的或组合。

窗口标志示例截图

这个例子包含两个类

  • ControllerWindow 是主应用程序小部件,允许用户在可行的窗口标志之间进行选择,并在单独的预览窗口中显示效果。
  • PreviewWindow 是一个自定义小部件,在只读文本编辑器中显示其当前设置的窗口标志的名称。

我们将首先查看 ControllerWindow 类,然后我们将看看 PreviewWindow 类。

ControllerWindow 类定义

class ControllerWindow : public QWidget
{
    Q_OBJECT

public:
    ControllerWindow(QWidget *parent = nullptr);

private slots:
    void updatePreview();

private:
    void createTypeGroupBox();
    void createHintsGroupBox();
    QCheckBox *createCheckBox(const QString &text);
    QRadioButton *createRadioButton(const QString &text);

    PreviewWindow *previewWindow;

    QGroupBox *typeGroupBox;
    QGroupBox *hintsGroupBox;
    QPushButton *quitButton;

    QRadioButton *windowRadioButton;
    QRadioButton *dialogRadioButton;
    QRadioButton *sheetRadioButton;
    QRadioButton *drawerRadioButton;
    QRadioButton *popupRadioButton;
    QRadioButton *toolRadioButton;
    QRadioButton *toolTipRadioButton;
    QRadioButton *splashScreenRadioButton;

    QCheckBox *msWindowsFixedSizeDialogCheckBox;
    QCheckBox *x11BypassWindowManagerCheckBox;
    QCheckBox *framelessWindowCheckBox;
    QCheckBox *windowNoShadowCheckBox;
    QCheckBox *windowTitleCheckBox;
    QCheckBox *windowSystemMenuCheckBox;
    QCheckBox *windowMinimizeButtonCheckBox;
    QCheckBox *windowMaximizeButtonCheckBox;
    QCheckBox *windowCloseButtonCheckBox;
    QCheckBox *windowContextHelpButtonCheckBox;
    QCheckBox *windowShadeButtonCheckBox;
    QCheckBox *windowStaysOnTopCheckBox;
    QCheckBox *windowStaysOnBottomCheckBox;
    QCheckBox *customizeWindowHintCheckBox;
};

ControllerWindow 继承自 QWidget。这个小部件允许用户在选择可行的窗口标志,并在单独的预览窗口中显示效果。

我们声明一个私有的 updatePreview() 槽来刷新预览窗口,每当用户更改窗口标志时。

我们还声明了几个私有函数以简化构造函数:我们调用私有函数 createTypeGroupBox() 来为每种可行的窗口类型创建一个单选按钮,并使用私有函数 createButton() 创建这些按钮,并将它们组合在一个分组框内。类似地,我们使用私有函数 createHintsGroupBox() 为每个可行的提示创建一个复选框,并使用私有函数 createCheckBox() 创建这些复选框。

除了各种单选按钮和复选框之外,我们还需要一个关联的 PreviewWindow 来显示当前选择的窗口标志的效果。

Screenshot of the Controller Window

ControllerWindow 类实现

ControllerWindow::ControllerWindow(QWidget *parent)
    : QWidget(parent)
{
    previewWindow = new PreviewWindow(this);

    createTypeGroupBox();
    createHintsGroupBox();

    quitButton = new QPushButton(tr("&Quit"));
    connect(quitButton, &QPushButton::clicked,
            qApp, &QCoreApplication::quit);

    QHBoxLayout *bottomLayout = new QHBoxLayout;
    bottomLayout->addStretch();
    bottomLayout->addWidget(quitButton);

    QHBoxLayout *mainLayout = new QHBoxLayout;
    mainLayout->addWidget(typeGroupBox);
    mainLayout->addWidget(hintsGroupBox);
    mainLayout->addLayout(bottomLayout);
    setLayout(mainLayout);

    setWindowTitle(tr("Window Flags"));
    updatePreview();
}

在构造函数中,我们首先创建预览窗口。然后,我们使用私有函数 createTypeGroupBox()createHintsGroupBox() 创建包含可行窗口标志的分组框。除了这些,我们创建了一个 退出 按钮。我们将按钮和可扩展空间放在一个单独的布局中,使按钮出现在 WindowFlag 小部件的右下角。

最后,我们将按钮的布局和两个分组框添加到一个 QVBoxLayout 中,设置窗口标题,并通过 updatePreview() 槽刷新预览窗口。

void ControllerWindow::updatePreview()
{
    Qt::WindowFlags flags;

    if (windowRadioButton->isChecked())
        flags = Qt::Window;
    else if (dialogRadioButton->isChecked())
        flags = Qt::Dialog;
    else if (sheetRadioButton->isChecked())
        flags = Qt::Sheet;
    else if (drawerRadioButton->isChecked())
        flags = Qt::Drawer;
    else if (popupRadioButton->isChecked())
        flags = Qt::Popup;
    else if (toolRadioButton->isChecked())
        flags = Qt::Tool;
    else if (toolTipRadioButton->isChecked())
        flags = Qt::ToolTip;
    else if (splashScreenRadioButton->isChecked())
        flags = Qt::SplashScreen;

每次用户更改窗口标志时,都会调用 updatePreview() 插槽。首先我们创建一个空的 Qt::WindowFlags flags,然后确定哪些类型已被检查,并将它们添加到 flags 中。

    if (msWindowsFixedSizeDialogCheckBox->isChecked())
        flags |= Qt::MSWindowsFixedSizeDialogHint;
    if (x11BypassWindowManagerCheckBox->isChecked())
        flags |= Qt::X11BypassWindowManagerHint;
    if (framelessWindowCheckBox->isChecked())
        flags |= Qt::FramelessWindowHint;
    if (windowNoShadowCheckBox->isChecked())
        flags |= Qt::NoDropShadowWindowHint;
    if (windowTitleCheckBox->isChecked())
        flags |= Qt::WindowTitleHint;
    if (windowSystemMenuCheckBox->isChecked())
        flags |= Qt::WindowSystemMenuHint;
    if (windowMinimizeButtonCheckBox->isChecked())
        flags |= Qt::WindowMinimizeButtonHint;
    if (windowMaximizeButtonCheckBox->isChecked())
        flags |= Qt::WindowMaximizeButtonHint;
    if (windowCloseButtonCheckBox->isChecked())
        flags |= Qt::WindowCloseButtonHint;
    if (windowContextHelpButtonCheckBox->isChecked())
        flags |= Qt::WindowContextHelpButtonHint;
    if (windowShadeButtonCheckBox->isChecked())
        flags |= Qt::WindowShadeButtonHint;
    if (windowStaysOnTopCheckBox->isChecked())
        flags |= Qt::WindowStaysOnTopHint;
    if (windowStaysOnBottomCheckBox->isChecked())
        flags |= Qt::WindowStaysOnBottomHint;
    if (customizeWindowHintCheckBox->isChecked())
        flags |= Qt::CustomizeWindowHint;

    previewWindow->setWindowFlags(flags);

我们还会确定哪些提示已被检查,并使用 OR 运算符将它们添加到 flags 中。我们使用 flags 来设置预览窗口的窗口标志。

    QPoint pos = previewWindow->pos();
    if (pos.x() < 0)
        pos.setX(0);
    if (pos.y() < 0)
        pos.setY(0);
    previewWindow->move(pos);
    previewWindow->show();
}

我们调整预览窗口的位置。这样做的原因是在一些平台上对窗口边框进行调整可能会在背后改变窗口位置。如果一个窗口位于屏幕的左上角,窗口的部分可能不可见。因此,我们调整小部件的位置以确保,如果发生这种情况,窗口将在屏幕边界内移动。最后,我们调用 QWidget::show() 确保预览窗口是可见的。

void ControllerWindow::createTypeGroupBox()
{
    typeGroupBox = new QGroupBox(tr("Type"));

    windowRadioButton = createRadioButton(tr("Window"));
    dialogRadioButton = createRadioButton(tr("Dialog"));
    sheetRadioButton = createRadioButton(tr("Sheet"));
    drawerRadioButton = createRadioButton(tr("Drawer"));
    popupRadioButton = createRadioButton(tr("Popup"));
    toolRadioButton = createRadioButton(tr("Tool"));
    toolTipRadioButton = createRadioButton(tr("Tooltip"));
    splashScreenRadioButton = createRadioButton(tr("Splash screen"));
    windowRadioButton->setChecked(true);

    QGridLayout *layout = new QGridLayout;
    layout->addWidget(windowRadioButton, 0, 0);
    layout->addWidget(dialogRadioButton, 1, 0);
    layout->addWidget(sheetRadioButton, 2, 0);
    layout->addWidget(drawerRadioButton, 3, 0);
    layout->addWidget(popupRadioButton, 0, 1);
    layout->addWidget(toolRadioButton, 1, 1);
    layout->addWidget(toolTipRadioButton, 2, 1);
    layout->addWidget(splashScreenRadioButton, 3, 1);
    typeGroupBox->setLayout(layout);
}

从构造函数中调用了私有函数 createTypeGroupBox()

首先我们创建一个分组框,然后为窗口标志中可用的每种类型创建一个单选按钮(使用私有函数 createRadioButton())。我们将 Qt::Window 设为最初应用的类型。我们将单选按钮放入一个 QGridLayout 中,并将布局安装到分组框上。

我们不包含默认的 Qt::Widget 类型。原因是它与其他类型的行为略有不同。如果未指定小部件的类型,并且它没有父级,则该小部件是一个窗口。然而,如果它有父级,则它是一个标准子小部件。其他类型都是顶级窗口,由于提示仅影响顶级窗口,所以我们放弃了 Qt::Widget 类型。

void ControllerWindow::createHintsGroupBox()
{
    hintsGroupBox = new QGroupBox(tr("Hints"));

    msWindowsFixedSizeDialogCheckBox =
            createCheckBox(tr("MS Windows fixed size dialog"));
    x11BypassWindowManagerCheckBox =
            createCheckBox(tr("X11 bypass window manager"));
    framelessWindowCheckBox = createCheckBox(tr("Frameless window"));
    windowNoShadowCheckBox = createCheckBox(tr("No drop shadow"));
    windowTitleCheckBox = createCheckBox(tr("Window title"));
    windowSystemMenuCheckBox = createCheckBox(tr("Window system menu"));
    windowMinimizeButtonCheckBox = createCheckBox(tr("Window minimize button"));
    windowMaximizeButtonCheckBox = createCheckBox(tr("Window maximize button"));
    windowCloseButtonCheckBox = createCheckBox(tr("Window close button"));
    windowContextHelpButtonCheckBox =
            createCheckBox(tr("Window context help button"));
    windowShadeButtonCheckBox = createCheckBox(tr("Window shade button"));
    windowStaysOnTopCheckBox = createCheckBox(tr("Window stays on top"));
    windowStaysOnBottomCheckBox = createCheckBox(tr("Window stays on bottom"));
    customizeWindowHintCheckBox= createCheckBox(tr("Customize window"));

    QGridLayout *layout = new QGridLayout;
    layout->addWidget(msWindowsFixedSizeDialogCheckBox, 0, 0);
    layout->addWidget(x11BypassWindowManagerCheckBox, 1, 0);
    layout->addWidget(framelessWindowCheckBox, 2, 0);
    layout->addWidget(windowNoShadowCheckBox, 3, 0);
    layout->addWidget(windowTitleCheckBox, 4, 0);
    layout->addWidget(windowSystemMenuCheckBox, 5, 0);
    layout->addWidget(customizeWindowHintCheckBox, 6, 0);
    layout->addWidget(windowMinimizeButtonCheckBox, 0, 1);
    layout->addWidget(windowMaximizeButtonCheckBox, 1, 1);
    layout->addWidget(windowCloseButtonCheckBox, 2, 1);
    layout->addWidget(windowContextHelpButtonCheckBox, 3, 1);
    layout->addWidget(windowShadeButtonCheckBox, 4, 1);
    layout->addWidget(windowStaysOnTopCheckBox, 5, 1);
    layout->addWidget(windowStaysOnBottomCheckBox, 6, 1);
    hintsGroupBox->setLayout(layout);
}

从构造函数中也调用了私有函数 createHintsGroupBox()

同样,我们首先创建一个分组框,然后为窗口标志中可用的每个提示创建一个复选框(使用私有函数 createCheckBox())。我们将复选框放入一个 QGridLayout 中,并将布局安装到分组框上。

QCheckBox *ControllerWindow::createCheckBox(const QString &text)
{
    QCheckBox *checkBox = new QCheckBox(text);
    connect(checkBox, &QCheckBox::clicked,
            this, &ControllerWindow::updatePreview);
    return checkBox;
}

createHintsGroupBox() 中调用了私有函数 createCheckBox()

我们仅创建一个带有所提供文本的 QCheckBox,将其连接到私有插槽 updatePreview(),并返回复选框的指针。

QRadioButton *ControllerWindow::createRadioButton(const QString &text)
{
    QRadioButton *button = new QRadioButton(text);
    connect(button, &QRadioButton::clicked,
            this, &ControllerWindow::updatePreview);
    return button;
}

在私有函数 createRadioButton() 中,我们创建了一个带有所提供文本的 QRadioButton 并将其连接到私有插槽 updatePreview()。该函数从 createTypeGroupBox() 中调用,并返回按钮的指针。

预览窗口类定义

class PreviewWindow : public QWidget
{
    Q_OBJECT

public:
    PreviewWindow(QWidget *parent = nullptr);

    void setWindowFlags(Qt::WindowFlags flags);

private:
    QTextEdit *textEdit;
    QPushButton *closeButton;
};

PreviewWindow 类继承自 QWidget。它是一个自定义小部件,显示当前设置的窗口标志的名称在一个只读文本编辑器中。还提供了一个关闭窗口的 QPushbutton。

我们重新实现了构造函数来创建 关闭 按钮、文本编辑器和 QWidget::setWindowFlags() 函数以显示窗口标志的名称。

Screenshot of the Preview Window

预览窗口类实现

PreviewWindow::PreviewWindow(QWidget *parent)
    : QWidget(parent)
{
    textEdit = new QTextEdit;
    textEdit->setReadOnly(true);
    textEdit->setLineWrapMode(QTextEdit::NoWrap);

    closeButton = new QPushButton(tr("&Close"));
    connect(closeButton, &QPushButton::clicked,
            this, &PreviewWindow::close);

    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(textEdit);
    layout->addWidget(closeButton);
    setLayout(layout);

    setWindowTitle(tr("Preview"));
}

在构造函数中,我们首先创建一个 QTextEdit 并确保它是只读的。

我们还禁止在文本编辑器中使用QTextEdit::setLineWrapMode()函数进行任何行包装。结果是在窗口标志的名称超过编辑器宽度时,会出现水平滚动条。由于我们使用内置的行断开来构建显示的文本,这种方法是合理的。如果没有保证换行,使用另一个QTextEdit::LineWrapMode可能更合理。

然后我们创建了一个关闭按钮,在设置窗口标题之前,将这两个小部件放入一个QVBoxLayout中。

void PreviewWindow::setWindowFlags(Qt::WindowFlags flags)
{
    QWidget::setWindowFlags(flags);

    QString text;

    Qt::WindowFlags type = (flags & Qt::WindowType_Mask);
    if (type == Qt::Window)
        text = "Qt::Window";
    else if (type == Qt::Dialog)
        text = "Qt::Dialog";
    else if (type == Qt::Sheet)
        text = "Qt::Sheet";
    else if (type == Qt::Drawer)
        text = "Qt::Drawer";
    else if (type == Qt::Popup)
        text = "Qt::Popup";
    else if (type == Qt::Tool)
        text = "Qt::Tool";
    else if (type == Qt::ToolTip)
        text = "Qt::ToolTip";
    else if (type == Qt::SplashScreen)
        text = "Qt::SplashScreen";

    if (flags & Qt::MSWindowsFixedSizeDialogHint)
        text += "\n| Qt::MSWindowsFixedSizeDialogHint";
    if (flags & Qt::X11BypassWindowManagerHint)
        text += "\n| Qt::X11BypassWindowManagerHint";
    if (flags & Qt::FramelessWindowHint)
        text += "\n| Qt::FramelessWindowHint";
    if (flags & Qt::NoDropShadowWindowHint)
        text += "\n| Qt::NoDropShadowWindowHint";
    if (flags & Qt::WindowTitleHint)
        text += "\n| Qt::WindowTitleHint";
    if (flags & Qt::WindowSystemMenuHint)
        text += "\n| Qt::WindowSystemMenuHint";
    if (flags & Qt::WindowMinimizeButtonHint)
        text += "\n| Qt::WindowMinimizeButtonHint";
    if (flags & Qt::WindowMaximizeButtonHint)
        text += "\n| Qt::WindowMaximizeButtonHint";
    if (flags & Qt::WindowCloseButtonHint)
        text += "\n| Qt::WindowCloseButtonHint";
    if (flags & Qt::WindowContextHelpButtonHint)
        text += "\n| Qt::WindowContextHelpButtonHint";
    if (flags & Qt::WindowShadeButtonHint)
        text += "\n| Qt::WindowShadeButtonHint";
    if (flags & Qt::WindowStaysOnTopHint)
        text += "\n| Qt::WindowStaysOnTopHint";
    if (flags & Qt::WindowStaysOnBottomHint)
        text += "\n| Qt::WindowStaysOnBottomHint";
    if (flags & Qt::CustomizeWindowHint)
        text += "\n| Qt::CustomizeWindowHint";

    textEdit->setPlainText(text);
}

在我们的setWindowFlags()函数的重新实现中,我们首先使用QWidget::setWindowFlags()函数设置小部件的标志。然后我们遍历可用的窗口标志,创建包含与flags参数匹配的标志名称的文本。最后,我们在小部件的文本编辑器中显示该文本。

示例项目 @ code.qt.io

© 2024 The Qt Company Ltd。本文档中的文档贡献受到各自所有者的版权保护。本提供的文档根据自由软件基金会发布的GNU自由Documentation License版本1.3的条款进行许可。Qt和在芬兰或世界范围内的相关标志是The Qt Company Ltd.的商标。所有其他商标均属于其各自所有者。