【问题标题】:Force TkInter Scale slider to snap to mouse强制 TkInter Scale 滑块与鼠标对齐
【发布时间】:2017-08-06 15:42:43
【问题描述】:

当一个 GUI 有一个 TkInter Scale 并且他们单击刻度上的某个位置时,默认行为似乎是沿着刻度向鼠标方向滑动滑块(然后意外地滑过他们的鼠标)。

我想要的是让滑块在用户单击滑块上的任意位置时始终跳转到并保持连接到用户的鼠标点。如果他们点击 Scale 上的特定点,滑块应直接跳到该点。

我在下面有一些代码尝试执行此操作,但似乎不起作用,我找不到原因。

import tkinter as tk
from tkinter import ttk

def show_values():
    print('w1 set to',w1.get())

def snapToVal1(val):
    scaleVal = float(w1.get())
    if int(scaleVal) != scaleVal:
        w1.set(round(float(val)))

def scaleFunc1(event):
    g = w1.grid_info()
    w1.set(round(8 * (event.y - g['pady'])/(w1.winfo_height() - 2*g['pady'] - 2*g['ipady']))-1)
    print('w1 set to',w1.get())

#---
root = tk.Tk()

f1 = ttk.Frame(root, relief = tk.GROOVE)

ttk.Label(f1, text='Stellar\nType').grid(row=0,column=0, columnspan=2,padx=2,pady=2)

for i,text in enumerate(['O','B','A','F','G','K','M','L']):
    ttk.Label(f1, text = text).grid(row=i+1,column=0,pady=5,padx=(2,0))

w1 = ttk.Scale(f1, to=7, command=snapToVal1, orient=tk.VERTICAL)
w1.grid(row = 1, column = 1, rowspan = 8, pady=5, padx=2, sticky='nsew')
w1.bind('<Button-1>',scaleFunc1)

f1.grid(row = 0, column = 0,padx=(2,1),pady=2,sticky='nsew')

ttk.Button(root, text='Show', command=show_values).grid(row=1,column=0)

root.mainloop()

这里的相关函数是scaleFunc1。这个想法是每当用户在秤上按下鼠标按钮时调用它。然后,它尝试根据事件像素位置和比例大小计算在比例上单击的小数位置,将其转​​换为比例值,并将其设置为用户点击的那个值。但是,我发现滑块并不总是跳到同一个地方,即使它报告它被设置为我期望的值。怎么回事?

我怀疑这与用户按住鼠标按钮的几分之一秒内仍试图移动的幻灯片有关。

【问题讨论】:

    标签: python tkinter


    【解决方案1】:

    这实际上是默认的右键单击行为。如果您也想让左键单击,那么最简单的方法是简单地检测左键并告诉 tkinter 它是右键单击:

    import tkinter as tk
    from tkinter import ttk
    
    class Scale(ttk.Scale):
        """a type of Scale where the left click is hijacked to work like a right click"""
        def __init__(self, master=None, **kwargs):
            ttk.Scale.__init__(self, master, **kwargs)
            self.bind('<Button-1>', self.set_value)
    
        def set_value(self, event):
            self.event_generate('<Button-3>', x=event.x, y=event.y)
            return 'break'
    
    def show_values():
        print('w1 set to',w1.get())
    
    root = tk.Tk()
    
    f1 = ttk.Frame(root, relief = tk.GROOVE)
    
    ttk.Label(f1, text='Stellar\nType').grid(row=0,column=0, columnspan=2,padx=2,pady=2)
    
    for i,text in enumerate(['O','B','A','F','G','K','M','L']):
        ttk.Label(f1, text = text).grid(row=i+1,column=0,pady=5,padx=(2,0))
    
    w1 = Scale(f1, to=7, orient=tk.VERTICAL)
    w1.grid(row = 1, column = 1, rowspan = 8, pady=5, padx=2, sticky='nsew')
    
    f1.grid(row = 0, column = 0,padx=(2,1),pady=2,sticky='nsew')
    
    ttk.Button(root, text='Show', command=show_values).grid(row=1,column=0)
    
    root.mainloop()
    

    【讨论】:

    • 太棒了!这比我尝试的复杂方法要简单得多。
    【解决方案2】:
    from tkinter import *
    
    root = Tk()
    root.geometry('500x200')
    
    scale = Scale(to=100, length=300, orient=HORIZONTAL)
    scale.pack()
    
    def move_to_click(event):
        new_coord = (event.x - 18 + (event.x / 10)) / (300 / 100)
        scale.set(new_coord)        
    
    scale.bind("<Button-1>", move_to_click) 
    
    root.mainloop()
    

    【讨论】:

    • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
    • 虽然您的解决方案可能会奏效,但请在答案中添加一些解释和支持链接或信息
    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 2013-01-08
    • 1970-01-01
    • 1970-01-01
    • 2019-09-18
    • 1970-01-01
    • 1970-01-01
    • 2021-01-08
    • 2022-01-22
    • 1970-01-01
    相关资源
    最近更新 更多