QSplitter(分离器或分隔符)
QSplitter(分离器或分隔符)
若对C++语法不熟悉,建议参阅《C++语法详解》一书,电子工业出版社出版
5.4.1 QSplitter类(分离器)
QSplitter类继承自QFrame类,也就是说该类是一个带有边框的可视部件。QSplitter类实现了分离器,分离器用于分离两个部件(原理见图5-39),用户可通过拖动部件之间的分界线来调整子部件的大小。
QSplitter的实现原理(见图5-39)与QBoxLayout布局的原理类似,即QSplitter把子部件以水平或垂直的方式添加到QSplitter中,只不过在这些子部件之间多了一条分界线,另外QSplitter继承自QFrmae,因此QSplitter是有边框的。而且它可以作为容器和窗口使用,但不推荐使用QPushButton pb(&splitter)的形式向QSplitter中添加子部件。
动态调整是指移动分界线时部件大小随之动态的改变。若不是动态调整的,则在移动分界线时部件大小不会改变,当完成分界线的移动时(即松开鼠标时),部件的大小才改变。
分离器中的子部件会随着分离器大小的改变而改变,当分离器大小改变时,分离器会重新分配空间,以使其所有子部件的相对大小保持相同的比例不变。
子部件的大小策略对分离器不起作用,分离器会把子部件填充满整个空间,即使子部件的大小策略设置为Fixed,仍会被拉伸。
1、分界线(QSplitterHandle类)
分界线是由QSplitterHandle类实现的,QSplitter类本身不实现分界线。因此QSplitterHandle是一个部件,而QSplitter是另一个部件,这是两个部件,只不过这两个部件通过Qt的内部设计让他们关联在一起,产生了一定的联系。
QSplitter的分界线默认有可能是看不见的(因为颜色与QSplitter背景色相同,所以看不见),因此要使分界线可见,需设置分界线的背景色。
分界线的索引从0开始编号,分界线的数量与子部件的数量一样多。但索引为0的分界线始终是隐藏的。对于垂直分离器,索引为0的子部件上方的分界线索引为0,对于水平分离器(对于从左到右的语言),则索引为0的子部件左侧的分界线的索引为0,对于从右到左的语言,则索引为0的子部件右侧的分界线的索引为0。
2、使用QSplitter的步骤如下:
①、创建一个QSplitter。
②、使用addWidget()函数把子部件添加到QSplitter中。
③、代码如下:
QSplitter *ps = new QSplitter; //创建分离器,默认为水平排列子部件。
QPushButton *pb, *pb1, *pb2; …..; //创建子部件
…… //初始化子部件。
ps->addwidget(pb); ps->addWidget(pb1); //把子部件添加到分离器ps中
……. //添加其他子部件
ps->setStyleSheet("QSplitter::handle{background-color: blue}"); //使用样式表把QSplitter分界线的背景色设置为某颜色(这样才能使分界线可见),这是一种快速设置所有分界线背景色的方法。
3、QSplitter类的属性
①、childrenCollapsible:bool 访问函数:bool childrenCollapsible() const; void setChildrenCollapsible(bool);
子部件是否可以被折叠(即调整到0的大小),默认为可折叠。
若部件可折叠,即使该部件的最小大小(minimumSize())不为零,也可将其大小调整为0。
设置该属性会作用于所有子部件上,使用函数setCollapsible()可设置单个子部件是否可折叠。
②、handleWidth:int 访问函数:int handleWidth() const; void setHandleWidth(int);
设置和获取分界线的宽度,默认取决于平台和样式。若设置为0或1,则实际抓取区域会增加到各自部件相重叠的几个像素。
③、opaqueResize:bool 访问函数:bool opaqueResize() const; void setOpaqueResize(bool opaque = true);
部件是否动态调整,即分界线是否是不透明的。默认为动态调整(即为true)。
④、orientation:Qt::Orientation 访问函数:Qt::Orientation orientation() const;void setOrientation(Qt::Orientation);
获取或设置QSplitter的方向,默认为水平方向,可取值为Qt::Horizontal和Qt::Vertical。
4、QSplitter类的函数
①、QSplitter(QWidget* parent = Q_NULLPTR); //构造函数
QSplitter(Qt::Orientation orientation , QWidget* parent = Q_NULLPTR);
②、void addWidget(QWidget* widget);
void insertWidget(int index, QWidget* widget);
把widget添加到末尾或插入到指定索引indix处,若widget已在分离器中,则将其移至新位置。注意:分离器会获得部件的所有权。
③、void setCollapsible(int index, bool collapse);
bool isCollapsible(int index) const;
设置或返回索引index处的子部件是否可折叠,要设置所有子部件都可折叠,请设置childrenCollapsible属性。
④、void setStretchFactor(int index, int stretch); //设置索引为index的子部件的拉伸因子。
⑤、void setSizes(const QList<int>& list);
QList<int> sizes() const;
使用列表list设置子部件的大小(以像素为单位),其规则为,若分离器是水平的,则使用列表中的值按从左到右的顺序设置每个子部件的宽度。若分离器是垂直的,则使用列表中的值按从上到下的顺序设置子部件的高度。若列表的值过少,则结果未定义,但程序仍能运行。
若为子部件指定的大小为0,则该子部件将不可见,若指定的值小于子部件的最小大小,则使用最小大小提示的值替换。
注意:分离器的总体大小不会受到影响,也就是说分离器的大小不会改变,仍是之前那么大,因此,当使用该函数为所有的子部件设置的值小于分离器总体的大小时,则其余多余的空间会根据设置的子部件大小的相对权重,在各子部件之间分配。
示例:假设分离器为垂直的,且高度为150,共有3个子部件,则
QList<int> q={30,30,30}; ps->setSizes(q);
设置之后各子部件的高度分别为50,50,50,而不是30,30,30,因为分离器剩余的空间按子部件大小的相对权重,分配给了各个子部件。
⑥、QByteArray saveState() const;
bool restoreState(const QByteArray& sate);
以上函数用于保存和恢复分离器的状态,通常应与QSettings类一起使用,也可单独使用,可以使用这两个函数来存储或恢复分离器的默认状态。
⑦、int count() const; //返回分离器中子部件的数量。
⑧、int indexOf(QWidget* widget) const; //返回widget在分离器中的索引,该函数也可用于分界线。
⑨、QWidget* widget(int index) const; //返回索引index处的子部件。
⑩、QWidget* replaceWidget(int index, QWidget* widget); //qt5.9
把索引index处的子部件替换为widget,若index有效,且widget不是分离器的子部件,则返回被替换掉的子部件,否则返回null,而不会进行替换,新插入的子部件会继承被替换的子部件的属性(比如大小、可折叠装态等)。注意:widget可能不会被立即设置,需在接收到适到的事件之后才会被设置。
⑪、void getRange(int index, int *min, int *max) const;
返回索引为index的分界线的有效范围,并存储在*min和*max中,即获取分界线可以调整大小的范围。
⑫、QSplitterHandle* handle(int index) const; //返回索引index处的分界线。
⑬、void refresh(); //更新分离器的状态,通常不需要调用这个函数。
⑭、QSplitterHandle* createHandle(); //虚拟的,受保护的。
把返回的分界线作为分离器的分界线,该函数可在子类中重新实现,以提供自定义的分界线。
⑮、void childEvent(QChildEvent* c); //虚拟的,受保护的。QObject::childEvent()的重新实现
当子部件已被插入或删除时产生该事件。
⑯、void moveSplitter(int pos, int index); //受保护的
把索引为index的分界线的左侧(或顶边),尽可能的移至位置pos处,pos是距离分离器左侧或顶边的距离。注:对于从右到左的语言,pos是距离分离器右侧的距离。
⑰、void setRubberBand(int pos); //受保护的
在位置pos处显示橡皮筋,若pos为负数,则移除橡
皮筋。当分离器不是动态调整,用户移动分界线时,看到
的那根线条就是橡皮筋,效果见图5-40。
⑱、void splitterMoved(int pos, int index); //信号
当索引为index的分界线移至位置pos时,发送此信号。
示例5.18:QSplitter类(分离器)的使用
#include<QtWidgets>
int main(int argc, char *argv[]){ QApplication a(argc,argv);
QWidget w; QSplitter *ps=new QSplitter(Qt::Vertical);
QPushButton *pb=new QPushButton("AAA"); QPushButton *pb1=new QPushButton("BBB");
QPushButton *pb2=new QPushButton("CCC");
QPushButton *pb3=new QPushButton("DDD",ps); //不推荐此种方式向分离器中添加子部件
pb1->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); //大小策略不起作用
pb->setMaximumSize(175,55); //最大最小值仍可限制子部件
ps->addWidget(pb); ps->addWidget(pb1); ps->addWidget(pb2);
ps->addWidget(pb3); //pb3已在分离器ps中,因此把pb3移至新位置
//设置分离器的边框
ps->setFrameShadow(QFrame::Raised); ps->setFrameShape(QFrame::Box); ps->setLineWidth(5);
ps->setChildrenCollapsible(0); //子部件不可折叠
ps->setOpaqueResize(1); //动态调整子部件
ps->setStretchFactor(0,1); //设置拉伸因子,以调整初始显示时各子部件间的位置
ps->setStretchFactor(1,2); ps->setStretchFactor(2,2);
ps->setHandleWidth(5); //设置分界线的宽度
//设置分界线的背景色为蓝色,若不设置背景色,则分界线可能会不可见
ps->setStyleSheet("QSplitter::handle{background-color: blue}"); //样式表见第13章
QVBoxLayout *pv=new QVBoxLayout; pv->addWidget(ps); //把分离器ps添加到布局pv中
w.setLayout(pv); w.resize(300,200); w.show(); return a.exec(); }
运行结果及说明见图5-41
示例5.19:QSplitter综合示例(设计的界要和要求见图5-42)
//m.h文件的内容
#ifndef M_H
#define M_H
#include<QtWidgets>
#include <iostream>
using namespace std;
class B:public QSplitter{ Q_OBJECT
public: static QString ddd; //用于存储来自行编辑器输入的文本
B(Qt::Orientation o=Qt::Horizontal,QWidget* p=0):QSplitter(o,p){}
public slots:
void f(){ int j=0; //计数器
int i[3]={0}; //用于存储从文本ddd中提出取来的数字。
QString s1=ddd;
while(!s1.isEmpty()){ s1=ddd.section(",",j,j); //提取文本中以","分隔的数字
if(j>2)return; //如果文本中的数字个数大于等于3个,则退出函数。
i[j]=s1.toInt(); //把提取出来的数字赋值给数组i。
j++;}
moveSplitter(i[0],i[1]); } }; /*使用文本中输入的前两个数字设置分界线的位置。注意:这个函数是受保护的,若索引(第二个参数)超出范围,该函数会产生错误。*/
class C:public QWidget{ Q_OBJECT
public: B *ps; //分离器
QLineEdit *pe,*pe1; QLabel *pl,*pl1; QPushButton *pb,*pb1,*pb2,*pb3,*pb4,*pb5;
QByteArray b; //用于保存和恢复分离器的状态
C(QWidget* p=0):QWidget(p){ //构造函数
ps=new B(Qt::Vertical); pl=new QLabel("EnterSize"); pl1=new QLabel("pose,index");
pe=new QLineEdit; pe1=new QLineEdit;
pe->setClearButtonEnabled(1); pe1->setClearButtonEnabled(1);//显示行编辑器的清除按钮
pb=new QPushButton("AAA"); pb1=new QPushButton("BBB"); pb2=new QPushButton("CCC");
pb3=new QPushButton("save"); pb4=new QPushButton("restore");
//布置分离器ps的子部件。
ps->addWidget(pb); ps->addWidget(pb1); ps->addWidget(pb2);
//设置分离器的边框及其他属性
ps->setFrameShadow(QFrame::Raised); ps->setFrameShape(QFrame::Box);
ps->setLineWidth(5); ps->setChildrenCollapsible(0);
//设置分离器的边框样式后,重新设置分离器的大小策略,否则分离器可能不会扩展。
ps->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
//设置分界线的背景色为蓝色,若不设置背景色,则分界线可能会不可见
ps->setHandleWidth(5); ps->setStyleSheet("QSplitter::handle{background-color: blue}");
//布置主窗口的子部件
QVBoxLayout *pv=new QVBoxLayout; //主窗口使用的主布局
QFormLayout *pf=new QFormLayout; pf->addRow(pl,pe); pf->addRow(pl1,pe1);
QHBoxLayout *ph=new QHBoxLayout; ph->addWidget(pb3); ph->addWidget(pb4);
//把布局pf,ph和分离器,添加到主布局中
pv->addLayout(ph); pv->addLayout(pf); pv->addWidget(ps); setLayout(pv);
QObject::connect(pe, &QLineEdit::returnPressed, this, &C::fSet);
QObject::connect(pb3, &QPushButton::clicked, this, &C::fSave);
QObject::connect(pb4, &QPushButton::clicked, this, &C::fRestore);
QObject::connect(pe1, &QLineEdit::returnPressed, this, &C::f);
QObject::connect(pe1, &QLineEdit::returnPressed, ps, &B::f); } //构造函数结束
public slots:
void fSet(){ //使用setSizes函数设置分离器各子部件的大小
QString ss=pe->text(); //获取行编辑器pe的文本
int j=0; //计数器
QString s1=ss;
QList<int> b; //使用列表存储从文本ss中提取出来的数字
while(!s1.isEmpty()){
s1=ss.section(",",j,j); //提取以","分隔的数字。
b.append(s1.toInt()); //把提取出来的数字追到加列表b的末尾。
j++; }
if(b.size()<=3) return; //如果输入的数字个数少于3个,则退出函数。
ps->setSizes(b); } //使用列表b设置各分离器子部件的位置
void fSave(){ b=ps->saveState(); cout<<"save OK"<<endl;} //保存分离器的状态。
void fRestore(){ ps->restoreState(b); } //恢复分离器的状态
void f(){ B::ddd=pe1->text(); }}; //把行编辑器pe1输入的内容存储到静态变量B::ddd中
#endif // M_H
#include "m.h"
QString B::ddd; //初始化静态变量,注意:静态变量不应在头文件中初始化,否则会出现重定义错误。
int main(int argc, char *argv[]){ QApplication a(argc,argv);
C w; w.resize(300,500); w.show(); return a.exec(); }
运行结果及说明见图5-42
示例5.20:QSplitter的嵌套使用(实现的界面如图5-43所示)
#include
int main(int argc, char *argv[]){
QApplication a(argc,argv);
QWidget w;
QSplitter *ps=new QSplitter(Qt::Vertical);
QSplitter *ps1=new QSplitter(Qt::Vertical);
QSplitter *ps2=new QSplitter(Qt::Horizontal);
QTextEdit *pt=new QTextEdit;
QTextEdit *pt1=new QTextEdit;
QTextEdit *pt2=new QTextEdit;
//设置分界线的背景色及边框
ps->setHandleWidth(1); ps->setStyleSheet("QSplitter::handle{background-color: blue}");
ps2->setFrameShadow(QFrame::Raised); ps2->setFrameShape(QFrame::Box);
ps2->setLineWidth(2); ps2->setHandleWidth(5);
ps2->setStyleSheet("QSplitter::handle{background-color: red}");
//布局子部件
ps->addWidget(pt); ps->addWidget(pt1); ps1->addWidget(pt2);
ps2->addWidget(ps1); ps2->addWidget(ps);
QHBoxLayout *ph=new QHBoxLayout; ph->addWidget(ps2); w.setLayout(ph);
w.resize(300,200); w.show(); return a.exec(); }
5.4.2 QSplitterHandle类(分界线)
QSplitterHandle类继承自QWidget,该类主要用于实现QSplitter(分离器)的分界线,因此,通常与分离器一起使用。该类的规则请参阅QSplitter类。
QSplitterHandle类比较简单,只有如下几个公有函数
QSplitterHandle(Qt::Orientation orientation, QSplitter* parent); //构造函数
QSplitter* splitter() const; //获取与此分界线关联的分离器
void setOrientation(Qt::Orientation orientation); //设置分界线的方向
Qt::Orientation orientation() const; //返回分界线的方向
bool opaqueResize() const; //设置是否不透明(动态调整),该值由QSplitter控制。
该类需结合QSplitter的如下两个函数使用,
QSplitterHandle* QSplitter::handle(int index) const; //通过该函数可间接的修改分界线。
QSplitterHandle* QSplitter::createHandle(); //虚拟的,受保护的。重写此函数可实现自定义的分界线。
示例5.21:QSplitterHandle(分界线)的使用
//m.h文件的内容
#ifndef M_H
#define M_H
#include<QtWidgets>
class B:public QSplitter{ Q_OBJECT
public: B(Qt::Orientation o=Qt::Horizontal,QWidget* p=0):QSplitter(o,p){}
public slots:
QSplitterHandle *createHandle(){ //重新实现该虚函数,创建自定义的分界线
QSplitterHandle *ph=new QSplitterHandle(orientation(),this); //自定义的分界线
ph->setMinimumSize(22,22); //设计分界线的最小大小
return ph; }}; //返回自定义的分界线
#endif // M_H
#include "m.h"
int main(int argc, char *argv[]){ QApplication a(argc,argv);
QWidget w; B *ps=new B(Qt::Vertical);
QPushButton *pb=new QPushButton("AAA"); QPushButton *pb1=new QPushButton("BBB");
QPushButton *pb2=new QPushButton("CCC");
ps->addWidget(pb); ps->addWidget(pb1); ps->addWidget(pb2);
ps->setStyleSheet("QSplitter::handle{background-color: red}"); //设置分界线的颜色(红色)
QHBoxLayout *ph=new QHBoxLayout; ph->addWidget(ps); w.setLayout(ph);
w.resize(300,200); w.show(); return a.exec(); }
运行结果及说明见图5-44