import tkinter as tk, robomasterpy as rm from tkinter import ttk import datetime class RobomasterUI: def __init__(self, master, cmd): self.master = master master.title("Robomaster Control") self.cmd = cmd master.configure(bg='#121212') style = ttk.Style(master) style.theme_use('clam') style.configure('TLabelframe', background='#121212', foreground='white') style.configure('TLabelframe.Label', background='#121212', foreground='white') style.configure('TLabel', background='#121212', foreground='white') style.configure('TButton', background='#333333', foreground='white', relief=tk.FLAT) style.map('TButton', background=[('active', '#555555')], foreground=[('active', 'white')]) self.output_text = tk.Text(master, height=10, width=50, state=tk.DISABLED, fg='white', bg='#121212', insertbackground='white', selectbackground='#555555', selectforeground='white') self.output_text.grid(row=6, column=0, columnspan=4, padx=10, pady=10) arm_frame = ttk.LabelFrame(master, text="Arm Control") arm_frame.grid(row=0, column=0, padx=10, pady=10, sticky="n") tk.Label(arm_frame, text="X:", fg='white', bg='#121212').grid(row=0, column=0, padx=5, pady=5, sticky="e") self.x_entry = tk.Entry(arm_frame, width=10, fg='white', bg='#333333', insertbackground='white') self.x_entry.insert(0, "0") self.x_entry.grid(row=0, column=1, padx=5, pady=5) tk.Label(arm_frame, text="Y:", fg='white', bg='#121212').grid(row=1, column=0, padx=5, pady=5, sticky="e") self.y_entry = tk.Entry(arm_frame, width=10, fg='white', bg='#333333', insertbackground='white') self.y_entry.insert(0, "0") self.y_entry.grid(row=1, column=1, padx=5, pady=5) ttk.Button(arm_frame, text="Move", command=self.move_arm).grid(row=0, column=2, padx=5, pady=5) ttk.Button(arm_frame, text="Move To", command=self.moveto_arm).grid(row=1, column=2, padx=5, pady=5) ttk.Button(arm_frame, text="Recenter", command=self.recenter_arm).grid(row=2, column=0, columnspan=3, pady=5, sticky="we") ttk.Button(arm_frame, text="Stop", command=self.stop_arm).grid(row=3, column=0, columnspan=3, pady=5, sticky="we") ttk.Button(arm_frame, text="Get Position", command=self.get_arm_pos).grid(row=4, column=0, columnspan=3, pady=5, sticky="we") gripper_frame = ttk.LabelFrame(master, text="Gripper Control") gripper_frame.grid(row=0, column=1, padx=10, pady=10, sticky="n") tk.Label(gripper_frame, text="Force (1-4, optional):", fg='white', bg='#121212').grid(row=0, column=0, padx=5, pady=5, sticky="e") self.force_entry = tk.Entry(gripper_frame, width=5, fg='white', bg='#333333', insertbackground='white') self.force_entry.grid(row=0, column=1, padx=5, pady=5) ttk.Button(gripper_frame, text="Open Gripper", command=self.open_gripper).grid(row=1, column=0, columnspan=2, pady=5, sticky="we") ttk.Button(gripper_frame, text="Close Gripper", command=self.close_gripper).grid(row=2, column=0, columnspan=2, pady=5, sticky="we") ttk.Button(gripper_frame, text="Gripper Status", command=self.get_gripper_status).grid(row=3, column=0, columnspan=2, pady=5, sticky="we") chassis_frame = ttk.LabelFrame(master, text="Chassis Control") chassis_frame.grid(row=1, column=0, columnspan=2, padx=10, pady=10, sticky="n") tk.Label(chassis_frame, text="XY Speed (m/s):", fg='white', bg='#121212').grid(row=0, column=0, padx=5, pady=5, sticky="e") self.chassis_x_speed_entry = tk.Entry(chassis_frame, width=5, fg='white', bg='#333333', insertbackground='white') self.chassis_x_speed_entry.insert(0, "0.5") self.chassis_x_speed_entry.grid(row=0, column=1, padx=5, pady=5) tk.Label(chassis_frame, text="Z Speed (deg/s):", fg='white', bg='#121212').grid(row=0, column=4, padx=5, pady=5, sticky="e") self.chassis_z_speed_entry = tk.Entry(chassis_frame, width=5, fg='white', bg='#333333', insertbackground='white') self.chassis_z_speed_entry.insert(0, "100") self.chassis_z_speed_entry.grid(row=0, column=5, padx=5, pady=5) ttk.Button(chassis_frame, text="Stop Chassis", command=self.stop_chassis).grid(row=2, column=0, columnspan=6, pady=5, sticky="we") master.bind("", lambda event: self.chassis_move_arrow(x=1)) master.bind("", lambda event: self.chassis_move_arrow(x=-1)) master.bind("", lambda event: self.chassis_move_arrow(y=-1)) master.bind("", lambda event: self.chassis_move_arrow(y=1)) master.bind("", lambda event: self.chassis_move_arrow(z=50)) master.bind("", lambda event: self.chassis_move_arrow(z=-50)) master.bind("", lambda event: self.chassis_move_arrow(x=0) if event.keysym == 'w' else None) master.bind("", lambda event: self.chassis_move_arrow(x=0) if event.keysym == 's' else None) master.bind("", lambda event: self.chassis_move_arrow(y=0) if event.keysym == 'a' else None) master.bind("", lambda event: self.chassis_move_arrow(y=0) if event.keysym == 'd' else None) master.bind("", lambda event: self.chassis_move_arrow(z=0) if event.keysym == 'q' else None) master.bind("", lambda event: self.chassis_move_arrow(z=0) if event.keysym == 'e' else None) master.bind("", lambda event: self.stop_chassis()) master.bind("", self.deselect_textbox) master.columnconfigure(0, weight=1) master.columnconfigure(1, weight=1) def deselect_textbox(self, event): focused_widget = self.master.focus_get() if isinstance(focused_widget, tk.Entry) and event.widget != focused_widget: self.master.focus_set() def _command_executor(self, func, *args, **kwargs): def command_execution(): try: result = func(*args, **kwargs) self.master.after(0, self._display_output, str(result)) except Exception as e: self.master.after(0, self._display_output, f"Error: {e}") self.master.after(0, command_execution) def _display_output(self, output_str): timestamp = datetime.datetime.now().strftime("%H:%M:%S") output_with_timestamp = f"[{timestamp}] {output_str}" self.output_text.config(state=tk.NORMAL) self.output_text.insert(tk.END, output_with_timestamp + "\n") self.output_text.see(tk.END) self.output_text.config(state=tk.DISABLED) def move_arm(self): x_str, y_str = self.x_entry.get(), self.y_entry.get() try: x, y = float(x_str), float(y_str) except ValueError: return self._display_output("Error: Invalid X or Y") self._command_executor(self.cmd.arm_move, x=x, y=y) def moveto_arm(self): x_str, y_str = self.x_entry.get(), self.y_entry.get() try: x, y = float(x_str), float(y_str) except ValueError: return self._display_output("Error: Invalid X or Y") self._command_executor(self.cmd.arm_moveto, x=x, y=y) def recenter_arm(self): self._command_executor(self.cmd.arm_recenter) def stop_arm(self): self._command_executor(self.cmd.arm_stop) def get_arm_pos(self): self._command_executor(self.cmd.arm_pos) def open_gripper(self): force_str = self.force_entry.get() force = None if force_str: try: force = int(force_str) if force not in range(1, 5): raise ValueError except ValueError: return self._display_output("Error: Force must be 1-4") self._command_executor(self.cmd.gripper_open, force=force) def close_gripper(self): force_str = self.force_entry.get() force = None if force_str: try: force = int(force_str) if force not in range(1, 5): raise ValueError except ValueError: return self._display_output("Error: Force must be 1-4") self._command_executor(self.cmd.gripper_close, force=force) def get_gripper_status(self): self._command_executor(self.cmd.gripper_status) def stop_chassis(self): self._command_executor(self.cmd.chassis_move, x=0, y=0, z=0) def chassis_move_arrow(self, x=None, y=None, z=None): try: self._command_executor(self.cmd.chassis_move, x=float(x) if x else 0, y=float(y) if y else 0, z=float(z) if z else 0, speed_xy=float(self.chassis_x_speed_entry.get()), speed_z=float(self.chassis_z_speed_entry.get())) except ValueError: self._display_output("Error: Invalid speed value") def main(): ip_address = "192.168.2.1" try: cmd = rm.Commander(ip=ip_address) print(f"Connected to Robomaster. Version: {cmd.version()}") root = tk.Tk() root.resizable(False, False) RobomasterUI(root, cmd) root.mainloop() except Exception as e: print(f"Error connecting to Robomaster: {e}") print("Make sure the Robomaster is powered on and connected.") if __name__ == "__main__": main()