Apologies if this was answered before but I cannot find anything out there with these specifics.
I'm finishing a query tool for the team I'm working with where they can choose the fields to query a database and a search a string (along with other options).
The result is being sent to a Tkinter TreeView widget (found this to be the best approach in terms of events control and visualization).
I have a main issue which is to constrain the size of the Treeview to a certain width, no matter how many fields the user chooses. Since the complete GUI is non scalable I want the Treeview to have a max size.
I have tried to include the width when defining the columns, but it seems to bypass that.
The Treeview is a child of a LabelFrame and the positioning is defined with grid.
Here a code sample of what I'm doing to set the TreeView (since this is a company application I have to be careful with disclosing some field names):
CoreQuery.groupResults = LabelFrame(CoreQuery.root, text="Query Result", padx=5, pady=5, height=470,width=960)
CoreQuery.tree = ttk.Treeview(CoreQuery.groupResults, selectmode='browse')
CoreQuery.tree.bind("<Double-1>", CoreQuery.OnTreeDoubleClick)
CoreQuery.scrollbar_horizontal = ttk.Scrollbar(CoreQuery.root, orient='horizontal', command=CoreQuery.tree.xview)
CoreQuery.scrollbar_vertical = ttk.Scrollbar(CoreQuery.root, orient='vertical', command=CoreQuery.tree.yview)
CoreQuery.tree.config(height=18, xscrollcommand=CoreQuery.scrollbar_horizontal.set, yscrollcommand=CoreQuery.scrollbar_vertical.set)
CoreQuery.tree.grid(row=0, sticky="w")
CoreQuery.scrollbar_horizontal.grid(row=1,column=0, sticky='ns')
CoreQuery.scrollbar_vertical.grid(row=0, column=2, sticky='ew')
CoreQuery.scrollbar_horizontal.configure(command=CoreQuery.tree.xview)
CoreQuery.scrollbar_vertical.configure(command=CoreQuery.tree.yview)
CoreQuery.tree.configure(yscroll=CoreQuery.scrollbar_vertical, xscroll=CoreQuery.scrollbar_horizontal)
The Following is the method that receives the SQL query result and places the data in the TreeView:
def ScreenPrintResults(header,rows):
columns=len(header)
minColumnsize=math.ceil(965/columns)
#Clear Treeview
CoreQuery.tree.delete(*CoreQuery.tree.get_children())
for values in rows:
values[0] = str(values[0]).replace(".0", "")
if (values[0].isdigit()):
values[0] = int(values[0])
auxCount=0
CoreQuery.tree['columns']=header
for value in header:
CoreQuery.tree.heading(value, text=str(value))
CoreQuery.tree.column(value, stretch=tk.NO)
for items in rows:
if auxCount==0:
CoreQuery.tree.column('#0', width=30, stretch=tk.NO)
else:
CoreQuery.tree.column(value, width=minColumnsize)
CoreQuery.tree.insert('',tk.END,auxCount+1,text=str(auxCount+1),
values=list(items))
auxCount=auxCount+1
CoreQuery.updateMessage.config(foreground="Black", font="Verdana 10 bold")
CoreQuery.message.set("...")
Is there any kind of limitation I can add the width of the TreeView so that it does not go beyond a certain width?
Or do I need to split the available width to the number of columns I get from the query?
I honestly don't care if all the information is on screen, hence I placed the scrollbars.
Here is a screenshots of the issue:
Thanks for the help!
EDIT: Changed the For loop code and added more clarifying screenshots of the issue
The example below demonstrate how to set width for the columns the minwith and maxwidth which takes integers. Also you can set height of the row using the rowheight attribute for your text to display well in the tree.
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
tree = ttk.Treeview(root, column=("col1","col2"), show="headings")
style = ttk.Style(root)
style.configure('my.Treeview', rowheight=50)
tree.configure(style='my.Treeview')
tree.heading("#0", text="COMPANY NAME")
tree.heading("#1", text="DEPARTMENT")
tree.heading("#2", text="BRANCH")
tree.column("#0", stretch=tk.NO, minwidth=100, width=000)
tree.column("#1", stretch=tk.NO, minwidth=100, width=400)
tree.column("#2", stretch=tk.NO, minwidth=100, width=400)
tree.insert("", tk.END, values=("Am using python version 3.6.1 \n on windows machine ", "This an example Tkinter Treeview in Python, which is from \nttk class make"))
tree.insert("", tk.END, values=("This an example Tkinter Treeview in Python, which is from \nttk class make sure #import ttk\n also from tkinter import *", "Apologies if this was answered before but I cannot find "))
tree.pack()
root.mainloop()
Related
I am using python 3.7 and tkinter.
I want to display text information which is periodically updated and I want to display it in a table format where each cell is of fixed width. Without going into too many specifics, I am using grid geometry manager. Unfortunately, the text is not displaying in the cells, and I don't know why.
I understand the construct of my "table"-like GUI is not as simple as it could be. I want to be able to easily hide/show rows, so I have given each row its own frame making it easy to show/hide any row. (I have also used a grid layout within each row as well, thinking that this will be the easiest way to eventually force the geometry manager to give me uniform/non-changing cell widths.)
My first problem is that no text is displaying in my labels in the "table".
I have verified via print('label text = ', label.cget("textvariable")), that the correct textvariable was in fact assigned to the label.
I have read many posts regarding "tkinter label text not showing" and nothing has helped.
Thank you for any insights. Here is the code:
driverList = ['bob','fred','ethel']
tracks = ['daytona', 'charlotte', 'atlanta', 'darlington','lasvegas','watkins','talladega']
dynamicAllDriverFlagReportDict = {'bob': ['bob','i','','RevDn','','','',''],
'fred': ['fred','In-Up','','','','RevUp','Ham',''],
'ethel': ['ethel','','RevDn','','','In-Dn','Sht','']
}
global driverFrameDict
driverFrameDict= dict()
# Set up the GUI
window = tk.Tk()
myDataFrame = tk.Frame(master=window)
myDataFrame.pack()
btnFrame= tk.Frame(master=window, bg="gray")
btnFrame.pack(side="bottom")
for driverIndex in range(len(driverList)):
myOneStockFrame = tk.Frame(master=myDataFrame, height=25, width=800)
for tfIndex in range(len(tracks)+1):
oneTFFrame = tk.Frame(master=myOneStockFrame,relief=tk.RAISED, borderwidth=1)#, height=25)#, width=8)
if tfIndex == 0:
label = tk.Label(master=oneTFFrame,text=driverList[driverIndex])#, height=15, width=8)
else:
print('driverIndex=', driverIndex, '; tfIndex=', tfIndex, 'dynamicAllDriverFlagReportDict=', dynamicAllDriverFlagReportDict[driverList[driverIndex]][tfIndex])
label = tk.Label(master=oneTFFrame,textvariable=dynamicAllDriverFlagReportDict[driverList[driverIndex]][tfIndex])#,height=20)#, width=8)
print('label text = ', label.cget("textvariable"))
label.pack()#padx=5,pady=5)
#label.pack_propagate(0)
oneTFFrame.grid(row=0,column=tfIndex)#, sticky= "nswe", padx=0, pady=0)
#oneTFFrame.grid_propagate(0)
myOneStockFrame.grid(row=driverIndex,column=0)#, sticky= "nswe", padx=0, pady=0)
#myOneStockFrame.grid_propagate(0)
driverFrameDict[driverList[driverIndex]] = myOneStockFrame
#print('driverFrameDict['+driverList[driverIndex]+'] = ', myOneStockFrame)
window.mainloop()
Here is the output:
Thank you.
textvariable has to be set to an instance of one of the special tkinter variables (StringVar, etc). You're passing a string, which causes a new internal variable to be created with a name that matches the string.
I want to have an entry and I want to have a listbox of fixed size under it which is fixed. and I want to have another listbox of dynamic height. That will appear and disappear in time and also change in size. I want the second listbox (which is actually a dropdown) to be shown over the other listbox which I want it to be fixed. My code for changing the size etc is correct and works perfectly with pack() but then it will move the other listbox up and down as it's size changes. And when I change pack() to place(...) it's not shown at all anymore.
Here is my code:
from tkinter import *
root = Tk()
entry = Entry(
root,
width=50
)
frame = Frame(
root,
height=10,
width=50,
background="#caeaa9"
)
dropdown = Listbox(
frame,
background="#11FF11",
height=5,
width=50
)
listbox = Listbox(
frame,
background="#FF1111",
height=10,
width=50
)
entry.pack()
listbox.pack()
dropdown.place()
frame.pack()
mainloop()
But the dropdown does not appear when I run it. What am I missing?
By the way, I want the top border of the dropdown to be exactly on the top border of the listbox and both of them right under the entry.
I highly recommend you don't use the place geometry manager. Ever. If you want to create larger or more complex interfaces, having to place widgets is terrible. I suggest using grid instead:
import tkinter as tk
# Avoid wildcard imports!
root = tk.Tk()
entry = tk.Entry(
root,
width=50
)
frame = tk.Frame(
root,
background="#caeaa9"
)
listbox = tk.Listbox(
frame,
background="#FF1111",
height=10,
width=50
)
dropdown = tk.Listbox(
frame,
background="#11FF11",
height=5,
width=50
)
entry.pack()
listbox.grid(column=0, row=0, sticky=N)
dropdown.grid(column=0, row=0, sticky=N)
frame.pack()
root.mainloop()
You have two problems:
you aren't telling place where to place the widget
the stacking order (z-index) of the dropdown is behind (lower) than the other listbox, so it will appear under the listbox.
give explicit coordinates to place
I'm not entirely clear where you want the dropdown to appear. Since you say "over" the other listbox, I suggest you use the in_ parameter to make the coordinates relative to the other listbox, and then use other place arguments to place it exactly where you want.
Example:
In the following example I make the dropdown exactly the same width, but half the height of the other listbox.
dropdown.place(in_=listbox, x=0, y=0, anchor="nw", relwidth=1.0, relheight=.5)
Fix the stacking order
All widgets have a stacking order. Some people call this a z-index. By default the order is the order in which the widgets are created. Since you create the dropdown before the other listbox, the other listbox has a higher stacking order. That means that it will appear above the dropdown.
A simple solution is to create the dropdown last. If you don't want to do that, you can call the lift method of the widget to raise its stacking order. The argument for lift is the name of a widget you want to be above.
Example:
dropdown.lift(listbox)
Tkinter place is one of the three geometry manager in tkinter. it allows us to specify the position of tkinter widgets in terms of x and y coordinate.
Here is the code example :
from tkinter import *
window = Tk()
Button(window,text = "Click Me").place(x = 50,y = 50)
window.mainloop()
For more info kindly refer this tutorial on tkinter place
In my code I have a spin box that the user uses to select the number of teams they want, then in a separate frame columns are created to match the number of teams they chose, and when they use the spin box to change this value it also changes the number of columns
frame=Frame(root)
frame.pack(anchor=CENTER)
Label(frame,text='Team selection').grid(row=0,column=0,columnspan=5)
NumTeams=StringVar()
Spinbox(frame,values=(1,2,3,4,5),textvariable=NumTeams).grid(row=2,column=0,columnspan=5)
frame2=Frame(root)
frame2.pack()
for i in range(int(NumTeams.get())):
Label(frame2,text=str(i)).grid(row=0,column=i)
frame2.update()
The above code is an attempt at achieving this, does anyone know a way that this can be done?
You can use the command argument to specify a method to be run whenever your spinbox changes values. I'm not entirely sure what type of columns you mean, but hopefully you can work with this.
from Tkinter import *
def on_spin():
num_teams.set("New text")
root = Tk()
frame = Frame(root)
frame.pack(anchor=CENTER)
frame2 = Frame(root)
frame2.pack()
num_teams = StringVar()
label = Label(frame2, textvariable=num_teams)
label.pack()
spin = Spinbox(frame, from_=1, to=5, command=on_spin)
spin.pack()
root.mainloop()
I am using a ttk.Treeview widget to display a list of Arabic books. Arabic is a right-to-left language, so the text should be aligned to the right.
The justify option that is available for Label and other ttk widgets does not seem to work for Treeview.
Does anyone know how to do this?
The ttk.Treeview widget has an anchor option you can set for each column. To set the anchor of a column to the right side use:
ttk.Treeview.column(column_id, anchor=Tkinter.E)
In addition to the #fhdrsdg answer, here you have a simple example of use:
# for python 3
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
def show_info():
messagebox.showinfo("More info", "First column represents the subject" \
" and the second represents its corresponding " \
"current number of tagged questions on Stack Overflow.")
root = tk.Tk()
tree = ttk.Treeview(root, columns=("Tags"), height=6)
subjects = {"Tkinter": "8,013",
"Python": "425,865",
"C++": "369,851",
"Java": "858,459"}
for subject in subjects.keys():
tree.insert("", "end", text=subject, values=(subjects[subject]))
tree.column("Tags", anchor="e")
tree.pack(fill="both", expand=True)
informer = tk.Button(root, text="More info", command=show_info)
informer.pack(side="bottom")
root.mainloop()
If you need more help on how to use ttk.Treeview widgets, have a look at this reference by The New Mexico Tech or this tutorial at TkDocs.
Question
I'm trying to make a 'ttk Label' which a) is 20 pixels high by 300 pixels wide, b) is scrollable (in this case horizontally), and c) uses the simplest code possible within reason (except for the fact that the text and scrollbar are both within a frame*). I've found stackoverflow to be helpful in describing the processes I need to go through (put the label in a frame, put the frame in a canvas, put the scroll bar next to or underneath the canvas and 'bind' them together somehow), but despite looking at a fair few docs and stackoverflow questions, I can't figure out why my code isn't working properly. Please could someone a) update the code so that it satisfies the conditions above, and b) let me know if I've done anything unnecessary? Thanks
*the frame will be going in a project of mine, with text that is relevant
Current code
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
myframe_outer = ttk.Frame(root)
mycanvas = tk.Canvas(myframe_outer, height=20, width=300)
myframe_inner = ttk.Frame(mycanvas)
myscroll = ttk.Scrollbar(myframe_outer, orient='horizontal', command=mycanvas.xview)
mycanvas.configure(xscrollcommand=myscroll.set)
myframe_outer.grid()
mycanvas.grid(row=1, sticky='nesw')
myscroll.grid(row=2, sticky='ew')
mycanvas.create_window(0, 0, window=myframe_inner, anchor='nw')
ttk.Label(myframe_inner, text='test ' * 30).grid(sticky='w')
root.mainloop()
Edit:
Current result
Answer
Use a readonly 'entry' widget - it looks the same as a label, and doesn't need to be put in a canvas.
Code
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
mytext = tk.StringVar(value='test ' * 30)
myframe = ttk.Frame(root)
myentry = ttk.Entry(myframe, textvariable=mytext, state='readonly')
myscroll = ttk.Scrollbar(myframe, orient='horizontal', command=myentry.xview)
myentry.config(xscrollcommand=myscroll.set)
myframe.grid()
myentry.grid(row=1, sticky='ew')
myscroll.grid(row=2, sticky='ew')
root.mainloop()
Result