Home
Navigation
Impressum
Coder Welten - Programmierung und Optimierung
Coder Welten
 
 

 

 

Ein universelles Notizbuch mit Python 3 und Tkinter

Ein kleines Schreibprogramm mit Editor (Version 2)

Die erste Version des Notizbuches diente als kleines Einstiegsprojekt, war und ist bis auf weiteres voll funktionsfähig, weißt jedoch einige Unzulänglichkeiten auf, die in Projekten von Python-Neulingen häufig zu finden sind. So enthält die erste Version globale Variablen, auf die ein gestandener Programmierer eigentlich nicht verwenden sollte, da diese vor allem bei etwas umfangreicheren Scripts die Fehler­suche erschweren können.
Die zweite Version enthält nur noch eine Konstante "FONT" außerhalb der Hilfs­funktion "liefere_datum" und der Klasse "Notizbuch" und im Gegensatz zu sonstigen globalen Variablen, deren Werte sich im weiteren Programmablauf mehr­fach verändern könnten, werden Konstanten mit einem Wert initialisiert, der im weiteren Programmablauf nicht mehr verändert werden sollte.

In Version 2.0 sind darüber hinausgehend noch weitere Änderungen enthalten. So wurde der Code der neueren Version mehr an den Leitfaden für Python-Code (PEP 8) angelehnt, z.B. in Bezug auf die camel_case Schreibweise bei den Namen der Methoden. Was hingegen nicht streng eingehalten wurde, ist der Abstand vor und hinter Gleichheitszeichen. Innerhalb von Funktionsköpfen und deren Aufrufe, sollten eigentlich innerhalb von (Klammern) keine Leerzeichen vor und hinter Gleichheitszeichen notiert werden, woran wir uns nur bei .pack, nicht aber bei den Optionen von Frames, Labels und Buttons hielten. Da alle Optionen jedoch unter­einander und nicht nebeneinander aufgelistet wurden, erschien uns diese Schreib­weise ein wenig gefälliger.
Dennoch sei jedem Einsteiger angeraten, sich wenigsten einmal mit der PEP 8 zu beschäftigen und das möglichst bevor er eine Frage zu seinem Code in einem Python-Forum stellt oder ein Script veröffentlichen und anbieten möchte.

Ansicht des Notizbuches
Ansicht des Notizbuches bei 520 x 360 Pixel mit einem 'Lorem ipsum'-Platzhaltertext.

Wie aus dem Script ersichtlich, wurde für das Speichern von neuen Notizen für "open" der Modus "a" gewählt wurde. In diesem Modus wird, falls noch keine Datei vorhanden sein sollte, eine neue Datei bei einem Klick auf dem Button "Neue speichern" unter dem Dateinamen des aktuellen Monats mit zugehöriger Jahreszahl angelegt.

"notizen-im-[aktueller Monat]-[aktuelle Jahreszahl].txt"

Besteht diese Datei bereits, was immer dann der Fall sein sollte, wenn bereits Einträge nach dem Ersten eines Monats erfolgten, wird bei jedem weiteren Eintrag lediglich der interne Dateizeiger nicht an den Anfang platziert, sondern bis zum Ende der bestehenden Datei vorgerückt. Die bestehende Datei wird dadurch um neuere Einträge erweitert.
Zusätzlich können jedoch bestehende Einträge geöffnet werden, um diese zu editieren oder unter einem anderen Dateinamen zu speichern und selbstver­ständlich können neue Dateien ebenfalls unter einem beliebigen Namen mit dem Button "Speichern unter ..." abgelegt werden.

Code des kleinen Tage- oder Notizbuches – Version 2.0:

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
# ---------------------------------------------------------------------
# Description: Ein universelles Notizbuch, als Tagebuch nutzbar.
# Autor:       Horst Müller
# Version:     2.0
# Datum:       14. September 2017
# Lizenz:      GPLv3, einsehbar unter http://www.gnu.org/licenses/
# ---------------------------------------------------------------------
from tkinter import (Tk, Frame, Button, Label, Text,
                     PhotoImage, Scrollbar, END)
from tkinter.filedialog import askopenfilename, asksaveasfile
import time
import os

FONT = "cambria"

# Deutsche Monatsnamen fürs Datum und dem monatlichen Dateinamen benutzen.
# Beispiel Dateiname: notizen-im-september-2017.txt
def liefere_datum(auswahl):
    deutsche = {
        "01" : "Januar",
        "02" : "Februar",
        "03" : "Maerz",
        "04" : "April",
        "05" : "Mai",
        "06" : "Juni",
        "07" : "Juli",
        "08" : "August",
        "09" : "September",
        "10" : "Oktober",
        "11" : "November",
        "12" : "Dezember"
        }

    wochentag  = time.strftime("%d")    # Wochentage 01 bis 31
    monatsname = time.strftime("%m")    # monatsname 01 bis 12
    jahreszahl = time.strftime("%Y")    # das Jahr allgemein

    # Ein aktuelles Datum für die Übernahme ins Textfeld erstellen.
    if auswahl == "dmY":
        return "{0:s}. {1:s} {2:s}".format(
               wochentag, deutsche[monatsname], jahreszahl)
    # Verwendung für einen monatlichen Dateinamen, wobei eine Anpassung
    # auf andere Zeiträume möglich ist.
    elif auswahl == "mY":
        return "notizen-im-{0:s}-{1:s}.txt".format(
               deutsche[monatsname].lower(), jahreszahl)


class Notizbuch:
    """
    Eine Klasse für ein universelles Tage- oder Notizbuch.
    """
    def __init__(self):
        self.fenster = Tk()
        self.fraktal = PhotoImage(file="tagebuch.gif")
        self.dateiname = None
        self.textfeld  = None

    # Den kompletten Pfad mit dem Name des Verzeichnisses und der Datei
    # für die Speicherung der Notizen liefern.
    def liefere_pfad(self):
        verzeichnis = "reminder"
        datei_name  = liefere_datum("mY")
        # Mit kompletten Pfad
        pfad = os.path.abspath(".")
        self.dateiname = os.path.join(pfad, verzeichnis, datei_name)

    # Datei anlegen (falls noch nicht vorhanden) und neue Notizen
    # speichern.
    def speichere_neue(self):
        self.liefere_pfad()

        with open(self.dateiname, "a") as datei:
            datei.write(self.textfeld.get(1.0, END))
            self.textfeld.delete(1.0, END)
            self.textfeld.insert(END, "Eintrag erfolgreich!")

    # Funktion für das Einfügen eines Datums als Option.
    def setze_datum(self):
        self.textfeld.insert(
            END, "Notizen vom {0:s}\n\n".format(liefere_datum("dmY")))

    # Bestehende Datei zum Lesen oder zum Editieren öffnen.
    def oeffne_notizen(self):
        self.dateiname = askopenfilename(
                         filetypes = [("Text Datei", "*.txt")])
        # Esventuell vorhandener Inhalt aus dem Textfeld löschen.
        self.textfeld.delete(1.0, END)
        # Inhalt einer geöffneten Datei zum Lesen und Editieren oder
        # bei Abbruch einen Hinweis ins Textfeld einfügen.
        if self.dateiname:
            with open(self.dateiname, "r") as datei:
                self.textfeld.insert(END, datei.read())
        else:
            self.textfeld.insert(END, "Es wurde keine Datei geöffnet!")

    # Die alte Datei wird bei Mode "w" nach dem Editieren überschrieben.
    # Wurde "oeffne_notizen()" nicht genutzt, bleibt self.dateiname bei
    # None und bei Abbruch von "oeffne_notizen()" liefert
    # askopenfilename einen leeren "" String.
    def speicher_notizen(self):
        if self.dateiname:
            with open(self.dateiname, "w") as datei:
                datei.write(self.textfeld.get(1.0, END))
                self.textfeld.delete(1.0, END)
                self.textfeld.insert(END, "Änderung erfolgreich!")
                self.dateiname = None
        # Falls keine Datei zum Editieren geöffnet wurde.
        elif self.dateiname is None or self.dateiname == "":
            self.waehle_pfadname()

    # Manuelle Wahl des Speicherortes und des Dateinamens.
    def waehle_pfadname(self):
        datei = asksaveasfile(mode = "a",
                              filetypes = [("Text Datei", "*.txt")])
        if datei:
            datei.write(self.textfeld.get(1.0, END))
            datei.close()
            self.textfeld.delete(1.0, END)
            self.textfeld.insert(END, "Gespeichert!")

    # Ein self.fenster erzeugen, einen Fenstertitel plus Icon
    # hinzufügen, sowie die Größe und Aufteilung festlegen.
    def layout(self):

        self.fenster.title("Mein Notizbuch")
        self.fenster.wm_iconbitmap("logo.ico")
        self.fenster.geometry("640x424")
        self.fenster.maxsize(width = 680, height = 464)
        self.fenster.config(bg = "#d9cda3")

        # Aufteilung in linken und rechten Frame
        frame_sidebar  = Frame(self.fenster, bg = "#d9cda3")
        frame_textfeld = Frame(self.fenster, bg = "#a2a2a2")

        # Sidebar (linke Seite) packen
        frame_sidebar.pack(side = "left")

        # Label für Sidebar (linke Seite oben)
        Label(
            frame_sidebar,
            text = "Notizen",
            bg = "#d9cda3",
            fg = "#904b00",
            font = (FONT, 12, "bold"),
            pady = 8).pack()

        # Datei anlegen (falls noch nicht vorhanden) und neue Notizen
        # speichern.
        Button(
            frame_sidebar,
            text = "Neue speichern",
            font = (FONT, 10),
            bg = "#6f6352",
            fg = "#ffe9b3",
            activebackground = "#806840",
            command = self.speichere_neue).pack(fill="x", padx=8)

        # Button für das Einfügen eines Datums als nutzbare Option.
        Button(
            frame_sidebar,
            text = "Datum einfügen",
            font = (FONT, 10),
            bg = "#6f6352",
            fg = "#ffe9b3",
            activebackground = "#806840",
            command = self.setze_datum).pack(fill="x", padx=8)

        # Label für linke Seite (Ältere Notizen) wurde mittig anordnet.
        Label(
            frame_sidebar,
            text = "Ältere Notizen",
            font = (FONT, 9, "bold"),
            bg = "#d9cda3",
            fg = "#904b00",
            pady = 8).pack(fill="x", padx=8)

        # Bei mehr als 2 Buttons in Folge bietet sich eine Schleife an.
        button_labels = ("Öffnen", "Speichern", "Speichern unter ...")
        button_commands = (self.oeffne_notizen,
                           self.speicher_notizen,
                           self.waehle_pfadname)
        for i in range(3):
            Button(
                frame_sidebar,
                text = button_labels[i],
                font = (FONT, 10),
                bg = "#6f6352",
                fg = "#ffe9b3",
                activebackground = "#806840",
                command = button_commands[i]
                ).pack(fill="x", padx=8)

        # Label für linke Seite unten mit einem Image als Designergänzung.
        Label(
            frame_sidebar,
            bg = "#d9cda3",
            image = self.fraktal).pack(pady=24)

        # Ein Textfeld mit Scrollbar erstellen und formatieren.
        self.textfeld = Text(
            frame_textfeld,
            pady = 10,
            padx = 10,
            wrap = "word")
        scrollbar = Scrollbar(frame_textfeld)
        scrollbar.config(command = self.textfeld.yview)
        self.textfeld.config(yscrollcommand = scrollbar.set)

        # Rechte Seite packen
        frame_textfeld.pack(side="left", pady=3)
        scrollbar.pack(side="right", fill="y")
        self.textfeld.pack(pady=2, padx=2)

    def main(self):
        self.layout()
        self.fenster.mainloop()

if __name__ == "__main__":
    Notizbuch().main()

Wie bereits die erste Version, so wurde auch diese Version des kleinen Tage- oder Notizbuches mit Python 3.6 unter Windows 10 getestet. Um es unter Windows nutzen zu können, müssen Sie eine neuere Python Version auf Ihrem Computer installieren, falls noch nicht vorhanden sein sollte. Weiterhin muss in dem Verzeich­nis, in dem sie dieses Script ablegen möchten, ein Verzeichnis mit der Bezeichnung "reminder" angelegt werden oder Sie müssen den der Variablen "verzeichnis" zuge­wiesenen Verzeichnisnamen entsprechend anpassen, um einen korrekten Pfad zu erhalten.
Abschließend sei noch erwähnt, dass wir keine Gewähr für die Fehlerfreiheit des Scripts übernehmen können. Die erste Version ist zwar noch einsehbar, wir emp­fehlen jedoch die Verwendung der neueren Version.

» Notizbuch Version 1.0 «

 

Copyright © Verlag Horst Müller - Stendal - 2006 - Impressum - Datenschutz - Nutzungsbedingungen