44 HIGHLIGHT_DROP_PEN = wx.Pen(wx.Colour(0, 128, 255)) |
44 HIGHLIGHT_DROP_PEN = wx.Pen(wx.Colour(0, 128, 255)) |
45 HIGHLIGHT_DROP_BRUSH = wx.Brush(wx.Colour(0, 128, 255, 128)) |
45 HIGHLIGHT_DROP_BRUSH = wx.Brush(wx.Colour(0, 128, 255, 128)) |
46 HIGHLIGHT_RESIZE_PEN = wx.Pen(wx.Colour(200, 200, 200)) |
46 HIGHLIGHT_RESIZE_PEN = wx.Pen(wx.Colour(200, 200, 200)) |
47 HIGHLIGHT_RESIZE_BRUSH = wx.Brush(wx.Colour(200, 200, 200)) |
47 HIGHLIGHT_RESIZE_BRUSH = wx.Brush(wx.Colour(200, 200, 200)) |
48 |
48 |
49 #------------------------------------------------------------------------------- |
49 # ------------------------------------------------------------------------------- |
50 # Base Debug Variable Viewer Class |
50 # Base Debug Variable Viewer Class |
51 #------------------------------------------------------------------------------- |
51 # ------------------------------------------------------------------------------- |
52 |
52 |
53 """ |
|
54 Class that implements a generic viewer that display a list of variable values |
|
55 This class has to be inherited to effectively display variable values |
|
56 """ |
|
57 |
53 |
58 class DebugVariableViewer: |
54 class DebugVariableViewer: |
59 |
55 """ |
|
56 Class that implements a generic viewer that display a list of variable values |
|
57 This class has to be inherited to effectively display variable values |
|
58 """ |
|
59 |
60 def __init__(self, window, items=[]): |
60 def __init__(self, window, items=[]): |
61 """ |
61 """ |
62 Constructor |
62 Constructor |
63 @param window: Reference to the Debug Variable Panel |
63 @param window: Reference to the Debug Variable Panel |
64 @param items: List of DebugVariableItem displayed by Viewer |
64 @param items: List of DebugVariableItem displayed by Viewer |
65 """ |
65 """ |
66 self.ParentWindow = window |
66 self.ParentWindow = window |
67 self.ItemsDict = OrderedDict([(item.GetVariable(), item) |
67 self.ItemsDict = OrderedDict([(item.GetVariable(), item) |
68 for item in items]) |
68 for item in items]) |
69 self.Items = self.ItemsDict.viewvalues() |
69 self.Items = self.ItemsDict.viewvalues() |
70 |
70 |
71 # Variable storing current highlight displayed in Viewer |
71 # Variable storing current highlight displayed in Viewer |
72 self.Highlight = HIGHLIGHT_NONE |
72 self.Highlight = HIGHLIGHT_NONE |
73 # List of buttons |
73 # List of buttons |
74 self.Buttons = [] |
74 self.Buttons = [] |
75 |
75 |
76 def __del__(self): |
76 def __del__(self): |
77 """ |
77 """ |
78 Destructor |
78 Destructor |
79 """ |
79 """ |
80 # Remove reference to Debug Variable Panel |
80 # Remove reference to Debug Variable Panel |
81 self.ParentWindow = None |
81 self.ParentWindow = None |
82 |
82 |
83 def GetIndex(self): |
83 def GetIndex(self): |
84 """ |
84 """ |
85 Return position of Viewer in Debug Variable Panel |
85 Return position of Viewer in Debug Variable Panel |
86 @return: Position of Viewer |
86 @return: Position of Viewer |
87 """ |
87 """ |
88 return self.ParentWindow.GetViewerIndex(self) |
88 return self.ParentWindow.GetViewerIndex(self) |
89 |
89 |
90 def GetItem(self, variable): |
90 def GetItem(self, variable): |
91 """ |
91 """ |
92 Return item storing values of a variable |
92 Return item storing values of a variable |
93 @param variable: Variable path |
93 @param variable: Variable path |
94 @return: Item storing values of this variable |
94 @return: Item storing values of this variable |
95 """ |
95 """ |
96 return self.ItemsDict.get(variable, None) |
96 return self.ItemsDict.get(variable, None) |
97 |
97 |
98 def GetItems(self): |
98 def GetItems(self): |
99 """ |
99 """ |
100 Return items displayed by Viewer |
100 Return items displayed by Viewer |
101 @return: List of items displayed in Viewer |
101 @return: List of items displayed in Viewer |
102 """ |
102 """ |
103 return self.ItemsDict.values() |
103 return self.ItemsDict.values() |
104 |
104 |
105 def AddItem(self, item): |
105 def AddItem(self, item): |
106 """ |
106 """ |
107 Add an item to the list of items displayed by Viewer |
107 Add an item to the list of items displayed by Viewer |
108 @param item: Item to add to the list |
108 @param item: Item to add to the list |
109 """ |
109 """ |
110 self.ItemsDict[item.GetVariable()] = item |
110 self.ItemsDict[item.GetVariable()] = item |
111 |
111 |
112 def RemoveItem(self, item): |
112 def RemoveItem(self, item): |
113 """ |
113 """ |
114 Remove an item from the list of items displayed by Viewer |
114 Remove an item from the list of items displayed by Viewer |
115 @param item: Item to remove from the list |
115 @param item: Item to remove from the list |
116 """ |
116 """ |
117 self.ItemsDict.pop(item.GetVariable(), None) |
117 self.ItemsDict.pop(item.GetVariable(), None) |
118 |
118 |
119 def ClearItems(self): |
119 def ClearItems(self): |
120 """ |
120 """ |
121 Clear list of items displayed by Viewer |
121 Clear list of items displayed by Viewer |
122 """ |
122 """ |
123 # Unsubscribe every items of the list |
123 # Unsubscribe every items of the list |
124 for item in self.Items: |
124 for item in self.Items: |
125 self.ParentWindow.RemoveDataConsumer(item) |
125 self.ParentWindow.RemoveDataConsumer(item) |
126 |
126 |
127 # Clear list |
127 # Clear list |
128 self.ItemsDict.clear() |
128 self.ItemsDict.clear() |
129 |
129 |
130 def ItemsIsEmpty(self): |
130 def ItemsIsEmpty(self): |
131 """ |
131 """ |
132 Return if list of items displayed by Viewer is empty |
132 Return if list of items displayed by Viewer is empty |
133 @return: True if list is empty |
133 @return: True if list is empty |
134 """ |
134 """ |
135 return len(self.Items) == 0 |
135 return len(self.Items) == 0 |
136 |
136 |
137 def SubscribeAllDataConsumers(self): |
137 def SubscribeAllDataConsumers(self): |
138 """ |
138 """ |
139 Function that unsubscribe and remove every item that store values of |
139 Function that unsubscribe and remove every item that store values of |
140 a variable that doesn't exist in PLC anymore |
140 a variable that doesn't exist in PLC anymore |
141 """ |
141 """ |
142 for item in self.ItemsDict.values()[:]: |
142 for item in self.ItemsDict.values()[:]: |
143 iec_path = item.GetVariable() |
143 iec_path = item.GetVariable() |
144 |
144 |
145 # Check that variablepath exist in PLC |
145 # Check that variablepath exist in PLC |
146 if self.ParentWindow.GetDataType(iec_path) is None: |
146 if self.ParentWindow.GetDataType(iec_path) is None: |
147 # If not, unsubscribe and remove it |
147 # If not, unsubscribe and remove it |
148 self.ParentWindow.RemoveDataConsumer(item) |
148 self.ParentWindow.RemoveDataConsumer(item) |
149 self.RemoveItem(item) |
149 self.RemoveItem(item) |
150 else: |
150 else: |
151 # If it exist, resubscribe and refresh data type |
151 # If it exist, resubscribe and refresh data type |
152 self.ParentWindow.AddDataConsumer(iec_path.upper(), item, True) |
152 self.ParentWindow.AddDataConsumer(iec_path.upper(), item, True) |
153 item.RefreshVariableType() |
153 item.RefreshVariableType() |
154 |
154 |
155 def ResetItemsData(self): |
155 def ResetItemsData(self): |
156 """ |
156 """ |
157 Reset data stored in every items displayed in Viewer |
157 Reset data stored in every items displayed in Viewer |
158 """ |
158 """ |
159 for item in self.Items: |
159 for item in self.Items: |
160 item.ResetData() |
160 item.ResetData() |
161 |
161 |
162 def GetItemsMinCommonTick(self): |
162 def GetItemsMinCommonTick(self): |
163 """ |
163 """ |
164 Return the minimum tick common to all iems displayed in Viewer |
164 Return the minimum tick common to all iems displayed in Viewer |
165 @return: Minimum common tick between items |
165 @return: Minimum common tick between items |
166 """ |
166 """ |
167 return reduce(max, [item.GetData()[0, 0] |
167 return reduce(max, [item.GetData()[0, 0] |
168 for item in self.Items |
168 for item in self.Items |
169 if len(item.GetData()) > 0], 0) |
169 if len(item.GetData()) > 0], 0) |
170 |
170 |
171 def RefreshViewer(self): |
171 def RefreshViewer(self): |
172 """ |
172 """ |
173 Method that refresh the content displayed by Viewer |
173 Method that refresh the content displayed by Viewer |
174 Need to be overridden by inherited classes |
174 Need to be overridden by inherited classes |
175 """ |
175 """ |
176 pass |
176 pass |
177 |
177 |
178 def SetHighlight(self, highlight): |
178 def SetHighlight(self, highlight): |
179 """ |
179 """ |
180 Set Highlight type displayed in Viewer |
180 Set Highlight type displayed in Viewer |
181 @return: True if highlight has changed |
181 @return: True if highlight has changed |
182 """ |
182 """ |
183 # Return immediately if highlight don't change |
183 # Return immediately if highlight don't change |
184 if self.Highlight == highlight: |
184 if self.Highlight == highlight: |
185 return False |
185 return False |
186 |
186 |
187 self.Highlight = highlight |
187 self.Highlight = highlight |
188 return True |
188 return True |
189 |
189 |
190 def GetButtons(self): |
190 def GetButtons(self): |
191 """ |
191 """ |
192 Return list of buttons defined in Viewer |
192 Return list of buttons defined in Viewer |
193 @return: List of buttons |
193 @return: List of buttons |
194 """ |
194 """ |
195 return self.Buttons |
195 return self.Buttons |
196 |
196 |
197 def IsOverButton(self, x, y): |
197 def IsOverButton(self, x, y): |
198 """ |
198 """ |
199 Return if point is over one button of Viewer |
199 Return if point is over one button of Viewer |
200 @param x: X coordinate of point |
200 @param x: X coordinate of point |
201 @param y: Y coordinate of point |
201 @param y: Y coordinate of point |
215 @return: True if a button was found and callback executed |
215 @return: True if a button was found and callback executed |
216 """ |
216 """ |
217 button = self.IsOverButton(x, y) |
217 button = self.IsOverButton(x, y) |
218 if button is None: |
218 if button is None: |
219 return False |
219 return False |
220 |
220 |
221 button.ProcessCallback() |
221 button.ProcessCallback() |
222 return True |
222 return True |
223 |
223 |
224 def ShowButtons(self, show): |
224 def ShowButtons(self, show): |
225 """ |
225 """ |
226 Set display state of buttons in Viewer |
226 Set display state of buttons in Viewer |
227 @param show: Display state (True if buttons must be displayed) |
227 @param show: Display state (True if buttons must be displayed) |
228 """ |
228 """ |
229 # Change display of every buttons |
229 # Change display of every buttons |
230 for button in self.Buttons: |
230 for button in self.Buttons: |
231 button.Show(show) |
231 button.Show(show) |
232 |
232 |
233 # Refresh button positions |
233 # Refresh button positions |
234 self.RefreshButtonsPosition() |
234 self.RefreshButtonsPosition() |
235 self.RefreshViewer() |
235 self.RefreshViewer() |
236 |
236 |
237 def RefreshButtonsPosition(self): |
237 def RefreshButtonsPosition(self): |
238 """ |
238 """ |
239 Function that refresh buttons position in Viewer |
239 Function that refresh buttons position in Viewer |
240 """ |
240 """ |
241 # Get Viewer size |
241 # Get Viewer size |
242 width, height = self.GetSize() |
242 width, height = self.GetSize() |
243 |
243 |
244 # Buttons are align right so we calculate buttons positions in |
244 # Buttons are align right so we calculate buttons positions in |
245 # reverse order |
245 # reverse order |
246 buttons = self.Buttons[:] |
246 buttons = self.Buttons[:] |
247 buttons.reverse() |
247 buttons.reverse() |
248 |
248 |
249 # Position offset on x coordinate |
249 # Position offset on x coordinate |
250 x_offset = 0 |
250 x_offset = 0 |
251 for button in buttons: |
251 for button in buttons: |
252 # Buttons are stacked right, removing those that are not active |
252 # Buttons are stacked right, removing those that are not active |
253 if button.IsEnabled(): |
253 if button.IsEnabled(): |
254 # Update button position according to button width and offset |
254 # Update button position according to button width and offset |
255 # on x coordinate |
255 # on x coordinate |
256 w, h = button.GetSize() |
256 w, h = button.GetSize() |
257 button.SetPosition(width - 5 - w - x_offset, 5) |
257 button.SetPosition(width - 5 - w - x_offset, 5) |
258 # Update offset on x coordinate |
258 # Update offset on x coordinate |
259 x_offset += w + 2 |
259 x_offset += w + 2 |
260 |
260 |
261 def DrawCommonElements(self, dc, buttons=None): |
261 def DrawCommonElements(self, dc, buttons=None): |
262 """ |
262 """ |
263 Function that draw common graphics for every Viewers |
263 Function that draw common graphics for every Viewers |
264 @param dc: wx.DC object corresponding to Device context where drawing |
264 @param dc: wx.DC object corresponding to Device context where drawing |
265 common graphics |
265 common graphics |
266 @param buttons: List of buttons to draw if different from default |
266 @param buttons: List of buttons to draw if different from default |
267 (default None) |
267 (default None) |
268 """ |
268 """ |
269 # Get Viewer size |
269 # Get Viewer size |
270 width, height = self.GetSize() |
270 width, height = self.GetSize() |
271 |
271 |
272 # Set dc styling for drop before or drop after highlight |
272 # Set dc styling for drop before or drop after highlight |
273 dc.SetPen(HIGHLIGHT_DROP_PEN) |
273 dc.SetPen(HIGHLIGHT_DROP_PEN) |
274 dc.SetBrush(HIGHLIGHT_DROP_BRUSH) |
274 dc.SetBrush(HIGHLIGHT_DROP_BRUSH) |
275 |
275 |
276 # Draw line at upper side of Viewer if highlight is drop before |
276 # Draw line at upper side of Viewer if highlight is drop before |
277 if self.Highlight == HIGHLIGHT_BEFORE: |
277 if self.Highlight == HIGHLIGHT_BEFORE: |
278 dc.DrawLine(0, 1, width - 1, 1) |
278 dc.DrawLine(0, 1, width - 1, 1) |
279 |
279 |
280 # Draw line at lower side of Viewer if highlight is drop before |
280 # Draw line at lower side of Viewer if highlight is drop before |
281 elif self.Highlight == HIGHLIGHT_AFTER: |
281 elif self.Highlight == HIGHLIGHT_AFTER: |
282 dc.DrawLine(0, height - 1, width - 1, height - 1) |
282 dc.DrawLine(0, height - 1, width - 1, height - 1) |
283 |
283 |
284 # If no specific buttons are defined, get default buttons |
284 # If no specific buttons are defined, get default buttons |
285 if buttons is None: |
285 if buttons is None: |
286 buttons = self.Buttons |
286 buttons = self.Buttons |
287 # Draw buttons |
287 # Draw buttons |
288 for button in buttons: |
288 for button in buttons: |
289 button.Draw(dc) |
289 button.Draw(dc) |
290 |
290 |
291 # If graph dragging is processing |
291 # If graph dragging is processing |
292 if self.ParentWindow.IsDragging(): |
292 if self.ParentWindow.IsDragging(): |
293 destBBox = self.ParentWindow.GetDraggingAxesClippingRegion(self) |
293 destBBox = self.ParentWindow.GetDraggingAxesClippingRegion(self) |
294 srcPos = self.ParentWindow.GetDraggingAxesPosition(self) |
294 srcPos = self.ParentWindow.GetDraggingAxesPosition(self) |
295 if destBBox.width > 0 and destBBox.height > 0: |
295 if destBBox.width > 0 and destBBox.height > 0: |
296 srcPanel = self.ParentWindow.DraggingAxesPanel |
296 srcPanel = self.ParentWindow.DraggingAxesPanel |
297 srcBBox = srcPanel.GetAxesBoundingBox() |
297 srcBBox = srcPanel.GetAxesBoundingBox() |
298 |
298 |
299 srcX = srcBBox.x - (srcPos.x if destBBox.x == 0 else 0) |
299 srcX = srcBBox.x - (srcPos.x if destBBox.x == 0 else 0) |
300 srcY = srcBBox.y - (srcPos.y if destBBox.y == 0 else 0) |
300 srcY = srcBBox.y - (srcPos.y if destBBox.y == 0 else 0) |
301 |
301 |
302 srcBmp = _convert_agg_to_wx_bitmap( |
302 srcBmp = _convert_agg_to_wx_bitmap( |
303 srcPanel.get_renderer(), None) |
303 srcPanel.get_renderer(), None) |
304 srcDC = wx.MemoryDC() |
304 srcDC = wx.MemoryDC() |
305 srcDC.SelectObject(srcBmp) |
305 srcDC.SelectObject(srcBmp) |
306 |
306 |
307 dc.Blit(destBBox.x, destBBox.y, |
307 dc.Blit(destBBox.x, destBBox.y, |
308 int(destBBox.width), int(destBBox.height), |
308 int(destBBox.width), int(destBBox.height), |
309 srcDC, srcX, srcY) |
309 srcDC, srcX, srcY) |
310 |
310 |
311 def OnEnter(self, event): |
311 def OnEnter(self, event): |
312 """ |
312 """ |
313 Function called when entering Viewer |
313 Function called when entering Viewer |
314 @param event: wx.MouseEvent |
314 @param event: wx.MouseEvent |
315 """ |
315 """ |
316 # Display buttons |
316 # Display buttons |
317 self.ShowButtons(True) |
317 self.ShowButtons(True) |
318 event.Skip() |
318 event.Skip() |
319 |
319 |
320 def OnLeave(self, event): |
320 def OnLeave(self, event): |
321 """ |
321 """ |
322 Function called when leaving Viewer |
322 Function called when leaving Viewer |
323 @param event: wx.MouseEvent |
323 @param event: wx.MouseEvent |
324 """ |
324 """ |
325 # Hide buttons |
325 # Hide buttons |
326 self.ShowButtons(False) |
326 self.ShowButtons(False) |
327 event.Skip() |
327 event.Skip() |
328 |
328 |
329 def OnCloseButton(self): |
329 def OnCloseButton(self): |
330 """ |
330 """ |
331 Function called when Close button is pressed |
331 Function called when Close button is pressed |
332 """ |
332 """ |
333 wx.CallAfter(self.ParentWindow.DeleteValue, self) |
333 wx.CallAfter(self.ParentWindow.DeleteValue, self) |
334 |
334 |
335 def OnForceButton(self): |
335 def OnForceButton(self): |
336 """ |
336 """ |
337 Function called when Force button is pressed |
337 Function called when Force button is pressed |
338 """ |
338 """ |
339 self.ForceValue(self.ItemsDict.values()[0]) |
339 self.ForceValue(self.ItemsDict.values()[0]) |
340 |
340 |
341 def OnReleaseButton(self): |
341 def OnReleaseButton(self): |
342 """ |
342 """ |
343 Function called when Release button is pressed |
343 Function called when Release button is pressed |
344 """ |
344 """ |
345 self.ReleaseValue(self.ItemsDict.values()[0]) |
345 self.ReleaseValue(self.ItemsDict.values()[0]) |
346 |
346 |
347 def OnMouseDragging(self, x, y): |
347 def OnMouseDragging(self, x, y): |
348 """ |
348 """ |
349 Function called when mouse is dragged over Viewer |
349 Function called when mouse is dragged over Viewer |
350 @param x: X coordinate of mouse pointer |
350 @param x: X coordinate of mouse pointer |
351 @param y: Y coordinate of mouse pointer |
351 @param y: Y coordinate of mouse pointer |
352 """ |
352 """ |
353 xw, yw = self.GetPosition() |
353 xw, yw = self.GetPosition() |
354 # Refresh highlight in Debug Variable Panel (highlight can be displayed |
354 # Refresh highlight in Debug Variable Panel (highlight can be displayed |
355 # in another Viewer |
355 # in another Viewer |
356 self.ParentWindow.RefreshHighlight(x + xw, y + yw) |
356 self.ParentWindow.RefreshHighlight(x + xw, y + yw) |
357 |
357 |
358 def RefreshHighlight(self, x, y): |
358 def RefreshHighlight(self, x, y): |
359 """ |
359 """ |
360 Function called by Debug Variable Panel asking Viewer to refresh |
360 Function called by Debug Variable Panel asking Viewer to refresh |
361 highlight according to mouse position |
361 highlight according to mouse position |
362 @param x: X coordinate of mouse pointer |
362 @param x: X coordinate of mouse pointer |
363 @param y: Y coordinate of mouse pointer |
363 @param y: Y coordinate of mouse pointer |
364 """ |
364 """ |
365 # Get Viewer size |
365 # Get Viewer size |
366 width, height = self.GetSize() |
366 width, height = self.GetSize() |
367 |
367 |
368 # Mouse is in the first half of Viewer |
368 # Mouse is in the first half of Viewer |
369 if y < height / 2: |
369 if y < height / 2: |
370 # If Viewer is the upper one, draw drop before highlight |
370 # If Viewer is the upper one, draw drop before highlight |
371 if self.ParentWindow.IsViewerFirst(self): |
371 if self.ParentWindow.IsViewerFirst(self): |
372 self.SetHighlight(HIGHLIGHT_BEFORE) |
372 self.SetHighlight(HIGHLIGHT_BEFORE) |
373 |
373 |
374 # Else draw drop after highlight in previous Viewer |
374 # Else draw drop after highlight in previous Viewer |
375 else: |
375 else: |
376 self.SetHighlight(HIGHLIGHT_NONE) |
376 self.SetHighlight(HIGHLIGHT_NONE) |
377 self.ParentWindow.HighlightPreviousViewer(self) |
377 self.ParentWindow.HighlightPreviousViewer(self) |
378 |
378 |
379 # Mouse is in the second half of Viewer, draw drop after highlight |
379 # Mouse is in the second half of Viewer, draw drop after highlight |
380 else: |
380 else: |
381 self.SetHighlight(HIGHLIGHT_AFTER) |
381 self.SetHighlight(HIGHLIGHT_AFTER) |
382 |
382 |
383 def OnEraseBackground(self, event): |
383 def OnEraseBackground(self, event): |
384 """ |
384 """ |
385 Function called when Viewer background is going to be erase |
385 Function called when Viewer background is going to be erase |
386 @param event: wx.EraseEvent |
386 @param event: wx.EraseEvent |
387 """ |
387 """ |
388 # Prevent flicker on Windows |
388 # Prevent flicker on Windows |
389 pass |
389 pass |
390 |
390 |
391 def OnResize(self, event): |
391 def OnResize(self, event): |
392 """ |
392 """ |
393 Function called when Viewer size changed |
393 Function called when Viewer size changed |
394 @param event: wx.ResizeEvent |
394 @param event: wx.ResizeEvent |
395 """ |
395 """ |
396 # Refresh button positions |
396 # Refresh button positions |
397 self.RefreshButtonsPosition() |
397 self.RefreshButtonsPosition() |
398 self.ParentWindow.ForceRefresh() |
398 self.ParentWindow.ForceRefresh() |
399 event.Skip() |
399 event.Skip() |
400 |
400 |
401 def ForceValue(self, item): |
401 def ForceValue(self, item): |
402 """ |
402 """ |
403 Force value of item given |
403 Force value of item given |
404 @param item: Item to force value |
404 @param item: Item to force value |
405 """ |
405 """ |