This tutorial is part of the skills labs within Interactive Data Science and Visualization.

Python Graphics

In the previous tutorial, we explored the basics of Python. We told the computer about new actions that could be taken (functions) and we saw how names (variables) allow us to manipulate things over time. However, we were confined to communicating with the computer through text. In this tutorial, we will start drawing: learning how to communicate through graphics. People who do this kind of work often refer to it as creative coding in making a distinction to more traditional programs like what we built in our last exercise. You may hear that phrase thrown around a bit during the semester.
Contents

Exercise

We will start by drawing a simple line. In the process, we will explore the coordinate system and understand how pixels are layed out within a sketch. Finally, we will add in the "user" as a component of our designs. Specifically, we will draw a line from the center of the screen to the user's cursor interactively. Note that, as folks reading this tutorial may have very different levels of prior experience in Python, I'll be explaining things in a lot of detail with expandable sections along the way. However, if you are very familiar with Python already, you may choose just to read through the code snippets.
About the user

There's some debate as to how to refer to the human on the other side of the screen using a piece of software. I'll often just lean on the phrase "user" as it is common but you may also hear people talk about the audience (like of a piece of art), the reader (like of a visualization) or the player (like of an interactive piece). Each framing is useful in different ways but we we will return to this debate later.

Setup

We are going to build full programs using Sketchingpy, a technology which supports creative coding in Python across desktop and web. This will give us access to graphics.

We should talk briefly about how we will do this programming. We used a computational notebook in our previous tutorial to try out little snippets of code. While notebooks are excellent for analysis and more traditional programming, I recommend that you start building these interaction-heavy experiences through an online sketchbook. The idea of a sketchbook is common in the creative coding community as an equivalent to computational notebooks and, with Sketchingpy, all you need is a web browser! Still, the choice is yours.

If you are using Python locally, you can instead install Sketchingpy on your own machine. Otherwise, go ahead and open up the online sketchbook before going to the next step.

Libraries

We are going to reuse some code from some other developers to help us. Specifically, we will import the Sketchingpy and time libraries into our project.
import sketchingpy
import time
You can learn more in the time reference, Sketchingpy examples, and Sketchingpy reference.
About libraries

We discussed previously that a program is like an empty world in which we construct new things with our thoughts. We express those thoughts through code, building the items which occupy those worlds and the rules that govern their behavior. We can build these worlds from scratch but sometimes it helps to pull in the work of other developers. We can do this through "imports" which reach into another developer's world and borrow variables from them. These two lines of code copy "sketchingpy" and "time" into our world as variables. These kinds of variables that you can bring in from other worlds are called libraries. Note that we will see soon how to make variables that contain other variables but sketchingpy.Sketch2D says use Sketch2D from within Sketchingpy.

Sketch

In the creative coding community, we often use the term "sketches" to refer to our programs. Sometimes sketches are just little doodles or early ideas we want to get onto the screen and see how they feel. Other times, they can be large software projects spanning many files of code to produce complex graphics. We can create a 2D sketch using the following:
sketch = sketchingpy.Sketch2D(500, 400)
This code builds a small space 500 pixels wide and 400 pixels tall where we can draw.
About pixels and coordinates

Some sketches are used to display things on a screen. Others are used to save images to files. In both cases, the smallest unit we can work with is called a pixel. Pixels are little lights that make up your screen that are turned on and off or changed color to show the user graphics. These are arranged in a grid which we refer to as a coordinate system. In a 2D sketch, the upper most left pixel is at horizontal position 0 and veritical position 0. Meanwhile, in our sketch, the bottom most right pixel is at horizontal position 500 and vertical position 400. We tend to refer to horizontal position as x and veritical position as y.

Drawing lines

Let's draw a line:
sketch.draw_line(500 / 2, 400 / 2, 100, 50)
This will draw a line across the sketch.
About draw_line

Here we are using a function called "draw_line" and the first number ("parameter") refers to the x position where to start the line and the second number refers to the y position where to start the line. The third and fourth numbers are the x and y positions of where to end. We say sketch.draw_line because we are telling the computer to execute the draw_line function within our sketch. Learn more about draw_line in the Sketchingpy reference.

Let's actually re-write that previous line as follows (go ahead and paste this in):
center_x = 500 / 2
center_y = 400 / 2
end_x = 100
end_y = 50
sketch.draw_line(center_x, center_y, end_x, end_y)
That is a little easier to read maybe. We are drawing a line from the center of the screen to the upper left. Finally, we need to tell the computer to show the sketch on the screen (as opposed to saving it to a file, for example):
sketch.show()
Now that we have something interesting, let's run this sketch. Try changing the numbers to see what happens to what is shown on the screen.

Life

Our program right now feels a little dead. It just draws a line on the screen and ends. What if it could do things in repetition like maybe change the end position where the line is drawn? Try the following (put it before the line that says sketch.show):
start_time = time.time()

def draw_moving_line(self):
    seconds = time.time() - start_time
    pixels_per_second = 5
    offset = seconds * pixels_per_second
    sketch.draw_line(250, 200, 100, 50 + offset)

sketch.on_step(draw_moving_line)
Need help? Here's what my python file looks like. Go ahead and run this. Afterwards, we will break down what this does step by step.
About comments

Note that, if you open my file, the lines starting with a hash mark are called comments. The computer ignores everything after the hash until the next line. I'm using comments here to indicate what the file does and what license it is under. To learn more about licenses, see the reading associated with this lecture.

Steps

The little snippet of code we just ran has a function that we want to have called each moment the program executes. For the computer, moments are very fast and called "steps" with roughly 30 steps per second. In other words, we are instructing the computer to call draw_moving_line each step that the sketch takes. Within that function, we instruct the computer to draw a line and change the ending y coordinate by 5 pixels for each second that has ellapsed. How do we know how long it has been? Let's look at time.
Terminology surrounding steps

Some frameworks call these moments steps. However, others may call them frames, redraws, or refreshes. We are sticking with the terminology used by Sketchingpy. That said, you may also hear "FPS" which stands for frames per second. In our case, this is steps per second.

Time

Each time the computer calls draw_moving_line, we see how many seconds it has been since start_time to determine by how much to change the end position of the line. This is important because sometimes the computer gets busy or interrupted as it multi-tasks. So, the time between each step is not always excatly the same. Refering back to time.time allows us to make sure things are smooth even if there is an interruption.
About time

For old reasons, time.time is a function that returns the number of seconds since a specific moment in 1970 called the epoch. This starting point is just an arbitrary reference used by computers around the world to keep track of what time it is. This has been useful for all sorts of stuff, especially networking. All that said, we can use it to keep track of how long our program has been running. When we create the start_time variable, we note down the current number of seconds when that line of code is executed. Afterwards, We can refer back to start_time and new calls to time.time to see how long it has been. Note that "time.time" says use the time function from the developer of time.

Self

What about that function parameter called self? This is actually just another name for our sketch variable. So, you could do this instead and it would have the same result:
def draw_moving_line(self):
    seconds = time.time() - start_time
    pixels_per_second = 5
    offset = seconds * pixels_per_second
    self.draw_line(250, 200, 100, 50 + offset)
Go ahead and give it a try! All that said, when programs are this small, I understand it feels a little silly to have two variables pointing to the same thing. However, we will see in the next tutorial when this becomes useful.

Conversation

Now our program feels a little more alive. However, even if it replies in graphics, so far we've only spoken to the computer through text (code). What if we could speak through motion? Go ahead and delete your old implementation and add the following:
def draw_moving_line(self):
    mouse = sketch.get_mouse()
    sketch.draw_line(250, 200, mouse.get_pointer_x(), mouse.get_pointer_y())
Need help? Here's what my interactive python file looks like. We ask our sketch to give us the position of the mouse. Then, we draw from the center of the sketch to the cursor position on each step.

Wrapping up

We will branch out into simluations in the next tutorial. We will also take a closer look at some of the Python language features we used here to help build sketches.
Motivation for the next tutorial

We didn't comment on it much but there is something new happening within this code. What's really going on with "sketch." in the code "sketch.draw_line" that we saw above? What's the significance of saying draw_line happens within sketch? We will explore that deeper in our next tutorial as we look to using creative coding for simulation and visualization.

If you are doing the course for-credit, this is all that is needed for Lecture 3 / Skills Lab 1. We will do Tutorial 3 as part of Lecture 4 / Skills Lab 2.
Citations