diff --git a/forms.py b/forms.py index d33f9ed..89037d6 100644 --- a/forms.py +++ b/forms.py @@ -6,8 +6,13 @@ import wx.grid import wx.propgrid as wxpg import wx.lib.plot as wxplot import wx.lib.agw.aui as aui +import gettext +import json +import os # import wx.aui as aui +_ = gettext.gettext + ID_NEW = wx.NewId() ID_SAVE = wx.NewId() ID_OPEN = wx.NewId() @@ -29,6 +34,9 @@ ID_ADD_PLOT = wx.NewId() ID_ADD_CURVES = wx.NewId() ID_ADD_MARKERS = wx.NewId() +ID_ENGLISH_LANG = wx.NewId() +ID_RUSSIAN_LANG = wx.NewId() + ID_ABOUT = wx.NewId() ID_EXPORT_CSV = wx.NewId() @@ -75,6 +83,20 @@ class MainFrame(wx.Frame): def __init__(self, parent): wx.Frame.__init__ (self, parent, title = 'Opal', size = wx.Size(873,594)) + self.settings = {} + self.LoadSettings() + + lang = self.settings['locale'] + if not lang: + locale = wx.Locale(wx.LANGUAGE_DEFAULT) + lang = locale.GetCanonicalName() + self.settings['locale'] = lang + Lang = gettext.translation('opal', './locale', languages=[lang], fallback=True) + Lang.install(unicode=True) + global _ + _ = Lang.ugettext + self.gettext = Lang.ugettext + self.auimgr = aui.AuiManager() self.auimgr.SetManagedWindow(self) self.auimgr.GetArtProvider().SetMetric(aui.AUI_DOCKART_SASH_SIZE, 3) @@ -89,7 +111,7 @@ class MainFrame(wx.Frame): # self.m_specs.SetMinSize(wx.Size(200,-1)) self.auimgr.AddPane(self.m_specs, - aui.AuiPaneInfo().Name("m_specs").Caption("Templates"). + aui.AuiPaneInfo().Name("m_specs").Caption(_("Templates")). Left().Layer(1).CloseButton(False)) # Пользовательские модели @@ -97,15 +119,15 @@ class MainFrame(wx.Frame): self.m_user_models = TreeListCtrl(self, size = (200, -1), style = wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.TR_FULL_ROW_HIGHLIGHT | wx.TR_ROW_LINES | wx.TR_MULTIPLE) - self.m_user_models.AddColumn("Model name") - self.m_user_models.AddColumn("Status") - self.m_user_models.AddColumn("Progress") - self.m_user_models.AddColumn("Comment") + self.m_user_models.AddColumn(_("Model name")) + self.m_user_models.AddColumn(_("Status")) + self.m_user_models.AddColumn(_("Progress")) + self.m_user_models.AddColumn(_("Comment")) self.m_user_models.SetMainColumn(0) self.m_user_models.SetImageList(self.ilist) self.auimgr.AddPane(self.m_user_models, - aui.AuiPaneInfo().Name("m_user_models").Caption("Models"). + aui.AuiPaneInfo().Name("m_user_models").Caption(_("Models")). CenterPane().Position(1)) # Параметры модели @@ -113,7 +135,7 @@ class MainFrame(wx.Frame): self.m_params = PropertyCtrl(self, size = (-1, 300)) self.auimgr.AddPane(self.m_params, - aui.AuiPaneInfo().Name("m_params").Caption("Parameters").CloseButton(False). + aui.AuiPaneInfo().Name("m_params").Caption(_("Parameters")).CloseButton(False). CenterPane().Bottom().Position(2)) # Быстрые результаты @@ -121,7 +143,7 @@ class MainFrame(wx.Frame): self.m_quick_result = PropertyCtrl(self, size = (200, -1)) self.auimgr.AddPane(self.m_quick_result, - aui.AuiPaneInfo().Name("m_quick_result").Caption("Quick results").CloseButton(False). + aui.AuiPaneInfo().Name("m_quick_result").Caption(_("Quick results")).CloseButton(False). Right().Position(1).Layer(1)) # Графики @@ -131,7 +153,7 @@ class MainFrame(wx.Frame): self.m_plots.SetImageList(self.ilist) self.auimgr.AddPane(self.m_plots, - aui.AuiPaneInfo().Name("m_plots").Caption("Plots").CloseButton(False). + aui.AuiPaneInfo().Name("m_plots").Caption(_("Plots")).CloseButton(False). Right().Position(2).Layer(1)) # Меню, панель инструментов и панель статуса @@ -145,6 +167,11 @@ class MainFrame(wx.Frame): self.BuildToolBar() + layout = self.settings['layout'] + if layout: + self.auimgr.LoadPerspective(layout, False) + print 'layout loaded' + self.auimgr.Update() def LoadIcons(self): @@ -168,62 +195,74 @@ class MainFrame(wx.Frame): menubar = wx.MenuBar() menu = wx.Menu() - menu.Append(ID_NEW, "&New\tCtrl+N") - menu.Append(ID_OPEN, "&Open\tCtrl+O") - menu.Append(ID_SAVE, "&Save\tCtrl+S") - menubar.Append(menu, '&Project') + menu.Append(ID_NEW, _("&New\tCtrl+N")) + menu.Append(ID_OPEN, _("&Open\tCtrl+O")) + menu.Append(ID_SAVE, _("&Save\tCtrl+S")) + menubar.Append(menu, _('&Project')) menu = wx.Menu() - menu.Append(ID_ADD_MODEL_ROOT, 'Add model to root') - menu.Append(ID_ADD_MODEL_SELECTED, 'Append model to selected') + menu.Append(ID_ADD_MODEL_ROOT, _('Add model to root')) + menu.Append(ID_ADD_MODEL_SELECTED, _('Append model to selected')) menu.AppendSeparator() - menu.Append(ID_DUPLICATE_MODEL, "&Duplicate\tCtrl+D") - menu.Append(ID_DUPLICATE_TREE, "&Duplicate with subitems\tCtrl+Shift+D") - menu.Append(ID_DELETE_MODEL, 'Delete\tCtrl+E') + menu.Append(ID_DUPLICATE_MODEL, _("&Duplicate\tCtrl+D")) + menu.Append(ID_DUPLICATE_TREE, _("&Duplicate with subitems\tCtrl+Shift+D")) + menu.Append(ID_DELETE_MODEL, _('Delete\tCtrl+E')) menu.AppendSeparator() - menu.Append(ID_TEST, "&Test\tCtrl+T") - menubar.Append(menu, '&Model') + menu.Append(ID_TEST, _("&Test\tCtrl+T")) + menubar.Append(menu, _('&Model')) menu = wx.Menu() - menu.Append(ID_PROCESS_MODEL, 'Process\tF5') - menu.Append(ID_STOP_MODEL, 'Stop\tF6') + menu.Append(ID_PROCESS_MODEL, _('Process\tF5')) + menu.Append(ID_STOP_MODEL, _('Stop\tF6')) #menu.AppendSeparator() - menubar.Append(menu, '&Run') + menubar.Append(menu, _('&Run')) menu = wx.Menu() - menu.Append(ID_SHOW_RESULT, 'Show report\tF7') + menu.Append(ID_SHOW_RESULT, _('Show report\tF7')) menu.AppendSeparator() - menu.Append(ID_SHOW_PLOT, 'Show plot\tF8') - menu.Append(ID_ADD_PLOT, 'Add plot') - #menu.Append(ID_ADD_LINE, 'Add line') - menubar.Append(menu, '&Result') + menu.Append(ID_SHOW_PLOT, _('Show plot\tF8')) + menu.Append(ID_ADD_PLOT, _('Add plot')) + #menu.Append(ID_ADD_LINE, _('Add line')) + menubar.Append(menu, _('&Result')) menu = wx.Menu() - menu.Append(ID_ABOUT, "&About\tF1") - menubar.Append(menu, '&Help') + submenu = wx.Menu() + submenu.Append(ID_ENGLISH_LANG, _('English')) + submenu.Append(ID_RUSSIAN_LANG, _('Russian')) + menu.AppendSubMenu(submenu, _('Language')) + menu.AppendSeparator() + # menu.Append(ID_SHOW_PLOT, _('Layout')) + # menu.Append(ID_ADD_PLOT, _('Options')) + #menu.Append(ID_ADD_LINE, _('Add line')) + menubar.Append(menu, _('&Settings')) + + + menu = wx.Menu() + menu.Append(ID_ABOUT, _("&About\tF1")) + menubar.Append(menu, _('&Help')) return menubar def BuildContextMenu(self): menu = wx.Menu() - menu.Append(ID_ADD_MODEL_ROOT, 'Add model to root') - menu.Append(ID_ADD_MODEL_SELECTED, 'Add model to selected') + menu.Append(ID_ADD_MODEL_ROOT, _('Add model to root')) + menu.Append(ID_ADD_MODEL_SELECTED, _('Add model to selected')) self.m_specs.Bind(wx.EVT_CONTEXT_MENU, lambda x: self.m_specs.PopupMenu(menu)) menu1 = wx.Menu() - menu1.Append(ID_ADD_PLOT, 'Add plot') + menu1.Append(ID_ADD_PLOT, _('Add plot')) menu1.AppendSeparator() - menu1.Append(ID_ADD_CURVES, 'Add curves') - menu1.Append(ID_ADD_MARKERS, 'Add markers') + menu1.Append(ID_ADD_CURVES, _('Add curves')) + menu1.Append(ID_ADD_MARKERS, _('Add markers')) self.m_plots.Bind(wx.EVT_CONTEXT_MENU, lambda x: self.m_plots.PopupMenu(menu1)) menu2 = wx.Menu() - menu2.Append(ID_SHOW_RESULT, 'Show report') + menu2.Append(ID_SHOW_RESULT, _('Show report')) menu2.AppendSeparator() - menu2.Append(ID_ADD_CURVES, 'Add curves') - menu2.Append(ID_ADD_MARKERS, 'Add markers') + menu2.Append(ID_ADD_CURVES, _('Add curves')) + menu2.Append(ID_ADD_MARKERS, _('Add markers')) self.m_user_models.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, lambda x: self.m_user_models.PopupMenu(menu2)) @@ -233,34 +272,52 @@ class MainFrame(wx.Frame): tb1.SetToolBitmapSize(wx.Size(16, 16)) tb1.AddSimpleTool(ID_ADD_MODEL_SELECTED, "model-new", wx.Bitmap('share/model-add.png'), - 'Add specification to selected model') + _('Add specification to selected model')) tb1.AddSimpleTool(ID_DUPLICATE_MODEL, "model-dup", wx.Bitmap('share/model-dup.png'), - 'Duplicate selected model') + _('Duplicate selected model')) tb1.AddSimpleTool(ID_DUPLICATE_TREE, "model-dup-tree", wx.Bitmap('share/model-dup-tree.png'), - 'Duplicate selected model and all submodels') + _('Duplicate selected model and all submodels')) tb1.AddSimpleTool(ID_DELETE_MODEL, "model-del", wx.Bitmap('share/model-delete.png'), - 'Delete selected model') + _('Delete selected model')) tb1.AddSeparator() tb1.AddSimpleTool(ID_PROCESS_MODEL, "model-go", wx.Bitmap('share/model-go.png'), - 'Start processing of selected models') + _('Start processing of selected models')) tb1.AddSimpleTool(ID_STOP_MODEL, "model-stop", wx.Bitmap('share/model-cancel.png'), - 'Stop processing of selected models') + _('Stop processing of selected models')) tb1.AddSeparator() tb1.AddSimpleTool(ID_SHOW_PLOT, "plot-quick", wx.Bitmap('share/plot-line.png'), - 'Show quick plot for selected model') + _('Show quick plot for selected model')) tb1.AddSimpleTool(ID_SHOW_RESULT, "report-show", wx.Bitmap('share/report-show.png'), - 'Show result data and table for selected model') + _('Show result data and table for selected model')) tb1.AddSeparator() tb1.AddSimpleTool(ID_ABOUT, "app-about", wx.Bitmap('share/app-about.png'), - 'Show infomation about application') + _('Show infomation about application')) tb1.Realize() - self.auimgr.AddPane(tb1, aui.AuiPaneInfo().Name("tb1").Caption("Toolbar"). + self.auimgr.AddPane(tb1, aui.AuiPaneInfo().Name("tb1").Caption(_("Toolbar")). ToolbarPane().Left().Floatable(False).Movable(False).Gripper(False)) + def SaveSettings(self): + self.settings['layout'] = self.auimgr.SavePerspective() + with open('settings.conf', 'w') as f: + json.dump(self.settings, f, indent = 2) + + def LoadSettings(self): + default = { + 'workers': 2, + 'conf': 'tasks.conf', + 'locale': None, + 'layout': None, + } + + self.settings = default + if os.path.exists('settings.conf'): + with open('settings.conf', 'r') as f: + self.settings.update(json.load(f)) + class SelectModelDialog(wx.Dialog): def __init__(self, parent): - wx.Dialog.__init__(self, parent, -1, 'Select model') + wx.Dialog.__init__(self, parent, -1, _('Select model')) sizer = wx.BoxSizer(wx.VERTICAL) self.mlist = wx.ListCtrl(self, style = wx.LC_ICON | wx.LC_SINGLE_SEL) @@ -301,9 +358,9 @@ class ResultFrame(wx.Frame): menubar = wx.MenuBar() menu = wx.Menu() - menu.Append(ID_EXPORT_CSV, 'CSV\tCtrl+E') + menu.Append(ID_EXPORT_CSV, _('CSV\tCtrl+E')) #menu.Append(wx.NewId(), 'TeX') - menubar.Append(menu, 'Export to') + menubar.Append(menu, _('Export to')) return menubar @@ -331,7 +388,7 @@ class LineSelectDialog(wx.Dialog): class SizeSelector(wx.Dialog): def __init__(self, parent): - wx.Dialog.__init__(self, parent, -1, 'Image size', size = (200, 100)) + wx.Dialog.__init__(self, parent, -1, _('Image size'), size = (200, 100)) bSizer = wx.BoxSizer(wx.HORIZONTAL) @@ -387,8 +444,8 @@ class PlotFrame(wx.Frame): menubar = wx.MenuBar() menu = wx.Menu() - menu.Append(ID_SAVE_PLOT, 'Save to file\tCtrl+S') - menubar.Append(menu, 'Plot') + menu.Append(ID_SAVE_PLOT, _('Save to file\tCtrl+S')) + menubar.Append(menu, _('Plot')) self.SetMenuBar(menubar) self.plot.canvas.Bind(wx.EVT_MOUSEWHEEL, self.OnZoom) @@ -419,7 +476,7 @@ class PlotFrame(wx.Frame): class AboutDialog(wx.Dialog): def __init__(self, parent): - wx.Dialog.__init__(self, parent, title = 'About Opal', size = (300, 330)) + wx.Dialog.__init__(self, parent, title = _('About Opal'), size = (300, 330)) title = 'Opal System' version = 'Aurora version' diff --git a/locale/ru_RU/LC_MESSAGES/opal.mo b/locale/ru_RU/LC_MESSAGES/opal.mo new file mode 100644 index 0000000..d43e350 Binary files /dev/null and b/locale/ru_RU/LC_MESSAGES/opal.mo differ diff --git a/opal.py b/opal.py index cebf81d..6ba1bab 100644 --- a/opal.py +++ b/opal.py @@ -27,6 +27,9 @@ import json import zlib from pprint import pprint +import gettext +_ = gettext.gettext + # состояния модели, унаследованные от состояния задачи MODEL_READY = server.JOB_READY MODEL_RUNNING = server.JOB_RUNNING @@ -133,10 +136,15 @@ class MainFrame(forms.MainFrame): def __init__(self): forms.MainFrame.__init__(self, None) + global _ + _ = self.gettext + self.model = None self.name_id = 1 - s = server.LocalServer() + conf = self.settings['conf'] + workers = int(self.settings['workers']) + s = server.LocalServer(conf=conf, workers=workers) s.LoadModels() self.models = s.GetModels() s.Start() @@ -200,6 +208,11 @@ class MainFrame(forms.MainFrame): self.Bind(wx.EVT_MENU, self.OnAddMarkers, id = forms.ID_ADD_MARKERS) + self.Bind(wx.EVT_MENU, lambda x: self.ChangeLocale('en_EN'), + id = forms.ID_ENGLISH_LANG) + self.Bind(wx.EVT_MENU, lambda x: self.ChangeLocale('ru_RU'), + id = forms.ID_RUSSIAN_LANG) + self.Bind(wx.EVT_MENU, self.OnAbout, id = forms.ID_ABOUT) @@ -224,8 +237,13 @@ class MainFrame(forms.MainFrame): # Функции приложения и обработки сервера + def ChangeLocale(self, locale): + self.settings['locale'] = locale + wx.MessageBox(_('Locale changed. Restart application to apply settings'), _('Information')) + def OnClose(self, event): self.server.Stop() + self.SaveSettings() self.Destroy() def OnAbout(self, event): @@ -272,7 +290,7 @@ class MainFrame(forms.MainFrame): data.state = state self.SetModelState(item, data.state) - p = 'Unknown' if percent < 0 else '{:%}'.format(percent) + p = _('Unknown') if percent < 0 else '{:%}'.format(percent) um.SetItemText(item, p, 2) um.SetItemText(item, comment, 3) @@ -355,7 +373,7 @@ class MainFrame(forms.MainFrame): # Создаем корневой элемент для окна с графиками self.m_plots.AddRoot('root') - self.SetStatusText('Model "{}" selected'.format(model.GetTitle()), 0) + self.SetStatusText(_('Model "{}" selected').format(model.GetTitle()), 0) return True # Project(model) @@ -412,7 +430,7 @@ class MainFrame(forms.MainFrame): selector = wx.FileDialog( self, - 'Select file to load project', + _('Select file to load project'), '', '', 'Opal files (*.opl)|*.opl|Text files (*.txt)|*.txt', @@ -451,7 +469,7 @@ class MainFrame(forms.MainFrame): self.m_plots.ExpandAll() except Exception, e: - wx.MessageBox("Can't load saved file", 'Error', wx.ICON_ERROR | wx.OK) + wx.MessageBox(_("Can't load saved file"), _('Error'), wx.ICON_ERROR | wx.OK) print 'Oops', type(e), e finally: wx.EndBusyCursor() @@ -513,7 +531,7 @@ class MainFrame(forms.MainFrame): selector = wx.FileDialog( self, - 'Select file to save project', + _('Select file to save project'), '', self.model.GetTitle() + ' project', 'Opal files (*.opl)|*.opl|Text files (*.txt)|*.txt', @@ -548,7 +566,7 @@ class MainFrame(forms.MainFrame): f.write(dump) except Exception as e: - wx.MessageBox("Can't save the project", 'Error', wx.ICON_ERROR | wx.OK) + wx.MessageBox(_("Can't save the project"), _('Error'), wx.ICON_ERROR | wx.OK) print e finally: wx.EndBusyCursor() @@ -589,23 +607,23 @@ class MainFrame(forms.MainFrame): def SetModelState(self, item, state): if state == MODEL_READY: icon = self.icons.mready - text = 'Ready' + text = _('Ready') elif state == MODEL_RUNNING: icon = self.icons.mrun - text = 'Running' + text = _('Running') elif state == MODEL_COMPLETED: icon = self.icons.mcomplete - text = 'Completed' + text = _('Completed') elif state == MODEL_STOPPED: icon = self.icons.mstopped - text = 'Stopped' + text = _('Stopped') else: icon = self.icons.mnoexec - text = 'No executable' + text = _('No executable') self.m_user_models.SetItemImage(item, icon) self.m_user_models.SetItemText(item, text, 1) @@ -673,7 +691,7 @@ class MainFrame(forms.MainFrame): um.Expand(item) um.SelectItem(child) else: - wx.MessageBox('It\'s impossible to append model', 'Error') + wx.MessageBox(_("It's impossible to append model"), _('Error')) # Реакция на выбор модели @@ -743,7 +761,7 @@ class MainFrame(forms.MainFrame): data.state = MODEL_READY self.SetModelState(item, data.state) - child, _ = um.GetFirstChild(item) + child, _null = um.GetFirstChild(item) while child.IsOk(): Walk(child) child = um.GetNextSibling(child) @@ -768,21 +786,21 @@ class MainFrame(forms.MainFrame): def GetSelectedItem(self, source): item = source.GetSelection() if not item.IsOk(): - raise ItemError('Invalid item') + raise ItemError(_('Invalid item')) return item def GetSelectedData(self, source): item = self.GetSelectedItem(source) data = source.GetPyData(item) if not data: - raise ItemError('Empty data') + raise ItemError(_('Empty data')) return data def GetSelectedItemData(self, source): item = self.GetSelectedItem(source) data = source.GetPyData(item) if not data: - raise ItemError('Empty data') + raise ItemError(_('Empty data')) return (item, data) # Дублирование модели @@ -862,7 +880,7 @@ class MainFrame(forms.MainFrame): def OnShowResult(self, event): item, data = self.GetSelectedItemData(self.m_user_models) title = self.m_user_models.GetItemText(item) - title = 'Result for model "{}"'.format(title) + title = _('Result for model "{}"').format(title) rframe = ResultFrame(self, title, data.res) rframe.Show() @@ -870,7 +888,7 @@ class MainFrame(forms.MainFrame): def OnAddPlot(self, event): root = self.m_plots.GetRootItem() - child = self.m_plots.AppendItem(root, 'New plot') + child = self.m_plots.AppendItem(root, _('New plot')) self.m_plots.SetPyData(child, 'plot') self.m_plots.SetItemImage(child, self.icons.porg) self.m_plots.SelectItem(child) @@ -916,11 +934,11 @@ class MainFrame(forms.MainFrame): data = um.GetPyData(item) title = um.GetItemText(item) - msg = 'Line(s) for "{}" ({}/{})'.format(title, index, count) + msg = _('Line(s) for "{}" ({}/{})').format(title, index, count) if not data.res: wx.MessageBox( - 'There is no any result data for model!', + _('There is no any result data for model!'), msg, wx.OK | wx.ICON_EXCLAMATION) else: f = CreateLineSelectDialog(self, msg, data) @@ -963,7 +981,7 @@ class MainFrame(forms.MainFrame): def ShowPlot(self, lines, plot_title = ''): if lines: - p = PlotFrame(self, 'Plot', lines) + p = PlotFrame(self, _('Plot'), lines) wx.FutureCall(20, p.Show) # p.Show() @@ -1076,7 +1094,8 @@ class ResultFrame(forms.ResultFrame): if not self.result or not self.result.table: return - text_file = wx.FileSelector('Save table to CSV', + text_file = wx.FileSelector( + _('Save table to CSV'), default_filename = 'table.csv', wildcard = 'PNG files (*.csv)|*.csv|Text files (*.txt)|*.txt', flags = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) @@ -1177,7 +1196,8 @@ class PlotFrame(forms.PlotFrame): self.plot.Draw(graph) def OnSaveImage(self, event): - img_file = wx.FileSelector('Save plot', + img_file = wx.FileSelector( + _('Save plot'), default_filename = 'plot.png', default_extension = 'png', wildcard = 'PNG files (*.png)|*.png',