Tooltip dla każdego elementu z Listbox w Tkinter

Tooltip dla każdego elementu z Listbox w Tkinter
TT
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 97
0

Hej chciałbym stworzyć Listbox w tkitner w taki sposób że jak najadę myszką na jakiś element z listy to zostanie wyświetlona jego cała nazwa. Ponizej screen pokazujacy pożądany efekt. Jak mozna to zrobic?

image

ledi12
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
Radosław Głębicki
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Sardine, Italy
  • Postów: 187
1

Do widgetList bind-ować <Enter>, <Motion>, <Leave>. <Enter> i <Leave> aby wiedzieć, że jesteś wewnątrz listboxa. <Motion> aby wyodrębnić y i przekazać do widgetList.nearest(y) i masz indeks nad którym jesteś. Otworzyć małe okienko TopLevel bez ramek (wm_overrideredirect) i sobie coś tam Label wypisać.

Każda zmiana nearest(y) zmieniać pozycję tooltipa, a <leave> zamknąć. Trzeba stosować .after dla otwarcia tooltipa po określonym czasie, a wartość z .after zachować dla przerwania tego niby "wątku". Strasznie to pokręcone, więc swego czasu przesiadłem się na PyQT bo tam jest wbudowane.

Poszukam to może znajdę kod.

Pozdrawiam
Radek Głębicki

Radosław Głębicki
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Sardine, Italy
  • Postów: 187
1

Tak na szybko, ale bez .after.

Kopiuj
import tkinter as tk
from tkinter import Text,Listbox,Label

tkWidget=tk.Tk()
tkWidget.minsize(500,400)
tkWidget.title("List tooltips")
tkWidget.geometry("600x700+100+100")

tkListWidget=Listbox(height=9,width=30)

lListOver=[-1,None,None] #[0] dla indeksu,[1] dla okienka tt,[2] ew. dla obj. .after
lListElms=['aaa','bbb','ccc','ddd']
lListCom =['aaa kom','bbb kom','ccc kom','ddd kom']

for elm in lListElms:
	tkListWidget.insert(tk.END,elm)

def f_coord(*d):
	global lListOver
	xEnrPos,yEnrPos=d[0].x,d[0].y
	idxOver=tkListWidget.nearest(yEnrPos)
	if idxOver!=lListOver[0]:
		sX,sY=str(tkWidget.winfo_pointerx()+10),str(tkWidget.winfo_pointery()+20)
		if lListOver[1] is not None: lListOver[1].destroy()
		lListOver[1]=tk.Toplevel(tkWidget)
		lListOver[1].wm_geometry("+"+sX+"+"+sY)
		lListOver[1].wm_overrideredirect(True)
		Label(lListOver[1],text=lListCom[idxOver],
				bd=1,justify=tk.LEFT).pack(padx=2,pady=2)
		lListOver[0]=idxOver
		print(idxOver)
	return None
tkListWidget.bind('<Motion>',f_coord)
def f_resetnListOver(*d):
	global lListOver
	lListOver[0]=-1
	lListOver[1].destroy()
	lListOver[1]=None
	lListOver[2]=None
	return None

tkListWidget.bind('<Leave>',f_resetnListOver)
tkListWidget.pack()
tkWidget.mainloop()
tkWidget.quit()

quit()

Pozdrawiam
Radek Głębicki

TT
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 97
1

@Radosław Głębicki: Super! Tylko mam teraz taki problem ze probowalem to przezucic na programowanie obiektowe tak ze teraz tkListWidget.bind('<Motion>',lambda x: self.f_coord(x)
i dla funkcji f_coord(self, l, *d) gdzie l to moj Listbox dostaje blad "Tuple index out of range" dla xEnrPos,yEnrPos=d[0].x,d[0].y.

TT
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 97
2

@Radosław Głębicki: Tzn ja chce poslac moja liste jako argument do funkcji f_coord zeby moc jej uzyc w innych klasach. Piszac tkListWidget.bind('<Motion>',lambda x: self.f_coord(x) mialem nadzieje ze wywola sie funkcja f_coord ktora jako argument l wezmie x ktore w tym wypadku bedzie tkListWidget. Gdy jednak w srodku funkcji wywoluje print(d) to dostaje () a dla print(l) dostaje <Motion event x=..., y =...>.

EDIT: Naprawione ale mam ostatnie pytanie. Co musze zmienic w kodzie zeby tooltip wyswietlal sie tylko jak najade na element a nie na sama liste? Bo jak mam dluga liste i jeden element to jak tylko najade liste to mi sie wyswietla ten element.

TT
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 97
1

@Radosław Głębicki: moglbys rozwinac ten bbox? W sensie jak powinien wygladac kod? Ja znam tkinter od 3 tygodni wiec niestety nic mi to jeszcze nie mowi :(

Radosław Głębicki
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Sardine, Italy
  • Postów: 187
2
Kopiuj
import tkinter as tk
from tkinter import Text,Listbox,Label

tkWidget=tk.Tk()
tkWidget.minsize(500,400)
tkWidget.title("List tooltips")
tkWidget.geometry("600x700+100+100")

tkListWidget=Listbox(height=9,width=30)

lListOver = [-1,None,None] #[0] dla indeksu,[1] dla okienka tt,[2] ew. dla obj. .after
lListElms = ['aaa','bbbbb','ccc','ddd','eee','fff','ggg','hhh','iii','jjj','kkk','lll']

e = 'Element '
lListCom = []
for elm in lListElms:
	lListCom.append(e + elm)

for elm in lListElms:
	tkListWidget.insert(tk.END,elm)

def f_coord(*d):
	global lListOver
	xEnrPos,yEnrPos = d[0].x,d[0].y
	idxOver = tkListWidget.nearest(yEnrPos)
	tLast = tkListWidget.bbox(len(lListElms)-1) # tupla ostatniego elementu listy
	if tLast is not None:
		yDownLast = tLast[1] + tLast[3] # sumujemy y elm. z wysokością elm.
		if yEnrPos>yDownLast:
			if lListOver[1] is not None:
				f_resetnListOver(None)
			return None
	if idxOver != lListOver[0]:
		sX,sY = str(tkWidget.winfo_pointerx()+15),str(tkWidget.winfo_pointery()+15)
		if lListOver[1] is not None: lListOver[1].destroy()
		lListOver[1] = tk.Toplevel(tkWidget)
		lListOver[1].wm_geometry("+"+sX+"+"+sY)
		lListOver[1].wm_overrideredirect(True)
		Label(lListOver[1],text=lListCom[idxOver],
				bd=1,justify=tk.LEFT).pack(padx=2,pady=2)
		lListOver[0] = idxOver
	return None
def f_resetnListOver(*d):
	global lListOver
	if lListOver[1] is None: return None
	lListOver[0] = -1
	lListOver[1].destroy()
	lListOver[1] = None
	lListOver[2] = None
	return None

tkListWidget.bind('<Motion>',f_coord)
tkListWidget.bind('<Leave>',f_resetnListOver)
tkListWidget.pack()
tkWidget.mainloop()
tkWidget.quit()

Pozdrawiam
Radek

TT
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 97
0

@Radosław Głębicki: Dzieki, mega mi pomogles!

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.