整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          網頁模版設計布局的使用

          在主流的一些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工具來快速構造一個做圖的工具。

          工具選擇

          工預善其事,必先利其器。第一件事是選擇一件合適的工具,開源時代,程序員還是很幸福的,選擇很多。

          • flowchart.js http://adrai.github.io/flowchart.js/ , 基于SVG創建Flow Chart
          • go.js http://www.gojs.net/latest/index.html go.js 提供一整套的JS工具 ,支持各種交互式圖表的創建。有免費版和收費版
          • joint.js http://www.jointjs.com/ joint.js 是另一個創建圖標庫的工具,也提供免費版和商業版
          • jsPlumb http://www.jsplumb.org/ jsPlumb是一套開源的流程圖創建工具 ,小巧精悍,使用簡單
          • d3 http://d3js.org 在html5領域,d3可謂是最好的可視化基礎庫,提供方面的DOM操作,非常強大。

          最終,我選擇了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);
          

          // 設置固定寬度


          主站蜘蛛池模板: 久久精品无码一区二区无码| 亚洲国产成人久久一区WWW | 国产精品99无码一区二区| 日韩精品一区二区三区不卡| 无码精品一区二区三区免费视频 | 无码人妻AⅤ一区二区三区水密桃| 麻豆精品久久久一区二区| 性色AV一区二区三区无码| 国产综合一区二区在线观看 | 韩国资源视频一区二区三区| 久久se精品一区精品二区| 国产亚洲福利一区二区免费看| 91在线看片一区国产| 日本道免费精品一区二区| 精品国产免费观看一区| 中字幕一区二区三区乱码| 熟妇人妻AV无码一区二区三区| 一区二区三区视频免费| 秋霞鲁丝片一区二区三区| 欧美亚洲精品一区二区| 一本久久精品一区二区| 亚洲区精品久久一区二区三区| 国模少妇一区二区三区| 国产午夜精品一区二区三区漫画| 无码毛片一区二区三区视频免费播放 | 3d动漫精品成人一区二区三| 日本一区二区免费看| 性色A码一区二区三区天美传媒| 精品国产香蕉伊思人在线在线亚洲一区二区 | 色天使亚洲综合一区二区| 亚洲一区二区无码偷拍| 麻豆精品人妻一区二区三区蜜桃| 色噜噜狠狠一区二区| 午夜福利无码一区二区| 国内精品一区二区三区在线观看 | 一区二区三区观看免费中文视频在线播放| 久久成人国产精品一区二区| 99精品国产高清一区二区三区| 国产美女精品一区二区三区| 香蕉久久一区二区不卡无毒影院 | 日本精品一区二区久久久|