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