Compare commits

..

1 Commits

Author SHA1 Message Date
waldek b9c486bb1b test 2022-04-27 12:02:24 +02:00
41 changed files with 507 additions and 1521 deletions

1
.gitignore vendored
View File

@ -2,4 +2,3 @@
*.pdf *.pdf
*.docx *.docx
.~lock* .~lock*
.idea/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

View File

@ -1,47 +0,0 @@
import subprocess
LINK = "https://github.com/ekalinin/github-markdown-toc"
OUTPUT = "readme.md"
OUTLINE = "outline.md"
INPUT = [
"learning_python3.md",
"introduction_to_solid.md",
"learning_python3_gui.md",
"learning_git.md",
"what_is_next.md",
]
CMD = "gh-md-toc"
FILTER = "(#"
TITLE = "Table of Contents"
if __name__ == "__main__":
try:
p = subprocess.Popen([CMD], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
except Exception as e:
print("please install {}".format(LINK))
exit()
CONTENT = []
for f in INPUT:
p = subprocess.Popen([CMD, f], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate()
output = output.decode().split("\n")
for line in output:
if CMD in line:
continue
if FILTER in line:
line = line.replace(FILTER, "(./{}#".format(f))
if TITLE in line:
title = " ".join(f.replace(".md", "").split("_")).capitalize()
title = title.replace("/", ": ")
line = title
CONTENT.append(line)
p = subprocess.Popen(["cp", OUTLINE, OUTPUT], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.wait()
print("writing")
with open(OUTPUT, "a") as fp:
for line in CONTENT:
fp.write("{}\n".format(line))
print("done...")

View File

@ -1,341 +0,0 @@
# Object-Oriented class design
## SOLID
SOLID coding is a principle created by Robert C.Martin, he is a famous computer scientist.
SOLID is an acronym for his five conventions of coding.
With their conventions, you can improve the structure of your code, reduce time to implement changes and technical debts, etc.
It is a collection of best practices.
And it was developed through this decade.
Principles of SOLID acronym are:
* The Single-Responsibility Principle (**SRP**)
* The Open-Closed Principle (**OCP**)
* The Liskov Substitution Principle (**LSP**)
* The Interface Segregation Principle (**ISP**)
* The Dependency inversion Principle (**DIP**)
The first convention is **SRP**, that means that all classes of your code must do one thing.
That is an important principle.
That is the best way to work with others people in the same project.
Version control is easier,
You will never have _Merge conflicts_, because other people work in other operations.
So, he will never have two same things in the code.
### Single-Responsibility
Let's start something !
We will make common mistake that violate **SRP** and correct them.
Let's code a bookstore invoice.
```python
class Book(object):
def __init__(self, name, authorName, year, price, isbn):
self.name = name
self.authorName = authorName
self.year = year
self.price = price
self.isbn = isbn
```
As you can see, there is a class named Book with some fields.
This fields are public and characterize a book.
OK ! Now we can start the invoice class.
This class will calculate the final price for a customer.
```python
class Invoice(object):
def __init__(self, book, quantity, discountRate, taxRate, total):
self.book = book
self.quantity = quantity
self.discountRate = discountRate
self.taxRate = taxRate
self.total = total
def calculateTotal(self):
self.price = ((self.book.price - self.book.price * self.discountRate)*self.quantity)
self.priceWithTaxes = self.price * (1 + self.taxRate)
return self.priceWithTaxes
def printInvoice(self):
print(self.quantity, "x", self.book.name,"", self.book.price, "$");
print("Discount Rate: ", self.discountRate)
print("Tax Rate: ", self.taxRate)
print("Total: ", self.total)
def saveToFile(self, fileName):
pass
```
Alright, now we have the _Invoice_ class, he had 3 methods (calculateTotal, printInvoice, saveToFile) and some fields too.
Why this code violate the first convention of **SOLID** ?
The _printInvoice_ method violate this one because the **SRP** told us to make just one thing per classes.
Here, our printing logic is in the same class than _calculateTotal_ method.
So, the printing logic is mixed with business logic in the same class.
As you think, the _saveToFile_ method violate this convention too.
Let's correct this example.
```python
class InvoicePrinter(object):
def __init__(self, invoice):
self.invoice = invoice
def printInvoice(self):
print(self.invoice.quantity, "x", self.invoice.book.name,"", self.invoice.book.price, "$");
print("Discount Rate: ", self.invoice.discountRate)
print("Tax Rate: ", self.invoice.taxRate)
print("Total: ", self.invoice.total)
```
```python
class InvoicePersistence(object):
def __init__(self, invoice):
self.invoice = invoice
def saveToFile(self):
pass
```
We have now two others classes, _InvoicePrinter_ class and _InvoicePersistence_ class.
The _InvoicePrinter_ is used to print information.
And the _InvoicePersistence_ is used to save information.
With these three classes, we respect the first principle of **SOLID**.
### Open-Closed Principle
This principle says that classes are open for extension and closed to modification.
Extension mean news functionalities and modification mean modifying your code.
If you want to add new functionalities, you are able to add it without manipulating the existing program.
If you touch the existing code, you have a risk to have news bugs.
So, if you want to add something else, you can use abstract classes and help of interface.
Ok so, let's add new functionality in the _InvoicePersistence_ class.
```python
class InvoicePersistence(object):
def __init__(self, invoice):
self.invoice = invoice
def saveToFile(self):
pass
def saveToDataBase(self):
pass
```
The _saveToDataBase_ method is used to save information in a Data Base.
We have modified the _InvoicePersistence_ class.
And this class will be more difficult to make easily extendable.
So, we violate the **OCP** convention.
If you want to respect this principle, you have to create a new class.
```python
class InvoicePersistence(abc.ABC):
@abstractmethod
def save(self, invoice):
pass
```
The new _InvoicePersistence_ class has an abstract method.
So, if a class inherits the _InvoicePersistence_ class, you have to implement the _save_ method.
And for example, we will create a _DataBasePersistence_ class, and this class inherits the abstract _InvoicePersistence_ class.
```python
class DatabasePersistence(InvoicePersistence):
def __init__(self, invoice):
self.invoice = invoice
self.save(self.invoice)
def save(self, invoice):
print("Save in database ...")
```
Let's do the same thing with _FilePersistence_ class.
```python
class FilePersistence(InvoicePersistence):
def __init__(self, invoice):
self.invoice = invoice
self.save(self.invoice)
def save(self, invoice):
print("Save to file ...")
```
Ok, we do a lot of things, let's make a UML to represent our class structure.
<center>
<img src="./assets/UML_Solid" alt="" width="1000"/>
</center>
That will be more simple to do extension.
### Liskov Substitution Principle
This convention tells us to create a substitutable subclass for their parents.
So, if you want to create an object in the subclass, you have to be able to pass it in the interface.
Let's make an example !
We will write a _Walk_ class and his subclass, _Jump_ class.
```python
class Walk(abc.ABC):
def __init__(self):
abc.ABC.__init__(self)
@abc.abstractmethod
def Speed(self):
pass
@abc.abstractmethod
def Ahead(self):
pass
'''the player walk ahead'''
@abc.abstractmethod
def Behind(self):
pass
'''the player walk behind'''
```
And the _Jump_ subclass.
```python
class Jump(Walk):
def __init__(self):
pass
def Speed(self):
pass
def Ahead(self):
pass
def Behind(self):
pass
```
As you can see, the _Jump_ subclass has all abstract method of _Walk_ class.
But we have a big problem !
The _Jump_ subclass need a new method, the _height_ method.
And he does not need the _Ahead_ and _Behind_ method.
If you remove abstract method and add a new method in _Jump_ subclass, you will be in a big trouble.
This subclass will be different about mother class.
The simple way to resolve this trouble is to create a new mother class for _Walk_ and _Jump_ class.
```python
class Movement(abc.ABC):
def __init__(self):
abc.ABC.__init__(self)
@abc.abstractmethod
def Speed(self):
pass
@abc.abstractmethod
def Animation(self):
pass
@abc.abstractmethod
def Direction(self):
pass
```
The _Movement_ class will be the mother class of _Walk_ and _Jump_ classes.
These subclasses will have three methods (_Speed_, _Animation_ and _Direction_ methods).
The problems that we had is resolved with the _Direction_ method.
With this method, you have choices to go ahead, behind, height, etc.
<details>
<summary>
Subclasses
</summary>
The _Walk_ subclass:
```python
class Walk(Movement):
def __init__(self):
pass
def Speed(self):
pass
def Animation(self):
pass
def Direction(self):
pass
```
And the _Jump_ subclass:
```python
class Jump(Movement):
def __init__(self):
pass
def Speed(self):
pass
def Animation(self):
pass
def Direction(self):
pass
```
</details>
### Interface Segregation Principle
The _Interface Segregation Principle_ means that all interfaces have to be separated.
That means that clients has not functions that they do not need.
OK ! So, let's make an example with client manager.
```python
class GoodCustomer(abc.ABC):
def __init__(self):
abc.ABC.__init__(self)
@abc.abstractmethod
def FirstName(self):
pass
@abc.abstractmethod
def LastName(self):
pass
@abc.abstractmethod
def Address(self):
pass
@abc.abstractmethod
def Work(self):
pass
```
The _GoodCustomer_ mother class is a class where good customer information are saved.
```python
class BadCustomer(GoodCustomer):
def __init__(self):
pass
def FirstName(self):
pass
def LastName(self):
pass
def Address(self):
pass
def Work(self):
pass
```
The _BadCustomer_ subclass is a class where bad customer information are saved.
For this example, we don't want to know the addresses of bad guys.
So what can we do ?
You have to create a new mother class for these two classes like the preceding example.
### Dependency Inversion Principle
This convention says that all classes that we use depends on the interface or abstract classes.
Like precedents example, all methods was abstracts.
>The Dependency Inversion principle states that our classes should depend upon interfaces or abstract classes instead of concrete classes and functions.
>We want our classes to be open to extension, so we have reorganized our dependencies to depend on interfaces instead of concrete classes.

View File

@ -1,306 +0,0 @@
# About
Git is a Version Control System.
It is an advanced open source project.
That is the most popular VCS with his advantage like security, efficiency, speed etc.
He was developed in 2005 by Linus Torvalds, the Linux's father.
Many companies projects using Git (Google, Gnome, Netflix, etc).
In 2016 12 million people were using Git.
All project histories were saved in the work file.
You can publish, with all histories, projects in a Git platform like Gitea, GitHub, Bitbucket, GitLab, etc.
With this tool, you can commit projects, compare projects.
That is the best tool for teamwork.
![git flow](./assets/git-flow.png)
# Git via `bash`
## Initialing a git repo
To initialize an empty git repository, you can tap _**git init**_ in a text editor like Git Bash.
This command will create a git repository in the initial branch **master**.
You will be able to add, commit, push, etc.
```
waldek@metal:~/Documents/my_first_git_repository$ git status
fatal: not a git repository (or any of the parent directories): .git
waldek@metal:~/Documents/my_first_git_repository$ git init .
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint: git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint: git branch -m <name>
Initialized empty Git repository in /home/local/waldek/Documents/my_first_git_repository/.git/
waldek@metal:~/Documents/my_first_git_repository$ git status
On branch master
No commits yet
nothing to commit (create/copy files and use "git add"
```
If you want to see if you are in a git repository, you can use _**git status**_.
He will inform you which branch that you are and how many commits in this repository.
Be careful with commands because you have to be in a folder.
In the preceding example, the new repository was in the "my_first_git_repository" folder.
## What's in this repo
You can see all files of your git repository with the command **_ls_**.
```
waldek@metal:~/Documents/my_first_git_repository$ ls -la
total 12
drwxr-xr-x 3 waldek waldek 4096 May 2 22:06 .
drwxr-xr-x 4 waldek waldek 4096 May 2 22:06 ..
drwxr-xr-x 7 waldek waldek 4096 May 2 22:07 .git
waldek@metal:~/Documents/my_first_git_repository$ ls -la .git/
total 40
drwxr-xr-x 7 waldek waldek 4096 May 2 22:07 .
drwxr-xr-x 3 waldek waldek 4096 May 2 22:06 ..
drwxr-xr-x 2 waldek waldek 4096 May 2 22:06 branches
-rw-r--r-- 1 waldek waldek 92 May 2 22:06 config
-rw-r--r-- 1 waldek waldek 73 May 2 22:06 description
-rw-r--r-- 1 waldek waldek 23 May 2 22:06 HEAD
drwxr-xr-x 2 waldek waldek 4096 May 2 22:06 hooks
drwxr-xr-x 2 waldek waldek 4096 May 2 22:06 info
drwxr-xr-x 4 waldek waldek 4096 May 2 22:06 objects
drwxr-xr-x 4 waldek waldek 4096 May 2 22:06 refs
waldek@metal:~/Documents/my_first_git_repository$
```
## Adding and tracking content
```
waldek@metal:~/Documents/my_first_git_repository$ cat readme.md
# Hello world
I am a paragraph.
I can contain multiple sentences, each on one line.
A blank line separates multiple paragraphs.
1. lists
1. will
1. auto increment in most HTML render engines
* sublists
* are possible
* on multiple
* levels
1. but I keep counting...
[I will become](https://kernel.org) a link.
And I'm a line:
---
waldek@metal:~/Documents/my_first_git_repository$ git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
readme.md
nothing added to commit but untracked files present (use "git add" to track)
waldek@metal:~/Documents/my_first_git_repository$
```
Now we can `add` and `commit` our changes.
```
waldek@metal:~/Documents/my_first_git_repository$ git add readme.md
waldek@metal:~/Documents/my_first_git_repository$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: readme.md
waldek@metal:~/Documents/my_first_git_repository$ git commit -m "adds a readme file"
[master (root-commit) 8278e12] adds a readme file
1 file changed, 22 insertions(+)
create mode 100644 readme.md
waldek@metal:~/Documents/my_first_git_repository$ git status
On branch master
nothing to commit, working tree clean
waldek@metal:~/Documents/my_first_git_repository$
```
We can use `git log` to inspect the list of commits.
Add the `--no-pager` to bypass `less` and print the output straight to the console.
```
waldek@metal:~/Documents/my_first_git_repository$ git --no-pager log
commit 8278e1273d1438920c1063cdca179eb70d5926d8 (HEAD -> master)
Author: waldek <waldek@mailbox.org>
Date: Mon May 2 22:16:50 2022 +0200
adds a readme file
waldek@metal:~/Documents/my_first_git_repository$
```
## The main workflow
1. edit your files
1. `git status` to verify what's new or has been modified
1. `git add` the files you want to commit
1. `git commit -m "A RELEVANT COMMIT MESSAGE"`
1. `git push` to wherever you have your repository
1. repeat step one...
# Git via Atom
# Git via Pycharm
## Starting a version controlled project
In Pycharm, you have to enable a version control integration.
There are many choices, but we will use Git.
![screenhot](./assets/git_pycharm_source_00.png)
![screenhot](./assets/git_pycharm_source_01.png)
As you can see, Git appears in the toolbar and a git window appear bottom of the window.
And you can identify the current branch that you are in.
So the default branch name is **master**.
![screenhot](./assets/git_pycharm_source_02.png)
Let's try to write some code.
And add all contents that we write in a git repository.
![screenhot](./assets/git_pycharm_source_03.png)
In the commit window, we can see which file had changed.
But we had three important things too.
We have a textBox and two buttons.
In the textBox, you can write a comment when you commit.
The _Commit_ button is used to save all change of your project in a git repository.
The _Commit and Push_ is used to commit and push the project in a Git.
![screenhot](./assets/git_pycharm_source_04.png)
When you press the _Commit and Push_, a new window appears.
You can see which branch and which commit that you push.
![screenhot](./assets/git_pycharm_source_05.png)
## Creating an online repository
If you use Gitea and you want to create a new repository.
That is pretty simple, you have to press the button **New Repository**.
![screenhot](./assets/git_pycharm_source_06.png)
And then, you can add the repository name, and others parameters like description, the visibility, etc.
![screenhot](./assets/git_pycharm_source_07.png)
When you have finish, you have to press the button **Create Repository**.
![screenhot](./assets/git_pycharm_source_08.png)
Et voila, you have created a new repository on Gitea and you receive an https link.
This link is used if you want to share your project with someone, or you want to clone this repository.
![screenhot](./assets/git_pycharm_source_09.png)
If you want to work in your repository in Pycharm, you have to clone it.
In the toolbar, you can click on Git and you will find the button **Clone**.
Then, a window will be open, and you have to copy and paste the https link of your repository.
![screenhot](./assets/git_pycharm_source_10.png)
When the clone is successfully does, you can change the project.
And if you have finished your job, you can **Commit and Push**.
If you want to commit and push all changes that you have made, you have to check all changes in the left window.
And then, you can press **Commit and Push**.
![screenhot](./assets/git_pycharm_source_11.png)
In the bottom git window, you can see all commits, and their comments, of this repository.
If you clone a another repository, you will be able to see all commits that people made and their comment.
That is pretty cool !
## Adding some changes to our local code
In Gitea, you can see what the git repository contain and the last comment of commits.
![screenhot](./assets/git_pycharm_source_12.png)
If you check all changes and you want to **Commit and Push**, don't forget the comment.
That is important for a good teamwork.
![screenhot](./assets/git_pycharm_source_13.png)
If you commit twice, you are able to verify how many commits and when commits have done.
![screenhot](./assets/git_pycharm_source_14.png)
<!---
![screenhot](./assets/git_pycharm_source_15.png)
-->
## Cloning the remote project into a new project
In the main menu of PyCharm, you can click in **Get from VCS**.
If you click in it, you have just copy and paste the https link of the git repository.
To arrive at the *startup screen* shown below you need to close all open projects.
![screenhot](./assets/git_pycharm_clone_00.png)
You have to trust the git repository.
![screenhot](./assets/git_pycharm_clone_01.png)
And then, you can go to settings and choice your **Python interpreter**.
If you don't have one, you can add one.
![screenhot](./assets/git_pycharm_clone_02.png)
![screenhot](./assets/git_pycharm_clone_03.png)
If you want to push your local repository, you have to identify with your name and your e-mail.
![screenhot](./assets/git_pycharm_clone_04.png)
Now, we have a new window where you can push yours commits.
![screenhot](./assets/git_pycharm_clone_05.png)
And if you push it, you have to login to Gitea.
![screenhot](./assets/git_pycharm_clone_06.png)
<!---
![screenhot](./assets/git_pycharm_source_16.png)
![screenhot](./assets/git_pycharm_source_17.png)
-->
## Updating the original project
If someone adds something to your git repository, and you want to refresh your git repository.
You can tap to Git in the toolbar and then press **Pull**.
![screenhot](./assets/git_pycharm_source_18.png)
A window will appear to ask you which repository do you want to pull.
![screenhot](./assets/git_pycharm_source_19.png)
Et voila, your git repository is refreshed.
![screenhot](./assets/git_pycharm_source_20.png)

View File

@ -65,22 +65,6 @@ TODO animated overview of the shell and the world of OOP
# Installing pycharm # Installing pycharm
Depending on the platform you use, you can install Pycharm in multiple ways.
The computers in the classroom come with Pycharm installed but if you want to continue working from home you can follow the instructions [here](https://www.jetbrains.com/pycharm/download/#section=windows).
If you run Windows at home you will probably need to install python as well.
Download the latest version from [here](https://www.python.org/downloads/).
On Linux and Mac OSX there will probably be some version of python installed.
From a terminal you can check the version that's installed by executing the following commmands.
```bash
waldek@metal:~$ python3 --version
Python 3.9.2
waldek@metal:~$
```
## Virtual environments
TODO TODO
# Your first project # Your first project
@ -96,10 +80,14 @@ print("Hello World!")
Just for reference below are a few helloworld programs in different languages. Just for reference below are a few helloworld programs in different languages.
First `c#` then `c` then `c++` and last but not least `javascript`. First `c#` then `c` then `c++` and last but not least `javascript`.
### c#
```c# ```c#
Console.WriteLine("Hello World!"); Console.WriteLine("Hello World!");
``` ```
### c
```c ```c
#include <stdio.h> #include <stdio.h>
@ -109,6 +97,8 @@ int main() {
} }
``` ```
### c++
```cpp ```cpp
// Your First C++ Program // Your First C++ Program
@ -120,6 +110,8 @@ int main() {
} }
``` ```
### javascript
```js ```js
alert( 'Hello, world!' ); alert( 'Hello, world!' );
``` ```
@ -217,7 +209,7 @@ But how can we **get** some information from the user?
This is done with the built-in `input` function. This is done with the built-in `input` function.
If we open up a python shell we can observe it's behaviour. If we open up a python shell we can observe it's behaviour.
```python ```python3
>>> input() >>> input()
hello world hello world
'hello world' 'hello world'
@ -436,41 +428,6 @@ What about one that takes floating point numbers?
* [realpython](https://realpython.com/python-conditional-statements/) conditional logic * [realpython](https://realpython.com/python-conditional-statements/) conditional logic
# Coding challenge - Currency converter
I would like you to write a program that converts EUR to DOLLAR.
You should do this in a **new** python file.
I suggest you call it `euro_to_dollar.py` or something that makes sense to you.
The result of this program *could* be as follows.
```bash
➜ python_course_doc git:(master) ✗ python3 test.py
How much EUR would you like to convert into DOLLAR? 140
140 EUR is 159.6 DOLLAR
➜ python_course_doc git:(master) ✗ python3 test.py
How much EUR would you like to convert into DOLLAR? blablabla
That's not a number I understand...
➜ python_course_doc git:(master) ✗
```
<details>
<summary>Spoiler warning</summary>
```python
result = input("How much EUR would you like to convert into DOLLAR? ")
rate = 1.14
if result.isdigit():
eur = int(result)
dollar = eur * rate
print("{} EUR is {} DOLLAR".format(eur, dollar))
else:
print("That's not a number I understand...")
```
</details>
# Coding challenge - Celsius to Fahrenheit converter # Coding challenge - Celsius to Fahrenheit converter
Your first challenge! Your first challenge!
@ -479,7 +436,7 @@ You should do this in a **new** python file.
I suggest you call it `c_to_f.py` or something that makes sense to you. I suggest you call it `c_to_f.py` or something that makes sense to you.
The result of this program *could* be as follows. The result of this program *could* be as follows.
``` ```bash
➜ ~ git:(master) ✗ python3 ex_celcius_to_fahrenheit.py ➜ ~ git:(master) ✗ python3 ex_celcius_to_fahrenheit.py
What's the temperature?30 What's the temperature?30
30°C equals 86.0°F 30°C equals 86.0°F
@ -523,6 +480,42 @@ else:
</details> </details>
# Coding challenge - Currency converter
I would like you to write a program that converts EUR to DOLLAR.
You should do this in a **new** python file.
I suggest you call it `euro_to_dollar.py` or something that makes sense to you.
The result of this program *could* be as follows.
```bash
➜ python_course_doc git:(master) ✗ python3 test.py
How much EUR would you like to convert into DOLLAR? 140
140 EUR is 159.6 DOLLAR
➜ python_course_doc git:(master) ✗ python3 test.py
How much EUR would you like to convert into DOLLAR? blablabla
That's not a number I understand...
➜ python_course_doc git:(master) ✗
```
<details>
<summary>Spoiler warning</summary>
```python
result = input("How much EUR would you like to convert into DOLLAR? ")
rate = 1.14
if result.isdigit():
eur = int(result)
dollar = eur * rate
print("{} EUR is {} DOLLAR".format(eur, dollar))
else:
print("That's not a number I understand...")
```
</details>
## Some links to read up on ## Some links to read up on
* how to convert string to float, Look at the response here: [str.isfloat()](https://stackoverflow.com/questions/736043/checking-if-a-string-can-be-converted-to-float-in-python)? * how to convert string to float, Look at the response here: [str.isfloat()](https://stackoverflow.com/questions/736043/checking-if-a-string-can-be-converted-to-float-in-python)?
@ -1191,17 +1184,19 @@ while counter <= 10:
print("after the loop, counter: {}".format(counter)) print("after the loop, counter: {}".format(counter))
``` ```
<details>
Or with only variables and a more verbose way of incrementing the numbers. <summary>Another example</summary>
```python3 ```python3
first_value = 20 FirstValue = 2
second_value = 0 SecondValue = 0
while first_value > second_value: while FirstValue>SecondValue:
print("second value {} is smaller than {}".format(second_value, first_value)) print("The second value is bigger")
second_value = second_value + 1 SecondValue = SecondValue + 1
``` ```
</details>
Two *extra* things might look new to you here. Two *extra* things might look new to you here.
First the `import time` and `time.sleep(1)`, can you tell me what it does? First the `import time` and `time.sleep(1)`, can you tell me what it does?
Next the `counter += 1` which is called [incrementing](https://stackoverflow.com/questions/1485841/behaviour-of-increment-and-decrement-operators-in-python). Next the `counter += 1` which is called [incrementing](https://stackoverflow.com/questions/1485841/behaviour-of-increment-and-decrement-operators-in-python).
@ -1252,17 +1247,12 @@ print("after the loop, counter: {}".format(counter))
Infinite loops are a cornerstone of modern programming. Infinite loops are a cornerstone of modern programming.
While they might look scary, don't overthink it, you'll get used to them very quickly. While they might look scary, don't overthink it, you'll get used to them very quickly.
Logical procedures can be drawn out with flow charts.
An example can be seen in the image below.
![flowchart while loop](./assets/flowchart_while_01.png)
⛑ **When testing out an infinite loop it's sometimes handy to insert a `time.sleep` in it to slow down the execution a bit so you can wrap your head around what's happening.** ⛑ **When testing out an infinite loop it's sometimes handy to insert a `time.sleep` in it to slow down the execution a bit so you can wrap your head around what's happening.**
🏃 Try it 🏃 Try it
--- ---
Go back to the Celsius to Fahrenheit converter and add a while loop to ensure the user puts in only numbers. Go back to the Celsius to Farenheit converter and add a while loop to ensure the user puts in only numbers.
# Coding challenge - Guess the number # Coding challenge - Guess the number
@ -1322,69 +1312,6 @@ if __name__ == "__main__":
</details> </details>
<details>
<summary> The Object Oriented program of Guess The Number </summary>
```python
import random
class ChoiceNumberInTheRange(object):
def ReturnRandomNumber(self, range):
return random.randint(0,int(range))
class GetTheNumberOfPlayer(object):
def GetNumber(self):
try:
Number = int(input("What is the number in your mind ?"))
return Number
except:
print("I need number !!")
class Right(object):
def Verification(self, right_number, number_guessed):
if right_number == number_guessed:
SayCorrect()
exit()
else:
NotCorrect(right_number, number_guessed)
class NotCorrect(object):
def __init__(self, right_number, number_guessed):
if right_number < number_guessed:
SayBigger()
elif right_number > number_guessed :
SaySmaller()
class SayBigger(object):
def __init__(self):
print("Your number is bigger")
class SaySmaller(object):
def __init__(self):
print("Your number is smaller")
class SayCorrect(object):
def __init__(self):
print("You win")
if __name__ == "__main__":
Range = input("What is the range do you want")
RandomValue = ChoiceNumberInTheRange().ReturnRandomNumber(Range)
while True:
TheGuestResponse = GetTheNumberOfPlayer().GetNumber()
Right().Verification(right_number= RandomValue, number_guessed= TheGuestResponse)
```
All classes have a different task.
As you can see, there are classes for getting a random value in a range value (_ChoiceNumberInTheRange_ class).
Class for checking if the number is right or not (_Right_ class).
Classes for talking with the player if he wins or not.
If player getting the right value, you can use exit() method to stop the program.
</details>
🏃 Try it 🏃 Try it
--- ---
@ -1454,10 +1381,9 @@ if __name__ == "__main__":
</details> </details>
# Logical Operators #Logical Operators
There is three types of logical operators. There is three types of logical operators. All operators returns boolean.
All operators return boolean values, ie `True` or `False`.
| Operator | Result | | Operator | Result |
|-----------------|---------------------------------------------| |-----------------|---------------------------------------------|
@ -1476,15 +1402,13 @@ DealerName = "Paul"
DealerAgreement = True DealerAgreement = True
if CustomerAgreement and DealerAgreement : if CustomerAgreement and DealerAgreement :
print(f"Youpi !!! {CustomerName} and {DealerName} agree") print(f"Youpi !!! {CustomerName} and {DealerName} are agreed ")
else: else:
print(f"Oh no, {CustomerName} and {DealerName} don't agree... ;(") print(f"Oh no {CustomerName} and {DealerName} are disagreed ;( ")
``` ```
As you can guess, Jean and Paul are agreeing to the deal. If I had put 'False' in DealerAgreement boolean, the result will be inverse.
As you can guess, Jean and Paul are agreeing to the deal. Let's show an another example with the `Or` operator.
If I had put `False` in `DealerAgreement` boolean, the result would be reversed.
Let's show another example but this time with the `Or` operator.
````python3 ````python3
def Choice_cold_hot(Temperature): def Choice_cold_hot(Temperature):
@ -1493,18 +1417,13 @@ def Choice_cold_hot(Temperature):
else: else:
print("Let's go to the beach") print("Let's go to the beach")
if __name__ == "__main__": if __name__ == "__main__":
Temperature = int(input("What is the temperature")) Temperature = int(input("What is the temperature"))
Choice_cold_hot(Temperature) Choice_cold_hot(Temperature)
```` ````
Look at this code, if the temperature is smaller than 20° or bigger than 40°, you must stay home and don't go to the beach. So, if I put 35° for temperature, it will say that you should go to the beach.
Look at this code, if the temperature is smaller than 20° or bigger than 40°, you must stay home and don't go to the beach. Let's make an exercise. You have to take the previous code and use the `And` operator.
So, if I put 35° as temperature, it will say that you should go to the beach.
Let's try this out as an exercise!
Reuse the previous code but integrate the `And` operator.
The result should be identical.
<details> <details>
<summary>Spoiler warning</summary> <summary>Spoiler warning</summary>
@ -1515,15 +1434,13 @@ def Choice_cold_hot(Temperature):
print("Let's go to the beach") print("Let's go to the beach")
else: else:
print("Don't go outside") print("Don't go outside")
if __name__ == "__main__": if __name__ == "__main__":
Temperature = int(input("What is the temperature")) Temperature = int(input("What is the temperature"))
Choice_cold_hot(Temperature) Choice_cold_hot(Temperature)
```` ````
</details> </details>
Now that we have discovered the base operators we can learn the last logical operator `not` which gives us the reverse of the expected result. Now, we have used that operators, we can use the last logical operator. The `Not` operator sends the reverse of the result.
````python3 ````python3
if __name__ == "__main__": if __name__ == "__main__":
@ -1533,9 +1450,7 @@ if __name__ == "__main__":
else: else:
print("That will be expensive") print("That will be expensive")
```` ````
In this example, if you tap yes, the result will be reversed. In this example, if you tap yes, the result will be reversed.
# Lists # Lists
The different built-in objects we've seen until now, such as `str` and `int` are simple [text](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str) and [numeric](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex) types. The different built-in objects we've seen until now, such as `str` and `int` are simple [text](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str) and [numeric](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex) types.

View File

@ -1,138 +1,138 @@
# tkinter # tkinter
need to document
## Tkinter helloworld ## Tkinter helloworld
The absolute most basic way to have a *hello world* GUI program up and running with Tkinter is the following. The absolute most basic way to have a *hello world* GUI program up and running with Tkinter is the following.
It creates an *application*, sets the *title* as `hello world` and enters the **main eventloop**.
```python ```python
import tkinter as tk from tkinter import *
root = tk.Tk() root = Tk()
root.title("hello world")
MyLabel = Label(root,text="hello world")
MyLabel.pack()
root.mainloop() root.mainloop()
``` ```
Tkinter have two popular architectures, the Tcl and Tk. This two architectures are different, they have their own functionality and their own official documentation.
We are gonna use the Tk architecture.
Tkinter has two popular architectures, Tcl and Tk. The Tk architecture is used to create a GUI widget. He adds a lot of custom commands, so the widget is very customizable.
These two architectures are quite different as they each have their own functionality and their own official documentation.
We are going use the Tk architecture.
The Tk architecture is used to create a GUI widget which has a lot of **methods** and **attributes** so it's quite customizable. In the previous code,the `mainloop()` instruction allows us to open the main window and to not close immediately the window.
And then, the `Label()` method creates label in a layout. This method has many parameters to customize a label. The instruction `pack()` will be always call , this instruction can
In the previous code,the `mainloop()` method allows us to open the main window and to not close the window immediately . add and adjust a widget.
This **event loop** will process all mouse event, button clicks, and changes.
While it works we know better by now. While it works we know better by now.
We should include the `if __name__ == "__main__"` statement so let's do this! We should include the `if __name__ == "__main__"` statement.
```python ```python
import tkinter as tk from tkinter import *
if __name__ == "__main__": if __name__ == "__main__":
root = tk.Tk() root = Tk()
root.title("hello world")
MyLabel = Label(root,text="hello world")
MyLabel.pack()
root.mainloop() root.mainloop()
``` ```
The instance of `tk.Tk()` is what will actually process our event loop and the `root` is the content of our window. We can customize `root` with instruction like geometry, title, etc. The instance of `Tk()` is what will actually process our event loop and the `root` is the content of our window. We can customize `root` with instruction like geometry, title,etc.
To this instance we can attach other widgets and display them. In the latter we will create our button and labels so should create our own class and inherit from it.
```python ```python
import tkinter as tk from tkinter import *
class MainWindow(Frame):
class Application(tk.Tk):
def __init__(self): def __init__(self):
tk.Tk.__init__(self) Label.__init__(self, text="hello world")
self.title("hello world") self.pack()
self.geometry("500x400")
if __name__ == "__main__": if __name__ == "__main__":
app = Application() root = Tk()
app.mainloop() root.title("title of the window")
root.geometry("500x300")
MainWindow()
root.mainloop()
``` ```
## Adding widgets We can add content to the *frame*, such as labels, input boxes and buttons as follows.
We can add content to this window, such as labels, input boxes and buttons as follows.
```python ```python
import tkinter as tk from tkinter import *
class MainWindow(Frame):
class Application(tk.Tk):
def __init__(self): def __init__(self):
tk.Tk.__init__(self) Label.__init__(self, text="hello world")
self.title("hello world") #Label
self.geometry("500x400") MyLabel = Label(self, text="This is a label")
self.resizable(0, 0) MyLabel.pack()
self.label = tk.Label(self, text="This is a label", bg="yellow")
self.label.pack()
self.config(bg="yellow")
self.pack()
if __name__ == "__main__": if __name__ == "__main__":
app = Application() root = Tk()
app.mainloop() root.title("title of the window")
root.geometry("500x300")
MainWindow()
root.mainloop()
``` ```
Let's try to put multiple visual object into the same window. Let's try to put multiple visual object into the same frame.
```python3 ```python3
import tkinter as tk from tkinter import *
class MainWindow(Frame):
class Application(tk.Tk):
def __init__(self): def __init__(self):
tk.Tk.__init__(self) Label.__init__(self, text="hello world")
self.title("hello world") #Label
self.geometry("500x400") MyLabel = Label(self, text="This is a label")
self.label = tk.Label(text="This is a label") MyLabel.pack()
self.label.pack() #Button
MyButton = Button(self, text="I'm clickable!")
self.button = tk.Button(text="I'm clickable!") MyButton.pack()
self.button.pack()
self.config(bg="yellow")
self.output = tk.Label(text="I'm a second label") self.pack()
self.output.pack()
if __name__ == "__main__": if __name__ == "__main__":
app = Application() root = Tk()
app.mainloop() root.title("title of the window")
root.geometry("500x300")
MainWindow()
root.mainloop()
``` ```
You see how they are *stacked* one on top of the other? You see how they are *stacked* one on top of the other?
We can overcome this problem with parameters of [pack()](https://wxpython.org/Phoenix/docs/html/sizers_overview.html) or we can use other geometry managers like [grid()](https://www.pythontutorial.net/tkinter/tkinter-grid/) and [place()](https://www.pythontutorial.net/tkinter/tkinter-place/). We can overcome this problem with parameters of [pack()](https://wxpython.org/Phoenix/docs/html/sizers_overview.html).
We can also use other geometry managers like [grid()](https://www.pythontutorial.net/tkinter/tkinter-grid/) and [place()](https://www.pythontutorial.net/tkinter/tkinter-place/).
```python ```python
import tkinter as tk from tkinter import *
class MainWindow(Frame):
class Application(tk.Tk):
def __init__(self): def __init__(self):
tk.Tk.__init__(self) Frame.__init__(self, master=None)
self.title("hello world") MyPanel = PanedWindow.__init__(self, bg="Blue")
self.geometry("500x400") #Label
self.resizable(0, 0) MyLabel = Label(MyPanel, text="this is a label", bg= "yellow")
self.label = tk.Label(self, text="This is a label", bg="yellow") MyLabel.pack(fill="x")
self.label.grid(column=0, row=0, sticky=tk.W, padx=5, pady=5) #Bouton
MyButton = Button(MyPanel, text="I'm clickable!")
self.button = tk.Button(self, text="I'm clickable!") MyButton.place(x=10, y=50)
self.button.grid(column=1, row=0, sticky=tk.E, padx=5, pady=5) self.pack(fill=BOTH,expand=True)
self.output = tk.Label(self, text="I'm a second label")
self.output.grid(column=1, row=1, sticky=tk.E, padx=5, pady=5)
self.config(bg="yellow")
if __name__ == "__main__": if __name__ == "__main__":
app = Application() root = Tk()
app.mainloop() root.title("this is the title of the window")
root.geometry("500x300")
win = MainWindow()
root.mainloop()
``` ```
This is looking better! This is looking better!
@ -154,279 +154,154 @@ Each time we click the button, that method will be called.
Because it is a *method* it has access to *self* so it can modify *anything* within the scope of the instance. Because it is a *method* it has access to *self* so it can modify *anything* within the scope of the instance.
```python ```python
import tkinter as tk from tkinter import *
class MainWindow(Frame):
def __init__(self):
Frame.__init__(self, master=None)
MyPanel = PanedWindow.__init__(self, bg="Blue")
#Label
MyLabel = Label(MyPanel, text="this is a label", bg= "yellow")
MyLabel.pack(fill="x")
#Bouton
MyButton = Button(MyPanel, text="I'm clickable!", command=lambda : self.ButtonEnable(MyLabel))
MyButton.place(x=10, y=50)
self.pack(fill=BOTH,expand=True)
def ButtonEnable(self, Label):
global number
number += 1
counter = "You have press the button {} time".format(number)
Label.config(text=counter)
class Application(tk.Tk): number=0
def __init__(self):
tk.Tk.__init__(self)
self.title("hello world")
self.geometry("500x400")
self.resizable(0, 0)
self.label = tk.Label(self, text="This is a label", bg="yellow")
self.label.pack()
self.button = tk.Button(self, text="I'm clickable!", command=self.clicked) if __name__ == "__main__":
self.button.pack() root = Tk()
root.title("this is the title of the window")
root.geometry("500x300")
win = MainWindow()
root.mainloop()
self.output = tk.Label(self, text="I'm a second label")
self.output.pack()
self.config(bg="yellow") ```
def clicked(self): We can use the same *idea* to grab input from the textbox.
print("hello world!")
```python
from tkinter import *
class MainWindow(Frame):
def __init__(self):
Frame.__init__(self, master=None)
MyPanel = PanedWindow.__init__(self, bg="Blue")
#Label
MyLabel = Label(MyPanel, text="this is a label", bg= "yellow")
MyLabel.pack(fill="x")
#TextBox
MyEntry = Entry(MyPanel)
MyEntry.place(x=200,y=50)
#Bouton
MyButton = Button(MyPanel, text="I'm clickable!", command=lambda : self.ButtonEnable(MyLabel,MyEntry))
MyButton.place(x=10, y=50)
self.pack(fill=BOTH,expand=True)
def ButtonEnable(self, MyLabel, MyEntry):
MyText = MyEntry.get()
MyLabel.config(text=MyText)
if __name__ == "__main__": if __name__ == "__main__":
app = Application() root = Tk()
app.mainloop() root.title("this is the title of the window")
root.geometry("500x300")
win = MainWindow()
root.mainloop()
``` ```
We can see *hello world!* printed to the console with each click! ## Tkinter guess the number
As we're using a `class` we have *access* to the instance of the application so we can use the `self.output` label to show our messages.
```python ```python
import tkinter as tk import time
from tkinter import *
class Application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("hello world")
self.geometry("500x400")
self.resizable(0, 0)
self.label = tk.Label(self, text="This is a label", bg="yellow")
self.label.pack()
self.button = tk.Button(self, text="I'm clickable!", command=self.clicked)
self.button.pack()
self.output = tk.Label(self, text="I'm a second label")
self.output.pack()
self.config(bg="yellow")
self.counter = 0
def clicked(self):
self.counter += 1
self.output.config(text="butter has been clicked {} times".format(self.counter))
if __name__ == "__main__":
app = Application()
app.mainloop()
```
🏃 Try it
---
We can use the same *idea* to grab input from the input boxes called `tk.Entry`.
The code below *adds* two numbers but it's not working properly.
Can you fix it for me please?
```python
import tkinter as tk
class Application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("hello world")
self.geometry("500x400")
self.resizable(0, 0)
self.label_one = tk.Label(self, text="Number 1:", bg="yellow")
self.label_one.grid(column=0, row=0, sticky=tk.W, padx=5, pady=5)
self.label_two = tk.Label(self, text="Number 2:", bg="yellow")
self.label_two.grid(column=0, row=1, sticky=tk.W, padx=5, pady=5)
self.input_one = tk.Entry(self)
self.input_one.grid(column=1, row=0, sticky=tk.W, padx=5, pady=5)
self.input_two = tk.Entry(self)
self.input_two.grid(column=1, row=1, sticky=tk.W, padx=5, pady=5)
self.button = tk.Button(self, text="add two numbers", command=self.clicked)
self.button.grid(column=0, row=3, sticky=tk.W, padx=5, pady=5)
self.output = tk.Label(self, text="...")
self.output.grid(column=1, row=3, sticky=tk.W, padx=5, pady=5)
self.config(bg="yellow")
def clicked(self):
number_one = self.input_one.get()
number_two = self.input_two.get()
total = number_one + number_two
self.output.config(text="sum: {}".format(total))
if __name__ == "__main__":
app = Application()
app.mainloop()
```
# Coding challenge - Guess the number
Can you code me a *guess the number* game but using `tkinter`?
<details>
<summary>Spoiler warning</summary>
```python
import tkinter as tk
import random import random
class MainWindow(Frame):
class Application(tk.Tk):
def __init__(self): def __init__(self):
tk.Tk.__init__(self) Frame.__init__(self, master=None, bg="white")
self.title("hello world") MyPanel = PanedWindow.__init__(self)
self.geometry("500x400")
self.resizable(0, 0)
self.number = random.randint(0, 10) MyNumber = random.randint(0, 100)
self.header = tk.Label(text="I have a number in mind...") #Label
self.header.grid(column=0, row=0, sticky=tk.W) self.MyLabel = Label(MyPanel, text="I have a number in mind...", bg= "blue")
self.MyLabel.pack(fill="x", ipadx=25, ipady=20)
#TextBox
MyEntry = Entry(MyPanel)
MyEntry.place(x=200,y=90)
#Bouton
MyButton = Button(MyPanel, text="I'm clickable!", command=lambda : self.ButtonEnable(MyEntry, MyNumber))
MyButton.place(x=10, y=90)
self.question = tk.Label(self, text="What's your guess?") self.pack(fill=BOTH,expand=True)
self.question.grid(column=0, row=1, sticky=tk.W)
self.input = tk.Entry(self)
self.input.grid(column=1, row=1, sticky=tk.W)
self.button = tk.Button(self, text="check", command=self.clicked) def ButtonEnable(self, MyEntry, MyNumber):
self.button.grid(column=0, row=2, sticky=tk.W) if self.IsCorrect(MyEntry.get()):
number = int(MyEntry.get())
self.output = tk.Label(self, text="...") if number != MyNumber:
self.output.grid(column=1, row=2, sticky=tk.W) self.GameOver(number, MyNumber)
else:
def _is_entry_digit(self): self.Win()
number = self.input.get()
if number.isdigit():
number = int(number)
return number
def clicked(self):
number = self._is_entry_digit()
if not number:
msg = "numbers please..."
else: else:
if number < self.number: self.MyLabel.config(text="I need numbers!")
msg = "my number is bigger"
elif number > self.number: def GameOver(self, number, MyNumber):
msg = "my number is smaller" if number > MyNumber:
elif number == self.number: self.MyLabel.config(text="My number is smaller")
msg = "bingo!" else:
self.output.config(text=msg) self.MyLabel.config(text="My number is bigger")
def Win(self):
self.MyLabel.config(text="You WIN!")
def IsCorrect(self, MyEntry):
x = str(MyEntry)
if x.isdigit() == True:
return True
else:
return False
if __name__ == "__main__": if __name__ == "__main__":
app = Application() root = Tk()
app.mainloop() root.title("Guess the number")
``` root.geometry("500x300")
</details> win = MainWindow()
root.mainloop()
<details>
<summary>With replay function</summary>
```python
import tkinter as tk
import random
class Application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("hello world")
self.geometry("500x400")
self.resizable(0, 0)
self.header = tk.Label(text="I have a number in mind...")
self.header.grid(column=0, row=0, sticky=tk.W)
self.question = tk.Label(self, text="What's your guess?")
self.question.grid(column=0, row=1, sticky=tk.W)
self.input = tk.Entry(self)
self.input.grid(column=1, row=1, sticky=tk.W)
self.button = tk.Button(self, text="check", command=self.clicked)
self.button.grid(column=0, row=2, sticky=tk.W)
self.output = tk.Label(self, text="...")
self.output.grid(column=1, row=2, sticky=tk.W)
self.init_new_game()
def _is_entry_digit(self):
number = self.input.get()
if number.isdigit():
number = int(number)
return number
def compare_numbers(self):
number = self._is_entry_digit()
if not number:
msg = "numbers please..."
else:
if number < self.number:
msg = "my number is bigger"
elif number > self.number:
msg = "my number is smaller"
elif number == self.number:
msg = "bingo!"
self.won = True
self.button.config(text="play again!")
self.output.config(text=msg)
def init_new_game(self):
self.won = False
self.number = random.randint(0, 100)
print(self.number)
self.button.config(text="check")
self.output.config(text="")
self.input.delete(0, 'end')
def clicked(self):
if self.won:
self.init_new_game()
else:
self.compare_numbers()
if __name__ == "__main__":
app = Application()
app.mainloop()
``` ```
</detail>
## MVC design pattern ## MVC design pattern
A simple console only MVC. A simple console only MVC.
We'll add the GUI view in a bit. We'll add the GUI view in a bit.
```python ```python
import tkinter as tk from tkinter import *
class ConsoleView(object): class ConsoleView(object):
"""A view for console.""" """A view for console."""
def __init__(self, controller):
self.controller = controller
def select_task(self): def select_task(self):
"""Asks which index to look up.""" """Asks which index to look up."""
idx = input("which task do you want to see? ") idx = input("which task do you want to see? ")
return idx return idx
def show_message(self, msg):
print("MSG: {}".format(msg))
def show(self, task): def show(self, task):
"""Displays the task to the console. This method is called from the """Displays the task to the console. This method is called from the
controller.""" controller."""
@ -436,125 +311,195 @@ class ConsoleView(object):
"""Prints error messages coming from the controller.""" """Prints error messages coming from the controller."""
print("error: {}".format(msg)) print("error: {}".format(msg))
def mainloop(self):
"""Super simple event loop."""
while True:
self.controller.request_number_of_tasks_from_model()
try:
idx = self.select_task()
idx = int(idx)
except Exception as e:
self.error(e)
continue
self.controller.request_task_from_model(idx)
class Model(object): class Model(object):
"""The model houses add data and should implement all methods related to """The model houses add data and should implement all methods related to
adding, modifying and deleting tasks.""" adding, modifying and deleting tasks."""
def __init__(self): db = ["go to the shops", "dryhop beer", "drop of motorbike"]
self.db = ["go to the shops", "dryhop beer", "drop of motorbike"]
def __len__(self):
return len(self.db)
def get_task(self, idx): def get_task(self, idx):
"""Performs a task lookun into the database and returns it when found. """Performs a task lookun into the database and returns it when found.
If no task is found, it returns an error message that will be displayed If no task is found, it returns an error message that will be displayed
in the view (via the controller).""" in the view (via the controller)."""
try: try:
task = self.db[idx] task = Model.db[idx]
except IndexError: except IndexError:
task = None task = "task with {} not found!".format(idx)
return task return task
class Controller(object): class Controller(object):
"""Binds the model and the view together.""" """Binds the model and the view together."""
def __init__(self, model): def __init__(self, view):
self.model = model self.model = Model()
self.view = view
def set_view(self, view): def run(self):
self.view = view """The controller's main function. Depending on what type of view is
if isinstance(view, TkinterView): selected, a different event loop is setup. Do note that the ConsoleView
self.request_number_of_tasks_from_model() is not a real event loop, just a basic flow of action."""
if self.view is ConsoleView:
self.view = self.view()
self._run_console_view()
elif self.view is TkinterView:
root = Tk()
root.title("Task Manager")
root.geometry("500x300")
self.view = self.view()
self.view._set_controller(self)
root.mainloop()
def request_number_of_tasks_from_model(self): def get_task_from_model(self, idx):
number = len(self.model) """Needed for the TkinterView to communicate with the controller."""
self.view.show_message("you have {} tasks".format(number))
def request_task_from_model(self, idx):
"""Needed for the ConsoleView and TkinterView to communicate with the controller."""
task = self.model.get_task(idx) task = self.model.get_task(idx)
if task is None: self.view.show_task(task)
self.view.error("task not found!")
else: def _run_console_view(self):
"""Super simple event loop."""
while True:
try:
idx = self.view.select_task()
idx = int(idx)
except Exception as e:
self.view.error(e)
continue
task = self.model.get_task(idx)
self.view.show(task) self.view.show(task)
if __name__ == "__main__": if __name__ == "__main__":
model = Model() view = ConsoleView
controller = Controller(model) app = Controller(view)
view = ConsoleView(controller) app.run()
controller.set_view(view)
view.mainloop()
``` ```
Below you can see a `tkinter` class that acts as a drop in replacement for the `ConsoleView` class. And now with the implemented `TkinterView` class.
```python ```python
class TkinterView(tk.Tk): from tkinter import *
def __init__(self, controller):
tk.Tk.__init__(self)
self.controller = controller
self.title("hello world")
self.geometry("500x400")
self.resizable(0, 0)
self.header = tk.Label() class ConsoleView(object):
self.header.grid(column=0, row=0, sticky=tk.W) """A view for console."""
self.question = tk.Label(self, text="Which task do you want to see?") def select_task(self):
self.question.grid(column=0, row=1, sticky=tk.W) """Asks which index to look up."""
idx = input("which task do you want to see? ")
self.input = tk.Entry(self) return idx
self.input.grid(column=1, row=1, sticky=tk.W)
self.button = tk.Button(self, text="lookup", command=self.clicked)
self.button.grid(column=0, row=2, sticky=tk.W)
self.output = tk.Label(self, text="...")
self.output.grid(column=1, row=2, sticky=tk.W)
def _is_entry_digit(self):
number = self.input.get()
if number.isdigit():
number = int(number)
return number
def clicked(self):
number = self._is_entry_digit()
if not number:
msg = "numbers please..."
else:
msg = self.controller.request_task_from_model(number)
def show_message(self, msg):
self.header.config(text=msg)
def show(self, task): def show(self, task):
"""Displays the task to the console. This method is called from the """Displays the task to the console. This method is called from the
controller.""" controller."""
self.output.config(text=task) print("your task: {}".format(task))
def error(self, msg): def error(self, msg):
"""Prints error messages coming from the controller.""" """Prints error messages coming from the controller."""
self.output.config(text=msg) print("error: {}".format(msg))
```
<!--
class TkinterView(Frame):
"""A view using a wx.Dialog window"""
def __init__(self):
Frame.__init__(self, master=None)
#Panel
self.panel = PanedWindow(self, bg="green")
self.panel.pack(fill=BOTH, expand=True)
#Task Label
self.task = Label(self.panel, text="your task")
self.task.pack(expand=True)
#SpinBox
self.idx = Spinbox(self.panel, from_=0, to=2, wrap=True )
self.idx.pack(side= TOP)
#Button
self.button = Button(self.panel, text="submit", command=lambda : self.select_task())
self.button.pack(ipadx=60, ipady=30)
self.pack(fill=BOTH, expand=True)
def _set_controller(self, controller):
"""Set the controller so the view can communicate it's requests to it
and update it's values too."""
self.controller = controller
def select_task(self):
"""Gets the index to look up in the model and submits the request to
the controller."""
idx = self.idx.get()
self.controller.get_task_from_model(idx)
def show_task(self, task):
"""Updates the visual label in the view with the task. This method is
called from the controller."""
self.task.config(text=task)
class Model(object):
"""The model houses add data and should implement all methods related to
adding, modifying and deleting tasks."""
db = ["go to the shops", "dryhop beer", "drop of motorbike"]
def get_task(self, idx):
"""Performs a task lookun into the database and returns it when found.
If no task is found, it returns an error message that will be displayed
in the view (via the controller)."""
try:
task = Model.db[int(idx)]
except IndexError:
task = "task with {} not found!".format(idx)
return task
class Controller(object):
"""Binds the model and the view together."""
def __init__(self, view):
self.model = Model()
self.view = view
def run(self):
"""The controller's main function. Depending on what type of view is
selected, a different event loop is setup. Do note that the ConsoleView
is not a real event loop, just a basic flow of action."""
if self.view is ConsoleView:
self.view = self.view()
self._run_console_view()
elif self.view is TkinterView:
root = Tk()
root.title("Task Manager")
root.geometry("500x300")
self.view = self.view()
self.view._set_controller(self)
root.mainloop()
def get_task_from_model(self, idx):
"""Needed for the TkinterView to communicate with the controller."""
task = self.model.get_task(idx)
self.view.show_task(task)
def _run_console_view(self):
"""Super simple event loop."""
while True:
try:
idx = self.view.select_task()
idx = int(idx)
except Exception as e:
self.view.error(e)
continue
task = self.model.get_task(idx)
self.view.show(task)
if __name__ == "__main__":
view = TkinterView
app = Controller(view)
app.run()
```
For a GUI only login generator an implementation without MVC could look a bit like this. For a GUI only login generator an implementation without MVC could look a bit like this.
Note that the actual calculation is done inside the window itself. Note that the actual calculation is done inside the window itself.
@ -1021,16 +966,10 @@ if __name__ == "__main__":
app.mainloop() app.mainloop()
```` ````
-->
## Coding challenge - Login generator with GUI ## Coding challenge - Login generator with GUI
TODO
## Coding challenge - Trivial pursuit with GUI ## Coding challenge - Trivial pursuit with GUI
TODO
# WXpython # WXpython
## wxpython helloworld ## wxpython helloworld

View File

@ -1,4 +0,0 @@
# Some links
* [MVC with tkinter](https://www.pythontutorial.net/tkinter/tkinter-mvc/)
* [principles of OOP](https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/)

View File

@ -1,20 +0,0 @@
# Program
| Day | Goal |
|-----|-----------------------------------------|
| 1 | coding challenge: temperature convertor |
| 2 | coding challenge: memento mori |
| 3 | coding challenge: guess the number |
| 4 | coding challenge: ROT13 |
| 5 | |
| 6 | coding challenge: task manager |
| 7 | useful scripting with pathlib |
| 8 | convert login manager to OOP |
| 9 | |
| 10 | |
| 11 | |
| 12 | coding challenge: hangman non-OOP/OOP |
| 13 | introduction to tkinter |
| 14 | |
| 15 | |

288
readme.md
View File

@ -1,186 +1,112 @@
# Program # Python course
| Day | Goal | This repository contains some notes and exercises for learning python3.
|-----|-----------------------------------------|
| 1 | coding challenge: temperature convertor |
| 2 | coding challenge: memento mori |
| 3 | coding challenge: guess the number |
| 4 | coding challenge: ROT13 |
| 5 | |
| 6 | coding challenge: task manager |
| 7 | useful scripting with pathlib |
| 8 | convert login manager to OOP |
| 9 | |
| 10 | |
| 11 | |
| 12 | coding challenge: hangman non-OOP/OOP |
| 13 | introduction to tkinter |
| 14 | |
| 15 | |
## Initiation to programming
Learning python3 * Day 1
================= * What we'll learn
* Concepts
* [What we'll learn](./learning_python3.md#what-well-learn) * Syntax
* [Concepts](./learning_python3.md#concepts) * Tools
* [Syntax](./learning_python3.md#syntax) * Writing code
* [Tools](./learning_python3.md#tools) * Running code
* [Writing code](./learning_python3.md#writing-code) * The python3 shell
* [Running code](./learning_python3.md#running-code) * Installing pycharm
* [The python3 shell](./learning_python3.md#the-python3-shell) * Your first project
* [Installing pycharm](./learning_python3.md#installing-pycharm) * c
* [Virtual environments](./learning_python3.md#virtual-environments) * c
* [Your first project](./learning_python3.md#your-first-project) * c++
* [How to execute](./learning_python3.md#how-to-execute) * javascript
* [Simple printing](./learning_python3.md#simple-printing) * How to execute
* [Try it](./learning_python3.md#-try-it) * Simple printing
* [Try it](./learning_python3.md#-try-it-1) * 🏃 Try it
* [String replacement](./learning_python3.md#string-replacement) * 🏃 Try it
* [String formatting](./learning_python3.md#string-formatting) * String replacement
* [Some links to read up](./learning_python3.md#some-links-to-read-up) * String formatting
* [Taking input](./learning_python3.md#taking-input) * Some links to read up
* [Some functions are blocking](./learning_python3.md#some-functions-are-blocking) * Taking input
* [Functions can return something](./learning_python3.md#functions-can-return-something) * Some functions are blocking
* [Try it](./learning_python3.md#-try-it-2) * Functions can return something
* [Functions can take arguments](./learning_python3.md#functions-can-take-arguments) * 🏃 Try it
* [Try it](./learning_python3.md#-try-it-3) * Functions can take arguments
* [Taking input and evaluation](./learning_python3.md#taking-input-and-evaluation) * 🏃 Try it
* [Conditional logic](./learning_python3.md#conditional-logic) * Taking input and evaluation
* [Class string methods](./learning_python3.md#class-string-methods) * Conditional logic
* [Try it](./learning_python3.md#-try-it-4) * Class string methods
* [Some links to read up on](./learning_python3.md#some-links-to-read-up-on) * Some links to read up on
* [Coding challenge - Currency converter](./learning_python3.md#coding-challenge---currency-converter) * Coding challenge - Celsius to Fahrenheit converter
* [Coding challenge - Celsius to Fahrenheit converter](./learning_python3.md#coding-challenge---celsius-to-fahrenheit-converter) * Day 2
* [Some links to read up on](./learning_python3.md#some-links-to-read-up-on-1) * A text based adventure game
* [A text based adventure game](./learning_python3.md#a-text-based-adventure-game) * Creating your own functions
* [Creating your own functions](./learning_python3.md#creating-your-own-functions) * Functions that *do* something
* [Functions that <em>do</em> something](./learning_python3.md#functions-that-do-something) * Variable scope
* [Variable scope](./learning_python3.md#variable-scope) * Functions that *return* something
* [Functions that <em>return</em> something](./learning_python3.md#functions-that-return-something) * 🏃 Try it
* [Try it](./learning_python3.md#-try-it-5) * Some links to read up on
* [Some links to read up on](./learning_python3.md#some-links-to-read-up-on-2) * Coding challenge - Pretty Print
* [Coding challenge - Pretty Print](./learning_python3.md#coding-challenge---pretty-print) * Using the standard library
* [Using the standard library](./learning_python3.md#using-the-standard-library) * Coding challenge - Memento Mori calculator
* [Coding challenge - Memento Mori calculator](./learning_python3.md#coding-challenge---memento-mori-calculator) * Day 3
* [Writing your first library](./learning_python3.md#writing-your-first-library) * Writing your first library
* [Try it](./learning_python3.md#-try-it-6) * 🏃 Try it
* [How do we write libraries?](./learning_python3.md#how-do-we-write-libraries) * How do we write libraries?
* [What is __name__ == "__main__"?](./learning_python3.md#what-is-__name__--__main__) * What is `__name__ == "__main__"`?
* [Anatomy of a program](./learning_python3.md#anatomy-of-a-program) * Anatomy of a program
* [Try it](./learning_python3.md#-try-it-7) * 🏃 Try it
* [While loop](./learning_python3.md#while-loop) * While loop
* [Try it](./learning_python3.md#-try-it-8) * 🏃 Try it
* [Coding challenge - Guess the number](./learning_python3.md#coding-challenge---guess-the-number) * Coding challenge - Guess the number
* [Try it](./learning_python3.md#-try-it-9) * 🏃 Try it
* [Logical Operators](./learning_python3.md#logical-operators) * Day 4
* [Lists](./learning_python3.md#lists) * Lists
* [Creating lists](./learning_python3.md#creating-lists) * Creating lists
* [List methods](./learning_python3.md#list-methods) * List methods
* [Try it](./learning_python3.md#-try-it-10) * 🏃 Try it
* [Picking elements and slicing lists](./learning_python3.md#picking-elements-and-slicing-lists) * Picking elements and slicing lists
* [Try it](./learning_python3.md#-try-it-11) * 🏃 Try it
* [For loop](./learning_python3.md#for-loop) * For loop
* [Coding challenge - Cheerleader chant](./learning_python3.md#coding-challenge---cheerleader-chant) * Coding challenge - Cheerleader chant
* [Coding challenge - ROT13](./learning_python3.md#coding-challenge---rot13) * Coding challenge - ROT13
* [Try it](./learning_python3.md#-try-it-12) * 🏃 Try it
* [Coding challenge - Christmas Tree](./learning_python3.md#coding-challenge---christmas-tree) * List comprehension
* [List comprehension](./learning_python3.md#list-comprehension) * Day 5
* [Handling files](./learning_python3.md#handling-files) * Handling files
* [Reading from a file](./learning_python3.md#reading-from-a-file) * Reading from a file
* [In-line way](./learning_python3.md#in-line-way) * In-line way
* [Pythonic way](./learning_python3.md#pythonic-way) * Pythonic way
* [Writing to a file](./learning_python3.md#writing-to-a-file) * Writing to a file
* [Coding challenge - Login generator](./learning_python3.md#coding-challenge---login-generator) * Coding challenge - Login generator
* [Dictionaries as data containers](./learning_python3.md#dictionaries-as-data-containers) * Dictionaries as data containers
* [Two dimensional lists](./learning_python3.md#two-dimensional-lists) * Coding challenge - Task manager
* [Dictionaries](./learning_python3.md#dictionaries) * Text based databases
* [Try it](./learning_python3.md#-try-it-13) * Day 6
* [Coding challenge - Task manager](./learning_python3.md#coding-challenge---task-manager) * Now for some useful scripting
* [CSV based task manager](./learning_python3.md#csv-based-task-manager) * Day 7
* [Text based databases](./learning_python3.md#text-based-databases) * Creating our own objects
* [Try it](./learning_python3.md#-try-it-14) * First some *abstract* examples
* [Now for some useful scripting](./learning_python3.md#now-for-some-useful-scripting) * 🏃 Try it
* [Creating our own objects](./learning_python3.md#creating-our-own-objects) * Class inheritance
* [First some <em>abstract</em> examples](./learning_python3.md#first-some-abstract-examples) * 🏃 Try it
* [Try it](./learning_python3.md#-try-it-15) * A *practical* example
* [Magic methods](./learning_python3.md#magic-methods) * Now some *practical* improvements
* [Class inheritance](./learning_python3.md#class-inheritance) * Improve the login generator
* [Try it](./learning_python3.md#-try-it-16) * Improve the task manager
* [Coding challenge - Chalet floor](./learning_python3.md#coding-challenge---chalet-floor) * Day 8
* [Now some <em>practical</em> improvements](./learning_python3.md#now-some-practical-improvements) * Infinite programs
* [Improve the login generator](./learning_python3.md#improve-the-login-generator) * Logic breakdown of a simple game
* [Improve the task manager](./learning_python3.md#improve-the-task-manager) * Trivial pursuit multiple choice game
* [Infinite programs](./learning_python3.md#infinite-programs) * Introduction to the `requests` library
* [Logic breakdown of a simple game](./learning_python3.md#logic-breakdown-of-a-simple-game) * Threading
* [A non object orientated solution](./learning_python3.md#a-non-object-orientated-solution) * Day 9
* [An object orientated solution](./learning_python3.md#an-object-orientated-solution) * GUI programming
* [Trivial pursuit multiple choice game](./learning_python3.md#trivial-pursuit-multiple-choice-game) * Tkinter helloworld
* [Introduction to the requests library](./learning_python3.md#introduction-to-the-requests-library) * Tkinter guess the number
* [Threading](./learning_python3.md#threading) * MVC design pattern
* Day 10-12
* Coding challenge - Login generator with GUI
* Coding challenge - Trivial pursuit with GUI
Introduction to solid
=================
* [Object-Oriented class design](./introduction_to_solid.md#object-oriented-class-design)
* [SOLID](./introduction_to_solid.md#solid)
* [Single-Responsibility](./introduction_to_solid.md#single-responsibility)
* [Open-Closed Principle](./introduction_to_solid.md#open-closed-principle)
* [Liskov Substitution Principle](./introduction_to_solid.md#liskov-substitution-principle)
* [Interface Segregation Principle](./introduction_to_solid.md#interface-segregation-principle)
* [Dependency Inversion Principle](./introduction_to_solid.md#dependency-inversion-principle)
Learning python3 gui
=================
* [tkinter](./learning_python3_gui.md#tkinter)
* [Tkinter helloworld](./learning_python3_gui.md#tkinter-helloworld)
* [Adding widgets](./learning_python3_gui.md#adding-widgets)
* [Try it](./learning_python3_gui.md#-try-it)
* [Coding challenge - Guess the number](./learning_python3_gui.md#coding-challenge---guess-the-number)
* [MVC design pattern](./learning_python3_gui.md#mvc-design-pattern)
* [Coding challenge - Login generator with GUI](./learning_python3_gui.md#coding-challenge---login-generator-with-gui)
* [Coding challenge - Trivial pursuit with GUI](./learning_python3_gui.md#coding-challenge---trivial-pursuit-with-gui)
* [WXpython](./learning_python3_gui.md#wxpython)
* [wxpython helloworld](./learning_python3_gui.md#wxpython-helloworld)
* [wxpython guess the number](./learning_python3_gui.md#wxpython-guess-the-number)
* [MVC design pattern](./learning_python3_gui.md#mvc-design-pattern-1)
* [Coding challenge - Login generator with GUI](./learning_python3_gui.md#coding-challenge---login-generator-with-gui-1)
* [Coding challenge - Trivial pursuit with GUI](./learning_python3_gui.md#coding-challenge---trivial-pursuit-with-gui-1)
Learning git
=================
* [About](./learning_git.md#about)
* [Git via bash](./learning_git.md#git-via-bash)
* [Initialing a git repo](./learning_git.md#initialing-a-git-repo)
* [What's in this repo](./learning_git.md#whats-in-this-repo)
* [Adding and tracking content](./learning_git.md#adding-and-tracking-content)
* [The main workflow](./learning_git.md#the-main-workflow)
* [Git via Atom](./learning_git.md#git-via-atom)
* [Git via Pycharm](./learning_git.md#git-via-pycharm)
* [Starting a version controlled project](./learning_git.md#starting-a-version-controlled-project)
* [Creating an online repository](./learning_git.md#creating-an-online-repository)
* [Adding some changes to our local code](./learning_git.md#adding-some-changes-to-our-local-code)
* [Cloning the remote project into a new project](./learning_git.md#cloning-the-remote-project-into-a-new-project)
* [Updating the original project](./learning_git.md#updating-the-original-project)
What is next
=================
* [Some random recommendations](./what_is_next.md#some-random-recommendations)
* [IDE](./what_is_next.md#ide)
* [Syntax](./what_is_next.md#syntax)
* [Concepts](./what_is_next.md#concepts)
* [Books](./what_is_next.md#books)
* [Linux](./what_is_next.md#linux)
## Linux system administrator
**TODO**

View File

@ -1,75 +0,0 @@
# Some random recommendations
After our twenty days of python together you should have a solid understanding of the world of programming.
The foundation of all essential concepts should be clear enough to understand code when it is in front of you.
A blank slate could still pose a challenge but for that, here are some links and recommendations.
## IDE
Maybe try out some other IDE to see if you like the workflow or layout better.
I'm a big fan of [vim](https://en.wikipedia.org/wiki/Vim_(text_editor)), especially with some good [plugins](https://gitea.86thumbs.net/waldek/linux_course_doc/src/branch/master/modules/qualifying/learning_vim_configuration.md).
It's not an *easy* editor to learn but the payoff is pretty great.
For the more *normal* people I would recommend having a look at the following editors, in no particular order.
* [atom](https://atom.io/)
* [vscode](https://code.visualstudio.com/)
* [brackets](https://brackets.io/)
* [spyder](https://www.spyder-ide.org/) for a more scientific approach
Don't hop around too much though.
If you're a bit bored with pycharm it could be refreshing to *see* your code through a different window.
## Syntax
Start paying attention to the style guides that pop up in your IDE and follow up on them by reading the actual rules.
It can be good way to discover some do's and don't and help you to make your code more readable.
I would also recommend you start browsing online for some coding projects in a field of your interests.
The bigger the project, the higher the chance it follows a specific style.
By reading and trying to understand their code, you'll start to build a better feel how to annotate, structure and make your code more readable.
As this is a *very* subjective topic I can only point you to projects I like.
* [python-language-server](https://github.com/palantir/python-language-server)
* [python-osc](https://github.com/attwad/python-osc)
* [urwid](https://urwid.org/index.html)
* [natural language toolkit](https://www.nltk.org/)
## Concepts
We touched on *most* of the elementary concepts but we did go over them quite quickly.
Take your time to review all base concepts with small exercises and try to chain them together into bigger projects.
Some python specific *new* concepts I would highly advise you to look into are the following.
* [decorators](https://realpython.com/primer-on-python-decorators/)
* [getters and setters](https://stackoverflow.com/questions/2627002/whats-the-pythonic-way-to-use-getters-and-setters)
* [concurrency](https://realpython.com/python-concurrency/)
* [threading vs async vs multiprocessing](https://leimao.github.io/blog/Python-Concurrency-High-Level/)
* [design patterns](https://python-patterns.guide/)
* [memory management in python](https://realpython.com/python-memory-management/)
* [virtual environments](https://docs.python.org/3/tutorial/venv.html)
If you're interested in creating web applications I would start out with [bottle](https://bottlepy.org/docs/dev/), work your way up to [flask](https://www.fullstackpython.com/flask.html) and maybe move over to [django](https://www.djangoproject.com/).
You'll definitely need to learn [SQL](https://en.wikipedia.org/wiki/SQL) at some point but you can make your life easier by using [sqlalchemy](https://www.sqlalchemy.org/).
If you want to dive deep into data science you'll need get comfortable with [numpy](https://numpy.org/), [pandas](https://pandas.pydata.org/) and [matplotlib](https://matplotlib.org/).
The easiest way to get comfortable with all of those is probably [jupyter notebook](https://jupyter-notebook.readthedocs.io/en/stable/index.html) which is an IDE that you use in your browser.
It's quite different from the pycharm you're used to but it's a very good tool to do data analysis.
If robotics is your thing you might want to give [micropython](https://micropython.org/) a go.
I've never used it myself but I've heard good things about it.
## Books
These are some books I've actually read and enjoyed.
* [mastering python design patterns](https://www.amazon.de/-/en/Kamon-Ayeva/dp/1788837487/ref=sr_1_2?keywords=python+design+muster&qid=1651784267&sprefix=python+des%2Caps%2C82&sr=8-2)
* [fluent python](https://www.amazon.de/-/en/Luciano-Ramalho-dp-1492056359/dp/1492056359/ref=dp_ob_title_bk)
* [design patterns](https://www.amazon.de/-/en/Erich-Gamma/dp/9332555400/ref=sr_1_9?keywords=gang+of+four+design+muster&qid=1651784413&sprefix=gang+of+%2Caps%2C68&sr=8-9)
* [learn python the hard way](https://learnpythonthehardway.org/python3/)
* [automate the boring stuff](https://automatetheboringstuff.com/)
# Linux
If you enjoyed the experience of the computers at the school you should give [linux](https://en.wikipedia.org/wiki/Linux) a go at home!
You used a [debian](https://www.debian.org/) machine with [GNOME](https://www.gnome.org/) as a graphical interface.
If you're really motivated you could read through my [introduction to linux](https://gitea.86thumbs.net/waldek/linux_short) course.
It's a work in progress and all feedback is welcome.