36 Class that implements a generic dialog containing a preview panel for displaying |
36 Class that implements a generic dialog containing a preview panel for displaying |
37 graphic created by dialog |
37 graphic created by dialog |
38 """ |
38 """ |
39 |
39 |
40 class BlockPreviewDialog(wx.Dialog): |
40 class BlockPreviewDialog(wx.Dialog): |
41 |
41 |
42 def __init__(self, parent, controller, tagname, title): |
42 def __init__(self, parent, controller, tagname, title): |
43 """ |
43 """ |
44 Constructor |
44 Constructor |
45 @param parent: Parent wx.Window of dialog for modal |
45 @param parent: Parent wx.Window of dialog for modal |
46 @param controller: Reference to project controller |
46 @param controller: Reference to project controller |
47 @param tagname: Tagname of project POU edited |
47 @param tagname: Tagname of project POU edited |
48 @param title: Title of dialog frame |
48 @param title: Title of dialog frame |
49 """ |
49 """ |
50 wx.Dialog.__init__(self, parent, title=title) |
50 wx.Dialog.__init__(self, parent, title=title) |
51 |
51 |
52 # Save reference to |
52 # Save reference to |
53 self.Controller = controller |
53 self.Controller = controller |
54 self.TagName = tagname |
54 self.TagName = tagname |
55 |
55 |
56 # Label for preview |
56 # Label for preview |
57 self.PreviewLabel = wx.StaticText(self, label=_('Preview:')) |
57 self.PreviewLabel = wx.StaticText(self, label=_('Preview:')) |
58 |
58 |
59 # Create Preview panel |
59 # Create Preview panel |
60 self.Preview = wx.Panel(self, style=wx.SIMPLE_BORDER) |
60 self.Preview = wx.Panel(self, style=wx.SIMPLE_BORDER) |
61 self.Preview.SetBackgroundColour(wx.WHITE) |
61 self.Preview.SetBackgroundColour(wx.WHITE) |
62 |
62 |
63 # Add function to preview panel so that it answers to graphic elements |
63 # Add function to preview panel so that it answers to graphic elements |
64 # like Viewer |
64 # like Viewer |
65 setattr(self.Preview, "GetDrawingMode", lambda:FREEDRAWING_MODE) |
65 setattr(self.Preview, "GetDrawingMode", lambda:FREEDRAWING_MODE) |
66 setattr(self.Preview, "GetScaling", lambda:None) |
66 setattr(self.Preview, "GetScaling", lambda:None) |
67 setattr(self.Preview, "GetBlockType", controller.GetBlockType) |
67 setattr(self.Preview, "GetBlockType", controller.GetBlockType) |
68 setattr(self.Preview, "IsOfType", controller.IsOfType) |
68 setattr(self.Preview, "IsOfType", controller.IsOfType) |
69 |
69 |
70 # Bind paint event on Preview panel |
70 # Bind paint event on Preview panel |
71 self.Preview.Bind(wx.EVT_PAINT, self.OnPaint) |
71 self.Preview.Bind(wx.EVT_PAINT, self.OnPaint) |
72 |
72 |
73 # Add default dialog buttons sizer |
73 # Add default dialog buttons sizer |
74 self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) |
74 self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) |
75 self.Bind(wx.EVT_BUTTON, self.OnOK, |
75 self.Bind(wx.EVT_BUTTON, self.OnOK, |
76 self.ButtonSizer.GetAffirmativeButton()) |
76 self.ButtonSizer.GetAffirmativeButton()) |
77 |
77 |
78 self.Element = None # Graphic element to display in preview |
78 self.Element = None # Graphic element to display in preview |
79 self.MinElementSize = None # Graphic element minimal size |
79 self.MinElementSize = None # Graphic element minimal size |
80 |
80 |
81 # Variable containing the graphic element name when dialog is opened |
81 # Variable containing the graphic element name when dialog is opened |
82 self.DefaultElementName = None |
82 self.DefaultElementName = None |
83 self.Fit() |
83 self.Fit() |
84 |
84 |
85 # List of variables defined in POU {var_name: (var_class, var_type),...} |
85 # List of variables defined in POU {var_name: (var_class, var_type),...} |
86 self.VariableList = {} |
86 self.VariableList = {} |
87 |
87 |
88 def __del__(self): |
88 def __del__(self): |
89 """ |
89 """ |
90 Destructor |
90 Destructor |
91 """ |
91 """ |
92 # Remove reference to project controller |
92 # Remove reference to project controller |
93 self.Controller = None |
93 self.Controller = None |
94 |
94 |
95 def _init_sizers(self, main_rows, main_growable_row, |
95 def _init_sizers(self, main_rows, main_growable_row, |
96 left_rows, left_growable_row, |
96 left_rows, left_growable_row, |
97 right_rows, right_growable_row): |
97 right_rows, right_growable_row): |
98 """ |
98 """ |
99 Initialize common sizers |
99 Initialize common sizers |
106 @param right_rows: Number of rows in right grid sizer |
106 @param right_rows: Number of rows in right grid sizer |
107 @param right_growable_row: Row that is growable in right grid sizer, |
107 @param right_growable_row: Row that is growable in right grid sizer, |
108 None if no row is growable |
108 None if no row is growable |
109 """ |
109 """ |
110 # Create dialog main sizer |
110 # Create dialog main sizer |
111 self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, |
111 self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, |
112 rows=main_rows, vgap=10) |
112 rows=main_rows, vgap=10) |
113 self.MainSizer.AddGrowableCol(0) |
113 self.MainSizer.AddGrowableCol(0) |
114 if main_growable_row is not None: |
114 if main_growable_row is not None: |
115 self.MainSizer.AddGrowableRow(main_growable_row) |
115 self.MainSizer.AddGrowableRow(main_growable_row) |
116 |
116 |
117 # Create a sizer for dividing parameters in two columns |
117 # Create a sizer for dividing parameters in two columns |
118 self.ColumnSizer = wx.BoxSizer(wx.HORIZONTAL) |
118 self.ColumnSizer = wx.BoxSizer(wx.HORIZONTAL) |
119 self.MainSizer.AddSizer(self.ColumnSizer, border=20, |
119 self.MainSizer.AddSizer(self.ColumnSizer, border=20, |
120 flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) |
120 flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) |
121 |
121 |
122 # Create a sizer for left column |
122 # Create a sizer for left column |
123 self.LeftGridSizer = wx.FlexGridSizer(cols=1, hgap=0, |
123 self.LeftGridSizer = wx.FlexGridSizer(cols=1, hgap=0, |
124 rows=left_rows, vgap=5) |
124 rows=left_rows, vgap=5) |
125 self.LeftGridSizer.AddGrowableCol(0) |
125 self.LeftGridSizer.AddGrowableCol(0) |
126 if left_growable_row is not None: |
126 if left_growable_row is not None: |
127 self.LeftGridSizer.AddGrowableRow(left_growable_row) |
127 self.LeftGridSizer.AddGrowableRow(left_growable_row) |
128 self.ColumnSizer.AddSizer(self.LeftGridSizer, 1, border=5, |
128 self.ColumnSizer.AddSizer(self.LeftGridSizer, 1, border=5, |
129 flag=wx.GROW|wx.RIGHT|wx.EXPAND) |
129 flag=wx.GROW|wx.RIGHT|wx.EXPAND) |
130 |
130 |
131 # Create a sizer for right column |
131 # Create a sizer for right column |
132 self.RightGridSizer = wx.FlexGridSizer(cols=1, hgap=0, |
132 self.RightGridSizer = wx.FlexGridSizer(cols=1, hgap=0, |
133 rows=right_rows, vgap=0) |
133 rows=right_rows, vgap=0) |
134 self.RightGridSizer.AddGrowableCol(0) |
134 self.RightGridSizer.AddGrowableCol(0) |
135 if right_growable_row is not None: |
135 if right_growable_row is not None: |
136 self.RightGridSizer.AddGrowableRow(right_growable_row) |
136 self.RightGridSizer.AddGrowableRow(right_growable_row) |
137 self.ColumnSizer.AddSizer(self.RightGridSizer, 1, border=5, |
137 self.ColumnSizer.AddSizer(self.RightGridSizer, 1, border=5, |
138 flag=wx.GROW|wx.LEFT) |
138 flag=wx.GROW|wx.LEFT) |
139 |
139 |
140 self.SetSizer(self.MainSizer) |
140 self.SetSizer(self.MainSizer) |
141 |
141 |
142 def SetMinElementSize(self, size): |
142 def SetMinElementSize(self, size): |
143 """ |
143 """ |
144 Define minimal graphic element size |
144 Define minimal graphic element size |
145 @param size: Tuple containing minimal size (width, height) |
145 @param size: Tuple containing minimal size (width, height) |
146 """ |
146 """ |
147 self.MinElementSize = size |
147 self.MinElementSize = size |
148 |
148 |
149 def GetMinElementSize(self): |
149 def GetMinElementSize(self): |
150 """ |
150 """ |
151 Get minimal graphic element size |
151 Get minimal graphic element size |
152 @return: Tuple containing minimal size (width, height) or None if no |
152 @return: Tuple containing minimal size (width, height) or None if no |
153 element defined |
153 element defined |
154 May be overridden by inherited classes |
154 May be overridden by inherited classes |
155 """ |
155 """ |
156 if self.Element is None: |
156 if self.Element is None: |
157 return None |
157 return None |
158 |
158 |
159 return self.Element.GetMinSize() |
159 return self.Element.GetMinSize() |
160 |
160 |
161 def SetPreviewFont(self, font): |
161 def SetPreviewFont(self, font): |
162 """ |
162 """ |
163 Set font of Preview panel |
163 Set font of Preview panel |
164 @param font: wx.Font object containing font style |
164 @param font: wx.Font object containing font style |
165 """ |
165 """ |
166 self.Preview.SetFont(font) |
166 self.Preview.SetFont(font) |
167 |
167 |
168 def RefreshVariableList(self): |
168 def RefreshVariableList(self): |
169 """ |
169 """ |
170 Extract list of variables defined in POU |
170 Extract list of variables defined in POU |
171 """ |
171 """ |
172 # Get list of variables defined in POU |
172 # Get list of variables defined in POU |
173 self.VariableList = { |
173 self.VariableList = { |
174 var.Name: (var.Class, var.Type) |
174 var.Name: (var.Class, var.Type) |
175 for var in self.Controller.GetEditedElementInterfaceVars( |
175 for var in self.Controller.GetEditedElementInterfaceVars( |
176 self.TagName) |
176 self.TagName) |
177 if var.Edit} |
177 if var.Edit} |
178 |
178 |
179 # Add POU name to variable list if POU is a function |
179 # Add POU name to variable list if POU is a function |
180 returntype = self.Controller.GetEditedElementInterfaceReturnType( |
180 returntype = self.Controller.GetEditedElementInterfaceReturnType( |
181 self.TagName) |
181 self.TagName) |
182 if returntype is not None: |
182 if returntype is not None: |
183 self.VariableList[ |
183 self.VariableList[ |
184 self.Controller.GetEditedElementName(self.TagName)] = \ |
184 self.Controller.GetEditedElementName(self.TagName)] = \ |
185 ("Output", returntype) |
185 ("Output", returntype) |
186 |
186 |
187 # Add POU name if POU is a transition |
187 # Add POU name if POU is a transition |
188 words = self.TagName.split("::") |
188 words = self.TagName.split("::") |
189 if words[0] == "T": |
189 if words[0] == "T": |
190 self.VariableList[words[2]] = ("Output", "BOOL") |
190 self.VariableList[words[2]] = ("Output", "BOOL") |
191 |
191 |
192 def TestElementName(self, element_name): |
192 def TestElementName(self, element_name): |
193 """ |
193 """ |
194 Test displayed graphic element name |
194 Test displayed graphic element name |
195 @param element_name: Graphic element name |
195 @param element_name: Graphic element name |
196 """ |
196 """ |
197 # Variable containing error message format |
197 # Variable containing error message format |
198 message_format = None |
198 message_format = None |
199 # Get graphic element name in upper case |
199 # Get graphic element name in upper case |
200 uppercase_element_name = element_name.upper() |
200 uppercase_element_name = element_name.upper() |
201 |
201 |
202 # Test if graphic element name is a valid identifier |
202 # Test if graphic element name is a valid identifier |
203 if not TestIdentifier(element_name): |
203 if not TestIdentifier(element_name): |
204 message_format = _("\"%s\" is not a valid identifier!") |
204 message_format = _("\"%s\" is not a valid identifier!") |
205 |
205 |
206 # Test that graphic element name isn't a keyword |
206 # Test that graphic element name isn't a keyword |
207 elif uppercase_element_name in IEC_KEYWORDS: |
207 elif uppercase_element_name in IEC_KEYWORDS: |
208 message_format = _("\"%s\" is a keyword. It can't be used!") |
208 message_format = _("\"%s\" is a keyword. It can't be used!") |
209 |
209 |
210 # Test that graphic element name isn't a POU name |
210 # Test that graphic element name isn't a POU name |
211 elif uppercase_element_name in self.Controller.GetProjectPouNames(): |
211 elif uppercase_element_name in self.Controller.GetProjectPouNames(): |
212 message_format = _("\"%s\" pou already exists!") |
212 message_format = _("\"%s\" pou already exists!") |
213 |
213 |
214 # Test that graphic element name isn't already used in POU by a variable |
214 # Test that graphic element name isn't already used in POU by a variable |
215 # or another graphic element |
215 # or another graphic element |
216 elif ((self.DefaultElementName is None or |
216 elif ((self.DefaultElementName is None or |
217 self.DefaultElementName.upper() != uppercase_element_name) and |
217 self.DefaultElementName.upper() != uppercase_element_name) and |
218 uppercase_element_name in self.Controller.\ |
218 uppercase_element_name in self.Controller.\ |
219 GetEditedElementVariables(self.TagName)): |
219 GetEditedElementVariables(self.TagName)): |
220 message_format = _("\"%s\" element for this pou already exists!") |
220 message_format = _("\"%s\" element for this pou already exists!") |
221 |
221 |
222 # If an error have been identify, show error message dialog |
222 # If an error have been identify, show error message dialog |
223 if message_format is not None: |
223 if message_format is not None: |
224 self.ShowErrorMessage(message_format % element_name) |
224 self.ShowErrorMessage(message_format % element_name) |
225 # Test failed |
225 # Test failed |
226 return False |
226 return False |
227 |
227 |
228 # Test succeed |
228 # Test succeed |
229 return True |
229 return True |
230 |
230 |
231 def ShowErrorMessage(self, message): |
231 def ShowErrorMessage(self, message): |
232 """ |
232 """ |
233 Show an error message dialog over this dialog |
233 Show an error message dialog over this dialog |
234 @param message: Error message to display |
234 @param message: Error message to display |
235 """ |
235 """ |
236 dialog = wx.MessageDialog(self, message, |
236 dialog = wx.MessageDialog(self, message, |
237 _("Error"), |
237 _("Error"), |
238 wx.OK|wx.ICON_ERROR) |
238 wx.OK|wx.ICON_ERROR) |
239 dialog.ShowModal() |
239 dialog.ShowModal() |
240 dialog.Destroy() |
240 dialog.Destroy() |
241 |
241 |
242 def OnOK(self, event): |
242 def OnOK(self, event): |
243 """ |
243 """ |
244 Called when dialog OK button is pressed |
244 Called when dialog OK button is pressed |
245 Need to be overridden by inherited classes to check that dialog values |
245 Need to be overridden by inherited classes to check that dialog values |
246 are valid |
246 are valid |
247 @param event: wx.Event from OK button |
247 @param event: wx.Event from OK button |
248 """ |
248 """ |
249 # Close dialog |
249 # Close dialog |
250 self.EndModal(wx.ID_OK) |
250 self.EndModal(wx.ID_OK) |
251 |
251 |
252 def RefreshPreview(self): |
252 def RefreshPreview(self): |
253 """ |
253 """ |
254 Refresh preview panel of graphic element |
254 Refresh preview panel of graphic element |
255 May be overridden by inherited classes |
255 May be overridden by inherited classes |
256 """ |
256 """ |
257 # Init preview panel paint device context |
257 # Init preview panel paint device context |
258 dc = wx.ClientDC(self.Preview) |
258 dc = wx.ClientDC(self.Preview) |
259 dc.SetFont(self.Preview.GetFont()) |
259 dc.SetFont(self.Preview.GetFont()) |
260 dc.Clear() |
260 dc.Clear() |
261 |
261 |
262 # Return immediately if no graphic element defined |
262 # Return immediately if no graphic element defined |
263 if self.Element is None: |
263 if self.Element is None: |
264 return |
264 return |
265 |
265 |
266 # Calculate block size according to graphic element min size due to its |
266 # Calculate block size according to graphic element min size due to its |
267 # parameters and graphic element min size defined |
267 # parameters and graphic element min size defined |
268 min_width, min_height = self.GetMinElementSize() |
268 min_width, min_height = self.GetMinElementSize() |
269 width = max(self.MinElementSize[0], min_width) |
269 width = max(self.MinElementSize[0], min_width) |
270 height = max(self.MinElementSize[1], min_height) |
270 height = max(self.MinElementSize[1], min_height) |
271 self.Element.SetSize(width, height) |
271 self.Element.SetSize(width, height) |
272 |
272 |
273 # Get element position and bounding box to center in preview |
273 # Get element position and bounding box to center in preview |
274 posx, posy = self.Element.GetPosition() |
274 posx, posy = self.Element.GetPosition() |
275 bbox = self.Element.GetBoundingBox() |
275 bbox = self.Element.GetBoundingBox() |
276 |
276 |
277 # Get Preview panel size |
277 # Get Preview panel size |
278 client_size = self.Preview.GetClientSize() |
278 client_size = self.Preview.GetClientSize() |
279 |
279 |
280 # If graphic element is too big to be displayed in preview panel, |
280 # If graphic element is too big to be displayed in preview panel, |
281 # calculate preview panel scale so that graphic element fit inside |
281 # calculate preview panel scale so that graphic element fit inside |
282 scale = (max(float(bbox.width) / client_size.width, |
282 scale = (max(float(bbox.width) / client_size.width, |
283 float(bbox.height) / client_size.height) * 1.1 |
283 float(bbox.height) / client_size.height) * 1.1 |
284 if bbox.width * 1.1 > client_size.width or |
284 if bbox.width * 1.1 > client_size.width or |
285 bbox.height * 1.1 > client_size.height |
285 bbox.height * 1.1 > client_size.height |
286 else 1.0) |
286 else 1.0) |
287 dc.SetUserScale(1.0 / scale, 1.0 / scale) |
287 dc.SetUserScale(1.0 / scale, 1.0 / scale) |
288 |
288 |
289 # Center graphic element in preview panel |
289 # Center graphic element in preview panel |
290 x = int(client_size.width * scale - bbox.width) / 2 + posx - bbox.x |
290 x = int(client_size.width * scale - bbox.width) / 2 + posx - bbox.x |
291 y = int(client_size.height * scale - bbox.height) / 2 + posy - bbox.y |
291 y = int(client_size.height * scale - bbox.height) / 2 + posy - bbox.y |
292 self.Element.SetPosition(x, y) |
292 self.Element.SetPosition(x, y) |
293 |
293 |
294 # Draw graphic element |
294 # Draw graphic element |
295 self.Element.Draw(dc) |
295 self.Element.Draw(dc) |
296 |
296 |
297 def OnPaint(self, event): |
297 def OnPaint(self, event): |
298 """ |
298 """ |
299 Called when Preview panel need to be redraw |
299 Called when Preview panel need to be redraw |
300 @param event: wx.PaintEvent |
300 @param event: wx.PaintEvent |
301 """ |
301 """ |
302 self.RefreshPreview() |
302 self.RefreshPreview() |
303 event.Skip() |
303 event.Skip() |
304 |
|