commit 62d4a1a9ae618fa0011a1897e13480bd5e98657c
parent 6d662ed52e0d3268dbdc9a3f1bec69add18da6ef
Author: Suzanne Soy <ligo@suzanne.soy>
Date: Thu, 21 Jan 2021 23:43:39 +0000
02020-01-21 stream: Moved list of apps and X11 utilities to separate files; loop over list of apps to create workbenches (but not the commands yet)
Diffstat:
| M | Embed.py | | | 73 | +++++++++++++++---------------------------------------------------------- |
| A | ExternalAppsList.py | | | 40 | ++++++++++++++++++++++++++++++++++++++++ |
| M | InitGui.py | | | 40 | +++++++++++++++++++++++++++++++++------- |
| A | MyX11Utils.py | | | 28 | ++++++++++++++++++++++++++++ |
4 files changed, 116 insertions(+), 65 deletions(-)
diff --git a/Embed.py b/Embed.py
@@ -6,37 +6,13 @@ import re
from PySide import QtGui
from PySide import QtCore
-class Tool():
- def __init__(self, name, *, start_command_and_args, xwininfo_filter_re, extra_xprop_filter):
- self.name = name
- self.start_command_and_args = start_command_and_args
- self.xwininfo_filter_re = re.compile(xwininfo_filter_re)
- self.extra_xprop_filter = extra_xprop_filter
-
-class Tools():
- def __init__(self, *tools):
- self.__dict__ = {tool.name: tool for tool in tools}
- def __getitem__(self, k): return self.__dict__[k]
-
-# tool-specific infos:
-tools = Tools(
- Tool('Mousepad',
- start_command_and_args = ['mousepad', '--disable-server'],
- xwininfo_filter_re = r'mousepad',
- extra_xprop_filter = lambda processId, windowId, i: True),
- Tool('Inkscape',
- start_command_and_args = ['inkscape'],
- xwininfo_filter_re = r'inkscape',
- extra_xprop_filter = lambda processId, windowId, i: x11prop(windowId, 'WM_STATE', 'WM_STATE') is not None),
- Tool('GIMP',
- start_command_and_args = ['env', '-i', 'DISPLAY=:0', '/home/suzanne/perso/dotfiles/nix/result/bin/gimp', '--new-instance'],
- xwininfo_filter_re = r'gimp',
- extra_xprop_filter = lambda processId, windowId, i: x11prop(windowId, 'WM_STATE', 'WM_STATE') is not None))
+import ExternalAppsList
+from MyX11Utils import *
class EmbeddedWindow(QtCore.QObject):
- def __init__(self, tool, externalAppInstance, processId, windowId):
+ def __init__(self, app, externalAppInstance, processId, windowId):
super(EmbeddedWindow, self).__init__()
- self.tool = tool
+ self.app = app
self.externalAppInstance = externalAppInstance
self.processId = processId
self.windowId = windowId
@@ -50,7 +26,7 @@ class EmbeddedWindow(QtCore.QObject):
self.xwd.setBaseSize(640,480)
self.mwx.setBaseSize(640,480)
self.mdiSub.setBaseSize(640,480)
- self.mdiSub.setWindowTitle(tool.name)
+ self.mdiSub.setWindowTitle(app.name)
self.mdiSub.show()
#self.xw.installEventFilter(self)
def eventFilter(self, obj, event):
@@ -63,25 +39,6 @@ class EmbeddedWindow(QtCore.QObject):
# <optional spaces> <digits (captured in group 1)> <optional spaces> "<quoted string>" <optional spaces> : <anything>
xwininfo_re = re.compile(r'^\s*([0-9]+)\s*"[^"]*"\s*:.*$')
-def x11stillAlive(windowId):
- try:
- subprocess.check_output(['xprop', '-id', str(windowId), '_NET_WM_PID'])
- return True
- except:
- return False
-
-def x11prop(windowId, prop, type):
- try:
- output = subprocess.check_output(['xprop', '-id', str(windowId), prop]).decode('utf-8', 'ignore').split('\n')
- except subprocess.CalledProcessError as e:
- output = []
- xprop_re = re.compile(r'^' + re.escape(prop) + r'\(' + re.escape(type) + r'\)((:)| =(.*))$')
- for line in output:
- trymatch = xprop_re.match(line)
- if trymatch:
- return trymatch.group(2) or trymatch.group(3)
- return None
-
def try_pipe_lines(commandAndArguments):
try:
return subprocess.check_output(commandAndArguments).decode('utf-8', 'ignore').split('\n')
@@ -101,17 +58,17 @@ def deleted(widget):
return True
class ExternalAppInstance(QtCore.QObject):
- def __init__(self, toolName):
+ def __init__(self, appName):
super(ExternalAppInstance, self).__init__()
- self.tool = tools[toolName]
+ self.app = ExternalAppsList.apps[appName]
# Start the application
# TODO: popen_process shouldn't be exposed to in-document scripts, it would allow them to redirect output etc.
- print('Starting ' + ' '.join(self.tool.start_command_and_args))
- self.popen_process = subprocess.Popen(self.tool.start_command_and_args)
- self.toolProcessIds = [self.popen_process.pid]
+ print('Starting ' + ' '.join(self.app.start_command_and_args))
+ self.popen_process = subprocess.Popen(self.app.start_command_and_args)
+ self.appProcessIds = [self.popen_process.pid]
self.initWaitForWindow()
self.foundWindows = dict()
- setattr(FreeCAD.ExternalApps, self.tool.name, self)
+ setattr(FreeCAD.ExternalApps, self.app.name, self)
def initWaitForWindow(self):
self.TimeoutHasOccurred = False # for other scritps to know the status
@@ -135,14 +92,14 @@ class ExternalAppInstance(QtCore.QObject):
def attemptToFindWindowWrapped(self):
# use decode('utf-8', 'ignore') to use strings instead of byte strings and discard ill-formed unicode in case these tool doesn't sanitize their output
for line in try_pipe_lines(['xwininfo', '-root', '-tree', '-int']):
- if self.tool.xwininfo_filter_re.search(line):
+ if self.app.xwininfo_filter_re.search(line):
windowId = int(xwininfo_re.match(line).group(1))
# use decode('utf-8', 'ignore') to use strings instead of byte strings and discard ill-formed unicode in case this tool doesn't sanitize their output
xprop_try_process_id = x11prop(windowId, '_NET_WM_PID', 'CARDINAL')
if xprop_try_process_id:
processId = int(xprop_try_process_id) # TODO try parse int and catch failure
- if processId in self.toolProcessIds:
- if self.tool.extra_xprop_filter(processId, windowId, len(self.foundWindows)):
+ if processId in self.appProcessIds:
+ if self.app.extra_xprop_filter(processId, windowId, len(self.foundWindows)):
self.foundWindow(processId, windowId)
if self.elapsed.elapsed() > self.startupTimeout:
@@ -151,7 +108,7 @@ class ExternalAppInstance(QtCore.QObject):
def foundWindow(self, processId, windowId):
if windowId not in self.foundWindows.keys():
- self.foundWindows[windowId] = EmbeddedWindow(self.tool, self, processId, windowId)
+ self.foundWindows[windowId] = EmbeddedWindow(self.app, self, processId, windowId)
# TODO: find an event instead of polling
for w in self.foundWindows.values():
#if not deleted(xw) and not xw.isActive():
diff --git a/ExternalAppsList.py b/ExternalAppsList.py
@@ -0,0 +1,40 @@
+import FreeCAD
+import FreeCADGui as Gui
+import subprocess
+import PySide
+import re
+from PySide import QtGui
+from PySide import QtCore
+
+from MyX11Utils import *
+
+class App():
+ def __init__(self, name, *, start_command_and_args, xwininfo_filter_re, extra_xprop_filter):
+ self.name = name
+ self.start_command_and_args = start_command_and_args
+ self.xwininfo_filter_re = re.compile(xwininfo_filter_re)
+ self.extra_xprop_filter = extra_xprop_filter
+
+class Apps():
+ def __init__(self, *apps):
+ # TODO: make this private
+ self.apps = {app.name: app for app in apps}
+ def __getitem__(self, k):
+ return self.apps[k]
+ def __iter__(self):
+ return self.apps.__iter__()
+
+# app-specific infos:
+apps = Apps(
+ App('Mousepad',
+ start_command_and_args = ['mousepad', '--disable-server'],
+ xwininfo_filter_re = r'mousepad',
+ extra_xprop_filter = lambda processId, windowId, i: True),
+ App('Inkscape',
+ start_command_and_args = ['inkscape'],
+ xwininfo_filter_re = r'inkscape',
+ extra_xprop_filter = lambda processId, windowId, i: x11prop(windowId, 'WM_STATE', 'WM_STATE') is not None),
+ App('GIMP',
+ start_command_and_args = ['env', '-i', 'DISPLAY=:0', '/home/suzanne/perso/dotfiles/nix/result/bin/gimp', '--new-instance'],
+ xwininfo_filter_re = r'gimp',
+ extra_xprop_filter = lambda processId, windowId, i: x11prop(windowId, 'WM_STATE', 'WM_STATE') is not None))
diff --git a/InitGui.py b/InitGui.py
@@ -1,9 +1,10 @@
import sys
-class XternalAppsWorkbench(Workbench):
- MenuText = "XternalApps"
- ToolTip = "Embeds external Applications in FreeCAD"
- Icon = """
+print("AAA")
+import ExternalAppsList
+print("BBB")
+
+myIcon = """
/* XPM */
static char * icon_xpm[] = {
"16 16 15 1",
@@ -40,8 +41,20 @@ class XternalAppsWorkbench(Workbench):
"................"};
"""
+print("CCC")
+class XternalAppsWorkbench(Workbench):
+ """Subclasses must implement the appName attribute"""
+ global myIcon
+ global XternalAppsWorkbench
+
+ ToolTip = "Embeds external Applications in FreeCAD"
+ Icon = myIcon
+
def __init__(self):
- super(self.__class__, self).__init__()
+ print('inside XternalAppsWorkbench __init__()')
+ self.MenuText = "XternalApps: " + self.appName
+ super(XternalAppsWorkbench, self).__init__()
+ print('finished XternalAppsWorkbench __init__()')
def Initialize(self):
print('Initialize')
@@ -52,7 +65,7 @@ class XternalAppsWorkbench(Workbench):
import GIMPCommand
import Embed
Embed.ExternalApps()
- self.list = ['GIMPCommand']
+ self.list = [self.appName + 'Command']
self.appendMenu("ExternalApplications", self.list)
self.appendToolbar("ExternalApplications", self.list)
@@ -70,4 +83,17 @@ class XternalAppsWorkbench(Workbench):
def GetClassName(self):
return "Gui::PythonWorkbench"
-Gui.addWorkbench(XternalAppsWorkbench())
+print("DDD")
+def addAppWorkbench(appName):
+ workbenchClass = type(
+ "XternalApps" + appName + "Workbench",
+ (XternalAppsWorkbench,), { 'appName': appName })
+ Gui.addWorkbench(workbenchClass())
+
+print("EEE")
+print(repr(dir(ExternalAppsList)))
+print(repr(ExternalAppsList.apps))
+for app in ExternalAppsList.apps:
+ print("FFF " + repr(app))
+ addAppWorkbench(app)
+print("GGG")
diff --git a/MyX11Utils.py b/MyX11Utils.py
@@ -0,0 +1,28 @@
+import FreeCAD
+import FreeCADGui as Gui
+import subprocess
+import PySide
+import re
+from PySide import QtGui
+from PySide import QtCore
+
+import ExternalAppsList
+
+def x11stillAlive(windowId):
+ try:
+ subprocess.check_output(['xprop', '-id', str(windowId), '_NET_WM_PID'])
+ return True
+ except:
+ return False
+
+def x11prop(windowId, prop, type):
+ try:
+ output = subprocess.check_output(['xprop', '-id', str(windowId), prop]).decode('utf-8', 'ignore').split('\n')
+ except subprocess.CalledProcessError as e:
+ output = []
+ xprop_re = re.compile(r'^' + re.escape(prop) + r'\(' + re.escape(type) + r'\)((:)| =(.*))$')
+ for line in output:
+ trymatch = xprop_re.match(line)
+ if trymatch:
+ return trymatch.group(2) or trymatch.group(3)
+ return None