Этот вопрос является расширением this .
Я создаю поля со списком как всплывающие windows, когда пользователь дважды щелкает по дереву. Когда я прокручиваю мышью в древовидной структуре, поле ввода со стрелкой перемещается нормально. Однако связанный раскрывающийся список не перемещается. Я вижу, что поле со списком 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()
Как мне получить доступ к «списку» и переместить его вместе с «записью»? Я использую tkinter версии 8.6 на Python 3.