【问题标题】:How to move a popup ttk.Combobox when scrolling a treeview?滚动树视图时如何移动弹出窗口ttk.Combobox?
【发布时间】:2020-05-30 02:20:58
【问题描述】:

这个问题是this的扩展。

当用户双击树视图时,我将组合框创建为弹出窗口。当我在树视图上滚动鼠标时,带有箭头的输入框移动正常。但是,关联的下拉列表框不会移动。我可以看到ttk组合框实际上是Entry和listbox/PopdownWindow的组合。但我找不到关于如何访问和移动列表框部分的适当文档。

import tkinter as tk
from tkinter import ttk

class ComboPopup(ttk.Combobox):
    def __init__(self, parent, itemId, col, **kw):
        super().__init__(parent, **kw)
        self.tv = parent
        self.iId = itemId
        self.column = col
        choices = ["option1", "option2", "option3"]
        v = tk.StringVar()
        self.config(state="readonly", textvariable=v, values=choices, width=9)

        self.focus_force()

        existingChoice = 1
        self.current(existingChoice)
        #self.set(self.choices[existingChoice])

        self.bind("<Return>", self.onReturn)
        self.bind("<Escape>", self.onEscape)
        #self.bind("<FocusOut>", self.onFocusOut)        

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

    def onEscape(self, event):
        print("ComboPopup::onEscape")
        # give focus back to the treeview.
        self.tv.focus_set()

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

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

        self.parent = parent
        self.tree = None
        self.comboPopup = None
        self.curSelectedRowId = ""
        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)

        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.tree.bind("<MouseWheel>", self.onMousewheel)

    def onMousewheel(self, event):
        popupWindow = None
        #TODO: Fix the scroll of combobox (the associated listbox)        
        if self.comboPopup != None:
            if ttk.Combobox.winfo_exists(self.comboPopup):
                popupWindow = self.comboPopup

                pd = popupWindow.tk.call('ttk::combobox::PopdownWindow', popupWindow)
                lb = popupWindow.tk.eval('return {}.f.l'.format(pd))
                print("pw: {}".format(popupWindow))
                print("pd: {}".format(pd))
                print("lb: {}".format(lb))

        if popupWindow != None:
            def _move():
                try:
                    iid = popupWindow.iId
                    x, y, width, height = self.tree.bbox(iid, column="#2") #hardcoded to col2
                    popupWindow.place(x=x, y=y+height//2, anchor='w', width=width)
                except ValueError:
                    popupWindow.place_forget()
                except tk.TclError:
                    pass

            popupWindow.after(5, _move)
            if ttk.Combobox.winfo_exists(self.comboPopup):
                #pd.after(5, _move) # does not work
                #lb.after(5, _move) # does not work
                pass

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

        # y-axis offset
        pady = height // 2
        self.comboPopup = ComboPopup(self.tree, row, column)
        self.comboPopup.x = x
        self.comboPopup.y = y+pady
        self.comboPopup.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()

如何访问“列表框”并将其与“条目”一起移动?我在 Python 3 上使用 tkinter 8.6 版。

【问题讨论】:

    标签: tkinter combobox popup treeview scrollbar


    【解决方案1】:

    正如我之前在帖子How to move popup window when scrolling a tkinter treeview? 中所说,您需要移动小部件,这意味着进行数学计算并跟踪它。我不确定为什么在编辑过程中您需要一个以上的活动弹出窗口小部件。我对树视图进行内联编辑的解决方案是一次只允许编辑一个单元格。这样做可以降低支持这种事情所需的代码的复杂性。我在 github 上发布了一个工作演示,我会在这里发布代码,但演示中有很多代码。如果您想涵盖所有内容,Treeview 是一个非常难以处理的野兽。无论如何,我会在下面发布链接,试一试它可能适合您的需求。该代码目前正在处理中,但可以按原样使用。代码在一个文件中,无需导入。

    https://github.com/unodan/TkInter-Treeview-Example-Demo

    【讨论】:

    • 我不想编辑多个单元格。 ttk.Combobox 实际上是 Entry 和 Listbox 的组合。当您启动组合框弹出窗口时,有两个小部件:一个文本字段和一个弹出列表框。当我在树视图鼠标滚轮上移动 Combobox 对象时,只有文本字段移动,弹出框不移动。看来我需要手动访问它并尝试移动它。我在这里找到了最接近的问题,但我无法使用上面提供的示例代码使其工作:stackoverflow.com/questions/48172185/…
    • @Jakaria,运行我在上面提供的树视图演示我在最后一列中有一个组合框。演示中只有第一列和最后一列是可编辑的。我的组合框工作正常。也许你正在以错误的方式解决这个问题。运行该代码,看看是否是您正在寻找的。也可以右键添加项目。
    • 非常感谢演示!这是一个很棒的项目。我实际上正在做类似的事情。但是,当您在树视图上滚动鼠标滚轮时,您的弹出窗口(条目和组合框)不会(还)上下移动。要进行复制,请将主窗口的高度变小,以便出现滚动条,双击第一列或最后一列,出现弹出窗口,将鼠标悬停在树上并滚动鼠标滚轮。弹出窗口不移动,只有树行和滚动条移动。我也希望弹出窗口移动。在我的代码中,弹出 Entry 可以正常工作,但 Combobox 不行。
    • @Jakaria,哦,我现在明白了,我会解决这个问题并告诉你我的解决方案。
    • @Jakaria,正如代码所示,你可以让它工作,它很紧张,我更喜欢在滚动时删除弹出窗口小部件。正如我之前所说,这样做会使代码更容易编写。用户可以随时重新单击他们想要编辑的单元格,所以这没什么大不了的。我再次更新了代码以按照我认为应该的方式工作。试一试,让我知道你的想法。
    猜你喜欢
    • 2020-09-10
    • 2015-12-16
    • 1970-01-01
    • 1970-01-01
    • 2016-04-08
    • 1970-01-01
    • 1970-01-01
    • 2014-10-29
    • 1970-01-01
    相关资源
    最近更新 更多