Compare commits
47 Commits
Author | SHA1 | Date |
---|---|---|
waldek | e6a747a22f | |
waldek | b40e4d8eb4 | |
waldek | 1a6966dae4 | |
waldek | ebb13f83e3 | |
Yousri | 62ddfad1a6 | |
waldek | 598d499356 | |
Yousri | 9a09ff821f | |
Yousri | 920982a741 | |
Yousri | 93422cad7f | |
Yousri | cd7343dec8 | |
Yousri | a52ad43855 | |
Yousri | 6a7f4d1205 | |
Yousri | 1bc82d4956 | |
Yousri | 5d2a4e677e | |
waldek | 3fa1ede369 | |
waldek | 9468c221c1 | |
waldek | 0fb66a9009 | |
Yousri | 53a61c5331 | |
waldek | 417f3a909b | |
Yousri | 335c987261 | |
waldek | c0e37e0b7e | |
waldek | 350a3b6a45 | |
waldek | 5700c73318 | |
waldek | b6ce0eaf47 | |
waldek | e3b2a98376 | |
waldek | 063615621f | |
waldek | 98f189fc9b | |
waldek | 6f2b949408 | |
waldek | 00ea8253cb | |
waldek | fb23fbca2e | |
waldek | d10bee235c | |
waldek | 5368c5023f | |
waldek | d4c452f6c7 | |
waldek | f8e18a62c7 | |
waldek | 27a862bb77 | |
waldek | 31be912721 | |
Yousri | 607c0b8ec6 | |
Yousri | 4a6d1ef41d | |
Yousri | 9b651f9374 | |
Yousri | a80ec966e8 | |
waldek | 46fe6d8276 | |
waldek | 108236dcf3 | |
waldek | 69234aaa9c | |
waldek | 842f182d44 | |
waldek | 9d95179707 | |
waldek | cabe212449 | |
waldek | dabb20053a |
|
@ -2,3 +2,4 @@
|
|||
*.pdf
|
||||
*.docx
|
||||
.~lock*
|
||||
.idea/
|
||||
|
|
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 149 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 153 KiB |
After Width: | Height: | Size: 159 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 193 KiB |
After Width: | Height: | Size: 180 KiB |
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 193 KiB |
After Width: | Height: | Size: 151 KiB |
After Width: | Height: | Size: 169 KiB |
After Width: | Height: | Size: 233 KiB |
After Width: | Height: | Size: 129 KiB |
After Width: | Height: | Size: 150 KiB |
After Width: | Height: | Size: 135 KiB |
After Width: | Height: | Size: 175 KiB |
After Width: | Height: | Size: 120 KiB |
After Width: | Height: | Size: 159 KiB |
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 134 KiB |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 102 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 144 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 135 KiB |
|
@ -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...")
|
|
@ -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.
|
|
@ -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)
|
|
@ -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.
|
||||
|
|
|
@ -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()
|
||||
|
||||
self.config(bg="yellow")
|
||||
self.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()
|
||||
|
||||
|
||||
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):
|
||||
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)
|
||||
import tkinter as tk
|
||||
|
||||
|
||||
number=0
|
||||
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()
|
||||
|
||||
if __name__ == "__main__":
|
||||
root = Tk()
|
||||
root.title("this is the title of the window")
|
||||
root.geometry("500x300")
|
||||
win = MainWindow()
|
||||
root.mainloop()
|
||||
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")
|
||||
|
||||
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
|
||||
|
|
|
@ -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/)
|
|
@ -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
|
@ -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**
|
||||
|
|
|
@ -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.
|