Вопрос-Ответ

Interactively validating Entry widget content in tkinter

Интерактивная проверка содержимого виджета ввода в tkinter

Какой рекомендуемый метод интерактивной проверки содержимого в виджете tkinter Entry?

Я прочитал сообщения об использовании validate=True и validatecommand=command, и, похоже, что эти функции ограничены тем фактом, что они очищаются, если validatecommand команда обновляет Entry значение виджета.

Учитывая такое поведение, должны ли мы привязываться к событиям KeyPress, Cut и Paste и отслеживать / обновлять значение нашего Entry виджета с помощью этих событий? (И другие связанные события, которые я, возможно, пропустил?)

Или нам следует вообще забыть об интерактивной проверке и проверять только по FocusOut событиям?

Переведено автоматически
Ответ 1

Правильный ответ: используйте validatecommand атрибут виджета. К сожалению, эта функция сильно недооценивается в мире Tkinter, хотя она вполне достаточно документирована в мире Tk. Несмотря на то, что он плохо документирован, в нем есть все необходимое для проверки, не прибегая к привязкам или трассировке переменных, или модификации виджета в рамках процедуры проверки.

Хитрость заключается в том, чтобы знать, что вы можете попросить Tkinter передать специальные значения вашей команде validate. Эти значения предоставляют вам всю информацию, которую вам нужно знать, чтобы решить, являются ли данные допустимыми или нет: значение до редактирования, значение после редактирования, если редактирование допустимо, и несколько других битов информации. Однако, чтобы использовать их, вам нужно проделать небольшую хитрость, чтобы передать эту информацию вашей команде validate.

Примечание: важно, чтобы команда проверки возвращала либо True, либо False. Все остальное приведет к отключению проверки для виджета.

Вот пример, который допускает использование только нижнего регистра. Он также выводит значения всех специальных значений для иллюстративных целей. Они не все необходимы; вам редко требуется больше одного или двух.

import tkinter as tk  # python 3.x
# import Tkinter as tk # python 2.x

class Example(tk.Frame):

def __init__(self, parent):
tk.Frame.__init__(self, parent)

# valid percent substitutions (from the Tk entry man page)
# note: you only have to register the ones you need; this
# example registers them all for illustrative purposes
#
# %d = Type of action (1=insert, 0=delete, -1 for others)
# %i = index of char string to be inserted/deleted, or -1
# %P = value of the entry if the edit is allowed
# %s = value of entry prior to editing
# %S = the text string being inserted or deleted, if any
# %v = the type of validation that is currently set
# %V = the type of validation that triggered the callback
# (key, focusin, focusout, forced)
# %W = the tk name of the widget

vcmd = (self.register(self.onValidate),
'%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
self.entry = tk.Entry(self, validate="key", validatecommand=vcmd)
self.text = tk.Text(self, height=10, width=40)
self.entry.pack(side="top", fill="x")
self.text.pack(side="bottom", fill="both", expand=True)

def onValidate(self, d, i, P, s, S, v, V, W):
self.text.delete("1.0", "end")
self.text.insert("end","OnValidate:\n")
self.text.insert("end","d='%s'\n" % d)
self.text.insert("end","i='%s'\n" % i)
self.text.insert("end","P='%s'\n" % P)
self.text.insert("end","s='%s'\n" % s)
self.text.insert("end","S='%s'\n" % S)
self.text.insert("end","v='%s'\n" % v)
self.text.insert("end","V='%s'\n" % V)
self.text.insert("end","W='%s'\n" % W)

# Disallow anything but lowercase letters
if S == S.lower():
return True
else:
self.bell()
return False

if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()

Для получения дополнительной информации о том, что происходит под капотом при вызове register метода, см. Почему для проверки ввода в tkinter требуется вызов register()?

Для получения канонической документации см. Раздел проверки справочной страницы ввода Tcl / Tk

Ответ 2

После изучения и экспериментов с кодом Брайана я создал минимальную версию проверки ввода. Следующий код будет отображать поле ввода и принимать только цифровые цифры.

from tkinter import *

root = Tk()

def testVal(inStr,acttyp):
if acttyp == '1': #insert
if not inStr.isdigit():
return False
return True

entry = Entry(root, validate="key")
entry['validatecommand'] = (entry.register(testVal),'%P','%d')
entry.pack()

root.mainloop()

Возможно, мне следует добавить, что я все еще изучаю Python и с радостью приму любые комментарии / предложения.

Ответ 3

Используйте a Tkinter.StringVar для отслеживания значения Entry виджета. Вы можете проверить значение StringVar, установив для него a trace.

Вот короткая рабочая программа, которая принимает только допустимые значения с плавающей запятой в Entry виджете.

try:
from tkinter import *
except ImportError:
from Tkinter import * # Python 2


root = Tk()
sv = StringVar()

def validate_float(var):
new_value = var.get()
try:
new_value == '' or float(new_value)
validate_float.old_value = new_value
except:
var.set(validate_float.old_value)

validate_float.old_value = '' # Define function attribute.

# trace wants a callback with nearly useless parameters, fixing with lambda.
sv.trace('w', lambda nm, idx, mode, var=sv: validate_float(var))
ent = Entry(root, textvariable=sv)
ent.pack()
ent.focus_set()

root.mainloop()

Ответ 4

Ответ Брайана правильный, однако никто не упомянул атрибут 'invalidcommand' виджета tkinter.

Хорошее объяснение находится здесь: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/entry-validation.html

Текст копируется / вставляется в случае неработающей ссылки

Виджет ввода также поддерживает опцию invalidcommand, которая определяет функцию обратного вызова, которая вызывается всякий раз, когда validatecommand возвращает False. Эта команда может изменять текст в виджете с помощью метода .set() для связанной с виджетом текстовой переменной. Настройка этой опции работает так же, как настройка validatecommand . Вы должны использовать метод .register() для переноса вашей функции Python; этот метод возвращает имя обернутой функции в виде строки. Затем вы передадите в качестве значения параметра invalidcommand либо эту строку, либо как первый элемент кортежа, содержащего коды подстановки.

Примечание: Есть только одна вещь, которую я не могу понять, как это сделать: если вы добавляете проверку к записи, а пользователь выбирает часть текста и вводит новое значение, нет способа зафиксировать исходное значение и сбросить запись. Вот пример


  1. Entry предназначен для приема только целых чисел за счет реализации 'validatecommand'

  2. Пользователь вводит 1234567

  3. Пользователь выбирает '345' и нажимает 'j'. Это регистрируется как два действия: удаление '345' и вставка 'j'. Tkinter игнорирует удаление и выполняет только вставку 'j'. 'validatecommand' возвращает значение False, а функции 'invalidcommand' передаются следующие значения: %d= 1, %i= 2, %P = 12j67, %s = 1267, %S= j

  4. Если код не реализует функцию 'invalidcommand', функция 'validatecommand' отклонит 'j', и результатом будет 1267. Если в коде действительно реализована функция 'invalidcommand', восстановить исходную 1234567 невозможно.

2023-07-17 07:58 python tkinter