tpaviot / pythonocc-core

Python package for 3D geometry CAD/BIM/CAM
GNU Lesser General Public License v3.0
1.38k stars 380 forks source link

pyinstaller - Standard GLSL programs are not found #553

Open smhty opened 6 years ago

smhty commented 6 years ago

Hi Everyone,

I am trying to use pyinstaller and generate a .exe. When I run the .exe file I get the following set of logs:

github

###### 3D rendering pipe initialisation ##### Display3d class initialization starting ... Aspect_DisplayConnection created. Graphic_Driver created. V3d_Viewer created. AIS_InteractiveContext created. V3d_View created WNT window created. Standard GLSL programs are not found in: C:/Miniconda35-x64/conda-bld/oce_1502695956759/_h_env/Library/share/oce/src/Shaders Traceback (most recent call last): File "main.py", line 2146, in <module> main() File "main.py", line 2140, in main form = dorna_app() File "main.py", line 79, in __init__ self.sml = simulation(25, 0.2, self.q_report, self) File "simulation.py", line 44, in __init__ self.canvas_init(parent) File "simulation.py", line 75, in canvas_init self.canva.InitDriver() File "site-packages\OCC\Display\qtDisplay.py", line 136, in InitDriver File "site-packages\OCC\Display\OCCViewer.py", line 182, in Create RuntimeError: Standard_Failure CSF_ShadersDirectory or CASROOT is set incorrectly [9288] Failed to execute script main [9288] LOADER: OK. [9288] LOADER: Cleaning up Python interpreter.

I tried to set the CASROOT variable but that didn't work. I want to know how can I fix this issue. Thank you very much :)

jf--- commented 6 years ago

I am trying to use pyinstaller

Looks like the shaders didnt end up in the package. Being able to bundle pythonocc via pyinstaller would be wonderful... 👍

Kane-LAU commented 5 years ago

have you solved it? i also came across this issue. can you post valid code or some description

Kane-LAU commented 5 years ago

I am trying to use pyinstaller

Looks like the shaders didnt end up in the package. Being able to bundle pythonocc via pyinstaller would be wonderful... 👍

I can't understanding your meaning appropriately. how to bundle pythonocc? some code was used? or copy file into somewhere?

smhty commented 5 years ago

@liuxin2322 I was not able to fix the issue. I switched to webgl. I will let you know if I was able to come up with any solution.

waterbug commented 5 years ago

I found a solution to this problem that works for my application: to use PyInstaller with a PyQt gui and pythonocc requires a highly customized ".spec" file for the app anyway (my app has a lot of data files), so in my .spec file I put the following:

# code ...
occ_pkg_path = os.path.dirname(OCC.__file__)
casroot = os.path.join(occ_pkg_path, '..', '..', '..',
                              'Library', 'share', 'oce')
casroot_paths = [(casroot, os.path.join('app_module', 'casroot'))]

(you can see that I stole the "casroot" path stuff from the OCCViewer code :) 'app_module' here is my app's main module path

App.__path__[0]

... but could be the path of any module that your app contains and can import and find) ... so PyInstaller copies the Library/share/oce directory to that location and calls it "casroot". Then when my app starts up at runtime, I have it create a "home" directory for itself within the user's home directory (as I say it has a lot of data files anyway, a database, etc.) and I copy the casroot directory there and set that path as the CASROOT env var:

# [5] if we are running on Windows and pyinstaller installed us, there will
#     be a 'casroot' directory that contains files needed by pythonocc --
#     copy them to home and set "CASROOT" env var ...
if sys.platform == 'win32':
    casroot_path = os.path.join(app_module_path, 'casroot')
    casroot_home = os.path.join(app_home_dir, 'casroot')
    if (os.path.exists(casroot_path) and
        not os.path.exists(casroot_home)):
        shutil.copytree(casroot_path, casroot_home)
        os.environ['CASROOT'] = casroot_home

... where app_module_path is the one PyInstaller used above (found the same way at runtime). So far this is working for my app, which uses OCCViewer.

Durksz commented 5 years ago

Dear Waterbug,

I'm facing the same problem as smhty. And am trying to implement your solution. I'm struggeling to understand (and implement) your 'app_module', 'app_module_path' and 'app_home_dir'.

As a test (before I try to create an exe file from my more complex program) I'm trying to create a .exe file from the core_geometry_splinecage.py example (can be found in the examples of pythonocc 0.18.1) Would you show how the .spec file and .py file for the .exe generation will look like for this example? It would help me and possibly many others a lot!

Thanks in advance!

Durksz commented 5 years ago

An update, I managed to succesfully implement Waterbug's solution for the "core_geometry_splinecage.py" example:

the modifications to the "core_geometry_splinecage.py" script are only needed in the first section where the imports are present:

import os
import random

# this section is the modification needed to create an executable using pyinstaller provided by Waterbug:
#  if we are running on Windows and pyinstaller installed us, there will
#  be a 'casroot' directory that contains files needed by pythonocc --
#  copy them to home and set "CASROOT" env var ...
import sys
app_module_path = 'app_module'
if sys.platform == 'win32':
    casroot_path = 'casroot'
    if os.path.exists(casroot_path):
        os.environ['CASROOT'] = casroot_path

#end modification

from OCC.BRepAdaptor import BRepAdaptor_Curve
from OCC.GCPnts import GCPnts_AbscissaPoint, GCPnts_UniformAbscissa
from OCC.GeomAbs import GeomAbs_G1
from OCC.BRepOffsetAPI import BRepOffsetAPI_MakeFilling
from OCC.TopAbs import TopAbs_FACE, TopAbs_EDGE

from OCC.Display.SimpleGui import init_display
from OCC.Display.OCCViewer import rgb_color
from core_topology_traverse import Topo
from core_load_step_ap203 import read_step_file

display, start_display, add_menu, add_function_to_menu = init_display()

#now the rest of the code is exacly the same as in the example.

And the .spec file looks like the following:

# -*- mode: python -*-

block_cipher = None

# code ...
import OCC
folder_paths = []
#copy the folder where the casroot is located to a folder called casroot in the executable folder
occ_pkg_path = os.path.dirname(OCC.__file__)
casroot = os.path.join(occ_pkg_path, '..', '..', '..',
                              'Library', 'share', 'oce')
casroot_paths = (casroot, 'casroot')
folder_paths.append(casroot_paths)
#copy the folder where the spinecage.stp is located including its contents.
model_path = (os.path.join(os.getcwd(), 'models'), 'models')
folder_paths.append(model_path)

a = Analysis(['core_geometry_splinecage.py'],
             pathex=[os.getcwd()],
             binaries=[],
             datas=folder_paths,
             hiddenimports=[],
             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='core_geometry_splinecage',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=False,
          console=True )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='core_geometry_splinecage')

the spec file must be executed in the same directory as the example is located.

I hope it helps someone.

waterbug commented 5 years ago

app.txt A further comment on this issue: I think the information documented in this issue about how to create a .exe and an installer (setup.exe) should be documented somewhere on the pythonocc web site so it isn't buried in this issue, where it is difficult for new pythonocc application developers to find!

I have just managed to use innosetup (http://www.jrsoftware.org/isinfo.php), a truly wonderful piece of software, to create a "setup.exe" installer for my pyinstaller-generated executable (which was created using the methods documented earlier in this issue). To use innosetup, create an application-specific ".iss" file (in principle somewhat similar to a "make file") and use it with the innosetup application to "compile" your pyinstaller-generated executable into a setup.exe file. Here is a generic example "app.txt" (should be .iss but google groups won't allow me to attach a file with suffix ".iss") derived from the .iss file I used for my application:

AllenJo11 commented 5 years ago

Hello everyone

I'm facing the same problem and tried to follow up the solutions above to the problem. But it does not go well for me... What I tried is to follow the code modification that you did on "core_geometry_splinecage.py" for "Hello dumb box!" example that I copied from pythonocc.org example section The original code is like following:

`from OCC.Display.SimpleGui import init_display from OCC.BRepPrimAPI import BRepPrimAPI_MakeBox

display, start_display, add_menu, add_function_to_menu = init_display() my_box = BRepPrimAPI_MakeBox(10., 20., 30.).Shape()

display.DisplayShape(my_box, update=True) start_display()`

And I followed up the modification on .py file as you mentioned and following is modified code:

`from OCC.Display.SimpleGui import init_display from OCC.BRepPrimAPI import BRepPrimAPI_MakeBox

import os import sys import OCC app_module_path = 'app_module' if sys.platform == 'win32': casroot_path = 'casroot' if os.path.exists(casroot_path): os.environ['CASROOT'] = casroot_path

display, start_display, add_menu, add_function_to_menu = init_display() my_box = BRepPrimAPI_MakeBox(10., 20., 30.).Shape()

display.DisplayShape(my_box, update=True) start_display()`

and the spec file that I modified following the comments is like following:

`# -- mode: python --

block_cipher = None

import OCC import os folder_paths = []

occ_pkg_path = os.path.dirname(OCC.file) casroot = os.path.join(occ_pkg_path, '..', '..', '..', 'Library', 'share', 'oce') casroot_paths = (casroot, 'casroot') folder_paths.append(casroot_paths)

model_path = (os.path.join(os.getcwd(), 'models'), 'models') folder_paths.append(model_path)

a = Analysis(['hello.py'], pathex=['C:\Users\ProDesk\Anaconda3\pythonocc-core-0.18.1\examples', 'C:\Users\ProDesk\Desktop\dopython', 'C:\Users\ProDesk\Anaconda3\pythonocc-core-0.18.1', os.getcwd()], binaries=[], datas=[], hiddenimports=[], 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='hello', debug=False, bootloader_ignore_signals=False, strip=False, upx=False, console=True ) coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, name='hello')`

Making the exe file using pyinstaller was done well but when I run the .exe file i get this log image (Sorry for the white parts... the log only appears very short time and I had to capture it from the video I took)

I think it happens because my program is running on virtual environment that I made.

These are the version info python version on virtual environment : python 3.5.5 python version on original : python 3.6.5

I may doing some simple simple mistake right now... if so, please forgive me for bothering you with it Thank you and bless to you all.

Durksz commented 5 years ago

OCC tries to find the casroot folder when it is imported. the enviroment therefore needs to be set correctly first.

You are importing the display first, and after that setting the casroot path. OCC tries to find the casroot before it is set properly, giving you the error.

Try to set the casroot enviroment before importing anything from the OCC libary.

like this:

#set envirment first BEFORE importing OCC
import sys
app_module_path = 'app_module'
if sys.platform == 'win32':
    casroot_path = 'casroot'
    if os.path.exists(casroot_path):
        os.environ['CASROOT'] = casroot_path

#after setting enviroment import OCC libaries.
from OCC.Display.SimpleGui import init_display
from OCC.BRepPrimAPI import BRepPrimAPI_MakeBox
AllenJo11 commented 5 years ago

Dear Durksz Thank you for kind reply.

But unfortunately, even after I changed the code as you told me same error massage appears and the .exe file does not operate.

I want to ask is the line code if sys.platform == 'win32' Does this line changes if I'm using window 64? I'd tried the code with if sys.platform == 'win64' to find out this line was the problem, but same error came out.

And I'm curious that at the error passage that comes out when I try to operate hello.exe, the massage that says 'Standard GLSL programs are not found in: C:\Miniconda35-x64\conda-bld\oce_1502695956759_h_env_Library\share\oce\src\Shaders' I tried to find the 'C:\Miniconda35-x64' folder but there was no folder named like that. And also I installed Anaconda3 not Miniconda... Is this can be source of the problem?

I will keep find the way to make it work and I'm very very thank you for your kind help!

Durksz commented 5 years ago

The casroot problem seems to be solved. The error you get now I can't replicate...

What virtual enviroment manager do you use? With conda enviroments i have compatibility issues with oce. I therefore use pythonOCC 0.18.2 from the tpaviot channel

I've made an example for you using the helloworld script: (The Python version = 3.6.8, pythonOCC = 0.18.2)

"""
The very first pythonocc example. This uses to be the script
used to check the following points:

pythonocc installation is correct, i.e. pythonocc modules are found
and properly imported

a GUI manager is installed. Wether it is wxpython or pyqt/pyside, it's necessary
to display a 3d window

the rendering window can be initialized and set up, that is to say the
graphic driver and OpenGl works correctly.

If this example run on your machine, that means you're ready to explore the wide
pythonocc world and run all the other examples.
"""

# add this bit for creating an exe file using pyinstaller before importing OCC
import sys,os
app_module_path = 'app_module'
if sys.platform == 'win32':
    casroot_path = 'casroot'
    if os.path.exists(casroot_path):
        os.environ['CASROOT'] = casroot_path

print("starting hello world window")

from OCC.Display.SimpleGui import init_display
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox

display, start_display, add_menu, add_function_to_menu = init_display()
my_box = BRepPrimAPI_MakeBox(10., 20., 30.).Shape()

display.DisplayShape(my_box, update=True)
start_display()

The spec script is in this case:

# -*- mode: python -*-

block_cipher = None

# code ...
import OCC
import os
folder_paths = []
occ_pkg_path = os.path.dirname(OCC.__file__)
casroot = os.path.join(occ_pkg_path, '..', '..', '..',
                              'Library', 'share', 'oce')
#casroot_paths = (casroot, os.path.join('app_module', 'casroot'))
casroot_paths = (casroot, 'casroot')
folder_paths.append(casroot_paths)

a = Analysis(['core_helloworld_to_exe.py'],
             pathex=[os.getcwd()],
             binaries=[],
             datas=folder_paths,
             hiddenimports=[],
             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='core_helloworld',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=False,
          console=True)
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=False,
               name='core_helloworld_to_exe')

It might also work with pythonOCC 0.18.1

AllenJo11 commented 5 years ago

Dear Durksz

Yes!!!! The code you gave me works fine at my pythonOCC 0.18.1 with one modification. From the .py code you told me from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox My machine could not find OCC.Core. So I tried OCC.BRepPrimAPI instead and the .exe file made with modified .spec file works well! I'm finally getting the clue all thanks to you!!!

Durksz commented 5 years ago

Good to hear that you've got it working. The change from OCC to OCC.Core is indeed the main difference between pythonOCC 0.18.1 and 0.18.2.

EmJay276 commented 2 years ago

The casroot problem seems to be solved. The error you get now I can't replicate...

This solution didn't work for me either, I used a runtime hook like in CQ editor. https://github.com/CadQuery/CQ-editor/blob/master/pyinstaller/pyi_rth_occ.py But I had to overwrite the OCCViewer. I also used pyside2 with 0.18.2 (had to overwrite the backend.py with the newer version)

My solution

myapp.spec

# -*- mode: python -*-

block_cipher = None

# code ...
import OCC
import os
folder_paths = []
occ_pkg_path = os.path.dirname(OCC.__file__)
casroot = os.path.join(occ_pkg_path, '..', '..', '..',
                              'Library', 'share', 'oce')
#casroot_paths = (casroot, os.path.join('app_module', 'casroot'))
casroot_paths = (casroot, 'casroot')
folder_paths.append(casroot_paths)

a = Analysis(['main.py'],
             pathex=[os.getcwd(), 'C:\\Anaconda3\\envs\\myenv', 'C:\\Anaconda3\\envs\\myenv\\Lib\\site-packages\\PySide2', 'C:\\Anaconda3\\envs\\myenv\\Library\\include\\PySide2\\QtGui', 'C:\Anaconda3\envs\myenv\Library\share\oce\src\Shaders'],
             binaries=[],
             datas=folder_paths,
             hiddenimports=['PySide2', 'PySide2.QtGui'],
             hookspath=[],
             runtime_hooks=['pyinstaller/pyi_rth_occ.py'],
             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='myapp',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=False)
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='myapp')

pyinstaller/pyi_rth_occ.py

from os import environ as env

env['CASROOT'] = 'casroot'

env['CSF_ShadersDirectory'] = 'casroot/src/Shaders'
env['CSF_UnitsLexicon'] = 'casroot/src/UnitsAPI/Lexi_Expr.dat'
env['CSF_UnitsDefinition'] = 'casroot/src/UnitsAPI/Units.dat'

Part of OCCViewer.py I needed to change

# Shaders and Units definition must be found by occ
# the fastest way to get done is to set the CASROOT env variable
# it must point to the /share folder.
# if sys.platform == "win32":
#     # do the same for Units
#     if "CASROOT" in os.environ:
#         casroot_path = os.environ["CASROOT"]
#         # raise an error, force the user to correctly set the variable
#         err_msg = "Please set the CASROOT env variable (%s is not ok)" % casroot_path
#         assert os.path.isdir(casroot_path), err_msg
#     else:  # on miniconda or anaconda or whatever conda
occ_package_path = os.path.dirname(OCC.__file__)
casroot_path = os.path.join(occ_package_path, '..', '..', '..',
                            'Library', 'share', 'oce')
# we check that all required files are at the right place
shaders_dict_found = os.path.isdir(os.path.join(casroot_path,
                                                'src', 'Shaders'))
unitlexicon_found = os.path.isfile(os.path.join(casroot_path,
                                                'src', 'UnitsAPI',
                                                'Lexi_Expr.dat'))
unitsdefinition_found = os.path.isfile(os.path.join(casroot_path,
                                                    'src', 'UnitsAPI',
                                                    'Units.dat'))
if shaders_dict_found and unitlexicon_found and unitsdefinition_found:
    os.environ["CASROOT"] = casroot_path

Then just run pyinstaller myapp.spec

Not sure if all "pathex" in the spec file are neccesary, but adding them won't probably hurt ;)