Setting background as image using configuration - Tkinter [duplicate] - python

I'm trying to add a background image to my root window but it doesn't seem to be working for me. This is my code. I would like the background image to cover the whole window and place the labels on top of the background image.
from tkinter import *
from tkinter import messagebox
top = Tk()
textButton = Frame(top)
textButton.pack()
img = PhotoImage(file="bk.gif")
img = img.subsample(1, 1)
background = Label(top, image = img, bd=0)
background.pack()
background.image = img
name_label = Label(textButton, text="Username")
name_label.grid(row=1, sticky=W)
name_entry = Entry(textButton)## the Entry will let the user entre text inside the text box
name_entry.grid(row=1, column=1)
password_label = Label(textButton, text="Password")
password_label.grid(row=2, sticky=W)
password_entry = Entry(textButton, show="*")
password_entry.grid(row=2, column=1)
top.mainloop

You can use place to use an image as a background for other widgets. place doesn't affect the geometry of other widgets so you can use it with grid or pack.
The main think to keep in mind is that you should do this before creating other widgets so that it is lowest in the stacking order. That, or be sure to call lower on it, otherwise it will overlay the other widgets rather than underlay them.
With your code, just remove background.pack() and replace it with these two lines:
background.place(relx=.5, rely=.5, anchor="center")
background.lower()
You don't need to change anything else. The above centers the image. If you instead want the image to start in the upper-left corner you can do it like this:
background.place(x=0, y=0, anchor="nw")

You have to use background as parent for widgets to put them inside Label with background.
I remove Frame to make it simpler. And now I can use weight to automatically resize empty rows and columns around widgets so they will be in the center.
import tkinter as tk
top = tk.Tk()
top.geometry('250x250')
img = tk.PhotoImage(file="hal_9000.gif")
img = img.subsample(1, 1)
background = tk.Label(top, image=img, bd=0)
background.pack(fill='both', expand=True)
background.image = img
# resize empty rows, columns to put other elements in center
background.rowconfigure(0, weight=100)
background.rowconfigure(3, weight=100)
background.columnconfigure(0, weight=100)
background.columnconfigure(3, weight=100)
name_label = tk.Label(background, text="Username")
name_label.grid(row=1, column=1, sticky='news')
name_entry = tk.Entry(background)## the Entry will let the user entre text inside the text box
name_entry.grid(row=1, column=2)
password_label = tk.Label(background, text="Password")
password_label.grid(row=2, column=1, sticky='news')
password_entry = tk.Entry(background, show="*")
password_entry.grid(row=2, column=2)
top.mainloop()
Result:
As you see widgets have gray background which you can't remove. If you need text without gray background then you have to use Canvas with create_text() (and create_window() to put Entry)
Gif file (with HAL 9000) to test code:

Related

How do I adjust this frame with scrollbar inside the black space?

I want to fit this frame with scrollbar(refer the provided image) in the black space present in the provided image. How do I do that. The frame should totally cover the black space.
The code for program.The image
from tkinter import *
from tkinter import ttk
C = Tk()
C.maxsize(1200, 750)
C.geometry("1200x750")
C.title("Mainscreen")
style = ttk.Style()
style.theme_use('clam')
BG = PhotoImage(file="Mainscreen bg.png")
ML = PhotoImage(file="Music_label.png")
BG_label = Label(C, image=BG, border=0)
BG_label.place(x=0, y=0)
style.configure("Vertical.TScrollbar", gripcount=0,
background="Cyan", darkcolor="gray6", lightcolor="LightGreen",
troughcolor="Turquoise4", bordercolor="gray6", arrowcolor="gray6",arrowsize=15)
wrapper1= LabelFrame(C, width="1600", height="100", background="gray6",bd=0)
mycanvas = Canvas(wrapper1,background="gray6",borderwidth=0, highlightthickness=0, width=700, height=600)
mycanvas.pack(side=LEFT, expand="false", padx=0)
yscrollbar = ttk.Scrollbar(wrapper1, orient="vertical",command=mycanvas.yview)
yscrollbar.pack(side=RIGHT, fill="y")
mycanvas.configure(yscrollcommand=yscrollbar.set)
mycanvas.bind('<Configure>',lambda e: mycanvas.configure(scrollregion=mycanvas.bbox("all")))
myframe = Frame(mycanvas)
mycanvas.create_window((0,0), window=myframe, anchor="n")
wrapper1.pack(side=RIGHT,expand="false", padx=0, pady=200)
for i in range(50):
Button(myframe, image=ML,bg="gray6",bd=0).pack()
mainloop()
EDIT:
Music_Label
Mainscreen bg
So after trying to understand the underlying problem for a while I have reached the conclusion, that the problem is with the fact that the button are being drawn in the myframe and the myframe is outside the mycanvas which contains the scrollbar. So by changing the master widget for the button from myframe to mycanvas, the problem gets fixed now the scrollbar is adjacent to the buttons. BUT, Also now the button r shifted the side since while packing the wrapper1, you did side = RIGHT, so I would also suggest that you use place here instead of pack, since pack depends on the space available and is not reliable if you are using a Background for your GUI and want the buttons within a portion of it.
I have changed the following lines -:
Button(mycanvas, image=ML,bg="gray6",bd=0).pack() # Changed to mycanvas from myframe
and
wrapper1.place(x = {YOUR X, WHERE THE BOX STARTS}, y = {YOUR Y, WHERE THE BOX STARTS}) # Use place instead..
You can use pack also, and change the padx and pady arguments but it will be tricky to get it to work always as expected.
The fixed code is this -:
from tkinter import *
from tkinter import ttk
C = Tk()
C.maxsize(1200, 750)
C.geometry("1200x750")
C.title("Mainscreen")
style = ttk.Style()
style.theme_use('clam')
BG = PhotoImage(file="Mainscreen bg.png")
ML = PhotoImage(file="Music_label.png")
BG_label = Label(C, image=BG, border=0)
BG_label.place(x=0, y=0)
style.configure("Vertical.TScrollbar", gripcount=0,
background="Cyan", darkcolor="gray6", lightcolor="LightGreen",
troughcolor="Turquoise4", bordercolor="gray6", arrowcolor="gray6",arrowsize=15)
wrapper1= LabelFrame(C, width="1600", height="100", background="gray6",bd=0)
mycanvas = Canvas(wrapper1,background="gray6",borderwidth=0, highlightthickness=0, width=700, height=600)
mycanvas.pack(side=LEFT, expand=False, padx=0)
yscrollbar = ttk.Scrollbar(wrapper1, orient="vertical",command=mycanvas.yview)
yscrollbar.pack(side=RIGHT, fill="y", expand = False)
mycanvas.configure(yscrollcommand=yscrollbar.set)
mycanvas.bind('<Configure>',lambda e: mycanvas.configure(scrollregion=mycanvas.bbox("all")))
myframe = Frame(mycanvas)
mycanvas.create_window((0,0), window=myframe, anchor="n")
wrapper1.pack(expand=True, padx=0, pady=200) # Use place instead
for i in range(50):
Button(mycanvas, image=ML,bg="gray6",bd=0).pack() # Change this to mycanvas from myframe
mainloop()

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).

Python Tkinter - How to make a child frame with image inside a set width

I'm working on a program, which I'm building using python's tkinter gui library.
My 2 Problems
I'm trying to make a frame which will house a screenshot of the selected website. I have the frame and image created, but the frame is not anchoring to the right side of the window as expected. When the label element inside the frame has an image the frame is the expected length
But, when the selected site does not have an image yet the frame is not stretching all the way to the right side of the window.
Below is the portion of my code where I set up the image container/frame and respective image.
self.frame_ImageContainer = Frame(self.tab_Details, width=320, height=180, bg='black', bd=2)
self.frame_ImageContainer.grid(column=4, row=0, rowspan=11, columnspan=3, sticky=(N, S, E, W))
self.button_TakeScreenshot = Button(self.frame_ImageContainer, text='Take Screenshot', command=self.fn_RunScraper)
self.button_TakeScreenshot.grid(column=1, row=5, sticky=(E, W))
self.widget_Image = Label(self.frame_ImageContainer, compound='top')
self.widget_Image.grid(column=0, row=0, rowspan=12, columnspan=3, sticky=N)
self.frame_ImageContainer.grid_columnconfigure(0, weight=1)
self.frame_ImageContainer.grid_columnconfigure(1, weight=1)
self.frame_ImageContainer.grid_columnconfigure(2, weight=1)
self.frame_ImageContainer.grid_rowconfigure(0, weight=1)
self.frame_ImageContainer.grid_rowconfigure(1, weight=1)
self.frame_ImageContainer.grid_rowconfigure(2, weight=1)
self.frame_ImageContainer.grid_rowconfigure(3, weight=1)
self.frame_ImageContainer.grid_rowconfigure(4, weight=1)
self.frame_ImageContainer.grid_rowconfigure(5, weight=1)
self.frame_ImageContainer.grid_rowconfigure(6, weight=1)
self.frame_ImageContainer.grid_rowconfigure(7, weight=1)
self.frame_ImageContainer.grid_rowconfigure(8, weight=1)
self.frame_ImageContainer.grid_rowconfigure(9, weight=1)
self.frame_ImageContainer.grid_rowconfigure(10, weight=1)
self.frame_ImageContainer.grid_rowconfigure(11, weight=1)
The Take Screenshot button will not center both vertically & horizontally. I've attempted to center the Take Screenshot button in the middle of the frame, but it only applies vertically currently. I've also tried using .place(relx=.5, rely=.5, anchor=CENTER), but when I try that the frame/image doesn't even show up on the screen.
What I'm trying to accomplish
Making the image frame a set width
Showing a Take Screenshot button in the center of the image frame when a site does not have a screenshot yet.
In Question Form
How can I make the frame & image stay a consistent width & height (320x180)?
How can I center the Take Screenshot button both vertically and horizontally inside the Image frame?
If you want a frame to have a fixed size then simply unset its propagation for the layout manager its children use, to discard the size change based on its children's size demands, while defining setting its width and height:
frame = tk.Frame(..., width=320, height=180, ...)
#frame.grid_propagate(False) # uncomment if children use pack
frame.pack_propagate(False) # uncomment if children use grid
For centering a widget one easy way is using place:
widget.place(relx=.5, rely=.5, anchor='center')
A Minimal, Complete, and Verifiable example that accomplishes both:
from PIL import Image, ImageTk
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
if __name__ == '__main__':
root = tk.Tk()
frame = tk.Frame(root, width=320, height=180, bg='#f48024')
img = Image.new('RGB', (640, 360), color=(0, 121, 152))
frame.image = ImageTk.PhotoImage(img)
frame_s_label = tk.Label(frame, image=frame.image)
button = tk.Button(frame, text="Button")
button.place(relx=.5, rely=.5, anchor='center')
frame.grid()
#frame.grid_propagate(False) # uncomment if children use pack
frame.pack_propagate(False) # uncomment if children use grid
frame_s_label.pack()
tk.mainloop()
How can I make the frame & image stay a consistent width & height (320x180)?
If you want the frame to be a specific size, start by giving it a specific size. Next, either use place to add widgets to it, or turn geometry propagation off if using grid or pack
# if using pack:
self.frame_ImageContainer.pack_propagate(False)
# if using grid:
self.frame_ImageContainer.grid_propagate(False)
How can I center the Take Screenshot button both vertically and horizontally inside the Image frame?
The easiest way is to use place, since it won't affect the size of its master, and you can provide relative coordinates.
self.button_TakeScreenshot.place(relx=.5, rely=.5, anchor="center")

tkinter maximum canvas size?

It seems I'm hitting some kind of preset maximum scrollable canvas size, that I didnt know about...
I've written a simple bare-bones iTunes replacement in Tkinter.
Since I like the album cover view, an album needs at least 200x200 px size, and I have A LOT of albums (~1600), it follows that I need a lot of space.
But I discovered that above a height ~ 35000px the window is unable to show them.
Here I written a sample code of it - it needs ImageMagick's convert, and around 15sec to run on my machine.
You can see that the window only shows 163 of the 170 squares...
from Tkinter import *
import subprocess
def main():
root = Tk()
root.geometry("%dx%d+0+0" % (1800,1000))
cv = Canvas(root)
vscrollbar = Scrollbar(root, orient=VERTICAL)
vscrollbar.pack(fill=Y, side=RIGHT)
vscrollbar.config(command=cv.yview)
cv.config(yscrollcommand=vscrollbar.set)
cv.configure(scrollregion=(0,0, 4000, 50000))
cv.pack(side=LEFT, fill=BOTH, expand=TRUE)
fcv=Frame(root)
cv.create_window(0, 0, anchor = "nw", window=fcv)
memimages=[]
for row_index in range(170):
a=subprocess.Popen("convert -size 200x200 -pointsize 22 -gravity center label:%d test.gif" % row_index, shell=True,
stdout=subprocess.PIPE,stderr=subprocess.PIPE)
output, errors = a.communicate()
iconimage = PhotoImage(file="test.gif")
b=Button(fcv,image=iconimage)
memimages.append(iconimage)
b.grid(row=row_index, column=0, sticky=N+S+E+W)
mainloop()
main()
I modified your code to show an image at specific pixel height locations, e.g. one at y=0, one at y=32000 and one at y=50000. The canvas is able to traverse from 0 all the way to 50,000 pixel height, and we can see the images as expected.
This means the canvas is able to scroll all the way to y=50000 pixels and the problem lies not with pixel height limitation of canvas but I am guessing it could be with the manner the button is placed into the frame of the canvas window or the placement of frame in the canvas window or the placement of the canvas window itself into the canvas.
You can run this revised code to see what I mean. Scroll all the way to the bottom. Hope this gives you more insight to troubleshoot your code.
from Tkinter import *
def main():
root = Tk()
root.geometry("%dx%d+0+0" % (1800,1000))
cv = Canvas(root)
vscrollbar = Scrollbar(root, orient=VERTICAL)
vscrollbar.pack(fill=Y, side=RIGHT)
vscrollbar.config(command=cv.yview)
cv.configure(yscrollcommand=vscrollbar.set)
cv.configure(scrollregion=(0,0, 4000, 50000))
cv.pack(side=LEFT, fill=BOTH, expand=TRUE)
iconimage = PhotoImage(file="monkey.gif")
testimage = cv.create_image(300, 0, image=iconimage)
testimage1 = cv.create_image(300, 32000, image=iconimage)
testimage2 = cv.create_image(300, 50000, image=iconimage)
mainloop()
main()
Update: After further testing, it does seems there is a limitation on the display height of the window formed by the Canvas.create_window() method. I added the code below, just before mainloop(), which attempts to create buttons and labels with image of 100x100 pixels. The max. no. of rows of buttons that could be displayed was 316+ while max. no. of rows of labels that could be displayed was 322+. If buttons and labels were created together, the max. no. of row that could be displayed was 316+. My conclusion appears to be identical to yours.
Sorry to not have been able to answer your question. However, I hope to support you with my answer, and recommend someone more knowledgeable explains why this behaviour is the case.
fcv=Frame(cv)
cv.create_window(0, 0, anchor = "nw", window=fcv)
iconimage = PhotoImage(file="monkey100.gif") # Image dimension is 100x100 pixels
for row_index in range(340):
b=Button(fcv,image=iconimage)
b.grid(row=row_index, column=0, sticky=N+S+E+W)
lb=Label(fcv,text=str(row_index), image=iconimage, compound=LEFT)
lb.grid(row=row_index, column=1, sticky=N+S+E+W)
I found a way out the problem.
the limit seens to come from create_window iteself.
So i create multiples windows and this works fine...
from tkinter import *
from PIL import Image, ImageTk, ImageDraw
root = Tk()
vsb = Scrollbar(root, orient=VERTICAL)
vsb.grid(row=0, column=1, sticky=N+S)
hsb = Scrollbar(root, orient=HORIZONTAL)
hsb.grid(row=1, column=0, sticky=E+W)
c = Canvas(root,yscrollcommand=vsb.set, xscrollcommand=hsb.set)
c.grid(row=0, column=0, sticky="news")
vsb.config(command=c.yview)
hsb.config(command=c.xview)
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
w, h = 200,350
image={}
for i in range(0,200):
fr = Frame(c)
c.create_window(2, i*(h+2), window=fr)
image[i]=Image.new ('RGB', (w, h))
draw = ImageDraw.Draw(image[i])
draw.rectangle ((0,0,w,h), fill = (20,20,20) )
draw.text ((1,1), str(i), (255,255,255))
image[i]=ImageTk.PhotoImage(image[i])
btn=Button(fr, image=image[i])
btn.pack()
fr.update_idletasks()
c.config(scrollregion=c.bbox("all"))
root.mainloop()
quit()

Canvas on a frame not being displayed

I have a Canvas inside a Frame in tkinter. The frame has a background color and the canvas too. But seemingly the frame background overrides the canvas color.
How can I increase the transparency of the frame background such that the canvas is visible?
import Tkinter
import tkMessageBox
from Tkinter import *
top = Tkinter.Tk()
frame = Frame(top, width=1000, height=1000, background="bisque")
frame.pack()
bottomframe = Frame(top, width=1000, height=1000, background="red")
bottomframe.pack( side = BOTTOM )
def creatLayers(no_of_layers, max_nodes_in_each_layer, frame1=bottomframe):
print 'here2'
listLayerRect=[]
listDelimiterRect=[]
#The canvas is created here.
mainCanvas=Tkinter.Canvas(frame1, bg="white", height=1000, width=1000)
frame1.pack(side=LEFT)
for i in range (0,no_of_layers):
print 'here3'
x=15*i
#rectangles that are being drawn on the canvas.
mainCanvas.create_polygon(x,0,x+10,0,x+10,1000,x,1000, outline='gray', fill='gray', width=2)
# listLayerRect.append(Tkinter.Canvas(frame1, bg="blue", height=1000, width=30))
# listDelimiterRect.append(Tkinter.Canvas(frame1, bg="yellow", height=1000, width=30))
L1 = Label(frame, text="Layers")
E1 = Entry(frame, bd =8)
L2 = Label(frame, text="Layers2")
def helloCallBack(E=E1,):
# tkMessageBox.showinfo( "Hello Python", "Hello World")
k=int(E.get())
print 'here'
print k
creatLayers(k,k)
B = Tkinter.Button(frame, text ="Enter", command = helloCallBack)
B.pack(side=LEFT)
#L1.pack(side=LEFT)
E1.pack(side=LEFT)
#L2.pack(side=LEFT)
top.mainloop()
So, basically, when you enter a number in the box and press Enter, a canvas gets created in the red part (frame) and a grid pattern should be drawn on that canvas. Essentially, there are 2 frames, the top frame contains the button and the entry box, the lower frame should be able to draw stuff inside on the canvas created within.
The reason why the canvas is not displayed is because you're not telling it to be displayed inside frame1, i.e. you forgot to pack (or grid, or place) it, so just do in the meantime:
...
mainCanvas=Tkinter.Canvas(frame1, bg="white", height=1000, width=1000)
mainCanvas.pack()
...
Now depending on what you really want to achieve from the layout point of view, you may need to think better how to use pack, grid and pack.
Here's the result after the correction above (on Mac OS X, Sierra)
Before clicking Enter
After clicking Enter
In general, just remember that a frame will have a empty body if it doesn't contain any widget with a certain specified size.

Categories