Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
在主流的一些MVC框架中都提供有網(wǎng)站布局功能,如Symfony、CodeIgniter、CakePHP等,這些框架默認的模版方案已經(jīng)啟用了布局功能。使用布局能夠有效地提高模版的開發(fā)速度,并且由于布局的限制,網(wǎng)站中的各頁面能夠保持高效的統(tǒng)一,方便后期維護。一個經(jīng)典的網(wǎng)站布局如圖所示
如圖所示,這是一個經(jīng)典的網(wǎng)站布局圖,如果網(wǎng)站頁面內(nèi)容量大,欄目多,那么布局就會更加復雜。如果不使用布局功能,那么要保持整個網(wǎng)站想通的布局形式,就必須在每個模版中使用相同的代碼,這無疑會極大地增加開發(fā)成本,降低開發(fā)效率。而一旦使用布局功能,那么只需要將header、side、footer區(qū)域的重復代碼放到布局模版中,留下content區(qū)域的代碼放到各自控制器動作模版中,那么就能夠有效地提高開發(fā)效率。
ThinkPHP3.0之前的模版引擎沒有布局的概念,但開發(fā)人員可以使用include包含文件達到網(wǎng)站布局的效果。新版的ThinkPHP引入了模版布局功能,要使用模版布局需要在配置項中打開Layout_on選項 (默認是關閉,即false)。為了方便演示,接下來將使用一個簡單的模版文件,演示布局功能的使用,幫助讀者加深布局的認識。
首先在項目Tpl根目錄下創(chuàng)建一個Html文件,并命名為layout.html。該文件即為當前應用的布局文件,使用可視化工具(如dreamweaver)設計一個網(wǎng)站外觀,將需要經(jīng)常改動的代碼使用{__CONTENT__}特定變量(沒有定界符)代替,如簡略圖所示
如上圖所示,因代碼的篇幅太多,截圖時收縮后效果,模版引擎會將{__CONTENT__}變量替換為相應的控制動作頁面,整個解釋流程將由layout.html開始,然后到各自的動作頁面。這樣一來,我們只需在控制器動作中嵌入少量的代碼,即可使頁面保持統(tǒng)一的布局與風格,如以下代碼所示
在用戶訪問該頁面時,上述代碼將會代替{__CONTENT__}變量,效果如圖所示
在布局文件中嵌入的變量或標簽都是非全局的,而只針對當前頁面生效。所以在布局代碼中聲明變量或語句,都應該在當前控制器動作中處理,否則就沒有存在的意義。布局文件允許嵌套,如果網(wǎng)站頁面更加復雜,還可以結(jié)合include等標簽實現(xiàn)開發(fā)需求。
另外,如果不需要全局布局,也可以單獨在當前頁面中使用布局文件,只需要在當前頁面頭部加上<layout name="layout"/>標簽即可,這種方式稱為局部布局,事實上與include標簽原理一樣。
我們的開發(fā)工程中經(jīng)常會使用到各種圖,所謂的圖就是由節(jié)點和節(jié)點之間的連接所形成的系統(tǒng),數(shù)學上專門有一個分支叫圖論(Graph Theroy)。利用圖我們可以做很多工具,比如思維導圖,流程圖,狀態(tài)機,組織架構(gòu)圖,等等。今天我要做的是用開源的HTML5工具來快速構(gòu)造一個做圖的工具。
工預善其事,必先利其器。第一件事是選擇一件合適的工具,開源時代,程序員還是很幸福的,選擇很多。
最終,我選擇了jsPlumb,因為它完全開源,使用很簡單,用D3的話可能會多花很多功夫。joint.js也不錯。大家可以根據(jù)自己的需要選擇。
下面我們一步一步的來使用jsPlumb來創(chuàng)建我們的流程圖工具。
第一步是等待DOM和jsPlumb初始化完畢,類似document.ready()和jquery.ready(), 要使用jsPlumb, 需要把代碼放在這個函數(shù)里:
jsPlumb.ready(function()?{ ????//?...?your?code?goes?here?... }
創(chuàng)建一個jsPlumb的實例,并初始化jsPlumb的配置參數(shù):
//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的方法)
下面我們要創(chuàng)建一個節(jié)點(node),每一個節(jié)點可以用一個DIV來實現(xiàn)。我這里提供了一個函數(shù)來創(chuàng)建節(jié)點。
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]; }
這里做的事情就是創(chuàng)建了一個DIV元素,并放在對應的容器的制定位置上,注意為了支持拖拽的功能,必須使用position:absolute 。
我使用D3來操作DOM,大家可能會更習慣JQuery,這純屬個人喜好的問題。
最后返回創(chuàng)建節(jié)點的實例引用,這是的selector使用了jsPlumb.getSelector()方法,它和JQuery的selector是一樣的,這樣用的好處是你可以使用不同的DOM操作庫,例如Vanilla
下面我使用一個函數(shù)來創(chuàng)建端點/錨點(anchor),錨點就是節(jié)點上的連接點,用于連接不同的節(jié)點。
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方法創(chuàng)建的Node實例
ports,是一個string的數(shù)組,指定端點的個數(shù)和名字
type,可能是output或者input,指定端點的種類,一個節(jié)點的輸出端口可以連接另一個節(jié)點的輸入端口。
這里anchor是一個四維數(shù)組,0維和1維分別是錨點在節(jié)點x軸和y軸的偏移百分比。我這里希望把端口畫在節(jié)點的左右兩側(cè),并按照端口的數(shù)量均勻分布。
最后使用instance.addEndpoint來創(chuàng)建端點。注意這里只要指定isSource和isTarget就可以用drag&drop的方式來連接端點,非常方便。
下面一步我們提供一個函數(shù)來連接端點:
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是源節(jié)點和目標節(jié)點的引用,port1和port2是源端口和目標端口的名字。
使用instance.connect方法來創(chuàng)建連接。 overlays用來添加連接線的箭頭效果或者其他風格,我這里沒有使用,因為覺得都不是很好看。大家如果要用,只要把overlays加入到instance.connect的方法參數(shù)就可以了。
調(diào)用以上方法來創(chuàng)建節(jié)點,端點和連接線。
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');
這里我們創(chuàng)建了兩個節(jié)點,第一個節(jié)點有兩個輸出端口,第二個節(jié)點有三個輸入端口,然后把第一個節(jié)點的out2端口連接到第二個端點的in端口。效果如下:
最后我們給節(jié)點增加drag&drop的功能,這樣我們就可以拖動這些節(jié)點來改變圖的布局了。
instance.draggable($('.node'));
這里似乎依賴于JQuery-UI,我還不是很清楚。
我們已經(jīng)初步具有了創(chuàng)建圖的功能,可是節(jié)點的創(chuàng)建必須通過程序,我們希望用交互的方式來創(chuàng)建節(jié)點。
通常我們希望有一個tree view的控件,讓后通過拖拽來創(chuàng)建對應類型的節(jié)點。這里我使用了這個開源的tree view,基于bootstrap https://github.com/jonmiles/bootstrap-treeview
我們先創(chuàng)建一個tree view:
function?getTreeData()?{ ??var?tree?=?[ ????{ ??????text:?"Nodes", ??????nodes:?[ ????????{ ??????????text:?"Node1", ????????}, ????????{ ??????????text:?"Node2" ????????} ??????] ????} ??];? ??return?tree; } //Initialize?Control?Tree?View $('#control-panel').treeview({data:?getTreeData()});
樹上有兩個節(jié)點:
然后我實現(xiàn)從樹上拖拽對應的節(jié)點,到流程圖上的邏輯。
//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方法中退出以執(zhí)行對應的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布局管理系統(tǒng)提供了一個簡單而有效的方式來自動組織子widget,以保證他們能夠很好地利用可用空間。
介紹:
Qt包含一個布局管理類的集合,它們被用來描述widgets如何在應用程序的用戶界面中呈現(xiàn)的。當可用空間發(fā)生變化時,這些布局將自動調(diào)整widgets的位置和大小,以確保它們布局的一致性和用戶界面主體可用。
所有QWidget的子類都可以用布局來管理它們的子類。QWidget::setLayout()函數(shù)給widget提供一個布局。當布局通過這種方式設置到widget,它將負責以下任務:
1.子widget的定位
2.窗口的合理默認空間
3.窗口的合理最小空間
4.調(diào)整大小處理
5.當內(nèi)容發(fā)生變化時自動調(diào)整
6.字體、大小或者內(nèi)容變化
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
網(wǎng)格布局
QLayout
The base class of geometry managers
布局,幾何管理的基類
QLayoutItem
Abstract item that a QLayout manipulates
管理的抽象元素
QSpacerItem
Blank space in a layout
空白區(qū)域布局
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,一次只顯示一個
水平、垂直、網(wǎng)格和表格布局:
Qt布局類之間的關系如圖1所示:
圖1 Qt布局類之間的關系
給widgets一個很好布局的最好方式是使用內(nèi)置的布局管理器: QHBoxLayout, QVBoxLayout, QGridLayout, and QFormLayout. 這些類都從QLayout繼承而來,它們都來源于QObject(而不是QWidget)。創(chuàng)建更加復雜的布局,可以讓它們彼此嵌套完成。
QHBoxLayout:水平布局
QVBoxLayout:垂直布局
QGridLayout: 表格布局
QGridLayout::addWidget()語法
layout->addWidget(widget, row, column, rowSpan, columnSpan);
參數(shù)widget:為插入到這個布局的子控件;
參數(shù)(row,column)為控件占據(jù)的左上角單元格位置;
參數(shù)rowSpan是控件占據(jù)的行數(shù),
參數(shù)colunmSpan是控件占據(jù)的列的個數(shù)。
(rowSpan和colunmSpan默認值為1)
Stacked Layouts:分組布局
QStackedLayout類把子控件進行分組或者分頁,一次只顯示一組或者一頁,隱藏其他組或者頁上的控件。
使用這些Qt布局管理類的另一個原因是,在程序、系統(tǒng)改變字體,語言或者在不同的平臺上運行時,布局管理器能夠自動調(diào)整窗體里所有控件的大小和尺寸。
其他可進行布局管理的類:這些類的共同特點是提供了更加靈活的布局管理,在一定程度上用戶能夠控制窗體內(nèi)控件的大小。
QSplitter,QScrollArea,QMainWindow,QWorkspace(對多文檔的支持)
2) 布局管理中結(jié)合控件的sizePolicy屬性,進行調(diào)整
結(jié)合控件的SizePolicy屬性,來控制布局管理中的控件的尺寸自適應方式。
控件的sizePolicy說明控件在布局管理中的縮放方式。Qt提供的控件都有一個合理的缺省sizePolicy,但是這個缺省值有時不能適合所有的布局,開發(fā)人員經(jīng)常需要改變窗體上的某些控件的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 是二位的網(wǎng)格布局。它可以容納多個單元格:
4.QFormLayout是兩列l(wèi)abel-field式的表單布局
代碼舉例:
下面代碼創(chuàng)建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();
布局技巧:
當使用布局的時候,在創(chuàng)建子widget時,沒必要給它傳遞父類。布局會自動重新定義它們的父類(通過QWidget::setParent())以確保它們是裝載布局的widget的子類。
注意1:布局中的控件是裝載布局控件的子控件,不是布局的子控件。控件只能以其他控件作為父類,不可以以布局作為父類。在布局上,可以使用addLayout來嵌套布局;被嵌套的布局,將變成上層布局的子布局。
向布局添加widgets:
添加布局到widgets時,布局過程執(zhí)行如下:
1.所有widgets將根據(jù)它們的 QWidget::sizePolicy() and QWidget::sizeHint()首先分配一些空間。
2. 如果有widgets設置了大于0的拉伸系數(shù),接下來它們將按照拉伸系數(shù)的比例來分配空間。
3. 如果有widgets設置的拉伸系數(shù)是0,它將在沒有其他widgets需要空間時獲取更多空間。其中,帶Expanding大小策略的widget將首先獲得空間。
4. 所有分配了小于最小空間(或者設置了最小的sizehint)的widget將按要求分配最小空間。(在拉伸系數(shù)成為決定因子時,widgets沒必要再用最小值或者最小hint)。
5. 任何分配了大于最大空間的widget將按要求分配最大空間。(拉伸系數(shù)起著決定作用)
拉伸系數(shù):
通常,widgets創(chuàng)建的時候沒有設置拉伸系數(shù)。當widget整理到一個布局中時,它們將根據(jù)QWidget::sizePolicy()或者最小大小hint(取決于誰更大)分配一定空間。拉伸系數(shù)被用于按比例改變widget的分配空間。
如果3個widget用QHBoxLayout 來布局,不帶拉伸系數(shù),它們將得到像下面的布局:
如果帶上拉伸系數(shù),情況將變成這樣:
自定義widget的布局:
當編寫自定義widget類時,需要顯示提供它的布局屬性。如果widget有Qt自帶的布局,它能夠自己滿足自己。如果沒有任何子布局,或者使用手動布局,可以通過下面的機制來改變widget的行為:
1.實現(xiàn)QWidget::sizeHint() 來返回首先大小
2.實現(xiàn)QWidget::minimumSizeHint()來返回widget可以擁有的最小空間
3.調(diào)用QWidget::setSizePolicy來描述widget所需的空間
當size hint、minimum size或size policy改變時,調(diào)用QWidget::updateGeometry()。這將促使布局重新進行計算。連續(xù)多次調(diào)用QWidget::updateGeometry()只會發(fā)生一次布局重新計算。
即便實現(xiàn)了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帶來一些問題。問題發(fā)生的原因是因為當label被文字環(huán)繞時,富文本被Qt的布局管理器控制。
在某些情況下,父類布局被放入QLayout::FreeResize模式,這意味著它將不適應內(nèi)容布局所設置的最小窗口,或者甚至阻止用戶讓窗口小到不可用的情況。這個可以通過將問題控件作為子類來解決,并實現(xiàn)合適的sizeHint()和minimumSizeHint()函數(shù)。
在一些情況下,當布局被添加到widget時需要特別注意。當設置QDockWidget ora QScrollArea widget時(用QDockWidget::setWidget() andQScrollArea::setWidget()),布局必須已經(jīng)被設置到widget上。否則,這些widget將不可見。
手動布局:
如果想自定義一個獨特的布局,可以按 如上所述地自定義一個widget。實現(xiàn)QWidget::resizeEvent()來計算所需的大小分配并在每個子類中調(diào)用setGeometry() 。
需要布局需要重新計算大小時,widget將提供一個事件接口QEvent::LayoutRequest 。實現(xiàn)QWidget::event()來接收QEvent::LayoutRequest事件。
自定義布局管理:
自定義布局的唯一方法是繼承QLayout來完成自己布局管理器。Border Layout 和Flow Layout 例子將說明如何來完成。
下面將舉個例子來說明。CardLayout 類,受同名java布局管理的啟發(fā)。它分層管理每個元素,每個元素的通過QLayout::spacing()來設置位移量。
編寫自定義布局類,必須定義以下內(nèi)容:
由布局控制的存放元素的數(shù)據(jù)結(jié)構(gòu)。每個元素都是一個QLayoutItem。在這個例子中,我們將使用QList 。
1. addItem(),描述如何添加元素到布局。
2.setGeometry(),描述如何完成布局
3.sizeHint(),布局的首選大小
4.itemAt(),描述如何遞歸布局
5.takeAt(),描述如何移除布局中的元素。
在大多數(shù)情況下,還需要實現(xiàn)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 實現(xiàn)文件 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作為可見的。
對于復雜布局,通過緩存計算將大大提高速度。在那種情況下,實現(xiàn)QLayoutItem::invalidate() 來標記數(shù)據(jù)是臟數(shù)據(jù)。
調(diào)用QLayoutItem::sizeHint()等的代價比較大。在通過函數(shù)中,需要再次使用,最好將結(jié)果保存在本地變量中。
在同樣函數(shù)的同一個元素中,不應該調(diào)用兩次 QLayoutItem::setGeometry()。 這個調(diào)用將耗費巨大,如果它用幾個子widget,因為布局管理器每次都要做一個完整的布局。替代方法:先計算geometry,然后再設置(這種事情,不僅應該在布局時注意,在實現(xiàn)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小時內(nèi)與您取得聯(lián)系。