""" Simulator. """ import tkinter from tkinter import ttk import rosebot.standard_rosebot as rb class Simulator(object): def __init__(self, robots=None, root=None, parent=None, width=600, height=400, background='grey', canvas_options=None, gui=None, milliseconds_per_cycle=50, robot_milliseconds_per_cycle=50): """ Optional arguments are: -- robot: Either a single RoseBot, or a sequence of RoseBots. If None, the sequence of RoseBots is set to an empty list. -- gui: A SimulatorGUI in which the simulator's results are shown. If None, a SimulatorGUI will be constructed. If not None, any arguments that refer to "the constructed GUI" will be ignored. -- width, height, background, canvas_options: The width, height, background color, and additional options used for the constructed Canvas. The canvas_options argument is not yet implemented. -- parent: A Widget that the constructed Canvas uses as its parent. If None, then a Toplevel object is constructed and used as the parent. -- root: The sole Tk object for the GUI. If None, then a Tk object is constructed. -- milliseconds_per_cycle: The simulation takes this many milliseconds (or more, if more is needed) for each of its cycles (iterations). -- robot_milliseconds_per_cycle: The simulation runs each of its RoseBots for this many simulated seconds at each cycle (iteration). """ if isinstance(robots, rb.RoseBot): self.robots = [robots] else: self.robots = robots or [] self.root = root or tkinter.Tk() self.parent = parent or tkinter.Toplevel() self.gui = gui or SimGUI(self.robots, parent, width, height, background) self.milliseconds_per_cycle = milliseconds_per_cycle self.robot_milliseconds_per_cycle = robot_milliseconds_per_cycle # The user must call run to start the simulation. # By default, the GUI starts out visible. self.is_running = False self._is_visible = True def mainloop(self): # CONSIDER - maybe this should NOT be available to the user? self.root.mainloop() # TODO - Make show and hide be properties via is_visable def show(self): if not self._is_visible: self._is_visible = True self.gui.show() def hide(self): if self._is_visible: self._is_visible = False self.gui.hide() def run(self, show_gui=True): self.show() if show_gui else self.hide() if not self.is_running: self.is_running = True self.update() def pause(self, show_gui=True): self.is_running = False self.show() if show_gui else self.hide() def update(self): if self.is_running: # Consider: Should each robot have its own AFTER? for robot in self.robots: robot_position = robot.update_position( self.gui.get_robots_environment()) self.gui.move(robot, robot_position) self.root.after(self.milliseconds_per_cycle, self.update) class RobotPath(object): def __init__(self, center, start_angle, end_angle, direction): self.center = center self.start_angle = start_angle self.end_angle = end_angle self.direction = direction class RobotPosition(object): def __init__(self, center, heading): self.center = center # Point self.heading = heading # float, in radians class Point(object): def __init__(self, x, y): self.x = x self.y = y class SimGUI(object): def __init__(self, robots, parent, width, height, background): self.robots = robots self.canvas = SimCanvas(parent, robots, width=width, height=height, background=background) self.controls_frame = SimControlsFrame() self.show() def update(self, robot, robot_path): self.canvas.update(robot, robot_path) def show(self): self.canvas.grid(row=1, column=1) self.controls_frame.grid(row=1, column=2) def hide(self): self.canvas.grid_forget() self.controls_frame.grid_forget() def get_robots_environment(self): return self.canvas.robots_environment class RobotEnvironment(object): def __init__(self, width, height): pass """ horizontal_scrollbar = ttk.Scrollbar(parent, orient=tkinter.HORIZONTAL) vertical_scrollbar = ttk.Scrollbar(parent, orient=tkinter.VERTICAL) super().__init__(parent, scrollregion=(0, 0, width, height), yscrollcommand=vertical_scrollbar.set, xscrollcommand=horizontal_scrollbar.set) horizontal_scrollbar['command'] = self.xview vertical_scrollbar['command'] = self.yview ttk.Sizegrip(parent).grid(column=1, row=1, sticky=(tkinter.S, tkinter.E)) self.grid(column=0, row=0, sticky=(tkinter.N, tkinter.W, tkinter.E, tkinter.S)) horizontal_scrollbar.grid(column=0, row=1, sticky=(tkinter.W, tkinter.E)) vertical_scrollbar.grid(column=1, row=0, sticky=(tkinter.N, tkinter.S)) parent.grid_columnconfigure(0, weight=1) parent.grid_rowconfigure(0, weight=1) self.lastx, self.lasty = 0, 0 self.bind("", self.xy) self.bind("", self.addLine) self.bind("", self.doneStroke) id = self.create_rectangle( (10, 10, 30, 30), fill="red", tags=('palette', 'palettered')) self.tag_bind(id, "", lambda x: self.setColor("red")) id = self.create_rectangle( (10, 35, 30, 55), fill="blue", tags=('palette', 'paletteblue')) self.tag_bind(id, "", lambda x: self.setColor("blue")) id = self.create_rectangle((10, 60, 30, 80), fill="black", tags=( 'palette', 'paletteblack', 'paletteSelected')) self.tag_bind(id, "", lambda x: self.setColor("black")) self.setColor('black') self.itemconfigure('palette', width=5) """ # def xy(self, event): # self.lastx, self.lasty = (self.canvasx(event.x), # self.canvasy(event.y)) # # def setColor(self, newcolor): # self.color = newcolor # self.dtag('all', 'paletteSelected') # self.itemconfigure('palette', outline='white') # self.addtag('paletteSelected', 'withtag', # 'palette%s' % self.color) # self.itemconfigure('paletteSelected', outline='#999999') # # def addLine(self, event): # x, y = self.canvasx(event.x), self.canvasy(event.y) # self.create_line((self.lastx, self.lasty, x, y), # fill=self.color, # width=5, tags='currentline') # self.lastx, self.lasty = x, y # # def doneStroke(self, _): # self.itemconfigure('currentline', width=1) class SimCanvas(tkinter.Canvas): colors = {'blue', 'green', 'red'} # TODO add more def __init__(self, parent, robots, width, height, background): super().__init__(parent, width=width, height=height, background=background) self.robots_environment = RobotEnvironment(width, height) self.sim_robots = {} self.make_robots(robots) self.draw_axes_and_labels() def draw_axes_and_labels(self): width, height = self.get_width_and_height() self.create_line((0, height // 2, width, height // 2), fill='black', width=5, tags='x_axis') self.create_line((width // 2, 0, width // 2, height), fill='black', width=5, tags='y_axis') def get_width_and_height(self): return int(self.cget('width')), int(self.cget('height')) def make_robots(self, robots): for k in range(len(robots)): robot = robots[k] color = self.colors[k % len(self.colors)] self.canvas_robots[robot] = self.make_robot(robot) self.robot_representations = self.make_robots(robots) def make_robot(self, robot): # Default is a filled polygon that forms a rectangle. pass def update(self, robot, robot_position): pass def make_robot_image(self, robot, position=None, color='blue', size=None): return self.canvas.create_polygon() class SimControlsFrame(ttk.Frame): def __init__(self): super().__init__() button = ttk.Button(self, text='Draw walls') button.grid() button = ttk.Button(self, text='Draw on floor') button.grid() def main(): test() return robot = rb.RoseBot() root = tkinter.Tk() frame0 = ttk.Frame(root) frame0.grid() sim = Simulator(robot, root, frame0) sim.run() root.mainloop() import time def my_sleep(widget, count, start, rate, seconds): if count == 0: print((time.time() - start) / seconds) return # if count % 1000 == 0: # print(time.time() - t) widget.after(rate, (lambda: my_sleep(widget, count - rate, start, rate, seconds))) def my_sleep2(widget, count, start, rate, seconds): for k in range(count // rate): time.sleep(rate / 1000) print((time.time() - start) / seconds) # if count % 1000 == 0: # print(time.time() - t) def test(): root = tkinter.Tk() start = time.time() rate = 10 seconds = 5 my_sleep2(root, seconds * 1000, start, rate, seconds) root.mainloop() end = time.time() main()