Cx_freeze exe troubleshooting - python

I wrote an application using wxpython and boa constructor. This application stores class instances in ordered dictionaries (I import odict) and ultimately stores the data in a shelve on the local machine.
The application runs as I expect, so I want to distribute it.
Previously, I have used pyinstaller but have learned that python 2.6 is not fully supported at this time (verified by me because my *exe didn't work) so I switched to cx_freeze. My newly compiled exe seems to run fine, but does not create the shelve file. Looking through the library file in the build folder, I do not see the odict module. I do see shelve. Seems like this is the problem but I don't know why odict is not automatically included. I get no errors when running the application so I'm not really sure how to go about finding the problem. Any tips or suggestions would be sincerely appreciated.
Using python 2.6.6, wx python 2.8.11, cx_freeze 4.2.2 on windows XP.
I wrote this example to try and determine if it would write the shelve file and it doesn't work after running cx_freeze....
import wx
import sys
import os
import shelve
def create(parent):
return Frame1(parent)
[wxID_FRAME1, wxID_FRAME1BUTTON1,
] = [wx.NewId() for _init_ctrls in range(2)]
class Frame1(wx.Frame):
def _init_ctrls(self, prnt):
# generated method, don't edit
wx.Frame.__init__(self, id=wxID_FRAME1, name='', parent=prnt,
pos=wx.Point(557, 369), size=wx.Size(400, 250),
style=wx.DEFAULT_FRAME_STYLE, title='Frame1')
self.SetClientSize(wx.Size(392, 223))
self.button1 = wx.Button(id=wxID_FRAME1BUTTON1, label='button1',
name='button1', parent=self, pos=wx.Point(0, 0), size=wx.Size(392,
223), style=0)
self.button1.Bind(wx.EVT_BUTTON, self.OnButton1Button,
id=wxID_FRAME1BUTTON1)
def __init__(self, parent):
self._init_ctrls(parent)
def OnButton1Button(self, event):
filename='c:\\MakeAShelve.db'
data=[1,2,3,4]
database=shelve.open(filename)
database['data']=data
database.close()
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = create(None)
frame.Show()
app.MainLoop()
The setup I ran is below and executed as python setup.py build
import sys
from cx_Freeze import setup,Executable
includefiles=[]
exe=Executable(
script="ShelveTesting.py",
base="Win32Gui",
)
setup(
name="TimingDiagram",
version="0.2",
description="An Excel Based Timing Diagram Application",
options={'build_exe':{'include_files':includefiles}},
executables=[exe]
)

You can always manually include modules like this
build_exe_options = {'packages': ['os','sys','shelve'],'include_files':includefiles}
options = {"build_exe": build_exe_options}
NOTE!!
When using wxpython some special care need to be made.
http://wiki.wxpython.org/cx_freeze

Related

Main Window Icon Not Displayed when Frozen with cx_Freeze

I want to display a custom icon in a PyQt window after freezing the baseline with cx_Freeze. The icon displays fine when the unfrozen script is executed from within the IDE (Spyder, for me). I'm using PyQt5, Python 3.6, and Windows 10. Here is my Python script (IconTest.py) that creates a main window and shows the path to the icon and whether the path exists. The icon file needs to be in the same directory as IconTest.py:
import sys, os
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
from PyQt5.QtGui import QIcon
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setGeometry(200, 300, 600, 100)
if getattr(sys, 'frozen', False): #If frozen with cx_Freeze
self.homePath = os.path.dirname(sys.executable)
else: # Otherwise, if running as a script (e.g., within Spyder)
self.homePath = os.path.dirname(__file__)
self.iconFileName = os.path.join(self.homePath, 'myIcon.ico')
self.setWindowIcon(QIcon(self.iconFileName))
self.setWindowTitle('Icon')
self.label1 = QLabel(self)
self.label2 = QLabel(self)
self.label1.move(10, 20)
self.label2.move(10, 40)
self.label1.setText("Path to icon file: " + str(self.iconFileName))
self.label2.setText("Does file exit? " + str(os.path.exists(self.iconFileName)))
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
Here is my result when running the script from within Spyder (unfrozen). As you can see, there is an icon displayed that resembles a stopwatch:
Here is my setup.py for creating the frozen baseline:
from cx_Freeze import setup, Executable
import os, sys
exeDir = os.path.dirname(sys.executable)
platformsPath = os.path.join(exeDir, "Library\\Plugins\\Platforms\\")
iconPath = os.path.join(os.path.dirname(__file__), "myIcon.ico")
exe=Executable(script="IconTest.py", base = "Win32GUI", icon = iconPath)
includes=[iconPath, platformsPath]
excludes=[]
packages=[]
setup(
version = "0.1",
description = "My Icon Demo",
options = {'build_exe': {'excludes':excludes,'packages':packages,'include_files':includes}},
executables = [exe]
)
Here is my result when running the frozen script (the executable in the build directory). As you can see, the stopwatch icon is replaced with a generic windows icon:
Suggestions?
Interesting question and nice minimal example. After some searching I guess it could have to do with PyQt5 missing a plugin/DLL to display .ico image files in the frozen application. See e.g. How to load .ico files in PyQt4 from network.
If this is true, you have 2 options:
Try the same example with a .png file as window icon
If the plugins directory is included in the frozen application but it cannot find it, try to add the following statements
pyqt_dir = os.path.dirname(PyQt5.__file__)
QApplication.addLibraryPath(os.path.join(pyqt_dir, "plugins"))`
before
app = QApplication(sys.argv)
in your main script. See this answer.
If the plugins directory is not included in the frozen application, you need to tell cx_Freeze to include it using the include_files entry of the build_exe option. Either you manage to dynamically let your setup script include it at the place where PyQt5 is looking for it, using a tuple (source_path_to_plugins, destination_path_to_plugins) in include_files, or you tell PyQt5 where to look for it, using QApplication.addLibraryPath.
In your previous question to this issue you actually had an entry to include a Plugins\\Platforms directory in your setup script, maybe you simply need to repair this include. Please note that cx_Freeze version 5.1.1 (current) and 5.1.0 move all packages into a lib subdirectory of the build directory, in contrary to the other versions.
ANSWER: I have been using the Anaconda platform and read in other posts that there are issues between PyInstaller and Anaconda because of the way Anaconda structures its content. Thinking the same issue might exist with cx_Freeze, I installed Python (no Anaconda) on a different machine and froze the script from this new Python installation.
The icon appeared as expected in the frozen script. To make the icon display properly, I made the following changes to the setup.py script:
Removed import sys
Removed the line exeDir = ...
Removed the line platformsPath = ...
Removed platformsPath from the includes = list
I had a similar problem and posted an answer here.
Basically, I have overcome this issue by storing the file in a python byte array and loading it via QPixmap into a QIcon.

Python Script with QGis - Python.exe Stopped Working

I purchased this book called Building Mapping Applications with QGIS and I am trying to work through one of the exercises. There is one script that I try to run that crashes python, generating the error message "python.exe has stopped working".
import sys
import os
from qgis.core import *
from qgis.gui import *
from PyQt4.QtGui import *
from PyQt4.QtCore import Qt
#############################################################################
class MapViewer(QMainWindow):
def __init__(self, shapefile):
QMainWindow.__init__(self)
self.setWindowTitle("Map Viewer")
canvas = QgsMapCanvas()
canvas.useImageToRender(False)
canvas.setCanvasColor(Qt.white)
canvas.show()
layer = QgsVectorLayer(shapefile, "layer1", "ogr")
if not layer.isValid():
raise IOError("Invalid shapefile")
QgsMapLayerRegistry.instance().addMapLayer(layer)
canvas.setExtent(layer.extent())
canvas.setLayerSet([QgsMapCanvasLayer(layer)])
layout = QVBoxLayout()
layout.addWidget(canvas)
contents = QWidget()
contents.setLayout(layout)
self.setCentralWidget(contents)
#############################################################################
def main():
""" Our main program.
"""
QgsApplication.setPrefixPath(os.environ['QGIS_PREFIX'], True)
QgsApplication.initQgis()
app = QApplication(sys.argv)
viewer = MapViewer("C:/folder/shapefile.shp")
viewer.show()
app.exec_()
QgsApplication.exitQgis()
#############################################################################
if __name__ == "__main__":
main()
I don't know a whole lot about Python with QGIS so I'm not too sure what is causing python to crash. I am positive that all of the modules are importing correctly because if I define my paths and then import the modules in the script using the OSGeo4W Shell, there are no error messages.
This is how my paths are defined:
SET OSGEO4W_ROOT=C:\OSGeo4W64
SET QGIS_PREFIX=%OSGEO4W_ROOT%\apps\qgis
SET PATH=%PATH%;%QGIS_PREFIX%\bin
SET PYTHONPATH=%QGIS_PREFIX%\python;%PYTHONPATH%
Given all of this, I think there has to be something wrong in the script. However, when I check the script using http://pep8online.com/ there are no errors that I can fix that will result in python not crashing.
Note that I have tried I have tried SET PATH=%QGIS_PREFIX%\bin;%PATH% instead of SET PATH=%PATH%;%QGIS_PREFIX%\bin with no success.
I was fortunate enough to get in touch with the author of the book so I will share his response here:
I suspect I may know what the problem is...after looking at this
reader's problems in more depth, I've discovered that something has
changed in newer versions of QGIS, and the example code no longer
works as it is written. In technical terms, it seems that you now
need to instantiate the QApplication object before making the call to
QgsApplication.initQgis() -- the example program in the book
instantiates the QApplication object after calling
QgsApplication.initQgis(), which causes the program to crash. To fix
this, change the main() function to look like the following:
def main():
""" Our main program.
"""
app = QApplication(sys.argv)
QgsApplication.setPrefixPath(os.environ['QGIS_PREFIX'],True)
QgsApplication.initQgis()
viewer = MapViewer("C:/folder/shapefile.shp")
viewer.show()
app.exec_()
QgsApplication.exitQgis()
As you can see, I've moved the "app = QApplication(sys.argv)" line to the top.
Important Note: Make sure that forward slashes are used in viewer = MapViewer("C:/folder/shapefile.shp") - using a backslash will result in an error message stating that the shapefile is invalid.
I also thought it would be worth mentioning that none of the above fixes (comments on the question) were necessary. So, the script will work if the paths are defined as follows:
SET OSGEO4W_ROOT=C:\OSGeo4W64
SET QGIS_PREFIX=%OSGEO4W_ROOT%\apps\qgis
SET PATH=%PATH%;%QGIS_PREFIX%\bin
SET PYTHONPATH=%QGIS_PREFIX%\python;%PYTHONPATH%
Then, the entire script looks like this:
import sys
import os
from qgis.core import *
from qgis.gui import *
from PyQt4.QtGui import *
from PyQt4.QtCore import Qt
#############################################################################
class MapViewer(QMainWindow):
def __init__(self, shapefile):
QMainWindow.__init__(self)
self.setWindowTitle("Map Viewer")
canvas = QgsMapCanvas()
canvas.useImageToRender(False)
canvas.setCanvasColor(Qt.white)
canvas.show()
layer = QgsVectorLayer(shapefile, "layer1", "ogr")
if not layer.isValid():
raise IOError("Invalid shapefile")
QgsMapLayerRegistry.instance().addMapLayer(layer)
canvas.setExtent(layer.extent())
canvas.setLayerSet([QgsMapCanvasLayer(layer)])
layout = QVBoxLayout()
layout.addWidget(canvas)
contents = QWidget()
contents.setLayout(layout)
self.setCentralWidget(contents)
#############################################################################
def main():
""" Our main program.
"""
app = QApplication(sys.argv)
QgsApplication.setPrefixPath(os.environ['QGIS_PREFIX'],True)
QgsApplication.initQgis()
viewer = MapViewer("C:/folder/shapefile.shp")
viewer.show()
app.exec_()
QgsApplication.exitQgis()
#############################################################################
if __name__ == "__main__":
main()
Execute it in the OSGEO4W Shell using the following command:
python "C:\script.py"
Lastly, note that at the time of this writing, the script works properly and launches a viewer showing the shapefile referenced, but returns a few errors in the shell that do not seem to be problematic:
ERROR: Opening of authentication db FAILED
ERROR: Unable to establish authentication database connection
ERROR: Auth db could not be created and opened
QSqlDatabasePrivate::database: unable to open database: "unable to open database file Error opening database"
ERROR: Opening of authentication db FAILED
Much thanks to the author Erik Westra for providing me with this solution.
One thing that seems suspect is that you're creating a gui element without giving it a parent - QgsMapCanvas() - and then trying to manually show() it before adding it to a layout. You should never have to call show() on subwidgets, and all subwidgets should be parented to the main widget (or one of its other subwidgets).
Also, you should store persistent references to the python objects; otherwise, it's possible the underlying C++ object with get garbage collected and cause your program to crash. You do this by assigning your widgets and layouts to an attribute on self
Ex.
self.layout = QVBoxLayout(...
self.layer = ...
You should be adding the canvas like this, you should not need to call .show()
self.canvas = QgsMapCanvas(self)
layout.addWidget(self.canvas)

Change title of Tkinter application in OS X Menu Bar

When you create an application with a GUI using Tkinter in Python, the name of your application appears as "Python" in the menu bar on OS X. How can you get it to appear as something else?
My answer is based on one buried in the middle of some forums. It was a bit difficult to find that solution, but I liked it because it allows you to distribute your application as a single cross platform script. There's no need to run it through py2app or anything similar, which would then leave you with an OS X specific package.
Anyways, I'm sharing my cleaned up version here to give it a bit more attention then it was getting there. You'll need to install pyobjc via pip to get the Foundation module used in the code.
from sys import platform
# Check if we're on OS X, first.
if platform == 'darwin':
from Foundation import NSBundle
bundle = NSBundle.mainBundle()
if bundle:
info = bundle.localizedInfoDictionary() or bundle.infoDictionary()
if info and info['CFBundleName'] == 'Python':
info['CFBundleName'] = <Your application name here>
May not be quite what you need but I am surprised no one has mentioned the simple, platform independent way (works with Python 3.x on Win 7) :
from tkinter import Tk
root = Tk()
root.title( "Your title here" ) # or root.wm_title
and if you want to change the icon:
''' Replace the default "Tk" icon with an Application-specific icon '''
''' (that is located in the same folder as the python source code). '''
import sys
from tkinter import PhotoImage
program_directory = sys.path[ 0 ]
IconFile = os.path.join( program_directory ) + "\ApplicationIcon.gif"
IconImage = PhotoImage( file = IconFile )
root.tk.call( 'wm', 'iconphoto', root._w, IconImage )
root.mainloop()

Can't get my QT Designer UI to run from script

Ok I hope I can make this understandable...
So I created an UI in QT designer - named heatwindow.ui
Next I wrote this following script in a file named heatwindow.py:
from PySide import QtGui, QtCore, QtUiTools
from HeatModel import *
from vsmutils import *
class HeatWindow:
"""Main window class for the Flow application"""
def __init__(self, app):
"""Class constructor"""
# Assign our application instance as a member variable
self.app = app
# Create an instance of our HeatModel class
#self.heatModel = HeatModel()
# Load and show our user interface
self.ui = loadUiWidget('heatwindow.ui')
self.ui.show()
self.ui.raise_()
if __name__ == '__main__':
app = appInstance()
app.Create()
HeatWindow(app)
window = HeatWindow(app)
app.Run()
Ok, so vsmutils is a script that we got from our teacher...
Anyways - my problem is that nothing happens when running the script. Actually it seems the process in Python gets killed entirely, I have to press "Run again this program" (Spyder) in the Console. When debugging it seems this happens after the line
window = HeatWindow(app)
I hope this is enough info to help me, if not please tell what I should add!
Regards
Ok, solved it finally.
So I am going to post how I did it for others to see if they had the same problem... Cause this was just frustrating.
So- I use Windows 7, Python 2.7.6, Spyder... The script crashed all the time when using QT/Pyside. The solution was to delete a file qt.conf that is inside a Python 2.7.6 folder... And now it works... Don't know why though, but it took me 16 hours to find out.

QApplication is not running in main thread when building PySide app bundle with py2app

I am having problems building my PySide Python app using py2app (for OS X). It appears that something funny happens with threads on the app bundle.
Here is the minimal example
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class App(QApplication):
def __init__(self):
QApplication.__init__(self, sys.argv, True)
self.timer = QTimer(self)
if __name__=='__main__':
app = App()
app.exec_()
When run from the command line: python test.py, this works fine without error. However when I then compile it with the following setup.py:
from setuptools import setup
import py2app
import PySide
APP = ['test.py']
DATA_FILES = []
OPTIONS = {'argv_emulation': False,
'includes' : 'PySide',
'resources' : "qt_menu.nib"
}
setup(
app=APP,
data_files=DATA_FILES,
options={'py2app': OPTIONS},
setup_requires=['py2app'],
)
these errors appear in the Console:
11/05/2013 13:54:20.958 [0x0-0xb37b37].org.pythonmac.unspecified.test: QObject: Cannot create children for a parent that is in a different thread.
11/05/2013 13:54:20.958 [0x0-0xb37b37].org.pythonmac.unspecified.test: (Parent is App(0x105f41f10), parent's thread is QThread(0x100106cc0), current thread is QThread(0x10251ea80)
So it appears that App is not being constructed to live in the main thread any more. Any ideas how to fix this?
The problem seems to be the way PySide manage the QThreads. Yo're creating a QTimer with a QApplication as parent. When using PyQt4 that's not as problem, but it might be on PySide.
A QTimer also spawns a QThread so, try run your code without creating the QTimer.
Note: At the time you asked the question, this could be a bug. And might be fixed in the last versions of PySide. (I'm just speculating :D)

Categories

Resources