diff --git a/FlatCAMApp.py b/FlatCAMApp.py index fb04b93e..094b2153 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -81,12 +81,16 @@ class App(QtCore.QObject): # Get Cmd Line Options cmd_line_shellfile = '' cmd_line_shellvar = '' + cmd_line_headless = None - cmd_line_help = "FlatCam.py --shellfile=\nFlatCam.py --shellvar=<1,'C:\\path',23>" + cmd_line_help = "FlatCam.py --shellfile=\n" \ + "FlatCam.py --shellvar=<1,'C:\\path',23>\n" \ + "FlatCam.py --headless=1" try: # Multiprocessing pool will spawn additional processes with 'multiprocessing-fork' flag cmd_line_options, args = getopt.getopt(sys.argv[1:], "h:", ["shellfile=", "shellvar=", + "headless=", "multiprocessing-fork="]) except getopt.GetoptError: print(cmd_line_help) @@ -99,6 +103,11 @@ class App(QtCore.QObject): cmd_line_shellfile = arg elif opt == '--shellvar': cmd_line_shellvar = arg + elif opt == '--headless': + try: + cmd_line_headless = eval(arg) + except NameError: + pass # ## Logging ### log = logging.getLogger('base') @@ -251,20 +260,33 @@ class App(QtCore.QObject): # ####### CONFIG FILE WITH PARAMETERS REGARDING PORTABILITY ############### # ######################################################################### config_file = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + '\\config\\configuration.txt' + try: + with open(config_file, 'r'): + pass + except FileNotFoundError: + config_file = os.path.dirname(os.path.realpath(__file__)) + '\\config\\configuration.txt' + try: with open(config_file, 'r') as f: try: for line in f: - param = str(line).rpartition('=') + param = str(line).replace('\n', '').rpartition('=') + if param[0] == 'portable': try: portable = eval(param[2]) except NameError: portable = False + if param[0] == 'headless': + if param[2].lower() == 'true': + self.cmd_line_headless = 1 + else: + self.cmd_line_headless = None except Exception as e: log.debug('App.__init__() -->%s' % str(e)) return - except FileNotFoundError: + except FileNotFoundError as e: + log.debug(str(e)) pass if portable is False: @@ -365,7 +387,7 @@ class App(QtCore.QObject): del settings show_splash = 1 - if show_splash: + if show_splash and self.cmd_line_headless != 1: splash_pix = QtGui.QPixmap('share/splash.png') self.splash = QtWidgets.QSplashScreen(splash_pix, Qt.WindowStaysOnTopHint) # self.splash.setMask(splash_pix.mask()) @@ -380,6 +402,8 @@ class App(QtCore.QObject): self.splash.showMessage(_("FlatCAM is initializing ..."), alignment=Qt.AlignBottom | Qt.AlignLeft, color=QtGui.QColor("gray")) + else: + show_splash = 0 # ############################################################################# # ##################### Initialize GUI ######################################## @@ -392,6 +416,7 @@ class App(QtCore.QObject): self.FC_dark_blue = '#0000ffbf' QtCore.QObject.__init__(self) + self.ui = FlatCAMGUI(self.version, self.beta, self) self.ui.geom_update[int, int, int, int, int].connect(self.save_geometry) @@ -1579,8 +1604,14 @@ class App(QtCore.QObject): # ################################################################# if self.defaults["global_systray_icon"]: self.parent_w = QtWidgets.QWidget() - self.trayIcon = FlatCAMSystemTray(app=self, icon=QtGui.QIcon('share/flatcam_icon32_green.png'), - parent=self.parent_w) + + if self.cmd_line_headless == 1: + self.trayIcon = FlatCAMSystemTray(app=self, icon=QtGui.QIcon('share/flatcam_icon32_green.png'), + headless=True, + parent=self.parent_w) + else: + self.trayIcon = FlatCAMSystemTray(app=self, icon=QtGui.QIcon('share/flatcam_icon32_green.png'), + parent=self.parent_w) # ############################################## # ######### SETUP OBJECT COLLECTION ############ @@ -2454,21 +2485,25 @@ class App(QtCore.QObject): # ########################## SHOW GUI ################################################# # ##################################################################################### - # finish the splash - self.splash.finish(self.ui) + # if the app is not started as headless, show it + if self.cmd_line_headless != 1: + # finish the splash + self.splash.finish(self.ui) - settings = QSettings("Open Source", "FlatCAM") - if settings.contains("maximized_gui"): - maximized_ui = settings.value('maximized_gui', type=bool) - if maximized_ui is True: - self.ui.showMaximized() + settings = QSettings("Open Source", "FlatCAM") + if settings.contains("maximized_gui"): + maximized_ui = settings.value('maximized_gui', type=bool) + if maximized_ui is True: + self.ui.showMaximized() + else: + self.ui.show() else: self.ui.show() - else: - self.ui.show() - if self.defaults["global_systray_icon"]: - self.trayIcon.show() + if self.defaults["global_systray_icon"]: + self.trayIcon.show() + else: + log.warning("******************* RUNNING HEADLESS *******************") # ##################################################################################### # ########################## START-UP ARGUMENTS ####################################### @@ -8867,11 +8902,14 @@ class App(QtCore.QObject): if name: filename = name - self.splash.showMessage('%s: %ssec\n%s' % (_("Canvas initialization started.\n" - "Canvas initialization finished in"), '%.2f' % self.used_time, - _("Executing FlatCAMScript file.")), - alignment=Qt.AlignBottom | Qt.AlignLeft, - color=QtGui.QColor("gray")) + if self.cmd_line_headless != 1: + self.splash.showMessage('%s: %ssec\n%s' % + (_("Canvas initialization started.\n" + "Canvas initialization finished in"), '%.2f' % self.used_time, + _("Executing FlatCAMScript file.") + ), + alignment=Qt.AlignBottom | Qt.AlignLeft, + color=QtGui.QColor("gray")) else: _filter_ = "TCL script (*.FlatScript);;TCL script (*.TCL);;TCL script (*.TXT);;All Files (*.*)" try: @@ -8890,12 +8928,18 @@ class App(QtCore.QObject): self.inform.emit('[WARNING_NOTCL] %s' % _("Run TCL script cancelled.")) else: - if self.ui.shell_dock.isHidden(): - self.ui.shell_dock.show() + if self.cmd_line_headless != 1: + if self.ui.shell_dock.isHidden(): + self.ui.shell_dock.show() + try: with open(filename, "r") as tcl_script: cmd_line_shellfile_content = tcl_script.read() - self.shell._sysShell.exec_command(cmd_line_shellfile_content) + if self.cmd_line_headless != 1: + self.shell._sysShell.exec_command(cmd_line_shellfile_content) + else: + self.shell._sysShell.exec_command(cmd_line_shellfile_content, no_echo=True) + if silent is False: self.inform.emit('[success] %s' % _("TCL script file opened in Code Editor and executed.")) diff --git a/README.md b/README.md index e2065c2f..7d454e37 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ CAD program, and create G-Code for Isolation routing. - made sure that optionally, when a script is run then it is also loaded into the code editor - added control over the display of Sys Tray Icon in Edit -> Preferences -> General -> GUI Settings -> Sys Tray Icon checkbox - updated some of the default values to more reasonable ones +- FlatCAM can be run in HEADLESS mode now. This node can be selected by using the --headless=1 command line argument or by changing the line headless=False to True in config/configuration.txt file. In this mod the Sys Tray Icon menu will hold only the Run Scrip menu entry and Exit entry. 18.09.2019 diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index 589f0af7..1075d275 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -8212,7 +8212,7 @@ class FlatCAMInfoBar(QtWidgets.QWidget): class FlatCAMSystemTray(QtWidgets.QSystemTrayIcon): - def __init__(self, app, icon, parent=None): + def __init__(self, app, icon, headless=None, parent=None): # QtWidgets.QSystemTrayIcon.__init__(self, icon, parent) super().__init__(icon, parent=parent) self.app = app @@ -8229,38 +8229,41 @@ class FlatCAMSystemTray(QtWidgets.QSystemTrayIcon): menu.addSeparator() - menu_open = menu.addMenu(QtGui.QIcon('share/folder32_bis.png'), _('Open')) + if headless is None: + self.menu_open = menu.addMenu(QtGui.QIcon('share/folder32_bis.png'), _('Open')) - # Open Project ... - menu_openproject = QtWidgets.QAction(QtGui.QIcon('share/folder16.png'), _('Open Project ...'), self) - menu_open.addAction(menu_openproject) - menu_open.addSeparator() + # Open Project ... + menu_openproject = QtWidgets.QAction(QtGui.QIcon('share/folder16.png'), _('Open Project ...'), self) + self.menu_open.addAction(menu_openproject) + self.menu_open.addSeparator() - # Open Gerber ... - menu_opengerber = QtWidgets.QAction(QtGui.QIcon('share/flatcam_icon24.png'), - _('Open &Gerber ...\tCTRL+G'), self) - menu_open.addAction(menu_opengerber) + # Open Gerber ... + menu_opengerber = QtWidgets.QAction(QtGui.QIcon('share/flatcam_icon24.png'), + _('Open &Gerber ...\tCTRL+G'), self) + self.menu_open.addAction(menu_opengerber) - # Open Excellon ... - menu_openexcellon = QtWidgets.QAction(QtGui.QIcon('share/open_excellon32.png'), - _('Open &Excellon ...\tCTRL+E'), self) - menu_open.addAction(menu_openexcellon) + # Open Excellon ... + menu_openexcellon = QtWidgets.QAction(QtGui.QIcon('share/open_excellon32.png'), + _('Open &Excellon ...\tCTRL+E'), self) + self.menu_open.addAction(menu_openexcellon) - # Open G-Code ... - menu_opengcode = QtWidgets.QAction(QtGui.QIcon('share/code.png'), _('Open G-&Code ...'), self) - menu_open.addAction(menu_opengcode) + # Open G-Code ... + menu_opengcode = QtWidgets.QAction(QtGui.QIcon('share/code.png'), _('Open G-&Code ...'), self) + self.menu_open.addAction(menu_opengcode) - menu_open.addSeparator() + self.menu_open.addSeparator() + + menu_openproject.triggered.connect(self.app.on_file_openproject) + menu_opengerber.triggered.connect(self.app.on_fileopengerber) + menu_openexcellon.triggered.connect(self.app.on_fileopenexcellon) + menu_opengcode.triggered.connect(self.app.on_fileopengcode) exitAction = menu.addAction(_("Exit")) exitAction.setIcon(QtGui.QIcon('share/power16.png')) self.setContextMenu(menu) - menu_runscript.triggered.connect(self.app.on_filerunscript) - menu_openproject.triggered.connect(self.app.on_file_openproject) - menu_opengerber.triggered.connect(self.app.on_fileopengerber) - menu_openexcellon.triggered.connect(self.app.on_fileopenexcellon) - menu_opengcode.triggered.connect(self.app.on_fileopengcode) + menu_runscript.triggered.connect(lambda: self.app.on_filerunscript( + silent=True if self.app.cmd_line_headless == 1 else False)) exitAction.triggered.connect(self.app.final_save) diff --git a/tclCommands/TclCommandPlotAll.py b/tclCommands/TclCommandPlotAll.py index 349eeb2f..a2028907 100644 --- a/tclCommands/TclCommandPlotAll.py +++ b/tclCommands/TclCommandPlotAll.py @@ -42,5 +42,5 @@ class TclCommandPlotAll(TclCommand): :param unnamed_args: :return: """ - - self.app.plot_all() + if self.app.cmd_line_headless != 1: + self.app.plot_all() diff --git a/tclCommands/TclCommandPlotObjects.py b/tclCommands/TclCommandPlotObjects.py index f7a418d7..4b2f77c2 100644 --- a/tclCommands/TclCommandPlotObjects.py +++ b/tclCommands/TclCommandPlotObjects.py @@ -42,10 +42,11 @@ class TclCommandPlotObjects(TclCommand): :param unnamed_args: :return: """ - names = [x.strip() for x in args['names'].split(",")] - objs = [] - for name in names: - objs.append(self.app.collection.get_by_name(name)) + if self.app.cmd_line_headless != 1: + names = [x.strip() for x in args['names'].split(",")] + objs = [] + for name in names: + objs.append(self.app.collection.get_by_name(name)) - for obj in objs: - obj.plot() + for obj in objs: + obj.plot()