Обновление значения в поле ввода 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)


arrow_upward
1
arrow_downward

Поскольку вы используете 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