allow to select frontend via command-line; add a GUI-less frontend for scripted actions
authorRalf Jung <post@ralfj.de>
Wed, 14 Nov 2012 14:00:13 +0000 (15:00 +0100)
committerRalf Jung <post@ralfj.de>
Wed, 14 Nov 2012 14:00:13 +0000 (15:00 +0100)
dsl.py
gui.py

diff --git a/dsl.py b/dsl.py
index d13d0a0f8dc058c008fe2e12dbc7393624cb242b..b4f77bf413391f2bf4b7e01d835c80b76b695299 100755 (executable)
--- a/dsl.py
+++ b/dsl.py
@@ -17,7 +17,8 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 import argparse, sys, os, re, subprocess
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 import argparse, sys, os, re, subprocess
-import gui
+from gui import getFrontend
+frontend = getFrontend("cli") # the fallback, until we got a proper frontend. This is guaranteed to be available.
 
 # for auto-config: common names of internal connectors
 commonInternalConnectorNames = ['LVDS', 'LVDS0', 'LVDS1', 'LVDS-0', 'LVDS-1']
 
 # for auto-config: common names of internal connectors
 commonInternalConnectorNames = ['LVDS', 'LVDS0', 'LVDS1', 'LVDS-0', 'LVDS-1']
@@ -182,11 +183,17 @@ if __name__ == "__main__":
        try:
                # parse command-line arguments
                parser = argparse.ArgumentParser(description='easy Display Setup for Laptops')
        try:
                # parse command-line arguments
                parser = argparse.ArgumentParser(description='easy Display Setup for Laptops')
+               parser.add_argument("-f, --frontend",
+                                                       dest="frontend",
+                                                       help="The frontend to be used for user interaction")
                parser.add_argument("-r, --relative-position",
                                                        dest="rel_position", choices=('left', 'right', 'external-only'),
                                                        help="Position of external screen relative to internal one")
                cmdArgs = parser.parse_args()
                
                parser.add_argument("-r, --relative-position",
                                                        dest="rel_position", choices=('left', 'right', 'external-only'),
                                                        help="Position of external screen relative to internal one")
                cmdArgs = parser.parse_args()
                
+               # load frontend
+               frontend = getFrontend(cmdArgs.frontend)
+               
                # load connectors and classify them
                connectors = getXrandrInformation()
                (internalConnector, externalConnectors) = classifyConnectors(connectors)
                # load connectors and classify them
                connectors = getXrandrInformation()
                (internalConnector, externalConnectors) = classifyConnectors(connectors)
@@ -212,7 +219,7 @@ if __name__ == "__main__":
                                setup = ScreenSetup(relPosition, connectors[internalConnector][0], connectors[usedExternalConnector][0]) # use default resolutions
                        else:
                                # use GUI
                                setup = ScreenSetup(relPosition, connectors[internalConnector][0], connectors[usedExternalConnector][0]) # use default resolutions
                        else:
                                # use GUI
-                               setup = gui.setup(connectors[internalConnector], connectors[usedExternalConnector])
+                               setup = frontend.setup(connectors[internalConnector], connectors[usedExternalConnector])
                        if setup is None: sys.exit(1) # the user canceled
                        # apply it
                        connectorArgs[internalConnector] = setup.getInternalArgs()
                        if setup is None: sys.exit(1) # the user canceled
                        # apply it
                        connectorArgs[internalConnector] = setup.getInternalArgs()
@@ -228,5 +235,5 @@ if __name__ == "__main__":
                print "Call that will be made:",call
                subprocess.check_call(call)
        except Exception as e:
                print "Call that will be made:",call
                subprocess.check_call(call)
        except Exception as e:
-               gui.error(str(e))
+               frontend.error(str(e))
                raise
                raise
diff --git a/gui.py b/gui.py
index 6dabe0f048df2f0e47bc8dfa363a785bcc3266f2..a87b82f1d69bec3302e07e950ccb5ca5da56aca4 100644 (file)
--- a/gui.py
+++ b/gui.py
@@ -29,42 +29,81 @@ def setup(internalResolutions, externalResolutions):
        The user should be asked about his display setup preferences.
        The function returns None if the user cancelled, and an instance of dsl.ScreenSetup otherwise.
 '''
        The user should be asked about his display setup preferences.
        The function returns None if the user cancelled, and an instance of dsl.ScreenSetup otherwise.
 '''
+import subprocess, collections
 
 
-# frontend detectors
-def qtAvailable():
-       try:
-               import PyQt4
-               return True
-       except ImportError:
-               return False
-
-def zenityAvailable():
-       try:
-               from dsl import processOutputIt
-               processOutputIt("zenity", "--version")
-               return True
-       except Exception:
-               return False
-
-# actual frontends
-if qtAvailable():
-       from PyQt4 import QtGui
-       from qt_dialogue import PositionSelection
-       app = QtGui.QApplication(sys.argv)
+# Qt frontend
+class QtFrontend:
+       def __init__(self):
+               from PyQt4 import QtGui
+               self.app = QtGui.QApplication(sys.argv)
+               print "Qt loaded"
        
        
-       def error(message):
+       def error(self, message):
+               from PyQt4 import QtGui
                QtGui.QMessageBox.critical(None, 'Fatal error', message)
        
                QtGui.QMessageBox.critical(None, 'Fatal error', message)
        
-       def setup(internalResolutions, externalResolutions):
+       def setup(self, internalResolutions, externalResolutions):
+               from qt_dialogue import PositionSelection
                return PositionSelection(internalResolutions, externalResolutions).run()
                return PositionSelection(internalResolutions, externalResolutions).run()
-
-elif zenityAvailable():
-       import subprocess
-       from zenity_dialogue import run as setup # this provides the setup function
        
        
+       @staticmethod
+       def isAvailable():
+               try:
+                       import PyQt4
+                       return True
+               except ImportError:
+                       return False
+
+
+# Zenity frontend
+class ZenityFrontend:
        def error(message):
                '''Displays a fatal error to the user'''
                subprocess.check_call(["zenity", "--error", "--text="+message])
        def error(message):
                '''Displays a fatal error to the user'''
                subprocess.check_call(["zenity", "--error", "--text="+message])
+       
+       def setup(self, internalResolutions, externalResolutions):
+               from zenity_dialogue import run
+               run(internalResolutions, externalResolutions)
+       
+       @staticmethod
+       def isAvailable():
+               try:
+                       from dsl import processOutputIt
+                       processOutputIt("zenity", "--version")
+                       return True
+               except Exception:
+                       return False
+
+
+# CLI frontend
+class CLIFrontend:
+       def error(self, message):
+               print >> sys.stderr, message
+       
+       def setup(self, internalResolutions, externalResolutions):
+               raise Exception("Choosing the setup interactively is not supported with the CLI frontend")
+       
+       @staticmethod
+       def isAvailable():
+               return True
+
+# list of available frontends
+frontends = collections.OrderedDict()
+frontends["qt"] = QtFrontend
+frontends["zenity"] = ZenityFrontend
+frontends["cli"] = CLIFrontend
 
 
-else:
-       print >> sys.stderr, 'No GUI frontend available, please make sure PyQt4 or Zenity is installed'
+# get a frontend
+def getFrontend(name = None):
+       # by name
+       if name is not None:
+               if name in frontends:
+                       if frontends[name].isAvailable():
+                               return frontends[name]() # call constructor
+               # frontend not found or not available
+               raise Exception("Frontend %s not found or not available" % name)
+       # auto-detect
+       for frontend in frontends.values():
+               if frontend.isAvailable():
+                       return frontend() # call constructor
+       raise Exception("No frontend is available - this should not happen")