Make scrollable canvas expand based on the embedded frame - python

I'm creating a frame which has many that are build vertically and I need a scrollbar to make them all visible. To do that I embedded my frame on a canvas.
The problem is that the widgets on the frame require more width that what the canvas provides as shown below
If I don't embed the frame on a canvas, then the widgets take the proper amount of width and are resized properly. I believe that the problem is the the canvas spans across 1 column whereas the widgets in the canvas require more than 2. I used `columnspan=2' on the canvas but the widgets on the embedded frame are still cut; they are just zoomed.
Is there a way to expand the width of the canvas according to the width they frame wants?
Here's an example code
import tkinter as tk
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
from PIL import ImageTk, Image
import os
# Method to make Label(Widget) invisible
def hide_frame(frame):
# This will remove the widget
frame.grid_remove()
# Method to make Label(widget) visible
def show_frame(frame, c, r):
# This will recover the widget
frame.grid(column=c, row=r)
def populate(frame):
'''Put in some fake data'''
for row in range(100):
tk.Label(frame, text="%s" % row, width=3, borderwidth="1",
relief="solid").grid(row=row, column=0)
t="Blah blah blah blah blah blah blah blah %s" %row
tk.Label(frame, text=t).grid(row=row+4, column=1)
#____________________________________________________________________________________________
#This will be the main window
window = tk.Tk()
#window.geometry('1500x1200')
window.attributes('-zoomed', True)
window.title("daq")
frame_main = Frame(window)
frame_main.place(rely=0.10, relx=0.0, relwidth=1.0)
#Create a tabcontrol
tabControl = ttk.Notebook(frame_main)
tabControl.grid(column=0, row=1)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Digitizer tab
tab_Digitizer = ttk.Frame(tabControl)
tabControl.add(tab_Digitizer, text=' Digitizer ')
digitizer_tab_dummy_label = Label(tab_Digitizer, text="")
digitizer_tab_dummy_label.grid(column=0, row=0)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Channel settings
lbl_channel_registers = Label(tab_Digitizer, text="Channel registers", font='bold')
lbl_channel_registers.grid(column=0, row=3, sticky=W)
frame_channel_registers = Frame(tab_Digitizer)
frame_channel_registers.grid(column=0, row=4)
btn_hide_channel_registers = Button(tab_Digitizer, text="Hide", fg="red", command=lambda: hide_frame(frame_channel_registers) )
btn_hide_channel_registers.grid(column=1, row=3)
btn_show_channel_registers = Button(tab_Digitizer, text="Show", fg="green", command=lambda: show_frame(frame_channel_registers, 0, 4))
btn_show_channel_registers.grid(column=2, row=3)
##Channel settings - PSD
def onFrameConfigure(event):
'''Reset the scroll region to encompass the inner frame'''
canvas.configure(scrollregion=canvas.bbox("all"))
def handle_user_scrolling(event):
canvas.yview_scroll(int(-event.delta/abs(event.delta)), "units")
def handle_scrollbar_scrolling(event):
canvas.configure(scrollregion=canvas.bbox("all"))
lbl_PSD_channel_registers = Label(frame_channel_registers, text="DPP-PSD registers")
lbl_PSD_channel_registers.grid(column=0, row=3, padx=5, sticky=W)
btn_hide_PSD_channel_registers = Button(frame_channel_registers, text="Hide", fg="red", command=lambda: [hide_frame(canvas), hide_frame(myscrollbar)] )
btn_hide_PSD_channel_registers.grid(column=1, row=3)
btn_show_PSD_channel_registers = Button(frame_channel_registers, text="Show", fg="green", command=lambda: [show_frame(canvas, 0, 4), show_frame(myscrollbar, 1, 4)])
btn_show_PSD_channel_registers.grid(column=2, row=3)
canvas = tk.Canvas(frame_channel_registers, borderwidth=1, background="#FF0000")
frame_PSD_channel_registers = Frame(canvas, background="#0000FF")
frame_PSD_channel_registers.grid(column=0, row=0, padx=25, sticky=W)
frame_PSD_channel_registers.bind("<MouseWheel>", handle_user_scrolling)
frame_PSD_channel_registers.bind("<Configure>", handle_scrollbar_scrolling)
myscrollbar=Scrollbar(frame_channel_registers ,orient="vertical", command=canvas.yview)
#canvas.configure(yscrollcommand=myscrollbar.set)
canvas.configure(scrollregion=canvas.bbox("all"))
myscrollbar.grid(column=1, row=4, sticky=NS)
canvas.grid(column=0, row=4, columnspan=3)
#frame_PSD_channel_registers = Frame(canvas, background="#0000FF")
#frame_PSD_channel_registers.grid(column=0, row=0, padx=25, sticky=W)
#frame_PSD_channel_registers.bind("<MouseWheel>", handle_user_scrolling)
#frame_PSD_channel_registers.bind("<Configure>", handle_scrollbar_scrolling)
canvas.create_window((0, 0), window=frame_PSD_channel_registers, anchor="nw")
canvas.configure(yscrollcommand=myscrollbar.set)
###### PSD channels settings
#Short gate
def short_gate_width_selection(event):
print ("Short gate width : " + str( entry_short_gate_width.get() ) + " " + var_short_gate_width_unit.get() )
pass
lbl_short_gate_width = Label(frame_PSD_channel_registers, text="Short gate width")
lbl_short_gate_width.grid(column=0, row=0, padx=25, sticky=W)
var_short_gate_width = IntVar(frame_PSD_channel_registers)
var_short_gate_width.set("15")
entry_short_gate_width = Entry(frame_PSD_channel_registers, width=10, textvariable=var_short_gate_width, exportselection=0)
entry_short_gate_width.grid(column=1, row=0, sticky=W)
opt_short_gate_width = ["samples", "ns"]
var_short_gate_width_unit = StringVar(frame_PSD_channel_registers)
var_short_gate_width_unit.set(opt_short_gate_width[0]) # default value
short_gate_width_unit = OptionMenu(frame_PSD_channel_registers, var_short_gate_width_unit, *opt_short_gate_width, command = short_gate_width_selection)
entry_short_gate_width.bind('<Return>', short_gate_width_selection)
short_gate_width_unit.grid(column=2, row=0, sticky=W)
#Long gate
def long_gate_width_selection(event):
print ("Long gate width : " + str( entry_long_gate_width.get() ) + " " + var_long_gate_width_unit.get() )
pass
lbl_long_gate_width = Label(frame_PSD_channel_registers, text="Long gate width")
lbl_long_gate_width.grid(column=0, row=1, padx=25, sticky=W)
var_long_gate_width = IntVar(frame_PSD_channel_registers)
var_long_gate_width.set("100")
entry_long_gate_width = Entry(frame_PSD_channel_registers, width=10, textvariable=var_long_gate_width, exportselection=0)
entry_long_gate_width.grid(column=1, row=1, sticky=W)
opt_long_gate_width = ["samples", "ns"]
var_long_gate_width_unit = StringVar(frame_PSD_channel_registers)
var_long_gate_width_unit.set(opt_long_gate_width[0]) # default value
long_gate_width_unit = OptionMenu(frame_PSD_channel_registers, var_long_gate_width_unit, *opt_long_gate_width, command = long_gate_width_selection)
entry_long_gate_width.bind('<Return>', long_gate_width_selection)
long_gate_width_unit.grid(column=2, row=1, sticky=W)
#Extras word options
def extras_word_options_selection(event):
if 'Counter' in var_extras_word_options_selection.get():
extras_word_options_step_counter.grid(column=2, row=22, sticky=W)
else:
extras_word_options_step_counter.grid_remove()
print ("Extras word options : " + var_extras_word_options_selection.get() )
pass
def extras_word_options_step_counter_selection(event):
print ("Step for trigger counter : ", var_extras_word_options_step_counter.get() )
pass
lbl_extras_word_options_selection = Label(frame_PSD_channel_registers, text="Extras word options")
lbl_extras_word_options_selection.grid(column=0, row=22, padx=25, sticky=W)
opt_extras_word_options_selection = ["[31:16] = Extended Time Stamp , [15:0] = Baseline * 4",
"[31:16] = Extended Time Stamp , [15:0] = Flags",
"[31:16] = Extended Time Stamp , [15:10] = Flags, [9:0] = Fine Time Stamp",
"[31:16] = Lost Trigger Counter , [15:0] = Total Trigger Counter",
"[31:16] = Positive Zero Crossing, [15:0] = Negative Zero Crossing",
"Fixed value = 0x12345678 (debug use only)."]
var_extras_word_options_selection = StringVar(frame_PSD_channel_registers)
var_extras_word_options_selection.set(opt_extras_word_options_selection[2]) # default value
extras_word_options_selection = OptionMenu(frame_PSD_channel_registers, var_extras_word_options_selection, *opt_extras_word_options_selection, command = extras_word_options_selection)
extras_word_options_selection.grid(column=1, row=22, sticky=W)
#This keeps the window open - has to be at the end
window.mainloop()

Related

How to align the LabelFrames to left on tkinter?

I am new to tkinter and learning to organize the frames in tkinter.
In the following code, I have created 3 label frames.
The frames are not aligned as per my requirements and I would like to get help organizing them cleanly.
Required
===========
By default it gives me following layout
LabelFrame1 LabelFrame2
LabelFrame3
I would like to have left aligned label frames like this:
LabelFrame1 LabelFrame2
LabelFrame3
How to align all label frames to left so that I can have spaces in right?
MWE
# %%writefile a.py
import tkinter as tk
from tkinter import ttk,messagebox
# variables
padding = dict(padx=20,pady=20)
padding_widget = dict(padx=10,pady=5)
# root app
win = tk.Tk()
w,h = 800, 600 # app
ws, hs = win.winfo_screenwidth(), win.winfo_screenheight() # screen
x,y = -10,hs*0.1
win.geometry('%dx%d+%d+%d' % (w, h, x, y))
#===================== end of Snippets =====================================
# Frame contains labelframes
f = tk.Frame(win,height=200, width=200)
f.grid(row=0,column=0,padx=20, pady=20)
f.pack(fill="both", expand="yes")
#=============================================================================
# label frame: Find and Replace
lf00 = tk.LabelFrame(f,text='Find and Replace from Clipboard')
lf00.grid(row=0,column=0,padx=20, pady=20)
b = tk.Button(lf00,text='Click to Replace')
b.grid(row=2,column=0,sticky='news',**padding)
#=============================================================================
# label frame: Text Manipulation
lf01 = tk.LabelFrame(f,text='Text Manipulation')
lf01.grid(row=0,column=1,padx=20, pady=20)
b = tk.Button(lf01,text='Click to Replace')
b.grid(row=2,column=0,sticky='news',**padding)
#=============================================================================
# LabelFrame: Calculator
lf10 = tk.LabelFrame(f,text='Calculator')
lf10.grid(row=1,column=0,padx=10, pady=10)
l_exp = tk.Label(lf10,text="Expression")
t_i = tk.Text(lf10, height=2, width=70, bg="light yellow", padx=0,pady=0)
t_o = tk.Text(lf10, height=4, width=40, bg="light cyan",padx=0,pady=0)
b_calc = tk.Button(lf10,height=1,width=10,text="Calculate")
l_exp.pack();t_i.pack();b_calc.pack();t_o.pack()
# Main window
win.config()
win.mainloop()
Update
# %%writefile a.py
import tkinter as tk
from tkinter import ttk,messagebox
# variables
padding = dict(padx=20,pady=20)
padding_widget = dict(padx=10,pady=5)
# root app
win = tk.Tk()
w,h = 800, 600 # app
ws, hs = win.winfo_screenwidth(), win.winfo_screenheight() # screen
x,y = -10,hs*0.1
win.geometry('%dx%d+%d+%d' % (w, h, x, y))
#===================== end of Snippets =====================================
# Frame contains labelframes
f = tk.Frame(win,height=200, width=200)
f.grid(row=0,column=0,padx=20, pady=20,sticky="w")
f.pack(fill="both", expand="yes")
#=============================================================================
# label frame: Find and Replace
lf00 = tk.LabelFrame(f,text='Find and Replace from Clipboard')
lf00.grid(row=0,column=0,padx=20, pady=20,sticky="w")
b = tk.Button(lf00,text='Click to Replace')
b.grid(row=2,column=0,sticky='w',**padding)
#=============================================================================
# label frame: Text Manipulation
lf01 = tk.LabelFrame(f,text='Text Manipulation')
lf01.grid(row=0,column=1,padx=20, pady=20,sticky="w")
b = tk.Button(lf01,text='Click to Replace')
b.grid(row=2,column=0,sticky='w',**padding)
#=============================================================================
# LabelFrame: Calculator
lf10 = tk.LabelFrame(f,text='Calculator')
lf10.grid(row=1,column=0,padx=10, pady=10,sticky="w")
l_exp = tk.Label(lf10,text="Expression")
t_i = tk.Text(lf10, height=2, width=70, bg="light yellow", padx=0,pady=0)
t_o = tk.Text(lf10, height=4, width=40, bg="light cyan",padx=0,pady=0)
b_calc = tk.Button(lf10,height=1,width=10,text="Calculate")
l_exp.pack();t_i.pack();b_calc.pack();t_o.pack()
# Main window
win.config()
win.mainloop()
For greater clarity, first draw on a piece of paper what you want to place in the window. Like this:
And you will see that you need to stretch tk.LabelFrame(f, text='Calculator') over several cells ( columnspan=3).
import tkinter as tk
from tkinter import ttk, messagebox
# variables
padding = dict(padx=20, pady=20)
padding_widget = dict(padx=10, pady=5)
# root app
win = tk.Tk()
w, h = 800, 600 # app
ws, hs = win.winfo_screenwidth(), win.winfo_screenheight() # screen
x, y = -10, hs * 0.1
win.geometry('%dx%d+%d+%d' % (w, h, x, y))
# ===================== end of Snippets =====================================
# Frame contains labelframes
f = tk.Frame(win, height=200, width=200)
f.grid(row=0, column=0, padx=20, pady=20)
f.pack(fill="both", expand="yes")
f.columnconfigure(1, weight=1)
# =============================================================================
# label frame: Find and Replace
lf00 = tk.LabelFrame(f, text='Find and Replace from Clipboard')
lf00.grid(row=0, column=0, padx=(20, 2), pady=20, sticky='e')
b = tk.Button(lf00, text='Click to Replace')
b.grid(row=0, column=0, sticky='nswe', **padding)
# =============================================================================
# label frame: Text Manipulation
lf01 = tk.LabelFrame(f, text='Text Manipulation')
lf01.grid(row=0, column=1, padx=2, pady=20, sticky='w')
b = tk.Button(lf01, text='Click to Replace')
b.grid(row=0, column=0, sticky='nswe', **padding)
# =============================================================================
# LabelFrame: Calculator
lf10 = tk.LabelFrame(f, text='Calculator')
lf10.grid(row=1, column=0, columnspan=3, padx=20, pady=20, sticky='w')
l_exp = tk.Label(lf10, text="Expression")
t_i = tk.Text(lf10, height=2, width=70, bg="light yellow", padx=0, pady=0)
t_o = tk.Text(lf10, height=4, width=40, bg="light cyan", padx=0, pady=0)
b_calc = tk.Button(lf10, height=1, width=10, text="Calculate")
l_exp.pack()
t_i.pack()
b_calc.pack()
t_o.pack()
# Main window
win.config()
win.mainloop()

Python tkinter : how to resize widgets when i resize the screen

I'm beginner...
When the code below is executed, how do I make the widgets inside increase when the screen size is increased to width? but, it couldn't resized..
Does it matter if I use either pack or grid?
i can't solve it....
from tkinter import *
from tkinter import ttk
class program:
def __init__(self):
self.root = Tk()
self.root.title("Python")
self.root.resizable(True, True)
self.create_project()
def create_project(self):
Topic_Label = ttk.Label(self.root, text="program")
Topic_Label.pack(side="top")
Record_LabelFrame = LabelFrame(self.root, text="Record information")
Record_LabelFrame.pack(side="top", anchor=W, padx=10)
date = ttk.Label(Record_LabelFrame, text="date")
date.grid(column=0, row=0, sticky=W)
Topic_Label_Entry1 = ttk.Entry(Record_LabelFrame)
Topic_Label_Entry1.grid(column=0, row=1)
time = ttk.Label(Record_LabelFrame, text="time")
time.grid(column=1, row=0, sticky=W)
Topic_Label_Combo1 = ttk.Combobox(Record_LabelFrame)
Topic_Label_Combo1.grid(column=1, row=1)
recorder = ttk.Label(Record_LabelFrame, text="record")
recorder.grid(column=2, row=0, sticky=W)
Topic_Label_Entry2 = ttk.Entry(Record_LabelFrame)
Topic_Label_Entry2.grid(column=2, row=1)
loc = ttk.Label(Record_LabelFrame, text="location")
loc.grid(column=0, row=2, sticky="W")
RadioVar = IntVar()
Radio_Frame = Frame(Record_LabelFrame)
Radio_Frame.grid(column=0, row=3, sticky=W)
Topic_Label_Radio1 = ttk.Radiobutton(Radio_Frame, text="A", variable=RadioVar, value=1)
Topic_Label_Radio1.grid(column=0, row=0)
Topic_Label_Radio2 = ttk.Radiobutton(Radio_Frame, text="B", variable=RadioVar, value=2)
Topic_Label_Radio2.grid(column=1, row=0)
Topic_Label_Radio3 = ttk.Radiobutton(Radio_Frame, text="C", variable=RadioVar, value=3)
Topic_Label_Radio3.grid(column=2, row=0)
count = ttk.Label(Record_LabelFrame, text="count")
count.grid(column=1, row=2, sticky="W")
Topic_Label_Combo2 = ttk.Combobox(Record_LabelFrame)
Topic_Label_Combo2.grid(column=1, row=3)
seed_name = ttk.Label(Record_LabelFrame, text="seed")
seed_name.grid(column=2, row=2, sticky="W")
Topic_Label_Entry4 = ttk.Entry(Record_LabelFrame)
Topic_Label_Entry4.grid(column=2, row=3)
mt = program()
mt.root.mainloop()
I want to print like this that widget resize automatically when i resize the screen
https://i.stack.imgur.com/iSbaJ.png
https://i.stack.imgur.com/mzv9R.png

Tkinter: Cannot edit entry widget after 2nd selection

I've made a simple GUI for placing objects in RoboDK via a Python script using Tkinter.
Essentailly, the user selects an object using the btnSelect button, which then updates the entry widgets with its coordinates (x, y, z, then Euler rotation). The user can then edit the entry widgets then select the "Move object" button (or btnMove in the code) to move the object to the new position. However, when selecting an object for the second time, the entry fields cannot be edited without selecting a new object.
from tkinter.constants import DISABLED, NORMAL, CENTER, END
from typing import *
import tkinter as tk
import threading
from robolink import * # RoboDK API
from robodk import * # Robot toolbox
X_MAX = 500
X_MIN = 0
Y_MAX = 300
Y_MIN = -360
ROTZ_MAX = 180
ROTZ_MIN = -180
# Keep track of selected item
obj = None
def main():
rdk = Robolink()
window = buildGUI(rdk)
window.mainloop()
def buildGUI(rdk: Robolink) -> tk.Tk:
window = tk.Tk()
canvas = tk.Canvas(window, width=200)
canvas.grid(columnspan=3, rowspan=14)
# Set the window title (must be unique for the docking to work, try to be creative)
window_title = 'Move object window'
window.title(window_title)
title = tk.Label(window, text="Move Object", font="none 14 bold")
title.grid(columnspan=3, column=0, row=0)
# Delete the window when we close it
window.protocol("WM_DELETE_WINDOW", lambda: onClose(window))
deadspace1 = tk.Label(text="")
deadspace1.grid(columnspan=3, column=0, row=1)
selectText = tk.StringVar()
btnSelect = tk.Button(window, textvariable=selectText, height=2, width=0,
bg="#bbbbbb", fg='white', justify=CENTER)
selectText.set("Select object")
btnSelect.grid(column=1, row=2)
deadspace2 = tk.Label("")
deadspace2.grid(columnspan=3, column=0, row=3)
objName = tk.StringVar()
objLabel = tk.Label(window, textvariable=objName, font="none 12 bold")
objName.set("None Selected")
objLabel.grid(columnspan=3, column=0, row=4)
deadspace2 = tk.Label("")
deadspace2.grid(columnspan=3, column=0, row=5)
# Position options
xLabel = tk.Label(window, text="x: ", font='none 12')
xLabel.grid(column=0, row=6)
xEntry = tk.Entry(window, width=10)
xEntry.grid(columnspan=2, column=1, row=6)
yLabel = tk.Label(window, text="y: ", font='none 12')
yLabel.grid(column=0, row=7)
yEntry = tk.Entry(window, width=10)
yEntry.grid(columnspan=2, column=1, row=7)
zLabel = tk.Label(window, text="z: ", font='none 12')
zLabel.grid(column=0, row=8)
zEntry = tk.Entry(window, width=10)
zEntry.grid(columnspan=2, column=1, row=8)
# Rotation options
rxLabel = tk.Label(window, text="rx: ", font='none 12')
rxLabel.grid(column=0, row=9)
rxEntry = tk.Entry(window, width=10)
rxEntry.grid(columnspan=2, column=1, row=9)
ryLabel = tk.Label(window, text="ry: ", font='none 12')
ryLabel.grid(column=0, row=10)
ryEntry = tk.Entry(window, width=10)
ryEntry.grid(columnspan=2, column=1, row=10)
rzLabel = tk.Label(window, text="rz: ", font='none 12')
rzLabel.grid(column=0, row=11)
rzEntry = tk.Entry(window, width=10)
rzEntry.grid(columnspan=2, column=1, row=11)
entries = [xEntry, yEntry, zEntry, rzEntry, ryEntry, rxEntry]
deadspace3 = tk.Label(text="")
deadspace3.grid(columnspan=3, column=0, row=12)
btnMove = tk.Button(window, text="Move object", height=2, width=0,
bg="#bbbbbb", fg='white', justify=CENTER,
command=lambda: moveObject(entries))
btnMove.grid(column=1, row=13)
selectCallback = lambda: select_item(rdk, selectText, objName, entries)
btnSelect['command'] = selectCallback
EmbedWindow(window_title)
return window
# Close the window
def onClose(window: tk.Tk):
window.destroy()
quit(0)
def select_item(rdk: Robolink, selectText: tk.StringVar, objName: tk.Label,
entries: List[tk.Entry]):
def thread_btnSelect():
selectText.set("Waiting...")
item = rdk.ItemUserPick('Select an item', ITEM_TYPE_OBJECT)
if item.Valid():
global obj
obj = item
objName.set(item.Name())
updateObjectPosition(item, entries)
selectText.set("Select object")
# Prevent RoboDK from freezing
threading.Thread(target=thread_btnSelect).start()
def updateObjectPosition(item: Item, entries: List[tk.Entry]):
pose = item.Pose()
coords = Pose_2_KUKA(pose)
for entry, coord in zip(entries, coords):
entry.delete(0, END)
entry.insert(0, str(coord))
def moveObject(entries: tk.Entry):
global obj
if obj is None:
ShowMessage("No object selected")
return
try:
coords = getCoords(entries)
obj.setPose(KUKA_2_Pose(coords))
except Exception as err:
ShowMessage(str(err))
def getCoords(entries: List[tk.Entry]) -> list:
coords = [0] * 6
for i, entry in enumerate(entries):
coords[i] = float(entry.get())
return coords
if __name__ == "__main__":
main()
Using a tkinter.Variable such as tkinter.DoubleVar with your entries should fix this issue. You can also use tkinter.DoubleSpin for convenience.
x, y, z, rx, ry, rz = Pose_2_TxyzRxyz(item.Pose())
xVar = tk.DoubleVar(value=x)
xEntry = tk.Spinbox(window, textvariable=xVar, format="%.2f", from_=-9999999, to=9999999)
xVal = xVal.get()

Tkinter create a hidden frame

I'm creating a GUI whose window will consists of a few frames.
I need to pack many different widgets in each frame so if all are shown it will occupy a lot of space.
The idea is that each frame will be revealed/hidden with the push of a button.
I'm able to hide and reveal each frame using buttons, however I'd like those frame to start as hidden.
Is there a way to do that?
Here's a code that hides/reveals the frames.
import tkinter as tk
from tkinter import *
from tkinter import ttk
from PIL import ImageTk, Image
import os
# Method to make Label(Widget) invisible
def hide_frame(frame):
# This will remove the widget
frame.grid_remove()
# Method to make Label(widget) visible
def show_frame(frame):
# This will recover the widget
frame.grid()
#____________________________________________________________________________________________
#This will be the main window
window = tk.Tk()
window.geometry('500x500')
window.title("daq")
#____________________________________________________________________________________________
#Upper frame
frame_logo = Frame(window)
#____________________________________________________________________________________________
#Lower frame
frame_main = Frame(window)
frame_main.place(rely=0.10, relx=0.0, relwidth=1.0)
#Create a tabcontrol
tabControl = ttk.Notebook(frame_main)
tabControl.grid(column=0, row=1)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Digitizer tab
tab_Digitizer = ttk.Frame(tabControl)
tabControl.add(tab_Digitizer, text=' Digitizer ')
digitizer_tab_dummy_label = Label(tab_Digitizer, text="")
digitizer_tab_dummy_label.grid(column=0, row=0)
#General board settings
lbl_general_board_registers = Label(tab_Digitizer,
text="General board registers",
font='bold')
lbl_general_board_registers.grid(column=0, row=1, sticky=W)
frame_general_board_registers = Frame(tab_Digitizer)
frame_general_board_registers.grid(column=0, row=2)
btn_hide_general_board_registers = Button(tab_Digitizer, text="Hide", fg="red", command=lambda: hide_frame(frame_general_board_registers) )
btn_hide_general_board_registers.grid(column=1, row=1)
btn_show_general_board_registers = Button(tab_Digitizer, text="Show", fg="green", command=lambda: show_frame(frame_general_board_registers))
btn_show_general_board_registers.grid(column=2, row=1)
lbl_threshold1 = Label(frame_general_board_registers, text="Threshold1")
lbl_threshold1.grid(column=1, row=0)
lbl_trigger1 = Label(frame_general_board_registers, text="Trigger1")
lbl_trigger1.grid(column=1, row=1)
#Channel settings
lbl_channel_registers = Label(tab_Digitizer,
text="Channel registers",
font='bold')
lbl_channel_registers.grid(column=0, row=3, sticky=W)
frame_channel_registers = Frame(tab_Digitizer)
frame_channel_registers.grid(column=0, row=4)
btn_hide_channel_registers = Button(tab_Digitizer, text="Hide", fg="red", command=lambda: hide_frame(frame_channel_registers) )
btn_hide_channel_registers.grid(column=1, row=3)
btn_show_channel_registers = Button(tab_Digitizer, text="Show", fg="green", command=lambda: show_frame(frame_channel_registers))
btn_show_channel_registers.grid(column=2, row=3)
lbl_threshold = Label(frame_channel_registers, text="Threshold")
lbl_threshold.grid(column=1, row=0)
lbl_trigger = Label(frame_channel_registers, text="Trigger")
lbl_trigger.grid(column=1, row=1)
#Misc settings
lbl_misc_registers = Label(tab_Digitizer,
text="Misc settings",
font='bold')
lbl_misc_registers.grid(column=0, row=5, sticky=W)
frame_misc_registers = Frame(tab_Digitizer)
frame_misc_registers.grid(column=0, row=6)
#_________________________________________________________________________________________________
tab_SignalViewer = ttk.Frame(tabControl)
tabControl.add(tab_SignalViewer, text=' Signal Viewer ')
#tabControl.grid(column=0, row=1)
#This keeps the window open - has to be at the end
window.mainloop()

Tkinter Treeview doesn't give indication for hidden characters

The text is "This is too long text".
As you can see the view provides an option to expand horizontally but there is not indication that there are more characters.
I need some indication for the user that there are more characters in the cell.
i.e showing "...".
This is the code:
from tkinter import ttk
import tkinter as tk
win = tk.Tk()
win.resizable(width=0, height=0)
tree = ttk.Treeview(win, selectmode='browse')
tree.grid(row=0, column=0)
vsb = ttk.Scrollbar(win, orient="horizontal", command=tree.xview)
vsb.grid(row=1, column=0, sticky=tk.W + tk.E + tk.N + tk.S)
tree.configure(xscrollcommand=vsb.set)
vsb = ttk.Scrollbar(win, orient="vertical", command=tree.yview)
vsb.grid(row=0, column=1, sticky=tk.W + tk.E + tk.N + tk.S)
tree.configure(yscrollcommand=vsb.set)
tree["columns"] = ("1", "2")
tree['show'] = 'headings'
tree.column("1", anchor='c')
tree.column("2", width=100, anchor='c')
tree.heading("1", text="Col 1")
tree.heading("2", text="Col 2")
tree.insert("", 'end', text="L4", values=("This text is too long","Short text"))
win.mainloop()
Update:
Turns out we can bind '<ButtonRelease-1>' to the treeview as noted on this post: how-to-detect-resizing-of-ttk-treeview-column that #stovfl has pointed out in the comments. So I have updated my answer with less overhead :D.
I created a method that will check to see of the column width has changed and then if it has then calculate each row to see if we need to add ... to the end.
This is accomplished with ImageFont from PIL thought for some reason there seams to be a size difference between Treeview font and PIL font. After some testing it appears that any font size you set in the style for the Treeview just add 5 to the PIL font and it should calculate correctly.
import tkinter as tk
import tkinter.ttk as ttk
from PIL import ImageFont
track_data = [[('This text is too long', 'Short text'), 'This text is too long'],
[('This text is toooooooo long', 'Short text'), 'This text is toooooooo long'],
[('This text is longer than most others', 'Short text'), 'This text is longer than most others'],
[('This text is short', 'Short text'), 'This text is short']]
def add_to_tree_list(long_text, short_text, top):
track_data.append([(long_text.get(), short_text.get()), long_text.get()])
top.destroy()
update_text()
def pop_list(ndex):
track_data.pop(ndex)
def remove_row_from_list():
top = tk.Toplevel(win)
build_top_frame(top)
update_text()
def build_top_frame(top):
row_button_list = []
for child in top.winfo_children():
child.destroy()
frame = tk.Frame(top)
frame.pack()
for ndex, sub_list in enumerate(track_data):
row_button_list.append([tk.Button(frame, text='Remove',
command=lambda n=ndex: (pop_list(n), update_text(), build_top_frame(top))),
tk.Label(frame, text='Row {}: {}'.format(ndex + 1, sub_list[1]))])
row_button_list[-1][0].grid(row=ndex, column=0)
row_button_list[-1][1].grid(row=ndex, column=1, sticky='w')
def top_for_new_row():
top = tk.Toplevel(win)
tk.Label(top, text='Add long text: ').grid(row=0, column=0)
tk.Label(top, text='Add short text: ').grid(row=1, column=0)
e1 = tk.Entry(top)
e2 = tk.Entry(top)
e1.grid(row=0, column=1)
e2.grid(row=1, column=1)
tk.Button(top, text='Submit', command=lambda: add_to_tree_list(e1, e2, top)).grid(row=2, column=0)
tk.Button(top, text='Cancel', command=top.destroy).grid(row=2, column=1)
def clear_and_load(data):
tree.delete(*tree.get_children())
for sub_list in data:
tree.insert('''''', 'end', text='L4', values=sub_list[0])
def update_text(_=None):
global old_col_width, old_track_data_len
col_width = tree.column('1')['width']
if col_width != old_col_width or old_track_data_len != len(track_data):
old_col_width = col_width
new_track_data = []
for text in track_data:
font = ImageFont.truetype("arial.ttf", 17)
size = font.getsize(text[0][0])
previous_long_text = ''
new_long_text = ''
if size[0] > col_width:
for char in text[0][0]:
new_long_text = '{}{}'.format(new_long_text, char)
new_size = font.getsize('{}{}'.format(new_long_text, '...'))[0]
if new_size < col_width:
previous_long_text = new_long_text
else:
new_track_data.append([('{}{}'.format(previous_long_text, '...'), text[0][1]), ''])
break
else:
new_track_data.append(text)
clear_and_load(new_track_data)
old_track_data_len = len(track_data)
win = tk.Tk()
win.columnconfigure(0, weight=1)
old_col_width = 200
old_track_data_len = 0
win.resizable(width=0, height=0)
style = ttk.Style()
style.configure('Treeview', font=('arial', 12))
tree = ttk.Treeview(win)
tree.grid(row=0, column=0)
tree.bind('<ButtonRelease-1>', update_text)
vsb = ttk.Scrollbar(win, orient='horizontal', command=tree.xview)
vsb.grid(row=1, column=0, sticky='nsew')
tree.configure(xscrollcommand=vsb.set)
vsb = ttk.Scrollbar(win, orient='vertical', command=tree.yview)
vsb.grid(row=0, column=1, sticky='nsew')
tree.configure(yscrollcommand=vsb.set)
btn_frame = tk.Frame(win)
btn_frame.grid(row=2, column=0, columnspan=2, sticky='ew')
btn_frame.columnconfigure(0, weight=1)
btn_frame.columnconfigure(1, weight=1)
tk.Button(btn_frame, text='Add Row!', command=top_for_new_row).grid(row=0, column=0, sticky='ew')
tk.Button(btn_frame, text='Remove Row!', command=remove_row_from_list).grid(row=0, column=1, sticky='ew')
columns = ('1', '2')
tree['columns'] = columns
tree['show'] = 'headings'
tree.column('1', width=200, stretch=True, anchor='w')
tree.column('2', width=100, anchor='w')
tree.heading('1', text='Col 1')
tree.heading('2', text='Col 2')
clear_and_load(track_data)
update_text()
win.mainloop()
Results:
Adding new line:
Removing lines:

Categories