Image not showing up when file updated/changed Tkinter - python-3.x

I have an image that I have placed, which works perfectly fine, but when I want to change it to a different image it doesn't change. The cards images have the same names as the ones in the list, i.e. 2C = 2 of Clubs.
root=Toplevel()
root.state('zoomed')
root.config(bg='#1b800b')
root.title('PokerChamp')
all_cards = ['2C','3C','4C','5C','6C','7C','8C','9C','10C','JC','QC','KC','AC','2D','3D','4D','5D','6D','7D','8D','9D','10D','JD','QD','KD','AD','2S','3S','4S','5S','6S','7S','8S','9S','10S','JS','QS','KS','AS','2H','3H','4H','5H','6H','7H','8H','9H','10H','JH','QH','KH','AH']
play_card1 = PhotoImage(file='files/cards/red_back.png')
card6 = Label(root, image=play_card1, bd=0)
card6.place_forget()
select_cards()
def select_cards():
card6.place(relx=0.45, rely=0.75)
player_card1 = random.choice(all_cards)
play_card1 = PhotoImage(file = f'files/cards/{player_card1}.png')
root.update()

When you load the first image you give it the name play_card1 in the global namespace.
The function select_cards() is a local namespace and when you assign a value to player_card1 it is a local name is not associated with the label and which will be garbage collected when the function ends.
The usual way to do this is to assign the new image to the label and then to save a reference to the image in the label object so the reference to tha image will not be lost as the function exits. See my example (I used slightly different images than you...):
from tkinter import *
import random
root = Toplevel()
root.config(bg='#1b800b')
root.title('PokerChamp')
all_cards = ['chapman','cleese','gilliam','idle','jones','palin']
play_card1 = PhotoImage(file='images/beer.png')
card6 = Label(root, image=play_card1, bd=0)
card6.place_forget()
def select_cards():
card6.place(relx=0.5, rely=0.5, anchor='center')
player_card1 = random.choice(all_cards)
play_card1 = PhotoImage(file = f'images/{player_card1}.png')
card6.config(image=play_card1) # Assign new image to label card6
card6.image = play_card1 # Keep a reference to image
root.update()
select_cards()
Also I would advise against using the name root for a Toplevel() window as root is usually used for the root window.

Related

Missing Argument / issues opening GUI program on macOS Big Sur

This is my first GUI program and I am having some major issues. I really need some help. First, I cannot get the program to open on my computer (mac). When running in Idle IDE I get this error message: import Tkinter
ModuleNotFoundError: No module named 'Tkinter'.
I have 3.9 installed which I thought had a GUI interface.
When debugging in VS Code i get this error message # line 44:
Exception has occurred: TypeError
init() takes at least 4 arguments (3 given)
I think I have 4
I'm not sure where to begin with these issues. From my research it appears that there is an issue running GUI programs on macs updated higher then 11.1.
Code is below
# Create a Tkinter GUI program that converts Celsius Temp to Fahrenheit
# F == Fahrenheit
# C == Celsius
# Tkinter imported
import Tkinter
# Global variable used
temp_val = 'Celsius'
#Set value for drop down menu
def store_temp (set_temp):
global temp_val
temp_Val = set_temp
class TemperatureConverter:
def __init__(self):
# create main window
self.main_window = Tkinter.Tk()
# create a title for window
self.main_window.title('Temperature Converter')
# create three frames
self.top_frame = Tkinter.Frame()
self.option_frame = Tkinter.Frame()
self.mid_frame = Tkinter.Frame()
self.bottom_frame = Tkinter.Frame()
#create widget for top frame
self.prompt_label = Tkinter.Label(self.top_frame, text= 'Enter a temperature in Celsius: ')
#pack top frame
self.prompt_label.pack(side='left')
# create str variable obj to hold empty string variable
self.inputNumber = Tkinter.StringVar()
self.var = Tkinter.StringVar()
# create widget for option drop down menu
self.entry = Tkinter.Entry(self.option_frame, textvariable=self.inputNumber )
self.dropDownList = ['Celsius','Fahrenheit']
self.drop_down = Tkinter.OptionMenu(self.option_frame, value=self.var , values=self.dropDownList, command=store_temp)
self.var.set(dropDownList[0])
# option widgets packed
self.entry.pack(side='right')
self.dropDownList.pack(side='left')
#create widget for middle frame
self.result_label = Tkinter.Label(self.mid_frame)
# create widgets for bottom frame
self.call_convert = (call_convert , result_label, inputNumber)
self.convert_button = Tkinter.Button(self.bottom_frame, text='Convert', command=self.call_convert)
self.quit_button= Tkinter.Button(self.bottom_frame, text= 'Quit', command= self.main_window.destroy)
#pack the buttons
self.convert_button.pack(side='left')
self.quit_button.pack(side='left')
#pack the frames
self.top_frame.pack()
self.option_frame.pack()
self.mid_frame.pack()
self.bottom_frame.pack()
#Enter the tkinter main loop
Tkinter.mainloop()
# convert method is callback fucntion for convert button
def call_convert(self):
if temp_Val == 'Celsius':
f = float((float(temp)* 9/5)+32)
self.result_label.config(text='The temperature in Fahrenhiet is:')
if temp_Val == 'Fahrenheit':
c = float((float(temp)-32) * 5 / 9)
self.result_label.config(text='The temperature in Celsius is:')
if __name__ == '__main__':
temp_converter = TemperatureConverter()
There were a lot of bugs in your code. I fixed all of them (I think). I had to guess where you wanted to put the label with the results. I also had to fix all of the indentations. This is the working code:
# Create a tkinter GUI program that converts Celsius Temp to Fahrenheit
# F == Fahrenheit
# C == Celsius
# tkinter imported
import tkinter
class TemperatureConverter:
def __init__(self):
# create main window
self.main_window = tkinter.Tk()
# create a title for window
self.main_window.title("Temperature Converter")
# create three frames
self.top_frame = tkinter.Frame(self.main_window)
self.option_frame = tkinter.Frame(self.main_window)
self.mid_frame = tkinter.Frame(self.main_window)
self.bottom_frame = tkinter.Frame(self.main_window)
# create widget for top frame
self.prompt_label = tkinter.Label(self.top_frame, text="Enter a temperature in Celsius:")
# pack top frame
self.prompt_label.pack(side="left")
# create str variable obj to hold empty string variable
self.inputNumber = tkinter.StringVar(self.main_window)
self.var = tkinter.StringVar()
# create widget for option drop down menu
self.entry = tkinter.Entry(self.option_frame, textvariable=self.inputNumber)
self.dropDownList = ["Celsius", "Fahrenheit"]
self.drop_down = tkinter.OptionMenu(self.option_frame, self.var, *self.dropDownList)
self.var.set(self.dropDownList[0])
# option widgets packed
self.entry.pack(side="right")
self.drop_down.pack(side="left")
# create widget for middle frame
self.result_label = tkinter.Label(self.mid_frame)
# create widgets for bottom frame
self.convert_button = tkinter.Button(self.bottom_frame, text="Convert", command=self.call_convert)
self.quit_button= tkinter.Button(self.bottom_frame, text= "Quit", command=self.main_window.destroy)
# pack the buttons
self.convert_button.pack(side="left")
self.quit_button.pack(side="left")
# pack the frames
self.top_frame.pack()
self.option_frame.pack()
self.mid_frame.pack()
self.bottom_frame.pack()
# It is better to call `<tkinter.Tk>.mainloop()`
self.main_window.mainloop()
# convert method is callback fucntion for convert button
def call_convert(self):
if self.var.get() == "Celsius":
f = float((float(self.entry.get())* 9/5)+32)
self.result_label.config(text="The temperature in Fahrenhiet is: "+str(f))
if self.var.get() == "Fahrenheit":
c = float((float(self.entry.get())-32) * 5 / 9)
self.result_label.config(text="The temperature in Celsius is: "+str(c))
self.result_label.pack(side="bottom")
if __name__ == "__main__":
temp_converter = TemperatureConverter()
Look at what I did for the OptionMenu and look at how I fixed you call_convert function. If you have any specific questions, tell me and I will try to answer them.
By the way I don't think any of the errors you were getting were caoused by your OS. Also I suggest that next time you use import tkinter as tk as it will make it way easier to write code.

tkinter doesn't open image via function

Tkinter doesn't open the image. We can ask the opening incorrectly, we need help. I need it to open the image through the menu. be sure to use pil, as the image can be anything. There are no errors in the syntax. Thank you = )
from tkinter import Tk, Frame, Menu, Canvas, PhotoImage
import easygui
from PIL import Image, ImageFilter, ImageTk
def input_file():
a = easygui.fileopenbox(filetypes=["*.jpg"])
original = Image.open(a)
original = original.resize((799, 799), Image.ANTIALIAS)
photoimg = ImageTk.PhotoImage(original)
canvas = Canvas(root, width=799, height=799)
imagesprite = canvas.create_image(10, 10,anchor='nw', image=photoimg)
canvas.pack()
return (imagesprite)
root = Tk()
root.title("Sputnikeca")
#root.iconbitmap('путь к иконке')
root.geometry("800x800+0+0")
my_menu = Menu(root)
root.config(menu=my_menu)
# Create a menu item
file_menu = Menu(my_menu)
my_menu.add_cascade(label = "Файл", menu=file_menu)
file_menu.add_command(label = "Импорт...", command=input_file())
file_menu.add_separator()
file_menu.add_command(label = "Выход", command=root.quit)
root.mainloop()
Here is what you have to do to solve the issue:
def input_file():
global photoimg #keeping a reference
a = easygui.fileopenbox(filetypes=["*.jpg"])
original = Image.open(a).resize((799, 799), Image.ANTIALIAS) #calling it all in one line
photoimg = ImageTk.PhotoImage(original)
canvas = Canvas(root, width=799, height=799)
imagesprite = canvas.create_image(10, 10,anchor='nw', image=photoimg)
canvas.pack()
return imagesprite
and then later remove the () around your function:
file_menu.add_command(label = "Импорт...", command=input_file)
What is being done?
In the first set of code im keeping a reference to the image so the image is not garbage collected by python. You can do so either by saying imagesprite.image = photoimg or global photoimg on top of the function. I also resized the image in the same line that I opened the image, to reduce codes.
And in the second set of codes, im just removing () so that the function is not called(invoked) before choosing the menu item.
And also tkinter itself has a filedialogbox that works like your easygui.fileopenbox(filetypes=["*.jpg"]), read some docs here
from tkinter import filedialog
a = filedialog.askopenfilename(title='Choose a file',initialdir='C:/',filetypes=(('All Files','*.*'),("JPEG
Files",'*.jpeg')))
Hope this helped you solve the error, do let me know if any doubts.
Cheers
If I am not mistaken, your menu opens as soon as you run the application, not when you click the import button.
It's because you need to pass the callback to the add_command, but you're calling the method instead
file_menu.add_command(label = "Import...", command=input_file())
Remove the () from input_file(). just pass input_file. it will not call the method directly anymore.
file_menu.add_command(label = "Import...", command=input_file)

Returning variable from a function

I have a main function page and i linked the next page with a button
when i click the button it executes a function in the main page but.
i want to use the result from the function in another file as a variable
########main.py
class Main(object):
def __init__(self,master):
self.master = master
mainFrame = Frame(self.master)
mainFrame.pack()
topFrame= Frame(mainFrame,width=1050,height =50, bg="#f8f8f8",padx =20, relief =SUNKEN,
borderwidth=2)
topFrame.pack(side=TOP,fill = X)
self.btnselfolder= Button(topFrame, text="Select Folder", compound=LEFT,
font="arial 12 bold", command=self.selectFolder)
self.btnselfolder.pack(side=LEFT)
def selectFolder(self):
print("folder")
return folder
################# selectfolder page
class Page2(Toplevel):
def __init__(self):
Toplevel.__init__(self)
self.geometry("450x300")
self.title("page2")
self.resizable(False,False)
self.topFrame = Frame(self, width=350,height=150, bg="white")
self.topFrame.pack(fill=X)
# call the function from main.py and it will give me the same output folder
y = selectFolder()
Since the selectFolder method is not static, you'll have to access it using instance. Like this:
main = Main()
folder = main.selectFolder()
folder should hold the value you returned.
I'm afraid when i just copied and pasted your code into my IDLE directly, it immediately presented me with naming errors. Not even a Tkinter window popped up. So to clear things up I would go about making a Tkinter window like the following. Also bare in mind I don't really use or know how to integrate classes with Tkinter itself, but I quickly learned how to by your mistakes :)
# import tkinter from the libraries
from tkinter import *
import os
# the Zen of Python
#import this
# declare the tkinter window variable
# scroll to bottom to see mainloop
root = Tk()
########main.py
# folder is not defined as a variable
# but i recommend looking into
# using system of which i imported
# for you. You can research how to
# use it yourself for good practice
# i am sorry if i am wrong about this
# but my impression is you're trying to
# use the OS to select a folder from a
# directory. So the 'selectFolder' function
# should not be declared as a method within the class
# therefore rendering the Page2 class useless.
def selectFolder():
print("folder")
# return will result in an error
# because it has not been declared as a variable
# return folder
class Main():
# all instances instantiated (created)
# within the __init__ method
# must be declared in the parentheses
def __init__(self, topFrame, btnselfolder):
# instantiate the topFrame object
self.topFrame = topFrame
# instantiate the btnselfolder
self.btnselfolder = btnselfolder
# pro tip - when having multiple
# arguments within an object, then
# to keep it clean and easy to read
# space out the code like i have for
# you. You should also read the "Zen of Python"
# which is at the top as 'import this'
# however i have commented it out for you
topFrame = Frame(root,
width=1050,
height = 50,
bg = "#f8f8f8",
padx = 20,
relief = SUNKEN,
borderwidth=2)
topFrame.pack(side=TOP, fill = X)
btnselfolder = Button(topFrame,
text = "Select Folder",
compound=LEFT,
font = "arial 12 bold",
command = selectFolder).pack()
# mainloop() method will keep the window open
# so that it doesn't appear for a millisecond
# and dissapear :)
root.mainloop()
# I hope this has been a big help
# Thanks for posting this question! :-)
# Enjoy your day, good luck and be safe in Lockdown!
Thanks again for the question, I thoroughly enjoyed solving it or at least giving you some guidence! :)

How to create multiple checkbox in loop using tkinter

I am New to use Tkinter.I want to create multiple checkbox from loop.I refer Tkinter checkboxes created in loop but i don't understand it.
I want to display all files as a checkbox located in a directory.
Help me and tell me what i need to change ?
Code:
from tkinter import filedialog,Checkbutton
import tkinter,os
window = tkinter.Tk()
def browse():
filez = filedialog.askdirectory(parent=window,title='Choose a file')#I choose a directory
ent1.insert(20,filez)#insert the path of directory to text box
dirs = os.listdir(filez)#gives all files of direcory
for file in dirs:
print(file)#Getting all files
var = tkinter.IntVar()
c = tkinter.Checkbutton(window,text=file,variable=var)#Create files to checkox
c.place(x=0,y=100)
window.title("First Gui")
window.geometry("400x400")
window.wm_iconbitmap("path of icon")
lbl = tkinter.Label(window,text="path")
lbl.place(x=0,y=60)
ent1 = tkinter.Entry(window)
ent1.place(x=80,y=60)
btn1 = tkinter.Button(window,text="Set Path",command=browse)
btn1.place(x=210,y=57)
window.mainloop()
After click on button set path i want to to display all files of directory as a checkbox using browse function
I see three problems
you use c.place(x=0,y=100) for all Checkbuttons so you can see only last one - other are hidden behind last one.
every Checkbutton need own IntVar which you can keep on list or in dictionary.
when you select new path then you have to remove previous Checkbuttons so you have to remember them on list or in dictionary.
Example shows how you could use pack() instead of place() to easily put all Checkbuttons. It also show how to uses dictionary to keep IntVars and check which was selected, and how to use list to keep Checkbuttons and remove them later from window.
import tkinter
import tkinter.filedialog
import os
# --- functions ---
def browse():
filez = tkinter.filedialog.askdirectory(parent=window, title='Choose a file')
ent1.insert(20, filez)
dirs = os.listdir(filez)
# remove previous IntVars
intvar_dict.clear()
# remove previous Checkboxes
for cb in checkbutton_list:
cb.destroy()
checkbutton_list.clear()
for filename in dirs:
# create IntVar for filename and keep in dictionary
intvar_dict[filename] = tkinter.IntVar()
# create Checkbutton for filename and keep on list
c = tkinter.Checkbutton(window, text=filename, variable=intvar_dict[filename])
c.pack()
checkbutton_list.append(c)
def test():
for key, value in intvar_dict.items():
if value.get() > 0:
print('selected:', key)
# --- main ---
# to keep all IntVars for all filenames
intvar_dict = {}
# to keep all Checkbuttons for all filenames
checkbutton_list = []
window = tkinter.Tk()
lbl = tkinter.Label(window, text="Path")
lbl.pack()
ent1 = tkinter.Entry(window)
ent1.pack()
btn1 = tkinter.Button(window, text="Select Path", command=browse)
btn1.pack()
btn1 = tkinter.Button(window, text="Test Checkboxes", command=test)
btn1.pack()
window.mainloop()

Cache overflowing while showing an image stream using python 3 on a Raspberry Pi 2 Model B running Jessie and up to date

I’m very new to python. I’m working on a ‘proof of concept’ piece of code; using PiCamera on a Raspberry Pi running Jessie.
I’ve based my code on a tutorial code from: https://pythonprogramming.net/tkinter-adding-text-images/
Once you hit the button to show the image, the code starts PiCamera and starts to get capture_continuous, passes it to a stream, applies crosshairs to it.
It works mostly well… but after a bit over two minutes, the disk drive lights up and it starts to slow drastically. Once I get the program to break, everything is fine. I’ve looked at a couple of logs, but I can’t for the life of me find out what cache is overflowing or why. I’ve tried a bunch of different ways and tried to leave those in as comments. I suspected it had something to do with having to clear the image in tkinter, but even that doesn’t seem to work and makes the video flash unevenly.
Any help would be great! I’ve started to explore using opencv instead. Still installing that.
Thanks!
The code:
# Simple enough, just import everything from tkinter.
from tkinter import *
import picamera
import picamera.array
import time
import threading
import io
import numpy as np
from PIL import Image, ImageTk
# Here, we are creating our class, Window, and inheriting from the Frame
# class. Frame is a class from the tkinter module. (see Lib/tkinter/__init__)
class Window(Frame):
# Create an array representing a 1280x720 image of
# a cross through the center of the display. The shape of
# the array must be of the form (height, width, color)
# Define settings upon initialization. Here you can specify
def __init__(self, master=None):
# parameters that you want to send through the Frame class.
Frame.__init__(self, master)
#reference to the master widget, which is the tk window
self.master = master
#with that, we want to then run init_window, which doesn't yet exist
self.init_window()
#Creation of init_window
def init_window(self):
# changing the title of our master widget
self.master.title("GUI")
# allowing the widget to take the full space of the root window
self.pack(fill=BOTH, expand=1)
# creating a menu instance
menu = Menu(self.master)
self.master.config(menu=menu)
# create the file object)
file = Menu(menu)
# adds a command to the menu option, calling it exit, and the
# command it runs on event is client_exit
file.add_command(label="Exit", command=self.client_exit)
#added "file" to our menu
menu.add_cascade(label="File", menu=file)
# create the file object)
edit = Menu(menu)
# adds a command to the menu option, calling it exit, and the
# command it runs on event is client_exit
edit.add_command(label="Show Img", command=self.showImg)
edit.add_command(label="Show Text", command=self.showText)
#added "file" to our menu
menu.add_cascade(label="Edit", menu=edit)
self.trim_running_bool = False
def showImg(self):
self.trim_running_bool = True
trim_thrd_thread = threading.Thread(target=self._cam_thread_def)
trim_thrd_thread.start()
self.update_idletasks()
def _cam_thread_def(self):
img_stream = io.BytesIO()
frame_count = 0
with picamera.PiCamera() as camera:
camera.resolution = (400, 300)
## while True: ### tried it this way too
for xxx in range(0,900):
img_stream = io.BytesIO()
frame_count = frame_count + 1
print(frame_count," ", xxx)
if self.trim_running_bool == False:
print("break")
break
camera.capture(img_stream, 'jpeg', use_video_port=True)
img_stream.seek(0)
img_load = Image.open(img_stream)
for xl_line in range(0,196,4):
img_load.putpixel((xl_line, 149), (xl_line, 0, 0))
xll=xl_line+2
img_load.putpixel((xl_line, 150), (xl_line, xl_line, xl_line))
img_load.putpixel((xl_line, 151), (xl_line, 0, 0))
(xl_line)
for xr_line in range(208,400,4):
clr = 400 - xr_line
img_load.putpixel((xr_line, 149), (clr, 0, 0))
img_load.putpixel((xr_line, 150), (clr, clr, clr))
img_load.putpixel((xr_line, 151), (clr, 0, 0))
(xr_line)
for yt_line in range(0,146,4):
clrt = int(yt_line * 1.7)
img_load.putpixel((199, yt_line), (clrt, 0, 0))
img_load.putpixel((200, yt_line), (clrt, clrt, clrt))
img_load.putpixel((201, yt_line), (clrt, 0, 0))
(yt_line)
for yb_line in range(158,300,4):
clrb = int((300 - yb_line) * 1.7)
img_load.putpixel((199, yb_line), (clrb, 0, 0))
img_load.putpixel((200, yb_line), (clrb, clrb, clrb))
img_load.putpixel((201, yb_line), (clrb, 0, 0))
(yb_line)
img_render = ImageTk.PhotoImage(img_load)
# labels can be text or images
img = Label(self, image=img_render)
img.image = img_render
img.place(x=0, y=0)
self.update_idletasks()
img_stream.seek(0)
img_stream.truncate(0)
# tried these:
## img_stream.flush()
## print("flushed ", img_stream)
## print("2nd ",img_stream)
## del img_load
##
##
## rawCapture.truncate(0)
##
## rawCapture.seek(0)
## rawCapture.truncate(0)
## del render
## img.image = None
## foregnd_image = None
(xxx)
pass
def showText(self):
text = Label(self, text="Hey there good lookin!")
text.pack()
def client_exit(self):
self.trim_running_bool = False
exit()
# root window created. Here, that would be the only window, but
# you can later have windows within windows.
root = Tk()
root.geometry("400x300")
#creation of an instance
app = Window(root)
#mainloop
root.mainloop()
Each time through your loop you are creating a new image object and a new label, as well as some other objects. That is a memory leak, since you never destroy the old image or old label.
Generally speaking, you should create exactly one label, then use the_label.configure(image=the_image) every time through the loop. With that, you don't need to create new labels or call place on it.
Even better, since a label automatically updates when the associated image changes, you only need to change the the bits that are in the image object itself and the label should update automatically.
The simplest solution is to move image creation to a function so that all of those objects you are creating are local objects that can get automatically garbage collected when the function returns.
The first step is to create a single label and single image in your main thread:
class Window(Frame):
def __init__(self, master=None):
...
self.image = PhotoImage(width=400, height=300)
self.label = Label(self, image=self.image)
...
Next, create a function that copies new data into the image. Unfortunately, tkinter's implementation of the copy method doesn't support the full power of the underlying image object. A workaround is described here: http://tkinter.unpythonic.net/wiki/PhotoImage#Copy_a_SubImage.
Note: the workaround example is a general purpose workaround that uses more arguments than we need. In the following example we can omit many of the arguments. The documentation for the underlying tk photo object copy method is here: http://tcl.tk/man/tcl8.5/TkCmd/photo.htm#M17
The implementation would look something like this (I'm guessing; I don't have a good way to test it):
def new_image(self):
# all your code to create the new image goes here...
...
img_render = ImageTk.PhotoImage(img_load)
# copy the new image bits to the existing image object
self.tk.call(self.image, 'copy', img_render)
Finally, your loop to update the image would be much simpler:
while True:
self.new_image()
# presumeably there's some sort of sleep here so you're
# not updating the image faster than the camera can
# capture it.
I don't know how fast new_image can run. If it can run in 200ms or less you don't even need threads. Instead, you can use after to run that function periodically.
Note: I haven't worked much with Tkinter photo images in a long time, and I have no good way to test this. Use this as a guide, rather than as a definitive solution.

Resources