--- a/canfestival/config_utils.py Fri Aug 11 15:18:19 2017 +0300
+++ b/canfestival/config_utils.py Mon Aug 14 19:13:01 2017 +0300
@@ -30,7 +30,7 @@
"LREAL":0x11,"STRING":0x09,"BYTE":0x05,"WORD":0x06,"DWORD":0x07,
"LWORD":0x1B,"WSTRING":0x0B}
-# Constants for PDO types
+# Constants for PDO types @@ -61,7 +61,7 @@
@param size: number of bytes generated
@return: a string containing the value converted
data = ("%" + str(size * 2) + "." + str(size * 2) + "X") % value
list_car = [data[i:i+2] for i in xrange(0, len(data), 2)]
@@ -71,12 +71,12 @@
def GetNodePDOIndexes(node, type, parameters = False):
Find the PDO indexes of a node
@param type: type of PDO searched (RPDO or TPDO or both)
@param parameters: indicate which indexes are expected (PDO paramaters : True or PDO mappings : False)
@return: a list of indexes found
indexes.extend([idx for idx in node.GetIndexes() if 0x1400 <= idx <= 0x15FF])
@@ -91,14 +91,14 @@
def SearchNodePDOMapping(loc_infos, node):
Find the PDO indexes of a node
@param type: type of PDO searched (RPDO or TPDO or both)
@param parameters: indicate which indexes are expected (PDO paramaters : True or PDO mappings : False)
@return: a list of indexes found
model = (loc_infos["index"] << 16) + (loc_infos["subindex"] << 8)
for PDOidx in GetNodePDOIndexes(node, loc_infos["pdotype"]):
values = node.GetEntry(PDOidx)
@@ -115,9 +115,9 @@
@param cobid: PDO generated COB ID
@param transmittype : PDO transmit type
@param pdomapping: list of PDO mappings
- @return: a tuple of value and number of parameters to add to DCF
+ @return: a tuple of value and number of parameters to add to DCF
# Create entry for RPDO or TPDO parameters and Disable PDO
# ---- INDEX ----- --- SUBINDEX ---- ----- SIZE ------ ------ DATA ------
@@ -152,7 +152,7 @@
# Dictionary of pointed variables
self.PointedVariables = {}
self.Manager = self.NodeList.Manager
self.MasterNode = self.Manager.GetCurrentNodeCopy()
@@ -161,18 +161,18 @@
def GetPointedVariables(self):
return self.PointedVariables
def RemoveUsedNodeCobId(self, node):
Remove all PDO COB ID used by the given node from the list of available COB ID
@return: a tuple of number of RPDO and TPDO for the node
# Get list of all node TPDO and RPDO indexes
nodeRpdoIndexes = GetNodePDOIndexes(node, RPDO, True)
nodeTpdoIndexes = GetNodePDOIndexes(node, TPDO, True)
# Mark all the COB ID of the node already mapped PDO as not available
for PdoIdx in nodeRpdoIndexes + nodeTpdoIndexes:
pdo_cobid = node.GetEntry(PdoIdx, 0x01)
@@ -182,20 +182,20 @@
# Remove COB ID from the list of available COB ID
if pdo_cobid in self.ListCobIDAvailable:
self.ListCobIDAvailable.remove(pdo_cobid)
return len(nodeRpdoIndexes), len(nodeTpdoIndexes)
def PrepareMasterNode(self):
Add mandatory entries for DCF generation into MasterNode.
# Adding DCF entry into Master node
if not self.MasterNode.IsEntry(0x1F22):
self.MasterNode.AddEntry(0x1F22, 1, "")
self.Manager.AddSubentriesToCurrent(0x1F22, 127, self.MasterNode)
# Adding trash mappable variables for unused mapped datas
idxTrashVariables = 0x2000 + self.MasterNode.GetNodeID()
# Add an entry for storing unexpected all variable
@@ -206,9 +206,9 @@
self.Manager.SetCurrentEntry(idxTrashVariables, subidx + 1, typeidx, "type", None, self.MasterNode)
# Store the mapping value for this entry
self.TrashVariables[size] = (idxTrashVariables << 16) + ((subidx + 1) << 8) + size
RPDOnumber, TPDOnumber = self.RemoveUsedNodeCobId(self.MasterNode)
# Store the indexes of the first RPDO and TPDO available for MasterNode
self.CurrentPDOParamsIdx = {RPDO : 0x1400 + RPDOnumber, TPDO : 0x1800 + TPDOnumber}
@@ -216,9 +216,9 @@
for idx, (nodeid, nodeinfos) in enumerate(self.NodeList.SlaveNodes.items()):
RPDOnumber, TPDOnumber = self.RemoveUsedNodeCobId(node)
# Get Slave's default SDO server parameters
RSDO_cobid = node.GetEntry(0x1200,0x01)
@@ -226,20 +226,20 @@
TSDO_cobid = node.GetEntry(0x1200,0x02)
TSDO_cobid = 0x580 + nodeid
# Configure Master's SDO parameters entries
self.Manager.ManageEntriesOfCurrent([0x1280 + idx], [], self.MasterNode)
self.MasterNode.SetEntry(0x1280 + idx, 0x01, RSDO_cobid)
self.MasterNode.SetEntry(0x1280 + idx, 0x02, TSDO_cobid)
- self.MasterNode.SetEntry(0x1280 + idx, 0x03, nodeid)
+ self.MasterNode.SetEntry(0x1280 + idx, 0x03, nodeid)
def AddParamsToDCF(self, nodeid, data, nbparams):
Add entry to DCF, for the requested nodeID
@@ -249,19 +249,19 @@
# Get current DCF for slave
nodeDCF = self.MasterNode.GetEntry(0x1F22, nodeid)
# Extract data and number of params in current DCF
if nodeDCF != None and nodeDCF != '':
tmpnbparams = [i for i in nodeDCF[:4]]
nbparams += int(''.join(["%2.2x"%ord(i) for i in tmpnbparams]), 16)
data = nodeDCF[4:] + data
dcf = LE_to_BE(nbparams, 0x04) + data
self.MasterNode.SetEntry(0x1F22, nodeid, dcf)
def GetEmptyPDO(self, nodeid, pdotype, start_index=None):
Search a not configured PDO for a slave
@@ -275,7 +275,7 @@
index = PDOTypeBaseIndex[pdotype]
# Search for all PDO possible index until find a configurable PDO
# starting from start_index
while index < PDOTypeBaseIndex[pdotype] + 0x200:
@@ -296,7 +296,7 @@
return index, cobid, values[0]
def AddPDOMapping(self, nodeid, pdotype, pdoindex, pdocobid, pdomapping, sync_TPDOs):
Record a new mapping request for a slave, and add related slave config to the DCF
@@ -305,16 +305,16 @@
@param pdomapping: list od variables to map with PDO
# Add an entry to MasterMapping
- self.MasterMapping[pdocobid] = {"type" : InvertPDOType[pdotype],
+ self.MasterMapping[pdocobid] = {"type" : InvertPDOType[pdotype], "mapping" : [None] + [(loc_infos["type"], name) for name, loc_infos in pdomapping]}
# Return the data to add to DCF
return GeneratePDOMappingDCF(pdoindex, pdocobid, 0x01, pdomapping)
return GeneratePDOMappingDCF(pdoindex, pdocobid, 0xFF, pdomapping)
def GenerateDCF(self, locations, current_location, sync_TPDOs):
Generate Concise DCF of MasterNode for the locations list given
@@ -322,18 +322,18 @@
@param current_location: tuple of the located prefixes not to be considered
@param sync_TPDOs: indicate if TPDO must be synchronous
#-------------------------------------------------------------------------------
# Verify that locations correspond to real slave variables
#-------------------------------------------------------------------------------
# Get list of locations check if exists and mappables -> put them in IECLocations
for location in locations:
COlocationtype = IECToCOType[location["IEC_TYPE"]]
if name in self.IECLocations:
if self.IECLocations[name]["type"] != COlocationtype:
- raise PDOmappingException, _("Type conflict for location \"%s\"") % name
+ raise PDOmappingException, _("Type conflict for location \"%s\"") % name # Get only the part of the location that concern this node
loc = location["LOC"][len(current_location):]
@@ -342,30 +342,30 @@
raise PDOmappingException, _("Bad location size : %s") % str(loc)
direction = location["DIR"]
sizelocation = location["SIZE"]
# Extract and check nodeid
nodeid, index, subindex = loc[:3]
# Check Id is in slave node list
if nodeid not in self.NodeList.SlaveNodes.keys():
raise PDOmappingException, _("Non existing node ID : {a1} (variable {a2})").format(a1 = nodeid, a2 = name)
# Get the model for this node (made from EDS)
node = self.NodeList.SlaveNodes[nodeid]["Node"]
# Extract and check index and subindex
if not node.IsEntry(index, subindex):
msg = _("No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})").\
format(a1 = "%x" % index, a2 ="%x" % subindex, a3 = nodeid, a4 = name)
raise PDOmappingException, msg
subentry_infos = node.GetSubentryInfos(index, subindex)
if subentry_infos and subentry_infos["pdo"]:
if sizelocation == "X" and len(loc) > 3:
@@ -376,11 +376,11 @@
raise PDOmappingException, msg
if location["IEC_TYPE"] != "BOOL" and subentry_infos["type"] != COlocationtype:
raise PDOmappingException, _("Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"").\
format(a1 = location["IEC_TYPE"], a2 = COlocationtype, a3 = subentry_infos["type"] , a4 = name)
typeinfos = node.GetEntryInfos(COlocationtype)
self.IECLocations[name] = {"type":COlocationtype, "pdotype":SlavePDOType[direction],
"nodeid": nodeid, "index": index,"subindex": subindex,
@@ -388,21 +388,21 @@
raise PDOmappingException, _("Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))").\
format(a1 = name, a2 = nodeid, a3 = "%x" % index, a4 = "%x" % subindex)
#-------------------------------------------------------------------------------
# Search for locations already mapped
#-------------------------------------------------------------------------------
for name, locationinfos in self.IECLocations.items():
node = self.NodeList.SlaveNodes[locationinfos["nodeid"]]["Node"]
# Search if slave has a PDO mapping this locations
result = SearchNodePDOMapping(locationinfos, node)
cobid = self.NodeList.GetSlaveNodeEntry(locationinfos["nodeid"], index - 0x200, 1)
# Add PDO to MasterMapping
if cobid not in self.MasterMapping.keys():
# Verify that PDO transmit type is conform to sync_TPDOs
@@ -414,10 +414,10 @@
# Change TransmitType to ASYCHRONE
data, nbparams = GeneratePDOMappingDCF(index - 0x200, cobid, 0xFF, [])
- # Add entry to slave dcf to change transmit type of
+ # Add entry to slave dcf to change transmit type of self.AddParamsToDCF(locationinfos["nodeid"], data, nbparams)
values = node.GetEntry(index)
# Store the size of each entry mapped in PDO
@@ -425,7 +425,7 @@
mapping.append(value % 0x100)
self.MasterMapping[cobid] = {"type" : InvertPDOType[locationinfos["pdotype"]], "mapping" : mapping}
# Indicate that this PDO entry must be saved
if locationinfos["bit"] is not None:
if not isinstance(self.MasterMapping[cobid]["mapping"][subindex], ListType):
@@ -434,24 +434,24 @@
self.MasterMapping[cobid]["mapping"][subindex][locationinfos["bit"]] = (locationinfos["type"], name)
self.MasterMapping[cobid]["mapping"][subindex] = (locationinfos["type"], name)
# Add location to those that haven't been mapped yet
if locationinfos["nodeid"] not in self.LocationsNotMapped.keys():
self.LocationsNotMapped[locationinfos["nodeid"]] = {TPDO : [], RPDO : []}
self.LocationsNotMapped[locationinfos["nodeid"]][locationinfos["pdotype"]].append((name, locationinfos))
#-------------------------------------------------------------------------------
# Build concise DCF for the others locations
#-------------------------------------------------------------------------------
for nodeid, locations in self.LocationsNotMapped.items():
node = self.NodeList.SlaveNodes[nodeid]["Node"]
# Initialize number of params and data to add to node DCF
# Generate the best PDO mapping for each type of PDO
for pdotype in (TPDO, RPDO):
if len(locations[pdotype]) > 0:
@@ -483,78 +483,78 @@
data, nbaddedparams = self.AddPDOMapping(nodeid, pdotype, pdoindex, pdocobid, pdomapping, sync_TPDOs)
nbparams += nbaddedparams
# Add number of params and data to node DCF
self.AddParamsToDCF(nodeid, dataparams, nbparams)
#-------------------------------------------------------------------------------
# Master Node Configuration
#-------------------------------------------------------------------------------
# Generate Master's Configuration from informations stored in MasterMapping
for cobid, pdo_infos in self.MasterMapping.items():
# Get next PDO index in MasterNode for this PDO type
current_idx = self.CurrentPDOParamsIdx[pdo_infos["type"]]
# Search if there is already a PDO in MasterNode with this cob id
for idx in GetNodePDOIndexes(self.MasterNode, pdo_infos["type"], True):
if self.MasterNode.GetEntry(idx, 1) == cobid:
# Add a PDO to MasterNode if not PDO have been found
if current_idx == self.CurrentPDOParamsIdx[pdo_infos["type"]]:
addinglist = [current_idx, current_idx + 0x200]
self.Manager.ManageEntriesOfCurrent(addinglist, [], self.MasterNode)
self.MasterNode.SetEntry(current_idx, 0x01, cobid)
# Increment the number of PDO for this PDO type
self.CurrentPDOParamsIdx[pdo_infos["type"]] += 1
# Change the transmit type of the PDO
self.MasterNode.SetEntry(current_idx, 0x02, 0x01)
self.MasterNode.SetEntry(current_idx, 0x02, 0xFF)
for item in pdo_infos["mapping"]:
if isinstance(item, ListType):
# Add some subentries to PDO mapping if there is not enough
self.Manager.AddSubentriesToCurrent(current_idx + 0x200, len(mapping) - 1, self.MasterNode)
# Generate MasterNode's PDO mapping
for subindex, variable in enumerate(mapping):
if isinstance(variable, (IntType, LongType)):
# If variable is an integer then variable is unexpected
self.MasterNode.SetEntry(current_idx + 0x200, subindex, self.TrashVariables[variable])
typeidx, varname = variable
variable_infos = self.IECLocations[varname]
# Calculate base index for storing variable
mapvariableidx = VariableStartIndex[variable_infos["pdotype"]] + \
VariableTypeOffset[variable_infos["sizelocation"]] * VariableIncrement + \
indexname = "%s%s%s_%d"%(VariableDirText[variable_infos["pdotype"]],
variable_infos["sizelocation"],
'_'.join(map(str,current_location)),
- variable_infos["nodeid"])
- # Search for an entry that has an empty subindex
+ variable_infos["nodeid"]) + # Search for an entry that has an empty subindex while mapvariableidx < VariableStartIndex[variable_infos["pdotype"]] + 0x2000:
- if not self.MasterNode.IsEntry(mapvariableidx):
+ if not self.MasterNode.IsEntry(mapvariableidx): # Add entry to MasterNode
self.Manager.AddMapVariableToCurrent(mapvariableidx, "beremiz"+indexname, 3, 1, self.MasterNode)
@@ -567,7 +567,7 @@
mapvariableidx += 8 * VariableIncrement
# Verify that a not full entry has been found
if mapvariableidx < VariableStartIndex[variable_infos["pdotype"]] + 0x2000:
@@ -582,13 +582,13 @@
# Add informations to the new subentry created
self.MasterNode.SetMappingEntry(mapvariableidx, nbsubentries, values = {"name" : subindexname})
self.MasterNode.SetMappingEntry(mapvariableidx, nbsubentries, values = {"type" : typeidx})
# Set value of the PDO mapping
typeinfos = self.Manager.GetEntryInfos(typeidx)
value = (mapvariableidx << 16) + ((nbsubentries) << 8) + typeinfos["size"]
self.MasterNode.SetEntry(current_idx + 0x200, subindex, value)
# Add variable to pointed variables
self.PointedVariables[(mapvariableidx, nbsubentries)] = "%s_%s"%(indexname, subindexname)
@@ -605,7 +605,7 @@
@param nodelist: CanFestival network editor model
@return: a modified copy of the given CanFestival network editor model
dcfgenerator = ConciseDCFGenerator(nodelist, nodename)
dcfgenerator.GenerateDCF(locations, current_location, sync_TPDOs)
masternode,pointers = dcfgenerator.GetMasterNode(), dcfgenerator.GetPointedVariables()
@@ -621,7 +621,7 @@
if IECLocations[name] != COlocationtype:
- raise PDOmappingException, _("Type conflict for location \"%s\"") % name
+ raise PDOmappingException, _("Type conflict for location \"%s\"") % name # Get only the part of the location that concern this node
loc = location["LOC"][len(current_location):]
@@ -630,25 +630,25 @@
raise PDOmappingException, _("Bad location size : %s") % str(loc)
# Extract and check nodeid
index, subindex = loc[:2]
# Extract and check index and subindex
if not slave.IsEntry(index, subindex):
raise PDOmappingException, _("No such index/subindex ({a1},{a2}) (variable {a3})").\
format(a1 = "%x" % index, a2 = "%x" % subindex, a3 = name)
- subentry_infos = slave.GetSubentryInfos(index, subindex)
+ subentry_infos = slave.GetSubentryInfos(index, subindex) if subentry_infos["type"] != COlocationtype:
raise PDOmappingException, _("Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"").\
format( a1 = location["IEC_TYPE"], a2 = COlocationtype, a3 = subentry_infos["type"] , a4 = name)
IECLocations[name] = COlocationtype
pointers[(index, subindex)] = name
if __name__ == "__main__":
@@ -667,7 +667,7 @@
Use with caution. Be sure that config_utils
is currently working properly.
# Boolean that indicate if reference result must be redefined
@@ -693,15 +693,15 @@
base_folder = os.path.split(base_folder)[0]
# Add CanFestival folder to search pathes
sys.path.append(os.path.join(base_folder, "CanFestival-3", "objdictgen"))
from nodemanager import *
# Open the test nodelist contained into test_config folder
nodelist = NodeList(manager)
result = nodelist.LoadProject("test_config")
# List of locations, we try to map for test
locations = [{"IEC_TYPE":"BYTE","NAME":"__IB0_1_64_24576_1","DIR":"I","SIZE":"B","LOC":(0,1,64,24576,1)},
{"IEC_TYPE":"INT","NAME":"__IW0_1_64_25601_2","DIR":"I","SIZE":"W","LOC":(0,1,64,25601,2)},
@@ -713,34 +713,34 @@
{"IEC_TYPE":"UDINT","NAME":"__ID0_1_64_25638_3","DIR":"I","SIZE":"D","LOC":(0,1,64,25638,3)},
{"IEC_TYPE":"UDINT","NAME":"__ID0_1_64_25638_4","DIR":"I","SIZE":"D","LOC":(0,1,64,25638,4)},
{"IEC_TYPE":"UDINT","NAME":"__ID0_1_4096_0","DIR":"I","SIZE":"D","LOC":(0,1,4096,0)}]
# Generate MasterNode configuration
masternode, pointedvariables = GenerateConciseDCF(locations, (0, 1), nodelist, True, "TestNode")
except ValueError, message:
print "%s\nTest Failed!"%message
- # Get Text corresponding to MasterNode
+ # Get Text corresponding to MasterNode result_node = masternode.PrintString()
result_vars = pprint.pformat(pointedvariables)
result = result_node + "\n********POINTERS*********\n" + result_vars + "\n"
# If reset has been choosen
# Write Text into reference result file
testfile = open("test_config/result.txt", "w")
print "Reset Successful!"
testfile = open("test_config/result_tmp.txt", "w")
os.system("diff test_config/result.txt test_config/result_tmp.txt")
os.remove("test_config/result_tmp.txt")
--- a/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Fri Aug 11 15:18:19 2017 +0300
+++ b/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Mon Aug 14 19:13:01 2017 +0300
@@ -83,23 +83,23 @@
elif range_min is not None:
min_value = min(min_value, range_min)
# Update maximal range value
elif range_min is not None:
max_value = max(max_value, range_max)
# Calculate range center and width if at least one valid range is defined
if min_value is not None and max_value is not None:
center = (min_value + max_value) / 2.
range_size = max(1.0, max_value - min_value)
# Set default center and with if no valid range is defined
# Return range expended from 10 %
return center - range_size * 0.55, center + range_size * 0.55
@@ -113,7 +113,7 @@
class DebugVariableGraphicDropTarget(wx.TextDropTarget):
def __init__(self, parent, window):
@@ -123,7 +123,7 @@
wx.TextDropTarget.__init__(self)
self.ParentControl = parent
self.ParentWindow = window
@@ -132,7 +132,7 @@
self.ParentControl = None
def OnDragOver(self, x, y, d):
Function called when mouse is dragged over Drop Target
@@ -142,9 +142,9 @@
# Signal parent that mouse is dragged over
self.ParentControl.OnMouseDragging(x, y)
return wx.TextDropTarget.OnDragOver(self, x, y, d)
def OnDropText(self, x, y, data):
Function called when mouse is released in Drop Target
@@ -154,9 +154,9 @@
# Signal Debug Variable Panel to reset highlight
self.ParentWindow.ResetHighlight()
# Check that data is valid regarding DebugVariablePanel
@@ -165,54 +165,54 @@
message = _("Invalid value \"%s\" for debug variable")%data
# Display message if data is invalid
wx.CallAfter(self.ShowMessage, message)
# Data contain a reference to a variable to debug
elif values[1] == "debug":
target_idx = self.ParentControl.GetIndex()
# If mouse is dropped in graph canvas bounding box and graph is
# not 3D canvas, graphs will be merged
rect = self.ParentControl.GetAxesBoundingBox()
if not self.ParentControl.Is3DCanvas() and rect.InsideXY(x, y):
# Default merge type is parallel
merge_type = GRAPH_PARALLEL
# If mouse is dropped in left part of graph canvas, graph
# wall be merged orthogonally
- merge_rect = wx.Rect(rect.x, rect.y,
+ merge_rect = wx.Rect(rect.x, rect.y, rect.width / 2., rect.height)
if merge_rect.InsideXY(x, y):
merge_type = GRAPH_ORTHOGONAL
- wx.CallAfter(self.ParentWindow.MergeGraphs,
+ wx.CallAfter(self.ParentWindow.MergeGraphs,
width, height = self.ParentControl.GetSize()
# Get Before which Viewer the variable has to be moved or added
# according to the position of mouse in Viewer.
# Drag'n Drop is an internal is an internal move inside Debug
if len(values) > 2 and values[2] == "move":
- self.ParentWindow.MoveValue(values[0],
+ self.ParentWindow.MoveValue(values[0],
# Drag'n Drop was initiated by another control of Beremiz
- self.ParentWindow.InsertValue(values[0],
+ self.ParentWindow.InsertValue(values[0],
Function called when mouse is leave Drop Target
@@ -220,15 +220,15 @@
# Signal Debug Variable Panel to reset highlight
self.ParentWindow.ResetHighlight()
return wx.TextDropTarget.OnLeave(self)
def ShowMessage(self, message):
Show error message in Error Dialog
@param message: Error message to display
- dialog = wx.MessageDialog(self.ParentWindow,
+ dialog = wx.MessageDialog(self.ParentWindow, @@ -243,7 +243,7 @@
class DebugVariableGraphicViewer(DebugVariableViewer, FigureCanvas):
def __init__(self, parent, window, items, graph_type):
@@ -253,36 +253,36 @@
@param graph_type: Graph display type (Parallel or orthogonal)
DebugVariableViewer.__init__(self, window, items)
self.GraphType = graph_type # Graph type display
self.CursorTick = None # Tick of the graph cursor
# Mouse position when start dragging
self.MouseStartPos = None
# Tick when moving tick start
self.StartCursorTick = None
# Canvas size when starting to resize canvas
- self.CanvasStartSize = None
+ self.CanvasStartSize = None # List of current displayed contextual buttons
self.ContextualButtons = []
# Reference to item for which contextual buttons was displayed
self.ContextualButtonsItem = None
# Flag indicating that zoom fit current displayed data range or whole
# Create figure for drawing graphs
self.Figure = matplotlib.figure.Figure(facecolor='w')
# Defined border around figure in canvas
- self.Figure.subplotpars.update(top=0.95, left=0.1,
+ self.Figure.subplotpars.update(top=0.95, left=0.1,
FigureCanvas.__init__(self, parent, -1, self.Figure)
self.SetWindowStyle(wx.WANTS_CHARS)
self.SetBackgroundColour(wx.WHITE)
self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
@@ -290,42 +290,42 @@
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
self.Bind(wx.EVT_SIZE, self.OnResize)
canvas_size = self.GetCanvasMinSize()
self.SetMinSize(canvas_size)
# Define Viewer drop target
self.SetDropTarget(DebugVariableGraphicDropTarget(self, window))
# Connect matplotlib events
self.mpl_connect('button_press_event', self.OnCanvasButtonPressed)
self.mpl_connect('motion_notify_event', self.OnCanvasMotion)
self.mpl_connect('button_release_event', self.OnCanvasButtonReleased)
self.mpl_connect('scroll_event', self.OnCanvasScroll)
# Add buttons for zooming on current displayed data range
GraphButton(0, 0, "fit_graph", self.OnZoomFitButton))
# Add buttons for changing canvas size with predefined height
[SIZE_MINI, SIZE_MIDDLE, SIZE_MAXI],
["minimize_graph", "middle_graph", "maximize_graph"]):
- GraphButton(0, 0, bitmap,
+ GraphButton(0, 0, bitmap, self.GetOnChangeSizeButton(size)))
# Add buttons for exporting graph values to clipboard and close graph
for bitmap, callback in [
("export_graph_mini", self.OnExportGraphButton),
("delete_graph", self.OnCloseButton)]:
self.Buttons.append(GraphButton(0, 0, bitmap, callback))
self.RefreshLabelsPosition(canvas_size.height)
Add an item to the list of items displayed by Viewer
@@ -333,14 +333,14 @@
DebugVariableViewer.AddItem(self, item)
def RemoveItem(self, item):
Remove an item from the list of items displayed by Viewer
@param item: Item to remove from the list
DebugVariableViewer.RemoveItem(self, item)
# If list of items is not empty
if not self.ItemsIsEmpty():
# Return to parallel graph if there is only one item
@@ -348,51 +348,51 @@
self.GraphType = GRAPH_PARALLEL
def SetCursorTick(self, cursor_tick):
@param cursor_tick: Cursor tick
self.CursorTick = cursor_tick
def SetZoomFit(self, zoom_fit):
Set flag indicating that zoom fit current displayed data range
@param zoom_fit: Flag for zoom fit (False: zoom fit whole data range)
- # Flag is different from the actual one
+ # Flag is different from the actual one if zoom_fit != self.ZoomFit:
# Update button for zoom fit bitmap
self.Buttons[0].SetBitmap("full_graph" if zoom_fit else "fit_graph")
def SubscribeAllDataConsumers(self):
Function that unsubscribe and remove every item that store values of
a variable that doesn't exist in PLC anymore
DebugVariableViewer.SubscribeAllDataConsumers(self)
# Graph still have data to display
if not self.ItemsIsEmpty():
# Reset flag indicating that zoom fit current displayed data range
Return if Viewer is a 3D canvas
@return: True if Viewer is a 3D canvas
return self.GraphType == GRAPH_ORTHOGONAL and len(self.Items) == 3
Return list of buttons defined in Viewer
@@ -400,7 +400,7 @@
# Add contextual buttons to default buttons
return self.Buttons + self.ContextualButtons
def PopupContextualButtons(self, item, rect, direction=wx.RIGHT):
Show contextual menu for item aside a label of this item defined
@@ -412,19 +412,19 @@
# Return immediately if contextual menu for item is already shown
if self.ContextualButtonsItem == item:
# Close already shown contextual menu
self.DismissContextualButtons()
# Save item for which contextual menu is shown
self.ContextualButtonsItem = item
# If item variable is forced, add button for release variable to
if self.ContextualButtonsItem.IsForced():
self.ContextualButtons.append(
GraphButton(0, 0, "release", self.OnReleaseItemButton))
# Add other buttons to contextual menu
for bitmap, callback in [
("force", self.OnForceItemButton),
@@ -432,13 +432,13 @@
("delete_graph", self.OnRemoveItemButton)]:
self.ContextualButtons.append(
GraphButton(0, 0, bitmap, callback))
# If buttons are shown at left side or upper side of rect, positions
# will be set in reverse order
buttons = self.ContextualButtons[:]
if direction in [wx.TOP, wx.LEFT]:
# Set contextual menu buttons position aside rect depending on
@@ -458,10 +458,10 @@
self.ParentWindow.ForceRefresh()
def DismissContextualButtons(self):
Close current shown contextual menu
@@ -469,14 +469,14 @@
# Return immediately if no contextual menu is shown
if self.ContextualButtonsItem is None:
# Reset variables corresponding to contextual menu
self.ContextualButtonsItem = None
self.ContextualButtons = []
self.ParentWindow.ForceRefresh()
def IsOverContextualButton(self, x, y):
Return if point is over one contextual button of Viewer
@@ -488,7 +488,7 @@
def ExportGraph(self, item=None):
Export item(s) data to clipboard in CSV format
@@ -497,17 +497,17 @@
self.ParentWindow.CopyDataToClipboard(
[(item, [entry for entry in item.GetData()])
- for item in (self.Items
+ for item in (self.Items
def OnZoomFitButton(self):
Function called when Viewer Zoom Fit button is pressed
# Toggle zoom fit flag value
self.SetZoomFit(not self.ZoomFit)
def GetOnChangeSizeButton(self, height):
Function that generate callback function for change Viewer height to
@@ -518,32 +518,32 @@
def OnChangeSizeButton():
self.SetCanvasHeight(height)
return OnChangeSizeButton
def OnExportGraphButton(self):
Function called when Viewer Export button is pressed
# Export data of every item in Viewer
def OnForceItemButton(self):
Function called when contextual menu Force button is pressed
- # Open dialog for forcing item variable value
+ # Open dialog for forcing item variable value self.ForceValue(self.ContextualButtonsItem)
self.DismissContextualButtons()
def OnReleaseItemButton(self):
Function called when contextual menu Release button is pressed
- # Release item variable value
+ # Release item variable value self.ReleaseValue(self.ContextualButtonsItem)
self.DismissContextualButtons()
def OnExportItemGraphButton(self):
Function called when contextual menu Export button is pressed
@@ -552,17 +552,17 @@
self.ExportGraph(self.ContextualButtonsItem)
self.DismissContextualButtons()
- def OnRemoveItemButton(self):
+ def OnRemoveItemButton(self): Function called when contextual menu Remove button is pressed
# Remove item from Viewer
- wx.CallAfter(self.ParentWindow.DeleteValue, self,
+ wx.CallAfter(self.ParentWindow.DeleteValue, self, self.ContextualButtonsItem)
self.DismissContextualButtons()
def HandleCursorMove(self, event):
Update Cursor position according to mouse position and graph type
@@ -571,7 +571,7 @@
start_tick, end_tick = self.ParentWindow.GetRange()
items = self.ItemsDict.values()
if self.GraphType == GRAPH_ORTHOGONAL:
# Extract items data displayed in canvas figure
@@ -579,31 +579,31 @@
end_tick = max(end_tick, start_tick)
x_data = items[0].GetData(start_tick, end_tick)
y_data = items[1].GetData(start_tick, end_tick)
# Search for the nearest point from mouse position
if len(x_data) > 0 and len(y_data) > 0:
- length = min(len(x_data), len(y_data))
+ length = min(len(x_data), len(y_data)) d = numpy.sqrt((x_data[:length,1]-event.xdata) ** 2 + \
(y_data[:length,1]-event.ydata) ** 2)
# Set cursor tick to the tick of this point
cursor_tick = x_data[numpy.argmin(d), 0]
data = items[0].GetData(start_tick, end_tick)
# Search for point that tick is the nearest from mouse X position
# and set cursor tick to the tick of this point
cursor_tick = data[numpy.argmin(
numpy.abs(data[:,0] - event.xdata)), 0]
if cursor_tick is not None:
self.ParentWindow.SetCursorTick(cursor_tick)
def OnCanvasButtonPressed(self, event):
Function called when a button of mouse is pressed
@@ -613,18 +613,18 @@
width, height = self.GetSize()
x, y = event.x, height - event.y
# Return immediately if mouse is over a button
if self.IsOverButton(x, y):
# Mouse was clicked inside graph figure
if event.inaxes == self.Axes:
# Find if it was on an item label
# Check every label paired with corresponding item
- for i, t in ([pair for pair in enumerate(self.AxesLabels)] +
+ for i, t in ([pair for pair in enumerate(self.AxesLabels)] + [pair for pair in enumerate(self.Labels)]):
(x0, y0), (x1, y1) = t.get_window_extent().get_points()
@@ -633,46 +633,46 @@
# If an item label have been clicked
# Hide buttons and contextual buttons
self.DismissContextualButtons()
# Start a drag'n drop from mouse position in wx coordinate of
xw, yw = self.GetPosition()
- self.ParentWindow.StartDragNDrop(self,
- self.ItemsDict.values()[item_idx],
+ self.ParentWindow.StartDragNDrop(self, + self.ItemsDict.values()[item_idx], x + xw, y + yw, # Current mouse position
x + xw, y + yw) # Mouse position when button was clicked
# Don't handle mouse button if canvas is 3D and let matplotlib do
# the default behavior (rotate 3D axes)
elif not self.Is3DCanvas():
# Save mouse position when clicked
self.MouseStartPos = wx.Point(x, y)
# Mouse button was left button, start moving cursor
# Save current tick in case a drag'n drop is initiate to
self.StartCursorTick = self.CursorTick
self.HandleCursorMove(event)
# Mouse button is middle button and graph is parallel, start
# moving graph along X coordinate (tick)
elif event.button == 2 and self.GraphType == GRAPH_PARALLEL:
self.StartCursorTick = self.ParentWindow.GetRange()[0]
# Mouse was clicked outside graph figure and over resize highlight with
# left button, start resizing Viewer
elif event.button == 1 and event.y <= 5:
self.MouseStartPos = wx.Point(x, y)
self.CanvasStartSize = height
def OnCanvasButtonReleased(self, event):
Function called when a button of mouse is released
@@ -686,37 +686,37 @@
# Give mouse position in wx coordinate of parent
self.ParentWindow.StopDragNDrop(item.GetVariable(),
xw + event.x, yw + height - event.y)
# Reset any move in progress
self.MouseStartPos = None
self.CanvasStartSize = None
# Handle button under mouse if it exist
width, height = self.GetSize()
self.HandleButton(event.x, height - event.y)
def OnCanvasMotion(self, event):
Function called when a button of mouse is moved over Viewer
@param event: Mouse event
width, height = self.GetSize()
# If a drag'n drop is in progress, move canvas dragged
if self.ParentWindow.IsDragging():
xw, yw = self.GetPosition()
# Give mouse position in wx coordinate of parent
self.ParentWindow.MoveDragNDrop(
- # If a Viewer resize is in progress, change Viewer size
+ # If a Viewer resize is in progress, change Viewer size elif event.button == 1 and self.CanvasStartSize is not None:
width, height = self.GetSize()
self.CanvasStartSize + height - event.y - self.MouseStartPos.y)
# If no button is pressed, show or hide contextual buttons or resize
elif event.button is None:
@@ -729,13 +729,13 @@
wx.LEFT, wx.BOTTOM] # Directions for Labels
else: # Graph is orthogonal in 3D
directions = [wx.LEFT] * len(self.Labels)
# Find if mouse is over an item label
- [pair for pair in enumerate(self.AxesLabels)] +
- [pair for pair in enumerate(self.Labels)],
+ [pair for pair in enumerate(self.AxesLabels)] + + [pair for pair in enumerate(self.Labels)], # Check every label paired with corresponding item
(x0, y0), (x1, y1) = t.get_window_extent().get_points()
@@ -745,19 +745,19 @@
- # If mouse is over an item label,
+ # If mouse is over an item label, self.PopupContextualButtons(
- self.ItemsDict.values()[item_idx],
+ self.ItemsDict.values()[item_idx],
# If mouse isn't over a contextual menu, hide the current shown one
if self.IsOverContextualButton(event.x, height - event.y) is None:
self.DismissContextualButtons()
# Update resize highlight
if self.SetHighlight(HIGHLIGHT_RESIZE):
@@ -767,46 +767,46 @@
if self.SetHighlight(HIGHLIGHT_NONE):
self.SetCursor(wx.NullCursor)
self.ParentWindow.ForceRefresh()
- # Handle buttons if canvas is not 3D
+ # Handle buttons if canvas is not 3D elif not self.Is3DCanvas():
# If left button is pressed
# Mouse is inside graph figure
if event.inaxes == self.Axes:
# If a cursor move is in progress, update cursor position
if self.MouseStartPos is not None:
self.HandleCursorMove(event)
# Mouse is outside graph figure, cursor move is in progress and
# there is only one item in Viewer, start a drag'n drop
elif self.MouseStartPos is not None and len(self.Items) == 1:
xw, yw = self.GetPosition()
self.ParentWindow.SetCursorTick(self.StartCursorTick)
- self.ParentWindow.StartDragNDrop(self,
+ self.ParentWindow.StartDragNDrop(self, self.ItemsDict.values()[0],
event.x + xw, height - event.y + yw,
# Mouse position when button was clicked
self.MouseStartPos.x + xw,
self.MouseStartPos.y + yw)
# If middle button is pressed and moving graph along X coordinate
elif event.button == 2 and self.GraphType == GRAPH_PARALLEL and \
self.MouseStartPos is not None:
start_tick, end_tick = self.ParentWindow.GetRange()
rect = self.GetAxesBoundingBox()
# Move graph along X coordinate
self.ParentWindow.SetCanvasPosition(
(self.MouseStartPos.x - event.x) *
(end_tick - start_tick) / rect.width)
def OnCanvasScroll(self, event):
Function called when a wheel mouse is use in Viewer
@@ -815,7 +815,7 @@
# Change X range of graphs if mouse is in canvas figure and ctrl is
if event.inaxes is not None and event.guiEvent.ControlDown():
# Calculate position of fixed tick point according to graph type
if self.GraphType == GRAPH_ORTHOGONAL:
@@ -824,10 +824,10 @@
self.ParentWindow.ChangeRange(int(-event.step) / 3, tick)
# Vetoing event to prevent parent panel to be scrolled
self.ParentWindow.VetoScrollEvent = True
def OnLeftDClick(self, event):
Function called when a left mouse button is double clicked
@@ -841,17 +841,17 @@
self.ParentWindow.SetCursorTick(self.StartCursorTick)
# Toggle to text Viewer(s)
self.ParentWindow.ToggleViewerType(self)
# Cursor tick move for each arrow key
def OnKeyDown(self, event):
Function called when key is pressed
@@ -863,7 +863,7 @@
self.ParentWindow.MoveCursorTick(move)
def OnLeave(self, event):
Function called when mouse leave Viewer
@@ -876,7 +876,7 @@
DebugVariableViewer.OnLeave(self, event)
def GetCanvasMinSize(self):
Return the minimum size of Viewer so that all items label can be
@@ -885,10 +885,10 @@
# The minimum height take in account the height of all items, padding
# inside figure and border around figure
- CANVAS_BORDER[0] + CANVAS_BORDER[1] +
+ CANVAS_BORDER[0] + CANVAS_BORDER[1] + 2 * CANVAS_PADDING + VALUE_LABEL_HEIGHT * len(self.Items))
def SetCanvasHeight(self, height):
Set Viewer size checking that it respects Viewer minimum size
@@ -899,7 +899,7 @@
self.SetMinSize(wx.Size(min_width, height))
self.RefreshLabelsPosition(height)
self.ParentWindow.RefreshGraphicsSizer()
def GetAxesBoundingBox(self, parent_coordinate=False):
Return figure bounding box in wx coordinate
@@ -911,15 +911,15 @@
ax, ay, aw, ah = self.figure.gca().get_position().bounds
bbox = wx.Rect(ax * width, height - (ay + ah) * height - 1,
aw * width + 2, ah * height + 1)
# If parent_coordinate, add Viewer position in parent
xw, yw = self.GetPosition()
def RefreshHighlight(self, x, y):
Refresh Viewer highlight according to mouse position
@@ -927,7 +927,7 @@
@param y: Y coordinate of mouse pointer
width, height = self.GetSize()
# Mouse is over Viewer figure and graph is not 3D
bbox = self.GetAxesBoundingBox()
if bbox.InsideXY(x, y) and not self.Is3DCanvas():
@@ -935,28 +935,28 @@
# Mouse is over Viewer left part of figure
self.SetHighlight(HIGHLIGHT_LEFT)
# Mouse is over Viewer right part of figure
self.SetHighlight(HIGHLIGHT_RIGHT)
# Mouse is over upper part of Viewer
# Viewer is upper one in Debug Variable Panel, show highlight
if self.ParentWindow.IsViewerFirst(self):
self.SetHighlight(HIGHLIGHT_BEFORE)
# Viewer is not the upper one, show highlight in previous one
# It prevents highlight to move when mouse leave one Viewer to
self.SetHighlight(HIGHLIGHT_NONE)
self.ParentWindow.HighlightPreviousViewer(self)
# Mouse is over lower part of Viewer
self.SetHighlight(HIGHLIGHT_AFTER)
def OnAxesMotion(self, event):
Function overriding default function called when mouse is dragged for
@@ -969,7 +969,7 @@
if current_time - self.LastMotionTime > REFRESH_PERIOD:
self.LastMotionTime = current_time
Axes3D._on_move(self.Axes, event)
def GetAddTextFunction(self):
Return function for adding text in figure according to graph type
@@ -987,58 +987,58 @@
self.Axes.set_prop_cycle(cycler('color',color))
self.Axes.set_color_cycle(color)
Reset figure and graphical elements displayed in it
- Called any time list of items or graph type change
+ Called any time list of items or graph type change # Clear figure from any axes defined
# Add 3D projection if graph is in 3D
self.Axes = self.Figure.gca(projection='3d')
- # Override function to prevent too much refresh when graph is
+ # Override function to prevent too much refresh when graph is self.LastMotionTime = gettime()
setattr(self.Axes, "_on_move", self.OnAxesMotion)
# Init graph mouse event so that graph can be rotated
# Set size of Z axis labels
self.Axes.tick_params(axis='z', labelsize='small')
self.Axes = self.Figure.gca()
self.SetAxesColor(COLOR_CYCLE)
# Set size of X and Y axis labels
self.Axes.tick_params(axis='x', labelsize='small')
self.Axes.tick_params(axis='y', labelsize='small')
# Init variables storing graphical elements added to figure
self.Plots = [] # List of curves
self.VLine = None # Vertical line for cursor
self.HLine = None # Horizontal line for cursor (only orthogonal 2D)
self.AxesLabels = [] # List of items variable path text label
self.Labels = [] # List of items text label
- # Get function to add a text in figure according to graph type
+ # Get function to add a text in figure according to graph type add_text_func = self.GetAddTextFunction()
# Graph type is parallel or orthogonal in 3D
if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
num_item = len(self.Items)
for idx in xrange(num_item):
# Get color from color cycle (black if only one item)
color = ('k' if num_item == 1
else COLOR_CYCLE[idx % len(COLOR_CYCLE)])
# In 3D graph items variable label are not displayed as text
# in figure, but as axis title
if not self.Is3DCanvas():
@@ -1046,12 +1046,12 @@
add_text_func(size='small', color=color,
verticalalignment='top'))
# Items variable labels are in figure lower right corner
- add_text_func(size='large', color=color,
+ add_text_func(size='large', color=color, horizontalalignment='right'))
# Graph type is orthogonal in 2D
# X coordinate labels are in figure lower side
@@ -1059,7 +1059,7 @@
add_text_func(size='large',
horizontalalignment='right'))
# Y coordinate labels are vertical and in figure left side
add_text_func(size='small', rotation='vertical',
@@ -1067,11 +1067,11 @@
add_text_func(size='large', rotation='vertical',
verticalalignment='top'))
# Refresh position of labels according to Viewer size
width, height = self.GetSize()
self.RefreshLabelsPosition(height)
def RefreshLabelsPosition(self, height):
Function called when mouse leave Viewer
@@ -1087,30 +1087,30 @@
(1.0 - (CANVAS_BORDER[0] + CANVAS_BORDER[1]) * canvas_ratio)
* height) # Divide by figure height in pixel
# Update position of figure (keeping up and bottom border the same
self.Figure.subplotpars.update(
- top= 1.0 - CANVAS_BORDER[1] * canvas_ratio,
+ top= 1.0 - CANVAS_BORDER[1] * canvas_ratio, bottom= CANVAS_BORDER[0] * canvas_ratio)
# Update position of items labels
if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
num_item = len(self.Items)
for idx in xrange(num_item):
# In 3D graph items variable label are not displayed
if not self.Is3DCanvas():
# Items variable labels are in figure upper left corner
self.AxesLabels[idx].set_position(
- 1.0 - (CANVAS_PADDING +
+ 1.0 - (CANVAS_PADDING + AXES_LABEL_HEIGHT * idx) * graph_ratio))
# Items variable labels are in figure lower right corner
self.Labels[idx].set_position(
- CANVAS_PADDING * graph_ratio +
+ CANVAS_PADDING * graph_ratio + (num_item - idx - 1) * VALUE_LABEL_HEIGHT * graph_ratio))
# X coordinate labels are in figure lower side
@@ -1118,16 +1118,16 @@
(0.1, CANVAS_PADDING * graph_ratio))
self.Labels[0].set_position(
(0.95, CANVAS_PADDING * graph_ratio))
# Y coordinate labels are vertical and in figure left side
self.AxesLabels[1].set_position(
(0.05, 2 * CANVAS_PADDING * graph_ratio))
self.Labels[1].set_position(
(0.05, 1.0 - CANVAS_PADDING * graph_ratio))
self.Figure.subplots_adjust()
def RefreshViewer(self, refresh_graphics=True):
Function called to refresh displayed by matplotlib canvas
@@ -1138,55 +1138,55 @@
# Get tick range of values to display
start_tick, end_tick = self.ParentWindow.GetRange()
- if self.GraphType == GRAPH_PARALLEL:
+ if self.GraphType == GRAPH_PARALLEL: # Init list of data range for each variable displayed
# Get data and range for each variable displayed
for idx, item in enumerate(self.Items):
data, min_value, max_value = item.GetDataAndValueRange(
start_tick, end_tick, not self.ZoomFit)
# Check that data is not empty
# Add variable range to list of variable data range
ranges.append((min_value, max_value))
# Add plot to canvas if not yet created
if len(self.Plots) <= idx:
self.Axes.plot(data[:, 0], data[:, 1])[0])
# Set data to already created plot in canvas
self.Plots[idx].set_data(data[:, 0], data[:, 1])
# Get X and Y axis ranges
x_min, x_max = start_tick, end_tick
y_min, y_max = merge_ranges(ranges)
# Display cursor in canvas if a cursor tick is defined and it is
# include in values tick range
- if (self.CursorTick is not None and
+ if (self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick):
# Define a vertical line to display cursor position if no
# line is already defined
- self.VLine = self.Axes.axvline(self.CursorTick,
+ self.VLine = self.Axes.axvline(self.CursorTick,
# Set value of vertical line if already defined
self.VLine.set_xdata((self.CursorTick, self.CursorTick))
self.VLine.set_visible(True)
# Hide vertical line if cursor tick is not defined or reset
elif self.VLine is not None:
self.VLine.set_visible(False)
# Update tick range, removing ticks that don't have a value for
@@ -1194,76 +1194,76 @@
start_tick = max(start_tick, self.GetItemsMinCommonTick())
end_tick = max(end_tick, start_tick)
items = self.ItemsDict.values()
# Get data and range for first variable (X coordinate)
x_data, x_min, x_max = items[0].GetDataAndValueRange(
start_tick, end_tick, not self.ZoomFit)
# Get data and range for second variable (Y coordinate)
y_data, y_min, y_max = items[1].GetDataAndValueRange(
start_tick, end_tick, not self.ZoomFit)
# Normalize X and Y coordinates value range
x_min, x_max = merge_ranges([(x_min, x_max)])
y_min, y_max = merge_ranges([(y_min, y_max)])
- # Get X and Y coordinates for cursor if cursor tick is defined
+ # Get X and Y coordinates for cursor if cursor tick is defined if self.CursorTick is not None:
x_cursor, x_forced = items[0].GetValue(
self.CursorTick, raw=True)
y_cursor, y_forced = items[1].GetValue(
self.CursorTick, raw=True)
# Get common data length so that each value has an x and y
length = (min(len(x_data), len(y_data))
if x_data is not None and y_data is not None
- # Graph is orthogonal 2D
+ # Graph is orthogonal 2D
# Check that x and y data are not empty
if x_data is not None and y_data is not None:
# Add plot to canvas if not yet created
- self.Axes.plot(x_data[:, 1][:length],
+ self.Axes.plot(x_data[:, 1][:length], y_data[:, 1][:length])[0])
# Set data to already created plot in canvas
# Display cursor in canvas if a cursor tick is defined and it is
# include in values tick range
- if (self.CursorTick is not None and
+ if (self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick):
# Define a vertical line to display cursor x coordinate
# if no line is already defined
- self.VLine = self.Axes.axvline(x_cursor,
+ self.VLine = self.Axes.axvline(x_cursor, # Set value of vertical line if already defined
self.VLine.set_xdata((x_cursor, x_cursor))
# Define a horizontal line to display cursor y
# coordinate if no line is already defined
- self.HLine = self.Axes.axhline(y_cursor,
+ self.HLine = self.Axes.axhline(y_cursor, # Set value of horizontal line if already defined
self.HLine.set_ydata((y_cursor, y_cursor))
self.VLine.set_visible(True)
self.HLine.set_visible(True)
# Hide vertical and horizontal line if cursor tick is not
@@ -1271,42 +1271,42 @@
self.VLine.set_visible(False)
if self.HLine is not None:
self.HLine.set_visible(False)
# Remove all plots already defined in 3D canvas
while len(self.Axes.lines) > 0:
# Get data and range for third variable (Z coordinate)
z_data, z_min, z_max = items[2].GetDataAndValueRange(
start_tick, end_tick, not self.ZoomFit)
# Normalize Z coordinate value range
z_min, z_max = merge_ranges([(z_min, z_max)])
# Check that x, y and z data are not empty
- if (x_data is not None and y_data is not None and
+ if (x_data is not None and y_data is not None and
# Get common data length so that each value has an x, y
length = min(length, len(z_data))
self.Axes.plot(x_data[:, 1][:length],
zs = z_data[:, 1][:length])
# Display cursor in canvas if a cursor tick is defined and
# it is include in values tick range
- if (self.CursorTick is not None and
+ if (self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick):
# Get Z coordinate for cursor
z_cursor, z_forced = items[2].GetValue(
self.CursorTick, raw=True)
# Add 3 lines parallel to x, y and z axis to display
for kwargs in [{"xs": numpy.array([x_min, x_max])},
@@ -1319,14 +1319,14 @@
kwargs.setdefault(param, value)
kwargs["color"] = CURSOR_COLOR
self.Axes.set_zlim(z_min, z_max)
# Set X and Y axis limits
self.Axes.set_xlim(x_min, x_max)
self.Axes.set_ylim(y_min, y_max)
# Get value and forced flag for each variable displayed in graph
# If cursor tick is not defined get value and flag of last received
# or get value and flag of variable at cursor tick
@@ -1335,34 +1335,34 @@
if self.CursorTick is not None
else (item.GetValue(), item.IsForced()))
# Get path of each variable displayed simplified using panel variable
- labels = [item.GetVariable(self.ParentWindow.GetVariableNameMask())
+ labels = [item.GetVariable(self.ParentWindow.GetVariableNameMask())
- # Get style for each variable according to
+ # Get style for each variable according to styles = map(lambda x: {True: 'italic', False: 'normal'}[x], forced)
# Graph is orthogonal 3D, set variables path as 3D axis label
- for idx, label_func in enumerate([self.Axes.set_xlabel,
+ for idx, label_func in enumerate([self.Axes.set_xlabel, label_func(labels[idx], fontdict={'size': 'small',
'color': COLOR_CYCLE[idx]})
# Graph is not orthogonal 3D, set variables path in axes labels
for label, text in zip(self.AxesLabels, labels):
# Set value label text and style according to value and forced flag for
# each variable displayed
for label, value, style in zip(self.Labels, values, styles):
@@ -1372,51 +1372,49 @@
# Render figure using agg
FigureCanvasAgg.draw(self)
# Get bitmap of figure rendered
self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None)
- if wx.VERSION < (3, 0, 0):
+ if wx.VERSION < (3, 0, 0):
# Create DC for rendering graphics in bitmap
destDC.SelectObject(self.bitmap)
# Get Graphics Context for DC, for anti-aliased and transparent
# Get canvas size and figure bounding box in canvas
width, height = self.GetSize()
bbox = self.GetAxesBoundingBox()
# If highlight to display is resize, draw thick grey line at bottom
if self.Highlight == HIGHLIGHT_RESIZE:
destGC.SetPen(HIGHLIGHT_RESIZE_PEN)
destGC.SetBrush(HIGHLIGHT_RESIZE_BRUSH)
destGC.DrawRectangle(0, height - 5, width, 5)
# If highlight to display is merging graph, draw 50% transparent blue
# rectangle on left or right part of figure depending on highlight type
elif self.Highlight in [HIGHLIGHT_LEFT, HIGHLIGHT_RIGHT]:
destGC.SetPen(HIGHLIGHT_DROP_PEN)
destGC.SetBrush(HIGHLIGHT_DROP_BRUSH)
- x_offset = (bbox.width / 2
+ x_offset = (bbox.width / 2 if self.Highlight == HIGHLIGHT_RIGHT
- destGC.DrawRectangle(bbox.x + x_offset, bbox.y,
+ destGC.DrawRectangle(bbox.x + x_offset, bbox.y, bbox.width / 2, bbox.height)
# Draw other Viewer common elements
self.DrawCommonElements(destGC, self.GetButtons())
self.gui_repaint(drawDC=drawDC)
--- a/controls/DebugVariablePanel/DebugVariablePanel.py Fri Aug 11 15:18:19 2017 +0300
+++ b/controls/DebugVariablePanel/DebugVariablePanel.py Mon Aug 14 19:13:01 2017 +0300
@@ -67,11 +67,11 @@
for item, data in variables:
else min(next_tick, data[0][0]))
#-------------------------------------------------------------------------------
@@ -84,7 +84,7 @@
class DebugVariableDropTarget(wx.TextDropTarget):
def __init__(self, window):
@@ -92,14 +92,14 @@
wx.TextDropTarget.__init__(self)
self.ParentWindow = window
# Remove reference to Debug Variable Panel
def OnDragOver(self, x, y, d):
Function called when mouse is dragged over Drop Target
@@ -110,7 +110,7 @@
# Signal Debug Variable Panel to refresh highlight giving mouse position
self.ParentWindow.RefreshHighlight(x, y)
return wx.TextDropTarget.OnDragOver(self, x, y, d)
def OnDropText(self, x, y, data):
Function called when mouse is released in Drop Target
@@ -120,9 +120,9 @@
# Signal Debug Variable Panel to reset highlight
self.ParentWindow.ResetHighlight()
# Check that data is valid regarding DebugVariablePanel
@@ -131,23 +131,23 @@
message = _("Invalid value \"%s\" for debug variable")%data
# Display message if data is invalid
wx.CallAfter(self.ShowMessage, message)
# Data contain a reference to a variable to debug
elif values[1] == "debug":
# Drag'n Drop is an internal is an internal move inside Debug
if len(values) > 2 and values[2] == "move":
self.ParentWindow.MoveValue(values[0])
# Drag'n Drop was initiated by another control of Beremiz
self.ParentWindow.InsertValue(values[0], force=True)
Function called when mouse is leave Drop Target
@@ -155,15 +155,15 @@
# Signal Debug Variable Panel to reset highlight
self.ParentWindow.ResetHighlight()
return wx.TextDropTarget.OnLeave(self)
def ShowMessage(self, message):
Show error message in Error Dialog
@param message: Error message to display
- dialog = wx.MessageDialog(self.ParentWindow,
+ dialog = wx.MessageDialog(self.ParentWindow, @@ -178,7 +178,7 @@
class DebugVariablePanel(wx.Panel, DebugViewer):
def __init__(self, parent, producer, window):
@@ -188,48 +188,48 @@
@param window: Reference to Beremiz frame
wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL)
# Save Reference to Beremiz frame
self.ParentWindow = window
# Variable storing flag indicating that variable displayed in table
# received new value and then table need to be refreshed
# Variable storing flag indicating that refresh has been forced, and
# that next time refresh is possible, it will be done even if no new
self.SetBackgroundColour(wx.WHITE)
main_sizer = wx.BoxSizer(wx.VERTICAL)
self.Ticks = numpy.array([]) # List of tick received
self.StartTick = 0 # Tick starting range of data displayed
self.Fixed = False # Flag that range of data is fixed
self.CursorTick = None # Tick of cursor for displaying values
self.DraggingAxesPanel = None
self.DraggingAxesBoundingBox = None
self.DraggingAxesMousePos = None
self.VetoScrollEvent = False
self.VariableNameMask = []
graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL)
range_label = wx.StaticText(self, label=_('Range:'))
graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL)
self.CanvasRange = wx.ComboBox(self, style=wx.CB_READONLY)
self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange)
- graphics_button_sizer.AddWindow(self.CanvasRange, 1,
+ graphics_button_sizer.AddWindow(self.CanvasRange, 1, border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
for idx, (text, value) in enumerate(RANGE_VALUES):
@@ -237,44 +237,44 @@
self.CanvasRange.SetSelection(default_range_idx)
for name, bitmap, help in [
("CurrentButton", "current", _("Go to current value")),
("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]:
- button = wx.lib.buttons.GenBitmapButton(self,
- bitmap=GetBitmap(bitmap),
+ button = wx.lib.buttons.GenBitmapButton(self, + bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER)
button.SetToolTipString(help)
setattr(self, name, button)
self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
- self.CanvasPosition = wx.ScrollBar(self,
+ self.CanvasPosition = wx.ScrollBar(self, size=wx.Size(0, 16), style=wx.SB_HORIZONTAL)
- self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK,
+ self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, self.OnPositionChanging, self.CanvasPosition)
- self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP,
+ self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, self.OnPositionChanging, self.CanvasPosition)
- self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN,
+ self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN, self.OnPositionChanging, self.CanvasPosition)
- self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP,
+ self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, self.OnPositionChanging, self.CanvasPosition)
- self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN,
+ self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, self.OnPositionChanging, self.CanvasPosition)
main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM)
self.TickSizer = wx.BoxSizer(wx.HORIZONTAL)
main_sizer.AddSizer(self.TickSizer, border=5, flag=wx.ALL|wx.GROW)
self.TickLabel = wx.StaticText(self)
self.TickSizer.AddWindow(self.TickLabel, border=5, flag=wx.RIGHT)
self.MaskLabel = wx.TextCtrl(self, style=wx.TE_READONLY|wx.TE_CENTER|wx.NO_BORDER)
self.TickSizer.AddWindow(self.MaskLabel, 1, border=5, flag=wx.RIGHT|wx.GROW)
self.TickTimeLabel = wx.StaticText(self)
self.TickSizer.AddWindow(self.TickTimeLabel)
self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL|wx.VSCROLL)
self.GraphicsWindow.SetBackgroundColour(wx.WHITE)
self.GraphicsWindow.SetDropTarget(DebugVariableDropTarget(self))
@@ -282,17 +282,17 @@
self.GraphicsWindow.Bind(wx.EVT_PAINT, self.OnGraphicsWindowPaint)
self.GraphicsWindow.Bind(wx.EVT_SIZE, self.OnGraphicsWindowResize)
self.GraphicsWindow.Bind(wx.EVT_MOUSEWHEEL, self.OnGraphicsWindowMouseWheel)
main_sizer.AddWindow(self.GraphicsWindow, 1, flag=wx.GROW)
self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL)
self.GraphicsWindow.SetSizer(self.GraphicsSizer)
DebugViewer.__init__(self, producer, True)
self.SetSizer(main_sizer)
def SetTickTime(self, ticktime=0):
Set Ticktime for calculate data range according to time range selected
@@ -300,26 +300,26 @@
# Set ticktime to millisecond if undefined
self.Ticktime = MILLISECOND
# Calculate range to apply to data
self.CurrentRange = RANGE_VALUES[
self.CanvasRange.GetSelection()][1] / self.Ticktime
def SetDataProducer(self, producer):
@param producer: Data Producer
DebugViewer.SetDataProducer(self, producer)
# Set ticktime if data producer is available
if self.DataProducer is not None:
self.SetTickTime(self.DataProducer.GetTicktime())
def RefreshNewData(self):
Called to refresh Panel according to values received by variables
@@ -329,50 +329,50 @@
if self.HasNewData or self.Force:
DebugViewer.RefreshNewData(self)
def NewDataAvailable(self, ticks):
Called by DataProducer for each tick captured or by panel to refresh
@param tick: PLC tick captured
- All other parameters are passed to refresh function
+ All other parameters are passed to refresh function
# Save tick as start tick for range if data is still empty
self.StartTick = ticks[0]
# Add tick to list of ticks received
self.Ticks = numpy.append(self.Ticks, ticks)
# Update start tick for range if range follow ticks received
if not self.Fixed or tick < self.StartTick + self.CurrentRange:
self.StartTick = max(self.StartTick, tick - self.CurrentRange)
# Force refresh if graph is fixed because range of data received
# is too small to fill data range selected
self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
DebugViewer.NewDataAvailable(self, ticks)
Called to force refresh of graphs
wx.CallAfter(self.NewDataAvailable, None)
def SetCursorTick(self, cursor_tick):
Set Cursor for displaying values of items at a tick given
@@ -381,37 +381,37 @@
self.CursorTick = cursor_tick
self.Fixed = cursor_tick is not None
- self.UpdateCursorTick()
+ self.UpdateCursorTick() def MoveCursorTick(self, move):
if self.CursorTick is not None:
- cursor_tick = max(self.Ticks[0],
- min(self.CursorTick + move,
+ cursor_tick = max(self.Ticks[0], + min(self.CursorTick + move, cursor_tick_idx = numpy.argmin(numpy.abs(self.Ticks - cursor_tick))
if self.Ticks[cursor_tick_idx] == self.CursorTick:
- cursor_tick_idx = max(0,
- min(cursor_tick_idx + abs(move) / move,
+ cursor_tick_idx = max(0, + min(cursor_tick_idx + abs(move) / move, self.CursorTick = self.Ticks[cursor_tick_idx]
self.StartTick = max(self.Ticks[
- numpy.argmin(numpy.abs(self.Ticks -
+ numpy.argmin(numpy.abs(self.Ticks - self.CursorTick + self.CurrentRange))],
min(self.StartTick, self.CursorTick))
self.RefreshCanvasPosition()
- self.UpdateCursorTick()
+ self.UpdateCursorTick() def ResetCursorTick(self):
def UpdateCursorTick(self):
for panel in self.GraphicPanels:
if isinstance(panel, DebugVariableGraphicViewer):
panel.SetCursorTick(self.CursorTick)
def StartDragNDrop(self, panel, item, x_mouse, y_mouse, x_mouse_start, y_mouse_start):
if len(panel.GetItems()) > 1:
self.DraggingAxesPanel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
@@ -424,21 +424,21 @@
self.DraggingAxesPanel = panel
self.DraggingAxesBoundingBox = panel.GetAxesBoundingBox(parent_coordinate=True)
self.DraggingAxesMousePos = wx.Point(
- x_mouse_start - self.DraggingAxesBoundingBox.x,
+ x_mouse_start - self.DraggingAxesBoundingBox.x, y_mouse_start - self.DraggingAxesBoundingBox.y)
self.MoveDragNDrop(x_mouse, y_mouse)
def MoveDragNDrop(self, x_mouse, y_mouse):
self.DraggingAxesBoundingBox.x = x_mouse - self.DraggingAxesMousePos.x
self.DraggingAxesBoundingBox.y = y_mouse - self.DraggingAxesMousePos.y
self.RefreshHighlight(x_mouse, y_mouse)
def RefreshHighlight(self, x_mouse, y_mouse):
for idx, panel in enumerate(self.GraphicPanels):
x, y = panel.GetPosition()
width, height = panel.GetSize()
rect = wx.Rect(x, y, width, height)
- if (rect.InsideXY(x_mouse, y_mouse) or
+ if (rect.InsideXY(x_mouse, y_mouse) or idx == 0 and y_mouse < 0 or
idx == len(self.GraphicPanels) - 1 and y_mouse > panel.GetPosition()[1]):
panel.RefreshHighlight(x_mouse - x, y_mouse - y)
@@ -448,7 +448,7 @@
def ResetHighlight(self):
for panel in self.GraphicPanels:
panel.SetHighlight(HIGHLIGHT_NONE)
@@ -456,10 +456,10 @@
return self.DraggingAxesPanel is not None
def GetDraggingAxesClippingRegion(self, panel):
x, y = panel.GetPosition()
width, height = panel.GetSize()
@@ -468,12 +468,12 @@
def GetDraggingAxesPosition(self, panel):
x, y = panel.GetPosition()
return wx.Point(self.DraggingAxesBoundingBox.x - x,
self.DraggingAxesBoundingBox.y - y)
def StopDragNDrop(self, variable, x_mouse, y_mouse):
if self.DraggingAxesPanel not in self.GraphicPanels:
self.DraggingAxesPanel.Destroy()
@@ -504,25 +504,25 @@
wx.CallAfter(self.MoveValue, variable, idx, True)
width, height = self.GraphicsWindow.GetVirtualSize()
rect = wx.Rect(0, 0, width, height)
if rect.InsideXY(x_mouse, y_mouse):
wx.CallAfter(self.MoveValue, variable, len(self.GraphicPanels), True)
def RefreshGraphicsSizer(self):
self.GraphicsSizer.Clear()
for panel in self.GraphicPanels:
self.GraphicsSizer.AddWindow(panel, flag=wx.GROW)
self.GraphicsSizer.Layout()
self.RefreshGraphicsWindowScrollbars()
self.RefreshCanvasPosition()
width, height = self.GraphicsWindow.GetVirtualSize()
bitmap = wx.EmptyBitmap(width, height)
dc = wx.BufferedDC(wx.ClientDC(self.GraphicsWindow), bitmap)
@@ -531,22 +531,22 @@
if self.DraggingAxesPanel is not None:
destBBox = self.DraggingAxesBoundingBox
srcBBox = self.DraggingAxesPanel.GetAxesBoundingBox()
srcBmp = _convert_agg_to_wx_bitmap(self.DraggingAxesPanel.get_renderer(), None)
srcDC.SelectObject(srcBmp)
- dc.Blit(destBBox.x, destBBox.y,
- int(destBBox.width), int(destBBox.height),
+ dc.Blit(destBBox.x, destBBox.y, + int(destBBox.width), int(destBBox.height), srcDC, srcBBox.x, srcBBox.y)
if not self.Fixed or self.Force:
if self.DraggingAxesPanel is not None and self.DraggingAxesPanel not in self.GraphicPanels:
self.DraggingAxesPanel.RefreshViewer(refresh_graphics)
for panel in self.GraphicPanels:
@@ -554,7 +554,7 @@
panel.RefreshViewer(refresh_graphics)
if self.CursorTick is not None:
elif len(self.Ticks) > 0:
@@ -570,27 +570,27 @@
((tick_duration % DAY) / HOUR, _("%dh")),
((tick_duration % HOUR) / MINUTE, _("%dm")),
((tick_duration % MINUTE) / SECOND, _("%ds"))]:
if value > 0 or not_null:
duration += format % value
- duration += _("%03gms") % (float(tick_duration % SECOND) / MILLISECOND)
+ duration += _("%03gms") % (float(tick_duration % SECOND) / MILLISECOND) self.TickTimeLabel.SetLabel("t: %s" % duration)
self.TickLabel.SetLabel("")
self.TickTimeLabel.SetLabel("")
def SubscribeAllDataConsumers(self):
DebugViewer.SubscribeAllDataConsumers(self)
if self.DataProducer is not None:
if self.DataProducer is not None:
self.SetTickTime(self.DataProducer.GetTicktime())
for panel in self.GraphicPanels[:]:
panel.SubscribeAllDataConsumers()
@@ -598,28 +598,28 @@
self.GraphicPanels.remove(panel)
self.ResetVariableNameMask()
self.RefreshGraphicsSizer()
self.UnsubscribeAllDataConsumers()
for panel in self.GraphicPanels:
self.ResetVariableNameMask()
self.RefreshGraphicsSizer()
def SetCanvasPosition(self, tick):
tick = max(self.Ticks[0], min(tick, self.Ticks[-1] - self.CurrentRange))
self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - tick))]
self.RefreshCanvasPosition()
def RefreshCanvasPosition(self):
pos = int(self.StartTick - self.Ticks[0])
@@ -628,7 +628,7 @@
self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange)
def ChangeRange(self, dir, tick=None):
current_range = self.CurrentRange
current_range_idx = self.CanvasRange.GetSelection()
@@ -644,7 +644,7 @@
self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - new_start_tick))]
self.Fixed = new_start_tick < self.Ticks[-1] - self.CurrentRange
if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
@@ -654,7 +654,7 @@
self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
def OnRangeChanged(self, event):
self.CurrentRange = RANGE_VALUES[self.CanvasRange.GetSelection()][1] / self.Ticktime
@@ -662,13 +662,13 @@
self.CanvasRange.SetValue(str(self.CurrentRange))
wx.CallAfter(self.RefreshRange)
def OnCurrentButton(self, event):
self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
def CopyDataToClipboard(self, variables):
text = "tick;%s;\n" % ";".join([item.GetVariable() for item, data in variables])
next_tick = NextTick(variables)
@@ -693,7 +693,7 @@
text += "%d;%s;\n" % (next_tick, ";".join(values))
next_tick = NextTick(variables)
self.ParentWindow.SetCopyBuffer(text)
def OnExportGraphButton(self, event):
items = reduce(lambda x, y: x + y,
[panel.GetItems() for panel in self.GraphicPanels],
@@ -703,25 +703,25 @@
wx.CallAfter(self.CopyDataToClipboard, variables)
def OnPositionChanging(self, event):
self.StartTick = self.Ticks[0] + event.GetPosition()
return self.StartTick, self.StartTick + self.CurrentRange
def GetViewerIndex(self, viewer):
if viewer in self.GraphicPanels:
return self.GraphicPanels.index(viewer)
def IsViewerFirst(self, viewer):
return viewer == self.GraphicPanels[0]
def HighlightPreviousViewer(self, viewer):
if self.IsViewerFirst(viewer):
@@ -729,7 +729,7 @@
self.GraphicPanels[idx-1].SetHighlight(HIGHLIGHT_AFTER)
def ResetVariableNameMask(self):
for panel in self.GraphicPanels:
@@ -743,10 +743,10 @@
self.VariableNameMask = []
self.MaskLabel.ChangeValue(".".join(self.VariableNameMask))
self.MaskLabel.SetInsertionPoint(self.MaskLabel.GetLastPosition())
def GetVariableNameMask(self):
return self.VariableNameMask
def InsertValue(self, iec_path, idx = None, force=False, graph=False):
for panel in self.GraphicPanels:
if panel.GetItem(iec_path) is not None:
@@ -758,7 +758,7 @@
item = DebugVariableItem(self, iec_path, True)
result = self.AddDataConsumer(iec_path.upper(), item, True)
if result is not None or force:
if item.IsNumVariable() and graph:
panel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
@@ -774,7 +774,7 @@
self.RefreshGraphicsSizer()
def MoveValue(self, iec_path, idx = None, graph=False):
idx = len(self.GraphicPanels)
@@ -787,9 +787,9 @@
if source_panel is not None:
source_panel_idx = self.GraphicPanels.index(source_panel)
if (len(source_panel.GetItems()) == 1):
if source_panel_idx < idx:
self.GraphicPanels.insert(idx, source_panel)
self.GraphicPanels.pop(source_panel_idx)
@@ -798,7 +798,7 @@
self.GraphicPanels.insert(idx, source_panel)
source_panel.RemoveItem(item)
source_size = source_panel.GetSize()
@@ -807,22 +807,22 @@
panel.SetCanvasHeight(source_size.height)
if self.CursorTick is not None:
panel.SetCursorTick(self.CursorTick)
panel = DebugVariableTextViewer(self.GraphicsWindow, self, [item])
self.GraphicPanels.insert(idx, panel)
if source_panel.ItemsIsEmpty():
if source_panel.HasCapture():
source_panel.ReleaseMouse()
self.GraphicPanels.remove(source_panel)
self.ResetVariableNameMask()
self.RefreshGraphicsSizer()
def MergeGraphs(self, source, target_idx, merge_type, force=False):
@@ -846,11 +846,11 @@
graph_type = target_panel.GraphType
if target_panel != source_panel:
if (merge_type == GRAPH_PARALLEL and graph_type != merge_type or
- merge_type == GRAPH_ORTHOGONAL and
+ merge_type == GRAPH_ORTHOGONAL and (graph_type == GRAPH_PARALLEL and len(target_panel.Items) > 1 or
graph_type == GRAPH_ORTHOGONAL and len(target_panel.Items) >= 3)):
if source_panel is not None:
source_panel.RemoveItem(source_item)
if source_panel.ItemsIsEmpty():
@@ -862,7 +862,7 @@
target_panel.RemoveItem(source_item)
if target_panel is not None:
target_panel.AddItem(source_item)
target_panel.GraphType = merge_type
@@ -874,15 +874,15 @@
target_panel.SetCanvasHeight(size.height)
target_panel.ResetGraphics()
self.ResetVariableNameMask()
self.RefreshGraphicsSizer()
def DeleteValue(self, source_panel, item=None):
source_idx = self.GetViewerIndex(source_panel)
if source_idx is not None:
source_panel.ClearItems()
@@ -900,7 +900,7 @@
def ToggleViewerType(self, panel):
panel_idx = self.GetViewerIndex(panel)
if panel_idx is not None:
@@ -916,7 +916,7 @@
self.RefreshGraphicsSizer()
def ResetGraphicsValues(self):
self.Ticks = numpy.array([])
@@ -931,22 +931,22 @@
posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT))
posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT))
self.GraphicsWindow.Scroll(posx, posy)
- self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT,
+ self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, posx, posy)
def OnGraphicsWindowEraseBackground(self, event):
def OnGraphicsWindowPaint(self, event):
def OnGraphicsWindowResize(self, event):
for panel in self.GraphicPanels:
panel_size = panel.GetSize()
- if (isinstance(panel, DebugVariableGraphicViewer) and
- panel.GraphType == GRAPH_ORTHOGONAL and
+ if (isinstance(panel, DebugVariableGraphicViewer) and + panel.GraphType == GRAPH_ORTHOGONAL and panel_size.width == panel_size.height):
panel.SetCanvasHeight(size.width)
self.RefreshGraphicsWindowScrollbars()
--- a/controls/LibraryPanel.py Fri Aug 11 15:18:19 2017 +0300
+++ b/controls/LibraryPanel.py Mon Aug 14 19:13:01 2017 +0300
@@ -42,7 +42,7 @@
class LibraryPanel(wx.Panel):
def __init__(self, parent, enable_drag=False):
@@ -51,19 +51,19 @@
be drag'n drop from LibraryPanel (default: False)
wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
# Define LibraryPanel main sizer
main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
main_sizer.AddGrowableCol(0)
main_sizer.AddGrowableRow(1)
# Add SearchCtrl to main sizer
self.SearchCtrl = wx.SearchCtrl(self)
# Add a button with a magnifying glass, essentially to show that this
# control is for searching in tree
self.SearchCtrl.ShowSearchButton(True)
self.Bind(wx.EVT_TEXT, self.OnSearchCtrlChanged, self.SearchCtrl)
- self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN,
+ self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, self.OnSearchButtonClick, self.SearchCtrl)
# Bind keyboard event on SearchCtrl text control to catch UP and DOWN
# for search previous and next occurrence
@@ -74,16 +74,16 @@
search_textctrl.Bind(wx.EVT_CHAR, self.OnKeyDown)
main_sizer.AddWindow(self.SearchCtrl, flag=wx.GROW)
# Add Splitter window for tree and block comment to main sizer
splitter_window = wx.SplitterWindow(self)
splitter_window.SetSashGravity(1.0)
main_sizer.AddWindow(splitter_window, flag=wx.GROW)
# Add TreeCtrl for functions and function blocks library in splitter
self.Tree = wx.TreeCtrl(splitter_window,
@@ -95,35 +95,35 @@
# tree to start a drag'n drop
self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag, self.Tree)
# Add TextCtrl for function and function block informations
- self.Comment = wx.TextCtrl(splitter_window, size=wx.Size(0, 80),
+ self.Comment = wx.TextCtrl(splitter_window, size=wx.Size(0, 80), style=wx.TE_READONLY|wx.TE_MULTILINE)
splitter_window.SplitHorizontally(self.Tree, self.Comment, -80)
self.SetSizer(main_sizer)
# Reference to the project controller
# Variable storing functions and function blocks library to display
# Remove reference to project controller
def SetController(self, controller):
Set reference to project controller
@param controller: Reference to project controller
self.Controller = controller
def SetBlockList(self, blocklist):
Set function and function block library to display in TreeCtrl
@@ -133,15 +133,15 @@
self.BlockList = blocklist
# Refresh TreeCtrl values
Called to give focus to LibraryPanel
- Override wx.Window SetFocus method
+ Override wx.Window SetFocus method # Give focus to SearchCtrl
self.SearchCtrl.SetFocus()
Reset LibraryPanel values displayed in controls
@@ -150,7 +150,7 @@
self.SearchCtrl.SetValue("")
self.Tree.DeleteAllItems()
self.Comment.SetValue("")
Refresh LibraryPanel values displayed in controls
@@ -160,13 +160,13 @@
if blocktypes is None and self.Controller is not None:
# Get library from project controller if not defined
blocktypes = self.Controller.GetBlockTypes()
# Refresh TreeCtrl values if a library is defined
if blocktypes is not None:
# List that will contain tree items to be deleted when TreeCtrl
# Get current selected item for selected it when values refreshed
selected_item = self.Tree.GetSelection()
selected_pydata = (self.Tree.GetPyData(selected_item)
@@ -179,16 +179,16 @@
if selected_pydata is not None and
selected_pydata["type"] == BLOCK
# Get TreeCtrl root item (hidden)
root = self.Tree.GetRootItem()
# Create root if not present
root = self.Tree.AddRoot("")
# Iterate over functions and function blocks library categories and
# add a tree item to root item for each of them
# Get first child under root item
category_item, root_cookie = self.Tree.GetFirstChild(root)
for category in blocktypes:
@@ -196,11 +196,11 @@
# extracting translated strings for gettext to consider "name"
category_name = category["name"]
# Tree item already exists, set item label
self.Tree.SetItemText(category_item, _(category_name))
# Tree item doesn't exist, add new one to root
category_item = self.Tree.AppendItem(root, _(category_name))
@@ -209,24 +209,24 @@
if wx.Platform != '__WXMSW__':
category_item, root_cookie = \
self.Tree.GetNextChild(root, root_cookie)
- # Set data associated to tree item (only save that item is a
+ # Set data associated to tree item (only save that item is a self.Tree.SetPyData(category_item, {"type" : CATEGORY})
# Iterate over functions and function blocks defined in library
- # category add a tree item to category tree item for each of
+ # category add a tree item to category tree item for each of
# Get first child under category tree item
blocktype_item, category_cookie = \
self.Tree.GetFirstChild(category_item)
for blocktype in category["list"]:
# Tree item already exists, set item label
if blocktype_item.IsOk():
self.Tree.SetItemText(blocktype_item, blocktype["name"])
# Tree item doesn't exist, add new one to category item
blocktype_item = self.Tree.AppendItem(
@@ -234,58 +234,58 @@
# See comment when adding category
if wx.Platform != '__WXMSW__':
blocktype_item, category_cookie = \
- self.Tree.GetNextChild(category_item,
+ self.Tree.GetNextChild(category_item,
# Define data to associate to block tree item
comment = blocktype["comment"]
- block_data = {"type" : BLOCK,
- "block_type" : blocktype["type"],
+ block_data = {"type" : BLOCK, + "block_type" : blocktype["type"], - in blocktype["inputs"]]),
+ in blocktype["inputs"]]), "extension" : (len(blocktype["inputs"])
if blocktype["extensible"]
- "comment": _(comment) +
+ "comment": _(comment) + blocktype.get("usage", "")}
self.Tree.SetPyData(blocktype_item, block_data)
# Select block tree item in tree if it corresponds to
# previously selected one
- if selected_infos == (blocktype["name"],
+ if selected_infos == (blocktype["name"], self.Tree.SelectItem(blocktype_item)
self.Comment.SetValue(block_data["comment"])
# Get next block tree item under category tree item
blocktype_item, category_cookie = \
self.Tree.GetNextChild(category_item, category_cookie)
# Add every remaining tree item under category tree item after
# updating all block items to the list of items to delete
while blocktype_item.IsOk():
items_to_delete.append(blocktype_item)
blocktype_item, category_cookie = \
self.Tree.GetNextChild(category_item, category_cookie)
# Get next category tree item under root item
category_item, root_cookie = \
self.Tree.GetNextChild(root, root_cookie)
- # Add every remaining tree item under root item after updating all
+ # Add every remaining tree item under root item after updating all # category items to the list of items to delete
while category_item.IsOk():
items_to_delete.append(category_item)
category_item, root_cookie = \
self.Tree.GetNextChild(root, root_cookie)
# Remove all items in list of items to delete from TreeCtrl
for item in items_to_delete:
def GetSelectedBlock(self):
Get selected block informations
@@ -298,17 +298,17 @@
if selected_item.IsOk() and
selected_item != self.Tree.GetRootItem()
# Return value is None if selected tree item is root or a category
- return ({"type": self.Tree.GetItemText(selected_item),
+ return ({"type": self.Tree.GetItemText(selected_item), "inputs": selected_pydata["inputs"]}
- if selected_pydata is not None and
+ if selected_pydata is not None and selected_pydata["type"] == BLOCK
def SelectTreeItem(self, name, inputs):
- Select Tree item corresponding to block informations given
+ Select Tree item corresponding to block informations given @param name: Block type name
@param inputs: List of block inputs type [input_type,...]
@@ -318,7 +318,7 @@
self.Tree.SelectItem(item)
self.Tree.EnsureVisible(item)
def FindTreeItem(self, item, name, inputs = None):
Find Tree item corresponding to block informations given
@@ -330,12 +330,12 @@
# Return immediately if item isn't valid
# Get data associated to item to test
item_pydata = self.Tree.GetPyData(item)
if item_pydata is not None and item_pydata["type"] == BLOCK:
# Only test item corresponding to block
# Test if block inputs type are the same than those given
type_inputs = item_pydata.get("inputs", None)
type_extension = item_pydata.get("extension", None)
@@ -351,11 +351,11 @@
# Return item if block data corresponds to informations given
if self.Tree.GetItemText(item) == name and same_inputs:
# Test item children if item doesn't correspond
child, child_cookie = self.Tree.GetFirstChild(item)
@@ -363,9 +363,9 @@
child, child_cookie = self.Tree.GetNextChild(item, child_cookie)
def SearchInTree(self, value, mode="first"):
Search in Tree and select item that name contains string given
@@ -378,13 +378,13 @@
root = self.Tree.GetRootItem()
# Set function to navigate in Tree item sibling according to search
sibling_function = (self.Tree.GetPrevSibling
else self.Tree.GetNextSibling)
# Get current selected item (for next and previous mode)
item = self.Tree.GetSelection()
if not item.IsOk() or mode == "first":
@@ -392,29 +392,29 @@
# Navigate through tree items until one matching found or reach tree
# Get item data to get item type
item_pydata = self.Tree.GetPyData(item)
# Item is a block category
if (item == root) or item_pydata["type"] == CATEGORY:
- # Get category first or last child according to search mode
+ # Get category first or last child according to search mode child = (self.Tree.GetLastChild(item)
else self.Tree.GetFirstChild(item)[0])
# If category has no child, go to sibling category
item = (child if child.IsOk() else sibling_function(item))
# Extract item block name
name = self.Tree.GetItemText(item)
# Test if block name contains string given
@@ -428,17 +428,17 @@
self.Tree.SelectItem(item)
self.Tree.EnsureVisible(item)
# Go to next item sibling if block not found
next = sibling_function(item)
# If category has no other child, go to next category sibling
else sibling_function(self.Tree.GetItemParent(item)))
def OnSearchCtrlChanged(self, event):
Called when SearchCtrl text control value changed
@@ -447,7 +447,7 @@
# Search for block containing SearchCtrl value in 'first' mode
self.SearchInTree(self.SearchCtrl.GetValue())
def OnSearchButtonClick(self, event):
Called when SearchCtrl search button was clicked
@@ -456,7 +456,7 @@
# Search for block containing SearchCtrl value in 'next' mode
self.SearchInTree(self.SearchCtrl.GetValue(), "next")
def OnTreeItemSelected(self, event):
Called when tree item is selected
@@ -468,13 +468,13 @@
if item_pydata is not None and item_pydata["type"] == BLOCK
# Call extra function defined when tree item is selected
if getattr(self, "_OnTreeItemSelected", None) is not None:
self._OnTreeItemSelected(event)
def OnTreeBeginDrag(self, event):
Called when a drag is started in tree
@@ -482,19 +482,19 @@
selected_item = event.GetItem()
item_pydata = self.Tree.GetPyData(selected_item)
# Item dragged is a block
if item_pydata is not None and item_pydata["type"] == BLOCK:
data = wx.TextDataObject(str(
- (self.Tree.GetItemText(selected_item),
- item_pydata["block_type"],
+ (self.Tree.GetItemText(selected_item), + item_pydata["block_type"], dragSource = wx.DropSource(self.Tree)
def OnKeyDown(self, event):
Called when key is pressed in SearchCtrl text control
@@ -503,17 +503,17 @@
# Get event keycode and value in SearchCtrl
keycode = event.GetKeyCode()
search_value = self.SearchCtrl.GetValue()
# Up key was pressed and SearchCtrl isn't empty, search for block in
if keycode == wx.WXK_UP and search_value != "":
self.SearchInTree(search_value, "previous")
# Down key was pressed and SearchCtrl isn't empty, search for block in
elif keycode == wx.WXK_DOWN and search_value != "":
self.SearchInTree(search_value, "next")
--- a/controls/PouInstanceVariablesPanel.py Fri Aug 11 15:18:19 2017 +0300
+++ b/controls/PouInstanceVariablesPanel.py Mon Aug 14 19:13:01 2017 +0300
@@ -59,14 +59,14 @@
w, h = self.GetClientSize()
total_h = self.GetLineHeight(item)
r_image_w, r_image_h = self._imageListRight.GetSize(rightimages[0])
bbox_width = (r_image_w + 4) * len(rightimages) + 4
bbox_height = r_image_h + 8
bbox_y = item.GetY() + ((total_h > r_image_h) and [(total_h-r_image_h)/2] or [0])[0]
return wx.Rect(bbox_x, bbox_y, bbox_width, bbox_height)
def IsOverItemRightImage(self, item, point):
@@ -75,30 +75,30 @@
point = self.CalcUnscrolledPosition(point)
r_image_w, r_image_h = self._imageListRight.GetSize(rightimages[0])
images_bbx = self.GetItemRightImagesBBox(item)
rect = wx.Rect(images_bbx.x + 4, images_bbx.y + 4,
for r_image in rightimages:
def PaintItem(self, item, dc, level, align):
CT.CustomTreeCtrl.PaintItem(self, item, dc, level, align)
rightimages = item.GetRightImages()
images_bbx = self.GetItemRightImagesBBox(item)
r_image_w, r_image_h = self._imageListRight.GetSize(rightimages[0])
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetPen(wx.TRANSPARENT_PEN)
bg_width = (r_image_w + 4) * len(rightimages) + 4
bg_height = r_image_h + 8
- dc.DrawRectangle(images_bbx.x, images_bbx.y,
+ dc.DrawRectangle(images_bbx.x, images_bbx.y, images_bbx.width, images_bbx.height)
for r_image in rightimages:
@@ -106,7 +106,7 @@
r_image, dc, x_pos, images_bbx.y + 4,
wx.IMAGELIST_DRAW_TRANSPARENT)
_ButtonCallbacks = namedtuple("ButtonCallbacks", ["leftdown", "dclick"])
from PLCControler import ITEMS_VARIABLE, ITEM_CONFIGURATION, ITEM_RESOURCE, ITEM_POU, ITEM_TRANSITION, ITEM_ACTION
@@ -115,26 +115,26 @@
class PouInstanceVariablesPanel(wx.Panel):
def __init__(self, parent, window, controller, debug):
- wx.Panel.__init__(self, name='PouInstanceTreePanel',
- parent=parent, pos=wx.Point(0, 0),
+ wx.Panel.__init__(self, name='PouInstanceTreePanel', + parent=parent, pos=wx.Point(0, 0), size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
self.ParentButton = wx.lib.buttons.GenBitmapButton(self,
bitmap=GetBitmap("top"), size=wx.Size(28, 28), style=wx.NO_BORDER)
self.ParentButton.SetToolTipString(_("Parent instance"))
- self.Bind(wx.EVT_BUTTON, self.OnParentButtonClick,
+ self.Bind(wx.EVT_BUTTON, self.OnParentButtonClick,
self.InstanceChoice = wx.ComboBox(self, size=wx.Size(0, 0), style=wx.CB_READONLY)
self.Bind(wx.EVT_COMBOBOX, self.OnInstanceChoiceChanged,
- self.DebugButton = wx.lib.buttons.GenBitmapButton(self,
+ self.DebugButton = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap("debug_instance"), size=wx.Size(28, 28), style=wx.NO_BORDER)
self.DebugButton.SetToolTipString(_("Debug instance"))
- self.Bind(wx.EVT_BUTTON, self.OnDebugButtonClick,
+ self.Bind(wx.EVT_BUTTON, self.OnDebugButtonClick,
self.VariablesList = CustomTreeCtrlWithRightImage(self,
agwStyle=CT.TR_NO_BUTTONS|
@@ -150,54 +150,54 @@
self.OnVariablesListItemActivated)
self.VariablesList.Bind(wx.EVT_LEFT_DOWN, self.OnVariablesListLeftDown)
self.VariablesList.Bind(wx.EVT_KEY_DOWN, self.OnVariablesListKeyDown)
self.TreeRightImageList = wx.ImageList(24, 24)
self.EditImage = self.TreeRightImageList.Add(GetBitmap("edit"))
self.DebugInstanceImage = self.TreeRightImageList.Add(GetBitmap("debug_instance"))
self.VariablesList.SetRightImageList(self.TreeRightImageList)
self.EditImage: _ButtonCallbacks(
self.EditButtonCallback, None),
self.DebugInstanceImage: _ButtonCallbacks(
self.DebugButtonCallback, self.DebugButtonDClickCallback)}
buttons_sizer = wx.FlexGridSizer(cols=3, hgap=0, rows=1, vgap=0)
buttons_sizer.AddWindow(self.ParentButton)
buttons_sizer.AddWindow(self.InstanceChoice, flag=wx.GROW)
buttons_sizer.AddWindow(self.DebugButton)
buttons_sizer.AddGrowableCol(1)
buttons_sizer.AddGrowableRow(0)
main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
main_sizer.AddSizer(buttons_sizer, flag=wx.GROW)
main_sizer.AddWindow(self.VariablesList, flag=wx.GROW)
main_sizer.AddGrowableCol(0)
main_sizer.AddGrowableRow(1)
self.SetSizer(main_sizer)
self.ParentWindow = window
self.Controller = controller
def SetTreeImageList(self, tree_image_list):
self.VariablesList.SetImageList(tree_image_list)
def SetController(self, controller):
self.Controller = controller
def SetPouType(self, tagname, pou_instance=None):
if self.Controller is not None:
@@ -206,7 +206,7 @@
tagname = self.Controller.ComputeConfigurationName(config_name)
if pou_instance is not None:
self.PouInstance = pou_instance
if self.PouTagName != tagname:
self.PouTagName = tagname
@@ -214,20 +214,20 @@
self.RefreshInstanceChoice()
self.VariablesList.DeleteAllItems()
if self.Controller is not None and self.PouTagName is not None:
if self.PouTagName.split('::')[0] in ['A', 'T']:
self.PouInfos = self.Controller.GetPouVariables('P::%s' % self.PouTagName.split('::')[1], self.Debug)
@@ -244,24 +244,24 @@
text = "%s (%s)" % (var_infos.name, var_infos.type)
right_images.append(self.EditImage)
if var_infos.debug and self.Debug:
right_images.append(self.DebugInstanceImage)
item = self.VariablesList.AppendItem(root, text)
item.SetRightImages(right_images)
self.VariablesList.SetItemImage(item, self.ParentWindow.GetTreeImage(var_infos.var_class))
self.VariablesList.SetPyData(item, var_infos)
self.RefreshInstanceChoice()
def RefreshInstanceChoice(self):
self.InstanceChoice.Clear()
self.InstanceChoice.SetValue("")
@@ -279,12 +279,12 @@
self.InstanceChoice.SetValue(_("Select an instance"))
def RefreshButtons(self):
enabled = self.InstanceChoice.GetSelection() != -1
self.ParentButton.Enable(enabled and self.PouInfos.var_class != ITEM_CONFIGURATION)
self.DebugButton.Enable(enabled and self.PouInfos.debug and self.Debug)
root = self.VariablesList.GetRootItem()
if root is not None and root.IsOk():
item, item_cookie = self.VariablesList.GetFirstChild(root)
@@ -295,12 +295,12 @@
if child.GetName() != "edit":
item, item_cookie = self.VariablesList.GetNextChild(root, item_cookie)
def EditButtonCallback(self, infos):
var_class = infos.var_class
if var_class == ITEM_RESOURCE:
tagname = self.Controller.ComputeConfigurationResourceName(
- self.InstanceChoice.GetStringSelection(),
+ self.InstanceChoice.GetStringSelection(), elif var_class == ITEM_TRANSITION:
tagname = self.Controller.ComputePouTransitionName(
@@ -314,7 +314,7 @@
tagname = self.Controller.ComputePouName(infos.type)
self.ParentWindow.EditProjectElement(var_class, tagname)
def DebugButtonCallback(self, infos):
if self.InstanceChoice.GetSelection() != -1:
var_class = infos.var_class
@@ -344,16 +344,16 @@
self.Controller.ComputePouName(infos.type))
def DebugButtonDClickCallback(self, infos):
if self.InstanceChoice.GetSelection() != -1:
if infos.var_class in ITEMS_VARIABLE:
self.ParentWindow.AddDebugVariable(
- "%s.%s" % (self.InstanceChoice.GetStringSelection(),
+ "%s.%s" % (self.InstanceChoice.GetStringSelection(),
def ShowInstanceChoicePopup(self):
self.InstanceChoice.SetFocusFromKbd()
size = self.InstanceChoice.GetSize()
@@ -364,7 +364,7 @@
#event = wx.KeyEvent(wx.EVT_KEY_DOWN._getEvtType())
#event.m_keyCode = wx.WXK_SPACE
self.InstanceChoice.GetEventHandler().ProcessEvent(event)
def OnParentButtonClick(self, event):
if self.InstanceChoice.GetSelection() != -1:
parent_path = self.InstanceChoice.GetStringSelection().rsplit(".", 1)[0]
@@ -373,11 +373,11 @@
wx.CallAfter(self.SetPouType, tagname, parent_path)
wx.CallAfter(self.ParentWindow.SelectProjectTreeItem, tagname)
def OnInstanceChoiceChanged(self, event):
def OnDebugButtonClick(self, event):
if self.InstanceChoice.GetSelection() != -1:
self.ParentWindow.OpenDebugViewer(
@@ -385,26 +385,26 @@
self.InstanceChoice.GetStringSelection(),
def OnVariablesListItemActivated(self, event):
selected_item = event.GetItem()
if selected_item is not None and selected_item.IsOk():
item_infos = self.VariablesList.GetPyData(selected_item)
if item_infos is not None:
item_button = self.VariablesList.IsOverItemRightImage(
selected_item, event.GetPoint())
if item_button is not None:
callback = self.ButtonCallBacks[item_button].dclick
elif item_infos.var_class not in ITEMS_VARIABLE:
instance_path = self.InstanceChoice.GetStringSelection()
if item_infos.var_class == ITEM_RESOURCE:
tagname = self.Controller.ComputeConfigurationResourceName(
@@ -424,7 +424,7 @@
self.SetPouType(tagname, item_path)
self.ParentWindow.SelectProjectTreeItem(tagname)
def OnVariablesListLeftDown(self, event):
if self.InstanceChoice.GetSelection() == -1:
wx.CallAfter(self.ShowInstanceChoicePopup)
@@ -434,15 +434,15 @@
item_infos = self.VariablesList.GetPyData(item)
if item_infos is not None:
item_button = self.VariablesList.IsOverItemRightImage(
item, event.GetPosition())
if item_button is not None:
callback = self.ButtonCallBacks[item_button].leftdown
- elif (flags & CT.TREE_HITTEST_ONITEMLABEL and
+ elif (flags & CT.TREE_HITTEST_ONITEMLABEL and item_infos.var_class in ITEMS_VARIABLE):
self.ParentWindow.EnsureTabVisible(
self.ParentWindow.DebugVariablePanel)
@@ -457,4 +457,3 @@
keycode = event.GetKeyCode()
if keycode != wx.WXK_LEFT:
--- a/graphics/FBD_Objects.py Fri Aug 11 15:18:19 2017 +0300
+++ b/graphics/FBD_Objects.py Mon Aug 14 19:13:01 2017 +0300
@@ -39,7 +39,7 @@
return name in ["OUT", "MN", "MX"] or name.startswith("IN") and (block_type, name) != ("EXPT", "IN2")
class FBD_Block(Graphic_Element):
def __init__(self, parent, type, name, id = None, extension = 0, inputs = None, connectors = {}, executionControl = False, executionOrder = 0):
Graphic_Element.__init__(self, parent)
@@ -56,7 +56,7 @@
self.Pen = MiterPen(wx.BLACK)
self.SetType(type, extension, inputs, connectors, executionControl)
# Make a clone of this FBD_Block
def Clone(self, parent, id = None, name = "", pos = None):
if self.Name != "" and name == "":
@@ -70,10 +70,10 @@
block.Inputs = [input.Clone(block) for input in self.Inputs]
block.Outputs = [output.Clone(block) for output in self.Outputs]
def GetConnectorTranslation(self, element):
return dict(zip(self.Inputs + self.Outputs, element.Inputs + element.Outputs))
for input in self.Inputs:
@@ -81,7 +81,7 @@
for output in self.Outputs:
def GetRedrawRect(self, movex = 0, movey = 0):
rect = Graphic_Element.GetRedrawRect(self, movex, movey)
@@ -93,26 +93,26 @@
rect = rect.Union(output.GetConnectedRedrawRect(movex, movey))
# Delete this block by calling the appropriate method
self.Parent.DeleteBlock(self)
# Unconnect all inputs and outputs
for input in self.Inputs:
input.UnConnect(delete = True)
for output in self.Outputs:
output.UnConnect(delete = True)
# Refresh the size of text for name
def RefreshNameSize(self):
self.NameSize = self.Parent.GetTextExtent(self.Name)
# Refresh the size of text for execution order
def RefreshExecutionOrderSize(self):
self.ExecutionOrderSize = self.Parent.GetTextExtent(str(self.ExecutionOrder))
# Returns if the point given is in the bounding box
def HitTest(self, pt, connectors=True):
@@ -121,7 +121,7 @@
test_block = self.GetBlockBoundingBox(connectors).InsideXY(pt.x, pt.y)
return test_text or test_block
# Returns the bounding box of the name outside the block
def GetTextBoundingBox(self):
# Calculate the size of the name outside the block
@@ -130,7 +130,7 @@
self.Pos.y - (text_height + 2),
# Returns the bounding box of function block without name outside
def GetBlockBoundingBox(self, connectors=True):
bbx_x, bbx_y = self.Pos.x, self.Pos.y
@@ -143,13 +143,13 @@
bbx_width = max(bbx_width, bbx_width + self.Pos.x + self.ExecutionOrderSize[0] - bbx_x - self.Size[0])
bbx_height = bbx_height + (self.ExecutionOrderSize[1] + 2)
return wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1)
# Refresh the block bounding box
def RefreshBoundingBox(self):
self.BoundingBox = self.GetBlockBoundingBox()
self.BoundingBox.Union(self.GetTextBoundingBox())
# Refresh the positions of the block connectors
def RefreshConnectors(self):
scaling = self.Parent.GetScaling()
@@ -170,15 +170,15 @@
self.Outputs[i].SetPosition(wx.Point(self.Size[0], ypos))
# Refresh the positions of wires connected to inputs and outputs
def RefreshConnected(self, exclude = []):
for input in self.Inputs:
input.MoveConnected(exclude)
for output in self.Outputs:
output.MoveConnected(exclude)
- # Returns the block connector that starts with the point given if it exists
+ # Returns the block connector that starts with the point given if it exists def GetConnector(self, position, output_name = None, input_name = None):
if input_name is not None:
# Test each input connector
@@ -193,14 +193,14 @@
if input_name is None and output_name is None:
return self.FindNearestConnector(position, self.Inputs + self.Outputs)
return tuple([input.GetType(True) for input in self.Inputs if input.GetName() != "EN"])
def SetOutputValues(self, values):
for output in self.Outputs:
output.SetValue(values.get(ouput.getName(), None))
def GetConnectionResultType(self, connector, connectortype):
if not TestConnectorName(connector.GetName(), self.Type):
@@ -216,11 +216,11 @@
if resulttype is None or outputtype is not None and self.IsOfType(outputtype, resulttype):
# Returns all the block connectors
return {"inputs" : self.Inputs, "outputs" : self.Outputs}
# Test if point given is on one of the block connectors
def TestConnector(self, pt, direction = None, exclude = True):
# Test each input connector
@@ -232,10 +232,10 @@
if output.TestPoint(pt, direction, exclude):
def SetType(self, type, extension, inputs = None, connectors = {}, executionControl = False):
- if type != self.Type or self.Extension != extension or executionControl != self.ExecutionControl:
+ if type != self.Type or self.Extension != extension or executionControl != self.ExecutionControl: self.TypeSize = self.Parent.GetTextExtent(self.Type)
@@ -264,7 +264,7 @@
inputs.insert(0, ("EN","BOOL","none"))
outputs.insert(0, ("ENO","BOOL","none"))
self.Pen = MiterPen(self.Colour)
# Extract the inputs properties and create or modify the corresponding connector
for input_name, input_type, input_modifier in inputs:
@@ -284,7 +284,7 @@
for input in self.Inputs:
input.UnConnect(delete = True)
self.Inputs = input_connectors
# Extract the outputs properties and create or modify the corresponding connector
for output_name, output_type, output_modifier in outputs:
@@ -304,47 +304,47 @@
for output in self.Outputs:
output.UnConnect(delete = True)
self.Outputs = output_connectors
for output in self.Outputs:
self.RefreshBoundingBox()
# Changes the extension name
def SetExtension(self, extension):
self.Extension = extension
# Returs the extension name
# Changes the execution order
def SetExecutionOrder(self, executionOrder):
self.ExecutionOrder = executionOrder
self.RefreshExecutionOrderSize()
# Returs the execution order
def GetExecutionOrder(self):
return self.ExecutionOrder
# Returs the execution order
def GetExecutionControl(self):
return self.ExecutionControl
# Refresh the block minimum size
def RefreshMinSize(self):
# Calculate the inputs maximum width
@@ -360,25 +360,25 @@
width = max(self.TypeSize[0] + 10, max_input + max_output + 15)
height = (max(len(self.Inputs), len(self.Outputs)) + 1) * BLOCK_LINE_SIZE
self.MinSize = width, height
# Returns the block minimum size
# Changes the negated property of the connector handled
def SetConnectorNegated(self, negated):
handle_type, handle = self.Handle
if handle_type == HANDLE_CONNECTOR:
handle.SetNegated(negated)
# Changes the edge property of the connector handled
def SetConnectorEdge(self, edge):
handle_type, handle = self.Handle
if handle_type == HANDLE_CONNECTOR:
## # Method called when a Motion event have been generated
## def OnMotion(self, event, dc, scaling):
## if not event.Dragging():
@@ -390,12 +390,12 @@
## tip = wx.TipWindow(self.Parent, "Test")
## tip.SetBoundingRect(rect)
## return Graphic_Element.OnMotion(self, event, dc, scaling)
# Method called when a LeftDClick event have been generated
def OnLeftDClick(self, event, dc, scaling):
# Edit the block properties
self.Parent.EditBlockContent(self)
# Method called when a RightUp event have been generated
def OnRightUp(self, event, dc, scaling):
pos = GetScaledEventPosition(event, dc, scaling)
@@ -406,7 +406,7 @@
self.Parent.PopupBlockMenu(connector)
self.Parent.PopupBlockMenu()
# Refreshes the block model
def RefreshModel(self, move=True):
self.Parent.RefreshBlockModel(self)
@@ -414,10 +414,10 @@
for output in self.Outputs:
def GetToolTipValue(self):
# 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:
@@ -427,7 +427,7 @@
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]].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"]:
@@ -438,7 +438,7 @@
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:
@@ -453,14 +453,14 @@
input.ClearHighlights(highlight_type)
for output in self.Outputs:
output.ClearHighlights(highlight_type)
Graphic_Element.Draw(self, dc)
dc.SetBrush(wx.WHITE_BRUSH)
dc.SetTextForeground(self.Colour)
if getattr(dc, "printing", False):
name_size = dc.GetTextExtent(self.Name)
type_size = dc.GetTextExtent(self.Type)
@@ -469,7 +469,7 @@
name_size = self.NameSize
type_size = self.TypeSize
executionorder_size = self.ExecutionOrderSize
# Draw a rectangle with the block size
dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
# Draw block name and block type
@@ -488,11 +488,11 @@
# 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 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])
#-------------------------------------------------------------------------------
# Function Block Diagram Variable
@@ -516,7 +516,7 @@
self.SetType(type, value_type)
# Make a clone of this FBD_Variable
def Clone(self, parent, id = None, pos = None):
variable = FBD_Variable(parent, self.Type, self.Name, self.ValueType, id)
@@ -530,7 +530,7 @@
variable.Output = self.Output.Clone(variable)
def GetConnectorTranslation(self, element):
if self.Input is not None:
@@ -538,7 +538,7 @@
if self.Output is not None:
connectors[self.Output] = element.Output
if self.Input is not None:
@@ -546,7 +546,7 @@
if self.Output is not None:
def GetRedrawRect(self, movex = 0, movey = 0):
rect = Graphic_Element.GetRedrawRect(self, movex, movey)
@@ -556,26 +556,26 @@
if self.Output and self.Output.IsConnected():
rect = rect.Union(self.Output.GetConnectedRedrawRect(movex, movey))
self.Input.UnConnect(delete = True)
self.Output.UnConnect(delete = True)
# Delete this variable by calling the appropriate method
self.Parent.DeleteVariable(self)
# Refresh the size of text for name
def RefreshNameSize(self):
self.NameSize = self.Parent.GetTextExtent(self.Name)
# Refresh the size of text for execution order
def RefreshExecutionOrderSize(self):
self.ExecutionOrderSize = self.Parent.GetTextExtent(str(self.ExecutionOrder))
# Refresh the variable bounding box
def RefreshBoundingBox(self):
if self.Type in (OUTPUT, INOUT):
@@ -594,7 +594,7 @@
bbx_width = max(bbx_width, bbx_width + self.Pos.x + self.ExecutionOrderSize[0] - bbx_x - self.Size[0])
bbx_height = bbx_height + (self.ExecutionOrderSize[1] + 2)
self.BoundingBox = wx.Rect(bbx_x, self.Pos.y, bbx_width + 1, bbx_height + 1)
# Refresh the position of the variable connector
def RefreshConnectors(self):
scaling = self.Parent.GetScaling()
@@ -607,14 +607,14 @@
self.Output.SetPosition(wx.Point(self.Size[0], position))
# Refresh the position of wires connected to connector
def RefreshConnected(self, exclude = []):
self.Input.MoveConnected(exclude)
self.Output.MoveConnected(exclude)
# Test if point given is on the variable connector
def TestConnector(self, pt, direction = None, exclude=True):
if self.Input and self.Input.TestPoint(pt, direction, exclude):
@@ -622,8 +622,8 @@
if self.Output and self.Output.TestPoint(pt, direction, exclude):
- # Returns the block connector that starts with the point given if it exists
+ # Returns the block connector that starts with the point given if it exists def GetConnector(self, position, name = None):
@@ -640,8 +640,8 @@
connectors.append(self.Output)
return self.FindNearestConnector(position, connectors)
- # Returns all the block connectors
+ # Returns all the block connectors connectors = {"inputs": [], "outputs": []}
@@ -649,14 +649,14 @@
connectors["outputs"].append(self.Output)
# Changes the negated property of the variable connector if handled
def SetConnectorNegated(self, negated):
handle_type, handle = self.Handle
if handle_type == HANDLE_CONNECTOR:
handle.SetNegated(negated)
# Changes the variable type
def SetType(self, type, value_type):
@@ -680,38 +680,38 @@
self.Input.SetType(value_type)
- self.Output.SetType(value_type)
+ self.Output.SetType(value_type) # Returns the variable type
# Returns the variable value type
# Changes the variable name
# Returns the variable name
# Changes the execution order
def SetExecutionOrder(self, executionOrder):
self.ExecutionOrder = executionOrder
self.RefreshExecutionOrderSize()
# Returs the execution order
def GetExecutionOrder(self):
return self.ExecutionOrder
# Returns the variable minimum size
return self.NameSize[0] + 10, self.NameSize[1] + 10
# Set size of the variable to the minimum size
def SetBestSize(self, scaling):
@@ -720,22 +720,22 @@
return Graphic_Element.SetBestSize(self, scaling, x_factor=0.)
return Graphic_Element.SetBestSize(self, scaling)
# Method called when a LeftDClick event have been generated
def OnLeftDClick(self, event, dc, scaling):
types = [INPUT, OUTPUT, INOUT]
self.Parent.ChangeVariableType(self,
types[(types.index(self.Type) + 1) % len(types)])
# Edit the variable properties
self.Parent.EditVariableContent(self)
# Method called when a RightUp event have been generated
def OnRightUp(self, event, dc, scaling):
self.Parent.PopupVariableMenu()
# Refreshes the variable model
def RefreshModel(self, move=True):
self.Parent.RefreshVariableModel(self)
@@ -744,35 +744,35 @@
if move and self.Type != OUTPUT:
self.Output.RefreshWires()
# 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:
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)
Graphic_Element.Draw(self, dc)
dc.SetPen(MiterPen(wx.BLACK))
dc.SetBrush(wx.WHITE_BRUSH)
if getattr(dc, "printing", False):
name_size = dc.GetTextExtent(self.Name)
executionorder_size = dc.GetTextExtent(str(self.ExecutionOrder))
name_size = self.NameSize
executionorder_size = self.ExecutionOrderSize
- text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2,
+ text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2, self.Pos.y + (self.Size[1] - name_size[1]) / 2)
# Draw a rectangle with the variable size
dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
@@ -781,7 +781,7 @@
if self.ExecutionOrder != 0:
# Draw variable execution order
@@ -789,7 +789,7 @@
self.Pos.y + self.Size[1] + 2)
if not getattr(dc, "printing", False):
DrawHighlightedText(dc, self.Name, self.Highlights, text_pos[0], text_pos[1])
#-------------------------------------------------------------------------------
# Function Block Diagram Connector
#-------------------------------------------------------------------------------
@@ -816,12 +816,12 @@
self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), EAST)
def GetRedrawRect(self, movex = 0, movey = 0):
rect = Graphic_Element.GetRedrawRect(self, movex, movey)
@@ -829,7 +829,7 @@
if self.Connector and self.Connector.IsConnected():
rect = rect.Union(self.Connector.GetConnectedRedrawRect(movex, movey))
# Make a clone of this FBD_Connector
def Clone(self, parent, id = None, pos = None):
connection = FBD_Connector(parent, self.Type, self.Name, id)
@@ -840,7 +840,7 @@
connection.SetPosition(self.Pos.x, self.Pos.y)
connection.Connector = self.Connector.Clone(connection)
def GetConnectorTranslation(self, element):
return {self.Connector : element.Connector}
@@ -848,15 +848,15 @@
self.Connector.UnConnect(delete = True)
# Delete this connection by calling the appropriate method
self.Parent.DeleteConnection(self)
# Refresh the size of text for name
def RefreshNameSize(self):
self.NameSize = self.Parent.GetTextExtent(self.Name)
# Refresh the connection bounding box
def RefreshBoundingBox(self):
if self.Type == CONNECTOR:
@@ -865,7 +865,7 @@
bbx_width = self.Size[0] + CONNECTOR_SIZE
self.BoundingBox = wx.Rect(bbx_x, self.Pos.y, bbx_width, self.Size[1])
# Refresh the position of the connection connector
def RefreshConnectors(self):
scaling = self.Parent.GetScaling()
@@ -878,23 +878,23 @@
self.Connector.SetPosition(wx.Point(self.Size[0], position))
# Refresh the position of wires connected to connector
def RefreshConnected(self, exclude = []):
self.Connector.MoveConnected(exclude)
# Test if point given is on the connection connector
def TestConnector(self, pt, direction = None, exclude=True):
if self.Connector and self.Connector.TestPoint(pt, direction, exclude):
# Returns the connection connector
def GetConnector(self, position = None, name = None):
- # Returns all the block connectors
+ # Returns all the block connectors connectors = {"inputs": [], "outputs": []}
if self.Type == CONNECTOR:
@@ -923,45 +923,45 @@
self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), EAST)
self.RefreshBoundingBox()
# Returns the connection type
def GetConnectionResultType(self, connector, connectortype):
if self.Type == CONTINUATION:
connector = self.Parent.GetConnectorByName(self.Name)
if connector is not None:
return connector.Connector.GetConnectedType()
# Changes the connection name
# Returns the connection name
# Set size of the variable to the minimum size
def SetBestSize(self, scaling):
if self.Type == CONTINUATION:
return Graphic_Element.SetBestSize(self, scaling, x_factor=1.)
return Graphic_Element.SetBestSize(self, scaling, x_factor=0.)
# Returns the connection minimum size
text_width, text_height = self.NameSize
return text_width + text_height + 20, text_height + 10
# Method called when a LeftDClick event have been generated
def OnLeftDClick(self, event, dc, scaling):
- # Change connection type
+ # Change connection type if self.Type == CONNECTOR:
self.Parent.ChangeConnectionType(self, CONTINUATION)
@@ -969,12 +969,12 @@
# Edit the connection properties
self.Parent.EditConnectionContent(self)
# Method called when a RightUp event have been generated
def OnRightUp(self, event, dc, scaling):
self.Parent.PopupConnectionMenu()
# Refreshes the connection model
def RefreshModel(self, move=True):
self.Parent.RefreshConnectionModel(self)
@@ -983,51 +983,50 @@
if move and self.Type == CONTINUATION:
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):
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)
Graphic_Element.Draw(self, dc)
dc.SetPen(MiterPen(wx.BLACK))
dc.SetBrush(wx.WHITE_BRUSH)
if getattr(dc, "printing", False):
name_size = dc.GetTextExtent(self.Name)
name_size = self.NameSize
# Draw a rectangle with the connection size with arrows inside
dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
arrowsize = min(self.Size[1] / 2, (self.Size[0] - name_size[0] - 10) / 2)
- dc.DrawLine(self.Pos.x, self.Pos.y, self.Pos.x + arrowsize,
+ dc.DrawLine(self.Pos.x, self.Pos.y, self.Pos.x + arrowsize, self.Pos.y + self.Size[1] / 2)
- dc.DrawLine(self.Pos.x + arrowsize, self.Pos.y + self.Size[1] / 2,
+ dc.DrawLine(self.Pos.x + arrowsize, self.Pos.y + self.Size[1] / 2, self.Pos.x, self.Pos.y + self.Size[1])
- dc.DrawLine(self.Pos.x + self.Size[0] - arrowsize, self.Pos.y,
+ dc.DrawLine(self.Pos.x + self.Size[0] - arrowsize, self.Pos.y, self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2)
- dc.DrawLine(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2,
+ 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])
- text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 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])
if not getattr(dc, "printing", False):
DrawHighlightedText(dc, self.Name, self.Highlights, text_pos[0], text_pos[1])
--- a/graphics/GraphicCommons.py Fri Aug 11 15:18:19 2017 +0300
+++ b/graphics/GraphicCommons.py Mon Aug 14 19:13:01 2017 +0300
@@ -83,12 +83,12 @@
# Contants for defining the direction of a connector
[EAST, NORTH, WEST, SOUTH] = [(1,0), (0,-1), (-1,0), (0,1)]
-# Contants for defining which mode is selected for each view
-[MODE_SELECTION, MODE_BLOCK, MODE_VARIABLE, MODE_CONNECTION, MODE_COMMENT,
- MODE_COIL, MODE_CONTACT, MODE_POWERRAIL, MODE_INITIALSTEP, MODE_STEP,
+# Contants for defining which mode is selected for each view +[MODE_SELECTION, MODE_BLOCK, MODE_VARIABLE, MODE_CONNECTION, MODE_COMMENT, + MODE_COIL, MODE_CONTACT, MODE_POWERRAIL, MODE_INITIALSTEP, MODE_STEP, MODE_TRANSITION, MODE_DIVERGENCE, MODE_JUMP, MODE_ACTION, MODE_MOTION] = range(15)
-# Contants for defining alignment types for graphic group
+# Contants for defining alignment types for graphic group [ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, ALIGN_TOP, ALIGN_MIDDLE, ALIGN_BOTTOM] = range(6)
# Contants for defining which drawing mode is selected for app
@@ -122,7 +122,7 @@
if constraint == 1 and xround < fraction:
@@ -224,7 +224,7 @@
dc.DrawText(part, x + offset_width, y)
dc.SetTextForeground(wx.BLACK)
#-------------------------------------------------------------------------------
# Graphic element base class
#-------------------------------------------------------------------------------
@@ -234,7 +234,7 @@
class Graphic_Element(ToolTipProducer):
# Create a new graphic element
def __init__(self, parent, id = None):
ToolTipProducer.__init__(self, parent)
@@ -251,22 +251,22 @@
self.Size = wx.Size(0, 0)
self.BoundingBox = wx.Rect(0, 0, 0, 0)
def TestVisible(self, screen):
self.Visible = self.Selected or self.GetRedrawRect().Intersects(screen)
def GetConnectorTranslation(self, element):
def FindNearestConnector(self, position, connectors):
for connector in connectors:
@@ -278,20 +278,20 @@
def IsOfType(self, type, reference):
return self.Parent.IsOfType(type, reference)
def IsEndType(self, type):
return self.Parent.IsEndType(type)
# Make a clone of this element
return Graphic_Element(parent, self.Id)
# Changes the block position
def SetPosition(self, x, y):
@@ -302,7 +302,7 @@
# Returns the block position
return self.Pos.x, self.Pos.y
# Changes the element size
def SetSize(self, width, height):
self.Size.SetWidth(width)
@@ -313,11 +313,11 @@
# Returns the element size
return self.Size.GetWidth(), self.Size.GetHeight()
# Returns the minimum element size
# Set size of the element to the minimum size
def SetBestSize(self, scaling, x_factor=0.5, y_factor=0.5):
width, height = self.GetSize()
@@ -336,31 +336,31 @@
height = round_scaling(height, scaling[1], 1)
self.SetSize(width, height)
return self.Pos.x - posx, self.Pos.y - posy
# Refresh the element Bounding Box
def RefreshBoundingBox(self):
self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0], self.Size[1])
# Refresh the element connectors position
def RefreshConnectors(self):
# Refresh the position of wires connected to element inputs and outputs
def RefreshConnected(self):
def SetParent(self, parent):
# Override this method for defining the method to call for deleting this element
# Returns if the point given is in the bounding box
def HitTest(self, pt, connectors=True):
@@ -368,19 +368,19 @@
rect = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0], self.Size[1])
return rect.InsideXY(pt.x, pt.y)
# Returns if the point given is in the bounding box
def IsInSelection(self, rect):
return rect.InsideXY(self.BoundingBox.x, self.BoundingBox.y) and rect.InsideXY(self.BoundingBox.x + self.BoundingBox.width, self.BoundingBox.y + self.BoundingBox.height)
# Override this method for refreshing the bounding box
def RefreshBoundingBox(self):
# Returns the bounding box
def GetBoundingBox(self):
def GetRedrawRect(self, movex = 0, movey = 0):
scalex, scaley = self.Parent.GetViewScale()
@@ -390,42 +390,42 @@
rect.width = self.BoundingBox.width + 2 * (int(HANDLE_SIZE / scalex) + abs(movex) + 1) + 4
rect.height = self.BoundingBox.height + 2 * (int(HANDLE_SIZE / scaley) + abs(movey) + 1) + 4
def Refresh(self, rect = None):
self.Parent.RefreshRect(self.Parent.GetScrolledRect(rect), False)
self.Parent.RefreshRect(self.Parent.GetScrolledRect(self.GetRedrawRect()), False)
# Change the variable that indicates if this element is selected
def SetSelected(self, selected):
# Change the variable that indicates if this element is highlighted
def SetHighlighted(self, highlighted):
self.Highlighted = highlighted
# Test if the point is on a handle of this element
def TestHandle(self, event):
dc = self.Parent.GetLogicalDC()
scalex, scaley = dc.GetUserScale()
pos = event.GetPosition()
pt = wx.Point(*self.Parent.CalcUnscrolledPosition(pos.x, pos.y))
left = (self.BoundingBox.x - 2) * scalex - HANDLE_SIZE
center = (self.BoundingBox.x + self.BoundingBox.width / 2) * scalex - HANDLE_SIZE / 2
right = (self.BoundingBox.x + self.BoundingBox.width + 2) * scalex
top = (self.BoundingBox.y - 2) * scaley - HANDLE_SIZE
middle = (self.BoundingBox.y + self.BoundingBox.height / 2) * scaley - HANDLE_SIZE / 2
bottom = (self.BoundingBox.y + self.BoundingBox.height + 2) * scaley
extern_rect = wx.Rect(left, top, right + HANDLE_SIZE - left, bottom + HANDLE_SIZE - top)
intern_rect = wx.Rect(left + HANDLE_SIZE, top + HANDLE_SIZE, right - left - HANDLE_SIZE, bottom - top - HANDLE_SIZE)
# Verify that this element is selected
if self.Selected and extern_rect.InsideXY(pt.x, pt.y) and not intern_rect.InsideXY(pt.x, pt.y):
# Find if point is on a handle horizontally
@@ -450,7 +450,7 @@
if (handle_x, handle_y) in VALID_HANDLES:
return handle_x, handle_y
# Method called when a LeftDown event have been generated
def OnLeftDown(self, event, dc, scaling):
pos = event.GetLogicalPosition(dc)
@@ -469,7 +469,7 @@
self.oldPos = GetScaledEventPosition(event, dc, scaling)
self.StartPos = wx.Point(self.Pos.x, self.Pos.y)
self.CurrentDrag = wx.Point(0, 0)
# Method called when a LeftUp event have been generated
def OnLeftUp(self, event, dc, scaling):
# If a dragging have been initiated
@@ -498,7 +498,7 @@
# Method called when a LeftDClick event have been generated
def OnLeftDClick(self, event, dc, scaling):
# Method called when a Motion event have been generated
def OnMotion(self, event, dc, scaling):
# If the cursor is dragging and the element have been clicked
@@ -537,12 +537,12 @@
self.Pos.y += max(-self.BoundingBox.y, dy)
self.RefreshConnected(exclude)
self.RefreshBoundingBox()
# Resizes the element from position and size given
def Resize(self, x, y, width, height):
self.SetSize(width, height)
# Refreshes the element state according to move defined and handle selected
def ProcessDragging(self, movex, movey, event, scaling, width_fac = 1, height_fac = 1):
handle_type, handle = self.Handle
@@ -618,27 +618,27 @@
movey = self.StartPos.y - self.Pos.y
movex = self.StartPos.x - self.Pos.x
- movey = self.StartPos.y + self.CurrentDrag.y - self.Pos.y
+ movey = self.StartPos.y + self.CurrentDrag.y - self.Pos.y
# Override this method for defining the method to call for adding an highlight to this element
def AddHighlight(self, infos, start, end, highlight_type):
# Override this method for defining the method to call for removing an highlight from this element
def RemoveHighlight(self, infos, start, end, highlight_type):
# 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):
# Override this method for defining the method to call for refreshing the model of this element
def RefreshModel(self, move=True):
# Draws the highlightment of this element if it is highlighted (can be overwritten)
def DrawHighlightment(self, dc):
scalex, scaley = dc.GetUserScale()
@@ -646,13 +646,13 @@
dc.SetPen(MiterPen(HIGHLIGHTCOLOR))
dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
dc.SetLogicalFunction(wx.AND)
- dc.DrawRectangle(int(round((self.Pos.x - 1) * scalex)) - 2,
- int(round((self.Pos.y - 1) * scaley)) - 2,
- int(round((self.Size.width + 3) * scalex)) + 5,
+ dc.DrawRectangle(int(round((self.Pos.x - 1) * scalex)) - 2, + int(round((self.Pos.y - 1) * scaley)) - 2, + int(round((self.Size.width + 3) * scalex)) + 5, int(round((self.Size.height + 3) * scaley)) + 5)
dc.SetLogicalFunction(wx.COPY)
dc.SetUserScale(scalex, scaley)
# Draws the handles of this element if it is selected
if not getattr(dc, "printing", False):
@@ -663,20 +663,20 @@
dc.SetPen(MiterPen(wx.BLACK))
dc.SetBrush(wx.BLACK_BRUSH)
left = (self.BoundingBox.x - 2) * scalex - HANDLE_SIZE
center = (self.BoundingBox.x + self.BoundingBox.width / 2) * scalex - HANDLE_SIZE / 2
right = (self.BoundingBox.x + self.BoundingBox.width + 2) * scalex
top = (self.BoundingBox.y - 2) * scaley - HANDLE_SIZE
middle = (self.BoundingBox.y + self.BoundingBox.height / 2) * scaley - HANDLE_SIZE / 2
bottom = (self.BoundingBox.y + self.BoundingBox.height + 2) * scaley
for x, y in [(left, top), (center, top), (right, top),
(left, middle), (right, middle),
(left, bottom), (center, bottom), (right, bottom)]:
dc.DrawRectangle(x, y, HANDLE_SIZE, HANDLE_SIZE)
dc.SetUserScale(scalex, scaley)
@@ -689,27 +689,27 @@
class Graphic_Group(Graphic_Element):
# Create a new group of graphic elements
def __init__(self, parent):
Graphic_Element.__init__(self, parent)
self.RefreshWireExclusion()
self.RefreshBoundingBox()
for element in self.Elements:
block, wire = element.GetDefinition()
# Make a clone of this element
def Clone(self, parent, pos = None):
group = Graphic_Group(parent)
@@ -751,20 +751,20 @@
if not isinstance(element, Wire):
parent.AddBlockInModel(element)
def CanAddBlocks(self, parent):
for element in self.Elements:
if not isinstance(element, Wire):
valid &= parent.CanAddElement(element)
for element in self.Elements:
# Refresh the list of wire excluded
def RefreshWireExclusion(self):
@@ -774,7 +774,7 @@
endblock = element.EndConnected.GetParentBlock()
if startblock in self.Elements and endblock in self.Elements:
self.WireExcluded.append(element)
def GetRedrawRect(self, movex = 0, movey = 0):
@@ -784,41 +784,41 @@
rect = rect.Union(element.GetRedrawRect(movex, movey))
# Clean this group of elements
# Clean all the elements of the group
for element in self.Elements:
# Delete this group of elements
# Delete all the elements of the group
for element in self.Elements:
# Returns if the point given is in the bounding box of one of the elements of this group
def HitTest(self, pt, connectors=True):
for element in self.Elements:
result |= element.HitTest(pt, connectors)
# Returns if the element given is in this group
def IsElementIn(self, element):
return element in self.Elements
# Change the elements of the group
def SetElements(self, elements):
self.RefreshWireExclusion()
self.RefreshBoundingBox()
# Returns the elements of the group
# Align the group elements
def AlignElements(self, horizontally, vertically):
minx = self.BoundingBox.x + self.BoundingBox.width
@@ -854,11 +854,11 @@
element.Move(movex, movey)
self.RefreshBoundingBox()
# Add the given element to the group of elements
def AddElement(self, element):
self.Elements.append(element)
# Remove or select the given element if it is or not in the group
def SelectElement(self, element):
if element in self.Elements:
@@ -867,7 +867,7 @@
self.Elements.append(element)
self.RefreshWireExclusion()
self.RefreshBoundingBox()
# Move this group of elements
def Move(self, movex, movey):
movex = max(-self.BoundingBox.x, movex)
@@ -879,7 +879,7 @@
elif element in self.WireExcluded:
element.Move(movex, movey, True)
self.RefreshBoundingBox()
# Refreshes the bounding box of this group of elements
def RefreshBoundingBox(self):
if len(self.Elements) > 0:
@@ -902,7 +902,7 @@
# Forbids to change the group position
# Returns the position of this group
def GetPosition(self, exclude_wires=False):
@@ -919,15 +919,15 @@
return self.BoundingBox.x, self.BoundingBox.y
# Forbids to change the group size
def SetSize(width, height):
# Returns the size of this group
return self.BoundingBox.width, self.BoundingBox.height
# Set size of the group elements to their minimum size
def SetBestSize(self, scaling):
max_movex = max_movey = 0
@@ -936,7 +936,7 @@
max_movex = max(max_movex, movex)
max_movey = max(max_movey, movey)
return max_movex, max_movey
# Refreshes the group elements to move defined and handle selected
def ProcessDragging(self, movex, movey, event, scaling):
handle_type, handle = self.Handle
@@ -960,17 +960,17 @@
# Change the variable that indicates if this element is highlighted
def SetHighlighted(self, highlighted):
for element in self.Elements:
element.SetHighlighted(highlighted)
def HighlightPoint(self, pos):
for element in self.Elements:
if isinstance(element, Wire):
element.HighlightPoint(pos)
# Method called when a LeftDown event have been generated
def OnLeftDown(self, event, dc, scaling):
Graphic_Element.OnLeftDown(self, event, dc, scaling)
@@ -1007,7 +1007,7 @@
class Connector(DebugDataConsumer, ToolTipProducer):
def __init__(self, parent, name, type, position, direction, negated = False, edge = "none", onlyone = False):
DebugDataConsumer.__init__(self)
@@ -1033,13 +1033,13 @@
for wire, handle in self.Wires:
def GetRedrawRect(self, movex = 0, movey = 0):
parent_pos = self.ParentBlock.GetPosition()
@@ -1072,22 +1072,22 @@
height * (self.Direction[1] - 1),
# Change the connector selection
def SetSelected(self, selected):
# Make a clone of the connector
def Clone(self, parent = None):
parent = self.ParentBlock
return Connector(parent, self.Name, self.Type, wx.Point(self.Pos[0], self.Pos[1]),
self.Direction, self.Negated)
# Returns the connector parent block
def GetParentBlock(self):
# Returns the connector type
def GetType(self, raw = False):
if self.ParentBlock.IsEndType(self.Type) or raw:
@@ -1096,7 +1096,7 @@
return self.ParentBlock.GetConnectionResultType(self, self.Type)
# Returns the connector type
def GetConnectedType(self):
if self.ParentBlock.IsEndType(self.Type):
@@ -1104,7 +1104,7 @@
elif len(self.Wires) == 1:
return self.Wires[0][0].GetOtherConnectedType(self.Wires[0][1])
# Returns the connector type
def GetConnectedRedrawRect(self, movex, movey):
@@ -1114,22 +1114,22 @@
rect = rect.Union(wire.GetRedrawRect())
# Returns if connector type is compatible with type given
def IsCompatible(self, type):
reference = self.GetType()
return self.ParentBlock.IsOfType(type, reference) or self.ParentBlock.IsOfType(reference, type)
# Changes the connector name
for wire, handle in self.Wires:
wire.SetValid(wire.IsConnectedCompatible())
# Returns the connector name
# Changes the connector name
@@ -1140,15 +1140,15 @@
self.Parent.ElementNeedRefresh(self)
def GetComputedValue(self):
if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
def GetToolTipValue(self):
return self.GetComputedValue()
def SetValue(self, value):
@@ -1161,7 +1161,7 @@
if self.ParentBlock.Visible:
self.ParentBlock.Parent.ElementNeedRefresh(self)
for wire, handle in self.Wires:
@@ -1169,12 +1169,12 @@
self.Value = self.ReceivingCurrent()
for wire, handle in self.Wires:
self.Valid &= wire.GetValid()
def ReceivingCurrent(self):
for wire, handle in self.Wires:
@@ -1184,34 +1184,34 @@
elif value == "undefined":
def SpreadCurrent(self, spreading):
for wire, handle in self.Wires:
# Changes the connector name size
def RefreshNameSize(self):
self.NameSize = self.ParentBlock.Parent.GetTextExtent(self.Name)
# Returns the connector name size
# Returns the wires connected to the connector
# Returns the parent block Id
return self.ParentBlock.GetId()
# Returns the connector relative position
def GetRelPosition(self):
# Returns the connector absolute position
def GetPosition(self, size = True):
parent_pos = self.ParentBlock.GetPosition()
@@ -1223,23 +1223,23 @@
x = parent_pos[0] + self.Pos.x
y = parent_pos[1] + self.Pos.y
# Change the connector relative position
def SetPosition(self, pos):
# Returns the connector direction
# Change the connector direction
def SetDirection(self, direction):
self.Direction = direction
# Connect a wire to this connector at the last place
def Connect(self, wire, refresh = True):
self.InsertConnect(len(self.Wires), wire, refresh)
# Connect a wire to this connector at the place given
def InsertConnect(self, idx, wire, refresh = True):
if wire not in self.Wires:
@@ -1250,14 +1250,14 @@
wire[0].ConnectEndPoint(None, self)
self.ParentBlock.RefreshModel(False)
# Returns the index of the wire given in the list of connected
def GetWireIndex(self, wire):
for i, (tmp_wire, handle) in enumerate(self.Wires):
# Unconnect a wire or all wires connected to the connector
def UnConnect(self, wire = None, unconnect = True, delete = False):
@@ -1281,11 +1281,11 @@
self.ParentBlock.RefreshModel(False)
# Returns if connector has one or more wire connected
return len(self.Wires) > 0
# Move the wires connected
def MoveConnected(self, exclude = []):
@@ -1300,21 +1300,21 @@
wire.MoveStartPoint(wx.Point(x, y))
wire.MoveEndPoint(wx.Point(x, y))
# Refreshes the model of all the wires connected
# Refreshes the parent block model
def RefreshParentBlock(self):
self.ParentBlock.RefreshModel(False)
# Highlight the parent block
def HighlightParentBlock(self, highlight):
self.ParentBlock.SetHighlighted(highlight)
self.ParentBlock.Refresh()
# Returns all the blocks connected to this connector
def GetConnectedBlocks(self):
@@ -1330,31 +1330,31 @@
# Returns the connector negated property
# Changes the connector negated property
def SetNegated(self, negated):
if self.ParentBlock.IsOfType("BOOL", self.Type):
# Returns the connector edge property
# Changes the connector edge property
if self.ParentBlock.IsOfType("BOOL", self.Type):
# assume that pointer is already inside of this connector
def ConnectionAvailable(self, direction=None, exclude=True):
wire_nums = len(self.Wires)
connector_free = (wire_nums<= 0)
connector_max_used = ((wire_nums > 0) and self.OneConnected)
if (self.Parent.CurrentLanguage in ["SFC", "LD"]) and (self.Type == "BOOL"):
@@ -1363,7 +1363,7 @@
# connector is available for new connection
connect = connector_free or not connector_max_used
return connect, connector_max_used
# Tests if the point given is near from the end point of this connector
def TestPoint(self, pt, direction=None, exclude=True):
@@ -1378,9 +1378,9 @@
height = ANCHOR_DISTANCE * 2 + abs(self.Direction[1]) * CONNECTOR_SIZE
rect = wx.Rect(x, y, width, height)
inside = rect.InsideXY(pt.x, pt.y);
# Draws the highlightment of this element if it is highlighted
def DrawHighlightment(self, dc):
scalex, scaley = dc.GetUserScale()
@@ -1393,7 +1393,7 @@
parent_pos = self.ParentBlock.GetPosition()
posx = parent_pos[0] + self.Pos.x
posy = parent_pos[1] + self.Pos.y
- xstart = parent_pos[0] + self.Pos.x
+ xstart = parent_pos[0] + self.Pos.x ystart = parent_pos[1] + self.Pos.y
if self.Direction[0] < 0:
@@ -1401,18 +1401,18 @@
xend = xstart + CONNECTOR_SIZE * self.Direction[0]
yend = ystart + CONNECTOR_SIZE * self.Direction[1]
- dc.DrawLine(round((xstart + self.Direction[0]) * scalex), round((ystart + self.Direction[1]) * scaley),
+ dc.DrawLine(round((xstart + self.Direction[0]) * scalex), round((ystart + self.Direction[1]) * scaley), round(xend * scalex), round(yend * scaley))
dc.SetLogicalFunction(wx.COPY)
dc.SetUserScale(scalex, scaley)
# 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:
AddHighlight(self.Highlights, (start, end, highlight_type))
# Removes an highlight from the connector
def RemoveHighlight(self, infos, start, end, highlight_type):
@@ -1425,7 +1425,7 @@
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):
@@ -1441,7 +1441,7 @@
for wire, handle in self.Wires:
wire.SetValid(wire.IsConnectedCompatible())
@@ -1466,19 +1466,19 @@
dc.SetPen(MiterPen(wx.BLACK))
dc.SetBrush(wx.WHITE_BRUSH)
parent_pos = self.ParentBlock.GetPosition()
if getattr(dc, "printing", False):
name_size = dc.GetTextExtent(self.Name)
name_size = self.NameSize
# If connector is negated, draw a circle
xcenter = parent_pos[0] + self.Pos.x + (CONNECTOR_SIZE * self.Direction[0]) / 2
ycenter = parent_pos[1] + self.Pos.y + (CONNECTOR_SIZE * self.Direction[1]) / 2
dc.DrawCircle(xcenter, ycenter, CONNECTOR_SIZE / 2)
- xstart = parent_pos[0] + self.Pos.x
+ xstart = parent_pos[0] + self.Pos.x ystart = parent_pos[1] + self.Pos.y
if self.Edge == "rising":
# If connector has a rising edge, draw a right arrow
@@ -1524,7 +1524,7 @@
self.ValueSize = self.ParentBlock.Parent.GetMiniTextExtent(self.ComputedValue)
if self.ValueSize is not None:
width, height = self.ValueSize
- dc.DrawText(self.ComputedValue,
+ dc.DrawText(self.ComputedValue, parent_pos[0] + self.Pos.x + CONNECTOR_SIZE * self.Direction[0] + \
width * (self.Direction[0] - 1) / 2,
parent_pos[1] + self.Pos.y + CONNECTOR_SIZE * self.Direction[1] + \
@@ -1541,7 +1541,7 @@
class Wire(Graphic_Element, DebugDataConsumer):
def __init__(self, parent, start = None, end = None):
Graphic_Element.__init__(self, parent)
@@ -1568,18 +1568,18 @@
self.ComputingType = False
self.Font = parent.GetMiniFont()
self.ErrHighlight = False
if self.StartConnected is not None and self.EndConnected is not None:
startblock = self.StartConnected.GetParentBlock()
endblock = self.EndConnected.GetParentBlock()
return [], [(startblock.GetId(), endblock.GetId())]
self.StartConnected = None
def GetRedrawRect(self, movex = 0, movey = 0):
rect = Graphic_Element.GetRedrawRect(self, movex, movey)
@@ -1607,7 +1607,7 @@
y = self.Points[middle].y - height
rect = rect.Union(wx.Rect(x, y, width, height))
def Clone(self, parent, connectors = {}, dx = 0, dy = 0):
start_connector = connectors.get(self.StartConnected, None)
end_connector = connectors.get(self.EndConnected, None)
@@ -1620,18 +1620,18 @@
wire.ConnectEndPoint(end_connector.GetPosition(), end_connector)
# Forbids to change the wire position
# Forbids to change the wire size
def SetSize(width, height):
# Forbids to et size of the group elements to their minimum size
# Moves and Resizes the element for fitting scaling
def SetBestSize(self, scaling):
@@ -1664,11 +1664,11 @@
return movex_max, movey_max
# Returns connector to which start point is connected
def GetStartConnected(self):
return self.StartConnected
# Returns connector to which start point is connected
def GetStartConnectedType(self):
if self.StartConnected and not self.ComputingType:
@@ -1677,11 +1677,11 @@
self.ComputingType = False
# Returns connector to which end point is connected
def GetEndConnected(self):
# Returns connector to which end point is connected
def GetEndConnectedType(self):
if self.EndConnected and not self.ComputingType:
@@ -1690,7 +1690,7 @@
self.ComputingType = False
def GetConnectionDirection(self):
if self.StartConnected is None and self.EndConnected is None:
@@ -1707,26 +1707,26 @@
return (-self.StartPoint[1][0], -self.StartPoint[1][1])
def GetOtherConnected(self, connector):
if self.StartConnected == connector:
return self.StartConnected
def GetOtherConnectedType(self, handle):
return self.GetEndConnectedType()
return self.GetStartConnectedType()
def IsConnectedCompatible(self):
return self.StartConnected.IsCompatible(self.GetEndConnectedType())
def SetForced(self, forced):
if self.Forced != forced:
@@ -1741,7 +1741,7 @@
if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
def GetToolTipValue(self):
return self.GetComputedValue()
@@ -1773,18 +1773,18 @@
if isinstance(value, BooleanType) and self.StartConnected is not None:
block = self.StartConnected.GetParentBlock()
# Unconnect the start and end points
self.UnConnectStartPoint()
# Delete this wire by calling the corresponding method
self.Parent.DeleteWire(self)
# Select a segment and not the whole wire. It's useful for Ladder Diagram
def SetSelectedSegment(self, segment):
# The last segment is indicated
@@ -1814,17 +1814,17 @@
self.EndConnected.SetSelected(True)
self.SelectedSegment = segment
def SetValid(self, valid):
self.StartConnected.RefreshValid()
self.EndConnected.RefreshValid()
# Reinitialize the wire points
if self.StartPoint and self.EndPoint:
@@ -1833,7 +1833,7 @@
# Refresh the wire bounding box
def RefreshBoundingBox(self):
@@ -1862,7 +1862,7 @@
self.Pos.x, self.Pos.y = minx, miny
self.Size = wx.Size(maxx - minx, maxy - miny)
self.BoundingBox = wx.Rect(minbbxx, minbbxy, maxbbxx - minbbxx + 1, maxbbxy - minbbxy + 1)
# Refresh the realpoints that permits to keep the proportionality in wire during resizing
def RefreshRealPoints(self):
@@ -1870,8 +1870,8 @@
# Calculate float relative position of each point with the minimum point
for point in self.Points:
self.RealPoints.append([float(point.x - self.Pos.x), float(point.y - self.Pos.y)])
- # Returns the wire minimum size
+ # Returns the wire minimum size @@ -1893,7 +1893,7 @@
height = MIN_SEGMENT_SIZE
return width + 1, height + 1
# Returns if the point given is on one of the wire segments
def HitTest(self, pt, connectors=True):
@@ -1903,7 +1903,7 @@
x1 = self.Points[i].x - self.Segments[0][0] * CONNECTOR_SIZE
y1 = self.Points[i].y - self.Segments[0][1] * CONNECTOR_SIZE
- x1, y1 = self.Points[i].x, self.Points[i].y
+ x1, y1 = self.Points[i].x, self.Points[i].y if i == len(self.Points) - 2 and self.EndConnected is not None:
x2 = self.Points[i + 1].x + self.Segments[-1][0] * CONNECTOR_SIZE
y2 = self.Points[i + 1].y + self.Segments[-1][1] * CONNECTOR_SIZE
@@ -1912,10 +1912,10 @@
# Calculate a rectangle around the segment
rect = wx.Rect(min(x1, x2) - ANCHOR_DISTANCE, min(y1, y2) - ANCHOR_DISTANCE,
abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE)
- test |= rect.InsideXY(pt.x, pt.y)
+ test |= rect.InsideXY(pt.x, pt.y)
- # Returns the wire start or end point if the point given is on one of them
+ # Returns the wire start or end point if the point given is on one of them # Test the wire start point
rect = wx.Rect(self.Points[0].x - ANCHOR_DISTANCE, self.Points[0].y - ANCHOR_DISTANCE,
@@ -1929,7 +1929,7 @@
if rect.InsideXY(pt.x, pt.y):
# Returns the wire segment if the point given is on it
def TestSegment(self, pt, all=False):
for i in xrange(len(self.Segments)):
@@ -1943,7 +1943,7 @@
if rect.InsideXY(pt.x, pt.y):
return i, self.Segments[i]
def SetPoints(self, points, verify=True):
@@ -1952,9 +1952,9 @@
self.StartPoint = [None, vector(self.Points[0], self.Points[1])]
self.EndPoint = [None, vector(self.Points[-1], self.Points[-2])]
# Calculate the start and end points
- self.StartPoint[0] = wx.Point(self.Points[0].x + CONNECTOR_SIZE * self.StartPoint[1][0],
+ self.StartPoint[0] = wx.Point(self.Points[0].x + CONNECTOR_SIZE * self.StartPoint[1][0], self.Points[0].y + CONNECTOR_SIZE * self.StartPoint[1][1])
- self.EndPoint[0] = wx.Point(self.Points[-1].x + CONNECTOR_SIZE * self.EndPoint[1][0],
+ self.EndPoint[0] = wx.Point(self.Points[-1].x + CONNECTOR_SIZE * self.EndPoint[1][0], self.Points[-1].y + CONNECTOR_SIZE * self.EndPoint[1][1])
self.Points[0] = self.StartPoint[0]
self.Points[-1] = self.EndPoint[0]
@@ -1979,37 +1979,37 @@
self.RefreshBoundingBox()
# Returns the position of the point indicated
def GetPoint(self, index):
if index < len(self.Points):
return self.Points[index].x, self.Points[index].y
# Returns a list of the position of all wire points
def GetPoints(self, invert = False):
points = self.VerifyPoints()
- points[0] = wx.Point(points[0].x - CONNECTOR_SIZE * self.StartPoint[1][0],
+ points[0] = wx.Point(points[0].x - CONNECTOR_SIZE * self.StartPoint[1][0], points[0].y - CONNECTOR_SIZE * self.StartPoint[1][1])
- points[-1] = wx.Point(points[-1].x - CONNECTOR_SIZE * self.EndPoint[1][0],
+ points[-1] = wx.Point(points[-1].x - CONNECTOR_SIZE * self.EndPoint[1][0], points[-1].y - CONNECTOR_SIZE * self.EndPoint[1][1])
# An inversion of the list is asked
# Returns the position of the two selected segment points
def GetSelectedSegmentPoints(self):
if self.SelectedSegment != None and len(self.Points) > 1:
return self.Points[self.SelectedSegment:self.SelectedSegment + 2]
# Returns if the selected segment is the first and/or the last of the wire
def GetSelectedSegmentConnections(self):
if self.SelectedSegment != None and len(self.Points) > 1:
return self.SelectedSegment == 0, self.SelectedSegment == len(self.Segments) - 1
# Returns the connectors on which the wire is connected
@@ -2018,7 +2018,7 @@
if self.EndConnected and self.EndPoint[1] == WEST:
connected.append(self.EndConnected)
# Returns the id of the block connected to the first or the last wire point
def GetConnectedInfos(self, index):
if index == 0 and self.StartConnected:
@@ -2026,14 +2026,14 @@
elif index == -1 and self.EndConnected:
return self.EndConnected.GetBlockId(), self.EndConnected.GetName()
# Update the wire points position by keeping at most possible the current positions
def GeneratePoints(self, realpoints = True):
# Calculate the start enad end points with the minimum segment size in the right direction
end = wx.Point(self.EndPoint[0].x + self.EndPoint[1][0] * MIN_SEGMENT_SIZE,
self.EndPoint[0].y + self.EndPoint[1][1] * MIN_SEGMENT_SIZE)
- start = wx.Point(self.StartPoint[0].x + self.StartPoint[1][0] * MIN_SEGMENT_SIZE,
+ start = wx.Point(self.StartPoint[0].x + self.StartPoint[1][0] * MIN_SEGMENT_SIZE, self.StartPoint[0].y + self.StartPoint[1][1] * MIN_SEGMENT_SIZE)
# Evaluate the point till it's the last
while i < len(self.Points) - 1:
@@ -2046,14 +2046,14 @@
# If the end point is not in the start direction, a point is added
if v_end != self.Segments[0] or v_end == self.EndPoint[1]:
self.Points.insert(1, wx.Point(start.x, start.y))
- self.Segments.insert(1, DirectionChoice((self.Segments[0][1],
+ self.Segments.insert(1, DirectionChoice((self.Segments[0][1], self.Segments[0][0]), v_end, self.EndPoint[1]))
# The current point is the second
# The previous direction and the target direction are mainly opposed, a point is added
if product(v_end, self.Segments[0]) < 0:
self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y))
- self.Segments.insert(2, DirectionChoice((self.Segments[1][1],
+ self.Segments.insert(2, DirectionChoice((self.Segments[1][1], self.Segments[1][0]), v_end, self.EndPoint[1]))
# The previous direction and the end direction are the same or they are
# perpendiculars and the end direction points towards current segment
@@ -2066,7 +2066,7 @@
# If the previous direction and the end direction are the same, a point is added
if product(self.Segments[0], self.EndPoint[1]) > 0:
self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y))
- self.Segments.insert(2, DirectionChoice((self.Segments[1][1],
+ self.Segments.insert(2, DirectionChoice((self.Segments[1][1], self.Segments[1][0]), v_end, self.EndPoint[1]))
# Current point is positioned in the middle of start point
@@ -2076,7 +2076,7 @@
if self.Segments[0][1] != 0:
self.Points[1].y = (end.y + start.y) / 2
self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y))
- self.Segments.insert(2, DirectionChoice((self.Segments[1][1],
+ self.Segments.insert(2, DirectionChoice((self.Segments[1][1], self.Segments[1][0]), v_end, self.EndPoint[1]))
# The previous direction and the end direction are perpendiculars
@@ -2093,7 +2093,7 @@
self.Segments[i - 1] = (-self.Segments[i - 1][0], -self.Segments[i - 1][1])
- # If the current point is the third, test if the second
+ # If the current point is the third, test if the second # point can be aligned with the end point
test_point = wx.Point(self.Points[1].x, self.Points[1].y)
@@ -2119,7 +2119,7 @@
if self.Segments[1][1] != 0:
self.Points[2].y = (self.Points[1].y + end.y) / 2
self.Points.insert(3, wx.Point(self.Points[2].x, self.Points[2].y))
- self.Segments.insert(3, DirectionChoice((self.Segments[2][1],
+ self.Segments.insert(3, DirectionChoice((self.Segments[2][1], self.Segments[2][0]), v_end, self.EndPoint[1]))
# Current point is aligned with end point
@@ -2140,15 +2140,15 @@
self.Points[i].y = (end.y + self.Points[i - 1].y) / 2
self.Points.insert(i + 1, wx.Point(self.Points[i].x, self.Points[i].y))
- self.Segments.insert(i + 1, DirectionChoice((self.Segments[i][1],
+ self.Segments.insert(i + 1, DirectionChoice((self.Segments[i][1], self.Segments[i][0]), v_end, self.EndPoint[1]))
# Current point is the first, and second is not mainly in the first direction
if i == 0 and product(vector(start, self.Points[1]), self.Segments[0]) < 0:
- # If first and second directions aren't perpendiculars, a point is added
+ # If first and second directions aren't perpendiculars, a point is added if product(self.Segments[0], self.Segments[1]) != 0:
self.Points.insert(1, wx.Point(start.x, start.y))
- self.Segments.insert(1, DirectionChoice((self.Segments[0][1],
+ self.Segments.insert(1, DirectionChoice((self.Segments[0][1], self.Segments[0][0]), vector(start, self.Points[1]), self.Segments[1]))
self.Points[1].x, self.Points[1].y = start.x, start.y
@@ -2165,7 +2165,7 @@
self.RefreshBoundingBox()
# Verify that two consecutive points haven't the same position
points = [point for point in self.Points]
@@ -2185,7 +2185,7 @@
self.RefreshBoundingBox()
# Moves all the wire points except the first and the last if they are connected
def Move(self, dx, dy, endpoints = False):
for i, point in enumerate(self.Points):
@@ -2195,7 +2195,7 @@
self.StartPoint[0] = self.Points[0]
self.EndPoint[0] = self.Points[-1]
# Resize the wire from position and size given
def Resize(self, x, y, width, height):
@@ -2254,25 +2254,25 @@
self.StartPoint[0] = self.Points[0]
self.EndPoint[0] = self.Points[-1]
self.GeneratePoints(False)
# Moves the wire start point and update the wire points
def MoveStartPoint(self, point):
self.StartPoint[0] = point
# Changes the wire start direction and update the wire points
def SetStartPointDirection(self, dir):
# Rotates the wire start direction by an angle of 90 degrees anticlockwise
def RotateStartPoint(self):
self.SetStartPointDirection((self.StartPoint[1][1], -self.StartPoint[1][0]))
# Connects wire start point to the connector given and moves wire start point
def ConnectStartPoint(self, point, connector):
@@ -2280,7 +2280,7 @@
self.MoveStartPoint(point)
self.StartConnected = connector
self.RefreshBoundingBox()
# Unconnects wire start point
def UnConnectStartPoint(self, delete = False):
@@ -2290,7 +2290,7 @@
self.StartConnected.UnConnect(self, unconnect = False)
self.StartConnected = None
self.RefreshBoundingBox()
# Moves the wire end point and update the wire points
def MoveEndPoint(self, point):
@@ -2303,7 +2303,7 @@
# Rotates the wire end direction by an angle of 90 degrees anticlockwise
def RotateEndPoint(self):
self.SetEndPointDirection((self.EndPoint[1][1], -self.EndPoint[1][0]))
@@ -2315,7 +2315,7 @@
self.EndConnected = connector
self.RefreshBoundingBox()
# Unconnects wire end point
def UnConnectEndPoint(self, delete = False):
@@ -2325,7 +2325,7 @@
self.EndConnected.UnConnect(self, unconnect = False)
self.RefreshBoundingBox()
# Moves the wire segment given by its index
def MoveSegment(self, idx, movex, movey, scaling):
if 0 < idx < len(self.Segments) - 1:
@@ -2356,7 +2356,7 @@
if start_y != self.Points[idx].y:
return 0, self.Points[idx].y - start_y
# Adds two points in the middle of the handled segment
handle_type, handle = self.Handle
@@ -2391,7 +2391,7 @@
self.Points.insert(segment + 4, wx.Point(p2x, p2y))
self.Segments.insert(segment + 4, dir)
# Delete the handled segment by removing the two segment points
handle_type, handle = self.Handle
@@ -2402,7 +2402,7 @@
self.Segments.pop(segment)
# Method called when a LeftDown event have been generated
def OnLeftDown(self, event, dc, scaling):
pos = GetScaledEventPosition(event, dc, scaling)
@@ -2424,7 +2424,7 @@
Graphic_Element.OnLeftDown(self, event, dc, scaling)
# Method called when a RightUp event has been generated
def OnRightUp(self, event, dc, scaling):
pos = GetScaledEventPosition(event, dc, scaling)
@@ -2437,7 +2437,7 @@
# Execute the default method for a graphic element
Graphic_Element.OnRightUp(self, event, dc, scaling)
# Method called when a LeftDClick event has been generated
def OnLeftDClick(self, event, dc, scaling):
rect = self.GetRedrawRect()
@@ -2492,7 +2492,7 @@
self.Parent.RefreshBuffer()
rect.Union(self.GetRedrawRect())
self.Parent.RefreshRect(self.Parent.GetScrolledRect(rect), False)
# Method called when a Motion event has been generated
def OnMotion(self, event, dc, scaling):
pos = GetScaledEventPosition(event, dc, scaling)
@@ -2511,7 +2511,7 @@
# Execute the default method for a graphic element
return Graphic_Element.OnMotion(self, event, dc, scaling)
# Refreshes the wire state according to move defined and handle selected
def ProcessDragging(self, movex, movey, event, scaling):
handle_type, handle = self.Handle
@@ -2572,14 +2572,14 @@
# Execute the default method for a graphic element
return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
# Refreshes the wire model
def RefreshModel(self, move=True):
if self.StartConnected and self.StartPoint[1] in [WEST, NORTH]:
self.StartConnected.RefreshParentBlock()
if self.EndConnected and self.EndPoint[1] in [WEST, NORTH]:
self.EndConnected.RefreshParentBlock()
# Change the variable that indicates if this element is highlighted
def SetHighlighted(self, highlighted):
self.Highlighted = highlighted
@@ -2587,7 +2587,7 @@
def HighlightPoint(self, pos):
start, end = self.OverStart, self.OverEnd
@@ -2602,7 +2602,7 @@
if start != self.OverStart or end != self.OverEnd:
# Draws the highlightment of this element if it is highlighted
def DrawHighlightment(self, dc):
scalex, scaley = dc.GetUserScale()
@@ -2617,31 +2617,31 @@
dc.SetLogicalFunction(wx.AND)
# Draw the start and end points if they are not connected or the mouse is over them
if len(self.Points) > 0 and (not self.StartConnected or self.OverStart):
- dc.DrawCircle(round(self.Points[0].x * scalex),
- round(self.Points[0].y * scaley),
+ dc.DrawCircle(round(self.Points[0].x * scalex), + round(self.Points[0].y * scaley), (POINT_RADIUS + 1) * scalex + 2)
if len(self.Points) > 1 and (not self.EndConnected or self.OverEnd):
dc.DrawCircle(self.Points[-1].x * scalex, self.Points[-1].y * scaley, (POINT_RADIUS + 1) * scalex + 2)
# Draw the wire lines and the last point (it seems that DrawLines stop before the last point)
- points = [wx.Point(round((self.Points[0].x - self.Segments[0][0]) * scalex),
+ points = [wx.Point(round((self.Points[0].x - self.Segments[0][0]) * scalex), round((self.Points[0].y - self.Segments[0][1]) * scaley))]
points.extend([wx.Point(round(point.x * scalex), round(point.y * scaley)) for point in self.Points[1:-1]])
- points.append(wx.Point(round((self.Points[-1].x + self.Segments[-1][0]) * scalex),
+ points.append(wx.Point(round((self.Points[-1].x + self.Segments[-1][0]) * scalex), round((self.Points[-1].y + self.Segments[-1][1]) * scaley)))
dc.SetLogicalFunction(wx.COPY)
dc.SetUserScale(scalex, scaley)
if self.StartConnected is not None:
self.StartConnected.DrawHighlightment(dc)
self.StartConnected.Draw(dc)
if self.EndConnected is not None:
self.EndConnected.DrawHighlightment(dc)
self.EndConnected.Draw(dc)
# Draws the wire lines and points
Graphic_Element.Draw(self, dc)
@@ -2750,7 +2750,7 @@
self.Pos = wx.Point(0, 0)
self.Size = wx.Size(0, 0)
# Make a clone of this comment
def Clone(self, parent, id = None, pos = None):
comment = Comment(parent, self.Content, id)
@@ -2758,19 +2758,19 @@
comment.SetPosition(pos.x, pos.y)
comment.SetSize(self.Size[0], self.Size[1])
# Method for keeping compatibility with others
# Delete this comment by calling the corresponding method
self.Parent.DeleteComment(self)
# Refresh the comment bounding box
def RefreshBoundingBox(self):
self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
# Changes the comment size
def SetSize(self, width, height):
self.Size.SetWidth(width)
@@ -2780,7 +2780,7 @@
# Returns the comment size
return self.Size.GetWidth(), self.Size.GetHeight()
# Returns the comment minimum size
dc = wx.ClientDC(self.Parent)
@@ -2793,7 +2793,7 @@
min_width = max(min_width, wordwidth)
min_height = max(min_height, wordheight)
return min_width + 20, min_height + 20
# Changes the comment position
def SetPosition(self, x, y):
@@ -2815,51 +2815,51 @@
# Returns the comment position
return self.Pos.x, self.Pos.y
def Move(self, dx, dy, connected = True):
self.RefreshBoundingBox()
# Resizes the comment with the position and the size given
def Resize(self, x, y, width, height):
self.SetSize(width, height)
# Method called when a RightUp event have been generated
def OnRightUp(self, event, dc, scaling):
self.Parent.PopupDefaultMenu()
# Refreshes the wire state according to move defined and handle selected
def ProcessDragging(self, movex, movey, event, scaling):
if self.Parent.GetDrawingMode() != FREEDRAWING_MODE and self.Parent.CurrentLanguage == "LD":
return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
# Refreshes the comment model
def RefreshModel(self, move=True):
self.Parent.RefreshCommentModel(self)
# Method called when a LeftDClick event have been generated
def OnLeftDClick(self, event, dc, scaling):
# 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()
@@ -2867,29 +2867,29 @@
dc.SetPen(MiterPen(HIGHLIGHTCOLOR))
dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
dc.SetLogicalFunction(wx.AND)
left = (self.Pos.x - 1) * scalex - 2
right = (self.Pos.x + self.Size[0] + 1) * scalex + 2
top = (self.Pos.y - 1) * scaley - 2
bottom = (self.Pos.y + self.Size[1] + 1) * scaley + 2
angle_top = (self.Pos.x + self.Size[0] - 9) * scalex + 2
angle_right = (self.Pos.y + 9) * scaley - 2
polygon = [wx.Point(left, top), wx.Point(angle_top, top),
wx.Point(right, angle_right), wx.Point(right, bottom),
dc.SetLogicalFunction(wx.COPY)
dc.SetUserScale(scalex, scaley)
# Draws the comment and its content
Graphic_Element.Draw(self, dc)
dc.SetPen(MiterPen(wx.BLACK))
dc.SetBrush(wx.WHITE_BRUSH)
# Draws the comment shape
- polygon = [wx.Point(self.Pos.x, self.Pos.y),
+ polygon = [wx.Point(self.Pos.x, self.Pos.y), wx.Point(self.Pos.x + self.Size[0] - 10, self.Pos.y),
wx.Point(self.Pos.x + self.Size[0], self.Pos.y + 10),
wx.Point(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1]),
@@ -2951,4 +2951,3 @@
if y + wordheight > self.Pos.y + self.Size[1] - 10:
--- a/graphics/LD_Objects.py Fri Aug 11 15:18:19 2017 +0300
+++ b/graphics/LD_Objects.py Mon Aug 14 19:13:01 2017 +0300
@@ -37,7 +37,7 @@
class LD_PowerRail(Graphic_Element):
# Create a new power rail
def __init__(self, parent, type, id=None, connectors=1):
Graphic_Element.__init__(self, parent)
@@ -47,12 +47,12 @@
self.Extensions = [LD_LINE_SIZE / 2, LD_LINE_SIZE / 2]
self.SetType(type, connectors)
for connector in self.Connectors:
# Make a clone of this LD_PowerRail
def Clone(self, parent, id = None, pos = None):
powerrail = LD_PowerRail(parent, self.Type, id)
@@ -65,11 +65,11 @@
for connector in self.Connectors:
powerrail.Connectors.append(connector.Clone(powerrail))
def GetConnectorTranslation(self, element):
return dict(zip([connector for connector in self.Connectors],
[connector for connector in element.Connectors]))
def GetRedrawRect(self, movex = 0, movey = 0):
rect = Graphic_Element.GetRedrawRect(self, movex, movey)
@@ -80,7 +80,7 @@
if connector.IsConnected():
rect = rect.Union(connector.GetConnectedRedrawRect(movex, movey))
# Forbids to change the power rail size
def SetSize(self, width, height):
if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
@@ -88,47 +88,47 @@
Graphic_Element.SetSize(self, LD_POWERRAIL_WIDTH, height)
# Forbids to select a power rail
def HitTest(self, pt, connectors=True):
if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
return Graphic_Element.HitTest(self, pt, connectors) or self.TestConnector(pt, exclude=False) != None
# Forbids to select a power rail
def IsInSelection(self, rect):
if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
return Graphic_Element.IsInSelection(self, rect)
# Deletes this power rail by calling the appropriate method
self.Parent.DeletePowerRail(self)
# Unconnect all connectors
for connector in self.Connectors:
connector.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
# Refresh the power rail bounding box
def RefreshBoundingBox(self):
self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
# Refresh the power rail size
self.Size = wx.Size(LD_POWERRAIL_WIDTH, max(LD_LINE_SIZE * len(self.Connectors), self.Size[1]))
self.RefreshBoundingBox()
# Returns the block minimum size
def GetMinSize(self, default=False):
height = (LD_LINE_SIZE * (len(self.Connectors) - 1)
return LD_POWERRAIL_WIDTH, height + self.Extensions[0] + self.Extensions[1]
# Add a connector or a blank to this power rail at the last place
self.InsertConnector(len(self.Connectors))
# Add a connector or a blank to this power rail at the place given
def InsertConnector(self, idx):
if self.Type == LEFTRAIL:
@@ -138,7 +138,7 @@
self.Connectors.insert(idx, connector)
# Moves the divergence connector given
def MoveConnector(self, connector, movey):
position = connector.GetRelPosition()
@@ -163,19 +163,19 @@
self.Size[1] = max(maxy + self.Extensions[1], self.Size[1])
connector.MoveConnected()
self.RefreshBoundingBox()
# Returns the index in connectors list for the connector given
def GetConnectorIndex(self, connector):
if connector in self.Connectors:
return self.Connectors.index(connector)
# Delete the connector or blank from connectors list at the index given
def DeleteConnector(self, idx):
# Refresh the positions of the power rail connectors
def RefreshConnectors(self):
scaling = self.Parent.GetScaling()
@@ -193,13 +193,13 @@
elif self.Type == RIGHTRAIL:
connector.SetPosition(wx.Point(0, position))
# Refresh the position of wires connected to power rail
def RefreshConnected(self, exclude = []):
for connector in self.Connectors:
connector.MoveConnected(exclude)
- # Returns the power rail connector that starts with the point given if it exists
+ # Returns the power rail connector that starts with the point given if it exists def GetConnector(self, position, name = None):
@@ -208,22 +208,22 @@
if name == connector.GetName():
return self.FindNearestConnector(position, [connector for connector in self.Connectors if connector is not None])
- # Returns all the power rail connectors
+ # Returns all the power rail connectors connectors = [connector for connector in self.Connectors if connector]
if self.Type == LEFTRAIL:
return {"inputs": [], "outputs": connectors}
return {"inputs": connectors, "outputs": []}
# Test if point given is on one of the power rail connectors
def TestConnector(self, pt, direction = None, exclude = True):
for connector in self.Connectors:
if connector.TestPoint(pt, direction, exclude):
# Returns the power rail type
def SetType(self, type, connectors):
if type != self.Type or len(self.Connectors) != connectors:
@@ -235,11 +235,11 @@
for connector in xrange(connectors):
# Returns the power rail type
# Method called when a LeftDown event have been generated
def OnLeftDown(self, event, dc, scaling):
@@ -253,12 +253,12 @@
self.RealConnectors = [0.5]
Graphic_Element.OnLeftDown(self, event, dc, scaling)
# Method called when a LeftUp event have been generated
def OnLeftUp(self, event, dc, scaling):
Graphic_Element.OnLeftUp(self, event, dc, scaling)
self.RealConnectors = None
# Method called when a LeftDown event have been generated
def OnRightDown(self, event, dc, scaling):
pos = GetScaledEventPosition(event, dc, scaling)
@@ -272,12 +272,12 @@
self.oldPos = GetScaledEventPosition(event, dc, scaling)
Graphic_Element.OnRightDown(self, event, dc, scaling)
# Method called when a LeftDClick event have been generated
def OnLeftDClick(self, event, dc, scaling):
# Edit the powerrail properties
self.Parent.EditPowerRailContent(self)
# Method called when a RightUp event have been generated
def OnRightUp(self, event, dc, scaling):
handle_type, handle = self.Handle
@@ -292,7 +292,7 @@
Graphic_Element.OnRightUp(self, event, dc, scaling)
self.Parent.PopupDefaultMenu()
def Resize(self, x, y, width, height):
if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
@@ -314,16 +314,16 @@
elif self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
# Refreshes the power rail model
def RefreshModel(self, move=True):
self.Parent.RefreshPowerRailModel(self)
- # If power rail has moved and power rail is of type LEFT, refresh the model
+ # If power rail has moved and power rail is of type LEFT, refresh the model # of wires connected to connectors
if move and self.Type == LEFTRAIL:
for connector in self.Connectors:
Graphic_Element.Draw(self, dc)
@@ -337,7 +337,7 @@
for connector in self.Connectors:
#-------------------------------------------------------------------------------
@@ -348,7 +348,7 @@
class LD_Contact(Graphic_Element, DebugDataConsumer):
def __init__(self, parent, type, name, id = None):
Graphic_Element.__init__(self, parent)
@@ -365,7 +365,7 @@
self.PreviousSpreading = False
if self.Input is not None:
@@ -373,13 +373,13 @@
if self.Output is not None:
def SetForced(self, forced):
if self.Forced != forced:
self.Parent.ElementNeedRefresh(self)
def SetValue(self, value):
if self.Type == CONTACT_RISING:
refresh = self.Value and not self.PreviousValue
@@ -393,7 +393,7 @@
self.Parent.ElementNeedRefresh(self)
@@ -414,7 +414,7 @@
elif not spreading and self.PreviousSpreading:
self.Output.SpreadCurrent(False)
self.PreviousSpreading = spreading
# Make a clone of this LD_Contact
def Clone(self, parent, id = None, pos = None):
contact = LD_Contact(parent, self.Type, self.Name, id)
@@ -426,10 +426,10 @@
contact.Input = self.Input.Clone(contact)
contact.Output = self.Output.Clone(contact)
def GetConnectorTranslation(self, element):
return {self.Input : element.Input, self.Output : element.Output}
def GetRedrawRect(self, movex = 0, movey = 0):
rect = Graphic_Element.GetRedrawRect(self, movex, movey)
@@ -446,29 +446,29 @@
if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, height_fac = 2)
# Forbids to change the contact size
def SetSize(self, width, height):
if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
Graphic_Element.SetSize(self, width, height)
# Delete this contact by calling the appropriate method
self.Parent.DeleteContact(self)
# Unconnect input and output
self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
# Refresh the size of text for name
def RefreshNameSize(self):
self.NameSize = self.Parent.GetTextExtent(self.Name)
# Refresh the size of text for type
def RefreshTypeSize(self):
@@ -482,7 +482,7 @@
self.TypeSize = self.Parent.GetTextExtent(typetext)
# Refresh the contact bounding box
def RefreshBoundingBox(self):
# Calculate the size of the name outside the contact
@@ -499,17 +499,17 @@
bbx_height = self.Size[1]
self.BoundingBox = wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1)
# Returns the block minimum size
# Refresh the position of wire connected to contact
def RefreshConnected(self, exclude = []):
self.Input.MoveConnected(exclude)
self.Output.MoveConnected(exclude)
- # Returns the contact connector that starts with the point given if it exists
+ # Returns the contact connector that starts with the point given if it exists def GetConnector(self, position, name = None):
@@ -519,11 +519,11 @@
if name == self.Output.GetName():
return self.FindNearestConnector(position, [self.Input, self.Output])
- # Returns input and output contact connectors
+ # Returns input and output contact connectors return {"inputs": [self.Input], "outputs": [self.Output]}
# Test if point given is on contact input or output connector
def TestConnector(self, pt, direction = None, exclude=True):
@@ -561,24 +561,24 @@
# Returns the contact type
# Method called when a LeftDClick event have been generated
def OnLeftDClick(self, event, dc, scaling):
# Edit the contact properties
self.Parent.EditContactContent(self)
# Method called when a RightUp event have been generated
def OnRightUp(self, event, dc, scaling):
self.Parent.PopupDefaultMenu()
# Refreshes the contact model
def RefreshModel(self, move=True):
self.Parent.RefreshContactModel(self)
# If contact has moved, refresh the model of wires connected to output
self.Output.RefreshWires()
# Draws the highlightment of this element if it is highlighted
def DrawHighlightment(self, dc):
scalex, scaley = dc.GetUserScale()
@@ -592,12 +592,12 @@
top = (self.Pos.y - 1) * scaley - 2
height = (self.Size[1] + 3) * scaley + 5
dc.DrawRectangle(left_left, top, width, height)
dc.DrawRectangle(right_left, top, width, height)
dc.SetLogicalFunction(wx.COPY)
dc.SetUserScale(scalex, scaley)
# Adds an highlight to the connection
def AddHighlight(self, infos, start, end, highlight_type):
highlights = self.Highlights.setdefault(infos[0], [])
@@ -606,13 +606,13 @@
AddHighlight(highlights, (start, end, highlight_type))
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:
@@ -623,11 +623,11 @@
highlights = ClearHighlights(highlight, highlight_type)
self.Highlights.pop(name)
Graphic_Element.Draw(self, dc)
- if self.Value is not None:
+ if self.Value is not None: if self.Type == CONTACT_NORMAL and self.Value or \
self.Type == CONTACT_REVERSE and not self.Value or \
self.Type == CONTACT_RISING and self.Value and not self.PreviousValue or \
@@ -643,7 +643,7 @@
dc.SetPen(MiterPen(wx.BLACK))
dc.SetBrush(wx.BLACK_BRUSH)
# Compiling contact type modifier symbol
if self.Type == CONTACT_REVERSE:
@@ -652,7 +652,7 @@
elif self.Type == CONTACT_FALLING:
if getattr(dc, "printing", False):
name_size = dc.GetTextExtent(self.Name)
@@ -661,7 +661,7 @@
name_size = self.NameSize
type_size = self.TypeSize
# Draw two rectangles for representing the contact
dc.DrawRectangle(self.Pos.x, self.Pos.y, 2, self.Size[1] + 1)
dc.DrawRectangle(self.Pos.x + self.Size[0] - 1, self.Pos.y, 2, self.Size[1] + 1)
@@ -677,7 +677,7 @@
# Draw input and output connectors
if not getattr(dc, "printing", False):
for name, highlights in self.Highlights.iteritems():
@@ -694,7 +694,7 @@
class LD_Coil(Graphic_Element):
def __init__(self, parent, type, name, id = None):
Graphic_Element.__init__(self, parent)
@@ -710,7 +710,7 @@
self.PreviousValue = False
if self.Input is not None:
@@ -718,7 +718,7 @@
if self.Output is not None:
self.PreviousValue = self.Value
@@ -729,7 +729,7 @@
self.Output.SpreadCurrent(False)
if self.Value != self.PreviousValue and self.Visible:
self.Parent.ElementNeedRefresh(self)
# Make a clone of this LD_Coil
def Clone(self, parent, id = None, pos = None):
coil = LD_Coil(parent, self.Type, self.Name, id)
@@ -741,10 +741,10 @@
coil.Input = self.Input.Clone(coil)
coil.Output = self.Output.Clone(coil)
def GetConnectorTranslation(self, element):
return {self.Input : element.Input, self.Output : element.Output}
def GetRedrawRect(self, movex = 0, movey = 0):
rect = Graphic_Element.GetRedrawRect(self, movex, movey)
@@ -756,34 +756,34 @@
if self.Output.IsConnected():
rect = rect.Union(self.Output.GetConnectedRedrawRect(movex, movey))
def ProcessDragging(self, movex, movey, event, scaling):
if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
- return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, height_fac = 2)
+ return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, height_fac = 2) # Forbids to change the Coil size
def SetSize(self, width, height):
if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
Graphic_Element.SetSize(self, width, height)
# Delete this coil by calling the appropriate method
self.Parent.DeleteCoil(self)
# Unconnect input and output
self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
# Refresh the size of text for name
def RefreshNameSize(self):
self.NameSize = self.Parent.GetTextExtent(self.Name)
# Refresh the size of text for type
def RefreshTypeSize(self):
@@ -801,7 +801,7 @@
self.TypeSize = self.Parent.GetTextExtent(typetext)
# Refresh the coil bounding box
def RefreshBoundingBox(self):
# Calculate the size of the name outside the coil
@@ -818,17 +818,17 @@
bbx_height = self.Size[1]
self.BoundingBox = wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1)
# Returns the block minimum size
# Refresh the position of wire connected to coil
def RefreshConnected(self, exclude = []):
self.Input.MoveConnected(exclude)
self.Output.MoveConnected(exclude)
- # Returns the coil connector that starts with the point given if it exists
+ # Returns the coil connector that starts with the point given if it exists def GetConnector(self, position, name = None):
@@ -838,11 +838,11 @@
if self.Output and name == self.Output.GetName():
return self.FindNearestConnector(position, [self.Input, self.Output])
- # Returns input and output coil connectors
+ # Returns input and output coil connectors return {"inputs": [self.Input], "outputs": [self.Output]}
# Test if point given is on coil input or output connector
def TestConnector(self, pt, direction = None, exclude=True):
@@ -852,7 +852,7 @@
if self.Output.TestPoint(pt, direction, exclude):
# Refresh the positions of the block connectors
def RefreshConnectors(self):
scaling = self.Parent.GetScaling()
@@ -862,7 +862,7 @@
self.Input.SetPosition(wx.Point(0, position))
self.Output.SetPosition(wx.Point(self.Size[0], position))
@@ -871,33 +871,33 @@
# Method called when a LeftDClick event have been generated
def OnLeftDClick(self, event, dc, scaling):
# Edit the coil properties
self.Parent.EditCoilContent(self)
# Method called when a RightUp event have been generated
def OnRightUp(self, event, dc, scaling):
self.Parent.PopupDefaultMenu()
# Refreshes the coil model
def RefreshModel(self, move=True):
self.Parent.RefreshCoilModel(self)
# If coil has moved, refresh the model of wires connected to output
self.Output.RefreshWires()
# Draws the highlightment of this element if it is highlighted
def DrawHighlightment(self, dc):
scalex, scaley = dc.GetUserScale()
@@ -906,19 +906,19 @@
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetLogicalFunction(wx.AND)
# Draw a two circle arcs for representing the coil
- dc.DrawEllipticArc(round(self.Pos.x * scalex),
- round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley),
- round(self.Size[0] * scalex),
+ dc.DrawEllipticArc(round(self.Pos.x * scalex), + round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley), + round(self.Size[0] * scalex), round((int(self.Size[1] * sqrt(2)) - 1) * scaley),
- dc.DrawEllipticArc(round(self.Pos.x * scalex),
- round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley),
- round(self.Size[0] * scalex),
+ dc.DrawEllipticArc(round(self.Pos.x * scalex), + round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley), + round(self.Size[0] * scalex), round((int(self.Size[1] * sqrt(2)) - 1) * scaley),
dc.SetLogicalFunction(wx.COPY)
dc.SetUserScale(scalex, scaley)
# Adds an highlight to the connection
def AddHighlight(self, infos, start, end, highlight_type):
highlights = self.Highlights.setdefault(infos[0], [])
@@ -927,13 +927,13 @@
AddHighlight(highlights, (start, end, highlight_type))
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:
@@ -944,7 +944,7 @@
highlights = ClearHighlights(highlight, highlight_type)
self.Highlights.pop(name)
Graphic_Element.Draw(self, dc)
@@ -953,8 +953,8 @@
dc.SetPen(MiterPen(wx.BLACK, 2, wx.SOLID))
dc.SetBrush(wx.TRANSPARENT_BRUSH)
- # Compiling coil type modifier symbol
+ # Compiling coil type modifier symbol if self.Type == COIL_REVERSE:
@@ -966,7 +966,7 @@
elif self.Type == COIL_FALLING:
if getattr(dc, "printing", False) and not isinstance(dc, wx.PostScriptDC):
# Draw an clipped ellipse for representing the coil
clipping_box = dc.GetClippingBox()
@@ -992,7 +992,7 @@
name_size = self.NameSize
type_size = self.TypeSize
name_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2,
self.Pos.y - (name_size[1] + 2))
@@ -1012,5 +1012,3 @@
DrawHighlightedText(dc, self.Name, highlights, name_pos[0], name_pos[1])
DrawHighlightedText(dc, typetext, highlights, type_pos[0], type_pos[1])
--- a/graphics/SFC_Objects.py Fri Aug 11 15:18:19 2017 +0300
+++ b/graphics/SFC_Objects.py Mon Aug 14 19:13:01 2017 +0300
@@ -43,7 +43,7 @@
class SFC_Step(Graphic_Element, DebugDataConsumer):
def __init__(self, parent, name, initial = False, id = None):
Graphic_Element.__init__(self, parent)
@@ -62,7 +62,7 @@
self.PreviousValue = None
self.PreviousSpreading = False
if self.Input is not None:
@@ -73,13 +73,13 @@
if self.Action is not None:
def SetForced(self, forced):
if self.Forced != forced:
self.Parent.ElementNeedRefresh(self)
def SetValue(self, value):
self.PreviousValue = self.Value
@@ -87,7 +87,7 @@
self.Parent.ElementNeedRefresh(self)
@@ -102,7 +102,7 @@
if self.Action is not None:
self.Action.SpreadCurrent(False)
self.PreviousSpreading = spreading
# Make a clone of this SFC_Step
def Clone(self, parent, id = None, name = "Step", pos = None):
step = SFC_Step(parent, name, self.Initial, id)
@@ -118,7 +118,7 @@
step.Action = self.Action.Clone(step)
def GetConnectorTranslation(self, element):
if self.Input is not None:
@@ -128,7 +128,7 @@
if self.Action is not None:
connectors[self.Action] = element.Action
def GetRedrawRect(self, movex = 0, movey = 0):
rect = Graphic_Element.GetRedrawRect(self, movex, movey)
@@ -146,11 +146,11 @@
if self.Action and self.Action.IsConnected():
rect = rect.Union(self.Action.GetConnectedRedrawRect(movex, movey))
# Delete this step by calling the appropriate method
self.Parent.DeleteStep(self)
# Unconnect input and output
@@ -159,50 +159,50 @@
self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
self.Action.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
# Refresh the size of text for name
def RefreshNameSize(self):
self.NameSize = self.Parent.GetTextExtent(self.Name)
# Add output connector to step
self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH)
self.RefreshBoundingBox()
# Remove output connector from step
self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
self.RefreshBoundingBox()
# Add output connector to step
self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True)
self.RefreshBoundingBox()
# Remove output connector from step
self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
self.RefreshBoundingBox()
# Add action connector to step
self.Action = Connector(self, "", None, wx.Point(self.Size[0], self.Size[1] / 2), EAST, onlyone = True)
self.RefreshBoundingBox()
# Remove action connector from step
self.Action.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
self.RefreshBoundingBox()
# Refresh the step bounding box
def RefreshBoundingBox(self):
# Calculate the bounding box size
@@ -222,7 +222,7 @@
bbx_height += CONNECTOR_SIZE
#self.BoundingBox = wx.Rect(self.Pos.x, bbx_y, bbx_width + 1, bbx_height + 1)
self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
# Refresh the positions of the step connectors
def RefreshConnectors(self):
scaling = self.Parent.GetScaling()
@@ -241,7 +241,7 @@
self.Action.SetPosition(wx.Point(self.Size[0], vertical_pos))
# Refresh the position of wires connected to step
def RefreshConnected(self, exclude = []):
@@ -250,8 +250,8 @@
self.Output.MoveConnected(exclude)
self.Action.MoveConnected(exclude)
- # Returns the step connector that starts with the point given if it exists
+ # Returns the step connector that starts with the point given if it exists def GetConnector(self, position, name = None):
@@ -273,12 +273,12 @@
connectors.append(self.Action)
return self.FindNearestConnector(position, connectors)
- # Returns action step connector
+ # Returns action step connector def GetActionConnector(self):
- # Returns input and output step connectors
+ # Returns input and output step connectors connectors = {"inputs": [], "outputs": []}
@@ -286,7 +286,7 @@
connectors["outputs"].append(self.Output)
# Test if point given is on step input or output connector
def TestConnector(self, pt, direction = None, exclude=True):
# Test input connector if it exists
@@ -312,7 +312,7 @@
# Returns the step initial property
# Returns the connector connected to input
def GetPreviousConnector(self):
@@ -320,7 +320,7 @@
return wires[0][0].GetOtherConnected(self.Input)
# Returns the connector connected to output
def GetNextConnector(self):
@@ -328,7 +328,7 @@
return wires[0][0].GetOtherConnected(self.Output)
# Returns the connector connected to action
def GetActionConnected(self):
@@ -336,7 +336,7 @@
return wires[0][0].GetOtherConnected(self.Action)
# Returns the number of action line
def GetActionExtraLineNumber(self):
@@ -346,7 +346,7 @@
action_block = wires[0][0].GetOtherConnected(self.Action).GetParentBlock()
return max(0, action_block.GetLineNumber() - 1)
# Returns the step minimum size
text_width, text_height = self.Parent.GetTextExtent(self.Name)
@@ -354,7 +354,7 @@
return text_width + 14, text_height + 14
return text_width + 10, text_height + 10
def UpdateSize(self, width, height):
diffx = self.Size.GetWidth() / 2 - width / 2
@@ -365,7 +365,7 @@
self.RefreshOutputPosition((0, diffy))
# Align input element with this step
def RefreshInputPosition(self):
@@ -382,7 +382,7 @@
input_block.MoveActionBlock((diffx, 0))
input_block.Move(diffx, 0)
input_block.RefreshInputPosition()
# Align output element with this step
def RefreshOutputPosition(self, move = None):
@@ -422,7 +422,7 @@
output_block.MoveActionBlock((diffx, 0))
output_block.Move(diffx, 0)
output_block.RefreshOutputPosition()
# Refresh action element with this step
def MoveActionBlock(self, move):
@@ -432,24 +432,24 @@
action_block = wires[0][0].GetOtherConnected(self.Action).GetParentBlock()
action_block.Move(move[0], move[1], self.Parent.Wires)
wires[0][0].Move(move[0], move[1], True)
# Resize the divergence from position and size given
def Resize(self, x, y, width, height):
if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
self.UpdateSize(width, height)
Graphic_Element.Resize(self, x, y, width, height)
# Method called when a LeftDClick event have been generated
def OnLeftDClick(self, event, dc, scaling):
# Edit the step properties
self.Parent.EditStepContent(self)
# Method called when a RightUp event have been generated
def OnRightUp(self, event, dc, scaling):
# Popup the menu with special items for a step
self.Parent.PopupDefaultMenu()
# Refreshes the step state according to move defined and handle selected
def ProcessDragging(self, movex, movey, event, scaling):
handle_type, handle = self.Handle
@@ -477,17 +477,17 @@
return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
# Refresh input element model
def RefreshInputModel(self):
input = self.GetPreviousConnector()
input_block = input.GetParentBlock()
input_block.RefreshModel(False)
if not isinstance(input_block, SFC_Divergence):
input_block.RefreshInputModel()
# Refresh output element model
def RefreshOutputModel(self, move=False):
@@ -497,7 +497,7 @@
output_block.RefreshModel(False)
if not isinstance(output_block, SFC_Divergence) or move:
output_block.RefreshOutputModel(move)
# Refreshes the step model
def RefreshModel(self, move=True):
self.Parent.RefreshStepModel(self)
@@ -513,21 +513,21 @@
self.RefreshOutputModel(self.Initial)
self.Output.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):
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)
Graphic_Element.Draw(self, dc)
@@ -541,12 +541,12 @@
dc.SetPen(MiterPen(wx.BLACK))
dc.SetBrush(wx.WHITE_BRUSH)
if getattr(dc, "printing", False):
name_size = dc.GetTextExtent(self.Name)
name_size = self.NameSize
# Draw two rectangles for representing the step
dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
@@ -562,10 +562,10 @@
if not getattr(dc, "printing", False):
DrawHighlightedText(dc, self.Name, self.Highlights, name_pos[0], name_pos[1])
#-------------------------------------------------------------------------------
# Sequencial Function Chart Transition
@@ -576,7 +576,7 @@
class SFC_Transition(Graphic_Element, DebugDataConsumer):
# Create a new transition
def __init__(self, parent, type = "reference", condition = None, priority = 0, id = None):
Graphic_Element.__init__(self, parent)
@@ -593,7 +593,7 @@
self.PreviousValue = None
self.PreviousSpreading = False
if self.Input is not None:
@@ -604,13 +604,13 @@
if self.Type == "connection" and self.Condition is not None:
def SetForced(self, forced):
if self.Forced != forced:
self.Parent.ElementNeedRefresh(self)
def SetValue(self, value):
self.PreviousValue = self.Value
@@ -618,7 +618,7 @@
self.Parent.ElementNeedRefresh(self)
@@ -629,7 +629,7 @@
elif not spreading and self.PreviousSpreading:
self.Output.SpreadCurrent(False)
self.PreviousSpreading = spreading
# Make a clone of this SFC_Transition
def Clone(self, parent, id = None, pos = None):
transition = SFC_Transition(parent, self.Type, self.Condition, self.Priority, id)
@@ -643,13 +643,13 @@
if self.Type == "connection":
transition.Condition = self.Condition.Clone(transition)
def GetConnectorTranslation(self, element):
connectors = {self.Input : element.Input, self.Output : element.Output}
if self.Type == "connection" and self.Condition is not None:
connectors[self.Condition] = element.Condition
def GetRedrawRect(self, movex = 0, movey = 0):
rect = Graphic_Element.GetRedrawRect(self, movex, movey)
@@ -665,17 +665,17 @@
if self.Type == "connection" and self.Condition.IsConnected():
rect = rect.Union(self.Condition.GetConnectedRedrawRect(movex, movey))
# Forbids to change the transition size
def SetSize(self, width, height):
if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
Graphic_Element.SetSize(self, width, height)
# Forbids to resize the transition
def Resize(self, x, y, width, height):
if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
Graphic_Element.Resize(self, x, y, width, height)
# Refresh the size of text for name
def RefreshConditionSize(self):
if self.Type != "connection":
@@ -683,7 +683,7 @@
self.ConditionSize = self.Parent.GetTextExtent(self.Condition)
self.ConditionSize = self.Parent.GetTextExtent("Transition")
# Refresh the size of text for name
def RefreshPrioritySize(self):
@@ -694,14 +694,14 @@
# Delete this transition by calling the appropriate method
self.Parent.DeleteTransition(self)
# Unconnect input and output
self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
if self.Type == "connection":
self.Condition.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
# Returns if the point given is in the bounding box
def HitTest(self, pt, connectors=True):
if self.Type != "connection":
@@ -715,7 +715,7 @@
return test_text or Graphic_Element.HitTest(self, pt, connectors)
# Refresh the transition bounding box
def RefreshBoundingBox(self):
bbx_x, bbx_y, bbx_width, bbx_height = self.Pos.x, self.Pos.y, self.Size[0], self.Size[1]
@@ -733,21 +733,21 @@
bbx_y = min(bbx_y, self.Pos.y - max(0, (text_height - self.Size[1]) / 2))
bbx_height = max(bbx_height, self.Pos.y - bbx_y + (self.Size[1] + text_height) / 2)
self.BoundingBox = wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1)
# Returns the connector connected to input
def GetPreviousConnector(self):
wires = self.Input.GetWires()
return wires[0][0].GetOtherConnected(self.Input)
# Returns the connector connected to output
def GetNextConnector(self):
wires = self.Output.GetWires()
return wires[0][0].GetOtherConnected(self.Output)
# Refresh the positions of the transition connectors
def RefreshConnectors(self):
scaling = self.Parent.GetScaling()
@@ -763,15 +763,15 @@
if self.Type == "connection":
self.Condition.SetPosition(wx.Point(0, vertical_pos))
# Refresh the position of the wires connected to transition
def RefreshConnected(self, exclude = []):
self.Input.MoveConnected(exclude)
self.Output.MoveConnected(exclude)
if self.Type == "connection":
self.Condition.MoveConnected(exclude)
- # Returns the transition connector that starts with the point given if it exists
+ # Returns the transition connector that starts with the point given if it exists def GetConnector(self, position, name = None):
@@ -786,17 +786,17 @@
if self.Type == "connection":
connectors.append(self.Condition)
return self.FindNearestConnector(position, connectors)
# Returns the transition condition connector
def GetConditionConnector(self):
if self.Type == "connection":
# Returns input and output transition connectors
return {"inputs": [self.Input], "outputs": [self.Output]}
# Test if point given is on transition input or output connector
def TestConnector(self, pt, direction = None, exclude=True):
@@ -814,7 +814,7 @@
def SetType(self, type, condition = None):
if self.Type == "connection":
- self.Condition.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
+ self.Condition.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) self.Condition = Connector(self, "", "BOOL", wx.Point(0, self.Size[1] / 2), WEST)
@@ -829,7 +829,7 @@
self.Condition = condition
self.RefreshConditionSize()
self.RefreshBoundingBox()
# Returns the transition type
@@ -839,7 +839,7 @@
self.RefreshPrioritySize()
self.RefreshBoundingBox()
# Returns the transition type
@@ -849,11 +849,11 @@
if self.Type != "connection":
# Returns the transition minimum size
return SFC_TRANSITION_SIZE
# Align input element with this step
def RefreshInputPosition(self):
wires = self.Input.GetWires()
@@ -870,7 +870,7 @@
input_block.MoveActionBlock((diffx, 0))
input_block.Move(diffx, 0)
input_block.RefreshInputPosition()
# Align output element with this step
def RefreshOutputPosition(self, move = None):
wires = self.Output.GetWires()
@@ -902,12 +902,12 @@
def OnLeftDClick(self, event, dc, scaling):
# Edit the transition properties
self.Parent.EditTransitionContent(self)
# Method called when a RightUp event have been generated
def OnRightUp(self, event, dc, scaling):
# Popup the menu with special items for a step
self.Parent.PopupDefaultMenu()
# Refreshes the transition state according to move defined and handle selected
def ProcessDragging(self, movex, movey, event, scaling):
if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
@@ -920,7 +920,7 @@
return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac = 2, height_fac = 2)
# Refresh input element model
def RefreshInputModel(self):
if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
@@ -930,7 +930,7 @@
input_block.RefreshModel(False)
if not isinstance(input_block, SFC_Divergence):
input_block.RefreshInputModel()
# Refresh output element model
def RefreshOutputModel(self, move=False):
output = self.GetNextConnector()
@@ -939,7 +939,7 @@
output_block.RefreshModel(False)
if not isinstance(output_block, SFC_Divergence) or move:
output_block.RefreshOutputModel(move)
# Refreshes the transition model
def RefreshModel(self, move=True):
self.Parent.RefreshTransitionModel(self)
@@ -950,20 +950,20 @@
self.RefreshOutputModel()
self.Output.RefreshWires()
# 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:
@@ -974,7 +974,7 @@
highlights = ClearHighlights(highlight, highlight_type)
self.Highlights.pop(name)
Graphic_Element.Draw(self, dc)
@@ -991,7 +991,7 @@
dc.SetPen(MiterPen(wx.BLACK))
dc.SetBrush(wx.BLACK_BRUSH)
if getattr(dc, "printing", False):
if self.Type != "connection":
condition_size = dc.GetTextExtent(self.Condition)
@@ -1002,14 +1002,14 @@
condition_size = self.ConditionSize
priority_size = self.PrioritySize
# Draw plain rectangle for representing the transition
- dc.DrawRectangle(self.Pos.x,
- self.Pos.y + (self.Size[1] - SFC_TRANSITION_SIZE[1])/2,
+ dc.DrawRectangle(self.Pos.x, + self.Pos.y + (self.Size[1] - SFC_TRANSITION_SIZE[1])/2, SFC_TRANSITION_SIZE[1] + 1)
vertical_line_x = self.Input.GetPosition()[0]
- dc.DrawLine(vertical_line_x, self.Pos.y, vertical_line_x, self.Pos.y + self.Size[1] + 1)
+ dc.DrawLine(vertical_line_x, self.Pos.y, vertical_line_x, self.Pos.y + self.Size[1] + 1) # Draw transition condition
if self.Type != "connection":
@@ -1028,7 +1028,7 @@
if self.Type == "connection":
if not getattr(dc, "printing", False):
for name, highlights in self.Highlights.iteritems():
@@ -1046,7 +1046,7 @@
class SFC_Divergence(Graphic_Element):
# Create a new divergence
def __init__(self, parent, type, number = 2, id = None):
Graphic_Element.__init__(self, parent)
@@ -1068,7 +1068,7 @@
self.Outputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True)]
self.PreviousValue = None
for input in self.Inputs:
@@ -1076,7 +1076,7 @@
for output in self.Outputs:
self.PreviousValue = self.Value
@@ -1102,7 +1102,7 @@
self.Parent.ElementNeedRefresh(self)
for output in self.Outputs:
output.SpreadCurrent(False)
# Make a clone of this SFC_Divergence
def Clone(self, parent, id = None, pos = None):
divergence = SFC_Divergence(parent, self.Type, max(len(self.Inputs), len(self.Outputs)), id)
@@ -1114,10 +1114,10 @@
divergence.Inputs = [input.Clone(divergence) for input in self.Inputs]
divergence.Outputs = [output.Clone(divergence) for output in self.Outputs]
def GetConnectorTranslation(self, element):
return dict(zip(self.Inputs + self.Outputs, element.Inputs + element.Outputs))
def GetRedrawRect(self, movex = 0, movey = 0):
rect = Graphic_Element.GetRedrawRect(self, movex, movey)
@@ -1129,27 +1129,27 @@
rect = rect.Union(output.GetConnectedRedrawRect(movex, movey))
# Forbids to resize the divergence
def Resize(self, x, y, width, height):
if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
Graphic_Element.Resize(self, x, 0, width, self.GetMinSize()[1])
# Delete this divergence by calling the appropriate method
self.Parent.DeleteDivergence(self)
# Returns the divergence type
# Unconnect input and output
for input in self.Inputs:
input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
for output in self.Outputs:
output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
# Add a branch to the divergence
if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
@@ -1168,7 +1168,7 @@
connector = Connector(self, "", None, wx.Point(maxx + SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone = True)
self.Inputs.append(connector)
self.MoveConnector(connector, SFC_DEFAULT_SEQUENCE_INTERVAL)
# Remove a branch from the divergence
def RemoveBranch(self, connector):
if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
@@ -1179,41 +1179,41 @@
if connector in self.Inputs and len(self.Inputs) > 2:
self.Inputs.remove(connector)
self.MoveConnector(self.Inputs[0], 0)
# Remove the handled branch from the divergence
def RemoveHandledBranch(self):
handle_type, handle = self.Handle
if handle_type == HANDLE_CONNECTOR:
handle.UnConnect(delete=True)
self.RemoveBranch(handle)
# Return the number of branches for the divergence
def GetBranchNumber(self):
if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]:
# Returns if the point given is in the bounding box
def HitTest(self, pt, connectors=True):
return self.BoundingBox.InsideXY(pt.x, pt.y) or self.TestConnector(pt, exclude=False) != None
# Refresh the divergence bounding box
def RefreshBoundingBox(self):
if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]:
- self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y,
+ self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]:
- self.BoundingBox = wx.Rect(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y,
+ self.BoundingBox = wx.Rect(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y, self.Size[0] + 2 * SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Size[1] + 1)
# Refresh the position of wires connected to divergence
def RefreshConnected(self, exclude = []):
for input in self.Inputs:
input.MoveConnected(exclude)
for output in self.Outputs:
output.MoveConnected(exclude)
# Moves the divergence connector given
def MoveConnector(self, connector, movex):
position = connector.GetRelPosition()
@@ -1241,8 +1241,8 @@
self.Size[0] = maxx - minx
connector.MoveConnected()
self.RefreshBoundingBox()
- # Returns the divergence connector that starts with the point given if it exists
+ # Returns the divergence connector that starts with the point given if it exists def GetConnector(self, position, name = None):
@@ -1254,11 +1254,11 @@
if name == output.GetName():
return self.FindNearestConnector(position, self.Inputs + self.Outputs)
- # Returns input and output divergence connectors
+ # Returns input and output divergence connectors return {"inputs": self.Inputs, "outputs": self.Outputs}
# Test if point given is on divergence input or output connector
def TestConnector(self, pt, direction = None, exclude=True):
@@ -1270,7 +1270,7 @@
if output.TestPoint(pt, direction, exclude):
# Changes the divergence size
def SetSize(self, width, height):
height = self.GetMinSize()[1]
@@ -1290,7 +1290,7 @@
self.Size = wx.Size(width, height)
self.RefreshBoundingBox()
# Returns the divergence minimum size
def GetMinSize(self, default=False):
@@ -1304,7 +1304,7 @@
elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]:
# Refresh the position of the block connected to connector
def RefreshConnectedPosition(self, connector):
wires = connector.GetWires()
@@ -1340,7 +1340,7 @@
self.RefreshOutputPosition((0, diffy))
for input in self.Inputs:
# Align output element with this divergence
def RefreshOutputPosition(self, move = None):
@@ -1359,7 +1359,7 @@
if not isinstance(output_block, SFC_Divergence) or output_block.GetConnectors()["inputs"].index(output) == 0:
output_block.Move(move[0], move[1], self.Parent.Wires)
output_block.RefreshOutputPosition(move)
# Method called when a LeftDown event have been generated
def OnLeftDown(self, event, dc, scaling):
self.RealConnectors = {"Inputs":[],"Outputs":[]}
@@ -1370,12 +1370,12 @@
position = output.GetRelPosition()
self.RealConnectors["Outputs"].append(float(position.x)/float(self.Size[0]))
Graphic_Element.OnLeftDown(self, event, dc, scaling)
# Method called when a LeftUp event have been generated
def OnLeftUp(self, event, dc, scaling):
Graphic_Element.OnLeftUp(self, event, dc, scaling)
self.RealConnectors = None
# Method called when a RightDown event have been generated
def OnRightDown(self, event, dc, scaling):
pos = GetScaledEventPosition(event, dc, scaling)
@@ -1389,7 +1389,7 @@
self.oldPos = GetScaledEventPosition(event, dc, scaling)
Graphic_Element.OnRightDown(self, event, dc, scaling)
# Method called when a RightUp event have been generated
def OnRightUp(self, event, dc, scaling):
pos = GetScaledEventPosition(event, dc, scaling)
@@ -1414,7 +1414,7 @@
# Popup the divergence menu without delete branch
self.Parent.PopupDivergenceMenu(False)
# Refreshes the divergence state according to move defined and handle selected
def ProcessDragging(self, movex, movey, event, scaling):
handle_type, handle = self.Handle
@@ -1430,7 +1430,7 @@
elif self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
# Refresh output element model
def RefreshOutputModel(self, move=False):
if move and self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
@@ -1442,7 +1442,7 @@
output_block.RefreshModel(False)
if not isinstance(output_block, SFC_Divergence) or move:
output_block.RefreshOutputModel(move)
# Refreshes the divergence model
def RefreshModel(self, move=True):
self.Parent.RefreshDivergenceModel(self)
@@ -1453,7 +1453,7 @@
for output in self.Outputs:
# Draws the highlightment of this element if it is highlighted
def DrawHighlightment(self, dc):
scalex, scaley = dc.GetUserScale()
@@ -1467,13 +1467,13 @@
if self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]:
posx -= SFC_SIMULTANEOUS_SEQUENCE_EXTRA
width += SFC_SIMULTANEOUS_SEQUENCE_EXTRA * 2
- dc.DrawRectangle(int(round((posx - 1) * scalex)) - 2,
- int(round((self.Pos.y - 1) * scaley)) - 2,
- int(round((width + 3) * scalex)) + 5,
+ dc.DrawRectangle(int(round((posx - 1) * scalex)) - 2, + int(round((self.Pos.y - 1) * scaley)) - 2, + int(round((width + 3) * scalex)) + 5, int(round((self.Size.height + 3) * scaley)) + 5)
dc.SetLogicalFunction(wx.COPY)
dc.SetUserScale(scalex, scaley)
Graphic_Element.Draw(self, dc)
@@ -1487,16 +1487,16 @@
if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]:
dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]:
- dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y,
+ dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y, self.Pos.x + self.Size[0] + SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Pos.y)
- dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y + self.Size[1],
+ dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y + self.Size[1], self.Pos.x + self.Size[0] + SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Pos.y + self.Size[1])
# Draw inputs and outputs connectors
for input in self.Inputs:
for output in self.Outputs:
#-------------------------------------------------------------------------------
# Sequencial Function Chart Jump to Step
@@ -1507,7 +1507,7 @@
class SFC_Jump(Graphic_Element):
def __init__(self, parent, target, id = None):
Graphic_Element.__init__(self, parent)
@@ -1519,19 +1519,19 @@
self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone = True)
self.PreviousValue = None
if self.Input is not None:
self.PreviousValue = self.Value
self.Value = self.Input.ReceivingCurrent()
if self.Value != self.PreviousValue and self.Visible:
self.Parent.ElementNeedRefresh(self)
# Make a clone of this SFC_Jump
def Clone(self, parent, id = None, pos = None):
jump = SFC_Jump(parent, self.Target, id)
@@ -1542,10 +1542,10 @@
jump.SetPosition(self.Pos.x, self.Pos.y)
jump.Input = self.Input.Clone(jump)
def GetConnectorTranslation(self, element):
return {self.Input : element.Input}
def GetRedrawRect(self, movex = 0, movey = 0):
rect = Graphic_Element.GetRedrawRect(self, movex, movey)
@@ -1555,29 +1555,29 @@
if self.Input.IsConnected():
rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey))
# Forbids to change the jump size
def SetSize(self, width, height):
if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
Graphic_Element.SetSize(self, width, height)
def Resize(self, x, y, width, height):
if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
Graphic_Element.Resize(self, x, y, width, height)
# Delete this jump by calling the appropriate method
self.Parent.DeleteJump(self)
self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
# Refresh the size of text for target
def RefreshTargetSize(self):
self.TargetSize = self.Parent.GetTextExtent(self.Target)
# Returns if the point given is in the bounding box
def HitTest(self, pt, connectors=True):
# Calculate the bounding box of the condition outside the transition
@@ -1587,22 +1587,22 @@
return text_bbx.InsideXY(pt.x, pt.y) or Graphic_Element.HitTest(self, pt, connectors)
# Refresh the jump bounding box
def RefreshBoundingBox(self):
text_width, text_height = self.Parent.GetTextExtent(self.Target)
# Calculate the bounding box size
bbx_width = self.Size[0] + 2 + text_width
- self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y - CONNECTOR_SIZE,
+ self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y - CONNECTOR_SIZE, bbx_width + 1, self.Size[1] + CONNECTOR_SIZE + 1)
# Returns the connector connected to input
def GetPreviousConnector(self):
wires = self.Input.GetWires()
return wires[0][0].GetOtherConnected(self.Input)
# Refresh the element connectors position
def RefreshConnectors(self):
scaling = self.Parent.GetScaling()
@@ -1611,41 +1611,41 @@
horizontal_pos = round(float(self.Pos.x + horizontal_pos) / float(scaling[0])) * scaling[0] - self.Pos.x
self.Input.SetPosition(wx.Point(horizontal_pos, 0))
# Refresh the position of wires connected to jump
def RefreshConnected(self, exclude = []):
self.Input.MoveConnected(exclude)
- # Returns input jump connector
+ # Returns input jump connector def GetConnector(self, position = None, name = None):
- # Returns all the jump connectors
+ # Returns all the jump connectors return {"inputs": [self.Input], "outputs": []}
# Test if point given is on jump input connector
def TestConnector(self, pt, direction = None, exclude = True):
if self.Input and self.Input.TestPoint(pt, direction, exclude):
# Changes the jump target
def SetTarget(self, target):
self.RefreshBoundingBox()
# Returns the jump target
# Returns the jump minimum size
# Align input element with this jump
def RefreshInputPosition(self):
@@ -1662,21 +1662,21 @@
input_block.MoveActionBlock((diffx, 0))
input_block.Move(diffx, 0)
input_block.RefreshInputPosition()
# Can't align output element, because there is no output
def RefreshOutputPosition(self, move = None):
# Method called when a LeftDClick event have been generated
def OnLeftDClick(self, event, dc, scaling):
# Edit the jump properties
self.Parent.EditJumpContent(self)
# Method called when a RightUp event have been generated
def OnRightUp(self, event, dc, scaling):
self.Parent.PopupDefaultMenu()
# Refreshes the jump state according to move defined and handle selected
def ProcessDragging(self, movex, movey, event, scaling):
if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
@@ -1688,7 +1688,7 @@
return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac = 2)
# Refresh input element model
def RefreshInputModel(self):
if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
@@ -1698,32 +1698,32 @@
input_block.RefreshModel(False)
if not isinstance(input_block, SFC_Divergence):
input_block.RefreshInputModel()
# Refresh output element model
def RefreshOutputModel(self, move=False):
# Refreshes the jump model
def RefreshModel(self, move=True):
self.Parent.RefreshJumpModel(self)
if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
# 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:
AddHighlight(self.Highlights, (start, end, highlight_type))
# Removes an highlight from the variable
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 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):
scalex, scaley = dc.GetUserScale()
@@ -1731,16 +1731,16 @@
dc.SetPen(MiterPen(HIGHLIGHTCOLOR))
dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
dc.SetLogicalFunction(wx.AND)
- points = [wx.Point(int(round((self.Pos.x - 2) * scalex)) - 3,
+ points = [wx.Point(int(round((self.Pos.x - 2) * scalex)) - 3, int(round((self.Pos.y - 2) * scaley)) - 2),
- wx.Point(int(round((self.Pos.x + self.Size[0] + 2) * scalex)) + 4,
+ wx.Point(int(round((self.Pos.x + self.Size[0] + 2) * scalex)) + 4, int(round((self.Pos.y - 2) * scaley)) - 2),
- wx.Point(int(round((self.Pos.x + self.Size[0] / 2) * scalex)),
+ wx.Point(int(round((self.Pos.x + self.Size[0] / 2) * scalex)), int(round((self.Pos.y + self.Size[1] + 3) * scaley)) + 4)]
dc.SetLogicalFunction(wx.COPY)
dc.SetUserScale(scalex, scaley)
Graphic_Element.Draw(self, dc)
@@ -1750,12 +1750,12 @@
dc.SetPen(MiterPen(wx.BLACK))
dc.SetBrush(wx.BLACK_BRUSH)
if getattr(dc, "printing", False):
target_size = dc.GetTextExtent(self.Target)
target_size = self.TargetSize
# Draw plain rectangle for representing the divergence
dc.DrawLine(self.Pos.x + self.Size[0] / 2, self.Pos.y, self.Pos.x + self.Size[0] / 2, self.Pos.y + self.Size[1])
points = [wx.Point(self.Pos.x, self.Pos.y),
@@ -1769,10 +1769,10 @@
if not getattr(dc, "printing", False):
DrawHighlightedText(dc, self.Target, self.Highlights, target_pos[0], target_pos[1])
#-------------------------------------------------------------------------------
# Sequencial Function Chart Action Block
@@ -1783,7 +1783,7 @@
class SFC_ActionBlock(Graphic_Element):
# Create a new action block
def __init__(self, parent, actions = [], id = None):
Graphic_Element.__init__(self, parent)
@@ -1796,19 +1796,19 @@
self.PreviousValue = None
if self.Input is not None:
self.PreviousValue = self.Value
self.Value = self.Input.ReceivingCurrent()
if self.Value != self.PreviousValue and self.Visible:
self.Parent.ElementNeedRefresh(self)
# Make a clone of this SFC_ActionBlock
def Clone(self, parent, id = None, pos = None):
actions = [action.copy() for action in self.Actions]
@@ -1820,10 +1820,10 @@
action_block.SetPosition(self.Pos.x, self.Pos.y)
action_block.Input = self.Input.Clone(action_block)
def GetConnectorTranslation(self, element):
return {self.Input : element.Input}
def GetRedrawRect(self, movex = 0, movey = 0):
rect = Graphic_Element.GetRedrawRect(self, movex, movey)
@@ -1833,17 +1833,17 @@
if self.Input.IsConnected():
rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey))
# Returns the number of action lines
if len(self.Actions) > 0:
return self.Size[1] / len(self.Actions)
return SFC_ACTION_MIN_SIZE[1]
# Forbids to resize the action block
def Resize(self, x, y, width, height):
if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
@@ -1851,38 +1851,38 @@
self.SetSize(width, self.Size[1])
Graphic_Element.Resize(self, x, y, width, height)
# Delete this action block by calling the appropriate method
self.Parent.DeleteActionBlock(self)
# Unconnect input and output
self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
# Refresh the action block bounding box
def RefreshBoundingBox(self):
self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
# Refresh the position of wires connected to action block
def RefreshConnected(self, exclude = []):
self.Input.MoveConnected(exclude)
- # Returns input action block connector
+ # Returns input action block connector def GetConnector(self, position = None, name = None):
- # Returns all the action block connectors
+ # Returns all the action block connectors return {"inputs": [self.Input], "outputs": []}
# Test if point given is on action block input connector
def TestConnector(self, pt, direction = None, exclude = True):
if self.Input.TestPoint(pt, direction, exclude):
# Refresh the element connectors position
def RefreshConnectors(self):
scaling = self.Parent.GetScaling()
@@ -1891,7 +1891,7 @@
vertical_pos = round(float(self.Pos.y + vertical_pos) / float(scaling[1])) * scaling[1] - self.Pos.y
self.Input.SetPosition(wx.Point(0, vertical_pos))
# Changes the action block actions
def SetActions(self, actions):
@@ -1931,25 +1931,25 @@
input_block = wires[0][0].GetOtherConnected(self.Input).GetParentBlock()
input_block.RefreshOutputPosition()
input_block.RefreshOutputModel(True)
# Returns the action block actions
# Returns the action block minimum size
# Method called when a LeftDClick event have been generated
def OnLeftDClick(self, event, dc, scaling):
# Edit the action block properties
self.Parent.EditActionBlockContent(self)
# Method called when a RightUp event have been generated
def OnRightUp(self, event, dc, scaling):
self.Parent.PopupDefaultMenu()
# Refreshes the action block state according to move defined and handle selected
def ProcessDragging(self, movex, movey, event, scaling):
if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
@@ -1970,18 +1970,18 @@
return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
# Refreshes the action block model
def RefreshModel(self, move=True):
self.Parent.RefreshActionBlockModel(self)
# Adds an highlight to the variable
def AddHighlight(self, infos, start, end, highlight_type):
if infos[0] == "action" and infos[1] < len(self.Actions):
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):
@@ -1991,7 +1991,7 @@
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:
@@ -2006,7 +2006,7 @@
action_highlights.pop(name)
if len(action_highlights) == 0:
self.Highlights.pop(number)
Graphic_Element.Draw(self, dc)
@@ -2018,14 +2018,14 @@
colsize = [self.ColSize[0], self.Size[0] - self.ColSize[0] - self.ColSize[2], self.ColSize[2]]
# Draw plain rectangle for representing the action block
dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
- dc.DrawLine(self.Pos.x + colsize[0], self.Pos.y,
+ dc.DrawLine(self.Pos.x + colsize[0], self.Pos.y, self.Pos.x + colsize[0], self.Pos.y + self.Size[1])
- dc.DrawLine(self.Pos.x + colsize[0] + colsize[1], self.Pos.y,
+ dc.DrawLine(self.Pos.x + colsize[0] + colsize[1], self.Pos.y, self.Pos.x + colsize[0] + colsize[1], self.Pos.y + self.Size[1])
line_size = self.GetLineSize()
for i, action in enumerate(self.Actions):
- dc.DrawLine(self.Pos.x, self.Pos.y + i * line_size,
+ dc.DrawLine(self.Pos.x, self.Pos.y + i * line_size, self.Pos.x + self.Size[0], self.Pos.y + i * line_size)
qualifier_size = dc.GetTextExtent(action.qualifier)
if action.duration != "":
@@ -2048,7 +2048,7 @@
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 not getattr(dc, "printing", False):
action_highlights = self.Highlights.get(i, {})
for name, attribute_highlights in action_highlights.iteritems():
@@ -2060,7 +2060,6 @@
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])
--- a/plcopen/plcopen.py Fri Aug 11 15:18:19 2017 +0300
+++ b/plcopen/plcopen.py Mon Aug 14 19:13:01 2017 +0300
@@ -49,14 +49,14 @@
VarOrder = ["Local","Temp","Input","Output","InOut","External","Global","Access"]
-Define which action qualifier must be associated with a duration
+Define which action qualifier must be associated with a duration -QualifierList = OrderedDict([("N", False), ("R", False), ("S", False),
- ("L", True), ("D", True), ("P", False), ("P0", False),
+QualifierList = OrderedDict([("N", False), ("R", False), ("S", False), + ("L", True), ("D", True), ("P", False), ("P0", False), ("P1", False), ("SD", True), ("DS", True), ("SL", True)])
-FILTER_ADDRESS_MODEL = "(%%[IQM](?:[XBWDL])?)(%s)((?:\.[0-9]+)*)"
+FILTER_ADDRESS_MODEL = "(%%[IQM](?:[XBWDL])?)(%s)((?:\.[0-9]+)*)" def update_address(address, address_model, new_leading):
result = address_model.match(address)
@@ -73,10 +73,10 @@
-Helper class for bounding_box calculation
+Helper class for bounding_box calculation
def __init__(self, x=None, y=None, width=None, height=None):
@@ -86,19 +86,19 @@
if height is not None and y is not None:
self.x_min = _init_and_compare(min, self.x_min, x)
self.x_max = _init_and_compare(max, self.x_max, x)
self.y_min = _init_and_compare(min, self.y_min, y)
self.y_max = _init_and_compare(max, self.y_max, y)
self.x_min = _init_and_compare(min, self.x_min, rect.x_min)
self.x_max = _init_and_compare(max, self.x_max, rect.x_max)
self.y_min = _init_and_compare(min, self.y_min, rect.y_min)
self.y_max = _init_and_compare(max, self.y_max, rect.y_max)
if self.x_min is not None and self.x_max is not None:
@@ -125,7 +125,7 @@
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])))
@@ -141,11 +141,11 @@
PLCOpen_XPath = lambda xpath: etree.XPath(xpath, namespaces=PLCOpenParser.NSMAP)
LOAD_POU_PROJECT_TEMPLATE = """
-<project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201"
- xmlns:xhtml="http://www.w3.org/1999/xhtml"
- xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+<project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201" + xmlns:xhtml="http://www.w3.org/1999/xhtml" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.plcopen.org/xml/tc6_0201">
- <fileHeader companyName="" productName="" productVersion=""
+ <fileHeader companyName="" productName="" productVersion="" creationDateTime="1970-01-01T00:00:00"/>
<contentHeader name="paste_project">
@@ -176,8 +176,8 @@
PLCOpen_v1_xml = PLCOpen_v1_file.read()
PLCOpen_v1_xml = PLCOpen_v1_xml.replace(
- "http://www.plcopen.org/xml/tc6.xsd",
- "http://www.plcopen.org/xml/tc6_0201")
+ "http://www.plcopen.org/xml/tc6.xsd", + "http://www.plcopen.org/xml/tc6_0201") PLCOpen_v1_xsd = etree.XMLSchema(etree.fromstring(PLCOpen_v1_xml))
# XPath for file compatibility process
@@ -190,21 +190,21 @@
def LoadProjectXML(project_xml):
project_xml = project_xml.replace(
- "http://www.plcopen.org/xml/tc6.xsd",
+ "http://www.plcopen.org/xml/tc6.xsd", "http://www.plcopen.org/xml/tc6_0201")
(re.compile("(?<!<xhtml:p>)(?:<!\[CDATA\[)"), "<xhtml:p><![CDATA["),
(re.compile("(?:]]>)(?!</xhtml:p>)"), "]]></xhtml:p>")]:
project_xml = cre.sub(repl, project_xml)
tree, error = PLCOpenParser.LoadXMLString(project_xml)
if PLCOpen_v1_xsd.validate(tree):
# Make file compatible with PLCOpen v2
# Update resource interval value
for resource in ProjectResourcesXPath(tree):
for task in resource.gettask():
@@ -229,13 +229,13 @@
text += "%dms"%(time_values[3] / 1000)
task.set("interval", text)
# Update resources pou instance attributes
for pouInstance in ResourceInstancesXpath(resource):
type_name = pouInstance.attrib.pop("type")
if type_name is not None:
pouInstance.set("typeName", type_name)
# Update transitions condition
for transition_condition in TransitionsConditionXPath(tree):
connections = ConditionConnectionsXPath(transition_condition)
@@ -245,23 +245,23 @@
connectionPointIn.setrelPositionXY(0, 0)
for connection in connections:
connectionPointIn.append(connection)
for actionBlock in ActionBlocksXPath(tree):
for connectionPointOut in ActionBlocksConnectionPointOutXPath(actionBlock):
actionBlock.remove(connectionPointOut)
for action in actionBlock.getaction():
action.set("localId", "0")
relPosition = PLCOpenParser.CreateElement("relPosition", "action")
relPosition.set("x", "0")
relPosition.set("y", "0")
action.setrelPosition(relPosition)
@@ -288,9 +288,9 @@
def SaveProject(project, filepath):
project_file = open(filepath, 'w')
project_file.write(etree.tostring(
@@ -302,7 +302,7 @@
text = pattern.sub(new_name, text)
setattr(cls, "updateElementName", updateElementName)
def updateElementAddress(self, address_model, new_leading):
@@ -315,28 +315,28 @@
result = address_model.search(text, startpos)
setattr(cls, "updateElementAddress", updateElementAddress)
def hasblock(self, block_type):
- text = self.getanyText()
+ text = self.getanyText() pattern = re.compile('\\b' + block_type + '\\b', re.IGNORECASE)
return pattern.search(text) is not None
setattr(cls, "hasblock", hasblock)
def Search(self, criteria, parent_infos):
return [(tuple(parent_infos),) + result for result in TestTextElement(self.getanyText(), criteria)]
setattr(cls, "Search", Search)
cls = PLCOpenParser.GetElementClass("project")
self.contentHeader.setname(name)
setattr(cls, "setname", setname)
return self.contentHeader.getname()
setattr(cls, "getname", getname)
fileheader_obj = self.fileHeader
@@ -351,7 +351,7 @@
("contentDescription", fileheader_obj.getcontentDescription())]
setattr(cls, "getfileHeader", getfileHeader)
def setfileHeader(self, fileheader):
fileheader_obj = self.fileHeader
for attr in ["companyName", "companyURL", "productName",
@@ -361,7 +361,7 @@
setattr(fileheader_obj, attr, value)
setattr(cls, "setfileHeader", setfileHeader)
def getcontentHeader(self):
contentheader_obj = self.contentHeader
@@ -378,7 +378,7 @@
contentheader["scaling"] = self.contentHeader.getscaling()
setattr(cls, "getcontentHeader", getcontentHeader)
def setcontentHeader(self, contentheader):
contentheader_obj = self.contentHeader
for attr, value in contentheader.iteritems():
@@ -392,7 +392,7 @@
elif attr in ["modificationDateTime", "organization", "language"]:
setattr(contentheader_obj, attr, value)
setattr(cls, "setcontentHeader", setcontentHeader)
def gettypeElementFunc(element_type):
elements_xpath = PLCOpen_XPath(
"ppx:types/ppx:%(element_type)ss/ppx:%(element_type)s[@name=$name]" % locals())
@@ -402,7 +402,7 @@
datatypes_xpath = PLCOpen_XPath("ppx:types/ppx:dataTypes/ppx:dataType")
filtered_datatypes_xpath = PLCOpen_XPath(
"ppx:types/ppx:dataTypes/ppx:dataType[@name!=$exclude]")
@@ -411,43 +411,43 @@
return filtered_datatypes_xpath(self, exclude=exclude)
return datatypes_xpath(self)
setattr(cls, "getdataTypes", getdataTypes)
setattr(cls, "getdataType", gettypeElementFunc("dataType"))
def appenddataType(self, name):
if self.getdataType(name) is not None:
raise ValueError, "\"%s\" Data Type already exists !!!"%name
self.types.appenddataTypeElement(name)
setattr(cls, "appenddataType", appenddataType)
def insertdataType(self, index, datatype):
self.types.insertdataTypeElement(index, datatype)
setattr(cls, "insertdataType", insertdataType)
def removedataType(self, name):
self.types.removedataTypeElement(name)
setattr(cls, "removedataType", removedataType)
def getpous(self, exclude=None, filter=[]):
- "ppx:types/ppx:pous/ppx:pou%s%s" %
+ "ppx:types/ppx:pous/ppx:pou%s%s" % (("[@name!='%s']" % exclude) if exclude is not None else '',
map(lambda x: "@pouType='%s'" % x, filter)))
if len(filter) > 0 else ""),
namespaces=PLCOpenParser.NSMAP)
setattr(cls, "getpous", getpous)
setattr(cls, "getpou", gettypeElementFunc("pou"))
def appendpou(self, name, pou_type, body_type):
self.types.appendpouElement(name, pou_type, body_type)
setattr(cls, "appendpou", appendpou)
def insertpou(self, index, pou):
self.types.insertpouElement(index, pou)
setattr(cls, "insertpou", insertpou)
def removepou(self, name):
self.types.removepouElement(name)
setattr(cls, "removepou", removepou)
@@ -473,7 +473,7 @@
new_configuration = PLCOpenParser.CreateElement("configuration", "configurations")
new_configuration.setname(name)
self.instances.configurations.appendconfiguration(new_configuration)
- setattr(cls, "addconfiguration", addconfiguration)
+ setattr(cls, "addconfiguration", addconfiguration) def removeconfiguration(self, name):
configuration = self.getconfiguration(name)
@@ -481,7 +481,7 @@
raise ValueError, ("\"%s\" configuration doesn't exist !!!") % name
self.instances.configurations.remove(configuration)
setattr(cls, "removeconfiguration", removeconfiguration)
resources_xpath = PLCOpen_XPath(
"ppx:instances/ppx:configurations/ppx:configuration[@name=$configname]/ppx:resource[@name=$name]")
def getconfigurationResource(self, config_name, name):
@@ -562,11 +562,11 @@
cls = PLCOpenParser.GetElementClass("contentHeader", "project")
def setpageSize(self, width, height):
self.coordinateInfo.setpageSize(width, height)
setattr(cls, "setpageSize", setpageSize)
return self.coordinateInfo.getpageSize()
setattr(cls, "getpageSize", getpageSize)
@@ -575,7 +575,7 @@
for language, (x, y) in scaling.items():
self.coordinateInfo.setscaling(language, x, y)
setattr(cls, "setscaling", setscaling)
scaling["FBD"] = self.coordinateInfo.getscaling("FBD")
@@ -595,7 +595,7 @@
self.pageSize.setx(width)
self.pageSize.sety(height)
setattr(cls, "setpageSize", setpageSize)
if self.pageSize is not None:
return self.pageSize.getx(), self.pageSize.gety()
@@ -613,7 +613,7 @@
setattr(cls, "setscaling", setscaling)
def getscaling(self, language):
return self.fbd.scaling.getx(), self.fbd.scaling.gety()
@@ -685,7 +685,7 @@
cls = PLCOpenParser.GetElementClass("configuration", "configurations")
def addglobalVar(self, var_type, name, location="", description=""):
globalvars = self.getglobalVars()
@@ -701,7 +701,7 @@
globalvars[-1].appendvariable(var)
setattr(cls, "addglobalVar", addglobalVar)
def updateElementName(self, old_name, new_name):
_updateConfigurationResourceElementName(self, old_name, new_name)
for resource in self.getresource():
@@ -727,7 +727,7 @@
search_result.extend(resource.Search(criteria, parent_infos))
setattr(cls, "Search", Search)
cls = PLCOpenParser.GetElementClass("resource", "configuration")
def updateElementName(self, old_name, new_name):
@@ -788,7 +788,7 @@
setattr(cls, "updateElementAddress", updateElementAddress)
def Search(self, criteria, parent_infos=[]):
- return _Search([("single", self.getsingle()),
+ return _Search([("single", self.getsingle()), ("interval", self.getinterval()),
("priority", str(self.getpriority()))],
@@ -802,7 +802,7 @@
setattr(cls, "updateElementName", updateElementName)
def Search(self, criteria, parent_infos=[]):
- return _Search([("name", self.getname()),
+ return _Search([("name", self.getname()), ("type", self.gettypeName())],
setattr(cls, "Search", Search)
@@ -822,22 +822,22 @@
elif vartype_content_name == "array":
base_type = vartype_content.baseType.getcontent()
base_type_name = base_type.getLocalTag()
- # Array derived directly from a user defined type
+ # Array derived directly from a user defined type if base_type_name == "derived":
basetype_name = base_type.getname()
- # Array derived directly from a string type
+ # 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
+ # Array derived directly from an elementary type basetype_name = base_type_name
return "ARRAY [%s] OF %s" % (",".join(map(lambda x : "%s..%s" % (x.getlower(), x.getupper()), vartype_content.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()),
+ search_result = _Search([("name", self.getname()), ("type", self.gettypeAsText()),
("location", self.getaddress())],
@@ -855,7 +855,7 @@
def getdataTypeElements(self):
return self.dataTypes.getdataType()
setattr(cls, "getdataTypeElements", getdataTypeElements)
def getdataTypeElement(self, name):
elements = self.dataTypes.getdataType()
@@ -870,11 +870,11 @@
new_datatype.setname(name)
new_datatype.baseType.setcontent(PLCOpenParser.CreateElement("BOOL", "dataType"))
setattr(cls, "appenddataTypeElement", appenddataTypeElement)
def insertdataTypeElement(self, index, dataType):
self.dataTypes.insertdataType(index, dataType)
setattr(cls, "insertdataTypeElement", insertdataTypeElement)
def removedataTypeElement(self, name):
for element in self.dataTypes.getdataType():
@@ -885,11 +885,11 @@
raise ValueError, _("\"%s\" Data Type doesn't exist !!!")%name
setattr(cls, "removedataTypeElement", removedataTypeElement)
def getpouElements(self):
return self.pous.getpou()
setattr(cls, "getpouElements", getpouElements)
def getpouElement(self, name):
elements = self.pous.getpou()
@@ -909,11 +909,11 @@
new_pou.appendbody(PLCOpenParser.CreateElement("body", "pou"))
new_pou.setbodyType(body_type)
setattr(cls, "appendpouElement", appendpouElement)
def insertpouElement(self, index, pou):
self.pous.insertpou(index, pou)
setattr(cls, "insertpouElement", insertpouElement)
def removepouElement(self, name):
for element in self.pous.getpou():
@@ -941,7 +941,7 @@
cls = PLCOpenParser.GetElementClass("dataType", "dataTypes")
setattr(cls, "updateElementName", _updateBaseTypeElementName)
def Search(self, criteria, parent_infos=[]):
filter = criteria["filter"]
@@ -956,7 +956,7 @@
cls = PLCOpenParser.GetElementClass("dataType")
def updateElementName(self, old_name, new_name):
content_name = self.content.getLocalTag()
if content_name in ["derived", "array", "subrangeSigned", "subrangeUnsigned"]:
@@ -987,7 +987,7 @@
if TextMatched(self.name, old_name):
setattr(cls, "updateElementName", updateElementName)
def Search(self, criteria, parent_infos=[]):
return [(tuple(parent_infos),) + result for result in TestTextElement(self.name, criteria)]
setattr(cls, "Search", Search)
@@ -995,7 +995,7 @@
cls = PLCOpenParser.GetElementClass("array", "dataType")
setattr(cls, "updateElementName", _updateBaseTypeElementName)
def Search(self, criteria, parent_infos=[]):
search_result = self.baseType.Search(criteria, parent_infos)
for i, dimension in enumerate(self.getdimension()):
@@ -1024,11 +1024,11 @@
cls = PLCOpenParser.GetElementClass("enum", "dataType")
def updateElementName(self, old_name, new_name):
setattr(cls, "updateElementName", updateElementName)
enumerated_datatype_values_xpath = PLCOpen_XPath("ppx:values/ppx:value")
def Search(self, criteria, parent_infos=[]):
@@ -1044,21 +1044,21 @@
if type_content_type == "derived":
return type_content.getname()
return type_content_type.upper()
cls = PLCOpenParser.GetElementClass("pou", "pous")
block_inputs_xpath = PLCOpen_XPath(
"ppx:interface/*[self::ppx:inputVars or self::ppx:inOutVars]/ppx:variable")
block_outputs_xpath = PLCOpen_XPath(
"ppx:interface/*[self::ppx:outputVars or self::ppx:inOutVars]/ppx:variable")
- def getblockInfos(self):
+ def getblockInfos(self): - "name" : self.getname(),
- "type" : self.getpouType(),
+ "name" : self.getname(), + "type" : self.getpouType(),
"comment" : self.getdescription()}
if self.interface is not None:
return_type = self.interface.getreturnType()
@@ -1071,15 +1071,15 @@
block_infos["outputs"].extend(
[(var.getname(), _getvariableTypeinfos(var.type), "none")
for var in block_outputs_xpath(self)])
- block_infos["usage"] = ("\n (%s) => (%s)" %
- (", ".join(["%s:%s" % (input[1], input[0])
+ block_infos["usage"] = ("\n (%s) => (%s)" % + (", ".join(["%s:%s" % (input[1], input[0]) for input in block_infos["inputs"]]),
- ", ".join(["%s:%s" % (output[1], output[0])
+ ", ".join(["%s:%s" % (output[1], output[0]) for output in block_infos["outputs"]])))
setattr(cls, "getblockInfos", getblockInfos)
def setdescription(self, description):
doc = self.getdocumentation()
@@ -1087,14 +1087,14 @@
self.setdocumentation(doc)
doc.setanyText(description)
setattr(cls, "setdescription", setdescription)
def getdescription(self):
doc = self.getdocumentation()
setattr(cls, "getdescription", getdescription)
def setbodyType(self, body_type):
if body_type in ["IL", "ST", "LD", "FBD", "SFC"]:
@@ -1102,66 +1102,66 @@
raise ValueError, "%s isn't a valid body type!"%type
setattr(cls, "setbodyType", setbodyType)
return self.body[0].getcontent().getLocalTag()
setattr(cls, "getbodyType", getbodyType)
def resetexecutionOrder(self):
self.body[0].resetexecutionOrder()
setattr(cls, "resetexecutionOrder", resetexecutionOrder)
def compileexecutionOrder(self):
self.body[0].compileexecutionOrder()
setattr(cls, "compileexecutionOrder", compileexecutionOrder)
def setelementExecutionOrder(self, instance, new_executionOrder):
self.body[0].setelementExecutionOrder(instance, new_executionOrder)
setattr(cls, "setelementExecutionOrder", setelementExecutionOrder)
def addinstance(self, instance):
self.body[0].appendcontentInstance(instance)
setattr(cls, "addinstance", addinstance)
return self.body[0].getcontentInstances()
setattr(cls, "getinstances", getinstances)
def getinstance(self, id):
return self.body[0].getcontentInstance(id)
setattr(cls, "getinstance", getinstance)
def getinstancesIds(self):
return self.body[0].getcontentInstancesIds()
setattr(cls, "getinstancesIds", getinstancesIds)
def getinstanceByName(self, name):
return self.body[0].getcontentInstanceByName(name)
setattr(cls, "getinstanceByName", getinstanceByName)
def removeinstance(self, id):
self.body[0].removecontentInstance(id)
setattr(cls, "removeinstance", removeinstance)
self.body[0].settext(text)
setattr(cls, "settext", settext)
return self.body[0].gettext()
@@ -1178,17 +1178,17 @@
vars.append((reverse_types[varlist.getLocalTag()], varlist))
setattr(cls, "getvars", getvars)
if self.interface is None:
self.interface = PLCOpenParser.CreateElement("interface", "pou")
self.interface.setcontent(vars)
setattr(cls, "setvars", setvars)
def addpouExternalVar(self, var_type, name):
self.addpouVar(var_type, name, "externalVars")
setattr(cls, "addpouExternalVar", addpouExternalVar)
def addpouVar(self, var_type, name, var_class="localVars", location="", description="", initval=""):
if self.interface is None:
self.interface = PLCOpenParser.CreateElement("interface", "pou")
@@ -1218,11 +1218,11 @@
el = PLCOpenParser.CreateElement("initialValue", "variable")
varlist.appendvariable(var)
setattr(cls, "addpouVar", addpouVar)
setattr(cls, "addpouLocalVar", addpouVar)
def changepouVar(self, old_type, old_name, new_type, new_name):
if self.interface is not None:
content = self.interface.getcontent()
@@ -1236,7 +1236,7 @@
vartype_content.setname(new_type)
setattr(cls, "changepouVar", changepouVar)
def removepouVar(self, var_type, name):
if self.interface is not None:
content = self.interface.getcontent()
@@ -1255,14 +1255,14 @@
if self.getbodyType() in ["SFC"]:
for instance in self.getinstances():
if isinstance(instance, PLCOpenParser.GetElementClass("step", "sfcObjects")) and TextMatched(instance.getname(), name):
setattr(cls, "hasstep", hasstep)
def hasblock(self, name=None, block_type=None):
if self.getbodyType() in ["FBD", "LD", "SFC"]:
for instance in self.getinstances():
- if (isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and
+ if (isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and (TextMatched(instance.getinstanceName(), name) or TextMatched(instance.gettypeName(), block_type))):
@@ -1279,7 +1279,7 @@
return self.body[0].hasblock(block_type)
setattr(cls, "hasblock", hasblock)
def addtransition(self, name, body_type):
if self.transitions is None:
@@ -1291,7 +1291,7 @@
transition.settext(":= ;")
setattr(cls, "addtransition", addtransition)
def gettransition(self, name):
if self.transitions is not None:
for transition in self.transitions.gettransition():
@@ -1299,13 +1299,13 @@
setattr(cls, "gettransition", gettransition)
def gettransitionList(self):
if self.transitions is not None:
return self.transitions.gettransition()
setattr(cls, "gettransitionList", gettransitionList)
def removetransition(self, name):
if self.transitions is not None:
@@ -1314,7 +1314,7 @@
if transition.getbodyType() in ["FBD", "LD", "SFC"]:
for instance in transition.getinstances():
if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")):
- self.removepouVar(instance.gettypeName(),
+ self.removepouVar(instance.gettypeName(), instance.getinstanceName())
self.transitions.remove(transition)
@@ -1332,7 +1332,7 @@
action.setbodyType(body_type)
setattr(cls, "addaction", addaction)
def getaction(self, name):
if self.actions is not None:
for action in self.actions.getaction():
@@ -1340,13 +1340,13 @@
setattr(cls, "getaction", getaction)
if self.actions is not None:
return self.actions.getaction()
setattr(cls, "getactionList", getactionList)
def removeaction(self, name):
if self.actions is not None:
@@ -1355,7 +1355,7 @@
if action.getbodyType() in ["FBD", "LD", "SFC"]:
for instance in action.getinstances():
if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")):
- self.removepouVar(instance.gettypeName(),
+ self.removepouVar(instance.gettypeName(), instance.getinstanceName())
self.actions.remove(action)
@@ -1417,7 +1417,7 @@
setattr(cls, "removeVariableByFilter", removeVariableByFilter)
def Search(self, criteria, parent_infos=[]):
filter = criteria["filter"]
@@ -1494,7 +1494,7 @@
def hasblock(self, name=None, block_type=None):
if self.getbodyType() in ["FBD", "LD", "SFC"]:
for instance in self.getinstances():
- if (isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and
+ if (isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and (TextMatched(instance.getinstanceName(), name) or TextMatched(instance.gettypeName(), block_type))):
elif block_type is not None:
@@ -1506,7 +1506,7 @@
def updateElementAddress(self, address_model, new_leading):
self.body.updateElementAddress(address_model, new_leading)
cls = PLCOpenParser.GetElementClass("transition", "transitions")
@@ -1526,7 +1526,7 @@
setattr(cls, "hasblock", hasblock)
setattr(cls, "updateElementName", updateElementName)
setattr(cls, "updateElementAddress", updateElementAddress)
def Search(self, criteria, parent_infos):
parent_infos = parent_infos[:-1] + ["T::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())]
@@ -1554,7 +1554,7 @@
setattr(cls, "hasblock", hasblock)
setattr(cls, "updateElementName", updateElementName)
setattr(cls, "updateElementAddress", updateElementAddress)
def Search(self, criteria, parent_infos):
parent_infos = parent_infos[:-1] + ["A::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())]
@@ -1571,24 +1571,24 @@
def resetcurrentExecutionOrderId(self):
object.__setattr__(self, "currentExecutionOrderId", 0)
setattr(cls, "resetcurrentExecutionOrderId", resetcurrentExecutionOrderId)
def getnewExecutionOrderId(self):
object.__setattr__(self, "currentExecutionOrderId", self.currentExecutionOrderId + 1)
return self.currentExecutionOrderId
setattr(cls, "getnewExecutionOrderId", getnewExecutionOrderId)
def resetexecutionOrder(self):
if self.content.getLocalTag() == "FBD":
for element in self.content.getcontent():
- if not isinstance(element, (PLCOpenParser.GetElementClass("comment", "commonObjects"),
- PLCOpenParser.GetElementClass("connector", "commonObjects"),
+ if not isinstance(element, (PLCOpenParser.GetElementClass("comment", "commonObjects"), + PLCOpenParser.GetElementClass("connector", "commonObjects"), PLCOpenParser.GetElementClass("continuation", "commonObjects"))):
element.setexecutionOrderId(0)
self.checkedBlocksDict.clear()
raise TypeError, _("Can only generate execution order on FBD networks!")
setattr(cls, "resetexecutionOrder", resetexecutionOrder)
def compileexecutionOrder(self):
if self.content.getLocalTag() == "FBD":
self.resetexecutionOrder()
@@ -1602,7 +1602,7 @@
raise TypeError, _("Can only generate execution order on FBD networks!")
setattr(cls, "compileexecutionOrder", compileexecutionOrder)
def compileelementExecutionOrder(self, link):
if self.content.getLocalTag() == "FBD":
localid = link.getrefLocalId()
@@ -1626,7 +1626,7 @@
raise TypeError, _("Can only generate execution order on FBD networks!")
setattr(cls, "compileelementExecutionOrder", compileelementExecutionOrder)
def setelementExecutionOrder(self, instance, new_executionOrder):
if self.content.getLocalTag() == "FBD":
old_executionOrder = instance.getexecutionOrderId()
@@ -1642,21 +1642,21 @@
raise TypeError, _("Can only generate execution order on FBD networks!")
setattr(cls, "setelementExecutionOrder", setelementExecutionOrder)
def appendcontentInstance(self, instance):
if self.content.getLocalTag() in ["LD","FBD","SFC"]:
self.content.appendcontent(instance)
raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag()
setattr(cls, "appendcontentInstance", appendcontentInstance)
def getcontentInstances(self):
if self.content.getLocalTag() in ["LD","FBD","SFC"]:
return self.content.getcontent()
raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag()
setattr(cls, "getcontentInstances", getcontentInstances)
instance_by_id_xpath = PLCOpen_XPath("*[@localId=$localId]")
instance_by_name_xpath = PLCOpen_XPath("ppx:block[@instanceName=$name]")
def getcontentInstance(self, local_id):
@@ -1668,7 +1668,7 @@
raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag()
setattr(cls, "getcontentInstance", getcontentInstance)
def getcontentInstancesIds(self):
if self.content.getLocalTag() in ["LD","FBD","SFC"]:
return OrderedDict([(instance.getlocalId(), True)
@@ -1676,7 +1676,7 @@
raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag()
setattr(cls, "getcontentInstancesIds", getcontentInstancesIds)
def getcontentInstanceByName(self, name):
if self.content.getLocalTag() in ["LD","FBD","SFC"]:
instance = instance_by_name_xpath(self.content)
@@ -1686,7 +1686,7 @@
raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag()
setattr(cls, "getcontentInstanceByName", getcontentInstanceByName)
def removecontentInstance(self, local_id):
if self.content.getLocalTag() in ["LD","FBD","SFC"]:
instance = instance_by_id_xpath(self.content, localId=local_id)
@@ -1697,7 +1697,7 @@
raise TypeError, "%s body don't have instances!"%self.content.getLocalTag()
setattr(cls, "removecontentInstance", removecontentInstance)
if self.content.getLocalTag() in ["IL","ST"]:
self.content.setanyText(text)
@@ -1711,14 +1711,14 @@
raise TypeError, _("%s body don't have text!")%self.content.getLocalTag()
setattr(cls, "gettext", gettext)
def hasblock(self, block_type):
if self.content.getLocalTag() in ["IL","ST"]:
return self.content.hasblock(block_type)
raise TypeError, _("%s body don't have text!")%self.content.getLocalTag()
setattr(cls, "hasblock", hasblock)
def updateElementName(self, old_name, new_name):
if self.content.getLocalTag() in ["IL", "ST"]:
self.content.updateElementName(old_name, new_name)
@@ -1753,7 +1753,7 @@
@@ -1828,7 +1828,7 @@
def _translate(self, dx, dy):
self.setx(self.getx() + dx)
self.sety(self.gety() + dy)
def _translateConnections(connectionPointIn, dx, dy):
connections = connectionPointIn.getconnections()
if connections is not None:
@@ -1892,11 +1892,11 @@
def setcontentText(self, text):
self.content.setanyText(text)
setattr(cls, "setcontentText", setcontentText)
def getcontentText(self):
return self.content.getanyText()
setattr(cls, "getcontentText", getcontentText)
def updateElementName(self, old_name, new_name):
self.content.updateElementName(old_name, new_name)
setattr(cls, "updateElementName", updateElementName)
@@ -2004,7 +2004,7 @@
condition.setcontent(PLCOpenParser.CreateElement("ST", "inline"))
setattr(cls, "setconditionContent", setconditionContent)
def getconditionContent(self):
if self.condition is not None:
content = self.condition.getcontent()
@@ -2035,21 +2035,21 @@
bbox.union(_getConnectionsBoundingBox(condition_connection))
setattr(cls, "getBoundingBox", getBoundingBox)
def translate(self, dx, dy):
_translateSingle(self, dx, dy)
condition_connection = self.getconditionConnection()
if condition_connection is not None:
_translateConnections(condition_connection, dx, dy)
setattr(cls, "translate", translate)
def filterConnections(self, connections):
_filterConnectionsSingle(self, connections)
condition_connection = self.getconditionConnection()
if condition_connection is not None:
_filterConnections(condition_connection, self.localId, connections)
setattr(cls, "filterConnections", filterConnections)
def updateConnectionsId(self, translation):
if self.connectionPointIn is not None:
@@ -2087,7 +2087,7 @@
return condition_connection.getconnections()
setattr(cls, "getconnections", getconnections)
def Search(self, criteria, parent_infos=[]):
parent_infos = parent_infos + ["transition", self.getlocalId()]
@@ -2104,7 +2104,7 @@
_initElementClass("selectionConvergence", "sfcObjects", "multiple")
_initElementClass("simultaneousDivergence", "sfcObjects", "single")
_initElementClass("simultaneousConvergence", "sfcObjects", "multiple")
cls = _initElementClass("jumpStep", "sfcObjects", "single")
def Search(self, criteria, parent_infos):
@@ -2117,7 +2117,7 @@
if self.reference is not None:
self.reference.setname(name)
setattr(cls, "setreferenceName", setreferenceName)
def getreferenceName(self):
if self.reference is not None:
return self.reference.getname()
@@ -2129,7 +2129,7 @@
self.inline.setcontent(PLCOpenParser.CreateElement("ST", "inline"))
self.inline.settext(content)
setattr(cls, "setinlineContent", setinlineContent)
def getinlineContent(self):
if self.inline is not None:
return self.inline.gettext()
@@ -2155,7 +2155,7 @@
return _Search([("inline", self.getinlineContent()),
- ("reference", self.getreferenceName()),
+ ("reference", self.getreferenceName()), ("qualifier", qualifier),
("duration", self.getduration()),
("indicator", self.getindicator())],
@@ -2318,33 +2318,33 @@
def removeconnections(self):
setattr(cls, "removeconnections", removeconnections)
connection_xpath = PLCOpen_XPath("ppx:connection")
connection_by_position_xpath = PLCOpen_XPath("ppx:connection[position()=$pos]")
def getconnections(self):
return connection_xpath(self)
setattr(cls, "getconnections", getconnections)
def getconnection(self, idx):
connection = connection_by_position_xpath(self, pos=idx+1)
setattr(cls, "getconnection", getconnection)
def setconnectionId(self, idx, local_id):
connection = self.getconnection(idx)
if connection is not None:
connection.setrefLocalId(local_id)
setattr(cls, "setconnectionId", setconnectionId)
def getconnectionId(self, idx):
connection = self.getconnection(idx)
if connection is not None:
return connection.getrefLocalId()
setattr(cls, "getconnectionId", getconnectionId)
def setconnectionPoints(self, idx, points):
connection = self.getconnection(idx)
if connection is not None:
@@ -2363,7 +2363,7 @@
if connection is not None:
connection.setformalParameter(parameter)
setattr(cls, "setconnectionParameter", setconnectionParameter)
def getconnectionParameter(self, idx):
connection = self.getconnection(idx)
if connection is not None:
@@ -2398,7 +2398,7 @@
setattr(cls, "setvalue", setvalue)
return self.content.getvalue()
setattr(cls, "getvalue", getvalue)
@@ -2420,7 +2420,7 @@
cls = PLCOpenParser.GetElementClass("arrayValue", "value")
arrayValue_model = re.compile("([0-9]+)\((.*)\)$")
def setvalue(self, value):
for item in extractValues(value[1:-1]):
@@ -2436,7 +2436,7 @@
setattr(cls, "setvalue", setvalue)
for element in self.value:
@@ -2457,7 +2457,7 @@
cls = PLCOpenParser.GetElementClass("structValue", "value")
structValue_model = re.compile("(.*):=(.*)")
def setvalue(self, value):
for item in extractValues(value[1:-1]):
@@ -2470,11 +2470,10 @@
setattr(cls, "setvalue", setvalue)
for element in self.value:
values.append("%s := %s"%(element.getmember(), element.getvalue()))
return "(%s)"%", ".join(values)
setattr(cls, "getvalue", getvalue)
--- a/xmlclass/xmlclass.py Fri Aug 11 15:18:19 2017 +0300
+++ b/xmlclass/xmlclass.py Mon Aug 14 19:13:01 2017 +0300
@@ -93,7 +93,7 @@
-[SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE,
+[SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE, ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG, CONSTRAINT,
@@ -140,7 +140,7 @@
def GetNormalizedString(attr, extract=True):
- Function that normalizes a string according to XML 1.0. Replace
+ Function that normalizes a string according to XML 1.0. Replace tabulations, line feed and carriage return by white space
@param attr: tree node containing data to extract or data to normalize
@param extract: attr is a tree node or not
@@ -155,14 +155,14 @@
def GetToken(attr, extract=True):
- Function that tokenizes a string according to XML 1.0. Remove any leading
- and trailing white space and replace internal sequence of two or more
+ Function that tokenizes a string according to XML 1.0. Remove any leading + and trailing white space and replace internal sequence of two or more spaces by only one white space
@param attr: tree node containing data to extract or data to tokenize
@param extract: attr is a tree node or not
@return: data tokenized as string
- return " ".join([part for part in
+ return " ".join([part for part in GetNormalizedString(attr, extract).split(" ")
@@ -186,7 +186,7 @@
raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value)
-def GenerateIntegerExtraction(minInclusive=None, maxInclusive=None,
+def GenerateIntegerExtraction(minInclusive=None, maxInclusive=None, minExclusive=None, maxExclusive=None):
Function that generates an extraction function for integer defining min and
@@ -517,7 +517,7 @@
check that all extracted items match the model
@param attr: tree node containing data to extract or data as a string
@param extract: attr is a tree node or not
- @return: data as a list of string if matching
+ @return: data as a list of string if matching value = GetAttributeValue(attr)
@@ -535,18 +535,18 @@
def GenerateAnyInfos(infos):
def GetTextElement(tree):
if infos["namespace"][0] == "##any":
return tree.xpath("p")[0]
return tree.xpath("ns:p", namespaces={"ns": infos["namespace"][0]})[0]
return GetTextElement(tree).text
def GenerateAny(tree, value):
GetTextElement(tree).text = etree.CDATA(value)
if infos["namespace"][0] == "##any":
@@ -555,9 +555,9 @@
p = etree.Element(element_name)
@@ -574,16 +574,16 @@
def GenerateTag(value, name=None, indent=0):
if name is not None and not (infos["minOccurs"] == 0 and value is None):
ind1, ind2 = getIndent(indent, name)
return ind1 + "<%s/>\n" % name
@@ -595,7 +595,7 @@
namespace, name = DecomposeQualifiedName(infos)
return factory.GetQualifiedNameInfos(name, namespace)
def GetElementInitialValue(factory, infos):
infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"])
if infos["minOccurs"] == 1:
@@ -667,7 +667,7 @@
prefix = ("%s:" % factory.TargetNamespace
if factory.TargetNamespace is not None else "")
choices_xpath = "|".join(map(lambda x: prefix + x, choices_dict.keys()))
content_name, infos = choices[0]
if content_name == "sequence":
@@ -678,7 +678,7 @@
content_value = GetElementInitialValue(factory, infos)
"choices_xpath": etree.XPath(choices_xpath, namespaces=factory.NSMAP),
@@ -693,13 +693,13 @@
def DecomposeQualifiedName(name):
result = QName_model.match(name)
- raise ValueError("\"%s\" isn't a valid QName value!" % name)
+ raise ValueError("\"%s\" isn't a valid QName value!" % name) parts = result.groups()[0].split(':')
-def GenerateElement(element_name, attributes, elements_model,
+def GenerateElement(element_name, attributes, elements_model, def ExtractElement(factory, node):
attrs = factory.ExtractNodeAttrs(element_name, node, attributes)
@@ -746,20 +746,20 @@
self.BaseFolder = self.FileName = None
# Dictionary for stocking Classes and Types definitions created from
self.XMLClassDefinitions = {}
self.DefinedNamespaces = {}
self.SchemaNamespace = None
self.TargetNamespace = None
self.etreeNamespaceFormat = "%s"
self.CurrentCompilations = []
# Dictionaries for stocking Classes and Types generated
if self.FileName is not None:
@@ -920,15 +920,15 @@
def AddEquivalentClass(self, name, base):
equivalences = self.EquivalentClassesParent.setdefault(self.etreeNamespaceFormat % base, {})
equivalences[self.etreeNamespaceFormat % name] = True
def AddDistinctionBetweenParentsInLookupClass(
self, lookup_classes, parent, typeinfos):
- parent = (self.etreeNamespaceFormat % parent
+ parent = (self.etreeNamespaceFormat % parent if parent is not None else None)
parent_class = lookup_classes.get(parent)
if parent_class is not None:
@@ -939,7 +939,7 @@
lookup_classes[parent] = [typeinfos, parent_class]
lookup_classes[parent] = typeinfos
def AddToLookupClass(self, name, parent, typeinfos):
lookup_name = self.etreeNamespaceFormat % name
if isinstance(typeinfos, (StringType, UnicodeType)):
@@ -958,7 +958,7 @@
self.AddDistinctionBetweenParentsInLookupClass(
lookup_classes, parent, typeinfos)
self.ComputedClassesLookUp[lookup_name] = lookup_classes
def ExtractTypeInfos(self, name, parent, typeinfos):
if isinstance(typeinfos, (StringType, UnicodeType)):
namespace, type_name = DecomposeQualifiedName(typeinfos)
@@ -986,12 +986,12 @@
return self.CreateClass(name, parent, typeinfos)
elif typeinfos["type"] == SIMPLETYPE:
def GetEquivalentParents(self, parent):
return reduce(lambda x, y: x + y,
[[p] + self.GetEquivalentParents(p)
for p in self.EquivalentClassesParent.get(parent, {}).keys()], [])
Methods that generates the classes
@@ -1028,7 +1028,7 @@
if result is not None and \
not isinstance(result, (UnicodeType, StringType)):
self.Namespaces[self.TargetNamespace][result["name"]] = result
for name, parents in self.ComputedClassesLookUp.iteritems():
if isinstance(parents, DictType):
computed_classes = parents.items()
@@ -1042,7 +1042,7 @@
parents = dict(computed_classes)
self.ComputedClassesLookUp[name] = parents
parents[equivalent_parent] = computed_class
return self.ComputedClasses
def CreateClass(self, name, parent, classinfos, baseclass = False):
@@ -1050,11 +1050,11 @@
classname = "%s_%s" % (parent, name)
# Checks that classe haven't been generated yet
if self.AlreadyComputed.get(classname, False):
return self.ComputedClassesInfos.get(classname, None)
# If base classes haven't been generated
base_infos = classinfos.get("base", None)
@@ -1088,12 +1088,12 @@
bases.append(DefaultElementClass)
classmembers = {"__doc__": classinfos.get("doc", ""), "IsBaseClass": baseclass}
self.AlreadyComputed[classname] = True
for attribute in classinfos["attributes"]:
infos = self.ExtractTypeInfos(attribute["name"], name, attribute["attr_type"])
if infos["type"] != SIMPLETYPE:
raise ValueError("\"%s\" type is not a simple type!" % attribute["attr_type"])
attrname = attribute["name"]
@@ -1105,7 +1105,7 @@
raise ValueError("\"%s\" type unrecognized!" % attribute["attr_type"])
attribute["attr_type"] = infos
for element in classinfos["elements"]:
if element["type"] == CHOICE:
elmtname = element["name"]
@@ -1141,13 +1141,13 @@
classmembers["delete%s" % elmtname] = generateDeleteMethod(elmtname)
classmembers["set%s" % elmtname] = generateSetMethod(elmtname)
classmembers["get%s" % elmtname] = generateGetMethod(elmtname)
classmembers["_init_"] = generateInitMethod(self, classinfos)
classmembers["StructurePattern"] = GetStructurePattern(classinfos)
classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos)
classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos)
classmembers["setElementValue"] = generateSetElementValue(self, classinfos)
class_definition = classobj(str(name), bases, classmembers)
setattr(class_definition, "__getattr__", generateGetattrMethod(self, class_definition, classinfos))
setattr(class_definition, "__setattr__", generateSetattrMethod(self, class_definition, classinfos))
@@ -1155,16 +1155,16 @@
"initial": generateClassCreateFunction(class_definition),
if self.FileName is not None:
self.ComputedClasses[self.FileName][classname] = class_definition
self.ComputedClasses[classname] = class_definition
self.ComputedClassesInfos[classname] = class_infos
self.AddToLookupClass(name, parent, class_definition)
self.AddToLookupClass(classname, None, class_definition)
@@ -1183,7 +1183,7 @@
for classname, xmlclass in items:
print "%s: %s" % (classname, str(xmlclass))
def PrintClassNames(self):
classnames = self.XMLClassDefinitions.keys()
@@ -1191,7 +1191,7 @@
-Method that generate the method for generating the xml tree structure model by
+Method that generate the method for generating the xml tree structure model by following the attributes list defined
def ComputeMultiplicity(name, infos):
@@ -1213,7 +1213,7 @@
if infos["maxOccurs"] == "unbounded":
return "(?:%s){%d,}" % (name, infos["minOccurs"], name)
- return "(?:%s){%d,%d}" % (name, infos["minOccurs"],
+ return "(?:%s){%d,%d}" % (name, infos["minOccurs"], def GetStructurePattern(classinfos):
@@ -1256,7 +1256,7 @@
attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"])
elements = dict([(element["name"], element) for element in classinfos["elements"]])
def getattrMethod(self, name):
if attributes.has_key(name):
attribute_infos = attributes[name]
@@ -1269,7 +1269,7 @@
elif attribute_infos.has_key("default"):
return attribute_infos["attr_type"]["extract"](attribute_infos["default"], extract=False)
elif elements.has_key(name):
element_infos = elements[name]
element_infos["elmt_type"] = FindTypeInfos(factory, element_infos["elmt_type"])
@@ -1279,7 +1279,7 @@
elif element_infos["type"] == ANY:
return element_infos["elmt_type"]["extract"](self)
elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE:
@@ -1290,7 +1290,7 @@
values = self.findall(element_name)
if element_infos["elmt_type"]["type"] == SIMPLETYPE:
- element_infos["elmt_type"]["extract"](value.text, extract=False),
+ element_infos["elmt_type"]["extract"](value.text, extract=False), @@ -1298,19 +1298,19 @@
if element_infos["elmt_type"]["type"] == SIMPLETYPE:
return element_infos["elmt_type"]["extract"](value.text, extract=False)
elif classinfos.has_key("base"):
return classinfos["base"].__getattr__(self, name)
return DefaultElementClass.__getattribute__(self, name)
def generateSetattrMethod(factory, class_definition, classinfos):
attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"])
elements = OrderedDict([(element["name"], element) for element in classinfos["elements"]])
def setattrMethod(self, name, value):
if attributes.has_key(name):
attribute_infos = attributes[name]
@@ -1323,26 +1323,26 @@
elif attribute_infos.has_key("fixed"):
return self.set(name, attribute_infos["attr_type"]["generate"](value))
elif elements.has_key(name):
element_infos = elements[name]
element_infos["elmt_type"] = FindTypeInfos(factory, element_infos["elmt_type"])
if element_infos["type"] == ANY:
element_infos["elmt_type"]["generate"](self, value)
elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE:
self.text = element_infos["elmt_type"]["generate"](value)
prefix = ("%s:" % factory.TargetNamespace
if factory.TargetNamespace is not None else "")
element_xpath = (prefix + name
else elements["content"]["elmt_type"]["choices_xpath"].path)
for element in self.xpath(element_xpath, namespaces=factory.NSMAP):
element_idx = elements.keys().index(name)
@@ -1351,27 +1351,27 @@
else elements["content"]["elmt_type"]["choices_xpath"].path,
elements.keys()[:element_idx]))
insertion_point = len(self.xpath(previous_elements_xpath, namespaces=factory.NSMAP))
if not isinstance(value, ListType):
for element in reversed(value):
if element_infos["elmt_type"]["type"] == SIMPLETYPE:
tmp_element = etree.Element(factory.etreeNamespaceFormat % name)
tmp_element.text = element_infos["elmt_type"]["generate"](element)
self.insert(insertion_point, element)
elif classinfos.has_key("base"):
return classinfos["base"].__setattr__(self, name, value)
raise AttributeError("'%s' can't have an attribute '%s'." % (self.__class__.__name__, name))
def gettypeinfos(name, facets):
@@ -1398,7 +1398,7 @@
attr_list.extend(classinfos["base"].getElementAttributes(self))
for attr in classinfos["attributes"]:
if attr["use"] != "prohibited":
- attr_params = {"name" : attr["name"], "use" : attr["use"],
+ attr_params = {"name" : attr["name"], "use" : attr["use"], "type" : gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]),
"value" : getattr(self, attr["name"], "")}
attr_list.append(attr_params)
@@ -1408,7 +1408,7 @@
def generateGetElementInfos(factory, classinfos):
attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
elements = dict([(element["name"], element) for element in classinfos["elements"]])
def getElementInfos(self, name, path=None, derived=False):
@@ -1419,14 +1419,14 @@
if attributes.has_key(parts[0]):
raise ValueError("Wrong path!")
- attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"],
+ attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"], attributes[parts[0]]["attr_type"]["facets"])
value = getattr(self, parts[0], "")
elif elements.has_key(parts[0]):
if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE:
raise ValueError("Wrong path!")
- attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"],
+ attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"], elements[parts[0]]["elmt_type"]["facets"])
value = getattr(self, parts[0], "")
elif parts[0] == "content":
@@ -1463,8 +1463,8 @@
if self.content is not None:
children.extend(self.content.getElementInfos(value)["children"])
elif element["elmt_type"]["type"] == SIMPLETYPE:
- children.append({"name": element_name, "require": element["minOccurs"] != 0,
- "type": gettypeinfos(element["elmt_type"]["basename"],
+ children.append({"name": element_name, "require": element["minOccurs"] != 0, + "type": gettypeinfos(element["elmt_type"]["basename"], element["elmt_type"]["facets"]),
"value": getattr(self, element_name, None)})
@@ -1478,7 +1478,7 @@
def generateSetElementValue(factory, classinfos):
attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"])
elements = dict([(element["name"], element) for element in classinfos["elements"]])
def setElementValue(self, path, value):
parts = path.split(".", 1)
@@ -1489,7 +1489,7 @@
setattr(self, parts[0], value)
elif attributes[parts[0]]["use"] == "optional" and value == "":
if attributes[parts[0]].has_key("default"):
- setattr(self, parts[0],
+ setattr(self, parts[0], attributes[parts[0]]["attr_type"]["extract"](
attributes[parts[0]]["default"], False))
@@ -1680,39 +1680,39 @@
NAMESPACE_PATTERN = re.compile("xmlns(?:\:[^\=]*)?=\"[^\"]*\" ")
class DefaultElementClass(etree.ElementBase):
StructurePattern = re.compile("$")
return etree.QName(self.tag).localname
return NAMESPACE_PATTERN.sub("", etree.tostring(self, pretty_print=True, encoding='utf-8')).decode('utf-8')
class XMLElementClassLookUp(etree.PythonElementClassLookup):
def __init__(self, classes, *args, **kwargs):
etree.PythonElementClassLookup.__init__(self, *args, **kwargs)
self.LookUpClasses = classes
def GetElementClass(self, element_tag, parent_tag=None, default=DefaultElementClass):
element_class = self.LookUpClasses.get(element_tag, (default, None))
if not isinstance(element_class, DictType):
if isinstance(element_class[0], (StringType, UnicodeType)):
return self.GetElementClass(element_class[0], default=default)
element_with_parent_class = element_class.get(parent_tag, default)
if isinstance(element_with_parent_class, (StringType, UnicodeType)):
return self.GetElementClass(element_with_parent_class, default=default)
return element_with_parent_class
def lookup(self, document, element):
parent = element.getparent()
- element_class = self.GetElementClass(element.tag,
+ element_class = self.GetElementClass(element.tag, parent.tag if parent is not None else None)
if isinstance(element_class, ListType):
@@ -1741,24 +1741,24 @@
self.RootNSMAP = namespaces
self.BaseClass = base_class
self.XSDSchema = xsd_schema
def set_element_class_lookup(self, class_lookup):
etree.XMLParser.set_element_class_lookup(self, class_lookup)
self.ClassLookup = class_lookup
def LoadXMLString(self, xml_string):
tree = etree.fromstring(xml_string, self)
if not self.XSDSchema.validate(tree):
error = self.XSDSchema.error_log.last_error
return tree, (error.line, error.message)
def Dumps(self, xml_obj):
return etree.tostring(xml_obj, encoding='utf-8')
def Loads(self, xml_string):
return etree.fromstring(xml_string, self)
if self.BaseClass is not None:
@@ -1767,14 +1767,14 @@
def GetElementClass(self, element_tag, parent_tag=None):
return self.ClassLookup.GetElementClass(
- self.DefaultNamespaceFormat % element_tag,
- self.DefaultNamespaceFormat % parent_tag
- if parent_tag is not None else parent_tag,
+ self.DefaultNamespaceFormat % element_tag, + self.DefaultNamespaceFormat % parent_tag + if parent_tag is not None else parent_tag,
def CreateElement(self, element_tag, parent_tag=None, class_idx=None):
element_class = self.GetElementClass(element_tag, parent_tag)
if isinstance(element_class, ListType):
@@ -1787,14 +1787,14 @@
DefaultElementClass.__setattr__(new_element, "tag", self.DefaultNamespaceFormat % element_tag)
def GenerateParser(factory, xsdstring):
ComputedClasses = factory.CreateClasses()
if factory.FileName is not None:
ComputedClasses = ComputedClasses[factory.FileName]
BaseClass = [(name, XSDclass) for name, XSDclass in ComputedClasses.items() if XSDclass.IsBaseClass]
factory.etreeNamespaceFormat,
@@ -1803,6 +1803,5 @@
strip_cdata = False, remove_blank_text=True)
class_lookup = XMLElementClassLookUp(factory.ComputedClassesLookUp)
parser.set_element_class_lookup(class_lookup)
--- a/xmlclass/xsdschema.py Fri Aug 11 15:18:19 2017 +0300
+++ b/xmlclass/xsdschema.py Mon Aug 14 19:13:01 2017 +0300
@@ -62,14 +62,14 @@
return generateXMLTextMethod
DEFAULT_FACETS = GenerateDictFacets(["pattern", "whiteSpace", "enumeration"])
NUMBER_FACETS = GenerateDictFacets(DEFAULT_FACETS.keys() + ["maxInclusive", "maxExclusive", "minInclusive", "minExclusive"])
DECIMAL_FACETS = GenerateDictFacets(NUMBER_FACETS.keys() + ["totalDigits", "fractionDigits"])
STRING_FACETS = GenerateDictFacets(DEFAULT_FACETS.keys() + ["length", "minLength", "maxLength"])
-ALL_FACETS = ["pattern", "whiteSpace", "enumeration", "maxInclusive",
- "maxExclusive", "minInclusive", "minExclusive", "totalDigits",
+ALL_FACETS = ["pattern", "whiteSpace", "enumeration", "maxInclusive", + "maxExclusive", "minInclusive", "minExclusive", "totalDigits", "fractionDigits", "length", "minLength", "maxLength"]
@@ -81,12 +81,12 @@
def ReduceAppInfo(factory, attributes, elements):
- return {"type": "appinfo", "source": attributes.get("source", None),
+ return {"type": "appinfo", "source": attributes.get("source", None), "content": "\n".join(elements)}
def ReduceDocumentation(factory, attributes, elements):
- return {"type": "documentation", "source": attributes.get("source", None),
+ return {"type": "documentation", "source": attributes.get("source", None), "language": attributes.get("lang", "any"), "content": "\n".join(elements)}
@@ -124,7 +124,7 @@
def ReduceList(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
list = {"type": "list", "itemType": attributes.get("itemType", None), "doc": annotations}
if len(children) > 0 and children[0]["type"] == SIMPLETYPE:
if list["itemType"] is None:
list["itemType"] = children[0]
@@ -138,7 +138,7 @@
def ReduceUnion(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
union = {"type": "union", "memberTypes": attributes.get("memberTypes", []), "doc": annotations}
if child["type"] == SIMPLETYPE:
union["memberTypes"].appendchild
@@ -153,29 +153,29 @@
simpleType = {"type": SIMPLETYPE, "final": attributes.get("final", [])}
if attributes.has_key("name"):
simpleType["name"] = attributes["name"]
if typeinfos["type"] in ["restriction", "extension"]:
# Search for base type definition
if isinstance(typeinfos["base"], (StringType, UnicodeType)):
basetypeinfos = factory.FindSchemaElement(typeinfos["base"], SIMPLETYPE)
if basetypeinfos is None:
- raise "\"%s\" isn't defined!" % typeinfos["base"]
+ raise "\"%s\" isn't defined!" % typeinfos["base"] basetypeinfos = typeinfos["base"]
# Check that base type is a simple type
if basetypeinfos["type"] != SIMPLETYPE:
raise ValueError("Base type given isn't a simpleType!")
simpleType["basename"] = basetypeinfos["basename"]
# Check that derivation is allowed
if basetypeinfos.has_key("final"):
if "#all" in basetypeinfos["final"]:
raise ValueError("Base type can't be derivated!")
if "restriction" in basetypeinfos["final"] and typeinfos["type"] == "restriction":
raise ValueError("Base type can't be derivated by restriction!")
# Extract simple type facets
for facet in typeinfos.get("facets", []):
facettype = facet["type"]
@@ -286,12 +286,12 @@
elif basevalue is not None and value > basevalue:
raise ValueError("\"totalDigits\" can't be greater than \"totalDigits\" defined in base type!")
facets[facettype] = (value, facet.get("fixed", False))
- # Report not redefined facet from base type to new created type
+ # Report not redefined facet from base type to new created type for facettype, facetvalue in basetypeinfos["facets"].items():
if not facets.has_key(facettype):
facets[facettype] = facetvalue
# Generate extract value for new created type
def ExtractSimpleTypeValue(attr, extract=True):
value = basetypeinfos["extract"](attr, extract)
@@ -317,7 +317,7 @@
model = re.compile("(?:%s)?$" % "|".join(map(lambda x: "(?:%s)" % x, facetvalue)))
result = model.match(value)
- if len(facetvalue) > 1:
+ if len(facetvalue) > 1: raise ValueError("value doesn't follow any of the patterns %s" % ",".join(facetvalue))
raise ValueError("value doesn't follow the pattern %s" % facetvalue[0])
@@ -327,7 +327,7 @@
elif facetvalue == "collapse":
value = GetToken(value, False)
def CheckSimpleTypeValue(value):
for facetname, (facetvalue, facetfixed) in facets.items():
if facetvalue is not None:
@@ -351,12 +351,12 @@
model = re.compile("(?:%s)?$" % "|".join(map(lambda x: "(?:%s)" % x, facetvalue)))
result = model.match(value)
- if len(facetvalue) > 1:
+ if len(facetvalue) > 1: raise ValueError("value doesn't follow any of the patterns %s" % ",".join(facetvalue))
raise ValueError("value doesn't follow the pattern %s" % facetvalue[0])
def SimpleTypeInitialValue():
for facetname, (facetvalue, facetfixed) in facets.items():
if facetvalue is not None:
@@ -375,9 +375,9 @@
elif facetname == "maxExclusive" and facetvalue <= 0:
return basetypeinfos["initial"]()
GenerateSimpleType = basetypeinfos["generate"]
elif typeinfos["type"] == "list":
# Search for item type definition
if isinstance(typeinfos["itemType"], (StringType, UnicodeType)):
@@ -386,41 +386,41 @@
raise "\"%s\" isn't defined!" % typeinfos["itemType"]
itemtypeinfos = typeinfos["itemType"]
# Check that item type is a simple type
if itemtypeinfos["type"] != SIMPLETYPE:
raise ValueError, "Item type given isn't a simpleType!"
simpleType["basename"] = "list"
# Check that derivation is allowed
if itemtypeinfos.has_key("final"):
if itemtypeinfos["final"].has_key("#all"):
raise ValueError("Item type can't be derivated!")
if itemtypeinfos["final"].has_key("list"):
raise ValueError("Item type can't be derivated by list!")
# Generate extract value for new created type
def ExtractSimpleTypeValue(attr, extract = True):
for value in GetToken(attr, extract).split(" "):
values.append(itemtypeinfos["extract"](value, False))
def CheckSimpleTypeValue(value):
result = itemtypeinfos["check"](item)
SimpleTypeInitialValue = lambda: []
GenerateSimpleType = GenerateSimpleTypeXMLText(lambda x: " ".join(map(itemtypeinfos["generate"], x)))
facets = GenerateDictFacets(["length", "maxLength", "minLength", "enumeration", "pattern"])
facets["whiteSpace"] = ("collapse", False)
elif typeinfos["type"] == "union":
# Search for member types definition
@@ -431,22 +431,22 @@
raise ValueError("\"%s\" isn't defined!" % membertype)
# Check that member type is a simple type
if infos["type"] != SIMPLETYPE:
raise ValueError("Member type given isn't a simpleType!")
# Check that derivation is allowed
if infos.has_key("final"):
if infos["final"].has_key("#all"):
raise ValueError("Item type can't be derivated!")
if infos["final"].has_key("union"):
raise ValueError("Member type can't be derivated by union!")
membertypesinfos.append(infos)
simpleType["basename"] = "union"
# Generate extract value for new created type
def ExtractSimpleTypeValue(attr, extract = True):
@@ -459,25 +459,25 @@
raise ValueError("\"%s\" isn't valid for type defined for union!")
def CheckSimpleTypeValue(value):
for infos in membertypesinfos:
result = infos["check"](value)
SimpleTypeInitialValue = membertypesinfos[0]["initial"]
def GenerateSimpleTypeFunction(value):
if isinstance(value, BooleanType):
return {True: "true", False: "false"}[value]
GenerateSimpleType = GenerateSimpleTypeXMLText(GenerateSimpleTypeFunction)
facets = GenerateDictFacets(["pattern", "enumeration"])
simpleType["facets"] = facets
simpleType["extract"] = ExtractSimpleTypeValue
simpleType["initial"] = SimpleTypeInitialValue
@@ -488,10 +488,10 @@
def ReduceSimpleType(factory, attributes, elements):
# Reduce all the simple type children
annotations, children = factory.ReduceElements(elements)
simpleType = CreateSimpleType(factory, attributes, children[0])
simpleType["doc"] = annotations
@@ -503,7 +503,7 @@
basetypeinfos = factory.FindSchemaElement(base)
if not isinstance(basetypeinfos, (UnicodeType, StringType)) and basetypeinfos["type"] == COMPLEXTYPE:
attrnames = dict(map(lambda x:(x["name"], True), basetypeinfos["attributes"]))
if element["type"] == ATTRIBUTE:
if attrnames.get(element["name"], False):
@@ -534,7 +534,7 @@
raise ValueError("Only one base type can be defined for restriction!")
if restriction["base"] is None:
raise ValueError("No base type has been defined for restriction!")
while len(children) > 0 and children[0]["type"] in ALL_FACETS:
restriction["facets"].append(children.pop(0))
restriction["attributes"] = ExtractAttributes(factory, children, restriction["base"])
@@ -571,9 +571,9 @@
def ReduceSimpleContent(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
simpleContent = children[0].copy()
basetypeinfos = factory.FindSchemaElement(simpleContent["base"])
if basetypeinfos["type"] == SIMPLETYPE:
contenttypeinfos = simpleContent.copy()
@@ -588,7 +588,7 @@
raise ValueError("No compatible base type defined for simpleContent!")
contenttypeinfos = CreateSimpleType(factory, attributes, contenttypeinfos)
simpleContent["elements"] = [{"name": "content", "type": ELEMENT,
"elmt_type": contenttypeinfos, "doc": annotations,
"minOccurs": 1, "maxOccurs": 1}]
@@ -605,7 +605,7 @@
def ReduceComplexType(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
if children[0]["type"] in ["simpleContent", "complexContent"]:
complexType = children[0].copy()
@@ -666,20 +666,20 @@
def ReduceAttribute(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
if attributes.has_key("default"):
if attributes.has_key("fixed"):
raise ValueError("\"default\" and \"fixed\" can't be defined at the same time!")
elif attributes.get("use", "optional") != "optional":
raise ValueError("if \"default\" present, \"use\" can only have the value \"optional\"!")
attribute = {"type": ATTRIBUTE, "attr_type": attributes.get("type", None), "doc": annotations}
if attribute["attr_type"] is None:
attribute["attr_type"] = children[0]
raise ValueError("Only one type can be defined for attribute!")
if attributes.has_key("ref"):
if attributes.has_key("name"):
raise ValueError("\"ref\" and \"name\" can't be defined at the same time!")
@@ -689,7 +689,7 @@
raise ValueError("if \"ref\" is present, no type can be defined!")
elif attribute["attr_type"] is None:
raise ValueError("No type has been defined for attribute \"%s\"!" % attributes["name"])
if attributes.has_key("type"):
tmp_attrs = attributes.copy()
@@ -711,14 +711,14 @@
def ReduceAny(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
any = {"type": ANY, "doc": annotations}
def ReduceElement(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
@@ -726,10 +726,10 @@
constraints.append(child)
if attributes.has_key("default") and attributes.has_key("fixed"):
raise ValueError("\"default\" and \"fixed\" can't be defined at the same time!")
if attributes.has_key("ref"):
for attr in ["name", "default", "fixed", "form", "block", "type"]:
if attributes.has_key(attr):
@@ -738,7 +738,7 @@
raise ValueError("\"ref\" and \"nillable\" can't be defined at the same time!")
raise ValueError("No type and no constraints can be defined where \"ref\" is defined!")
infos = factory.FindSchemaElement(attributes["ref"], ELEMENT)
@@ -748,7 +748,7 @@
raise ValueError("\"%s\" base type isn't defined or circular referenced!" % name)
elif attributes.has_key("name"):
element = {"type": ELEMENT, "elmt_type": attributes.get("type", None), "constraints": constraints, "doc": annotations}
@@ -759,7 +759,7 @@
elif element["elmt_type"] is None:
element["elmt_type"] = "tag"
if attributes.has_key("type"):
tmp_attrs = attributes.copy()
@@ -772,18 +772,18 @@
def ReduceAll(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
if children["maxOccurs"] == "unbounded" or children["maxOccurs"] > 1:
raise ValueError("\"all\" item can't have \"maxOccurs\" attribute greater than 1!")
return {"type": "all", "elements": children, "minOccurs": attributes["minOccurs"],
"maxOccurs": attributes["maxOccurs"], "order": False, "doc": annotations}
def ReduceChoice(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
if child["type"] in [ELEMENT, ANY, TAG]:
@@ -795,7 +795,7 @@
elif child["type"] == CHOICE:
choices.extend(child["choices"])
elif child["type"] == "group":
- elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP)
+ elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) if not elmtgroup.has_key("choices"):
raise ValueError("Only group composed of \"choice\" can be referenced in \"choice\" element!")
@@ -810,18 +810,18 @@
choices_tmp.append(choice)
choices.extend(choices_tmp)
attributes["minOccurs"] = min(attributes["minOccurs"], choice["minOccurs"])
return {"type": CHOICE, "choices": choices, "minOccurs": attributes["minOccurs"],
"maxOccurs": attributes["maxOccurs"], "doc": annotations}
def ReduceSequence(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
if child["type"] in [ELEMENT, ANY, TAG, CHOICE]:
@@ -844,14 +844,14 @@
elements_tmp.append(element)
sequence.extend(elements_tmp)
return {"type": "sequence", "elements": sequence, "minOccurs": attributes["minOccurs"],
"maxOccurs": attributes["maxOccurs"], "order": True, "doc": annotations}
def ReduceGroup(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
if attributes.has_key("ref"):
return {"type": "group", "ref": attributes["ref"], "doc": annotations}
@@ -868,39 +868,39 @@
def ReduceUnique(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
unique = {"type": CONSTRAINT, "const_type": "unique", "selector": children[0], "fields": children[1:]}
unique.update(attributes)
def ReduceKey(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
key = {"type": CONSTRAINT, "const_type": "key", "selector": children[0], "fields": children[1:]}
def ReduceKeyRef(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
keyref = {"type": CONSTRAINT, "const_type": "keyref", "selector": children[0], "fields": children[1:]}
keyref.update(attributes)
def ReduceSelector(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
selector = {"type": CONSTRAINT, "const_type": "selector"}
selector.update(attributes)
def ReduceField(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
field = {"type": CONSTRAINT, "const_type": "field"}
@@ -910,7 +910,7 @@
def ReduceInclude(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
if factory.FileName is None:
raise ValueError("Include in XSD string not yet supported")
filepath = attributes["schemaLocation"]
@@ -922,7 +922,7 @@
include_factory = XSDClassFactory(minidom.parse(xsdfile), filepath)
include_factory.CreateClasses()
if factory.TargetNamespace == include_factory.TargetNamespace:
factory.Namespaces[factory.TargetNamespace].update(include_factory.Namespaces[include_factory.TargetNamespace])
@@ -931,7 +931,7 @@
factory.ComputedClassesLookUp.update(include_factory.ComputedClassesLookUp)
factory.EquivalentClassesParent.update(include_factory.EquivalentClassesParent)
def ReduceRedefine(factory, attributes, elements):
annotations, children = factory.ReduceElements(elements)
raise ValueError("\"redefine\" element isn't supported yet!")
@@ -944,15 +944,15 @@
factory.ElementFormDefault = attributes["elementFormDefault"]
factory.BlockDefault = attributes["blockDefault"]
factory.FinalDefault = attributes["finalDefault"]
targetNamespace = attributes.get("targetNamespace", None)
factory.TargetNamespace = factory.DefinedNamespaces.get(targetNamespace, None)
if factory.TargetNamespace is not None:
factory.etreeNamespaceFormat = "{%s}%%s" % targetNamespace
factory.Namespaces[factory.TargetNamespace] = {}
annotations, children = factory.ReduceElements(elements, True)
if child.has_key("name"):
infos = factory.GetQualifiedNameInfos(child["name"], factory.TargetNamespace, True)
@@ -987,7 +987,7 @@
return schema == reference
#-------------------------------------------------------------------------------
# Base class for XSD schema extraction
#-------------------------------------------------------------------------------
@@ -999,7 +999,7 @@
ClassFactory.__init__(self, document, filepath, debug)
self.Namespaces["xml"] = {
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateModelNameExtraction("lang", LANGUAGE_model)
@@ -1007,31 +1007,31 @@
self.Namespaces["xsi"] = {
"noNamespaceSchemaLocation": {
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": NotSupportedYet("noNamespaceSchemaLocation")
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": NotSupportedYet("nil")
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": NotSupportedYet("schemaLocation")
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": NotSupportedYet("type")
for child in self.Document.childNodes:
if child.nodeType == self.Document.ELEMENT_NODE:
@@ -1067,7 +1067,7 @@
if element_type is not None and element["type"] != element_type:
raise ValueError("\"%s\" isn't of the expected type!" % element_name)
def CreateSchemaElement(self, element_name, element_type):
for type, attributes, elements in self.Schema[2]:
namespace, name = DecomposeQualifiedName(type)
@@ -1091,7 +1091,7 @@
-This function opens the xsd file and generate a xml parser with class lookup from
+This function opens the xsd file and generate a xml parser with class lookup from def GenerateParserFromXSD(filepath):
@@ -1129,9 +1129,9 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, element*)
- "default": GenerateElement("all", ["id", "maxOccurs", "minOccurs"],
+ "default": GenerateElement("all", ["id", "maxOccurs", "minOccurs"], re.compile("((?:annotation )?(?:element )*)"))
@@ -1143,9 +1143,9 @@
{any attributes with non-schema namespace . . .}>
Content: (appinfo | documentation)*
- "default": GenerateElement("annotation", ["id"],
+ "default": GenerateElement("annotation", ["id"], re.compile("((?:app_info |documentation )*)"))
"reduce": ReduceAnnotation
@@ -1161,10 +1161,10 @@
{any attributes with non-schema namespace . . .}>
- "default": GenerateElement("any",
- ["id", "maxOccurs", "minOccurs", "namespace", "processContents"],
+ "default": GenerateElement("any", + ["id", "maxOccurs", "minOccurs", "namespace", "processContents"], re.compile("((?:annotation )?(?:simpleType )*)"))
@@ -1178,7 +1178,7 @@
{any attributes with non-schema namespace . . .}>
"default": GenerateElement("anyAttribute",
["id", "namespace", "processContents"], ONLY_ANNOTATION)
@@ -1192,7 +1192,7 @@
{any attributes with non-schema namespace . . .}>
"default": GenerateElement("appinfo", ["source"], re.compile("(.*)"), True)
@@ -1212,13 +1212,13 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, simpleType?)
- "default": GenerateElement("attribute",
- ["default", "fixed", "form", "id", "name", "ref", "type", "use"],
+ "default": GenerateElement("attribute", + ["default", "fixed", "form", "id", "name", "ref", "type", "use"], re.compile("((?:annotation )?(?:simpleType )?)")),
- "schema": GenerateElement("attribute",
- ["default", "fixed", "form", "id", "name", "type"],
+ "schema": GenerateElement("attribute", + ["default", "fixed", "form", "id", "name", "type"], re.compile("((?:annotation )?(?:simpleType )?)"))
"reduce": ReduceAttribute
@@ -1232,12 +1232,12 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, ((attribute | attributeGroup)*, anyAttribute?))
- "default": GenerateElement("attributeGroup",
+ "default": GenerateElement("attributeGroup", ["id", "ref"], ONLY_ANNOTATION),
"schema": GenerateElement("attributeGroup",
re.compile("((?:annotation )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))"))
"reduce": ReduceAttributeGroup
@@ -1251,9 +1251,9 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (element | group | choice | sequence | any)*)
- "default": GenerateElement("choice", ["id", "maxOccurs", "minOccurs"],
+ "default": GenerateElement("choice", ["id", "maxOccurs", "minOccurs"], re.compile("((?:annotation )?(?:element |group |choice |sequence |any )*)"))
@@ -1266,9 +1266,9 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (restriction | extension))
- "default": GenerateElement("complexContent", ["id", "mixed"],
+ "default": GenerateElement("complexContent", ["id", "mixed"], re.compile("((?:annotation )?(?:restriction |extension ))"))
"reduce": ReduceComplexContent
@@ -1285,10 +1285,10 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (simpleContent | complexContent | ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?))))
- "default": GenerateElement("complexType",
- ["abstract", "block", "final", "id", "mixed", "name"],
+ "default": GenerateElement("complexType", + ["abstract", "block", "final", "id", "mixed", "name"], re.compile("((?:annotation )?(?:simpleContent |complexContent |(?:(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))))"))
"reduce": ReduceComplexType
@@ -1301,9 +1301,9 @@
{any attributes with non-schema namespace . . .}>
- "default": GenerateElement("documentation",
+ "default": GenerateElement("documentation", ["source", "lang"], re.compile("(.*)"), True)
"reduce": ReduceDocumentation
@@ -1328,13 +1328,13 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, ((simpleType | complexType)?, (unique | key | keyref)*))
- "default": GenerateElement("element",
- ["abstract", "block", "default", "final", "fixed", "form", "id", "maxOccurs", "minOccurs", "name", "nillable", "ref", "substitutionGroup", "type"],
+ "default": GenerateElement("element", + ["abstract", "block", "default", "final", "fixed", "form", "id", "maxOccurs", "minOccurs", "name", "nillable", "ref", "substitutionGroup", "type"], re.compile("((?:annotation )?(?:simpleType |complexType )?(?:unique |key |keyref )*)")),
- "schema": GenerateElement("element",
- ["abstract", "block", "default", "final", "fixed", "form", "id", "name", "nillable", "substitutionGroup", "type"],
+ "schema": GenerateElement("element", + ["abstract", "block", "default", "final", "fixed", "form", "id", "name", "nillable", "substitutionGroup", "type"], re.compile("((?:annotation )?(?:simpleType |complexType )?(?:unique |key |keyref )*)"))
@@ -1347,7 +1347,7 @@
{any attributes with non-schema namespace . . .}>
"default": GenerateElement("enumeration", ["id", "value"], ONLY_ANNOTATION)
@@ -1361,11 +1361,11 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?)))
- "default": GenerateElement("extension", ["base", "id"],
+ "default": GenerateElement("extension", ["base", "id"], re.compile("((?:annotation )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))")),
- "complexContent": GenerateElement("extension", ["base", "id"],
+ "complexContent": GenerateElement("extension", ["base", "id"], re.compile("((?:annotation )?(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))"))
"reduce": ReduceExtension
@@ -1378,7 +1378,7 @@
{any attributes with non-schema namespace . . .}>
"default": GenerateElement("field", ["id", "xpath"], ONLY_ANNOTATION)
@@ -1393,9 +1393,9 @@
{any attributes with non-schema namespace . . .}>
- "default": GenerateElement("fractionDigits",
+ "default": GenerateElement("fractionDigits", ["fixed", "id", "value"], ONLY_ANNOTATION)
"reduce": GenerateFacetReducing("fractionDigits", True)
@@ -1411,13 +1411,13 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (all | choice | sequence)?)
"default": GenerateElement("group",
- ["id", "maxOccurs", "minOccurs", "ref"],
+ ["id", "maxOccurs", "minOccurs", "ref"], re.compile("((?:annotation )?(?:all |choice |sequence )?)")),
"schema": GenerateElement("group",
re.compile("((?:annotation )?(?:all |choice |sequence )?)"))
@@ -1431,7 +1431,7 @@
{any attributes with non-schema namespace . . .}>
"default": GenerateElement("import",
["id", "namespace", "schemaLocation"], ONLY_ANNOTATION)
@@ -1446,7 +1446,7 @@
{any attributes with non-schema namespace . . .}>
"default": GenerateElement("include",
["id", "schemaLocation"], ONLY_ANNOTATION)
@@ -1461,9 +1461,9 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (selector, field+))
- "default": GenerateElement("key", ["id", "name"],
+ "default": GenerateElement("key", ["id", "name"], re.compile("((?:annotation )?(?:selector (?:field )+))"))
@@ -1477,9 +1477,9 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (selector, field+))
- "default": GenerateElement("keyref", ["id", "name", "refer"],
+ "default": GenerateElement("keyref", ["id", "name", "refer"], re.compile("((?:annotation )?(?:selector (?:field )+))"))
@@ -1493,9 +1493,9 @@
{any attributes with non-schema namespace . . .}>
- "default": GenerateElement("length",
+ "default": GenerateElement("length", ["fixed", "id", "value"], ONLY_ANNOTATION)
"reduce": GenerateFacetReducing("length", True)
@@ -1508,9 +1508,9 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, simpleType?)
- "default": GenerateElement("list", ["id", "itemType"],
+ "default": GenerateElement("list", ["id", "itemType"], re.compile("((?:annotation )?(?:simpleType )?)$"))
@@ -1524,7 +1524,7 @@
{any attributes with non-schema namespace . . .}>
"default": GenerateElement("maxExclusive",
["fixed", "id", "value"], ONLY_ANNOTATION)
@@ -1540,9 +1540,9 @@
{any attributes with non-schema namespace . . .}>
- "default": GenerateElement("maxInclusive",
+ "default": GenerateElement("maxInclusive", ["fixed", "id", "value"], ONLY_ANNOTATION)
"reduce": GenerateFacetReducing("maxInclusive", True)
@@ -1556,9 +1556,9 @@
{any attributes with non-schema namespace . . .}>
- "default": GenerateElement("maxLength",
+ "default": GenerateElement("maxLength", ["fixed", "id", "value"], ONLY_ANNOTATION)
"reduce": GenerateFacetReducing("maxLength", True)
@@ -1572,9 +1572,9 @@
{any attributes with non-schema namespace . . .}>
- "default": GenerateElement("minExclusive",
+ "default": GenerateElement("minExclusive", ["fixed", "id", "value"], ONLY_ANNOTATION)
"reduce": GenerateFacetReducing("minExclusive", True)
@@ -1588,9 +1588,9 @@
{any attributes with non-schema namespace . . .}>
- "default": GenerateElement("minInclusive",
+ "default": GenerateElement("minInclusive", ["fixed", "id", "value"], ONLY_ANNOTATION)
"reduce": GenerateFacetReducing("minInclusive", True)
@@ -1604,7 +1604,7 @@
{any attributes with non-schema namespace . . .}>
"default": GenerateElement("minLength",
["fixed", "id", "value"], ONLY_ANNOTATION)
@@ -1619,7 +1619,7 @@
{any attributes with non-schema namespace . . .}>
"default": GenerateElement("pattern", ["id", "value"], ONLY_ANNOTATION)
@@ -1633,9 +1633,9 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation | (simpleType | complexType | group | attributeGroup))*
- "default": GenerateElement("refine", ["id", "schemaLocation"],
+ "default": GenerateElement("refine", ["id", "schemaLocation"], re.compile("((?:annotation |(?:simpleType |complexType |group |attributeGroup ))*)"))
@@ -1648,13 +1648,13 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?))
- "default": GenerateElement("restriction", ["base", "id"],
+ "default": GenerateElement("restriction", ["base", "id"], re.compile("((?:annotation )?(?:(?:simpleType )?(?:(?:minExclusive |minInclusive |maxExclusive |maxInclusive |totalDigits |fractionDigits |length |minLength |maxLength |enumeration |whiteSpace |pattern )*)))")),
- "simpleContent": GenerateElement("restriction", ["base", "id"],
+ "simpleContent": GenerateElement("restriction", ["base", "id"], re.compile("((?:annotation )?(?:(?:simpleType )?(?:(?:minExclusive |minInclusive |maxExclusive |maxInclusive |totalDigits |fractionDigits |length |minLength |maxLength |enumeration |whiteSpace |pattern )*)?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?)))")),
- "complexContent": GenerateElement("restriction", ["base", "id"],
+ "complexContent": GenerateElement("restriction", ["base", "id"], re.compile("((?:annotation )?(?:(?:simpleType )?(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?)))")),
"reduce": ReduceRestriction
@@ -1673,10 +1673,10 @@
{any attributes with non-schema namespace . . .}>
Content: ((include | import | redefine | annotation)*, (((simpleType | complexType | group | attributeGroup) | element | attribute | notation), annotation*)*)
"default": GenerateElement("schema",
- ["attributeFormDefault", "blockDefault", "elementFormDefault", "finalDefault", "id", "targetNamespace", "version", "lang"],
+ ["attributeFormDefault", "blockDefault", "elementFormDefault", "finalDefault", "id", "targetNamespace", "version", "lang"], re.compile("((?:include |import |redefine |annotation )*(?:(?:(?:simpleType |complexType |group |attributeGroup )|element |attribute |annotation )(?:annotation )*)*)"))
@@ -1688,7 +1688,7 @@
{any attributes with non-schema namespace . . .}>
"default": GenerateElement("selector", ["id", "xpath"], ONLY_ANNOTATION)
@@ -1703,9 +1703,9 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (element | group | choice | sequence | any)*)
- "default": GenerateElement("sequence", ["id", "maxOccurs", "minOccurs"],
+ "default": GenerateElement("sequence", ["id", "maxOccurs", "minOccurs"], re.compile("((?:annotation )?(?:element |group |choice |sequence |any )*)"))
@@ -1717,9 +1717,9 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (restriction | extension))
- "default": GenerateElement("simpleContent", ["id"],
+ "default": GenerateElement("simpleContent", ["id"], re.compile("((?:annotation )?(?:restriction |extension ))"))
"reduce": ReduceSimpleContent
@@ -1733,9 +1733,9 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (restriction | list | union))
- "default": GenerateElement("simpleType", ["final", "id", "name"],
+ "default": GenerateElement("simpleType", ["final", "id", "name"], re.compile("((?:annotation )?(?:restriction |list |union ))"))
"reduce": ReduceSimpleType
@@ -1749,9 +1749,9 @@
{any attributes with non-schema namespace . . .}>
- "default": GenerateElement("totalDigits",
+ "default": GenerateElement("totalDigits", ["fixed", "id", "value"], ONLY_ANNOTATION),
"reduce": GenerateFacetReducing("totalDigits", True)
@@ -1764,9 +1764,9 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, simpleType*)
- "default": GenerateElement("union", ["id", "memberTypes"],
+ "default": GenerateElement("union", ["id", "memberTypes"], re.compile("((?:annotation )?(?:simpleType )*)"))
@@ -1779,14 +1779,14 @@
{any attributes with non-schema namespace . . .}>
Content: (annotation?, (selector, field+))
- "default": GenerateElement("unique", ["id", "name"],
+ "default": GenerateElement("unique", ["id", "name"], re.compile("((?:annotation )?(?:selector |(?:field )+))"))
"whiteSpace": {"struct" : """
@@ -1795,9 +1795,9 @@
{any attributes with non-schema namespace . . .}>
- "default": GenerateElement("whiteSpace",
+ "default": GenerateElement("whiteSpace", ["fixed", "id", "value"], ONLY_ANNOTATION)
"reduce": GenerateFacetReducing("whiteSpace", True)
@@ -1808,7 +1808,7 @@
#-------------------------------------------------------------------------------
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, @@ -1818,7 +1818,7 @@
"attributeFormDefault": {
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateEnumeratedExtraction("member attributeFormDefault", ["qualified", "unqualified"])
@@ -1826,23 +1826,23 @@
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateModelNameExtraction("member base", QName_model)
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateGetList("block", ["restriction", "extension", "substitution"])
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateGetList("block", ["restriction", "extension", "substitution"])
@@ -1850,16 +1850,16 @@
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GetAttributeValue
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateEnumeratedExtraction("member elementFormDefault", ["qualified", "unqualified"])
@@ -1867,9 +1867,9 @@
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateGetList("final", ["restriction", "extension", "substitution"]),
"simpleType": GenerateGetList("final", ["list", "union", "restriction"])
@@ -1877,7 +1877,7 @@
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateGetList("finalDefault", ["restriction", "extension", "list", "union"])
@@ -1885,9 +1885,9 @@
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "attribute": GetAttributeValue,
@@ -1901,35 +1901,35 @@
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateEnumeratedExtraction("member form", ["qualified", "unqualified"])
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateModelNameExtraction("member id", NCName_model)
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateModelNameExtraction("member itemType", QName_model)
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateModelNameListExtraction("member memberTypes", QNames_model)
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateLimitExtraction(),
"all": GenerateLimitExtraction(1, 1, False)
@@ -1940,7 +1940,7 @@
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateLimitExtraction(unbounded = False),
"all": GenerateLimitExtraction(0, 1, False)
@@ -1951,7 +1951,7 @@
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, @@ -1960,16 +1960,16 @@
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateModelNameExtraction("member name", NCName_model)
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateModelNameExtraction("member namespace", URI_model),
@@ -1981,14 +1981,14 @@
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE,
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateEnumeratedExtraction("member processContents", ["lax", "skip", "strict"])
@@ -1996,9 +1996,9 @@
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateModelNameExtraction("member ref", QName_model)
@@ -2010,44 +2010,44 @@
"default": GenerateModelNameExtraction("member refer", QName_model)
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateModelNameExtraction("member schemaLocation", URI_model)
"default": GenerateModelNameExtraction("member source", URI_model)
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateModelNameExtraction("member substitutionGroup", QName_model)
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateModelNameExtraction("member targetNamespace", URI_model)
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateModelNameExtraction("member type", QName_model)
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GenerateEnumeratedExtraction("member usage", ["required", "optional", "prohibited"])
@@ -2057,7 +2057,7 @@
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, "default": GetAttributeValue,
"fractionDigits": GenerateIntegerExtraction(minInclusive=0),
@@ -2077,13 +2077,13 @@
- "type": SYNTAXATTRIBUTE,
+ "type": SYNTAXATTRIBUTE, # "default": NotSupportedYet("xpath")
"default": GetAttributeValue
#-------------------------------------------------------------------------------
# Simple types definition
#-------------------------------------------------------------------------------
@@ -2110,27 +2110,27 @@
"generate": GenerateSimpleTypeXMLText(lambda x : x),
"check": lambda x: isinstance(x, (StringType, UnicodeType))
- "basename": "base64Binary",
+ "basename": "base64Binary", "extract": NotSupportedYet("base64Binary"),
"generate": GenerateSimpleTypeXMLText(str),
"check": lambda x: isinstance(x, (IntType, LongType))
- "basename": "hexBinary",
+ "basename": "hexBinary", "extract": GetHexInteger,
"generate": GenerateSimpleTypeXMLText(lambda x: ("%."+str(int(round(len("%X"%x)/2.)*2))+"X")%x),
@@ -2140,24 +2140,24 @@
"extract": GenerateIntegerExtraction(),
"facets": DECIMAL_FACETS,
"generate": GenerateSimpleTypeXMLText(str),
"check": lambda x: isinstance(x, IntType)
- "basename": "positiveInteger",
+ "basename": "positiveInteger", "extract": GenerateIntegerExtraction(minExclusive=0),
"facets": DECIMAL_FACETS,
"generate": GenerateSimpleTypeXMLText(str),
"check": lambda x: isinstance(x, IntType)
"basename": "negativeInteger",
@@ -2167,27 +2167,27 @@
"check": lambda x: isinstance(x, IntType)
- "basename": "nonNegativeInteger",
+ "basename": "nonNegativeInteger", "extract": GenerateIntegerExtraction(minInclusive=0),
"facets": DECIMAL_FACETS,
"generate": GenerateSimpleTypeXMLText(str),
"check": lambda x: isinstance(x, IntType)
- "basename": "nonPositiveInteger",
+ "basename": "nonPositiveInteger", "extract": GenerateIntegerExtraction(maxInclusive=0),
"facets": DECIMAL_FACETS,
"generate": GenerateSimpleTypeXMLText(str),
"check": lambda x: isinstance(x, IntType)
@@ -2197,7 +2197,7 @@
"check": lambda x: isinstance(x, IntType)
"basename": "unsignedLong",
@@ -2240,7 +2240,7 @@
- "basename": "unsignedShort",
+ "basename": "unsignedShort", "extract": GenerateIntegerExtraction(minInclusive=0,maxExclusive=2**16),
"facets": DECIMAL_FACETS,
"generate": GenerateSimpleTypeXMLText(str),
@@ -2306,7 +2306,7 @@
"generate": GenerateSimpleTypeXMLText(lambda x:{True : "true", False : "false"}[x]),
"initial": lambda: False,
"check": lambda x: isinstance(x, BooleanType)
@@ -2337,7 +2337,7 @@
"initial": lambda: datetime.date(1,1,1),
"check": lambda x: isinstance(x, datetime.date)
@@ -2407,7 +2407,7 @@
"check": lambda x: isinstance(x, (StringType, UnicodeType))
@@ -2531,4 +2531,3 @@
"anyType": {"type": COMPLEXTYPE, "extract": lambda x:None},