首页 > 解决方案 > 在 tkinter 中调整 PanedWindow 大小时的视觉伪影

问题描述

我正在开发 tkinter 中的应用程序。无法发布代码,因为它太复杂并且在简化版本中看不到工件。

您可以在附加的视频中看到,如果我调整整个窗口的大小,它看起来还不错。它不是完美的光滑,但没关系。但是,当我使用 PanedWindos 窗扇调整大小时,在调整大小期间会看到很多伪影。最后,当我释放鼠标按钮时,它看起来还不错。

你有这方面的经验吗?一些提示什么可能导致这种行为?
调整大小的 youtube 视频

调整大小

编辑:

我创建了重现该行为的最小版本。ScrollFrame 类使工件更加明显。

import tkinter as tk
import random


class ScrollFrame(tk.Frame):
    def __init__(self, parent, **kwargs):
        super().__init__(parent, **kwargs)

        self.canvas = tk.Canvas(self, borderwidth=0, highlightthickness=0)
        self.canvas.bind("<Enter>", self._bind_mouse)
        self.canvas.bind("<Leave>", self._unbind_mouse)
        self.viewPort = tk.Frame(self.canvas)
        self.vsb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=self.vsb.set)

        self.vsb.pack(side="right", fill="y")
        self.canvas.pack(side="left", fill="both", expand=True)
        self.canvas_window = self.canvas.create_window((4, 4), window=self.viewPort,
                                                       anchor="nw",
                                                       tags="self.viewPort",
                                                       )

        self.viewPort.bind("<Configure>", self.onFrameConfigure)
        self.canvas.bind("<Configure>", self.onCanvasConfigure)

        self.onFrameConfigure(None)

    def onFrameConfigure(self, event):
        '''Reset the scroll region to encompass the inner frame'''
        self.canvas.configure(scrollregion=self.canvas.bbox(
            "all"))

    def onCanvasConfigure(self, event):
        '''Reset the canvas window to encompass inner frame when required'''
        canvas_width = event.width
        self.canvas.itemconfig(self.canvas_window,
                               width=canvas_width)

    def _bind_mouse(self, event=None):
        self.canvas.bind_all("<4>", self._on_mousewheel)
        self.canvas.bind_all("<5>", self._on_mousewheel)
        self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)

    def _unbind_mouse(self, event=None):
        self.canvas.unbind_all("<4>")
        self.canvas.unbind_all("<5>")
        self.canvas.unbind_all("<MouseWheel>")

    def _on_mousewheel(self, event):
        """Linux uses event.num; Windows / Mac uses event.delta"""
        if self.vsb.get() != (0, 1):
            if event.num == 4 or event.delta > 0:
                self.canvas.yview_scroll(-1, "units")
            elif event.num == 5 or event.delta < 0:
                self.canvas.yview_scroll(1, "units")


class OptionAttribute:
    def __init__(self, target_widget, name, options, unit):
        self.name = tk.Label(target_widget, text=name)
        self.input = tk.Button(target_widget, text=options[0], anchor='w',
                               command=lambda: self.show_options(self.input), relief='flat', bg='white', bd=0)
        self.menu = tk.Menu(target_widget, tearoff=0)
        for o in options:
            self.menu.add_command(label=o, command=lambda selected=o: self.input.configure(text=selected))
        self.unit = tk.Label(target_widget, text=unit)

    def show_options(self, widget):
        self.input.focus_set()
        self.menu.post(widget.winfo_rootx(), widget.winfo_rooty())


if __name__ == '__main__':
    root = tk.Tk()
    root.minsize(width=640, height=480)
    root.grid_columnconfigure(0, weight=1)
    root.grid_rowconfigure(0, weight=1)

    pw = tk.PanedWindow(root, orient='horizontal', sashrelief='ridge', sashwidth=8)
    f1 = tk.Frame(pw)
    pw.add(f1, stretch='always', minsize=200, width=400)

    f2 = tk.Frame(pw)
    f2.grid_columnconfigure(0, weight=1)
    f2.grid_rowconfigure(0, weight=1)

    f2_scroll = ScrollFrame(f2)
    f2_scorr_inner = f2_scroll.viewPort
    f2_scorr_inner.grid_columnconfigure(1, weight=80)
    f2_scorr_inner.grid_columnconfigure(2, weight=20)
    f2_scroll.grid(sticky='nsew')

    option_attrs = [OptionAttribute(f2_scorr_inner,
                                    str(random.randint(0, 100000)),
                                    [str(random.randint(0, 100000)) for __ in range(10)],
                                    random.choice(['mm', 'nm', 'kg', 'h', 'km', ])
                                    ) for _ in range(100)]
    r = 0
    for oa in option_attrs:
        oa.name.grid(column=0, row=r, sticky='w', padx=(0, 10))
        oa.input.grid(column=1, row=r, sticky='w')
        oa.unit.grid(column=2, row=r, sticky='w')
        r += 1
        tk.Frame(f2_scorr_inner, height=1, bg="gray80").grid(column=0, row=r, sticky="ew", columnspan=3)
        r += 1

    pw.add(f2, stretch='always', minsize=200, width=400)
    pw.grid(sticky='nsew')

    root.mainloop()

在此处输入图像描述

标签: pythontkintervisual-artifacts

解决方案


这不是一个解决方案,而是一种掩盖 gui 对用户的丑陋行为的方法。

pw = PanedWindow()    
pw.configure(opaqueresize=False)

推荐阅读