def test2(): # test w/o scrolling canvas root = Tk() root.configure(bd=0) canvas = Canvas(root, bg="white", highlightthickness=0) canvas.pack(expand=1, fill="both") item = FileTreeItem(os.curdir) node = TreeNode(canvas, None, item) node.update() canvas.focus_set()
class ScrolledCanvas: def __init__(self, master, **opts): if not opts.has_key('yscrollincrement'): opts['yscrollincrement'] = 17 self.master = master self.frame = Frame(master) self.frame.rowconfigure(0, weight=1) self.frame.columnconfigure(0, weight=1) self.canvas = Canvas(self.frame, **opts) self.canvas.grid(row=0, column=0, sticky="nsew") self.vbar = Scrollbar(self.frame, name="vbar") self.vbar.grid(row=0, column=1, sticky="nse") self.hbar = Scrollbar(self.frame, name="hbar", orient="horizontal") self.hbar.grid(row=1, column=0, sticky="ews") self.canvas['yscrollcommand'] = self.vbar.set self.vbar['command'] = self.canvas.yview self.canvas['xscrollcommand'] = self.hbar.set self.hbar['command'] = self.canvas.xview self.canvas.bind("<Key-Prior>", self.page_up) self.canvas.bind("<Key-Next>", self.page_down) self.canvas.bind("<Key-Up>", self.unit_up) self.canvas.bind("<Key-Down>", self.unit_down) #if isinstance(master, Toplevel) or isinstance(master, Tk): self.canvas.bind("<Alt-Key-2>", self.zoom_height) self.canvas.focus_set() def page_up(self, event): self.canvas.yview_scroll(-1, "page") return "break" def page_down(self, event): self.canvas.yview_scroll(1, "page") return "break" def unit_up(self, event): self.canvas.yview_scroll(-1, "unit") return "break" def unit_down(self, event): self.canvas.yview_scroll(1, "unit") return "break" def zoom_height(self, event): ZoomHeight.zoom_height(self.master) return "break"
class ScrolledCanvas: def __init__(self, master, **opts): if 'yscrollincrement' not in opts: opts['yscrollincrement'] = 17 self.master = master self.frame = Frame(master) self.frame.rowconfigure(0, weight=1) self.frame.columnconfigure(0, weight=1) self.canvas = Canvas(self.frame, **opts) self.canvas.grid(row=0, column=0, sticky="nsew") self.vbar = Scrollbar(self.frame, name="vbar") self.vbar.grid(row=0, column=1, sticky="nse") self.hbar = Scrollbar(self.frame, name="hbar", orient="horizontal") self.hbar.grid(row=1, column=0, sticky="ews") self.canvas['yscrollcommand'] = self.vbar.set self.vbar['command'] = self.canvas.yview self.canvas['xscrollcommand'] = self.hbar.set self.hbar['command'] = self.canvas.xview self.canvas.bind("<Key-Prior>", self.page_up) self.canvas.bind("<Key-Next>", self.page_down) self.canvas.bind("<Key-Up>", self.unit_up) self.canvas.bind("<Key-Down>", self.unit_down) #if isinstance(master, Toplevel) or isinstance(master, Tk): self.canvas.bind("<Alt-Key-2>", self.zoom_height) self.canvas.focus_set() def page_up(self, event): self.canvas.yview_scroll(-1, "page") return "break" def page_down(self, event): self.canvas.yview_scroll(1, "page") return "break" def unit_up(self, event): self.canvas.yview_scroll(-1, "unit") return "break" def unit_down(self, event): self.canvas.yview_scroll(1, "unit") return "break" def zoom_height(self, event): ZoomHeight.zoom_height(self.master) return "break"
def test(): global vid, canvas vid = imageio.get_reader( r'C:\Users\ohadassi\PycharmProjects\untitled\test.mp4', 'ffmpeg') master = Tk() canvas = Canvas(master) canvas.pack() canvas.bind("<1>", lambda event: canvas.focus_set()) canvas.bind('n', nextFrame) canvas.bind('p', prevFrame) canvas.bind('<B1-Motion>', motion) canvas.bind('<1>', press) nextFrame(None) mainloop()
class SudokuUI(Frame): '''Tkinter UI responsible for drawing the board and accepting user input.''' def __init__(self, parent, game): '''Initialize the game with a parent class in the tkinter module.''' self.game = game self.parent = parent # Main window of the whole program Frame.__init__(self, parent) self.row, self.column = 0, 0 self.__initUI() def __initUI(self): '''The GUI that contains all of the frontend widgets etc.''' self.parent.title('Sudoku') # Title of our window self.pack(fill = BOTH, expand = 1) self.canvas = Canvas(self, width = WIDTH, height = HEIGHT) self.canvas.pack(fill = BOTH, side = TOP) clear_button = Button(self, text = 'Clear answers', command=self.__clear_answers) clear_button.pack(fill = BOTH, side = BOTTOM) self.__draw_grid() self.__draw_puzzle() self.canvas.bind('<Button-1>', self.__cell_clicked) self.canvas.bind('<Key>', self.__key_pressed) def __draw_grid(self): '''Draws grid divided by blue lines into 3x3 subgrids.''' for i in xrange(10): color = 'blue' if i % 3 == 0 else 'gray' x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line(x0, y0, x1, y1, fill = color) x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill = color) def __draw_puzzle(self): '''Draw on the UI corresponding to the backend grid's cell number in 2D array.''' self.canvas.delete('numbers') for i in xrange(9): for j in xrange(9): answer = self.game.puzzle[i][j] if answer != 0: x = MARGIN + j * SIDE + SIDE / 2 # Column margin (x direction i.e. j) y = MARGIN + i * SIDE + SIDE / 2 # Row margin (y direction i.e. i) original = self.game.start_puzzle[i][j] color = 'black' if answer == original else 'sea green' # Pre-filled numbers are black; new user input is sea green self.canvas.create_text(x, y, text = answer, tags = 'numbers', fill = color) def __clear_answers(self): '''Delete the user's input from the backend grid and UI. Hence the duplicated puzzle 2D array.''' self.game.start() self.canvas.delete('victory') self.__draw_puzzle() def __cell_clicked(self, event): '''Respond to user mouse clicks on a particular cell/location.''' if self.game.game_over: # If the game is over, do nothing return x, y = event.x, event.y if (MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN): # Grab x and y location of click if within bounds of puzzle board self.canvas.focus_set() # Focus on area that is clicked # Get row and column numbers from (x, y) coordinates row, column = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE # If cell was selected already, deselect it. if (row, column) == (self.row, self.column): self.row, self.column = -1, -1 elif self.game.puzzle[row][column] == 0: self.row, self.column = row, column else: self.row, self.column = -1, -1 self.__draw_cursor() def __draw_cursor(self): '''Draws a highlighted rectangle around the grid cell location that the user clicked on.''' self.canvas.delete('cursor') if self.row >= 0 and self.column >= 0: # If row and column are set in focus, then compute dimensions of cell and create rectangle x0 = MARGIN + self.column * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.column + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 self.canvas.create_rectangle(x0, y0, x1, y1, outline = 'red', tags = 'cursor') def __key_pressed(self, event): '''Handle key pressed events accordingly. If integer, place integer into grid cell etc.''' if self.game.game_over: return if self.row >= 0 and self.column >= 0 and event.char in '1234567890': self.game.puzzle[self.row][self.column] = int(event.char) self.column, self.row = -1, -1 # Dereference the box self.__draw_puzzle() self.__draw_cursor() if self.game.check_win(): self.__draw_victory() def __draw_victory(self): '''If the user has completed the Sudoku puzzle successfully, draw a circle congratulating them on their victory.''' x0 = y0 = MARGIN + SIDE * 2 x1 = y1 = MARGIN + SIDE * 7 # Draw the oval self.canvas.create_oval(x0, y0, x1, y1, tags = 'victory', fill = 'dark oxrange', outline = 'oxrange') # Create the text inside the oval x = y = MARGIN + 4 * SIDE + SIDE / 2 self.canvas.create_text(x, y, text = 'You win!', tags = 'winner', fill = 'white', font = ('Arial', 32))
class SudokuUI(Frame): """ The Tkinter UI, responsible for drawing the board and accepting user input. """ def __init__(self, parent, game): self.game = game Frame.__init__(self, parent) self.parent = parent self.row, self.col = -1, -1 self.rgb= [[[] for i in range(3)] for j in range(N**2)] self.init_ui() def init_ui(self): self.parent.title("Sudoku") self.pack(fill=BOTH) self.canvas = Canvas(self, width=WIDTH, height=HEIGHT) self.canvas.pack(fill=BOTH, side=TOP) # clear_button = Button(self, # text="Clear answers", # command=self.__clear_answers) # clear_button.pack(fill=BOTH, side=BOTTOM) submit_button = Button(self, text="Submit", command=self.submit_answers) submit_button.pack(fill=X, side=BOTTOM) nonomino_button = Button(self, text="Nonomino", command=self.nonomino) nonomino_button.pack(fill=X, side=BOTTOM) self.draw_grid() self.draw_puzzle(False) self.canvas.bind("<Button-1>", self.cell_clicked) self.canvas.bind("<Key>", self.key_pressed) def draw_grid(self): """ Draws grid divided with blue lines into squares """ for i in range(N * N + 1): color = "blue" if i % N == 0 else "gray" x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line(x0, y0, x1, y1, fill=color) x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) def draw_puzzle(self, submitted): self.canvas.delete("numbers") for i in range(N * N): for j in range(N * N): answer = self.game.board[i][j] if answer != 0: x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 original = self.game.board[i][j] if submitted and 0 in [cell for row in self.game.board for cell in row]: color="red" elif nonomino>=0: color = "white" else: color="black" self.canvas.create_text( x, y, text=answer, tags="numbers", fill=color ) def draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 outline='white' if nonomino>=0 else 'red' self.canvas.create_rectangle( x0, y0, x1, y1, outline=outline, tags="cursor" ) def cell_clicked(self, event): x, y = event.x, event.y if MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN: self.canvas.focus_set() # get row and col numbers from x,y coordinates row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE row = int(row) col = int(col) self.row, self.col = row, col else: self.row, self.col = -1, -1 global nonomino x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 # if nonomino>=0 and nonomino<N**4 and self.row>=0: # square_num=nonomino/N**2 # nonomino_squares[square_num]=self.row*N**2+self.col # nonomino_cells[self.row*N**2+self.col]=square_num # if (nonomino) % N**2==0: # self.rgb[square_num][0]=random.randint(20, 230) # self.rgb[square_num][1]=random.randint(20, 230) # self.rgb[square_num][2]=random.randint(20, 230) # self.canvas.create_rectangle(x0, y0, x1, y1, fill= "#%02x%02x%02x" % (self.rgb[square_num][0], self.rgb[square_num][1], self.rgb[square_num][2])) # nonomino+=1 self.draw_cursor() def key_pressed(self, event): if event.keysym == 'Right' and self.col < N * N - 1: self.col += 1 self.draw_cursor() if event.keysym == 'Left' and self.col > 0: self.col -= 1 self.draw_cursor() if event.keysym == 'Down' and self.row < N * N - 1: self.row += 1 self.draw_cursor() if event.keysym == 'Up' and self.row > 0: self.row -= 1 self.draw_cursor() if event.keysym == 'BackSpace' or event.keysym=='Delete' and self.row>=0 and self.col>=0: self.game.board[self.row][self.col]=0 self.draw_puzzle(False) elif self.row >= 0 and self.col >= 0 and event.char.isdigit(): current_val=self.game.board[self.row][self.col] if current_val != 0: self.game.board[self.row][self.col]= int(str(current_val)+event.char) else: self.game.board[self.row][self.col]= int(event.char) self.draw_puzzle(False) self.draw_cursor() elif self.row >= 0 and self.col >= 0 and event.char.isalpha() and nonomino>=0: square_num=ord(event.char)-97 x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 if square_num>=0 and square_num<N**2: for square in nonomino_squares: if self.row*N**2+self.col in square: square.remove(self.row*N**2+self.col) nonomino_squares[square_num].append(self.row*N**2+self.col) nonomino_cells[self.row*N**2+self.col]=square_num if not self.rgb[square_num][0]: self.rgb[square_num][0]=random.randint(20, 230) self.rgb[square_num][1]=random.randint(20, 230) self.rgb[square_num][2]=random.randint(20, 230) self.canvas.create_rectangle(x0, y0, x1, y1, fill= "#%02x%02x%02x" % (self.rgb[square_num][0], self.rgb[square_num][1], self.rgb[square_num][2])) def submit_answers(self): self.game.solve() self.draw_puzzle(True) def nonomino(self): global nonomino nonomino=0
class PinceauCanvas: def __init__(self, master, width, height): self.__master = master self.__width = width self.__height = height self.__tmp = {} self.__tmp_rendered = None self.__shapes = [] # Default constants self.__draw_mode = 'add' self.__shape_mode = 'normal' self.__shape_type = 'rectangle' self.__tmp_color = '#E57373' self.__final_color = '#F44336' # Main canvas self.__canvas = Canvas(self.__master, width=self.__width, height=self.__height) self.bind_events() def set_send(self, send_func): # Method sending the drawn shape to the server. self.__send = send_func def switch_shape_mode(self): # Method enabling to switch from a straight shape to a nomrmal shape # and vice-versa. self.__shape_mode = ('straight' if self.__shape_mode == 'normal' else 'normal') def switch_draw_mode(self): # Method enabling to switch from the drawing mode to the erasing mode # and vice-versa. self.__draw_mode = ('erase' if self.__draw_mode == 'add' else 'add') def change_color(self, tmp_color, color): # Method enabling to switch from one color to another. self.__tmp_color = tmp_color self.__final_color = color def change_shape(self, shape_type): # Method enabling to switch from one shape to another. self.__shape_type = shape_type def pack(self): self.__canvas.pack() def bind_events(self): self.__canvas.bind('<KeyPress>', self.key_press) self.__canvas.bind('<KeyRelease>', self.key_press) self.__canvas.bind('<Button-1>', self.mouse_click) self.__canvas.bind('<B1-Motion>', self.mouse_drag) self.__canvas.bind('<ButtonRelease-1>', self.mouse_release) def key_press(self, event): # Method to detect if the 'CTRL' key or 'MAJ' key is pressed using the # keycodes from iOS and Windows. # Indeed, if 'CTRL' is pressed, we turn to erasing mode. # And if 'MAJ" is pressed, we turn to straight shapes mode. keycode = event.keycode if keycode == 262145 or keycode == 262401 or keycode == 17: # Ctrl key is pressed self.switch_draw_mode() if keycode == 131330 or keycode == 131074 or keycode == 131076 or keycode == 16: # Maj key is pressed self.switch_shape_mode() def mouse_click(self, event): self.__canvas.focus_set() x, y = event.x, event.y if self.__draw_mode == 'erase': action = {'action': 'erase', 'x': x, 'y': y} self.erase(action) self.__send(action) elif self.__draw_mode == 'add': self.__tmp = { 'action': 'add', 'x1': x, 'y1': y, 'x2': x, 'y2': y, 'mode': self.__shape_mode, 'type': self.__shape_type, 'fill': self.__tmp_color } def erase(self, action): # Method erasing a shape on the canvas. x, y = action['x'], action['y'] shape = self.__canvas.find_closest(x, y) if shape: self.__canvas.delete(shape) def mouse_drag(self, event): if self.__draw_mode == 'add': self.__canvas.focus_set() self.__tmp['x2'] = event.x self.__tmp['y2'] = event.y self.__tmp['mode'] = self.__shape_mode self.__tmp_rendered = self.draw(self.__tmp) def mouse_release(self, event): if self.__draw_mode == 'add': self.__tmp['fill'] = self.__final_color if self.__tmp['type'] is not None: final_shape = self.draw(self.__tmp) self.__shapes.append(final_shape) self.__send(self.__tmp) # Reset temp shape self.__tmp = {} self.__tmp_rendered = None def draw(self, shape): # Method drawing a shape on the canvas. self.__canvas.delete(self.__tmp_rendered) rendered_shape = None if shape['type'] == 'rectangle' and shape['mode'] == 'normal': rendered_shape = Rectangle(shape) elif shape['type'] == 'rectangle' and shape['mode'] == 'straight': rendered_shape = Square(shape) elif shape['type'] == 'oval' and shape['mode'] == 'normal': rendered_shape = Oval(shape) elif shape['type'] == 'oval' and shape['mode'] == 'straight': rendered_shape = Circle(shape) elif shape['type'] == 'line' and shape['mode'] == 'normal': rendered_shape = Line(shape) elif shape['type'] == 'line' and shape['mode'] == 'straight': rendered_shape = Diagonal(shape) shape_id = rendered_shape.draw_on(self.__canvas) return shape_id
class SudokuUI(Frame): """ The Tkinter UI, responsible for drawing the board and accepting user input. """ def __init__(self, parent, game): self.game = game Frame.__init__(self, parent) self.parent = parent self.row, self.col = -1, -1 # self.__popupmsg("hi") self.__initUI() def __initUI(self): self.parent.title("Super Sudoku") self.pack() self.canvas = Canvas(self, width=WIDTH, height=HEIGHT) self.canvas.pack(fill=BOTH) clear_button = Button(self, padx=50, text="Clear answers", command=self.__clear_answers) clear_button.pack(fill=BOTH, side=LEFT) undoOne = Button(self, text="Undo", command = self.undo) undoOne.pack(fill=BOTH, side=LEFT) hint_button = Button(self, text="Hint", command = self.__hint) hint_button.pack(fill=BOTH, side=LEFT) hint_better_button = Button(self, text="Better hint", command=self.__better_hint) hint_better_button.pack(fill=BOTH, side=LEFT) solve_button = Button(self, text="BEST hint", command = self.__solve) solve_button.pack(fill=BOTH, side=LEFT) self.__draw_grid() self.__draw_puzzle() self.canvas.bind("<Button-1>", self.__cell_clicked) self.canvas.bind("<Key>", self.__key_pressed) def __draw_grid(self): """ Draws grid divided with blue lines into 3x3 squares """ for i in xrange(10): color = "red" if i % 3 == 0 else "gray" x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line(x0, y0, x1, y1, fill=color) x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) def __draw_puzzle(self): self.canvas.delete("numbers") for i in xrange(9): for j in xrange(9): answer = self.game.puzzle[i][j] if answer != 0: x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 original = self.game.start_puzzle[i][j] color = "black" if answer == original else "blue" self.canvas.create_text( x, y, text=answer, tags="numbers", fill=color ) def __draw_cursor(self): # removes highlighted red region self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 self.canvas.create_rectangle( x0, y0, x1, y1, outline="red", tags="cursor" ) # def __popupmsg(self,msg): # popup = Tkinter.Tk() # popup.wm_title("!") # label = ttk.Label(popup, text=msg, font=NORM_FONT) # label.pack(side="top", fill="x", pady=10) # B1 = ttk.Button(popup, text="Okay", command = popup.destroy) # B1.pack() # popup.mainloop() def __draw_victory(self): # create a oval (which will be a circle) x0 = y0 = MARGIN + SIDE * 3.5 x1 = y1 = MARGIN + SIDE * 5.5 self.canvas.create_oval( x0, y0, x1, y1, tags="victory", fill="dark green", outline="orange" ) # create text x = y = MARGIN + 4 * SIDE + SIDE / 2 self.canvas.create_text( x, y, text="You win!", tags="victory", fill="white", font=("Arial", 24) ) def __draw_mistake(self): # create a oval (which will be a circle) x0 = y0 = MARGIN + SIDE * 3.5 x1 = y1 = MARGIN + SIDE * 5.5 self.canvas.create_oval( x0, y0, x1, y1, tags="mistake", fill="dark orange", outline="orange" ) # create text x = y = MARGIN + 4 * SIDE + SIDE / 2 self.canvas.create_text( x, y, text="That's a mistake!!", tags="mistake", fill="white", font=("Arial", 12) ) def __cell_clicked(self, event): if self.game.game_over: return x, y = event.x, event.y if (MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN): self.canvas.focus_set() # get row and col numbers from x,y coordinates row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE # if cell was selected already - deselect it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 elif self.game.puzzle[row][col] == 0: self.row, self.col = row, col else: self.row, self.col = -1, -1 self.__draw_cursor() def __key_pressed(self, event): # print(int(event.char)) if event.char == u'\uf700': self.row -= 1 self.__draw_cursor() if event.char == u'\uf701': self.row += 1 self.__draw_cursor() if event.char == u'\uf702': self.col -= 1 self.__draw_cursor() if event.char == u'\uf703': self.col += 1 self.__draw_cursor() if event.char == '\x7f': self.game.puzzle[self.row][self.col] = 0 self.__draw_puzzle() if self.game.game_over: return if self.row >= 0 and self.col >= 0 and event.char in "123456789": self.game.puzzle[self.row][self.col] = int(event.char) if sud.checkMistake(self.game.puzzle) == True: self.game.lastLoc = [self.row,self.col] self.__draw_puzzle() self.__draw_mistake() # self.__draw_cursor() # sleep(2) # self.canvas.delete("mistake") else:# print("MISTAKE") self.__draw_puzzle() self.__draw_cursor() if self.game.check_win(): self.col, self.row = -10,-10 self.__draw_cursor() self.__draw_victory() def undo(self): if(len(self.game.lastLoc) > 0): row,col = self.game.lastLoc[0],self.game.lastLoc[1] self.game.puzzle[row][col] = 0 self.canvas.delete("mistake") self.__draw_puzzle() def __clear_answers(self): self.game.start() self.canvas.delete("victory") self.canvas.delete("mistake") self.__draw_puzzle() def __solve(self): # print("we need to figure this out") # self.game.puzzle = sud.sudoku_solver(self.game.puzzle) sol = sud.sudoku_solver(self.game.puzzle) if sol != None: self.game.puzzle = sol self.__draw_puzzle() if self.game.check_win(): self.__draw_victory() def __hint(self): sol = sud.one_step(self.game.puzzle) if sol != None: self.game.puzzle = sol self.__draw_puzzle() if self.game.check_win(): self.__draw_victory() # print(hint) def __better_hint(self): sol = sud.get_best_hint(self.game.puzzle) if sol == None: return self.game.puzzle[sol[1]][sol[2]] = sol[0] self.__draw_puzzle() if self.game.check_win(): self.__draw_victory()
class PiPresents(object): def __init__(self): gc.set_debug(gc.DEBUG_UNCOLLECTABLE|gc.DEBUG_INSTANCES|gc.DEBUG_OBJECTS|gc.DEBUG_SAVEALL) self.pipresents_issue="1.3" self.pipresents_minorissue = '1.3.1g' # position and size of window without -f command line option self.nonfull_window_width = 0.45 # proportion of width self.nonfull_window_height= 0.7 # proportion of height self.nonfull_window_x = 0 # position of top left corner self.nonfull_window_y=0 # position of top left corner self.pp_background='black' StopWatch.global_enable=False # set up the handler for SIGTERM signal.signal(signal.SIGTERM,self.handle_sigterm) # **************************************** # Initialisation # *************************************** # get command line options self.options=command_options() # get Pi Presents code directory pp_dir=sys.path[0] self.pp_dir=pp_dir if not os.path.exists(pp_dir+"/pipresents.py"): if self.options['manager'] is False: tkMessageBox.showwarning("Pi Presents","Bad Application Directory:\n{0}".format(pp_dir)) exit(103) # Initialise logging and tracing Monitor.log_path=pp_dir self.mon=Monitor() # Init in PiPresents only self.mon.init() # uncomment to enable control of logging from within a class # Monitor.enable_in_code = True # enables control of log level in the code for a class - self.mon.set_log_level() # make a shorter list to log/trace only some classes without using enable_in_code. Monitor.classes = ['PiPresents', 'pp_paths', 'HyperlinkShow','RadioButtonShow','ArtLiveShow','ArtMediaShow','MediaShow','LiveShow','MenuShow', 'PathManager','ControlsManager','ShowManager','PluginManager', 'MplayerDriver','OMXDriver','UZBLDriver', 'KbdDriver','GPIODriver','TimeOfDay','ScreenDriver','Animate','OSCDriver' ] # Monitor.classes=['PiPresents','ArtMediaShow','VideoPlayer','OMXDriver'] # get global log level from command line Monitor.log_level = int(self.options['debug']) Monitor.manager = self.options['manager'] # print self.options['manager'] self.mon.newline(3) self.mon.log (self, "Pi Presents is starting, Version:"+self.pipresents_minorissue) # self.mon.log (self," OS and separator:" + os.name +' ' + os.sep) self.mon.log(self,"sys.path[0] - location of code: "+sys.path[0]) if os.geteuid() !=0: user=os.getenv('USER') else: user = os.getenv('SUDO_USER') self.mon.log(self,'User is: '+ user) # self.mon.log(self,"os.getenv('HOME') - user home directory (not used): " + os.getenv('HOME')) # does not work # self.mon.log(self,"os.path.expanduser('~') - user home directory: " + os.path.expanduser('~')) # does not work # optional other classes used self.root=None self.ppio=None self.tod=None self.animate=None self.gpiodriver=None self.oscdriver=None self.osc_enabled=False self.gpio_enabled=False self.tod_enabled=False # get home path from -o option self.pp_home = pp_paths.get_home(self.options['home']) if self.pp_home is None: self.end('error','Failed to find pp_home') # get profile path from -p option # pp_profile is the full path to the directory that contains # pp_showlist.json and other files for the profile self.pp_profile = pp_paths.get_profile_dir(self.pp_home, self.options['profile']) if self.pp_profile is None: self.end('error','Failed to find profile') # check profile exists if os.path.exists(self.pp_profile): self.mon.log(self,"Found Requested profile - pp_profile directory is: " + self.pp_profile) else: self.mon.err(self,"Failed to find requested profile: "+ self.pp_profile) self.end('error','Failed to find profile') self.mon.start_stats(self.options['profile']) # check 'verify' option if self.options['verify'] is True: val =Validator() if val.validate_profile(None,pp_dir,self.pp_home,self.pp_profile,self.pipresents_issue,False) is False: self.mon.err(self,"Validation Failed") self.end('error','Validation Failed') # initialise and read the showlist in the profile self.showlist=ShowList() self.showlist_file= self.pp_profile+ "/pp_showlist.json" if os.path.exists(self.showlist_file): self.showlist.open_json(self.showlist_file) else: self.mon.err(self,"showlist not found at "+self.showlist_file) self.end('error','showlist not found') # check profile and Pi Presents issues are compatible if float(self.showlist.sissue()) != float(self.pipresents_issue): self.mon.err(self,"Version of profile " + self.showlist.sissue() + " is not same as Pi Presents, must exit") self.end('error','wrong version of profile') # get the 'start' show from the showlist index = self.showlist.index_of_show('start') if index >=0: self.showlist.select(index) self.starter_show=self.showlist.selected_show() else: self.mon.err(self,"Show [start] not found in showlist") self.end('error','start show not found') if self.starter_show['start-show']=='': self.mon.warn(self,"No Start Shows in Start Show") # ******************** # SET UP THE GUI # ******************** # turn off the screenblanking and saver if self.options['noblank'] is True: call(["xset","s", "off"]) call(["xset","s", "-dpms"]) self.root=Tk() self.title='Pi Presents - '+ self.pp_profile self.icon_text= 'Pi Presents' self.root.title(self.title) self.root.iconname(self.icon_text) self.root.config(bg=self.pp_background) self.mon.log(self, 'native screen dimensions are ' + str(self.root.winfo_screenwidth()) + ' x ' + str(self.root.winfo_screenheight()) + ' pixcels') if self.options['screensize'] =='': self.screen_width = self.root.winfo_screenwidth() self.screen_height = self.root.winfo_screenheight() else: reason,message,self.screen_width,self.screen_height=self.parse_screen(self.options['screensize']) if reason =='error': self.mon.err(self,message) self.end('error',message) self.mon.log(self, 'commanded screen dimensions are ' + str(self.screen_width) + ' x ' + str(self.screen_height) + ' pixcels') # set window dimensions and decorations if self.options['fullscreen'] is False: self.window_width=int(self.root.winfo_screenwidth()*self.nonfull_window_width) self.window_height=int(self.root.winfo_screenheight()*self.nonfull_window_height) self.window_x=self.nonfull_window_x self.window_y=self.nonfull_window_y self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y)) else: self.window_width=self.screen_width self.window_height=self.screen_height self.root.attributes('-fullscreen', True) os.system('unclutter 1>&- 2>&- &') # Suppress 'someone created a subwindow' complaints from unclutter self.window_x=0 self.window_y=0 self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y)) self.root.attributes('-zoomed','1') # canvs cover the whole screen whatever the size of the window. self.canvas_height=self.screen_height self.canvas_width=self.screen_width # make sure focus is set. self.root.focus_set() # define response to main window closing. self.root.protocol ("WM_DELETE_WINDOW", self.handle_user_abort) # setup a canvas onto which will be drawn the images or text self.canvas = Canvas(self.root, bg=self.pp_background) if self.options['fullscreen'] is True: self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=0) else: self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=1, highlightcolor='yellow') self.canvas.place(x=0,y=0) # self.canvas.config(bg='black') self.canvas.focus_set() # **************************************** # INITIALISE THE INPUT DRIVERS # **************************************** # each driver takes a set of inputs, binds them to symboic names # and sets up a callback which returns the symbolic name when an input event occurs/ # use keyboard driver to bind keys to symbolic names and to set up callback kbd=KbdDriver() if kbd.read(pp_dir,self.pp_home,self.pp_profile) is False: self.end('error','cannot find or error in keys.cfg') kbd.bind_keys(self.root,self.handle_input_event) self.sr=ScreenDriver() # read the screen click area config file reason,message = self.sr.read(pp_dir,self.pp_home,self.pp_profile) if reason == 'error': self.end('error','cannot find screen.cfg') # create click areas on the canvas, must be polygon as outline rectangles are not filled as far as find_closest goes # click areas are made on the Pi Presents canvas not the show canvases. reason,message = self.sr.make_click_areas(self.canvas,self.handle_input_event) if reason == 'error': self.mon.err(self,message) self.end('error',message) # **************************************** # INITIALISE THE APPLICATION AND START # **************************************** self.shutdown_required=False self.exitpipresents_required=False # kick off GPIO if enabled by command line option self.gpio_enabled=False if os.path.exists(self.pp_profile + os.sep + 'pp_io_config'+os.sep+ 'gpio.cfg'): # initialise the GPIO self.gpiodriver=GPIODriver() reason,message=self.gpiodriver.init(pp_dir,self.pp_home,self.pp_profile,self.canvas,50,self.handle_input_event) if reason == 'error': self.end('error',message) else: self.gpio_enabled=True # and start polling gpio self.gpiodriver.poll() # kick off animation sequencer self.animate = Animate() self.animate.init(pp_dir,self.pp_home,self.pp_profile,self.canvas,200,self.handle_output_event) self.animate.poll() #create a showmanager ready for time of day scheduler and osc server show_id=-1 self.show_manager=ShowManager(show_id,self.showlist,self.starter_show,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home) # first time through set callback to terminate Pi Presents if all shows have ended. self.show_manager.init(self.canvas,self.all_shows_ended_callback,self.handle_command,self.showlist) # Register all the shows in the showlist reason,message=self.show_manager.register_shows() if reason == 'error': self.mon.err(self,message) self.end('error',message) # Init OSCDriver, read config and start OSC server self.osc_enabled=False if os.path.exists(self.pp_profile + os.sep + 'pp_io_config'+ os.sep + 'osc.cfg'): self.oscdriver=OSCDriver() reason,message=self.oscdriver.init(self.pp_profile,self.handle_command,self.handle_input_event,self.e_osc_handle_output_event) if reason == 'error': self.end('error',message) else: self.osc_enabled=True self.root.after(1000,self.oscdriver.start_server()) # and run the start shows self.run_start_shows() # set up the time of day scheduler including catchup self.tod_enabled=False if os.path.exists(self.pp_profile + os.sep + 'schedule.json'): # kick off the time of day scheduler which may run additional shows self.tod=TimeOfDay() self.tod.init(pp_dir,self.pp_home,self.pp_profile,self.root,self.handle_command) self.tod_enabled = True # then start the time of day scheduler if self.tod_enabled is True: self.tod.poll() # start Tkinters event loop self.root.mainloop( ) def parse_screen(self,size_text): fields=size_text.split('*') if len(fields)!=2: return 'error','do not understand --fullscreen comand option',0,0 elif fields[0].isdigit() is False or fields[1].isdigit() is False: return 'error','dimensions are not positive integers in ---fullscreen',0,0 else: return 'normal','',int(fields[0]),int(fields[1]) # ********************* # RUN START SHOWS # ******************** def run_start_shows(self): self.mon.trace(self,'run start shows') # parse the start shows field and start the initial shows show_refs=self.starter_show['start-show'].split() for show_ref in show_refs: reason,message=self.show_manager.control_a_show(show_ref,'open') if reason == 'error': self.mon.err(self,message) # ********************* # User inputs # ******************** # handles one command provided as a line of text def handle_command(self,command_text): self.mon.log(self,"command received: " + command_text) if command_text.strip()=="": return if command_text[0]=='/': if self.osc_enabled is True: self.oscdriver.send_command(command_text) return fields= command_text.split() show_command=fields[0] if len(fields)>1: show_ref=fields[1] else: show_ref='' if show_command in ('open','close'): if self.shutdown_required is False: reason,message=self.show_manager.control_a_show(show_ref,show_command) else: return elif show_command == 'exitpipresents': self.exitpipresents_required=True if self.show_manager.all_shows_exited() is True: # need root.after to get out of st thread self.root.after(1,self.e_all_shows_ended_callback) return else: reason,message= self.show_manager.exit_all_shows() elif show_command == 'shutdownnow': # need root.after to get out of st thread self.root.after(1,self.e_shutdown_pressed) return else: reason='error' message = 'command not recognised: '+ show_command if reason=='error': self.mon.err(self,message) return def e_all_shows_ended_callback(self): self.all_shows_ended_callback('normal','no shows running') def e_shutdown_pressed(self): self.shutdown_pressed('now') def e_osc_handle_output_event(self,line): #jump out of server thread self.root.after(1, lambda arg=line: self.osc_handle_output_event(arg)) def osc_handle_output_event(self,line): self.mon.log(self,"output event received: "+ line) #osc sends output events as a string reason,message,delay,name,param_type,param_values=self.animate.parse_animate_fields(line) if reason == 'error': self.mon.err(self,message) self.end(reason,message) self.handle_output_event(name,param_type,param_values,0) def handle_output_event(self,symbol,param_type,param_values,req_time): if self.gpio_enabled is True: reason,message=self.gpiodriver.handle_output_event(symbol,param_type,param_values,req_time) if reason =='error': self.mon.err(self,message) self.end(reason,message) else: self.mon.warn(self,'GPIO not enabled') # all input events call this callback with a symbolic name. # handle events that affect PP overall, otherwise pass to all active shows def handle_input_event(self,symbol,source): self.mon.log(self,"event received: "+symbol + ' from '+ source) if symbol == 'pp-terminate': self.handle_user_abort() elif symbol == 'pp-shutdown': self.shutdown_pressed('delay') elif symbol == 'pp-shutdownnow': # need root.after to grt out of st thread self.root.after(1,self.e_shutdown_pressed) return elif symbol == 'pp-exitpipresents': self.exitpipresents_required=True if self.show_manager.all_shows_exited() is True: # need root.after to grt out of st thread self.root.after(1,self.e_all_shows_ended_callback) return reason,message= self.show_manager.exit_all_shows() else: # events for shows affect the show and could cause it to exit. for show in self.show_manager.shows: show_obj=show[ShowManager.SHOW_OBJ] if show_obj is not None: show_obj.handle_input_event(symbol) def shutdown_pressed(self, when): if when == 'delay': self.root.after(5000,self.on_shutdown_delay) else: self.shutdown_required=True if self.show_manager.all_shows_exited() is True: self.all_shows_ended_callback('normal','no shows running') else: # calls exit method of all shows, results in all_shows_closed_callback self.show_manager.exit_all_shows() def on_shutdown_delay(self): # 5 second delay is up, if shutdown button still pressed then shutdown if self.gpiodriver.shutdown_pressed() is True: self.shutdown_required=True if self.show_manager.all_shows_exited() is True: self.all_shows_ended_callback('normal','no shows running') else: # calls exit method of all shows, results in all_shows_closed_callback self.show_manager.exit_all_shows() def handle_sigterm(self,signum,frame): self.mon.log(self,'SIGTERM received - '+ str(signum)) self.terminate() def handle_user_abort(self): self.mon.log(self,'User abort received') self.terminate() def terminate(self): self.mon.log(self, "terminate received") needs_termination=False for show in self.show_manager.shows: # print show[ShowManager.SHOW_OBJ], show[ShowManager.SHOW_REF] if show[ShowManager.SHOW_OBJ] is not None: needs_termination=True self.mon.log(self,"Sent terminate to show "+ show[ShowManager.SHOW_REF]) # call shows terminate method # eventually the show will exit and after all shows have exited all_shows_callback will be executed. show[ShowManager.SHOW_OBJ].terminate() if needs_termination is False: self.end('killed','killed - no termination of shows required') # ****************************** # Ending Pi Presents after all the showers and players are closed # ************************** # callback from ShowManager when all shows have ended def all_shows_ended_callback(self,reason,message): self.canvas.config(bg=self.pp_background) if reason in ('killed','error') or self.shutdown_required is True or self.exitpipresents_required is True: self.end(reason,message) def end(self,reason,message): self.mon.log(self,"Pi Presents ending with reason: " + reason) if self.root is not None: self.root.destroy() self.tidy_up() # gc.collect() # print gc.garbage if reason == 'killed': self.mon.log(self, "Pi Presents Aborted, au revoir") # close logging files self.mon.finish() sys.exit(101) elif reason == 'error': self.mon.log(self, "Pi Presents closing because of error, sorry") # close logging files self.mon.finish() sys.exit(102) else: self.mon.log(self,"Pi Presents exiting normally, bye") # close logging files self.mon.finish() if self.shutdown_required is True: # print 'SHUTDOWN' call(['sudo', 'shutdown', '-h', '-t 5','now']) sys.exit(100) else: sys.exit(100) # tidy up all the peripheral bits of Pi Presents def tidy_up(self): self.mon.log(self, "Tidying Up") # turn screen blanking back on if self.options['noblank'] is True: call(["xset","s", "on"]) call(["xset","s", "+dpms"]) # tidy up animation and gpio if self.animate is not None: self.animate.terminate() if self.gpio_enabled==True: self.gpiodriver.terminate() if self.osc_enabled is True: self.oscdriver.terminate() # tidy up time of day scheduler if self.tod_enabled is True: self.tod.terminate()
class SudokuUI(Frame): """ The Tkinter UI, responsible for drawing the board and accepting user input. """ def __init__(self, parent, game, main_ui): self.game = game self.main_ui = main_ui Frame.__init__(self, parent) self.parent = parent self.boardFont = tkFont.Font(family="Helvetica", size=20) self.row, self.col = -1, -1 self.__initUI() def __initUI(self): """ Initialises the Sudoku board """ self.pack(fill=BOTH) self.canvas = Canvas(self, width=WIDTH, height=HEIGHT, highlightthickness=0) self.canvas.pack(fill=BOTH, side=TOP) self.__draw_grid() self.draw_puzzle() self.canvas.bind("<Button-1>", self.__cell_clicked) self.canvas.bind("<Key>", self.__key_pressed) def __draw_grid(self): """ Draws grid divided with blue lines into 3x3 squares """ for i in xrange(10): color = "black" if i % 3 == 0 else "gray" x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line(x0, y0, x1, y1, fill=color) x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) def draw_puzzle(self): """ Deletes all the numbers and draws them from scratch """ self.canvas.delete("numbers") for i in xrange(9): for j in xrange(9): answer = self.game.puzzle[i][j] can_edit = self.game.heatmap[i][j] == 2 x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 if answer != 0: coords = str(i) + " " + str(j) color = "sea green" if can_edit else "black" self.canvas.create_text(x, y, text=answer, tags=["numbers", coords], fill=color, font=self.boardFont) def draw_update(self, i, j, number): tag = str(i) + " " + str(j) #self.canvas.delete(tag) x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 if number != 0: self.canvas.create_text(x, y, text=number, tags=["numbers", tag], fill="sea green", font=self.boardFont) def __draw_cursor(self): """ Hides the highlighted border of a the selected square """ self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 self.canvas.create_rectangle(x0, y0, x1, y1, outline="DeepSkyBlue2", tags="cursor") def draw_victory(self, content): """ Draws the circle containing the result of the game """ x0 = y0 = MARGIN + SIDE * 2 x1 = y1 = MARGIN + SIDE * 7 self.canvas.create_oval(x0, y0, x1, y1, tags="victory", fill="dark orange", outline="orange") # create text x = y = MARGIN + 4 * SIDE + SIDE / 2 self.canvas.create_text(x, y, text=content, tags="victory", fill="white", font=("Arial", 12)) def __cell_clicked(self, event): """ Handles cell clicking """ if self.game.game_over: return x, y = event.x, event.y if MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN: self.canvas.focus_set() # get row and col numbers from x,y coordinates row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE # if cell was selected already - deselect it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 elif self.game.heatmap[row][col] == 2: self.row, self.col = row, col else: self.row, self.col = -1, -1 self.__draw_cursor() def __key_pressed(self, event): """ Handles key-presses """ if self.game.game_over: return if self.row >= 0 and self.col >= 0 and event.char in "1234567890" and event.char != "": self.main_ui.insert_number(self.row, self.col, int(event.char)) def __clear_answers(self): self.game.start() self.canvas.delete("victory") self.__draw_puzzle()
class ScrolledCanvas: def __init__(self, master, **opts): if not opts.has_key('yscrollincrement'): opts['yscrollincrement'] = 20 self.master = master self.frame = TFrame(master, style="FlatFrame") self.frame.rowconfigure(0, weight=1) self.frame.columnconfigure(0, weight=1) self.canvas = Canvas(self.frame, **opts) self.canvas.grid(row=0, column=0, sticky="nsew") self.vbar = TScrollbar(self.frame, name="vbar") self.vbar.grid(row=0, column=1, sticky="nse") self.hbar = TScrollbar(self.frame, name="hbar", orient="horizontal") self.hbar.grid(row=1, column=0, sticky="ews") self.canvas['yscrollcommand'] = lambda f, l: self.scroll_sh( self.vbar, f, l) self.vbar['command'] = self.canvas.yview self.canvas['xscrollcommand'] = lambda f, l: self.scroll_sh( self.hbar, f, l) self.hbar['command'] = self.canvas.xview self.canvas.bind("<Key-Prior>", self.page_up) self.canvas.bind("<Key-Next>", self.page_down) self.canvas.bind("<Key-Up>", self.unit_up) self.canvas.bind("<Key-Down>", self.unit_down) self.canvas.bind("<Alt-Key-2>", self.zoom_height) self.canvas.bind("<Button-4>", self.unit_up) self.canvas.bind("<Button-5>", self.unit_down) self.canvas.focus_set() def page_up(self, event): self.canvas.yview_scroll(-1, "page") return "break" def page_down(self, event): self.canvas.yview_scroll(1, "page") return "break" def unit_up(self, event): first, last = self.vbar.get() if first <= 0 and last >= 1: return "break" self.canvas.yview_scroll(-1, "unit") return "break" def unit_down(self, event): first, last = self.vbar.get() if first <= 0 and last >= 1: return "break" self.canvas.yview_scroll(1, "unit") return "break" def zoom_height(self, event): return "break" def scroll_sh(self, scroll, first, last): first, last = float(first), float(last) if first <= 0 and last >= 1: scroll.grid_remove() else: scroll.grid() scroll.set(first, last)
class SudokuUI(Frame): def __init__(self, parent, game): self.game = game self.parent = parent Frame.__init__(self, parent) self.row, self.col = 0, 0 self.__initUI() def __initUI(self): self.parent.title("Sudoku") self.pack(fill=BOTH, expand=1) self.canvas = Canvas(self, width=WIDTH, height=HEIGHT) self.canvas.pack(fill=BOTH, side=TOP) clear_button = Button(self, text="Clear answers", command=self.__clear_answers) clear_button.pack(fill=BOTH, side=BOTTOM) self.__draw_grid() self.__draw_puzzle() self.canvas.bind("<Button-1>", self.__cell_clicked) self.canvas.bind("<Key>", self.__key_pressed) def __draw_grid(self): for i in xrange(10): color = "blue" if i % 3 == 0 else "gray" x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line(x0, y0, x1, y1, fill=color) x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) def __draw_puzzle(self): self.canvas.delete("numbers") for i in xrange(9): for j in xrange(9): answer = self.game.puzzle[i][j] if answer != 0: x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 original = self.game.start_puzzle[i][j] color = "black" if answer == original else "sea green" self.canvas.create_text(x, y, text=answer, tags="numbers", fill=color) def __clear_answers(self): self.game.start() self.canvas.delete("victory") self.__draw_puzzle() def __cell_clicked(self, event): if self.game.game_over: return x, y = event.x, event.y if (MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN): self.canvas.focus_set() row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 elif self.game.puzzle[row][col] == 0 or self.game.puzzle[row][col] != self.game.start_puzzle[row][col]: self.row, self.col = row, col else: self.row, self.col = -1, -1 self.__draw_cursor() def __draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 self.canvas.create_rectangle(x0, y0, x1, y1, outline="red", tags="cursor") def __key_pressed(self, event): if self.game.game_over: return if self.row >= 0 and self.col >= 0 and event.char in "1234567890": self.game.puzzle[self.row][self.col] = int(event.char) self.col, self.row = -1, -1 self.__draw_puzzle() self.__draw_cursor() if self.game.check_win(): self.__draw_victory() def __draw_victory(self): x0 = y0 = MARGIN + SIDE * 2 x1 = y1 = MARGIN + SIDE * 7 self.canvas.create_oval(x0, y0, x1, y1, tags="victory", fill="dark orange", outline="orange") x = y = MARGIN + 4 * SIDE + SIDE / 2 self.canvas.create_text(x, y, text="You win!", tags="winner", fill="white", font=("Arial", 32))
class SudokuUI(Frame): """ The Tkinter UI, responsible for drawing the board and accepting user input. """ def __init__(self, parent, game): self.game = game self.parent = parent Frame.__init__(self, parent) self.row, self.col = 0, 0 self.__initUI() def __initUI(self): # set the parent title which is our main/only window self.parent.title("Sudoku") # Frame attribute that organizes the frames geometry relative to the parent, this is filling the entire frame, fill=BOTH mean fill both horizontally and vertically, other options include NONE, X or Y self.pack(fill=BOTH, expand=1) # canvas is a general pupose widget we use to display the board self.canvas = Canvas(self, width=WIDTH, height=HEIGHT) # setting pack, where the puzzle will fill the space and be pulled to the top part of the window. self.canvas.pack(fill=BOTH, side=TOP) # below the canvas, is the button to clear answers. We create button with Button, giving it text and the command for the button to call when it's pressed. here we set the command to __clear_answers. # Like canvas we will set pack for the butoon to fill the space and have the button sit at the bottom of the window clear_button = Button(self, text="Clear answers", command=self.__clear_answers) clear_button.pack(fill=BOTH, side=BOTTOM) # Two helper methods ... self.__draw_grid() self.__draw_puzzle() # Binding left mouse button click a callback, the bind method will pass the x y location of the cursor, whcih in __cell_clicked we will turn into actual cells of the puzzle. self.canvas.bind("<Button-1>", self.__cell_clicked) # Binding <Key> to the callback function __key_pressed. This binds the key a user pressed (e.g. the guessed number) to the __key_pressed method. self.canvas.bind("<Key>", self.__key_pressed) def __draw_grid(self): """ Draws grid divided with blue lines into 3x3 squares """ for i in xrange(10): color = "blue" if i % 3 == 0 else "gray" # create the vertical lines x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line(x0, y0, x1, y1, fill=color) # create the horizontal lines x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) def __draw_puzzle(self): self.canvas.delete("numbers") for i in xrange(9): for j in xrange(9): answer = self.game.puzzle[i][j] if answer != 0: x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 original = self.game.start_puzzle[i][j] color = "black" if answer == original else "sea green" self.canvas.create_text(x, y, text=answer, tags="numbers", fill=color) def __clear_answers(self): # reset puzzle to original state self.game.start() # delete the previous victory status/tag self.canvas.delete("victory") # re-draw the puzzle with the original puzzlepyth self.__draw_puzzle() # callback for button-1 def __cell_clicked(self, event): if self.game.game_over: return x, y = event.x, event.y if (MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN): self.canvas.focus_set() # get row and col numbers from x,y coordinates row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE # if cell was selected already - deselect it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 elif self.game.puzzle[row][col] == 0: self.row, self.col = row, col self.__draw_cursor() def __draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 self.canvas.create_rectangle(x0, y0, x1, y1, outline="red", tags="cursor") def __key_pressed(self, event): if self.game.game_over: return if self.row >= 0 and self.col >= 0 and event.char in "1234567890": self.game.puzzle[self.row][self.col] = int(event.char) self.col, self.row = -1, -1 self.__draw_puzzle() self.__draw_cursor() if self.game.check_win(): self.__draw_victory() def __draw_victory(self): # create an ovl (which will actually be a circle) x0 = y0 = MARGIN + SIDE * 2 x1 = y1 = MARGIN + SIDE * 7 self.canvas.create_oval(x0, y0, x1, y1, tags="victory", fill="dark orange", outline="orange") # create text x = y = MARGIN + 4 * SIDE + SIDE / 2 self.canvas.create_text(x, y, text="You Win!", tags="winner", fill="white", font=("Arial", 32))
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)
class SudokuUI(Frame): # Frame is a rectangular region on a screen """ The Tkinter UI, responsible for drawing the board and accepting user input """ def __init__(self, parent, game): self.game = game Frame.__init__(self, parent) self.parent = parent # all widgets belong to a parent self.row, self.col = -1, -1 # initialize row and col to use later self.__initUI() # calls the initUI function def __initUI(self): self.parent.title("Sudoku") # our parent window will be called Sudoku self.pack( fill=BOTH, expand=1 ) # Frame attribute, organizes geometry relative to parent, fill options: both, none, x, y self.canvas = Canvas(self, width=WIDTH, height=HEIGHT) self.canvas.pack(fill=BOTH, side=TOP) # canvas attribute, helps display our board clear_button = Button(self, text="Clear answers", command=self.__clear_answers) clear_button.pack( fill=BOTH, side=BOTTOM) # Clear button is at the bottom and fills the space self.__draw_grid() # helper functions self.__draw_puzzle() self.canvas.bind( "<Button-1>", self.__cell_clicked ) # binds Button-1 to a callback, another method: cell_clicked # in Tkinter, Button-1 is a default left mouse click self.canvas.bind( "<Key>", self.__key_pressed ) # binds the key (guesed number) to the key presseed method def __draw_grid( self ): # make the sudoku layout, do all private functions take self and then other potential arguments? """ Draws grid divided with blue lines into 3x3 squares """ for i in xrange(10): color = "blue" if i % 3 == 0 else "gray" # blue lines if divisible by 3 # draw vertical lines x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line( x0, y0, x1, y1, fill=color ) # draw the vertical lines coordinates are (x0, y0) (x1, y1) # draw horizontal lines x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) def __draw_puzzle(self): self.canvas.delete("numbers") # delete old numbers? for i in xrange(9): for j in xrange(9): answer = self.game.puzzle[i][j] if answer != 0: x = MARGIN + j * SIDE + SIDE / 2 # in the middle of the applicable cell y = MARGIN + i * SIDE + SIDE / 2 original = self.game.start_puzzle[i][j] color = "black" if answer == original else "sea green" self.canvas.create_text(x, y, text=answer, tags="numbers", fill=color) def __draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: # you set these variables as 0 first in init x0 = MARGIN + self.col * SIDE + 1 # what does -1 do to these variables? y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 self.canvas.create_rectangle(x0, y0, x1, y1, outline="red", tags="cursor") def __draw_victory(self): # creates an oval/circle x0 = y0 = MARGIN + SIDE * 2 # upper left box of circle starts margin + 2 rows in x1 = y1 = MARGIN + SIDE * 7 self.canvas.create_oval(x0, y0, x1, y1, tags="victory", fill="dark orange", outline="orange") # create text x = y = MARGIN + 4 * SIDE + SIDE / 2 # middle of the circle self.canvas.create_text(x, y, text="You win!", tags="victory", fill="white", font=("Arial", 32)) def __cell_clicked( self, event ): # event parameter: gives us x&y coordinates of where user clicked if self.game.game_over: return # do nothing if game is over x, y = event.x, event.y if (MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN): # if our puzzle grid is clicked self.canvas.focus_set() # focus_set: move focus to a widget # get row and col numbers from x, y coordinates row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE # if cell was selected already, another click should de-select it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 # I assume -1 means de-selecting? elif self.game.puzzle[row][ col] == 0: # otherwise, grab corresponding cell self.row, self.col = row, col else: self.row, self.col = -1, -1 self.__draw_cursor() def __key_pressed(self, event): if self.game.game_over: return if self.row >= 0 and self.col >= 0 and event.char in "1234567890": # where does event.char come from? tkinter? self.game.puzzle[self.row][self.col] = int(event.char) self.col, self.row = -1, -1 self.__draw_puzzle() self.__draw_cursor() if self.game.check_win( ): # every time you enter in a number, the game checks to see if you have won self.__draw_victory() def __clear_answers(self): self.game.start() self.canvas.delete("victory") # remove the victory circle self.__draw_puzzle()
class PiPresents(object): def pipresents_version(self): vitems=self.pipresents_issue.split('.') if len(vitems)==2: # cope with 2 digit version numbers before 1.3.2 return 1000*int(vitems[0])+100*int(vitems[1]) else: return 1000*int(vitems[0])+100*int(vitems[1])+int(vitems[2]) def __init__(self): gc.set_debug(gc.DEBUG_UNCOLLECTABLE|gc.DEBUG_INSTANCES|gc.DEBUG_OBJECTS|gc.DEBUG_SAVEALL) self.pipresents_issue="1.3.2" self.pipresents_minorissue = '1.3.2a' # position and size of window without -f command line option self.nonfull_window_width = 0.45 # proportion of width self.nonfull_window_height= 0.7 # proportion of height self.nonfull_window_x = 0 # position of top left corner self.nonfull_window_y=0 # position of top left corner StopWatch.global_enable=False # set up the handler for SIGTERM signal.signal(signal.SIGTERM,self.handle_sigterm) # **************************************** # Initialisation # *************************************** # get command line options self.options=command_options() # get Pi Presents code directory pp_dir=sys.path[0] self.pp_dir=pp_dir if not os.path.exists(pp_dir+"/pipresents.py"): if self.options['manager'] is False: tkMessageBox.showwarning("Pi Presents","Bad Application Directory") exit(102) # Initialise logging and tracing Monitor.log_path=pp_dir self.mon=Monitor() # Init in PiPresents only self.mon.init() # uncomment to enable control of logging from within a class # Monitor.enable_in_code = True # enables control of log level in the code for a class - self.mon.set_log_level() # make a shorter list to log/trace only some classes without using enable_in_code. Monitor.classes = ['PiPresents', 'HyperlinkShow','RadioButtonShow','ArtLiveShow','ArtMediaShow','MediaShow','LiveShow','MenuShow', 'GapShow','Show','ArtShow', 'AudioPlayer','BrowserPlayer','ImagePlayer','MenuPlayer','MessagePlayer','VideoPlayer','Player', 'MediaList','LiveList','ShowList', 'PathManager','ControlsManager','ShowManager','PluginManager', 'MplayerDriver','OMXDriver','UZBLDriver', 'KbdDriver','GPIODriver','TimeOfDay','ScreenDriver','Animate','OSCDriver', 'Network','Mailer' ] # Monitor.classes=['PiPresents','MediaShow','GapShow','Show','VideoPlayer','Player','OMXDriver'] # get global log level from command line Monitor.log_level = int(self.options['debug']) Monitor.manager = self.options['manager'] # print self.options['manager'] self.mon.newline(3) self.mon.sched (self, "Pi Presents is starting, Version:"+self.pipresents_minorissue + ' at '+time.strftime("%Y-%m-%d %H:%M.%S")) self.mon.log (self, "Pi Presents is starting, Version:"+self.pipresents_minorissue+ ' at '+time.strftime("%Y-%m-%d %H:%M.%S")) # self.mon.log (self," OS and separator:" + os.name +' ' + os.sep) self.mon.log(self,"sys.path[0] - location of code: "+sys.path[0]) # log versions of Raspbian and omxplayer, and GPU Memory with open("/boot/issue.txt") as file: self.mon.log(self,'\nRaspbian: '+file.read()) self.mon.log(self,'\n'+check_output(["omxplayer", "-v"])) self.mon.log(self,'\nGPU Memory: '+check_output(["vcgencmd", "get_mem", "gpu"])) if "DESKTOP_SESSION" not in os.environ: print 'Pi Presents must be run from the Desktop' self.mon.log(self,'Pi Presents must be run from the Desktop') self.mon.finish() sys.exit(102) else: self.mon.log(self,'Desktop is '+ os.environ['DESKTOP_SESSION']) # optional other classes used self.root=None self.ppio=None self.tod=None self.animate=None self.gpiodriver=None self.oscdriver=None self.osc_enabled=False self.gpio_enabled=False self.tod_enabled=False self.email_enabled=False if os.geteuid() == 0: self.mon.err(self,'Do not run Pi Presents with sudo') self.end('error','Do not run Pi Presents with sudo') user=os.getenv('USER') self.mon.log(self,'User is: '+ user) # self.mon.log(self,"os.getenv('HOME') - user home directory (not used): " + os.getenv('HOME')) # does not work # self.mon.log(self,"os.path.expanduser('~') - user home directory: " + os.path.expanduser('~')) # does not work # check network is available self.network_connected=False self.network_details=False self.interface='' self.ip='' self.unit='' # sets self.network_connected and self.network_details self.init_network() # start the mailer and send email when PP starts self.email_enabled=False if self.network_connected is True: self.init_mailer() if self.email_enabled is True and self.mailer.email_at_start is True: subject= '[Pi Presents] ' + self.unit + ': PP Started on ' + time.strftime("%Y-%m-%d %H:%M") message = time.strftime("%Y-%m-%d %H:%M") + '\nUnit: ' + self.unit + ' Profile: '+ self.options['profile']+ '\n ' + self.interface + '\n ' + self.ip self.send_email('start',subject,message) # get profile path from -p option if self.options['profile'] != '': self.pp_profile_path="/pp_profiles/"+self.options['profile'] else: self.mon.err(self,"Profile not specified in command ") self.end('error','Profile not specified with the commands -p option') # get directory containing pp_home from the command, if self.options['home'] == "": home = os.sep+ 'home' + os.sep + user + os.sep+"pp_home" else: home = self.options['home'] + os.sep+ "pp_home" self.mon.log(self,"pp_home directory is: " + home) # check if pp_home exists. # try for 10 seconds to allow usb stick to automount found=False for i in range (1, 10): self.mon.log(self,"Trying pp_home at: " + home + " (" + str(i)+')') if os.path.exists(home): found=True self.pp_home=home break time.sleep (1) if found is True: self.mon.log(self,"Found Requested Home Directory, using pp_home at: " + home) else: self.mon.err(self,"Failed to find pp_home directory at " + home) self.end('error',"Failed to find pp_home directory at " + home) # check profile exists self.pp_profile=self.pp_home+self.pp_profile_path if os.path.exists(self.pp_profile): self.mon.sched(self,"Running profile: " + self.pp_profile_path) self.mon.log(self,"Found Requested profile - pp_profile directory is: " + self.pp_profile) else: self.mon.err(self,"Failed to find requested profile: "+ self.pp_profile) self.end('error',"Failed to find requested profile: "+ self.pp_profile) self.mon.start_stats(self.options['profile']) if self.options['verify'] is True: val =Validator() if val.validate_profile(None,pp_dir,self.pp_home,self.pp_profile,self.pipresents_issue,False) is False: self.mon.err(self,"Validation Failed") self.end('error','Validation Failed') # initialise and read the showlist in the profile self.showlist=ShowList() self.showlist_file= self.pp_profile+ "/pp_showlist.json" if os.path.exists(self.showlist_file): self.showlist.open_json(self.showlist_file) else: self.mon.err(self,"showlist not found at "+self.showlist_file) self.end('error',"showlist not found at "+self.showlist_file) # check profile and Pi Presents issues are compatible if self.showlist.profile_version() != self.pipresents_version(): self.mon.err(self,"Version of showlist " + self.showlist.profile_version_string + " is not same as Pi Presents") self.end('error',"Version of showlist " + self.showlist.profile_version_string + " is not same as Pi Presents") # get the 'start' show from the showlist index = self.showlist.index_of_show('start') if index >=0: self.showlist.select(index) self.starter_show=self.showlist.selected_show() else: self.mon.err(self,"Show [start] not found in showlist") self.end('error',"Show [start] not found in showlist") # ******************** # SET UP THE GUI # ******************** # turn off the screenblanking and saver if self.options['noblank'] is True: call(["xset","s", "off"]) call(["xset","s", "-dpms"]) self.root=Tk() self.title='Pi Presents - '+ self.pp_profile self.icon_text= 'Pi Presents' self.root.title(self.title) self.root.iconname(self.icon_text) self.root.config(bg=self.starter_show['background-colour']) self.mon.log(self, 'monitor screen dimensions are ' + str(self.root.winfo_screenwidth()) + ' x ' + str(self.root.winfo_screenheight()) + ' pixels') if self.options['screensize'] =='': self.screen_width = self.root.winfo_screenwidth() self.screen_height = self.root.winfo_screenheight() else: reason,message,self.screen_width,self.screen_height=self.parse_screen(self.options['screensize']) if reason =='error': self.mon.err(self,message) self.end('error',message) self.mon.log(self, 'forced screen dimensions (--screensize) are ' + str(self.screen_width) + ' x ' + str(self.screen_height) + ' pixels') # set window dimensions and decorations if self.options['fullscreen'] is False: self.window_width=int(self.root.winfo_screenwidth()*self.nonfull_window_width) self.window_height=int(self.root.winfo_screenheight()*self.nonfull_window_height) self.window_x=self.nonfull_window_x self.window_y=self.nonfull_window_y self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y)) else: self.window_width=self.screen_width self.window_height=self.screen_height self.root.attributes('-fullscreen', True) os.system('unclutter &') self.window_x=0 self.window_y=0 self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y)) self.root.attributes('-zoomed','1') # canvas cover the whole screen whatever the size of the window. self.canvas_height=self.screen_height self.canvas_width=self.screen_width # make sure focus is set. self.root.focus_set() # define response to main window closing. self.root.protocol ("WM_DELETE_WINDOW", self.handle_user_abort) # setup a canvas onto which will be drawn the images or text self.canvas = Canvas(self.root, bg=self.starter_show['background-colour']) if self.options['fullscreen'] is True: self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=0) else: self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=1, highlightcolor='yellow') self.canvas.place(x=0,y=0) # self.canvas.config(bg='black') self.canvas.focus_set() # **************************************** # INITIALISE THE INPUT DRIVERS # **************************************** # each driver takes a set of inputs, binds them to symboic names # and sets up a callback which returns the symbolic name when an input event occurs/ # use keyboard driver to bind keys to symbolic names and to set up callback kbd=KbdDriver() if kbd.read(pp_dir,self.pp_home,self.pp_profile) is False: self.end('error','cannot find, or error in keys.cfg') kbd.bind_keys(self.root,self.handle_input_event) self.sr=ScreenDriver() # read the screen click area config file reason,message = self.sr.read(pp_dir,self.pp_home,self.pp_profile) if reason == 'error': self.end('error','cannot find, or error in screen.cfg') # create click areas on the canvas, must be polygon as outline rectangles are not filled as far as find_closest goes # click areas are made on the Pi Presents canvas not the show canvases. reason,message = self.sr.make_click_areas(self.canvas,self.handle_input_event) if reason == 'error': self.mon.err(self,message) self.end('error',message) # **************************************** # INITIALISE THE APPLICATION AND START # **************************************** self.shutdown_required=False self.terminate_required=False self.exitpipresents_required=False # delete omxplayer dbus files # if os.path.exists("/tmp/omxplayerdbus.{}".format(user)): # os.remove("/tmp/omxplayerdbus.{}".format(user)) # if os.path.exists("/tmp/omxplayerdbus.{}.pid".format(user)): # os.remove("/tmp/omxplayerdbus.{}.pid".format(user)) # kick off GPIO if enabled by command line option self.gpio_enabled=False if os.path.exists(self.pp_profile + os.sep + 'pp_io_config'+os.sep+ 'gpio.cfg'): # initialise the GPIO self.gpiodriver=GPIODriver() reason,message=self.gpiodriver.init(pp_dir,self.pp_home,self.pp_profile,self.canvas,50,self.handle_input_event) if reason == 'error': self.end('error',message) else: self.gpio_enabled=True # and start polling gpio self.gpiodriver.poll() # kick off animation sequencer self.animate = Animate() self.animate.init(pp_dir,self.pp_home,self.pp_profile,self.canvas,200,self.handle_output_event) self.animate.poll() #create a showmanager ready for time of day scheduler and osc server show_id=-1 self.show_manager=ShowManager(show_id,self.showlist,self.starter_show,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home) # first time through set callback to terminate Pi Presents if all shows have ended. self.show_manager.init(self.canvas,self.all_shows_ended_callback,self.handle_command,self.showlist) # Register all the shows in the showlist reason,message=self.show_manager.register_shows() if reason == 'error': self.mon.err(self,message) self.end('error',message) # Init OSCDriver, read config and start OSC server self.osc_enabled=False if self.network_connected is True: if os.path.exists(self.pp_profile + os.sep + 'pp_io_config'+ os.sep + 'osc.cfg'): self.oscdriver=OSCDriver() reason,message=self.oscdriver.init(self.pp_profile,self.handle_command,self.handle_input_event,self.e_osc_handle_output_event) if reason == 'error': self.mon.err(self,message) self.end('error',message) else: self.osc_enabled=True self.root.after(1000,self.oscdriver.start_server()) # enable ToD scheduler if schedule exists if os.path.exists(self.pp_profile + os.sep + 'schedule.json'): self.tod_enabled = True else: self.tod_enabled=False # warn if the network not available when ToD required if self.tod_enabled is True and self.network_connected is False: self.mon.warn(self,'Network not connected so Time of Day scheduler may be using the internal clock') # warn about start shows and scheduler if self.starter_show['start-show']=='' and self.tod_enabled is False: self.mon.sched(self,"No Start Shows in Start Show and no shows scheduled") self.mon.warn(self,"No Start Shows in Start Show and no shows scheduled") if self.starter_show['start-show'] !='' and self.tod_enabled is True: self.mon.sched(self,"Start Shows in Start Show and shows scheduled - conflict?") self.mon.warn(self,"Start Shows in Start Show and shows scheduled - conflict?") # run the start shows self.run_start_shows() # kick off the time of day scheduler which may run additional shows if self.tod_enabled is True: self.tod=TimeOfDay() self.tod.init(pp_dir,self.pp_home,self.pp_profile,self.root,self.handle_command) self.tod.poll() # start Tkinters event loop self.root.mainloop( ) def parse_screen(self,size_text): fields=size_text.split('*') if len(fields)!=2: return 'error','do not understand --screensize comand option',0,0 elif fields[0].isdigit() is False or fields[1].isdigit() is False: return 'error','dimensions are not positive integers in --screensize',0,0 else: return 'normal','',int(fields[0]),int(fields[1]) # ********************* # RUN START SHOWS # ******************** def run_start_shows(self): self.mon.trace(self,'run start shows') # parse the start shows field and start the initial shows show_refs=self.starter_show['start-show'].split() for show_ref in show_refs: reason,message=self.show_manager.control_a_show(show_ref,'open') if reason == 'error': self.mon.err(self,message) # ********************* # User inputs # ******************** # handles one command provided as a line of text def handle_command(self,command_text,source='',show=''): # print 'PIPRESENTS ',command_text,source,'from',show self.mon.log(self,"command received: " + command_text) if command_text.strip()=="": return if command_text[0]=='/': if self.osc_enabled is True: self.oscdriver.send_command(command_text) return fields= command_text.split() show_command=fields[0] if len(fields)>1: show_ref=fields[1] else: show_ref='' if show_command in ('open','close'): self.mon.sched(self, command_text + ' received from show:'+show) if self.shutdown_required is False and self.terminate_required is False: reason,message=self.show_manager.control_a_show(show_ref,show_command) else: return elif show_command =='monitor': self.handle_monitor_command(show_ref) return elif show_command == 'event': self.handle_input_event(show_ref,'Show Control') return elif show_command == 'exitpipresents': self.exitpipresents_required=True if self.show_manager.all_shows_exited() is True: # need root.after to get out of st thread self.root.after(1,self.e_all_shows_ended_callback) return else: reason,message= self.show_manager.exit_all_shows() elif show_command == 'shutdownnow': # need root.after to get out of st thread self.root.after(1,self.e_shutdown_pressed) return else: reason='error' message = 'command not recognised: '+ show_command if reason=='error': self.mon.err(self,message) return def handle_monitor_command(self,command): if command == 'on': os.system('vcgencmd display_power 1 >/dev/null') elif command == 'off': os.system('vcgencmd display_power 0 >/dev/null') def e_all_shows_ended_callback(self): self.all_shows_ended_callback('normal','no shows running') def e_shutdown_pressed(self): self.shutdown_pressed('now') def e_osc_handle_output_event(self,line): #jump out of server thread self.root.after(1, lambda arg=line: self.osc_handle_output_event(arg)) def osc_handle_output_event(self,line): self.mon.log(self,"output event received: "+ line) #osc sends output events as a string reason,message,delay,name,param_type,param_values=self.animate.parse_animate_fields(line) if reason == 'error': self.mon.err(self,message) self.end(reason,message) self.handle_output_event(name,param_type,param_values,0) def handle_output_event(self,symbol,param_type,param_values,req_time): if self.gpio_enabled is True: reason,message=self.gpiodriver.handle_output_event(symbol,param_type,param_values,req_time) if reason =='error': self.mon.err(self,message) self.end(reason,message) else: self.mon.warn(self,'GPIO not enabled') # all input events call this callback with a symbolic name. # handle events that affect PP overall, otherwise pass to all active shows def handle_input_event(self,symbol,source): self.mon.log(self,"event received: "+symbol + ' from '+ source) if symbol == 'pp-terminate': self.handle_user_abort() elif symbol == 'pp-shutdown': self.shutdown_pressed('delay') elif symbol == 'pp-shutdownnow': # need root.after to grt out of st thread self.root.after(1,self.e_shutdown_pressed) return elif symbol == 'pp-exitpipresents': self.exitpipresents_required=True if self.show_manager.all_shows_exited() is True: # need root.after to grt out of st thread self.root.after(1,self.e_all_shows_ended_callback) return reason,message= self.show_manager.exit_all_shows() else: # events for shows affect the show and could cause it to exit. for show in self.show_manager.shows: show_obj=show[ShowManager.SHOW_OBJ] if show_obj is not None: show_obj.handle_input_event(symbol) def shutdown_pressed(self, when): if when == 'delay': self.root.after(5000,self.on_shutdown_delay) else: self.shutdown_required=True if self.show_manager.all_shows_exited() is True: self.all_shows_ended_callback('normal','no shows running') else: # calls exit method of all shows, results in all_shows_closed_callback self.show_manager.exit_all_shows() def on_shutdown_delay(self): # 5 second delay is up, if shutdown button still pressed then shutdown if self.gpiodriver.shutdown_pressed() is True: self.shutdown_required=True if self.show_manager.all_shows_exited() is True: self.all_shows_ended_callback('normal','no shows running') else: # calls exit method of all shows, results in all_shows_closed_callback self.show_manager.exit_all_shows() def handle_sigterm(self,signum,frame): self.mon.log(self,'SIGTERM received - '+ str(signum)) self.terminate() def handle_user_abort(self): self.mon.log(self,'User abort received') self.terminate() def terminate(self): self.mon.log(self, "terminate received") self.terminate_required=True needs_termination=False for show in self.show_manager.shows: # print show[ShowManager.SHOW_OBJ], show[ShowManager.SHOW_REF] if show[ShowManager.SHOW_OBJ] is not None: needs_termination=True self.mon.log(self,"Sent terminate to show "+ show[ShowManager.SHOW_REF]) # call shows terminate method # eventually the show will exit and after all shows have exited all_shows_callback will be executed. show[ShowManager.SHOW_OBJ].terminate() if needs_termination is False: self.end('killed','killed - no termination of shows required') # ****************************** # Ending Pi Presents after all the showers and players are closed # ************************** # callback from ShowManager when all shows have ended def all_shows_ended_callback(self,reason,message): self.canvas.config(bg=self.starter_show['background-colour']) if reason in ('killed','error') or self.shutdown_required is True or self.exitpipresents_required is True: self.end(reason,message) def end(self,reason,message): self.mon.log(self,"Pi Presents ending with reason: " + reason) if self.root is not None: self.root.destroy() self.tidy_up() # gc.collect() # print gc.garbage if reason == 'killed': if self.email_enabled is True and self.mailer.email_on_terminate is True: subject= '[Pi Presents] ' + self.unit + ': PP Exited with reason: Terminated' message = time.strftime("%Y-%m-%d %H:%M") + '\n ' + self.unit + '\n ' + self.interface + '\n ' + self.ip self.send_email(reason,subject,message) self.mon.sched(self, "Pi Presents Terminated, au revoir\n") self.mon.log(self, "Pi Presents Terminated, au revoir") # close logging files self.mon.finish() sys.exit(101) elif reason == 'error': if self.email_enabled is True and self.mailer.email_on_error is True: subject= '[Pi Presents] ' + self.unit + ': PP Exited with reason: Error' message_text = 'Error message: '+ message + '\n'+ time.strftime("%Y-%m-%d %H:%M") + '\n ' + self.unit + '\n ' + self.interface + '\n ' + self.ip self.send_email(reason,subject,message_text) self.mon.sched(self, "Pi Presents closing because of error, sorry\n") self.mon.log(self, "Pi Presents closing because of error, sorry") # close logging files self.mon.finish() sys.exit(102) else: self.mon.sched(self,"Pi Presents exiting normally, bye\n") self.mon.log(self,"Pi Presents exiting normally, bye") # close logging files self.mon.finish() if self.shutdown_required is True: # print 'SHUTDOWN' call (['sudo','shutdown','now','SHUTTING DOWN']) sys.exit(100) def init_network(self): timeout=int(self.options['nonetwork']) if timeout== 0: self.network_connected=False self.unit='' self.ip='' self.interface='' return self.network=Network() self.network_connected=False # try to connect to network self.mon.log (self, 'Waiting up to '+ str(timeout) + ' seconds for network') success=self.network.wait_for_network(timeout) if success is False: self.mon.warn(self,'Failed to connect to network after ' + str(timeout) + ' seconds') # tkMessageBox.showwarning("Pi Presents","Failed to connect to network so using fake-hwclock") return self.network_connected=True self.mon.sched (self, 'Time after network check is '+ time.strftime("%Y-%m-%d %H:%M.%S")) self.mon.log (self, 'Time after network check is '+ time.strftime("%Y-%m-%d %H:%M.%S")) # Get web configuration self.network_details=False network_options_file_path=self.pp_dir+os.sep+'pp_config'+os.sep+'pp_web.cfg' if not os.path.exists(network_options_file_path): self.mon.warn(self,"pp_web.cfg not found at "+network_options_file_path) return self.mon.log(self, 'Found pp_web.cfg in ' + network_options_file_path) self.network.read_config(network_options_file_path) self.unit=self.network.unit # get interface and IP details of preferred interface self.interface,self.ip = self.network.get_preferred_ip() if self.interface == '': self.network_connected=False return self.network_details=True self.mon.log (self, 'Network details ' + self.unit + ' ' + self.interface + ' ' +self.ip) def init_mailer(self): self.email_enabled=False email_file_path = self.pp_dir+os.sep+'pp_config'+os.sep+'pp_email.cfg' if not os.path.exists(email_file_path): self.mon.log(self,'pp_email.cfg not found at ' + email_file_path) return self.mon.log(self,'Found pp_email.cfg at ' + email_file_path) self.mailer=Mailer() self.mailer.read_config(email_file_path) # all Ok so can enable email if config file allows it. if self.mailer.email_allowed is True: self.email_enabled=True self.mon.log (self,'Email Enabled') def send_email(self,reason,subject,message): if self.try_connect() is False: return False else: success,error = self.mailer.send(subject,message) if success is False: self.mon.log(self, 'Failed to send email: ' + str(error)) success,error=self.mailer.disconnect() if success is False: self.mon.log(self,'Failed disconnect after send:' + str(error)) return False else: self.mon.log(self,'Sent email for ' + reason) success,error=self.mailer.disconnect() if success is False: self.mon.log(self,'Failed disconnect from email server ' + str(error)) return True def try_connect(self): tries=1 while True: success, error = self.mailer.connect() if success is True: return True else: self.mon.log(self,'Failed to connect to email SMTP server ' + str(tries) + '\n ' +str(error)) tries +=1 if tries >5: self.mon.log(self,'Failed to connect to email SMTP server after ' + str(tries)) return False # tidy up all the peripheral bits of Pi Presents def tidy_up(self): self.handle_monitor_command('on') self.mon.log(self, "Tidying Up") # turn screen blanking back on if self.options['noblank'] is True: call(["xset","s", "on"]) call(["xset","s", "+dpms"]) # tidy up animation and gpio if self.animate is not None: self.animate.terminate() if self.gpio_enabled==True: self.gpiodriver.terminate() if self.osc_enabled is True: self.oscdriver.terminate() # tidy up time of day scheduler if self.tod_enabled is True: self.tod.terminate()
ht=win.winfo_screenheight()-menubar.winfo_height()-objFrame.winfo_height()-statsFrame.winfo_height() stateDiagramFrame=ttk.LabelFrame(win,text="Display") stateDiagramFrame.pack(fill=BOTH,anchor='s') #Add a canvas to the stateDiagramFrame canvas=Canvas(stateDiagramFrame,bg='#FFFFFF',height=ht) #canvas.scalex=1.0 canvas.pack(side="top", fill=BOTH, expand=True) #Add a scrollbar for the canvas setScrollBar(canvas,stateDiagramFrame) #bind scroll left-to-right scroll events to the canvas canvas.bind('<4>', lambda event : canvas.xview('scroll', -1, 'units')) canvas.bind('<5>', lambda event : canvas.xview('scroll', 1, 'units')) canvas.focus_set() externalDisplay1=ttk.Button(frmInsideTab1,text="Browser View",width=10) externalDisplay1.pack(anchor='w',fill=X,padx=2,pady=2) def displayFromBrowsercallback(event): displayFromBrowser(canvas,'ktail') externalDisplay1.bind('<ButtonRelease-1>',displayFromBrowsercallback) def callChildWindow(): if TraceLoaded(tracePad)==False: tkMessageBox.showerror("No trace Log", "Please load traces first.") return else: from ktail import kTails
class text_canvas(Frame): def __init__(self, parent, font_size, input_handler, filename): Frame.__init__(self, parent) self.parent = parent self.text_font = tkFont.Font(family='Monaco', size=font_size, weight='bold') self.filename = filename self.cheight, self.cwidth, self.line_num_spacing = font_size, self.text_font.measure('c'), 50 self.line_height = ((self.winfo_screenheight() - self.cheight)/(self.cheight + 2) - 4) self.init_UI(input_handler) def init_UI(self, input_handler): self.parent.title('') self.pack(fill=BOTH, expand=1) self.init_canvas(input_handler) def get_dimensions(self): return { 'cheight': self.cheight, 'cwidth': self.cwidth, 'line_num_spacing':self.line_num_spacing, 'line_height': self.line_height, 'screen_width': self.winfo_screenwidth(), 'screen_height': self.winfo_screenheight() } def init_canvas(self, input_handler): self.canvas = Canvas(self, highlightthickness=0, width=self.winfo_screenwidth(), height=self.winfo_screenheight(), bg=options['background_color']) self.canvas.pack() self.canvas.focus_set() self.bind_events(input_handler) def clear_all(self): self.canvas.delete('all') def get_line_height(self): return self.line_height def get_grid_y(self, y): return self.cheight * y + (y * 2) def write_line_grid(self, y, line): for token in line: self.write_text_grid(token[0], y, token[1], token[2]) # write line of text at given grid co-ordinates def write_text_grid(self, x, y, text, color=options['text_color']): x_val = self.cwidth * x + self.line_num_spacing # 2 pixel spacing between each line y_val = self.cheight * y + (y * 2) self.canvas.create_text(x_val, y_val, anchor='nw', text=text, font=self.text_font, fill=color) def write_status_line(self, text, textcolor=options['status_text_color'], backgroundcolor=options['status_background_color']): y = self.line_height + 1 self.canvas.create_rectangle(0, self.cheight * y + (y * 2), self.winfo_screenwidth(), self.cheight * y + (y * 2) + self.cheight + 4, fill=backgroundcolor, outline=backgroundcolor) self.write_text_grid(0, self.line_height + 1, text, textcolor) def draw_highlight_grid(self, y, x1, x2, highlightcolor=options['text_highlight_color']): y_val = self.cheight * y + (y * 2) x1_val = self.cwidth * x1 + self.line_num_spacing x2_val = self.cwidth * x2 + self.line_num_spacing self.canvas.create_rectangle(x1_val, y_val, x2_val, y_val + self.cheight + 4, fill=highlightcolor, outline=highlightcolor) def draw_line_numbers(self, start, highlightcolor=options['line_num_highlight_color'], textcolor=options['line_num_text_color']): self.canvas.create_rectangle(0, 0, self.line_num_spacing / 2, self.winfo_screenheight(), fill=highlightcolor, outline=highlightcolor) for i in range(self.line_height + 1): self.canvas.create_text(0, self.cheight * i + (i * 2), anchor='nw', text=str(start + i), font=self.text_font, fill=textcolor) def draw_cursor(self, x, y, highlightcolor=options['cursor_highlight_color'], cursorcolor=options['cursor_color']): x_val = self.cwidth * x + self.line_num_spacing y_val = self.cheight * y + (y * 2) self.canvas.create_rectangle(0, y_val, self.winfo_screenwidth(), y_val + self.cheight + 4, fill=highlightcolor, outline=highlightcolor) self.canvas.create_rectangle(x_val, 0, x_val + self.cwidth, self.winfo_screenheight(), fill=highlightcolor, outline=highlightcolor) self.canvas.create_rectangle(x_val, y_val, x_val + self.cwidth, y_val + self.cheight + 4, fill=cursorcolor, outline=cursorcolor) def draw_rectangle_absolute(self, x1, y1, x2, y2, color): self.canvas.create_rectangle(x1, y1, x2, y2, fill=color, outline=color) def bind_events(self, input_handler): # TODO: this should be cleaned up ideally into a separate handler list input_handler.set_GUI_reference(self) self.canvas.bind('<Key>', input_handler.key) self.canvas.bind_all('<Escape>', input_handler.escape) self.canvas.bind_all('<Control-a>', input_handler.control_a) self.canvas.bind_all('<Control-b>', input_handler.control_b) self.canvas.bind_all('<Control-c>', input_handler.control_c) self.canvas.bind_all('<Control-d>', input_handler.control_d) self.canvas.bind_all('<Control-e>', input_handler.control_e) self.canvas.bind_all('<Control-f>', input_handler.control_f) self.canvas.bind_all('<Control-g>', input_handler.control_g) self.canvas.bind_all('<Control-h>', input_handler.control_h) self.canvas.bind_all('<Control-i>', input_handler.control_i) self.canvas.bind_all('<Control-j>', input_handler.control_j) self.canvas.bind_all('<Control-k>', input_handler.control_k) self.canvas.bind_all('<Control-l>', input_handler.control_l) self.canvas.bind_all('<Control-m>', input_handler.control_m) self.canvas.bind_all('<Control-n>', input_handler.control_n) self.canvas.bind_all('<Control-o>', input_handler.control_o) self.canvas.bind_all('<Control-p>', input_handler.control_p) self.canvas.bind_all('<Control-q>', input_handler.control_q) self.canvas.bind_all('<Control-r>', input_handler.control_r) self.canvas.bind_all('<Control-s>', input_handler.control_s) self.canvas.bind_all('<Control-t>', input_handler.control_t) self.canvas.bind_all('<Control-u>', input_handler.control_u) self.canvas.bind_all('<Control-v>', input_handler.control_v) self.canvas.bind_all('<Control-w>', input_handler.control_w) self.canvas.bind_all('<Control-x>', input_handler.control_x) self.canvas.bind_all('<Control-y>', input_handler.control_y) self.canvas.bind_all('<Control-z>', input_handler.control_z) self.canvas.bind_all("<MouseWheel>", input_handler.mouse_scroll) self.canvas.bind_all('<Control-braceright>', input_handler.control_braceright) self.canvas.bind_all('<Control-braceleft>', input_handler.control_braceleft)
class SudokuUI(Frame): """ The Tkinter UI, responsible for drawing the board and accepting user input. """ def __init__(self, parent, game): self.game = game Frame.__init__(self, parent) self.parent = parent self.row, self.col = -1, -1 self.initUI() def initUI(self): self.parent.title("Sudoku") self.pack(fill=BOTH, expand=1) self.canvas = Canvas(self, width=WIDTH, height=HEIGHT, highlightthickness=1) self.canvas.pack(fill=BOTH, side=TOP) clear_button = Button(self, text="RESET", command=self.clear_answers) check = Button(self, text="validate progress", command=self.check_progress) ans = Button(self, text="Solution", command=self.get_answer) win_check = Button(self, text="Submit", command=self.check_victory) clear = Button(self, text="Clear", command=self.clear) clear_button.pack(side=LEFT) check.pack(side=LEFT) ans.pack(side=LEFT) win_check.pack(side=LEFT) clear.pack(side=LEFT) self.draw_grid() self.draw_puzzle() self.canvas.bind("<Button-1>", self.cell_clicked) self.canvas.bind("<Key>", self.key_pressed) def draw_grid(self): for i in xrange(10): self.canvas.create_line(MARGIN + i * SIDE, MARGIN, MARGIN + i * SIDE, HEIGHT - MARGIN, fill="blue" if i % 3 == 0 else "gray") self.canvas.create_line(MARGIN, MARGIN + i * SIDE, WIDTH - MARGIN, MARGIN + i * SIDE, fill="blue" if i % 3 == 0 else "gray") def draw_puzzle(self): self.canvas.delete("numbers") for i in xrange(9): for j in xrange(9): answer = self.game.answer[i][j] original = self.game.puzzle[i][j] if answer != 0: self.canvas.create_text( MARGIN + j * SIDE + SIDE / 2, MARGIN + i * SIDE + SIDE / 2, text=answer, tags="numbers", fill="black" if answer == original else "slate gray") def draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: self.canvas.create_rectangle(MARGIN + self.col * SIDE + 1, MARGIN + self.row * SIDE + 1, MARGIN + (self.col + 1) * SIDE - 1, MARGIN + (self.row + 1) * SIDE - 1, outline="red", tags="cursor") def draw_victory(self, status=False): self.canvas.create_oval(MARGIN + SIDE * 2, MARGIN + SIDE * 2, MARGIN + SIDE * 7, MARGIN + SIDE * 7, tags="victory", fill="dark orange", outline="orange") self.canvas.create_text(MARGIN + 4 * SIDE + SIDE / 2, MARGIN + 4 * SIDE + SIDE / 2, text="You win!" if status else "You Lose", tags="victory", fill="white", font=("Arial", 32)) def draw_progress(self, status=False): self.canvas.create_oval(MARGIN + SIDE * 2, MARGIN + SIDE * 2, MARGIN + SIDE * 7, MARGIN + SIDE * 7, tags="progress", fill="dark orange", outline="orange") self.canvas.create_text(MARGIN + 4 * SIDE + SIDE / 2, MARGIN + 4 * SIDE + SIDE / 2, text="Valid" if status else "inValid", tags="progress", fill="white", font=("Arial", 32)) def cell_clicked(self, event): if self.game.game_over: return x, y = event.x, event.y if (x > MARGIN and x < WIDTH - MARGIN and y > MARGIN and y < HEIGHT - MARGIN): self.canvas.focus_set() row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 elif self.game.puzzle[row][col] == 0: self.row, self.col = row, col else: self.row, self.col = -1, -1 self.draw_cursor() def key_pressed(self, event): if self.game.game_over: return if self.row >= 0 and self.col >= 0 and event.char in "1234567890": self.game.answer[self.row][self.col] = int(event.char) self.col, self.row = -1, -1 self.draw_puzzle() self.draw_cursor() def check_victory(self): if self.game.check(self.game.answer): self.draw_victory(status=True) else: self.draw_victory() def clear_answers(self): self.game.set_answer_to_puzzle() self.canvas.delete("victory") self.canvas.delete("progress") self.draw_puzzle() def clear(self): self.canvas.delete("victory") self.canvas.delete("progress") def get_answer(self): self.canvas.delete("victory") self.canvas.delete("progress") self.game.set_answer_to_puzzle() self.game.set_answer_to_solution() self.draw_puzzle() def check_progress(self): if self.game.check(self.game.answer, progress=True): self.draw_progress(status=True) else: self.draw_progress()
class SudokuGUI(object): def __init__(self, sudoku): """ Creates a GUI. """ self.sudoku = sudoku self.row = self.col = -1 self.tk = Tk() self.tk.title('Sudoku') self.frame = Frame(self.tk) self.frame.pack(fill=BOTH, expand=1) self.canvas = Canvas(self.frame, width=FRAME_WIDTH, height=FRAME_HEIGHT) self.canvas.pack() button_clear = Button( self.frame, width=BUTTON_WIDTH, height=BUTTON_HEIGHT, text='Clear board', command=self.clear_board ) button_clear.pack(side=LEFT) button_check = Button( self.frame, width=BUTTON_WIDTH, height=BUTTON_HEIGHT, text='Check answers', command=self.check_answers ) button_check.pack(side=RIGHT) for i in xrange(10): self.canvas.create_line( PADDING + i * CELL_WIDTH, PADDING, PADDING + i * CELL_WIDTH, FRAME_WIDTH - 3 * PADDING, fill='gray' if i % 3 else 'black' ) self.canvas.create_line( PADDING, PADDING + i * CELL_WIDTH, FRAME_HEIGHT - PADDING, PADDING + i * CELL_WIDTH, fill='gray' if i % 3 else 'black' ) self.canvas.bind('<Button-1>', self.mouse_click) self.canvas.bind('<Key>', self.key_press) def set_answer_to_board(self): """ Populates GUI board with the current value of sudoku.answer. """ if DEBUG: print 'setting answer: %s' % self.sudoku.answer self.canvas.delete('input') for i in xrange(81): if self.sudoku.answer[i] != '0': self.canvas.create_text( PADDING + i % 9 * CELL_WIDTH + CELL_WIDTH / 2, PADDING + i / 9 * CELL_WIDTH + CELL_WIDTH / 2, text=self.sudoku.answer[i], tags='input', fill='black' if self.sudoku.is_in_puzzle(i) else 'blue' ) def clear_board(self): """ Clears the board of user input, setting it to the value of original puzzle. """ self.sudoku.set_answer_to_puzzle() self.set_answer_to_board() def check_answers(self): """ Checks user input and displays appropriate message. """ if self.sudoku.check_solution(): tkMessageBox.showinfo(title='You Win!', message='Congratulations! You Won!', parent=self.tk) else: tkMessageBox.showinfo(title='Sorry!', message='Your solution is not correct!', parent=self.tk) def mouse_click(self, event): """ Handles mouse click event. Selects a cell by drawing a blue rectangle around it. """ x, y = event.x, event.y if PADDING < x < FRAME_WIDTH - PADDING and PADDING < y < FRAME_HEIGHT - PADDING: self.canvas.focus_set() row, col = (y - PADDING) / CELL_WIDTH, (x - PADDING) / CELL_WIDTH if DEBUG: print 'cell clicked: (%s, %s)' % (row, col) if not (0 <= row <= 8 and 0 <= col <= 8): return if (row, col) == (self.row, self.col): self.row = self.col = -1 elif self.sudoku.puzzle[row * 9 + col] == '0': self.row, self.col = row, col else: self.row = self.col = -1 self.canvas.delete('select') if self.row >= 0 and self.col >= 0: self.canvas.create_rectangle( PADDING + self.col * CELL_WIDTH + 1, PADDING + self.row * CELL_WIDTH + 1, PADDING + (self.col + 1) * CELL_WIDTH - 1, PADDING + (self.row + 1) * CELL_WIDTH - 1, outline='blue', tags='select' ) def key_press(self, event): """ Handles key press event. If the key pressed is a number, fill it in the answer board. """ key = event.char if DEBUG: print 'key pressed', key if not key.isdigit(): return if self.row >= 0 and self.col >= 0: i = self.row * 9 + self.col self.sudoku.answer = self.sudoku.answer[:i] + key + self.sudoku.answer[i + 1:] self.set_answer_to_board() def start(self): """ Puts the GUI in motion. """ self.tk.geometry('%dx%d' % (FRAME_HEIGHT, FRAME_WIDTH)) self.tk.mainloop()
class ChineseCheckerUI(Frame): def __init__(self, parent, options = 0): Frame.__init__(self, parent) if options == 0: self.full = 0 self.game = boardState(options = 'smallGame') self.HEIGHT = pieceSize * self.game.height + 2 * margin self.WIDTH = pieceSize * self.game.mid_width_max + 2 * margin elif options == 1: self.full = 1 self.game = boardState(options = 'fullGame') self.HEIGHT = pieceSize * self.game.height + 2 * margin self.WIDTH = pieceSize * self.game.mid_width_max + 2 * margin self.row, self.col = -1, -1 self.parent = parent self.initUI() def initUI(self): self.parent.title("Colours") self.pack(fill=BOTH, expand=1) self.canvas = Canvas(self) for i in xrange(self.game.height): for j in xrange(self.game.mid_width_max): if self.game.board[i][j] == 1: self.canvas.create_oval(margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="blue", width=2) elif self.game.board[i][j] == 2: self.canvas.create_oval(margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="red", width=2) elif self.game.board[i][j] == 0: self.canvas.create_oval(margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="grey", width=2) self.canvas.pack(fill=BOTH, expand=1) self.canvas.bind("<Button-1>", self.__cell_clicked) def __draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: if self.game.board[self.row][self.col] == 1: self.canvas.create_oval(margin + self.col * pieceSize + spacing, margin + self.row * pieceSize + spacing, margin + (self.col + 1) * pieceSize - spacing, margin + (self.row + 1) * pieceSize - spacing, outline="green", fill="blue", width=2, tags="cursor") elif self.game.board[self.row][self.col] == 2: self.canvas.create_oval(margin + self.col * pieceSize + spacing, margin + self.row * pieceSize + spacing, margin + (self.col + 1) * pieceSize - spacing, margin + (self.row + 1) * pieceSize - spacing, outline="green", fill="red", width=2, tags="cursor") elif self.game.board[self.row][self.col] == 0: self.canvas.create_oval(margin + self.col * pieceSize + spacing, margin + self.row * pieceSize + spacing, margin + (self.col + 1) * pieceSize - spacing, margin + (self.row + 1) * pieceSize - spacing, outline="green", fill="grey", width=2, tags="cursor") def __cell_clicked(self, event): x, y = event.x, event.y if (margin < x < self.WIDTH - margin and margin < y < self.HEIGHT - margin): self.canvas.focus_set() # get row and col numbers from x,y coordinates row, col = (y - margin) / pieceSize, (x - margin) / pieceSize # if cell was selected already - deselect it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 #self.__draw_cursor(0) elif self.game.board[row][col] != -1: self.row, self.col = row, col self.__draw_cursor()
class ChineseCheckerUI(Frame): def __init__(self, parent, options=0): Frame.__init__(self, parent) if options == 0: self.full = 0 self.game = boardState(options='smallGame') self.HEIGHT = pieceSize * self.game.height + 2 * margin self.WIDTH = pieceSize * self.game.mid_width_max + 2 * margin elif options == 1: self.full = 1 self.game = boardState(options='midGame') self.HEIGHT = pieceSize * self.game.height + 2 * margin self.WIDTH = pieceSize * self.game.mid_width_max + 2 * margin self.possibleMoves = [] self.cantGo = [] self.weights = [0.911, 0.140, 0.388] self.depth = 2 self.turn = 1 self.row, self.col = -1, -1 self.parent = parent self.initUI() def initUI(self): self.parent.title("Chinese Checkers") self.pack(fill=BOTH, expand=1) self.canvas = Canvas(self) for i in xrange(self.game.height): for j in xrange(self.game.mid_width_max): if self.game.board[i][j] == 1: self.canvas.create_oval( margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="blue", width=2) elif self.game.board[i][j] == 2: self.canvas.create_oval( margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="red", width=2) elif self.game.board[i][j] == 0: self.canvas.create_oval( margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="grey", width=2) self.canvas.pack(fill=BOTH, expand=1) self.msgWindow = Entry(self.parent) self.msgWindow.insert(0, 'Your Turn') self.msgWindow.pack() self.canvas.bind("<Button-1>", self.__cell_clicked) def __repaint(self): for i in xrange(self.game.height): for j in xrange(self.game.mid_width_max): if self.game.board[i][j] == 1: self.canvas.create_oval( margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="blue", width=2) elif self.game.board[i][j] == 2: self.canvas.create_oval( margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="red", width=2) elif self.game.board[i][j] == 0: self.canvas.create_oval( margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="grey", width=2) def __draw_cursor(self): self.canvas.delete("cursor") self.canvas.delete("possible_moves") if self.row >= 0 and self.col >= 0: if self.game.board[self.row][self.col] == 1: self.canvas.create_oval( margin + self.col * pieceSize + spacing, margin + self.row * pieceSize + spacing, margin + (self.col + 1) * pieceSize - spacing, margin + (self.row + 1) * pieceSize - spacing, outline="green", fill="blue", width=2, tags="cursor") elif self.game.board[self.row][self.col] == 2: self.canvas.create_oval( margin + self.col * pieceSize + spacing, margin + self.row * pieceSize + spacing, margin + (self.col + 1) * pieceSize - spacing, margin + (self.row + 1) * pieceSize - spacing, outline="green", fill="red", width=2, tags="cursor") elif self.game.board[self.row][self.col] == 0: self.canvas.create_oval( margin + self.col * pieceSize + spacing, margin + self.row * pieceSize + spacing, margin + (self.col + 1) * pieceSize - spacing, margin + (self.row + 1) * pieceSize - spacing, outline="green", fill="grey", width=2, tags="cursor") def __draw_possible_moves(self, possibleMoves): for (newi, newj) in possibleMoves: if self.game.board[newi][newj] == 0: self.canvas.create_oval( margin + newj * pieceSize + spacing, margin + newi * pieceSize + spacing, margin + (newj + 1) * pieceSize - spacing, margin + (newi + 1) * pieceSize - spacing, outline="yellow", fill="grey", width=2, tags="possible_moves") def __cell_clicked(self, event): x, y = event.x, event.y if (margin < x < self.WIDTH - margin and margin < y < self.HEIGHT - margin): self.canvas.focus_set() # get row and col numbers from x,y coordinates row, col = (y - margin) / pieceSize, (x - margin) / pieceSize # if cell was selected already - deselect it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 #self.__draw_cursor(0) elif self.game.board[row][col] != -1: self.row, self.col = row, col if (self.row, self.col) not in self.possibleMoves: ## draw selected cells self.__draw_cursor() ## draw possible moves if self.row != -1 and self.col != -1: self.oldi = self.row self.oldj = self.col if self.turn == 1: moves = computeLegalMoveSpecify( self.game, 1, self.row, self.col) elif self.turn == 2: moves = computeLegalMoveSpecify( self.game, 2, self.row, self.col) self.possibleMoves = [ (newi, newj) for (oldi, oldj, newi, newj) in moves ] print self.possibleMoves self.__draw_possible_moves(self.possibleMoves) else: self.game = self.game.takeMove( (self.oldi, self.oldj, self.row, self.col)) self.row, self.col = -1, -1 self.possibleMoves = [] self.turn = 3 - self.turn msg = 'Computing' self.msgWindow.delete(0, END) self.msgWindow.insert(0, msg) self.__repaint() time.sleep(1) if self.turn == 2: move = findMove_Advanced(self.game, self.turn, self.weights, self.depth, self.cantGo) print move self.cantGo.append(move) if (len(self.cantGo) >= 5): self.cantGo.pop(0) self.game = self.game.takeMove(move) self.__repaint() if self.game.isEnd() == 1: msg = 'You won!' elif self.game.isEnd() == 2: msg = 'You lost!' else: msg = 'Your Turn!' self.msgWindow.delete(0, END) self.msgWindow.insert(0, msg) self.row, self.col = -1, -1 self.possibleMoves = [] self.turn = 3 - self.turn
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 SudokuUI(Frame): def __draw_puzzle(self): self.canvas.delete("numbers") for i in xrange(9): for j in xrange(9): answer = self.puzzle[i][j] if answer != 0: x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 color = "black" self.canvas.create_text( x, y, text=answer, tags="numbers", fill=color ) def __draw_grid(self): """ Draws grid divided with blue lines into 3x3 squares """ for i in xrange(10): color = "blue" if i % 3 == 0 else "gray" x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line(x0, y0, x1, y1, fill=color) x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) def __initUI(self): self.parent.title("Sudoku Solver") self.pack(fill=BOTH) self.canvas = Canvas(self, width=WIDTH, height=HEIGHT) self.canvas.pack(fill=BOTH, side=TOP) f = Frame(width=100) f.pack() solve_button = Button(f, text="Solve", command=self.__solve) solve_button.pack(side=LEFT) clear_button = Button(f, text="Clear", command=self.__clear) clear_button.pack(side=LEFT) self.__draw_grid() self.__draw_puzzle() self.canvas.bind("<Button-1>", self.__cell_clicked) self.canvas.bind("<Key>", self.__key_pressed) self.canvas.bind("<Left>", self.__go_left) self.canvas.bind("<Right>", self.__go_right) self.canvas.bind("<Up>", self.__go_up) self.canvas.bind("<Down>", self.__go_down) def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.row, self.col = -1, -1 self.puzzle = [] for i in range(9): self.puzzle.append([]) for j in range(9): self.puzzle[i].append(0) self.convert() self.__initUI() def reset(self): for i in range(9): for j in range(9): self.puzzle[i][j] = 0 def convert(self): self.string = ''.join([str(cell) for sub in self.puzzle for cell in sub]) def __draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 self.canvas.create_rectangle( x0, y0, x1, y1, outline="red", tags="cursor" ) def __cell_clicked(self, event): x, y = event.x, event.y if (MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN): self.canvas.focus_set() # get row and col numbers from x,y coordinates row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE #if cell was selected already - deselect it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 else: self.row = row self.col = col else: self.row, self.col = -1, -1 self.__draw_cursor() def __go_up(self, event): if self.row >= 1 and self.row <= 8: self.row -= 1 self.__draw_cursor() def __go_down(self, event): if self.row >= 0 and self.row <= 7: self.row += 1 self.__draw_cursor() def __go_right(self, event): if self.col >= 0 and self.col <= 7: self.col += 1 self.__draw_cursor() def __go_left(self, event): if self.col >= 1 and self.col <= 8: self.col -= 1 self.__draw_cursor() def __key_pressed(self, event): if self.row >= 0 and self.col >= 0 and event.char in "1234567890": self.puzzle[self.row][self.col] = int(event.char) #self.col, self.row = -1, -1 self.__draw_puzzle() self.__draw_cursor() def __clear(self): self.reset() self.__draw_puzzle() def __solve(self): self.convert() raw = search(parse_grid(self.string)) if raw == False: tkMessageBox.showinfo("Error", "Your sudoku is invalid. Check your input again") else: it = iter(sorted(raw.items())) temp = [] i = 0 while i < 81: temp.append(it.next()[1]) i += 1 for i in range(9): for j in range(9): self.puzzle[i][j] = temp[i*9 + j] self.__draw_puzzle()
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 (x, y) = canvas.coords(zoeid) print x, y if zoeid: canvas.delete(zoeid) size = int(scaling_factor * Zoe.size[0]), int(scaling_factor * Zoe.size[1])
class ChineseCheckerUI(Frame): def __init__(self, parent, options = 0): Frame.__init__(self, parent) if options == 0: self.full = 0 self.game = boardState(options = 'smallGame') self.HEIGHT = pieceSize * self.game.height + 2 * margin self.WIDTH = pieceSize * self.game.mid_width_max + 2 * margin elif options == 1: self.full = 1 self.game = boardState(options = 'midGame') self.HEIGHT = pieceSize * self.game.height + 2 * margin self.WIDTH = pieceSize * self.game.mid_width_max + 2 * margin self.possibleMoves = [] self.cantGo = [] self.weights = [0.911, 0.140, 0.388] self.depth = 2 self.turn = 1 self.row, self.col = -1, -1 self.parent = parent self.initUI() def initUI(self): self.parent.title("Chinese Checkers") self.pack(fill=BOTH, expand=1) self.canvas = Canvas(self) for i in xrange(self.game.height): for j in xrange(self.game.mid_width_max): if self.game.board[i][j] == 1: self.canvas.create_oval(margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="blue", width=2) elif self.game.board[i][j] == 2: self.canvas.create_oval(margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="red", width=2) elif self.game.board[i][j] == 0: self.canvas.create_oval(margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="grey", width=2) self.canvas.pack(fill=BOTH, expand = 1) self.msgWindow = Entry(self.parent) self.msgWindow.insert(0, 'Your Turn') self.msgWindow.pack() self.canvas.bind("<Button-1>", self.__cell_clicked) def __repaint(self): for i in xrange(self.game.height): for j in xrange(self.game.mid_width_max): if self.game.board[i][j] == 1: self.canvas.create_oval(margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="blue", width=2) elif self.game.board[i][j] == 2: self.canvas.create_oval(margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="red", width=2) elif self.game.board[i][j] == 0: self.canvas.create_oval(margin + j * pieceSize + spacing, margin + i * pieceSize + spacing, margin + (j + 1) * pieceSize - spacing, margin + (i + 1) * pieceSize - spacing, outline="black", fill="grey", width=2) def __draw_cursor(self): self.canvas.delete("cursor") self.canvas.delete("possible_moves") if self.row >= 0 and self.col >= 0: if self.game.board[self.row][self.col] == 1: self.canvas.create_oval(margin + self.col * pieceSize + spacing, margin + self.row * pieceSize + spacing, margin + (self.col + 1) * pieceSize - spacing, margin + (self.row + 1) * pieceSize - spacing, outline="green", fill="blue", width=2, tags="cursor") elif self.game.board[self.row][self.col] == 2: self.canvas.create_oval(margin + self.col * pieceSize + spacing, margin + self.row * pieceSize + spacing, margin + (self.col + 1) * pieceSize - spacing, margin + (self.row + 1) * pieceSize - spacing, outline="green", fill="red", width=2, tags="cursor") elif self.game.board[self.row][self.col] == 0: self.canvas.create_oval(margin + self.col * pieceSize + spacing, margin + self.row * pieceSize + spacing, margin + (self.col + 1) * pieceSize - spacing, margin + (self.row + 1) * pieceSize - spacing, outline="green", fill="grey", width=2, tags="cursor") def __draw_possible_moves(self, possibleMoves): for (newi, newj) in possibleMoves: if self.game.board[newi][newj] == 0: self.canvas.create_oval(margin + newj * pieceSize + spacing, margin + newi * pieceSize + spacing, margin + (newj + 1) * pieceSize - spacing, margin + (newi + 1) * pieceSize - spacing, outline="yellow", fill="grey", width=2, tags="possible_moves") def __cell_clicked(self, event): x, y = event.x, event.y if (margin < x < self.WIDTH - margin and margin < y < self.HEIGHT - margin): self.canvas.focus_set() # get row and col numbers from x,y coordinates row, col = (y - margin) / pieceSize, (x - margin) / pieceSize # if cell was selected already - deselect it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 #self.__draw_cursor(0) elif self.game.board[row][col] != -1: self.row, self.col = row, col if (self.row, self.col) not in self.possibleMoves: ## draw selected cells self.__draw_cursor() ## draw possible moves if self.row != -1 and self.col != -1: self.oldi = self.row self.oldj = self.col if self.turn == 1: moves = computeLegalMoveSpecify(self.game, 1, self.row, self.col) elif self.turn == 2: moves = computeLegalMoveSpecify(self.game, 2, self.row, self.col) self.possibleMoves = [(newi, newj) for (oldi, oldj, newi, newj) in moves] print self.possibleMoves self.__draw_possible_moves(self.possibleMoves) else: self.game = self.game.takeMove((self.oldi, self.oldj, self.row, self.col)) self.row, self.col = -1, -1 self.possibleMoves = [] self.turn = 3 - self.turn msg = 'Computing' self.msgWindow.delete(0, END) self.msgWindow.insert(0, msg) self.__repaint() time.sleep(1) if self.turn == 2: move = findMove_Advanced(self.game, self.turn, self.weights, self.depth, self.cantGo) print move self.cantGo.append(move) if (len(self.cantGo) >= 5) : self.cantGo.pop(0) self.game = self.game.takeMove(move) self.__repaint() if self.game.isEnd() == 1: msg = 'You won!' elif self.game.isEnd() == 2: msg = 'You lost!' else: msg = 'Your Turn!' self.msgWindow.delete(0, END) self.msgWindow.insert(0, msg) self.row, self.col = -1, -1 self.possibleMoves = [] self.turn = 3 - self.turn
class SudokuUI(Frame): """ Sudoku grid UI class. Adapted from: http://newcoder.io/gui/part-4/ """ def __init__(self, parent, board): self.game = board Frame.__init__(self, parent) self.parent = parent self.row, self.col = -1, -1 self.new_entry = None self.previous_guess = None self.game_state = 0 self.scores = [] self.__initUI() def __initUI(self): """ Initialize sudoku playing field grid and bind clicking and entry handlers.""" self.parent.title("Sudoku") self.pack(fill=BOTH, expand=1) self.canvas = Canvas(self, width=WIDTH, height=HEIGHT) self.canvas.grid(row=1, column=0) self.__draw_grid() self.__draw_puzzle() self.__draw_scores() self.canvas.bind("<Button-1>", self.__cell_clicked) self.canvas.bind("<Key>", self.__key_pressed) def __draw_grid(self): """ Draw Sudoku 3x3 x 3x3 grid.""" for i in xrange(10): color = "blue" if i % 3 == 0 else "gray" x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line(x0, y0, x1, y1, fill=color) x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) def __draw_puzzle(self): """ Draw Sudoku solution numbers.""" self.canvas.delete("numbers") for i in xrange(9): for j in xrange(9): answer = self.game.board[i][j] if answer != 0: x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 self.canvas.create_text(x, y, text=answer, tags="numbers") def __cell_clicked(self, event): """ Handle a single cell click and highlight the clicked cell. :param event: """ x, y = event.x, event.y if MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN: self.canvas.focus_set() # get row and col numbers from x,y coordinates row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE # if cell was selected already - deselect it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 elif self.game.board[row][col] == 0: self.row, self.col = row, col self.__draw_cursor() def __draw_cursor(self): """ Draw the red outline for the selected square.""" self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 self.canvas.create_rectangle(x0, y0, x1, y1, outline="green", tags="cursor") def __key_pressed(self, event): """ Handle solution number entry.""" if self.row >= 0 and self.col >= 0 and event.char in "123456789" and self.game_state == 1: self.new_entry = (self.row, self.col, int(event.char)) self.col, self.row = -1, -1 self.__draw_puzzle() self.__draw_cursor() def __draw_scores(self): self.score_list = Treeview(self, columns=('name', 'score')) self.score_list['show'] = 'headings' self.score_list.heading('name', text='Name') self.score_list.column('name', width=80, anchor=CENTER) self.score_list.heading('score', text='Score') self.score_list.column('score', width=80, anchor=CENTER) self.score_list.grid(row=1, column=1, padx=(0, 20)) def __update_scores(self, scores): self.score_list.delete(*self.score_list.get_children()) for entry in scores: self.score_list.insert('', 'end', values=(entry[0], entry[1])) def show_winner(self, score, user_id): if score[2] == user_id: winner_text = "YOU HAVE WON!" else: winner_text = "Player " + str(score[0]) + " has won!" tkMessageBox.showwarning("A WINNER IS FOUND", winner_text) def update_board(self, root, board, scores, new_game_state): """ Update board during the gameplay. If all players are not connected, solution entry is not permitted. In case of a wrong answer the selected square is flashed red for a fraction of a second to notify the player about his life decisions. :param root: :param board: :param new_game_state: :return entered value: """ return_val = None self.__update_scores(scores) # Check for game state, if it is "0", just return, else continue the game if self.game_state == 0: root.update() if new_game_state != 0: self.game_state = new_game_state return return_val # If previous guess was not correct flash it red if self.previous_guess is not None and board[self.previous_guess[0]][self.previous_guess[1]] != \ self.previous_guess[2]: row, col, _ = self.previous_guess x0 = MARGIN + col * SIDE + 1 y0 = MARGIN + row * SIDE + 1 x1 = MARGIN + (col + 1) * SIDE - 1 y1 = MARGIN + (row + 1) * SIDE - 1 self.canvas.create_rectangle(x0, y0, x1, y1, fill="red", tags="fail") else: self.canvas.delete("fail") # Initiate return value to none, update the board and draw it self.game.update_board(board) self.__draw_puzzle() root.update() # If user has entered anything in between, write it into the return value and previous guess and return if self.new_entry is not None: return_val = self.new_entry self.previous_guess = self.new_entry self.new_entry = None else: self.previous_guess = None return return_val
class GameWindow(Frame): def __init__(self, parent, controller): Frame.__init__(self, parent) self.parent = parent self.watchdog_socket = None self.is_first_time = True self.is_alive = True self.play_again = False self.controller = controller self.root = self self.keys = {} self.items = [] self.extents = None # field size self.background = None # the following variables are needed for arrow key servicing # <<<<< self.amove = 'STAY' self.ajump = 0 self.movereg = 0 self.bup = 0 self.bdown = 0 self.bleft = 0 self.bright = 0 self.sprint = 1 self.direction = None self.next_move = 'STAY' self.up_key = False self.down_key = False self.right_key = False self.left_key = False self.up_key_count = 0 self.down_key_count = 0 self.right_key_count = 0 self.left_key_count = 0 self.local_class = "SPECTATOR" # >>>>> # Everything that is visible in the side bar # Info: Server, Nickname, Class, Score, Time # Buttons: try again, leave # <<<<<< sidebar = Frame(self) sidebar.pack(side='left',fill='y') opts_frame = Frame(sidebar) GameWindow.vSer = StringVar() self.vN = StringVar() self.vC = StringVar() self.vS = StringVar() self.vT = StringVar() i = 0 label00 = Label(opts_frame, text='Server: ') label00.grid(row=i,column=0) self.labe01 = Label(opts_frame, textvariable=GameWindow.vSer) self.labe01.grid(row=i,column=1) i=i+1 if self.vC != "SPECTATOR": label1 = Label(opts_frame, text='Nickname: ') label1.grid(row=i,column=0) self.label2 = Label(opts_frame, textvariable=self.vN) self.label2.grid(row=i,column=1) i=i+1 label3 = Label(opts_frame, text='Class: ') label3.grid(row=i,column=0) self.label4 = Label(opts_frame, textvariable=self.vC) self.label4.grid(row=i,column=1) i=i+1 label5 = Label(opts_frame, text='Score: ') label5.grid(row=i,column=0) self.label6 = Label(opts_frame, textvariable=self.vS) self.label6.grid(row=i,column=1) i=i+1 label7 = Label(opts_frame, text="Time") label7.grid(row=i,column=0) self.label8 = Label(opts_frame, textvariable=self.vT) self.label8.grid(row=i,column=1) opts_frame.pack(side='top',fill='x', ) self.leave_button = Button(sidebar, text="Try again!") self.leave_button.pack(side='top',fill='x') self.leave_button.config(command=self._again) self.leave_button = Button(sidebar, text="Leave") self.leave_button.pack(side='top',fill='x') self.leave_button.config(command=self._leave) # >>>>>> # bind arrow key events to the canvas which is used to show the game field self.canvas = Canvas(self) self.canvas.bind('<KeyPress>', self.kp) self.canvas.bind('<KeyRelease>', self.kr) self.canvas.bind("<1>", lambda event: self.canvas.focus_set()) self.canvas.pack(expand=True,fill='both') # List info: players on the game field self.players_list = Text(sidebar, width=20, state=DISABLED) self.players_list.pack(side='top',expand=True, fill='both') # Start the scheduler self._tick() # key press def kp(self,event): if event.keysym == 'Up': self.direction = UP self.up_key = True self.up_key_count += 1 elif event.keysym == 'Down': self.direction = DOWN self.down_key = True self.down_key_count += 1 elif event.keysym == 'Left': self.direction = LEFT self.left_key = True self.left_key_count += 1 # 0 is do not move elif event.keysym == 'Right': self.direction = RIGHT self.right_key = True self.right_key_count += 1 elif event.keysym == 'Shift_L' or event.keysym == 'Shift_R': if ChooseType.plx_type == "FROG": self.sprint = 2 # key release def kr(self,event): if event.keysym == 'Up': self.up_key = False elif event.keysym == 'Down': self.down_key = False elif event.keysym == 'Left': self.left_key = False elif event.keysym == 'Right': self.right_key = False elif event.keysym == 'Shift_L' or event.keysym == 'Shift_R': if ChooseType.plx_type == "FROG": self.sprint = 1 def prepare_move(self): if ChooseType.plx_type != 'SPECTATOR': # UP if self.direction == UP: if self.sprint == 1: self.next_move = 'UP' elif self.sprint == 2: self.next_move = '2UP' self.up_key_count = 0 # DOWN elif self.direction == DOWN: if self.sprint == 1: self.next_move = 'DOWN' elif self.sprint == 2: self.next_move = '2DOWN' self.down_key_count = 0 # LEFT elif self.direction == LEFT: if self.sprint == 1: self.next_move = 'LEFT' elif self.sprint == 2: self.next_move = '2LEFT' self.left_key_count = 0 # RIGHT elif self.direction == RIGHT: if self.sprint == 1: self.next_move = 'RIGHT' elif self.sprint == 2: self.next_move = '2RIGHT' self.right_key_count = 0 # no motion else: self.direction = 'none' self.next_move = 'STAY' self._send_actions(self.next_move) self.next_move = 'STAY' self.direction = 'none' def update_field(self, extents): "Creates field background." if extents==self.extents: return # Delete the old background if self.background!=None: self.canvas.delete(self.background) self.extents = extents M,N = extents self.background = self.canvas.create_rectangle(0,0,WIDTH*M,WIDTH*N, fill=BACKGROUND_COLOR) def update_objects(self, objects): "Create canvas items from triples: (x,y,class)." oldItems = list(self.items) self.items = [] # add new objects for (i,j,tp) in objects: if tp not in C_CODE: continue size = SIZES[tp] tx = int(j)*WIDTH+(WIDTH-size)/2 ty = int(i)*WIDTH+(WIDTH-size)/2 x = int(tx) y = int(ty) if tp in [RC_FROG]: # the cell is visible, so put background bItem = self.canvas.create_rectangle(x,y,x+WIDTH,y+WIDTH, fill='#ffc', width=0) item = self.canvas.create_oval(x,y,x+size-1,y+size-1, fill='green') # reduce by 1 for outline self.items.extend([bItem,item]) elif tp in [RC_FLY]: # the cell is visible, so put background bItem = self.canvas.create_rectangle(x-S_WIDTH,y-S_WIDTH,x+S2_WIDTH,y+S2_WIDTH, fill='#ffc', width=0) item = self.canvas.create_oval(x,y,x+size-1,y+size-1, fill='black') # reduce by 1 for outline self.items.extend([bItem,item]) elif tp in [RC_ZERO]: item = self.canvas.create_rectangle(x,y,x+size,y+size, fill='#ffc', width=0) self.items.append(item) # delete old objects for item in oldItems: self.canvas.delete(item) def _tick(self): # If the game has started: if ChooseType.start_game: # Check if it's a client's entity if global_state == 0: # If the first time in a particular game, initialize the watch dog. # and start the listening thread. if self.is_first_time: self.is_alive = True self.watchdog_socket = self._init_watchdog(BROADCASTING_PORT, WATCHDOG_PING) wdt = self.server_alive_watchdog_thread(self.watchdog_socket, SERVER_LIST) wdt.start() THREADS.append(wdt) print "Watchdog thread started!" # Wait for the server to be considered online by the watch dog for the first time while not self.is_alive: pass self.is_first_time = False # Check if the server is still running by using the watch dog. if self.is_alive: self._recScope() self.prepare_move() self._recList() else: # If server down - show a pop up message and leave the game. tkMessageBox.showwarning("Game status", "Server down") self._leave() else: if CreateServer.local_world.keep_alive: self._recScope() self.prepare_move() self._recList() # If a player pressed the 'X' window button, leave and destroy the app. if shut_down_level == 2: if self.play_again: ChooseType.start_game = True self._leave() set_shut_down_level(0) self.parent.destroy() self.parent.quit() self.root.after(TICK_TIME, self._tick) # Must be larger than engine's sleep # receive message def _rec_msg(self): buf = ChooseType.socket.recv(100) message = buf.decode('utf-8') return message # play again def _again(self): if ChooseType.start_game == False: return self.play_again = True ChooseType.button4.config(state="disabled") if global_state == 0: self._send_one_word('LEAVE') else: CreateServer.local_world.set_player_attr(ChooseType.create_player, 1, 'character', "SPECTATOR") ChooseType.start_game = False self.controller.show_frame("ChooseType") def _leave(self): if ChooseType.start_game == False: return ChooseType.start_game = False self.play_again = False set_shut_down_level(0) ChooseType.button4.config(state="normal") if global_state == 0: print 'Client trying to leave' self._send_one_word('QUIT') ChooseType.socket.close() # Close the socket print 'Exited succesfully!' self.is_first_time = True # Signal the watchdog thread to end self.is_alive = False else: CreateServer.local_world.keep_alive = False # Kill some threads # Kill the server thread by sending the appropriate message s = socket.socket(AF_INET,SOCK_STREAM) host = globvar[0] s.connect((host,QUAD_AXE)) msg = 'CLOSE_SOCKET' s.send(msg.encode()) s.close() # Remove the client from the list. for client in CLIENTS: CLIENTS.remove(client) # Remove all clients set_global_state(0) self.controller.show_frame("StartPage") # receive the scope def _recScope(self): if ChooseType.start_game == False: return try: if global_state == 0: self._send_one_word("PRINT") buf = ChooseType.socket.recv(BUF_SIZE) scope = pickle.loads(buf) else: scope = CreateServer.local_world.gamefield_scope(ChooseType.create_player) if scope: self._recv_command2(scope) except Exception: if not self.is_alive: ChooseType.socket.close() # receive the player list def _recList(self): if ChooseType.start_game == False: return try: if global_state == 0: self._send_one_word("SCORE") buf = ChooseType.socket.recv(BUF_SIZE) p_list = buf.decode('utf-8') self.process_list(p_list) else: p_list = CreateServer.local_world.get_player_info(ChooseType.create_player) self.process_list(p_list) except Exception: if not self.is_alive: ChooseType.socket.close() # decode the player list def process_list(self,message): m_split = message.split(';') # get player type, if player has died, the serve switches player class to spectator fly_list = [] frog_list = [] player_stats = [] # the lists are created by deciphering the received packet from server for i, c in enumerate(m_split): # code for player list: PL1 if c == "PL1": # fly or forg player list key: PL1;PL2; if m_split[i] == "PL1" and m_split[i+1] == "PL2": # fly list key: PL1;PL2;FLY; if m_split[i+2] == "FLY": # full sequence: PL1;PL2;FLY;NAME;SCORE;TIME for k in range(3,6): fly_list.append(str(m_split[i+k])) # frog list key: PL1;PL2;FROG; if m_split[i+2] == "FROG": # full sequence: PL1;PL2;FROG;NAME;SCORE;TIME for k in range(3,6): frog_list.append(str(m_split[i+k])) # player info key: PL1;PL3; if m_split[i] == "PL1" and m_split[i+1] == "PL3": # full sequence: PL1;PL3;CLASS;NAME;SCORE;TIME for k in range(2,6): player_stats.append(str(m_split[i+k])) if player_stats: self.vN.set(player_stats[1]) self.vC.set(player_stats[0]) self.vS.set(player_stats[2]) self.vT.set(player_stats[3]) else: self.vN.set("") self.vC.set("SPECTATOR") self.vS.set("") self.vT.set("") self._update_list(fly_list,frog_list) # send MOVE action to he server or directly ad it to the engine movement variables def _send_actions(self, actions): if global_state == 0: self._send_two_words('MOVE', actions) else: CreateServer.local_world.set_player_attr(ChooseType.create_player, 1, 'nextmove', actions) # send two word command def _send_one_word(self, com1): if ChooseType.socket==None: return try: if global_state == 0: data = com1 + ";" ChooseType.socket.send(data.encode()) except Exception: if not self.is_alive: ChooseType.socket.close() # send one word command def _send_two_words(self, com1, com2): if ChooseType.socket==None: return try: data = com1+";"+com2 if global_state == 0: ChooseType.socket.send(data.encode()) except Exception: if not self.is_alive: ChooseType.socket.close() # prepare the received game scope for game field rendering def _recv_command2(self, args): objects = [] numrows = len(args) numcols = len(args[0]) for i in range(numrows): for j in range(numcols): cell = args[i][j] objects.append((i,j,cell)) self.update_field((numcols,numrows)) self.update_objects(objects) # sidebar list info : list is divided in tow parts: fly list and frog list def _update_list(self,fly_list,frog_list): self.players_list.config(state=NORMAL) self.players_list.delete("1.0",END) flyc = len(fly_list) frogc = len(frog_list) if fly_list: self.players_list.insert(END,("FLYs<:>Score<:>Time\n")) for i in range(0, flyc, 3): self.players_list.insert(END,(fly_list[i]+": "+fly_list[i+1]+" "+fly_list[i+2]+"\n")) if frog_list: self.players_list.insert(END,("FROGs<:>Score<:>Time\n")) for i in range(0, frogc, 3): self.players_list.insert(END,(frog_list[i]+": "+frog_list[i+1]+" "+frog_list[i+2] +"\n")) self.players_list.config(state=DISABLED) def _init_watchdog(self, port, timeout): '''Initialize the listening watch dog. ''' s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) s.bind(('', port)) s.settimeout(timeout) return s def _server_alive_watchog(self, socket, list_of_servers): '''The watch dog listens if the server is still alive. If not - sets a particular flag. ''' socket.settimeout(WATCHDOG_PING) try: while self.is_alive: message, address = socket.recvfrom(8192) if message == '\DIE': if address == globvar[1]: # If the registered server is the one that broadcasted, then it's alive self.is_alive = False print 'Watchdog thread ended!' socket.close() except Exception: socket.close() def server_alive_watchdog_thread(self, socket, list_of_servers): args = { 'socket':socket, 'list_of_servers':list_of_servers} def server_wd(x,a): self._server_alive_watchog(args['socket'], args['list_of_servers']) name = "Server watchdog thread" t = Thread(target=server_wd, name=name, args=args) return t
class SudokuUI(Frame): """ The Tkinter UI, responsible for drawing the board and accepting user input. """ def __init__(self, parent, game): self.game = game Frame.__init__(self, parent) self.parent = parent self.row, self.col = -1, -1 self.__init_ui() def __init_ui(self): self.parent.title("Sudoku") self.pack(fill=BOTH) self.canvas = Canvas(self, width=WIDTH, height=HEIGHT) self.canvas.pack(fill=BOTH, side=TOP) self.bottom = Frame(self) self.bottom.pack(side=BOTTOM) clear_button = Button(self.bottom, text="Clear answers", command=self.__clear_answers) clear_button.pack(fill=BOTH, side=LEFT) solve_button = Button(self.bottom, text="Solve", command=self.__solve) solve_button.pack(fill=BOTH, side=RIGHT) self.__draw_grid() self.__draw_puzzle() self.canvas.bind("<Button-1>", self.__cell_clicked) self.canvas.bind("<Key>", self.__key_pressed) def __solve(self): solved,self.game.puzzle = self.game.solve() if solved: self.col, self.row = -1, -1 self.__draw_puzzle() self.__draw_cursor() self.game.check_win() else: self.__draw_invalid() def __draw_grid(self): """ Draws grid divided with blue lines into n^2xn^2 squares """ for i in xrange(SUDOKU_SIDE + 1): color = "blue" if i % SUDOKU_DEGREE == 0 else "gray" x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line(x0, y0, x1, y1, fill=color) x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) def __draw_puzzle(self): self.canvas.delete("numbers") for i in xrange(SUDOKU_SIDE): for j in xrange(SUDOKU_SIDE): answer = self.game.puzzle[i][j] if answer != 0: x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 original = self.game.start_puzzle[i][j] color = "black" if answer == original else "sea green" self.canvas.create_text( x, y, text=answer, tags="numbers", fill=color ) def __draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 self.canvas.create_rectangle( x0, y0, x1, y1, outline="red", tags="cursor" ) def __draw_victory(self): # create a oval (which will be a circle) x0 = y0 = MARGIN + SIDE * (float(SUDOKU_SIDE) / 2 - 2) x1 = y1 = MARGIN + SIDE * (float(SUDOKU_SIDE) / 2 + 2) self.canvas.create_oval( x0, y0, x1, y1, tags="victory", fill="dark orange", outline="orange" ) # create text x = y = MARGIN + (float(SUDOKU_SIDE) / 2) * SIDE self.canvas.create_text( x, y, text="Solved!", tags="victory", fill="white", font=("Arial", 32) ) def __draw_invalid(self): # create a oval (which will be a circle) x0 = y0 = MARGIN + SIDE * (float(SUDOKU_SIDE) / 2 - 2) x1 = y1 = MARGIN + SIDE * (float(SUDOKU_SIDE) / 2 + 2) self.canvas.create_oval( x0, y0, x1, y1, tags="victory", fill="dark red", outline="red" ) # create text x = y = MARGIN + (float(SUDOKU_SIDE) / 2) * SIDE self.canvas.create_text( x, y, text="Invalid\nPuzzle!", tags="victory", fill="white", font=("Arial", 32) ) def __draw_wrong(self): # create a oval (which will be a circle) x0 = y0 = MARGIN + SIDE * (float(SUDOKU_SIDE) / 2 - 2) x1 = y1 = MARGIN + SIDE * (float(SUDOKU_SIDE) / 2 + 2) self.canvas.create_oval( x0, y0, x1, y1, tags="victory", fill="dark red", outline="red" ) # create text x = y = MARGIN + (float(SUDOKU_SIDE) / 2) * SIDE self.canvas.create_text( x, y, text="Wrong\nMove!", tags="victory", fill="white", font=("Arial", 32) ) def __cell_clicked(self, event): if self.game.game_over: return x, y = event.x, event.y if MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN: self.canvas.focus_set() # get row and col numbers from x,y coordinates row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE # if cell was selected already - deselect it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 elif self.game.start_puzzle[row][col] == 0: self.row, self.col = row, col else: self.row, self.col = -1, -1 self.__draw_cursor() def __key_pressed(self, event): if self.game.game_over: return if self.row >= 0 and self.col >= 0 and event.char in "1234567890": self.game.puzzle[self.row][self.col] = int(event.char) self.col, self.row = -1, -1 self.__draw_puzzle() self.__draw_cursor() if self.game.check_win(): self.__draw_victory() def __clear_answers(self): self.game.start() self.canvas.delete("victory") self.__draw_puzzle()
class SudokuUI(Frame): def __init__(self, parent, game): self.game = game self.parent = parent Frame.__init__(self, parent) self.row = 0 self.col = 0 self.__initUI__() """ Initialise the UI """ def __initUI__(self): # Title Name self.parent.title("Sudoku") # Fill frame's geometry relative to parent self.pack(fill=BOTH, expand=1) # General purpose widget to display board self.canvas = Canvas(self, width=WIDTH, height=HEIGHT) # Fill the entire space, place at the top self.canvas.pack(fill=BOTH, side=TOP) # Setup button to clear entire board # Reinitialise to starting board setup clear_button = Button(self, text="Clear answers", command=self.__clear_answers) # Fill the entire space, place at the bottom clear_button.pack(fill=BOTH, side=BOTTOM) # Draw the grid self.__draw_grid() # Draw the numbers self.__draw_board() # Callbacks for mouse presses self.canvas.bind("<Button-1>", self.__cell_clicked) self.canvas.bind("<Key>", self.__key_pressed) """ Draws grid divided with blue lines into 3x3 squares """ def __draw_grid(self): # Fill in all 10 lines for i in xrange(10): # Use blue to seperate squares color = "blue" if i % 3 == 0 else "gray" x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN # Create vertical line using margins self.canvas.create_line(x0, y0, x1, y1, fill=color) x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE # Create horizontal line using margins self.canvas.create_line(x0, y0, x1, y1, fill=color) """ Draws the numbers """ def __draw_board(self): # Clear canvas of previously inputed numbers self.canvas.delete("numbers") # Iterate through whole board for i in xrange(9): for j in xrange(9): # Take corresponding starting number position answer = self.game.curr_board[i][j] # If number is present if answer != 0: # Create a box, using margins x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 # If number is present from the start, color black # Otherwise, color green original = self.game.start_board[i][j] color = "black" if answer == original else "green3" # Display the text self.canvas.create_text( x, y, text=answer, tags="numbers", fill=color ) """ Clear game board of answers """ def __clear_answers(self): self.game.start() self.canvas.delete("victory") self.canvas.delete("winner") self.__draw_board() """ Allow for input of numbers after clicking cell """ def __cell_clicked(self, event): # Do nothing if game is completed if self.game.game_over: return # Get x,y coordinates from mouse click x, y = event.x, event.y if (MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN): self.canvas.focus_set() # Get row and col numbers from x,y coordinates row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE # If cell was selected already - deselect it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 elif self.game.curr_board[row][col] == 0: self.row, self.col = row, col self.__draw_cursor() """ Takes self.row and self.col and highlight the corresponding cell """ def __draw_cursor(self): # Set to cursor self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 # Draw rectangle at corresponding row,col self.canvas.create_rectangle( x0, y0, x1, y1, outline="red", tags="cursor" ) """ Handles user input """ def __key_pressed(self, event): # If game is complete, do nothing if self.game.game_over: return # If box is highlighted, and input is numerical (1,9) if self.row >= 0 and self.col >= 0 and event.char in "1234567890": # Update the current board self.game.curr_board[self.row][self.col] = int(event.char) # Deselect cell self.col, self.row = -1, -1 # Redraw the board self.__draw_board() # Remove cursor box self.__draw_cursor() # Check for finished game state if self.game.check_win(): # Draw victory self.__draw_victory() """ Draw the victory end game screen """ def __draw_victory(self): # Create a circle x0 = y0 = MARGIN + SIDE * 2 x1 = y1 = MARGIN + SIDE * 7 # Fill circle with victory tag self.canvas.create_oval( x0, y0, x1, y1, tags="victory", fill="dark orange", outline="orange" ) # Add victory text x = y = MARGIN + 4 * SIDE + SIDE / 2 self.canvas.create_text( x, y, text="You win!", tags="winner", fill="white", font=("Arial", 32) )
class KakuroUI(Frame): """ The Tkinter UI: draw the board and accept input """ def __init__(self, parent, game): self.game = game Frame.__init__(self, parent) self.parent = parent self.row, self.col = -1, -1 self.initUI() def initUI(self): if self.game.gameId != 0: self.parent.title("Kakuro | Puzzle: " + str(self.game.gameId)) else: self.parent.title("Kakuro | Puzzle: Custom") self.pack(fill=BOTH, expand=1) self.canvas = Canvas(self, width=WIDTH, height=HEIGHT, highlightthickness=0) self.canvas.pack(fill=BOTH, side=TOP) solve_button = Button(self, text="Solve!", command=self.solve) solve_button.pack(side=RIGHT, padx=10) clear_button = Button(self, text="Clear answers", command=self.clear_answers) clear_button.pack(side=RIGHT) self.draw_grid() self.draw_puzzle() self.canvas.bind("<Button-1>", self.cell_clicked) self.canvas.bind("<Up>", self.Upkey_pressed) self.canvas.bind("<Down>", self.Downkey_pressed) self.canvas.bind("<Right>", self.Rightkey_pressed) self.canvas.bind("<Left>", self.Leftkey_pressed) self.canvas.bind("<BackSpace>", self.Bkspkey_pressed) self.canvas.bind("<Key>", self.key_pressed) def draw_grid(self): for i in range(10): stretch = 0 if i % 9 == 0: stretch = 1 self.canvas.create_line(MARGIN + i * SIDE, MARGIN - stretch, MARGIN + i * SIDE, HEIGHT - MARGIN + stretch, width=2) self.canvas.create_line(MARGIN, MARGIN + i * SIDE, WIDTH - MARGIN, MARGIN + i * SIDE, width=2) for i in range(9): for j in range(9): if [i, j] not in self.game.data_fills: self.canvas.create_rectangle(MARGIN + j * SIDE + 1, MARGIN + i * SIDE + 1, MARGIN + j * SIDE + SIDE - 2, MARGIN + i * SIDE + SIDE - 2, outline="gray", fill="gray", tag="grays") self.canvas.create_line(MARGIN + j * SIDE, MARGIN + i * SIDE, MARGIN + j * SIDE + SIDE, MARGIN + i * SIDE + SIDE, width=2, tag="grayliners") def draw_puzzle(self): self.canvas.delete("numbersfilled") for elem in self.game.data_totals: i = elem[3][0] j = elem[3][1] if elem[1] == 'v': modif = -1 else: modif = 1 self.canvas.create_text( MARGIN + j * SIDE + SIDE / 2 + modif * SIDE / 4, MARGIN + i * SIDE + SIDE / 2 + (-modif) * SIDE / 4, text=elem[0], tags="numbers", fill="black") for elem in self.game.data_filled: i = elem[0] j = elem[1] self.canvas.create_text(MARGIN + j * SIDE + SIDE / 2, MARGIN + i * SIDE + SIDE / 2, font=("Purissa", 20), text=elem[2], tags="numbersfilled", fill="slate gray") def draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: self.canvas.create_rectangle(MARGIN + self.col * SIDE + 1, MARGIN + self.row * SIDE + 1, MARGIN + (self.col + 1) * SIDE - 1, MARGIN + (self.row + 1) * SIDE - 1, outline="red", tags="cursor") def create_circs(self, addrs): if len(addrs) == 0: return for elem in addrs: self.canvas.create_oval( # Figure out the inversion! MARGIN + SIDE * elem[1], MARGIN + SIDE * elem[0], MARGIN + SIDE * elem[1] + SIDE, MARGIN + SIDE * elem[0] + SIDE, tags="circ", outline="red", width=2.0) def draw_victory(self): self.canvas.create_oval(MARGIN + SIDE * 2, MARGIN + SIDE * 2, MARGIN + SIDE * 7, MARGIN + SIDE * 7, tags="victory", fill="dark orange", outline="orange") self.canvas.create_text(MARGIN + 4 * SIDE + SIDE / 2, MARGIN + 4 * SIDE + SIDE / 2, text="Correct!", tags="victory", fill="white", font=("Ubuntu", 32)) def cell_clicked(self, event): if self.game.game_over: return x, y = event.x, event.y if (x > MARGIN and x < WIDTH - MARGIN and y > MARGIN and y < HEIGHT - MARGIN): self.canvas.focus_set() row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE self.row, self.col = row, col else: self.row, self.col = -1, -1 self.draw_cursor() def road(self, addr): if bool(addr[0] == self.row) == bool(addr[1] == self.col): return False elif addr[0] == self.row: curr_row = self.row for iter in range(min(addr[1], self.col), max(addr[1], self.col)): if [curr_row, iter] not in self.game.data_fills: return False return True else: curr_col = self.col for iter in range(min(addr[0], self.row), max(addr[0], self.row)): if [iter, curr_col] not in self.game.data_fills: return False return True def key_pressed(self, event): self.canvas.delete("victory") self.canvas.delete("circ") if self.game.game_over: return if self.row >= 0 and self.col >= 0 and event.char in "123456789" and event.char != '' and [ self.row, self.col ] in self.game.data_fills: found_flag = False for ind, item in enumerate(self.game.data_filled): if item[0] == self.row and item[1] == self.col: found_flag = True self.game.data_filled[ind][2] = int(event.char) if (not found_flag): self.game.data_filled = self.game.data_filled + [[ self.row, self.col, int(event.char) ]] circlists = [] for elem in self.game.data_filled: if self.road(elem) and elem[2] == int(event.char): if [self.row, self.col] not in circlists: circlists = circlists + [[self.row, self.col]] if [elem[0], elem[1]] not in circlists: circlists = circlists + [[elem[0], elem[1]]] self.create_circs(circlists) self.draw_puzzle() self.draw_cursor() if self.game.check_win(): self.draw_victory() def Upkey_pressed(self, event): self.canvas.delete("victory") if self.game.game_over: return if self.row > 0 and self.col >= 0: self.row = self.row - 1 self.draw_cursor() def Downkey_pressed(self, event): self.canvas.delete("victory") if self.game.game_over: return if self.row >= 0 and self.col >= 0 and self.row < 8: self.row = self.row + 1 self.draw_cursor() def Rightkey_pressed(self, event): self.canvas.delete("victory") if self.game.game_over: return if self.row >= 0 and self.col >= 0 and self.col < 8: self.col = self.col + 1 self.draw_cursor() def Leftkey_pressed(self, event): self.canvas.delete("victory") if self.game.game_over: return if self.row >= 0 and self.col > 0: self.col = self.col - 1 self.draw_cursor() def Bkspkey_pressed(self, event): self.canvas.delete("victory") self.canvas.delete("circ") if self.game.game_over: return if self.row >= 0 and self.col >= 0: self.game.data_filled = [ item for item in self.game.data_filled if item[0] != self.row or item[1] != self.col ] self.draw_cursor() self.draw_puzzle() def clear_answers(self): self.game.data_filled = [] self.canvas.delete("victory") self.canvas.delete("circ") self.draw_puzzle() def solve(self): self.game.data_filled = [] self.canvas.delete("victory") self.canvas.delete("circ") options = ["1", "2", "3", "4", "5", "6", "7", "8", "9"] # Remember to zero down the indices vals = options rows = options cols = options prob = LpProblem("Kakuro Problem", LpMinimize) choices = LpVariable.dicts("Choice", (vals, rows, cols), 0, 1, LpInteger) # The complete set of boolean choices prob += 0, "Arbitrary Objective Function" # Force singular values. Even for extraneous ones for r in rows: for c in cols: prob += lpSum([choices[v][r][c] for v in vals]) == 1, "" # Force uniqueness in each 'zone' and sum constraint for that zone # Row zones for r in rows: zonecolsholder = [] activated = False zonecolssumholder = 0 for c in cols: if not activated and [int(r) - 1, int(c) - 1 ] in self.game.data_fills: activated = True zonecolsholder = zonecolsholder + [c] zonecolssumholder = [ elem[0] for elem in self.game.data_totals if elem[1] == 'h' and elem[2] == int(r) - 1 and elem[3] == int(c) - 2 ][0] if c == "9": # Uniqueness in that zone of columns for v in vals: prob += lpSum( [choices[v][r][co] for co in zonecolsholder]) <= 1, "" # Sum constraint for that zone of columns prob += lpSum([ int(v) * choices[v][r][co] for v in vals for co in zonecolsholder ]) == zonecolssumholder, "" elif activated and [int(r) - 1, int(c) - 1 ] in self.game.data_fills: zonecolsholder = zonecolsholder + [c] if c == "9": # Uniqueness in that zone of columns for v in vals: prob += lpSum( [choices[v][r][co] for co in zonecolsholder]) <= 1, "" # Sum constraint for that zone of columns prob += lpSum([ int(v) * choices[v][r][co] for v in vals for co in zonecolsholder ]) == zonecolssumholder, "" elif activated and [int(r) - 1, int(c) - 1 ] not in self.game.data_fills: activated = False # Uniqueness in that zone of columns for v in vals: prob += lpSum( [choices[v][r][co] for co in zonecolsholder]) <= 1, "" # Sum constraint for that zone of columns prob += lpSum([ int(v) * choices[v][r][co] for v in vals for co in zonecolsholder ]) == zonecolssumholder, "" zonecolssumholder = 0 zonecolsholder = [] # Col zones for c in cols: zonerowsholder = [] activated = False zonerowssumholder = 0 for r in rows: if not activated and [int(r) - 1, int(c) - 1 ] in self.game.data_fills: activated = True zonerowsholder = zonerowsholder + [r] zonerowssumholder = [ elem[0] for elem in self.game.data_totals if elem[1] == 'v' and elem[2] == int(r) - 2 and elem[3] == int(c) - 1 ][0] if r == "9": # Uniqueness in that zone of rows for v in vals: prob += lpSum( [choices[v][ro][c] for ro in zonerowsholder]) <= 1, "" # Sum constraint for that zone of rows prob += lpSum([ int(v) * choices[v][ro][c] for v in vals for ro in zonerowsholder ]) == zonerowssumholder, "" elif activated and [int(r) - 1, int(c) - 1 ] in self.game.data_fills: zonerowsholder = zonerowsholder + [r] if r == "9": # Uniqueness in that zone of rows for v in vals: prob += lpSum( [choices[v][ro][c] for ro in zonerowsholder]) <= 1, "" # Sum constraint for that zone of rows prob += lpSum([ int(v) * choices[v][ro][c] for v in vals for ro in zonerowsholder ]) == zonerowssumholder, "" elif activated and [int(r) - 1, int(c) - 1 ] not in self.game.data_fills: activated = False # Uniqueness in that zone of rows for v in vals: prob += lpSum( [choices[v][ro][c] for ro in zonerowsholder]) <= 1, "" # Sum constraint for that zone of rows prob += lpSum([ int(v) * choices[v][ro][c] for v in vals for ro in zonerowsholder ]) == zonerowssumholder, "" zonerowssumholder = 0 zonerowsholder = [] # Force all extraneous values to 1 (arbitrary) | Possibly many times for ite in self.game.data_totals: prob += choices["1"][str(ite[2] + 1)][str(ite[3] + 1)] == 1, "" # Suppress calculation messages GLPK(msg=0).solve(prob) # Solution: The commented print statements are for debugging aid. for v in prob.variables(): # print v.name, "=", v.varValue if v.varValue == 1 and [int(v.name[9]) - 1, int(v.name[11]) - 1 ] in self.game.data_fills: # print v.name, ":::", v.varValue, [int(v.name[9])-1, int(v.name[11])-1, int(v.name[7])] self.game.data_filled = self.game.data_filled + [[ int(v.name[9]) - 1, int(v.name[11]) - 1, int(v.name[7]) ]] self.draw_puzzle()
class Sudoku: def __init__(self): self.frame = Tkinter.Tk() self.frame.geometry("%dx%d" % (WIDTH + 300, HEIGHT + 40)) self.frame.title("Sudoku") self.canvas = Canvas(width=WIDTH, height=HEIGHT, highlightthickness=0) self.canvas.pack(fill=BOTH, side=TOP) self.draw_grid() self.button = Button(text="ReStart", command=self.reStart) self.button.pack(side=BOTTOM) self.draw_puzzle() self.selection_board() self.canvas.bind("<Button-1>", self.setFocus) self.canvas.bind("<BackSpace>", self.cleanCell) self.x = -1 self.y = -1 self.tag = {} self.frame.mainloop() pass def cleanCell(self,event): tag = str(self.x)+str(self.y) if (tag in self.tag): self.canvas.delete(self.tag[tag]) global board board[self.x][self.y] = 0 self.canvas.delete("focus-ring") self.canvas.delete('candidate') del self.tag[tag] pass def makeSelection(self, x, y): row = (y - MARGIN) / RECT column = (x - SELECT_START) / RECT if selectable[row][column] == True: self.updateBoard(select_board[row][column]) pass def updateBoard(self, num): # if self.canvas.find() # self.canvas.delete(str(self.x)+str(self.y)) t = str(self.x) + str(self.y) if t in self.tag: self.canvas.delete(self.tag[t]) item= self.canvas.create_text( MARGIN + self.y * RECT + RECT / 2, MARGIN + self.x * RECT + RECT / 2, text=num, fill='black',tag=t ) self.tag[t] = item self.canvas.delete('candidate') self.canvas.delete('focus-ring') global board board[self.x][self.y] = num initBoard(selectable, False) if self.check_success() == True: tkMessageBox.showinfo("Sudoku", "You Win!") pass def check_success(self): i = 0 j = 0 global board while i < 9: while j < 9: if board[i][j] == 0: return False j += 1 j = 0 i += 1 pass def selection_board(self): for i in xrange(4): self.canvas.create_line(MARGIN + WIDTH, MARGIN + RECT * i, WIDTH + MARGIN + 3 * RECT, MARGIN + RECT * i, fill='blue' if i % 3 == 0 else 'gray') self.canvas.create_line(WIDTH + MARGIN + i * RECT, MARGIN, WIDTH + MARGIN + i * RECT, MARGIN + 3 * RECT, fill='blue' if i % 3 == 0 else 'gray') pass num = 1 for i in xrange(3): for j in xrange(3): self.canvas.create_text(MARGIN + WIDTH + j * RECT + RECT / 2, MARGIN + i * RECT + RECT / 2, text=num) num += 1 pass def setFocus(self, event): x = event.x y = event.y if x > SELECT_START and x < SELECT_START + SELECT_WIDTH and y < MARGIN + 3 * RECT and y > MARGIN: self.makeSelection(x, y) row = (y - MARGIN) / RECT column = (x - MARGIN) / RECT # if click outside the board. if row >= 9 or column >= 9: return self.canvas.delete('focus-ring') if modifyable[row][column] == False: self.canvas.delete('candidate') self.x = -1 self.y = -1 initBoard(selectable,False) return self.canvas.create_rectangle(MARGIN + column * RECT, MARGIN + row * RECT, MARGIN + (column + 1) * RECT, MARGIN + (row + 1) * RECT, outline='red', tags='focus-ring') self.x = row self.y = column # this is important for key-board binding self.canvas.focus_set() self.select() pass def select(self): valid = [] for i in xrange(1, 10): if isValid(i, self.x, self.y): valid.append(i) if len(valid) != 0: self.updateSelectionBorad(valid) pass def updateSelectionBorad(self, validList): self.canvas.delete("candidate") for num in validList: i = (num - 1) / 3 j = (num - 1) % 3 self.canvas.create_rectangle( SELECT_START + j * RECT, MARGIN + i * RECT, SELECT_START + (j + 1) * RECT, MARGIN + (i + 1) * RECT, outline='green', tag='candidate' ) selectable[i][j] = True pass def draw_grid(self): for i in xrange(10): # draw vertical line self.canvas.create_line(MARGIN, MARGIN + RECT * i, MARGIN + 9 * RECT, MARGIN + RECT * i, fill='blue' if i % 3 == 0 else 'gray') self.canvas.create_line(MARGIN + i * RECT, MARGIN, MARGIN + i * RECT, HEIGHT - MARGIN, fill='blue' if i % 3 == 0 else 'gray') pass def draw_puzzle(self): for i in xrange(9): for j in xrange(9): num = default_date[i][j] num = int(num) if num != 0: self.canvas.create_text(MARGIN + j * RECT + RECT / 2, MARGIN + i * RECT + RECT / 2, text=num, fill='black') modifyable[i][j] = False pass pass pass pass def reStart(self): self.clean() pass def clean(self): for t in self.tag: self.canvas.delete(self.tag[t]) self.text = {} self.x = -1 self.y = -1 global_init() initBoard(selectable, False)
class AppAnalysis: def __init__(self, root): self.canvas = Canvas(root, width = 400, height = 350) self.canvas.configure(cursor="crosshair") self.canvas.pack(expand=YES, fill=BOTH, side='right') self.canvas.bind("<Key>", self.handle_key) self.canvas.bind("<Double-Button-1>", self.set_focus) self.canvas.bind("<Button-1>", self.set_cursor) self.canvas.bind("<Return>", self.remove_highlight) self.image, self.ponto1, self.ponto2 = (None, None, None) self.menubar = Menu(root) filemenu = Menu(self.menubar, tearoff=0) filemenu.add_command(label="Open Image", command=self.openImage) filemenu.add_command(label="Save", command=self.hello) filemenu.add_separator() filemenu.add_command(label="Exit", command=root.quit) self.menubar.add_cascade(label="File", menu=filemenu) editmenu = Menu(self.menubar, tearoff=0) for e in ("Cut","Copy","Paste"): editmenu.add_command(label=e, command=self.hello) self.menubar.add_cascade(label="Edit", menu=editmenu) filtermenu = Menu(self.menubar, tearoff=0) filtermenu.add_command(label="Threshold", command=self.thresholdFilter) self.menubar.add_cascade(label="Filter", menu=filtermenu) reportmenu = Menu(self.menubar, tearoff=0) reportmenu.add_command(label="Relatorio.txt", command=self.generateReport) reportmenu.add_command(label="Relatorio.pdf") reportmenu.add_command(label="Email") self.menubar.add_cascade(label="Report", menu=reportmenu) helpmenu = Menu(self.menubar, tearoff=0) helpmenu.add_command(label="About", command=self.hello) self.menubar.add_cascade(label="Help", menu=helpmenu) root.config(menu=self.menubar) self.toolbar = Frame(root) self.toolbar.pack(side='left', fill='both') clean = Label(self.toolbar, text='Clean') clean.bind("<Button-1>", self.clean) b = Label(self.toolbar, text='B') c = Label(self.toolbar, text='C') d = Label(self.toolbar, text='D') for w in (clean,b,c,d): w.configure(relief="groove", font="Times 12 bold") w.pack(fill='both') def openImage(self): arquivo = tkFileDialog.askopenfile(parent=self.canvas,mode='rb', title='Imagem') e = ['GIF','JPEG','JPG','BMP','PNG','TIF'] if(e.__contains__(arquivo.name.split(".")[-1].upper())): self.ponto1, self.ponto2 = (None,None) img_tmp = Image.open(arquivo) #self.img_name = path.dirname(path.abspath(arquivo.name)) self.img_name = arquivo.name print self.img_name self.new_img_name = arquivo.name.split('/')[-1] + "_tmp.gif" pathtemp = mydir +"/temp/"+ self.new_img_name img_tmp.save(pathtemp) self.image = PhotoImage(file=pathtemp) self.setImage() self.canvas.bind("<Button-1>", self.click) self.proporcao = "" def clean(self, event): self.ponto1, self.ponto2 = (None,None) self.setImage() self.proporcao = "" def setImage(self): self.canvas.delete(ALL) if self.image.width() > 200 and self.image.height > 200: self.canvas.config(width = self.image.width()) self.canvas.config(height = self.image.height()) self.canvas.create_image(0, 0, image=self.image, anchor=NW) def generateReport(self): report = GeradorRelatorio(self.img_name) report.start() def hello(self): print "hello!" def thresholdFilter(self): img = Image.open(self.img_name) new_img = img.filter(ImageFilter.BLUR) aux = mydir +"/temp/"+ self.new_img_name new_img.save(aux) self.image = PhotoImage(file=aux) self.setImage() def click(self, event): if not self.ponto1: self.canvas.create_oval(event.x, event.y, event.x+5, event.y+5, fill="red") self.ponto1 = (event.x,event.y) else: if not self.ponto2: self.canvas.create_oval(event.x, self.ponto1[1], event.x+5, self.ponto1[1]+5, fill="red") self.ponto2 = (event.x,self.ponto1[1]) pontos = [self.ponto1[0]+1,self.ponto1[1]+2, self.ponto2[0]+1,self.ponto2[1]+2] self.canvas.create_line(pontos, tags="theline", fill='red') x = (self.ponto2[0] + self.ponto1[0]) / 2 self.canvas.create_text(x, self.ponto1[1]+8, text="1 umm") def remove_highlight(self,event): self.canvas.delete("highlight") def highlight(self, item): bbox = self.canvas.bbox(item) self.canvas.delete("highlight") if bbox: i = self.canvas.create_rectangle( bbox, fill="white", tag="highlight" ) self.canvas.lower(i, item) def has_focus(self): return self.canvas.focus() def has_selection(self): return self.canvas.tk.call(self.canvas._w, 'select', 'item') def set_focus(self, event): if self.canvas.type(CURRENT) != "text": return self.highlight(CURRENT) self.canvas.focus_set() self.canvas.focus(CURRENT) self.canvas.select_from(CURRENT, 0) self.canvas.select_to(CURRENT, END) def set_cursor(self, event): item = self.has_focus() if not item: return x = self.canvas.canvasx(event.x) y = self.canvas.canvasy(event.y) self.canvas.icursor(item, "@%d,%d" % (x, y)) self.canvas.select_clear() def handle_key(self, event): item = self.has_focus() if not item: return insert = self.canvas.index(item, INSERT) if event.char >= " ": if self.has_selection(): self.canvas.dchars(item, SEL_FIRST, SEL_LAST) self.canvas.select_clear() self.canvas.insert(item, "insert", event.char) self.highlight(item) elif event.keysym == "BackSpace": if self.has_selection(): self.canvas.dchars(item, SEL_FIRST, SEL_LAST) self.canvas.select_clear() else: if insert > 0: self.canvas.dchars(item, insert-1, insert) self.highlight(item) elif event.keysym == "Home": self.canvas.icursor(item, 0) self.canvas.select_clear() elif event.keysym == "End": self.canvas.icursor(item, END) self.canvas.select_clear() elif event.keysym == "Right": self.canvas.icursor(item, insert+1) self.canvas.select_clear() elif event.keysym == "Left": self.canvas.icursor(item, insert-1) self.canvas.select_clear() else: pass
class GcFrame(LabelFrame): def __init__(self, root, filename, contents, settings, logger, *arg): LabelFrame.__init__(self, root, *arg, text="gcode Viewer") self.label = Label(self) self.canvas = Canvas(self, width=settings.buildarea[0]*SCALE, height=settings.buildarea[1]*SCALE, bd=2, relief=RIDGE, bg="black") self.canvas.config(takefocus="1") self.root = root self.settings = settings self.buildarea = settings.buildarea self.log = logger self.zoom = 1 self.offsetx = 0 self.offsety = 0 self.movestartx = 0 self.movestarty = 0 self.sb = Scrollbar(self) self.sb.config(command=self.scroll) self.sb.set(0.0, 1.0) self.sbslidersize = 1.0 self.syncwithprint = IntVar() self.syncwithprint.set(1) self.bZoomOut = Button(self, text="-", width=3, command=self.doZoomOut) self.bZoomIn = Button(self, text="+", width=3, command=self.doZoomIn) self.cb = Checkbutton(self, text="Sync view with print", variable=self.syncwithprint, command=self.syncClick) self.bReset = Button(self, text="Reset View", command=self.pressReset) self.canvas.bind("<Button-1>", self.startMove); self.canvas.bind("<B1-Motion>", self.continueMove); self.canvas.bind("<MouseWheel>", self.mouseWheel); self.canvas.bind("<Button-4>", self.mouseWheel); self.canvas.bind("<Button-5>", self.mouseWheel); self.canvas.bind("<Shift-MouseWheel>", self.mouseWheelZoom); self.canvas.bind("<Shift-Button-4>", self.mouseWheelZoom); self.canvas.bind("<Shift-Button-5>", self.mouseWheelZoom); self.label.grid(row=1, column=1, columnspan=5) self.canvas.grid(row=2,column=1,columnspan=5) self.sb.grid(row=2, column=6, sticky=N+S) self.bZoomOut.grid(row=3, column=1, sticky=E) self.bZoomIn.grid(row=3, column=2, sticky=W) self.cb.grid(row=3, column=3, columnspan=2) self.bReset.grid(row=3, column=5, columnspan=2) self.currentlayer = None self.currentdrawn = None self.currentlx = None self.layercount = 0 self.filename = None self.printprogress = 0 self.minline = 0 self.maxline = 0 self.drawGrid() self.model = Model() if filename != None: self.loadFile(filename, contents) #self.drawLayer(self.currentlayer) def getPrintStartLine(self): return self.model.getPrintStartLine() def pressReset(self): self.resetView() def startMove(self, e): self.canvas.focus_set() self.movestartx = e.x self.movestarty = e.y self.moveoffsetx = self.offsetx self.moveoffsety = self.offsety def continueMove(self, e): dx = e.x - self.movestartx dy = e.y - self.movestarty self.offsetx = self.moveoffsetx - dx/(2*self.zoom) self.offsety = self.moveoffsety - dy/(2*self.zoom) self.drawCanvas() def updatePrintProgress(self, n, restart=False): if n == 0: self.printprogress = 0 if self.syncwithprint.get() == 1: if restart: self.currentLayer = self.model.bottomLayer() self.currentdrawn = None self.currentlx = 0 self.setSliderPos() self.drawLayer(self.currentlayer) return if n <= self.printprogress: return ll = self.model.findLayersbyLineNo(self.printprogress, n) if len(ll) == 0: return self.printprogress = n if self.syncwithprint.get() == 1: if self.currentlayer != ll[-1]: self.currentlayer = ll[-1] self.currentlx = self.model.getLayerNumber(self.currentlayer) self.drawLayer(self.currentlayer) self.setSliderPos() else: self.drawOneLayer(self.currentlayer, updateonly=True) else: if self.currentlayer in ll: self.drawOneLayer(self.currentlayer, updateonly=True) def scroll(self, *a): if self.currentlx == None: self.currentlx = 0 if a[0] == "scroll": nlx = self.currentlx - int(a[1]) if nlx < 0: nlx = 0 elif nlx >= self.model.countLayers(): nlx = self.model.countLayers()-1 elif a[0] == "moveto": pos = 1.0 - float(a[1]) nlx = int(pos / self.sbslidersize) - 1 if nlx < 0: nlx = 0 elif nlx >= self.model.countLayers(): nlx = self.model.countLayers()-1 else: return self.currentlx = nlx self.currentlayer = self.model.getLayerName(nlx) self.drawLayer(self.currentlayer) self.setSliderPos() def setSliderPos(self): if self.currentlx == None: self.currentlx = 0 sbpos = 1.0 - (self.currentlx * self.sbslidersize) - self.sbslidersize if sbpos < 0.0: sbpos = 0.0 self.sb.set(sbpos, sbpos+self.sbslidersize) def syncClick(self): if self.syncwithprint.get() == 1: self.updatePrintProgress(self.printprogress) def mouseWheel(self, e): if e.num == 5 or e.keycode == -120: #scroll down self.scroll("scroll", 1) elif e.num == 4 or e.keycode == 120: #scroll up self.scroll("scroll", -1) def mouseWheelZoom(self, e): if e.num == 5 or e.keycode == -120: #zoom in self.doZoomIn() elif e.num == 4 or e.keycode == 120: #zoom out if self.zoom > 1: self.doZoomOut() def doZoomIn(self): cw = self.buildarea[0]/self.zoom ch = self.buildarea[1]/self.zoom self.zoom += 1 nw = self.buildarea[0]/self.zoom nh = self.buildarea[1]/self.zoom self.offsetx += (cw-nw)/2 self.offsety += (ch-nh)/2 self.drawCanvas() def doZoomOut(self): if self.zoom > 1: cw = self.buildarea[0]/self.zoom ch = self.buildarea[1]/self.zoom self.zoom -= 1 if self.zoom < 1: self.zoom = 1 nw = self.buildarea[0]/self.zoom nh = self.buildarea[1]/self.zoom self.offsetx -= (nw-cw)/2 self.offsety -= (nh-ch)/2 self.drawCanvas() def loadFile(self, filename, contents): self.filename = filename self.model.addFile(contents) self.currentlayer = self.model.bottomLayer() self.currentlx = 0 self.currentdrawn = None self.printprogress = 0 self.layercount = self.model.countLayers() self.drawLayer(self.currentlayer) if self.layercount > 0: self.sbslidersize = 1.0 / self.layercount self.setSliderPos() def getLayerNumberByHeight(self, z): return self.model.getLayerNumberByHeight(z) def resetView(self): self.zoom = 1 self.offsety = 0 self.offsetx = 0 self.drawCanvas() def drawCanvas(self): self.currentdrawn = None self.drawGrid() self.drawLayer(self.currentlayer) self.setSliderPos() def drawGrid(self): self.canvas.delete("GRID") ltGrey = "#424242" dkGrey = "#404040" yleft = (0 - self.offsety)*self.zoom*SCALE if yleft < 0: yleft = 0 yright = (self.buildarea[1] - self.offsety)*self.zoom*SCALE if yright > self.buildarea[1]*SCALE: yright = self.buildarea[1]*SCALE for x in range(0, self.buildarea[0], 10): if x%50 == 0: c = ltGrey else: c = dkGrey x = (x - self.offsetx)*self.zoom*SCALE if x >= 0 and x <= self.buildarea[0]*SCALE: self.canvas.create_line(x, yleft, x, yright, fill=c, tags="GRID") xtop = (0 - self.offsetx)*self.zoom*SCALE if xtop <0: xtop = 0 xbottom = (self.buildarea[0] - self.offsetx)*self.zoom*SCALE if xbottom > self.buildarea[0]*SCALE: xbottom = self.buildarea[0]*SCALE for y in range(0, self.buildarea[1], 10): if y%50 == 0: c = dkGrey else: c = ltGrey y = (y - self.offsety)*self.zoom*SCALE if y >= 0 and y <= self.buildarea[1]*SCALE: self.canvas.create_line(xtop, y, xbottom, y, fill=c, tags="GRID") def drawLayer(self, layername): if layername == self.currentdrawn: #print "deleting object" #self.canvas.delete("OBJECT") self.drawOneLayer(layername, updateonly=True) else: self.canvas.delete("OBJECT") self.canvas.delete("SHADOW") pl = self.model.prevLayer(layername) if pl and self.settings.showprevious: self.drawOneLayer(pl, background=True) self.drawOneLayer(layername) self.currentdrawn = layername def drawOneLayer(self, layername, background=False, updateonly=False): if not self.model.setLayer(layername): return lx = self.model.getLayerNumber(layername) prev = [None, None] segmentextrusion = 0.0 cx = 0 segments = 0 if background: tag = "SHADOW" else: tag = "OBJECT" if not background and not updateonly: self.label.config(text="file: %.30s z = %.3f (%d/%d)" % (os.path.basename(self.filename), self.model.getLayerHeight(layername), lx+1, self.model.countLayers())) for p in self.model: if prev == [None, None]: prev = [p[0], p[1]] if segments != 0: segments = 0 cx = (cx + 1) % len(colors) else: if p[3] <= segmentextrusion or p[3] == -1: if p[3] == -1: segmentextrusion = 0.0 if not updateonly: if not background and self.settings.showmoves: c = "white" if prev != [p[0], p[1]]: (x1, y1) = self.transform(prev[0], self.buildarea[1]-prev[1]) (x2, y2) = self.transform(p[0], self.buildarea[1]-p[1]) self.canvas.create_line(x1, y1, x2, y2, fill=c, tags=tag) if segments != 0: segments = 0 cx = (cx + 1) % len(colors) else: if prev != [p[0], p[1]]: segments += 1 segmentextrusion = p[3] if background: c = grey f = not updateonly elif self.printprogress >= p[5]: c = "red" f = True else: c = colors[cx] f = not updateonly if f: (x1, y1) = self.transform(prev[0], self.buildarea[1]-prev[1]) (x2, y2) = self.transform(p[0], self.buildarea[1]-p[1]) self.canvas.create_line(x1, y1, x2, y2, fill=c, tags=tag) prev = [p[0], p[1]] def transform(self, ptx, pty): x = (ptx - self.offsetx)*self.zoom*SCALE y = (pty - self.offsety)*self.zoom*SCALE return (x, y)
class SudokuUI(Frame): """ The Tkinter UI, responsible for drawing the board and accepting user input. """ def __init__(self, parent, game): self.game = game Frame.__init__(self, parent) self.parent = parent self.row, self.col = -1, -1 self.__initUI() def __initUI(self): self.parent.title("Sudoku") self.pack(fill=BOTH) self.canvas = Canvas(self, width=WIDTH, height=HEIGHT) self.canvas.pack(fill=BOTH, side=TOP) clear_puzzle_button = Button(self, text="Clear Puzzle", command=self.__clear_puzzle) clear_puzzle_button.pack(side=LEFT, padx=PAD) clear_solution_button = Button(self, text="Clear Solution", command=self.__clear_solution) clear_solution_button.pack(side=LEFT, padx=PAD) solve_button = Button(self, text="Solve", command=self.__solve_puzzle) solve_button.pack(side=LEFT, padx=PAD) count_button = Button(self, text="Count Solutions", command=self.__count_solutions) count_button.pack(side=LEFT, padx=PAD) self.__draw_grid() self.__draw_puzzle() self.canvas.bind("<Button-1>", self.__cell_clicked) self.canvas.bind("<Key>", self.__key_pressed) def __draw_grid(self): """ Draws grid divided with blue lines into 3x3 squares """ for i in xrange(10): color = "blue" if i % 3 == 0 else "gray" x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line(x0, y0, x1, y1, fill=color) x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) def __draw_puzzle(self): self.canvas.delete("numbers") for i in xrange(9): for j in xrange(9): answer = self.game.puzzle[i][j] if answer != 0: x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 original = self.game.start_puzzle[i][j] color = "black" if answer == original else "sea green" self.canvas.create_text(x, y, text=answer, tags="numbers", fill=color) elif self.game.solution is not None: solution = self.game.solution[i][j] if solution != 0: x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 self.canvas.create_text(x, y, text=solution, tags="numbers", fill="purple") def __draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 self.canvas.create_rectangle(x0, y0, x1, y1, outline="red", tags="cursor") def __drawMessage(self, tag, textstr, fillc, outlinec): # create a oval (which will be a circle) x0 = y0 = MARGIN + SIDE * 2 x1 = y1 = MARGIN + SIDE * 7 self.canvas.create_oval(x0, y0, x1, y1, tags=tag, fill=fillc, outline=outlinec) # create text x = y = MARGIN + 4 * SIDE + SIDE / 2 self.canvas.create_text(x, y, text=textstr, tags=tag, fill="white", font=("Arial", 32)) def __draw_victory(self): self.__drawMessage("victory", "You win!", "dark orange", "orange") def __draw_no_solution(self): self.__drawMessage("failure", "No Solution!", "dark red", "red") def __draw_solution_count(self): count = self.game.countSolutions() if count == 0: text = "No solutions." elif count == 1: text = "Unique solution." elif count < ALEPH_NOUGHT: text = "{0} solutions".format(count) else: text = ">= {0} solutions".format(count) self.__drawMessage("count", text, "dark green", "green") def __cell_clicked(self, event): if self.game.game_over: return x, y = event.x, event.y if MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN: self.canvas.focus_set() # get row and col numbers from x,y coordinates row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE # if cell was selected already - deselect it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 #iam: the elif choice makes an entry permanent, not sure why Lynn Root chose that route. #elif self.game.puzzle[row][col] == 0: else: self.row, self.col = row, col else: self.row, self.col = -1, -1 self.__draw_cursor() def __key_pressed(self, event): if self.game.game_over: return if self.row >= 0 and self.col >= 0 and event.char in "1234567890": self.game.puzzle[self.row][self.col] = int(event.char) self.col, self.row = -1, -1 self.__draw_puzzle() self.__draw_cursor() if self.game.check_win(): self.__draw_victory() def __clear_messages(self): for tag in ["victory", "failure", "count"]: self.canvas.delete(tag) def __clear_puzzle(self): self.game.start() self.__clear_messages() self.__draw_puzzle() def __clear_solution(self): self.__clear_messages() self.game.clear_solution() self.__draw_puzzle() def __solve_puzzle(self): if not self.game.solve(): self.__draw_no_solution() self.__draw_puzzle() def __count_solutions(self): self.__draw_solution_count() self.__draw_puzzle()
class SudokuUI(Frame): """ The Tkinter UI, responsible for drawing the board and accepting user input. """ def __init__(self, parent, game): self.game = game Frame.__init__(self, parent) self.parent = parent self.row, self.col = -1, -1 self.__initUI() def __initUI(self): self.parent.title("Sudoku") self.pack(fill=BOTH) self.canvas = Canvas(self, width=WIDTH, height=HEIGHT) self.canvas.pack(fill=BOTH, side=TOP) clear_button = Button(self, text="Clear answers", command=self.__clear_answers) clear_button.pack(fill=BOTH, side=BOTTOM) self.__draw_grid() self.__draw_puzzle() self.canvas.bind("<Button-1>", self.__cell_clicked) self.canvas.bind("<Key>", self.__key_pressed) def __draw_grid(self): """ Draws grid divided with blue lines into 3x3 squares """ for i in range(10): color = "blue" if i % 3 == 0 else "grey" x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line(x0, y0, x1, y1, fill=color) x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) def __draw_puzzle(self): self.canvas.delete("numbers") for i in range(9): for j in range(9): answer = self.game.puzzle[i][j] if answer != 0: x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 original = self.game.start_puzzle[i][j] color = "black" if answer == original else "sea green" self.canvas.create_text(x, y, text=answer, tags="numbers", fill=color) def __draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 self.canvas.create_rectangle(x0, y0, x1, y1, outline="red", tags="cursor") def __draw_victory(self): # create a oval (which will be a circle) x0 = y0 = MARGIN + SIDE * 2 x1 = y1 = MARGIN + SIDE * 7 self.canvas.create_oval(x0, y0, x1, y1, tags="victory", fill="dark orange", outline="orange") # create text x = y = MARGIN + 4 * SIDE + SIDE / 2 self.canvas.create_text(x, y, text="You win!", tags="victory", fill="white", font=("Arial", 32)) def __draw_loose(self): # create a oval (which will be a circle) x0 = y0 = MARGIN + SIDE * 2 x1 = y1 = MARGIN + SIDE * 7 self.canvas.create_oval(x0, y0, x1, y1, tags="victory", fill="yellow", outline="yellow") # create text x = y = MARGIN + 4 * SIDE + SIDE / 2 self.canvas.create_text(x, y, text="You loose!", tags="victory", fill="white", font=("Arial", 32)) def __cell_clicked(self, event): if self.game.game_over: return x, y = event.x, event.y if (MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN): self.canvas.focus_set() # get row and col numbers from x,y coordinates row, col = (y - MARGIN) // SIDE, (x - MARGIN) // SIDE # if cell was selected already - deselect it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 elif self.game.check[row][col] == 0: self.row, self.col = row, col else: self.row, self.col = -1, -1 self.__draw_cursor() def __key_pressed(self, event): if self.game.game_over: return if self.row >= 0 and self.col >= 0 and event.char in "1234567890": self.game.puzzle[self.row][self.col] = int(event.char) self.col, self.row = -1, -1 self.__draw_puzzle() self.__draw_cursor() a = self.game.check_win() # print(a) if a == 0: self.__draw_loose() return 0 elif a == 1: self.__draw_victory() def __clear_answers(self): self.game.start() self.canvas.delete("victory") self.__draw_puzzle()
class PiPresents(object): def __init__(self): gc.set_debug(gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_INSTANCES | gc.DEBUG_OBJECTS | gc.DEBUG_SAVEALL) self.pipresents_issue = "1.3" self.pipresents_minorissue = '1.3.1g' # position and size of window without -f command line option self.nonfull_window_width = 0.45 # proportion of width self.nonfull_window_height = 0.7 # proportion of height self.nonfull_window_x = 0 # position of top left corner self.nonfull_window_y = 0 # position of top left corner self.pp_background = 'black' StopWatch.global_enable = False # set up the handler for SIGTERM signal.signal(signal.SIGTERM, self.handle_sigterm) # **************************************** # Initialisation # *************************************** # get command line options self.options = command_options() # get Pi Presents code directory pp_dir = sys.path[0] self.pp_dir = pp_dir if not os.path.exists(pp_dir + "/pipresents.py"): if self.options['manager'] is False: tkMessageBox.showwarning( "Pi Presents", "Bad Application Directory:\n{0}".format(pp_dir)) exit(103) # Initialise logging and tracing Monitor.log_path = pp_dir self.mon = Monitor() # Init in PiPresents only self.mon.init() # uncomment to enable control of logging from within a class # Monitor.enable_in_code = True # enables control of log level in the code for a class - self.mon.set_log_level() # make a shorter list to log/trace only some classes without using enable_in_code. Monitor.classes = [ 'PiPresents', 'pp_paths', 'HyperlinkShow', 'RadioButtonShow', 'ArtLiveShow', 'ArtMediaShow', 'MediaShow', 'LiveShow', 'MenuShow', 'PathManager', 'ControlsManager', 'ShowManager', 'PluginManager', 'MplayerDriver', 'OMXDriver', 'UZBLDriver', 'KbdDriver', 'GPIODriver', 'TimeOfDay', 'ScreenDriver', 'Animate', 'OSCDriver' ] # Monitor.classes=['PiPresents','ArtMediaShow','VideoPlayer','OMXDriver'] # get global log level from command line Monitor.log_level = int(self.options['debug']) Monitor.manager = self.options['manager'] # print self.options['manager'] self.mon.newline(3) self.mon.log( self, "Pi Presents is starting, Version:" + self.pipresents_minorissue) # self.mon.log (self," OS and separator:" + os.name +' ' + os.sep) self.mon.log(self, "sys.path[0] - location of code: " + sys.path[0]) if os.geteuid() != 0: user = os.getenv('USER') else: user = os.getenv('SUDO_USER') self.mon.log(self, 'User is: ' + user) # self.mon.log(self,"os.getenv('HOME') - user home directory (not used): " + os.getenv('HOME')) # does not work # self.mon.log(self,"os.path.expanduser('~') - user home directory: " + os.path.expanduser('~')) # does not work # optional other classes used self.root = None self.ppio = None self.tod = None self.animate = None self.gpiodriver = None self.oscdriver = None self.osc_enabled = False self.gpio_enabled = False self.tod_enabled = False # get home path from -o option self.pp_home = pp_paths.get_home(self.options['home']) if self.pp_home is None: self.end('error', 'Failed to find pp_home') # get profile path from -p option # pp_profile is the full path to the directory that contains # pp_showlist.json and other files for the profile self.pp_profile = pp_paths.get_profile_dir(self.pp_home, self.options['profile']) if self.pp_profile is None: self.end('error', 'Failed to find profile') # check profile exists if os.path.exists(self.pp_profile): self.mon.log( self, "Found Requested profile - pp_profile directory is: " + self.pp_profile) else: self.mon.err( self, "Failed to find requested profile: " + self.pp_profile) self.end('error', 'Failed to find profile') self.mon.start_stats(self.options['profile']) # check 'verify' option if self.options['verify'] is True: val = Validator() if val.validate_profile(None, pp_dir, self.pp_home, self.pp_profile, self.pipresents_issue, False) is False: self.mon.err(self, "Validation Failed") self.end('error', 'Validation Failed') # initialise and read the showlist in the profile self.showlist = ShowList() self.showlist_file = self.pp_profile + "/pp_showlist.json" if os.path.exists(self.showlist_file): self.showlist.open_json(self.showlist_file) else: self.mon.err(self, "showlist not found at " + self.showlist_file) self.end('error', 'showlist not found') # check profile and Pi Presents issues are compatible if float(self.showlist.sissue()) != float(self.pipresents_issue): self.mon.err( self, "Version of profile " + self.showlist.sissue() + " is not same as Pi Presents, must exit") self.end('error', 'wrong version of profile') # get the 'start' show from the showlist index = self.showlist.index_of_show('start') if index >= 0: self.showlist.select(index) self.starter_show = self.showlist.selected_show() else: self.mon.err(self, "Show [start] not found in showlist") self.end('error', 'start show not found') if self.starter_show['start-show'] == '': self.mon.warn(self, "No Start Shows in Start Show") # ******************** # SET UP THE GUI # ******************** # turn off the screenblanking and saver if self.options['noblank'] is True: call(["xset", "s", "off"]) call(["xset", "s", "-dpms"]) self.root = Tk() self.title = 'Pi Presents - ' + self.pp_profile self.icon_text = 'Pi Presents' self.root.title(self.title) self.root.iconname(self.icon_text) self.root.config(bg=self.pp_background) self.mon.log( self, 'native screen dimensions are ' + str(self.root.winfo_screenwidth()) + ' x ' + str(self.root.winfo_screenheight()) + ' pixcels') if self.options['screensize'] == '': self.screen_width = self.root.winfo_screenwidth() self.screen_height = self.root.winfo_screenheight() else: reason, message, self.screen_width, self.screen_height = self.parse_screen( self.options['screensize']) if reason == 'error': self.mon.err(self, message) self.end('error', message) self.mon.log( self, 'commanded screen dimensions are ' + str(self.screen_width) + ' x ' + str(self.screen_height) + ' pixcels') # set window dimensions and decorations if self.options['fullscreen'] is False: self.window_width = int(self.root.winfo_screenwidth() * self.nonfull_window_width) self.window_height = int(self.root.winfo_screenheight() * self.nonfull_window_height) self.window_x = self.nonfull_window_x self.window_y = self.nonfull_window_y self.root.geometry("%dx%d%+d%+d" % (self.window_width, self.window_height, self.window_x, self.window_y)) else: self.window_width = self.screen_width self.window_height = self.screen_height self.root.attributes('-fullscreen', True) os.system( 'unclutter 1>&- 2>&- &' ) # Suppress 'someone created a subwindow' complaints from unclutter self.window_x = 0 self.window_y = 0 self.root.geometry("%dx%d%+d%+d" % (self.window_width, self.window_height, self.window_x, self.window_y)) self.root.attributes('-zoomed', '1') # canvs cover the whole screen whatever the size of the window. self.canvas_height = self.screen_height self.canvas_width = self.screen_width # make sure focus is set. self.root.focus_set() # define response to main window closing. self.root.protocol("WM_DELETE_WINDOW", self.handle_user_abort) # setup a canvas onto which will be drawn the images or text self.canvas = Canvas(self.root, bg=self.pp_background) if self.options['fullscreen'] is True: self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=0) else: self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=1, highlightcolor='yellow') self.canvas.place(x=0, y=0) # self.canvas.config(bg='black') self.canvas.focus_set() # **************************************** # INITIALISE THE INPUT DRIVERS # **************************************** # each driver takes a set of inputs, binds them to symboic names # and sets up a callback which returns the symbolic name when an input event occurs/ # use keyboard driver to bind keys to symbolic names and to set up callback kbd = KbdDriver() if kbd.read(pp_dir, self.pp_home, self.pp_profile) is False: self.end('error', 'cannot find or error in keys.cfg') kbd.bind_keys(self.root, self.handle_input_event) self.sr = ScreenDriver() # read the screen click area config file reason, message = self.sr.read(pp_dir, self.pp_home, self.pp_profile) if reason == 'error': self.end('error', 'cannot find screen.cfg') # create click areas on the canvas, must be polygon as outline rectangles are not filled as far as find_closest goes # click areas are made on the Pi Presents canvas not the show canvases. reason, message = self.sr.make_click_areas(self.canvas, self.handle_input_event) if reason == 'error': self.mon.err(self, message) self.end('error', message) # **************************************** # INITIALISE THE APPLICATION AND START # **************************************** self.shutdown_required = False self.exitpipresents_required = False # kick off GPIO if enabled by command line option self.gpio_enabled = False if os.path.exists(self.pp_profile + os.sep + 'pp_io_config' + os.sep + 'gpio.cfg'): # initialise the GPIO self.gpiodriver = GPIODriver() reason, message = self.gpiodriver.init(pp_dir, self.pp_home, self.pp_profile, self.canvas, 50, self.handle_input_event) if reason == 'error': self.end('error', message) else: self.gpio_enabled = True # and start polling gpio self.gpiodriver.poll() # kick off animation sequencer self.animate = Animate() self.animate.init(pp_dir, self.pp_home, self.pp_profile, self.canvas, 200, self.handle_output_event) self.animate.poll() #create a showmanager ready for time of day scheduler and osc server show_id = -1 self.show_manager = ShowManager(show_id, self.showlist, self.starter_show, self.root, self.canvas, self.pp_dir, self.pp_profile, self.pp_home) # first time through set callback to terminate Pi Presents if all shows have ended. self.show_manager.init(self.canvas, self.all_shows_ended_callback, self.handle_command, self.showlist) # Register all the shows in the showlist reason, message = self.show_manager.register_shows() if reason == 'error': self.mon.err(self, message) self.end('error', message) # Init OSCDriver, read config and start OSC server self.osc_enabled = False if os.path.exists(self.pp_profile + os.sep + 'pp_io_config' + os.sep + 'osc.cfg'): self.oscdriver = OSCDriver() reason, message = self.oscdriver.init( self.pp_profile, self.handle_command, self.handle_input_event, self.e_osc_handle_output_event) if reason == 'error': self.end('error', message) else: self.osc_enabled = True self.root.after(1000, self.oscdriver.start_server()) # and run the start shows self.run_start_shows() # set up the time of day scheduler including catchup self.tod_enabled = False if os.path.exists(self.pp_profile + os.sep + 'schedule.json'): # kick off the time of day scheduler which may run additional shows self.tod = TimeOfDay() self.tod.init(pp_dir, self.pp_home, self.pp_profile, self.root, self.handle_command) self.tod_enabled = True # then start the time of day scheduler if self.tod_enabled is True: self.tod.poll() # start Tkinters event loop self.root.mainloop() def parse_screen(self, size_text): fields = size_text.split('*') if len(fields) != 2: return 'error', 'do not understand --fullscreen comand option', 0, 0 elif fields[0].isdigit() is False or fields[1].isdigit() is False: return 'error', 'dimensions are not positive integers in ---fullscreen', 0, 0 else: return 'normal', '', int(fields[0]), int(fields[1]) # ********************* # RUN START SHOWS # ******************** def run_start_shows(self): self.mon.trace(self, 'run start shows') # parse the start shows field and start the initial shows show_refs = self.starter_show['start-show'].split() for show_ref in show_refs: reason, message = self.show_manager.control_a_show( show_ref, 'open') if reason == 'error': self.mon.err(self, message) # ********************* # User inputs # ******************** # handles one command provided as a line of text def handle_command(self, command_text): self.mon.log(self, "command received: " + command_text) if command_text.strip() == "": return if command_text[0] == '/': if self.osc_enabled is True: self.oscdriver.send_command(command_text) return fields = command_text.split() show_command = fields[0] if len(fields) > 1: show_ref = fields[1] else: show_ref = '' if show_command in ('open', 'close'): if self.shutdown_required is False: reason, message = self.show_manager.control_a_show( show_ref, show_command) else: return elif show_command == 'exitpipresents': self.exitpipresents_required = True if self.show_manager.all_shows_exited() is True: # need root.after to get out of st thread self.root.after(1, self.e_all_shows_ended_callback) return else: reason, message = self.show_manager.exit_all_shows() elif show_command == 'shutdownnow': # need root.after to get out of st thread self.root.after(1, self.e_shutdown_pressed) return else: reason = 'error' message = 'command not recognised: ' + show_command if reason == 'error': self.mon.err(self, message) return def e_all_shows_ended_callback(self): self.all_shows_ended_callback('normal', 'no shows running') def e_shutdown_pressed(self): self.shutdown_pressed('now') def e_osc_handle_output_event(self, line): #jump out of server thread self.root.after(1, lambda arg=line: self.osc_handle_output_event(arg)) def osc_handle_output_event(self, line): self.mon.log(self, "output event received: " + line) #osc sends output events as a string reason, message, delay, name, param_type, param_values = self.animate.parse_animate_fields( line) if reason == 'error': self.mon.err(self, message) self.end(reason, message) self.handle_output_event(name, param_type, param_values, 0) def handle_output_event(self, symbol, param_type, param_values, req_time): if self.gpio_enabled is True: reason, message = self.gpiodriver.handle_output_event( symbol, param_type, param_values, req_time) if reason == 'error': self.mon.err(self, message) self.end(reason, message) else: self.mon.warn(self, 'GPIO not enabled') # all input events call this callback with a symbolic name. # handle events that affect PP overall, otherwise pass to all active shows def handle_input_event(self, symbol, source): self.mon.log(self, "event received: " + symbol + ' from ' + source) if symbol == 'pp-terminate': self.handle_user_abort() elif symbol == 'pp-shutdown': self.shutdown_pressed('delay') elif symbol == 'pp-shutdownnow': # need root.after to grt out of st thread self.root.after(1, self.e_shutdown_pressed) return elif symbol == 'pp-exitpipresents': self.exitpipresents_required = True if self.show_manager.all_shows_exited() is True: # need root.after to grt out of st thread self.root.after(1, self.e_all_shows_ended_callback) return reason, message = self.show_manager.exit_all_shows() else: # events for shows affect the show and could cause it to exit. for show in self.show_manager.shows: show_obj = show[ShowManager.SHOW_OBJ] if show_obj is not None: show_obj.handle_input_event(symbol) def shutdown_pressed(self, when): if when == 'delay': self.root.after(5000, self.on_shutdown_delay) else: self.shutdown_required = True if self.show_manager.all_shows_exited() is True: self.all_shows_ended_callback('normal', 'no shows running') else: # calls exit method of all shows, results in all_shows_closed_callback self.show_manager.exit_all_shows() def on_shutdown_delay(self): # 5 second delay is up, if shutdown button still pressed then shutdown if self.gpiodriver.shutdown_pressed() is True: self.shutdown_required = True if self.show_manager.all_shows_exited() is True: self.all_shows_ended_callback('normal', 'no shows running') else: # calls exit method of all shows, results in all_shows_closed_callback self.show_manager.exit_all_shows() def handle_sigterm(self, signum, frame): self.mon.log(self, 'SIGTERM received - ' + str(signum)) self.terminate() def handle_user_abort(self): self.mon.log(self, 'User abort received') self.terminate() def terminate(self): self.mon.log(self, "terminate received") needs_termination = False for show in self.show_manager.shows: # print show[ShowManager.SHOW_OBJ], show[ShowManager.SHOW_REF] if show[ShowManager.SHOW_OBJ] is not None: needs_termination = True self.mon.log( self, "Sent terminate to show " + show[ShowManager.SHOW_REF]) # call shows terminate method # eventually the show will exit and after all shows have exited all_shows_callback will be executed. show[ShowManager.SHOW_OBJ].terminate() if needs_termination is False: self.end('killed', 'killed - no termination of shows required') # ****************************** # Ending Pi Presents after all the showers and players are closed # ************************** # callback from ShowManager when all shows have ended def all_shows_ended_callback(self, reason, message): self.canvas.config(bg=self.pp_background) if reason in ( 'killed', 'error' ) or self.shutdown_required is True or self.exitpipresents_required is True: self.end(reason, message) def end(self, reason, message): self.mon.log(self, "Pi Presents ending with reason: " + reason) if self.root is not None: self.root.destroy() self.tidy_up() # gc.collect() # print gc.garbage if reason == 'killed': self.mon.log(self, "Pi Presents Aborted, au revoir") # close logging files self.mon.finish() sys.exit(101) elif reason == 'error': self.mon.log(self, "Pi Presents closing because of error, sorry") # close logging files self.mon.finish() sys.exit(102) else: self.mon.log(self, "Pi Presents exiting normally, bye") # close logging files self.mon.finish() if self.shutdown_required is True: # print 'SHUTDOWN' call(['sudo', 'shutdown', '-h', '-t 5', 'now']) sys.exit(100) else: sys.exit(100) # tidy up all the peripheral bits of Pi Presents def tidy_up(self): self.mon.log(self, "Tidying Up") # turn screen blanking back on if self.options['noblank'] is True: call(["xset", "s", "on"]) call(["xset", "s", "+dpms"]) # tidy up animation and gpio if self.animate is not None: self.animate.terminate() if self.gpio_enabled == True: self.gpiodriver.terminate() if self.osc_enabled is True: self.oscdriver.terminate() # tidy up time of day scheduler if self.tod_enabled is True: self.tod.terminate()
class SudokuUI(Frame): """ The Tkinter UI, responsible for drawing the board and accepting user input. """ def __init__(self, parent, game): self.game = game Frame.__init__(self, parent) self.parent = parent self.row, self.col = -1, -1 self.__initUI() def __initUI(self): self.parent.title("Sudoku") self.pack(fill=BOTH, expand=1) self.canvas = Canvas( self, width=WIDTH, height=HEIGHT, highlightthickness=0 ) self.canvas.pack(fill=BOTH, side=TOP) clear_button = Button( self, text="Clear answers", command=self.__clear_answers ) clear_button.pack(fill=BOTH, side=BOTTOM) self.__draw_grid() self.__draw_puzzle() self.canvas.bind("<Button-1>", self.__cell_clicked) self.canvas.bind("<Key>", self.__key_pressed) def __draw_grid(self): """ Draws grid divided with blue lines into squares 3x3 """ for i in xrange(10): color = "blue" if i % 3 == 0 else "gray" x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line(x0, y0, x1, y1, fill=color) x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) def __draw_puzzle(self): self.canvas.delete("numbers") for i in xrange(9): for j in xrange(9): answer = self.game.answer[i][j] if answer != 0: x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 original = self.game.puzzle[i][j] color = "black" if answer == original else "slate gray" self.canvas.create_text( x, y, text=answer, tags="numbers", fill=color ) def __draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 self.canvas.create_rectangle( x0, y0, x1, y1, outline="red", tags="cursor" ) def __draw_victory(self): # create oval x0 = y0 = MARGIN + SIDE * 2 x1 = y1 = MARGIN + SIDE * 7 self.canvas.create_oval( x0, y0, x1, y1, tags="victory", fill="dark orange", outline="orange" ) # create text x = y = MARGIN + 4 * SIDE + SIDE / 2 self.canvas.create_text( x, y, text="You win!", tags="victory", fill="white", font=("Arial", 32) ) def __cell_clicked(self, event): if self.game.game_over: return x, y = event.x, event.y if (MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN): self.canvas.focus_set() # get row and col numbers from x,y coordinates row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE # if cell was selected already - deselect it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 elif self.game.puzzle[row][col] == 0: self.row, self.col = row, col else: self.row, self.col = -1, -1 self.__draw_cursor() def __key_pressed(self, event): if self.game.game_over: return if self.row >= 0 and self.col >= 0 and event.char in "1234567890": self.game.answer[self.row][self.col] = int(event.char) self.col, self.row = -1, -1 self.__draw_puzzle() self.__draw_cursor() if self.game.check_win(): self.__draw_victory() def __clear_answers(self): self.game.set_answer_to_puzzle() self.canvas.delete("victory") self.__draw_puzzle()
class SodokuUI(Frame): """The Tkinter UI responsible for drawing sodoku boards and accepting user input""" def __init__(self, parent, game): self.game = game self.parent = parent Frame.__init__(self, parent) self.row, self.col = 0, 0 self.__initUI() def __initUI(self): self.parent.title("Soduku") #title of parent window self.pack(fill=BOTH, expand=1) self.canvas = Canvas(self, width=WIDTH, height=HEIGHT) self.canvas.pack(fill=BOTH, side=TOP) clear_button = Button(self, text="Clear answers", command=self.__clear_answers) clear_button.pack(fill=BOTH, side=BOTTOM) self.__draw_puzzle() self.canvas.bind( "<Button-1>", self.__cell_clicked ) #sends x, y coordinates of left mouse click to self.__cell_clicked self.canvas.bind( "<Key>", self.__key_pressed) #sends key pressed to self.__key_pressed def __draw_grid(self): """ Draws grid with blue lines divided into 3x3 squares by grey lines """ for i in range(10): color, dash, thickness = ("grey", None, 2) if i % 3 == 0 else ("grey", (1, ), 1) x0 = MARGIN x1 = WIDTH - MARGIN y0 = MARGIN + i * SIDE y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, dash=dash, fill=color, width=thickness) x0 = MARGIN + i * SIDE x1 = MARGIN + i * SIDE y0 = MARGIN y1 = HEIGHT - MARGIN self.canvas.create_line(x0, y0, x1, y1, dash=dash, fill=color, width=thickness) def __draw_puzzle(self): """ Draws the numbers on the puzzle """ self.canvas.delete("numbers") #deletes everything tagged numbers for i in range(9): for j in range(9): answer = self.game.puzzle[i][j] if answer != 0: x = MARGIN + (j + 0.5) * SIDE y = MARGIN + (i + 0.5) * SIDE if answer == self.game.start_puzzle[i][j]: self.canvas.create_rectangle(x - SIDE / 2, y - SIDE / 2, x + SIDE / 2, y + SIDE / 2, tags="numbers", fill="grey") self.canvas.create_text(x, y, text=answer, tags="numbers", fill="white") else: self.canvas.create_rectangle(x - SIDE / 2, y - SIDE / 2, x + SIDE / 2, y + SIDE / 2, tags="numbers", fill="seashell") self.canvas.create_text(x, y, text=answer, tags="numbers", fill="black") self.__draw_grid() self.__draw_cursor() def __clear_answers(self): self.game.start() self.canvas.delete("victory") self.__draw_puzzle() def __cell_clicked(self, event): ''' event param gives us the x & y coordinates ''' if self.game.gameOver: return #do nothing x, y = event.x, event.y if (MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN): self.canvas.focus_set() r, c = (event.y - MARGIN) // SIDE, (event.x - MARGIN) // SIDE if ((self.row == r and self.col == c) or self.game.start_puzzle[r][c] != 0): self.row, self.col = -1, -1 #if same cell reselected or cell part of original board, deselect else: self.row, self.col = r, c else: self.row, self.col = -1, -1 self.__draw_cursor() def __draw_cursor(self): ''' Draws cursor around selected box ''' self.canvas.delete("cursor") if self.row == -1 or self.col == -1: return x0 = MARGIN + self.col * SIDE x1 = MARGIN + (self.col + 1) * SIDE y0 = MARGIN + self.row * SIDE y1 = MARGIN + (self.row + 1) * SIDE self.canvas.create_rectangle(x0, y0, x1, y1, outline="red", tags="cursor") def __key_pressed(self, event): if self.game.gameOver: return if self.row != -1 and self.col != -1 and event.char in "0123456789" and event.char != '': self.game.puzzle[self.row][self.col] = int(event.char) self.__draw_puzzle() if self.game.check_win(): self.__draw_victory() def __draw_victory(self): x0 = y0 = MARGIN + SIDE * 2 x1 = y1 = MARGIN + SIDE * 7 self.canvas.create_oval(x0, y0, x1, y1, fill="dark orange", outline="orange", tags="victory") x = y = MARGIN + SIDE * 4.5 self.canvas.create_text(x, y, text="You win!", font=("Arial", 32), fill="white", tags="victory")
class TutorWindow( Toplevel ): """ Window for displaying a basic help """ labels = {} # Dictionary for keeping clickable labels size_x = 600 # horizontal size of canvas size_y = 800 # vertical size of canvas last_images = [] # handle on images currently on the canvas images = [] # new images to go on the canvas curr_key = None # Current key that is looked at # Names of label links and list of pictures to load. These pictures are generated from a pdf by save as, type .png help_dict = { "Get Pictures" : [ "Get_Pictures_Page_1.png" , "Get_Pictures_Page_2.png" , "Get_Pictures_Page_3.png" ], "Save Pictures" : [ "Save_Pictures_Page_1.png", "Save_Pictures_Page_2.png", "Save_Pictures_Page_3.png" ], "Pictures Effects": [ "Pic_Effects_Page_1.png" , "Pic_Effects_Page_2.png" ], "Options" : [ "Options.png" ], } def __init__( self ): """ Initialize window settings """ Toplevel.__init__( self ) self.title( "Tutorial" ) self.iconbitmap( ICON_FILENAME ) self.geometry( "+100+50" ) # init frames for window. This window contains complicated frames. i.e. frames with frames inside them. fr11 = Frame( self ) fr1 = Frame( fr11 ) fr2 = Frame( fr11 ) fr3 = Frame( self ) # create labels links for displaying different help information for name in self.help_dict: self.labels[ name ] = Label( fr1, text=name, fg="blue" ) self.labels[ name ].bind( "<ButtonPress-1>", lambda e, arg=name: self.HandleLB( e, arg ) ) self.labels[ name ].pack( fill=X ) fr1.pack( side=LEFT ) # create/configure canvas and scrollbar for displaying help pictures self.canv = Canvas( fr2, width=self.size_x, height=self.size_y, scrollregion=( 0, 0, 300, 0 ) ) self.sbar = Scrollbar( fr2 ) self.sbar.config( command=self.canv.yview ) self.canv.config( yscrollcommand=self.sbar.set ) self.canv.focus_set() self.sbar.pack( side=RIGHT, fill=Y ) self.canv.pack( side=LEFT, fill=Y ) fr2.pack( side=LEFT ) fr11.pack() # create ok button for closing the window btn = Button( fr3, text="Ok", width=10, command=self.quit ) btn.pack( side=LEFT ) fr3.pack() self.mainloop() self.destroy() def HandleLB( self, event, key ): """ handle clicking a label link """ if( key != self.curr_key ): # reset the position of the scrollbar to the top self.canv.yview_moveto( 0.0 ) # load new images print "Key: ", key self.LoadImages( key ) # change formatting on labels, color red for current one clicked self.FormatLabels( key ) # remove old pictures from the canvas before adding new ones if( len( self.last_images ) != 0 ): for image in self.last_images: self.canv.delete( image ) self.last_images = [] # There's an offset required in order to show everything correctly, don't know why... image_y = 390 # change scrollable area for the canvas to be exact size of all pictures self.canv.config( scrollregion=( 0, 0, 300, 776*len( self.images ) ) ) # add new pictures to canvas stacking them on top of each other and making them seamless for i in range( len( self.images ) ): self.last_images.append( self.canv.create_image( ( self.size_x/2, image_y ), image=self.images[ i ] ) ) image_y += self.images[ i ].height() self.curr_key = key def LoadImages( self, key ): """ load new inmages into class storage """ self.images = [] print "help_dict: ", self.help_dict # get images from hardcoded array for image in self.help_dict[ key ]: # open PIL image print "image: ", path.join( HELP_DIR, image ) image1 = PILopen( path.join( HELP_DIR, image ) ) # resize to fit canvas area image1 = image1.resize( ( self.size_x , self.size_y ), ANTIALIAS ) # make into a tkimage im = PhotoImage( image1 ) # add to list of images to display self.images.append( im ) def FormatLabels( self, key ): for name in self.labels: self.labels[ name ].config( fg="blue" ) self.labels[ key ].config( fg="red" )
class SudokuUI(Frame): """ The Tkinter UI, responsible for drawing the board and accepting user input. """ def __init__(self, parent, game): self.game = game Frame.__init__(self, parent) self.parent = parent self.row, self.col = -1, -1 self.initUI() def initUI(self): self.parent.title("Sudoku") self.pack(fill=BOTH, expand=1) self.canvas = Canvas( self, width=WIDTH, height=HEIGHT, highlightthickness=0 ) self.canvas.pack(fill=BOTH, side=TOP) clear_button = Button( self, text="Clear answers", command=self.clear_answers ) clear_button.pack(fill=BOTH, side=BOTTOM) self.draw_grid() self.draw_puzzle() self.canvas.bind("<Button-1>", self.cell_clicked) self.canvas.bind("<Key>", self.key_pressed) def draw_grid(self): for i in xrange(10): self.canvas.create_line( MARGIN + i * SIDE, MARGIN, MARGIN + i * SIDE, HEIGHT - MARGIN, fill="blue" if i % 3 == 0 else "gray" ) self.canvas.create_line( MARGIN, MARGIN + i * SIDE, WIDTH - MARGIN, MARGIN + i * SIDE, fill="blue" if i % 3 == 0 else "gray" ) def draw_puzzle(self): self.canvas.delete("numbers") for i in xrange(9): for j in xrange(9): answer = self.game.answer[i][j] original = self.game.puzzle[i][j] if answer != 0: self.canvas.create_text( MARGIN + j * SIDE + SIDE / 2, MARGIN + i * SIDE + SIDE / 2, text=answer, tags="numbers", fill="black" if answer == original else "slate gray" ) def draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: self.canvas.create_rectangle( MARGIN + self.col * SIDE + 1, MARGIN + self.row * SIDE + 1, MARGIN + (self.col + 1) * SIDE - 1, MARGIN + (self.row + 1) * SIDE - 1, outline="red", tags="cursor" ) def draw_victory(self): self.canvas.create_oval( MARGIN + SIDE * 2, MARGIN + SIDE * 2, MARGIN + SIDE * 7, MARGIN + SIDE * 7, tags="victory", fill="dark orange", outline="orange" ) self.canvas.create_text( MARGIN + 4 * SIDE + SIDE / 2, MARGIN + 4 * SIDE + SIDE / 2, text="You win!", tags="victory", fill="white", font=("Arial", 32) ) def cell_clicked(self, event): if self.game.game_over: return x, y = event.x, event.y if (x > MARGIN and x < WIDTH - MARGIN and y > MARGIN and y < HEIGHT - MARGIN): self.canvas.focus_set() row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 elif self.game.puzzle[row][col] == 0: self.row, self.col = row, col else: self.row, self.col = -1, -1 self.draw_cursor() def key_pressed(self, event): if self.game.game_over: return if self.row >= 0 and self.col >= 0 and event.char in "1234567890": self.game.answer[self.row][self.col] = int(event.char) self.col, self.row = -1, -1 self.draw_puzzle() self.draw_cursor() if self.game.check_win(): self.draw_victory() def clear_answers(self): self.game.set_answer_to_puzzle() self.canvas.delete("victory") self.draw_puzzle()
class SudokuUI(Frame): def __init__(self, parent, game): self.game = game self.parent = parent Frame.__init__(self, parent) self.row, self.col = 0, 0 self.__initUI() def __initUI(self): self.parent.title("Sudoku") self.pack(fill=BOTH, expand=1) self.canvas = Canvas(self, width=WIDTH, height=HEIGHT) self.canvas.pack(fill=BOTH, side=TOP) clear_button = Button(self, text="Clear answers", command=self.__clear_answers) clear_button.pack(fill=BOTH, side=BOTTOM) self.__draw_grid() self.__draw_puzzle() self.canvas.bind("<Button-1>", self.__cell_clicked) self.canvas.bind("<Key>", self.__key_pressed) def __draw_grid(self): for i in xrange(10): color = "blue" if i % 3 == 0 else "gray" x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line(x0, y0, x1, y1, fill=color) x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) def __draw_puzzle(self): self.canvas.delete("numbers") for i in xrange(9): for j in xrange(9): answer = self.game.puzzle[i][j] if answer != 0: x = MARGIN + j * SIDE + SIDE / 2 y = MARGIN + i * SIDE + SIDE / 2 original = self.game.start_puzzle[i][j] color = "black" if answer == original else "sea green" self.canvas.create_text(x, y, text=answer, tags="numbers", fill=color) def __clear_answers(self): self.game.start() self.canvas.delete("victory") self.__draw_puzzle() def __cell_clicked(self, event): if self.game.game_over: return x, y = event.x, event.y if (MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN): self.canvas.focus_set() row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 elif self.game.puzzle[row][col] == 0 or self.game.puzzle[row][ col] != self.game.start_puzzle[row][col]: self.row, self.col = row, col else: self.row, self.col = -1, -1 self.__draw_cursor() def __draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: x0 = MARGIN + self.col * SIDE + 1 y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 self.canvas.create_rectangle(x0, y0, x1, y1, outline="red", tags="cursor") def __key_pressed(self, event): if self.game.game_over: return if self.row >= 0 and self.col >= 0 and event.char in "1234567890": self.game.puzzle[self.row][self.col] = int(event.char) self.col, self.row = -1, -1 self.__draw_puzzle() self.__draw_cursor() if self.game.check_win(): self.__draw_victory() def __draw_victory(self): x0 = y0 = MARGIN + SIDE * 2 x1 = y1 = MARGIN + SIDE * 7 self.canvas.create_oval(x0, y0, x1, y1, tags="victory", fill="dark orange", outline="orange") x = y = MARGIN + 4 * SIDE + SIDE / 2 self.canvas.create_text(x, y, text="You win!", tags="winner", fill="white", font=("Arial", 32))
class SudokuUI(Frame): # Frame is a rectangular region on a screen """ The Tkinter UI, responsible for drawing the board and accepting user input """ def __init__(self, parent, game): self.game = game Frame.__init__(self, parent) self.parent = parent # all widgets belong to a parent self.row, self.col = -1, -1 # initialize row and col to use later self.__initUI() # calls the initUI function def __initUI(self): self.parent.title("Sudoku") # our parent window will be called Sudoku self.pack( fill=BOTH, expand=1 ) # Frame attribute, organizes geometry relative to parent, fill options: both, none, x, y self.canvas = Canvas(self, width=WIDTH, height=HEIGHT) self.canvas.pack(fill=BOTH, side=TOP) # canvas attribute, helps display our board clear_button = Button(self, text="Clear answers", command=self.__clear_answers) clear_button.pack(fill=BOTH, side=BOTTOM) # Clear button is at the bottom and fills the space self.__draw_grid() # helper functions self.__draw_puzzle() self.canvas.bind( "<Button-1>", self.__cell_clicked ) # binds Button-1 to a callback, another method: cell_clicked # in Tkinter, Button-1 is a default left mouse click self.canvas.bind("<Key>", self.__key_pressed) # binds the key (guesed number) to the key presseed method def __draw_grid( self ): # make the sudoku layout, do all private functions take self and then other potential arguments? """ Draws grid divided with blue lines into 3x3 squares """ for i in xrange(10): color = "blue" if i % 3 == 0 else "gray" # blue lines if divisible by 3 # draw vertical lines x0 = MARGIN + i * SIDE y0 = MARGIN x1 = MARGIN + i * SIDE y1 = HEIGHT - MARGIN self.canvas.create_line( x0, y0, x1, y1, fill=color ) # draw the vertical lines coordinates are (x0, y0) (x1, y1) # draw horizontal lines x0 = MARGIN y0 = MARGIN + i * SIDE x1 = WIDTH - MARGIN y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) def __draw_puzzle(self): self.canvas.delete("numbers") # delete old numbers? for i in xrange(9): for j in xrange(9): answer = self.game.puzzle[i][j] if answer != 0: x = MARGIN + j * SIDE + SIDE / 2 # in the middle of the applicable cell y = MARGIN + i * SIDE + SIDE / 2 original = self.game.start_puzzle[i][j] color = "black" if answer == original else "sea green" self.canvas.create_text(x, y, text=answer, tags="numbers", fill=color) def __draw_cursor(self): self.canvas.delete("cursor") if self.row >= 0 and self.col >= 0: # you set these variables as 0 first in init x0 = MARGIN + self.col * SIDE + 1 # what does -1 do to these variables? y0 = MARGIN + self.row * SIDE + 1 x1 = MARGIN + (self.col + 1) * SIDE - 1 y1 = MARGIN + (self.row + 1) * SIDE - 1 self.canvas.create_rectangle(x0, y0, x1, y1, outline="red", tags="cursor") def __draw_victory(self): # creates an oval/circle x0 = y0 = MARGIN + SIDE * 2 # upper left box of circle starts margin + 2 rows in x1 = y1 = MARGIN + SIDE * 7 self.canvas.create_oval(x0, y0, x1, y1, tags="victory", fill="dark orange", outline="orange") # create text x = y = MARGIN + 4 * SIDE + SIDE / 2 # middle of the circle self.canvas.create_text(x, y, text="You win!", tags="victory", fill="white", font=("Arial", 32)) def __cell_clicked(self, event): # event parameter: gives us x&y coordinates of where user clicked if self.game.game_over: return # do nothing if game is over x, y = event.x, event.y if MARGIN < x < WIDTH - MARGIN and MARGIN < y < HEIGHT - MARGIN: # if our puzzle grid is clicked self.canvas.focus_set() # focus_set: move focus to a widget # get row and col numbers from x, y coordinates row, col = (y - MARGIN) / SIDE, (x - MARGIN) / SIDE # if cell was selected already, another click should de-select it if (row, col) == (self.row, self.col): self.row, self.col = -1, -1 # I assume -1 means de-selecting? elif self.game.puzzle[row][col] == 0: # otherwise, grab corresponding cell self.row, self.col = row, col else: self.row, self.col = -1, -1 self.__draw_cursor() def __key_pressed(self, event): if self.game.game_over: return if self.row >= 0 and self.col >= 0 and event.char in "1234567890": # where does event.char come from? tkinter? self.game.puzzle[self.row][self.col] = int(event.char) self.col, self.row = -1, -1 self.__draw_puzzle() self.__draw_cursor() if self.game.check_win(): # every time you enter in a number, the game checks to see if you have won self.__draw_victory() def __clear_answers(self): self.game.start() self.canvas.delete("victory") # remove the victory circle self.__draw_puzzle()
class at_graph(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.u = utils('atoutput.pkl') self.km = dict() self.price = dict() self.km[0] = (min(self.u.all_km), max(self.u.all_km)) self.price[0] = (min(self.u.all_price), max(self.u.all_price)) self.zoom_level = 0 try: self.parent.title("Auto trader results") self.is_standalone = True except: self.is_standalone = False self.style = Style() self.style.theme_use("classic") # Assume the parent is the root widget; make the frame take up the # entire widget size. print self.is_standalone if self.is_standalone: self.w, self.h = map(int, self.parent.geometry().split('+')[0].split('x')) self.w, self.h = 800, 800 else: self.w, self.h = 600, 600 self.c = None # Are they hovering over a data point? self.is_hovering = False # Filter the description strings: lower and whiten any non-matching # data point. self.filter = '' self.re = list() self.replot() def replot(self, zlfrac=None): """Replot the graph. If zlfrac is not None, then it should be a fractional value between 0 and 1; this is used to do smooth zooming, which doesn't plot the axes (it only redraws the car points).""" if self.c is not None: self.c.destroy() self.c = Canvas(self, height=self.h, width=self.w, bd=1, bg='#f3f5f9') self.c.grid(sticky=S, pady=1, padx=1) zl = self.zoom_level if zlfrac is not None: z1l, z1h = self.zoom_price_start z2l, z2h = self.zoom_price_end price_low = z1l + (z2l - z1l) * zlfrac price_high = z1h + (z2h - z1h) * zlfrac z1l, z1h = self.zoom_km_start z2l, z2h = self.zoom_km_end km_low = z1l + (z2l - z1l) * zlfrac km_high = z1h + (z2h - z1h) * zlfrac self.axis((price_low, price_high), 'y', draw=False) self.axis((km_low, km_high), 'x', draw=False) self.car_points(draw=False) else: self.axis(self.price[zl], 'y') self.axis(self.km[zl], 'x') self.car_points() self.pack(fill=BOTH, expand=1) def xyp(self, x, y): "Given x in km and y in $, return canvas position (xp, yp)." xp = int(math.floor((1.0 * x - self.x1) / (self.x2 - self.x1) \ * (self.xp2 - self.xp1) + self.xp1 + 0.5)) yp = int(math.floor((1.0 * y - self.y1) / (self.y2 - self.y1) \ * (self.yp2 - self.yp1) + self.yp1 + 0.5)) return (xp, yp) def axis(self, arange, ax, draw=True): "Add an axis ax='x' or 'y', with arange=(min, max) values." if draw: a1, a2, ast = self.u.axis(*arange) else: a1, a2 = arange ast = (a2 - a1) * 0.2 nt = int(math.floor((a2 - a1) / ast + 0.5)) + 1 st_offset = 50 # Remember the min and max axis values, along with the canvas points # that correspond to each location (xp1 and xp2). This allows us to # calculate where on the canvas a particular (x, y) value falls. if ax == 'x': self.x1, self.x2 = a1, a2 self.xp1, self.xp2 = st_offset, self.w - st_offset self.xtick = [a1 + i * ast for i in range(nt)] # Remember where the midpoint of the axis is, relative to canvas. self.xmid = (self.xp1 + self.xp2) / 2 else: self.y1, self.y2 = a1, a2 self.yp1, self.yp2 = self.h - st_offset, st_offset self.ytick = [a1 + i * ast for i in range(nt)] # Remember where the midpoint of the axis is, relative to canvas. self.ymid = (self.yp1 + self.yp2) / 2 # Figure out tick labels. atick = ['%g' % ((a1 + i * ast) / 1000) for i in range(nt)] # Figure out maximum decimal places on all tick labels, and ensure # they all have that many decimal places. max_dec = max(map(lambda x: 0 if '.' not in x else len(x.split('.')[1]), atick)) if max_dec > 0: atick = map(lambda x: x + '.' + '0'*max_dec if '.' not in x else x + '0'*(max_dec - len(x.split('.')[1])), atick) yst, xst = self.h - st_offset, st_offset # Draw axis line proper, and axis label. if draw: if ax == 'x': self.c.create_line(xst, yst, self.w - st_offset, yst) xp = (xst + self.w - st_offset) / 2 self.c.create_text(xp, yst + 30, text='Mileage (km x 1000)') else: self.c.create_line(xst, yst, xst, st_offset) self.c.create_text(xst, st_offset - 30, text='Price') self.c.create_text(xst, st_offset - 15, text='($000)') tick_anchor = [N, E][ax == 'y'] tick_x, tick_y = xst, yst tick_step = ([self.w, self.h][ax == 'y'] - st_offset * 2 * 1.0) / \ (nt - 1) label_offset = 3 for i1, tick in enumerate(atick): x_of, y_of = -label_offset, label_offset if ax == 'y': y_of = int(-i1 * tick_step) else: x_of = int(i1 * tick_step) if draw: self.c.create_text(tick_x + x_of, tick_y + y_of, text=tick, anchor=tick_anchor) x_mini, y_mini = 0, 0 x_maxi, y_maxi = 0, 0 if ax == 'y': x_of += label_offset x_mini, x_maxi = 8, self.w - st_offset * 2 # Remember what y coord this grid line is at. if i1 == 0: self.y_grid = [] self.y_grid.append(tick_y + y_of) else: y_of -= label_offset y_mini, y_maxi = -8, st_offset * 2 - self.h # Remember what x coord this grid line is at. if i1 == 0: self.x_grid = [] self.x_grid.append(tick_x + x_of) if draw: # Draw the little solid tick, next to the axis. self.c.create_line(tick_x + x_of, tick_y + y_of, tick_x + x_of + x_mini, tick_y + y_of + y_mini) # Draw a dashed grid line, across the entire graph. self.c.create_line(tick_x + x_of, tick_y + y_of, tick_x + x_of + x_maxi, tick_y + y_of + y_maxi, dash=(1, 3)) def car_points(self, draw=True): "Plot the cars themselves." # 199 215 151 151 199 224 230 162 157 250 224 167 178 165 192 249 200 216 204 204 204 191 173 158 color_order = ['#c7d797', '#97c7e0', '#e6a29d', '#fae0a7', '#b2a5c0', '#f9c8d8', '#bfad9e', '#cccccc'] #color_order = ['#98df8a', '#dbdb8d', '#aec7e8', '#c9acd4', '#f7b6d2', # '#ffbb80', '#dc9b8d', '#e9ab17', '#dddddd'] # Those colors above aren't saturated enough. Saturate them more. color_order = map(lambda x: resaturate(x, -80), color_order) # Change color depending on year. cy = dict() for i1, year in enumerate(reversed(sorted(set(self.u.all_year)))): cy[year] = color_order[-1] if i1 < len(color_order): cy[year] = color_order[i1] i1 = -1 # Tuples of (index into self.u.all_* arrays, x position, y position). self.ov_dict = dict() if draw: self.c.focus_set() self.c.bind('<Button-1>', func=self.zoom) self.c.bind('<Button-2>', func=self.unzoom) self.c.bind('<Left>', func=self.left_key) self.c.bind('<Right>', func=self.right_key) self.c.bind('<Up>', func=self.up_key) self.c.bind('<Down>', func=self.down_key) legend = set() osz = 3 + self.zoom_level * 1 # Total vehicle count, and vehicles which pass the filter count. self.vcount = self.fcount = 0 for year, km, price in zip(self.u.all_year, self.u.all_km, self.u.all_price): x, y = self.xyp(km, price) i1 += 1 if x < self.x_grid[0] or x > self.x_grid[-1] or \ y > self.y_grid[0] or y < self.y_grid[-1]: continue self.vcount += 1 legend.add((year, cy[year])) filtered = False if not re.search(self.filter, self.u.all_descr[i1], re.I): filtered = True # If a data point is filtered out, make its outline reflect its # model year, and fill it with white. # # Else, make its outline and fill color reflect the model year, and # upon mouseover, make it entirely red. ov = self.c.create_oval(x-osz, y-osz, x+osz, y+osz, outline=cy[year], activeoutline=['red', cy[year]][filtered], fill=[cy[year], 'white'][filtered], activefill=['red', 'white'][filtered], ) self.ov_dict[ov] = (i1, x, y, cy[year], filtered) # If a data point is filtered out, mousing over it does nothing, # and also, lower it behind everything else. if filtered: self.c.lower(ov) else: self.fcount += 1 if draw: use_tag = 'Tag %d' % i1 self.c.addtag_withtag(use_tag, ov) self.c.tag_bind(use_tag, sequence='<Enter>', func=self.mouseover) self.c.tag_bind(use_tag, sequence='<Leave>', func=self.mouseoff) self.c.tag_bind(use_tag, sequence='<Button-1>', func=self.select) if draw: # OK, add a legend for every year that's displayed. i1 = 0 for yr, color in reversed(sorted(legend)): xp, yp = self.x_grid[-1] + 10, self.y_grid[-1] + 15 * i1 self.c.create_oval(xp-osz, yp-osz, xp+osz, yp+osz, outline=color, fill=color) self.c.create_text(xp + 8, yp, text=str(yr), anchor=W) i1 += 1 # And, add a title. tistr = 'Vehicle count: %d' % self.vcount if self.fcount != self.vcount: tistr = 'Filtered vehicle count: %d' % self.fcount xp = (self.x_grid[0] + self.x_grid[-1]) / 2 yp = self.y_grid[-1] - 30 self.c.create_text(xp, yp, text=tistr, font=('Helvetica', '16')) zstr1 = 'Click on a blank graph location to zoom in' zstr2 = 'Right click to zoom out' if self.zoom_level == 0: zstr = zstr1 elif self.zoom_level == 2: zstr = zstr2 else: zstr = zstr1 + ', or r' + zstr2[1:] self.c.create_text(xp, yp + 16, text=zstr, font=('Helvetica', '14')) def mouseover(self, event): oval = event.widget.find_closest(event.x, event.y)[0] # XXX Sometimes, the closest widget is an axis grid line, not an oval. # Need to handle this correctly eventually. if oval not in self.ov_dict: return self.is_hovering = True ind, x, y, color, filtered = self.ov_dict[oval] # Figure out how high the box needs to be by creating the text off- # graph, then getting its bbox and deleting it. w = 200 de_text = self.u.all_descr[ind] deobj = self.c.create_text(self.w + 3, self.h + 3, text=de_text, anchor=N+W, width=w-6, font=('Helvetica', '14')) bbox = self.c.bbox(deobj) self.c.delete(deobj) h = 18 + bbox[3] - bbox[1] border = 5 if x > self.xmid: x -= (w + border) else: x += border if y > self.ymid: y -= (h + border) else: y += border self.re = list() self.re.append(self.c.create_rectangle(x, y, x + w, y + h, fill=resaturate(color, 50))) pr_text = '$%s' % self.u.commafy(self.u.all_price[ind]) self.re.append(self.c.create_text(x + 3, y + 3, text=pr_text, anchor=N+W, font=('Helvetica', '10'))) km_text = '%skm' % self.u.commafy(self.u.all_km[ind]) self.re.append(self.c.create_text(x + w - 3, y + 3, text=km_text, anchor=N+E, font=('Helvetica', '10'))) wh_text = self.u.all_wherestr[ind] if wh_text[0].isdigit(): wh_text += ' away' self.re.append(self.c.create_text(x + w/2, y + 3, text=wh_text, anchor=N, font=('Helvetica', '10'))) self.re.append(self.c.create_text(x + 3, y + 16, text=de_text, anchor=N+W, width=w-6, font=('Helvetica', '14'))) def set_filter(self, st): "Given a string 'st', filter ovals whose description doesn't match." self.filter = st self.replot() def mouseoff(self, event): "Code for mousing off a data point." # The tooptip rectangle and all its sub-objects need to be destroyed. map(self.c.delete, self.re) # Also, need to note that we're no longer over an oval -- that way, # Button-1 events will cause a zoom, rather than launching a web page. self.is_hovering = False def _zoom_animation(self): import time from math import tanh scale = 5 for i1 in range(-scale, scale+1): self.replot(zlfrac=0.5 + 0.5*tanh(i1*2.0/scale)/tanh(2.0)) self.c.update() def zoom(self, event): # Only zoom in if we're actually within the graph boundaries. if event.x <= self.x_grid[0] or event.x > self.x_grid[-1]: return if event.y >= self.y_grid[0] or event.y < self.y_grid[-1]: return # Don't zoom if we're hovering over a data point: let the web browser # event handler operate. if self.is_hovering: return # Don't zoom in more than twice. if self.zoom_level >= 2: return # Find the grid square which we're inside. for i1 in range(len(self.x_grid) - 1): if event.x <= self.x_grid[i1 + 1]: xgrid = i1 + 1 break for i1 in range(len(self.y_grid) - 1): if event.y >= self.y_grid[i1 + 1]: ygrid = i1 + 1 break self.zoom_level += 1 zl = self.zoom_level # Make the limits of the new graph be those of the grid square which # was clicked inside. self.km[zl] = (self.xtick[xgrid-1], self.xtick[xgrid]) self.price[zl] = (self.ytick[ygrid-1], self.ytick[ygrid]) if zl == 1: self.zoom_price_start = self.u.axis(*self.price[0])[:2] self.zoom_km_start = self.u.axis(*self.km[0])[:2] else: self.zoom_price_start = self.price[zl - 1] self.zoom_km_start = self.km[zl - 1] self.zoom_price_end = self.price[zl] self.zoom_km_end = self.km[zl] self._zoom_animation() self.replot() def unzoom(self, event): # If already at maximum zoom, nothing to be done. if self.zoom_level == 0: return # If not clicking inside graph boundaries, don't unzoom. if event.x <= self.x_grid[0] or event.x > self.x_grid[-1]: return if event.y >= self.y_grid[0] or event.y < self.y_grid[-1]: return self.zoom_level -= 1 zl = self.zoom_level self.zoom_price_start = self.price[zl + 1] self.zoom_km_start = self.km[zl + 1] if zl == 0: self.zoom_price_end = self.u.axis(*self.price[0])[:2] self.zoom_km_end = self.u.axis(*self.km[0])[:2] else: self.zoom_price_end = self.price[zl] self.zoom_km_end = self.km[zl] self._zoom_animation() self.replot() def left_key(self, event): zl = self.zoom_level if zl == 0: return # If at left edge already, don't scroll. kz = self.km[zl] if self.km[0][0] > kz[0]: return self.zoom_price_start = self.zoom_price_end = self.price[zl] self.zoom_km_start = kz self.km[zl] = (kz[0] - (kz[1] - kz[0]), kz[0]) self.zoom_km_end = self.km[zl] self._zoom_animation() self.replot() def right_key(self, event): zl = self.zoom_level if zl == 0: return # If at right edge already, don't scroll. kz = self.km[zl] if self.km[0][1] < kz[1]: return self.zoom_price_start = self.zoom_price_end = self.price[zl] self.zoom_km_start = kz self.km[zl] = (kz[1], kz[1] + (kz[1] - kz[0])) self.zoom_km_end = self.km[zl] self._zoom_animation() self.replot() def down_key(self, event): zl = self.zoom_level if zl == 0: return # If at bottom edge already, don't scroll. pz = self.price[zl] if self.price[0][0] > pz[0]: return self.zoom_km_start = self.zoom_km_end = self.km[zl] self.zoom_price_start = pz self.price[zl] = (pz[0] - (pz[1] - pz[0]), pz[0]) self.zoom_price_end = self.price[zl] self._zoom_animation() self.replot() def up_key(self, event): zl = self.zoom_level if zl == 0: return # If at top edge already, don't scroll. pz = self.price[zl] if self.price[0][1] < pz[1]: return self.zoom_km_start = self.zoom_km_end = self.km[zl] self.zoom_price_start = pz self.price[zl] = (pz[1], pz[1] + (pz[1] - pz[0])) self.zoom_price_end = self.price[zl] self._zoom_animation() self.replot() def select(self, event): "Open a web page, when a data point has been clicked on." oval = event.widget.find_closest(event.x, event.y)[0] # XXX As above, sometimes the closest widget is a grid line, not an # oval. Need to handle this correctly, eventually. if oval not in self.ov_dict: return ind, xp, yp, color, filtered = self.ov_dict[oval] webbrowser.open(self.u.all_alink[ind])
class ScrolledCanvas: def __init__(self, master, **opts): if not opts.has_key('yscrollincrement'): opts['yscrollincrement'] = 20 self.master = master self.frame = TFrame(master, style="FlatFrame") self.frame.rowconfigure(0, weight=1) self.frame.columnconfigure(0, weight=1) self.canvas = Canvas(self.frame, **opts) self.canvas.grid(row=0, column=0, sticky="nsew") self.vbar = TScrollbar(self.frame, name="vbar") self.vbar.grid(row=0, column=1, sticky="nse") self.hbar = TScrollbar(self.frame, name="hbar", orient="horizontal") self.hbar.grid(row=1, column=0, sticky="ews") self.canvas['yscrollcommand'] = lambda f, l: self.scroll_sh(self.vbar, f, l) self.vbar['command'] = self.canvas.yview self.canvas['xscrollcommand'] = lambda f, l: self.scroll_sh(self.hbar, f, l) self.hbar['command'] = self.canvas.xview self.canvas.bind("<Key-Prior>", self.page_up) self.canvas.bind("<Key-Next>", self.page_down) self.canvas.bind("<Key-Up>", self.unit_up) self.canvas.bind("<Key-Down>", self.unit_down) self.canvas.bind("<Alt-Key-2>", self.zoom_height) self.canvas.bind("<Button-4>", self.unit_up) self.canvas.bind("<Button-5>", self.unit_down) self.canvas.focus_set() def page_up(self, event): self.canvas.yview_scroll(-1, "page") return "break" def page_down(self, event): self.canvas.yview_scroll(1, "page") return "break" def unit_up(self, event): first,last=self.vbar.get() if first <= 0 and last >= 1: return "break" self.canvas.yview_scroll(-1, "unit") return "break" def unit_down(self, event): first,last=self.vbar.get() if first <= 0 and last >= 1: return "break" self.canvas.yview_scroll(1, "unit") return "break" def zoom_height(self, event): return "break" def scroll_sh(self, scroll, first, last): first, last = float(first), float(last) if first <= 0 and last >= 1: scroll.grid_remove() else: scroll.grid() scroll.set(first, last)
class PiPresents(object): def pipresents_version(self): vitems=self.pipresents_issue.split('.') if len(vitems)==2: # cope with 2 digit version numbers before 1.3.2 return 1000*int(vitems[0])+100*int(vitems[1]) else: return 1000*int(vitems[0])+100*int(vitems[1])+int(vitems[2]) def __init__(self): # gc.set_debug(gc.DEBUG_UNCOLLECTABLE|gc.DEBUG_INSTANCES|gc.DEBUG_OBJECTS|gc.DEBUG_SAVEALL) gc.set_debug(gc.DEBUG_UNCOLLECTABLE|gc.DEBUG_SAVEALL) self.pipresents_issue="1.3.5" self.pipresents_minorissue = '1.3.5d' # position and size of window without -f command line option self.nonfull_window_width = 0.45 # proportion of width self.nonfull_window_height= 0.7 # proportion of height self.nonfull_window_x = 0 # position of top left corner self.nonfull_window_y=0 # position of top left corner StopWatch.global_enable=False # set up the handler for SIGTERM signal.signal(signal.SIGTERM,self.handle_sigterm) # **************************************** # Initialisation # *************************************** # get command line options self.options=command_options() # get Pi Presents code directory pp_dir=sys.path[0] self.pp_dir=pp_dir if not os.path.exists(pp_dir+"/pipresents.py"): if self.options['manager'] is False: tkMessageBox.showwarning("Pi Presents","Bad Application Directory") exit(102) # Initialise logging and tracing Monitor.log_path=pp_dir self.mon=Monitor() # Init in PiPresents only self.mon.init() # uncomment to enable control of logging from within a class # Monitor.enable_in_code = True # enables control of log level in the code for a class - self.mon.set_log_level() # make a shorter list to log/trace only some classes without using enable_in_code. Monitor.classes = ['PiPresents', 'HyperlinkShow','RadioButtonShow','ArtLiveShow','ArtMediaShow','MediaShow','LiveShow','MenuShow', 'GapShow','Show','ArtShow', 'AudioPlayer','BrowserPlayer','ImagePlayer','MenuPlayer','MessagePlayer','VideoPlayer','Player', 'MediaList','LiveList','ShowList', 'PathManager','ControlsManager','ShowManager','PluginManager','IOPluginManager', 'MplayerDriver','OMXDriver','UZBLDriver', 'TimeOfDay','ScreenDriver','Animate','OSCDriver','CounterManager', 'Network','Mailer' ] # Monitor.classes=['PiPresents','MediaShow','GapShow','Show','VideoPlayer','Player','OMXDriver'] # Monitor.classes=['OSCDriver'] # get global log level from command line Monitor.log_level = int(self.options['debug']) Monitor.manager = self.options['manager'] # print self.options['manager'] self.mon.newline(3) self.mon.sched (self,None, "Pi Presents is starting, Version:"+self.pipresents_minorissue + ' at '+time.strftime("%Y-%m-%d %H:%M.%S")) self.mon.log (self, "Pi Presents is starting, Version:"+self.pipresents_minorissue+ ' at '+time.strftime("%Y-%m-%d %H:%M.%S")) # self.mon.log (self," OS and separator:" + os.name +' ' + os.sep) self.mon.log(self,"sys.path[0] - location of code: "+sys.path[0]) # log versions of Raspbian and omxplayer, and GPU Memory with open("/boot/issue.txt") as ifile: self.mon.log(self,'\nRaspbian: '+ifile.read()) self.mon.log(self,'\n'+check_output(["omxplayer", "-v"])) self.mon.log(self,'\nGPU Memory: '+check_output(["vcgencmd", "get_mem", "gpu"])) if os.geteuid() == 0: print 'Do not run Pi Presents with sudo' self.mon.log(self,'Do not run Pi Presents with sudo') self.mon.finish() sys.exit(102) if "DESKTOP_SESSION" not in os.environ: print 'Pi Presents must be run from the Desktop' self.mon.log(self,'Pi Presents must be run from the Desktop') self.mon.finish() sys.exit(102) else: self.mon.log(self,'Desktop is '+ os.environ['DESKTOP_SESSION']) # optional other classes used self.root=None self.ppio=None self.tod=None self.animate=None self.ioplugin_manager=None self.oscdriver=None self.osc_enabled=False self.tod_enabled=False self.email_enabled=False user=os.getenv('USER') if user is None: tkMessageBox.showwarning("You must be logged in to run Pi Presents") exit(102) if user !='pi': self.mon.warn(self,"You must be logged as pi to use GPIO") self.mon.log(self,'User is: '+ user) # self.mon.log(self,"os.getenv('HOME') - user home directory (not used): " + os.getenv('HOME')) # does not work # self.mon.log(self,"os.path.expanduser('~') - user home directory: " + os.path.expanduser('~')) # does not work # check network is available self.network_connected=False self.network_details=False self.interface='' self.ip='' self.unit='' # sets self.network_connected and self.network_details self.init_network() # start the mailer and send email when PP starts self.email_enabled=False if self.network_connected is True: self.init_mailer() if self.email_enabled is True and self.mailer.email_at_start is True: subject= '[Pi Presents] ' + self.unit + ': PP Started on ' + time.strftime("%Y-%m-%d %H:%M") message = time.strftime("%Y-%m-%d %H:%M") + '\nUnit: ' + self.unit + ' Profile: '+ self.options['profile']+ '\n ' + self.interface + '\n ' + self.ip self.send_email('start',subject,message) # get profile path from -p option if self.options['profile'] != '': self.pp_profile_path="/pp_profiles/"+self.options['profile'] else: self.mon.err(self,"Profile not specified in command ") self.end('error','Profile not specified with the commands -p option') # get directory containing pp_home from the command, if self.options['home'] == "": home = os.sep+ 'home' + os.sep + user + os.sep+"pp_home" else: home = self.options['home'] + os.sep+ "pp_home" self.mon.log(self,"pp_home directory is: " + home) # check if pp_home exists. # try for 10 seconds to allow usb stick to automount found=False for i in range (1, 10): self.mon.log(self,"Trying pp_home at: " + home + " (" + str(i)+')') if os.path.exists(home): found=True self.pp_home=home break time.sleep (1) if found is True: self.mon.log(self,"Found Requested Home Directory, using pp_home at: " + home) else: self.mon.err(self,"Failed to find pp_home directory at " + home) self.end('error',"Failed to find pp_home directory at " + home) # check profile exists self.pp_profile=self.pp_home+self.pp_profile_path if os.path.exists(self.pp_profile): self.mon.sched(self,None,"Running profile: " + self.pp_profile_path) self.mon.log(self,"Found Requested profile - pp_profile directory is: " + self.pp_profile) else: self.mon.err(self,"Failed to find requested profile: "+ self.pp_profile) self.end('error',"Failed to find requested profile: "+ self.pp_profile) self.mon.start_stats(self.options['profile']) if self.options['verify'] is True: self.mon.err(self,"Validation option not supported - use the editor") self.end('error','Validation option not supported - use the editor') # initialise and read the showlist in the profile self.showlist=ShowList() self.showlist_file= self.pp_profile+ "/pp_showlist.json" if os.path.exists(self.showlist_file): self.showlist.open_json(self.showlist_file) else: self.mon.err(self,"showlist not found at "+self.showlist_file) self.end('error',"showlist not found at "+self.showlist_file) # check profile and Pi Presents issues are compatible if self.showlist.profile_version() != self.pipresents_version(): self.mon.err(self,"Version of showlist " + self.showlist.profile_version_string + " is not same as Pi Presents") self.end('error',"Version of showlist " + self.showlist.profile_version_string + " is not same as Pi Presents") # get the 'start' show from the showlist index = self.showlist.index_of_start_show() if index >=0: self.showlist.select(index) self.starter_show=self.showlist.selected_show() else: self.mon.err(self,"Show [start] not found in showlist") self.end('error',"Show [start] not found in showlist") # ******************** # SET UP THE GUI # ******************** # turn off the screenblanking and saver if self.options['noblank'] is True: call(["xset","s", "off"]) call(["xset","s", "-dpms"]) self.root=Tk() self.title='Pi Presents - '+ self.pp_profile self.icon_text= 'Pi Presents' self.root.title(self.title) self.root.iconname(self.icon_text) self.root.config(bg=self.starter_show['background-colour']) self.mon.log(self, 'monitor screen dimensions are ' + str(self.root.winfo_screenwidth()) + ' x ' + str(self.root.winfo_screenheight()) + ' pixels') if self.options['screensize'] =='': self.screen_width = self.root.winfo_screenwidth() self.screen_height = self.root.winfo_screenheight() else: reason,message,self.screen_width,self.screen_height=self.parse_screen(self.options['screensize']) if reason =='error': self.mon.err(self,message) self.end('error',message) self.mon.log(self, 'forced screen dimensions (--screensize) are ' + str(self.screen_width) + ' x ' + str(self.screen_height) + ' pixels') # set window dimensions and decorations if self.options['fullscreen'] is False: self.window_width=int(self.root.winfo_screenwidth()*self.nonfull_window_width) self.window_height=int(self.root.winfo_screenheight()*self.nonfull_window_height) self.window_x=self.nonfull_window_x self.window_y=self.nonfull_window_y self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y)) else: self.window_width=self.screen_width self.window_height=self.screen_height self.root.attributes('-fullscreen', True) os.system('unclutter &') self.window_x=0 self.window_y=0 self.root.geometry("%dx%d%+d%+d" % (self.window_width,self.window_height,self.window_x,self.window_y)) self.root.attributes('-zoomed','1') # canvas cover the whole screen whatever the size of the window. self.canvas_height=self.screen_height self.canvas_width=self.screen_width # make sure focus is set. self.root.focus_set() # define response to main window closing. self.root.protocol ("WM_DELETE_WINDOW", self.handle_user_abort) # setup a canvas onto which will be drawn the images or text self.canvas = Canvas(self.root, bg=self.starter_show['background-colour']) if self.options['fullscreen'] is True: self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=0) else: self.canvas.config(height=self.canvas_height, width=self.canvas_width, highlightthickness=1, highlightcolor='yellow') self.canvas.place(x=0,y=0) # self.canvas.config(bg='black') self.canvas.focus_set() # **************************************** # INITIALISE THE TOUCHSCREEN DRIVER # **************************************** # each driver takes a set of inputs, binds them to symboic names # and sets up a callback which returns the symbolic name when an input event occurs self.sr=ScreenDriver() # read the screen click area config file reason,message = self.sr.read(pp_dir,self.pp_home,self.pp_profile) if reason == 'error': self.end('error','cannot find, or error in screen.cfg') # create click areas on the canvas, must be polygon as outline rectangles are not filled as far as find_closest goes # click areas are made on the Pi Presents canvas not the show canvases. reason,message = self.sr.make_click_areas(self.canvas,self.handle_input_event) if reason == 'error': self.mon.err(self,message) self.end('error',message) # **************************************** # INITIALISE THE APPLICATION AND START # **************************************** self.shutdown_required=False self.reboot_required=False self.terminate_required=False self.exitpipresents_required=False # initialise the I/O plugins by importing their drivers self.ioplugin_manager=IOPluginManager() reason,message=self.ioplugin_manager.init(self.pp_dir,self.pp_profile,self.root,self.handle_input_event) if reason == 'error': # self.mon.err(self,message) self.end('error',message) # kick off animation sequencer self.animate = Animate() self.animate.init(pp_dir,self.pp_home,self.pp_profile,self.canvas,200,self.handle_output_event) self.animate.poll() #create a showmanager ready for time of day scheduler and osc server show_id=-1 self.show_manager=ShowManager(show_id,self.showlist,self.starter_show,self.root,self.canvas,self.pp_dir,self.pp_profile,self.pp_home) # first time through set callback to terminate Pi Presents if all shows have ended. self.show_manager.init(self.canvas,self.all_shows_ended_callback,self.handle_command,self.showlist) # Register all the shows in the showlist reason,message=self.show_manager.register_shows() if reason == 'error': self.mon.err(self,message) self.end('error',message) # Init OSCDriver, read config and start OSC server self.osc_enabled=False if self.network_connected is True: if os.path.exists(self.pp_profile + os.sep + 'pp_io_config'+ os.sep + 'osc.cfg'): self.oscdriver=OSCDriver() reason,message=self.oscdriver.init(self.pp_profile, self.unit,self.interface,self.ip, self.handle_command,self.handle_input_event,self.e_osc_handle_animate) if reason == 'error': self.mon.err(self,message) self.end('error',message) else: self.osc_enabled=True self.root.after(1000,self.oscdriver.start_server()) # initialise ToD scheduler calculating schedule for today self.tod=TimeOfDay() reason,message,self.tod_enabled = self.tod.init(pp_dir,self.pp_home,self.pp_profile,self.showlist,self.root,self.handle_command) if reason == 'error': self.mon.err(self,message) self.end('error',message) # warn if the network not available when ToD required if self.tod_enabled is True and self.network_connected is False: self.mon.warn(self,'Network not connected so Time of Day scheduler may be using the internal clock') # init the counter manager self.counter_manager=CounterManager() self.counter_manager.init() # warn about start shows and scheduler if self.starter_show['start-show']=='' and self.tod_enabled is False: self.mon.sched(self,None,"No Start Shows in Start Show and no shows scheduled") self.mon.warn(self,"No Start Shows in Start Show and no shows scheduled") if self.starter_show['start-show'] !='' and self.tod_enabled is True: self.mon.sched(self,None,"Start Shows in Start Show and shows scheduled - conflict?") self.mon.warn(self,"Start Shows in Start Show and shows scheduled - conflict?") # run the start shows self.run_start_shows() # kick off the time of day scheduler which may run additional shows if self.tod_enabled is True: self.tod.poll() # start the I/O plugins input event generation self.ioplugin_manager.start() # start Tkinters event loop self.root.mainloop( ) def parse_screen(self,size_text): fields=size_text.split('*') if len(fields)!=2: return 'error','do not understand --screensize comand option',0,0 elif fields[0].isdigit() is False or fields[1].isdigit() is False: return 'error','dimensions are not positive integers in --screensize',0,0 else: return 'normal','',int(fields[0]),int(fields[1]) # ********************* # RUN START SHOWS # ******************** def run_start_shows(self): self.mon.trace(self,'run start shows') # parse the start shows field and start the initial shows show_refs=self.starter_show['start-show'].split() for show_ref in show_refs: reason,message=self.show_manager.control_a_show(show_ref,'open') if reason == 'error': self.mon.err(self,message) # ********************* # User inputs # ******************** def e_osc_handle_animate(self,line): #jump out of server thread self.root.after(1, lambda arg=line: self.osc_handle_animate(arg)) def osc_handle_animate(self,line): self.mon.log(self,"animate command received: "+ line) #osc sends output events as a string reason,message,delay,name,param_type,param_values=self.animate.parse_animate_fields(line) if reason == 'error': self.mon.err(self,message) self.end(reason,message) self.handle_output_event(name,param_type,param_values,0) # output events are animate commands def handle_output_event(self,symbol,param_type,param_values,req_time): reason,message=self.ioplugin_manager.handle_output_event(symbol,param_type,param_values,req_time) if reason =='error': self.mon.err(self,message) self.end(reason,message) # all input events call this callback providing a symbolic name. # handle events that affect PP overall, otherwise pass to all active shows def handle_input_event(self,symbol,source): self.mon.log(self,"event received: "+symbol + ' from '+ source) if symbol == 'pp-terminate': self.handle_user_abort() elif symbol == 'pp-shutdown': self.mon.err(self,'pp-shutdown removed in version 1.3.3a, see Release Notes') self.end('error','pp-shutdown removed in version 1.3.3a, see Release Notes') elif symbol == 'pp-shutdownnow': # need root.after to grt out of st thread self.root.after(1,self.shutdownnow_pressed) return elif symbol == 'pp-exitpipresents': self.exitpipresents_required=True if self.show_manager.all_shows_exited() is True: # need root.after to grt out of st thread self.root.after(1,self.e_all_shows_ended_callback) return reason,message= self.show_manager.exit_all_shows() else: # pass the input event to all registered shows for show in self.show_manager.shows: show_obj=show[ShowManager.SHOW_OBJ] if show_obj is not None: show_obj.handle_input_event(symbol) # commands are generaed by tracks and shows # they can open or close shows, generate input events and do special tasks # commands also generate osc outputs to other computers # handles one command provided as a line of text def handle_command(self,command_text,source='',show=''): # print 'PIPRESENTS ',command_text,'\n Source',source,'from',show self.mon.log(self,"command received: " + command_text) if command_text.strip()=="": return fields= command_text.split() if fields[0] in ('osc','OSC'): if self.osc_enabled is True: status,message=self.oscdriver.parse_osc_command(fields[1:]) if status=='warn': self.mon.warn(self,message) if status=='error': self.mon.err(self,message) self.end('error',message) return if fields[0] =='counter': status,message=self.counter_manager.parse_counter_command(fields[1:]) if status=='error': self.mon.err(self,message) self.end('error',message) return show_command=fields[0] if len(fields)>1: show_ref=fields[1] else: show_ref='' if show_command in ('open','close','closeall','openexclusive'): self.mon.sched(self, TimeOfDay.now,command_text + ' received from show:'+show) if self.shutdown_required is False and self.terminate_required is False: reason,message=self.show_manager.control_a_show(show_ref,show_command) else: return elif show_command =='monitor': self.handle_monitor_command(show_ref) return elif show_command =='cec': self.handle_cec_command(show_ref) return elif show_command == 'event': self.handle_input_event(show_ref,'Show Control') return elif show_command == 'exitpipresents': self.exitpipresents_required=True if self.show_manager.all_shows_exited() is True: # need root.after to get out of st thread self.root.after(1,self.e_all_shows_ended_callback) return else: reason,message= self.show_manager.exit_all_shows() elif show_command == 'shutdownnow': # need root.after to get out of st thread self.root.after(1,self.shutdownnow_pressed) return elif show_command == 'reboot': # need root.after to get out of st thread self.root.after(1,self.reboot_pressed) return else: reason='error' message = 'command not recognised: '+ show_command if reason=='error': self.mon.err(self,message) return def handle_monitor_command(self,command): if command == 'on': os.system('vcgencmd display_power 1 >/dev/null') elif command == 'off': os.system('vcgencmd display_power 0 >/dev/null') def handle_cec_command(self,command): if command == 'on': os.system('echo "on 0" | cec-client -s') elif command == 'standby': os.system('echo "standby 0" | cec-client -s') elif command == 'scan': os.system('echo scan | cec-client -s -d 1') # deal with differnt commands/input events def shutdownnow_pressed(self): self.shutdown_required=True if self.show_manager.all_shows_exited() is True: self.all_shows_ended_callback('normal','no shows running') else: # calls exit method of all shows, results in all_shows_closed_callback self.show_manager.exit_all_shows() def reboot_pressed(self): self.reboot_required=True if self.show_manager.all_shows_exited() is True: self.all_shows_ended_callback('normal','no shows running') else: # calls exit method of all shows, results in all_shows_closed_callback self.show_manager.exit_all_shows() def handle_sigterm(self,signum,fframe): self.mon.log(self,'SIGTERM received - '+ str(signum)) self.terminate() def handle_user_abort(self): self.mon.log(self,'User abort received') self.terminate() def terminate(self): self.mon.log(self, "terminate received") self.terminate_required=True needs_termination=False for show in self.show_manager.shows: # print show[ShowManager.SHOW_OBJ], show[ShowManager.SHOW_REF] if show[ShowManager.SHOW_OBJ] is not None: needs_termination=True self.mon.log(self,"Sent terminate to show "+ show[ShowManager.SHOW_REF]) # call shows terminate method # eventually the show will exit and after all shows have exited all_shows_callback will be executed. show[ShowManager.SHOW_OBJ].terminate() if needs_termination is False: self.end('killed','killed - no termination of shows required') # ****************************** # Ending Pi Presents after all the showers and players are closed # ************************** def e_all_shows_ended_callback(self): self.all_shows_ended_callback('normal','no shows running') # callback from ShowManager when all shows have ended def all_shows_ended_callback(self,reason,message): self.canvas.config(bg=self.starter_show['background-colour']) if reason in ('killed','error') or self.shutdown_required is True or self.exitpipresents_required is True or self.reboot_required is True: self.end(reason,message) def end(self,reason,message): self.mon.log(self,"Pi Presents ending with reason: " + reason) if self.root is not None: self.root.destroy() self.tidy_up() if reason == 'killed': if self.email_enabled is True and self.mailer.email_on_terminate is True: subject= '[Pi Presents] ' + self.unit + ': PP Exited with reason: Terminated' message = time.strftime("%Y-%m-%d %H:%M") + '\n ' + self.unit + '\n ' + self.interface + '\n ' + self.ip self.send_email(reason,subject,message) self.mon.sched(self, None,"Pi Presents Terminated, au revoir\n") self.mon.log(self, "Pi Presents Terminated, au revoir") # close logging files self.mon.finish() print 'Uncollectable Garbage',gc.collect() # objgraph.show_backrefs(objgraph.by_type('Monitor')) sys.exit(101) elif reason == 'error': if self.email_enabled is True and self.mailer.email_on_error is True: subject= '[Pi Presents] ' + self.unit + ': PP Exited with reason: Error' message_text = 'Error message: '+ message + '\n'+ time.strftime("%Y-%m-%d %H:%M") + '\n ' + self.unit + '\n ' + self.interface + '\n ' + self.ip self.send_email(reason,subject,message_text) self.mon.sched(self,None, "Pi Presents closing because of error, sorry\n") self.mon.log(self, "Pi Presents closing because of error, sorry") # close logging files self.mon.finish() print 'uncollectable garbage',gc.collect() sys.exit(102) else: self.mon.sched(self,None,"Pi Presents exiting normally, bye\n") self.mon.log(self,"Pi Presents exiting normally, bye") # close logging files self.mon.finish() if self.reboot_required is True: # print 'REBOOT' call (['sudo','reboot']) if self.shutdown_required is True: # print 'SHUTDOWN' call (['sudo','shutdown','now','SHUTTING DOWN']) print 'uncollectable garbage',gc.collect() sys.exit(100) # tidy up all the peripheral bits of Pi Presents def tidy_up(self): self.handle_monitor_command('on') self.mon.log(self, "Tidying Up") # turn screen blanking back on if self.options['noblank'] is True: call(["xset","s", "on"]) call(["xset","s", "+dpms"]) # tidy up animation if self.animate is not None: self.animate.terminate() # tidy up i/o plugins if self.ioplugin_manager != None: self.ioplugin_manager.terminate() if self.osc_enabled is True: self.oscdriver.terminate() # tidy up time of day scheduler if self.tod_enabled is True: self.tod.terminate() # ******************************* # Connecting to network and email # ******************************* def init_network(self): timeout=int(self.options['nonetwork']) if timeout== 0: self.network_connected=False self.unit='' self.ip='' self.interface='' return self.network=Network() self.network_connected=False # try to connect to network self.mon.log (self, 'Waiting up to '+ str(timeout) + ' seconds for network') success=self.network.wait_for_network(timeout) if success is False: self.mon.warn(self,'Failed to connect to network after ' + str(timeout) + ' seconds') # tkMessageBox.showwarning("Pi Presents","Failed to connect to network so using fake-hwclock") return self.network_connected=True self.mon.sched (self, None,'Time after network check is '+ time.strftime("%Y-%m-%d %H:%M.%S")) self.mon.log (self, 'Time after network check is '+ time.strftime("%Y-%m-%d %H:%M.%S")) # Get web configuration self.network_details=False network_options_file_path=self.pp_dir+os.sep+'pp_config'+os.sep+'pp_web.cfg' if not os.path.exists(network_options_file_path): self.mon.warn(self,"pp_web.cfg not found at "+network_options_file_path) return self.mon.log(self, 'Found pp_web.cfg in ' + network_options_file_path) self.network.read_config(network_options_file_path) self.unit=self.network.unit # get interface and IP details of preferred interface self.interface,self.ip = self.network.get_preferred_ip() if self.interface == '': self.network_connected=False return self.network_details=True self.mon.log (self, 'Network details ' + self.unit + ' ' + self.interface + ' ' +self.ip) def init_mailer(self): self.email_enabled=False email_file_path = self.pp_dir+os.sep+'pp_config'+os.sep+'pp_email.cfg' if not os.path.exists(email_file_path): self.mon.log(self,'pp_email.cfg not found at ' + email_file_path) return self.mon.log(self,'Found pp_email.cfg at ' + email_file_path) self.mailer=Mailer() self.mailer.read_config(email_file_path) # all Ok so can enable email if config file allows it. if self.mailer.email_allowed is True: self.email_enabled=True self.mon.log (self,'Email Enabled') def try_connect(self): tries=1 while True: success, error = self.mailer.connect() if success is True: return True else: self.mon.log(self,'Failed to connect to email SMTP server ' + str(tries) + '\n ' +str(error)) tries +=1 if tries >5: self.mon.log(self,'Failed to connect to email SMTP server after ' + str(tries)) return False def send_email(self,reason,subject,message): if self.try_connect() is False: return False else: success,error = self.mailer.send(subject,message) if success is False: self.mon.log(self, 'Failed to send email: ' + str(error)) success,error=self.mailer.disconnect() if success is False: self.mon.log(self,'Failed disconnect after send:' + str(error)) return False else: self.mon.log(self,'Sent email for ' + reason) success,error=self.mailer.disconnect() if success is False: self.mon.log(self,'Failed disconnect from email server ' + str(error)) return True