I'm having a python project and use git as version control software.
The software will be deployed using Cx_Freeze.
I would like to display the version and author (and possibly other metadata) captured during the build process (freezing the script) within an About dialogue in my Application.
This is an example of the setup script:
import sys
import subprocess
from cx_Freeze import setup, Executable
build_exe_options = {}
base = "Win32GUI"
version = subprocess.run(['git', 'describe', '--abbrev=4', '--dirty', '--always', '--tags'],
capture_output=True, encoding='utf-8').stdout.strip()
setup(
name="XY",
version=version,
author="My Name",
description="Mysterious GUI application",
options={"build_exe": build_exe_options},
executables=[Executable("XY.py", base=base)],
)
Simple example of an About dialogue:
from tkinter import messagebox
def on_about():
messagebox.showinfo(f'About', 'Software XY, written by {author}, version {version}')
# Should display e.g. 'Software XY, written by My Name, version 4b06-dirty'
Does anyone know if this is possible and how to achieve this?
Thanks to all in advance!
I came up with a first solution where I create a sub-module within the main package of my application when the setup script is being executed. I import the __version__ variable into the __init__.py of that package only when its frozen and if the sub-module exists:
setup.py:
import subprocess
import os.path
import mymodule
from cx_Freeze import setup, Executable
def create_versionmodule(packagepath: str):
"""
creates a file packagepath/_version.py which defines a __version__ variable
which contains the stripped output of "git describe --dirty --always --tags"
"""
version = subprocess.run(['git', 'describe', '--dirty', '--always', '--tags'],
capture_output=True, encoding='utf-8').stdout.strip()
with open(os.path.join(packagepath, '_version.py'), mode='w', encoding='utf-8') as file:
file.write(f'__version__: str = {version!r}\n')
build_exe_options = {}
base = "Win32GUI"
create_versionmodule(packagepath=os.path.dirname(mymodule.__file__))
setup(
name="XY",
description="Mysterious GUI application",
options={"build_exe": build_exe_options},
executables=[Executable("XY.py", base=base)],
)
mymodule/__init__.py:
import sys as _sys
__version__ = 'UNKNOWN'
if getattr(_sys, "frozen", False):
try:
from mymodule._version import __version__
except ModuleNotFoundError:
pass
Now I can access the version variable from everywhere in my code:
import mymodule
from tkinter import messagebox
def on_about():
messagebox.showinfo(f'About', 'Software XY, version {mymodule.__version__}')
Related
hello
I try to learn Python ...
I did by myself a little software for read data from XLSX, every things runs good when I launch by the "normal way / python way " (ctrl + B in sublime text).
... BUT ...
When I compil it to get my ".exe" with "cx.freeze" and when launch my .exe, I get this error window :
(https://i.stack.imgur.com/E2GVw.png)
I tryed with the library asked I updated all my library but nothing
here the begin and the end of my code with the library installed by PIP:
# c-*- coding: utf-8 -*-
# Bibliotheques
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import openpyxl
import xlrd
import mpl_toolkits
import sys
import os
from tkinter import *
from tkinter import messagebox
from tkinter.filedialog import * # askopenfilename
from functools import partial
from PIL import Image, ImageTk
class MyApp(Tk): # --- Class.N°1 --- #
def __init__(self):
Tk.__init__(self)
if __name__ == '__main__':
MyApp()
here the CX.freeze scrypt I used :
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')
base = "Win32GUI" #pour appli graphique ss windows
#base = "console" #pour appli console
options = {
'build_exe': {
'include_files':[
os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tk86t.dll'),
os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tcl86t.dll'),
],
},
}
# On appelle la fonction setup
setup(name = "GraphEditor",
options = options,
version = "V1.1.2",
author = "Scorn",
description = "Reading and editing trends from 2D table",
executables = [Executable("GraphEditor.py",base=base, icon="xln.ico")]
)
So my question is : why I have this error, and how I can solve it ?
thx you for your time and your answer :)
I found that CXFreeze had not worked well in many cases. So I prefer to use Nuitka as an alternative. It's quite straightforward to use.
nuitka --file-reference-choice=runtime --recurse-to=[some_module] main.py
I used Nuitka to freeze a very big Python app (integrated web server using NumPy and OpenGL). Some reports say there's some problem when compiling NumPy. But I think Pandas will be fine with it.
I solved my problem.
I used the very very good: Auto PY to EXE https://pypi.org/project/auto-py-to-exe/ .
I downgrade my Python to V 3.7
Take care to clean your library because for example: I had 2 times Tkinter, downloaded one time by PIP and already inside the standard library.
Here is the current state of my twistd plugin, which is located in project_root/twisted/plugins/my_plugin.py:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from zope.interface import implements
from twisted.plugin import IPlugin
from twisted.python.usage import Options
from twisted.application import internet, service
from mylib.io import MyFactory
class Options(Options):
"""Flags and options for the plugin."""
optParameters = [
('sock', 's', '/tmp/io.sock', 'Path to IO socket'),
]
class MyServiceMaker(object):
implements(service.IServiceMaker, IPlugin)
tapname = "myplugin"
description = "description for my plugin"
options = Options
def makeService(self, options):
return internet.UNIXServer(options['sock'], MyFactory())
There is no __init__.py file in project_root/twisted/plugins/
The output of twistd doesn't show my plugin when run from the project's root directory
I installed my library via python setup.py develop --user and it is importable from anywhere
Any ideas?
As suspected, it was something very simple: I needed to instantiate an instance of MyServiceMaker, so simply adding service_maker = MyServiceMaker() at the bottom of the script fixes the issue.
Platform is Windows 7 64bit using python 2.7 and GTK3+ installed from http://sourceforge.net/projects/pygobjectwin32/files/?source=navbar
The exe is compiled but fails to run, due to this
The following modules appear to be missing
['gi.repository.Gdk', 'gi.repository.Gtk', 'overrides.registry']
How can i properly include these files?
imports in my .py file
from gi.repository import Gtk, Gdk
my setup file
#!/usr/bin/env python
from distutils.core import setup
import py2exe, sys
sys.path.append("C:\Python27\Lib\site-packages\gnome")
sys.path.append("C:\Python27\Lib\site-packages\repository")#tried including these extra dirs
sys.path.append("C:\Python27\Lib\site-packages\override")#tried including these extra dirs
sys.path.append("C:\Python27\Lib\site-packages\gi") #tried including these extra dirs
setup(
options = {
'py2exe': {
'bundle_files': 1,
#this does not work 'includes': ['Gtk']
}
},
console=["gui.py"],
zipfile=None
)
The executable error when ran:
ImportError: MemoryLoadLibrary failed loading gi\_gi.pyd
Thanks
You need to add "gi" to "packages".
'options': {
'py2exe': {
'packages': 'gi',
}
}
I haven't tested it on 64bit but this is the setup.py I've used to build with cx_freeze, py2exe looks like is not maintained for a long time.
from cx_Freeze import setup, Executable
import os, site, sys
## Get the site-package folder, not everybody will install
## Python into C:\PythonXX
site_dir = site.getsitepackages()[1]
include_dll_path = os.path.join(site_dir, "gtk")
## Collect the list of missing dll when cx_freeze builds the app
missing_dll = ['libgtk-3-0.dll',
'libgdk-3-0.dll',
'libatk-1.0-0.dll',
'libcairo-gobject-2.dll',
'libgdk_pixbuf-2.0-0.dll',
'libjpeg-8.dll',
'libpango-1.0-0.dll',
'libpangocairo-1.0-0.dll',
'libpangoft2-1.0-0.dll',
'libpangowin32-1.0-0.dll',
'libgnutls-26.dll',
'libgcrypt-11.dll',
'libp11-kit-0.dll'
]
## We also need to add the glade folder, cx_freeze will walk
## into it and copy all the necessary files
glade_folder = 'glade'
## We need to add all the libraries too (for themes, etc..)
gtk_libs = ['etc', 'lib', 'share']
## Create the list of includes as cx_freeze likes
include_files = []
for dll in missing_dll:
include_files.append((os.path.join(include_dll_path, dll), dll))
## Let's add glade folder and files
include_files.append((glade_folder, glade_folder))
## Let's add gtk libraries folders and files
for lib in gtk_libs:
include_files.append((os.path.join(include_dll_path, lib), lib))
base = None
## Lets not open the console while running the app
if sys.platform == "win32":
base = "Win32GUI"
executables = [
Executable("main.py",
base=base
)
]
buildOptions = dict(
compressed = False,
includes = ["gi"],
packages = ["gi"],
include_files = include_files
)
setup(
name = "test_gtk3_app",
author = "Gian Mario Tagliaretti",
version = "1.0",
description = "GTK 3 test",
options = dict(build_exe = buildOptions),
executables = executables
)
Depending on the libraries you have used you might have to add some missing dll, look at the output of cx_freeze.
I've posted the same some time ago on gnome's wiki:
https://wiki.gnome.org/Projects/PyGObject#Building_on_Win32_with_cx_freeze
I'm trying to make an exe by py2exe. The program is showing a popup-like window using Tkinter. The problem is, everything works fine when I run the setup like this:
setup(windows = [{'script': "msg.py"}], zipfile = None)
but it fails when I try to make an one-file exe:
setup(windows = [{'script': "msg.py"}], zipfile = None, options = {'py2exe': {'bundle_files': 1, 'compressed': True}})
Actually the final exe runs without problems, but it doesn't display any window. I've read there may be problems with bundle_files=1 at Windows 7, but I also tried bundle_files=2 with the same effect.
Here is my msg.py script:
from win32gui import FindWindow, SetForegroundWindow
from Image import open as iopen
from ImageTk import PhotoImage
from Tkinter import Tk, Label
from threading import Timer
from subprocess import Popen
import os
def Thread(t, fun, arg=None):
if arg<>None: x = Timer(t, fun, arg)
else: x = Timer(t, fun)
x.daemon = True
x.start()
def NewMessage():
global root
if not os.path.exists('dane/MSG'):
open('dane/MSG', 'w').write('')
root = Tk()
img = PhotoImage(iopen("incl/nowa.png"))
label = Label(root, image=img)
label.image = img
label.bind("<Button-1>", Click)
label.pack()
root.geometry('-0-40')
root.wm_attributes("-topmost", 1)
root.overrideredirect(1)
root.mainloop()
def Click(event):
global root, exit
root.destroy()
os.remove('dane/MSG')
OpenApp()
exit = True
def OpenApp():
hwnd = FindWindow(None, 'My program name')
if hwnd: SetForegroundWindow(hwnd)
else: Popen('app.exe')
root, exit = None, False
NewMessage()
Any ideas? I've read there are some problems with Tkinter, but there were about compilation. My script is compiled and it doesn't throw any exceptions, but doesn't show the window...
I ended up encountering this same issue, my solution involved doing the following:
Add
"dll_excludes": ["tcl85.dll", "tk85.dll"],
in your options = {...}
and then manually copy those two DLLs from
PYTHON_PATH\DLLs\ (in my case C:\Python27\DLLs)
to the location of your exe and try running it.
An alternative to dll_excludes and manual copying is to patch py2exe to know these files have to be placed directly in the dist directory.
Inside build_exe.py, there's a class called py2exe, which contains a list dlls_in_exedir for dll that have to go there. This list is set during a function named plat_prepare, and you can add the tclXX.dll and tkXX.dll files to it to make sure they are copied correctly.
Of course, unless you're the only one who will ever build this, you don't necessarily know which Tcl and Tk version you need to bundle - someone might have built their Python themselves, or are using an older Python with older DLLs. Therefore, you'll need to check which versions the system is actually using. py2exe actually already does this in a different place: by importing the internal _tkinter module (the actual Tk interface, usually a DLL) and accessing TK_VERSION and TCL_VERSION, which you can then use to generate and add the correct filenames.
If others are supposed to build your application, you probably don't want to make them modify their py2exe install, so here's how you can monkeypatch it from your setup.py:
import py2exe
py2exe.build_exe.py2exe.old_prepare = py2exe.build_exe.py2exe.plat_prepare
def new_prep(self):
self.old_prepare()
from _tkinter import TK_VERSION, TCL_VERSION
self.dlls_in_exedir.append('tcl{0}.dll'.format(TCL_VERSION.replace('.','')))
self.dlls_in_exedir.append('tk{0}.dll'.format(TK_VERSION.replace('.','')))
py2exe.build_exe.py2exe.plat_prepare = new_prep
This even works with bundle_files=1 on Windows 7.
If you have only one version the you can copy files with
via data_file. Below a full example:
WinXP
Python2.7.6
tk8.5
tcl8.5
tix8.4.3
py2exe 0.6.9
foo.py:
# -*- coding: iso-8859-1 -*-
import Tkinter
"""
sets TCL_LIBRARY, TIX_LIBRARY and TK_LIBRARY - see installation Lib\lib-tk\FixTk.py
"""
Tkinter._test()
Setup.py :
# -*- coding: iso-8859-1 -*-
from distutils.core import setup
import py2exe
import sys
import os
import os.path
sys.argv.append ('py2exe')
setup (
options =
{'py2exe':
{ "bundle_files" : 1 # 3 = don't bundle (default)
# 2 = bundle everything but the Python interpreter
# 1 = bundle everything, including the Python interpreter
, "compressed" : False # (boolean) create a compressed zipfile
, "unbuffered" : False # if true, use unbuffered binary stdout and stderr
, "includes" :
[ "Tkinter", "Tkconstants"
]
, "excludes" : ["tcl", ]
, "optimize" : 0 #-O
, "packages" :
[
]
, "dist_dir" : "foo"
, "dll_excludes": ["tcl85.dll", "tk85.dll"]
,
}
}
, windows =
["foo.py"
]
, zipfile = None
# the syntax for data files is a list of tuples with (dest_dir, [sourcefiles])
# if only [sourcefiles] then they are copied to dist_dir
, data_files = [ os.path.join (sys.prefix, "DLLs", f)
for f in os.listdir (os.path.join (sys.prefix, "DLLs"))
if ( f.lower ().startswith (("tcl", "tk"))
and f.lower ().endswith ((".dll", ))
)
]
,
)
Hey am relatively new to compiling python scripts to exe. Im using cx_freeze to compile my scripts and once its built i run the exe and it gives me this error. Have google around alot but not too sure. Error is:
Cannot import traceback module.
Exception: No module named re
Original Exception: No module named re
Not too sure how to go about fixing this. I read that possibly there is a clash between a module named re? in python? and a module named re in cx_freeze module?
My setup file looks like:
from cx_Freeze import setup, Executable
includes = []
includefiles = ['remindersText.pkl']
eggsacutibull = Executable(
script = "podlancer.py",
initScript = None,
base = 'Win32GUI',
targetName = "podlancer.exe",
compress = True,
copyDependentFiles = True,
appendScriptToExe = False,
appendScriptToLibrary = False,
icon = None
)
setup(
name = "Podlancer",
version = "0.1",
author = 'jono',
description = "Podlancer UI script",
options = {"build_exe": {"includes":includes, "include_files": includefiles}},
executables = [eggsacutibull]
)
Try to change
includes = []
to
includes = ["re"]
That worked for me
cx_freeze will barf if the runtime working directory is not the directory that the executable is in.
Is re the first import you do? What happens when you do them in a different order?
Meeting this same problem putting re in includes didn't work for me. It produced a cx_Freeze.freezer.ConfigError when rebuilding the .py file.
import sys
from cx_Freeze import setup, Executable
build_exe_options = {'include_files': ['re']}
setup( name = "Foreground Window Montior",
version = "0.1",
description = "Query the foreground window.",
options = {'build_exe': build_exe_options},
executables = [Executable("actWin_Query.py")])
If I put re in packages rather than in include_files it did not produce this compile error.
import sys
from cx_Freeze import setup, Executable
build_exe_options = {"packages": ["re"]}
setup( name = "Foreground Window Montior",
version = "0.1",
description = "Query the foreground window.",
options = {'build_exe': build_exe_options},
executables = [Executable("actWin_Query.py")])