def main(): root = Tk() # w, h = root.winfo_screenwidth(), root.winfo_screenheight() w, h = 960, 540 # get value from arduino # ser = serial.Serial('/dev/tty.usbserial', 9600) pos = 0 root.overrideredirect(1) root.focus_set() root.bind("<Escape>", lambda e: e.widget.quit()) root.geometry("%dx%d+300+300" % (w, h)) canvas = Canvas(root, width=w, height=h, background="black") rect0 = canvas.create_rectangle(w/2-75, h/2-20, w/2+75, h/2+20, fill="#05f", outline="#05f") rect1 = canvas.create_rectangle(w/2-20, h/2-75, w/2+20, h/2+75, fill="#05f", outline="#05f") canvas.pack() while (True): # gets angle and moves accordingly # pos = ser.readline() canvas.move(rect0, 1, 0) canvas.move(rect1, 1, 0) root.update() root.after(30) app = App(root) time.sleep(0.5) root.mainloop()
class Grid(object): cell_size = 5 def __init__(self): master = Tk() self.canvas = Canvas(master, width=800, height=600, background='black') self.canvas.pack() self.columns = int(self.canvas['width']) / self.cell_size self.rows = int(self.canvas['height']) / self.cell_size def put(self, x, y, tags=None): self.canvas.create_rectangle(x * self.cell_size, y*self.cell_size, x * self.cell_size + self.cell_size, y*self.cell_size + self.cell_size, fill="white", outline="white", tags=tags) def move(self, tags, dx, dy): self.canvas.move(tags, dx*self.cell_size, dy*self.cell_size)
class Example(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.buttons = {} self.nodes = {} self.edges = {} self.active_node = None self.active_edge = None self.start = None self.x = None self.y = None self.cycles = None self.show_cycles_only_mode = False self.steps = None self.step_index = None self.parent.title("Demonstrační aplikace - nalezení elementárních cyklů v orientovaném grafu") self.style = Style() self.style.theme_use("default") self.pack(fill=BOTH, expand=1) self.columnconfigure(1, weight=1) self.columnconfigure(3, pad=7) self.rowconfigure(5, weight=1) self.rowconfigure(6, pad=7) self.label = Label(self, text="graf1.graphml") self.label.grid(sticky=W, pady=4, padx=5) self.canvas = Canvas(self) self.canvas.bind('<Double-Button-1>', self.event_add_node) self.canvas.bind('<Button-1>', self.event_add_edge_start) self.canvas.bind('<B1-Motion>', self.event_add_edge_move) self.canvas.bind('<ButtonRelease-1>', self.event_add_edge_end) self.canvas.bind('<Button-3>', self.event_move_node_start) self.canvas.bind('<B3-Motion>', self.event_move_node) self.canvas.pack() self.canvas.grid(row=1, column=0, columnspan=2, rowspan=6, padx=5, sticky=E + W + S + N) self.buttons['start'] = b = Button(self, text="Start", width=15) b.bind('<Button-1>', self.event_start) b.grid(row=1, column=3) self.buttons['next'] = b = Button(self, text=">>", width=15, state=DISABLED) b.bind('<Button-1>', self.event_next_step) b.grid(row=2, column=3, pady=4) self.buttons['prev'] = b = Button(self, text="<<", width=15, state=DISABLED) b.bind('<Button-1>', self.event_prev_step) b.grid(row=3, column=3, pady=4) b = Checkbutton(self, text="Pouze cykly", command=self.event_change_mode) b.grid(row=4, column=3, pady=4) self.buttons['reset'] = b = Button(self, text="Reset", width=15) b.bind('<Button-1>', self.event_reset) b.grid(row=6, column=3) menubar = Menu(self.parent) self.parent.config(menu=menubar) fileMenu = Menu(menubar) fileMenu.add_command(label="Načíst", command=self.onLoad) fileMenu.add_command(label="Uložit", command=self.onSave) fileMenu.add_separator() fileMenu.add_command(label="Konec", command=self.onExit) menubar.add_cascade(label="Soubor", menu=fileMenu) fileMenu = Menu(menubar) fileMenu.add_command(label="O aplikaci", command=self.onAbout) menubar.add_cascade(label="Nápověda", menu=fileMenu) def onExit(self): self.quit() def onLoad(self): fileTypes = [('Soubory typu GraphML', '*.graphml')] dialog = tkFileDialog.Open(self, filetypes=fileTypes) filename = dialog.show() if filename != '': self.readFile(filename) def onSave(self): fileTypes = [('GraphML files', '*.graphml')] dialog = tkFileDialog.SaveAs(self, filetypes=fileTypes) filename = dialog.show() if filename != '': if not filename.endswith(".graphml"): filename += ".graphml" self.writeFile(filename) def onAbout(self): box.showinfo("O aplikaci", "Demonstrace algoritmu nalezení elementárních cyklů v orientovaném grafu podle D. B. Johnsona. \n\n" "Autoři:\n" "Paulík Miroslav\n" "Pavlů Igor\n" "FIT VUT v Brně 2013") def readFile(self, filename): self.reset() try: parser = GraphMLParser() g = parser.parse(filename) except Exception: box.showerror("Chyba při zpracování vstupního souboru", "Chybný formát souboru.") return nodeMap = {} try: for gnode in g.nodes(): nodeMap[gnode.id] = self.__add_node(int(gnode['x']), int(gnode['y'])) except KeyError: box.showerror("Chyba při zpracování vstupního souboru", "Uzlum chybi udaje o pozici (atributy x a y).") self.reset() return try: for gedge in g.edges(): start = nodeMap[gedge.node1.id] end = nodeMap[gedge.node2.id] isCurve = gedge.node1.id == gedge.node2.id self.__add_edge(start, end, isCurve) self.label.configure(text=os.path.basename(filename)) except KeyError: box.showerror("Chyba při zpracování vstupního souboru", "Soubor obsahuje hrany spojujici neexistujici hrany") self.reset() return self.repaint() def writeFile(self, filename): g = Graph() for i in self.nodes: node = self.nodes[i] node.name = str(i) gnode = g.add_node(i) gnode['label'] = i gnode['x'] = node.x gnode['y'] = node.y for i in self.edges: edge = self.edges[i] edge.name = i parser = GraphMLParser() parser.write(g, filename) def repaint(self): for e in self.edges: edge = self.edges[e] self.canvas.itemconfigure(e, fill=edge.color) for v in self.nodes: node = self.nodes[v] self.canvas.itemconfigure(v, fill=node.color) def reset_colors(self): for n in self.nodes: self.nodes[n].color = "white" for e in self.edges: self.edges[e].color = "grey" def reset(self): self.nodes = {} self.edges = {} self.canvas.delete("all") self.buttons['prev'].config(state=DISABLED) self.buttons['next'].config(state=DISABLED) def run(self): x = ElementaryCircuitsDetector(self.nodes, self.edges) x.detect_cycles() self.cycles = x.cycles self.step_index = 0 self.steps = x.get_all_steps() self.algorithm_step_move(0) if len(self.steps) > 0: self.buttons['prev'].config(state=1) self.buttons['next'].config(state=1) def event_reset(self, event): self.reset() def event_prev_step(self, event): if str(self.buttons['prev'].cget("state")) != str(DISABLED): self.algorithm_step_move(-1) def event_next_step(self, event): if str(self.buttons['next'].cget("state")) != str(DISABLED): self.algorithm_step_move(1) def event_start(self, event): self.run() def event_change_mode(self): self.show_cycles_only_mode = not self.show_cycles_only_mode self.run() def event_add_edge_start(self, event): self.x = event.x self.y = event.y def event_add_edge_move(self, event): if self.active_edge is None: self.active_edge = self.canvas.create_line(self.x, self.y, event.x, event.y, arrow="last", width=2) else: x1, y1, x2, y2 = self.canvas.coords(self.active_edge) self.canvas.coords(self.active_edge, x1, y1, event.x, event.y) def event_add_edge_end(self, event): if self.active_edge is None: return x1, y1, x2, y2 = self.canvas.coords(self.active_edge) start = self.__get_node_from_position(x1, y1) end = self.__get_node_from_position(x2, y2) if start is None or end is None: self.canvas.delete(self.active_edge) elif start == end: self.canvas.delete(self.active_edge) edge = Edge(start, start, True) points = edge.get_coords() self.active_edge = self.canvas.create_line(points, width=2, smooth=True, arrow="last") self.canvas.tag_lower(self.active_edge, min(self.nodes.keys())) self.edges[self.active_edge] = edge else: x, y = self.__calculate_edge_end_from_nodes(start, end) self.canvas.coords(self.active_edge, start.x, start.y, x, y) self.canvas.tag_lower(self.active_edge, min(self.nodes.keys())) edge = Edge(start, end) self.edges[self.active_edge] = edge self.active_edge = None self.x = None self.y = None def event_move_node_start(self, event): id = self.__get_id_from_position(event.x, event.y) if id is None: return self.__activate_node(id) self.x = event.x self.y = event.y def event_move_node(self, event): id = self.active_node if id is None: return deltax = event.x - self.x deltay = event.y - self.y self.canvas.move(id, deltax, deltay) self.x = event.x self.y = event.y coord = self.canvas.coords(id) self.nodes[self.active_node].x = (coord[2] - coord[0]) / 2 + coord[0] self.nodes[self.active_node].y = (coord[3] - coord[1]) / 2 + coord[1] self.__repair_edge_starting_in_node(self.nodes[self.active_node]) self.__repair_edge_ending_in_node(self.nodes[self.active_node]) def event_add_node(self, event): id = self.__get_id_from_position(event.x, event.y, reverse=True) if id is None or id not in self.nodes: self.__add_node(event.x, event.y) def __repair_edge_ending_in_node(self, node): list_of_edge_ids = [] for edge_id in self.edges: edge = self.edges[edge_id] if edge.end == node: list_of_edge_ids.append(edge_id) for edge_id in list_of_edge_ids: edge = self.edges[edge_id] x, y = self.__calculate_edge_end_from_nodes(edge.start, edge.end) if edge.is_curve: coords = edge.get_coords() self.canvas.coords(edge_id, coords[0][0], coords[0][1], coords[1][0], coords[1][1], coords[2][0], coords[2][1], coords[3][0], coords[3][1]) else: self.canvas.coords(edge_id, edge.start.x, edge.start.y, x, y) def __repair_edge_starting_in_node(self, node): list_of_edge_ids = [] for edge_id in self.edges: edge = self.edges[edge_id] if edge.start == node: list_of_edge_ids.append(edge_id) for edge_id in list_of_edge_ids: edge = self.edges[edge_id] x, y = self.__calculate_edge_end_from_nodes(edge.start, edge.end) if edge.is_curve: coords = edge.get_coords() self.canvas.coords(edge_id, coords[0][0], coords[0][1], coords[1][0], coords[1][1], coords[2][0], coords[2][1], coords[3][0], coords[3][1]) else: self.canvas.coords(edge_id, edge.start.x, edge.start.y, x, y) def __calculate_edge_end_from_nodes(self, start_node, end_node): diffx = end_node.x - start_node.x diffy = end_node.y - start_node.y distance = math.sqrt(diffx ** 2 + diffy ** 2) if distance > 0: ratio = NODE_SIZE / 2 / distance x = end_node.x - diffx * ratio y = end_node.y - diffy * ratio return x, y return end_node.x, end_node.y def __activate_node(self, id): self.__deactivate_node() if id in self.nodes: self.active_node = id def __deactivate_node(self): self.active_node = None def __get_id_from_position(self, x, y, reverse=False): overlaping = self.canvas.find_overlapping(x, y, x, y) if len(overlaping) > 0: if reverse: return overlaping[-1] else: return overlaping[0] else: return None def __get_node_from_position(self, x, y): id = self.__get_id_from_position(x, y) if id is not None and id in self.nodes: return self.nodes[id] else: return None def __add_node(self, x, y): node = Node(x, y) id = self.canvas.create_oval(node.get_coord(), fill="blue") self.nodes[id] = node return node def __add_edge(self, start, end, is_curve=False): edge = Edge(start, end, is_curve) if is_curve: id = self.canvas.create_line(edge.get_coords(), width=2, smooth=True, arrow="last") else: id = self.canvas.create_line(start.x, start.y, end.x, end.y, arrow="last", width=2) self.edges[id] = edge self.canvas.tag_lower(id, min(self.nodes.keys())) self.__repair_edge_starting_in_node(start) return edge def algorithm_step_move(self, move): if self.show_cycles_only_mode: # cycles only if (self.step_index + move) < len(self.cycles) and self.step_index + move >= 0: self.step_index += move self.reset_colors() colors = ['green', 'blue', 'red', 'yellow', 'purple', 'brown'] color_index = self.step_index % len(colors) for edge in self.cycles[self.step_index]: edge.color = edge.start.color = edge.end.color = colors[color_index] self.repaint() else: if (self.step_index + move) < len(self.steps) and self.step_index + move >= 0: self.step_index += move self.reset_colors() for i in range(self.step_index + 1): colors = self.steps[i] for id in colors: if id in self.nodes.keys(): self.nodes[id].color = colors[id] elif id in self.edges.keys(): self.edges[id].color = colors[id] self.repaint()
global scaling_factor if event.char == '>': scaling_factor = scaling_factor + 0.1 resize() elif event.char == '<': scaling_factor = scaling_factor - 0.1 resize() elif event.char == "<Right>": x = x + 5 print x (x, y) = canvas.coords(zoeid) canvas.bind("<Right>", lambda event: canvas.move(zoeid, 5, 0)) canvas.bind("<Left>", lambda event: canvas.move(zoeid, -5, 0)) canvas.bind("<Up>", lambda event: canvas.move(zoeid, 0, -5)) canvas.bind("<Down>", lambda event: canvas.move(zoeid, 0, 5)) canvas.focus_set() canvas.bind("<Key>", key) scaling_factor = 1.0 def resize(): global canvas, zoeid, photo1, Zoe, scaling_factor, coorx, coory, x, y
class GUI(object): def __init__(self, logger): self.logger = logger self.root = Tk() self.canvas = Canvas(self.root, width=800, height=800) self.canvas.pack() self.root.after(33, self.update) self.pos = (0,0) self.circle = self.canvas.create_oval(-10,-10,10,10,fill='black') self.gestures = [ lambda t,x,y,s: ((sin(t*2*pi*0.5)*300 + 400, abs(cos(t*2*pi*0.2)*300) + 200), s), lambda t,x,y,s: ((sin(t*2*pi*1)*300 + 400, cos(t*2*pi*1)*300 + 400), s), lambda t,x,y,s: ((sin(t*2*pi*0.75)*300 + 400, -abs(cos(t*2*pi*0.75)*300) + 400), s), lambda t,x,y,s: ((x+(s[0] - x)*0.1, y+(s[1] - y)*0.1), (uniform(100,700)*(t%1<0.05) + (t%1>0.05)*s[0], uniform(100,700)*(t%1<0.05) + (t%1>0.05)*s[1])), lambda t,x,y,s: ((400, y+s[1] if y < 700 else 700-s[1]), (0, (10 + s[1])*(-0.5 if y>700 else 1))), ] self.state = [0,0] self.current_gesture = -1 self.last_gesture_time = time.time() self.next_gesture(self.last_gesture_time) self.waiting = True self.root.bind('<space>', self.on_space) self.logger.set_tag('waiting') def on_space(self, *args): self.waiting = False self.last_gesture_time = time.time() def run(self): self.root.mainloop() def next_gesture(self, t): self.current_gesture += 1 if self.current_gesture >= len(self.gestures): self.root.quit() self.logger.set_done() return True else: self.last_gesture_time = t gestureID = 'gesture%d'%self.current_gesture self.logger.set_tag(gestureID) print gestureID def update(self): if not self.waiting: t = time.time() if (t - self.last_gesture_time) > SECONDS_PER_GESTURE: if self.next_gesture(t): return pos, self.state = self.gestures[self.current_gesture](t,self.pos[0],self.pos[1],self.state) delta = pos[0] - self.pos[0], pos[1] - self.pos[1] self.canvas.move(self.circle, delta[0], delta[1]) self.pos = pos self.root.after(33, self.update)
class Breakout(Tk): def __init__(self): Tk.__init__(self) #self.canvas.delete('all') self.geometry('790x600') self.resizable( 0, 0 ) #set both parameters to false and to check whether window is resizable in x and y directions self.func() # game screen def func(self): self.canvas = Canvas(self, bg='skyblue', width=990, height=600, highlightcolor='green') self.canvas.pack( expand=1, fill=BOTH ) #when it is true and widget expands to fill any space # ball self._initiate_new_ball() #self.level=choice([1]) # paddle self.canvas.create_rectangle(375, 975, 525, 585, fill='red', tags='paddle') self.bind('<Key>', self._move_paddle) # bricks self.bricks = {} brick_coords = [15, 12, 60, 45] for i in range(56): self.canvas.create_rectangle(brick_coords, outline='green', fill=('yellow'), tags='brick' + str(i)) self.bricks['brick' + str(i)] = None brick_coords[0] += 55 brick_coords[2] += 55 if brick_coords[2] > 790: brick_coords[0] = 15 brick_coords[2] = 60 brick_coords[1] += 55 brick_coords[3] += 55 def _initiate_new_ball(self): if self.canvas.find_withtag('ball'): self.canvas.delete('ball') self.x = 300 self.y = 350 self.angle = 240 self.speed = 10 self.level = 0 self.score = 0 self.canvas.create_oval(self.x, self.y, self.x + 10, self.y + 10, fill='orange', outline='red', tags='ball') self.after(2000, self._move_ball) def _move_paddle(self, event): if event.keysym == 'Left': if self.canvas.coords('paddle')[0] > 0: self.canvas.move('paddle', -20, 0) elif event.keysym == 'Right': if self.canvas.coords('paddle')[2] < 990: self.canvas.move('paddle', +20, 0) #def _move_ball1(self): # call1() #self._initiate_new_ball1() def _move_ball(self): # variables to determine where ball is in relation to other objects ball = self.canvas.find_withtag('ball')[0] bounds = self.canvas.find_overlapping(0, 0, 790, 600) paddle = self.canvas.find_overlapping( *self.canvas.coords('paddle')) for brick in self.bricks.iterkeys(): self.bricks[brick] = self.canvas.find_overlapping( *self.canvas.bbox(brick)) # calculate change in x,y values of ball angle = self.angle - 120 # correct for quadrant IV increment_x = cos(radians(angle)) * self.speed increment_y = sin(radians(angle)) * self.speed #self.level += choice([1]) #score=self.score #score=0 # finite state machine to set ball state if ball in bounds: self.ball_state = 'moving' for brick, hit in self.bricks.iteritems(): if ball in hit: self.ball_state = 'hit_brick' delete_brick = brick elif ball in paddle: self.ball_state = 'hit_wall' elif (self.score) // 3 == 56: self.canvas.create_text( WIDTH / 4, HEIGHT / 2.3, text="CONGRATS!! GAME COMPLETED!", font=("Helvetica", 20), fill="orange") self.canvas.create_text(WIDTH / 4, HEIGHT / 2, text=(self.score // 3), font=("Helvetica", 20), fill="red") self.canvas.create_text(WIDTH / 4, HEIGHT / 1.7, text="YOUR SCORE ", font=("Helvetica", 20), fill="darkorange") #image() quit_to_exit() call() elif ball not in bounds: if self.canvas.coords('ball')[1] < 600: self.ball_state = 'hit_wall' else: self.ball_state = 'out_of_bounds' #self.level += choice([1]) self.canvas.create_text( WIDTH / 2.5, HEIGHT / 2.3, text="GAME OVER !!PLEASE CLOSE THE WINDOW TO RESTART!", tag="cr2", font=("Helvetica", 20), fill="orange") self.canvas.create_text(WIDTH / 4, HEIGHT / 2, text="YOUR SCORE IS", tag="cr1", font=("Helvetica", 20), fill="orange") self.canvas.create_text(WIDTH / 4, HEIGHT / 1.7, text=(self.score // 3) + 1, tag="cr", font=("Helvetica", 20), fill="red") quit_to_exit() call() self._initiate_new_ball() #self.bind('<key>',self.game_over) #game = Breakout() #game.mainloop() # handler for ball state if self.ball_state is 'moving': self.canvas.move('ball', increment_x, increment_y) self.after(35, self._move_ball) # self.level +=choice([1]) elif self.ball_state is 'hit_brick' or self.ball_state is 'hit_wall': if self.ball_state == 'hit_brick': self.canvas.delete(delete_brick) del self.bricks[delete_brick] self.canvas.move('ball', -increment_x, -increment_y) # self.level += choice([1]) self.score += choice([1]) self.angle += choice([135]) self.canvas.delete("cr") self.canvas.delete("cr1") self.canvas.delete("cr2") #canvas.create_text(WIDTH/4,HEIGHT/5,text="GAME OVER!") self._move_ball()
class GUI(object): def __init__(self, logger): self.logger = logger self.root = Tk() self.canvas = Canvas(self.root, width=800, height=800) self.canvas.pack() self.root.after(33, self.update) self.pos = (0, 0) self.circle = self.canvas.create_oval(-10, -10, 10, 10, fill='black') self.gestures = [ lambda t, x, y, s: ((sin(t * 2 * pi * 0.5) * 300 + 400, abs(cos(t * 2 * pi * 0.2) * 300) + 200), s), lambda t, x, y, s: ((sin(t * 2 * pi * 1) * 300 + 400, cos(t * 2 * pi * 1) * 300 + 400), s), lambda t, x, y, s: ((sin(t * 2 * pi * 0.75) * 300 + 400, -abs( cos(t * 2 * pi * 0.75) * 300) + 400), s), lambda t, x, y, s: ((x + (s[0] - x) * 0.1, y + (s[1] - y) * 0.1), (uniform(100, 700) * (t % 1 < 0.05) + (t % 1 > 0.05) * s[0], uniform(100, 700) * (t % 1 < 0.05) + (t % 1 > 0.05) * s[1])), lambda t, x, y, s: ((400, y + s[1] if y < 700 else 700 - s[1]), (0, (10 + s[1]) * (-0.5 if y > 700 else 1))), ] self.state = [0, 0] self.current_gesture = -1 self.last_gesture_time = time.time() self.next_gesture(self.last_gesture_time) self.waiting = True self.root.bind('<space>', self.on_space) self.logger.set_tag('waiting') def on_space(self, *args): self.waiting = False self.last_gesture_time = time.time() def run(self): self.root.mainloop() def next_gesture(self, t): self.current_gesture += 1 if self.current_gesture >= len(self.gestures): self.root.quit() self.logger.set_done() return True else: self.last_gesture_time = t gestureID = 'gesture%d' % self.current_gesture self.logger.set_tag(gestureID) print gestureID def update(self): if not self.waiting: t = time.time() if (t - self.last_gesture_time) > SECONDS_PER_GESTURE: if self.next_gesture(t): return pos, self.state = self.gestures[self.current_gesture](t, self.pos[0], self.pos[1], self.state) delta = pos[0] - self.pos[0], pos[1] - self.pos[1] self.canvas.move(self.circle, delta[0], delta[1]) self.pos = pos self.root.after(33, self.update)
class Breakout(Tk): def __init__(self): Tk.__init__(self) self.geometry('400x400') self.resizable(0,0) # game screen self.canvas = Canvas(self, bg='black', width=400, height=400) self.canvas.pack(expand=1, fill=BOTH) # ball self._initiate_new_ball() # paddle self.canvas.create_rectangle(175,375,225,385, fill='black', outline='white', tags='paddle') self.bind('<Key>', self._move_paddle) # bricks self.bricks = {} brick_coords = [5,5,35,15] for i in range(39): self.canvas.create_rectangle(*brick_coords, outline='white', fill=('#{}'.format(randint(100000,999999))), tags='brick' + str(i)) self.bricks['brick' + str(i)] = None brick_coords[0] += 30; brick_coords[2] += 30 if brick_coords[2] > 395: brick_coords[0] = 5; brick_coords[2] = 35 brick_coords[1] += 10; brick_coords[3] += 10 def _initiate_new_ball(self): if self.canvas.find_withtag('ball'): self.canvas.delete('ball') self.x = 60; self.y = 100 self.angle = 140; self.speed = 5 self.canvas.create_oval(self.x,self.y,self.x+10,self.y+10, fill='lawn green', outline='white', tags='ball') self.after(1000, self._move_ball) def _move_paddle(self, event): if event.keysym == 'Left': if self.canvas.coords('paddle')[0] > 0: self.canvas.move('paddle', -10, 0) elif event.keysym == 'Right': if self.canvas.coords('paddle')[2] < 400: self.canvas.move('paddle', +10, 0) def _move_ball(self): # variables to determine where ball is in relation to other objects ball = self.canvas.find_withtag('ball')[0] bounds = self.canvas.find_overlapping(0,0,400,400) paddle = self.canvas.find_overlapping(*self.canvas.coords('paddle')) for brick in self.bricks.iterkeys(): self.bricks[brick] = self.canvas.find_overlapping(*self.canvas.bbox(brick)) # calculate change in x,y values of ball angle = self.angle - 90 # correct for quadrant IV increment_x = cos(radians(angle)) * self.speed increment_y = sin(radians(angle)) * self.speed # finite state machine to set ball state if ball in bounds: self.ball_state = 'moving' for brick, hit in self.bricks.iteritems(): if ball in hit: self.ball_state = 'hit_brick' delete_brick = brick elif ball in paddle: self.ball_state = 'hit_wall' elif ball not in bounds: if self.canvas.coords('ball')[1] < 400: self.ball_state = 'hit_wall' else: self.ball_state = 'out_of_bounds' self._initiate_new_ball() # handler for ball state if self.ball_state is 'moving': self.canvas.move('ball', increment_x, increment_y) self.after(15, self._move_ball) elif self.ball_state is 'hit_brick' or self.ball_state is 'hit_wall': if self.ball_state == 'hit_brick': self.canvas.delete(delete_brick) del self.bricks[delete_brick] self.canvas.move('ball', -increment_x, -increment_y) self.angle += choice([119, 120, 121]) self._move_ball()
class Screen(Observer): def __init__(self, parent, model): self.parent = parent self.model = model self.canvas = Canvas(parent) self.curve_id = -1 self.control_points_id = [] self.control_point_index = -1 def set_canvas(self, canvas): self.canvas = canvas def get_canvas(self): return self.canvas # View : update() the Bezier curve def update(self, subject): curve = subject.get_curve() self.canvas.delete(self.curve_id) self.curve_id = self.canvas.create_line(curve, width=3, fill='gray40') # View : control points visualisation def update_control_points(self, model): control_points = model.get_control_points() # self.canvas.delete('ctrl_pts') del self.control_points_id[:] i = 0 while i < len(control_points): x, y = control_points[i] self.control_points_id.append( self.canvas.create_oval(x, y, x + 10, y + 10, outline='black', fill='green')) i = i + 1 # Controler : control point interaction to update the Bezier curve def select_point(self, event, model): control_points = model.get_control_points() # selection of a control point i = 0 while i < len(control_points): x, y = control_points[i] if x - 10 < event.x < x + 10 and y - 10 < event.y < y + 10: self.control_point_index = i self.canvas.itemconfigure( self.control_points_id[self.control_point_index], fill='red') break i = i + 1 # insertion of a control point if self.control_point_index == -1: i = 0 while i < len(control_points) - 1: x1, y1 = control_points[i] x2, y2 = control_points[i + 1] print(event.x, event.y) if (x1 < event.x < x2 or x2 < event.x < x1) and (y1 < event.y < y2 or y2 < event.y < y1): control_points.insert(i, (event.x, event.y)) model.set_control_points(control_points) self.update_control_points(model) break i = i + 1 self.update_control_points(model) def move_point(self, event, model): if 0 <= self.control_point_index < len(self.control_points_id): coords = self.canvas.coords( self.control_points_id[self.control_point_index]) x1, y1 = coords[0], coords[1] x1, y1 = event.x - x1, event.y - y1 control_points = model.get_control_points() control_points[self.control_point_index] = event.x, event.y model.set_control_points(control_points) self.canvas.move(self.control_points_id[self.control_point_index], x1, y1) model.compute_curve() def release_point(self, event): self.canvas.itemconfigure( self.control_points_id[self.control_point_index], fill='green') self.control_point_index = -1 def packing(self): self.canvas.pack(fill='both', expand=True)
class Game(): WIDTH = 300 HEIGHT = 500 def start(self): self.level = 1 self.score = 0 self.speed = 500 self.counter = 0 self.create_new_game = True self.root = Tk() self.root.title("Tetris") self.status_var = StringVar() self.status_var.set("Level: 1, Score: 0") self.status = Label(self.root, textvariable=self.status_var, font=("Helvetica", 10, "bold")) self.status.pack() self.canvas = Canvas( self.root, width=Game.WIDTH, height=Game.HEIGHT) self.canvas.pack() self.root.bind("<Key>", self.handle_events) self.timer() self.root.mainloop() def timer(self): if self.create_new_game == True: self.current_shape = Shape(self.canvas) self.create_new_game = False if not self.current_shape.fall(): lines = self.remove_complete_lines() if lines: self.score += 10 * self.level**2 * lines**2 self.status_var.set("Level: %d, Score: %d" % (self.level, self.score)) self.current_shape = Shape(self.canvas) if self.is_game_over(): self.create_new_game = True self.game_over() self.counter += 1 if self.counter == 5: self.level += 1 self.speed -= 20 self.counter = 0 self.status_var.set("Level: %d, Score: %d" % (self.level, self.score)) self.root.after(self.speed, self.timer) def handle_events(self, event): if event.keysym == "Left": self.current_shape.move(-1, 0) if event.keysym == "Right": self.current_shape.move(1, 0) if event.keysym == "Down": self.current_shape.move(0, 1) if event.keysym == "Up": self.current_shape.rotate() def is_game_over(self): for box in self.current_shape.boxes: if not self.current_shape.can_move_box(box, 0, 1): return True return False def remove_complete_lines(self): shape_boxes_coords = [self.canvas.coords(box)[3] for box in self.current_shape.boxes] all_boxes = self.canvas.find_all() all_boxes_coords = {k : v for k, v in zip(all_boxes, [self.canvas.coords(box)[3] for box in all_boxes])} lines_to_check = set(shape_boxes_coords) boxes_to_check = dict((k, v) for k, v in all_boxes_coords.iteritems() if any(v == line for line in lines_to_check)) counter = Counter() for box in boxes_to_check.values(): counter[box] += 1 complete_lines = [k for k, v in counter.iteritems() if v == (Game.WIDTH/Shape.BOX_SIZE)] if not complete_lines: return False for k, v in boxes_to_check.iteritems(): if v in complete_lines: self.canvas.delete(k) del all_boxes_coords[k] for (box, coords) in all_boxes_coords.iteritems(): for line in complete_lines: if coords < line: self.canvas.move(box, 0, Shape.BOX_SIZE) return len(complete_lines) def game_over(self): self.canvas.delete(Tkinter.ALL) tkMessageBox.showinfo( "Game Over", "You scored %d points." % self.score)
class Game(): WIDTH = 300 HEIGHT = 500 def start(self): '''Starts the game. Creates a window, a canvas, and a first shape. Binds the event handler. Then starts a GUI timer of ms interval self.speed and starts the GUI main loop. ''' #TODO start() needs to be refactored so that the creation of the # window, label, and canvas are independent from setting them to # defaults and starting the game. # # There should also be a way for the user to restart and pause # the game if he or she wishes. # # It's a little weird that level is based only on time and that # as a result it increases faster and faster. Wouldn't it make # more sense for level to be a result of completed lines? self.level = 1 self.score = 0 self.speed = 500 self.counter = 0 self.create_new_game = True self.root = Tk() self.root.title("Tetris") self.status_var = StringVar() self.status_var.set("Level: 1, Score: 0") self.status = Label(self.root, textvariable=self.status_var, font=("Helvetica", 10, "bold")) self.status.pack() self.canvas = Canvas(self.root, width=Game.WIDTH, height=Game.HEIGHT) self.canvas.pack() self.root.bind("<Key>", self.handle_events) self.timer() self.root.mainloop() def timer(self): '''Every self.speed ms, attempt to cause the current_shape to fall(). If fall() returns False, create a new shape and check if it can fall. If it can't, then the game is over. ''' if self.create_new_game == True: self.current_shape = Shape(self.canvas) self.create_new_game = False if not self.current_shape.fall(): lines = self.remove_complete_lines() if lines: self.score += 10 * self.level**2 * lines**2 self.status_var.set("Level: %d, Score: %d" % (self.level, self.score)) self.current_shape = Shape(self.canvas) if self.is_game_over(): #TODO This is a problem. You rely on the timer method to # create a new game rather than creating it here. As a # result, there is an intermittent error where the user # event keypress Down eventually causes can_move_box # to throw an IndexError, since the current shape has # no boxes. Instead, you need to cleanly start a new # game. I think refactoring start() might help a lot # here. # # Furthermore, starting a new game currently doesn't reset # the levels. You should place all your starting constants # in the same place so it's clear what needs to be reset # when. self.create_new_game = True self.game_over() self.counter += 1 if self.counter == 5: self.level += 1 self.speed -= 20 self.counter = 0 self.status_var.set("Level: %d, Score: %d" % (self.level, self.score)) self.root.after(self.speed, self.timer) def handle_events(self, event): '''Handle all user events.''' if event.keysym == "Left": self.current_shape.move(-1, 0) if event.keysym == "Right": self.current_shape.move(1, 0) if event.keysym == "Down": self.current_shape.move(0, 1) if event.keysym == "Up": self.current_shape.rotate() def is_game_over(self): '''Check if a newly created shape is able to fall. If it can't fall, then the game is over. ''' for box in self.current_shape.boxes: if not self.current_shape.can_move_box(box, 0, 1): return True return False def remove_complete_lines(self): shape_boxes_coords = [ self.canvas.coords(box)[3] for box in self.current_shape.boxes ] all_boxes = self.canvas.find_all() all_boxes_coords = { k: v for k, v in zip(all_boxes, [self.canvas.coords(box)[3] for box in all_boxes]) } lines_to_check = set(shape_boxes_coords) boxes_to_check = dict((k, v) for k, v in all_boxes_coords.iteritems() if any(v == line for line in lines_to_check)) counter = Counter() for box in boxes_to_check.values(): counter[box] += 1 complete_lines = [ k for k, v in counter.iteritems() if v == (Game.WIDTH / Shape.BOX_SIZE) ] if not complete_lines: return False for k, v in boxes_to_check.iteritems(): if v in complete_lines: self.canvas.delete(k) del all_boxes_coords[k] #TODO Would be cooler if the line flashed or something for (box, coords) in all_boxes_coords.iteritems(): for line in complete_lines: if coords < line: self.canvas.move(box, 0, Shape.BOX_SIZE) return len(complete_lines) def game_over(self): self.canvas.delete(Tkinter.ALL) tkMessageBox.showinfo("Game Over", "You scored %d points." % self.score)
class StlFrame(Frame): def __init__(self, root, scale=1): Frame.__init__(self, root) self.app = root self.gridOffset = 10 self.arrangeMargin = 2 self.centerOnLoad = True self.offsetx = 0 self.offsety = 0 self.scale = scale self.zoom = 1 self.selection = None self.selectable = [] self.itemMap = {} self.canvas = Canvas(self, width=200*self.scale+13, height=200*self.scale+13, bd=2, relief=RIDGE, bg="white") self.canvas.config(takefocus="1") self.canvas.bind("<Key>", self.keyCanvas) self.canvas.bind("<Button-1>", self.clickCanvas) self.canvas.bind("<B1-Motion>", self.dragCanvas) self.canvas.bind("<MouseWheel>", self.wheelCanvas) self.canvas.bind("<Button-4>", self.wheelCanvas) self.canvas.bind("<Button-5>", self.wheelCanvas) self.root = root self.buildarea = [200, 200] self.canvas.grid(row=1,column=1,columnspan=5) self.drawGrid() def keyCanvas(self, event): self.app.setModified() if event.keysym == "Left": self.moveStl(-1, 0) elif event.keysym == "Right": self.moveStl(1, 0) elif event.keysym == "Up": if (event.state & SHIFT_MASK) != 0: self.rotateStl(1) else: self.moveStl(0, 1) elif event.keysym == "Down": if (event.state & SHIFT_MASK) != 0: self.rotateStl(-1) else: self.moveStl(0, -1) def clickCanvas(self, event): self.app.setModified() self.canvas.focus_set() self.startx = event.x/float(self.scale) self.starty = event.y/float(self.scale) itemIdList = self.canvas.find_closest(event.x, event.y) for itemId in itemIdList: if itemId in self.selectable: self.setSelection(itemId) self.app.setSelection(self.itemMap[itemId]) return def dragCanvas(self, event): self.app.setModified() self.canvas.focus_set() x = event.x/float(self.scale) y = event.y/float(self.scale) self.moveStl((x - self.startx), (self.starty - y)) self.startx = x self.starty = y def wheelCanvas(self, event): self.app.setModified() if event.num == 4 or event.delta == 120: self.rotateStl(1) elif event.num == 5 or event.delta == -120: self.rotateStl(-1) def moveStl(self, dx, dy): if self.selection is None: return self.canvas.move(self.selection, dx*self.scale, -dy*self.scale) stlObj = self.itemMap[self.selection] stlObj.deltaTranslation(dx, dy) def rotateStl(self, angle): if self.selection is None: return #self.canvas.move(self.selection, dx, -dy) stlObj = self.itemMap[self.selection] rads = math.radians(angle+stlObj.rotation) cos = math.cos(rads) sin = math.sin(rads) d = [] xMin = yMin = 99999 xMax = yMax = -99999 for x,y in stlObj.hull: xp = (x-stlObj.hxCenter)*cos - (y-stlObj.hyCenter)*sin + stlObj.hxCenter if xp < xMin: xMin=xp if xp > xMax: xMax=xp xp += stlObj.translatex yp = (x-stlObj.hxCenter)*sin + (y-stlObj.hyCenter)*cos + stlObj.hyCenter if yp < yMin: yMin=yp if yp > yMax: yMax=yp yp += stlObj.translatey d.extend(self.transform(xp,self.buildarea[1]-yp)) self.canvas.delete(stlObj.name) self.selectable.remove(self.selection) del self.itemMap[self.selection] itemId = self.canvas.create_polygon(d, fill=hilite, outline="black", tag=stlObj.name, width=3) self.selectable.append(itemId) self.itemMap[itemId] = stlObj self.setSelection(itemId) stlObj.deltaRotation(angle) stlObj.hxSize = xMax-xMin stlObj.hySize = yMax-yMin stlObj.hArea = stlObj.hxSize * stlObj.hySize def setSelection(self, itemId): if itemId not in self.selectable: return if self.selection is not None: self.canvas.itemconfig(self.selection, fill=gray) self.canvas.itemconfig(itemId, fill=hilite) self.selection = itemId def setSelectionByName(self, name): for i in self.itemMap.keys(): if self.itemMap[i].name == name: self.setSelection(i) return def getSelection(self): return self.selection def getSelectedStl(self): if self.selection is None: return None return self.itemMap[self.selection] def drawGrid(self): self.canvas.delete("GRID") ltGray = "#C0C0C0" dkGray = "#808080" yleft = 0 yright = self.buildarea[1]*self.zoom*self.scale if yright > self.buildarea[1]*self.scale: yright = self.buildarea[1]*self.scale for x in range(0, self.buildarea[0]+1, 10): if x%50 == 0: c = dkGray else: c = ltGray x = x*self.zoom*self.scale if x >= 0 and x <= self.buildarea[0]*self.scale: self.canvas.create_line(x+self.gridOffset, yleft+self.gridOffset, x+self.gridOffset, yright+self.gridOffset, fill=c, tags="GRID") xtop = 0 xbottom = self.buildarea[0]*self.zoom*self.scale if xbottom > self.buildarea[0]*self.scale: xbottom = self.buildarea[0]*self.scale for y in range(0, self.buildarea[1]+1, 10): if y%50 == 0: c = dkGray else: c = ltGray y = y*self.zoom*self.scale if y >= 0 and y <= self.buildarea[1]*self.scale: self.canvas.create_line(xtop+self.gridOffset, y+self.gridOffset, xbottom+self.gridOffset, y+self.gridOffset, fill=c, tags="GRID") def addStl(self, stlObject, highlight=False, process=True): self.canvas.delete(stlObject.name) if highlight: col = hilite else: col = gray if self.centerOnLoad and process: dx = (self.buildarea[0]/2) - stlObject.hxCenter dy = (self.buildarea[1]/2) - stlObject.hyCenter stlObject.deltaTranslation(dx, dy) stlObject.applyDeltas() d = [] for x,y in stlObject.hull: d.extend(self.transform(x,self.buildarea[1]-y)) itemId = self.canvas.create_polygon(d, fill=col, outline="black", tag=stlObject.name, width=3) self.selectable.append(itemId) self.itemMap[itemId] = stlObject if highlight: self.setSelection(itemId) def delStl(self): if self.selection is None: return stlObj = self.itemMap[self.selection] self.canvas.delete(stlObj.name) self.selectable.remove(self.selection) del self.itemMap[self.selection] self.selection = None def delAll(self): objs = self.itemMap.values() for o in objs: self.canvas.delete(o.name) self.selectable = [] self.itemMap = {} self.selection = None def commit(self): if self.selection is not None: o = self.itemMap[self.selection] name = o.name else: name = "" objs = self.itemMap.values() self.selectable = [] self.itemMap = {} for o in objs: self.canvas.delete(o.name) o.applyDeltas() self.addStl(o, highlight=(o.name == name), process=False) def getItemIdFromObject(self, obj): for itemId in self.itemMap.keys(): if obj.name == self.itemMap[itemId].name: return itemId return None def arrange(self): def cmpobj(a, b): return cmp(b.hArea, a.hArea) omap = Map(self.buildarea) maxx = maxy = -99999 minx = miny = 99999 objs = sorted(self.itemMap.values(), cmpobj) for o in objs: itemId = self.getItemIdFromObject(o) if itemId is None: continue x, y = omap.find(o.hxSize+self.arrangeMargin*2, o.hySize+self.arrangeMargin*2) if x is None or y is None: showinfo("Plate full", "Object %s does not fit" % o.name, parent=self) else: dx = x - o.hxCenter - o.translatex + o.hxSize/2 + self.arrangeMargin dy = y - o.hyCenter - o.translatey + o.hySize/2 + self.arrangeMargin self.setSelection(itemId) self.app.setSelection(o) self.moveStl(dx, dy) deltax = o.hxSize+self.arrangeMargin*2 deltay = o.hySize+self.arrangeMargin*2 omap.mark(x, y, deltax, deltay) if x < minx: minx=x if x+deltax > maxx: maxx=x+deltax if y < miny: miny=y if y+deltay > maxy: maxy=y+deltay dx = self.buildarea[0]/2-(maxx+minx)/2 dy = self.buildarea[1]/2-(maxy+miny)/2 for o in objs: itemId = self.getItemIdFromObject(o) if itemId is None: continue self.setSelection(itemId) self.app.setSelection(o) self.moveStl(dx, dy) def getStls(self): return self.itemMap.values() def countObjects(self): return len(self.selectable) def transform(self, ptx, pty): x = (ptx+self.offsetx)*self.zoom*self.scale+self.gridOffset y = (pty-self.offsety)*self.zoom*self.scale+self.gridOffset return (x, y) def triangulate(self, p1, p2): dx = p2[0] - p1[0] dy = p2[1] - p1[1] d = math.sqrt(dx*dx + dy*dy) return d
class Simulator(object): def __init__(self): tk=Tk() tk.title = "T da B's Gravity Simulator" self.width = 1900 self.height = 1000 self.canvas=Canvas(tk, width=self.width, height=self.height, bg='black') self.masses = list() self.new_masses = list() for _ in range(20): size = randint(3, 7) m = MassiveObject( size * 10**16, size, randint(100, self.width - 100), randint(100, self.height - 100), randint(-15, 15), randint(-15, 15), random_color() ) m.canvas_id = self.canvas.create_oval(m.x - m.radius, m.y - m.radius, m.x + m.radius, m.y + m.radius, outline=m.color, fill=m.color) self.masses.append(m) self.canvas.pack() self.draw() tk.mainloop() def draw(self): """ Main loop """ for m in self.masses: #if m.is_deleted is True: # self.masses.remove(m) # continue for m2 in self.masses: if m2 == m: continue else: self.update(m, m2) #m.draw_accel_vector() #m.draw_vel_vector() self.masses += self.new_masses self.new_masses = list() self.canvas.after(10, self.draw) def update(self, m1, m2): """ Update MassiveObject m1 with respect to m2 """ #if ( distance(m1, m2) < m1.radius + m2.radius ): # logger.info("%s and %s collided!" % (m1.color, m2.color)) # self.canvas.delete(m1.canvas_id, m2.canvas_id) # text = self.canvas.create_text((m1.x + m2.x) / 2, # (m1.y + m2.y) / 2, # text="COLLISION!", # fill='red') # self.canvas.after(1000, self.canvas.delete, text) # m1.is_deleted = True # m2.is_deleted = True if ( distance(m1, m2) < m1.radius + m2.radius ): self.canvas.delete(m1.canvas_id, m2.canvas_id) ''' text = self.canvas.create_text((m1.x + m2.x) / 2, (m1.y + m2.y) / 2, text="COLLISION!", fill='red') self.canvas.after(1000, self.canvas.delete, text) ''' self.masses.remove(m1) self.masses.remove(m2) mid_x = (m1.x + m2.x) / 2 mid_y = (m1.y + m2.y) / 2 c1 = m1.mass / (m1.mass + m2.mass) c2 = m2.mass / (m1.mass + m2.mass) v_x = c1 * m1.v_x + c2 * m2.v_x v_y = c1 * m1.v_y + c2 * m2.v_y new_m = MassiveObject( m1.mass + m2.mass, m1.radius + m2.radius, mid_x, mid_y, v_x, v_y, avg_color(m1.color, m2.color) ) new_m.canvas_id = self.canvas.create_oval(new_m.x - new_m.radius, new_m.y - new_m.radius, new_m.x + new_m.radius, new_m.y + new_m.radius, outline=new_m.color, fill=new_m.color) self.new_masses.append(new_m) return old_x = m1.x old_y = m1.y g_x, g_y = grav_force(m1, m2) m1.a_x = g_x / m1.mass m1.a_y = g_y / m1.mass m1.t += m1.timestep m1.x += m1.timestep * (m1.v_x + .5*(m1.timestep * m1.a_x)) m1.y += m1.timestep * (m1.v_y + .5*(m1.timestep * m1.a_y)) g_x, g_y = grav_force(m1, m2) new_a_x, new_a_y = g_x / m1.mass, g_y / m1.mass m1.v_x += m1.timestep * .5 * (m1.a_x + new_a_x) m1.v_y += m1.timestep * .5 * (m1.a_y + new_a_y) self.canvas.move(m1.canvas_id, m1.x - old_x, m1.y - old_y) logger.info("Updated %s from (%s, %s) to (%s, %s)" % (m1.color, old_x, old_y, m1.x, m1.y)) def draw_accel_vector(self): m = mag(self.x, self.y, self.x + self.a_x, self.y + self.a_y) if m > 1: line = self.canvas.create_line(self.x, self.y, self.x + self.a_x, self.y + self.a_y, fill='yellow', arrow='last') text = self.canvas.create_text(self.x + 1.2 * self.a_x, self.y + 1.2 * self.a_y, text=str(int(m)), fill='yellow') self.canvas.after(int(self.timestep * 1000), self.canvas.delete, line, text) def draw_vel_vector(self): m = mag(self.x, self.y, self.x + self.v_x, self.y + self.v_y) if m > 1: line = self.canvas.create_line(self.x, self.y, self.x + self.v_x, self.y + self.v_y, fill='red', arrow='last') text = self.canvas.create_text(self.x + 1.2 * self.v_x, self.y + 1.2 * self.v_y, text=str(int(m)), fill='red') self.canvas.after(int(self.timestep * 1000), self.canvas.delete, line, text)
class Breakout(Tk): def __init__(self): Tk.__init__(self) self.geometry('400x400') self.resizable(0, 0) # game screen self.canvas = Canvas(self, bg='black', width=400, height=400) self.canvas.pack(expand=1, fill=BOTH) # ball self._initiate_new_ball() # paddle self.canvas.create_rectangle(175, 375, 225, 385, fill='black', outline='white', tags='paddle') self.bind('<Key>', self._move_paddle) # bricks self.bricks = {} brick_coords = [5, 5, 35, 15] for i in range(39): self.canvas.create_rectangle(*brick_coords, outline='white', fill=('#{}'.format( randint(100000, 999999))), tags='brick' + str(i)) self.bricks['brick' + str(i)] = None brick_coords[0] += 30 brick_coords[2] += 30 if brick_coords[2] > 395: brick_coords[0] = 5 brick_coords[2] = 35 brick_coords[1] += 10 brick_coords[3] += 10 def _initiate_new_ball(self): if self.canvas.find_withtag('ball'): self.canvas.delete('ball') self.x = 60 self.y = 100 self.angle = 140 self.speed = 5 self.canvas.create_oval(self.x, self.y, self.x + 10, self.y + 10, fill='lawn green', outline='white', tags='ball') self.after(1000, self._move_ball) def _move_paddle(self, event): if event.keysym == 'Left': if self.canvas.coords('paddle')[0] > 0: self.canvas.move('paddle', -10, 0) elif event.keysym == 'Right': if self.canvas.coords('paddle')[2] < 400: self.canvas.move('paddle', +10, 0) def _move_ball(self): # variables to determine where ball is in relation to other objects ball = self.canvas.find_withtag('ball')[0] bounds = self.canvas.find_overlapping(0, 0, 400, 400) paddle = self.canvas.find_overlapping(*self.canvas.coords('paddle')) for brick in self.bricks.iterkeys(): self.bricks[brick] = self.canvas.find_overlapping( *self.canvas.bbox(brick)) # calculate change in x,y values of ball angle = self.angle - 90 # correct for quadrant IV increment_x = cos(radians(angle)) * self.speed increment_y = sin(radians(angle)) * self.speed # finite state machine to set ball state if ball in bounds: self.ball_state = 'moving' for brick, hit in self.bricks.iteritems(): if ball in hit: self.ball_state = 'hit_brick' delete_brick = brick elif ball in paddle: self.ball_state = 'hit_wall' elif ball not in bounds: if self.canvas.coords('ball')[1] < 400: self.ball_state = 'hit_wall' else: self.ball_state = 'out_of_bounds' self._initiate_new_ball() # handler for ball state if self.ball_state is 'moving': self.canvas.move('ball', increment_x, increment_y) self.after(15, self._move_ball) elif self.ball_state is 'hit_brick' or self.ball_state is 'hit_wall': if self.ball_state == 'hit_brick': self.canvas.delete(delete_brick) del self.bricks[delete_brick] self.canvas.move('ball', -increment_x, -increment_y) self.angle += choice([119, 120, 121]) self._move_ball()
class Game(): WIDTH = 300 HEIGHT = 500 def start(self): '''Starts the game. Creates a window, a canvas, and a first shape. Binds the event handler. Then starts a GUI timer of ms interval self.speed and starts the GUI main loop. ''' #TODO start() needs to be refactored so that the creation of the # window, label, and canvas are independent from setting them to # defaults and starting the game. # # There should also be a way for the user to restart and pause # the game if he or she wishes. # # It's a little weird that level is based only on time and that # as a result it increases faster and faster. Wouldn't it make # more sense for level to be a result of completed lines? self.level = 1 self.score = 0 self.speed = 500 self.counter = 0 self.create_new_game = True self.root = Tk() self.root.title("Tetris") self.status_var = StringVar() self.status_var.set("Level: 1, Score: 0") self.status = Label(self.root, textvariable=self.status_var, font=("Helvetica", 10, "bold")) self.status.pack() self.canvas = Canvas( self.root, width=Game.WIDTH, height=Game.HEIGHT) self.canvas.pack() self.root.bind("<Key>", self.handle_events) self.timer() self.root.mainloop() def timer(self): '''Every self.speed ms, attempt to cause the current_shape to fall(). If fall() returns False, create a new shape and check if it can fall. If it can't, then the game is over. ''' if self.create_new_game == True: self.current_shape = Shape(self.canvas) self.create_new_game = False if not self.current_shape.fall(): lines = self.remove_complete_lines() if lines: self.score += 10 * self.level**2 * lines**2 self.status_var.set("Level: %d, Score: %d" % (self.level, self.score)) self.current_shape = Shape(self.canvas) if self.is_game_over(): #TODO This is a problem. You rely on the timer method to # create a new game rather than creating it here. As a # result, there is an intermittent error where the user # event keypress Down eventually causes can_move_box # to throw an IndexError, since the current shape has # no boxes. Instead, you need to cleanly start a new # game. I think refactoring start() might help a lot # here. # # Furthermore, starting a new game currently doesn't reset # the levels. You should place all your starting constants # in the same place so it's clear what needs to be reset # when. self.create_new_game = True self.game_over() self.counter += 1 if self.counter == 5: self.level += 1 self.speed -= 20 self.counter = 0 self.status_var.set("Level: %d, Score: %d" % (self.level, self.score)) self.root.after(self.speed, self.timer) def handle_events(self, event): '''Handle all user events.''' if event.keysym == "Left": self.current_shape.move(-1, 0) if event.keysym == "Right": self.current_shape.move(1, 0) if event.keysym == "Down": self.current_shape.move(0, 1) if event.keysym == "Up": self.current_shape.rotate() def is_game_over(self): '''Check if a newly created shape is able to fall. If it can't fall, then the game is over. ''' for box in self.current_shape.boxes: if not self.current_shape.can_move_box(box, 0, 1): return True return False def remove_complete_lines(self): shape_boxes_coords = [self.canvas.coords(box)[3] for box in self.current_shape.boxes] all_boxes = self.canvas.find_all() all_boxes_coords = {k : v for k, v in zip(all_boxes, [self.canvas.coords(box)[3] for box in all_boxes])} lines_to_check = set(shape_boxes_coords) boxes_to_check = dict((k, v) for k, v in all_boxes_coords.iteritems() if any(v == line for line in lines_to_check)) counter = Counter() for box in boxes_to_check.values(): counter[box] += 1 complete_lines = [k for k, v in counter.iteritems() if v == (Game.WIDTH/Shape.BOX_SIZE)] if not complete_lines: return False for k, v in boxes_to_check.iteritems(): if v in complete_lines: self.canvas.delete(k) del all_boxes_coords[k] #TODO Would be cooler if the line flashed or something for (box, coords) in all_boxes_coords.iteritems(): for line in complete_lines: if coords < line: self.canvas.move(box, 0, Shape.BOX_SIZE) return len(complete_lines) def game_over(self): self.canvas.delete(Tkinter.ALL) tkMessageBox.showinfo( "Game Over", "You scored %d points." % self.score)
class histogramWidget: BACKGROUND = "#222222" EDGE_HISTOGRAM_COLOR = "#999999" NODE_HISTOGRAM_COLOR = "#555555" TOOLTIP_COLOR="#FFFF55" PADDING = 8 CENTER_WIDTH = 1 CENTER_COLOR = "#444444" ZERO_GAP = 1 UPDATE_WIDTH = 9 UPDATE_COLOR = "#FFFFFF" HANDLE_WIDTH = 5 HANDLE_COLOR = "#FFFFFF" HANDLE_LENGTH = (HEIGHT-2*PADDING) TICK_COLOR = "#FFFFFF" TICK_WIDTH = 10 TICK_FACTOR = 2 LOG_BASE = 10.0 def __init__(self, parent, x, y, width, height, data, logScale=False, callback=None): self.canvas = Canvas(parent,background=histogramWidget.BACKGROUND, highlightbackground=histogramWidget.BACKGROUND,width=width,height=height) self.canvas.place(x=x,y=y,width=width,height=height,bordermode="inside") self.logScale = logScale self.callback = callback self.edgeBars = [] self.nodeBars = [] self.binValues = [] self.numBins = len(data) - 1 self.currentBin = self.numBins # start the slider at the highest bin edgeRange = 0.0 nodeRange = 0.0 for values in data.itervalues(): if values[0] > edgeRange: edgeRange = values[0] if values[1] > nodeRange: nodeRange = values[1] edgeRange = float(edgeRange) # ensure that it will yield floats when used in calculations... nodeRange = float(nodeRange) if logScale: edgeRange = math.log(edgeRange,histogramWidget.LOG_BASE) nodeRange = math.log(nodeRange,histogramWidget.LOG_BASE) # calculate the center line - but don't draw it yet self.center_x = histogramWidget.PADDING if self.logScale: self.center_x += histogramWidget.TICK_WIDTH+histogramWidget.PADDING self.center_y = height/2 self.center_x2 = width-histogramWidget.PADDING self.center_y2 = self.center_y + histogramWidget.CENTER_WIDTH # draw the histograms with background-colored baseline rectangles (these allow tooltips to work on very short bars with little area) self.bar_interval = float(self.center_x2 - self.center_x) / (self.numBins+1) bar_x = self.center_x edge_y2 = self.center_y-histogramWidget.PADDING edge_space = edge_y2-histogramWidget.PADDING node_y = self.center_y2+histogramWidget.PADDING node_space = (height-node_y)-histogramWidget.PADDING thresholds = sorted(data.iterkeys()) for threshold in thresholds: self.binValues.append(threshold) edgeWeight = data[threshold][0] nodeWeight = data[threshold][1] if logScale: if edgeWeight > 0: edgeWeight = math.log(edgeWeight,histogramWidget.LOG_BASE) else: edgeWeight = 0 if nodeWeight > 0: nodeWeight = math.log(nodeWeight,histogramWidget.LOG_BASE) else: nodeWeight = 0 bar_x2 = bar_x + self.bar_interval edge_y = histogramWidget.PADDING + int(edge_space*(1.0-edgeWeight/edgeRange)) edge = self.canvas.create_rectangle(bar_x,edge_y,bar_x2,edge_y2,fill=histogramWidget.EDGE_HISTOGRAM_COLOR,width=0) baseline = self.canvas.create_rectangle(bar_x,edge_y2+histogramWidget.ZERO_GAP,bar_x2,edge_y2+histogramWidget.PADDING,fill=histogramWidget.BACKGROUND,width=0) self.canvas.addtag_withtag("Threshold: %f" % threshold,edge) self.canvas.addtag_withtag("No. Edges: %i" % data[threshold][0],edge) self.canvas.tag_bind(edge,"<Enter>",self.updateToolTip) self.canvas.tag_bind(edge,"<Leave>",self.updateToolTip) self.edgeBars.append(edge) self.canvas.addtag_withtag("Threshold: %f" % threshold,baseline) self.canvas.addtag_withtag("No. Edges: %i" % data[threshold][0],baseline) self.canvas.tag_bind(baseline,"<Enter>",self.updateToolTip) self.canvas.tag_bind(baseline,"<Leave>",self.updateToolTip) node_y2 = node_y + int(node_space*(nodeWeight/nodeRange)) node = self.canvas.create_rectangle(bar_x,node_y,bar_x2,node_y2,fill=histogramWidget.NODE_HISTOGRAM_COLOR,width=0) baseline = self.canvas.create_rectangle(bar_x,node_y-histogramWidget.PADDING,bar_x2,node_y-histogramWidget.ZERO_GAP,fill=histogramWidget.BACKGROUND,width=0) self.canvas.addtag_withtag("Threshold: %f" % threshold,node) self.canvas.addtag_withtag("No. Nodes: %i" % data[threshold][1],node) self.canvas.tag_bind(node,"<Enter>",self.updateToolTip) self.canvas.tag_bind(node,"<Leave>",self.updateToolTip) self.nodeBars.append(node) self.canvas.addtag_withtag("Threshold: %f" % threshold,baseline) self.canvas.addtag_withtag("No. Nodes: %i" % data[threshold][1],baseline) self.canvas.tag_bind(baseline,"<Enter>",self.updateToolTip) self.canvas.tag_bind(baseline,"<Leave>",self.updateToolTip) bar_x = bar_x2 # now draw the center line self.centerLine = self.canvas.create_rectangle(self.center_x,self.center_y,self.center_x2,self.center_y2,fill=histogramWidget.CENTER_COLOR,width=0) # draw the tick marks if logarithmic if self.logScale: tick_x = histogramWidget.PADDING tick_x2 = histogramWidget.PADDING+histogramWidget.TICK_WIDTH start_y = edge_y2 end_y = histogramWidget.PADDING dist = start_y-end_y while dist > 1: dist /= histogramWidget.TICK_FACTOR self.canvas.create_rectangle(tick_x,end_y+dist-1,tick_x2,end_y+dist,fill=histogramWidget.TICK_COLOR,width=0) start_y = node_y end_y = height-histogramWidget.PADDING dist = end_y-start_y while dist > 1: dist /= histogramWidget.TICK_FACTOR self.canvas.create_rectangle(tick_x,end_y-dist,tick_x2,end_y-dist+1,fill=histogramWidget.TICK_COLOR,width=0) # draw the update bar bar_x = self.currentBin*self.bar_interval + self.center_x bar_x2 = self.center_x2 bar_y = self.center_y-histogramWidget.UPDATE_WIDTH/2 bar_y2 = bar_y+histogramWidget.UPDATE_WIDTH self.updateBar = self.canvas.create_rectangle(bar_x,bar_y,bar_x2,bar_y2,fill=histogramWidget.UPDATE_COLOR,width=0) # draw the handle handle_x = self.currentBin*self.bar_interval-histogramWidget.HANDLE_WIDTH/2+self.center_x handle_x2 = handle_x+histogramWidget.HANDLE_WIDTH handle_y = self.center_y-histogramWidget.HANDLE_LENGTH/2 handle_y2 = handle_y+histogramWidget.HANDLE_LENGTH self.handleBar = self.canvas.create_rectangle(handle_x,handle_y,handle_x2,handle_y2,fill=histogramWidget.HANDLE_COLOR,width=0) self.canvas.tag_bind(self.handleBar, "<Button-1>",self.adjustHandle) self.canvas.tag_bind(self.handleBar, "<B1-Motion>",self.adjustHandle) self.canvas.tag_bind(self.handleBar, "<ButtonRelease-1>",self.adjustHandle) parent.bind("<Left>",lambda e: self.nudgeHandle(e,-1)) parent.bind("<Right>",lambda e: self.nudgeHandle(e,1)) # init the tooltip as nothing self.toolTipBox = self.canvas.create_rectangle(0,0,0,0,state="hidden",fill=histogramWidget.TOOLTIP_COLOR,width=0) self.toolTip = self.canvas.create_text(0,0,state="hidden",anchor="nw") self.canvas.bind("<Enter>",self.updateToolTip) self.canvas.bind("<Leave>",self.updateToolTip) def adjustHandle(self, event): newBin = int(self.numBins*(event.x-self.center_x)/float(self.center_x2-self.center_x)+0.5) if newBin == self.currentBin or newBin < 0 or newBin > self.numBins: return self.canvas.move(self.handleBar,(newBin-self.currentBin)*self.bar_interval,0) self.currentBin = newBin if self.callback != None: self.callback(self.binValues[newBin]) def nudgeHandle(self, event, distance): temp = self.currentBin+distance if temp < 0 or temp > self.numBins: return self.canvas.move(self.handleBar,distance*self.bar_interval,0) self.currentBin += distance if self.callback != None: self.callback(self.binValues[self.currentBin]) def update(self, currentBins): currentBar = self.canvas.coords(self.updateBar) self.canvas.coords(self.updateBar,currentBins*self.bar_interval+self.center_x,currentBar[1],currentBar[2],currentBar[3]) def updateToolTip(self, event): allTags = self.canvas.gettags(self.canvas.find_overlapping(event.x,event.y,event.x+1,event.y+1)) if len(allTags) == 0: self.canvas.itemconfig(self.toolTipBox,state="hidden") self.canvas.itemconfig(self.toolTip,state="hidden") return outText = "" for t in allTags: if t == "current": continue outText += t + "\n" outText = outText[:-1] # strip the last return self.canvas.coords(self.toolTip,event.x+20,event.y) self.canvas.itemconfig(self.toolTip,state="normal",text=outText,anchor="nw") # correct if our tooltip is off screen textBounds = self.canvas.bbox(self.toolTip) if textBounds[2] >= WIDTH-2*histogramWidget.PADDING: self.canvas.itemconfig(self.toolTip, anchor="ne") self.canvas.coords(self.toolTip,event.x-20,event.y) if textBounds[3] >= HEIGHT-2*histogramWidget.PADDING: self.canvas.itemconfig(self.toolTip, anchor="se") elif textBounds[3] >= HEIGHT-2*histogramWidget.PADDING: self.canvas.itemconfig(self.toolTip, anchor="sw") # draw the box behind it self.canvas.coords(self.toolTipBox,self.canvas.bbox(self.toolTip)) self.canvas.itemconfig(self.toolTipBox, state="normal")
class carGUI: carDict = {} carIDs = [] def __init__(self, master): self.master = master master.title("A simple GUI") # Initialize Canvas self.canv = Canvas(master) self.canv.pack(fill='both', expand=True) # Initialize X-Lane self.xTop = self.canv.create_line(0, 470, 1000, 470, fill='black', tags=('top')) self.xBottom = self.canv.create_line(0, 510, 1000, 510, fill='black', tags=('left')) # Initialize Y-Lane self.yLeft = self.canv.create_line(470, 0, 470, 1000, fill='blue', tags='right') self.yRight = self.canv.create_line(510, 0, 510, 1000, fill='blue', tags='bottom') # Highlight Intersection self.rect = self.canv.create_rectangle(470, 470, 510, 510, fill='green') # Show Regulation Lines self.xLimit = self.canv.create_line(470 - 40, 450, 470 - 40, 530, fill="red") self.yLimit = self.canv.create_line(450, 470 - 40, 530, 470 - 40, fill="red") # Create button to begin simulation b = Button(text="Start Simluation!", command=self.simClickListener) b.pack() # Create checkbox to differentiate real world sim from autonomous sim self.CheckVar = IntVar() self.checkConventional = Checkbutton(text="Conventional System", variable=self.CheckVar, \ onvalue=1, offvalue=0, height=5) self.checkConventional.pack() # Create text fields to show first in queue cars self.carDisplayX = self.canv.create_text(10, 10, anchor="nw", fill="red") self.carDisplayY = self.canv.create_text(600, 10, anchor="nw", fill="black") def drawCar(self, lane, ID): if (lane == 1): # Draw an X car self.rect = self.canv.create_rectangle(0, 485, 10, 495, fill='black') elif (lane == 2): # Draw a Y car self.rect = self.canv.create_rectangle(485, 0, 495, 10, fill='red') self.canv.addtag_below(self.rect, "HELLO") # Register the ID of the car self.carIDs.append(ID) # Ad the key value pair to the car dictionary for the GUI self.carDict[ID] = self.rect def moveCars(self, carList, timeInterval): self.master.update_idletasks() # THIS UPDATES THE GUI for i in range(0, len(carList)): self.canv.move(self.carDict[carList[i].ID], carList[i].velocityX * timeInterval, carList[i].velocityY * timeInterval) def highlightCar(self, car, color): self.canv.itemconfig(self.carDict[car.ID], fill=color) def simClickListener(self): from Simulation import simulation as sim sim(self) def updateCarInformationDisplay(self, car): carData = "position X = " + str(car.positionX) + "\nposition Y = " + \ str(car.positionY) + "\nvelocity X = " + str(car.velocityX) + \ "\nvelocity Y = " + str(car.velocityY) if (car.velocityX > 0): self.canv.itemconfig(self.carDisplayX, text=carData) else: self.canv.itemconfig(self.carDisplayY, text=carData)
class MazePlannerCanvas(Frame): """ MazePlannerCanvas contains the main frontend workhorse functionality of the entire application. it allows the user to graphically place nodes and define the edges between them """ def __init__(self, parent, status=None, manager=DataStore()): """ Construct an instance of the MazePlannerCanvas :param parent: The parent widget that the mazePlannerCanvas will sit in :param status: The statusbar that will receive mouse updates :type manager: DataStore :return: """ Frame.__init__(self, parent) self._manager = manager self._canvas = Canvas(self, bg="grey", cursor="tcross") self._canvas.pack(fill=BOTH, expand=1) self._commands = { (ControlSpecifier.DRAG_NODE, ExecutionStage.START) : self._begin_node_drag, (ControlSpecifier.CREATE_EDGE, ExecutionStage.START) : self._begin_edge, (ControlSpecifier.DRAG_NODE, ExecutionStage.END) : self._end_node_drag, (ControlSpecifier.CREATE_EDGE, ExecutionStage.END) : self._end_edge, (ControlSpecifier.DRAG_NODE, ExecutionStage.EXECUTE) : self._execute_drag, (ControlSpecifier.CREATE_EDGE, ExecutionStage.EXECUTE) : self._execute_edge, (ControlSpecifier.MENU, ExecutionStage.EXECUTE) : self._launch_menu, (ControlSpecifier.CREATE_NODE, ExecutionStage.EXECUTE) : self.create_new_node, } self._commands = load_controls(self._commands) self._edge_cache = \ { "x_start" : None, "y_start" : None, "x_end" : None, "y_end" : None, "item_start" : None, "item_end" : None, "edge" : None } self._command_cache = None self._cache = \ { "item" : None, "x" : 0, "y" : 0, "event" : None } self._status = status self._edge_bindings = {} self._node_listing = {} self._object_listing = {} self._curr_start = None self._construct(parent) def _construct(self, parent): """ Construct all of the event bindings and callbacks for mouse events """ self._canvas.focus_set() self._canvas.bind("<B1-Motion>", lambda event, m_event=Input_Event.DRAG_M1: self._handle_mouse_events(m_event, event)) self._canvas.bind("<B2-Motion>", lambda event, m_event=Input_Event.DRAG_M2: self._handle_mouse_events(m_event, event)) self._canvas.bind("<B3-Motion>", lambda event, m_event=Input_Event.DRAG_M3: self._handle_mouse_events(m_event, event)) self._canvas.bind("<ButtonPress-2>", lambda event, m_event=Input_Event.CLICK_M2: self._handle_mouse_events(m_event, event)) self._canvas.bind("<ButtonRelease-2>", lambda event, m_event=Input_Event.RELEASE_M2: self._handle_mouse_events(m_event, event)) self._canvas.bind("<ButtonPress-1>", lambda event, m_event=Input_Event.CLICK_M1: self._handle_mouse_events(m_event, event)) self._canvas.bind("<ButtonPress-3>", lambda event, m_event=Input_Event.CLICK_M3: self._handle_mouse_events(m_event, event)) self._canvas.bind("<ButtonRelease-1>", lambda event, m_event=Input_Event.RELEASE_M1: self._handle_mouse_events(m_event, event)) self._canvas.bind("<ButtonRelease-3>", lambda event, m_event=Input_Event.RELEASE_M3: self._handle_mouse_events(m_event, event)) self._canvas.bind("<Return>", lambda event, m_event=Input_Event.RETURN: self._handle_mouse_events(m_event, event)) self._canvas.bind("<Double-Button-1>", lambda event, m_event=Input_Event.D_CLICK_M1: self._handle_mouse_events(m_event, event)) self._canvas.bind("<Double-Button-2>", lambda event, m_event=Input_Event.D_CLICK_M2: self._handle_mouse_events(m_event, event)) self._canvas.bind("<Double-Button-3>", lambda event, m_event=Input_Event.D_CLICK_M3: self._handle_mouse_events(m_event, event)) self._canvas.bind("<Motion>", lambda event, m_event=None : self._handle_mot(m_event, event)) self._canvas.bind("<Enter>", lambda event: self._canvas.focus_set()) self._canvas.bind("<space>", lambda event, m_event=Input_Event.SPACE: self._handle_mouse_events(m_event, event)) def _handle_mot(self, m_event, event): """ Callback function to handle movement of the mouse Function updates the mouse location status bar as well as setting cache values to the current location of the mouse :m_event: The specifier for the type of event that has been generated :event: The tk provided event object """ event.x = int(self._canvas.canvasx(event.x)) event.y = int(self._canvas.canvasy(event.y)) self._status.set_text("Mouse X:" + str(event.x) + "\tMouse Y:" + str(event.y)) item = self._get_current_item((event.x, event.y)) if self._is_node(item): Debug.printi("Node: " + str(item), Debug.Level.INFO) if self._is_edge(item): d_x = self._edge_bindings[item].x_start - self._edge_bindings[item].x_end d_y = self._edge_bindings[item].y_start - self._edge_bindings[item].y_end square = (d_x * d_x) + (d_y * d_y) distance = int(math.sqrt(square)) Debug.printi("Edge: " + str(item) + " | Source: " + str(self._edge_bindings[item].item_start) + " | Target: " + str(self._edge_bindings[item].item_end) + " | Length: " + str(distance)) self._cache["x"] = event.x self._cache["y"] = event.y def _handle_mouse_events(self, m_event, event): """ Function that routes mouse events to the appropriate handlers Prints logging and UI information about the state of the mouse and then routes the mouse event to the appropriate handler :m_event: The specifier for the tupe of event that has been generated :event: The tk provided event object """ event.x = int(self._canvas.canvasx(event.x)) event.y = int(self._canvas.canvasy(event.y)) self._status.set_text("Mouse X:" + str(self._cache["x"]) + "\tMouse Y:" + str(self._cache["y"])) Debug.printet(event, m_event, Debug.Level.INFO) self._cache["event"] = event try: self._commands[m_event]((event.x, event.y)) except KeyError: Debug.printi("Warning, no control mapped to " + m_event, Debug.Level.ERROR) self._command_cache = m_event def _begin_node_drag(self, coords): """ Handles starting operations for dragging a node Updates the cache information regarding a node drag event, we will used this cache value as the handle on which node to update the information for :coords: The mouse coordinates associated with this event """ # Determine which node has been selected, cache this information item = self._get_current_item(coords) if item in self._node_listing: self._update_cache(item, coords) def _end_node_drag(self, coords): """ Performs actions to complete a node drag operation Validates node location, and other associated object information and updates the cache when a node drag is completed :coords: The coordinates associated with this event """ if self._cache["item"] is None: return # Obtain the final points x = coords[0] y = coords[1] item = self._cache["item"] self._validate_node_position(coords) container = self._manager.request(DataStore.DATATYPE.NODE, item) container.x_coordinate = x container.y_coordinate = y self._manager.inform(DataStore.EVENT.NODE_EDIT, container.empty_container(), self._cache["item"]) Debug.printi("Node " + str(self._cache["item"]) + " has been moved", Debug.Level.INFO) # Clean the cache self._clear_cache(coords) def _validate_node_position(self, coords): """ if x < 0: x = 0 if y < 0: y = 0 if x > self._canvas.winfo_width(): x = self._canvas.winfo_width()-25 if y > self._canvas.winfo_height(): y = self._canvas.winfo_height()-25 self._canvas.move(item, x, y) """ pass def _execute_drag(self, coords): """ Updates object position on canvas when user is dragging a node :param coords: The coordinates associated with this event """ # Abort drag if the item is not a node if self._cache["item"] not in self._node_listing: return # Update the drawing information delta_x = coords[0] - self._cache["x"] delta_y = coords[1] - self._cache["y"] # move the object the appropriate amount as long as the drag event has not been done on the empty canvas if not self._cache["item"] is None: self._canvas.move(self._cache["item"], delta_x, delta_y) # record the new position self._cache["x"] = coords[0] self._cache["y"] = coords[1] self._update_attached_edges(self._cache["item"], coords) self._update_attached_objects(self._cache["item"], coords) def _update_attached_objects(self, item, coords): if item not in self._object_listing: return container = self._manager.request(DataStore.DATATYPE.OBJECT, item) container.x_coordinate = coords[0] container.y_coordinate = coords[1] self._manager.inform(DataStore.EVENT.OBJECT_EDIT, container.empty_container(), item) def _update_attached_edges(self, node, coords): """ Updates all associated edges related to a node drag event :param node: The node that has been dragged :param coords: The mouse coordinates which are the new coordinates of the node """ # Go through dictionary and gather list of all attached edge bindings for a node start_bindings = [] for key, binding in self._edge_bindings.iteritems(): if binding.item_start == node: start_bindings.append(binding) end_bindings = [] for key, binding in self._edge_bindings.iteritems(): if binding.item_end == node: end_bindings.append(binding) # Adjust the bindings with this node as the starting edge for binding in start_bindings: self._canvas.delete(binding.edge) del self._edge_bindings[binding.edge] old_edge = binding.edge binding.edge = self._canvas.create_line(coords[0], coords[1], binding.x_end, binding.y_end, tags="edge", activefill="RoyalBlue1", tag="edge") self._edge_bindings[binding.edge] = binding self._manager.update_key(DataStore.EVENT.EDGE_EDIT, binding.edge, old_edge) binding.x_start = coords[0] binding.y_start = coords[1] # Adjust the bindings with this node as the ending edge for binding in end_bindings: self._canvas.delete(binding.edge) del self._edge_bindings[binding.edge] old_edge = binding.edge binding.edge = self._canvas.create_line(binding.x_start, binding.y_start, coords[0], coords[1], tags="edge", activefill="RoyalBlue1", tag="edge") self._edge_bindings[binding.edge] = binding self._manager.update_key(DataStore.EVENT.EDGE_EDIT, binding.edge, old_edge) binding.x_end = coords[0] binding.y_end = coords[1] # Remember to adjust all of the edges so that they sit under the node images self._canvas.tag_lower("edge") def _launch_menu(self, coords): """ Callback function in response to the pressing of the Return key Launches a context menu based on the location of the mouse :param coords: :return: """ # Configure the "static" menu entries -- they can't be static without seriously destroying readability # due to the Python version that is being used -.- so now it has to be not optimal until I find a better # solution p_menu = Menu(self._canvas) item = self._get_current_item((self._cache["x"], self._cache["y"])) updated_coords = self._canvas_to_screen((self._cache["x"], self._cache["y"])) if item is None: # No node is currently selected, create the general menu p_menu.add_command(label="Place Room", command=lambda: self.create_new_node((self._cache["x"], self._cache["y"]))) p_menu.add_command(label="Delete All", command=lambda: self.delete_all()) p_menu.tk_popup(updated_coords[0], updated_coords[1]) return if self._is_node(item): # Create the node specific menu p_menu.add_command(label="Place Object", command=lambda: self._mark_object((self._cache["x"], self._cache["y"]))) p_menu.add_command(label="Edit Room", command=lambda: self._selection_operation((self._cache["x"], self._cache["y"]))) p_menu.add_command(label="Delete Room", command=lambda: self.delete_node(self._get_current_item((self._cache["x"], self._cache["y"])))) p_menu.add_command(label="Mark as start", command=lambda: self._mark_start_node(self._get_current_item((self._cache["x"], self._cache["y"])))) if self._is_object(item): # Launch the node menu as well as an an added option for selecting stuff to edit an object p_menu.add_command(label="Edit Object", command=lambda: self._edit_object(coords)) p_menu.add_command(label="Delete Object", command=lambda: self._delete_object(self._get_current_item((self._cache["x"], self._cache["y"])))) p_menu.delete(0) p_menu.tk_popup(updated_coords[0], updated_coords[1]) return if self._is_edge(item): p_menu.add_command(label="Edit Corridor", command=lambda: self._selection_operation((self._cache["x"], self._cache["y"]))) p_menu.add_command(label="Delete Corridor", command=lambda: self.delete_edge(self._get_current_item((self._cache["x"], self._cache["y"])))) p_menu.tk_popup(updated_coords[0], updated_coords[1]) return self._clear_cache(coords) def _edit_object(self, coords): """ Awkward moment when you find a threading related bug in the Tkinter library, caused by some Tcl issue or something like that. The below line must be left commented out otherwise the window_wait call in the dialog will crash out with a Tcl ponter based issue :/ item = self._get_current_item((self._cache["x"], self._cache["y"])) This means that we can only use the mouse to edit objects """ item = self._get_current_item(coords) if item not in self._object_listing: Debug.printi("Not a valid object to edit", Debug.Level.ERROR) return obj = ObjectDialog(self, coords[0] + 10, coords[1] + 10, populator=self._manager.request(DataStore.DATATYPE.OBJECT, item)) Debug.printi("Editing object " + str(item), Debug.Level.INFO) self._manager.inform(DataStore.EVENT.OBJECT_EDIT, obj._entries, item) Debug.printi("Editing object " + str(item), Debug.Level.INFO) def _delete_object(self, item): if item not in self._object_listing: Debug.printi("Object does not exist to delete", Debug.Level.ERROR) return del self._object_listing[item] self._manager.inform(DataStore.EVENT.OBJECT_DELETE, data_id=item) self._canvas.itemconfig(item, outline="red", fill="black", activeoutline="black", activefill="red") def _mark_object(self, coords, prog=False, data=None): """ Mark a node as containing an object :param coords: :return: """ # Retrieve the item item = self._get_current_item(coords) if not prog: if item not in self._node_listing: Debug.printi("Invalid object placement selection", Debug.Level.ERROR) return if item in self._object_listing: Debug.printi("This room already has an object in it", Debug.Level.ERROR) return # Retrieve its coordinates # Launch the object maker dialog obj = ObjectDialog(self, coords[0] + 10, coords[1] + 10, populator=Containers.ObjectContainer(key_val={ "x_coordinate" : coords[0], "y_coordinate" : coords[1], "name" : "Object_"+str(item), "mesh" : None, "scale" : None })) entries = obj._entries else: entries = { "x_coordinate": coords[0], "y_coordinate": coords[1], "name": data["name"], "mesh": data["mesh"], "scale": data["scale"] } # Save informatoin to the manager self._manager.inform(DataStore.EVENT.OBJECT_CREATE, entries, item) self._object_listing[item] = item self._canvas.itemconfig(item, fill="blue") Debug.printi("Object created in room " + str(item), Debug.Level.INFO) def _valid_edge_cache(self): """ Return true if the edge cache contains a valid edge descriptor A valid edge descriptor is when the edge has a valid starting node, if the edge does not contain a valid starting node, this means that the edge was not created in the proper manner and should thus be ignored by any edge operations """ valid = not self._edge_cache["item_start"] == (None,) return valid def _canvas_to_screen(self, coords): """ Convert canvas coordinates into screen coordinates :param coords: The current canvas coordinates :return: """ """ # upper left corner of the visible region x0 = self._canvas.winfo_rootx() y0 = self._canvas.winfo_rooty() # given a canvas coordinate cx/cy, convert it to window coordinates: wx0 = x0 + coords[0] wy0 = y0 + coords[1] # upper left corner of the visible region x0 = self._canvas.canvasx(0) y0 = self._canvas.canvasy(0) # given a canvas coordinate cx/cy, convert it to window coordinates: wx0 = coords[0] - x0 wy0 = coords[1] - y0 #""" return (self._cache["event"].x_root, self._cache["event"].y_root) def _begin_edge(self, coords): """ Begin recording information regarding the placement of an edge :param coords: The coordinates associated with this event """ # Record the starting node self._edge_cache["item_start"] = self._get_current_item((self._cache["x"], self._cache["y"])) # Abort the operation if the item was not a valid node to be selecting if self._edge_cache["item_start"] is None or self._edge_cache["item_start"] not in self._node_listing: self._clear_edge_cache() return self._edge_cache["x_start"] = self._cache["x"] self._edge_cache["y_start"] = self._cache["y"] def _end_edge(self, coords, prog=False, data=None): """ Perform the operations required to complete an edge creation operation :param coords: :return: """ # Check if the cursor is over a node, if so continue, else abort curr = self._get_current_item((coords[0], coords[1])) if not prog: if curr is None or not self._valid_edge_cache() or curr not in self._node_listing: # Abort the edge creation process self._canvas.delete(self._edge_cache["edge"]) self._clear_edge_cache() return # Check if this edge already exists in the program if self._check_duplicate_edges(self._edge_cache["item_start"], curr): self.delete_edge(self._edge_cache["edge"]) Debug.printi("Multiple edges between rooms not permitted", Debug.Level.ERROR) return #Ensure that edges arent made between the same room if curr == self._edge_cache["item_start"]: Debug.printi("Cannot allow paths starting and ending in the same room", Debug.Level.ERROR) return self._canvas.tag_lower("edge") self._edge_cache["item_end"] = curr # Note that we use the edge ID as the key self._edge_bindings[self._edge_cache["edge"]] = EdgeBind(self._edge_cache) self._edge_bindings[self._edge_cache["edge"]].x_end = coords[0] self._edge_bindings[self._edge_cache["edge"]].y_end = coords[1] # Inform the manager if not prog: self._manager.inform( DataStore.EVENT.EDGE_CREATE, { "source" : self._edge_cache["item_start"], "target" : self._edge_cache["item_end"], "height" : None, "wall1" : { "height":Defaults.Edge.WALL_HEIGHT, "textures":{ Defaults.Wall.PATH: { "path":Defaults.Wall.PATH, "tile_x":Defaults.Wall.TILE_X, "tile_y":Defaults.Wall.TILE_Y, "height":None } } } if Defaults.Config.EASY_MAZE else None, "wall2" : { "height": Defaults.Edge.WALL_HEIGHT, "textures": { Defaults.Wall.PATH: { "path": Defaults.Wall.PATH, "tile_x": Defaults.Wall.TILE_X, "tile_y": Defaults.Wall.TILE_Y, "height":None } } } } if Defaults.Config.EASY_MAZE else None, self._edge_cache["edge"]) else: # We are programmatically adding the edges in self._manager.inform( DataStore.EVENT.EDGE_CREATE, { "source": self._edge_cache["item_start"], "target": self._edge_cache["item_end"], "height": None, "wall1": data["wall1"], "wall2": data["wall2"] }, self._edge_cache["edge"]) Debug.printi("Edge created between rooms " + str(self._edge_cache["item_start"]) + " and " + str(self._edge_cache["item_end"]) , Debug.Level.INFO) self._clear_edge_cache() self._clear_cache(coords) def _check_duplicate_edges(self, start_node, end_node): for binding in self._edge_bindings.itervalues(): if ( start_node == binding.item_start and end_node == binding.item_end )\ or ( start_node == binding.item_end and end_node == binding.item_start): return True return False def _execute_edge(self, coords): """ Perform the operations that occur during the motion of an edge drag :param coords: :return: """ # Update the line position # We will update the line position by deleting and redrawing if not self._valid_edge_cache(): return self._canvas.delete(self._edge_cache["edge"]) self._edge_cache["edge"] = self._canvas.create_line( \ self._edge_cache["x_start"], self._edge_cache["y_start"], coords[0]-1, coords[1]-1, tags="edge", activefill="RoyalBlue1", tag="edge") d_x = self._edge_cache["x_start"] - coords[0] d_y = self._edge_cache["y_start"] - coords[1] square = (d_x * d_x) + (d_y * d_y) distance = math.sqrt(square) Debug.printi("Current corridor distance: " + str(int(distance))) def _update_cache(self, item, coords): """ Update the local cache with the item id and coordinates of the mouse :param item: The item with which to update the cache :param coords: The current event coordinates """ self._cache["item"] = item self._cache["x"] = coords[0] self._cache["y"] = coords[1] def _clear_cache(self, coords): """ Clear the cache Set the cache values to the current mouse position and None the item :param coords: The coordinates of the mouse at that event time """ self._cache["item"] = None self._cache["x"] = coords[0] self._cache["y"] = coords[1] def _clear_edge_cache(self): """ Clear the edge cache to None for all values :return: """ self._edge_cache["x_start"] = None, self._edge_cache["y_start"] = None, self._edge_cache["x_end"] = None, self._edge_cache["y_end"] = None, self._edge_cache["item_start"] = None, self._edge_cache["item_end"] = None, self._edge_cache["edge"] = None def _get_current_item(self, coords): """ Return the item(if any) that the mouse is currently over :param coords: The current coordinates of the mouse :return: """ item = self._canvas.find_overlapping(coords[0]-1, coords[1]-1, coords[0]+1, coords[1]+1) if item is (): return None # Hacky solution # Return the first node that we come across, since they seem to be returned by tkinter # in reverse order to their visual positioning, we'll go through the list backwards for val in item[::-1]: if val in self._node_listing: return val # Else, just return the first item and be done with it return item[0] def _is_node(self, obj): """ Returns true if the supplied object is a node :param obj: The object id to id :return: """ return obj in self._node_listing def _is_edge(self, obj): """ Returns true if the supplied object is an edge :param obj: The object id to id :return: """ return obj in self._edge_bindings def _is_object(self, obj): """ Returns true if the supplied object is an object :param obj: The object id to id :return: """ return obj in self._object_listing def _get_obj_type(self, obj): """ Returns the Object type of the supplied object :param obj: The object to identify :return: """ if self._is_node(obj): return EditableObject.NODE if self._is_edge(obj): return EditableObject.EDGE if self._is_object(obj): return EditableObject.OBJECT return None def _selection_operation(self, coords): """ Contextually create or edit a node :param coords: :return: """ # Determine the item ID item = self._get_current_item(coords) self._cache["item"] = item true_coords = self._canvas_to_screen((self._cache["x"], self._cache["y"])) if self._is_node(item): Debug.printi("Node Selected : " + str(item) + " | Launching Editor", Debug.Level.INFO) # Make request from object manager using the tag assigned populator = self._manager.request(DataStore.DATATYPE.NODE, item) updated_node = NodeDialog(self, true_coords[0] + 10, true_coords[1] + 10, populator=populator) # post information to object manager, or let the dialog handle it, or whatever self._manager.inform(DataStore.EVENT.NODE_EDIT, updated_node._entries, item) return if self._is_edge(item): Debug.printi("Edge Selected : " + str(item) + " | Launching Editor", Debug.Level.INFO) # Make a request from the object manager to populate the dialog populator = self._manager.request(DataStore.DATATYPE.EDGE, item) updated_edge = EdgeDialog(self, true_coords[0] + 10, true_coords[1] + 10, populator=populator) # Make sure that information is posted to the object manager self._manager.inform(DataStore.EVENT.EDGE_EDIT, updated_edge._entries, item) return if self._is_object(item): self._edit_object(coords) return def create_new_node(self, coords, prog = False, data=None): """ Creates a new node on the Canvas and adds it to the datastore :param coords: :return: """ # Create the node on Canvas self._cache["item"] = self._canvas.create_rectangle(coords[0], coords[1], coords[0]+25, coords[1]+25, outline="red", fill="black", activeoutline="black", activefill="red", tag="node") self._node_listing[self._cache["item"]] = self._cache["item"] if not prog: if not Defaults.Config.EASY_MAZE: true_coords = self._canvas_to_screen((self._cache["x"], self._cache["y"])) new_node = NodeDialog(self, true_coords[0] + 25, true_coords[1] + 25, populator=Containers.NodeContainer( { "node_id": self._cache["item"], "x_coordinate": self._cache["x"], "y_coordinate": self._cache["y"], "room_texture": None, "wall_pictures": None })) entries = new_node._entries else: entries = { "node_id": self._cache["item"], "x_coordinate": self._cache["x"], "y_coordinate": self._cache["y"], "room_texture": Defaults.Node.ROOM_TEXTURE, "wall_pictures": None } else: pics = data[1] data = data[0] entries = { "node_id": data["id"], "x_coordinate": data["x"], "y_coordinate": data["y"], "room_texture": data["texture"], "wall_pictures": pics } # Inform the datastore self._manager.inform(DataStore.EVENT.NODE_CREATE, entries, self._cache["item"]) self._clear_cache(coords) def delete_all(self): """ Delete all nodes and associated edges and objects from the canvas """ # Iterate over each node in the node listing and delete it using delete node for key in self._node_listing.keys(): self.delete_node(key) # Delete any rouge edge bindings that may exist for binding in self._edge_bindings: self.delete_edge(binding) self._object_listing.clear() # Delete any naughty objects that are left self._canvas.delete("all") self._manager.inform(DataStore.EVENT.DELETE_ALL) def delete_node(self, node_id): """ Delete a node and all its associated edges and object from the canvas :param node_id: The tkinter id of the node to be deleted """ # Delete from our internal representations if node_id not in self._node_listing: return del self._node_listing[node_id] # Delete from the canvas self._canvas.delete(node_id) # Iterate through the edge bindings and delete all of those for key in self._edge_bindings.keys(): if self._edge_bindings[key].item_start == node_id or self._edge_bindings[key].item_end == node_id: self.delete_edge(key) # Inform the object manager that a node as been deleted if node_id in self._object_listing: self._delete_object(node_id) self._manager.inform(DataStore.EVENT.NODE_DELETE, data_id=node_id) def delete_edge(self, edge_id): """ Delete the specified edge from the MazeCanvas :param edge_id: The edge to be deleted :return: """ # Go through the edge bindings and delete the appropriate edge try: # try to delete the edge binding if it exists del self._edge_bindings[edge_id] except KeyError: # Terrible I know, but I dont have the time to find the root cause pass # Delete the edge from the canvas self._canvas.delete(edge_id) # Inform the object manager that an edge has been deleted self._manager.inform(DataStore.EVENT.EDGE_DELETE, data_id=edge_id) def _mark_start_node(self, node_id): """ Mark the passed in node as the starting node :param node_id: :return: """ # Print the debug information # Mark as the new starting node on the canvas, first check that it is a node if node_id in self._node_listing: Debug.printi("Node:" + str(node_id) + " has been marked as the new starting node", Debug.Level.INFO) if self._curr_start is not None: # Return the old starting node to its normal colour self._canvas.itemconfig(self._curr_start, outline="red", fill="black", activeoutline="black", activefill="red") self._curr_start = node_id self._canvas.itemconfig(node_id, outline="black", fill="green", activeoutline="green", activefill="black") # Inform the object manager that there is a new starting node environment_container = self._manager.request(DataStore.DATATYPE.ENVIRONMENT) environment_container.start_node = node_id self._manager.inform(DataStore.EVENT.ENVIRONMENT_EDIT, environment_container)