1

I am working on an App in tkinter. Can not post the code because it is too complicated and the artifacts would not be visible in simplified version.

You can see on the attached video that if I resize the whole window, it looks not bad. It is not perfect smooth but it is ok. But when I use PanedWindos sashes to resize, a lot off artifacts is visible during resizing. In the end when I release the mouse button it looks ok.

Do you have experience with this? Some tips what could cause such a behavior?
youtube video with resizing

resizing

EDIT:

I have created minimal version which reproduces the behavior. ScrollFrame class makes the artifacts more noticeable.

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()

enter image description here

BTjacker
  • 101
  • 1
  • 5
  • Try this approach [how-to-limit-the-number-of-events-generated-by-widget](https://stackoverflow.com/questions/58906587). Relevant [python-tkinter-canvas-flickering](https://stackoverflow.com/questions/59603077) – stovfl Feb 13 '20 at 12:29
  • You are probably right. But I am still not sure how to do this. Maybe I could use grid propagate to stop prevent right windows from changing dimension so often, but it would not be smooth. I would like to know if there is a known solution rather than try to invent my own:) Or maybe I do it completely wrong way. Thank you. – BTjacker Feb 14 '20 at 09:02
  • "***" a known solution"***: The answer at the given link [how-to-limit-the-number-of-events-generated-by-widget](https://stackoverflow.com/a/59604537/7414759) shows a possible solution. – stovfl Feb 14 '20 at 09:38

1 Answers1

1

This is not a solution, but rather a way to mask gui's ugly behavior towards the user.

pw = PanedWindow()    
pw.configure(opaqueresize=False)
daikini
  • 1,307
  • 6
  • 23
  • 36