I have a program that resizes the buttons when you resize the window but the image doesn't resize with the buttons.
This is my code:
l = Label(window, image=img).grid(row=0, column=0, rowspan=3, sticky='nesw')
con = Frame(window).grid(row=0, column=1, sticky='nesw')
nextImg = PhotoImage(file='nextImg.png')
lastImg = PhotoImage(file='lastImg.png')
ok = PhotoImage(file="ok".png')
nextCam = Button(con, image=nextImg, command=nxt, background='#2B2B2B').grid(row=0, column=1, sticky='nesw')
lastCam = Button(con, image=lastImg, command=lst, background='#2B2B2B').grid(row=2, column=1, sticky='nesw')
takeImg = Button(con, image=ok, command=ok, background='#2B2B2B').grid(row=1, column=1, sticky='nesw')
I expect the output to look like this:
But what it actually does is:
------------------------Edit-----------------------
This needs to work with more than 2 buttons.
Interesting question. PhotoImage does not have a resize method, but you could use a PIL image to get that. If you don't have PIL you need to pip install pillow to get it.
import tkinter as tk
from PIL import Image, ImageTk
class ImageButton(tk.Button):
"""A button that displays an Image and resizes the image as the Button size changes"""
def __init__(self, master=None, image=None, **kwargs):
super().__init__(master, **kwargs)
if not image: return # no image provided; act as a normal Button
if isinstance(image, str):
self.image = Image.open(image)
elif isinstance(image, Image.Image):
self.image = image
else:
raise TypeError("'image' argument must be a PIL.Image or filename")
self.bind("<Configure>", self._on_configure)
def _on_configure(self, event=None):
size = event.width-4, event.height-4
self.photoimage = ImageTk.PhotoImage(self.image.resize(size))
self.config(image=self.photoimage)
### test / demo code: ###
def main():
root = tk.Tk()
root.geometry('200x200')
win = ImageButton(root, image="ON.gif")
win.pack(fill=tk.BOTH, expand=True)
root.mainloop()
if __name__ == '__main__':
main()
Note that you MUST define the initial size of the window for this to work. If you don't then every resize will trigger it to grow some more.
Related
I want to show images in the Listbox tkinter widget. My code looks like this:
mylist = Listbox(self.labelFrame3, yscrollcommand=self.yscrollbar.set, selectmode=SINGLE)
mylist.grid(row=0, column=0, rowspan=21, columnspan=21, sticky=(N, S, E, W))
img = Image.open('modeldata/photosamples/amanullahattamuhammad2.0.jpg') # PIL solution
img = img.resize((200, 200), Image.ANTIALIAS) # The (250, 250) is (height, width)
img = ImageTk.PhotoImage(img)
How to print this image in Listbox?
As someone mentioned in a comment you can't put images in a Listbox, but as I mentioned in another, you could use a Text widget instead because you can put images into them. Below is a relatively simple demonstration of how something like that can be done. Its two buttons illustrate how it can display just text or a combination of the two simultaneously. Text widget are actually very versatile.
Anyway, since presumably you want a scrollable list, the Text subclass named tkinter.scrolledtext is used instead of plain one to save work. Since it's a subclass, it can do anything its baseclass can do.
from pathlib import Path
from PIL import Image, ImageTk
import tkinter as tk
from tkinter.constants import *
from tkinter.scrolledtext import ScrolledText
class App:
def __init__(self, image_folder_path, image_file_extensions):
self.root = tk.Tk()
self.image_folder_path = image_folder_path
self.image_file_extensions = image_file_extensions
self.create_widgets()
self.root.mainloop()
def create_widgets(self):
self.list_btn = tk.Button(self.root, text='List Images', command=self.list_images)
self.list_btn.grid(row=0, column=0)
self.show_btn = tk.Button(self.root, text='Show Images', command=self.show_images)
self.show_btn.grid(row=1, column=0)
self.text = ScrolledText(self.root, wrap=WORD)
self.text.grid(row=2, column=0, padx=10, pady=10)
self.text.image_filenames = []
self.text.images = []
def list_images(self):
''' Create and display a list of the images the in folder that have one
of the specified extensions. '''
self.text.image_filenames.clear()
for filepath in Path(self.image_folder_path).iterdir():
if filepath.suffix in self.image_file_extensions:
self.text.insert(INSERT, filepath.name+'\n')
self.text.image_filenames.append(filepath)
def show_images(self):
''' Show the listed image names along with the images themselves. '''
self.text.delete('1.0', END) # Clear current contents.
self.text.images.clear()
# Display images in Text widget.
for image_file_path in self.text.image_filenames:
img = Image.open(image_file_path).resize((64, 64), Image.ANTIALIAS)
img = ImageTk.PhotoImage(img)
self.text.insert(INSERT, image_file_path.name+'\n')
self.text.image_create(INSERT, padx=5, pady=5, image=img)
self.text.images.append(img) # Keep a reference.
self.text.insert(INSERT, '\n')
image_folder_path = 'modeldata/photosamples'
image_file_extensions = {'.jpg', '.png'}
App(image_folder_path, image_file_extensions)
Here's it running on a folder on my computer with the images shown:
I'm trying to write a script to accept an image, then process the image and put a grid over it. I haven't merged the script that modifies the image yet. I'm trying to put a front end on this, I'm planning on publishing the script in a DnD facebook group for others to use to overlay grids onto their battlemaps. I can't seem to have the GUI update the labels which display the pixel length of the image the user selects.
import tkinter as tk
from tkinter import filedialog
import imageGrid
import sys
from PIL import *
from PIL import Image
root= tk.Tk()
root.withdraw()
iWidth = tk.StringVar()
iHeight = tk.StringVar()
class pinger(tk.Tk):
def __init__(self, parent):
tk.Tk.__init__(self, parent)
self.parent = parent
self.initialize()
def initialize(self):
self.grid()
button = tk.Button(self,text="exit",command=lambda: closeProgram())
button.grid(column=3,row=9)
buttonOpen = tk.Button(self, text="Select an Image", command= lambda: openExplorer()
)
buttonOpen.grid(column=2, row=2)
labelSig = tk.Label(self, text='By Johnathan Keith, 2020. Ver 1.0')
labelSig.grid(column=3,row=10)
labelImgWidth = tk.Label(self, textvariable=iWidth)
labelImgWidth.grid(column=2,row=3)
labelStaticImg= tk.Label(self, text="Width of image, in pixels: ")
labelStaticImg.grid(column=1,row=3)
labelStaticHeight= tk.Label(self, text="Height of image, in pixels: ")
labelStaticHeight.grid(column=3,row=3)
labelImgHeight = tk.Label(self, textvariable=iHeight)
labelImgHeight.grid(column=4,row=3)
labelWidth = tk.Label(self, text='Enter the width of the grid, in pixels.')
labelWidth.grid(column=4,row=2)
labelDisclaim = tk.Label(self, text='Currently only works with jpegs')
labelDisclaim.grid(column=2, row=1)
def openFile(imagefilename):
Img = Image.open(imagefilename)
height, width = Img.size
iHeight.set(height)
iWidth.set(width)
def closeProgram():
app.destroy()
sys.exit()
def openExplorer():
app.filename= filedialog.askopenfilename(initialdir="/", title="Select an Image", filetypes=(("jpeg files", "*.jpg"),("all files", "*.*")))
if app.filename:
print(app.filename)
pinger.openFile(app.filename)
if __name__ == "__main__":
app = pinger(None)
app.title('Image Gridder')
app.minsize(height=680,width=480)
app.mainloop()
I've been searching through other SE questions and none of them seemed to work with the way my code is written. I'm trying to update the StringVar()'s iWidth and iHeight which will eventually allow the user to specify how they want the grid to overlay the image. I've tried moving them all over the code, in and out of the class, and nothing works. Also, StackExchange kinda butchered the indentation, so don't mind that.
Thank you!
It is because you have two instances of Tk(): root and app (pinger). StringVar iWidth and iHeight are within root scope, other widgets are within app scope. So the content of the StringVars are not shown in widgets within app.
You can remove root stuff and only have app as the only instance of Tk():
import tkinter as tk
from tkinter import filedialog
from PIL import Image
class pinger(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.initialize()
def initialize(self):
self.iWidth = tk.StringVar()
self.iHeight = tk.StringVar()
# row 1
labelDisclaim = tk.Label(self, text='Currently only works with jpegs')
labelDisclaim.grid(column=2, row=1)
# row 2
labelWidth = tk.Label(self, text='Enter the width of the grid, in pixels.')
labelWidth.grid(column=4,row=2)
buttonOpen = tk.Button(self, text="Select an Image", command=self.openExplorer)
buttonOpen.grid(column=2, row=2)
# row 3
labelStaticImg= tk.Label(self, text="Width of image, in pixels: ")
labelStaticImg.grid(column=1,row=3)
labelImgWidth = tk.Label(self, textvariable=self.iWidth)
labelImgWidth.grid(column=2,row=3)
labelStaticHeight= tk.Label(self, text="Height of image, in pixels: ")
labelStaticHeight.grid(column=3,row=3)
labelImgHeight = tk.Label(self, textvariable=self.iHeight)
labelImgHeight.grid(column=4,row=3)
# row 9
button = tk.Button(self,text="exit",command=self.closeProgram)
button.grid(column=3,row=9)
# row 10
labelSig = tk.Label(self, text='By Johnathan Keith, 2020. Ver 1.0')
labelSig.grid(column=3,row=10)
def openFile(self, imagefilename):
Img = Image.open(imagefilename)
height, width = Img.size
self.iHeight.set(height)
self.iWidth.set(width)
def closeProgram(self):
self.destroy()
def openExplorer(self):
filename= filedialog.askopenfilename(initialdir="/", title="Select an Image", filetypes=(("jpeg files", "*.jpg"),("all files", "*.*")))
if filename:
print(filename)
self.openFile(filename)
if __name__ == "__main__":
app = pinger()
app.title('Image Gridder')
app.minsize(height=680,width=480)
app.mainloop()
Ok, so i have a problem and tried way too long to fix it. I dont know how to resize an image without it breaking my layout. In case (1) it fills up the whole space thus destroying my layout and i think it is because i call the configure function in a function that is bound to the <Configure> event. In case (2) i can resize the image but i have a huge border around the scaled image which destroys the layout. I even tried to use a Canvas instead of the Label but just drawing the Canvas destroys the layout, because it takes up too much space and shifts everything to the right.
Ideas how to fix it?
from tkinter import *
from PIL import ImageTk, Image
global image_label, image_ref
def main():
global image_label
root = Tk()
root.geometry("600x400")
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
content_frame = Frame(root, bg="#FF0000")
content_frame.grid(row=0, column=0, sticky="news")
column_ratio = [10,25,10,45,10]
row_ratio = [10,80,10]
for col_idx in range(5):
content_frame.grid_columnconfigure(col_idx, weight=column_ratio[col_idx])
for row_idx in range(3):
content_frame.grid_rowconfigure(row_idx, weight=row_ratio[row_idx])
image_label = Label(content_frame, bg="#00FF00")
image_label.grid(row=1, column=1, sticky="news")
#image_label.bind("<Configure>", resize_configure) # <--- (1)
second_label = Label(content_frame, bg="#0000FF", text="Label on the right")
second_label.grid(row=1, column=3, sticky="news")
second_label.bind("<Button-1>", resize_click) # <--- (2)
root.mainloop()
def resize(size, label):
global image_ref
image = Image.open("test.png").resize(size, Image.ANTIALIAS)
image_ref = ImageTk.PhotoImage(image)
label.configure(image=image_ref)
def resize_configure(event):
global image_label
size = (event.width, event.height)
resize(size, image_label)
def resize_click(event):
global image_label
image_label.update()
size = (image_label.winfo_width(), image_label.winfo_height())
resize(size, image_label)
if __name__ == "__main__":
main()
Case 1 as an image
Case 2 as an image
How it should look like
You can use place() instead of grid():
import tkinter as tk
from PIL import Image, ImageTk
base_img = Image.open('test.png')
def on_label_resize(event):
lbl = event.widget
img = ImageTk.PhotoImage(base_img.resize((event.width, event.height)))
lbl.config(image=img)
lbl.image = img
def main():
root = tk.Tk()
root.geometry('640x400')
img_lbl = tk.Label(root, image=None, bg='#00ff00')
img_lbl.place(relx=.1, rely=.1, relwidth=.25, relheight=.8)
img_lbl.bind('<Configure>', on_label_resize)
txt_lbl = tk.Label(root, text='Label on the right', bg='#0000ff')
txt_lbl.place(relx=.45, rely=.1, relwidth=.45, relheight=.8)
root.mainloop()
if __name__ == '__main__':
main()
I am developing a GUI on Python and I have the following problem: I want to have the picture at the top of the window and buttons right underneath it. I am using the Tkinter module and whatever geometry I use (place, pack or grid) the buttons don't move. The are only displayed if I move the image using grid to row 1 (which is the second row), otherwise they don't appear at all. Here is the code I am using for now. For reference the picture has dimensions of 291x87 pixels.
import Tkinter
from Tkinter import *
def main():
window =Tk()
window.geometry("300x300")
window.title("Dienes Blocks Application")
window.iconbitmap(default='favicon.ico')
app = HomeScreen(window)
window.mainloop()
class HomeScreen(Frame):
def __init__(self, master):
Frame.__init__(self,master)
self.create_buttons()
self.sparx_head()
def sparx_head(self):
self.grid()
photo = Tkinter.PhotoImage(file="logosmall.gif")
sparx_header = Label(image=photo)
sparx_header.image = photo # keep a reference!
sparx_header.grid(column=0, row=0, columnspan=2, rowspan=2, sticky='NSEW')
def create_buttons(self):
self.grid()
#teacher button
teacher_button = Tkinter.Button(self, text="Teacher")
teacher_button.grid(column=0, row=10)
# student button
student_button = Tkinter.Button(self, text="Student")
student_button.grid(column=2, row=10)
# prototype button
prototype_button = Tkinter.Button(self, text="Prototype")
prototype_button.grid(column=1, row=10)
if __name__ == "__main__":
main()
You just need to use self in the image label:
sparx_header = Label(self,image=photo)
They were not having the same parent & that's why you were having this problem
This seems like a pretty straightforward question, but i am having trouble displaying a jpg image when a button is clicked. Here is my code (without the button code for the sake of time):
from tkinter import *
#screen stuff here
canvas = Canvas(app)
canvas.grid(row = 0,column = 0)
photo = PhotoImage(file = "test.jpg")
canvas.create_image(0,0, image = photo)
def show_image():
global canvas
global photo
canvas.create_image(0,0, image = photo)
#button that calls the function down here
Thanks!
This works with Python2:
import Tkinter as tk
import ImageTk
def show_image():
x = canvas.create_image(125, 125, image=tk_img)
while True:
print('show')
canvas.itemconfigure(x, state=tk.NORMAL)
button.configure(text = 'Hide')
yield
print('hide')
canvas.itemconfigure(x, state=tk.HIDDEN)
button.configure(text = 'Show')
yield
root = tk.Tk()
canvas = tk.Canvas(root, width=250, height=250)
canvas.grid(row=0, column=0)
tk_img = ImageTk.PhotoImage(file='image.png')
button = tk.Button(
root, text="Show", command=show_image().next, anchor='w',
width=10, activebackground="#33B5E5")
button.grid(row=1, column=0)
root.mainloop()
In Python3, PhotoImage can open GIF, PPM/PGM images. To open other formats, you may need to install Pillow (a fork of the PIL project for Python3).