Example #1
0
class SudokuUI(tk.Frame):
    def __init__(self, parent, size, positions):
        tk.Frame.__init__(self, parent)
        self.size = size
        self.parent = parent
        self.parent.title("Sudoku")
        self.__define_menu()
        self.cursor = None
        self.__key_bind()
        self.pack(fill=tk.BOTH, padx=50, pady=50)
        self.grid_canvas = tk.Canvas(self)
        self.grid_canvas.pack(anchor="center", side=tk.LEFT)
        
        self.__new_board(positions)
        
        self.__draw_buttons()
        self.focus_set()

    def __new_board(self, positions):
        self.board = Game(positions)
        self.board_vars = {}
        self.grid_canvas.delete("all")
        self.__draw_grid(self.size)
        
    def __open_board(self):
        self.parent.filename = filedialog.askopenfilename(initialdir = "/",title = "Select file",filetypes = (("text files","*.txt"),("all files","*.*")))
        f = open(self.parent.filename, "r")
        positions = eval(f.read())
        f.close()
        self.__new_board(positions)
        
    def __draw_grid(self, size):
        # set up the grid. Even i's are the lines and odd i's are the cells
        for i in range(19):
            # make the line thicker for the border of 3x3
            if i % 6 == 0:
                w = 2
            elif i % 2 == 0:
                w = 1
            else:
                w = size
            self.grid_canvas.grid_rowconfigure(i, minsize=w)
            self.grid_canvas.grid_columnconfigure(i, minsize=w)
        # Construct the sudoku cells
        self.cells = {}
        for i in range(9):
            for j in range(9):
                if (i, j) in self.board.initial_cells:
                    e = tk.Label(self.grid_canvas, bg="white", cursor="hand2",
                                 text=str(self.board.initial_board[(i, j)][1]), font=bigfont)
                else:
                    var = tk.StringVar()
                    self.board_vars[(i, j)] = var
                    e = tk.Label(self.grid_canvas, textvariable=var, fg="navy", bg="white", cursor="hand2", font=bigfont)
                e.bind("<Button-1>", self.__cell_clicked)
                e.grid(row=2 * i + 1, column=2 * j + 1, sticky="nesw")
                e.config(wraplength=50)
                self.cells[(i, j)] = e
        # Draw the grid lines
        for i in range(10):
            tk.Frame(self.grid_canvas, bg='black').grid(
                row=2 * i, column=0, columnspan=19, sticky="news")
            tk.Frame(self.grid_canvas, bg='black').grid(
                column=2 * i, row=0, rowspan=19, sticky="news")

    def __draw_buttons(self):
        self.button_frame = tk.Frame(self)
        self.button_frame.pack(side=tk.LEFT, padx=20)
        self.inputType = tk.StringVar(self.button_frame, NORMAL)
        tk.Radiobutton(self.button_frame, text="Normal(n)", variable=self.inputType, value=NORMAL).pack(anchor=tk.W)
        tk.Radiobutton(self.button_frame, text="Centre(c)", variable=self.inputType, value=CENTRE).pack(anchor=tk.W)
        tk.Radiobutton(self.button_frame, text="Top(t)", variable=self.inputType, value=TOP).pack(anchor=tk.W)
        self.clear_button = tk.Button(self.button_frame, text="Clear", command=self.__clear)
        self.clear_button.pack(fill=tk.BOTH, side=tk.LEFT)
        self.check_button = tk.Button(self.button_frame, text="Check", command=self.__check)
        self.check_button.pack(fill=tk.BOTH, side=tk.RIGHT)

    def __define_menu(self):
        menu_bar = tk.Menu(self.parent)
        file_menu = tk.Menu(menu_bar, tearoff=0)
        file_menu.add_command(label="New Ctrl+N", command=(lambda: self.__new_board([])))
        file_menu.add_command(label="Open Ctrl+O", command=self.__open_board)
        #file_menu.add_command(label="Save", command=placeholder)
        file_menu.add_separator()
        file_menu.add_command(label="Exit", command=self.parent.quit)
        menu_bar.add_cascade(label="File", menu=file_menu)
        
        #help_menu = tk.Menu(menu_bar, tearoff=0)
        #help_menu.add_command(label="Help Index", command=placeholder)
        #help_menu.add_command(label="About...", command=placeholder)
        #menu_bar.add_cascade(label="Help", menu=help_menu)

        self.parent.config(menu=menu_bar)

    def __cell_clicked(self, event):
        self.__move_cursor(event.widget)

    def __move_cursor(self, new):
        if self.cursor:
            self.cursor["relief"] = "flat"
        self.cursor = new
        new["relief"] = "solid"

    def __key_bind(self):
        self.bind('<Left>', lambda event: self.__movement("left"))
        self.bind('<Right>', lambda event: self.__movement("right"))
        self.bind('<Up>', lambda event: self.__movement("up"))
        self.bind('<Down>', lambda event: self.__movement("down"))
        self.bind('<n>', lambda event: self.__change_input_type(NORMAL))
        self.bind('<c>', lambda event: self.__change_input_type(CENTRE))
        self.bind('<t>', lambda event: self.__change_input_type(TOP))
        self.bind('<Control-n>', lambda event: self.__new_board([]))
        self.bind('<Control-o>', lambda event: self.__open_board())
        self.bind('<Key>', self.__key_pressed)

    def __movement(self, direction):
        if self.cursor:
            i, j = self.__cell_cursor()
            if direction == "left" and  j != 0:  # check if we can go left
                self.__move_cursor(self.cells[(i, j - 1)])
            elif direction == "right" and j != 8:  # check if we can go right
                self.__move_cursor(self.cells[(i, j + 1)])
            elif direction == "up" and i != 0:  # check if we can go up
                self.__move_cursor(self.cells[(i - 1, j)])
            elif direction == "down" and i != 8:  # check if we can go down
                self.__move_cursor(self.cells[(i + 1, j)])
                
    def __change_input_type(self, input_type):
        self.inputType.set(input_type)

    def __key_pressed(self, event):
        if event.char in list("123456789"):
            self.__update_cell(event.char)

    def __update_cell(self, c):
        if self.cursor:
            coord = self.__cell_cursor()
            int_c = int(c)
            if coord not in self.board.initial_cells:
                if self.inputType.get() == NORMAL:
                    self.board.board[coord] = (NORMAL, int_c)
                elif self.inputType.get() == CENTRE:
                    if self.board.board[coord][0] != CENTRE:
                        self.board.board[coord] = (CENTRE, set([int_c]))
                    else:
                        self.board.board[coord][1].symmetric_difference_update([int_c])
                elif self.inputType.get() == TOP:
                    if self.board.board[coord][0] != TOP:
                        self.board.board[coord] = (TOP, set([int_c]))
                    else:
                        self.board.board[coord][1].symmetric_difference_update([int_c])
                self.__update_cell_UI(coord)

    def __update_cell_UI(self, coord):
        status, val = self.board.board[coord]
        if status == NORMAL:
            self.cells[coord].config(anchor=tk.CENTER, font=bigfont, fg="navy")
            self.board_vars[coord].set(val)
        elif status == CENTRE:
            self.cells[coord].config(anchor=tk.CENTER, font=smallfont, fg="blue")
            self.board_vars[coord].set(" ".join(sorted([str(e) for e in val])))
        elif status == TOP:
            self.cells[coord].config(anchor=tk.N, font=smallfont, fg="blue")
            self.board_vars[coord].set(" ".join(sorted([str(e) for e in val])))
    
    def __cell_cursor(self):
        return self.cursor.grid_info()["row"] // 2, self.cursor.grid_info()["column"] // 2

    def __clear(self):
        self.board.reset_board()
        for i in range(9):
            for j in range(9):
                if (i, j) not in self.board.initial_cells:
                    self.board_vars[(i, j)].set("")

    def __check(self):
        tk.messagebox.showinfo(message=self.board.check_win()[1])