Aims

The aim of this lab is to setup a simple python project using the Turtle module and experiment with writing code and functions. By the end of the Lab we should be able to

1. setup a simple python project
2. run programs from the command line
3. expand our use of the Turtle module.
4. use version control to take snapshots of our code.

Getting Started

We are going to create a simple folder called “TurtleFunctions” to put our code to use and test.

mkdir TurtleFunctions
cd TurtleFunctions
touch square.py
code .


In the square.py file we will add the following

#!/usr/bin/env python
import turtle

def square(t, width=10):
for i in range(4):
t.forward(width)
t.right(90)

if __name__ == "__main__":
my_turtle = turtle.Turtle()
square(my_turtle, 100)
turtle.mainloop()


__name__ and __main__

These are know as “dunder” attributes, in this particular case we are using the __name__ one which tells programmatically if your code is being run directly as a script or if it is being imported from another module. As we will see later this can be really useful for testing and other things. For example run python -m turtle in the terminal. This will determine that the turtle is being run as a script and run all the tests.

Getting started with git

Git is a version control system ( vcs) it can be used in a number of ways both for individual projects or working as a team. For todays example we are going to use it as an individual.

Git is a command line tool but also has a gui and it is also integrated into the IDE (VSCode).

First we need to tell git who we are and give it an email address (this helps git to know who is making the commits and who to blame!).

git config --global user.name " put your name here"
git config --global user.email " put your email here"


To get started we will change into the TurtleFunctions folder

cd TurtleFunctions


We can run the following command to get information about the current folder.

git status
fatal: not a git repository (or any of the parent directories): .git


To setup our folder as a git repository we need to run the following

git init
Initialized empty Git repository in /Users/jmacey/tmp/PythonExamples/.git/


Note this will generate a .git folder which is where all the version control data will be stored.

On branch master

No commits yet

Untracked files:
(use "git add <file>..." to include in what will be committed)
.vscode/
__init__.py
__pycache__/
square.py

nothing added to commit but untracked files present (use "git add" to track)


We are going to add some files to this folder, to start with we will create some empty files and then add them to the version control system.

touch README.md .gitignore
git status
On branch main

No commits yet

Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
square.py
nothing added to commit but untracked files present (use "git add" to track)


To add these files to version control we use the command

git add README.md .gitignore
git status
On branch master

No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file:   .gitignore

Untracked files:
(use "git add <file>..." to include in what will be committed)
.vscode/
__pycache__/
square.py


The README.md file is used as the front page of the Github webpage, adding one to any folder will also make that a front page. It is written in a syntax called Markdown and in particular GitHub has it’s own version. Markdown can also be used in jupyter notebooks for text so it is well worth learning the basic syntax.

# This is a title
Normal text is here
## Subtitle
This is a subtitle, for the rest of the examples I will edit the file, as this webpage is written in markdown so embedding is harder :-)


.gitignore

This file is used by git to ignore certain types of file when adding, these are usually language / project specific, however we can add global setting as well.

For ease I usually copy a pre-prepared one from other projects. The contents of which is shown below. GitHub also offers pre-prepared templates here of which the one below is modified from.

# vs code project setup files
.vscode
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*\$py.class
.vscode/
# stubgen files and MyPY
out/
.mypy_cache/
# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
.pytest_cache/
docs/build
# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Cython debug symbols
cython_debug/



committing

To take a snapshot of the code we use the git commit command. Typically we do this once a significant part of the work, or tests have been completed. There is no real set time to do this but it helps to do this frequently to be able to revert to older versions etc.

All commits usually have a commit message there is an art to writing good commit messages and some companies have style guides on how to do these. In general make them describe what has been done, what bugs are fixed etc.

git commit -am "added first programs to git"
[master (root-commit) 17476a0] added first programs to git
4 files changed, 88 insertions(+)
create mode 100644 .gitignore
create mode 100644 __init__.py
create mode 100755 square.py


We are now going to add some code to the square.py file.

def square(t, start_x,start_y,width=10):
t.penup()
t.goto(start_x,start_y)
t.pendown()
for i in range(4):
t.forward(width)
t.right(90)


 git commit -am "update the square function to add position"
[master 0ec50d6] update the square function to add position
1 file changed, 5 insertions(+), 2 deletions(-)



I’m now going to add some garbage to the file and save. We can now run git diff

git diff
diff --git a/square.py b/square.py
index acfc6c2..48d2fc4 100755
--- a/square.py
+++ b/square.py
@@ -11,7 +11,7 @@ def square(t, start_x, start_y, width=10):
t.forward(width)
t.right(90)

-
if __name__ == "__main__":
my_turtle = turtle.Turtle()
square(my_turtle, 200, 100, 100)


If we just want to get back the original file, we can use the git checkout function

git checkout square.py
Updated 1 path from the index


reverting a commit

I’m going to add a new function to the file

def totally_broken_function() :
print "this is broken"


I will then commit it

git commit -am "added a new function"
[main 8baf6a0] added a new function
1 file changed, 3 insertions(+)


As this function is broken we may want to revert back to the previous commit

git log
Author: jmacey <jmacey@bournemouth.ac.uk>
Date:   Wed Sep 14 14:14:31 2022 +0100

commit 2fd7e9c27c92e5de871deebf260cec6a257281aa
Author: jmacey <jmacey@bournemouth.ac.uk>
Date:   Wed Sep 14 14:06:31 2022 +0100

working demo framework

commit d9d3d7389c7800d9f2c17e99d945eaf0311e689c
Author: jmacey <jmacey@bournemouth.ac.uk>
Date:   Wed Sep 14 12:42:04 2022 +0100

initial commit of README.md and .gitignore file


The log shows us the full history, you will see a hash we should be able to use this to go back to a previous snapshot.

git revert [hash]


Branching and merging

The typical workflow when using git is to create a new branch when adding / testing new stuff. We can create a new branch then check it out as follows

git branch filledsquare
git checkout filledsquare
Switched to branch 'filledsquare'


I’m now going to modify the code and add some new stuff.

def square(t, start_x, start_y, width=10,colour="red"):
t.penup()
t.color(colour,colour)
t.goto(start_x, start_y)
t.pendown()
t.begin_fill()
for i in range(4):
t.forward(width)
t.right(90)
t.end_fill()


git commit -am "added some new functionality"
[newfunctions 19ec737] added some new functionality
1 file changed, 5 insertions(+), 3 deletions(-)
mode change 100644 => 100755 demo.py


Now we have done this we can re-checkout the main branch.

git checkout master
Switched to branch 'master'


Typically if this worked we could now merge this into the main branch.

git merge filledsquare
Updating 0ec50d6..6c567ff
Fast-forward
square.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)



Typically we now commit this to the main branch.

Using GitHub

At present all of this work is on the local machine. By using GitHub we can upload our work to a repository. The easiest way to do this is by creating a new repo in the web interface, then add this as the origin. The instructions are on the webpage and I will do this as a practical example in the lab.

Exercises

The following examples will help us to understand more of the maths module and try to implement some simple algorithms. It is important to understand that most computers deal with cos,sin and tan functions in radians so we may need to convert from radians to degrees and back.

To do this we can use the following

math.degrees(0.234)


To find the inverse of the cos and sin we can use the following.

math.acos(math.radians(45))


Using the square.py example create a new file called ellipse.py and write a function called ellipse().

It’s signature will be as follows

def ellipse(t : Turtle, start_x : int, start_y : int, r1 : int r2 : int, step : int = 15) :


The algorithm to draw an ellipse can be implemented with the parametric function as follows.

1. move to the start x,y (pen up)
2. calculate the new x,y position for theta = 0 -> 360 in steps x
x=r1 * cos(theta)
y=r2 * sin(theta)

1. goto(x,y)

Using the previous example, create a new file called polygon.py

In this write the following functions and implement the algorithms.

1. an polygon function, this will draw an n sided polygon of lenght l
def polygon(t : Turtle, start_x : int , start_y : int , sides : int =3, length: int=10):

1. Using the SSS triangle formula discussed here write a triangle function that takes in three lengths a,b,c and draws a triangle.
def triangle(t : Turtle, start_x : int , start_y : int, a : int , b : int , c: int)


Try to add a rotation parameter to each of the drawing routines. This should apply a 2D rotation to the x,y co-ordinate pairs being drawn.

The equations to do this is

x= x * cos(theta) - y * sin(theta)
y= x * sin(theta) + y * cos(theta)