• Qt 停靠布局QDockWidget使用


    基本使用

    QDockWidget是一个可以停靠在QMainWindow内的窗口控件,它可以保持浮动状态或在指定位置作为子窗口附加到主窗口中。停靠窗口QDockWidget类是应用程序中经常用到的,设置停靠窗口的一般流程如下。

    • 创建一个QDockWidget对象的停靠窗体。
    • 设置此停靠窗体的属性,通常调用setFeatures()及setAllowedAreas()两种方法。
    • 新建一个要插入停靠窗体的控件,常用的有QListWidget和QTextEdit。
    • 将控件插入停靠窗体,调用QDockWidget的setWidget()方法。
    • 使用addDockWidget()方法在MainWindow中加入此停靠窗体。

    这里使用Qt Designer ,随意拖拽添加QDockWidget到MainWindow任意区域可以停靠即可,在代码中对QDockWidget进行管理。Qt Designer无法任意调整位置,可以使用代码初始化QDockWidget布局。
    在这里插入图片描述

    布局相关

    停靠特性

    setFeatures()方法设置停靠窗体的特性,参数QDockWidget::DockWidgetFeatures指定停靠窗体的特性,包括以下几种参数。
    ① QDockWidget::DockWidgetClosable:停靠窗体可关闭。
    ② QDockWidget::DockWidgetMovable:停靠窗体可移动。
    ③ QDockWidget::DockWidgetFloatable:停靠窗体可浮动。
    ④ QDockWidget::AllDockWidgetFeatures:此参数表示拥有停靠窗体的所有特性。
    ⑤ QDockWidget::NoDockWidgetFeatures:不可移动、不可关闭、不可浮动。

    停靠区域

    setAllowedAreas()方法设置停靠窗体可停靠的区域,参数Qt::DockWidgetAreas指定了停靠窗体可停靠的区域,包括以下几种参数。
    ① Qt::LeftDockWidgetArea:可在主窗口的左侧停靠。
    ② Qt::RightDockWidgetArea:可在主窗口的右侧停靠。
    ③ Qt::TopDockWidgetArea:可在主窗口的顶端停靠。
    ④ Qt::BottomDockWidgetArea:可在主窗口的底部停靠。
    ⑤ Qt::AllDockWidgetArea:可在主窗口任意(以上四个)部位停靠。
    ⑥ Qt::NoDockWidgetArea:只可停靠在插入处。

    添加dock

    addDockWidget()方法用于添加dock,给dock指定位置,同时也可以更改dock的位置。

    void QMainWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget * dockwidget)
    
    • 1

    分割dock

    splitDockWidget()方法用于分割dock窗口,是把两个dock进行左右或上下并排布置,做成一个类似QSplit的功能,分割原则是:水平从左到右,竖直从上到下。

    void QMainWindow::splitDockWidget(QDockWidget * first, QDockWidget * second, Qt::Orientation orientation)
    
    • 1

    dock tab化窗口

    tabifyDockWidget()方法用于tab化窗口,把多个dock变成一个tab形式的窗体。

    void QMainWindow::tabifyDockWidget(QDockWidget * first, QDockWidget * second)
    
    • 1

    初始化大小

    靠左右布局的QDockWidget的高度是自适应的,宽度需要初始化设置,同理靠上下布局的高度需要初始化设置。使用splitDockWidget分割、tabifyDockWidget tab化窗口的QDockWidget的初始化大小与其依赖的QDockWidget(参数first)初始化大小一样。

    void QMainWindow::resizeDocks(const QList<QDockWidget *> &docks, const QList<int> &sizes, Qt::Orientation orientation)
    
    • 1

    它的第一个参数是用来配置是哪个dock窗口需要调整大小;第二个参数是用来配置dock所占的像素大小,如果配置大于或者小于QMainWindow本身空间,Qt会根据所配置的像素大小的相对权重分配到dock中;第三个参数用来配置调整的方向,如果为Qt::Horizontal,调整dock宽度,Qt::Vertical调整dock高度,确定了停靠位置后resizeDocks才起作用。需要注意的是Qt官方文档上有注明这个方法在Qt5.6中引入,所以比Qt5.6低的版本并不能使用本方法。resizeDocks在多行或多列时初始化高宽无效问题!

    使用setFeatures、setAllowedAreas、addDockWidget、splitDockWidget、tabifyDockWidget、resizeDocks可以满足基本的Dock布局了。

    标题栏设置

    去掉标题栏,但是不能拖动了。

    QWidget *Widget = new QWidget;
    ui->dockwidget_dockWidget_1->setTitleBarWidget(Widget);
    
    • 1
    • 2

    自定义QWidget即可以自定义标题栏。

    标题栏竖起

    ui->dockwidget_dockWidget_6->setFeatures(QDockWidget::DockWidgetVerticalTitleBar);
    
    • 1

    其他问题

    当使用tabifyDockWidgets进行tab化窗口时对Tabbar设置背景色时发现Tabbar上方有一行间隙。在QSS中使用qproperty-drawBase: 0;可以使得背景色填充到间隙中,但是发现Tabbar超过两个后其他的Tabbar不生效!!!

    QTabBar {
      qproperty-drawBase: 0;
      background: rgb(45, 45, 45);
    } 
    
    • 1
    • 2
    • 3
    • 4

    直接在代码中遍历Tabbar设置DrawBase属性可以解决Tabbar上方有一行间隙无法填充背景色问题。

    Q_FOREACH (QTabBar *bar, this->findChildren<QTabBar *>())
    {
      bar->setDrawBase(false);
    }
    
    • 1
    • 2
    • 3
    • 4

    当tab个数大于一定个数时,会出现如下图左侧白线所示,这个是Qt自带的,作用是点击回到第一个tab,但是显示不好看,这里隐藏掉

    QTabBar::tear {
      width: 0px;
      border: none;
    }
    
    • 1
    • 2
    • 3
    • 4

    后续

    • 自定义标题栏。
    • 补充还原布局。

    效果

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    相关代码

    #include "dockwidget.h"
    #include 
    
    DockWidget::DockWidget(QWidget *parent) : QMainWindow(parent), ui(new Ui_dockwidget)
    {
      ui->setupUi(this);
      StyleMgr::SetStyleToWidgetByCssFile(this, ":/helloqt/resources/qss/custom/dockwidget.qss");
    
      // 如果不需要MainWindow的中间窗口,整个视图都由QDockWidget组成,可以把QMainWindow的中间窗口部件去除
      // QWidget *p = takeCentralWidget();
      // if (p)
      //   delete p;
    
      // 当不需要MainWindow的中间窗口时,发现不能拖动QDockWidget到中间,需要设置
      // setDockNestingEnabled(true);
    
      ui->dockwidget_dockWidget_1->setWindowTitle("Dock 1");
      ui->dockwidget_dockWidget_2->setWindowTitle("Dock 2");
      ui->dockwidget_dockWidget_3->setWindowTitle("Dock 3");
      ui->dockwidget_dockWidget_4->setWindowTitle("Dock 4");
      ui->dockwidget_dockWidget_5->setWindowTitle("Dock 5");
      ui->dockwidget_dockWidget_6->setWindowTitle("Dock 6");
      ui->dockwidget_dockWidget_7->setWindowTitle("Dock 7");
      ui->dockwidget_dockWidget_8->setWindowTitle("Dock 8");
      ui->dockwidget_dockWidget_9->setWindowTitle("Dock 9");
    
      ui->dockwidget_dockWidget_1->setFeatures(QDockWidget::DockWidgetMovable);                                   //可移动
      ui->dockwidget_dockWidget_2->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); //可关闭、移动
      ui->dockwidget_dockWidget_3->setFeatures(QDockWidget::DockWidgetMovable);                                   //可移动
      ui->dockwidget_dockWidget_4->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); //可关闭、移动
      ui->dockwidget_dockWidget_5->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); //可关闭、移动
      ui->dockwidget_dockWidget_6->setFeatures(QDockWidget::DockWidgetMovable);                                   //可移动
      ui->dockwidget_dockWidget_7->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); //可关闭、移动
      ui->dockwidget_dockWidget_8->setFeatures(QDockWidget::DockWidgetMovable);                                   //可移动
      ui->dockwidget_dockWidget_9->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); //可关闭、移动
    
      ui->dockwidget_dockWidget_1->setAllowedAreas(Qt::TopDockWidgetArea);    //可在主窗口的上侧停靠。
      ui->dockwidget_dockWidget_3->setAllowedAreas(Qt::LeftDockWidgetArea);   //可在主窗口的左侧停靠。
      ui->dockwidget_dockWidget_6->setAllowedAreas(Qt::RightDockWidgetArea);  //可在主窗口的右侧停靠。
      ui->dockwidget_dockWidget_8->setAllowedAreas(Qt::BottomDockWidgetArea); //可在主窗口的下侧停靠。
    
      // 去掉标题栏
      // QWidget *Widget = new QWidget;
      // ui->dockwidget_dockWidget_1->setTitleBarWidget(Widget);
    
      // 把QDockWidget标题栏竖起来
      // ui->dockwidget_dockWidget_6->setFeatures(QDockWidget::DockWidgetVerticalTitleBar);
    
      m_docks.append(ui->dockwidget_dockWidget_1);
      m_docks.append(ui->dockwidget_dockWidget_2);
      m_docks.append(ui->dockwidget_dockWidget_3);
      m_docks.append(ui->dockwidget_dockWidget_4);
      m_docks.append(ui->dockwidget_dockWidget_5);
      m_docks.append(ui->dockwidget_dockWidget_6);
      m_docks.append(ui->dockwidget_dockWidget_7);
      m_docks.append(ui->dockwidget_dockWidget_8);
      m_docks.append(ui->dockwidget_dockWidget_9);
    }
    
    DockWidget::~DockWidget()
    {
      delete ui;
    }
    
    void DockWidget::ShowDockLayout(int type)
    {
      RemoveAllDock();
      switch (type)
      {
      case 1:
        addDockWidget(Qt::TopDockWidgetArea, ui->dockwidget_dockWidget_1);
        addDockWidget(Qt::LeftDockWidgetArea, ui->dockwidget_dockWidget_3);
        addDockWidget(Qt::RightDockWidgetArea, ui->dockwidget_dockWidget_6);
        addDockWidget(Qt::BottomDockWidgetArea, ui->dockwidget_dockWidget_8);
        resizeDocks({ui->dockwidget_dockWidget_1, ui->dockwidget_dockWidget_8}, {200, 100}, Qt::Vertical);   //上下
        resizeDocks({ui->dockwidget_dockWidget_3, ui->dockwidget_dockWidget_6}, {300, 300}, Qt::Horizontal); //左右
        ShowDock(QList<int>() << 0 << 2 << 5 << 7);
        break;
      case 2:
        addDockWidget(Qt::TopDockWidgetArea, ui->dockwidget_dockWidget_1);
        addDockWidget(Qt::LeftDockWidgetArea, ui->dockwidget_dockWidget_3);
        addDockWidget(Qt::RightDockWidgetArea, ui->dockwidget_dockWidget_6);
        addDockWidget(Qt::BottomDockWidgetArea, ui->dockwidget_dockWidget_8);
    
        resizeDocks({ui->dockwidget_dockWidget_1, ui->dockwidget_dockWidget_8}, {200, 100}, Qt::Vertical);   //上下,初始化高度
        resizeDocks({ui->dockwidget_dockWidget_3, ui->dockwidget_dockWidget_6}, {300, 300}, Qt::Horizontal); //左右,初始化宽度
    
        resizeDocks({ui->dockwidget_dockWidget_1}, {200}, Qt::Horizontal);
        splitDockWidget(ui->dockwidget_dockWidget_1, ui->dockwidget_dockWidget_2, Qt::Horizontal);
    
        resizeDocks({ui->dockwidget_dockWidget_3}, {50}, Qt::Vertical);
        splitDockWidget(ui->dockwidget_dockWidget_3, ui->dockwidget_dockWidget_4, Qt::Vertical);
        resizeDocks({ui->dockwidget_dockWidget_4}, {100}, Qt::Vertical);
        splitDockWidget(ui->dockwidget_dockWidget_4, ui->dockwidget_dockWidget_5, Qt::Vertical);
    
        resizeDocks({ui->dockwidget_dockWidget_6}, {150}, Qt::Vertical);
        splitDockWidget(ui->dockwidget_dockWidget_6, ui->dockwidget_dockWidget_7, Qt::Vertical);
    
        resizeDocks({ui->dockwidget_dockWidget_8}, {300}, Qt::Horizontal);
        splitDockWidget(ui->dockwidget_dockWidget_8, ui->dockwidget_dockWidget_9, Qt::Horizontal);
    
        ShowDock(QList<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8);
        break;
      case 3:
        addDockWidget(Qt::TopDockWidgetArea, ui->dockwidget_dockWidget_1);
        tabifyDockWidget(ui->dockwidget_dockWidget_1, ui->dockwidget_dockWidget_2);
    
        addDockWidget(Qt::LeftDockWidgetArea, ui->dockwidget_dockWidget_3);
        tabifyDockWidget(ui->dockwidget_dockWidget_3, ui->dockwidget_dockWidget_4);
        tabifyDockWidget(ui->dockwidget_dockWidget_4, ui->dockwidget_dockWidget_5);
    
        addDockWidget(Qt::RightDockWidgetArea, ui->dockwidget_dockWidget_6);
        tabifyDockWidget(ui->dockwidget_dockWidget_6, ui->dockwidget_dockWidget_7);
    
        addDockWidget(Qt::BottomDockWidgetArea, ui->dockwidget_dockWidget_8);
        tabifyDockWidget(ui->dockwidget_dockWidget_8, ui->dockwidget_dockWidget_9);
    
        resizeDocks({ui->dockwidget_dockWidget_1, ui->dockwidget_dockWidget_8}, {200, 100}, Qt::Vertical);   //上下
        resizeDocks({ui->dockwidget_dockWidget_3, ui->dockwidget_dockWidget_6}, {300, 300}, Qt::Horizontal); //左右
        ShowDock(QList<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8);
        break;
      default:
        break;
      }
    
      // 解决Tabbar上方有一行间隙无法填充背景色问题
      Q_FOREACH (QTabBar *bar, this->findChildren<QTabBar *>())
      {
        bar->setDrawBase(false);
      }
    }
    
    /// @brief 移除并隐藏所有的dock
    void DockWidget::RemoveAllDock()
    {
      for (int i = 0; i < 9; ++i)
      {
        removeDockWidget(m_docks[i]);
      }
    }
    
    /// @brief 显示指定序号的dock
    /// @param index 指定序号,如果不指定,则会显示所有
    void DockWidget::ShowDock(const QList<int> &index)
    {
      if (index.isEmpty())
      {
        for (int i = 0; i < 9; ++i)
        {
          m_docks[i]->show();
        }
      }
      else
      {
        foreach (int i, index)
        {
          m_docks[i]->show();
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    QWidget#dockwidget {
      border: none;
      background-color: rgb(30, 30, 30);
    }
    
    QDockWidget#dockwidget_dockWidget_1,
    #dockwidget_dockWidgetContents_1 {
      border: none;
      background-color: rgb(189, 79, 60);
    }
    
    QDockWidget#dockwidget_dockWidget_2,
    #dockwidget_dockWidgetContents_2 {
      border: none;
      background-color: rgb(189, 133, 60);
    }
    
    QDockWidget#dockwidget_dockWidget_3,
    #dockwidget_dockWidgetContents_3 {
      border: none;
      background-color: rgb(161, 189, 60);
    }
    
    QDockWidget#dockwidget_dockWidget_4,
    #dockwidget_dockWidgetContents_4 {
      border: none;
      background-color: rgb(60, 189, 109);
    }
    
    QDockWidget#dockwidget_dockWidget_5,
    #dockwidget_dockWidgetContents_5 {
      border: none;
      background-color: rgb(60, 152, 189);
    }
    
    QDockWidget#dockwidget_dockWidget_6,
    #dockwidget_dockWidgetContents_6 {
      border: none;
      background-color: rgb(62, 60, 189);
    }
    
    QDockWidget#dockwidget_dockWidget_7,
    #dockwidget_dockWidgetContents_7 {
      border: none;
      background-color: rgb(105, 60, 189);
    }
    
    QDockWidget#dockwidget_dockWidget_8,
    #dockwidget_dockWidgetContents_8 {
      border: none;
      background-color: rgb(178, 60, 189);
    }
    
    QDockWidget#dockwidget_dockWidget_9,
    #dockwidget_dockWidgetContents_9 {
      border: none;
      background-color: rgb(189, 60, 120);
    }
    
    
    /* QDockWidget之间的间距调整 */
    QMainWindow::separator {
      width: 0px;
      height: 0px;
      margin: 0px;
      /* 如果将两个QDockWidget之间的间距调为0后,QDockWidget将没办法拉拽,所以留出一点小缝隙 */
      padding: 0, 1px;
    }
    
    QDockWidget {
      color: white;
    }
    
    /* 标题设置 */
    QDockWidget::title {
      /* 标题字体在这里设置无效,要在QDockWidget中设置! */
      /* color: white; */
      text-align: left;
      /* 不设置背景色时标题栏下方有空隙! */
      background: rgb(45, 45, 48);
      padding-left: 5px;
    }
    
    /* 按钮图标 */
    QDockWidget {
      border: 1px solid rgb(45, 45, 48);
      titlebar-close-icon: url(:/dockwidget/resources/image/dockwidget/close.png);
      titlebar-normal-icon: url(:/dockwidget/resources/image/dockwidget/normal.png);
    }
    
    QDockWidget::close-button,
    QDockWidget::float-button {
      /* 不设置border则hover、pressed时的background颜色不生效! */
      border: 1px solid transparent;
      background: rgb(45, 45, 48);
      /* 会影响标题栏高度 */
      /* padding: 0px; */
      /* 不起作用! */
      icon-size: 32px;
    }
    
    QDockWidget::close-button:hover,
    QDockWidget::float-button:hover {
      color: rgb(0, 151, 251);
      background: rgb(62, 62, 64);
    }
    
    QDockWidget::close-button:pressed,
    QDockWidget::float-button:pressed {
      background: rgb(37, 37, 38);
      padding: 1px -1px -1px 1px;
    }
    
    
    /* tabifyDockWidgets TabBar设置*/
    QTabBar {
      qproperty-drawBase: 0;
      background: rgb(45, 45, 48);
    }
    
    QTabBar::tab {
      color: white;
      background: rgb(45, 45, 48);
      padding: 4px;
      border: 1px solid transparent;
    }
    
    QTabBar::tab:hover {
      color: rgb(0, 151, 251);
    }
    
    QTabBar::tab:selected {
      color: rgb(0, 151, 251);
      background: rgb(37, 37, 38);
      border-bottom-color: rgb(0, 151, 251);
    }
    
    /* 当tab个数大于一定个数时,会出现如下图左侧白线所示,这个是Qt自带的,作用是点击回到第一个tab,但是显示不好看,这里隐藏掉 */
    QTabBar::tear {
      width: 0px;
      border: none;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142

    github

    https://github.com/weichangk/helloqt

    参考:

    https://doc.qt.io/qt-5/qdockwidget.html
    https://doc.qt.io/qt-5/qmainwindow.html#resizeDocks
    https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qdockwidget
    https://github.com/czyt1988/czyBlog/tree/master/tech/QDockWidget_VSStudioMode
    https://zhuanlan.zhihu.com/p/381444869
    https://forum.qt.io/topic/88409/stylesheet-and-qtabbar-which-element-is-this

  • 相关阅读:
    kafka的安装、部署及应用
    Axios
    家庭的破碎与人性的考验
    GoLang核心知识点
    大数据有何优缺点
    系列三、创建线程的方式
    c语言:汽车时代
    五年数据库专家,带你深入高性能 MySQL 架构系统,不要等到面试再追悔莫及
    [附源码]Python计算机毕业设计Django学生疫情防控信息填报系统
    什么是泛型编程和模板技术?C语言中如何实现泛型编程?
  • 原文地址:https://blog.csdn.net/qq_39827640/article/details/127927467