2014-02-10 48 views
0

我想PanedWindow小部件的子類可以按比例收縮/展開窗格。不過,我似乎無法將窗框放置到比窗口小部件要求大的座標上。演示:Tkinter PanedWindow.sash_place()失敗

from tkinter import * 

class VerticalPropPanedWindow(PanedWindow): 

    def __init__(self, parent, *args, **kwargs): 
     super().__init__(parent, *args, orient="vertical", 
      **kwargs) 
     self._default_weights, self._saved_weights = [], [] 

     self.bind("<Button-3>", self.reset) 
     self.bind("<ButtonRelease-1>", self.save_weights) 
     self.bind("<Configure>", self.changed) 

    def add(self, child, weight, **options): 
     self._default_weights.append(weight) 
     self._saved_weights.append(weight) 
     super().add(child, **options) 

    def align(self): 
     wsize = self.winfo_height() 
     print("height: {}, reqheight: {}".format(wsize, 
      self.winfo_reqheight())) 
     sumw, coords = sum(self._saved_weights), [] 
     for w in self._saved_weights[:-1]: 
      coords.append(sum(coords[-1:]) + int(wsize * w/sumw)) 
     print("aligning to: ", coords) 
     for i, c in enumerate(coords): 
      self.sash_place(i, 1, c) 
     print("after align: ", [self.sash_coord(i)[1] 
      for i in range(len(self.panes()) - 1)]) 

    def changed(self, event): 
     self.align() 

    def reset(self, event): 
     self._saved_weights = self._default_weights 
     self.align() 

    def save_weights(self, event): 
     n = len(self.panes()) - 1 
     wsize, coords = self.winfo_height(), [] 
     for i in range(n): 
      coords.append(self.sash_coord(i)[1] - sum(coords)) 
     self._saved_weights = coords + [wsize - sum(coords)] 

if __name__ == "__main__": 
    root = Tk() 
    root.p = VerticalPropPanedWindow(root, bg="black") 
    root.p.add(Label(root.p, text="1/5"), 1, sticky="nesw") 
    root.p.add(Label(root.p, text="3/5"), 3, sticky="nesw") 
    root.p.add(Label(root.p, text="1/5"), 1, sticky="nesw") 
    root.p.pack(expand=1, fill='both') 
    root.mainloop() 

嘗試調整窗口大小來體驗奇怪的行爲。通過檢查控制檯上的打印,您可以看到如果reqheight不夠好,第二個座標上的對齊失敗。

但是,通過手動拖動窗格abit然後右鍵單擊它(重置原始分佈)的作品。

我看到了兩個解決方案,在這裏:

  1. 要強制部件reqsize實際大小,而是如何?
  2. 要找到一些hacky的方式來首先拖動窗格,因爲它將由用戶完成。怎麼樣?

乾杯, 亞當

注:只有兩個窗格效果很好。

編輯:對齊():SUM(COORDS) - >總和(COORDS [-1:])

+0

對於「按比例縮小/擴大窗格」的含義,你能更具體一點嗎?你想要什麼與它已經做的不同?如果我縮小第一個窗格,是不是想讓第二個和第三個窗格的等量增長? –

+0

當我調整窗口大小時,窗格應該很好地保持1-3-1的比例。當我手動拖動一個應該定義一個新的發行版的窗格時,這個發行版將被保存並保存在隨後的調整大小。 – SzieberthAdam

回答

2

相反子類PanedWindow的,你可能要考慮子類化框架,並使用place給孩子增加框架。 Place擅長於在相對位置和相對高度放置小部件。有了這個,在窗口大小調整的時候,你不必做任何詭計。這是我們在PanedWindow小部件添加到tk之前創建窗格窗口的方式。

不利的一面是,您必須編寫代碼來繪製和響應窗框事件。我不確定哪個是更多的工作,但處理窗框非常簡單 - 只需在每個面板之間包含一個或兩個像素的框架,並設置一個綁定來調整框架上下的高度它。

這是一個非常快速的黑客,子幀的位置。它不處理窗扇,但它確實在每個窗格之間留下了兩個像素高的區域,您可以在窗格中放置窗扇。

import Tkinter as tk 

class CustomPanedWindow(tk.Frame): 
    def __init__(self, parent): 
     tk.Frame.__init__(self, parent) 
     self.panes = [] 

    def add(self, widget, weight=1): 
     '''Add a pane with the given weight''' 
     self.panes.append({"widget": widget, "weight": weight}) 
     self.layout() 

    def layout(self): 
     for child in self.place_slaves(): 
      child.place_forget() 

     total_weight = sum([pane["weight"] for pane in self.panes]) 
     rely= 0 

     for i, pane in enumerate(self.panes): 
      relheight = pane["weight"]/float(total_weight) 
      # Note: relative and absolute heights are additive; thus, for 
      # something like 'relheight=.5, height=-1`, that means it's half 
      # the height of its parent, minus one pixel. 
      if i == 0: 
       pane["widget"].place(x=0, y=0, relheight=relheight, relwidth=1.0) 
      else: 
       # every pane except the first needs some extra space 
       # to simulate a sash 
       pane["widget"].place(x=0, rely=rely, relheight=relheight, relwidth=1.0, 
            height=-2, y=2) 
      rely = rely + relheight 

class Example(tk.Frame): 
    def __init__(self, parent): 
     tk.Frame.__init__(self, parent) 

     paned = CustomPanedWindow(self) 
     paned.pack(side="top", fill="both", expand=True) 

     f1 = tk.Frame(self, background="red", width=200, height=200) 
     f2 = tk.Frame(self, background="green", width=200, height=200) 
     f3 = tk.Frame(self, background="blue", width=200, height=200) 

     paned.add(f1, 1) 
     paned.add(f2, 2) 
     paned.add(f3, 4) 

if __name__ == "__main__": 
    root = tk.Tk() 
    Example(root).pack(fill="both", expand=True) 
    root.geometry("400x900") 
    root.mainloop() 
+0

謝謝,我會嘗試使用地方。 – SzieberthAdam

+0

到目前爲止,我沒有時間去開發它。但是,我認爲我可以接受你的答案。 :) 再次感謝! – SzieberthAdam