在主流的一些MVC框架中都提供有网站布局功能,如Symfony、CodeIgniter、CakePHP等,这些框架默认的模版方案已经启用了布局功能。使用布局能够有效地提高模版的开发速度,并且由于布局的限制,网站中的各页面能够保持高效的统一,方便后期维护。一个经典的网站布局如图所示
如图所示,这是一个经典的网站布局图,如果网站页面内容量大,栏目多,那么布局就会更加复杂。如果不使用布局功能,那么要保持整个网站想通的布局形式,就必须在每个模版中使用相同的代码,这无疑会极大地增加开发成本,降低开发效率。而一旦使用布局功能,那么只需要将header、side、footer区域的重复代码放到布局模版中,留下content区域的代码放到各自控制器动作模版中,那么就能够有效地提高开发效率。
ThinkPHP3.0之前的模版引擎没有布局的概念,但开发人员可以使用include包含文件达到网站布局的效果。新版的ThinkPHP引入了模版布局功能,要使用模版布局需要在配置项中打开Layout_on选项 (默认是关闭,即false)。为了方便演示,接下来将使用一个简单的模版文件,演示布局功能的使用,帮助读者加深布局的认识。
首先在项目Tpl根目录下创建一个Html文件,并命名为layout.html。该文件即为当前应用的布局文件,使用可视化工具(如dreamweaver)设计一个网站外观,将需要经常改动的代码使用{__CONTENT__}特定变量(没有定界符)代替,如简略图所示
如上图所示,因代码的篇幅太多,截图时收缩后效果,模版引擎会将{__CONTENT__}变量替换为相应的控制动作页面,整个解释流程将由layout.html开始,然后到各自的动作页面。这样一来,我们只需在控制器动作中嵌入少量的代码,即可使页面保持统一的布局与风格,如以下代码所示
在用户访问该页面时,上述代码将会代替{__CONTENT__}变量,效果如图所示
在布局文件中嵌入的变量或标签都是非全局的,而只针对当前页面生效。所以在布局代码中声明变量或语句,都应该在当前控制器动作中处理,否则就没有存在的意义。布局文件允许嵌套,如果网站页面更加复杂,还可以结合include等标签实现开发需求。
另外,如果不需要全局布局,也可以单独在当前页面中使用布局文件,只需要在当前页面头部加上<layout name="layout"/>标签即可,这种方式称为局部布局,事实上与include标签原理一样。
我们的开发工程中经常会使用到各种图,所谓的图就是由节点和节点之间的连接所形成的系统,数学上专门有一个分支叫图论(Graph Theroy)。利用图我们可以做很多工具,比如思维导图,流程图,状态机,组织架构图,等等。今天我要做的是用开源的HTML5工具来快速构造一个做图的工具。
工预善其事,必先利其器。第一件事是选择一件合适的工具,开源时代,程序员还是很幸福的,选择很多。
最终,我选择了jsPlumb,因为它完全开源,使用很简单,用D3的话可能会多花很多功夫。joint.js也不错。大家可以根据自己的需要选择。
下面我们一步一步的来使用jsPlumb来创建我们的流程图工具。
第一步是等待DOM和jsPlumb初始化完毕,类似document.ready()和jquery.ready(), 要使用jsPlumb, 需要把代码放在这个函数里:
jsPlumb.ready(function() { // ... your code goes here ... }
创建一个jsPlumb的实例,并初始化jsPlumb的配置参数:
//Initialize JsPlumb var color = "#E8C870"; var instance = jsPlumb.getInstance({ // notice the 'curviness' argument to this Bezier curve. the curves on this page are far smoother // than the curves on the first demo, which use the default curviness value. Connector : [ "Bezier", { curviness:50 } ], DragOptions : { cursor: "pointer", zIndex:2000 }, PaintStyle : { strokeStyle:color, lineWidth:2 }, EndpointStyle : { radius:5, fillStyle:color }, HoverPaintStyle : {strokeStyle:"#7073EB" }, EndpointHoverStyle : {fillStyle:"#7073EB" }, Container:"container-id" });
这里给给出了一些配置包括,连接线(这里配置了一个贝塞尔曲线),线的风格,连接点得风格。Container需要配置一个对应的DIV容器的id。(这里也可以使用setContainer的方法)
下面我们要创建一个节点(node),每一个节点可以用一个DIV来实现。我这里提供了一个函数来创建节点。
function addNode(parentId, nodeId, nodeLable, position) { var panel = d3.select("#" + parentId); panel.append('div').style('width','120px').style('height','50px') .style('position','absolute') .style('top',position.y).style('left',position.x) .style('border','2px #9DFFCA solid').attr('align','center') .attr('id',nodeId).classed('node',true) .text(nodeLable); return jsPlumb.getSelector('#' + nodeId)[0]; }
这里做的事情就是创建了一个DIV元素,并放在对应的容器的制定位置上,注意为了支持拖拽的功能,必须使用position:absolute 。
我使用D3来操作DOM,大家可能会更习惯JQuery,这纯属个人喜好的问题。
最后返回创建节点的实例引用,这是的selector使用了jsPlumb.getSelector()方法,它和JQuery的selector是一样的,这样用的好处是你可以使用不同的DOM操作库,例如Vanilla
下面我使用一个函数来创建端点/锚点(anchor),锚点就是节点上的连接点,用于连接不同的节点。
function addPorts(instance, node, ports, type) { //Assume horizental layout var number_of_ports = ports.length; var i = 0; var height = $(node).height(); //Note, jquery does not include border for height var y_offset = 1 / ( number_of_ports + 1); var y = 0; for ( ; i < number_of_ports; i++ ) { var anchor = [0,0,0,0]; var paintStyle = { radius:5, fillStyle:'#FF8891' }; var isSource = false, isTarget = false; if ( type === 'output' ) { anchor[0] = 1; paintStyle.fillStyle = '#D4FFD6'; isSource = true; } else { isTarget =true; } anchor[1] = y + y_offset; y = anchor[1]; instance.addEndpoint(node, { uuid:node.getAttribute("id") + "-" + ports[i], paintStyle: paintStyle, anchor:anchor, maxConnections:-1, isSource:isSource, isTarget:isTarget }); } }
instance是jsPlumb的实例
node是我们用addNode方法创建的Node实例
ports,是一个string的数组,指定端点的个数和名字
type,可能是output或者input,指定端点的种类,一个节点的输出端口可以连接另一个节点的输入端口。
这里anchor是一个四维数组,0维和1维分别是锚点在节点x轴和y轴的偏移百分比。我这里希望把端口画在节点的左右两侧,并按照端口的数量均匀分布。
最后使用instance.addEndpoint来创建端点。注意这里只要指定isSource和isTarget就可以用drag&drop的方式来连接端点,非常方便。
下面一步我们提供一个函数来连接端点:
function connectPorts(instance, node1, port1, node2 , port2) { // declare some common values: var color = "gray"; var arrowCommon = { foldback:0.8, fillStyle:color, width:5 }, // use three-arg spec to create two different arrows with the common values: overlays = [ [ "Arrow", { location:0.8 }, arrowCommon ], [ "Arrow", { location:0.2, direction:-1 }, arrowCommon ] ]; var uuid_source = node1.getAttribute("id") + "-" + port1; var uuid_target = node2.getAttribute("id") + "-" + port2; instance.connect({uuids:[uuid_source, uuid_target]}); }
node1和node2是源节点和目标节点的引用,port1和port2是源端口和目标端口的名字。
使用instance.connect方法来创建连接。 overlays用来添加连接线的箭头效果或者其他风格,我这里没有使用,因为觉得都不是很好看。大家如果要用,只要把overlays加入到instance.connect的方法参数就可以了。
调用以上方法来创建节点,端点和连接线。
var node1 = addNode('container-id','node1', 'node1', {x:'80px',y:'20px'}); var node2 = addNode('container-id','node2', 'node2', {x:'280px',y:'20px'}); addPorts(instance, node1, ['out1','out2'],'output'); addPorts(instance, node2, ['in','in1','in2'],'input'); connectPorts(instance, node1, 'out2', node2, 'in');
这里我们创建了两个节点,第一个节点有两个输出端口,第二个节点有三个输入端口,然后把第一个节点的out2端口连接到第二个端点的in端口。效果如下:
最后我们给节点增加drag&drop的功能,这样我们就可以拖动这些节点来改变图的布局了。
instance.draggable($('.node'));
这里似乎依赖于JQuery-UI,我还不是很清楚。
我们已经初步具有了创建图的功能,可是节点的创建必须通过程序,我们希望用交互的方式来创建节点。
通常我们希望有一个tree view的控件,让后通过拖拽来创建对应类型的节点。这里我使用了这个开源的tree view,基于bootstrap https://github.com/jonmiles/bootstrap-treeview
我们先创建一个tree view:
function getTreeData() { var tree = [ { text: "Nodes", nodes: [ { text: "Node1", }, { text: "Node2" } ] } ]; return tree; } //Initialize Control Tree View $('#control-panel').treeview({data: getTreeData()});
树上有两个节点:
然后我实现从树上拖拽对应的节点,到流程图上的逻辑。
//Handle drag and drop $('.list-group-item').attr('draggable','true').on('dragstart', function(ev){ //ev.dataTransfer.setData("text", ev.target.id); ev.originalEvent.dataTransfer.setData('text',ev.target.textContent); console.log('drag start'); }); $('#container-id').on('drop', function(ev){ //avoid event conlict for jsPlumb if (ev.target.className.indexOf('_jsPlumb') >= 0 ) { return; } ev.preventDefault(); var mx = '' + ev.originalEvent.offsetX + 'px'; var my = '' + ev.originalEvent.offsetY + 'px'; console.log('on drop : ' + ev.originalEvent.dataTransfer.getData('text')); var uid = new Date().getTime(); var node = addNode('flow-panel','node' + uid, 'node', {x:mx,y:my}); addPorts(instance, node, ['out'],'output'); addPorts(instance, node, ['in1','in2'],'input'); instance.draggable($(node)); }).on('dragover', function(ev){ ev.preventDefault(); console.log('on drag over'); });
这里要注意的是要避免和jsPlumb拖拽端点的逻辑冲突,当检测到target是jsPlumb对象是需要直接从drop方法中退出以执行对应的jsPlumb的drop逻辑。
好了,一个绘制流程图的软件工具初步完工。
我把代码放在oschina的代码托管服务上了, 大家有兴趣可以去试试。
载自:http://blog.csdn.net/fanyun_01/article/details/52623777
Qt布局管理手册:
http://doc.qt.io/qt-5/qtwidgets-index.html#styles
http://doc.qt.io/qt-5/qtwidgets-index.html#widgets
http://doc.qt.io/qt-5/qtwidgets-index.html#layouts
以下是Qt手册中的《布局管理》的译文:
在一个Widget中,Qt布局管理系统提供了一个简单而有效的方式来自动组织子widget,以保证他们能够很好地利用可用空间。
介绍:
Qt包含一个布局管理类的集合,它们被用来描述widgets如何在应用程序的用户界面中呈现的。当可用空间发生变化时,这些布局将自动调整widgets的位置和大小,以确保它们布局的一致性和用户界面主体可用。
所有QWidget的子类都可以用布局来管理它们的子类。QWidget::setLayout()函数给widget提供一个布局。当布局通过这种方式设置到widget,它将负责以下任务:
1.子widget的定位
2.窗口的合理默认空间
3.窗口的合理最小空间
4.调整大小处理
5.当内容发生变化时自动调整
6.字体、大小或者内容变化
7.显示或 隐藏widget
8.移除子widget
Qt的布局类:
QGraphicsAnchorLayout
Layout where one can anchor widgets together in Graphics View
在制图视图中布局widget
QGraphicsAnchor
Represents an anchor between two items in a QGraphicsAnchorLayout
表示一个QGraphicsAnchorLayout中两个图元之间的anchor
QBoxLayout
Lines up child widgets horizontally or vertically
水平或垂直整理子widget
QHBoxLayout
Lines up widgets horizontally
水平整理子控件
QVBoxLayout
Lines up widgets vertically
垂直整理子控件
QFormLayout
Manages forms of input widgets and their associated labels
label-inputwidget模式表单布局
QGridLayout
Lays out widgets in a grid
网格布局
QLayout
The base class of geometry managers
布局,几何管理的基类
QLayoutItem
Abstract item that a QLayout manipulates
管理的抽象元素
QSpacerItem
Blank space in a layout
空白区域布局
QWidgetItem
Layout item that represents a widget
布局元素
QSizePolicy
Layout attribute describing horizontal and vertical resizing policy
大小策略
QStackedLayout
Stack of widgets where only one widget is visible at a time
栈模式布局,一次只显示一个
QButtonGroup
Container to organize groups of button widgets
管理按钮的容器
QGroupBox
Group box frame with a title
带标题的组箱框架
QStackedWidget
Stack of widgets where only one widget is visible at a time
栈模式的widget,一次只显示一个
水平、垂直、网格和表格布局:
Qt布局类之间的关系如图1所示:
图1 Qt布局类之间的关系
给widgets一个很好布局的最好方式是使用内置的布局管理器: QHBoxLayout, QVBoxLayout, QGridLayout, and QFormLayout. 这些类都从QLayout继承而来,它们都来源于QObject(而不是QWidget)。创建更加复杂的布局,可以让它们彼此嵌套完成。
QHBoxLayout:水平布局
QVBoxLayout:垂直布局
QGridLayout: 表格布局
QGridLayout::addWidget()语法
layout->addWidget(widget, row, column, rowSpan, columnSpan);
参数widget:为插入到这个布局的子控件;
参数(row,column)为控件占据的左上角单元格位置;
参数rowSpan是控件占据的行数,
参数colunmSpan是控件占据的列的个数。
(rowSpan和colunmSpan默认值为1)
Stacked Layouts:分组布局
QStackedLayout类把子控件进行分组或者分页,一次只显示一组或者一页,隐藏其他组或者页上的控件。
使用这些Qt布局管理类的另一个原因是,在程序、系统改变字体,语言或者在不同的平台上运行时,布局管理器能够自动调整窗体里所有控件的大小和尺寸。
其他可进行布局管理的类:这些类的共同特点是提供了更加灵活的布局管理,在一定程度上用户能够控制窗体内控件的大小。
QSplitter,QScrollArea,QMainWindow,QWorkspace(对多文档的支持)
2) 布局管理中结合控件的sizePolicy属性,进行调整
结合控件的SizePolicy属性,来控制布局管理中的控件的尺寸自适应方式。
控件的sizePolicy说明控件在布局管理中的缩放方式。Qt提供的控件都有一个合理的缺省sizePolicy,但是这个缺省值有时不能适合所有的布局,开发人员经常需要改变窗体上的某些控件的sizePolicy。一个QSizePolicy的所有变量对水平方向和垂直方向都适用。下面列举了一些最长用的值:
A. Fixed:控件不能放大或者缩小,控件的大小就是它的sizeHint。
B. Minimum:控件的sizeHint为控件的最小尺寸。控件不能小于这个sizeHint,但是可以
放大。
C. Maximum:控件的sizeHint为控件的最大尺寸,控件不能放大,但是可以缩小到它的最小
的允许尺寸。
D. Preferred:控件的sizeHint是它的sizeHint,但是可以放大或者缩小
E. Expandint:控件可以自行增大或者缩小
注:sizeHint(布局管理中的控件默认尺寸,如果控件不在布局管理中就为无效的值)
1.QHBoxLayout是水平布局,将从左往右(orright to left for right-to-left languages )widget布局成水平行
2.QVBoxLayout是垂直布局,从顶部到底部
3.QGridLayout 是二位的网格布局。它可以容纳多个单元格:
4.QFormLayout是两列label-field式的表单布局
代码举例:
下面代码创建QHBoxLayout来管理5个QPushButtons的几何图形:
QWidget *window= new QWidget; QPushButton *button1= new QPushButton("One"); QPushButton *button2= new QPushButton("Two"); QPushButton *button3= new QPushButton("Three"); QPushButton *button4= new QPushButton("Four"); QPushButton *button5= new QPushButton("Five"); QHBoxLayout *layout= new QHBoxLayout; layout->addWidget(button1); layout->addWidget(button2); layout->addWidget(button3); layout->addWidget(button4); layout->addWidget(button5); window->setLayout(layout); window->show();
QGridLayout示例如下:
QWidget *window = new QWidget; QPushButton *button1 = new QPushButton("One"); QPushButton *button2 = new QPushButton("Two"); QPushButton *button3 = new QPushButton("Three"); QPushButton *button4 = new QPushButton("Four"); QPushButton *button5 = new QPushButton("Five"); QGridLayout *layout = new QGridLayout; layout->addWidget(button1, 0, 0); layout->addWidget(button2, 0, 1); layout->addWidget(button3, 1, 0, 1, 2); layout->addWidget(button4, 2, 0); layout->addWidget(button5, 2, 1); window->setLayout(layout); window->show();
QFormLayout示例如下:
QWidget *window = new QWidget; QPushButton *button1 = new QPushButton("One"); QLineEdit *lineEdit1 = new QLineEdit(); QPushButton *button2 = new QPushButton("Two"); QLineEdit *lineEdit2 = new QLineEdit(); QPushButton *button3 = new QPushButton("Three"); QLineEdit *lineEdit3 = new QLineEdit(); QFormLayout *layout = new QFormLayout; layout->addRow(button1, lineEdit1); layout->addRow(button2, lineEdit2); layout->addRow(button3, lineEdit3); window->setLayout(layout); window->show();
布局技巧:
当使用布局的时候,在创建子widget时,没必要给它传递父类。布局会自动重新定义它们的父类(通过QWidget::setParent())以确保它们是装载布局的widget的子类。
注意1:布局中的控件是装载布局控件的子控件,不是布局的子控件。控件只能以其他控件作为父类,不可以以布局作为父类。在布局上,可以使用addLayout来嵌套布局;被嵌套的布局,将变成上层布局的子布局。
向布局添加widgets:
添加布局到widgets时,布局过程执行如下:
1.所有widgets将根据它们的 QWidget::sizePolicy() and QWidget::sizeHint()首先分配一些空间。
2. 如果有widgets设置了大于0的拉伸系数,接下来它们将按照拉伸系数的比例来分配空间。
3. 如果有widgets设置的拉伸系数是0,它将在没有其他widgets需要空间时获取更多空间。其中,带Expanding大小策略的widget将首先获得空间。
4. 所有分配了小于最小空间(或者设置了最小的sizehint)的widget将按要求分配最小空间。(在拉伸系数成为决定因子时,widgets没必要再用最小值或者最小hint)。
5. 任何分配了大于最大空间的widget将按要求分配最大空间。(拉伸系数起着决定作用)
拉伸系数:
通常,widgets创建的时候没有设置拉伸系数。当widget整理到一个布局中时,它们将根据QWidget::sizePolicy()或者最小大小hint(取决于谁更大)分配一定空间。拉伸系数被用于按比例改变widget的分配空间。
如果3个widget用QHBoxLayout 来布局,不带拉伸系数,它们将得到像下面的布局:
如果带上拉伸系数,情况将变成这样:
自定义widget的布局:
当编写自定义widget类时,需要显示提供它的布局属性。如果widget有Qt自带的布局,它能够自己满足自己。如果没有任何子布局,或者使用手动布局,可以通过下面的机制来改变widget的行为:
1.实现QWidget::sizeHint() 来返回首先大小
2.实现QWidget::minimumSizeHint()来返回widget可以拥有的最小空间
3.调用QWidget::setSizePolicy来描述widget所需的空间
当size hint、minimum size或size policy改变时,调用QWidget::updateGeometry()。这将促使布局重新进行计算。连续多次调用QWidget::updateGeometry()只会发生一次布局重新计算。
即便实现了QWidget::heightForWidth(),也有必要提供合理的sizeHint()。
进一步了解,参见:Trading Height for Width.
布局问题:
The use of rich text in a label widget can introduce some problemsto the layout of its parent widget. Problems occur due to the way rich text ishandled by Qt's layout managers when the label is word wrapped.
Incertain cases the parent layout is put into QLayout::FreeResize mode, meaningthat it will not adapt the layout of its contents to fit inside small sizedwindows, or even prevent the user from making the window too small to beusable. This can be overcome by subclassing the problematic widgets, andimplementing suitable sizeHint() andminimumSizeHint() functions.
Insome cases, it is relevant when a layout is added to a widget. When you set thewidget of a QDockWidget ora QScrollArea (with QDockWidget::setWidget() andQScrollArea::setWidget()), the layout mustalready have been set on the widget. If not, the widget will not be visible.
在QLabel中使用富文本会给布局的父类widget带来一些问题。问题发生的原因是因为当label被文字环绕时,富文本被Qt的布局管理器控制。
在某些情况下,父类布局被放入QLayout::FreeResize模式,这意味着它将不适应内容布局所设置的最小窗口,或者甚至阻止用户让窗口小到不可用的情况。这个可以通过将问题控件作为子类来解决,并实现合适的sizeHint()和minimumSizeHint()函数。
在一些情况下,当布局被添加到widget时需要特别注意。当设置QDockWidget ora QScrollArea widget时(用QDockWidget::setWidget() andQScrollArea::setWidget()),布局必须已经被设置到widget上。否则,这些widget将不可见。
手动布局:
如果想自定义一个独特的布局,可以按 如上所述地自定义一个widget。实现QWidget::resizeEvent()来计算所需的大小分配并在每个子类中调用setGeometry() 。
需要布局需要重新计算大小时,widget将提供一个事件接口QEvent::LayoutRequest 。实现QWidget::event()来接收QEvent::LayoutRequest事件。
自定义布局管理:
自定义布局的唯一方法是继承QLayout来完成自己布局管理器。Border Layout 和Flow Layout 例子将说明如何来完成。
下面将举个例子来说明。CardLayout 类,受同名java布局管理的启发。它分层管理每个元素,每个元素的通过QLayout::spacing()来设置位移量。
编写自定义布局类,必须定义以下内容:
由布局控制的存放元素的数据结构。每个元素都是一个QLayoutItem。在这个例子中,我们将使用QList 。
1. addItem(),描述如何添加元素到布局。
2.setGeometry(),描述如何完成布局
3.sizeHint(),布局的首选大小
4.itemAt(),描述如何递归布局
5.takeAt(),描述如何移除布局中的元素。
在大多数情况下,还需要实现minimumSize()。
头文件 card.h #ifndef CARD_H #define CARD_H #include <QtGui> #include <QList> class CardLayout : public QLayout { public: CardLayout(QWidget *parent, int dist): QLayout(parent, 0, dist) {} CardLayout(QLayout *parent, int dist): QLayout(parent, dist) {} CardLayout(int dist): QLayout(dist) {} ~CardLayout(); void addItem(QLayoutItem *item); QSize sizeHint() const; QSize minimumSize() const; int count() const; QLayoutItem *itemAt(int) const; QLayoutItem *takeAt(int); void setGeometry(const QRect &rect); private: QList<QLayoutItem*> list; }; #endif 实现文件 card.cpp #include "card.h" int CardLayout::count() const { // QList::size() returns the number of QLayoutItems in the list return list.size(); } int CardLayout::count() const { // QList::size() returns the number of QLayoutItems in the list return list.size(); } int CardLayout::count() const { // QList::size() returns the number of QLayoutItems in the list return list.size(); } CardLayout::~CardLayout() { QLayoutItem *item; while ((item = takeAt(0))) delete item; } void CardLayout::setGeometry(const QRect &r) { QLayout::setGeometry(r); if (list.size() == 0) return; int w = r.width() - (list.count() - 1) * spacing(); int h = r.height() - (list.count() - 1) * spacing(); int i = 0; while (i < list.size()) { QLayoutItem *o = list.at(i); QRect geom(r.x() + i * spacing(), r.y() + i * spacing(), w, h); o->setGeometry(geom); ++i; } } QSize CardLayout::sizeHint() const { QSize s(0,0); int n = list.count(); if (n > 0) s = QSize(100,70); //start with a nice default size int i = 0; while (i < n) { QLayoutItem *o = list.at(i); s = s.expandedTo(o->sizeHint()); ++i; } return s + n*QSize(spacing(), spacing()); } QSize CardLayout::minimumSize() const { QSize s(0,0); int n = list.count(); int i = 0; while (i < n) { QLayoutItem *o = list.at(i); s = s.expandedTo(o->minimumSize()); ++i; } return s + n*QSize(spacing(), spacing()); }
进一步说明:自定义布局没有控制宽和高。
忽略了 QLayoutItem::isEmpty(),这意味着布局将把隐藏widget作为可见的。
对于复杂布局,通过缓存计算将大大提高速度。在那种情况下,实现QLayoutItem::invalidate() 来标记数据是脏数据。
调用QLayoutItem::sizeHint()等的代价比较大。在通过函数中,需要再次使用,最好将结果保存在本地变量中。
在同样函数的同一个元素中,不应该调用两次 QLayoutItem::setGeometry()。 这个调用将耗费巨大,如果它用几个子widget,因为布局管理器每次都要做一个完整的布局。替代方法:先计算geometry,然后再设置(这种事情,不仅应该在布局时注意,在实现resizeEvent()时也需要按同样方法来做)。
另外:
作为QLayout的父类,QLayoutItem提供了下列方法,包括绘制和范围的信息:
virtual QSize sizeHint() const = 0 virtual QRect geometry() const = 0 virtual void invalidate() virtual QLayout * layout() Qt::Alignment alignment() const
QLayout提供的信息就比较多了:提供了子页面、子Layout的添加接口,设置边界、菜单项等的接口
virtual void addItem(QLayoutItem *item) = 0 void addWidget(QWidget*w) void setContentsMargins(intleft, int top, int right, int bottom) void setMenuBar(QWidget*widget)
QBoxLayout作为QLayout的子类,提供了一些额外的信息:提供元素的拉伸比例,添加空元素等
void addLayout(QLayout*layout, int stretch = 0) void addSpacerItem(QSpacerItem*spacerItem) void addSpacing(intsize) bool setStretchFactor(QWidget*widget, int stretch) bool setStretchFactor(QLayout*layout, int stretch)
界面的继承情况,在http://doc.qt.io/qt-5/qlayout.html有详细的介绍,不做额外的介绍。
使用的过程的例子介绍一些:
// 设置边界为0
QHBoxLayout *pLayout1 = new QHBoxLayout(); QHBoxLayout *pLayout2 = new QHBoxLayout(); pLayout1->setMargin(0); pLayout2->setMargin(0);
// 设置拉伸比例2:3
pLayoutMain->addLayout(pLayout1); pLayoutMain->addLayout(pLayout2); pLayoutMain->setStretch(0, 2); pLayoutMain->setStretch(1, 3);
// 底面边距设0
int left = 0, right = 0, top = 0, bottom = 0; pMainLayout->getContentsMargins(&left, &top, &right, &bottom); pMainLayout->setContentsMargins(left, right, top, 0);
// 添加填充弹簧条
pLayout1->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum));
// 元素设置位置:左对齐,上下居中
pLabel->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
// 设置固定高度
pLabel->setFixedHeight(21);
// 设置固定宽度
*请认真填写需求信息,我们会在24小时内与您取得联系。