Issue with multiple inheritance of Eto.Forms.Dialog with py3 in rhino8

Hi there,

I am trying to port my rhinoscripts from ironPython2.7 to python3 in rhino8. I often use a mechanism that inherits multiple parent classes to combine Eto.Forms.Dialog and Eto.Forms.Form.

When I run the script below in python3, I expect the “SampleEtoRoomNumberDialog” to inherit the method “OnOKButtonClick” from ParentDialog, but instead I get the error message:

AttributeError: ‘0, Culture=neutral, PublicKeyToken=7cec85d7bea77’ object has no attribute ‘OnOKButtonClick’

(The script works fine in Rhin8 / IronPython2.7)

Can anyone please help me out?

Thanks!
Tim

# Imports
import Rhino
from Eto import Forms as forms

class ParentDialog(object):
    # OK button click handler
    def OnOKButtonClick(self, sender, e):
        self.Close(True)
 

# SampleEtoRoomNumber dialog class
class SampleEtoRoomNumberDialog(forms.Dialog[bool], ParentDialog):
 
    # Dialog box Class initializer
    def __init__(self):
        super(SampleEtoRoomNumberDialog, self).__init__()

        # Create the default button
        self.DefaultButton = forms.Button()
        self.DefaultButton.Text = 'OK'
        self.DefaultButton.Click += self.OnOKButtonClick
 
        # Create a table layout and add all the controls
        layout = forms.DynamicLayout()
        layout.AddRow(self.DefaultButton)
 
        # Set the dialog content
        self.Content = layout

 
def main():
    dialog = SampleEtoRoomNumberDialog();
    rc = dialog.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow)

if __name__ == "__main__":
    main()

hi @curtisw,
you’ve helped me before solving a question related to ETO. Do you maybe have a solution for this issue as well? I hope you can help me out!

thanks,
Tim

Hi @timcastelijn ,
The issue is with Inheritance.
Use composition as shown below. I’ve updated your code and works now.

import Rhino
from Eto import Forms as forms
class ParentDialog(object):
    def OnOKButtonClick(self, sender, e):
        print ("hi")
        self.dialog.Close(True)
class SampleEtoRoomNumberDialog(ParentDialog):
    def __init__(self):
        self.dialog = forms.Dialog[bool]()
        self.DefaultButton = forms.Button()
        self.DefaultButton.Text = 'OK'
        self.DefaultButton.Click += self.OnOKButtonClick
        layout = forms.DynamicLayout()
        layout.AddRow(self.DefaultButton)
        self.dialog.Content = layout

    def ShowModal(self, owner):
        return self.dialog.ShowModal(owner)
def main():
    dialog = SampleEtoRoomNumberDialog()
    rc = dialog.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow)
if __name__ == "__main__":
    main()

Hope this helps,
Farouk

hi @farouk.serragedine,

thanks! That solves my issue.

However, I am curious why it’s not possible to follow the pattern below. This post seems to indicate that it should be a valid solution as well. Could you maybe help me understand?

Thanks!
Tim

class Foo(object):
    def __init__(self):
        self.foo = 'foo'
    
    def my_foo_method(self):
        print(self.foo)

class Bar(object):
    def __init__(self, bar):
        self.bar = bar

    def my_bar_method(self):
        print(self.bar)

class FooBar(Foo, Bar):
    def __init__(self, bar='bar'):
        super().__init__()  # this calls all constructors up to Foo
        super(Foo, self).__init__(bar)  # this calls all constructors after Foo up to Bar

my_foo_bar = FooBar()
my_foo_bar.my_foo_method() # prints "foo"
my_foo_bar.my_bar_method() # prints: "bar"