如何在tkinter中使用`pack_propagate`方法来控制单方向布局?

huangapple go评论68阅读模式
英文:

How to tkinter pack_propagate in one direction?

问题

以下是您要翻译的代码部分:

class MyWindow(tk.Tk):
   def __init__(self):
      super().__init__();
      self.title("My Window");

      # Setting up main frame on window
      self.main_frame = tk.Frame(self.master);
      self.main_frame.pack(fill='both', expand=True);

      self.main_frame.pack_propagate(False);
      self.main_frame.configure(width=500, height=500);

      # Header Frame
      self.header = tk.Frame(self.main_frame, height=50);
      self.header.pack(side='top', fill='both');
      self.header.pack_propagate(False);

      # Some Header Widgets
      self.button1 = tk.Button(self.header, text="button1");
      self.button1.pack(side='left');
      
      self.button2 = tk.Button(self.header, text="button2");
      self.button2.pack(side='left');

      # Body Frame
      self.body = tk.Frame(self.main_frame);
      self.body.pack(side='top', fill='both', expand=True);
      self.body.pack_propagate(False);

      # Scrollable Body Frame Canvas
      self.canvas = tk.Canvas(self.body, bg='red');
      self.canvas.pack(side='left', fill='both', expand=True);

      self.scrollbar = tk.Scrollbar(self.body, command=self.canvas.yview);
      self.scrollbar.pack(side='right', fill='y');
      
      self.canvas.configure(yscrollcommand=self.scrollbar.set);
      self.canvas.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox('all')));

      self.canvas_frame = tk.Frame(self.canvas);
      self.canvas.create_window((0, 0), window=self.canvas_frame, anchor='nw');

      self.item_list = [];
      for i in range(10):
         self.item_list.append(self.A(self, i));

   class A(tk.Frame):
      def __init__(self, parent, index):
         # Basic Info
         self.index = index;
         if self.index % 2 == 0:
            self.bg_color = 'white';
         else:
            self.bg_color = 'gray';
         
         super().__init__(parent.canvas_frame, bg=self.bg_color);
         self.pack(side="top", fill='both');

         self.parent = parent;
         self.index = index;
      
         # Sample Widgets
         tk.Label(self, text="Item" + str(self.index) + ", Label1", bg=self.bg_color).pack(side="top");
         tk.Label(self, text="Item" + str(self.index) + ", Label2", bg=self.bg_color).pack(side="top");
         tk.Label(self, text="Item" + str(self.index) + ", Label3", bg=self.bg_color).pack(side="top");

         # Plus other widgets...

if __name__ == "__main__":
    window = MyWindow()
    window.mainloop()

希望这对您有所帮助。如果您有其他问题,欢迎提出。

英文:

So here's my issue:

I have some class "A" that represents a commonly used format for a tkinter frame. Multiple instances of "A" are stacked on top of each other vertically representing a list of sorts (too complicated for tk.Listbox or Treeview, though). These instances are also supposed to function with a scrollbar, so I placed them in a canvas_frame, which itself is in a canvas, with the appropriate setup. My issue is this: I would like the width of these frames to extend to the edge of the window, however have heights automatically calculated by the usual Tkinter algorithm (however it works). To summarize in code:

'''

import tkinter as tk;
class MyWindow(tk.Tk):
def __init__(self):
super().__init__();
self.title(&quot;My Window&quot;);
# Setting up main frame on window
self.main_frame = tk.Frame(self.master);
self.main_frame.pack(fill = &#39;both&#39;, expand = True);
self.main_frame.pack_propagate(False);
self.main_frame.configure(width = 500, height = 500);
# Header Frame
self.header = tk.Frame(self.main_frame, height = 50);
self.header.pack(side = &#39;top&#39;, fill = &#39;both&#39;);
self.header.pack_propagate(False);
# Some Header Widgets
self.button1 = tk.Button(self.header, text = &quot;button1&quot;);
self.button1.pack(side = &#39;left&#39;);
self.button2 = tk.Button(self.header, text = &quot;button2&quot;);
self.button2.pack(side = &#39;left&#39;);
# Body Frame
self.body = tk.Frame(self.main_frame);
self.body.pack(side = &#39;top&#39;, fill = &#39;both&#39;, expand = True);
self.body.pack_propagate(False);
# Scrollable Body Frame Canvas
self.canvas = tk.Canvas(self.body, bg = &#39;red&#39;);
self.canvas.pack(side = &#39;left&#39;, fill = &#39;both&#39;, expand = True);
self.scrollbar = tk.Scrollbar(self.body, command = self.canvas.yview);
self.scrollbar.pack(side = &#39;right&#39;, fill = &#39;y&#39;);
self.canvas.configure(yscrollcommand = self.scrollbar.set);
self.canvas.bind(&quot;&lt;Configure&gt;&quot;, lambda e: self.canvas.configure(scrollregion = self.canvas.bbox(&#39;all&#39;)));
self.canvas_frame = tk.Frame(self.canvas);
self.canvas.create_window((0,0), window = self.canvas_frame, anchor = &#39;nw&#39;);
self.item_list = [];
for i in range(10):
self.item_list.append(self.A(self, i));
class A(tk.Frame):
def __init__(self, parent, index):
# Basic Info
self.index = index;
if self.index % 2 == 0:
self.bg_color = &#39;white&#39;;
else:
self.bg_color = &#39;gray&#39;;
super().__init__(parent.canvas_frame, bg = self.bg_color);
self.pack(side = &quot;top&quot;, fill = &#39;both&#39;);
self.parent = parent;
self.index = index;
# Sample Widgets
tk.Label(self, text = &quot;Item&quot; + str(self.index) + &quot;, Label1&quot;, bg = self.bg_color).pack(side = &quot;top&quot;);
tk.Label(self, text = &quot;Item&quot; + str(self.index) + &quot;, Label2&quot;, bg = self.bg_color).pack(side = &quot;top&quot;);
tk.Label(self, text = &quot;Item&quot; + str(self.index) + &quot;, Label3&quot;, bg = self.bg_color).pack(side = &quot;top&quot;);
# Plus other widgets...
if __name__ == &quot;__main__&quot;:
window = MyWindow()
window.mainloop()

As I stated, my goal is to have each row/"A" frame fill the window/canvas horizontally, but be distributed vertically based on their internal widgets. I belive this is handled by pack_propagate (I may be wrong).

In other words: how do I pack propagate in only one direction?

I tried two solutions:

First is to keep pack_propagate on, but to set the width of the widgets automatically. So within the MyWindow class, I added the following:

self.bind(&quot;&lt;Configure&gt;&quot;, self.resize);
def resize(self, event):
for item in self.item_list:
item.config(width = event.width);

however when I try this, I think the program ends up in a loop, where each resize changes the width of the frame (due to pack_propagate) and in turn, calls the resize function again.

My second was to turn off pack_propagate, and to try and to do the same thing, but then the height of each "A" instance is 1 and doesn't size automatically.

答案1

得分: 1

你可以在self.canvas上的&lt;Configure&gt;事件的回调函数内,将self.canvas_frame的宽度设置为self.canvas的宽度:

class MyWindow(tk.Tk):
    def __init__(self):
        ...
        #self.canvas.bind(&quot;&lt;Configure&gt;&quot;, lambda e: self.canvas.configure(scrollregion=self.canvas.bbox(&#39;all&#39;)))

        self.canvas_frame = tk.Frame(self.canvas)
        self.internal = self.canvas.create_window((0,0), window=self.canvas_frame, anchor=&#39;nw&#39;)

        self.canvas.bind(&quot;&lt;Configure&gt;&quot;, lambda e: self.canvas.itemconfig(self.internal, width=e.width))
        self.canvas_frame.bind(&quot;&lt;Configure&gt;&quot;, lambda e: self.canvas.config(scrollregion=self.canvas.bbox(&quot;all&quot;)))
        ...

请注意,canvasscrollregion应在内部框架被调整大小时更新,而不是在canvas本身被调整大小时更新。

另外,行尾的分号';'不是必需的。

英文:

You can set the width of self.canvas_frame to the width of self.canvas inside the callback of &lt;Configure&gt; event on self.canvas:

class MyWindow(tk.Tk):
    def __init__(self):
        ...
        #self.canvas.bind(&quot;&lt;Configure&gt;&quot;, lambda e: self.canvas.configure(scrollregion=self.canvas.bbox(&#39;all&#39;)))

        self.canvas_frame = tk.Frame(self.canvas);
        self.internal = self.canvas.create_window((0,0), window=self.canvas_frame, anchor=&#39;nw&#39;)

        self.canvas.bind(&quot;&lt;Configure&gt;&quot;, lambda e: self.canvas.itemconfig(self.internal, width=e.width))
        self.canvas_frame.bind(&quot;&lt;Configure&gt;&quot;, lambda e: self.canvas.config(scrollregion=self.canvas.bbox(&quot;all&quot;)))
        ...

Note that scrollregion of the canvas should be updated whenever the internal frame is resized, not the canvas itself.

Also those ; at the end of lines are not necessary.

huangapple
  • 本文由 发表于 2023年4月4日 09:16:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/75924805.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定