Whenever I run this code with a background Image the button grid gets misplaced and pushed towards the bottom. Fortunately, it works as intended when no background is added .I want them to cover the background when executed. Pictures for reference are added below. Your help is highly appreciated.
# importing the module
import tkinter.messagebox
from tkinter import *
import random
# importing the module
# initialising tkinter
class window(Frame):
def __init__(self,master = None):
Frame.__init__(self,master)
self.master = master
# initialising tkinter
# creating the window
root = Tk()
app = window(root)
root.geometry("630x630")
root.title('Odd Even Game')
C = Canvas(root, bg="blue", height=250, width=300)
filename = PhotoImage(file = "BG.png")
background_label = Label(root,image=filename)
background_label.place(x=0, y=0, relwidth=1, relheight=1)
C.pack()
frame = Frame(root)
frame.pack()
# creating the window
# image
level_1e = "p1.png"
level_1o = "pe.png"
level_2e = "r1.png"
level_2o = "re.png"
# image
def create_cards(odd_image,even_image,next_level,fno,order,suc,err,w,h):
rx = random.randint(0,order-1)
ry = random.randint(0,order-1)
for i in range(0,order):
for j in range(0,order):
if i == rx and j == ry:
create_button(i,j,suc,odd_image,next_level,fno,odd_image,w,h)
else:
create_button(i,j,err,even_image,next_level,fno,odd_image,w,h)
def second_level(fno):
fno.pack_forget()
frame2 = Frame(root)
frame2.pack()
suc = "Congratulations! You have cleared level 2..Keep Going Buddy!"
err = "Wrong Answer..Don't give up yet!"
create_cards(level_2o,level_2e,final_level,frame2,4,suc,err,157.5,157.5)
def final_level(fno):
fno.pack_forget()
root.geometry("700x700")
ap = App(root)
# creating a button function
def create_button(x,y,msg,picture,next_level,fno,odd,w,h):
if picture == odd:
image = PhotoImage(file=picture)
click = Button(fno, image=image, width= w, height=h, bd = 0,command = lambda : [score_update(),next_level(fno),tkinter.messagebox.showinfo( "Odd One Out Project",msg)])
click.image = image
click.grid( row = x, column = y)
else:
image = PhotoImage(file=picture)
click = Button(fno, image=image, width= w, height=h, bd = 0,command = lambda : [next_level(fno),tkinter.messagebox.showinfo( "Odd One Out Project",msg)])
click.image = image
click.grid( row = x, column = y)
# creating a button function
def create_frame(fno):
root.geometry("630x630")
fno.pack_forget()
frame = Frame(root)
frame.pack()
suc = "Congratulations! You have cleared level 1..Time to increas[![enter image description here][1]][1]e the difficulty!"
err = "Wrong Answer..Please Try again !!"
create_cards(level_1o,level_1e,second_level,frame,3,suc,err,200,200)
def intro():
root.geometry("630x630")
frame0 = Frame(root)
frame0.pack()
click = Button(frame0,text="Start!" ,command = lambda [create_frame(frame0),tkinter.messagebox.showinfo( "Odd One Out Project","The game has begun!!")])
click.pack()
intro()
# starting the widget
root.mainloop()
# starting the widget
The first image is the error. Second Image is the required output.
Note: I'm still a beginner in Python and Tkinter hence various terms and methods might be something out of my scope. Would be appreciated if taken into consideration.
In case needed, you might know that this is a tkinter project for picking the odd one out image out of A*A grid.
I got the answer myself so will be sharing for future use.
C = Canvas(root, bg="blue", height=250, width=300)
This part draws a canvas of 250*300 dimensions hence does not let the buttons draw over it.
just change it to,
C = Canvas(root, bg="blue", height=0, width=0)
for the desired result
Related
I try to put the widgets like this:
I don't understand why my code doesn't do that, tried to look for examples online but didn't find a solution and nothing I tried brought me closer to the requested result.
This is my code so far(if you have any comments about anything in the code feel free to tell me because it's my first try with tkinter and GUIs in general):
from Tkinter import *
class box(object):
def __init__ (self, colour,s):
self.root = root
self.listbox = Listbox(self.root, fg = colour, bg = 'black')
self.s = s
self.place_scrollbar()
self.listbox.pack(side = self.s)
def place_scrollbar(self):
scrollbar = Scrollbar(self.root)
scrollbar.pack(side = self.s, fill = Y)
self.listbox.config(yscrollcommand = scrollbar.set)
scrollbar.config(command = self.listbox.yview)
def write(self, contenet):
self.listbox.insert(END, contenet)
root = Tk()
root.resizable(False, False)
boxs = Frame(root)
boxs.pack()
box.root = boxs
server = box("red", LEFT)
client = box("green", RIGHT )
bf = Frame(root)
bf.pack(side = BOTTOM)
entry = Entry(bf,bg ='black', fg = 'white')
entry.pack()
root.mainloop()
You can't do this without using an additional frame to contain the box objects while still using pack, while still maintaining resizability.
But it is more organized in some cases to: use an additional frame to contain your box objects, by initializing it with a parent option.
Right now the widgets inside the box class are children to global root object. Which isn't really a good practice. So let's first pass and use a parent object to be used for widgets inside.
Replace:
def __init__ (self, colour,s):
self.root = root
self.listbox = Listbox(self.root, ...)
...
def place_scrollbar(self):
scrollbar = Scrollbar(self.root)
...
with:
def __init__ (self, parent, colour,s):
self.parent= parent
self.listbox = Listbox(self.parent, ...)
...
def place_scrollbar(self):
scrollbar = Scrollbar(self.parent)
...
This makes it so that you now need to initialize the object like the following:
server = box(root, "red", LEFT)
client = box(root, "green", RIGHT )
Now that we can pass a parent widget, let's create a parent frame to contain them. Actually, there's an un-used frame already, boxs let's use that by passing it as the parent as opposed to root:
server = box(boxs, "red", LEFT)
client = box(boxs, "green", RIGHT )
Now everything looks fine, optionally if you want to make it so that entry occupies as much left space as possible currently add fill='x' as an option to the pack of both the entry and the frame that contains it:
bf.pack(side = BOTTOM, fill='x')
...
entry.pack(fill='x')
Your whole code should look like:
from Tkinter import *
class box(object):
def __init__ (self, parent, colour,s):
self.parent = parent
self.listbox = Listbox(self.parent, fg = colour, bg = 'black')
self.s = s
self.place_scrollbar()
self.listbox.pack(side = self.s)
def place_scrollbar(self):
scrollbar = Scrollbar(self.parent)
scrollbar.pack(side = self.s, fill = Y)
self.listbox.config(yscrollcommand = scrollbar.set)
scrollbar.config(command = self.listbox.yview)
def write(self, contenet):
self.listbox.insert(END, contenet)
root = Tk()
root.resizable(False, False)
boxs = Frame(root)
boxs.pack()
box.root = boxs
server = box(boxs, "red", LEFT)
client = box(boxs, "green", RIGHT )
bf = Frame(root)
bf.pack(side = BOTTOM, fill='x')
entry = Entry(bf,bg ='black', fg = 'white')
entry.pack(fill='x')
root.mainloop()
Or: use grid instead of pack (with columnspan=2 option for entry).
General Answer
More generally putting a widget beneath two widgets that are side-by-side can be done by:
Encapsulating the side-by-side widgets with a frame, and then simply putting the frame above the other widget:
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
def main():
root = tk.Tk()
side_by_side_widgets = dict()
the_widget_beneath = tk.Entry(root)
frame = tk.Frame(root)
for name in {"side b", "y side"}:
side_by_side_widgets[name] = tk.Label(frame, text=name)
side_by_side_widgets[name].pack(side='left', expand=True)
frame.pack(fill='x')
the_widget_beneath.pack()
root.mainloop()
if __name__ == '__main__':
main()
Using grid:
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
def main():
root = tk.Tk()
side_by_side_widgets = dict()
the_widget_beneath = tk.Entry(root)
for index, value in enumerate({"side b", "y side"}):
side_by_side_widgets[value] = tk.Label(root, text=value)
side_by_side_widgets[value].grid(row=0, column=index)
the_widget_beneath.grid(row=1, column=0, columnspan=2)
root.mainloop()
if __name__ == '__main__':
main()
Without using additional frames, by calling pack for the_widget_beneath with side='bottom' as the first pack call, as in Bryan's comment:
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
def main():
root = tk.Tk()
side_by_side_widgets = dict()
the_widget_beneath = tk.Entry(root)
the_widget_beneath.pack(side='bottom')
for name in {"side b", "y side"}:
side_by_side_widgets[name] = tk.Label(root, text=name)
side_by_side_widgets[name].pack(side='left', expand=True)
root.mainloop()
if __name__ == '__main__':
main()
You can more easily notice reliability to global objects by creating a global main method, and add main-body of your script there and call:
...
def main():
root = Tk()
root.resizable(False, False)
boxs = Frame(root)
boxs.pack()
box.root = boxs
server = box(boxs, "red", LEFT)
client = box(boxs, "green", RIGHT )
bf = Frame(root)
bf.pack(side = BOTTOM, fill='x')
entry = Entry(bf,bg ='black', fg = 'white')
entry.pack(fill='x')
root.mainloop()
if __name__ == '__main__':
main()
How to put a widget beneath two widgets that are side-by-side using pack?
For a very simple layout like in your diagram, you simply need to pack the thing on the bottom first. That is because pack uses a "cavity" model. Each widget is organized in an unfilled cavity. Once that widget has been placed, that portion of the cavity is filled, and is unavailable for any other widgets.
In your case, you want the bottom cavity to be filled with the entry widget, so you should pack it first. Then, in the remaining upper cavity you can place your two frames side-by side, one on the left and one on the right.
For example:
import Tkinter as tk
root = tk.Tk()
entry = tk.Entry(root)
frame1 = tk.Frame(root, width=100, height=100, background="red")
frame2 = tk.Frame(root, width=100, height=100, background="green")
entry.pack(side="bottom", fill="x")
frame1.pack(side="left", fill="both", expand=True)
frame2.pack(side="right", fill="both", expand=True)
root.mainloop()
In the body of your question things get a bit more complicated, as you don't just have three widgets like your title suggests, you have several, with some being packed in the root and some being packed elsewhere, with pack statements scattered everywhere.
When using pack, it's best to group widgets into vertical or horizontal slices, and not mix top/bottom with left/right within the same group. It almost seems like you're trying to do that with your boxes, but then you don't -- your "box" is actually two widgets.
Bottom line: be organized. It also really, really helps if all of your pack (or place or grid) statements for a given parent are all in the same block of code. When you scatter them around it makes it impossible to visualize, and impossible to fix. Also, make sure that widgets have the appropriate parents.
I've been programming a random operator name generator for Rainbox Six Siege and I want the operators picture to appear when their name comes up. The image appears fine, but it won't go away. This is my Code:
from tkinter import *
import tkinter
import random
names = ['Sledge','Thatcher','Ash','Thermite','Twitch','Montagne','Glaz','Fuze','Blitz','IQ','Buck','Blackbeard','Capitão','Hibana']
name = ["Smoke","Mute","Castle","Pulse","Doc","Rook","Kapkan","Tachanka","Jäger","Bandit","Frost","Valkyrie","Caveira","Echo"]
root = tkinter.Tk()
def pickName():
rad = random.choice(names)
photo = PhotoImage(file=rad+".png")
label = Label(image=photo)
label.image = photo # keep a reference!
label.pack()
nameLabel.configure(text=rad, foreground="white", background="blue")
root.configure(background='blue')
def pickName1(): nameLabel.configure(text=random.choice(name),background="orange",foreground="black")
root.configure(background='orange')
root.title("Operator Picker")
root.geometry("250x100")
nameLabel = tkinter.Label(root, text="", font=('Helvetica', 32))
nameLabel.pack()
Grid()
f1 = tkinter.Frame(root, height=100, width=100) #defines frame size in
pixels
f1.pack(side=tkinter.LEFT) #packs on the left
f1.pack_propagate(0) #tells frame not to let children control size
pickButton1 = tkinter.Button(f1, command=pickName, text="Pick
Attack",background="blue",foreground="white")
pickButton1.pack(fill=tkinter.BOTH, expand=1) #takes up all available space
f2 = tkinter.Frame(root, height=100, width=100)
f2.pack(side=tkinter.RIGHT)
f2.pack_propagate(0)
pickButton2 = tkinter.Button(f2, command=pickName1, text="Pick
Defend",background="orange",foreground="black")
pickButton2.pack(fill=tkinter.BOTH, expand=1)
root.mainloop()
Note: This is still a WIP, all I need is to know how to get rid of the pictures once they appear. This is what it looks like when more than one image appears: https://imgur.com/eroXLLn
You are adding a new Label every time you call that function. Instead, you should make the Label only once (probably in the initialization stage), and update the picture. Just like you update the text for nameLabel, plus the step to keep the reference.
photo_label = tkinter.Label()
def pickName():
rad = random.choice(names)
photo = PhotoImage(file=rad+".png")
photo_label.configure(image = photo)
photo_label.image = photo # keep a reference!
photo_label.pack()
nameLabel.configure(text=rad, foreground="white", background="blue")
and your whole code should look like:
from tkinter import *
import tkinter
import random
names = ['Sledge','Thatcher','Ash','Thermite','Twitch','Montagne','Glaz','Fuze','Blitz','IQ','Buck','Blackbeard','Capitão','Hibana']
name = ["Smoke","Mute","Castle","Pulse","Doc","Rook","Kapkan","Tachanka","Jäger","Bandit","Frost","Valkyrie","Caveira","Echo"]
root = tkinter.Tk()
photo_label = tkinter.Label()
def pickName():
rad = random.choice(names)
photo = PhotoImage(file=rad+".png")
photo_label.configure(image = photo)
photo_label.image = photo # keep a reference!
photo_label.pack()
nameLabel.configure(text=rad, foreground="white", background="blue")
root.configure(background='blue')
def pickName1(): nameLabel.configure(text=random.choice(name),background="orange",foreground="black")
root.configure(background='orange')
root.title("Operator Picker")
root.geometry("250x100")
nameLabel = tkinter.Label(root, text="", font=('Helvetica', 32))
nameLabel.pack()
Grid()
f1 = tkinter.Frame(root, height=100, width=100) #defines frame size inpixels
f1.pack(side=tkinter.LEFT) #packs on the left
f1.pack_propagate(0) #tells frame not to let children control size
pickButton1 = tkinter.Button(f1, command=pickName, text="PickAttack",background="blue",foreground="white")
pickButton1.pack(fill=tkinter.BOTH, expand=1) #takes up all available space
f2 = tkinter.Frame(root, height=100, width=100)
f2.pack(side=tkinter.RIGHT)
f2.pack_propagate(0)
pickButton2 = tkinter.Button(f2, command=pickName1, text="PickDefend",background="orange",foreground="black")
pickButton2.pack(fill=tkinter.BOTH, expand=1)
root.mainloop()
Im trying to set up simple frame work for a trashy hang man game, the images are named in series of 6 for each stage of failure. S0 is the beginning and S6, is the final stage.
Im trying to figure out how to upudate the photo.
from tkinter import *
from PIL import ImageTk, Image
root = Tk()
i=0
frameLeft = Frame(root)
frameRight = Frame(root)
frameRight.pack(side=RIGHT)
frameLeft.pack(side=LEFT)
#frame Left
#function Declaration
entry1 = Entry(frameLeft)
entry1.pack()
def updatePhoto():
i = entry1.get()
img = ImageTk.PhotoImage(Image.open("S" + i + ".gif"))
imgPanel = Label(frameRight, image = img)
imgPanel.pack(side=BOTTOM, fill=BOTH, expand=YES)
labelInstruction = Label(frameLeft, text=entry1.get())
labelInstruction.pack()
submitButton = Button(frameLeft, text="Submit", command=updatePhoto)
submitButton.pack()
#frame Right
img = ImageTk.PhotoImage(Image.open("S" + str(i) + ".gif"))
imgPanel = Label(frameRight, image = img)
imgPanel.pack(side=BOTTOM, fill=BOTH, expand=YES)
root.mainloop()
Instead of these lines:
imgPanel = Label(frameRight, image = img)
imgPanel.pack(side=BOTTOM, fill=BOTH, expand=YES)
I was able to use these
imgPanel.configure(image=img)
imgPanel.image = img
I also changed the order of left and right side making right come first as it's a scripting language and requires declaration.
I am trying to display a button over an image.The button appears to be in front of the image.
How can I set the image to the background of the app?
The button also needs to change his location, so the image should be permenant.
My code:
from Tkinter import *
class App:
def __init__(self,master):
frame = Frame(master)
master.geometry("400x200")
frame.pack()
self.hello_b = Button(master,text="ME",command=sys.exit, height=1, width=3,fg= "blue",bg = "green")
self.hello_b.pack()
photo = PhotoImage(file="unnamed.gif")
w = Label (master, image=photo)
w.place(x=0, y=0, relwidth=1, relheight=1)
w.photo = photo
w.pack()
root = Tk()
app = App(root)
root.mainloop()
The answer, which I suspected, is here: "[the code] packs a Label widget in a frame widget, and then places a Button in the upper right corner of the frame. The button will overlap the label." I tested like so
from tkinter import *
class App:
def __init__(self,master):
master.geometry("400x200")
photo = PhotoImage(file="C:/programs/Python34/temimage.png")
self.w = w = Label (master, image=photo)
w.photo = photo
w.pack()
self.hello_b = Button(master,text="ME",command=sys.exit, height=1,
width=3,fg= "white",bg = "green")
self.hello_b.place(x=200, y=5)
root = Tk()
app = App(root)
root.mainloop()
The button is indeed on top of the image. The x,y positions seems to be the upper left corner of the button. Adding
def move():
app.hello_b.place(x=100, y=100)
root.after(1000, move)
caused the button to jump after a second, as expected.
I am new to programming and python so I thank you in advance for your patience.
I have created a class which creates a new window with a test image for it's background ("test1.gif").
I have also created a drop down menu within the same class that lets you choose between 3 countries.
I can comment off either the code that makes the background or the code that makes the menu and the other will display. However I want the menu to appear above the background. When I run the full code I only see the background.
I appreciate I am probably missing something very obvious here, if some one could please point out what that is.
Thanks for your time.
Dan
from Tkinter import *
import Tkinter as tk
class dropDown():
def __init__(self, master):
#background set_up
image1 = tk.PhotoImage(file="test1.gif")
w = image1.width()
h = image1.height()
root.geometry("%dx%d+0+0" % (w, h))
panel1 = tk.Label(root, image=image1)
panel1.pack(side='top', fill='both', expand='yes')
# save the panel's image from 'garbage collection'
panel1.image = image1
#Drop down menu
self.var = StringVar(master)
self.var.set('Alaska') # initial value
#Have not used countries list just pasted
self.option = OptionMenu(master, self.var, 'Alberta', 'Australia')
self.option.pack()
root = Tk()
root.title('drop down test')
dropDown(root)
mainloop()
(I think) Your problem is that you're restricting the size of the root window to be the size of the image (instead of the sizeof(image)+sizeof(menu)). This fixes the problem for me on OS-X.
import Tkinter as tk
class dropDown():
def __init__(self, master):
#background set_up
image1 = tk.PhotoImage(file="test.gif")
w = image1.width()
h = image1.height()
panel1 = tk.Label(root, image=image1)
panel1.pack(side='top', fill='both', expand='yes')
# save the panel's image from 'garbage collection'
panel1.image = image1
#Drop down menu
self.var = tk.StringVar(master)
self.var.set('Alaska') # initial value
#Have not used countries list just pasted
self.option = tk.OptionMenu(master, self.var, 'Alaska','Alberta', 'Australia')
self.option.pack()
geomstr="%dx%d+0+0" % (w, panel1.winfo_reqheight()+self.option.winfo_reqheight())
root.geometry(geomstr)
root = tk.Tk()
root.title('drop down test')
dropDown(root)
root.mainloop()
A side note, it would be very easy to have your dropDown inherit from Frame and then adjust the size of your Frame (Although it probably isn't necessary). Then you can move this widget anywhere you want to on the GUI.
EDIT
subclassing Frame:
import Tkinter as tk
class dropDown(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
#background set_up
image1 = tk.PhotoImage(file="test.gif")
panel1 = tk.Label(root, image=image1)
panel1.pack(side='top', fill='both', expand='yes')
# save the panel's image from 'garbage collection'
panel1.image = image1
#Drop down menu
self.var = tk.StringVar(master)
self.var.set('Alaska') # initial value
#Have not used countries list just pasted
self.option = tk.OptionMenu(master, self.var, 'Alaska','Alberta', 'Australia')
self.option.pack()
root = tk.Tk()
root.title('drop down test')
d=dropDown(root)
d.pack(side='top',fill='both',expand='yes')
root.mainloop()
Note that now Tkinter/TK take care of all the frame resizing for you. :)