Problem mit StringVar() bei Tabellenausgabe

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
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen

Ich habe ein Problem bei der Tabellenausgabe mit StringVar().
Bei meinem jetzigen Konstrukt, wird immer die letzte Zeile der Liste in var übernommen, die vorhergehenden Zeilen werden ignoriert.
Ich habe es lange versucht, das Problem zu lösen, leider ohne Erfolg.

Ich poste hier mal mein Konstrukt, das lauffähig ist und Ihr so Euch einen Überblick verschaffen könnt.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x

import tkinter as tk


"""Hauptkonfiguration für die GUI Tkinter."""
CONFIG = {
    'width' : 0,
    'button_width' : 12,
    'title' : "Tabelle",
    'start_font': ('NimbusSansL', 80), # Startfenster
    'info_font': ('NimbusSansL', 40), # Infofenster
    'bbig_font': ('NimbusSansL', 14, 'bold'), # Buttons
    'btxt_bground': 'darkgrey', # Buttons
    'bfont_white': 'white',
    'bfont_color': 'darkred',
    'big_font': ('NimbusSansL', 14), # Buttons, Texteingabe
    'sub_font': ('NimbusSansL', 12), # Buttons, Texteingabe
    'txt_font': ('NimbusSansL', 12), # Textausgabe
    'txt_bground': 'grey', # Texteingabe, Textfenster, Buttons
    'font_color': 'black',
    'back_ground' : 'darkgrey'
    }


class TabelleWindow(object):
    """Listbox mit Scrollmenü.
    Tabellarische Ausgabe der Daten in Spalten.
    Tabellenausgabe, mit automatischer Anpassung an die Spaltenanzahl
    der jeweiligen Liste.

    Aufruf:
    TabelleScroll(LISTE, LISTE_FÜR_SPALTENBENENNUNG, MAXIMALE_SPALTENWEITE)
    Beispiel:
    TabelleScroll(self.data, ['Artikel', 'Benennung', 'Vk'], [10, 60, 4])

    LISTE = Liste
    LISTE_FÜR_SPALTENBENENNUNG = Liste wie z.B. ['Artikel', 'Benennung', 'Vk']
    MAXIMALE_SPALTENWEITE = Liste wie z.B. [10, 60, 4]"""

    def __init__(self, result, name, max_width):

        self.root = tk.Toplevel()
        self.conf = CONFIG
        self.root.title(self.conf['title'])
        self.result = result
        self.label_names = name
        self.max_width = max_width

        self.frame = tk.Frame(self.root)
        self.frame.pack(side='top', expand=False)
        self.winLabel = tk.Label(self.frame)
        self.winLabel.pack(expand=False)

        self.button_frame = tk.Label(self.frame,
            bg=self.conf['back_ground'])
        self.button_frame.pack()


    def set_scrolled_tables(self):

        tk.Button(self.button_frame, bg=self.conf['txt_bground'],
            width=self.conf['button_width'],
            font=(self.conf['big_font']), text="übernehmen",
            command=lambda: self.myResult()
            ).grid(row=0, column=0, sticky=tk.SW)
        tk.Button(self.button_frame, bg=self.conf['txt_bground'],
            width=self.conf['button_width'],
            font=(self.conf['big_font']), text="Schließen",
            command=lambda: self.root.destroy()
            ).grid(row=0, column=1, sticky=tk.SE)

        zeilen = len(self.result)
        if zeilen == 0:
            info('Es sind keine Daten vorhanden!')
            return
        names = len(self.label_names)
        w_max = sum(self.max_width)
        h_now = zeilen * 60
        h_max = self.root.winfo_screenheight() - 120
        if h_now < h_max:
            h_max = h_now
        
        self.tabellenWindow = tk.Frame(self.winLabel)
        self.tabellenWindow.grid(row=0, column=0, sticky='nswe')
        self.tabellenWindow.grid_rowconfigure(0, weight=1)
        self.tabellenWindow.grid_columnconfigure(0, weight=1)

        ## scrollbar erstellen
        yscrollbar = tk.Scrollbar(self.tabellenWindow)
        yscrollbar.grid(row=0, column=1, sticky='ns')

        label_container = tk.Frame(self.tabellenWindow,)
        label_container.grid(row=0, column=0,
            sticky=tk.NS)

        self.entry_vars = [tk.StringVar() for _ in self.label_names]

        canvas = tk.Canvas(self.tabellenWindow, bd=0, height=h_max,
            scrollregion=(0, 0, h_max, 500),
            yscrollcommand=yscrollbar.set)
        canvas.grid(row=0, column=0, sticky='nswe')
        self.label_frame = tk.Frame(canvas)

        xpos = 0
        ypos = 0
        ENTRY_IPADX = 5 # Abstand zu Text in x
        ENTRY_IPADY = 2 # Abstand zu Text in y
        if names != '':
            labels = list()
            for s in range(names):
                label = tk.Label(canvas, width=max_width[s],
                    text=name[s], font=('NimbusSansL', 14),
                    bg=self.conf['btxt_bground'],
                    fg=self.conf['bfont_white'],
                    bd=1, highlightthickness=1, anchor=tk.W)
                label.grid(row=0, column=s, padx=ENTRY_IPADX,
                    pady=ENTRY_IPADY)
                canvas.create_window(xpos, ypos, window=label,
                    anchor='nw')
                xpos += label.winfo_reqwidth()
                labels.append(label)
            xpos = 0
            ypos += label.winfo_reqheight()
        self.entries = list()
        for i, row in enumerate(self.result):
            print(i, row)
            for s, var in zip(range(names), self.entry_vars):
                entry = tk.Entry(canvas, textvariable=var,
                    width=max_width[s], font=('NimbusSansL', 14),
                    bg=self.conf['txt_bground'], bd=1,
                    highlightthickness=1)
                entry.grid(row=i, column=s, padx=ENTRY_IPADX,
                    pady=ENTRY_IPADY)
                try:
                    print(''.join(row[s]))
                    var.set(''.join(row[s]))
                except IndexError:
                    var.set('IndexError')
                self.entries.append(var)
                canvas.create_window(xpos, ypos, window=entry,anchor='nw')
                xpos += entry.winfo_reqwidth()
            xpos = 0
            ypos += entry.winfo_reqheight()

        yscrollbar = tk.Scrollbar(self.tabellenWindow)
        yscrollbar.grid(row=0, column=1, sticky='ns')
        yscrollbar.config(command=canvas.yview)

        canvas.update_idletasks()
        x, y, w, h = canvas.bbox(tk.ALL)
        canvas.config(width=w, yscrollcommand=yscrollbar.set)
        canvas.config(scrollregion=(x, y, w, h), width=w, height=h_max)

        return self.myResult(), self.frame.mainloop()
    

    def myResult(self):
        new_data = dict()
        for index, entry_var in enumerate(self.entry_vars):
            new_data[index] = entry_var.get()
        print(new_data)


if __name__ == '__main__':
    result = [['TomTom', '4711', 'Hier wird erklärt, was TomTom ist.',
        'Ausführliche Beschreibung zu TomTom.'],
        ['HalloHallo', '102302', 'Benennung Hallo.',
        'Ausführliche Beschreibung zu HalloHallo.']]
    name = ['Lieferant', 'Artikelnummer', 'Benennung', 'Beschreibung']
    max_width = [20, 20, 60, 60]
    TabelleWindow(result, name, max_width).set_scrolled_tables()
Ich hoffe, daß Ihr mir hier helfen könnt. :wink:

Grüße Nobuddy
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Sodele, habe selbst die Lösung :D

Der betreffende Code befindet sich von Zeile 126 bis Zeile 155.
Vielleicht habt Ihr eine Idee, wie es noch einfacher geht?

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x

import tkinter as tk


"""Hauptkonfiguration für die GUI Tkinter."""
CONFIG = {
    'width' : 0,
    'button_width' : 12,
    'title' : "Tabelle",
    'start_font': ('NimbusSansL', 80), # Startfenster
    'info_font': ('NimbusSansL', 40), # Infofenster
    'bbig_font': ('NimbusSansL', 14, 'bold'), # Buttons
    'btxt_bground': 'darkgrey', # Buttons
    'bfont_white': 'white',
    'bfont_color': 'darkred',
    'big_font': ('NimbusSansL', 14), # Buttons, Texteingabe
    'sub_font': ('NimbusSansL', 12), # Buttons, Texteingabe
    'txt_font': ('NimbusSansL', 12), # Textausgabe
    'txt_bground': 'grey', # Texteingabe, Textfenster, Buttons
    'font_color': 'black',
    'back_ground' : 'darkgrey'
    }


class TabelleWindow(object):
    """Listbox mit Scrollmenü.
    Tabellarische Ausgabe der Daten in Spalten.
    Tabellenausgabe, mit automatischer Anpassung an die Spaltenanzahl
    der jeweiligen Liste.

    Aufruf:
    TabelleScroll(LISTE, LISTE_FÜR_SPALTENBENENNUNG, MAXIMALE_SPALTENWEITE)
    Beispiel:
    TabelleScroll(self.data, ['Artikel', 'Benennung', 'Vk'], [10, 60, 4])

    LISTE = Liste
    LISTE_FÜR_SPALTENBENENNUNG = Liste wie z.B. ['Artikel', 'Benennung', 'Vk']
    MAXIMALE_SPALTENWEITE = Liste wie z.B. [10, 60, 4]"""

    def __init__(self, result, name, max_width):

        self.root = tk.Toplevel()
        self.conf = CONFIG
        self.root.title(self.conf['title'])
        self.result = result
        self.label_names = name
        self.max_width = max_width

        self.frame = tk.Frame(self.root)
        self.frame.pack(side='top', expand=False)
        self.winLabel = tk.Label(self.frame)
        self.winLabel.pack(expand=False)

        self.button_frame = tk.Label(self.frame,
            bg=self.conf['back_ground'])
        self.button_frame.pack()


    def set_scrolled_tables(self):

        tk.Button(self.button_frame, bg=self.conf['txt_bground'],
            width=self.conf['button_width'],
            font=(self.conf['big_font']), text="übernehmen",
            command=lambda: self.myResult()
            ).grid(row=0, column=0, sticky=tk.SW)
        tk.Button(self.button_frame, bg=self.conf['txt_bground'],
            width=self.conf['button_width'],
            font=(self.conf['big_font']), text="Schließen",
            command=lambda: self.root.destroy()
            ).grid(row=0, column=1, sticky=tk.SE)

        zeilen = len(self.result)
        if zeilen == 0:
            info('Es sind keine Daten vorhanden!')
            return
        names = len(self.label_names)
        w_max = sum(self.max_width)
        h_now = zeilen * 60
        h_max = self.root.winfo_screenheight() - 120
        if h_now < h_max:
            h_max = h_now
        
        self.tabellenWindow = tk.Frame(self.winLabel)
        self.tabellenWindow.grid(row=0, column=0, sticky='nswe')
        self.tabellenWindow.grid_rowconfigure(0, weight=1)
        self.tabellenWindow.grid_columnconfigure(0, weight=1)

        ## scrollbar erstellen
        yscrollbar = tk.Scrollbar(self.tabellenWindow)
        yscrollbar.grid(row=0, column=1, sticky='ns')

        label_container = tk.Frame(self.tabellenWindow,)
        label_container.grid(row=0, column=0,
            sticky=tk.NS)

        canvas = tk.Canvas(self.tabellenWindow, bd=0, height=h_max,
            scrollregion=(0, 0, h_max, 500),
            yscrollcommand=yscrollbar.set)
        canvas.grid(row=0, column=0, sticky='nswe')
        self.label_frame = tk.Frame(canvas)

        xpos = 0
        ypos = 0
        ENTRY_IPADX = 5 # Abstand zu Text in x
        ENTRY_IPADY = 2 # Abstand zu Text in y
        if names != '':
            labels = list()
            for s in range(names):
                label = tk.Label(canvas, width=max_width[s],
                    text=name[s], font=('NimbusSansL', 14),
                    bg=self.conf['btxt_bground'],
                    fg=self.conf['bfont_white'],
                    bd=1, highlightthickness=1, anchor=tk.W)
                label.grid(row=0, column=s, padx=ENTRY_IPADX,
                    pady=ENTRY_IPADY)
                canvas.create_window(xpos, ypos, window=label,
                    anchor='nw')
                xpos += label.winfo_reqwidth()
                labels.append(label)
            xpos = 0
            ypos += label.winfo_reqheight()

        self.entry_vars = list()
        [self.entry_vars.append(tk.StringVar()) for _ in self.result
            for i in range(names)]
        table_var = [((i, x, row), var) for ((i, x, row), var)
            in zip([(i, x, row[x]) for i, row in enumerate(self.result)
            for x in range(names)], self.entry_vars)]
        x = 0
        self.entries = list()
        while x < len(self.result):
            for row in table_var:
                i = row[0][0]
                s = row[0][1]
                var = row[1]
                if i == x:
                    entry = tk.Entry(canvas, textvariable=var,
                        width=max_width[s], font=('NimbusSansL', 14),
                        bg=self.conf['txt_bground'], bd=1,
                        highlightthickness=1)
                    entry.grid(row=i, column=s, padx=ENTRY_IPADX,
                        pady=ENTRY_IPADY)
                    try:
                        var.set(row[0][2])
                    except IndexError:
                        var.set('IndexError')
                    self.entries.append(var)
                    canvas.create_window(xpos, ypos, window=entry,anchor='nw')
                    xpos += entry.winfo_reqwidth()
            x += 1
            xpos = 0
            ypos += entry.winfo_reqheight()

        yscrollbar = tk.Scrollbar(self.tabellenWindow)
        yscrollbar.grid(row=0, column=1, sticky='ns')
        yscrollbar.config(command=canvas.yview)

        canvas.update_idletasks()
        x, y, w, h = canvas.bbox(tk.ALL)
        canvas.config(width=w, yscrollcommand=yscrollbar.set)
        canvas.config(scrollregion=(x, y, w, h), width=w, height=h_max)

        return self.myResult(), self.frame.mainloop()
    

    def myResult(self):
        new_data = dict()
        for index, entry_var in enumerate(self.entry_vars):
            new_data[index] = entry_var.get()
        print(new_data)


if __name__ == '__main__':
    result = [['TomTom', '4711', 'Hier wird erklärt, was TomTom ist.',
        'Ausführliche Beschreibung zu TomTom.'],
        ['HalloHallo', '102302', 'Benennung Hallo.',
        'Ausführliche Beschreibung zu HalloHallo.']]
    name = ['Lieferant', 'Artikelnummer', 'Benennung', 'Beschreibung']
    max_width = [20, 20, 60, 60]
    TabelleWindow(result, name, max_width).set_scrolled_tables()
Sirius3
User
Beiträge: 17745
Registriert: Sonntag 21. Oktober 2012, 17:20

@Nobuddy: Deine Tabelle funktioniert, wenn dann nur aus Zufall.
bei

Code: Alles auswählen

if namen != '':
vergleichst Du eine Zahl mit einem String.
Drei Zeilen später verwendest Du name, was nie definiert wurde.
Die for-Schleife schreibt sich besser als

Code: Alles auswählen

for s,name in enumerate(self.label_names):
xpos=0 sollte auch dort stehen, wo es benutzt wird, also jeweils vor der for Schleife.
Die List-Comprehension ist mehr als abenteuerlich.
Du meinst wohl

Code: Alles auswählen

        self.entry_vars = [tk.StringVar() for _ in range(names*len(self.result))]
        table_var = zip([(i, x, row[x]) for i, row in enumerate(self.result)
            for x in range(names)], self.entry_vars)
Achnee, das Entwirren der Schleifen geht ja noch weiter. Warum gehst Du immer wieder
die ganze Liste durch, um einzelne Indizes mit x zu vergleichen???

Code: Alles auswählen

        self.entry_vars = []
        for i, row in enumerate(self.result):
            xpos = 0
            for s, width in enumerate(max_width):
                var = tk.StringVar()
                entry = tk.Entry(canvas, textvariable=var,
                    width=width, font=('NimbusSansL', 14),
                    bg=self.conf['txt_bground'], bd=1,
                    highlightthickness=1)
                entry.grid(row=i, column=s, padx=ENTRY_IPADX,
                    pady=ENTRY_IPADY)
                try:
                    var.set(row[s])
                except IndexError:
                    var.set('IndexError')
                self.entry_vars.append(var)
                canvas.create_window(xpos, ypos, window=entry,anchor='nw')
                xpos += entry.winfo_reqwidth()
            ypos += entry.winfo_reqheight()
oder so ähnlich.
Du stellst wirklich komplizierte Irrgartenaufgaben.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo Sirius3

Danke für Deine Unterstützung, dachte mir daß dies einfacher gehen müßte, aber wenn man im Wald steht, kann es passieren, daß man vor lauter Wald keine Bäume mehr sieht. :wink:

Zum Ersten Deiner Punkte.
Richtig heißt es 'if names != '''.
Hier hast Du Recht, denn names bezieht sich auf die Anzahl der Überschriftspalten.
Ich habe es entsprechend geändert:

Code: Alles auswählen

if names > 0:
Dein Zweiter Punkt, läßt sich so nicht anwenden, da ja die Möglichkeit bestehen kann, daß es keine 'self.label_names' gibt aber 'self.result' jedoch Daten enthält. Daher auch zuvor die Abfrage 'if names > 0:'.
Das mit xpos=0 habe ich an dieser Stelle entfernt, da es ja oberhalb dieser for-Schleife schon steht.

Zum Dritten und Vierten Punk.
Da gebe ich Dir völlig Recht, meine List-Comprehension war wirklich abenteuerlich, zumal sie völlig überflüssig war und es wesentlich einfacher geht, wie Du mir in Deinem letzten Code-Stück aufzeigst.

Ich habe jetzt noch ein Problem.
Ich rufe das Modul aus einer GUI (tkinter) auf und übergebe dort auch die Daten, was soweit funktioniert.
Was nicht funktioniert ist die Rückmeldung, wenn ich Daten dort verändere.
Ich erhalte die Rückmeldung erst im Terminal, wenn die GUI geschlossen wurde.

Vielleicht hast Du eine Idee, an was das liegen könnte.

Ich poste noch zuletzt, nochmals den geänderten, aktuellen Code des Moduls.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x

import tkinter as tk


"""Hauptkonfiguration für die GUI Tkinter."""
CONFIG = {
    'button_width' : 12,
    'title' : "Tabelle",
    'btxt_bground': 'darkgrey', # Buttons
    'bfont_white': 'white',
    'bfont_color': 'darkred',
    'big_font': ('NimbusSansL', 14), # Buttons, Texteingabe
    'txt_bground': 'grey', # Texteingabe, Textfenster, Buttons
    'back_ground' : 'darkgrey'
    }


class TabelleWork(object):
    """Listbox mit Scrollmenü.
    Tabellarische Ausgabe der Daten in Spalten.
    Tabellenausgabe, mit automatischer Anpassung an die Spaltenanzahl
    der jeweiligen Liste.

    Aufruf:
    TabelleScroll(LISTE, LISTE_FÜR_SPALTENBENENNUNG, MAXIMALE_SPALTENWEITE).run()
    Beispiel:
    TabelleScroll(self.data, ['Artikel', 'Benennung', 'Vk'], [10, 60, 4]).run()

    LISTE = Liste
    LISTE_FÜR_SPALTENBENENNUNG = Liste wie z.B. ['Artikel', 'Benennung', 'Vk']
    MAXIMALE_SPALTENWEITE = Liste wie z.B. [10, 60, 4]"""

    def __init__(self, result, name, max_width):

        self.root = tk.Toplevel()
        self.conf = CONFIG
        self.root.title(self.conf['title'])
        self.result = result
        self.label_names = name
        self.max_width = max_width

        self.frame = tk.Frame(self.root)
        self.frame.pack(side='top', expand=False)
        self.winLabel = tk.Label(self.frame)
        self.winLabel.pack(expand=False)

        self.button_frame = tk.Label(self.frame,
            bg=self.conf['back_ground'])
        self.button_frame.pack()


    def set_scrolled_tables(self):

        zeilen = len(self.result)
        if zeilen == 0:
            return '', self.root.destroy()

        tk.Button(self.button_frame, bg=self.conf['txt_bground'],
            width=self.conf['button_width'],
            font=(self.conf['big_font']), text="übernehmen",
            command=lambda: self.myResult()
            ).grid(row=0, column=0, sticky=tk.SW)
        tk.Button(self.button_frame, bg=self.conf['txt_bground'],
            width=self.conf['button_width'],
            font=(self.conf['big_font']), text="Schließen",
            command=lambda: self.root.destroy()
            ).grid(row=0, column=1, sticky=tk.SE)

        names = len(self.label_names)
        w_max = sum(self.max_width)
        h_now = zeilen * 60
        h_max = self.root.winfo_screenheight() - 120
        if h_now < h_max:
            h_max = h_now
        
        self.tabellenWindow = tk.Frame(self.winLabel)
        self.tabellenWindow.grid(row=0, column=0, sticky='nswe')
        self.tabellenWindow.grid_rowconfigure(0, weight=1)
        self.tabellenWindow.grid_columnconfigure(0, weight=1)

        ## scrollbar erstellen
        yscrollbar = tk.Scrollbar(self.tabellenWindow)
        yscrollbar.grid(row=0, column=1, sticky='ns')

        label_container = tk.Frame(self.tabellenWindow,)
        label_container.grid(row=0, column=0,
            sticky=tk.NS)

        canvas = tk.Canvas(self.tabellenWindow, bd=0, height=h_max,
            scrollregion=(0, 0, h_max, 500),
            yscrollcommand=yscrollbar.set)
        canvas.grid(row=0, column=0, sticky='nswe')
        self.label_frame = tk.Frame(canvas)

        xpos = 0
        ypos = 0
        ENTRY_IPADX = 5 # Abstand zu Text in x
        ENTRY_IPADY = 2 # Abstand zu Text in y
        if names > 0:
            labels = list()
            for s in range(names):
                label = tk.Label(canvas, width=self.max_width[s],
                    text=self.label_names[s], font=('NimbusSansL', 14),
                    bg=self.conf['btxt_bground'],
                    fg=self.conf['bfont_white'],
                    bd=1, highlightthickness=1, anchor=tk.W)
                label.grid(row=0, column=s, padx=ENTRY_IPADX,
                    pady=ENTRY_IPADY)
                canvas.create_window(xpos, ypos, window=label,
                    anchor='nw')
                xpos += label.winfo_reqwidth()
                labels.append(label)
            ypos += label.winfo_reqheight()
        
        self.entry_vars = list()
        for i, row in enumerate(self.result):
            xpos = 0
            for s, width in enumerate(self.max_width):
                var = tk.StringVar()
                entry = tk.Entry(canvas, textvariable=var,
                    width=width, font=('NimbusSansL', 14),
                    bg=self.conf['txt_bground'], bd=1,
                    highlightthickness=1)
                entry.grid(row=i, column=s, padx=ENTRY_IPADX,
                    pady=ENTRY_IPADY)
                try:
                    var.set(row[s])
                except IndexError:
                    var.set('IndexError')
                self.entry_vars.append(var)
                canvas.create_window(xpos, ypos, window=entry,anchor='nw')
                xpos += entry.winfo_reqwidth()
            ypos += entry.winfo_reqheight()

        yscrollbar = tk.Scrollbar(self.tabellenWindow)
        yscrollbar.grid(row=0, column=1, sticky='ns')
        yscrollbar.config(command=canvas.yview)

        canvas.update_idletasks()
        x, y, w, h = canvas.bbox(tk.ALL)
        canvas.config(width=w, yscrollcommand=yscrollbar.set)
        canvas.config(scrollregion=(x, y, w, h), width=w, height=h_max)
    

    def myResult(self):
        self.new_data = dict([(index, entry_var.get())
            for index, entry_var in enumerate(self.entry_vars)])
        print(self.new_data)
        return self.new_data, self.root.destroy()


    def run(self):
        self.set_scrolled_tables()
        self.frame.mainloop()

        try:
            print('self.new_data', self.new_data)
            return self.new_data
        except AttributeError:
            pass


if __name__ == '__main__':
    result = [['TomTom', '4711', 'Hier wird erklärt, was TomTom ist.',
        'Ausführliche Beschreibung zu TomTom.'],
        ['HalloHallo', '102302', 'Benennung Hallo.',
        'Ausführliche Beschreibung zu HalloHallo.']]
    name = ['Lieferant', 'Artikelnummer', 'Benennung', 'Beschreibung']
    max_width = [20, 20, 60, 60]
    TabelleWork(result, name, max_width).run()
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich würde noch empfehlen, die beiden tief verschachtelten Abschnitte mit den Schleifen in eigene Methoden a la `.set_labels()` und `.set_entries()` auszulagern. Das mag zwar keinen direkten Nutzen in der Bedienung bringen, aber es lässt die Methode nicht so lang und hässlich aussehen. Mit dem Rest des Codes habe ich mich jetzt nicht näher befasst. Ins Auge fällt mir aber noch `.myResult()` - das ist ein Methodenname, den du vielleicht nochmal überdenken möchtest. Methoden beschreiben für gewöhnlich eine Sache, die sie tun. Unter `myResult()` kann man sich IMHO nicht wirklich etwas vorstellen.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo snafu

Das mit dem Unterteilen in zwei weitere Funktionen (a la `.set_labels()` und `.set_entries()`), ist eine Möglichkeit die ich mir noch überlegen werde.
Das mit `.myResult()` hat mir auch nicht besonders gefallen und habe es im Zuge einer Veränderung weggelassen.

Das Problem mit der Datenübergabe (Rückmeldung) habe ich jetzt auch gelöst.

Vielleicht Fällt Euch noch etwas gravierendes auf, poste hier meinen aktuell geänderten Code:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x

import tkinter as tk


"""Hauptkonfiguration für die GUI Tkinter."""
CONFIG = {
    'button_width' : 12,
    'title' : "Tabelle",
    'btxt_bground': 'darkgrey', # Buttons
    'bfont_white': 'white',
    'bfont_color': 'darkred',
    'big_font': ('NimbusSansL', 14), # Buttons, Texteingabe
    'txt_bground': 'grey', # Texteingabe, Textfenster, Buttons
    'back_ground' : 'darkgrey'
    }


class TabelleWork(object):
    """Listbox mit Scrollmenü.
    Tabellarische Ausgabe der Daten in Spalten.
    Tabellenausgabe, mit automatischer Anpassung an die Spaltenanzahl
    der jeweiligen Liste.

    Aufruf:
    Controller(LISTE, LISTE_FÜR_SPALTENBENENNUNG, MAXIMALE_SPALTENWEITE).run()
    Beispiel:
    Controller(self.data, ['Artikel', 'Benennung', 'Vk'], [10, 60, 4]).run()

    LISTE = Liste
    LISTE_FÜR_SPALTENBENENNUNG = Liste wie z.B. ['Artikel', 'Benennung', 'Vk']
    MAXIMALE_SPALTENWEITE = Liste wie z.B. [10, 60, 4]"""

    def __init__(self, controller, result, name, max_width):

        self.root = tk.Toplevel()
        self.controller = controller
        self.conf = CONFIG
        self.root.title(self.conf['title'])
        self.result = result
        self.label_names = name
        self.max_width = max_width

        self.frame = tk.Frame(self.root)
        self.frame.pack(side='top', expand=False)
        self.winLabel = tk.Label(self.frame)
        self.winLabel.pack(expand=False)

        self.button_frame = tk.Label(self.frame,
            bg=self.conf['back_ground'])
        self.button_frame.pack()


    def set_scrolled_tables(self):

        zeilen = len(self.result)
        if zeilen == 0:
            return '', self.root.destroy()

        tk.Button(self.button_frame, bg=self.conf['txt_bground'],
            width=self.conf['button_width'],
            font=(self.conf['big_font']), text="übernehmen",
            command=self.controller.end
            ).grid(row=0, column=0, sticky=tk.SW)
        tk.Button(self.button_frame, bg=self.conf['txt_bground'],
            width=self.conf['button_width'],
            font=(self.conf['big_font']), text="Schließen",
            command=lambda: self.root.destroy()
            ).grid(row=0, column=1, sticky=tk.SE)

        names = len(self.label_names)
        w_max = sum(self.max_width)
        h_now = zeilen * 60
        h_max = self.root.winfo_screenheight() - 120
        if h_now < h_max:
            h_max = h_now
        
        self.tabellenWindow = tk.Frame(self.winLabel)
        self.tabellenWindow.grid(row=0, column=0, sticky='nswe')
        self.tabellenWindow.grid_rowconfigure(0, weight=1)
        self.tabellenWindow.grid_columnconfigure(0, weight=1)

        ## scrollbar erstellen
        yscrollbar = tk.Scrollbar(self.tabellenWindow)
        yscrollbar.grid(row=0, column=1, sticky='ns')

        label_container = tk.Frame(self.tabellenWindow,)
        label_container.grid(row=0, column=0,
            sticky=tk.NS)

        canvas = tk.Canvas(self.tabellenWindow, bd=0, height=h_max,
            scrollregion=(0, 0, h_max, 500),
            yscrollcommand=yscrollbar.set)
        canvas.grid(row=0, column=0, sticky='nswe')
        self.label_frame = tk.Frame(canvas)

        xpos = 0
        ypos = 0
        ENTRY_IPADX = 5 # Abstand zu Text in x
        ENTRY_IPADY = 2 # Abstand zu Text in y
        if names > 0:
            labels = list()
            for s in range(names):
                label = tk.Label(canvas, width=self.max_width[s],
                    text=self.label_names[s], font=('NimbusSansL', 14),
                    bg=self.conf['btxt_bground'],
                    fg=self.conf['bfont_white'],
                    bd=1, highlightthickness=1, anchor=tk.W)
                label.grid(row=0, column=s, padx=ENTRY_IPADX,
                    pady=ENTRY_IPADY)
                canvas.create_window(xpos, ypos, window=label,
                    anchor='nw')
                xpos += label.winfo_reqwidth()
                labels.append(label)
            ypos += label.winfo_reqheight()
        
        self.entry_vars = list()
        for i, row in enumerate(self.result):
            xpos = 0
            for s, width in enumerate(self.max_width):
                var = tk.StringVar()
                entry = tk.Entry(canvas, textvariable=var,
                    width=width, font=('NimbusSansL', 14),
                    bg=self.conf['txt_bground'], bd=1,
                    highlightthickness=1)
                entry.grid(row=i, column=s, padx=ENTRY_IPADX,
                    pady=ENTRY_IPADY)
                try:
                    var.set(row[s])
                except IndexError:
                    var.set('IndexError')
                self.entry_vars.append(var)
                canvas.create_window(xpos, ypos, window=entry,anchor='nw')
                xpos += entry.winfo_reqwidth()
            ypos += entry.winfo_reqheight()

        yscrollbar = tk.Scrollbar(self.tabellenWindow)
        yscrollbar.grid(row=0, column=1, sticky='ns')
        yscrollbar.config(command=canvas.yview)

        canvas.update_idletasks()
        x, y, w, h = canvas.bbox(tk.ALL)
        canvas.config(width=w, yscrollcommand=yscrollbar.set)
        canvas.config(scrollregion=(x, y, w, h), width=w, height=h_max)


    def run(self):
        self.set_scrolled_tables()
        self.root.mainloop()


class Controller(object):

    def __init__(self, result, name, max_width):
        self.model = Model(result)
        self.view = TabelleWork(self, self.model.result, name, max_width)

    def end(self):
        self.view.root.quit()
        
    def run(self):
        self.view.run()
        self.new_data = dict([(index, entry_var.get())
            for index, entry_var in enumerate(self.view.entry_vars)])
        print(self.new_data)
        self.view.root.destroy()
        return self.new_data


class Model(object):

    def __init__(self, result):
        self.result = result


def main():
    try:
        Controller(result, name, max_width).run()
    except NameError:
        result = [['TomTom', '4711', 'Hier wird erklärt, was TomTom ist.',
            'Ausführliche Beschreibung zu TomTom.'],
            ['HalloHallo', '102302', 'Benennung Hallo.',
            'Ausführliche Beschreibung zu HalloHallo.']]
        name = ['Lieferant', 'Artikelnummer', 'Benennung', 'Beschreibung']
        max_width = [20, 20, 60, 60]
        Controller(result, name, max_width).run()


if __name__ == '__main__':
    main()
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Code: Alles auswählen

self.new_data = dict([(index, entry_var.get())
                      for index, entry_var in enumerate(self.view.entry_vars)])
Muss es wirklich ein `dict` an der Stelle sein?

Ansonsten ginge nämlich auch:

Code: Alles auswählen

self.new_data = [entry_var.get() for entry_var in self.view.entry_vars]
Die Benennung `self.new_data` finde ich jetzt auch nicht so prickelnd für ein Instanzattribut. Gibt es denn `old_data`? Und wenn doch eh schon `self.new_data` zurückgeliefert wird, warum dann nicht gleich so:

Code: Alles auswählen

def get_data(self):
    return [entry_var.get() for entry_var in self.view.entry_vars]
# ...
def run(self):
    self.view.run()
    self.data = self.get_data()
    # bzw alternativ auf direktem Wege (und wahrscheinlich auch besser):
    # self.data = [entry_var.get() for entry_var in self.view.entry_vars]
    self.view.root.destroy()
Und bitte lagere so umfangreichen Code auf etwas wie pastebin.com aus.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo snafu

In diesem Falle muß es kein 'dict' sein, da reicht auch völlig Dein Vorschlag.
Die Benennung `self.new_data` ist überhaupt nicht wichtig, da ja die Daten an das Hauptprogramm übergeben werden.

Bei `self.new_data` mußte ich die Daten, wieder zu Datensätze erstellen.
Beispiel Ausgabe '[entry_var.get() for entry_var in self.view.entry_vars]' von:
['Maier', '4711', 'Müller', '0815']
in
[['Maier', '4711'], ['Müller', '0815']]

Deine Vorschläge haben mir geholfen, das Ganze besser zu ordnen.
Auch `.set_labels()` und `.set_entries()` habe ich jetzt separiert.

Weiter habe ich eine sinnvolle Erweiterung vorgenommen.
Mit dem Button 'Neu', habe ich jetzt die Möglichkeit, neue Datensätze zu erstellen.

Ich habe meinen neuen Code bei pastebin.com unter dem Titel 'Tabellen-GUI' erstellt http://pastebin.com/LyzcJmVF.

Nachtrag!
In der Class Controller habe ich in der Funktion run() dies

Code: Alles auswählen

max_count = len(self.model.name)
durch dies ersetzt:

Code: Alles auswählen

max_line = max([len(row) for row in self.model.result])
run() sieht dann so aus:

Code: Alles auswählen

    def run(self):
        self.view.run()
        mydata = self.get_data()
        max_line = max([len(row) for row in self.model.result])
        data = list()
        self.new_data = list()
        for i, row in enumerate(mydata):
            if i < max_line:
                data.append(row)
            if i == (max_line - 1):
                self.new_data.append(data)
                data = list()
                max_line = max_line + max([len(row)
                    for row in self.model.result])
        print(self.new_data)
        return self.new_data
Grund dafür war, daß ja die Möglichkeit bestehen kann, daß es keine Liste für die Benennungsspalte gibt.
Daher habe ich die Spaltenanzahl aus self.model.result entnommen.

Grüße Nobuddy
Antworten