Tkinter nach 10 Buttons den Rest unten drunter

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
matze1708
User
Beiträge: 112
Registriert: Dienstag 12. März 2019, 11:49

Hallo,

ich habe folgenden Code:

Code: Alles auswählen

def buttons_places(self):
         self.frame_buttons_places = tk.Frame(self.master)
         occupied_places = sql_aufrufe.get_occupied_place_numbers(self.db, self.range_id)
         max_places= sql_aufrufe.get_max_place_number(self.db, self.range_id)
         label = tk.Label(self.frame_buttons_places, text = "Bitte tippe auf den Platz auf dem du schießt: ", font= "Arial 12 bold")
         label.pack(padx=10, pady=10)
         
         buttons = []
         for place in  range(1, max_places+1):
             button = tk.Button(self.frame_buttons_places, text = place , 
                         height = 3, width = 3, 
                         command=lambda button_value=place: self.get_place_from_button(button_value)) 
             if place in occupied_places:
                 button.config(state=tk.DISABLED)
             button.pack(padx=3, pady=3, side=tk.LEFT)
             buttons.append(button)
                   
         self.frame_buttons_places.pack(side=tk.TOP)
Da ich die Buttons über ein Raspberry Touch Display steuern mag, passen 20 Knöpfe nicht nebeneinander.

Wie kann ich nach maximal 10 Buttons die restlichen in eine weitere Zeile packen?

Danke und Liebe Grüße
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@matze1708: Da würde sich ein `grid()`-Layout anbieten und die `divmod()`-Funktion um aus einem Index die Zeilen- und Spaltennummer für den Platz im Grid zu berechnen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
matze1708
User
Beiträge: 112
Registriert: Dienstag 12. März 2019, 11:49

Hallo blackJack

danke für die Antwort, dass bedeutet aber das mein ganzens Skript auf grid umgestellt werden muss?
Ich habe aktuell die Idee, per if zuschauen ob der maxplace größer ist wie 10, dann in einen neuen Frame drunter. .... Weiss aber nicht ob das sinn macht.

ggf. muss ich wirklich alles umstellen, an paar anderen Stellen, wäre mir bessere Anpassbarkeit, lieber wie das da.
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@matze1708: Nein Du musst nicht alles auf Grid umstellen. Nur den Frame wo die Buttons drin sind.

Man darf Grid und Place nicht im gleichen Container-Widget mischen. In verschiedenen Container-Widgets kann man aber problemlos verschiedene Layout-Manager verwenden.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
matze1708
User
Beiträge: 112
Registriert: Dienstag 12. März 2019, 11:49

Ah geil!
Dann gucke ich mir das gleich mal an
matze1708
User
Beiträge: 112
Registriert: Dienstag 12. März 2019, 11:49

Ich schätze auf Grund meiner Struktur vermische ich dennoch beides.
Habe jetzt nur mal in o.g. Code auf grid geändert, das mag er nicht.
In der Klasse fange ich ja an mit pack. Dann vererbe ich mit self.master.

Lässt sich das irgendwie ändern?
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@matze1708: zeig den Code, den Du ausprobiert hast und sag, was daran nicht funktioniert.
matze1708
User
Beiträge: 112
Registriert: Dienstag 12. März 2019, 11:49

Das wäre der ganze Code

Code: Alles auswählen

import tkinter as tk
import sql_aufrufe



class MainWindow:
   
    
    
    def __init__(self, master):
        self.db = sql_aufrufe.connect()
        self.user_id = ""
        self.range_id = ""
        self.place_id = ""
        self.standaufsicht = "0"
        self.caliber_id = ""
        self.username = ""
        self.range_name = ""
        self.titel = tk.StringVar()
        
        self.titel.set("Schießbuch")
        
        self.master = master
        self.master.geometry("1200x480")
        self.master.title("Schießbuch")
        #self.master.attributes('-zoomed', True)
        
        self.frame1 = tk.Frame(self.master)
        self.label1 = tk.Label(self.frame1, textvariable = self.titel, font = "Arial 16 bold")
        self.label1.pack(padx=5, pady=20)
        self.frame1.pack(side=tk.TOP)
        
        self.frame_bottom = tk.Frame(self.master)
        self.label1 = tk.Label(self.frame_bottom, text = "Matthias Aßmann", font = "Arial 10")
        self.label1.pack(padx=5, pady=20, side=tk.BOTTOM)
        self.frame_bottom.pack(side=tk.BOTTOM)
    
    
        self.show_users()
    
        self.input_user_id()
  
    def run_again(self):
        self.label_username.pack_forget()
        self.frame_user.pack_forget()
        self.frame_show_users.pack_forget()
        self.user_id = ""
        self.range_id = ""
        self.place_id = ""
        self.standaufsicht = 0
        self.caliber_id = ""
        self.username = ""
        self.range_name = ""
        self.show_users()
        self.input_user_id()
        
    
    
    def show_users(self):
        self.frame_show_users =tk.Frame(self.master)
        users = sql_aufrufe.get_users(self.db)
        for row in users:
            user_label = tk.Label(self.frame_show_users, text= (row["ID"], row["UName"], row["UVorname"]),
                    fg = "red",
                    font = "Arial")
            user_label.pack(fill=tk.X, pady=10)
        self.frame_show_users.pack(side=tk.TOP)   
        
        
       
    def input_user_id(self):
        self.frame_user =tk.Frame(self.master)
        label = tk.Label(self.frame_user, text = "Bitte wähle deine ID aus: ", font = "Arial 12 bold")
        label.pack(padx=5, pady=20)
        self.entry_userid = tk.Entry(self.frame_user)
        self.entry_userid.pack(padx=5, pady=20, side=tk.LEFT)
        self.entry_userid.focus()
        self.button_choice_userid = tk.Button(self.frame_user, text ="OK", command = self.get_user_id_from_button)
        self.button_choice_userid.pack(padx=5, pady=20, side=tk.LEFT)
        self.frame_user.pack(side=tk.TOP)
        

    def get_user_id_from_button(self):
        self.frame_user.pack_forget()
        self.frame_show_users.pack_forget()
        self.user_id = self.entry_userid.get()
        self.entry_userid.delete(0, 'end')
        self.show_logged_in_user()
        
        
        if not sql_aufrufe.is_user_registered(self.db, self.user_id):
                self.frame_until_registerd = tk.Frame(self.master)
                label = tk.Label(self.frame_until_registerd, 
                    text = "Du bist bereits registriert! \n \n Möchtest du dich abmelden?", font = "Arial 14 bold")
                label.pack(padx=5, pady=20, side=tk.LEFT)
                self.button_yes_registered = tk.Button(self.frame_until_registerd, text ="Ja", command = lambda:(self.unregister_user(), self.frame_until_registerd.pack_forget()))
                self.button_yes_registered.pack(padx=5, pady=20, side=tk.LEFT)
                self.button_no_registered = tk.Button(self.frame_until_registerd, text ="Nein", command = lambda:(self.run_again(), self.frame_until_registerd.pack_forget()))
                self.button_no_registered.pack(padx=5, pady=20, side=tk.LEFT) 
                self.frame_until_registerd.pack(side=tk.TOP)               
        else:
                self.show_range(self.db)
                self.input_range_id()


    def show_logged_in_user(self):
        logged_in_user = sql_aufrufe.get_logged_in_user(self.db, self.user_id)
        for row in logged_in_user:
            self.label_username = tk.Label(self.frame1, text = "Hallo " + row["UVorname"] , font = "Arial 14")    
            self.label_username.pack(padx=5, pady=10, side = tk.TOP)
    
    def unregister_user(self):
        
        sql_aufrufe.update_user_unregister(self.db, self.user_id)
        
        self.frame_unregister_user = tk.Frame(self.master)
        self.label_unregister = tk.Label(self.frame_unregister_user, text = "Du hast dich erfolgreich abgemeldet!"  , font = "Arial 14")    
        self.label_unregister.pack(padx=5, pady=10, side = tk.LEFT)
        self.button_ok_unregister = tk.Button(self.frame_unregister_user, text ="OK", command = lambda:(self.run_again(), self.frame_unregister_user.pack_forget()))
        self.button_ok_unregister.pack(padx=5, pady=20, side=tk.LEFT)
        self.frame_unregister_user.pack()
        
                
        
        
        

    def show_range(self, db):
        self.frame_show_range =tk.Frame(self.master)
        range_str = sql_aufrufe.get_range(db)
        for row in range_str:
            range_label = tk.Label(self.frame_show_range, text= (row["ID"], row["StandLang"]),
                    fg = "red",
                    font = "Arial")
            range_label.pack(fill=tk.X, pady=10)
        self.frame_show_range.pack(side=tk.TOP)
        
        
    def input_range_id(self):
        self.frame_caliber =tk.Frame(self.master)
        label = tk.Label(self.frame_caliber, text = "Bitte wähle den Stand aus: ", font = "Arial 12 bold")
        label.pack(padx=5, pady=20)
        self.entry_rangeid = tk.Entry(self.frame_caliber)
        self.entry_rangeid.pack(padx=5, pady=5, side=tk.LEFT)
        self.entry_rangeid.focus()
        self.button_choice_rangeid = tk.Button(self.frame_caliber, text ="OK", command = self.get_range_id_from_button)
        self.button_choice_rangeid.pack(padx=5, pady=5, side=tk.LEFT)
        self.frame_caliber.pack(side=tk.TOP)


    def get_range_id_from_button(self):
        self.frame_show_range.pack_forget()
        self.frame_caliber.pack_forget()
        self.range_id = self.entry_rangeid.get()
        self.entry_rangeid.delete(0, 'end')
        
        if sql_aufrufe.is_everyone_current_standaufsicht(self.db, self.range_id):
                self.frame_no_standaufsicht = tk.Frame(self.master)
                label = tk.Label(self.frame_no_standaufsicht, 
                    text = "Es gibt aktuell keine Standaufsicht! \n \n Möchtest du Standaufsicht sein?", 
                    font = "Arial 14 bold")
                label.pack(padx=5, pady=20, side=tk.LEFT)
                self.button_yes_no_standaufsicht = tk.Button(self.frame_no_standaufsicht,  
                            text ="Ja", command = self.user_is_standaufsicht)
                self.button_yes_no_standaufsicht.pack(padx=5, pady=20, side=tk.LEFT) 
                self.button_no_no_standaufsicht = tk.Button(self.frame_no_standaufsicht, 
                            text ="Nein", command = lambda:(self.run_again(), self.frame_no_standaufsicht.pack_forget()))
                self.button_no_no_standaufsicht.pack(padx=5, pady=20, side=tk.LEFT) 
                self.frame_no_standaufsicht.pack(side=tk.TOP)               
        else:
                self.buttons_places()
                self.get_range_name()


    def get_range_name(self):
        range_str = sql_aufrufe.get_range_by_ID(self.db, self.range_id)
        for row in range_str:
            self.titel.set("Schießbuch - "+ row["StandLang"])
        

    
    def user_is_standaufsicht(self):
        self.frame_no_standaufsicht.pack_forget()
        self.standaufsicht = 1
        self.buttons_places()
        
        
        
    def check_standaufsicht_avalaible(self, db):
        self.frame_show_range =tk.Frame(self.master)
        range_str = sql_aufrufe.get_range(db)
        for row in range_str:
            range_label = tk.Label(self.frame_show_range, text= (row["ID"], row["StandLang"]),
                    fg = "red",
                    font = "Arial")
            range_label.pack(fill=tk.X, pady=10)
        self.frame_show_range.pack(side=tk.TOP)
        

    def buttons_places(self):
         self.frame_buttons_places = tk.Frame(self.master)
         occupied_places = sql_aufrufe.get_occupied_place_numbers(self.db, self.range_id)
         max_places= sql_aufrufe.get_max_place_number(self.db, self.range_id)
         label = tk.Label(self.frame_buttons_places, text = "Bitte tippe auf den Platz auf dem du schießt: ", font= "Arial 12 bold")
         label.pack(padx=10, pady=10)
         
         buttons = []
         for place in  range(1, max_places+1):
             button = tk.Button(self.frame_buttons_places, text = place , 
                         height = 3, width = 3, 
                         command=lambda button_value=place: self.get_place_from_button(button_value)) 
             if place in occupied_places:
                 button.config(state=tk.DISABLED)
             button.pack(padx=1, pady=3, side=tk.LEFT)
             buttons.append(button)
                   
         self.frame_buttons_places.pack(side=tk.TOP)


    def get_place_from_button(self, place1_id):
        self.frame_buttons_places.pack_forget()
        self.place_id = place1_id
        self.input_show_caliber_buttons()
   
   
    def input_show_caliber_buttons(self):
        self.frame_show_caliber =tk.Frame(self.master)
        caliber_str = sql_aufrufe.get_caliber(self.db, self.range_id)
        buttons = []
        
        for row in caliber_str:
            caliber_button = tk.Button(self.frame_show_caliber, text = row["KaliberLang"] ,
                height = 4, width = 20, 
                command=lambda caliber_value=row["ID"]: self.get_caliber_from_button(caliber_value)) 
                   
            caliber_button.pack(padx=3, pady=3, )
            buttons.append(caliber_button)
       
       
        self.frame_show_caliber.pack(side=tk.TOP)
        
    
    def get_caliber_from_button(self, caliber1_id):
        
        
        self.caliber_id = caliber1_id
        sql_aufrufe.insert_rifleman(self.db, self.user_id, self.range_id, self.place_id, self.caliber_id, self.standaufsicht) 
        self.frame_show_caliber.pack_forget()
        self.frame_register_user = tk.Frame(self.master)
        self.label_unregister = tk.Label(self.frame_register_user, text = "Du hast dich erfolgreich angemeldet!"  , font = "Arial 14")    
        self.label_unregister.pack(padx=5, pady=10, side = tk.LEFT)
        self.button_ok_unregister = tk.Button(self.frame_register_user, text ="OK", command = lambda:(self.run_again(), self.frame_register_user.pack_forget()))
        self.button_ok_unregister.pack(padx=5, pady=20, side=tk.LEFT)
        self.frame_register_user.pack()
        
        
              
        
 


def main():
    root = tk.Tk()
    app = MainWindow(root)
    root.mainloop()
    
    
    
    

if __name__ == '__main__':
    main()

matze1708
User
Beiträge: 112
Registriert: Dienstag 12. März 2019, 11:49

Das ist der geänderte Code,

Code: Alles auswählen

def buttons_places(self):
         occupied_places = sql_aufrufe.get_occupied_place_numbers(self.db, self.range_id)
         max_places= sql_aufrufe.get_max_place_number(self.db, self.range_id)
         self.frame_buttons_places = tk.Frame(self.master)
         label = tk.Label(self.frame_buttons_places, text = "Bitte tippe auf den Platz auf dem du schießt: ", font= "Arial 12 bold")
         label.grid(row=1, column=0)
         
         buttons = []
         for place in  range(1, max_places+1):
             button = tk.Button(self.frame_buttons_places, text = place , 
                         height = 3, width = 3, 
                         command=lambda button_value=place: self.get_place_from_button(button_value)) 
             if place in occupied_places:
                 button.config(state=tk.DISABLED)
             button.grid(row=1, column=0)
             buttons.append(button)
                   
         self.frame_buttons_places.grid()
Ich habe aber auf dem gleichen Fenster Dinge mit pack . Überschrift . Anmeldename
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@matze1708: Ähm, Du packst das Label und alle Buttons in die gleiche Zelle. Was erwartest Du da denn von, ausser das die dann alle übereinander in der gleichen Zelle stecken und man nur noch den obersten sieht, der da zuletzt drauf gestapelt wurde? Und eventuell noch den Teil vom Label der unter den Buttons hervor schaut wenn das grösser ist als die Buttons.

Das ist mir auch viel zu unübersichtlich. Alles steckt in dieser Gott-Klasse, fast jede Methode definiert irgendwelche neuen Attribute auf dem Objekt, wo insgesamt auch viel zu viele drauf sind. Das ist im Grunde ein Programm das Unmengen an globalen Variablen verwendet von Modulebene in eine Klasse verschoben. Es ist schwer da nachzuvollziehen welche Methode welche Widgets erstellt und wieder entfernt, und das muss dann auch noch mehrfach funktionieren. Normalerweise erstellt man die GUI-Elemente einmal alle am Anfang und erstellt und entfernt sie nicht dauernd. Man ”versteckt” sie höchstens, also das man sie alle in die gleiche Gridzelle steckt und dann das jeweils aktuell benötigte in der Z-Ebene nach ”oben” holt. Am besten auch mit der Möglichkeit nicht nur eine Seite weiter zu gehen, sondern auch einen Schritt zurück zu machen. Da hat man dann auch gemeinsame Teile die man in eine ”Wizard”-Klasse heraus ziehen kann. Ich glaube ich schrob es bereits das andere GUI-Rahmenwerke so etwas meistens schon fertig anbieten.

Was mir auch irgendwie fehlt ist eine getrennte Schicht für die Programmlogik zwischen der GUI und der Datenhaltung.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
matze1708
User
Beiträge: 112
Registriert: Dienstag 12. März 2019, 11:49

Welches Rahmenwerk würdest du mir dafür empfehlen.

Tatsächlich überlege ich auch einen zurück Knopf ein zubauen.

Wie trenne ich die Logik von der Datenhaltung ab?
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du sollst nicht die Logik von der Datenhaltung abtrennen. Du sollst die GUI von der Datenhaltung (und damit auch deren Logik) abtrennen. Und das machst du, indem du Klassen baust, die keinerlei GUI-Bezug haben, aber den kompletten Funktionsumfang der Abfrage und des Eintrags von deinen gewuenschten Daten haben. Und diese Klassen dann (sinnvollerweise in einem anderen Modul befindlich) von der GUI aus nur noch benutzt.
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Die beiden verbreiteten Rahmenwerke sind Gtk und Qt. Bei Gtk heisst das Assistent, bei Qt heisst das Wizard. Man muss nicht unbedingt dort hin wechseln, aber es lohnt sich sicherlich mal anzuschauen wie so etwas in modernen GUI-Rahmenwerken gelöst wird, also welche API die Klassen haben, und wie man sie verwendet. Beide Rahmenwerke haben auch ein ”stacked widget” was als Grundlage dient, was man sich in Tk auch selbst basteln muss.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
matze1708
User
Beiträge: 112
Registriert: Dienstag 12. März 2019, 11:49

jetzt muss ich mal Butter mit Fische machen.

Könnt ihr mir mal ein Beispiel zeigen, wie ich die GUI abhänge? Vielleicht wird dann mein Skript wirr warr auch einfacher.

Das doofe ist, das mein Skript ja in etwa so läuft wie es soll.... Jetzt muss nur der COde noch sinniger werden.

Ich habe ja für die ganzen Zugriffe auf den Datenbankserver die Datei SQL_aufrufe.py

Jetzt muss ich mal erkennen und verstehen, wie ich die Logik noch von der GUI trenne.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Da das ganze auf fielen Rechnern laufen soll, die alle auf einen gemeinsamen Server zugreifen, würde ich ja das ganze Projekt als Web-App schreiben. Chrome kann auch schon auf den Serialport zugreifen:
- https://developer.chrome.com/apps/serial
- http://p.cweiske.de/109

Vorteile sind zum einen, dass man keinen öffentlich erreichbaren DB-Server braucht, dass der Zugriff schön gekapselt ist, dass man keine neuen Versionen auf vielen Rechnern ausrollen muß, dass man eine schöne Oberfläche relativ einfach schreiben kann, ...
matze1708
User
Beiträge: 112
Registriert: Dienstag 12. März 2019, 11:49

Das würde heissen, ich könnte den ganzen Code im PHP schreiben und hole den RFID kram per Chrome Serial rein?

Oder falsch verstanden vor lauter Euphorie?
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wenn Du das in PHP schreiben möchtest, dann halt in PHP. Also auf dem Server. Für die Client-Seite brauchst Du dann JavaScript, oder irgendeine Sprache die nach JavaScript kompiliert wird.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@matze1708: ich weiß nicht, wie weit Du in PHP schon mit der Umsetzung bist, aber Du kannst auch den Server in reinem Python schreiben, die Datenbankanbindung wäre ja schon fertig. Auf Browserseite wären das natürlich Javascript und HTML/CSS.
matze1708
User
Beiträge: 112
Registriert: Dienstag 12. März 2019, 11:49

mhmm... ich sehe schon man kann das aus verschiedenen Ecken angehen.

Jetzt habe ich aber schon so ein Grund Gerüst in Python .....
matze1708
User
Beiträge: 112
Registriert: Dienstag 12. März 2019, 11:49

Sirius3 hat geschrieben: Donnerstag 4. April 2019, 14:09 @matze1708: ich weiß nicht, wie weit Du in PHP schon mit der Umsetzung bist, aber Du kannst auch den Server in reinem Python schreiben, die Datenbankanbindung wäre ja schon fertig. Auf Browserseite wären das natürlich Javascript und HTML/CSS.
Richtig, ich habe auch schon etwas in PHP geschrieben dazu. Aber noch lange nicht alles wie jetzt im Python.
Antworten