I'm trying to loop random images from a folder. So far, I can loop the images but everytime I try to use random.choice, I'm getting an error. Below is my code without random imported
import tkinter as tk
import glob
root = tk.Tk()
from PIL import ImageTk, Image
root.geometry('600x600')
pics = glob.glob("./imgs/*.png")
photos = [random.choice(tk.PhotoImage(file=x)) for x in pics]
label = tk.Label(root)
label.photos = photos
label.counter = 0
def changeimage():
label['image'] = label.photos[label.counter%len(label.photos)]
label.after(8000, changeimage)
label.counter += 1
label.pack(padx=10, pady=10)
changeimage()
root.mainloop()
Error
Traceback (most recent call last):
File "/Users/ad/Documents/Python/Project_tkinter/test1.py", line 148, in <module>
photos = [random.choice(tk.PhotoImage(file=x)) for x in pics]
File "/Users/ad/Documents/Python/Project_tkinter/test1.py", line 148, in <listcomp>
photos = [random.choice(tk.PhotoImage(file=x)) for x in pics]
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/random.py", line 288, in choice
i = self._randbelow(len(seq))
TypeError: object of type 'PhotoImage' has no len()
You have to first create list of photos and later choose single photo
list_of_photos = [tk.PhotoImage(file=x) for x in pics]
single_photo = random.choice(list_of_photos)
but if you want to loop this list then you rather need random.shuffle() to change order on the list to have files in random order.
list_of_photos = [tk.PhotoImage(file=x) for x in pics]
random.shuffle(list_of_photos)
random.shuffle() changes original list and it doesn't return new list.
Related
I get this error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Hunter\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
File "c:\Users\Hunter\Documents\Programming\Python Scripts\Scripts\spoolClient\menuScript.py", line 46, in <lambda>
updateJsonButton = Button(preferences, text="Save Preferences", command=lambda: updateJson())
File "c:\Users\Hunter\Documents\Programming\Python Scripts\Scripts\spoolClient\menuScript.py", line 17, in updateJson
for i, j in zip(entryNames, entry):
File "C:\Users\Hunter\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 1643, in cget
return self.tk.call(self._w, 'cget', '-' + key)
TypeError: can only concatenate str (not "int") to str
When trying to run my script:
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox
from tkinter import filedialog
import qrMaker
import qrReader
import json
settings = {}
#define vars
preferencesSkippedRows = [1, 3, 5, 7, 9, 11]
def openPreferences():
def updateJson():
print("here")
for i, j in zip(entryNames, entry):
print("loopdie")
value = str(j.get())
settings[i]=value
settingsjson = json.dumps(settings)
print(settingsjson)
f = open("preferences.json","w")
f.write(settingsjson)
f.close()
preferences = Tk()
preferences.title("Preferences")
preferences.iconbitmap(qrMaker.getCurrentPath()+'icon.ico')
preferences.geometry('400x600')
topText = Label(preferences, width=30, text="Filament Spool Client Preferences")
cameraText = Label(preferences, width=30, text="Select Camera Instance:")
cameraEntry = Combobox(preferences, width=30, values=qrReader.getCameras())
qrWidthText = Label(preferences, width=30, text="QR Output Width (in.)")
qrWidthEntry = Entry(preferences, width=30)
qrHeightText = Label(preferences, width=30, text="QR Output Height (in.)")
qrHeightEntry = Entry(preferences, width=30)
text = [cameraText, qrWidthText, qrHeightText]
entry = [cameraEntry, qrWidthEntry, qrHeightEntry]
entryNames = ['cameraEntry', 'qrWidthEntry', 'qrHeightEntry']
updateJsonButton = Button(preferences, text="Save Preferences", command=lambda: updateJson())
for i in preferencesSkippedRows:
preferences.grid_rowconfigure(i, minsize=10)
topText.grid(column = 0, row = 0)
row=2
for text, entry in zip(text, entry):
text.grid(column = 0, row = row)
entry.grid(column = 1, row = row)
row+=2
updateJsonButton.grid(column=1, row=row+2)
preferences.mainloop()
openPreferences() #I call script.openPreferences() in my main program but I left this here for debugging purposes
I can see from the error message that the error occurs somewhere in the line that my zip function occurs, but I have no idea what causes this. Oddly enough, this error goes away if instead of setting updateJson equal to the command value of my Tkinter button state, I set updateJson, which calls the function right as the button object is initialized. I also know what the error is saying, I just don't know where an integer is coming from, and how I can fix this issue. Any help would be appreciated.
Update: I've just found that the actual zipping of the two lists is not the problem, but when I introduce the for loop, the same error occurs.
Answering to close out this thread, answer from "user2357112 supports Monica".
The issue in this script is that for text, entry in zip(text, entry) literally uses "entry" in the for loop, and is executed after the button instance is created, meaning that if updateJson is called during the button object initialization, then there will be no error thrown as entry is still defined as a list. However, after for text, entry in zip(text, entry) executes at startup, entry is now defined as the last object in the list entry, no longer the list entry itself. When the user presses the button and updateJson is called, an error is thrown as entry is not a list anymore(I'm not 100% sure on the error part).
I would like to put a variable in a Tkinter label that prints out the value of the variable 'x'.
I tried to follow the example from the Tkinter documentation but it still seems to give me a trace error 'in second_click lblx2.place(window)'
x = max(numbers)
y = min(numbers)
z = sum(numbers)
a = float(z / len(numbers))
var_x = StringVar()
var_x.set(x)
lblx2 = Label(window, textvariable=var_x, font=('Arial Bold', 15), bg='blue', fg='red')
lblx2.place(window)
I expect it to just place the number on my window like regular text but it does not print anything.
The full error:
Exception in Tkinter callback
Traceback (most recent call last):
File "REDACTED", line 1705, in __call__ return self.func(*args)
File "REDACTED", line 49, in <lambda> btn['command'] = (lambda: second_click())
File "REDACTED", line 102, in second_click lblx2.place(window)
File "REDACTED", line 2188, in place_configure
File "REDACTED", line 1320, in _options cnf = _cnfmerge(cnf)
File "REDACTED", line 104, in _cnfmerge for c in _flatten(cnfs):
TypeError: object of type 'Tk' has no len()
When I remove (window) from place, it stops giving the errors but still does not put the number on the window
I believe the error is that window is not a suitable parameter for a Label object. Instead, it is used to change the location of the element. You could either
turn it into this: lblx2.place(x=whatever,y=whatever) (note: replace whatever with the location)
or
Remove the whole line of code entirely as it's unnecessary
Don't see a problem. Please provide a full example, not just pieces of code one cannot simply run. Here is mine, that DOESN'T reproduce the issue, and it really doesn't get much simpler than this:
from tkinter import *
from random import random
x = random()
root = Tk()
var_x = StringVar()
var_x.set(x)
Label(root, textvariable=var_x).pack()
root.mainloop()
I am working on Yolo3-4-PY to implement it with tkinter.
I've looked up everywhere but not able to resolve the issue.
When I run the program the canvas is displayed but when I click on Start Video(btton) I get the following error:
Loading weights from weights/yolov3.weights...Done!
/usr/local/lib/python3.5/dist-packages/PIL/ImageTk.py:119: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
if mode not in ["1", "L", "RGB", "RGBA"]:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.5/tkinter/__init__.py", line 1553, in __call__
return self.func(*args)
File "webcam_demo.py", line 13, in start_video
show_frame()
File "webcam_demo.py", line 39, in show_frame
imgtk = ImageTk.PhotoImage(image=cv2image)
File "/usr/local/lib/python3.5/dist-packages/PIL/ImageTk.py", line 120, in
__init__
mode = Image.getmodebase(mode)
File "/usr/local/lib/python3.5/dist-packages/PIL/Image.py", line 313, in
getmodebase
return ImageMode.getmode(mode).basemode
File "/usr/local/lib/python3.5/dist-packages/PIL/ImageMode.py", line 55, in
getmode
return _modes[mode]
TypeError: unhashable type: 'numpy.ndarray'
Exception ignored in: <bound method PhotoImage.__del__ of
<PIL.ImageTk.PhotoImage object at 0x7f4b73f455c0>>
Traceback (most recent call last):
File "/usr/local/lib/python3.5/dist-packages/PIL/ImageTk.py", line 130, in
__del__ name = self.__photo.name
AttributeError: 'PhotoImage' object has no attribute '_PhotoImage__photo'
in my case , correct with just simply add this line
root = tkinter.Tk()
complete code :
root = tkinter.Tk()
image = PIL.Image.open(r"C:\Users\Hamid\Desktop\asdasd\2.jpeg")
img = ImageTk.PhotoImage(image)
l = Label(image=img)
l.pack()
Issue
In the line imgtk = ImageTk.PhotoImage(image=cv2image), you are passing a numpy array (cv2image) as input to ImageTk.PhotoImage. But the source code of PIL.ImageTk mentions that it requires a PIL image.
This is what source code of PIL.ImageTk mentions for init() of PhotoImage.
class PhotoImage(object):
.....
:param image: Either a PIL image, or a mode string. If a mode string is
used, a size must also be given.
Solution
So basically, you will have to convert the numpy array to a PIL Image and then pass it to ImageTk.PhotoImage().
So, can you replace the line imgtk = ImageTk.PhotoImage(image=cv2image) with imgtk = ImageTk.PhotoImage(image=PIL.Image.fromarray(cv2image))?
This would convert the numpy array to a PIL Image and it would be passed into the method.
References
I extracted the code for converting a numpy array to PIL Image from this source.
when you place the image variable in the label , you must initiate the image variable to "image".
Eg: (CORRECT APPROACH)
photo = PhotoImage(file = "C://Users//Carl//Downloads//download.png")
label1 = Label(image = photo)
label1.pack()
Eg : (WRONG APPROACH)
photo = PhotoImage(file = "C://Users//Carl//Downloads//download.png")
label1 = Label(photo)
label1.pack()
Interesting.... there's apparently a nasty side-effect in Tkinter which can cause this.
Note (from hamidjahandideh's answer ) that it matters that you create your root window BEFORE cresting the ImageTk.
ie. this fails with AttributeError: 'PhotoImage' object has no attribute '_PhotoImage__photo'
im_numpy = cv2.imread(ResourcePhotos.BLUE_PERSON_TRAIL_PHOTO)[:, :, ::-1].copy() # Load BGR Image
im_pil = Image.fromarray(im_numpy)
imagetk = ImageTk.PhotoImage(im_pil)
window = tk.Tk() # This line must come BEFORE crearting ImageTk
tk.Label(window, image=imagetk).pack()
window.mainloop()
But this works:
im_numpy = cv2.imread(ResourcePhotos.BLUE_PERSON_TRAIL_PHOTO)[:, :, ::-1].copy() # Load BGR Image
im_pil = Image.fromarray(im_numpy)
window = tk.Tk() # This line must come BEFORE creating ImageTk
imagetk = ImageTk.PhotoImage(im_pil)
tk.Label(window, image=imagetk).pack()
window.mainloop()
I'm receiving the following error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python33\lib\tkinter\__init__.py", line 1475, in __call__
return self.func(*args)
File "D:\COMPUTER SCIENCE\Seating Plan\SeatingPlan TEST.py", line 205, in displayText
if roomChange.get().strip() == "":
NameError: global name 'roomChange' is not defined
When attempting to run the following code:
from tkinter import *
import tkinter.messagebox
def displayText():
""" Display the Entry text value. """
global roomChange
if roomChange.get().strip() == "":
tkinter.messagebox.showerror("Invalid Value", "Please enter a valid classroom name.")
else:
tkinter.messagebox.showinfo("Temporary Window", "Text value = " + roomChange.get().strip())
def roomChanger():
chrm = Tk()
chrm.title("Change Room")
chrm.wm_iconbitmap('./Includes/icon.ico')
chrm["padx"] = 40
chrm["pady"] = 20
# Create a text frame to hold the text Label and the Entry widget
textFrame = Frame(chrm)
#Create a Label in textFrame
roomChangeLabel = Label(textFrame)
roomChangeLabel["text"] = "Enter name of classroom: "
roomChangeLabel.pack(side=LEFT)
# Create an Entry Widget in textFrame
roomChange = Entry(textFrame)
roomChange["width"] = 50
roomChange.pack(side=LEFT)
textFrame.pack()
roomChangeButton = Button(chrm, text="Submit", command=displayText)
roomChangeButton.pack()
chrm.mainloop()
testButton = Button(window, text='Change Room', command=roomChanger)
testButton.place(x = 825, y = 360)
Can anyone suggest a solution to my problem?
Thanks
In roomChanger() you assign to roomChange:
roomChange = Entry(textFrame)
so you need to mark that name as a global inside that function too. Add a global roomChange statement in that function.
displayText() on the other hand, never tries to assign to roomChange and the global statement in that function can safely be removed.
I had the same problem.
Here was my solution:
from tkinter import *
from tkinter import messagebox
Some sort of namespace glitch. That second line shouldn't be necessary. Technically from a syntax perspective import * implies import messagebox too because it's part of it all.
Use those two lines, take away import tkinter.messagebox
This is a GUI I’ve been writing for a script I already have working. What I’m struggling with here is retrieving the information in the textboxes.
Under the definition generate I am able to pop a name off of listx but I am unable to grab the local variable entry from any of the instances of the new_title_box class.
from Tkinter import *
import ttk
boxvar=""
folder=""
listx=[]
count = 1
myrow = 1
class new_title_box:
def __init__(self,name):
global myrow, count, listx
self.entry = StringVar()
self.name = name
self.name = ttk.Entry(mainframe,width=45,textvariable=self.entry)
self.name.grid(column=1,row=myrow+1,sticky=(N,W))
listx.append(name)
print(listx) ## For debugging to insure that it is working correctly, if it gives output it, this part works
myrow = myrow + 1
count=count+1
def make_new(*args):
new_title_box('box'+str(count))
def generate(*args):
global listx, boxvar
while len(listx) > 0:
boxvar=listx.pop(0)
print(boxvar) ## For debugging to insure that it is working correctly, if it gives output it, this part works
folder = boxvar.entry.get() ## Not working here
print(folder) ## For debugging to insure that it is working correctly, if it gives output it, this part works
root = Tk()
root.title("File Maker")
mainframe = ttk.Frame(root, padding = "50 50 50 50")
mainframe.grid(column = 0,row = 0,sticky = (N, W, E, S))
mainframe.columnconfigure(0,weight=1)
mainframe.columnconfigure(0,weight=1)
add_entry = ttk.Button(mainframe,width=20, text = "add entry", command=make_new)
add_entry.grid(column=2,row=2,sticky=(N,W))
add_entry = ttk.Button(mainframe,width=20, text = "make files", command=generate)
add_entry.grid(column=2,row=3,sticky=(N,W))
root.mainloop()
Here's the traceback I'm getting:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python33\lib\tkinter_init_.py", line 1442, in call
return self.func(*args)
File "C:\python\SampAqTkinter.py", line 28, in generate
folder = boxvar.entry ## Not working here
AttributeError: 'str' object has no attribute 'entry'
There are two things that need to be changed to fix the problem you describe:
In the new_title_box.__init__() method change: listx.append(name) to listx.append(self.name)
In the generate() function, change: folder = boxvar.entry.get() to folder = boxvar.get().
You are appending a string to listx, use self.name instead of the local string name