Paste #hvZkA2IVS

import os.path
import time
from tkinter import *
import sqlite3
import sys
sys.path.append('..')


class TextFormat(object):

    @classmethod
    def formatText(self, text):
        retString = ''
        for c in text:
            if ord(c) <= 128:
                retString += c
            elif ord(c) == 196:
                retString += 'Ae'
            elif ord(c) == 214:
                retString += 'Oe'
            elif ord(c) == 220:
                retString += 'Ue'
            elif ord(c) == 228:
                retString += 'ae'
            elif ord(c) == 246:
                retString += 'oe'
            elif ord(c) == 252:
                retString += 'ue'
            elif ord(c) == 223:
                retString += 'ss'
            else:
                retString += '?'
        retString = retString.lstrip().rstrip()
        return retString


class DB_Choice(object):

    def __init__(self, db, command):
        self.fenster = Tk()
        self.fenster.focus_force()
        self.fenster.geometry('300x70')
        self.fenster.title('DB Auswahl')
        self.lab = Label(self.fenster, text='Bitte Datenbankdatei eingeben:')
        self.lab.pack()
        self.ent = Entry(self.fenster, font=('Arial', 14), width=25)
        self.ent.insert(END, db)
        self.ent.focus_force()
        self.ent.pack()
        self.lab2 = Label(self.fenster, text='Bestätigen mit ENTER')
        self.lab2.pack()
        Widget.bind(self.ent, '<Return>', command)

    def item(self):
        text = self.ent.get()
        incorrect = False
        if text == '':
            return ''
        else:
            if not text.endswith('.db'):
                text = text + '.db'
            for c in text.upper():
                if c not in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._':
                    incorrect = True
                    break
            if incorrect:
                return ''
            else:
                return text


class Ergebnis_GUI:

    def __init__(self, breite=180, hoehe=45):
        self.fenster = Tk()
        self.fenster.title('GHO')
        self.rahmen = Frame(master=self.fenster)
        self.scrollbarY = Scrollbar(master=self.rahmen)
        self.scrollbarX = Scrollbar(master=self.rahmen, orient=HORIZONTAL)
        self.anzeige = Text(master=self.rahmen, font=('Courier New', 10), bg='light green', width=breite,
                            height=hoehe, wrap=NONE, yscrollcommand=self.scrollbarY.set, xscrollcommand=self.scrollbarX.set)
        self.scrollbarY.config(command=self.anzeige.yview)
        self.scrollbarX.config(command=self.anzeige.xview)
        self.rahmen.pack()
        self.scrollbarY.pack(side=RIGHT, fill=Y)
        self.anzeige.pack(fill=BOTH, padx=10, pady=10)
        self.scrollbarX.pack(fill=X)
        self.anzeige.focus_force()

    def zeigeAn(self, text):
        self.anzeige.config(state=NORMAL)
        self.loescheAnzeige()
        self.anzeige.insert(END, text)
        self.anzeige.config(state=DISABLED)

    def loescheAnzeige(self):
        self.anzeige.delete(0.0, END)


class DB_SQLite_GUI:

    def __init__(self, datei='', windowsize=3, debug=FALSE):
        padweite = 10
        eingabeWeite = 95
        buttonWeite = 50
        self.version = '1.4'
        self.debug = debug
        if windowsize > 5 or windowsize < 1:
            self.windowsize = 3
        else:
            self.windowsize = windowsize - 1
        self.db = None
        self.dbt = None
        self.ergGUIs = []
        self.fenster = Tk()
        self.fenster.title(
            'DB_SQLite_Tools and DB_SQLite_GUI (C) GHO V' + self.version)
        self.fenster.geometry('1040x680+0+2')
        self.menu = Menu(self.fenster)
        self.menu.add_command(
            label='  Datenbank auswählen  ', command=self.dbcWindow)
        self.menu.add_command(label='  Alle Tabellen und Views anzeigen  ',
                              command=self.alleTabellenUndViewsKommando)
        self.menu.add_command(
            label='  Alle Create-Statements anzeigen  ', command=self.createKommandos)
        self.menu.add_command(
            label='Fenstergröße: ' + str(self.windowsize), command=self.windowsizeKommando)
        self.helpmenu = Menu(self.menu)
        self.menu.add_cascade(label='  Hilfe  ', menu=self.helpmenu)
        self.helpmenu.add_command(
            label='Datenbankabfrage', command=self.helpSelectKommando)
        self.helpmenu.add_command(
            label='Datensatz einfügen', command=self.helpInsertKommando)
        self.helpmenu.add_command(
            label='Datensatz aktualisieren', command=self.helpUpdateKommando)
        self.helpmenu.add_command(
            label='Datensatz löschen', command=self.helpDeleteKommando)
        self.helpmenu.add_command(
            label='Tabelle anlegen', command=self.helpCreateKommando)
        self.helpmenu.add_command(
            label='Tabelle löschen', command=self.helpDropKommando)
        self.helpmenu.add_command(
            label='View anlegen', command=self.helpViewKommando)
        self.menu.add_command(label='  Beenden  ',
                              command=self.destroyKommando)
        self.fenster.config(menu=self.menu)
        self.selectButton = Button(master=self.fenster, bg='blue', text='SELECT-Befehl ausführen', font=('Arial', 12, 'bold'),
                                   width=buttonWeite, borderwidth=8, justify=CENTER, fg='white', relief=RAISED, command=self.selectKommando)
        self.selectResetButton = Button(master=self.fenster, bg='dark green', text='SELECT-Eingabe zurücksetzen', font=(
            'Arial', 12, 'bold'), width=buttonWeite//2, borderwidth=8, justify=CENTER, fg='white', relief=RAISED, command=self.selectResetKommando)
        self.sqlButton = Button(master=self.fenster, bg='dark red', text='SQL-Befehle ausführen', font=('Arial', 12, 'bold'),
                                width=buttonWeite, borderwidth=8, justify=CENTER, fg='white', relief=RAISED, command=self.sqlKommando)
        self.sqlClearButton = Button(master=self.fenster, bg='dark green', text='SQL-Eingabefeld löschen', font=(
            'Arial', 12, 'bold'), width=buttonWeite//2, borderwidth=8, justify=CENTER, fg='white', relief=RAISED, command=self.sqlClearKommando)
        self.projLabel = Label(
            master=self.fenster, text='SELECT', fg='blue', font=('Arial', 12, 'bold'))
        self.tabLabel = Label(master=self.fenster, text='FROM', font=(
            'Arial', 12, 'bold'), fg='blue')
        self.selLabel = Label(master=self.fenster, text='WHERE',
                              fg='blue', font=('Arial', 12, 'bold'))
        self.sqlLabel = Label(
            master=self.fenster, text='   SQL-Befehle:', fg='red', font=('Arial', 12, 'bold'))
        self.projEntry = Entry(
            master=self.fenster, width=eingabeWeite, font=('Arial', 12), state=DISABLED)
        self.tabEntry = Entry(master=self.fenster, width=eingabeWeite, font=(
            'Arial', 12), state=DISABLED)
        self.selText = Text(master=self.fenster, width=eingabeWeite, font=(
            'Arial', 12), height=8, state=DISABLED)
        self.sqlText = Text(master=self.fenster, width=eingabeWeite, font=(
            'Arial', 12), height=15, state=DISABLED)
        zeile = 0
        self.projLabel.grid(column=0, row=zeile, sticky=E)
        self.projEntry.grid(column=1, row=zeile, sticky=W,
                            columnspan=2, padx=2*padweite, pady=padweite)
        zeile += 1
        self.tabLabel.grid(column=0, row=zeile, sticky=E)
        self.tabEntry.grid(column=1, row=zeile, sticky=W,
                           columnspan=2, padx=2*padweite, pady=padweite)
        zeile += 1
        self.selLabel.grid(column=0, row=zeile, sticky=E)
        self.selText.grid(column=1, row=zeile, sticky=W,
                          columnspan=2, padx=2*padweite, pady=padweite)
        zeile += 1
        self.selectButton.grid(column=1, row=zeile,
                               sticky=N, padx=2*padweite, pady=padweite)
        self.selectResetButton.grid(
            column=2, row=zeile, sticky=N, padx=padweite, pady=padweite)
        zeile += 1
        self.sqlLabel.grid(column=0, row=zeile, sticky=W)
        self.sqlText.grid(column=1, row=zeile, sticky=W,
                          columnspan=2, padx=2*padweite, pady=padweite)
        zeile += 1
        self.sqlButton.grid(column=1, row=zeile, sticky=N,
                            padx=2*padweite, pady=padweite)
        self.sqlClearButton.grid(
            column=2, row=zeile, sticky=N, padx=padweite, pady=padweite)
        if len(datei) > 0:
            self.__setDBfinish(datei)
        self.windowsizeKommando()

    def dbcWindow(self):
        self.dbc = DB_Choice(self.db, self.setDB)
        self.dbc.fenster.mainloop()

    def setDB(self, event):
        dbitem = self.dbc.item()
        self.dbc.fenster.destroy()
        if dbitem != '' and dbitem != self.db:
            self.__setDBfinish(dbitem)

    def __setDBfinish(self, dbitem):
        correct = True
        if not dbitem.endswith('.db'):
            dbitem = dbitem + '.db'
        for c in dbitem.upper():
            if c not in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._':
                correct = False
                break
        if correct:
            self.db = dbitem
            self.dbt = DB_SQLite_Tools(self.db, self.debug)
            self.setTitle()
            self.tabEntry.config(state=NORMAL)
            self.projEntry.config(state=NORMAL)
            self.selText.config(state=NORMAL)
            self.sqlText.config(state=NORMAL)
            self.selectResetKommando()

    def debugKommando(self):
        self.debug = not self.debug
        self.setTitle()

    def setTitle(self):
        self.fenster.title(
            'DB_SQLite_Tools and DB_SQLite_GUI (C) GHO V' + self.version)

    def getSqlText(self):
        text = TextFormat.formatText(self.sqlText.get(0.0, END))
        return text.replace('\n', '')

    def getTabEntry(self):
        return TextFormat.formatText(self.tabEntry.get())

    def getProjEntry(self):
        proj = TextFormat.formatText(self.projEntry.get())
        if 'COUNT(*)' not in proj and 'count(*)' not in proj:
            proj = proj.replace('COUNT(', 'COUNT (').replace(
                'count(', 'COUNT (')
        proj = proj.replace('AVG(', 'AVG (').replace('avg(', 'AVG (')
        proj = proj.replace(' as ', ' AS ')
        proj = proj.replace('distinct ', 'DISTINCT ')
        proj = proj.replace('MAX(', 'MAX (').replace('max(', 'MAX (')
        proj = proj.replace('MIN(', 'MIN (').replace('min(', 'MIN (')
        proj = proj.replace('SUM(', 'SUM (').replace('sum(', 'SUM (')
        return proj

    def getSelText(self):
        text = TextFormat.formatText(self.selText.get(0.0, END))
        text = text.replace('order by', 'ORDER BY')
        text = text.replace('group by', 'GROUP BY')
        text = text.replace('length(', 'LENGTH(')
        text = text.replace(' and ', ' AND ')
        text = text.replace(' or ', ' OR ')
        text = text.replace(' not ', ' NOT ')
        text = text.replace(' desc', ' DESC')
        text = text.replace(' asc', ' ASC')
        return text

    def selectKommando(self):
        self.debugprint('die blaue Schaltfläche wurde gedrückt')
        tab = self.getTabEntry()
        if tab != '':
            istView = False
            liste = self.dbt.alleTabellenUndViews()
            if tab in liste[1]:
                istView = True
            proj = self.getProjEntry()
            sel = self.getSelText()
            if len(proj) == 0:
                proj = '*'
            if len(sel) == 0:
                sel = '1'
            if sel.upper().startswith('ORDER'):
                sel = '1 ' + sel
            if sel.upper().startswith('GROUP'):
                sel = '1 ' + sel
            (text, anzahl, success) = self.dbt.showSelect(tab, proj, sel, istView)
            if success:
                text += ' Anzahl Datensaetze: ' + str(anzahl)
            ergv = Ergebnis_GUI(breite=36*self.windowsize,
                                hoehe=9*self.windowsize)
            self.ergGUIs.append(ergv)
            ergv.zeigeAn(text)
            ergv.fenster.mainloop()

    def createKommandos(self):
        if self.dbt == None:
            return
        text = ''
        liste = self.dbt.alleTabellenUndViews()
        for el in liste[0]:
            text += self.dbt.showCreateStatement(table=el) + '\n'
        for el in liste[1]:
            text += self.dbt.showCreateStatement(table=el) + '\n'
        ergv = Ergebnis_GUI(breite=36*self.windowsize, hoehe=9*self.windowsize)
        self.ergGUIs.append(ergv)
        ergv.zeigeAn(text)
        ergv.fenster.mainloop()

    def alleTabellenUndViewsKommando(self):
        if self.dbt == None:
            return
        liste = self.dbt.alleTabellenUndViews()
        text = 'Tabellen:\n========='
        tabListe = liste[0]
        viewListe = liste[1]
        for el in tabListe:
            text += '\n' + str(el)
        if len(viewListe) > 0:
            text += '\n\nViews:\n======'
        for el in viewListe:
            text += '\n' + str(el)
        ergv = Ergebnis_GUI(breite=36*self.windowsize, hoehe=9*self.windowsize)
        self.ergGUIs.append(ergv)
        ergv.zeigeAn(text)
        ergv.fenster.mainloop()

    def sqlKommando(self):
        if self.dbt == None:
            return
        sqltext = self.getSqlText()
        if ';' not in sqltext:
            ergv = Ergebnis_GUI(breite=36*self.windowsize,
                                hoehe=9*self.windowsize)
            self.ergGUIs.append(ergv)
            ergv.zeigeAn("""incomplete SQL statement: ';' is missing
""")
            ergv.fenster.mainloop()
        else:
            liste1 = sqltext.split(';')
            liste = []
            for el in liste1:
                cmd = el
                if cmd != '':
                    if ';' not in cmd:
                        cmd += ';'
                    liste.append(cmd)
            for command in liste:
                if command != '':
                    self.debugprint(command)
                    text = self.dbt.sqlcommand(command)
                    if len(liste) <= 1:
                        ergv = Ergebnis_GUI(
                            breite=36*self.windowsize, hoehe=9*self.windowsize)
                        self.ergGUIs.append(ergv)
                        ergv.zeigeAn(text)
                        ergv.fenster.mainloop()

    def sqlClearKommando(self):
        self.sqlText.delete('0.0', END)

    def selectResetKommando(self):
        self.projEntry.delete('0', END)
        self.projEntry.insert(END, '*')
        self.tabEntry.delete('0', END)
        self.selText.delete('0.0', END)

    def windowsizeKommando(self):
        self.windowsize = (self.windowsize + 1) % 5
        if self.windowsize == 0:
            self.windowsize = 5
        self.menu.entryconfigure(
            4, label='Fenstergröße: ' + str(self.windowsize))

    def debugKommando(self):
        self.debug = not self.debug
        if self.debug:
            self.configmenu.entryconfigure(2, label='Debug: On')
        else:
            self.configmenu.entryconfigure(2, label='Debug: Off')

    def destroyKommando(self):
        for el in self.ergGUIs:
            try:
                el.fenster.destroy()
            except:
                pass
        self.fenster.destroy()

    def debugprint(self, text):
        if self.debug:
            print(str(text))

    def helpSelectKommando(self):
        text = """
 ============================================
 Datenbankabfrage:
 ============================================

   SELECT <projektion> FROM <tabellenname(n) / view(s)> WHERE <selektion>;

     Innerhalb der Projektion:
     =========================
     Angabe der Attribute (Spaltenüberschriften) in der gewünschten Reihenfolge
     Tipp: * liefert alle Attribute
     Hinweis: bei mehreren Tabellen sollte der Tabellenname vorangestellt werden
              z.B.: kunden.kunr
     
     Funktionen:
     -----------
       Zählen:
         COUNT(Primärschlüsselattribut) 
         Hinweis: Vermeide COUNT(*)
       Maximum / Minimum:
         MAX(Attribut)
         MIN(Attribut)
       Summe / Durchschnitt:
         SUM(Attribut)
         AVG(Attribut)
         
     Mehrfach auftretende Datensätze nur einmal nennen:
       SELECT DISTINCT ...
     
     
     Innerhalb der Selektion:
     =========================

       Vergleichsoperatoren:
         >    ist größer als
         <    ist kleiner als
         >=   ist größer oder gleich als
         <=   ist kleiner oder gleich als
         =    ist gleich
         between Wert1 AND Wert2     zwischen zwei Werten, inklusive Wert1 und Wert2         
       
         Bei Textvergleichen besser LIKE anstelle von = benutzen!       
         LIKE '%Textteil%'       % ist ein beliebiger Textteil, 
                                 der auch leer sein kann
         LIKE 'M_ier'            _ ist genau ein beliebiges Zeichen
         
       IN: Liegt der Wert innerhalb einer Liste?
         kuname IN ('Cim', 'Fischer',  'Gulder',  'Schmidt') 

       Join: Verbinden zweier Tabellen (Entitäts- und Relationstabelle)
       ----------------------------------------------------------------
         Das Primärschlüsselattribut der Entitätstabelle wird mit dem
         dazu passenden Fremdschlüsselattribut der Relationstabelle
         gleichgesetzt.
         Beispiel: kunden = Entitätstabelle und ausleiehe = Relationstabelle
           kunden.kunr = ausleihe.kunr
       
       Sortieren:
         ORDER BY Attribut ASC            aufsteigend
         ORDER BY Attribut DESC           absteigend

       Gruppieren:
         GROUP BY Attribut
       
     Funktionen:
     -----------
       Länge:
         LENGTH(Attribut)
         
     Logische Verknüpfungen:
     -----------------------
       AND, OR, NOT

     Abfragen über mehrere Tabellen:
       Beispiel: Alle Klausuren eines Schülers
       SELECT schueler.vorname, klausur.fach
       FROM schueler, klausur 
       WHERE schueler.vorname like \"Ferdinand\" AND schueler.nr = klausur.schueler;
"""
        ergv = Ergebnis_GUI(breite=36*self.windowsize, hoehe=9*self.windowsize)
        self.ergGUIs.append(ergv)
        ergv.zeigeAn(text)
        ergv.fenster.title('GHO - Daten abfragen')
        ergv.fenster.mainloop()

    def helpInsertKommando(self):
        text = """
 ============================================
 Datensatz eintragen
 ============================================

   INSERT INTO <tabellenname> VALUES (<WertAttribut1>, <WertAttribut2>, ... <WertAttributn>);

   oder bei Nennung / Auslassung von Attributen:

   INSERT INTO <tabellenname> (<attribut3>, <attribut1>, ...) VALUES (<WertAttribut3>, <WertAttribut1>, ... );

   Beispiel:
   INSERT INTO person VALUES (\"S0815\", \"Heinemann\", \"Gustav\");
   oder bei anderer Reihenfolge
   INSERT INTO person (\"vorname\", \"nachname\", \"PID\") VALUES (\"Gustav\", \"Heinemann\", \"P0815\");

   TIPP: Manchmal möchte man einen neuen Datensatz hinzufügen oder, falls es diesen Datensatz bereits gibt,
   den bestehenden Datensatz aktualisieren. Dann ergänzt man den INSERT-Befehl und schreibt:
   
   INSERT OR REPLACE INTO <tabellenname> (<attribut3>, <attribut1>, ...) VALUES (<WertAttribut3>, <WertAttribut1>, ... );

"""
        ergv = Ergebnis_GUI(breite=36*self.windowsize, hoehe=9*self.windowsize)
        self.ergGUIs.append(ergv)
        ergv.zeigeAn(text)
        ergv.fenster.title('GHO - Datensatz eintragen ')
        ergv.fenster.mainloop()

    def helpUpdateKommando(self):
        text = """

 ============================================
 Datensatz aktualisieren
 ============================================

   UPDATE <tabellenname> SET <attribut1> = \"neuerWert1\", <attribut2> = \"neuerWert2\", ... WHERE <selektion>;

   Beispiel:

   UPDATE person SET nachname = \"Meier\" WHERE PID = 4711;

"""
        ergv = Ergebnis_GUI(breite=36*self.windowsize, hoehe=9*self.windowsize)
        self.ergGUIs.append(ergv)
        ergv.zeigeAn(text)
        ergv.fenster.title('GHO - Datensatz aktualisieren ')
        ergv.fenster.mainloop()

    def helpDeleteKommando(self):
        text = """

 ============================================
 Datensatz löschen
 ============================================

   DELETE FROM <tabellenname> WHERE <selektion>;

   Obacht: Abhängigkeiten beachten

   Beispiel:
   DELETE FROM schueler WHERE jahr < 2000;
 
"""
        ergv = Ergebnis_GUI(breite=36*self.windowsize, hoehe=9*self.windowsize)
        self.ergGUIs.append(ergv)
        ergv.zeigeAn(text)
        ergv.fenster.title('GHO - Datensatz löschen ')
        ergv.fenster.mainloop()

    def helpCreateKommando(self):
        text = """
 
 ===================================================
 Datenbanktabelle anlegen:
 ===================================================
 
   A. Entität mit 1 Primärschlüsselattribut
   =================================================
   CREATE TABLE <tabellenname>(
     <prim_key_attribut1>  <datentyp(anzahl)> PRIMARY KEY,
     <attribut2>  <datentyp(anzahl)>,
     <attribut3>  <datentyp(anzahl)>,
     ...
     <attributn>  <datentyp(anzahl)>
   );


   Alle Primärschlüsselattribute müssen ausgefüllt werden.
   Dürfen weitere Attribute wie im Beispiel Vor- und Nachname
   nicht leer bleiben, schreibt man jeweils rechts daneben: NOT NULL

   Beispiel:
   CREATE TABLE person(
     id            INT(10)     PRIMARY KEY,
     vorname       VARCHAR(50) NOT NULL,
     nachname      VARCHAR(50) NOT NULL,
     geburtsdatum  VARCHAR(10),
     mail          VARCHAR(60)
   );
 
   B. Entität mit mehreren Primärschlüsselattributen
   =================================================
   CREATE TABLE <tabellenname>(
     <prim_key_attribut1>  <datentyp(anzahl)>,
     <prim_key_attribut2>  <datentyp(anzahl)>,
     ...
     <attributn>  <datentyp(anzahl)>,
     PRIMARY KEY (prim_key_attribut1, prim_key_attribut2, ...)
   );
   
   Beispiel:
   CREATE TABLE person(
     vorname       VARCHAR(30),
     nachname      VARCHAR(30),
     geburtsdatum  VARCHAR(10),
     mail          VARCHAR(60),
     PRIMARY KEY (vorname, nachname)
   );
 
   C. Relation (n:m)
   =================================================
   CREATE TABLE <tabellenname> (
     <fk_attribut1> <datentyp(anzahl)>, 
     <fk_attribut2> <datentyp(anzahl)>,
     ...
     <attributn> <datentyp(anzahl)>,
     FOREIGN KEY (fk_attribut1) REFERENCES <tabelle1> (<prim_key_tabelle1>),
     FOREIGN KEY (fk_attribut2) REFERENCES <tabelle2> (<prim_key_tabelle2>)
   );
   Hinweis 1: Die Datentypen müssen exakt mit den Primärschlüsselattributen übereinstimmen.
   Hinweis 2: Hat eine Tabelle mehrere Primärschlüsselattribute, müssen diese auch bei den 
   Fremdschlüsseln zusammengefasst werden.
   
   Beispiel:
   CREATE TABLE in_Kurs_einschreiben(
      pv     VARCHAR(30),
      pn     VARCHAR(30),
      kn     VARCHAR(10),
      datum  VARCHAR(10),
      FOREIGN KEY (pv, pn) REFERENCES person(vorname, nachname),   
      FOREIGN KEY (kn) REFERENCES kurs(kursnr)
   );

   D. Entität inkl. 1:n Relation
   =================================================
   CREATE TABLE <Tabelle> (
     <attribut1> <datentyp(anzahl)> PRIMARY KEY,
     <attribut2> <datentyp(anzahl)>,
     <attribut3> <datentyp(anzahl)>,
     ...
     <fk_attributn> <datentyp(anzahl)>,
     FOREIGN KEY (fk_attributn) REFERENCES <tabelle1> (<prim_key_tabelle1>)
   );
   Hinweis: Die Datentypen müssen exakt mit den Primärschlüsselattributen übereinstimmen.

   Beispiel:
   CREATE TABLE auto(
     kennzeichen    VARCHAR(30) PRIMARY KEY,
     fabrikat       VARCHAR(50),
     modell         VARCHAR(30),
     farbe          VARCHAR(50),
     fahrer         INT(10),
     FOREIGN KEY (fahrer) REFERENCES person(id)
   );

  E. Automatische Hochzählen des Primärschlüssels
  ===============================================

  Möchte man sich nicht selbst um die Nummerierung der Datensätze kümmern,
  kann man das entsprechende Primärschlüsselattribut automatisch hochzählen
  lassen.

  Wichtig ist, dass man diese Nummer wie im Beispiel als Integer ohne Stellenangabe
  definiert und als letztes das Schlüsselwort AUTOINCREMENT kommt.
  
  CREATE TABLE person (
    nummer   INTEGER PRIMARY KEY AUTOINCREMENT,
    vorname  VARCHAR(50),
    nachname VARCHAR(50)
  );

"""
        ergv = Ergebnis_GUI(breite=36*self.windowsize, hoehe=9*self.windowsize)
        self.ergGUIs.append(ergv)
        ergv.zeigeAn(text)
        ergv.fenster.title('GHO - Tabelle anlegen')
        ergv.fenster.mainloop()

    def helpViewKommando(self):
        text = """
 
 ============================================
 Datenbank-View anlegen:
 ============================================

 CREATE VIEW <viewname> AS <selection>;

 Beispiel:
 
 CREATE VIEW informatikkurs AS SELECT * from kurs WHERE fach LIKE \"Informatik\";

"""
        ergv = Ergebnis_GUI(breite=36*self.windowsize, hoehe=9*self.windowsize)
        self.ergGUIs.append(ergv)
        ergv.zeigeAn(text)
        ergv.fenster.title('GHO - View anlegen')
        ergv.fenster.mainloop()

    def helpDropKommando(self):
        text = """

 ============================================
 Datenbanktabelle löschen:
 ============================================

   DROP TABLE <tabellenname>;

   Obacht: Abhängigkeiten beachten

   Beispiel:

   DROP TABLE auto;

 ============================================       

        """
        ergv = Ergebnis_GUI(breite=36*self.windowsize, hoehe=9*self.windowsize)
        self.ergGUIs.append(ergv)
        ergv.zeigeAn(text)
        ergv.fenster.title('GHO - Tabelle löschen')
        ergv.fenster.mainloop()


class DB_SQLite_Tools:
    __doc__ = """ Die Klasse DB_SQLite_Tools stellt verschiedene Hilfsmethoden
        für die Verwendung der relationalen Datenbank SQLite zur Verfügung.
    """

    def __init__(self, database, debug=False):
        self.db = database
        self.connection = None
        self.cursor = None
        self.debug = debug

    def __openConnection__(self):
        self.connection = sqlite3.connect(self.db)
        self.connection.text_factory = str
        self.cursor = self.connection.cursor()
        self.cursor.execute('PRAGMA foreign_keys = 1;')

    def showCreateStatement(self, table):
        ''' Liefert das Create-Statement der angegebenen Tabelle oder View.
        '''
        self.__openConnection__()
        retString = ''
        statement = "SELECT sql FROM sqlite_master WHERE name = '" + table + "';"
        self.cursor.execute(statement)
        text = str(self.cursor.fetchone())
        text = text[2:]
        text = text[:-3]
        text = text.replace('\\n', ' ')
        text = text.replace('\\r', ' ')
        text = text.replace('\\t', ' ')
        for i in range(10):
            text = text.replace('  ', ' ')
        text = text.replace(' ,', ',')
        text = text.replace(' (', '(')
        for i in range(len(text)):
            if text[i] == '(':
                retString = text[:i + 1]
                text = text[i + 1:].lstrip(' ')
                break
            elif i == len(text) - 1:
                self.connection.close()
                return text + '; \n'
        klammerauf = False
        while len(text) > 0:
            for i in range(len(text)):
                if text[i] == '(':
                    klammerauf = True
                if text[i] == ')':
                    klammerauf = False
                if text[i] == ',' and not klammerauf:
                    retString += '\n' + text[:i + 1]
                    text = text[i + 1:].lstrip(' ')
                    break
                elif i == len(text) - 1:
                    retString += '\n' + text[:-1]
                    retString += '\n' + text[-1]
                    text = ''
                    break
        self.connection.close()
        return retString + '; \n'

    def attributes(self, table, proj='*', isView=False):
        ''' Liefert eine Liste der Attribute der angegebenen Tabelle.
            Die \xfcbergebene Projektion wird dabei ber\xfccksichtigt.
            Einschr\xe4nkungen bei COUNT(*).
        '''
        proj = proj.replace('DISTINCT ', '')
        if 'COUNT(' in proj.upper():
            return ['COUNT(*)']
        projektion = TextFormat.formatText(proj)
        if isView:
            return self.__attributesView__(table)
        else:
            s = self.showCreateStatement(table)
            liste = []
            for i in range(len(s)):
                if s[i] == '(':
                    s = s[i + 2:]
                    break
            while len(s) > 0:
                ende = len(s) - 1
                found = False
                for i in range(len(s)):
                    if s[i] == ' ' and not found:
                        liste.append(s[:i])
                        found = True
                    elif s[i] == ',':
                        s = s[i + 2:]
                        break
                    elif s[i:i + 9].upper() == 'PRIMARY (':
                        s = ''
                        break
                    elif s[i:i + 12].upper() == 'PRIMARY KEY(':
                        s = ''
                        break
                    elif s[i:i + 12].upper() == 'FOREIGN KEY(':
                        s = ''
                        break
                    elif i == ende:
                        s = ''
                        break
            if projektion != '*':
                tempListe = []
                for el in liste:
                    if el in projektion:
                        eintrag = el
                        if 'AVG (' + el in projektion:
                            eintrag = 'AVG (' + el + ')'
                        elif 'COUNT (' + el in projektion:
                            eintrag = 'COUNT (' + el + ')'
                        elif 'MAX (' + el in projektion:
                            eintrag = 'MAX (' + el + ')'
                        elif 'MIN (' + el in projektion:
                            eintrag = 'MIN (' + el + ')'
                        elif 'SUM (' + el in projektion:
                            eintrag = 'SUM (' + el + ')'
                        tempListe.append(eintrag)
                posListe = []
                for el in tempListe:
                    posListe.append(str(projektion).find(el))
                projListe = []
                for k in range(len(posListe)):
                    minimum = posListe[0]
                    stelle = 0
                    for i in range(len(posListe)):
                        if posListe[i] < minimum:
                            stelle = i
                    projListe.append(tempListe[stelle])
                    posListe.remove(posListe[stelle])
                    tempListe.remove(tempListe[stelle])
                return projListe
            else:
                return liste

    def __attributesView__(self, View):
        ' Liefert eine Liste der Attribute der angegebenen View.'
        s = self.showCreateStatement(View)
        liste = []
        while len(s) > 0:
            ende = len(s) - 1
            for i in range(len(s)):
                if s[i] == ',' or s[i:i + 5] == ' from':
                    k = i - 1
                    att = ''
                    c = s[k]
                    while True:
                        if s[k] == ' ':
                            break
                        else:
                            att = s[k] + att
                            k = k - 1
                    liste.append(att)
                    s = s[i + 1:]
                    break
                elif i == ende:
                    s = ''
                    break
        return liste

    def alleTabellenUndViews(self):
        ''' Liefert eine Liste mit allen Tabellen- und View-Namen,
            die jeweils in einer eigenen Liste stehen.'''
        tabList = []
        viewList = []
        self.__openConnection__()
        statement = "SELECT name FROM sqlite_master WHERE type = 'table';"
        self.cursor.execute(statement)
        liste = self.cursor.fetchall()
        for el in liste:
            tabList.append(el[0])
        self.connection.close()
        self.__openConnection__()
        statement = "SELECT name FROM sqlite_master WHERE type = 'view';"
        self.cursor.execute(statement)
        liste = self.cursor.fetchall()
        for el in liste:
            viewList.append(el[0])
        self.connection.close()
        return [tabList, viewList]

    def select(self, table, proj='*', sel='1'):
        ''' Liefert das Ergebnis eines select-Statements
            als Ergebnisliste zur\xfcck.
        '''
        errormsg = ''
        retList = []
        self.__openConnection__()
        projektion = TextFormat.formatText(proj)
        selektion = TextFormat.formatText(sel)
        if ';' not in selektion:
            selektion += ';'
        statement = 'SELECT ' + projektion + ' FROM ' + table + ' WHERE ' + selektion
        try:
            self.cursor.execute(statement)
        except sqlite3.Error as e:
            errormsg += ' Result: An error occured: ' + str(e.args[0]) + '!'
        if errormsg == '':
            for row in self.cursor.fetchall():
                retList.append(row)
        self.connection.close()
        return (retList, errormsg)

    def showSelect(self, tab, proj='*', sel='1', isView=False):
        ''' Liefert das Ergebnis eines select-Statements
            als formatierte Tabelle zur\xfcck.
        '''
        table = TextFormat.formatText(tab)
        projektion = TextFormat.formatText(proj)
        projektionsliste = projektion.split(',')
        for i in range(len(projektionsliste)):
            if ' AS ' in projektionsliste[i]:
                hilfsliste = projektionsliste[i].split(' ')
                projektionsliste[i] = hilfsliste[-1]
        self.debugprint(projektionsliste)
        selektion = TextFormat.formatText(sel)
        if ';' not in selektion:
            selektion += ';'
        (liste, errormsg) = self.select(table, projektion, selektion)
        if errormsg == '':
            retString = ' SELECT ' + \
                str(projektion) + ' FROM ' + str(table) + \
                ' WHERE ' + str(selektion) + '\n\n'
            if ',' not in table:
                self.debugprint('genau eine Tabelle: ' + tab)
                att = self.attributes(table, projektion, isView)
                if ' AS ' in projektion:
                    att = projektionsliste
                self.debugprint(att)
                attlen = len(att)
                maxlen = []
                for el in att:
                    maxlen.append(len(el))
                for el in liste:
                    for i in range(attlen):
                        if len(str(el[i])) > maxlen[i]:
                            maxlen[i] = len(str(el[i]))
                gesamt = 0
                for el in maxlen:
                    gesamt += el + 5
                top = ' Tabelle ' + table.upper() + ' '
                if len(top) > gesamt:
                    gesamt = len(top)
                else:
                    diff = gesamt - len(top)
                    top = diff//2*' ' + top
                retString += '\n' + gesamt*'-' + '\n'
                retString += top
                retString += '\n' + gesamt*'-' + '\n '
                for i in range(len(att)):
                    retString += str(att[i].upper())
                    if len(att[i]) < maxlen[i]:
                        retString += (maxlen[i] - len(att[i]))*' '
                    retString += '  |  '
                retString += '\n' + gesamt*'-'
                for i in range(len(liste)):
                    retString += '\n '
                    for j in range(len(maxlen)):
                        anzahlSonderzeichen = 0
                        for c in str(liste[i][j]):
                            if ord(c) == 195:
                                anzahlSonderzeichen += 1
                        retString += str(liste[i][j]) + (anzahlSonderzeichen +
                                                         maxlen[j] - len(str(liste[i][j])))*' ' + '  |  '
                retString += '\n' + gesamt*'-' + '\n'
                datensaetze = len(liste)
            else:
                self.debugprint('Abfrage über mehrere Tabellen: ' + tab)
                spaltenanzahl = len(liste[0])
                spalten = []
                spaltenbreiten = []
                strich = False
                for i in range(spaltenanzahl):
                    spalten.append([i])
                    spaltenbreiten.append(0)
                if '*' not in projektion:
                    atts = projektion.split(',')
                    attliste = []
                    for a in atts:
                        a = a.upper().split(' AS ')[-1]
                        attliste.append(a.upper().lstrip().rstrip())
                else:
                    helpliste1 = table.split(',')
                    helpliste2 = []
                    attliste = []
                    for tab in helpliste1:
                        helpliste2.append(tab.lstrip().rstrip())
                    for tab in helpliste2:
                        atts = self.attributes(tab, '*', isView)
                        for a in atts:
                            attliste.append(tab.upper() + '.' + a.upper())
                liste2 = []
                liste2.append(attliste)
                for el in liste:
                    liste2.append(el)
                liste = liste2
                strich = True
                for el in liste:
                    for i in range(spaltenanzahl):
                        spalten[i].append(el[i])
                for i in range(spaltenanzahl):
                    for el in spalten[i]:
                        if len(str(el)) > spaltenbreiten[i]:
                            spaltenbreiten[i] = len(str(el))
                for el in liste:
                    retString += '\n '
                    for i in range(spaltenanzahl):
                        anzahlSonderzeichen = 0
                        for c in str(el[i]):
                            if ord(c) == 195:
                                anzahlSonderzeichen += 1
                        retString += str(el[i]) + (anzahlSonderzeichen +
                                                   spaltenbreiten[i] - len(str(el[i])))*' ' + ' | '
                    if strich:
                        strich = False
                        retString += '\n-'
                        for i in range(spaltenanzahl):
                            retString += spaltenbreiten[i]*'-' + '---'
                retString += '\n-'
                for i in range(spaltenanzahl):
                    retString += spaltenbreiten[i]*'-' + '---'
                retString += '\n'
                datensaetze = len(liste) - 1
            return (retString, datensaetze, True)
        else:
            return (errormsg, len(liste), False)

    def sqlcommand(self, stmt, complete=True):
        ''' Das \xfcbergebene Statement wird ausgef\xfchrt und committed.
        '''
        success = True
        select = False
        self.__openConnection__()
        statement = TextFormat.formatText(stmt)
        retval = """
 I try to execute the SQL statement:
 """ + statement + '\n'
        raw = ''
        if str(statement).find(';') < 0:
            retval += """ Result: incomplete SQL statement: ';' is missing
"""
            return retval
        else:
            try:
                self.cursor.execute(statement)
                self.connection.commit()
                if statement.lstrip().upper().startswith('SELECT'):
                    select = True
                    liste = self.cursor.fetchall()
                    if len(liste) == 0:
                        retval += ' Result: empty'
                    else:
                        retval += ' Result: \n'
                    for entry in liste:
                        retval += '> ' + str(entry) + '\n'
                        raw += str(entry) + '\n'
            except sqlite3.Error as e:
                success = False
                retval += ' Result: An error occured: ' + str(e.args[0]) + '!'
                raw += ' Result: An error occured: ' + str(e.args[0]) + '!'
            self.connection.close()
            if complete:
                if success:
                    if not select:
                        retval = retval + """ Result: The SQL statement was executed successfully.
"""
                return retval
            else:
                return raw

    def debugprint(self, text):
        if self.debug:
            print(str(text))


if __name__ == '__main__':
    dbv = DB_SQLite_GUI('videocenter', 3, True)
    dbv.fenster.mainloop()