├── .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 | --------------------------------------------------------------------------------