I'm making a Tkinter application and created an executable of my project containing the main.py file and 2 other helper .py files located in "path/to/python/project/folder" using Pyinstaller:
pyinstaller --exclude-module PyQt5 --onefile -p "path/to/python/project/folder" main.py
At some point in the main.py file an image is selected by the user from their system and then shown in the GUI. To do so I create image.jpg in "path/to/python/project/folder". When running my code in an IDE things work fine but when I run main.exe I get the following error: OSError: cannot identify image file 'image.jpg'
There's also an empty image created in the same folder as where the .exe file is located.
Is there a way to make the .exe file behave like the original python 'project'? Or can one simply not create new files and access them from an .exe file?
EDIT: the user selects a video and the application shows the middel frame as an image that’s why a new image is created.
EDIT: here's some code to maybe clarify some things:
import tkinter as tk
from tkinter import filedialog, messagebox, simpledialog
import cv2
from PIL import Image, ImageTk
import os
fpsGlobal = -1
class GUI:
def __init__(self, master):
self.master = master
self.master.title("TITLE")
self.readyForAnalysis = False
self.workdir = os.getcwd()
self.data = None
self.fps = fpsGlobal
self.finished = False
self.frame = tk.Frame(master)
self.frame.pack()
self.menu = tk.Menu(self.frame)
self.file_expand = tk.Menu(self.menu, tearoff=0)
self.file_expand.add_command(label='Open...',command=self.openVideo)
self.menu.add_cascade(label='File', menu=self.file_expand)
self.master.config(menu=self.menu)
def openVideo(self):
'''Opens the video when open... button is clicked and shows a screenshot of a frame from the video'''
self.filename = filedialog.askopenfilename(initialdir = '/', title = 'Select file', filetypes = (("avi files",".avi"),("all files","*.*")))
# if a video is loaded and openfiledialog is not cancelled
if self.filename:
# read videofile
cap = cv2.VideoCapture(self.filename)
self.totalFrames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
global fpsGlobal
fpsGlobal = int(cap.get(cv2.CAP_PROP_FPS))
cap.set(cv2.CAP_PROP_POS_FRAMES, int(self.totalFrames/2))
ret,frame = cap.read()
cv2.imwrite("image.jpg", frame)
# show image
image = Image.open("image.jpg")
#print("image.size = (%d, %d)" % image.size)
resizedImg = image.resize((704, 576), Image.ANTIALIAS)
picture = ImageTk.PhotoImage(resizedImg)
self.label = tk.Label(self.frame,image=picture)
self.label.image = picture
self.label.pack()
cap.release()
cv2.destroyAllWindows()
try:
os.remove("image.jpg")
except: print("no")
self.readyForAnalysis = True
self.analyzeButton.configure(background='green2')
self.welcome.config(text="Start the analysis of the video by clicking the 'Analyze' button." )
def main():
root = tk.Tk()
root.geometry('1000x750')
my_gui = GUI(root)
root.mainloop()
if __name__== "__main__":
main()
So in the GUI the user is able to select a video file which later will be analyzed. To provide some feedback to the user i'll show the middle frame of the video as an image within the GUI. When running the code in my IDE everything works but running it from the .exe file I get an error (see error above) at line image = Image.open("image.jpg")
I believe I may have found the solution to my own question. It is not possible to create files in an executable the same way as in a Python project folder and access them afterwards since there's no folder structure in the .exe file.
The way this probably can be solved is by using the tempfile library.
Related
from PIL import Image
from tkinter import filedialog as fd
import os
import ctypes
import tkinter
class ImageList:
path = ""
dir = ""
top = tkinter.Tk()
canvas = tkinter.Canvas()
canvas.pack()
def __init__(self):
self.path = os.path.expanduser('~/')
def findImage(self):
image = Image.open(self.dir, "rb")
image.show()
def fileExplorer(self, path):
self.canvas.destroy()
self.canvas = tkinter.Canvas()
self.canvas.pack()
obj = os.scandir(path)
for entry in obj:
if entry.is_dir():
#self.path = os.path.abspath(self.path)
b = tkinter.Button(self.canvas, text=entry.name, command=lambda: self.fileExplorer(os.path.abspath(entry).replace('//', '\\')))
b.pack()
elif entry.is_file():
b = tkinter.Button(self.canvas, text=entry.name, command=lambda: print(entry.name))
b.pack()
else:
obj.close()
self.top.mainloop()
As shown in the code above, I am trying to make exe that shows the subdirs and files after C:\Users using buttons in tkinter and repeating it by using recursion.
The first error is that in "os.path.abspath(entry).replace('//', '\')" shows path in a format of "C://Users" and I intended to change that to "C:\Users" using the replace method. However, using "\" doesn't replace "//" and still shows as the original format.
The second error is that after running the code, when I press any button it acts as if I pressed the last button in the tkinter window, meaning that it recursively called the last subdir or the file below "C:\Users".
I'm just getting used to python and this is my first time using stackoverflow. I'm sorry if I did not abide by any of the rules in stackoverflow. Thanks for anyone helping.
I'm new to Python GUI's and am currently trying to build an app with DearPy. I was wondering if it's possible to use an mp4 formatted logo (animated logo) instead of the png logo's it would usually accept. For example:
with window("Appi", width= 520, height=677):
print("GUI is running")
set_window_pos("Appi", 0, 0)
add_drawing("logo", width=520, height=290)
draw_image("logo", "random.png", [0,240])
My question is: would it be possible to change the add_drawing to an add video and then the draw_image to allow me to insert an mp4 instead of a png? I've tried to look through the documentation but haven't found guidance for this yet.
Or is there an alternative package I should be using (i.e. tkinter)?
Thanks!
I used tkinter for this and it worked perfectly fine:
import tkinter as tk
import threading
import os
import time
try:
import imageio
except ModuleNotFoundError:
os.system('pip install imageio')
import imageio
from PIL import Image, ImageTk
# Settings:
video_name = 'your_video_path_here.extension' #This is your video file path
video_fps = 60
video_fps = video_fps * 1.2 # because loading the frames might take some time
try:
video = imageio.get_reader(video_name)
except:
os.system('pip install imageio-ffmpeg')
video = imageio.get_reader(video_name)
def stream(label):
'''Streams a video to a image label
label: the label used to show the image
'''
for frame in video.iter_data():
# render a frame
frame_image = ImageTk.PhotoImage(Image.fromarray(frame))
label.config(image=frame_image)
label.image = frame_image
time.sleep(1/video_fps) # wait
# GUI
root = tk.Tk()
my_label = tk.Label(root)
my_label.pack()
thread = threading.Thread(target=stream, args=(my_label,))
thread.daemon = 1
thread.start()
root.mainloop()
Edit: Thanks <3
Videos are in DearPyGui currently not supported
from tkinter import *
from PIL import Image, ImageTk
import glob, os
root = Tk()
root.geometry("800x600")
# Function to display image
def displayImg(img):
image = Image.open(img)
photo = ImageTk.PhotoImage(image)
newPhoto_label = Label(image=photo)
newPhoto_label.pack()
# gta_images = []
os.chdir("gta")
for file in glob.glob("*.jpg"):
# gta_images.append(str(file))
displayImg(file)
print(file)
# print(gta_images)
root.mainloop()
I am trying to load images from a folder called "gta" and then display those game logos on my app. Program has no error but I think its a logical error. I am new to Python I don't know maybe there is some scoping logic problem in my displayImg funcion.
Note: When a PhotoImage object is garbage-collected by Python (e.g.
when you return from a function which stored an image in a local
variable), the image is cleared even if it’s being displayed by a
Tkinter widget.
For more.
from tkinter import *
from PIL import Image, ImageTk
import glob, os
root = Tk()
root.geometry("800x600")
photos = []
def displayImg(img):
image = Image.open(img)
photo = ImageTk.PhotoImage(image)
photos.append(photo)#keep references!
newPhoto_label = Label(image=photo)
newPhoto_label.pack()
for file in glob.glob("*.jpg"):
displayImg(file)
print(file)
root.mainloop()
Not sure if it will work but try using the path of the gta folder you are getting the images from, instead of its name simply - replace the name with the path.
I would like to change the text of a Label widget from the QLabel class to display the file path of an image file chosen by a user when they click a button to open the file dialog to choose an image from their computer. Here is the code:
class GUI(QMainWindow):
#Load the GUI.
def __init__(self):
super().__init__()
#Define the window icon.
self.window_icon = QIcon("LightMap.png")
#Make the UI.
self.init_ui()
#Fill the GUI.
def init_ui(self):
#Load the UI file.
main_window = uic.loadUi("mainwindow.ui", self)
#Set the window icon.
self.setWindowIcon(self.window_icon)
#Add commands for actions under the submenus.
self.command_file_menu(main_window)
self.command_settings_menu(main_window)
self.command_help_menu(main_window)
#Handle the case that the user clicks on the "Open Image" button.
main_window.button_open_image.setStatusTip("Open Image")
main_window.button_open_image.clicked.connect(self.open_file)
#Make sure this variable has been declared so that we can click "Start Mapping" at any time.
self.file_chosen = None
#Handle the case that the user clicks on the "Start Mapping" button.
main_window.button_start_mapping.setStatusTip("Start Mapping")
main_window.button_start_mapping.clicked.connect(self.start_mapping)
#Show the main window.
self.show()
#Add commands for actions under the File menu.
def command_file_menu(self, main_window):
#Back-end logic for Open Image.
main_window.action_open_image.setShortcut("CTRL+O")
main_window.action_open_image.setStatusTip("Open Image")
main_window.action_open_image.triggered.connect(self.open_file)
#Open an image file.
def open_file(self):
#Select the file dialog design.
dialog_style = QFileDialog.DontUseNativeDialog
dialog_style |= QFileDialog.DontUseCustomDirectoryIcons
#Open the file dialog to select an image file.
self.file_chosen, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", "",
"JPEG (*.JPEG *.jpeg *.JPG *.jpg *.JPE *.jpe *JFIF *.jfif);; PNG (*.PNG *.png);; GIF (*.GIF *.gif);; Bitmap Files (*.BMP *.bmp *.DIB *.dib);; TIFF (*.TIF *.tif *.TIFF *.tiff);; ICO (*.ICO *.ico)", options=dialog_style)
#Show the path of the file chosen.
if self.file_chosen:
#Change the text on the label to display the file path chosen.
else:
#Change the text on the label to say that "No file was selected. Please select an image."
#This 'else' statement is used to catch the case where the user presses 'Cancel' after opening the file dialog,
#which will close the file dialog without choosing any file, even if they had already previously chosen a file
#from previously opening the file dialog.
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = GUI()
sys.exit(app.exec_())
I have already tried to directly pass main_window into open_file() and directly set the label text like this: main_window.label_file_name.setText(self.file_chosen), but the file dialog opens immediately as I launch the GUI with the error TypeError: argument1 has unexpected type NoneType.
I managed to get this feature working with TkInter, but I haven't been able to figure out how to repeat that functionality over to PyQt5. Here is an example of my working TkInter code:
class GUI:
#Structure the GUI.
def __init__(self):
#Create a blank window.
self.root = Tk()
#Create the frame.
frameRoot = Frame(self.root)
frameRoot.pack()
#Create the menu.
menu = Menu(self.root)
self.root.config(menu=menu)
#Create the "File" submenu.
fileMenu = Menu(menu, tearoff=0)
menu.add_cascade(label="File", menu=fileMenu)
fileMenu.add_command(label="Open Image", command=self.openFile)
#Make a button to open the image file.
self.fileChosen = None #Prevent the user from mapping without first selecting an image.
self.buttonFile = Button(frameRoot, text="Open Image...", command=self.openFile)
self.buttonFile.grid(row=3, column=1)
#Display the directory path of the file chosen.
self.fileName = StringVar()
self.fileName.set("No File Selected")
self.labelFileName = Label(frameRoot, textvariable=self.fileName, fg="red")
self.labelFileName.grid(row=4, column=1)
#Keep the window open.
self.root.mainloop()
#Open the image file.
def openFile(self):
#Only accept the following file types.
self.fileChosen = filedialog.askopenfilename(filetypes=[("Bitmap Files", "*.BMP *.bmp *.DIB *.dib"),
("JPEG", "*.JPEG *.jpeg *.JPG *.jpg *.JPE *.jpe *JFIF *.jfif"),
("PNG", "*.PNG *.png"),
("GIF", "*.GIF *.gif"),
("TIFF", "*.TIF *.tif *.TIFF *.tiff"),
("ICO", "*.ICO *.ico")
])
#If a file was selected, show the file path. Else, inform the user.
if self.fileChosen:
self.fileName.set(self.fileChosen)
else:
self.fileName.set("No image was selected. Please select an image.")
if __name__ == "__main__":
#Create an object to access the class.
g = GUI()
The key to my success with my TkInter implementation was the usage of the StringVar() class which comes from TkInter. I've tried doing a mix of TkInter and PyQt5 just to get this requirement working, but that just threw some more errors.
In your case you should be able to access the variable main_window since it allows me to access the label, for this you must pass it as a parameter, so I would suggest that you use a lambda function and modify your code to:
[...]
main_window.button_open_image.clicked.connect(lambda: self.open_file(main_window))
#Open an image file.
def open_file(self, main_window):
#Select the file dialog design.
dialog_style = QFileDialog.DontUseNativeDialog
dialog_style |= QFileDialog.DontUseCustomDirectoryIcons
#Open the file dialog to select an image file.
self.file_chosen, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", "",
"JPEG (*.JPEG *.jpeg *.JPG *.jpg *.JPE *.jpe *JFIF *.jfif);; PNG (*.PNG *.png);; GIF (*.GIF *.gif);; Bitmap Files (*.BMP *.bmp *.DIB *.dib);; TIFF (*.TIF *.tif *.TIFF *.tiff);; ICO (*.ICO *.ico)", options=dialog_style)
#Show the path of the file chosen.
if self.file_chosen:
main_window.label_file_name.setText(self.file_chosen)
#Change the text on the label to display the file path chosen.
else:
main_window.label_file_name.setText("No file was selected. Please select an image.")
#Change the text on the label to say that "No file was selected. Please select an image."
#This 'else' statement is used to catch the case where the user presses 'Cancel' after opening the file dialog,
#which will close the file dialog without choosing any file, even if they had already previously chosen a file
#from previously opening the file dialog.
I want to have the user select images to have the logo image pasted onto. The file dialog pops up and lets you choose the image files fine. The logo image also loads up and gets resized. And the New Image file is made.
My problem comes with opening the images that were selected. When it gets to "im = PIL.Image.open(img)" it gives me an IOError that says "No such file or directory".
import PIL
import os.path
import PIL.Image
from Tkinter import *
from Tkinter import Tk
from tkFileDialog import askopenfilename
import Tkinter, tkFileDialog
def logo():
imgs = tkFileDialog.askopenfilename(parent=root, title='Choose files', multiple = True)
logo = PIL.Image.open('logo.jpeg')
logo_new = logo.resize((125, 100))
newpath = r'C:\Users\Austin\Desktop\New Images'
if not os.path.exists(newpath):
os.makedirs(newpath)
for img in imgs:
im = PIL.Image.open(img)
width, height = im.size()
im.paste(logo_new, ((width-125), (height-100)), mask=logo_new)
im.save(newpath)
#GUI Code
root = Tk()
root.title("Ad Maker") #GUI title
root.geometry("250x250") #GUI size
app = Frame(root)
app.grid()
button1 = Button(app, text="Logo", command=logo)
button1.grid() #sets button for the logo function
button2 = Button(app, text = "Frame")
button2.grid() #sets button for the frame function
root.mainloop()
I know this code probably isn't the best as I'm still fairly new to programming, but any help in getting the logo() function finally working would be really appreciated.