Using PyInstaller for MPF packaging #97
Replies: 9 comments 6 replies
-
This is awesome, thanks! Looks like there are definitely some nuggets in here for the docs. This is making me wonder about something. Dunno if this is the right place for it, but I'm wondering if we should not tell people to use And if people do have another python app or environment, we can guide them to pipx, but also they probably know more about what they're doing? |
Beta Was this translation helpful? Give feedback.
-
Oh yeah, I think this could definitely be done. MPF is simple, and MPF-MC is essentially a Kivy app, and Kivy has lots of instructions for creating packages like this: https://kivy.org/doc/stable/guide/packaging-windows.html (Also Linux here, though Linux is pretty straightforward and most people using Linux probably can figure out how to install everything.) The main issue is just finding people who want to take on the ownership of doing this. But definitely doable. BTW if you do pull at this thread, the MPF-MC has some components written in Cython that must be compiled as part of the package creation process. Also BTW #2, MPF Monitor uses PyQt6 for graphics, not Kivy (since it needs a more standard forms-based desktop app, not raw graphics like the MC). Also not impossible, and prob would be easy to build in (I think MPF Monitor is simple to install also and usually "just works" for people.) |
Beta Was this translation helpful? Give feedback.
-
So far it appears PyInstaller only works smoothly for projects that are really designed a certain way, maybe that started using PyInstaller early on or only using packages designed for it. I'll probably keep working on this, but a couple examples of why it will not be trivial: ruamel doesn't work well: pyinstaller/pyinstaller#5338 -- PyInstaller group isn't going to change their side, and so far looks like ruamel hasn't been updated (or MPF is not using the latest version?). I did the manual hack stated here, editing the ruamel file in my local packages folder, which did at least work. PyInstaller doesn't work well with dynamic imports, or anything using importlib. Even after doing the easy/documented things to get the "hidden packages" listed so PyInstaller will bundle them, there are still issues getting them to load correctly when included in the PYZ -- packages/modules being inside the EXE instead of loading from the file system as normal. On the plus side -- I did get mpf --version to "work". But, only when building in a mode where all of the packages/modules are included in the package as subfolders and inside the EXE, which isn't really a good usable solution or option. |
Beta Was this translation helpful? Give feedback.
-
Got a lot further, MPF loads the system attract and game YAML files now, and a bunch of devices. inside text_ui.py it imports psutil -- this seems to be different than the other import related issues I fixed. Something special about psutil, I'll do some searching tomorrow to see why the same fix for other missing modules isn't working with this one. |
Beta Was this translation helpful? Give feedback.
-
Issue with psutil was just that I didn't have it installed on my machine, and PyInstaller will only bundle something if installed. Now I have a single EXE (15 MB) that I can place in mc_demo folder and run it. It works, in that I see the asciimatic UI loaded as expected, and no errors or warnings in the log files. Next steps:
|
Beta Was this translation helpful? Give feedback.
-
Whoa, awesome! I’ll be home in a week and will be happy to test it out too on a Windows VM. A lot of people will be very excited for the work you’re doing! |
Beta Was this translation helpful? Give feedback.
-
My test package worked on my wife's laptop, running Windows 10 and having nothing Python related or MPF preinstalled. Probably will be better if I provide some instructions to build the package from source, and maybe push the minor changes (to one file) to a branch somewhere... but for now I just created this to transfer to my wife's laptop to run it, so sharing this for now: https://drive.google.com/drive/folders/1L6U9YC50xksD0QZOHP6iZT9q-ssYJXNM There is an mc_demo.zip file in here. It is just a copy of the mc_demo as-is (no changes), with an extra run.exe in it, which is the file I made from the MPF code with PyInstaller. I just unzipped the file on my wife's laptop and ran the run.exe file in it, and I saw the expected MPF console window. I haven't made the minor changes needed to let you pass command-line args, so I'll probably do that soon, and then push this code somewhere, maybe my private github repo for now. I've only made changes to main.py, basically added a bunch of import statements so that PyInstaller would package all of the stuff that uses dynamic imports in the real code, and changed one line of code to bypass the "commands" system, just always runs "game". Maybe I'll just attach the file to this thread, we'll see. |
Beta Was this translation helpful? Give feedback.
-
Fork created here: https://github.com/ericselk2018/mpf/tree/pyinstaller I added some notes to top of readme.md file, for the simple command-line and test steps I have been doing. I've only been testing on Windows, but according to PyInstaller docs, it will build an executable for whatever platform it is run on -- so in theory, the same steps done on Mac will produce a Mac compatible package (seems too good to be true, surely some extra work needed). |
Beta Was this translation helpful? Give feedback.
-
Mostly some notes to self, but also sharing in case I don't get to follow up on this. I did some work on MC today. Started out trying to run PyInstaller from the MC repo, but that didn't work very well, probably because MC isn't designed to be a console application, but also for circular dependency issues (MPF imports MC and MC imports MPF). So then I added a static import for mpfmc.commands.mc to main.py in MPF code, and called the Command() method on that. This will likely be the way to go and keep the commands interface the same -- for example, a user can still run "mpf mc" instead of something new like mpf-mc.exe. I need to look at the code in commands/both.py to run MPF and MC concurrently, currently I'm just testing by running one of these commands to toggle between the two in main.py: The MC tries to run with what I have now, but stops because mpfmc/mcconfig.yaml does not exist. I need to figure out why PyInstaller isn't including this file, I think it might need to be added as a required dependency in the toml file in MPF MC maybe. It is in my Python site packages (pip installed folder), but PyInstaller isn't copying it to the compiled PYZ bundle. Edit: Was able to resolve the mcconfig.yaml issue by adding --collect-all=mpfmc option. There are other related commands that might be better than "all" to collect just data files, just binary files, or other options. Now getting error about a missing audio related DLL (aka progress). |
Beta Was this translation helpful? Give feedback.
-
In another forum we have been talking a lot about issues that people are having trying to install MPF on Windows (most Mac users aren't having as many issues). Since I have a good amount of experience with installed applications on Windows, I've been trying to help. My knowledge of Python, MPF, and Python Packages is almost 0 though -- I am an NPM/TypeScript dev today, and before that C++/EXE/DLL native Windows apps.
I've put together detailed notes of my "clean install on Windows 10" in case it might help or start some discussion. It should be publicly available here:
https://docs.google.com/document/d/1Z8QHu7pB-2hzHaD4zLK4NsKw9J4VwKIWOpJmpkL_q2U/edit?usp=sharing
TLDR; It seems for me, I needed to check the "install for all users" option in the Python installer to have success installing MPF. Without that, the pipx MPF install step was unable to locate pip. Since it creates the venv using the --without-pip flag, maybe that is why it can't find pip unless it is installed globally "for all users". If this does seem like a valid requirement, I can try updating the Windows installer docs and submit a PR for that (never done it, but I understand the basic concepts). If anyone sees anything else useful in my notes, I'd be happy to discuss or recreate my steps to get more info from them.
At least until the end of May, and maybe June... I can't work on pinball, so happy to help in other ways I can. After that I may go dark for a while, heads up.
Beta Was this translation helpful? Give feedback.
All reactions