Displaying Images Using Python Events - python-3.x

I am creating a small program that should display an image overlayed the main window based on a button click event. However, whenever I test the code that should be doing this, I get this error: AttributeError: 'int' object has no attribute '_create'
Here is the appropriate code:
def display_image(event):
fail_image = tk.PhotoImage(file="./RedXMark.png")
Canvas.create_image(0, 0, image=fail_image, anchor="nw")
# Later in the code:
root.bind('<t>', display_image)
I have been trying to use Tkinter's labels to display the image and, while I wouldn't get an error, it wouldn't display the image. The image is in the same folder as the program and is saved with that exact name.
Any advice would be welcome!

You must call create_image on an instance of the Canvas class, but you're calling it on the class itself.
something = tk.Canvas(...)
...
something.create_image(0, 0, image=fail_image, anchor="nw")

Related

Problem in saving Python tkinter Canvas drawing as an image file using PIL Image and ImageDraw functions

Problem statement
I have written a code in Python on which a user can draw on a tkinter GUI canvas. The program is capable of taking the app's screenshot but saving the canvas content to a viewable image file (such as .jpg or .png) is not properly done yet.
Screenshots
What I draw on the tkinter app's canvas.
What the code (given below) saved.
What I expect.
I have tried and will expect
A number of possible solutions from the internet (including Stack Overflow) have been tried but somehow the process cannot be completed successfully. After drawing whatever on the canvas, the saved image will have nothing but a dot whether it is in *.jpg or *.png. I need the image of the full canvas with all the colorful drawings on it in that saved image file.
For any reference, here is my code
from tkinter import Tk, Canvas, Button
from PIL import Image, ImageDraw
X = 0
Y = 0
app = Tk()
def set_xy(click):
global X, Y
X, Y = click.x, click.y
def draw_line(drag):
global X, Y
draw_panel.create_line((X, Y, drag.x, drag.y), fill="black", capstyle="round", smooth=True)
draw.line([(X, Y), (drag.x, drag.y)], fill="black", joint="curve")
X, Y = drag.x, drag.y
def save(event=None):
# ATTENTION: This is not working properly
image.save("image.png", "png")
# App's drawing panel
draw_panel = Canvas(app, background="white", cursor="dot")
draw_panel.place(x=0, relwidth=1.0, y=0, relheight=1.0)
# Invisible canvas for saving as an image later on
image = Image.new("RGB", (draw_panel.winfo_width(), draw_panel.winfo_height()), "white")
draw = ImageDraw.Draw(image)
# Mouse and Key bindings
draw_panel.bind('<Button-1>', set_xy)
draw_panel.bind('<B1-Motion>', draw_line)
app.bind('<s>', save) # press 's' to save the drawing as .png
app.mainloop()
Addition Informations:
HOST: Windows 10 Version 20H2 (OS Build 19042.2130)
IDE: PyCharm 2022.2 (Community Edition) (Build #PC-222.3345.131)
Language: Python 3.10.6
Framework: tkinter Tk/Tcl 8.6
Library: pillow 9.2.0
Any solution shall be highly appreciated, and I shall be thankful.
That's a nice idea and there is just one piece missing. You set the size of the image to the size of the canvas before you call mainloop() for the first time. The canvas does not have a size at this time because the window has not been initiated, yet.
You need to force an update before getting the width and height. Then, it works fine:
# Invisible canvas for saving as an image later on
draw_panel.update()
image = Image.new("RGB", (draw_panel.winfo_width(), draw_panel.winfo_height()), "white")
draw = ImageDraw.Draw(image)
If you print the draw_panel.winfo_width() before and after calling the update, you see what I mean. Before update(), width and height are both 1. Afterwards, they are 200, as they should be.

PhotoImage does not recognize PNG image - Keep getting following error: _tkinter.TclError: couldn't recognize data in image file "MyImage.png"

I'm currently learning Tkinter, and I wanted to add a PNG image to my Tkinter Canvas object. The code is shown below.
window = Tk()
window.title('My Title')
canvas = Canvas(width=200, height=200)
canvas.pack()
img = PhotoImage(file='MyImage.png')
canvas.create_image(100, 100, image=img)
window.mainloop()
Seems simple enough, but I keep getting the following error: _tkinter.TclError: couldn't recognize data in image file "MyImage.png"
I read that PhotoImage will return this error if you try to read a .jpg file, however, it should work with PNG files. I've also tried out PIL, but that came with a whole bunch of other issues. Any suggestions?

Unable to display image label in second Tkinter frame

I am trying to build a simple gui using Tkinter. The application involves a smaller secondary frame opening up over the primary one upon pressing a button. This secondary frame must contain an image. Image labels appear easily on the primary frame, but on the secondary frame, the image label appears as an empty box the size of the image, with whatever background colour I set.
Here's how I'm doing it:
#send diagram page
def send_diagram():
send_diagram_frame=tk.Frame(frame, bg="#D4BAEC")
send_diagram_frame.place(relx=0.5, rely=0.5, relheight=0.7, relwidth=0.7, anchor="center")
send_diagram_entry_working_image=Image.open('/home/raghav/RemEdi/design/assets/generic_page_entry.png')
send_diagram_entry_image=ImageTk.PhotoImage(send_diagram_entry_working_image)
send_diagram_entry_label=tk.Label(send_diagram_frame, image=send_diagram_entry_image)
send_diagram_entry_label.place(relx=0.5, rely=0.5, anchor="center")
return
As visible, send_diagram() is the command for the button.
I have tried adding another smaller frame inside the secondary frame to contain the image, but that did not work either.
Any help would be greatly helpful. Thanks!
You are creating the new image inside a function, with it's own local namespace. When the function ends the reference to the image will be garbage collected.
You can fix this by saving a reference to the image in the Label widget. Put this line in the function after the image is created:
send_diagram_entry_label.image = send_diagram_entry_image
Here is working code. I have try it and it's working for me.
def make_label_image(parent, img):
label = tk.Label(parent, image=img)
label.place(relx=0.5, rely=0.5, anchor="center")
def send_diagram():
send_diagram_fram = tk.Frame(frame, bg="#D4BAEC")
send_diagram_frame.place(relx=0.5, rely=0.5, relheight=0.7, relwidth=0.7, anchor="center")
send_diagram_fram.pack_propagate(0)
send_diagram_fram.pack()
img = ImageTk.PhotoImage(Image.open('C:/Users/xxxx/Desktop/logo.jpg'))
make_label_image(send_diagram_fram, img)

Tkinter: pack()ing frames that use grid()

I am working on a UI for a data-display applet. I started with a tutorial and have since expanded it well beyond the scope of the tutorial, but some legacy bits remain from the tutorial that are now causing me difficulty. In particular relating to pack() and grid().
Following the tutorial I have defined a class Window(Frame) object, which I then declare as app = Window(root) where root = Tk(). Within the Window object is an initializing function def init_window(self), where my problems arise. Here is the relevant code in init_window():
def init_window(self):
self.master.title('Data Explorer') #changing the widget title
self.pack(fill=BOTH,expand=1) # allow widget to take full space of root
# Initializing a grid to place objects on
self.mainframe = Frame(root)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )
self.mainframe.columnconfigure(0, weight = 1)
self.mainframe.rowconfigure(0, weight = 1)
self.mainframe.pack(pady = 10, padx = 10)
where the object self.mainframe contains a number of data selection dropdowns and buttons later on.
If I understand what this code is expected to do: it sets up the full window to be pack()ed with various frames. It then initializes a frame, self.mainframe, and within that frame initializes a grid(). Thus pack() and grid() do not collide. This setup was built by following the aforementioned tutorial.
This works correctly on my computer where I am developing the applet. However, when a collaborator compiles, they receive
_tkinter.TclError: cannot use geometry manager grid inside . which already has slaves managed by pack
on the line self.mainframe.grid(...). I have replaced the mainframe.pack() command with a mainframe.place() command, but this has not resolved the issue (since his compile does not reach that point); I have not figured out a way to remove the self.pack() command without causing all other elements of my UI to vanish.
Can anyone help us understand what is going wrong? For reference, we are both using MacOS, and compiling with Python3. I can provide additional information as requested, within limits.
The error is telling you exactly what is wrong. You can't use grid on a widget in the root window when you've already used pack to manage a widget in the root window.
You wrote:
It then initializes a frame, self.mainframe, and within that frame initializes a grid()
No, that is not what your code is doing. It is not setting up a grid within the frame, it's attempting to use grid to add the widget to the root window.
First you have this line of code which uses pack on a widget in the root window:
self.pack(fill=BOTH,expand=1)
Later, you try to use grid for another window in the root window:
self.mainframe = Frame(root)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )
The above isn't setting up a grid within self.mainframe, it's using grid to add the widget to the root window.
You need to use one or the other, you can't use both for different windows that are both direct children of the root window.
In other words, you're doing this:
self.pack(fill=BOTH,expand=1)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )
but since both self and self.mainframe are a direct child of the root window, you can't do that. You need to either use pack for both:
self.pack(fill=BOTH,expand=1)
self.mainframe.pack(...)
... or grid for both:
self.grid(...)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )

PhotoImage GIF won't appear (noob friendly please)

Alright, so I want to make a gif display when a function has been called, but the gif will go invisible and not show up. I searched for possible answers but all of them mention "create a reference to (insert code here)" and I don't really get it because:
1. 99% of them use objects and classes in which I have 0 experience
2. Some say to make a reference with "self.img = PhotoImage(...)" which I'm pretty sure its connected to objects and classes.
3. Others only say to create a reference.
Sorry for being somewhat rude. I'm just really fed up, I searched for answers for 2 hours now.
I tried to assign the variable to global, place the variable in the function and tried to remake the gif and rename the file
This is what I tried to do
def red_flicker():
global root
red_btn_flicker = tk.PhotoImage(file='test.gif')
label_red = tk.Label(image=red_btn_flicker)
label_red.place(x=red_btn_place_x, y=red_btn_place_y)
the gif is invisible.
Please be noob friendly.
Any stuff about python 2.7 and using objects/classes will be ignored
Ok so first things first.
Your function is adding a new label every time it is call so you probably should generate the label in the global namespace once and then just apply the image to the label in the function. This way you can call the function all you want without adding more labels.
I would also move your PhotoImage to the global so you do not need to keep reopening the image each time you load the function.
By making this change we do not even need to use global as the widget creating and image loading happens in the global already.
Make sure to save the reference to the image so its not garbage collected.
import tkinter as tk
root = tk.Tk()
red_btn_flicker = tk.PhotoImage(file='test.gif')
label_red = tk.Label(root)
label_red.pack()
def red_flicker():
label_red.config(image=red_btn_flicker)
label_red.image = red_btn_flicker # saving reference to image.
red_flicker()
root.mainloop()
You must save a reference, as mentioned in the answer to this question: Why does Tkinter image not show up if created in a function?
Since you aren't using classes, you can use a global variable. For example:
def red_flicker():
global red_btn_flicker
red_btn_flicker = tk.PhotoImage(file='test.gif')
label_red = tk.Label(image=red_btn_flicker)
label_red.place(x=red_btn_place_x, y=red_btn_place_y)
Another simple technique is to attach the image as an attribute of the label itself. This works because python lets you create custom attributes on an object. However, you must make sure that the reference to the label itself isn't lost
def red_flicker():
global label_red
red_btn_flicker = tk.PhotoImage(file='test.gif')
label_red = tk.Label(image=red_btn_flicker)
label_red.place(x=red_btn_place_x, y=red_btn_place_y)
label_red.image = red_btn_flicker

Resources