Paste #5DiLcb3NQ

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

class TextFormat(object):

    @classmethod
    def formatText(self, text):
        retString = ''
        for c in text:
            if ord(c) <= 128:
                retString += c
            else:
                if ord(c) == 196:
                    retString += 'Ae'
                else:
                    if ord(c) == 214:
                        retString += 'Oe'
                    else:
                        if ord(c) == 220:
                            retString += 'Ue'
                        else:
                            if ord(c) == 228:
                                retString += 'ae'
                            else:
                                if ord(c) == 246:
                                    retString += 'oe'
                                else:
                                    if ord(c) == 252:
                                        retString += 'ue'
                                    else:
                                        if 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 ''
        if not text.endswith('.db'):
            text = text + '.db'
        for c in text.upper():
            if c not in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._':
                incorrect = True
                break

        if incorrect:
            return ''
        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._DB_SQLite_GUI__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 != '':
            if dbitem != self.db:
                self._DB_SQLite_GUI__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:
            if '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\n")
            ergv.fenster.mainloop()
        else:
            liste1 = sqltext.split(';')
            liste = []
            for el in liste1:
                cmd = el
                if cmd != '':
                    if ';' not in cmd:
                        cmd += ';'
                    else:
                        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 = '\n ============================================\n Datenbankabfrage:\n ============================================\n\n   SELECT <projektion> FROM <tabellenname(n) / view(s)> WHERE <selektion>;\n\n     Innerhalb der Projektion:\n     =========================\n     Angabe der Attribute (Spaltenüberschriften) in der gewünschten Reihenfolge\n     Tipp: * liefert alle Attribute\n     Hinweis: bei mehreren Tabellen sollte der Tabellenname vorangestellt werden\n              z.B.: kunden.kunr\n     \n     Funktionen:\n     -----------\n       Zählen:\n         COUNT(Primärschlüsselattribut) \n         Hinweis: Vermeide COUNT(*)\n       Maximum / Minimum:\n         MAX(Attribut)\n         MIN(Attribut)\n       Summe / Durchschnitt:\n         SUM(Attribut)\n         AVG(Attribut)\n         \n     Mehrfach auftretende Datensätze nur einmal nennen:\n       SELECT DISTINCT ...\n     \n     \n     Innerhalb der Selektion:\n     =========================\n\n       Vergleichsoperatoren:\n         >    ist größer als\n         <    ist kleiner als\n         >=   ist größer oder gleich als\n         <=   ist kleiner oder gleich als\n         =    ist gleich\n         between Wert1 AND Wert2     zwischen zwei Werten, inklusive Wert1 und Wert2         \n       \n         Bei Textvergleichen besser LIKE anstelle von = benutzen!       \n         LIKE \'%Textteil%\'       % ist ein beliebiger Textteil, \n                                 der auch leer sein kann\n         LIKE \'M_ier\'            _ ist genau ein beliebiges Zeichen\n         \n       IN: Liegt der Wert innerhalb einer Liste?\n         kuname IN (\'Cim\', \'Fischer\',  \'Gulder\',  \'Schmidt\') \n\n       Join: Verbinden zweier Tabellen (Entitäts- und Relationstabelle)\n       ----------------------------------------------------------------\n         Das Primärschlüsselattribut der Entitätstabelle wird mit dem\n         dazu passenden Fremdschlüsselattribut der Relationstabelle\n         gleichgesetzt.\n         Beispiel: kunden = Entitätstabelle und ausleiehe = Relationstabelle\n           kunden.kunr = ausleihe.kunr\n       \n       Sortieren:\n         ORDER BY Attribut ASC            aufsteigend\n         ORDER BY Attribut DESC           absteigend\n\n       Gruppieren:\n         GROUP BY Attribut\n       \n     Funktionen:\n     -----------\n       Länge:\n         LENGTH(Attribut)\n         \n     Logische Verknüpfungen:\n     -----------------------\n       AND, OR, NOT\n\n     Abfragen über mehrere Tabellen:\n       Beispiel: Alle Klausuren eines Schülers\n       SELECT schueler.vorname, klausur.fach\n       FROM schueler, klausur \n       WHERE schueler.vorname like "Ferdinand" AND schueler.nr = klausur.schueler;\n'
        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 = '\n ============================================\n Datensatz eintragen\n ============================================\n\n   INSERT INTO <tabellenname> VALUES (<WertAttribut1>, <WertAttribut2>, ... <WertAttributn>);\n\n   oder bei Nennung / Auslassung von Attributen:\n\n   INSERT INTO <tabellenname> (<attribut3>, <attribut1>, ...) VALUES (<WertAttribut3>, <WertAttribut1>, ... );\n\n   Beispiel:\n   INSERT INTO person VALUES ("S0815", "Heinemann", "Gustav");\n   oder bei anderer Reihenfolge\n   INSERT INTO person ("vorname", "nachname", "PID") VALUES ("Gustav", "Heinemann", "P0815");\n\n   TIPP: Manchmal möchte man einen neuen Datensatz hinzufügen oder, falls es diesen Datensatz bereits gibt,\n   den bestehenden Datensatz aktualisieren. Dann ergänzt man den INSERT-Befehl und schreibt:\n   \n   INSERT OR REPLACE INTO <tabellenname> (<attribut3>, <attribut1>, ...) VALUES (<WertAttribut3>, <WertAttribut1>, ... );\n\n'
        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 = '\n\n ============================================\n Datensatz aktualisieren\n ============================================\n\n   UPDATE <tabellenname> SET <attribut1> = "neuerWert1", <attribut2> = "neuerWert2", ... WHERE <selektion>;\n\n   Beispiel:\n\n   UPDATE person SET nachname = "Meier" WHERE PID = 4711;\n\n'
        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 = '\n\n ============================================\n Datensatz löschen\n ============================================\n\n   DELETE FROM <tabellenname> WHERE <selektion>;\n\n   Obacht: Abhängigkeiten beachten\n\n   Beispiel:\n   DELETE FROM schueler WHERE jahr < 2000;\n \n'
        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 = '\n \n ===================================================\n Datenbanktabelle anlegen:\n ===================================================\n \n   A. Entität mit 1 Primärschlüsselattribut\n   =================================================\n   CREATE TABLE <tabellenname>(\n     <prim_key_attribut1>  <datentyp(anzahl)> PRIMARY KEY,\n     <attribut2>  <datentyp(anzahl)>,\n     <attribut3>  <datentyp(anzahl)>,\n     ...\n     <attributn>  <datentyp(anzahl)>\n   );\n\n\n   Alle Primärschlüsselattribute müssen ausgefüllt werden.\n   Dürfen weitere Attribute wie im Beispiel Vor- und Nachname\n   nicht leer bleiben, schreibt man jeweils rechts daneben: NOT NULL\n\n   Beispiel:\n   CREATE TABLE person(\n     id            INT(10)     PRIMARY KEY,\n     vorname       VARCHAR(50) NOT NULL,\n     nachname      VARCHAR(50) NOT NULL,\n     geburtsdatum  VARCHAR(10),\n     mail          VARCHAR(60)\n   );\n \n   B. Entität mit mehreren Primärschlüsselattributen\n   =================================================\n   CREATE TABLE <tabellenname>(\n     <prim_key_attribut1>  <datentyp(anzahl)>,\n     <prim_key_attribut2>  <datentyp(anzahl)>,\n     ...\n     <attributn>  <datentyp(anzahl)>,\n     PRIMARY KEY (prim_key_attribut1, prim_key_attribut2, ...)\n   );\n   \n   Beispiel:\n   CREATE TABLE person(\n     vorname       VARCHAR(30),\n     nachname      VARCHAR(30),\n     geburtsdatum  VARCHAR(10),\n     mail          VARCHAR(60),\n     PRIMARY KEY (vorname, nachname)\n   );\n \n   C. Relation (n:m)\n   =================================================\n   CREATE TABLE <tabellenname> (\n     <fk_attribut1> <datentyp(anzahl)>, \n     <fk_attribut2> <datentyp(anzahl)>,\n     ...\n     <attributn> <datentyp(anzahl)>,\n     FOREIGN KEY (fk_attribut1) REFERENCES <tabelle1> (<prim_key_tabelle1>),\n     FOREIGN KEY (fk_attribut2) REFERENCES <tabelle2> (<prim_key_tabelle2>)\n   );\n   Hinweis 1: Die Datentypen müssen exakt mit den Primärschlüsselattributen übereinstimmen.\n   Hinweis 2: Hat eine Tabelle mehrere Primärschlüsselattribute, müssen diese auch bei den \n   Fremdschlüsseln zusammengefasst werden.\n   \n   Beispiel:\n   CREATE TABLE in_Kurs_einschreiben(\n      pv     VARCHAR(30),\n      pn     VARCHAR(30),\n      kn     VARCHAR(10),\n      datum  VARCHAR(10),\n      FOREIGN KEY (pv, pn) REFERENCES person(vorname, nachname),   \n      FOREIGN KEY (kn) REFERENCES kurs(kursnr)\n   );\n\n   D. Entität inkl. 1:n Relation\n   =================================================\n   CREATE TABLE <Tabelle> (\n     <attribut1> <datentyp(anzahl)> PRIMARY KEY,\n     <attribut2> <datentyp(anzahl)>,\n     <attribut3> <datentyp(anzahl)>,\n     ...\n     <fk_attributn> <datentyp(anzahl)>,\n     FOREIGN KEY (fk_attributn) REFERENCES <tabelle1> (<prim_key_tabelle1>)\n   );\n   Hinweis: Die Datentypen müssen exakt mit den Primärschlüsselattributen übereinstimmen.\n\n   Beispiel:\n   CREATE TABLE auto(\n     kennzeichen    VARCHAR(30) PRIMARY KEY,\n     fabrikat       VARCHAR(50),\n     modell         VARCHAR(30),\n     farbe          VARCHAR(50),\n     fahrer         INT(10),\n     FOREIGN KEY (fahrer) REFERENCES person(id)\n   );\n\n  E. Automatische Hochzählen des Primärschlüssels\n  ===============================================\n\n  Möchte man sich nicht selbst um die Nummerierung der Datensätze kümmern,\n  kann man das entsprechende Primärschlüsselattribut automatisch hochzählen\n  lassen.\n\n  Wichtig ist, dass man diese Nummer wie im Beispiel als Integer ohne Stellenangabe\n  definiert und als letztes das Schlüsselwort AUTOINCREMENT kommt.\n  \n  CREATE TABLE person (\n    nummer   INTEGER PRIMARY KEY AUTOINCREMENT,\n    vorname  VARCHAR(50),\n    nachname VARCHAR(50)\n  );\n\n'
        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 = '\n \n ============================================\n Datenbank-View anlegen:\n ============================================\n\n CREATE VIEW <viewname> AS <selection>;\n\n Beispiel:\n \n CREATE VIEW informatikkurs AS SELECT * from kurs WHERE fach LIKE "Informatik";\n\n'
        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 = '\n\n ============================================\n Datenbanktabelle löschen:\n ============================================\n\n   DROP TABLE <tabellenname>;\n\n   Obacht: Abhängigkeiten beachten\n\n   Beispiel:\n\n   DROP TABLE auto;\n\n ============================================       \n\n        '
        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:
    """ 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
                elif text[i] == ')':
                    klammerauf = False
                if text[i] == ',':
                    if 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 übergebene Projektion wird dabei berücksichtigt.
            Einschränkungen bei COUNT(*).
        """
        proj = proj.replace('DISTINCT ', '')
        if 'COUNT(' in proj.upper():
            return ['COUNT(*)']
        projektion = TextFormat.formatText(proj)
        if isView:
            return self.__attributesView__(table)
        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] == ' ':
                    if not found:
                        liste.append(s[:i])
                        found = True
                    else:
                        if s[i] == ',':
                            s = s[i + 2:]
                            break
                        else:
                            if s[i:i + 9].upper() == 'PRIMARY (':
                                s = ''
                                break
                            else:
                                if s[i:i + 12].upper() == 'PRIMARY KEY(':
                                    s = ''
                                    break
                                else:
                                    if 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 + ')'
                    if 'SUM (' + el in projektion:
                        eintrag = 'SUM (' + el + ')'
                    else:
                        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
        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 not s[i] == ',':
                    if 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ück.
        """
        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:
            try:
                errormsg += ' Result: An error occured: ' + str(e.args[0]) + '!'
            finally:
                e = None
                del e

        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ück.
        """
        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])) * ' '
                    else:
                        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)
        return (
         errormsg, len(liste), False)

    def sqlcommand(self, stmt, complete=True):
        """ Das übergebene Statement wird ausgeführt und committed.
        """
        success = True
        select = False
        self.__openConnection__()
        statement = TextFormat.formatText(stmt)
        retval = '\n I try to execute the SQL statement:\n ' + statement + '\n'
        raw = ''
        if str(statement).find(';') < 0:
            retval += " Result: incomplete SQL statement: ';' is missing\n"
            return retval
        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:
            try:
                success = False
                retval += ' Result: An error occured: ' + str(e.args[0]) + '!'
                raw += ' Result: An error occured: ' + str(e.args[0]) + '!'
            finally:
                e = None
                del e

        self.connection.close()
        if complete:
            if success:
                if not select:
                    retval = retval + ' Result: The SQL statement was executed successfully.\n'
            return retval
        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()