diff --git a/src/waypoint_navigation/README.md b/src/waypoint_navigation/README.md index 693cfbc..551bba0 100644 --- a/src/waypoint_navigation/README.md +++ b/src/waypoint_navigation/README.md @@ -2,7 +2,3 @@ - Tkinter 8.6 - Pillow 9.2.0 - ruamel.yaml 0.17.21 - -waypoint_manager 参考 -- https://imagingsolution.net/category/program/python/tkinter/ -- https://daeudaeu.com/tkinter-mousewheel/ diff --git a/src/waypoint_navigation/waypoint_manager/CMakeLists.txt b/src/waypoint_navigation/waypoint_manager/CMakeLists.txt deleted file mode 100644 index 2941813..0000000 --- a/src/waypoint_navigation/waypoint_manager/CMakeLists.txt +++ /dev/null @@ -1,202 +0,0 @@ -cmake_minimum_required(VERSION 3.0.2) -project(waypoint_manager) - -## Compile as C++11, supported in ROS Kinetic and newer -# add_compile_options(-std=c++11) - -## Find catkin macros and libraries -## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) -## is used, also find other catkin packages -find_package(catkin REQUIRED) - -## System dependencies are found with CMake's conventions -# find_package(Boost REQUIRED COMPONENTS system) - - -## Uncomment this if the package has a setup.py. This macro ensures -## modules and global scripts declared therein get installed -## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html -# catkin_python_setup() - -################################################ -## Declare ROS messages, services and actions ## -################################################ - -## To declare and build messages, services or actions from within this -## package, follow these steps: -## * Let MSG_DEP_SET be the set of packages whose message types you use in -## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...). -## * In the file package.xml: -## * add a build_depend tag for "message_generation" -## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET -## * If MSG_DEP_SET isn't empty the following dependency has been pulled in -## but can be declared for certainty nonetheless: -## * add a exec_depend tag for "message_runtime" -## * In this file (CMakeLists.txt): -## * add "message_generation" and every package in MSG_DEP_SET to -## find_package(catkin REQUIRED COMPONENTS ...) -## * add "message_runtime" and every package in MSG_DEP_SET to -## catkin_package(CATKIN_DEPENDS ...) -## * uncomment the add_*_files sections below as needed -## and list every .msg/.srv/.action file to be processed -## * uncomment the generate_messages entry below -## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...) - -## Generate messages in the 'msg' folder -# add_message_files( -# FILES -# Message1.msg -# Message2.msg -# ) - -## Generate services in the 'srv' folder -# add_service_files( -# FILES -# Service1.srv -# Service2.srv -# ) - -## Generate actions in the 'action' folder -# add_action_files( -# FILES -# Action1.action -# Action2.action -# ) - -## Generate added messages and services with any dependencies listed here -# generate_messages( -# DEPENDENCIES -# std_msgs # Or other packages containing msgs -# ) - -################################################ -## Declare ROS dynamic reconfigure parameters ## -################################################ - -## To declare and build dynamic reconfigure parameters within this -## package, follow these steps: -## * In the file package.xml: -## * add a build_depend and a exec_depend tag for "dynamic_reconfigure" -## * In this file (CMakeLists.txt): -## * add "dynamic_reconfigure" to -## find_package(catkin REQUIRED COMPONENTS ...) -## * uncomment the "generate_dynamic_reconfigure_options" section below -## and list every .cfg file to be processed - -## Generate dynamic reconfigure parameters in the 'cfg' folder -# generate_dynamic_reconfigure_options( -# cfg/DynReconf1.cfg -# cfg/DynReconf2.cfg -# ) - -################################### -## catkin specific configuration ## -################################### -## The catkin_package macro generates cmake config files for your package -## Declare things to be passed to dependent projects -## INCLUDE_DIRS: uncomment this if your package contains header files -## LIBRARIES: libraries you create in this project that dependent projects also need -## CATKIN_DEPENDS: catkin_packages dependent projects also need -## DEPENDS: system dependencies of this project that dependent projects also need -catkin_package( -# INCLUDE_DIRS include -# LIBRARIES waypoint_manager -# CATKIN_DEPENDS other_catkin_pkg -# DEPENDS system_lib -) - -########### -## Build ## -########### - -## Specify additional locations of header files -## Your package locations should be listed before other locations -include_directories( -# include -# ${catkin_INCLUDE_DIRS} -) - -## Declare a C++ library -# add_library(${PROJECT_NAME} -# src/${PROJECT_NAME}/waypoint_manager.cpp -# ) - -## Add cmake target dependencies of the library -## as an example, code may need to be generated before libraries -## either from message generation or dynamic reconfigure -# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) - -## Declare a C++ executable -## With catkin_make all packages are built within a single CMake context -## The recommended prefix ensures that target names across packages don't collide -# add_executable(${PROJECT_NAME}_node src/waypoint_manager_node.cpp) - -## Rename C++ executable without prefix -## The above recommended prefix causes long target names, the following renames the -## target back to the shorter version for ease of user use -## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node" -# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "") - -## Add cmake target dependencies of the executable -## same as for the library above -# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) - -## Specify libraries to link a library or executable target against -# target_link_libraries(${PROJECT_NAME}_node -# ${catkin_LIBRARIES} -# ) - -############# -## Install ## -############# - -# all install targets should use catkin DESTINATION variables -# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html - -## Mark executable scripts (Python etc.) for installation -## in contrast to setup.py, you can choose the destination -# catkin_install_python(PROGRAMS -# scripts/my_python_script -# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} -# ) - -## Mark executables for installation -## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html -# install(TARGETS ${PROJECT_NAME}_node -# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} -# ) - -## Mark libraries for installation -## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html -# install(TARGETS ${PROJECT_NAME} -# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} -# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} -# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} -# ) - -## Mark cpp header files for installation -# install(DIRECTORY include/${PROJECT_NAME}/ -# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} -# FILES_MATCHING PATTERN "*.h" -# PATTERN ".svn" EXCLUDE -# ) - -## Mark other files for installation (e.g. launch and bag files, etc.) -# install(FILES -# # myfile1 -# # myfile2 -# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} -# ) - -############# -## Testing ## -############# - -## Add gtest based cpp test target and link libraries -# catkin_add_gtest(${PROJECT_NAME}-test test/test_waypoint_manager.cpp) -# if(TARGET ${PROJECT_NAME}-test) -# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) -# endif() - -## Add folders to be run by python nosetests -# catkin_add_nosetests(test) diff --git a/src/waypoint_navigation/waypoint_manager/launch/start_manager.launch b/src/waypoint_navigation/waypoint_manager/launch/start_manager.launch deleted file mode 100644 index e69de29..0000000 --- a/src/waypoint_navigation/waypoint_manager/launch/start_manager.launch +++ /dev/null diff --git a/src/waypoint_navigation/waypoint_manager/package.xml b/src/waypoint_navigation/waypoint_manager/package.xml deleted file mode 100644 index e77ca19..0000000 --- a/src/waypoint_navigation/waypoint_manager/package.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - waypoint_manager - 0.0.0 - The waypoint_manager package - - - - - ubuntu - - - - - - TODO - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - catkin - - - - - - - - diff --git a/src/waypoint_navigation/waypoint_manager/scripts/icons/delete_param_btn.png b/src/waypoint_navigation/waypoint_manager/scripts/icons/delete_param_btn.png new file mode 100644 index 0000000..86f4660 --- /dev/null +++ b/src/waypoint_navigation/waypoint_manager/scripts/icons/delete_param_btn.png Binary files differ diff --git a/src/waypoint_navigation/waypoint_manager/scripts/icons/new_param_btn.png b/src/waypoint_navigation/waypoint_manager/scripts/icons/new_param_btn.png new file mode 100644 index 0000000..fdf4e36 --- /dev/null +++ b/src/waypoint_navigation/waypoint_manager/scripts/icons/new_param_btn.png Binary files differ diff --git a/src/waypoint_navigation/waypoint_manager/scripts/manager_GUI.py b/src/waypoint_navigation/waypoint_manager/scripts/manager_GUI.py index cd38a8a..24696f8 100755 --- a/src/waypoint_navigation/waypoint_manager/scripts/manager_GUI.py +++ b/src/waypoint_navigation/waypoint_manager/scripts/manager_GUI.py @@ -3,8 +3,10 @@ import math import ruamel.yaml import gc +import itertools from pathlib import Path from tkinter import messagebox +from PIL import Image, ImageTk from lib.mymaplib import MyMap from lib.waypointlib import WaypointList, FinishPose, get_waypoint_yaml @@ -94,19 +96,27 @@ self.master.bind("", self.ctrl_right_click) self.master.bind("", self.window_resize_callback) - #### その他必要になる変数 #### - self.mymap = None - self.waypoints = None - self.finish_pose = None - self.waypoints_filepath = None + #### アイコン #### + icon = Image.open(Path(__file__).parent / Path("icons","new_param_btn.png")) + icon = icon.resize((30, 30)) + self.new_param_icon = ImageTk.PhotoImage(image=icon) + icon = Image.open(Path(__file__).parent / Path("icons","delete_param_btn.png")) + icon = icon.resize((30, 30)) + self.del_param_icon = ImageTk.PhotoImage(image=icon) + + #### その他必要になる変数,オブジェクト #### + self.mymap: MyMap = None + self.waypoints: WaypointList = None + self.finish_pose: FinishPose = None + self.waypoints_filepath: Path = None self.editing_waypoint_id = None # 編集中のウェイポイントを示す図形のオブジェクトID - self.moving_waypoint = False # ウェイポイントをDnDで動かしている最中かどうか + self.moving_waypoint = False # ウェイポイントをmoveで動かしている最中かどうか self.setting_finish_pose = 0 # finish pose のセット中かどうか self.old_click_point = None # 最後にカーソルのあった座標を保持 self.wp_info_win: tk.Toplevel = None # ウェイポイント情報を表示するウィンドウ self.point_rad = 10 # 画像上に示すポイントの半径ピクセル self.footprint = [[0.25, 0.4], [0.25, -0.4], [-0.65, -0.4], [-0.65, 0.4]] - self.footprint_id = [] + self.trajectory = [] return @@ -142,6 +152,7 @@ return self.message("Read map file " + map_path) self.canvas.delete("all") + self.trajectory = [] if self.waypoints is not None: self.waypoints.number_dict = {} ## キャンバスサイズに合わせて画像を表示 scale = 1 @@ -170,6 +181,7 @@ self.menu_open_waypoints() else: self.plot_waypoints() + self.draw_trajectory() gc.collect() return @@ -185,12 +197,6 @@ elif (self.waypoints is not None): self.master.title(str(self.master.title()).replace(self.waypoints_filepath.name + " - ", "")) - self.canvas.delete("all") - self.draw_image() - self.plot_origin() - if (self.wp_info_win is not None) and (self.wp_info_win.winfo_exists()): - self.wp_info_win.destroy() - filepath = tkinter.filedialog.askopenfilename( parent=self.master, title="Select waypoints yaml file", @@ -203,11 +209,20 @@ if (not "waypoints" in wp_yaml.keys()) or (not "finish_pose" in wp_yaml.keys()): messagebox.showerror(title="Format error", message="Selected waypoints file is unexpected format.") return + + self.canvas.delete("all") + self.trajectory = [] + self.draw_image() + self.plot_origin() + if (self.wp_info_win is not None) and (self.wp_info_win.winfo_exists()): + self.wp_info_win.destroy() + del self.waypoints self.waypoints = WaypointList(wp_yaml) self.finish_pose = FinishPose(wp_yaml) self.waypoints_filepath = Path(filepath) self.plot_waypoints() + self.draw_trajectory() self.master.title(self.waypoints_filepath.name + " - " + self.master.title()) self.message("Read waypoints file " + filepath) self.file_menu.entryconfigure("Save", state=tk.NORMAL) @@ -339,6 +354,7 @@ sub_win.destroy() self.update_title() self.plot_waypoints() + self.draw_trajectory() return frame3 = tk.Frame(sub_win) set_btn = tk.Button(frame3, text="Set", width=5, command=set_btn_callback) @@ -403,10 +419,8 @@ Y = xy[0]*math.sin(th) + xy[1]*math.cos(th) + y cx, cy = self.real2canvas(float(X), float(Y)) polygon.append([cx, cy]) - id = self.canvas.create_polygon(polygon, fill="", outline="green") - self.canvas.lower(id) - self.canvas.lift(id, "map_image") - self.footprint_id.append(id) + id = self.canvas.create_polygon(polygon, fill="", outline="green", tags="footprint") + self.canvas.lift("footprint", "map_image") return ## if (self.show_fp.get()): @@ -417,9 +431,7 @@ # last waypoint create_footprint(float(next_wp["x"]), float(next_wp["y"]), self.finish_pose.x, self.finish_pose.y) else: - for id in self.footprint_id: - self.canvas.delete(id) - self.footprint_id = [] + self.canvas.delete("footprint") return @@ -433,16 +445,18 @@ +++++ 地図上の原点に円を描画 +++++ """ def plot_origin(self): - x, y = self.mymap.transform(self.mymap.img_origin[0], self.mymap.img_origin[1]) + cx, cy = self.mymap.transform(self.mymap.img_origin[0], self.mymap.img_origin[1]) r = self.point_rad # 円の半径(ピクセル) - x0 = x - r - y0 = y - r - x1 = x + r + 1 - y1 = y + r + 1 + x0 = cx - r + y0 = cy - r + x1 = cx + r + 1 + y1 = cy + r + 1 if self.canvas.find_withtag("origin"): self.canvas.moveto("origin", x0, y0) + self.trajectory[0] = [cx, cy] else: self.canvas.create_oval(x0, y0, x1, y1, tags="origin", fill='cyan', outline='blue') + self.trajectory.append([cx, cy]) return @@ -457,7 +471,8 @@ cx, cy = self.real2canvas(float(wp["x"]), float(wp["y"])) x0 = cx - self.point_rad y0 = cy - self.point_rad - self.canvas.moveto(id, round(x0), round(y0)) + self.canvas.moveto(id, x0, y0) + self.trajectory[self.waypoints.get_num(id)] = [cx, cy] return if len(self.waypoints.get_id_list()) == 0: @@ -472,7 +487,8 @@ cx, cy = self.real2canvas(float(wp["x"]), float(wp["y"])) x0 = cx - self.point_rad y0 = cy - self.point_rad - self.canvas.moveto(id, round(x0), round(y0)) + self.canvas.moveto(id, x0, y0) + self.trajectory[self.waypoints.get_num(id)] = [cx, cy] # trajectory # Finish poseを描画 cx, cy = self.real2canvas(self.finish_pose.x, self.finish_pose.y) x0 = cx @@ -483,31 +499,50 @@ # movetoだと上手くいかないので、毎回削除、再描画 self.canvas.delete(self.finish_pose.id) self.finish_pose.id = self.canvas.create_line(x0, y0, x1, y1, tags="finish_pose", - width=10, arrow=tk.LAST, arrowshape=(12,15,9), fill="#AAF" + width=10, arrow=tk.LAST, arrowshape=(12,15,9), fill="#AAF" ) + # trajectory + if self.canvas.find_withtag("trajectory"): + self.trajectory[-1] = [cx, cy] + else: + self.trajectory.append([cx, cy]) return """ + +++++ ウェイポイントをつないだ軌道を描画する +++++ + """ + def draw_trajectory(self): + if self.canvas.find_withtag("trajectory"): + self.canvas.coords("trajectory", list(itertools.chain.from_iterable(self.trajectory))) + else: + self.canvas.create_line(list(itertools.chain.from_iterable(self.trajectory)), + fill="#DF2", width=2, tags="trajectory") + self.canvas.lift("trajectory", "map_image") + return + + + """ +++++ キャンバスに新たなウェイポイントを描画する +++++ """ def create_waypoint(self, waypoint: dict): img_x, img_y = self.mymap.real2image(float(waypoint["x"]), float(waypoint["y"])) - cx, cy = self.mymap.transform(img_x, img_y) - x0 = round(cx - self.point_rad) - y0 = round(cy - self.point_rad) - x1 = round(cx + self.point_rad + 1) - y1 = round(cy + self.point_rad + 1) + cx, cy = self.real2canvas(float(waypoint["x"]), float(waypoint["y"])) + x0 = (cx - self.point_rad) + y0 = (cy - self.point_rad) + x1 = (cx + self.point_rad + 1) + y1 = (cy + self.point_rad + 1) if (img_x < 0) or (img_y < 0) or (img_x > self.mymap.width()) or (img_y > self.mymap.height()): - id = self.canvas.create_oval(x0, y0, x1, y1, fill='#FEE', outline='#FAA', activefill='#F88') + id = self.canvas.create_oval(x0, y0, x1, y1, fill='#FEE', outline='#FAA', activefill='#F88', tags="waypoints") self.canvas.tag_bind(id, "", lambda event, wp_id=id: self.waypoint_enter(event, wp_id)) self.canvas.tag_bind(id, "", self.waypoint_leave) else: - id = self.canvas.create_oval(x0, y0, x1, y1, fill='#FDD', outline='red', activefill='red') + id = self.canvas.create_oval(x0, y0, x1, y1, fill='#FDD', outline='red', activefill='red', tags="waypoints") self.canvas.tag_bind(id, "", lambda event, wp_id=id: self.waypoint_clicked(event, wp_id)) self.canvas.tag_bind(id, "", lambda event, wp_id=id: self.waypoint_enter(event, wp_id)) self.canvas.tag_bind(id, "", self.waypoint_leave) self.canvas.tag_bind(id, "", self.waypoint_click_move) + self.trajectory.append([cx, cy]) #trajectory return id @@ -537,9 +572,9 @@ delta_y = event.y-self.old_click_point[1] self.canvas.move(self.editing_waypoint_id, delta_x, delta_y) box = self.canvas.bbox(self.editing_waypoint_id) - px = (box[2] + box[0]) / 2 # ウィンドウ上の座標 - py = (box[3] + box[1]) / 2 - img_x, img_y = self.mymap.inv_transform(px, py) + cx = (box[2] + box[0]) / 2 # ウィンドウ上の座標 + cy = (box[3] + box[1]) / 2 + img_x, img_y = self.mymap.inv_transform(cx, cy) # マップ画像上の座標を、実際の座標に変換 x, y = self.mymap.image2real(img_x, img_y) # 編集中のウェイポイント情報を更新 @@ -554,6 +589,9 @@ txt_box.insert(tk.END, y) self.old_click_point = [event.x, event.y] self.update_title() + # trajectory + self.trajectory[self.waypoints.get_num(self.editing_waypoint_id)] = [cx, cy] + self.draw_trajectory() return @@ -594,22 +632,24 @@ txt_box = tk.Entry(self.wp_info_win, width=20, font=("Consolas", 15)) txt_box.insert(tk.END, str(point[key]).lower()) txt_box.grid(column=1, row=i, padx=2, pady=2, ipady=3, sticky=tk.EW) - del_btn = tk.Button(self.wp_info_win, text="-", width=2, bg="red", fg="white", font=("",13,"bold")) + del_btn = tk.Button(self.wp_info_win, image=self.del_param_icon, relief=tk.FLAT) del_btn["command"] = lambda name=key, val=str(point[key]).lower(): self.del_param_btn_callback(name, val) del_btn.grid(column=2, row=i, padx=5, pady=5) + self.wp_info_win.grid_columnconfigure(1, weight=1) # New parameter - new_param_btn = tk.Button(self.wp_info_win, text="New Parameter", bg="#AFA") + new_param_btn = tk.Button(self.wp_info_win, image=self.new_param_icon, relief=tk.FLAT) new_param_btn["command"] = self.new_param_btn_callback - new_param_btn.grid(column=0, columnspan=2, row=self.wp_info_win.grid_size()[1], pady=10) - # Apply, DnD(Drag & Drop), remove ボタン + new_param_btn.grid(column=0, columnspan=3, row=self.wp_info_win.grid_size()[1], pady=10) + # Apply, Move(Drag & Drop), remove ボタン canv = tk.Canvas(self.wp_info_win) canv.grid(column=0, columnspan=3, row=self.wp_info_win.grid_size()[1], sticky=tk.EW, pady=5) apply_btn = tk.Button(canv, text="Apply", width=5, height=1, bg="#FDD", command=self.apply_btn_callback) apply_btn.pack(side=tk.RIGHT, anchor=tk.SE, padx=5, pady=5) - dnd_btn = tk.Button(canv, text="DnD", width=5, height=1, bg="#EEE") - dnd_btn["command"] = lambda obj=dnd_btn: self.dnd_btn_callback(dnd_btn) - dnd_btn.pack(side=tk.RIGHT, anchor=tk.SE, padx=5, pady=5) + self.wp_info_win.bind('', self.apply_btn_callback) + move_btn = tk.Button(canv, text="Move", width=5, height=1, bg="#EEE") + move_btn["command"] = lambda obj=move_btn: self.move_btn_callback(move_btn) + move_btn.pack(side=tk.RIGHT, anchor=tk.SE, padx=5, pady=5) remove_btn = tk.Button(canv, text="Remove", width=7, height=1, bg="#F00", command=self.remove_btn_callback) remove_btn.pack(side=tk.LEFT, anchor=tk.SE, padx=5, pady=5) @@ -617,8 +657,8 @@ self.wp_info_win.update() w = self.wp_info_win.winfo_width() h = self.wp_info_win.winfo_height() - x = self.canvas.winfo_x() + self.canv_w - w - y = self.canvas.winfo_y() + self.canv_h - h + x = self.canvas.winfo_x() + self.canv_w - w -10 + y = self.canvas.winfo_y() + self.canv_h - h -10 self.wp_info_win.lift() self.wp_info_win.attributes('-topmost', True) # サブウィンドウを最前面で固定 self.wp_info_win.geometry("+{}+{}".format(x, y)) @@ -629,21 +669,26 @@ """ +++++ Applyボタンを押したときのコールバック +++++ """ - def apply_btn_callback(self): + def apply_btn_callback(self, event=None): + if (event is not None) and (event.keysym != "Return"): return point = self.waypoints.get_waypoint(id=self.editing_waypoint_id) for i, key in enumerate(point.keys()): txt_box = self.wp_info_win.grid_slaves(column=1, row=i)[0] - self.waypoints.set_waypoint_val(self.editing_waypoint_id, key, txt_box.get()) - self.plot_waypoints(id=self.editing_waypoint_id) - self.message("Apply changes of waypoint parameters") - self.update_title() + val = txt_box.get() + if (str(point[key]) == val): continue + self.update_title() + self.message("Apply changes of waypoint parameters") + self.waypoints.set_waypoint_val(self.editing_waypoint_id, key, val) + if ((key == "x") or (key == "y")): + self.plot_waypoints(self.editing_waypoint_id) + self.draw_trajectory() return """ - +++++ ドラッグ&ドロップボタン(DnD)を押したときのコールバック +++++ + +++++ ドラッグ&ドロップボタン(Moveボタン)を押したときのコールバック +++++ """ - def dnd_btn_callback(self, obj=None): + def move_btn_callback(self, obj=None): if obj is None: return btn = obj # 押された状態とそうでない状態を切り替える @@ -664,11 +709,15 @@ +++++ removeボタンを押したときのコールバック +++++ """ def remove_btn_callback(self): - self.waypoints.remove(self.editing_waypoint_id) - self.canvas.delete(self.editing_waypoint_id) # ウェイポイントを示す円を削除 - self.close_wp_info() - self.message("Removed waypoint") - self.update_title() + yn = messagebox.askyesno("Delete parameter", message="Are you sure you want to remove this waypoint?") + if (yn == True): + self.trajectory.pop(self.waypoints.get_num(self.editing_waypoint_id)) + self.waypoints.remove(self.editing_waypoint_id) + self.canvas.delete(self.editing_waypoint_id) # ウェイポイントを示す円を削除 + self.close_wp_info() + self.message("Removed waypoint") + self.update_title() + self.draw_trajectory() return @@ -794,7 +843,7 @@ win.attributes('-topmost', True) return num = int(num) - if (num < 0) or (num > len(self.waypoints.waypoints)+1): + if (num < 1) or (num > len(self.waypoints.waypoints)+1): win.attributes('-topmost', False) messagebox.showwarning(title="Warning", message="The number is out of range.") win.attributes('-topmost', True) @@ -811,8 +860,11 @@ elif (key=="z"): point[key] = 0.0 else: point[key] = "" id = self.create_waypoint(point) + cx,cy = self.trajectory.pop(-1) + self.trajectory[num:num] = [[cx, cy]] self.waypoints.insert(num, point, id=id) self.plot_waypoints(id=id) + self.draw_trajectory() self.editing_waypoint_id = id self.canvas.itemconfig(id, fill='red') self.disp_waypoint_info(id) @@ -853,24 +905,6 @@ """ - +++++ マウスホイールを回転したとき(タッチパッドをドラッグしたとき) +++++ - """ - def mouse_wheel(self, event): - if not self.mymap: return - if event.delta > 0: - scale = 1.1 # 上に回転(タッチパッドなら下にドラッグ)=> 拡大 - else: - scale = 0.9 # 下に回転(タッチパッドなら上にドラッグ)=> 縮小 - self.mymap.scale_at(event.x, event.y, scale) - self.draw_image() - self.plot_origin() - self.plot_waypoints() - for id in self.footprint_id: - self.canvas.scale(id, event.x, event.y, scale, scale) - return - - - """ +++++ 左クリックされたとき +++++ """ def left_click(self, event): @@ -902,6 +936,8 @@ self.finish_pose.id = self.canvas.create_line(x0, y0, x1, y1, tags="finish_pose", width=10, arrow=tk.LAST, arrowshape=(12,15,9), fill="#AAF" ) + self.trajectory[-1] = [x0, y0] + self.draw_trajectory() img_x, img_y = self.mymap.inv_transform(x0, y0) real_x, real_y = self.mymap.image2real(img_x, img_y) self.finish_pose.x = real_x @@ -932,9 +968,14 @@ # origin, waypoints finish_pose を平行移動 self.canvas.move("origin", delta_x, delta_y) if self.waypoints: - for id in list(self.waypoints.get_id_list()) + self.footprint_id: - self.canvas.move(id, delta_x, delta_y) + self.canvas.move("waypoints", delta_x, delta_y) self.canvas.move("finish_pose", delta_x, delta_y) + self.canvas.move("trajectory", delta_x, delta_y) + self.canvas.lift("trajectory", "map_image") + self.canvas.move("footprint", delta_x, delta_y) + for i in range(0, len(self.trajectory)): + self.trajectory[i][0] = self.trajectory[i][0] + delta_x + self.trajectory[i][1] = self.trajectory[i][1] + delta_y self.old_click_point = [event.x, event.y] return @@ -986,8 +1027,8 @@ self.draw_image() self.plot_origin() self.plot_waypoints() - for id in self.footprint_id: - self.canvas.scale(id, event.x, event.y, scale, scale) + self.canvas.scale("footprint", event.x, event.y, scale, scale) + self.canvas.scale("trajectory", event.x, event.y, scale, scale) self.message("Zoom In") return @@ -1003,13 +1044,31 @@ self.draw_image() self.plot_origin() self.plot_waypoints() - for id in self.footprint_id: - self.canvas.scale(id, event.x, event.y, scale, scale) + self.canvas.scale("footprint", event.x, event.y, scale, scale) + self.canvas.scale("trajectory", event.x, event.y, scale, scale) self.message("Zoom Out") return """ + +++++ マウスホイールを回転したとき(タッチパッドをドラッグしたとき) +++++ + """ + def mouse_wheel(self, event): + if not self.mymap: return + if event.delta > 0: + scale = 1.1 # 上に回転(タッチパッドなら下にドラッグ)=> 拡大 + else: + scale = 0.9 # 下に回転(タッチパッドなら上にドラッグ)=> 縮小 + self.mymap.scale_at(event.x, event.y, scale) + self.draw_image() + self.plot_origin() + self.plot_waypoints() + self.canvas.scale("footprint", event.x, event.y, scale, scale) + self.canvas.scale("trajectory", event.x, event.y, scale, scale) + return + + + """ +++++ ウィンドウサイズが変更されたとき、情報を更新する +++++ """ def window_resize_callback(self, event): @@ -1060,14 +1119,14 @@ def real2canvas(self, x, y): img_x, img_y = self.mymap.real2image(x,y) cx, cy = self.mymap.transform(img_x, img_y) - return cx, cy + return round(cx), round(cy) def update_title(self): title = self.master.title() if title[0] != "*": self.master.title("* "+title) - + diff --git a/src/waypoint_navigation/waypoint_manager/scripts/to_exe.bat b/src/waypoint_navigation/waypoint_manager/scripts/to_exe.bat new file mode 100644 index 0000000..30a7c4c --- /dev/null +++ b/src/waypoint_navigation/waypoint_manager/scripts/to_exe.bat @@ -0,0 +1,14 @@ +pyinstaller.exe manager_GUI.py --noconsole --noconfirm + +if exist .\waypoint_manager PowerShell -command " rm -r .\waypoint_manager " +PowerShell -command " mv .\dist\manager_GUI .\waypoint_manager " +PowerShell -command " rm -r .\build " +PowerShell -command " rm -r .\dist " +PowerShell -command " rm .\manager_GUI.spec " +PowerShell -command " cp -r .\icons .\waypoint_manager\ " + + + + + + diff --git a/src/waypoint_navigation/waypoint_manager/scripts/to_exe.sh b/src/waypoint_navigation/waypoint_manager/scripts/to_exe.sh new file mode 100755 index 0000000..00dddf8 --- /dev/null +++ b/src/waypoint_navigation/waypoint_manager/scripts/to_exe.sh @@ -0,0 +1,13 @@ +python -m PyInstaller manager_GUI.py --noconsole --noconfirm + +if [ -d ./waypoint_manager ]; then + rm -r ./waypoint_manager +fi + +mv ./dist/manager_GUI ./waypoint_manager +rm -r ./build +rm -r ./dist +rm ./manager_GUI.spec +cp -r ./icons ./waypoint_manager/ + +# If the ModuleNotFoundError occur, copy the module from your environment diff --git a/src/waypoint_navigation/waypoint_manager/waypoint-manager-for-ubuntu.tar b/src/waypoint_navigation/waypoint_manager/waypoint-manager-for-ubuntu.tar new file mode 100644 index 0000000..d247cb0 --- /dev/null +++ b/src/waypoint_navigation/waypoint_manager/waypoint-manager-for-ubuntu.tar Binary files differ diff --git a/src/waypoint_navigation/waypoint_manager/waypoint-manager-for-windows10.zip b/src/waypoint_navigation/waypoint_manager/waypoint-manager-for-windows10.zip new file mode 100644 index 0000000..48cd956 --- /dev/null +++ b/src/waypoint_navigation/waypoint_manager/waypoint-manager-for-windows10.zip Binary files differ