graphics/GraphicCommons.py
changeset 1176 f4b434672204
parent 1173 ad09b4a755ce
child 1226 93e7a8abce5e
equal deleted inserted replaced
1175:01842255c9ff 1176:f4b434672204
    21 #You should have received a copy of the GNU General Public
    21 #You should have received a copy of the GNU General Public
    22 #License along with this library; if not, write to the Free Software
    22 #License along with this library; if not, write to the Free Software
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    24 
    24 
    25 import wx
    25 import wx
    26 from time import time as gettime
       
    27 from math import *
    26 from math import *
    28 from types import *
    27 from types import *
    29 import datetime
    28 import datetime
    30 from threading import Lock,Timer
    29 from threading import Lock,Timer
    31 
    30 
    32 from graphics.ToolTipProducer import ToolTipProducer
    31 from graphics.ToolTipProducer import ToolTipProducer
       
    32 from graphics.DebugDataConsumer import DebugDataConsumer
    33 
    33 
    34 #-------------------------------------------------------------------------------
    34 #-------------------------------------------------------------------------------
    35 #                               Common constants
    35 #                               Common constants
    36 #-------------------------------------------------------------------------------
    36 #-------------------------------------------------------------------------------
    37 
    37 
   184         return (-v_base[0], -v_base[1])
   184         return (-v_base[0], -v_base[1])
   185     elif dir_product == 0 and product(v_base, dir_target) != 0:
   185     elif dir_product == 0 and product(v_base, dir_target) != 0:
   186         return dir_target
   186         return dir_target
   187     return v_base
   187     return v_base
   188 
   188 
   189 SECOND = 1000000
       
   190 MINUTE = 60 * SECOND
       
   191 HOUR = 60 * MINUTE
       
   192 DAY = 24 * HOUR
       
   193 
       
   194 def generate_time(value):
       
   195     microseconds = float(value.days * DAY + value.seconds * SECOND + value.microseconds)
       
   196     negative = microseconds < 0
       
   197     microseconds = abs(microseconds)
       
   198     data = "T#"
       
   199     not_null = False
       
   200     if negative:
       
   201         data += "-"
       
   202     for val, format in [(int(microseconds) / DAY, "%dd"),
       
   203                         ((int(microseconds) % DAY) / HOUR, "%dh"),
       
   204                         ((int(microseconds) % HOUR) / MINUTE, "%dm"),
       
   205                         ((int(microseconds) % MINUTE) / SECOND, "%ds")]:
       
   206         if val > 0 or not_null:
       
   207             data += format % val
       
   208             not_null = True
       
   209     data += "%gms" % (microseconds % SECOND / 1000.)
       
   210     return data
       
   211 
       
   212 def generate_date(value):
       
   213     base_date = datetime.datetime(1970, 1, 1)
       
   214     date = base_date + value 
       
   215     return date.strftime("DATE#%Y-%m-%d")
       
   216 
       
   217 def generate_datetime(value):
       
   218     base_date = datetime.datetime(1970, 1, 1)
       
   219     date_time = base_date + value 
       
   220     return date_time.strftime("DT#%Y-%m-%d-%H:%M:%S.%f")
       
   221 
       
   222 def generate_timeofday(value):
       
   223     microseconds = float(value.days * DAY + value.seconds * SECOND + value.microseconds)
       
   224     negative = microseconds < 0
       
   225     microseconds = abs(microseconds)
       
   226     data = "TOD#"
       
   227     for val, format in [(int(microseconds) / HOUR, "%2.2d:"),
       
   228                         ((int(microseconds) % HOUR) / MINUTE, "%2.2d:"),
       
   229                         ((int(microseconds) % MINUTE) / SECOND, "%2.2d."),
       
   230                         (microseconds % SECOND, "%6.6d")]:
       
   231         data += format % val
       
   232     return data
       
   233 
       
   234 TYPE_TRANSLATOR = {"TIME": generate_time,
       
   235                    "DATE": generate_date,
       
   236                    "DT": generate_datetime,
       
   237                    "TOD": generate_timeofday}
       
   238 
       
   239 def MiterPen(colour, width=1, style=wx.SOLID):
   189 def MiterPen(colour, width=1, style=wx.SOLID):
   240     pen = wx.Pen(colour, width, style)
   190     pen = wx.Pen(colour, width, style)
   241     pen.SetJoin(wx.JOIN_MITER)
   191     pen.SetJoin(wx.JOIN_MITER)
   242     pen.SetCap(wx.CAP_PROJECTING)
   192     pen.SetCap(wx.CAP_PROJECTING)
   243     return pen
   193     return pen
   244 
       
   245 #-------------------------------------------------------------------------------
       
   246 #                            Debug Data Consumer Class
       
   247 #-------------------------------------------------------------------------------
       
   248 
       
   249 class DebugDataConsumer:
       
   250     
       
   251     def __init__(self):
       
   252         self.LastValue = None
       
   253         self.Value = None
       
   254         self.DataType = None
       
   255         self.LastForced = False
       
   256         self.Forced = False
       
   257         self.Inhibited = False
       
   258     
       
   259     def Inhibit(self, inhibit):
       
   260         self.Inhibited = inhibit
       
   261         if not inhibit and self.LastValue is not None:
       
   262             self.SetForced(self.LastForced)
       
   263             self.SetValue(self.LastValue)
       
   264             self.LastValue = None
       
   265     
       
   266     def SetDataType(self, data_type):
       
   267         self.DataType = data_type
       
   268     
       
   269     def NewValue(self, tick, value, forced=False):
       
   270         value = TYPE_TRANSLATOR.get(self.DataType, lambda x:x)(value)
       
   271         if self.Inhibited:
       
   272             self.LastValue = value
       
   273             self.LastForced = forced
       
   274         else:
       
   275             self.SetForced(forced)
       
   276             self.SetValue(value)
       
   277     
       
   278     def SetValue(self, value):
       
   279         self.Value = value
       
   280     
       
   281     def GetValue(self):
       
   282         return self.Value
       
   283     
       
   284     def SetForced(self, forced):
       
   285         self.Forced = forced
       
   286     
       
   287     def IsForced(self):
       
   288         return self.Forced
       
   289 
       
   290 #-------------------------------------------------------------------------------
       
   291 #                               Debug Viewer Class
       
   292 #-------------------------------------------------------------------------------
       
   293 
       
   294 REFRESH_PERIOD = 0.1
       
   295 DEBUG_REFRESH_LOCK = Lock()
       
   296 
       
   297 class DebugViewer:
       
   298     
       
   299     def __init__(self, producer, debug, register_tick=True):
       
   300         self.DataProducer = None
       
   301         self.Debug = debug
       
   302         self.RegisterTick = register_tick
       
   303         self.Inhibited = False
       
   304         
       
   305         self.DataConsumers = {}
       
   306         
       
   307         self.LastRefreshTime = gettime()
       
   308         self.HasAcquiredLock = False
       
   309         self.AccessLock = Lock()
       
   310         self.TimerAccessLock = Lock()
       
   311         
       
   312         self.LastRefreshTimer = None
       
   313         
       
   314         self.SetDataProducer(producer)
       
   315         
       
   316     def __del__(self):
       
   317         self.DataProducer = None
       
   318         self.DeleteDataConsumers()
       
   319         if self.LastRefreshTimer is not None:
       
   320             self.LastRefreshTimer.Stop()
       
   321         if self.HasAcquiredLock:
       
   322             DEBUG_REFRESH_LOCK.release()
       
   323     
       
   324     def SetDataProducer(self, producer):
       
   325         if self.RegisterTick and self.Debug:
       
   326             if producer is not None:
       
   327                 producer.SubscribeDebugIECVariable("__tick__", self)
       
   328             if self.DataProducer is not None:
       
   329                 self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self)
       
   330         self.DataProducer = producer
       
   331     
       
   332     def IsDebugging(self):
       
   333         return self.Debug
       
   334     
       
   335     def Inhibit(self, inhibit):
       
   336         for consumer, iec_path in self.DataConsumers.iteritems():
       
   337             consumer.Inhibit(inhibit)
       
   338         self.Inhibited = inhibit
       
   339     
       
   340     def AddDataConsumer(self, iec_path, consumer):
       
   341         if self.DataProducer is None:
       
   342             return None
       
   343         result = self.DataProducer.SubscribeDebugIECVariable(iec_path, consumer)
       
   344         if result is not None and consumer != self:
       
   345             self.DataConsumers[consumer] = iec_path
       
   346             consumer.SetDataType(self.GetDataType(iec_path))
       
   347         return result
       
   348     
       
   349     def RemoveDataConsumer(self, consumer):
       
   350         iec_path = self.DataConsumers.pop(consumer, None)
       
   351         if iec_path is not None:
       
   352             self.DataProducer.UnsubscribeDebugIECVariable(iec_path, consumer)
       
   353     
       
   354     def RegisterVariables(self):
       
   355         if self.RegisterTick and self.Debug and self.DataProducer is not None:
       
   356             self.DataProducer.SubscribeDebugIECVariable("__tick__", self)
       
   357     
       
   358     def GetDataType(self, iec_path):
       
   359         if self.DataProducer is not None:
       
   360             data_type = self.DataProducer.GetDebugIECVariableType(iec_path.upper())
       
   361             if data_type is not None:
       
   362                 return data_type
       
   363             
       
   364             infos = self.DataProducer.GetInstanceInfos(iec_path)
       
   365             if infos is not None:
       
   366                 return infos["type"]
       
   367         return None
       
   368     
       
   369     def IsNumType(self, data_type):
       
   370         return self.DataProducer.IsNumType(data_type)
       
   371     
       
   372     def ForceDataValue(self, iec_path, value):
       
   373         if self.DataProducer is not None:
       
   374             self.DataProducer.ForceDebugIECVariable(iec_path, value)
       
   375     
       
   376     def ReleaseDataValue(self, iec_path):
       
   377         if self.DataProducer is not None:
       
   378             self.DataProducer.ReleaseDebugIECVariable(iec_path)
       
   379     
       
   380     def DeleteDataConsumers(self):
       
   381         if self.DataProducer is not None:
       
   382             for consumer, iec_path in self.DataConsumers.iteritems():
       
   383                 self.DataProducer.UnsubscribeDebugIECVariable(iec_path, consumer)
       
   384         self.DataConsumers = {}
       
   385     
       
   386     def ShouldRefresh(self):
       
   387         if self:
       
   388             wx.CallAfter(self._ShouldRefresh)
       
   389         
       
   390     def _ShouldRefresh(self):
       
   391         if self:
       
   392             if DEBUG_REFRESH_LOCK.acquire(False):
       
   393                 self.AccessLock.acquire()
       
   394                 self.HasAcquiredLock = True
       
   395                 self.AccessLock.release()
       
   396                 self.RefreshNewData()
       
   397             else:
       
   398                 self.TimerAccessLock.acquire()
       
   399                 self.LastRefreshTimer = Timer(REFRESH_PERIOD, self.ShouldRefresh)
       
   400                 self.LastRefreshTimer.start()
       
   401                 self.TimerAccessLock.release()
       
   402     
       
   403     def NewDataAvailable(self, tick, *args, **kwargs):
       
   404         self.TimerAccessLock.acquire()
       
   405         if self.LastRefreshTimer is not None:
       
   406             self.LastRefreshTimer.cancel()
       
   407             self.LastRefreshTimer=None
       
   408         self.TimerAccessLock.release()
       
   409         if self.IsShown() and not self.Inhibited:
       
   410             if gettime() - self.LastRefreshTime > REFRESH_PERIOD and DEBUG_REFRESH_LOCK.acquire(False):
       
   411                 self.AccessLock.acquire()
       
   412                 self.HasAcquiredLock = True
       
   413                 self.AccessLock.release()
       
   414                 self.LastRefreshTime = gettime()
       
   415                 self.Inhibit(True)
       
   416                 wx.CallAfter(self.RefreshViewOnNewData, *args, **kwargs)
       
   417             else:
       
   418                 self.TimerAccessLock.acquire()
       
   419                 self.LastRefreshTimer = Timer(REFRESH_PERIOD, self.ShouldRefresh)
       
   420                 self.LastRefreshTimer.start()
       
   421                 self.TimerAccessLock.release()
       
   422         elif not self.IsShown() and self.HasAcquiredLock:
       
   423             DebugViewer.RefreshNewData(self)
       
   424             
       
   425     def RefreshViewOnNewData(self, *args, **kwargs):
       
   426         if self:
       
   427             self.RefreshNewData(*args, **kwargs)
       
   428     
       
   429     def RefreshNewData(self, *args, **kwargs):
       
   430         self.Inhibit(False)
       
   431         self.AccessLock.acquire()
       
   432         if self.HasAcquiredLock:
       
   433             DEBUG_REFRESH_LOCK.release()
       
   434             self.HasAcquiredLock = False
       
   435         if gettime() - self.LastRefreshTime > REFRESH_PERIOD:
       
   436             self.LastRefreshTime = gettime()
       
   437         self.AccessLock.release()
       
   438 
   194 
   439 #-------------------------------------------------------------------------------
   195 #-------------------------------------------------------------------------------
   440 #                    Helpers for highlighting text
   196 #                    Helpers for highlighting text
   441 #-------------------------------------------------------------------------------
   197 #-------------------------------------------------------------------------------
   442 
   198 
  1375             if self.Visible:
  1131             if self.Visible:
  1376                 self.Parent.ElementNeedRefresh(self)
  1132                 self.Parent.ElementNeedRefresh(self)
  1377     
  1133     
  1378     def GetComputedValue(self):
  1134     def GetComputedValue(self):
  1379         if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
  1135         if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
  1380             wire_type = self.GetType()
  1136             return self.Value
  1381             if wire_type == "STRING":
       
  1382                 return "'%s'"%self.Value
       
  1383             elif wire_type == "WSTRING":
       
  1384                 return "\"%s\""%self.Value
       
  1385             else:
       
  1386                 return str(self.Value)
       
  1387         return None
  1137         return None
  1388     
  1138     
  1389     def GetToolTipValue(self):
  1139     def GetToolTipValue(self):
  1390         return self.GetComputedValue()
  1140         return self.GetComputedValue()
  1391     
  1141     
  1958             if self.Visible:
  1708             if self.Visible:
  1959                 self.Parent.ElementNeedRefresh(self)
  1709                 self.Parent.ElementNeedRefresh(self)
  1960 
  1710 
  1961     def GetComputedValue(self):
  1711     def GetComputedValue(self):
  1962         if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
  1712         if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
  1963             wire_type = self.GetEndConnectedType()
  1713             return self.Value
  1964             if wire_type == "STRING":
       
  1965                 return "'%s'"%self.Value
       
  1966             elif wire_type == "WSTRING":
       
  1967                 return "\"%s\""%self.Value
       
  1968             else:
       
  1969                 return str(self.Value)
       
  1970         return None
  1714         return None
  1971     
  1715     
  1972     def GetToolTipValue(self):
  1716     def GetToolTipValue(self):
  1973         return self.GetComputedValue()
  1717         return self.GetComputedValue()
  1974 
  1718