How can I include ui and image files while using py2exe? - windows

I am working on a project using Python 2.7 and PySide 1.1.2. My code is working without any problem on my GNU/Linux but I want to distribute for Windows ( 7 and 8 ) as well. I can't expect users to install Python and PySide, so I decided to use py2exe (I also tried cx_freeze and pyinstaller).
First of all, here is my file tree: My Project on GitHub
I created a setup.py, here it is:
# -*- coding: utf-8 -*-
from distutils.core import setup
import py2exe
setup(
console=['bin/metusuite.py'],
name='metusuite',
version='0.1',
author='H. Gökhan Sarı',
author_email='me#th0th.me',
packages=['metusuite_libs'],
package_dir={'metusuite_libs': 'metusuite_libs'},
package_data={'metusuite_libs': ['ui/*', 'images/*']},
scripts=['bin/metusuite.py'],
url='https://github.com/th0th/metusuite/',
license='LICENSE.txt',
description='METU Suite.',
long_description=open('README.md').read(),
)
When I run
setup.py py2exe
it successfully builds metusuite.exe in 'dist' folder, however, since application depends on external user interface files -created with Qt Designer- and it can't find them, I get an error:
Designer: An error has occurred while reading the UI file at line 1, column 0: Premature end of document.
Traceback (most recent call last):
File "metusuite.py", line 38, in <module>
File "metusuite_libs\msCafeteriaMenu.pyc", line 37, in __init__
File "metusuite_libs\msCafeteriaMenu.pyc", line 17, in __init__
RuntimeError: Unable to open/read ui device
And I couldn't figure out how am I supposed to add *.ui files (also there are some .png icons) into that structure. I was thinking of converting .ui files to Python code, then I would have encounter same issue when I need to add some icons.
Hence, how can I add my ui and png files in py2exe structure? Or is there any alternative method for what I am trying to accomplish?

Well, I think you could do one of two realistic things:
Compile your .ui files to .py files using pyside-uic and modify your code to do conditional loading of the py files for the user interface and place the png files in a Qt Resource file
Create a Qt Resource file with your ui files inside of it, compile that with pyside-rcc, and then load the ui files using QtUiTools or some similar process
pyside-uic
I greatly prefer using the pyside-uic method for loading ui files because it is the most straightforward way of loading ui files into a program that correlates with my knowledge of Qt in C++. pyside-uic is included with the PySide applications and for me it is found in the Scripts directory of my Python installation, e.g. C:\Python27\Scripts\pyside-uic.exe. Taking a note from how C++ compilation handles ui files, I typically compile my ui files to have a name like ui_[Name of the ui file].py:
C:\Python27\Scripts\pyside-uic mainwindow.ui > ui_mainwindow.py
Inside of that resulting .py file, pyside-uic creates a class named the same name as the base class of the ui file prepended with Ui_. So, for instance, if you created a mainwindow.ui that contained the definition for a class named MainWindow, the created class would be Ui_MainWindow. If the ui file defined a class named SourceWindow, the class within the .py file Ui_SourceWindow. In Qt Designer you set the class name by setting objectName in the root element of the object tree (in the upper right of the window).
With your files cafeteria_menu,ui and dialog_login.ui, you would get derived classes Ui_cafeteria_menu and Ui_dialog_login.
Once you have the .py file generated, it can be used by importing it into the definition file for your widget and used using the setupUi method of the class in the Ui file
from PySide import QtCore, QtGui
from ui_mainwindow import Ui_MainWindow
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
Once you have ui defined for the class, all of the connections and ui elements for the widget need to be accessed through self.ui
self.ui.lineEdit.textChanged[str].connect(self.processText)
Since you would have to put your .png files in a Qt Resource File, I'll talk about it in the next section.
pyside-rcc
Like pyside-uic, pyside-rcc is included with the PySide application, although mine is in the site-packages directory of Python instead of in Scripts (if it's in the same place for you, you can always copy it).
C:\Python27\lib\site-packages\PySide\pyside-rcc.exe
Before you can compile the Qt Resource File, you have to first create it using one of the Qt Tools. I use Qt Creator since it can perform almost all of the functions related to Qt in one application. The documentation for the Qt Resource System shows that the resource file is really just an XML file that defines file paths and internal paths for the resource system. You can set up and organize the files however you want but when it comes to compile, all of the files defined in the Resource File must be in the same directory or a sub-directory of the file. Once you have the Resource File defined, you need to use pyside-rcc.exe to compile it into a .py file. I typically name the resource file the same as the project and keep everything in one resource file to make dealing with the resources more concise.
C:\Python27\lib\site-packages\PySide\pyside-rcc.exe -py2 MyProject.qrc > MyProject_Resources.py
The -py2 switch defines that the output from the file should be formatted for Python 2.x. If you are using Python 3.x or plan to use it in the future, you can use the -py3 switch and the outcome will be compatible with Python 3.x.
Putting it all together
Since you are already loading the ui files directly QUiLoader, you just need to refactor your QUiLoader statements to load a QFile that opens the ui resource from the resource system. To use the files from the resource system, all you need to do is import your Resource .py file, the one generated from pyside-rcc, into the main script file of your program and the last line in the resource file is a call to qInitResources() which initializes the resources to be used in the entire program. To load a file using QFile, use a path that starts with ":" and then references the paths that were defined in the Resource File. You could create a file msResources.qrc that has ui and images that has your ui and png files defined as sub categories.
So, if your resource file looks something like this
/ui
cafeteria_menu.ui
dialog_login.ui
/images
cafeteria-menu.png
exit.png
logo.png
mail-fetch.ong
And, if you want to load those files, you just need to create a QIcon or QFile like so:
cafeteriaMenuIcon = QtGui.QIcon(":/images/cafeteria-menu.png")
cafeteriaMenuUi = QtCore.QFile(":/ui/cafeteria_menu.ui")
In use in your code for GUICafeteriaMenu in msCafeteriaMenu, I would just change the __init__ method for GuiCafeteriaMenu to load and use the ui file from the resources:
uiFile = QFile(":/ui/cafeteria_menu.ui")
uiFile.open(QFile.ReadOnly)
UiLoader.load(uiFile, self)
uiFile.close()
I would probably place the output from pyside-rcc into the metsuite_libs package into something like msResources.py and import the msResources file in the __init__.py file as part of your package. That way, once you have the .py files created and imported into your program, the extra file would be encapsulated in your package and you will not need to change your setup.py file. Before you do the py2exe conversion, running the refactored program should work just fine normally. Additionally, no matter how you handle the ui files, you will always need to use a Resource File to be able to package icons into the program. For portability reasons, using resource files for icons is probably a good habit to get in to.

Related

Application doesn't launch from ZeroBrane Studio

I'm having a lot of problems with ZeroBraneStudio!
I enjoyed using it to write my first application on Windows, but now I fail to make it working on MacBook Air and even on WIndows after reinstalling Solar2D and ZeroBrane!
If I launch my app opening Corona Simulator everythings works well, but ZeroBrane doesn't work. The errors on MacBook and on Windows are similar.
On MacBook:
Program starting as '"/Applications/Corona-3664/Native/Corona/mac/bin/lua" -e "io.stdout:setvbuf('no')" "/var/folders/4w/hn6pxl5s20v31f0q2n9msjs40000gn/T/.bO0D1R"'.
Program 'lua' started in '/Applications/Corona-3664' (pid: 1638).
Debugging session started in '/Applications/Corona-3664/'.
SampleCode/Interface/Composer/main.lua:18: module 'composer' not found:
no field package.preload['composer']
no file './composer.lua'
no file '/usr/local/share/lua/5.1/composer.lua'
no file '/usr/local/share/lua/5.1/composer/init.lua'
no file '/usr/local/lib/lua/5.1/composer.lua'
no file '/usr/local/lib/lua/5.1/composer/init.lua'
no file './composer.lua'
no file './composer/init.lua'
no file './lua/composer.lua'
no file './lua/composer/init.lua'
no file '/Applications/ZeroBraneStudio.app/Contents/ZeroBraneStudio/lualibs/composer/composer.lua'
no file '/Applications/ZeroBraneStudio.app/Contents/ZeroBraneStudio/lualibs/composer.lua'
no file '/Applications/ZeroBraneStudio.app/Contents/ZeroBraneStudio/lualibs/composer/composer/init.lua'
no file '/Applications/ZeroBraneStudio.app/Contents/ZeroBraneStudio/lualibs/composer/init.lua'
no file './composer.so'
no file '/usr/local/lib/lua/5.1/composer.so'
no file '/usr/local/lib/lua/5.1/loadall.so'
no file '/Applications/ZeroBraneStudio.app/Contents/ZeroBraneStudio/bin/clibs/composer.dylib'
no file '/Applications/ZeroBraneStudio.app/Contents/ZeroBraneStudio/bin/clibs/libcomposer.dylib'
stack traceback:
[C]: in function 'require'
SampleCode/Interface/Composer/main.lua:18: in ma etc.
Please help me to understand what is going wrong!
Thanks
It looks like your project points to /Applications/Corona-3664/ folder, but it needs to point to the location of your project files, such that the require statement for your project modules work from that location (or you need to adjust package.path values so that Lua interpreter can find the modules you are loading).
In this particular case it looks like the application expects the composer.lua to be in the same folder as main.lua, so try setting your project folder to /Applications/Corona-3664/SampleCode/Interface/Composer/ (although it's better to keep it outside of Applications/ folder).

Workaround for an API not allowing require a specific library (luasocket) and not able to include it via moving DLL

I am using an API called Piepan, which allows me to write Lua scripts for Mumble bots. For context, it is written in Golang using an alternative mumble implementation called Gumble. Piepan scripts are executed via cmd prompt through a piepan.exe.
I can require most libraries, like inspect.lua, and I can easily require luasocket in non-piepan scripts (scripts executed via lua.exe), but if I try to require luasocket (or what I really want, a redis library that depends on luasocket), I get an error. This is less of an error and more of a missing feature from the API, which the creator acknowledges. The creator suggests to someone else with this problem that they simply use Gumble, but I cannot do that as I am only a Lua programmer.
Here's the code of me just trying to require luasocket:
local socket = require ("include-test.socket")
(I've also tried include-test.socket.core and just socket.core)
In accordance with this stackoverflow answer, I moved my files to resemble the user's own directory, so it looks like this:
Piepan folder
-piepan exe and dlls (not luasocket dlls)
-include-test (folder)
--Script for piepan
--socket.lua
--socket folder
---core.dll
Despite the directory looking how I imagine it should based on other users' Q/As, I get this error:
.\include-test\socket.lua:13: module socket.core not found:
no field package.preload['socket.core']
Lstat : The system cannot find the path specified.
GetFileAttributesEx .\socket\core.lua: The system cannot find the path specified.
GetFileAttributesEx C:\Users\Michael\piepan\lua\socket\core.lua: The system cannot find the path specified.
GetFileAttributesEx C:\Users\Michael\piepan\lua\socket\core\init.lua: The system cannot find the path specified.
GetFileAttributesEx C:\Program Files (x86)\Lua\5.1\lua\socket\core.luac: The system cannot find the path specified.,
I've also tried including the following line, inspired by this stackoverflow answer.
package.cpath = package.cpath .. ';include-test/?.dll'
to no avail.
I am looking for any available solution, whether that be moving around dlls or compiling the original Piepan w/ extra files as needed.
(To clarify, I need a workaround that allows me to require the redis library within the same script I run through piepan. Using an outside script with the redis library to then, say, launch piepan and do something there, is not helpful to me.)

Writing .cargo/config.toml to allow rust code to be imported by python

I'm using rust-cpython to make a python module in rust. I've run my code on a linux os and it runs just fine but I get the familiar "linking with cc failed:exit code 1 error". I've gathered from this that I need to add the .cargo/config file to my project as suggested at the bottom of this:
https://github.com/dgrunwald/rust-cpython
I've copied and pasted their code into a file, config.toml, and place there in a directory, .cargo. I've tried nesting this in my src directory and my project directory with no success, what am I missing?
Solution found: Thought I'd post it as this gave me grief.
Everything with this setup is fine except the config file can't have the extension .toml despite being written in a toml format

How to package a Kivy app with Pyinstaller

I have a lot of troubles following the instructions form the Kivy website, many steps aren't explained like what should I answer to the warning.
WARNING: The output directory "..." and ALL ITS CONTENTS will be REMOVED! Continue? (y/n)
Even if I choose y, the folder isn't removed.
Also should I always add these lines:
from kivy.deps import sdl2, glew
Tree('C:\\Users\\<username>\\Desktop\\MyApp\\'),
*[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)]
in the .spec file? Why are they necessary?
Not many info is available for Kivy.
Because I spent a lot of time understanding how I should package my app, here are some instructions that would have really helped me.
Some info are available at http://pythonhosted.org/PyInstaller/
Python 3.6 as of march 2017
Because packaging my app gave me the error IndexError: tuple index out of range, I had to install the developement version of PyInstaller:
pip install https://github.com/pyinstaller/pyinstaller/archive/develop.zip
Step 1:
I moved all the files of MyApp in a folder "C:\Users\<username>\Desktop\MyApp": the .py, the .kv and the images and I created an icon.ico.
I created another folder C:\Users\<username>\Desktop\MyPackagedApp. In this folder I press Shift+right click and select open command window here.
Then I pasted this:
python -m PyInstaller --name MyApp --icon "C:\Users\<username>\Desktop\MyApp\icon.ico" "C:\Users\<username>\Desktop\MyApp\myapp.py"
This creates two folders, build and dist, and a .spec file. In dist/MyApp, I can find a .exe. Apparently, if my app is really simple (just one label), the packaged app can works without the Step 2.
Step 2:
The second step involves editing the .spec file. Here is an exemple of mine.
(cf Step 3, for the explanations about my_hidden_modules)
I go back to the cmd, and enter
python -m MyApp myapp.spec
I then got this warning:
WARNING: The output directory "..." and ALL ITS CONTENTS will be REMOVED! Continue? (y/n)
I enter y and then press enter.
Because I choosed y, I was surpised that the folder build was still there and that the dist/MyApp was still containing many files. But this is normal. PyInstaller can output a single file .exe or a single folder which contains all the script’s dependencies and an executable file. But the default output is a single folder with multiple files.
Step 3: adding hidden modules
When I click on the myapp.exe in dist/MyApp, the app crashed. In the log C:\Users\.kivy\logs\ I could find 2 errors: ModuleNotFoundError: No module named 'win32timezone' and SystemError: <class '_frozen_importlib._ModuleLockManager'>.
Because of this I had to edit the .spec file and add these lines:
my_hidden_modules = [
( 'C:\\Users\\<username>\\AppData\\Local\\Programs\\Python\\Python36\\Lib\\site-packages\\win32\\lib\\win32timezone.py', '.' )
]
in a = Analysis I changed datas = [] to datas = my_hidden_modules,
Apparently this is because I used a FileChooser widget.
So, the line:
ALL ITS CONTENTS will be REMOVED!
yes, it will be removed AND replaced later with new files. Check the date. I think it prints permission denied if it can't do such a thin both for files and the whole folder, so you'd notice it. It's important though, because you need to add additional files into your folder.
Those additional files of two types:
kivy dependencies
application data
Dependencies are just binaries (+/- loaders, licenses, or so), you get them through the *[Tree(p) ...] piece of code, which is just a command for "get all files from that folder". Without them Kivy won't even start.
Similarly to that, the second Tree(<app folder>) does the same, but for your own files such as .py files, .kv files, images, music, databases, basically whatever you create.
Obviously if you remove the deps, app won't start and if you remove app data, you'll get some path errors and most likely crash. You don't want any of that :P
It also works if in the 'a = Analysis...' block in the spec file one substitutes
hiddenimports=[]
for
hiddenimports=['win32file', 'win32timezone']
for win32file, win32timezone or for whatever files are missing

Embedded Python loads module but does not load that module's internal import statements

At long last(!) I've compiled Boost::Python and have gotten my XCode project to import a local module. This module starts with the line from xml.dom import minidom, but when it executes, I'm given this error:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "FeedStore.py", line 1, in <module>
from xml.dom import minidom
ImportError: No module named xml.dom
However, I know that I've installed the xml Python module -- when I open Python from my command prompt and type from xml.dom import minidom, everything goes smoothly. Moreover, when I import the module, it behaves as I would expect.
I suspected that there was something wrong with sys.path, so I compared the one I get from the prompt to the one that's being used in my embedded module. The only difference is that the embedded sys.path does not include ''. I've tried appending it, but that didn't change the behavior.
I also suspected that the embedded version was accessing a different version of Python than I was using from the prompt, but sys.prefix matched between both executions.
Here's the code that imports my module and runs it. It's pretty bare-bones at the moment (not even reference counting yet) because at this point I'd just like to make sure I'll be able to embed my module (I'm a total newbie C++ programmer).
Py_Initialize();
//PyRun_SimpleString("import sys");
//PyRun_SimpleString("sys.path.append('')"); //tried this to no avail!
PySys_SetPath("/Users/timoooo/Documents/Code/TestEmbed/"); //this allows me to import my local module
PyRun_SimpleString("import FeedStore as fs"); //here's where it whines about the lack of xml.dom
PyRun_SimpleString("store = fs.feedStore()");
PyRun_SimpleString("print store.next()");
Py_Finalize();
I'm probably misunderstanding something essential about boost::python. Can anyone help me out?
Despite having identical sys.path values, calling
PyRun_SimpleString("sys.path.append(\"<<path>>\")");
with the places I needed fixed the problem.

Resources