Tkinter 滚动树状视图时如何移动弹出的ttk.Combobox?
这个问题是对这个问题的延伸 当用户双击树视图时,我创建组合框作为弹出窗口。当我在树状视图上滚动鼠标时,带有箭头的输入框可以正常移动。但是,关联的下拉列表框不会移动。我可以看到ttk组合框实际上是一个条目和一个listbox/PopdownWindow的组合。但是我找不到关于如何访问列表框部分并移动它的适当文档Tkinter 滚动树状视图时如何移动弹出的ttk.Combobox?,tkinter,combobox,popup,treeview,scrollbar,Tkinter,Combobox,Popup,Treeview,Scrollbar,这个问题是对这个问题的延伸 当用户双击树视图时,我创建组合框作为弹出窗口。当我在树状视图上滚动鼠标时,带有箭头的输入框可以正常移动。但是,关联的下拉列表框不会移动。我可以看到ttk组合框实际上是一个条目和一个listbox/PopdownWindow的组合。但是我找不到关于如何访问列表框部分并移动它的适当文档 import tkinter as tk from tkinter import ttk class ComboPopup(ttk.Combobox): def __init__
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作为tk导入
从tkinter导入ttk
类ComboPopup(ttk.Combobox):
定义初始值(自身、父项、项ID、列,**kw):
超级()
self.tv=家长
self.iId=itemId
self.column=col
选项=[“选项1”、“选项2”、“选项3”]
v=tk.StringVar()
self.config(state=“readonly”,textvariable=v,values=choices,width=9)
self.focus_force()
现有选项=1
自身电流(现有选择)
#self.set(self.choices[existingChoice])
self.bind(“,self.onReturn)
self.bind(“,self.onEscape)
#self.bind(“,self.onFocusOut)
def saveCombo(自我):
self.tv.set(self.iId,column=self.column,value=self.get())
打印(“EntryPopup::saveEdit--{}”.format(self.iId))
def onEscape(自我、事件):
打印(“ComboPopup::onEscape”)
#把焦点放回树视图。
self.tv.focus_set()
自我毁灭
def onReturn(自身、事件):
self.tv.focus_set()
self.saveCombo()
自我毁灭
类EditableDataTable(tk.Frame):
定义初始化(自身,父级):
tk.Frame.\uuuu init\uuuuu(自,父)
self.parent=parent
self.tree=None
self.comboPopup=None
self.curseelectedrowid=“”
列=(“Col1”、“Col2”)
#使用垂直滚动条创建树状视图。
self.tree=ttk.Treeview(self,columns=columns,show=“headers”)
self.tree.grid(列=0,行=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(列=1,行=0,sticky='ns')
self.grid\u column配置(0,权重=1)
self.grid_rowconfigure(0,权重=1)
col1=[]
col2=[]
对于范围(50)内的r:
col1.append(“数据1-{}”.format(r))
col2.append(“数据2-{}”.format(r))
对于范围内的i(最小值(len(col1),len(col2)):
self.tree.insert(“”,i,value=(col1[i],col2[i]))
self.tree.bind(“”,self.onDoubleClick)
self.tree.bind(“,self.onmouseheel)
鼠标滚轮上的def(自身、事件):
popupWindow=无
#TODO:修复combobox(关联列表框)的滚动
如果self.comboPopup!=无:
如果ttk.Combobox.winfo_存在(self.Combobox弹出窗口):
popupWindow=self.comboPopup
pd=popupWindow.tk.call('ttk::combobox::PopupDownWindow',popupWindow)
lb=popupWindow.tk.eval('return{}.f.l'.format(pd))
打印(“pw:{}”.format(popupWindow))
打印(“pd:{}”。格式(pd))
打印(“lb:{}”。格式(lb))
如果弹出窗口!=无:
def_move():
尝试:
iid=popupWindow.iid
x、 y,width,height=self.tree.bbox(iid,column=“#2”)#硬编码为col2
popupWindow.place(x=x,y=y+高度//2,锚点=w',宽度=宽度)
除值错误外:
poppuphindow.place_忘记()
除了tk.TclError:
通过
在(5,_移动)之后弹出窗口
如果ttk.Combobox.winfo_存在(self.Combobox弹出窗口):
#pd.在(5)之后,移动不起作用
#lb.after(5,_move)#不起作用
通过
def createPopup(自身、行、列):
x、 y,宽度,高度=self.tree.bbox(行,列)
#y轴偏移
帕迪=高度//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)
定义双击(自身、事件):
rowid=self.tree.identification\u行(event.y)
column=self.tree.identification\u列(event.x)
self.createPopup(rowid,column)
root=tk.tk()
对于范围(2)中的行:
root.grid\u rowconfigure(行,权重=1)
root.grid\u columnconfigure(0,权重=1)
label=tk.label(root,text=“双击编辑并按‘Enter’”)
label.grid(行=0,列=0,sticky='news',padx=10,pady=5)
dataTable=EditableDataTable(根)
网格(行=1,列=0,sticky=“news”,pady=10,padx=10)
根几何(“450x300”)
root.mainloop()
如何访问“列表框”并将其与“条目”一起移动?我在Python3上使用的是tkinter 8.6版。正如我之前在文章中所说的,您需要移动这些小部件,这意味着要计算并跟踪它。我不知道为什么在编辑过程中需要多个活动弹出窗口小部件。我的treeview内联编辑解决方案是一次只允许编辑一个单元格。这样做可以降低支持这种事情所需的代码的复杂性。我在github上发布了一个关于这个的工作演示,我会在这里发布代码,但是演示中有很多代码。Treeview是一个非常难对付的野兽,如果你想把它全部覆盖的话。无论如何,我会发布下面的链接,让它运行一下,它可能适合你的需要。代码目前正在处理中,但可以按原样使用。代码在一个文件中,不需要导入
我不想编辑多个单元格。实际上,ttk.Combobox是条目和列表框的组合。启动组合框弹出窗口时,有两个小部件:文本字段和