У меня есть класс: 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(). Так является ли здесь проблемой механизм использования кнопки с лямбдой?
Если вы не отправляете данные в метод calculate
, то lambda
вам не нужен. Попробуйте изменить command = lambda: calculate(self)
на command = self.calculate
acw1668 Да, я согласен, но я не могу показать большую часть кода, потому что это рабочий проект. Я пытаюсь показать ровно столько, чтобы мои проблемы были описаны здесь.
Используя код предыдущих вопросов в качестве контекста, вы испытываете эти ошибки из-за области, в которой вы определяете свои функции 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()
Выручил меня снова! Хорошо, я проведу дополнительные исследования, поскольку tkinter более сложен, чем я думал. Я еще не до конца усвоил эту концепцию. Хотя спасибо за объяснение.
Лучше предоставить минимально воспроизводимый пример вместо блоков кода.