Compare commits

...

47 Commits

Author SHA1 Message Date
waldek e6a747a22f new toc 2022-05-23 19:22:45 +02:00
waldek b40e4d8eb4 Merge branch 'master' of ssh://86thumbs.net:3022/waldek/python_introduction 2022-05-20 15:43:28 +02:00
waldek 1a6966dae4 fixes git picture asset path for pandoc 2022-05-20 15:42:50 +02:00
waldek ebb13f83e3 Merge pull request 'The Guess the Number, object-oriented version' (#13) from Yousri/python_course_doc:master into master
Reviewed-on: https://gitea.86thumbs.net/waldek/python_course_doc/pulls/13
2022-05-12 13:59:53 +02:00
Yousri 62ddfad1a6 The Guess the Number, object-oriented version 2022-05-12 11:08:50 +02:00
waldek 598d499356 Merge pull request 'master' (#12) from Yousri/python_course_doc:master into master
Reviewed-on: https://gitea.86thumbs.net/waldek/python_course_doc/pulls/12
2022-05-10 16:59:07 +02:00
Yousri 9a09ff821f Quote Added 2022-05-10 16:08:05 +02:00
Yousri 920982a741 DIP explication starting 2022-05-10 15:28:26 +02:00
Yousri 93422cad7f Resize Images 2022-05-10 12:07:39 +02:00
Yousri cd7343dec8 Information of Liskov Substitution Principle and Interface Segregation Principle: make example 2022-05-10 12:03:43 +02:00
Yousri a52ad43855 UML OCP finished, Start with Liskov Substitution Principle 2022-05-10 11:15:24 +02:00
Yousri 6a7f4d1205 OCP UML Starting 2022-05-10 10:54:02 +02:00
Yousri 1bc82d4956 Talk about OCP(not finish) 2022-05-09 17:04:21 +02:00
Yousri 5d2a4e677e Start- Intro_to_Solid.md 2022-05-09 12:12:37 +02:00
waldek 3fa1ede369 Merge remote branch 'yousri/master' 2022-05-06 12:27:05 +02:00
waldek 9468c221c1 from git gui 2022-05-06 12:25:48 +02:00
waldek 0fb66a9009 adds some final notes 2022-05-05 23:09:49 +02:00
Yousri 53a61c5331 Git Tutorial - Bash, PyCharm finished 2022-05-03 13:58:26 +02:00
waldek 417f3a909b betters titles in readme and toc generator 2022-05-03 11:45:42 +02:00
Yousri 335c987261 I'm in the chapter - Cloning the remote project 2022-05-03 11:39:05 +02:00
waldek c0e37e0b7e adds an outline md file and updates the TOC generator 2022-05-03 10:22:33 +02:00
waldek 350a3b6a45 minor title update in gui 2022-05-02 23:05:15 +02:00
waldek 5700c73318 fixes some TOC errors 2022-05-02 23:01:31 +02:00
waldek b6ce0eaf47 adds an automated TOC script 2022-05-02 22:57:37 +02:00
waldek e3b2a98376 adds flow image 2022-05-02 22:27:36 +02:00
waldek 063615621f adds flow image 2022-05-02 22:25:49 +02:00
waldek 98f189fc9b spellcheck... 2022-05-02 22:23:17 +02:00
waldek 6f2b949408 starts the git bash tutorial 2022-05-02 22:21:04 +02:00
waldek 00ea8253cb cleans up the idea folder and adds gitignore 2022-05-02 22:00:06 +02:00
waldek fb23fbca2e Merge remote-tracking branch 'yousri/master'
merges yoursi changes to git tutorial.
2022-05-02 21:58:32 +02:00
waldek d10bee235c updates toc for readme 2022-05-02 21:56:05 +02:00
waldek 5368c5023f adds replay to guess the number gui 2022-05-02 16:49:16 +02:00
waldek d4c452f6c7 closes #7 2022-05-02 16:37:00 +02:00
waldek f8e18a62c7 fixes spelling mistakes 2022-05-02 16:32:53 +02:00
waldek 27a862bb77 fixes image of flowchart 2022-05-02 16:24:50 +02:00
waldek 31be912721 adds flowchart 2022-05-02 16:22:33 +02:00
Yousri 607c0b8ec6 Commit And Push finish 2022-05-02 14:58:17 +02:00
Yousri 4a6d1ef41d Commit And Push finish 2022-05-02 14:54:34 +02:00
Yousri 9b651f9374 Merge branch 'master' of https://gitea.86thumbs.net/waldek/python_course_doc into HEAD 2022-05-02 14:04:17 +02:00
Yousri a80ec966e8 Introduction Git 1 2022-05-02 13:57:29 +02:00
waldek 46fe6d8276 adds chapter titles 2022-05-02 12:07:01 +02:00
waldek 108236dcf3 fixes resize of clone screenshots 2022-05-02 12:01:00 +02:00
waldek 69234aaa9c fixes comment error 2022-05-02 11:20:09 +02:00
waldek 842f182d44 fixes surplus screenshots 2022-05-02 11:17:14 +02:00
waldek 9d95179707 fixes order of screenshots 2022-05-02 11:15:06 +02:00
waldek cabe212449 add a start to the git tutorial 2022-05-02 11:10:45 +02:00
waldek dabb20053a tkinter review 2022-04-27 23:16:39 +02:00
41 changed files with 1541 additions and 525 deletions

1
.gitignore vendored
View File

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

BIN
assets/UML_Solid Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

BIN
assets/git-flow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

47
generate_toc.py Normal file
View File

@ -0,0 +1,47 @@
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...")

341
introduction_to_solid.md Normal file
View File

@ -0,0 +1,341 @@
# 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.

306
learning_git.md Normal file
View File

@ -0,0 +1,306 @@
# 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,6 +65,22 @@ TODO animated overview of the shell and the world of OOP
# 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
# Your first project
@ -80,14 +96,10 @@ print("Hello World!")
Just for reference below are a few helloworld programs in different languages.
First `c#` then `c` then `c++` and last but not least `javascript`.
### c#
```c#
Console.WriteLine("Hello World!");
```
### c
```c
#include <stdio.h>
@ -97,8 +109,6 @@ int main() {
}
```
### c++
```cpp
// Your First C++ Program
@ -110,8 +120,6 @@ int main() {
}
```
### javascript
```js
alert( 'Hello, world!' );
```
@ -209,7 +217,7 @@ But how can we **get** some information from the user?
This is done with the built-in `input` function.
If we open up a python shell we can observe it's behaviour.
```python3
```python
>>> input()
hello world
'hello world'
@ -428,6 +436,41 @@ What about one that takes floating point numbers?
* [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
Your first challenge!
@ -436,7 +479,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.
The result of this program *could* be as follows.
```bash
```
➜ ~ git:(master) ✗ python3 ex_celcius_to_fahrenheit.py
What's the temperature?30
30°C equals 86.0°F
@ -480,42 +523,6 @@ else:
</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
* 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)?
@ -1184,19 +1191,17 @@ while counter <= 10:
print("after the loop, counter: {}".format(counter))
```
<details>
<summary>Another example</summary>
Or with only variables and a more verbose way of incrementing the numbers.
```python3
FirstValue = 2
SecondValue = 0
while FirstValue>SecondValue:
print("The second value is bigger")
SecondValue = SecondValue + 1
first_value = 20
second_value = 0
while first_value > second_value:
print("second value {} is smaller than {}".format(second_value, first_value))
second_value = second_value + 1
```
</details>
Two *extra* things might look new to you here.
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).
@ -1247,12 +1252,17 @@ print("after the loop, counter: {}".format(counter))
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.
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.**
🏃 Try it
---
Go back to the Celsius to Farenheit converter and add a while loop to ensure the user puts in only numbers.
Go back to the Celsius to Fahrenheit converter and add a while loop to ensure the user puts in only numbers.
# Coding challenge - Guess the number
@ -1312,6 +1322,69 @@ if __name__ == "__main__":
</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
---
@ -1381,9 +1454,10 @@ if __name__ == "__main__":
</details>
#Logical Operators
# Logical Operators
There is three types of logical operators. All operators returns boolean.
There is three types of logical operators.
All operators return boolean values, ie `True` or `False`.
| Operator | Result |
|-----------------|---------------------------------------------|
@ -1402,13 +1476,15 @@ DealerName = "Paul"
DealerAgreement = True
if CustomerAgreement and DealerAgreement :
print(f"Youpi !!! {CustomerName} and {DealerName} are agreed ")
print(f"Youpi !!! {CustomerName} and {DealerName} agree")
else:
print(f"Oh no {CustomerName} and {DealerName} are disagreed ;( ")
print(f"Oh no, {CustomerName} and {DealerName} don't agree... ;(")
```
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.
Let's show an another example with the `Or` operator.
As you can guess, Jean and Paul are agreeing to the deal.
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
def Choice_cold_hot(Temperature):
@ -1417,13 +1493,18 @@ def Choice_cold_hot(Temperature):
else:
print("Let's go to the beach")
if __name__ == "__main__":
Temperature = int(input("What is the 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.
Let's make an exercise. You have to take the previous code and use the `And` operator.
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° 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>
<summary>Spoiler warning</summary>
@ -1434,13 +1515,15 @@ def Choice_cold_hot(Temperature):
print("Let's go to the beach")
else:
print("Don't go outside")
if __name__ == "__main__":
Temperature = int(input("What is the temperature"))
Choice_cold_hot(Temperature)
````
</details>
Now, we have used that operators, we can use the last logical operator. The `Not` operator sends the reverse of the result.
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.
````python3
if __name__ == "__main__":
@ -1450,7 +1533,9 @@ if __name__ == "__main__":
else:
print("That will be expensive")
````
In this example, if you tap yes, the result will be reversed.
# 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.

View File

@ -3,134 +3,136 @@
## Tkinter helloworld
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
from tkinter import *
import tkinter as tk
root = Tk()
MyLabel = Label(root,text="hello world")
MyLabel.pack()
root = tk.Tk()
root.title("hello world")
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.
The Tk architecture is used to create a GUI widget. He adds a lot of custom commands, so the widget is very customizable.
Tkinter has two popular architectures, Tcl and Tk.
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.
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
add and adjust a widget.
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()` method allows us to open the main window and to not close the window immediately .
This **event loop** will process all mouse event, button clicks, and changes.
While it works we know better by now.
We should include the `if __name__ == "__main__"` statement.
We should include the `if __name__ == "__main__"` statement so let's do this!
```python
from tkinter import *
if __name__ == "__main__":
root = Tk()
import tkinter as tk
MyLabel = Label(root,text="hello world")
MyLabel.pack()
if __name__ == "__main__":
root = tk.Tk()
root.title("hello world")
root.mainloop()
```
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.
In the latter we will create our button and labels so should create our own class and inherit from it.
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.
To this instance we can attach other widgets and display them.
```python
from tkinter import *
import tkinter as tk
class MainWindow(Frame):
class Application(tk.Tk):
def __init__(self):
Label.__init__(self, text="hello world")
self.pack()
tk.Tk.__init__(self)
self.title("hello world")
self.geometry("500x400")
if __name__ == "__main__":
root = Tk()
root.title("title of the window")
root.geometry("500x300")
MainWindow()
root.mainloop()
app = Application()
app.mainloop()
```
We can add content to the *frame*, such as labels, input boxes and buttons as follows.
## Adding widgets
We can add content to this window, such as labels, input boxes and buttons as follows.
```python
from tkinter import *
import tkinter as tk
class MainWindow(Frame):
class Application(tk.Tk):
def __init__(self):
Label.__init__(self, text="hello world")
#Label
MyLabel = Label(self, text="This is a label")
MyLabel.pack()
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.config(bg="yellow")
self.pack()
if __name__ == "__main__":
root = Tk()
root.title("title of the window")
root.geometry("500x300")
MainWindow()
root.mainloop()
app = Application()
app.mainloop()
```
Let's try to put multiple visual object into the same frame.
Let's try to put multiple visual object into the same window.
```python3
from tkinter import *
import tkinter as tk
class MainWindow(Frame):
class Application(tk.Tk):
def __init__(self):
Label.__init__(self, text="hello world")
#Label
MyLabel = Label(self, text="This is a label")
MyLabel.pack()
#Button
MyButton = Button(self, text="I'm clickable!")
MyButton.pack()
tk.Tk.__init__(self)
self.title("hello world")
self.geometry("500x400")
self.label = tk.Label(text="This is a label")
self.label.pack()
self.button = tk.Button(text="I'm clickable!")
self.button.pack()
self.output = tk.Label(text="I'm a second label")
self.output.pack()
self.config(bg="yellow")
self.pack()
if __name__ == "__main__":
root = Tk()
root.title("title of the window")
root.geometry("500x300")
MainWindow()
root.mainloop()
app = Application()
app.mainloop()
```
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).
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/).
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/).
```python
from tkinter import *
import tkinter as tk
class MainWindow(Frame):
class Application(tk.Tk):
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!")
MyButton.place(x=10, y=50)
self.pack(fill=BOTH,expand=True)
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.grid(column=0, row=0, sticky=tk.W, padx=5, pady=5)
self.button = tk.Button(self, text="I'm clickable!")
self.button.grid(column=1, row=0, sticky=tk.E, padx=5, pady=5)
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__":
root = Tk()
root.title("this is the title of the window")
root.geometry("500x300")
win = MainWindow()
root.mainloop()
app = Application()
app.mainloop()
```
This is looking better!
@ -152,353 +154,408 @@ 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.
```python
from tkinter import *
class MainWindow(Frame):
import tkinter as tk
class Application(tk.Tk):
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)
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()
def ButtonEnable(self, Label):
global number
number += 1
counter = "You have press the button {} time".format(number)
Label.config(text=counter)
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()
number=0
self.config(bg="yellow")
if __name__ == "__main__":
root = Tk()
root.title("this is the title of the window")
root.geometry("500x300")
win = MainWindow()
root.mainloop()
```
We can use the same *idea* to grab input from the textbox.
```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)
def clicked(self):
print("hello world!")
if __name__ == "__main__":
root = Tk()
root.title("this is the title of the window")
root.geometry("500x300")
win = MainWindow()
root.mainloop()
app = Application()
app.mainloop()
```
## Tkinter guess the number
We can see *hello world!* printed to the console with each click!
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
import time
from tkinter import *
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 = 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
class MainWindow(Frame):
class Application(tk.Tk):
def __init__(self):
Frame.__init__(self, master=None, bg="white")
MyPanel = PanedWindow.__init__(self)
tk.Tk.__init__(self)
self.title("hello world")
self.geometry("500x400")
self.resizable(0, 0)
MyNumber = random.randint(0, 100)
self.number = random.randint(0, 10)
#Label
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.header = tk.Label(text="I have a number in mind...")
self.header.grid(column=0, row=0, sticky=tk.W)
self.pack(fill=BOTH,expand=True)
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)
def ButtonEnable(self, MyEntry, MyNumber):
if self.IsCorrect(MyEntry.get()):
number = int(MyEntry.get())
if number != MyNumber:
self.GameOver(number, MyNumber)
else:
self.Win()
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)
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:
self.MyLabel.config(text="I need numbers!")
def GameOver(self, number, MyNumber):
if number > MyNumber:
self.MyLabel.config(text="My number is smaller")
else:
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 number < self.number:
msg = "my number is bigger"
elif number > self.number:
msg = "my number is smaller"
elif number == self.number:
msg = "bingo!"
self.output.config(text=msg)
if __name__ == "__main__":
root = Tk()
root.title("Guess the number")
root.geometry("500x300")
win = MainWindow()
root.mainloop()
app = Application()
app.mainloop()
```
</details>
<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
A simple console only MVC.
We'll add the GUI view in a bit.
```python
from tkinter import *
import tkinter as tk
class ConsoleView(object):
"""A view for console."""
def select_task(self):
"""Asks which index to look up."""
idx = input("which task do you want to see? ")
return idx
def show(self, task):
"""Displays the task to the console. This method is called from the
controller."""
print("your task: {}".format(task))
def error(self, msg):
"""Prints error messages coming from the controller."""
print("error: {}".format(msg))
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[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 = ConsoleView
app = Controller(view)
app.run()
```
And now with the implemented `TkinterView` class.
```python
from tkinter import *
class ConsoleView(object):
"""A view for console."""
def select_task(self):
"""Asks which index to look up."""
idx = input("which task do you want to see? ")
return idx
def show(self, task):
"""Displays the task to the console. This method is called from the
controller."""
print("your task: {}".format(task))
def error(self, msg):
"""Prints error messages coming from the controller."""
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."""
def __init__(self, controller):
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)
"""Asks which index to look up."""
idx = input("which task do you want to see? ")
return 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)
def show_message(self, msg):
print("MSG: {}".format(msg))
def show(self, task):
"""Displays the task to the console. This method is called from the
controller."""
print("your task: {}".format(task))
def error(self, msg):
"""Prints error messages coming from the controller."""
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):
"""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 __init__(self):
self.db = ["go to the shops", "dryhop beer", "drop of motorbike"]
def __len__(self):
return len(self.db)
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)]
task = self.db[idx]
except IndexError:
task = "task with {} not found!".format(idx)
task = None
return task
class Controller(object):
"""Binds the model and the view together."""
def __init__(self, view):
self.model = Model()
def __init__(self, model):
self.model = model
def set_view(self, view):
self.view = view
if isinstance(view, TkinterView):
self.request_number_of_tasks_from_model()
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 request_number_of_tasks_from_model(self):
number = len(self.model)
self.view.show_message("you have {} tasks".format(number))
def get_task_from_model(self, idx):
"""Needed for the TkinterView to communicate with the controller."""
def request_task_from_model(self, idx):
"""Needed for the ConsoleView and 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)
if task is None:
self.view.error("task not found!")
else:
self.view.show(task)
if __name__ == "__main__":
view = TkinterView
app = Controller(view)
app.run()
model = Model()
controller = Controller(model)
view = ConsoleView(controller)
controller.set_view(view)
view.mainloop()
```
Below you can see a `tkinter` class that acts as a drop in replacement for the `ConsoleView` class.
```python
class TkinterView(tk.Tk):
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()
self.header.grid(column=0, row=0, sticky=tk.W)
self.question = tk.Label(self, text="Which task do you want to see?")
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="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):
"""Displays the task to the console. This method is called from the
controller."""
self.output.config(text=task)
def error(self, msg):
"""Prints error messages coming from the controller."""
self.output.config(text=msg)
```
<!--
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.
This is not a good idea because we should separate responsibilities into classes!
@ -964,10 +1021,16 @@ if __name__ == "__main__":
app.mainloop()
````
-->
## Coding challenge - Login generator with GUI
TODO
## Coding challenge - Trivial pursuit with GUI
TODO
# WXpython
## wxpython helloworld

4
links.md Normal file
View File

@ -0,0 +1,4 @@
# 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/)

20
outline.md Normal file
View File

@ -0,0 +1,20 @@
# 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,112 +1,186 @@
# Python course
# Program
This repository contains some notes and exercises for learning python3.
| 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 | |
## Initiation to programming
* Day 1
* What we'll learn
* Concepts
* Syntax
* Tools
* Writing code
* Running code
* The python3 shell
* Installing pycharm
* Your first project
* c
* c
* c++
* javascript
* How to execute
* Simple printing
* 🏃 Try it
* 🏃 Try it
* String replacement
* String formatting
* Some links to read up
* Taking input
* Some functions are blocking
* Functions can return something
* 🏃 Try it
* Functions can take arguments
* 🏃 Try it
* Taking input and evaluation
* Conditional logic
* Class string methods
* Some links to read up on
* Coding challenge - Celsius to Fahrenheit converter
* Day 2
* A text based adventure game
* Creating your own functions
* Functions that *do* something
* Variable scope
* Functions that *return* something
* 🏃 Try it
* Some links to read up on
* Coding challenge - Pretty Print
* Using the standard library
* Coding challenge - Memento Mori calculator
* Day 3
* Writing your first library
* 🏃 Try it
* How do we write libraries?
* What is `__name__ == "__main__"`?
* Anatomy of a program
* 🏃 Try it
* While loop
* 🏃 Try it
* Coding challenge - Guess the number
* 🏃 Try it
* Day 4
* Lists
* Creating lists
* List methods
* 🏃 Try it
* Picking elements and slicing lists
* 🏃 Try it
* For loop
* Coding challenge - Cheerleader chant
* Coding challenge - ROT13
* 🏃 Try it
* List comprehension
* Day 5
* Handling files
* Reading from a file
* In-line way
* Pythonic way
* Writing to a file
* Coding challenge - Login generator
* Dictionaries as data containers
* Coding challenge - Task manager
* Text based databases
* Day 6
* Now for some useful scripting
* Day 7
* Creating our own objects
* First some *abstract* examples
* 🏃 Try it
* Class inheritance
* 🏃 Try it
* A *practical* example
* Now some *practical* improvements
* Improve the login generator
* Improve the task manager
* Day 8
* Infinite programs
* Logic breakdown of a simple game
* Trivial pursuit multiple choice game
* Introduction to the `requests` library
* Threading
* Day 9
* GUI programming
* Tkinter helloworld
* Tkinter guess the number
* MVC design pattern
* Day 10-12
* Coding challenge - Login generator with GUI
* Coding challenge - Trivial pursuit with GUI
Learning python3
=================
* [What we'll learn](./learning_python3.md#what-well-learn)
* [Concepts](./learning_python3.md#concepts)
* [Syntax](./learning_python3.md#syntax)
* [Tools](./learning_python3.md#tools)
* [Writing code](./learning_python3.md#writing-code)
* [Running code](./learning_python3.md#running-code)
* [The python3 shell](./learning_python3.md#the-python3-shell)
* [Installing pycharm](./learning_python3.md#installing-pycharm)
* [Virtual environments](./learning_python3.md#virtual-environments)
* [Your first project](./learning_python3.md#your-first-project)
* [How to execute](./learning_python3.md#how-to-execute)
* [Simple printing](./learning_python3.md#simple-printing)
* [Try it](./learning_python3.md#-try-it)
* [Try it](./learning_python3.md#-try-it-1)
* [String replacement](./learning_python3.md#string-replacement)
* [String formatting](./learning_python3.md#string-formatting)
* [Some links to read up](./learning_python3.md#some-links-to-read-up)
* [Taking input](./learning_python3.md#taking-input)
* [Some functions are blocking](./learning_python3.md#some-functions-are-blocking)
* [Functions can return something](./learning_python3.md#functions-can-return-something)
* [Try it](./learning_python3.md#-try-it-2)
* [Functions can take arguments](./learning_python3.md#functions-can-take-arguments)
* [Try it](./learning_python3.md#-try-it-3)
* [Taking input and evaluation](./learning_python3.md#taking-input-and-evaluation)
* [Conditional logic](./learning_python3.md#conditional-logic)
* [Class string methods](./learning_python3.md#class-string-methods)
* [Try it](./learning_python3.md#-try-it-4)
* [Some links to read up on](./learning_python3.md#some-links-to-read-up-on)
* [Coding challenge - Currency converter](./learning_python3.md#coding-challenge---currency-converter)
* [Coding challenge - Celsius to Fahrenheit converter](./learning_python3.md#coding-challenge---celsius-to-fahrenheit-converter)
* [Some links to read up on](./learning_python3.md#some-links-to-read-up-on-1)
* [A text based adventure game](./learning_python3.md#a-text-based-adventure-game)
* [Creating your own functions](./learning_python3.md#creating-your-own-functions)
* [Functions that <em>do</em> something](./learning_python3.md#functions-that-do-something)
* [Variable scope](./learning_python3.md#variable-scope)
* [Functions that <em>return</em> something](./learning_python3.md#functions-that-return-something)
* [Try it](./learning_python3.md#-try-it-5)
* [Some links to read up on](./learning_python3.md#some-links-to-read-up-on-2)
* [Coding challenge - Pretty Print](./learning_python3.md#coding-challenge---pretty-print)
* [Using the standard library](./learning_python3.md#using-the-standard-library)
* [Coding challenge - Memento Mori calculator](./learning_python3.md#coding-challenge---memento-mori-calculator)
* [Writing your first library](./learning_python3.md#writing-your-first-library)
* [Try it](./learning_python3.md#-try-it-6)
* [How do we write libraries?](./learning_python3.md#how-do-we-write-libraries)
* [What is __name__ == "__main__"?](./learning_python3.md#what-is-__name__--__main__)
* [Anatomy of a program](./learning_python3.md#anatomy-of-a-program)
* [Try it](./learning_python3.md#-try-it-7)
* [While loop](./learning_python3.md#while-loop)
* [Try it](./learning_python3.md#-try-it-8)
* [Coding challenge - Guess the number](./learning_python3.md#coding-challenge---guess-the-number)
* [Try it](./learning_python3.md#-try-it-9)
* [Logical Operators](./learning_python3.md#logical-operators)
* [Lists](./learning_python3.md#lists)
* [Creating lists](./learning_python3.md#creating-lists)
* [List methods](./learning_python3.md#list-methods)
* [Try it](./learning_python3.md#-try-it-10)
* [Picking elements and slicing lists](./learning_python3.md#picking-elements-and-slicing-lists)
* [Try it](./learning_python3.md#-try-it-11)
* [For loop](./learning_python3.md#for-loop)
* [Coding challenge - Cheerleader chant](./learning_python3.md#coding-challenge---cheerleader-chant)
* [Coding challenge - ROT13](./learning_python3.md#coding-challenge---rot13)
* [Try it](./learning_python3.md#-try-it-12)
* [Coding challenge - Christmas Tree](./learning_python3.md#coding-challenge---christmas-tree)
* [List comprehension](./learning_python3.md#list-comprehension)
* [Handling files](./learning_python3.md#handling-files)
* [Reading from a file](./learning_python3.md#reading-from-a-file)
* [In-line way](./learning_python3.md#in-line-way)
* [Pythonic way](./learning_python3.md#pythonic-way)
* [Writing to a file](./learning_python3.md#writing-to-a-file)
* [Coding challenge - Login generator](./learning_python3.md#coding-challenge---login-generator)
* [Dictionaries as data containers](./learning_python3.md#dictionaries-as-data-containers)
* [Two dimensional lists](./learning_python3.md#two-dimensional-lists)
* [Dictionaries](./learning_python3.md#dictionaries)
* [Try it](./learning_python3.md#-try-it-13)
* [Coding challenge - Task manager](./learning_python3.md#coding-challenge---task-manager)
* [CSV based task manager](./learning_python3.md#csv-based-task-manager)
* [Text based databases](./learning_python3.md#text-based-databases)
* [Try it](./learning_python3.md#-try-it-14)
* [Now for some useful scripting](./learning_python3.md#now-for-some-useful-scripting)
* [Creating our own objects](./learning_python3.md#creating-our-own-objects)
* [First some <em>abstract</em> examples](./learning_python3.md#first-some-abstract-examples)
* [Try it](./learning_python3.md#-try-it-15)
* [Magic methods](./learning_python3.md#magic-methods)
* [Class inheritance](./learning_python3.md#class-inheritance)
* [Try it](./learning_python3.md#-try-it-16)
* [Coding challenge - Chalet floor](./learning_python3.md#coding-challenge---chalet-floor)
* [Now some <em>practical</em> improvements](./learning_python3.md#now-some-practical-improvements)
* [Improve the login generator](./learning_python3.md#improve-the-login-generator)
* [Improve the task manager](./learning_python3.md#improve-the-task-manager)
* [Infinite programs](./learning_python3.md#infinite-programs)
* [Logic breakdown of a simple game](./learning_python3.md#logic-breakdown-of-a-simple-game)
* [A non object orientated solution](./learning_python3.md#a-non-object-orientated-solution)
* [An object orientated solution](./learning_python3.md#an-object-orientated-solution)
* [Trivial pursuit multiple choice game](./learning_python3.md#trivial-pursuit-multiple-choice-game)
* [Introduction to the requests library](./learning_python3.md#introduction-to-the-requests-library)
* [Threading](./learning_python3.md#threading)
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**

75
what_is_next.md Normal file
View File

@ -0,0 +1,75 @@
# 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.