22 #License along with this library; if not, write to the Free Software |
22 #License along with this library; if not, write to the Free Software |
23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
24 |
24 |
25 import wx |
25 import wx |
26 |
26 |
27 from graphics import * |
27 from graphics.GraphicCommons import INPUT, INOUT, OUTPUT |
|
28 from graphics.FBD_Objects import FBD_Variable |
|
29 from BlockPreviewDialog import BlockPreviewDialog |
28 |
30 |
29 #------------------------------------------------------------------------------- |
31 #------------------------------------------------------------------------------- |
30 # Helpers |
32 # Helpers |
31 #------------------------------------------------------------------------------- |
33 #------------------------------------------------------------------------------- |
32 |
34 |
|
35 # Dictionaries containing correspondence between variable block class and string |
|
36 # to be shown in Class combo box in both sense |
33 VARIABLE_CLASSES_DICT = {INPUT : _("Input"), |
37 VARIABLE_CLASSES_DICT = {INPUT : _("Input"), |
34 INOUT : _("InOut"), |
38 INOUT : _("InOut"), |
35 OUTPUT : _("Output")} |
39 OUTPUT : _("Output")} |
36 VARIABLE_CLASSES_DICT_REVERSE = dict( |
40 VARIABLE_CLASSES_DICT_REVERSE = dict( |
37 [(value, key) for key, value in VARIABLE_CLASSES_DICT.iteritems()]) |
41 [(value, key) for key, value in VARIABLE_CLASSES_DICT.iteritems()]) |
38 |
42 |
39 #------------------------------------------------------------------------------- |
43 #------------------------------------------------------------------------------- |
40 # Create New Variable Dialog |
44 # Set Variable Parameters Dialog |
41 #------------------------------------------------------------------------------- |
45 #------------------------------------------------------------------------------- |
42 |
46 |
43 class FBDVariableDialog(wx.Dialog): |
47 """ |
44 |
48 Class that implements a dialog for defining parameters of a FBD variable graphic |
45 def __init__(self, parent, controller, transition = ""): |
49 element |
46 wx.Dialog.__init__(self, parent, |
50 """ |
|
51 |
|
52 class FBDVariableDialog(BlockPreviewDialog): |
|
53 |
|
54 def __init__(self, parent, controller, tagname): |
|
55 """ |
|
56 Constructor |
|
57 @param parent: Parent wx.Window of dialog for modal |
|
58 @param controller: Reference to project controller |
|
59 @param tagname: Tagname of project POU edited |
|
60 """ |
|
61 BlockPreviewDialog.__init__(self, parent, controller, tagname, |
47 size=wx.Size(400, 380), title=_('Variable Properties')) |
62 size=wx.Size(400, 380), title=_('Variable Properties')) |
48 |
63 |
|
64 # Create dialog main sizer |
49 main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=4, vgap=10) |
65 main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=4, vgap=10) |
50 main_sizer.AddGrowableCol(0) |
66 main_sizer.AddGrowableCol(0) |
51 main_sizer.AddGrowableRow(2) |
67 main_sizer.AddGrowableRow(2) |
52 |
68 |
|
69 # Create a sizer for dividing FBD variable parameters in two columns |
53 column_sizer = wx.BoxSizer(wx.HORIZONTAL) |
70 column_sizer = wx.BoxSizer(wx.HORIZONTAL) |
54 main_sizer.AddSizer(column_sizer, border=20, |
71 main_sizer.AddSizer(column_sizer, border=20, |
55 flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) |
72 flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) |
56 |
73 |
|
74 # Create a sizer for left column |
57 left_gridsizer = wx.FlexGridSizer(cols=1, hgap=0, rows=4, vgap=5) |
75 left_gridsizer = wx.FlexGridSizer(cols=1, hgap=0, rows=4, vgap=5) |
58 left_gridsizer.AddGrowableCol(0) |
76 left_gridsizer.AddGrowableCol(0) |
59 column_sizer.AddSizer(left_gridsizer, 1, border=5, |
77 column_sizer.AddSizer(left_gridsizer, 1, border=5, |
60 flag=wx.GROW|wx.RIGHT) |
78 flag=wx.GROW|wx.RIGHT) |
61 |
79 |
|
80 # Create label for variable class |
62 class_label = wx.StaticText(self, label=_('Class:')) |
81 class_label = wx.StaticText(self, label=_('Class:')) |
63 left_gridsizer.AddWindow(class_label, flag=wx.GROW) |
82 left_gridsizer.AddWindow(class_label, flag=wx.GROW) |
64 |
83 |
|
84 # Create a combo box for defining variable class |
65 self.Class = wx.ComboBox(self, style=wx.CB_READONLY) |
85 self.Class = wx.ComboBox(self, style=wx.CB_READONLY) |
66 self.Bind(wx.EVT_COMBOBOX, self.OnClassChanged, self.Class) |
86 self.Bind(wx.EVT_COMBOBOX, self.OnClassChanged, self.Class) |
67 left_gridsizer.AddWindow(self.Class, flag=wx.GROW) |
87 left_gridsizer.AddWindow(self.Class, flag=wx.GROW) |
68 |
88 |
69 execution_order_label = wx.StaticText(self, label=_('Execution Order:')) |
89 # Create label for variable execution order |
|
90 execution_order_label = wx.StaticText(self, |
|
91 label=_('Execution Order:')) |
70 left_gridsizer.AddWindow(execution_order_label, flag=wx.GROW) |
92 left_gridsizer.AddWindow(execution_order_label, flag=wx.GROW) |
71 |
93 |
|
94 # Create spin control for defining variable execution order |
72 self.ExecutionOrder = wx.SpinCtrl(self, min=0, style=wx.SP_ARROW_KEYS) |
95 self.ExecutionOrder = wx.SpinCtrl(self, min=0, style=wx.SP_ARROW_KEYS) |
73 self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged, self.ExecutionOrder) |
96 self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged, |
|
97 self.ExecutionOrder) |
74 left_gridsizer.AddWindow(self.ExecutionOrder, flag=wx.GROW) |
98 left_gridsizer.AddWindow(self.ExecutionOrder, flag=wx.GROW) |
75 |
99 |
|
100 # Create a sizer for right column |
76 right_gridsizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=0) |
101 right_gridsizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=0) |
77 right_gridsizer.AddGrowableCol(0) |
102 right_gridsizer.AddGrowableCol(0) |
78 right_gridsizer.AddGrowableRow(2) |
103 right_gridsizer.AddGrowableRow(2) |
79 column_sizer.AddSizer(right_gridsizer, 1, border=5, |
104 column_sizer.AddSizer(right_gridsizer, 1, border=5, |
80 flag=wx.GROW|wx.LEFT) |
105 flag=wx.GROW|wx.LEFT) |
81 |
106 |
|
107 # Create label for variable expression |
82 name_label = wx.StaticText(self, label=_('Expression:')) |
108 name_label = wx.StaticText(self, label=_('Expression:')) |
83 right_gridsizer.AddWindow(name_label, border=5, flag=wx.GROW|wx.BOTTOM) |
109 right_gridsizer.AddWindow(name_label, border=5, flag=wx.GROW|wx.BOTTOM) |
84 |
110 |
|
111 # Create text control for defining variable expression |
85 self.Expression = wx.TextCtrl(self) |
112 self.Expression = wx.TextCtrl(self) |
86 self.Bind(wx.EVT_TEXT, self.OnExpressionChanged, self.Expression) |
113 self.Bind(wx.EVT_TEXT, self.OnExpressionChanged, self.Expression) |
87 right_gridsizer.AddWindow(self.Expression, flag=wx.GROW) |
114 right_gridsizer.AddWindow(self.Expression, flag=wx.GROW) |
88 |
115 |
|
116 # Create a list box to selected variable expression in the list of |
|
117 # variables defined in POU |
89 self.VariableName = wx.ListBox(self, size=wx.Size(0, 120), |
118 self.VariableName = wx.ListBox(self, size=wx.Size(0, 120), |
90 style=wx.LB_SINGLE|wx.LB_SORT) |
119 style=wx.LB_SINGLE|wx.LB_SORT) |
91 self.Bind(wx.EVT_LISTBOX, self.OnNameChanged, self.VariableName) |
120 self.Bind(wx.EVT_LISTBOX, self.OnNameChanged, self.VariableName) |
92 right_gridsizer.AddWindow(self.VariableName, flag=wx.GROW) |
121 right_gridsizer.AddWindow(self.VariableName, flag=wx.GROW) |
93 |
122 |
94 preview_label = wx.StaticText(self, label=_('Preview:')) |
123 # Add preview panel and associated label to sizers |
95 main_sizer.AddWindow(preview_label, border=20, |
124 main_sizer.AddWindow(self.PreviewLabel, border=20, |
96 flag=wx.GROW|wx.LEFT|wx.RIGHT) |
125 flag=wx.GROW|wx.LEFT|wx.RIGHT) |
97 |
|
98 self.Preview = wx.Panel(self, |
|
99 style=wx.TAB_TRAVERSAL|wx.SIMPLE_BORDER) |
|
100 self.Preview.SetBackgroundColour(wx.Colour(255,255,255)) |
|
101 setattr(self.Preview, "GetDrawingMode", lambda:FREEDRAWING_MODE) |
|
102 setattr(self.Preview, "GetScaling", lambda:None) |
|
103 setattr(self.Preview, "IsOfType", controller.IsOfType) |
|
104 self.Preview.Bind(wx.EVT_PAINT, self.OnPaint) |
|
105 main_sizer.AddWindow(self.Preview, border=20, |
126 main_sizer.AddWindow(self.Preview, border=20, |
106 flag=wx.GROW|wx.LEFT|wx.RIGHT) |
127 flag=wx.GROW|wx.LEFT|wx.RIGHT) |
107 |
128 |
108 button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) |
129 # Add buttons sizer to sizers |
109 self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) |
130 main_sizer.AddSizer(self.ButtonSizer, border=20, |
110 main_sizer.AddSizer(button_sizer, border=20, |
|
111 flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) |
131 flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) |
112 |
132 |
113 self.SetSizer(main_sizer) |
133 self.SetSizer(main_sizer) |
114 |
134 |
115 self.Transition = transition |
135 # Set options that can be selected in class combo box |
116 self.Variable = None |
|
117 self.VarList = [] |
|
118 self.MinVariableSize = None |
|
119 |
|
120 for choice in VARIABLE_CLASSES_DICT.itervalues(): |
136 for choice in VARIABLE_CLASSES_DICT.itervalues(): |
121 self.Class.Append(choice) |
137 self.Class.Append(choice) |
122 self.Class.SetStringSelection(VARIABLE_CLASSES_DICT[INPUT]) |
138 self.Class.SetStringSelection(VARIABLE_CLASSES_DICT[INPUT]) |
123 |
139 |
|
140 # Get list of variables defined in POU |
|
141 self.VarList = { |
|
142 var["Name"]: (var["Class"], var["Type"]) |
|
143 for var in self.Controller.GetEditedElementInterfaceVars( |
|
144 self.TagName) |
|
145 if var["Edit"]} |
|
146 |
|
147 # Add POU name to variable list if POU is a function |
|
148 returntype = self.Controller.GetEditedElementInterfaceReturnType( |
|
149 self.TagName) |
|
150 if returntype is not None: |
|
151 self.VarList[ |
|
152 self.Controller.GetEditedElementName(self.TagName)] = \ |
|
153 ("Output", returntype) |
|
154 |
|
155 # Add POU name if POU is a transition |
|
156 words = tagname.split("::") |
|
157 if words[0] == "T": |
|
158 self.VarList[words[2]] = ("Output", "BOOL") |
|
159 |
|
160 # Refresh values in name list box |
124 self.RefreshNameList() |
161 self.RefreshNameList() |
|
162 |
|
163 # Class combo box is default control having keyboard focus |
125 self.Class.SetFocus() |
164 self.Class.SetFocus() |
126 |
165 |
127 def SetPreviewFont(self, font): |
|
128 self.Preview.SetFont(font) |
|
129 |
|
130 def RefreshNameList(self): |
166 def RefreshNameList(self): |
131 selected = self.Expression.GetValue() |
167 """ |
132 var_class = VARIABLE_CLASSES_DICT_REVERSE[self.Class.GetStringSelection()] |
168 Called to refresh names in name list box |
|
169 """ |
|
170 # Get variable class to select POU variable applicable |
|
171 var_class = VARIABLE_CLASSES_DICT_REVERSE[ |
|
172 self.Class.GetStringSelection()] |
|
173 |
|
174 # Refresh names in name list box by selecting variables in POU variables |
|
175 # list that can be applied to variable class |
133 self.VariableName.Clear() |
176 self.VariableName.Clear() |
134 for name, var_type, value_type in self.VarList: |
177 for name, (var_type, value_type) in self.VarList.iteritems(): |
135 if var_type != "Input" or var_class == INPUT: |
178 if var_type != "Input" or var_class == INPUT: |
136 self.VariableName.Append(name) |
179 self.VariableName.Append(name) |
137 if selected != "" and self.VariableName.FindString(selected) != wx.NOT_FOUND: |
180 |
|
181 # Get variable expression and select corresponding value in name list |
|
182 # box if it exists |
|
183 selected = self.Expression.GetValue() |
|
184 if (selected != "" and |
|
185 self.VariableName.FindString(selected) != wx.NOT_FOUND): |
138 self.VariableName.SetStringSelection(selected) |
186 self.VariableName.SetStringSelection(selected) |
139 else: |
187 else: |
140 self.VariableName.SetSelection(wx.NOT_FOUND) |
188 self.VariableName.SetSelection(wx.NOT_FOUND) |
|
189 |
|
190 # Disable name list box if no name present inside |
141 self.VariableName.Enable(self.VariableName.GetCount() > 0) |
191 self.VariableName.Enable(self.VariableName.GetCount() > 0) |
142 |
192 |
143 def SetMinVariableSize(self, size): |
|
144 self.MinVariableSize = size |
|
145 |
|
146 def SetVariables(self, vars): |
|
147 self.VarList = vars |
|
148 self.RefreshNameList() |
|
149 |
|
150 def SetValues(self, values): |
193 def SetValues(self, values): |
151 value_type = values.get("type", None) |
194 """ |
152 value_name = values.get("name", None) |
195 Set default variable parameters |
153 if value_type: |
196 @param values: Variable parameters values |
154 self.Class.SetStringSelection(VARIABLE_CLASSES_DICT[value_type]) |
197 """ |
|
198 |
|
199 # Get class parameter value |
|
200 var_class = values.get("class", None) |
|
201 if var_class is not None: |
|
202 # Set class selected in class combo box |
|
203 self.Class.SetStringSelection(VARIABLE_CLASSES_DICT[var_class]) |
|
204 # Refresh names in name list box according to var class |
155 self.RefreshNameList() |
205 self.RefreshNameList() |
156 if value_name: |
206 |
157 self.Expression.ChangeValue(value_name) |
207 # For each parameters defined, set corresponding control value |
158 if self.VariableName.FindString(value_name) != wx.NOT_FOUND: |
208 for name, value in values.items(): |
159 self.VariableName.SetStringSelection(value_name) |
209 |
160 else: |
210 # Parameter is variable expression |
161 self.VariableName.SetSelection(wx.NOT_FOUND) |
211 if name == "expression": |
162 if "executionOrder" in values: |
212 # Set expression text control value |
163 self.ExecutionOrder.SetValue(values["executionOrder"]) |
213 self.Expression.ChangeValue(value) |
|
214 # Select corresponding text in name list box if it exists |
|
215 if self.VariableName.FindString(value) != wx.NOT_FOUND: |
|
216 self.VariableName.SetStringSelection(value) |
|
217 else: |
|
218 self.VariableName.SetSelection(wx.NOT_FOUND) |
|
219 |
|
220 # Parameter is variable execution order |
|
221 elif name == "executionOrder": |
|
222 self.ExecutionOrder.SetValue(value) |
|
223 |
|
224 # Refresh preview panel |
164 self.RefreshPreview() |
225 self.RefreshPreview() |
165 |
226 |
166 def GetValues(self): |
227 def GetValues(self): |
167 values = {} |
228 """ |
168 values["type"] = VARIABLE_CLASSES_DICT_REVERSE[self.Class.GetStringSelection()] |
229 Return block parameters defined in dialog |
169 values["name"] = self.Expression.GetValue() |
230 @return: {parameter_name: parameter_value,...} |
170 values["value_type"] = None |
231 """ |
171 for var_name, var_type, value_type in self.VarList: |
232 expression = self.Expression.GetValue() |
172 if var_name == values["name"]: |
233 values = { |
173 values["value_type"] = value_type |
234 "class": VARIABLE_CLASSES_DICT_REVERSE[ |
174 values["width"], values["height"] = self.Variable.GetSize() |
235 self.Class.GetStringSelection()], |
175 values["executionOrder"] = self.ExecutionOrder.GetValue() |
236 "expression": expression, |
|
237 "var_type": self.VarList.get(expression, (None, None))[1], |
|
238 "executionOrder": self.ExecutionOrder.GetValue()} |
|
239 values["width"], values["height"] = self.Element.GetSize() |
176 return values |
240 return values |
177 |
241 |
178 def OnOK(self, event): |
242 def OnOK(self, event): |
|
243 """ |
|
244 Called when dialog OK button is pressed |
|
245 Test if parameters defined are valid |
|
246 @param event: wx.Event from OK button |
|
247 """ |
179 message = None |
248 message = None |
|
249 |
|
250 # Test that an expression have been selected or typed by user |
180 value = self.Expression.GetValue() |
251 value = self.Expression.GetValue() |
181 if value == "": |
252 if value == "": |
182 message = _("At least a variable or an expression must be selected!") |
253 message = _("At least a variable or an expression must be selected!") |
|
254 |
|
255 # Show error message if an error is detected |
183 if message is not None: |
256 if message is not None: |
184 message = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) |
257 self.ShowErrorMessage(message) |
185 message.ShowModal() |
258 |
186 message.Destroy() |
|
187 else: |
259 else: |
188 self.EndModal(wx.ID_OK) |
260 # Call BlockPreviewDialog function |
|
261 BlockPreviewDialog.OnOK(self, event) |
189 |
262 |
190 def OnClassChanged(self, event): |
263 def OnClassChanged(self, event): |
|
264 """ |
|
265 Called when variable class value changed |
|
266 @param event: wx.ComboBoxEvent |
|
267 """ |
|
268 # Refresh name list box values |
191 self.RefreshNameList() |
269 self.RefreshNameList() |
|
270 |
192 self.RefreshPreview() |
271 self.RefreshPreview() |
193 event.Skip() |
272 event.Skip() |
194 |
273 |
195 def OnNameChanged(self, event): |
274 def OnNameChanged(self, event): |
196 self.Expression.ChangeValue( |
275 """ |
197 self.VariableName.GetStringSelection()) |
276 Called when name selected in name list box changed |
|
277 @param event: wx.ListBoxEvent |
|
278 """ |
|
279 # Change expression test control value to the value selected in name |
|
280 # list box if value selected is valid |
|
281 if self.VariableName.GetSelection() != wx.NOT_FOUND: |
|
282 self.Expression.ChangeValue(self.VariableName.GetStringSelection()) |
|
283 |
198 self.RefreshPreview() |
284 self.RefreshPreview() |
199 event.Skip() |
285 event.Skip() |
200 |
286 |
201 def OnExpressionChanged(self, event): |
287 def OnExpressionChanged(self, event): |
202 expression = self.Expression.GetValue() |
288 """ |
|
289 Called when expression text control is changed by user |
|
290 @param event: wx.ListBoxEvent |
|
291 """ |
|
292 # Select the corresponding value in name list box if it exists |
203 self.VariableName.SetSelection( |
293 self.VariableName.SetSelection( |
204 self.VariableName.FindString(expression)) |
294 self.VariableName.FindString(self.Expression.GetValue())) |
|
295 |
205 self.RefreshPreview() |
296 self.RefreshPreview() |
206 event.Skip() |
297 event.Skip() |
207 |
298 |
208 def OnExecutionOrderChanged(self, event): |
299 def OnExecutionOrderChanged(self, event): |
|
300 """ |
|
301 Called when block execution control value changed |
|
302 @param event: wx.SpinEvent |
|
303 """ |
209 self.RefreshPreview() |
304 self.RefreshPreview() |
210 event.Skip() |
305 event.Skip() |
211 |
306 |
212 def RefreshPreview(self): |
307 def RefreshPreview(self): |
213 dc = wx.ClientDC(self.Preview) |
308 """ |
214 dc.SetFont(self.Preview.GetFont()) |
309 Refresh preview panel of graphic element |
215 dc.Clear() |
310 Override BlockPreviewDialog function |
|
311 """ |
|
312 # Get expression value to put in FBD variable element |
216 name = self.Expression.GetValue() |
313 name = self.Expression.GetValue() |
217 type = "" |
314 |
218 for var_name, var_type, value_type in self.VarList: |
315 # Set graphic element displayed, creating a FBD variable element |
219 if var_name == name: |
316 self.Element = FBD_Variable(self.Preview, |
220 type = value_type |
317 VARIABLE_CLASSES_DICT_REVERSE[ |
221 classtype = VARIABLE_CLASSES_DICT_REVERSE[self.Class.GetStringSelection()] |
318 self.Class.GetStringSelection()], |
222 self.Variable = FBD_Variable(self.Preview, classtype, name, type, executionOrder = self.ExecutionOrder.GetValue()) |
319 name, |
223 width, height = self.MinVariableSize |
320 self.VarList.get(name, ("", ""))[1], |
224 min_width, min_height = self.Variable.GetMinSize() |
321 executionOrder = self.ExecutionOrder.GetValue()) |
225 width, height = max(min_width, width), max(min_height, height) |
322 |
226 self.Variable.SetSize(width, height) |
323 # Call BlockPreviewDialog function |
227 clientsize = self.Preview.GetClientSize() |
324 BlockPreviewDialog.RefreshPreview(self) |
228 x = (clientsize.width - width) / 2 |
325 |
229 y = (clientsize.height - height) / 2 |
326 |
230 self.Variable.SetPosition(x, y) |
|
231 self.Variable.Draw(dc) |
|
232 |
|
233 def OnPaint(self, event): |
|
234 self.RefreshPreview() |
|
235 event.Skip() |
|