From bd8f607b32cea1ff322195acc074de2e23c528cf Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Thu, 22 Aug 2019 02:53:18 +0300 Subject: [PATCH] - added possibility to turn application portable from the Edit -> Preferences -> General -> App. Preferences -> Portable checkbox --- FlatCAMApp.py | 191 +++++++++++++++++++++++++++++++-------- README.md | 2 +- config/configuration.txt | 2 +- flatcamGUI/FlatCAMGUI.py | 15 +++ 4 files changed, 169 insertions(+), 41 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 736fca8b..e1590a3f 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -347,6 +347,7 @@ class App(QtCore.QObject): # General App "units": self.ui.general_defaults_form.general_app_group.units_radio, "global_app_level": self.ui.general_defaults_form.general_app_group.app_level_radio, + "global_portable": self.ui.general_defaults_form.general_app_group.portability_cb, "global_language": self.ui.general_defaults_form.general_app_group.language_cb, "global_shell_at_startup": self.ui.general_defaults_form.general_app_group.shell_startup_cb, @@ -703,6 +704,7 @@ class App(QtCore.QObject): "global_tabs_detachable": True, "units": "IN", "global_app_level": 'b', + "global_portable": False, "global_language": 'English', "global_version_check": True, "global_send_stats": True, @@ -1326,13 +1328,8 @@ class App(QtCore.QObject): self.tools_form = None self.on_options_combo_change(0) # Will show the initial form - # ### Define OBJECT COLLECTION ### - self.collection = ObjectCollection(self) - self.ui.project_tab_layout.addWidget(self.collection.view) # ################################ - self.log.debug("Finished creating Object Collection.") - # ### Initialize the color box's color in Preferences -> Global -> Color # Init Plot Colors self.ui.general_defaults_form.general_gui_group.pf_color_entry.set_value(self.defaults['global_plot_fill']) @@ -1404,10 +1401,16 @@ class App(QtCore.QObject): "background-color:%s" % str(self.defaults['cncjob_annotation_fontcolor'])[:7]) # ### End of Data #### - # ### Plot Area #### + # ############################################### + # ############# SETUP Plot Area ################# + # ############################################### + start_plot_time = time.time() # debug self.plotcanvas = PlotCanvas(self.ui.right_layout, self) + # So it can receive key presses + self.plotcanvas.vispy_canvas.native.setFocus() + self.plotcanvas.vis_connect('mouse_move', self.on_mouse_move_over_plot) self.plotcanvas.vis_connect('mouse_press', self.on_mouse_click_over_plot) self.plotcanvas.vis_connect('mouse_release', self.on_mouse_click_release_over_plot) @@ -1416,10 +1419,10 @@ class App(QtCore.QObject): # Keys over plot enabled self.plotcanvas.vis_connect('key_press', self.ui.keyPressEvent) - self.ui.splitter.setStretchFactor(1, 2) + end_plot_time = time.time() + self.log.debug("Finished Canvas initialization in %s seconds." % (str(end_plot_time - start_plot_time))) - # So it can receive key presses - self.plotcanvas.vispy_canvas.native.setFocus() + self.ui.splitter.setStretchFactor(1, 2) self.app_cursor = self.plotcanvas.new_cursor() self.app_cursor.enabled = False @@ -1428,22 +1431,34 @@ class App(QtCore.QObject): # depending on from where those tools are called different actions can be done self.call_source = 'app' - end_plot_time = time.time() - self.log.debug("Finished Canvas initialization in %s seconds." % (str(end_plot_time - start_plot_time))) + # ############################################## + # ######### SETUP OBJECT COLLECTION ############ + # ############################################## + + self.collection = ObjectCollection(self) + self.ui.project_tab_layout.addWidget(self.collection.view) # ### Adjust tabs width ## ## # self.collection.view.setMinimumWidth(self.ui.options_scroll_area.widget().sizeHint().width() + # self.ui.options_scroll_area.verticalScrollBar().sizeHint().width()) self.collection.view.setMinimumWidth(290) + self.log.debug("Finished creating Object Collection.") + + # ############################################### + # ############# Worker SETUP #################### + # ############################################### - # ### Worker #### if self.defaults["global_worker_number"]: self.workers = WorkerStack(workers_number=int(self.defaults["global_worker_number"])) else: self.workers = WorkerStack(workers_number=2) self.worker_task.connect(self.workers.add_task) + self.log.debug("Finished creating Workers crew.") + + # ################################################ + # ############### Signal handling ################ + # ################################################ - # ### Signal handling ### # ### Custom signals ### self.inform.connect(self.info) self.app_quit.connect(self.quit_application) @@ -1722,6 +1737,9 @@ class App(QtCore.QObject): self.ui.buttonFind.clicked.connect(self.handleFindGCode) self.ui.buttonReplace.clicked.connect(self.handleReplaceGCode) + # portability changed + self.ui.general_defaults_form.general_app_group.portability_cb.stateChanged.connect(self.on_portable_checked) + # Object list self.collection.view.activated.connect(self.on_row_activated) @@ -1738,6 +1756,7 @@ class App(QtCore.QObject): # when there are arguments at application startup this get launched self.args_at_startup.connect(self.on_startup_args) + self.log.debug("Finished connecting Signals.") # this is a flag to signal to other tools that the ui tooltab is locked and not accessible self.tool_tab_locked = False @@ -1748,21 +1767,18 @@ class App(QtCore.QObject): else: self.ui.splitter.setSizes([0, 1]) - # ################### - # ### Other setups ## - # ################### + # ########################################### + # ################# Other setups ############ + # ########################################### + # Sets up FlatCAMObj, FCProcess and FCProcessContainer. self.setup_obj_classes() self.setup_recent_items() self.setup_component_editor() - # ############ - # ### Shell ## - # ############ - - # ######################### - # Auto-complete KEYWORDS ## - # ######################### + # ########################################### + # #######Auto-complete KEYWORDS ############# + # ########################################### self.tcl_commands_list = ['add_circle', 'add_poly', 'add_polygon', 'add_polyline', 'add_rectangle', 'aligndrill', 'clear', 'aligndrillgrid', 'cncjob', 'cutout', 'delete', 'drillcncjob', @@ -1972,6 +1988,10 @@ class App(QtCore.QObject): self.myKeywords = self.tcl_commands_list + self.ordinary_keywords + self.tcl_keywords + # ########################################### + # ########### Shell SETUP ################### + # ########################################### + self.shell = FCShell(self, version=self.version) self.shell._edit.set_model_data(self.myKeywords) self.ui.code_editor.set_model_data(self.myKeywords) @@ -1998,9 +2018,9 @@ class App(QtCore.QObject): else: self.ui.shell_dock.hide() - # ######################## - # ### Tools and Plugins ## - # ######################## + # ########################################### + # ######### Tools and Plugins ############### + # ########################################### self.dblsidedtool = None self.measurement_tool = None @@ -2026,6 +2046,10 @@ class App(QtCore.QObject): # self.f_parse = ParseFont(self) # self.parse_system_fonts() + # ############################################### + # ######## START-UP ARGUMENTS ################### + # ############################################### + # test if the program was started with a script as parameter if self.cmd_line_shellfile: try: @@ -2036,9 +2060,9 @@ class App(QtCore.QObject): print("ERROR: ", ext) sys.exit(2) - # ########################## - # ### Check for updates #### - # ########################## + # ############################################### + # ############# Check for updates ############### + # ############################################### # Separate thread (Not worker) # Check for updates on startup but only if the user consent and the app is not in Beta version @@ -2051,9 +2075,9 @@ class App(QtCore.QObject): 'params': []}) self.thr2.start(QtCore.QThread.LowPriority) - # ################################### - # ### Variables for global usage #### - # ################################### + # ################################################ + # ######### Variables for global usage ########### + # ################################################ # coordinates for relative position display self.rel_point1 = (0, 0) @@ -2154,7 +2178,7 @@ class App(QtCore.QObject): os.chmod(filename_factory, S_IREAD | S_IRGRP | S_IROTH) #################################################### - # ### EDITOR section ############################### + # ### ADDING FlatCAM EDITORS section ############### #################################################### # watch out for the position of the editors instantiation ... if it is done before a save of the default values @@ -3508,7 +3532,7 @@ class App(QtCore.QObject): # log.debug("Application defaults saved ... Exit event.") # QtWidgets.qApp.quit() - def save_defaults(self, silent=False): + def save_defaults(self, silent=False, data_path=None): """ Saves application default options ``self.defaults`` to current_defaults.FlatConfig. @@ -3517,9 +3541,12 @@ class App(QtCore.QObject): """ self.report_usage("save_defaults") + if data_path is None: + data_path = self.data_path + # Read options from file try: - f = open(self.data_path + "/current_defaults.FlatConfig") + f = open(data_path + "/current_defaults.FlatConfig") defaults_file_content = f.read() f.close() except: @@ -3576,7 +3603,7 @@ class App(QtCore.QObject): # Save update options try: - f = open(self.data_path + "/current_defaults.FlatConfig", "w") + f = open(data_path + "/current_defaults.FlatConfig", "w") json.dump(defaults, f, default=to_dict, indent=2, sort_keys=True) f.close() except: @@ -3586,7 +3613,7 @@ class App(QtCore.QObject): if not silent: self.inform.emit(_("[success] Defaults saved.")) - def save_factory_defaults(self, silent=False): + def save_factory_defaults(self, silent=False, data_path=None): """ Saves application factory default options ``self.defaults`` to factory_defaults.FlatConfig. @@ -3596,9 +3623,12 @@ class App(QtCore.QObject): """ self.report_usage("save_factory_defaults") + if data_path is None: + data_path = self.data_path + # Read options from file try: - f_f_def = open(self.data_path + "/factory_defaults.FlatConfig") + f_f_def = open(data_path + "/factory_defaults.FlatConfig") factory_defaults_file_content = f_f_def.read() f_f_def.close() except: @@ -3624,7 +3654,7 @@ class App(QtCore.QObject): # Save update options try: - f_f_def_s = open(self.data_path + "/factory_defaults.FlatConfig", "w") + f_f_def_s = open(data_path + "/factory_defaults.FlatConfig", "w") json.dump(factory_defaults, f_f_def_s, default=to_dict, indent=2, sort_keys=True) f_f_def_s.close() except: @@ -3685,6 +3715,89 @@ class App(QtCore.QObject): log.debug("App.final_save() --> App UI state saved.") QtWidgets.qApp.quit() + def on_portable_checked(self, state): + line_no = 0 + data = None + + if sys.platform != 'win32': + # this won't work in Linux or MacOS + return + + # test if the app was frozen and choose the path for the configuration file + if getattr(sys, "frozen", False) is True: + current_data_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + '\\config' + else: + current_data_path = os.path.dirname(os.path.realpath(__file__)) + '\\config' + + config_file = current_data_path + '\\configuration.txt' + try: + with open(config_file, 'r') as f: + try: + data = f.readlines() + except Exception as e: + log.debug('App.__init__() -->%s' % str(e)) + return + except FileNotFoundError: + pass + + for line in data: + line = line.strip('\n') + param = str(line).rpartition('=') + if param[0] == 'portable': + break + line_no += 1 + + if state: + data[line_no] = 'portable=True\n' + # create the new defauults files + # create current_defaults.FlatConfig file if there is none + try: + f = open(current_data_path + '/current_defaults.FlatConfig') + f.close() + except IOError: + App.log.debug('Creating empty current_defaults.FlatConfig') + f = open(current_data_path + '/current_defaults.FlatConfig', 'w') + json.dump({}, f) + f.close() + + # create factory_defaults.FlatConfig file if there is none + try: + f = open(current_data_path + '/factory_defaults.FlatConfig') + f.close() + except IOError: + App.log.debug('Creating empty factory_defaults.FlatConfig') + f = open(current_data_path + '/factory_defaults.FlatConfig', 'w') + json.dump({}, f) + f.close() + + try: + f = open(current_data_path + '/recent.json') + f.close() + except IOError: + App.log.debug('Creating empty recent.json') + f = open(current_data_path + '/recent.json', 'w') + json.dump([], f) + f.close() + + try: + fp = open(current_data_path + '/recent_projects.json') + fp.close() + except IOError: + App.log.debug('Creating empty recent_projects.json') + fp = open(current_data_path + '/recent_projects.json', 'w') + json.dump([], fp) + fp.close() + + # save the current defaults to the new defaults file + self.save_defaults(silent=True, data_path=current_data_path) + self.save_factory_defaults(silent=True, data_path=current_data_path) + + else: + data[line_no] = 'portable=False\n' + + with open(config_file, 'w') as f: + f.writelines(data) + def on_toggle_shell(self): """ toggle shell if is visible close it if closed open it diff --git a/README.md b/README.md index df8ced74..d9dceb5f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ CAD program, and create G-Code for Isolation routing. - added ability to turn ON/OFF the detachable capability of the tabs in Notebook through a context menu activated by right mouse button click on the Notebook header - added ability to turn ON/OFF the detachable capability of the tabs in Plot Tab Area through a context menu activated by right mouse button click on the Plot Tab Area header - +- added possibility to turn application portable from the Edit -> Preferences -> General -> App. Preferences -> Portable checkbox 21.08.2019 diff --git a/config/configuration.txt b/config/configuration.txt index fca42d67..22bbfb42 100644 --- a/config/configuration.txt +++ b/config/configuration.txt @@ -1 +1 @@ -portable=False \ No newline at end of file +portable=False diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index f53f7833..c16afb3e 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -15,6 +15,7 @@ from PyQt5.QtCore import QSettings from flatcamGUI.GUIElements import * import platform import webbrowser +import sys from flatcamEditors.FlatCAMGeoEditor import FCShapeTool @@ -3982,6 +3983,14 @@ class GeneralAppPrefGroupUI(OptionsGroupUI): self.app_level_radio = RadioSet([{'label': _('Basic'), 'value': 'b'}, {'label': _('Advanced'), 'value': 'a'}]) + # Application Level for FlatCAM + self.portability_label = QtWidgets.QLabel('%s:' % _('Portability')) + self.portability_label.setToolTip(_("Choose if the application should run as portable.\n\n" + "If Checked the application will run portable,\n" + "which means that the preferences files will be saved\n" + "in the application folder, in the lib\config subfolder.")) + self.portability_cb = FCCheckBox() + # Languages for FlatCAM self.languagelabel = QtWidgets.QLabel('%s:' % _('Languages')) self.languagelabel.setToolTip(_("Set the language used throughout FlatCAM.")) @@ -4130,6 +4139,9 @@ class GeneralAppPrefGroupUI(OptionsGroupUI): # Add (label - input field) pair to the QFormLayout self.form_box.addRow(self.unitslabel, self.units_radio) self.form_box.addRow(self.app_level_label, self.app_level_radio) + self.form_box.addRow(self.portability_label, self.portability_cb) + self.form_box.addRow(QtWidgets.QLabel('')) + self.form_box.addRow(self.languagelabel, self.language_cb) self.form_box.addRow(self.languagespace, self.language_apply_btn) @@ -4205,6 +4217,9 @@ class GeneralAppPrefGroupUI(OptionsGroupUI): self.layout.addStretch() + if sys.platform != 'win32': + self.portability_label.hide() + self.portability_cb.hide() class GerberGenPrefGroupUI(OptionsGroupUI): def __init__(self, parent=None):