Can't run app exe on other machines, python2.7, fpdf, pyinstaller - python-2.7

I have an issue with running exe application from pyinstaller on other machines. It is looking for path on PC, where I built application:
console output
I use and added font in pyPDF in following way:
from fpdf import FPDF
pwd = os.path.realpath(os.path.dirname(sys.argv[0])) + "\\font\\DejaVuSansCondensed.ttf"
pdf = FPDF(orientation = 'L', unit = 'mm', format='A4')
pdf.add_page()
# Add a DejaVu Unicode font (uses UTF-8)
# Supports more than 200 languages. For a coverage status see:
# http://dejavu.svn.sourceforge.net/viewvc/dejavu/trunk/dejavu-fonts/langcover.txt
pdf.add_font('DejaVu', '', pwd, uni=True)
pdf.set_font('DejaVu', '', 18)
#then I use pdf.write() to write data
#save and close pdf file
pdf.output('C:\\Users\\' + getpass.getuser() + '\\Documents\\pdf_file' + time_stamp + '.pdf', 'F')
I tried to build it in following ways:
pyinstaller app.py
pyinstaller --onefile app.py
There is no issue on machine, where I build code. I suppose there is something in output method from fpdf or settings of pyinstaller, am I right?
I have to create pdf with unicode characters. I use latest versions of fpdf and pyinstaller modules.
I will be thankful for any help.
Thank you in advance,

There is no issue when you build it on your machine since you have DejaVuSansCondensed.ttf where python expects it to be. But when you compile with PyInstaller and run it on another system, it is looking in the same spot (which may not exist on other systems).
What I would suggest is to copy that ttf file to your current working directory and update the line font line to (or something similar that fits your code):
pwd = "DejaVuSansCondensed.ttf"
You also have to make sure that the .exe has access to that file, independent of system (relative to the exe). So you have to copy the ttf file to the same directory as your exe so that when you run it on another computer, your code will look in the directory that the exe is in and find the ttf file.

Related

Dockerfile COPY command doesn't make my file available with I run the image

I am using Linux Ubuntu 20.04, Pycharm Pro, Python 3.9, Docker (installed a couple weeks ago, don't remember ver).
I have a Python project in the path (
/home/crusty.user/PythonProjects/NoLegals
There are 4 files in this path: main.py, utils.py, NoLegals_Config.csv, Dockerfile
The csv file acts as a config to tell the python project which parts of the research to do, or not to. It reads a line, with a Y or N. Pretty simple. It works great in Linux and in windows.
From the path above, I run sudo docker build -t nolegals .
Everything runs successfully.
When I try to run the Dockerfile (sudo docker run nolegals) it fails when it gets to the csv file with the error:
FileNotFoundError: [Errno 2] No such file or directory: '/home/crusty.user/PythonProjects/NoLegals/NoLegals_Config.csv'
In my Dockerfile I have:
WORKDIR /NoLegals
Further down I have:
COPY NoLegals_Config.csv /
COPY main.py /
COPY utils.py /
--There's a bunch of otherstuff for setting up the environment, libraries, etc. all of which runs successfully on the build. Also, I don't get a failure regarding the path of the csv file during build. I've been digging around and I've learned that it might having something to do with not being able to find the csv file within the Docker image when it builds, but it finds the main.py and utils.py just fine. There is a line of code in the Python main.py file that points to the location of the csv file dynamically as a suggestion to fix the problem but this too has failed. The path the error prints is also the correct path to the csv file.
#this works in linux, just not in the Dockerfile
filename = r'NoLegals_Config.csv
filepath = os.path.join(os.getcwd(), filename)
print(filepath)
I've tried LOTS of different things in that COPY NoLegals_config.csv / line, but to no avail. I appreciate any suggestions.
I've tried various forms of the COPY. Previous to the one listed was using the syntax:
COPY <source-path> <destination-Path> COPY NoLegals_Config.csv / COPY <full path of source> </NoLegals/NoLegals_Config.csv>
I've tried some other things that I can't recall.
What I ended up doing was fully qualifying the COPY statements.
WORKDIR /NoLegals
COPY NoLegals_Config.csv /NoLegals/NoLegals_Config.csv
COPY main.py /NoLegals/main.py
COPY utils.py /NoLegals/utils.py
CMD ["python"."/NoLegals/main.py"]
Is there a simpler way I could have written this?

Using custom fonts on shinyapps.io

I would like to use a custom font in my shiny app (on plots) on shinyapps.io. I have my Roboto-Regular.ttf in the ./www/ directory. And this is the upper portion of my app.R file:
dir.create('~/.fonts')
system("chmod +x ./www/Roboto-Regular.ttf")
system("cp ./www/Roboto-Regular.ttf ~/.fonts/")
system('fc-cache -f -v ~/.fonts/')
system('fc-match Roboto')
library(ggplot2)
library(shiny)
library(shinythemes)
library(extrafont)
font_import(pattern="Roboto",prompt=FALSE)
loadfonts()
print(fonts())
Upon deploying the app, I end up with an error that looks like this:
Registering fonts with R
Scanning ttf files in /usr/share/fonts/, ~/.fonts/ ...
Extracting .afm files from .ttf files...
/home/shiny/.fonts/Roboto-Regular.ttfWarning in gzfile(dest, "w") :
cannot open compressed file '/opt/R/3.5.1/lib/R/library/extrafontdb/metrics/Roboto-Regular.afm.gz', probable reason 'Permission denied'
Error in value[[3L]](cond) : cannot open the connection
Calls: local ... tryCatch -> tryCatchList -> tryCatchOne -> <Anonymous>
Execution halted
Does anyone see what might be wrong?
After a bit of struggle I found an even simpler solution that works on shinyapps.io:
Here we go:
Place custom font in www directory: e.g. IndieFlower.ttf from here
Follow the steps from here
This leads to the following upper part of the app.R file:
dir.create('~/.fonts')
file.copy("www/IndieFlower.ttf", "~/.fonts")
system('fc-cache -f ~/.fonts')
Since Linux looks into the .fonts directory to search fonts, you don't need the extrafont package, but you can directly use those fonts like:
ggplot(mapping=aes(x=seq(1,10,.1), y=seq(1,10,.1))) +
geom_line(position="jitter", color="red", size=2) + theme_bw() +
theme(text=element_text(size = 16, family = "IndieFlower"))
This is the answer I received from RStudio regarding this. I haven't tested this out myself.
Hi,
Our developer was able to advise this is due to a possibly unfortunate design choice made when they created extrafont and the associated extrafontdb package. The extrafont font database is stored in the extrafontdb package directory -- that's essentially all that the extrafontdb package is used for.
This means that the extrafontdb directory needs to be user-writable. If the user installs the package, this will work fine, but if root installs the package (as is the case on shinyapps.io), then it won't work.
One potential workaround is to install the extrafontdb package to library that is in subdirectory of the app.
To do it: create an r-lib/ subdir, and download the extrafontdb source package there:
dir.create('r-lib')
download.file('https://cran.r-project.org/src/contrib/extrafontdb_1.0.tar.gz','r-lib/extrafontdb_1.0.tar.gz')
When deployed, the app will include this r-lib/ subdirectory and the extrafontdb source package.
Then, at the top of the app, install the extrafontdb package from the source package, into the r-lib directory.
.libPaths(c('r-lib', .libPaths()))
install.packages('r-lib/extrafontdb_1.0.tar.gz',type = 'source',repos = NULL)
They deployed an app on shinyapps.io that does the extrafontdb installation, and it works fine. The libpath is set so so that install.packages() will install from the provided source package to the r-lib/ subdirectory of the app.
Please let us know if you're able to implement the above or have any additional questions.
Thanks,
Adding an alternative answer to symbolrush's answer which I found did not work. Here was the code I used initially:
# Add fonts to shiny linux server
if (Sys.info()[['sysname']] == 'Linux') {
dir.create('~/.fonts')
fonts = c(
"www/IBMPlexSans-Regular.ttf",
"www/IBMPlexSans-Bold.ttf",
"www/IBMPlexSans-Medium.ttf"
)
file.copy(fonts, "~/.fonts")
system('fc-cache -f ~/.fonts')
}
# Load fonts and set theme
font_paths("fonts")
font_add("IBMPlexSans", regular = "IBMPlexSans-Regular.ttf")
font_add("IBMPlexSans-Bold", regular = "IBMPlexSans-Bold.ttf")
font_add("IBMPlexSans-Medium", regular = "IBMPlexSans-Medium.ttf")
showtext_auto()
The bizarre thing is that the first instance of the app on shinyapps.io worked, including the custom fonts. However when the app went to sleep and was opened a second time, I get this error in the log:
Error in value[[3L]](cond) : font file not found for 'regular' type
I was never able to debug why this was the case, but I tried a simpler solution that has worked perfectly so far. I moved my fonts to a /font folder in the app folder (I don't think using the /www folder is necessary) and added the /font folder using path_folder():
library(showtext)
# Load fonts and set theme
font_paths("fonts")
font_add("IBMPlexSans", regular = "IBMPlexSans-Regular.ttf")
font_add("IBMPlexSans-Bold", regular = "IBMPlexSans-Bold.ttf")
font_add("IBMPlexSans-Medium", regular = "IBMPlexSans-Medium.ttf")
showtext_auto()
I hope this helps anyone who is having problems with their app not running after the first instance, as I could not find the same situation anywhere on stackoverflow.

Single executable file created by py2exe not working

I've created a Python app using Pyqt4 to open up a dialog and do some image processing using opencv2. The app is working fine when executing the script as:
python script.py
To create a single executable file for the script, I'm using py2exe with bundle_files = 1 option. It is creating a single exe file but when clicking the file, a console appears stays for few seconds and a pop-up appears saying program has stopped responding.
I'm working on Windows with anaconda. Please help me with this.
Py2exe generates a logfile.txt in the same folder you first-time execute the generated .exe.
Check this logfile to debug.
Most of the time you need to exclude some dll`s and include at least sip-module;
In your setup.py file:
from distutils.core import setup
import shutil, py2exe
opts = {'py2exe': {'compressed': True, "dll_excludes": ["MSVCP90.dll"], "includes" : ["sip"]}}
setup(console=[{"script" : "main.py"}], options=opts)
shutil.rmtree('build', ignore_errors=True) #Remove the build folder
Personally I did not encounter the need to include PyQt4 modules for compilation..
And btw be glad that python is an interpreted language, otherwise you`ll have to worry about linking to libraries on every build (like in cpp.., which is annoying)
Greets Dr Cobra

Changing FirefoxProfile() preferences more than once using Selenium/Python

So I am trying to download multiple excel links to different file paths depending on the link using Selenium.
I am able to set up the FirefoxProfile to download all links to a certain single path, but I can't change the path on the fly as I try to download different files into different file paths. Does anyone have a fix for this?
self.fp = webdriver.FirefoxProfile()
self.ft.set_preferences("browser.download.folderList", 2)
self.ft.set_preferences("browser.download.showWhenStarting", 2)
self.ft.set_preferences("browser.download.dir", "C:\SOURCE FILES\BACKHAUL")
self.ft.set_preferences("browser.helperApps.neverAsk.saveToDisk", ("application/vnd.ms-excel))
self.driver = webdriver.Firefox(firefox_profile = self.fp)
This code will set the path I want once. But I want to be able to set it multiple times while running one script.
On Linux and Mac you can set the profile preferences to download to a directory that is a symbolic link to another directory. Then, while running the Selenium driver, you can change the destination of the symlink to the directory you want to download a file in, using the os library in Python.
Code outline (without try/except etc):
import os
from selenium import webdriver
DRIVER = "/usr/local/bin/geckodriver"
fp = webdriver.FirefoxProfile()
fp.set_preference("browser.download.dir", "/your/symlink/name")
fp.set_preference("browser.download.folderList",2)
fp.set_preference("browser.helperApps.neverAsk.saveToDisk", "your/mime/type")
driver = webdriver.Firefox(firefox_profile=fp, executable_path=DRIVER)
# delete symlink before changing to prevent a FileExistsError when changing the link
if os.path.exists("/your/symlink/name"):
os.unlink("/your/symlink/name")
# actually change/create the link
os.symlink("/real/target/directory", "/your/symlink/name")
# download the file
driver.get("https://example.com/file")
# set new target directory
os.unlink("/your/symlink/name")
os.symlink("/different/target/directory", "/your/symlink/name")
driver.get("https://example.com/otherfile")
You can define it only while initializing driver. So to do it with a new path you should driver.quit and start it again.

Can I create a single exe with custom modules files and other files?

Now I'm developing a medium proj with python.
It has many modules and some of them are customize modules. And some of them are non-python files(may be config files), are need to use such as copy/paste (e.g when the client run this app and plug some the hw device, the app will config the hw device and copy the some file of app to hw device (may be ROM))
first my question is : can I create 1 single exe with all of my code, modules and non-python files?
if it is yes, how can I?
you could try changing your approach and putting all the files in the module as text.
then you could use the py2exe to create executable and it would contain all the data you need. this approach makes sense only if you have some small files.
if you want to embed more and bigger files take a look at that solution:
Py2exe: Embed static files in exe file itself and access them
open CMD
Change path to PyInstaller directory.(ex-C:\PyInstaller-2.1)
use this command
pyinstaller.py my.py --noconsole --onefile --icon=icon.ico
change my.py to your script name.
--noconsole option remove the python console while executing your program. If you need console to get an output,just remove --noconsole
--onefile option makes all the modules and files in to one executable file.
--icon=icon.ico gives your exe an icon.Remember to put your icon file in Pyinstaller directory