GUI ComboBox in CPython3

Hi,

I am pretty new in making GUI and I’ve been keen to learn how to write the code myself and refrain from using external packages. I’m trying to create a dropdown list that user can select in dynamo with WinForm. I followed a tutorial from AU: How to Build Graphical User Interface Nodes for Dynamo Using Python | Autodesk University

However, my code is returning:
image

Although I have created a method dropDownOutput in the class… Here is my code below, I’m writing in CPython3:

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

clr.AddReference('RevitAPIUI')
from  Autodesk.Revit.UI import Selection

clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

clr.AddReference('RevitServices')

import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from System.Collections.Generic import *

import sys
import os
localapp = os.getenv(r'LOCALAPPDATA')
sys.path.append(os.path.join(localapp, r'python-3.9.12-embed-amd64\Lib\site-packages'))
import importlib

import math
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application

#------------------------------------------------
#UI additional references
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")


#from System.Windows.Forms import *
from System.Windows.Forms import Application, Form, FormWindowState, Screen, Label, PictureBox, PictureBoxSizeMode, AnchorStyles, BorderStyle, ComboBox, ComboBoxStyle, FormBorderStyle
from System.Windows.Forms import Button, LinkLabel, Panel
from System.Drawing import Icon, Color, Font, Point, Size


#------------------------------------------------

#INPUTS
message = IN[0]
listInput =tuple(IN[1]) 
userOutputDefaultStr = "No selection made, Re-run, and select an item from the dropdown menu"

#------------------------------------------------

#Dropdown Form
class DropDownForm(Form):       
    def __init__(self):     
        self.Text = "AMC Knowledge Base"      
        #self.Icon = Icon.FromHandle(icon.GetHicon()) #takes a bitmap image and converts to a file that can be used as a Icon for the titlebar
        self.BackColor = Color.FromArgb(255, 255, 255) 
        
        self.WindowState = FormWindowState.Normal 
        self.CenterToScreen()   
        self.BringToFront()    
        self.Topmost = True   
        
        screenSize = Screen.GetWorkingArea(self)  
        self.Width = screenSize.Width / 4  
        self.Height = screenSize.Height / 4
        uiWidth = self.DisplayRectangle.Width    
        uiHeight = self.DisplayRectangle.Height
 
        self.FormBorderStyle = FormBorderStyle.FixedDialog      
        
        self.userOutput = userOutputDefaultStr  
        self.runNextOutput =  False  #set these default values
#-----------------------------------------------
           
        spacing = 10    
       
        # creates the text box for a info message
        userMessage = Label()   
        font = Font("Helvetica ", 10)
        userMessage.Text = message
        userMessage.Font = font
        userMessage.Location = Point(spacing, spacing)  
        userMessage.Size = Size(uiWidth-(spacing*2),(uiHeight/4))   
        self.Controls.Add(userMessage)      
        #-----------------------------------------------
        
        cBox = ComboBox()   
        cBox.Location = Point(spacing,uiHeight/3)       
        cBox.Width = uiWidth -(spacing*2)
        cBox.Items.AddRange(listInput)    
        cBox.DropDownStyle = ComboBoxStyle.DropDownList     
        cBox.SelectedIndexChanged += self.dropDownOutput
        self.Controls.Add(cBox)
        
        #-----------------------------------------------                 
        
        
        btnOk = Button()    
        btnOk.Text = "Next"
        btnOk.Location = Point(uiWidth - ((btnOk.Width * 2) + spacing ), uiHeight - (btnOk.Height + spacing ))
        btnOk.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right)
        btnOk.Click += self.okButtonPressed    
        self.Controls.Add(btnOk)
        
        #-----------------------------------------------
        
        #Create Cancel Button
        btnCancel = Button()
        #btnCancel.Parent = self
        btnCancel.Text = "Cancel"
        btnCancel.Location = Point(uiWidth - (btnOk.Width + spacing), uiHeight - (btnOk.Height + spacing ))
        btnCancel.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right)
        btnCancel.Click += self.CnlButtonPressed
        self.Controls.Add(btnCancel) 
        #-----------------------------------------------
        
        def dropDownOutput(self, sender, args):
            self.userOutput = sender.SelectedItem   
            
        def okButtonPressed(self, sender, args):
            self.Close()    
            self.runNextOutput = True 
             
        def CnlButtonPressed(self, sender, args):
            self.Close()
            self.runNextOutput = False


dropdownForm = DropDownForm()
Application.Run(dropdownForm)
if ddForm.userOutput == userOutputDefaultStr:      
        results = ddForm.userOutput, False      
else:
        results = ddForm.userOutput, ddForm.runNextOutput       
        
OUT = results

Can anyone tell me what I’m doing wrong?

Hi,

there is a little indentation error on event handler methods.
due to the Python scope, instance methods should be directly in class DropDownForm not in __init __ method (initialization)

here fixed code


import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

clr.AddReference('RevitAPIUI')
from  Autodesk.Revit.UI import Selection

clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

clr.AddReference('RevitServices')

import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from System.Collections.Generic import *

import sys
import os
localapp = os.getenv(r'LOCALAPPDATA')
sys.path.append(os.path.join(localapp, r'python-3.9.12-embed-amd64\Lib\site-packages'))
import importlib

import math
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application

#------------------------------------------------
#UI additional references
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")


#from System.Windows.Forms import *
from System.Windows.Forms import Application, Form, FormWindowState, Screen, Label, PictureBox, PictureBoxSizeMode, AnchorStyles, BorderStyle, ComboBox, ComboBoxStyle, FormBorderStyle
from System.Windows.Forms import Button, LinkLabel, Panel
from System.Drawing import Icon, Color, Font, Point, Size


#------------------------------------------------

#INPUTS
message = IN[0]
listInput =tuple(IN[1]) 
userOutputDefaultStr = "No selection made, Re-run, and select an item from the dropdown menu"

#------------------------------------------------

#Dropdown Form
class DropDownForm(Form):       
    def __init__(self):  
        self.InitializeComponent()

    def InitializeComponent(self):
        self.SuspendLayout()
        self.Text = "AMC Knowledge Base"      
        #self.Icon = Icon.FromHandle(icon.GetHicon()) #takes a bitmap image and converts to a file that can be used as a Icon for the titlebar
        self.BackColor = Color.FromArgb(255, 255, 255) 
        
        self.WindowState = FormWindowState.Normal 
        self.CenterToScreen()   
        self.BringToFront()    
        self.Topmost = True   
        
        screenSize = Screen.GetWorkingArea(self)  
        self.Width = screenSize.Width // 4
        self.Height = screenSize.Height // 4
        uiWidth = self.DisplayRectangle.Width    
        uiHeight = self.DisplayRectangle.Height
 
        self.FormBorderStyle = FormBorderStyle.FixedDialog      
        
        self.userOutput = userOutputDefaultStr  
        self.runNextOutput =  False  #set these default values
#-----------------------------------------------
           
        spacing = 10    
       
        # creates the text box for a info message
        userMessage = Label()   
        font = Font("Helvetica ", 10)
        userMessage.Text = message
        userMessage.Font = font
        userMessage.Location = Point(spacing, spacing)  
        userMessage.Size = Size(uiWidth- spacing*2, uiHeight // 4)   
        self.Controls.Add(userMessage)      
        #-----------------------------------------------
        
        cBox = ComboBox()   
        cBox.Location = Point(spacing, uiHeight // 3)       
        cBox.Width = uiWidth -(spacing*2)
        cBox.Items.AddRange(listInput)    
        cBox.DropDownStyle = ComboBoxStyle.DropDownList     
        cBox.SelectedIndexChanged += self.dropDownOutput
        self.Controls.Add(cBox)
        
        #-----------------------------------------------                 
        
        
        btnOk = Button()    
        btnOk.Text = "Next"
        btnOk.Location = Point(uiWidth - ((btnOk.Width * 2) + spacing ), uiHeight - (btnOk.Height + spacing ))
        btnOk.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right)
        btnOk.Click += self.okButtonPressed    
        self.Controls.Add(btnOk)
        
        #-----------------------------------------------
        
        #Create Cancel Button
        btnCancel = Button()
        #btnCancel.Parent = self
        btnCancel.Text = "Cancel"
        btnCancel.Location = Point(uiWidth - (btnOk.Width + spacing), uiHeight - (btnOk.Height + spacing ))
        btnCancel.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right)
        btnCancel.Click += self.CnlButtonPressed
        self.Controls.Add(btnCancel) 
        self.ResumeLayout(False)
        #-----------------------------------------------
    
    def dropDownOutput(self, sender, e):
        self.userOutput = sender.SelectedItem   
        
    def okButtonPressed(self, sender, e):
        self.Close()    
        self.runNextOutput = True 
         
    def CnlButtonPressed(self, sender, e):
        self.Close()
        self.runNextOutput = False



ddForm = DropDownForm()
Application.Run(ddForm)
if ddForm.userOutput == userOutputDefaultStr:      
        results = ddForm.userOutput, False      
else:
        results = ddForm.userOutput, ddForm.runNextOutput       
        
OUT = results
5 Likes

Ah! rookie mistake :see_no_evil:
Thank you!

However, do you know why this only works in IronPython2?
I tried using CPython3 and it returned this:

image

Translation:
PythonEvaluator.Evaluate failed.
The constructor for the type
“System.Reflection.Emit.TypeBuilder” was not found.

Interesting, it worked for me… Could it be a language thing?

for now, there is an issue with PythonNet2, you can use IronPython(2 or 3) or C# instead

we will probably have to wait for the integration of PythonNet 3 into the CPython3 engine to resolve the problem

strange, if you click on the button it’s works ?

Yeah it works for me and i’m not getting reports of any issues from users… We also have pyRevit here, perhaps the associated installs are making a difference?

1 Like