使用 PyQt5 构建并由 pyinstaller 编译的 GUI 无法正常工作 [不重复] [重复]

Posted

技术标签:

【中文标题】使用 PyQt5 构建并由 pyinstaller 编译的 GUI 无法正常工作 [不重复] [重复]【英文标题】:GUI built with PyQt5 and compile by pyinstaller not working [NOT duplicate] [duplicate] 【发布时间】:2020-06-06 05:09:32 【问题描述】:

我正在重新发布这个问题,因为它已关闭。

但是,How to add static(html, css, js) files in pyinstaller to create standalone exe file? 与我的问题不同,因为我的 GUI 不使用除 .py 文件(下面提供的代码)之外的任何其他类型的文件。 requests_html 只是一个导入的模块。


当我尝试编译一个简单的 GUI 时,编译后的 .exe 文件会打开一个空的命令窗口几秒钟然后自动关闭。

我尝试过使用 --hidden-import=sip 和其他任何我可以在网上找到的东西,但无法让它工作或弄清楚它为什么不能正常工作。

由于我无法收到任何错误消息,因此我在下面包含了 GUI 代码。

提前致谢!

from PyQt5 import QtCore, QtWidgets
import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic
from requests_html import HTMLSession
import os, os.path
import wget
import glob

from remove_empty import removeEmptyFolders

form_class = uic.loadUiType('light_gui.ui')[0]


class Ui_MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(Ui_MainWindow, self).__init__()
        self.setupUi(self)

        self.dir_open.clicked.connect(self.pick_dir)
        self.start_btn.clicked.connect(self.doong_light)



    def pick_dir(self):
        fname = QFileDialog.getExistingDirectory(self)
        self.down_path.setText('%s\\' % os.path.normpath(fname)) # os.path.normpath(path) --> Change "/" to "\" on Windows OS
        return(fname)



    def doong_light(self):
        def one_page_contents(target_page):
            contents = target_page.html.find('img')
            vid_contents = target_page.html.find('video')

            attached = []
            others = []

            for content in contents:
                if 'attach' in content.attrs['src']:
                    # print(content.attrs['src'][0])
                    # if content.attrs['src'][0] == '/files':
                    #     print('https://twicenest.com' + content.attrs['src'])
                    #     attached.append('https://twicenest.com' + content.attrs['src'])
                    # else:
                    #     attached.append(content.attrs['src'])
                    # attached.append(content.attrs['src'])
                    attached.append(str(content.attrs['src']))

                elif 'kakao' in content.attrs['src']:
                    others.append(content.attrs['src'])
                elif 'blogspot' in content.attrs['src']:
                    others.append(content.attrs['src'])
                try:
                    if '이미지' in content.attrs['alt']:
                        others.append(str(content.attrs['src']).split('?')[0] + '.jpg')
                except:
                    pass

            for content in vid_contents:
                if 'attach' in content.attrs['src']:
                    # if content.attrs['src'][0] == '/files':
                    #     attached.append('https://twicenest.com' + content.attrs['src'])
                    # else:
                    #     attached.append(content.attrs['src'])
                    # attached.append(content.attrs['src'])
                    attached.append(str(content.attrs['src']))

                elif 'kakao' in content.attrs['src']:
                    others.append(content.attrs['src'])
                elif 'blogspot' in content.attrs['src']:
                    others.append(content.attrs['src'])
                try:
                    if '비디오' in content.attrs['alt']:
                        others.append(str(content.attrs['src']).split('?')[0] + '.mp4')
                except:
                    pass

            return attached, others

        def alphanumeric(title):

            kill_list = '''!()-[];:'"\,<>./?@#$%^&*_~'''
            no_punct = ''

            for s in title:
                if s not in kill_list:
                    no_punct += s
            post_title = no_punct

            if len(post_title) >= 30:
                post_title = ('%.30s' % post_title)
                post_title = post_title.strip()

            return post_title

        def contents_download(attached, dst, post_title, error, title_num, choice):
            img_num = 1
            base_url = 'https://www.twicenest.com'
            base_url = str(base_url)

            for i in range(len(attached)):
                # print(content.attrs['src'][0])
                # if content.attrs['src'][0] == '/':
                #     print('https://twicenest.com' + content.attrs['src'])
                #     attached.append('https://twicenest.com' + content.attrs['src'])
                # else:
                #     attached.append(content.attrs['src'])
                # attached.append(content.attrs['src'])
                if attached[i][0] == '/':
                    img_url = base_url + attached[i]

                else:
                    img_url = attached[i]
                ### 글 하나에 있는 사진/영상 한폴더로 다운로드하기 위해 폴더 만들기###

                if choice == 2:

                    if not os.path.exists(dst + str(title_num) + '_' + str(post_title)):
                        print('폴더 만들기')
                        os.mkdir(dst + str(title_num) + '_' + str(post_title))

                    try:
                        wget.download(img_url, dst + str(title_num) + '_' + str(post_title) + '\\')

                    except:
                        error[post_title] = img_url
                        pass
                    file_list = glob.glob((dst + str(title_num) + '_' + str(post_title) + '\\*'))
                    if len(file_list) != 0:
                        latest_file = max(file_list, key=os.path.getctime)
                        if latest_file.split('.')[-1] == 'wget':
                            os.rename(latest_file,
                                      dst + str(title_num) + '_' + str(post_title) + '\\' + str(img_num) + str(
                                          post_title) + '.' + img_url.split('.')[-1])
                        else:
                            try:
                                os.rename(latest_file,
                                          dst + str(title_num) + '_' + str(post_title) + '\\' + str(img_num) + str(
                                              post_title) + '.' + img_url.split('.')[-1])
                            except:
                                pass

                        # file_name = latest_file.split('\\')[-1]

                        print(post_title + ' 다운 시작' + ' ' + str(img_num))
                        img_num += 1

                if choice == 1:
                    ### 다운 목표 페이지에 있는 모든 첨부 한폴더에 저장
                    try:
                        print(post_title + ' 다운 시작' + ' ' + str(img_num))
                        # print(img_url)
                        wget.download(img_url, dst)
                    except:
                        error[post_title] = img_url
                        pass
                    file_list = glob.glob((dst + '*'))
                    if len(file_list) >= 1:
                        latest_file = max(file_list, key=os.path.getctime)
                        # file_name = latest_file.split('\\')[-1]
                        if latest_file.split('.')[-1] == 'wget':
                            os.rename(latest_file, dst + str(img_num) + str(post_title) + '.' + img_url.split('.')[-1])
                        else:
                            try:
                                os.rename(latest_file,
                                          dst + str(post_title) + '_' +
                                          latest_file.split('.')[-2].split('\\')[-1] + str(img_num) + '.' +
                                          latest_file.split('.')[-1])
                            except:
                                pass

                    img_num += 1

            for key in error:
                error_path = key.strip()
                if not os.path.exists(dst + error_path):
                    os.mkdir(dst + error_path)
                try:
                    wget.download(error[key], dst + error_path)
                except:
                    # print(error[key])
                    pass

            title_num += 1

        # dst = input('다운 받을 경로 어디? : ')
        dst = str(self.down_path.text())

        print('')
        # url = input('다운 받을 페이지 url : ')
        url = str(self.target_url.text())
        session = HTMLSession()
        target_page = session.get(url)

        title = target_page.html.find('meta')[6].attrs['content']
        post_title = alphanumeric(title)

        error = dict()
        attached, others = one_page_contents(target_page)
        # print(attached, others)

        contents_download(attached, dst, post_title, error, 1, 1)
        contents_download(others, dst, post_title, error, 1, 1)
        removeEmptyFolders(dst)

        return attached, others

    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(389, 182)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label_3 = QtWidgets.QLabel(self.centralwidget)
        self.label_3.setGeometry(QtCore.QRect(20, 10, 151, 16))
        self.label_3.setObjectName("label_3")
        self.widget = QtWidgets.QWidget(self.centralwidget)
        self.widget.setGeometry(QtCore.QRect(20, 40, 333, 82))
        self.widget.setObjectName("widget")
        self.gridLayout_2 = QtWidgets.QGridLayout(self.widget)
        self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.label = QtWidgets.QLabel(self.widget)
        self.label.setObjectName("label")
        self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
        self.target_url = QtWidgets.QLineEdit(self.widget)
        self.target_url.setObjectName("target_url")
        self.gridLayout.addWidget(self.target_url, 0, 1, 1, 2)
        self.label_2 = QtWidgets.QLabel(self.widget)
        self.label_2.setObjectName("label_2")
        self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
        self.down_path = QtWidgets.QLineEdit(self.widget)
        self.down_path.setObjectName("down_path")
        self.gridLayout.addWidget(self.down_path, 1, 1, 1, 1)
        self.dir_open = QtWidgets.QPushButton(self.widget)
        self.dir_open.setObjectName("dir_open")
        self.gridLayout.addWidget(self.dir_open, 1, 2, 1, 1)
        self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 1, 1)
        self.start_btn = QtWidgets.QPushButton(self.widget)
        self.start_btn.setObjectName("start_btn")
        self.gridLayout_2.addWidget(self.start_btn, 1, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 389, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label_3.setText(_translate("MainWindow", "둥닷 다운로더 라이트 v1.0"))
        self.label.setText(_translate("MainWindow", "다운 받을 URL : "))
        self.label_2.setText(_translate("MainWindow", "다운 받을 폴더 : "))
        self.dir_open.setText(_translate("MainWindow", "..."))
        self.start_btn.setText(_translate("MainWindow", "GO!"))

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    light_gui = Ui_MainWindow()
    light_gui.show()
    sys.exit(app.exec_())

【问题讨论】:

【参考方案1】:

我遇到了类似的问题。我用 cx_Freeze 而不是 pyinstaller 解决了它。 首先使用 pip 安装 cx_Freeze,因为它不是内置的 python 包。

然后创建一个包含您尝试创建的 GUI 的所有组件(即代码和图像(如果有))的目录。然后在名为“setup.py”的同一目录中创建一个新的 python 文件。然后将我在下面编写的脚本包含到 setup.py 文件中:

import cx_Freeze

executables = [cx_Freeze.Executable(<main python file that drives the GUI with .py extension>, base="Win32GUI")

cx_Freeze.setup(
    name=<name of the file in quotes>,
    options="build_exe": "packages": ["PyQt5", "sys", "requests_html", "os", "wget", "glob"], "include_files": [<list of all the external file names separated by , which is required by the main python script to run along with extensions, eg: images, sound or any other supporting python script>],
    executables = executables
)

用引号中要求的信息替换标有 的文本。如果组件位于其他目录中,请记住使用文件扩展名并提供完整的文件路径。 保存python文件。运行 cmd 并导航到包含 setup.py 和其他组件的目录。从那里输入命令python setup.py build 如果一切都正确完成,那么在成功完成 cmd 功能后,您将在导航到的目录中拥有一个名为“build”的新目录。在该目录中,您会找到包含所有其他组件的 .exe 文件。 编辑:如果您的脚本确实有支持文件,则不要将 .exe 与其他文件分开,否则它将无法工作。

【讨论】:

以上是关于使用 PyQt5 构建并由 pyinstaller 编译的 GUI 无法正常工作 [不重复] [重复]的主要内容,如果未能解决你的问题,请参考以下文章

Pyinstaller 的 PyQT5 QFileDialog 问题

在 macOS 和 Windows 上使用 PyInstaller 编译时,简单的 PyQt5 GUI 看起来像 GTK

PyQt5 在使用 nuitka 构建后变得丑陋

PyQt5快速入门PyQt5扩展

pyinstaller 打包pyqt5 报错

pyinstaller 打包pyqt5 报错