gpt4 book ai didi

python - Tkinter Treeview 如何用鼠标正确选择多个项目

转载 作者:行者123 更新时间:2023-12-03 14:29:57 26 4
gpt4 key购买 nike

我正在尝试使用鼠标来选择和取消选择多个项目。我有它的工作,但是当用户快速移动鼠标时会出现问题。当鼠标快速移动时,一些项目会被跳过并且根本没有被选中。我一定是走错了路。
更新 1:
我决定使用我自己的选择系统,但我得到了与上面相同的结果。当鼠标快速移动时,某些项目会被跳过,因此它们没有添加正确的颜色标签并保持不变。如果鼠标缓慢移动,所有项目都会被正确选择。下面是新代码和问题的另一个图像。
更新 2:我已经解决了这个问题并发布了工作代码只是为了完整性和将来帮助其他人。我最终使用了我自己的选择系统而不是内置的选择系统。
Sample Image

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()
第二次尝试:
Sample Image 2
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 中测试和工作
更新:我已经更新了代码,一切都在 Windows 和 Linux 中工作,尽管在 Windows 中,蓝色透明窗口在从右到左调整大小时会抖动,从左到右很好。在 Linux 中,不知道为什么会发生抖动?如果有人能让我知道下面的代码是否适用于 MacOS,那也很棒。

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()

关于python - Tkinter Treeview 如何用鼠标正确选择多个项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61350804/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com