diff --git a/forms.py b/forms.py index bfcae84..7ef7001 100644 --- a/forms.py +++ b/forms.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import sys import wx import wx.gizmos import wx.grid @@ -29,6 +30,10 @@ ID_ADD_MARKERS = wx.NewId() ID_ABOUT = wx.NewId() +ID_EXPORT_CSV = wx.NewId() + +ID_SAVE_PLOT = wx.NewId() + class TreeListCtrl(wx.gizmos.TreeListCtrl): def __iter__(self): return TreeListCtrlIterator(self) @@ -137,8 +142,7 @@ class MainFrame(wx.Frame): self.BuildContextMenu() - # tbar = self.BuildToolBar() - # self.SetToolBar(tbar) + self.BuildToolBar() self.auimgr.Update() @@ -171,7 +175,7 @@ class MainFrame(wx.Frame): 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_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") @@ -211,11 +215,41 @@ class MainFrame(wx.Frame): 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.AppendSeparator() + 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)) + def BuildToolBar(self): - tbar = wx.ToolBar(self, -1) - tbar.AddLabelTool(ID_SHOW_PLOT, 'Plot', wx.Bitmap('share/show-plot.png')) - tbar.Realize() - return tbar + tb1 = aui.AuiToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, + agwStyle = aui.AUI_TB_DEFAULT_STYLE | aui.AUI_TB_VERTICAL) + tb1.SetToolBitmapSize(wx.Size(16, 16)) + + tb1.AddSimpleTool(ID_ADD_MODEL_SELECTED, "model-new", wx.Bitmap('share/model-add.png'), + 'Add spacification to selected model') + tb1.AddSeparator() + tb1.AddSimpleTool(ID_DUPLICATE_MODEL, "model-dup", wx.Bitmap('share/model-dup.png'), + 'Duplicate selected model') + tb1.AddSimpleTool(ID_DELETE_MODEL, "model-del", wx.Bitmap('share/model-delete.png'), + 'Delete selected model') + tb1.AddSeparator() + tb1.AddSimpleTool(ID_PROCESS_MODEL, "model-go", wx.Bitmap('share/model-go.png'), + 'Start process selected model') + tb1.AddSeparator() + tb1.AddSimpleTool(ID_SHOW_PLOT, "plot-quick", wx.Bitmap('share/plot-line.png'), + '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') + tb1.AddSeparator() + tb1.AddSimpleTool(ID_ABOUT, "app-about", wx.Bitmap('share/app-about.png'), + 'Show infomation about application') + tb1.Realize() + + self.auimgr.AddPane(tb1, aui.AuiPaneInfo().Name("tb1").Caption("Toolbar"). + ToolbarPane().Left().Floatable(False).Movable(False).Gripper(False)) class SelectModelDialog(wx.Dialog): def __init__(self, parent): @@ -259,8 +293,8 @@ class ResultFrame(wx.Frame): menubar = wx.MenuBar() menu = wx.Menu() - menu.Append(wx.NewId(), 'CSV\tCtrl+E') - menu.Append(wx.NewId(), 'TeX') + menu.Append(ID_EXPORT_CSV, 'CSV\tCtrl+E') + #menu.Append(wx.NewId(), 'TeX') menubar.Append(menu, 'Export to') return menubar @@ -287,6 +321,37 @@ class LineSelectDialog(wx.Dialog): self.Layout() self.Centre(wx.BOTH) +class SizeSelector(wx.Dialog): + def __init__(self, parent): + wx.Dialog.__init__(self, parent, -1, 'Image size', size = (200, 100)) + + bSizer = wx.BoxSizer(wx.HORIZONTAL) + + self.width = wx.SpinCtrl(self) + self.width.SetRange(1, 5000) + self.width.SetValue(800) + bSizer.Add(self.width, 1, wx.EXPAND | wx.LEFT, 5) + + self.height = wx.SpinCtrl(self) + self.height.SetRange(1, 5000) + self.height.SetValue(600) + bSizer.Add(self.height, 1, wx.EXPAND | wx.RIGHT, 5) + + buttonsSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL) + + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.AddStretchSpacer(1) + sizer.Add(bSizer, 0, wx.EXPAND | wx.ALL, 0) + sizer.AddStretchSpacer(1) + sizer.Add(buttonsSizer, 0, wx.EXPAND | wx.ALL, 5) + + self.SetSizer(sizer) + self.Layout() + self.Centre(wx.BOTH) + + def GetValues(self): + return self.width.GetValue(), self.height.GetValue() + class PlotFrame(wx.Frame): def __init__(self, parent, title): wx.Frame.__init__ (self, parent, -1, title, size = wx.Size(600, 400)) @@ -299,3 +364,40 @@ class PlotFrame(wx.Frame): self.plot.SetEnableLegend(True) self.Centre(wx.BOTH) + + menubar = wx.MenuBar() + menu = wx.Menu() + menu.Append(ID_SAVE_PLOT, 'Save to file\tCtrl+S') + menubar.Append(menu, 'Plot') + self.SetMenuBar(menubar) + + +class AboutDialog(wx.Dialog): + def __init__(self, parent): + wx.Dialog.__init__(self, parent, title = 'About Opal', size = (300, 330)) + + title = 'Opal System' + version = 'Aurora version' + copyr = '(c) 2012 Anton Vakhrushev' + + self.SetBackgroundColour(wx.Colour(42, 42, 40)) + + img = wx.StaticBitmap(self) + img.SetBitmap(wx.Bitmap('share/opal_logo.png')) + + st = wx.StaticText(self, -1, title, + pos = (15, 170), size = (270, 100)) + st.SetForegroundColour(wx.Colour(245, 245, 0)) + st.SetFont(wx.Font(24, wx.SWISS, wx.NORMAL, wx.NORMAL, False, "Verdana")); + + st = wx.StaticText(self, -1, version, + pos = (25, 215), size = (250, 20)) + st.SetForegroundColour(wx.Colour(240, 240, 240)) + st.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, "Verdana")); + + st = wx.StaticText(self, -1, copyr, + pos = (25, 255), size = (250, 30)) + st.SetForegroundColour(wx.Colour(240, 240, 240)) + st.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False, "Verdana")); + + self.Centre(wx.BOTH) diff --git a/opal.py b/opal.py index 4533a9a..6b0ca5a 100644 --- a/opal.py +++ b/opal.py @@ -33,7 +33,7 @@ class ModelData: # если мы создаем набор данных из другого набора данных elif isinstance(model, ModelData): self.mdef = model.mdef.Copy() - self.jid = server.CreateJob() if model.jid != None else None + self.jid = server.CreateJob() if model.jid else None else: self.mdef = None self.jid = None @@ -133,6 +133,9 @@ class MainFrame(forms.MainFrame): self.Bind(wx.EVT_MENU, self.OnAddMarkers, id = forms.ID_ADD_MARKERS) + self.Bind(wx.EVT_MENU, self.OnAbout, + id = forms.ID_ABOUT) + # События приложения self.Bind(wx.EVT_CLOSE, self.OnClose) @@ -158,6 +161,11 @@ class MainFrame(forms.MainFrame): self.server.Stop() self.Destroy() + def OnAbout(self, event): + self.do_nothing = True + forms.AboutDialog(self).ShowModal() + self.do_nothing = False + def OnIdle(self, event): pass @@ -226,6 +234,23 @@ class MainFrame(forms.MainFrame): except Exception, e: print 'Error in overseer: ', e + def item_protector(func): + """ + Защитный механизм, который ловит исключения при неправильном + обращении к элементам деревьев (компоненты TreeCtrl, TreeListCtrl) + + Возвращает None, если было поймано исключение. + Использование с функциями, которые не являются обработчиками событий + не желательно + """ + def Checker(*args, **kwargs): + try: + return func(*args, **kwargs) + except ItemError: + print 'Oops' + + return Checker + # Функции создания модели, сохранения и загрузки def BuildSpecs(self, model): @@ -319,7 +344,7 @@ class MainFrame(forms.MainFrame): Добавляет пользовательскую модель или спецификацию в корень дерева моделей. """ - + # строим список моделей, которые будут добавлены ms = [] while model: ms.append(model) @@ -342,6 +367,8 @@ class MainFrame(forms.MainFrame): um.SetPyData(item, data) if root: um.Expand(root) + um.SelectItem(item) + um.SetFocus() def OnAddModelToRoot(self, event): model = self.GetSelectedData(self.m_specs) @@ -364,7 +391,9 @@ class MainFrame(forms.MainFrame): new_data = ModelData(self.server, model, pmdef) um.SetPyData(child, new_data) um.SetItemImage(child, self.icons.mready) + um.SetFocus() um.Expand(item) + um.SelectItem(child) else: wx.MessageBox('It\'s impossible to append model', 'Error') @@ -429,17 +458,37 @@ class MainFrame(forms.MainFrame): pass def OnParamChanged(self, event): + + def Walk(item): + um.SetItemImage(item, self.icons.mready) + child, _ = um.GetFirstChild(item) + while child.IsOk(): + Walk(child) + child = um.GetNextSibling(child) + + um = self.m_user_models prop = event.GetProperty() if not prop: return value = prop.GetValue() param = prop.GetClientData() - item, data = self.GetSelectedItemData(self.m_user_models) + item, data = self.GetSelectedItemData(um) data.mdef[param] = value - self.m_user_models.SetItemImage(item, self.icons.mready) + Walk(item) + def OnTest(self, event): + + def Walk(item): + print um.GetItemText(item) + um.SetItemImage(item, self.icons.mready) + child, cookie = um.GetFirstChild(item) + while child.IsOk(): + Walk(child) + child = um.GetNextSibling(child) + um = self.m_user_models + Walk(um.GetRootItem()) # Получение данных выбранной модели @@ -465,6 +514,15 @@ class MainFrame(forms.MainFrame): # Дублирование модели + def Duplicate(self, item_src, item_dst): + um = self.m_user_models + data = um.GetPyData(item_src) + title = um.GetItemText(item_src) + new_data = ModelData(self.server, data) + um.SetItemText(item_dst, self.GenerateName(title)) + um.SetPyData(item_dst, new_data) + um.SetItemImage(item_dst, self.icons.mready) + def OnDuplicate(self, event): """ Обработчик события "дублирование модели" @@ -474,17 +532,29 @@ class MainFrame(forms.MainFrame): Результаты модели-оригинала не копируются. """ um = self.m_user_models - item, data = self.GetSelectedItemData(self.m_user_models) - title = um.GetItemText(item) - parent = um.GetItemParent(item) - child = um.AppendItem(parent, self.GenerateName(title)) - new_data = ModelData(self.server, data) - um.SetPyData(child, new_data) - um.SetItemImage(child, self.icons.mready) - self.SetStatusText('Copy for "{}" created'.format(title), 0) + item_src = self.GetSelectedItem(um) + parent = um.GetItemParent(item_src) + item_dst = um.AppendItem(parent, 'new-item') + self.Duplicate(item_src, item_dst) + # self.SetStatusText('Copy for "{}" created'.format(title), 0) def OnDuplicateTree(self, event): - pass + + def Walk(item_src, item_dst): + self.Duplicate(item_src, item_dst) + + child_src, _ = um.GetFirstChild(item_src) + while child_src.IsOk(): + child_dst = um.AppendItem(item_dst, 'new-item') + Walk(child_src, child_dst) + child_src = um.GetNextSibling(child_src) + + um = self.m_user_models + item_src = self.GetSelectedItem(um) + parent = um.GetItemParent(item_src) + item_dst = um.AppendItem(parent, 'new-item') + Walk(item_src, item_dst) + um.Expand(item_dst) # Удаление модели @@ -579,6 +649,7 @@ class MainFrame(forms.MainFrame): else: self.m_plots.SetItemImage(child, self.icons.pline) + @item_protector def OnAddCurves(self, event): self.AddLines(LINE_CURVE) @@ -749,7 +820,10 @@ class PlotFrame(forms.PlotFrame): def __init__(self, parent, title, lines): forms.PlotFrame.__init__(self, parent, title) - colours = ['red', 'blue', 'green'] + self.Bind(wx.EVT_MENU, self.OnSaveImage, + id = forms.ID_SAVE_PLOT) + + colours = ['red', 'blue', 'green', 'magenta', 'purple', 'brown', 'yellow'] plot_lines = [] for i, line in enumerate(lines): attr = {} @@ -766,6 +840,23 @@ class PlotFrame(forms.PlotFrame): graph = wxplot.PlotGraphics(plot_lines) self.plot.Draw(graph) + def OnSaveImage(self, event): + img_file = wx.FileSelector('Save plot', + default_filename = 'plot.png', + default_extension = 'png', + wildcard = 'PNG files (*.png)|*.png', + flags = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) + size_sel = forms.SizeSelector(self) + if img_file and size_sel.ShowModal() == wx.ID_OK: + self.plot.Freeze() + w, h = size_sel.GetValues() + old_size = self.plot.GetSize() + self.plot.SetSize((w, h)) + self.plot.SaveFile(img_file) + self.plot.SetSize(old_size) + self.plot.Thaw() + + #----------------------------------------------------------------------------- # Приложение #----------------------------------------------------------------------------- diff --git a/plot.png b/plot.png new file mode 100644 index 0000000..416591c Binary files /dev/null and b/plot.png differ diff --git a/server.py b/server.py index 21da102..8b7c255 100644 --- a/server.py +++ b/server.py @@ -130,34 +130,26 @@ class LocalServer: def GetJobsCount(self): return len(self.jobs) - #def CheckJID(self, func): - # def - def GetJobState(self, jid): job = self.jobs.get(jid) - if job != None: + if job: return job.GetState() def IsJobChanged(self, jid): job = self.jobs.get(jid) - if job != None: - return job.IsChanged() - else: - False + return job.IsChanged() if job else False def GetJobResult(self, jid): job = self.jobs.get(jid) - if job != None: - return job.GetResult() + return job.GetResult() if job else None def GetJobTID(self, jid): job = self.jobs.get(jid) - if job != None: - return job.tid + return job.tid if job else None def LaunchJob(self, jid, data_def): job = self.jobs.get(jid) - if job != None: + if job: tid = data_def.DD.tid datadump = data_def.PackParams() job.Launch(tid, datadump) @@ -165,12 +157,12 @@ class LocalServer: def StopJob(self, jid): job = self.jobs.get(jid) - if job != None: + if job: job.Stop() def DeleteJob(self, jid): job = self.jobs.get(jid) - if job != None: + if job: job.Stop() del self.jobs[jid] @@ -317,8 +309,8 @@ class Job: self.ChangeState() def Stop(self): - WriteToLog('Try to kill') if self.proc and self.proc.poll() == None: + WriteToLog('Try to kill') self.proc.kill() self.ChangeState() WriteToLog('Job killed') diff --git a/share/app-about.png b/share/app-about.png new file mode 100644 index 0000000..e33422d Binary files /dev/null and b/share/app-about.png differ diff --git a/share/model-add.png b/share/model-add.png new file mode 100644 index 0000000..b983568 Binary files /dev/null and b/share/model-add.png differ diff --git a/share/model-delete.png b/share/model-delete.png new file mode 100644 index 0000000..ea4e4fe Binary files /dev/null and b/share/model-delete.png differ diff --git a/share/model-dup.png b/share/model-dup.png new file mode 100644 index 0000000..a87b1b9 Binary files /dev/null and b/share/model-dup.png differ diff --git a/share/model-go.png b/share/model-go.png new file mode 100644 index 0000000..5eed333 Binary files /dev/null and b/share/model-go.png differ diff --git a/share/opal_logo.png b/share/opal_logo.png new file mode 100644 index 0000000..80fb56a Binary files /dev/null and b/share/opal_logo.png differ diff --git a/share/report-show.png b/share/report-show.png new file mode 100644 index 0000000..9ae5052 Binary files /dev/null and b/share/report-show.png differ