robomaster-python/controller/main.py

245 lines
13 KiB
Python

import tkinter as tk, robomasterbfi as rm, datetime
from tkinter import ttk
class RobomasterUI:
def __init__(self, master, cmd):
self.master = master
master.title("Robomaster Control")
self.cmd = cmd
self._command_executor(cmd.led_control, comp="all", effect="solid", r=255, g=0, b=0)
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')])
style.configure('TCombobox',
background='#333333',
foreground='white',
fieldbackground='#333333',
selectbackground='#555555',
selectforeground='white',
insertcolor='white')
style.map('TCombobox',
fieldbackground=[('readonly', '#333333'), ('!readonly', '#333333')],
background=[('readonly', '#333333'), ('!readonly', '#333333')])
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")
led_frame = ttk.LabelFrame(master, text="LED Control")
led_frame.grid(row=0, column=2, padx=10, pady=10, sticky="n")
led_comp_values = ["all", "top_all", "top_right", "top_left", "bottom_all", "bottom_front", "bottom_back", "bottom_left", "bottom_right"]
tk.Label(led_frame, text="Comp:", fg='white', bg='#121212').grid(row=0, column=0, padx=5, pady=5, sticky="e")
self.comp_combo = ttk.Combobox(led_frame, values=led_comp_values, width=10, style='DarkCombobox.TCombobox')
self.comp_combo.set(led_comp_values[0])
self.comp_combo.grid(row=0, column=1, padx=5, pady=5)
led_effect_values = ["solid", "off", "pulse", "blink", "scrolling"]
tk.Label(led_frame, text="Effect:", fg='white', bg='#121212').grid(row=1, column=0, padx=5, pady=5, sticky="e")
self.effect_combo = ttk.Combobox(led_frame, values=led_effect_values, width=10, style='DarkCombobox.TCombobox')
self.effect_combo.set(led_effect_values[0])
self.effect_combo.grid(row=1, column=1, padx=5, pady=5)
tk.Label(led_frame, text="R:", fg='white', bg='#121212').grid(row=2, column=0, padx=5, pady=5, sticky="e")
self.r_entry = tk.Entry(led_frame, width=5, fg='white', bg='#333333', insertbackground='white')
self.r_entry.insert(0, "255")
self.r_entry.grid(row=2, column=1, padx=5, pady=5)
tk.Label(led_frame, text="G:", fg='white', bg='#121212').grid(row=3, column=0, padx=5, pady=5, sticky="e")
self.g_entry = tk.Entry(led_frame, width=5, fg='white', bg='#333333', insertbackground='white')
self.g_entry.insert(0, "255")
self.g_entry.grid(row=3, column=1, padx=5, pady=5)
tk.Label(led_frame, text="B:", fg='white', bg='#121212').grid(row=4, column=0, padx=5, pady=5, sticky="e")
self.b_entry = tk.Entry(led_frame, width=5, fg='white', bg='#333333', insertbackground='white')
self.b_entry.insert(0, "255")
self.b_entry.grid(row=4, column=1, padx=5, pady=5)
ttk.Button(led_frame, text="Set LED", command=self.led_control_ui).grid(row=5, column=0, columnspan=2, pady=5, sticky="we")
ttk.Button(master, text="Get Battery", command=self.get_battery).grid(row=1, column=2, padx=0, pady=0, sticky="n")
chassis_frame = ttk.LabelFrame(master, text="Chassis Control (WASDQE)")
chassis_frame.grid(row=1, column=0, columnspan=3, 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("<KeyPress-w>", lambda event: self.chassis_move_arrow(x=1))
master.bind("<KeyPress-s>", lambda event: self.chassis_move_arrow(x=-1))
master.bind("<KeyPress-a>", lambda event: self.chassis_move_arrow(y=-1))
master.bind("<KeyPress-d>", lambda event: self.chassis_move_arrow(y=1))
master.bind("<KeyPress-q>", lambda event: self.chassis_move_arrow(z=50))
master.bind("<KeyPress-e>", lambda event: self.chassis_move_arrow(z=-50))
master.bind("<KeyRelease-w>", lambda event: self.chassis_move_arrow(x=0) if event.keysym == 'w' else None)
master.bind("<KeyRelease-s>", lambda event: self.chassis_move_arrow(x=0) if event.keysym == 's' else None)
master.bind("<KeyRelease-a>", lambda event: self.chassis_move_arrow(y=0) if event.keysym == 'a' else None)
master.bind("<KeyRelease-d>", lambda event: self.chassis_move_arrow(y=0) if event.keysym == 'd' else None)
master.bind("<KeyRelease-q>", lambda event: self.chassis_move_arrow(z=0) if event.keysym == 'q' else None)
master.bind("<KeyRelease-e>", lambda event: self.chassis_move_arrow(z=0) if event.keysym == 'e' else None)
master.bind("<space>", lambda event: self.stop_chassis())
master.bind("<Button-1>", self.deselect_textbox)
master.columnconfigure(0, weight=1)
master.columnconfigure(1, weight=1)
master.columnconfigure(2, 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 get_battery(self):
self._command_executor(self.cmd.robot_battery)
def led_control_ui(self):
comp_str = self.comp_combo.get()
effect_str = self.effect_combo.get()
r_str = self.r_entry.get()
g_str = self.g_entry.get()
b_str = self.b_entry.get()
try:
r = int(r_str)
g = int(g_str)
b = int(b_str)
if not all(0 <= color <= 255 for color in [r, g, b]):
return self._display_output("Error: R, G, B values must be between 0 and 255")
except ValueError:
return self._display_output("Error: Invalid R, G, B values")
self._command_executor(self.cmd.led_control, comp=comp_str, effect=effect_str, r=r, g=g, b=b)
def main():
ip_address = "192.168.2.1"
try:
cmd = rm.Commander(ip=ip_address, timeout=5)
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}")
finally:
try:
if cmd:
cmd.led_control(comp="all", effect="solid", r=0, g=0, b=255)
except Exception:
pass
if __name__ == "__main__":
main()