Layout mit grid

Fragen zu Tkinter.
Antworten
Maschinenhans
User
Beiträge: 20
Registriert: Donnerstag 14. Juli 2011, 12:23

Hallo,

ich habe ein Problem mit dem Layout.

Es geht um eine Kalenderprogramm. Ich dachte mir, im Programmfenster Frames anzulegen (ein Frame pro Tag), in die dann Datum etc. per label und listbox eintragen werden, wäre eine gute Idee. Zwar werden die widgets per grip() angeordnet, aber leider nicht am Programmfenster.

Eventuell kann sich mal einer den Code ansehen und mir klar machen, wo der Fehler liegt?

Datei: termine.py

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import views
import tkinter

__version__ = 'version 0.0.1'

# Global shortcuts
def key(event):
    # print(event.keysym)
    if event.keysym == 'q' or event.keysym == 'Q': # Quit application
        app.quit()

def main():
    '''Program execution'''
    global app
    app = tkinter.Tk()
    app.title('termine')
    #app.geometry('800x600')
    app.bind('<Key>', key)

    view = views.ViewWeekFilo(app)

    app.mainloop()
    
if __name__ == '__main__':
    main()
Datei: views.py

Code: Alles auswählen

import tkinter

class ViewWeekFilo():
    '''
    This is a "Filofax like" view with two columns and four rows.
    '''
    def frameDay(self, master, dateLine, appointments, gRow, gCol):
        '''Frame to show the Day'''
        frame = tkinter.Frame(master)
        frame.grid(row=gRow, column=gCol)
        lblMonDate = tkinter.Label(frame, text=dateLine, anchor='w')
        lblMonDate.grid(row='0')

        apts = tkinter.Listbox(frame)
        apts.grid(row='1')
        for a in appointments:
            apts.insert('end', a)

        return frame

    def __init__(self, master):
        day1 = ('- Geburtstag Kontakt', '- Feiertag', '07:00-18:00 Termin 1')
        day2 = ('18:00- ...  Termin 2',)
        day3 = (' ... -07:00 Termin 3',)
        day4 = ('',)
        day5 = ('',)
        day6 = ('',)
        day7 = ('',)

        frmMon = self.frameDay(master, '24.12. Montag', day1, 0, 0)
        frmTue = self.frameDay(master, '25.12. Dienstag', day2, 1, 0)
        frmWed = self.frameDay(master, '26.12. Mittwoch', day3, 2, 0)
        frmThu = self.frameDay(master, '27.12. Donnerstag', day4, 3, 0)
        frmFri = self.frameDay(master, '28.12. Freitag', day5, 0, 1)
        frmSat = self.frameDay(master, '29.12. Samstag', day6, 1, 1)
        frmSun = self.frameDay(master, '30.12. Sonntag', day7, 2, 1)
        
        frmTool = tkinter.Frame(master)
        frmTool.grid(row='3', column='1')
        self.lblToolDate = tkinter.Label(frmTool, text='KW: 51 / 2012', anchor='n')
        self.lblToolDate.grid(row='0')
Vielen Dank für eure Mühe.

Gruß
Maschinenhans
Alles wird gut.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Maschinenhans
aber leider nicht am Programmfenster
Verstehe leider nicht was du damit meinst.

Gruss wuf :wink:
Take it easy Mates!
Maschinenhans
User
Beiträge: 20
Registriert: Donnerstag 14. Juli 2011, 12:23

Hallo wuf,

ein Bild sagt mehr als tausend Worte; ich habe zwei gemacht :wink: .

Nach dem Einkommentieren der Zeile:
termine.py

Code: Alles auswählen

app.geometry('800x600')
kommt bei mir folgendes raus:
Bild

Das Programmfenster (Toplevel fenster?) hat eine zugewiesene Größe. Die widgets sind prinzipiell auch angeordnet, aber nicht mit dem Programmfenster (app) verankert. Das ist auch mein Problem.

Wenn ich die Größe des Programmfensters ändere, ändert sich die Anordnung der widgets dann natürlich auch nicht:
Bild

Meine Frage ist nun:
Warum werden die Frame widgets für die Einzeltage nicht per grid in dem Programmfenster (app) angeordnet?

Soweit ich das in den Tutorien verstanden habe ist durch "app = tkinter.Tk()" schon ein Frame für die Anwendung angelegt. Oder ist das Falsch und ich muß in dieses Toplevel widget erst mal einen einzelnen Frame legen, welcher dann die "Tage"-Frames enthält? Dann hätte ich ja einfach eine benötigte Ebene weggelassen. Das habe ich zwar schon probiert, bin aber zu keinem befriedigendem Ergebnis gekommen.

Selbstredend habe ich im Vorfeld zu der Frage ausgiebig Google und die Forumsuche befragt. Vielleicht frage ich auch falsch :K .

Gruß
Maschinenhans
Alles wird gut.
BlackJack

@Maschinenhans: Die Tage *werden* per Grid im Tk-Fenster angeordnet. Was Dir anscheinend fehlt, ist das Zeilen und Spalten des Grids mehr Platz einnehmen als ihr Inhalt benötigt, wenn mehr Platz im Elternwidget zur Verfügung steht. Das tun sie nämlich normalerweise nicht, das muss man extra sagen. Schau Dir mal hier http://www.effbot.org/tkinterbook/grid.htm die `grid_columnconfigure()`-Methode an, und da insbesondere das `weight`-Argument. Es gibt auch eine entsprechende Methode für die Zeilen des Grids.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Maschinenhans

Danke für deine Antwort. Du hast geschrieben:
Das Programmfenster (Toplevel fenster?) hat eine zugewiesene Größe. Die widgets sind prinzipiell auch angeordnet, aber nicht mit dem Programmfenster (app) verankert. Das ist auch mein Problem.
Das Programmfenster ist das Hauptfenster deiner Anwendung. Dieses kann in einer Anwendung nur einmal existieren. Ein Toplevelfenster ist ein zusätzliches Fenster, welches du mit dem Tk-Widget Toplevel erzeugst und zu deiner Anwendung gehört. Eine Anwendung kann natürlich auch mehrere Toplevelfenster beinhalten.

Was meinst du genau mit:
aber nicht mit dem Programmfenster (app) verankert
Meinst du damit, dass sich die Tages-Widgets beim verändern der Hauptfenstergrösse proportional skaliert an die neue Fenstergrösse anpassen, aber deren Anordung gleichbleibt?

Gruß wuf :wink:
Take it easy Mates!
Maschinenhans
User
Beiträge: 20
Registriert: Donnerstag 14. Juli 2011, 12:23

Hallo,

erst mal vielen Dank für die schnellen Antworten.
BlackJack hat geschrieben:@Maschinenhans: Die Tage *werden* per Grid im Tk-Fenster angeordnet. Was Dir anscheinend fehlt, ist das Zeilen und Spalten des Grids mehr Platz einnehmen als ihr Inhalt benötigt, wenn mehr Platz im Elternwidget zur Verfügung steht. Das tun sie nämlich normalerweise nicht, das muss man extra sagen. Schau Dir mal hier http://www.effbot.org/tkinterbook/grid.htm die `grid_columnconfigure()`-Methode an, und da insbesondere das `weight`-Argument. Es gibt auch eine entsprechende Methode für die Zeilen des Grids.
Das werde ich mir anschauen.
wuf hat geschrieben:Das Programmfenster ist das Hauptfenster deiner Anwendung. Dieses kann in einer Anwendung nur einmal existieren. Ein Toplevelfenster ist ein zusätzliches Fenster, welches du mit dem Tk-Widget Toplevel erzeugst und zu deiner Anwendung gehört. Eine Anwendung kann natürlich auch mehrere Toplevelfenster beinhalten.

Was meinst du genau mit, skaliert mit dem Programmfenster:
Zitat:
aber nicht mit dem Programmfenster (app) verankert
Meinst du damit, dass sich die Tages-Widgets beim verändern der Hauptfenstergrösse proportional skaliert an die neue Fenstergrösse anpassen, aber deren Anordung gleichbleibt?
Ja richtig. Hier will ich hin:

Bild

Gruß
Maschinenhans
Alles wird gut.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Maschinenhans

Jeder hat so seine Gewohnheiten. Auch ich. Der pack Layout-Manager ist mein Favorit. Mit ihm habe ich dir einen Prototyp zusammengestellt der sich in etwa so verhält wie du es geplant hast. Habe mich nur auf den Layout konzentriert. Das optimieren der Datenaufbereitung und einhalten der PEP-8 Richtlinien möchte ich dir überlassen. Sicher gibt es hier noch Forumfreunde die eher mit dem grid Lauyout-Manager vertraut sind und dir noch weitere Tipps geben können.

Hier das Prototyp-Startskript (termine_01.py:

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import views_01 as views
import tkinter as tk

__version__ = 'version 0.0.1'

# Global shortcuts
def key(event):
    # print(event.keysym)
    if event.keysym == 'q' or event.keysym == 'Q': # Quit application
        app.quit()

def main():
    '''Program execution'''
    app = tk.Tk()
    app.title('termine')
    app.geometry('600x400')
    app.bind('<Key>', key)
    
    main_frame = tk.Frame(app)
    main_frame.pack(fill='both', expand=True)
    
    view = views.ViewWeekFilo(main_frame)

    app.mainloop()
   
if __name__ == '__main__':
    main()
Das Layout-Skript (views_01.py):

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter as tk

LISTBOX_HEIGHT = 5 # Lines (Height of line is font dependend!)

class DayField(tk.Frame):
    
    def __init__(self, parent, date_line, appointments=None):
        
        tk.Frame.__init__(self, parent, bd=0, relief='raised')
        self.pack(fill='both', expand=True)
        
        self.label = tk.Label(self, text=date_line, anchor='w', fg='steelblue4')
        self.label.pack(fill='x', padx=4, pady=1)

        self.apts = tk.Listbox(self, bg='white', height=LISTBOX_HEIGHT,
            highlightthickness=0, bd=0)
        self.apts.pack(fill='both', padx=4)
        
        if appointments != None:
            for item in appointments:
                self.apts.insert('end', item)
                
class ViewWeekFilo():
    '''
    This is a "Filofax like" view with two columns and four rows.
    '''
    def __init__(self, parent):
        self.days = (
            ('- Geburtstag Kontakt', '- Feiertag', '07:00-18:00 Termin 1'),
            ('18:00- ...  Termin 2',),
            (' ... -07:00 Termin 3',),
            ('',),
            ('',),
            ('',),
            ('',),
            )

        frame_left = tk.Frame(parent)
        frame_left.pack(side='left', fill='both', expand=True)
        
        frmMon = DayField(frame_left, '24.12. Montag', self.days[0])
        frmTue = DayField(frame_left, '25.12. Dienstag', self.days[1])
        frmWed = DayField(frame_left, '26.12. Mittwoch', self.days[2])
        frmThu = DayField(frame_left, '27.12. Donnerstag', self.days[3])

        frame_right = tk.Frame(parent)
        frame_right.pack(side='left', fill='both', expand=True)

        frmFri = DayField(frame_right, '28.12. Freitag', self.days[4])
        frmSat = DayField(frame_right, '29.12. Samstag', self.days[5])
        frmSun = DayField(frame_right, '30.12. Sonntag', self.days[6])

        rmTool = DayField(frame_right, 'KW: 51 / 2012')
WICHTIG: Die Höhe der Listbox bzw. deren Anzahl Zeilen beachten. Habe sie mit folgender Konstante vorgegeben:

Code: Alles auswählen

LISTBOX_HEIGHT = 5 
Gruß wuf :wink:
Take it easy Mates!
Maschinenhans
User
Beiträge: 20
Registriert: Donnerstag 14. Juli 2011, 12:23

Hallo allerseits,

hab's dank eurer Hilfe jetzt, wie ich es möchte.

@wuf: Vielen Dank für den Prototyp mit pack(). grid() ist nur genau für diese Tabellarische Auflösung gemacht, dann sollte man ihn auch verwenden, denke ich. Da ich -wie soll ich sagen- "nicht vorbelastet" bin macht es für mich keinen Unterschied, ob ich pack()- oder grid() lerne. Bei grid() gibt nur die Stolpersteine "grid_(row|column)configure" und die Nummer mit "weight".

Nach dem der Tipp von BlackJack mich auf die richtige Fährte gebracht hatte, habe ich viel gelesen und im ~/python/sandkasten gespielt.
Nach der Erkenntnis, das per default in grid keine Größenanpassung der widgets erfolgt, diese aber mit "weight" mittels:
Datei: views.py

Code: Alles auswählen

frame.grid_rowconfigure(1, weight=1) # Zeile 1, da das label nicht skaliert wird
frame.grid_columnconfigure(0, weight=1)
angeschaltet werden kann und in jedem Frame (widget?), jede Zeile und jede Spalte, die eine Größenanpassung haben soll, auch angeschaltet werden muß, war es eine -auch für mich- lösbare Aufgabe. Der Satz ist ja mal kurz :? .

Das Resultat ist folgendes (die Datei "termine.py" bleibt wie sie war):
Datei: views.py

Code: Alles auswählen

#!/usr/bin/env python3

import tkinter

class WeekFilo():
    ''' This is a "Filofax like" view with two columns and four rows. '''

    def day(self, master, date_line, appointments, g_row, g_col):
        ''' Frame to show the Day '''
        
        frame = tkinter.Frame(master)
        frame.grid(row=g_row, column=g_col, sticky='wens')
        frame.grid_rowconfigure(1, weight=1)
        frame.grid_columnconfigure(0, weight=1)

        day_title = tkinter.Label(frame, text=date_line, anchor='w')
        day_title.grid(row='0', sticky='we')

        apts = tkinter.Listbox(frame)
        apts.grid(row='1', sticky='wens')
        for a in appointments:
            apts.insert('end', a)
        return frame

    def __init__(self, master):
        for c in 0, 1:
            master.grid_columnconfigure(c, weight=1)
        for r in 0, 1, 2, 3:
            master.grid_rowconfigure(r, weight=1)

        day1 = ('- Geburtstag Kontakt', '- Feiertag', '07:00-18:00 [D] Termin 1')
        day2 = ('18:00- ...  [D] Termin 2',)
        day3 = (' ... -07:00 [D] Termin 2',)
        day4 = ('',)
        day5 = ('',)
        day6 = ('',)
        day7 = ('',)

        monday = self.day(master, '24.12. Montag', day1, 0, 0)
        tuesday = self.day(master, '25.12. Dienstag', day2, 1, 0)
        wednesday = self.day(master, '26.12. Mittwoch', day3, 2, 0)
        thursday = self.day(master, '27.12. Donnerstag', day4, 3, 0)
        friday = self.day(master, '28.12. Freitag', day5, 0, 1)
        saturday = self.day(master, '29.12. Samstag', day6, 1, 1)
        sunday = self.day(master, '30.12. Sonntag', day7, 2, 1)
        
        tool = tkinter.Frame(master)
        tool.grid(row='3', column='1')
        self.tool_date = tkinter.Label(tool, text='KW: 51 / 2012', anchor='nw')
        self.tool_date.grid(row='0')
Ich habe auch die PEP8 übersetzung noch mal gelesen und hoffe nun keine groben Schnitzer mehr drinnen zu haben.

Vielen Dank für eure nette und schnelle Hilfe.

Jetzt kann es weiter gerhen mit der weiteren Formatierung der Ansicht, Datenaufbereitung usw.

Gruß
Maschinenhans
Alles wird gut.
Antworten