How to make button command to open image inside window in tkinter? - python

I am attempting to create a very simple image viewer in tkinter with two simple buttons view and close. I have the close button functioning as intended but I am unable to get my view button to open the specified image in my file directory. I have tried importing ImageTK to write a button command to open it but have so far been unsuccessful.
import tkinter as tk
from PIL import ImageTk,Image
class image_viewer:
def __init__(self, win):
self.root = win
self.root.title('ImageViewer')
self.root.geometry('400x350')
self.btnView = tk.Button(text='View', command= ImageTk.PhotoImage(Image.open(r"C:\Users\SteveSmith\eclipse-workspace\SteveSmith-ex1\src\raw\pythonIsFun.jpg")))
self.btnView.pack(side=tk.LEFT)
self.btnView.place(x=20, y=265)
self.btnClose = tk.Button(text='close', command=self.root.destroy)
self.btnClose.pack(side=tk.LEFT)
self.btnClose.place(x=65, y=265)
def main():
root = tk.Tk()
image_viewer(root)
root.mainloop()
if __name__ == '__main__':
main()

There are a number of errors in your code and previously I closed it after picking one of them and marking it as a duplicate of another question that had been asked and answered before that covered that problem.
However, based on comments you made and after thinking it over, I decided to reopen it and attempt to address all or at least most of the issues I saw — otherwise it would likely have taken you quite a while to get everything fixed.
Here's the result:
from PIL import ImageTk, Image
import tkinter as tk
class ImageViewer:
def __init__(self, root, image_filename):
self.root = root
self.image_filename = image_filename
self.root.title('ImageViewer')
self.root.geometry('400x350')
self.canvas = tk.Canvas(self.root, width=300, height=300)
self.canvas.place(x=10, y=10)
self.btnView = tk.Button(text='View', command=self.view_image)
self.btnView.place(x=20, y=265)
self.btnClose = tk.Button(text='close', command=self.root.destroy)
self.btnClose.place(x=65, y=265)
def view_image(self):
self.img = ImageTk.PhotoImage(Image.open(self.image_filename)) # Keep ref to image.
self.canvas.create_image(20, 20, anchor=tk.NW, image=self.img)
def main(image_filename):
root = tk.Tk()
ImageViewer(root, image_filename)
root.mainloop()
if __name__ == '__main__':
main(r"C:\Users\SteveSmith\eclipse-workspace\SteveSmith-ex1\src\raw\pythonIsFun.jpg")

Related

Tkinter not displaying default Entry variable until button is added

I am trying to create a GUI in Python 3.9.1 with tkinter and am having issues getting a default value to show in an Entry box.
from tkinter import *
from tkinter import ttk
class GUI:
def __init__(self, root, *args):
self.root = root
self.initial_frame = ttk.Frame(self.root)
self.initial_frame.grid(row=0)
ttk.Label(self.initial_frame, text="User question here?").grid(row=1, column=1)
self.ext_len = StringVar(value=4)
ext_len_entry = ttk.Entry(self.initial_frame, textvariable=self.ext_len)
ext_len_entry.grid(row=1, column=2)
root = Tk()
GUI(root)
root.mainloop()
Note: official tkinter docs say to use from tkinter import * and from tkinter import ttk.
See this link and look in the Python section in the step-by-step walkthrough https://tkdocs.com/tutorial/firstexample.html
I've also tried using self.ext_len.set(4) after initializing the entry but before putting it on the grid. I've tried self.ext_len.insert(0,4) as well.
If I add a button with a callback that does nothing, the 4 shows up in the entry box. I found that I don't even have to render it on the grid. Just initializing it is enough. A button without a callback does not work.
Working code:
from tkinter import *
from tkinter import ttk
class GUI:
def __init__(self, root, *args):
self.root = root
self.initial_frame = ttk.Frame(self.root)
self.initial_frame.grid(row=0)
ttk.Label(self.initial_frame, text="User question here?").grid(row=1, column=1)
self.ext_len = StringVar(value=4)
ext_len_entry = ttk.Entry(self.initial_frame, textvariable=self.ext_len)
ext_len_entry.grid(row=1, column=2)
ttk.Button(self.initial_frame, text="Test button", command=self.test_func)
def test_func(self, *args):
pass
root = Tk()
GUI(root)
root.mainloop()
Why does initializing the button cause it to work?

How to make a file open by default with tkinter app?

I made a tkinter app for displaying images, and I was wondering if there's a way to make an image open by default with this app
At the moment if I try that, I get an error for some non declared icon file (this is the icon that appears near the name of the app at the top)
There's no real goal behin this Gui, I'm just experimenting and learning.
Thanks
Try this:
from PIL import Image, ImageTk
import tkinter as tk
from tkinter import filedialog
my_img = []
def FileImport():
file = filedialog.askopenfilename()
my_img.clear()
my_img.append(ImageTk.PhotoImage(Image.open(file)))
label1.config(image=my_img[0])
root= tk.Tk()
root.title('Main')
root.geometry('400x400')
label = tk.Label(root, text = "Browse", fg="purple")
label.pack()
button = tk.Button(root, text='See Image',fg="blue", command=FileImport)
button.pack()
my_img.append(ImageTk.PhotoImage(Image.open(your_first_image)))
label1 = tk.Label(root, image = my_img[0])
label1.pack(pady= 50)
root.mainloop()
When you run this:
After you tap to see different image from computer:
Hope It helps!

I can't get an image to load on the canvas in Tkinter python

I am having a big issue. The Canvas loads perfectly but the image does not display.
I started Python 1 week ago and I have no clue why does is not working. Can anyone please show me the way to solve the issue of the image not loading on the canvas?
from Tkinter import *
from PIL import ImageTk
from PIL import Image
class Fake_Virus:
def __init__(self, master):
self.master = master
master.title("Totally not a virus!")
b = Button(master, text="Help", command=self.prank)
b.pack(padx=10, pady=10, side=LEFT)
quit = Button(master, text="Close", command=self.close_window)
quit.pack(padx=10, pady=10, side=RIGHT)
photo = PhotoImage("eh.gif")
label = Label(image=photo)
label.image = photo # keep a reference!
label.pack()
f = Frame(master, height=150, width=150)
f.pack_propagate(0) # don't shrink
f.pack()
def prank(self):
print "work"
return
def close_window(self):
root.destroy()
return
root = Tk()
my_gui = Fake_Virus(root)
root.mainloop()
You should use the file option to initialize the photo image object.
This means you need to change photo = PhotoImage("eh.gif") to photo = PhotoImage(file="eh.gif")
Now your code will work. But a working code is not necessarily a good code. There are other issues with your code. Let me go through them quickly:
It is better to code import Tkinter as Tk than from Tkinter import *
Why that hyphen in your class name? Follow PEP8 so that, in the futur, people will find it easy to review and understand your code.
Good that you have written self.master = master (read complete code to know why) but then you have never used it. This means you made a good decision and you render it useless.
You set the title of the window within the initializer. It is better if you do that in a separate function so that whenever you want to add additional settings to your GUI (such as the size, font or whatever) you will only add code to that function instead of vomiting lot of trash inside the initializer which rather needs to be clean.
None of the widgets you created is 'selfed' (you may read Why explicit self has to stay)
It is better you create the widgets in a separate function otherwise your __init__() will be dirty.
Why do you use return in prank() and close_window()? By default, Python functions that do not return something return None anyway so it is useless to code that.
Why did you pack one button to left and the other one to right and then no pack siding for the label? Read about the pack() geometry manager.
Why you did not attach the label to a parent widget as you did for the 2 other buttons? All Tkinter widgets need to be clung into a parent widget. The grand parent of those widgets is an instance of Tkinter.Tk()
Why did you create that frame and then you never used it? You are not doing anything with it, so ..?
Given these remarks, I want to provide you an improved -but not perfect- version of your program. You can then follow this 'philosophy' to add or modifying existing widgets:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import Tkinter as Tk
from PIL import ImageTk
class FakeVirus:
def __init__(self, master):
self.master = master
self.configure_gui()
self.create_widgets()
def configure_gui(self):
self.master.title('Totally not a virus!')
def create_widgets(self):
self.create_buttons()
self.create_label_for_image()
def create_buttons(self):
self.help = Tk.Button(self.master, text='Help', command=self.prank)
self.help.pack(side=Tk.LEFT)
self.quit = Tk.Button(self.master, text='Close', command=self.close_window)
self.quit.pack(side=Tk.LEFT)
def create_label_for_image(self):
self.image_label = Tk.Label(self.master)
self.image_label.pack(side=Tk.LEFT)
self.load_image_to_label()
def load_image_to_label(self):
self.photo = ImageTk.PhotoImage(file='eh.gif')
self.image_label.image = self.photo
self.image_label.config(image=self.photo)
def prank(self):
print "work"
def close_window(self):
root.destroy()
if __name__ == '__main__':
root = Tk.Tk()
my_gui = FakeVirus(root)
root.mainloop()
The output of the above program is:

Saving canvas from tkinter to file

So I followed this post:
How can I convert canvas content to an image?
And when I tried to do as mentioned in the last suggestion, I get the following problem:
When I call it out like this, the image/screenshot is taken too early and therefore the required image won't be captured. Here's the code:
from tkinter import *
from PIL import ImageGrab
root = Tk()
cv = Canvas(root)
cv.pack()
cv.create_rectangle(10,10,50,50)
#cv.create_line([0, 10, 10, 10], fill='green')
cv.update()
#print(root.winfo_width())
def getter(widget):
x=root.winfo_rootx()+widget.winfo_x()
print(x)
y=root.winfo_rooty()+widget.winfo_y()
print(y)
x1=x+widget.winfo_width()
print(x1)
y1=y+widget.winfo_height()
print(y1)
ImageGrab.grab().crop((x,y,x1,y1)).save("em.jpg")
getter(cv)
root.mainloop()
By the way, if there is a simplier solution, I would appreciate it!
The thing is that the saving part will be added to the code dynamically later so the solution should be as light as possible.
Thanks in advance!
PS: Maybe it is even possible to save the canvas without displaying it beforehand? Just by the code?
Below is the code for taking screenshots of just the tkinter canvas. PIL.ImageGrab module does not work in Linux; replaced that with pyscreenshot. Tested this code in Ubuntu 16.04. You may have to check if it operates in Windows/OSx. Please note the remarks in function self._grabtofile.
Remark: In Ubuntu, this script had to be executed directly on commandline/terminal to work. It did not work when executed from IDLE for python3.
Summary:
Able to display the screenschoot of a tkinter canvas and save it to file a
using two events.
Able to screenshot tkinter canvas (w/o displaying it) and save it to
file using one event.
Working code:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
try:
import tkinter as tk # Python 3 tkinter modules
except ImportError:
import Tkinter as tk # Python 2 tkinter modules
from PIL import Image, ImageTk
#from PIL import Image, ImageTk, ImageGrab # For Windows & OSx
import pyscreenshot as ImageGrab # For Linux
class App(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent=parent
file = 'images.jpg'
self.img = Image.open(file)
#self.img.show() #Check to proof image can be read in and displayed correctly.
self.photo = ImageTk.PhotoImage(self.img)
print('size of self.img =', self.img.size)
centerx= self.img.size[0]//2
centery= self.img.size[1]//2
print ('center of self.img = ', centerx, centery)
self.cv = tk.Canvas(self)
self.cv.create_image(centerx, centery, image=self.photo)
self.cv.create_rectangle(centerx*0.5,centery*0.5,centerx*1.5,centery*1.5,
outline='blue')
self.cv.grid(row=0, column=0, columnspan=3, sticky='nsew')
self.snappic=tk.Button(self, text='SNAP', command=self._snapCanvas)
self.snappic.grid(row=1, column=0, sticky='nsew')
self.savepic=tk.Button(self, text='SAVE', command=self._save)
self.savepic.grid(row=1, column=1, sticky='nsew')
self.directsavepic=tk.Button(self, text='Grab_to_File', command=self._grabtofile)
self.directsavepic.grid(row=1, column=2, sticky='nsew')
self.snapsave=tk.Button(self, text='SNAP & SAVE', command=self._snapsaveCanvas)
self.snapsave.grid(row=2, column=0, columnspan=2, sticky='nsew')
def _snapCanvas(self):
print('\n def _snapCanvas(self):')
canvas = self._canvas() # Get Window Coordinates of Canvas
self.grabcanvas = ImageGrab.grab(bbox=canvas)
self.grabcanvas.show()
def _save(self):
self.grabcanvas.save("out.jpg")
print('Screenshoot of tkinter.Canvas saved in "out.jpg"')
def _grabtofile(self):
'''Remark: The intension was to directly save a screenshoot of the canvas in
"out_grabtofile.png".
Issue 1: Only a full screenshot was save.
Issue 2: Saved image format defaults to .png. Other format gave errors.
Issue 3: "ImageGrab.grab_to_file" only able to return full screenshoot
and not just the canvas. '''
print('\n def _grabtofile(self):')
canvas = self._canvas() # Get Window Coordinates of Canvas
print('canvas = ', canvas)
ImageGrab.grab_to_file("out_grabtofile.png", ImageGrab.grab(bbox=canvas))
print('Screenshoot of tkinter.Canvas directly saved in "out_grabtofile.png"')
def _snapsaveCanvas(self):
print('\n def _snapsaveCanvas(self):')
canvas = self._canvas() # Get Window Coordinates of Canvas
self.grabcanvas = ImageGrab.grab(bbox=canvas).save("out_snapsave.jpg")
print('Screencshot tkinter canvas and saved as "out_snapsave.jpg w/o displaying screenshoot."')
def _canvas(self):
print(' def _canvas(self):')
print('self.cv.winfo_rootx() = ', self.cv.winfo_rootx())
print('self.cv.winfo_rooty() = ', self.cv.winfo_rooty())
print('self.cv.winfo_x() =', self.cv.winfo_x())
print('self.cv.winfo_y() =', self.cv.winfo_y())
print('self.cv.winfo_width() =', self.cv.winfo_width())
print('self.cv.winfo_height() =', self.cv.winfo_height())
x=self.cv.winfo_rootx()+self.cv.winfo_x()
y=self.cv.winfo_rooty()+self.cv.winfo_y()
x1=x+self.cv.winfo_width()
y1=y+self.cv.winfo_height()
box=(x,y,x1,y1)
print('box = ', box)
return box
if __name__ == '__main__':
root = tk.Tk()
root.title('App'), root.geometry('300x300')
app = App(root)
app.grid(row=0, column=0, sticky='nsew')
root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
app.rowconfigure(0, weight=10)
app.rowconfigure(1, weight=1)
app.columnconfigure(0, weight=1)
app.columnconfigure(1, weight=1)
app.columnconfigure(2, weight=1)
app.mainloop()
Screenshot of GUI:
Screenshot of GUI's tk.Canvas:
I know this is an old post but I just want to share what worked for me, hopefully it will help somebody with similar problem.
import tkinter as tk
from PIL import Image
import io
import os
import subprocess
root = Tk()
cv = Canvas(root)
cv.pack()
cv.create_rectangle(10,10,50,50)
ps = cv.postscript(colormode='color')
img = Image.open(io.BytesIO(ps.encode('utf-8')))
img.save('filename.jpg', 'jpeg')
root.mainloop()
In this solution, you have to have ghostscript and Pillow installed.
For MacOS, install ghostscript with
brew install ghostscript
and Pillow with
pip install Pillow

askdirectory() changes focus to different window

I am using Tkinter to build two windows. One the main one, pressing a button leads to the creation of the second window.
This second window does not get focus immediately when it created. That I am able to fix by calling .focus_force(). However, when I call the askdirectory() function from tkFileDialog, the focus changes back to the first window.
How can I prevent that focus switch from happening, without simply calling focus_force() all over the place?
To replicate problem:
from Tkinter import *
from tkFileDialog import *
class app:
def __init__(self, master):
Button(master, command = make_new).grid()
def make_new(self):
root = Tk()
new = new_win(root)
root.mainloop() #here the focus is on the first window
class new_win:
def __init__(self, master):
f = askdirectory() #even after placing focus on second window,
#focus goes back to first window here
I am using Python 2.7.3. Thanks!
the little-documented wm_attributes method might help:
from Tkinter import *
import tkFileDialog
root = Tk()
top = Toplevel()
top.wm_attributes('-topmost', 1)
top.withdraw()
top.protocol('WM_DELETE_WINDOW', top.withdraw)
def do_dialog():
oldFoc = top.focus_get()
print tkFileDialog.askdirectory()
if oldFoc: oldFoc.focus_set()
b0 = Button(top, text='choose dir', command=do_dialog)
b0.pack(padx=100, pady=100)
def popup():
top.deiconify()
b0.focus_set()
b1 = Button(root, text='popup', command=popup)
b1.pack(padx=100, pady=100)
root.mainloop()

Categories