gui - datenbank (tabelle)

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Gast

Freitag 8. April 2005, 12:45

hallo zusammen,

update 1!

nachstehend eine anwendung die eine gui erstellt, die zugriff auf sqltabellen erlaubt.
dabei habe ich versucht so abstrakt zu programmieren, dass tabellen unterschiedlicher struktur genutzt werden können und keine oder nur minimale anpassungen nötig sind.

das ziel konnte ich mal erreichen, deshalb poste ich das teil in seiner momentanen fassung.

ich habe damit 2 tabellen ausprobiert, die sehr unterschiedlich waren. die erste hat einen primary key, die andere nicht, zudem unterscheiden sie sich in feldanzahl wie datentypen.

was ist noch nicht erledigt:

- alles was mit design zu tun hat, grins, doch schon etwas besser, zumindest wird nicht mehr
wildsaumäßig ein tupel in die gui geschmissen, ggg

- autoaktualisierung der ansicht,
(wird wohl bald erledigt sein)

- es gibt soch noch einiges an code der besser zusammengefasst, vereinfacht werden kann
naja, das wird wohl nie ganz erledigt sein.

- da viel code aus einer alten anwendung von mir wiederverwendet wurde, ist die namensgebung nicht einheitlich und sicher noch ein manko im codedesign.

was wurde sonst gemacht:
ich habe einige klassenvariablen gekillt, denke ist für die performance nicht uninteressant.

eine horizontale scrollleiste

fenster baut sich entsprechend der feldmenge dynamisch auf,
Eingabe-, BearbeitungsFenster positionieren sich neben dem HauptFenster, position wird entsrpechend der aktuellen breite des HF ermittelt.

Code: Alles auswählen


#!/usr/bin/env python


from Tkinter import*
from ScrolledText import*
import tkMessageBox
import sys
from database import *
           
        

               
class Fenster:


    #Abstand der Elemente
    a_x=5 #Abstand horizontal
    a_y=5 #Abstand vertikal

    #Die Buttons bekommen alle dieselbe Breite
    b=20

    #Farbe wird auch von la verwendet
    f="#00ff00"

    lt=""

    
    def __init__(self):

        raise NotImplementedError("Abstract class")

    

    def prepare(self):

        self.root.resizable(0,0)

        self.root.protocol("WM_DELETE_WINDOW",self.mainexit)

        la=Label(self.root,bg=self.f,text="///GARO_GUI")
        la.grid(row=0,column=0,            
                     columnspan=2,
                     padx=self.a_x,pady=self.a_y)
      
        la_1=Label(self.root,bg=self.f,text=self.lt)
        la_1.grid(row=1,column=0,
                       columnspan=2,
                       padx=self.a_x,pady=self.a_y)
       
        button=Button(self.root,
                           text="Prog. Beenden",
                           width=10,command=self.ende)
        button.grid(row=15,column=0,
                         columnspan=2,
                         padx=10,pady=10)



    def ende(self):

        antwort=tkMessageBox.askyesno\
                 ("Warnung","Sicher beenden?")
        if antwort==1:
            sys.exit(0)


    def mainexit(self):
        
        self.root.withdraw()

   
    
  
class HauptFenster(Fenster):

    lt="Ausgabe"

       
    def __init__(self,host,user,db,db_table,passwd=''):

        self.root=Tk()

        self.root.wm_geometry('+20+20')

        self.prepare()

        self.root.title("Hauptfenster")
          
        self.database=DataBase(host,user,db,db_table,passwd)

        self.db_table=db_table
        
        choice=StringVar()

        self.cols_names=[elements[0] for elements in self.database.db_table_cols
                         if 'auto_increment' not in elements]


        li_1width=len(self.cols_names)*12

        self.s_cols_names=", ".join(self.cols_names)


        scb_v=Scrollbar(self.root, orient="vertical")
        scb_h=Scrollbar(self.root, orient="horizontal")

        self.li_1=Listbox(self.root,
                          width=li_1width,height=8,
                          yscrollcommand=scb_v.set,
                          xscrollcommand=scb_h.set)
        
        scb_v["command"]=self.li_1.yview
        scb_h["command"]=self.li_1.xview
        
        self.li_1.grid(row=2,column=0,
                       columnspan=2,
                       padx=self.a_x,pady=self.a_y)
        
        scb_v.grid(row=2,column=2,
                      padx=self.a_x,pady=self.a_y)
        
        scb_h.grid(row=3,column=0,
                        columnspan=2,
                        padx=self.a_x,pady=self.a_y)
                       

        b_1=Button(self.root,width=self.b,text="Alle Daten",
                        command=lambda:
                        self.list_data
                        (result=self.database.get_data
                        ("select %s from %s"%(self.s_cols_names,self.db_table))))
        b_1.grid(row=4,column=0,
                      padx=self.a_x,pady=self.a_y)
        

        b_2=Button(self.root,width=self.b,
                        text="Loeschen",command=self.ask)
        b_2.grid(row=4,column=1,
                      padx=self.a_x,pady=self.a_y)
        
        
        b_3=Button(self.root,width=self.b,text="Sortieren",
                        command=lambda:
                        self.list_data
                        (result=self.database.get_data
                        ("select %s from %s order by %s"%(self.s_cols_names,self.db_table,choice.get()))))
        b_3.grid(row=5,column=0,
                      padx=self.a_x,pady=self.a_y)
        

        b_4=Button(self.root,width=self.b,text="Eingabe",
                        command=lambda:
                        EingabeFenster
                        (self.root,self.database,
                         self.db_table,
                         self.root.winfo_width()))
        b_4.grid(row=5,column=1,
                      padx=self.a_x,pady=self.a_y)


        b_5=Button(self.root,width=self.b,text="Suchen",
                        command=lambda:
                        SuchFenster
                        (self,self.root,self.database,self.db_table))
        b_5.grid(row=6,column=0,
                      padx=self.a_x,pady=self.a_y)


        b_6=Button(self.root,width=self.b,text="Bearbeiten",
                        command=lambda:
                        BearbeitungsFenster
                        (self.root,self.database,
                         self.db_table,self.li_1.get("active"),
                         self.root.winfo_width()))
        b_6.grid(row=6,column=1,padx=self.a_x,pady=self.a_y)


        for i in range(len(self.cols_names)):

            rb=Radiobutton(self.root,text=self.cols_names[i],
                        value=self.cols_names[i],
                        variable=choice)
            rb.grid(row=i+7,column=1,
                         padx=self.a_x,pady=self.a_y)

            if i==0:
                rb.select()

        self.list_data(result=self.database.get_data("select %s from %s"%(self.s_cols_names,self.db_table)))




    def list_data(self,result):
        
        self.li_1.delete(0,END)
        result=[list(element) for element in result]

        for element in result:
            for i in range(len(element)):
                element[i]=str(element[i])
                
        result=["   ".join(element) for element in result]

        for elements in result:
                self.li_1.insert("end",elements)
            

    def get_selected(self,action):

        selected=self.li_1.get("active")
        selected=selected.split()
        selected=[str(selected[i]).replace(str(selected[i]),'"'+str(selected[i])+'"')
                     for i in range(len(selected))]
                     
        target=[self.cols_names[i]+"="+selected[i]
                       for i in range(len(selected))]
        target=" and ".join(target)

        check=self.database.get_data("select * from %s where %s"%(self.db_table,target))
        check=check[0][0]

        action=action%check
        return action
    

    def ask(self):

        answer=tkMessageBox.askyesno("Warnung",
                                     "Wollen Sie den gewaehlten Datensatz sicher loeschen ?")
        if answer==1:
            self.database.set_data(action=self.get_selected("delete from %s where %s = %s"%(self.db_table,
                                             self.database.db_table_cols[0][0],
                                             "%s")))
        else:
            pass

            

       
class EingabeFenster(Fenster):

    lt="Eingabe/Bearbeitung"

    
    def __init__(self,root,database,db_table,hfwidth):

        self.root=Toplevel(root)

        self.root.wm_geometry('%s+20'%('+'+str(hfwidth+40)))

        self.root.title("Eingabefenster")

        self.prepare()

        self.database=database
        self.db_table=db_table


        self.cols_names=[elements[0] for elements in self.database.db_table_cols
                         if 'auto_increment' not in elements]

        self.s_cols_names=", ".join(self.cols_names)

        self.entrylist=[]

        for i in range(len(self.cols_names)):
            
            la=Label(self.root,
                          text=self.cols_names[i])
            la.grid(row=i+2,column=0)

            entry=Entry(self.root)
            entry.grid(row=i+2,column=1,
                            padx=self.a_x,pady=self.a_y)
            self.entrylist.append(entry)

        b_1=Button(self.root,width=self.b,
                        text="Eintragen",
                        command=lambda:
                        self.database.set_data
                        (action=self.set_sql
                        ("insert into %s (%s) values (%s)")))
                        
        b_1.grid(row=7,column=1,
                      columnspan=2,
                      padx=self.a_x,pady=self.a_y)
        
       
    def get_entries(self):

        entries=[element.get() for element in self.entrylist]
        
        for i in range(len(entries)):
            test=entries[i].find(" ")
            if test and test !=-1:
                entries[i]=entries[i].replace(entries[i][test],"/")
                
        for element in self.entrylist: element.delete(0,"end")
        
        entries=[entries[i].replace(entries[i],'"'+entries[i]+'"')
                      for i in range(len(entries))]

        return entries

    
    def set_sql(self,action):
        
        entries=self.get_entries()

        s_entries=", ".join(entries)
        
        action=action%(self.db_table,self.s_cols_names,s_entries)
        return action




class BearbeitungsFenster(Fenster,EingabeFenster):

    lt="Bearbeitung"
    

    def __init__(self,root,database,db_table,selected,hfwidth):
        
        self.selected=selected
        self.selected=self.selected.split()

        EingabeFenster.__init__(self,root,database,db_table,hfwidth)
        
        self.root.title("Bearbeitungsfenster")

        for i in range(len(self.entrylist)):
            self.entrylist[i].insert(0,self.selected[i])

        b_1=Button(self.root,width=self.b,
                        text="Eintragen",
                        command=lambda:
                        self.database.set_data
                        (action=self.set_sql
                        ("update %s set %s where %s=%s")))
                        
        b_1.grid(row=7,column=1,
                      columnspan=2,
                      padx=self.a_x,pady=self.a_y)
        
    
    def set_sql(self,action):

        entries=self.get_entries()

        result=[self.cols_names[i]+"="+entries[i]
                for i in range(len(entries))]
        result=", ".join(result)

        self.selected=list(self.selected)
        self.selected=[str(self.selected[i]).replace(str(self.selected[i]),'"'+str(self.selected[i])+'"')
                       for i in range(len(self.selected))]
        
        target=[self.cols_names[i]+"="+self.selected[i]
                for i in range(len(self.selected))]
        target=" and ".join(target)

        check=self.database.get_data("select * from %s where %s"%(self.db_table,target))
        check=check[0][0]
        
        self.cols_names_b=[elements[0] for elements in self.database.db_table_cols]

        action=action%(self.db_table,result,self.cols_names_b[0],check)
        return action
       

                   
class SuchFenster(Fenster):

    lt="Suche"


    def __init__(self,hf,root,database,db_table):

        self.root=Toplevel(root)

        self.root.title("Suchfenster")

        self.prepare()
        
        choice=StringVar()
        self.database=database
        self.db_table=db_table


        entry=Entry(self.root)
        entry.grid(row=3,column=0)

        for i in range(len(hf.cols_names)):

            rb=Radiobutton(self.root,text=hf.cols_names[i],
                                value=hf.cols_names[i],
                                variable=choice)
            rb.grid(row=i+2,column=1,
                         padx=self.a_x,pady=self.a_y)

            if i==0:
                rb.select()
           

        b_1=Button(self.root,text="Suche",
                        command=lambda:
                        hf.list_data
                        (result=self.database.get_data
                        ("select %s from %s where %s= '%s'"%(hf.s_cols_names,
                                                             self.db_table,
                                                             choice.get(),
                                                             entry.get()))))
                        
        b_1.grid(row=9,column=0)


if __name__ == "__main__":

    hf=HauptFenster("localhost","username","datenbank","tabelle","passwort")
    #hf=HauptFenster("localhost","username","andere datenbank", usw)

    mainloop()
hier noch das modul database

Code: Alles auswählen

#!/usr/bin/env python


import MySQLdb
from _mysql_exceptions import *


class DataBase:


    def __init__(self,host,user,db,db_table,passwd=''):
        
        try:
            self.conn=MySQLdb.connect(host=host,
                                      user=user,
                                      db=db,
                                      passwd=passwd)
   
             
        except OperationalError, msg:
            print msg[1]

        self.db_table_cols=self.get_data("show columns from %s"%db_table)

    
    def get_data(self,action):

        cursor=self.conn.cursor()
        cursor.execute(action)
        result=cursor.fetchall()
        cursor.close()
        return result


    def set_data(self,action):

        cursor=self.conn.cursor()
        cursor.execute(action)
        cursor.close()


    def close(self):
        self.conn.close()
kommentare erwünscht, vielleicht habt ihr nützliche tipps was ich noch ändern, überarbeiten sollte, bevor es in die 3. runde geht oben genannte todo list umzusetzen.

ich bedanke mich an dieser stelle noch einmal bei allen in diesem forum, die mir bei diesem projekt bis jetzt schon mit wertvollen tipps geholfen haben.

mfg

rolgal
Zuletzt geändert von Gast am Samstag 23. April 2005, 23:35, insgesamt 1-mal geändert.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Freitag 8. April 2005, 17:24

Kompliment, sieht recht eindrucksvoll aus - zumindest der Code, den Rest kann ich nicht beurteilen, weil ich Tkinter nicht installiert habe. Schön finde ich es auch zu wissen, daß wir jetzt einen MySQL-Experten unter uns haben ;-) - werde bei Gelegenheit darauf zurückgreifen, wenn es recht ist.

Gruß,
Christian
Gast

Freitag 8. April 2005, 18:11

hi christian,

danke für das kompliment, auch wenn es noch viel zu tun und verbessern gibt, bin ich doch auch schon etwas zufrieden damit, denke dass ich mit dem programm einen schritt nach vorne gemacht habe.
den Rest kann ich nicht beurteilen, weil ich Tkinter nicht installiert habe.
tja das design braucht noch viel pflege, dass mach ich immer zuletzt, ist was für die sommerferien :D
Schön finde ich es auch zu wissen, daß wir jetzt einen MySQL-Experten unter uns haben Wink - werde bei Gelegenheit darauf zurückgreifen, wenn es recht ist.
naja, experte ist sicherlich zuviel der ehre, aber ich kann es ja mal als vorschuss nehmen und sehen dass ich dem in zukunft gerecht werde :D

gruß

rolgal
Gast

Samstag 23. April 2005, 23:51

hallo zusammen,

wie oben angeführt gibt es ein erstes update,

mfg

rolgal
tabellarGast

Mittwoch 1. Juni 2005, 23:54

Hi rolgal,

ich bin zwar nicht der Prog Profi, aber ich hab doch einiges mit DBs und den entsprechenden Zugriffen darauf zu tun. Ein Vorschlag von mir wäre, dass du die SQL Statements aus der Fensterklasse rausnimmst und in deiner database Klasse integrierst. Ziel ist immer eine allgemeine DB API zu erstellen, damit die eigentliche Anwendung nicht vom Datenbanktyp abhängig ist. So kann dann leicht eine andere DB dahintergesteckt werden.

Gruss Tabellar
Gast

Donnerstag 2. Juni 2005, 11:33

hallo tabellarGast,

danke für deine anregung, ich werde das mal durchdenken und ausprobieren,
da ich mich eigentlich nur mit mysql beschäftige bin ich auf deine idee nicht gekommen.

grüße,


rolgal
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Donnerstag 2. Juni 2005, 11:48

Sowas für SQLite wäre sicher ziemlich nett :)
My god, it's full of CARs! | Leonidasvoice vs Modvoice
TabellarGast

Donnerstag 2. Juni 2005, 18:04

Ich hätte da noch ein paar Anregungen:

- Start/Initialisierung sollte OHNE Datenbankverbindungsaufbau geschehen
In der Laufzeitumgebung müsste es dann eine CONNECT Funktion geben, die mit der gewünschten Datenbank verbindet. Am schicksten wäre es natürlich, man könnte das ganze so kapseln, dass man z.B. MySQL, PostgreSQL, SQLite, Gadfly, MSAccess, CSV, XML ... auswählen kann :P

- Das ganze würde dann einen Datenbankhandler voraussetzen, der je nach gewünschter Datenbank das entsprechende DB API verwendet. Z.B. rolgalMySQLAPI.py, tabellarPGSQLAPI.py, leonidasSQLiteAPI.py, etc..

- Das ganze bräuchte dann so eine Art Plugin Mechanismus. Neue Datenbank, neues DBAPI Modul und los geht's...

Wenn ich heute abend dazu komme, teste ich mal Dein Programm. Dazu muss ich aber mal schauen, wo ich gerade eine MySQL DB installiert habe.
Meine derzeitige Lieblingsdatenbank ist PostgreSQL. Wenn ich dazu komme, mach ich dann mal eine DB API für PostgeSQL.

Damit mehrere hier im Forum mittesten können, müsste man vielleicht zuerst eine einfachere Datenanbindung haben, z.B. eine CSV oder XML Datei. Damit das alles dann auch funktioniert, liegt alleine am Datenbankhandler, der die nötige Datenkapselung mitbringt.

Tabellar
Gast

Donnerstag 2. Juni 2005, 21:03

hallo TabellarGast,

klingt alles gut und schön, man darf aber nicht die zielsetzung aus den augen verlieren, eine gui, die mit verschiedenen mysqltabellen umgehen kann, um z.b. die klassischen tabellen einer webseite am lokalen rechner zu verwalten, die haben oft ja nur eine tabelle.

dabei wollte ich, dass es entweder keine oder nur minimale anpassungen braucht...

inzwischen bastel ich daran, dass die anwendung auch mit datenbanken umgehen kann, die aus mehreren tabellen besteht, so weit funktioniert es auch schon, da ich aber mit dem code noch nicht zufrieden bin und manches sicher noch schöner gelöst werden kann, habe ich es noch nicht reingestellt, kommt aber bald

:D

auf was ich raus will, es gibt ständig was zu tun, und ich werde mich sicher mal mit deinen vorschlägen auseinandersetzen, aber die oben genannte zielsetzung soll zuerst optimiert werden auch andere dinge sind zuerst zu erledigen, die auf der todoliste stehen.

so muss das laufen, sonst wirds chaotisch,

danke trotzdem, ich hoffe du hast mich nicht falsch verstanden,

grüße

rolgal

i
BlackJack

Donnerstag 2. Juni 2005, 22:07

Wenn Du Dich erstmal auf Datenbanken beschränkst, deren Python-Modul die DB API 2.0 unterstützen, dann sind die grössten Unterschiede eigentlich nur der Verbindungsaufbau, also was konkret bei `connect()` übergeben werden kann/muss, und wie die Platzhalter bei SQL Anweisungen aussehen, wenn man das Datenbankmodul bei `execute()` Werte einfügen/escapen lässt.
tabellarGast

Freitag 3. Juni 2005, 00:17

Hi rolgal,

klar, Deine Zielsetzung darfst Du nicht aus den Augen verlieren!
Was ich da geschrieben habe ist vielleicht wirklich zu viel des guten... :roll:
Aber als Denkansatz waren die Gedanken vielleicht ganz gut.

Wie schon BlackJack erwähnte, wenn Du Python DB API 2.0 unterstützende
Module verwendest, passt das ganze schon. Trotzdem nochmal, versuch doch
schön am klassischen drei Schicht Modell zu bleiben:
1. Client (GUI, CLI, Interpretermode)
2. Funktionsbibliothek [table=Table("books",db), table.insert("v1" , "v2") etc.]
3. Data Storage (DB)

Tabellar
Gast

Freitag 3. Juni 2005, 01:31

hallo,...


....also auf grund der tatsache, dass meine wichtigste tätigkeit eigentlich eine ganz andere ist:
http://crossoverguitar.weberanto.net, die webseite ist natürlich nicht gemeint, werde ich allein wohl eine ewigkeit brauchen, die ganzen wertvollen anregungen umzusetzen.
mich nervt, dass es immer einen schöne lange zeit braucht bis man wieder in der materie richtig drin ist, wenn man abstand davon hatte :roll:


also stellt sich die frage, ob sich nicht ein paar leute finden, die gemeinsam dran weiterbasteln wollen?

grüße

rolgal
TabellarGast

Freitag 3. Juni 2005, 10:42

... ich spiele auch gerne Gitarre, zumindest früher ;-) . Ich werde mit meinen bescheidenen Programmierkenntnissen mal eine databasewrapper Klasse für Dein Programm erstellen, damit die DB-Funktionalität von dem GUI getrennt ist. Braucht aber ein bisschen, da ich viel am Arbeiten bin (Brötchen) und am Abend dann noch meine eigenen Projekte habe. Aber das Thema databaseWrapper und TKinter interessiert mich...

Tabellar
Gast

Freitag 3. Juni 2005, 19:17

hi tabellargast,

super, bin schon gespannt auf deinen vorschlag,...

habe mir auch schon einige gedanken gemacht,...

wenn es richtig gut ist, kriegst ein paar gratisstunden :D für dein gitarrespiel...

grüße

rolgal
Gast

Freitag 3. Juni 2005, 22:56

also, ich dachte mal in kleinen schritten:

Code: Alles auswählen

>>> sql={"mysql":{"list":"select* from user"},"posql":{"list":"select bla bla bla"}}
>>> db_type="mysql"
>>> sql[db_type]['list']
'select* from user'
etwas weiter gedacht...

Code: Alles auswählen

>>> sql={"mysql":{"list":"select* from user"},"posql":{"list":"select bla bla bla"}}
>>> def sql_action(db_type,command,host,user,database,password):
	
	    if db_type=="mysql":
		    from dbApis import MySqlApi
		    database=MySqlApi(host,user,database,password)
		    result=database.get_data(sql[db_type][command])
		    return result

	
>>> sql_action("mysql","list","localhost","rolgal","crossover","ein passwort")
zum besseren verständnis, dbApis könnte so aussehen z.b.

Code: Alles auswählen

class MySqlApi:

    import MySQLdb
    from _mysql_exceptions import *


    def __init__(self,host,user,db,passwd=''):

        self.db=db
        
        try:
            self.conn=MySQLdb.connect(host=host,
                                      user=user,
                                      db=db,
                                      passwd=passwd)
   
             
        except OperationalError, msg:
            print msg[1]


    
    def get_data(self,action):

        cursor=self.conn.cursor()
        cursor.execute(action)
        result=cursor.fetchall()
        cursor.close()
        return result


    def set_data(self,action):

        cursor=self.conn.cursor()
        cursor.execute(action)
        cursor.close()
 

    def close(self):
        
        self.conn.close()
so in etwas könnte das hinhauen, oder?


die frage ist:
wann und wie ermittelt man am besten den typ, wenn man es überhaupt so macht wie oben angedacht...

güße

rolgal
Antworten