From 60d322432a67d4fb01f1723e1756905f32729539 Mon Sep 17 00:00:00 2001 From: Veronica Date: Sat, 11 Jan 2014 18:10:09 -0800 Subject: [PATCH 1/3] Outline for rest of User Interface Setup --- .../2013-05-06-Part-1-Sudoku-Board-Setup.md | 167 +++++++++++++++++- 1 file changed, 165 insertions(+), 2 deletions(-) diff --git a/website/_posts/GUI/2013-05-06-Part-1-Sudoku-Board-Setup.md b/website/_posts/GUI/2013-05-06-Part-1-Sudoku-Board-Setup.md index 42018272..15ae7c88 100644 --- a/website/_posts/GUI/2013-05-06-Part-1-Sudoku-Board-Setup.md +++ b/website/_posts/GUI/2013-05-06-Part-1-Sudoku-Board-Setup.md @@ -65,7 +65,7 @@ def parse_arguments(): You can read more about Python's argparse module by working through this [howto](http://docs.python.org/2/howto/argparse.html). Basically, `argparse` allows us to define the usage of arguments for the command line, as well as setting up how many arguments to expect (`add_argument`), the flags for the arguments (`--level`), defaults (`default=-1`), etc, as well as returning the exact parsed argument we want, `args['level']` and `args['board']`. -You'll notice in the second argument, `--board`, callese the `check_negative` function. This is just to check in case a user passes a negative number for a board number (only positive numbers are allowed): +You'll notice in the second argument, `--board`, calls the `check_negative` function. This is just to check in case a user passes a negative number for a board number (only positive numbers are allowed): ```python def check_negative(value): @@ -116,4 +116,167 @@ class SudokuUI(Frame): self.__initUI() ``` -We inherit from Tkinter's `Frame` class, and initiallize the board \ No newline at end of file +We inherit from Tkinter's `Frame` class, and initiallize the board. +Why is parent an argument? What does it represent? +We set self.row and self.col equal to -1 because initially no cell is selected. + + +```python + 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("", self.__cell_clicked) + self.canvas.bind("", self.__key_pressed) +``` + +Sudoku is the title of the GUI. It appears on the grey top part +The packer allows you to specify the appearance of your GUI. For example, where the elements are placed. +The Canvas widget is a rectangular area intended for drawing pictures or other complex layouts. On it you can place graphics, text, widgets, or frames. + What is a widget? +We create a button widget with the text Clear Answers. When it is clicked, self.__clear_answers is called. +We then call self.__draw_grid() and self.__draw_puzzle(). +You can learn more about bindings and events by reading this [howto](http://docs.python.org/2/library/tkinter.html#bindings-and-events). Basically.... + +```python +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) +``` + +We draw Sudoku grid. + +```python +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 + ) +``` + +We add the provided numbers in black to the grid. The numbers the user inpus will be in grey. + + +```python +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" + ) +``` + +We highlight the curently selected box in red. + +```python + 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) + ) +``` + +When the user solves the puzzle.... + +```python +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() +``` + +This ascertains which cell the user clicks on and calls self.__draw_cursor() if the cell was not already selected. It unselects the row if it was already selected. + +```python + 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() +``` + +This sets the cell value equal to what the user types only when they type an integer 1-9. It also sets the user input to self.game.answer[self.row][self.col] and redraws the puzzle with the selected curser. Now the Sudoku grid shows the new user input. Finally it checks to see if the user has now solved the puzzle. + +```python + def __clear_answers(self): + self.game.set_answer_to_puzzle() + self.canvas.delete("victory") + self.__draw_puzzle() +``` + +This deletes all user submitted answers.... +Uses set_answer_to_puzzle() from SudokuGame +Deletes everything on the canvas with tag="victory" ???? +And then dras the puzzle \ No newline at end of file From 35f6655cd9596f244193cb85acd7919aa23ae782 Mon Sep 17 00:00:00 2001 From: Veronica Date: Sun, 12 Jan 2014 15:48:48 -0800 Subject: [PATCH 2/3] Progress on User Interface Setup --- .../2013-05-06-Part-1-Sudoku-Board-Setup.md | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/website/_posts/GUI/2013-05-06-Part-1-Sudoku-Board-Setup.md b/website/_posts/GUI/2013-05-06-Part-1-Sudoku-Board-Setup.md index 15ae7c88..4a655ada 100644 --- a/website/_posts/GUI/2013-05-06-Part-1-Sudoku-Board-Setup.md +++ b/website/_posts/GUI/2013-05-06-Part-1-Sudoku-Board-Setup.md @@ -118,7 +118,7 @@ class SudokuUI(Frame): We inherit from Tkinter's `Frame` class, and initiallize the board. Why is parent an argument? What does it represent? -We set self.row and self.col equal to -1 because initially no cell is selected. +We set self.row and self.col equal to -1 because initially no cell is selected. We other methods we check if self.row and self.col are >= 0. If so, a cell on the grid is selected. ```python @@ -144,12 +144,17 @@ We set self.row and self.col equal to -1 because initially no cell is selected. ``` Sudoku is the title of the GUI. It appears on the grey top part -The packer allows you to specify the appearance of your GUI. For example, where the elements are placed. + +The packer allows you to specify the relative positioning of widgets within their container. To make our lives easier it takes a qualitative relationship specification and determines the exact placement coordinates for us. A widget will appear only after its geometry is specified. In this case, when the packer’s pack() method is applied to it. We use the packer options fill, expand and side. +What does fill=BOTH mean? +What does expand=1 mean? + The Canvas widget is a rectangular area intended for drawing pictures or other complex layouts. On it you can place graphics, text, widgets, or frames. - What is a widget? + We create a button widget with the text Clear Answers. When it is clicked, self.__clear_answers is called. We then call self.__draw_grid() and self.__draw_puzzle(). -You can learn more about bindings and events by reading this [howto](http://docs.python.org/2/library/tkinter.html#bindings-and-events). Basically.... + +The last two lines use the concepts of bindings and events. Basically we specify an event (ex `""`) that always triggers a certain function.`` corresponds to clicking on the mouse. Functions called in this way are commonly known as callbacks. You can learn more about bindings and events by reading this [howto](http://docs.python.org/2/library/tkinter.html#bindings-and-events). ```python def __draw_grid(self): @@ -172,7 +177,7 @@ def __draw_grid(self): self.canvas.create_line(x0, y0, x1, y1, fill=color) ``` -We draw Sudoku grid. +We draw the Sudoku grid. ```python def __draw_puzzle(self): @@ -190,8 +195,10 @@ def __draw_puzzle(self): ) ``` -We add the provided numbers in black to the grid. The numbers the user inpus will be in grey. - +This adds the provided numbers in black to the grid. The numbers the user inputs will be in grey. +why would answer == 0? does each cell have answer == 0 initially if its value is not supplied to the user? +This deletes all the user submitted answers and and then iterates through the board, coloring the origional numbers in the board black and the user submitted answers slate gray. It then .... +This is called when the board is initialized, when a key is pressed and when the user clears their answers. ```python def __draw_cursor(self): @@ -279,4 +286,4 @@ This sets the cell value equal to what the user types only when they type an int This deletes all user submitted answers.... Uses set_answer_to_puzzle() from SudokuGame Deletes everything on the canvas with tag="victory" ???? -And then dras the puzzle \ No newline at end of file +And then draw the puzzle \ No newline at end of file From 274e1bafd044504b95610786e693c1aca490dd13 Mon Sep 17 00:00:00 2001 From: Veronica Date: Sat, 18 Jan 2014 13:24:55 -0800 Subject: [PATCH 3/3] finish User Interface Setup --- .../2013-05-06-Part-1-Sudoku-Board-Setup.md | 38 +++++++------------ 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/website/_posts/GUI/2013-05-06-Part-1-Sudoku-Board-Setup.md b/website/_posts/GUI/2013-05-06-Part-1-Sudoku-Board-Setup.md index 4a655ada..8ca9d916 100644 --- a/website/_posts/GUI/2013-05-06-Part-1-Sudoku-Board-Setup.md +++ b/website/_posts/GUI/2013-05-06-Part-1-Sudoku-Board-Setup.md @@ -116,9 +116,7 @@ class SudokuUI(Frame): self.__initUI() ``` -We inherit from Tkinter's `Frame` class, and initiallize the board. -Why is parent an argument? What does it represent? -We set self.row and self.col equal to -1 because initially no cell is selected. We other methods we check if self.row and self.col are >= 0. If so, a cell on the grid is selected. +We inherit from Tkinter's `Frame` class, and initiallize the board. ```python @@ -143,18 +141,13 @@ We set self.row and self.col equal to -1 because initially no cell is selected. self.canvas.bind("", self.__key_pressed) ``` -Sudoku is the title of the GUI. It appears on the grey top part +We give our GUI the title Sudoku. -The packer allows you to specify the relative positioning of widgets within their container. To make our lives easier it takes a qualitative relationship specification and determines the exact placement coordinates for us. A widget will appear only after its geometry is specified. In this case, when the packer’s pack() method is applied to it. We use the packer options fill, expand and side. -What does fill=BOTH mean? -What does expand=1 mean? +The packer allows you to specify the relative positioning of widgets within their container. To make our lives easier it takes a qualitative relationship specification and determines the exact placement coordinates for us. A widget will appear only after its geometry is specified. In this case, when the packer’s `pack()` method is applied to it. We use the packer options fill, expand and side. You can learn more about the packer options [here](http://effbot.org/tkinterbook/pack.htm). -The Canvas widget is a rectangular area intended for drawing pictures or other complex layouts. On it you can place graphics, text, widgets, or frames. +We first create the `Canvas` widget, a rectangular area intended for drawing pictures or other complex layouts. On it we can place graphics, text, widgets, or frames. We then create a `Button` widget with the text Clear Answers. When it is clicked, `clear_answers()` is called. We then call `draw_grid()` and `draw_puzzle()`. -We create a button widget with the text Clear Answers. When it is clicked, self.__clear_answers is called. -We then call self.__draw_grid() and self.__draw_puzzle(). - -The last two lines use the concepts of bindings and events. Basically we specify an event (ex `""`) that always triggers a certain function.`` corresponds to clicking on the mouse. Functions called in this way are commonly known as callbacks. You can learn more about bindings and events by reading this [howto](http://docs.python.org/2/library/tkinter.html#bindings-and-events). +The last two lines use the concepts of bindings and events. We specify an event like `""` that always triggers a certain function.`` corresponds to clicking on the mouse. Functions called in this way are commonly known as callbacks. You can learn more about bindings and events by reading the [documentation](http://docs.python.org/2/library/tkinter.html#bindings-and-events). ```python def __draw_grid(self): @@ -176,8 +169,7 @@ def __draw_grid(self): y1 = MARGIN + i * SIDE self.canvas.create_line(x0, y0, x1, y1, fill=color) ``` - -We draw the Sudoku grid. +We draw the Sudoku grid. The method `create_line(x0, y0, x1, y1, ..., xn, yn, option, ...)` creates a line object that goes through a series of points on the Canvas. You can read more about the different Canvas objects [here](http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/canvas.html) ```python def __draw_puzzle(self): @@ -195,10 +187,9 @@ def __draw_puzzle(self): ) ``` -This adds the provided numbers in black to the grid. The numbers the user inputs will be in grey. -why would answer == 0? does each cell have answer == 0 initially if its value is not supplied to the user? -This deletes all the user submitted answers and and then iterates through the board, coloring the origional numbers in the board black and the user submitted answers slate gray. It then .... -This is called when the board is initialized, when a key is pressed and when the user clears their answers. +We first delete all existing numbers on the board. We then iterate through all the cells in the board and find the ones whose value in the answer list is nonzero. You will find out later that the board files have zeros in the cells that are empty when the game starts. That is why the cell with nonzero values `black` text while the remaining cells have `slate gray` text. A `tag` is a string you can associate with objects on the `Canvas`. `Tags` are useful because they allow you to perform operations on all the objects with the same tag, such as changing their color or deleting them. + +`draw_puzzle()` is called when the board is initialized, when a key is pressed and when the user clears their answers. ```python def __draw_cursor(self): @@ -234,7 +225,7 @@ We highlight the curently selected box in red. ) ``` -When the user solves the puzzle.... +When the user solves the puzzle an orange oval appears on the puzzle with the text "You win!". ```python def __cell_clicked(self, event): @@ -259,7 +250,7 @@ def __cell_clicked(self, event): self.__draw_cursor() ``` -This ascertains which cell the user clicks on and calls self.__draw_cursor() if the cell was not already selected. It unselects the row if it was already selected. +We identify which cell the user clicks on and select the cell if it was not already selected. Otherwise we unselect the cell. ```python def __key_pressed(self, event): @@ -274,7 +265,7 @@ This ascertains which cell the user clicks on and calls self.__draw_cursor() if self.__draw_victory() ``` -This sets the cell value equal to what the user types only when they type an integer 1-9. It also sets the user input to self.game.answer[self.row][self.col] and redraws the puzzle with the selected curser. Now the Sudoku grid shows the new user input. Finally it checks to see if the user has now solved the puzzle. +We set the cell value equal to what the user inputs only when they type an integer 1-9. We then check to see if the user has solved the puzzle. If so, we call `draw_victory()`. ```python def __clear_answers(self): @@ -283,7 +274,4 @@ This sets the cell value equal to what the user types only when they type an int self.__draw_puzzle() ``` -This deletes all user submitted answers.... -Uses set_answer_to_puzzle() from SudokuGame -Deletes everything on the canvas with tag="victory" ???? -And then draw the puzzle \ No newline at end of file +We call `set_answer_to_puzzle()` which creates a two dimensional array of the correct answers for all the cells in the board. We then deletes all objects on the canvas with the tag victory. In our case it's just the happy orange oval with the text "You win!". We then draw the puzzle. \ No newline at end of file