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()
Example #2
0
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()
Example #3
0
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"
Example #4
0
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"
Example #5
0
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()
Example #6
0
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))
Example #7
0
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
Example #8
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
Example #9
0
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()
Example #11
0
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()
Example #12
0
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)
Example #13
0
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))
Example #14
0
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))
Example #15
0
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)
Example #16
0
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()
Example #17
0
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()
Example #18
0
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
Example #19
0
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)
Example #20
0
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()
Example #21
0
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()
Example #23
0
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
Example #24
0
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
Example #25
0
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 
Example #30
0
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()
Example #31
0
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)
	    )
Example #32
0
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()
Example #33
0
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
Example #35
0
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)
Example #36
0
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()
Example #37
0
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()
Example #39
0
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()
Example #40
0
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")
Example #41
0
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" )
Example #42
0
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()
Example #43
0
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))
Example #44
0
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()
Example #45
0
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])
Example #46
-1
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)
Example #47
-1
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