Stanford Code in Place - Python入門課程Week 6
Stanford Python程式課 - 第六週
從第六週開始Code in Place課程提供學員CS 106A後續課程資料、程式碼自學,學校不對外提供上課影片!
第六週學習目標
目前課程進度與後續課程規劃,Source: Stanford Code in Place & CS106A |
Lesson 15 More Lists
學習重點
- 學習 slices
- 熟悉 two-dimensional lists
課程講義
講義下載連結:More lists
點我到 More lists 課程程式範例
Lesson 16: Graphics
學習重點
- 如何繪製圖形
- Variables 被存在記憶體位址,記憶體位址像是網址 URL
- Binding 是修改記憶體位址
- Mutating 是修改對應的內容
- 使用 tkinter
課程講義
講義下載連結:Graphics
延伸閱讀 tkinter
使用 Tk
Tk 是Python上最常被用來繪圖的模組,使用前需要在程式開頭 import tkinter。
import tkinter
Canvas是繪圖空間,最左上角座標為 (0,0),X是水平方向、Y是垂直方向的座標。
點我到 Graphics 程式範例
Lesson 17: Animation
學習重點
- 寫出動畫程式
講義下載連結:Animation
點我到 Animation 程式範例
請到程式範例下載 graphics.py ,存放在程式範例相同目錄下,程式才能順利執行。
Assignment 3
Assignment 3 講義下載:Assignment3Handout.pdf
Assignment 3 Starter code下載:Assignment3.zip
延伸閱讀
未完待續 ...
More list 程式
gridswap.py
""" File: gridswap.py ----------------- This program shows an example of swapping items in a list of lists (grid). """ def swap(grid, row1, col1, row2, col2): """ This function swaps the elements at locations (row1, col1) and (row2, col2) in the grid passed in. """ temp = grid[row1][col1] grid[row1][col1] = grid[row2][col2] grid[row2][col2] = temp def main(): my_grid = [[10, 20, 30], [40, 50, 60]] print("Original grid:") print(my_grid) swap(my_grid, 0, 1, 1, 2) print("Grid after swapping two elements:") print(my_grid) if __name__ == '__main__': main()
listswap.py
""" File: listswap.py ----------------- This program shows examples of buggy and successful ways of swapping two elements in a list. """ def swap_elements_buggy(elem1, elem2): """ This function tries to swap the two elements passed it. This code is BUGGY! """ temp = elem1 elem1 = elem2 elem2 = temp def swap_elements_working(alist, index1, index2): """ This function successfully swaps the two elements at positions index1 and index2 in the list (alist) passed in. """ temp = alist[index1] alist[index1] = alist[index2] alist[index2] = temp def main(): my_list = [10, 20, 30] print("Original list:") print(my_list) swap_elements_buggy(my_list[0], my_list[1]) print("List after buggy attempt at swap:") print(my_list) print("List after successful attempt at swap:") swap_elements_working(my_list, 0, 1) print(my_list) if __name__ == '__main__': main()
spread.py
""" File: spread.py --------------- This program simulates the spread of an infection in a population. A world is represented as a list of lists (2-dimensional list), where each cell contains either a person who is negative for a condition ('-'), a person who is positive for a condition ('+'), no person but the cell is within reach of infection ('.'), or is empty and not within reach of infection(None). An initial infection point is specified and all people within RADIUS cells around that point (i.e., a square) are infected. Iteratively, all newly infected people can infect others who are within RADIUS cells of them. The simulation continues until no new infections are detected. """ import random SIZE = 10 # The world used will be SIZE x SIZE RADIUS = 1 # Radius around which an infection spreads DENSITY = 0.25 # Probability that a cell starts with a (negative) person in it def initialize_grid(n): """ Creates and returns a grid (list of lists), that has n rows and each row has n columns. All the elements in the grid are set to random values, representing None (no one) or '-' (person who is negative for the initial condition) based on DENSITY of people. """ grid = [] # Create empty grid for row in range(n): # Create rows one at a time row = [] for col in range(n): # Build up each row by appending to a list chance = random.random() # Get random float between 0.0 and 1.0 if chance < DENSITY: # Add a person (who is negative) row.append('-') else: row.append(None) # Add an "empty" cell grid.append(row) # Append the row (list) onto grid return grid def set_infection(grid, row, col): """ Given a grid and an infection point (col, row), this function sets any person who is negative within RADIUS squares of the infection point to be positive (denoted by '+') and any other cells to '.' to denoted they were exposed. The function returns True if any previously negative people were infected (i.e., become positive), False otherwise. """ start_row = row - RADIUS end_row = row + RADIUS start_col = col - RADIUS end_col = col + RADIUS infected = False for row in range(start_row, end_row + 1): for col in range(start_col, end_col + 1): if row >= 0 and row < SIZE and col >= 0 and col < SIZE: if grid[row][col] == '-': # negative people become positive grid[row][col] = '+' infected = True elif not grid[row][col]: # "not grid[row][col]" checks for None grid[row][col] = '.' # empty cells marked as infected return infected def spread_infection(grid): """ Loops through all cells in the grid and for every positive person, it makes them an infection point to potentially spread infection. The function returns True if any previously negative people were infected (i.e., become positive), False otherwise. """ infected = False rows = len(grid) # Could have used SIZE, but wanted cols = len(grid[0]) # to show a different way to do this. for row in range(rows): for col in range(cols): if grid[row][col] == '+': # Positive person is an infection point new_infection = set_infection(grid, row, col) infected = infected or new_infection return infected def get_value_in_range(prompt, min, max): """ Asks the user for a value using the prompt string. Checks if user entered value is between min and max (inclusive) and prompts the user if the value is out of range. Returns a valid value entered by the user. """ while True: value = int(input(prompt)) if value >= min and value <= max: return value print("Invalid entry. Please enter a value between", min, "and", max) def print_borderline(length): """ Prints a border line of (length + 1) #'s, with leading spaces """ print(' ', end='') for i in range(length + 1): print('# ', end='') print('#') def print_column_indexes(length): """ Prints the column index numbers (length of them), appropriately spaced """ print(' ', end='') for i in range(length): print_formatted(i) print('') # Print a "return" to end the line def print_formatted(num): """ Prints a one or two digit number in two spaces """ if num < 10: print(str(num) + ' ', end='') else: print(str(num), end='') def print_grid(grid): """ Prints out grid, including row/column index numbers and borders """ print_column_indexes(SIZE) print_borderline(SIZE) rows = len(grid) # Could have used SIZE, but wanted cols = len(grid[0]) # to show a different way to do this. for row in range(rows): print_formatted(row) print('# ', end='') # Print left-hand border for col in range(cols): symbol = grid[row][col] if not symbol: # Print a space if symbol is None print(' ', end='') else: print(symbol, end='') print(' ', end='') # Prints space for paddingS print('#') # Print right-hand border (and return) print_borderline(SIZE) def main(): """ Create the initial (random) grid, ask the user for an initial infection point and then keep spreading the infection until no new people in the simulation are infected. Print out the initial and final grids """ random.seed(2) # Create the initial grid grid = initialize_grid(SIZE) print_grid(grid) # Get initial infection point row = get_value_in_range("Initial infection row: ", 0, SIZE - 1) col = get_value_in_range("Initial infection col: ", 0, SIZE - 1) new_infections = set_infection(grid, row, col) # Loop the repeatedly spreads the infection from positive people while new_infections: new_infections = spread_infection(grid) print_grid(grid) if __name__ == '__main__': main()
Graphics 程式
circle.py
import sys import tkinter import random WIDTH = 800 HEIGHT = 600 RADIUS_MIN = 30 RADIUS_MAX = 100 N_CIRCLES_MAX = 10 def make_canvas(): """ (provided) Creates and returns a drawing canvas of the given int size with a blue border, ready for drawing. """ top = tkinter.Tk() top.minsize(width=WIDTH + 10, height=HEIGHT + 10) canvas = tkinter.Canvas(top, width=WIDTH, height=HEIGHT) canvas.pack() canvas.xview_scroll(6, 'units') # hack so (0, 0) works correctly canvas.yview_scroll(6, 'units') return canvas def make_all_circles(): canvas = make_canvas() # TODO your code here def main(): """ random-circles.py Write a program that draws a random number of circles of random sizes at random positions on the canvas. Be careful to make sure that none of the drawn circles are cut off by the edge of your canvas. You are provided with the constants WIDTH/HEIGHT (the canvas width/height), RADIUS_MAX/RADIUS_MIN (the maximum/minimum radius that each random circle may have), and N_CIRCLES_MAX (the maximum number of circles that may be generated in one run of our program. Note that each run should generate between 1 and N_CIRCLES_MAX circles inclusive on both ends). """ make_all_circles() tkinter.mainloop() if __name__ == "__main__": main()
circle.py
import tkinter import time CANVAS_HEIGHT = 100 CANVAS_WIDTH = 500 DX = 4 DY = 0 DELAY = 0.01 def main(): canvas = make_canvas(CANVAS_WIDTH, CANVAS_HEIGHT, "News Ticker") pass canvas.mainloop() """ You don't need to modify code below here """ def get_right_x(canvas, object): bbox = canvas.bbox(object) return bbox[2] def make_canvas(width, height, title): """ Creates and returns a drawing canvas of the given int size with a blue border, ready for drawing. """ top = tkinter.Tk() top.minsize(width=width, height=height) top.title(title) canvas = tkinter.Canvas(top, width=width + 1, height=height + 1) canvas.pack() return canvas if __name__ == "__main__": main()
Animation 程式
課程提供的 graphics.py
附註:請將graphics.py 跟 Animation python程式放在同一磁碟目錄內,範例程式才能順利執行!
import random import tkinter import tkinter.font """ File: graphics.py Authors: Chris Piech, Lisa Yan and Nick Troccoli Version Date: August 11, 2020 This file creates a standard Tkinter Canvas (in other words it does the work of making the window pop up for you). You can use any function that you would normally use on a tk canvas (https://effbot.org/tkinterbook/canvas.htm) In addition we added a few convenience functions: - canvas.get_mouse_x() - canvas.get_new_mouse_clicks() - canvas.get_new_key_presses() - canvas.wait_for_click() - and a few more... You can reuse this file in future projects freely and with joy. """ class Canvas(tkinter.Canvas): """ Canvas is a simplified interface on top of the tkinter Canvas to allow for easier manipulation of graphical objects. Canvas has a variety of functionality to create, modify and delete graphical objects, and also get information about the canvas contents. Canvas is a subclass of `tkinter.Canvas`, so all tkinter functionality is also available if needed. """ DEFAULT_WIDTH = 754 """The default width of the canvas is 754.""" DEFAULT_HEIGHT = 492 """The default height of the canvas is 492.""" DEFAULT_TITLE = "Canvas" """The default text shown in the canvas window titlebar is 'Canvas'.""" def __init__(self, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT, title=DEFAULT_TITLE): """ When creating a canvas, you can optionally specify a width and height. If no width and height are specified, the canvas is initialized with its default size. Args: width: the width of the Canvas to create (or if not specified, uses `Canvas.DEFAULT_WIDTH`) height: the height of the Canvas to create (or if not specified, uses `Canvas.DEFAULT_HEIGHT`) """ # Create the main program window self.main_window = tkinter.Tk() self.main_window.geometry("{}x{}".format(width, height)) self.main_window.title(title) # call the tkinter Canvas constructor super().__init__(self.main_window, width=width, height=height, bd=0, highlightthickness=0) # Optional callbacks the client can specify to be called on each event self.on_mouse_pressed = None self.on_mouse_released = None self.on_key_pressed = None # Tracks whether the mouse is currently on top of the canvas self.mouse_on_canvas = False # List of presses not handled by a callback self.mouse_presses = [] # List of key presses not handled by a callback self.key_presses = [] # These are state variables so wait_for_click knows when to stop waiting and to # not call handlers when we are waiting for click self.wait_for_click_click_happened = False self.currently_waiting_for_click = False # bind events self.focus_set() self.bind("", lambda event: self.__mouse_pressed(event)) self.bind(" ", lambda event: self.__mouse_released(event)) self.bind(" ", lambda event: self.__key_pressed(event)) self.bind(" ", lambda event: self.__mouse_entered()) self.bind(" ", lambda event: self.__mouse_exited()) self._image_gb_protection = {} self.pack() self.update() def set_canvas_background_color(self, color): """ Sets the background color of the canvas to the specified color string. Args: color: the color (string) to make the background of the canvas. """ self.config(background=color) def get_canvas_width(self): """ Get the width of the canvas. Returns: the current width of the canvas. """ return self.winfo_width() def get_canvas_height(self): """ Get the height of the canvas. Returns: the current height of the canvas. """ return self.winfo_height() def set_canvas_title(self, title): """ Sets the title text displayed in the Canvas's window title bar to be the specified text. Args: title: the text to display in the title bar """ self.main_window.title(title) def set_canvas_size(self, width, height): """ Sets the size of the canvas and its containing window to the specified width and height. Args: width: the width to set for the canvas and containing window height: the height to set for the canvas and containing window """ self.main_window.geometry("{}x{}".format(width, height)) self.config(width=width, height=height) """ EVENT HANDLING """ def set_on_mouse_pressed(self, callback): """ Set the specified function to be called whenever the mouse is pressed. If this function is called multiple times, only the last specified function is called when the mouse is pressed. Args: callback: a function to call whenever the mouse is pressed. Must take in two parameters, which are the x and y coordinates (in that order) of the mouse press that just occurred. E.g. func(x, y). If this parameter is None, no function will be called when the mouse is pressed. """ self.on_mouse_pressed = callback def set_on_mouse_released(self, callback): """ Set the specified function to be called whenever the mouse is released. If this function is called multiple times, only the last specified function is called when the mouse is released. Args: callback: a function to call whenever the mouse is released. Must take in two parameters, which are the x and y coordinates (in that order) of the mouse release that just occurred. E.g. func(x, y). If this parameter is None, no function will be called when the mouse is released. """ self.on_mouse_released = callback def set_on_key_pressed(self, callback): """ Set the specified function to be called whenever a keyboard key is pressed. If this function is called multiple times, only the last specified function is called when a key is pressed. Args: callback: a function to call whenever a key is pressed. Must take in one parameter, which is the text name of the key that was just pressed (e.g. 'a' for the a key, 'b' for the b key, etc). E.g. func(key_char). If this parameter is None, no function will be called when a key is pressed. """ self.on_key_pressed = callback def get_new_mouse_clicks(self): """ Returns a list of all mouse clicks that have occurred since the last call to this method or any registered mouse handler. Returns: a list of all mouse clicks that have occurred since the last call to this method or any registered mouse handler. Each mouse click contains x and y properties for the click location, e.g. clicks = canvas.get_new_mouse_clicks(); print(clicks[0].x). """ presses = self.mouse_presses self.mouse_presses = [] return presses def get_new_key_presses(self): """ Returns a list of all key presses that have occurred since the last call to this method or any registered key handler. Returns: a list of all key presses that have occurred since the last call to this method or any registered key handler. Each key press contains a keysym property for the key pressed, e.g. presses = canvas.get_new_key_presses(); print(presses[0].keysym). """ presses = self.key_presses self.key_presses = [] return presses def __mouse_pressed(self, event): """ Called every time the mouse is pressed. If we are currently waiting for a mouse click via wait_for_click, do nothing. Otherwise, if we have a registered mouse press handler, call that. Otherwise, append the press to the list of mouse presses to be handled later. Args: event: an object representing the mouse press that just occurred. Assumed to have x and y properties containing the x and y coordinates for this mouse press. """ if not self.currently_waiting_for_click and self.on_mouse_pressed: self.on_mouse_pressed(event.x, event.y) elif not self.currently_waiting_for_click: self.mouse_presses.append(event) def __mouse_released(self, event): """ Called every time the mouse is released. If we are currently waiting for a mouse click via wait_for_click, update our state to reflect that a click happened. Otherwise, if we have a registered mouse release handler, call that. Args: event: an object representing the mouse release that just occurred. Assumed to have x and y properties containing the x and y coordinates for this mouse release. """ # Do this all in one go to avoid setting click happened to True, # then having wait for click set currently waiting to false, then we go if self.currently_waiting_for_click: self.wait_for_click_click_happened = True return self.wait_for_click_click_happened = True if self.on_mouse_released: self.on_mouse_released(event.x, event.y) def __key_pressed(self, event): """ Called every time a keyboard key is pressed. If we have a registered key press handler, call that. Otherwise, append the key press to the list of key presses to be handled later. Args: event: an object representing the key press that just occurred. Assumed to have a keysym property containing the name of this key press. """ if self.on_key_pressed: self.on_key_pressed(event.keysym) else: self.key_presses.append(event) def __mouse_entered(self): """ Called every time the mouse enters the canvas. Updates the internal state to record that the mouse is currently on the canvas. """ self.mouse_on_canvas = True def __mouse_exited(self): """ Called every time the mouse exits the canvas. Updates the internal state to record that the mouse is currently not on the canvas. """ self.mouse_on_canvas = False def mouse_is_on_canvas(self): """ Returns whether or not the mouse is currently on the canvas. Returns: True if the mouse is currently on the canvas, or False otherwise. """ return self.mouse_on_canvas def wait_for_click(self): """ Waits until a mouse click occurs, and then returns. """ self.currently_waiting_for_click = True self.wait_for_click_click_happened = False while not self.wait_for_click_click_happened: self.update() self.currently_waiting_for_click = False self.wait_for_click_click_happened = False def get_mouse_x(self): """ Returns the mouse's current X location on the canvas. Returns: the mouses's current X location on the canvas. """ """ Note: winfo_pointerx is absolute mouse position (to screen, not window), winfo_rootx is absolute window position (to screen) Since move takes into account relative position to window, we adjust this mouse_x to be relative position to window. """ return self.winfo_pointerx() - self.winfo_rootx() def get_mouse_y(self): """ Returns the mouse's current Y location on the canvas. Returns: the mouse's current Y location on the canvas. """ """ Note: winfo_pointery is absolute mouse position (to screen, not window), winfo_rooty is absolute window position (to screen) Since move takes into account relative position to window, we adjust this mouse_y to be relative position to window. """ return self.winfo_pointery() - self.winfo_rooty() """ GRAPHICAL OBJECT MANIPULATION """ def get_left_x(self, obj): """ Returns the leftmost x coordinate of the specified graphical object. Args: obj: the object for which to calculate the leftmost x coordinate Returns: the leftmost x coordinate of the specified graphical object. """ if self.type(obj) != "text": return self.coords(obj)[0] else: return self.coords(obj)[0] - self.get_width(obj) / 2 def get_top_y(self, obj): """ Returns the topmost y coordinate of the specified graphical object. Args: obj: the object for which to calculate the topmost y coordinate Returns: the topmost y coordinate of the specified graphical object. """ if self.type(obj) != "text": return self.coords(obj)[1] else: return self.coords(obj)[1] - self.get_height(obj) / 2 def move_to(self, obj, new_x, new_y): """ Same as `Canvas.moveto`. """ # Note: Implements manually due to inconsistencies on some machines of bbox vs. coord. old_x = self.get_left_x(obj) old_y = self.get_top_y(obj) self.move(obj, new_x - old_x, new_y - old_y) def moveto(self, obj, x='', y=''): """ Moves the specified graphical object to the specified location, which is its bounding box's new upper-left corner. Args: obj: the object to move x: the new x coordinate of the upper-left corner for the object y: the new y coordinate of the upper-left corner for the object """ self.move_to(obj, float(x), float(y)) def set_hidden(self, obj, hidden): """ Sets the given graphical object to be either hidden or visible on the canvas. Args: obj: the graphical object to make hidden or visible on the canvas. hidden: True if the object should be hidden, False if the object should be visible. """ self.itemconfig(obj, state='hidden' if hidden else 'normal') def set_fill_color(self, obj, fill_color): """ Sets the fill color of the specified graphical object. Cannot be used to change the fill color of non-fillable objects such as images - throws a tkinter.TclError. Args: obj: the object for which to set the fill color fill_color: the color to set the fill color to be, as a string. If this is the empty string, the object will be set to be not filled. """ try: self.itemconfig(obj, fill=fill_color) except tkinter.TclError: raise tkinter.TclError("You can't set the fill color on this object") def set_outline_color(self, obj, outline_color): """ Sets the outline color of the specified graphical object. Cannot be used to change the outline color of non-outlined objects such as images or text - throws a tkinter.TclError. Args: obj: the object for which to set the outline color outline_color: the color to set the outline color to be, as a string. If this is the empty string, the object will be set to not have an outline. """ try: self.itemconfig(obj, outline=outline_color) except tkinter.TclError as e: raise tkinter.TclError("You can't set the outline color on this object") def set_outline_width(self, obj, width): """ Sets the thickness of the outline of the specified graphical object. Cannot be used on objects that are not outline-able, such as images or text. Args: obj: the object for which to set the outline width width: the width to set the outline to be. """ self.itemconfig(obj, width=width) def set_text(self, obj, text): """ Sets the text displayed by the given text object. Cannot be used on any non-text graphical object. Args: obj: the text object for which to set the displayed text text: the new text for this graphical object to display """ self.itemconfig(obj, text=text) def get_text(self, obj): """ Returns the text displayed by the given text object. Cannot be used on any non-text graphical object. Args: obj: the text object for which to get the displayed text Returns: the text currently displayed by this graphical object. """ return self.itemcget(obj, 'text') def set_font(self, obj, font, size): """ Sets the font and size for the text displayed by the given text object. Cannot be used on any non-text graphical object. Args: obj: the text object for which to set the font and size font: the name of the font, as a string size: the size of the font """ self.itemconfig(obj, font=(font, size)) def raise_to_front(self, obj): """ Sends the given object to the very front of all the other objects on the canvas. Args: obj: the object to bring to the front of the objects on the canvas """ self.raise_in_front_of(obj, 'all') def raise_in_front_of(self, obj, above): """ Sets the first object to be directly in front of the second object in Z-ordering on the canvas. In other words, the first object will now appear in front of the second object and all objects behind the second object, but behind all objects that the second object is also behind. Args: obj: the object to put in front of the second object above: the object to put the first object directly in front of """ self.tag_raise(obj, above) def lower_to_back(self, obj): """ Sends the given object to be behind all the other objects on the canvas Args: obj: the object to put behind all other objects on the canvas """ self.lower_behind(obj, 'all') def lower_behind(self, obj, behind): """ Sets the first object to be directly behind the second object in Z-ordering on the canvas. In other words, the first object will now appear directly behind the second object and all objects in front of the second object, but in front of all objects that the second object is also in front of. Args: obj: the object to put in front of the second object behind: the object to put the first object directly behind """ self.tag_lower(obj, behind) def create_image_with_size(self, x, y, width, height, file_path, **kwargs): """ Creates an image with the specified filename at the specified position on the canvas, and resized to the specified width and height. Args: x: the x coordinate of the top-left corner of the image on the canvas y: the y coordinate of the top-left corner of the image on the canvas width: the width to set for the image height: the height to set for the image file_path: the path to the image file to load and display on the canvas kwargs: other tkinter keyword args Returns: the graphical image object that is displaying the specified image at the specified location with the specified size. """ return self.__create_image_with_optional_size(x, y, file_path, width=width, height=height, **kwargs) def __create_image_with_optional_size(self, x, y, file_path, width=None, height=None, **kwargs): """ Creates an image with the specified filename at the specified position on the canvas. Optionally specify the width and height to resize the image. Args: x: the x coordinate of the top-left corner of the image on the canvas y: the y coordinate of the top-left corner of the image on the canvas file_path: the path to the image file to load and display on the canvas width: optional width to include for the image. If none, uses the width of the image file. height: optional height to include for the image If none, uses the height of the image file. kwargs: other tkinter keyword args Returns: the graphical image object that is displaying the specified image at the specified location. """ from PIL import ImageTk from PIL import Image image = Image.open(file_path) # Resize the image if another width and height is specified if width is not None and height is not None: image = image.resize((width, height)) image = ImageTk.PhotoImage(image) img_obj = super().create_image(x, y, anchor="nw", image=image, **kwargs) # note: if you don't do this, the image gets garbage collected!!! # this introduces a memory leak which can be fixed by overloading delete self._image_gb_protection[img_obj] = image return img_obj
move_rect.py
""" File: move_rect.py ---------------- YOUR DESCRIPTION HERE """ import tkinter from graphics import Canvas CANVAS_WIDTH = 600 # Width of drawing canvas in pixels CANVAS_HEIGHT = 600 # Height of drawing canvas in pixels SQUARE_SIZE = 70 def main(): canvas = Canvas(CANVAS_WIDTH, CANVAS_HEIGHT, 'Move Square') # how beautiful it is, the humble task of creating a rectangle... start_y = CANVAS_HEIGHT / 2 - SQUARE_SIZE / 2 end_y = start_y + SQUARE_SIZE rect = canvas.create_rectangle(0, start_y, SQUARE_SIZE, end_y, fill='black') # TODO: make the magic happen canvas.mainloop() if __name__ == '__main__': main()
move_rect_soln.py
""" File: move_rect_soln.py ---------------- YOUR DESCRIPTION HERE """ import tkinter import time from graphics import Canvas CANVAS_WIDTH = 600 # Width of drawing canvas in pixels CANVAS_HEIGHT = 600 # Height of drawing canvas in pixels SQUARE_SIZE = 70 def main(): canvas = Canvas(CANVAS_WIDTH, CANVAS_HEIGHT, 'Move Square') # make the rectangle... start_y = CANVAS_HEIGHT / 2 - SQUARE_SIZE / 2 end_y = start_y + SQUARE_SIZE rect = canvas.create_rectangle(0, start_y, SQUARE_SIZE, end_y, fill='black') # animation loop while not is_past_middle(canvas, rect): # update the world canvas.move(rect, 1, 0) canvas.update() # pause time.sleep(1/50.) #parameter is seconds to pause. canvas.mainloop() def is_past_middle(canvas, rect): max_x = CANVAS_WIDTH / 2 - SQUARE_SIZE / 2 curr_x = canvas.get_left_x(rect) return curr_x > max_x if __name__ == '__main__': main()
ball_soln.py
""" File: ball_soln.py ---------------- YOUR DESCRIPTION HERE """ import tkinter import time from graphics import Canvas CANVAS_WIDTH = 600 # Width of drawing canvas in pixels CANVAS_HEIGHT = 600 # Height of drawing canvas in pixels CHANGE_X_START = 10 CHANGE_Y_START = 7 BALL_SIZE = 70 def main(): canvas = Canvas(CANVAS_WIDTH, CANVAS_HEIGHT, 'Bouncing Ball') ball = canvas.create_oval(0, 0, BALL_SIZE, BALL_SIZE, fill='blue', outline='blue') dx = CHANGE_X_START dy = CHANGE_Y_START while True: # update world canvas.move(ball, dx, dy) if hit_left_wall(canvas, ball) or hit_right_wall(canvas, ball): dx *= -1 if hit_top_wall(canvas, ball) or hit_bottom_wall(canvas, ball): dy *= -1 # redraw canvas canvas.update() # pause time.sleep(1/50.) def hit_left_wall(canvas, object): return canvas.get_left_x(object) <= 0 def hit_top_wall(canvas, object): return canvas.get_top_y(object) <= 0 def hit_right_wall(canvas, object): return canvas.get_left_x(object) + BALL_SIZE >= CANVAS_WIDTH def hit_bottom_wall(canvas, object): return canvas.get_top_y(object) + BALL_SIZE >= CANVAS_HEIGHT if __name__ == '__main__': main()
ball_colors.py
""" File: ball_colors.py ---------------- YOUR DESCRIPTION HERE """ import tkinter import time import random from graphics import Canvas CANVAS_WIDTH = 600 # Width of drawing canvas in pixels CANVAS_HEIGHT = 600 # Height of drawing canvas in pixels BALL_SIZE = 70 def main(): canvas = Canvas(CANVAS_WIDTH, CANVAS_HEIGHT, 'Bouncing Ball') ball = canvas.create_oval(0, 0, BALL_SIZE, BALL_SIZE, fill='blue', outline='blue') dx = 10 dy = 7 while True: # update world canvas.move(ball, dx, dy) if hit_left_wall(canvas, ball) or hit_right_wall(canvas, ball): dx *= -1 change_color(canvas, ball) if hit_top_wall(canvas, ball) or hit_bottom_wall(canvas, ball): dy *= -1 change_color(canvas, ball) # redraw canvas canvas.update() # pause time.sleep(1/50.) def change_color(canvas, shape): ''' When you call this method, the provided shape will change color to a randomly chosen color. :param canvas: the canvas where the shape exists :param shape: the shape you want to have its color changed ''' # 1. get a random color color = random.choice(['blue', 'salmon', 'red', 'green', 'orange', 'plum']) # 2. use the tkinter method to change the shape's color # canvas.itemconfig(object, fill=color, outline=color) # change color canvas.set_fill_color(shape, color) canvas.set_outline_color(shape, color) def hit_left_wall(canvas, object): return canvas.get_left_x(object) <= 0 def hit_top_wall(canvas, object): return canvas.get_top_y(object) <= 0 def hit_right_wall(canvas, object): return canvas.get_left_x(object) + BALL_SIZE >= CANVAS_WIDTH def hit_bottom_wall(canvas, object): return canvas.get_top_y(object) + BALL_SIZE >= CANVAS_HEIGHT if __name__ == '__main__': main()
pong.py
import tkinter import time import math from graphics import Canvas CANVAS_WIDTH = 600 # Width of drawing canvas in pixels CANVAS_HEIGHT = 600 # Height of drawing canvas in pixels PADDLE_Y = CANVAS_HEIGHT - 60 PADDLE_WIDTH = 100 PADDLE_HEIGHT = 20 BALL_SIZE = 70 def main(): canvas = Canvas(CANVAS_WIDTH, CANVAS_HEIGHT, 'Pong 1') ball = canvas.create_oval(0, 0, BALL_SIZE, BALL_SIZE, fill='red', outline='red') # TODO: 1. we now make a paddle paddle = canvas.create_rectangle(0, PADDLE_Y, PADDLE_WIDTH, PADDLE_Y + PADDLE_HEIGHT, fill="blue") dx = 10 dy = 7 while True: # TODO: 2. get the mouse location and react to it mouse_x = canvas.get_mouse_x() canvas.moveto(paddle, mouse_x, PADDLE_Y) canvas.move(ball, dx, dy) if hit_left_wall(canvas, ball) or hit_right_wall(canvas, ball): dx *= -1 if hit_top_wall(canvas, ball): dy *= -1 # TODO: 3. check if the ball hits the paddle if hit_paddle(canvas, ball, paddle): dy = -abs(dy) # redraw canvas canvas.update() # pause time.sleep(1/50.) def hit_paddle(canvas, ball, paddle): # TODO: paddle_coords is of type list. Come to lecture Monday! paddle_coords = canvas.coords(paddle) x1 = paddle_coords[0] y1 = paddle_coords[1] x2 = paddle_coords[2] y2 = paddle_coords[3] results = canvas.find_overlapping(x1, y1, x2, y2) return len(results) > 1 def hit_left_wall(canvas, object): return canvas.get_left_x(object) <= 0 def hit_top_wall(canvas, object): return canvas.get_top_y(object) <= 0 def hit_right_wall(canvas, object): return canvas.get_left_x(object) + BALL_SIZE >= CANVAS_WIDTH def hit_bottom_wall(canvas, object): return canvas.get_top_y(object) + BALL_SIZE >= CANVAS_HEIGHT if __name__ == '__main__': main()
史丹佛
動畫
程式語言
資訊工程
繪圖
animation
Code in Place
Computer Science
CS 106A
graphics
list
Python
Stanford
Tech
1 comments
Hello, 你真是太佛心來的,從你提供的這些整理,真的對Python的學習又更進一步了。前面week1 到week5的影片講解真的很清楚,實在太感謝了。
回覆刪除可惜Week6後學校不開放,只能告自己摸索。不過多虧你熱心提供,還真的要謝謝你。
不好意思,關於動畫部份,能請教你一個問題嗎?
我在試著學動畫程式的部份時,將graphics.py放在同一個目錄下,但執行時便出現以下錯誤訊息:
Traceback (most recent call last):
File "D:\AI_lab\Standford\week6\MoveRect.py", line 26, in
main()
File "D:\AI_lab\Standford\week6\MoveRect.py", line 15, in main
canvas = Canvas(CANVAS_WIDTH, CANVAS_HEIGHT, 'Move Square')
File "D:\AI_lab\Standford\week6\graphics.py", line 82, in __init__
self.bind("", lambda event: self.__mouse_pressed(event))
File "C:\Users\shang\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1384, in bind
return self._bind(('bind', self._w), sequence, func, add)
File "C:\Users\shang\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1338, in _bind
self.tk.call(what + (sequence, cmd))
_tkinter.TclError: no events specified in binding
我猜測是graphics.py在呼叫__init__.py 時找不到對應的函數。我試過用python 3.9 跟3.6 都是同樣狀況,也試著用google corelab (上面是python 3.7)執行,也是有其它錯誤訊息,可能在學校的平台上執行沒有這種情況,不知道是不是也有其它同學在自己的電腦上執行動畫程式時也有類似的情況呢?