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 on YouTube here.
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)
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:
- It is built into IronPython. No need to download a library and install it right
- It will have a consistent look and feel to Workbench
- 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.
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:
Run: 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.