How to move popup window when scrolling a tkinter treeview?

て烟熏妆下的殇ゞ 提交于 2021-02-19 04:56:14

问题


I have a tkinter treeview with a vertical scrollbar. To make it (look like) editable, I create a popup Entry when the user double-clicks on a cell of the treeview. However, I can't make the popup to move when the treeview is scrolled.

import tkinter  as tk
from tkinter import ttk

class EntryPopup(ttk.Entry):
    def __init__(self, parent, itemId, col, **kw):
        super().__init__(parent, **kw)
        self.tv = parent
        self.iId = itemId
        self.column = col
        self['exportselection'] = False

        self.focus_force()
        self.bind("<Return>", self.onReturn)

    def saveEdit(self):
        self.tv.set(self.iId, column=self.column, value=self.get())
        print("EntryPopup::saveEdit---{}".format(self.iId))

    def onReturn(self, event):
        self.tv.focus_set()        
        self.saveEdit()
        self.destroy()


class EditableDataTable(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        self.parent = parent
        self.tree = None
        self.entryPopup = None
        columns = ("Col1", "Col2")
        # Create a treeview with vertical scrollbar.
        self.tree = ttk.Treeview(self, columns=columns, show="headings")
        self.tree.grid(column=0, row=0, sticky='news')
        self.tree.heading("#1", text="col1")
        self.tree.heading("#2", text="col2")

        self.vsb = ttk.Scrollbar(self, orient="vertical", command=self.tree.yview)
        self.tree.configure(yscrollcommand=self.vsb.set)
        self.vsb.grid(column=1, row=0, sticky='ns')

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

        self.entryPopup = None
        self.curSelectedRowId = ""

        col1 = []
        col2 = []
        for r in range(50):
            col1.append("data 1-{}".format(r))
            col2.append("data 2-{}".format(r))

        for i in range(min(len(col1),len(col2))):
            self.tree.insert('', i, values=(col1[i], col2[i]))

        self.tree.bind('<Double-1>', self.onDoubleClick)

    def createPopup(self, row, column):
        x,y,width,height = self.tree.bbox(row, column)

        # y-axis offset
        pady = height // 2
        self.entryPopup = EntryPopup(self.tree, row, column)
        self.entryPopup.place(x=x, y=y+pady, anchor='w', width=width)


    def onDoubleClick(self, event):
        rowid = self.tree.identify_row(event.y)
        column = self.tree.identify_column(event.x)
        self.createPopup(rowid, column)

root = tk.Tk()

for row in range(2):
    root.grid_rowconfigure(row, weight=1)
root.grid_columnconfigure(0, weight=1)

label = tk.Label(root, text="Double-click to edit and press 'Enter'")
label.grid(row=0, column=0, sticky='news', padx=10, pady=5)

dataTable = EditableDataTable(root)
dataTable.grid(row=1, column=0, sticky="news", pady=10, padx=10)

root.geometry("450x300")
root.mainloop()

To reproduce the problem, double-click on the treeview. While the edit box is open, move your mouse pointer to hover over the treeview. Now scroll using the mouse wheel. The treeview moves but the popup edit box does not.


回答1:


I have done something similar before by binding a function to mousewheel and recalculate all the new x & y location of your hovering widgets.

class EditableDataTable(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        self.parent = parent
        self.tree = None
        self.entryPopup = None
        self.list_of_entries = []

        ...

        self.tree.bind("<MouseWheel>", self._on_mousewheel)

    def _on_mousewheel(self, event):
        if self.list_of_entries:
            def _move():
                for i in self.list_of_entries:
                    try:
                        iid = i.iId
                        x, y, width, height = self.tree.bbox(iid, column="Col2") #hardcoded to col2
                        i.place(x=x, y=y+height//2, anchor='w', width=width)
                    except ValueError:
                        i.place_forget()
                    except tk.TclError:
                        pass
            self.master.after(5, _move)

    def createPopup(self, row, column):
        x,y,width,height = self.tree.bbox(row, column)
        # y-axis offset
        pady = height // 2
        self.entryPopup = EntryPopup(self.tree, row, column)
        self.list_of_entries.append(self.entryPopup)
        self.entryPopup.place(x=x, y=y+pady, anchor='w', width=width)

Note that this only works on the second column, but should be easy enough to implement for the rest.




回答2:


You will need to do the math and move the entry widget when the tree is scrolled. I have edited your code and I programmed the scrollbar buttons only. If you click the button the entry widget will scroll with the tree. I did not program the wheelmouse scrolling or dragging the scrollbar. You should be able to figure out the rest.

import tkinter as tk
import tkinter.font as tkfont
from tkinter import ttk

class EntryPopup(ttk.Entry):
    def __init__(self, parent, itemId, col, **kw):
        super().__init__(parent, **kw)
        self.tv = parent
        self.iId = itemId
        self.column = col
        self['exportselection'] = False

        self.focus_force()
        self.bind("<Return>", self.onReturn)

    def saveEdit(self):
        self.tv.set(self.iId, column=self.column, value=self.get())
        print("EntryPopup::saveEdit---{}".format(self.iId))

    def onReturn(self, event):
        self.tv.focus_set()
        self.saveEdit()
        self.destroy()


class EditableDataTable(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        self.parent = parent
        self.tree = None
        self.entryPopup = None
        columns = ("Col1", "Col2")
        # Create a treeview with vertical scrollbar.
        self.tree = ttk.Treeview(self, columns=columns, show="headings")
        self.tree.grid(column=0, row=0, sticky='news')
        self.tree.heading("#1", text="col1")
        self.tree.heading("#2", text="col2")
        self.vsb = ttk.Scrollbar(self, orient="vertical", command=self.tree.yview)
        self.tree.configure(yscrollcommand=self.vsb.set)
        self.vsb.grid(column=1, row=0, sticky='ns')

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

        self.entryPopup = None
        self.curSelectedRowId = ""

        col1 = []
        col2 = []
        for r in range(50):
            col1.append("data 1-{}".format(r))
            col2.append("data 2-{}".format(r))

        for i in range(min(len(col1),len(col2))):
            self.tree.insert('', i, values=(col1[i], col2[i]))

        self.tree.bind('<Double-1>', self.onDoubleClick)
        self.vsb.bind('<ButtonPress-1>', self.func)

    def func(self, event):
        print(self.vsb.identify(event.x, event.y))
        if hasattr(self.entryPopup, 'y'):
            item = self.vsb.identify(event.x, event.y)

            if item == 'uparrow':
                self.entryPopup.y += 20
            elif item == 'downarrow':
                self.entryPopup.y -= 20

            self.entryPopup.place(x=self.entryPopup.x, y=self.entryPopup.y, )

    def createPopup(self, row, column):
        x, y, width, height = self.tree.bbox(row, column)

        # y-axis offset
        pady = height // 2
        self.entryPopup = EntryPopup(self.tree, row, column)
        self.entryPopup.x = x
        self.entryPopup.y = y+pady
        self.entryPopup.place(x=x, y=y+pady, anchor='w', width=width)


    def onDoubleClick(self, event):
        rowid = self.tree.identify_row(event.y)
        column = self.tree.identify_column(event.x)
        self.createPopup(rowid, column)


root = tk.Tk()

for row in range(2):
    root.grid_rowconfigure(row, weight=1)
root.grid_columnconfigure(0, weight=1)

label = tk.Label(root, text="Double-click to edit and press 'Enter'")
label.grid(row=0, column=0, sticky='news', padx=10, pady=5)

dataTable = EditableDataTable(root)
dataTable.grid(row=1, column=0, sticky="news", pady=10, padx=10)

root.geometry("450x300")
root.mainloop()


来源:https://stackoverflow.com/questions/61975670/how-to-move-popup-window-when-scrolling-a-tkinter-treeview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!