图形故障 ttk 小部件
Graphic glitch ttk widgets
这一切都源于这样一个事实,即正常的 tk.Scale
没有被正确地恐惧(我使用的是自定义主题)。
然后我切换到 ttk.Scale
但是当我更改它时它没有显示滑块上方的值。
然后我发现了 ttkwidget,它似乎可以工作,但有图形故障。有人有任何解决办法吗?
代码:https://pastebin.com/bxgvsjSF
截图:https://ibb.co/tLdjLrx
P.S。此外,有问题的小部件加载小部件非常慢
编辑:解决方案在最佳答案的评论中
我找到了一种方法来消除 ttk.LabeledScale
中的故障。我认为问题是整个 Label
小部件的重绘,所以我改用了 Canvas
。使用Canvas
,文字移动时,无需重新绘制背景,动画更流畅。
下面的代码是基于ttk.LabeledScale
的源代码(来自Python 3.9.1,你可以在tkinter/ttk.py中找到它)。但是小部件现在基于 Canvas
,其中比例和文本添加了 create_window()
和 ćreate_text()
。我修改了 __init__()
和 _adjust()
方法并添加了一个 _on_theme_change()
方法,该方法在主题更改时调用以更新 canvas 的样式并调整元素的位置.
import tkinter as tk
from tkinter import ttk
class LabeledScale(tk.Canvas):
def __init__(self, master=None, variable=None, from_=0, to=10, **kw):
self._label_top = kw.pop('compound', 'top') == 'top'
tk.Canvas.__init__(self, master, **kw)
self._variable = variable or tk.IntVar(master)
self._variable.set(from_)
self._last_valid = from_
# use style to set the Canvas background color
self._style = ttk.Style(self)
self.configure(bg=self._style.lookup('Horizontal.TScale', 'background'))
# create the scale
self.scale = ttk.Scale(self, variable=self._variable, from_=from_, to=to)
self.scale.bind('<<RangeChanged>>', self._adjust)
# put scale in canvas
self._scale = self.create_window(0, 0, window=self.scale, anchor='nw')
# put label in canvas (the position will be updated later)
self._label = self.create_text(0, 0, text=self._variable.get(),
fill=self._style.lookup('TLabel', 'foreground'),
anchor='s' if self._label_top else 'n')
# adjust canvas height to fit the whole content
bbox = self.bbox(self._label)
self.configure(width=self.scale.winfo_reqwidth(),
height=self.scale.winfo_reqheight() + bbox[3] - bbox[1])
# bindings and trace to update the label
self.__tracecb = self._variable.trace_variable('w', self._adjust)
self.bind('<Configure>', self._adjust)
self.bind('<Map>', self._adjust)
# update sizes, positions and appearances on theme change
self.bind('<<ThemeChanged>>', self._on_theme_change)
def destroy(self):
"""Destroy this widget and possibly its associated variable."""
try:
self._variable.trace_vdelete('w', self.__tracecb)
except AttributeError:
pass
else:
del self._variable
super().destroy()
self.label = None
self.scale = None
def _on_theme_change(self, *args):
"""Update position and appearance on theme change."""
def adjust_height():
bbox = self.bbox(self._label)
self.configure(height=self.scale.winfo_reqheight() + bbox[3] - bbox[1])
self.configure(bg=self._style.lookup('Horizontal.TScale', 'background'))
self.itemconfigure(self._label, fill=self._style.lookup('TLabel', 'foreground'))
self._adjust()
self.after_idle(adjust_height)
def _adjust(self, *args):
"""Adjust the label position according to the scale."""
def adjust_label():
self.update_idletasks() # "force" scale redraw
x, y = self.scale.coords()
if self._label_top:
y = 0
else:
y = self.scale.winfo_reqheight()
# avoid that the label goes off the canvas
bbox = self.bbox(self._label)
x = min(max(x, 0), self.winfo_width() - (bbox[2] - bbox[0])/2)
self.coords(self._label, x, y) # move label
self.configure(scrollregion=self.bbox('all'))
self.yview_moveto(0) # make sure everything is visible
self.itemconfigure(self._scale, width=self.winfo_width())
from_ = ttk._to_number(self.scale['from'])
to = ttk._to_number(self.scale['to'])
if to < from_:
from_, to = to, from_
newval = self._variable.get()
if not from_ <= newval <= to:
# value outside range, set value back to the last valid one
self.value = self._last_valid
return
self._last_valid = newval
self.itemconfigure(self._label, text=newval)
self.after_idle(adjust_label)
@property
def value(self):
"""Return current scale value."""
return self._variable.get()
@value.setter
def value(self, val):
"""Set new scale value."""
self._variable.set(val)
root = tk.Tk()
style = ttk.Style(root)
style.theme_use('alt')
scale = LabeledScale(root, from_=0, to=100, compound='bottom')
scale.pack(expand=True, fill='x')
root.mainloop()
这一切都源于这样一个事实,即正常的 tk.Scale
没有被正确地恐惧(我使用的是自定义主题)。
然后我切换到 ttk.Scale
但是当我更改它时它没有显示滑块上方的值。
然后我发现了 ttkwidget,它似乎可以工作,但有图形故障。有人有任何解决办法吗?
代码:https://pastebin.com/bxgvsjSF
截图:https://ibb.co/tLdjLrx
P.S。此外,有问题的小部件加载小部件非常慢
编辑:解决方案在最佳答案的评论中
我找到了一种方法来消除 ttk.LabeledScale
中的故障。我认为问题是整个 Label
小部件的重绘,所以我改用了 Canvas
。使用Canvas
,文字移动时,无需重新绘制背景,动画更流畅。
下面的代码是基于ttk.LabeledScale
的源代码(来自Python 3.9.1,你可以在tkinter/ttk.py中找到它)。但是小部件现在基于 Canvas
,其中比例和文本添加了 create_window()
和 ćreate_text()
。我修改了 __init__()
和 _adjust()
方法并添加了一个 _on_theme_change()
方法,该方法在主题更改时调用以更新 canvas 的样式并调整元素的位置.
import tkinter as tk
from tkinter import ttk
class LabeledScale(tk.Canvas):
def __init__(self, master=None, variable=None, from_=0, to=10, **kw):
self._label_top = kw.pop('compound', 'top') == 'top'
tk.Canvas.__init__(self, master, **kw)
self._variable = variable or tk.IntVar(master)
self._variable.set(from_)
self._last_valid = from_
# use style to set the Canvas background color
self._style = ttk.Style(self)
self.configure(bg=self._style.lookup('Horizontal.TScale', 'background'))
# create the scale
self.scale = ttk.Scale(self, variable=self._variable, from_=from_, to=to)
self.scale.bind('<<RangeChanged>>', self._adjust)
# put scale in canvas
self._scale = self.create_window(0, 0, window=self.scale, anchor='nw')
# put label in canvas (the position will be updated later)
self._label = self.create_text(0, 0, text=self._variable.get(),
fill=self._style.lookup('TLabel', 'foreground'),
anchor='s' if self._label_top else 'n')
# adjust canvas height to fit the whole content
bbox = self.bbox(self._label)
self.configure(width=self.scale.winfo_reqwidth(),
height=self.scale.winfo_reqheight() + bbox[3] - bbox[1])
# bindings and trace to update the label
self.__tracecb = self._variable.trace_variable('w', self._adjust)
self.bind('<Configure>', self._adjust)
self.bind('<Map>', self._adjust)
# update sizes, positions and appearances on theme change
self.bind('<<ThemeChanged>>', self._on_theme_change)
def destroy(self):
"""Destroy this widget and possibly its associated variable."""
try:
self._variable.trace_vdelete('w', self.__tracecb)
except AttributeError:
pass
else:
del self._variable
super().destroy()
self.label = None
self.scale = None
def _on_theme_change(self, *args):
"""Update position and appearance on theme change."""
def adjust_height():
bbox = self.bbox(self._label)
self.configure(height=self.scale.winfo_reqheight() + bbox[3] - bbox[1])
self.configure(bg=self._style.lookup('Horizontal.TScale', 'background'))
self.itemconfigure(self._label, fill=self._style.lookup('TLabel', 'foreground'))
self._adjust()
self.after_idle(adjust_height)
def _adjust(self, *args):
"""Adjust the label position according to the scale."""
def adjust_label():
self.update_idletasks() # "force" scale redraw
x, y = self.scale.coords()
if self._label_top:
y = 0
else:
y = self.scale.winfo_reqheight()
# avoid that the label goes off the canvas
bbox = self.bbox(self._label)
x = min(max(x, 0), self.winfo_width() - (bbox[2] - bbox[0])/2)
self.coords(self._label, x, y) # move label
self.configure(scrollregion=self.bbox('all'))
self.yview_moveto(0) # make sure everything is visible
self.itemconfigure(self._scale, width=self.winfo_width())
from_ = ttk._to_number(self.scale['from'])
to = ttk._to_number(self.scale['to'])
if to < from_:
from_, to = to, from_
newval = self._variable.get()
if not from_ <= newval <= to:
# value outside range, set value back to the last valid one
self.value = self._last_valid
return
self._last_valid = newval
self.itemconfigure(self._label, text=newval)
self.after_idle(adjust_label)
@property
def value(self):
"""Return current scale value."""
return self._variable.get()
@value.setter
def value(self, val):
"""Set new scale value."""
self._variable.set(val)
root = tk.Tk()
style = ttk.Style(root)
style.theme_use('alt')
scale = LabeledScale(root, from_=0, to=100, compound='bottom')
scale.pack(expand=True, fill='x')
root.mainloop()