2

I am trying to display the full header name when the user hovers over the column header in Tkinter treeview(Tooltip). I made it show a text over button/label widgets but no luck with the column heading. Can you please help with some examples? The codes are below which has no error incurred but it doesn't show the full name over the headers as intended with lambda command in the headings...

import re
import webbrowser
from tkinter.tix import Balloon
import pandas as pd
import psycopg2
import os.path
from tkinter import *
from tkinter import ttk, messagebox
from tkinter import tix
from idlelib.tooltip import Hovertip
from datetime import datetime
import pyperclip
import sys

path = r'G:\MOTOR\EC\MOTOR-2909415798-Blast contacts book GUI'
os.chdir(path)
cwd = os.getcwd()
print(cwd)

# Create a GUI frame
root = Tk()
root.title('Blast Contacts')
root.iconbitmap(r'G:\MOTOR\EC\MOTOR-2909415798-Blast contacts book GUI\download.ico')
root.geometry("1000x800")

# Add style
style = ttk.Style()
style.theme_use('clam')

# Configure the Treeview colors
style.configure("Treeview",
                background="lightblue",
                foreground="black",
                rowheight=35,
                fieldbackground="D3D3D3")
# Change selected color
style.map('Treeview', background=[('selected', '#347083')])

# Connect to the Contact DB
conn = psycopg2.connect(host='rr18xz3319w4czb.cydjxinfag3j.us-east-1.rds.amazonaws.com', database='DPCAMOTR',
                        user='echoi', password='lfwr941jfJ', options="-c search_path=dbo,public")
cursor = conn.cursor()


# Query the Contacts on initialization and refresh
def query_contacts():

    # Connect to the Contact DB
    conn = psycopg2.connect(host='rr18xz3319w4czb.cydjxinfag3j.us-east-1.rds.amazonaws.com', database='DPCAMOTR',
                            user='echoi', password='lfwr941jfJ', options="-c search_path=dbo,public")
    cursor = conn.cursor()

    # clear the Treeview
    for record in my_tree.get_children():
        my_tree.delete(record)
    # retrieve the contact sql
    sql_file = open('Blast_Contacts_scripts_for GUI_v2.sql')
    query = sql_file.read()
    contacts_query = pd.read_sql_query(query, conn)

    # change df to list
    records = contacts_query.values.tolist()

    # add the contacts sql output to the screen
    global count
    count = 1
    for record in records:
        if count % 2 == 0:
            my_tree.insert(parent='', index='end', iid=count, text="", values=(
                count, record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7],
                record[8], record[9], record[10],
                record[11], record[12], record[13], record[14], record[15], record[16], record[17], record[18],
                record[19], record[20]),
                           tags=('evenrow',))
        else:
            my_tree.insert(parent='', index='end', iid=count, text="", values=(
                count, record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7],
                record[8], record[9], record[10],
                record[11], record[12], record[13], record[14], record[15], record[16], record[17], record[18],
                record[19], record[20]),
                           tags=('oddrow',))
        count += 1

    conn.close()

def tooltip(tv, col):
    print("clicked")
    if col == 'TB':
        tooltip = Hovertip(col, 'TEST BLAST', hover_delay=100)
    elif col == 'AP':
        tooltip = Hovertip(col, 'AFFIRM PEND', hover_delay=100)
    elif col == 'GU':
        tooltip = Hovertip(col, 'GSCC UNCOMPARED', hover_delay=100)
    elif col == 'CB':
        tooltip = Hovertip(col, 'CLIENT BLAST', hover_delay=100)
    elif col == 'DB':
        tooltip = Hovertip(col, 'DEALER BLAST', hover_delay=100)
    elif col == 'CD':
        tooltip = Hovertip(col, 'CMU DEALER', hover_delay=100)
    elif col == 'PE':
        tooltip = Hovertip(col, 'PREMATCH ECLR', hover_delay=100)
    elif col == 'PF':
        tooltip = Hovertip(col, 'PREMATCH FED', hover_delay=100)
    elif col == 'FT':
        tooltip = Hovertip(col, 'FAIL TRADES', hover_delay=100)
    elif col == 'PB':
        tooltip = Hovertip(col, 'PARTIALS BLAST', hover_delay=100)
    elif col == 'DK':
        tooltip = Hovertip(col, 'DTCC_DK BLAST', hover_delay=100)
    elif col == 'SE':
        tooltip = Hovertip(col, 'SALES ESCALATION', hover_delay=100)


    # blastname_tooltip = Button(root)
    # tooltip = Hovertip(blastname_tooltip, 'GSCC Uncompared', hover_delay=100)
    # blastname_tooltip.place(x=1140, y=160)
    # bc = my_tree.identify_column(event.x)
    # print(bc)
    #region = my_tree.identify("region", event.x, event.y)

    # x, y = event.x, event.y
    # if 1000 < x < 1030 and 0 < y < 30:
    # popup_blastName = Menu(my_tree, tearoff=0)
    # popup_blastName.add_command(label="Test Blast")
    # popup_blastName.tk_popup(event.x_root, event.y_root)


    #my_tree.bind('<Double-1>', tooltip)
    #tv.heading(col, text=col, command=lambda _col=col: tooltip(tv, _col, event))


# Create a Treeview frame
tree_frame = Frame(root)
tree_frame.pack(side=BOTTOM)

# Create a right Treeview scrollbar
tree_scroll = Scrollbar(tree_frame)
tree_scroll.pack(side=RIGHT, fill=Y)
# Create a bottom Treeview scrollbar
tree_scroll_bottom = Scrollbar(tree_frame, orient='horizontal')
tree_scroll_bottom.pack(side=BOTTOM, fill=X)

# Create the Treeview
my_tree = ttk.Treeview(tree_frame, xscrollcommand=tree_scroll_bottom.set, yscrollcommand=tree_scroll.set,
                       selectmode="extended", show='headings', height=11)
my_tree.pack()
# Configure the scrollbar
tree_scroll.config(command=my_tree.yview)
tree_scroll_bottom.config(command=my_tree.xview)

# Make a function for sorting columns by ascending/descending
def sort_column(tv, col, reverse: bool):
    try:
        data_list = [(int(my_tree.set(k, col).replace(',', '')), k) for k in my_tree.get_children("")]
    except Exception:
        data_list = [(my_tree.set(k, col), k) for k in my_tree.get_children("")]

    data_list.sort(reverse=reverse)

    for index, (val, k) in enumerate(data_list):
        my_tree.move(k, "", index)

    # reverse sort next time
    tv.heading(col, text=col, command=lambda _col=col: sort_column(tv, _col, not reverse))

    # retag rows after sorting
    tag = 'oddrow'
    for k in my_tree.get_children(""):
        tag = 'oddrow' if tag == 'evenrow' else 'evenrow'
        my_tree.item(k, tags=(tag,))

    # reindexing after sorting
    global count
    count = 1
    for k in my_tree.get_children(""):
        reindex = my_tree.item(k, 'values')
        # save new data
        my_tree.item(k, values=(
            count, reindex[1], reindex[2], reindex[3], reindex[4], reindex[5], reindex[6], reindex[7], reindex[8],
            reindex[9], reindex[10], reindex[11], reindex[12], reindex[13], reindex[14], reindex[15], reindex[16],
            reindex[17], reindex[18], reindex[19], reindex[20], reindex[21]))
        count += 1


# Define columns
my_tree['columns'] = ("ID",
                      "CC",
                      "ACCOUNT_NAME",
                      "BLAST_EMAIL",
                      "SALES_EMAIL",
                      "TB",
                      "AP",
                      "GU",
                      "CB",
                      "DB",
                      "CD",
                      "M_S",
                      "PE",
                      "PF",
                      "FT",
                      "PB",
                      "DK",
                      "SE",
                      "COMMENTS",
                      "AUDIT",
                      "A",
                      "CONTACTS_ID"
                      )
# Format columns
my_tree.column("#0", width=0, stretch=NO)
my_tree.column("ID", anchor=CENTER, width=40)
my_tree.column("CC", anchor=CENTER, width=50)
my_tree.column("ACCOUNT_NAME", anchor=W, width=400)
my_tree.column("BLAST_EMAIL", anchor=W, width=300)
my_tree.column("SALES_EMAIL", anchor=W, width=300)
my_tree.column("TB", anchor=CENTER, width=30)
my_tree.column("AP", anchor=CENTER, width=30)
my_tree.column("GU", anchor=CENTER, width=30)
my_tree.column("CB", anchor=CENTER, width=30)
my_tree.column("DB", anchor=CENTER, width=30)
my_tree.column("CD", anchor=CENTER, width=30)
my_tree.column("M_S", anchor=CENTER, width=35)
my_tree.column("PE", anchor=CENTER, width=30)
my_tree.column("PF", anchor=CENTER, width=30)
my_tree.column("FT", anchor=CENTER, width=30)
my_tree.column("PB", anchor=CENTER, width=30)
my_tree.column("DK", anchor=CENTER, width=30)
my_tree.column("SE", anchor=CENTER, width=30)
my_tree.column("COMMENTS", anchor=W, width=300)
my_tree.column("AUDIT", anchor=W, width=200)
my_tree.column("A", anchor=CENTER, width=40)
my_tree.column("CONTACTS_ID", anchor=CENTER, width=100)

# Create headings
my_tree.heading("#0", text="", anchor=CENTER)
my_tree.heading("ID", text="ID", anchor=CENTER)
my_tree.heading("CC", text="CC", anchor=CENTER,
                command=lambda _col="CC": sort_column(my_tree, _col, False))
my_tree.heading("ACCOUNT_NAME", text="ACCOUNT_NAME", anchor=CENTER,
                command=lambda _col="ACCOUNT_NAME": sort_column(my_tree, _col, False))
my_tree.heading("BLAST_EMAIL", text="BLAST_EMAIL", anchor=CENTER,
                command=lambda _col="BLAST_EMAIL": sort_column(my_tree, _col, False))
my_tree.heading("SALES_EMAIL", text="SALES_EMAIL", anchor=CENTER,
                command=lambda _col="SALES_EMAIL": sort_column(my_tree, _col, False))
my_tree.heading("TB", text="TB", anchor=CENTER,
                command=lambda _col="TB": tooltip(my_tree, _col))
my_tree.heading("AP", text="AP", anchor=CENTER,
                command=lambda _col="AP": tooltip(my_tree, _col))
my_tree.heading("GU", text="GU", anchor=CENTER,
                command=lambda _col="GU": tooltip(my_tree, _col))
my_tree.heading("CB", text="CB", anchor=CENTER,
                command=lambda _col="CB": tooltip(my_tree, _col))
my_tree.heading("DB", text="DB", anchor=CENTER,
                command=lambda _col="DB": tooltip(my_tree, _col))
my_tree.heading("CD", text="CD", anchor=CENTER,
                command=lambda _col="CD": tooltip(my_tree, _col))
my_tree.heading("M_S", text="M_S", anchor=CENTER)
my_tree.heading("PE", text="PE", anchor=CENTER,
                command=lambda _col="PE": tooltip(my_tree, _col))
my_tree.heading("PF", text="PF", anchor=CENTER,
                command=lambda _col="PF": tooltip(my_tree, _col))
my_tree.heading("FT", text="FT", anchor=CENTER,
                command=lambda _col="FT": tooltip(my_tree, _col))
my_tree.heading("PB", text="PB", anchor=CENTER,
                command=lambda _col="PB": tooltip(my_tree, _col))
my_tree.heading("DK", text="DK", anchor=CENTER,
                command=lambda _col="DK": tooltip(my_tree, _col))
my_tree.heading("SE", text="SE", anchor=CENTER,
                command=lambda _col="SE": tooltip(my_tree, _col))
my_tree.heading("COMMENTS", text="COMMENTS", anchor=CENTER,
                command=lambda _col="COMMENTS": sort_column(my_tree, _col, False))
my_tree.heading("AUDIT", text="AUDIT", anchor=CENTER,
                command=lambda _col="AUDIT": sort_column(my_tree, _col, False))
my_tree.heading("A", text="A", anchor=CENTER)
my_tree.heading("CONTACTS_ID", text="CONTACTS_ID", anchor=CENTER)

# Run to pull the SR data from the SQLIte on start
query_contacts()
root.mainloop()
  • 2
    What did you try? If you post a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) then we can make relevant suggestions. – Rory Aug 02 '22 at 13:20
  • If all you need is examples for how to use the `Tooltip` with widgets then look at [this example](https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter). Without seeing any code it's very difficult to offer you something useful. However, if you `bind` the `Tooltip` to your `treeview` then it would activate anyhwere in the region and it sounds like you don't want that. So maybe you can pick up mouse coordinates for the area of interest and use a callback for activating the `Tooltip` in that region? – Rory Aug 02 '22 at 18:09

1 Answers1

0

Would something like this work for you? I found the region of interest by just printing the coordinates when my mouse was over the heading and then used a callback to bind the "Tooltip" based on the coordinates of the header.

from tkinter import ttk
from tkinter import tix

root = tix.Tk()
root.geometry('600x600')

columns = 'something'

tip = tix.Balloon(root)

tree = ttk.Treeview(root, columns=columns, show='headings')
tree.grid(row=0, column=0, sticky='nsew')

tree.heading('something', text='something title')
    
    
def manage_tooltip(event):
    x, y = event.x, event.y
    if 0 < x < 200 and 0 < y < 25:
        tip.bind_widget(tree, balloonmsg="tooltip message goes here")
    else:
        tip.unbind_widget(tree)
        

root.bind('<Motion>', manage_tooltip)

root.mainloop()
Rory
  • 661
  • 1
  • 6
  • 15
  • is there a way to show the balloon right next to the column where the cursor is hovering over? currently it pops up right in the middle of my treeview not right next to the column – EUNHEE CHOI Aug 11 '22 at 15:32
  • @EUNHEECHOI I do not know how you would specify that using the `tix` module `Baloon` widget. However [this page here](https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter) has several ideas that I think you could implement. The `tix` module you can see is identified as deprecated in the messages. I used it just to quickly illustrate an idea with minimal code, my apologies for that. The SO thread I posted would be the right direction to go here I think. – Rory Aug 11 '22 at 22:25