diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 3e80c752..34f4b81e 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -71,40 +71,6 @@ if '_' not in builtins.__dict__: # ######################################## -class ArgsThread(QtCore.QThread): - open_signal = pyqtSignal(list) - - if sys.platform == 'win32': - address = (r'\\.\pipe\NPtest', 'AF_PIPE') - else: - address = ('/tmp/testipc', 'AF_UNIX') - - def my_loop(self, address): - try: - listener = Listener(*address) - while True: - conn = listener.accept() - self.serve(conn) - except socket.error as e: - conn = Client(*address) - conn.send(sys.argv) - conn.send('close') - # close the current instance only if there are args - if len(sys.argv) > 1: - sys.exit() - - def serve(self, conn): - while True: - msg = conn.recv() - if msg == 'close': - break - self.open_signal.emit(msg) - conn.close() - - def run(self): - self.my_loop(self.address) - - class App(QtCore.QObject): """ The main application class. The constructor starts the GUI. @@ -754,10 +720,11 @@ class App(QtCore.QObject): self.defaults.set_change_callback(self.on_defaults_dict_change) # When the dictionary changes. self.defaults.update({ # Global APP Preferences + "first_run": True, + "units": "IN", "global_serial": 0, "global_stats": {}, "global_tabs_detachable": True, - "units": "IN", "global_app_level": 'b', "global_portable": False, "global_language": 'English', @@ -1800,6 +1767,15 @@ class App(QtCore.QObject): # when there are arguments at application startup this get launched self.args_at_startup.connect(lambda: self.on_startup_args()) + + # connect the 'Apply' buttons from the Preferences/File Associations + self.ui.fa_defaults_form.fa_excellon_group.exc_list_btn.clicked.connect( + lambda: self.on_register_files(obj_type='excellon')) + self.ui.fa_defaults_form.fa_gcode_group.gco_list_btn.clicked.connect( + lambda: self.on_register_files(obj_type='gcode')) + self.ui.fa_defaults_form.fa_gerber_group.grb_list_btn.clicked.connect( + lambda: self.on_register_files(obj_type='gerber')) + self.log.debug("Finished connecting Signals.") # this is a flag to signal to other tools that the ui tooltab is locked and not accessible @@ -2127,7 +2103,11 @@ class App(QtCore.QObject): # ################################################ # ######### Variables for global usage ########### # ################################################ - self.on_apply_associations() + + # register files with FlatCAM; it works only for Windows for now + if sys.platform == 'win32' and self.defaults["first_run"] is True: + self.on_register_files() + # coordinates for relative position display self.rel_point1 = (0, 0) self.rel_point2 = (0, 0) @@ -2245,6 +2225,9 @@ class App(QtCore.QObject): self.set_ui_title(name=_("New Project - Not saved")) + # after the first run, this object should be False + self.defaults["first_run"] = False + # accept some type file as command line parameter: FlatCAM project, FlatCAM preferences or scripts # the path/file_name must be enclosed in quotes if it contain spaces if App.args: @@ -3896,7 +3879,9 @@ class App(QtCore.QObject): else: self.ui.shell_dock.show() - def on_apply_associations(self): + def on_register_files(self, obj_type=None): + log.debug("Manufacturing files extensions are registered with FlatCAM.") + new_reg_path = 'Software\\Classes\\' # find if the current user is admin try: @@ -3919,20 +3904,77 @@ class App(QtCore.QObject): except WindowsError: return False - exc_list = self.ui.fa_defaults_form.fa_excellon_group.exc_list_text.get_value().split(',') - for ext in exc_list: - new_k = new_reg_path + ext.replace(' ', '') - set_reg('', root_path=root_path, new_reg_path=new_k, value='FlatCAM') + # delete key in registry + def delete_reg(root_path, reg_path, key_to_del): + key_to_del_path = reg_path + key_to_del + try: + winreg.DeleteKey(root_path, key_to_del_path) + return True + except WindowsError: + return False - gco_list = self.ui.fa_defaults_form.fa_gcode_group.gco_list_text.get_value().split(',') - for ext in gco_list: - new_k = new_reg_path + ext.replace(' ', '') - set_reg('', root_path=root_path, new_reg_path=new_k, value='FlatCAM') + if obj_type is None or obj_type == 'excellon': + exc_list = self.ui.fa_defaults_form.fa_excellon_group.exc_list_text.get_value().replace(' ', '').split(',') + exc_list = [x for x in exc_list if x != ''] - grb_list = self.ui.fa_defaults_form.fa_gerber_group.grb_list_text.get_value().split(',') - for ext in grb_list: - new_k = new_reg_path + ext.replace(' ', '') - set_reg('', root_path=root_path, new_reg_path=new_k, value='FlatCAM') + # register all keys in the Preferences window + for ext in exc_list: + new_k = new_reg_path + ext + set_reg('', root_path=root_path, new_reg_path=new_k, value='FlatCAM') + + # and unregister those that are no longer in the Preferences windows but are in the file + for ext in self.defaults["fa_excellon"].replace(' ', '').split(','): + if ext not in exc_list: + delete_reg(root_path=root_path, reg_path=new_reg_path, key_to_del=ext) + + # now write the updated extensions to the self.defaults + new_ext = '' + for ext in exc_list: + new_ext = new_ext + ext + ', ' + self.defaults["fa_excellon"] = new_ext + self.inform.emit(_("[success] Selected Excellon file extensions registered with FlatCAM.")) + + if obj_type is None or obj_type == 'gcode': + gco_list = self.ui.fa_defaults_form.fa_gcode_group.gco_list_text.get_value().replace(' ', '').split(',') + gco_list = [x for x in gco_list if x != ''] + + # register all keys in the Preferences window + for ext in gco_list: + new_k = new_reg_path + ext + set_reg('', root_path=root_path, new_reg_path=new_k, value='FlatCAM') + + # and unregister those that are no longer in the Preferences windows but are in the file + for ext in self.defaults["fa_gcode"].replace(' ', '').split(','): + if ext not in gco_list: + delete_reg(root_path=root_path, reg_path=new_reg_path, key_to_del=ext) + + # now write the updated extensions to the self.defaults + new_ext = '' + for ext in gco_list: + new_ext = new_ext + ext + ', ' + self.defaults["fa_gcode"] = new_ext + self.inform.emit(_("[success] Selected GCode file extensions registered with FlatCAM.")) + + if obj_type is None or obj_type == 'gerber': + grb_list = self.ui.fa_defaults_form.fa_gerber_group.grb_list_text.get_value().replace(' ', '').split(',') + grb_list = [x for x in grb_list if x != ''] + + # register all keys in the Preferences window + for ext in grb_list: + new_k = new_reg_path + ext + set_reg('', root_path=root_path, new_reg_path=new_k, value='FlatCAM') + + # and unregister those that are no longer in the Preferences windows but are in the file + for ext in self.defaults["fa_gerber"].replace(' ', '').split(','): + if ext not in grb_list: + delete_reg(root_path=root_path, reg_path=new_reg_path, key_to_del=ext) + + # now write the updated extensions to the self.defaults + new_ext = '' + for ext in grb_list: + new_ext = new_ext + ext + ', ' + self.defaults["fa_gerber"] = new_ext + self.inform.emit(_("[success] Selected Gerber file extensions registered with FlatCAM.")) def on_edit_join(self, name=None): """ @@ -9588,4 +9630,38 @@ The normal flow when working in FlatCAM is the following:

obj.options[oname] = self.defaults[option] obj.to_form() # Update UI + +class ArgsThread(QtCore.QThread): + open_signal = pyqtSignal(list) + + if sys.platform == 'win32': + address = (r'\\.\pipe\NPtest', 'AF_PIPE') + else: + address = ('/tmp/testipc', 'AF_UNIX') + + def my_loop(self, address): + try: + listener = Listener(*address) + while True: + conn = listener.accept() + self.serve(conn) + except socket.error as e: + conn = Client(*address) + conn.send(sys.argv) + conn.send('close') + # close the current instance only if there are args + if len(sys.argv) > 1: + sys.exit() + + def serve(self, conn): + while True: + msg = conn.recv() + if msg == 'close': + break + self.open_signal.emit(msg) + conn.close() + + def run(self): + self.my_loop(self.address) + # end of file diff --git a/README.md b/README.md index eacc8d18..18973297 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,15 @@ CAD program, and create G-Code for Isolation routing. - in NCC Tool added a warning in case there are isolation tools and if those isolation's are interrupted by an area or a box - in Paint Tool made that the area selection is repeated until RMB click - in Paint Tool and NCC Tool fixed the RMB click detection when Area selection is used +- finished the work on file extensions registration with FlatCAM. If the file extensions are deleted in the Preferences -> File Associations then those extensions are unregistered with FlatCAM +- fixed bug in NCC Tools and in SolderPaste Tool if in Edit -> Preferences only one tool is entered 2.09.2019 - fixed issue in NCC Tool when using area option - added formatting for some strings in the app strings, making the future translations easier - made changes in the Excellon Tools Table to make it more clear that the tools are selected in the # column and not in the Plot column -- in Excellon and Gerber Seleted tab made the Plot (mark) columns not selectable +- in Excellon and Gerber Selected tab made the Plot (mark) columns not selectable - some ToolTips were modified - in Properties Tool made threaded the calculation of convex_hull area and also made it to work for multi-geo objects - in NCC tool the type of tool that is used is transferred to the Geometry object diff --git a/flatcamTools/ToolNonCopperClear.py b/flatcamTools/ToolNonCopperClear.py index fd6a50c5..ad0909d1 100644 --- a/flatcamTools/ToolNonCopperClear.py +++ b/flatcamTools/ToolNonCopperClear.py @@ -1488,7 +1488,10 @@ class NonCopperClear(FlatCAMTool, Gerber): # Generate area for each tool offset = sum(sorted_tools) current_uid = int(1) - tool = eval(self.app.defaults["tools_ncctools"])[0] + try: + tool = eval(self.app.defaults["tools_ncctools"])[0] + except TypeError: + tool = eval(self.app.defaults["tools_ncctools"]) # ################################################################################################### # Calculate the empty area by subtracting the solid_geometry from the object bounding box geometry ## @@ -1715,7 +1718,10 @@ class NonCopperClear(FlatCAMTool, Gerber): cleared_by_last_tool = [] rest_geo = [] current_uid = 1 - tool = eval(self.app.defaults["tools_ncctools"])[0] + try: + tool = eval(self.app.defaults["tools_ncctools"])[0] + except TypeError: + tool = eval(self.app.defaults["tools_ncctools"]) # repurposed flag for final object, geo_obj. True if it has any solid_geometry, False if not. app_obj.poly_not_cleared = True diff --git a/flatcamTools/ToolSolderPaste.py b/flatcamTools/ToolSolderPaste.py index 726d2e16..3b4b9a9a 100644 --- a/flatcamTools/ToolSolderPaste.py +++ b/flatcamTools/ToolSolderPaste.py @@ -1124,7 +1124,7 @@ class SolderPaste(FlatCAMTool): # some or all the pads are not covered with solder paste if work_geo: app_obj.inform.emit(_("[WARNING_NOTCL] Some or all pads have no solder " - "due of inadequate nozzle diameters...")) + "due of inadequate nozzle diameters...")) return 'fail' if use_thread: @@ -1300,11 +1300,11 @@ class SolderPaste(FlatCAMTool): try: if obj.special_group != 'solder_paste_tool': self.app.inform.emit(_("[WARNING_NOTCL] This CNCJob object can't be processed. " - "NOT a solder_paste_tool CNCJob object.")) + "NOT a solder_paste_tool CNCJob object.")) return except AttributeError: self.app.inform.emit(_("[WARNING_NOTCL] This CNCJob object can't be processed. " - "NOT a solder_paste_tool CNCJob object.")) + "NOT a solder_paste_tool CNCJob object.")) return gcode = '(G-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s)\n' % \ @@ -1356,7 +1356,7 @@ class SolderPaste(FlatCAMTool): if obj.special_group != 'solder_paste_tool': self.app.inform.emit(_("[WARNING_NOTCL] This CNCJob object can't be processed. " - "NOT a solder_paste_tool CNCJob object.")) + "NOT a solder_paste_tool CNCJob object.")) return _filter_ = "G-Code Files (*.nc);;G-Code Files (*.txt);;G-Code Files (*.tap);;G-Code Files (*.cnc);;" \