Seite 1 von 1

Aktuelle Scrollposition

Verfasst: Donnerstag 2. Januar 2020, 10:51
von DMD-OS
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?

Re: Aktuelle Scrollposition

Verfasst: Donnerstag 2. Januar 2020, 11:50
von __deets__
y-Koordinate geteilt durch Höhe des Canvas. Beschrieben in der Dokumentation. https://effbot.org/tkinterbook/canvas.h ... te-systems

Re: Aktuelle Scrollposition

Verfasst: Donnerstag 2. Januar 2020, 13:12
von DMD-OS
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

Re: Aktuelle Scrollposition

Verfasst: Donnerstag 2. Januar 2020, 13:18
von __deets__
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.

Re: Aktuelle Scrollposition

Verfasst: Donnerstag 2. Januar 2020, 14:04
von DMD-OS
Kannst du mir helfen, wie ich die rausbekomme :)

Re: Aktuelle Scrollposition

Verfasst: Donnerstag 2. Januar 2020, 14:23
von __deets__
Bitte den Link den ich gepostet habe gut durchlesen. Da steht es doch drin. canvas.bbox(ALL) gibt dir die Dimensionen des Canvas.

Re: Aktuelle Scrollposition

Verfasst: Donnerstag 2. Januar 2020, 15:35
von DMD-OS
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 :(

Re: Aktuelle Scrollposition

Verfasst: Donnerstag 2. Januar 2020, 15:40
von __deets__
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

Re: Aktuelle Scrollposition - Neuauflage

Verfasst: Freitag 3. Januar 2020, 12:45
von DMD-OS
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()

Re: Aktuelle Scrollposition

Verfasst: Freitag 3. Januar 2020, 13:02
von __deets__
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?

Re: Aktuelle Scrollposition

Verfasst: Freitag 3. Januar 2020, 15:13
von __deets__
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.

Re: Aktuelle Scrollposition

Verfasst: Montag 6. Januar 2020, 09:36
von DMD-OS
ich versuche dann erst einmal ein canvas text zu nehmen...
vielen dank dir.

Re: Aktuelle Scrollposition

Verfasst: Montag 6. Januar 2020, 16:22
von DMD-OS
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???

Re: Aktuelle Scrollposition

Verfasst: Montag 6. Januar 2020, 16:43
von __deets__
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.

Re: Aktuelle Scrollposition

Verfasst: Dienstag 7. Januar 2020, 08:11
von DMD-OS
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!?

Re: Aktuelle Scrollposition

Verfasst: Dienstag 7. Januar 2020, 10:30
von DMD-OS
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?

Re: Aktuelle Scrollposition

Verfasst: Mittwoch 8. Januar 2020, 16:12
von DMD-OS
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")

Re: Aktuelle Scrollposition

Verfasst: Mittwoch 8. Januar 2020, 16:18
von __blackjack__

Code: Alles auswählen

In [5]: re.sub(r"(.)\1*", r"\1", "SSSSSOOOOOOOLLLLLLLLLEEEEEEDDDDD")            
Out[5]: 'SOLED'
Ich glaube da fehlt ein "V". 🤔

Re: Aktuelle Scrollposition

Verfasst: Mittwoch 8. Januar 2020, 17:12
von __deets__
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.