Red_Head_CPU
Red_Head_CPU

Reputation: 19

Problems with compiling .py to .exe

I try to compile a .py file to a .exe file with the pyinstaller, but I allways get this warning in the terminal:

c:\users\cpuhv\appdata\local\programs\python\python39\lib\site-packages\setuptools\distutils_patch.py:25: UserWarning: Distutils was imported before Setuptools. This u
sage is discouraged and may exhibit undesirable behaviors or errors. Please use Setuptools' objects directly or at least import Setuptools first.
  warnings.warn(

When I try to run the .exe file I get a Warning Window with the text: "Failed to execute script"

Does anyone know how to fix this?

Upvotes: 0

Views: 2074

Answers (4)

nobl3man
nobl3man

Reputation: 1

I had this exact same problem with getting PyQt6 to compile properly with pyinstaller. I discovered that the problem is that pyinstaller is not copying all the needed PyQt6 files when it creates the .exe file.

What helped me was to add the whole PyQt6 folder using the "--add-data" flag.

So, you end up having:

pyinstaller --noconfirm --onefile --windowed  "C:/Users/cpuhv/Documents/Projekte/Python/Python Test/PDFConverter.py" --add-data "PYTHON_DIRECTORY/Lib/site-packages/PyQt6;PyQt6"

Of course you need to replace "PYTHON_DIRECTORY" with the path to your Python directory. If you are using a virtual environment, then put the path to the python "Lib" folder in your venv.

Although doing the above might create a duplicate problem which you need to solve in your created .spec file. If you try to launch the Qt application and you receive an error along the line of "the file already exists" for QtCore.pyd, QtGui.pyd and QtWidgets.pyd, don't sweat it. Just add the below code after the "Analysis" part of your created .spec file:

for d in a.datas:
    if 'QtCore.pyd' in d[0]:
        a.datas.remove(d)
        break

for d in a.datas:
    if 'QtGui.pyd' in d[0]:
        a.datas.remove(d)
        break

for d in a.datas:
    if 'QtWidgets.pyd' in d[0]:
        a.datas.remove(d)
        break

What the above code does is to remove these three files from the PyQt6 folder in "datas", since these files already exist in the folder created by pyinstaller.

When you are done with this, run your build again using "pyinstaller somefile.spec" et voila.

Hope this helps someone out there, if the above solutions also did not work for you

Upvotes: 0

miserd
miserd

Reputation: 36

I found a solution for you. Onefile and all. I'm leaving my previous answer as even though it is not ideal, it may be helpful to someone.

Follow the instructions found here: https://github.com/AlJohri/docx2pdf/issues/5

pyinstaller is missing hook script to run docx2pdf.

save hook-docx2pdf.py to \Lib\site-packages\PyInstaller\hooks, Copy the text below in:

from PyInstaller.utils.hooks import collect_all

hiddenimports = collect_all('docx2pdf')

I had to exit and restart PyCharm afterwards, but that fixed all errors relating to docx2pdf import troubles.

After that, I was running into a lot of errors relating to PyQt. I've had trouble with PyQt and Pyinstaller working together in the past.

If you downgrade from PyQt6 to PyQt5 though, all of your problems will be solved. Then simply change your imports list to import from PyQt5.

Then run pyinstaller --onefile -w main.py

It seems that Pyinstaller and PyQt6 are not fully compatible yet.

If this worked, I would appreciate if you would mark this as the accepted answer.

Upvotes: 1

miserd
miserd

Reputation: 36

So I was having similar issues yesterday. I have a workaround that will allow the program to run, but not as one file. It will also make the folder unfortunately large, as it will include all of your virtual env packages. I hope someone else can provide you a better answer.

First off, do not use --windowed when debugging. Omit --windowed, and then run the .exe using command line. This will show errors for you. In this case:

  Traceback (most recent call last):
  File "main.py", line 6, in <module>
  File "PyInstaller\loader\pyimod03_importers.py", line 531, in exec_module
  File "docx2pdf\__init__.py", line 13, in <module>
  File "importlib\metadata.py", line 551, in version
  File "importlib\metadata.py", line 524, in distribution
  File "importlib\metadata.py", line 187, in from_name
importlib.metadata.PackageNotFoundError: docx2pdf
[6860] Failed to execute script main

We see an import error because Pyinstaller does not account for second level imports. In this case I believe docx2pdf has its own import list. Look into Hooks and import errors, there are many solutions - however I was personally not able to achieve results with the solutions I read.

So my alternative "bandaid" solution for you requires you to use the .spec file. Run Pyinstaller --noconfirm main.py Next you need to edit the main.spec file that was generated in your working directory. add your site-packages folder to the datas so it looks like this:

datas=[('C:\\PathToProjectFolder\\venv\\Lib\\site-packages', '.')]

And edit the console line in the .spec file to: console=False (hides the console when you click your .exe)

With the updated .spec file, we can run Pyinstaller again, this time entering:

pyinstaller --noconfirm main.spec

note the main.spec, not main.py.

The .exe in the dist folder should run now. Good luck finding a better solution that doesn't make your folder exorbitantly large.

Upvotes: 0

Red_Head_CPU
Red_Head_CPU

Reputation: 19

import sys
import os
import comtypes.client
from pdf2docx import Converter
from docx2pdf import convert
from PyQt6.QtWidgets import QApplication, QWidget, QPushButton, QToolTip, QFileDialog, QLineEdit, QLabel
from PyQt6.QtGui import QIcon, QFont
from pathlib import Path


home_directory = str(Path.home()).replace("/", "\\")


def name(file):
    path = file
    i = 1
    while os.path.exists(path):
        path = file
        path += " " + str(i)
        i += 1
    return path


class MainGui(QWidget):
    def __init__(self):
        super().__init__()

        self.row1 = 50
        self.row2 = 100
        self.row3 = 175
        self.row4 = 260

        self.button_font = QFont('Verdana', 10)
        self.line = QLineEdit(self)
        self.browse = QPushButton("Durchsuchen", self)
        self.selected_files_count = QLabel("0 Elemente ausgewählt", self)
        self.docx_files = []
        self.pdf_files = []
        self.init_me()

    def init_me(self):
        font = QFont('Verdana', 8)
        font.setBold(True)
        QToolTip.setFont(font)

        pdf_to_docx_button = QPushButton('PDF Dateien auswählen', self)
        pdf_to_docx_button.setFont(self.button_font)
        pdf_to_docx_button.setToolTip('öffen Sie einen Datei-Dialog, um Dateien auszuwählen')
        pdf_to_docx_button.move(50, self.row1)
        pdf_to_docx_button.setFixedWidth(200)
        pdf_to_docx_button.setFixedHeight(35)
        pdf_to_docx_button.clicked.connect(self.pdf_to_docx_button_pressed)

        docx_to_pdf_button = QPushButton('DOCX Dateien auswählen', self)
        docx_to_pdf_button.setFont(self.button_font)
        docx_to_pdf_button.setToolTip('öffen Sie einen Datei-Dialog, um Dateien auszuwählen')
        docx_to_pdf_button.move(300, self.row1)
        docx_to_pdf_button.setFixedWidth(200)
        docx_to_pdf_button.setFixedHeight(35)
        docx_to_pdf_button.clicked.connect(self.docx_to_pdf_button_pressed)

        self.line.move(50, self.row3)
        self.line.setFixedWidth(320)
        self.line.setFixedHeight(30)
        self.line.setFont(self.button_font)
        self.line.setText(home_directory)

        self.browse.move(390, self.row3)
        self.browse.setFont(self.button_font)
        self.browse.setFixedWidth(110)
        self.browse.setFixedHeight(30)
        self.browse.setToolTip('öffen Sie einen Datei-Dialog, um den Ausgabe-Ordner auszuwählen')
        self.browse.clicked.connect(self.browse_button_pressed)

        convert_button = QPushButton('Konvertieren', self)
        convert_button.move(190, self.row4)
        convert_button.setFixedWidth(170)
        convert_button.setFixedHeight(50)
        convert_button.setFont(self.button_font)
        convert_button.clicked.connect(self.convert)

        self.selected_files_count.move(200, self.row2)
        self.selected_files_count.setFont(self.button_font)
        self.selected_files_count.setFixedSize(200, 50)

        self.move(50, 50)
        self.setFixedSize(550, 400)
        self.setWindowTitle("Konverter")
        self.setWindowIcon(QIcon(r"icon.png"))
        self.show()

    def pdf_to_docx_button_pressed(self):
        fd = QFileDialog()
        fd.setAcceptMode(QFileDialog.AcceptMode.AcceptOpen)
        path = fd.getOpenFileNames(self, "PDF Dateien öffnen", home_directory, "PDF (*.pdf)")
        fd.close()
        print(len(path[0]))
        if len(path[0]) > 0:
            self.pdf_files.extend(path[0])
            print(True)
            self.update_label()

    def docx_to_pdf_button_pressed(self):
        fd = QFileDialog()
        fd.setAcceptMode(QFileDialog.AcceptMode.AcceptOpen)
        path = fd.getOpenFileNames(self, "Docx Dateien öffnen", home_directory, "Word-Dokument ("
                                                                                                  "*.docx)")
        fd.close()
        if len(path[0]) > 0:
            self.docx_files.extend(path[0])
            self.update_label()

    def convert(self):
        directory = self.line.text()
        if not os.path.exists(directory):
            return
        if not os.path.isdir(directory):
            return
        for file in self.pdf_files:
            docx_file = name(directory + "/" + os.path.basename(file).replace(".pdf", ".docx"))
            convert_pdf_to_docx(file, docx_file)
            update_docx(docx_file)
        for file in self.docx_files:
            convert_docx_to_pdf(file, name(directory + "/" + os.path.basename(file).replace(".docx", ".pdf")))
        self.docx_files = []
        self.pdf_files = []

    def browse_button_pressed(self):
        fd = QFileDialog()
        fd.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
        path = fd.getExistingDirectory(self, "Ausgabeordner auswählen", home_directory)
        if path is not None and not len(path) <= 0:
            self.line.setText(path)

    def update_label(self):
        count = len(self.pdf_files) + len(self.docx_files)
        if count != 1:
            self.selected_files_count.setText(str(count) + " Elemente ausgewählt")
        else:
            self.selected_files_count.setText("ein Element ausgewählt")


def convert_pdf_to_docx(pdf_file, docx_file):
    cv = Converter(pdf_file)
    cv.convert(docx_file, start=0, end=None)
    cv.close()


def convert_docx_to_pdf(docx_file, pdf_file):
    convert(docx_file, pdf_file)


def update_docx(file):
    docx_format_name = 16
    file_in = os.path.abspath(file)
    file_out = file_in.replace(".docx", "2.docx")
    word_application = comtypes.client.CreateObject('word.Application')
    if word_application is not None:
        doc = word_application.Documents.Open(file_in)
        doc.SaveAs(file_out, FileFormat=docx_format_name)
        doc.Close()
        word_application.Quit()
        os.remove(file_in)
        os.rename(file_out, file_in)


app = QApplication(sys.argv)

w = MainGui()

sys.exit(app.exec())

This is my code.

The command is:

pyinstaller --noconfirm --onefile --windowed  "C:/Users/cpuhv/Documents/Projekte/Python/Python Test/PDFConverter.py"

Upvotes: 0

Related Questions