我创建了一个使用pythonnet的python脚本。该脚本位于名为main.py
的文件中。当我从命令行运行脚本时(只需在Windows命令提示符下键入main.py
),导入的pythonnet模块clr
正常工作。但是当我尝试构建一个exe时,我得到一个错误:No module named clr
。
为了找出原因,我已经验证使用py2exe构建可执行文件(在我的情况下是一个简单的Tkinter应用程序)。我只安装了Python 3.4,并确认where python
指向C:\Python34\python.exe
。
错误发生在可执行构建时,似乎是通过在clr
{"includes":["sip","clr"]}}
中setup.py
部分py2exe
中包含Traceback (most recent call last):
File "setup.py", line 32, in <module>
windows = [{'script': "main.py"}],
File "C:\Python34\lib\distutils\core.py", line 148, in setup
dist.run_commands()
File "C:\Python34\lib\distutils\dist.py", line 917, in run_commands
self.run_command(cmd)
File "C:\Python34\lib\distutils\dist.py", line 936, in run_command
cmd_obj.run()
File "C:\Python34\lib\site-packages\py2exe\distutils_buildexe.py", line 188, i
n run
self._run()
File "C:\Python34\lib\site-packages\py2exe\distutils_buildexe.py", line 267, i
n _run
builder.analyze()
File "C:\Python34\lib\site-packages\py2exe\runtime.py", line 164, in analyze
mf.import_hook(modname)
File "C:\Python34\lib\site-packages\py2exe\mf3.py", line 120, in import_hook
module = self._gcd_import(name)
File "C:\Python34\lib\site-packages\py2exe\mf3.py", line 273, in _gcd_import
raise ImportError('No module named {!r}'.format(name), name=name)
ImportError: No module named 'clr'
来触发的。完整的追溯如下:
https://docs.python.org/2/distutils/setupscript.html
https://pythonhosted.org/setuptools/setuptools.html
http://sourceforge.net/p/py2exe/mailman/message/6937658
我也阅读/试过这些:
clr.pyd
引导我将Python.Runtime.dll
和main.py
移动到各个位置,包括C:\Python34\Lib\site-packages
,C:\Python34\Lib\site-packages\py2exe
(原来的位置)和py2exe
这些都没有奏效,我不知道下一步该尝试什么。我可以看到clr.pyd
因某些原因无法找到Python.Runtime.dll
或main.py
或两者,但无法理解原因。有没有人有任何想法?
我的import clr
clr.AddReference("name.xxxx")
from name.xxxx import aaa
from clr import System
# All my functioning code, that I've verified works when run from the command line
脚本如下所示:
setup.py
这是我的from distutils.core import setup
import py2exe, sys, os
mydata_files = []
for files in os.listdir('C:\\d\\Project\\TOOLS\\data_acquisition\\trunk\\DLL'):
f1 = 'C:\\d\\Project\\TOOLS\\data_acquisition\\trunk\\DLL\\' + files
if os.path.isfile(f1): # skip directories
f2 = '.', [f1]
mydata_files.append(f2)
setup(
data_files=mydata_files,
# options = {"py2exe" : {"includes" : "module1,module2,module3"}}
options = {"py2exe": {"includes":["sip", "clr"]}},
# options = {'py2exe': {'bundle_files': 1 , 'compressed': True,"includes":["sip"]}},
#python setup.py py2exe
#CLR.dll and PythonRuntime.dll
# options = {'py2exe': {'bundle_files': 1, "skip_archive":1 ,"includes":["sip"]}},
windows = [{'script': "main.py"}],
# data_files=mydata_files,
# zipfile = None
)
文件所包含的内容(我留下了一些评论,以便您可以看到我尝试过的内容):
options = {"py2exe": {"includes":["sip", "clr"]}},
如果我将行options = {"py2exe": {"includes":["sip"]}},
更改为.exe
,则构建 package com.example.bilal.aaa;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity {
EditText t,t2;
TextView t3;
Button b1,b2,b3,b4,b6;
@Override
protected void onCreate(Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
// setContentView(R.layout.home);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
t = (EditText)findViewById(R.id.edittext);
t2 = (EditText)findViewById(R.id.edittext2);
t3 = (TextView)findViewById(R.id.edittext3);
Button b1 = (Button) findViewById(R.id.button1);
b1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
create(v);
}
});
Button b6 = (Button) findViewById(R.id.button6);
Button b2 = (Button) findViewById(R.id.button2);
b2.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
create(v);
}
});
Button b3 = (Button) findViewById(R.id.button3);
b3.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
create(v);
}
});
Button b4 = (Button) findViewById(R.id.button4);
b4.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
create(v);
}
});
try{
b1.setOnClickListener((View.OnClickListener) this);
b2.setOnClickListener((View.OnClickListener) this);
b3.setOnClickListener((View.OnClickListener) this);
b4.setOnClickListener((View.OnClickListener) this);
b6.setOnClickListener((View.OnClickListener)this);
}
catch(Exception e){
Toast.makeText(getApplicationContext(), "Please Add the Number", Toast.LENGTH_LONG).show();
}
}
String a,b;
Double A1,B1;
Double result;
int AA1,BB1;
public void create(View v) {
switch (v.getId()) {
case R.id.button1:
a = t.getText().toString();
b=t2.getText().toString();
A1=Double.parseDouble(a);
B1=Double.parseDouble(b);
if (a.equals(null)) {
Toast.makeText(getApplicationContext(), "Enter First Number", Toast.LENGTH_LONG).show();
// create(v);
} else if (b.equals(null)) {
Toast.makeText(getApplicationContext(), "Enter Second Number", Toast.LENGTH_LONG).show();
// create(v);
} else if (A1.equals(null)) {
Toast.makeText(getApplicationContext(), "Enter First Number", Toast.LENGTH_LONG).show();
// create(v);
} else if (B1.equals(null)) {
Toast.makeText(getApplicationContext(), "Enter Second Number", Toast.LENGTH_LONG).show();
// create(v);
} else
{
result=A1/B1;
answer(v);
}
// Log.e("EXCEPTION", a);
// Log.e("EXCEPTION",b);}
// else{
//
//
// Toast.makeText(getBaseContext(), "value"+A1, Toast.LENGTH_LONG).show();
// Toast.makeText(getBaseContext(), "value"+B1, Toast.LENGTH_LONG).show();
// result=A1/B1;
// answer(v);
// Log.e("EXCEPTION","A is Null");
// }
// if(a.equals(null)){
// Log.e("EXCEPTION",a);
// Log.e("EXCEPTION",b);
// Toast.makeText(getApplicationContext(), "Enter First Number", Toast.LENGTH_LONG).show();
// create(v);
//
//
// }
// else{
// if(b.equals(null)){
// Log.e("EXCEPTION","A is Null");
// Toast.makeText(getApplicationContext(), "Enter Second Number", Toast.LENGTH_LONG).show();
// create(v);
//
// }
// else {
//// A1=Double.parseDouble(a);
//// B1=Double.parseDouble(b);
//
// Toast.makeText(getBaseContext(), "value"+A1, Toast.LENGTH_LONG).show();
// Toast.makeText(getBaseContext(), "value"+B1, Toast.LENGTH_LONG).show();
// result=A1/B1;
// answer(v);
//
// }
//
// }
break;
case R.id.button2:
a = t.getText().toString();
b=t2.getText().toString();
A1=Double.parseDouble(a);
B1=Double.parseDouble(b);
if(a!=null){
if(b!=null){
result=A1*B1;
answer(v);
}
else {
Toast.makeText(getApplicationContext(), "Enter Second Number", Toast.LENGTH_LONG).show();
}
}
else {
Toast.makeText(getApplicationContext(), "Enter First Number", Toast.LENGTH_LONG).show();
}
{
Toast.makeText(getApplicationContext(), "Enter First Number", Toast.LENGTH_LONG).show();
}
break;
case R.id.button3:
a = t.getText().toString();
b=t2.getText().toString();
A1=Double.parseDouble(a);
B1=Double.parseDouble(b);
if(A1 != 0){
if(B1 != 0){
result=A1+B1;
answer(v);
}
else {
Toast.makeText(getApplicationContext(), "Enter Second Number", Toast.LENGTH_LONG).show();
}
}
else
{
Toast.makeText(getApplicationContext(), "Enter First Number", Toast.LENGTH_LONG).show();
}
break;
case R.id.button4:
a = t.getText().toString();
b=t2.getText().toString();
A1=Double.parseDouble(a);
B1=Double.parseDouble(b);
if(A1 != 0){
if(B1 != 0){
result=A1-B1;
answer(v);
}
else {
Toast.makeText(getApplicationContext(), "Enter Second Number", Toast.LENGTH_LONG).show();
}
}
else
{
Toast.makeText(getApplicationContext(), "Enter First Number", Toast.LENGTH_LONG).show();
}
break;
default: Toast.makeText(this, "INVALID OPTION", Toast.LENGTH_SHORT).show();
break;
}
}
public void clear(View v)
{
t.setText("");
t2.setText("");
}
public void answer(View v)
{
String tmpstring = Double.toString(result);
t3.setText(tmpstring);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
,但显然无法正常运行。
答案 0 :(得分:4)
作为参考,我使用py2exe
执行了pip install py2exe
的标准安装。这会将py2exe
放入Lib\site-packages
以进行python安装。接下来,我通过从Christoph Gohlke .whl
下载unofficial Windows binaries page,然后使用pythonnet
安装pip install path\to\pythonnet-2.0.0<version_numbers>.whl
。这会将clr.pyd
和Python.Runtime.dll
放入Lib\site-packages
进行python安装。 This question and answers还有更多信息。
py2exe
这是一个很难调试的奇怪行为。我认为这纯粹是该工具中的一个错误。此外,错误消息没有帮助。
问题是该工具通过其hooks.py
文件明确排除了模块clr
。目前尚不清楚为什么。您可以看到line that does this exclusion here。
解决方法是从py2exe安装中的clr
变量windows_excludes
文件中删除单词hooks.py
。假设一切都在标准位置 - 这意味着删除位于hooks.py
的文件C:\Python34\Lib\site-packages\py2exe
中的第23行。您还需要确保Python.Runtime.dll
与.exe
打包在一起 - 我通过将其添加到数据文件中进行了测试。以下是我测试和工作的示例 - 我使用了一个非常简单的main.py
来说明导入,并向自己保证程序实际上正在运行。我让你的setup.py尽可能接近你的版本,注释掉那些不适合我系统的行
实际制作.exe
- 使用以下内容(如果python是您的Python 3安装的别名,则可能不需要python.exe的路径)
C:\python34\python.exe setup.py py2exe
import clr
# I import clr, but don't use it as this is not my
# expertise. The fact it imports without error means
# I'm pretty sure it will work
with open('out.txt','a') as f:
for i in range(30):
f.write(str(i))
from distutils.core import setup
import py2exe, sys, os
mydata_files = []
# I had to comment these out as they did not apply to my test environment
# for files in os.listdir('C:\\d\\Project\\TOOLS\\data_acquisition\\trunk\\DLL'):
# f1 = 'C:\\d\\Project\\TOOLS\\data_acquisition\\trunk\\DLL' + files
# if os.path.isfile(f1): # skip directories
# f2 = 'dll', [f1]
# mydata_files.append(f2)
# It's essential that the Python.Runtime.dll is packaged with main.exe
# This is how I've done it
mydata_files.append(('.',['C:\\Python34\\Lib\\site-packages\\Python.Runtime.dll']))
setup(
data_files=mydata_files,
# I've left all your commented lines in - they weren't necessary for my test
# options = {"py2exe" : {"includes" : "module1,module2,module3"}}
# I haven't included sip as I don't have it installed, but I think it will work
options = {"py2exe": {"includes":["clr"]}},
# options = {'py2exe': {'bundle_files': 1 , 'compressed': True,"includes":["sip"]}},
#python setup.py py2exe
#CLR.dll and PythonRuntime.dll
# options = {'py2exe': {'bundle_files': 1, "skip_archive":1 ,"includes":["sip"]}},
windows = [{'script': "main.py"}],
# data_files=mydata_files,
# zipfile = None
)
编辑: 对于任何感兴趣的人 - 我在a chat conversation中详细介绍了我是如何隔离并发现此错误的。