Select to view content in your preferred language

how to pass string or array from .pyt to my dockpane (xaml.cs)

1115
11
10-25-2023 07:39 AM
nadja
by
Frequent Contributor

it seems like a basic question, but even with the documentation and other posts I cannot get it to work properly. I've got a Python-Toolbox in which I establish the connection to a ArcSDE using a connection file. now I would like to list the content of the ArcSDE in my dockpane (because it looks prettier). I can list the content of my dockpane in my Python-Toolbox and display them. It seems that it's also correct how I pass them back (at least there is no error message). But I don't see how I can access the returned strings and arrays. How do I pass my ArcSDE content as string or array (or anything else) to my dockpane?

here is what I've got in my Python Toolbox:

import arcpy
from tkinter import messagebox
from osgeo import gdal
import os
import ast

class Toolbox(object):
    def __init__(self):
        """Define the toolbox (the name of the toolbox is the name of the
        .pyt file)."""
        self.label = "Toolbox"
        self.alias = "toolbox"

        # List of tool classes associated with this toolbox
        self.tools = [ArcSDEContent]
        
def read_sde():
    if os.path.exists(os.environ['AppDAta']+"/Esri/ArcGISPro/Favorites/xxx.sde"):
      sde_connfile = os.environ['AppDAta']+"/Esri/ArcGISPro/Favorites/xxx.sde"
        conn = arcpy.ArcSDESQLExecute(sde_connfile)
        sql = "select t.name as mytype,i.name as myname,  \
            CASE \
            WHEN has_table_privilege(user,i.name,'SELECT') THEN 1 \
            ELSE 0 \
            END AS mypriv \
            FROM GDB_itemtypes t \
            right join GDB_items i \
            ON i.type = t.uuid \
            where t.name in ( \
            'Tin', \
            'Geometric Network', \
            'Survey Dataset', \
            'Schematic Dataset', \
            'Table', \
            'Feature Class', \
            'Raster Dataset', \
            'Raster Catalog', \
            'Network Dataset', \
            'Terrain', \
            'Parcel Fabric', \
            'Mosaic Dataset', \
            'Utility Network', \
            'Parcel Dataset' \
            );"
        try:
            sql_return = conn.execute(sql)
                              
            layer_files = []
            layer_schemas = []
            for i in sql_return:
              if i[2] == 0:
                continue
              layer_type = i[0]
              layer_fullname = sde_connfile + '/' + i[1]
              layer_name = i[1].split('.')[1] + '.' + i[1].split('.')[2]
              layer_shortname = i[1].split('.')[2]
              layer_schema = i[1].split('.')[1]
              if layer_schema not in layer_schemas:
                layer_schemas.append(layer_schema)
           layer_files.append([layer_name,layer_shortname,layer_schema,layer_fullname,layer_type])
            layer_schemas.sort() 
            layer_files.sort()
            return layer_files, layer_schemas  
                    
        except Exception as err:
            sql_return = False
      
    else:
      messagebox.showinfo(title="test 2", message="sde file does not exist")
       

class ArcSDEContent(object):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "ArcSDE Content"
        self.description = ""
        
    def getParameterInfo(self):
        """Define parameter definitions"""
        params = []
        params.append({
            'name': 'layer_files',
            'displayName': 'Layer Files',
            'datatype': 'String',
            'parameterType': 'Derived',
            'direction': 'Output'
        })
        params.append({
            'name': 'layer_schemas',
            'displayName': 'Layer Schemas',
            'datatype': 'String',
            'parameterType': 'Derived',
            'direction': 'Output'
        })
        return params

    def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
      layer_files, layer_schemas = read_sde()

        # Set the output parameters with the results
      parameters[0].value = ";".join(layer_files)  # Join the list to a string
      parameters[1].value = ";".join(layer_schemas) 
 

and in my dockpane.xaml.cs I've got:

        private async void ReadArcSDE(object sender, RoutedEventArgs e)
        {
            string installPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
            string toolboxPath = System.IO.Path.Combine(installPath, "MyPythonToolbox.pyt\\ArcSDEContent");
            IGPResult result =  await Geoprocessing.ExecuteToolAsync(toolboxPath, null, null, null, null, GPExecuteToolFlags.InheritGPOptions);
        }

 

thanks for any hint

0 Kudos
11 Replies
MatthewDriscoll
MVP Alum

At the end of your dockpane.xaml.cs add

return result;

0 Kudos
nadja
by
Frequent Contributor

thanks @MatthewDriscoll for the hint. Apparantly I didn't phrase my problem correctly, because that's not what I meant. I mean how can I now access the content of the result? I mean how can I extract layer_files  layer_schemas? (for example the layer_name that is on the second position in each layer_file?) every method I try returns nonsens or void. my best try was with 

IEnumerable<Tuple<string, string, string, bool>> parameters = result.Parameters;

but I just cant figure out how to access my result values. 

0 Kudos
GKmieliauskas
Esri Regular Contributor

Hi,

IGPResult has property Values.

It contains output values or null if tool execution fails. It's list of strings

swiss_parks_network_nbernhard
Occasional Contributor

I'm sorry, but I'm still stuck... I tried to make my example as easy as possible, but I don't get it to work at all...

in my python toolbox:

class ArcSDEContent(object):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "ArcSDE Content"
        self.description = ""
        
    def getParameterInfo(self):
        """Define parameter definitions"""
        params = []
        params.append({
            'name': 'layer_files',
            'displayName': 'Layer Files',
            'datatype': ['String'],
            'parameterType': 'Derived',
            'direction': 'Output'
        })
        return params

    def isLicensed(self):
        return True

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):
       #read_sde()
       #return
       try:
         
         #  parameters.append("test1")
         arcpy.SetParameterAsText(0, "Your output value")
         return 
       except Exception as e:
          arcpy.AddError(f"An error occurred: {str(e)}")
       

 

and in my Doxpane.xaml.cs

private async void ReadArcSDE(object sender, RoutedEventArgs e)
{
    string installPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
    string toolboxPath = System.IO.Path.Combine(installPath, "test.pyt\\ArcSDEContent");
    IGPResult result = await Geoprocessing.ExecuteToolAsync(toolboxPath, null, null, null, null, GPExecuteToolFlags.InheritGPOptions);

    string value = result.ReturnValue;
    ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show(value, "return value");

   /* var outputParameterValue = result.Parameters;
    foreach (Tuple<string, string, string, bool> param in outputParameterValue)
    {
        string i1 = param.Item1;
        string i2 = param.Item2;
        string i3 = param.Item3;
        bool i4 = param.Item4;
        ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show("parameters", "item 1:" + i1);
    }*/

      values = result.Values;
      foreach (string val in values)
      {
          ArcGIS.Desktop.Framework.Dialogs.MessageBox.Show(val ,"values val");
      }
}

how can I access my value from my python script? it shouldn't be that hard...

0 Kudos
MatthewDriscoll
MVP Alum

@GKmieliauskas is correct you need to use the IGPResult.  Below is how I access python results in the cs.

 

        public async Task<IGPResult> ExecuteModel()
        {

            var progDlg = new ProgressDialog("Running Geoprocessing Tool", "Cancel");
            progDlg.Show();

            var progsrc=new CancelableProgressorSource(progDlg);


            var pathPython = System.IO.Path.GetDirectoryName((new System.Uri(Assembly.GetExecutingAssembly().CodeBase)).AbsolutePath);
            pathPython = Uri.UnescapeDataString(pathPython);
            System.Diagnostics.Debug.WriteLine(pathPython);


            var tool_path = System.IO.Path.Combine(pathPython, @"YourPythonScript.pyt\Tool");

            var parameters = Geoprocessing.MakeValueArray();

            IGPResult gp_result = await Geoprocessing.ExecuteToolAsync(tool_path, parameters, null, new CancelableProgressorSource(progDlg).Progressor);


            return gp_result;

        }

 

0 Kudos
swiss_parks_network_nbernhard
Occasional Contributor

thanks @MatthewDriscoll  for your reply. I don't see why I would need to return the gp_result from my method. I need the result in the same method later on - imagine you do sth with gp_result in "ExecuteModel". how would you access the results? As far as I understand it are you returning the gp_result to the "scope" where you call ExecuteModel.  

0 Kudos
GKmieliauskas
Esri Regular Contributor

I think the issue is in line 34:

         arcpy.SetParameterAsText(0, "Your output value")

First parameters of SetParameterAsText can't be 0. As I understand your tool has 5 parameters, last parameter is your output. So, first parameter must be 4.

I am not sure, but I use SetParameter instead of SetParameterAsText.

0 Kudos
swiss_parks_network_nbernhard
Occasional Contributor

thanks @GKmieliauskas, how do you access that parameter? 

0 Kudos
GKmieliauskas
Esri Regular Contributor

As I wrote earlier.

                var gpResult = Geoprocessing.ExecuteToolAsync(fullToolPath,                     parameters, null, null, (eventName, o) => GPHelper.ProcessGPEvent(eventName, o), GPExecuteToolFlags.AddToHistory);

                gpResult.Wait();

                string errMsg = string.Empty;
                if (gpResult.Result.IsFailed)
                {
                    throw new Exception("Tool failed");
                }
                else
                {
                    var resultValues = gpResult.Result.Values;

                    if (resultValues == null)
                    {
                        throw new Exception("Nothing is returned");
                    }
                    else
                    {
                        if (resultValues.Count > 0)
                        {
                             // reading output parameters
                        }
                    }
                }
0 Kudos