【问题标题】:Import internal exe modules from an external subprocess module (pyinstaller)从外部子进程模块(pyinstaller)导入内部 exe 模块
【发布时间】:2020-05-08 03:38:59
【问题描述】:

我想从外部进程导入内部 exe 模块。我可以从 IDE 中很好地做到这一点,但是当我用 pyinstaller 打包项目然后运行它时,找不到模块。

高级流程如下所示:

从外部userSetup.py 中导入maya_app 会返回错误:ImportError: No modules named maya_app

This post 似乎应该对我有所帮助,但我的设置有一些东西阻止了这个解决方案的工作。或者我做错了。

是否可以将模块从一个 exe 导入另一个 exe?谢谢!

代码:

launch_maya.py

import os
import sys
import subprocess

from PySide2 import QtWidgets


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)

        # GUI
        btn_launch = QtWidgets.QPushButton('launch maya')
        btn_launch.clicked.connect(self.on_launch)

        # Layout
        main_layout = QtWidgets.QHBoxLayout(self)
        main_layout.addWidget(btn_launch)
        self.setLayout(main_layout)

        # Root path exe vs ide
        if getattr(sys, 'frozen', False):
            self.root_path = sys._MEIPASS
        else:
            self.root_path = os.path.join(os.path.dirname(os.path.realpath(__file__)))

    def _set_app_envs(self):

        _envs = os.environ.copy()
        _envs['MAYA_SCRIPT_PATH'] = os.path.join(self.root_path, 'scripts').replace('\\', '/')

        # Python path envs
        _python_path_list = [
            'C:\\Program Files\\Autodesk\\Maya2020\\Python\\Lib\\site-packages',
            'C:\\Program Files\\Autodesk\\Maya2020\\Python\\DLLs',
            os.path.join(self.root_path, 'scripts').replace('\\', '/'),
            os.path.join(self.root_path, 'internal_source', 'maya_app')
        ]

        # PYTHONPATH exe vs ide
        if getattr(sys, 'frozen', False):

            _envs['PYTHONPATH'] = os.pathsep.join(_python_path_list)
            _envs['PYTHONHOME'] = 'C:\\Program Files\\Autodesk\\Maya2020\\bin'

        else:
            _envs['PYTHONPATH'] += os.pathsep + os.pathsep.join(_python_path_list)

        return _envs

    def on_launch(self):

        # Maya file path
        file_path_abs = '{}/scenes/test.mb'.format(self.root_path).replace('\\', '/')
        print(file_path_abs)
        app_exe = r'C:/Program Files/Autodesk/Maya2020/bin/maya.exe'

        _envs = self._set_app_envs()

        if os.path.exists(file_path_abs):
            proc = subprocess.Popen(
                [app_exe, file_path_abs],
                env=_envs,
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                shell=True,
                creationflags=subprocess.CREATE_NEW_PROCESS_GROUP
            )


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = Widget()
    window.resize(400, 400)
    window.show()
    sys.exit(app.exec_())

bundle.spec

# -*- mode: python ; coding: utf-8 -*-
block_cipher = None

added_files = [
         ('./scenes', 'scenes'),
         ('./scripts', 'scripts')
         ]

a = Analysis(['launch_maya.py'],
             pathex=[
             'D:/GitStuff/mb-armada/example_files/exe_bundle',
             'D:/GitStuff/mb-armada/dependencies/Qt.py',
             'D:/GitStuff/mb-armada/venv/Lib/site-packages',
             ],
             binaries=[],
             datas=added_files,
             hiddenimports=['internal_source', 'internal_source.maya_app'],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          [],
          exclude_binaries=True,
          name='bundle',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=True )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               upx_exclude=[],
               name='bundle')

maya_app.py

import os
import sys
import subprocess

from PySide2 import QtWidgets


class MainWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(MainWidget, self).__init__(parent)

        # GUI
        btn_launch = QtWidgets.QPushButton('say hey')
        btn_launch.clicked.connect(self.on_say_hey)

        # Layout
        main_layout = QtWidgets.QHBoxLayout(self)
        main_layout.addWidget(btn_launch)
        self.setLayout(main_layout)
        print('I should be alive')

    def on_say_hey(self):
        print('hey')


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = MainWidget()
    window.resize(100, 100)
    window.show()
    sys.exit(app.exec_())

userSetup.py

import os
import sys
import maya.cmds as mc


print('hey')
def tweak_launch(*args):

    print('Startup sequence running...')
    os.environ['mickey'] = '--------ebae--------'
    print(os.environ['mickey'])

    root_path = os.getenv('_MMM_ROOT_PATH')
    main_app_path = os.path.join(root_path, 'internal_source')

    if not root_path in sys.path:
        sys.path.append(main_app_path)

    from internal_source import maya_app

    w = maya_app.MainWidget()
    w.show()
    print('window should be up')


mc.evalDeferred("tweak_launch()")

【问题讨论】:

  • 如果您可以获得所需模块的磁盘路径,您应该始终能够访问它。但是,您似乎正在尝试将 Maya 包装在另一个 QT 应用程序中,这似乎也会导致其他问题。这里的目标是创建一个 GUI 启动器应用程序吗?还是您想实际远程控制 GUI Maya?
  • @theodox 我想我最终要做的是将maya需要的源代码包含在exe的dist中,这样它就可以访问目录。
  • 另外,我不认为我将 Maya 包装在 Qt 应用程序中,因为我正在使用子进程使用其自己的 maya.exe 启动 Maya - 我认为这会导致内部/外部断开连接.我的目标是创建一个 GUI 启动器应用程序,我已经成功了。启动器应用程序(内部)具有在应用程序的 Maya 版本(外部)中重复使用的模块我希望能够在启动时将它们“发送”到 Maya 应用程序,但它看起来并不存在。

标签: python-3.x pyinstaller maya pyside2


【解决方案1】:

在最高级别上,这似乎只是关于您启动的 Maya 中的路径管理 - 大概启动器应用知道它自己的路径,并且需要告知您想要启动的 Maya 它们的存在。

最可靠和最不容易变魔术的方法就是让您的启动器将它需要与 Maya 共享的任何脚本解绑到一个已知位置——隐藏目录甚至会话特定的临时目录将随着启动器的转速,您获得最新版本的可能性最大。

在启动 Maya 时,您可以通过几种方式传递路径——可能最简单的方法就是使用 site.addsitedir 将它们放在该会话的路径上,而不是依赖于 Maya 的许多其他可能的搜索位置。您可以使用-c mel 标志启动 maya,并在启动时传递 python 命令,因此您可以完全从启动器控制此行为,而不必担心与 userSetup.py 的交互。这些方面的东西会让你的东西对 Maya 可用:

    # unpack the 'payload' of scripts to share to a 
    # known location with something like pkgutil.get_data()
    # https://docs.python.org/3/library/pkgutil.html

    # note the single quotes -- you need them to handle escaping!
    py_cmd = "import site; site.addsitedir('{}'); import my_startup_module"
    python_to_execute = py_cmd.format(path_to_unpacked_modules)

    # again, note annoying escapes
    start_cmd = '\"python(\"{}\");\"'.format(python_to_execute)


    maya_session = subprocess.Popen(['maya.exe', '-c', start_cmd])

如果您的 exe 已将文件解压到 path_to_unpacked_modules,maya 将运行该 mel 命令,该命令将调用 python,将您的模块目录添加为站点包目录,然后导入并运行 my_startup_module(从该位置)。这让您的启动器可以控制启动过程,让您不必单独使用userSetup.py——另外,由于它不涉及环境变量,您不必费力地并排运行不同的会话。

如果您要共享的代码只是一堆模块(没有二进制扩展名),您可以将模块保存在 zip 文件中并将其添加到路径中——python 将能够自动在 zip 文件中查找模块.这样可以节省解压时间,并确保您没有旧运行的剩余 PYC 文件。

这些线程中有很多有用的信息: https://tech-artists.org/t/python-maya-startup-script/2145/17 https://tech-artists.org/t/deploy-tools-for-your-maya-team/5029/13

【讨论】:

  • 我已经将它设置为移动必要的源文件,它运行良好。我想知道,是否可以将代码捆绑为插件并在启动时加载它?是否可以将 python 包转换为 .mll?
  • 还有!是否有可能仅移动 .pyc 文件而不是 .py 文件的过程?
  • 如果您有 pycs,您可以交付它们——只要您在将使用它们的相同 os 和 python 组合上生成它们。查看 py_compile 模块以自动生成它们——它有很好的副作用来验证你的代码是否存在语法错误
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-27
  • 2015-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多