Обновление значения в поле ввода Python TKinter

Я использую Python 3.8.1 с tkinter версии 8.6.

У меня есть класс GUI, Pressureinput, который принимает входные данные для симулятора датчика давления. Я хочу, чтобы запись была в единицах кПа (исходные единицы измерения датчика), но я также хочу, чтобы пользователь знал, что такое эквивалент в фунтах на квадратный дюйм. Итак, когда пользователь обновляет значение kpa, я хочу, чтобы значение psi обновлялось, но я не хочу, чтобы пользователь мог обновлять значение psi вручную. Я использую поле ввода для обоих. Они начинаются со значения по умолчанию 242 кПа.

Я пытаюсь использовать validate="focusout" для запуска события после того, как поле ввода kpa теряет фокус.

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

Я понимаю, что метод, который я использую с моей функцией pressurevalid, не будет работать, потому что объекты поля ввода, kpa и psi неизменяемы, и он не изменит исходные объекты.

Обратите внимание, что я установил переменные StringVar psitext и kpatext. Однако каждый раз, когда я пытаюсь использовать их в своей функции pressurevalid, я получаю сообщение об ошибке, говорящее, что их не существует.

Все остальное, что я пробовал, заканчивается ошибками, которые не запускаются, и я думаю, что это, по крайней мере, иллюстрирует то, что я хочу сделать:

import tkinter as tkGUI

#global constants for conversion
global psi2kpa
global kpa2psi
psi2kpa = 6.894757
kpa2psi = 1 / psi2kpa

class Pressureinput(tkGUI.Frame):

    def __init__(self,parent):
            tkGUI.Frame.__init__(self,parent)
            self.parent = parent
            self.initialize()

    def initialize(self):

            kpatext = tkGUI.StringVar()
            psitext = tkGUI.StringVar()

            self.IDlabel = tkGUI.Label(self,text="Sensor ID (hex):")
            self.IDlabel.grid(row=0, column=0)
            self.ID = tkGUI.Entry(self)
            self.ID.insert(0,"AABBCCDD")
            self.ID.grid(row=0, column=1)

            self.kpalabel = tkGUI.Label(self,text="Pressure (kPa):")
            self.kpalabel.grid(row=1, column=0)
            self.kpa = tkGUI.Entry(self)
            self.kpa.insert(0,242)
            self.kpa.grid(row=1, column=1)

            self.psilabel = tkGUI.Label(self,text="Pressure (PSI):")
            self.psilabel.grid(row=2, column=0)
            self.psi = tkGUI.Entry(self, textvariable=psitext)
            self.psi.insert(0,float(self.kpa.get())*kpa2psi)
            self.psi.grid(row=2, column=1)
            self.psi.config(state='disabled') #state = 'normal' to restore

            vpressure = self.register(self.pressurevalid(self.kpa,self.psi))
            self.kpa = tkGUI.Entry(self, textvariable=kpatext, validate="focusout", validatecommand=vpressure)

            self.sendbutton = tkGUI.Button(self,text="Send Transmission",state="disabled",command=self.send_data)
            self.sendbutton.grid(row=9,columnspan=2)

    def pressurevalid(self,kpa,psi):
            if len(kpa.get()) < 1:
                    kpa.delete(0,tkGUI.END)
                    kpa.insert(0,"0");
            elif 2*int(round(float(kpa.get())) / 2) != int(kpa.get()):
                    kpa.delete(0,tkGUI.END)
                    kpa.insert(0,2 * int(round(float(kpa.get()))) / 2)

            psi.config(state='normal')
            psi.delete(0,tkGUI.END)
            psi.insert(0,float(kpa.get())*kpa2psi)
            psi.config(state='disabled')
            return True

    def send_data(self):
            ID = int(self.ID.get(),16)
            pressure = int(self.kpa.get())
            if pressure >= 510:
                    pressure = 255
            else:
                 pressure = int(round(pressure/2))

            sendstring =  str(ID) + "," + str(function_code) + "," + str(pressure)
            print (sendstring)

person Trashman    schedule 30.01.2020    source источник


Ответы (1)


Поскольку вы используете StringVar для записей, вы можете настроить трассировку переменной для вызова функции при каждом изменении значения. Это будет постоянно обновлять значение, а не ждать события фокуса.

Во-первых, вам нужно преобразовать переменные в атрибуты класса, а не делать их локальными переменными:

self.kpatext = tkGUI.StringVar()
self.psitext = tkGUI.StringVar()

Вам также придется настроить другие места, которые ссылаются на эти переменные:

self.psi = tkGUI.Entry(..., textvariable=self.psitext, ...)
self.kpa = tkGUI.Entry(..., textvariable=self.kpatext, ...)

Затем настройте трассировку на self.kpatext сразу после создания переменных:

self.kpatext.trace("w", self.update_psi)

И, наконец, напишите метод self.update_psi. Следующий код установит PSI в пустую строку, если текущее значение кПа не может быть преобразовано.

def update_psi(self, *args):
    try:
        psi = int(self.kpatext.get())*kpa2psi
        self.psitext.set(psi)
    except Exception as e:
        self.psitext.set("")

Дополнительные сведения об аргументах функции трассировки см. в разделе Каковы аргументы для обратных вызовов методов трассировки переменных Tkinter?. В этом примере они нам не нужны, но функция все равно должна их принять.


Обратите внимание, что ваш код определяет self.kpa дважды — один раз без использования textvariable и один раз с. Я не понимаю, почему вы это делаете, учитывая, что второй никогда не добавляется на экран с пакетом/местом/сеткой. Мое решение работает при условии, что вы собираетесь использовать исходный self.kpa.

person Bryan Oakley    schedule 30.01.2020
comment
Если я добавлю трассировку, я предполагаю, что смогу удалить команду проверки и проверки из моего определения self.kpa? (Примечание: второе определение self.kpa было неудачной попыткой обойти некоторые ошибки, которые я получал с порядком определения моего объекта) - person Trashman; 30.01.2020
comment
@Trashman: да, вы можете удалить функцию проверки, если не хотите выполнять какую-либо проверку ввода. Я бы порекомендовал сохранить проверку, но использовать ее только для проверки — например, разрешать только цифры и одну десятичную точку. - person Bryan Oakley; 30.01.2020
comment
Хорошо, код отлично работает для обновления значения psi при изменении kpa. Я немного изменил его, чтобы также изменить значение kpa на допустимое значение (даже только целое число), и это работает слишком хорошо. Поскольку он обновляется сразу же после изменения, я вообще не могу ввести нечетное число. Итак, если я хочу 254 кПа, сначала будет введено 244, а затем я должен использовать курсор, чтобы изменить средние 4 на 5. Могу ли я что-нибудь сделать, чтобы изменить трассировку, чтобы она работала как focusout, а не корректировалась до тех пор, пока пользователь покидает ящик? - person Trashman; 30.01.2020
comment
Итак, я сделал две функции и теперь вызываю их отдельно. У меня есть update_psi, использующий трассировку, как вы предложили. Я повторно добавил свою функцию vpression (теперь как self.vpressure - я думаю, что моя самая большая проблема изначально заключалась в недостаточном использовании ссылки на себя) в качестве действия проверки на focusout - и, похоже, она работает, за исключением того, что она работает только один раз . Первый раз ввожу данные в поле, оно фиксирует. Если я снова вернусь к ящику, я могу ввести все, что захочу, и он не будет снова проверять/исправлять это. - person Trashman; 30.01.2020
comment
@Trashman: tkinter удалит функцию проверки при определенных условиях, в частности, когда функция проверки возвращает ошибку. Кроме того, вы не должны изменять значение в функции проверки. - person Bryan Oakley; 30.01.2020
comment
Итак, это приводит меня к загадке. Трассировка слишком быстро изменяет мое значение, и я не могу использовать валидацию, потому что я не просто хочу проверить валидацию, я хочу изменить проверяемое значение, если оно неверно. Есть ли другой вариант? Или способ выполнить отдельное действие после того, как проверка вернет false? - person Trashman; 30.01.2020
comment
Попытался использовать недействительную команду и создал новую функцию, correctkpa, которая вызывается недействительной командой. Я сделал так, что vpression только проверяет значение и возвращает True или False, а затем пытается изменить значение в correctkpa. К сожалению, тот же результат, поэтому, очевидно, неверная команда также не может изменить исходное значение. - person Trashman; 30.01.2020
comment
@Trashman: каноническое объяснение того, как работает проверка, находится здесь: tcl.tk /man/tcl8.5/TkCmd/entry.htm#M7. Он написан для tcl/tk, но его легко мысленно преобразовать в tkinter. - person Bryan Oakley; 30.01.2020
comment
Полное раскрытие: всю эту неделю я работал с Python/tkinter. Я изменяю сценарий от кого-то другого. Я знаю другие языки, поэтому я попытался сразу же начать. К моей чести, это был мой единственный камень преткновения в изменениях, которые я сделал. Похоже, мне нужно вызвать после простоя {%W config -validate %v} в моей функции correct_kpa. Я не уверен, где это вписать в синтаксис. Должен ли я использовать это в оболочке self.register? Или я использую оболочку self.register для передачи %v и %W в качестве параметров для correct_kpa и использую там python after_idle? Я могу отправить эту команду в виде строки? - person Trashman; 30.01.2020
comment
ОК, разобрался. В моей функции correct_kpa мне пришлось добавить: self.after_idle(lambda: self.kpa.config(validate='focusout')) , это повторно включает проверку после того, как она автоматически отключается tkinter из-за моего изменения исходного значения . Я думаю, что также может работать само изменение в команде after_idle, но я сначала заставил это работать таким образом. Мне помогла эта ссылка: stupidpythonideas.blogspot.com/2013/12/tkinter-validation .html - person Trashman; 31.01.2020