Workbench Scripting: Using Forms in your Script

Last week we had a very well attended Webinar on using Scripting with Workbench. We talked about how you can control projects, parameters, and material properties with python.  We also talked about how you can use python to drive data compliant applications by sending commands and/or parameters to it.  You can watch the webinar at:

https://padtincevents.webex.com or click Here to download it and watch it with the WebEx player.

Click on PADT ANSYS Webinar Series and then find the recording from August 11, 2011.

Several attendees asked if we had an example of using python to bring up a GUI on top of workbench. We did not. This is how The Focus articles are born.

The Basics

In case you missed the webinar or didn’t follow some of it, here are the important points:

  • Workbench has IronPython built into it from the ground up
  • You can record whatever you do in the GUI to create scripts
  • You can read those scripts in to control Workbench
  • ANSYS uses IronPython because it talks to Windows programs
  • Get detailed info at www.python.org, www.ironpython.net, and www.ironpython.info

Most of the examples below are derived from information I got at: http://www.ironpython.info/index.php/Contents#Windows_Forms

We will be making a GUI for the famous Tower of Test application.  We will need a place to enter in the size of the tower, a pressure load. Then a simple table showing the results when we get them. And of course, buttons to cancel and execute.

If you are already an experienced Windows programmer, this will be a slam dunk. If you are not, then you will need to make sure you learn your way around the MSDN .Net Class Library Reference:

http://msdn.microsoft.com/en-US/library/ms229335(v=VS.80).aspx

That is where you find properties and methods for the GUI.  We will be spending most of our time in System.Windows.Form.

Why .NET?

Right now, some of you might be asking “Why .NET?”  Python supports probably over a dozen different GUI’s.  I am using .NET for a few reasons:

  1. It is built into IronPython. No need to download a library and install it right
  2. It will have a consistent look and feel to Workbench
  3. More people know .NET than the other GUI libraries.

If you are anit-microsoft, you can poke around and find a library that fits your needs.  The process will be the same.

Crawl: Always Start with Hello World

Anytime I try and learn a new language, I usually find a “Hello World” Example.  And, thanks to the internet I found one that only needed slight modification:

   1: import clr

   2: clr.AddReference('System.Windows.Forms')

   3:

   4: from System.Windows.Forms import Application, Form, Label

   5:

   6: form = Form(Text="Hello World Form")

   7: label = Label(Text="Hello World!")

   8:

   9: form.Controls.Add(label)

  10:

  11: Form.ShowDialog(form)

So first, we access the huge number of Windows tools by importing clr.  Then, get a reference to Windows Forms.  Once that is done we need to import more from Windows Forms. This is a big tool box so it has lots of levels you have to dig through.  For this guy we need to be able to do the form itself (the window) and a label (text in the window).

Now that we have our objects, we create a form with the title “Hello World Form” and a label with the text “Hello World!”  We then use the Add method to add the label control to the form. Last, we use the Show Dialog method to show the form.  TADA.

To give it a try, copy and paste this into your own text editor and then go into Workbench and choose:

File->Scripting->Open Command Window…

Then paste in the script.  Or you can save it to a file and read it in. I like to paste so I can see my error messages as it goes.

image

Now you are a Workbench GUI programmer.  Put it on your resume!

Walk: Add Some Controls to the Window

Next we want to do a bit more than saying hello.

The important thing I learned that I just didn’t’ get before is that you have to make a class out of your form and any functions that get called when the user clicks on a button or interacts in any way with the form.  You do this because Windows will then pass information about the form back to the event function.

In the example below is like “Hello World” to start, then we make 4 text boxes with labels for the user to enter data into.  We then add an OK and a Cancel button.  The code is commented with an explanation of what is going on.  If something does not make sense, look it up in a python reference or ask someone who is a python programmer.

   1: # --  Bring in the .NET stuff, and reference Forms and drawing

   2: import clr

   3: clr.AddReference('System.Windows.Forms')

   4: clr.AddReference('System.Drawing')

   5:

   6: #   get the objects you need from forms and drawing

   7: from System.Windows.Forms import Form, Label, Button, TextBox

   8: from System.Drawing import Point, ContentAlignment

   9:

  10: #-- The way python works with events, you have to make a class out of your form.  

  11: #   Then the objects 

  12: #    you add are available in event functions.

  13: class TowerForm(Form):

  14:     def __init__(self):

  15: #-- First, make the form (self)

  16:         self.text="Tower Simulation Tool"

  17:         self.Height=500

  18:         self.Width=250

  19:

  20: #-- Create a Simple Text Label describing the tool

  21: #   it is not self.label because we are not going to access it in event functions

  22:         lbl1 = Label()

  23:         lbl1.Text = "This is the Tower of Test Tool. \n\

  24:             Please Enter values and click OK to run the \

  25:             model and get new results\n"

  26:         lbl1.Height = 50

  27:         lbl1.Width = 200

  28:

  29: #-- Create the Text Labels and text boxes to get Dimensions and Pressure

  30: #   We need to get to the textboxes so we will make them part of the class

  31: #

  32: #   some constants to make the positioning easier

  33:         x1 = 10

  34:         y1 = 80

  35:         w1 = 70

  36:         w2 = 80

  37:         x2 = x1 + w1 + 10

  38: #   Make the labels for the text boxes.  This is done in the Labe() call with 

  39: #   Name = Value arguments 

  40: #   instead of object.name = value to save space.  All in one line instead of 

  41: #   four lines.

  42: #     The alighment is done so that the labels are all right justified to line 

  43: #     up with the boxes. Set ContentAlignment.MiddleRight to mr to save space

  44:         mr = ContentAlignment.MiddleRight

  45:         lb_Length=Label(Text="Length", Width=w1,TextAlign=mr)

  46:         lb_Width  = Label(Text="Width",   Width = w1, TextAlign=mr)

  47:         lb_Height = Label(Text="Height",  Width = w1, TextAlign=mr)

  48:         lb_Press  = Label(Text="Pressure",Width = w1, TextAlign=mr)

  49:

  50: #   Make the text boxes. Note that these are put in the self. class. 

  51: #   We do this so that when the OK 

  52: #   button is pushed, we have access to the text boxes and therefore the values 

  53: #   typed within

  54:

  55:         self.tb_Length = TextBox(Width = w2)

  56:         self.tb_Width  = TextBox(Width = w2)

  57:         self.tb_Height = TextBox(Width = w2)

  58:         self.tb_Press  = TextBox(Width = w2)

  59:

  60: #   Specify the location for the label and the text boxes.  Move down by 

  61: #   30 after each line

  62:         lb_Length.Location = Point(x1,y1)

  63:         self.tb_Length.Location = Point(x2,y1)

  64:         y1 = y1 + 30

  65:         lb_Width.Location = Point(x1,y1)

  66:         self.tb_Width.Location = Point(x2,y1)

  67:         y1 = y1 + 30

  68:         lb_Height.Location = Point(x1,y1)

  69:         self.tb_Height.Location = Point(x2,y1)

  70:         y1 = y1 + 30

  71:         lb_Press.Location = Point(x1,y1)

  72:         self.tb_Press.Location = Point(x2,y1)

  73:

  74: #   Make an OK and a Cancel Button

  75:         okBut = Button(Text="OK", Width=50)

  76:         okBut.Location = Point(30,430)

  77: #       This is where you specify the funtion to be called when the button is clicked

  78: #       note that you call the function self.functionname.

  79:         okBut.Click += self.okButPressed

  80:

  81:         cancelBut = Button(Text="Cancel", Width=50)

  82:         cancelBut.Location = Point(110,430)

  83:         cancelBut.Click += self.cancelButPressed

  84:

  85: #   Put everything on the form (some sort of list and loop would make this easier)

  86:         self.Controls.Add(lbl1)

  87:         self.Controls.Add(lb_Length)

  88:         self.Controls.Add(self.tb_Length)

  89:         self.Controls.Add(lb_Width)

  90:         self.Controls.Add(self.tb_Width)

  91:         self.Controls.Add(lb_Height)

  92:         self.Controls.Add(self.tb_Height)

  93:         self.Controls.Add(lb_Press)

  94:         self.Controls.Add(self.tb_Press)

  95:         self.Controls.Add(okBut)

  96:         self.Controls.Add(cancelBut)

  97:

  98: #-- Now define the button event functions as part of the class

  99: #

 100: # Cancel simply closes the form down.  Nothing fancy

 101:     def cancelButPressed(self, sender, args ):

 102:         print 'Closing the Window... Bye...\n'

 103:         self.Close()

 104:

 105: # OK prints the values of the text boxes to the console

 106: #   This will be replaced with calls to workbench for the next step

 107:     def okButPressed(self, sender, args ):

 108:         print 'Values Entered:\n'

 109:         print '  Length: %s\n' %self.tb_Length.Text

 110:         print '   Width: %s\n' %self.tb_Width.Text

 111:         print '  Height: %s\n' %self.tb_Height.Text

 112:         print 'Pressure: %s\n' %self.tb_Press.Text

 113:         self.Close()

 114: #---------End of class definition

 115:

 116:

 117: # Instantiate the form and make the form visible

 118: print '=======================================\n'

 119: print 'Opening the Windows...\n'

 120: myForm = TowerForm()

 121: Form.ShowDialog(myForm)

 122:

If you have not programmed a go dialog box in a while by hand, instead of using a WYSIWIG GUI builder, you can see why.  Lots of lines… lots of lines…
Here is the file without the numbers in it so you can run it in Workbench:  TowerToolTextOnly.py

imageRun:  Send Values to Workbench, Update Model, Get Results

If you remember the previous article on Driving Workbench from Excel this will all look familiar.  If you don’t review the article, especially the part about getting parameters in and out of Workbench with Python.  To finish our example we will use the same basic code to:

  • Get the current values from Workbench and set our dialog to show those values
  • When the user hits OK:
    • Set the Workbench parameters to the current dialog box parameters
    • Update the model
    • Get the resulting deflection and display it.

The code is shown below, with the bits added for the Workbench interaction highlighted.  Note that we also introduced the specification of font properties on a label.  Making it even more fancy.

   1: # --  Bring in the .NET stuff, and reference Forms and drawing

   2: import clr

   3: clr.AddReference('System.Windows.Forms')

   4: clr.AddReference('System.Drawing')

   5:

   6: #   get the objects you need from forms and drawing

   7: from System.Windows.Forms import Form, Label, Button, TextBox

   8: from System.Drawing import Point, ContentAlignment,

Font, FontStyle, GraphicsUnit

   9:

  10: #-- The way python works with events, you have to make a class out of your form.  

  11: #   Then the objects 

  12: #    you add are available in event functions.

  13: class TowerForm(Form):

  14:     def __init__(self):

  15: #-- First, make the form (self)

  16:         self.text="Tower Simulation Tool"

  17:         self.Height=500

  18:         self.Width=250

  19:

  20: #-- Create a Simple Text Label describing the tool

  21: #   it is not self.label because we are not going to access it in event functions

  22:         lbl1 = Label()

  23:         lbl1.Text = "This is the Tower of Test Tool. \n\

  24:             Please Enter values and click OK to run the \

  25:             model and get new results\n"

  26:         lbl1.Height = 50

  27:         lbl1.Width = 200

  28:

  29: #-- Create the Text Labels and text boxes to get Dimensions and Pressure

  30: #   We need to get to the textboxes so we will make them part of the class

  31: #

  32: #   some constants to make the positioning easier

  33:         x1 = 10

  34:         y1 = 80

  35:         w1 = 70

  36:         w2 = 80

  37:         x2 = x1 + w1 + 10

  38: #   Make the labels for the text boxes.  This is done in the Labe() call with 

  39: #   Name = Value arguments 

  40: #   instead of object.name = value to save space.  All in one line instead of 

  41: #   four lines.

  42: #     The alighment is done so that the labels are all right justified to line 

  43: #     up with the boxes. Set ContentAlignment.MiddleRight to mr to save space

  44:         mr = ContentAlignment.MiddleRight

  45:         lb_Length=Label(Text="Length", Width=w1,TextAlign=mr)

  46:         lb_Width  = Label(Text="Width",   Width = w1, TextAlign=mr)

  47:         lb_Height = Label(Text="Height",  Width = w1, TextAlign=mr)

  48:         lb_Press  = Label(Text="Pressure",Width = w1, TextAlign=mr)

  49:

  50: #   Make a label to give status of the update in:

  51:

self.updlbl = Label(Text=” “,Width=200, Height=100,\

  52:

TextAlign=ContentAlignment.BottomCenter)

  53:

self.updlbl.Location = Point(0,200)

  54:

self.updlbl.Font = \

  55:

Font(‘Verdana’,10, FontStyle.Bold, GraphicsUnit.Point,0)

  56:

  57: #   Make the text boxes. Note that these are put in the self. class. 

  58: #   We do this so that when the OK 

  59: #   button is pushed, we have access to the text boxes and therefore the values 

  60: #   typed within

  61:

  62:         self.tb_Length = TextBox(Width = w2)

  63:         self.tb_Width  = TextBox(Width = w2)

  64:         self.tb_Height = TextBox(Width = w2)

  65:         self.tb_Press  = TextBox(Width = w2)

  66:

  67: #   Specify the location for the label and the text boxes.  Move down by 

  68: #   30 after each line

  69:         lb_Length.Location = Point(x1,y1)

  70:         self.tb_Length.Location = Point(x2,y1)

  71:         y1 = y1 + 30

  72:         lb_Width.Location = Point(x1,y1)

  73:         self.tb_Width.Location = Point(x2,y1)

  74:         y1 = y1 + 30

  75:         lb_Height.Location = Point(x1,y1)

  76:         self.tb_Height.Location = Point(x2,y1)

  77:         y1 = y1 + 30

  78:         lb_Press.Location = Point(x1,y1)

  79:         self.tb_Press.Location = Point(x2,y1)

  80:

  81:         self.tb_Length.Text = Parameters.GetParameter(Name="P1").Expression

  82:         self.tb_Width.Text = Parameters.GetParameter(Name="P2").Expression

  83:         self.tb_Height.Text = Parameters.GetParameter(Name="P3").Expression

  84:         self.tb_Press.Text = Parameters.GetParameter(Name="P5").Expression

  85:

  86: #   Make an OK and a Cancel Button

  87:         okBut = Button(Text="OK", Width=50)

  88:         okBut.Location = Point(30,430)

  89: #       This is where you specify the funtion to be called when the button is clicked

  90: #       note that you call the function self.functionname.

  91:         okBut.Click += self.okButPressed

  92:

  93:         cancelBut = Button(Text="Cancel", Width=50)

  94:         cancelBut.Location = Point(110,430)

  95:         cancelBut.Click += self.cancelButPressed

  96:

  97: #   Put everything on the form (some sort of list and loop would make this easier)

  98:         self.Controls.Add(lbl1)

  99:         self.Controls.Add(lb_Length)

 100:         self.Controls.Add(self.tb_Length)

 101:         self.Controls.Add(lb_Width)

 102:         self.Controls.Add(self.tb_Width)

 103:         self.Controls.Add(lb_Height)

 104:         self.Controls.Add(self.tb_Height)

 105:         self.Controls.Add(lb_Press)

 106:         self.Controls.Add(self.tb_Press)

 107:

self.Controls.Add(self.updlbl)

 108:         self.Controls.Add(okBut)

 109:         self.Controls.Add(cancelBut)

 110:

 111: #-- Now define the button event functions as part of the class

 112: #

 113: # Cancel simply closes the form down.  Nothing fancy

 114:     def cancelButPressed(self, sender, args ):

 115:         print 'Closing the Window... Bye...\n'

 116:         self.Close()

 117:

 118: # OK prints the values of the text boxes to the console

 119: #   This will be replaced with calls to workbench for the next step

 120:     def okButPressed(self, sender, args ):

 121:

 122: # In Workbench: Get objects for all the parameters you need access to

 123: #   Remember, it goes by parameter name, not the name you give them.

 124:

lenParam = Parameters.GetParameter(Name=“P1”)

 125:

widParam = Parameters.GetParameter(Name=“P2”)

 126:

hgtParam = Parameters.GetParameter(Name=“P3”)

 127:

prsParam = Parameters.GetParameter(Name=“P5”)

 128: # don't forget the resulting deflection output parameter:

 129:

defParam = Parameters.GetParameter(Name=“P4”)

 130:

 131: # Now set the workbench values to the values from the dialog

 132:

lenParam.Expression = self.tb_Length.Text

 133:

widParam.Expression = self.tb_Width.Text

 134:

hgtParam.Expression = self.tb_Height.Text

 135:

prsParam.Expression = self.tb_Press.Text

 136:

self.updlbl.Text = “Updating”

 137:

 138:

Update()

 139:

dflValue = defParam.Value.Value

 140:

self.updlbl.Text = “Deflection is %s in\n” %dflValue

 141:

 142:

 143: #---------End of class definition

 144:

 145:

 146: # Instantiate the form and make the form visible

 147: print '=======================================\n'

 148: print 'Opening the Windows...\n'

 149: myForm = TowerForm()

 150: Form.ShowDialog(myForm)

 151:

 

Next Steps and Suggestions

And that in a nutshell is the basics of putting a GUI on Workbench.  By using the calls to Workbench, .NET or any other complaint application, you can script away and do what you want.

Much more can be done.  You could have the solver you are running in update and create some plots, and then use python to find those plots (say search for any *.jpg files in the project director created after the OK button was pushed) and insert them into PowerPoint with text that you also gather.

There really are no limitations.

To learn more, try stuff.  Get to know the MSDN documentation to know what you can do to make a more professional GUI.  The Windows Forms part of ironpython.info is a great resource for this and points to MSDN as needed.

There is a lot more you can do with ANSYS  Workbench and the various solvers.  And with each release, workbench will add and document more.

If you write a cool application you can share, make sure you put it on XANSYS or send it to Sheldon at ANSYS.net.