From 9741fc36e384058c5eda167d4ea3c48db390f774 Mon Sep 17 00:00:00 2001 From: anwinged Date: Tue, 1 May 2012 09:03:38 +0000 Subject: [PATCH] Plots, icons, bugfixes --- trunk/forms.py | 95 ++++++++++---- trunk/opal.py | 219 ++++++++++++++++++++++++++------- trunk/share/model-complete.png | Bin 0 -> 542 bytes trunk/share/model-ready.png | Bin 0 -> 567 bytes trunk/share/model-run.png | Bin 0 -> 518 bytes trunk/share/plot-histogram.png | Bin 0 -> 604 bytes trunk/share/plot-line.png | Bin 0 -> 759 bytes trunk/share/plot-marker.png | Bin 0 -> 755 bytes trunk/share/plot-org.png | Bin 0 -> 459 bytes trunk/task.py | 9 +- trunk/tasks/testt.json | 2 + 11 files changed, 251 insertions(+), 74 deletions(-) create mode 100644 trunk/share/model-complete.png create mode 100644 trunk/share/model-ready.png create mode 100644 trunk/share/model-run.png create mode 100644 trunk/share/plot-histogram.png create mode 100644 trunk/share/plot-line.png create mode 100644 trunk/share/plot-marker.png create mode 100644 trunk/share/plot-org.png diff --git a/trunk/forms.py b/trunk/forms.py index 7b0b1d0..81d2948 100644 --- a/trunk/forms.py +++ b/trunk/forms.py @@ -6,6 +6,10 @@ import wx.grid import wx.propgrid as wxpg import wx.lib.plot as wxplot +ID_NEW = wx.NewId() +ID_SAVE = wx.NewId() +ID_OPEN = wx.NewId() + ID_TEST = wx.NewId() ID_ADD_MODEL_ROOT = wx.NewId() ID_ADD_MODEL_SELECTED = wx.NewId() @@ -15,14 +19,15 @@ ID_DELETE_MODEL = wx.NewId() ID_PROCESS_MODEL = wx.NewId() ID_SHOW_RESULT = wx.NewId() + ID_SHOW_PLOT = wx.NewId() ID_ADD_PLOT = wx.NewId() -ID_ADD_LINE = wx.NewId() +ID_ADD_CURVES = wx.NewId() +ID_ADD_MARKERS = wx.NewId() ID_ABOUT = wx.NewId() class TreeListCtrl(wx.gizmos.TreeListCtrl): - def __iter__(self): return TreeListCtrlIterator(self) @@ -41,14 +46,23 @@ class TreeListCtrlIterator: self.item = self.owner.GetNext(self.item) return item +class Icons: + """ + Пустой класс для хранения идентификаторов иконок, чтобы к ним можно было + удобно обращаться: + icons = Icons() + icons.open = wxIcon(...) + """ + pass class MainFrame (wx.Frame): - def __init__(self, parent): wx.Frame.__init__ (self, parent, title = 'Opal', size = wx.Size(873,594)) self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.ilist, self.icons = self.LoadIcons() + bSizer3 = wx.BoxSizer(wx.HORIZONTAL) self.m_specs = wx.TreeCtrl(self, style = wx.TR_DEFAULT_STYLE) @@ -67,6 +81,7 @@ class MainFrame (wx.Frame): 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) bSizer4.Add(self.m_user_models, 0, wx.ALL | wx.EXPAND, 1) @@ -84,9 +99,10 @@ class MainFrame (wx.Frame): self.m_quick_result.SetMinSize(wx.Size(200, -1)) bSizer5.Add(self.m_quick_result, 1, wx.EXPAND | wx.ALL, 1) - self.m_plots = wx.TreeCtrl(self, style = wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) - self.m_plots.AddRoot('root') - bSizer5.Add(self.m_plots, 1, wx.EXPAND | wx.ALL, 1) + self.m_plots = wx.TreeCtrl(self, + style = wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.TR_EDIT_LABELS) + bSizer5.Add(self.m_plots, 1, wx.EXPAND | wx.ALL, 1) + self.m_plots.SetImageList(self.ilist) bSizer3.Add(bSizer5, 0, wx.ALL | wx.EXPAND, 1) @@ -97,43 +113,60 @@ class MainFrame (wx.Frame): self.SetMenuBar(mbar) self.BuildContextMenu() - #tbar = self.BuildToolBar() - #self.SetToolBar(tbar) + # tbar = self.BuildToolBar() + # self.SetToolBar(tbar) self.SetSizer(bSizer3) self.Layout() - self.Centre(wx.BOTH) - il = wx.ImageList(16, 16) + def LoadIcons(self): + icons = Icons() + ilist = wx.ImageList(16, 16) + + icons.mready = ilist.Add(wx.Bitmap('share/model-ready.png')) + icons.mrun = ilist.Add(wx.Bitmap('share/model-run.png')) + icons.mcomplete = ilist.Add(wx.Bitmap('share/model-complete.png')) + + icons.porg = ilist.Add(wx.Bitmap('share/plot-org.png')) + icons.pline = ilist.Add(wx.Bitmap('share/plot-line.png')) + icons.pmarker = ilist.Add(wx.Bitmap('share/plot-marker.png')) + icons.phist = ilist.Add(wx.Bitmap('share/plot-histogram.png')) + + return ilist, icons def BuildMenu(self): menubar = wx.MenuBar() menu = wx.Menu() - menu.Append(1, "&Open\tCtrl+O") - menubar.Append(menu, '&File') + menu.Append(ID_NEW, "&New\tCtrl+N") + menu.Append(ID_OPEN, "&Open\tCtrl+O") + menu.Append(ID_SAVE, "&Save\tCtrl+S") + menubar.Append(menu, '&Model') menu = wx.Menu() - menu.Append(ID_PROCESS_MODEL, 'Process\tF5') - menu.AppendSeparator() 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_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') + menubar.Append(menu, '&Edit') menu = wx.Menu() - menu.Append(ID_SHOW_RESULT, 'Show result\tCtrl+S') + menu.Append(ID_PROCESS_MODEL, 'Process\tF5') + #menu.AppendSeparator() + menubar.Append(menu, '&Run') + + menu = wx.Menu() + menu.Append(ID_SHOW_RESULT, 'Show numbers\tF7') menu.AppendSeparator() - menu.Append(ID_SHOW_PLOT, 'Show plot\tCtrl+G') + menu.Append(ID_SHOW_PLOT, 'Show plot\tF8') menu.Append(ID_ADD_PLOT, 'Add plot') - menu.Append(ID_ADD_LINE, 'Add line') + #menu.Append(ID_ADD_LINE, 'Add line') menubar.Append(menu, '&Result') menu = wx.Menu() @@ -143,7 +176,6 @@ class MainFrame (wx.Frame): 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') @@ -152,17 +184,33 @@ class MainFrame (wx.Frame): menu1 = wx.Menu() menu1.Append(ID_ADD_PLOT, 'Add plot') - menu1.Append(ID_ADD_LINE, 'Add line') + menu1.AppendSeparator() + 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)) - def BuildToolBar(self): tbar = wx.ToolBar(self, -1) - tbar.AddLabelTool(ID_SHOW_PLOT, 'Show plot', wx.Bitmap('share/show-plot.png')) + tbar.AddLabelTool(ID_SHOW_PLOT, 'Plot', wx.Bitmap('share/show-plot.png')) tbar.Realize() return tbar +class SelectModelDialog(wx.Dialog): + def __init__(self, parent): + 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) + sizer.Add(self.mlist, 1, wx.EXPAND | wx.ALL, 0) + + buttonsSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL) + sizer.Add(buttonsSizer, 0, wx.EXPAND | wx.ALL, 5) + + self.SetSizer(sizer) + self.Layout() + self.Centre(wx.BOTH) + class ResultFrame(wx.Frame): def __init__(self, parent, title): wx.Frame.__init__ (self, parent, -1, title, size = wx.Size(500, 500), @@ -215,3 +263,4 @@ class PlotFrame(wx.Frame): self.plot.SetEnableGrid(True) self.plot.SetEnableAntiAliasing(True) self.plot.SetEnableHiRes(True) + self.plot.SetEnableLegend(True) diff --git a/trunk/opal.py b/trunk/opal.py index 1f440ab..1151d2d 100644 --- a/trunk/opal.py +++ b/trunk/opal.py @@ -22,6 +22,7 @@ import datetime import os import threading import re +from wx.lib.embeddedimage import PyEmbeddedImage class ModelData: def __init__(self, server, model, parent_data = None): @@ -39,6 +40,30 @@ class ModelData: self.res = None + +LINE_CURVE = 1 +LINE_MARKER = 2 +LINE_HISTOGRAM = 3 + +class LineData: + """ + Данные одной линии для графика + + Предназначен для использования совместно с графическим компонентом, + поэтому не имеет собственного значения названия. Вместо этого + название берется из графического компонента. + """ + def __init__(self, type, mdata, columns, colour = None, style = None): + self.type = type # тип графика + self.mdata = mdata # указатель на данные модели + self.title = '' + self.columns = columns # пара (x, y) + self.colour = colour # цвет: если не задан выбирается из списка + self.style = style # стиль: если не задан, еспользуется по умолчанию + + def GetPoints(self): + return self.mdata.res.Zip(*self.columns) + class ItemError(Exception): pass @@ -55,6 +80,7 @@ class MainFrame(forms.MainFrame): s = server.LocalServer() s.LoadModels() models = s.GetModels() + self.models = models s.Start() self.server = s @@ -74,6 +100,9 @@ class MainFrame(forms.MainFrame): self.OnPlotProcess) + self.Bind(wx.EVT_MENU, self.OnNewModel, + id = forms.ID_NEW) + self.Bind(wx.EVT_MENU, self.OnTest, id = forms.ID_TEST) self.Bind(wx.EVT_MENU, self.OnAddModelToRoot, @@ -91,12 +120,15 @@ class MainFrame(forms.MainFrame): self.Bind(wx.EVT_MENU, self.OnShowResult, id = forms.ID_SHOW_RESULT) - self.Bind(wx.EVT_MENU, self.OnShowPlot, + + self.Bind(wx.EVT_MENU, self.OnQuickShowPlot, id = forms.ID_SHOW_PLOT) self.Bind(wx.EVT_MENU, self.OnAddPlot, id = forms.ID_ADD_PLOT) - self.Bind(wx.EVT_MENU, self.OnAddLines, - id = forms.ID_ADD_LINE) + self.Bind(wx.EVT_MENU, self.OnAddCurves, + id = forms.ID_ADD_CURVES) + self.Bind(wx.EVT_MENU, self.OnAddMarkers, + id = forms.ID_ADD_MARKERS) self.Bind(wx.EVT_CLOSE, self.OnClose) self.Bind(wx.EVT_IDLE, self.OnIdle) @@ -169,12 +201,16 @@ class MainFrame(forms.MainFrame): print 'JID', jid, (state, percent, comment) # завершающие действия по окончанию выполнения работы if state == server.JOB_COMPLETED: + # устанавливаем иконку для завершенной модели + um.SetItemImage(item, self.icons.mcomplete) # получаем результаты выполнения data.res = self.server.GetJobResult(jid) # если завершившаяся задача в данный момент выделена # то сразу же показываем этот результат if um.IsSelected(item): self.ShowQuickResult(data.res) + else: + um.SetItemImage(item, self.icons.mrun) finally: wx.MutexGuiLeave() pass @@ -243,6 +279,7 @@ class MainFrame(forms.MainFrame): for i, m in enumerate(ms): name = self.GenerateName(m.GetTitle()) item = um.AppendItem(item, name) + um.SetItemImage(item, self.icons.mready) if not i: root = item data = ModelData(self.server, m, defparent) @@ -357,12 +394,24 @@ class MainFrame(forms.MainFrame): return value = prop.GetValue() param = prop.GetClientData() - data = self.GetSelectedData(self.m_user_models) + item, data = self.GetSelectedItemData(self.m_user_models) data.mdef[param] = value + self.m_user_models.SetItemImage(item, self.icons.mready) def OnTest(self, event): um = self.m_user_models + def OnNewModel(self, event): + self.do_nothing = True + f = SelectModelDialog(self, self.models) + if f.ShowModal() == wx.ID_OK: + model = f.GetSelectedModel() + if model: + print model.GetTitle() + else: + print 'Bad :(' + self.do_nothing = False + def OnAddModelToRoot(self, event): model = self.GetSelectedData(self.m_specs) self.AddModelToRoot(model) @@ -383,6 +432,7 @@ class MainFrame(forms.MainFrame): child = um.AppendItem(item, name) new_data = ModelData(self.server, model, pmdef) um.SetPyData(child, new_data) + um.SetItemImage(child, self.icons.mready) um.Expand(item) else: wx.MessageBox('It\'s impossible to append model', 'Error') @@ -402,6 +452,7 @@ class MainFrame(forms.MainFrame): 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) def OnDuplicateTree(self, event): @@ -425,16 +476,12 @@ class MainFrame(forms.MainFrame): rframe = ResultFrame(self, title, data.res) rframe.Show() - def GetLines(self): + def GetLines(self, line_type): """ Возвращает набор линий, которые пользователь указал для построения графика к выбранной модели. - Линии представляют из себя кортежи из 4х элементов: - [ внутренний индекс в иерархии моделей, - данные модели, - колонка-х, - колонка-у ] + Возвращает список экземпляров LineData """ um = self.m_user_models item, data = self.GetSelectedItemData(um) @@ -453,46 +500,67 @@ class MainFrame(forms.MainFrame): self.do_nothing = True try: if f.ShowModal() == wx.ID_OK: - lines = [ (item, data, x, y) for x, y in f.GetData() ] + lines = [ LineData(line_type, data, xy) + for xy in f.GetLineColumns() ] finally: self.do_nothing = False return lines - def ShowPlot(self, lines, title = ''): - if not lines: - return - - data = [] - for item, moddata, x, y in lines: - data.append(moddata.res.Zip(x, y)) - - p = PlotFrame(self, 'Plot for model "%s"' % title, data) - p.Show() - - - def OnShowPlot(self, event): - lines = self.GetLines() - self.ShowPlot(lines) - + def ShowPlot(self, lines, plot_title = ''): + if lines: + p = PlotFrame(self, 'Plot', lines) + p.Show() + def OnQuickShowPlot(self, event): + lines = self.GetLines(LINE_CURVE) + um = self.m_user_models + item, data = self.GetSelectedItemData(um) + title = um.GetItemText(item) + for line in lines: + colx, coly = line.columns + title_x = data.res.columns[colx].GetTitle() + title_y = data.res.columns[coly].GetTitle() + line.title = "{}: {}({})".format(title, title_y, title_x) + self.ShowPlot(lines, title) def OnAddPlot(self, event): root = self.m_plots.GetRootItem() 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) - def OnAddLines(self, event): - item = self.m_plots.GetSelection() - data = self.m_plots.GetItemPyData(item) + def AddLines(self, line_type): + item, data = self.GetSelectedItemData(self.m_plots) if data != 'plot': return - lines = self.GetLines() + lines = self.GetLines(line_type) if not lines: return + it = self.GetSelectedItem(self.m_user_models) for line in lines: - child = self.m_plots.AppendItem(item, 'Line') + x, y = line.columns + data = line.mdata + model_name = self.m_user_models.GetItemText(it) + x_name = data.res.columns[x].GetTitle() + y_name = data.res.columns[y].GetTitle() + title = "{}: {}({})".format(model_name, y_name, x_name) + child = self.m_plots.AppendItem(item, title) self.m_plots.SetPyData(child, line) + self.m_plots.SetItemImage(child, self.icons.pline) + self.m_plots.Expand(item) + if line.type == LINE_MARKER: + self.m_plots.SetItemImage(child, self.icons.pmarker) + else: + self.m_plots.SetItemImage(child, self.icons.pline) + + + def OnAddCurves(self, event): + self.AddLines(LINE_CURVE) + + def OnAddMarkers(self, event): + self.AddLines(LINE_MARKER) def OnPlotProcess(self, event): item = self.m_plots.GetSelection() @@ -501,16 +569,50 @@ class MainFrame(forms.MainFrame): if data == 'plot': child, cookie = self.m_plots.GetFirstChild(item) while child.IsOk(): - lines.append(self.m_plots.GetItemPyData(child)) + title = self.m_plots.GetItemText(child) + line_data = self.m_plots.GetItemPyData(child) + line_data.title = title + lines.append(line_data) child, cookie = self.m_plots.GetNextChild(item, cookie) else: - lines = [data] + title = self.m_plots.GetItemText(item) + data.title = title + lines = [ data ] self.ShowPlot(lines) def OnIdle(self, event): pass +#----------------------------------------------------------------------------- +# Форма с выбором модели из представленного списка +#----------------------------------------------------------------------------- + +class SelectModelDialog(forms.SelectModelDialog): + def __init__(self, parent, models): + forms.SelectModelDialog.__init__(self, parent) + + self.ilist = wx.ImageList(32, 32) + self.mlist.SetImageList(self.ilist, wx.IMAGE_LIST_NORMAL) + self.data_list = {} + + for model in models: + item = wx.ListItem() + item.SetText(model.GetTitle()) + #item.Data = model + img_data = model.GetImage() + if img_data: + img = PyEmbeddedImage(img_data) + index = self.ilist.Add(img.GetBitmap()) + item.SetImage(index) + index = self.mlist.InsertItem(item) + self.data_list[index] = model + + def GetSelectedModel(self): + index = self.mlist.GetNextItem(-1, wx.LIST_NEXT_ALL, wx.LIST_STATE_SELECTED) + return self.data_list.get(index) + + #----------------------------------------------------------------------------- # Форма с результатами выполнения работы #----------------------------------------------------------------------------- @@ -542,8 +644,13 @@ class ResultFrame(forms.ResultFrame): self.table.AutoSize() pg = self.scalar - for label, param in self.result.data.iteritems(): - pg.Append(wxpg.StringProperty(label, value = str(param.GetValue()))) + data = self.result.data + if not data: + pg.Show(0) + else: + for label, param in data.iteritems(): + pg.Append(wxpg.StringProperty(label, + value = str(param.GetValue()))) #----------------------------------------------------------------------------- # Форма с выбором наборов значений для построения графика @@ -553,17 +660,32 @@ class LineSelectDialog(forms.LineSelectDialog): def __init__(self, parent, title): forms.LineSelectDialog.__init__(self, parent, title) - def Add(self, title, data = None): + def Add(self, title, data): self.left.Append(title, data) self.right.Append(title, data) def SetSelections(self): + """ + Выделяет первую строку в левом столбце колонок + и все, кроме первой, во втором. + + Таким образом по умолчанию предлагается построить зависимость + каждого значения от первого. Это логично, поскольку первым + обычно идет независимый параметр. + """ + # выделяем первую строку слева + # (первый столбец результата) if self.left.GetCount(): self.left.Select(0) + # выделяем все, кроме первой, строки справа + # (второй столбец результата) for i in xrange(1, self.right.GetCount()): self.right.Select(i) - def GetData(self): + def GetLineColumns(self): + """ + Возвращает список пар колонок, которые были выбраны + """ item = self.left.GetSelection() x = self.left.GetClientData(item) @@ -578,17 +700,24 @@ class LineSelectDialog(forms.LineSelectDialog): #----------------------------------------------------------------------------- class PlotFrame(forms.PlotFrame): - def __init__(self, parent, title, lines_with_data): + def __init__(self, parent, title, lines): forms.PlotFrame.__init__(self, parent, title) - #self.data = data - data = lines_with_data - lines = [] colours = ['red', 'blue', 'green'] - for i, d in enumerate(data): - lines.append( wxplot.PolyLine(d, colour = colours[i % len(colours)]) ) + plot_lines = [] + for i, line in enumerate(lines): + attr = {} + if line.type == LINE_MARKER: + handle = wxplot.PolyMarker + attr['size'] = 1 + else: + handle = wxplot.PolyLine + points = line.GetPoints() + attr['colour'] = line.colour or colours[i % len(colours)] + attr['legend'] = line.title or 'Unknown line' + plot_lines.append(handle(points, **attr)) - graph = wxplot.PlotGraphics(lines) + graph = wxplot.PlotGraphics(plot_lines) self.plot.Draw(graph) #----------------------------------------------------------------------------- diff --git a/trunk/share/model-complete.png b/trunk/share/model-complete.png new file mode 100644 index 0000000000000000000000000000000000000000..08f8b8c25a23734b28abe4b90f809d770d592600 GIT binary patch literal 542 zcmV+(0^$9MP)&_j|AFb@eFc^O>F*3Ix($Y4ciwBgc{D&@XEZjtWPaqjd$=UTUNw zIh{_F$z-Tntr}LVwHS>?BjIqE{C>XxXS10;91itaTUOVB>f^CM*_9F&2xF&*!7V;cz#w2Mn;_fGYxA zRxTI}M!a4x@`nKou;APTSUsz08trzwKLvmR794QZO9lF9G@@>|OHQYg_%39&+wI~V zr1g3Y?=rhwF5#F3a1XRtENDC)KkU2BW+R8gAsn+ngHO}pZ?#%I6mYxUQW-cm0rpej z9_yh{h!jPU8^1SHRTXQD6^^-ra&}-Jh*itu-R;gu>){!U10yM?RHzZ zJQDGVGBKNT>;VHTIOaA|GMOy#lg&4q%|WNrq4|6+ry`JJ4ST=(RCwB? zlf6qLQ540`m~k@6B&)$JL_`pqEN&s`to#cED?zXlOnN&ji>lBU%VNW*L*Y{n7iv?^U;2_#vgN68FrpMyAUuubSXuS51&KBk}Fe zv+J;8bA89HETzZ2+TpvaJ=hhDuqL84jug~z`?VFbrugvTXD zq5H$??5%nkjb|H;^1Z|&3@fTU-UjkVIH*6}qGF#9ym#HOV3_8AkEcOK)BTCSrwD4! z;1|aXMIfy5^cu){4)WKz#N?C(pB6$}68)A>*WnKi@xG5~>3jEE_{e$@U;xybA=+8l&9ndj002ovPDHLk FV1g942#Npz literal 0 HcmV?d00001 diff --git a/trunk/share/model-run.png b/trunk/share/model-run.png new file mode 100644 index 0000000000000000000000000000000000000000..c1cfd618165818684ac7b77688e8211eba7628dc GIT binary patch literal 518 zcmV+h0{Q)kP)w=iD719*olGJ`u;t`yNl;=&&qe^akU zr5EDB*znR#)vK4(tB!WPUg@2sL7?S+tFbDh$!IXFTWrV}F#3$qo!E6g$iU%pxlp}c zC*SvN!!SC9LZO(?=P8{|3vsjASj*+o3d7Joolb4$_LwU<5#S(so@bk;=~gNg%H?vB zJfF|9CzDAkm&*_=PN!3sxd`|0&~L=5rfHp0sYIz%N(7$)IdH&5%|qaLJT|l0Y!Pqb z^GkpOE@~bEtlr3EGITf`{uBTQT+}=~;Dj}w&uP2e68j(o?D2S9JV4QIx1;rXEjwC4 zjVd69tyU`vg5c%cW3d<|5(%kM1%|9@ac>yeO971b_60+Aj01kNg(N#Hjz3#`N2E1; zC2G_u^cTF)2Gjw#t>PXB2VB&s_h1x7QF}6((0;#{K4Bns^^6=i;G#wyBh_lP9=q&} z$K%CpHj|0yx-P&OG33Aj_ui)bOrI_HUq91V*ZCEAXZ;8;02?$hzPLAPUH||907*qo IM6N<$f*?ZUEC2ui literal 0 HcmV?d00001 diff --git a/trunk/share/plot-histogram.png b/trunk/share/plot-histogram.png new file mode 100644 index 0000000000000000000000000000000000000000..59c4544d8dc60c05ca516bac11a4491e6536844d GIT binary patch literal 604 zcmV-i0;BzjP){b?+>P zOdvQjJzdp3?^V639)q51`|7#p(#G{9#Wxmk&VIVG{o(B2-}*iRiE;hnWCEP{QWn0R z#`|Ya&h&lTOOPZ%q{ZIDm)T#vIYDS<_-6u>bR@WXu<#SCG^pjAz21jq26xVf zyymZO;b#+Zi~$f??cX&KPZJ4R*x(lg5lLshkJpw2sjC*ef*;)21O}fWB$&!6G>=}9 zfV#U3WC5V=Bf(OlnkJKajXafIy!-e9Vt}=^Tkr}#L|~;vGrUKdffRye7aB8#smg(w z)Al)C)}WJ+;q+;YJTs7DX9ArYd$1&EX)DC?7%nBScq-dkW>9GjNI+ZHyi(8YJ`y{| z?q$2gB#94Qtx7b4Rw|ck;5!LAw{90^kZVMHPoWtU!l5p!&*gFDnE_=u$ACOci9y=Y zx2%Z)kcQte#H#)gKoyqg0rzUQcsw3wd0{1B?rNBgyZ2HX_3W3F#Piez4ii;*=>oUB qg4!8eY!+*V8M!R>0{-LrBftPq6T(-wgCI5l0000 zwIs_&TA4F?AANUbt{E<>1BdUsmvjE#|D69hQfq5#kD@3=ny!Q4|6mr21zDD-D=I2h z$%>*DRa91@82PG-q-p5F!zM|;L6_WR*~W$QEza;=gkyw1$xb4@yPv@~ugG$_!hr#Q zfUGQ!kxd}r$L)5b3GlSE)bJSwFi;KLm`o1~PrI;r>jcz4oxzl^SdPLAwoq{LIs_~;}ak6SR9kG`#&48SVf;Y`L= zb(+qb$Ef`pU|pPxrd_=hY}~|+w_`N*jPkJE4~mL@cK6Z}3|XMCPQo4sDFv|aK@z1W zJF&%R>~DX}o#9#P3!R)OT*;bPL0vNX7G+?{-+NrgD&YLXW7sDf2vzQ-y4;6fjc4Z6 zHb(pdWVkXBvZIOyo9afIC_oj0LVpXIGL5U@90$%^gLv5A}6(ynkqfTvs~gu!C;rq=POxUAVVM>^uKJQ p)oT52f@cYL^8coaKJ@+)U;v(21ii69<$3@B002ovPDHLkV1icVU>5)Y literal 0 HcmV?d00001 diff --git a/trunk/share/plot-marker.png b/trunk/share/plot-marker.png new file mode 100644 index 0000000000000000000000000000000000000000..2ea8f387e50742e37cf85e5ac0ce318d82cb46d6 GIT binary patch literal 755 zcmV$bE)0ij@t zML-k|o;2~I3F23R21Er+NK8C>@W3Ac2RL~1XgqlF(o$1nF!3W8HQEwSCQ8JQ7)eVP z`auhgp>d(KyW{MZ7ECnJd6~?6v+wttnK!diAP{(~Y1%O&!rw&V1yjjfk|e=mvHTbs z8rm%z;ER_ogY6%ILdcL=EsWPVMqVpG5`;%n4{MD(vJqL=HN@g^{vZ%rT7t*p;b6r3 zIQF-8;`YoSUd@Ma`alz$RH{lQ*eC7hmSqd{%i$G#!1y8GZPeBTHHd1Dj=*#CQbzB3jXNkVfDWtM(y9&|^M3a+#GRlz6 zr;=sZShI;VLRW^N##5-W0{ypIFn5QL%d5!gYuJ=FF?&~oE^Q}Z5^x2W;np0CrP(21 z6BRggUBdH;48pMm$C1(-MAQFy3A`Q+a7rGzZhtpk;XflOpBy3J%X%6HPQ~%)}^o$+F z+0HKPZK>nos>4DufyRMhyzK3Rvu_aX?ndaYdPqCR)#vrmPKhgN)9N}=II5KWkwlW% zMLhE6+3WQd+bD_O_|VksD*gxTZrj&f_P4tC6#b%Dwb-(>S7_^w#!-eZ1T^E4Va7Jlxz%p@py^gsEmr}yi1=Qn{WHnK z-TN39B_%mr2j(*biY1J zis%M=-IT50DS-e002ovPDHLkV1l`( B$A$m^ literal 0 HcmV?d00001 diff --git a/trunk/task.py b/trunk/task.py index c584ade..f2efe32 100644 --- a/trunk/task.py +++ b/trunk/task.py @@ -112,12 +112,12 @@ class DataDescription: def IsExecutable(self): return self.data.get('exec', True) + def GetImage(self): + return self.data.get('img') + def __getitem__(self, label): return self.pdata.get(label) - def type(self): - return 'data-def' - #------------------------------------------------------------------------------- class DataDefinition: @@ -154,9 +154,6 @@ class DataDefinition: package.reverse() return json.dumps(package) - def type(self): - return 'data-def' - #------------------------------------------------------------------------------- class ResultData: diff --git a/trunk/tasks/testt.json b/trunk/tasks/testt.json index ba77d1c..f427e4d 100644 --- a/trunk/tasks/testt.json +++ b/trunk/tasks/testt.json @@ -13,6 +13,8 @@ "exec": true, + "img": "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", + "params": { "r": {