Как вызвать функцию из функции в классе, который вызывается с помощью кнопки с лямбдой в Python3x?

У меня есть класс: MyClassName, например, с двумя функциями: одна для вычисления числа и одна для проверки того, все ли группы полей со списком (tkinter) имеют выбор. Я также использую tkinter для кнопки для вызова функции расчета с использованием лямбды, потому что я хочу, чтобы пользователь делал выбор в поле со списком, а затем нажимал кнопку расчета, отсюда и необходимость в лямбде.

Вот код кнопки:

# Calculate Button
        self.calculate_button = ttk.Button(button_frame,  
                            command = lambda: calculate(self),  # Use a lambda to send params to the function for command.
                            width = 12, 
                            text = "CALCULATE",
                            bootstyle = 'primary', 
                            state = 'enabled') 
# In the calculate function, I call another function:
def calculate(self):
            # Call the Class function from within another Class function.
            self.validate_Comboboxes() ...

А в функции validate_Comboboxes я использую что-то вроде этого:

 # Function to validate if all the Comboboxes were selected.
        def validate_Comboboxes(self):
           for widget in self.base_frame.winfo_children():
                if isinstance(widget, ttk.Combobox) and widget.get() == "":
                    messagebox.showerror("error", "All Comboboxes for the base frame must be selected for calculations!")
                    break
                    ...

Я тестировал функцию validate_Comboboxes отдельно вне этого приложения и знаю, что она работает. Однако я получаю эту ошибку: AttributeError: объект MyClassName не имеет атрибута validate_Comboboxes. Кажется, у меня это неправильно объявлено? Я понимаю, что если бы это был просто атрибут класса, вы бы использовали: myAttribute(self) для его объявления, но validate_Comboboxes(self) не имеет для меня смысла, поскольку я объявляю его с помощью своего оператора def, если только я что-то не упускаю?

Каков правильный способ закодировать это для моего варианта использования, главным образом для проверки того, что пользователь выбрал все поля со списком, прежде чем он нажмет кнопку расчета?

Я подумал, что если бы у вас было две функции внутри класса, например functionA(self) и functionB(self), вы могли бы вызвать функциюA из функцииB с помощью self.function(). Так является ли здесь проблемой механизм использования кнопки с лямбдой?


1
55
1

Ответ:

Решено

Используя код предыдущих вопросов в качестве контекста, вы испытываете эти ошибки из-за области, в которой вы определяете свои функции validateComboboxes, calculate и clear.

Учитывая следующее, это обобщенная версия вашего кода...

class ComboBoxTestGroup:
    def __init__(self, master):
        ...
        ...
        
        def calculate(self):
            ...
        def clear(self):
            ...
        def validateComboboxes(self):
            ...

Поскольку три ранее упомянутые функции определены внутри тела метода __init__, эти функции не привязаны к экземпляру ComboBoxTestGroup и доступны только для кода, который выполняется в области действия методов __init__. Это, а также тот факт, что вы используете лямбду, которая будет оцениваться во время выполнения, означает, что когда вы попытаетесь вызвать calculate(self), определение функции больше не будет существовать.

Есть очень простое решение. Определите свои методы в теле/области класса вместо метода __init__. Это гарантирует, что функции будут привязаны к экземпляру класса и останутся доступными для вызова в течение всего времени существования объекта экземпляра.

Пример:

import tkinter
from tkinter import *
from tkinter import messagebox
from tkinter.ttk import Combobox
import ttkbootstrap as ttk
from ttkbootstrap.constants import *

window = ttk.Window(themename = 'superhero')
window.title('Combobox Test Group')
window.geometry('1200x275')

class ComboBoxTestGroup:
    def __init__(self, master):

        # Create the main frame.
        self.main_frame = ttk.Frame(master, relief=SUNKEN, borderwidth=1)
        self.main_frame.grid(row=0, column=0)

        # Dropdowns
        self.base_frame = tkinter.LabelFrame(self.main_frame, text='BASE', relief=SUNKEN, borderwidth=1)
        self.base_frame.grid(row=4, column=0, sticky = "news", padx=10, pady=15)

        self.base_label_ti = ttk.Label(self.base_frame, text='Technical Interface (TI)', font=('Helvetica', 10))
        self.base_label_ti.grid(row=0, column=0)

        self.ti_values = [("High", "H", "1.0"), ("Medium", "M", "0.5"), ("Low", "L", "0.1"),  ("None", "N", "0.0")]
        self.ti_combobox = ttk.Combobox(self.base_frame, bootstyle='primary', values=self.ti_values, state = 'readonly')
        self.ti_combobox.grid(row=1, column=0)

        self.base_label_ap = ttk.Label(self.base_frame, text='Application Project (AP)', font=('Helvetica', 10))
        self.base_label_ap.grid(row=0, column=1)

        self.ap_values = [("High", "H", "1.0"), ("Medium", "M", "0.5"), ("Low", "L", "0.1"),  ("None", "N", "0.0")]
        self.ap_combobox = ttk.Combobox(self.base_frame, bootstyle='primary', values=self.ap_values, state = 'readonly')
        self.ap_combobox.grid(row=1, column=1)

        self.base_label_al = ttk.Label(self.base_frame, text='Application Logs (AL)', font=('Helvetica', 10))
        self.base_label_al.grid(row=0, column=2)

        self.al_values = [("High", "H", "1.0"), ("Medium", "M", "0.5"), ("Low", "L", "0.1"),  ("None", "N", "0.0")]
        self.al_combobox = ttk.Combobox(self.base_frame, bootstyle='primary', values=self.al_values, state = 'readonly')
        self.al_combobox.grid(row=1, column=2)

        self.base_label_ic = ttk.Label(self.base_frame, text='Internal Composition (IC)', font=('Helvetica', 10))
        self.base_label_ic.grid(row=0, column=3)

        self.ic_values = [("High", "H", "1.0"), ("Medium", "M", "0.5"), ("Low", "L", "0.1"),  ("None", "N", "0.0")]
        self.ic_combobox = ttk.Combobox(self.base_frame, bootstyle='primary', values=self.ic_values, state = 'readonly')
        self.ic_combobox.grid(row=1, column=3)

        self.base_label_fc = ttk.Label(self.base_frame, text='Fixed Controls (FC)', font=('Helvetica', 10))
        self.base_label_fc.grid(row=0, column=4)

        self.fc_values = [("High", "H", "1.0"), ("Medium", "M", "0.5"), ("Low", "L", "0.1"),  ("None", "N", "0.0")]
        self.fc_combobox = ttk.Combobox(self.base_frame, bootstyle='primary', values=self.fc_values, state = 'readonly')
        self.fc_combobox.grid(row=1, column=4)

        # For loop to space widgets within the base_frame
        for widget in self.base_frame.winfo_children():
            widget.grid_configure(padx=20, pady=10)

        # Create a frame for the buttons.
        button_frame = ttk.Frame(self.main_frame, relief=SUNKEN, borderwidth=1)
        button_frame.grid(row=5, column=0)

        # Validate Button
        self.calculate_button = ttk.Button(button_frame,
                            command = self.calculate,  # no need for lambda
                            width = 12,
                            text = "VALIDATE",
                            bootstyle = 'primary',
                            state = 'enabled')

        self.calculate_button.grid(row=10, column=0, padx=75, pady=10)

        # Clear Button
        self.clear_button = ttk.Button(button_frame,
                            command=self.clear,  # no need for lambda
                            width = 12,
                            text = "CLEAR",
                            bootstyle = 'primary',
                            state = 'enabled')
        self.clear_button.grid(row=10, column=1, padx=75, pady=10)

    def calculate(self):
        self.validateComboboxes()

    # Function to clear the ttk widgets.
    def clear(self):
        # This clears the Comboboxes by the enclosing frame.
        for widget in self.base_frame.winfo_children():
            if isinstance(widget, ttk.Combobox):
                widget.set("")

                # Function to validate if all the Comboboxes were selected.
    def validateComboboxes(self):
        for widget in self.base_frame.winfo_children():
            if isinstance(widget, ttk.Combobox) and widget.get() == "":
                messagebox.showerror("error", "All Comboboxes must be selected in order to do calculations!")

widget_class = ComboBoxTestGroup(window)

# Run the GUI
window.mainloop()