From: Constantin Berhard Date: Tue, 7 Jul 2015 14:33:56 +0000 (+0200) Subject: updated zenity frontend, added cli frontend X-Git-Url: https://git.ralfj.de/lilass.git/commitdiff_plain/6fd92bb21b1a743d0a794141277a7aea857d3c50?ds=sidebyside updated zenity frontend, added cli frontend --- diff --git a/gui.py b/gui.py index 998783f..d586442 100644 --- a/gui.py +++ b/gui.py @@ -31,6 +31,9 @@ def setup(internalResolutions, externalResolutions): ''' import subprocess, collections +from question_frontend import QuestionFrontend +from screen import processOutputIt + # Qt frontend class QtFrontend: def __init__(self): @@ -56,33 +59,57 @@ class QtFrontend: # Zenity frontend -class ZenityFrontend: - def error(message): +class ZenityFrontend(QuestionFrontend): + def error(self, message): '''Displays a fatal error to the user''' subprocess.check_call(["zenity", "--error", "--text="+message]) - - def setup(self, situation): - from zenity_dialogue import run - return run(situation.internalResolutions(), situation.externalResolutions()) - - @staticmethod + def userChoose (self, title, choices, returns, fallback): + assert len(choices) == len(returns) + args = ["zenity", "--list", "--text="+title, "--column="]+choices + switch = dict (list(zip (choices,returns))) + try: + for line in processOutputIt(*args): + return switch.get(line.strip(), fallback) + except Exception: + # on user cancel, the return code of zenity is nonzero + return fallback + # if the output was empty + return fallback def isAvailable(): try: - from screen import processOutputIt processOutputIt("zenity", "--version") return True - except Exception: + except FileNotFoundError: + return False + except PermissionError: return False - # CLI frontend -class CLIFrontend: +class CLIFrontend(QuestionFrontend): def error(self, message): print(message, file=sys.stderr) - - def setup(self, internalResolutions, externalResolutions, commonRes): - raise Exception("Choosing the setup interactively is not supported with the CLI frontend") - + + def userChoose (self, title, choices, returns, fallback): + while True: + # print question + print(title) + for i in range(len(choices)): + print("%d. %s"%(i,choices[i])) + print("Enter 'c' to cancel.") + # handle input + answer = input("> ") + if answer == "c": + return None + #else + try: + answerint = int(answer) + if answerint >= 0 and answerint < len(choices): + return returns[answerint] + except ValueError: + pass + # if we are here something invalid was entered + print("INVALID ANSWER: '%s'" % answer) + @staticmethod def isAvailable(): return True @@ -100,8 +127,10 @@ def getFrontend(name = 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) + else: + raise Exception("Frontend %s not available" % name) + # frontend not found + raise Exception("Frontend %s not found" % name) # auto-detect for frontend in frontends.values(): if frontend.isAvailable(): diff --git a/question_frontend.py b/question_frontend.py new file mode 100644 index 0000000..4782ba1 --- /dev/null +++ b/question_frontend.py @@ -0,0 +1,81 @@ +# DSL - easy Display Setup for Laptops +# Copyright (C) 2012 Ralf Jung +# Copyright (C) 2012-2015 Constantin Berhard +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from screen import RelativeScreenPosition, ScreenSetup, processOutputIt + +from enum import Enum + +class OperationMode(Enum): + INTERNAL_ONLY = ("Use internal display only") + EXTERNAL_ONLY = ("Use external display only") + USE_BOTH = ("Use both displays") + def __init__(self, text): + # auto numbering + cls = self.__class__ + self._value_ = len(cls.__members__) + # description + self.text = text + +class QuestionFrontend: + def userChoose (self, title, choices, returns, fallback): + raise Exception("The abstract method 'userChoose' has not been implemented by %s"%str(self.__class__)) + def selectResolution(self, displayname, availablemodes): + modedescs = list(map(str, availablemodes)) + return self.userChoose("Select resolution for %s"%displayname, modedescs, availablemodes, None) + def setup (self, situation): + operationmodes = list(OperationMode) + operationmodedescs = list(map(lambda x: x.text, operationmodes)) + operationmode = self.userChoose ("Display setup", operationmodedescs, operationmodes, None) + if operationmode is None: + return None + elif operationmode is OperationMode.INTERNAL_ONLY: + intres = self.selectResolution("the internal screen", situation.internalResolutions()) + if intres is None: + return None + else: + return ScreenSetup(intres, None, None, False) + elif operationmode is OperationMode.EXTERNAL_ONLY: + extres = self.selectResolution("the external screen", situation.externalResolutions()) + if extres is None: + return None + else: + return ScreenSetup(None, extres, None, True) + else: + assert operationmode is OperationMode.USE_BOTH + relscrpositions = list(RelativeScreenPosition) + relscrdescs = list(map(lambda x: x.text+" internal screen", relscrpositions)) + relpos = self.userChoose ("Position of external screen", relscrdescs, relscrpositions, None) + if relpos == None: + return None + elif relpos == RelativeScreenPosition.MIRROR: + # for mirroring only ask for common resolutions + commonres = self.selectResolution("both screens", situation.commonResolutions()) + if commonres is None: + return None + return ScreenSetup(commonres,commonres,relpos,False) + # select resolutions independently + intres = self.selectResolution("the internal screen", situation.internalResolutions()) + if intres is None: + return None + extres = self.selectResolution("the external screen", situation.externalResolutions()) + if extres is None: + return None + extprim = self.userChoose("Select primary screen", ["Internal screen is primary","External screen is primary"], [False,True], None) + if extprim is None: + return None + return ScreenSetup(intres,extres,relpos,extprim) diff --git a/zenity_dialogue.py b/zenity_dialogue.py deleted file mode 100644 index 7dbbc87..0000000 --- a/zenity_dialogue.py +++ /dev/null @@ -1,47 +0,0 @@ -# DSL - easy Display Setup for Laptops -# Copyright (C) 2012 Ralf Jung -# Copyright (C) 2012-2015 Constantin Berhard -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -from screen import RelativeScreenPosition, ScreenSetup, processOutputIt - -def userChoose (title, choices, returns, fallback): - assert len(choices) == len(returns) - args = ["zenity", "--list", "--text="+title, "--column="]+choices - switch = dict (list(zip (choices,returns))) - try: - for line in processOutputIt(*args): - return switch.get(line.strip(), fallback) - except Exception: - # on user cancel, the return code of zenity is nonzero - return fallback - return fallback - -def run (internalResolutions, externalResolutions): - relpos = userChoose ("Position of external screen", ["Left of internal screen", "Right of internal screen"], [RelativeScreenPosition.LEFT, RelativeScreenPosition.RIGHT], None) - if relpos == None: - return None - intres = internalResolutions[0] - extres = externalResolutions[0] - extprim = True - extres = userChoose ("external display resolution", list(map(str,externalResolutions)), externalResolutions, None) - if extres == None: - return None - if extprim == None: - extprim = userChoose ("Which display should be the primary display?", ["internal display", "external display"], [False, True], None) - if extprim == None: - return None - return ScreenSetup(intres, extres, relpos, extIsPrimary = extprim)