IDE, SVGHMI: Workaround Snap package not launching Inskape, POEdit or Chromium directly, producing lot of output in Incskape CLI, and messing with TMPDIR wxPython4
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Mon, 08 Aug 2022 18:09:26 +0200
branchwxPython4
changeset 3573 1ee56fb544fc
parent 3572 b46af5b80c7d
child 3574 889cbde22ec4
IDE, SVGHMI: Workaround Snap package not launching Inskape, POEdit or Chromium directly, producing lot of output in Incskape CLI, and messing with TMPDIR
dialogs/MessageBoxOnce.py
dialogs/__init__.py
docutil/docsvg.py
svghmi/i18n.py
svghmi/svghmi.py
svghmi/ui.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dialogs/MessageBoxOnce.py	Mon Aug 08 18:09:26 2022 +0200
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of Beremiz
+# Copyright (C) 2022: Edouard TISSERANT
+#
+# See COPYING file for copyrights details.
+
+
+from __future__ import absolute_import
+import wx
+
+
+# class RichMessageDialog is still not available in wxPython 3.0.2
+class  MessageBoxOnce(wx.Dialog):
+    """
+    wx.MessageBox that user can ask not to show again
+    """
+    def __init__(self, title, message, config_key):
+        self.Config = wx.ConfigBase.Get()
+        self.config_key = config_key
+        dont_show = self.Config.Read(self.config_key) == "True"
+
+        if dont_show:
+            return
+
+        wx.Dialog.__init__(self, None, title=title)
+
+        main_sizer = wx.BoxSizer(wx.VERTICAL)
+
+        message = wx.StaticText(self, label=message)
+        main_sizer.Add(message, border=20,
+            flag=wx.ALIGN_CENTER_HORIZONTAL | wx.TOP | wx.LEFT | wx.RIGHT)
+
+        self.check = wx.CheckBox(self, label=_("don't show this message again"))
+        main_sizer.Add(self.check, border=20,
+            flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_CENTER_HORIZONTAL)
+
+        buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)
+
+        Button = wx.Button(self, label="OK")
+
+        self.Bind(wx.EVT_BUTTON, self.OnOKButton, Button)
+        buttons_sizer.Add(Button)
+
+        main_sizer.Add(buttons_sizer, border=20,
+                            flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_RIGHT)
+
+        self.SetSizer(main_sizer)
+        self.Fit()
+
+        self.ShowModal()
+
+    def OnOKButton(self, event):
+        if self.check.GetValue():
+            self.Config.Write(self.config_key, "True")
+        self.EndModal(wx.ID_OK)
--- a/dialogs/__init__.py	Mon Aug 08 18:07:23 2022 +0200
+++ b/dialogs/__init__.py	Mon Aug 08 18:09:26 2022 +0200
@@ -51,3 +51,4 @@
 from dialogs.BrowseValuesLibraryDialog import BrowseValuesLibraryDialog
 from dialogs.UriEditor import UriEditor
 from dialogs.IDManager import IDManager
+from dialogs.MessageBoxOnce import MessageBoxOnce
--- a/docutil/docsvg.py	Mon Aug 08 18:07:23 2022 +0200
+++ b/docutil/docsvg.py	Mon Aug 08 18:09:26 2022 +0200
@@ -24,8 +24,10 @@
 
 
 from __future__ import absolute_import
+import os
 import wx
 import subprocess
+from dialogs import MessageBoxOnce
 
 
 def _get_inkscape_path():
@@ -86,11 +88,23 @@
     _inkscape_version = _get_inkscape_version()
     return _inkscape_version
 
-def open_svg(svgfile):
-    """ Generic function to open SVG file """
-    
-    inkpath = get_inkscape_path()
-    if inkpath is None:
-        wx.MessageBox("Inkscape is not found or installed !")
-    else:
-        subprocess.Popen([inkpath,svgfile])
+if os.environ.has_key("SNAP"):
+    def open_svg(svgfile):
+        MessageBoxOnce("Launching Inkscape with xdg-open",
+                "Confined app can't launch Inkscape directly.\n"+
+                    "Instead, SVG file is passed to xdg-open.\n"+
+                    "Please select Inskape when proposed.\n\n"+
+                    "Notes: \n"+
+                    " - Inkscape must be installed on you system.\n"+
+                    " - If no choice is proposed, use file manager to change SVG file properties.\n",
+                "DocSVGSnapWarning")
+
+        subprocess.Popen(["xdg-open",svgfile])
+else:
+    def open_svg(svgfile):
+        """ Generic function to open SVG file """
+        inkpath = get_inkscape_path()
+        if inkpath is None:
+            wx.MessageBox("Inkscape is not found or installed !")
+        else:
+            subprocess.Popen([inkpath,svgfile])
--- a/svghmi/i18n.py	Mon Aug 08 18:07:23 2022 +0200
+++ b/svghmi/i18n.py	Mon Aug 08 18:09:26 2022 +0200
@@ -20,6 +20,7 @@
 # https://pypi.org/project/pycountry/18.12.8/
 # python2 -m pip install pycountry==18.12.8 --user
 import pycountry
+from dialogs import MessageBoxOnce
 
 cmd_parser = re.compile(r'(?:"([^"]+)"\s*|([^\s]+)\s*)?')
 
@@ -39,10 +40,21 @@
             poedit_path = None
 
     else:
-        try:
-            poedit_path = subprocess.check_output("command -v poedit", shell=True).strip()
-        except subprocess.CalledProcessError:
-            poedit_path = None
+        if os.environ.has_key("SNAP"):
+            MessageBoxOnce("Launching POEdit with xdg-open",
+                    "Confined app can't launch POEdit directly.\n"+
+                        "Instead, PO/POT file is passed to xdg-open.\n"+
+                        "Please select POEdit when proposed.\n\n"+
+                    "Notes: \n"+
+                    " - POEdit must be installed on you system.\n"+
+                    " - If no choice is proposed, use file manager to change POT/PO file properties.\n",
+                    "SVGHMII18SnapWarning")
+            poedit_path = "xdg-open"
+        else:
+            try:
+                poedit_path = subprocess.check_output("command -v poedit", shell=True).strip()
+            except subprocess.CalledProcessError:
+                poedit_path = None
 
     if poedit_path is None:
         wx.MessageBox("POEdit is not found or installed !")
--- a/svghmi/svghmi.py	Mon Aug 08 18:07:23 2022 +0200
+++ b/svghmi/svghmi.py	Mon Aug 08 18:09:26 2022 +0200
@@ -8,6 +8,7 @@
 
 from __future__ import absolute_import
 import os
+import sys
 import shutil
 import hashlib
 import shlex
@@ -302,10 +303,14 @@
 
         return ret
 
-if wx.Platform == '__WXMSW__':
+if sys.platform.startswith('win'):
     default_cmds={
         "launch":"cmd.exe /c 'start msedge {url}'",
         "watchdog":"cmd.exe /k 'echo watchdog for {url} !'"}
+elif os.environ.has_key("SNAP"):
+    default_cmds={
+        "launch":"xdg-open {url}",
+        "watchdog":"echo Watchdog for {name} !"}
 else:
     default_cmds={
         "launch":"chromium {url}",
--- a/svghmi/ui.py	Mon Aug 08 18:07:23 2022 +0200
+++ b/svghmi/ui.py	Mon Aug 08 18:09:26 2022 +0200
@@ -31,6 +31,19 @@
 
 from util.ProcessLogger import ProcessLogger
 
+# When running as a confined Snap, /tmp isn't accessible from the outside
+# and Widget DnD to Inkscape can't work, since it can't find generated svg 
+# This forces tmp directory in $SNAP_USER_DATA, accessible from other apps
+if os.environ.has_key("SNAP"):
+     NamedTemporaryFile_orig = NamedTemporaryFile
+     tmpdir = os.path.join(os.environ["SNAP_USER_DATA"], ".tmp")
+     if not os.path.exists(tmpdir):
+         os.mkdir(tmpdir)
+     def NamedTemporaryFile(*args,**kwargs):
+        kwargs["dir"] = tmpdir
+        return NamedTemporaryFile_orig(*args, **kwargs)
+
+
 ScriptDirectory = paths.AbsDir(__file__)
 
 HMITreeDndMagicWord = "text/beremiz-hmitree"
@@ -464,7 +477,8 @@
 
         # TODO: spawn a thread, to decouple thumbnail gen
         status, result, _err_result = ProcessLogger(
-            self.Controler.GetCTRoot().logger,
+            #self.Controler.GetCTRoot().logger,
+            None,
             '"' + inkpath + '" "' + svgpath + '" ' +
             export_opt + ' "' + thumbpath +
             '" -D -h ' + str(_preview_height)).spin()