# Winforms Python

**URL:** <https://forum.dynamobim.com/t/winforms-python/50993>\
**Category:** Uncategorized\
**Created:** [May 23, 2020, 8:56am UTC](https://forum.dynamobim.com/t/winforms-python/50993 "2020-05-23T08:56:40Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Mark.Ackerley](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.dynamobim.com/mark.ackerley/32/79310_2.png) [@Mark.Ackerley](https://forum.dynamobim.com/u/Mark.Ackerley)\
**Post date:** [May 23, 2020, 8:56am UTC](https://forum.dynamobim.com/t/winforms-python/50993/1 "2020-05-23T08:56:40Z")

</div>

Hi,

Just an update for some fun I’ve been having with @BenRobinson_HB’s work on Winforms and python.

[Winforms Example.dyn](https://forum.dynamobim.com/uploads/short-url/ibHKfNPwEIxiWJs2zPHBoqnWPgk.dyn) (19.9 KB)

I wanted tp be able to run a single Python script and include a dialogue…

 ![wpf](https://us1.discourse-cdn.com/flex022/uploads/dynamobim/original/3X/5/2/528fe61554944434f5ec10c6dbe87e63d1c6825f.gif)

Usually you would make an XAML code to define where everything goes and how it works, then you’d have a separate C# code to do all the heavy lifting.

A different option is to still use the XAML, but load it to Python using PresentationFramework in presentationframework, seemingly some limitations, but easier to work with.

I think the biggest problem for this method is that you can’t pass input updates from window to window, I suspect this is a Dynamo limitation, more than a Python limitation.

Ben’s work is found here: [How to Build Graphical User Interface Nodes for Dynamo Using Python | Autodesk University](https://www.autodesk.com/autodesk-university/class/How-Build-Graphical-User-Interface-Nodes-Dynamo-Using-Python-2019)

I left in the dropdown he made, I’ve also done some other things… Checkboxes, Text input &Text input warning… This is fun because there is no WInform just for number input, I tried to make a Dynamic warning which ran when you put in a character which failed a reg-ex check, but it wouldn’t dynamically update when you removed the illegal character.

There’re are lots of lines of code, but they are relatively simple… As Ben noted, there is very little Python documentation on this, so hopefully it is useful to expand it a bit.

I have kept the code as readable as possible. All comments welcomed!

You will need to change the paths to your own logos / bitmaps, otherwise it should just run. I tried to get bitmaps from a web address but I couldn’t get that working without saving locally (which seemed a bit rude)…

> #thanks to Ben Robinson
> 
> import clr  
> import sys  
> pyt\_path = r’C:\Program Files (x86)\IronPython 2.7\Lib’  
> sys.path.append(pyt\_path)
> 
> ```
> ### UI additional references ###
> 
> ```
> 
> clr.AddReference(“System.Windows.Forms”)  
> clr.AddReference(“System.Drawing”)
> 
> from System.Windows.Forms import Application, Form, FormWindowState, Screen, Label, PictureBox, PictureBoxSizeMode, AnchorStyles, BorderStyle, ComboBox, ComboBoxStyle, FormBorderStyle, CheckBox, TextBox, TextBoxBase  
> from System.Windows.Forms import Button, LinkLabel, Panel, Button  
> from System.Drawing import Icon, Color, Font, Point, Size#, Image
> 
> #system location C:\Windows\Microsoft.NET\Framework64\v4.0.30319  
> import [System.IO](http://System.IO)
> 
> ```
> ### Define some variables ###
> 
> ```
> 
> #these variables are static through the 2 windows  
> bitmapImage = System.Drawing.Bitmap(“C:\Me\Dynamo\WPF in Dynamo\” + “dyna-sco-logo.png”) #titlebar logo as bmp  
> titleIcon = Icon.FromHandle(bitmapImage.GetHicon()) #titlebar logo as icon  
> titleText = “Dimensioning” #text that appears in the GUI titlebar  
> btnHeight = 40 #declared here as used for btn locations  
> btnWidth = 120  
> spacing = 20 #spacing size for GUI elements to form a consistent border  
> fontMessage = Font("Helvetica ", 9)  
> fontCK = Font("Helvetica ", 8) #set Checkbox Font  
> winSize = Size(1000,600) #consistant window size
> 
> ```
> ### Global defs ###
> 
> ```
> 
> def button(txt, loc, clc):  
> btn = Button()  
> #btnCancel.Parent = self  
> btn.Text = txt  
> btn.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right)  
> btn.Location = loc  
> btn.Click += clc  
> btn.Height = btnHeight  
> btn.Width = btnWidth  
> btn.BackColor = Color.FromArgb(220, 220, 220)  
> return btn
> 
> def logo(uiW, uiH):  
> logo = PictureBox()  
> bitmapImage = System.Drawing.Bitmap(“C:\Me\Dynamo\WPF in Dynamo\” + “dyna-sco-logo-text.png”)  
> logo.Image = bitmapImage  
> ratio = float(logo.Height)/ float(logo.Width) #needs to be a float as int will round to the nearest whole number  
> logo.Size = Size(220, 130\*ratio) #fixed for consistancy  
> logo.Location = Point(spacing, (uiH - logo.Height)-spacing)  
> logo.SizeMode = PictureBoxSizeMode.Zoom # zooms the image to fit the extent  
> logo.Anchor = (AnchorStyles.Bottom | AnchorStyles.Left) #anchor styles lock elements to a given corner of the GUI if you allow users change size  
> return logo
> 
> #Create a Class to define the first window
> 
> #create a instance of the form class called dimsionInputs.  
> #In Winforms, any window or a dialog is a Form.#
> 
> class dimsionInputs(Form):
> 
> ```
> def __init__ (self): #the __init__ method inside a class is its constructor
> 
> self.Text = titleText             
> self.Icon = titleIcon
> self.BackColor = Color.FromArgb(255, 255, 255)  
> self.WindowState = FormWindowState.Normal # set maximised minimised or normal size GUI
> self.CenterToScreen() #centres GUI to the middle of your screen 
> self.BringToFront() #brings the GUI to the front of all open windows
> self.Topmost = True #true to display the GUI infront of any other active forms
> self.Size = winSize #small enough to fit on most screens
> uiWidth = self.DisplayRectangle.Width #get the size of the form to use to scale form elements
> uiHeight = self.DisplayRectangle.Height
> stHeight = uiHeight / 4.5 #the height at which the check & text boxes will start
> htSpacing = 50 #a standard spacing vertical gap betwen check & text boxes    
> self.FormBorderStyle = FormBorderStyle.FixedDialog # fixed dialog stops the user from adjusting the form size. Recomended disabling this when testing to see if elements are in the wrong place.#
>     
> ### Set Default Values ###
>     
> #self.dropDownOutput = 'pick one'
>     
> self.txtBoxOffOutput = '500' #offset of dimension from wall
> self.txtBoxCoordOutput = 'Internal' #name of coordinate system to be used
> self.ckBoxESOutput = False #External Side?
> self.ckBoxLWOutput = False #Linked Walls?
> self.ckBoxAWOutput = False #All Walls?
> self.ckBoxMDOutput = False #Multiple dims per wall?
> self.messText = 'test'
> self.runNextOutput = False #how we decide if we want the next window to fire#
> self.Controls.Add(logo(uiWidth, uiHeight)) #add logo
>     
> 
> #Create Main Window Text
>     
> userMessage = Label() #label displays texts
> font = fontMessage
> userMessage.Text = 'Automatic Dimensions for Linked or Project Walls'
> userMessage.Font = font
> userMessage.Location = Point(spacing, spacing) #all location require a point object from system.Drawing to set the location.
> userMessage.Size = Size(uiWidth-(spacing*2),(uiHeight/4)-60) #size the control with the width of the GUI to ensure it scales with different screen
> self.Controls.Add(userMessage) #this adds control element to the GUI
> 
> #combox drop down
> #cBox = ComboBox() #dropdown control form
> #cBox.Location = Point(spacing,uiHeight/3)       
> #cBox.Width = uiWidth -(spacing*2)
> # cBox.Items.AddRange(tuple[(1,2,3,])) # Adds an array of items to the list of items for a ComboBox.
> #cBox.DropDownStyle = ComboBoxStyle.DropDownList #setting to dropdown list prevents users from being able to add aditional text values
> #cBox.SelectedIndexChanged += self.dropDownOutput #.Click+= registers the press of the button to register the event handler and determine what action takes place when button clicked
> #self.Controls.Add(cBox)   
> 
> ### CHECKBOXES ##
>           
> #checkbox External Side
> ckBoxES = CheckBox()
> ckBoxES.Name="External Side"  
> ckBoxES.Text="External Side? (or internal side)"
> ckBoxES.Location = Point(spacing,stHeight)       
> ckBoxES.Width = uiWidth -(spacing*2)
> ckBoxES.Font = fontCK
> ckBoxES.Height = 40   
> ckBoxES.CheckStateChanged += self.ckBoxESChecked
> 
> #checkbox Linked Walls 
> ckBoxLW = CheckBox()
> ckBoxLW.Name="Linked Walls"  
> ckBoxLW.Text="Linked Walls?"
> ckBoxLW.Location = Point(spacing,stHeight+htSpacing)       
> ckBoxLW.Width = uiWidth -(spacing*2)
> ckBoxLW.Font = fontCK
> ckBoxLW.Height = 40
> ckBoxLW.CheckStateChanged += self.ckBoxLWChecked
> 
>     
> #checkbox All Walls
> ckBoxAW = CheckBox()
> ckBoxAW.Name="All Walls"  
> ckBoxAW.Text="All Walls?"
> ckBoxAW.Location = Point(spacing,(stHeight+htSpacing*2))       
> ckBoxAW.Width = uiWidth -(spacing*2)
> ckBoxAW.Height = 40
> ckBoxAW.Font = fontCK
> ckBoxAW.CheckStateChanged += self.ckBoxAWChecked
>     
> #checkbox MultiDims
> ckBoxMD = CheckBox()
> ckBoxMD.Name="Multi-Dims"  
> ckBoxMD.Text="Multi-Dims for each wall (for brick checking)"
> ckBoxMD.Location = Point(spacing,(stHeight+htSpacing*3))       
> ckBoxMD.Width = uiWidth -(spacing*2)
> ckBoxMD.Height = 40
> ckBoxMD.Font = fontCK
> ckBoxMD.CheckStateChanged += self.ckBoxMDChecked
>     
> #add to Window
> self.Controls.Add(ckBoxES)
> self.Controls.Add(ckBoxLW)
> self.Controls.Add(ckBoxAW) 
> self.Controls.Add(ckBoxMD)  
> 
> ###TEXTBOXES ##
>  
> #TextBox Coordinate System      
> textBoxCoord = TextBox()
> textBoxCoord.Width = 300
> textBoxCoord.Text="Internal"
> textBoxCoord.Font = fontCK
> textBoxCoord.Location = Point(spacing + 400 ,(stHeight+htSpacing*4))
> textBoxCoord.TextChanged += self.txtBoxCoordChanged
>     
> #TextBox Coordinate System - Label
> textBoxCoordLabel = Label() #label displays texts
> textBoxCoordLabel.Text = "Coordinate System Name"
> textBoxCoordLabel.Width = 400
> textBoxCoordLabel.Font = fontCK
> textBoxCoordLabel.Location = Point(spacing,(stHeight+htSpacing*4))
> textBoxCoordLabel.Height = 50
>     
> #add to Window
> self.Controls.Add(textBoxCoordLabel)  
> self.Controls.Add(textBoxCoord)
> 
>     
> #TextBox Offset        
> textBoxOffset = TextBox()
> textBoxOffset.Location = Point(spacing + 400,(stHeight+htSpacing*5))       
> textBoxOffset.Width = 300
> textBoxOffset.Text= "500"
> textBoxOffset.Name="Offset Distance"  
> textBoxOffset.Font = fontCK
> textBoxOffset.TextChanged += self.txtBoxOffChanged
> 
>            
> #TextBox Offset - Label
> textBoxOffsetLabel = Label() #label displays texts
> textBoxOffsetLabel.Text = "Offset Distance"
> textBoxOffsetLabel.Width = 200
> textBoxOffsetLabel.Font = fontCK
> textBoxOffsetLabel.Height = 40
> textBoxOffsetLabel.Location = Point(spacing,(stHeight+htSpacing*5))
> #this is how you would do a margin
> #textBoxOffsetLabel.Margin = Padding(712,418,0,0) 
>     
> #add to Window
> self.Controls.Add(textBoxOffsetLabel)  
> self.Controls.Add(textBoxOffset)
> 
> #Buttons
> 
> #Create Ok Button
> btnOkClick = self.okButtonPressed #register click by user
> btnOkLoc = Point(uiWidth - ((btnWidth * 2) + spacing + 30), uiHeight - (btnHeight + spacing))
> btnOk = button('OK', btnOkLoc, btnOkClick) 
>     
> #Create Cancel Button
> btnCnclClick = self.CnlButtonPressed #register click by user
> btnCnclLoc = Point(uiWidth - (btnHeight + spacing + 90), uiHeight - (btnHeight + spacing ))
> btnCancel = button('Cancel', btnCnclLoc, btnCnclClick)
>     
> #add to Window
> self.Controls.Add(btnOk)
> self.Controls.Add(btnCancel)   
> 
> ###Return Info From User
> 
> ```
> 
> #these defs are the mechanism by which we assess the info from the user  
> #self is the instance of the GUI form. Sender is the control/widget. args is the argument/event provided from the control
> 
> ```
> ###dropDown ####
> #def dropDownOutput(self, sender, args):
> self.dropDownOutput = sender.SelectedItem #output the selected item.
> 
> ### checkBoxes ###    
> def ckBoxESChecked(self, sender, args):
> if sender.Checked:
> self.ckBoxESOutput = True
> #otherwise indeterminate states give erroneous results
> else:
> self.ckBoxESOutput = False 
>         
> def ckBoxLWChecked(self, sender, args):
> if sender.Checked:
> self.ckBoxLWOutput = True
> else:
> self.ckBoxLWOutput = False
> 
> def ckBoxAWChecked(self, sender, args):
> if sender.Checked:
> self.ckBoxAWOutput = True
> else:
> self.ckBoxAWOutput = False
> 
> def ckBoxMDChecked(self, sender, args):
> if sender.Checked:
> self.ckBoxMDOutput = True
> else:
> self.ckBoxMDOutput = False
>         
>        
> ### textBoxes ###        
> def txtBoxOffChanged(self, sender, args):     
> self.txtBoxOffOutput = sender.Text
>             
>     
> def txtBoxCoordChanged(self, sender, args):
> self.txtBoxCoordOutput = sender.Text 
> 
> ### buttons ###
> def okButtonPressed(self, sender, args):
> numErrorProvider = System.Windows.Forms.ErrorProvider()
> try:
> int(self.txtBoxOffOutput)
> self.Close() #trigger to close the GUI when button is pressed
> self.runNextOutput = True #if the ok button is pressed set runNextOutput as True
> except ValueError:        
> numErrorProvider.SetError(sender, 'Offset must be a number')
> def CnlButtonPressed(self, sender, args):
> self.Close()
> self.runNextOutput = False #if the cancel button is pressed set runNextOutput as False 
> 
> ###Create a Class to define the second window ####
> 
> ```
> 
> class followForm(Form):
> 
> ```
> def __init__ (self): #the __init__ method inside a class is its constructor
> self.Text = titleText             
> self.Icon = titleIcon        
> self.Size = winSize
> self.CenterToScreen()
> self.FormBorderStyle = FormBorderStyle.FixedDialog        
> self.runNextOutput = False #how we decide if we want the next window to fire 
> uiWidth = self.DisplayRectangle.Width #get the size of the form to use to scale form elements
> uiHeight = self.DisplayRectangle.Height
> self.Controls.Add(logo(uiWidth, uiHeight)) #add logo       
> 
> ```
> 
> #############--------------------------#############
> 
> ```
> ###Buttons ## 
> 
> #Create Ok Button
> btnOkClick = self.okButtonPressed #register click by user
> btnOkLoc = Point(uiWidth - ((btnWidth * 2) + spacing + 30), uiHeight - (btnHeight + spacing))
> btnOk = button('OK', btnOkLoc, btnOkClick) 
> 
> #Create Cancel Button
> btnCnclClick = self.CnlButtonPressed #register click by user
> btnCnclLoc = Point(uiWidth - (btnHeight + spacing + 90), uiHeight - (btnHeight + spacing ))
> btnCancel = button('Cancel', btnCnclLoc, btnCnclClick)
>     
> #add to Window
> self.Controls.Add(btnOk)
> self.Controls.Add(btnCancel)  
> 
> ###Create Second Window Text ###
>            
> userMessageScnd = Label() #label displays texts
> font = fontMessage
> dimsionInputs()
> #message(ckBoxLWChecked, ckBoxAWChecked)
> userMessageScnd.Text = 'runNextOutput = ' + str(dimForm.runNextOutput) + '''\n\nThis is a contradiction! \n\nThis window only fired because runNextOutput was changed to True by the previous window! \n\nSo, sadly, it seems we can\'t link a series of windows driven by dynamic information without daisy-chaining python nodes together'''
> #userMessageScnd.Text = dimsionInputs().messText
> userMessageScnd.Font = font
> userMessageScnd.Location = Point(spacing, spacing) #all location require a point object from system.Drawing to set the location.
> userMessageScnd.Size = Size(uiWidth-(spacing*2),(uiHeight)) #size the control with the width of the GUI to ensure it scales with different screen
> self.Controls.Add(userMessageScnd) #this adds control element to the GUI
> 
> ###Return Info From User
>     
> ###buttons ###
> def okButtonPressed(self, sender, args):
> self.Close() #trigger to close the GUI when button is pressed
> self.runNextOutput = True #if the ok button is pressed set runNextOutput as True#
> def CnlButtonPressed(self, sender, args):
> self.Close()
> 
> ###And Run ##
> 
> ```
> 
> dimForm = dimsionInputs()  
> followForm = followForm()
> 
> Application.Run(dimForm)
> 
> if dimForm.runNextOutput:  
> Application.Run(followForm)
> 
> OUT = dimForm.ckBoxESOutput, dimForm.ckBoxLWOutput, dimForm.ckBoxAWOutput, dimForm.ckBoxMDOutput, dimForm.txtBoxOffOutput, dimForm.txtBoxCoordOutput,

Cheers,

Mark

---

<div class="post-metadata">

**Author:** ![Thomas\_Mahon](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.dynamobim.com/thomas_mahon/32/64209_2.png) [@Thomas\_Mahon](https://forum.dynamobim.com/u/Thomas_Mahon)\
**Post date:** [May 23, 2020, 9:33am UTC](https://forum.dynamobim.com/t/winforms-python/50993/2 "2020-05-23T09:33:53Z")

</div>

This is WinForms not WPF 😜

---

<div class="post-metadata">

**Author:** ![Mark.Ackerley](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.dynamobim.com/mark.ackerley/32/79310_2.png) [@Mark.Ackerley](https://forum.dynamobim.com/u/Mark.Ackerley)\
**Post date:** [May 23, 2020, 10:00am UTC](https://forum.dynamobim.com/t/winforms-python/50993/3 "2020-05-23T10:00:40Z")

</div>

Argh sorry Thomas, will edit title 🙂

---

<div class="post-metadata">

**Author:** ![Thomas\_Mahon](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.dynamobim.com/thomas_mahon/32/64209_2.png) [@Thomas\_Mahon](https://forum.dynamobim.com/u/Thomas_Mahon)\
**Post date:** [May 23, 2020, 10:27am UTC](https://forum.dynamobim.com/t/winforms-python/50993/4 "2020-05-23T10:27:33Z")

</div>

No problem mate, thanks for sharing, its a great resource.

---

<div class="post-metadata">

**Author:** ![Mark.Ackerley](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.dynamobim.com/mark.ackerley/32/79310_2.png) [@Mark.Ackerley](https://forum.dynamobim.com/u/Mark.Ackerley)\
**Post date:** [May 23, 2020, 10:32am UTC](https://forum.dynamobim.com/t/winforms-python/50993/5 "2020-05-23T10:32:30Z")

</div>

Oddly Chrome displays the code strangely but it looks fine on my phone… I’ve added a dyn.

---

<div class="post-metadata">

**Author:** ![BenRobinson\_HB](https://avatars.discourse-cdn.com/v4/letter/b/8dc957/32.png) [@BenRobinson\_HB](https://forum.dynamobim.com/u/BenRobinson_HB)\
**Post date:** [May 26, 2020, 7:52am UTC](https://forum.dynamobim.com/t/winforms-python/50993/6 "2020-05-26T07:52:47Z")

</div>

looking good!  
I need to pass on credit to @Mostafa_El_Ayoubi I started looking into this after using the DataShapes packages which is great!

Doing Winforms in python gave me the skills to take the jump to C# and i will say its a lot bloody easier to do in Visual Studio. Having said that I still build winform nodes / use DataShapes if I am building a quick Dynamo script for someone in the office. Once you have the fundamental elements set up its quick to duplicate.
