【问题标题】:Tkinter PanedWindow.sash_place() failsTkinter PanedWindow.sash_place() 失败
【发布时间】:2014-02-10 17:03:46
【问题描述】:

我想继承 PanedWindow 小部件,使其可以按比例缩小/扩展其窗格。但是,似乎我无法将窗扇放置在大于小部件 reqsize 的坐标上。演示:

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 不够大,第二个坐标上的对齐是如何失败的。

但是,通过手动拖动一个窗格并在此后右键单击它(这会重置原始分布)可以工作。

我在这里看到了两种解决方案:

  1. 强制小部件 reqsize 为实际大小,但如何?
  2. 首先要找到一些hacky 方法来拖动窗格,因为它会由用户完成。怎么样?

干杯, 阿达姆

注意:它只适用于两个窗格。

编辑:在 align(): sum(coords) -> sum(coords[-1:])

【问题讨论】:

  • 您能否更具体地说明“按比例缩小/扩展其窗格”的含义?你想要的和它已经做的有什么不同?如果我缩小第一个窗格,您是否希望第二个和第三个窗格都增长相同的数量?
  • 当我调整窗口大小时,窗格应该很好地保持它们的 1-3-1 比例。当我手动拖动一个窗格时,该窗格应定义一个新分布,该分布通过后续调整大小保存和保留。

标签: python python-3.x widget tkinter


【解决方案1】:

您可能需要考虑对 Frame 进行子类化,并使用 place 将子类添加到框架中,而不是子类化 PanedWindow。 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()

【讨论】:

  • 谢谢,我用place试试。
  • 到目前为止我没有时间开发这个。但是,我想我可以接受你的回答。 :) 再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-19
  • 2013-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多