Aktuelle Scrollposition

Fragen zu Tkinter.
Antworten
DMD-OS
User
Beiträge: 165
Registriert: Freitag 28. Dezember 2018, 13:52

hallo
in meinem code:

Code: Alles auswählen

    def coloring_row(self, preview_lst):
        preview_name, preview_max_rows, preview_max_columns, preview_user = preview_lst
        for lb_cnt, lb_lst in enumerate(self.label_lst):
            for label in lb_lst:
                label.configure(bg="white")
            lb_lst[preview_user-1].configure(bg="powderblue")
            if lb_cnt == 0:
                print("User-Eingabe:", preview_user)
                label_id = str(self.number_lst[preview_user-1])
                print("Label:", label_id)
                # print(self.canvas.nametowidget(label_id))
                print("x:", self.number_lst[preview_user-1].winfo_x(), self.number_lst[preview_user-1].winfo_rootx())
                print("y:", self.number_lst[preview_user-1].winfo_y(), self.number_lst[preview_user-1].winfo_rooty())
                # Hier muss die y-Koordinate des Labels der Zeile: 'User-Eingabe' rein.
                # self.canvas.yview(tk.MOVETO, 0.8)  # <-- canvas_y
                self.canvas.yview_moveto(float(self.number_lst[preview_user-1].winfo_y()))
makiere ich die durch den User ausgewählte Zeile in einer Tabelle farblich.
Ich versuche jetzt zusetzlich, die Scrollbar in die ausgesuchte Zeile springen zu lassen.
Doch die Scrollbar-Position wechselt nur zwischen 0 (für Eingabe 1) oder 1 (für Eingabe 2-...)
Wenn man 1 eintippt, ist sie ganz oben, ab Eingabe 2 ist sie immer ganz unten.
Könnte mir jmd helfen, wie ich die richtige float-Zahl für die Scrollbar rausbekomme?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

y-Koordinate geteilt durch Höhe des Canvas. Beschrieben in der Dokumentation. https://effbot.org/tkinterbook/canvas.h ... te-systems
DMD-OS
User
Beiträge: 165
Registriert: Freitag 28. Dezember 2018, 13:52

Klappt noch nicht ganz:

Code: Alles auswählen

    def coloring_row(self, preview_lst):
        preview_name, preview_max_rows, preview_max_columns, preview_user = preview_lst
        for lb_cnt, lb_lst in enumerate(self.label_lst):
            for label in lb_lst:
                label.configure(bg="white")
            lb_lst[preview_user-1].configure(bg="powderblue")
            if lb_cnt == 0:
                print("User-Eingabe --> Zeile:", preview_user)
                # y-Koordinate geteilt durch Höhe des Canvas. Beschrieben in der Dokumentation.
                canvas_y = self.canvas.canvasy(self.number_lst[preview_user - 1].winfo_y())
                canvas_height = self.canvas.winfo_height()
                scroll_y = float(canvas_y/canvas_height)
                print("canvas_höhe:", canvas_height, "canvas-y-coordinate:", canvas_y, "scrollwert:", scroll_y)
                self.canvas.yview_moveto(scroll_y)
Ausgabe:

Code: Alles auswählen

User-Eingabe --> Zeile: 1
canvas_höhe: 145 canvas-y-coordinate: -2.0 scrollwert: -0.013793103448275862
User-Eingabe --> Zeile: 2
canvas_höhe: 145 canvas-y-coordinate: 27.0 scrollwert: 0.18620689655172415
User-Eingabe --> Zeile: 3
canvas_höhe: 145 canvas-y-coordinate: 137.0 scrollwert: 0.9448275862068966
User-Eingabe --> Zeile: 4
canvas_höhe: 145 canvas-y-coordinate: 379.0 scrollwert: 2.613793103448276
User-Eingabe --> Zeile: 5
canvas_höhe: 145 canvas-y-coordinate: 408.0 scrollwert: 2.8137931034482757
User-Eingabe --> Zeile: 6
canvas_höhe: 145 canvas-y-coordinate: 437.0 scrollwert: 3.013793103448276
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst ja auch durch die “virtuelle” Höhe des Canvas teilen. Wenn die nicht größer als die sichtbare ist, dann gibt’s nix zu scrollen.
DMD-OS
User
Beiträge: 165
Registriert: Freitag 28. Dezember 2018, 13:52

Kannst du mir helfen, wie ich die rausbekomme :)
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Bitte den Link den ich gepostet habe gut durchlesen. Da steht es doch drin. canvas.bbox(ALL) gibt dir die Dimensionen des Canvas.
DMD-OS
User
Beiträge: 165
Registriert: Freitag 28. Dezember 2018, 13:52

Kannste mir noch mal helfen?
Ich habe versucht, deinen Tipp einzubauen.

Code: Alles auswählen

def coloring_row(self, preview_lst):
    preview_name, preview_max_rows, preview_max_columns, preview_user = preview_lst
    for lb_cnt, lb_lst in enumerate(self.label_lst):
        for label in lb_lst:
            label.configure(bg="white")
        lb_lst[preview_user-1].configure(bg="powderblue")
        if lb_cnt == 0:
            print("User-Eingabe --> Zeile:", preview_user)
            lb_y = self.canvas.canvasy(self.number_lst[preview_user - 1].winfo_y())
            _, y1, _, y2 = self.canvas.bbox(tk.ALL)
            scroll_y_value = float(lb_y / (y2-y1))
            print(scroll_y_value)
            self.canvas.yview_moveto(scroll_y_value)
    print('---')
Ergebnis

Code: Alles auswählen

User-Eingabe --> Zeile: 1
-0.004597701149425287
User-Eingabe --> Zeile: 2
0.06206896551724138
User-Eingabe --> Zeile: 3
0.19080459770114944
User-Eingabe --> Zeile: 4
0.38620689655172413
User-Eingabe --> Zeile: 5
0.6482758620689655
User-Eingabe --> Zeile: 6
0.9770114942528736
User-Eingabe --> Zeile: 7
1.071264367816092
Insgesamt habe ich 15 Zeilen. Nach der 7 wird die 1 schon überschritten :(
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich vermute mal dein winfo_y-Zeug ist Unfug. Du musst halt die korrekten Koordinaten des entsprechenden Elementes rausfinden. Wie das geht steht auch alles in der Dokumentation. Du hast da ja irgendwas angelegt, und fuer eine item-id bekommst du auch die Position, *wo* das in Canvas-Koordinaten ist: https://effbot.org/tkinterbook/canvas.h ... rds-method
DMD-OS
User
Beiträge: 165
Registriert: Freitag 28. Dezember 2018, 13:52

hi
ich habe nun meinen code vereinfacht zusammengefasst, aus dem mein problem deutlich wird.
das problem ist, daß ich es nicht richtig hinbekomme die höhe eines labels in einem canvas korrekt auszulesen,
und zwar so, daß das die Label-Höhe mit der Scrollhöhe zusammenpasst.
in diesem beispiel ist schon bei eingabe der zeile 5 das ende der Scrollhöhe erreicht, obwohl es insgesamt 15 zeilen gibt :(
ich hoffe, jemand kann mir helfen :)

Code: Alles auswählen

import tkinter as tk


val_list = [['Spalte_1',  'Spalte_2',  'Spalte_3', 'Spalte_4'],
            ['a', 'b', 'c', 'd'],
            ['e', 'f', 'g', 'h'],
            ['i', 'j', 'k', 'l'],
            ['m', 'n', 'o', 'p'],
            ['q', 'r', 's', 't'],
            ['u', 'v', 'w', 'x'],
            ['y', 'z', '', ''],
            ['aa', 'bb', 'cc', 'dd'],
            ['ee', 'ff', 'gg', 'hh'],
            ['ii', 'jj', 'kk', 'll'],
            ['mm', 'nn', 'oo', 'pp'],
            ['qq', 'rr', 'ss', 'tt'],
            ['uu', 'vv', 'ww', 'xx'],
            ['yy', 'zz', '', '']
            ]

root = tk.Tk()
root.geometry("300x350+500+200")

info_frame = tk.Frame(root)
info_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=False, padx=5)

preview_frame = tk.Frame(root)
preview_frame.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True, padx=5)
preview_frame.columnconfigure(0, weight=1)
preview_frame.rowconfigure(0, weight=1)

canvas = tk.Canvas(preview_frame, bg="lightgrey", bd=1, highlightthickness=1)
canvas.grid(row=0, column=0, sticky=tk.NSEW, padx=1, pady=1)
vsbar = tk.Scrollbar(preview_frame, orient=tk.VERTICAL, command=canvas.yview)
vsbar.grid(row=0, column=1, sticky=tk.NS)
hsbar = tk.Scrollbar(preview_frame, orient=tk.HORIZONTAL, command=canvas.xview)
hsbar.grid(row=1, column=0, sticky=tk.EW)
canvas.configure(xscrollcommand=hsbar.set, yscrollcommand=vsbar.set)
label_frame = tk.Frame(canvas, bg="lightgrey")
user_input = tk.StringVar()

number_lst = []
label_lst = []


def main():
    user_frame = tk.Frame(info_frame)
    user_frame.grid(column=0, row=2, sticky=tk.NSEW, pady=10)
    tk.Label(user_frame, text="Zeile:", justify=tk.LEFT, anchor=tk.W).grid(column=0, row=0, sticky=tk.NSEW, padx=2)
    tk.Entry(user_frame, textvariable=user_input, width=25).grid(column=0, row=1, sticky=tk.NSEW, padx=2, pady=5)
    tk.Button(user_frame, text="Losrömern", command=get_user_input).grid(column=0, row=2)


def set_table(values):

    for val_idx in range(len(values)):
        lb_cell = tk.Label(label_frame,
                           text=val_idx + 1,
                           width=3
                           )
        lb_cell.grid(column=0, row=val_idx, sticky=tk.NSEW)
        number_lst.append(lb_cell)

    for i, cell_list in enumerate(values):
        cell_lst = []
        for j, cell_text in enumerate(cell_list):
            lb_cell = tk.Label(label_frame,
                               text=cell_text,
                               relief=tk.GROOVE,
                               bg="white",
                               fg="black",
                               padx=10,
                               pady=5,
                               )
            lb_cell.grid(column=j+1, row=i, sticky=tk.NSEW)
            cell_lst.append(lb_cell)
        label_lst.append(cell_lst)

    canvas.create_window((0, 0), window=label_frame, anchor=tk.NW)
    label_frame.update_idletasks()
    bbox = canvas.bbox(tk.ALL)
    canvas.configure(scrollregion=bbox,
                     width=preview_frame.winfo_width(),
                     height=preview_frame.winfo_height(),
                     xscrollcommand=hsbar.set,
                     yscrollcommand=vsbar.set
                     )

    def _bound_to_mousewheel(_):
        canvas.bind_all("<MouseWheel>", _on_mousewheel)

    def _unbound_to_mousewheel(_):
        canvas.unbind_all("<MouseWheel>")

    def _on_mousewheel(event):
        canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")

    canvas.bind('<Enter>', _bound_to_mousewheel)
    canvas.bind('<Leave>', _unbound_to_mousewheel)


def get_user_input():
    if user_input.get().isdigit():
        # 'target_label' symbolisiert die Zeilenhöhe (durchnummeriert ganz links in der Tabelle).
        # Zu diesem Label soll immer hingescrollt werden.
        target_label = number_lst[int(user_input.get())-1]
        print("Zeile:", int(user_input.get()), "Name Ziel-Label:", target_label)

        # y-Koordinate
        label_y = canvas.canvasy(number_lst[int(user_input.get())-1].winfo_y())  # WAHRSCHEINLICH FALSCH ? :(
        print(label_y)

        # Höhe des Canvas
        x1, y1, x2, y2 = canvas.bbox(tk.ALL)
        canvas_height = y2-y1
        print(canvas_height)

        # y-Koordinate geteilt durch Höhe des Canvas
        scroll_value = float(label_y / canvas_height)
        canvas.yview_moveto(scroll_value)

    else:
        print("Bitte 'ne ganze Zahl")
    print('---')


main()
set_table(val_list)

root.mainloop()
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das was ich beschrieben habe funktioniert natuerlich nur, wenn man Items des *canvas* benutz. Nicht beliebige andere Widgets, die eignetlich mit dem Canvas ueberhaupt nichts zu tun haben. Wieso ist da der Canvas drin? Warum benutzt du keine ListBox? Oder wenn es denn ein Canvas sein soll, dann eben dessen Text-Items?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe gerade mal ein bisschen gespielt, und so schnell auch noch keine Antwort gefunden. Aber eine Sache die klar scheint: der Canvas resized sich ja nicht automatisch nur weil seine Kinder ueber ihn hinausgehen. Das wird auch erklaeren, warum das alles nicht zueinander passt.

Ich schaue mal, ob ich da ein schluessigeres Beispiel gebaut bekomme. Aber nicht den Atem anhalten, es gibt noch viel anderes zu tun.
DMD-OS
User
Beiträge: 165
Registriert: Freitag 28. Dezember 2018, 13:52

ich versuche dann erst einmal ein canvas text zu nehmen...
vielen dank dir.
DMD-OS
User
Beiträge: 165
Registriert: Freitag 28. Dezember 2018, 13:52

Ich hab jetzt was vorbereitet, das funktionieren könnte.

Code: Alles auswählen

import tkinter as tk

val_list = [['Spalte_1', 'Spalte_2', 'Spalte_3', 'Spalte_4'],
            ['a', 'b', 'c', 'd'],
            ['e', 'f', 'g', 'h'],
            ['i', 'j', 'k', 'l'],
            ['m', 'n', 'o', 'p'],
            ['q', 'r', 's', 't'],
            ['u', 'v', 'w', 'x'],
            ['y', 'z', '', ''],
            ['aa', 'bb', 'cc', 'dd'],
            ['ee', 'ff', 'gg', 'hh'],
            ['ii', 'jj', 'kk', 'll'],
            ['mm', 'nn', 'oo', 'pp'],
            ['qq', 'rr', 'ss', 'tt'],
            ['uu', 'vv', 'ww', 'xx'],
            ['yy', 'zz', '', '']
            ]

root = tk.Tk()
root.geometry("250x200+500+200")

info_frame = tk.Frame(root)
info_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=False, padx=5)

preview_frame = tk.Frame(root)
preview_frame.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True, padx=5)
preview_frame.columnconfigure(0, weight=1)
preview_frame.rowconfigure(0, weight=1)

canvas = tk.Canvas(preview_frame, bg="lightgrey", bd=1, highlightthickness=1)
canvas.grid(row=0, column=0, sticky=tk.NSEW, padx=1, pady=1)
vsbar = tk.Scrollbar(preview_frame, orient=tk.VERTICAL, command=canvas.yview)
vsbar.grid(row=0, column=1, sticky=tk.NS)
hsbar = tk.Scrollbar(preview_frame, orient=tk.HORIZONTAL, command=canvas.xview)
hsbar.grid(row=1, column=0, sticky=tk.EW)


def main(values):
    for widget_idx, widget_text in enumerate(values):
        widget_name = str(widget_idx)
        # print(widget_name)
        canvas.create_text(10, int(widget_name)*20, text="[{0}] - {1}".format(widget_name, widget_text), anchor="w", tags=widget_name)
    bbox = canvas.bbox(tk.ALL)
    canvas.configure(scrollregion=bbox,
                     xscrollcommand=hsbar.set,
                     yscrollcommand=vsbar.set
                     )

    # Hier gibt der User eine Zeile ein, die er sich zur Anzeige aussucht.
    # Ich mach dann nur 'ne Zahl draus und setzt die Scrollbar da hin.
    widget_x, widget_y = canvas.coords(['1'])  							# <-- HIER SOLLTE canvas.coords(['0'] DOCH AUCH ANSPRECHBAR SEIN!
    print(widget_x, widget_y)

    '''
    canvas_x1, canvas_y1, canvas_x2, canvas_y2 = canvas.bbox(tk.ALL)
    canvas_height = canvas_y2-canvas_y1
    scroll_value = float(widget_y/canvas_height)
    print(zeile, widget_y, canvas_height, scroll_value)
    canvas.yview_moveto(scroll_value)
    '''

    def _bound_to_mousewheel(_):
        canvas.bind_all("<MouseWheel>", _on_mousewheel)

    def _unbound_to_mousewheel(_):
        canvas.unbind_all("<MouseWheel>")

    def _on_mousewheel(event):
        canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")

    canvas.bind('<Enter>', _bound_to_mousewheel)
    canvas.bind('<Leave>', _unbound_to_mousewheel)


main(val_list)
root.mainloop()
... aber wieso kann ich nicht mit:
widget_x, widget_y = canvas.coords(['0'])
die Zeile 0 ansprechen???
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Coords bekommt nicht irgendwas, sondern eine item-id des Text-Elementes das du meinst. Der canvas weiss nicht, das fuer dich eine Liste mit 0 drin das oberste element ist. So ein Konzept kennt der nicht.
DMD-OS
User
Beiträge: 165
Registriert: Freitag 28. Dezember 2018, 13:52

das ist mir ja auch klar...
ich nenne den ersten aber doch 0, kann den aber nicht ansprechen, sondern spreche erst mit 1 den 0-ten an!?
DMD-OS
User
Beiträge: 165
Registriert: Freitag 28. Dezember 2018, 13:52

So, bin jetzt soweit:

Code: Alles auswählen

import tkinter as tk


val_list = [['Spalte_1',  'Spalte_2',  'Spalte_3', 'Spalte_4'],
            ['a', 'b', 'c', 'd'],
            ['e', 'f', 'g', 'h'],
            ['i', 'j', 'k', 'l'],
            ['m', 'n', 'o', 'p'],
            ['q', 'r', 's', 't'],
            ['u', 'v', 'w', 'x'],
            ['y', 'z', '', ''],
            ['aa', 'bb', 'cc', 'dd'],
            ['ee', 'ff', 'gg', 'hh'],
            ['ii', 'jj', 'kk', 'll'],
            ['mm', 'nn', 'oo', 'pp'],
            ['qq', 'rr', 'ss', 'tt'],
            ['uu', 'vv', 'ww', 'xx'],
            ['yy', 'zz', '', '']
            ]

root = tk.Tk()
root.geometry("400x400+500+200")

user_input = tk.StringVar()

info_frame = tk.Frame(root)
info_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=False, padx=5)

preview_frame = tk.Frame(root)
preview_frame.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True, padx=5)
preview_frame.columnconfigure(0, weight=1)
preview_frame.rowconfigure(0, weight=1)

canvas = tk.Canvas(preview_frame, bg="lightgrey", bd=1, highlightthickness=1)
canvas.grid(row=0, column=0, sticky=tk.NSEW, padx=1, pady=1)
vsbar = tk.Scrollbar(preview_frame, orient=tk.VERTICAL, command=canvas.yview)
vsbar.grid(row=0, column=1, sticky=tk.NS)
hsbar = tk.Scrollbar(preview_frame, orient=tk.HORIZONTAL, command=canvas.xview)
hsbar.grid(row=1, column=0, sticky=tk.EW)
label_frame = tk.Frame(canvas, bg="lightgrey")


def main(values):
    user_frame = tk.Frame(info_frame)
    user_frame.grid(column=0, row=2, sticky=tk.NSEW, pady=10)
    tk.Label(user_frame, text="Zeile:", justify=tk.LEFT, anchor=tk.W).grid(column=0, row=0, sticky=tk.NSEW, padx=2)
    tk.Entry(user_frame, textvariable=user_input, width=25).grid(column=0, row=1, sticky=tk.NSEW, padx=2, pady=5)
    tk.Button(user_frame, text="Losrömern", command=get_user_input).grid(column=0, row=2)

    for widget_idx, widget_text in enumerate(values):
        widget_name = str(widget_idx)
        canvas.create_text(0, int(widget_name)*28, text="[{0}]".format(widget_idx+1, widget_text), anchor="w", tags=widget_name)

    for i, cell_list in enumerate(values):
        for j, cell_text in enumerate(cell_list):
            lb_cell = tk.Label(label_frame,
                               text=cell_text,
                               relief=tk.GROOVE,
                               bg="white",
                               fg="black",
                               padx=10,
                               pady=4,
                               )
            lb_cell.grid(column=j+1, row=i, sticky=tk.NSEW)

    canvas.create_window((25, -10), window=label_frame, anchor=tk.NW)

    bbox = canvas.bbox(tk.ALL)
    canvas.configure(scrollregion=bbox,
                     xscrollcommand=hsbar.set,
                     yscrollcommand=vsbar.set
                     )

    def _bound_to_mousewheel(_):
        canvas.bind_all("<MouseWheel>", _on_mousewheel)

    def _unbound_to_mousewheel(_):
        canvas.unbind_all("<MouseWheel>")

    def _on_mousewheel(event):
        canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")

    canvas.bind('<Enter>', _bound_to_mousewheel)
    canvas.bind('<Leave>', _unbound_to_mousewheel)


def get_user_input():
    if user_input.get().isdigit():
        widget_x, widget_y = canvas.coords([user_input.get()])
        canvas_x1, canvas_y1, canvas_x2, canvas_y2 = canvas.bbox(tk.ALL)
        canvas_x, canvas_y2 = canvas_x2-canvas_x1, canvas_y2-canvas_y1
        scroll_value = widget_y/canvas_y2
        canvas.yview_moveto(scroll_value)

    else:
        print("Bitte 'ne ganze Zahl")


main(val_list)

root.mainloop()
ich bräuchte aber noch eine gute idee, wie ich jetzt die Positionen (d.h. x und y) überall anpassen kann.
das ist noch irgendwie alles schief.
und die horizontale scrollbar funktioniert auch nicht mehr!
hat da jmd (__deets__ vielleicht :)) ne gute idee?
DMD-OS
User
Beiträge: 165
Registriert: Freitag 28. Dezember 2018, 13:52

SSSSSOOOOOOOLLLLLLLLLEEEEEEDDDDD :):)

Code: Alles auswählen

def get_user_input():
    if user_input.get().isdigit():
        label = label_lst[int(user_input.get())-2]
        widget_x, widget_y = label.winfo_x(), label.winfo_y()
        canvas_x1, canvas_y1, canvas_x2, canvas_y2 = canvas.bbox(tk.ALL)
        canvas_x, canvas_y2 = canvas_x2-canvas_x1, canvas_y2-canvas_y1
        scroll_value = widget_y / canvas_y2
        canvas.yview_moveto(scroll_value)
    else:
        print("Bitte 'ne ganze Zahl")
Benutzeravatar
__blackjack__
User
Beiträge: 14087
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Code: Alles auswählen

In [5]: re.sub(r"(.)\1*", r"\1", "SSSSSOOOOOOOLLLLLLLLLEEEEEEDDDDD")            
Out[5]: 'SOLED'
Ich glaube da fehlt ein "V". 🤔
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das du den ersten so *nennst* hat nichts damit zu tun, welche ID der bekommt - die wird dir zurueckgegeben, bei canvas.create_text - und den Rueckgabewert laesst du unter den Tisch fallen.
Antworten