import tkinter as tk from tkinter import filedialog as fd from tkinter import ttk class PlotType: USER = 1 PRESET = 2 POINT = 3 class PresetType: LINE = "ax + b = 0" PARABOLA = "ax^2 + bx + c = 0" @classmethod def options(cls): return [cls.LINE, cls.PARABOLA] class PlotTypeFrame(tk.Frame): def __init__(self, master, plot_type_var, on_type_change): super().__init__(master) self.master = master self.plot_type = plot_type_var self.on_type_change = on_type_change self.create_widgets() def create_widgets(self): self.preset_plot = ttk.Radiobutton(self) self.preset_plot["variable"] = self.plot_type self.preset_plot["text"] = "Предустановленный" self.preset_plot["value"] = PlotType.PRESET self.preset_plot["command"] = self.on_type_change self.preset_plot.grid(row=0, column=0, sticky=tk.W) self.user_plot = ttk.Radiobutton(self) self.user_plot["variable"] = self.plot_type self.user_plot["text"] = "Пользовательский" self.user_plot["value"] = PlotType.USER self.user_plot["command"] = self.on_type_change self.user_plot.grid(row=1, column=0, sticky=tk.W) self.point_plot = ttk.Radiobutton(self) self.point_plot["variable"] = self.plot_type self.point_plot["text"] = "По точкам" self.point_plot["value"] = PlotType.POINT self.point_plot["command"] = self.on_type_change self.point_plot.grid(row=2, column=0, sticky=tk.W) self.preset_plot.invoke() class PresetPlotFrame(tk.Frame): def __init__(self, master): super().__init__(master) self.master = master self.create_widgets() def create_widgets(self): # Виджеты для указания значений параметров графиков self.variables = {} self.l_min = tk.Label(self) self.l_min["text"] = "min(x)" self.l_min.grid(row=1, column=0) self.min = tk.Entry(self) self.min.grid(row=1, column=1) self.l_max = tk.Label(self) self.l_max["text"] = "max(x)" self.l_max.grid(row=2, column=0) self.max = tk.Entry(self) self.max.grid(row=2, column=1) la = tk.Label(self) la["text"] = "a: " a = tk.Entry(self) lb = tk.Label(self) lb["text"] = "b: " b = tk.Entry(self) lc = tk.Label(self) lc["text"] = "c: " c = tk.Entry(self) # В соответствие каждому графику ставятся виджеты его переменных. # None соответствуют все виджеты переменных, чтобы их можно было # скрывать. self.variables[PresetType.LINE] = [(la, a), (lb, b)] self.variables[PresetType.PARABOLA] = [(la, a), (lb, b), (lc, c)] self.variables[None] = [(la, a), (lb, b), (lc, c)] # Изменения этой переменной будут передаваться в метод type_changed self.preset_type = tk.StringVar() self.preset_type.trace("w", self.type_changed) self.options = ttk.OptionMenu( self, self.preset_type, PresetType.options()[0], *PresetType.options(), ) self.options.grid(row=0, column=0, columnspan=2, rowspan=1, sticky=tk.NSEW) def type_changed(self, *args): new_type = self.preset_type.get() for (label, var) in self.variables[None]: label.grid_forget() var.grid_forget() for i, (label, var) in enumerate(self.variables[new_type], 3): label.grid(row=i, column=0) var.grid(row=i, column=1) def grid_size(self): return (4, 2) def show(self): self.grid(row=0, column=0, columnspan=2, rowspan=4, sticky=tk.NSEW) self.grid_columnconfigure(0, weight=1) self.grid_columnconfigure(1, weight=4) def collect_info(self): # TODO: Добавить валидацию данных в этих полях. cur_type = self.preset_type.get() v = self.variables[cur_type] if cur_type == PresetType.PARABOLA: return { "status": "ok", "graph": "preset", "type": "parabola", "min": self.min.get(), "max": self.max.get(), "a": v[0][1].get(), "b": v[1][1].get(), "c": v[2][1].get() } elif cur_type == PresetType.LINE: return { "status": "ok", "graph": "preset", "type": "line", "min": self.min.get(), "max": self.max.get(), "a": v[0][1].get(), "b": v[1][1].get() } class UserPlotFrame(tk.Frame): def __init__(self, master): super().__init__(master) self.master = master self.create_widgets() def create_widgets(self): pass def show(self): self.grid(row=0, column=0, columnspan=1, rowspan=2, sticky=tk.NSEW) def collect_info(self): return {"status": "ok"} class PointPlotFrame(tk.Frame): def __init__(self, master): super().__init__(master) self.master = master self.show() self.create_widgets() self._path = "" def create_widgets(self): self.choose_file = tk.Button(self) self.choose_file["text"] = "Выбрать файл" self.choose_file["command"] = self.open_file self.choose_file.grid(row=0, column=0, columnspan=1, rowspan=1, padx=3, pady=3, sticky=tk.NSEW) self.filepath = tk.Entry(self) self.filepath.grid(row=1, column=0, sticky=tk.NSEW) def open_file(self): file_handle = tk.filedialog.askopenfile() if file_handle is not None: self._path = file_handle.name self.filepath.delete(0, tk.END) self.filepath.insert(0, file_handle.name) def show(self): gr, gc = (2, 1) self.grid(row=0, column=0, columnspan=gc, rowspan=gr, sticky=tk.NSEW) grid_conf(self, gr, gc) def collect_info(self): # TODO: Добавить обработку ошибок (нет файла, нельзя прочитать, # неправильный формат данных и т.д) with open(self._path) as f: data = f.read().strip().split() points = [] for line in data: x, y = map(int, line.split(',')) points.append((x, y)) return {"status": "ok", "graph": "point", "points": points} class App(tk.Frame): def __init__(self, master): super().__init__(master) self.grid(row=0, column=0, columnspan=2, rowspan=6, sticky=tk.NSEW) self.create_widgets() def create_widgets(self): # Создаём фреймы с настройкой выбранного типа графика self.cfg_pt = { "row": 0, "column": 1, "rowspan": 5, "columnspan": 1, "padx": 10, "pady": 10, "sticky": tk.NSEW } self.right_frame = ttk.LabelFrame(self) self.right_frame["text"] = "Настройка графика" self.right_frame.grid(**self.cfg_pt) grid_conf(self.right_frame, 5, 1) self.preset_plot_frame = PresetPlotFrame(self.right_frame) self.user_plot_frame = UserPlotFrame(self.right_frame) self.point_plot_frame = PointPlotFrame(self.right_frame) self.pw = { PlotType.PRESET: self.preset_plot_frame, PlotType.USER: self.user_plot_frame, PlotType.POINT: self.point_plot_frame, } # Фрейм с выбором типа графика self.left_frame = ttk.LabelFrame(self) self.left_frame["text"] = "Тип графика" self.left_frame.grid(row=0, column=0, rowspan=5, columnspan=1, padx=10, pady=10, sticky=tk.NSEW) self.plot_type = tk.IntVar() self.w_plot_type = PlotTypeFrame( self.left_frame, self.plot_type, self.change_plot_type ) self.w_plot_type.grid(row=0, column=0, rowspan=5, padx=3, pady=3) ############################# self.change_plot_type() self.submit_btn = tk.Button(self) self.submit_btn["text"] = "Построить график" self.submit_btn["command"] = self.submit self.submit_btn.grid(row=5, column=0, rowspan=1, columnspan=2, padx=10, pady=10, sticky=tk.NSEW) def change_plot_type(self): cur_type = self.plot_type.get() for ptype, widget in self.pw.items(): if cur_type == ptype: widget.show() else: widget.grid_forget() def submit(self): cur_type = self.plot_type.get() widget = self.pw[cur_type] print(widget.collect_info()) def grid_conf(obj, rows, cols, uniform=False): for i in range(rows): obj.grid_rowconfigure(i, weight=1, uniform=uniform) for i in range(cols): obj.grid_columnconfigure(i, weight=1, uniform=uniform) if __name__ == "__main__": root = tk.Tk() app = App(root) grid_conf(app, 6, 2) grid_conf(root, 6, 2) root.mainloop()