Error after compiling with cx_freeze
Desktop (please complete the following information):
OS: WINDOWS
Describe the bug
Hello, after compiling with cx_freeze, I get the error cannot import name "utility" from partially initialized module "ttkbootstrap" (most likely due to a circular import)
No vs code opens normally, this error occurs when opening the executable.
Any ideas on how to resolve this?
This happens in the code below.
import tkinter as tk
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
class Main(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.resizable(True, True)
w = 1024
h = 768
ws = self.winfo_screenwidth()
hs = self.winfo_screenheight()
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
self.geometry('%dx%d+%d+%d' % (w, h, x, y))
self.title("TITLE")
b1 = ttk.Button(self, text="BUTTON 1", bootstyle=SUCCESS)
b1.pack(side=LEFT, padx=5, pady=10)
b2 = ttk.Button(self, text="BUTTON 2", bootstyle=(INFO, OUTLINE))
b2.pack(side=LEFT, padx=5, pady=10)
if __name__ == "__main__":
Main()
tk.mainloop()
To Reproduce
No response
Expected behavior
No response
Screenshots
No response
Additional context
No response
That's strange. I've not used cxfreeze before. I'll experiment with it this weekend and see if I can identify the issue.
There may be a few issues here. One of the issues is that cx_freeze is not pulling in all of the tcl data files. Typically this is not an issue with Tkinter alone. However, I'm using additional TCL functionality that is defined in one of the tcl folders it is ignoring. This is not a ttkbootstrap bug, but I'll see if I can identify the cx_freeze configuration needed to pull these in.
I had the same issue and got it solved through building with the following command which was inspired from a similar case I found on stackoverflow (https://stackoverflow.com/questions/66821044/circular-dependency-while-executing-cx-freeze-result):
python setup.py build_exe -p ttkbootstrap
@actioncodes, haven't forgotten about this, but looking for a better solution.
The missing files are in ...Python\Python39\tcl\tcl8
cx_Freeze is not grabbing these files. I'm not sure how to tell to grab the files either. But, you can manually copy and paste this folder into lib\tkinter in your build folder and it will work.
This is not a bug with ttkbootstrap, but rather an issue with cx_Freeze not pulling in all of the required library files from tkinter. There is a work-around above where you can copy the missing folder "tcl8" into the build folder. I'm not familiar enough with cx_Freeze to propose a cx_Freeze solution, but perhaps someone in the community can figure that out.
I built a solution that works in my cx_freeze setup scripts. I use a variable BUILDPATH to specify the directory for my distribution. After the call of the cx_freeze setup function I added a call to shutil.copytree to copy the missing tcl8 directory to the distribution directory.
from typing
import List
import shutil
import os
import sys
from cx_Freeze import setup, Executable
NAME = "PictureTools"
DESCRIPTION = "Bearbeiten von Bild Dateien"
VERSION = "1.0.0"
BUILDPATH = "..\\distribution\\PictureTools"
includes: List[str] = []
excludes: List[str] = ['bsddb', 'curses', 'pywin.debugger', 'pywin.debugger.dbgcon', 'pywin.dialogs',
'asyncio', 'lib2to3', 'unittest', 'pydoc_data', 'email', 'html', 'http',
'mypy', 'numpy', 'scipy', 'test', 'pycparser', 'wx', 'cffi', 'ctypes']
includefiles: List[str] = [".\\images\\"]
packages: List[str] = ['ttkbootstrap', 'tkinter']
path:List[str] = []
Target_1 = Executable(
script = "picturetools.pyw",
# initScript = None,
base = 'Win32GUI', # 'Win32GUI' or 'console' or "Win32Service ???"
icon = '.\\images\\icon.ico',
target_name = NAME + ".exe"
)
# ------------------------------------------------------------------------------------------------------------
def main() -> None:
"""Hauptprogramm"""
setup(version = VERSION,
description=DESCRIPTION,
author = "Heribert",
name = NAME,
packages = [],
options = {"build_exe": {
"includes": includes,
"excludes": excludes,
"packages": packages,
"path": path,
"include_files": includefiles,
"silent": True,
"optimize": 2,
"build_exe": BUILDPATH
},
},
executables = [Target_1]
)
# Copy tcl8 directory for bootstrap
# Due to changes in the directory structure theta cx_freeze creates I changed that to
for mypath in sys.path:
if os.path.exists(os.path.join(mypath, "tcl\\tcl8")):
shutil.copytree(os.path.join(mypath, "tcl\\tcl8"), os.path.join(BUILDPATH, ".\\lib\\tcltk\\tcl8"))
break
# ---------------------------------------------------------------------------
if __name__ == '__main__':
main()
The above shutil doesn't find the tcl8 directory if you use a python virtual environment. So I modified it to work in venv:
for mypath in sys.path:
if os.path.exists(os.path.join(mypath, "tcl\\tcl8")):
shutil.copytree(os.path.join(mypath, "tcl\\tcl8"), os.path.join(BUILDPATH, ".\\lib\\tcltk\\tcl8"))
break
This is a hook for that problem. I inserted it into hooks/init.py in the new pre release 6.12.0.dev1
def load_ttkbootstrap(finder: ModuleFinder, module: Module) -> None:
"""The ttkbootstrap package needs the tcl8 directory.
The tkinter module has data files (also called tcl/tk libraries) that
are required to be loaded at runtime."""
folders = []
tcltk = get_resource_file_path("bases", "tcltk", "")
if tcltk and tcltk.is_dir():
# manylinux wheels and macpython wheels store tcl/tk libraries
folders.append(("TCL_LIBRARY", list(tcltk.glob("tcl*"))[0]))
else:
# Windows, MSYS2, Miniconda: collect the tcl/tk libraries
try:
tkinter = __import__("tkinter")
except (ImportError, AttributeError):
return
root = tkinter.Tk(useTk=False)
source_path = Path(root.tk.exprstring("$tcl_library"))
folders.append(("TCL_LIBRARY", source_path))
for env_name, source_path in folders:
# get the root tcl dir. If soure_path.name = tcl8.6 then tcl8
new_name = os.path.splitext(source_path.name)[0]
target_path = Path("lib", "tcltk", new_name)
finder.include_files(os.path.join(os.path.split(source_path)[0], new_name), target_path)
Marcelo Duarte modified the latest cx_freeze development release 6.15.0.dev1 so that the tkinter hook now copies the tcl8 directory needed for ttkbootstrap. You can get it with: pip install --upgrade --pre --extra-index-url https://marcelotduarte.github.io/packages/ cx_Freeze