Grasshopper

algorithmic modeling for Rhino

Hi guys,

I have made a component on visual studio that can be double clicked on to load a specific file. However the data does not remain when re-opening the file after saving it.

Is there a method to "internalise" the data so that it stays there for next time until someone loads another file? I could not find one in the Grasshopper sdk unfortunately.

Many thanks,

Arthur

Views: 3361

Replies to This Discussion

Hi Arthur,

where do you store this data? In an input parameter? If so, what type? Perhaps in some local variable? 

Internalising data is about moving data from the volatile to the persistent records, it sounds like you want to (de)serialize your data.

--

David Rutten

david@mcneel.com

Poprad, Slovakia

Hi David,


Thanks a lot for your reply. I store the data in a local variable called m_settings which is a string[] type. I then store the variable in the output parameter of the component with the following line:

DA.SetDataList(0, m_settings);

Following your advice and the Basic Serialization page on msdn, I've made my GH_Component class Serializable using the following synthax:

[Serializable] public class SettingsComponent : GH_Component

I have also added a BinaryFormatter() object to serialize m_settings into a file:

FileStream fs = new FileStream("DataFiletemp.dat", FileMode.Create);
                        BinaryFormatter formatter = new BinaryFormatter();
                        try
                        {
                            formatter.Serialize(fs, m_settings);
                        }
                        catch (SerializationException e)
                        {
                            Console.WriteLine("Failed to serialize. Reason: " + e.Message);
                            throw;
                        }
                        finally
                        {
                            fs.Close();
                        }

                       DA.SetDataList(0, m_settings);

However this is the error I get when loading the file from the component on Grasshopper:

What am I missing? Many thanks,

Arthur

 

That certainly isn't my advice on MSDN, someone at Microsoft wrote that.

If you want to write data to a ghx/gh file (and then read data back in again) you don't have to add the [serializable] attribute. What you need to do is override the Write() and Read() methods on GH_Component. When you do this, you must call the base class methods, otherwise you'll shortcircuit the (de)serialization entirely.

Here's an example of an overridden write method that stores an array of strings:

public override bool Write(GH_IO.Serialization.GH_IWriter writer)
{
  if (m_strings != null && m_strings.Length > 0)

  {

    writer.SetInt32("StringCount", m_strings.Length);

    for (int i = 0; i < m_strings.Length; i++)

    {

      writer.SetString("String", i, m_strings[i]);

    }

  }
  return base.Write(writer);
}

If you're worried about storing large amounts of data this way, you can choose to also convert your strings to a byte-array, then compress it and store the compressed data. This won't make a different in *.gh files as they are already compressed.

Note that the writer.SetXXXX() methods have overrides that accept integers. This allows you to store many different items with the same name.

--

David Rutten

david@mcneel.com

Poprad, Slovakia

Thanks a lot David,

I have added the method to the class below and changed the my variable name to m_settings. It compiles with no errors but it is still not saving the data when saving the file unfortunately. What am I doing wrong?

public class SettingsComponent : GH_Component
      
           {
                public SettingsComponent(): base("LoadSettings", "LoadSettings", "Loading ini", "Extra", "Silkworm") { }

                public override void CreateAttributes()
                {
                    m_attributes = new SettingsComponentAttributes(this);
                }

                string m_settings_temp;
                string[] m_settings;

                public void ShowSettingsGui()
                {
                    var dialog = new OpenFileDialog { Filter = "Data Sources (*.ini)|*.ini*|All Files|*.*" };
                    if (dialog.ShowDialog() != DialogResult.OK) return;

                    m_settings_temp = File.ReadAllText(dialog.FileName);
                    m_settings = m_settings_temp.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
                    ExpireSolution(true);
                }


                public override bool Write(GH_IO.Serialization.GH_IWriter writer)
                {
                    if (m_settings != null && m_settings.Length > 0)
                    {
                        writer.SetInt32("StringCount", m_settings.Length);
                        for (int i = 0; i < m_settings.Length; i++)
                        {
                            writer.SetString("String", i, m_settings[i]);
                        }
                    }
                    return base.Write(writer);
                }

                protected override void SolveInstance(IGH_DataAccess DA)
                {
                    if (m_settings == null)
                    {
                        AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "You must declare some valid settings");
                        return;
                    }
                    else
                    {
                       DA.SetDataList(0, m_settings);
                    }      
                }

Is the data not in the file, or is the data not read back in? I didn't write an example of the Read() method as it is basically a mirror-image of the write method, but you'll have to code it before data survives both writing and reading.

--

David Rutten

david@mcneel.com

Poprad, Slovakia

Thanks David. The data is not read back in the component when re-opening the file. The Read() and Write() method are now both in the code which compiles with no error but the data still does not save with the file unfortunately.


                public override bool Read(GH_IO.Serialization.GH_IReader reader)
                {
                    if (m_settings != null && m_settings.Length > 0)
                    {
                        Int32 a = m_settings.Length;
                        reader.TryGetInt32("StringCount", ref a);
                        for (int i = 0; i < m_settings.Length; i++)
                        {
                            reader.TryGetString("String", i, ref m_settings[i]);
                        }
                    }
                    return base.Read(reader);
                }

When you're reading the data it doesn't matter whether m_settings is null or empty. You're going to create a new m_settings array from the archive.

private string[] m_settings;


public override bool Write(GH_IWriter writer)
{
  if (m_strings != null && m_strings.Length > 0)
  {
    writer.SetInt32("StringCount", m_strings.Length);
    for (int i = 0; i < m_strings.Length; i++)
    {
      writer.SetString("String", i, m_strings[i]);
    }
  }

  return base.Write(writer);
}


public override bool Read(GH_IReader reader)
{
  m_settings = null;

  int count = 0;
  reader.TryGetInt32("StringCount", ref count);
  if (count > 0)
  {
    System.Array.Resize(ref m_settings, count);
    for (int i = 0; i < count; i++)
    {
      string line = null;
      reader.TryGetString("String", i, ref line);
      m_settings[i] = line;
    }
  }

  return base.Read(reader);
}

(code is untested, but I think it should work).

--

David Rutten

david@mcneel.com

Poprad, Slovakia

Yippee Yay it's working! Thanks so much David.

RSS

About

Translate

Search

Videos

  • Add Videos
  • View All

© 2024   Created by Scott Davidson.   Powered by

Badges  |  Report an Issue  |  Terms of Service