I applied a fix for this type check problem. It’s not perfect but the problem area is complicated especially between how IronPython used to work and how Python 3 interop with dotnet runtime is expected to work.
This code works now:
for obj in gh.Instances.ActiveCanvas.Document.Objects:
if isinstance(obj, gh.Kernel.Special.GH_Panel):
if obj.NickName == Panel:
obj.Properties.Colour = Color
type(obj) is gh.Kernel.Special.GH_Panel
will not work because the returned object is of type IGH_DocumentObject
. It’s more pythonic to type check using the isinstance()
method anyway. (Types can customize their type check logic when called from isinstance()
)
TLDR
C# allows types to explicitly define members for an interface they are adopting:
interface ISayHello
{
string SayHello();
}
class Person : ISayHello
{
public string Name { get; }
// note the ISayHello. part and lack of access modifier e.g. public/private
// this makes the method explicitly accessible from ISayHello
string ISayHello.SayHello() => "Hello!!!!!!"
}
When an instance of this type is access with type Person p = new Person();
, you are not allowed to call p.SayHello()
because that method is explicitly accessible when you have a variable of type ISayHello
. So to make it work you have to ((ISayHello)p).SayHello()
Now here is where it gets complicated:
interface ISayHelloInFarsi
{
string SayHello();
}
class Person : ISayHello, ISayHelloInFarsi
{
public string Name { get; }
string ISayHello.SayHello() => "Hello!!!!!!"
string ISayHelloInFarsi.SayHello() => "Salaaaam!!!"
}
The type person ends up having two SayHello()
methods and that’s okay because they are explicit to their own interfaces. so this is how they would be accessed:
((ISayHello)p).SayHello(); // "Hello!!!!!!"
((ISayHelloInFarsi)p).SayHello(); // "Salaaaam!!!"
This gets complicated in python when you have access to the variable p
. You can not access either of these methods directly in IronPython. Instead you have to:
ISayHelloInFarsi.SayHello(p) # notice we are passing p to the method
Pythonnet has taken a different approach. In the context of Grasshopper, all objects return from Grasshopper.Instances.ActiveCanvas.Document.Objects
are of type IGH_DocumentObject
. Pythonnet automatically wraps all these instances with the return type which is IGH_DocumentObject
:
import Grasshopper
for obj in Grasshopper.Instances.ActiveCanvas.Document.Objects:
print(type(obj)) # would print <class 'Grasshopper.Kernel.IGH_DocumentObject'>
There was a bug in the runtime that would return False to isinstance(obj, Grasshopper.Kernel.Special.GH_Panel)
although the actuall type of returned object is GH_Panel
. This bug is fixed.
However, statements below would work in IronPython:
# IronPython
type(obj) is gh.Kernel.Special.GH_Panel # returns True
type(obj) == gh.Kernel.Special.GH_Panel # returns True
obj.GetType() is gh.Kernel.Special.GH_Panel # returns False
obj.GetType() == gh.Kernel.Special.GH_Panel # returns True
But they will not work in Python 3. Making them work require a bigger change in Pythonnet that could complicate the type system.
# Python 3
type(obj) is gh.Kernel.Special.GH_Panel # returns False
type(obj) == gh.Kernel.Special.GH_Panel # returns False
obj.GetType() is gh.Kernel.Special.GH_Panel # returns False
obj.GetType() == gh.Kernel.Special.GH_Panel # returns False