this is my first question on stack-overflow!
I am currently writing a Python script (well, actually a few scripts) to manage book collections, that I would now like to freeze and distribute (this is my first 'big' project).
After looking at many options I decided to try with Cx_Freeze.
(I'm using Python 3.6 and Cx_Freeze 5.1.1).
In this project I often use 'subprocess' to move from a script to another.
In the interpreter it works just fine, if I let Cx_Freeze make the build folder using
python setup.py build
it works as well, but when I try to create a distributable file with
python setup.py bdist_msi
after installation it starts and works up to the first call for a subprocess, then nothing more.
Here is setup.py
from cx_Freeze import setup, Executable
import os.path
PYTHON_INSTALL_DIR = os.path.dirname(os.path.dirname(os.__file__))
os.environ['TCL_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tcl8.6')
os.environ['TK_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tk8.6')
setup(
name = "Libro",
version = "1.0.0",
options = {"build_exe": {
'packages': ["tkinter", "subprocess", ],
'include_files': [os.path.join(PYTHON_INSTALL_DIR, 'DLLs','tk86t.dll'), \
os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tcl86t.dll'), \
'logo50x50.gif', 'check_1.txt', 'check_2.txt', 'start.py', \
'createdb.py', *and_a_few_more_files*],
'include_msvcr': True,
}},
executables = [Executable("Libro.py",base="Win32GUI")]
And this is the Libro.py script that becomes the executable.
#This script checks the documents check_1 and check_2 and then launches
# createdb.py or start.py
import subprocess
from tkinter import *
import tkinter.messagebox as box
root= Tk()
root.withdraw()
with open('check_1.txt', 'r') as check_1:
for line in check_1:
line = line.strip()
value_1 = int(line)
with open('check_2.txt', 'r') as check_2:
for line in check_2:
line = line.strip()
value_2 = int(line)
if value_1 == 0 and value_2 == 0:
box.showinfo('Libro 1.0', '''
Welcome to the installation of Libro.
I am now creating the database for your catalogue.
This may take a moment.''')
subprocess.call("createdb.py", shell=True)
else:
subprocess.call("start.py", shell=True)
root.mainloop()
It starts, it looks for check_1 and check_2, shows the tkinter showinfo window and then... that's it.
I would be very grateful for any suggestion!! Thanks :)
You would need to freeze all of your scripts, not just the top level one! (create multiple Executable() entries). And then call subprocess to run the frozen executables. If you don't do that you'll end up requiring Python to be installed on the target machine -- and then why freeze any of it! Of course, it might also be helpful to explain why you need to run your code in a subprocess instead of directly.
In the end it looks like it would be easier and more economic to treat my scripts as modules and then import them when needed.
I tried some simplified operations and they seem to work.
For example:
being main.py
from tkinter import *
from modules import from_file
root = Tk()
root.title('Trial window')
btn_1 = Button(root, text='Read from file', command=from_file)
btn_1.grid(row=1, column=1)
and being modules.py
from tkinter import *
def from_file():
ft = open('text.txt', 'r')
string = ''
for line in ft:
line = line.strip()
string = string+line
ft.close()
root2 = Tk()
result = Label(root2, text=string)
result.grid(row=1, column=1)
root2.mainloop()
the script reads and visualize the content of 'text.txt' in the new windows it opens also after being frozen with cx_freeze.
PS The setup.py I used is
from cx_Freeze import setup, Executable
import os.path
PYTHON_INSTALL_DIR = os.path.dirname(os.path.dirname(os.__file__))
os.environ['TCL_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tcl8.6')
os.environ['TK_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tk8.6')
setup(
name = "Prova",
version = "1.0.0",
options = {"build_exe": {
'packages': ["tkinter"],
'include_files' : [os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tk86t.dll'), \
os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tcl86t.dll'), 'text.txt'],
'include_msvcr': True,
}},
executables = [Executable("main.py", base="Win32GUI")]
)
Related
I just finished freezing my program with cx_Freeze. When I try to run it, it just stops without showing any error messages, so I want to know if is there any way to know what's wrong with my program or my freezing script:
import sys, os
from cx_Freeze import setup, Executable
PYTHON_INSTALL_DIR = os.path.dirname(os.path.dirname(os.__file__))
sys.path.append('pandastable')
includes = ["pandastable"]
includefiles = [
os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tk86t.dll'),
os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tcl86t.dll'),'ClasseAgents.py','ClasseData.py','ClassePerformance.py','ClasseTime.py','ClasseTraitement.py','PredictionFlux.py','icone.ico','VCbase.db'
]
# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options = {"packages": ["os","numpy","matplotlib","pandas",
#"scipy","seaborn","IPython","statsmodels",
"pandastable"],
"excludes": ['seaborn','statsmodels'],
"namespace_packages": ['mpl_toolkits'],
"include_msvcr": True,
"includes": includes,
"include_files": includefiles}
base = None
if sys.platform == "win32":
base = "Win32GUI"
executables = [Executable("Main.py", base=base,
#copyDependentFiles = True,
targetName='TaskManager.exe',
shortcutName="TakManaer",
shortcutDir="DesktopFolder",
icon="icone.ico")]
setup( name = "Task manager for BPO",
version = "1.0",
description = "task manager est un gestionnaire de traitement intelligent",
options = {"build_exe": build_exe_options},
executables = executables)
Try to redirect the output of your executable into a file with the following command in a cmd prompt:
TaskManager.exe > out.txt
The content of the output file can then be viewed e.g. with:
type out.txt
The output file might contain additional error messages for applications frozen with the Win32GUI base.
I'd like to import internal exe modules from an external process. I can do this fine from an IDE, but when I package the project with pyinstaller, then run it, the modules can't be found.
The high level flow looks like this:
Importing maya_app from within the external userSetup.py returns an error: ImportError: No modules named maya_app
This post seems like it should help me but there's something about my setup that's preventing this solution from working. That or I'm just doing it wrong.
Is it even possible to import modules from one exe into another exe? Thanks!
Code:
launch_maya.py
import os
import sys
import subprocess
from PySide2 import QtWidgets
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
# GUI
btn_launch = QtWidgets.QPushButton('launch maya')
btn_launch.clicked.connect(self.on_launch)
# Layout
main_layout = QtWidgets.QHBoxLayout(self)
main_layout.addWidget(btn_launch)
self.setLayout(main_layout)
# Root path exe vs ide
if getattr(sys, 'frozen', False):
self.root_path = sys._MEIPASS
else:
self.root_path = os.path.join(os.path.dirname(os.path.realpath(__file__)))
def _set_app_envs(self):
_envs = os.environ.copy()
_envs['MAYA_SCRIPT_PATH'] = os.path.join(self.root_path, 'scripts').replace('\\', '/')
# Python path envs
_python_path_list = [
'C:\\Program Files\\Autodesk\\Maya2020\\Python\\Lib\\site-packages',
'C:\\Program Files\\Autodesk\\Maya2020\\Python\\DLLs',
os.path.join(self.root_path, 'scripts').replace('\\', '/'),
os.path.join(self.root_path, 'internal_source', 'maya_app')
]
# PYTHONPATH exe vs ide
if getattr(sys, 'frozen', False):
_envs['PYTHONPATH'] = os.pathsep.join(_python_path_list)
_envs['PYTHONHOME'] = 'C:\\Program Files\\Autodesk\\Maya2020\\bin'
else:
_envs['PYTHONPATH'] += os.pathsep + os.pathsep.join(_python_path_list)
return _envs
def on_launch(self):
# Maya file path
file_path_abs = '{}/scenes/test.mb'.format(self.root_path).replace('\\', '/')
print(file_path_abs)
app_exe = r'C:/Program Files/Autodesk/Maya2020/bin/maya.exe'
_envs = self._set_app_envs()
if os.path.exists(file_path_abs):
proc = subprocess.Popen(
[app_exe, file_path_abs],
env=_envs,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True,
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = Widget()
window.resize(400, 400)
window.show()
sys.exit(app.exec_())
bundle.spec
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
added_files = [
('./scenes', 'scenes'),
('./scripts', 'scripts')
]
a = Analysis(['launch_maya.py'],
pathex=[
'D:/GitStuff/mb-armada/example_files/exe_bundle',
'D:/GitStuff/mb-armada/dependencies/Qt.py',
'D:/GitStuff/mb-armada/venv/Lib/site-packages',
],
binaries=[],
datas=added_files,
hiddenimports=['internal_source', 'internal_source.maya_app'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='bundle',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='bundle')
maya_app.py
import os
import sys
import subprocess
from PySide2 import QtWidgets
class MainWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MainWidget, self).__init__(parent)
# GUI
btn_launch = QtWidgets.QPushButton('say hey')
btn_launch.clicked.connect(self.on_say_hey)
# Layout
main_layout = QtWidgets.QHBoxLayout(self)
main_layout.addWidget(btn_launch)
self.setLayout(main_layout)
print('I should be alive')
def on_say_hey(self):
print('hey')
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MainWidget()
window.resize(100, 100)
window.show()
sys.exit(app.exec_())
userSetup.py
import os
import sys
import maya.cmds as mc
print('hey')
def tweak_launch(*args):
print('Startup sequence running...')
os.environ['mickey'] = '--------ebae--------'
print(os.environ['mickey'])
root_path = os.getenv('_MMM_ROOT_PATH')
main_app_path = os.path.join(root_path, 'internal_source')
if not root_path in sys.path:
sys.path.append(main_app_path)
from internal_source import maya_app
w = maya_app.MainWidget()
w.show()
print('window should be up')
mc.evalDeferred("tweak_launch()")
At the highest level, this seems like it's really just about path management within your launched maya -- presumably the launcher app knows it's own paths, and the maya you want to fire off needs to be told about their existence.
The most reliable and least magic-prone way to do this is just to have your launcher unbundle any scripts scripts it needs to share with maya proper to a known location -- a hidden directory or even a session-specific temp directory will give you the maximum likelihood that you've got the latest version as your launcher revs.
When launching maya you can pass the paths a few ways -- probably the easiest is just to use site.addsitedir to put them on the path for that session rather than relying on Maya's many other possible search locations. You can launch the maya with the -c mel flag and pass a python command at startup, so you can control this behavior entirely from the launcher and not have to worry about interactions with userSetup.py. Something along these lines would make your stuff available to maya:
# unpack the 'payload' of scripts to share to a
# known location with something like pkgutil.get_data()
# https://docs.python.org/3/library/pkgutil.html
# note the single quotes -- you need them to handle escaping!
py_cmd = "import site; site.addsitedir('{}'); import my_startup_module"
python_to_execute = py_cmd.format(path_to_unpacked_modules)
# again, note annoying escapes
start_cmd = '\"python(\"{}\");\"'.format(python_to_execute)
maya_session = subprocess.Popen(['maya.exe', '-c', start_cmd])
If your exe has unpacked the files to path_to_unpacked_modules, maya will run that mel command, which will call python, add your module dir as a site-packages directory, and then import and run my_startup_module (from that location). This lets your launcher control the startup process and keeps you from having to fiddle separately with userSetup.py -- plus, since it does not touch env vars, you don't have to work to hard to run different sessions side by side.
If the code you want to share is just a bunch of modules (no binary extensions) you can keep your modules in a zip file and add that to the path -- python will be able to find modules inside the zip file automatically. That saves unpack time and also ensure you don't have leftover PYC files from older runs.
There's a lot of useful info in these threads:
https://tech-artists.org/t/python-maya-startup-script/2145/17
https://tech-artists.org/t/deploy-tools-for-your-maya-team/5029/13
When trying to build my file using cx_freeze it spits out an error
raise KeyError(key) from None
KeyError: 'TCL_LIBRARY'
I know there are other posts describing this, but I already tried adding
PYTHON_INSTALL_DIR = os.path.dirname(os.path.dirname(os.__file__))
os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tk86t.dll')
os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tcl86t.dll')
which is what other posts recommend, but i still get the same error
This is my code so far
from cx_Freeze import setup, Executable
import sys, os
shortcut_table = [
("DesktopShortcut", # Shortcut
"DesktopFolder", # Directory_
"program", # Name
"TARGETDIR", # Component_
"[TARGETDIR]main.exe",# Target
None, # Arguments
None, # Description
None, # Hotkey
"icon.ico", # Icon
None, # IconIndex
None, # ShowCmd
'TARGETDIR' # WkDir
)
]
base = None
if sys.platform == "win32": base = "Win32GUI"
os.environ['TCL_LIBRARY'] = "C:\\Program Files\\Python35-32\\tcl\\tcl8.6"
os.environ['TK_LIBRARY'] = "C:\\Program Files\\Python35-32\\tcl\\tk8.6"
msi_data = {"Shortcut": shortcut_table}
bdist_msi_options = {'data': msi_data}
executables = [Executable("main.py", shortcutName='2048', shortcutDir='DesktopFolder', icon='icon.ico', base=base), Executable("extras.pyw"),]
setup(
name = '2048',
author = 'Ethan',
options={
"build_exe": {
"packages":["pygame", "sys", "random", "os", "ctypes"],
"include_files":["scores.txt",
"icon.ico",
]
}},
executables = executables,
version = "1.0"
)
edit: changes to code
after changing the code, it currently runs without error but only creates a folder with an ul-launchable exe
build
exe.win-amd64-3.6
api-ms-win-crt-conio-l1-1-0.dll
api-ms-win-crt-convert-l1-1-0.dll
api-ms-win-crt-environment-l1-1-0.dll
api-ms-win-crt-filesystem-l1-1-0.dll
api-ms-win-crt-heap-l1-1-0.dll
api-ms-win-crt-locale-l1-1-0.dll
api-ms-win-crt-math-l1-1-0.dll
api-ms-win-crt-process-l1-1-0.dll
api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-crt-stdio-l1-1-0.dll
api-ms-win-crt-string-l1-1-0.dll
api-ms-win-crt-time-l1-1-0.dll
main.exe
python36.dll
VCRUNTIME140.dll
EDIT 2:
so i did some more tinkering and found out the TCL error comes from including ctypes(not sure why). So i removed it and changed my python version to 3.6 and it built perfectly. But i would prefer to have ctypes included as i use it to disable application scaling.
is there another way to deal with screen scaling or a way to fix the TCL error?
cx_freeze does not work with python 3.7 so switching to 3.6 allowed it to work, then adding
import os.path
PYTHON_INSTALL_DIR = os.path.dirname(os.path.dirname(os.__file__))
os.environ['TCL_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tcl8.6')
os.environ['TK_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tk8.6')
sets the directory in order to use ctypes
I created a python 3 application that has a tkinter GUI and selenium action upon clicking a button. Here is the setup.py file:
from cx_Freeze import setup, Executable
import sys
import os
from pathlib import Path
buildOptions = {"include_files":['execute.py'], "packages": ['encodings'], "includes": ['numpy.core._methods', 'numpy.lib.format'], "excludes": []}
if sys.platform=='win32':
base = "Win32GUI"
else:
base=None
executables = [
Executable('bot_gui.py', base=base)
]
home_path = str(Path.home())
user_txt_path = []
if sys.platform=='win32':
print("windows detected")
user_txt_path.append('\\user.txt')
else:
print("macos detected")
user_txt_path.append('/user.txt')
#for initialization
f = open(home_path + user_txt_path[0], "w+")
for i in range(12):
f.write("---\n")
setup(name='tachysloth',
version = '1.0',
description = 'test',
options = dict(build_exe = buildOptions),
executables = executables)
The problem is when I run python setup.py build, everything works fine; but when I run python setup.py bdist_mac, the GUI works when you click on the application (called tachysloth-1.0 below)
but when I click on a button to in the GUI to open google.com on Chrome, the button does not do anything.
If you need more information to assess this problem, please let me know and I will provide that information as soon as possible.
Thank you again.
I am trying to build an .exe file of my Python application. I have tried various ways to create a setup file from both Github and Stackoverflow and I keep getting this same error:
pywintypes.error: (2, 'BeginUpdateResource', 'The system cannot find the file specified.').
I am using Python 3.6.1, and cx_Freeze to build the application.
Here are the different setup files I have tried:
Attempt 1:
import os.path
from cx_Freeze import setup, Executable
os.environ['TCL_LIBRARY'] = r'C:\Users\RedCode\AppData\Local\Programs\Python\Python36-32\tcl\tcl8.6'
os.environ['TK_LIBRARY'] = r'C:\Users\RedCode\AppData\Local\Programs\Python\Python36-32\tcl\tk8.6'
executables = [Executable("myprogram.py", base="Win32GUI")]
options = {
'build_exe': {
'include_files': [
os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tk86t.dll'),
os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tcl86t.dll'),
]
},
}
setup(
name="Standalone.exe",
options=options,
version="0.1",
description='Simple tkinter application',
executables=executables, requires=['cx_Freeze', 'cx_Freeze']
)
Attempt 2:
from cx_Freeze import setup, Executable
import sys
import os
productName = "My Program"
if 'bdist_msi' in sys.argv:
sys.argv += ['--initial-target-dir', r'C:\Users\RedCode\PycharmProjects\Standalone' + productName]
sys.argv += ['--install-script', 'myprogram.py']
os.environ['TCL_LIBRARY'] = r'C:\Users\RedCode\AppData\Local\Programs\Python\Python36-32\tcl\tcl8.6'
os.environ['TK_LIBRARY'] = r'C:\Users\RedCode\AppData\Local\Programs\Python\Python36-32\tcl\tk8.6'
exe = Executable(
script="myprogram.py",
base="Win32GUI",
targetName="Product.exe"
)
setup(
name="Standalone.exe",
version="1.0",
author="Me",
description="Copyright 2012",
executables=[exe],
scripts=[
'myprogram.py'
]
)
Attempt 3:
import sys
import os
from cx_Freeze import setup, Executable
build_exe_options = {"packages": ["os"], "includes": ["tkinter"]}
def s_SourceFile():
if (sys.argv[0] == ''):
return __file__
else:
return sys.argv[0]
os.environ['TCL_LIBRARY'] = r'C:\Users\stefano.demattia\AppData\Local\Programs\Python\Python36-32\tcl\tcl8.6'
os.environ['TK_LIBRARY'] = r'C:\Users\stefano.demattia\AppData\Local\Programs\Python\Python36-32\tcl\tk8.6'
setup(name="Standalone",
version="0.1",
description="My GUI application!",
options={"build_exe": build_exe_options},
executables=[Executable("myprogram.py", base="Win32GUI", targetName="Standalone.exe")])
Attempt 4:
application_title = "Car Database"
main_python_file = "cardb.py"
import sys
from cx_Freeze import setup, Executable
base = None
if sys.platform == "win32":
base = "Win32GUI"
setup(
name = "myprogram.py",
version = "0.1",
description = "Simple embedded database application",
executables = [Executable("myprogram.py", base = base)])
I have also tried pyinstaller but it keeps giving me a tuple out of range error no matter what I do, and I tried bbfreeze which I can't even get to install. I have checked my files and everything is there, so I do not see how it can still tell me a specified file is not found.
What else can I do or how can I edit the above attempts so that the application actually builds properly? Is there even a way to determine which file is missing?
Remove the version, I got the same error, looked at the code and noticed it was referencing versioning in the source, so thought I'd try it, and it fixed it for me.
I know the poster probably no longer needs this, but posting it for others that come across this in the future since I didn't see the answer anywhere else.
cx_Freeze.setup(
name="SampleName",
options={"build_exe": {"packages": ["tkinter", "os"],
"include_files": ['tcl86t.dll', 'tk86t.dll', 'closenew.png', 'sujata.png', 'config.py', 'ui_attributes.py']}},
version="0.01", # Remove this line
description="Tkinter Application",
executables=executables
)
I have removed the version and it worked.
cx_Freeze.setup(
name="SampleName",
options={"build_exe": {"packages": ["tkinter", "os"],
"include_files": ['tcl86t.dll', 'tk86t.dll', 'closenew.png', 'sujata.png', 'config.py', 'ui_attributes.py']}},
description="Tkinter Application",
executables=executables
)