<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 第十一章 使用sizer放置構件 1. [使用sizer放置窗口部件](#A.2BT391KA-sizer.2BZT5.2FbnqXU.2BOQ6E72-) 1. [sizer是什么?](#sizer.2BZi9OwE5I.2Fx8-) 2. [基本的sizer:grid](#A.2BV.2FpnLHaE-sizer.2B.2Fxo-grid) 1. [什么是grid](#A.2BTsBOSGYv-grid) 2. [如何對sizer添加或移除孩子?](#A.2BWYJPVVv5-sizer.2BbftSoGIWefuWZFtpW1D.2FHw-) 3. [sizer是如何管理它的孩子的尺寸和對齊的?](#sizer.2BZi9Zgk9Ve6F0BluDdoRbaVtQdoRcOlv4VIxb.2BZ9QdoT.2FHw-) 4. [能夠為sizer或它的孩子指定一個最小的尺寸嗎?](#A.2BgP1ZH046-sizer.2BYhZbg3aEW2lbUGMHW5pOAE4qZwBcD3aEXDpb.2BFQX.2Fx8-) 5. [sizer如何管理每個孩子的邊框?](#sizer.2BWYJPVXuhdAZrz04qW2lbUHaEj7loRv8f-) 3. [使用其它類型的sizer](#A.2BT391KFF2W4N8e1eLdoQ-sizer) 1. [什么是flex grid sizer?](#A.2BTsBOSGYv-flex_grid__sizer.2B.2Fx8-) 2. [什么是grid bag sizer?](#A.2BTsBOSGYv-grid_bag__sizer.3F) 3. [什么是box](#A.2BTsBOSGYv-box) 4. [什么是static](#A.2BTsBOSGYv-static) 4. [一個現實中使用sizer的例子](#A.2BTgBOKnOwW55OLU9.2FdSg-sizer.2BdoRPi1tQ-) 5. [本章小結](#A.2BZyx64FwPftM-) **本章內容** ·理解`sizer` ·使用`sizer`分隔窗口部件 ·使用`sizer`的`grid`系列 ·使用`box` `sizer` ·看看`sizer`的實際應用 傳統上,在用戶界面編程中的最苦惱的一個問題是管理窗口中的窗口部件的實際布局。因為它涉及到與絕對位置直接打交到,這是很痛苦的一件事情。 所以你需要一個結構,它根據預設的模式來決定如何調整和移動窗口部件。目前推薦用來處理復雜布局的方法是使用`sizer`。`sizer`是用于自動布局一組窗口部件的算法。`sizer`被附加到一個容器,通常是一個框架或面板。在父容器中創建的子窗口部件必須被分別地添加到`sizer`。當`sizer`被附加到容器時,它隨后就管理它所包含的孩子的布局。 使用`sizer`的好處是很多的。當子窗口部件的容器的尺寸改變時,`sizer`將自動計算它的孩子的布局并作出相應的調整。同樣,如果其中的一個孩子改變了尺寸,那么`sizer`能夠自動地刷新布局。此外,當你想要改變布局時,`sizer`管理起來很容易。最大的弊端是`sizer`的布局有一些局限性。但是,最靈活`sizer`——`grid` `bag`和`box`,能夠做你要它們做的幾乎任何事。 ## sizer是什么? 一個`wxPython` `sizer`是一個對象,它唯一的目的就是管理容器中的窗口部件的布局。`sizer`本身不是一個容器或一個窗口部件。它只是一個屏幕布局的算法。所有的`sizer`都是抽象類`wx.Sizer`的一個子類的實例。`wxPython`提供了5個`sizer`,定義在表11.1中。`sizer`可以被放置到別的`sizer`中以達到更靈活的管理。 **表11.1** **`wxPython`中預定義的`sizer`** `Grid`:一個十分基礎的網格布局。當你要放置的窗口部件都是同樣的尺寸且整齊地放入一個規則的網格中是使用它。 `Flex` `grid`:對`grid` `sizer`稍微做了些改變,當窗口部件有不同的尺寸時,可以有更好的結果。 `Grid` `bag`:`grid` `sizer`系列中最靈活的成員。使得網格中的窗口部件可以更隨意的放置。 `Box`:在一條水平或垂直線上的窗口部件的布局。當尺寸改變時,在控制窗口部件的的行為上很靈活。通常用于嵌套的樣式。可用于幾乎任何類型的布局。 `Static` `box`:一個標準的`box` `sizer`。帶有標題和環線。 如果你想要你的布局類似`grid`或`box`,`wxPython`可以變通;實際上,任何有效的布局都能夠被想像為一個`grid`或一系列`box`。 所有的`sizer`都知道它們的每個孩子的最小尺寸。通常,`sizer`也允許有關布局的額外的信息,例如窗口部件之間有多少空間,它能夠使一個窗口部件的尺寸增加多以填充空間,以及當窗口部件比分配給它們的空間小時如何對齊這些窗口部件等。根據這些少量的信息`sizer`用它的布局算法來確定每個孩子的尺寸和位置。`wxPython`中的每種`sizer`對于同組子窗口部件所產生的最終布局是不同的。 貫穿本章,你都會看到,我們使用非常相似的布局來演示每個`sizer`類型。 下面是使用一個`sizer`的三個基本步驟: * 創建并關聯`sizer`到一個容器。`sizer`被關聯到容器使用`wx.Window`的`SetSizer(sizer)`方法。由于這是一個`wx.Window`的方法,所以這意味著任何`wxPython`窗口部件都可以有一個`sizer`,盡管`sizer`只對容器類的窗口部件有意義。 * 添加每個孩子到這個`sizer`。所有的孩子窗口部件需要被單獨添加到該`sizer`。僅僅創建使用容器作為父親的孩子窗口部件是不夠的。還要將孩子窗口部件添加到一個`sizer`,這個主要的方法是`Add()`。`Add()`方法有一對不同的標記,我們將在下一節討論。 * (可選的)使`sizer`能夠計算它的尺寸。告訴`sizer`去根據它的孩子來計算它的尺寸,這通過在父窗口對象上調用`wx.Window`的`Fit()`方法或該`sizer`的`Fit(window)`方法。(這個窗口方法重定向到`sizer`方法。)兩種情況下,這個`Fit()`方法都要求`sizer`根據它所掌握的它的孩子的情況去計算它的尺寸,并且它調整父窗口部件到合適的尺寸。還有一個相關的方法:`FitInside()`,它不改變父窗口部件的顯示尺寸,但是它改變它虛擬尺寸——這意味著如果窗口部件是在一個可滾動的面板中,那么`wxPython`會重新計算是否需要滾動條。 我們既要討論特定的`sizer`的行為,也要討論所有`sizer`的共同行為。這就有個先后的問題。我們將以介紹`grid` `sizer`作為開始,它是最容易理解的。之后,我們將討論所有`sizer`的共同行為,使用`grid` `sizer`作為一個例子。(使用`grid` `sizer`作為例子使得最共同的行為更形象化。)之后我們將討論其它的特定類型的`sizer`。 ## 基本的sizer:grid **`sizer`** 后面所有的例子使用了一個有點無聊的窗口部件,目的是占據布局中的空間,這樣你可以看到`sizer`是如何工作的。例11.1給出了該窗口部件的代碼,它被本章中的其余的例子導入。從始至終,你將看到它的大量的圖片——它基本上是一個帶有標簽的簡單的矩形。 **例11.1** **塊狀窗口,在后面的例子中用作一個窗口部件** ``` import wx class BlockWindow(wx.Panel): def __init__(self, parent, ID=-1, label="", pos=wx.DefaultPosition, size=(100, 25)): wx.Panel.__init__(self, parent, ID, pos, size, wx.RAISED_BORDER, label) self.label = label self.SetBackgroundColour("white") self.SetMinSize(size) self.Bind(wx.EVT_PAINT, self.OnPaint) def OnPaint(self, evt): sz = self.GetClientSize() dc = wx.PaintDC(self) w,h = dc.GetTextExtent(self.label) dc.SetFont(self.GetFont()) dc.DrawText(self.label, (sz.width-w)/2, (sz.height-h)/2) ``` 貫穿本章,我們將使用不同的`sizer`來在一個框架中放上幾個這樣的塊窗口部件。我們將使用`grid` `sizer`作為開始。 ### 什么是grid **`sizer`?** `wxPython`提供的最簡單的`sizer`是`grid`。顧名思義,一個`grid` `sizer`把它的孩子放置在一個二維網格中。位于這個`sizer`的孩子列表中的第一個窗口部件放置在網格的左上角,其余的按從左到右,從上到下的方式排列,直到最后一個窗口部件被放置在網格的右底部。圖11.1顯示了一個例子,有九個窗口部件被放置在一個3*3的網格中。注意每個部件之間有一些間隙。 **圖11.1** ![](https://box.kancloud.cn/2016-08-21_57b99644c3cf1.gif) 當你調整`grid` `sizer`的大小時,每個部件之間的間隙將隨之改變,但是默認情況下,窗口部件的尺寸不會變,并且始終按左上角依次排列。圖11.2顯示了調整尺寸后的同一窗口。 **圖11.2** ![](https://box.kancloud.cn/2016-08-21_57b99644d64b9.gif) 例11.2顯示了用于產生圖11.1和11.2的代碼。 **例11.2** **使用`grid`** **`sizer`** ``` import wx from blockwindow import BlockWindow labels = "one two three four five six seven eight nine".split() class GridSizerFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "Basic Grid Sizer") sizer = wx.GridSizer(rows=3, cols=3, hgap=5, vgap=5)#創建grid sizer for label in labels: bw = BlockWindow(self, label=label) sizer.Add(bw, 0, 0)#添加窗口部件到sizer self.SetSizer(sizer)#把sizer與框架關聯起來 self.Fit() app = wx.PySimpleApp() GridSizerFrame().Show() app.MainLoop() ``` 你可以從例11.2看到,一個`grid` `sizer`是類`wx.GridSizer`的一個實例。構造函數顯式地設置四個屬性,這些屬性是`grid` `sizer`獨一無二的: ``` wx.GridSizer(rows, cols, vgap, hgap) ``` 這個構造函數中的`rows`和`cols`是整數,它們指定了網格的尺寸——所能放置的窗口部件的數量。如果這兩個參數之一被設置為0,那么它的實際的值根據`sizer`中的孩子的數量而定。例如如果使用`wx.GridSizer(2`, 0, 0, 0),且`sizer`有八個孩子,那么它就需要有四列來填充這些孩子。 `vgap`和`hgap`使你可以決定窗口控件間的間隔的多少。`vgap`是兩相鄰列間的間隔的象素量,`hgapvgap`是兩相鄰行間的間隔的象素量。這些象素量是除了窗口控件邊框的量。屬性`rows`, `cols`, `vgap`, `hgap`都有各自的`get`*和`set`*方法——`GetRows()`, `SetRows(rows)`, `GetCols()`,`SetCols(cols)`, `GetVGap()`, `SetVGap(gap)`, `GetHGap()`, 和`SetHGap(gap)` 。 `grid` `sizer`的尺寸和位置的算法是十分簡單的。當`Fit()`第一次被調用時,創建初始化的網格布局。如果有必要,行和列的數量根據列表中元素的數量來計算。在`grid`中每個空格的尺寸是相同的——即使每個窗口部件的尺寸不同。這個最大的尺度是根據網格中寬度最寬的孩子的寬度和高度最高的孩子的高度來計算的。所以,`grid` `sizer`最適合用于所有孩子相同尺寸的情況。有著不同尺寸窗口部件的`grid` `sizer`看起來有點怪異。如果你仍想要一個類似`grid`的布局,但是你又有不同尺寸的窗口部件的話,那么可以使用`flex` `grid` `sizer`或`grid` `bag` `sizer`。 ### 如何對sizer添加或移除孩子? 添加孩子部件到`sizer`中的次序是非常重要的。這與將孩子添加到一個父窗口部件中的通常情況是不一樣的。`sizer`的通常的布局算法要求一次添加一個孩子,以便于決定它們的顯示位置。下一項的位置是依賴于前一被添加項的位置的。例如,`grid` `sizer`基于窗口部件的次序來從左到右,從上到下的添加并顯示。在多數情況下,當你在父窗口部件的構造器中創建`sizer`時,你將會按正確的次序添加這些項目。但是有時候,如果你在運行時動態地改變你的布局,那么你需要更靈活和更細致。 **使用`Add()`方法** 添加一個窗口部件到一個`sizer`中的最常用的方法是`Add()`,它將新的窗口部件添加到`sizer`的孩子列表的尾部。“添加到`sizer`的孩子列表的尾部”的準確的意思信賴于該`sizer`的類型,但是通常它意味這個新的窗口部件將依次顯示在右下位置。`Add()`方法有三個不同的樣式: ``` Add(window, proportion=0, flag=0, border=0, userData=None) Add(sizer, proportion=0, flag=0, border=0, userData=None) Add(size, proportion=0, flag=0, border=0, userData=None) ``` 第一個版本是你最常要用到的,它使你能夠將一個窗口部件添加到`sizer`。 第二個版本用于將一個`sizer`嵌套在另一個中——這最常用于`box` `sizer`,但可用于任何類型的`sizer`。 第三個版本使你能夠添加一個`wx.Size`對象的空的空白尺寸或一個(寬,高)元組到`sizer`,通常用作一個分隔符(例如,在一個工具欄中)。另外,這在`box` `sizer`中最常使用,但也可在任何`sizer`中使用以用于形成窗口的一個空白區域或分隔不同的窗口部件。 其它的參數影響`sizer`中的項目如何顯示。其中的一些只對某種`sizer`有效。`proportion`僅被`box` `sizer`使用,并當父窗口尺寸改變時影響一個項目如何被繪制。這個稍后我們將在`box` `sizer`時討論。 `flag`參數用于放置位標記,它控制對齊、邊框和調整尺寸。這些項將在后面的章節中討論。如果在`flag`參數中指定了邊框,那么`border`參數包含邊框的寬度。如果`sizer`的算法需要,`userData`參數可被用來傳遞額外的數據。如果你正在設計一個自定義的`sizer`,那么你可以使用該參數。 **使用`insert()`方法** 這里有用于將新的窗口部件插入到`sizer`中不同位置的方法。`insert()`方法使你能夠按任意的索引來放置新的窗口部件。它也有三個形式: ``` Insert(index, window, proportion=0, flag=0, border=0, userData=None) Insert(index, sizer, proportion=0, flag=0, border=0, userData=None) Insert(index, size, proportion=0, flag=0, border=0, userData=None) ``` **使用`Prepend()`方法** 該方法將新的窗口部件、`sizer`或空白添加到`sizer`的列表的開頭,這意味所添加的東西將被顯示到左上角: ``` Prepend(window, proportion=0, flag=0, border=0, userData=None) Prepend(sizer, proportion=0, flag=0, border=0, userData=None) Prepend(size, proportion=0, flag=0, border=0, userData=None) ``` 圖11.3顯示了在例11.1中如果`Add()`替換為`Prepend()`后的布局。 **圖11.3** ![](https://box.kancloud.cn/2016-08-21_57b99644eb6c3.gif) 如果`sizer`已在屏幕上顯示了,而你又要給`sizer`添加一個新的項目,那么你需要調用`sizer`的`Layout()`方法來迫使`sizer`自己重新排列,以容納新的項。 **使用`Detach()`方法** 為了從`sizer`中移除一項,你需要調用`Detach()`方法,它從`sizer`中移除項目,但是沒有銷毀該項目。這對于你以后再使用它是有用的。使用`Detach()`有三種方法。你可以將你想要移除的窗口、`sizer`對象、對象的索引作為參數傳遞給`Detach()`: ``` Detach(window) Detach(sizer) Detach(index) ``` 在這三種情況中,`Detach()`方法返回一個布爾值,它表明項目是否真的被刪除了——如果你試圖移除`sizer`中沒有的項,將返回`false`。和你曾見過的其它的刪除方法不同,`Detach()`不返回被刪除的項目,所以如果你想要得到它的話,你需要之前用一個變量來存儲對它的引用。 從`sizer`中刪除項目,不會自動改變在屏幕上的顯示。你需要調用`Layout()`方法來執行重繪。 你可以得到一個包含了窗口的`sizer`的引用,這通過使用`wx.Window`的`GetContainingSizer()`方法。如果該窗口部件沒有被包含在`sizer`中,那么該方法返回`None`。 ### sizer是如何管理它的孩子的尺寸和對齊的? 當一個新的項目被添加到一個`sizer`時,`sizer`就使用這個項目的初始尺寸或根據它的布局計算給出恰當的尺寸(如果它的初始尺寸沒有設置)。換句話說,`sizer`不調整一個項目的大小,除非要求,這通常發生在一個窗口尺寸的改變時。 當`sizer`的父窗口部件改變了尺寸時,`sizer`需要改變它的組分的尺寸。默認情況下,`sizer`保持這些窗口部件的對齊方式不變。 當你添加一個窗口部件到`sizer`時,可以通過給`flag`參數一個特定值來調整該窗口部件的尺寸改變行為。圖11.4展示了在用戶放大窗口后,幾個不同標記應用于這個基本的`grid` `sizer`的結果。 **圖11.4** ![](https://box.kancloud.cn/2016-08-21_57b996450aa2b.gif) 例11.3顯示了產生圖11.4的代碼。除了在窗口部件被添加到`sizer`時應用了一個標記字典外,其它的與前一個例子相同。 **例11.3 使用了用于對齊和調整尺寸的標記的一個`grid` `sizer`** ``` import wx from blockwindow import BlockWindow labels = "one two three four five six seven eight nine".split() #對齊標記 flags = {"one": wx.ALIGN_BOTTOM, "two": wx.ALIGN_CENTER, "four": wx.ALIGN_RIGHT, "six": wx.EXPAND, "seven": wx.EXPAND, "eight": wx.SHAPED} class TestFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "GridSizer Resizing") sizer = wx.GridSizer(rows=3, cols=3, hgap=5, vgap=5) for label in labels: bw = BlockWindow(self, label=label) flag = flags.get(label, 0) sizer.Add(bw, 0, flag) self.SetSizer(sizer) self.Fit() app = wx.PySimpleApp() TestFrame().Show() app.MainLoop() ``` 在這個例子中,窗口部件“`one`,” “`two`,” 和“`four`”分別使用`wx.ALIGN_BOTTOM`, `wx.ALIGN_CENTER`, `and` `wx.ALIGN_RIGHT`標記改變它們的對齊方式。當窗口大小改變時,你可以看到效果,部件“`three`"沒有指定一個標記,所以它按左上角對齊。窗口"`six`"和"`seven`"均使用了`wx.EXPAND`標記來告訴`sizer`改變它們的尺寸以填滿格子,而窗口部件"`eight`"使用`wx.SHAPED`來改變它的尺寸,以保持比例不變。 表11.2顯示與尺寸調整和對齊相關的`flag`的值。 **表11.2 尺寸調整和對齊行為標記** | | | | --- | --- | | `wx.ALIGN_BOTTOM` | 按照窗口部件被分配的空間(格子)的底部對齊。 | | `wx.ALIGN_CENTER` | 放置窗口部件,使窗口部件的中心處于其所分配的空間的中心。 | | `wx.ALIGN_CENTER_HORIZONTAL` | 在它所處的格子中,水平居中。 | | `wx.ALIGN_CENTER_VERTICAL` | 在它所處的格子中,垂直居中。 | | `wx.ALIGN_LEFT` | 靠著它所處的格子左邊緣。這是默認行為。 | | `wx.ALIGN_TOP` | 靠著它所處的格子的上邊緣。這是默認的行為。 | | `wx.EXPAND` | 填滿它所處的格子空間。 | | `wx.FIXED_MINSIZE` | 保持固定項的最小尺寸。 | | `wx.GROW` | 與`wx.EXPAND`相同。但比之少兩個字符,節約了時間。 | | `wx.SHAPED` | 窗口部件的尺寸改變時,只在一個方向上填滿格子,另一個方向上按窗口部件原先的形狀尺寸的比列填充。 | 這些標記可以使用|來組合,有時,這些組合會很有意思。`wx.ALIGN_TOP` | `wx.ALIGN_RIGHT`使得窗口部件位于格子的右上角。(注意,互相排斥的標記組合如`wx.ALIGN_TOP` | `wx.ALIGN_BOTTOM`中,默認的標記不起作用,這是因為默認標記的相應位上是0,在或操作中沒有什么影響)。 還有一些方法,你可以用來在運行時處理`sizer`或它的孩子的尺寸和位置。你可以使用方法`GetSize()`和 `GetPosition()`來得到`sizer`的當前尺寸和位置——這個位置是`sizer`相對于它所關聯的容器的。如果`sizer`嵌套在另一個`sizer`中,那么這些方法是很有用的。你可以通過調用`SetDimension(x`, y, `width`, `height)`方法來指定一個`sizer`的尺寸,這樣`sizer`將根據它的新尺寸和位置重新計算它的孩子的尺寸。 ### 能夠為sizer或它的孩子指定一個最小的尺寸嗎? `sizer`的窗口部件的布局中的另一個重要的要素是為`sizer`或它的孩子指定一個最小尺寸的能力。一般你不想要一個控件或一個`sizer`小于一個特定的尺寸,通常因為這樣會導致文本被窗口部件的邊緣截斷。或在一個嵌套的`sizer`中,控件在窗口中不能被顯示出來。為了避免諸如此類的情況,你可以使用最小尺寸。 圖11.5顯示了對一個特定的窗口部件設置最小尺寸的一個例子。該窗口的尺寸已被用戶改變了。 **圖11.5** ![](https://box.kancloud.cn/2016-08-21_57b996451d356.gif) 例11.4展示了產生該圖的代碼。它類似于基本的`grid`的代碼,其中增加了一個`SetMinSize()`調用。 **例11.4** **使用最小尺寸設置的`grid`** **`sizer`** ``` import wx from blockwindow import BlockWindow labels = "one two three four five six seven eight nine".split() class TestFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "GridSizer Test") sizer = wx.GridSizer(rows=3, cols=3, hgap=5, vgap=5) for label in labels: bw = BlockWindow(self, label=label) sizer.Add(bw, 0, 0) center = self.FindWindowByName("five") center.SetMinSize((150,50)) self.SetSizer(sizer) self.Fit() app = wx.PySimpleApp() TestFrame().Show() app.MainLoop() ``` 當一個`sizer`被創建時,它根據它的孩子的綜合的最小尺寸(最小的寬度和最小的高度)隱含地創建一個最小尺寸。多數控件都知道它們最小化的“最佳尺寸”,`sizer`查詢該值以確定默認的布局。如果顯式地使用一個尺寸值來創建一個控件,那么這個設置的尺寸值覆蓋所計算出的最佳尺寸。一個控件的最小尺寸可以使用窗口的方法`SetMinSize(width`, `height)`和`SetSizeHints(minW`, `minH`, `maxW`, `maxH)`來設置——第二個方法使你也能夠指定一個最大尺寸。如果一個窗口部件的屬性(通常是所顯示的字體或文本標簽)改變,該窗口部件通常將調整它的最佳尺寸。 如果一個窗口有相關的`sizer`,那么這個容器窗口的最佳尺寸由它的`sizer`來確定。如果沒有,那么該窗口的最佳尺寸就是足夠大到顯示所有子控件的尺寸。如果該窗口沒有孩子,那么它使用所設置的最小尺寸為最佳尺寸。如果上述都沒有,那么該容器窗口的當前尺寸作為其最佳尺寸。 你可以使用`GetMinSize()`來訪問整個`sizer`的最小尺寸。如果你想為整個`sizer`設置一個較大的最小尺寸,那么你可以使用`SetMinSize(width`,`height)`,該函數也可以使用一個`wx.Size`實例作為參數——`SetMinSize(size)`,盡管在`wxPython`中你很少顯式地創建一個`wx.Size`。在最小尺寸已被設置后,`GetMinSize()`返回設置的尺寸或孩子的綜合的尺寸。 如果你只想設置`sizer`內的一個特定的孩子的最小尺寸,那么使用`sizer`的`SetItemMinSize()`方法。它也有三個形式: ``` SetItemMinSize(window, size) SetItemMinSize(sizer, size) SetItemMinSize(index, size) ``` 這里,參數`window`和`sizer`必須是一個`sizer`實例的孩子。如果需要的話,方法將在整個嵌套樹中搜索特定的子窗口或子`sizer`。參數`index`是`sizer`的孩子列表中的索引。參數`size`是一個`wx.Size`對象或一個(寬,高)元組,它是`sizer`中的項目被設置的最小尺寸。如果你設置的最小尺寸比窗口部件當前的尺寸大,那么它自動調整。你不能根據`sizer`來設置最大尺寸,只能根據窗口部件來使用`SetSizeHints()`設置。 ### sizer如何管理每個孩子的邊框? `wxPython` `sizer`能夠使它的一個或所有孩子有一個邊框。邊框是連續數量的空白空間,它們分離相鄰的窗口部件。當`sizer`計算它的孩子的布置時,邊框的尺寸是被考慮進去了的,孩子的尺寸不會小于邊框的寬度。當`sizer`調整尺寸時,邊框的尺寸不會改變。 圖11.6顯示了一個10像素的邊框。在每行中,中間的元素四邊都有邊框圍繞,而其它的只是部分邊有邊框。增加邊框不會使窗口部件更小,而是使得框架更大了。 **圖11.6** ![](https://box.kancloud.cn/2016-08-21_57b996453eb19.gif) 例11.5是產生圖11.6的相關代碼。它和基本的`grid` `sizer`相似,只是我們增加了一個邊框值字典,并給`Add()`一個10像素的邊框。 **例11.5** **使用邊框設置的`grid`** **`sizer`代碼** ``` import wx from blockwindow import BlockWindow labels = "one two three four five six seven eight nine".split() #邊框標記 flags = {"one": wx.BOTTOM, "two": wx.ALL, "three": wx.TOP, "four": wx.LEFT, "five": wx.ALL, "six": wx.RIGHT, "seven": wx.BOTTOM | wx.TOP, "eight": wx.ALL, "nine": wx.LEFT | wx.RIGHT} class TestFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "GridSizer Borders") sizer = wx.GridSizer(rows=3, cols=3, hgap=5, vgap=5) for label in labels: bw = BlockWindow(self, label=label) flag = flags.get(label, 0) sizer.Add(bw, 0, flag, 10)#添加指定邊框的窗口部件 self.SetSizer(sizer) self.Fit() app = wx.PySimpleApp() TestFrame().Show() app.MainLoop() ``` 要在一個`sizer`中的窗口部件周圍放置邊框,需要兩步。第一步是當窗口部件被添加到該`sizer`時,傳遞額外的標記給`flags`參數。你可以使用標記`wx.ALL`來指定邊框圍繞整個窗口部件,或使用`wx.BOTTOM`, `wx.LEFT`, `wx.RIGHT`, `wx.TOP`來指定某一邊有邊框。這些標記當然可以組合成你想要的,如`wx.RIGHT` | `wx.BOTTOM`將使你的窗口部件的右邊和底邊有邊框。由于邊框、尺寸調整、對齊這些信息都是經由`flags`參數,所以對于同一個窗口部件,你通常必須將三種標記組合起來使用。 在你傳遞了邊框信息到`flags`參數后,你也需要傳遞邊框寬度的像素值給`border`參數。例如,下面的調用將添加窗口部件到`sizer`列表的尾部,并使該窗口部件的周圍有5個像素寬度的邊框: ``` sizer.Add(widget, 0, wx.ALL | wx.EXPAND, 5) ``` 該部件然后將擴展以填充它的有效空間,且四周始終留有5個像素的空白。 ## 使用其它類型的sizer 我們已經討論了基本的`sizer`,現在我們可以轉向更復雜和更靈活的`sizer`了。其中兩個(`flex` `grid` `sizer`和`grid` `bag` `sizer`)本質上是`grid`的變種。另外兩個(`box`和`static` `box` `sizer`)使用一個不同的和更靈活的布局結構。 ### 什么是flex grid sizer? `flex` `grid` `sizer`是`grid` `sizer`的一個更靈活的版本。它與標準的`grid` `sizer`幾乎相同,除了下面的例外: 1、每行和每列可以有各自的尺寸。 2、默認情況下,當尺寸調整時,它不改變它的單元格的尺寸。如果需要的話,你可以指定哪行或哪列應該增長。 3、它可以在兩個方向之一靈活地增長,意思是你可以為個別的子元素指定比列量,并且你可以指定固定方向上的行為。 圖11.7顯示了一個`flex` `grid` `sizer`,它的布局也是9個單元格。這里的中間單元格更大。 **圖11.7** ![](https://box.kancloud.cn/2016-08-21_57b996454fdd6.gif) 與圖11.5相比較,對于相同的布局,圖11.5中每個單元格的尺寸與中間對象的相同,在`flex` `grid` `sizer`中,單元格的尺寸大小根據它所在的行和列來定。它們寬度是該列中寬度最大的項目的寬度,它們的高度是該行中寬度最高的項目的寬度。在這里,項目“`four`”和項目“`six`”的單元格的高度比項目本身的高度更高,因為其同行中的項目“`five`”,而"`two`"和"`seven`"的單元格的寬度也更寬。“`one`,” “`three`,” “`seven`,” 和“`nine`”的單元格是正常的尺寸,并且不受較大的窗口部件的影響。 圖11.8展示了當調整窗口尺寸時,`flex` `grid` `sizer`的默認行為——單元格的尺寸不改變。 **圖11.8** ![](https://box.kancloud.cn/2016-08-21_57b99645626af.gif) 例11.6顯示了產生了圖11.8的代碼 **例11.6** **創建一個`flex`** **`grid`** **`sizer`** ``` import wx from blockwindow import BlockWindow labels = "one two three four five six seven eight nine".split() class TestFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "FlexGridSizer") sizer = wx.FlexGridSizer(rows=3, cols=3, hgap=5, vgap=5) for label in labels: bw = BlockWindow(self, label=label) sizer.Add(bw, 0, 0) center = self.FindWindowByName("five") center.SetMinSize((150,50)) self.SetSizer(sizer) self.Fit() app = wx.PySimpleApp() TestFrame().Show() app.MainLoop() ``` 一個`flex` `grid` `sizer`是`wx.FlexGridSizer`的一個實例。類`wx.FlexGridSizer`是`wx.GridSizer`的子類,所以`wx.GridSizer`的屬性方法依然有效。`wx.FlexGridSizer`的構造函數與其父類的相同: `wx.FlexGridSizer(rows`, `cols`, `vgap`, `hgap)` 為了當`sizer`擴展時,使一行或列也擴展,你需要使用適當的方法顯式地告訴該`sizer`該行或列是可擴展的: ``` AddGrowableCol(idx, proportion=0) AddGrowableRow(idx, proportion=0) ``` 當`sizer`水平擴展時,關于新寬度的默認行為被等同地分配給每個可擴展的列。同樣,一個垂直的尺寸調整也被等同地分配給每個可擴展的行。要改變這個默認的行為并且使不同的行和列有不現的擴展比率,你需要使用`proportion`參數。如果`proportion`參數被使用了,那么與該參數相關的新的空間就被分配給了相應的行或列。例如,如果你有兩個尺寸可調整的行,并且它們的`proportion`分別是2和1,那么這第一個行將得到新空間的2/3,第二行將得到1/3。圖11.9顯示使用`proportional`(比列)空間的`flex` `grid` `sizer`。在這里,中間行和列所占的比例是2和5,兩端的行和列所占的比例是1。 **圖11.9** ![](https://box.kancloud.cn/2016-08-21_57b99645626af.gif) 正如你可以看到的,當所有的單元格增大時,中間的行和列的增大是兩端的兩倍。窗口部件的沒有改變尺寸以填表充它們的單元格,雖然可以通過在當它們被添加到`sizer`時使用`wx.EXPAND`來實現。例11.7顯示了產生圖11.9的代碼。 **例11.7** ``` import wx from blockwindow import BlockWindow labels = "one two three four five six seven eight nine".split() class TestFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "Resizing Flex Grid Sizer") sizer = wx.FlexGridSizer(rows=3, cols=3, hgap=5, vgap=5) for label in labels: bw = BlockWindow(self, label=label) sizer.Add(bw, 0, 0) center = self.FindWindowByName("five") center.SetMinSize((150,50)) sizer.AddGrowableCol(0, 1) sizer.AddGrowableCol(1, 2) sizer.AddGrowableCol(2, 1) sizer.AddGrowableRow(0, 1) sizer.AddGrowableRow(1, 5) sizer.AddGrowableRow(2, 1) self.SetSizer(sizer) self.Fit() app = wx.PySimpleApp() TestFrame().Show() app.MainLoop() ``` 如果你對一個可擴展的行或列使用了比例尺寸,那么你需要對該方向上的所有可擴展的行或列指定一個比例量,否則你將得到一個糟糕的效果。 在`flex` `grid` `sizer`中還有另外一個機制用于控制窗口部件的增長(執不執行先前`AddGrowable`*方法的設置)。默認情況下,比例尺寸適用于`flex` `grid`的兩個方向;但是你可以通過使用`SetFlexibleDirection(direction)`方法來指定僅某個方向應該按比例調整尺寸,參數`direction`的值可以是:`wx.HORIZONTAL`, `wx.VERTICAL`, 或`wx.BOTH` (默認值)。然后你可以使用`SetNonFlexibleGrowMode(mode)`方法來指定另一個方向上的行為。例如,如果你調用了`SetFlexibleDirection(wx.HORIZONTAL)`方法,列的行為就遵循`AddGrowableCol()`,然后調用`SetNonFlexibleGrowMode()`來定義行的行為。表11.3顯示了`mode`參數的有效值。 **表11.3** `wx.FLEX_GROWMODE_ALL`:`flex` `grid`在沒有使用`SetFlexibleDirection`*的方向上等同地調整所有單元格的尺寸。這將覆蓋使用`AddGrowable`*方法設置的任何行為——所有的單元格都將被調整尺寸,不管它們的比例或它們是否被指定為可擴展(增長)的。 `wx.FLEX_GROWMODE_NONE`:在沒有使用`SetFlexibleDirection`*的方向上的單元格的尺寸不變化,不管它們是否被指定為可增長的。 `wx.FLEX_GROWMODE_SPECIFIED`:在沒有使用`SetFlexibleDirection`*的方向上,只有那些可增長的單元格才增長。但是`sizer`將忽略任何的比例信息并等同地增長那些單元格。這是一個默認行為。 上面段落中所討論的`SetFlexibleDirection`和`SetNonFlexibleGrowMode`方法都有對應的方法:`GetFlexibleDirection()`和`GetNonFlexibleGrowMode()`,它們返回整型標記。在上表中要強調的是,任何使用這些方法來指定的設置將取代通過`AddGrowableCol()`和`AddGrowableRow()`創建的設置。 ### 什么是grid bag sizer? `grid` `bag` `sizer`是對`flex` `grid` `sizer`進一步的增強。在`grid` `bag` `sizer`中有兩個新的變化: 1、能夠將一個窗口部件添加到一個特定的單元格。 2、能夠使一個窗口部件跨越幾個單元格(就像`HTML`表單中的表格所能做的一樣)。 **圖11.10** ![](https://box.kancloud.cn/2016-08-21_57b996457b9c6.gif) 圖11.10顯示了一個`grid` `bag` `sizer`的示例。它與本章前面的例子很相似,只是增加了新的窗口部件以展示跨行和跨列。 例11.8顯示了產生圖11.10的代碼。注意這里的`Add()`方法與以前的看起來有點不同。 **例11.8 `Grid` `bag` `sizer`示例代碼** ``` #coding=utf-8 #!python import wx from blockwindow import BlockWindow labels = "one two three four five six seven eight nine".split() class TestFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "GridBagSizer Test") sizer = wx.GridBagSizer(hgap=5, vgap=5) for col in range(3): for row in range(3): bw = BlockWindow(self, label=labels[row*3 + col]) sizer.Add(bw, pos=(row,col)) # 跨行 bw = BlockWindow(self, label="span 3 rows") sizer.Add(bw, pos=(0,3), span=(3,1), flag=wx.EXPAND) # 跨列 bw = BlockWindow(self, label="span all columns") sizer.Add(bw, pos=(3,0), span=(1,4), flag=wx.EXPAND) # 使最后的行和列可增長 sizer.AddGrowableCol(3) sizer.AddGrowableRow(3) self.SetSizer(sizer) self.Fit() app = wx.PySimpleApp() TestFrame().Show() app.MainLoop() ``` `grid` `bag` `sizer`是`wx.GridBagSizer`的實例,`wx.GridBagSizer`是`wx.FlexGridSizer`的一個子類。這意味著所有`flex` `grid` `sizer`的屬性,`grid` `bag` `sizer`都適用。 `wx.GridBagSizer`的構造函數與它的父類有點不同: ``` wx.GridBagSizer(vgap=0, hgap=0) ``` 在一個`grid` `bag` `sizer`中,你不必去指定行和列的數量,因為你可以直接將子項目添加進特定的單元格——`sizer`將據此計算出網格的尺度。 **在`grid`** **`bag`** **`sizer`上使用`Add()`方法** 對于`grid` `bag` `sizer`,`Add()`方法與別的`sizer`不同。它有四個可選的形式: ``` 1 Add(window, pos, span=wx.DefaultSpan, flag=0, border=0, userData=None) 2 Add(sizer, pos, span=wx.DefaultSpan, flag=0, border=0, userData=None) 3 Add(size, pos, span=wx.DefaultSpan, flag=0, border=0, userData=None) 4 AddItem(item) ``` 這些看起來應該很熟悉,在運行上也與通常的`sizer`的方法相似。`window`, `sizer`, `size`, `flag`, `border`, 和`userData`參數的行為與通常`sizer`的方法中的是相同的。`pos`參數代表`sizer`中的窗口部件要賦予的單元格。技術上講,`pos`參數是類`wx.GBPosition`的一個實例,但是通過`wxPython`變換,你可以僅傳遞一個(行,列)形式的元組,`grid` `bag`的左上角是(0,0)。 同樣,`span`參數代表窗口部件應該占據的行和列的數量。它是類`wx.GBSpan`的一個實例,但是,`wxPython`也使你能夠傳遞一個(行的范圍,列的范圍)形式的元組。如果跨度沒有指定,那么默認值是(1,1),這意味該窗口部件在兩個方向都只能占據一個單元格。例如,要在第二行第一列放置一個窗口部件,并且使它占據三行兩列,那么你將這樣調用:`Add(widget`, (1, 0), (3, 2))(索引是從0開始的)。 `Additem`方法的`item`參數是類`wx.GBSizerItem`的一個實例,它包含了`grid` `bag` `sizer`放置項目所需要的全部信息。你不太可能需要直接去創建一個`wx.GBSizerItem`。如果你想去創建一個,那么它的構造函數的參數與`grid` `bag` `sizer`的其它`Add()`方法相同。一旦你有了一個`wx.GBSizerItem`,這兒有許多的`get`*方法使你能夠訪問項目的屬性,也許這最有用的是`GetWindow()`,它返回實際顯示的窗口部件。 由于項目是使用行和列的索引以及跨度被添加到一個`grid` `bag` `sizer`的,所以項目被添加的順序不必按照其它`sizer`所要求的對應它們的顯示順序。這使得跟蹤哪個項實際顯示在哪個單元格有一點頭痛。表11.4列出了幾個方法,`grid` `bag` `sizer`通過它們來使你對項目的跟蹤較為容易。 **表11.4 `Grid` `bag` `sizer` 管理項目的方法** `CheckForIntersection(item`,`excludeItem`=`None)` `CheckForIntersection(pos`,`span`, `excludeItem`=`None)`:將給定的項目或給定的位置和跨度同`sizer`中的項目進行比對。如果有任一項與給定項目的位置或給定的位置和跨度重疊,則返回`True`。`excludeItem`是一個可選的項,它不被包括在比對中(或許是因為它正在測試中)。`pos`參數是一個`wx.GBPosition`或一個元組。`span`參數是一個`wx.GPSpan`或一個元組。 `FindItem(window)` `FindItem(sizer)`:返回對應于給定的窗口或`sizer`的`wx.GBSizerItem`。如果窗口或`sizer`不在`grid` `bag`中則返回`None`。這個方法不遞歸檢查其中的子`sizer`。 `FindItemAtPoint(pt)`:`pt`參數是對應于所包含的框架的坐標的一個`wx.Point`實例或一個`Python`元組。這個方法返回位于該點的`wx.GBSizerItem` 。如果這個位置在框架的邊界之外或如果該點沒有`sizer`項目,則返回`None`。 `FindItemAtPosition(pos)`:該方法返回位于給定單元格位置的`wx.GBSizerItem`,參數`pos`是一個`wx.GBPosition`或一個`Python`元組。如果該位置在`sizer`的范圍外或該位置沒有項目,則返回`None`。 `FindItemWithData(userData)`:返回`grid` `bag`中帶有給定的`userData`對象的一個項目的`wx.GBSizerItem`。 `Grid` `bag`也有一對能夠用于處理單元格尺寸和項目位置的屬性。在`grid` `bag`被布局好并顯示在屏幕上后,你可以使用方法`GetCellSize(row`, `col)`來獲取給定的單元格顯示在屏幕上的尺寸。這個尺寸包括了由`sizer`本身所管理的水平和垂直的間隔。你可以使用方法`GetEmptyCellSize()`得到一個空單元格的尺寸,并且你可以使用`SetEmptyCellSize(sz)`改變該屬性,這里的`sz`是一個`wx.Size`對象或一個`Python`元組。 你也可以使用方法`GetItemPosition()`和`GetItemSpan()`來得到`grid` `bag`中的一個對象的位置或跨度。這兩個方法要求一個窗口,一個`sizer`或一個索引作為參數。這個索引參數與`sizer`的`Add()`列表中的索引相對應,這個索引在`grid` `bag`的上下文中沒多大意思。上面的兩個`get`*方法都有對應的`set`*方法,`SetItemPosition(window`, `pos)`和`SetItemSpan(window`, `span)`,這兩個方法的第一個參數可以是`window`,`sizer`,或`index`,第二個參數是一個`Python`元組或一個`wx.GBPosition`或`wx.GBSpan`對象。 ### 什么是box **`sizer`?** `box` `sizer`是`wxPython`所提供的`sizer`中的最簡單和最靈活的`sizer`。一個`box` `sizer`是一個垂直列或水平行,窗口部件在其中從左至右或從上到下布置在一條線上。雖然這聽起來好像用處太簡單,但是來自相互之間嵌套`sizer`的能力使你能夠在每行或每列很容易放置不同數量的項目。由于每個`sizer`都是一個獨立的實體,因此你的布局就有了更多的靈活性。對于大多數的應用程序,一個嵌套有水平`sizer`的垂直`sizer`將使你能夠創建你所需要的布局。 圖11.11-11.14展示了幾個簡單的`box` `sizer`的例子。圖中所展示的各個框架我們都是手動調整了大小的,以便展示每個`sizer`是如何響應框架的增大的。圖11.11顯示了一個水平的`box` `sizer`,圖11.12在一個垂直的`box` `sizer`顯示了現圖11.11相同的窗口部件。 **圖11.11** ![](https://box.kancloud.cn/2016-08-21_57b996458e9b5.gif) **圖11.12** ![](https://box.kancloud.cn/2016-08-21_57b99645a4c4f.gif) 圖11.13顯示了一個垂直的`sizer`,其中一個窗口部件被設置成可擴展并填充有效的垂直空間。圖11.14展示了一個垂直的`sizer`,其中的兩個窗口部件設置為按不同的比例占據有效的垂直空間。 **圖11.13** ![](https://box.kancloud.cn/2016-08-21_57b99645b8d83.gif) **圖11.14** ![](https://box.kancloud.cn/2016-08-21_57b99645ce5f2.gif) 生成以上四個`sizer`框架的示例代碼如例11.9所示。 **例11.9** **產生多個`box`** **`sizer`** ``` import wx from blockwindow import BlockWindow labels = "one two three four".split() class TestFrame(wx.Frame): title = "none" def __init__(self): wx.Frame.__init__(self, None, -1, self.title) sizer = self.CreateSizerAndWindows() self.SetSizer(sizer) self.Fit() class VBoxSizerFrame(TestFrame): title = "Vertical BoxSizer" def CreateSizerAndWindows(self): sizer = wx.BoxSizer(wx.VERTICAL) for label in labels: bw = BlockWindow(self, label=label, size=(200,30)) sizer.Add(bw, flag=wx.EXPAND) return sizer class HBoxSizerFrame(TestFrame): title = "Horizontal BoxSizer" def CreateSizerAndWindows(self): sizer = wx.BoxSizer(wx.HORIZONTAL) for label in labels: bw = BlockWindow(self, label=label, size=(75,30)) sizer.Add(bw, flag=wx.EXPAND) return sizer class VBoxSizerStretchableFrame(TestFrame): title = "Stretchable BoxSizer" def CreateSizerAndWindows(self): sizer = wx.BoxSizer(wx.VERTICAL) for label in labels: bw = BlockWindow(self, label=label, size=(200,30)) sizer.Add(bw, flag=wx.EXPAND) # Add an item that takes all the free space bw = BlockWindow(self, label="gets all free space", size=(200,30)) sizer.Add(bw, 1, flag=wx.EXPAND) return sizer class VBoxSizerMultiProportionalFrame(TestFrame): title = "Proportional BoxSizer" def CreateSizerAndWindows(self): sizer = wx.BoxSizer(wx.VERTICAL) for label in labels: bw = BlockWindow(self, label=label, size=(200,30)) sizer.Add(bw, flag=wx.EXPAND) # Add an item that takes one share of the free space bw = BlockWindow(self, label="gets 1/3 of the free space", size=(200,30)) sizer.Add(bw, 1, flag=wx.EXPAND) # Add an item that takes 2 shares of the free space bw = BlockWindow(self, label="gets 2/3 of the free space", size=(200,30)) sizer.Add(bw, 2, flag=wx.EXPAND) return sizer app = wx.PySimpleApp() frameList = [VBoxSizerFrame, HBoxSizerFrame, VBoxSizerStretchableFrame, VBoxSizerMultiProportionalFrame] for klass in frameList: frame = klass() frame.Show() app.MainLoop() ``` `box` `sizer`是類`wx.BoxSizer`的實例,`wx.BoxSizer`是`wx.Sizer`的子類,相對于`wx.Sizer`,`wx.BoxSizer`幾乎沒有增加新的方法。`wx.BoxSizer`的構造函數有一個參數: ``` wx.BoxSizer(orient) ``` 參數`orient`代表該`sizer`的方向,它的取值可以是`wx.VERTICAL`或`wx.HORIZONTAL`。對于`box` `sizer`所定義的唯一一個新的方法是`GetOrientation()`,它返回構造函數中`orient`的整數值。一旦一個`box` `sizer`被創建后,你就不能改變它的方向了。`box` `sizer`的其它的函數使用本章早先所討論的一般的`sizer`的方法。 `box` `sizer`的布局算法對待該`sizer`的主方向(當構建的時候已被它的方向參數所定義)和次要方向是不同的。特別地,`proportion`參數只適用于當`sizer`沿主方向伸縮時,而`wx.EXPAND`標記僅適用于當`sizer`的尺寸在次方向上變化時。換句話說,當一個垂直的`box` `sizer`被垂直地繪制時,傳遞給每個`Add()`方法調用的參數`proportion`決定了每個項目將如何垂直地伸縮。除了影響`sizer`和它的項目的水平增長外,參數`proportion`以同樣的方式影響水平的`box` `sizer`。在另一方面,次方向的增長是由對項目所使用的`wx.EXPAND`標記來控制的,所以,如果它們設置了`wx.EXPAND`標記的話,在一個垂直的`box` `sizer`中的項目將只在水平方向增長。否則這些項目保持它們的最小或最合適的尺寸。圖6.7演示了這個過程。 在`box` `sizer`中,項目的比例增長類似于`flex` `grid` `sizer`,但有一些例外。第一,`box` `sizer`的比例行為是在窗口部件被添加到該`sizer`時,使用`proportion`參數被確定的——你無需像`flex` `grid` `sizer`那樣單獨地指定它的增長性。第二,比例為0的行為是不同的。在`box` `sizer`中,0比例意味著該窗口部件在主方向上不將根據它的最小或最合適尺寸被調整尺寸,但是如果`wx.EXPAND`標記被使用了的話,它仍可以在次方向上增長。當`box` `sizer`在主方向上計算它的項目的布局時,它首先合計固定尺寸的項目所需要的空間,這些固定尺寸的項目,它們的比例為0。余下的空間按項目的比例分配。 ### 什么是static **`box`** **`sizer`?** 一個`static` `box` `sizer`合并了`box` `sizer`和靜態框(`static` `box`),靜態框在`sizer`的周圍提供了一個漂亮的邊框和文本標簽。圖11.15顯示了三個`static` `box` `sizer`。 **圖11.15** ![](https://box.kancloud.cn/2016-08-21_57b99645e12cd.gif) 例11.10顯示了產生上圖的代碼。這里有三個值得注意的事件。首先你必須單獨于`sizer`創建靜態框對象,第二是這個例子展示了如何使用嵌套的`box` `sizer`。本例中,三個垂直的`static` `box` `sizer`被放置于一個水平的`box` `sizer`中。 **例11.10** **`static`** **`box`** **`sizer`的一個例子** ``` import wx from blockwindow import BlockWindow labels = "one two three four five six seven eight nine".split() class TestFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "StaticBoxSizer Test") self.panel = wx.Panel(self) # make three static boxes with windows positioned inside them box1 = self.MakeStaticBoxSizer("Box 1", labels[0:3]) box2 = self.MakeStaticBoxSizer("Box 2", labels[3:6]) box3 = self.MakeStaticBoxSizer("Box 3", labels[6:9]) # We can also use a sizer to manage the placement of other # sizers (and therefore the windows and sub-sizers that they # manage as well.) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.Add(box1, 0, wx.ALL, 10) sizer.Add(box2, 0, wx.ALL, 10) sizer.Add(box3, 0, wx.ALL, 10) self.panel.SetSizer(sizer) sizer.Fit(self) def MakeStaticBoxSizer(self, boxlabel, itemlabels): # first the static box box = wx.StaticBox(self.panel, -1, boxlabel) # then the sizer sizer = wx.StaticBoxSizer(box, wx.VERTICAL) # then add items to it like normal for label in itemlabels: bw = BlockWindow(self.panel, label=label) sizer.Add(bw, 0, wx.ALL, 2) return sizer app = wx.PySimpleApp() TestFrame().Show() app.MainLoop() ``` `static` `box` `sizer`是類`wx.StaticBoxSizer`的實例,`wx.StaticBoxSizer`是`wx.BoxSizer`的子類。它的構造函數要求的參數是靜態框和方向: `wx.StaticBoxSizer(box`, `oient)` 在這個構造函數中,`orient`的意義與原`wx.BoxSizer`相同,`box`參數是一個`wx.StaticBox`。對于`static` `box` `sizer`所定義的別的方法只有一個:`GetStaticBox()`,它返回用于建造該`sizer`的`wx.StaticBox`。一旦該`sizer`被創建,那么你就不能再改變這個靜態框了。 `wx.StaticBox`類有一個用于`wxPython`控件的標準的構造函數,但是其中許多參數都有默認值,可以忽略。 `wx.StaticBox(parent`, `id`, `label`, `pos`=`wx.DefaultPosition`, * `size`=`wx.DefaultSize`, `style`=0, `name`="`staticBox`") 使用一個`static` `box` `sizer`,你不需要去設置`pos`, `size`, `style`, 或`name`參數,因為位置和尺寸將由`sizer`管理,并且沒用單獨用于`wx.StaticBox`的樣式。這使得構造更簡單: `box` = `wx.StaticBox(self.panel`, -1, `boxlabel)` 到目前為止,我們已經展示了各種`sizer`,我們將給你展示如何在實際的布局中使用它們。對于另外一個用于創建一個復雜布局的例子,請參看第六章。 ## 一個現實中使用sizer的例子 迄今為止,我們所展示的有關`sizer`的例子都是在顯示它們的功能方面。下面,我們將展示一個如何使用`sizer`來建造一個真實的布局。圖11.16顯示了一個使用不同`sizer`建造的復雜程度適中的布局。 **圖11.16** ![](https://box.kancloud.cn/2016-08-21_57b99646012bd.gif) 例11.11顯示了產生上圖的代碼。這段代碼看起來有點復雜,但是我們將對它分塊解讀。 **例11.11** **用`sizer`來建造地址表單** ``` #coding=utf-8 #!python import wx class TestFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, -1, "Real World Test") panel = wx.Panel(self) # First create the controls topLbl = wx.StaticText(panel, -1, "Account Information")#1 創建窗口部件 topLbl.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)) nameLbl = wx.StaticText(panel, -1, "Name:") name = wx.TextCtrl(panel, -1, ""); addrLbl = wx.StaticText(panel, -1, "Address:") addr1 = wx.TextCtrl(panel, -1, ""); addr2 = wx.TextCtrl(panel, -1, ""); cstLbl = wx.StaticText(panel, -1, "City, State, Zip:") city = wx.TextCtrl(panel, -1, "", size=(150,-1)); state = wx.TextCtrl(panel, -1, "", size=(50,-1)); zip = wx.TextCtrl(panel, -1, "", size=(70,-1)); phoneLbl = wx.StaticText(panel, -1, "Phone:") phone = wx.TextCtrl(panel, -1, ""); emailLbl = wx.StaticText(panel, -1, "Email:") email = wx.TextCtrl(panel, -1, ""); saveBtn = wx.Button(panel, -1, "Save") cancelBtn = wx.Button(panel, -1, "Cancel") # Now do the layout. # mainSizer is the top-level one that manages everything #2 垂直的sizer mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(topLbl, 0, wx.ALL, 5) mainSizer.Add(wx.StaticLine(panel), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) # addrSizer is a grid that holds all of the address info #3 地址列 addrSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5) addrSizer.AddGrowableCol(1) addrSizer.Add(nameLbl, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) addrSizer.Add(name, 0, wx.EXPAND) addrSizer.Add(addrLbl, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) addrSizer.Add(addr1, 0, wx.EXPAND) #4 帶有空白空間的行 addrSizer.Add((10,10)) # some empty space addrSizer.Add(addr2, 0, wx.EXPAND) addrSizer.Add(cstLbl, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) # the city, state, zip fields are in a sub-sizer #5 水平嵌套 cstSizer = wx.BoxSizer(wx.HORIZONTAL) cstSizer.Add(city, 1) cstSizer.Add(state, 0, wx.LEFT|wx.RIGHT, 5) cstSizer.Add(zip) addrSizer.Add(cstSizer, 0, wx.EXPAND) #6 電話和電子郵箱 addrSizer.Add(phoneLbl, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) addrSizer.Add(phone, 0, wx.EXPAND) addrSizer.Add(emailLbl, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) addrSizer.Add(email, 0, wx.EXPAND) # now add the addrSizer to the mainSizer #7 添加Flex sizer mainSizer.Add(addrSizer, 0, wx.EXPAND|wx.ALL, 10) # The buttons sizer will put them in a row with resizeable # gaps between and on either side of the buttons #8 按鈕行 btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add((20,20), 1) btnSizer.Add(saveBtn) btnSizer.Add((20,20), 1) btnSizer.Add(cancelBtn) btnSizer.Add((20,20), 1) mainSizer.Add(btnSizer, 0, wx.EXPAND|wx.BOTTOM, 10) panel.SetSizer(mainSizer) # Fit the frame to the needs of the sizer. The frame will # automatically resize the panel as needed. Also prevent the # frame from getting smaller than this size. mainSizer.Fit(self) mainSizer.SetSizeHints(self) app = wx.PySimpleApp() TestFrame().Show() app.MainLoop() ``` **#1** 代碼的第一部分是創建使用在窗口中的窗口部件,它們在這行開始。我們在增加`sizer`前將它們全部創建。 **#2** 在這個布局中的主`sizer`是`mainSizer`,它是一個豎直的`box` `sizer`。被添加到`mainSizer`的第一個元素是頂部的靜態文本標簽和一個`static` `line`。 **#3** 在`box` `sizer`中接下來的元素是`addrSizer`,它是一個`flex` `grid` `sizer`,它有兩列,它兩列用于容納其余的地址信息。`addrSizer`的左列被設計用于靜態文本標簽,而右列用于得到文本控件。這意味著標簽和控件需要被交替的添加,以保證`grid`的正確。你可以看到`nameLbl`, `name`, `addrLbl`, 和`addr1`是首先被添加到該`flex` `grid`中的四個元素。 **#4** 這接下來的行是不同的,因為這第二個地址行沒有標簽,一個(10,10)尺寸的空白塊被添加,然后是`addr2`控件。 **#5** 接下來的行又有所不同,包括“`City`, `State`, `Zip`”的行要求三個不同的文本控件,基于這種情況,我們創建了一個水平的`box` `sizer`:`cstSizer`。這三個控件被添加給`cstSizer`,然后這個`box` `sizer`被添加到`addrSizer`。 **#6** 電話和電子郵箱行被添加到`flex` `sizer`。 **#7** 有關地址的`flex` `sizer`被正式添加到主`sizer`。 **#8** 按鈕行作為水平`box` `sizer`被添加,其中有一些空白元素以分隔按鈕。 在調整了`sizer`(`mainSizer.Fit(self)`)和防止框架變得更小之后(`mainSizer.SetSizeHints(self)`),元素的布局就結束了。 在讀這接下來的段落或運行這個例子之前,請試著想出該框架將會如何在水平和豎直方向上響應增長。 如果該窗口在豎直方向上的大小改變了,其中的元素不會移動。這是因為主`sizer`是一個垂直的`box` `sizer`,你是在它的主方向上改變尺寸,它沒有一個頂級元素是以大于0的比列被添加的。如果這個窗口在水平方向被調整尺寸,由于這個主`sizer`是一個垂直的`box` `sizer`,你是在它的次方向改變尺寸,因此它的所有有`wx.EXPAND`標記的元素將水平的伸展。這意味著頂部的標簽不增長,但是`static` `line`和子`sizer`將水平的增長。用于地址的`flex` `grid` `sizer`指定列1是可增長的,這意味著包含文本控件的第二列將增長。在“`City`, `State`, `Zip`” 行內,比列為1的`city`元素將伸展,而`state`和`ZIP`控件將保持尺寸不變。按鈕將保持原有的尺寸,因為它們的比列是0,但是按鈕所在行的空白空間將等分地占據剩下的空間,因為它們每個的比列都是1。 因此如果你想出的窗口伸展的樣子和下圖11.17相同,那么你是正確的。 **圖11.17** ![](https://box.kancloud.cn/2016-08-21_57b9964613709.gif) ## 本章小結 1、`Sizer`是對`wxPython`程序中管理布局問題的解決方法。不用手動指定每個元素在布局中的位置和尺寸,你可以將元素添加到一個`sizer`中,由`sizer`負責將每個元素放置到屏幕上。當用戶調整框架的尺寸時,`sizer`管理布局是相當好的。 2、所有的`wxPython`的`sizer`都是類`wx.Sizer`的一個子類的實例。要使用一個`sizer`,你需要把它與一個容器型的窗口部件關聯起來。然后,對于要添加到容器中的子窗口部件,你也必需將它們添加到該`sizer`。最后,調用該`sizer`的`Fit()`方法來觸發該`sizer`的算法,以便布局和放置。 3、所有的`sizer`開始都給它們的孩子以最小的尺寸。每種`sizer`各自使用不同的機制來放置窗口部件,所以相同組的窗口部件放置在不同的`sizer`中時,它們的外觀也是不同的。 4、或許在`wxPython`中,最簡單的`sizer`是`grid` `sizer(wx.GridSizer)`。在`grid` `sizer`中,元素按照它們被添加給`sizer`的順序被放置在一個二維的網格中,按照從左到右,從上到下的方式排列。通常你負責設定列數,`sizer`確定行數。你能夠同時指定行和列,如果你愿意的話。 5、所有的`sizer`都有各自不同的用來將窗口部件添加到`sizer`的方法。由于窗口部件添加到`sizer`中的順序對于最后的布局是重要的,所以有不同的方法用來添加一個新的窗口部件到列表中的前面、后面或任意點。在一個窗口部件被添加到`sizer`時,另外的屬性可以被設置,它們控制當`sizer`增減時,其中的子元素如何變化。`sizer`也能夠被配置來在對象的某些或全部邊上放置一個邊界間隙。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看