float number in button size in tkinter - python

When the following code is executed, it gives an error that float numbers are not acceptable. So how do you adjust the height and width with float numbers?
win = Tk()
btn = Button(win, width = 3.5 , height = 4.5)
btn.grid()
win.mainloop()

I am not sure if you want to specify the size of the button in pixels, or specifically in a rational amount of characters, but in the first case skip to the second part of the answer.
In both cases you will have to set the size in pixels, if you want to specify it in a rational amount of characters, you will first have to get the size of these characters:
import tkinter as tk
import tkinter.font as font
win = tk.Tk()
defaultfont = font.nametofont("TkDefaultFont")
width = defaultfont.measure("k") # has character length, not sure if this works on any platform
height = defaultfont.metrics()['linespace']
w = int(3.5*width)
h = int(4.5*height)
Then you have to set the button size in pixels, you have 2 possibilities:
Set a button image, and specify the size in pixels (like Matiiss proposed)
Put a frame with a specific size and make the button span the frame
Like this:
pixelVirtual = tk.PhotoImage(width=1, height=1)
btn = tk.Button(win, text='kkkk', image=pixelVirtual, width=w , height=h, compound="c")
btn.grid(row=0, column=1)
fr = tk.Frame(win, width=w, height=h, borderwidth=0, highlightthickness=0 )
fr.propagate(False)
fr.grid(row=1, column=1, sticky="nsew")
btn2 = tk.Button(fr, text='kkkk')
btn2.pack(expand=True, fill="both")
The result will not be entirely the same as there is some padding when using character sizes with the button (you can see this by making a button of integer sizes with this method compared to making the button with integer sizes in the default character based method.)

Related

How to make a floating Frame in tkinter

For my tkinter app I want to make a frame that would on top of other widgets, not taking any space (like html position fixed).
The Frame will have to contain widgets, if Frame is not possible labels or buttons will do.
I have no idea how to do it so haven't tried anything yet. Please help!
Here is a demonstration of place manager.
Remarks in the code explain behaviour.
import tkinter as tk
root = tk.Tk()
root.geometry("868x131")
button = tk.Button(root, text = "A Button")
button.place(x = 2, y = 2)
Frame = tk.LabelFrame(root, text = "A LabelFrame using place", bg="cyan")
# Frame using place with x, y, width, height in absolute coordinates
Frame.place(x = 250, y = 20, width = 600, height = 70)
ButtonInFrame = tk.Button(Frame, text = "A Button IN LabelFrame", bg="white")
# Note: place x,y is referenced to the container (Frame)
# Note: place uses just x,y and allows Button to determine width and height
ButtonInFrame.place(x = 2, y = 2)
Label = tk.Label(root, text = "A Label on LabelFrame\nWith multiple lines\nOf Text.", bg="light green")
# Label sits on top of buttonFrame and Frame
# Note place uses just x,y and allows Label to determine width and height
Label.place(x = 330, y = 60)
root.mainloop()

How to change the window size after the program starts?

I want that after starting the program, it checks its actual size and when "window.winfo_width > 520" it changes its size to 520. Is it possible?
from tkinter import *
from time import *
def upd():
tstr = strftime("%I:%M:%S %p")
ltime.config(text=tstr)
lday.config(text=strftime("%A"))
dtstr = strftime("%B %d, %Y")
ldate.config(text=dtstr)
ltime.after(1000, upd)
window = Tk()
window.config(bg='black')
window.title("Clock")
window.geometry('520x300')
window.resizable(0,1)
ltime = Label(window, font="Adobe 60", text='', fg='green', bg='black')
ltime.pack()
lday = Label(window, font="Adobe 60", text='', fg='green', bg='black')
lday.pack()
ldate = Label(window, font="Adobe 60", text='', fg='green', bg='black')
ldate.pack()
upd()
window.mainloop()
Firstly, window.winfo_geometry() will give you the geometry of the window.
For getting width only, use window.winfo_width().
For getting height only, use window.winfo_height()
e.g.
if window.winfo_width() > 520 :
window.geometry('520x300')
Now, since you are using window.resizable(0,1), you are disabling the window's width to be resized, so windows's width will be fixed to 520.
So, how will it exceed 520, if it's not resizable?
If you enable it, and then want window's width to not exceed 520:
Tkinter offers .maxsize() and .minsize() to fix the size of the root window.
e.g. if you want that the window's width to not exceed 520, then just set the width in maxsize as 520.
window.geometry('520x300')
window.maxsize(520, 300)
The window's width will not be resized more than 520 now.
In case, you want to resize according to the user's screen's size:
To get the screen size, you can use winfo_screenwidth() which returns the screen width and winfo_screenheight() for the height of the screen in pixels.
window.geometry() can be recalled if needed.
EDIT: An example of recalling window.geometry() after the start of program(, even after resizing is disabled horizontally):
def resizeScreen():
window.geometry('520x300')
b = Button(window, text="RESIZE", command=resizeScreen)
b.pack()
Whenever this button is clicked, the screen will be resized, from what it is currently.
You can call this same function without using a button, by just calling the function. It depends on your code, how you use it.

How to add a specific spacing in pixels between tkinter buttons?

I have written some code for some buttons. However, I am not sure how to add a specific number of pixels of spacing for each button. So far is the code I have written. However, I have not yet figured out a reliable way to add spacing between the buttons in pixel sizes.
import tkinter as tk
#from tkinter import PhotoImage
def banana():
print ("Sundae")
def tomato():
print ("Ketchup")
def potato():
print ("Potato chips")
root = tk.Tk()
root.geometry("960x600")
f1 = tk.Frame(root, width=70, height=30)
f1.grid(row=3, column=0, sticky="we")
button_qwer = tk.Button(f1, text="Banana", command=banana)
button_asdf = tk.Button(f1, text="Tomato", command=tomato)
button_zxcv = tk.Button(f1, text="Potato", command=potato)
button_qwer.grid(row=0, column=0)
button_asdf.grid(row=0, column=1)
button_zxcv.grid(row=0, column=2)
root.mainloop()
Adding space between widgets depends on how you are putting the widgets in the window. Since you are using grid, one simple solution is to leave empty columns between the buttons, and then give these columns a minsize equal to the space you want.
Example:
f1.grid_columnconfigure((1, 3), minsize=10, weight=0)
button_qwer.grid(row=0, column=0)
button_asdf.grid(row=0, column=2)
button_zxcv.grid(row=0, column=4)
Using a specific number of pixels of spacing between each Buttondoesn't sound to me like such as good idea because it isn't very flexible nor easily portable to devices with different resolutions.
Nevertheless I've figured-out a way of doing it—namely by putting a do-nothing invisible button between of the each real ones. This got somewhat involved, mostly because it requires putting an image on each Button used this way so its width option argument will be interpreted as number of pixels instead of number of characters (here's some documentation describing the various Button widget configuration options).
import tkinter as tk
# Inline XBM format data for a 1x1 pixel image.
BITMAP = """
#define im_width 1
#define im_height 1
static char im_bits[] = {
0x00
};
"""
root = tk.Tk()
root.geometry("960x600")
bitmap = tk.BitmapImage(data=BITMAP, maskdata=BITMAP)
f1 = tk.Frame(root, width=70, height=30)
f1.grid(row=3, column=0, sticky=tk.EW)
def banana():
print ("Sundae")
def tomato():
print ("Ketchup")
def potato():
print ("Potato chips")
def layout_buttons(parent, buttons, spacing):
if buttons:
first, *rest = buttons
first.grid(row=0, column=0) # Position first Button.
for index, button in enumerate(rest, start=1):
col = 2*index
# Dummy widget to separate each button from the one before it.
separator = tk.Button(parent, relief=tk.FLAT, state=tk.ACTIVE,
image=bitmap, borderwidth=0, highlightthickness=0,
width=spacing)
separator.grid(row=0, column=col-1)
button.grid(row=0, column=col)
buttons = (
tk.Button(f1, text="Banana", command=banana),
tk.Button(f1, text="Tomato", command=tomato),
tk.Button(f1, text="Potato", command=potato),
)
layout_buttons(f1, buttons, 30)
root.mainloop()
Result:
Here's a blow-up showing that the spacing is exactly 30 pixels (as counted in my image editor and indicated by the thin horizontal black line between the adjacent edges of the two Buttons).

borders in tkinter not getting displayed

I am writing a small program in python to get a grid like effect wherin each grid is a label.
below is my code:
from Tkinter import *
root = Tk()
root.configure(bg='black')
FRAME = Frame( bg='red')
FRAME.pack()
heading=["source","name","code","identity","source","name","code","identity"]
r1=[1,2,3,4,5,6,7,8]
r=0
c=0
for k in range(0,5):
for i in heading:
lab = Label (FRAME,text = i,bg='cyan',padx='3.0m', pady='3.0m')
lab.grid(row=r,column=c)
c = c+1
r=r+1
c=0
root.mainloop()
I cannot get the border of labels set. Please specify which attribute I should incorporate, I've tried using border width without success.
You say you can't get the border of the labels to be set, but I don't see anywhere in your code where you've defined a border for the labels.
If you want borders on the actual label widgets, use the borderwidth and relief options:
lab = Label (..., borderwidth=1, relief="solid"
If, instead of a border on the widget, you want a space between each widget so that the background shows through, use the padx and pady options when you use grid:
lab.grid(..., padx=1, pady=1)

Keeping the background colour when adding buttons to a grid/frame

The problem I'm having is keeping my background colour when adding buttons to a frame, as soon as I run the module the background colour disappears, any help will be appreciated, thanks.
Heres me code:
import tkinter
import tkinter as tk
root = tk.Tk()
root.geometry('1000x600')
var=tk.StringVar()
Frame1 = tk.Frame(root)
Frame1.configure(background='light blue',height='300',width='500')
Frame1.grid(row='0',column='0')
Frame2 = tk.Frame(root)
Frame2.configure(background='grey',height='300',width='500')
Frame2.grid(row='0',column='1')
Frame3 = tk.Frame(root)
Frame3.configure(background='grey',height='300',width='500')
Frame3.grid(row='1',column='0')
Frame4 = tk.Frame(root)
Frame4.configure(background='light blue',height='300',width='500')
Frame4.grid(row='1',column='1')
def PrintOrder():
LabelOrder = tk.Label(Frame3,text="DONUT ORDER")
LabelOrder.grid(row='0',column='0')
return
Button1 = tk.Button(Frame1,text="Apple Cinnamon",height='2',width='15',command=PrintOrder).grid(row='0',column='0')
Button2 = tk.Button(Frame1,text="Strawberry",height='2',width='15',command=PrintOrder).grid(row='0',column='1')
Button3 = tk.Button(Frame1,text="Custard",height='2',width='15',command=PrintOrder).grid(row='0',column='2')
Button4 = tk.Button(Frame1,text="Sugar Ring",height='2',width='15',command=PrintOrder).grid(row='1',column='0')
Button5 = tk.Button(Frame1,text="Chocolate Caramel",height='2',width='15',command=PrintOrder).grid(row='1',column='1')
Button6 = tk.Button(Frame1,text="Lemon Circle",height='2',width='15',command=PrintOrder).grid(row='1',column='2')
Button7 = tk.Button(Frame1,text="Blueberry Blaster",height='2',width='15',command=PrintOrder).grid(row='2',column='0')
Button8 = tk.Button(Frame1,text="Strawberry Surprise",height='2',width='15',command=PrintOrder).grid(row='2',column='1')
Button9 = tk.Button(Frame1,text="Simple Sugar",height='2',width='15',command=PrintOrder).grid(row='2',column='2')
Label1 = tk.Label(Frame2,text="Donut special 6 for the price of 5").grid(row='0',column='0')
Button10 = tk.Button(Frame2,text="SPECIAL",height='5',width='20').grid(row='1',column='0')
root.mainloop()
Your frame still has its background color. You can see this pretty easily if you give it a distinct color so that it will show (eg: "red"), and add padding between the buttons (eg: tk.Button(...).grid(..., padx=10, pady=10). I think the only thing that is happening is that there is no space between the buttons for the color to show through, and the default behavior is for the frame to shrink (or grow) to fit its contents.
Other problems include the fact that you aren't giving any rows or columns a weight, so they won't grow or shrink as the main window grows an shrinks. Also, you don't have the sticky attribute set for the frames, so they won't fill the grid cell that they occupy. Add sticky="nsew" to where you grid the frames and you'll likely see more color.
A rule of thumb when using grid is to always set the sticky attribute for each item, and to give at least one row and one column a weight of 1 (one).
You can use grid_propagate(0) on your frames. With this, the frame's size is not adjusted to the widgets' size.
On your code, I used the next line to keep the size of Frame1:
Frame1.grid_propagate(0)
You can check this:
http://effbot.org/tkinterbook/grid.htm#Tkinter.Grid.grid_propagate-method
grid_propagate(flag) [#]
Enables or disables geometry propagation. When enabled, a grid manager connected to this widget attempts to change the size of the widget whenever a child widget changes size. Propagation is always enabled by default.
flag
True to enable propagation.

Categories