为什么ttk小部件只在配置更改事件发生后出现,而不是在按下按钮时出现?

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

Why would the ttk widget only appears after there is a configuration change event and not when the button is pressed?

问题

Can you help me understand why the ttk.Checkbutton widget does not appear after the ttk.Button widget is pressed? The ttk.Checkbutton widget would only appear when there is a configuration change event, for example after moving the sash of the ttk.PanedWindow widget or by resizing the root window. Or, when the height of self.f2 is less than the Checkbutton. This code uses a vertical scroll frame which is saved in the file named scrframe.py

Test Code:

import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
from scrframe import VerticalScrollFrame


class App(ttk.PanedWindow):

    def __init__(self, parent, orient="vertical"):
        super().__init__(parent, orient=orient)
        self.parent = parent
        self.after_id = None
        
        self.f1 = ttk.Frame(self, style="Top.TFrame")
        self.f2 = VerticalScrollFrame(self, background='green')
        self.add(self.f1)
        self.add(self.f2)
        self.b1 = ttk.Button(self.f1, text="Button", command=self.load_image,
                             width=30)
        self.b1.grid(row=0, column=0, sticky="nsew", padx=5, pady=5)
        
        self.f1.bind("<Configure>", self.schedule_event)
        self.f2.bind("<Configure>", self.schedule_event)
        self.bind("<<SashMoved>>", self.do_something)

    # Event handlers
    def schedule_event(self, event):
        if self.after_id:
            self.after_cancel(self.after_id)
        self.after_id = self.after(500, self.event_generate, "<<SashMoved>>")

    def do_something(self, event):
        print("do_something was called")

    def load_image(self):
        im = '/home/user/Pictures/image.jpeg'
        im = Image.open(im)
        im.thumbnail((200, 200))
        self.thumbnail = ImageTk.PhotoImage(im)
        self.thumbnail.image = im
        self.cb = ttk.Checkbutton(self.f2.interior, text="Image",
                                  image=self.thumbnail, compound="top")
        self.cb.grid(row=0, column=0, padx=5, pady=5)
        self.update_idletasks()  # Does not work


if __name__ == '__main__':
    root = tk.Tk()
    ss = ttk.Style()
    ss.configure('Top.TFrame', background="red")
    app = App(root)
    app.pack(fill="both", expand=True)
    root.mainloop()

Issue: Checkbutton does not appear immediately when the height of self.f2 is greater than the Checkbutton.

为什么ttk小部件只在配置更改事件发生后出现,而不是在按下按钮时出现?

Checkbutton appears immediately when the height of self.f2 is less than the Checkbutton:

为什么ttk小部件只在配置更改事件发生后出现,而不是在按下按钮时出现?

英文:

Can you help me understand why the ttk.Checkbutton widget does not appear after the ttk.Button widget is pressed? The ttk.Checkbutton widget would only appear when there is a configuration change event, for example after moving the sash of the ttk.PanedWindow widget or by resizing the root window. Or, when the height of self.f2 is less than the Checkbutton. This code uses a vertical scroll frame which is saved in the file named scrframe.py

Test Code:

import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk
from scrframe import VerticalScrollFrame
class App(ttk.PanedWindow):
def __init__(self, parent, orient=&quot;vertical&quot;):
super().__init__(parent, orient=orient)
self.parent = parent
self.after_id = None
self.f1 = ttk.Frame(self, style=&quot;Top.TFrame&quot;)
self.f2 = VerticalScrollFrame(self, background=&#39;green&#39;)
self.add(self.f1)
self.add(self.f2)
self.b1 = ttk.Button(self.f1, text=&quot;Button&quot;, command=self.load_image,
width=30)
self.b1.grid(row=0, column=0, sticky=&quot;nsew&quot;, padx=5, pady=5)
self.f1.bind(&quot;&lt;Configure&gt;&quot;, self.schedule_event)
self.f2.bind(&quot;&lt;Configure&gt;&quot;, self.schedule_event)
self.bind(&quot;&lt;&lt;SashMoved&gt;&gt;&quot;, self.do_something)
# Event handlers
def schedule_event(self, event):
if self.after_id:
self.after_cancel(self.after_id)
self.after_id = self.after(500, self.event_generate, &quot;&lt;&lt;SashMoved&gt;&gt;&quot;)
def do_something(self, event):
print(&quot;do_something was called&quot;)
def load_image(self):
im = &#39;/home/user/Pictures/image.jpeg&#39;
im = Image.open(im)
im.thumbnail((200, 200))
self.thumbnail = ImageTk.PhotoImage(im)
self.thumbnail.image = im
self.cb = ttk.Checkbutton(self.f2.interior, text=&quot;Image&quot;,
image=self.thumbnail, compound=&quot;top&quot;)
self.cb.grid(row=0, column=0, padx=5, pady=5)
self.update_idletasks()  # Does not work
if __name__ == &#39;__main__&#39;:
root = tk.Tk()
ss = ttk.Style()
ss.configure(&#39;Top.TFrame&#39;, background=&quot;red&quot;)
app = App(root)
app.pack(fill=&quot;both&quot;, expand=True)
root.mainloop()

Issue: Checkbutton does not appears immediately when the height of self.f2 is greater than the Checkbutton.

为什么ttk小部件只在配置更改事件发生后出现,而不是在按下按钮时出现?

Checkbutton appears immediately when the height of self.f2 is less than the Checkbutton:

为什么ttk小部件只在配置更改事件发生后出现,而不是在按下按钮时出现?

答案1

得分: 0

感谢@acw1668的评论。看起来问题源于scrframe.py中的._configure_canvas事件处理程序只使用了canvas.winfo_reqwidth()。下面是在释放按钮后打印出的值:

def _configure_interior(event):
    interior.winfo_reqwidth()=228 interior.winfo_reqheight()=165
    canvas.winfo_reqwidth()=1 canvas.winfo_reqheight()=223
    event.width=261 event.height=165
    do_something was called

它们显示以前包含self.interior(一个ttk.Frame小部件)的“画布窗口对象”的宽度由于canvas.winfo_reqwidth()=1而误设置为1像素宽度,因此ttk.Checkbutton小部件被隐藏了。

经过进一步测试,我发现仅仅使用event.width并不是一个健壮的解决方案。在Tk窗口宽度小于Checkbutton的宽度的情况下,当水平滚动Canvas时,Checkbutton的外观将被截断。

因此,用于“画布窗口对象”的正确宽度应该是canvas.winfo_reqwidth()event.width的最大值。

def _configure_canvas(event):
    print(f"\ndef _configure_canvas(event):")
    print(f"{interior.winfo_reqwidth()=} {interior.winfo_reqheight()=}")
    print(f"{canvas.winfo_reqwidth()=} {canvas.winfo_reqheight()=}")
    print(f"{event.width=} {event.height=}")
    if interior.winfo_reqwidth() != canvas.winfo_width():
        # 更新画布窗口对象的宽度为canvas.winfo_reqwidth()和event.width的最大值
        maxwidth = max([canvas.winfo_reqwidth(), event.width])
        canvas.itemconfigure(interior_id, width=maxwidth)

使用event.width的结果:

为什么ttk小部件只在配置更改事件发生后出现,而不是在按下按钮时出现?

使用max([canvas.winfo_reqwidth(), event.width])的结果:

为什么ttk小部件只在配置更改事件发生后出现,而不是在按下按钮时出现?

英文:

Thanks to the comments by @acw1668. It looks like the issue stems from the ._configure_canvas event handler in scrframe.py simply using canvas.winfo_reqwidth(). Below are printed-out values after the Button is released:

def _configure_interior(event):
interior.winfo_reqwidth()=228 interior.winfo_reqheight()=165
canvas.winfo_reqwidth()=1 canvas.winfo_reqheight()=223
event.width=261 event.height=165
do_something was called

They showed that previously the width of the "canvas window objected" that contained self.interior (a ttk.Frame widget) was mistakenly set to 1 pixels width since canvas.winfo_reqwidth()=1 hence the ttk.Checkbutton widget was hidden.

After further testing, I discovered that simply using event.width is not a robust solution. In the situation when the Tk window width is less than the width of the Checkbutton, the appearance of the Checkbutton will be truncated when the Canvas is scrolled horizontally.

Hence, the correct width to use for the "canvas window objected" should instead be the max of canvas.winfo_reqwidth() and event.width.

def _configure_canvas(event):
print(f&quot;\ndef _configure_canvas(event):&quot;)
print(f&quot;{interior.winfo_reqwidth()=} {interior.winfo_reqheight()=}&quot;)
print(f&quot;{canvas.winfo_reqwidth()=} {canvas.winfo_reqheight()=}&quot;)
print(f&quot;{event.width=} {event.height=}&quot;)
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the canvas window object&#39;s width to the max of canvas.winfo_reqwidth() and event.width
maxwidth = max([canvas.winfo_reqwidth(), event.width])
canvas.itemconfigure(interior_id, width=maxwidth)

Outcome from using event.width:

为什么ttk小部件只在配置更改事件发生后出现,而不是在按下按钮时出现?

Outcome from using max([canvas.winfo_reqwidth(), event.width]):
为什么ttk小部件只在配置更改事件发生后出现,而不是在按下按钮时出现?

huangapple
  • 本文由 发表于 2023年7月17日 20:56:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76704696.html
匿名

发表评论

匿名网友

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

确定