diff --git a/Ch 03 Python Program Structure/__pycache__/snaps.cpython-36.pyc b/Ch 03 Python Program Structure/__pycache__/snaps.cpython-36.pyc new file mode 100644 index 0000000..1ed9828 Binary files /dev/null and b/Ch 03 Python Program Structure/__pycache__/snaps.cpython-36.pyc differ diff --git a/Ch 03 Python Program Structure/snaps.py b/Ch 03 Python Program Structure/snaps.py index b5f7d61..2ddbaaa 100644 --- a/Ch 03 Python Program Structure/snaps.py +++ b/Ch 03 Python Program Structure/snaps.py @@ -3,6 +3,8 @@ # Rob Miles July 2017 # Version 1.0 +import time +import random import pygame surface = None @@ -42,12 +44,12 @@ def setup(width=800, height=600, title=''): pygame.display.set_caption(title) - def handle_events(): ''' Consume events that are generated by the pygame window - These are not presntly used for anything + Captures key pressed events and sets the key_pressed value ''' + global key_pressed setup() for event in pygame.event.get(): pass @@ -95,17 +97,45 @@ def clear_display(): if image is not None: surface.blit(image, (0, 0)) + pygame.display.flip() + +def split_lines_on_spaces(text): + ''' + returns a list of words which have been + extracted from the text. + Spaces on the ends of words are + preserved + ''' + result = [] + got_space = False + word = '' + for ch in text: + if ch == ' ': + got_space = True + word = word + ch + else: + if got_space: + # first character of next word + result.append(word) + word=ch + got_space=False + else: + word = word + ch + + result.append(word) + + return result + def get_display_lines(text, font, width): ''' Returns a list of strings which have been split to fit the given window width using the supplied font ''' - space_width = font.size(' ')[0] result = [] - text_lines = text.splitlines() + text_lines = text.splitlines() for text_line in text_lines: - words = text_line.split() + words = split_lines_on_spaces(text_line) x = 0 line = '' for word in words: @@ -113,36 +143,19 @@ def get_display_lines(text, font, width): if x + word_width > width: # Remove the trailing space from the line # before adding to the list of lines to return - line = line.strip() result.append(line) - line = word + ' ' - x = word_width + space_width + line = word + x = word_width else: - line = line + word + ' ' - x = x + word_width + space_width - - if line != '': - # Got a partial line to add to the end - # Remove the trailing space from the line - # before adding to the list of lines to return - line = line.strip() - result.append(line) - return result + line = line + word + x = x + word_width + result.append(line) + return result -def display_message(text, size=200, margin=20, horiz='center', vert='center', - color=(255, 0, 0)): - ''' - Displays the text as a message - Sice can be used to select the size of the - text - ''' - global window_size - global surface - - handle_events() - clear_display() +def render_message(text, size=200, margin=20, horiz='center', vert='center', + color=(255, 0, 0), cursor=''): # Get the text version of the input text = str(text) @@ -167,20 +180,239 @@ def display_message(text, size=200, margin=20, horiz='center', vert='center', if vert == 'center': y = (window_size[1] - height) / 2.0 - elif vert == 'top': - y = margin elif vert == 'bottom': y=(window_size[1]-margin) - height + else: + # default vertical cursor position is top + y = margin + for rendered_line in rendered_lines: width = rendered_line.get_width() height = rendered_line.get_height() if horiz == 'center': x = (available_width - width) / 2.0 + margin - elif horiz == 'left': - x = margin elif horiz == 'right': - x = self.window_size[0] - width - margin + x = window_size[0] - width - margin + else: + # default position is left margin + x = margin surface.blit(rendered_line, (x, y)) y += height + + if cursor: + cursor_size = font.size(cursor) + cursor_width = cursor_size[0] + cursor_height = cursor_size[1] + if len(rendered_lines): + # put the cursor on the end of an existing line + y -= height + x += width + else: + # put the cursor in the start position for this + # orientation + # default x position is the margin + x = margin + + if horiz == 'center': + x = (available_width - cursor_width) / 2.0 + margin + elif horiz == 'right': + x = window_size[0] - cursor_width - margin + else: + # default position is left margin + x = margin + + if vert == 'center': + y = (window_size[1] - cursor_height) / 2.0 + elif vert == 'bottom': + y=(window_size[1]-margin) - cursor_height + else: + # default vertical cursor position is top + y = margin + + cursor_image = font.render(cursor, 1, color) + surface.blit(cursor_image, (x, y)) + + pygame.display.flip() + + +def display_message(text, size=200, margin=20, horiz='center', vert='center', + color=(255, 0, 0)): + ''' + Displays the text as a message + Size can be used to select the size of the + text + ''' + global window_size + global surface + + handle_events() + + clear_display() + + render_message(text, size=size, margin=margin, horiz=horiz, vert=vert, color=color) + +def get_dot(): + ''' + Waits for a mouse movement and then returns it + as a tuple of x and y coordinates + ''' + setup() + while True: + event = pygame.event.wait() + if event.type == 4: + # Event 4 is mouse motion + pos = event.dict['pos'] + return pos + +BLACK = ( 0, 0, 0) +WHITE = (255, 255, 255) +BLUE = ( 0, 0, 255) +GREEN = ( 0, 255, 0) +RED = (255, 0, 0) +YELLOW = (255, 255, 0) +MAGENTA = (255, 0, 255) +CYAN = (0, 255, 255) + +dot_color = WHITE + +def set_color(r,g,b): + dot_color = (r,g,b) + +def set_random_color(): + global dot_color + dot_color = (random.randint(0,255), + random.randint(0,255), + random.randint(0,255)) + +def get_mouse_pressed(): + return pygame.mouse.get_pressed()[0] + + +def draw_dot(pos, radius): + setup() + pygame.draw.circle(surface, dot_color, pos, radius) pygame.display.flip() + + +def get_key(): + ''' + Waits for a keypress and then returns it as a string + Only characters are returned, not control keys + ''' + setup() + while True: + event = pygame.event.wait() + if event.type == 2: + # Event 2 is keydown + key_code = event.dict['unicode'] + if key_code: + return key_code + + +def get_string(prompt, size=50, margin=20, + color=(255, 0, 0), horiz='left', vert='center', + max_line_length=20): + ''' + Reads a string from the user + ''' + + setup() + + result = '' + cursor_char = '*' + cursor = None + + def redraw(): + clear_display() + + render_message(prompt+result, margin=margin, size=size, + horiz=horiz, vert=vert, color=color, cursor=cursor) + + def cursor_flip(): + nonlocal cursor + + # create a timer for the cursor + + cursor_event = pygame.USEREVENT+1 + + pygame.time.set_timer(cursor_event,500) + + while True: + event = pygame.event.wait() + + if event.type == cursor_event: + if cursor: + cursor = None + else: + cursor = cursor_char + redraw() + elif event.type == 2: + # Event 2 is keydown + key_code = event.dict['unicode'] + if key_code is None: + continue + if key_code == '\r': + break + elif key_code == '\x08': + if len(result) > 0: + result=result[:-1] + redraw() + else: + if len(result) < max_line_length: + result += key_code + redraw() + + # disable the timer for the cursor + pygame.time.set_timer(cursor_event,0) + return result + +import urllib.request +import xml.etree.ElementTree + +def get_weather_temp(latitude,longitude): + ''' + Uses forecast.weather.gov to get the weather + for the specified latitude and longitude + ''' + url="http://forecast.weather.gov/MapClick.php?lat={0}&lon={1}&unit=0&lg=english&FcstType=dwml".format(latitude,longitude) + req=urllib.request.urlopen(url) + page=req.read() + doc=xml.etree.ElementTree.fromstring(page) + # I'm not proud of this, but by gum it works... + for child in doc: + if child.tag == 'data': + if child.attrib['type'] == 'current observations': + for item in child: + if item.tag == 'parameters': + for i in item: + if i.tag == 'temperature': + if i.attrib['type'] == 'apparent': + for t in i: + if t.tag =='value': + return int(t.text) + + +def get_weather_desciption(latitude,longitude): + ''' + Uses forecast.weather.gov to get the weather + for the specified latitude and longitude + ''' + url="http://forecast.weather.gov/MapClick.php?lat={0}&lon={1}&unit=0&lg=english&FcstType=dwml".format(latitude,longitude) + req=urllib.request.urlopen(url) + page=req.read() + doc=xml.etree.ElementTree.fromstring(page) + # I'm not proud of this, but by gum it works... + for child in doc: + if child.tag == 'data': + if child.attrib['type'] == 'current observations': + for item in child: + if item.tag == 'parameters': + for i in item: + if i.tag == 'weather': + for t in i: + if t.tag == 'weather-conditions': + if t.get('weather-summary') is not None: + return t.get('weather-summary') + + diff --git a/Ch 04 Working with Data in Python/__pycache__/snaps.cpython-36.pyc b/Ch 04 Working with Data in Python/__pycache__/snaps.cpython-36.pyc new file mode 100644 index 0000000..293ec32 Binary files /dev/null and b/Ch 04 Working with Data in Python/__pycache__/snaps.cpython-36.pyc differ diff --git a/Ch 04 Working with Data in Python/snaps.py b/Ch 04 Working with Data in Python/snaps.py index 33297e5..2ddbaaa 100644 --- a/Ch 04 Working with Data in Python/snaps.py +++ b/Ch 04 Working with Data in Python/snaps.py @@ -3,6 +3,8 @@ # Rob Miles July 2017 # Version 1.0 +import time +import random import pygame surface = None @@ -42,12 +44,12 @@ def setup(width=800, height=600, title=''): pygame.display.set_caption(title) - def handle_events(): ''' Consume events that are generated by the pygame window - These are not presntly used for anything + Captures key pressed events and sets the key_pressed value ''' + global key_pressed setup() for event in pygame.event.get(): pass @@ -95,17 +97,45 @@ def clear_display(): if image is not None: surface.blit(image, (0, 0)) + pygame.display.flip() + +def split_lines_on_spaces(text): + ''' + returns a list of words which have been + extracted from the text. + Spaces on the ends of words are + preserved + ''' + result = [] + got_space = False + word = '' + for ch in text: + if ch == ' ': + got_space = True + word = word + ch + else: + if got_space: + # first character of next word + result.append(word) + word=ch + got_space=False + else: + word = word + ch + + result.append(word) + + return result + def get_display_lines(text, font, width): ''' Returns a list of strings which have been split to fit the given window width using the supplied font ''' - space_width = font.size(' ')[0] result = [] - text_lines = text.splitlines() + text_lines = text.splitlines() for text_line in text_lines: - words = text_line.split() + words = split_lines_on_spaces(text_line) x = 0 line = '' for word in words: @@ -113,36 +143,19 @@ def get_display_lines(text, font, width): if x + word_width > width: # Remove the trailing space from the line # before adding to the list of lines to return - line = line.strip() result.append(line) - line = word + ' ' - x = word_width + space_width + line = word + x = word_width else: - line = line + word + ' ' - x = x + word_width + space_width - - if line != '': - # Got a partial line to add to the end - # Remove the trailing space from the line - # before adding to the list of lines to return - line = line.strip() - result.append(line) - return result + line = line + word + x = x + word_width + result.append(line) + return result -def display_message(text, size=200, margin=20, horiz='center', vert='center', - color=(255, 0, 0)): - ''' - Displays the text as a message - Sice can be used to select the size of the - text - ''' - global window_size - global surface - - handle_events() - clear_display() +def render_message(text, size=200, margin=20, horiz='center', vert='center', + color=(255, 0, 0), cursor=''): # Get the text version of the input text = str(text) @@ -167,28 +180,196 @@ def display_message(text, size=200, margin=20, horiz='center', vert='center', if vert == 'center': y = (window_size[1] - height) / 2.0 - elif vert == 'top': - y = margin elif vert == 'bottom': y=(window_size[1]-margin) - height + else: + # default vertical cursor position is top + y = margin + for rendered_line in rendered_lines: width = rendered_line.get_width() height = rendered_line.get_height() if horiz == 'center': x = (available_width - width) / 2.0 + margin - elif horiz == 'left': - x = margin elif horiz == 'right': - x = self.window_size[0] - width - margin + x = window_size[0] - width - margin + else: + # default position is left margin + x = margin surface.blit(rendered_line, (x, y)) y += height + + if cursor: + cursor_size = font.size(cursor) + cursor_width = cursor_size[0] + cursor_height = cursor_size[1] + if len(rendered_lines): + # put the cursor on the end of an existing line + y -= height + x += width + else: + # put the cursor in the start position for this + # orientation + # default x position is the margin + x = margin + + if horiz == 'center': + x = (available_width - cursor_width) / 2.0 + margin + elif horiz == 'right': + x = window_size[0] - cursor_width - margin + else: + # default position is left margin + x = margin + + if vert == 'center': + y = (window_size[1] - cursor_height) / 2.0 + elif vert == 'bottom': + y=(window_size[1]-margin) - cursor_height + else: + # default vertical cursor position is top + y = margin + + cursor_image = font.render(cursor, 1, color) + surface.blit(cursor_image, (x, y)) + pygame.display.flip() + +def display_message(text, size=200, margin=20, horiz='center', vert='center', + color=(255, 0, 0)): + ''' + Displays the text as a message + Size can be used to select the size of the + text + ''' + global window_size + global surface + + handle_events() + + clear_display() + + render_message(text, size=size, margin=margin, horiz=horiz, vert=vert, color=color) + +def get_dot(): + ''' + Waits for a mouse movement and then returns it + as a tuple of x and y coordinates + ''' + setup() + while True: + event = pygame.event.wait() + if event.type == 4: + # Event 4 is mouse motion + pos = event.dict['pos'] + return pos + +BLACK = ( 0, 0, 0) +WHITE = (255, 255, 255) +BLUE = ( 0, 0, 255) +GREEN = ( 0, 255, 0) +RED = (255, 0, 0) +YELLOW = (255, 255, 0) +MAGENTA = (255, 0, 255) +CYAN = (0, 255, 255) + +dot_color = WHITE + +def set_color(r,g,b): + dot_color = (r,g,b) + +def set_random_color(): + global dot_color + dot_color = (random.randint(0,255), + random.randint(0,255), + random.randint(0,255)) + +def get_mouse_pressed(): + return pygame.mouse.get_pressed()[0] + + +def draw_dot(pos, radius): + setup() + pygame.draw.circle(surface, dot_color, pos, radius) + pygame.display.flip() + + +def get_key(): + ''' + Waits for a keypress and then returns it as a string + Only characters are returned, not control keys + ''' + setup() + while True: + event = pygame.event.wait() + if event.type == 2: + # Event 2 is keydown + key_code = event.dict['unicode'] + if key_code: + return key_code + + +def get_string(prompt, size=50, margin=20, + color=(255, 0, 0), horiz='left', vert='center', + max_line_length=20): + ''' + Reads a string from the user + ''' + + setup() + + result = '' + cursor_char = '*' + cursor = None + + def redraw(): + clear_display() + + render_message(prompt+result, margin=margin, size=size, + horiz=horiz, vert=vert, color=color, cursor=cursor) + + def cursor_flip(): + nonlocal cursor + + # create a timer for the cursor + + cursor_event = pygame.USEREVENT+1 + + pygame.time.set_timer(cursor_event,500) + + while True: + event = pygame.event.wait() + + if event.type == cursor_event: + if cursor: + cursor = None + else: + cursor = cursor_char + redraw() + elif event.type == 2: + # Event 2 is keydown + key_code = event.dict['unicode'] + if key_code is None: + continue + if key_code == '\r': + break + elif key_code == '\x08': + if len(result) > 0: + result=result[:-1] + redraw() + else: + if len(result) < max_line_length: + result += key_code + redraw() + + # disable the timer for the cursor + pygame.time.set_timer(cursor_event,0) + return result + import urllib.request import xml.etree.ElementTree - def get_weather_temp(latitude,longitude): ''' Uses forecast.weather.gov to get the weather @@ -233,3 +414,5 @@ def get_weather_desciption(latitude,longitude): if t.tag == 'weather-conditions': if t.get('weather-summary') is not None: return t.get('weather-summary') + + diff --git a/Ch 05 Making Decisions in Programs/__pycache__/snaps.cpython-36.pyc b/Ch 05 Making Decisions in Programs/__pycache__/snaps.cpython-36.pyc new file mode 100644 index 0000000..83b33b9 Binary files /dev/null and b/Ch 05 Making Decisions in Programs/__pycache__/snaps.cpython-36.pyc differ diff --git a/Ch 06 Loops/__pycache__/snaps.cpython-36.pyc b/Ch 06 Loops/__pycache__/snaps.cpython-36.pyc new file mode 100644 index 0000000..5fda7f1 Binary files /dev/null and b/Ch 06 Loops/__pycache__/snaps.cpython-36.pyc differ diff --git a/Ch 07 Methods/snaps.py b/Ch 07 Methods/snaps.py new file mode 100644 index 0000000..2ddbaaa --- /dev/null +++ b/Ch 07 Methods/snaps.py @@ -0,0 +1,418 @@ +# Some pygame helper functions for simple image display +# and sound effect playback +# Rob Miles July 2017 +# Version 1.0 + +import time +import random +import pygame + +surface = None + + +def setup(width=800, height=600, title=''): + ''' + Sets up the pygame environment + ''' + global window_size + global back_color + global text_color + global image + global surface + + # Don't initialise if we already have + + if surface is not None: + return + + window_size = (width, height) + back_color = (255, 255, 255) + text_color = (255, 0, 0) + image = None + + # pre initialise pyGame's audio engine to avoid sound latency issues + pygame.mixer.pre_init(frequency=44100) + pygame.init() + + # initialise pyGame's audio engine + pygame.mixer.init() + + # Create the game surface + surface = pygame.display.set_mode(window_size) + + clear_display() + + pygame.display.set_caption(title) + +def handle_events(): + ''' + Consume events that are generated by the pygame window + Captures key pressed events and sets the key_pressed value + ''' + global key_pressed + setup() + for event in pygame.event.get(): + pass + + +def play_sound(filepath): + ''' + Plays the specified sound file + ''' + pygame.mixer.init() + sound = pygame.mixer.Sound(filepath) + sound.play() + + +def display_image(filepath): + ''' + Displays the image from the given filepath + Starts pygame if required + May throw exceptions + ''' + global surface + global window_size + global image + + handle_events() + image = pygame.image.load(filepath) + image = pygame.transform.smoothscale(image, window_size) + surface.blit(image, (0, 0)) + pygame.display.flip() + + +def clear_display(): + ''' + Clears the display to the background colour + and the image (if any) on top of it + ''' + + global surface + global image + global back_color + + handle_events() + + surface.fill(back_color) + if image is not None: + surface.blit(image, (0, 0)) + + pygame.display.flip() + +def split_lines_on_spaces(text): + ''' + returns a list of words which have been + extracted from the text. + Spaces on the ends of words are + preserved + ''' + result = [] + got_space = False + word = '' + for ch in text: + if ch == ' ': + got_space = True + word = word + ch + else: + if got_space: + # first character of next word + result.append(word) + word=ch + got_space=False + else: + word = word + ch + + result.append(word) + + return result + + +def get_display_lines(text, font, width): + ''' + Returns a list of strings which have been split + to fit the given window width using the supplied font + ''' + result = [] + text_lines = text.splitlines() + for text_line in text_lines: + words = split_lines_on_spaces(text_line) + x = 0 + line = '' + for word in words: + word_width = font.size(word)[0] + if x + word_width > width: + # Remove the trailing space from the line + # before adding to the list of lines to return + result.append(line) + line = word + x = word_width + else: + line = line + word + x = x + word_width + + result.append(line) + return result + + +def render_message(text, size=200, margin=20, horiz='center', vert='center', + color=(255, 0, 0), cursor=''): + + # Get the text version of the input + text = str(text) + + font = pygame.font.Font(None, size) + + available_width = window_size[0] - (margin * 2) + + lines = get_display_lines(text, font, available_width) + + rendered_lines = [] + + height = 0 + + for line in lines: + rendered_line = font.render(line, 1, color) + height += rendered_line.get_height() + rendered_lines.append(rendered_line) + + if height > window_size[1]: + raise Exception('Text too large for window') + + if vert == 'center': + y = (window_size[1] - height) / 2.0 + elif vert == 'bottom': + y=(window_size[1]-margin) - height + else: + # default vertical cursor position is top + y = margin + + + for rendered_line in rendered_lines: + width = rendered_line.get_width() + height = rendered_line.get_height() + if horiz == 'center': + x = (available_width - width) / 2.0 + margin + elif horiz == 'right': + x = window_size[0] - width - margin + else: + # default position is left margin + x = margin + surface.blit(rendered_line, (x, y)) + y += height + + if cursor: + cursor_size = font.size(cursor) + cursor_width = cursor_size[0] + cursor_height = cursor_size[1] + if len(rendered_lines): + # put the cursor on the end of an existing line + y -= height + x += width + else: + # put the cursor in the start position for this + # orientation + # default x position is the margin + x = margin + + if horiz == 'center': + x = (available_width - cursor_width) / 2.0 + margin + elif horiz == 'right': + x = window_size[0] - cursor_width - margin + else: + # default position is left margin + x = margin + + if vert == 'center': + y = (window_size[1] - cursor_height) / 2.0 + elif vert == 'bottom': + y=(window_size[1]-margin) - cursor_height + else: + # default vertical cursor position is top + y = margin + + cursor_image = font.render(cursor, 1, color) + surface.blit(cursor_image, (x, y)) + + pygame.display.flip() + + +def display_message(text, size=200, margin=20, horiz='center', vert='center', + color=(255, 0, 0)): + ''' + Displays the text as a message + Size can be used to select the size of the + text + ''' + global window_size + global surface + + handle_events() + + clear_display() + + render_message(text, size=size, margin=margin, horiz=horiz, vert=vert, color=color) + +def get_dot(): + ''' + Waits for a mouse movement and then returns it + as a tuple of x and y coordinates + ''' + setup() + while True: + event = pygame.event.wait() + if event.type == 4: + # Event 4 is mouse motion + pos = event.dict['pos'] + return pos + +BLACK = ( 0, 0, 0) +WHITE = (255, 255, 255) +BLUE = ( 0, 0, 255) +GREEN = ( 0, 255, 0) +RED = (255, 0, 0) +YELLOW = (255, 255, 0) +MAGENTA = (255, 0, 255) +CYAN = (0, 255, 255) + +dot_color = WHITE + +def set_color(r,g,b): + dot_color = (r,g,b) + +def set_random_color(): + global dot_color + dot_color = (random.randint(0,255), + random.randint(0,255), + random.randint(0,255)) + +def get_mouse_pressed(): + return pygame.mouse.get_pressed()[0] + + +def draw_dot(pos, radius): + setup() + pygame.draw.circle(surface, dot_color, pos, radius) + pygame.display.flip() + + +def get_key(): + ''' + Waits for a keypress and then returns it as a string + Only characters are returned, not control keys + ''' + setup() + while True: + event = pygame.event.wait() + if event.type == 2: + # Event 2 is keydown + key_code = event.dict['unicode'] + if key_code: + return key_code + + +def get_string(prompt, size=50, margin=20, + color=(255, 0, 0), horiz='left', vert='center', + max_line_length=20): + ''' + Reads a string from the user + ''' + + setup() + + result = '' + cursor_char = '*' + cursor = None + + def redraw(): + clear_display() + + render_message(prompt+result, margin=margin, size=size, + horiz=horiz, vert=vert, color=color, cursor=cursor) + + def cursor_flip(): + nonlocal cursor + + # create a timer for the cursor + + cursor_event = pygame.USEREVENT+1 + + pygame.time.set_timer(cursor_event,500) + + while True: + event = pygame.event.wait() + + if event.type == cursor_event: + if cursor: + cursor = None + else: + cursor = cursor_char + redraw() + elif event.type == 2: + # Event 2 is keydown + key_code = event.dict['unicode'] + if key_code is None: + continue + if key_code == '\r': + break + elif key_code == '\x08': + if len(result) > 0: + result=result[:-1] + redraw() + else: + if len(result) < max_line_length: + result += key_code + redraw() + + # disable the timer for the cursor + pygame.time.set_timer(cursor_event,0) + return result + +import urllib.request +import xml.etree.ElementTree + +def get_weather_temp(latitude,longitude): + ''' + Uses forecast.weather.gov to get the weather + for the specified latitude and longitude + ''' + url="http://forecast.weather.gov/MapClick.php?lat={0}&lon={1}&unit=0&lg=english&FcstType=dwml".format(latitude,longitude) + req=urllib.request.urlopen(url) + page=req.read() + doc=xml.etree.ElementTree.fromstring(page) + # I'm not proud of this, but by gum it works... + for child in doc: + if child.tag == 'data': + if child.attrib['type'] == 'current observations': + for item in child: + if item.tag == 'parameters': + for i in item: + if i.tag == 'temperature': + if i.attrib['type'] == 'apparent': + for t in i: + if t.tag =='value': + return int(t.text) + + +def get_weather_desciption(latitude,longitude): + ''' + Uses forecast.weather.gov to get the weather + for the specified latitude and longitude + ''' + url="http://forecast.weather.gov/MapClick.php?lat={0}&lon={1}&unit=0&lg=english&FcstType=dwml".format(latitude,longitude) + req=urllib.request.urlopen(url) + page=req.read() + doc=xml.etree.ElementTree.fromstring(page) + # I'm not proud of this, but by gum it works... + for child in doc: + if child.tag == 'data': + if child.attrib['type'] == 'current observations': + for item in child: + if item.tag == 'parameters': + for i in item: + if i.tag == 'weather': + for t in i: + if t.tag == 'weather-conditions': + if t.get('weather-summary') is not None: + return t.get('weather-summary') + + diff --git a/Ch 08 Collections/snaps.py b/Ch 08 Collections/snaps.py new file mode 100644 index 0000000..2ddbaaa --- /dev/null +++ b/Ch 08 Collections/snaps.py @@ -0,0 +1,418 @@ +# Some pygame helper functions for simple image display +# and sound effect playback +# Rob Miles July 2017 +# Version 1.0 + +import time +import random +import pygame + +surface = None + + +def setup(width=800, height=600, title=''): + ''' + Sets up the pygame environment + ''' + global window_size + global back_color + global text_color + global image + global surface + + # Don't initialise if we already have + + if surface is not None: + return + + window_size = (width, height) + back_color = (255, 255, 255) + text_color = (255, 0, 0) + image = None + + # pre initialise pyGame's audio engine to avoid sound latency issues + pygame.mixer.pre_init(frequency=44100) + pygame.init() + + # initialise pyGame's audio engine + pygame.mixer.init() + + # Create the game surface + surface = pygame.display.set_mode(window_size) + + clear_display() + + pygame.display.set_caption(title) + +def handle_events(): + ''' + Consume events that are generated by the pygame window + Captures key pressed events and sets the key_pressed value + ''' + global key_pressed + setup() + for event in pygame.event.get(): + pass + + +def play_sound(filepath): + ''' + Plays the specified sound file + ''' + pygame.mixer.init() + sound = pygame.mixer.Sound(filepath) + sound.play() + + +def display_image(filepath): + ''' + Displays the image from the given filepath + Starts pygame if required + May throw exceptions + ''' + global surface + global window_size + global image + + handle_events() + image = pygame.image.load(filepath) + image = pygame.transform.smoothscale(image, window_size) + surface.blit(image, (0, 0)) + pygame.display.flip() + + +def clear_display(): + ''' + Clears the display to the background colour + and the image (if any) on top of it + ''' + + global surface + global image + global back_color + + handle_events() + + surface.fill(back_color) + if image is not None: + surface.blit(image, (0, 0)) + + pygame.display.flip() + +def split_lines_on_spaces(text): + ''' + returns a list of words which have been + extracted from the text. + Spaces on the ends of words are + preserved + ''' + result = [] + got_space = False + word = '' + for ch in text: + if ch == ' ': + got_space = True + word = word + ch + else: + if got_space: + # first character of next word + result.append(word) + word=ch + got_space=False + else: + word = word + ch + + result.append(word) + + return result + + +def get_display_lines(text, font, width): + ''' + Returns a list of strings which have been split + to fit the given window width using the supplied font + ''' + result = [] + text_lines = text.splitlines() + for text_line in text_lines: + words = split_lines_on_spaces(text_line) + x = 0 + line = '' + for word in words: + word_width = font.size(word)[0] + if x + word_width > width: + # Remove the trailing space from the line + # before adding to the list of lines to return + result.append(line) + line = word + x = word_width + else: + line = line + word + x = x + word_width + + result.append(line) + return result + + +def render_message(text, size=200, margin=20, horiz='center', vert='center', + color=(255, 0, 0), cursor=''): + + # Get the text version of the input + text = str(text) + + font = pygame.font.Font(None, size) + + available_width = window_size[0] - (margin * 2) + + lines = get_display_lines(text, font, available_width) + + rendered_lines = [] + + height = 0 + + for line in lines: + rendered_line = font.render(line, 1, color) + height += rendered_line.get_height() + rendered_lines.append(rendered_line) + + if height > window_size[1]: + raise Exception('Text too large for window') + + if vert == 'center': + y = (window_size[1] - height) / 2.0 + elif vert == 'bottom': + y=(window_size[1]-margin) - height + else: + # default vertical cursor position is top + y = margin + + + for rendered_line in rendered_lines: + width = rendered_line.get_width() + height = rendered_line.get_height() + if horiz == 'center': + x = (available_width - width) / 2.0 + margin + elif horiz == 'right': + x = window_size[0] - width - margin + else: + # default position is left margin + x = margin + surface.blit(rendered_line, (x, y)) + y += height + + if cursor: + cursor_size = font.size(cursor) + cursor_width = cursor_size[0] + cursor_height = cursor_size[1] + if len(rendered_lines): + # put the cursor on the end of an existing line + y -= height + x += width + else: + # put the cursor in the start position for this + # orientation + # default x position is the margin + x = margin + + if horiz == 'center': + x = (available_width - cursor_width) / 2.0 + margin + elif horiz == 'right': + x = window_size[0] - cursor_width - margin + else: + # default position is left margin + x = margin + + if vert == 'center': + y = (window_size[1] - cursor_height) / 2.0 + elif vert == 'bottom': + y=(window_size[1]-margin) - cursor_height + else: + # default vertical cursor position is top + y = margin + + cursor_image = font.render(cursor, 1, color) + surface.blit(cursor_image, (x, y)) + + pygame.display.flip() + + +def display_message(text, size=200, margin=20, horiz='center', vert='center', + color=(255, 0, 0)): + ''' + Displays the text as a message + Size can be used to select the size of the + text + ''' + global window_size + global surface + + handle_events() + + clear_display() + + render_message(text, size=size, margin=margin, horiz=horiz, vert=vert, color=color) + +def get_dot(): + ''' + Waits for a mouse movement and then returns it + as a tuple of x and y coordinates + ''' + setup() + while True: + event = pygame.event.wait() + if event.type == 4: + # Event 4 is mouse motion + pos = event.dict['pos'] + return pos + +BLACK = ( 0, 0, 0) +WHITE = (255, 255, 255) +BLUE = ( 0, 0, 255) +GREEN = ( 0, 255, 0) +RED = (255, 0, 0) +YELLOW = (255, 255, 0) +MAGENTA = (255, 0, 255) +CYAN = (0, 255, 255) + +dot_color = WHITE + +def set_color(r,g,b): + dot_color = (r,g,b) + +def set_random_color(): + global dot_color + dot_color = (random.randint(0,255), + random.randint(0,255), + random.randint(0,255)) + +def get_mouse_pressed(): + return pygame.mouse.get_pressed()[0] + + +def draw_dot(pos, radius): + setup() + pygame.draw.circle(surface, dot_color, pos, radius) + pygame.display.flip() + + +def get_key(): + ''' + Waits for a keypress and then returns it as a string + Only characters are returned, not control keys + ''' + setup() + while True: + event = pygame.event.wait() + if event.type == 2: + # Event 2 is keydown + key_code = event.dict['unicode'] + if key_code: + return key_code + + +def get_string(prompt, size=50, margin=20, + color=(255, 0, 0), horiz='left', vert='center', + max_line_length=20): + ''' + Reads a string from the user + ''' + + setup() + + result = '' + cursor_char = '*' + cursor = None + + def redraw(): + clear_display() + + render_message(prompt+result, margin=margin, size=size, + horiz=horiz, vert=vert, color=color, cursor=cursor) + + def cursor_flip(): + nonlocal cursor + + # create a timer for the cursor + + cursor_event = pygame.USEREVENT+1 + + pygame.time.set_timer(cursor_event,500) + + while True: + event = pygame.event.wait() + + if event.type == cursor_event: + if cursor: + cursor = None + else: + cursor = cursor_char + redraw() + elif event.type == 2: + # Event 2 is keydown + key_code = event.dict['unicode'] + if key_code is None: + continue + if key_code == '\r': + break + elif key_code == '\x08': + if len(result) > 0: + result=result[:-1] + redraw() + else: + if len(result) < max_line_length: + result += key_code + redraw() + + # disable the timer for the cursor + pygame.time.set_timer(cursor_event,0) + return result + +import urllib.request +import xml.etree.ElementTree + +def get_weather_temp(latitude,longitude): + ''' + Uses forecast.weather.gov to get the weather + for the specified latitude and longitude + ''' + url="http://forecast.weather.gov/MapClick.php?lat={0}&lon={1}&unit=0&lg=english&FcstType=dwml".format(latitude,longitude) + req=urllib.request.urlopen(url) + page=req.read() + doc=xml.etree.ElementTree.fromstring(page) + # I'm not proud of this, but by gum it works... + for child in doc: + if child.tag == 'data': + if child.attrib['type'] == 'current observations': + for item in child: + if item.tag == 'parameters': + for i in item: + if i.tag == 'temperature': + if i.attrib['type'] == 'apparent': + for t in i: + if t.tag =='value': + return int(t.text) + + +def get_weather_desciption(latitude,longitude): + ''' + Uses forecast.weather.gov to get the weather + for the specified latitude and longitude + ''' + url="http://forecast.weather.gov/MapClick.php?lat={0}&lon={1}&unit=0&lg=english&FcstType=dwml".format(latitude,longitude) + req=urllib.request.urlopen(url) + page=req.read() + doc=xml.etree.ElementTree.fromstring(page) + # I'm not proud of this, but by gum it works... + for child in doc: + if child.tag == 'data': + if child.attrib['type'] == 'current observations': + for item in child: + if item.tag == 'parameters': + for i in item: + if i.tag == 'weather': + for t in i: + if t.tag == 'weather-conditions': + if t.get('weather-summary') is not None: + return t.get('weather-summary') + + diff --git a/Ch 09 Using classes to store data/snaps.py b/Ch 09 Using classes to store data/snaps.py new file mode 100644 index 0000000..2ddbaaa --- /dev/null +++ b/Ch 09 Using classes to store data/snaps.py @@ -0,0 +1,418 @@ +# Some pygame helper functions for simple image display +# and sound effect playback +# Rob Miles July 2017 +# Version 1.0 + +import time +import random +import pygame + +surface = None + + +def setup(width=800, height=600, title=''): + ''' + Sets up the pygame environment + ''' + global window_size + global back_color + global text_color + global image + global surface + + # Don't initialise if we already have + + if surface is not None: + return + + window_size = (width, height) + back_color = (255, 255, 255) + text_color = (255, 0, 0) + image = None + + # pre initialise pyGame's audio engine to avoid sound latency issues + pygame.mixer.pre_init(frequency=44100) + pygame.init() + + # initialise pyGame's audio engine + pygame.mixer.init() + + # Create the game surface + surface = pygame.display.set_mode(window_size) + + clear_display() + + pygame.display.set_caption(title) + +def handle_events(): + ''' + Consume events that are generated by the pygame window + Captures key pressed events and sets the key_pressed value + ''' + global key_pressed + setup() + for event in pygame.event.get(): + pass + + +def play_sound(filepath): + ''' + Plays the specified sound file + ''' + pygame.mixer.init() + sound = pygame.mixer.Sound(filepath) + sound.play() + + +def display_image(filepath): + ''' + Displays the image from the given filepath + Starts pygame if required + May throw exceptions + ''' + global surface + global window_size + global image + + handle_events() + image = pygame.image.load(filepath) + image = pygame.transform.smoothscale(image, window_size) + surface.blit(image, (0, 0)) + pygame.display.flip() + + +def clear_display(): + ''' + Clears the display to the background colour + and the image (if any) on top of it + ''' + + global surface + global image + global back_color + + handle_events() + + surface.fill(back_color) + if image is not None: + surface.blit(image, (0, 0)) + + pygame.display.flip() + +def split_lines_on_spaces(text): + ''' + returns a list of words which have been + extracted from the text. + Spaces on the ends of words are + preserved + ''' + result = [] + got_space = False + word = '' + for ch in text: + if ch == ' ': + got_space = True + word = word + ch + else: + if got_space: + # first character of next word + result.append(word) + word=ch + got_space=False + else: + word = word + ch + + result.append(word) + + return result + + +def get_display_lines(text, font, width): + ''' + Returns a list of strings which have been split + to fit the given window width using the supplied font + ''' + result = [] + text_lines = text.splitlines() + for text_line in text_lines: + words = split_lines_on_spaces(text_line) + x = 0 + line = '' + for word in words: + word_width = font.size(word)[0] + if x + word_width > width: + # Remove the trailing space from the line + # before adding to the list of lines to return + result.append(line) + line = word + x = word_width + else: + line = line + word + x = x + word_width + + result.append(line) + return result + + +def render_message(text, size=200, margin=20, horiz='center', vert='center', + color=(255, 0, 0), cursor=''): + + # Get the text version of the input + text = str(text) + + font = pygame.font.Font(None, size) + + available_width = window_size[0] - (margin * 2) + + lines = get_display_lines(text, font, available_width) + + rendered_lines = [] + + height = 0 + + for line in lines: + rendered_line = font.render(line, 1, color) + height += rendered_line.get_height() + rendered_lines.append(rendered_line) + + if height > window_size[1]: + raise Exception('Text too large for window') + + if vert == 'center': + y = (window_size[1] - height) / 2.0 + elif vert == 'bottom': + y=(window_size[1]-margin) - height + else: + # default vertical cursor position is top + y = margin + + + for rendered_line in rendered_lines: + width = rendered_line.get_width() + height = rendered_line.get_height() + if horiz == 'center': + x = (available_width - width) / 2.0 + margin + elif horiz == 'right': + x = window_size[0] - width - margin + else: + # default position is left margin + x = margin + surface.blit(rendered_line, (x, y)) + y += height + + if cursor: + cursor_size = font.size(cursor) + cursor_width = cursor_size[0] + cursor_height = cursor_size[1] + if len(rendered_lines): + # put the cursor on the end of an existing line + y -= height + x += width + else: + # put the cursor in the start position for this + # orientation + # default x position is the margin + x = margin + + if horiz == 'center': + x = (available_width - cursor_width) / 2.0 + margin + elif horiz == 'right': + x = window_size[0] - cursor_width - margin + else: + # default position is left margin + x = margin + + if vert == 'center': + y = (window_size[1] - cursor_height) / 2.0 + elif vert == 'bottom': + y=(window_size[1]-margin) - cursor_height + else: + # default vertical cursor position is top + y = margin + + cursor_image = font.render(cursor, 1, color) + surface.blit(cursor_image, (x, y)) + + pygame.display.flip() + + +def display_message(text, size=200, margin=20, horiz='center', vert='center', + color=(255, 0, 0)): + ''' + Displays the text as a message + Size can be used to select the size of the + text + ''' + global window_size + global surface + + handle_events() + + clear_display() + + render_message(text, size=size, margin=margin, horiz=horiz, vert=vert, color=color) + +def get_dot(): + ''' + Waits for a mouse movement and then returns it + as a tuple of x and y coordinates + ''' + setup() + while True: + event = pygame.event.wait() + if event.type == 4: + # Event 4 is mouse motion + pos = event.dict['pos'] + return pos + +BLACK = ( 0, 0, 0) +WHITE = (255, 255, 255) +BLUE = ( 0, 0, 255) +GREEN = ( 0, 255, 0) +RED = (255, 0, 0) +YELLOW = (255, 255, 0) +MAGENTA = (255, 0, 255) +CYAN = (0, 255, 255) + +dot_color = WHITE + +def set_color(r,g,b): + dot_color = (r,g,b) + +def set_random_color(): + global dot_color + dot_color = (random.randint(0,255), + random.randint(0,255), + random.randint(0,255)) + +def get_mouse_pressed(): + return pygame.mouse.get_pressed()[0] + + +def draw_dot(pos, radius): + setup() + pygame.draw.circle(surface, dot_color, pos, radius) + pygame.display.flip() + + +def get_key(): + ''' + Waits for a keypress and then returns it as a string + Only characters are returned, not control keys + ''' + setup() + while True: + event = pygame.event.wait() + if event.type == 2: + # Event 2 is keydown + key_code = event.dict['unicode'] + if key_code: + return key_code + + +def get_string(prompt, size=50, margin=20, + color=(255, 0, 0), horiz='left', vert='center', + max_line_length=20): + ''' + Reads a string from the user + ''' + + setup() + + result = '' + cursor_char = '*' + cursor = None + + def redraw(): + clear_display() + + render_message(prompt+result, margin=margin, size=size, + horiz=horiz, vert=vert, color=color, cursor=cursor) + + def cursor_flip(): + nonlocal cursor + + # create a timer for the cursor + + cursor_event = pygame.USEREVENT+1 + + pygame.time.set_timer(cursor_event,500) + + while True: + event = pygame.event.wait() + + if event.type == cursor_event: + if cursor: + cursor = None + else: + cursor = cursor_char + redraw() + elif event.type == 2: + # Event 2 is keydown + key_code = event.dict['unicode'] + if key_code is None: + continue + if key_code == '\r': + break + elif key_code == '\x08': + if len(result) > 0: + result=result[:-1] + redraw() + else: + if len(result) < max_line_length: + result += key_code + redraw() + + # disable the timer for the cursor + pygame.time.set_timer(cursor_event,0) + return result + +import urllib.request +import xml.etree.ElementTree + +def get_weather_temp(latitude,longitude): + ''' + Uses forecast.weather.gov to get the weather + for the specified latitude and longitude + ''' + url="http://forecast.weather.gov/MapClick.php?lat={0}&lon={1}&unit=0&lg=english&FcstType=dwml".format(latitude,longitude) + req=urllib.request.urlopen(url) + page=req.read() + doc=xml.etree.ElementTree.fromstring(page) + # I'm not proud of this, but by gum it works... + for child in doc: + if child.tag == 'data': + if child.attrib['type'] == 'current observations': + for item in child: + if item.tag == 'parameters': + for i in item: + if i.tag == 'temperature': + if i.attrib['type'] == 'apparent': + for t in i: + if t.tag =='value': + return int(t.text) + + +def get_weather_desciption(latitude,longitude): + ''' + Uses forecast.weather.gov to get the weather + for the specified latitude and longitude + ''' + url="http://forecast.weather.gov/MapClick.php?lat={0}&lon={1}&unit=0&lg=english&FcstType=dwml".format(latitude,longitude) + req=urllib.request.urlopen(url) + page=req.read() + doc=xml.etree.ElementTree.fromstring(page) + # I'm not proud of this, but by gum it works... + for child in doc: + if child.tag == 'data': + if child.attrib['type'] == 'current observations': + for item in child: + if item.tag == 'parameters': + for i in item: + if i.tag == 'weather': + for t in i: + if t.tag == 'weather-conditions': + if t.get('weather-summary') is not None: + return t.get('weather-summary') + + diff --git a/Ch 10 Using classes to create active objects/__pycache__/snaps.cpython-36.pyc b/Ch 10 Using classes to create active objects/__pycache__/snaps.cpython-36.pyc new file mode 100644 index 0000000..caad8e6 Binary files /dev/null and b/Ch 10 Using classes to create active objects/__pycache__/snaps.cpython-36.pyc differ diff --git a/Ch 13 Python and Graphical User Interfaces/EG13-01 First Adding machine.py b/Ch 13 Python and Graphical User Interfaces/EG13-01 First Adding machine.py index e0ce244..8b8f3ca 100644 --- a/Ch 13 Python and Graphical User Interfaces/EG13-01 First Adding machine.py +++ b/Ch 13 Python and Graphical User Interfaces/EG13-01 First Adding machine.py @@ -4,7 +4,7 @@ class Adder(object): ''' - Implementes an adding machine using a the Tkinter GUI + Implements an adding machine using the Tkinter GUI Call the method display to initiate the display ''' def display(self): diff --git a/Ch 13 Python and Graphical User Interfaces/EG13-02 Exception handler with messages.py b/Ch 13 Python and Graphical User Interfaces/EG13-02 Exception handler with messages.py index bf8c89d..74739b3 100644 --- a/Ch 13 Python and Graphical User Interfaces/EG13-02 Exception handler with messages.py +++ b/Ch 13 Python and Graphical User Interfaces/EG13-02 Exception handler with messages.py @@ -4,7 +4,7 @@ class Adder(object): ''' - Implementes an adding machine using a the Tkinter GUI + Implements an adding machine using the Tkinter GUI Call the method display to initiate the display ''' def display(self): diff --git a/Ch 13 Python and Graphical User Interfaces/EG13-04 Adder with messagebox.py b/Ch 13 Python and Graphical User Interfaces/EG13-04 Adder with messagebox.py index 5ba8b95..df20e1a 100644 --- a/Ch 13 Python and Graphical User Interfaces/EG13-04 Adder with messagebox.py +++ b/Ch 13 Python and Graphical User Interfaces/EG13-04 Adder with messagebox.py @@ -5,7 +5,7 @@ class Adder(object): ''' - Implementes an adding machine using a the Tkinter GUI + Implements an adding machine using the Tkinter GUI Call the method display to initiate the display ''' def display(self): diff --git a/Ch 13 Python and Graphical User Interfaces/EG13-05 TemperatureConverter Starter/TemperatureConverter.py b/Ch 13 Python and Graphical User Interfaces/EG13-05 TemperatureConverter Starter/TemperatureConverter.py index 6fb521b..cada867 100644 --- a/Ch 13 Python and Graphical User Interfaces/EG13-05 TemperatureConverter Starter/TemperatureConverter.py +++ b/Ch 13 Python and Graphical User Interfaces/EG13-05 TemperatureConverter Starter/TemperatureConverter.py @@ -30,7 +30,7 @@ def display(self): def fah_to_cent(): ''' - Convert from fahrenheit to centigrade and display the result + Convert from Fahrenheit to centigrade and display the result ''' fah_string = fah_entry.get() fah_float = float(fah_string) @@ -40,7 +40,7 @@ def fah_to_cent(): def cent_to_fah(): ''' - Convert from centigrade to fahrenheit and display the result + Convert from centigrade to Fahrenheit and display the result ''' cent_string = cent_entry.get() cent_float = float(cent_string) diff --git a/Ch 13 Python and Graphical User Interfaces/EG13-06 TemperatureConverter Complete/TemperatureConverter.py b/Ch 13 Python and Graphical User Interfaces/EG13-06 TemperatureConverter Complete/TemperatureConverter.py index f41256c..e61545f 100644 --- a/Ch 13 Python and Graphical User Interfaces/EG13-06 TemperatureConverter Complete/TemperatureConverter.py +++ b/Ch 13 Python and Graphical User Interfaces/EG13-06 TemperatureConverter Complete/TemperatureConverter.py @@ -33,7 +33,7 @@ def display(self): def fah_to_cent(): ''' - Convert from fahrenheit to centigrade and display the result + Convert from Fahrenheit to centigrade and display the result ''' fah_string = fah_entry.get() fah_float = float(fah_string) @@ -43,7 +43,7 @@ def fah_to_cent(): def cent_to_fah(): ''' - Convert from centigrade to fahrenheit and display the result + Convert from centigrade to Fahrenheit and display the result ''' cent_string = cent_entry.get() cent_float = float(cent_string) diff --git a/Ch 15 Python programs as network servers/EG15-01 Tiny socket web server.py b/Ch 15 Python programs as network servers/EG15-01 Tiny socket web server.py index 11d9749..34f2cfa 100644 --- a/Ch 15 Python programs as network servers/EG15-01 Tiny socket web server.py +++ b/Ch 15 Python programs as network servers/EG15-01 Tiny socket web server.py @@ -22,21 +22,21 @@ html_request_string = network_message_bytes.decode() print(html_request_string) -status = 'HTTP/1.1 200 OK' +status_string = 'HTTP/1.1 200 OK' -header = '''Content-Type: text/html; charset=UTF-8 +header_string = '''Content-Type: text/html; charset=UTF-8 Connection: close ''' -content=''' +content_string = '''

hello from our tiny server

''' -response_string = status + header + content +response_string = status_string + header_string + content_string response_bytes = response_string.encode() diff --git a/Ch 15 Python programs as network servers/EG15-02 Python web server.py b/Ch 15 Python programs as network servers/EG15-02 Python web server.py index c605176..8b1160a 100644 --- a/Ch 15 Python programs as network servers/EG15-02 Python web server.py +++ b/Ch 15 Python programs as network servers/EG15-02 Python web server.py @@ -6,7 +6,7 @@ class webServerHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): ''' - This method is calld when the server receives + This method is called when the server receives a GET requests from the client It sends a fixed message back to the client ''' diff --git a/Ch 15 Python programs as network servers/EG15-03 Python web page server/EG15-03 Python web page server.py b/Ch 15 Python programs as network servers/EG15-03 Python web page server/EG15-03 Python web page server.py index 434e8c6..81fc750 100644 --- a/Ch 15 Python programs as network servers/EG15-03 Python web page server/EG15-03 Python web page server.py +++ b/Ch 15 Python programs as network servers/EG15-03 Python web page server/EG15-03 Python web page server.py @@ -6,7 +6,7 @@ class WebServerHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): ''' - This method is calld when the server receives + This method is called when the server receives a GET requests from the client It opens a file with the requested path and sends back the contents