426
|
1 |
#!/usr/bin/env python
|
|
2 |
# -*- coding: utf-8 -*-
|
|
3 |
|
|
4 |
#This file is part of Beremiz, a Integrated Development Environment for
|
|
5 |
#programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
|
|
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 |
__version__ = "$Revision$"
|
|
26 |
|
|
27 |
import os, sys, getopt, wx
|
|
28 |
import tempfile
|
|
29 |
import shutil
|
|
30 |
import random
|
|
31 |
|
|
32 |
CWD = os.path.split(os.path.realpath(__file__))[0]
|
|
33 |
|
|
34 |
def Bpath(*args):
|
|
35 |
return os.path.join(CWD,*args)
|
|
36 |
|
|
37 |
if __name__ == '__main__':
|
|
38 |
def usage():
|
|
39 |
print "\nUsage of Beremiz.py :"
|
|
40 |
print "\n %s [Projectpath] [Buildpath]\n"%sys.argv[0]
|
|
41 |
|
|
42 |
try:
|
|
43 |
opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
|
|
44 |
except getopt.GetoptError:
|
|
45 |
# print help information and exit:
|
|
46 |
usage()
|
|
47 |
sys.exit(2)
|
|
48 |
|
|
49 |
for o, a in opts:
|
|
50 |
if o in ("-h", "--help"):
|
|
51 |
usage()
|
|
52 |
sys.exit()
|
|
53 |
|
|
54 |
if len(args) > 2:
|
|
55 |
usage()
|
|
56 |
sys.exit()
|
|
57 |
elif len(args) == 1:
|
|
58 |
projectOpen = args[0]
|
|
59 |
buildpath = None
|
|
60 |
elif len(args) == 2:
|
|
61 |
projectOpen = args[0]
|
|
62 |
buildpath = args[1]
|
|
63 |
else:
|
|
64 |
projectOpen = None
|
|
65 |
buildpath = None
|
|
66 |
|
|
67 |
app = wx.PySimpleApp()
|
|
68 |
app.SetAppName('beremiz')
|
|
69 |
wx.InitAllImageHandlers()
|
|
70 |
|
|
71 |
bmp = wx.Image(Bpath("images","splash.png")).ConvertToBitmap()
|
|
72 |
splash=wx.SplashScreen(bmp,wx.SPLASH_CENTRE_ON_SCREEN, 1000, None)
|
|
73 |
wx.Yield()
|
|
74 |
|
|
75 |
# Import module for internationalization
|
|
76 |
import gettext
|
|
77 |
import __builtin__
|
|
78 |
|
|
79 |
# Get folder containing translation files
|
|
80 |
localedir = os.path.join(CWD,"locale")
|
|
81 |
# Get the default language
|
|
82 |
langid = wx.LANGUAGE_DEFAULT
|
|
83 |
# Define translation domain (name of translation files)
|
|
84 |
domain = "Beremiz"
|
|
85 |
|
|
86 |
# Define locale for wx
|
|
87 |
loc = __builtin__.__dict__.get('loc', None)
|
|
88 |
if loc is None:
|
|
89 |
loc = wx.Locale(langid)
|
|
90 |
__builtin__.__dict__['loc'] = loc
|
|
91 |
# Define location for searching translation files
|
|
92 |
loc.AddCatalogLookupPathPrefix(localedir)
|
|
93 |
# Define locale domain
|
|
94 |
loc.AddCatalog(domain)
|
|
95 |
|
|
96 |
def unicode_translation(message):
|
|
97 |
return wx.GetTranslation(message).encode("utf-8")
|
|
98 |
|
|
99 |
if __name__ == '__main__':
|
|
100 |
__builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation
|
|
101 |
|
|
102 |
#Quick hack to be able to find Beremiz IEC tools. Should be config params.
|
|
103 |
base_folder = os.path.split(sys.path[0])[0]
|
|
104 |
sys.path.append(base_folder)
|
|
105 |
sys.path.append(os.path.join(base_folder, "plcopeneditor"))
|
|
106 |
sys.path.append(os.path.join(base_folder, "docutils"))
|
|
107 |
|
|
108 |
import wx.lib.buttons, wx.lib.statbmp
|
|
109 |
import TextCtrlAutoComplete, cPickle
|
|
110 |
import types, time, re, platform, time, traceback, commands
|
|
111 |
from plugger import PluginsRoot, MATIEC_ERROR_MODEL
|
|
112 |
from wxPopen import ProcessLogger
|
|
113 |
|
|
114 |
from docutils import *
|
|
115 |
from PLCOpenEditor import IDEFrame, Viewer, AppendMenu, TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, TYPESTREE, INSTANCESTREE, LIBRARYTREE, SCALING
|
|
116 |
from PLCControler import LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
|
|
117 |
|
|
118 |
SCROLLBAR_UNIT = 10
|
|
119 |
WINDOW_COLOUR = wx.Colour(240,240,240)
|
|
120 |
TITLE_COLOUR = wx.Colour(200,200,220)
|
|
121 |
CHANGED_TITLE_COLOUR = wx.Colour(220,200,220)
|
|
122 |
CHANGED_WINDOW_COLOUR = wx.Colour(255,240,240)
|
|
123 |
|
|
124 |
if wx.Platform == '__WXMSW__':
|
|
125 |
faces = { 'times': 'Times New Roman',
|
|
126 |
'mono' : 'Courier New',
|
|
127 |
'helv' : 'Arial',
|
|
128 |
'other': 'Comic Sans MS',
|
|
129 |
'size' : 16,
|
|
130 |
}
|
|
131 |
else:
|
|
132 |
faces = { 'times': 'Times',
|
|
133 |
'mono' : 'Courier',
|
|
134 |
'helv' : 'Helvetica',
|
|
135 |
'other': 'new century schoolbook',
|
|
136 |
'size' : 18,
|
|
137 |
}
|
|
138 |
|
|
139 |
# Some helpers to tweak GenBitmapTextButtons
|
|
140 |
# TODO: declare customized classes instead.
|
|
141 |
gen_mini_GetBackgroundBrush = lambda obj:lambda dc: wx.Brush(obj.GetParent().GetBackgroundColour(), wx.SOLID)
|
|
142 |
gen_textbutton_GetLabelSize = lambda obj:lambda:(wx.lib.buttons.GenButton._GetLabelSize(obj)[:-1] + (False,))
|
|
143 |
|
|
144 |
def make_genbitmaptogglebutton_flat(button):
|
|
145 |
button.GetBackgroundBrush = gen_mini_GetBackgroundBrush(button)
|
|
146 |
button.labelDelta = 0
|
|
147 |
button.SetBezelWidth(0)
|
|
148 |
button.SetUseFocusIndicator(False)
|
|
149 |
|
|
150 |
# Patch wx.lib.imageutils so that gray is supported on alpha images
|
|
151 |
import wx.lib.imageutils
|
|
152 |
from wx.lib.imageutils import grayOut as old_grayOut
|
|
153 |
def grayOut(anImage):
|
|
154 |
if anImage.HasAlpha():
|
|
155 |
AlphaData = anImage.GetAlphaData()
|
|
156 |
else :
|
|
157 |
AlphaData = None
|
|
158 |
|
|
159 |
old_grayOut(anImage)
|
|
160 |
|
|
161 |
if AlphaData is not None:
|
|
162 |
anImage.SetAlphaData(AlphaData)
|
|
163 |
|
|
164 |
wx.lib.imageutils.grayOut = grayOut
|
|
165 |
|
|
166 |
class GenBitmapTextButton(wx.lib.buttons.GenBitmapTextButton):
|
|
167 |
def _GetLabelSize(self):
|
|
168 |
""" used internally """
|
|
169 |
w, h = self.GetTextExtent(self.GetLabel())
|
|
170 |
if not self.bmpLabel:
|
|
171 |
return w, h, False # if there isn't a bitmap use the size of the text
|
|
172 |
|
|
173 |
w_bmp = self.bmpLabel.GetWidth()+2
|
|
174 |
h_bmp = self.bmpLabel.GetHeight()+2
|
|
175 |
height = h + h_bmp
|
|
176 |
if w_bmp > w:
|
|
177 |
width = w_bmp
|
|
178 |
else:
|
|
179 |
width = w
|
|
180 |
return width, height, False
|
|
181 |
|
|
182 |
def DrawLabel(self, dc, width, height, dw=0, dy=0):
|
|
183 |
bmp = self.bmpLabel
|
|
184 |
if bmp != None: # if the bitmap is used
|
|
185 |
if self.bmpDisabled and not self.IsEnabled():
|
|
186 |
bmp = self.bmpDisabled
|
|
187 |
if self.bmpFocus and self.hasFocus:
|
|
188 |
bmp = self.bmpFocus
|
|
189 |
if self.bmpSelected and not self.up:
|
|
190 |
bmp = self.bmpSelected
|
|
191 |
bw,bh = bmp.GetWidth(), bmp.GetHeight()
|
|
192 |
if not self.up:
|
|
193 |
dw = dy = self.labelDelta
|
|
194 |
hasMask = bmp.GetMask() != None
|
|
195 |
else:
|
|
196 |
bw = bh = 0 # no bitmap -> size is zero
|
|
197 |
|
|
198 |
dc.SetFont(self.GetFont())
|
|
199 |
if self.IsEnabled():
|
|
200 |
dc.SetTextForeground(self.GetForegroundColour())
|
|
201 |
else:
|
|
202 |
dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
|
|
203 |
|
|
204 |
label = self.GetLabel()
|
|
205 |
tw, th = dc.GetTextExtent(label) # size of text
|
|
206 |
if not self.up:
|
|
207 |
dw = dy = self.labelDelta
|
|
208 |
|
|
209 |
pos_x = (width-bw)/2+dw # adjust for bitmap and text to centre
|
|
210 |
pos_y = (height-bh-th)/2+dy
|
|
211 |
if bmp !=None:
|
|
212 |
dc.DrawBitmap(bmp, pos_x, pos_y, hasMask) # draw bitmap if available
|
|
213 |
pos_x = (width-tw)/2+dw # adjust for bitmap and text to centre
|
|
214 |
pos_y += bh + 2
|
|
215 |
|
|
216 |
dc.DrawText(label, pos_x, pos_y) # draw the text
|
|
217 |
|
|
218 |
|
|
219 |
class GenStaticBitmap(wx.lib.statbmp.GenStaticBitmap):
|
|
220 |
""" Customized GenStaticBitmap, fix transparency redraw bug on wx2.8/win32,
|
|
221 |
and accept image name as __init__ parameter, fail silently if file do not exist"""
|
|
222 |
def __init__(self, parent, ID, bitmapname,
|
|
223 |
pos = wx.DefaultPosition, size = wx.DefaultSize,
|
|
224 |
style = 0,
|
|
225 |
name = "genstatbmp"):
|
|
226 |
|
|
227 |
bitmappath = Bpath( "images", bitmapname)
|
|
228 |
if os.path.isfile(bitmappath):
|
|
229 |
bitmap = wx.Bitmap(bitmappath)
|
|
230 |
else:
|
|
231 |
bitmap = None
|
|
232 |
wx.lib.statbmp.GenStaticBitmap.__init__(self, parent, ID, bitmap,
|
|
233 |
pos, size,
|
|
234 |
style,
|
|
235 |
name)
|
|
236 |
|
|
237 |
def OnPaint(self, event):
|
|
238 |
dc = wx.PaintDC(self)
|
|
239 |
colour = self.GetParent().GetBackgroundColour()
|
|
240 |
dc.SetPen(wx.Pen(colour))
|
|
241 |
dc.SetBrush(wx.Brush(colour ))
|
|
242 |
dc.DrawRectangle(0, 0, *dc.GetSizeTuple())
|
|
243 |
if self._bitmap:
|
|
244 |
dc.DrawBitmap(self._bitmap, 0, 0, True)
|
|
245 |
|
|
246 |
|
|
247 |
class LogPseudoFile:
|
|
248 |
""" Base class for file like objects to facilitate StdOut for the Shell."""
|
|
249 |
def __init__(self, output):
|
|
250 |
self.red_white = wx.TextAttr("RED", "WHITE")
|
|
251 |
self.red_yellow = wx.TextAttr("RED", "YELLOW")
|
|
252 |
self.black_white = wx.TextAttr("BLACK", "WHITE")
|
|
253 |
self.default_style = None
|
|
254 |
self.output = output
|
|
255 |
|
|
256 |
def write(self, s, style = None):
|
|
257 |
if style is None : style=self.black_white
|
|
258 |
self.output.Freeze();
|
|
259 |
if self.default_style != style:
|
|
260 |
self.output.SetDefaultStyle(style)
|
|
261 |
self.default_style = style
|
|
262 |
self.output.AppendText(s)
|
|
263 |
self.output.ScrollLines(s.count('\n')+1)
|
|
264 |
self.output.ShowPosition(self.output.GetLastPosition())
|
|
265 |
self.output.Thaw()
|
|
266 |
|
|
267 |
def write_warning(self, s):
|
|
268 |
self.write(s,self.red_white)
|
|
269 |
|
|
270 |
def write_error(self, s):
|
|
271 |
self.write(s,self.red_yellow)
|
|
272 |
|
|
273 |
def flush(self):
|
|
274 |
self.output.SetValue("")
|
|
275 |
|
|
276 |
def isatty(self):
|
|
277 |
return false
|
|
278 |
|
|
279 |
[ID_BEREMIZ, ID_BEREMIZMAINSPLITTER,
|
|
280 |
ID_BEREMIZPLCCONFIG, ID_BEREMIZLOGCONSOLE,
|
|
281 |
ID_BEREMIZINSPECTOR] = [wx.NewId() for _init_ctrls in range(5)]
|
|
282 |
|
|
283 |
[ID_BEREMIZRUNMENUBUILD, ID_BEREMIZRUNMENUSIMULATE,
|
|
284 |
ID_BEREMIZRUNMENURUN, ID_BEREMIZRUNMENUSAVELOG,
|
|
285 |
] = [wx.NewId() for _init_coll_EditMenu_Items in range(4)]
|
|
286 |
|
|
287 |
class Beremiz(IDEFrame):
|
|
288 |
|
|
289 |
def _init_coll_FileMenu_Items(self, parent):
|
|
290 |
AppendMenu(parent, help='', id=wx.ID_NEW,
|
|
291 |
kind=wx.ITEM_NORMAL, text=_(u'New\tCTRL+N'))
|
|
292 |
AppendMenu(parent, help='', id=wx.ID_OPEN,
|
|
293 |
kind=wx.ITEM_NORMAL, text=_(u'Open\tCTRL+O'))
|
|
294 |
AppendMenu(parent, help='', id=wx.ID_SAVE,
|
|
295 |
kind=wx.ITEM_NORMAL, text=_(u'Save\tCTRL+S'))
|
|
296 |
AppendMenu(parent, help='', id=wx.ID_CLOSE,
|
|
297 |
kind=wx.ITEM_NORMAL, text=_(u'Close Tab\tCTRL+W'))
|
|
298 |
AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL,
|
|
299 |
kind=wx.ITEM_NORMAL, text=_(u'Close Project'))
|
|
300 |
parent.AppendSeparator()
|
|
301 |
AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP,
|
|
302 |
kind=wx.ITEM_NORMAL, text=_(u'Page Setup'))
|
|
303 |
AppendMenu(parent, help='', id=wx.ID_PREVIEW,
|
|
304 |
kind=wx.ITEM_NORMAL, text=_(u'Preview'))
|
|
305 |
AppendMenu(parent, help='', id=wx.ID_PRINT,
|
|
306 |
kind=wx.ITEM_NORMAL, text=_(u'Print'))
|
|
307 |
parent.AppendSeparator()
|
|
308 |
AppendMenu(parent, help='', id=wx.ID_PROPERTIES,
|
|
309 |
kind=wx.ITEM_NORMAL, text=_(u'Properties'))
|
|
310 |
parent.AppendSeparator()
|
|
311 |
AppendMenu(parent, help='', id=wx.ID_EXIT,
|
|
312 |
kind=wx.ITEM_NORMAL, text=_(u'Quit\tCTRL+Q'))
|
|
313 |
|
|
314 |
self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW)
|
|
315 |
self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN)
|
|
316 |
self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
|
|
317 |
self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE)
|
|
318 |
self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL)
|
|
319 |
self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP)
|
|
320 |
self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
|
|
321 |
self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
|
|
322 |
self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES)
|
|
323 |
self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)
|
|
324 |
|
|
325 |
def _init_coll_HelpMenu_Items(self, parent):
|
|
326 |
parent.Append(help='', id=wx.ID_HELP,
|
|
327 |
kind=wx.ITEM_NORMAL, text=_(u'Beremiz\tF1'))
|
|
328 |
parent.Append(help='', id=wx.ID_ABOUT,
|
|
329 |
kind=wx.ITEM_NORMAL, text=_(u'About'))
|
|
330 |
self.Bind(wx.EVT_MENU, self.OnBeremizMenu, id=wx.ID_HELP)
|
|
331 |
self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)
|
|
332 |
|
|
333 |
def _init_coll_PLCConfigMainSizer_Items(self, parent):
|
|
334 |
parent.AddSizer(self.PLCParamsSizer, 0, border=10, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
|
|
335 |
parent.AddSizer(self.PluginTreeSizer, 0, border=10, flag=wx.BOTTOM|wx.LEFT|wx.RIGHT)
|
|
336 |
|
|
337 |
def _init_coll_PLCConfigMainSizer_Growables(self, parent):
|
|
338 |
parent.AddGrowableCol(0)
|
|
339 |
parent.AddGrowableRow(1)
|
|
340 |
|
|
341 |
def _init_coll_PluginTreeSizer_Growables(self, parent):
|
|
342 |
parent.AddGrowableCol(0)
|
|
343 |
parent.AddGrowableCol(1)
|
|
344 |
|
|
345 |
def _init_beremiz_sizers(self):
|
|
346 |
self.PLCConfigMainSizer = wx.FlexGridSizer(cols=1, hgap=2, rows=2, vgap=2)
|
|
347 |
self.PLCParamsSizer = wx.BoxSizer(wx.VERTICAL)
|
|
348 |
#self.PluginTreeSizer = wx.FlexGridSizer(cols=3, hgap=0, rows=0, vgap=2)
|
|
349 |
self.PluginTreeSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=0, vgap=2)
|
|
350 |
|
|
351 |
self._init_coll_PLCConfigMainSizer_Items(self.PLCConfigMainSizer)
|
|
352 |
self._init_coll_PLCConfigMainSizer_Growables(self.PLCConfigMainSizer)
|
|
353 |
self._init_coll_PluginTreeSizer_Growables(self.PluginTreeSizer)
|
|
354 |
|
|
355 |
self.PLCConfig.SetSizer(self.PLCConfigMainSizer)
|
|
356 |
|
|
357 |
def _init_ctrls(self, prnt):
|
|
358 |
IDEFrame._init_ctrls(self, prnt)
|
|
359 |
|
|
360 |
self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=ID_BEREMIZINSPECTOR)
|
|
361 |
accel = wx.AcceleratorTable([wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), ID_BEREMIZINSPECTOR)])
|
|
362 |
self.SetAcceleratorTable(accel)
|
|
363 |
|
|
364 |
self.PLCConfig = wx.ScrolledWindow(id=ID_BEREMIZPLCCONFIG,
|
|
365 |
name='PLCConfig', parent=self.LeftNoteBook, pos=wx.Point(0, 0),
|
|
366 |
size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER|wx.HSCROLL|wx.VSCROLL)
|
|
367 |
self.PLCConfig.SetBackgroundColour(wx.WHITE)
|
|
368 |
self.PLCConfig.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown)
|
|
369 |
self.PLCConfig.Bind(wx.EVT_SIZE, self.OnMoveWindow)
|
|
370 |
self.PLCConfig.Bind(wx.EVT_MOUSEWHEEL, self.OnPLCConfigScroll)
|
|
371 |
self.BottomNoteBook.InsertPage(0, self.PLCConfig, _("Topology"), True)
|
|
372 |
|
|
373 |
self.LogConsole = wx.TextCtrl(id=ID_BEREMIZLOGCONSOLE, value='',
|
|
374 |
name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0),
|
|
375 |
size=wx.Size(0, 0), style=wx.TE_MULTILINE|wx.TE_RICH2)
|
|
376 |
self.LogConsole.Bind(wx.EVT_LEFT_DCLICK, self.OnLogConsoleDClick)
|
|
377 |
self.BottomNoteBook.AddPage(self.LogConsole, _("Log Console"))
|
|
378 |
|
|
379 |
self._init_beremiz_sizers()
|
|
380 |
|
|
381 |
def __init__(self, parent, projectOpen=None, buildpath=None, plugin_root=None, debug=True):
|
|
382 |
IDEFrame.__init__(self, parent, debug)
|
|
383 |
self.Config = wx.ConfigBase.Get()
|
|
384 |
|
|
385 |
self.Log = LogPseudoFile(self.LogConsole)
|
|
386 |
|
|
387 |
self.local_runtime = None
|
|
388 |
self.runtime_port = None
|
|
389 |
self.local_runtime_tmpdir = None
|
|
390 |
|
|
391 |
self.DisableEvents = False
|
|
392 |
# Variable allowing disabling of PLCConfig scroll when Popup shown
|
|
393 |
self.ScrollingEnabled = True
|
|
394 |
|
|
395 |
self.PluginInfos = {}
|
|
396 |
|
|
397 |
if projectOpen is not None and os.path.isdir(projectOpen):
|
|
398 |
self.PluginRoot = PluginsRoot(self, self.Log)
|
|
399 |
self.Controler = self.PluginRoot
|
|
400 |
result = self.PluginRoot.LoadProject(projectOpen, buildpath)
|
|
401 |
if not result:
|
|
402 |
self.DebugVariablePanel.SetDataProducer(self.PluginRoot)
|
|
403 |
self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
|
|
404 |
self.RefreshAll()
|
|
405 |
else:
|
|
406 |
self.ResetView()
|
|
407 |
self.ShowErrorMessage(result)
|
|
408 |
else:
|
|
409 |
self.PluginRoot = plugin_root
|
|
410 |
self.Controler = plugin_root
|
|
411 |
if plugin_root is not None:
|
|
412 |
self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
|
|
413 |
self.RefreshAll()
|
|
414 |
|
|
415 |
# Add beremiz's icon in top left corner of the frame
|
|
416 |
self.SetIcon(wx.Icon(Bpath( "images", "brz.ico"), wx.BITMAP_TYPE_ICO))
|
|
417 |
|
|
418 |
self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
|
|
419 |
|
|
420 |
self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
|
|
421 |
|
|
422 |
def RefreshTitle(self):
|
|
423 |
name = _("Beremiz")
|
|
424 |
if self.PluginRoot is not None:
|
|
425 |
projectname = self.PluginRoot.GetProjectName()
|
|
426 |
if self.PluginRoot.PlugTestModified():
|
|
427 |
projectname = "~%s~" % projectname
|
|
428 |
self.SetTitle("%s - %s" % (name, projectname))
|
|
429 |
else:
|
|
430 |
self.SetTitle(name)
|
|
431 |
|
|
432 |
def StartLocalRuntime(self, taskbaricon = True):
|
|
433 |
if self.local_runtime is None or self.local_runtime.finished:
|
|
434 |
# create temporary directory for runtime working directory
|
|
435 |
self.local_runtime_tmpdir = tempfile.mkdtemp()
|
|
436 |
# choose an arbitrary random port for runtime
|
|
437 |
self.runtime_port = int(random.random() * 1000) + 61131
|
|
438 |
# launch local runtime
|
|
439 |
self.local_runtime = ProcessLogger(self.Log,
|
|
440 |
"\"%s\" \"%s\" -p %s -i localhost %s %s"%(sys.executable,
|
|
441 |
Bpath("Beremiz_service.py"),
|
|
442 |
self.runtime_port,
|
|
443 |
{False : "-x 0", True :"-x 1"}[taskbaricon],
|
|
444 |
self.local_runtime_tmpdir),
|
|
445 |
no_gui=False)
|
|
446 |
self.local_runtime.spin(timeout=500, keyword = "working", kill_it = False)
|
|
447 |
return self.runtime_port
|
|
448 |
|
|
449 |
def KillLocalRuntime(self):
|
|
450 |
if self.local_runtime is not None:
|
|
451 |
# shutdown local runtime
|
|
452 |
self.local_runtime.kill(gently=False)
|
|
453 |
# clear temp dir
|
|
454 |
shutil.rmtree(self.local_runtime_tmpdir)
|
|
455 |
|
|
456 |
def OnOpenWidgetInspector(self, evt):
|
|
457 |
# Activate the widget inspection tool
|
|
458 |
from wx.lib.inspection import InspectionTool
|
|
459 |
if not InspectionTool().initialized:
|
|
460 |
InspectionTool().Init()
|
|
461 |
|
|
462 |
# Find a widget to be selected in the tree. Use either the
|
|
463 |
# one under the cursor, if any, or this frame.
|
|
464 |
wnd = wx.FindWindowAtPointer()
|
|
465 |
if not wnd:
|
|
466 |
wnd = self
|
|
467 |
InspectionTool().Show(wnd, True)
|
|
468 |
|
|
469 |
def OnLogConsoleDClick(self, event):
|
|
470 |
wx.CallAfter(self.SearchLineForError)
|
|
471 |
event.Skip()
|
|
472 |
|
|
473 |
def SearchLineForError(self):
|
|
474 |
if self.PluginRoot is not None:
|
|
475 |
text = self.LogConsole.GetRange(0, self.LogConsole.GetInsertionPoint())
|
|
476 |
line = self.LogConsole.GetLineText(len(text.splitlines()) - 1)
|
|
477 |
result = MATIEC_ERROR_MODEL.match(line)
|
|
478 |
if result is not None:
|
|
479 |
first_line, first_column, last_line, last_column, error = result.groups()
|
|
480 |
infos = self.PluginRoot.ShowError(self.Log,
|
|
481 |
(int(first_line), int(first_column)),
|
|
482 |
(int(last_line), int(last_column)))
|
|
483 |
|
|
484 |
def OnCloseFrame(self, event):
|
|
485 |
if self.PluginRoot is not None:
|
|
486 |
if self.PluginRoot.ProjectTestModified():
|
|
487 |
dialog = wx.MessageDialog(self,
|
|
488 |
_("Save changes ?"),
|
|
489 |
_("Close Application"),
|
|
490 |
wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
|
|
491 |
answer = dialog.ShowModal()
|
|
492 |
dialog.Destroy()
|
|
493 |
if answer == wx.ID_YES:
|
|
494 |
self.PluginRoot.SaveProject()
|
|
495 |
event.Skip()
|
|
496 |
elif answer == wx.ID_NO:
|
|
497 |
event.Skip()
|
|
498 |
return
|
|
499 |
else:
|
|
500 |
event.Veto()
|
|
501 |
return
|
|
502 |
|
|
503 |
self.KillLocalRuntime()
|
|
504 |
|
|
505 |
event.Skip()
|
|
506 |
|
|
507 |
def OnMoveWindow(self, event):
|
|
508 |
self.GetBestSize()
|
|
509 |
self.RefreshScrollBars()
|
|
510 |
event.Skip()
|
|
511 |
|
|
512 |
def EnableScrolling(self, enable):
|
|
513 |
self.ScrollingEnabled = enable
|
|
514 |
|
|
515 |
def OnPLCConfigScroll(self, event):
|
|
516 |
if self.ScrollingEnabled:
|
|
517 |
event.Skip()
|
|
518 |
|
|
519 |
def OnPanelLeftDown(self, event):
|
|
520 |
focused = self.FindFocus()
|
|
521 |
if isinstance(focused, TextCtrlAutoComplete.TextCtrlAutoComplete):
|
|
522 |
focused.DismissListBox()
|
|
523 |
event.Skip()
|
|
524 |
|
|
525 |
def RefreshFileMenu(self):
|
|
526 |
if self.PluginRoot is not None:
|
|
527 |
selected = self.TabsOpened.GetSelection()
|
|
528 |
if selected >= 0:
|
|
529 |
graphic_viewer = isinstance(self.TabsOpened.GetPage(selected), Viewer)
|
|
530 |
else:
|
|
531 |
graphic_viewer = False
|
|
532 |
if self.TabsOpened.GetPageCount() > 0:
|
|
533 |
self.FileMenu.Enable(wx.ID_CLOSE, True)
|
|
534 |
if graphic_viewer:
|
|
535 |
self.FileMenu.Enable(wx.ID_PREVIEW, True)
|
|
536 |
self.FileMenu.Enable(wx.ID_PRINT, True)
|
|
537 |
else:
|
|
538 |
self.FileMenu.Enable(wx.ID_PREVIEW, False)
|
|
539 |
self.FileMenu.Enable(wx.ID_PRINT, False)
|
|
540 |
else:
|
|
541 |
self.FileMenu.Enable(wx.ID_CLOSE, False)
|
|
542 |
self.FileMenu.Enable(wx.ID_PREVIEW, False)
|
|
543 |
self.FileMenu.Enable(wx.ID_PRINT, False)
|
|
544 |
self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
|
|
545 |
self.FileMenu.Enable(wx.ID_SAVE, True)
|
|
546 |
self.FileMenu.Enable(wx.ID_PROPERTIES, True)
|
|
547 |
self.FileMenu.Enable(wx.ID_CLOSE_ALL, True)
|
|
548 |
else:
|
|
549 |
self.FileMenu.Enable(wx.ID_CLOSE, False)
|
|
550 |
self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
|
|
551 |
self.FileMenu.Enable(wx.ID_PREVIEW, False)
|
|
552 |
self.FileMenu.Enable(wx.ID_PRINT, False)
|
|
553 |
self.FileMenu.Enable(wx.ID_SAVE, False)
|
|
554 |
self.FileMenu.Enable(wx.ID_PROPERTIES, False)
|
|
555 |
self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)
|
|
556 |
|
|
557 |
def RefreshScrollBars(self):
|
|
558 |
xstart, ystart = self.PLCConfig.GetViewStart()
|
|
559 |
window_size = self.PLCConfig.GetClientSize()
|
|
560 |
sizer = self.PLCConfig.GetSizer()
|
|
561 |
if sizer:
|
|
562 |
maxx, maxy = sizer.GetMinSize()
|
|
563 |
self.PLCConfig.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT,
|
|
564 |
maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT,
|
|
565 |
max(0, min(xstart, (maxx - window_size[0]) / SCROLLBAR_UNIT)),
|
|
566 |
max(0, min(ystart, (maxy - window_size[1]) / SCROLLBAR_UNIT)))
|
|
567 |
|
|
568 |
def RefreshPLCParams(self):
|
|
569 |
self.Freeze()
|
|
570 |
self.ClearSizer(self.PLCParamsSizer)
|
|
571 |
|
|
572 |
if self.PluginRoot is not None:
|
|
573 |
plcwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
|
|
574 |
if self.PluginRoot.PlugTestModified():
|
|
575 |
bkgdclr = CHANGED_TITLE_COLOUR
|
|
576 |
else:
|
|
577 |
bkgdclr = TITLE_COLOUR
|
|
578 |
|
|
579 |
if self.PluginRoot not in self.PluginInfos:
|
|
580 |
self.PluginInfos[self.PluginRoot] = {"right_visible" : False}
|
|
581 |
|
|
582 |
plcwindow.SetBackgroundColour(TITLE_COLOUR)
|
|
583 |
plcwindow.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown)
|
|
584 |
self.PLCParamsSizer.AddWindow(plcwindow, 0, border=0, flag=wx.GROW)
|
|
585 |
|
|
586 |
plcwindowsizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
587 |
plcwindow.SetSizer(plcwindowsizer)
|
|
588 |
|
|
589 |
st = wx.StaticText(plcwindow, -1)
|
|
590 |
st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
|
|
591 |
st.SetLabel(self.PluginRoot.GetProjectName())
|
|
592 |
plcwindowsizer.AddWindow(st, 0, border=5, flag=wx.ALL|wx.ALIGN_CENTER)
|
|
593 |
|
|
594 |
addbutton_id = wx.NewId()
|
|
595 |
addbutton = wx.lib.buttons.GenBitmapButton(id=addbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Add.png')),
|
|
596 |
name='AddPluginButton', parent=plcwindow, pos=wx.Point(0, 0),
|
|
597 |
size=wx.Size(16, 16), style=wx.NO_BORDER)
|
|
598 |
addbutton.SetToolTipString(_("Add a sub plugin"))
|
|
599 |
addbutton.Bind(wx.EVT_BUTTON, self.Gen_AddPluginMenu(self.PluginRoot), id=addbutton_id)
|
|
600 |
plcwindowsizer.AddWindow(addbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER)
|
|
601 |
|
|
602 |
plcwindowmainsizer = wx.BoxSizer(wx.VERTICAL)
|
|
603 |
plcwindowsizer.AddSizer(plcwindowmainsizer, 0, border=5, flag=wx.ALL)
|
|
604 |
|
|
605 |
plcwindowbuttonsizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
606 |
plcwindowmainsizer.AddSizer(plcwindowbuttonsizer, 0, border=0, flag=wx.ALIGN_CENTER)
|
|
607 |
|
|
608 |
msizer = self.GenerateMethodButtonSizer(self.PluginRoot, plcwindow, not self.PluginInfos[self.PluginRoot]["right_visible"])
|
|
609 |
plcwindowbuttonsizer.AddSizer(msizer, 0, border=0, flag=wx.GROW)
|
|
610 |
|
|
611 |
paramswindow = wx.Panel(plcwindow, -1, size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL)
|
|
612 |
paramswindow.SetBackgroundColour(TITLE_COLOUR)
|
|
613 |
paramswindow.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown)
|
|
614 |
plcwindowbuttonsizer.AddWindow(paramswindow, 0, border=0, flag=0)
|
|
615 |
|
|
616 |
psizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
617 |
paramswindow.SetSizer(psizer)
|
|
618 |
|
|
619 |
plugin_infos = self.PluginRoot.GetParamsAttributes()
|
|
620 |
self.RefreshSizerElement(paramswindow, psizer, self.PluginRoot, plugin_infos, None, False)
|
|
621 |
|
|
622 |
if not self.PluginInfos[self.PluginRoot]["right_visible"]:
|
|
623 |
paramswindow.Hide()
|
|
624 |
|
|
625 |
minimizebutton_id = wx.NewId()
|
|
626 |
minimizebutton = wx.lib.buttons.GenBitmapToggleButton(id=minimizebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Maximize.png')),
|
|
627 |
name='MinimizeButton', parent=plcwindow, pos=wx.Point(0, 0),
|
|
628 |
size=wx.Size(24, 24), style=wx.NO_BORDER)
|
|
629 |
make_genbitmaptogglebutton_flat(minimizebutton)
|
|
630 |
minimizebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Minimize.png')))
|
|
631 |
minimizebutton.SetToggle(self.PluginInfos[self.PluginRoot]["right_visible"])
|
|
632 |
plcwindowbuttonsizer.AddWindow(minimizebutton, 0, border=5, flag=wx.ALL)
|
|
633 |
|
|
634 |
def togglewindow(event):
|
|
635 |
if minimizebutton.GetToggle():
|
|
636 |
paramswindow.Show()
|
|
637 |
msizer.SetCols(1)
|
|
638 |
else:
|
|
639 |
paramswindow.Hide()
|
|
640 |
msizer.SetCols(len(self.PluginRoot.PluginMethods))
|
|
641 |
self.PluginInfos[self.PluginRoot]["right_visible"] = minimizebutton.GetToggle()
|
|
642 |
self.PLCConfigMainSizer.Layout()
|
|
643 |
self.RefreshScrollBars()
|
|
644 |
event.Skip()
|
|
645 |
minimizebutton.Bind(wx.EVT_BUTTON, togglewindow, id=minimizebutton_id)
|
|
646 |
|
|
647 |
self.PluginInfos[self.PluginRoot]["main"] = plcwindow
|
|
648 |
self.PluginInfos[self.PluginRoot]["params"] = paramswindow
|
|
649 |
|
|
650 |
self.PLCConfigMainSizer.Layout()
|
|
651 |
self.RefreshScrollBars()
|
|
652 |
self.Thaw()
|
|
653 |
|
|
654 |
def GenerateMethodButtonSizer(self, plugin, parent, horizontal = True):
|
|
655 |
normal_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = faces["helv"])
|
|
656 |
mouseover_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline=True, faceName = faces["helv"])
|
|
657 |
if horizontal:
|
|
658 |
msizer = wx.FlexGridSizer(cols=len(plugin.PluginMethods))
|
|
659 |
else:
|
|
660 |
msizer = wx.FlexGridSizer(cols=1)
|
|
661 |
for plugin_method in plugin.PluginMethods:
|
|
662 |
if "method" in plugin_method and plugin_method.get("shown",True):
|
|
663 |
id = wx.NewId()
|
|
664 |
label = plugin_method["name"]
|
|
665 |
button = GenBitmapTextButton(id=id, parent=parent,
|
|
666 |
bitmap=wx.Bitmap(Bpath( "%s.png"%plugin_method.get("bitmap", os.path.join("images", "Unknown")))), label=label,
|
|
667 |
name=label, pos=wx.DefaultPosition, style=wx.NO_BORDER)
|
|
668 |
button.SetFont(normal_bt_font)
|
|
669 |
button.SetToolTipString(plugin_method["tooltip"])
|
|
670 |
button.Bind(wx.EVT_BUTTON, self.GetButtonCallBackFunction(plugin, plugin_method["method"]), id=id)
|
|
671 |
# a fancy underline on mouseover
|
|
672 |
def setFontStyle(b, s):
|
|
673 |
def fn(event):
|
|
674 |
b.SetFont(s)
|
|
675 |
b.Refresh()
|
|
676 |
event.Skip()
|
|
677 |
return fn
|
|
678 |
button.Bind(wx.EVT_ENTER_WINDOW, setFontStyle(button, mouseover_bt_font))
|
|
679 |
button.Bind(wx.EVT_LEAVE_WINDOW, setFontStyle(button, normal_bt_font))
|
|
680 |
#hack to force size to mini
|
|
681 |
if not plugin_method.get("enabled",True):
|
|
682 |
button.Disable()
|
|
683 |
msizer.AddWindow(button, 0, border=0, flag=wx.ALIGN_CENTER)
|
|
684 |
return msizer
|
|
685 |
|
|
686 |
def RefreshPluginTree(self):
|
|
687 |
self.Freeze()
|
|
688 |
self.ClearSizer(self.PluginTreeSizer)
|
|
689 |
if self.PluginRoot is not None:
|
|
690 |
for child in self.PluginRoot.IECSortedChilds():
|
|
691 |
self.GenerateTreeBranch(child)
|
|
692 |
if not self.PluginInfos[child]["expanded"]:
|
|
693 |
self.CollapsePlugin(child)
|
|
694 |
self.PLCConfigMainSizer.Layout()
|
|
695 |
self.RefreshScrollBars()
|
|
696 |
self.Thaw()
|
|
697 |
|
|
698 |
def SetPluginParamsAttribute(self, plugin, *args, **kwargs):
|
|
699 |
res, StructChanged = plugin.SetParamsAttribute(*args, **kwargs)
|
|
700 |
if StructChanged:
|
|
701 |
wx.CallAfter(self.RefreshPluginTree)
|
|
702 |
else:
|
|
703 |
if plugin == self.PluginRoot:
|
|
704 |
bkgdclr = CHANGED_TITLE_COLOUR
|
|
705 |
items = ["main", "params"]
|
|
706 |
else:
|
|
707 |
bkgdclr = CHANGED_WINDOW_COLOUR
|
|
708 |
items = ["left", "right", "params"]
|
|
709 |
for i in items:
|
|
710 |
self.PluginInfos[plugin][i].SetBackgroundColour(bkgdclr)
|
|
711 |
self.PluginInfos[plugin][i].Refresh()
|
|
712 |
return res
|
|
713 |
|
|
714 |
def ExpandPlugin(self, plugin, force = False):
|
|
715 |
for child in self.PluginInfos[plugin]["children"]:
|
|
716 |
self.PluginInfos[child]["left"].Show()
|
|
717 |
self.PluginInfos[child]["right"].Show()
|
|
718 |
if force or not self.PluginInfos[child]["expanded"]:
|
|
719 |
self.ExpandPlugin(child, force)
|
|
720 |
if force:
|
|
721 |
self.PluginInfos[child]["expanded"] = True
|
|
722 |
locations_infos = self.PluginInfos[plugin].get("locations_infos", None)
|
|
723 |
if locations_infos is not None:
|
|
724 |
if force or locations_infos["root"]["expanded"]:
|
|
725 |
self.ExpandLocation(locations_infos, "root", force)
|
|
726 |
if force:
|
|
727 |
locations_infos["root"]["expanded"] = True
|
|
728 |
|
|
729 |
|
|
730 |
def CollapsePlugin(self, plugin, force = False):
|
|
731 |
for child in self.PluginInfos[plugin]["children"]:
|
|
732 |
self.PluginInfos[child]["left"].Hide()
|
|
733 |
self.PluginInfos[child]["right"].Hide()
|
|
734 |
self.CollapsePlugin(child, force)
|
|
735 |
if force:
|
|
736 |
self.PluginInfos[child]["expanded"] = False
|
|
737 |
locations_infos = self.PluginInfos[plugin].get("locations_infos", None)
|
|
738 |
if locations_infos is not None:
|
|
739 |
self.CollapseLocation(locations_infos, "root", force)
|
|
740 |
if force:
|
|
741 |
locations_infos["root"]["expanded"] = False
|
|
742 |
|
|
743 |
def ExpandLocation(self, locations_infos, group, force = False):
|
|
744 |
for child in locations_infos[group]["children"]:
|
|
745 |
locations_infos[child]["left"].Show()
|
|
746 |
locations_infos[child]["right"].Show()
|
|
747 |
if force or locations_infos[child]["expanded"]:
|
|
748 |
self.ExpandLocation(locations_infos, child, force)
|
|
749 |
if force:
|
|
750 |
locations_infos[child]["expanded"] = True
|
|
751 |
|
|
752 |
def CollapseLocation(self, locations_infos, group, force = False):
|
|
753 |
for child in locations_infos[group]["children"]:
|
|
754 |
locations_infos[child]["left"].Hide()
|
|
755 |
locations_infos[child]["right"].Hide()
|
|
756 |
self.CollapseLocation(locations_infos, child, force)
|
|
757 |
if force:
|
|
758 |
locations_infos[child]["expanded"] = False
|
|
759 |
|
|
760 |
def GenerateTreeBranch(self, plugin):
|
|
761 |
leftwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
|
|
762 |
if plugin.PlugTestModified():
|
|
763 |
bkgdclr=CHANGED_WINDOW_COLOUR
|
|
764 |
else:
|
|
765 |
bkgdclr=WINDOW_COLOUR
|
|
766 |
|
|
767 |
leftwindow.SetBackgroundColour(bkgdclr)
|
|
768 |
|
|
769 |
if not self.PluginInfos.has_key(plugin):
|
|
770 |
self.PluginInfos[plugin] = {"expanded" : False, "right_visible" : False}
|
|
771 |
|
|
772 |
self.PluginInfos[plugin]["children"] = plugin.IECSortedChilds()
|
|
773 |
plugin_locations = []
|
|
774 |
if len(self.PluginInfos[plugin]["children"]) == 0:
|
|
775 |
plugin_locations = plugin.GetVariableLocationTree()["children"]
|
|
776 |
if not self.PluginInfos[plugin].has_key("locations_infos"):
|
|
777 |
self.PluginInfos[plugin]["locations_infos"] = {"root": {"expanded" : False}}
|
|
778 |
|
|
779 |
self.PluginInfos[plugin]["locations_infos"]["root"]["children"] = []
|
|
780 |
|
|
781 |
self.PluginTreeSizer.AddWindow(leftwindow, 0, border=0, flag=wx.GROW)
|
|
782 |
|
|
783 |
leftwindowsizer = wx.FlexGridSizer(cols=1, rows=2)
|
|
784 |
leftwindowsizer.AddGrowableCol(0)
|
|
785 |
leftwindow.SetSizer(leftwindowsizer)
|
|
786 |
|
|
787 |
leftbuttonmainsizer = wx.FlexGridSizer(cols=3, rows=1)
|
|
788 |
leftbuttonmainsizer.AddGrowableCol(0)
|
|
789 |
leftwindowsizer.AddSizer(leftbuttonmainsizer, 0, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT) #|wx.TOP
|
|
790 |
|
|
791 |
leftbuttonsizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
792 |
leftbuttonmainsizer.AddSizer(leftbuttonsizer, 0, border=5, flag=wx.GROW|wx.RIGHT)
|
|
793 |
|
|
794 |
leftsizer = wx.BoxSizer(wx.VERTICAL)
|
|
795 |
leftbuttonsizer.AddSizer(leftsizer, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
|
|
796 |
|
|
797 |
rolesizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
798 |
leftsizer.AddSizer(rolesizer, 0, border=0, flag=wx.GROW|wx.RIGHT)
|
|
799 |
|
|
800 |
enablebutton_id = wx.NewId()
|
|
801 |
enablebutton = wx.lib.buttons.GenBitmapToggleButton(id=enablebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Disabled.png')),
|
|
802 |
name='EnableButton', parent=leftwindow, size=wx.Size(16, 16), pos=wx.Point(0, 0), style=0)#wx.NO_BORDER)
|
|
803 |
enablebutton.SetToolTipString(_("Enable/Disable this plugin"))
|
|
804 |
make_genbitmaptogglebutton_flat(enablebutton)
|
|
805 |
enablebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Enabled.png')))
|
|
806 |
enablebutton.SetToggle(plugin.MandatoryParams[1].getEnabled())
|
|
807 |
def toggleenablebutton(event):
|
|
808 |
res = self.SetPluginParamsAttribute(plugin, "BaseParams.Enabled", enablebutton.GetToggle())
|
|
809 |
enablebutton.SetToggle(res)
|
|
810 |
event.Skip()
|
|
811 |
enablebutton.Bind(wx.EVT_BUTTON, toggleenablebutton, id=enablebutton_id)
|
|
812 |
rolesizer.AddWindow(enablebutton, 0, border=0, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
|
|
813 |
|
|
814 |
roletext = wx.StaticText(leftwindow, -1)
|
|
815 |
roletext.SetLabel(plugin.PlugHelp)
|
|
816 |
rolesizer.AddWindow(roletext, 0, border=5, flag=wx.RIGHT|wx.ALIGN_LEFT)
|
|
817 |
|
|
818 |
plugin_IECChannel = plugin.BaseParams.getIEC_Channel()
|
|
819 |
|
|
820 |
iecsizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
821 |
leftsizer.AddSizer(iecsizer, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
|
|
822 |
|
|
823 |
st = wx.StaticText(leftwindow, -1)
|
|
824 |
st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
|
|
825 |
st.SetLabel(plugin.GetFullIEC_Channel())
|
|
826 |
iecsizer.AddWindow(st, 0, border=0, flag=0)
|
|
827 |
|
|
828 |
updownsizer = wx.BoxSizer(wx.VERTICAL)
|
|
829 |
iecsizer.AddSizer(updownsizer, 0, border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
|
|
830 |
|
|
831 |
if plugin_IECChannel > 0:
|
|
832 |
ieccdownbutton_id = wx.NewId()
|
|
833 |
ieccdownbutton = wx.lib.buttons.GenBitmapButton(id=ieccdownbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'IECCDown.png')),
|
|
834 |
name='IECCDownButton', parent=leftwindow, pos=wx.Point(0, 0),
|
|
835 |
size=wx.Size(16, 16), style=wx.NO_BORDER)
|
|
836 |
ieccdownbutton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(plugin, plugin_IECChannel - 1), id=ieccdownbutton_id)
|
|
837 |
updownsizer.AddWindow(ieccdownbutton, 0, border=0, flag=wx.ALIGN_LEFT)
|
|
838 |
|
|
839 |
ieccupbutton_id = wx.NewId()
|
|
840 |
ieccupbutton = wx.lib.buttons.GenBitmapTextButton(id=ieccupbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'IECCUp.png')),
|
|
841 |
name='IECCUpButton', parent=leftwindow, pos=wx.Point(0, 0),
|
|
842 |
size=wx.Size(16, 16), style=wx.NO_BORDER)
|
|
843 |
ieccupbutton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(plugin, plugin_IECChannel + 1), id=ieccupbutton_id)
|
|
844 |
updownsizer.AddWindow(ieccupbutton, 0, border=0, flag=wx.ALIGN_LEFT)
|
|
845 |
|
|
846 |
adddeletesizer = wx.BoxSizer(wx.VERTICAL)
|
|
847 |
iecsizer.AddSizer(adddeletesizer, 0, border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
|
|
848 |
|
|
849 |
deletebutton_id = wx.NewId()
|
|
850 |
deletebutton = wx.lib.buttons.GenBitmapButton(id=deletebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Delete.png')),
|
|
851 |
name='DeletePluginButton', parent=leftwindow, pos=wx.Point(0, 0),
|
|
852 |
size=wx.Size(16, 16), style=wx.NO_BORDER)
|
|
853 |
deletebutton.SetToolTipString(_("Delete this plugin"))
|
|
854 |
deletebutton.Bind(wx.EVT_BUTTON, self.GetDeleteButtonFunction(plugin), id=deletebutton_id)
|
|
855 |
adddeletesizer.AddWindow(deletebutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER)
|
|
856 |
|
|
857 |
if len(plugin.PlugChildsTypes) > 0:
|
|
858 |
addbutton_id = wx.NewId()
|
|
859 |
addbutton = wx.lib.buttons.GenBitmapButton(id=addbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Add.png')),
|
|
860 |
name='AddPluginButton', parent=leftwindow, pos=wx.Point(0, 0),
|
|
861 |
size=wx.Size(16, 16), style=wx.NO_BORDER)
|
|
862 |
addbutton.SetToolTipString(_("Add a sub plugin"))
|
|
863 |
addbutton.Bind(wx.EVT_BUTTON, self.Gen_AddPluginMenu(plugin), id=addbutton_id)
|
|
864 |
adddeletesizer.AddWindow(addbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER)
|
|
865 |
|
|
866 |
expandbutton_id = wx.NewId()
|
|
867 |
expandbutton = wx.lib.buttons.GenBitmapToggleButton(id=expandbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'plus.png')),
|
|
868 |
name='ExpandButton', parent=leftwindow, pos=wx.Point(0, 0),
|
|
869 |
size=wx.Size(13, 13), style=wx.NO_BORDER)
|
|
870 |
expandbutton.labelDelta = 0
|
|
871 |
expandbutton.SetBezelWidth(0)
|
|
872 |
expandbutton.SetUseFocusIndicator(False)
|
|
873 |
expandbutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'minus.png')))
|
|
874 |
|
|
875 |
if len(self.PluginInfos[plugin]["children"]) > 0:
|
|
876 |
expandbutton.SetToggle(self.PluginInfos[plugin]["expanded"])
|
|
877 |
def togglebutton(event):
|
|
878 |
if expandbutton.GetToggle():
|
|
879 |
self.ExpandPlugin(plugin)
|
|
880 |
else:
|
|
881 |
self.CollapsePlugin(plugin)
|
|
882 |
self.PluginInfos[plugin]["expanded"] = expandbutton.GetToggle()
|
|
883 |
self.PLCConfigMainSizer.Layout()
|
|
884 |
self.RefreshScrollBars()
|
|
885 |
event.Skip()
|
|
886 |
expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id)
|
|
887 |
elif len(plugin_locations) > 0:
|
|
888 |
locations_infos = self.PluginInfos[plugin]["locations_infos"]
|
|
889 |
expandbutton.SetToggle(locations_infos["root"]["expanded"])
|
|
890 |
def togglebutton(event):
|
|
891 |
if expandbutton.GetToggle():
|
|
892 |
self.ExpandLocation(locations_infos, "root")
|
|
893 |
else:
|
|
894 |
self.CollapseLocation(locations_infos, "root")
|
|
895 |
self.PluginInfos[plugin]["expanded"] = expandbutton.GetToggle()
|
|
896 |
locations_infos["root"]["expanded"] = expandbutton.GetToggle()
|
|
897 |
self.PLCConfigMainSizer.Layout()
|
|
898 |
self.RefreshScrollBars()
|
|
899 |
event.Skip()
|
|
900 |
expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id)
|
|
901 |
else:
|
|
902 |
expandbutton.Enable(False)
|
|
903 |
iecsizer.AddWindow(expandbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
|
|
904 |
|
|
905 |
tc_id = wx.NewId()
|
|
906 |
tc = wx.TextCtrl(leftwindow, tc_id, size=wx.Size(150, 25), style=wx.NO_BORDER)
|
|
907 |
tc.SetFont(wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
|
|
908 |
tc.ChangeValue(plugin.MandatoryParams[1].getName())
|
|
909 |
tc.Bind(wx.EVT_TEXT, self.GetTextCtrlCallBackFunction(tc, plugin, "BaseParams.Name"), id=tc_id)
|
|
910 |
iecsizer.AddWindow(tc, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
|
|
911 |
|
|
912 |
rightwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
|
|
913 |
rightwindow.SetBackgroundColour(bkgdclr)
|
|
914 |
|
|
915 |
self.PluginTreeSizer.AddWindow(rightwindow, 0, border=0, flag=wx.GROW)
|
|
916 |
|
|
917 |
rightwindowmainsizer = wx.BoxSizer(wx.VERTICAL)
|
|
918 |
rightwindow.SetSizer(rightwindowmainsizer)
|
|
919 |
|
|
920 |
rightwindowsizer = wx.FlexGridSizer(cols=2, rows=1)
|
|
921 |
rightwindowsizer.AddGrowableCol(1)
|
|
922 |
rightwindowsizer.AddGrowableRow(0)
|
|
923 |
rightwindowmainsizer.AddSizer(rightwindowsizer, 0, border=8, flag=wx.TOP|wx.GROW)
|
|
924 |
|
|
925 |
msizer = self.GenerateMethodButtonSizer(plugin, rightwindow, not self.PluginInfos[plugin]["right_visible"])
|
|
926 |
rightwindowsizer.AddSizer(msizer, 0, border=0, flag=wx.GROW)
|
|
927 |
|
|
928 |
rightparamssizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
929 |
rightwindowsizer.AddSizer(rightparamssizer, 0, border=0, flag=wx.ALIGN_RIGHT)
|
|
930 |
|
|
931 |
paramswindow = wx.Panel(rightwindow, -1, size=wx.Size(-1, -1))
|
|
932 |
paramswindow.SetBackgroundColour(bkgdclr)
|
|
933 |
|
|
934 |
psizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
935 |
paramswindow.SetSizer(psizer)
|
|
936 |
self.PluginInfos[plugin]["params"] = paramswindow
|
|
937 |
|
|
938 |
rightparamssizer.AddWindow(paramswindow, 0, border=5, flag=wx.ALL)
|
|
939 |
|
|
940 |
plugin_infos = plugin.GetParamsAttributes()
|
|
941 |
self.RefreshSizerElement(paramswindow, psizer, plugin, plugin_infos, None, False)
|
|
942 |
|
|
943 |
if not self.PluginInfos[plugin]["right_visible"]:
|
|
944 |
paramswindow.Hide()
|
|
945 |
|
|
946 |
rightminimizebutton_id = wx.NewId()
|
|
947 |
rightminimizebutton = wx.lib.buttons.GenBitmapToggleButton(id=rightminimizebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Maximize.png')),
|
|
948 |
name='MinimizeButton', parent=rightwindow, pos=wx.Point(0, 0),
|
|
949 |
size=wx.Size(24, 24), style=wx.NO_BORDER)
|
|
950 |
make_genbitmaptogglebutton_flat(rightminimizebutton)
|
|
951 |
rightminimizebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Minimize.png')))
|
|
952 |
rightminimizebutton.SetToggle(self.PluginInfos[plugin]["right_visible"])
|
|
953 |
rightparamssizer.AddWindow(rightminimizebutton, 0, border=5, flag=wx.ALL)
|
|
954 |
|
|
955 |
def togglerightwindow(event):
|
|
956 |
if rightminimizebutton.GetToggle():
|
|
957 |
rightparamssizer.Show(0)
|
|
958 |
msizer.SetCols(1)
|
|
959 |
else:
|
|
960 |
rightparamssizer.Hide(0)
|
|
961 |
msizer.SetCols(len(plugin.PluginMethods))
|
|
962 |
self.PluginInfos[plugin]["right_visible"] = rightminimizebutton.GetToggle()
|
|
963 |
self.PLCConfigMainSizer.Layout()
|
|
964 |
self.RefreshScrollBars()
|
|
965 |
event.Skip()
|
|
966 |
rightminimizebutton.Bind(wx.EVT_BUTTON, togglerightwindow, id=rightminimizebutton_id)
|
|
967 |
|
|
968 |
self.PluginInfos[plugin]["left"] = leftwindow
|
|
969 |
self.PluginInfos[plugin]["right"] = rightwindow
|
|
970 |
for child in self.PluginInfos[plugin]["children"]:
|
|
971 |
self.GenerateTreeBranch(child)
|
|
972 |
if not self.PluginInfos[child]["expanded"]:
|
|
973 |
self.CollapsePlugin(child)
|
|
974 |
if len(plugin_locations) > 0:
|
|
975 |
locations_infos = self.PluginInfos[plugin]["locations_infos"]
|
|
976 |
for location in plugin_locations:
|
|
977 |
locations_infos["root"]["children"].append("root.%s" % location["name"])
|
|
978 |
self.GenerateLocationTreeBranch(locations_infos, "root", location)
|
|
979 |
if not locations_infos["root"]["expanded"]:
|
|
980 |
self.CollapseLocation(locations_infos, "root")
|
|
981 |
|
|
982 |
LOCATION_BITMAP = {LOCATION_PLUGIN: "CONFIGURATION",
|
|
983 |
LOCATION_MODULE: "RESOURCE",
|
|
984 |
LOCATION_GROUP: "PROGRAM",
|
|
985 |
LOCATION_VAR_INPUT: "VAR_INPUT",
|
|
986 |
LOCATION_VAR_OUTPUT: "VAR_OUTPUT",
|
|
987 |
LOCATION_VAR_MEMORY: "VAR_LOCAL"}
|
|
988 |
|
|
989 |
def GenerateLocationTreeBranch(self, locations_infos, parent, location):
|
|
990 |
leftwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
|
|
991 |
self.PluginTreeSizer.AddWindow(leftwindow, 0, border=0, flag=wx.GROW)
|
|
992 |
|
|
993 |
leftwindowsizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
994 |
leftwindow.SetSizer(leftwindowsizer)
|
|
995 |
|
|
996 |
rightwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
|
|
997 |
self.PluginTreeSizer.AddWindow(rightwindow, 0, border=0, flag=wx.GROW)
|
|
998 |
|
|
999 |
location_name = "%s.%s" % (parent, location["name"])
|
|
1000 |
if not locations_infos.has_key(location_name):
|
|
1001 |
locations_infos[location_name] = {"expanded" : False}
|
|
1002 |
|
|
1003 |
if location["type"] in [LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP]:
|
|
1004 |
leftwindow.SetBackgroundColour(WINDOW_COLOUR)
|
|
1005 |
rightwindow.SetBackgroundColour(WINDOW_COLOUR)
|
|
1006 |
|
|
1007 |
st = wx.StaticText(leftwindow, -1)
|
|
1008 |
st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
|
|
1009 |
st.SetLabel(location["location"])
|
|
1010 |
leftwindowsizer.AddWindow(st, 0, border=5, flag=wx.RIGHT)
|
|
1011 |
|
|
1012 |
expandbutton_id = wx.NewId()
|
|
1013 |
expandbutton = wx.lib.buttons.GenBitmapToggleButton(id=expandbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'plus.png')),
|
|
1014 |
name='ExpandButton', parent=leftwindow, pos=wx.Point(0, 0),
|
|
1015 |
size=wx.Size(13, 13), style=wx.NO_BORDER)
|
|
1016 |
expandbutton.labelDelta = 0
|
|
1017 |
expandbutton.SetBezelWidth(0)
|
|
1018 |
expandbutton.SetUseFocusIndicator(False)
|
|
1019 |
expandbutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'minus.png')))
|
|
1020 |
expandbutton.SetToggle(locations_infos[location_name]["expanded"])
|
|
1021 |
|
|
1022 |
if len(location["children"]) > 0:
|
|
1023 |
def togglebutton(event):
|
|
1024 |
if expandbutton.GetToggle():
|
|
1025 |
self.ExpandLocation(locations_infos, location_name)
|
|
1026 |
else:
|
|
1027 |
self.CollapseLocation(locations_infos, location_name)
|
|
1028 |
locations_infos[location_name]["expanded"] = expandbutton.GetToggle()
|
|
1029 |
self.PLCConfigMainSizer.Layout()
|
|
1030 |
self.RefreshScrollBars()
|
|
1031 |
event.Skip()
|
|
1032 |
expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id)
|
|
1033 |
else:
|
|
1034 |
expandbutton.Enable(False)
|
|
1035 |
leftwindowsizer.AddWindow(expandbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
|
|
1036 |
|
|
1037 |
else:
|
|
1038 |
leftwindow.SetBackgroundColour(wx.WHITE)
|
|
1039 |
rightwindow.SetBackgroundColour(wx.WHITE)
|
|
1040 |
|
|
1041 |
leftwindowsizer.Add(wx.Size(20, 16), 0)
|
|
1042 |
|
|
1043 |
sb = wx.StaticBitmap(leftwindow, -1)
|
|
1044 |
sb.SetBitmap(wx.Bitmap(os.path.join(base_folder, "plcopeneditor", 'Images', '%s.png' % self.LOCATION_BITMAP[location["type"]])))
|
|
1045 |
leftwindowsizer.AddWindow(sb, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
|
|
1046 |
|
|
1047 |
st_id = wx.NewId()
|
|
1048 |
st = wx.StaticText(leftwindow, st_id, size=wx.DefaultSize, style=wx.NO_BORDER)
|
|
1049 |
label = location["name"]
|
|
1050 |
if location["type"] in [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]:
|
|
1051 |
label += " (%s)" % location["location"]
|
|
1052 |
infos = location.copy()
|
|
1053 |
infos.pop("children")
|
|
1054 |
st.SetFont(wx.Font(faces["size"] * 0.5, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = faces["helv"]))
|
|
1055 |
st.Bind(wx.EVT_LEFT_DOWN, self.GenerateLocationLeftDownFunction(infos))
|
|
1056 |
else:
|
|
1057 |
st.SetFont(wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
|
|
1058 |
st.SetLabel(label)
|
|
1059 |
leftwindowsizer.AddWindow(st, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
|
|
1060 |
|
|
1061 |
locations_infos[location_name]["left"] = leftwindow
|
|
1062 |
locations_infos[location_name]["right"] = rightwindow
|
|
1063 |
locations_infos[location_name]["children"] = []
|
|
1064 |
for child in location["children"]:
|
|
1065 |
child_name = "%s.%s" % (location_name, child["name"])
|
|
1066 |
locations_infos[location_name]["children"].append(child_name)
|
|
1067 |
self.GenerateLocationTreeBranch(locations_infos, location_name, child)
|
|
1068 |
if not locations_infos[location_name]["expanded"]:
|
|
1069 |
self.CollapseLocation(locations_infos, location_name)
|
|
1070 |
|
|
1071 |
def GenerateLocationLeftDownFunction(self, infos):
|
|
1072 |
def OnLocationLeftDownFunction(event):
|
|
1073 |
data = wx.TextDataObject(str((infos["location"], "location", infos["IEC_type"], infos["name"], infos["description"])))
|
|
1074 |
dragSource = wx.DropSource(self)
|
|
1075 |
dragSource.SetData(data)
|
|
1076 |
dragSource.DoDragDrop()
|
|
1077 |
event.Skip()
|
|
1078 |
return OnLocationLeftDownFunction
|
|
1079 |
|
|
1080 |
def RefreshAll(self):
|
|
1081 |
self.RefreshPLCParams()
|
|
1082 |
self.RefreshPluginTree()
|
|
1083 |
|
|
1084 |
def GetItemChannelChangedFunction(self, plugin, value):
|
|
1085 |
def OnPluginTreeItemChannelChanged(event):
|
|
1086 |
res = self.SetPluginParamsAttribute(plugin, "BaseParams.IEC_Channel", value)
|
|
1087 |
event.Skip()
|
|
1088 |
return OnPluginTreeItemChannelChanged
|
|
1089 |
|
|
1090 |
def _GetAddPluginFunction(self, name, plugin):
|
|
1091 |
def OnPluginMenu(event):
|
|
1092 |
wx.CallAfter(self.AddPlugin, name, plugin)
|
|
1093 |
return OnPluginMenu
|
|
1094 |
|
|
1095 |
def Gen_AddPluginMenu(self, plugin):
|
|
1096 |
def AddPluginMenu(event):
|
|
1097 |
main_menu = wx.Menu(title='')
|
|
1098 |
if len(plugin.PlugChildsTypes) > 0:
|
|
1099 |
for name, XSDClass, help in plugin.PlugChildsTypes:
|
|
1100 |
new_id = wx.NewId()
|
|
1101 |
main_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=_("Append ")+help)
|
|
1102 |
self.Bind(wx.EVT_MENU, self._GetAddPluginFunction(name, plugin), id=new_id)
|
|
1103 |
self.PopupMenuXY(main_menu)
|
|
1104 |
return AddPluginMenu
|
|
1105 |
|
|
1106 |
def GetButtonCallBackFunction(self, plugin, method):
|
|
1107 |
""" Generate the callbackfunc for a given plugin method"""
|
|
1108 |
def OnButtonClick(event):
|
|
1109 |
# Disable button to prevent re-entrant call
|
|
1110 |
event.GetEventObject().Disable()
|
|
1111 |
# Call
|
|
1112 |
getattr(plugin,method)()
|
|
1113 |
# Re-enable button
|
|
1114 |
event.GetEventObject().Enable()
|
|
1115 |
# Trigger refresh on Idle
|
|
1116 |
wx.CallAfter(self.RefreshAll)
|
|
1117 |
event.Skip()
|
|
1118 |
return OnButtonClick
|
|
1119 |
|
|
1120 |
def GetChoiceCallBackFunction(self, choicectrl, plugin, path):
|
|
1121 |
def OnChoiceChanged(event):
|
|
1122 |
res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection())
|
|
1123 |
choicectrl.SetStringSelection(res)
|
|
1124 |
event.Skip()
|
|
1125 |
return OnChoiceChanged
|
|
1126 |
|
|
1127 |
def GetChoiceContentCallBackFunction(self, choicectrl, staticboxsizer, plugin, path):
|
|
1128 |
def OnChoiceContentChanged(event):
|
|
1129 |
res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection())
|
|
1130 |
if wx.VERSION < (2, 8, 0):
|
|
1131 |
self.ParamsPanel.Freeze()
|
|
1132 |
choicectrl.SetStringSelection(res)
|
|
1133 |
infos = self.PluginRoot.GetParamsAttributes(path)
|
|
1134 |
staticbox = staticboxsizer.GetStaticBox()
|
|
1135 |
staticbox.SetLabel("%(name)s - %(value)s"%infos)
|
|
1136 |
self.RefreshSizerElement(self.ParamsPanel, staticboxsizer, infos["children"], "%s.%s"%(path, infos["name"]), selected=selected)
|
|
1137 |
self.ParamsPanelMainSizer.Layout()
|
|
1138 |
self.ParamsPanel.Thaw()
|
|
1139 |
self.ParamsPanel.Refresh()
|
|
1140 |
else:
|
|
1141 |
wx.CallAfter(self.RefreshAll)
|
|
1142 |
event.Skip()
|
|
1143 |
return OnChoiceContentChanged
|
|
1144 |
|
|
1145 |
def GetTextCtrlCallBackFunction(self, textctrl, plugin, path):
|
|
1146 |
def OnTextCtrlChanged(event):
|
|
1147 |
res = self.SetPluginParamsAttribute(plugin, path, textctrl.GetValue())
|
|
1148 |
if res != textctrl.GetValue():
|
|
1149 |
textctrl.ChangeValue(res)
|
|
1150 |
event.Skip()
|
|
1151 |
return OnTextCtrlChanged
|
|
1152 |
|
|
1153 |
def GetCheckBoxCallBackFunction(self, chkbx, plugin, path):
|
|
1154 |
def OnCheckBoxChanged(event):
|
|
1155 |
res = self.SetPluginParamsAttribute(plugin, path, chkbx.IsChecked())
|
|
1156 |
chkbx.SetValue(res)
|
|
1157 |
event.Skip()
|
|
1158 |
return OnCheckBoxChanged
|
|
1159 |
|
|
1160 |
def ClearSizer(self, sizer):
|
|
1161 |
staticboxes = []
|
|
1162 |
for item in sizer.GetChildren():
|
|
1163 |
if item.IsSizer():
|
|
1164 |
item_sizer = item.GetSizer()
|
|
1165 |
self.ClearSizer(item_sizer)
|
|
1166 |
if isinstance(item_sizer, wx.StaticBoxSizer):
|
|
1167 |
staticboxes.append(item_sizer.GetStaticBox())
|
|
1168 |
sizer.Clear(True)
|
|
1169 |
for staticbox in staticboxes:
|
|
1170 |
staticbox.Destroy()
|
|
1171 |
|
|
1172 |
def RefreshSizerElement(self, parent, sizer, plugin, elements, path, clean = True):
|
|
1173 |
if clean:
|
|
1174 |
if wx.VERSION < (2, 8, 0):
|
|
1175 |
self.ClearSizer(sizer)
|
|
1176 |
else:
|
|
1177 |
sizer.Clear(True)
|
|
1178 |
first = True
|
|
1179 |
for element_infos in elements:
|
|
1180 |
if path:
|
|
1181 |
element_path = "%s.%s"%(path, element_infos["name"])
|
|
1182 |
else:
|
|
1183 |
element_path = element_infos["name"]
|
|
1184 |
if element_infos["type"] == "element":
|
|
1185 |
label = element_infos["name"]
|
|
1186 |
staticbox = wx.StaticBox(id=-1, label=_(label),
|
|
1187 |
name='%s_staticbox'%element_infos["name"], parent=parent,
|
|
1188 |
pos=wx.Point(0, 0), size=wx.Size(10, 0), style=0)
|
|
1189 |
staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL)
|
|
1190 |
if first:
|
|
1191 |
sizer.AddSizer(staticboxsizer, 0, border=0, flag=wx.GROW|wx.TOP)
|
|
1192 |
else:
|
|
1193 |
sizer.AddSizer(staticboxsizer, 0, border=0, flag=wx.GROW)
|
|
1194 |
self.RefreshSizerElement(parent, staticboxsizer, plugin, element_infos["children"], element_path)
|
|
1195 |
else:
|
|
1196 |
boxsizer = wx.FlexGridSizer(cols=3, rows=1)
|
|
1197 |
boxsizer.AddGrowableCol(1)
|
|
1198 |
if first:
|
|
1199 |
sizer.AddSizer(boxsizer, 0, border=5, flag=wx.GROW|wx.ALL)
|
|
1200 |
else:
|
|
1201 |
sizer.AddSizer(boxsizer, 0, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM)
|
|
1202 |
staticbitmap = GenStaticBitmap(ID=-1, bitmapname="%s.png"%element_infos["name"],
|
|
1203 |
name="%s_bitmap"%element_infos["name"], parent=parent,
|
|
1204 |
pos=wx.Point(0, 0), size=wx.Size(24, 24), style=0)
|
|
1205 |
boxsizer.AddWindow(staticbitmap, 0, border=5, flag=wx.RIGHT)
|
|
1206 |
label = element_infos["name"]
|
|
1207 |
statictext = wx.StaticText(id=-1, label="%s:"%_(label),
|
|
1208 |
name="%s_label"%element_infos["name"], parent=parent,
|
|
1209 |
pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
|
|
1210 |
boxsizer.AddWindow(statictext, 0, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT)
|
|
1211 |
id = wx.NewId()
|
|
1212 |
if isinstance(element_infos["type"], types.ListType):
|
|
1213 |
combobox = wx.ComboBox(id=id, name=element_infos["name"], parent=parent,
|
|
1214 |
pos=wx.Point(0, 0), size=wx.Size(300, 28), style=wx.CB_READONLY)
|
|
1215 |
boxsizer.AddWindow(combobox, 0, border=0, flag=0)
|
|
1216 |
if element_infos["use"] == "optional":
|
|
1217 |
combobox.Append("")
|
|
1218 |
if len(element_infos["type"]) > 0 and isinstance(element_infos["type"][0], types.TupleType):
|
|
1219 |
for choice, xsdclass in element_infos["type"]:
|
|
1220 |
combobox.Append(choice)
|
|
1221 |
name = element_infos["name"]
|
|
1222 |
value = element_infos["value"]
|
|
1223 |
staticbox = wx.StaticBox(id=-1, label="%s - %s"%(_(name), _(value)),
|
|
1224 |
name='%s_staticbox'%element_infos["name"], parent=parent,
|
|
1225 |
pos=wx.Point(0, 0), size=wx.Size(10, 0), style=0)
|
|
1226 |
staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL)
|
|
1227 |
sizer.AddSizer(staticboxsizer, 0, border=5, flag=wx.GROW|wx.BOTTOM)
|
|
1228 |
self.RefreshSizerElement(parent, staticboxsizer, plugin, element_infos["children"], element_path)
|
|
1229 |
callback = self.GetChoiceContentCallBackFunction(combobox, staticboxsizer, plugin, element_path)
|
|
1230 |
else:
|
|
1231 |
for choice in element_infos["type"]:
|
|
1232 |
combobox.Append(choice)
|
|
1233 |
callback = self.GetChoiceCallBackFunction(combobox, plugin, element_path)
|
|
1234 |
if element_infos["value"] is None:
|
|
1235 |
combobox.SetStringSelection("")
|
|
1236 |
else:
|
|
1237 |
combobox.SetStringSelection(element_infos["value"])
|
|
1238 |
combobox.Bind(wx.EVT_COMBOBOX, callback, id=id)
|
|
1239 |
elif isinstance(element_infos["type"], types.DictType):
|
|
1240 |
scmin = -(2**31)
|
|
1241 |
scmax = 2**31-1
|
|
1242 |
if "min" in element_infos["type"]:
|
|
1243 |
scmin = element_infos["type"]["min"]
|
|
1244 |
if "max" in element_infos["type"]:
|
|
1245 |
scmax = element_infos["type"]["max"]
|
|
1246 |
spinctrl = wx.SpinCtrl(id=id, name=element_infos["name"], parent=parent,
|
|
1247 |
pos=wx.Point(0, 0), size=wx.Size(300, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT)
|
|
1248 |
spinctrl.SetRange(scmin,scmax)
|
|
1249 |
boxsizer.AddWindow(spinctrl, 0, border=0, flag=0)
|
|
1250 |
spinctrl.SetValue(element_infos["value"])
|
|
1251 |
spinctrl.Bind(wx.EVT_SPINCTRL, self.GetTextCtrlCallBackFunction(spinctrl, plugin, element_path), id=id)
|
|
1252 |
else:
|
|
1253 |
if element_infos["type"] == "boolean":
|
|
1254 |
checkbox = wx.CheckBox(id=id, name=element_infos["name"], parent=parent,
|
|
1255 |
pos=wx.Point(0, 0), size=wx.Size(17, 25), style=0)
|
|
1256 |
boxsizer.AddWindow(checkbox, 0, border=0, flag=0)
|
|
1257 |
checkbox.SetValue(element_infos["value"])
|
|
1258 |
checkbox.Bind(wx.EVT_CHECKBOX, self.GetCheckBoxCallBackFunction(checkbox, plugin, element_path), id=id)
|
|
1259 |
elif element_infos["type"] in ["unsignedLong", "long","integer"]:
|
|
1260 |
if element_infos["type"].startswith("unsigned"):
|
|
1261 |
scmin = 0
|
|
1262 |
else:
|
|
1263 |
scmin = -(2**31)
|
|
1264 |
scmax = 2**31-1
|
|
1265 |
spinctrl = wx.SpinCtrl(id=id, name=element_infos["name"], parent=parent,
|
|
1266 |
pos=wx.Point(0, 0), size=wx.Size(300, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT)
|
|
1267 |
spinctrl.SetRange(scmin, scmax)
|
|
1268 |
boxsizer.AddWindow(spinctrl, 0, border=0, flag=0)
|
|
1269 |
spinctrl.SetValue(element_infos["value"])
|
|
1270 |
spinctrl.Bind(wx.EVT_SPINCTRL, self.GetTextCtrlCallBackFunction(spinctrl, plugin, element_path), id=id)
|
|
1271 |
else:
|
|
1272 |
choices = cPickle.loads(str(self.Config.Read(element_path, cPickle.dumps([""]))))
|
|
1273 |
textctrl = TextCtrlAutoComplete.TextCtrlAutoComplete(id=id,
|
|
1274 |
name=element_infos["name"],
|
|
1275 |
parent=parent,
|
|
1276 |
appframe=self,
|
|
1277 |
choices=choices,
|
|
1278 |
element_path=element_path,
|
|
1279 |
pos=wx.Point(0, 0),
|
|
1280 |
size=wx.Size(300, 25),
|
|
1281 |
style=0)
|
|
1282 |
|
|
1283 |
boxsizer.AddWindow(textctrl, 0, border=0, flag=0)
|
|
1284 |
textctrl.ChangeValue(str(element_infos["value"]))
|
|
1285 |
textctrl.Bind(wx.EVT_TEXT, self.GetTextCtrlCallBackFunction(textctrl, plugin, element_path))
|
|
1286 |
first = False
|
|
1287 |
|
|
1288 |
def ResetView(self):
|
|
1289 |
IDEFrame.ResetView(self)
|
|
1290 |
self.PluginInfos = {}
|
|
1291 |
if self.PluginRoot is not None:
|
|
1292 |
self.PluginRoot.CloseProject()
|
|
1293 |
self.PluginRoot = None
|
|
1294 |
self.Log.flush()
|
|
1295 |
self.DebugVariablePanel.SetDataProducer(None)
|
|
1296 |
|
|
1297 |
def OnNewProjectMenu(self, event):
|
|
1298 |
if not self.Config.HasEntry("lastopenedfolder"):
|
|
1299 |
defaultpath = os.path.expanduser("~")
|
|
1300 |
else:
|
|
1301 |
defaultpath = self.Config.Read("lastopenedfolder")
|
|
1302 |
|
|
1303 |
dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, wx.DD_NEW_DIR_BUTTON)
|
|
1304 |
if dialog.ShowModal() == wx.ID_OK:
|
|
1305 |
projectpath = dialog.GetPath()
|
|
1306 |
dialog.Destroy()
|
|
1307 |
self.Config.Write("lastopenedfolder", os.path.dirname(projectpath))
|
|
1308 |
self.Config.Flush()
|
|
1309 |
self.ResetView()
|
|
1310 |
self.PluginRoot = PluginsRoot(self, self.Log)
|
|
1311 |
self.Controler = self.PluginRoot
|
|
1312 |
result = self.PluginRoot.NewProject(projectpath)
|
|
1313 |
if not result:
|
|
1314 |
self.DebugVariablePanel.SetDataProducer(self.PluginRoot)
|
|
1315 |
self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
|
|
1316 |
self.RefreshAll()
|
|
1317 |
else:
|
|
1318 |
self.ResetView()
|
|
1319 |
self.ShowErrorMessage(result)
|
|
1320 |
self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
|
|
1321 |
|
|
1322 |
def OnOpenProjectMenu(self, event):
|
|
1323 |
if not self.Config.HasEntry("lastopenedfolder"):
|
|
1324 |
defaultpath = os.path.expanduser("~")
|
|
1325 |
else:
|
|
1326 |
defaultpath = self.Config.Read("lastopenedfolder")
|
|
1327 |
|
|
1328 |
dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, wx.DD_NEW_DIR_BUTTON)
|
|
1329 |
if dialog.ShowModal() == wx.ID_OK:
|
|
1330 |
projectpath = dialog.GetPath()
|
|
1331 |
if os.path.isdir(projectpath):
|
|
1332 |
self.Config.Write("lastopenedfolder", os.path.dirname(projectpath))
|
|
1333 |
self.Config.Flush()
|
|
1334 |
self.ResetView()
|
|
1335 |
self.PluginRoot = PluginsRoot(self, self.Log)
|
|
1336 |
self.Controler = self.PluginRoot
|
|
1337 |
result = self.PluginRoot.LoadProject(projectpath)
|
|
1338 |
if not result:
|
|
1339 |
self.DebugVariablePanel.SetDataProducer(self.PluginRoot)
|
|
1340 |
self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
|
|
1341 |
self.RefreshAll()
|
|
1342 |
else:
|
|
1343 |
self.ResetView()
|
|
1344 |
self.ShowErrorMessage(result)
|
|
1345 |
else:
|
|
1346 |
self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath)
|
|
1347 |
self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
|
|
1348 |
dialog.Destroy()
|
|
1349 |
|
|
1350 |
def OnCloseProjectMenu(self, event):
|
|
1351 |
if self.PluginRoot is not None:
|
|
1352 |
if self.PluginRoot.ProjectTestModified():
|
|
1353 |
dialog = wx.MessageDialog(self,
|
|
1354 |
_("Save changes ?"),
|
|
1355 |
_("Close Application"),
|
|
1356 |
wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
|
|
1357 |
answer = dialog.ShowModal()
|
|
1358 |
dialog.Destroy()
|
|
1359 |
if answer == wx.ID_YES:
|
|
1360 |
self.PluginRoot.SaveProject()
|
|
1361 |
elif answer == wx.ID_CANCEL:
|
|
1362 |
return
|
|
1363 |
self.ResetView()
|
|
1364 |
self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
|
|
1365 |
self.RefreshAll()
|
|
1366 |
|
|
1367 |
def OnSaveProjectMenu(self, event):
|
|
1368 |
if self.PluginRoot is not None:
|
|
1369 |
self.PluginRoot.SaveProject()
|
|
1370 |
self.RefreshAll()
|
|
1371 |
self.RefreshTitle()
|
|
1372 |
|
|
1373 |
def OnPropertiesMenu(self, event):
|
|
1374 |
self.ShowProperties()
|
|
1375 |
|
|
1376 |
def OnQuitMenu(self, event):
|
|
1377 |
self.Close()
|
|
1378 |
|
|
1379 |
def OnBeremizMenu(self, event):
|
|
1380 |
open_pdf(Bpath( "doc", "manual_beremiz.pdf"))
|
|
1381 |
|
|
1382 |
def OnAboutMenu(self, event):
|
|
1383 |
OpenHtmlFrame(self,_("About Beremiz"), Bpath("doc","about.html"), wx.Size(550, 500))
|
|
1384 |
|
|
1385 |
def GetAddButtonFunction(self, plugin, window):
|
|
1386 |
def AddButtonFunction(event):
|
|
1387 |
if plugin and len(plugin.PlugChildsTypes) > 0:
|
|
1388 |
plugin_menu = wx.Menu(title='')
|
|
1389 |
for name, XSDClass, help in plugin.PlugChildsTypes:
|
|
1390 |
new_id = wx.NewId()
|
|
1391 |
plugin_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=name)
|
|
1392 |
self.Bind(wx.EVT_MENU, self._GetAddPluginFunction(name, plugin), id=new_id)
|
|
1393 |
window_pos = window.GetPosition()
|
|
1394 |
wx.CallAfter(self.PLCConfig.PopupMenu, plugin_menu)
|
|
1395 |
event.Skip()
|
|
1396 |
return AddButtonFunction
|
|
1397 |
|
|
1398 |
def GetDeleteButtonFunction(self, plugin):
|
|
1399 |
def DeleteButtonFunction(event):
|
|
1400 |
wx.CallAfter(self.DeletePlugin, plugin)
|
|
1401 |
event.Skip()
|
|
1402 |
return DeleteButtonFunction
|
|
1403 |
|
|
1404 |
def AddPlugin(self, PluginType, plugin):
|
|
1405 |
dialog = wx.TextEntryDialog(self, _("Please enter a name for plugin:"), _("Add Plugin"), "", wx.OK|wx.CANCEL)
|
|
1406 |
if dialog.ShowModal() == wx.ID_OK:
|
|
1407 |
PluginName = dialog.GetValue()
|
|
1408 |
plugin.PlugAddChild(PluginName, PluginType)
|
|
1409 |
self.RefreshPluginTree()
|
|
1410 |
self.PluginRoot.RefreshPluginsBlockLists()
|
|
1411 |
dialog.Destroy()
|
|
1412 |
|
|
1413 |
def DeletePlugin(self, plugin):
|
|
1414 |
dialog = wx.MessageDialog(self, _("Really delete plugin ?"), _("Remove plugin"), wx.YES_NO|wx.NO_DEFAULT)
|
|
1415 |
if dialog.ShowModal() == wx.ID_YES:
|
|
1416 |
self.PluginInfos.pop(plugin)
|
|
1417 |
plugin.PlugRemove()
|
|
1418 |
del plugin
|
|
1419 |
self.PluginRoot.RefreshPluginsBlockLists()
|
|
1420 |
self.RefreshPluginTree()
|
|
1421 |
dialog.Destroy()
|
|
1422 |
|
|
1423 |
#-------------------------------------------------------------------------------
|
|
1424 |
# Exception Handler
|
|
1425 |
#-------------------------------------------------------------------------------
|
|
1426 |
|
|
1427 |
Max_Traceback_List_Size = 20
|
|
1428 |
|
|
1429 |
def Display_Exception_Dialog(e_type, e_value, e_tb, bug_report_path):
|
|
1430 |
trcbck_lst = []
|
|
1431 |
for i,line in enumerate(traceback.extract_tb(e_tb)):
|
|
1432 |
trcbck = " " + str(i+1) + _(". ")
|
|
1433 |
if line[0].find(os.getcwd()) == -1:
|
|
1434 |
trcbck += _("file : ") + str(line[0]) + _(", ")
|
|
1435 |
else:
|
|
1436 |
trcbck += _("file : ") + str(line[0][len(os.getcwd()):]) + _(", ")
|
|
1437 |
trcbck += _("line : ") + str(line[1]) + _(", ") + _("function : ") + str(line[2])
|
|
1438 |
trcbck_lst.append(trcbck)
|
|
1439 |
|
|
1440 |
# Allow clicking....
|
|
1441 |
cap = wx.Window_GetCapture()
|
|
1442 |
if cap:
|
|
1443 |
cap.ReleaseMouse()
|
|
1444 |
|
|
1445 |
dlg = wx.SingleChoiceDialog(None,
|
|
1446 |
_("""
|
|
1447 |
An unhandled exception (bug) occured. Bug report saved at :
|
|
1448 |
(%s)
|
|
1449 |
|
|
1450 |
Please contact LOLITech at:
|
|
1451 |
+33 (0)3 29 57 60 42
|
|
1452 |
or please be kind enough to send this file to:
|
|
1453 |
bugs_beremiz@lolitech.fr
|
|
1454 |
|
|
1455 |
You should now restart Beremiz.
|
|
1456 |
|
|
1457 |
Traceback:
|
|
1458 |
""") % bug_report_path +
|
|
1459 |
str(e_type) + " : " + str(e_value),
|
|
1460 |
_("Error"),
|
|
1461 |
trcbck_lst)
|
|
1462 |
try:
|
|
1463 |
res = (dlg.ShowModal() == wx.ID_OK)
|
|
1464 |
finally:
|
|
1465 |
dlg.Destroy()
|
|
1466 |
|
|
1467 |
return res
|
|
1468 |
|
|
1469 |
def Display_Error_Dialog(e_value):
|
|
1470 |
message = wxMessageDialog(None, str(e_value), _("Error"), wxOK|wxICON_ERROR)
|
|
1471 |
message.ShowModal()
|
|
1472 |
message.Destroy()
|
|
1473 |
|
|
1474 |
def get_last_traceback(tb):
|
|
1475 |
while tb.tb_next:
|
|
1476 |
tb = tb.tb_next
|
|
1477 |
return tb
|
|
1478 |
|
|
1479 |
|
|
1480 |
def format_namespace(d, indent=' '):
|
|
1481 |
return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()])
|
|
1482 |
|
|
1483 |
|
|
1484 |
ignored_exceptions = [] # a problem with a line in a module is only reported once per session
|
|
1485 |
|
|
1486 |
def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]):
|
|
1487 |
|
|
1488 |
def handle_exception(e_type, e_value, e_traceback):
|
|
1489 |
traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func
|
|
1490 |
last_tb = get_last_traceback(e_traceback)
|
|
1491 |
ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno)
|
|
1492 |
if str(e_value).startswith("!!!"):
|
|
1493 |
Display_Error_Dialog(e_value)
|
|
1494 |
elif ex not in ignored_exceptions:
|
|
1495 |
date = time.ctime()
|
|
1496 |
bug_report_path = path+os.sep+"bug_report_"+date.replace(':','-').replace(' ','_')+".txt"
|
|
1497 |
result = Display_Exception_Dialog(e_type,e_value,e_traceback,bug_report_path)
|
|
1498 |
if result:
|
|
1499 |
ignored_exceptions.append(ex)
|
|
1500 |
info = {
|
|
1501 |
'app-title' : wx.GetApp().GetAppName(), # app_title
|
|
1502 |
'app-version' : app_version,
|
|
1503 |
'wx-version' : wx.VERSION_STRING,
|
|
1504 |
'wx-platform' : wx.Platform,
|
|
1505 |
'python-version' : platform.python_version(), #sys.version.split()[0],
|
|
1506 |
'platform' : platform.platform(),
|
|
1507 |
'e-type' : e_type,
|
|
1508 |
'e-value' : e_value,
|
|
1509 |
'date' : date,
|
|
1510 |
'cwd' : os.getcwd(),
|
|
1511 |
}
|
|
1512 |
if e_traceback:
|
|
1513 |
info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value)
|
|
1514 |
last_tb = get_last_traceback(e_traceback)
|
|
1515 |
exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred
|
|
1516 |
info['locals'] = format_namespace(exception_locals)
|
|
1517 |
if 'self' in exception_locals:
|
|
1518 |
info['self'] = format_namespace(exception_locals['self'].__dict__)
|
|
1519 |
|
|
1520 |
output = open(bug_report_path,'w')
|
|
1521 |
lst = info.keys()
|
|
1522 |
lst.sort()
|
|
1523 |
for a in lst:
|
|
1524 |
output.write(a+":\n"+str(info[a])+"\n\n")
|
|
1525 |
|
|
1526 |
#sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args)
|
|
1527 |
sys.excepthook = handle_exception
|
|
1528 |
|
|
1529 |
if __name__ == '__main__':
|
|
1530 |
# Install a exception handle for bug reports
|
|
1531 |
AddExceptHook(os.getcwd(),__version__)
|
|
1532 |
|
|
1533 |
frame = Beremiz(None, projectOpen, buildpath)
|
|
1534 |
frame.Show()
|
|
1535 |
splash.Close()
|
|
1536 |
app.MainLoop()
|