I wrote a program in Python 2.7 for my office. Using PyInstaller with the --onefile flag, I've compiled it into a single EXE file to distribute to my co-workers. What I'm trying to figure out now is how to supply updates automatically but still keep the program as a single file.
My current method is to have the program alert the user of a new version and give instructions where to obtain the updated file. This works, but I want to find some method of updating without the user's involvement.
I am aware of Esky, which can create auto-updating Python programs, but as far as I can tell, it requires the program to exist as multiple files.
Any suggestions on how I make a standalone, single file EXE python program that can auto-update?
you can download your new executable to a temp folder
use __file__ to know the current location of the executable
show a message that you need to restart
use sys.execcv or subrocess alternative to replace the current running process with a small script something like
"sleep(1) & copy {tempfile} {filename}".format(tmp_file,__file__)
(simple batch commands that overwrite the old exe with the new exe)
When your application has alerted user of a new version and user has said they want it, download the new exe, start the new exe, and shut down current exe. The new exe should remove any old exes it sees around. The different versions need to be named differently.
Related
I'm attempting to run a python script that creates a custom menu inside Maya when a .bat file is opened to start Maya. The python script is not inside the Maya project and I wanted to add the path in the batch file. I have this in the .bat file:
start D:\TOOLS\Maya2019\bin\maya.exe -command evalDeferred(python('execfile(\"D:\CustomMenu_startup.py\")'))
Several attempts already but it's returning a syntax error.
solved by using:
start D:\TOOLS\Maya2019\bin\maya.exe -command evalDeferred(python(\"execfile('D:\\CustomMenu_startup.py')\"))
I'd suggest that, as a rule, it'd be better to push the deferred evaluation into the python itself. That way you don't have to think about it in all 3 languages (BAT, MEL, and Python).
There may also be parts of the work you can execute before the main Maya event loop kicks in, which will save some startup time -- evaldeferred is the safe choice before touching the Maya UI or the scene but you might have other jobs (like downloading files or checking the user's disk) that can be done safely while Maya itself is still loading. That's another reason to do the deferred part in Python instead of in the outermost MEL call.
If you're interested in generating launchers like this you can simply distribute a mel file instead of the BAT; MEL is executable by Maya as a file argument so your commandline gets simpler and if you have the correct file associations set up it's double-clickable.
You might also want to check out these blog posts about how to create python launchers for Maya:
https://theodox.github.io/2018/pythonception#pythonception
https://theodox.github.io/2018/keystone#keystone
I'm asking help today because I'm new to Tkinter and Pyinstaller (and python in general) and I'm having troubles with it.
I have a simple app working with sqlite, tkinter and pyinstaller to compile all of this in an executable program, the entrance point of my program is a file named main.py
This file calls all the dependancies (like the sqlite module for python, tkinter and my other files like classes etc...)
I made a very simple interface, with a Hello World in a tkinter label and a button to go to page 2 which displays page2 (also in a label), just to see if I'm capable of making it all run and compile all of these pieces together.
I can run it throught my shell executing it like : python main.py and everything is working fine.
But when I run pyinstaller on my linux machine, and start executing the program, nothing appears, my database.db (sqlite database file) is created but I don't have any interface like when I run it with my shell. The thing is getting even worse on windows where, once I've my .exe it just opens a shell and crash after few seconds, not even creating the database.
What I did is I created a 'log file', in which I write the steps of the program.
As you can see on the following picture, the 2 first prints are wrote in my log file (on linux), so I think it crashes when I try to create the window.
If any of you have an idea on what I do wrong, I would really appreciate help :)
General
From the PyInstaller manual:
Before you attempt to bundle to one file, make sure your app works correctly when bundled to one folder. It is is much easier to diagnose problems in one-folder mode.
As the comments suggested, use a catch-all try/except block to log all exceptions to a file. That is probably the best way to see what is really happening. Make sure that the logfile is created in an existing location where you have the necessary permissions.
I would suggest to take advantage of the built-in logging module instead of creating your own. It can e.g. automatically add from which file a log line was created.
IMHO, it is probable that the failures on Linux and ms-windows have completely different causes. You should probably treat them as different issues.
Linux
When you use single file mode, that file is unpacked into a temporary folder, probably somewhere in /tmp. Some Linux distributions mount the /tmp filesystem with the noexec flag. This is incompatible with PyInstaller.
ms-windows
On windows, there are basically two different Pythons; python.exe and pythonw.exe. Basically it is one of the quirks of windows that this is necessary. The latter is for GUI programs like tkinter programs. A tkinter script should not show a cmd window. So I'm guessing that PyInstaller calls your command with python.exe instead of pythonw.exe. From the manual:
By default the bootloader creates a command-line console (a terminal window in GNU/Linux and Mac OS, a command window in Windows). It gives this window to the Python interpreter for its standard input and output. Your script’s use of print and input() are directed here. Error messages from Python and default logging output also appear in the console window.
An option for Windows and Mac OS is to tell PyInstaller to not provide a console window. The bootloader starts Python with no target for standard output or input. Do this when your script has a graphical interface for user input and can properly report its own diagnostics.
As noted in the CPython tutorial Appendix, for Windows a file extention of .pyw suppresses the console window that normally appears. Likewise, a console window will not be provided when using a myscript.pyw script with PyInstaller.
Also, on windows it can matter which Python distribution you're using. I used to be a fan of Anaconda, but lately I've come to prefer the python.org version because it gives me less headaches. On anaconda Python I had the problem that tkinter programs would not launch without showing a cmd window, whatever I tried. Only switching to python.org Python solved that problem.
I currently have a Python scrip that runs through all Excel files in the current directory and generates a PDF report.
It works fine now but I don't want the users to be anywhere near frozen Python scripts. I created an MSI with cxFreeze which puts the EXE and scripts in the Program Files directory.
What I would like to be able to do is create a shortcut to this executable and pass the directory the shortcut was run from to the Python program so that can be set as the working directory. This would allow the user to move the shortcut to any folder of Excel files and generate a report there.
Does Windows send the location of a opened shortcut to the executable and is there a way to access it from Python?
When you launch a shortcut, Windows changes the working directory to the directory specified in the shortcut, in the Start in field. At this point, Windows has no memory of where the shortcut was stored.
You could change the Start in field to point to the directory that the shortcut is in. But you'd have to do that for every single shortcut, and never make a mistake.
The better approach is to use a script, rather than a shortcut. Place your actual Python script (which we'll call doit.py for sake of example) somewhere in your PYTHONPATH. Then create a single-line Python script that imports it:
import doit
Save it (but don't name it doit.py) and copy it to each directory from which you want to be able to invoke the main script. In doit.py you can use os.getcwd() to find out what directory you're being invoked from.
You could also do it with a batch file. This is a little more flexible in that you can specify the exact name of the script and which Python interpreter should be used, and don't need to store the script in a directory in PYTHONPATH. Also, you don't need to worry about the file's name clashing with the name of a Python module. Simply put this line in a file:
C:\path\to\your\python.exe C:\path\to\your\script.py
Save it as (e.g.) doit.bat and copy it into the directories from which you want to invoke it. As before, your Python script can call os.getcwd() to get the directory. Or you can write it so your Python script accepts it as the first argument, and write your batch file like:
C:\path\to\your\python.exe C:\path\to\your\script.py %cd%
Another thing you can do with the batch file approach is add a pause command to the end so that the user is asked to press a key after the script runs, giving them the opportunity to read any output generated by the script. You could even make this conditional so that it only happens if an error occurs (which requires returning a proper exit code from the script). I'll leave that as an exercise. :-)
Is there a problem with modifying the script to take the directory to process as a command line argument?
You could then configure the different shortcuts to pass in the appropriate directory.
Type the following into a batch file (i.e. script.bat):
python \absolute\path\to\your\script.py %~dp0
pause
Then add these imports at the top of your python file script.py (if not already included):
import os
import sys
And add this to the bottom of the python file (or combine it with a similar statement):
if __name__ == "__main__":
# set current working directory:
if len(sys.argv) > 1:
os.chdir(sys.argv[1])
main()
replace main() with whatever function you want to call or code you want to run.
The following is how I came to my answer:
I tried using kindall's answer and had the following issues:
The first suggestion of storing the script somewhere in PYTHONPATH could not be applied to my situation because my script will be used on a server and needs to be independent of the client computer's python environment (besides having the required pip installations).
I tried calling my python script from a Windows Batch File which could be moved to a different location. Instead of the batch file's location being used as the current working directory, it was C:\Windows.
I tried passing %cd% as an argument to my python script, then setting that to be my CWD. This still resulted in a CWD of C:\Windows.
After reviewing the comments, I tried Eryk Sun's suggestion of instead passing %~dp0 as an argument to the python script. This resulted in the CWD being correctly set to the batch file's location.
I hope this helps others facing similar difficulties.
I'm making a chat client for OSX, and I wanted it to be able to run as both a .app (for my less technologically inclined users) and as a .py file. I made a workflow app that contained two .py files (an auto-updater and the client itself), run by a python script in the .wflow file. This worked well. However, I couldn't update the updater or workflow script, and the icon was the Python rocket instead of the icon I had chosen. Then, I combined the client .py file with the updater .py file. This still worked, and now I could update the updater. I still couldn't update the python script in the workflow, though, and the icon was still wrong. So, I modified the updater to open the .wflow file, split it into a list (based on python comments in the workflow's python script, such as "#Start") of the stuff before the script, the script's modification time, and the stuff after the script. If the modification time isn't the same as the modification time of the remote file (the one that the updater updates from), then the script downloads the remote .py file, replaces characters (<, >, &) that .wflow files replace ('<' -> "<"), and opens document.wflow with the "w" (write/replace) flag. Then, the stuff that was before the old script, the downloaded script, and the stuff that was after the old script (using file.write(''.join(list))) are all put into document.wflow. This should work, but OSX no longer sees document as an automator file.
As you can see, OSX thinks that the old file is a Workflow, while the new file is a "Microsoft Excel 97-2004 workbook". The IMClient.app (the application that contains document.wflow) gives this message when I try to run it: "The document "IMClient" could not be opened because it is damaged or incomplete." Does anyone know how to fix this?
I'm using python 2.7 and OSX 10.7. The updater is downloading files via FTP.
If clarification is necessary, just ask. I'll post the working and nonworking files if anyone wants them.
EDIT: the file command gives "document.wflow: XML document text" for both the old and new file.
I am new to python programming and development. After much self study through online tutorials I have been able to make a GUI with wxpython. This GUI interacts with a access database in my computer to load list of teams and employees into the comboboxes.
Now my first question is while converting the whole program into a windows exe file can I also include the .accdb file with it...as in I only need to send the exe file to the users and not the database..if yes how.
My second question is... I actually tried converting the program into exe using the py2exe (excluding the database...am not sure how to do that) and I got the .exe file of my program into the "Dist" folder. But when I double click it to run it a black screen (cmd) appears for less than a second and disappears. Please help me understand the above issue and resolve it.
am not sure if I have a option of attaching files...then I could have attached my wxpython program for reference.
Thanks in advance.
Regards,
Premanshu
The console could possibly appear if you used the 'console' parameter to setup(). Switch to 'windows' instead if that is the case. Can't say for sure without seeing your setup.py script. Possibly your app could also be opening console, but again hard to say without seeing source. One thing to check is to make sure you are not printing anything to stdout or stderr. You might want to redirect all of stdout and stderr to your log just in case, and do this right at the top of your start script so that if some 3rd party import was writing to stdout you'd be able to capture that.
The db is not part of your executable, so py2exe will not do anything with it. However, you should probably package your application with an installer, and you can make the installer include the db and install it along with the executable.