首页 > 解决方案 > PyInstaller 生成的 PyQT5 应用程序的可执行文件显示“QCoreApplication::exec: 事件循环已在运行”

问题描述

我创建了一个基于 PyQt5 的应用程序,它将所有 3D 对象(*.stl 文件)从输入目录加载到 QListWidget 中。用户可以从此列表中选择项目并将其移动到另一个他想要执行任何操作(过滤)的 QListWidget。

我已经使用 vtk 渲染器来显示选定的对象。我还提供了一个小部件来输入*.mlx从 meshlab 创建的过滤器 () 文件。基于所有这些输入,当用户单击“应用过滤器”按钮时,我的代码将 meshlab 过滤器应用于所选对象,并在单独的 QListWidget 中显示过滤操作后的对象。

UI 看起来像这样:

在此处输入图像描述

当我从 spyder 或命令提示符运行它时,该应用程序按预期工作。接下来,我使用 PyInstaller 生成了一个应用程序。当我打开应用程序时,它会毫无问题地打开,并且我能够加载对象、将对象添加到 QListWidget 以及所有其他操作。但是当我单击“应用过滤器”按钮时,应用程序停止并给出以下错误“QCoreApplication::exec: 事件循环已在运行”。

我不确定这可能是什么问题,因为该应用程序在 IDE 中正常工作。以下是代码(抱歉代码不干净)

import sys
import os
import vtk
from PyQt5 import Qt, QtCore, QtWidgets
from PyQt5.QtWidgets import QPushButton, QLabel, QLineEdit, QListWidget
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
from PyQt5.QtCore import pyqtSlot
import meshlabxml as mlx
from pathlib import Path

class MainWindow(Qt.QMainWindow):
    def __init__(self, parent = None):
        Qt.QMainWindow.__init__(self, parent)
        cwd = os.getcwd()
        self.file_list = []
        self.selected_list = []
        self.source_dir = os.path.join(cwd, 'inputs')
        self.out_dir = os.path.join(cwd, 'outputs')
        self.meshlab_dir = Path(r"C:\Program Files\VCG\MeshLab")
        self.filter_dir = os.path.join(cwd, 'filter.mlx')
        self.cur_filename = os.path.join(cwd, 'sample.stl')
        self.initUI()
        
        
    def initUI(self):
        self.frame = Qt.QFrame()
        self.setWindowState(QtCore.Qt.WindowMaximized)
        self.setWindowTitle("Mesh Processor")
        
        lbl_inpdir = QLabel('STL input directory')
        self.edt_inpdir = QLineEdit()
        btn_load = QPushButton('Load')
        self.lbl_load = QLabel('')
        btn_add = QPushButton('>>')
        btn_remove = QPushButton('<<')
        self.load_lst = QListWidget()
        self.select_lst = QListWidget()
        
        lbl_outdir = QLabel('STL output directory')
        self.edt_outdir = QLineEdit()
        lbl_meshlab = QLabel('Meshlab Server Path')
        self.edt_meshlab = QLineEdit()
        lbl_filter = QLabel('Filter (MLX)')
        self.edt_filter = QLineEdit()
        btn_filter = QPushButton('Apply Filter')
        self.upsampled_lst = QListWidget()
        self.edt_inpdir.setText(str(self.source_dir))
        self.edt_outdir.setText(str(self.out_dir))
        self.edt_meshlab.setText(str(self.meshlab_dir))
        self.edt_filter.setText(str(self.filter_dir))
        
        btn_load.clicked.connect(self.on_load)
        btn_add.clicked.connect(self.on_add)
        btn_remove.clicked.connect(self.on_remove)
        btn_filter.clicked.connect(self.on_applyfilter)
        self.load_lst.itemClicked.connect(self.on_loadlst)
        self.select_lst.itemClicked.connect(self.on_selectlst)
        self.upsampled_lst.itemClicked.connect(self.on_upsampledlst)
        
        layout_final = Qt.QHBoxLayout()
        layout_left = Qt.QVBoxLayout()
        layout_left_bottom = Qt.QVBoxLayout()
        
        layout_inp = Qt.QHBoxLayout()
        layout_inp.addWidget(lbl_inpdir)
        layout_inp.addWidget(self.edt_inpdir)
        
        layout_loadinp = Qt.QHBoxLayout()
        layout_loadinp.addWidget(btn_load)
        layout_loadinp.addWidget(self.lbl_load)
        
        layout_btns = Qt.QVBoxLayout()
        layout_btns.addWidget(btn_add)
        layout_btns.addWidget(btn_remove)
        
        layout_lstbox = Qt.QHBoxLayout()
        layout_lstbox.addWidget(self.load_lst)
        layout_lstbox.addLayout(layout_btns)
        layout_lstbox.addWidget(self.select_lst)
        
        layout_out = Qt.QHBoxLayout()
        layout_out.addWidget(lbl_outdir)
        layout_out.addWidget(self.edt_outdir)
        
        layout_meshlab = Qt.QHBoxLayout()
        layout_meshlab.addWidget(lbl_meshlab)
        layout_meshlab.addWidget(self.edt_meshlab)
        
        layout_filter = Qt.QHBoxLayout()
        layout_filter.addWidget(lbl_filter)
        layout_filter.addWidget(self.edt_filter)
        
        layout_left_bottom.addLayout(layout_out)
        layout_left_bottom.addLayout(layout_meshlab)
        layout_left_bottom.addLayout(layout_filter)
        layout_left_bottom.addWidget(btn_filter)
        layout_left_bottom.addWidget(self.upsampled_lst)
        
        
        layout_left.addLayout(layout_inp)
        layout_left.addLayout(layout_loadinp)
        layout_left.addLayout(layout_lstbox)
        layout_left.addLayout(layout_left_bottom)
        #layout_left.addWidget(self.txtedit_lst)
        
        
        layout_right = Qt.QVBoxLayout()
        self.lbl_filename = QLabel(self.cur_filename)
        vtkWidget = QVTKRenderWindowInteractor(self.frame)
        layout_right.addWidget(self.lbl_filename)
        layout_right.addWidget(vtkWidget)
        
        
        layout_final.addLayout(layout_left)
        layout_final.addLayout(layout_right)
        
        
        self.ren = vtk.vtkRenderer()
        vtkWidget.GetRenderWindow().AddRenderer(self.ren)
        
        self.iren = vtkWidget.GetRenderWindow().GetInteractor()
        filename2 = "sample.stl"
        self.reader = vtk.vtkSTLReader()
        self.reader.SetFileName(filename2)
        self.mapper = vtk.vtkPolyDataMapper()
        self.mapper.SetInputConnection(self.reader.GetOutputPort())

        # Create an actor
        self.actor = vtk.vtkActor()
        self.actor.SetMapper(self.mapper)

        self.ren.AddActor(self.actor)

        self.ren.ResetCamera()

        self.frame.setLayout(layout_final)
        self.setCentralWidget(self.frame)

        self.show()
        self.iren.Initialize()
        self.iren.Start()
        
    @pyqtSlot()
    def on_load(self):
        self.load_lst.clear()
        self.select_lst.clear()
        self.source_dir = Path(self.edt_inpdir.text())
        
        for root, dirs, files in os.walk(self.source_dir):
            for file in files:
                if file.endswith(".stl"):
                    self.file_list.append(os.path.join(root, file))
                    
        self.lbl_load.setText(str(len(self.file_list))+ ' files loaded from the source directory')
        self.load_lst.addItems(self.file_list)
        self.load_lst.setCurrentRow(0)
        self.cur_filename = self.file_list[0]
        
        self.lbl_filename.setText(self.cur_filename)
        self.reader.SetFileName(self.cur_filename)
        self.mapper = vtk.vtkPolyDataMapper()
        self.mapper.SetInputConnection(self.reader.GetOutputPort())

        # Create an actor
        self.actor = vtk.vtkActor()
        self.actor.SetMapper(self.mapper)

        self.ren.AddActor(self.actor)

        self.ren.ResetCamera()
        self.setCentralWidget(self.frame)

        self.show()
        self.iren.Initialize()
        self.iren.Start()
        
        
    @pyqtSlot()
    def on_add(self):
        self.select_lst.addItem(self.load_lst.currentItem().text())
        self.file_list.remove(self.load_lst.currentItem().text())
        self.selected_list.append(self.load_lst.currentItem().text())
        self.load_lst.takeItem(self.load_lst.currentRow())
        
        
    @pyqtSlot()
    def on_remove(self):
        self.load_lst.addItem(self.select_lst.currentItem().text())
        self.file_list.append(self.select_lst.currentItem().text())
        self.selected_list.remove(self.select_lst.currentItem().text())
        self.select_lst.takeItem(self.select_lst.currentRow())
    
    @pyqtSlot()    
    def on_loadlst(self):
        #print(self.load_lst.currentItem().text())
        self.cur_filename = self.load_lst.currentItem().text()
        self.lbl_filename.setText(self.cur_filename)
        self.reader.SetFileName(self.cur_filename)
        self.mapper = vtk.vtkPolyDataMapper()
        self.mapper.SetInputConnection(self.reader.GetOutputPort())

        # Create an actor
        self.actor = vtk.vtkActor()
        self.actor.SetMapper(self.mapper)

        self.ren.AddActor(self.actor)

        self.ren.ResetCamera()
        self.setCentralWidget(self.frame)

        self.show()
        self.iren.Initialize()
        self.iren.Start()
        
    @pyqtSlot()    
    def on_selectlst(self):
        #print(self.select_lst.currentItem())
        self.cur_filename = self.select_lst.currentItem().text()
        self.lbl_filename.setText(self.cur_filename)
        self.reader.SetFileName(self.cur_filename)
        self.mapper = vtk.vtkPolyDataMapper()
        self.mapper.SetInputConnection(self.reader.GetOutputPort())

        # Create an actor
        self.actor = vtk.vtkActor()
        self.actor.SetMapper(self.mapper)

        self.ren.AddActor(self.actor)

        self.ren.ResetCamera()
        self.setCentralWidget(self.frame)

        self.show()
        self.iren.Initialize()
        self.iren.Start()
        
    @pyqtSlot()    
    def on_applyfilter(self):
        self.outdir = self.edt_outdir.text()
        self.filter_dir = self.edt_filter.text()
        self.meshlab_dir = self.edt_meshlab.text()
        
        for item in self.selected_list:
            #apply_filter(self.meshlab_dir, self.filter_dir, item, self.outdir)
            MESHLABSERVER_PATH = self.meshlab_dir
            os.environ['PATH'] += os.pathsep + MESHLABSERVER_PATH
            script = Path(self.filter_dir)
            input_file = Path(item)
            input_path, filename = os.path.split(input_file)
            output_path = Path(self.outdir)
            output_file = output_path / filename
            log_file = 'log.txt'
            mlx.run(script = script, log=log_file, file_in=input_file, file_out=output_file)
            
            
            input_file = Path(item)
            input_path, filename = os.path.split(input_file)
            output_path = Path(self.outdir)
            output_file = output_path / filename
            self.upsampled_lst.addItem(str(output_file))

    @pyqtSlot()    
    def on_upsampledlst(self):
        #print(self.load_lst.currentItem().text())
        self.cur_filename = self.upsampled_lst.currentItem().text()
        self.lbl_filename.setText(self.cur_filename)
        self.reader.SetFileName(self.cur_filename)
        self.mapper = vtk.vtkPolyDataMapper()
        self.mapper.SetInputConnection(self.reader.GetOutputPort())

        # Create an actor
        self.actor = vtk.vtkActor()
        self.actor.SetMapper(self.mapper)

        self.ren.AddActor(self.actor)

        self.ren.ResetCamera()
        self.setCentralWidget(self.frame)

        self.show()
        self.iren.Initialize()
        self.iren.Start()


if __name__ == '__main__':
    if not QtWidgets.QApplication.instance():
        app = QtWidgets.QApplication(sys.argv)
    else:
        app = QtWidgets.QApplication.instance()
    app.setStyle('Fusion')
    main = MainWindow()
    main.show()
    app.exec_()

***更新:过滤文件和示例输入可以从http://www.filedropper.com/inputs下载***

***更新:我能够使用这些解决它:

  1. 使用子进程执行 meshlab 过滤器,而不是使用 meshlabxml 库。
  2. 使用 cx freeze 生成可执行文件***

标签: pythonpyqtpyqt5pyinstallermeshlab

解决方案


推荐阅读