svghmi/ui.py
branchsvghmi
changeset 3247 7282b40374b0
parent 3245 c441181247cf
child 3250 22da8e0b07b8
equal deleted inserted replaced
3245:c441181247cf 3247:7282b40374b0
    25 
    25 
    26 from util.ProcessLogger import ProcessLogger
    26 from util.ProcessLogger import ProcessLogger
    27 
    27 
    28 ScriptDirectory = paths.AbsDir(__file__)
    28 ScriptDirectory = paths.AbsDir(__file__)
    29 
    29 
       
    30 HMITreeDndMagicWord = "text/beremiz-hmitree"
       
    31 
    30 class HMITreeSelector(wx.TreeCtrl):
    32 class HMITreeSelector(wx.TreeCtrl):
    31     def __init__(self, parent):
    33     def __init__(self, parent):
    32         global on_hmitree_update
    34 
    33         wx.TreeCtrl.__init__(self, parent, style=(
    35         wx.TreeCtrl.__init__(self, parent, style=(
    34             wx.TR_MULTIPLE |
    36             wx.TR_MULTIPLE |
    35             wx.TR_HAS_BUTTONS |
    37             wx.TR_HAS_BUTTONS |
    36             wx.SUNKEN_BORDER |
    38             wx.SUNKEN_BORDER |
    37             wx.TR_LINES_AT_ROOT))
    39             wx.TR_LINES_AT_ROOT))
    38 
    40 
       
    41         self.ordered_items = []
       
    42         self.parent = parent
       
    43 
    39         self.MakeTree()
    44         self.MakeTree()
       
    45 
       
    46         self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeNodeSelection)
       
    47         self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag)
    40 
    48 
    41     def _recurseTree(self, current_hmitree_root, current_tc_root):
    49     def _recurseTree(self, current_hmitree_root, current_tc_root):
    42         for c in current_hmitree_root.children:
    50         for c in current_hmitree_root.children:
    43             if hasattr(c, "children"):
    51             if hasattr(c, "children"):
    44                 display_name = ('{} (class={})'.format(c.name, c.hmiclass)) \
    52                 display_name = ('{} (class={})'.format(c.name, c.hmiclass)) \
    49                 self._recurseTree(c,tc_child)
    57                 self._recurseTree(c,tc_child)
    50             else:
    58             else:
    51                 display_name = '{} {}'.format(c.nodetype[4:], c.name)
    59                 display_name = '{} {}'.format(c.nodetype[4:], c.name)
    52                 tc_child = self.AppendItem(current_tc_root, display_name)
    60                 tc_child = self.AppendItem(current_tc_root, display_name)
    53                 self.SetPyData(tc_child, c)
    61                 self.SetPyData(tc_child, c)
       
    62 
       
    63     def OnTreeNodeSelection(self, event):
       
    64         items = self.GetSelections()
       
    65         items_pydata = [self.GetPyData(item) for item in items]
       
    66 
       
    67         # append new items to ordered item list
       
    68         for item_pydata in items_pydata:
       
    69             if item_pydata not in self.ordered_items:
       
    70                 self.ordered_items.append(item_pydata)
       
    71 
       
    72         # filter out vanished items
       
    73         self.ordered_items = [
       
    74             item_pydata 
       
    75             for item_pydata in self.ordered_items 
       
    76             if item_pydata in items_pydata]
       
    77 
       
    78         self.parent.OnHMITreeNodeSelection(self.ordered_items)
       
    79 
       
    80     def OnTreeBeginDrag(self, event):
       
    81         """
       
    82         Called when a drag is started in tree
       
    83         @param event: wx.TreeEvent
       
    84         """
       
    85         if self.ordered_items:
       
    86             print("boink")
       
    87             # Just send a recognizable mime-type, drop destination
       
    88             # will get python data from parent
       
    89             data = wx.CustomDataObject(HMITreeDndMagicWord)
       
    90             dragSource = wx.DropSource(self)
       
    91             dragSource.SetData(data)
       
    92             dragSource.DoDragDrop()
    54 
    93 
    55     def MakeTree(self, hmi_tree_root=None):
    94     def MakeTree(self, hmi_tree_root=None):
    56 
    95 
    57         self.Freeze()
    96         self.Freeze()
    58 
    97 
   125             self._recurseTree(lib_dir, self.root, [])
   164             self._recurseTree(lib_dir, self.root, [])
   126             self.Expand(self.root)
   165             self.Expand(self.root)
   127 
   166 
   128         self.Thaw()
   167         self.Thaw()
   129 
   168 
   130 class PathEditor(wx.Panel):
   169 class PathDropTarget(wx.DropTarget):
   131     def __init__(self, parent, path):
   170 
   132 
   171     def __init__(self, parent):
   133         wx.Panel.__init__(self, parent)
   172         data = wx.CustomDataObject(HMITreeDndMagicWord)
   134         label = path.get("name") + ": " + path.text + "(" + path.get("accepts") + ")"
   173         wx.DropTarget.__init__(self, data)
       
   174         self.ParentWindow = parent
       
   175 
       
   176     def OnDrop(self, x, y):
       
   177         self.ParentWindow.OnHMITreeDnD()
       
   178         return True
       
   179 
       
   180 class ParamEditor(wx.Panel):
       
   181     def __init__(self, parent, paramdesc):
       
   182 
       
   183         wx.Panel.__init__(self, parent.main_panel)
       
   184         label = paramdesc.get("name")+ ": " + paramdesc.get("accepts") 
       
   185         if paramdesc.text:
       
   186             label += "\n\"" + paramdesc.text + "\""
   135         self.desc = wx.StaticText(self, label=label)
   187         self.desc = wx.StaticText(self, label=label)
   136         self.focus_sbmp = wx.StaticBitmap(self, -1, wx.ArtProvider.GetBitmap(wx.ART_GO_FORWARD, wx.ART_TOOLBAR, (32,32)))
   188         self.valid_bmp = wx.ArtProvider.GetBitmap(wx.ART_TICK_MARK, wx.ART_TOOLBAR, (16,16))
   137         self.valid_bmp = wx.ArtProvider.GetBitmap(wx.ART_TICK_MARK, wx.ART_TOOLBAR, (32,32))
   189         self.invalid_bmp = wx.ArtProvider.GetBitmap(wx.ART_CROSS_MARK, wx.ART_TOOLBAR, (16,16))
   138         self.invalid_bmp = wx.ArtProvider.GetBitmap(wx.ART_CROSS_MARK, wx.ART_TOOLBAR, (32,32))
       
   139         self.validity_sbmp = wx.StaticBitmap(self, -1, self.invalid_bmp)
   190         self.validity_sbmp = wx.StaticBitmap(self, -1, self.invalid_bmp)
   140         self.edit = wx.TextCtrl(self)
   191         self.edit = wx.TextCtrl(self)
   141         self.edit_sizer = wx.FlexGridSizer(cols=3, hgap=0, rows=1, vgap=0)
   192         self.edit_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0)
   142         self.edit_sizer.AddGrowableCol(1)
   193         self.edit_sizer.AddGrowableCol(0)
   143         self.edit_sizer.AddGrowableRow(0)
   194         self.edit_sizer.AddGrowableRow(0)
   144         self.edit_sizer.Add(self.focus_sbmp, flag=wx.GROW)
       
   145         self.edit_sizer.Add(self.edit, flag=wx.GROW)
   195         self.edit_sizer.Add(self.edit, flag=wx.GROW)
   146         self.edit_sizer.Add(self.validity_sbmp, flag=wx.GROW)
   196         self.edit_sizer.Add(self.validity_sbmp, flag=wx.GROW)
   147         self.main_sizer = wx.BoxSizer(wx.VERTICAL)
   197         self.main_sizer = wx.BoxSizer(wx.VERTICAL)
   148         self.main_sizer.Add(self.desc, flag=wx.GROW)
   198         self.main_sizer.Add(self.desc, flag=wx.GROW)
   149         self.main_sizer.Add(self.edit_sizer, flag=wx.GROW)
   199         self.main_sizer.Add(self.edit_sizer, flag=wx.GROW)
   150         self.SetSizer(self.main_sizer)
   200         self.SetSizer(self.main_sizer)
   151         self.main_sizer.Fit(self)
   201         self.main_sizer.Fit(self)
   152 
   202 
       
   203 class ArgEditor(ParamEditor):
       
   204     pass
       
   205 
       
   206 class PathEditor(ParamEditor):
       
   207     def __init__(self, parent, pathdesc):
       
   208         ParamEditor.__init__(self, parent, pathdesc)
       
   209         self.ParentObj = parent
       
   210         DropTarget = PathDropTarget(self)
       
   211         self.edit.SetDropTarget(DropTarget)
       
   212         self.Bind(wx.EVT_TEXT_ENTER, self.OnPathChanged, self.edit)
       
   213 
       
   214     def OnHMITreeDnD(self):
       
   215         self.ParentObj.GotPathDnDOn(self)
       
   216 
       
   217     def SetPathValue(self, value):
       
   218         self.edit.SetValue(value)
       
   219 
       
   220     def OnPathChanged(self, event):
       
   221         # TODO : update validity
       
   222         event.Skip()
       
   223     
       
   224 
   153 _conf_key = "SVGHMIWidgetLib"
   225 _conf_key = "SVGHMIWidgetLib"
   154 _preview_height = 200
   226 _preview_height = 200
   155 _preview_margin = 5
   227 _preview_margin = 5
   156 class WidgetLibBrowser(wx.SplitterWindow):
   228 class WidgetLibBrowser(wx.SplitterWindow):
   157     def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
   229     def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
   160         wx.SplitterWindow.__init__(self, parent,
   232         wx.SplitterWindow.__init__(self, parent,
   161                                    style=wx.SUNKEN_BORDER | wx.SP_3D)
   233                                    style=wx.SUNKEN_BORDER | wx.SP_3D)
   162 
   234 
   163         self.bmp = None
   235         self.bmp = None
   164         self.msg = None
   236         self.msg = None
   165         self.hmitree_node = None
   237         self.hmitree_nodes = []
   166         self.selected_SVG = None
   238         self.selected_SVG = None
   167 
   239 
   168         self.Config = wx.ConfigBase.Get()
   240         self.Config = wx.ConfigBase.Get()
   169         self.libdir = self.RecallLibDir()
   241         self.libdir = self.RecallLibDir()
   170 
   242 
   195 
   267 
   196         self.main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=0)
   268         self.main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=0)
   197         self.main_sizer.AddGrowableCol(0)
   269         self.main_sizer.AddGrowableCol(0)
   198         self.main_sizer.AddGrowableRow(2)
   270         self.main_sizer.AddGrowableRow(2)
   199 
   271 
       
   272         self.staticmsg = wx.StaticText(self, label = _("Drag selected Widget from here to Inkscape"))
   200         self.preview = wx.Panel(self.main_panel, size=(-1, _preview_height + _preview_margin*2))
   273         self.preview = wx.Panel(self.main_panel, size=(-1, _preview_height + _preview_margin*2))
   201         self.staticmsg = wx.StaticText(self)
       
   202         self.signature_sizer = wx.BoxSizer(wx.VERTICAL)
   274         self.signature_sizer = wx.BoxSizer(wx.VERTICAL)
       
   275         self.args_box = wx.StaticBox(self.main_panel, -1,
       
   276                                      _("Widget's arguments"),
       
   277                                      style = wx.ALIGN_RIGHT)
       
   278         self.args_sizer = wx.StaticBoxSizer(self.args_box, wx.VERTICAL)
       
   279         self.paths_box = wx.StaticBox(self.main_panel, -1,
       
   280                                       _("Widget's variables"),
       
   281                                       style = wx.ALIGN_RIGHT)
       
   282         self.paths_sizer = wx.StaticBoxSizer(self.paths_box, wx.VERTICAL)
       
   283         self.signature_sizer.Add(self.args_sizer, flag=wx.GROW)
       
   284         self.signature_sizer.AddSpacer(5)
       
   285         self.signature_sizer.Add(self.paths_sizer, flag=wx.GROW)
       
   286         self.main_sizer.Add(self.staticmsg, flag=wx.GROW)
   203         self.main_sizer.Add(self.preview, flag=wx.GROW)
   287         self.main_sizer.Add(self.preview, flag=wx.GROW)
   204         self.main_sizer.Add(self.staticmsg, flag=wx.GROW)
       
   205         self.main_sizer.Add(self.signature_sizer, flag=wx.GROW)
   288         self.main_sizer.Add(self.signature_sizer, flag=wx.GROW)
   206         self.main_sizer.Layout()
   289         self.main_sizer.Layout()
   207         self.main_panel.SetAutoLayout(True)
   290         self.main_panel.SetAutoLayout(True)
   208         self.main_panel.SetSizer(self.main_sizer)
   291         self.main_panel.SetSizer(self.main_sizer)
   209         self.main_sizer.Fit(self.main_panel)
   292         self.main_sizer.Fit(self.main_panel)
   214                                    style=wx.TE_READONLY | wx.TE_MULTILINE)
   297                                    style=wx.TE_READONLY | wx.TE_MULTILINE)
   215 
   298 
   216         self.picker_desc_splitter.SplitHorizontally(self.picker_panel, self.desc, 400)
   299         self.picker_desc_splitter.SplitHorizontally(self.picker_panel, self.desc, 400)
   217         self.SplitVertically(self.main_panel, self.picker_desc_splitter, 300)
   300         self.SplitVertically(self.main_panel, self.picker_desc_splitter, 300)
   218 
   301 
   219         self.msg = _("Drag selected Widget from here to Inkscape")
       
   220         self.tempf = None 
   302         self.tempf = None 
   221 
   303 
       
   304         self.args_editors = []
   222         self.paths_editors = []
   305         self.paths_editors = []
   223 
   306 
   224     def ResetSignature(self):
   307     def ResetSignature(self):
   225         self.signature_sizer.Clear()
   308         self.args_sizer.Clear()
       
   309         for editor in self.args_editors:
       
   310             editor.Destroy()
       
   311         self.args_editors = []
       
   312 
       
   313         self.paths_sizer.Clear()
   226         for editor in self.paths_editors:
   314         for editor in self.paths_editors:
   227             editor.Destroy()
   315             editor.Destroy()
   228         self.paths_editors = []
   316         self.paths_editors = []
   229 
   317 
       
   318     def AddArgToSignature(self, arg):
       
   319         new_editor = ArgEditor(self, arg)
       
   320         self.args_editors.append(new_editor)
       
   321         self.args_sizer.Add(new_editor, flag=wx.GROW)
       
   322 
   230     def AddPathToSignature(self, path):
   323     def AddPathToSignature(self, path):
   231         new_editor = PathEditor(self.main_panel, path)
   324         new_editor = PathEditor(self, path)
   232         self.paths_editors.append(new_editor)
   325         self.paths_editors.append(new_editor)
   233         self.signature_sizer.Add(new_editor, flag=wx.GROW)
   326         self.paths_sizer.Add(new_editor, flag=wx.GROW)
       
   327 
       
   328     def GotPathDnDOn(self, target_editor):
       
   329         dndindex = self.paths_editors.index(target_editor)
       
   330 
       
   331         for selected,editor in zip(self.hmitree_nodes,
       
   332                                    self.paths_editors[dndindex:]):
       
   333             editor.SetPath(selected.hmi_path())
   234 
   334 
   235     def RecallLibDir(self):
   335     def RecallLibDir(self):
   236         conf = self.Config.Read(_conf_key)
   336         conf = self.Config.Read(_conf_key)
   237         if len(conf) == 0:
   337         if len(conf) == 0:
   238             return None
   338             return None
   280         Called when Preview panel needs to be redrawn
   380         Called when Preview panel needs to be redrawn
   281         @param event: wx.PaintEvent
   381         @param event: wx.PaintEvent
   282         """
   382         """
   283         self.DrawPreview()
   383         self.DrawPreview()
   284         event.Skip()
   384         event.Skip()
   285 
       
   286         self.staticmsg.SetLabel(self.msg)
       
   287 
   385 
   288     def GenThumbnail(self, svgpath, thumbpath):
   386     def GenThumbnail(self, svgpath, thumbpath):
   289         inkpath = get_inkscape_path()
   387         inkpath = get_inkscape_path()
   290         if inkpath is None:
   388         if inkpath is None:
   291             self.msg = _("Inkscape is not installed.")
   389             self.msg = _("Inkscape is not installed.")
   333 
   431 
   334                 self.bmp = wx.Bitmap(thumbpath) if have_thumb else None
   432                 self.bmp = wx.Bitmap(thumbpath) if have_thumb else None
   335 
   433 
   336                 self.selected_SVG = svgpath if have_thumb else None
   434                 self.selected_SVG = svgpath if have_thumb else None
   337 
   435 
   338                 self.AnalyseWidgetAndUpdateUI()
   436                 self.AnalyseWidgetAndUpdateUI(fname)
       
   437 
       
   438                 if self.msg:
       
   439                     self.staticmsg.Show()
       
   440                     self.staticmsg.SetLabel(self.msg)
       
   441                 else:
       
   442                     self.staticmsg.Hide()
       
   443 
   339 
   444 
   340             except IOError:
   445             except IOError:
   341                 self.msg = _("Widget library must be writable")
   446                 self.msg = _("Widget library must be writable")
   342 
   447 
   343             self.Refresh()
   448             self.Refresh()
   344         event.Skip()
   449         event.Skip()
   345 
   450 
   346     def OnHMITreeNodeSelection(self, hmitree_nodes):
   451     def OnHMITreeNodeSelection(self, hmitree_nodes):
   347         self.hmitree_node = hmitree_nodes[0] if len(hmitree_nodes) else None
   452         self.hmitree_nodes = hmitree_nodes
   348         self.ValidateWidget()
   453         # [0] if len(hmitree_nodes) else None
   349         self.Refresh()
   454         # self.ValidateWidget()
       
   455         # self.Refresh()
   350 
   456 
   351     def OnLeftDown(self, evt):
   457     def OnLeftDown(self, evt):
   352         if self.tempf is not None:
   458         if self.tempf is not None:
   353             filename = self.tempf.name
   459             filename = self.tempf.name
   354             data = wx.FileDataObject()
   460             data = wx.FileDataObject()
   366             self.msg += msg.text + "\n"
   472             self.msg += msg.text + "\n"
   367 
   473 
   368     def GetSubHMITree(self, _context):
   474     def GetSubHMITree(self, _context):
   369         return [self.hmitree_node.etree()]
   475         return [self.hmitree_node.etree()]
   370 
   476 
   371     def AnalyseWidgetAndUpdateUI(self):
   477     def AnalyseWidgetAndUpdateUI(self, fname):
   372         self.msg = ""
   478         self.msg = ""
   373         self.ResetSignature()
   479         self.ResetSignature()
   374 
   480 
   375         try:
   481         try:
   376             if self.selected_SVG is None:
   482             if self.selected_SVG is None:
   387                 self.msg += "XSLT: " + entry.message + "\n" 
   493                 self.msg += "XSLT: " + entry.message + "\n" 
   388 
   494 
   389         except Exception as e:
   495         except Exception as e:
   390             self.msg += str(e)
   496             self.msg += str(e)
   391         except XSLTApplyError as e:
   497         except XSLTApplyError as e:
   392             self.msg += "Widget analysis error: " + e.message
   498             self.msg += "Widget " + fname + " analysis error: " + e.message
   393         else:
   499         else:
   394             
   500             
       
   501             self.msg += "Widget " + fname + ": OK"
   395 
   502 
   396             print(etree.tostring(signature, pretty_print=True))
   503             print(etree.tostring(signature, pretty_print=True))
   397             widgets = signature.getroot()
   504             widgets = signature.getroot()
   398             for defs in widgets.iter("defs"):
   505             for defs in widgets.iter("defs"):
   399 
   506 
   400                 # Keep double newlines (to mark paragraphs)
   507                 # Keep double newlines (to mark paragraphs)
   401                 self.desc.SetValue(defs.find("type").text + ":\n" + "\n\n".join(map(
   508                 self.desc.SetValue(defs.find("type").text + ":\n" + "\n\n".join(map(
   402                     lambda s:s.replace("\n"," ").replace("  ", " "),
   509                     lambda s:s.replace("\n"," ").replace("  ", " "),
   403                     defs.find("longdesc").text.split("\n\n"))))
   510                     defs.find("longdesc").text.split("\n\n"))))
   404                 for arg in defs.iter("arg"):
   511                 args = [arg for arg in defs.iter("arg")]
       
   512                 self.args_box.Show(len(args)!=0)
       
   513                 for arg in args:
       
   514                     self.AddArgToSignature(arg)
   405                     print(arg.get("name"))
   515                     print(arg.get("name"))
   406                     print(arg.get("accepts"))
   516                     print(arg.get("accepts"))
   407                 for path in defs.iter("path"):
   517                 paths = [path for path in defs.iter("path")]
       
   518                 self.paths_box.Show(len(paths)!=0)
       
   519                 for path in paths:
   408                     self.AddPathToSignature(path)
   520                     self.AddPathToSignature(path)
   409                     print(path.get("name"))
   521                     print(path.get("name"))
   410                     print(path.get("accepts"))
   522                     print(path.get("accepts"))
   411 
   523 
   412             for widget in widgets:
   524             for widget in widgets:
   465 
   577 
   466     def __init__(self, parent, register_for_HMI_tree_updates):
   578     def __init__(self, parent, register_for_HMI_tree_updates):
   467         wx.SplitterWindow.__init__(self, parent,
   579         wx.SplitterWindow.__init__(self, parent,
   468                                    style=wx.SUNKEN_BORDER | wx.SP_3D)
   580                                    style=wx.SUNKEN_BORDER | wx.SP_3D)
   469 
   581 
   470         self.ordered_items = []
       
   471 
       
   472         self.SelectionTree = HMITreeSelector(self)
   582         self.SelectionTree = HMITreeSelector(self)
   473         self.Staging = WidgetLibBrowser(self)
   583         self.Staging = WidgetLibBrowser(self)
   474         self.SplitVertically(self.SelectionTree, self.Staging, 300)
   584         self.SplitVertically(self.SelectionTree, self.Staging, 300)
   475         register_for_HMI_tree_updates(weakref.ref(self))
   585         register_for_HMI_tree_updates(weakref.ref(self))
   476         self.Bind(wx.EVT_TREE_SEL_CHANGED,
       
   477             self.OnHMITreeNodeSelection, self.SelectionTree)
       
   478 
       
   479     def OnHMITreeNodeSelection(self, event):
       
   480         items = self.SelectionTree.GetSelections()
       
   481         items_pydata = [self.SelectionTree.GetPyData(item) for item in items]
       
   482 
       
   483         # append new items to ordered item list
       
   484         for item_pydata in items_pydata:
       
   485             if item_pydata not in self.ordered_items:
       
   486                 self.ordered_items.append(item_pydata)
       
   487 
       
   488         # filter out vanished items
       
   489         self.ordered_items = [
       
   490             item_pydata 
       
   491             for item_pydata in self.ordered_items 
       
   492             if item_pydata in items_pydata]
       
   493 
       
   494         self.Staging.OnHMITreeNodeSelection(items_pydata)
       
   495 
   586 
   496     def HMITreeUpdate(self, hmi_tree_root):
   587     def HMITreeUpdate(self, hmi_tree_root):
   497             self.SelectionTree.MakeTree(hmi_tree_root)
   588         self.SelectionTree.MakeTree(hmi_tree_root)
   498 
   589 
       
   590     def OnHMITreeNodeSelection(self, hmitree_nodes):
       
   591         self.Staging.OnHMITreeNodeSelection(hmitree_nodes)