I have a python script which gets an image from the internet, downloads it, sets as desktop background and updates after on minute. The problem is most likely cx_Freeze not including the os module, as the same code with absolute paths works fine. My code also works perfectly, until it goes through freezing. It works before it is frozen when I load throught the console, run from IDLE or double-click on it. Whenever i run the frozen file I get the error (If i use setup.py or cxfreeze file.py:
C:\Python33\Scripts>C:\Python33\Scripts\dist\desktopchanger.exe
Traceback (most recent call last):
File "C:\Python33\lib\site-packages\cx_Freeze\initscripts\Console3.py", line 2
7, in <module>
exec(code, m.__dict__)
File "C:\Python33\desktopchanger.pyw", line 7, in <module>
dir = path.dirname(__file__)
NameError: name '__file__' is not defined
My Code
import pythoncom
from urllib import request
from win32com.shell import shell, shellcon
from time import sleep
from os import path
dir = path.dirname(__file__) #get dierctory script is in
startpath = str(path.join(dir+'/bg/bg.jpg')) #add /bg/bg.jpg to path of script
pathtoimg=[]
for char in startpath:
if char != "/":
pathtoimg.append(char) #replace / with \, necessary for setting bg
else:
pathtoimg.append("\\")
newpath = "".join(pathtoimg)
def get_image():
f = open(newpath, 'wb') #open .....\bg\bg.jpg
f.write(request.urlopen('http://blablabl.com/totale.jpg? i=0.387725243344903').read()) #get image from web and write over previous file
f.close()
while 1:
get_image()
#sets background below
iad = pythoncom.CoCreateInstance(shell.CLSID_ActiveDesktop, None,
pythoncom.CLSCTX_INPROC_SERVER, shell.IID_IActiveDesktop)
iad.SetWallpaper(newpath, 0)
iad.ApplyChanges(shellcon.AD_APPLY_ALL)
sleep(60)
setup.py
from cx_Freeze import setup, Executable
exe=Executable(
script="desktop_changer_with_url2.py",
base="Win32Gui",
icon="icon.ico"
)
includes = ["os","urllib","time","pythoncom","win32com.shell"]
setup(
name = "Heindl Webcam als Desktop" ,
version = "1",
description = "eowjbadpoaäbaaplabipösdbjosdobsaboösac bjcaähpdaöbökabidsidsöds.",
executables = [exe],
)
Source:
http://cx-freeze.readthedocs.org/en/latest/faq.html
Your old line:
dir = path.dirname(__file__)
Substitute this with the following lines to run your script both frozen or unfrozen:
if getattr(sys, 'frozen', False):
# frozen
dir_ = os.path.dirname(sys.executable)
else:
# unfrozen
dir_ = os.path.dirname(os.path.realpath(__file__))
Tested with python 3.3.4. on win32
upd.: changed in accordance with the comment
Related
I found the answer
So it looks like PyInstaller actually runs in a temp directory, not your own, which explains my issue. This is an explanation for that. I guess I will keep this up incase people in the future have problems.
Original question
I am trying to use PyInstaller to create an executable of a simple python script called test.py that just creates a text file and adds numbers to it, as a test of PyInstaller. This file works correctly when run normally.
from os.path import dirname, abspath
def main():
txtfile = dirname(abspath(__file__)) + '/text.txt'
with open(txtfile, 'w') as f:
for num in range(101):
f.write(str(num) + '\n')
if __name__ == '__main__':
main()
print('script executed')
When I use:
pyinstaller test.py --onefile in the same directory as test.py it successfully creates the dist file with the binary file test inside of it.
when I cd dist and do ./test to execute the file inside dist it successfully prints out main called and script executed but it doesn't actually create the file. So, main is being called and the script is being executed, but the file isn't created at all, and I am quite confused about what I'm doing wrong..I must be getting file paths messed up? But I have specified the exact full path with os.path, so it doesn't make sense to me.
The system exit code is 0, and there are no errors raised when I call ./test
I found this that shows that PyInstaller will save to a temp file. I created this script below to check if the script is being executed directly or via PyInstaller.
import os
import sys
def create_file(path):
with open(path + '/test.txt', 'w') as f:
for num in range(101):
f.write(str(num) + '\n')
def check_using_pyinstaller():
if getattr(sys, 'frozen', False):
application_path = os.path.dirname(sys.executable)
return application_path
return os.path.dirname(os.path.abspath(__file__))
def main():
path = check_using_pyinstaller()
os.chdir(path)
create_file(path)
if __name__ == '__main__':
main()
How can i make an executable that can load image, and that my friends don't depend on downloading the executable and image or a heavy folder?
I'm trying to create an executable for my python program, but when I try to open its executable, I get this error:
Traceback (most recent call last):
File "main.py", line 49, in <module>
File "tkinter\__init__.py", line 4103, in __init__
File "tkinter\__init__.py", line 4048, in __init__
_tkinter.TclError: couldn't open "logo.png": no such file or directory
The code I'm using to compile is this:
pyinstaller --onefile --windowed --add-data "logo.png;." main.py
Is there any other compilation method or other way to make an executable with the logo attached? I tried some methods like:
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
return os.path.join(base_path, relative_path)
But to no avail, and my complete code is this:
from pytube import Playlist, YouTube
from tkinter import *
from moviepy import *
import os
import re
if os.name == "nt":
downfolder = f"{os.getenv('USERPROFILE')}\\Downloads"
else:
downfolder = f"{os.getenv('HOME')}/Downloads"
def playlist_audio():
get_link = link_field.get()
p = Playlist(get_link)
p._video_regex = re.compile(r"\"url\":\"(/watch\?v=[\w-]*)")
for video in p.videos:
screen.title('downloading.')
mp3_audio = video.streams.filter(only_audio=True).first().download(downfolder)
audioconvert(mp3_audio)
#
screen.title('completed')
def video_mp3():
get_link = link_field.get()
screen.title('downloading.')
mp3_audio = YouTube(get_link).streams.filter(only_audio=True).first().download(downfolder)
audioconvert(mp3_audio)
#
screen.title('completed')
def audioconvert(mp3_audio):
try:
base, ext = os.path.splitext(mp3_audio)
new_file = base + '.mp3'
os.rename(mp3_audio , new_file)
except WindowsError:
os.remove(new_file)
os.rename(mp3_audio , new_file)
screen = Tk()
title = screen.title('Python mp3')
screen.resizable(False, False)
Canvas = Canvas(screen, width=400, height=200)
Canvas.pack()
logoimg = PhotoImage(file='logo.png')
logo_img = logoimg.subsample(2, 2)
Canvas.create_image(215, 25, image=logo_img)
link_field = Entry(screen, width=50)
link_label = Label(screen, text="Enter the song/playlist URL.")
Canvas.create_window(200, 90, window=link_field)
Canvas.create_window(200, 70, window=link_label)
def convert():
try:
video_mp3()
return True
except:
screen.title("wait a few seconds")
try:
playlist_audio()
return False
except:
screen.title("unknown error, try again")
download = Button(screen, text="download", bg='green', padx='20', pady='5',font=('Arial', 12), fg='#fff', command=convert)
Canvas.create_window(200, 140, window=download)
screen.mainloop()
I was able to get this to work.
In your python code add this to access your image from the MEI directory
photopath = os.path.join(os.path.dirname(__file__), 'logo.png')
logoimg = PhotoImage(file=photopath)
Here are the steps I took.
create empty folder; cd into it; copy the logo.png an script(main.py)
py -m venv venv && venv\Scripts\activate
py -m pip install --upgrade pip pyinstaller moviepy pytube
pyinstaller -F main.py
Then in the spec file add on line 11 (i think) edit datas=[('logo.png','.')],
You can make other adjustments in the spec file as well like setting console=False for it to work in windowed mode and changing the name of the output executable file and other stuff.
pyinstaller main.spec
Thats it... the executable should work fine and load your photo and all.
I have two files, one bell.mp3 one main.py file, that plays back bell.mp3 via subprocess.
If I do:
pyinstaller main.py
the Dist file ends up correctly, and everything works fine, The program runs in a directory.
This is the code for my file which i call pyinst_tester.py
it creates a text file, and plays a bell.mp3 file
#
from con import * # this is just a configuration file that has g='play' in it.
import subprocess
f=open(r'/home/godzilla/Desktop/Pyinstaller testing/testfile1','w')
f.write('This has worked')
f.close()
file='/home/godzilla/Desktop/Pyinstaller testing/data/bell.mp3'
if 'play' == g:
subprocess.call(['/usr/bin/cvlc',file])
a single file is created, but if I delete the bell.mp3 file it doesn't work. In a single file isn't the bell.mp3 zipped inside the main.exe ? therefore, redundant as a separate file ?
What Is the point having a single file exe, if you need an adjacent file with all the mp3s inside?
Pyinstaller has many features and if you want to include non python files (for example mp3 files) you have to do so explicitly with the --add-binary switch.
In one file mode the executable will be unpacked into a temporary directory prior to execution of the python code.
So how to write your code to access these data files.
You might want to look at the pyinstaller documention at following sections:
https://pyinstaller.readthedocs.io/en/stable/runtime-information.html#run-time-information
https://pyinstaller.readthedocs.io/en/stable/runtime-information.html#using-sys-executable-and-sys-argv-0
I personally place all my files in a separate directory. e.g. data.
If you place the file bell.mp3 in the directory data, then you had to call pyinstaller with the option --add-binary data:data
in the one file mode the executable is extracted into a temporary directory
whose path you get get from the variable sys._MEIPASS
Your data directory will bi in the sub directory data of sys._MEIPASS
In my example I create a function, that will be able to locate the data files in normal python mode and in pyinstaller one file or one directory mode.
Just try it out it should be self explaining
simple example:
minitst.py
import os, sys
import time
is_frozen = getattr(sys, "frozen", False)
MYDIR = os.path.realpath(os.path.dirname(__file__))
def data_fname(fname):
if is_frozen:
return os.path.join(sys._MEIPASS, "data", fname)
else:
return os.path.join(MYDIR, "data", fname)
def main():
print("This application is %s frozen" %
("" if is_frozen else "not"))
print("executable =", sys.executable,
"File =", __file__,
"mydir =", MYDIR)
if is_frozen:
print("MEIPASS", sys._MEIPASS)
fname = data_fname("tst.txt")
print("will open", fname)
with open(fname) as fin:
print(fin.read())
time.sleep(5) # this shall allow to view the console e.g. on windows if clicking on the executable.
if __name__ == "__main__":
main()
now create a directory data and place a file "tst.txt"
data/tst.txt
Hello world
Now call
pyinstaller -F minitst.py --add-binary data:data -c
and call dist/minitst from a console.
The output should look like:
This application is frozen
executable = /home/gelonida/so/pyinst/dist/minitst File = minitst.py mydir = /home/gelonida/so/pyinst
MEIPASS /tmp/_MEIKGqah9
will open /tmp/_MEIKGqah9/data/tst.txt
Hello
Now concerning your code.
I compacted the code to determine the datadir a little, but it is the same logic as in the upper example
import os, sys
from con import * # this is just a configuration file that has g='play' in it.
import subprocess
basedir = getattr(sys, "_MEIPASS", os.path.realpath(os.path.dirname(__file__)))
f=open('testfile1','w')
f.write('This has worked')
f.close()
file=os.path.join(basedir, 'data/bell.mp3')
if 'play' == g:
subprocess.call(['/usr/bin/cvlc',file])
This question already has answers here:
How do you properly determine the current script directory?
(16 answers)
Closed 6 months ago.
def main():
fh = open('lines.txt')
for line in fh.readlines():
print(line)
if __name__ == "__main__": main()
Directory files
I am on for-working.py file, and am trying to access the lines.txt file within the same working directory. But I get error
No such file or directory: 'lines.txt'
Does python need to have an absolute path when opening files?
why doesn't this relative path work here?
Running python 3.6
EDIT ^1 I'm running visualstudio code with the python package extension by Don Jayamanne, and "Code Runner" package to compile/execute python code
EDIT ^2 Full error:
Traceback (most recent call last):
File "c:\www\Ex_Files_Python_3_EssT(1)\Ex_Files_Python_3_EssT\Exercise Files\07 Loops\for-working.py", line 11, in <module>
if __name__ == "__main__": main()
File "c:\www\Ex_Files_Python_3_EssT(1)\Ex_Files_Python_3_EssT\Exercise Files\07 Loops\for-working.py", line 7, in main
fh = open('lines.txt', 'r')
FileNotFoundError: [Errno 2] No such file or directory: 'lines.txt'
EDIT ^3 checking sys.path
import sys
print(sys.path)
produces this information:
['c:\\www\\Ex_Files_Python_3_EssT(1)\\Ex_Files_Python_3_EssT\\Exercise Files\\07 Loops',
'C:\\Users\\Kagerjay\\AppData\\Local\\Programs\\Python\\Python36\\python36.zip', 'C:\\Users\\Kagerjay\\AppData\\Local\\Programs\\Python\\Python36\\DLLs', 'C:\\Users\\Kagerjay\\AppData\\Local\\Programs\\Python\\Python36\\lib', 'C:\\Users\\Kagerjay\\AppData\\Local\\Programs\\Python\\Python36', 'C:\\Users\\Kagerjay\\AppData\\Local\\Programs\\Python\\Python36\\lib\\site-packages']
EDIT ^4 checking os.getcwd()
Running
import os
print(os.getcwd())
Produces
c:\www\Ex_Files_Python_3_EssT(1)\Ex_Files_Python_3_EssT\Exercise Files
Well its definitely not in the right subdirectory (needs to cd 07 loops folder, that narrows the issue down
EDIT ^5 what is in lines.txt file
My lines.txt file i am opening looks like this. No extra whitespace or anything at start
01 This is a line of text
02 This is a line of text
03 This is a line of text
04 This is a line of text
05 This is a line of text
IN SUMMARY
Visual studio code's Code runner extension needs to be tweaked slightly to open files within a subdirectory so any of the below answers would provide a more robust solution to be independent of any extension / dependencies with the IDE
import os
print(os.getcwd())
Is most useful for diagnosing problem to the current directory python interpreter sees
Get the directory of the file, and join it with the file you want to open:
def main():
dir_path = os.path.dirname(os.path.realpath(__file__))
lines = os.path.join(dir_path, "lines.txt")
fh = open(lines)
for line in fh.readlines():
print(line)
if __name__ == "__main__": main()
This should do the trick.
def main():
fh = open('lines.txt')
for line in fh.readlines():
print(line)
if __name__ == "__main__":
import os
curr_dir = os.path.dirname(os.path.realpath(__file__)) # get's the path of the script
os.chdir(curr_dir) # changes the current path to the path of the script
main()
I faced the same trouble today. The solution was outside the code, in the environment.
Shows the picture of VSCode settings editor
Command prompt in VSCode opens with the directory where VSCode executable is placed. When you execute the code, Python is searching for the file in the location where the VSCode executable is located.
This setting can be changed to the directory that you are working in(shown in figure). So when you are running the code in yourprog.py file, the interpretor is started in your working directory.
Then the VScode runner will do the way you are thinking.
okay summary of working solutions to my problem from others not mentioned in post
Direct absolute path manually
def main():
fh = open('lines.txt')
for line in fh.readlines():
print(line)
if __name__ == "__main__":
import os
curr_dir = 'c:\\www\\Ex_Files_Python_3_EssT(1)\\Ex_Files_Python_3_EssT\\Exercise Files\\07 Loops'
os.chdir(curr_dir)
main()
I commented out unnecessary parts ... and open(lines at beginning
Below solution is very good for lazy implementation tests (e.g. copy-paste template), since all the absolute path correction code is up top seperated from everything else (my preferred solution)
import os
dir_path = os.path.dirname(os.path.realpath(__file__))
lines = os.path.join(dir_path, "lines.txt")
# open(lines)
# ...
def main():
fh = open(lines)
for line in fh.readlines():
print(line)
if __name__ == "__main__": main()
Most robust solution would be this one below, since its self-contained only in the function definition when main() gets called. Original answer didn't include the import os so I included it here
def main():
import os
dir_path = os.path.dirname(os.path.realpath(__file__))
lines = os.path.join(dir_path, "lines.txt")
fh = open(lines)
for line in fh.readlines():
print(line)
if __name__ == "__main__": main()
I'm having troubles with praw, cx_freeze, pyside and requests, before freezing everything works fine, but when i freeze this happens, something with requests goes wrong i think:
http://pastie.org/10614254
This is the project that i'm working with: https://github.com/MrCappuccino/WallDit-QT
This is my setup.py: https://gist.github.com/MrCappuccino/0f1b0571d29d47a95895
import sys
import cx_Freeze
import PySide
import praw
import requests.certs
from cx_Freeze import setup, Executable
exe = Executable(
script="WallDit_QT.py",
base="Win32GUI",
targetName="WallDit_QT.exe"
)
#includefiles = ['README.txt', 'CHANGELOG.txt', 'helpers\uncompress\unRAR.exe', , 'helpers\uncompress\unzip.exe']
#build_exe_options = {"packages": ["os"], "includefiles": ['README.txt', 'CHANGELOG.txt']}
setup(name = 'WallDit_QT',
version = '1.0',
author = 'Disco Dolan',
description ='Set your wallpaper interactively!',
executables = [exe],
options = {'build.exe': {"include_files":['cacert.pem', 'praw.ini', 'README.md']}},
requires = ['PySide', 'cx_Freeze', 'praw', 'shutil', 'requests']
)
could anybody help out?
I have tried adding cacert.pem, to no avail, at this point i have no more ideas
For some frozen applications, you have to set the the cacert (or external data in general) path inside the frozen applications.
Setup.py Section
You first need to include it in your build options and manually specify the installation directory. This is the only part that goes inside the setup.py:
# notice how I say the folder the certificate is installed
{"include_files":[(requests.certs.where(),'cacert.pem')]}
In your case, this produces the following setup file:
import requests
import sys
# more imports
setup(name = 'WallDit_QT',
version = '1.0',
author = 'Disco Dolan',
description ='Set your wallpaper interactively!',
executables = [exe],
options = {
'build.exe': {
"include_files": [
(requests.certs.where(),'cacert.pem'),
'praw.ini',
'README.md'
]
}
},
requires = ['PySide', 'cx_Freeze', 'praw', 'shutil', 'requests']
)
Application Section
You then need to get the certificate path at runtime inside the frozen application.
For PyInstaller, a path is defined at runtime to the data directory called _MEIPASS (which can be gotten from sys._MEIPASS), allowing you to access all the data required for the application. In the case of cacert.pem, the path would be determined as follows:
cacertpath = os.path.join(sys._MEIPASS, "cacert.pem")
For cx_Freeze, the path can be determined from the path of the installation and joining it with the data desired. Here, we get the path as follows:
cacertpath = os.path.join(datadir, 'cacert.pem')
You can get the data directory easily for frozen applications with the following:
datadir = os.path.dirname(sys.executable)
(Please note that this won't work with a non-frozen application, so to ensure it works for both frozen and non-frozen applications, Cx_Freeze recommends you code it as follows):
def find_data_file(filename):
if getattr(sys, 'frozen', False):
# The application is frozen
datadir = os.path.dirname(sys.executable)
else:
# The application is not frozen
# Change this bit to match where you store your data files:
datadir = os.path.dirname(__file__)
return os.path.join(datadir, filename)
You then include this path all your requests module GET and POST requests as follows:
request.get(url, headers=headers, verify=cacertpath)
Example 1
An example code snippet would be as follows:
# load modules
import os
import sys
import requests
# define our path finder
def find_data_file(filename):
if getattr(sys, 'frozen', False):
# The application is frozen
datadir = os.path.dirname(sys.executable)
else:
# The application is not frozen
# Change this bit to match where you store your data files:
datadir = os.path.dirname(__file__)
return os.path.join(datadir, filename)
# get our cacert path and post our GET request
cacertpath = find_data_file('cacert.pem')
r = requests.get('https://api.github.com/events', verify=cacertpath)
# print the text from the request
print(r.text)
Example 2
You can also tell requests where to find the certificate in the future by doing the following:
os.environ["REQUESTS_CA_BUNDLE"] = cacertpath
In this case, we would do the following. The advantage here is that the cacertpath does not to be explicitly defined in every module (or imported from another module) and can be defined in the environment.
import os
import sys
import requests
def find_data_file(filename):
if getattr(sys, 'frozen', False):
# The application is frozen
datadir = os.path.dirname(sys.executable)
else:
# The application is not frozen
# Change this bit to match where you store your data files:
datadir = os.path.dirname(__file__)
return os.path.join(datadir, filename)
cacertpath = find_data_file('cacert.pem')
os.environ["REQUESTS_CA_BUNDLE"] = cacertpath
r = requests.get('https://api.github.com/events')
r.text