问题
When a GUI has a TkInter Scale
and they click somewhere on the scale, the default behavior seems to be to slide the slider along the Scale in the direction towards the mouse (and then unexpectedly past their mouse).
What I'd want instead is to have the slider always jump to and stay attached to the user's mouse point while they're clicking anywhere on the slider. If they click to a particular point on the Scale, the slider should jump directly to that point.
I have some code below which attempts to do this but doesn't seem to work and I cannot find the reason for it.
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()
The pertinent function here is scaleFunc1
. The idea is to have this called whenever the user presses their mouse button on the scale. It then tries to calculate, from the event pixel location and the Scale size, the fractional position of the click on the scale, convert this to a scale value, and set it to that value where the user clicked. However, I'm finding that the slider doesn't always jump to the same place, even if it reports it was set to the value I'd expect. What's going on?
I suspect it has something to do with the slide still trying to move for the fraction of a second the user keeps the mouse button pressed down.
回答1:
That's actually the default right-click behavior. If you want to make the left click do that too, then the easiest thing is to simply detect leftclick and tell tkinter it was a right click instead:
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()
来源:https://stackoverflow.com/questions/42817599/force-tkinter-scale-slider-to-snap-to-mouse