--- a/DataTypeEditor.py Sun Oct 09 23:31:13 2011 +0200
+++ b/DataTypeEditor.py Sun Oct 09 23:31:50 2011 +0200
@@ -26,6 +26,7 @@
import wx.grid
import wx.gizmos
from plcopen.structures import IEC_KEYWORDS, TestIdentifier
+from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD
import re
@@ -261,7 +262,7 @@
parent.AddWindow(self.staticText2, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
parent.AddWindow(self.DirectlyBaseType, 1, border=5, flag=wx.GROW|wx.ALL)
parent.AddWindow(self.staticText3, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
- parent.AddWindow(self.DirectlyInitialValue, 1, border=5, flag=wx.GROW|wx.ALL)
+ parent.AddWindow(self.DirectlyInitialValue, 1, border=5, flag=wx.ALL)
def _init_coll_SubrangePanelSizer_Items(self, parent):
parent.AddWindow(self.staticText4, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
@@ -299,7 +300,7 @@
def _init_coll_ArrayPanelRightSizer_Items(self, parent):
parent.AddWindow(self.staticText10, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
- parent.AddWindow(self.ArrayInitialValue, 1, border=5, flag=wx.GROW|wx.ALL)
+ parent.AddWindow(self.ArrayInitialValue, 1, border=5, flag=wx.ALL)
def _init_coll_StructurePanelSizer_Items(self, parent):
parent.AddWindow(self.staticText11, 0, border=5, flag=wx.ALL)
@@ -582,12 +583,18 @@
self.ArrayPanel.Hide()
self.StructurePanel.Hide()
self.CurrentPanel = "Directly"
- self.Errors = []
+ self.Highlights = []
self.Initializing = False
self.ParentWindow = window
self.Controler = controler
self.TagName = tagname
+
+ self.RefreshHighlightsTimer = wx.Timer(self, -1)
+ self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
+
+ def __del__(self):
+ self.RefreshHighlightsTimer.Stop()
def SetTagName(self, tagname):
self.TagName = tagname
@@ -921,63 +928,62 @@
self.ParentWindow.RefreshEditMenu()
#-------------------------------------------------------------------------------
-# Errors showing functions
+# Highlights showing functions
#-------------------------------------------------------------------------------
- def ClearErrors(self):
- self.Errors = []
+ def OnRefreshHighlightsTimer(self, event):
self.RefreshView()
-
- def AddShownError(self, infos, start, end):
- self.Errors.append((infos, start, end))
+ event.Skip()
+
+ def ClearHighlights(self, highlight_type=None):
+ if highlight_type is None:
+ self.Highlights = []
+ else:
+ self.Highlights = [(infos, start, end, highlight) for (infos, start, end, highlight) in self.Highlights if highlight != highlight_type]
+ self.RefreshView()
+
+ def AddHighlight(self, infos, start, end ,highlight_type):
+ self.Highlights.append((infos, start, end, highlight_type))
+ self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
def ShowErrors(self):
type_infos = self.Controler.GetDataTypeInfos(self.TagName)
- for infos, start, end in self.Errors:
+ for infos, start, end, highlight_type in self.Highlights:
if infos[0] == "base":
if type_infos["type"] == "Directly":
- self.DirectlyBaseType.SetBackgroundColour(wx.Colour(255, 255, 0))
- self.DirectlyBaseType.SetForegroundColour(wx.RED)
+ self.DirectlyBaseType.SetBackgroundColour(highlight_type[0])
+ self.DirectlyBaseType.SetForegroundColour(highlight_type[1])
elif type_infos["type"] == "Subrange":
- self.SubrangeBaseType.SetBackgroundColour(wx.Colour(255, 255, 0))
- self.SubrangeBaseType.SetForegroundColour(wx.RED)
+ self.SubrangeBaseType.SetBackgroundColour(highlight_type[0])
+ self.SubrangeBaseType.SetForegroundColour(highlight_type[1])
elif type_infos["type"] == "Array":
- self.ArrayBaseType.SetBackgroundColour(wx.Colour(255, 255, 0))
- self.ArrayBaseType.SetForegroundColour(wx.RED)
+ self.ArrayBaseType.SetBackgroundColour(highlight_type[0])
+ self.ArrayBaseType.SetForegroundColour(highlight_type[1])
elif infos[0] == "lower":
- self.SubrangeMinimum.SetBackgroundColour(wx.Colour(255, 255, 0))
- self.SubrangeMaximum.SetForegroundColour(wx.RED)
+ self.SubrangeMinimum.SetBackgroundColour(highlight_type[0])
+ self.SubrangeMaximum.SetForegroundColour(highlight_type[1])
elif infos[0] == "upper":
- self.SubrangeMinimum.SetBackgroundColour(wx.Colour(255, 255, 0))
- self.SubrangeMaximum.SetForegroundColour(wx.RED)
+ self.SubrangeMinimum.SetBackgroundColour(highlight_type[0])
+ self.SubrangeMaximum.SetForegroundColour(highlight_type[1])
elif infos[0] == "value":
listctrl = self.EnumeratedValues.GetListCtrl()
- listctrl.SetItemBackgroundColour(infos[1], wx.Colour(255, 255, 0))
- listctrl.SetItemTextColour(infos[1], wx.RED)
+ listctrl.SetItemBackgroundColour(infos[1], highlight_type[0])
+ listctrl.SetItemTextColour(infos[1], highlight_type[1])
listctrl.Select(listctrl.FocusedItem, False)
elif infos[0] == "range":
listctrl = self.EnumeratedValues.GetListCtrl()
- listctrl.SetItemBackgroundColour(infos[1], wx.Colour(255, 255, 0))
- listctrl.SetItemTextColour(infos[1], wx.RED)
+ listctrl.SetItemBackgroundColour(infos[1], highlight_type[0])
+ listctrl.SetItemTextColour(infos[1], highlight_type[1])
listctrl.SetStringSelection("")
elif infos[0] == "initial":
if type_infos["type"] == "Directly":
- text = self.DirectlyInitialValue.GetValue()
- self.DirectlyInitialValue.SetValue(text[:start[1]])
- self.DirectlyInitialValue.SetDefaultStyle(wx.TextAttr(wx.RED, wx.Colour(255, 255, 0)))
- self.DirectlyInitialValue.AppendText(text[start[1]:end[1] + 1])
- self.DirectlyInitialValue.SetDefaultStyle(wx.TextAttr(wx.BLACK, wx.WHITE))
- self.DirectlyInitialValue.AppendText(text[end[1] + 1:])
+ self.DirectlyInitialValue.SetStyle(start[1], end[1] + 1, wx.TextAttr(highlight_type[1], highlight_type[0]))
elif type_infos["type"] == "Subrange":
- self.SubrangeInitialValue.SetBackgroundColour(wx.Colour(255, 255, 0))
- self.SubrangeInitialValue.SetForegroundColour(wx.RED)
+ self.SubrangeInitialValue.SetBackgroundColour(highlight_type[0])
+ self.SubrangeInitialValue.SetForegroundColour(highlight_type[1])
elif type_infos["type"] == "Enumerated":
- self.EnumeratedInitialValue.SetBackgroundColour(wx.Colour(255, 255, 0))
- self.EnumeratedInitialValue.SetForegroundColour(wx.RED)
+ self.EnumeratedInitialValue.SetBackgroundColour(highlight_type[0])
+ self.EnumeratedInitialValue.SetForegroundColour(highlight_type[1])
elif type_infos["type"] == "Array":
- text = self.ArrayInitialValue.GetValue()
- self.ArrayInitialValue.SetValue(text[:start[1]])
- self.ArrayInitialValue.SetDefaultStyle(wx.TextAttr(wx.RED, wx.Colour(255, 255, 0)))
- self.ArrayInitialValue.AppendText(text[start[1]:end[1] + 1])
- self.ArrayInitialValue.SetDefaultStyle(wx.TextAttr(wx.BLACK, wx.WHITE))
- self.ArrayInitialValue.AppendText(text[end[1] + 1:])
+ self.ArrayInitialValue.SetStyle(start[1], end[1] + 1, wx.TextAttr(highlight_type[1], highlight_type[0]))
+
Binary file Images/BLOCK.png has changed
Binary file Images/COIL.png has changed
Binary file Images/COMMENT.png has changed
Binary file Images/CONNECTOR.png has changed
Binary file Images/CONTACT.png has changed
Binary file Images/IO_VARIABLE.png has changed
Binary file Images/JUMP.png has changed
Binary file Images/STEP.png has changed
--- a/Images/icons.svg Sun Oct 09 23:31:13 2011 +0200
+++ b/Images/icons.svg Sun Oct 09 23:31:50 2011 +0200
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
@@ -13,7 +14,7 @@
height="16"
id="svg2"
sodipodi:version="0.32"
- inkscape:version="0.46"
+ inkscape:version="0.48.2 r9819"
sodipodi:modified="TRUE"
version="1.0"
inkscape:export-filename="/taf/Pim/workspace_laurent/plcopeneditor/Images/SFC.png"
@@ -485,6 +486,174 @@
y1="11"
x2="19"
y2="23" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5175-2"
+ id="linearGradient3808-6"
+ gradientUnits="userSpaceOnUse"
+ x1="0"
+ y1="1"
+ x2="15"
+ y2="16"
+ gradientTransform="translate(-40,0)" />
+ <linearGradient
+ id="linearGradient5175-2">
+ <stop
+ style="stop-color:#bdcccd;stop-opacity:1;"
+ offset="0"
+ id="stop5177-8" />
+ <stop
+ style="stop-color:#7979ff;stop-opacity:1;"
+ offset="1"
+ id="stop5179-9" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5175-9"
+ id="linearGradient3808-1"
+ gradientUnits="userSpaceOnUse"
+ x1="0"
+ y1="1"
+ x2="15"
+ y2="16"
+ gradientTransform="translate(-40,0)" />
+ <linearGradient
+ id="linearGradient5175-9">
+ <stop
+ style="stop-color:#bdcccd;stop-opacity:1;"
+ offset="0"
+ id="stop5177-9" />
+ <stop
+ style="stop-color:#7979ff;stop-opacity:1;"
+ offset="1"
+ id="stop5179-6" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5175-5"
+ id="linearGradient3808-3"
+ gradientUnits="userSpaceOnUse"
+ x1="0"
+ y1="1"
+ x2="15"
+ y2="16"
+ gradientTransform="translate(-40,0)" />
+ <linearGradient
+ id="linearGradient5175-5">
+ <stop
+ style="stop-color:#bdcccd;stop-opacity:1;"
+ offset="0"
+ id="stop5177-94" />
+ <stop
+ style="stop-color:#7979ff;stop-opacity:1;"
+ offset="1"
+ id="stop5179-8" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5175-3"
+ id="linearGradient3808-8"
+ gradientUnits="userSpaceOnUse"
+ x1="0"
+ y1="1"
+ x2="15"
+ y2="16"
+ gradientTransform="translate(-40,0)" />
+ <linearGradient
+ id="linearGradient5175-3">
+ <stop
+ style="stop-color:#bdcccd;stop-opacity:1;"
+ offset="0"
+ id="stop5177-2" />
+ <stop
+ style="stop-color:#7979ff;stop-opacity:1;"
+ offset="1"
+ id="stop5179-3" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5175-1"
+ id="linearGradient3808-19"
+ gradientUnits="userSpaceOnUse"
+ x1="0"
+ y1="1"
+ x2="15"
+ y2="16"
+ gradientTransform="translate(-40,0)" />
+ <linearGradient
+ id="linearGradient5175-1">
+ <stop
+ style="stop-color:#bdcccd;stop-opacity:1;"
+ offset="0"
+ id="stop5177-5" />
+ <stop
+ style="stop-color:#7979ff;stop-opacity:1;"
+ offset="1"
+ id="stop5179-0" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5175-6"
+ id="linearGradient3808-2"
+ gradientUnits="userSpaceOnUse"
+ x1="0"
+ y1="1"
+ x2="15"
+ y2="16"
+ gradientTransform="translate(-40,0)" />
+ <linearGradient
+ id="linearGradient5175-6">
+ <stop
+ style="stop-color:#bdcccd;stop-opacity:1;"
+ offset="0"
+ id="stop5177-56" />
+ <stop
+ style="stop-color:#7979ff;stop-opacity:1;"
+ offset="1"
+ id="stop5179-63" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5175-34"
+ id="linearGradient3808-9"
+ gradientUnits="userSpaceOnUse"
+ x1="0"
+ y1="1"
+ x2="15"
+ y2="16"
+ gradientTransform="translate(-40,0)" />
+ <linearGradient
+ id="linearGradient5175-34">
+ <stop
+ style="stop-color:#bdcccd;stop-opacity:1;"
+ offset="0"
+ id="stop5177-6" />
+ <stop
+ style="stop-color:#7979ff;stop-opacity:1;"
+ offset="1"
+ id="stop5179-2" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient5175-97"
+ id="linearGradient3808-97"
+ gradientUnits="userSpaceOnUse"
+ x1="0"
+ y1="1"
+ x2="15"
+ y2="16"
+ gradientTransform="translate(-40,0)" />
+ <linearGradient
+ id="linearGradient5175-97">
+ <stop
+ style="stop-color:#bdcccd;stop-opacity:1;"
+ offset="0"
+ id="stop5177-3" />
+ <stop
+ style="stop-color:#7979ff;stop-opacity:1;"
+ offset="1"
+ id="stop5179-5" />
+ </linearGradient>
</defs>
<sodipodi:namedview
id="base"
@@ -493,9 +662,9 @@
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
- inkscape:zoom="2"
- inkscape:cx="82.340349"
- inkscape:cy="31.586684"
+ inkscape:zoom="8"
+ inkscape:cx="264.59165"
+ inkscape:cy="46.838908"
inkscape:document-units="px"
inkscape:current-layer="layer1"
width="16px"
@@ -508,7 +677,8 @@
inkscape:window-width="1623"
inkscape:window-height="998"
inkscape:window-x="52"
- inkscape:window-y="47">
+ inkscape:window-y="47"
+ inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid2410"
@@ -1210,7 +1380,7 @@
sodipodi:end="3.8497933"
sodipodi:start="2.4295312"
transform="matrix(3.333333,0,0,1.666667,-10.63542,-12)"
- d="M 5.5467128,19.440281 A 2.25,4.5 0 0 1 5.5410496,13.572893"
+ d="M 5.5467128,19.440281 C 4.8199153,17.756163 4.8175085,15.262611 5.5410496,13.572893"
sodipodi:ry="4.5"
sodipodi:rx="2.25"
sodipodi:cy="16.5"
@@ -1223,7 +1393,7 @@
sodipodi:end="3.8497933"
sodipodi:start="2.4295312"
transform="matrix(-3.333333,0,0,1.666667,34.63541,-12)"
- d="M 5.5467128,19.440281 A 2.25,4.5 0 0 1 5.5410496,13.572893"
+ d="M 5.5467128,19.440281 C 4.8199153,17.756163 4.8175085,15.262611 5.5410496,13.572893"
sodipodi:ry="4.5"
sodipodi:rx="2.25"
sodipodi:cy="16.5"
@@ -1334,7 +1504,7 @@
transform="translate(-142,91)">
<path
transform="matrix(1.25,0,0,1.265625,0,9.9375)"
- d="M 4,2 A 2,2 0 1 1 0,2 A 2,2 0 1 1 4,2 z"
+ d="M 4,2 C 4,3.1045695 3.1045695,4 2,4 0.8954305,4 0,3.1045695 0,2 0,0.8954305 0.8954305,0 2,0 3.1045695,0 4,0.8954305 4,2 z"
sodipodi:ry="2"
sodipodi:rx="2"
sodipodi:cy="2"
@@ -1344,7 +1514,7 @@
sodipodi:type="arc" />
<path
transform="matrix(1.25,0,0,1.25,19,18.996094)"
- d="M 4,2 A 2,2 0 1 1 0,2 A 2,2 0 1 1 4,2 z"
+ d="M 4,2 C 4,3.1045695 3.1045695,4 2,4 0.8954305,4 0,3.1045695 0,2 0,0.8954305 0.8954305,0 2,0 3.1045695,0 4,0.8954305 4,2 z"
sodipodi:ry="2"
sodipodi:rx="2"
sodipodi:cy="2"
@@ -2332,5 +2502,370 @@
x="8"
id="tspan4028"
sodipodi:role="line">%% VAR_LOCAL VAR_INPUT VAR_OUTPUT VAR_INOUT %%</tspan></text>
+ <text
+ id="text4026-5"
+ y="-54"
+ x="210"
+ style="font-size:5px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="-54"
+ x="210"
+ id="tspan4028-7"
+ sodipodi:role="line">%% COMMENT BLOCK IO_VARIABLE CONNECTOR CONTACT COIL STEP JUMP %%</tspan></text>
+ <use
+ style="display:inline"
+ x="0"
+ y="0"
+ xlink:href="#g3824"
+ id="COMMENT"
+ transform="translate(265,-48)"
+ width="16"
+ height="16"
+ inkscape:label="#use3839" />
+ <use
+ style="display:inline"
+ x="0"
+ y="0"
+ xlink:href="#g3824"
+ id="BLOCK"
+ transform="translate(289,-48)"
+ width="16"
+ height="16"
+ inkscape:label="#use3839" />
+ <g
+ style="display:inline"
+ id="g3333-5"
+ inkscape:label="Calque 1"
+ transform="translate(224,-54)">
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
+ d="m 3,8 7.65625,0 4.28309,4.15625 0,7.816022 L 3,20 z"
+ id="rect3335-3"
+ sodipodi:nodetypes="cccccc" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible"
+ d="m 381,99 0,12 12,0 0,-8.44286 L 389.40625,99 z m 1,1 6,0 0,4 4,0 0,6 -10,0 z m 7,0 3,3 -3,0 z"
+ transform="translate(-378,-91)"
+ id="path3337-9"
+ sodipodi:nodetypes="ccccccccccccccccc" />
+ <text
+ transform="scale(1.0345959,0.96656095)"
+ sodipodi:linespacing="125%"
+ id="text3339-4"
+ y="17.998188"
+ x="4.4307899"
+ style="font-size:3.46388555px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ id="tspan1988-1"
+ y="17.998188"
+ x="4.4307899"
+ sodipodi:role="line">CMT</tspan></text>
+ </g>
+ <g
+ transform="translate(129,-48)"
+ style="display:inline"
+ id="g4204-0">
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccccccccccccccccccccccccccc"
+ id="rect2479-6"
+ d="m 123,2 0,1 -1,0 0,1 1,0 0,2 -1,0 0,1 1,0 0,2 -1,0 0,1 1,0 0,2 -1,0 0,1 1,0 0,1 10,0 0,-3 1,0 0,-1 -1,0 0,-4 1,0 0,-1 -1,0 0,-3 -10,0 z m 1,1 8,0 0,10 -8,0 0,-10 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.33726069px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <text
+ transform="scale(1.0096698,0.9904228)"
+ sodipodi:linespacing="125%"
+ id="text6441-8"
+ y="8.1013546"
+ x="123.80813"
+ style="font-size:4.17388439px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="8.1013546"
+ x="123.80813"
+ id="tspan6443-7"
+ sodipodi:role="line">FB</tspan></text>
+ </g>
+ <use
+ style="display:inline"
+ x="0"
+ y="0"
+ xlink:href="#g3824"
+ id="IO_VARIABLE"
+ transform="translate(316,-48)"
+ width="16"
+ height="16"
+ inkscape:label="#use3839" />
+ <g
+ style="display:inline"
+ id="g3579-7"
+ inkscape:label="Calque 1"
+ transform="translate(269,-52.95581)">
+ <rect
+ y="7.9558101"
+ x="9"
+ height="10"
+ width="10"
+ id="rect3581-3"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
+ ry="0" />
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ sodipodi:nodetypes="cccccccccccccc"
+ id="path3583-9"
+ d="m 9,8.95581 0,8 10,0 0,-4 2,0 0,-1 -2,0 0,-3 z m 1,1 8,-4e-6 0,6 -8,4e-6 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text3585-0"
+ y="13.95581"
+ x="11"
+ style="font-size:2.67459083px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="13.95581"
+ x="11"
+ id="tspan3587-7"
+ sodipodi:role="line">VAR</tspan></text>
+ </g>
+ <use
+ style="display:inline"
+ x="0"
+ y="0"
+ xlink:href="#g3824"
+ id="CONNECTOR"
+ transform="translate(348,-48)"
+ width="16"
+ height="16"
+ inkscape:label="#use3839" />
+ <g
+ style="display:inline"
+ id="g3579-7-2"
+ inkscape:label="Calque 1"
+ transform="translate(301,-52.95581)">
+ <rect
+ y="7.9558101"
+ x="9"
+ height="10"
+ width="10"
+ id="rect3581-3-2"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
+ ry="0" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccc"
+ id="path1951-4"
+ d="M 15,9.3879711 18.35476,12.95581 15,16.5"
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline" />
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccc"
+ id="path1949-6"
+ d="M 9.4649098,9.5580851 12.55965,12.95581 9.5309398,16.357614"
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text3365-3"
+ y="13.95581"
+ x="13.46875"
+ style="font-size:2.69470572px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="13.95581"
+ x="13.46875"
+ id="tspan3367-9"
+ sodipodi:role="line">C</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ sodipodi:nodetypes="cccccccccccccc"
+ id="path3583-9-4"
+ d="m 9,8.95581 0,8 10,0 0,-4 2,0 0,-1 -2,0 0,-3 z m 1,1 8,-4e-6 0,6 -8,4e-6 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible" />
+ </g>
+ <g
+ style="display:inline"
+ id="g3383-5"
+ inkscape:label="Calque 1"
+ transform="translate(335,-49)">
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ sodipodi:nodetypes="ccccccccc"
+ id="path3385-2"
+ d="m 10,6 0,9 2,0 0,-4 2,0 0,-1 -2,0 0,-4 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text3387-1"
+ y="5"
+ x="5"
+ style="font-size:2.80351019px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="5"
+ x="5"
+ id="tspan3389-3"
+ sodipodi:role="line">VAR</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ sodipodi:nodetypes="ccccccccc"
+ id="path3391-2"
+ d="m 6,6 0,9 -2,0 0,-4 -2,0 0,-1 2,0 0,-4 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible" />
+ </g>
+ <use
+ style="display:inline"
+ x="0"
+ y="0"
+ xlink:href="#g3824"
+ id="CONTACT"
+ transform="translate(375,-48)"
+ width="16"
+ height="16"
+ inkscape:label="#use3839" />
+ <use
+ style="display:inline"
+ x="0"
+ y="0"
+ xlink:href="#g3824"
+ id="COIL"
+ transform="translate(393,-48)"
+ width="16"
+ height="16"
+ inkscape:label="#use3839" />
+ <use
+ style="display:inline"
+ x="0"
+ y="0"
+ xlink:href="#g3824"
+ id="STEP"
+ transform="translate(411,-48)"
+ width="16"
+ height="16"
+ inkscape:label="#use3839" />
+ <use
+ style="display:inline"
+ x="0"
+ y="0"
+ xlink:href="#g3824"
+ id="JUMP"
+ transform="translate(429,-48)"
+ width="16"
+ height="16"
+ inkscape:label="#use3839" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible"
+ d="m 354.98855,-38.999999 0,1 3,-10e-7 0,-1 z"
+ id="path3309-2"
+ sodipodi:nodetypes="ccccc"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible"
+ d="m 366.98855,-39 0,1 -3,-10e-7 0,-1 z"
+ id="path3314-4"
+ sodipodi:nodetypes="ccccc"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ <path
+ sodipodi:type="arc"
+ style="fill:none;stroke:#000000;stroke-width:0.84852809;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
+ id="path1984-0"
+ sodipodi:cx="7.25"
+ sodipodi:cy="16.5"
+ sodipodi:rx="2.25"
+ sodipodi:ry="3.6750026"
+ d="m 5.3391583,18.440291 a 2.25,3.6750026 0 0 1 -0.00478,-3.867976"
+ transform="matrix(3.333333,0,0,1.666667,341.35313,-65.999999)"
+ sodipodi:start="2.5853841"
+ sodipodi:end="3.6937671"
+ sodipodi:open="true" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text3387-1-9"
+ y="-44"
+ x="357.98856"
+ style="font-size:2.80351019px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="-44"
+ x="357.98856"
+ id="tspan3389-3-6"
+ sodipodi:role="line">VAR</tspan></text>
+ <path
+ sodipodi:open="true"
+ sodipodi:end="3.6937671"
+ sodipodi:start="2.5853841"
+ transform="matrix(-3.333333,0,0,1.666667,380.75449,-65.999999)"
+ d="m 5.3391583,18.440291 a 2.25,3.6750026 0 0 1 -0.00478,-3.867976"
+ sodipodi:ry="3.6750026"
+ sodipodi:rx="2.25"
+ sodipodi:cy="16.5"
+ sodipodi:cx="7.25"
+ id="path3855"
+ style="fill:none;stroke:#000000;stroke-width:0.84852809;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc" />
+ <g
+ style="display:inline"
+ id="g3539-4"
+ inkscape:label="Calque 1"
+ transform="translate(364,-53)">
+ <rect
+ y="9"
+ x="10"
+ height="8"
+ width="9"
+ id="rect3541-3"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" />
+ <path
+ inkscape:connector-curvature="0"
+ inkscape:export-ydpi="90"
+ inkscape:export-xdpi="90"
+ sodipodi:nodetypes="cccccccccccccccccccccc"
+ id="path3543-6"
+ d="m 19,9 -4,0 0,-2 -1,0 0,2 -4,0 0,8 4,0 0,2 1,0 0,-2 4,0 0,-4 2,0 0,-1 -2,0 z m -1,1 0,6 -7,0 0,-6 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible" />
+ <text
+ transform="scale(0.94258374,1.0609137)"
+ sodipodi:linespacing="125%"
+ id="text3545-3"
+ y="13.081616"
+ x="12.051801"
+ style="font-size:2.41527915px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ id="tspan1952-7"
+ y="13.081616"
+ x="12.051801"
+ sodipodi:role="line">STEP</tspan></text>
+ </g>
+ <g
+ style="display:inline"
+ id="g3445-5"
+ inkscape:label="Calque 1"
+ transform="translate(383,-51)">
+ <text
+ transform="scale(1.0096698,0.99042281)"
+ sodipodi:linespacing="125%"
+ id="text3447-6"
+ y="11.106368"
+ x="15.846765"
+ style="font-size:4.09374714px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="11.106368"
+ x="15.846765"
+ id="tspan3449-5"
+ sodipodi:role="line">JP</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccc"
+ id="path1941-7"
+ d="M 11,7 11,11 8,9 11.516466,17 15,9 12,11 12,7 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none" />
+ </g>
</g>
</svg>
--- a/PLCControler.py Sun Oct 09 23:31:13 2011 +0200
+++ b/PLCControler.py Sun Oct 09 23:31:50 2011 +0200
@@ -23,7 +23,7 @@
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from xml.dom import minidom
-from types import StringType, UnicodeType
+from types import StringType, UnicodeType, TupleType
import cPickle
import os,sys,re
import datetime
@@ -311,7 +311,14 @@
self.Project.setcontentHeader(properties)
if name is not None or properties is not None:
self.BufferProject()
-
+
+ # Return project name
+ def GetProjectName(self, debug=False):
+ project = self.GetProject(debug)
+ if project is not None:
+ return project.getname()
+ return None
+
# Return project properties
def GetProjectProperties(self, debug = False):
project = self.GetProject(debug)
@@ -1358,6 +1365,8 @@
return True
def IsLocatableType(self, type, debug = False):
+ if isinstance(type, TupleType):
+ return False
project = self.GetProject(debug)
if project is not None:
datatype = project.getdataType(type)
@@ -2695,6 +2704,13 @@
return True
#-------------------------------------------------------------------------------
+# Search in Current Project Functions
+#-------------------------------------------------------------------------------
+
+ def SearchInProject(self, criteria):
+ return self.Project.Search(criteria)
+
+#-------------------------------------------------------------------------------
# Current Buffering Management Functions
#-------------------------------------------------------------------------------
--- a/PLCGenerator.py Sun Oct 09 23:31:13 2011 +0200
+++ b/PLCGenerator.py Sun Oct 09 23:31:50 2011 +0200
@@ -40,6 +40,12 @@
pouTypeNames = {"function" : "FUNCTION", "functionBlock" : "FUNCTION_BLOCK", "program" : "PROGRAM"}
+errorVarTypes = {
+ "VAR_INPUT": "var_input",
+ "VAR_OUTPUT": "var_output",
+ "VAR_INOUT": "var_inout",
+}
+
# Helper function for reindenting text
def ReIndentText(text, nb_spaces):
compute = ""
@@ -247,57 +253,39 @@
var_number = 0
# Generate any global variable in configuration
for varlist in configuration.getglobalVars():
+ variable_type = errorVarTypes.get("VAR_GLOBAL", "var_local")
# Generate variable block with modifier
config += [(" VAR_GLOBAL", ())]
if varlist.getconstant():
- config += [(" CONSTANT", (tagname, "variable", (var_number, var_number + len(varlist.getvariable())), "constant"))]
+ config += [(" CONSTANT", (tagname, variable_type, (var_number, var_number + len(varlist.getvariable())), "constant"))]
elif varlist.getretain():
- config += [(" RETAIN", (tagname, "variable", (var_number, var_number + len(varlist.getvariable())), "retain"))]
+ config += [(" RETAIN", (tagname, variable_type, (var_number, var_number + len(varlist.getvariable())), "retain"))]
elif varlist.getnonretain():
- config += [(" NON_RETAIN", (tagname, "variable", (var_number, var_number + len(varlist.getvariable())), "non_retain"))]
+ config += [(" NON_RETAIN", (tagname, variable_type, (var_number, var_number + len(varlist.getvariable())), "non_retain"))]
config += [("\n", ())]
# Generate any variable of this block
for var in varlist.getvariable():
vartype_content = var.gettype().getcontent()
- # Variable type is a user data type
if vartype_content["name"] == "derived":
var_type = vartype_content["value"].getname()
- # Variable type is a string type
- elif vartype_content["name"] in ["string", "wstring"]:
- var_type = vartype_content["name"].upper()
- # Variable type is an array
- elif vartype_content["name"] == "array":
- base_type = vartype_content["value"].baseType.getcontent()
- # Array derived directly from a user defined type
- if base_type["name"] == "derived":
- basetype_name = base_type["value"].getname()
- self.GenerateDataType(basetype_name)
- # Array derived directly from a string type
- elif base_type["name"] in ["string", "wstring"]:
- basetype_name = base_type["name"].upper()
- # Array derived directly from an elementary type
- else:
- basetype_name = base_type["name"]
- var_type = "ARRAY [%s] OF %s" % (",".join(map(lambda x : "%s..%s" % (x.getlower(), x.getupper()), vartype_content["value"].getdimension())), basetype_name)
- # Variable type is an elementary type
- else:
- var_type = vartype_content["name"]
+ self.GenerateDataType(var_type)
+
config += [(" ", ()),
- (var.getname(), (tagname, "variable", var_number, "name")),
+ (var.getname(), (tagname, variable_type, var_number, "name")),
(" ", ())]
# Generate variable address if exists
address = var.getaddress()
if address:
config += [("AT ", ()),
- (address, (tagname, "variable", var_number, "address")),
+ (address, (tagname, variable_type, var_number, "address")),
(" ", ())]
config += [(": ", ()),
- (var_type, (tagname, "variable", var_number, "type"))]
+ (var.gettypeAsText(), (tagname, variable_type, var_number, "type"))]
# Generate variable initial value if exists
initial = var.getinitialValue()
if initial:
config += [(" := ", ()),
- (self.ComputeValue(initial.getvalue(), var_type), (tagname, "variable", var_number, "initial"))]
+ (self.ComputeValue(initial.getvalue(), var_type), (tagname, variable_type, var_number, "initial"))]
config += [(";\n", ())]
var_number += 1
config += [(" END_VAR\n", ())]
@@ -316,57 +304,39 @@
var_number = 0
# Generate any global variable in configuration
for varlist in resource.getglobalVars():
+ variable_type = errorVarTypes.get("VAR_GLOBAL", "var_local")
# Generate variable block with modifier
resrce += [(" VAR_GLOBAL", ())]
if varlist.getconstant():
- resrce += [(" CONSTANT", (tagname, "variable", (var_number, var_number + len(varlist.getvariable())), "constant"))]
+ resrce += [(" CONSTANT", (tagname, variable_type, (var_number, var_number + len(varlist.getvariable())), "constant"))]
elif varlist.getretain():
- resrce += [(" RETAIN", (tagname, "variable", (var_number, var_number + len(varlist.getvariable())), "retain"))]
+ resrce += [(" RETAIN", (tagname, variable_type, (var_number, var_number + len(varlist.getvariable())), "retain"))]
elif varlist.getnonretain():
- resrce += [(" NON_RETAIN", (tagname, "variable", (var_number, var_number + len(varlist.getvariable())), "non_retain"))]
+ resrce += [(" NON_RETAIN", (tagname, variable_type, (var_number, var_number + len(varlist.getvariable())), "non_retain"))]
resrce += [("\n", ())]
# Generate any variable of this block
for var in varlist.getvariable():
vartype_content = var.gettype().getcontent()
- # Variable type is a user data type
if vartype_content["name"] == "derived":
var_type = vartype_content["value"].getname()
- # Variable type is a string type
- elif vartype_content["name"] in ["string", "wstring"]:
- var_type = vartype_content["name"].upper()
- # Variable type is an array
- elif vartype_content["name"] == "array":
- base_type = vartype_content["value"].baseType.getcontent()
- # Array derived directly from a user defined type
- if base_type["name"] == "derived":
- basetype_name = base_type["value"].getname()
- self.GenerateDataType(basetype_name)
- # Array derived directly from a string type
- elif base_type["name"] in ["string", "wstring"]:
- basetype_name = base_type["name"].upper()
- # Array derived directly from an elementary type
- else:
- basetype_name = base_type["name"]
- var_type = "ARRAY [%s] OF %s" % (",".join(map(lambda x : "%s..%s" % (x.getlower(), x.getupper()), vartype_content["value"].getdimension())), basetype_name)
- # Variable type is an elementary type
- else:
- var_type = vartype_content["name"]
+ self.GenerateDataType(var_type)
+
resrce += [(" ", ()),
- (var.getname(), (tagname, "variable", var_number, "name")),
+ (var.getname(), (tagname, variable_type, var_number, "name")),
(" ", ())]
address = var.getaddress()
# Generate variable address if exists
if address:
resrce += [("AT ", ()),
- (address, (tagname, "variable", var_number, "address")),
+ (address, (tagname, variable_type, var_number, "address")),
(" ", ())]
resrce += [(": ", ()),
- (var_type, (tagname, "variable", var_number, "type"))]
+ (var.gettypeAsText(), (tagname, variable_type, var_number, "type"))]
# Generate variable initial value if exists
initial = var.getinitialValue()
if initial:
resrce += [(" := ", ()),
- (self.ComputeValue(initial.getvalue(), var_type), (tagname, "variable", var_number, "initial"))]
+ (self.ComputeValue(initial.getvalue(), var_type), (tagname, variable_type, var_number, "initial"))]
resrce += [(";\n", ())]
var_number += 1
resrce += [(" END_VAR\n", ())]
@@ -598,6 +568,7 @@
else:
variables.append(variable)
else:
+ self.ParentGenerator.GenerateDataType(var_type)
initial = var.getinitialValue()
if initial:
initial_value = initial.getvalue()
@@ -609,30 +580,13 @@
else:
variables.append((vartype_content["value"].getname(), var.getname(), None, initial_value))
else:
+ var_type = var.gettypeAsText()
initial = var.getinitialValue()
if initial:
initial_value = initial.getvalue()
else:
initial_value = None
address = var.getaddress()
- if vartype_content["name"] in ["string", "wstring"]:
- var_type = vartype_content["name"].upper()
- # Variable type is an array
- elif vartype_content["name"] == "array":
- base_type = vartype_content["value"].baseType.getcontent()
- # Array derived directly from a user defined type
- if base_type["name"] == "derived":
- basetype_name = base_type["value"].getname()
- self.ParentGenerator.GenerateDataType(basetype_name)
- # Array derived directly from a string type
- elif base_type["name"] in ["string", "wstring"]:
- basetype_name = base_type["name"].upper()
- # Array derived directly from an elementary type
- else:
- basetype_name = base_type["name"]
- var_type = "ARRAY [%s] OF %s" % (",".join(map(lambda x : "%s..%s" % (x.getlower(), x.getupper()), vartype_content["value"].getdimension())), basetype_name)
- else:
- var_type = vartype_content["name"]
if address is not None:
located.append((var_type, var.getname(), address, initial_value))
else:
@@ -879,6 +833,10 @@
block_type = instance.gettypeName()
self.ParentGenerator.GeneratePouProgram(block_type)
block_infos = self.GetBlockType(block_type, tuple([self.ConnectionTypes.get(variable.connectionPointIn, "ANY") for variable in instance.inputVariables.getvariable() if variable.getformalParameter() != "EN"]))
+ if block_infos is None:
+ block_infos = self.GetBlockType(block_type)
+ if block_infos is None:
+ raise PLCGenException, _("Undefined block type \"%s\" in \"%s\" POU")%(block_type, self.Name)
block_infos["generate"](self, instance, block_infos, body, None)
elif isinstance(instance, plcopen.commonObjects_connector):
connector = instance.getname()
@@ -934,6 +892,10 @@
block_type = next.gettypeName()
self.ParentGenerator.GeneratePouProgram(block_type)
block_infos = self.GetBlockType(block_type, tuple([self.ConnectionTypes.get(variable.connectionPointIn, "ANY") for variable in next.inputVariables.getvariable() if variable.getformalParameter() != "EN"]))
+ if block_infos is None:
+ block_infos = self.GetBlockType(block_type)
+ if block_infos is None:
+ raise PLCGenException, _("Undefined block type \"%s\" in \"%s\" POU")%(block_type, self.Name)
paths.append(str(block_infos["generate"](self, next, block_infos, body, connection, order)))
elif isinstance(next, plcopen.commonObjects_continuation):
name = next.getname()
@@ -1331,24 +1293,25 @@
raise PLCGenException, _("No body defined in \"%s\" POU")%self.Name
var_number = 0
for list_type, option, located, variables in self.Interface:
+ variable_type = errorVarTypes.get(list_type, "var_local")
program += [(" %s"%list_type, ())]
if option is not None:
- program += [(" %s"%option, (self.TagName, "variable", (var_number, var_number + len(variables)), option.lower()))]
+ program += [(" %s"%option, (self.TagName, variable_type, (var_number, var_number + len(variables)), option.lower()))]
program += [("\n", ())]
for var_type, var_name, var_address, var_initial in variables:
program += [(" ", ())]
if var_name:
- program += [(var_name, (self.TagName, "variable", var_number, "name")),
+ program += [(var_name, (self.TagName, variable_type, var_number, "name")),
(" ", ())]
if var_address != None:
program += [("AT ", ()),
- (var_address, (self.TagName, "variable", var_number, "address")),
+ (var_address, (self.TagName, variable_type, var_number, "address")),
(" ", ())]
program += [(": ", ()),
- (var_type, (self.TagName, "variable", var_number, "type"))]
+ (var_type, (self.TagName, variable_type, var_number, "type"))]
if var_initial != None:
program += [(" := ", ()),
- (self.ParentGenerator.ComputeValue(var_initial, var_type), (self.TagName, "variable", var_number, "initial"))]
+ (self.ParentGenerator.ComputeValue(var_initial, var_type), (self.TagName, variable_type, var_number, "initial"))]
program += [(";\n", ())]
var_number += 1
program += [(" END_VAR\n", ())]
--- a/PLCOpenEditor.py Sun Oct 09 23:31:13 2011 +0200
+++ b/PLCOpenEditor.py Sun Oct 09 23:31:50 2011 +0200
@@ -114,6 +114,7 @@
from DataTypeEditor import *
from PLCControler import *
from VariablePanel import VariablePanel
+from SearchResultPanel import SearchResultPanel
# Define PLCOpenEditor controls id
[ID_PLCOPENEDITOR, ID_PLCOPENEDITORLEFTNOTEBOOK,
@@ -135,8 +136,9 @@
# Define PLCOpenEditor EditMenu extra items id
[ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, ID_PLCOPENEDITOREDITMENUADDDATATYPE,
ID_PLCOPENEDITOREDITMENUADDFUNCTION, ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK,
- ID_PLCOPENEDITOREDITMENUADDPROGRAM, ID_PLCOPENEDITOREDITMENUADDCONFIGURATION,
-] = [wx.NewId() for _init_coll_EditMenu_Items in range(6)]
+ ID_PLCOPENEDITOREDITMENUADDPROGRAM, ID_PLCOPENEDITOREDITMENUADDCONFIGURATION,
+ ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT,
+] = [wx.NewId() for _init_coll_EditMenu_Items in range(7)]
#-------------------------------------------------------------------------------
@@ -330,6 +332,9 @@
AppendMenu(parent, help='', id=wx.ID_PASTE,
kind=wx.ITEM_NORMAL, text=_(u'Paste\tCTRL+V'))
parent.AppendSeparator()
+ AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT,
+ kind=wx.ITEM_NORMAL, text=_(u'Search in Project'))
+ parent.AppendSeparator()
addmenu = wx.Menu(title='')
parent.AppendMenu(wx.ID_ADD, _("Add Element"), addmenu)
AppendMenu(addmenu, help='', id=ID_PLCOPENEDITOREDITMENUADDDATATYPE,
@@ -352,6 +357,8 @@
self.Bind(wx.EVT_MENU, self.OnCutMenu, id=wx.ID_CUT)
self.Bind(wx.EVT_MENU, self.OnCopyMenu, id=wx.ID_COPY)
self.Bind(wx.EVT_MENU, self.OnPasteMenu, id=wx.ID_PASTE)
+ self.Bind(wx.EVT_MENU, self.OnSearchInProjectMenu,
+ id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT)
self.Bind(wx.EVT_MENU, self.OnAddDataTypeMenu,
id=ID_PLCOPENEDITOREDITMENUADDDATATYPE)
self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("function"),
@@ -594,6 +601,9 @@
self.VariablePanelIndexer = VariablePanelIndexer(self.BottomNoteBook, self)
self.BottomNoteBook.AddPage(self.VariablePanelIndexer, _("Variables"))
+ self.SearchResultPanel = SearchResultPanel(self.BottomNoteBook, self)
+ self.BottomNoteBook.AddPage(self.SearchResultPanel, _("Search"))
+
self.LibraryPanel = wx.Panel(id=ID_PLCOPENEDITORLIBRARYPANEL,
name='LibraryPanel', parent=self.RightNoteBook, pos=wx.Point(0,
0), size=wx.Size(0, 0), style=0)
@@ -613,9 +623,9 @@
id=ID_PLCOPENEDITORLIBRARYTREE)
self.LibraryComment = wx.TextCtrl(id=ID_PLCOPENEDITORLIBRARYCOMMENT,
- name='LibraryComment', parent=self.LibraryPanel,
- pos=wx.Point(0, 0), size=wx.Size(0, 160),
- style=wx.TE_READONLY|wx.TE_MULTILINE)
+ name='LibraryComment', parent=self.LibraryPanel,
+ pos=wx.Point(0, 0), size=wx.Size(0, 160),
+ style=wx.TE_READONLY|wx.TE_MULTILINE)
self._init_sizers()
@@ -683,7 +693,7 @@
self.CurrentToolBar = []
self.CurrentLanguage = ""
self.SelectedItem = None
- self.Errors = []
+ self.Highlights = {}
self.DrawingMode = FREEDRAWING_MODE
#self.DrawingMode = DRIVENDRAWING_MODE
if USE_AUI:
@@ -992,6 +1002,7 @@
#self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, True)
#self.EditMenu.Check(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO,
# self.Controler.IsProjectBufferEnabled())
+ self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True)
self.EditMenu.Enable(wx.ID_ADD, True)
self.EditMenu.Enable(wx.ID_DELETE, True)
if self.TabsOpened.GetPageCount() > 0:
@@ -1015,6 +1026,7 @@
self.EditMenu.Enable(wx.ID_COPY, False)
self.EditMenu.Enable(wx.ID_PASTE, False)
self.EditMenu.Enable(wx.ID_SELECTALL, False)
+ self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, False)
self.EditMenu.Enable(wx.ID_ADD, False)
self.EditMenu.Enable(wx.ID_DELETE, False)
@@ -1086,7 +1098,15 @@
event.m_keyCode = wx.WXK_DELETE
window.ProcessEvent(event)
-
+ def OnSearchInProjectMenu(self, event):
+ dialog = SearchInProjectDialog(self)
+ if dialog.ShowModal() == wx.ID_OK:
+ criteria = dialog.GetCriteria()
+ result = self.Controler.SearchInProject(criteria)
+ self.ClearSearchResults()
+ self.SearchResultPanel.SetSearchResults(criteria, result)
+ self.BottomNoteBook.SetSelection(self.BottomNoteBook.GetPageIndex(self.SearchResultPanel))
+
#-------------------------------------------------------------------------------
# Display Menu Functions
#-------------------------------------------------------------------------------
@@ -1183,10 +1203,12 @@
if USE_AUI:
for child in self.TabsOpened.GetChildren():
if isinstance(child, wx.aui.AuiTabCtrl):
- window = child.GetWindowFromIdx(child.GetActivePage())
- window.RefreshView()
- if not window.IsDebugging() and self.TabsOpened.GetPageIndex(window) == selected and variablepanel:
- self.RefreshVariablePanel(window.GetTagName())
+ active_page = child.GetActivePage()
+ if active_page >= 0:
+ window = child.GetWindowFromIdx(child.GetActivePage())
+ window.RefreshView()
+ if not window.IsDebugging() and self.TabsOpened.GetPageIndex(window) == selected and variablepanel:
+ self.RefreshVariablePanel(window.GetTagName())
elif selected >= 0:
window = self.TabsOpened.GetPage(idx)
window.RefreshView()
@@ -1273,12 +1295,9 @@
item_name = _(item_name)
self.TypesTree.SetItemText(root, item_name)
self.TypesTree.SetPyData(root, infos["type"])
- if infos.get("tagname", None) in self.Errors:
- self.TypesTree.SetItemBackgroundColour(root, wx.Colour(255, 255, 0))
- self.TypesTree.SetItemTextColour(root, wx.RED)
- else:
- self.TypesTree.SetItemBackgroundColour(root, wx.WHITE)
- self.TypesTree.SetItemTextColour(root, wx.BLACK)
+ highlight_colours = self.Highlights.get(infos.get("tagname", None), (wx.WHITE, wx.BLACK))
+ self.TypesTree.SetItemBackgroundColour(root, highlight_colours[0])
+ self.TypesTree.SetItemTextColour(root, highlight_colours[1])
if infos["type"] == ITEM_POU:
self.TypesTree.SetItemImage(root, self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"])])
else:
@@ -2422,32 +2441,48 @@
#-------------------------------------------------------------------------------
-# Errors showing functions
-#-------------------------------------------------------------------------------
-
- def ShowError(self, infos, start, end):
- self.EditProjectElement(self.Controler.GetElementType(infos[0]), infos[0])
+# Highlights showing functions
+#-------------------------------------------------------------------------------
+
+ def ShowHighlight(self, infos, start, end, highlight_type):
+ print infos, start, end, highlight_type
self.SelectTypesTreeItem(infos[0])
if infos[1] == "name":
- self.Errors.append(infos[0])
+ self.Highlights[infos[0]] = highlight_type
self.RefreshTypesTree()
self.TypesTree.Unselect()
- elif infos[1] == "variable":
- self.VariablePanelIndexer.AddVariableError(infos)
else:
- selected = self.TabsOpened.GetSelection()
- if selected != -1:
- viewer = self.TabsOpened.GetPage(selected)
- viewer.AddShownError(infos[1:], start, end)
- viewer.RefreshView()
-
- def ClearErrors(self):
- self.Errors = []
+ self.EditProjectElement(self.Controler.GetElementType(infos[0]), infos[0])
+ if infos[1] in plcopen.searchResultVarTypes.values() + ["var_local"]:
+ self.VariablePanelIndexer.AddVariableHighlight(infos, highlight_type)
+ else:
+ selected = self.TabsOpened.GetSelection()
+ if selected != -1:
+ viewer = self.TabsOpened.GetPage(selected)
+ viewer.AddHighlight(infos[1:], start, end, highlight_type)
+
+ def ShowError(self, infos, start, end):
+ self.ShowHighlight(infos, start, end, ERROR_HIGHLIGHT)
+
+ def ShowSearchResult(self, infos, start, end):
+ self.ShowHighlight(infos, start, end, SEARCH_RESULT_HIGHLIGHT)
+
+ def ClearHighlights(self, highlight_type=None):
+ if highlight_type is None:
+ self.Highlights = {}
+ else:
+ self.Highlights = dict([(name, highlight) for name, highlight in self.Highlights.iteritems() if highlight != highlight_type])
self.RefreshTypesTree()
- self.VariablePanelIndexer.ClearErrors()
+ self.VariablePanelIndexer.ClearHighlights(highlight_type)
for i in xrange(self.TabsOpened.GetPageCount()):
viewer = self.TabsOpened.GetPage(i)
- viewer.ClearErrors()
+ viewer.ClearHighlights(highlight_type)
+
+ def ClearErrors(self):
+ self.ClearHighlights(ERROR_HIGHLIGHT)
+
+ def ClearSearchResults(self):
+ self.ClearHighlights(SEARCH_RESULT_HIGHLIGHT)
#-------------------------------------------------------------------------------
# PLCOpenEditor Main Class
@@ -3814,6 +3849,12 @@
# Variables Editor Panel
#-------------------------------------------------------------------------------
+def PouTagname(tagname):
+ words = tagname.split("::")
+ if words[0] in ["T", "A"]:
+ return "P::%s" % words[1]
+ return tagname
+
class VariablePanelIndexer(wx.Panel):
def _init_sizers(self):
@@ -3837,58 +3878,72 @@
self.CurrentPanel = None
def AddVariablePanel(self, tagname, element_type):
- new_panel = VariablePanel(self, self.ParentWindow, self.ParentWindow.Controler, element_type)
- new_panel.SetTagName(tagname)
- new_panel.Hide()
- new_panel.RefreshView()
- self.MainSizer.AddWindow(new_panel, 1, border=0, flag=wx.GROW)
- self.VariablePanelList[tagname] = new_panel
+ tagname = PouTagname(tagname)
+ panel, users = self.VariablePanelList.get(tagname, (None, 0))
+ if panel is None:
+ panel = VariablePanel(self, self.ParentWindow, self.ParentWindow.Controler, element_type)
+ panel.SetTagName(tagname)
+ panel.Hide()
+ panel.RefreshView()
+ self.MainSizer.AddWindow(panel, 1, border=0, flag=wx.GROW)
+ self.VariablePanelList[tagname] = (panel, users + 1)
def RemoveVariablePanel(self, tagname):
- if tagname in self.VariablePanelList:
- panel = self.VariablePanelList.pop(tagname)
+ tagname = PouTagname(tagname)
+ panel, users = self.VariablePanelList.pop(tagname, (None, 0))
+ if panel is not None:
+ if users > 1:
+ self.VariablePanelList[tagname] = (panel, users - 1)
+ else:
+ self.MainSizer.Remove(panel)
+ panel.Destroy()
+ if self.CurrentPanel == tagname:
+ self.CurrentPanel = None
+
+ def RemoveAllPanels(self):
+ for panel, users in self.VariablePanelList.itervalues():
self.MainSizer.Remove(panel)
panel.Destroy()
- if self.CurrentPanel == tagname:
- self.CurrentPanel = None
-
- def RemoveAllPanels(self):
- for tagname in self.VariablePanelList.keys():
- self.RemoveVariablePanel(tagname)
+ self.VariablePanelList = {}
+ self.CurrentPanel = None
def UpdateVariablePanelTagName(self, old_tagname, new_tagname):
- if old_tagname in self.VariablePanelList:
+ old_tagname = PouTagname(old_tagname)
+ new_tagname = PouTagname(new_tagname)
+ if old_tagname in self.VariablePanelList and old_tagname != new_tagname:
self.VariablePanelList[new_tagname] = self.VariablePanelList.pop(old_tagname)
if self.CurrentPanel == old_tagname:
self.CurrentPanel = new_tagname
def ChangeVariablePanel(self, tagname):
- if tagname in self.VariablePanelList:
- if tagname != self.CurrentPanel:
- if self.CurrentPanel is not None:
- self.VariablePanelList[self.CurrentPanel].Hide()
- self.CurrentPanel = tagname
- self.VariablePanelList[self.CurrentPanel].RefreshView()
- self.VariablePanelList[self.CurrentPanel].Show()
- self.MainSizer.Layout()
- else:
+ tagname = PouTagname(tagname)
+ panel, users = self.VariablePanelList.get(tagname, (None, 0))
+ if panel is None:
if self.CurrentPanel is not None:
- self.VariablePanelList[self.CurrentPanel].Hide()
+ self.VariablePanelList[self.CurrentPanel][0].Hide()
self.CurrentPanel = None
self.MainSizer.Layout()
+ elif tagname != self.CurrentPanel:
+ if self.CurrentPanel is not None:
+ self.VariablePanelList[self.CurrentPanel][0].Hide()
+ self.CurrentPanel = tagname
+ panel.RefreshView()
+ panel.Show()
+ self.MainSizer.Layout()
def RefreshVariablePanel(self, tagname):
- if tagname in self.VariablePanelList:
- self.VariablePanelList[self.CurrentPanel].RefreshView()
-
- def AddVariableError(self, infos):
+ panel, users = self.VariablePanelList.get(PouTagname(tagname), (None, 0))
+ if panel is not None:
+ panel.RefreshView()
+
+ def AddVariableHighlight(self, infos, highlight_type):
self.ChangeVariablePanel(infos[0])
if self.CurrentPanel is not None:
- self.VariablePanelList[self.CurrentPanel].AddVariableError(infos[2:])
-
- def ClearErrors(self):
- for panel in self.VariablePanelList.values():
- panel.ClearErrors()
+ self.VariablePanelList[self.CurrentPanel][0].AddVariableHighlight(infos[2:], highlight_type)
+
+ def ClearHighlights(self, highlight_type=None):
+ for panel, users in self.VariablePanelList.values():
+ panel.ClearHighlights(highlight_type)
#-------------------------------------------------------------------------------
# Debug Variables Panel
--- a/RessourceEditor.py Sun Oct 09 23:31:13 2011 +0200
+++ b/RessourceEditor.py Sun Oct 09 23:31:50 2011 +0200
@@ -101,7 +101,7 @@
wx.grid.PyGridTableBase.__init__(self)
self.data = data
self.colnames = colnames
- self.Errors = {}
+ self.Highlights = {}
self.Parent = parent
self.ColAlignements = []
@@ -210,6 +210,7 @@
grid.SetColSize(col, self.ColSizes[col])
for row in range(self.GetNumberRows()):
+ row_highlights = self.Highlights.get(row, {})
for col in range(self.GetNumberCols()):
editor = None
renderer = None
@@ -244,13 +245,9 @@
grid.SetCellEditor(row, col, editor)
grid.SetCellRenderer(row, col, renderer)
- if row in self.Errors and self.Errors[row][0] == colname.lower():
- grid.SetCellBackgroundColour(row, col, wx.Colour(255, 255, 0))
- grid.SetCellTextColour(row, col, wx.RED)
- grid.MakeCellVisible(row, col)
- else:
- grid.SetCellTextColour(row, col, wx.BLACK)
- grid.SetCellBackgroundColour(row, col, wx.WHITE)
+ highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1]
+ grid.SetCellBackgroundColour(row, col, highlight_colours[0])
+ grid.SetCellTextColour(row, col, highlight_colours[1])
if wx.Platform == '__WXMSW__':
grid.SetRowMinimalHeight(row, 20)
else:
@@ -289,11 +286,31 @@
self.data = []
self.editors = []
- def AddError(self, infos):
- self.Errors[infos[0]] = infos[1:]
-
- def ClearErrors(self):
- self.Errors = {}
+#-------------------------------------------------------------------------------
+# Highlights showing functions
+#-------------------------------------------------------------------------------
+
+ def OnRefreshHighlightsTimer(self, event):
+ self.Table.ResetView(self.VariablesGrid)
+ event.Skip()
+
+ def AddHighlight(self, infos, highlight_type):
+ row_highlights = self.Highlights.setdefault(infos[0], {})
+ col_highlights = row_highlights.setdefault(infos[1], [])
+ col_highlights.append(highlight_type)
+ self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
+
+ def ClearHighlights(self, highlight_type=None):
+ if highlight_type is None:
+ self.Highlights = {}
+ else:
+ for row, row_highlights in self.Highlights.iteritems():
+ row_items = row_highlights.items()
+ for col, col_highlights in row_items:
+ if highlight_type in col_highlights:
+ col_highlights.remove(highlight_type)
+ if len(col_highlights) == 0:
+ row_highlights.pop(col)
[ID_RESOURCEEDITOR, ID_RESOURCEEDITORSTATICTEXT1,
ID_RESOURCEEDITORSTATICTEXT2, ID_RESOURCEEDITORINSTANCESGRID,
@@ -465,6 +482,9 @@
self.Controler = controler
self.TagName = tagname
+ self.RefreshHighlightsTimer = wx.Timer(self, -1)
+ self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
+
self.TasksDefaultValue = {"Name" : "", "Triggering" : "", "Single" : "", "Interval" : "", "Priority" : 0}
self.TasksTable = ResourceTable(self, [], GetTasksTableColnames())
self.TasksTable.SetColAlignements([wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_RIGHT, wx.ALIGN_RIGHT])
@@ -481,6 +501,9 @@
self.InstancesGrid.SetRowLabelSize(0)
self.InstancesTable.ResetView(self.InstancesGrid)
+ def __del__(self):
+ self.RefreshHighlightsTimer.Stop()
+
def SetTagName(self, tagname):
self.TagName = tagname
@@ -668,21 +691,37 @@
event.Skip()
#-------------------------------------------------------------------------------
-# Errors showing functions
-#-------------------------------------------------------------------------------
-
- def ClearErrors(self):
- self.TasksTable.ClearErrors()
- self.InstancesTable.ClearErrors()
+# Search result showing functions
+#-------------------------------------------------------------------------------
+
+ def AddShownSearchResult(self, infos, start, end):
+ if infos[0] == "task":
+ self.TasksTable.AddSearchResult(infos[1:])
+ elif infos[0] == "instance":
+ self.InstancesTable.AddSearchResult(infos[1:])
+
+ def ClearSearchResults(self):
+ self.TasksTable.ClearSearchResults()
+ self.InstancesTable.ClearSearchResults()
self.TasksTable.ResetView(self.TasksGrid)
self.InstancesTable.ResetView(self.InstancesGrid)
- def AddShownError(self, infos, start, end):
+
+#-------------------------------------------------------------------------------
+# Highlights showing functions
+#-------------------------------------------------------------------------------
+
+ def AddHighlight(self, infos, start, end, highlight_type):
if infos[0] == "task":
- self.TasksTable.AddError(infos[1:])
+ self.TasksTable.AddHighlight(infos[1:], highlight_type)
elif infos[0] == "instance":
- self.InstancesTable.AddError(infos[1:])
-
+ self.InstancesTable.AddHighlight(infos[1:], highlight_type)
+
+ def ClearHighlights(self, highlight_type=None):
+ self.TasksTable.ClearHighlights(highlight_type)
+ self.InstancesTable.ClearHighlights(highlight_type)
+ self.TasksTable.ResetView(self.TasksGrid)
+ self.InstancesTable.ResetView(self.InstancesGrid)
class DurationCellControl(wx.PyControl):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SearchResultPanel.py Sun Oct 09 23:31:50 2011 +0200
@@ -0,0 +1,327 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
+#based on the plcopen standard.
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+from types import TupleType
+
+import wx
+import wx.lib.agw.customtreectrl as CT
+
+from PLCControler import *
+
+CWD = os.path.split(os.path.realpath(__file__))[0]
+
+def GenerateName(infos):
+ if infos[0] in ["input", "output"]:
+ return "%s %d:" % (infos[0], infos[1])
+ return "%s:" % infos[0]
+
+#-------------------------------------------------------------------------------
+# Search Result Panel
+#-------------------------------------------------------------------------------
+
+[ID_SEARCHRESULTPANEL, ID_SEARCHRESULTPANELHEADERLABEL,
+ ID_SEARCHRESULTPANELSEARCHRESULTSTREE, ID_SEARCHRESULTPANELRESETBUTTON,
+] = [wx.NewId() for _init_ctrls in range(4)]
+
+class SearchResultPanel(wx.Panel):
+
+ if wx.VERSION < (2, 6, 0):
+ def Bind(self, event, function, id = None):
+ if id is not None:
+ event(self, id, function)
+ else:
+ event(self, function)
+
+ def _init_coll_MainSizer_Items(self, parent):
+ parent.AddSizer(self.HeaderSizer, 0, border=0, flag=wx.GROW)
+ parent.AddWindow(self.SearchResultsTree, 1, border=0, flag=wx.GROW)
+
+ def _init_coll_MainSizer_Growables(self, parent):
+ parent.AddGrowableCol(0)
+ parent.AddGrowableRow(1)
+
+ def _init_coll_HeaderSizer_Items(self, parent):
+ parent.AddWindow(self.HeaderLabel, 1, border=5, flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
+ parent.AddWindow(self.ResetButton, 0, border=0, flag=0)
+
+ def _init_coll_HeaderSizer_Growables(self, parent):
+ parent.AddGrowableCol(0)
+
+ def _init_sizers(self):
+ self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
+ self.HeaderSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ self._init_coll_MainSizer_Items(self.MainSizer)
+ self._init_coll_MainSizer_Growables(self.MainSizer)
+ self._init_coll_HeaderSizer_Items(self.HeaderSizer)
+
+ self.SetSizer(self.MainSizer)
+
+ def _init_ctrls(self, prnt):
+ wx.Panel.__init__(self, id=ID_SEARCHRESULTPANEL,
+ name='SearchResultPanel', parent=prnt, pos=wx.Point(0, 0),
+ size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
+
+ self.HeaderLabel = wx.StaticText(id=ID_SEARCHRESULTPANELHEADERLABEL,
+ name='HeaderLabel', parent=self,
+ pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0)
+
+ self.SearchResultsTree = CT.CustomTreeCtrl(id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE,
+ name="SearchResultsTree", parent=self,
+ pos=wx.Point(0, 0), style=0)
+ self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnSearchResultsTreeItemActivated,
+ id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE)
+
+ self.ResetButton = wx.Button(id=ID_SEARCHRESULTPANELRESETBUTTON, label=_('Reset'),
+ name='ResetButton', parent=self, pos=wx.Point(0, 0),
+ size=wx.DefaultSize, style=0)
+ self.Bind(wx.EVT_BUTTON, self.OnResetButton,
+ id=ID_SEARCHRESULTPANELRESETBUTTON)
+
+ self._init_sizers()
+
+ def __init__(self, parent, window):
+ self._init_ctrls(parent)
+ self.ParentWindow = window
+
+ # Define Tree item icon list
+ self.TreeImageList = wx.ImageList(16, 16)
+ self.TreeImageDict = {}
+
+ # Icons for other items
+ for imgname, itemtype in [
+ #editables
+ ("PROJECT", ITEM_PROJECT),
+ ("TRANSITION", ITEM_TRANSITION),
+ ("ACTION", ITEM_ACTION),
+ ("CONFIGURATION", ITEM_CONFIGURATION),
+ ("RESOURCE", ITEM_RESOURCE),
+ ("DATATYPE", ITEM_DATATYPE),
+ ("ACTION", "action_block"),
+ ("IL", "IL"),
+ ("ST", "ST")]:
+ self.TreeImageDict[itemtype]=self.TreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%imgname)))
+
+ for itemtype in ["function", "functionBlock", "program",
+ "comment", "block", "io_variable",
+ "connector", "contact", "coil",
+ "step", "transition", "jump",
+ "var_local", "var_input",
+ "var_inout", "var_output"]:
+ self.TreeImageDict[itemtype]=self.TreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%itemtype.upper())))
+
+ # Assign icon list to TreeCtrl
+ self.SearchResultsTree.SetImageList(self.TreeImageList)
+
+ self.ResetSearchResults()
+
+ def SetSearchResults(self, criteria, search_results):
+ self.Criteria = criteria
+
+ for infos, start, end, text in search_results:
+ if infos[0] not in self.ElementsOrder:
+ self.ElementsOrder.append(infos[0])
+
+ results = self.SearchResults.setdefault(infos[0], [])
+ results.append((infos, start, end, text))
+
+ self.RefreshView()
+
+ def ResetSearchResults(self):
+ self.Criteria = None
+ self.ElementsOrder = []
+ self.SearchResults = {}
+ self.RefreshView()
+
+ def RefreshView(self):
+ if self.Criteria is None:
+ self.HeaderLabel.SetLabel(_("No search results available."))
+ self.SearchResultsTree.DeleteAllItems()
+ self.ResetButton.Enable(False)
+ else:
+ matches_number = 0
+ search_results_tree_infos = {"name": _("Project '%s':") % self.ParentWindow.Controler.GetProjectName(),
+ "type": ITEM_PROJECT,
+ "data": None,
+ "text": None,
+ "matches": None,
+ }
+ search_results_tree_children = search_results_tree_infos.setdefault("children", [])
+ for tagname in self.ElementsOrder:
+ results = self.SearchResults.get(tagname, [])
+ matches_number += len(results)
+
+ words = tagname.split("::")
+
+ element_type = self.ParentWindow.Controler.GetElementType(tagname)
+ if element_type == ITEM_POU:
+ element_type = self.ParentWindow.Controler.GetPouType(words[1])
+
+ element_infos = {"name": words[-1],
+ "type": element_type,
+ "data": tagname,
+ "text": None,
+ "matches": len(results)}
+
+ children = element_infos.setdefault("children", [])
+ for infos, start, end, text in results:
+ child_type = infos[1]
+ if child_type == "body":
+ child_name = "body"
+ if element_type == ITEM_TRANSITION:
+ child_type = self.ParentWindow.Controler.GetTransitionBodyType(words[1], words[2])
+ elif element_type == ITEM_ACTION:
+ child_type = self.ParentWindow.Controler.GetActionBodyType(words[1], words[2])
+ else:
+ child_type = self.ParentWindow.Controler.GetPouBodyType(words[1])
+ else:
+ child_name = GenerateName(infos[3:])
+ child_infos = {"name": child_name,
+ "type": child_type,
+ "data": (infos, start, end ,None),
+ "text": text,
+ "matches": 1,
+ "children": [],
+ }
+ children.append(child_infos)
+
+ if len(words) > 2:
+ for _element_infos in search_results_tree_children:
+ if _element_infos["name"] == words[1]:
+ _element_infos["matches"] += len(children)
+ _element_infos["children"].append(element_infos)
+ break
+ else:
+ search_results_tree_children.append(element_infos)
+
+ if matches_number < 2:
+ header_format = _("'%s' - %d match in project")
+ else:
+ header_format = _("'%s' - %d matches in project")
+
+ self.HeaderLabel.SetLabel(header_format % (self.Criteria["raw_pattern"], matches_number))
+ self.ResetButton.Enable(True)
+
+ if matches_number > 0:
+ root = self.SearchResultsTree.GetRootItem()
+ if root is None:
+ root = self.SearchResultsTree.AddRoot(search_results_tree_infos["name"])
+ self.GenerateSearchResultsTreeBranch(root, search_results_tree_infos)
+ self.SearchResultsTree.Expand(root)
+
+ def GetTextCtrlClickFunction(self, item):
+ def OnTextCtrlClick(event):
+ self.SearchResultsTree.SelectItem(item)
+ event.Skip()
+ return OnTextCtrlClick
+
+ def GetTextCtrlDClickFunction(self, item):
+ def OnTextCtrlDClick(event):
+ self.ShowSearchResults(item)
+ event.Skip()
+ return OnTextCtrlDClick
+
+ def GenerateSearchResultsTreeBranch(self, root, infos):
+ to_delete = []
+ if infos["name"] == "body":
+ item_name = "%d:" % infos["data"][1][0]
+ else:
+ item_name = infos["name"]
+
+ self.SearchResultsTree.SetItemText(root, item_name)
+ self.SearchResultsTree.SetPyData(root, infos["data"])
+ self.SearchResultsTree.SetItemBackgroundColour(root, wx.WHITE)
+ self.SearchResultsTree.SetItemTextColour(root, wx.BLACK)
+ if infos["type"] is not None:
+ if infos["type"] == ITEM_POU:
+ self.SearchResultsTree.SetItemImage(root, self.TreeImageDict[self.ParentWindow.Controler.GetPouType(infos["name"])])
+ else:
+ self.SearchResultsTree.SetItemImage(root, self.TreeImageDict[infos["type"]])
+
+ if infos["text"] is not None:
+ text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0),
+ value=infos["text"], style=wx.BORDER_NONE|wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_RICH2)
+ width, height = wx.PaintDC(text_ctrl).GetTextExtent(infos["text"])
+ text_ctrl.SetClientSize(wx.Size(width + 1, height))
+ text_ctrl.SetBackgroundColour(self.SearchResultsTree.GetBackgroundColour())
+ text_ctrl.Bind(wx.EVT_LEFT_DOWN, self.GetTextCtrlClickFunction(root))
+ text_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.GetTextCtrlDClickFunction(root))
+ start, end = infos["data"][1:3]
+ text_lines = infos["text"].splitlines()
+ end_idx = reduce(lambda x, y: x + y, map(lambda x: len(x) + 1, text_lines[:end[0] - start[0]]), end[1] + 1)
+ text_ctrl.SetInsertionPoint(0)
+ text_ctrl.SetStyle(start[1], end_idx, wx.TextAttr(wx.BLACK, wx.Colour(206, 204, 247)))
+
+ self.SearchResultsTree.SetItemWindow(root, text_ctrl)
+
+ elif infos["type"] is not None and infos["matches"] > 1:
+ text = _("(%d matches)") % infos["matches"]
+ text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0),
+ value=text, style=wx.BORDER_NONE|wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_RICH2)
+ width, height = wx.PaintDC(text_ctrl).GetTextExtent(text)
+ text_ctrl.SetClientSize(wx.Size(width + 1, height))
+ text_ctrl.SetBackgroundColour(self.SearchResultsTree.GetBackgroundColour())
+ text_ctrl.Bind(wx.EVT_LEFT_DOWN, self.GetTextCtrlClickFunction(root))
+ text_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.GetTextCtrlDClickFunction(root))
+
+ text_ctrl.SetInsertionPoint(0)
+ text_ctrl.SetStyle(0, len(text), wx.TextAttr(wx.Colour(0, 127, 174)))
+
+ self.SearchResultsTree.SetItemWindow(root, text_ctrl)
+
+
+ if wx.VERSION >= (2, 6, 0):
+ item, root_cookie = self.SearchResultsTree.GetFirstChild(root)
+ else:
+ item, root_cookie = self.SearchResultsTree.GetFirstChild(root, 0)
+ for child in infos["children"]:
+ if item is None:
+ item = self.SearchResultsTree.AppendItem(root, "")
+ if wx.Platform != '__WXMSW__':
+ item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie)
+ self.GenerateSearchResultsTreeBranch(item, child)
+ item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie)
+ while item is not None:
+ to_delete.append(item)
+ item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie)
+ for item in to_delete:
+ self.SearchResultsTree.Delete(item)
+
+ def ShowSearchResults(self, item):
+ data = self.SearchResultsTree.GetPyData(item)
+ if isinstance(data, TupleType):
+ search_results = [data]
+ else:
+ search_results = self.SearchResults.get(data, [])
+ for infos, start, end, text in search_results:
+ self.ParentWindow.ShowSearchResult(infos, start, end)
+
+ def OnSearchResultsTreeItemActivated(self, event):
+ self.ShowSearchResults(event.GetItem())
+ event.Skip()
+
+ def OnResetButton(self, event):
+ self.ResetSearchResults()
+ self.ParentWindow.ClearSearchResults()
+ event.Skip()
--- a/TextViewer.py Sun Oct 09 23:31:13 2011 +0200
+++ b/TextViewer.py Sun Oct 09 23:31:50 2011 +0200
@@ -28,6 +28,8 @@
import re
+from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD
+
#-------------------------------------------------------------------------------
# Textual programs Viewer class
#-------------------------------------------------------------------------------
@@ -42,7 +44,7 @@
[STC_PLC_WORD, STC_PLC_COMMENT, STC_PLC_NUMBER, STC_PLC_STRING,
STC_PLC_VARIABLE, STC_PLC_PARAMETER, STC_PLC_FUNCTION, STC_PLC_JUMP,
- STC_PLC_ERROR] = range(9)
+ STC_PLC_ERROR, STC_PLC_SEARCH_RESULT] = range(10)
[SPACE, WORD, NUMBER, STRING, WSTRING, COMMENT] = range(6)
[ID_TEXTVIEWER,
@@ -70,6 +72,11 @@
LABEL_MODEL = re.compile("[ \t\n]%(identifier)s:[ \t\n]"%re_texts)
EXTENSIBLE_PARAMETER = re.compile("IN[1-9][0-9]*$")
+HIGHLIGHT_TYPES = {
+ ERROR_HIGHLIGHT: STC_PLC_ERROR,
+ SEARCH_RESULT_HIGHLIGHT: STC_PLC_SEARCH_RESULT,
+}
+
def GetCursorPos(old, new):
old_length = len(old)
new_length = len(new)
@@ -128,6 +135,7 @@
self.StyleSetSpec(STC_PLC_STRING, "fore:#007F00,size:%(size)d" % faces)
self.StyleSetSpec(STC_PLC_JUMP, "fore:#FF7FFF,size:%(size)d" % faces)
self.StyleSetSpec(STC_PLC_ERROR, "fore:#FF0000,back:#FFFF00,size:%(size)d" % faces)
+ self.StyleSetSpec(STC_PLC_SEARCH_RESULT, "fore:#FFFFFF,back:#FFA500,size:%(size)d" % faces)
# Indicators styles
self.IndicatorSetStyle(0, wx.stc.STC_INDIC_SQUIGGLE)
@@ -154,7 +162,7 @@
self.TextSyntax = "ST"
self.CurrentAction = None
self.TagName = tagname
- self.Errors = []
+ self.Highlights = []
self.Debug = debug
self.InstancePath = instancepath
self.ContextStack = []
@@ -163,6 +171,9 @@
self.ParentWindow = window
self.Controler = controler
+ self.RefreshHighlightsTimer = wx.Timer(self, -1)
+ self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
+
self.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT|
wx.stc.STC_MOD_BEFOREDELETE|
wx.stc.STC_PERFORMED_USER)
@@ -174,6 +185,9 @@
self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
self.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModification, id=ID_TEXTVIEWER)
+ def __del__(self):
+ self.RefreshHighlightsTimer.Stop()
+
def SetTagName(self, tagname):
self.TagName = tagname
@@ -568,7 +582,7 @@
self.SetStyling(current_pos - last_styled_pos, 31)
else:
self.SetStyling(current_pos - start_pos, 31)
- self.ShowErrors(start_pos, end_pos)
+ self.ShowHighlights(start_pos, end_pos)
event.Skip()
def Cut(self):
@@ -637,27 +651,39 @@
event.Skip()
#-------------------------------------------------------------------------------
-# Errors showing functions
+# Highlights showing functions
#-------------------------------------------------------------------------------
- def ClearErrors(self):
- self.Errors = []
+ def OnRefreshHighlightsTimer(self, event):
self.RefreshView()
-
- def AddShownError(self, infos, start, end):
- if infos[0] == "body":
- self.Errors.append((infos[1], start, end))
-
- def ShowErrors(self, start_pos, end_pos):
- for indent, start, end in self.Errors:
+ event.Skip()
+
+ def ClearHighlights(self, highlight_type=None):
+ if highlight_type is None:
+ self.Highlights = []
+ else:
+ highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None)
+ if highlight_type is not None:
+ self.Highlights = [(infos, start, end, highlight) for (infos, start, end, highlight) in self.Highlights if highlight != highlight_type]
+ self.RefreshView()
+
+ def AddHighlight(self, infos, start, end, highlight_type):
+ highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None)
+ if infos[0] == "body" and highlight_type is not None:
+ self.Highlights.append((infos[1], start, end, highlight_type))
+ self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
+
+ def ShowHighlights(self, start_pos, end_pos):
+ for indent, start, end, highlight_type in self.Highlights:
if start[0] == 0:
- error_start_pos = start[1] - indent
- else:
- error_start_pos = self.GetLineEndPosition(start[0] - 1) + start[1] - indent + 1
+ highlight_start_pos = start[1] - indent
+ else:
+ highlight_start_pos = self.GetLineEndPosition(start[0] - 1) + start[1] - indent + 1
if end[0] == 0:
- error_end_pos = end[1] - indent + 1
- else:
- error_end_pos = self.GetLineEndPosition(end[0] - 1) + end[1] - indent + 2
- if start_pos <= error_start_pos <= end_pos or start_pos <= error_end_pos <= end_pos:
- self.StartStyling(error_start_pos, 0xff)
- self.SetStyling(error_end_pos - error_start_pos, STC_PLC_ERROR)
+ highlight_end_pos = end[1] - indent + 1
+ else:
+ highlight_end_pos = self.GetLineEndPosition(end[0] - 1) + end[1] - indent + 2
+ if highlight_start_pos < end_pos and highlight_end_pos > start_pos:
+ self.StartStyling(highlight_start_pos, 0xff)
+ self.SetStyling(highlight_end_pos - highlight_start_pos, highlight_type)
+
--- a/VariablePanel.py Sun Oct 09 23:31:13 2011 +0200
+++ b/VariablePanel.py Sun Oct 09 23:31:50 2011 +0200
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
# -*- coding: utf-8 -*-
#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
@@ -28,6 +29,7 @@
from plcopen.structures import LOCATIONDATATYPES, TestIdentifier, IEC_KEYWORDS
from PLCControler import LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
+from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD
from dialogs import ArrayTypeDialog
CWD = os.path.split(os.path.realpath(__file__))[0]
@@ -94,7 +96,7 @@
self.data = data
self.old_value = None
self.colnames = colnames
- self.Errors = {}
+ self.Highlights = {}
self.Parent = parent
# XXX
# we need to store the row length and collength to
@@ -200,6 +202,7 @@
for row in range(self.GetNumberRows()):
var_class = self.GetValueByName(row, "Class")
var_type = self.GetValueByName(row, "Type")
+ row_highlights = self.Highlights.get(row, {})
for col in range(self.GetNumberCols()):
editor = None
renderer = None
@@ -254,13 +257,9 @@
grid.SetCellEditor(row, col, editor)
grid.SetCellRenderer(row, col, renderer)
- if row in self.Errors and self.Errors[row][0] == colname.lower():
- grid.SetCellBackgroundColour(row, col, wx.Colour(255, 255, 0))
- grid.SetCellTextColour(row, col, wx.RED)
- grid.MakeCellVisible(row, col)
- else:
- grid.SetCellTextColour(row, col, wx.BLACK)
- grid.SetCellBackgroundColour(row, col, wx.WHITE)
+ highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1]
+ grid.SetCellBackgroundColour(row, col, highlight_colours[0])
+ grid.SetCellTextColour(row, col, highlight_colours[1])
if wx.Platform == '__WXMSW__':
grid.SetRowMinimalHeight(row, 20)
else:
@@ -292,11 +291,23 @@
self.data = []
self.editors = []
- def AddError(self, infos):
- self.Errors[infos[0]] = infos[1:]
-
- def ClearErrors(self):
- self.Errors = {}
+ def AddHighlight(self, infos, highlight_type):
+ row_highlights = self.Highlights.setdefault(infos[0], {})
+ col_highlights = row_highlights.setdefault(infos[1], [])
+ col_highlights.append(highlight_type)
+
+ def ClearHighlights(self, highlight_type=None):
+ if highlight_type is None:
+ self.Highlights = {}
+ else:
+ for row, row_highlights in self.Highlights.iteritems():
+ row_items = row_highlights.items()
+ for col, col_highlights in row_items:
+ if highlight_type in col_highlights:
+ col_highlights.remove(highlight_type)
+ if len(col_highlights) == 0:
+ row_highlights.pop(col)
+
class VariableDropTarget(wx.TextDropTarget):
'''
@@ -524,6 +535,9 @@
self.Controler = controler
self.ElementType = element_type
+ self.RefreshHighlightsTimer = wx.Timer(self, -1)
+ self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
+
self.Filter = "All"
self.FilterChoices = []
self.FilterChoiceTransfer = GetFilterChoiceTransfer()
@@ -597,6 +611,9 @@
self.VariablesGrid.SetColMinimalWidth(col, self.ColSizes[col])
self.VariablesGrid.AutoSizeColumn(col, False)
+ def __del__(self):
+ self.RefreshHighlightsTimer.Stop()
+
def SetTagName(self, tagname):
self.TagName = tagname
@@ -920,17 +937,27 @@
self.Controler.BufferProject()
self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, INSTANCESTREE, LIBRARYTREE)
- def AddVariableError(self, infos):
+#-------------------------------------------------------------------------------
+# Highlights showing functions
+#-------------------------------------------------------------------------------
+
+ def OnRefreshHighlightsTimer(self, event):
+ self.Table.ResetView(self.VariablesGrid)
+ event.Skip()
+
+ def AddVariableHighlight(self, infos, highlight_type):
if isinstance(infos[0], TupleType):
for i in xrange(*infos[0]):
- self.Table.AddError((i,) + infos[1:])
- else:
- self.Table.AddError(infos)
+ self.Table.AddHighlight((i,) + infos[1:], highlight_type)
+ else:
+ self.Table.AddHighlight(infos, highlight_type)
+ self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
+
+ def ClearHighlights(self, highlight_type=None):
+ self.Table.ClearHighlights(highlight_type)
self.Table.ResetView(self.VariablesGrid)
- def ClearErrors(self):
- self.Table.ClearErrors()
- self.Table.ResetView(self.VariablesGrid)
+
class LocationCellControl(wx.PyControl):
--- a/Viewer.py Sun Oct 09 23:31:13 2011 +0200
+++ b/Viewer.py Sun Oct 09 23:31:50 2011 +0200
@@ -77,7 +77,6 @@
ZOOM_FACTORS = [math.sqrt(2) ** x for x in xrange(-6, 7)]
-
def GetVariableCreationFunction(variable_type):
def variableCreationFunction(viewer, id, specific_values):
return FBD_Variable(viewer, variable_type,
@@ -466,7 +465,7 @@
self.DrawingWire = False
self.current_id = 0
self.TagName = tagname
- self.Errors = []
+ self.Highlights = []
self.InstancePath = instancepath
self.StartMousePos = None
self.StartScreenPos = None
@@ -507,6 +506,9 @@
self.SetScale(len(ZOOM_FACTORS) / 2)
+ self.RefreshHighlightsTimer = wx.Timer(self, -1)
+ self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
+
self.ResetView()
# Link Viewer event to corresponding methods
@@ -526,6 +528,10 @@
self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheelWindow)
self.Bind(wx.EVT_SIZE, self.OnMoveWindow)
+ def __del__(self):
+ DebugViewer.__del__(self)
+ self.RefreshHighlightsTimer.Stop()
+
def SetCurrentCursor(self, cursor):
global CURSORS
if self.CurrentCursor != cursor:
@@ -839,7 +845,7 @@
self.Inhibit(False)
self.RefreshVisibleElements()
- self.ShowErrors()
+ self.ShowHighlights()
self.Refresh(False)
def GetPreviousSteps(self, connectors):
@@ -1848,6 +1854,7 @@
dialog = wx.TextEntryDialog(self.ParentWindow, _("Edit comment"), _("Please enter comment text"), "", wx.OK|wx.CANCEL|wx.TE_MULTILINE)
else:
dialog = wx.TextEntryDialog(self.ParentWindow, _("Edit comment"), _("Please enter comment text"), "", wx.OK|wx.CANCEL)
+ dialog.SetClientSize(wx.Size(400, 200))
if dialog.ShowModal() == wx.ID_OK:
value = dialog.GetValue()
id = self.GetNewId()
@@ -2349,6 +2356,7 @@
dialog = wx.TextEntryDialog(self.ParentWindow, _("Edit comment"), _("Please enter comment text"), comment.GetContent(), wx.OK|wx.CANCEL|wx.TE_MULTILINE)
else:
dialog = wx.TextEntryDialog(self.ParentWindow, _("Edit comment"), _("Please enter comment text"), comment.GetContent(), wx.OK|wx.CANCEL)
+ dialog.SetClientSize(wx.Size(400, 200))
if dialog.ShowModal() == wx.ID_OK:
value = dialog.GetValue()
rect = comment.GetRedrawRect(1, 1)
@@ -2747,22 +2755,30 @@
#-------------------------------------------------------------------------------
-# Errors showing functions
-#-------------------------------------------------------------------------------
-
- def ClearErrors(self):
- self.Errors = []
+# Highlights showing functions
+#-------------------------------------------------------------------------------
+
+ def OnRefreshHighlightsTimer(self, event):
self.RefreshView()
-
- def AddShownError(self, infos, start, end):
- self.Errors.append((infos, start, end))
-
- def ShowErrors(self):
- for infos, start, end in self.Errors:
- if infos[0] in ["io_variable", "block", "coil", "contact", "transition", "step", "action_block"]:
+ event.Skip()
+
+ def ClearHighlights(self, highlight_type=None):
+ if highlight_type is None:
+ self.Highlights = []
+ else:
+ self.Highlights = [(infos, start, end, highlight) for (infos, start, end, highlight) in self.Highlights if highlight != highlight_type]
+ self.RefreshView()
+
+ def AddHighlight(self, infos, start, end, highlight_type):
+ self.Highlights.append((infos, start, end, highlight_type))
+ self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
+
+ def ShowHighlights(self):
+ for infos, start, end, highlight_type in self.Highlights:
+ if infos[0] in ["comment", "io_variable", "block", "connector", "coil", "contact", "step", "transition", "jump", "action_block"]:
block = self.FindElementById(infos[1])
if block is not None:
- block.AddError(infos[2:], start, end)
+ block.AddHighlight(infos[2:], start, end, highlight_type)
#-------------------------------------------------------------------------------
# Drawing functions
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dialogs/SearchInProjectDialog.py Sun Oct 09 23:31:50 2011 +0200
@@ -0,0 +1,207 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
+#based on the plcopen standard.
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import re
+
+import wx
+
+RE_ESCAPED_CHARACTERS = ".*+()[]?:|{}^$<>=-,"
+
+def EscapeText(text):
+ text = text.replace('\\', '\\\\')
+ for c in RE_ESCAPED_CHARACTERS:
+ text = text.replace(c, '\\' + c)
+ return text
+
+#-------------------------------------------------------------------------------
+# Search In Project Dialog
+#-------------------------------------------------------------------------------
+
+def GetElementsChoices():
+ _ = lambda x: x
+ return [("datatype", _("Data Type")),
+ ("function", _("Function")),
+ ("functionBlock", _("Function Block")),
+ ("program", _("Program")),
+ ("configuration", _("Configuration"))]
+
+[ID_SEARCHINPROJECTDIALOG, ID_SEARCHINPROJECTDIALOGPATTERNLABEL,
+ ID_SEARCHINPROJECTDIALOGPATTERN, ID_SEARCHINPROJECTDIALOGCASESENSITIVE,
+ ID_SEARCHINPROJECTDIALOGREGULAREXPRESSION, ID_SEARCHINPROJECTDIALOGSCOPESTATICBOX,
+ ID_SEARCHINPROJECTDIALOGWHOLEPROJECT, ID_SEARCHINPROJECTDIALOGONLYELEMENTS,
+ ID_SEARCHINPROJECTDIALOGELEMENTSLIST,
+] = [wx.NewId() for _init_ctrls in range(9)]
+
+class SearchInProjectDialog(wx.Dialog):
+
+ if wx.VERSION < (2, 6, 0):
+ def Bind(self, event, function, id = None):
+ if id is not None:
+ event(self, id, function)
+ else:
+ event(self, function)
+
+ def _init_coll_MainSizer_Items(self, parent):
+ parent.AddSizer(self.PatternSizer, 0, border=20, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
+ parent.AddSizer(self.ScopeSizer, 0, border=20, flag=wx.GROW|wx.LEFT|wx.RIGHT)
+ parent.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
+
+ def _init_coll_MainSizer_Growables(self, parent):
+ parent.AddGrowableCol(0)
+ parent.AddGrowableRow(1)
+
+ def _init_coll_PatternSizer_Items(self, parent):
+ parent.AddWindow(self.PatternLabel, 0, border=0, flag=wx.ALIGN_BOTTOM)
+ parent.AddWindow(self.CaseSensitive, 0, border=0, flag=wx.GROW)
+ parent.AddWindow(self.Pattern, 0, border=0, flag=wx.GROW)
+ parent.AddWindow(self.RegularExpression, 0, border=0, flag=wx.GROW)
+
+ def _init_coll_PatternSizer_Growables(self, parent):
+ parent.AddGrowableCol(0)
+
+ def _init_coll_ScopeSizer_Items(self, parent):
+ parent.AddSizer(self.ScopeSelectionSizer, 1, border=5, flag=wx.GROW|wx.TOP|wx.LEFT|wx.BOTTOM)
+ parent.AddWindow(self.ElementsList, 1, border=5, flag=wx.GROW|wx.TOP|wx.RIGHT|wx.BOTTOM)
+
+ def _init_coll_ScopeSelectionSizer_Items(self, parent):
+ parent.AddWindow(self.WholeProject, 0, border=5, flag=wx.GROW|wx.BOTTOM)
+ parent.AddWindow(self.OnlyElements, 0, border=0, flag=wx.GROW)
+
+ def _init_sizers(self):
+ self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10)
+ self.PatternSizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5)
+ self.ScopeSizer = wx.StaticBoxSizer(self.ScopeStaticBox, wx.HORIZONTAL)
+ self.ScopeSelectionSizer = wx.BoxSizer(wx.VERTICAL)
+
+ self._init_coll_MainSizer_Items(self.MainSizer)
+ self._init_coll_MainSizer_Growables(self.MainSizer)
+ self._init_coll_PatternSizer_Items(self.PatternSizer)
+ self._init_coll_PatternSizer_Growables(self.PatternSizer)
+ self._init_coll_ScopeSizer_Items(self.ScopeSizer)
+ self._init_coll_ScopeSelectionSizer_Items(self.ScopeSelectionSizer)
+
+ self.SetSizer(self.MainSizer)
+
+ def _init_ctrls(self, prnt):
+ wx.Dialog.__init__(self, id=ID_SEARCHINPROJECTDIALOG,
+ name='SearchInProjectDialog', parent=prnt,
+ size=wx.Size(600, 300), style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER,
+ title=_('Search in Project'))
+
+ self.PatternLabel = wx.StaticText(id=ID_SEARCHINPROJECTDIALOGPATTERNLABEL,
+ label=_('Pattern to search:'), name='PatternLabel', parent=self,
+ pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
+
+ self.Pattern = wx.TextCtrl(id=ID_SEARCHINPROJECTDIALOGPATTERN,
+ name='Pattern', parent=self, pos=wx.Point(0, 0),
+ size=wx.Size(0, 24), style=0)
+
+ self.CaseSensitive = wx.CheckBox(id=ID_SEARCHINPROJECTDIALOGCASESENSITIVE,
+ label=_('Case sensitive'), name='CaseSensitive', parent=self,
+ pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
+
+ self.RegularExpression = wx.CheckBox(id=ID_SEARCHINPROJECTDIALOGREGULAREXPRESSION,
+ label=_('Regular expression'), name='RegularExpression', parent=self,
+ pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
+
+ self.ScopeStaticBox = wx.StaticBox(id=ID_SEARCHINPROJECTDIALOGSCOPESTATICBOX,
+ label=_('Scope'), name='ScopeStaticBox', parent=self,
+ pos=wx.Point(0, 0), size=wx.Size(0, 0), style=0)
+
+ self.WholeProject = wx.RadioButton(id=ID_SEARCHINPROJECTDIALOGWHOLEPROJECT,
+ label=_('Whole Project'), name='WholeProject', parent=self,
+ pos=wx.Point(0, 0), size=wx.Size(0, 24), style=wx.RB_GROUP)
+ self.Bind(wx.EVT_RADIOBUTTON, self.OnScopeChanged, id=ID_SEARCHINPROJECTDIALOGWHOLEPROJECT)
+ self.WholeProject.SetValue(True)
+
+ self.OnlyElements = wx.RadioButton(id=ID_SEARCHINPROJECTDIALOGONLYELEMENTS,
+ label=_('Only Elements'), name='OnlyElements', parent=self,
+ pos=wx.Point(0, 0), size=wx.Size(0, 24), style=0)
+ self.Bind(wx.EVT_RADIOBUTTON, self.OnScopeChanged, id=ID_SEARCHINPROJECTDIALOGONLYELEMENTS)
+ self.OnlyElements.SetValue(False)
+
+ self.ElementsList = wx.CheckListBox(id=ID_SEARCHINPROJECTDIALOGELEMENTSLIST,
+ name='ElementsList', parent=self, pos=wx.Point(0, 0),
+ size=wx.Size(0, 0), style=0)
+ self.ElementsList.Enable(False)
+
+ self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
+ if wx.VERSION >= (2, 5, 0):
+ ok_button = self.ButtonSizer.GetAffirmativeButton()
+ else:
+ ok_button = self.ButtonSizer.GetChildren()[0].GetSizer().GetChildren()[0].GetWindow()
+ ok_button.SetLabel(_('Search'))
+ self.Bind(wx.EVT_BUTTON, self.OnOK, id=ok_button.GetId())
+
+ self._init_sizers()
+
+ def __init__(self, parent):
+ self._init_ctrls(parent)
+
+ for name, label in GetElementsChoices():
+ self.ElementsList.Append(_(label))
+
+ def GetCriteria(self):
+ raw_pattern = self.Pattern.GetValue()
+ if not self.CaseSensitive.GetValue():
+ pattern = raw_pattern.upper()
+ if not self.RegularExpression.GetValue():
+ pattern = EscapeText(raw_pattern)
+ criteria = {
+ "raw_pattern": raw_pattern,
+ "pattern": re.compile(pattern),
+ "case_sensitive": self.CaseSensitive.GetValue(),
+ "regular_expression": self.RegularExpression.GetValue(),
+ }
+ if self.WholeProject.GetValue():
+ criteria["filter"] = "all"
+ elif self.OnlyElements.GetValue():
+ criteria["filter"] = []
+ for index, (name, label) in enumerate(GetElementsChoices()):
+ if self.ElementsList.IsChecked(index):
+ criteria["filter"].append(name)
+ return criteria
+
+ def OnScopeChanged(self, event):
+ self.ElementsList.Enable(self.OnlyElements.GetValue())
+ event.Skip()
+
+ def OnOK(self, event):
+ if self.Pattern.GetValue() == "":
+ message = wx.MessageDialog(self, _("Form isn't complete. Pattern to search must be filled!"), _("Error"), wx.OK|wx.ICON_ERROR)
+ message.ShowModal()
+ message.Destroy()
+ else:
+ wrong_pattern = False
+ if self.RegularExpression.GetValue():
+ try:
+ re.compile(self.Pattern.GetValue())
+ except:
+ wrong_pattern = True
+ if wrong_pattern:
+ message = wx.MessageDialog(self, _("Syntax error in regular expression of pattern to search!"), _("Error"), wx.OK|wx.ICON_ERROR)
+ message.ShowModal()
+ message.Destroy()
+ else:
+ self.EndModal(wx.ID_OK)
--- a/dialogs/__init__.py Sun Oct 09 23:31:13 2011 +0200
+++ b/dialogs/__init__.py Sun Oct 09 23:31:50 2011 +0200
@@ -37,3 +37,4 @@
from ForceVariableDialog import ForceVariableDialog
from ArrayTypeDialog import ArrayTypeDialog
from DurationEditorDialog import DurationEditorDialog
+from SearchInProjectDialog import SearchInProjectDialog
--- a/graphics/FBD_Objects.py Sun Oct 09 23:31:13 2011 +0200
+++ b/graphics/FBD_Objects.py Sun Oct 09 23:31:50 2011 +0200
@@ -51,7 +51,7 @@
self.Colour = wx.BLACK
self.Pen = MiterPen(wx.BLACK)
self.SetType(type, extension, inputs, connectors, executionControl)
- self.Errors = {}
+ self.Highlights = {}
# Make a clone of this FBD_Block
def Clone(self, parent, id = None, name = "", pos = None):
@@ -368,13 +368,41 @@
for output in self.Outputs:
output.RefreshWires()
- def AddError(self, infos, start, end):
+ # Adds an highlight to the block
+ def AddHighlight(self, infos, start, end ,highlight_type):
if infos[0] in ["type", "name"] and start[0] == 0 and end[0] == 0:
- self.Errors[infos[0]] = (start, end)
+ highlights = self.Highlights.setdefault(infos[0], [])
+ AddHighlight(highlights, (start, end, highlight_type))
elif infos[0] == "input" and infos[1] < len(self.Inputs):
- self.Inputs[infos[1]].AddError(infos[2:], start, end)
+ self.Inputs[infos[1]].AddHighlight(infos[2:], start, end, highlight_type)
elif infos[0] == "output" and infos[1] < len(self.Outputs):
- self.Outputs[infos[1]].AddError(infos[2:], start, end)
+ self.Outputs[infos[1]].AddHighlight(infos[2:], start, end, highlight_type)
+
+ # Removes an highlight from the block
+ def RemoveHighlight(self, infos, start, end, highlight_type):
+ if infos[0] in ["type", "name"]:
+ highlights = self.Highlights.get(infos[0], [])
+ if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0:
+ self.Highlights.pop(infos[0])
+ elif infos[0] == "input" and infos[1] < len(self.Inputs):
+ self.Inputs[infos[1]].RemoveHighlight(infos[2:], start, end, highlight_type)
+ elif infos[0] == "output" and infos[1] < len(self.Outputs):
+ self.Outputs[infos[1]].RemoveHighlight(infos[2:], start, end, highlight_type)
+
+ # Removes all the highlights of one particular type from the block
+ def ClearHighlight(self, highlight_type=None):
+ if highlight_type is None:
+ self.Highlights = {}
+ else:
+ highlight_items = self.Highlights.items()
+ for name, highlights in highlight_items:
+ highlights = ClearHighlights(highlights, highlight_type)
+ if len(highlights) == 0:
+ self.Highlights.pop(name)
+ for input in self.Inputs:
+ input.ClearHighlights(highlight_type)
+ for output in self.Outputs:
+ output.ClearHighlights(highlight_type)
# Draws block
def Draw(self, dc):
@@ -410,11 +438,10 @@
# Draw block execution order
dc.DrawText(str(self.ExecutionOrder), self.Pos.x + self.Size[0] - executionorder_size[0],
self.Pos.y + self.Size[1] + 2)
- if self.Errors.has_key("name"):
- HighlightErrorZone(dc, name_pos[0], name_pos[1], name_size[0], name_size[1])
- if self.Errors.has_key("type"):
- HighlightErrorZone(dc, type_pos[0], type_pos[1], type_size[0], type_size[1])
- dc.SetTextForeground(wx.BLACK)
+
+ if not getattr(dc, "printing", False):
+ DrawHighlightedText(dc, self.Name, self.Highlights.get("name", []), name_pos[0], name_pos[1])
+ DrawHighlightedText(dc, self.Type, self.Highlights.get("type", []), type_pos[0], type_pos[1])
#-------------------------------------------------------------------------------
@@ -438,7 +465,7 @@
self.Input = None
self.Output = None
self.SetType(type, value_type)
- self.Errors = []
+ self.Highlights = []
# Make a clone of this FBD_Variable
def Clone(self, parent, id = None, pos = None):
@@ -644,9 +671,19 @@
if self.Output:
self.Output.RefreshWires()
- def AddError(self, infos, start, end):
+ # Adds an highlight to the variable
+ def AddHighlight(self, infos, start, end, highlight_type):
if infos[0] == "expression" and start[0] == 0 and end[0] == 0:
- self.Errors.append((start[1], end[1]))
+ AddHighlight(self.Highlights, (start, end, highlight_type))
+
+ # Removes an highlight from the variable
+ def RemoveHighlight(self, infos, start, end, highlight_type):
+ if infos[0] == "expression":
+ RemoveHighlight(self.Highlights, (start, end, highlight_type))
+
+ # Removes all the highlights of one particular type from the variable
+ def ClearHighlight(self, highlight_type=None):
+ ClearHighlights(self.Highlights, highlight_type)
# Draws variable
def Draw(self, dc):
@@ -676,11 +713,9 @@
# Draw variable execution order
dc.DrawText(str(self.ExecutionOrder), self.Pos.x + self.Size[0] - executionorder_size[0],
self.Pos.y + self.Size[1] + 2)
- for start, end in self.Errors:
- offset = dc.GetTextExtent(self.Name[:start])
- size = dc.GetTextExtent(self.Name[start:end + 1])
- HighlightErrorZone(dc, text_pos[0] + offset[0], text_pos[1], size[0], size[1])
-
+ if not getattr(dc, "printing", False):
+ DrawHighlightedText(dc, self.Name, self.Highlights, text_pos[0], text_pos[1])
+
#-------------------------------------------------------------------------------
# Function Block Diagram Connector
#-------------------------------------------------------------------------------
@@ -699,6 +734,7 @@
self.SetName(name)
self.Pos = wx.Point(0, 0)
self.Size = wx.Size(0, 0)
+ self.Highlights = []
# Create an input or output connector according to connection type
if self.Type == CONNECTOR:
self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), WEST, onlyone = True)
@@ -851,6 +887,20 @@
if self.Connector:
self.Connector.RefreshWires()
+ # Adds an highlight to the connection
+ def AddHighlight(self, infos, start, end, highlight_type):
+ if infos[0] == "name" and start[0] == 0 and end[0] == 0:
+ AddHighlight(self.Highlights, (start, end, highlight_type))
+
+ # Removes an highlight from the connection
+ def RemoveHighlight(self, infos, start, end, highlight_type):
+ if infos[0] == "name":
+ RemoveHighlight(self.Highlights, (start, end, highlight_type))
+
+ # Removes all the highlights of one particular type from the connection
+ def ClearHighlight(self, highlight_type=None):
+ ClearHighlights(self.Highlights, highlight_type)
+
# Draws connection
def Draw(self, dc):
Graphic_Element.Draw(self, dc)
@@ -874,9 +924,13 @@
dc.DrawLine(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2,
self.Pos.x + self.Size[0] - arrowsize, self.Pos.y + self.Size[1])
# Draw connection name
- dc.DrawText(self.Name, self.Pos.x + (self.Size[0] - name_size[0]) / 2,
- self.Pos.y + (self.Size[1] - name_size[1]) / 2)
+ text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2,
+ self.Pos.y + (self.Size[1] - name_size[1]) / 2)
+ dc.DrawText(self.Name, text_pos[0], text_pos[1])
# Draw connector
if self.Connector:
self.Connector.Draw(dc)
+ if not getattr(dc, "printing", False):
+ DrawHighlightedText(dc, self.Name, self.Highlights, text_pos[0], text_pos[1])
+
--- a/graphics/GraphicCommons.py Sun Oct 09 23:31:13 2011 +0200
+++ b/graphics/GraphicCommons.py Sun Oct 09 23:31:50 2011 +0200
@@ -93,7 +93,14 @@
# Color for Highlighting
HIGHLIGHTCOLOR = wx.CYAN
-
+
+# Define highlight types
+ERROR_HIGHLIGHT = (wx.NamedColour("yellow"), wx.RED)
+SEARCH_RESULT_HIGHLIGHT = (wx.NamedColour("orange"), wx.WHITE)
+
+# Define highlight refresh inhibition period in second
+REFRESH_HIGHLIGHT_PERIOD = 0.1
+
HANDLE_CURSORS = {
(1, 1) : 2,
(3, 3) : 2,
@@ -531,19 +538,38 @@
event.Skip()
#-------------------------------------------------------------------------------
-# Helper for highlighting error in drawn text
+# Helpers for highlighting text
#-------------------------------------------------------------------------------
-def HighlightErrorZone(dc, x, y, width, height):
+def AddHighlight(highlights, infos):
+ RemoveHighlight(highlights, infos)
+ highlights.append(infos)
+
+def RemoveHighlight(highlights, infos):
+ if infos in highlights:
+ highlights.remove(infos)
+ return True
+ return False
+
+def ClearHighlight(highlights, highlight_type=None):
+ if highlight_type is not None:
+ return [highlight for highlight in highlights if highlight[2] != highlight_type]
+ return []
+
+def DrawHighlightedText(dc, text, highlights, x, y):
+ current_pen = dc.GetPen()
dc.SetPen(wx.TRANSPARENT_PEN)
- dc.SetLogicalFunction(wx.AND)
- dc.SetBrush(wx.Brush(wx.Colour(0,255,0)))
- dc.DrawRectangle(x, y, width, height)
- dc.SetLogicalFunction(wx.XOR)
- dc.SetBrush(wx.Brush(wx.Colour(255,0,0)))
- dc.DrawRectangle(x, y, width, height)
- dc.SetLogicalFunction(wx.COPY)
-
+ for start, end, highlight_type in highlights:
+ dc.SetBrush(wx.Brush(highlight_type[0]))
+ offset_width, offset_height = dc.GetTextExtent(text[:start[1]])
+ part = text[start[1]:end[1] + 1]
+ part_width, part_height = dc.GetTextExtent(part)
+ dc.DrawRectangle(x + offset_width, y, part_width, part_height)
+ dc.SetTextForeground(highlight_type[1])
+ dc.DrawText(part, x + offset_width, y)
+ dc.SetPen(current_pen)
+ dc.SetTextForeground(wx.BLACK)
+
#-------------------------------------------------------------------------------
# Graphic element base class
#-------------------------------------------------------------------------------
@@ -934,7 +960,16 @@
return movex, movey
return 0, 0
- def AddError(self, infos, start, end):
+ # Override this method for defining the method to call for adding an highlight to this element
+ def AddHighlight(self, infos, start, end, highlight_type):
+ pass
+
+ # Override this method for defining the method to call for removing an highlight from this element
+ def RemoveHighlight(self, infos, start, end, highlight_type):
+ pass
+
+ # Override this method for defining the method to call for removing all the highlights of one particular type from this element
+ def ClearHighlight(self, highlight_type=None):
pass
# Override this method for defining the method to call for refreshing the model of this element
@@ -1300,7 +1335,7 @@
self.Value = None
self.Forced = False
self.Selected = False
- self.Errors = {}
+ self.Highlights = []
self.RefreshNameSize()
def Flush(self):
@@ -1396,7 +1431,7 @@
self.Valid = True
for wire, handle in self.Wires:
self.Valid &= wire.GetValid()
-
+
def ReceivingCurrent(self):
current = False
for wire, handle in self.Wires:
@@ -1607,21 +1642,50 @@
dc.SetLogicalFunction(wx.COPY)
dc.SetUserScale(scalex, scaley)
- def AddError(self, infos, start, end):
- if len(infos) == 0:
+ # Adds an highlight to the connector
+ def AddHighlight(self, infos, start, end, highlight_type):
+ if highlight_type == ERROR_HIGHLIGHT:
for wire, handle in self.Wires:
wire.SetValid(False)
+ AddHighlight(self.Highlights, (start, end, highlight_type))
+
+ # Removes an highlight from the connector
+ def RemoveHighlight(self, infos, start, end, highlight_type):
+ error = False
+ highlights = []
+ for highlight in self.Highlights:
+ if highlight != (start, end, highlight_type):
+ highlights.append(highlight)
+ error |= highlight == ERROR_HIGHLIGHT
+ self.Highlights = highlights
+ if not error:
+ for wire, handle in self.Wires:
+ wire.SetValid(wire.IsConnectedCompatible())
+
+ # Removes all the highlights of one particular type from the connector
+ def ClearHighlight(self, highlight_type=None):
+ error = False
+ if highlight_type is None:
+ self.Highlights = []
else:
- self.Errors[infos[0]] = (start, end)
+ highlights = []
+ for highlight in self.Highlights:
+ if highlight[2] != highlight_type:
+ highlights.append(highlight)
+ error |= highlight == ERROR_HIGHLIGHT
+ self.Highlights = highlights
+ if not error:
+ for wire, handle in self.Wires:
+ wire.SetValid(wire.IsConnectedCompatible())
# Draws the connector
def Draw(self, dc):
if self.Selected:
dc.SetPen(MiterPen(wx.BLUE, 3))
dc.SetBrush(wx.WHITE_BRUSH)
- elif len(self.Errors) > 0:
- dc.SetPen(MiterPen(wx.RED))
- dc.SetBrush(wx.Brush(wx.Colour(255, 255, 0)))
+ #elif len(self.Highlights) > 0:
+ # dc.SetPen(MiterPen(self.Highlights[-1][1]))
+ # dc.SetBrush(wx.Brush(self.Highlights[-1][0]))
else:
if not self.Valid:
dc.SetPen(MiterPen(wx.RED))
@@ -1672,9 +1736,6 @@
xend = xstart + CONNECTOR_SIZE * self.Direction[0]
yend = ystart + CONNECTOR_SIZE * self.Direction[1]
dc.DrawLine(xstart + self.Direction[0], ystart + self.Direction[1], xend, yend)
- if len(self.Errors) > 0:
- dc.SetPen(self.Pen)
- dc.SetBrush(wx.WHITE_BRUSH)
if self.Direction[0] != 0:
ytext = parent_pos[1] + self.Pos.y - name_size[1] / 2
if self.Direction[0] < 0:
@@ -1689,7 +1750,8 @@
ytext = parent_pos[1] + self.Pos.y - (name_size[1] + 5)
# Draw the text
dc.DrawText(self.Name, xtext, ytext)
-
+ if not getattr(dc, "printing", False):
+ DrawHighlightedText(dc, self.Name, self.Highlights, xtext, ytext)
#-------------------------------------------------------------------------------
# Common Wire Element
@@ -2775,7 +2837,7 @@
if self.EndConnected is not None:
self.EndConnected.DrawHighlightment(dc)
self.EndConnected.Draw(dc)
-
+
# Draws the wire lines and points
def Draw(self, dc):
Graphic_Element.Draw(self, dc)
@@ -2848,6 +2910,26 @@
# Graphic comment element
#-------------------------------------------------------------------------------
+def FilterHighlightsByRow(highlights, row, length):
+ _highlights = []
+ for start, end, highlight_type in highlights:
+ if start[0] <= row and end[0] >= row:
+ if start[0] < row:
+ start = (row, 0)
+ if end[0] > row:
+ end = (row, length)
+ _highlights.append((start, end, highlight_type))
+ return _highlights
+
+def FilterHighlightsByColumn(highlights, start_col, end_col):
+ _highlights = []
+ for start, end, highlight_type in highlights:
+ if end[1] > start_col and start[1] < end_col:
+ start = (start[0], max(start[1], start_col) - start_col)
+ end = (end[0], min(end[1], end_col) - start_col)
+ _highlights.append((start, end, highlight_type))
+ return _highlights
+
"""
Class that implements a comment
"""
@@ -2861,6 +2943,7 @@
self.Content = content
self.Pos = wx.Point(0, 0)
self.Size = wx.Size(0, 0)
+ self.Highlights = []
# Make a clone of this comment
def Clone(self, parent, id = None, pos = None):
@@ -2958,6 +3041,19 @@
# Edit the comment content
self.Parent.EditCommentContent(self)
+ # Adds an highlight to the comment
+ def AddHighlight(self, infos, start, end, highlight_type):
+ if infos[0] == "content":
+ AddHighlight(self.Highlights, (start, end, highlight_type))
+
+ # Removes an highlight from the comment
+ def RemoveHighlight(self, infos, start, end, highlight_type):
+ RemoveHighlight(self.Highlights, (start, end, highlight_type))
+
+ # Removes all the highlights of one particular type from the comment
+ def ClearHighlight(self, highlight_type=None):
+ self.Highlights = ClearHighlights(self.Highlights, highlight_type)
+
# Draws the highlightment of this element if it is highlighted
def DrawHighlightment(self, dc):
scalex, scaley = dc.GetUserScale()
@@ -2999,32 +3095,46 @@
dc.DrawLines(lines)
# Draws the comment content
y = self.Pos.y + 10
- for line in self.Content.splitlines():
+ for idx, line in enumerate(self.Content.splitlines()):
first = True
linetext = ""
words = line.split(" ")
+ if not getattr(dc, "printing", False):
+ highlights = FilterHighlightsByRow(self.Highlights, idx, len(line))
+ highlights_offset = 0
for i, word in enumerate(words):
if first:
- test = word
+ text = word
else:
- test = linetext + " " + word
- wordwidth, wordheight = dc.GetTextExtent(test)
+ text = linetext + " " + word
+ wordwidth, wordheight = dc.GetTextExtent(text)
if y + wordheight > self.Pos.y + self.Size[1] - 10:
break
if wordwidth < self.Size[0] - 20:
if i < len(words) - 1:
- linetext = test
+ linetext = text
first = False
else:
- dc.DrawText(test, self.Pos.x + 10, y)
+ dc.DrawText(text, self.Pos.x + 10, y)
+ if not getattr(dc, "printing", False):
+ DrawHighlightedText(dc, text, FilterHighlightsByColumn(highlights, highlights_offset, highlights_offset + len(text)), self.Pos.x + 10, y)
+ highlights_offset += len(text) + 1
y += wordheight + 5
else:
- dc.DrawText(linetext, self.Pos.x + 10, y)
- if i == len(words) - 1:
- y += wordheight + 5
- if y + wordheight > self.Pos.y + self.Size[1] - 10:
- break
+ if not first:
+ dc.DrawText(linetext, self.Pos.x + 10, y)
+ if not getattr(dc, "printing", False):
+ DrawHighlightedText(dc, linetext, FilterHighlightsByColumn(highlights, highlights_offset, highlights_offset + len(linetext)), self.Pos.x + 10, y)
+ highlights_offset += len(linetext) + 1
+ if first or i == len(words) - 1:
+ if not first:
+ y += wordheight + 5
+ if y + wordheight > self.Pos.y + self.Size[1] - 10:
+ break
dc.DrawText(word, self.Pos.x + 10, y)
+ if not getattr(dc, "printing", False):
+ DrawHighlightedText(dc, word, FilterHighlightsByColumn(highlights, highlights_offset, highlights_offset + len(word)), self.Pos.x + 10, y)
+ highlights_offset += len(word) + 1
else:
linetext = word
y += wordheight + 5
--- a/graphics/LD_Objects.py Sun Oct 09 23:31:13 2011 +0200
+++ b/graphics/LD_Objects.py Sun Oct 09 23:31:50 2011 +0200
@@ -354,7 +354,7 @@
self.Name = name
self.Id = id
self.Size = wx.Size(LD_ELEMENT_SIZE[0], LD_ELEMENT_SIZE[1])
- self.Errors = {}
+ self.Highlights = {}
# Create an input and output connector
self.Input = Connector(self, "", "BOOL", wx.Point(0, self.Size[1] / 2 + 1), WEST)
self.Output = Connector(self, "", "BOOL", wx.Point(self.Size[0], self.Size[1] / 2 + 1), EAST)
@@ -595,8 +595,31 @@
dc.SetLogicalFunction(wx.COPY)
dc.SetUserScale(scalex, scaley)
- def AddError(self, infos, start, end):
- self.Errors[infos[0]] = (start[1], end[1])
+ # Adds an highlight to the connection
+ def AddHighlight(self, infos, start, end, highlight_type):
+ highlights = self.Highlights.setdefault(infos[0], [])
+ if infos[0] == "reference":
+ if start[0] == 0 and end[0] == 0:
+ AddHighlight(highlights, (start, end, highlight_type))
+ else:
+ AddHighlight(highlights, ((0, 0), (0, 1), highlight_type))
+
+ # Removes an highlight from the connection
+ def RemoveHighlight(self, infos, start, end, highlight_type):
+ highlights = self.Highlights.get(infos[0], [])
+ if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0:
+ self.Highlights.pop(infos[0])
+
+ # Removes all the highlights of one particular type from the connection
+ def ClearHighlight(self, highlight_type=None):
+ if highlight_type is None:
+ self.Highlights = {}
+ else:
+ highlight_items = self.Highlights.items()
+ for name, highlights in highlight_items:
+ highlights = ClearHighlights(highlight, highlight_type)
+ if len(highlights) == 0:
+ self.Highlights.pop(name)
# Draws contact
def Draw(self, dc):
@@ -651,11 +674,13 @@
# Draw input and output connectors
self.Input.Draw(dc)
self.Output.Draw(dc)
- if self.Errors.has_key("reference"):
- HighlightErrorZone(dc, name_pos[0], name_pos[1], name_size[0], name_size[1])
- if typetext != "" and (self.Errors.has_key("negated") or self.Errors.has_key("rising") or self.Errors.has_key("falling")):
- HighlightErrorZone(dc, type_pos[0], type_pos[1], type_size[0], type_size[1])
-
+
+ if not getattr(dc, "printing", False):
+ for name, highlights in self.Highlights.iteritems():
+ if name == "reference":
+ DrawHighlightedText(dc, self.Name, highlights, name_pos[0], name_pos[1])
+ elif typetext != "":
+ DrawHighlightedText(dc, typetext, highlights, type_pos[0], type_pos[1])
#-------------------------------------------------------------------------------
# Ladder Diagram Coil
@@ -674,7 +699,7 @@
self.Name = name
self.Id = id
self.Size = wx.Size(LD_ELEMENT_SIZE[0], LD_ELEMENT_SIZE[1])
- self.Errors = {}
+ self.Highlights = {}
# Create an input and output connector
self.Input = Connector(self, "", "BOOL", wx.Point(0, self.Size[1] / 2 + 1), WEST)
self.Output = Connector(self, "", "BOOL", wx.Point(self.Size[0], self.Size[1] / 2 + 1), EAST)
@@ -891,8 +916,31 @@
dc.SetLogicalFunction(wx.COPY)
dc.SetUserScale(scalex, scaley)
- def AddError(self, infos, start, end):
- self.Errors[infos[0]] = (start[1], end[1])
+ # Adds an highlight to the connection
+ def AddHighlight(self, infos, start, end, highlight_type):
+ highlights = self.Highlights.setdefault(infos[0], [])
+ if infos[0] == "reference":
+ if start[0] == 0 and end[0] == 0:
+ AddHighlight(highlights, (start, end, highlight_type))
+ else:
+ AddHighlight(highlights, ((0, 0), (0, 1), highlight_type))
+
+ # Removes an highlight from the connection
+ def RemoveHighlight(self, infos, start, end, highlight_type):
+ highlights = self.Highlights.get(infos[0], [])
+ if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0:
+ self.Highlights.pop(infos[0])
+
+ # Removes all the highlights of one particular type from the connection
+ def ClearHighlight(self, highlight_type=None):
+ if highlight_type is None:
+ self.Highlights = {}
+ else:
+ highlight_items = self.Highlights.items()
+ for name, highlights in highlight_items:
+ highlights = ClearHighlights(highlight, highlight_type)
+ if len(highlights) == 0:
+ self.Highlights.pop(name)
# Draws coil
def Draw(self, dc):
@@ -954,9 +1002,12 @@
# Draw input and output connectors
self.Input.Draw(dc)
self.Output.Draw(dc)
- if self.Errors.has_key("reference"):
- HighlightErrorZone(dc, name_pos[0], name_pos[1], name_size[0], name_size[1])
- if typetext != "" and (self.Errors.has_key("negated") or self.Errors.has_key("rising") or self.Errors.has_key("falling")):
- HighlightErrorZone(dc, type_pos[0], type_pos[1], type_size[0], type_size[1])
+
+ if not getattr(dc, "printing", False):
+ for name, highlights in self.Highlights.iteritems():
+ if name == "reference":
+ DrawHighlightedText(dc, self.Name, highlights, name_pos[0], name_pos[1])
+ elif typetext != "":
+ DrawHighlightedText(dc, typetext, highlights, type_pos[0], type_pos[1])
--- a/graphics/SFC_Objects.py Sun Oct 09 23:31:13 2011 +0200
+++ b/graphics/SFC_Objects.py Sun Oct 09 23:31:50 2011 +0200
@@ -50,7 +50,7 @@
self.SetName(name)
self.Initial = initial
self.Id = id
- self.Error = None
+ self.Highlights = []
self.Size = wx.Size(SFC_STEP_DEFAULT_SIZE[0], SFC_STEP_DEFAULT_SIZE[1])
# Create an input and output connector
if not self.Initial:
@@ -513,9 +513,19 @@
elif self.Output:
self.Output.RefreshWires()
- def AddError(self, infos, start, end):
+ # Adds an highlight to the connection
+ def AddHighlight(self, infos, start, end, highlight_type):
if infos[0] == "name" and start[0] == 0 and end[0] == 0:
- self.Error = (start[1], end[1])
+ AddHighlight(self.Highlights, (start, end, highlight_type))
+
+ # Removes an highlight from the connection
+ def RemoveHighlight(self, infos, start, end, highlight_type):
+ if infos[0] == "name":
+ RemoveHighlight(self.Highlights, (start, end, highlight_type))
+
+ # Removes all the highlights of one particular type from the connection
+ def ClearHighlight(self, highlight_type=None):
+ ClearHighlights(self.Highlights, highlight_type)
# Draws step
def Draw(self, dc):
@@ -551,8 +561,9 @@
self.Output.Draw(dc)
if self.Action:
self.Action.Draw(dc)
- if self.Error is not None:
- HighlightErrorZone(dc, name_pos[0], name_pos[1], name_size[0], name_size[1])
+
+ if not getattr(dc, "printing", False):
+ DrawHighlightedText(dc, self.Name, self.Highlights, name_pos[0], name_pos[1])
#-------------------------------------------------------------------------------
@@ -578,7 +589,7 @@
self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True)
self.SetType(type, condition)
self.SetPriority(priority)
- self.Errors = {}
+ self.Highlights = {}
self.PreviousValue = None
self.PreviousSpreading = False
@@ -923,15 +934,29 @@
else:
self.Output.RefreshWires()
- def AddError(self, infos, start, end):
- if infos[0] == "priority" and start[0] == 0 and start[1] == 0:
- self.Errors[infos[0]] = (start[1], end[1])
- elif infos[0] == "inline":
- if not self.Errors.has_key(infos[0]):
- self.Errors[infos[0]] = []
- self.Errors[infos[0]].append((start[1], end[1]))
- else:
- pass
+ # Adds an highlight to the block
+ def AddHighlight(self, infos, start, end ,highlight_type):
+ if infos[0] in ["reference", "inline", "priority"] and start[0] == 0 and end[0] == 0:
+ highlights = self.Highlights.setdefault(infos[0], [])
+ AddHighlight(highlights, (start, end, highlight_type))
+
+ # Removes an highlight from the block
+ def RemoveHighlight(self, infos, start, end, highlight_type):
+ if infos[0] in ["reference", "inline", "priority"]:
+ highlights = self.Highlights.get(infos[0], [])
+ if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0:
+ self.Highlights.pop(infos[0])
+
+ # Removes all the highlights of one particular type from the block
+ def ClearHighlight(self, highlight_type=None):
+ if highlight_type is None:
+ self.Highlights = {}
+ else:
+ highlight_items = self.Highlights.items()
+ for name, highlights in highlight_items:
+ highlights = ClearHighlights(highlight, highlight_type)
+ if len(highlights) == 0:
+ self.Highlights.pop(name)
# Draws transition
def Draw(self, dc):
@@ -986,14 +1011,13 @@
self.Output.Draw(dc)
if self.Type == "connection":
self.Condition.Draw(dc)
- if self.Errors.has_key("priority"):
- HighlightErrorZone(dc, priority_pos[0], priority_pos[1], priority_size[0], priority_size[1])
- if self.Errors.has_key("inline"):
- for start, end in self.Errors["inline"]:
- offset = dc.GetTextExtent(self.Condition[:start])
- size = dc.GetTextExtent(self.Condition[start:end + 1])
- HighlightErrorZone(dc, condition_pos[0] + offset[0], condition_pos[1], size[0], size[1])
-
+
+ if not getattr(dc, "printing", False):
+ for name, highlights in self.Highlights.iteritems():
+ if name == "priority":
+ DrawHighlightedText(dc, str(self.Priority), highlights, priority_pos[0], priority_pos[1])
+ else:
+ DrawHighlightedText(dc, condition, highlights, condition_pos[0], condition_pos[1])
#-------------------------------------------------------------------------------
# Sequencial Function Chart Divergence and Convergence
@@ -1474,7 +1498,7 @@
self.SetTarget(target)
self.Id = id
self.Size = wx.Size(SFC_JUMP_SIZE[0], SFC_JUMP_SIZE[1])
- self.Errors = {}
+ self.Highlights = []
# Create an input and output connector
self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone = True)
self.Value = None
@@ -1659,9 +1683,19 @@
if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
self.RefreshInputModel()
- def AddError(self, infos, start, end):
+ # Adds an highlight to the variable
+ def AddHighlight(self, infos, start, end, highlight_type):
if infos[0] == "target" and start[0] == 0 and end[0] == 0:
- self.Errors[infos[0]] = (start[1], end[1])
+ AddHighlight(self.Highlights, (start, end, highlight_type))
+
+ # Removes an highlight from the variable
+ def RemoveHighlight(self, infos, start, end, highlight_type):
+ if infos[0] == "target":
+ RemoveHighlight(self.Highlights, (start, end, highlight_type))
+
+ # Removes all the highlights of one particular type from the variable
+ def ClearHighlight(self, highlight_type=None):
+ ClearHighlights(self.Highlights, highlight_type)
# Draws the highlightment of this element if it is highlighted
def DrawHighlightment(self, dc):
@@ -1708,8 +1742,9 @@
# Draw input connector
if self.Input:
self.Input.Draw(dc)
- if self.Errors.has_key("target"):
- HighlightErrorZone(dc, target_pos[0], target_pos[1], target_size[0], target_size[1])
+
+ if not getattr(dc, "printing", False):
+ DrawHighlightedText(dc, self.Target, self.Highlights, target_pos[0], target_pos[1])
#-------------------------------------------------------------------------------
@@ -1728,7 +1763,7 @@
self.Id = id
self.Size = wx.Size(SFC_ACTION_MIN_SIZE[0], SFC_ACTION_MIN_SIZE[1])
self.MinSize = wx.Size(SFC_ACTION_MIN_SIZE[0], SFC_ACTION_MIN_SIZE[1])
- self.Errors = {}
+ self.Highlights = {}
# Create an input and output connector
self.Input = Connector(self, "", None, wx.Point(0, SFC_ACTION_MIN_SIZE[1] / 2), WEST, onlyone = True)
self.SetActions(actions)
@@ -1907,20 +1942,41 @@
return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
- # Refreshes the action block model
+ # Refreshes the action block model
def RefreshModel(self, move=True):
self.Parent.RefreshActionBlockModel(self)
- def AddError(self, infos, start, end):
+ # Adds an highlight to the variable
+ def AddHighlight(self, infos, start, end, highlight_type):
if infos[0] == "action" and infos[1] < len(self.Actions):
- if not self.Errors.has_key(infos[1]):
- self.Errors[infos[1]] = {}
- if infos[2] == "inline":
- if not self.Errors[infos[1]].has_key(infos[2]):
- self.Errors[infos[1]][infos[2]] = []
- self.Errors[infos[1]][infos[2]].append((start[1], end[1]))
- else:
- self.Errors[infos[1]][infos[2]] = (start[1], end[1])
+ action_highlights = self.Highlights.setdefault(infos[1], {})
+ attribute_highlights = action_highlights.setdefault(infos[2], [])
+ AddHighlight(attribute_highlights, (start, end, highlight_type))
+
+ # Removes an highlight from the block
+ def RemoveHighlight(self, infos, start, end, highlight_type):
+ if infos[0] == "action" and infos[1] < len(self.Actions):
+ action_highlights = self.Highlights.get(infos[1], {})
+ attribute_highlights = action_highlights.setdefault(infos[2], [])
+ if RemoveHighlight(attribute_highlights, (start, end, highlight_type)) and len(attribute_highlights) == 0:
+ action_highlights.pop(infos[2])
+ if len(action_highlights) == 0:
+ self.Highlights.pop(infos[1])
+
+ # Removes all the highlights of one particular type from the block
+ def ClearHighlight(self, highlight_type=None):
+ if highlight_type is None:
+ self.Highlights = {}
+ else:
+ highlight_items = self.Highlights.items()
+ for number, action_highlights in highlight_items:
+ action_highlight_items = action_highlights.items()
+ for name, attribute_highlights in action_highlights:
+ attribute_highlights = ClearHighlights(attribute_highlights, highlight_type)
+ if len(attribute_highlights) == 0:
+ action_highlights.pop(name)
+ if len(action_highlights) == 0:
+ self.Highlights.pop(number)
# Draws divergence
def Draw(self, dc):
@@ -1963,20 +2019,19 @@
indicator_pos = (self.Pos.x + colsize[0] + colsize[1] + (colsize[2] - indicator_size[0]) / 2,
self.Pos.y + i * line_size + (line_size - indicator_size[1]) / 2)
dc.DrawText(action["indicator"], indicator_pos[0], indicator_pos[1])
- if i in self.Errors:
- if self.Errors[i].has_key("duration") and action.has_key("duration"):
- HighlightErrorZone(dc, duration_pos[0], duration_pos[1], duration_size[0], duration_size[1])
- if self.Errors[i].has_key("qualifier"):
- HighlightErrorZone(dc, qualifier_pos[0], qualifier_pos[1], qualifier_size[0], qualifier_size[1])
- if self.Errors[i].has_key("reference"):
- HighlightErrorZone(dc, content_pos[0], content_pos[1], content_size[0], content_size[1])
- elif self.Errors[i].has_key("inline"):
- for start, end in self.Errors[i]["inline"]:
- offset = dc.GetTextExtent(action["value"][:start])
- size = dc.GetTextExtent(action["value"][start:end + 1])
- HighlightErrorZone(dc, content_pos[0] + offset[0], content_pos[1], size[0], size[1])
- if self.Errors[i].has_key("indicator"):
- HighlightErrorZone(dc, indicator_pos[0], indicator_pos[1], indicator_size[0], indicator_size[1])
+
+ if not getattr(dc, "printing", False):
+ action_highlights = self.Highlights.get(i, {})
+ for name, attribute_highlights in action_highlights.iteritems():
+ if name == "qualifier":
+ DrawHighlightedText(dc, action["qualifier"], attribute_highlights, qualifier_pos[0], qualifier_pos[1])
+ elif name == "duration":
+ DrawHighlightedText(dc, action["duration"], attribute_highlights, duration_pos[0], duration_pos[1])
+ elif name in ["reference", "inline"]:
+ DrawHighlightedText(dc, action["value"], attribute_highlights, content_pos[0], content_pos[1])
+ elif name == "indicator":
+ DrawHighlightedText(dc, action["indicator"], attribute_highlights, indicator_pos[0], indicator_pos[1])
+
# Draw input connector
self.Input.Draw(dc)
--- a/plcopen/plcopen.py Sun Oct 09 23:31:13 2011 +0200
+++ b/plcopen/plcopen.py Sun Oct 09 23:31:50 2011 +0200
@@ -34,6 +34,12 @@
"Output" : "outputVars", "InOut" : "inOutVars", "External" : "externalVars",
"Global" : "globalVars", "Access" : "accessVars"}
+searchResultVarTypes = {
+ "inputVars": "var_input",
+ "outputVars": "var_output",
+ "inOutVars": "var_inout"
+}
+
"""
Define in which order var types must be displayed
"""
@@ -96,7 +102,25 @@
if self.y_min is not None and self.y_max is not None:
height = self.y_max - self.y_min
return self.x_min, self.y_min, width, height
-
+
+def TextLenInRowColumn(text):
+ if text == "":
+ return (0, 0)
+ lines = text.split("\n")
+ return len(lines) - 1, len(lines[-1])
+
+def TestTextElement(text, criteria):
+ lines = text.splitlines()
+ if not criteria["case_sensitive"]:
+ text = text.upper()
+ test_result = []
+ result = criteria["pattern"].search(text)
+ while result is not None:
+ start = TextLenInRowColumn(text[:result.start()])
+ end = TextLenInRowColumn(text[:result.end() - 1])
+ test_result.append((start, end, "\n".join(lines[start[0]:end[0] + 1])))
+ result = criteria["pattern"].search(text, result.end())
+ return test_result
PLCOpenClasses = GenerateClassesFromXSD(os.path.join(os.path.split(__file__)[0], "tc6_xml_v201.xsd"))
@@ -127,6 +151,10 @@
result = address_model.search(self.text, startpos)
setattr(cls, "updateElementAddress", updateElementAddress)
+ def Search(self, criteria, parent_infos):
+ return [(tuple(parent_infos),) + result for result in TestTextElement(self.gettext(), criteria)]
+ setattr(cls, "Search", Search)
+
cls = PLCOpenClasses.get("project", None)
if cls:
cls.singleLineAttributes = False
@@ -677,6 +705,13 @@
return True
setattr(cls, "IsLocatableType", IsLocatableType)
+ def Search(self, criteria, parent_infos=[]):
+ result = self.types.Search(criteria, parent_infos)
+ for configuration in self.instances.configurations.getconfiguration():
+ result.extend(configuration.Search(criteria, parent_infos))
+ return result
+ setattr(cls, "Search", Search)
+
cls = PLCOpenClasses.get("project_fileHeader", None)
if cls:
cls.singleLineAttributes = False
@@ -746,61 +781,99 @@
return 0, 0
setattr(cls, "getscaling", getscaling)
+def _Search(attributes, criteria, parent_infos):
+ search_result = []
+ for attr, value in attributes:
+ if value is not None:
+ search_result.extend([(tuple(parent_infos + [attr]),) + result for result in TestTextElement(value, criteria)])
+ return search_result
+
+def _updateConfigurationResourceElementName(self, old_name, new_name):
+ for varlist in self.getglobalVars():
+ for var in varlist.getvariable():
+ var_address = var.getaddress()
+ if var_address is not None:
+ if var_address == old_name:
+ var.setaddress(new_name)
+ if var.getname() == old_name:
+ var.setname(new_name)
+
+def _updateConfigurationResourceElementAddress(self, address_model, new_leading):
+ for varlist in self.getglobalVars():
+ for var in varlist.getvariable():
+ var_address = var.getaddress()
+ if var_address is not None:
+ var.setaddress(update_address(var_address, address_model, new_leading))
+
+def _removeConfigurationResourceVariableByAddress(self, address):
+ for varlist in self.getglobalVars():
+ variables = varlist.getvariable()
+ for i in xrange(len(variables)-1, -1, -1):
+ if variables[i].getaddress() == address:
+ variables.pop(i)
+
+def _removeConfigurationResourceVariableByFilter(self, address_model):
+ for varlist in self.getglobalVars():
+ variables = varlist.getvariable()
+ for i in xrange(len(variables)-1, -1, -1):
+ var_address = variables[i].getaddress()
+ if var_address is not None:
+ result = address_model.match(var_address)
+ if result is not None:
+ variables.pop(i)
+
+def _SearchInConfigurationResource(self, criteria, parent_infos=[]):
+ search_result = []
+ for result in TestTextElement(self.getname(), criteria):
+ search_result.append((tuple(parent_infos + ["name"]),) + result)
+ var_number = 0
+ for varlist in self.getglobalVars():
+ variable_type = searchResultVarTypes.get("globalVars", "var_local")
+ variables = varlist.getvariable()
+ for modifier, has_modifier in [("constant", varlist.getconstant()),
+ ("retain", varlist.getretain()),
+ ("non_retain", varlist.getnonretain())]:
+ if has_modifier:
+ for result in TestTextElement(modifier, criteria):
+ search_result.append((tuple(parent_infos + [variable_type, (var_number, var_number + len(variables)), modifier]),) + result)
+ break
+ for variable in variables:
+ search_result.extend(variable.Search(criteria, parent_infos + [variable_type, var_number]))
+ var_number += 1
+ return search_result
+
cls = PLCOpenClasses.get("configurations_configuration", None)
if cls:
def updateElementName(self, old_name, new_name):
- for varlist in self.getglobalVars():
- for var in varlist.getvariable():
- var_address = var.getaddress()
- if var_address is not None:
- if var_address == old_name:
- var.setaddress(new_name)
- if var.getname() == old_name:
- var.setname(new_name)
+ _updateConfigurationResourceElementName(self, old_name, new_name)
for resource in self.getresource():
resource.updateElementName(old_name, new_name)
setattr(cls, "updateElementName", updateElementName)
def updateElementAddress(self, address_model, new_leading):
- for varlist in self.getglobalVars():
- for var in varlist.getvariable():
- var_address = var.getaddress()
- if var_address is not None:
- var.setaddress(update_address(var_address, address_model, new_leading))
+ _updateConfigurationResourceElementAddress(self, address_model, new_leading)
for resource in self.getresource():
resource.updateElementAddress(address_model, new_leading)
setattr(cls, "updateElementAddress", updateElementAddress)
- def removeVariableByAddress(self, address):
- for varlist in self.getglobalVars():
- variables = varlist.getvariable()
- for i in xrange(len(variables)-1, -1, -1):
- if variables[i].getaddress() == address:
- variables.pop(i)
- setattr(cls, "removeVariableByAddress", removeVariableByAddress)
-
- def removeVariableByFilter(self, address_model):
- for varlist in self.getglobalVars():
- variables = varlist.getvariable()
- for i in xrange(len(variables)-1, -1, -1):
- var_address = variables[i].getaddress()
- if var_address is not None:
- result = address_model.match(var_address)
- if result is not None:
- variables.pop(i)
- setattr(cls, "removeVariableByFilter", removeVariableByFilter)
-
+ setattr(cls, "removeVariableByAddress", _removeConfigurationResourceVariableByAddress)
+ setattr(cls, "removeVariableByFilter", _removeConfigurationResourceVariableByFilter)
+
+ def Search(self, criteria, parent_infos=[]):
+ search_result = []
+ parent_infos = parent_infos + ["C::%s" % self.getname()]
+ filter = criteria["filter"]
+ if filter == "all" or "configuration" in filter:
+ search_result = _SearchInConfigurationResource(self, criteria, parent_infos)
+ for resource in self.getresource():
+ search_result.extend(resource.Search(criteria, parent_infos))
+ return search_result
+ setattr(cls, "Search", Search)
+
cls = PLCOpenClasses.get("configuration_resource", None)
if cls:
def updateElementName(self, old_name, new_name):
- for varlist in self.getglobalVars():
- for var in varlist.getvariable():
- var_address = var.getaddress()
- if var_address is not None:
- if var_address == old_name:
- var.setaddress(new_name)
- if var.getname() == old_name:
- var.setname(new_name)
+ _updateConfigurationResourceElementName(self, old_name, new_name)
for instance in self.getpouInstance():
instance.updateElementName(old_name, new_name)
for task in self.gettask():
@@ -808,33 +881,35 @@
setattr(cls, "updateElementName", updateElementName)
def updateElementAddress(self, address_model, new_leading):
- for varlist in self.getglobalVars():
- for var in varlist.getvariable():
- var_address = var.getaddress()
- if var_address is not None:
- var.setaddress(update_address(var_address, address_model, new_leading))
+ _updateConfigurationResourceElementAddress(self, address_model, new_leading)
for task in self.gettask():
task.updateElementAddress(address_model, new_leading)
setattr(cls, "updateElementAddress", updateElementAddress)
- def removeVariableByAddress(self, address):
- for varlist in self.getglobalVars():
- variables = varlist.getvariable()
- for i in xrange(len(variables)-1, -1, -1):
- if variables[i].getaddress() == address:
- variables.pop(i)
- setattr(cls, "removeVariableByAddress", removeVariableByAddress)
-
- def removeVariableByFilter(self, address_model):
- for varlist in self.getglobalVars():
- variables = varlist.getvariable()
- for i in xrange(len(variables)-1, -1, -1):
- var_address = variables[i].getaddress()
- if var_address is not None:
- result = address_model.match(var_address)
- if result is not None:
- variables.pop(i)
- setattr(cls, "removeVariableByFilter", removeVariableByFilter)
+ setattr(cls, "removeVariableByAddress", _removeConfigurationResourceVariableByAddress)
+ setattr(cls, "removeVariableByFilter", _removeConfigurationResourceVariableByFilter)
+
+ def Search(self, criteria, parent_infos=[]):
+ parent_infos = parent_infos[:-1] + ["R::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())]
+ search_result = _SearchInConfigurationResource(self, criteria, parent_infos)
+ task_number = 0
+ instance_number = 0
+ for task in self.gettask():
+ results = TestTextElement(task.getname(), criteria)
+ for result in results:
+ search_result.append((tuple(parent_infos + ["task", task_number, "name"]),) + result)
+ search_result.extend(task.Search(criteria, parent_infos + ["task", task_number]))
+ task_number += 1
+ for instance in task.getpouInstance():
+ search_result.extend(task.Search(criteria, parent_infos + ["instance", instance_number]))
+ for result in results:
+ search_result.append((tuple(parent_infos + ["instance", instance_number, "task"]),) + result)
+ instance_number += 1
+ for instance in self.getpouInstance():
+ search_result.extend(instance.Search(criteria, parent_infos + ["instance", instance_number]))
+ instance_number += 1
+ return search_result
+ setattr(cls, "Search", Search)
cls = PLCOpenClasses.get("resource_task", None)
if cls:
@@ -878,6 +953,13 @@
self.interval = update_address(self.interval, address_model, new_leading)
setattr(cls, "updateElementAddress", updateElementAddress)
+ def Search(self, criteria, parent_infos=[]):
+ return _Search([("single", self.getsingle()),
+ ("interval", self.getinterval()),
+ ("priority", str(self.getpriority()))],
+ criteria, parent_infos)
+ setattr(cls, "Search", Search)
+
cls = PLCOpenClasses.get("pouInstance", None)
if cls:
def compatibility(self, tree):
@@ -890,6 +972,53 @@
self.typeName = new_name
setattr(cls, "updateElementName", updateElementName)
+ def Search(self, criteria, parent_infos=[]):
+ return _Search([("name", self.getname()),
+ ("type", self.gettypeName())],
+ criteria, parent_infos)
+ setattr(cls, "Search", Search)
+
+cls = PLCOpenClasses.get("varListPlain_variable", None)
+if cls:
+ def gettypeAsText(self):
+ vartype_content = self.gettype().getcontent()
+ # Variable type is a user data type
+ if vartype_content["name"] == "derived":
+ return vartype_content["value"].getname()
+ # Variable type is a string type
+ elif vartype_content["name"] in ["string", "wstring"]:
+ return vartype_content["name"].upper()
+ # Variable type is an array
+ elif vartype_content["name"] == "array":
+ base_type = vartype_content["value"].baseType.getcontent()
+ # Array derived directly from a user defined type
+ if base_type["name"] == "derived":
+ basetype_name = base_type["value"].getname()
+ # Array derived directly from a string type
+ elif base_type["name"] in ["string", "wstring"]:
+ basetype_name = base_type["name"].upper()
+ # Array derived directly from an elementary type
+ else:
+ basetype_name = base_type["name"]
+ return "ARRAY [%s] OF %s" % (",".join(map(lambda x : "%s..%s" % (x.getlower(), x.getupper()), vartype_content["value"].getdimension())), basetype_name)
+ # Variable type is an elementary type
+ return vartype_content["name"]
+ setattr(cls, "gettypeAsText", gettypeAsText)
+
+ def Search(self, criteria, parent_infos=[]):
+ search_result = _Search([("name", self.getname()),
+ ("type", self.gettypeAsText()),
+ ("address", self.getaddress())],
+ criteria, parent_infos)
+ initial = self.getinitialValue()
+ if initial is not None:
+ search_result.extend(_Search([("initial", initial.getvalue())], criteria, parent_infos))
+ doc = self.getdocumentation()
+ if doc is not None:
+ search_result.extend(doc.Search(criterias, parent_infos + ["documentation"]))
+ return search_result
+ setattr(cls, "Search", Search)
+
cls = PLCOpenClasses.get("project_types", None)
if cls:
def getdataTypeElements(self):
@@ -965,6 +1094,17 @@
raise ValueError, _("\"%s\" POU doesn't exist !!!")%name
setattr(cls, "removepouElement", removepouElement)
+ def Search(self, criteria, parent_infos=[]):
+ search_result = []
+ filter = criteria["filter"]
+ #if filter == "all" or "datatype" in filter:
+ # for datatype in self.dataTypes.getdataType():
+ # search_result.extend(datatype.Search(criteria, parent_infos))
+ for pou in self.pous.getpou():
+ search_result.extend(pou.Search(criteria, parent_infos))
+ return search_result
+ setattr(cls, "Search", Search)
+
cls = PLCOpenClasses.get("pous_pou", None)
if cls:
@@ -1287,6 +1427,37 @@
if result is not None:
variables.pop(i)
setattr(cls, "removeVariableByFilter", removeVariableByFilter)
+
+ def Search(self, criteria, parent_infos=[]):
+ search_result = []
+ filter = criteria["filter"]
+ if filter == "all" or self.getpouType() in filter:
+ parent_infos = parent_infos + ["P::%s" % self.getname()]
+ for result in TestTextElement(self.getname(), criteria):
+ search_result.append((tuple(parent_infos + ["name"]),) + result)
+ if self.interface is not None:
+ var_number = 0
+ for content in self.interface.getcontent():
+ variable_type = searchResultVarTypes.get(content["value"], "var_local")
+ variables = content["value"].getvariable()
+ for modifier, has_modifier in [("constant", content["value"].getconstant()),
+ ("retain", content["value"].getretain()),
+ ("non_retain", content["value"].getnonretain())]:
+ if has_modifier:
+ for result in TestTextElement(modifier, criteria):
+ search_result.append((tuple(parent_infos + [variable_type, (var_number, var_number + len(variables)), modifier]),) + result)
+ break
+ for variable in variables:
+ search_result.extend(variable.Search(criteria, parent_infos + [variable_type, var_number]))
+ var_number += 1
+ if len(self.body) > 0:
+ search_result.extend(self.body[0].Search(criteria, parent_infos))
+ for action in self.getactionList():
+ search_result.extend(action.Search(criteria, parent_infos))
+ for transition in self.gettransitionList():
+ search_result.extend(transition.Search(criteria, parent_infos))
+ return search_result
+ setattr(cls, "Search", Search)
def setbodyType(self, type):
if type == "IL":
@@ -1370,6 +1541,15 @@
return False
setattr(cls, "hasblock", hasblock)
+ def Search(self, criteria, parent_infos):
+ search_result = []
+ parent_infos = parent_infos[:-1] + ["T::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())]
+ for result in TestTextElement(self.getname(), criteria):
+ search_result.append((tuple(parent_infos + ["name"]),) + result)
+ search_result.extend(self.body.Search(criteria, parent_infos))
+ return search_result
+ setattr(cls, "Search", Search)
+
cls = PLCOpenClasses.get("actions_action", None)
if cls:
setattr(cls, "setbodyType", setbodyType)
@@ -1402,6 +1582,15 @@
return False
setattr(cls, "hasblock", hasblock)
+ def Search(self, criteria, parent_infos):
+ search_result = []
+ parent_infos = parent_infos[:-1] + ["A::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())]
+ for result in TestTextElement(self.getname(), criteria):
+ search_result.append((tuple(parent_infos + ["name"]),) + result)
+ search_result.extend(self.body.Search(criteria, parent_infos))
+ return search_result
+ setattr(cls, "Search", Search)
+
cls = PLCOpenClasses.get("body", None)
if cls:
cls.currentExecutionOrderId = 0
@@ -1569,6 +1758,16 @@
element["value"].updateElementAddress(address_model, new_leading)
setattr(cls, "updateElementAddress", updateElementAddress)
+ def Search(self, criteria, parent_infos=[]):
+ if self.content["name"] in ["IL", "ST"]:
+ search_result = self.content["value"].Search(criteria, parent_infos + ["body", 0])
+ else:
+ search_result = []
+ for element in self.content["value"].getcontent():
+ search_result.extend(element["value"].Search(criteria, parent_infos))
+ return search_result
+ setattr(cls, "Search", Search)
+
def getx(self):
return self.position.getx()
@@ -1681,6 +1880,9 @@
def _updateElementAddress(self, address_model, new_leading):
pass
+def _SearchInElement(self, criteria, parent_infos=[]):
+ return []
+
_connectionsFunctions = {
"bbox": {"none": _getBoundingBox,
"single": _getBoundingBoxSingle,
@@ -1693,7 +1895,7 @@
"multiple": _filterConnectionsMultiple},
"update": {"none": lambda self, translation: {},
"single": _updateConnectionsIdSingle,
- "multiple": _updateConnectionsIdMultiple}
+ "multiple": _updateConnectionsIdMultiple},
}
def _initElementClass(name, classname, connectionPointInType="none"):
@@ -1710,6 +1912,7 @@
setattr(cls, "translate", _connectionsFunctions["translate"][connectionPointInType])
setattr(cls, "filterConnections", _connectionsFunctions["filter"][connectionPointInType])
setattr(cls, "updateConnectionsId", _connectionsFunctions["update"][connectionPointInType])
+ setattr(cls, "Search", _SearchInElement)
return cls
def _getexecutionOrder(instance, specific_values):
@@ -1867,6 +2070,10 @@
self.content.updateElementAddress(address_model, new_leading)
setattr(cls, "updateElementAddress", updateElementAddress)
+ def Search(self, criteria, parent_infos=[]):
+ return self.content.Search(criteria, parent_infos + ["comment", self.getlocalId(), "content"])
+ setattr(cls, "Search", Search)
+
cls = _initElementClass("block", "fbdObjects_block")
if cls:
def getBoundingBox(self):
@@ -1912,6 +2119,20 @@
_translateConnections(input.connectionPointIn, dx, dy)
setattr(cls, "translate", translate)
+ def Search(self, criteria, parent_infos=[]):
+ parent_infos = parent_infos + ["block", self.getlocalId()]
+ search_result = _Search([("name", self.getinstanceName()),
+ ("type", self.gettypeName())],
+ criteria, parent_infos)
+ for i, variable in enumerate(self.inputVariables.getvariable()):
+ for result in TestTextElement(variable.getformalParameter(), criteria):
+ search_result.append((tuple(parent_infos + ["input", i]),) + result)
+ for i, variable in enumerate(self.outputVariables.getvariable()):
+ for result in TestTextElement(variable.getformalParameter(), criteria):
+ search_result.append((tuple(parent_infos + ["output", i]),) + result)
+ return search_result
+ setattr(cls, "Search", Search)
+
cls = _initElementClass("leftPowerRail", "ldObjects_leftPowerRail")
if cls:
setattr(cls, "getinfos", _getpowerrailinfosFunction("leftPowerRail"))
@@ -1933,6 +2154,10 @@
self.variable = update_address(self.variable, address_model, new_leading)
setattr(cls, "updateElementAddress", updateElementAddress)
+ def Search(self, criteria, parent_infos=[]):
+ return _Search([("reference", self.getvariable())], criteria, parent_infos + ["contact", self.getlocalId()])
+ setattr(cls, "Search", Search)
+
cls = _initElementClass("coil", "ldObjects_coil", "single")
if cls:
setattr(cls, "getinfos", _getldelementinfosFunction("coil"))
@@ -1946,6 +2171,10 @@
self.variable = update_address(self.variable, address_model, new_leading)
setattr(cls, "updateElementAddress", updateElementAddress)
+ def Search(self, criteria, parent_infos=[]):
+ return _Search([("reference", self.getvariable())], criteria, parent_infos + ["coil", self.getlocalId()])
+ setattr(cls, "Search", Search)
+
cls = _initElementClass("step", "sfcObjects_step", "single")
if cls:
def getinfos(self):
@@ -1963,6 +2192,10 @@
return infos
setattr(cls, "getinfos", getinfos)
+ def Search(self, criteria, parent_infos=[]):
+ return _Search([("name", self.getname())], criteria, parent_infos + ["step", self.getlocalId()])
+ setattr(cls, "Search", Search)
+
cls = PLCOpenClasses.get("transition_condition", None)
if cls:
def compatibility(self, tree):
@@ -2058,6 +2291,17 @@
return content["value"].getconnections()
setattr(cls, "getconnections", getconnections)
+ def Search(self, criteria, parent_infos=[]):
+ parent_infos = parent_infos + ["transition", self.getlocalId()]
+ search_result = []
+ content = self.condition.getcontent()
+ if content["name"] == "reference":
+ search_result.extend(_Search([("reference", content["value"].getname())], criteria, parent_infos))
+ elif content["name"] == "inline":
+ search_result.extend(content["value"].Search(criteria, parent_infos + ["inline"]))
+ return search_result
+ setattr(cls, "Search", Search)
+
cls = _initElementClass("selectionDivergence", "sfcObjects_selectionDivergence", "single")
if cls:
setattr(cls, "getinfos", _getdivergenceinfosFunction(True, False))
@@ -2084,6 +2328,10 @@
return infos
setattr(cls, "getinfos", getinfos)
+ def Search(self, criteria, parent_infos):
+ return _Search([("target", self.gettargetName())], criteria, parent_infos + ["jump", self.getlocalId()])
+ setattr(cls, "Search", Search)
+
cls = PLCOpenClasses.get("actionBlock_action", None)
if cls:
def compatibility(self, tree):
@@ -2134,6 +2382,18 @@
self.inline.updateElementAddress(address_model, new_leading)
setattr(cls, "updateElementAddress", updateElementAddress)
+ def Search(self, criteria, parent_infos=[]):
+ qualifier = self.getqualifier()
+ if qualifier is None:
+ qualifier = "N"
+ return _Search([("inline", self.getinlineContent()),
+ ("reference", self.getreferenceName()),
+ ("qualifier", qualifier),
+ ("duration", self.getduration()),
+ ("indicator", self.getindicator())],
+ criteria, parent_infos)
+ setattr(cls, "Search", Search)
+
cls = _initElementClass("actionBlock", "commonObjects_actionBlock", "single")
if cls:
def compatibility(self, tree):
@@ -2201,6 +2461,17 @@
action.updateElementAddress(address_model, new_leading)
setattr(cls, "updateElementAddress", updateElementAddress)
+ def Search(self, criteria, parent_infos=[]):
+ parent_infos = parent_infos + ["action_block", self.getlocalId()]
+ search_result = []
+ for idx, action in enumerate(self.action):
+ search_result.extend(action.Search(criteria, parent_infos + ["action", idx]))
+ return search_result
+ setattr(cls, "Search", Search)
+
+def _SearchInIOVariable(self, criteria, parent_infos=[]):
+ return _Search([("expression", self.getexpression())], criteria, parent_infos + ["io_variable", self.getlocalId()])
+
cls = _initElementClass("inVariable", "fbdObjects_inVariable")
if cls:
setattr(cls, "getinfos", _getvariableinfosFunction("input", False, True))
@@ -2214,6 +2485,8 @@
self.expression = update_address(self.expression, address_model, new_leading)
setattr(cls, "updateElementAddress", updateElementAddress)
+ setattr(cls, "Search", _SearchInIOVariable)
+
cls = _initElementClass("outVariable", "fbdObjects_outVariable", "single")
if cls:
setattr(cls, "getinfos", _getvariableinfosFunction("output", True, False))
@@ -2227,6 +2500,8 @@
self.expression = update_address(self.expression, address_model, new_leading)
setattr(cls, "updateElementAddress", updateElementAddress)
+ setattr(cls, "Search", _SearchInIOVariable)
+
cls = _initElementClass("inOutVariable", "fbdObjects_inOutVariable", "single")
if cls:
setattr(cls, "getinfos", _getvariableinfosFunction("inout", True, True))
@@ -2240,13 +2515,21 @@
self.expression = update_address(self.expression, address_model, new_leading)
setattr(cls, "updateElementAddress", updateElementAddress)
+ setattr(cls, "Search", _SearchInIOVariable)
+
+
+def _SearchInConnector(self, criteria, parent_infos=[]):
+ return _Search([("name", self.getname())], criteria, parent_infos + ["connector", self.getlocalId()])
+
cls = _initElementClass("continuation", "commonObjects_continuation")
if cls:
setattr(cls, "getinfos", _getconnectorinfosFunction("continuation"))
+ setattr(cls, "Search", _SearchInConnector)
cls = _initElementClass("connector", "commonObjects_connector", "single")
if cls:
setattr(cls, "getinfos", _getconnectorinfosFunction("connector"))
+ setattr(cls, "Search", _SearchInConnector)
cls = PLCOpenClasses.get("connection", None)
if cls: