Координаты виджета на кадре tkinter? Варианты определения позиции прокрутки для установки? - PullRequest
0 голосов
/ 17 января 2019

Моя цель - автоматически прокрутить кадр tkinter как пользователи / в виджеты за пределами видимой области. У меня это работает, используя общее количество строк и строку сфокусированного виджета для вычисления позиции прокрутки, но это работает только для строк одинаковой высоты. (В моей программе непредсказуемое количество строк разной высоты).

Я использую фрейм для поддержки менеджера сетки, так как без него мне пришлось бы использовать абсолютное размещение виджетов в окне. Это было бы ужасно быстро с динамически создаваемыми строками. Недостатком является отсутствие доступа к координатам холста для виджетов (насколько я могу судить).

Пример кода показывает рабочую простую версию с равными высотными строками. Любые предложения о том, как обрабатывать непредсказуемое количество строк разной высоты? Я делаю неправильные предположения или полностью пропускаю другой подход?

(Фактическая область прокрутки программы имеет разделы заголовка, много столбцов и до 200 строк виджетов / ярлыков ввода с текстовыми / пустыми метками-разделителями).

import tkinter as tk

class ScrollFrame(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent, bd = 4)
        self.parent = parent
        self.page_start_row = 0
        self.page_rows = 5

        self.vsb = tk.Scrollbar(self)
        self.canvas = tk.Canvas(self, yscrollcommand = self.vsb.set)
        self.vsb.config(command = self.canvas.yview) 
        self.vsb.grid(row = 0, column = 1, sticky = 'ns')
        self.canvas.grid(row = 0, column = 0, sticky = 'nesw')

        self.grid_rowconfigure(0, weight = 1)
        self.grid_columnconfigure(0, weight = 1)

        self.interior = tk.Frame(self.canvas)
        self.interior.grid_rowconfigure(1, weight = 1)
        self.interior.grid_columnconfigure(1, weight = 1)

        self.canvas.create_window(0, 0, window = 
                                  self.interior, anchor = 'nw')
        self.canvas.config(scrollregion = self.canvas.bbox('all'))

    def moveto(self, event):
        # I'm inflexible and not elegant :(
        if event.widget.row_index < self.page_start_row:
            self.page_start_row = event.widget.row_index
        elif event.widget.row_index > self.page_start_row + self.page_rows:
            self.page_start_row = (event.widget.row_index - self.page_rows)
        self.canvas.yview_moveto(self.page_start_row/self.parent.row_count)

        print(event.y) #Test - I just return "??" (without quotes)


class CustomEntry(tk.Entry):
    def __init__(self, parent_frame, row_index, *args, **kwargs):
        tk.Entry.__init__(self, parent_frame, *args, **kwargs) 
        self.parent_frame = parent_frame
        self.row_index = row_index


class SampleApp(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.geometry('200x200')    
        self.resizable(False, False)
        self.scroll_frame = ScrollFrame(self)
        self.scroll_frame.grid(row = 0, column = 0, sticky = 'nesw')
        self.grid_rowconfigure(0, weight = 1)
        self.grid_columnconfigure(0, weight = 1)

        control_frame = tk.Frame(self)
        add_rows_btn = tk.Button(control_frame, text = 'Add More Rows', 
                                 command = self.add_rows)
        add_rows_btn.grid(row = 0, column = 0)
        control_frame.grid(row = 1, column = 0, sticky = 'esw')
        self.row_count = 0
        self.add_rows()

    def add_rows(self):            
        for row_i in range(self.row_count, self.row_count + 10):
            entry = CustomEntry(self.scroll_frame.interior, row_i)
            entry.grid(row = row_i, column = 0)
            entry.bind('<FocusIn>', 
                       lambda event: self.scroll_frame.moveto(event))
        self.row_count += 10

        self.scroll_frame.interior.update_idletasks() 
        self.scroll_frame.canvas.config(scrollregion = 
                                self.scroll_frame.canvas.bbox('all'))        


if __name__ == '__main__':
    app = SampleApp()
    app.mainloop()

Примечание: высота, установленная в self.geometry('200x200'), соответствует высоте 5 строк в self.page_rows = 5 в OS X, но, возможно, потребуется настроить для других платформ. Если выключен, он будет прокручиваться в неподходящее время. Это еще один пример, когда основание его на строках не так гибко, как использование геометрии / координат.

...