├── .vscode
└── settings.json
├── 01 - Kostenquote
├── Kosten.py
├── KostenGUI.py
└── README.md
├── 02 - Mehrwertsteuer
├── Mehrwertsteuer.py
└── README.md
├── 03 - Blutalkohol
├── Blutalkohol.py
└── README.md
├── 04 - Minderung
├── Minderung.py
└── README.md
├── 05 - Examensnote
├── Examensnote.py
└── README.md
├── 06 - GUI
├── (a) In einer Datei.py
├── (b) In zwei Dateien.py
├── FensterDatei.py
├── GUI.ui
├── README.md
└── __pycache__
│ └── Ui_GUI.cpython-39.pyc
├── 07 - Daten
├── README.MD
├── feiertage.py
└── fristen.py
└── README.md
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "python.pythonPath": "C:\\Python39\\python.exe",
3 | "python.analysis.extraPaths": ["./06 - GUI"]
4 | }
--------------------------------------------------------------------------------
/01 - Kostenquote/Kosten.py:
--------------------------------------------------------------------------------
1 | # Programm 01: Kostenquote nach §§ 91, 92 ZPO im Zweipersonenverhältnis
2 | # (C) 13.04.2021, Michael Beurskens
3 |
4 | # Im Folgenden werden einige Variablen definiert, die wir später brauchen. Dies geschieht durch Angabe des Namens,
5 | # gefolgt von einem Doppelpunkt mit dem Typ und einem Gleichheitszeichen mit dem Wert
6 |
7 | streitwert: float = 0
8 | erfolg: float = 0
9 | unterliegen: float = 0
10 | kostenKlaeger: float = 0
11 | kostenBeklagter: float = 0
12 | # Noch ein Kommentar
13 |
14 |
15 | # Mit der Funktion print() kann man Texte ausgeben. Diese stehen in Anführungszeichen.
16 | print("Dieses Programm berechnet die Kostenquote nach §§ 91, 92 ZPO im Zweipersonenverhältnis")
17 |
18 | # Mit der Funktion input() kann man Texte, die über die Tastatur eingegeben werden, in eine Variable speichern. Dazu schreibt man einfach Variablenname : str = input().
19 | eingabeStreitwert: str = input("Wie hoch ist der Streitwert (in Euro)? ")
20 | eingabeErfolg: str = input(
21 | "In welchem Umfang war die Klage erfolgreich (in Euro)? ")
22 |
23 | # Man kann Werte umwandeln (etwa einen Text in eine Zahl), indem man den gewünschten Typ vor eine Variable setzt und diese in Klammern dahinterschreibt, also z.B. int(jahr), str(zahl)
24 | streitwert = float(eingabeStreitwert)
25 | erfolg = float(eingabeErfolg)
26 | unterliegen = streitwert-erfolg
27 | kostenKlaeger = round((streitwert-erfolg)/streitwert*100, 2)
28 | kostenBeklagter = 100-kostenKlaeger
29 |
30 | # Wenn man \n im Rahmen eines print()-Befehls eingibt, wird eine leere Zeile ("Absatz") ausgegeben
31 | print("\n")
32 | print("-----------------------------------------------------------------------")
33 |
34 | # Hier folgt jetzt eine etwas kompliziertere Ausgabe. Dabei ist folgendes zu beachten:
35 | # 1. Wenn eine Zeile zu lang wird, kann man diese mit einem Backslash (Alt Gr+ ß) in der nächsten Zeile fortsetzen.
36 | # 2. Wenn man ein Prozentzeichen in einem Text angibt, wird dieses durch Wert ersetzt, die man nach dem Text auflistet.
37 | # 3. Wenn man hinter dem Prozentzeichen %f angibt, weiß der Computer, dass eine Gleitkommazahl gewünscht ist.
38 | # 4. Wenn man hinter dem Prozentzeichen %.2f angibt, weiß der Computer, dass hinter dem Punkt (Komma) nur zwei Ziffern stehen sollen (der Rest fällt ohne Rundung weg)
39 | # 5. Wenn es in einem Text mehrere Lücken gibt, werden die einzufügenden Werte in einer Klammer hinter dem Text angegeben
40 | print("Die Kosten des Rechtsstreits trägt der Kläger zu %.2f / %.2f \
41 | und der Beklagte zu %.2f / %.2f." % (unterliegen, streitwert, erfolg, streitwert))
42 | print("\n")
43 | print("- oder (alternativer Tenor) -")
44 | print("\n")
45 | print("Die Kosten des Rechtsstreits trägt der Kläger zu %.2f Prozent \
46 | und der Beklagte zu %.2f Prozent." % (kostenKlaeger, kostenBeklagter))
47 | print("-----------------------------------------------------------------------")
48 |
--------------------------------------------------------------------------------
/01 - Kostenquote/KostenGUI.py:
--------------------------------------------------------------------------------
1 | # Programm 01a: Kostenquote nach §§ 91, 92 ZPO im Zweipersonenverhältnis mit grafischer Oberfläche und Eingabevalidierung
2 | # (C) 14.05.2021, Michael Beurskens
3 |
4 | ################################################################################
5 | # Form generated from reading UI file 'designeravlkQV.ui'
6 | ##
7 | # Created by: Qt User Interface Compiler version 5.15.2
8 | ##
9 | # WARNING! All changes made in this file will be lost when recompiling UI file!
10 | ################################################################################
11 |
12 |
13 |
14 | # Die folgenden Zeilen laden bestimmte (notwendige) Klassen, um grafische Oberflächen anzuzeigen
15 | # Sie müssen diese nicht selber schreiben, da sie von QT-Designer automatisch erstellt werdne
16 |
17 | from PyQt5.QtCore import * # Für die Benutzeroberflächenelemente
18 | from PyQt5.QtGui import * # Für die Benutzeroberflächenelemente
19 | from PyQt5.QtWidgets import * # Für die Benutzeroberflächenelemente
20 | import sys # Für die Ein- und Ausgabewerte des Betriebssystems
21 | import locale # Für die Umwandlung von Zahlen und Währungsangaben in ein "deutsches" Format (Euro, Komma statt Punkte für Nachkommastellen)
22 | import decimal
23 |
24 |
25 | # Globalen Variablen (die über die Funktionen hinaus bestehen sollen)
26 | formulierungProzent:str="" # Formulierung für Tenor mit Prozentangaben
27 | formulierungBruch:str="" # Formulierung für Tenor mit Brüchen
28 |
29 |
30 | class Ui_MainWindow(object):
31 | def setupUi(self, MainWindow):
32 | if not MainWindow.objectName():
33 | MainWindow.setObjectName(u"MainWindow")
34 | MainWindow.resize(1107, 640)
35 | self.action_Beenden = QAction(MainWindow)
36 | self.action_Beenden.setObjectName(u"action_Beenden")
37 | self.centralwidget = QWidget(MainWindow)
38 | self.centralwidget.setObjectName(u"centralwidget")
39 | self.verticalLayout = QVBoxLayout(self.centralwidget)
40 | self.verticalLayout.setObjectName(u"verticalLayout")
41 |
42 | self.erklaerung = QLabel(self.centralwidget)
43 | self.erklaerung.setObjectName(u"erklaerung")
44 | self.erklaerung.setTextFormat(Qt.PlainText)
45 | self.erklaerung.setAlignment(Qt.AlignCenter)
46 | self.verticalLayout.addWidget(self.erklaerung)
47 | self.horizontalLayout = QHBoxLayout()
48 | self.horizontalLayout.setObjectName(u"horizontalLayout")
49 | self.label = QLabel(self.centralwidget)
50 | self.label.setObjectName(u"label")
51 | self.label.setAlignment(
52 | Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter)
53 |
54 | self.horizontalLayout.addWidget(self.label)
55 |
56 | self.antrag = QComboBox(self.centralwidget)
57 | self.antrag.setObjectName(u"antrag")
58 | self.antrag.setEditable(True)
59 |
60 | self.horizontalLayout.addWidget(self.antrag)
61 |
62 | self.label_2 = QLabel(self.centralwidget)
63 | self.label_2.setObjectName(u"label_2")
64 | self.label_2.setAlignment(
65 | Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter)
66 |
67 | self.horizontalLayout.addWidget(self.label_2)
68 |
69 | self.erfolg = QComboBox(self.centralwidget)
70 | self.erfolg.setObjectName(u"erfolg")
71 | self.erfolg.setEditable(True)
72 |
73 | self.horizontalLayout.addWidget(self.erfolg)
74 |
75 | self.berechnen = QPushButton(self.centralwidget)
76 | self.berechnen.setObjectName(u"berechnen")
77 | self.berechnen.setAutoDefault(True)
78 | self.berechnen.setEnabled(False)
79 |
80 | self.horizontalLayout.addWidget(self.berechnen)
81 |
82 | self.verticalLayout.addLayout(self.horizontalLayout)
83 |
84 | self.fehlermeldung = QLabel(self.centralwidget)
85 | self.fehlermeldung.setObjectName(u"fehlermeldung")
86 | font = QFont()
87 | font.setBold(True)
88 | font.setWeight(75)
89 | self.fehlermeldung.setFont(font)
90 | self.fehlermeldung.setStyleSheet(u"color:rgb(170, 0, 0)")
91 | self.fehlermeldung.setAlignment(Qt.AlignCenter)
92 |
93 | self.verticalLayout.addWidget(self.fehlermeldung)
94 |
95 | self.ergebnis = QTextBrowser(self.centralwidget)
96 | self.ergebnis.setObjectName(u"ergebnis")
97 | self.verticalLayout.addWidget(self.ergebnis)
98 |
99 | self.horizontalLayout2 = QHBoxLayout()
100 | self.horizontalLayout2.setObjectName(u"horizontalLayout2")
101 |
102 |
103 |
104 | self.copyPercent = QPushButton(self.centralwidget)
105 | self.copyPercent.setObjectName(u"copyPercent")
106 | self.copyPercent.setAutoDefault(False)
107 | self.copyPercent.setEnabled(False)
108 | self.horizontalLayout2.addWidget(self.copyPercent)
109 |
110 | self.copyFraction = QPushButton(self.centralwidget)
111 | self.copyFraction.setObjectName(u"copyFraction")
112 | self.copyFraction.setAutoDefault(False)
113 | self.copyFraction.setEnabled(False)
114 | self.horizontalLayout2.addWidget(self.copyFraction)
115 | self.verticalLayout.addLayout(self.horizontalLayout2)
116 |
117 | MainWindow.setCentralWidget(self.centralwidget)
118 | self.menubar = QMenuBar(MainWindow)
119 | self.menubar.setObjectName(u"menubar")
120 | self.menubar.setGeometry(QRect(0, 0, 1107, 21))
121 | self.menuDatei = QMenu(self.menubar)
122 | self.menuDatei.setObjectName(u"menuDatei")
123 | MainWindow.setMenuBar(self.menubar)
124 | self.statusbar = QStatusBar(MainWindow)
125 | self.statusbar.setObjectName(u"statusbar")
126 | MainWindow.setStatusBar(self.statusbar)
127 |
128 | self.menubar.addAction(self.menuDatei.menuAction())
129 | self.menuDatei.addAction(self.action_Beenden)
130 |
131 | self.retranslateUi(MainWindow)
132 |
133 | self.berechnen.setDefault(True)
134 |
135 | QMetaObject.connectSlotsByName(MainWindow)
136 | # setupUi
137 |
138 | def retranslateUi(self, MainWindow):
139 | MainWindow.setWindowTitle(QCoreApplication.translate(
140 | "MainWindow", u"Kostenrechner", None))
141 | self.action_Beenden.setText(
142 | QCoreApplication.translate("MainWindow", u"&Beenden", None))
143 | self.erklaerung.setText(QCoreApplication.translate(
144 | "MainWindow", u"Dieses Programm berechnet die Kostenquote für eine Klage eines Klägers gegen einen Beklagten (ohne Widerklage oder Streitgenossenschaft).", None))
145 | self.label.setText(QCoreApplication.translate(
146 | "MainWindow", u"Beantragter Betrag:", None))
147 | self.label_2.setText(QCoreApplication.translate(
148 | "MainWindow", u"Zugesprochener Betrag:", None))
149 | self.berechnen.setText(QCoreApplication.translate(
150 | "MainWindow", u"Kostenquote berechnen", None))
151 | self.copyFraction.setText(QCoreApplication.translate(
152 | "MainWindow", u"Formulierung mit Brüchen kopieren", None))
153 | self.copyPercent.setText(QCoreApplication.translate(
154 | "MainWindow", u"Formulierung mit Prozentangaben kopieren", None))
155 | self.fehlermeldung.setText("")
156 | self.menuDatei.setTitle(QCoreApplication.translate(
157 | "MainWindow", u"&Datei", None))
158 | # retranslateUi
159 |
160 |
161 | # Ab hier beginnt das eigentliche Programm
162 |
163 |
164 | # Hilfsfunktion: Größten gemeinsamen Teiler berechnen
165 |
166 | def ggt(wert1: int, wert2: int) -> int:
167 | '''Euklidischer Algorithmus (https://de.wikipedia.org/wiki/Euklidischer_Algorithmus) zur Berechnung des größten gemeinsamen Teilers'''
168 | # Muss man nicht auswendig können, sondern kann man bei Bedarf nachschlagen!
169 | while wert2 != 0:
170 | temp: int = wert1 % wert2
171 | wert1 = wert2
172 | wert2 = temp
173 | return wert1
174 |
175 | # Prüffunktionen zur Validierung der Eingabe
176 |
177 | def pruefeEuro(betrag: str, feld: str) -> bool:
178 | '''Prüfen, ob ein Textwert einen gültigen Euro-Betrag darstellt; ggf. Fehlermeldung mit Angabe des betroffenen Feldes'''
179 |
180 | try:
181 | betragZahl: decimal.Decimal = decimal.Decimal(betrag.replace(",", "."))
182 | if(betragZahl < 0):
183 | fenster.fehlermeldung.setText(
184 | f"Bitte eine positive Zahl im Feld {feld} eingeben (Euro-Beträge können nicht negativ sein).")
185 | return False
186 | # Wenn bei Runden eine Nachkommastelle wegfällt, hat die Zahl offenbar mehr als zwei Nachkommastellen
187 | elif (round(betragZahl, 2) != betragZahl):
188 | fenster.fehlermeldung.setText(
189 | f"Bitte maximal zwei Nachkommastellen im Feld {feld} eingeben (ein Euro hat 100 Cent).")
190 | return False
191 | else:
192 | return True
193 | except:
194 | fenster.fehlermeldung.setText(
195 | f"Bitte einen Eurobetrag im Feld {feld} eingeben.")
196 | return False
197 |
198 |
199 | def erfolgKleinerAntrag() -> bool:
200 | '''Prüfen, ob Erfolg kleiner als der Antrag ist'''
201 | streitwert = decimal.Decimal(fenster.antrag.currentText().replace(",", "."))
202 | erfolg = decimal.Decimal(fenster.erfolg.currentText().replace(",", "."))
203 | if(erfolg > streitwert):
204 | fenster.fehlermeldung.setText(
205 | f"Der Erfolg muss kleiner oder gleich dem Antrag sein.")
206 | return False
207 | return True
208 |
209 | def stringZuGeld(wert:str)->decimal.Decimal:
210 | result:decimal.Decimal =decimal.Decimal(0)
211 | wert=wert.replace(",",".")
212 | if(wert.find(".")==len(wert)-2):
213 | wert=wert[:len(wert)-1]+'0'+wert[len(wert)-1:]
214 |
215 | return decimal.Decimal(wert)
216 |
217 |
218 | # Reaktionen auf Ereignisse (d.h. Änderungen in der Benutzeroberfläche wie Knöpfe, die angeklickt werden oder Felder, die ausgefüllt werden)
219 |
220 | def changed():
221 | '''Bei Eingabe: Prüfen, ob Eingabe in beiden Eingabefelder gültig ist, so dass der Berechnen-Knopf aktiviert werden kann'''
222 | fenster.fehlermeldung.setText("") # Text im Feld Fehlermeldung löschen
223 | fenster.berechnen.setEnabled(False) # Berechnen-Knopf deaktivieren
224 | if pruefeEuro(fenster.antrag.currentText(), "Antrag") and pruefeEuro(fenster.erfolg.currentText(), "Erfolg") and erfolgKleinerAntrag():
225 | # Berechnen-Knopf aktivieren, wenn alles in Ordnung ist
226 | fenster.berechnen.setEnabled(True)
227 |
228 | def calculate():
229 | if not pruefeEuro(fenster.antrag.currentText(), "Antrag"):
230 | fenster.antrag.setFocus()
231 | elif not pruefeEuro(fenster.erfolg.currentText(), "Erfolg") or not erfolgKleinerAntrag():
232 | fenster.erfolg.setFocus()
233 | else:
234 | knopfGedrueckt()
235 |
236 | def knopfGedrueckt():
237 | '''Berechnung durchführen und Ergebnis anzeigen'''
238 | global formulierungBruch
239 | global formulierungProzent
240 | streitwert: decimal.Decimal = stringZuGeld(fenster.antrag.currentText())
241 | erfolg: decimal.Decimal = stringZuGeld(fenster.erfolg.currentText())
242 | unterliegen: decimal.Decimal = streitwert-erfolg
243 | kostenKlaeger: float = float(unterliegen/streitwert*100)
244 | kostenBeklagter: float = 100-kostenKlaeger
245 |
246 |
247 |
248 | # Das folgende ist neu: Die Brüche werden jetzt gekürzt und vor allem als ganze Zahlen ausgewertet (d.h. nicht mehr 32,12/400,74)
249 | kostenKlaegerNenner: int = int(
250 | streitwert*100/ggt(int(unterliegen*100), int(streitwert*100))) # Um schöne Brüche auszugeben, kürzen wir automatisch
251 | kostenKlaegerZaehler: int = int(
252 | unterliegen*100/ggt(int(unterliegen*100), int(streitwert*100))) # Um schöne Brüche auszugeben, kürzen wir automatisch
253 | kostenBeklagterZaehler: int = int(
254 | erfolg*100/ggt(int(erfolg*100), int(streitwert*100))) # Um schöne Brüche auszugeben, kürzen wir automatisch
255 | kostenBeklagterNenner: int = int(
256 | streitwert*100/ggt(int(erfolg*100), int(streitwert*100))) # Um schöne Brüche auszugeben, kürzen wir automatisch
257 |
258 |
259 |
260 |
261 | fenster.ergebnis.append("
Einfache Streitwertberechnung
") # Hier wird der Text als HTML eingefügt - das erlaubt Formatierungen (z.B. im konkreten Fall als h3 = Überschrift 3)
262 |
263 | fenster.ergebnis.append(
264 | f"Die klagende Partei hat {locale.currency(streitwert)} beantragt, ihr wurden {locale.currency(erfolg)} zugesprochen - sie unterliegt also in Höhe von {locale.currency(unterliegen)}. das entspricht {locale.format_string('%.2f',kostenKlaeger).rstrip('00').rstrip(',')}% .
")
265 | #Die beiden Geldbeträge werden automatisch von Python richtig (Kommazahl, Euro) formatiert, wenn man die entsprechenden Zahlen an die Funktion locale.currency übergibt - diese wandelt also jede Zahl in einen String um, der diese als Euro-Betrag ausgibt
266 |
267 | if kostenKlaeger < 10: # Bei weniger als 10% der Kosten wenden die Gerichte in der Praxis § 92 Abs. 2 Nr. 2 ZPO an und legen die Kosten einer Partei allein auf
268 | formulierungBruch=f"Die Kosten des Rechtsstreits trägt die beklagte Partei."
269 | formulierungProzent=formulierungBruch
270 | fenster.ergebnis.append(f"{formulierungBruch}
")
271 | if kostenKlaeger > 0: # Klarstellender Hinweis ist nur relevant, falls der Kläger überhaupt irgendwelche Kosten tragen würde
272 | fenster.ergebnis.append(
273 | f"
(beachten Sie § 92 Abs. 2 Nr. 2 ZPO - hier müsste die klagende Partei sonst (nur) {locale.format_string('%.2g',kostenKlaeger)}% der Kosten tragen).
")
274 | elif kostenBeklagter < 10: # Bei weniger als 10% der Kosten wenden die Gerichte in der Praxis § 92 Abs. 2 Nr. 2 ZPO an und legen die Kosten einer Partei allein auf
275 | formulierungBruch=f"Die Kosten des Rechtsstreits trägt die klagende Partei."
276 | formulierungProzent=formulierungBruch
277 | fenster.ergebnis.append(f"{formulierungBruch}
")
278 | if kostenBeklagter > 0: # Klarstellender Hinweis ist nur relevant, falls der Beklagte überhaupt irgendwelche Kosten tragen würde
279 | fenster.ergebnis.append(
280 | f"
(beachten Sie § 92 Abs. 2 Nr. 2 ZPO - hier müsste die beklagte Partei sonst (nur) {locale.format_string('%.2g',kostenBeklagter)}% der Kosten tragen).
")
281 | else:
282 | formulierungBruch=f"Die Kosten des Rechtsstreits trägt die klagende Partei zu {kostenKlaegerZaehler}/{kostenKlaegerNenner} und die beklagte Partei zu {kostenBeklagterZaehler}/{kostenBeklagterNenner}."
283 | formulierungProzent=f"Die Kosten des Rechtsstreits trägt die klagende Partei zu {locale.format_string('%.2f',kostenKlaeger).rstrip('00').rstrip(',')}% und die beklagte Partei zu {locale.format_string('%.2f',kostenBeklagter).rstrip('00').rstrip(',')}% Prozent."
284 | fenster.ergebnis.append(
285 | f"{formulierungBruch}
")
286 | fenster.ergebnis.append(
287 | "
- oder (alternativer Tenor) -
")
288 | fenster.ergebnis.append(
289 | f"{formulierungProzent}
")
290 | fenster.ergebnis.append("
")
291 |
292 | fenster.copyFraction.setEnabled(True)
293 | fenster.copyPercent.setEnabled(True)
294 |
295 | # Fügt die Eingabe für den Antrag in die Archivliste des Eingabefeldes ein (falls man denselben Betrag noch einmal benötigt)
296 | fenster.antrag.addItem(fenster.antrag.currentText())
297 | # Fügt die Eingabe für den Erfolg in die Archivliste des Eingabefeldes ein (falls man denselben Betrag noch einmal benötigt)
298 | fenster.erfolg.addItem(fenster.erfolg.currentText())
299 | # Löscht den bisherigen Text im Eingabefeld für den Antrag
300 | fenster.antrag.setCurrentText("")
301 | # Löscht den bisherigen Text im Eingabefeld für den Erfolg
302 | fenster.erfolg.setCurrentText("")
303 | # Bewirkt, dass das Eingabefeld für den Antrag wieder aktiv wird (d.h., dass Sie dort als nächstes Text eingeben sollen)
304 | fenster.antrag.setFocus()
305 |
306 | def prozentKopieren():
307 | '''Kopiert das Ergebnis als Prozentangabe in die Zwischenablage'''
308 | clipboard:QClipboard=app.clipboard()
309 | mimeData:QMimeData=QMimeData()
310 | mimeData.setHtml(formulierungProzent)
311 | clipboard.setMimeData(mimeData)
312 |
313 | def bruchKopieren():
314 | '''Kopiert das Ergebnis als Bruch in die Zwischenablage'''
315 | clipboard:QClipboard=app.clipboard()
316 | mimeData:QMimeData=QMimeData()
317 | mimeData.setHtml(formulierungBruch)
318 | clipboard.setMimeData(mimeData)
319 |
320 | # Das ist das eigentliche Programm. Die folgenden Befehle müssen Sie *immer* bei grafischen Anwendungen ausführen. Nur der Name des Fensters ist ggf. ein anderer (hier heißt es Ui_Dialog, den Namen können Sie aber in QT-Designer anpassen)
321 | # Die eigentlichen Knöpfe etc. positionieren Sie mit QT Designer. Dadurch ändert sich nur der Teil oben, nicht aber die folgenden vier Zeilen. Merken Sie sich also diese Zeilen!
322 |
323 |
324 | locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8') # Deutsche Formatierung für Währungen und Kommazahlen
325 |
326 |
327 |
328 |
329 | # Die folgenden Zeilen sind für alle Programme mit grafischer Oberfläche im Wesentlichen gleich
330 | # Anwendung initialisieren (für Sie unsichtbar - alle Parameter von Python weiterleiten)
331 | app: QApplication = QApplication(sys.argv)
332 | x: QMainWindow = QMainWindow() # Fenster erstellen - QMainWindow ist eine Klasse von QT; wie diese funktioniert kann Ihnen egal sein. Kurz gesagt erstellt diese Zeile eine leere Seite, in die man Inhalte (die Sie in QT-Designer definiert haben) laden kann
333 | # konkretes Objekt mit dem oben definierten Fensterinhalt erstellen
334 | fenster: Ui_MainWindow = Ui_MainWindow()
335 | fenster.setupUi(x) # Fensterinhalt in das Fenster einfügen - das heißt, der gesamte Programmcode von oben (den QT-Designer erstellt hat) wird ausgeführt. Dadurch werden z.B. Eingabefelder, Knöpfe etc. eingefügt
336 |
337 | # Die folgenden beiden Funktionen dienen der Überprüfung der Eingabe, während Tasten im Feld gedrückt werden
338 | # Dies verknüpft (connect) das Ereignis "editTextChanged" (Text geändert) des Objekts "antrag" im Objekt "fenster" (siehe oben im von QT-Designer erstellten Programmcode: self.knopf = QPushButton(Dialog)) mit einer Funktion "changed", die Sie oben mit def erstellt haben
339 | fenster.antrag.editTextChanged.connect(changed)
340 | # Dies verknüpft (connect) das Ereignis "editTextChanged" (Text geändert) des Objekts "erfolg" im Objekt "fenster" (siehe oben im von QT-Designer erstellten Programmcode: self.knopf = QPushButton(Dialog)) mit einer Funktion "changed", die Sie oben mit def erstellt haben
341 | fenster.erfolg.editTextChanged.connect(changed)
342 |
343 | # Die folgende Zeile dient dem Beenden des Programms, wenn der entsprechende Menüeintrag gewählt wurde
344 | fenster.action_Beenden.triggered.connect(quit)
345 |
346 | # An dieser Stelle kommt der eigentliche Funktionsteil des Programms. Hier programmieren Sie sog. "Ereignisse" (Events), d.h. was passiert, wenn der Nutzer etwas macht
347 | # Dies verknüpft (connect) das Ereignis "clicked" (angeklickt) des Objekts "berechnen" im Objekt "fenster" (siehe oben im von QT-Designer erstellten Programmcode: self.knopf = QPushButton(Dialog)) mit einer Funktion "knopfGedrueckt", die Sie oben mit def erstellt haben
348 | fenster.berechnen.clicked.connect(knopfGedrueckt)
349 | fenster.copyPercent.clicked.connect(prozentKopieren) # Ersten Formulierungsvorschlag (mit Prozent) in Zwischenablage kopieren
350 | fenster.copyFraction.clicked.connect(bruchKopieren) # Zweiten Formulierungsvorschlag (mit Brüchen) in Zwischenablage kopieren
351 |
352 |
353 |
354 | enter = QShortcut(QKeySequence(Qt.Key_Return | Qt.Key_Enter), x) # Reaktion auf Druck der Entertaste vormerkens
355 | enter.activated.connect(calculate) # Entertaste mit Funktion calculate verbinden (d.h. die Funktion wird immer aufgerufen, wenn irgendwo im Programm die Entertaste gedrückt wird)
356 |
357 | x.show() # Fenster anzeigen (dies erfolgt immer als vorletzter Schritt - vergessen Sie diesen Befehl, ist ihr Programm "unsichtbar" und damit nutzlos)
358 |
359 |
360 |
361 |
362 | # Programm bis zum bitteren Ende ausführen (und am Ende den Rückgabewert von QT zurückliefern)
363 | sys.exit(app.exec())
364 |
--------------------------------------------------------------------------------
/01 - Kostenquote/README.md:
--------------------------------------------------------------------------------
1 | # Court Cost Calculator
2 | This first (and very simple) example (console-/text-based) calculates dvision of court and attorney fees in two party litigation under German Law (§ 92 ZPO).
--------------------------------------------------------------------------------
/02 - Mehrwertsteuer/Mehrwertsteuer.py:
--------------------------------------------------------------------------------
1 | # Programm 01: Kostenquote nach §§ 91, 92 ZPO im Zweipersonenverhältnis
2 | # (C) 13.04.2021, Michael Beurskens
3 |
4 |
5 | print("Dieses Programm berechnet Preise mit und ohne Umsatzsteuer (Brutto und Nettopreise)")
6 | betragText: str = input("Betrag (in Euro): ")
7 | steuersatzText: str = input("Steuersatz (in Prozent): ")
8 |
9 | betrag: float = round(float(betragText), 2)
10 | steuersatz: float = round(float(steuersatzText), 2)
11 | brutto: float = round(betrag*(100 + steuersatz)/100, 2)
12 | netto: float = round(betrag / ((100 + steuersatz)/100), 2)
13 | steuerBrutto: float = round(brutto-betrag, 2)
14 | steuerNetto: float = round(betrag-netto, 2)
15 | print("\n")
16 | print("-----------------------------------------------------------------------")
17 |
18 | print(f"{betrag:,.2f} € netto + {steuersatz}% \
19 | Umsatzsteuer ({steuerBrutto:,.2f} €) = {brutto:,.2f} € brutto.")
20 | print(f"{betrag:,.2f} € brutto = {netto:,.2f} € netto + {steuersatz}% \
21 | Umsatzsteuer ({steuerNetto:,.2f} €).")
22 | print("-----------------------------------------------------------------------")
23 |
--------------------------------------------------------------------------------
/02 - Mehrwertsteuer/README.md:
--------------------------------------------------------------------------------
1 | # Value Added Tax (VAT) Calculator
2 | This second (still very simple) example (console-/text-based) calculates the tax included in a price or to be added to it.
--------------------------------------------------------------------------------
/03 - Blutalkohol/Blutalkohol.py:
--------------------------------------------------------------------------------
1 | # Programm 03: Blutalkoholkonzentration nach der Widmark-Formel
2 | # (C) 20.04.2021, Michael Beurskens
3 |
4 | alkoholDichte: float = 0.8
5 | print("Dieses Programm berechnet die Alkoholkonzentration im Blut nach der sog. Widmark-Formel.")
6 | print("\n")
7 | volumenText: str = input("Wie viel Milliliter haben Sie getrunken? ")
8 | alkoholText: str = input("Wie viel Alkohol enthielt das Getränk in Prozent? ")
9 | gewichtText: str = input("Wie viel wiegen Sie in Kilogramm? ")
10 | resorptionText: str = input("Geben Sie jetzt den sog. Verteilungsfaktor ein - \
11 | dieser beträgt für Männer 0.7, für Frauen 0.6 oder für Kinder 0.8: ")
12 |
13 | volumen: float = float(volumenText)
14 | alkohol: float = float(alkoholText)/100
15 | gewicht: float = float(gewichtText)
16 | resorption: float = float(resorptionText)
17 | alkoholMasse: float = volumen*alkohol*alkoholDichte
18 | blutalkohol: float = alkoholMasse/(gewicht*resorption)
19 | print("\n")
20 | print("---------------------------------------------------")
21 | print("Masse aufgenommenen Alkohols = Getränkemenge in Milliliter * Alkoholvolum in Prozent * Dichte von Alkohol")
22 | print("<=> %.2f g = %.2f ml * %.2f %% * %.2f g/ml" %
23 | (alkoholMasse, volumen, alkohol*100, alkoholDichte))
24 | print("\n")
25 | print("Promille im Körper = Masse aufgenommenen Alkohols / (Gewicht * Verteilungsfaktor)")
26 | print("<=> %.2f ‰ = %.2f g / (%.2f kg * %.2f)" %
27 | (blutalkohol, alkoholMasse, gewicht, resorption))
28 | print("---------------------------------------------------")
29 |
--------------------------------------------------------------------------------
/03 - Blutalkohol/README.md:
--------------------------------------------------------------------------------
1 | # Blood Alcohol Calculator
2 |
3 | This third (still very simple) example (console-/text-based) calculates the amount of alcohol remaining in the blood circuit after drinking a certain amount.
--------------------------------------------------------------------------------
/04 - Minderung/Minderung.py:
--------------------------------------------------------------------------------
1 | # Minderung im Kaufrecht
2 | # (C) 20.04.2021 Michael Beurskens
3 |
4 | def GeldBetrag(text: str) -> float:
5 | "Wandelt Text-Eingaben in gültige Geldbeträge mit zwei Nachkommastellen um; Erklärung für den Nutzer durch Parameter text"
6 | while True:
7 | wert = input(text)
8 | # Wenn die Zahl mit Komma statt mit Punkt eingegeben wurde
9 | wert = wert.replace(",", ".")
10 | try:
11 | eingabe = float(wert)
12 | if(eingabe < 0):
13 | print(
14 | "Bitte eine positive Zahl eingeben (Zahlungen können nicht negativ sein).")
15 | else:
16 | # Wenn bei Runden eine Nachkommastelle wegfällt, hat die Zahl offenbar mehr als zwei Nachkommastellen
17 | if(round(eingabe, 2) != eingabe):
18 | print(
19 | "Bitte maximal zwei Nachkommastellen eingeben (ein Euro hat maximal 100 Cent).")
20 | else:
21 | break
22 | except:
23 | print(
24 | "Bitte geben Sie eine Ganzzahl (100...) oder eine Kommazahl (20.40) ein.")
25 | return eingabe
26 |
27 |
28 | while True:
29 | # Kaufpreis muss > 0 sein
30 | while True: # Kaufpreis muss größer Null sein
31 | print("-------------------------------------------------------------------------------------------------------------")
32 | print("Um die Minderung nach § 441 Abs. 3 S. 1 BGB zu berechnen brauchen wir drei Angaben: Den vereinbarten Kaufpreis, den wahren Wert des Kaufgegenstands mit Mangel und den hypothetischen Wert ohne Mangel.")
33 | print("\n")
34 | Kaufpreis: float = GeldBetrag("Geben Sie den Kaufpreis in Euro ein: ")
35 | if(Kaufpreis == 0):
36 | print("Der Kaufpreis muss größer Null sein (sonst liegt eine Schenkung vor).")
37 | else:
38 | break
39 |
40 | # Keine zusätzliche Bedingung für wahren Wert (es reicht ein beliebiger Geldbetrag)
41 | WahrerWert: float = GeldBetrag(
42 | "Geben Sie den wahren Wert (mit Mangel) in Euro ein: ")
43 |
44 | # Wahrer Wert muss größer oder gleich dem hypothetischen Wert sein (keine "Herabsetzung" bei Wertsteigerung
45 | while True:
46 | HypoWert: float = GeldBetrag(
47 | "Geben Sie den hypothetischen Wert (ohne Mangel) in Euro ein: ")
48 | if(HypoWert < WahrerWert):
49 | print(
50 | "Der hypothetische Wert (ohne Mangel) muss größer als der wahre Wert (mit Mangel) sein.")
51 | else:
52 | break
53 |
54 | print("\n")
55 | print("Geminderter Kaufpreis (%.2f Euro) = Vereinbarter Kaufpreis (%.2f Euro) * Wahrer Wert mit Mangel (%.2f Euro) / Hypothetischer Wert ohne Mangel (%.2f Euro)" %
56 | (round(Kaufpreis*WahrerWert/HypoWert, 2), Kaufpreis, WahrerWert, HypoWert))
57 | print("Der zurückzuzahlende Minderungsbetrag ist %.2f Euro." %
58 | (Kaufpreis-(round(Kaufpreis*WahrerWert/HypoWert, 2))))
59 | print("\n")
60 | if(input("Wollen Sie noch einen weiteren Minderungsbetrag berechnen (J für Ja)? ").lower()[:1] != "j"):
61 | break
62 |
--------------------------------------------------------------------------------
/04 - Minderung/README.md:
--------------------------------------------------------------------------------
1 | # Price Reduction in sales law
2 |
3 | This third (again: simple) example calculates the reduced price under German Sales Law
--------------------------------------------------------------------------------
/05 - Examensnote/Examensnote.py:
--------------------------------------------------------------------------------
1 | # Programm: Berechnung der Gesamtnotenstufe und Punktzahl nach § 17 Abs. 1 JAPO
2 | # (C), 26.04.2012, Valerie Pichlmayr
3 |
4 | # Funktion:
5 | def GesamtNotenstufe(Note: float) -> str:
6 | if Note >= 14.00:
7 | return "sehr gut (%.2f Punkte)" % Note
8 | elif Note >= 11.50:
9 | return "gut (%.2f Punkte)" % Note
10 | elif Note >= 9.00:
11 | return "vollbefriedigend (%.2f Punkte)" % Note
12 | elif Note >= 6.50:
13 | return "befriedigend (%.2f Punkte)" % Note
14 | elif Note >= 4.00:
15 | return "ausreichend (%.2f Punkte)" % Note
16 | elif Note >= 1.50:
17 | return "mangelhaft (%.2f Punkte)" % Note
18 | else:
19 | return f"ungenügend ({Note:.2f} Punkte)"
20 |
21 |
22 | def Einzelnotenstufe(Note: float) -> str:
23 | if Note >= 16:
24 | return "sehr gut (%.2f Punkte)" % Note
25 | elif Note >= 13:
26 | return "gut (%.2f Punkte)" % Note
27 | elif Note >= 10:
28 | return "vollbefriedigend (%.2f Punkte)" % Note
29 | elif Note >= 7:
30 | return "befriedigend (%.2f Punkte)" % Note
31 | elif Note >= 4:
32 | return "ausreichend (%.2f Punkte)" % Note
33 | elif Note >= 1:
34 | return "mangelhaft (%.2f Punkte)" % Note
35 | else:
36 | return "ungenügend (%.2f Punkte)" % Note
37 |
38 |
39 | def ErlasseneKlausuren() -> int:
40 | while True:
41 | eingabe: str = input(
42 | "Wie viele Klausuren wurden Ihnen erlassen (0 bis 2) ")
43 | try:
44 | zahl: int = int(eingabe)
45 | if(zahl >= 0) and (zahl <= 2):
46 | return zahl
47 | else:
48 | print(
49 | "Die Zahl der erlassenen Klausuren darf nur zwischen 0 und 2 liegen.")
50 | except:
51 | print("Bitte geben Sie ausschließlich Zahlen (zwischen 0 und 2) ein.")
52 |
53 |
54 | def NotenEingabeGanz(text: str) -> int:
55 | while True:
56 | eingabe: str = input(text)
57 | try:
58 | zahl: int = int(eingabe)
59 | if(zahl >= 0) and (zahl <= 18):
60 | return zahl
61 | else:
62 | print("Noten dürfen nur zwischen 0 und 18 liegen.")
63 | except:
64 | print("Bitte geben Sie ausschließlich Zahlen (zwischen 0 und 18) ein.")
65 |
66 |
67 | def NotenEingabeKomma(text: str) -> float:
68 | while True:
69 | eingabe: str = input(text)
70 | eingabe = eingabe.replace(",", ".")
71 | try:
72 | zahl: float = float(eingabe)
73 | if(zahl >= 0) and (zahl <= 18):
74 | if zahl % 0.5 == 0:
75 | return zahl
76 | else:
77 | print("Die Note darf nur um 0.5 zwischen zwei Stufen liegen.")
78 | else:
79 | print("Noten dürfen nur zwischen 0 und 18 liegen.")
80 | except:
81 | print("Bitte geben Sie ausschließlich Zahlen (zwischen 0 und 18) ein.")
82 |
83 |
84 | print("Dieses Programm berechnet Ihre Gesamtnote für das Erste Staatsexamen (bestehend aus EJS und JUP)\n\n\n")
85 | print("\n")
86 | print("--- EJS: Klausurnoten ---")
87 | print("\n")
88 |
89 |
90 | klausurzaehler: int = 1
91 | erlassenenKlausuren: int = ErlasseneKlausuren()
92 | summeKlausuren: float = 0
93 | anzahlUnter40: int = 0
94 |
95 | maximalErgebnis: float = (6-erlassenenKlausuren)*18
96 | gesamtNoteStaatlich: float = 0
97 |
98 | # Klausuren
99 |
100 | while(klausurzaehler < 7-erlassenenKlausuren):
101 | klausurNote: float = NotenEingabeKomma(
102 | f"Geben Sie die Note der {klausurzaehler}. Klausur ein: ")
103 |
104 | print(
105 | f"Sie haben in der {klausurzaehler}. Klausur die Note {Einzelnotenstufe(klausurNote)} erzielt.")
106 |
107 | klausurzaehler += 1
108 | summeKlausuren += klausurNote
109 |
110 | maximalErgebnis -= 18
111 | maximalErgebnis += klausurNote
112 |
113 | if(klausurNote < 4.0):
114 | anzahlUnter40 += 1 # <=> anzahlUnter40=anzahlUnter40+1
115 | print(
116 | f"Sie können nach dieser Klausur noch maximal {maximalErgebnis/(6-erlassenenKlausuren)} Punkte erzielen.")
117 |
118 | if(anzahlUnter40 > 3):
119 | print("Sie können ohnehin nicht mehr bestehen, da Sie mehr als 3 Klausuren nicht bestanden haben.")
120 | break
121 |
122 | if(maximalErgebnis/(6-erlassenenKlausuren) < 3.8):
123 | print(
124 | "Sie können nicht mehr als 3.8 Punkte erreichen und sind damit durchgefallen.")
125 | break
126 |
127 |
128 | durchschnittsnoteKlausuren: float = summeKlausuren/(6-erlassenenKlausuren)
129 |
130 | print(
131 | f"Die Gesamtnote der Klausuren ist {GesamtNotenstufe(durchschnittsnoteKlausuren)}.")
132 |
133 |
134 | print("\n")
135 | print("--- EJS: mündliche Prüfung ---")
136 | print("\n")
137 |
138 | # Mündliche Prüfung
139 | if durchschnittsnoteKlausuren < 3.8 \
140 | or ((erlassenenKlausuren != 2) and anzahlUnter40 > 3) \
141 | or ((erlassenenKlausuren == 2) and anzahlUnter40 > 2):
142 | print("Sie wurden nicht zur mündlichen Prüfung zugelassen. Sie haben damit die EJS nicht bestanden.")
143 | else:
144 | print("Sie wurden zur mündlichen Prüfung zugelassen.")
145 | summeMuendlich: int = NotenEingabeGanz(
146 | "Welche Note haben Sie in der mündlichen Prüfung im Zivilrecht erreicht? ")
147 | summeMuendlich += NotenEingabeGanz(
148 | "Welche Note haben Sie in der mündlichen Prüfung im Strafrecht erreicht? ")
149 | summeMuendlich += NotenEingabeGanz(
150 | "Welche Note haben Sie in der mündlichen Prüfung im Öffentlichen Recht erreicht? ")
151 | durchschnittsnoteMuendlich: float = summeMuendlich/3
152 | print(
153 | f"Die Gesamtnote der mündlichen Prüfung ist {GesamtNotenstufe(durchschnittsnoteMuendlich)}.")
154 | gesamtNoteStaatlich = (
155 | durchschnittsnoteKlausuren*3+durchschnittsnoteMuendlich)/4
156 | print(
157 | f"Die Gesamtnote der staatlichen Prüfung ist {GesamtNotenstufe(gesamtNoteStaatlich)}.")
158 |
159 |
160 | print("\n")
161 | print("--- JUP ---")
162 | print("\n")
163 |
164 | # Universitätsprüfung
165 |
166 | schwerpunktKlausur: float = NotenEingabeKomma(
167 | "Welche Note haben Sie in der Schwerpunktklausur erzielt? ")
168 | schwerpunktSeminarMuendlich: int = NotenEingabeGanz(
169 | "Welche mündliche Note haben Sie im Seminar erzielt? ")
170 | schwerpunktSeminarArbeit: float = NotenEingabeKomma(
171 | "Welche Note haben Sie in der Seminararbeit erzielt? ")
172 |
173 | print(
174 | f"Die Gesamtnote für die Teilleistung 1 beträgt {GesamtNotenstufe( (schwerpunktSeminarArbeit*2/3 + schwerpunktSeminarMuendlich/3))}")
175 |
176 | gesamtNoteSchwerpunkt: float =\
177 | schwerpunktKlausur*0.4 + \
178 | (schwerpunktSeminarArbeit*2/3 + schwerpunktSeminarMuendlich/3)*0.6
179 |
180 | print(
181 | f"Die Gesamtnote im Schwerpunktbereich beträgt {GesamtNotenstufe(gesamtNoteSchwerpunkt)}")
182 |
183 | if(gesamtNoteSchwerpunkt < 4.0):
184 | print("Damit haben Sie die Juristische Universitätsprüfung nicht bestanden.")
185 |
186 |
187 | print("\n")
188 | print("--- Gesamtnote ---")
189 | print("\n")
190 |
191 | if(gesamtNoteStaatlich > 4.0 and gesamtNoteSchwerpunkt > 4.0):
192 | print(
193 | f"Die Gesamtnote der Ersten Juristischen Prüfung (staatlicher Teil und Universitätsteil) beträgt {GesamtNotenstufe(gesamtNoteSchwerpunkt*0.3+gesamtNoteStaatlich*0.7)}")
194 | else:
195 | print("Sie haben die Prüfung bislang noch nicht bestanden.")
196 |
--------------------------------------------------------------------------------
/05 - Examensnote/README.md:
--------------------------------------------------------------------------------
1 | # Exam Grade Calculator
2 |
3 | This is a very simple (console-/text-based) tool for calculating grades in State Examinations in Bavaria under the recent applicable rules (JAPO).
--------------------------------------------------------------------------------
/06 - GUI/(a) In einer Datei.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | ################################################################################
4 | ## Form generated from reading UI file 'designeravlkQV.ui'
5 | ##
6 | ## Created by: Qt User Interface Compiler version 5.15.2
7 | ##
8 | ## WARNING! All changes made in this file will be lost when recompiling UI file!
9 | ################################################################################
10 |
11 |
12 |
13 | # Die folgenden Zeilen laden bestimmte (notwendige) Klassen, um grafische Oberflächen anzuzeigen
14 | # Sie müssen diese nicht selber schreiben, da sie von QT-Designer automatisch erstellt werdne
15 | from PyQt5.QtCore import * # Alle Klassen aus QtCore importieren, so dass man sie benutzen kann
16 | from PyQt5.QtGui import * # Alle Klassen aus QtGui importieren, so dass man sie benutzen kann
17 | from PyQt5.QtWidgets import * # Alle Klassen aus QtWidgets importieren, so dass man sie benutzen kann
18 | import sys
19 |
20 |
21 | # Diesen Teil des Programms erstellt QT-Designer automatisch; am Besten hier nichts ändern!
22 | class Ui_Dialog(object):
23 | def setupUi(self, Dialog):
24 | if not Dialog.objectName():
25 | Dialog.setObjectName(u"Dialog")
26 | Dialog.resize(668, 595)
27 | Dialog.setAutoFillBackground(True)
28 | self.knopf = QPushButton(Dialog)
29 | self.knopf.setObjectName(u"knopf")
30 | self.knopf.setGeometry(QRect(410, 130, 75, 23))
31 | self.knopf.setAutoDefault(True)
32 | self.knopf.setFlat(False)
33 | self.eingabeExamensnote = QLineEdit(Dialog)
34 | self.eingabeExamensnote.setObjectName(u"eingabeExamensnote")
35 | self.eingabeExamensnote.setGeometry(QRect(30, 130, 381, 20))
36 | self.ausgabefeld = QTextBrowser(Dialog)
37 | self.ausgabefeld.setObjectName(u"ausgabefeld")
38 | self.ausgabefeld.setGeometry(QRect(30, 160, 461, 281))
39 | self.ausgabefeld.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
40 | self.ausgabefeld.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
41 |
42 | self.retranslateUi(Dialog)
43 |
44 | QMetaObject.connectSlotsByName(Dialog)
45 | # setupUi
46 |
47 | def retranslateUi(self, Dialog):
48 | Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None))
49 | self.knopf.setText(QCoreApplication.translate("Dialog", u"Anzeigen", None))
50 | self.eingabeExamensnote.setText("")
51 | self.eingabeExamensnote.setPlaceholderText(QCoreApplication.translate("Dialog", u"Examensnote eingeben", None))
52 | # retranslateUi
53 |
54 |
55 | def knopfGedrueckt():
56 | fenster.ausgabefeld.insertPlainText(fenster.eingabeExamensnote.text())
57 |
58 |
59 |
60 |
61 | # Das ist das eigentliche Programm. Die folgenden Befehle müssen Sie *immer* bei grafischen Anwendungen ausführen. Nur der Name des Fensters ist ggf. ein anderer (hier heißt es Ui_Dialog, den Namen können Sie aber in QT-Designer anpassen)
62 | # Die eigentlichen Knöpfe etc. positionieren Sie mit QT Designer. Dadurch ändert sich nur der Teil oben, nicht aber die folgenden vier Zeilen. Merken Sie sich also diese Zeilen!
63 |
64 | app :QApplication = QApplication(sys.argv) # Anwendung initialisieren (für Sie unsichtbar - alle Parameter von Python weiterleiten)
65 | x:QDialog=QDialog() # Fenster erstellen - QDialog ist eine Klasse von QT; wie diese funktioniert kann Ihnen egal sein. Kurz gesagt erstellt diese Zeile eine leere Seite, in die man Inhalte (die Sie in QT-Designer definiert haben) laden kann
66 | fenster: Ui_Dialog = Ui_Dialog() # konkretes Objekt mit dem oben definierten Fensterinhalt erstellen
67 | fenster.setupUi(x) # Fensterinhalt in das Fenster einfügen - das heißt, der gesamte Programmcode von oben (den QT-Designer erstellt hat) wird ausgeführt. Dadurch werden z.B. Eingabefelder, Knöpfe etc. eingefügt
68 |
69 |
70 | # An dieser Stelle kommt der eigentliche Funktionsteil des Programms. Hier programmieren Sie sog. "Ereignisse" (Events), d.h. was passiert, wenn der Nutzer etwas macht
71 | fenster.knopf.clicked.connect(knopfGedrueckt) # Dies verknüpft (connect) das Ereignis "clicked" (angeklickt) des Objekts "knopf" im Objekt "fenster" (siehe oben im von QT-Designer erstellten Programmcode: self.knopf = QPushButton(Dialog)) mit einer Funktion "knopfGedrueckt", die Sie oben mit def erstellt haben
72 | #fenster.eingabeExamensnote.textChanged.connect(knopfGedrueckt) # Diese Zeile verknüpft (connect) das Ereignis "textChanged" des Objekts "eingabeEamensnote" im Objekt "fenster" (siehe oben im von QT-Designer erstellten Programmcode: self.eingabeExamensnote = QLineEdit(Dialog)) mit einer Funktion "knopfGedrueckt", die Sie oben mit def erstellt haben
73 |
74 |
75 | x.show() # Fenster anzeigen (dies erfolgt immer als vorletzter Schritt - vergessen Sie diesen Befehl, ist ihr Programm "unsichtbar" und damit nutzlos)
76 | sys.exit(app.exec()) # Programm bis zum bitteren Ende ausführen (und am Ende den Rückgabewert von QT zurückliefern)
--------------------------------------------------------------------------------
/06 - GUI/(b) In zwei Dateien.py:
--------------------------------------------------------------------------------
1 | # GUI-Test: Beispiel für eine grafische Benutzeroberfläche
2 | # (C) 22.04.2021, Michael Beurskens
3 |
4 | import sys # Systemfunktionen: Erforderlich um Befehlszeilenparameter auszulesen und einen Rückgabewert zu liefern
5 |
6 | from PyQt5.QtWidgets import QApplication, QDialog, QDialogButtonBox # Wichtige Funktionen für die grafische Benutzeroberfläche
7 | from FensterDatei import Ui_Dialog # Selbst gestaltetete Oberfläche (generiert von QT Designer) - anders als bei "fenster.py" wurde diese hier in einer eigenen Datei ("Ui_GUI.py") gespeichert, so dass hier nur die eigenen Befehle stehen
8 |
9 |
10 |
11 |
12 | # Funktionen, die auf Änderungen in der Oberfläche reagieren
13 |
14 | def check():
15 | """Prüft, ob die Eingabe gültig ist"""
16 | if(ui.spinBox.value() > 0): # Positiver Wert eingegeben
17 | ui.pushButton.setEnabled(True) # Knopf wird aktiviert
18 | else: # sonst (negativer Wert)
19 | ui.pushButton.setEnabled(False) # Knopf wird deaktiviert
20 |
21 |
22 | def update():
23 | """Aktualisiert die Liste und setzt das Eingabefeld zurück"""
24 | ui.textBrowser.insertPlainText(
25 | str(ui.spinBox.value())+"\n") # Neue Zeile in die Textanzeige einfügen
26 | # Wert auf Null zurücksetzen
27 | ui.spinBox.setValue(0)
28 | ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(
29 | (True)) # Ok-Knopf aktivieren
30 |
31 |
32 | # Das eigentliche Programm beginnt hier
33 |
34 | # Diese Zeile stellt sicher, dass die folgenden Befehle nur ausgeführt werden, wenn dieses Programm direkt gestartet (und nicht nur importiert) wird
35 | if __name__ == '__main__':
36 |
37 | # Initialisiert die grafische Oberfläche und gibt ihr die zusätzlichen Argumente, die beim Start angegeben wurden
38 | app: QApplication = QApplication(sys.argv)
39 | w: QDialog = QDialog() # Öffnet ein neues Fenster
40 | # Generiert die Inhalte des Fensters anhand der Vorgaben, die mit QT Designer erstellt wurden
41 | ui: Ui_Dialog = Ui_Dialog()
42 | ui.setupUi(w) # Fügt die einzelnen Elemente zum Fenster hinzu
43 |
44 | # Funktionen des Programms
45 | ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(
46 | (False)) # OK-Knopf deaktivieren
47 | # Bei Änderungen des Eingabefelds Knopf aktivieren oder deaktivieren
48 | ui.spinBox.valueChanged.connect(check)
49 | # Bei Anklicken des Knopfes Inhalt des Eingabefelds an Liste anhängen und OK-Knopf aktivieren
50 | ui.pushButton.clicked.connect(update)
51 |
52 | # Rückgabewert bei Schließen des Fensters durch Klick auf "OK" ausgeben
53 | w.accepted.connect(lambda: print("Ok"))
54 | # Rückgabewert bei Schließen des Fensters durch Klick auf "Abbrechen" ausgeben
55 | w.rejected.connect(lambda: print("Abbruch"))
56 |
57 | w.show() # Fenster anzeigen
58 |
59 | # Beim Schließen des Fensters das Programm beenden und als Rückgabewert die Reaktion der grafischen Oberfläche weiterreichen
60 | sys.exit(app.exec_())
61 |
--------------------------------------------------------------------------------
/06 - GUI/FensterDatei.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'c:\Users\beurs\Skriptsprachen\PythonForLawyers\06 - GUI\GUI.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.15.2
6 | #
7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is
8 | # run again. Do not edit this file unless you know what you are doing.
9 |
10 |
11 | from PyQt5 import QtCore, QtGui, QtWidgets
12 |
13 |
14 | class Ui_Dialog(object):
15 | def setupUi(self, Dialog):
16 | Dialog.setObjectName("Dialog")
17 | Dialog.resize(454, 300)
18 | Dialog.setSizeGripEnabled(True)
19 | self.horizontalLayout = QtWidgets.QHBoxLayout(Dialog)
20 | self.horizontalLayout.setObjectName("horizontalLayout")
21 | self.verticalLayout = QtWidgets.QVBoxLayout()
22 | self.verticalLayout.setObjectName("verticalLayout")
23 | self.frame = QtWidgets.QFrame(Dialog)
24 | self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
25 | self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
26 | self.frame.setObjectName("frame")
27 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.frame)
28 | self.horizontalLayout_3.setObjectName("horizontalLayout_3")
29 | self.spinBox = QtWidgets.QSpinBox(self.frame)
30 | self.spinBox.setObjectName("spinBox")
31 | self.horizontalLayout_3.addWidget(self.spinBox)
32 | self.pushButton = QtWidgets.QPushButton(self.frame)
33 | self.pushButton.setEnabled(False)
34 | self.pushButton.setObjectName("pushButton")
35 | self.horizontalLayout_3.addWidget(self.pushButton)
36 | self.verticalLayout.addWidget(self.frame)
37 | self.textBrowser = QtWidgets.QTextBrowser(Dialog)
38 | self.textBrowser.setObjectName("textBrowser")
39 | self.verticalLayout.addWidget(self.textBrowser)
40 | self.horizontalLayout.addLayout(self.verticalLayout)
41 | self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
42 | self.buttonBox.setOrientation(QtCore.Qt.Vertical)
43 | self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
44 | self.buttonBox.setObjectName("buttonBox")
45 | self.horizontalLayout.addWidget(self.buttonBox)
46 |
47 | self.retranslateUi(Dialog)
48 | self.buttonBox.accepted.connect(Dialog.accept)
49 | self.buttonBox.rejected.connect(Dialog.reject)
50 | QtCore.QMetaObject.connectSlotsByName(Dialog)
51 |
52 | def retranslateUi(self, Dialog):
53 | _translate = QtCore.QCoreApplication.translate
54 | Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
55 | self.pushButton.setText(_translate("Dialog", "PushButton"))
56 |
--------------------------------------------------------------------------------
/06 - GUI/GUI.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 454
10 | 300
11 |
12 |
13 |
14 | Beispielprogramm
15 |
16 |
17 | true
18 |
19 |
20 | -
21 |
22 |
-
23 |
24 |
25 | QFrame::StyledPanel
26 |
27 |
28 | QFrame::Raised
29 |
30 |
31 |
-
32 |
33 |
34 | -
35 |
36 |
37 | false
38 |
39 |
40 | Übernehmen
41 |
42 |
43 |
44 |
45 |
46 |
47 | -
48 |
49 |
50 |
51 |
52 | -
53 |
54 |
55 | Qt::Vertical
56 |
57 |
58 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | buttonBox
68 | accepted()
69 | Dialog
70 | accept()
71 |
72 |
73 | 370
74 | 243
75 |
76 |
77 | 157
78 | 274
79 |
80 |
81 |
82 |
83 | buttonBox
84 | rejected()
85 | Dialog
86 | reject()
87 |
88 |
89 | 396
90 | 249
91 |
92 |
93 | 286
94 | 274
95 |
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/06 - GUI/README.md:
--------------------------------------------------------------------------------
1 | # Example for designing a graphical User Interface using QT-Designer and PyQT5
2 |
3 | This folder requires installation of
4 | * PYQT5 (pip3 install pyqt5)
5 | * PYQT5-tools (pip3 install pyqt5-tools)
6 |
7 | It is recommended to use the PyQT Integration Extension by Feng Zhou (https://marketplace.visualstudio.com/items?itemName=zhoufeng.pyqt-integration) and setup paths in @ext:zhoufeng.pyqt-integration (Settings / Extensions / PYQT-Integration) - this allows for direct compilation of QT-Designer-files in Visual Studio (and one click editing of UI-files in Designer). It is also possible to Copy & Paste Code from Designer itself (but unnecessarily cumbersome).
8 |
9 | This directory includes two versions of the example (with identical code and functionality, but a different split among files to illustrate imports):
10 | * (a) In einer Datei.py (UI and code in a single file)
11 | * (b) In zwei Dateien.py (UI in a separate file, FensterDatei.py)
12 |
13 | The program itself does nothing exciting (It copies text from the input field to the textlister at the bottom). It is meant to illustrate how to transfer text-based tools to GUI (which is the homework for the following week)
--------------------------------------------------------------------------------
/06 - GUI/__pycache__/Ui_GUI.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beursken/PythonForLawyers/395f4297cca2ad0cd08ffd842396224c3eb99675/06 - GUI/__pycache__/Ui_GUI.cpython-39.pyc
--------------------------------------------------------------------------------
/07 - Daten/README.MD:
--------------------------------------------------------------------------------
1 | # Two Examples for dealing with Dates in Python
2 |
3 | * feiertage.py - Checks whether any given date is a holiday in a specified German Land
4 | * fristen.py - Calculates terms determined by weeks (not taking into account holidays)
--------------------------------------------------------------------------------
/07 - Daten/feiertage.py:
--------------------------------------------------------------------------------
1 | # Feiertagschecker - Prüfung, ob ein bestimmtes Datum in einem bestimmten Bundesland ein Feiertag ist
2 | # Hierzu wird (a) auf eine Webseite zugegriffen und (b) eine XML-Datei ausgewertet
3 | # Die Schnittstelle ist unter https://www.spiketime.de/blog/spiketime-feiertag-api-feiertage-nach-bundeslandern/ dokumentiert
4 | # (C) 11.05.2021, Michael Beurskens
5 |
6 | from datetime import date,timedelta,datetime # Funktionen für den Umgang mit Daten (siehe auch fristen.py)
7 | from urllib.request import urlopen, Request # Um Daten aus dem Internet abzurufen
8 | import xml.etree.ElementTree as ET # Funktionen zur Verarbeitung von XML-Daten
9 |
10 |
11 | def inputDate(text:str)->date:
12 | '''Datum abfragen (bis gültige Eingabe erfolgt)'''
13 | while True: # Endlosschleife
14 | value:str=input(text).replace(" ","").replace("-",".").replace("/",".").replace(",","") # Text einlesen; dabei alle Striche, Punkte und Leerzeichen entfernen
15 | try:
16 | enteredDate:date=datetime.strptime(value, '%d.%m.%Y') # Umwandlungsfunktion: Formatiertes Datum. Was die %-Angaben bedeuten sieht man unter https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes (hier: Tag.Monat.Jahr (4-stellig))
17 | return enteredDate
18 | except:
19 | print("Bitte geben Sie ein gültiges Datum mit vierstelligem Jahr (01.01.2020) ein.") # Fehlermeldung bei ungültiger Eingabe
20 |
21 | def inputContinue()->bool:
22 | '''Programm beenden, wenn J, Ja, j, jau, etc. eingegeben wird'''
23 | value:str=input("Drücken Sie jetzt ""J"", um das Programm zu beenden: ")
24 | if len(value)>0 and value.lower()[0]=='j': # Eingabe in Kleinbuchstaben umwandeln und nur ersten Buchstaben auf "j" prüfen
25 | return True
26 | else:
27 | return False
28 |
29 |
30 | def getBundesLand()->str:
31 | '''Eingabe eines Bundeslandes und Prüfung, ob dies gültig ist'''
32 | url=urlopen(
33 | Request("https://www.spiketime.de/feiertagapi/bundeslaender",
34 | headers={"Accept": "application/xml"})
35 | , data=None)
36 |
37 | # Lade eine Datei aus dem Internet ("urlopen") - dabei wichtig die Angaben hinter Request: Zunächst die Adresse ("url") und dann die akzeptierten Datenformate (hier nur: "XML")
38 | # Diese Vorgaben kann man eigentlich immer nutzen, wenn es um strukturierte Daten geht
39 | # Schließlich: Keine Parameter übermitteln.
40 |
41 | namespaces={'ft': 'http://schemas.datacontract.org/2004/07/FeiertagAPI'} # Verwendete Namespaces (Beschreibungen) in der XML-Datei (siehe erste Zeile unte https://www.spiketime.de/feiertagapi/bundeslaender : xmlns="http://schemas.datacontract.org/2004/07/FeiertagAPI")
42 |
43 | document:ET.ElementTree = ET.parse(url) # Geladene Seite in interne Datenstrukturen vom Typ ElementTree umwandeln
44 | laender:ET.Element = document.getroot() # Ersten Knoten der Datei ("" und alle Unterknoten öffnen)
45 |
46 | while True: # Endlosschleife
47 | value:str=input("Bitte geben Sie die Abkürzung des betroffenen Bundeslandes ein: ").lower().replace(" ","") # In Kleinbuchstaben umwandeln und Leerzeichen entfernen
48 | for land in laender: # Prüfe alle Unterpunkte des Ersten Knotens (also ... siehe https://www.spiketime.de/feiertagapi/bundeslaender)
49 | if land.find("ft:Name",namespaces).text.lower()==value or land.find("ft:Abkuerzung",namespaces).text.lower()==value: # Jeweils den Text der Unter-Unterknoten und mit der Eingabe vergleichen
50 | return str(land.find("ft:Abkuerzung",namespaces).text) # Wenn Treffer: Abkürzung als Ergebnis liefern (man kann also den vollen Namen oder eine Abkürzung eingeben; Groß- und Kleinschreibung sind egal)
51 | print("Dieses Bundesland ist unbekannt. Zulässige Eingabewerte sind:") # Sinnvolle Fehlermeldung mit Hinweis auf zulässige Eingaben
52 | for land in laender:
53 | print(f"{land.find('ft:Name',namespaces).text} ({land.find('ft:Abkuerzung',namespaces).text})") # Formatierte Liste: Für jedes Bundesland Name und dahinter in Klammern die Abkürzung
54 | return value
55 |
56 | def getBundeslandLang(bundesLandKurz:str)->str:
57 | '''Abkürzung eines Bundeslandes in vollen Namen konvertieren über Zugriff auf Webservice'''
58 | url=urlopen(
59 | Request("https://www.spiketime.de/feiertagapi/bundeslaender",
60 | headers={"Accept": "application/xml"})
61 | , data=None) #
62 |
63 | # Wie oben: Lade aus dem Internet (urlopen) mit den VOrgaben unter Request (Adresse=URL und Header für gewünschten Inhaltstyp) und ohne zu übermittelnde Daten
64 |
65 |
66 | namespaces={'ft': 'http://schemas.datacontract.org/2004/07/FeiertagAPI'} # Wie oben: Verwendete Typbezeichnungen (siehe erste Zeile der Datei hinter xmlns)
67 |
68 | document:ET.ElementTree = ET.parse(url) # Abgerufene Datei in von Python verarbeitungsfähiges Objekt umwandeln (siehe oben)
69 | laender:ET.Element = document.getroot() # Ersten Knoten (hier: aufrufen)
70 |
71 |
72 | for land in laender: # Der Reihe nach alle Unterknoten öffnen
73 | if land.find("ft:Abkuerzung",namespaces).text==bundesLandKurz: # Den Unter-Unterknoten Abkürzung mit dem Parameter vergleichen (dieser ist schon richtig geschrieben, da wir bei Eingabe validiert haben)
74 | return str(land.find("ft:Name",namespaces).text) # Den langen Namen als Ergebnis zurückgeben
75 | return ""
76 |
77 | def checkFeiertag(datum:date, bundesland:str, bundeslandLang:str)->str:
78 | '''Funktion, um zu prüfen, ob ein Tag ein Feiertag ist'''
79 | result:str=f"Der {datum.strftime('%d.%m.%Y')} ist in {bundeslandLang} kein Feiertag." # Standardtext (die meisten Tage sind keine Feiertage)
80 | url:urllib._UrlopenRet=urlopen(
81 | Request(f"https://www.spiketime.de/feiertagapi/feiertage/{bundesland}/{datum.year}",
82 | headers={"Accept": "application/xml"}),
83 | data=None)
84 | # Zum dritten (und letzten) Mal: Abruf einer Datei aus dem Internet, aber diesmal von einer anderen Adresse. Die Adresse wird zusammengesetzt aus Bundesland und Jahr des zu prüfenden Datums.
85 |
86 | namespaces={'ft': 'http://schemas.datacontract.org/2004/07/FeiertagAPI'}
87 | # Auch diese Datei hat den gleichen Namespace, siehe auch dort die erste Zeile
88 | document:ET.ElementTree = ET.parse(url) # Wie oben: Gelesene Datei in interne Datenstruktur umwandeln
89 | feiertage:ET.Element = document.getroot() # Wie oben: Ersten Knoten aufrufen (hier: , siehe https://www.spiketime.de/feiertagapi/feiertage/SN/2014)
90 |
91 | for feiertag in feiertage: # Wie oben: Jeden Unterknoten (hier: ) der Reihe nach durchlaufen
92 | feiertagDatum:date=datetime.strptime(feiertag.find('ft:Datum',namespaces).text[0:10],'%Y-%m-%d') # Die Daten in der XML-Datei haben ein anderes Format. Daher: 1. Die Uhrzeiten wegschneiden (nur die ersten zehn Zeichen nutzen - aus 2014-01-01T00:00:00 wird also 2014-01-01 und dann konvertieren mit der Formatvorgabe Jahr(vierstellig)-Monat-Date)
93 | if feiertagDatum==datum: # Zu suchendes Datum mit Datum aus der Feiertagsliste vergleichen
94 | return (f"Am {feiertagDatum.strftime('%d.%m.%Y')} ist in {bundeslandLang} ein Feiertag ({feiertag.find('ft:Feiertag/ft:Name',namespaces).text}).") # Wenn gefunden: Hinweis mit Namen des Feiertags zurückgeben. Die Datei hat den Namen in einem Unter-Unterknoten von versteckt - sie steht nämlich unter - den Unterpfad kann man mit dem / abrufen
95 | break
96 | if feiertagDatum>datum: # Da die Daten in der Datei chronologisch sortiert sind, kann abgebrochen werden, sobald das erste Datum nach der Eingabe liegt
97 | break
98 | return result
99 |
100 |
101 | print("\n\n Dieses Programm bestimmt, ob an einem bestimmten Tag in einem bestimmten Bundesland ein Feiertag war.")
102 | print("Dazu wird auf die SpikeTime-Feiertag-API zurückgegriffen (https://www.spiketime.de/blog/spiketime-feiertag-api-feiertage-nach-bundeslandern/).")
103 | print("-----\n\n")
104 | while True:
105 | bundesland:str=getBundesLand() # Eingabefunktion für Bundesland aufrufen (siehe oben)
106 | datum:date=inputDate("Bitte geben Sie ein Datum ein: ") # Eingabefunktion für Datum aufrufen (siehe oben)
107 | print(checkFeiertag(datum,bundesland,getBundeslandLang(bundesland))) # Prüf- und Ergebnisfunktion aufrufen
108 | if(inputContinue()): # Prüfen, ob das Programm beendet werden soll
109 | break
110 | else:
111 | print("---------") # Zweite Runde mit Trennlinie vorbereiten
112 | print("\n")
113 |
--------------------------------------------------------------------------------
/07 - Daten/fristen.py:
--------------------------------------------------------------------------------
1 | # Fristenrechner 1.0 - Berechnung von Fristen nach §§ 187 ff. BGB
2 | # (C) 11.05.2021, Michael Beurskens
3 |
4 | from datetime import date,timedelta,datetime # Funktionen für den Umgang mit Daten
5 |
6 |
7 | wochentage:list[str]=["Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag","Sonntag"] # Für die deutsche Darstellung des Namens des Tages
8 |
9 | def inputDate(text:str)->date:
10 | '''Datum abfragen (bis gültige Eingabe erfolgt)'''
11 | while True: # Endlosschleife
12 | value:str=input(text).replace(" ","").replace("-",".").replace("/",".").replace(",","") # Text einlesen; dabei alle Striche, Punkte und Leerzeichen entfernen
13 | try:
14 | enteredDate:date=datetime.strptime(value, '%d.%m.%Y') # Umwandlungsfunktion: Formatiertes Datum. Was die %-Angaben bedeuten sieht man unter https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes (hier: Tag.Monat.Jahr (4-stellig))
15 | return enteredDate
16 | except:
17 | print("Bitte geben Sie ein gültiges Datum mit vierstelligem Jahr (01.01.2020) ein.") # Fehlermeldung bei ungültiger Eingabe
18 |
19 | def inputContinue()->bool:
20 | '''Programm beenden, wenn J, Ja, j, jau, etc. eingegeben wird'''
21 | value:str=input("Drücken Sie jetzt ""J"", um das Programm zu beenden: ")
22 | if len(value)>0 and value.lower()[0]=='j': # Eingabe in Kleinbuchstaben umwandeln und nur ersten Buchstaben auf "j" prüfen
23 | return True
24 | else:
25 | return False
26 |
27 | def inputNumber(text:str)->int:
28 | '''Eingabe von natürlichen Zahlen (ohne Komma, größer Null)'''
29 | while True: # Endlosschleife
30 | value:str=input(text) # Text eingeben mit Ausgabe des als Parameter übergebenen Erklärungstextes
31 | try:
32 | number:int=int(value) # Text in Zahl umwandeln
33 | if number<1: # Fehlermeldung bei negativen Zahlen
34 | print("Bitte geben Sie einen positiven Wert ein.")
35 | elif value.count(",")>0 or value.count(".")>0: # Fehlermeldung bei Kommazahlen (wobei das Programm nicht bis hier kommen sollte)
36 | print("Bitte geben Sie eine Zahl oder Komma ein.")
37 | else:
38 | return number # Zahl als Ergebnis zurückliefern
39 | except:
40 | print("Bitte geben Sie eine positive Zahl ein.") # Fehlermeldung bei unzulässigen Eingaben
41 |
42 | print("\n\nDieses Programm berechnet Wochenfristen nach §§ 187 Abs. 1, 188 BGB.")
43 | print("---------\n\n")
44 |
45 |
46 | while True:
47 | start:date=inputDate("Bitte geben Sie das Datum an, in dem das Ereignis eingetreten ist: ") # Eingabe eines gültigen Startdatums
48 | frist:int=inputNumber("Bitte geben Sie die Zahl der Wochen ein, welche die Frist dauert: ") # Eingabe einer gültigen Frist
49 |
50 | beginn:date=start+timedelta(days=1) # Fristbeginn: Einen Tag nach dem Ereignis (also: Addiere eine "Delta" von einem Tag -> timedelta(days=1))
51 | ziel:date=start+timedelta(weeks=frist) # Fristende: x Wochen nach dem Ereignis - nicht etwa nach dem Beginn (also: addiere ein "Delta" von eingegeben Wochen -> timedelta(weeks=frist))
52 |
53 | print(f"Fristbeginn: {wochentage[beginn.weekday()]}, {beginn.strftime('%d.%m.%Y')}, 00:00 Uhr") # Ausgabe des Ergebnisses - zur Klarstellung mit Uhrzeit; wichtig hier: strftime zur Formatierung. Was die %-Angaben bedeuten sieht man unter https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes (hier: Tag.Monat.Jahr (4-stellig))
54 | print(f"Fristende: {wochentage[ziel.weekday()]}, {ziel.strftime('%d.%m.%Y')} 24:00 Uhr") # Ausgabe des Ergebnisses - zur Klarstellung mit Uhrzeit; wichtig hier: strftime zur Formatierung. Was die %-Angaben bedeuten sieht man unter https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes (hier: Tag.Monat.Jahr (4-stellig))
55 | if(inputContinue()): # Prüfen, ob das Programm beendet werden soll
56 | break
57 | else:
58 | print("---------") # Zweite Runde mit Trennlinie vorbereiten
59 | print("\n")
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Examples used in my Software Development Course for Lawyers in 2021
2 |
3 | This code is plain and simple (and in German). It is merely used as an example.
4 |
5 | * 01 - Kostenquote: Text-based calculator for distribution of court and attorney fees under §§ 91, 92 ZPO (in a two-party-procedure).
6 | * 02 - Mehrwertsteuer: Text-based calculator for sales tax
7 | * 03 - Blutalkohol: Text-based calculator for blood alcohol based on intake of liquids
8 | * 04 - Minderung: Text-based calculator for reduction in price in case of damaged goods
9 | * 05 - Examensnote: Text-based calculator for exam results based on legal provisions in Bavaria
10 | * 06 - GUI: GUI-based calculator (using PyQt5)
11 | * 07 - Daten: Simple Calculator for Week-based Terms (not taking into account § 193 BGB); Simple Holiday-Checker using Webservice
12 |
13 | Requirements:
14 | * Git - see https://git-scm.com/downloads
15 | * Python (3.x) - see https://www.python.org/downloads/
16 | * PyQt5 (pip install PyQt5)
17 | * PyQt5 Tools (pip install pyqt5-tools)
18 |
19 | Recommended:
20 | * Visual Studio Code - see https://code.visualstudio.com/
21 | * Visual Studio Code Python Extension - see https://marketplace.visualstudio.com/items?itemName=ms-python.python
22 | * Visual Studio Code PyQT Integration Extension - see https://marketplace.visualstudio.com/items?itemName=zhoufeng.pyqt-integration
23 | * Visual Studio Code Github Pull Requests and Issues Extension - see https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github
24 |
25 |
26 | Further requirements will be added during the course.
27 |
--------------------------------------------------------------------------------