【问题标题】:How to communicate between dm-script and python in Digital Micrograph如何在 Digital Micrograph 中的 dm-script 和 python 之间进行通信
【发布时间】:2020-08-19 14:12:44
【问题描述】:

如何在 dm-script 代码和 python 代码之间进行交流,两者都在 Digital Micrograph 中执行?


在我的具体情况下,我有复杂的对话框。因为python 不支持对话框,但在dm-script 中,我在dm-script 中编写了我的对话框。现在的问题是从对话框传递到python代码。

考虑以下示例:

import DigitalMicrograph as DM

script = """string field_value = "";

class ButtonDialog : UIFrame{
    void field_changed(object self, TagGroup field){
        field_value = field.DLGGetStringValue();
    }
    
    object init(object self){
        TagGroup dlg, dlg_items, field;
        
        dlg = DLGCreateDialog("Type in a number", dlg_items);
        
        dlg.DLGAddElement(DLGCreateLabel("Number:"));
        
        field = DLGCreateIntegerField(0, 10, "field_changed");
        dlg.DLGAddElement(field);
        
        self.super.init(dlg);
        return self;
    }
}

object dialog = alloc(ButtonDialog).init();
dialog.pose();
"""

DM.ExecuteScriptString(script)

# how do I get this?
field_value = ""

【问题讨论】:

  • 有趣的是,您将混合脚本作为 Python-calling-DM-String 执行。我个人更喜欢相反的情况(即让 DM 脚本执行 Python 脚本字符串)。
  • 我在 python 方面的经验比在 dm-script 方面的经验要多。所以我尽量用python做,只有在绝对必要时才去dm-script :)
  • @mile7 当然,这里相同但相反:c)但是在 DM 脚本中解决 GMS 特定的事情比通过 Python+DM 容易得多。实际上,我觉得你想出了如何以这种方式混合对话非常酷。

标签: python synchronization dm-script communicate


【解决方案1】:

DM 脚本调用 Python 脚本

这个答案显示了 Python 和 DM-script 之间的一些通信,但形成了相反的一面。它还使用持久性 tagGroup 作为传输代理。

从 DM 脚本传入和传出 Python 的简单值

// //////////////////////////////////////////////////
// Helper methods 
// Build the DM-String representing the PythonScript
// which is then called
// //////////////////////////////////////////////////
string AddPythonValueIN( number val, string valName, string valType )
{
    string py
    py += "\n#Get value DM->Python via global tags" + "\n"
    py += "success, " + valName + " = DM.GetPersistentTagGroup().GetTagAs" + valType + "('Python_temp:in:" + valName + "')" + "\n"
    py += "if False == success:" + "\n"
    py += "    print( 'Error. No value passed in by DM-script sucessfully.' )" + "\n"
    py += "    exit(0)" + "\n"
    return py
}

string AddPythonValueIN( string val, string valName, string valType )
{
    string py
    py += "\n#Get value DM->Python via global tags" + "\n"
    py += "success, " + valName + " = DM.GetPersistentTagGroup().GetTagAs" + valType + "('Python_temp:in:" + valName + "')" + "\n"
    py += "if False == success:" + "\n"
    py += "    print( 'Error. No value passed in by DM-script sucessfully.' )" + "\n"
    py += "    exit(0)" + "\n"
    return py
}

string AddPythonValueOut( string valName, string valType )
{
    string py
    py += "\n#Get value Python->DM via global tags" + "\n"
    py += "DM.GetPersistentTagGroup().SetTagAs" + valType + "('Python_temp:out:" + valName + "'," + valName + ")" + "\n"
    return py
}

string AddPythonPrintModifyPrint()
{
    string py
    py += "\n#Print InValue, modify, print OutValue" + "\n"
    py += "print('InValue',InValue)"+ "\n"
    py += "OutValue = InValue * 2" + "\n"
    py += "print('OutValue',OutValue)"+ "\n"
    return py
}

// //////////////////////////////////////////////////
// Example calls 
// Demonstrating value passing from DM script to a 
// called Python snippet. TagTypes need to be 
// explicit in Python.
// //////////////////////////////////////////////////
void PythonValueInAndOut_Float()
{
    number value = 13.2
    GetPersistentTagGroup().TagGroupSetTagAsFloat("Python_temp:in:InValue",value)
    
    Result( " DM-Script passed value in: " + value + "\n")
    
    string py = "#Script to demonstrate value passing in Hybrid scripts. DM calling Python." + "\n"
    py += AddPythonValueIN( value, "InValue", "Float" )
    py += AddPythonPrintModifyPrint() 
    py += AddPythonValueOut( "OutValue", "Float" )
    
    Result( "\n PYTHON CODE EXECUTES\n --------------------- \n\n" )
    ExecutePythonScriptString( py, 1 )
    Result( "\n --------------------- \n PYTHON CODE FINISHED \n\n" )
    
    if ( !GetPersistentTagGroup().TagGroupGetTagAsFloat("Python_temp:out:OutValue",value))
        Throw("Python value passed out not found.")
        
    Result( "DM-Script received value: " + value + "\n")
    
    GetPersistentTagGroup().TagGroupDeleteTagWithLabel("Python_temp")
}

void PythonValueInAndOut_Double()
{
    number value = 13.2
    GetPersistentTagGroup().TagGroupSetTagAsDouble("Python_temp:in:InValue",value)
    
    Result( " DM-Script passed value in: " + value + "\n")
    
    string py = "#Script to demonstrate value passing in Hybrid scripts. DM calling Python." + "\n"
    py += AddPythonValueIN( value, "InValue", "Double" )
    py += AddPythonPrintModifyPrint() 
    py += AddPythonValueOut( "OutValue", "Double" )
    
    Result( "\n PYTHON CODE EXECUTES\n --------------------- \n\n" )
    ExecutePythonScriptString( py, 1 )
    Result( "\n --------------------- \n PYTHON CODE FINISHED \n\n" )
    
    if ( !GetPersistentTagGroup().TagGroupGetTagAsDouble("Python_temp:out:OutValue",value))
        Throw("Python value passed out not found.")
        
    Result( "DM-Script received value: " + value + "\n")
    
    GetPersistentTagGroup().TagGroupDeleteTagWithLabel("Python_temp")
}

void PythonValueInAndOut_String()
{
    string value = "hallo"
    GetPersistentTagGroup().TagGroupSetTagAsString("Python_temp:in:InValue",value)
    
    Result( " DM-Script passed value in: " + value + "\n")
    
    string py = "#Script to demonstrate value passing in Hybrid scripts. DM calling Python." + "\n"
    py += AddPythonValueIN( value, "InValue", "String" )
    py += AddPythonPrintModifyPrint() 
    py += AddPythonValueOut( "OutValue", "String" )
    
    Result( "\n PYTHON CODE EXECUTES\n --------------------- \n\n" )
    ExecutePythonScriptString( py, 1 )
    Result( "\n --------------------- \n PYTHON CODE FINISHED \n\n" )
    
    if ( !GetPersistentTagGroup().TagGroupGetTagAsString("Python_temp:out:OutValue",value))
        Throw("Python value passed out not found.")
        
    Result( "DM-Script received value: " + value + "\n")
    
    GetPersistentTagGroup().TagGroupDeleteTagWithLabel("Python_temp")
}

ClearResults()
PythonValueInAndOut_float()
PythonValueInAndOut_double()
PythonValueInAndOut_string()

通过 DM 脚本将图像数据传入和传出 Python

// Images and other objects which are in memory can be most easily passed
// by passing their ID value and then finding them by ID.
// However, this does not work if the object would go out of scope at the 
// end of a script (like an image which is not displayed)
//
// To pass a NumpyArray out of Python without showing an image, one can
// instead write the array to the global tags directly, together with the
// information needed to read the tags out into a DM-image
void PythonImageInOut( )
{
    image img := RealImage("test",4,5,5)
    img = icol
    
    Result( " DM-Script passed image ["+img.ImageGetLabel()+"] in.\n")
        
    string py
    py += "\n#Get image DM->Python via imageLabel" + "\n"
    py += "img = DM.FindImageByID("+ img.ImageGetID()+")" + "\n"
    py += "if None == img:" + "\n"
    py += "    print( 'Error.No image passed in by DM-script sucessfully.' )" + "\n"
    py += "    exit(0)" + "\n"
    py += "\n"
    py += "print('Image from DM: Label = ', img.GetLabel())" + "\n"
    py += "print('Image from DM: Name  = ', img.GetName())" + "\n"
    py += "print('Image from DM: Value = \\n', img.GetNumArray())" + "\n"
    py += "del img #ALWAYS delete DM objects in Python scripts" + "\n"
    py += "\n"
    py += "\n#Create DM image in Python and pass out as ID via global tags" + "\n"
    py += "import numpy as np" + "\n"
    py += "data = np.arange(48).reshape(6,8)" + "\n"
    py += "outImg = DM.CreateImage(data.copy(order='C'))" + "\n"
    py += "DM.GetPersistentTagGroup().SetTagAsLong('Python_temp:out:ID',outImg.GetID())" + "\n"
    py += "print('Create new image with ID:',outImg.GetID())"+"\n"
    py += "outImg.ShowImage()    #Image needs to be shown or it will not stay in memory" + "\n"
    py += "del outImg #ALWAYS delete DM objects in Python scripts" + "\n"
    
    Result( "\n PYTHON CODE EXECUTES\n --------------------- \n\n" )
    ExecutePythonScriptString( py, 1 )
    Result( "\n --------------------- \n PYTHON CODE FINISHED \n\n" )
    
    number imgID
    if ( !GetPersistentTagGroup().TagGroupGetTagAsLong("Python_temp:out:ID",imgID))
        Throw("Python imageID value passed out not found.")
        
    image outImg := FindImageByID(imgID)
    if ( !outImg.ImageIsValid()  )
        Throw("Python imageID value passed out but no matching image found.")
        
        
    Result( "DM-Script received image: " + outImg.ImageGetLabel()+ "\n")
    
    GetPersistentTagGroup().TagGroupDeleteTagWithLabel("Python_temp")
    
}


void PythonArrayOut( )
{
    string py
    py += "#Passing Numpy array values via global tags" + "\n"
    py += "import numpy as np" + "\n"
    py += "data = np.arange(6*8*3).reshape(6,8,3)" + "\n"
    py += "outImg = DM.CreateImage(data.copy(order='C'))" + "\n"
    py += "DM.GetPersistentTagGroup().SetTagAsArray('Python_temp:out:array',outImg)" + "\n"
    py += "DM.GetPersistentTagGroup().SetTagAsLong('Python_temp:out:type',outImg.GetDataType())" + "\n"
    py += "for i in range(data.ndim,0,-1):" + "\n"
    py += "    DM.GetPersistentTagGroup().SetTagAsLong('Python_temp:out:dim:' + str(i-1),data.shape[i-1])" + "\n"   
    py += "\n"
    py += "del outImg #ALWAYS delete DM objects in Python scripts" + "\n"
    
    Result( "\n PYTHON CODE EXECUTES\n --------------------- \n\n" )
    ExecutePythonScriptString( py, 1 )
    Result( "\n --------------------- \n PYTHON CODE FINISHED \n\n" )

    number dataType
    if ( !GetPersistentTagGroup().TagGroupGetTagAsLong("Python_temp:out:type",dataType))
        Throw("Python dataType value passed out not found.")
    
    TagGroup dimTG
    if ( !GetPersistentTagGroup().TagGroupGetTagAsTagGroup("Python_temp:out:dim",dimTG))
        Throw("Python dimensions passed out not found.")
    
    number nDim = dimTG.TagGroupCountTags()
    number n0, n1, n2, n3, n4
    dimTG.TagGroupGetIndexedTagAsLong(0,n0) 
    dimTG.TagGroupGetIndexedTagAsLong(1,n1)
    dimTG.TagGroupGetIndexedTagAsLong(2,n2)
    dimTG.TagGroupGetIndexedTagAsLong(3,n3)
    dimTG.TagGroupGetIndexedTagAsLong(4,n4)
    
    image outImg 
    if ( 1 == nDim  )
        outImg := NewImage("Test", dataType, n0 )
    else if ( 2 == nDim  )
        outImg := NewImage("Test", dataType, n0, n1 )
    else if ( 3 == nDim  )
        outImg := NewImage("Test", dataType, n0, n1, n2 )
    else if ( 4 == nDim  )
        outImg := NewImage("Test", dataType, n0, n1, n2, n3 )
    else if ( 5 == nDim  )
        outImg := NewImage("Test", dataType, n0, n1, n2, n3, n4 )
        
    if ( !GetPersistentTagGroup().TagGroupGetTagAsArray("Python_temp:out:array",outImg))
        Throw("Python array data passed out not found.")
    
    Result("\n DM recreated image from array values: " + outImg.ImageGetLabel()+ "\n")
        
    GetPersistentTagGroup().TagGroupDeleteTagWithLabel("Python_temp")
}

clearresults()
Result("\n\nEXAMPLE Python<->DM image passing\n")
PythonImageInOut()
Result("\n\nEXAMPLE Python-->DM array passing\n")
PythonArrayOut( )

【讨论】:

    【解决方案2】:

    要在dm-scriptpython 在同一实例中运行时同步数据(本例中的线程,也可以针对不同线程进行修改),可以使用持久性标签 .

    dm-script 正在设置持久标签。然后python 可以再次读取持久化标签。


    更新:Python 模块

    由于下面提到的限制以及令人难以置信的增长和不可读的代码,我决定为此编写自己的 python 模块。使用execdmscript,可以非常轻松地从 Digital Micrograph 执行具有变量同步的 dm-script 代码。

    查看以下示例:

    from execdmscript import exec_dmscript
    
    # some script to execute
    dmscript = """
    number input;
    number s = GetNumber("Enter a number between " + min + " and " + max + ".", init, input);"
    """
    
    # variables that will be defined for the dm-scripts (and readable later on in python)
    sv = {"min": 1, "max": 10, "init": 2}
    # variables the dm-script defines and that should be readable in the python file
    rv = {"input": "number", "s": "number"}
    
    with exec_dmscript(dmscript, readvars=rv, setvars=sv) as script:
        if script["s"]:
            print(script["input"])
        else:
            print("User pressed cancel.")
    

    这隐藏了所有与 dm-script 相关的保存和问题(如下所述)。它允许使用listdicts 以及boolstrintfloat 等基本类型。所有类型和值都可以以 Python 方式使用,无需关心 dm-script 类型。 dm 脚本可以移动到一个单独的文件中以进一步清理代码。

    请注意,为了调试,有一个exec_dmscript(debug=True, debug_file="path/to/file") 开关将代码保存到给定的debug_file。然后可以在显示错误的 GMS 中手动执行此文件。

    免责声明:如前所述,我是execdmscript 模块的作者。我仍然认为这是这种情况下的最佳解决方案。事实上这个问题是我写这个模块的原因。


    基本数据类型

    对于stringnumber 等基本数据类型,可以手动创建和添加代码。下面的例子展示了这个想法:

    import DigitalMicrograph as DM
    import time
    
    sync_id = "sync-{}".format(int(time.time() * 100))
    
    dmscript = """
    number input;
    number s = GetNumber("Enter a number between {min} and {max}.", {init}, input);
    
    TagGroup p = GetPersistentTagGroup();
    p.TagGroupSetTagAsBoolean("{id}-success", s);
    p.TagGroupSetTagAsLong("{id}-input", input);
    """
    
    DM.ExecuteScriptString(dmscript.format(min=1, max=10, init=2, id=sync_id))
    
    # cannot save TagGroups to variables, check below
    s, success = DM.GetPersistentTagGroup().GetTagAsBoolean("{}-success".format(sync_id))
    
    if s and success:
        # cannot save TagGroups to variables, check below
        s, input = DM.GetPersistentTagGroup().GetTagAsLong("{}-input".format(sync_id))
        
        if s:
            print(input)
        else:
            print("Did not find 'input' in persistent tags.")
    elif not s:
        print("Did not find 'success' in persistent tags.")
    else:
        print("User clicked cancel.")
    
    # cannot save TagGroups to variables, check below
    # delete tag, otherwise the persistent tags gets filled with garbage
    DM.GetPersistentTagGroup().DeleteTagWithLabel("{}-success".format(sync_id))
    DM.GetPersistentTagGroup().DeleteTagWithLabel("{}-input".format(sync_id))
    

    可以看出这种方法有一些缺点。只需输入一个数字,就有很多代码创建设置和同步。这使代码混乱。也有人可能希望将 dm-script 代码保存在一个单独的文件中,以添加 python 文件打开。在此代码中调试 dm-script 代码也很困难。最后,TagGroup 在 python 端的旅行也有限制(请参阅下文)。


    限制

    更新:下面提到的python模块可以处理TagGrous和TagLists,因为它将它们线性化并使用单独的键和类型保存。

    注意TagGroups 很难同步。 TagGroups 不得分配给变量。一旦它们存在,它们就不再可用了。这可以通过以下非常简单的代码来说明:

    import DigitalMicrograph as DM
    
    # create group for the example
    group1 = DM.NewTagGroup()
    group1.SetTagAsString("test-string", "test content")
    print("Group 1 valid:", group1.IsValid(), "(type:", type(group1), ")")
    
    # save to persistent tags
    DM.GetPersistentTagGroup().SetTagAsTagGroup("test", group1)
    
    # get the group without assigning to a variable, works
    s, group2 = DM.GetPersistentTagGroup().GetTagAsTagGroup("test")
    print("Group 2 success:", s, ", valid:", group2.IsValid(), "(type:", type(group2), ")")
    
    # assign one parent group to a variable, doesn't work
    tags = DM.GetPersistentTagGroup()
    s, group3 = tags.GetTagAsTagGroup("test")
    print("Group 3 success:", s, ", valid:", group3.IsValid(), "(type:", type(group3), ")")
    

    这段代码将产生输出

    Group 1 valid: True (type: <class 'DigitalMicrograph.Py_TagGroup'> )
    Group 2 success: True , valid: True (type: <class 'DigitalMicrograph.Py_TagGroup'> )
    Group 3 success: True , valid: False (type: <class 'DigitalMicrograph.Py_TagGroup'> )
    

    这表明您不能将TagGroups 分配给python 变量。


    异步想法

    这可以扩展为异步应用程序。 dm-scriptpython 实现可以设置观察持久标签的线程。 (请注意,您可以使用TagGroupMarkAsChanged()TagGroups 标记为已修改),最好使用不同的标签。然后两者都可以向标签添加命令和/或数据。另一个“实例”可以读取和处理它们。

    这必须手动编码。这(当前)不包含在 execdmscript 模块中。

    【讨论】:

    • 是的,按标签是这样做的。或者,可以通过磁盘上的文件和(脚本)对象停留在范围内(并通过它的 ID 获取)或最前面图像的标签。但是使用持久标签通常是最简单的。只要确保清理这些标签。
    猜你喜欢
    • 1970-01-01
    • 2019-02-04
    • 1970-01-01
    • 2012-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多