找回密码
 立即注册
收起左侧

23. KS05_01 对话框-走起

0
回复
204
查看
[复制链接]
累计签到:41 天
连续签到:1 天
来源: 原创 2019-8-31 19:04:26 显示全部楼层 |阅读模式

马上注册,查看详细内容!注册请先查看:注册须知

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖最后由 baizy77 于 2019-9-1 21:10 编辑

----------------------------------------------------------------
该文章原创于Qter开源社区(www.qter.org
作者: 女儿叫老白
转载请注明出处!
----------------------------------------------------------------


引言
----------------------------------------------------------------
在利用Qt开发界面应用程序时,使用对话框进行人机交互或者信息展示是最常用的手段之一。这也是我们选择Qt进行跨平台界面类应用程序开发的一个重要原因。本节,我们来讨论一下对话框外观设置相关的内容。

正文
----------------------------------------------------------------
本节课所讲的对话框,其实是对Qt封装的人机交互界面的统称。在Qt中,界面类的基类是QWidget[注1],我们常说的对话框(QDialog)、浮动窗(QDockWidget)、主窗口(QMainWindow)等都从它派生。如果从信息展示和人机交互的角度来讲,恐怕用的最多的是QDialog和QWidget。QDialog主要用来实现独立的对话框界面,而QWidget主要用来实现子控件或者子界面。比如我们可以提供几个的QWidget子控件分别放到QTabWidget的不同页面中用作多个配置页,或者把QWidget放到QDockWidget中作为属性窗使用。
现在来看一下QWidget的构造函数:
  1. QWidget::QWidget(QWidget *parent = nullptr, Qt::WindowFlags f = ...)
复制代码
在它的入口参数中,第一个参数是父窗体指针。如果指定了父窗体,那么在父窗体析构时会自动析构所有的子窗体。只要制定了父窗体,我们就不用关心子窗体的内存释放问题。同理,如果您的某个子控件A被设置成其他控件的parent,那么该控件A析构时会自动析构所有其他子控件。如果没有特殊需求,您无需保存所有其他控件的指针,Qt会替您保存,而且这些对象会被Qt自动析构,所以您无需担心内存泄漏问题。
第二个参数用来设置窗体的属性标志,它是Qt::WindowType的取值的集合。可以通过QWidget:: setWindowFlags()接口对QWidget的窗体属性进行设置。

我们来看一下Qt::WindowsFlags的定义:
  1. enum WindowType {
  2.     Widget = 0x00000000,
  3.     Window = 0x00000001,
  4.     Dialog = 0x00000002 | Window,
  5.     Sheet = 0x00000004 | Window,
  6.     Drawer = Sheet | Dialog,
  7.     Popup = 0x00000008 | Window,
  8.     ……
  9.    
  10.     WindowType_Mask = 0x000000ff,
  11.     MSWindowsFixedSizeDialogHint = 0x00000100,
  12.     MSWindowsOwnDC = 0x00000200,
  13.     BypassWindowManagerHint = 0x00000400,
  14.     X11BypassWindowManagerHint = BypassWindowManagerHint,
  15.     ……

  16.     CustomizeWindowHint = 0x02000000,
  17.     WindowStaysOnBottomHint = 0x04000000,
  18.     WindowCloseButtonHint = 0x08000000,
  19.     MacWindowToolBarButtonHint = 0x10000000,
  20.     BypassGraphicsProxyWidget = 0x20000000,
  21.     NoDropShadowWindowHint = 0x40000000,
  22.     WindowFullscreenButtonHint = 0x80000000
  23. };
  24. Q_DECLARE_FLAGS(WindowFlags, WindowType)
复制代码
其中CoverWindow = 0x00000040 |Window,之前的部分用来设置窗体的类型(比如,窗体是对话框还是tooltip小窗)。MSWindowsFixedSizeDialogHint以及之后的枚举值用来设置窗体的外观样式,比如是否含有菜单栏、是否含有最大化按钮等。如果您希望把您的对话框设置为无边框,可以调用:
setWindowFlags(Qt::FramelessWindowHint);
本节的示例程序借鉴了Qt的example中的项目windowFlags的思路,但是我们重新按照ui文件方式提供界面而非完全采用编码的方式实现。朋友们可以运行示例程序直观的感受一下这些枚举值的含义。
请注意,这是QWidget的接口,也就是说所有从QWidget派生的子类、孙子类、重孙子类等,都支持这个接口。
在示例程序中,我们先弹出一个控制窗体,然后又使用了一个预览窗体,通过在控制窗体中修改参数,在预览窗体中直接查看效果。代码中用到了Qt的信号槽机制,本节我们先简单介绍,在下一节我们将详细介绍它。请朋友们把关注点放在窗体的setWindowFlags()接口上,通过该接口我们可以修改窗体的外观。
    前面介绍了本节的核心内容。下面看一下本节的实现代码。先看控制窗口的实现,再看一下预览窗实现,最后看main()函数的实现。


控制窗:

代码清单05-01-01

ctrlwindow.h
  1. #pragma once

  2. #include <QWidget>

  3. #include "ui_ctrlwindow.h"
  4. #include "prevwindow.h"

  5. QT_BEGIN_NAMESPACE
  6. class QCheckBox;
  7. class QRadioButton;
  8. QT_END_NAMESPACE

  9. class CCtrlWindow : public QWidget
  10. {
  11.     Q_OBJECT

  12. public:
  13.     /// 构造函数
  14.     CCtrlWindow();

  15. private slots:
  16.     void slot_updatePreview();

  17. private:
  18.     void connectTypeGroupBox();
  19.     void connectHintsGroupBox();
  20.     QCheckBox *createCheckBox(const QString &text);
  21.     QRadioButton *createRadioButton(const QString &text);

  22. private:
  23. Ui::CControllerWindow ui;
  24. CPreviewWindow *m_pPreviewWindow;
  25. };
复制代码
代码清单05-01-01中:
    第13行,定义了控制窗类名为CCtrlWindow,它派生自QWidget。
    第22行,定义了槽函数slot_updatePreview(),当在控制窗中点击窗体的某个属性按钮后,Qt将自动调用该槽函数。
    第25、26行分别将不同属性按钮的信号绑定到对应的槽函数,一遍当单击某个按钮时,可以触发Qt的信号槽机制,从而让Qt调用对应的槽函数。
    第27、28行接口用来创建窗体的属性按钮。
    第32行定义了预览窗体对象。当控制窗体的某个属性按钮被点击时,将通过该对象指针设置预览窗体的属性,从而改变其外观。

代码清单05-01-02

ctrlwindow.cpp
  1. #include <QtWidgets>
  2. #include "ctrlwindow.h"

  3. CCtrlWindow::CCtrlWindow()
  4. {
  5.     ui.setupUi(this);
  6.     connectHintsGroupBox();
  7.     connectTypeGroupBox();

  8.     m_pPreviewWindow = new CPreviewWindow(this);

  9.     connect(ui.quitButton, SIGNAL(clicked()), qApp, SLOT(quit()));

  10.     setWindowTitle(tr("Window Flags"));
  11.     slot_updatePreview();
  12. }

  13. void CCtrlWindow::slot_updatePreview()
  14. {
  15.     Qt::WindowFlags flags = 0;

  16.     if (ui.windowRadioButton->isChecked()) {
  17.         flags = Qt::Window;
  18.     } else if (ui.dialogRadioButton->isChecked()) {
  19.         flags = Qt::Dialog;
  20.     } else if (ui.sheetRadioButton->isChecked()) {
  21.         flags = Qt::Sheet;
  22.     } else if (ui.drawerRadioButton->isChecked()) {
  23.         flags = Qt::Drawer;
  24.     } else if (ui.popupRadioButton->isChecked()) {
  25.         flags = Qt::Popup;
  26.     } else if (ui.toolRadioButton->isChecked()) {
  27.         flags = Qt::Tool;
  28.     } else if (ui.toolTipRadioButton->isChecked()) {
  29.         flags = Qt::ToolTip;
  30.     } else if (ui.splashScreenRadioButton->isChecked()) {
  31.         flags = Qt::SplashScreen;
  32.     }

  33.     if (ui.msWindowsFixedSizeDialogCheckBox->isChecked())
  34.         flags |= Qt::MSWindowsFixedSizeDialogHint;
  35.     if (ui.x11BypassWindowManagerCheckBox->isChecked())
  36.         flags |= Qt::X11BypassWindowManagerHint;
  37.     //......
  38.     if (ui.customizeWindowHintCheckBox->isChecked())
  39.         flags |= Qt::CustomizeWindowHint;

  40.     m_pPreviewWindow->setWindowFlags(flags);

  41.     QPoint pos = m_pPreviewWindow->pos();
  42.     if (pos.x() < 0)
  43.         pos.setX(0);
  44.     if (pos.y() < 0)
  45.         pos.setY(0);
  46.     m_pPreviewWindow->move(pos);
  47.     m_pPreviewWindow->show();
  48. }

  49. void CCtrlWindow::connectTypeGroupBox()
  50. {
  51.     connect(ui.windowRadioButton, SIGNAL(clicked()), this, SLOT(slot_updatePreview()));
  52.     connect(ui.dialogRadioButton, SIGNAL(clicked()), this, SLOT(slot_updatePreview()));
  53.     connect(ui.sheetRadioButton, SIGNAL(clicked()), this, SLOT(slot_updatePreview()));
  54.     connect(ui.drawerRadioButton, SIGNAL(clicked()), this, SLOT(slot_updatePreview()));
  55.     connect(ui.popupRadioButton, SIGNAL(clicked()), this, SLOT(slot_updatePreview()));
  56.     connect(ui.toolRadioButton, SIGNAL(clicked()), this, SLOT(slot_updatePreview()));
  57.     connect(ui.toolTipRadioButton, SIGNAL(clicked()), this, SLOT(slot_updatePreview()));
  58.     connect(ui.splashScreenRadioButton, SIGNAL(clicked()), this, SLOT(slot_updatePreview()));

  59.     ui.windowRadioButton->setChecked(true);

  60. }

  61. void CCtrlWindow::connectHintsGroupBox()
  62. {
  63.     connect(ui.msWindowsFixedSizeDialogCheckBox, SIGNAL(clicked()), this, SLOT(slot_updatePreview()));
  64.    //......
  65.     connect(ui.customizeWindowHintCheckBox, SIGNAL(clicked()), this, SLOT(slot_updatePreview()));

  66. }
复制代码
    代码清单05-01-02中:
    我们给出了CCtrlWindows的实现。篇幅所限,我们省略了部分代码。
    重点说一下第18行的slot_updatePreview()。当控制窗中任何一个属性按钮被点击时,都将触发该槽函数的调用。在第22~46行,通过判断属性按钮的点击(选中)清况为flags变量赋值,并且在第48行通过setWindowFlags()设置预览窗对象的标志,从而达到预览效果。
    第82行的connectHintsGroupBox()用来实现属性按钮的信号到槽函数的绑定(也可以称为连接)。方法是使用connect宏。比如第84行中,将ui.msWindowsFixedSizeDialogCheckBox的clicked()信号绑定到this(控制窗体自身)的槽函数slot_updatePreview()。本节我们只对信号槽绑定做简单介绍,下一章节我们再为读者详细介绍。

预览窗:
代码清单05-01-03

prewindow.h
  1. #pragma once

  2. #include <QWidget>
  3. #include "ui_prevwindow.h"

  4. class CPreviewWindow : public QWidget
  5. {
  6.     Q_OBJECT
  7. public:
  8.     CPreviewWindow(QWidget *parent = 0);
  9.     void setWindowFlags(Qt::WindowFlags flags);
  10. private:
  11.     Ui::CPreviewWindow ui;
  12. };
复制代码
代码清单05-01-03中:
    提供了预览窗类CPreviewWindow的定义。该类比较简单,主要是定义了setWindowFlags()接口,用来设置窗体标志并将标志以文字方式展示出来。
代码清单05-01-04

prewindow.cpp
  1. #include <QtWidgets>

  2. #include "prevwindow.h"

  3. CPreviewWindow::CPreviewWindow(QWidget *parent)
  4.     : QWidget(parent)
  5. {
  6.     ui.setupUi(this);
  7.     connect(ui.closeButton, SIGNAL(clicked()), this, SLOT(close()));
  8.     setWindowTitle(QString::fromLocal8Bit("预览窗体"));
  9. }
  10. void CPreviewWindow::setWindowFlags(Qt::WindowFlags flags)
  11. {
  12.     QWidget::setWindowFlags(flags);
  13.     QString text;
  14.     Qt::WindowFlags type = (flags & Qt::WindowType_Mask);
  15.     if (type == Qt::Window) {
  16.         text = "Qt::Window";
  17. }
  18. //......
  19. else if (type == Qt::SplashScreen) {
  20.         text = "Qt::SplashScreen";
  21.     }
  22.     if (flags & Qt::MSWindowsFixedSizeDialogHint)
  23.         text += "\n| Qt::MSWindowsFixedSizeDialogHint";
  24.     //......
  25.     if (flags & Qt::CustomizeWindowHint)
  26.         text += "\n| Qt::CustomizeWindowHint";

  27.     ui.textEdit->setPlainText(text);
  28. }
复制代码
代码清单05-01-04中:
    提供了预览窗类CPreviewWindow的实现。在CPreviewWindow::setWindowFlags()接口的实现中,除了在第14行调用父类接口设置窗体标志以外,第15~30行的代码都是用来根据设置的窗体标志来组织一个文本并展示在预览窗中。

main()函数
    最后看一下main()函数。
代码清单05-01-05

main.cpp
  1. #include <QApplication>

  2. #include "ctrlwindow.h"

  3. int main(int argc, char * argv[])
  4. {
  5.     QApplication app(argc, argv);
  6.         CCtrlWindow ctrlWindow;
  7.         ctrlWindow.show();
  8.     return app.exec();
  9. }
复制代码
    代码清单05-01-05中,
    在第8~9行定义了一个CCtrlWindow对象,并将其显示出来。

结语
----------------------------------------------------------------
本节的核心内容不算多,主要介绍了QWidget::setWindowFlags()接口的使用并通过示例程序为朋友们展示了该接口的执行效果。本节课内容算是一个引子,后面我们讲介绍界面编程中的常用控件。需要注意的是,如果一个QWidget对象在构建时指定了父对象,那么在父对象析构时会自动析构(delete)其子对象,所以该QWdiget对象必须建立在堆上而非栈上,否则会导致异常(因为不能对栈上的对象地址执行delete操作)。


注解
----------------------------------------------------------------
注1: QWdiget从QObject 和QPaintDevice派生而来。因为本节主要讨论对话框,所以忽略了这两个类。


----------------------------------------------------------------
课程目录: 《Qt入门与提高-GUI产品开发》目录


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册