I have following code:
import Tkinter as tk
import locale
from Tkinter import *
#locale.setlocale(locale.LC_NUMERIC, 'pl_PL')
master = tk.Tk()
w = tk.Scale(master, from_=0.05, to=0.1, resolution=0.01)
w.pack()
tk.mainloop()
And the slider works like it should. It doesn't slide when I uncomment that line with locale setting. That is probably due to the fact, that pl_PL
locale uses comma for float separation. This may be a bug. How can I work around it so I can have the locale set properly?
Here's a slightly clunky workaround for this bug, using some ideas & code from Bryan Oakley's answer to Dynamically reformat tkinter scale value python 2.7, where Bryan shows how to replace the usual value display with a custom formatted display. Unfortunately, we need to do a little more work here because even when we create Scale with showvalue=False
it still gets blocked by float numbers that contain commas, even though it's not even showing them!
The solution is to force the numbers to be integers. That's easy enough to do if the to
and from_
values are whole number multiples of resolution
, as shown below.
import Tkinter as tk
import locale
locale.setlocale(locale.LC_NUMERIC, 'pl_PL.UTF8')
class NewScale(tk.Frame):
def __init__(self, master=None, **options):
tk.Frame.__init__(self, master)
# Disable normal value display...
options['showvalue'] = False
# ... and use custom display instead
options['command'] = self._on_scale
# Set resolution to 1 and adjust to & from value
self.res = options.get('resolution', 1)
from_ = int(options.get('from_', 0) / self.res)
to = int(options.get('to', 100) / self.res)
options.update({'resolution': 1, 'to': to, 'from_': from_})
# This could be improved...
if 'digits' in options:
self.digits = ['digits']
del options['digits']
else:
self.digits = 2
self.scale = tk.Scale(self, **options)
self.scale_label = tk.Label(self)
orient = options.get('orient', tk.VERTICAL)
if orient == tk.VERTICAL:
side, fill = 'right', 'y'
else:
side, fill = 'top', 'x'
self.scale.pack(side=side, fill=fill)
self.scale_label.pack(side=side)
def _on_scale(self, value):
value = locale.atof(value) * self.res
value = locale.format_string('%.*f', (self.digits, value))
self.scale_label.configure(text=value)
if __name__ == '__main__':
master = tk.Tk()
w = NewScale(master, from_=0.05, to=0.1, resolution=0.01)
w.pack(fill='both', expand=True)
master.mainloop()
This code has been tested on Python 2.6.6 and 3.6.0. To run it on Python 3, change import Tkinter as tk
to import tkinter as tk
.
The NewScale widget supports both tk.VERTICAL
and tk.HORIZONTAL
orientations, with tk.VERTICAL
being the default (the same as the normalScale widget). Its support for the digits
option is currently rather primitive.
Here are a couple of methods that make NewScale a little more useful:
def get(self):
return self.scale.get() * self.res
def set(self, value):
self.scale.set(int(0.5 + value / self.res))
To test those methods, change the calling code to:
if __name__ == '__main__':
master = tk.Tk()
w = NewScale(master, from_=0.05, to=0.1, resolution=0.01)
w.pack(fill='both', expand=True)
w.set(0.07)
tk.Button(master, text='Print', command=lambda: print(w.get())).pack()
master.mainloop()
And it's probably a Good Idea to round the adjusted to
& from_
values to the nearest integer, rather than truncating them. That can be done by changing their initializers to:
from_ = int(0.5 + options.get('from_', 0) / self.res)
to = int(0.5 + options.get('to', 100) / self.res)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With