Reading and Writing

Jed Rembold & Fred Agbo

March 11, 2024

Announcements

  • Project 2: Breakout is due tomorrow at 10pm!
  • Problem set 5 will be posted tomorrow: To be due next week Tuesday
  • Grading for Problem Set 4 will be returned this week!
  • Polling continues on this link https://www.polleverywhere.com/agbofred203

Review!

Suppose I construct the below 2D array using list comprehension:

A = [[i+j for i in range(3)] for j in range(4)]

What would be the output of:

print([A[i][2] for i in range(len(A))])


  1. [0,1,2]
  2. [2,3,4]
  3. [2,3,4,5]
  4. [2,2,2,2]

Breaking out the Pixel Colors

  • You do not need to convert the pixel values yourself! PGL has built-in ways to extract the various colors
Function Description
GImage.get_red(pixel) Returns the integer (0-255) corresponding to the red portion of the pixel
GImage.get_green(pixel) Returns the integer (0-255) corresponding to the green portion of the pixel
GImage.get_blue(pixel) Returns the integer (0-255) corresponding to the blue portion of the pixel
GImage.get_alpha(pixel) Returns the integer (0-255) corresponding to the alpha portion of the pixel
GImage.create_rgb_pixel(r,g,b) Returns a 32-bit integer corresponding to the desired color

Image Thresholding

  • As an example of reading and manipulating pixel values, lets look at how we could threshold the image to the right
  • Thresholding is when you take a grayscale image and convert it to a black and white image, where a pixel is set to be white if it is above a certain threshold in brightness
  • Grayscale, so each RGB component is the same Let’s threshold at a value of 30
Blurry Moon by Jed

Image Thresholding Example


from pgl import GWindow, GOval, GImage

gw =GWindow(600,400)
image = GImage("Moon.png", 0,0)
image.scale(gw.get_width()/image.get_width())
gw.add(image)

def imagetreshold(e):
    TRESHOLD = 130
    pixel = image.get_pixel_array()
    #print(pixel)
    for r in range(len(pixel)):
        for c in range(len(pixel[0])):
            value = pixel[r][c]
            red =GImage.get_red(value)
            if red< TRESHOLD:
                pixel[r][c]= GImage.create_rgb_pixel(0,0,0)
            else:
                pixel[r][c] = GImage.create_rgb_pixel(255,255,255)
    # You must create a new Gimage
    new_image = GImage(pixel)
    gw.add(new_image)
gw.add_event_listener("click", imagetreshold)

Aren’t you Exceptional

  • When opening a file for reading, it is possible the file does not exist!
    • Python handles this (and many other potential errors that can arise) using a mechanism called exception handling
    • Common in other languages as well
  • An exception is a object that belongs to an overarching hierarchy of exception classes
    • Different classes/types for different purposes
    • File operations, for example, use the exception class IOError
  • If open encounters an error, it reports the error by raising an exception with IOError as its type.
    • Raising an exception generally immediately terminates your program, but sometimes that is undesirable

Ignore Yoda, there is a try

  • Python uses the try statement to indicate an interest in trying to handle a possible exception

  • In simplest form, the syntax is:

    try:
        # Code that may cause an exception
    except type_of_exception:
        # Code to handle that type of exception
  • type_of_exception here is the class name of the exception being handled

    • IOError for the file reading errors we are discussing
  • Any exceptions arising from within the try block or within functions called within the try block would be “caught” and the lower block of code run instead of terminating the program

Example: Requesting Existing File

  • As an example, the below function will repeatedly ask the user to supply a file name that actually exists.

  • It will not just immediately break should they give it an invalid filename!

    def get_existing_file(prompt="Input a filename: "):
        while True:
            filename = input(prompt)
            try:
                with open(filename):
                    return filename
            except IOError:
                print("That filename is invalid!")
  • If the open call succeeds, we immediately just return the filename, but if it fails due to a IOError, we display a message and then keep asking

Choosing Wisely

  • The Python package used to implement pgl.py also supports a mechanism to choose files interactively, made available through the filechooser.py library module.
  • filechooser.py exports two functions:
    • choose_input_file for selecting a file
    • choose_output_file for selecting a folder and filename to save a file to
  • Both open up a file dialog that lets you select/choose a file
    • Clicking Open or Save returns the full pathname of the file
    • Clicking Cancel returns an empty string
  • Using it thus looks something like:
filename = choose_input_file()
with open(filename) as f:
    # Code to read file

Writing Text Files

  • You can write text files using almost the same syntax as reading:

    with open(filename, mode) as file_handle:
        # Code to write the file using file_handle
  • Note the mode parameter to open here! Mode is a string which is either

    • "w" to write a new file (or overwrite an existing file)
    • "a" to append new contents to the end of an existing file
  • The file handler supports the methods:

    • .write(some_string) to write a string to the file
    • .writelines(iterable_of_strings) to write each iterable element to the file

Writing ASCII SINE

  • Suppose I wanted to try my hand at some ASCII art and fill a text file with a vertical oscillating sine wave
  • A sine wave generally looks like: \[ A + A \sin\left(\frac{2\pi}{T}x\right)\] where \(A\) is the amplitude of the wave and \(T\) the period of the wave, or how quickly it repeats
    • The extra \(A +\) out front is to push the wave over to the right, since we can’t really place negative characters
  • How can we put this together?

ASCII SINE Code


from math import sin, pi

def sine_file(filename, A, T, symbol, padding=" "):
    """ 
    Creates a new sine wave in the provided file with the provided amplitude (A),
    and period (T) with the indicated symbol at the end.

    Inputs:
        filename (string): the name of the file to write the art to
        A (int): the amplitude of the wave in terms of number of characters
        T (int): the period of the wave in terms of number of lines
        symbol (string): the symbol to place to mark the wave
        padding (string): what character to pad the left side of the wave with

    Outputs:
        None
    """

    def compute_symb_placement(A, T, x):
        """Computes where the symbol should be placed."""
        value = A * sin(2 * pi / T * x) + A
        return int(value) # to integer character placement

    def construct_line(placement, symbol, padding):
        """Constructs the line with the necessary padding and symbol at the end."""
        return padding * placement + symbol

    with open(filename, 'w') as fh:
        for x in range(10 * T): # write 10 periods worth of lines
            v = compute_symb_placement(A, T, x)
            line = construct_line(v, symbol, padding)
            fh.write(line + '\n') # need the newline character at the end!

if __name__ == '__main__':
    sine_file('sine_test.txt', A=30, T=50, symbol='X')
// reveal.js plugins