Setting Tkinter/ttk Frame background color - python

I'm trying to change the background color of a ttk frame and I've looked up other examples, but none have seemed to work. This is my code so far:
from Tkinter import *
import ttk
p = Tk()
p.geometry('600x350')
p.configure(bg='#334353')
gui_style = ttk.Style()
gui_style.configure('My.TButton', foreground='#334353')
gui_style.configure('My.TFrame', background='#334353')
frame = ttk.Frame(p, style='My.TFrame')
frame.grid(column=1, row=1)
ttk.Button(frame, text='test', style='My.TButton').grid(column=0, row=0)
ttk.Button(frame, text='Test 2', style='My.TButton').grid(column=3, row=3)
p.mainloop()
The window has the background color that I want, but the frame still has the default gray background. Is there something i need to add differently? I want the entire window except for the buttons to be the color #334353. How do I do this?
EDIT: I've attached what my window looks like. I don't want the gray. :/ (Note. I don't have enough rep to post images apparently, so here is a link to imgur with my current window: http://imgur.com/KyhbdMB

Your frame is only sized to the minimum size required to hold the two child windows (the buttons). It seems like you want the frame to fill the main window. When you grid the frame you should add the sticky option to have it expand to fill the available space (eg: frame.grid(column=1,row=1,sticky='news')). Then you need to have the parent allocate all the space space to this grid cell. For that you want to use the grid_rowconfigure and grid_columnconfigure methods for the parent window. In this case:
p.grid_columnconfigure(1, weight=1)
p.grid_rowconfigure(1, weight=1)
which tells the main frame grid geometry manager that spare space should be given to the cell and row 1 column 1. This will lead to your frame expanding to fill the window.

It works on my PC!
Try this:
Update your Python environment(Tested under Py 3.4 Windows 32bit)
Install the lastest TTK package

Related

Adaptive resizing for a tkinter Text widget

I have been attempting to create a application that contains two Text() widgets, both of which can dynamically resize when the window size is changed. Before I have always used the root.pack() manager, with fill='both' and expand=True.
While this works for LabelFrames and most other widgets, it does not work when a Text widget is resized smaller then its original dimensions. Is there a way to have dynamically resizing Text widgets?
Ex.
import tkinter as tk
window = tk.Tk()
editor = tk.Text(bg='red')
editor.pack(side='top',fill='both',expand=True)
output = tk.Text(bg='green')
output.pack(side='top',fill='both',expand=True)
window.mainloop()
Tkinter will try to honor the requested size of a text widget. Since you didn't specify a size, the text widget will request a size of 80x24. When you resize the window smaller, pack tries to make room for everything at its requested size, and it does so in stacking order.
As the window shrinks, there's room for all of the first text widget but not enough for both. Because there's not enough room, it has to subtract space from the remaining widgets. Thus, it starts chopping off the last text widget.
To combat this, you can set the requested size of the text widgets to a small value that will fit in almost any window size, and then force them to grow by setting the size of the window as a whole. This way, pack will first allocate enough space for each small window, and then expand them equally when there's extra space.
For example:
import tkinter as tk
window = tk.Tk()
window.geometry("400x400")
editor = tk.Text(bg='red', width=1, height=1)
output = tk.Text(bg='green', width=1, height=1)
editor.pack(side='top',fill='both',expand=True)
output.pack(side='top',fill='both',expand=True)
window.mainloop()
The other solution is to use grid which lets you specify that rows and columns should be of uniform size.
import tkinter as tk
window = tk.Tk()
editor = tk.Text(bg='red')
output = tk.Text(bg='green')
editor.grid(row=0, column=0, sticky="nsew")
output.grid(row=1, column=0, sticky="nsew")
window.grid_columnconfigure(0, weight=1)
window.grid_rowconfigure((0,1), weight=1, uniform=1)
window.mainloop()

condensing/reducing vertical spacing between widgets in tkinter (Python) (AKA: setting vertical line/text spacing/padding)

Here's my program:
import tkinter as tk
#Create main window object
root = tk.Tk()
#build GUI
for i in range(5):
tk.Label(root, text="hello", height=0).grid(row=i)
#mainloop
root.mainloop()
It produces the following (running in Xubuntu 16.04 LTS)
Notice all that extra vertical space between the lines of text. I don't want that! How do I decrease it?
If I run this code instead:
import tkinter as tk
#Create main window object
root = tk.Tk()
#build GUI
for i in range(5):
tk.Label(root, text="hello", height=0).grid(row=i)
tk.Grid.rowconfigure(root, i, weight=1) #allow vertical compression/expansion to fit window size after manual resizing
#mainloop
root.mainloop()
...it opens up and initially looks exactly the same as before, but now I can manually drag the box vertically to shrink it, like so:
Notice how much more vertically-compressed it is! But, how do I do this programatically, so I don't have to manually drag it to make it this way? I want to set this tight vertical spacing from the start, but no matter which parameters I change in Label, grid, or rowconfigure I can't seem to make it work without me manually dragging the box with the mouse to resize and vertically compress the text.
There are many ways to affect vertical spacing.
When you use grid or pack there are options for padding (eg: pady, ipady, minsize). Also, the widget itself has many options which control its appearance. For example, in the case of a label you can set the borderwidth, highlightthickness and pady values to zero in order to make the widget less tall.
Different systems have different default values for these various options, and for some of the options the default is something bigger than zero. When trying to configure the visual aspects of your GUI, the first step is to read the documentation, and look for options that affect the visual appearance. Then, you can start experimenting with them to see which ones give you the look that you desire.
In your specific case, this is about the most compact you can get:
label = tk.Label(root, highlightthickness=0, borderwidth=0, pady=0, text="hello")
label.grid(row=i, pady=0, ipady=0)
You can programatically modify the geometry just before starting the main loop instead of manually dragging it (change 0.6 to whatever % reduction you want):
import tkinter as tk
#Create main window object
root = tk.Tk()
#build GUI
for i in range(5):
label = tk.Label(root, text = 'hello')
label.grid(row=i)
tk.Grid.rowconfigure(root, i, weight=1) #allow vertical compression/expansion to fit window size after manual resizing
#mainloop
root.update()
root.geometry("{}x{}".format(root.winfo_width(), int(0.6*root.winfo_height())))
root.mainloop()
Here is a screenshot of the result running on Xubuntu 16.04 LTS with Python 3.5.2:

tkinter's .pack_propagate() method

I am experimenting with Tkinter, as I was trying to figure out is there a way to set the tkinter's window size without using canvas. I came upon this how to set frame size question on SO's Question & Answer. So I went ahead and test it by writing a very small program to display a text label. But I found out it is "missing", or disappear when I use frame.pack_propagate(0)
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root, width=400, height=400)
# Does not work at the moment, textBox is missing
# frame.pack_propagate(0)
frame.pack()
textBox = tk.Label(frame, text="(x,y): ")
textBox.pack()
root.mainloop()
So my question is, can you explain why my textBox (Label) is not appearing when I use the frame.pack_propagate(0) instead of frame.pack() method? And secondly, is there a way to set the window size without using a canvas? I want to know because I am writing a series of small programs to teach my friend about tkinter, before introducing canvas to him. It would be nice if the window size are all the same across my tkinter samples. And I am just wondering as well (curious). Thank you very much.
I am using python 3.2.2 on MAC OS 10.5.8.
pack_propagate only sets a flag, it doesn't cause the frame to be placed in the widget. It is not a substitute for calling pack.
In other words you must do this:
# put the frame in its parent
frame.pack()
# tell frame not to let its children control its size
frame.pack_propagate(0)
# put the textbox in the frame
textBox.pack()
To answer your second question: Yeah, there is a way.
tkinters Tk do have the Tk.geometry function. When you just call it without arguments, you will get the current geometry in form of 'widthxheight+x+y', so for example (on Windows 10) '200x200+26+26' when you create your first Tk window. Using that format you can resize the Tk by, e.g., writing: root.geometry('400x500+60+60') to set the width to 400, the height to 500 and place it at the coordinates (60|60).
This works for Tk alswell as for Toplevel. But Toplevel also takes the arguments height and width when initialized or configured. If you want them to keep their size when packing something inside just use root.pack_propagate(False) on them.
By the way there is something similar for the grid manager: root.grid_propagate(False)

Stretching frames using grid layout in Python Tkinter

I'm trying to get stretching to work using Python 2.6.7 with Tkinter. I'd expect the below code to stretch the first button to the width of the second, but both buttons are only as wide as they need to be to fit their text.
#!/usr/bin/python
from Tkinter import *
win = Frame()
win.grid(sticky=N+S+E+W)
inner_a = Frame(win)
inner_a.grid(row=0, column=0, sticky=N+E+W)
inner_b = Frame(win)
inner_b.grid(row=1, column=0, sticky=S+E+W)
Button(inner_a, text='1').grid(row=0, column=0, sticky=E+W)
Button(inner_b, text='Some long text').grid(row=0, column=0, sticky=E+W)
win.mainloop()
By my understanding, the single column in win will expand to the width of the largest thing it contains, ie the width of inner_b, and then the width of inner_a, and thence of the first button, will be that of the second button.
Actually, what happens is the below; the first button is only wide enough to contain "1", not as wide as the second button.
What do I need to do to get the first button to expand the size of the second?
If you want widgets to line up in a grid, the first thing to do is make sure they have the same parent. This isn't strictly necessary if all the widgets are the same size or you are only using one column.
Another thing you need to do is give your column a weight. What is happening in your code is that the widget is expanding to fill the column, but the column isn't expanding to fill the master. If you give it a weight of 1 it will. You ned to do something like inner_a.columnconfigure(1, weight=1), and then do likewise for inner_b.

Frame resizing issue in Tk/ttk python

I'm creating a GUI using Tkinter/ttk in Python 2.7, and I'm having an issue where a Frame will resize itself once a widget is placed inside of it. I am new to Python and haven't used Tkinter before.
example:
ROOT = Tk()
FRAME = ttk.Frame(ROOT, width=300, height=300, relief='groove')
FRAME.grid()
ROOT.mainloop()
will produce a grooved frame 300x300, if i place a widget inside it like so:
ROOT = Tk()
FRAME = ttk.Frame(ROOT, width=300, height=300, relief='groove')
BUTTON = ttk.Button(FRAME, text="DON'T READ THIS TEXT")
FRAME.grid()
BUTTON.grid()
ROOT.mainloop()
the frame will shrink down to fit the button. Any way to force the frame not to resize?
To force the frame to keep its original dimensions turn "geometry propagation" off. In your case you would call FRAME.grid_propagate(False).
Speaking as someone with over 15 years of Tk experience, might I suggest that you almost certainly don't need this feature. Tk's ability to "shrink to fit" is really great, and makes it really easy to create GUIs with proper resize behavior. Once you start turning geometry propagation off you'll find you'll either have GUIs with bad resize behavior, or you'll spend a lot of time tweaking sizes and widget placement.
if you want to add padding, then use widg.grid(ipadx=..., ipady=..., padx=..., pady=...)
otherwise, you will need to add more context on what layout you're trying to achieve
This works for me:
app = Application()
app.master.title('Example')
app.master.geometry('640x480')
app.mainloop()
Apllication is Frame in my case with grid layout. It resizes to size of master window, so we need to change size of master window.
This should do the job: ROOT.geometry("640x480")

Categories

Resources