tkinter strange phenomenon with buttons and images - python

I don't know what to say because I am clueless on why this is not working.
The first button appears but the image does not the second and third button does not appear.
from tkinter import *
Master = Tk()
Master.geometry("1408x768")
Master.configure(background = "#000000")
# Top
Top = Frame(Master)
Top.configure(background = "#1C1C1C", width = 1024.0, height = 384.0)
Top.place(x = 0.0, y = -5.684341886080802e-14)
Nextimg = PhotoImage(file = "Next.png")
Next = Button(master = Top, background = "#0084FF", image = Nextimg)
Next.place(x = 624.0, y = 551.0, width = 100, height = 50)
# Bottom
Bottom = Frame(Master)
Bottom.configure(background = "#8C8C8C", width = 1024.0, height = 384.0)
Bottom.place(x = 0.0, y = 384.0)
Nextimg = PhotoImage(file = "Next.png")
Next = Button(master = Bottom, background = "#0084FF", image = Nextimg)
Next.place(x = 624.0, y = 551.0, width = 100, height = 50)
# Dashboard
Dashboard = Frame(Master)
Dashboard.configure(background = "#252525", width = 384.0, height = 768.0)
Dashboard.place(x = 1024.0, y = 0.0)
Continueimg = PhotoImage(file = "Continue.png")
Continue = Button(master = Dashboard, background = "#FF8900", image = Continueimg)
Continue.place(x = 1091.0, y = 359.0, width = 250, height = 50)

Your primary issue here appears that you are expecting your widget placement to be relative to the root window but in fact they are relative to the frames they are placed in.
Try this. Change all your buttons X/Y to 20 and see what I mean.
You will see all the buttons show up in the top left corner of each frame they are assigned to.
One issue is that you are using place to manage widgets and this is very bad for code maintenance. It is much better to use grid() and/or pack() to build your windows for several reasons.
Automatic weights to adjust placement based on window size and easy of maintainability as your code grows.
The issue you will see with your 1st and 2nd buttons is the 1st button does not show the image due to the reference being reassigned.
If you want to use the same image for both just save a reference once.
In this case you can remove the 2nd Nextimg = from your code.
See what happens when we change the placement to 20's
from tkinter import *
Master = Tk()
Master.geometry("1408x768")
Master.configure(background = "#000000")
Top = Frame(Master)
Top.configure(background = "#1C1C1C", width = 1024.0, height = 384.0)
Top.place(x = 0.0, y = -5.684341886080802e-14)
Nextimg = PhotoImage(file = "Next.png")
Next = Button(master = Top, background = "#0084FF", image = Nextimg)
Next.place(x = 20, y = 20, width = 100, height = 50)
Bottom = Frame(Master)
Bottom.configure(background = "#8C8C8C", width = 1024.0, height = 384.0)
Bottom.place(x = 0.0, y = 384.0)
# Nextimg = PhotoImage(file = "Next.png") Removed this as it is why your image does not show up on the first button.
Next = Button(master = Bottom, background = "#0084FF", image = Nextimg)
Next.place(x = 20, y = 20, width = 100, height = 50)
Dashboard = Frame(Master)
Dashboard.configure(background = "#252525", width = 384.0, height = 768.0)
Dashboard.place(x = 1024.0, y = 0.0)
Continueimg = PhotoImage(file = "Continue.png")
Continue = Button(master = Dashboard, background = "#FF8900", image = Continueimg)
Continue.place(x = 20, y = 20, width = 250, height = 50)
Master.mainloop()
Note that I replaced your images with grey squares since I did not have your image to use.
Results:
A more conventional way to write this would be to use grid() or pack().
Also I have rewritten the code to more closely fit PEP* standards.
See below example:
import tkinter as tk
master = tk.Tk()
master.geometry("1408x768")
master.configure(background="#000000")
top = tk.Frame(master)
top.grid_propagate(False)
top.configure(background="#1C1C1C", width=1024.0, height=384.0)
top.grid(row=0, column=0)
nextimg = tk.PhotoImage(file="gray.png")
tk.Button(master=top, background="#0084FF", image=nextimg).grid(row=0, column=0)
bottom = tk.Frame(master)
bottom.grid_propagate(False)
bottom.configure(background="#8C8C8C", width=1024.0, height=384.0)
bottom.grid(row=1, column=0)
tk.Button(master=bottom, background="#0084FF", image=nextimg).grid(row=0, column=0)
dashboard = tk.Frame(master)
dashboard.grid_propagate(False)
dashboard.configure(background = "#252525", width = 384.0, height = 768.0)
dashboard.grid(row=0, column=1, rowspan=2)
continueimg = tk.PhotoImage(file="gray.png")
tk.Button(master=dashboard, background="#FF8900", image=continueimg).grid(row=0, column=0)
master.mainloop()
Results:
You can also manage spacing of the widgets from the edges if you like. That can be done by providing a padx/pady parameter to the grid().

Related

Tkinter frame expand is only displaying half my window

I'm trying to create a phone in tkinter and i'm using frames but the image is being cut in half. Does anyone know why?
https://i.stack.imgur.com/mOtEn.png
import tkinter as tk
def raiseframe(frame):
frame.tkraise()
root = tk.Tk()
bgImage = tk.PhotoImage(file="Phone.gif")
WALogo = tk.PhotoImage(file="whatsappLogo.gif")
width = bgImage.width()
height = bgImage.height()
root.title("Phone")
root.geometry("%dx%d+0+0" % (width, height))
main = tk.Frame()
whatsapp = tk.Frame()
for frame in (main, whatsapp):
frame.pack(fill = tk.BOTH, expand = True)
#Mainscreen:
canvas = tk.Canvas(main, width = width, height = height, bg = "black")
canvas.create_image((width / 2, height / 2), image = bgImage)
canvas.place(x = 0, y = 0)
WAB = tk.Button(main, image = WALogo, command = lambda: raiseframe(whatsapp))
WAB.place(x = 35, y = 85)
raiseframe(main)
root.mainloop()
I believe you have added the picture into a frame which only takes up up to half of the screen.
You could either:
change the dimensions of the frame
add the picture to root like this:
canvas = tk.Canvas(root, width = width, height = height, bg = "black")
canvas.create_image((width / 2, height / 2), image = bgImage)

How do you create a movable sprite image in an image frame with Tkinter?

The goal is to have a character sprite that can move around in a box. The box to the left is where the visuals are, and the right are descriptions (etc). At the bottom there is a text box so the user can enter commands. Here's an image to demonstrate. I don't want the character to be able to move into the text. How would I go about doing this using Tkinter? Here's the code for setting up the GUI:
def setUpGui(self):
self.pack(fill = BOTH, expand = 1)
#Setting up player input region of the GUI
Game.player_input = Entry(self, bg = "white")
#Executes process when enter pressed
Game.player_input.bind("<Return>", self.process)
#Stretches it across the bottom
Game.player_input.pack(side = BOTTOM, fill = X)
#Makes this the focus upon running the program
Game.player_input.focus()
img =None
Game.image = Label(self, width = WIDTH/2, image = img)
Game.image.image = img
Game.image.pack(side = LEFT, fill = Y)
Game.image.pack_propagate(False)
text_frame = Frame(self, width = WIDTH/2)
Game.text = Text(text_frame, bg = "lightgrey", state = DISABLED)
Game.text.pack(side = RIGHT, fill = Y)
text_frame.pack(side = RIGHT, fill = Y)
text_frame.pack_propagate(False)
def setRoomImage(self):
if (Game.currentRoom == None):
Game.img = PhotoImage(file = "skull.gif")
else:
Game.img = PhotoImage(file = Game.currentRoom.image)
Game.image.config(image = Game.img)
Game.image.image = Game.img

PhotoImage is not responding to tkinter Scale widget

I have a python script which allows a user to open up an image in a tkinter canvas widget. The program is printing out the size that the new image should be, but only the original image shows up. How do I refresh the canvas with the new size.
Here is the python code:
from Tkinter import *
import tkFileDialog
from PIL import ImageTk, Image
factor = 1.0
width_org = 500
height_org = 500
mGui = Tk()
mGui.geometry("600x600")
mGui.configure(background = 'yellow')
frame = Frame(mGui, width = 400, height = 400)
frame.pack(anchor = 'nw')
frame.configure(background = 'red')
canvasframe = Canvas(frame, width = 400, height = 400, scrollregion = (-500,-500,500,500))
hbar = Scrollbar(frame,orient=HORIZONTAL)
hbar.pack(side=BOTTOM, fill = X)
hbar.configure(command= canvasframe.xview)
vbar = Scrollbar(frame,orient = VERTICAL )
vbar.pack(side=RIGHT, fill = Y)
vbar.configure(command= canvasframe.yview)
canvasframe.configure(xscrollcommand= hbar.set, yscrollcommand= vbar.set)
canvasframe.pack( expand = True, fill = BOTH)
pil_img = Image.open("rose-flower-500x500.jpg")
img = ImageTk.PhotoImage(pil_img)
def openit():
in_path = tkFileDialog.askopenfilename()
try:
pil_img = Image.open(in_path)
except IOError:
pass
width_org, height_org = pil_img.size
try:
img = ImageTk.PhotoImage(pil_img)
except IOError:
pass
canvasframe.create_image(20,20, image = img)
canvasframe.img = img
valueList = [25, 50, 100, 150, 200]
def valuecheck(value):
newval = min(valueList, key=lambda x:abs(x-float(value)))
scalepic.set(newval)
factor = newval/100.0
w = int(width_org*factor)
h = int(height_org*factor)
print (w, h)
pil_img = pil_img.resize((w,h),Image.ANTIALIAS)
img = ImageTk.PhotoImage(pil_img)
canvasframe.create_image(20,20, image =img)
openpic = Button(mGui, text = "Open", command = openit).pack()
scalelabel = Label(mGui, text = "Resize Image").pack()
scalepic = Scale(mGui, from_=min(valueList), to=max(valueList), command=valuecheck, orient = HORIZONTAL)
scalepic.set(100)
scalepic.pack()
mGui.mainloop()
Also, how can I open a new image? I'm thinking of a for loop and some kind of dispose method, but I'm not sure about the syntax.
EDIT
Part of code in classes
from Tkinter import *
import tkFileDialog
from PIL import ImageTk, Image
factor = 1.0
width_org = 500
height_org = 500
class MainApp(Frame):
def createControls(self):
frame = Frame(self, width = 600, height = 500)
frame.configure(background = 'red')
frame.pack(anchor = 'nw')
canvasframe = Canvas(frame, width = 600, height = 500, scrollregion = (-600,-500,600,500))
canvasframe.configure(xscrollcommand= hbar.set, yscrollcommand= vbar.set)
canvasframe.pack(expand = True, fill = BOTH)
hbar = Scrollbar(frame, orient=HORIZONTAL)
hbar.pack(side=BOTTOM, fill=X)
hbar.configure(command= canvasframe.xview)
vbar = Scrollbar(frame, orient=VERTICAL)
vbar.pack(side=RIGHT, fill = Y)
vbar.configure(command= canvasframe.yview)
def __init__(self, parent):
Frame.__init__(self, parent, width = 800, height = 600, background = 'yellow')
self.pack()
self.createControls()
root = Tk()
app = MainApp(parent =root)
app.mainloop()
You forgot to save the reference to the new PhotoImage in canvasframe.img, so it gets garbage collected when valuecheck returns.
Also, rather than creating a new Canvas image with create_image for the rescaled image you should simply update the existing one via itemconfig. To do that, you need to save the item id number returned by create_image. Eg, in openit, do
canvasframe.imgid = canvasframe.create_image(20,20, image = img)
And then in valuecheck, after you create the new PhotoImage do
canvasframe.img = img
canvasframe.itemconfig(canvasframe.imgid, image=img)
You don't have to store img and imgid as attributes of canvasframe, but it's convenient in this case. It would get messy though if you had lots of things on the canvas that you had to track. But for larger Tkinter programs it's a good idea to put everything into one or more classes. It makes things more organized, and easier to access.

Tkinter listboxes and canvas won't scroll together

I want the following layout in TkInter:
4 listboxes next to eachother, a canvas next to them, filled with buttons (one button for every element in the listboxes)
The problem is, that the canvas can be scrolled with the listboxes, also on it's own, but even higher than the point where it has content, which means, you can scroll it to the very top, but the listbox belonging to it won't even move, and they are de-synched already. So question is, how can I give a point for the scrolling where it can not go higher?
The code I have is the following:
from Tkinter import *
def Scroll2Gether(*args):
list1.yview(*args)
list2.yview(*args)
list3.yview(*args)
list4.yview(*args)
ButtonLine.yview(*args)
master = Tk()
EndLayer = Canvas(master)
scroll = Scrollbar(orient="vertical", command=Scroll2Gether)
UpperLayer = Frame(EndLayer, bg = '#000fff000')
list1 = Listbox(UpperLayer, bg = "#B2B2B2",width = 60)
list3 = Listbox(UpperLayer, bg = "#B2B2B2",width = 60)
list4 = Listbox(UpperLayer, bg = "#E5E5E5",width = 60)
list2 = Listbox(UpperLayer, bg = "#E5E5E5",width = 60)
Framm = Frame(UpperLayer, bg = "#ffa300", width = 30, )
Address1 = Label(list1, text = "OWNER1", bg= "#E5E5E5",width = 60,font=("Helvetica", 8,"bold"))
Address2 = Label(list2, text = "OWNER2", bg= "#B2B2B2", width = 60,font=("Helvetica", 8,"bold"))
Address3 = Label(list3, text = "MailDL", bg= "#E5E5E5", width = 60,font=("Helvetica", 8,"bold"))
Address4 = Label(list4, text = "ExpirationDate", bg= "#B2B2B2", width = 60,font=("Helvetica", 8, "bold"))
ButtonLine = Canvas(Framm, bg = "#E5E5E5", width = 30)
Address1.pack(side = TOP)
Address2.pack(side = TOP)
Address3.pack(side = TOP)
Address4.pack(side = TOP)
list1.pack(side = LEFT, fill = "y")
list2.pack(side = LEFT, fill = "y")
scroll.pack(side="right",fill="y")
list3.pack(side = LEFT, fill = "y")
list4.pack(side = LEFT, fill = "y")
Framm.pack(side=LEFT)
ButtonLine.pack(side = BOTTOM)
UpperLayer.pack()
EndLayer.pack()
x = 0
photo=PhotoImage(file="email.gif")
for i in ("qwertzuiopasfghjklyxcvbnm"):
i = Button(text="Mail")
i.config(image = photo, width = 20, height = 20)
Button_Window = ButtonLine.create_window(1, 22+x, anchor = NW, window = i)
x = x+26
Thanks for any help in advance.
SOLVED! I found out that it makes no sense to group 3 columns, so I created lines which are canvas and created canvases in them for every input in a loop.

Python Tkinter xscrollbar not working

I'm almost at the point of tearing my hair out over this one:
I'm trying to get an xscrollbar and yscrollbar working so that I can move around a large canvas in Tkinter. The reason I'm so frustrated is because the yscrollbar seems to be working WITH THE EXACT SAME CODE (replacing x with y everywhere). Here's what I have:
master = Tk()
scrolly = Scrollbar(master,orient = VERTICAL)
scrollx = Scrollbar(master,orient = HORIZONTAL)
scrollx.pack(side = TOP,fill = X)
scrolly.pack(side = RIGHT,fill = Y)
w = Canvas(master, width=1000,height=1000,yscrollcommand = scrolly.set,xscrollcommand = scrollx.set,scrollregion=(0,0,1000,1000))
s = Scale(master,from_= 0, to=len(worldlist)-1,orient = HORIZONTAL,length = 595)
s.pack(side = BOTTOM)
w.pack()
setSys(worldlist[0],master,w)
def show(self):
w.delete(ALL)
setSys(worldlist[s.get()],master,w)
s.config(command = show)
scrolly.config(command = w.yview)
scrollx.config(command = w.xview)
mainloop()
I want the canvas and a scale widget sitting at the bottom. And at the right and top, a ybar and an xbar, respectively. Can anyone see what I'm doing wrong? I'm quite desperate for some help!
Thanks!
Gabe
The reason the xbar isn't working is because it's got nothing to scroll to. After you configure the canvas to a size bigger than the screen, it starts working. Take a look at the following code to see where I've added the scrollregion config. http://www.java2s.com/Code/Python/GUI-Tk/ScrolledCanvas.htm
from Tkinter import *
def show(self):
w.delete(ALL)
setSys(worldlist[s.get()],master,w)
master = Tk()
scrolly = Scrollbar(master,orient = VERTICAL)
scrollx = Scrollbar(master,orient = HORIZONTAL)
scrollx.pack(side = TOP,fill = X)
scrolly.pack(side = RIGHT,fill = Y)
w = Canvas(master, width=500, height=500, yscrollcommand = scrolly.set,
xscrollcommand = scrollx.set, scrollregion=(0,0,1000,1000))
w.config(scrollregion=(0, 0, 500, 5000))
w.pack()
s = Scale(master,from_= 0, to=100-1,orient = HORIZONTAL,length = 595)
s.pack(side = BOTTOM)
s.config(command = show)
scrolly.config(command = w.yview)
scrollx.config(command = w.xview)
mainloop()

Categories