Laurent@814: #!/usr/bin/env python Laurent@814: # -*- coding: utf-8 -*- Laurent@814: andrej@1571: # This file is part of Beremiz, a Integrated Development Environment for andrej@1571: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. Laurent@814: # andrej@1571: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD andrej@1696: # Copyright (C) 2017: Andrey Skvortsov Laurent@814: # andrej@1571: # See COPYING file for copyrights details. Laurent@814: # andrej@1571: # This program is free software; you can redistribute it and/or andrej@1571: # modify it under the terms of the GNU General Public License andrej@1571: # as published by the Free Software Foundation; either version 2 andrej@1571: # of the License, or (at your option) any later version. Laurent@814: # andrej@1571: # This program is distributed in the hope that it will be useful, andrej@1571: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1571: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrej@1571: # GNU General Public License for more details. Laurent@814: # andrej@1571: # You should have received a copy of the GNU General Public License andrej@1571: # along with this program; if not, write to the Free Software andrej@1571: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Laurent@814: Laurent@814: import re Laurent@814: Laurent@814: import wx Laurent@814: andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: # Helpers andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: Laurent@814: MICROSECONDS = 0.001 Laurent@814: MILLISECONDS = 1 Laurent@814: SECOND = 1000 Laurent@814: MINUTE = 60 * SECOND Laurent@814: HOUR = 60 * MINUTE Laurent@814: DAY = 24 * HOUR Laurent@814: Laurent@879: IEC_TIME_MODEL = re.compile("(?:T|TIME)#(-)?(?:(%(float)s)D_?)?(?:(%(float)s)H_?)?(?:(%(float)s)M(?!S)_?)?(?:(%(float)s)S_?)?(?:(%(float)s)MS)?$" % {"float": "[0-9]+(?:\.[0-9]+)?"}) Laurent@814: Laurent@814: CONTROLS = [ Laurent@814: ("Days", _('Days:')), Laurent@814: ("Hours", _('Hours:')), Laurent@814: ("Minutes", _('Minutes:')), Laurent@814: ("Seconds", _('Seconds:')), Laurent@814: ("Milliseconds", _('Milliseconds:')), Laurent@814: ("Microseconds", _('Microseconds:')), Laurent@814: ] Laurent@814: andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: # Edit Duration Value Dialog andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: andrej@1736: Laurent@814: class DurationEditorDialog(wx.Dialog): Laurent@814: Laurent@814: def __init__(self, parent): andrej@1696: wx.Dialog.__init__(self, parent, title=_('Edit Duration')) andrej@1730: Laurent@814: main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) Laurent@814: main_sizer.AddGrowableCol(0) Laurent@814: main_sizer.AddGrowableRow(0) andrej@1730: Laurent@814: controls_sizer = wx.FlexGridSizer(cols=len(CONTROLS), hgap=10, rows=2, vgap=10) andrej@1730: main_sizer.AddSizer(controls_sizer, border=20, andrej@1768: flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) andrej@1730: Laurent@814: controls = [] Laurent@814: for i, (name, label) in enumerate(CONTROLS): Laurent@814: controls_sizer.AddGrowableCol(i) andrej@1730: Laurent@814: st = wx.StaticText(self, label=label) Laurent@814: txtctrl = wx.TextCtrl(self, value='0', style=wx.TE_PROCESS_ENTER) andrej@1730: self.Bind(wx.EVT_TEXT_ENTER, andrej@1730: self.GetControlValueTestFunction(txtctrl), Laurent@814: txtctrl) Laurent@814: setattr(self, name, txtctrl) andrej@1730: Laurent@814: controls.append((st, txtctrl)) andrej@1730: Laurent@814: for st, txtctrl in controls: Laurent@814: controls_sizer.AddWindow(st, flag=wx.GROW) andrej@1730: Laurent@814: for st, txtctrl in controls: Laurent@814: controls_sizer.AddWindow(txtctrl, flag=wx.GROW) andrej@1730: andrej@1745: button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) Laurent@814: self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) andrej@1730: main_sizer.AddSizer(button_sizer, border=20, andrej@1768: flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) andrej@1730: Laurent@814: self.SetSizer(main_sizer) andrej@1696: self.Fit() Laurent@814: self.Days.SetFocus() andrej@1730: Laurent@814: def SetDuration(self, value): Laurent@814: result = IEC_TIME_MODEL.match(value.upper()) Laurent@814: if result is not None: Laurent@814: values = result.groups() Laurent@814: for control, index in [(self.Days, 1), (self.Hours, 2), Laurent@814: (self.Minutes, 3), (self.Seconds, 4)]: Laurent@814: value = values[index] Laurent@814: if value is not None: Laurent@814: control.SetValue(value) Laurent@814: else: Laurent@814: control.SetValue("0") Laurent@814: milliseconds = values[5] Laurent@814: if milliseconds is not None: Laurent@814: self.Milliseconds.SetValue("%d" % int(float(milliseconds))) Laurent@814: self.Microseconds.SetValue("%.3f" % ((float(milliseconds) % MILLISECONDS) / MICROSECONDS)) Laurent@814: else: Laurent@814: self.Milliseconds.SetValue("0") Laurent@814: self.Microseconds.SetValue("0") andrej@1730: Laurent@814: def GetControlValueTestFunction(self, control): Laurent@814: def OnValueChanged(event): Laurent@814: try: Laurent@814: value = float(control.GetValue()) Laurent@814: except ValueError, e: andrej@1745: message = wx.MessageDialog(self, _("Invalid value!\nYou must fill a numeric value."), _("Error"), wx.OK | wx.ICON_ERROR) Laurent@814: message.ShowModal() Laurent@814: message.Destroy() Laurent@814: event.Skip() andrej@1702: self.OnCloseDialog() Laurent@814: return OnValueChanged Laurent@814: Laurent@814: def GetDuration(self): Laurent@814: milliseconds = 0 Laurent@814: for control, factor in [(self.Days, DAY), (self.Hours, HOUR), Laurent@814: (self.Minutes, MINUTE), (self.Seconds, SECOND), Laurent@814: (self.Milliseconds, MILLISECONDS), (self.Microseconds, MICROSECONDS)]: andrej@1730: Laurent@814: milliseconds += float(control.GetValue()) * factor andrej@1730: Laurent@814: not_null = False Laurent@814: duration = "T#" andrej@1768: for value, format in [((int(milliseconds) / DAY), "%dd"), andrej@1768: ((int(milliseconds) % DAY) / HOUR, "%dh"), andrej@1768: ((int(milliseconds) % HOUR) / MINUTE, "%dm"), andrej@1768: ((int(milliseconds) % MINUTE) / SECOND, "%ds")]: andrej@1730: Laurent@814: if value > 0 or not_null: Laurent@814: duration += format % value Laurent@814: not_null = True andrej@1730: Laurent@814: duration += "%gms" % (milliseconds % SECOND) Laurent@814: return duration andrej@1730: Laurent@814: def OnOK(self, event): andrej@1702: self.OnCloseDialog() andrej@1702: andrej@1702: def OnCloseDialog(self): Laurent@814: errors = [] andrej@1730: for control, name in [(self.Days, _("days")), (self.Hours, _("hours")), andrej@1495: (self.Minutes, _("minutes")), (self.Seconds, _("seconds")), andrej@1495: (self.Milliseconds, _("milliseconds"))]: Laurent@814: try: Laurent@814: value = float(control.GetValue()) Laurent@814: except ValueError, e: Laurent@814: errors.append(name) Laurent@814: if len(errors) > 0: Laurent@814: if len(errors) == 1: Laurent@814: message = _("Field %s hasn't a valid value!") % errors[0] Laurent@814: else: Laurent@814: message = _("Fields %s haven't a valid value!") % ",".join(errors) andrej@1745: dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) Laurent@814: dialog.ShowModal() Laurent@814: dialog.Destroy() Laurent@814: else: Laurent@814: self.EndModal(wx.ID_OK)