# HG changeset patch # User laurent # Date 1256316324 -7200 # Node ID ea09f33ce7171f4867b2ad65e45175a6a855dc8c # Parent 7ac746c07ff2bb045f45fcb34f8cbb3fea72a9fa Update internationalization for new functionalities. diff -r 7ac746c07ff2 -r ea09f33ce717 Beremiz.py --- a/Beremiz.py Fri Oct 23 15:41:48 2009 +0200 +++ b/Beremiz.py Fri Oct 23 18:45:24 2009 +0200 @@ -1,1550 +1,1550 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -#This file is part of Beremiz, a Integrated Development Environment for -#programming IEC 61131-3 automates supporting plcopen standard and CanFestival. -# -#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD -# -#See COPYING file for copyrights details. -# -#This library is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public -#License as published by the Free Software Foundation; either -#version 2.1 of the License, or (at your option) any later version. -# -#This library is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -#General Public License for more details. -# -#You should have received a copy of the GNU General Public -#License along with this library; if not, write to the Free Software -#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -__version__ = "$Revision$" - -import os, sys, getopt, wx -import tempfile -import shutil -import random - -CWD = os.path.split(os.path.realpath(__file__))[0] - -def Bpath(*args): - return os.path.join(CWD,*args) - -if __name__ == '__main__': - def usage(): - print "\nUsage of Beremiz.py :" - print "\n %s [Projectpath] [Buildpath]\n"%sys.argv[0] - - try: - opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) - except getopt.GetoptError: - # print help information and exit: - usage() - sys.exit(2) - - for o, a in opts: - if o in ("-h", "--help"): - usage() - sys.exit() - - if len(args) > 2: - usage() - sys.exit() - elif len(args) == 1: - projectOpen = args[0] - buildpath = None - elif len(args) == 2: - projectOpen = args[0] - buildpath = args[1] - else: - projectOpen = None - buildpath = None - - app = wx.PySimpleApp() - app.SetAppName('beremiz') - wx.InitAllImageHandlers() - - bmp = wx.Image(Bpath("images","splash.png")).ConvertToBitmap() - splash=wx.SplashScreen(bmp,wx.SPLASH_CENTRE_ON_SCREEN, 1000, None) - wx.Yield() - -# Import module for internationalization -import gettext -import __builtin__ - -# Get folder containing translation files -localedir = os.path.join(CWD,"locale") -# Get the default language -langid = wx.LANGUAGE_DEFAULT -# Define translation domain (name of translation files) -domain = "Beremiz" - -# Define locale for wx -loc = __builtin__.__dict__.get('loc', None) -if loc is None: - loc = wx.Locale(langid) - __builtin__.__dict__['loc'] = loc -# Define location for searching translation files -loc.AddCatalogLookupPathPrefix(localedir) -# Define locale domain -loc.AddCatalog(domain) - -def unicode_translation(message): - return wx.GetTranslation(message).encode("utf-8") - -if __name__ == '__main__': - __builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation - -#Quick hack to be able to find Beremiz IEC tools. Should be config params. -base_folder = os.path.split(sys.path[0])[0] -sys.path.append(base_folder) -sys.path.append(os.path.join(base_folder, "plcopeneditor")) -sys.path.append(os.path.join(base_folder, "docutils")) - -import wx.lib.buttons, wx.lib.statbmp -import TextCtrlAutoComplete, cPickle -import types, time, re, platform, time, traceback, commands -from plugger import PluginsRoot, MATIEC_ERROR_MODEL -from wxPopen import ProcessLogger - -from docutils import * -from PLCOpenEditor import IDEFrame, Viewer, AppendMenu, TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, TYPESTREE, INSTANCESTREE, LIBRARYTREE, SCALING -from PLCControler import LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY - -SCROLLBAR_UNIT = 10 -WINDOW_COLOUR = wx.Colour(240,240,240) -TITLE_COLOUR = wx.Colour(200,200,220) -CHANGED_TITLE_COLOUR = wx.Colour(220,200,220) -CHANGED_WINDOW_COLOUR = wx.Colour(255,240,240) - -if wx.Platform == '__WXMSW__': - faces = { 'times': 'Times New Roman', - 'mono' : 'Courier New', - 'helv' : 'Arial', - 'other': 'Comic Sans MS', - 'size' : 16, - } -else: - faces = { 'times': 'Times', - 'mono' : 'Courier', - 'helv' : 'Helvetica', - 'other': 'new century schoolbook', - 'size' : 18, - } - -# Some helpers to tweak GenBitmapTextButtons -# TODO: declare customized classes instead. -gen_mini_GetBackgroundBrush = lambda obj:lambda dc: wx.Brush(obj.GetParent().GetBackgroundColour(), wx.SOLID) -gen_textbutton_GetLabelSize = lambda obj:lambda:(wx.lib.buttons.GenButton._GetLabelSize(obj)[:-1] + (False,)) - -def make_genbitmaptogglebutton_flat(button): - button.GetBackgroundBrush = gen_mini_GetBackgroundBrush(button) - button.labelDelta = 0 - button.SetBezelWidth(0) - button.SetUseFocusIndicator(False) - -# Patch wx.lib.imageutils so that gray is supported on alpha images -import wx.lib.imageutils -from wx.lib.imageutils import grayOut as old_grayOut -def grayOut(anImage): - if anImage.HasAlpha(): - AlphaData = anImage.GetAlphaData() - else : - AlphaData = None - - old_grayOut(anImage) - - if AlphaData is not None: - anImage.SetAlphaData(AlphaData) - -wx.lib.imageutils.grayOut = grayOut - -class GenBitmapTextButton(wx.lib.buttons.GenBitmapTextButton): - def _GetLabelSize(self): - """ used internally """ - w, h = self.GetTextExtent(self.GetLabel()) - if not self.bmpLabel: - return w, h, False # if there isn't a bitmap use the size of the text - - w_bmp = self.bmpLabel.GetWidth()+2 - h_bmp = self.bmpLabel.GetHeight()+2 - height = h + h_bmp - if w_bmp > w: - width = w_bmp - else: - width = w - return width, height, False - - def DrawLabel(self, dc, width, height, dw=0, dy=0): - bmp = self.bmpLabel - if bmp != None: # if the bitmap is used - if self.bmpDisabled and not self.IsEnabled(): - bmp = self.bmpDisabled - if self.bmpFocus and self.hasFocus: - bmp = self.bmpFocus - if self.bmpSelected and not self.up: - bmp = self.bmpSelected - bw,bh = bmp.GetWidth(), bmp.GetHeight() - if not self.up: - dw = dy = self.labelDelta - hasMask = bmp.GetMask() != None - else: - bw = bh = 0 # no bitmap -> size is zero - - dc.SetFont(self.GetFont()) - if self.IsEnabled(): - dc.SetTextForeground(self.GetForegroundColour()) - else: - dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) - - label = self.GetLabel() - tw, th = dc.GetTextExtent(label) # size of text - if not self.up: - dw = dy = self.labelDelta - - pos_x = (width-bw)/2+dw # adjust for bitmap and text to centre - pos_y = (height-bh-th)/2+dy - if bmp !=None: - dc.DrawBitmap(bmp, pos_x, pos_y, hasMask) # draw bitmap if available - pos_x = (width-tw)/2+dw # adjust for bitmap and text to centre - pos_y += bh + 2 - - dc.DrawText(label, pos_x, pos_y) # draw the text - - -class GenStaticBitmap(wx.lib.statbmp.GenStaticBitmap): - """ Customized GenStaticBitmap, fix transparency redraw bug on wx2.8/win32, - and accept image name as __init__ parameter, fail silently if file do not exist""" - def __init__(self, parent, ID, bitmapname, - pos = wx.DefaultPosition, size = wx.DefaultSize, - style = 0, - name = "genstatbmp"): - - bitmappath = Bpath( "images", bitmapname) - if os.path.isfile(bitmappath): - bitmap = wx.Bitmap(bitmappath) - else: - bitmap = None - wx.lib.statbmp.GenStaticBitmap.__init__(self, parent, ID, bitmap, - pos, size, - style, - name) - - def OnPaint(self, event): - dc = wx.PaintDC(self) - colour = self.GetParent().GetBackgroundColour() - dc.SetPen(wx.Pen(colour)) - dc.SetBrush(wx.Brush(colour )) - dc.DrawRectangle(0, 0, *dc.GetSizeTuple()) - if self._bitmap: - dc.DrawBitmap(self._bitmap, 0, 0, True) - - -class LogPseudoFile: - """ Base class for file like objects to facilitate StdOut for the Shell.""" - def __init__(self, output): - self.red_white = wx.TextAttr("RED", "WHITE") - self.red_yellow = wx.TextAttr("RED", "YELLOW") - self.black_white = wx.TextAttr("BLACK", "WHITE") - self.default_style = None - self.output = output - - def write(self, s, style = None): - if style is None : style=self.black_white - self.output.Freeze(); - if self.default_style != style: - self.output.SetDefaultStyle(style) - self.default_style = style - self.output.AppendText(s) - self.output.ScrollLines(s.count('\n')+1) - self.output.ShowPosition(self.output.GetLastPosition()) - self.output.Thaw() - - def write_warning(self, s): - self.write(s,self.red_white) - - def write_error(self, s): - self.write(s,self.red_yellow) - - def flush(self): - self.output.SetValue("") - - def isatty(self): - return false - -[ID_BEREMIZ, ID_BEREMIZMAINSPLITTER, - ID_BEREMIZPLCCONFIG, ID_BEREMIZLOGCONSOLE, - ID_BEREMIZINSPECTOR] = [wx.NewId() for _init_ctrls in range(5)] - -[ID_BEREMIZRUNMENUBUILD, ID_BEREMIZRUNMENUSIMULATE, - ID_BEREMIZRUNMENURUN, ID_BEREMIZRUNMENUSAVELOG, -] = [wx.NewId() for _init_coll_EditMenu_Items in range(4)] - -class Beremiz(IDEFrame): - - def _init_coll_FileMenu_Items(self, parent): - AppendMenu(parent, help='', id=wx.ID_NEW, - kind=wx.ITEM_NORMAL, text=_(u'New\tCTRL+N')) - AppendMenu(parent, help='', id=wx.ID_OPEN, - kind=wx.ITEM_NORMAL, text=_(u'Open\tCTRL+O')) - AppendMenu(parent, help='', id=wx.ID_SAVE, - kind=wx.ITEM_NORMAL, text=_(u'Save\tCTRL+S')) - AppendMenu(parent, help='', id=wx.ID_SAVEAS, - kind=wx.ITEM_NORMAL, text=_(u'Save as\tCTRL+S')) - AppendMenu(parent, help='', id=wx.ID_CLOSE, - kind=wx.ITEM_NORMAL, text=_(u'Close Tab\tCTRL+W')) - AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL, - kind=wx.ITEM_NORMAL, text=_(u'Close Project')) - parent.AppendSeparator() - AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP, - kind=wx.ITEM_NORMAL, text=_(u'Page Setup')) - AppendMenu(parent, help='', id=wx.ID_PREVIEW, - kind=wx.ITEM_NORMAL, text=_(u'Preview')) - AppendMenu(parent, help='', id=wx.ID_PRINT, - kind=wx.ITEM_NORMAL, text=_(u'Print')) - parent.AppendSeparator() - AppendMenu(parent, help='', id=wx.ID_PROPERTIES, - kind=wx.ITEM_NORMAL, text=_(u'Properties')) - parent.AppendSeparator() - AppendMenu(parent, help='', id=wx.ID_EXIT, - kind=wx.ITEM_NORMAL, text=_(u'Quit\tCTRL+Q')) - - self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW) - self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN) - self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE) - self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS) - self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE) - self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL) - self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP) - self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW) - self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT) - self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES) - self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT) - - def _init_coll_HelpMenu_Items(self, parent): - parent.Append(help='', id=wx.ID_HELP, - kind=wx.ITEM_NORMAL, text=_(u'Beremiz\tF1')) - parent.Append(help='', id=wx.ID_ABOUT, - kind=wx.ITEM_NORMAL, text=_(u'About')) - self.Bind(wx.EVT_MENU, self.OnBeremizMenu, id=wx.ID_HELP) - self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT) - - def _init_coll_PLCConfigMainSizer_Items(self, parent): - parent.AddSizer(self.PLCParamsSizer, 0, border=10, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - parent.AddSizer(self.PluginTreeSizer, 0, border=10, flag=wx.BOTTOM|wx.LEFT|wx.RIGHT) - - def _init_coll_PLCConfigMainSizer_Growables(self, parent): - parent.AddGrowableCol(0) - parent.AddGrowableRow(1) - - def _init_coll_PluginTreeSizer_Growables(self, parent): - parent.AddGrowableCol(0) - parent.AddGrowableCol(1) - - def _init_beremiz_sizers(self): - self.PLCConfigMainSizer = wx.FlexGridSizer(cols=1, hgap=2, rows=2, vgap=2) - self.PLCParamsSizer = wx.BoxSizer(wx.VERTICAL) - #self.PluginTreeSizer = wx.FlexGridSizer(cols=3, hgap=0, rows=0, vgap=2) - self.PluginTreeSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=0, vgap=2) - - self._init_coll_PLCConfigMainSizer_Items(self.PLCConfigMainSizer) - self._init_coll_PLCConfigMainSizer_Growables(self.PLCConfigMainSizer) - self._init_coll_PluginTreeSizer_Growables(self.PluginTreeSizer) - - self.PLCConfig.SetSizer(self.PLCConfigMainSizer) - - def _init_ctrls(self, prnt): - IDEFrame._init_ctrls(self, prnt) - - self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=ID_BEREMIZINSPECTOR) - accel = wx.AcceleratorTable([wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), ID_BEREMIZINSPECTOR)]) - self.SetAcceleratorTable(accel) - - self.PLCConfig = wx.ScrolledWindow(id=ID_BEREMIZPLCCONFIG, - name='PLCConfig', parent=self.LeftNoteBook, pos=wx.Point(0, 0), - size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER|wx.HSCROLL|wx.VSCROLL) - self.PLCConfig.SetBackgroundColour(wx.WHITE) - self.PLCConfig.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown) - self.PLCConfig.Bind(wx.EVT_SIZE, self.OnMoveWindow) - self.PLCConfig.Bind(wx.EVT_MOUSEWHEEL, self.OnPLCConfigScroll) - self.BottomNoteBook.InsertPage(0, self.PLCConfig, _("Topology"), True) - - self.LogConsole = wx.TextCtrl(id=ID_BEREMIZLOGCONSOLE, value='', - name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0), - size=wx.Size(0, 0), style=wx.TE_MULTILINE|wx.TE_RICH2) - self.LogConsole.Bind(wx.EVT_LEFT_DCLICK, self.OnLogConsoleDClick) - self.BottomNoteBook.AddPage(self.LogConsole, _("Log Console")) - - self._init_beremiz_sizers() - - def __init__(self, parent, projectOpen=None, buildpath=None, plugin_root=None, debug=True): - IDEFrame.__init__(self, parent, debug) - self.Config = wx.ConfigBase.Get() - - self.Log = LogPseudoFile(self.LogConsole) - - self.local_runtime = None - self.runtime_port = None - self.local_runtime_tmpdir = None - - self.DisableEvents = False - # Variable allowing disabling of PLCConfig scroll when Popup shown - self.ScrollingEnabled = True - - self.PluginInfos = {} - - if projectOpen is not None and os.path.isdir(projectOpen): - self.PluginRoot = PluginsRoot(self, self.Log) - self.Controler = self.PluginRoot - result = self.PluginRoot.LoadProject(projectOpen, buildpath) - if not result: - self.DebugVariablePanel.SetDataProducer(self.PluginRoot) - self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE) - self.RefreshAll() - else: - self.ResetView() - self.ShowErrorMessage(result) - else: - self.PluginRoot = plugin_root - self.Controler = plugin_root - if plugin_root is not None: - self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE) - self.RefreshAll() - - # Add beremiz's icon in top left corner of the frame - self.SetIcon(wx.Icon(Bpath( "images", "brz.ico"), wx.BITMAP_TYPE_ICO)) - - self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) - - self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) - - def RefreshTitle(self): - name = _("Beremiz") - if self.PluginRoot is not None: - projectname = self.PluginRoot.GetProjectName() - if self.PluginRoot.PlugTestModified(): - projectname = "~%s~" % projectname - self.SetTitle("%s - %s" % (name, projectname)) - else: - self.SetTitle(name) - - def StartLocalRuntime(self, taskbaricon = True): - if self.local_runtime is None or self.local_runtime.finished: - # create temporary directory for runtime working directory - self.local_runtime_tmpdir = tempfile.mkdtemp() - # choose an arbitrary random port for runtime - self.runtime_port = int(random.random() * 1000) + 61131 - # launch local runtime - self.local_runtime = ProcessLogger(self.Log, - "\"%s\" \"%s\" -p %s -i localhost %s %s"%(sys.executable, - Bpath("Beremiz_service.py"), - self.runtime_port, - {False : "-x 0", True :"-x 1"}[taskbaricon], - self.local_runtime_tmpdir), - no_gui=False) - self.local_runtime.spin(timeout=500, keyword = "working", kill_it = False) - return self.runtime_port - - def KillLocalRuntime(self): - if self.local_runtime is not None: - # shutdown local runtime - self.local_runtime.kill(gently=False) - # clear temp dir - shutil.rmtree(self.local_runtime_tmpdir) - - def OnOpenWidgetInspector(self, evt): - # Activate the widget inspection tool - from wx.lib.inspection import InspectionTool - if not InspectionTool().initialized: - InspectionTool().Init() - - # Find a widget to be selected in the tree. Use either the - # one under the cursor, if any, or this frame. - wnd = wx.FindWindowAtPointer() - if not wnd: - wnd = self - InspectionTool().Show(wnd, True) - - def OnLogConsoleDClick(self, event): - wx.CallAfter(self.SearchLineForError) - event.Skip() - - def SearchLineForError(self): - if self.PluginRoot is not None: - text = self.LogConsole.GetRange(0, self.LogConsole.GetInsertionPoint()) - line = self.LogConsole.GetLineText(len(text.splitlines()) - 1) - result = MATIEC_ERROR_MODEL.match(line) - if result is not None: - first_line, first_column, last_line, last_column, error = result.groups() - infos = self.PluginRoot.ShowError(self.Log, - (int(first_line), int(first_column)), - (int(last_line), int(last_column))) - - def OnCloseFrame(self, event): - if self.PluginRoot is not None: - if self.PluginRoot.ProjectTestModified(): - dialog = wx.MessageDialog(self, - _("Save changes ?"), - _("Close Application"), - wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION) - answer = dialog.ShowModal() - dialog.Destroy() - if answer == wx.ID_YES: - self.PluginRoot.SaveProject() - event.Skip() - elif answer == wx.ID_NO: - event.Skip() - return - else: - event.Veto() - return - - self.KillLocalRuntime() - - event.Skip() - - def OnMoveWindow(self, event): - self.GetBestSize() - self.RefreshScrollBars() - event.Skip() - - def EnableScrolling(self, enable): - self.ScrollingEnabled = enable - - def OnPLCConfigScroll(self, event): - if self.ScrollingEnabled: - event.Skip() - - def OnPanelLeftDown(self, event): - focused = self.FindFocus() - if isinstance(focused, TextCtrlAutoComplete.TextCtrlAutoComplete): - focused.DismissListBox() - event.Skip() - - def RefreshFileMenu(self): - if self.PluginRoot is not None: - selected = self.TabsOpened.GetSelection() - if selected >= 0: - graphic_viewer = isinstance(self.TabsOpened.GetPage(selected), Viewer) - else: - graphic_viewer = False - if self.TabsOpened.GetPageCount() > 0: - self.FileMenu.Enable(wx.ID_CLOSE, True) - if graphic_viewer: - self.FileMenu.Enable(wx.ID_PREVIEW, True) - self.FileMenu.Enable(wx.ID_PRINT, True) - else: - self.FileMenu.Enable(wx.ID_PREVIEW, False) - self.FileMenu.Enable(wx.ID_PRINT, False) - else: - self.FileMenu.Enable(wx.ID_CLOSE, False) - self.FileMenu.Enable(wx.ID_PREVIEW, False) - self.FileMenu.Enable(wx.ID_PRINT, False) - self.FileMenu.Enable(wx.ID_PAGE_SETUP, True) - self.FileMenu.Enable(wx.ID_SAVE, True) - self.FileMenu.Enable(wx.ID_SAVEAS, True) - self.FileMenu.Enable(wx.ID_PROPERTIES, True) - self.FileMenu.Enable(wx.ID_CLOSE_ALL, True) - else: - self.FileMenu.Enable(wx.ID_CLOSE, False) - self.FileMenu.Enable(wx.ID_PAGE_SETUP, False) - self.FileMenu.Enable(wx.ID_PREVIEW, False) - self.FileMenu.Enable(wx.ID_PRINT, False) - self.FileMenu.Enable(wx.ID_SAVE, False) - self.FileMenu.Enable(wx.ID_SAVEAS, False) - self.FileMenu.Enable(wx.ID_PROPERTIES, False) - self.FileMenu.Enable(wx.ID_CLOSE_ALL, False) - - def RefreshScrollBars(self): - xstart, ystart = self.PLCConfig.GetViewStart() - window_size = self.PLCConfig.GetClientSize() - sizer = self.PLCConfig.GetSizer() - if sizer: - maxx, maxy = sizer.GetMinSize() - self.PLCConfig.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, - maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT, - max(0, min(xstart, (maxx - window_size[0]) / SCROLLBAR_UNIT)), - max(0, min(ystart, (maxy - window_size[1]) / SCROLLBAR_UNIT))) - - def RefreshPLCParams(self): - self.Freeze() - self.ClearSizer(self.PLCParamsSizer) - - if self.PluginRoot is not None: - plcwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1)) - if self.PluginRoot.PlugTestModified(): - bkgdclr = CHANGED_TITLE_COLOUR - else: - bkgdclr = TITLE_COLOUR - - if self.PluginRoot not in self.PluginInfos: - self.PluginInfos[self.PluginRoot] = {"right_visible" : False} - - plcwindow.SetBackgroundColour(TITLE_COLOUR) - plcwindow.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown) - self.PLCParamsSizer.AddWindow(plcwindow, 0, border=0, flag=wx.GROW) - - plcwindowsizer = wx.BoxSizer(wx.HORIZONTAL) - plcwindow.SetSizer(plcwindowsizer) - - st = wx.StaticText(plcwindow, -1) - st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"])) - st.SetLabel(self.PluginRoot.GetProjectName()) - plcwindowsizer.AddWindow(st, 0, border=5, flag=wx.ALL|wx.ALIGN_CENTER) - - addbutton_id = wx.NewId() - addbutton = wx.lib.buttons.GenBitmapButton(id=addbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Add.png')), - name='AddPluginButton', parent=plcwindow, pos=wx.Point(0, 0), - size=wx.Size(16, 16), style=wx.NO_BORDER) - addbutton.SetToolTipString(_("Add a sub plugin")) - addbutton.Bind(wx.EVT_BUTTON, self.Gen_AddPluginMenu(self.PluginRoot), id=addbutton_id) - plcwindowsizer.AddWindow(addbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER) - - plcwindowmainsizer = wx.BoxSizer(wx.VERTICAL) - plcwindowsizer.AddSizer(plcwindowmainsizer, 0, border=5, flag=wx.ALL) - - plcwindowbuttonsizer = wx.BoxSizer(wx.HORIZONTAL) - plcwindowmainsizer.AddSizer(plcwindowbuttonsizer, 0, border=0, flag=wx.ALIGN_CENTER) - - msizer = self.GenerateMethodButtonSizer(self.PluginRoot, plcwindow, not self.PluginInfos[self.PluginRoot]["right_visible"]) - plcwindowbuttonsizer.AddSizer(msizer, 0, border=0, flag=wx.GROW) - - paramswindow = wx.Panel(plcwindow, -1, size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL) - paramswindow.SetBackgroundColour(TITLE_COLOUR) - paramswindow.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown) - plcwindowbuttonsizer.AddWindow(paramswindow, 0, border=0, flag=0) - - psizer = wx.BoxSizer(wx.HORIZONTAL) - paramswindow.SetSizer(psizer) - - plugin_infos = self.PluginRoot.GetParamsAttributes() - self.RefreshSizerElement(paramswindow, psizer, self.PluginRoot, plugin_infos, None, False) - - if not self.PluginInfos[self.PluginRoot]["right_visible"]: - paramswindow.Hide() - - minimizebutton_id = wx.NewId() - minimizebutton = wx.lib.buttons.GenBitmapToggleButton(id=minimizebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Maximize.png')), - name='MinimizeButton', parent=plcwindow, pos=wx.Point(0, 0), - size=wx.Size(24, 24), style=wx.NO_BORDER) - make_genbitmaptogglebutton_flat(minimizebutton) - minimizebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Minimize.png'))) - minimizebutton.SetToggle(self.PluginInfos[self.PluginRoot]["right_visible"]) - plcwindowbuttonsizer.AddWindow(minimizebutton, 0, border=5, flag=wx.ALL) - - def togglewindow(event): - if minimizebutton.GetToggle(): - paramswindow.Show() - msizer.SetCols(1) - else: - paramswindow.Hide() - msizer.SetCols(len(self.PluginRoot.PluginMethods)) - self.PluginInfos[self.PluginRoot]["right_visible"] = minimizebutton.GetToggle() - self.PLCConfigMainSizer.Layout() - self.RefreshScrollBars() - event.Skip() - minimizebutton.Bind(wx.EVT_BUTTON, togglewindow, id=minimizebutton_id) - - self.PluginInfos[self.PluginRoot]["main"] = plcwindow - self.PluginInfos[self.PluginRoot]["params"] = paramswindow - - self.PLCConfigMainSizer.Layout() - self.RefreshScrollBars() - self.Thaw() - - def GenerateMethodButtonSizer(self, plugin, parent, horizontal = True): - normal_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = faces["helv"]) - mouseover_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline=True, faceName = faces["helv"]) - if horizontal: - msizer = wx.FlexGridSizer(cols=len(plugin.PluginMethods)) - else: - msizer = wx.FlexGridSizer(cols=1) - for plugin_method in plugin.PluginMethods: - if "method" in plugin_method and plugin_method.get("shown",True): - id = wx.NewId() - label = plugin_method["name"] - button = GenBitmapTextButton(id=id, parent=parent, - bitmap=wx.Bitmap(Bpath( "%s.png"%plugin_method.get("bitmap", os.path.join("images", "Unknown")))), label=label, - name=label, pos=wx.DefaultPosition, style=wx.NO_BORDER) - button.SetFont(normal_bt_font) - button.SetToolTipString(plugin_method["tooltip"]) - button.Bind(wx.EVT_BUTTON, self.GetButtonCallBackFunction(plugin, plugin_method["method"]), id=id) - # a fancy underline on mouseover - def setFontStyle(b, s): - def fn(event): - b.SetFont(s) - b.Refresh() - event.Skip() - return fn - button.Bind(wx.EVT_ENTER_WINDOW, setFontStyle(button, mouseover_bt_font)) - button.Bind(wx.EVT_LEAVE_WINDOW, setFontStyle(button, normal_bt_font)) - #hack to force size to mini - if not plugin_method.get("enabled",True): - button.Disable() - msizer.AddWindow(button, 0, border=0, flag=wx.ALIGN_CENTER) - return msizer - - def RefreshPluginTree(self): - self.Freeze() - self.ClearSizer(self.PluginTreeSizer) - if self.PluginRoot is not None: - for child in self.PluginRoot.IECSortedChilds(): - self.GenerateTreeBranch(child) - if not self.PluginInfos[child]["expanded"]: - self.CollapsePlugin(child) - self.PLCConfigMainSizer.Layout() - self.RefreshScrollBars() - self.Thaw() - - def SetPluginParamsAttribute(self, plugin, *args, **kwargs): - res, StructChanged = plugin.SetParamsAttribute(*args, **kwargs) - if StructChanged: - wx.CallAfter(self.RefreshPluginTree) - else: - if plugin == self.PluginRoot: - bkgdclr = CHANGED_TITLE_COLOUR - items = ["main", "params"] - else: - bkgdclr = CHANGED_WINDOW_COLOUR - items = ["left", "right", "params"] - for i in items: - self.PluginInfos[plugin][i].SetBackgroundColour(bkgdclr) - self.PluginInfos[plugin][i].Refresh() - return res - - def ExpandPlugin(self, plugin, force = False): - for child in self.PluginInfos[plugin]["children"]: - self.PluginInfos[child]["left"].Show() - self.PluginInfos[child]["right"].Show() - if force or not self.PluginInfos[child]["expanded"]: - self.ExpandPlugin(child, force) - if force: - self.PluginInfos[child]["expanded"] = True - locations_infos = self.PluginInfos[plugin].get("locations_infos", None) - if locations_infos is not None: - if force or locations_infos["root"]["expanded"]: - self.ExpandLocation(locations_infos, "root", force) - if force: - locations_infos["root"]["expanded"] = True - - - def CollapsePlugin(self, plugin, force = False): - for child in self.PluginInfos[plugin]["children"]: - self.PluginInfos[child]["left"].Hide() - self.PluginInfos[child]["right"].Hide() - self.CollapsePlugin(child, force) - if force: - self.PluginInfos[child]["expanded"] = False - locations_infos = self.PluginInfos[plugin].get("locations_infos", None) - if locations_infos is not None: - self.CollapseLocation(locations_infos, "root", force) - if force: - locations_infos["root"]["expanded"] = False - - def ExpandLocation(self, locations_infos, group, force = False): - for child in locations_infos[group]["children"]: - locations_infos[child]["left"].Show() - locations_infos[child]["right"].Show() - if force or locations_infos[child]["expanded"]: - self.ExpandLocation(locations_infos, child, force) - if force: - locations_infos[child]["expanded"] = True - - def CollapseLocation(self, locations_infos, group, force = False): - for child in locations_infos[group]["children"]: - locations_infos[child]["left"].Hide() - locations_infos[child]["right"].Hide() - self.CollapseLocation(locations_infos, child, force) - if force: - locations_infos[child]["expanded"] = False - - def GenerateTreeBranch(self, plugin): - leftwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1)) - if plugin.PlugTestModified(): - bkgdclr=CHANGED_WINDOW_COLOUR - else: - bkgdclr=WINDOW_COLOUR - - leftwindow.SetBackgroundColour(bkgdclr) - - if not self.PluginInfos.has_key(plugin): - self.PluginInfos[plugin] = {"expanded" : False, "right_visible" : False} - - self.PluginInfos[plugin]["children"] = plugin.IECSortedChilds() - plugin_locations = [] - if len(self.PluginInfos[plugin]["children"]) == 0: - plugin_locations = plugin.GetVariableLocationTree()["children"] - if not self.PluginInfos[plugin].has_key("locations_infos"): - self.PluginInfos[plugin]["locations_infos"] = {"root": {"expanded" : False}} - - self.PluginInfos[plugin]["locations_infos"]["root"]["children"] = [] - - self.PluginTreeSizer.AddWindow(leftwindow, 0, border=0, flag=wx.GROW) - - leftwindowsizer = wx.FlexGridSizer(cols=1, rows=2) - leftwindowsizer.AddGrowableCol(0) - leftwindow.SetSizer(leftwindowsizer) - - leftbuttonmainsizer = wx.FlexGridSizer(cols=3, rows=1) - leftbuttonmainsizer.AddGrowableCol(0) - leftwindowsizer.AddSizer(leftbuttonmainsizer, 0, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT) #|wx.TOP - - leftbuttonsizer = wx.BoxSizer(wx.HORIZONTAL) - leftbuttonmainsizer.AddSizer(leftbuttonsizer, 0, border=5, flag=wx.GROW|wx.RIGHT) - - leftsizer = wx.BoxSizer(wx.VERTICAL) - leftbuttonsizer.AddSizer(leftsizer, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) - - rolesizer = wx.BoxSizer(wx.HORIZONTAL) - leftsizer.AddSizer(rolesizer, 0, border=0, flag=wx.GROW|wx.RIGHT) - - enablebutton_id = wx.NewId() - enablebutton = wx.lib.buttons.GenBitmapToggleButton(id=enablebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Disabled.png')), - name='EnableButton', parent=leftwindow, size=wx.Size(16, 16), pos=wx.Point(0, 0), style=0)#wx.NO_BORDER) - enablebutton.SetToolTipString(_("Enable/Disable this plugin")) - make_genbitmaptogglebutton_flat(enablebutton) - enablebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Enabled.png'))) - enablebutton.SetToggle(plugin.MandatoryParams[1].getEnabled()) - def toggleenablebutton(event): - res = self.SetPluginParamsAttribute(plugin, "BaseParams.Enabled", enablebutton.GetToggle()) - enablebutton.SetToggle(res) - event.Skip() - enablebutton.Bind(wx.EVT_BUTTON, toggleenablebutton, id=enablebutton_id) - rolesizer.AddWindow(enablebutton, 0, border=0, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) - - roletext = wx.StaticText(leftwindow, -1) - roletext.SetLabel(plugin.PlugHelp) - rolesizer.AddWindow(roletext, 0, border=5, flag=wx.RIGHT|wx.ALIGN_LEFT) - - plugin_IECChannel = plugin.BaseParams.getIEC_Channel() - - iecsizer = wx.BoxSizer(wx.HORIZONTAL) - leftsizer.AddSizer(iecsizer, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) - - st = wx.StaticText(leftwindow, -1) - st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"])) - st.SetLabel(plugin.GetFullIEC_Channel()) - iecsizer.AddWindow(st, 0, border=0, flag=0) - - updownsizer = wx.BoxSizer(wx.VERTICAL) - iecsizer.AddSizer(updownsizer, 0, border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) - - if plugin_IECChannel > 0: - ieccdownbutton_id = wx.NewId() - ieccdownbutton = wx.lib.buttons.GenBitmapButton(id=ieccdownbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'IECCDown.png')), - name='IECCDownButton', parent=leftwindow, pos=wx.Point(0, 0), - size=wx.Size(16, 16), style=wx.NO_BORDER) - ieccdownbutton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(plugin, plugin_IECChannel - 1), id=ieccdownbutton_id) - updownsizer.AddWindow(ieccdownbutton, 0, border=0, flag=wx.ALIGN_LEFT) - - ieccupbutton_id = wx.NewId() - ieccupbutton = wx.lib.buttons.GenBitmapTextButton(id=ieccupbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'IECCUp.png')), - name='IECCUpButton', parent=leftwindow, pos=wx.Point(0, 0), - size=wx.Size(16, 16), style=wx.NO_BORDER) - ieccupbutton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(plugin, plugin_IECChannel + 1), id=ieccupbutton_id) - updownsizer.AddWindow(ieccupbutton, 0, border=0, flag=wx.ALIGN_LEFT) - - adddeletesizer = wx.BoxSizer(wx.VERTICAL) - iecsizer.AddSizer(adddeletesizer, 0, border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) - - deletebutton_id = wx.NewId() - deletebutton = wx.lib.buttons.GenBitmapButton(id=deletebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Delete.png')), - name='DeletePluginButton', parent=leftwindow, pos=wx.Point(0, 0), - size=wx.Size(16, 16), style=wx.NO_BORDER) - deletebutton.SetToolTipString(_("Delete this plugin")) - deletebutton.Bind(wx.EVT_BUTTON, self.GetDeleteButtonFunction(plugin), id=deletebutton_id) - adddeletesizer.AddWindow(deletebutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER) - - if len(plugin.PlugChildsTypes) > 0: - addbutton_id = wx.NewId() - addbutton = wx.lib.buttons.GenBitmapButton(id=addbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Add.png')), - name='AddPluginButton', parent=leftwindow, pos=wx.Point(0, 0), - size=wx.Size(16, 16), style=wx.NO_BORDER) - addbutton.SetToolTipString(_("Add a sub plugin")) - addbutton.Bind(wx.EVT_BUTTON, self.Gen_AddPluginMenu(plugin), id=addbutton_id) - adddeletesizer.AddWindow(addbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER) - - expandbutton_id = wx.NewId() - expandbutton = wx.lib.buttons.GenBitmapToggleButton(id=expandbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'plus.png')), - name='ExpandButton', parent=leftwindow, pos=wx.Point(0, 0), - size=wx.Size(13, 13), style=wx.NO_BORDER) - expandbutton.labelDelta = 0 - expandbutton.SetBezelWidth(0) - expandbutton.SetUseFocusIndicator(False) - expandbutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'minus.png'))) - - if len(self.PluginInfos[plugin]["children"]) > 0: - expandbutton.SetToggle(self.PluginInfos[plugin]["expanded"]) - def togglebutton(event): - if expandbutton.GetToggle(): - self.ExpandPlugin(plugin) - else: - self.CollapsePlugin(plugin) - self.PluginInfos[plugin]["expanded"] = expandbutton.GetToggle() - self.PLCConfigMainSizer.Layout() - self.RefreshScrollBars() - event.Skip() - expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id) - elif len(plugin_locations) > 0: - locations_infos = self.PluginInfos[plugin]["locations_infos"] - expandbutton.SetToggle(locations_infos["root"]["expanded"]) - def togglebutton(event): - if expandbutton.GetToggle(): - self.ExpandLocation(locations_infos, "root") - else: - self.CollapseLocation(locations_infos, "root") - self.PluginInfos[plugin]["expanded"] = expandbutton.GetToggle() - locations_infos["root"]["expanded"] = expandbutton.GetToggle() - self.PLCConfigMainSizer.Layout() - self.RefreshScrollBars() - event.Skip() - expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id) - else: - expandbutton.Enable(False) - iecsizer.AddWindow(expandbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) - - tc_id = wx.NewId() - tc = wx.TextCtrl(leftwindow, tc_id, size=wx.Size(150, 25), style=wx.NO_BORDER) - tc.SetFont(wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"])) - tc.ChangeValue(plugin.MandatoryParams[1].getName()) - tc.Bind(wx.EVT_TEXT, self.GetTextCtrlCallBackFunction(tc, plugin, "BaseParams.Name"), id=tc_id) - iecsizer.AddWindow(tc, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) - - rightwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1)) - rightwindow.SetBackgroundColour(bkgdclr) - - self.PluginTreeSizer.AddWindow(rightwindow, 0, border=0, flag=wx.GROW) - - rightwindowmainsizer = wx.BoxSizer(wx.VERTICAL) - rightwindow.SetSizer(rightwindowmainsizer) - - rightwindowsizer = wx.FlexGridSizer(cols=2, rows=1) - rightwindowsizer.AddGrowableCol(1) - rightwindowsizer.AddGrowableRow(0) - rightwindowmainsizer.AddSizer(rightwindowsizer, 0, border=8, flag=wx.TOP|wx.GROW) - - msizer = self.GenerateMethodButtonSizer(plugin, rightwindow, not self.PluginInfos[plugin]["right_visible"]) - rightwindowsizer.AddSizer(msizer, 0, border=0, flag=wx.GROW) - - rightparamssizer = wx.BoxSizer(wx.HORIZONTAL) - rightwindowsizer.AddSizer(rightparamssizer, 0, border=0, flag=wx.ALIGN_RIGHT) - - paramswindow = wx.Panel(rightwindow, -1, size=wx.Size(-1, -1)) - paramswindow.SetBackgroundColour(bkgdclr) - - psizer = wx.BoxSizer(wx.HORIZONTAL) - paramswindow.SetSizer(psizer) - self.PluginInfos[plugin]["params"] = paramswindow - - rightparamssizer.AddWindow(paramswindow, 0, border=5, flag=wx.ALL) - - plugin_infos = plugin.GetParamsAttributes() - self.RefreshSizerElement(paramswindow, psizer, plugin, plugin_infos, None, False) - - if not self.PluginInfos[plugin]["right_visible"]: - paramswindow.Hide() - - rightminimizebutton_id = wx.NewId() - rightminimizebutton = wx.lib.buttons.GenBitmapToggleButton(id=rightminimizebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Maximize.png')), - name='MinimizeButton', parent=rightwindow, pos=wx.Point(0, 0), - size=wx.Size(24, 24), style=wx.NO_BORDER) - make_genbitmaptogglebutton_flat(rightminimizebutton) - rightminimizebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Minimize.png'))) - rightminimizebutton.SetToggle(self.PluginInfos[plugin]["right_visible"]) - rightparamssizer.AddWindow(rightminimizebutton, 0, border=5, flag=wx.ALL) - - def togglerightwindow(event): - if rightminimizebutton.GetToggle(): - rightparamssizer.Show(0) - msizer.SetCols(1) - else: - rightparamssizer.Hide(0) - msizer.SetCols(len(plugin.PluginMethods)) - self.PluginInfos[plugin]["right_visible"] = rightminimizebutton.GetToggle() - self.PLCConfigMainSizer.Layout() - self.RefreshScrollBars() - event.Skip() - rightminimizebutton.Bind(wx.EVT_BUTTON, togglerightwindow, id=rightminimizebutton_id) - - self.PluginInfos[plugin]["left"] = leftwindow - self.PluginInfos[plugin]["right"] = rightwindow - for child in self.PluginInfos[plugin]["children"]: - self.GenerateTreeBranch(child) - if not self.PluginInfos[child]["expanded"]: - self.CollapsePlugin(child) - if len(plugin_locations) > 0: - locations_infos = self.PluginInfos[plugin]["locations_infos"] - for location in plugin_locations: - locations_infos["root"]["children"].append("root.%s" % location["name"]) - self.GenerateLocationTreeBranch(locations_infos, "root", location) - if not locations_infos["root"]["expanded"]: - self.CollapseLocation(locations_infos, "root") - - LOCATION_BITMAP = {LOCATION_PLUGIN: "CONFIGURATION", - LOCATION_MODULE: "RESOURCE", - LOCATION_GROUP: "PROGRAM", - LOCATION_VAR_INPUT: "VAR_INPUT", - LOCATION_VAR_OUTPUT: "VAR_OUTPUT", - LOCATION_VAR_MEMORY: "VAR_LOCAL"} - - def GenerateLocationTreeBranch(self, locations_infos, parent, location): - leftwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1)) - self.PluginTreeSizer.AddWindow(leftwindow, 0, border=0, flag=wx.GROW) - - leftwindowsizer = wx.BoxSizer(wx.HORIZONTAL) - leftwindow.SetSizer(leftwindowsizer) - - rightwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1)) - self.PluginTreeSizer.AddWindow(rightwindow, 0, border=0, flag=wx.GROW) - - location_name = "%s.%s" % (parent, location["name"]) - if not locations_infos.has_key(location_name): - locations_infos[location_name] = {"expanded" : False} - - if location["type"] in [LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP]: - leftwindow.SetBackgroundColour(WINDOW_COLOUR) - rightwindow.SetBackgroundColour(WINDOW_COLOUR) - - st = wx.StaticText(leftwindow, -1) - st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"])) - st.SetLabel(location["location"]) - leftwindowsizer.AddWindow(st, 0, border=5, flag=wx.RIGHT) - - expandbutton_id = wx.NewId() - expandbutton = wx.lib.buttons.GenBitmapToggleButton(id=expandbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'plus.png')), - name='ExpandButton', parent=leftwindow, pos=wx.Point(0, 0), - size=wx.Size(13, 13), style=wx.NO_BORDER) - expandbutton.labelDelta = 0 - expandbutton.SetBezelWidth(0) - expandbutton.SetUseFocusIndicator(False) - expandbutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'minus.png'))) - expandbutton.SetToggle(locations_infos[location_name]["expanded"]) - - if len(location["children"]) > 0: - def togglebutton(event): - if expandbutton.GetToggle(): - self.ExpandLocation(locations_infos, location_name) - else: - self.CollapseLocation(locations_infos, location_name) - locations_infos[location_name]["expanded"] = expandbutton.GetToggle() - self.PLCConfigMainSizer.Layout() - self.RefreshScrollBars() - event.Skip() - expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id) - else: - expandbutton.Enable(False) - leftwindowsizer.AddWindow(expandbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) - - else: - leftwindow.SetBackgroundColour(wx.WHITE) - rightwindow.SetBackgroundColour(wx.WHITE) - - leftwindowsizer.Add(wx.Size(20, 16), 0) - - sb = wx.StaticBitmap(leftwindow, -1) - sb.SetBitmap(wx.Bitmap(os.path.join(base_folder, "plcopeneditor", 'Images', '%s.png' % self.LOCATION_BITMAP[location["type"]]))) - leftwindowsizer.AddWindow(sb, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) - - st_id = wx.NewId() - st = wx.StaticText(leftwindow, st_id, size=wx.DefaultSize, style=wx.NO_BORDER) - label = location["name"] - if location["type"] in [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]: - label += " (%s)" % location["location"] - infos = location.copy() - infos.pop("children") - st.SetFont(wx.Font(faces["size"] * 0.5, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = faces["helv"])) - st.Bind(wx.EVT_LEFT_DOWN, self.GenerateLocationLeftDownFunction(infos)) - else: - st.SetFont(wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"])) - st.SetLabel(label) - leftwindowsizer.AddWindow(st, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) - - locations_infos[location_name]["left"] = leftwindow - locations_infos[location_name]["right"] = rightwindow - locations_infos[location_name]["children"] = [] - for child in location["children"]: - child_name = "%s.%s" % (location_name, child["name"]) - locations_infos[location_name]["children"].append(child_name) - self.GenerateLocationTreeBranch(locations_infos, location_name, child) - if not locations_infos[location_name]["expanded"]: - self.CollapseLocation(locations_infos, location_name) - - def GenerateLocationLeftDownFunction(self, infos): - def OnLocationLeftDownFunction(event): - data = wx.TextDataObject(str((infos["location"], "location", infos["IEC_type"], infos["name"], infos["description"]))) - dragSource = wx.DropSource(self) - dragSource.SetData(data) - dragSource.DoDragDrop() - event.Skip() - return OnLocationLeftDownFunction - - def RefreshAll(self): - self.RefreshPLCParams() - self.RefreshPluginTree() - - def GetItemChannelChangedFunction(self, plugin, value): - def OnPluginTreeItemChannelChanged(event): - res = self.SetPluginParamsAttribute(plugin, "BaseParams.IEC_Channel", value) - event.Skip() - return OnPluginTreeItemChannelChanged - - def _GetAddPluginFunction(self, name, plugin): - def OnPluginMenu(event): - wx.CallAfter(self.AddPlugin, name, plugin) - return OnPluginMenu - - def Gen_AddPluginMenu(self, plugin): - def AddPluginMenu(event): - main_menu = wx.Menu(title='') - if len(plugin.PlugChildsTypes) > 0: - for name, XSDClass, help in plugin.PlugChildsTypes: - new_id = wx.NewId() - main_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=_("Append ")+help) - self.Bind(wx.EVT_MENU, self._GetAddPluginFunction(name, plugin), id=new_id) - self.PopupMenuXY(main_menu) - return AddPluginMenu - - def GetButtonCallBackFunction(self, plugin, method): - """ Generate the callbackfunc for a given plugin method""" - def OnButtonClick(event): - # Disable button to prevent re-entrant call - event.GetEventObject().Disable() - # Call - getattr(plugin,method)() - # Re-enable button - event.GetEventObject().Enable() - # Trigger refresh on Idle - wx.CallAfter(self.RefreshAll) - event.Skip() - return OnButtonClick - - def GetChoiceCallBackFunction(self, choicectrl, plugin, path): - def OnChoiceChanged(event): - res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection()) - choicectrl.SetStringSelection(res) - event.Skip() - return OnChoiceChanged - - def GetChoiceContentCallBackFunction(self, choicectrl, staticboxsizer, plugin, path): - def OnChoiceContentChanged(event): - res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection()) - if wx.VERSION < (2, 8, 0): - self.ParamsPanel.Freeze() - choicectrl.SetStringSelection(res) - infos = self.PluginRoot.GetParamsAttributes(path) - staticbox = staticboxsizer.GetStaticBox() - staticbox.SetLabel("%(name)s - %(value)s"%infos) - self.RefreshSizerElement(self.ParamsPanel, staticboxsizer, infos["children"], "%s.%s"%(path, infos["name"]), selected=selected) - self.ParamsPanelMainSizer.Layout() - self.ParamsPanel.Thaw() - self.ParamsPanel.Refresh() - else: - wx.CallAfter(self.RefreshAll) - event.Skip() - return OnChoiceContentChanged - - def GetTextCtrlCallBackFunction(self, textctrl, plugin, path): - def OnTextCtrlChanged(event): - res = self.SetPluginParamsAttribute(plugin, path, textctrl.GetValue()) - if res != textctrl.GetValue(): - textctrl.ChangeValue(res) - event.Skip() - return OnTextCtrlChanged - - def GetCheckBoxCallBackFunction(self, chkbx, plugin, path): - def OnCheckBoxChanged(event): - res = self.SetPluginParamsAttribute(plugin, path, chkbx.IsChecked()) - chkbx.SetValue(res) - event.Skip() - return OnCheckBoxChanged - - def ClearSizer(self, sizer): - staticboxes = [] - for item in sizer.GetChildren(): - if item.IsSizer(): - item_sizer = item.GetSizer() - self.ClearSizer(item_sizer) - if isinstance(item_sizer, wx.StaticBoxSizer): - staticboxes.append(item_sizer.GetStaticBox()) - sizer.Clear(True) - for staticbox in staticboxes: - staticbox.Destroy() - - def RefreshSizerElement(self, parent, sizer, plugin, elements, path, clean = True): - if clean: - if wx.VERSION < (2, 8, 0): - self.ClearSizer(sizer) - else: - sizer.Clear(True) - first = True - for element_infos in elements: - if path: - element_path = "%s.%s"%(path, element_infos["name"]) - else: - element_path = element_infos["name"] - if element_infos["type"] == "element": - label = element_infos["name"] - staticbox = wx.StaticBox(id=-1, label=_(label), - name='%s_staticbox'%element_infos["name"], parent=parent, - pos=wx.Point(0, 0), size=wx.Size(10, 0), style=0) - staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) - if first: - sizer.AddSizer(staticboxsizer, 0, border=0, flag=wx.GROW|wx.TOP) - else: - sizer.AddSizer(staticboxsizer, 0, border=0, flag=wx.GROW) - self.RefreshSizerElement(parent, staticboxsizer, plugin, element_infos["children"], element_path) - else: - boxsizer = wx.FlexGridSizer(cols=3, rows=1) - boxsizer.AddGrowableCol(1) - if first: - sizer.AddSizer(boxsizer, 0, border=5, flag=wx.GROW|wx.ALL) - else: - sizer.AddSizer(boxsizer, 0, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM) - staticbitmap = GenStaticBitmap(ID=-1, bitmapname="%s.png"%element_infos["name"], - name="%s_bitmap"%element_infos["name"], parent=parent, - pos=wx.Point(0, 0), size=wx.Size(24, 24), style=0) - boxsizer.AddWindow(staticbitmap, 0, border=5, flag=wx.RIGHT) - label = element_infos["name"] - statictext = wx.StaticText(id=-1, label="%s:"%_(label), - name="%s_label"%element_infos["name"], parent=parent, - pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) - boxsizer.AddWindow(statictext, 0, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT) - id = wx.NewId() - if isinstance(element_infos["type"], types.ListType): - combobox = wx.ComboBox(id=id, name=element_infos["name"], parent=parent, - pos=wx.Point(0, 0), size=wx.Size(300, 28), style=wx.CB_READONLY) - boxsizer.AddWindow(combobox, 0, border=0, flag=0) - if element_infos["use"] == "optional": - combobox.Append("") - if len(element_infos["type"]) > 0 and isinstance(element_infos["type"][0], types.TupleType): - for choice, xsdclass in element_infos["type"]: - combobox.Append(choice) - name = element_infos["name"] - value = element_infos["value"] - staticbox = wx.StaticBox(id=-1, label="%s - %s"%(_(name), _(value)), - name='%s_staticbox'%element_infos["name"], parent=parent, - pos=wx.Point(0, 0), size=wx.Size(10, 0), style=0) - staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) - sizer.AddSizer(staticboxsizer, 0, border=5, flag=wx.GROW|wx.BOTTOM) - self.RefreshSizerElement(parent, staticboxsizer, plugin, element_infos["children"], element_path) - callback = self.GetChoiceContentCallBackFunction(combobox, staticboxsizer, plugin, element_path) - else: - for choice in element_infos["type"]: - combobox.Append(choice) - callback = self.GetChoiceCallBackFunction(combobox, plugin, element_path) - if element_infos["value"] is None: - combobox.SetStringSelection("") - else: - combobox.SetStringSelection(element_infos["value"]) - combobox.Bind(wx.EVT_COMBOBOX, callback, id=id) - elif isinstance(element_infos["type"], types.DictType): - scmin = -(2**31) - scmax = 2**31-1 - if "min" in element_infos["type"]: - scmin = element_infos["type"]["min"] - if "max" in element_infos["type"]: - scmax = element_infos["type"]["max"] - spinctrl = wx.SpinCtrl(id=id, name=element_infos["name"], parent=parent, - pos=wx.Point(0, 0), size=wx.Size(300, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT) - spinctrl.SetRange(scmin,scmax) - boxsizer.AddWindow(spinctrl, 0, border=0, flag=0) - spinctrl.SetValue(element_infos["value"]) - spinctrl.Bind(wx.EVT_SPINCTRL, self.GetTextCtrlCallBackFunction(spinctrl, plugin, element_path), id=id) - else: - if element_infos["type"] == "boolean": - checkbox = wx.CheckBox(id=id, name=element_infos["name"], parent=parent, - pos=wx.Point(0, 0), size=wx.Size(17, 25), style=0) - boxsizer.AddWindow(checkbox, 0, border=0, flag=0) - checkbox.SetValue(element_infos["value"]) - checkbox.Bind(wx.EVT_CHECKBOX, self.GetCheckBoxCallBackFunction(checkbox, plugin, element_path), id=id) - elif element_infos["type"] in ["unsignedLong", "long","integer"]: - if element_infos["type"].startswith("unsigned"): - scmin = 0 - else: - scmin = -(2**31) - scmax = 2**31-1 - spinctrl = wx.SpinCtrl(id=id, name=element_infos["name"], parent=parent, - pos=wx.Point(0, 0), size=wx.Size(300, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT) - spinctrl.SetRange(scmin, scmax) - boxsizer.AddWindow(spinctrl, 0, border=0, flag=0) - spinctrl.SetValue(element_infos["value"]) - spinctrl.Bind(wx.EVT_SPINCTRL, self.GetTextCtrlCallBackFunction(spinctrl, plugin, element_path), id=id) - else: - choices = cPickle.loads(str(self.Config.Read(element_path, cPickle.dumps([""])))) - textctrl = TextCtrlAutoComplete.TextCtrlAutoComplete(id=id, - name=element_infos["name"], - parent=parent, - appframe=self, - choices=choices, - element_path=element_path, - pos=wx.Point(0, 0), - size=wx.Size(300, 25), - style=0) - - boxsizer.AddWindow(textctrl, 0, border=0, flag=0) - textctrl.ChangeValue(str(element_infos["value"])) - textctrl.Bind(wx.EVT_TEXT, self.GetTextCtrlCallBackFunction(textctrl, plugin, element_path)) - first = False - - def ResetView(self): - IDEFrame.ResetView(self) - self.PluginInfos = {} - if self.PluginRoot is not None: - self.PluginRoot.CloseProject() - self.PluginRoot = None - self.Log.flush() - self.DebugVariablePanel.SetDataProducer(None) - - def OnNewProjectMenu(self, event): - if not self.Config.HasEntry("lastopenedfolder"): - defaultpath = os.path.expanduser("~") - else: - defaultpath = self.Config.Read("lastopenedfolder") - - dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, wx.DD_NEW_DIR_BUTTON) - if dialog.ShowModal() == wx.ID_OK: - projectpath = dialog.GetPath() - dialog.Destroy() - self.Config.Write("lastopenedfolder", os.path.dirname(projectpath)) - self.Config.Flush() - self.ResetView() - self.PluginRoot = PluginsRoot(self, self.Log) - self.Controler = self.PluginRoot - result = self.PluginRoot.NewProject(projectpath) - if not result: - self.DebugVariablePanel.SetDataProducer(self.PluginRoot) - self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE) - self.RefreshAll() - else: - self.ResetView() - self.ShowErrorMessage(result) - self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU) - - def OnOpenProjectMenu(self, event): - if not self.Config.HasEntry("lastopenedfolder"): - defaultpath = os.path.expanduser("~") - else: - defaultpath = self.Config.Read("lastopenedfolder") - - dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, wx.DD_NEW_DIR_BUTTON) - if dialog.ShowModal() == wx.ID_OK: - projectpath = dialog.GetPath() - if os.path.isdir(projectpath): - self.Config.Write("lastopenedfolder", os.path.dirname(projectpath)) - self.Config.Flush() - self.ResetView() - self.PluginRoot = PluginsRoot(self, self.Log) - self.Controler = self.PluginRoot - result = self.PluginRoot.LoadProject(projectpath) - if not result: - self.DebugVariablePanel.SetDataProducer(self.PluginRoot) - self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE) - self.RefreshAll() - else: - self.ResetView() - self.ShowErrorMessage(result) - else: - self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath) - self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU) - dialog.Destroy() - - def OnCloseProjectMenu(self, event): - if self.PluginRoot is not None: - if self.PluginRoot.ProjectTestModified(): - dialog = wx.MessageDialog(self, - _("Save changes ?"), - _("Close Application"), - wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION) - answer = dialog.ShowModal() - dialog.Destroy() - if answer == wx.ID_YES: - self.PluginRoot.SaveProject() - elif answer == wx.ID_CANCEL: - return - self.ResetView() - self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU) - self.RefreshAll() - - def OnSaveProjectMenu(self, event): - if self.PluginRoot is not None: - self.PluginRoot.SaveProject() - self.RefreshAll() - self.RefreshTitle() - - def OnSaveProjectAsMenu(self, event): - if self.PluginRoot is not None: - self.PluginRoot.SaveProjectAs() - self.RefreshAll() - self.RefreshTitle() - event.Skip() - - def OnPropertiesMenu(self, event): - self.ShowProperties() - - def OnQuitMenu(self, event): - self.Close() - - def OnBeremizMenu(self, event): - open_pdf(Bpath( "doc", "manual_beremiz.pdf")) - - def OnAboutMenu(self, event): - OpenHtmlFrame(self,_("About Beremiz"), Bpath("doc","about.html"), wx.Size(550, 500)) - - def GetAddButtonFunction(self, plugin, window): - def AddButtonFunction(event): - if plugin and len(plugin.PlugChildsTypes) > 0: - plugin_menu = wx.Menu(title='') - for name, XSDClass, help in plugin.PlugChildsTypes: - new_id = wx.NewId() - plugin_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=name) - self.Bind(wx.EVT_MENU, self._GetAddPluginFunction(name, plugin), id=new_id) - window_pos = window.GetPosition() - wx.CallAfter(self.PLCConfig.PopupMenu, plugin_menu) - event.Skip() - return AddButtonFunction - - def GetDeleteButtonFunction(self, plugin): - def DeleteButtonFunction(event): - wx.CallAfter(self.DeletePlugin, plugin) - event.Skip() - return DeleteButtonFunction - - def AddPlugin(self, PluginType, plugin): - if self.PluginRoot.CheckProjectPathPerm(): - dialog = wx.TextEntryDialog(self, _("Please enter a name for plugin:"), _("Add Plugin"), "", wx.OK|wx.CANCEL) - if dialog.ShowModal() == wx.ID_OK: - PluginName = dialog.GetValue() - plugin.PlugAddChild(PluginName, PluginType) - self.RefreshPluginTree() - self.PluginRoot.RefreshPluginsBlockLists() - dialog.Destroy() - - def DeletePlugin(self, plugin): - if self.PluginRoot.CheckProjectPathPerm(): - dialog = wx.MessageDialog(self, _("Really delete plugin ?"), _("Remove plugin"), wx.YES_NO|wx.NO_DEFAULT) - if dialog.ShowModal() == wx.ID_YES: - self.PluginInfos.pop(plugin) - plugin.PlugRemove() - del plugin - self.PluginRoot.RefreshPluginsBlockLists() - self.RefreshPluginTree() - dialog.Destroy() - -#------------------------------------------------------------------------------- -# Exception Handler -#------------------------------------------------------------------------------- - -Max_Traceback_List_Size = 20 - -def Display_Exception_Dialog(e_type, e_value, e_tb, bug_report_path): - trcbck_lst = [] - for i,line in enumerate(traceback.extract_tb(e_tb)): - trcbck = " " + str(i+1) + _(". ") - if line[0].find(os.getcwd()) == -1: - trcbck += _("file : ") + str(line[0]) + _(", ") - else: - trcbck += _("file : ") + str(line[0][len(os.getcwd()):]) + _(", ") - trcbck += _("line : ") + str(line[1]) + _(", ") + _("function : ") + str(line[2]) - trcbck_lst.append(trcbck) - - # Allow clicking.... - cap = wx.Window_GetCapture() - if cap: - cap.ReleaseMouse() - - dlg = wx.SingleChoiceDialog(None, - _(""" -An unhandled exception (bug) occured. Bug report saved at : -(%s) - -Please contact LOLITech at: -+33 (0)3 29 57 60 42 -or please be kind enough to send this file to: -bugs_beremiz@lolitech.fr - -You should now restart Beremiz. - -Traceback: -""") % bug_report_path + - str(e_type) + " : " + str(e_value), - _("Error"), - trcbck_lst) - try: - res = (dlg.ShowModal() == wx.ID_OK) - finally: - dlg.Destroy() - - return res - -def Display_Error_Dialog(e_value): - message = wxMessageDialog(None, str(e_value), _("Error"), wxOK|wxICON_ERROR) - message.ShowModal() - message.Destroy() - -def get_last_traceback(tb): - while tb.tb_next: - tb = tb.tb_next - return tb - - -def format_namespace(d, indent=' '): - return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()]) - - -ignored_exceptions = [] # a problem with a line in a module is only reported once per session - -def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]): - - def handle_exception(e_type, e_value, e_traceback): - traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func - last_tb = get_last_traceback(e_traceback) - ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno) - if str(e_value).startswith("!!!"): - Display_Error_Dialog(e_value) - elif ex not in ignored_exceptions: - date = time.ctime() - bug_report_path = path+os.sep+"bug_report_"+date.replace(':','-').replace(' ','_')+".txt" - result = Display_Exception_Dialog(e_type,e_value,e_traceback,bug_report_path) - if result: - ignored_exceptions.append(ex) - info = { - 'app-title' : wx.GetApp().GetAppName(), # app_title - 'app-version' : app_version, - 'wx-version' : wx.VERSION_STRING, - 'wx-platform' : wx.Platform, - 'python-version' : platform.python_version(), #sys.version.split()[0], - 'platform' : platform.platform(), - 'e-type' : e_type, - 'e-value' : e_value, - 'date' : date, - 'cwd' : os.getcwd(), - } - if e_traceback: - info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value) - last_tb = get_last_traceback(e_traceback) - exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred - info['locals'] = format_namespace(exception_locals) - if 'self' in exception_locals: - info['self'] = format_namespace(exception_locals['self'].__dict__) - - output = open(bug_report_path,'w') - lst = info.keys() - lst.sort() - for a in lst: - output.write(a+":\n"+str(info[a])+"\n\n") - - #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args) - sys.excepthook = handle_exception - -if __name__ == '__main__': - # Install a exception handle for bug reports - AddExceptHook(os.getcwd(),__version__) - - frame = Beremiz(None, projectOpen, buildpath) - frame.Show() - splash.Close() - app.MainLoop() +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of Beremiz, a Integrated Development Environment for +#programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +__version__ = "$Revision$" + +import os, sys, getopt, wx +import tempfile +import shutil +import random + +CWD = os.path.split(os.path.realpath(__file__))[0] + +def Bpath(*args): + return os.path.join(CWD,*args) + +if __name__ == '__main__': + def usage(): + print "\nUsage of Beremiz.py :" + print "\n %s [Projectpath] [Buildpath]\n"%sys.argv[0] + + try: + opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) + except getopt.GetoptError: + # print help information and exit: + usage() + sys.exit(2) + + for o, a in opts: + if o in ("-h", "--help"): + usage() + sys.exit() + + if len(args) > 2: + usage() + sys.exit() + elif len(args) == 1: + projectOpen = args[0] + buildpath = None + elif len(args) == 2: + projectOpen = args[0] + buildpath = args[1] + else: + projectOpen = None + buildpath = None + + app = wx.PySimpleApp() + app.SetAppName('beremiz') + wx.InitAllImageHandlers() + + bmp = wx.Image(Bpath("images","splash.png")).ConvertToBitmap() + splash=wx.SplashScreen(bmp,wx.SPLASH_CENTRE_ON_SCREEN, 1000, None) + wx.Yield() + +# Import module for internationalization +import gettext +import __builtin__ + +# Get folder containing translation files +localedir = os.path.join(CWD,"locale") +# Get the default language +langid = wx.LANGUAGE_DEFAULT +# Define translation domain (name of translation files) +domain = "Beremiz" + +# Define locale for wx +loc = __builtin__.__dict__.get('loc', None) +if loc is None: + loc = wx.Locale(langid) + __builtin__.__dict__['loc'] = loc +# Define location for searching translation files +loc.AddCatalogLookupPathPrefix(localedir) +# Define locale domain +loc.AddCatalog(domain) + +def unicode_translation(message): + return wx.GetTranslation(message).encode("utf-8") + +if __name__ == '__main__': + __builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation + +#Quick hack to be able to find Beremiz IEC tools. Should be config params. +base_folder = os.path.split(sys.path[0])[0] +sys.path.append(base_folder) +sys.path.append(os.path.join(base_folder, "plcopeneditor")) +sys.path.append(os.path.join(base_folder, "docutils")) + +import wx.lib.buttons, wx.lib.statbmp +import TextCtrlAutoComplete, cPickle +import types, time, re, platform, time, traceback, commands +from plugger import PluginsRoot, MATIEC_ERROR_MODEL +from wxPopen import ProcessLogger + +from docutils import * +from PLCOpenEditor import IDEFrame, Viewer, AppendMenu, TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, TYPESTREE, INSTANCESTREE, LIBRARYTREE, SCALING +from PLCControler import LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY + +SCROLLBAR_UNIT = 10 +WINDOW_COLOUR = wx.Colour(240,240,240) +TITLE_COLOUR = wx.Colour(200,200,220) +CHANGED_TITLE_COLOUR = wx.Colour(220,200,220) +CHANGED_WINDOW_COLOUR = wx.Colour(255,240,240) + +if wx.Platform == '__WXMSW__': + faces = { 'times': 'Times New Roman', + 'mono' : 'Courier New', + 'helv' : 'Arial', + 'other': 'Comic Sans MS', + 'size' : 16, + } +else: + faces = { 'times': 'Times', + 'mono' : 'Courier', + 'helv' : 'Helvetica', + 'other': 'new century schoolbook', + 'size' : 18, + } + +# Some helpers to tweak GenBitmapTextButtons +# TODO: declare customized classes instead. +gen_mini_GetBackgroundBrush = lambda obj:lambda dc: wx.Brush(obj.GetParent().GetBackgroundColour(), wx.SOLID) +gen_textbutton_GetLabelSize = lambda obj:lambda:(wx.lib.buttons.GenButton._GetLabelSize(obj)[:-1] + (False,)) + +def make_genbitmaptogglebutton_flat(button): + button.GetBackgroundBrush = gen_mini_GetBackgroundBrush(button) + button.labelDelta = 0 + button.SetBezelWidth(0) + button.SetUseFocusIndicator(False) + +# Patch wx.lib.imageutils so that gray is supported on alpha images +import wx.lib.imageutils +from wx.lib.imageutils import grayOut as old_grayOut +def grayOut(anImage): + if anImage.HasAlpha(): + AlphaData = anImage.GetAlphaData() + else : + AlphaData = None + + old_grayOut(anImage) + + if AlphaData is not None: + anImage.SetAlphaData(AlphaData) + +wx.lib.imageutils.grayOut = grayOut + +class GenBitmapTextButton(wx.lib.buttons.GenBitmapTextButton): + def _GetLabelSize(self): + """ used internally """ + w, h = self.GetTextExtent(self.GetLabel()) + if not self.bmpLabel: + return w, h, False # if there isn't a bitmap use the size of the text + + w_bmp = self.bmpLabel.GetWidth()+2 + h_bmp = self.bmpLabel.GetHeight()+2 + height = h + h_bmp + if w_bmp > w: + width = w_bmp + else: + width = w + return width, height, False + + def DrawLabel(self, dc, width, height, dw=0, dy=0): + bmp = self.bmpLabel + if bmp != None: # if the bitmap is used + if self.bmpDisabled and not self.IsEnabled(): + bmp = self.bmpDisabled + if self.bmpFocus and self.hasFocus: + bmp = self.bmpFocus + if self.bmpSelected and not self.up: + bmp = self.bmpSelected + bw,bh = bmp.GetWidth(), bmp.GetHeight() + if not self.up: + dw = dy = self.labelDelta + hasMask = bmp.GetMask() != None + else: + bw = bh = 0 # no bitmap -> size is zero + + dc.SetFont(self.GetFont()) + if self.IsEnabled(): + dc.SetTextForeground(self.GetForegroundColour()) + else: + dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) + + label = self.GetLabel() + tw, th = dc.GetTextExtent(label) # size of text + if not self.up: + dw = dy = self.labelDelta + + pos_x = (width-bw)/2+dw # adjust for bitmap and text to centre + pos_y = (height-bh-th)/2+dy + if bmp !=None: + dc.DrawBitmap(bmp, pos_x, pos_y, hasMask) # draw bitmap if available + pos_x = (width-tw)/2+dw # adjust for bitmap and text to centre + pos_y += bh + 2 + + dc.DrawText(label, pos_x, pos_y) # draw the text + + +class GenStaticBitmap(wx.lib.statbmp.GenStaticBitmap): + """ Customized GenStaticBitmap, fix transparency redraw bug on wx2.8/win32, + and accept image name as __init__ parameter, fail silently if file do not exist""" + def __init__(self, parent, ID, bitmapname, + pos = wx.DefaultPosition, size = wx.DefaultSize, + style = 0, + name = "genstatbmp"): + + bitmappath = Bpath( "images", bitmapname) + if os.path.isfile(bitmappath): + bitmap = wx.Bitmap(bitmappath) + else: + bitmap = None + wx.lib.statbmp.GenStaticBitmap.__init__(self, parent, ID, bitmap, + pos, size, + style, + name) + + def OnPaint(self, event): + dc = wx.PaintDC(self) + colour = self.GetParent().GetBackgroundColour() + dc.SetPen(wx.Pen(colour)) + dc.SetBrush(wx.Brush(colour )) + dc.DrawRectangle(0, 0, *dc.GetSizeTuple()) + if self._bitmap: + dc.DrawBitmap(self._bitmap, 0, 0, True) + + +class LogPseudoFile: + """ Base class for file like objects to facilitate StdOut for the Shell.""" + def __init__(self, output): + self.red_white = wx.TextAttr("RED", "WHITE") + self.red_yellow = wx.TextAttr("RED", "YELLOW") + self.black_white = wx.TextAttr("BLACK", "WHITE") + self.default_style = None + self.output = output + + def write(self, s, style = None): + if style is None : style=self.black_white + self.output.Freeze(); + if self.default_style != style: + self.output.SetDefaultStyle(style) + self.default_style = style + self.output.AppendText(s) + self.output.ScrollLines(s.count('\n')+1) + self.output.ShowPosition(self.output.GetLastPosition()) + self.output.Thaw() + + def write_warning(self, s): + self.write(s,self.red_white) + + def write_error(self, s): + self.write(s,self.red_yellow) + + def flush(self): + self.output.SetValue("") + + def isatty(self): + return false + +[ID_BEREMIZ, ID_BEREMIZMAINSPLITTER, + ID_BEREMIZPLCCONFIG, ID_BEREMIZLOGCONSOLE, + ID_BEREMIZINSPECTOR] = [wx.NewId() for _init_ctrls in range(5)] + +[ID_BEREMIZRUNMENUBUILD, ID_BEREMIZRUNMENUSIMULATE, + ID_BEREMIZRUNMENURUN, ID_BEREMIZRUNMENUSAVELOG, +] = [wx.NewId() for _init_coll_EditMenu_Items in range(4)] + +class Beremiz(IDEFrame): + + def _init_coll_FileMenu_Items(self, parent): + AppendMenu(parent, help='', id=wx.ID_NEW, + kind=wx.ITEM_NORMAL, text=_(u'New\tCTRL+N')) + AppendMenu(parent, help='', id=wx.ID_OPEN, + kind=wx.ITEM_NORMAL, text=_(u'Open\tCTRL+O')) + AppendMenu(parent, help='', id=wx.ID_SAVE, + kind=wx.ITEM_NORMAL, text=_(u'Save\tCTRL+S')) + AppendMenu(parent, help='', id=wx.ID_SAVEAS, + kind=wx.ITEM_NORMAL, text=_(u'Save as\tCTRL+SHIFT+S')) + AppendMenu(parent, help='', id=wx.ID_CLOSE, + kind=wx.ITEM_NORMAL, text=_(u'Close Tab\tCTRL+W')) + AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL, + kind=wx.ITEM_NORMAL, text=_(u'Close Project')) + parent.AppendSeparator() + AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP, + kind=wx.ITEM_NORMAL, text=_(u'Page Setup')) + AppendMenu(parent, help='', id=wx.ID_PREVIEW, + kind=wx.ITEM_NORMAL, text=_(u'Preview')) + AppendMenu(parent, help='', id=wx.ID_PRINT, + kind=wx.ITEM_NORMAL, text=_(u'Print')) + parent.AppendSeparator() + AppendMenu(parent, help='', id=wx.ID_PROPERTIES, + kind=wx.ITEM_NORMAL, text=_(u'Properties')) + parent.AppendSeparator() + AppendMenu(parent, help='', id=wx.ID_EXIT, + kind=wx.ITEM_NORMAL, text=_(u'Quit\tCTRL+Q')) + + self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW) + self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN) + self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE) + self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS) + self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE) + self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL) + self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP) + self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW) + self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT) + self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES) + self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT) + + def _init_coll_HelpMenu_Items(self, parent): + parent.Append(help='', id=wx.ID_HELP, + kind=wx.ITEM_NORMAL, text=_(u'Beremiz\tF1')) + parent.Append(help='', id=wx.ID_ABOUT, + kind=wx.ITEM_NORMAL, text=_(u'About')) + self.Bind(wx.EVT_MENU, self.OnBeremizMenu, id=wx.ID_HELP) + self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT) + + def _init_coll_PLCConfigMainSizer_Items(self, parent): + parent.AddSizer(self.PLCParamsSizer, 0, border=10, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) + parent.AddSizer(self.PluginTreeSizer, 0, border=10, flag=wx.BOTTOM|wx.LEFT|wx.RIGHT) + + def _init_coll_PLCConfigMainSizer_Growables(self, parent): + parent.AddGrowableCol(0) + parent.AddGrowableRow(1) + + def _init_coll_PluginTreeSizer_Growables(self, parent): + parent.AddGrowableCol(0) + parent.AddGrowableCol(1) + + def _init_beremiz_sizers(self): + self.PLCConfigMainSizer = wx.FlexGridSizer(cols=1, hgap=2, rows=2, vgap=2) + self.PLCParamsSizer = wx.BoxSizer(wx.VERTICAL) + #self.PluginTreeSizer = wx.FlexGridSizer(cols=3, hgap=0, rows=0, vgap=2) + self.PluginTreeSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=0, vgap=2) + + self._init_coll_PLCConfigMainSizer_Items(self.PLCConfigMainSizer) + self._init_coll_PLCConfigMainSizer_Growables(self.PLCConfigMainSizer) + self._init_coll_PluginTreeSizer_Growables(self.PluginTreeSizer) + + self.PLCConfig.SetSizer(self.PLCConfigMainSizer) + + def _init_ctrls(self, prnt): + IDEFrame._init_ctrls(self, prnt) + + self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=ID_BEREMIZINSPECTOR) + accel = wx.AcceleratorTable([wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), ID_BEREMIZINSPECTOR)]) + self.SetAcceleratorTable(accel) + + self.PLCConfig = wx.ScrolledWindow(id=ID_BEREMIZPLCCONFIG, + name='PLCConfig', parent=self.LeftNoteBook, pos=wx.Point(0, 0), + size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER|wx.HSCROLL|wx.VSCROLL) + self.PLCConfig.SetBackgroundColour(wx.WHITE) + self.PLCConfig.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown) + self.PLCConfig.Bind(wx.EVT_SIZE, self.OnMoveWindow) + self.PLCConfig.Bind(wx.EVT_MOUSEWHEEL, self.OnPLCConfigScroll) + self.BottomNoteBook.InsertPage(0, self.PLCConfig, _("Topology"), True) + + self.LogConsole = wx.TextCtrl(id=ID_BEREMIZLOGCONSOLE, value='', + name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0), + size=wx.Size(0, 0), style=wx.TE_MULTILINE|wx.TE_RICH2) + self.LogConsole.Bind(wx.EVT_LEFT_DCLICK, self.OnLogConsoleDClick) + self.BottomNoteBook.AddPage(self.LogConsole, _("Log Console")) + + self._init_beremiz_sizers() + + def __init__(self, parent, projectOpen=None, buildpath=None, plugin_root=None, debug=True): + IDEFrame.__init__(self, parent, debug) + self.Config = wx.ConfigBase.Get() + + self.Log = LogPseudoFile(self.LogConsole) + + self.local_runtime = None + self.runtime_port = None + self.local_runtime_tmpdir = None + + self.DisableEvents = False + # Variable allowing disabling of PLCConfig scroll when Popup shown + self.ScrollingEnabled = True + + self.PluginInfos = {} + + if projectOpen is not None and os.path.isdir(projectOpen): + self.PluginRoot = PluginsRoot(self, self.Log) + self.Controler = self.PluginRoot + result = self.PluginRoot.LoadProject(projectOpen, buildpath) + if not result: + self.DebugVariablePanel.SetDataProducer(self.PluginRoot) + self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE) + self.RefreshAll() + else: + self.ResetView() + self.ShowErrorMessage(result) + else: + self.PluginRoot = plugin_root + self.Controler = plugin_root + if plugin_root is not None: + self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE) + self.RefreshAll() + + # Add beremiz's icon in top left corner of the frame + self.SetIcon(wx.Icon(Bpath( "images", "brz.ico"), wx.BITMAP_TYPE_ICO)) + + self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) + + self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) + + def RefreshTitle(self): + name = _("Beremiz") + if self.PluginRoot is not None: + projectname = self.PluginRoot.GetProjectName() + if self.PluginRoot.PlugTestModified(): + projectname = "~%s~" % projectname + self.SetTitle("%s - %s" % (name, projectname)) + else: + self.SetTitle(name) + + def StartLocalRuntime(self, taskbaricon = True): + if self.local_runtime is None or self.local_runtime.finished: + # create temporary directory for runtime working directory + self.local_runtime_tmpdir = tempfile.mkdtemp() + # choose an arbitrary random port for runtime + self.runtime_port = int(random.random() * 1000) + 61131 + # launch local runtime + self.local_runtime = ProcessLogger(self.Log, + "\"%s\" \"%s\" -p %s -i localhost %s %s"%(sys.executable, + Bpath("Beremiz_service.py"), + self.runtime_port, + {False : "-x 0", True :"-x 1"}[taskbaricon], + self.local_runtime_tmpdir), + no_gui=False) + self.local_runtime.spin(timeout=500, keyword = "working", kill_it = False) + return self.runtime_port + + def KillLocalRuntime(self): + if self.local_runtime is not None: + # shutdown local runtime + self.local_runtime.kill(gently=False) + # clear temp dir + shutil.rmtree(self.local_runtime_tmpdir) + + def OnOpenWidgetInspector(self, evt): + # Activate the widget inspection tool + from wx.lib.inspection import InspectionTool + if not InspectionTool().initialized: + InspectionTool().Init() + + # Find a widget to be selected in the tree. Use either the + # one under the cursor, if any, or this frame. + wnd = wx.FindWindowAtPointer() + if not wnd: + wnd = self + InspectionTool().Show(wnd, True) + + def OnLogConsoleDClick(self, event): + wx.CallAfter(self.SearchLineForError) + event.Skip() + + def SearchLineForError(self): + if self.PluginRoot is not None: + text = self.LogConsole.GetRange(0, self.LogConsole.GetInsertionPoint()) + line = self.LogConsole.GetLineText(len(text.splitlines()) - 1) + result = MATIEC_ERROR_MODEL.match(line) + if result is not None: + first_line, first_column, last_line, last_column, error = result.groups() + infos = self.PluginRoot.ShowError(self.Log, + (int(first_line), int(first_column)), + (int(last_line), int(last_column))) + + def OnCloseFrame(self, event): + if self.PluginRoot is not None: + if self.PluginRoot.ProjectTestModified(): + dialog = wx.MessageDialog(self, + _("Save changes ?"), + _("Close Application"), + wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION) + answer = dialog.ShowModal() + dialog.Destroy() + if answer == wx.ID_YES: + self.PluginRoot.SaveProject() + event.Skip() + elif answer == wx.ID_NO: + event.Skip() + return + else: + event.Veto() + return + + self.KillLocalRuntime() + + event.Skip() + + def OnMoveWindow(self, event): + self.GetBestSize() + self.RefreshScrollBars() + event.Skip() + + def EnableScrolling(self, enable): + self.ScrollingEnabled = enable + + def OnPLCConfigScroll(self, event): + if self.ScrollingEnabled: + event.Skip() + + def OnPanelLeftDown(self, event): + focused = self.FindFocus() + if isinstance(focused, TextCtrlAutoComplete.TextCtrlAutoComplete): + focused.DismissListBox() + event.Skip() + + def RefreshFileMenu(self): + if self.PluginRoot is not None: + selected = self.TabsOpened.GetSelection() + if selected >= 0: + graphic_viewer = isinstance(self.TabsOpened.GetPage(selected), Viewer) + else: + graphic_viewer = False + if self.TabsOpened.GetPageCount() > 0: + self.FileMenu.Enable(wx.ID_CLOSE, True) + if graphic_viewer: + self.FileMenu.Enable(wx.ID_PREVIEW, True) + self.FileMenu.Enable(wx.ID_PRINT, True) + else: + self.FileMenu.Enable(wx.ID_PREVIEW, False) + self.FileMenu.Enable(wx.ID_PRINT, False) + else: + self.FileMenu.Enable(wx.ID_CLOSE, False) + self.FileMenu.Enable(wx.ID_PREVIEW, False) + self.FileMenu.Enable(wx.ID_PRINT, False) + self.FileMenu.Enable(wx.ID_PAGE_SETUP, True) + self.FileMenu.Enable(wx.ID_SAVE, True) + self.FileMenu.Enable(wx.ID_SAVEAS, True) + self.FileMenu.Enable(wx.ID_PROPERTIES, True) + self.FileMenu.Enable(wx.ID_CLOSE_ALL, True) + else: + self.FileMenu.Enable(wx.ID_CLOSE, False) + self.FileMenu.Enable(wx.ID_PAGE_SETUP, False) + self.FileMenu.Enable(wx.ID_PREVIEW, False) + self.FileMenu.Enable(wx.ID_PRINT, False) + self.FileMenu.Enable(wx.ID_SAVE, False) + self.FileMenu.Enable(wx.ID_SAVEAS, False) + self.FileMenu.Enable(wx.ID_PROPERTIES, False) + self.FileMenu.Enable(wx.ID_CLOSE_ALL, False) + + def RefreshScrollBars(self): + xstart, ystart = self.PLCConfig.GetViewStart() + window_size = self.PLCConfig.GetClientSize() + sizer = self.PLCConfig.GetSizer() + if sizer: + maxx, maxy = sizer.GetMinSize() + self.PLCConfig.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, + maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT, + max(0, min(xstart, (maxx - window_size[0]) / SCROLLBAR_UNIT)), + max(0, min(ystart, (maxy - window_size[1]) / SCROLLBAR_UNIT))) + + def RefreshPLCParams(self): + self.Freeze() + self.ClearSizer(self.PLCParamsSizer) + + if self.PluginRoot is not None: + plcwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1)) + if self.PluginRoot.PlugTestModified(): + bkgdclr = CHANGED_TITLE_COLOUR + else: + bkgdclr = TITLE_COLOUR + + if self.PluginRoot not in self.PluginInfos: + self.PluginInfos[self.PluginRoot] = {"right_visible" : False} + + plcwindow.SetBackgroundColour(TITLE_COLOUR) + plcwindow.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown) + self.PLCParamsSizer.AddWindow(plcwindow, 0, border=0, flag=wx.GROW) + + plcwindowsizer = wx.BoxSizer(wx.HORIZONTAL) + plcwindow.SetSizer(plcwindowsizer) + + st = wx.StaticText(plcwindow, -1) + st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"])) + st.SetLabel(self.PluginRoot.GetProjectName()) + plcwindowsizer.AddWindow(st, 0, border=5, flag=wx.ALL|wx.ALIGN_CENTER) + + addbutton_id = wx.NewId() + addbutton = wx.lib.buttons.GenBitmapButton(id=addbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Add.png')), + name='AddPluginButton', parent=plcwindow, pos=wx.Point(0, 0), + size=wx.Size(16, 16), style=wx.NO_BORDER) + addbutton.SetToolTipString(_("Add a sub plugin")) + addbutton.Bind(wx.EVT_BUTTON, self.Gen_AddPluginMenu(self.PluginRoot), id=addbutton_id) + plcwindowsizer.AddWindow(addbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER) + + plcwindowmainsizer = wx.BoxSizer(wx.VERTICAL) + plcwindowsizer.AddSizer(plcwindowmainsizer, 0, border=5, flag=wx.ALL) + + plcwindowbuttonsizer = wx.BoxSizer(wx.HORIZONTAL) + plcwindowmainsizer.AddSizer(plcwindowbuttonsizer, 0, border=0, flag=wx.ALIGN_CENTER) + + msizer = self.GenerateMethodButtonSizer(self.PluginRoot, plcwindow, not self.PluginInfos[self.PluginRoot]["right_visible"]) + plcwindowbuttonsizer.AddSizer(msizer, 0, border=0, flag=wx.GROW) + + paramswindow = wx.Panel(plcwindow, -1, size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL) + paramswindow.SetBackgroundColour(TITLE_COLOUR) + paramswindow.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown) + plcwindowbuttonsizer.AddWindow(paramswindow, 0, border=0, flag=0) + + psizer = wx.BoxSizer(wx.HORIZONTAL) + paramswindow.SetSizer(psizer) + + plugin_infos = self.PluginRoot.GetParamsAttributes() + self.RefreshSizerElement(paramswindow, psizer, self.PluginRoot, plugin_infos, None, False) + + if not self.PluginInfos[self.PluginRoot]["right_visible"]: + paramswindow.Hide() + + minimizebutton_id = wx.NewId() + minimizebutton = wx.lib.buttons.GenBitmapToggleButton(id=minimizebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Maximize.png')), + name='MinimizeButton', parent=plcwindow, pos=wx.Point(0, 0), + size=wx.Size(24, 24), style=wx.NO_BORDER) + make_genbitmaptogglebutton_flat(minimizebutton) + minimizebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Minimize.png'))) + minimizebutton.SetToggle(self.PluginInfos[self.PluginRoot]["right_visible"]) + plcwindowbuttonsizer.AddWindow(minimizebutton, 0, border=5, flag=wx.ALL) + + def togglewindow(event): + if minimizebutton.GetToggle(): + paramswindow.Show() + msizer.SetCols(1) + else: + paramswindow.Hide() + msizer.SetCols(len(self.PluginRoot.PluginMethods)) + self.PluginInfos[self.PluginRoot]["right_visible"] = minimizebutton.GetToggle() + self.PLCConfigMainSizer.Layout() + self.RefreshScrollBars() + event.Skip() + minimizebutton.Bind(wx.EVT_BUTTON, togglewindow, id=minimizebutton_id) + + self.PluginInfos[self.PluginRoot]["main"] = plcwindow + self.PluginInfos[self.PluginRoot]["params"] = paramswindow + + self.PLCConfigMainSizer.Layout() + self.RefreshScrollBars() + self.Thaw() + + def GenerateMethodButtonSizer(self, plugin, parent, horizontal = True): + normal_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = faces["helv"]) + mouseover_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline=True, faceName = faces["helv"]) + if horizontal: + msizer = wx.FlexGridSizer(cols=len(plugin.PluginMethods)) + else: + msizer = wx.FlexGridSizer(cols=1) + for plugin_method in plugin.PluginMethods: + if "method" in plugin_method and plugin_method.get("shown",True): + id = wx.NewId() + label = plugin_method["name"] + button = GenBitmapTextButton(id=id, parent=parent, + bitmap=wx.Bitmap(Bpath( "%s.png"%plugin_method.get("bitmap", os.path.join("images", "Unknown")))), label=label, + name=label, pos=wx.DefaultPosition, style=wx.NO_BORDER) + button.SetFont(normal_bt_font) + button.SetToolTipString(plugin_method["tooltip"]) + button.Bind(wx.EVT_BUTTON, self.GetButtonCallBackFunction(plugin, plugin_method["method"]), id=id) + # a fancy underline on mouseover + def setFontStyle(b, s): + def fn(event): + b.SetFont(s) + b.Refresh() + event.Skip() + return fn + button.Bind(wx.EVT_ENTER_WINDOW, setFontStyle(button, mouseover_bt_font)) + button.Bind(wx.EVT_LEAVE_WINDOW, setFontStyle(button, normal_bt_font)) + #hack to force size to mini + if not plugin_method.get("enabled",True): + button.Disable() + msizer.AddWindow(button, 0, border=0, flag=wx.ALIGN_CENTER) + return msizer + + def RefreshPluginTree(self): + self.Freeze() + self.ClearSizer(self.PluginTreeSizer) + if self.PluginRoot is not None: + for child in self.PluginRoot.IECSortedChilds(): + self.GenerateTreeBranch(child) + if not self.PluginInfos[child]["expanded"]: + self.CollapsePlugin(child) + self.PLCConfigMainSizer.Layout() + self.RefreshScrollBars() + self.Thaw() + + def SetPluginParamsAttribute(self, plugin, *args, **kwargs): + res, StructChanged = plugin.SetParamsAttribute(*args, **kwargs) + if StructChanged: + wx.CallAfter(self.RefreshPluginTree) + else: + if plugin == self.PluginRoot: + bkgdclr = CHANGED_TITLE_COLOUR + items = ["main", "params"] + else: + bkgdclr = CHANGED_WINDOW_COLOUR + items = ["left", "right", "params"] + for i in items: + self.PluginInfos[plugin][i].SetBackgroundColour(bkgdclr) + self.PluginInfos[plugin][i].Refresh() + return res + + def ExpandPlugin(self, plugin, force = False): + for child in self.PluginInfos[plugin]["children"]: + self.PluginInfos[child]["left"].Show() + self.PluginInfos[child]["right"].Show() + if force or not self.PluginInfos[child]["expanded"]: + self.ExpandPlugin(child, force) + if force: + self.PluginInfos[child]["expanded"] = True + locations_infos = self.PluginInfos[plugin].get("locations_infos", None) + if locations_infos is not None: + if force or locations_infos["root"]["expanded"]: + self.ExpandLocation(locations_infos, "root", force) + if force: + locations_infos["root"]["expanded"] = True + + + def CollapsePlugin(self, plugin, force = False): + for child in self.PluginInfos[plugin]["children"]: + self.PluginInfos[child]["left"].Hide() + self.PluginInfos[child]["right"].Hide() + self.CollapsePlugin(child, force) + if force: + self.PluginInfos[child]["expanded"] = False + locations_infos = self.PluginInfos[plugin].get("locations_infos", None) + if locations_infos is not None: + self.CollapseLocation(locations_infos, "root", force) + if force: + locations_infos["root"]["expanded"] = False + + def ExpandLocation(self, locations_infos, group, force = False): + for child in locations_infos[group]["children"]: + locations_infos[child]["left"].Show() + locations_infos[child]["right"].Show() + if force or locations_infos[child]["expanded"]: + self.ExpandLocation(locations_infos, child, force) + if force: + locations_infos[child]["expanded"] = True + + def CollapseLocation(self, locations_infos, group, force = False): + for child in locations_infos[group]["children"]: + locations_infos[child]["left"].Hide() + locations_infos[child]["right"].Hide() + self.CollapseLocation(locations_infos, child, force) + if force: + locations_infos[child]["expanded"] = False + + def GenerateTreeBranch(self, plugin): + leftwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1)) + if plugin.PlugTestModified(): + bkgdclr=CHANGED_WINDOW_COLOUR + else: + bkgdclr=WINDOW_COLOUR + + leftwindow.SetBackgroundColour(bkgdclr) + + if not self.PluginInfos.has_key(plugin): + self.PluginInfos[plugin] = {"expanded" : False, "right_visible" : False} + + self.PluginInfos[plugin]["children"] = plugin.IECSortedChilds() + plugin_locations = [] + if len(self.PluginInfos[plugin]["children"]) == 0: + plugin_locations = plugin.GetVariableLocationTree()["children"] + if not self.PluginInfos[plugin].has_key("locations_infos"): + self.PluginInfos[plugin]["locations_infos"] = {"root": {"expanded" : False}} + + self.PluginInfos[plugin]["locations_infos"]["root"]["children"] = [] + + self.PluginTreeSizer.AddWindow(leftwindow, 0, border=0, flag=wx.GROW) + + leftwindowsizer = wx.FlexGridSizer(cols=1, rows=2) + leftwindowsizer.AddGrowableCol(0) + leftwindow.SetSizer(leftwindowsizer) + + leftbuttonmainsizer = wx.FlexGridSizer(cols=3, rows=1) + leftbuttonmainsizer.AddGrowableCol(0) + leftwindowsizer.AddSizer(leftbuttonmainsizer, 0, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT) #|wx.TOP + + leftbuttonsizer = wx.BoxSizer(wx.HORIZONTAL) + leftbuttonmainsizer.AddSizer(leftbuttonsizer, 0, border=5, flag=wx.GROW|wx.RIGHT) + + leftsizer = wx.BoxSizer(wx.VERTICAL) + leftbuttonsizer.AddSizer(leftsizer, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + + rolesizer = wx.BoxSizer(wx.HORIZONTAL) + leftsizer.AddSizer(rolesizer, 0, border=0, flag=wx.GROW|wx.RIGHT) + + enablebutton_id = wx.NewId() + enablebutton = wx.lib.buttons.GenBitmapToggleButton(id=enablebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Disabled.png')), + name='EnableButton', parent=leftwindow, size=wx.Size(16, 16), pos=wx.Point(0, 0), style=0)#wx.NO_BORDER) + enablebutton.SetToolTipString(_("Enable/Disable this plugin")) + make_genbitmaptogglebutton_flat(enablebutton) + enablebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Enabled.png'))) + enablebutton.SetToggle(plugin.MandatoryParams[1].getEnabled()) + def toggleenablebutton(event): + res = self.SetPluginParamsAttribute(plugin, "BaseParams.Enabled", enablebutton.GetToggle()) + enablebutton.SetToggle(res) + event.Skip() + enablebutton.Bind(wx.EVT_BUTTON, toggleenablebutton, id=enablebutton_id) + rolesizer.AddWindow(enablebutton, 0, border=0, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + + roletext = wx.StaticText(leftwindow, -1) + roletext.SetLabel(plugin.PlugHelp) + rolesizer.AddWindow(roletext, 0, border=5, flag=wx.RIGHT|wx.ALIGN_LEFT) + + plugin_IECChannel = plugin.BaseParams.getIEC_Channel() + + iecsizer = wx.BoxSizer(wx.HORIZONTAL) + leftsizer.AddSizer(iecsizer, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + + st = wx.StaticText(leftwindow, -1) + st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"])) + st.SetLabel(plugin.GetFullIEC_Channel()) + iecsizer.AddWindow(st, 0, border=0, flag=0) + + updownsizer = wx.BoxSizer(wx.VERTICAL) + iecsizer.AddSizer(updownsizer, 0, border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) + + if plugin_IECChannel > 0: + ieccdownbutton_id = wx.NewId() + ieccdownbutton = wx.lib.buttons.GenBitmapButton(id=ieccdownbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'IECCDown.png')), + name='IECCDownButton', parent=leftwindow, pos=wx.Point(0, 0), + size=wx.Size(16, 16), style=wx.NO_BORDER) + ieccdownbutton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(plugin, plugin_IECChannel - 1), id=ieccdownbutton_id) + updownsizer.AddWindow(ieccdownbutton, 0, border=0, flag=wx.ALIGN_LEFT) + + ieccupbutton_id = wx.NewId() + ieccupbutton = wx.lib.buttons.GenBitmapTextButton(id=ieccupbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'IECCUp.png')), + name='IECCUpButton', parent=leftwindow, pos=wx.Point(0, 0), + size=wx.Size(16, 16), style=wx.NO_BORDER) + ieccupbutton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(plugin, plugin_IECChannel + 1), id=ieccupbutton_id) + updownsizer.AddWindow(ieccupbutton, 0, border=0, flag=wx.ALIGN_LEFT) + + adddeletesizer = wx.BoxSizer(wx.VERTICAL) + iecsizer.AddSizer(adddeletesizer, 0, border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) + + deletebutton_id = wx.NewId() + deletebutton = wx.lib.buttons.GenBitmapButton(id=deletebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Delete.png')), + name='DeletePluginButton', parent=leftwindow, pos=wx.Point(0, 0), + size=wx.Size(16, 16), style=wx.NO_BORDER) + deletebutton.SetToolTipString(_("Delete this plugin")) + deletebutton.Bind(wx.EVT_BUTTON, self.GetDeleteButtonFunction(plugin), id=deletebutton_id) + adddeletesizer.AddWindow(deletebutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER) + + if len(plugin.PlugChildsTypes) > 0: + addbutton_id = wx.NewId() + addbutton = wx.lib.buttons.GenBitmapButton(id=addbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Add.png')), + name='AddPluginButton', parent=leftwindow, pos=wx.Point(0, 0), + size=wx.Size(16, 16), style=wx.NO_BORDER) + addbutton.SetToolTipString(_("Add a sub plugin")) + addbutton.Bind(wx.EVT_BUTTON, self.Gen_AddPluginMenu(plugin), id=addbutton_id) + adddeletesizer.AddWindow(addbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER) + + expandbutton_id = wx.NewId() + expandbutton = wx.lib.buttons.GenBitmapToggleButton(id=expandbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'plus.png')), + name='ExpandButton', parent=leftwindow, pos=wx.Point(0, 0), + size=wx.Size(13, 13), style=wx.NO_BORDER) + expandbutton.labelDelta = 0 + expandbutton.SetBezelWidth(0) + expandbutton.SetUseFocusIndicator(False) + expandbutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'minus.png'))) + + if len(self.PluginInfos[plugin]["children"]) > 0: + expandbutton.SetToggle(self.PluginInfos[plugin]["expanded"]) + def togglebutton(event): + if expandbutton.GetToggle(): + self.ExpandPlugin(plugin) + else: + self.CollapsePlugin(plugin) + self.PluginInfos[plugin]["expanded"] = expandbutton.GetToggle() + self.PLCConfigMainSizer.Layout() + self.RefreshScrollBars() + event.Skip() + expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id) + elif len(plugin_locations) > 0: + locations_infos = self.PluginInfos[plugin]["locations_infos"] + expandbutton.SetToggle(locations_infos["root"]["expanded"]) + def togglebutton(event): + if expandbutton.GetToggle(): + self.ExpandLocation(locations_infos, "root") + else: + self.CollapseLocation(locations_infos, "root") + self.PluginInfos[plugin]["expanded"] = expandbutton.GetToggle() + locations_infos["root"]["expanded"] = expandbutton.GetToggle() + self.PLCConfigMainSizer.Layout() + self.RefreshScrollBars() + event.Skip() + expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id) + else: + expandbutton.Enable(False) + iecsizer.AddWindow(expandbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + + tc_id = wx.NewId() + tc = wx.TextCtrl(leftwindow, tc_id, size=wx.Size(150, 25), style=wx.NO_BORDER) + tc.SetFont(wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"])) + tc.ChangeValue(plugin.MandatoryParams[1].getName()) + tc.Bind(wx.EVT_TEXT, self.GetTextCtrlCallBackFunction(tc, plugin, "BaseParams.Name"), id=tc_id) + iecsizer.AddWindow(tc, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + + rightwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1)) + rightwindow.SetBackgroundColour(bkgdclr) + + self.PluginTreeSizer.AddWindow(rightwindow, 0, border=0, flag=wx.GROW) + + rightwindowmainsizer = wx.BoxSizer(wx.VERTICAL) + rightwindow.SetSizer(rightwindowmainsizer) + + rightwindowsizer = wx.FlexGridSizer(cols=2, rows=1) + rightwindowsizer.AddGrowableCol(1) + rightwindowsizer.AddGrowableRow(0) + rightwindowmainsizer.AddSizer(rightwindowsizer, 0, border=8, flag=wx.TOP|wx.GROW) + + msizer = self.GenerateMethodButtonSizer(plugin, rightwindow, not self.PluginInfos[plugin]["right_visible"]) + rightwindowsizer.AddSizer(msizer, 0, border=0, flag=wx.GROW) + + rightparamssizer = wx.BoxSizer(wx.HORIZONTAL) + rightwindowsizer.AddSizer(rightparamssizer, 0, border=0, flag=wx.ALIGN_RIGHT) + + paramswindow = wx.Panel(rightwindow, -1, size=wx.Size(-1, -1)) + paramswindow.SetBackgroundColour(bkgdclr) + + psizer = wx.BoxSizer(wx.HORIZONTAL) + paramswindow.SetSizer(psizer) + self.PluginInfos[plugin]["params"] = paramswindow + + rightparamssizer.AddWindow(paramswindow, 0, border=5, flag=wx.ALL) + + plugin_infos = plugin.GetParamsAttributes() + self.RefreshSizerElement(paramswindow, psizer, plugin, plugin_infos, None, False) + + if not self.PluginInfos[plugin]["right_visible"]: + paramswindow.Hide() + + rightminimizebutton_id = wx.NewId() + rightminimizebutton = wx.lib.buttons.GenBitmapToggleButton(id=rightminimizebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Maximize.png')), + name='MinimizeButton', parent=rightwindow, pos=wx.Point(0, 0), + size=wx.Size(24, 24), style=wx.NO_BORDER) + make_genbitmaptogglebutton_flat(rightminimizebutton) + rightminimizebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Minimize.png'))) + rightminimizebutton.SetToggle(self.PluginInfos[plugin]["right_visible"]) + rightparamssizer.AddWindow(rightminimizebutton, 0, border=5, flag=wx.ALL) + + def togglerightwindow(event): + if rightminimizebutton.GetToggle(): + rightparamssizer.Show(0) + msizer.SetCols(1) + else: + rightparamssizer.Hide(0) + msizer.SetCols(len(plugin.PluginMethods)) + self.PluginInfos[plugin]["right_visible"] = rightminimizebutton.GetToggle() + self.PLCConfigMainSizer.Layout() + self.RefreshScrollBars() + event.Skip() + rightminimizebutton.Bind(wx.EVT_BUTTON, togglerightwindow, id=rightminimizebutton_id) + + self.PluginInfos[plugin]["left"] = leftwindow + self.PluginInfos[plugin]["right"] = rightwindow + for child in self.PluginInfos[plugin]["children"]: + self.GenerateTreeBranch(child) + if not self.PluginInfos[child]["expanded"]: + self.CollapsePlugin(child) + if len(plugin_locations) > 0: + locations_infos = self.PluginInfos[plugin]["locations_infos"] + for location in plugin_locations: + locations_infos["root"]["children"].append("root.%s" % location["name"]) + self.GenerateLocationTreeBranch(locations_infos, "root", location) + if not locations_infos["root"]["expanded"]: + self.CollapseLocation(locations_infos, "root") + + LOCATION_BITMAP = {LOCATION_PLUGIN: "CONFIGURATION", + LOCATION_MODULE: "RESOURCE", + LOCATION_GROUP: "PROGRAM", + LOCATION_VAR_INPUT: "VAR_INPUT", + LOCATION_VAR_OUTPUT: "VAR_OUTPUT", + LOCATION_VAR_MEMORY: "VAR_LOCAL"} + + def GenerateLocationTreeBranch(self, locations_infos, parent, location): + leftwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1)) + self.PluginTreeSizer.AddWindow(leftwindow, 0, border=0, flag=wx.GROW) + + leftwindowsizer = wx.BoxSizer(wx.HORIZONTAL) + leftwindow.SetSizer(leftwindowsizer) + + rightwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1)) + self.PluginTreeSizer.AddWindow(rightwindow, 0, border=0, flag=wx.GROW) + + location_name = "%s.%s" % (parent, location["name"]) + if not locations_infos.has_key(location_name): + locations_infos[location_name] = {"expanded" : False} + + if location["type"] in [LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP]: + leftwindow.SetBackgroundColour(WINDOW_COLOUR) + rightwindow.SetBackgroundColour(WINDOW_COLOUR) + + st = wx.StaticText(leftwindow, -1) + st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"])) + st.SetLabel(location["location"]) + leftwindowsizer.AddWindow(st, 0, border=5, flag=wx.RIGHT) + + expandbutton_id = wx.NewId() + expandbutton = wx.lib.buttons.GenBitmapToggleButton(id=expandbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'plus.png')), + name='ExpandButton', parent=leftwindow, pos=wx.Point(0, 0), + size=wx.Size(13, 13), style=wx.NO_BORDER) + expandbutton.labelDelta = 0 + expandbutton.SetBezelWidth(0) + expandbutton.SetUseFocusIndicator(False) + expandbutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'minus.png'))) + expandbutton.SetToggle(locations_infos[location_name]["expanded"]) + + if len(location["children"]) > 0: + def togglebutton(event): + if expandbutton.GetToggle(): + self.ExpandLocation(locations_infos, location_name) + else: + self.CollapseLocation(locations_infos, location_name) + locations_infos[location_name]["expanded"] = expandbutton.GetToggle() + self.PLCConfigMainSizer.Layout() + self.RefreshScrollBars() + event.Skip() + expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id) + else: + expandbutton.Enable(False) + leftwindowsizer.AddWindow(expandbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + + else: + leftwindow.SetBackgroundColour(wx.WHITE) + rightwindow.SetBackgroundColour(wx.WHITE) + + leftwindowsizer.Add(wx.Size(20, 16), 0) + + sb = wx.StaticBitmap(leftwindow, -1) + sb.SetBitmap(wx.Bitmap(os.path.join(base_folder, "plcopeneditor", 'Images', '%s.png' % self.LOCATION_BITMAP[location["type"]]))) + leftwindowsizer.AddWindow(sb, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + + st_id = wx.NewId() + st = wx.StaticText(leftwindow, st_id, size=wx.DefaultSize, style=wx.NO_BORDER) + label = location["name"] + if location["type"] in [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]: + label += " (%s)" % location["location"] + infos = location.copy() + infos.pop("children") + st.SetFont(wx.Font(faces["size"] * 0.5, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = faces["helv"])) + st.Bind(wx.EVT_LEFT_DOWN, self.GenerateLocationLeftDownFunction(infos)) + else: + st.SetFont(wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"])) + st.SetLabel(label) + leftwindowsizer.AddWindow(st, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + + locations_infos[location_name]["left"] = leftwindow + locations_infos[location_name]["right"] = rightwindow + locations_infos[location_name]["children"] = [] + for child in location["children"]: + child_name = "%s.%s" % (location_name, child["name"]) + locations_infos[location_name]["children"].append(child_name) + self.GenerateLocationTreeBranch(locations_infos, location_name, child) + if not locations_infos[location_name]["expanded"]: + self.CollapseLocation(locations_infos, location_name) + + def GenerateLocationLeftDownFunction(self, infos): + def OnLocationLeftDownFunction(event): + data = wx.TextDataObject(str((infos["location"], "location", infos["IEC_type"], infos["name"], infos["description"]))) + dragSource = wx.DropSource(self) + dragSource.SetData(data) + dragSource.DoDragDrop() + event.Skip() + return OnLocationLeftDownFunction + + def RefreshAll(self): + self.RefreshPLCParams() + self.RefreshPluginTree() + + def GetItemChannelChangedFunction(self, plugin, value): + def OnPluginTreeItemChannelChanged(event): + res = self.SetPluginParamsAttribute(plugin, "BaseParams.IEC_Channel", value) + event.Skip() + return OnPluginTreeItemChannelChanged + + def _GetAddPluginFunction(self, name, plugin): + def OnPluginMenu(event): + wx.CallAfter(self.AddPlugin, name, plugin) + return OnPluginMenu + + def Gen_AddPluginMenu(self, plugin): + def AddPluginMenu(event): + main_menu = wx.Menu(title='') + if len(plugin.PlugChildsTypes) > 0: + for name, XSDClass, help in plugin.PlugChildsTypes: + new_id = wx.NewId() + main_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=_("Append ")+help) + self.Bind(wx.EVT_MENU, self._GetAddPluginFunction(name, plugin), id=new_id) + self.PopupMenuXY(main_menu) + return AddPluginMenu + + def GetButtonCallBackFunction(self, plugin, method): + """ Generate the callbackfunc for a given plugin method""" + def OnButtonClick(event): + # Disable button to prevent re-entrant call + event.GetEventObject().Disable() + # Call + getattr(plugin,method)() + # Re-enable button + event.GetEventObject().Enable() + # Trigger refresh on Idle + wx.CallAfter(self.RefreshAll) + event.Skip() + return OnButtonClick + + def GetChoiceCallBackFunction(self, choicectrl, plugin, path): + def OnChoiceChanged(event): + res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection()) + choicectrl.SetStringSelection(res) + event.Skip() + return OnChoiceChanged + + def GetChoiceContentCallBackFunction(self, choicectrl, staticboxsizer, plugin, path): + def OnChoiceContentChanged(event): + res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection()) + if wx.VERSION < (2, 8, 0): + self.ParamsPanel.Freeze() + choicectrl.SetStringSelection(res) + infos = self.PluginRoot.GetParamsAttributes(path) + staticbox = staticboxsizer.GetStaticBox() + staticbox.SetLabel("%(name)s - %(value)s"%infos) + self.RefreshSizerElement(self.ParamsPanel, staticboxsizer, infos["children"], "%s.%s"%(path, infos["name"]), selected=selected) + self.ParamsPanelMainSizer.Layout() + self.ParamsPanel.Thaw() + self.ParamsPanel.Refresh() + else: + wx.CallAfter(self.RefreshAll) + event.Skip() + return OnChoiceContentChanged + + def GetTextCtrlCallBackFunction(self, textctrl, plugin, path): + def OnTextCtrlChanged(event): + res = self.SetPluginParamsAttribute(plugin, path, textctrl.GetValue()) + if res != textctrl.GetValue(): + textctrl.ChangeValue(res) + event.Skip() + return OnTextCtrlChanged + + def GetCheckBoxCallBackFunction(self, chkbx, plugin, path): + def OnCheckBoxChanged(event): + res = self.SetPluginParamsAttribute(plugin, path, chkbx.IsChecked()) + chkbx.SetValue(res) + event.Skip() + return OnCheckBoxChanged + + def ClearSizer(self, sizer): + staticboxes = [] + for item in sizer.GetChildren(): + if item.IsSizer(): + item_sizer = item.GetSizer() + self.ClearSizer(item_sizer) + if isinstance(item_sizer, wx.StaticBoxSizer): + staticboxes.append(item_sizer.GetStaticBox()) + sizer.Clear(True) + for staticbox in staticboxes: + staticbox.Destroy() + + def RefreshSizerElement(self, parent, sizer, plugin, elements, path, clean = True): + if clean: + if wx.VERSION < (2, 8, 0): + self.ClearSizer(sizer) + else: + sizer.Clear(True) + first = True + for element_infos in elements: + if path: + element_path = "%s.%s"%(path, element_infos["name"]) + else: + element_path = element_infos["name"] + if element_infos["type"] == "element": + label = element_infos["name"] + staticbox = wx.StaticBox(id=-1, label=_(label), + name='%s_staticbox'%element_infos["name"], parent=parent, + pos=wx.Point(0, 0), size=wx.Size(10, 0), style=0) + staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) + if first: + sizer.AddSizer(staticboxsizer, 0, border=0, flag=wx.GROW|wx.TOP) + else: + sizer.AddSizer(staticboxsizer, 0, border=0, flag=wx.GROW) + self.RefreshSizerElement(parent, staticboxsizer, plugin, element_infos["children"], element_path) + else: + boxsizer = wx.FlexGridSizer(cols=3, rows=1) + boxsizer.AddGrowableCol(1) + if first: + sizer.AddSizer(boxsizer, 0, border=5, flag=wx.GROW|wx.ALL) + else: + sizer.AddSizer(boxsizer, 0, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM) + staticbitmap = GenStaticBitmap(ID=-1, bitmapname="%s.png"%element_infos["name"], + name="%s_bitmap"%element_infos["name"], parent=parent, + pos=wx.Point(0, 0), size=wx.Size(24, 24), style=0) + boxsizer.AddWindow(staticbitmap, 0, border=5, flag=wx.RIGHT) + label = element_infos["name"] + statictext = wx.StaticText(id=-1, label="%s:"%_(label), + name="%s_label"%element_infos["name"], parent=parent, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + boxsizer.AddWindow(statictext, 0, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT) + id = wx.NewId() + if isinstance(element_infos["type"], types.ListType): + combobox = wx.ComboBox(id=id, name=element_infos["name"], parent=parent, + pos=wx.Point(0, 0), size=wx.Size(300, 28), style=wx.CB_READONLY) + boxsizer.AddWindow(combobox, 0, border=0, flag=0) + if element_infos["use"] == "optional": + combobox.Append("") + if len(element_infos["type"]) > 0 and isinstance(element_infos["type"][0], types.TupleType): + for choice, xsdclass in element_infos["type"]: + combobox.Append(choice) + name = element_infos["name"] + value = element_infos["value"] + staticbox = wx.StaticBox(id=-1, label="%s - %s"%(_(name), _(value)), + name='%s_staticbox'%element_infos["name"], parent=parent, + pos=wx.Point(0, 0), size=wx.Size(10, 0), style=0) + staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) + sizer.AddSizer(staticboxsizer, 0, border=5, flag=wx.GROW|wx.BOTTOM) + self.RefreshSizerElement(parent, staticboxsizer, plugin, element_infos["children"], element_path) + callback = self.GetChoiceContentCallBackFunction(combobox, staticboxsizer, plugin, element_path) + else: + for choice in element_infos["type"]: + combobox.Append(choice) + callback = self.GetChoiceCallBackFunction(combobox, plugin, element_path) + if element_infos["value"] is None: + combobox.SetStringSelection("") + else: + combobox.SetStringSelection(element_infos["value"]) + combobox.Bind(wx.EVT_COMBOBOX, callback, id=id) + elif isinstance(element_infos["type"], types.DictType): + scmin = -(2**31) + scmax = 2**31-1 + if "min" in element_infos["type"]: + scmin = element_infos["type"]["min"] + if "max" in element_infos["type"]: + scmax = element_infos["type"]["max"] + spinctrl = wx.SpinCtrl(id=id, name=element_infos["name"], parent=parent, + pos=wx.Point(0, 0), size=wx.Size(300, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT) + spinctrl.SetRange(scmin,scmax) + boxsizer.AddWindow(spinctrl, 0, border=0, flag=0) + spinctrl.SetValue(element_infos["value"]) + spinctrl.Bind(wx.EVT_SPINCTRL, self.GetTextCtrlCallBackFunction(spinctrl, plugin, element_path), id=id) + else: + if element_infos["type"] == "boolean": + checkbox = wx.CheckBox(id=id, name=element_infos["name"], parent=parent, + pos=wx.Point(0, 0), size=wx.Size(17, 25), style=0) + boxsizer.AddWindow(checkbox, 0, border=0, flag=0) + checkbox.SetValue(element_infos["value"]) + checkbox.Bind(wx.EVT_CHECKBOX, self.GetCheckBoxCallBackFunction(checkbox, plugin, element_path), id=id) + elif element_infos["type"] in ["unsignedLong", "long","integer"]: + if element_infos["type"].startswith("unsigned"): + scmin = 0 + else: + scmin = -(2**31) + scmax = 2**31-1 + spinctrl = wx.SpinCtrl(id=id, name=element_infos["name"], parent=parent, + pos=wx.Point(0, 0), size=wx.Size(300, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT) + spinctrl.SetRange(scmin, scmax) + boxsizer.AddWindow(spinctrl, 0, border=0, flag=0) + spinctrl.SetValue(element_infos["value"]) + spinctrl.Bind(wx.EVT_SPINCTRL, self.GetTextCtrlCallBackFunction(spinctrl, plugin, element_path), id=id) + else: + choices = cPickle.loads(str(self.Config.Read(element_path, cPickle.dumps([""])))) + textctrl = TextCtrlAutoComplete.TextCtrlAutoComplete(id=id, + name=element_infos["name"], + parent=parent, + appframe=self, + choices=choices, + element_path=element_path, + pos=wx.Point(0, 0), + size=wx.Size(300, 25), + style=0) + + boxsizer.AddWindow(textctrl, 0, border=0, flag=0) + textctrl.ChangeValue(str(element_infos["value"])) + textctrl.Bind(wx.EVT_TEXT, self.GetTextCtrlCallBackFunction(textctrl, plugin, element_path)) + first = False + + def ResetView(self): + IDEFrame.ResetView(self) + self.PluginInfos = {} + if self.PluginRoot is not None: + self.PluginRoot.CloseProject() + self.PluginRoot = None + self.Log.flush() + self.DebugVariablePanel.SetDataProducer(None) + + def OnNewProjectMenu(self, event): + if not self.Config.HasEntry("lastopenedfolder"): + defaultpath = os.path.expanduser("~") + else: + defaultpath = self.Config.Read("lastopenedfolder") + + dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, wx.DD_NEW_DIR_BUTTON) + if dialog.ShowModal() == wx.ID_OK: + projectpath = dialog.GetPath() + dialog.Destroy() + self.Config.Write("lastopenedfolder", os.path.dirname(projectpath)) + self.Config.Flush() + self.ResetView() + self.PluginRoot = PluginsRoot(self, self.Log) + self.Controler = self.PluginRoot + result = self.PluginRoot.NewProject(projectpath) + if not result: + self.DebugVariablePanel.SetDataProducer(self.PluginRoot) + self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE) + self.RefreshAll() + else: + self.ResetView() + self.ShowErrorMessage(result) + self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU) + + def OnOpenProjectMenu(self, event): + if not self.Config.HasEntry("lastopenedfolder"): + defaultpath = os.path.expanduser("~") + else: + defaultpath = self.Config.Read("lastopenedfolder") + + dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, wx.DD_NEW_DIR_BUTTON) + if dialog.ShowModal() == wx.ID_OK: + projectpath = dialog.GetPath() + if os.path.isdir(projectpath): + self.Config.Write("lastopenedfolder", os.path.dirname(projectpath)) + self.Config.Flush() + self.ResetView() + self.PluginRoot = PluginsRoot(self, self.Log) + self.Controler = self.PluginRoot + result = self.PluginRoot.LoadProject(projectpath) + if not result: + self.DebugVariablePanel.SetDataProducer(self.PluginRoot) + self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE) + self.RefreshAll() + else: + self.ResetView() + self.ShowErrorMessage(result) + else: + self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath) + self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU) + dialog.Destroy() + + def OnCloseProjectMenu(self, event): + if self.PluginRoot is not None: + if self.PluginRoot.ProjectTestModified(): + dialog = wx.MessageDialog(self, + _("Save changes ?"), + _("Close Application"), + wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION) + answer = dialog.ShowModal() + dialog.Destroy() + if answer == wx.ID_YES: + self.PluginRoot.SaveProject() + elif answer == wx.ID_CANCEL: + return + self.ResetView() + self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU) + self.RefreshAll() + + def OnSaveProjectMenu(self, event): + if self.PluginRoot is not None: + self.PluginRoot.SaveProject() + self.RefreshAll() + self.RefreshTitle() + + def OnSaveProjectAsMenu(self, event): + if self.PluginRoot is not None: + self.PluginRoot.SaveProjectAs() + self.RefreshAll() + self.RefreshTitle() + event.Skip() + + def OnPropertiesMenu(self, event): + self.ShowProperties() + + def OnQuitMenu(self, event): + self.Close() + + def OnBeremizMenu(self, event): + open_pdf(Bpath( "doc", "manual_beremiz.pdf")) + + def OnAboutMenu(self, event): + OpenHtmlFrame(self,_("About Beremiz"), Bpath("doc","about.html"), wx.Size(550, 500)) + + def GetAddButtonFunction(self, plugin, window): + def AddButtonFunction(event): + if plugin and len(plugin.PlugChildsTypes) > 0: + plugin_menu = wx.Menu(title='') + for name, XSDClass, help in plugin.PlugChildsTypes: + new_id = wx.NewId() + plugin_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=name) + self.Bind(wx.EVT_MENU, self._GetAddPluginFunction(name, plugin), id=new_id) + window_pos = window.GetPosition() + wx.CallAfter(self.PLCConfig.PopupMenu, plugin_menu) + event.Skip() + return AddButtonFunction + + def GetDeleteButtonFunction(self, plugin): + def DeleteButtonFunction(event): + wx.CallAfter(self.DeletePlugin, plugin) + event.Skip() + return DeleteButtonFunction + + def AddPlugin(self, PluginType, plugin): + if self.PluginRoot.CheckProjectPathPerm(): + dialog = wx.TextEntryDialog(self, _("Please enter a name for plugin:"), _("Add Plugin"), "", wx.OK|wx.CANCEL) + if dialog.ShowModal() == wx.ID_OK: + PluginName = dialog.GetValue() + plugin.PlugAddChild(PluginName, PluginType) + self.RefreshPluginTree() + self.PluginRoot.RefreshPluginsBlockLists() + dialog.Destroy() + + def DeletePlugin(self, plugin): + if self.PluginRoot.CheckProjectPathPerm(): + dialog = wx.MessageDialog(self, _("Really delete plugin ?"), _("Remove plugin"), wx.YES_NO|wx.NO_DEFAULT) + if dialog.ShowModal() == wx.ID_YES: + self.PluginInfos.pop(plugin) + plugin.PlugRemove() + del plugin + self.PluginRoot.RefreshPluginsBlockLists() + self.RefreshPluginTree() + dialog.Destroy() + +#------------------------------------------------------------------------------- +# Exception Handler +#------------------------------------------------------------------------------- + +Max_Traceback_List_Size = 20 + +def Display_Exception_Dialog(e_type, e_value, e_tb, bug_report_path): + trcbck_lst = [] + for i,line in enumerate(traceback.extract_tb(e_tb)): + trcbck = " " + str(i+1) + _(". ") + if line[0].find(os.getcwd()) == -1: + trcbck += _("file : ") + str(line[0]) + _(", ") + else: + trcbck += _("file : ") + str(line[0][len(os.getcwd()):]) + _(", ") + trcbck += _("line : ") + str(line[1]) + _(", ") + _("function : ") + str(line[2]) + trcbck_lst.append(trcbck) + + # Allow clicking.... + cap = wx.Window_GetCapture() + if cap: + cap.ReleaseMouse() + + dlg = wx.SingleChoiceDialog(None, + _(""" +An unhandled exception (bug) occured. Bug report saved at : +(%s) + +Please contact LOLITech at: ++33 (0)3 29 57 60 42 +or please be kind enough to send this file to: +bugs_beremiz@lolitech.fr + +You should now restart Beremiz. + +Traceback: +""") % bug_report_path + + str(e_type) + " : " + str(e_value), + _("Error"), + trcbck_lst) + try: + res = (dlg.ShowModal() == wx.ID_OK) + finally: + dlg.Destroy() + + return res + +def Display_Error_Dialog(e_value): + message = wxMessageDialog(None, str(e_value), _("Error"), wxOK|wxICON_ERROR) + message.ShowModal() + message.Destroy() + +def get_last_traceback(tb): + while tb.tb_next: + tb = tb.tb_next + return tb + + +def format_namespace(d, indent=' '): + return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()]) + + +ignored_exceptions = [] # a problem with a line in a module is only reported once per session + +def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]): + + def handle_exception(e_type, e_value, e_traceback): + traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func + last_tb = get_last_traceback(e_traceback) + ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno) + if str(e_value).startswith("!!!"): + Display_Error_Dialog(e_value) + elif ex not in ignored_exceptions: + date = time.ctime() + bug_report_path = path+os.sep+"bug_report_"+date.replace(':','-').replace(' ','_')+".txt" + result = Display_Exception_Dialog(e_type,e_value,e_traceback,bug_report_path) + if result: + ignored_exceptions.append(ex) + info = { + 'app-title' : wx.GetApp().GetAppName(), # app_title + 'app-version' : app_version, + 'wx-version' : wx.VERSION_STRING, + 'wx-platform' : wx.Platform, + 'python-version' : platform.python_version(), #sys.version.split()[0], + 'platform' : platform.platform(), + 'e-type' : e_type, + 'e-value' : e_value, + 'date' : date, + 'cwd' : os.getcwd(), + } + if e_traceback: + info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value) + last_tb = get_last_traceback(e_traceback) + exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred + info['locals'] = format_namespace(exception_locals) + if 'self' in exception_locals: + info['self'] = format_namespace(exception_locals['self'].__dict__) + + output = open(bug_report_path,'w') + lst = info.keys() + lst.sort() + for a in lst: + output.write(a+":\n"+str(info[a])+"\n\n") + + #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args) + sys.excepthook = handle_exception + +if __name__ == '__main__': + # Install a exception handle for bug reports + AddExceptHook(os.getcwd(),__version__) + + frame = Beremiz(None, projectOpen, buildpath) + frame.Show() + splash.Close() + app.MainLoop() diff -r 7ac746c07ff2 -r ea09f33ce717 TextCtrlAutoComplete.py --- a/TextCtrlAutoComplete.py Fri Oct 23 15:41:48 2009 +0200 +++ b/TextCtrlAutoComplete.py Fri Oct 23 18:45:24 2009 +0200 @@ -1,250 +1,250 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -#This file is part of Beremiz, a Integrated Development Environment for -#programming IEC 61131-3 automates supporting plcopen standard and CanFestival. -# -#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD -# -#See COPYING file for copyrights details. -# -#This library is free software; you can redistribute it and/or -#modify it under the terms of the GNU General Public -#License as published by the Free Software Foundation; either -#version 2.1 of the License, or (at your option) any later version. -# -#This library is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -#General Public License for more details. -# -#You should have received a copy of the GNU General Public -#License along with this library; if not, write to the Free Software -#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -import wx -import cPickle - -MAX_ITEM_COUNT = 10 -MAX_ITEM_SHOWN = 6 -if wx.Platform == '__WXMSW__': - ITEM_INTERVAL_HEIGHT = 3 -else: - ITEM_INTERVAL_HEIGHT = 6 - -if wx.Platform == '__WXMSW__': - popupclass = wx.PopupTransientWindow -else: - popupclass = wx.PopupWindow - -class PopupWithListbox(popupclass): - - def __init__(self, parent, choices=[]): - popupclass.__init__(self, parent, wx.SIMPLE_BORDER) - - self.ListBox = wx.ListBox(self, -1, style=wx.LB_HSCROLL|wx.LB_SINGLE|wx.LB_SORT) - if not wx.Platform == '__WXMSW__': - self.ListBox.Bind(wx.EVT_LISTBOX, self.OnListBoxClick) - self.ListBox.Bind(wx.EVT_LISTBOX_DCLICK, self.OnListBoxClick) - - self.SetChoices(choices) - - self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) - - def SetChoices(self, choices): - max_text_width = 0 - max_text_height = 0 - - self.ListBox.Clear() - for choice in choices: - self.ListBox.Append(choice) - w, h = self.ListBox.GetTextExtent(choice) - max_text_width = max(max_text_width, w) - max_text_height = max(max_text_height, h) - - itemcount = min(len(choices), MAX_ITEM_SHOWN) - width = self.Parent.GetSize()[0] - height = max_text_height * itemcount + ITEM_INTERVAL_HEIGHT * (itemcount + 1) - if max_text_width + 10 > width: - height += 15 - size = wx.Size(width, height) - self.ListBox.SetSize(size) - self.SetClientSize(size) - - def MoveSelection(self, direction): - selected = self.ListBox.GetSelection() - if selected == wx.NOT_FOUND: - if direction >= 0: - selected = 0 - else: - selected = self.ListBox.GetCount() - 1 - else: - selected = (selected + direction) % (self.ListBox.GetCount() + 1) - if selected == self.ListBox.GetCount(): - selected = wx.NOT_FOUND - self.ListBox.SetSelection(selected) - - def GetSelection(self): - return self.ListBox.GetStringSelection() - - def ProcessLeftDown(self, event): - selected = self.ListBox.HitTest(wx.Point(event.m_x, event.m_y)) - if selected != wx.NOT_FOUND: - wx.CallAfter(self.Parent.SetValueFromSelected, self.ListBox.GetString(selected)) - return False - - def OnListBoxClick(self, event): - selected = event.GetSelection() - if selected != wx.NOT_FOUND: - wx.CallAfter(self.Parent.SetValueFromSelected, self.ListBox.GetString(selected)) - event.Skip() - - def OnKeyDown(self, event): - self.Parent.ProcessEvent(event) - - def OnDismiss(self): - self.Parent.listbox = None - wx.CallAfter(self.Parent.DismissListBox) - -class TextCtrlAutoComplete(wx.TextCtrl): - - def __init__ (self, parent, appframe, choices=None, dropDownClick=True, - element_path=None, **therest): - """ - Constructor works just like wx.TextCtrl except you can pass in a - list of choices. You can also change the choice list at any time - by calling setChoices. - """ - - therest['style'] = wx.TE_PROCESS_ENTER | therest.get('style', 0) - - wx.TextCtrl.__init__(self, parent, **therest) - self.AppFrame = appframe - - #Some variables - self._dropDownClick = dropDownClick - self._lastinsertionpoint = None - - self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y) - self.element_path = element_path - - self.listbox = None - - self.SetChoices(choices) - - #gp = self - #while ( gp != None ) : - # gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp ) - # gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp ) - # gp = gp.GetParent() - - self.Bind(wx.EVT_KILL_FOCUS, self.OnControlChanged) - self.Bind(wx.EVT_TEXT_ENTER, self.OnControlChanged) - self.Bind(wx.EVT_TEXT, self.OnEnteredText) - self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) - - #If need drop down on left click - if dropDownClick: - self.Bind(wx.EVT_LEFT_DOWN, self.OnClickToggleDown) - self.Bind(wx.EVT_LEFT_UP, self.OnClickToggleUp) - - def __del__(self): - self.AppFrame = None - - def ChangeValue(self, value): - wx.TextCtrl.ChangeValue(self, value) - self.RefreshListBoxChoices() - - def OnEnteredText(self, event): - wx.CallAfter(self.RefreshListBoxChoices) - event.Skip() - - def OnKeyDown(self, event): - """ Do some work when the user press on the keys: - up and down: move the cursor - """ - keycode = event.GetKeyCode() - if keycode in [wx.WXK_DOWN, wx.WXK_UP]: - self.PopupListBox() - if keycode == wx.WXK_DOWN: - self.listbox.MoveSelection(1) - else: - self.listbox.MoveSelection(-1) - elif keycode in [wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_RETURN] and self.listbox is not None: - self.SetValueFromSelected(self.listbox.GetSelection()) - elif event.GetKeyCode() == wx.WXK_ESCAPE: - self.DismissListBox() - else: - event.Skip() - - def OnClickToggleDown(self, event): - self._lastinsertionpoint = self.GetInsertionPoint() - event.Skip() - - def OnClickToggleUp(self, event): - if self.GetInsertionPoint() == self._lastinsertionpoint: - wx.CallAfter(self.PopupListBox) - self._lastinsertionpoint = None - event.Skip() - - def OnControlChanged(self, event): - res = self.GetValue() - config = wx.ConfigBase.Get() - listentries = cPickle.loads(str(config.Read(self.element_path, cPickle.dumps([])))) - if res and res not in listentries: - listentries = (listentries + [res])[-MAX_ITEM_COUNT:] - config.Write(self.element_path, cPickle.dumps(listentries)) - config.Flush() - self.SetChoices(listentries) - self.DismissListBox() - event.Skip() - - def SetChoices(self, choices): - self._choices = choices - self.RefreshListBoxChoices() - - def GetChoices(self): - return self._choices - - def SetValueFromSelected(self, selected): - """ - Sets the wx.TextCtrl value from the selected wx.ListCtrl item. - Will do nothing if no item is selected in the wx.ListCtrl. - """ - if selected != "": - self.SetValue(selected) - self.DismissListBox() - - def RefreshListBoxChoices(self): - if self.listbox is not None: - text = self.GetValue() - choices = [choice for choice in self._choices if choice.startswith(text)] - self.listbox.SetChoices(choices) - - def PopupListBox(self): - if self.listbox is None: - self.listbox = PopupWithListbox(self) - - # Show the popup right below or above the button - # depending on available screen space... - pos = self.ClientToScreen((0, 0)) - sz = self.GetSize() - self.listbox.Position(pos, (0, sz[1])) - - self.RefreshListBoxChoices() - - if wx.Platform == '__WXMSW__': - self.listbox.Popup() - else: - self.listbox.Show() - self.AppFrame.EnableScrolling(False) - - def DismissListBox(self): - if self.listbox is not None: - if wx.Platform == '__WXMSW__': - self.listbox.Dismiss() - else: - self.listbox.Destroy() - self.listbox = None - self.AppFrame.EnableScrolling(True) - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of Beremiz, a Integrated Development Environment for +#programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import wx +import cPickle + +MAX_ITEM_COUNT = 10 +MAX_ITEM_SHOWN = 6 +if wx.Platform == '__WXMSW__': + ITEM_INTERVAL_HEIGHT = 3 +else: + ITEM_INTERVAL_HEIGHT = 6 + +if wx.Platform == '__WXMSW__': + popupclass = wx.PopupTransientWindow +else: + popupclass = wx.PopupWindow + +class PopupWithListbox(popupclass): + + def __init__(self, parent, choices=[]): + popupclass.__init__(self, parent, wx.SIMPLE_BORDER) + + self.ListBox = wx.ListBox(self, -1, style=wx.LB_HSCROLL|wx.LB_SINGLE|wx.LB_SORT) + if not wx.Platform == '__WXMSW__': + self.ListBox.Bind(wx.EVT_LISTBOX, self.OnListBoxClick) + self.ListBox.Bind(wx.EVT_LISTBOX_DCLICK, self.OnListBoxClick) + + self.SetChoices(choices) + + self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) + + def SetChoices(self, choices): + max_text_width = 0 + max_text_height = 0 + + self.ListBox.Clear() + for choice in choices: + self.ListBox.Append(choice) + w, h = self.ListBox.GetTextExtent(choice) + max_text_width = max(max_text_width, w) + max_text_height = max(max_text_height, h) + + itemcount = min(len(choices), MAX_ITEM_SHOWN) + width = self.Parent.GetSize()[0] + height = max_text_height * itemcount + ITEM_INTERVAL_HEIGHT * (itemcount + 1) + if max_text_width + 10 > width: + height += 15 + size = wx.Size(width, height) + self.ListBox.SetSize(size) + self.SetClientSize(size) + + def MoveSelection(self, direction): + selected = self.ListBox.GetSelection() + if selected == wx.NOT_FOUND: + if direction >= 0: + selected = 0 + else: + selected = self.ListBox.GetCount() - 1 + else: + selected = (selected + direction) % (self.ListBox.GetCount() + 1) + if selected == self.ListBox.GetCount(): + selected = wx.NOT_FOUND + self.ListBox.SetSelection(selected) + + def GetSelection(self): + return self.ListBox.GetStringSelection() + + def ProcessLeftDown(self, event): + selected = self.ListBox.HitTest(wx.Point(event.m_x, event.m_y)) + if selected != wx.NOT_FOUND: + wx.CallAfter(self.Parent.SetValueFromSelected, self.ListBox.GetString(selected)) + return False + + def OnListBoxClick(self, event): + selected = event.GetSelection() + if selected != wx.NOT_FOUND: + wx.CallAfter(self.Parent.SetValueFromSelected, self.ListBox.GetString(selected)) + event.Skip() + + def OnKeyDown(self, event): + self.Parent.ProcessEvent(event) + + def OnDismiss(self): + self.Parent.listbox = None + wx.CallAfter(self.Parent.DismissListBox) + +class TextCtrlAutoComplete(wx.TextCtrl): + + def __init__ (self, parent, appframe, choices=None, dropDownClick=True, + element_path=None, **therest): + """ + Constructor works just like wx.TextCtrl except you can pass in a + list of choices. You can also change the choice list at any time + by calling setChoices. + """ + + therest['style'] = wx.TE_PROCESS_ENTER | therest.get('style', 0) + + wx.TextCtrl.__init__(self, parent, **therest) + self.AppFrame = appframe + + #Some variables + self._dropDownClick = dropDownClick + self._lastinsertionpoint = None + + self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y) + self.element_path = element_path + + self.listbox = None + + self.SetChoices(choices) + + #gp = self + #while ( gp != None ) : + # gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp ) + # gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp ) + # gp = gp.GetParent() + + self.Bind(wx.EVT_KILL_FOCUS, self.OnControlChanged) + self.Bind(wx.EVT_TEXT_ENTER, self.OnControlChanged) + self.Bind(wx.EVT_TEXT, self.OnEnteredText) + self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) + + #If need drop down on left click + if dropDownClick: + self.Bind(wx.EVT_LEFT_DOWN, self.OnClickToggleDown) + self.Bind(wx.EVT_LEFT_UP, self.OnClickToggleUp) + + def __del__(self): + self.AppFrame = None + + def ChangeValue(self, value): + wx.TextCtrl.ChangeValue(self, value) + self.RefreshListBoxChoices() + + def OnEnteredText(self, event): + wx.CallAfter(self.RefreshListBoxChoices) + event.Skip() + + def OnKeyDown(self, event): + """ Do some work when the user press on the keys: + up and down: move the cursor + """ + keycode = event.GetKeyCode() + if keycode in [wx.WXK_DOWN, wx.WXK_UP]: + self.PopupListBox() + if keycode == wx.WXK_DOWN: + self.listbox.MoveSelection(1) + else: + self.listbox.MoveSelection(-1) + elif keycode in [wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_RETURN] and self.listbox is not None: + self.SetValueFromSelected(self.listbox.GetSelection()) + elif event.GetKeyCode() == wx.WXK_ESCAPE: + self.DismissListBox() + else: + event.Skip() + + def OnClickToggleDown(self, event): + self._lastinsertionpoint = self.GetInsertionPoint() + event.Skip() + + def OnClickToggleUp(self, event): + if self.GetInsertionPoint() == self._lastinsertionpoint: + wx.CallAfter(self.PopupListBox) + self._lastinsertionpoint = None + event.Skip() + + def OnControlChanged(self, event): + res = self.GetValue() + config = wx.ConfigBase.Get() + listentries = cPickle.loads(str(config.Read(self.element_path, cPickle.dumps([])))) + if res and res not in listentries: + listentries = (listentries + [res])[-MAX_ITEM_COUNT:] + config.Write(self.element_path, cPickle.dumps(listentries)) + config.Flush() + self.SetChoices(listentries) + self.DismissListBox() + event.Skip() + + def SetChoices(self, choices): + self._choices = choices + self.RefreshListBoxChoices() + + def GetChoices(self): + return self._choices + + def SetValueFromSelected(self, selected): + """ + Sets the wx.TextCtrl value from the selected wx.ListCtrl item. + Will do nothing if no item is selected in the wx.ListCtrl. + """ + if selected != "": + self.SetValue(selected) + self.DismissListBox() + + def RefreshListBoxChoices(self): + if self.listbox is not None: + text = self.GetValue() + choices = [choice for choice in self._choices if choice.startswith(text)] + self.listbox.SetChoices(choices) + + def PopupListBox(self): + if self.listbox is None: + self.listbox = PopupWithListbox(self) + + # Show the popup right below or above the button + # depending on available screen space... + pos = self.ClientToScreen((0, 0)) + sz = self.GetSize() + self.listbox.Position(pos, (0, sz[1])) + + self.RefreshListBoxChoices() + + if wx.Platform == '__WXMSW__': + self.listbox.Popup() + else: + self.listbox.Show() + self.AppFrame.EnableScrolling(False) + + def DismissListBox(self): + if self.listbox is not None: + if wx.Platform == '__WXMSW__': + self.listbox.Dismiss() + else: + self.listbox.Destroy() + self.listbox = None + self.AppFrame.EnableScrolling(True) + diff -r 7ac746c07ff2 -r ea09f33ce717 i18n/Beremiz_fr_FR.po --- a/i18n/Beremiz_fr_FR.po Fri Oct 23 15:41:48 2009 +0200 +++ b/i18n/Beremiz_fr_FR.po Fri Oct 23 18:45:24 2009 +0200 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-10-09 16:27+0200\n" -"PO-Revision-Date: 2009-10-09 17:48+0100\n" +"POT-Creation-Date: 2009-10-23 18:41+0200\n" +"PO-Revision-Date: 2009-10-23 18:42+0100\n" "Last-Translator: \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ../Beremiz.py:1432 +#: ../Beremiz.py:1460 #, python-format msgid "" "\n" @@ -44,11 +44,11 @@ "\n" "Origine :\n" -#: ../plugger.py:1370 +#: ../plugger.py:1453 msgid " generation failed !\n" msgstr ": la construction a échouée !\n" -#: ../Beremiz.py:1332 +#: ../Beremiz.py:1351 #, python-format msgid "\"%s\" folder is not a valid Beremiz project\n" msgstr "Le dossier \"%s\" ne contient pas de projet Beremiz valide\n" @@ -57,48 +57,48 @@ msgid "&Edit" msgstr "&Editer" -#: ../Beremiz.py:1420 -#: ../Beremiz.py:1422 -#: ../Beremiz.py:1423 +#: ../Beremiz.py:1448 +#: ../Beremiz.py:1450 +#: ../Beremiz.py:1451 msgid ", " msgstr ", " -#: ../Beremiz.py:1418 +#: ../Beremiz.py:1446 msgid ". " msgstr ". " -#: ../plugger.py:432 +#: ../plugger.py:443 #, python-format msgid "A child names \"%s\" already exist -> \"%s\"\n" msgstr "" -#: ../plugger.py:464 +#: ../plugger.py:475 #, python-format msgid "A child with IEC channel %d already exist -> %d\n" msgstr "" -#: ../Beremiz.py:329 +#: ../Beremiz.py:332 msgid "About" msgstr "A propos" -#: ../Beremiz.py:1369 +#: ../Beremiz.py:1395 msgid "About Beremiz" msgstr "A propos de Beremiz" -#: ../Beremiz.py:1391 +#: ../Beremiz.py:1418 msgid "Add Plugin" msgstr "Ajouter un plugin" -#: ../Beremiz.py:585 -#: ../Beremiz.py:849 +#: ../Beremiz.py:603 +#: ../Beremiz.py:867 msgid "Add a sub plugin" msgstr "Ajouter un sous plugin" -#: ../plugger.py:1683 +#: ../plugger.py:1766 msgid "Already connected. Please disconnect\n" msgstr "Déjà connecté. Veuillez déconnecter\n" -#: ../Beremiz.py:1088 +#: ../Beremiz.py:1106 msgid "Append " msgstr "Ajouter " @@ -108,62 +108,62 @@ msgid "Bad location size : %s" msgstr "Mauvaise taille d'adresse : %s" -#: ../Beremiz.py:417 +#: ../Beremiz.py:426 msgid "Beremiz" msgstr "Beremiz" -#: ../Beremiz.py:327 +#: ../Beremiz.py:330 msgid "Beremiz\tF1" msgstr "Beremiz\tF1" -#: ../plugger.py:1466 +#: ../plugger.py:1549 msgid "Broken" msgstr "Cassé" -#: ../plugger.py:1807 +#: ../plugger.py:1890 msgid "Build" msgstr "Compiler" -#: ../plugger.py:1434 +#: ../plugger.py:1517 msgid "Build directory already clean\n" msgstr "Le répertoire de compilation est déjà nettoyé\n" -#: ../plugger.py:1808 +#: ../plugger.py:1891 msgid "Build project into build folder" msgstr "Compiler le projet dans le répertoire ce compilation" -#: ../plugger.py:1388 +#: ../plugger.py:1471 msgid "C Build crashed !\n" msgstr "La compilation du C a mal fonctionné !\n" -#: ../plugger.py:1385 +#: ../plugger.py:1468 msgid "C Build failed.\n" msgstr "La compilation du C a échouée !\n" -#: ../plugger.py:1374 +#: ../plugger.py:1457 msgid "C code generated successfully.\n" msgstr "Code C généré avec succès.\n" -#: ../targets/toolchain_gcc.py:125 +#: ../targets/toolchain_gcc.py:129 #, python-format msgid "C compilation of %s failed.\n" msgstr "La compilation C de %s a échouée.\n" -#: ../plugger.py:1117 +#: ../plugger.py:1200 #, python-format msgid "Can't find module for target %s!\n" msgstr "Impossible de trouver le module correspondant à la cible %s !\n" -#: ../plugger.py:1756 +#: ../plugger.py:1839 msgid "Cannot compare latest build to target. Please build.\n" msgstr "Impossible de comparer la cible avec la dernière compilation. Veuillez compiler le projet.\n" -#: ../plugger.py:502 +#: ../plugger.py:513 #, python-format msgid "Cannot create child %s of type %s " msgstr "Impossible d'ajouter un élément \"%s\" de type \"%s\"" -#: ../plugger.py:457 +#: ../plugger.py:468 #, python-format msgid "Cannot find lower free IEC channel than %d\n" msgstr "Impossible de trouver un numéro IEC inférieur à %d libre\n" @@ -172,7 +172,7 @@ msgid "Cannot get PLC status - connection failed.\n" msgstr "Impossible d'obtenir le statut de l'automate - la connexion a échoué.\n" -#: ../plugger.py:1215 +#: ../plugger.py:1298 msgid "Cannot open/parse VARIABLES.csv!\n" msgstr "Impossible d'ouvrir ou d'analyser le fichier VARIABLES.csv !\n" @@ -201,8 +201,12 @@ msgid "Choose a SVG file" msgstr "Choisissez un fichier SVG" -#: ../Beremiz.py:1289 -#: ../Beremiz.py:1314 +#: ../plugger.py:969 +msgid "Choose a directory to save project" +msgstr "Choisissez un dossier où enregistrer le projet" + +#: ../Beremiz.py:1308 +#: ../Beremiz.py:1333 msgid "Choose a project" msgstr "Choisissez un projet" @@ -210,48 +214,48 @@ msgid "Choose a working directory " msgstr "Choisissez un dossier de travail" -#: ../plugger.py:882 +#: ../plugger.py:927 msgid "Chosen folder doesn't contain a program. It's not a valid project!" msgstr "Le répertoire ne contient pas de programme. Ce n'est pas un projet valide !" -#: ../plugger.py:847 +#: ../plugger.py:892 msgid "Chosen folder isn't empty. You can't use it for a new project!" msgstr "Le répertoire n'est pas vide. Vous ne pouvez pas l'utiliser pour créer un nouveau projet !" -#: ../plugger.py:1811 +#: ../plugger.py:1894 msgid "Clean" msgstr "Nettoyer" -#: ../plugger.py:1813 +#: ../plugger.py:1896 msgid "Clean project build folder" msgstr "Nettoyer le répertoire de compilation" -#: ../plugger.py:1431 +#: ../plugger.py:1514 msgid "Cleaning the build directory\n" msgstr "Répertoire de compilation en cours de nettoyage\n" -#: ../Beremiz.py:483 -#: ../Beremiz.py:1341 +#: ../Beremiz.py:492 +#: ../Beremiz.py:1360 msgid "Close Application" msgstr "Fermer l'application" -#: ../Beremiz.py:299 +#: ../Beremiz.py:301 msgid "Close Project" msgstr "Fermer le projet" -#: ../Beremiz.py:297 +#: ../Beremiz.py:299 msgid "Close Tab\tCTRL+W" msgstr "Fermer l'onglet\tCTRL+W" -#: ../plugger.py:1039 +#: ../plugger.py:1122 msgid "Compiling IEC Program into C code...\n" msgstr "Compilation du program en IEC vers du code C en cours...\n" -#: ../plugger.py:1835 +#: ../plugger.py:1918 msgid "Connect" msgstr "Connecter" -#: ../plugger.py:1836 +#: ../plugger.py:1919 msgid "Connect to the target PLC" msgstr "Connecter à l'automate cible" @@ -260,23 +264,23 @@ msgid "Connecting to URI : %s\n" msgstr "Connection à l'URI %s en cours...\n" -#: ../plugger.py:1702 +#: ../plugger.py:1785 msgid "Connection canceled!\n" msgstr "La connection a été abandonnée !\n" -#: ../plugger.py:1719 +#: ../plugger.py:1802 #, python-format msgid "Connection failed to %s!\n" msgstr "La connection à \"%s\" a échouée !\n" -#: ../plugger.py:625 +#: ../plugger.py:634 #, python-format msgid "" "Could not add child \"%s\", type %s :\n" "%s\n" msgstr "" -#: ../plugger.py:602 +#: ../plugger.py:611 #, python-format msgid "" "Couldn't load plugin base parameters %s :\n" @@ -285,7 +289,7 @@ "Impossible de charger les paramètres de base du plugin %s :\n" " %s" -#: ../plugger.py:613 +#: ../plugger.py:622 #, python-format msgid "" "Couldn't load plugin parameters %s :\n" @@ -294,11 +298,11 @@ "Impossible de charger les paramètres du plugin %s :\n" " %s" -#: ../plugger.py:1647 +#: ../plugger.py:1730 msgid "Couldn't start PLC debug !\n" msgstr "Impossible d'arrêter le débogage de l'automate !\n" -#: ../plugger.py:1677 +#: ../plugger.py:1760 msgid "Couldn't stop PLC !\n" msgstr "Impossible d'arrêter l'automate !\n" @@ -306,54 +310,54 @@ msgid "Create HMI" msgstr "Créer une IHM" -#: ../plugger.py:1821 +#: ../plugger.py:1904 msgid "Debug" msgstr "Déboguer" -#: ../plugger.py:1520 +#: ../plugger.py:1603 #, python-format msgid "Debug : Unknown variable %s\n" msgstr "Débogage : variable \"%s\" inconnue\n" -#: ../plugger.py:1632 +#: ../plugger.py:1715 msgid "Debug Thread couldn't be killed" msgstr "Le thread de débogage n'a pu être détruit" -#: ../plugger.py:1616 +#: ../plugger.py:1699 #, python-format msgid "Debug data not coherent %d != %d\n" msgstr "Les données de débogage ne sont pas cohérentes %d != %d\n" -#: ../plugger.py:1624 +#: ../plugger.py:1707 msgid "Debugger disabled\n" msgstr "Débogueur désactivé\n" -#: ../Beremiz.py:840 +#: ../Beremiz.py:858 msgid "Delete this plugin" msgstr "Supprimer ce plugin" -#: ../plugger.py:1463 +#: ../plugger.py:1546 msgid "Dirty" msgstr "Corrompu" -#: ../plugger.py:1844 +#: ../plugger.py:1927 msgid "Disconnect" msgstr "Déconnecter" -#: ../plugger.py:1846 +#: ../plugger.py:1929 msgid "Disconnect from PLC" msgstr "Déconnecter l'automate" -#: ../plugger.py:1469 +#: ../plugger.py:1552 msgid "Disconnected" msgstr "Déconnecté" -#: ../plugins/c_ext/c_ext.py:236 -#: ../plugins/c_ext/c_ext.py:237 +#: ../plugins/c_ext/c_ext.py:250 +#: ../plugins/c_ext/c_ext.py:251 msgid "Edit C File" msgstr "Editer le fichier C" -#: ../plugins/canfestival/canfestival.py:217 +#: ../plugins/canfestival/canfestival.py:246 msgid "Edit CanOpen Network with NetworkEdit" msgstr "Editer le réseau CANOpen à l'aide de NetworkEdit" @@ -361,19 +365,19 @@ msgid "Edit a WxWidgets GUI with WXGlade" msgstr "Editer une IHM WxWidgets à l'aide de WXGlade" -#: ../plugins/canfestival/canfestival.py:216 +#: ../plugins/canfestival/canfestival.py:245 msgid "Edit network" msgstr "Editer le réseau" -#: ../plugger.py:1855 +#: ../plugger.py:1938 msgid "Edit raw IEC code added to code generated by PLCGenerator" msgstr "Editer le code IEC ajouté au code généré par PLCGenerator" -#: ../plugger.py:1460 +#: ../plugger.py:1543 msgid "Empty" msgstr "Vide" -#: ../Beremiz.py:790 +#: ../Beremiz.py:808 msgid "Enable/Disable this plugin" msgstr "Activer/Désactiver le plugin" @@ -389,23 +393,24 @@ msgid "Enter the IP of the interface to bind" msgstr "Saisissez l'adresse IP de l'interface à lier" -#: ../Beremiz.py:1446 -#: ../Beremiz.py:1456 +#: ../Beremiz.py:1474 +#: ../Beremiz.py:1484 +#: ../plugger.py:873 #: ../Beremiz_service.py:268 #: ../Beremiz_service.py:392 msgid "Error" msgstr "Erreur" -#: ../plugger.py:1087 +#: ../plugger.py:1170 msgid "Error : At least one configuration and one resource must be declared in PLC !\n" msgstr "Erreur : Au moins une configuration ou une ressource doit être déclarée dans l'automate !\n" -#: ../plugger.py:1079 +#: ../plugger.py:1162 #, python-format msgid "Error : IEC to C compiler returned %d\n" msgstr "Erreur : Le compilateur d'IEC en C a retourné %d\n" -#: ../plugger.py:1021 +#: ../plugger.py:1104 #, python-format msgid "" "Error in ST/IL/SFC code generator :\n" @@ -414,33 +419,33 @@ "Erreur dans le générateur de code ST/IL/SFC :\n" "%s\n" -#: ../plugger.py:207 +#: ../plugger.py:218 #, python-format msgid "Error while saving \"%s\"\n" msgstr "Erreur lors de l'enregistrement de \"%s\"\n" -#: ../plugins/canfestival/canfestival.py:208 +#: ../plugins/canfestival/canfestival.py:237 msgid "Error: No Master generated\n" msgstr "Erreur : Aucun maître généré\n" -#: ../plugins/canfestival/canfestival.py:203 +#: ../plugins/canfestival/canfestival.py:232 msgid "Error: No PLC built\n" msgstr "Erreur : Aucun automate compilé\n" -#: ../plugger.py:1713 +#: ../plugger.py:1796 #, python-format msgid "Exception while connecting %s!\n" msgstr "Une exception est apparu au cours de la connexion %s !\n" -#: ../plugger.py:1091 +#: ../plugger.py:1174 msgid "Extracting Located Variables...\n" msgstr "Extraction des variables adressées en cours...\n" -#: ../plugger.py:1771 +#: ../plugger.py:1854 msgid "Failed : Must build before transfer.\n" msgstr "Echec : Le projet doit être compilé avant d'être transféré.\n" -#: ../plugger.py:1379 +#: ../plugger.py:1462 msgid "Fatal : cannot get builder.\n" msgstr "Erreur fatale : impossible de trouver un compilateur.\n" @@ -448,15 +453,15 @@ msgid "Force runtime reload\n" msgstr "Redémarrage du runtime forcé\n" -#: ../plugger.py:1011 +#: ../plugger.py:1094 msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" msgstr "Création du code ST/IL/SFC de l'automate IEC-61131 en cours...\n" -#: ../plugger.py:1329 +#: ../plugger.py:1412 msgid "Generating plugins C code\n" msgstr "Création du code C des plugins en cours\n" -#: ../plugger.py:1321 +#: ../plugger.py:1404 msgid "IEC-61131-3 code generation failed !\n" msgstr "La création du code IEC-61131-3 a échouée !\n" @@ -480,15 +485,15 @@ msgid "Invalid type \"%s\"-> %d != %d for location\"%s\"" msgstr "Type invalide \"%s\"-> %d != %d pour cette adresse \"%s\"" -#: ../plugger.py:1777 +#: ../plugger.py:1860 msgid "Latest build already matches current target. Transfering anyway...\n" msgstr "La dernière compilation correspond à la cible actuelle...\n" -#: ../plugger.py:1747 +#: ../plugger.py:1830 msgid "Latest build does not match with target, please transfer.\n" msgstr "La dernière compilation ne correspond pas a la cible actuelle, veuillez transférer le programme.\n" -#: ../plugger.py:1751 +#: ../plugger.py:1834 msgid "Latest build matches target, no transfer needed.\n" msgstr "La dernière compilation correspond à la cible actuelle. il n'est pas nécessaire de transférer le programme.\n" @@ -500,7 +505,7 @@ msgid "Launch a live Python shell" msgstr "Lancer une console Python" -#: ../targets/toolchain_gcc.py:133 +#: ../targets/toolchain_gcc.py:137 msgid "Linking :\n" msgstr "Linkage :\n" @@ -508,11 +513,11 @@ msgid "Local" msgstr "Local" -#: ../Beremiz.py:376 +#: ../Beremiz.py:380 msgid "Log Console" msgstr "Console de log" -#: ../plugger.py:512 +#: ../plugger.py:523 #, python-format msgid "Max count (%d) reached for this plugin of type %s " msgstr "Nombre limite(%d) atteint pour les plugin de type %s" @@ -525,7 +530,7 @@ msgid "New\tCTRL+N" msgstr "Nouveau\tCTRL+N" -#: ../plugger.py:1801 +#: ../plugger.py:1884 msgid "No PLC to transfer (did build succeed ?)\n" msgstr "Aucun automate à transférer (la compilation a-t-elle réussi ?)\n" @@ -562,29 +567,49 @@ msgid "Open\tCTRL+O" msgstr "Ouvrir\tCTRL+O" -#: ../targets/toolchain_gcc.py:101 +#: ../plugins/c_ext/c_ext.py:230 +msgid "Open CFileEditor" +msgstr "Ouverture de CFileEditor" + +#: ../plugins/python/modules/svgui/svgui.py:105 +msgid "Open Inkscape" +msgstr "Ouverture de Inkscape" + +#: ../plugins/canfestival/canfestival.py:208 +msgid "Open NetworkEdit" +msgstr "Ouverture de NetworkEdit" + +#: ../plugins/canfestival/canfestival.py:108 +msgid "Open ObjDictEdit" +msgstr "Ouverture de ObjdictEdit" + +#: ../plugins/python/modules/wxglade_hmi/wxglade_hmi.py:107 +msgid "Open wxGlade" +msgstr "Ouverture de wxGlade" + +#: ../targets/toolchain_gcc.py:105 msgid "PLC :\n" msgstr "Automate :\n" -#: ../plugger.py:1489 -#: ../plugger.py:1733 +#: ../plugger.py:1572 +#: ../plugger.py:1816 #, python-format msgid "PLC is %s\n" msgstr "L'automate est dans l'état %s\n" -#: ../Beremiz.py:302 +#: ../Beremiz.py:304 msgid "Page Setup" msgstr "Mise en page..." -#: ../Beremiz.py:1391 +#: ../Beremiz.py:1418 msgid "Please enter a name for plugin:" msgstr "Saisissez un nom pour le plugin :" -#: ../targets/toolchain_gcc.py:99 +#: ../targets/toolchain_gcc.py:103 msgid "Plugin : " msgstr "Plugin :" -#: ../plugger.py:1335 +#: ../plugger.py:1418 msgid "Plugins code generation failed !\n" msgstr "La création du code des plugins a échoué !\n" @@ -596,29 +621,29 @@ msgid "Port number must be an integer!" msgstr "Le numéro de port doit être un entier !" -#: ../Beremiz.py:304 +#: ../Beremiz.py:306 msgid "Preview" msgstr "Aperçu avant impression" -#: ../Beremiz.py:306 +#: ../Beremiz.py:308 msgid "Print" msgstr "Imprimer" -#: ../plugger.py:856 +#: ../plugger.py:901 msgid "Project not created" msgstr "Le projet n'a pu être créé" -#: ../plugger.py:540 +#: ../plugger.py:549 #, python-format msgid "Project tree layout do not match plugin.xml %s!=%s " msgstr "L'organisation du projet ne correspond pas à plugin.xml %s!=%s" -#: ../Beremiz.py:309 +#: ../Beremiz.py:311 msgid "Properties" msgstr "Propriétés" #: ../plugins/python/PythonEditor.py:513 -#: ../plugins/python/PythonEditor.py:566 +#: ../plugins/python/PythonEditor.py:565 msgid "PythonEditor" msgstr "PythonEditor" @@ -626,15 +651,15 @@ msgid "Quit" msgstr "Quitter" -#: ../Beremiz.py:312 +#: ../Beremiz.py:314 msgid "Quit\tCTRL+Q" msgstr "Quitter\tCTRL+Q" -#: ../plugger.py:1854 +#: ../plugger.py:1937 msgid "Raw IEC code" msgstr "Ajout code IEC" -#: ../Beremiz.py:1400 +#: ../Beremiz.py:1428 msgid "Really delete plugin ?" msgstr "Voulez-vous réellement supprimer le plugin ?" @@ -650,11 +675,11 @@ msgid "Refresh\tCTRL+R" msgstr "Actualiser\tCTRL+R" -#: ../Beremiz.py:1400 +#: ../Beremiz.py:1428 msgid "Remove plugin" msgstr "Enlever le plugin" -#: ../plugger.py:1816 +#: ../plugger.py:1899 msgid "Run" msgstr "Exécuter" @@ -666,8 +691,12 @@ msgid "Save\tCTRL+S" msgstr "Enregistrer\tCTRL+S" -#: ../Beremiz.py:482 -#: ../Beremiz.py:1340 +#: ../Beremiz.py:297 +msgid "Save as\tCTRL+SHIFT+S" +msgstr "Enregistrer sous...\tCTRL+SHIFT+S" + +#: ../Beremiz.py:491 +#: ../Beremiz.py:1359 msgid "Save changes ?" msgstr "Enregistrer les changements ?" @@ -675,49 +704,49 @@ msgid "Services available:" msgstr "Services disponibles:" -#: ../plugger.py:1851 +#: ../plugger.py:1934 msgid "Show IEC code generated by PLCGenerator" msgstr "Afficher le code IEC généré par PLCGenerator" -#: ../plugins/canfestival/canfestival.py:220 +#: ../plugins/canfestival/canfestival.py:249 msgid "Show Master" msgstr "Afficher le maître" -#: ../plugins/canfestival/canfestival.py:221 +#: ../plugins/canfestival/canfestival.py:250 msgid "Show Master generated by config_utils" msgstr "Afficher le maître généré par config_utils" -#: ../plugger.py:1849 +#: ../plugger.py:1932 msgid "Show code" msgstr "Afficher le code" -#: ../plugger.py:1818 +#: ../plugger.py:1901 #: ../Beremiz_service.py:317 msgid "Start PLC" msgstr "Démarrer l'automate" -#: ../plugger.py:1823 +#: ../plugger.py:1906 msgid "Start PLC (debug mode)" msgstr "Démarrer l'automate (en mode debug)" -#: ../plugger.py:1313 +#: ../plugger.py:1396 #, python-format msgid "Start build in %s\n" msgstr "Début de la compilation dans %s\n" -#: ../plugger.py:1454 +#: ../plugger.py:1537 msgid "Started" msgstr "Démarré" -#: ../plugger.py:1451 +#: ../plugger.py:1534 msgid "Starting" msgstr "Démarrage" -#: ../plugger.py:1641 +#: ../plugger.py:1724 msgid "Starting PLC (debug mode)\n" msgstr "Démarrage de l'automate (en mode debug) en cours\n" -#: ../plugger.py:1830 +#: ../plugger.py:1913 msgid "Stop" msgstr "Arrêter" @@ -725,35 +754,35 @@ msgid "Stop PLC" msgstr "Arrêter l'automate" -#: ../plugger.py:1832 +#: ../plugger.py:1915 msgid "Stop Running PLC" msgstr "Arrêter l'automate en cours d'exécution" -#: ../plugger.py:1457 +#: ../plugger.py:1540 msgid "Stopped" msgstr "Arrêté" -#: ../plugger.py:1673 +#: ../plugger.py:1756 msgid "Stopping debug\n" msgstr "Arrêt du débogage en cours\n" -#: ../Beremiz.py:370 +#: ../Beremiz.py:374 msgid "Topology" msgstr "Topologie" -#: ../plugger.py:1839 +#: ../plugger.py:1922 msgid "Transfer" msgstr "Transférer" -#: ../plugger.py:1841 +#: ../plugger.py:1924 msgid "Transfer PLC" msgstr "Transférer l'automate" -#: ../plugger.py:1797 +#: ../plugger.py:1880 msgid "Transfer completed successfully.\n" msgstr "Transfert effectué avec succès.\n" -#: ../plugger.py:1799 +#: ../plugger.py:1882 msgid "Transfer failed\n" msgstr "Le transfert a échoué\n" @@ -785,7 +814,7 @@ msgid "WXGLADE GUI" msgstr "IHM WXGlade" -#: ../plugger.py:1016 +#: ../plugger.py:1099 msgid "Warnings in ST/IL/SFC code generator :\n" msgstr "Mises en garde du generateur de code ST/IL/SFC :\n" @@ -793,21 +822,69 @@ msgid "Wrong URI, please check it !\n" msgstr "URI inconnue, veuillez vérifier l'adresse !\n" -#: ../wxPopen.py:134 +#: ../plugins/c_ext/c_ext.py:229 +msgid "" +"You don't have write permissions.\n" +"Open CFileEditor anyway ?" +msgstr "" +"Vous n'avez pas les permissions d'écriture.\n" +"Ouvrir CFileEditor tout de même ?" + +#: ../plugins/python/modules/svgui/svgui.py:104 +msgid "" +"You don't have write permissions.\n" +"Open Inkscape anyway ?" +msgstr "" +"Vous n'avez pas les permissions d'écriture.\n" +"Ouvrir Inkscape tout de même ?" + +#: ../plugins/canfestival/canfestival.py:207 +msgid "" +"You don't have write permissions.\n" +"Open NetworkEdit anyway ?" +msgstr "" +"Vous n'avez pas les permissions d'écriture.\n" +"Ouvrir NetworkEdit tout de même ?" + +#: ../plugins/canfestival/canfestival.py:107 +msgid "" +"You don't have write permissions.\n" +"Open ObjDictEdit anyway ?" +msgstr "" +"Vous n'avez pas les permissions d'écriture.\n" +"Ouvrir ObjdictEdit tout de même ?" + +#: ../plugins/python/modules/wxglade_hmi/wxglade_hmi.py:106 +msgid "" +"You don't have write permissions.\n" +"Open wxGlade anyway ?" +msgstr "" +"Vous n'avez pas les permissions d'écriture.\n" +"Ouvrir wxGlade tout de même ?" + +#: ../plugger.py:872 +msgid "" +"You must have permission to work on the project\n" +"Work on a project copy ?" +msgstr "" +"Vous n'avez pas la permission de travailler sur le projet.\n" +"Travailler sur une copie du projet ?" + +#: ../wxPopen.py:145 #, python-format msgid "exited with status %s (pid %s)\n" msgstr "a quitté avec le status %s (pid %s)\n" -#: ../Beremiz.py:1420 -#: ../Beremiz.py:1422 +#: ../Beremiz.py:1448 +#: ../Beremiz.py:1450 msgid "file : " msgstr "fichier :" -#: ../Beremiz.py:1423 +#: ../Beremiz.py:1451 msgid "function : " msgstr "fonction :" -#: ../Beremiz.py:1423 +#: ../Beremiz.py:1451 msgid "line : " msgstr "ligne :" diff -r 7ac746c07ff2 -r ea09f33ce717 i18n/messages.pot --- a/i18n/messages.pot Fri Oct 23 15:41:48 2009 +0200 +++ b/i18n/messages.pot Fri Oct 23 18:45:24 2009 +0200 @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-10-09 16:27+0200\n" +"POT-Creation-Date: 2009-10-23 18:41+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,7 +16,7 @@ "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: ../Beremiz.py:1432 +#: ../Beremiz.py:1460 #, python-format msgid "" "\n" @@ -33,11 +33,11 @@ "Traceback:\n" msgstr "" -#: ../plugger.py:1370 +#: ../plugger.py:1453 msgid " generation failed !\n" msgstr "" -#: ../Beremiz.py:1332 +#: ../Beremiz.py:1351 #, python-format msgid "\"%s\" folder is not a valid Beremiz project\n" msgstr "" @@ -46,45 +46,45 @@ msgid "&Edit" msgstr "" -#: ../Beremiz.py:1420 ../Beremiz.py:1422 ../Beremiz.py:1423 +#: ../Beremiz.py:1448 ../Beremiz.py:1450 ../Beremiz.py:1451 msgid ", " msgstr "" +#: ../Beremiz.py:1446 +msgid ". " +msgstr "" + +#: ../plugger.py:443 +#, python-format +msgid "A child names \"%s\" already exist -> \"%s\"\n" +msgstr "" + +#: ../plugger.py:475 +#, python-format +msgid "A child with IEC channel %d already exist -> %d\n" +msgstr "" + +#: ../Beremiz.py:332 +msgid "About" +msgstr "" + +#: ../Beremiz.py:1395 +msgid "About Beremiz" +msgstr "" + #: ../Beremiz.py:1418 -msgid ". " -msgstr "" - -#: ../plugger.py:432 -#, python-format -msgid "A child names \"%s\" already exist -> \"%s\"\n" -msgstr "" - -#: ../plugger.py:464 -#, python-format -msgid "A child with IEC channel %d already exist -> %d\n" -msgstr "" - -#: ../Beremiz.py:329 -msgid "About" -msgstr "" - -#: ../Beremiz.py:1369 -msgid "About Beremiz" -msgstr "" - -#: ../Beremiz.py:1391 msgid "Add Plugin" msgstr "" -#: ../Beremiz.py:585 ../Beremiz.py:849 +#: ../Beremiz.py:603 ../Beremiz.py:867 msgid "Add a sub plugin" msgstr "" -#: ../plugger.py:1683 +#: ../plugger.py:1766 msgid "Already connected. Please disconnect\n" msgstr "" -#: ../Beremiz.py:1088 +#: ../Beremiz.py:1106 msgid "Append " msgstr "" @@ -94,62 +94,62 @@ msgid "Bad location size : %s" msgstr "" -#: ../Beremiz.py:417 +#: ../Beremiz.py:426 msgid "Beremiz" msgstr "" -#: ../Beremiz.py:327 +#: ../Beremiz.py:330 msgid "Beremiz\tF1" msgstr "" -#: ../plugger.py:1466 +#: ../plugger.py:1549 msgid "Broken" msgstr "" -#: ../plugger.py:1807 +#: ../plugger.py:1890 msgid "Build" msgstr "" -#: ../plugger.py:1434 +#: ../plugger.py:1517 msgid "Build directory already clean\n" msgstr "" -#: ../plugger.py:1808 +#: ../plugger.py:1891 msgid "Build project into build folder" msgstr "" -#: ../plugger.py:1388 +#: ../plugger.py:1471 msgid "C Build crashed !\n" msgstr "" -#: ../plugger.py:1385 +#: ../plugger.py:1468 msgid "C Build failed.\n" msgstr "" -#: ../plugger.py:1374 +#: ../plugger.py:1457 msgid "C code generated successfully.\n" msgstr "" -#: ../targets/toolchain_gcc.py:125 +#: ../targets/toolchain_gcc.py:129 #, python-format msgid "C compilation of %s failed.\n" msgstr "" -#: ../plugger.py:1117 +#: ../plugger.py:1200 #, python-format msgid "Can't find module for target %s!\n" msgstr "" -#: ../plugger.py:1756 +#: ../plugger.py:1839 msgid "Cannot compare latest build to target. Please build.\n" msgstr "" -#: ../plugger.py:502 +#: ../plugger.py:513 #, python-format msgid "Cannot create child %s of type %s " msgstr "" -#: ../plugger.py:457 +#: ../plugger.py:468 #, python-format msgid "Cannot find lower free IEC channel than %d\n" msgstr "" @@ -158,7 +158,7 @@ msgid "Cannot get PLC status - connection failed.\n" msgstr "" -#: ../plugger.py:1215 +#: ../plugger.py:1298 msgid "Cannot open/parse VARIABLES.csv!\n" msgstr "" @@ -187,7 +187,11 @@ msgid "Choose a SVG file" msgstr "" -#: ../Beremiz.py:1289 ../Beremiz.py:1314 +#: ../plugger.py:969 +msgid "Choose a directory to save project" +msgstr "" + +#: ../Beremiz.py:1308 ../Beremiz.py:1333 msgid "Choose a project" msgstr "" @@ -195,47 +199,47 @@ msgid "Choose a working directory " msgstr "" -#: ../plugger.py:882 +#: ../plugger.py:927 msgid "Chosen folder doesn't contain a program. It's not a valid project!" msgstr "" -#: ../plugger.py:847 +#: ../plugger.py:892 msgid "Chosen folder isn't empty. You can't use it for a new project!" msgstr "" -#: ../plugger.py:1811 +#: ../plugger.py:1894 msgid "Clean" msgstr "" -#: ../plugger.py:1813 +#: ../plugger.py:1896 msgid "Clean project build folder" msgstr "" -#: ../plugger.py:1431 +#: ../plugger.py:1514 msgid "Cleaning the build directory\n" msgstr "" -#: ../Beremiz.py:483 ../Beremiz.py:1341 +#: ../Beremiz.py:492 ../Beremiz.py:1360 msgid "Close Application" msgstr "" +#: ../Beremiz.py:301 +msgid "Close Project" +msgstr "" + #: ../Beremiz.py:299 -msgid "Close Project" -msgstr "" - -#: ../Beremiz.py:297 msgid "Close Tab\tCTRL+W" msgstr "" -#: ../plugger.py:1039 +#: ../plugger.py:1122 msgid "Compiling IEC Program into C code...\n" msgstr "" -#: ../plugger.py:1835 +#: ../plugger.py:1918 msgid "Connect" msgstr "" -#: ../plugger.py:1836 +#: ../plugger.py:1919 msgid "Connect to the target PLC" msgstr "" @@ -244,41 +248,41 @@ msgid "Connecting to URI : %s\n" msgstr "" -#: ../plugger.py:1702 +#: ../plugger.py:1785 msgid "Connection canceled!\n" msgstr "" -#: ../plugger.py:1719 +#: ../plugger.py:1802 #, python-format msgid "Connection failed to %s!\n" msgstr "" -#: ../plugger.py:625 +#: ../plugger.py:634 #, python-format msgid "" "Could not add child \"%s\", type %s :\n" "%s\n" msgstr "" -#: ../plugger.py:602 +#: ../plugger.py:611 #, python-format msgid "" "Couldn't load plugin base parameters %s :\n" " %s" msgstr "" -#: ../plugger.py:613 +#: ../plugger.py:622 #, python-format msgid "" "Couldn't load plugin parameters %s :\n" " %s" msgstr "" -#: ../plugger.py:1647 +#: ../plugger.py:1730 msgid "Couldn't start PLC debug !\n" msgstr "" -#: ../plugger.py:1677 +#: ../plugger.py:1760 msgid "Couldn't stop PLC !\n" msgstr "" @@ -286,53 +290,53 @@ msgid "Create HMI" msgstr "" -#: ../plugger.py:1821 +#: ../plugger.py:1904 msgid "Debug" msgstr "" -#: ../plugger.py:1520 +#: ../plugger.py:1603 #, python-format msgid "Debug : Unknown variable %s\n" msgstr "" -#: ../plugger.py:1632 +#: ../plugger.py:1715 msgid "Debug Thread couldn't be killed" msgstr "" -#: ../plugger.py:1616 +#: ../plugger.py:1699 #, python-format msgid "Debug data not coherent %d != %d\n" msgstr "" -#: ../plugger.py:1624 +#: ../plugger.py:1707 msgid "Debugger disabled\n" msgstr "" -#: ../Beremiz.py:840 +#: ../Beremiz.py:858 msgid "Delete this plugin" msgstr "" -#: ../plugger.py:1463 +#: ../plugger.py:1546 msgid "Dirty" msgstr "" -#: ../plugger.py:1844 +#: ../plugger.py:1927 msgid "Disconnect" msgstr "" -#: ../plugger.py:1846 +#: ../plugger.py:1929 msgid "Disconnect from PLC" msgstr "" -#: ../plugger.py:1469 +#: ../plugger.py:1552 msgid "Disconnected" msgstr "" -#: ../plugins/c_ext/c_ext.py:236 ../plugins/c_ext/c_ext.py:237 +#: ../plugins/c_ext/c_ext.py:250 ../plugins/c_ext/c_ext.py:251 msgid "Edit C File" msgstr "" -#: ../plugins/canfestival/canfestival.py:217 +#: ../plugins/canfestival/canfestival.py:246 msgid "Edit CanOpen Network with NetworkEdit" msgstr "" @@ -340,19 +344,19 @@ msgid "Edit a WxWidgets GUI with WXGlade" msgstr "" -#: ../plugins/canfestival/canfestival.py:216 +#: ../plugins/canfestival/canfestival.py:245 msgid "Edit network" msgstr "" -#: ../plugger.py:1855 +#: ../plugger.py:1938 msgid "Edit raw IEC code added to code generated by PLCGenerator" msgstr "" -#: ../plugger.py:1460 +#: ../plugger.py:1543 msgid "Empty" msgstr "" -#: ../Beremiz.py:790 +#: ../Beremiz.py:808 msgid "Enable/Disable this plugin" msgstr "" @@ -368,54 +372,54 @@ msgid "Enter the IP of the interface to bind" msgstr "" -#: ../Beremiz.py:1446 ../Beremiz.py:1456 ../Beremiz_service.py:268 -#: ../Beremiz_service.py:392 +#: ../Beremiz.py:1474 ../Beremiz.py:1484 ../plugger.py:873 +#: ../Beremiz_service.py:268 ../Beremiz_service.py:392 msgid "Error" msgstr "" -#: ../plugger.py:1087 +#: ../plugger.py:1170 msgid "Error : At least one configuration and one resource must be declared in PLC !\n" msgstr "" -#: ../plugger.py:1079 +#: ../plugger.py:1162 #, python-format msgid "Error : IEC to C compiler returned %d\n" msgstr "" -#: ../plugger.py:1021 +#: ../plugger.py:1104 #, python-format msgid "" "Error in ST/IL/SFC code generator :\n" "%s\n" msgstr "" -#: ../plugger.py:207 +#: ../plugger.py:218 #, python-format msgid "Error while saving \"%s\"\n" msgstr "" -#: ../plugins/canfestival/canfestival.py:208 +#: ../plugins/canfestival/canfestival.py:237 msgid "Error: No Master generated\n" msgstr "" -#: ../plugins/canfestival/canfestival.py:203 +#: ../plugins/canfestival/canfestival.py:232 msgid "Error: No PLC built\n" msgstr "" -#: ../plugger.py:1713 +#: ../plugger.py:1796 #, python-format msgid "Exception while connecting %s!\n" msgstr "" -#: ../plugger.py:1091 +#: ../plugger.py:1174 msgid "Extracting Located Variables...\n" msgstr "" -#: ../plugger.py:1771 +#: ../plugger.py:1854 msgid "Failed : Must build before transfer.\n" msgstr "" -#: ../plugger.py:1379 +#: ../plugger.py:1462 msgid "Fatal : cannot get builder.\n" msgstr "" @@ -423,15 +427,15 @@ msgid "Force runtime reload\n" msgstr "" -#: ../plugger.py:1011 +#: ../plugger.py:1094 msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" msgstr "" -#: ../plugger.py:1329 +#: ../plugger.py:1412 msgid "Generating plugins C code\n" msgstr "" -#: ../plugger.py:1321 +#: ../plugger.py:1404 msgid "IEC-61131-3 code generation failed !\n" msgstr "" @@ -454,15 +458,15 @@ msgid "Invalid type \"%s\"-> %d != %d for location\"%s\"" msgstr "" -#: ../plugger.py:1777 +#: ../plugger.py:1860 msgid "Latest build already matches current target. Transfering anyway...\n" msgstr "" -#: ../plugger.py:1747 +#: ../plugger.py:1830 msgid "Latest build does not match with target, please transfer.\n" msgstr "" -#: ../plugger.py:1751 +#: ../plugger.py:1834 msgid "Latest build matches target, no transfer needed.\n" msgstr "" @@ -474,7 +478,7 @@ msgid "Launch a live Python shell" msgstr "" -#: ../targets/toolchain_gcc.py:133 +#: ../targets/toolchain_gcc.py:137 msgid "Linking :\n" msgstr "" @@ -482,11 +486,11 @@ msgid "Local" msgstr "" -#: ../Beremiz.py:376 +#: ../Beremiz.py:380 msgid "Log Console" msgstr "" -#: ../plugger.py:512 +#: ../plugger.py:523 #, python-format msgid "Max count (%d) reached for this plugin of type %s " msgstr "" @@ -499,7 +503,7 @@ msgid "New\tCTRL+N" msgstr "" -#: ../plugger.py:1801 +#: ../plugger.py:1884 msgid "No PLC to transfer (did build succeed ?)\n" msgstr "" @@ -536,28 +540,48 @@ msgid "Open\tCTRL+O" msgstr "" -#: ../targets/toolchain_gcc.py:101 +#: ../plugins/c_ext/c_ext.py:230 +msgid "Open CFileEditor" +msgstr "" + +#: ../plugins/python/modules/svgui/svgui.py:105 +msgid "Open Inkscape" +msgstr "" + +#: ../plugins/canfestival/canfestival.py:208 +msgid "Open NetworkEdit" +msgstr "" + +#: ../plugins/canfestival/canfestival.py:108 +msgid "Open ObjDictEdit" +msgstr "" + +#: ../plugins/python/modules/wxglade_hmi/wxglade_hmi.py:107 +msgid "Open wxGlade" +msgstr "" + +#: ../targets/toolchain_gcc.py:105 msgid "PLC :\n" msgstr "" -#: ../plugger.py:1489 ../plugger.py:1733 +#: ../plugger.py:1572 ../plugger.py:1816 #, python-format msgid "PLC is %s\n" msgstr "" -#: ../Beremiz.py:302 +#: ../Beremiz.py:304 msgid "Page Setup" msgstr "" -#: ../Beremiz.py:1391 +#: ../Beremiz.py:1418 msgid "Please enter a name for plugin:" msgstr "" -#: ../targets/toolchain_gcc.py:99 +#: ../targets/toolchain_gcc.py:103 msgid "Plugin : " msgstr "" -#: ../plugger.py:1335 +#: ../plugger.py:1418 msgid "Plugins code generation failed !\n" msgstr "" @@ -569,28 +593,28 @@ msgid "Port number must be an integer!" msgstr "" -#: ../Beremiz.py:304 +#: ../Beremiz.py:306 msgid "Preview" msgstr "" -#: ../Beremiz.py:306 +#: ../Beremiz.py:308 msgid "Print" msgstr "" -#: ../plugger.py:856 +#: ../plugger.py:901 msgid "Project not created" msgstr "" -#: ../plugger.py:540 +#: ../plugger.py:549 #, python-format msgid "Project tree layout do not match plugin.xml %s!=%s " msgstr "" -#: ../Beremiz.py:309 +#: ../Beremiz.py:311 msgid "Properties" msgstr "" -#: ../plugins/python/PythonEditor.py:513 ../plugins/python/PythonEditor.py:566 +#: ../plugins/python/PythonEditor.py:513 ../plugins/python/PythonEditor.py:565 msgid "PythonEditor" msgstr "" @@ -598,15 +622,15 @@ msgid "Quit" msgstr "" -#: ../Beremiz.py:312 +#: ../Beremiz.py:314 msgid "Quit\tCTRL+Q" msgstr "" -#: ../plugger.py:1854 +#: ../plugger.py:1937 msgid "Raw IEC code" msgstr "" -#: ../Beremiz.py:1400 +#: ../Beremiz.py:1428 msgid "Really delete plugin ?" msgstr "" @@ -622,11 +646,11 @@ msgid "Refresh\tCTRL+R" msgstr "" -#: ../Beremiz.py:1400 +#: ../Beremiz.py:1428 msgid "Remove plugin" msgstr "" -#: ../plugger.py:1816 +#: ../plugger.py:1899 msgid "Run" msgstr "" @@ -638,7 +662,11 @@ msgid "Save\tCTRL+S" msgstr "" -#: ../Beremiz.py:482 ../Beremiz.py:1340 +#: ../Beremiz.py:297 +msgid "Save as\tCTRL+SHIFT+S" +msgstr "" + +#: ../Beremiz.py:491 ../Beremiz.py:1359 msgid "Save changes ?" msgstr "" @@ -646,48 +674,48 @@ msgid "Services available:" msgstr "" -#: ../plugger.py:1851 +#: ../plugger.py:1934 msgid "Show IEC code generated by PLCGenerator" msgstr "" -#: ../plugins/canfestival/canfestival.py:220 +#: ../plugins/canfestival/canfestival.py:249 msgid "Show Master" msgstr "" -#: ../plugins/canfestival/canfestival.py:221 +#: ../plugins/canfestival/canfestival.py:250 msgid "Show Master generated by config_utils" msgstr "" -#: ../plugger.py:1849 +#: ../plugger.py:1932 msgid "Show code" msgstr "" -#: ../plugger.py:1818 ../Beremiz_service.py:317 +#: ../plugger.py:1901 ../Beremiz_service.py:317 msgid "Start PLC" msgstr "" -#: ../plugger.py:1823 +#: ../plugger.py:1906 msgid "Start PLC (debug mode)" msgstr "" -#: ../plugger.py:1313 +#: ../plugger.py:1396 #, python-format msgid "Start build in %s\n" msgstr "" -#: ../plugger.py:1454 +#: ../plugger.py:1537 msgid "Started" msgstr "" -#: ../plugger.py:1451 +#: ../plugger.py:1534 msgid "Starting" msgstr "" -#: ../plugger.py:1641 +#: ../plugger.py:1724 msgid "Starting PLC (debug mode)\n" msgstr "" -#: ../plugger.py:1830 +#: ../plugger.py:1913 msgid "Stop" msgstr "" @@ -695,35 +723,35 @@ msgid "Stop PLC" msgstr "" -#: ../plugger.py:1832 +#: ../plugger.py:1915 msgid "Stop Running PLC" msgstr "" -#: ../plugger.py:1457 +#: ../plugger.py:1540 msgid "Stopped" msgstr "" -#: ../plugger.py:1673 +#: ../plugger.py:1756 msgid "Stopping debug\n" msgstr "" -#: ../Beremiz.py:370 +#: ../Beremiz.py:374 msgid "Topology" msgstr "" -#: ../plugger.py:1839 +#: ../plugger.py:1922 msgid "Transfer" msgstr "" -#: ../plugger.py:1841 +#: ../plugger.py:1924 msgid "Transfer PLC" msgstr "" -#: ../plugger.py:1797 +#: ../plugger.py:1880 msgid "Transfer completed successfully.\n" msgstr "" -#: ../plugger.py:1799 +#: ../plugger.py:1882 msgid "Transfer failed\n" msgstr "" @@ -755,7 +783,7 @@ msgid "WXGLADE GUI" msgstr "" -#: ../plugger.py:1016 +#: ../plugger.py:1099 msgid "Warnings in ST/IL/SFC code generator :\n" msgstr "" @@ -763,20 +791,56 @@ msgid "Wrong URI, please check it !\n" msgstr "" -#: ../wxPopen.py:134 +#: ../plugins/c_ext/c_ext.py:229 +msgid "" +"You don't have write permissions.\n" +"Open CFileEditor anyway ?" +msgstr "" + +#: ../plugins/python/modules/svgui/svgui.py:104 +msgid "" +"You don't have write permissions.\n" +"Open Inkscape anyway ?" +msgstr "" + +#: ../plugins/canfestival/canfestival.py:207 +msgid "" +"You don't have write permissions.\n" +"Open NetworkEdit anyway ?" +msgstr "" + +#: ../plugins/canfestival/canfestival.py:107 +msgid "" +"You don't have write permissions.\n" +"Open ObjDictEdit anyway ?" +msgstr "" + +#: ../plugins/python/modules/wxglade_hmi/wxglade_hmi.py:106 +msgid "" +"You don't have write permissions.\n" +"Open wxGlade anyway ?" +msgstr "" + +#: ../plugger.py:872 +msgid "" +"You must have permission to work on the project\n" +"Work on a project copy ?" +msgstr "" + +#: ../wxPopen.py:145 #, python-format msgid "exited with status %s (pid %s)\n" msgstr "" -#: ../Beremiz.py:1420 ../Beremiz.py:1422 +#: ../Beremiz.py:1448 ../Beremiz.py:1450 msgid "file : " msgstr "" -#: ../Beremiz.py:1423 +#: ../Beremiz.py:1451 msgid "function : " msgstr "" -#: ../Beremiz.py:1423 +#: ../Beremiz.py:1451 msgid "line : " msgstr "" diff -r 7ac746c07ff2 -r ea09f33ce717 locale/fr_FR/LC_MESSAGES/Beremiz.mo Binary file locale/fr_FR/LC_MESSAGES/Beremiz.mo has changed diff -r 7ac746c07ff2 -r ea09f33ce717 plugger.py --- a/plugger.py Fri Oct 23 15:41:48 2009 +0200 +++ b/plugger.py Fri Oct 23 18:45:24 2009 +0200 @@ -869,7 +869,7 @@ if CheckPathPerm(self.ProjectPath): return True dialog = wx.MessageDialog(self.AppFrame, - _('You must have write permission to work on the project\nWork on a project copy ?'), + _('You must have permission to work on the project\nWork on a project copy ?'), _('Error'), wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) answer = dialog.ShowModal()