Передача переменных

Как передать переменную entry из def changer() в def analyse()?

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

TypeError: changer() missing 1 required positional argument: 'entry'

import tkinter as tk
from tkinter import ttk


def changer(event):
    units_get = str(units.get())
    entry = float(entry_a.get())

    if units_get == 'нед.':
        entry *= 7 / float(30.416)
        entry = round(entry, 2)
        ttk.Label(changed_units, style='changed_units.TLabel', text=f'->  {entry} мес.').place(x=0, y=3)
    elif units_get == 'мес.':
        for widget in changed_units.winfo_children():
            widget.destroy()
    elif units_get == 'годы':
        entry *= float(30.416)
        entry = round(entry, 2)
        ttk.Label(changed_units, style='changed_units.TLabel', text=f'->  {entry} мес.').place(x=0, y=3)
    return entry

def analyse(entry):
    var_a = float(entry)
    var_b = float(b.get())
    score = var_a  + var_b
    print(score)


app = tk.Tk()
app.title('changer')
width = 300
height = 100
x = int((app.winfo_screenwidth() / 2) - (width / 2))
y = int((app.winfo_screenheight() / 2) - (height / 2))
app.geometry(f'{width}x{height}+{x}+{y}')
app.resizable(width=False, height=False)
app['background'] = '#666666'

ttk.Style().configure('changed_units.TLabel', font=('Tahoma', 8),
                      foreground='#ffffff', background='#666666', border=0)
changed_units = tk.Frame(app, bg='#666666')
changed_units.place(x=80, y=50, height=25, width=100)

units = ttk.Combobox(app, state='readonly', values=['нед.', 'мес.', 'годы'])
units.place(x=10, y=50, height=25, width=60)
units.current(1)
units.bind('<<ComboboxSelected>>', changer)

a = tk.StringVar()
entry_a = ttk.Entry(app, textvariable=a)
entry_a.place(x=10, y=12, height=25, width=60)

b = tk.StringVar()
entry_b = ttk.Entry(app, textvariable=b)
entry_b.place(x=80, y=12, height=25, width=60)

ttk.Button(app, text='Анализ', command=analyse).place(x=220, y=10, height=30, width=60)

app.mainloop()

Ответы (3 шт):

Автор решения: S. Nick

Можно объявить entry и сделать ее глобальной в функциях changer() и analyse().

А лучше используя ООП, как-то так:

main.py

import tkinter as tk
from tkinter import ttk


class App(tk.Frame):
    def __init__(self, master):
        super().__init__(master)

        self.entry = 1
        self.score = 0  

        ttk.Style().configure(
            'self.changed_units.TLabel', 
            font=('Tahoma', 8),
            foreground='#ffffff', 
            background='#666666', 
            border=0)

        self.changed_units = tk.Frame(master, bg='#666666')
        self.changed_units.place(
            x=80, y=50, height=25, width=100)

        self.units = ttk.Combobox(master, 
            state='readonly', 
            values=['нед.', 'мес.', 'годы'])
        self.units.place(x=10, y=50, height=25, width=60)
        self.units.current(1)
        self.units.bind('<<ComboboxSelected>>', self.changer)

        self.a = tk.StringVar()
        self.entry_a = ttk.Entry(master, textvariable=self.a)
        self.entry_a.place(x=10, y=12, height=25, width=60)

        self.b = tk.StringVar()
        self.entry_b = ttk.Entry(master, textvariable=self.b)
        self.entry_b.place(x=80, y=12, height=25, width=60)

        ttk.Button(master, 
            text='Анализ', 
            command=self.analyse
        ).place(x=220, y=10, height=30, width=60)

    def changer(self, event):
        units_get = str(self.units.get())
        self.entry = float(self.entry_a.get())

        if units_get == 'нед.':
            self.entry *= 7 / float(30.416)
            self.entry = round(self.entry, 2)
            ttk.Label(self.changed_units, 
                style='self.changed_units.TLabel', 
                text=f'->  {self.entry} мес.').place(x=0, y=3)
        elif units_get == 'мес.':
            for widget in self.changed_units.winfo_children():
                widget.destroy()
        elif units_get == 'годы':
            self.entry *= float(30.416)
            self.entry = round(self.entry, 2)
            ttk.Label(self.changed_units, 
                style='self.changed_units.TLabel', 
                text=f'->  {self.entry} мес.').place(x=0, y=3)
#        return entry

    def analyse(self):                              # (entry) 
        var_a = float(self.entry)
        var_b = float(self.b.get())
        self.score = var_a  + var_b
        print(self.score)        
        
        
if __name__ == '__main__':
    root = tk.Tk()
    root.title('changer')
    width = 300
    height = 100
    x = int((root.winfo_screenwidth() / 2) - (width / 2))
    y = int((root.winfo_screenheight() / 2) - (height / 2))
    root.geometry(f'{width}x{height}+{x}+{y}')
    root.resizable(width=False, height=False)
    root['background'] = '#666666'    
    
    app = App(root)
    app.pack()

    root.mainloop()

введите сюда описание изображения

→ Ссылка
Автор решения: Amgarak

Могу предложить посмотреть в сторону functools.partial — это метод из стандартной либы, которым можно воспользоваться, что бы создать что-то вроде замыкания.

Если проще, то можно зафиксировать аргументы функции как в lambda, но при этом сразу ссылаться на конкретную реализацию.

Небольшой пример:

import tkinter as tk
from functools import partial

def set_value():
    value = entry.get()
    button.config(command=partial(print_value, value))

def print_value(value):
    label.config(text=f"value: {value}")

root = tk.Tk()
entry = tk.Entry(root)
entry.pack()

tk.Button(root, text="Запомнить value!", command=set_value).pack()

button = tk.Button(root, text="Вывести value")
button.pack()

label = tk.Label(root, text="value: ")
label.pack()

root.mainloop()

Есть еще один слегка безумный вариант, помима глобальной переменной, приведу его просто в качестве шутки:

import tkinter as tk

def set_value():
    value = entry.get()
    print_value.value=value

def print_value():
    label.config(text=f"value: {print_value.value}")

root = tk.Tk()
entry = tk.Entry(root)
entry.pack()

tk.Button(root, text="Запомнить value!", command=set_value).pack()

button = tk.Button(root, text="Вывести value", command=print_value)
button.pack()

label = tk.Label(root, text="value: ")
label.pack()

root.mainloop()

Так как функции, это ничто иное как класс, то можно добавлять им любые новые паля и хранить там что угодно.

→ Ссылка
Автор решения: KLYSTRON

Исправил! Теперь работает как нужно и все передается!

import tkinter as tk
from tkinter import ttk


def changer(*args):
    units_get = str(units.get())
    entry = float(entry_a.get())

    if units_get == 'нед.':
        entry *= 7 / float(30.416)
        entry = round(entry, 2)
        ttk.Label(changed_units, style='changed_units.TLabel', text=f'->  {entry} мес.').place(x=0, y=3)
    elif units_get == 'мес.':
        for widget in changed_units.winfo_children():
            widget.destroy()
    elif units_get == 'годы':
        entry *= float(30.416)
        entry = round(entry, 2)
        ttk.Label(changed_units, style='changed_units.TLabel', text=f'->  {entry} мес.').place(x=0, y=3)
    return entry


def analyse(*args):
    entry = changer()
    var_a = float(entry)
    var_b = float(b.get())
    score = var_a + var_b
    print(score)


app = tk.Tk()
app.title('changer')
width = 300
height = 100
x = int((app.winfo_screenwidth() / 2) - (width / 2))
y = int((app.winfo_screenheight() / 2) - (height / 2))
app.geometry(f'{width}x{height}+{x}+{y}')
app.resizable(width=False, height=False)
app['background'] = '#666666'

ttk.Style().configure('changed_units.TLabel', font=('Tahoma', 8),
                      foreground='#ffffff', background='#666666', border=0)
changed_units = tk.Frame(app, bg='#666666')
changed_units.place(x=80, y=50, height=25, width=100)

units = ttk.Combobox(app, state='readonly', values=['нед.', 'мес.', 'годы'])
units.place(x=10, y=50, height=25, width=60)
units.current(1)
units.bind('<<ComboboxSelected>>', changer)


a = tk.StringVar()
entry_a = ttk.Entry(app, textvariable=a)
entry_a.place(x=10, y=12, height=25, width=60)

b = tk.StringVar()
entry_b = ttk.Entry(app, textvariable=b)
entry_b.place(x=80, y=12, height=25, width=60)

ttk.Button(app, text='Анализ', command=analyse).place(x=220, y=10, height=30, width=60)

app.mainloop()

Если я правильно все понял, ошибка возникала из-за того, что нужно было расширить количество параметров передаваемых функциям changer и analyse, поэтому переменная entry терялась. Поправьте, если ошибаюсь.

И еще вариант, в котором вообще не нужна передача из одной функции в другую, но, работает точно так же.

import tkinter as tk
from tkinter import ttk


def units_indicator(event):
    entry = float(entry_a.get())
    units_get = str(units.get())

    if units_get == 'нед.':
        entry *= 7 / float(30.416)
        entry = round(entry, 2)
        ttk.Label(changed_units, style='changed_units.TLabel', text=f'->  {entry} мес.').place(x=0, y=3)
    elif units_get == 'мес.':
        for widget in changed_units.winfo_children():
            widget.destroy()
    elif units_get == 'годы':
        entry *= float(30.416)
        entry = round(entry, 2)
        ttk.Label(changed_units, style='changed_units.TLabel', text=f'->  {entry} мес.').place(x=0, y=3)


def analyse():
    entry = float(entry_a.get())
    units_get = str(units.get())

    if units_get == 'нед.':
        entry *= 7 / float(30.416)
        entry = round(entry, 2)
    elif units_get == 'мес.':
        entry = float(entry_a.get())
    elif units_get == 'годы':
        entry *= float(30.416)
        entry = round(entry, 2)

    var_a = float(entry)
    var_b = float(b.get())
    score = var_a + var_b
    print(score)


app = tk.Tk()
app.title('changer')
width = 300
height = 100
x = int((app.winfo_screenwidth() / 2) - (width / 2))
y = int((app.winfo_screenheight() / 2) - (height / 2))
app.geometry(f'{width}x{height}+{x}+{y}')
app.resizable(width=False, height=False)
app['background'] = '#666666'

ttk.Style().configure('changed_units.TLabel', font=('Tahoma', 8),
                      foreground='#ffffff', background='#666666', border=0)
changed_units = tk.Frame(app, bg='#666666')
changed_units.place(x=80, y=50, height=25, width=100)

units = ttk.Combobox(app, state='readonly', values=['нед.', 'мес.', 'годы'])
units.place(x=10, y=50, height=25, width=60)
units.current(1)
units.bind('<<ComboboxSelected>>', units_indicator)

a = tk.StringVar()
entry_a = ttk.Entry(app, textvariable=a)
entry_a.place(x=10, y=12, height=25, width=60)

b = tk.StringVar()
entry_b = ttk.Entry(app, textvariable=b)
entry_b.place(x=80, y=12, height=25, width=60)

ttk.Button(app, text='Анализ', command=analyse).place(x=220, y=10, height=30, width=60)

app.mainloop()
→ Ссылка