import tkinter as tk import tkinter.filedialog import re from pathlib import Path from .tools import Tools class Application(tk.Frame): def __init__(self, master): super().__init__(master) self.theme = {"main": "#444", "bg1": "#222"} #### 画面上部のメニューバーを作成 #### self.menu_bar = tk.Menu(self) ## Fileメニューの作成 self.file_menu = tk.Menu(self.menu_bar, tearoff=tk.OFF, bg=self.theme["main"], fg="white", activebackground="gray", activeborderwidth=5 ) ## Fileメニュー内のOpenメニューを作成 self.open_menu = tk.Menu(self.file_menu, tearoff=tk.OFF, bg=self.theme["main"], fg="white", activebackground="gray", activeborderwidth=5, disabledforeground="black" ) self.open_menu.add_command(label="Base map", command=self.menu_open_base) self.open_menu.add_command(label="Additional map", command=self.menu_open_addtion, state="disabled") self.file_menu.add_cascade(label="Open", menu=self.open_menu) ## Fileメニューその他 self.file_menu.add_command(label="Export", command=self.menu_export, accelerator="Ctrl+E") self.file_menu.add_separator() self.file_menu.add_command(label="Exit", command=self.menu_exit, accelerator="Ctrl+Q") ## キーボードショートカットを設定 self.bind_all("<Control-e>", self.menu_export) self.bind_all("<Control-q>", self.menu_exit) ## 大元に作成したメニューバーを設定 self.menu_bar.add_cascade(label=" File ", menu=self.file_menu) # Fileメニューとしてバーに追加 self.master.config(menu=self.menu_bar) #### 仕切り線で大きさを変更できるウィンドウ #### paned_window = tk.PanedWindow(self.master, sashwidth=3, bg="gray", relief=tk.RAISED) paned_window.pack(expand=True, fill=tk.BOTH) #### ツールボタン群とレイヤーリストを表示するフレーム #### self.tools = Tools(paned_window, self.theme, width=300, bg=self.theme["main"]) paned_window.add(self.tools, minsize=self.tools.min_width, padx=2, pady=2) ## マップ画像を表示するフレームを追加 paned_window.add(self.tools.map_disp, minsize=self.tools.map_disp.min_width, padx=2, pady=2) return """ ++++++++++ File menu functions ++++++++++ """ def menu_open_base(self, event=None): map_path = self.menu_open(title="Select base map yaml file") if not map_path: return waypoints_path = self.menu_open(title="Select waypoints file for the map") if not waypoints_path: return self.tools.set_base_map(Path(map_path).resolve(), Path(waypoints_path).resolve()) self.open_menu.entryconfigure("Base map", state="disabled") self.open_menu.entryconfigure("Additional map", state="normal") return def menu_open_addtion(self, event=None): map_path = self.menu_open(title="Select additional map yaml file") if not map_path: return waypoints_path = self.menu_open(title="Select waypoints file for the map") if not waypoints_path: return self.tools.add_map(Path(map_path).resolve(), Path(waypoints_path).resolve()) return def menu_open(self, title): filepath = tkinter.filedialog.askopenfilename( parent=self.master, title=title, initialdir=str(Path(".")), filetypes=[("YAML", ".yaml")] ) return filepath def menu_export(self, event=None): if (len(self.tools.label_list) < 2): return win = tk.Toplevel() win.geometry("800x300+50+50") win.minsize(width=500, height=300) win.attributes('-topmost', True) win.title("Export") font = ("Consolas", 12) ### ファイルパスを参照するダイアログを開く関数(ボタンコールバック) ### def ref_btn_callback_f(entry: tk.Entry, init_dir): filepath = tkinter.filedialog.asksaveasfilename( parent=win, title="File path to export", initialdir=init_dir, filetypes=[("YAML", ".yaml")] ) if not filepath: return entry.delete(0, tk.END) entry.insert(tk.END, str(filepath)) ### ファルダパスを参照するダイアログを開く関数(ボタンコールバック) ### def ref_btn_callback_d(entry: tk.Entry, init_dir): dirpath = tkinter.filedialog.askdirectory( parent=win, title="Directory path to export maps", initialdir=init_dir ) if not dirpath: return entry.delete(0, tk.END) entry.insert(tk.END, str(dirpath)) ### ファイルの保存場所の表示、変更をするフィールドを作成する関数 ### def create_entry(label_txt, init_path, callback): frame = tk.Frame(win) frame.pack(expand=False, fill=tk.X, padx=5, pady=10) label = tk.Label(frame, text=label_txt, anchor=tk.W, font=font) label.grid(column=0, row=0, padx=3, pady=2, sticky=tk.EW) ref_btn = tk.Button(frame, image=self.tools.folder_icon) ref_btn.grid(column=1, row=1, sticky=tk.E, padx=5) tbox = tk.Entry(frame, font=font) tbox.grid(column=0, row=1, padx=20, pady=2, sticky=tk.EW) tbox.insert(tk.END, init_path) init_dir = str(Path(init_path).parent) ref_btn["command"] = lambda entry=tbox, init_dir=init_dir: callback(entry, init_dir) frame.grid_columnconfigure(0, weight=1) return tbox ## マルチマップyaml、結合したウェイポイント、合成した地図画像と情報yamlを取得 # multimap_yaml, path = self.tools.get_multimap_yaml() map_img_list, map_yaml_list, path = self.tools.get_map_lists() tbox1 = create_entry("- Multi mps directory:", str(path), ref_btn_callback_d) wp_yaml, path = self.tools.get_waypoints_yaml() tbox2 = create_entry("- Merged waypoints yaml file:", str(path), ref_btn_callback_f) merged_img_pil, merged_yaml, path = self.tools.get_merged_map() tbox3 = create_entry("- Merged map (yaml, pgm) file:", str(path), ref_btn_callback_f) ### それぞれをファイルに書き込む関数(ボタンコールバック) ### def export_btn_callback(): # multi maps path = tbox1.get() for i, img in enumerate(map_img_list): img_path = Path(path) / Path("map{}.pgm".format(i)) img.save(str(img_path.resolve())) map_yaml_list[i]["image"] = str(img_path.resolve()) str_yaml = "" line = "\n" for key, val in map_yaml_list[i].items(): str_yaml += "{}: {}".format(key, val) + line with open(str(img_path.with_suffix(".yaml")), 'w') as f: f.write(str_yaml) # merged waypoints path = tbox2.get() with open(path, 'w') as f: f.write(wp_yaml) # merged map path = Path(tbox3.get()).resolve() new_merged_yaml = re.sub("image: .*\n", "image: {}\n".format(str(path.with_suffix(".pgm"))), merged_yaml) with open(str(path.with_suffix(".yaml")), 'w') as f: f.write(new_merged_yaml) merged_img_pil.save(str(path.with_suffix(".pgm"))) win.destroy() return ## エクスポートボタン、キャンセルボタン export_btn = tk.Button(win, text="Export", font=font) export_btn["command"] = export_btn_callback export_btn.pack(side=tk.RIGHT, padx=50, pady=20) cancel_btn = tk.Button(win, text="Cancel", font=font) cancel_btn["command"] = win.destroy cancel_btn.pack(side=tk.LEFT, padx=50, pady=20) return def menu_exit(self, event=None): self.master.destroy()