Exception AttributeError: when I try to get Tkinter to refresh image - python-2.7

I'm new to Tkinter, but I want to have my jpg image refreshed every 5 second, I've made this code, but I'm getting an exception attribute error.
can someone guide me ??
I receive this error:
Exception AttributeError: "'PhotoImage' object has no attribute '_PhotoImage__ph
oto'" in <bound method PhotoImage.__del__ of <PIL.ImageTk.PhotoImage object at 0
x754b16f0>> ignored
# -*- coding: utf-8 -*-
from Tkinter import *
from apscheduler.schedulers.background import BackgroundScheduler
from PIL import ImageTk, Image
import time
window = Tk()
scheduler = BackgroundScheduler()
a = 0
def readimage():
global a, img, img1, img2, imglab
a = a +1
img = Image.open("./web1.jpg")
img1 = img.resize((288, 162), Image.ANTIALIAS)
img2 = ImageTk.PhotoImage(img1)
readimage()
window.attributes('-fullscreen', True)
window.configure(background = "black")
endbutton = Button(window, text="exit", command=window.destroy)
endbutton.grid(row=1,column=1, sticky="nw")
alabel = Label(window, text=a)
alabel.grid(row=2, column=2, sticky="w")
imglab = Label(window, image=img2, bg="black",fg="white", font=("Arial", 18))
imglab.place(relx=.6, rely=1.0,anchor="sw")
imglab.lower()
window.update()
scheduler.add_job(readimage, 'interval', seconds=5)
scheduler.start()
while True:
time.sleep(1)
alabel[ "text"]=a
imglab[ "image"]=img2
window.update()
mainloop()

Related

Trying to modify a code to capture an image every 15 minutes or so (time lapse)

Below code was taken from an existing post by Kieleth which I use as a subset of my larger codebase. I'm trying to leverage it to capture a list of frames taken once every thousand+ real time frames and later play in a time-lapse fashion. I've captured the frames but can't seem to view them when calling a simple function. I've seen in other posts that for loops are not recommended for this type of event but haven't figured out how to properly display. Any advise on this one would be appreciated?
from tkinter import ttk
import time
import cv2
from PIL import Image,ImageTk
#import threading
root = Tk()
def video_button1():# this flips between start and stop when video button pressed.
if root.video_btn1.cget('text') == "Stop Video":
root.video_btn1.configure(text = "Start Video")
root.cap.release()
elif root.video_btn1.cget('text') == "Start Video":
root.video_btn1.configure(text = "Stop Video")
root.cap = cv2.VideoCapture(0)
show_frame()
def show_frame():
# if video_btn1.cget('text') == "Stop Video":
global time_lapse_counter
ret, frame = root.cap.read()
if ret:
frame = cv2.flip(frame, 1) #flip image
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img) #converts img into tkinter readable format
root.video_label.imgtk = imgtk
if time_lapse_counter >= 20: # for Every 20 frames, capture one into time_lapse_list
time_lapse_list.append(imgtk)
time_lapse_counter = 0
root.video_label.configure(image=imgtk)
if len(time_lapse_list) == 5: # keep only 4 frames in the list *** debug purposes.
time_lapse_list.pop(0)
time_lapse_counter += 1
video_loop = root.after(40, show_frame)
else:
root.video_btn1.configure(text = "Start Video")
def time_lapse_play():
root.cap.release() #stop capturing video.
for image in time_lapse_list:
print (image, " ", len(time_lapse_list)," ",time_lapse_list) #
#*** I see the print of the pyimagexxx but nothing appears on the video***#
imgtk = image
root.video_label.imgtk = imgtk
root.video_label.configure(image=imgtk)
cv2.waitKey(500)
# video_loop = root.after(500, time_lapse_play)
def setup_widgets(): #simple label and 2 button widget setup
#Setup Top Right Window with pictures
f_width, f_height = 810, 475
root.rightframe= Frame(root, border=0, width=f_width, height = f_height)
root.rightframe.grid(row=0, column=0, padx=10, pady=0)
# Show video in Right Frame
root.cap = cv2.VideoCapture(0)
root.cap.set(cv2.CAP_PROP_FRAME_WIDTH, f_width)
root.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, f_height)
root.video_label = Label(root.rightframe)
root.video_label.grid(row=0, column = 0)
root.video_btn1 = Button(root.rightframe, fg='maroon', bg="yellow", text = "Stop Video", font=("Arial",10),height=0, width = 10, command=video_button1)
root.video_btn1.grid(row=0, column = 1)
root.video_btn2 = Button(root.rightframe, fg='maroon', bg="yellow", text="Time Lapse", font=("Arial",10),height=0, width = 10, command=time_lapse_play)
root.video_btn2.grid(row=1, column = 1)
# Main Code
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
screen_resolution = str(screen_width)+'x'+str(screen_height)
root.geometry(screen_resolution)
time_lapse_counter = 0
time_lapse_list=[]
setup_widgets()
show_frame()
root.mainloop()```
I've finally figure this one out. Seems that the for loop effectively doesn't work when using callback. I've modified the code to remove the for loop. I'm sure it could use some improvements but it works. I'm not sure how to reset the image count within the list as pop/append grows the list image number over time and I wonder about an overflow error. i.e after a few minutes, the list will contain [pyimage100 - pyimage200], after a few hours, [pyimage1000000 - pyimage1000100].
from tkinter import *
import cv2
from PIL import Image,ImageTk
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg)
import matplotlib.pyplot as plt
root = Tk()
def video_button1():# this flips between start and stop when video button pressed.
if root.video_btn1.cget('text') == "Stop Video":
root.video_btn1.configure(text = "Start Video")
root.cap.release()
elif root.video_btn1.cget('text') == "Start Video":
root.video_btn1.configure(text = "Stop Video")
root.cap = cv2.VideoCapture(0)
root.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 400)
root.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 400)
show_frame()
def show_frame():
# if video_btn1.cget('text') == "Stop Video":
global time_lapse_counter
ret, frame = root.cap.read()
if ret:
frame = cv2.flip(frame, 1) #flip image
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img) #converts img into tkinter readable format
root.video_label.imgtk = imgtk
root.video_label.configure(image=imgtk)
if time_lapse_counter >= 20: # for Every 20 frames, capture one into time_lapse_list
time_lapse_list.append(imgtk)
time_lapse_counter = 0
if len(time_lapse_list) == 100: # keep 99 frames in the list
time_lapse_list.pop(0)
time_lapse_counter += 1
video_loop = root.after(40, show_frame)
else:
root.video_btn1.configure(text = "Start Video")
def time_lapse_play():
root.cap.release() #stop capturing video.
global frame_counter
if frame_counter <= len(time_lapse_list)-1:
imgtk = time_lapse_list[frame_counter] # get image from list
root.video_label.configure(image=imgtk) # update label with image from list
frame_counter += 1
video_loop = root.after(250, time_lapse_play) #wait 250ms until next frame
else:
frame_counter = 0 #reset frame_counter
def setup_widgets(): #simple label and 2 button widget setup
#Setup Top Right Window with pictures
f_width, f_height = 1200, 500
root.rightframe= Frame(root, border=0, width=f_width, height = f_height)
root.rightframe.grid(row=0, column=0, padx=10, pady=0)
# Show video in Right Frame
root.cap = cv2.VideoCapture(0)
root.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 400)
root.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 400)
root.video_label = Label(root.rightframe)
root.video_label.grid(row=0, column = 0)
root.video_btn1 = Button(root.rightframe, fg='maroon', bg="yellow", text =
"Stop Video", font=("Arial",10),height=0, width = 10, command=video_button1)
root.video_btn1.grid(row=0, column = 1)
root.video_btn2 = Button(root.rightframe, fg='maroon', bg="yellow", text="Time Lapse", font=("Arial",10),height=0, width = 10, command=time_lapse_play)
root.video_btn2.grid(row=1, column = 1)
fig = plt.figure(1)
canvas = FigureCanvasTkAgg(fig, root.rightframe)
canvas.get_tk_widget().place(x=700,y=0)
canvas.get_tk_widget().config(border=2, bg="yellow", width=400, height=400)
# Main Code
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
screen_resolution = str(screen_width)+'x'+str(screen_height)
root.geometry(screen_resolution)
time_lapse_counter = 0
frame_counter = 0
time_lapse_list=[]
setup_widgets()
show_frame()
root.mainloop()
`

Unable to display panel at the desired position

I created a Panel in a wxFrame and then created a FigureCanvas. I would like to fit the FigureCanvas into the panel completely, but somehow the FigureCanvas does not go into the panel2_2, but exactly on just the opposite side.
Following is my code.
import wx
from numpy import arange, sin, pi
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
class Frame1(wx.Frame):
def __init__(self, prnt):
wx.Frame.__init__(self, parent=prnt,
pos=wx.Point(0, 0), size=wx.Size(1340, 720),
style=wx.DEFAULT_FRAME_STYLE)
self.panel2_2 = wx.Panel(parent=self,
pos=wx.Point(940, 30), size=wx.Size(400, 690),
style=wx.TAB_TRAVERSAL)
self.figure = Figure()
self.axes = self.figure.add_subplot(111)
self.canvas = FigureCanvas(self.panel2_2, -1, self.figure)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.canvas, 0, wx.EXPAND)
self.panel2_2.SetSizer(sizer)
self.panel2_2.Fit()
t = arange(0.0, 3.0, 0.01)
s = sin(2 * pi * t)
self.axes.plot(t, s)
#Every wxWidgets application must have a class derived from wxApp
class MyApp(wx.App):
# wxWidgets calls this method to initialize the application
def OnInit(self):
# Create an instance of our customized Frame class
self.frame = Frame1(None)
self.SetTopWindow(self.frame)
self.frame.Show()
return True
if __name__ == '__main__':
application = MyApp(0)
application.MainLoop()
and the result
I would like to have the image on the panel2_2 ( that is on the right hand side ) and not on the left hand side
I think the canvas is indeed going to panel2_2. The problem in your MWE is that you do not define a sizer for panel2_2. Therefore, panel2_2 is rendered from the upper left corner of the frame. This results in panel2_2 plus the canvas showing in the left of the frame. What you see to the right of the canvas is not panel2_2 but the rest of the frame since the size of the frame is larger than the size of panel2_2. If you add a blue panel1_1 and assign another wx.BoxSizer to the frame you get the canvas showing to the right.
import wx
from numpy import arange, sin, pi
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
class Frame1(wx.Frame):
def __init__(self, prnt):
wx.Frame.__init__(self, parent=prnt,
pos=wx.Point(0, 0), size=wx.Size(1340, 720),
style=wx.DEFAULT_FRAME_STYLE)
self.panel1_1 = wx.Panel(parent=self, size=(400, 690))
self.panel1_1.SetBackgroundColour('blue')
self.panel2_2 = wx.Panel(parent=self,
pos=wx.Point(940, 30), size=wx.Size(400, 690),
style=wx.TAB_TRAVERSAL)
self.figure = Figure()
self.axes = self.figure.add_subplot(111)
self.canvas = FigureCanvas(self.panel2_2, -1, self.figure)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.canvas, 0, wx.EXPAND)
self.panel2_2.SetSizer(sizer)
self.panel2_2.Fit()
sizerPanels = wx.BoxSizer(wx.HORIZONTAL)
sizerPanels.Add(self.panel1_1)
sizerPanels.Add(self.panel2_2)
sizerPanels.Fit(self)
self.SetSizer(sizerPanels)
t = arange(0.0, 3.0, 0.01)
s = sin(2 * pi * t)
self.axes.plot(t, s)
self.Center()
#Every wxWidgets application must have a class derived from wxApp
class MyApp(wx.App):
# wxWidgets calls this method to initialize the application
def OnInit(self):
# Create an instance of our customized Frame class
self.frame = Frame1(None)
self.SetTopWindow(self.frame)
self.frame.Show()
return True
if __name__ == '__main__':
application = MyApp(0)
application.MainLoop()

Tkinter remove stored file from memory

from Tkinter import *
import Tkinter as tk
import ttk
import tkFileDialog
import numpy as np
import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
class Look():
def __init__(self, master):
self.master = master
self.master.title("Demo")
self.master.configure(background = "grey91") #the color will be changed later
self.master.minsize(500, 300) # width + height
self.master.resizable(False, False)
self.top_frame = ttk.Frame(self.master, padding = (10, 10))
self.top_frame.pack()
ttk.Button(self.top_frame, text = "Load file", command = self.load_file,
style = "TButton").pack()
ttk.Button(self.top_frame, text = "Reset", command = self.clear_file,
style = "TButton").pack()
ttk.Button(self.top_frame, text = "Plot", command = self.plot_file,
style = "TButton").pack()
self.bottom_frame = ttk.Frame(self.master, padding = (10, 10))
self.bottom_frame.pack()
self.fig = plt.figure(figsize=(12, 5), dpi=100) ##create a figure; modify the size here
self.fig.add_subplot()
plt.title("blah")
self.canvas = FigureCanvasTkAgg(self.fig, master = self.bottom_frame)
self.canvas.show()
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.bottom_frame)
self.toolbar.update()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
def load_file(self):
self.file = tkFileDialog.askopenfilename(defaultextension = ".txt", filetypes = [("Text Documents", "*.txt")])
def clear_file(self):
self.fig.clf()
self.fig.add_subplot()
plt.xticks()
plt.yticks()
self.canvas.draw()
def plot_file(self):
self.r, self.g = np.loadtxt(self.file).transpose()
self.fig.clf()
plt.plot(self.r, self.g)
self.canvas.show()
def main():
root = Tk()
GUI = Look(root)
root.mainloop()
if __name__ == "__main__": main()
The code above creates a program has three buttons. The Load file buttom loads a txt file, Reset button is supposed to clear the plot and delete the file just loaded into the memory. Plot button is to plot the figure onto canvas below.
I have a question about how to write function associated with Reset function, i.e. clear_file function. Currently what it does is just clear the plot from canvas. But it seems the file that has been loaded to plot is stored in memory, since click again Plot, it will display the plot. My goal is to use Reset button to bring it to a fresh start--nothing stored in memory. I know loading a new file will overwrite the previous file. But when there are multiple buttons for loading different files, the situations could become complicated. Therefore I hope Reset can do the job.
If you want to try this little program, you may create a simple txt with two column data to be loaded into the program.
Thanks.
I would make 2 changes. Move the code that sets up your plot into its own method and reset the frame each time the method is called. This can be done with destroying the frame and remaking it. The 2nd change I would change the command on your reset to reference this new method.
Taking your code this is what I would change to be able to reset the plot.
import tkinter as tk
import tkinter.ttk as ttk
from tkinter import filedialog
import numpy as np
import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
class Look():
def __init__(self, master):
self.master = master
self.master.title("Demo")
self.master.configure(background = "grey91") #the color will be changed later
self.master.minsize(500, 300) # width + height
self.master.resizable(False, False)
self.top_frame = ttk.Frame(self.master, padding = (10, 10))
self.top_frame.pack()
ttk.Button(self.top_frame, text = "Load file", command = self.load_file,style = "TButton").pack()
ttk.Button(self.top_frame, text = "Reset", command = self.new_plot,style = "TButton").pack()
ttk.Button(self.top_frame, text = "Plot", command = self.plot_file,style = "TButton").pack()
self.bottom_frame = ttk.Frame(self.master, padding = (10, 10))
self.bottom_frame.pack()
self.new_plot()
# this function will reset your plot with a fresh one.
def new_plot(self):
self.bottom_frame.destroy()
self.bottom_frame = ttk.Frame(self.master, padding = (10, 10))
self.bottom_frame.pack()
self.fig = plt.figure(figsize=(12, 5), dpi=100) ##create a figure; modify the size here
self.fig.add_subplot()
plt.title("blah")
self.canvas = FigureCanvasTkAgg(self.fig, master = self.bottom_frame)
self.canvas.show()
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.bottom_frame)
self.toolbar.update()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
def load_file(self):
self.file = filedialog.askopenfilename(defaultextension = ".txt", filetypes = [("Text Documents", "*.txt")])
def clear_file(self):
self.fig.clf()
self.fig.add_subplot()
plt.xticks()
plt.yticks()
self.canvas.draw()
def plot_file(self):
self.r, self.g = np.loadtxt(self.file).transpose()
self.fig.clf()
plt.plot(self.r, self.g)
self.canvas.show()
if __name__ == "__main__":
root = tk.Tk()
GUI = Look(root)
root.mainloop()

how to insert buttons on the west side an Image file on the East Side or simply as a background using Tk

Here is my script
# Python 2.7.14 version
from Tkinter import *
import Tkinter
import tkMessageBox
from urllib2 import urlopen
import PIL
from PIL import ImageTk
import ImageTk
FILENAME = 'Fleur_de_lys.jpg'
root = Tk()
background = Canvas(root, width=250, height=250)##AttributeError: class Tk has no attribute 'Canvas'
canvas.pack()
tk_img = ImageTk.PhotoImage( file = FILENAME)
canvas.create_image(125, 125, image=tk_img)
quit_button = tk.Button(root, text = "Quit", command = root.quit, anchor = 'w',
width = 10, activebackground = "#33B5E5")
quit_button_window = canvas.create_window(10, 10, anchor='nw', window=quit_button)
root.mainloop()
No matter what attempt I do I keep getting AttributeError: class Tk has no attribute 'Canvas' where is my error if I just create button I have no issue what so ever all work but when I attempt to have a background image everything does not work
The problem appears to be that you're creating a canvas, and storing it in a variable named background, yet the very next line you are trying to call something called 'canvas', which you've never created.
Change this:
background = Canvas(root, width=250, height=250)
canvas.pack()
...
canvas.create_image(125, 125, image=tk_img)
to this:
background = Canvas(root, width=250, height=250)
background.pack()
...
background.create_image(125, 125, image=tk_img)
I fixed the issue jpg image was not being accepted I made it with a gif. Also found how to correct the buttons location and posn.
Here is the example without all the program
canvas = Canvas(root, width = 500, height = 500, bg='black')
canvas.pack(expand=YES, fill=BOTH)
my_image = PhotoImage(file='C:\\MOTD\\fleur_de_lys.gif')
canvas.create_image(0, 0, anchor = NW, image=my_image)
b1 = Button(root, text="From the Commander", command=callback,anchor = 'w',
width = 18, activebackground = "#33B5E5")
button_window1 = canvas.create_window(10, 10, anchor='nw', window=b1)

Image does not display in tkinter Canvas

I am trying to create a new class of Canvas that will be able to display and resize images. However The image does not display. I searched for similar questions and all the answers said to keep a reference of the PhotoImage in the class.( This answer for example). I tried doing that in a number of ways and none worked. In this example I tried saving a reference in a dictionary. I also tried self.tkimage = ImageTk.PhotoImage(image=img) which didn't work. And also tried using globals. I assume it has something to do with the garbage collector, but don't know how to solve.
Please assist.
import Tkinter as tk
from PIL import ImageTk, Image
import numpy as np
# a subclass of Canvas
class MyCanvas(tk.Canvas):
def __init__(self,parent,**kwargs):
tk.Canvas.__init__(self,parent,**kwargs)
self.tkimg = {}
self.images = []
self.image_on_canvas = []
def my_create_image(self, *args, **kw):
img = kw['image']
self.images.append(img)
self.tkimg[img] = ImageTk.PhotoImage(image=img)
kw['image'] = self.tkimg[img]
item = self.create_image(*args, **kw)
self.image_on_canvas.append(item)
def main():
root = tk.Tk()
myframe = tk.Frame(root)
myframe.pack(fill=tk.BOTH, expand=tk.YES)
mycanvas1 = MyCanvas(myframe, width=850, height=400, bg="red", highlightthickness=0)
mycanvas2 = MyCanvas(myframe, width=850, height=400, bg="green", highlightthickness=0)
mycanvas1.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.YES)
mycanvas2.pack(side=tk.RIGHT, fill=tk.BOTH, expand=tk.YES)
# add some widgets to the canvas
mycanvas2.create_line(0, 100, 200, 0, fill="red", dash=(4, 4))
mycanvas2.create_rectangle(50, 25, 150, 75, fill="blue")
arr1 = np.random.random([256,256])*255
img1 = Image.fromarray(arr1)
mycanvas1.my_create_image(0, 0, image=img1, anchor=tk.NW)
root.mainloop()
if __name__ == "__main__":
main()