Ttk版本中也存在多个Tk小部件。通常它们具有相同的一般行为,但使用“样式”和“主题”而不是每个实例的外观属性(例如int[52]
等...)。这很好,因为Ttk小部件默认采用操作系统窗口管理器的“标准外观”,而无需配置任何外观。
但是,出于某种原因,bg
窗口小部件没有ttk.Scale
窗口小部件的两个非常有用的选项:tk.Scale
和showvalue
(请参阅reference) 。这很奇怪,因为那些更多的是关于行为而不是看起来。
在保持tickinterval
外观的同时“模仿”这两个选项会很棒。
以下代码是我对此的笨拙尝试。问题是:有更好的方法吗? (除了将整个事物封装在一个类中,显然)以及如何合理地获得半自动ttk
替代(而不是像下面的代码中那样“手动”)。
tickinterval
在实际“滑动”Scales之前,前一代码的结果可能如下所示,左边是Tk Scale,右边是Ttk Scale(每个OS /窗口管理器会有明显不同):
答案 0 :(得分:2)
您可以使用place
以及公式给出的位置x(以像素为单位)自动放置刻度线和显示值的标签:
x = ((value - start) / extent) * (width - sliderlength) + sliderlength / 2
使用:
value
:tick的值start
:比例的起点(即from
选项)extent
:结束 - 开始width
:比例的宽度 ((value - start) / extent)
以百分比给出位置然后,我只需要将它乘以刻度的长度,但要考虑滑块的长度。
然后将勾号放在:
place(in_=self.scale, bordermode='outside', x=x, rely=1, anchor='n')
(使用rely=0, anchor='s'
作为显示值的标签)
以下是完整的代码。我还添加了对digits
选项的支持。
import tkinter as tk
import tkinter.ttk as ttk
class TtkScale(ttk.Frame):
def __init__(self, master=None, **kwargs):
ttk.Frame.__init__(self, master)
self.columnconfigure(0, weight=1)
self.showvalue = kwargs.pop('showvalue', True)
self.tickinterval = kwargs.pop('tickinterval', 0)
self.digits = kwargs.pop('digits', '0')
if 'command' in kwargs:
# add self.display_value to the command
fct = kwargs['command']
def cmd(value):
fct(value)
self.display_value(value)
kwargs['command'] = cmd
else:
kwargs['command'] = self.display_value
self.scale = ttk.Scale(self, **kwargs)
# get slider length
style = ttk.Style(self)
style_name = kwargs.get('style', '%s.TScale' % (str(self.scale.cget('orient')).capitalize()))
self.sliderlength = style.lookup(style_name, 'sliderlength', default=30)
self.extent = kwargs['to'] - kwargs['from_']
self.start = kwargs['from_']
# showvalue
if self.showvalue:
ttk.Label(self, text=' ').grid(row=0)
self.label = ttk.Label(self, text='0')
self.label.place(in_=self.scale, bordermode='outside', x=0, y=0, anchor='s')
self.display_value(self.scale.get())
self.scale.grid(row=1, sticky='ew')
# ticks
if self.tickinterval:
ttk.Label(self, text=' ').grid(row=2)
self.ticks = []
self.ticklabels = []
nb_interv = round(self.extent/self.tickinterval)
formatter = '{:.' + str(self.digits) + 'f}'
for i in range(nb_interv + 1):
tick = kwargs['from_'] + i * self.tickinterval
self.ticks.append(tick)
self.ticklabels.append(ttk.Label(self, text=formatter.format(tick)))
self.ticklabels[i].place(in_=self.scale, bordermode='outside', x=0, rely=1, anchor='n')
self.place_ticks()
self.scale.bind('<Configure>', self.on_configure)
def convert_to_pixels(self, value):
return ((value - self.start)/ self.extent) * (self.scale.winfo_width()- self.sliderlength) + self.sliderlength / 2
def display_value(self, value):
# position (in pixel) of the center of the slider
x = self.convert_to_pixels(float(value))
# pay attention to the borders
half_width = self.label.winfo_width() / 2
if x + half_width > self.scale.winfo_width():
x = self.scale.winfo_width() - half_width
elif x - half_width < 0:
x = half_width
self.label.place_configure(x=x)
formatter = '{:.' + str(self.digits) + 'f}'
self.label.configure(text=formatter.format(float(value)))
def place_ticks(self):
# first tick
tick = self.ticks[0]
label = self.ticklabels[0]
x = self.convert_to_pixels(tick)
half_width = label.winfo_width() / 2
if x - half_width < 0:
x = half_width
label.place_configure(x=x)
# ticks in the middle
for tick, label in zip(self.ticks[1:-1], self.ticklabels[1:-1]):
x = self.convert_to_pixels(tick)
label.place_configure(x=x)
# last tick
tick = self.ticks[-1]
label = self.ticklabels[-1]
x = self.convert_to_pixels(tick)
half_width = label.winfo_width() / 2
if x + half_width > self.scale.winfo_width():
x = self.scale.winfo_width() - half_width
label.place_configure(x=x)
def on_configure(self, event):
"""Redisplay the ticks and the label so that they adapt to the new size of the scale."""
self.display_value(self.scale.get())
self.place_ticks()
if __name__ == '__main__':
root = tk.Tk()
root.geometry('400x300')
style = ttk.Style(root)
style.configure('my.Horizontal.TScale', sliderlength=10)
s1 = tk.Scale(root, orient='horizontal', tickinterval=0.2, from_=-1,
to=1, showvalue=True, resolution=0.1, sliderlength=10)
s2 = TtkScale(root, style='my.Horizontal.TScale', orient='horizontal',
tickinterval=0.2, from_=-1, to=1, showvalue=True,
digits=1)
ttk.Label(root, text='tk.Scale').pack()
s1.pack(fill='x')
ttk.Label(root, text='ttk.Scale').pack()
s2.pack(fill='x')
root.mainloop()
此小部件的更完整版本在ttkwidgets模块中以名称TickScale提供。