|
1 #!/usr/bin/env python |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor |
|
5 #based on the plcopen standard. |
|
6 # |
|
7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD |
|
8 # |
|
9 #See COPYING file for copyrights details. |
|
10 # |
|
11 #This library is free software; you can redistribute it and/or |
|
12 #modify it under the terms of the GNU General Public |
|
13 #License as published by the Free Software Foundation; either |
|
14 #version 2.1 of the License, or (at your option) any later version. |
|
15 # |
|
16 #This library is distributed in the hope that it will be useful, |
|
17 #but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
19 #General Public License for more details. |
|
20 # |
|
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 |
|
23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
24 |
|
25 import re |
|
26 |
|
27 import wx |
|
28 |
|
29 SECOND = 1000 |
|
30 MINUTE = 60 * SECOND |
|
31 HOUR = 60 * MINUTE |
|
32 DAY = 24 * HOUR |
|
33 |
|
34 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]+)?"}) |
|
35 |
|
36 #------------------------------------------------------------------------------- |
|
37 # Edit Duration Value Dialog |
|
38 #------------------------------------------------------------------------------- |
|
39 |
|
40 [ID_DURATIONEDITORDIALOG, ID_DURATIONEDITORDIALOGDAYSLABEL, |
|
41 ID_DURATIONEDITORDIALOGDAYS, ID_DURATIONEDITORDIALOGHOURSLABEL, |
|
42 ID_DURATIONEDITORDIALOGHOURS, ID_DURATIONEDITORDIALOGMINUTESLABEL, |
|
43 ID_DURATIONEDITORDIALOGMINUTES, ID_DURATIONEDITORDIALOGSECONDSLABEL, |
|
44 ID_DURATIONEDITORDIALOGSECONDS, ID_DURATIONEDITORDIALOGMILLISECONDSLABEL, |
|
45 ID_DURATIONEDITORDIALOGMILLISECONDS, |
|
46 ] = [wx.NewId() for _init_ctrls in range(11)] |
|
47 |
|
48 class DurationEditorDialog(wx.Dialog): |
|
49 |
|
50 if wx.VERSION < (2, 6, 0): |
|
51 def Bind(self, event, function, id = None): |
|
52 if id is not None: |
|
53 event(self, id, function) |
|
54 else: |
|
55 event(self, function) |
|
56 |
|
57 def _init_coll_MainSizer_Items(self, parent): |
|
58 parent.AddSizer(self.ControlsSizer, 0, border=20, flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW) |
|
59 parent.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW) |
|
60 |
|
61 def _init_coll_MainSizer_Growables(self, parent): |
|
62 parent.AddGrowableCol(0) |
|
63 parent.AddGrowableRow(0) |
|
64 |
|
65 def _init_coll_ControlsSizer_Items(self, parent): |
|
66 parent.AddWindow(self.DaysLabel, 0, border=0, flag=wx.GROW) |
|
67 parent.AddWindow(self.HoursLabel, 0, border=0, flag=wx.GROW) |
|
68 parent.AddWindow(self.MinutesLabel, 0, border=0, flag=wx.GROW) |
|
69 parent.AddWindow(self.SecondsLabel, 0, border=0, flag=wx.GROW) |
|
70 parent.AddWindow(self.MillisecondsLabel, 0, border=0, flag=wx.GROW) |
|
71 parent.AddWindow(self.Days, 0, border=0, flag=wx.GROW) |
|
72 parent.AddWindow(self.Hours, 0, border=0, flag=wx.GROW) |
|
73 parent.AddWindow(self.Minutes, 0, border=0, flag=wx.GROW) |
|
74 parent.AddWindow(self.Seconds, 0, border=0, flag=wx.GROW) |
|
75 parent.AddWindow(self.Milliseconds, 0, border=0, flag=wx.GROW) |
|
76 |
|
77 def _init_coll_ControlsSizer_Growables(self, parent): |
|
78 parent.AddGrowableCol(0) |
|
79 parent.AddGrowableCol(1) |
|
80 parent.AddGrowableCol(2) |
|
81 parent.AddGrowableCol(3) |
|
82 parent.AddGrowableCol(4) |
|
83 |
|
84 def _init_sizers(self): |
|
85 self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) |
|
86 self.ControlsSizer = wx.FlexGridSizer(cols=5, hgap=10, rows=2, vgap=10) |
|
87 |
|
88 self._init_coll_MainSizer_Items(self.MainSizer) |
|
89 self._init_coll_MainSizer_Growables(self.MainSizer) |
|
90 self._init_coll_ControlsSizer_Items(self.ControlsSizer) |
|
91 self._init_coll_ControlsSizer_Growables(self.ControlsSizer) |
|
92 |
|
93 self.SetSizer(self.MainSizer) |
|
94 |
|
95 def _init_ctrls(self, prnt): |
|
96 wx.Dialog.__init__(self, id=ID_DURATIONEDITORDIALOG, |
|
97 name='DurationEditorDialog', parent=prnt, |
|
98 size=wx.Size(600, 200), style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER, |
|
99 title=_('Edit Duration')) |
|
100 |
|
101 self.DaysLabel = wx.StaticText(id=ID_DURATIONEDITORDIALOGDAYSLABEL, |
|
102 label=_('Days:'), name='DaysLabel', parent=self, |
|
103 pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0) |
|
104 |
|
105 self.Days = wx.TextCtrl(id=ID_DURATIONEDITORDIALOGDAYS, value='0', |
|
106 name='Days', parent=self, pos=wx.Point(0, 0), |
|
107 size=wx.Size(0, 24), style=wx.TE_PROCESS_ENTER) |
|
108 self.Bind(wx.EVT_TEXT_ENTER, self.GetControlValueTestFunction(self.Days), id=ID_DURATIONEDITORDIALOGDAYS) |
|
109 |
|
110 self.HoursLabel = wx.StaticText(id=ID_DURATIONEDITORDIALOGHOURSLABEL, |
|
111 label=_('Hours:'), name='HoursLabel', parent=self, |
|
112 pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0) |
|
113 |
|
114 self.Hours = wx.TextCtrl(id=ID_DURATIONEDITORDIALOGHOURS, value='0', |
|
115 name='Hours', parent=self, pos=wx.Point(0, 0), |
|
116 size=wx.Size(0, 24), style=wx.TE_PROCESS_ENTER) |
|
117 self.Bind(wx.EVT_TEXT_ENTER, self.GetControlValueTestFunction(self.Hours), id=ID_DURATIONEDITORDIALOGHOURS) |
|
118 |
|
119 self.MinutesLabel = wx.StaticText(id=ID_DURATIONEDITORDIALOGMINUTESLABEL, |
|
120 label=_('Minutes:'), name='MinutesLabel', parent=self, |
|
121 pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0) |
|
122 |
|
123 self.Minutes = wx.TextCtrl(id=ID_DURATIONEDITORDIALOGMINUTES, value='0', |
|
124 name='Minutes', parent=self, pos=wx.Point(0, 0), |
|
125 size=wx.Size(0, 24), style=wx.TE_PROCESS_ENTER) |
|
126 self.Bind(wx.EVT_TEXT_ENTER, self.GetControlValueTestFunction(self.Minutes), id=ID_DURATIONEDITORDIALOGMINUTES) |
|
127 |
|
128 self.SecondsLabel = wx.StaticText(id=ID_DURATIONEDITORDIALOGSECONDSLABEL, |
|
129 label=_('Seconds:'), name='SecondsLabel', parent=self, |
|
130 pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0) |
|
131 |
|
132 self.Seconds = wx.TextCtrl(id=ID_DURATIONEDITORDIALOGSECONDS, value='0', |
|
133 name='Seconds', parent=self, pos=wx.Point(0, 0), |
|
134 size=wx.Size(0, 24), style=wx.TE_PROCESS_ENTER) |
|
135 self.Bind(wx.EVT_TEXT_ENTER, self.GetControlValueTestFunction(self.Seconds), id=ID_DURATIONEDITORDIALOGSECONDS) |
|
136 |
|
137 self.MillisecondsLabel = wx.StaticText(id=ID_DURATIONEDITORDIALOGMILLISECONDSLABEL, |
|
138 label=_('Milliseconds:'), name='MillisecondsLabel', parent=self, |
|
139 pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0) |
|
140 |
|
141 self.Milliseconds = wx.TextCtrl(id=ID_DURATIONEDITORDIALOGMILLISECONDS, value='0', |
|
142 name='Milliseconds', parent=self, pos=wx.Point(0, 0), |
|
143 size=wx.Size(0, 24), style=wx.TE_PROCESS_ENTER) |
|
144 self.Bind(wx.EVT_TEXT_ENTER, self.GetControlValueTestFunction(self.Milliseconds), id=ID_DURATIONEDITORDIALOGMILLISECONDS) |
|
145 |
|
146 self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) |
|
147 if wx.VERSION >= (2, 5, 0): |
|
148 self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetAffirmativeButton().GetId()) |
|
149 else: |
|
150 self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId()) |
|
151 |
|
152 self._init_sizers() |
|
153 |
|
154 def __init__(self, parent): |
|
155 self._init_ctrls(parent) |
|
156 |
|
157 def SetDuration(self, value): |
|
158 result = IEC_TIME_MODEL.match(value.upper()) |
|
159 if result is not None: |
|
160 values = result.groups() |
|
161 for control, index in [(self.Days, 1), (self.Hours, 2), |
|
162 (self.Minutes, 3), (self.Seconds, 4), |
|
163 (self.Milliseconds, 5)]: |
|
164 value = values[index] |
|
165 if value is not None: |
|
166 control.SetValue(str(value)) |
|
167 |
|
168 def GetControlValueTestFunction(self, control): |
|
169 def OnValueChanged(event): |
|
170 try: |
|
171 value = float(control.GetValue()) |
|
172 except ValueError, e: |
|
173 message = wx.MessageDialog(self, _("Invalid value!\nYou must fill a numeric value."), _("Error"), wx.OK|wx.ICON_ERROR) |
|
174 message.ShowModal() |
|
175 message.Destroy() |
|
176 event.Skip() |
|
177 return OnValueChanged |
|
178 |
|
179 def GetDuration(self): |
|
180 milliseconds = 0 |
|
181 for control, factor in [(self.Days, DAY), (self.Hours, HOUR), |
|
182 (self.Minutes, MINUTE), (self.Seconds, SECOND), |
|
183 (self.Milliseconds, 1)]: |
|
184 |
|
185 milliseconds += float(control.GetValue()) * factor |
|
186 |
|
187 not_null = False |
|
188 duration = "T#" |
|
189 for value, format in [(int(milliseconds) / DAY, "%dd"), |
|
190 ((int(milliseconds) % DAY) / HOUR, "%dh"), |
|
191 ((int(milliseconds) % HOUR) / MINUTE, "%dm"), |
|
192 ((int(milliseconds) % MINUTE) / SECOND, "%ds")]: |
|
193 |
|
194 if value > 0 or not_null: |
|
195 duration += format % value |
|
196 not_null = True |
|
197 |
|
198 duration += "%gms" % (milliseconds % SECOND) |
|
199 return duration |
|
200 |
|
201 def OnOK(self, event): |
|
202 errors = [] |
|
203 for control, name in [(self.Days, "days"), (self.Hours, "hours"), |
|
204 (self.Minutes, "minutes"), (self.Seconds, "seconds"), |
|
205 (self.Milliseconds, "milliseconds")]: |
|
206 try: |
|
207 value = float(control.GetValue()) |
|
208 except ValueError, e: |
|
209 errors.append(name) |
|
210 if len(errors) > 0: |
|
211 if len(errors) == 1: |
|
212 message = _("Field %s hasn't a valid value!") % errors[0] |
|
213 else: |
|
214 message = _("Fields %s haven't a valid value!") % ",".join(errors) |
|
215 dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) |
|
216 dialog.ShowModal() |
|
217 dialog.Destroy() |
|
218 else: |
|
219 self.EndModal(wx.ID_OK) |