2017-01-01 23:26:10 (edited by TJ.Breitenfeldt 2017-01-01 23:29:27)

Hi ,I am trying to compile my audio game I just built using pygame and accessible_output2. I have never compiled a python scrip successfully before, but I would really like to try and get this working this time so I can show other people without needing python installed on other computers.

The only imports I am using in my project are random, pygame, and accessible _output2.

The two compiling packages I have seen are py2exe and PyInstaller. I have tried both and had no luck, I keep getting errors. I have been trying to get PyInstaller working because I understand that py2exe executables usually need visual studio installed? Also, it would be nice to be able to compile into a single executable rather than dealing with a folder of files.

My problem with PyInstaller is that in my warning text file it is listing a large number of modules PyInstaller could not be imported. I tracked a couple down, and it seems they don't exist in the directory that the python module is trying to import them? I don't know. I have been using the link:

https://pythonhosted.org/PyInstaller/wh … wrong.html

Can someone help me please? This is becoming very frustrating.

TJ Breitenfeldt

2017-01-02 07:53:29

Its not uncommon to get warnings in the warning file, does pyinstaller fail to generate the excecutable in a "dist" folder? Or does the excecutable just not work? Do you have pywin32 installed?

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2017-01-02 18:47:53

Yes, I have pywin32 installed, as far as I can tell PyInstaller installed without any warning messages. It is giving me the executable, but when I click on it I get the error:

"Fatal Error! Failed to execute script pig"

I also noticed that the pygame window is opening, but when I click OK in the Fatal Error dialog, the pygame window disappears.

After looking through the build time messages, these are the only messages that stand out to me to be possibly problematic.

...
1450 INFO: UPX is not available.
1450 INFO: Extending PYTHONPATH with paths
...
24738 WARNING: Hidden import "pygame._view" not found!
...
27438 WARNING: library loader required via ctypes not found

2017-01-03 03:34:08 (edited by magurp244 2017-01-03 03:34:27)

According to [this] thread, you could try recompiling your exe with console enabled and running it from a command prompt to get the full error output for why it may be throwing the fatal error. Hopefull that can give us a better clue.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2017-01-03 03:57:42

So, I recompiled my game, and left out the --windowed argument. When I ran the executable in the command prompt I realized my first problem was that I needed to make sure my sounds were in the same directory as the executable, oops. Second problem was the error:

Traceback (most recent call last):
  File "pig.py", line 217, in <module>
  File "pig.py", line 28, in main
  File "accessible_output2\outputs\auto.py", line 12, in __init__
  File "accessible_output2\outputs\base.py", line 19, in __init__
  File "accessible_output2\__init__.py", line 15, in load_library
  File "ctypes\__init__.py", line 426, in __getitem__
  File "ctypes\__init__.py", line 421, in __getattr__
  File "site-packages\PyInstaller\loader\pyiboot01_bootstrap.py", line 152, in _
_init__
  File "ctypes\__init__.py", line 351, in __init__
OSError: [WinError 126] The specified module could not be found
Failed to execute script pig

2017-01-03 06:32:37

Well the first thing jumping out there is accessible_output2, lets see here.. In auto.py init is:

 def __init__(self):
  output_classes = accessible_output2.get_output_classes()
  self.outputs = []
  for output in output_classes:
   try:
    self.outputs.append(output())
   except OutputError:
    pass

Base.py init is:

 def __init__(self):
  is_32bit = platform.architecture()[0] == "32bit"
  if self.lib32 and is_32bit:
   self.lib = load_library(self.lib32)
  elif self.lib64:
   self.lib = load_library(self.lib64)

Hm, the line numbers i'm looking at don't match, although maybe i'm looking at a different version of the file online here. The load_library function in init is:

def load_library(libname):
 if paths.is_frozen():
  libfile = os.path.join(paths.embedded_data_path(), 'accessible_output2', 'lib', libname)
 else:
  libfile = os.path.join(paths.module_path(), 'lib', libname)
 return ctypes.windll[libfile]

So it looks like it can't find lib64 or lib32, which seems to be a 32/64 bit issue. Hmm, digging around a bit it seems [this] thread has an error output that looks pretty similar, it has to do with accessible_output2 not being able to find the nvda controller client library. You may need to find and copy that over into the same directory as your binary.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2017-01-03 08:40:12

Okay, so I did some testing, I tried copying over the
nvdaControllerClient32.dll
file into my programs directory, it did not work. I tried compiling it as a single directory rather than a single file and it actually worked once I dropped in my sound folders without the NVDA dll. I would still like to try and make it work with a single executable though. So, I traced the error message myself, and noticed a message underneath the message about base.py.

  File "accessible_output2\__init__.py", line 15, in load_library

I found that file and it looks like line 15 is refering to say.py, which is for voice over.

2017-01-04 01:46:06

accessible_output2.find_datafiles provides a list of datafiles as expected by the py2exe setup.py datafiles argument.
See the setup.py from my shooter project for examples

2017-01-04 21:39:43

I would rather not use py2exe if I can avoid it just simply so that I can build a one file executable and not have to deal with all of the files.

2017-01-07 04:00:42

This seems like a bit of a silly reason -- any moderately complex application or game you ever develop will consist of more resources than just a single executable. You'll have sounds and other game assets, documentation, translation strings, so on and so forth. Might as well learn how to deal with this stuff now by creating an actual installer.
This may be useful: https://pypi.python.org/pypi/innosetup
I have a hacked up version of this as a large component of my installer_builder package.

This is really the big downside here. We harp at people to use modern programming languages, ecosystems and packages, but this pulls in a heck of a lot of complexity.
If you're working with the stack every day like I am, you learn how to simplify the complexity and write tools where they don't exist to make your life easier, but if you're just trying to ship a game, I can see this being super annoying.

Anybody got relatively good technical documentation chops? I would be willing to open source a lot of my tooling, like my simplified installer builder, but this stuff all needs good documentation for me to make it public.

2017-01-25 00:58:26

Bump. Well I managed to get accessible_output2 working in a single binary with pyinstaller, though i'm not sure if this may be the same issue you've had. The problem had to do with accessible_output2/__init__.py load_library(). Basically its hardcoded to search in "your-binary-directory/accessible_output2/lib/", I tested it and if you build as one file and drop the accessible_output2 folder next to the binary it works fine. It breaks when I tried packing the dll's with the exe though because when I run it, all the resources are unpacked to a temporary _MEI folder on C drive, and AO2 can't find them then because its hardcoded to look in the compiled binaries working folder. So, I made these changes to accessible_output2/ __init__.py:

def load_library(libname, cdll=False):
    if paths.is_frozen():
        libfile = os.path.join(paths.embedded_data_path(), 'accessible_output2', 'lib', libname)
    else:
        libfile = os.path.join(paths.module_path(), 'lib', libname)
    if cdll:
                try:
                        return ctypes.cdll[libfile]
                except:
                        libfile = resource_path(libname)
                        return ctypes.cdll[libfile]
    else:
                try:
                        return ctypes.windll[libfile]
                except:
                        libfile = resource_path(libname)
                        return ctypes.windll[libfile]

def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative_path)

    return os.path.join(os.path.abspath("."), relative_path)

After that just add the dlls to the spec file as regular data files like so:

a.datas += [('dolapi.dll','D:\\compile\\dolapi.dll','DATA'),('nvdaControllerClient32.dll','D:\\compile\\nvdaControllerClient32.dll','DATA'),('PCTKUSR.dll','D:\\compile\\PCTKUSR.dll','DATA'),('PCTKUSR64.dll','D:\\compile\\PCTKUSR64.dll','DATA'),('SAAPI32.dll','D:\\compile\\SAAPI32.dll','DATA')]

During tests the sapi5 driver didn't do anything though, but the NVDA driver worked fine.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2017-02-01 03:19:10

When using AO2 and Pyinstaller, I use a try and except routine to change screen readers.  In a bundle 6 out of 7 work fine, window eyes being the exception.  In a single file distribution, I am usually down to around 3.  I have taken the easy opt for distributing bundles until I can understand more and have gone back to coding rather than trying to understand what I don't .  However one issue that I did overcome successfully in the one file executable was a routine to check for bundle or single file, your sounds and data will be either relative or unpacked to a temporary folder.  Perhaps by now though, you know all this and things compile and run without the added AO2 hiccups.  Keep posting your progress, I guess there's a few of us on similar journeys.

Try my free games and software at www.rockywaters.co.uk

2017-02-02 01:16:35 (edited by magurp244 2017-02-02 07:22:11)

I just can't seem to figure out whats up with the sapi drivers, everything in the driver seems to check out but nothing happens with a compiled binary. Its weird because Pyttsx's sapi drivers haven't given me any problems when compiling. Digging a bit more into a_o2 I was actually really surprised at just how easy it is to plug in and run with the NVDA controller though, not that I play with dll's much, heh. Example:

import ctypes

#load NVDA driver
test = ctypes.windll['nvdaControllerClient32.dll']

#check if NVDA is running
if test.nvdaController_testIfRunning() == 0:
    #stop talking
    test.nvdaController_cancelSpeech()
    #speak
    test.nvdaController_speakText(ctypes.c_wchar_p('speech test'))
    #braille output
    test.nvdaController_brailleMessage(ctypes.c_wchar_p('braille test'))

It looks like the other screen reader drivers are just as easy to implement, though it looks like Jaws, Dolphin, and Window Eyes all seem to have their own proprietary drivers that may require an installed copy, and none of them work on XP so i'm a bit hosed on that. I think maybe i'll go with Pyttsx and directly embed the screen reader drivers, see how that goes. On a side note, other than getting nvdacontrollerclient32.dll from Accessible_output2, is there any other official download or repo for it?

Edit: Nevermind, found it on the [NVDA github].

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer