@PC_Like

Python Tkinter — как сделать рабочий GUI с множеством вкладок и динамическим добавлением элементов?

Стоит задача - воспроизвести алгоритм нечеткого бенчмаркинга:
1) Получить два параметра - число альтернатив и число параметров оценки
2) Исходя из значений параметров запросить ввод оценок для каждой альтернативы
3) Запросить значения весовых коэффициентов для каждой оценки
4) Запросить требования (ожидания) по каждому параметру
5) Рассчитать расстояния Хэмминга для каждой оценки каждой альтернативы
6) По расстояниям посчитать коэффициенты рассогласования (формулы имеются)
7) По соотношению (имеется в методичке) - выбрать наиболее удачную альтернативу

Предполагается реализация исполняемого файла - exe'шника с GUI на Python + Tkinter. В процессе реализации столкнулся со странным поведением собираемого интерфейса
Ниже приведен код формы
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
from bench import *
labels = []
fields = []
values = []
weights = []
req_labels = []
req_fields = []
requirements = []
marks = []
tabs = {"Исходные данные", "Оценки", "Рекомендации"}
window = Tk()
window.title('Нечеткий бенчмаркинг')
window.geometry('1280x720')
control = ttk.Notebook(window)

def saveParams():
	params = int(params_field.get())
	return params

def saveOptions():
	options = int(options_quant.get())
	return options

def fillWeight():
	params = saveParams()
	data = weights_field.get()
	if (data != ''):
		del weights[:]
		push = data.split(' ')
		if (len(push) == params):
			digits = []
			for bits in push:
				digits.append(float(bits.replace(",", ".")))
			if (sum(digits) == 1):
				weights.append(digits)
			else:
				alert = messagebox.showerror("Ошибка ввода", "Проверьте вес")
				if (alert == 'ok'):
					alert.destroy()


def calc():
	params = saveParams()
	options = saveOptions()
	if (params != ''):
		del values[:]
		del requirements[:]
		del marks[:]
		for a in fields:
			data = a.get()
			if (data != ''):
				push = data.split(' ')
				if (len(push) == params):
					numbers = []
					for number in push:
						numbers.append(float(number.replace(",", ".")))
					values.append(numbers)
				else:
					box = messagebox.showerror("Ошибка ввода", "Проверьте ввод для альтернатив")
					if (box == 'ok'):
						box.destroy()
		for b in req_fields:
			data = b.get()
			if (data != ''):
				push = float(data.replace(",", "."))
				requirements.append(push)
			else:
				alarm = messagebox.showerror("Ошибка ввода", "Проверьте ввод для требований")
				if (alarm == 'ok'):
					alarm.destroy()
	print(values)
	print(weights)
	print(requirements)
	if ((len(values) == options) & (len(requirements) == len(weights[0]) == len(values[0]) == params) & (sum(weights[0]) == 1)):
		result = HammingDistance(values, requirements, weights)
		marks.append(result)

def getDistances():
	start = 0
	options = saveOptions()
	finish = len(marks) + 1
	for i in range(0, len(marks) - 1):
		row = start + i + 1
		mark_label = Label(tab, text = 'Невязки '+str(i + 1)+' альтернативы:')
		mark_label.grid(column = 0, row = row, padx = 15, pady = 15)
		mark_field = Entry(tab, width = 25)
		value = ' '.join(str(e) for e in marks[i])
		mark_field.insert(0, value)
		mark_field.grid(column = 1, row = row, padx = 15, pady = 15)

def run():
	params = saveParams()
	options = saveOptions()
	start = 1
	finish = start + options + 1
	for a in labels:
		a.destroy()
	for b in fields:
		b.destroy()
	for c in req_labels:
		c.destroy()
	for d in req_fields:
		d.destroy()
	for i in range(options):
		row = start + i + 1
		label = Label(tab, text = 'Оценки '+str(i + 1)+ ' альтернативы (через пробел):')
		label.grid(column = 0, row = row, padx = 15, pady = 15)
		labels.append(label)
		field = Entry(tab, width = 25)
		field.grid(column = 1, row = row, padx = 15, pady = 15)
		fields.append(field)
	for k in range(params):
		row = start + k + 1
		req_label = Label(tab, text = 'Требование по '+str(k + 1)+' параметру:')
		req_label.grid(column = 2, row = row, padx = 15, pady = 15)
		req_labels.append(req_label)
		req_field = Entry(tab, width = 25)
		req_field.grid(column = 3, row = row, padx = 15, pady = 15)
		req_fields.append(req_field)

for value in tabs:
	tab = ttk.Frame(control)
	control.add(tab, text = value)
	if (value == 'Исходные данные'):
		options_label = Label(tab, text = 'Число альтернатив:')
		options_label.grid(column = 0, row = 0, padx = 15, pady = 15)
		options_quant = Entry(tab, width = 25)
		options_quant.grid(column = 1, row = 0, padx = 15, pady = 15)
		params_label = Label(tab, text = 'Число параметров:')
		params_label.grid(column = 0, row = 1, padx = 15, pady = 15)
		params_field = Entry(tab, width = 25)
		params_field.grid(column = 1, row = 1, padx = 15, pady = 15)
		options_save = Button(tab, text = 'Сохранить', command = run)
		options_save.place(x = 470, y = 12.5)
		calcbutton = Button(tab, text = 'Выполнить расчет', command = calc)
		calcbutton.grid(column = 8, row = 1, padx = 15, pady = 15)
		weights_label = Label(tab, text = 'Весовые коэффициенты через пробел (сумма должна быть равна 1):')
		weights_label.grid(column = 2, row = 1, padx = 15, pady = 15)
		weights_field = Entry(tab, width = 25)
		weights_field.grid(column = 3, row = 1, padx = 15, pady = 15)
		weights_button = Button(tab, text = 'Учесть вес', command = fillWeight)
		weights_button.grid(column = 4, row = 1, padx = 15, pady = 15)
	else:
		if (value == 'Оценки'):
			start_button = Button(tab, text = 'Вывести расстояния', command = getDistances)
			start_button.grid(column = 0, row = 0, padx = 15, pady = 15)
control.pack(expand = 1, fill = 'both')
window.mainloop()

Здесь все просто - собирается window с тремя вкладками - исходные данные, рекомендации и оценка. Ниже код подключаемого модуля с логикой, куда предполагается пушить данные с формы
from math import exp
def HammingDistance(values, requirements, weights):
	result = []
	for option in values:
		push = []
		for value in option:
			index = option.index(value)
			mark = weights[0][index]*(requirements[index] - value)
			push.append(mark)
		result.append(push)
	return result

С чем столкнулся - кнопка "Сохранить" стала отрабатывать со второго запуска приложения. Далее - попытки как-то оUserFriendly'ть приложение - закрепить вкладки, либо выделить главную, сделать дополнительные переключатели, подчеркивания и прочее - приводят к отказу всей фронтовой логики. Собственно, вопросов у меня три по существу: почему попытки прикрутить "красоту" рушат приложение и почему кнопка "Сохранить" работает только после перезапуска приложения? И третий - когда пытаюсь задействовать кнопку "Вывести расстояния" (естественно, после получения результатов) - ничего не происходит. У меня есть предположение, что динамический проброс инпутов и прочих элементов во вкладки - не самое удачное решение, но в другом проекте это сработало. А в этом нет.
  • Вопрос задан
  • 806 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы