I combined a scrollbar with a ttk notebook by adapting this example and additionally porting it to Python3.
I am using ttk widgets as often as possible to get a more 'modern' UI. However, there is no ttk canvas widget, so I used the standard tkinter canvas. By default the backgroundcolor of the canvas widget seems to be white (at least on Mac OS), while the default background of all ttk widgets is grey (see screenshot below).
How can I get the default background of the ttk frame containing the label widgets, so that I can pass it to the canvas by Canvas(parent, background='bg_color') and remove the white space around the ttk frame containing the ttk labels?
I know that ttk uses styles to define the look of the widgets. However, I have no idea about how to read the background value of the (default) style.
#!/usr/bin/env python3
# coding: utf-8
from tkinter import *
from tkinter import ttk
master = Tk()
def populate():
"""Put in some fake data"""
for row in range(100):
ttk.Label(frame, text="%s" % row, width=3, borderwidth="1", relief="solid").grid(row=row, column=0)
t="this is the second column for row %s" % row
ttk.Label(frame, text=t).grid(row=row, column=1)
def OnFrameConfigure(event):
"""Reset the scroll region to encompass the inner frame"""
canvas.configure(scrollregion=canvas.bbox("all"))
nbook = ttk.Notebook(master)
nbook.grid()
tab = Frame(nbook)
canvas = Canvas(tab, borderwidth=0)
frame = ttk.Frame(canvas)
vsb = Scrollbar(tab, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
canvas.pack(side='left', fill='both', expand=True)
canvas.create_window((0, 0), window=frame, anchor='nw', tags='frame')
frame.bind("<Configure>", OnFrameConfigure)
populate()
nbook.add(tab, text='Test')
master.mainloop()
You can use s.lookup() to get settings of a style. If you want the background setting of your ttk Frame use:
s = ttk.Style()
bg = s.lookup('TFrame', 'background')
Which you can apply to your Canvas:
canvas = Canvas(tab, borderwidth=0, background=bg)
I came across, a similar problem. Setting the background colour to 'systemWindowBody' does not do anything.
Upon searching further, I came across this website with tkinter colors (http://www.science.smith.edu/dftwiki/index.php/Color_Charts_for_TKinter)
I found the closest match to Mac OS grey as 'gray93'. Maybe that helps you achieve the same colour.
For anyone curious about how to just get the color of a frame (or widget), you can use the following line
widget.cget('bg')
where widget is the widget from which you want to retrieve the background color.
Thus, for a frame you can do:
your_frame.cget('bg')
Related
Python beginner. I placed a scrollbar widget in window and that works, but no matter what I do I can't get the scrollbox widget to change size. Could go with a larger scrollbox or for it to resize when the window resizes, but can't figure out how to force either to happen. Tried lots of different solutions, but feels like the grid and canvas are defaulting to a size and can't figure out how to change that. Help would be appreciated. Code is below:
import tkinter as tk
from tkinter import ttk
import os
import subprocess
class Scrollable(tk.Frame):
"""
Make a frame scrollable with scrollbar on the right.
After adding or removing widgets to the scrollable frame,
call the update() method to refresh the scrollable area.
"""
def __init__(self, frame, width=16):
scrollbar = tk.Scrollbar(frame, width=width)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y, expand=True)
self.canvas = tk.Canvas(frame, yscrollcommand=scrollbar.set)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.config(command=self.canvas.yview)
self.canvas.bind('<Configure>', self.__fill_canvas)
# base class initialization
tk.Frame.__init__(self, frame)
# assign this obj (the inner frame) to the windows item of the canvas
self.windows_item = self.canvas.create_window(0,0, window=self, anchor=tk.NW)
def __fill_canvas(self, event):
"Enlarge the windows item to the canvas width"
canvas_width = event.width
self.canvas.itemconfig(self.windows_item, width = canvas_width)
def update(self):
"Update the canvas and the scrollregion"
self.update_idletasks()
self.canvas.config(scrollregion=self.canvas.bbox(self.windows_item))
root = tk.Tk()
root.title("application")
root.geometry('750x800')
dbEnvs = ['a','b']
x = 1
header = ttk.Frame(root)
body = ttk.Frame(root)
footer = ttk.Frame(root)
header.pack(side = "top")
body.pack()
footer.pack(side = "top")
#setup Environment selection
envLabel = tk.Label(header, text="Environment:")
envLabel.grid(row=0,column=0,sticky='nw')
dbselection = tk.StringVar()
scrollable_body = Scrollable(body, width=20)
x = 1
for row in range(50):
checkboxVar = tk.IntVar()
checkbox = ttk.Checkbutton(scrollable_body, text=row, variable=checkboxVar)
checkbox.var = checkboxVar # SAVE VARIABLE
checkbox.grid(row=x, column=1, sticky='w')
x += 1
scrollable_body.update()
#setup buttons on the bottom
pullBtn = tk.Button(footer, text='Pull')
pullBtn.grid(row=x, column=2, sticky='ew')
buildBtn = tk.Button(footer, text='Build')
buildBtn.grid(row=x, column=3, sticky='ew')
compBtn = tk.Button(footer, text='Compare')
compBtn.grid(row=x, column=4, sticky='ew')
root.mainloop()
have tried anchoring, changing the window base size and multiple other things (8 or 19 different items, plus reading lots of posts), but they normally involve packing and since I used grids that normally and ends with more frustration and nothing changed.
If you want the whole scrollbox to expand to fill the body frame, you must instruct pack to do that using the expand and fill options:
body.pack(side="top", fill="both", expand=True)
Another problem is that you're setting expand to True for the scrollbar. That's probably not something you want to do since it means the skinny scrollbar will be allocated more space than is needed. So, remove that attribute or set it to False.
scrollbar.pack(side=tk.RIGHT, fill=tk.Y, expand=False)
tip: when debugging layout problems, the problems are easier to visualize when you temporarily give each widget a unique color. For example, set the canvas to one color, body to another, the instance of Scrollable to another, etc. This will let you see which parts are visible, which are growing or shrinking, which are inside the borders of others, etc.
I'm working on a GUI with tkinter and i have a problem.
When i add a scrollbar to my app, the frame on my canvas overlaps the outlines (see image)
Here is the code:
from tkinter import *
window = Tk()
window.geometry("400x225")
scrollbar1 = Scrollbar(window, orient=VERTICAL)
canvas1 = Canvas(window, bg="#003333", yscrollcommand=scrollbar1.set)
frame1 = Frame(canvas1, bg="#003333")
scrollbar1.config(command=canvas1.yview)
scrollbar1.pack(side=RIGHT, fill=Y)
canvas1.pack(fill=BOTH, expand=True)
canvas1.create_window((0, 0), window=frame1, anchor="nw")
for x in range(20):
string = "line " + str(x)
label1 = Label(frame1, fg="white", bg="#003333", text=string, font=("Calibri Bold", 14))
label1.pack(pady=5)
window.update()
canvas1.config(scrollregion=canvas1.bbox("all"))
window.mainloop()
I don't know if it's possible but i want the frame to fit within the canvas and keeping the outlines as well.
I hope you get my problem and can probably help me out! Thanks in advance.
The highlightthickness
Specifies a non-negative value indicating the width of the highlight rectangle to draw around the outside of the widget when it has the input focus.
So, this is not really the "border" that you want. It is a part of the drawing space within the canvas, when you use window_create to draw a window, the parent of that window is the canvas, which begins before the highlight and so the window slides over it.
A solution, as also suggested by #martineau would be to make this 0 by specifying highlightthickness=0 and as you suggested that you need the "border" around the whole thing, you can either create a container frame and specify the bd parameter, or just set the bd of the window window.config(bd=2).
I'm trying to center a Label in Tkinter. I am using the grid() method to show my widgets.
In the code below I created a frame and put the label in that frame.
The sticky method doesn't seem to work for some reason.
I have tried sticky='WE' but the label is still stuck to the left side of the frame.
'''
from tkinter import *
from tkinter import ttk
mw=Tk()
mw.title('Window')
mw_width, mw_height = 300, 200
mw.minsize(mw_width,mw_height)
frame1=Frame(mw)
frame1.grid(row=0,column=0)
main_label=ttk.Label(frame1,text='My label')
main_label.grid(row=0,column=0,sticky='WE')
mw.mainloop()
'''
grid by default will center a widget in the space allocated to it. The problem is that you've not configured the column to grow to fill the window. Also by default, a row or column will be just large enough to fit the widgets in it.
To do that, you need to give column 0 a non-zero weight:
mw.grid_columnconfigure(0, weight=1)
Do you need to use the grid() method for your layout? pack() would automatically center it.
from tkinter import *
from tkinter import ttk
mw=Tk()
mw.title('Window')
mw_width, mw_height = 300, 200
mw.minsize(mw_width,mw_height)
frame1=Frame(mw)
frame1.pack()
main_label=ttk.Label(frame1,text='My label', justify="center")
main_label.pack()
mw.mainloop()
You could also use place().
from tkinter import *
from tkinter import ttk
mw = Tk()
mw.title('Window')
mw_width, mw_height = 300, 200
mw.minsize(mw_width, mw_height)
frame1 = Frame(mw)
frame1.place(width=mw_width, height=mw_height)
main_label = ttk.Label(frame1, text='My label', justify="center")
main_label.place(relx=.5, rely=0, anchor="n")
mw.mainloop()
I was making a treeview for my tkinter program, and when I insert the data into it, there are some rows with too many characters and some of them are unseen.
Is there any way to set an auto line feed for the row height? I don't want to add a horizontal scrollbar to solve this. This means when the width of a strings is bigger then th columns with a newline should be iserted (automaticly). In that case the height of the row should be adjusted (automaicly) in that case to display more then one line.
from tkinter import *
import tkinter.ttk as ttk
root = Tk()
frame = Frame(root)
frame.pack()
tree = ttk.Treeview(frame, height=15,
columns=('c1', 'c2'),
show="headings")
tree.column('c1', width=100, anchor='center')
tree.column('c2', width=200, anchor='center')
tree.heading('c1', text='text1')
tree.heading('c2', text='text2')
tree.pack()
tree.insert('', 1, values=['edew', 'dewd'])
tree.insert('', 2, values=['abcd'*10, 'wxyz'*10])
root.resizable(width=False, height=False)
root.mainloop()
The height of alls row can be modified with this code.
root = Tk()
style = ttk.Style(root)
style.configure('Treeview', rowheight=45)
It would be very nice to know if each row in a Treeview can have it's own style to defined individual rowheight (depending on newline characters).
In response to Aleksandar Beat's question:
In order to change the style of a specific Treeview, you have to define a different identifier before the ttk widget name:
Style().configure('AnythingYouWantHere.Treeview', rowheight=45)
# The '.Treeview' can not be changed
However, if you do it this way, you have to specify the style option of the ttk widget.
Sample usage:
root = Tk()
Style().configure('MyStyle1.Treeview', rowheight=45)
styledTreeView = Treeview(root, style='MyStyle1.Treeview')
normalTreeView = Treeview(root) # Style will not automatically apply
This also applies to any ttk widgets.
I want to get a table, but when I realized, that there's no table in python, I decided to fix it with two listboxes.
The point is, that I don't want a border between them.
So my Question is: How to remove the border from the Tk Listbox in Python?
Even if it'll become white I had a solution...
You want to set the borderwidth to zero, but you also want to set highlightthickness to zero. Highlightthickness represents a rectangle that surrounds the whole widget when it has keyboard focus. Finally, when you use pack or grid, make sure you don't add any padding between them.
If you want to complete the illusion that the two widgets are one, put them in a frame and give the frame a borderwidth and relief.
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent, borderwidth=1, relief="sunken")
lb1 = tk.Listbox(self, borderwidth=0, highlightthickness=0)
lb2 = tk.Listbox(self, borderwidth=0, highlightthickness=0)
lb1.pack(side="left", fill="both", expand=True)
lb2.pack(side="left", fill="both", expand=True)
lb1.insert(0, "left")
lb2.insert(0, "right")
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True, padx=8, pady=8)
root.mainloop()
I think the best you can achieve would be this:
import tkinter as tk
root = tk.Tk()
wrapper = tk.Frame(root, bd=2, relief="sunken")
L1 = tk.Listbox(wrapper, bd=0)
L2 = tk.Listbox(wrapper, bd=0)
L1.grid(column = 1, row = 1)
L2.grid(column = 2, row = 1)
wrapper.pack()
root.mainloop()
note setting the border of each listbox to 0, (bd=0) and to give the overall widget a similar look to the original listbox i've wrapped it in a frame and given that the same border style as the default listbox.
also worth nothing that you need to get the bindings right to make them scroll as expected, just binding to the scroll wheel and scroll bar is insufficient as the lists can be moved with the arrow keys when an item is highlighted, like in the second example on this page:
scrolling multiple listboxes together
by Santiclause
Speicfy borderwidth as 0 when you create a listbox.
For example:
from Tkinter import * # from tkinter import * (Python 3.x)
root = Tk()
lb = Listbox(borderwidth=0) # <---
lb.pack()
root.mainloop()