Falsches Fensterformat, bei Änderung der Spaltenanzahl in Tabelle

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

Hallo zusammen,

ich arbeite an einem tk-Projekt, bei dem das Hauptfenster folgende Komponenten beinhaltet:
Header, Label für Tabelle, Tabelle, xscroll und yscroll Tabelle, eine Infoeingabezeile, Positionsinfo zu Tabelle und Buttons.

Das Ganze funktioniert prima.
Wenn ich bei aktivem Hauptfenster, Tabellenzeilen hinzufüge, wird nach der Aktualisierung des Hauptfenster, das richtige Fensterformat ausgegeben.
Wenn ich aber dagegen Tabellenzeilen entferne, wird das Fensterformat falsch ausgegeben, das sich so verhält dass das Fenster der Tabelle sich nicht verändert, hingegen wird die Buttonzeile entsprechend abeschnitten.

Ich verwende grid und canvas.

Würde Euch gerne den Code posten, dieser ist aber zu umfangreich.

Vielleicht aber von Euch eine Idee, an was dieses merkwürdige Verhalten liegen könnte?

Grüße Nobuddy
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ohne Code? Nein. Da kann man nicht wirklich eine Idee bekommen, was da falsch laufen koennte.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hier ein Teilausschnitt:

Code: Alles auswählen

 .......
 ....
 ..
        # Aktualisierung Canvas.
        self.canvas.update_idletasks()
        self.x_window += max(self.x_window_list)
        # Layoutsteuerung für Canvas
        (self.x, self.y, self.x_table_scroll, self.y_table_scroll
            ) = WindowDimensions().set_up_dimensions(self)
        # Konfiguration Canvas mit aktuellen Werten für:
        # Scrollregion, Fensterbreite und Fensterhöhe.
        self.canvas.config(
                scrollregion=(self.canvas.bbox(tk.ALL)),
                width=self.x_table_scroll,
                height=self.y_table_scroll
                )
        # Aktualisierung Fenster
        self.screen_update()
        try:
            self.viewstart
        except AttributeError:
            self.viewstart = True
 
 
     def screen_update(self):
        """
        Aktualisierung, X- und Y-Koordinaten der GUI.
        """

        # Zentriere das Fenster auf dem Bildschirm
        x, y, hdw, hdh = CenterWindow().center_window(self)
        self.my_win.geometry('{}x{}+{}+{}'.format(x, y, hdw, hdh))
        self.my_win.update()

Code: Alles auswählen

class WindowDimensions(object):
    """
    Überprüfung und Berechnung der Größen x und y für canvas.
    """


    def set_up_dimensions(self, master):
        """
        Überprüfung und Aktualisierung der Fenster-Breite / Höhe.
        """

        # Zeichenweite
        sign_text_width = int(master.txt2[1])
        # Fensterbreite in X
        self.width_to_screen(master)
        # Fensterhöhe in Y
        self.heigth_to_screen(master)
        # Scrollbreite in X
        self.scroll_to_x(master)
        # Scrollhöhe in Y
        self.scroll_to_y(master)
        return master.x, master.y, master.x_table_scroll, master.y_table_scroll


    def width_to_screen(self, master):
        """
        Ermittlung der Fensterbreite, unter Beachtung der Displaybreite.
        """

        master.x = master.x_window
        if master.x_window > master.screenx:
            master.x = master.screenx
        return


    def heigth_to_screen(self, master):
        """
        Ermittlung der Fensterhöhe, unter Beachtung der Displayhöhe.
        """

        master.y = master.y_header + master.y_button + master.y_xscroll
        data_counter = len(master.data)
        diff_data_counter = 0
        if data_counter > 0:
            try:
                master.viewstart
                diff_data_counter = master.old_data_counter - data_counter
                y_row = diff_data_counter * -1 * master.y_row
                master.old_y += y_row
                master.y = master.old_y
                master.old_data_counter = data_counter
                master.old_y = master.y
            except AttributeError:
                faktor = MultipleRowHeader(master).check_screensize(master)
                master.y += round(master.y_table * 0.405 * faktor)
                master.y_row = round(master.y_table / data_counter)
                master.old_data_counter = data_counter
                master.old_y = master.y
        if master.y > master.screeny:
            master.y = master.screeny
        return


    def scroll_to_x(self, master):
        """
        Ermittlung der maximalen Scrollbreite für yscroll,
        unter Beachtung der Displaybreite.
        """

        master.x_table_scroll = master.x_window - master.x_yscroll
        if not master.table_work:
            # Leere Tabelle
            master.x_table_scroll = 0
        elif master.x_table_scroll > master.screenx:
            # Scrollbreite größer als Displaybreite
            master.x_table_scroll = (
                master.window.winfo_reqheight() - master.x_yscroll)
        return


    def scroll_to_y(self, master):
        """
        Ermittlung der maximalen Scrollhöhe für yscroll,
        unter Beachtung der Displayhöhe.
        """

        master.y_table_scroll = master.y_table
        if (master.screeny - master.y) < master.y_table:
            # Scrollhöhe größer als Displayhöhe
            master.y_table_scroll = master.screeny - master.y
        return
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Tut mir leid. Aber das ist so trotzdem nicht nachvollziehbar. Das einzige das auffällt - WindowDimensions ist keine Klasse. Ich vermute das gleiche gilt für CenterWindow. Benutz Module statt künstlich Namensräume für eine Reihe von Funktionen zu basteln durch eine Klasse. Aber mit dem Problem an sich hat das erstmal so nichts zu tun.

Wenn du kannst, Bau ein minimales Beispiel, welches das gleiche Verhalten erzeugt. Entweder fällig dir dabei schon selbst auf, wo du ein Problem hast. Oder wir können das dann nachvollziehen & entwanzen.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Ok, mache ich, wird aber ein paar Tage dauern, denn davor komme ich nicht mehr dazu.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Nobuddy: was auffällt, `update_idletasks` sollte in einem Programm nicht vorkommen. `x_window` wird offensichtlich nicht benutzt, oh halt, doch, in einer ganz anderen Klasse, sehr verwirrend. AttributeError ist normalerweise ein Programmierfehler und sollte nicht missbraucht werden um verspätet Attribute zu initialisieren. Initialisiere `viewstart` in __init__.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Habe hier einen funktionierenden Code, der das Problem erkennen lässt.
Wenn Du ein paar mal auf den Button "+Data" betätigst und dann das Gleiche mit dem "-Data" Button machst, kannst Du es erkennen, was ich meine.

Code: Alles auswählen

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


import tkinter as tk
from datetime import date, timedelta
now = date.today()
heute = now.strftime('%Y.%m.%d')


class BuildingWindow(object):
    """
    Bausteine zur Fenster-Erstellung.
    """

    def __init__(self, tk_status, document, label, data):

        # TK
        try:
            self.tk_status = tk_status
        except AttributeError:
            self.tk_status = 'tk'
        tk_check = {'tk' : tk.Tk, 'top' : tk.Toplevel}
        self.my_win = tk_check[self.tk_status]()
        self.my_win.protocol("WM_DELETE_WINDOW", self.close)
        self.label = label
        self.data = data
        self.table_width2column = dict([(i, len(x) + 10)
            for i, x in enumerate(data[0])])
        self.color = 'yellow'
        # Ermittle die Größe des Bildschirmes
        self.screenx = self.my_win.winfo_screenwidth()
        self.screeny = self.my_win.winfo_screenheight()
        # Basis-Fenster
        self.window = tk.Frame(
            self.my_win,
            bg=self.color
            )
        self.window.grid(
            row=0,
            column=0,
            sticky=tk.NSEW
            )
        self.window.grid_rowconfigure(0, weight=1)
        self.window.grid_columnconfigure(0, weight=1)


# Build formular

    def build_formulars_view(self):
        """
        Erstellung Formular-Fenster.
        """

        # Startwerte für Gesamtfenster.
        self.y_window = 17      # title
        self.x_window = 0
        self.x_window_list = list()
        # Erstelle scrollbar für y und x.
        self.table_work = False
        self.canvas = tk.Canvas(
            self.window,
            )
        self.canvas.grid(
            row=0,
            column=0,
            sticky=tk.NSEW
            )
        self.x_yscroll = 0
        self.y_xscroll = 0
        if len(self.data) > 0:
            self.table_work = True
            yscrollbar = tk.Scrollbar(
                self.window,
                orient=tk.VERTICAL,
                activebackground='lightsteelblue',
                troughcolor='yellow'
                )
            yscrollbar.grid(
                row=0,
                column=1,
                sticky=tk.NS
                )
            xscrollbar = tk.Scrollbar(
                self.window,
                orient=tk.HORIZONTAL,
                activebackground='lightsteelblue',
                troughcolor='yellow'
                )
            xscrollbar.grid(
                row=1,
                column=0,
                sticky=tk.EW
                )
            # Festlegung der Scrollregion.
            self.canvas.config(
                scrollregion=(0, 0, 0, 0),
                xscrollcommand=xscrollbar.set,
                yscrollcommand=yscrollbar.set)
            # Konfiguration Scrollbar in y und x.
            yscrollbar.config(command=self.canvas.yview)
            xscrollbar.config(command=self.canvas.xview)
            self.x_yscroll = yscrollbar.winfo_reqwidth()
            self.y_xscroll = xscrollbar.winfo_reqheight()
            self.y_window += self.y_xscroll
        # Buttons erstellen
        self.y_button = 0
        self.load_buttons()
        self.y_window += self.y_button
        # Erstelle Benennungsspalte.
        self.y_label = 0
        self.set_labels()
        # Erstelle Tabelle mit Daten.
        self.y_table = 0
        self.set_entrys()
        self.x_window += max(self.x_window_list)
        print('###################################')
        print('self.y_window', self.y_window)
        print('self.x_window', self.x_window)
        print('self.y_button', self.y_button)
        print('self.y_label', self.y_label)
        print('self.y_table', self.y_table)
        print('###################################')
        print(self.canvas.bbox(tk.ALL))
        # Layoutsteuerung für Canvas
        (self.x, self.y, self.x_table_scroll, self.y_table_scroll
            ) = self.set_up_dimensions()
        print(self.x, self.y, self.x_table_scroll, self.y_table_scroll)
        print('###################################')
        # Konfiguration Canvas mit aktuellen Werten für:
        # Scrollregion, Fensterbreite und Fensterhöhe.
        self.canvas.config(
                scrollregion=(self.canvas.bbox(tk.ALL)),
                width=self.x_table_scroll,
                height=self.y_table_scroll
                )
        # Aktualisierung Fenster
        self.screen_update()


    def add_data(self):
        """
        Füge einen Datensatz in die Tabelle ein.
        """

        new_row = self.data[-1].copy()
        new_pos = str(int(new_row[0]) + 1)
        position = '{}{}'.format((3 - len(new_pos)) * '0', new_pos)
        new_row[0] = position
        self.data.append(new_row)
        self.build_formulars_view()
        return


    def remove_data(self):
        """
        Füge einen Datensatz in die Tabelle ein.
        """

        del self.data[-1]
        self.build_formulars_view()
        return


    def screen_update(self):
        """
        Aktualisierung, X- und Y-Koordinaten der GUI.
        """

        # Zentriere das Fenster auf dem Bildschirm
        x, y, hdw, hdh = self.center_window()
        self.my_win.geometry('{}x{}+{}+{}'.format(x, y, hdw, hdh))
        self.my_win.update()


    def center_window(self):
        """
        Zentrierung von Fenster auf Bildschirm, 
        wenn Fenster kleiner als Bildschirm.
        """

        # Berechne die halbe Differenz zwischen Bildschirm und Fenster
        half_diff_width = 0
        if ((self.screenx - self.x) / 2) > 0:
            half_diff_width = round((self.screenx - self.x) / 2)
        half_diff_hight = 0
        if ((self.screeny - self.y) / 2) > 0:
            half_diff_hight = round((self.screeny - self.y) / 2)

        # Werte für das Zentrieren das Fensters auf dem Bildschirm
        return self.x, self.y, half_diff_width, half_diff_hight


    def set_up_dimensions(self):
        """
        Überprüfung und Aktualisierung der Fenster-Breite / Höhe.
        """

        # Fensterbreite in X
        self.width_to_screen()
        # Fensterhöhe in Y
        self.heigth_to_screen()
        # Scrollbreite in X
        self.scroll_to_x()
        # Scrollhöhe in Y
        self.scroll_to_y()
        return self.x, self.y, self.x_table_scroll, self.y_table_scroll


    def width_to_screen(self):
        """
        Ermittlung der Fensterbreite, unter Beachtung der Displaybreite.
        """

        self.x = self.x_window + self.x_yscroll
        if self.x_window > self.screenx:
            self.x = self.screenx + self.x_yscroll
        return


    def heigth_to_screen(self):
        """
        Ermittlung der Fensterhöhe, unter Beachtung der Displayhöhe.
        """

        self.y = self.y_window + self.y_table
        if self.y > self.screeny:
            self.y = self.screeny
        return


    def scroll_to_x(self):
        """
        Ermittlung der maximalen Scrollbreite für yscroll,
        unter Beachtung der Displaybreite.
        """

        self.x_table_scroll = self.x_window + self.x_yscroll
        if not self.table_work:
            # Leere Tabelle
            self.x_table_scroll = 0
        elif self.x_table_scroll > self.screenx:
            # Scrollbreite größer als Displaybreite
            self.x_table_scroll = self.screenx - (2*self.x_yscroll)
        return


    def scroll_to_y(self):
        """
        Ermittlung der maximalen Scrollhöhe für yscroll,
        unter Beachtung der Displayhöhe.
        """

        self.y_table_scroll = self.y_table
        if self.screeny < self.y:
            # Scrollhöhe größer als Displayhöhe
            self.y_table_scroll = self.screeny - self.y_window
        return


    def load_buttons(self):
        """
        Erstellung Buttons.
        """
        
        self.buttons = {
            0 : ('+ data', lambda: self.add_data()),
            1 : ('- data', lambda: self.remove_data()),
            3 : ('Schließen', lambda: self.close()),
            }

        self.button_frame = tk.Label(
            self.window,
            bg=self.color
            )
        self.button_frame.grid(
            row=2,
            column=0,
            sticky=tk.E
            )
        button_width = max([len(self.buttons[i])
            for i in self.buttons]) + 5
        x_sum = 0
        for i in sorted(self.buttons):
            name, command = self.buttons[i]
            self.button_widget = tk.Button(
                self.button_frame,
                width=button_width,
                text=name.upper(),
                command=command
                )
            self.button_widget.grid(
                row=0,
                column=i + 1
                )
            x_sum += self.button_widget.winfo_reqwidth()
        self.y_button += self.button_widget.winfo_reqheight()
        self.x_window_list.append(x_sum)
        return


    def set_labels(self):
        """
        Erstellung Benennungsspalte
        """

        ypos = 0
        xpos = 0
        for i, name in enumerate(self.label):
            width = self.table_width2column[i]
            label = tk.Label(
                self.canvas,
                width=width,
                text=name.upper(),
                bg='lightgreen',
                bd=1,
                highlightthickness=1,
                anchor=tk.NW
                )
            label.grid(
                row=0,
                column=i,
                )
            self.canvas.create_window(
                xpos,
                label.winfo_reqheight(),
                window=label,
                anchor=tk.NW
                )
            xpos += label.winfo_reqwidth()
        self.x_window_list.append(xpos)
        self.y_label += label.winfo_reqheight()
        return


    def set_entrys(self):
        """
        Erstellung Tabellenspalte
        """

        row_start = 0
        if self.y_label > 0:
            #self.y_table += self.y_label
            row_start = 1
            self.y_table += (2*self.y_label)
        for i, row in enumerate(self.data):
            xpos = 0
            for s, width in self.table_width2column.items():
                self.var = tk.StringVar(value=row[s].upper())
                self.entry = tk.Entry(
                    self.canvas,
                    text=self.var,
                    width=width,
                    bd=1,
                    highlightthickness=1
                    )
                self.entry.grid(
                    row=i + row_start,
                    column=s,
                    )
                self.canvas.create_window(
                    xpos,
                    self.y_table,
                    window=self.entry,
                    anchor=tk.NW
                    )
                xpos += self.entry.winfo_reqwidth()
            self.y_table += self.entry.winfo_reqheight()
            self.x_window_list.append(xpos)
        self.y_table -= self.y_label
        return


    def close(self):
        """
        Beendigung ohne Datenübernahme.
        """

        self.take_end = False
        self.my_win.quit()
        self.my_win.destroy()
        return


    def run(self, title):

        self.my_win.title(title)
        self.build_formulars_view()
        self.my_win.mainloop()


def main(document):
    tk_status = 'tk'
    client = 'kunde'
    label = ['position', 'artikel', 'benennung', 'hersteller',
        'herstellernummer', 've', 'inhalt', 'vk', 'mwst']
    data = [['001', '123456789', 'pppppppppppppppppppppppppppppppppppppppppppppppprodukt 123456789, din a4, weiß, 80g',
        'hersteller', 'herstellernummer', 'stück', '1', '3.25', '0.19']]

    title = '{}: {}'.format(document.upper(), client.upper())
    BuildingWindow(tk_status, document, label, data).run(title)

if __name__ == '__main__':
    document = 'test'
    main(document)
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Meine bisherige Erkenntnis, ist dass es irgendwas mit der yscrollbar zu tun hat.
Ich habe mal dies geändert:

Code: Alles auswählen

        try:
            self.viewstart
        except AttributeError:
            self.y_window = 17      # title
            self.row_counter = 0
            self.x_yscroll = 0
            self.y_xscroll = 0
            # Erstelle scrollbar für y und x.
            self.table_work = False
            self.canvas = tk.Canvas(
                self.window,
                )
            self.canvas.grid(
                row=0,
                column=0,
                sticky=tk.NSEW
                )
            if len(self.data) > 0:
                self.table_work = True
                self.yscrollbar = tk.Scrollbar(
                    self.window,
                    orient=tk.VERTICAL,
                    activebackground='lightsteelblue',
                    troughcolor='yellow'
                    )
                self.yscrollbar.grid(
                    row=self.row_counter,
                    column=1,
                    sticky=tk.NS
                    )
                self.xscrollbar = tk.Scrollbar(
                    self.window,
                    orient=tk.HORIZONTAL,
                    activebackground='lightsteelblue',
                    troughcolor='yellow'
                    )
                self.xscrollbar.grid(
                    row=self.row_counter+1,
                    column=0,
                    sticky=tk.EW
                    )
                # Festlegung der Scrollregion.
                self.canvas.config(
                    scrollregion=(0, 0, 0, 0),
                    xscrollcommand=self.xscrollbar.set,
                    yscrollcommand=self.yscrollbar.set)
                # Konfiguration Scrollbar in y und x.
                self.yscrollbar.config(command=self.canvas.yview)
                self.xscrollbar.config(command=self.canvas.xview)
                self.y_window += self.y_xscroll
Beim ersten Start, wird yscrollbar und xscrollbar erstellt.
Danach ändert sich das Fenster, je nach dem ich Daten hinzufügen oder lösche.
Allerdings sind die gelöschten Datenzeilen nicht wirklich gelöscht, sondern sind in der Tabelle durch scrollen in y sichtbar.
In der Datenliste, sind diese aber nicht mehr vorhanden........!?

Das ist wirklich bescheuert!
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Die einzigste Lösung, was ich gefunden habe, ist:

Code: Alles auswählen

    def add_data(self):
        """
        Füge einen Datensatz in die Tabelle ein.
        """

        new_row = self.data[-1].copy()
        new_pos = str(int(new_row[0]) + 1)
        position = '{}{}'.format((3 - len(new_pos)) * '0', new_pos)
        new_row[0] = position
        self.data.append(new_row)
        self.my_win.quit()
        self.my_win.destroy()
        BuildingWindow(self.tk_status, self.document, self.label, self.data
	    ).run(self.title)
        #self.build_formulars_view()
        return


    def remove_data(self):
        """
        Füge einen Datensatz in die Tabelle ein.
        """

        del self.data[-1]
        self.my_win.quit()
        self.my_win.destroy()
        BuildingWindow(self.tk_status, self.document, self.label, self.data
	    ).run(self.title)
        #self.build_formulars_view()
        return
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

`einzig` ist nicht steigerbar. Ein Code wo AttributeError für irgendwas krudes missbraucht wird, ist kein guter Ansatz um ein Programm aufzubauen.
In einem Programm sollte es auch nur einen Aufruf von mainloop geben. Es sollten auch nicht ständig das Hauptfenster zerstört und neu gebaut werden.
`BuildingWindow` hört sich vom Namen her mehr nach einer Funktion als einer Klasse an. Ist das überhaupt eine Klasse?

Suchst Du einfach nur entry.forget?
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Puh. Ich kann das zwar nachvollziehen. Doch simples forget reicht leider nicht, weil du da so unglaublich viel rumroedelst.

Gibt es einen Grund, warum du da das Rad mit so viel Muehe neu erfindest? Fuer solche tabellarischen Ansichten gibt es den TreeView.

https://stackoverflow.com/questions/361 ... rting-data

Und du solltest auch eine andere Struktur der ganzen GUI waehlen. Das sich das Anwendungsfenster um eine Zeile vergroessert, ist ungewoehnlich und ziemlich nervend. Wenn du das machen wuerdest wie jedes andere Programm (zB Excel), dann haettest du einen *Frame* (bzw Tree-View), der groesser und kleiner wird, und an dessen Position man hinscrollt.

Du erledigst hier viel Arbeit selbst, und das auf ungewoehnliche und fehleranfaellige Art und Weise, fuer die das GUI-Toolkit schon Mittel hat.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Dass mein letztes Code-Beispiel, natürlich Quatsch ist ist doch völlig klar, das ist so keine Lösung.

Habe das Gefühl, dass ich mich da im Kreis drehe ...., vielleicht ist ja TreeView die Lösung, kenne mich aber damit noch nicht aus.
Wie kann ich anhand dieses Code-Beispiel TreeView umsetzen?

Code: Alles auswählen

    def set_entrys(self):
        """
        Erstellung Tabellenspalte
        """
        for i, row in enumerate(self.data):
            xpos = 0
            print(i, row)
            for s, width in self.table_width2column.items():
                self.var = tk.StringVar(value=row[s].upper())
                self.entry = tk.Entry(
                    self.canvas,
                    text=self.var,
                    width=width,
                    bd=1,
                    highlightthickness=1
                    )
                self.entry.grid(
                    row=i + 1,
                    column=s,
                    )
                self.canvas.create_window(
                    xpos,
                    self.y_table,
                    window=self.entry,
                    anchor=tk.NW
                    )
                xpos += self.entry.winfo_reqwidth()
            self.y_table += self.entry.winfo_reqheight()
            self.x_window_list.append(xpos)
        self.y_table -= self.y_label
        return
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es sieht nicht so aus, als ob man das 1:1 uebertragen kann, da ein TreeView auch wirklich nur ein View ist. Das waere anders bei Qt, da kann man den ItemListView auch editierbar machen.

Aber die Loesung ist einfach, und so wie in dem von mir verlinkten Beispiel dargestellt: die Entries zum bearbeiten eines Eintrags sind extra, und der Inhalt einer ausgewaehlten Zeile wird dann darin zur Anzeige und Bearbeitung gebracht.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Habe mir das verlinkte Beispiel angeschaut.
Werde mich damit intensiver beschäftigen.

"die Entries zum bearbeiten eines Eintrags sind extra, und der Inhalt einer ausgewaehlten Zeile wird dann darin zur Anzeige und Bearbeitung gebracht."
Das würde mich interessieren!
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Siehe Beispiel.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Habe das Beispiel ein wenig modifiziert:

Code: Alles auswählen

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

import tkinter as tk
import tkinter.ttk as ttk
'''
Created on Mar 21, 2016

@author: Bill Begueradj
'''

class Begueradj(tk.Frame):
    '''
    classdocs
    '''  
    def __init__(self, parent, label, data, width):
        '''
        Constructor
        '''
        tk.Frame.__init__(self, parent)
        self.parent=parent
        self.label=label
        self.data=data
        self.width=width
        self.initialize_user_interface()

    def initialize_user_interface(self):
        """Draw a user interface allowing the user to type
        items and insert them into the treeview
        """
        self.parent.title("Canvas Test")       
        self.parent.grid_rowconfigure(0,weight=1)
        self.parent.grid_columnconfigure(0,weight=1)
        self.parent.config(background="lavender")

        self.tree = ttk.Treeview(self.parent, columns=self.label)
        for i, name in enumerate(self.label):
            name = name.upper()
            text = '{}:'.format(name)
            width = self.width[i] * 10
            # Set the treeview
            self.tree.heading('{}'.format(i), text=name, anchor=tk.W)
            self.tree.column('{}'.format(i), width=width, stretch=tk.YES)
            self.tree.grid(row=0, columnspan=i+1, sticky=tk.NSEW)
            # Define the different GUI widgets
            if name == 'LIEFERANT':
                self.supplier_label = tk.Label(self.parent, width=width,
                    bg='lavender', text = text, anchor=tk.E)
                self.supplier_entry = tk.Entry(self.parent)
                self.supplier_label.grid(row = 1, column = 0, sticky = tk.E)
                self.supplier_entry.grid(row = 1, column = 1, sticky = tk.W)
            elif name == 'ARTIKEL':
                self.article_label = tk.Label(self.parent, width=width,
                    bg='lavender',  text = text, anchor=tk.E)
                self.article_entry = tk.Entry(self.parent)
                self.article_label.grid(row = 2, column = 0, sticky = tk.E)
                self.article_entry.grid(row = 2, column = 1, sticky = tk.W)

        self.submit_button = tk.Button(self.parent, text = "Insert",
            command = self.insert_data)
        self.submit_button.grid(row = 3, column = 1, sticky = tk.W)
        self.exit_button = tk.Button(self.parent, text = "Exit",
            command = self.output_data)
        self.exit_button.grid(row = 3, column = 2, sticky = tk.W)

        self.treeview = self.tree
        # Initialize the counter
        self.i = 1


    def insert_data(self):
        """
        Insertion method.
        """
        data = self.data[(self.supplier_entry.get(), self.article_entry.get())]
        pos = '#{}{}'.format((3 - len(str(self.i))) * '0', self.i)
        data[0] = str(pos)
        self.treeview.insert('', 'end', text='ITEM_{}'.format(self.i),
            values=[x.upper() for x in data])
        # Increment counter
        self.i = self.i + 1  


    def output_data(self):
        """
        Output method.
        """
        self.parent.quit()
        for child in self.treeview.get_children():
            print(self.treeview.item(child)["values"])

def main():
    root=tk.Tk()
    label = ['position', 'lieferant', 'artikel', 'benennung', 'hersteller',
            'herstellernummer', 've', 'inhalt', 'vk', 'mwst']
    data = {
        ('123', '123456789') :['', '123', '123456789',
        'produkt 123456789, din a4, weiß, 80g',
        'hersteller', 'herstellernummer', 'stück', '1', '3.25', '0.19']
        }
    width = [10, 10, 20, 50, 25, 30, 10, 10, 15, 10]
    d=Begueradj(root, label, data, width)
    root.mainloop()

if __name__=="__main__":
    main()
Bei "EXIT" lasse ich mir die Values ausgeben.
Ich habe dabei bemerkt, dass bei reinen Zahlenfeldern, wenn eine "0" voran steht diese entfernt wird.
Es gibt ja z.B. Bestellartikel, bei der eine 0 voran steht > 01236689 == 1236689?
Ist das so gewollt, oder gibt es da einen Trick, dies zu unterbinden?

Auch, ist die erste Spalte sehr breit, wie kann ich da die Breite verändern?
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dein Beispiel funktioniert fuer mich nicht, beim versuch, ein Datum einzutragen kracht es, weil data ein Dictionary ist, bei dem du auf einen nicht-existierenden Schuessel zugreifen willst.

Code: Alles auswählen

(foobar) beer:tmp deets$ python /tmp/test.py
2018-12-06 18:20:29.929 Python[75513:738406] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to (null)
Exception in Tkinter callback
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
    return self.func(*args)
  File "/tmp/test.py", line 76, in insert_data
    data = self.data[(self.supplier_entry.get(), self.article_entry.get())]
KeyError: ('peter', 'wagen')
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Ja, ich habe hier ein Dictionary verwendet, um mit einem Produkt-Schlüssel, eine Datenzeile zu erstellen, im Gegensatz zur reinen Eingabe.
Als Schlüssel, verwende dies: Lieferant: 123, Artikel: 123456789 (Steht auch im Code unten)
Den KeyError kann man ja abfangen, was ich jetzt in dem Beispiel nicht getan habe.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Nobuddy: Doc-Strings für Module müssen vor den Importen stehen. Der Doc-String für die Klasse und __init__ sind nicht wirklich sinnvoll, bzw. falsch. Das was in `initialize_user_interface` steht sollte direkt __init__ stehen. Das Kind sollte nicht den `parent` konfigurieren.
Begueradj ist ein Frame, Du packst aber alles in parent. Die Widgets sollten aber im Frame sein.

supplier_label und article_label werden später nicht mehr gebraucht und sollten keine Attribute sein. Die beiden if-Blöcke sind quasi identisch und können zusammengefaßt werden. submit_button und exit_button werden später nicht mehr gebraucht und sollten keine Attribute sein.
`i` ist ein sehr schlechter Attributname.

Zeile 77ff: Wenn man führende Nullen will, macht man das über format-Parameter, pos ist schon ein String, den nochmal in einen String umzuwandeln ist unsinnig.

Code: Alles auswählen

data[0] = '#{:03d}'.format(self.i)
Warum konvertierst Du alles zu Großbuchstaben?
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Ich verwende Großbuchstaben in den Datenzeilen, dies vereinheitlicht ich die Texte.
Ich verwende dies z.B. bei Produktdaten. Ausschlaggebend war mit unter die verschiedenen Schreibweisen von Lieferanten.
Z.B. Herstellernummer: cB-123abc, Cb-123-abc, CB123-abc
Nach meiner Formatierung, wird daraus: CB123ABC
Buchstaben und Zahlen bleiben in der Reihenfolge bestehen, der Rest wird entfernt und ist nun untereinander vergleichbar.

Ich habe das Beispiel aus dem Beispiel-Link von weiter oben verwendet und wie gepostet abgeändert.
Das was Du über "Doc-Strings für Module müssen vor den Importen stehen" geschrieben hast, verstehe ich ansatzweise, weis aber nicht wie ich dies hier umsetzen soll, vielleicht kannst Du mir anhand von Code dies verdeutlichen?
Antworten