问题描述
限时送ChatGPT账号..我正在尝试使用鼠标来选择和取消选择多个项目.我有它的工作,但是当用户快速移动鼠标时会出现问题.当鼠标快速移动时,一些项目会被跳过并且根本没有被选中.我一定是走错了路.
I'm trying to use the mouse to select and deselect multiple items. I have it working sort of but there is a problem when the user moves the mouse to fast. When the mouse is moved fast some items are skipped and are not selected at all. I must be going about this the wrong way.
更新 1:我决定使用我自己的选择系统,但我得到了与上面相同的结果.当鼠标快速移动时,某些项目会被跳过,因此它们没有添加正确的颜色标签并保持不变.如果鼠标缓慢移动,所有项目都会被正确选择.下面是新代码和问题的另一个图像.
Update 1: I decided to use my own selecting system, but I get the same results as above. Some items are skipped when the mouse is moved to fast and therefore they don't get the correct color tag added and remain unchanged. If the mouse is moved slowly all items get selected correctly. Below is the new code and another Image of the problem.
更新 2:我已经解决了这个问题并发布了工作代码,只是为了完整性和将来帮助其他人.我最终使用了自己的选择系统,而不是内置的选择系统.
Update 2: I have solved this issue and posted the working code just for completeness and to help others in the future. I ended up using my own selection selecting system instead of the built in one.
import tkinter as tk
import tkinter.ttk as ttk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title('Treeview Demo')
self.geometry('300x650')
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
tv = self.tv = ttk.Treeview(self)
tv.heading('#0', text='Name')
# Populate tree with test data.
for idx in range(0, 4):
tv.insert('', idx, f'!{idx}', text=f'Item {idx+1}', tags='TkTextFont', open=1)
iid = f'!{idx}_!{idx}'
tv.insert(f'!{idx}', '0', iid, text=f'Python {idx+1}', tags='TkTextFont', open=1)
for i in range(0, 5):
tv.insert(iid, f'{i}', f'{iid}_!{i}', text=f'Sub item {i+1}', tags='TkTextFont')
tv.grid(sticky='NSEW')
self.active_item = None
def motion(_):
x, y = tv.winfo_pointerxy()
item = tv.identify('item', x - tv.winfo_rootx(), y - tv.winfo_rooty())
if not item or item == self.active_item:
return
if not self.active_item:
self.active_item = item
tv.selection_toggle(item)
self.active_item = item
def escape(_):
tv.selection_remove(tv.selection())
def button_press(_):
self.bind('<Motion>', motion)
def button_release(_):
self.unbind('<Motion>')
self.active_item = None
self.bind('<Escape>', escape)
self.bind('<Button-1>', button_press)
self.bind('<ButtonRelease-1>', button_release)
def main():
app = App()
app.mainloop()
if __name__ == '__main__':
main()
第二次尝试:
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tkfont
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title('Treeview Demo')
self.geometry('700x650')
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
tv = self.tv = ttk.Treeview(self)
tv.heading('#0', text='Name')
tv.tag_configure('odd', background='#aaaaaa')
tv.tag_configure('even', background='#ffffff')
tv.tag_configure('selected_odd', background='#25a625')
tv.tag_configure('selected_even', background='#b0eab2')
tag = 'odd'
# Populate tree with test data.
for idx in range(0, 4):
tag = 'even' if tag == 'odd' else 'odd'
tv.insert('', idx, f'!{idx}', text=f'Item {idx+1}', open=1, tags=(tag,))
tag = 'even' if tag == 'odd' else 'odd'
iid = f'!{idx}_!{idx}'
tv.insert(f'!{idx}', '0', iid, text=f'Python {idx+1}', open=1, tags=(tag,))
for i in range(0, 5):
tag = 'even' if tag == 'odd' else 'odd'
tv.insert(iid, i, f'{iid}_!{i}', text=f'Sub item {i+1}', tags=(tag,))
tv.config(selectmode="none")
tv.grid(sticky='NSEW')
dw = tk.Toplevel()
dw.overrideredirect(True)
dw.wait_visibility(self)
dw.wm_attributes('-alpha', 0.2)
dw.wm_attributes("-topmost", True)
dw.config(bg='#00aaff')
dw.withdraw()
self.selected = False
self.active_item = None
def motion(event):
x, y = self.winfo_pointerxy()
width = event.x-self.anchor_x
height = event.y-self.anchor_y
if width < 0:
coord_x = event.x+self.winfo_rootx()
width = self.anchor_x - event.x
else:
coord_x = self.anchor_x+self.winfo_rootx()
if coord_x+width > self.winfo_rootx()+self.winfo_width():
width -= (coord_x+width)-(self.winfo_rootx()+self.winfo_width())
elif x < self.winfo_rootx():
width -= (self.winfo_rootx() - x)
coord_x = self.winfo_rootx()
if height < 0:
coord_y = event.y+self.winfo_rooty()
height = self.anchor_y - event.y
else:
coord_y = self.anchor_y+self.winfo_rooty()
if coord_y+height > self.winfo_rooty()+self.winfo_height():
height -= (coord_y+height)-(self.winfo_rooty()+self.winfo_height())
elif y < self.winfo_rooty():
height -= (self.winfo_rooty() - y)
coord_y = self.winfo_rooty()
dw.geometry(f'{width}x{height}+{coord_x}+{coord_y}')
item = tv.identify('item', coord_x, coord_y-40)
if not item or item == self.active_item:
self.active_item = None
return
self.active_item = item
tags = list(tv.item(item, 'tags'))
if 'odd' in tags:
tags.pop(tags.index('odd'))
tags.append('selected_odd')
if 'even' in tags:
tags.pop(tags.index('even'))
tags.append('selected_even')
tv .item(item, tags=tags)
def escape(_=None):
for item in tv.tag_has('selected_odd'):
tags = list(tv.item(item, 'tags'))
tags.pop(tags.index('selected_odd'))
tags.append('odd')
tv.item(item, tags=tags)
for item in tv.tag_has('selected_even'):
tags = list(tv.item(item, 'tags'))
tags.pop(tags.index('selected_even'))
tags.append('even')
tv.item(item, tags=tags)
def button_press(event):
if self.selected and not event.state & 1 << 2:
escape()
self.selected = False
dw.deiconify()
self.anchor_item = tv.identify('item', event.x, event.y-40)
self.anchor_x, self.anchor_y = event.x, event.y
self.bind('<Motion>', motion)
self.selected = True
def button_release(event):
dw.withdraw()
dw.geometry('0x0+0+0')
self.unbind('<Motion>')
self.bind('<Escape>', escape)
self.bind('<Button-1>', button_press)
self.bind('<ButtonRelease-1>', button_release)
def main():
app = App()
app.mainloop()
if __name__ == '__main__':
main()
推荐答案
工作示例:
在 Windows 和 Linux 中测试和工作
Tested and works in Windows and Linux
更新:我已经更新了代码,在 Windows 和 Linux 中一切正常,尽管在 Windows 中蓝色透明窗口在从右到左调整大小时会抖动,从左到右很好.在 Linux 中,不知道为什么会发生抖动?如果有人可以让我知道下面的代码是否在 MacOS 中也能运行,那也很棒.
UPDATE: I have updated the code and everything works in Windows and Linux, although in Windows the blue transparent window is jittery when sizing from right to left, left to right is fine. In Linux the jitters don't happen anyone know why? If someone could let me know if the code below works in MacOS that would be great also.
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tkfont
class Treeview(ttk.Treeview):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self.root = parent.winfo_toplevel()
self.selected_items = []
self.origin_x = \
self.origin_y = \
self.active_item = \
self.origin_item = None
sw = self.select_window = tk.Toplevel(self.root)
sw.wait_visibility(self.root)
sw.withdraw()
sw.config(bg='#00aaff')
sw.overrideredirect(True)
sw.wm_attributes('-alpha', 0.3)
sw.wm_attributes("-topmost", True)
self.font = tkfont.nametofont('TkTextFont')
self.style = parent.style
self.linespace = self.font.metrics('linespace') + 5
self.bind('<Escape>', self.tags_reset)
self.bind('<Button-1>', self.button_press)
self.bind('<ButtonRelease-1>', self.button_release)
def fixed_map(self, option):
return [elm for elm in self.style.map("Treeview", query_opt=option) if elm[:2] != ("!disabled", "!selected")]
def tag_add(self, tags, item):
self.tags_update('add', tags, item)
def tag_remove(self, tags, item=None):
self.tags_update('remove', tags, item)
def tag_replace(self, old, new, item=None):
for item in (item,) if item else self.tag_has(old):
self.tags_update('add', new, item)
self.tags_update('remove', old, item)
def tags_reset(self, _=None):
self.tag_remove(('selected', '_selected'))
self.tag_replace('selected_odd', 'odd')
self.tag_replace('selected_even', 'even')
def tags_update(self, opt, tags, item):
def get_items(node):
items.append(node)
for node in self.get_children(node):
get_items(node)
if not tags:
return
elif isinstance(tags, str):
tags = (tags,)
if not item:
items = []
for child in self.get_children():
get_items(child)
else:
items = (item,)
for item in items:
_tags = list(self.item(item, 'tags'))
for _tag in tags:
if opt == 'add':
if _tag not in _tags:
_tags.append(_tag)
elif opt == 'remove':
if _tag in _tags:
_tags.pop(_tags.index(_tag))
self.item(item, tags=_tags)
def button_press(self, event):
self.origin_x, self.origin_y = event.x, event.y
item = self.origin_item = self.active_item = self.identify('item', event.x, event.y)
sw = self.select_window
sw.geometry('0x0+0+0')
sw.deiconify()
self.bind('<Motion>', self.set_selected)
if not item:
if not event.state & 1 << 2:
self.tags_reset()
return
if event.state & 1 << 2:
if self.tag_has('odd', item):
self.tag_add('selected', item)
self.tag_replace('odd', 'selected_odd', item)
elif self.tag_has('even', item):
self.tag_add('selected', item)
self.tag_replace('even', 'selected_even', item)
elif self.tag_has('selected_odd', item):
self.tag_replace('selected_odd', 'odd', item)
elif self.tag_has('selected_even', item):
self.tag_replace('selected_even', 'even', item)
else:
self.tags_reset()
self.tag_add('selected', item)
if self.tag_has('odd', item):
self.tag_replace('odd', 'selected_odd', item)
elif self.tag_has('even', item):
self.tag_replace('even', 'selected_even', item)
def button_release(self, _):
self.select_window.withdraw()
self.unbind('<Motion>')
for item in self.selected_items:
if self.tag_has('odd', item) or self.tag_has('even', item):
self.tag_remove(('selected', '_selected'), item)
else:
self.tag_replace('_selected', 'selected', item)
def get_selected(self):
return sorted(self.tag_has('selected_odd') + self.tag_has('selected_even'))
def set_selected(self, event):
def selected_items():
items = []
window_y = int(self.root.geometry().rsplit('+', 1)[-1])
titlebar_height = self.root.winfo_rooty() - window_y
sw = self.select_window
start = sw.winfo_rooty() - titlebar_height - window_y
end = start + sw.winfo_height()
while start < end:
start += 1
node = self.identify('item', event.x, start)
if not node or node in items:
continue
items.append(node)
return sorted(items)
def set_row_colors():
items = self.selected_items = selected_items()
for item in items:
if self.tag_has('selected', item):
if item == self.origin_item:
continue
if self.tag_has('selected_odd', item):
self.tag_replace('selected_odd', 'odd', item)
elif self.tag_has('selected_even', item):
self.tag_replace('selected_even', 'even', item)
elif self.tag_has('odd', item):
self.tag_replace('odd', 'selected_odd', item)
elif self.tag_has('even', item):
self.tag_replace('even', 'selected_even', item)
self.tag_add('_selected', item)
for item in self.tag_has('_selected'):
if item not in items:
self.tag_remove('_selected', item)
if self.tag_has('odd', item):
self.tag_replace('odd', 'selected_odd', item)
elif self.tag_has('even', item):
self.tag_replace('even', 'selected_even', item)
elif self.tag_has('selected_odd', item):
self.tag_replace('selected_odd', 'odd', item)
elif self.tag_has('selected_even', item):
self.tag_replace('selected_even', 'even', item)
root_x = self.root.winfo_rootx()
if event.x < self.origin_x:
width = self.origin_x - event.x
coord_x = root_x + event.x
else:
width = event.x - self.origin_x
coord_x = root_x + self.origin_x
if coord_x+width > root_x+self.winfo_width():
width -= (coord_x+width)-(root_x+self.winfo_width())
elif self.winfo_pointerx() < root_x:
width -= (root_x - self.winfo_pointerx())
coord_x = root_x
root_y = self.winfo_rooty()
if event.y < self.origin_y:
height = self.origin_y - event.y
coord_y = root_y + event.y
else:
height = event.y - self.origin_y
coord_y = root_y + self.origin_y
if coord_y+height > root_y+self.winfo_height():
height -= (coord_y+height)-(root_y+self.winfo_height())
elif self.winfo_pointery() < root_y + self.linespace:
height -= (root_y - self.winfo_pointery() + self.linespace)
coord_y = root_y + self.linespace
if height < 0:
height = self.winfo_rooty() + self.origin_y
set_row_colors()
self.select_window.geometry(f'{width}x{height}+{coord_x}+{coord_y}')
class App(tk.Tk):
def __init__(self):
super().__init__()
def print_selected_items(_):
print(tv.get_selected())
return 'break'
style = self.style = ttk.Style()
tv = Treeview(self)
style.map("Treeview", foreground=tv.fixed_map("foreground"), background=tv.fixed_map("background"))
tv.heading('#0', text='Name')
tv.tag_configure('odd', background='#ffffff')
tv.tag_configure('even', background='#aaaaaa')
tv.tag_configure('selected_odd', background='#b0eab2')
tv.tag_configure('selected_even', background='#25a625')
color_tag = 'odd'
for idx in range(0, 4):
# Populating the tree with test data.
color_tag = 'even' if color_tag == 'odd' else 'odd'
tv.insert('', idx, f'{idx}', text=f'Item {idx+1}', open=1, tags=(color_tag,))
color_tag = 'even' if color_tag == 'odd' else 'odd'
iid = f'{idx}_{0}'
tv.insert(f'{idx}', '0', iid, text=f'Menu {idx+1}', open=1, tags=(color_tag,))
for i in range(0, 5):
color_tag = 'even' if color_tag == 'odd' else 'odd'
tv.insert(iid, i, f'{iid}_{i}', text=f'Sub item {i+1}', tags=(color_tag,))
color_tag = 'even' if color_tag == 'odd' else 'odd'
tv.insert(iid, 5, f'{iid}_{5}', text=f'Another Menu {idx+1}', open=1, tags=(color_tag,))
iid = f'{iid}_{5}'
for i in range(0, 3):
color_tag = 'even' if color_tag == 'odd' else 'odd'
tv.insert(iid, i, f'{iid}_{i}', text=f'Sub item {i+1}', tags=(color_tag,))
self.title('Treeview Demo')
self.geometry('275x650+3000+250')
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
tv.config(selectmode="none")
tv.grid(sticky='NSEW')
button = ttk.Button(self, text='Get Selected Items')
button.grid()
button.bind('<Button-1>', print_selected_items)
def main():
app = App()
app.mainloop()
if __name__ == '__main__':
main()
这篇关于Tkinter Treeview 如何用鼠标正确选择多个项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
更多推荐
[db:关键词]
发布评论