I have been working on my first GUI in tkinter - I am using Windows. My goal right now is to have buttons that accomplish these goals:
The buttons are highlighted when moused over.
The button remains highlighted if clicked.
Only one button can be "selected" (click-highlighted) at a time.
I initially thought that I had accomplished this! But I realize now that my work is not complete.
Here is what I am seeing:
I mouse over button A. It becomes highlighted! (GOOD)
I click on button A. It stays highlighted! (GOOD)
I mouse over button B. It becomes highlighted! (GOOD)
I click on button B. It stays highlighted! The highlight from A is removed! (GOOD)
I mouse over button A. It does not highlight. (BAD)
I am calling the default_coloring class function on button A when I click on button B. However, this appears to turn off the highlighting functions of button A, and the button no longer functions correctly according to the three rules I listed at the top.
How do I ensure that the buttons continue to function normally, even after the command is called? Am I approaching this the wrong way?
import tkinter as tk
blue = '#0000BB'
white = '#FFFFFF'
class HoverButton(tk.Button):
def __init__(self, master, position = None, **kw):
tk.Button.__init__(self,master=master,**kw)
self.defaultBackground = self["background"]
self.defaultForeground = self["foreground"]
self.bind("<Enter>", self.on_enter)
self.bind("<Leave>", self.on_leave)
self.bind("<Button-1>", self.hover_click)
self.state = 0
self.position = position
def on_enter(self, e):
if self.state == 0:
self['background'] = self['activebackground']
self['foreground'] = self['activeforeground']
def on_leave(self, e):
if self.state == 2:
self.state = 0
if self.state == 0:
self['background'] = self.defaultBackground
self['foreground'] = self.defaultForeground
def hover_click(self, e):
self.state += 1
self.state = self.state % 3
if self.state == 2:
self['background'] = self.defaultBackground
self['foreground'] = self.defaultForeground
def default_coloring(self):
self['background'] = self.defaultBackground
self['foreground'] = self.defaultForeground
class AddOnFrame(tk.Frame):
def __init__(self, master):
self.selectedbutton = None
super().__init__(master)
games = ['A','B','C']
self.objs = list()
self['bg'] = blue
for i in range(3):
self.objs.append(HoverButton(self,position = i, text = games[i].upper(), activebackground = white,activeforeground = blue,fg = white, bg = blue, borderwidth=0, relief = 'flat', highlightbackground = white))
self.objs[i]['command'] = lambda c=i: self._hover_button_clicked(self.objs[c])
self.objs[i].grid(row = i, column = 0, sticky = tk.W + tk.E)
self.blanklabel = tk.Label(self, text = '', background = white)
self.blanklabel.grid(row = 0, column = 1,rowspan = 10, sticky = tk.N + tk.E + tk.W + tk.S)
self.grid_columnconfigure(1, weight=1, minsize=10)
self.grid_columnconfigure(2, weight=1, minsize=500)
self.grid_columnconfigure(3, weight=1, minsize=500)
self.grid_columnconfigure(4, weight=1, minsize=500)
self.pack(expand = True)
def _hover_button_clicked(self, HoverButton):
self.lastbutton = self.selectedbutton
if self.lastbutton != None:
self.objs[self.lastbutton].default_coloring()
self.selectedbutton = HoverButton.position
window = tk.Tk()
window.geometry('1750x950')
window['bg'] = blue
window.title('Testing')
lf = AddOnFrame(window)
lf['bg'] = blue
window.mainloop()
I think I found the main source of the problem. When another button is clicked, you restore color of the last clicked button, but you do not reset its state. Change your default_coloring function to:
def default_coloring(self):
self.state = 0
self['background'] = self.defaultBackground
self['foreground'] = self.defaultForeground
But you should also prevent default_coloring if same button is pressed again:
def _hover_button_clicked(self, HoverButton):
self.lastbutton = self.selectedbutton
if (self.lastbutton != None) and (self.lastbutton != HoverButton.position):
self.objs[self.lastbutton].default_coloring()
self.selectedbutton = HoverButton.position
After cursory inspection, this sequence seems to be the problem:
When a button is clicked, the AddOnFrame._hover_button_clicked
method is invoked.
AddOnFrame.selectedbutton is initially None, which means the
if-statement in AddOnFrame._hover_button_clicked will not be
executed the first time. This is why the buttons seem to work the
first time you click them, but not after that.
However, the next time it is invoked (the next time a button is
pressed), AddOnFrame.selectedbutton is not None, and will never
be None again, meaning that from now on, every click will result in
a call to that HoverButton's default_coloring method.
default_coloring is invoked as soon as a button is clicked, which
results in a quick flash from the active color to the default color,
and the button does not stay highlighted.
The quick fix:
Basically, don't do the default_coloring stuff. It seems to be hurting you more than it's helping. Not really sure why you're doing it in the first place (all that stuff with setting the command, the lambda, the whole _hover_button_clicked method) since the buttons seem to be setting their colors back to the default just fine when on_leave or hover_click are invoked. You can fix your problem by changing the body of your HoverButton.default_coloring function to this:
def default_coloring(self):
return
The real fix would be some restructuring of your code.
EDIT I'm offering this to help you simplify things:
import tkinter as tk
colors = {
"white": "#FFFFFF",
"blue": "#0000BB"
}
class HoverButton(tk.Button):
def __init__(self, *args, **kwargs):
tk.Button.__init__(self, *args, **kwargs)
self.is_selected = False
self.is_highlighted = False
self["borderwidth"] = 0
self["relief"] = tk.FLAT
self["font"] = ("United Sans Cd Bk", 30)
self["activeforeground"] = colors["blue"]
self["activebackground"] = colors["white"]
self["highlightbackground"] = colors["white"]
self.recolor()
self.bind("<Enter>", self.on_enter)
self.bind("<Leave>", self.on_leave)
self.bind("<Button-1>", self.on_click)
def recolor(self):
self["background"] = [colors["blue"], colors["white"]][self.is_highlighted]
self["foreground"] = [colors["white"], colors["blue"]][self.is_highlighted]
def on_enter(self, *args):
self.is_highlighted = True
self.recolor()
def on_leave(self, *args):
if self.is_selected:
return
self.is_highlighted = False
self.recolor()
def on_click(self, *args):
self.is_selected = not self.is_selected
class Application(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("Window")
self.geometry("256x256")
self.resizable(width=False, height=False)
self["background"] = colors["blue"]
button_labels = ["A", "B", "C"]
self.buttons = []
for row, button_label in enumerate(button_labels):
button = HoverButton(text=button_label)
button.grid(row=row, column=0, sticky=tk.W)
self.buttons.append(button)
def main():
application = Application()
application.mainloop()
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
Related
In my app I'm trying to have a blinking image. This image should be blinking just five times and then stay still in the frame for five seconds. Right now I've menaged to make the image flash, but I don't know how to make it blink just five times and then stay still.
I've tried using a for loop but it did not solve it. This is my code:
import tkinter as tk
from PIL import ImageTk, Image, ImageGrab
class Blinking(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.first_img = ImageTk.PhotoImage(
Image.open("img_1.png")
)
self.first_img_label = tk.Label(self, image=self.first_img, foreground="black")
self.first_img_label.pack(padx=50, side="left", anchor="w")
self.button = tk.Button(self, text="start", command=self.blink_img)
self.button.pack()
def blink_img(self):
current_color = self.first_img_label.cget("foreground")
background_color = self.first_img_label.cget("background")
blink_clr = "black" if current_color == background_color else background_color
blink_img = "" if current_color == background_color else self.first_img
self.first_img_label.config(image=blink_img, foreground=blink_clr)
self.after_f = self.first_img_label.after(601, self.blink_img)
if __name__ == "__main__":
root = tk.Tk()
root.attributes("-fullscreen", True)
root.attributes("-topmost", True)
Blinking(root).pack(side="top", fill="both", expand=True)
root.mainloop()
How can I achieve this?
Keep track of how many times you've 'blinked', then use after_cancel() to stop when you want.
def __init__(self, parent, *args, **kwargs):
... # code removed for brevity
self.blink_count = 0 # initialize blink counter
def blink_img(self):
current_color = self.first_img_label.cget("foreground")
background_color = self.first_img_label.cget("background")
blink_clr = "black" if current_color == background_color else background_color
blink_img = "" if current_color == background_color else self.first_img
self.first_img_label.config(image=blink_img, foreground=blink_clr)
if self.blink_count < 5:
self.after_f = self.first_img_label.after(601, self.blink_img)
self.blink_count += 1
else:
self.after_cancel(self.after_f)
self.blink_count = 0 # reset counter
how to remove the page1's widgets when the next button is pressed, so that only the page2's widgets is shown.
and vice versa if the back button is pressed on page 2, so the widgets don't overlap
from tkinter import *
class Buttons(Button):
def __init__(self,master,**kwargs):
super().__init__(master=master,**kwargs)
self.look = {"fg":"ghost white","bg":"DarkBlue"}
self.config(self.look)
def makeButton(self,name,texts,wide,rows,cols,com):
self.name = name
self.texts = texts
self.wide = wide
self.rows = rows
self.cols = cols
self.com = com
self.name = Buttons(root,text=self.texts,width=self.wide,command=self.com)
self.name.place(x=self.rows,y=self.cols)
class make(Buttons):
def __init__(self, mainFrame):
super().__init__(mainFrame)
self.main_frame = Frame(mainFrame, width=400, height=300)
self.main_frame.place()
self.page1()
def page1(self):
self.makeButton("name1","Page1-widgets1",15,125,30,None)
self.makeButton("name2","Page1-widgets2",15,125,80,None)
self.makeButton("name3","Next",15,125,130,self.page2)
self.makeButton("name4","Exit",15,125,180,exit)
def page2(self):
self.makeButton("name5","Page2-widgets1",15,135,40,None)
self.makeButton("name6","Page2-widgets2",15,135,90,None)
self.makeButton("name7","Page2-widgets3",15,135,140,None)
self.makeButton("name8","Back",15,135,210,self.page1)
def main():
global root
root = Tk()
root.geometry('400x300+50+50')
script = make(root)
root.mainloop()
if __name__ == '__main__':
main()
was gonna comment but not enough reps, so gonna ask my question here. I copied your script and produced same error too. Firstly its not clear what you want to achieve with script so comments between definitions would help a lot!. When continue button is pressed you want current page to be destroyed and new one to be created right ?
enter code here
def frame_elements_remove(self, elements):
self.elements = elements
for self.element in self.elements:
self.element.destroy()
To destroy button it has to object. When I try to check for type of element in list ;
self.frame_elements = []
with
def mainPage(self):
self.frame_elements_remove(self.frame_elements)
self.makeButton("Button1", "Continue", 10, 10, self.page1)
self.makeButton("Button2", "Exit", 10, 80, quit)
self.controler = 1
self.frame_elements = [self.makeButton]
#just put here print for type check
print(type(self.frame_elements[0]))
it returns;
<class 'method'>
So its seems, its not destroyable because it is not object but a method.
Hope it helps!
I'm working with python 3.8 and tkinter to create a GUI that uses a ttk notebook (tabbed window). I want to have tooltips displayed for the individual tabs but I can't see how to add the tooltip for the tab. I have a class defined that takes an object and text and adds the tooltip and it works for buttons and other objects.
I have example code here:
'''
At no time were Katz, Dawgs, Mice or Squirrels harmed in any way during the development of this application. But...
Well just lets say that skunk next door had it coming!
'''
import tkinter as tk
from tkinter import ttk
class CreateToolTip(object):
"""
create a tooltip for a given widget
"""
def __init__(self, widget, text='widget info'):
self.waittime = 250 #miliseconds
self.wraplength = 180 #pixels
self.widget = widget
self.text = text
self.widget.bind("<Enter>", self.enter)
self.widget.bind("<Leave>", self.leave)
self.widget.bind("<ButtonPress>", self.leave)
self.id = None
self.tw = None
def enter(self, event=None):
self.schedule()
def leave(self, event=None):
self.unschedule()
self.hidetip()
def schedule(self):
self.unschedule()
self.id = self.widget.after(self.waittime, self.showtip)
def unschedule(self):
id = self.id
self.id = None
if id:
self.widget.after_cancel(id)
def showtip(self, event=None):
x = y = 0
x, y, cx, cy = self.widget.bbox("insert")
x += self.widget.winfo_rootx() + 25
y += self.widget.winfo_rooty() + 20
# creates a toplevel window
self.tw = tk.Toplevel(self.widget)
# Leaves only the label and removes the app window
self.tw.wm_overrideredirect(True)
self.tw.wm_geometry("+%d+%d" % (x, y))
label = tk.Label(self.tw, text=self.text, justify='left',
background="cornsilk", relief='solid', borderwidth=1,
wraplength = self.wraplength)
label.pack(ipadx=1)
def hidetip(self):
tw = self.tw
self.tw= None
if tw:
tw.destroy()
class MyCanvas(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tabControl = ttk.Notebook(self)
self.geometry('600x300')
self.title('An Indepth study of the Differences Between Katz and Dawgs!')
self.tab = {}
self.tab['dawg tab'] = ttk.Frame(self.tabControl)
self.tabControl.add(self.tab['dawg tab'], text='dawgs', state='normal')
self.tab['katz tab'] = ttk.Frame(self.tabControl)
self.tabControl.add(self.tab['katz tab'], text='katz', state='normal')
self.tabControl.enable_traversal()
btn1 = tk.Button(self.tab['dawg tab'], text='Squirrel')
btn1.grid(row=1, column=0)
CreateToolTip(btn1, 'Despised by dawgs!!')
btn2 = tk.Button(self.tab['katz tab'], text='Mice')
btn2.grid(row=1, sticky='EW')
CreateToolTip(btn2, 'Katz find them a tasty treat.')
self.tabControl.grid(row=0, column=0, sticky="W")
# testing ...
if __name__ == '__main__':
root = MyCanvas()
root.mainloop()
Any advice or constructive comments would be greatly appreciated. And yes, the tooltip class was totally plagiarized from someone else here on stack overflow.
Cheers!!
So after a few days and lots of help from a friend here is what I have that actually works:
'''
At no time were Katz, Dawgs, Mice or Squirrels harmed in any way during the development of this application. But...
Well lets just say that skunk next door had it coming!
'''
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.scrolledtext as tkst
class MyCanvas(ttk.Notebook):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tooltips = dict()
self.tab_list = dict()
self.current_tab = None
self.waittime = 250 #miliseconds
self.wraplength = 180 #pixels
self.id = None
self.tw = None
self.bind('<Motion>', self.motion)
self.bind('<ButtonPress>', self.leave)
self.line_number = 0
def leave(self, event=None):
self.unschedule()
self.hide_tip()
def schedule(self):
self.unschedule()
self.id = self.after(self.waittime, self.show_tip)
def unschedule(self):
id = self.id
self.id = None
if id:
self.after_cancel(id)
def show_tip(self):
if self.tw:
return
if self.current_tab in self.tooltips.keys():
x = y = 0
x, y, cx, cy = self.bbox('insert')
x += self.winfo_rootx() + 25
y += self.winfo_rooty() - 20
# creates a toplevel window
self.tw = tk.Toplevel(self)
# Leaves only the label and removes the app window
self.tw.wm_overrideredirect(True)
self.tw.wm_geometry(f'+{x}+{y}')
self.tw.wm_attributes('-topmost', True)
label = ttk.Label(self.tw, text=self.tooltips[self.current_tab], background='cornsilk')
label.pack()
def add(self, *args, **kwargs):
if 'tooltip' in kwargs.keys():
tooltip = kwargs['tooltip']
del kwargs['tooltip']
else:
tooltip = None
self.tab_list[kwargs.get('text')] = args[0]
super().add(*args, **kwargs)
if tooltip and ('text' in kwargs.keys()):
self.set_tooltip(kwargs['text'], tooltip)
def set_tooltip(self, tab_text, tooltip):
for idx in range(self.index('end')):
if self.tab(idx, 'text') == tab_text:
self.tooltips[tab_text] = tooltip
return True
return False
def motion(self, event):
if self.identify(event.x, event.y) == 'label':
index = self.index(f'#{event.x},{event.y}')
if self.current_tab != self.tab(index, 'text'):
self.current_tab = self.tab(index, 'text')
self.show_tip()
else:
self.hide_tip()
self.current_tab = None
def hide_tip(self):
tw = self.tw
self.tw = None
if tw:
tw.destroy()
def insert_text_to_tab(self, **kwargs):
string_var = tk.StringVar()
if 'tab' in kwargs.keys():
the_tab = kwargs.get('tab')
string_var.set(kwargs.get('data'))
the_label = tk.Label(self.tab_list[the_tab], textvariable=string_var, background='lightblue')
the_label.pack()
def get_scrolled_text_widget(parent):
st_widget = tkst.ScrolledText(parent)
return st_widget
if __name__ == '__main__':
root = tk.Tk()
root.title('An Indepth study of the Differences Between Katz and Dawgs!')
canvas = MyCanvas(root)
canvas.pack(fill='both', expand=1)
canvas.add(get_scrolled_text_widget(canvas), text='Dawgz', tooltip='A quadraped that doesn\'t look like a kat')
canvas.add(get_scrolled_text_widget(canvas), text='Katz', tooltip='A quadraped that doesn\'t look like a dawg')
canvas.insert_text_to_tab(data='Dawgz hate squirrels!!', tab='Dawgz')
canvas.insert_text_to_tab(data='Katz find mice a tasty treat.', tab='Katz')
root.mainloop()
So it would appear that I was thinking about it a bit wrong and I had only to think of each tabbed entry as an object instead of an attribute (I'm sure the python peanut gallery are going to be all over me for saying that) the solution is quit straight forward.
Cheers!!
I'm adding tooltips to my program, they should appear if mouse is on the widget more than 1 seconds. But there is a problem that tooltip always appears when mouse simply passes over the widget. I mean they should appear if mouse remains on the widget more than 1 seconds but they also appear when mouse doesn't remains on the widget.
So I decided to add some lines to code that delays 1 seconds when mouse enters the widget and checks if mouse is still on the widget. If mouse is still there, then makes tooltip appear. But I don't know how to check if mouse is on a widget. I've tried widget.focus_get() but it simply gives a dot for output. Which is also gives a dot if mouse wasn't there. So this is useless for me. I've done lot's of research but I wasn't able to find something about this.
I am using Python 3.7.9 and tkinter.ttk
I was copied the code from here and modified a little bit to fade-in and fade-out animations. Here is my code:
label = Label(root, text="Label")
class ToolTip(object):
def __init__(self, widget):
self.widget = widget
self.tipwindow = None
self.id = None
self.x = self.y = 0
def showtip(self, text, widget):
global tw
self.text = text
if self.tipwindow or not self.text:
return
x, y, cx, cy = self.widget.bbox("insert")
x = x + self.widget.winfo_rootx() + 20
y = y + cy + self.widget.winfo_rooty() +20
self.tipwindow = tw = Toplevel(self.widget)
tw.wm_overrideredirect(1)
tw.wm_geometry("+%d+%d" % (x, y))
tw.attributes("-alpha", 0)
label = Label(tw, text=self.text, justify=LEFT, relief=SOLID, borderwidth=1)
label.pack(ipadx=1)
def fade_in():
alpha = tw.attributes("-alpha")
if alpha < root.attributes("-alpha"):
alpha += .1
tw.attributes("-alpha", alpha)
tw.after(10, fade_in)
elif alpha == root.attributes("-alpha"):
tw.attributes("-alpha", root.attributes("-alpha"))
try:
tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w, "help", "noActivates")
except TclError:
pass
fade_in()
def hidetip(self, widget):
tw = self.tipwindow
self.tipwindow = None
def fade_away():
alpha = tw.attributes("-alpha")
if alpha > 0:
alpha -= .1
tw.attributes("-alpha", alpha)
tw.after(10, fade_away)
else:
tw.destroy()
fade_away()
def createToolTip(widget, text):
toolTip = ToolTip(widget)
def enter(event):
time.sleep(1000) #Wait 1 second
if widget.focus_get() == True: #Check if mouse is still on the widget. But not working.
toolTip.showtip(text, widget)
def leave(event):
ToolTipActive = False
toolTip.hidetip(widget)
widget.bind('<Enter>', enter)
widget.bind('<Leave>', leave)
createToolTip(label, "This is a tooltip")
I don't know your goal. You can make a tooltip/ hovertip by yourself.
if you want to make it for yourself, you need to use bind when hovering. Also you can use this:
from idlelib.tooltip import Hovertip
mytip = Hovertip(self, "Info")
Edit for binding: (there are different ways)
from tkinter import *
root = Tk()
root.geometry("400x400")
def hover_this_enter(event): # on enter
hover_lbl.place_configure(x=100, y=170)
hover_lbl.after(1000) #after method (you can change)
def hover_this_leave(event): # if you want destroy, make sure you get this item back, or you get attribute errors
hover_lbl.place_configure(x=-200)
btn = Button(root, text="give me tooltips")
btn.pack(side="left")
btn.bind("<Enter>", hover_this_enter)
btn.bind("<Leave>", hover_this_leave)
hover_lbl = Label(root, text="i was hovered", bg="black", fg="white")
if __name__ == '__main__':
root.mainloop()
I have multiple Tkinter.labels in a row and i would like the user to be able to click and drag their mouse over each one, activating them in the process.
I know about bindings, but i need multiple events in a single binding. Ive been messing around with <Button-1> and <Enter>, however i need a callback to be called only when both are true.
I know l.bind('<Button-1>,<Enter>', ...) is not valid.
Anyone with more Tkinter experience know of a way to chain binding, or make a multi-bind??
The way you solve this particular problem is to have a binding on ButtonPress and ButtonRelease that sets a flag. Then, in your binding for <Enter> (or any other event) you check for that flag.
However, while the button is pressed you won't get any <Enter> events. This is because the widget you clicked over grabs the pointer and owns it until you release the button. The only <Enter> events you'll get while the button is pressed are when you enter the widget you originally clicked on.
What you want to do instead is bind to <B1-Motion>. You can then use the x/y coordinates of the event and winfo_containing to determine what widget you are over.
That being said, trying to simulate selection over a row of labels is a lot of work for very little benefit. Why not just use a text widget that already has selection built in? You can tweak it so that it looks like a bunch of labels (ie: make the background the same color as a frame) and you can turn editing off. That might be an easier way to go.
I encountered this same problem today and thanks to #Bryan Oakley's answer I was able to code a working solution. I will share my code in the hope that it will help someone someday.
This example builds 2 tkinter TreeViews, and enables dragging-and-dropping treeItems between the 2 trees. The key point is that by binding both trees to the B1-motion event, both trees are able to respond to the events.
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showinfo
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class TreeItem:
"""
Keeps a reference to a treeItem together with its parent tree.
"""
def __init__(self, tree, item):
self.tree = tree
self.item = item
self.itemTxt = tree.item(item,"text")
def __str__(self):
"""
Prints 'treename, itemname' upon calling str(TreeItem)
"""
return f'{self.tree}, {self.itemTxt}'
class Mouse(metaclass=Singleton):
"""
Handles treeitem clicking, dragging, dropping and shows feedback messages about them.
"""
def __init__(self, root):
self.root = root
self.clicked_item = None
self.is_dragging = False
self.drag_time = 0
self.current_hovering_widget = None
def OnMouseDown(self, event):
clicked_item = self.get_item_under_mouse(event)
print("You clicked on", str(clicked_item))
self.clicked_item = clicked_item
def OnDrag(self, event):
self.is_dragging = True
self.show_drag_init_msg()
self.show_hovering_item_change_msg(event)
self.drag_time += 1
def OnMouseUp(self, event):
if self.is_dragging:
self.finish_drag()
self.show_drop_msg()
self.clicked_item = None
def finish_drag(self):
self.is_dragging = False
self.drag_time = 0
def show_drag_init_msg(self):
if self.drag_time == 0:
print("You are now dragging item", self.clicked_item.tree, self.clicked_item.itemTxt)
def show_hovering_item_change_msg(self, event):
currently_hovering = self.get_item_under_mouse(event)
if str(self.current_hovering_widget) != str(currently_hovering):
print("Mouse is above", str(currently_hovering))
self.current_hovering_widget = currently_hovering
def show_drop_msg(self):
dragged_item:TreeItem = self.clicked_item
dragged_onto:TreeItem = self.current_hovering_widget
print(f'You dropped {str(dragged_item)} onto {str(dragged_onto)}')
def get_item_under_mouse(self, event):
current_tree = self.root.winfo_containing(event.x_root, event.y_root)
current_tree_item = current_tree.identify("item", event.x, event.y)
return TreeItem(tree=current_tree, item=current_tree_item)
class Tree:
def __init__(self, root, row, col):
self.root: tk.Tk = root
self.create_tree(root, row, col)
def OnDrag(self,event):
Mouse(self.root).OnDrag(event)
def OnMouseDown(self, event):
Mouse(self.root).OnMouseDown(event)
def OnMouseUp(self, event):
Mouse(self.root).OnMouseUp(event)
def create_tree(self, root, row, col):
self.tree = ttk.Treeview(root)
self.tree.heading('#0', text='Departments', anchor='w')
self.tree.grid(row=row, column=col, sticky='nsew')
self.add_dummy_data()
# add bindings
self.tree.bind("<ButtonPress-1>", self.OnMouseDown)
self.tree.bind("<ButtonRelease-1>", self.OnMouseUp)
self.tree.bind("<B1-Motion>", self.OnDrag)
def add_dummy_data(self):
# adding data
self.tree.insert('', tk.END, text='Administration', iid=0, open=False)
self.tree.insert('', tk.END, text='Logistics', iid=1, open=False)
self.tree.insert('', tk.END, text='Sales', iid=2, open=False)
self.tree.insert('', tk.END, text='Finance', iid=3, open=False)
self.tree.insert('', tk.END, text='IT', iid=4, open=False)
# adding children of first node
self.tree.insert('', tk.END, text='John Doe', iid=5, open=False)
self.tree.insert('', tk.END, text='Jane Doe', iid=6, open=False)
self.tree.move(5, 0, 0)
self.tree.move(6, 0, 1)
root = tk.Tk()
root.geometry('620x200')
# make two trees
tree1 = Tree(root,0,0)
tree2 = Tree(root,0,1)
# run the app
root.mainloop()