Просмотр исходного кода

Merged marius_stanciu/flatcam_beta/Beta_8.994 into Beta

Marius Stanciu 5 лет назад
Родитель
Сommit
7ca5d69ed4

+ 5 - 0
CHANGELOG.md

@@ -15,6 +15,11 @@ CHANGELOG for FlatCAM beta
 - fix an older issue that made that only the Custom choice created an effect when changing the Offset in the Geometry Object Tool Table
 - trying to optimize Gerber Editor selection with the mouse
 - optimized some of the strings
+- fixed the project context save functionality to work in the new program configuration
+- updated Turkish translation (by Mehmet Kaya)
+- in NCC and Isolation Tools, the Validity Checking of the tools is now multithreaded when the Check Validity UI control is checked
+- translation strings updated
+- fixed an error in Gerber parser, when it encounter a pen-up followed by pen-down move while in a region
 
 2.11.2020
 

+ 33 - 28
appParsers/ParseGerber.py

@@ -953,36 +953,41 @@ class Gerber(Geometry):
                                 # Reset path starting point
                                 path = [[current_x, current_y]]
 
-                                # --- BUFFERED ---
-                                # Draw the flash
-                                # this treats the case when we are storing geometry as paths
-                                geo_dict = {}
-                                geo_flash = Point([current_x, current_y])
-                                follow_buffer.append(geo_flash)
-                                geo_dict['follow'] = geo_flash
-
-                                # this treats the case when we are storing geometry as solids
-                                flash = self.create_flash_geometry(
-                                    Point([current_x, current_y]),
-                                    self.apertures[current_aperture],
-                                    self.steps_per_circle
-                                )
-                                if not flash.is_empty:
-                                    if self.app.defaults['gerber_simplification']:
-                                        poly_buffer.append(flash.simplify(s_tol))
-                                    else:
-                                        poly_buffer.append(flash)
+                                # treat the case when there is a flash inside a Gerber Region when the current_aperture
+                                # is None
+                                if current_aperture is None:
+                                    pass
+                                else:
+                                    # --- BUFFERED ---
+                                    # Draw the flash
+                                    # this treats the case when we are storing geometry as paths
+                                    geo_dict = {}
+                                    geo_flash = Point([current_x, current_y])
+                                    follow_buffer.append(geo_flash)
+                                    geo_dict['follow'] = geo_flash
+
+                                    # this treats the case when we are storing geometry as solids
+                                    flash = self.create_flash_geometry(
+                                        Point([current_x, current_y]),
+                                        self.apertures[current_aperture],
+                                        self.steps_per_circle
+                                    )
+                                    if not flash.is_empty:
+                                        if self.app.defaults['gerber_simplification']:
+                                            poly_buffer.append(flash.simplify(s_tol))
+                                        else:
+                                            poly_buffer.append(flash)
 
-                                    if self.is_lpc is True:
-                                        geo_dict['clear'] = flash
-                                    else:
-                                        geo_dict['solid'] = flash
+                                        if self.is_lpc is True:
+                                            geo_dict['clear'] = flash
+                                        else:
+                                            geo_dict['solid'] = flash
 
-                                if current_aperture not in self.apertures:
-                                    self.apertures[current_aperture] = {}
-                                if 'geometry' not in self.apertures[current_aperture]:
-                                    self.apertures[current_aperture]['geometry'] = []
-                                self.apertures[current_aperture]['geometry'].append(deepcopy(geo_dict))
+                                    if current_aperture not in self.apertures:
+                                        self.apertures[current_aperture] = {}
+                                    if 'geometry' not in self.apertures[current_aperture]:
+                                        self.apertures[current_aperture]['geometry'] = []
+                                    self.apertures[current_aperture]['geometry'].append(deepcopy(geo_dict))
 
                             if making_region is False:
                                 # if the aperture is rectangle then add a rectangular shape having as parameters the

+ 125 - 36
appTools/ToolIsolation.py

@@ -908,9 +908,59 @@ class ToolIsolation(AppTool, Gerber):
             })
 
     def on_find_optimal_tooldia(self):
-        self.find_safe_tooldia_worker(is_displayed=True)
+        self.find_safe_tooldia_worker()
 
-    def find_safe_tooldia_worker(self, is_displayed):
+    @staticmethod
+    def find_optim_mp(aperture_storage, decimals):
+        msg = 'ok'
+        total_geo = []
+
+        for ap in list(aperture_storage.keys()):
+            if 'geometry' in aperture_storage[ap]:
+                for geo_el in aperture_storage[ap]['geometry']:
+                    if 'solid' in geo_el and geo_el['solid'] is not None and geo_el['solid'].is_valid:
+                        total_geo.append(geo_el['solid'])
+
+        total_geo = MultiPolygon(total_geo)
+        total_geo = total_geo.buffer(0)
+
+        try:
+            __ = iter(total_geo)
+            geo_len = len(total_geo)
+        except TypeError:
+            msg = ('[ERROR_NOTCL] %s' % _("The Gerber object has one Polygon as geometry.\n"
+                                          "There are no distances between geometry elements to be found."))
+
+        min_dict = {}
+        idx = 1
+        for geo in total_geo:
+            for s_geo in total_geo[idx:]:
+                # minimize the number of distances by not taking into considerations
+                # those that are too small
+                dist = geo.distance(s_geo)
+                dist = float('%.*f' % (decimals, dist))
+                loc_1, loc_2 = nearest_points(geo, s_geo)
+
+                proc_loc = (
+                    (float('%.*f' % (decimals, loc_1.x)), float('%.*f' % (decimals, loc_1.y))),
+                    (float('%.*f' % (decimals, loc_2.x)), float('%.*f' % (decimals, loc_2.y)))
+                )
+
+                if dist in min_dict:
+                    min_dict[dist].append(proc_loc)
+                else:
+                    min_dict[dist] = [proc_loc]
+
+            idx += 1
+
+        min_list = list(min_dict.keys())
+        min_dist = min(min_list)
+
+        return msg, min_dist
+
+    # multiprocessing variant
+    def find_safe_tooldia_multiprocessing(self):
+        self.app.inform.emit(_("Checking tools for validity."))
         self.units = self.app.defaults['units'].upper()
 
         obj_name = self.ui.object_combo.currentText()
@@ -926,8 +976,73 @@ class ToolIsolation(AppTool, Gerber):
             self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
             return
 
-        def job_thread(app_obj, is_display):
-            with self.app.proc_container.new(_("Working ...")):
+        def job_thread(app_obj):
+            with self.app.proc_container.new(_("Checking ...")):
+
+                ap_storage = fcobj.apertures
+
+                p = app_obj.pool.apply_async(self.find_optim_mp, args=(ap_storage, self.decimals))
+                res = p.get()
+
+                if res[0] != 'ok':
+                    app_obj.inform.emit(res[0])
+                    return 'fail'
+                else:
+                    min_dist = res[1]
+
+                try:
+                    min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
+                    self.safe_tooldia = min_dist_truncated
+
+                    if self.safe_tooldia:
+                        # find the selected tool ID's
+                        sorted_tools = []
+                        table_items = self.ui.tools_table.selectedItems()
+                        sel_rows = {t.row() for t in table_items}
+                        for row in sel_rows:
+                            tid = int(self.ui.tools_table.item(row, 3).text())
+                            sorted_tools.append(tid)
+                        if not sorted_tools:
+                            msg = _("There are no tools selected in the Tool Table.")
+                            self.app.inform.emit('[ERROR_NOTCL] %s' % msg)
+                            return 'fail'
+
+                        # check if the tools diameters are less then the safe tool diameter
+                        for tool in sorted_tools:
+                            tool_dia = float(self.iso_tools[tool]['tooldia'])
+                            if tool_dia > self.safe_tooldia:
+                                msg = _("Incomplete isolation. "
+                                        "At least one tool could not do a complete isolation.")
+                                self.app.inform.emit('[WARNING] %s' % msg)
+                                break
+
+                        # reset the value to prepare for another isolation
+                        self.safe_tooldia = None
+                except Exception as ee:
+                    log.debug(str(ee))
+                    return
+
+        self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
+
+    def find_safe_tooldia_worker(self):
+        self.app.inform.emit(_("Checking tools for validity."))
+        self.units = self.app.defaults['units'].upper()
+
+        obj_name = self.ui.object_combo.currentText()
+
+        # Get source object.
+        try:
+            fcobj = self.app.collection.get_by_name(obj_name)
+        except Exception:
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(obj_name)))
+            return
+
+        if fcobj is None:
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
+            return
+
+        def job_thread(app_obj):
+            with self.app.proc_container.new(_("Checking ...")):
                 try:
                     old_disp_number = 0
                     pol_nr = 0
@@ -995,42 +1110,16 @@ class ToolIsolation(AppTool, Gerber):
                     min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
                     self.safe_tooldia = min_dist_truncated
 
-                    if is_display:
-                        self.optimal_found_sig.emit(min_dist_truncated)
+                    self.optimal_found_sig.emit(min_dist_truncated)
 
-                        app_obj.inform.emit('[success] %s: %s %s' %
-                                            (_("Optimal tool diameter found"), str(min_dist_truncated),
-                                             self.units.lower()))
-                    else:
-                        if self.safe_tooldia:
-                            # find the selected tool ID's
-                            sorted_tools = []
-                            table_items = self.ui.tools_table.selectedItems()
-                            sel_rows = {t.row() for t in table_items}
-                            for row in sel_rows:
-                                tid = int(self.ui.tools_table.item(row, 3).text())
-                                sorted_tools.append(tid)
-                            if not sorted_tools:
-                                msg = _("There are no tools selected in the Tool Table.")
-                                self.app.inform.emit('[ERROR_NOTCL] %s' % msg)
-                                return 'fail'
-
-                            # check if the tools diameters are less then the safe tool diameter
-                            for tool in sorted_tools:
-                                tool_dia = float(self.iso_tools[tool]['tooldia'])
-                                if tool_dia > self.safe_tooldia:
-                                    msg = _("Incomplete isolation. "
-                                            "At least one tool could not do a complete isolation.")
-                                    self.app.inform.emit('[WARNING] %s' % msg)
-                                    break
-
-                            # reset the value to prepare for another isolation
-                            self.safe_tooldia = None
+                    app_obj.inform.emit('[success] %s: %s %s' %
+                                        (_("Optimal tool diameter found"), str(min_dist_truncated),
+                                         self.units.lower()))
                 except Exception as ee:
                     log.debug(str(ee))
                     return
 
-        self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app, is_displayed]})
+        self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
 
     def on_tool_add(self, custom_dia=None):
         self.blockSignals(True)
@@ -1384,7 +1473,7 @@ class ToolIsolation(AppTool, Gerber):
             return
 
         if self.ui.valid_cb.get_value() is True:
-            self.find_safe_tooldia_worker(is_displayed=False)
+            self.find_safe_tooldia_multiprocessing()
 
         def worker_task(iso_obj):
             with self.app.proc_container.new(_("Isolating ...")):

+ 130 - 41
appTools/ToolNCC.py

@@ -845,10 +845,128 @@ class NonCopperClear(AppTool, Gerber):
             })
 
     def on_find_optimal_tooldia(self):
-        self.find_safe_tooldia_worker(is_displayed=True)
+        self.find_safe_tooldia_worker()
 
-    def find_safe_tooldia_worker(self, is_displayed):
-        self.app.inform.emit(_("NCC Tool. Checking tools for validity."))
+    @staticmethod
+    def find_optim_mp(aperture_storage, decimals):
+        msg = 'ok'
+        total_geo = []
+
+        for ap in list(aperture_storage.keys()):
+            if 'geometry' in aperture_storage[ap]:
+                for geo_el in aperture_storage[ap]['geometry']:
+                    if 'solid' in geo_el and geo_el['solid'] is not None and geo_el['solid'].is_valid:
+                        total_geo.append(geo_el['solid'])
+
+        total_geo = MultiPolygon(total_geo)
+        total_geo = total_geo.buffer(0)
+
+        try:
+            __ = iter(total_geo)
+            geo_len = len(total_geo)
+        except TypeError:
+            msg = ('[ERROR_NOTCL] %s' % _("The Gerber object has one Polygon as geometry.\n"
+                                          "There are no distances between geometry elements to be found."))
+
+        min_dict = {}
+        idx = 1
+        for geo in total_geo:
+            for s_geo in total_geo[idx:]:
+                # minimize the number of distances by not taking into considerations
+                # those that are too small
+                dist = geo.distance(s_geo)
+                dist = float('%.*f' % (decimals, dist))
+                loc_1, loc_2 = nearest_points(geo, s_geo)
+
+                proc_loc = (
+                    (float('%.*f' % (decimals, loc_1.x)), float('%.*f' % (decimals, loc_1.y))),
+                    (float('%.*f' % (decimals, loc_2.x)), float('%.*f' % (decimals, loc_2.y)))
+                )
+
+                if dist in min_dict:
+                    min_dict[dist].append(proc_loc)
+                else:
+                    min_dict[dist] = [proc_loc]
+
+            idx += 1
+
+        min_list = list(min_dict.keys())
+        min_dist = min(min_list)
+
+        return msg, min_dist
+
+    # multiprocessing variant
+    def find_safe_tooldia_multiprocessing(self):
+        self.app.inform.emit(_("Checking tools for validity."))
+        self.units = self.app.defaults['units'].upper()
+
+        obj_name = self.ui.object_combo.currentText()
+
+        # Get source object.
+        try:
+            fcobj = self.app.collection.get_by_name(obj_name)
+        except Exception:
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(obj_name)))
+            return
+
+        if fcobj is None:
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
+            return
+
+        def job_thread(app_obj):
+            with self.app.proc_container.new(_("Checking ...")):
+
+                ap_storage = fcobj.apertures
+
+                p = app_obj.pool.apply_async(self.find_optim_mp, args=(ap_storage, self.decimals))
+                res = p.get()
+
+                if res[0] != 'ok':
+                    app_obj.inform.emit(res[0])
+                    return 'fail'
+                else:
+                    min_dist = res[1]
+
+                try:
+                    min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
+                    self.safe_tooldia = min_dist_truncated
+
+                    # find the selected tool ID's
+                    sorted_tools = []
+                    table_items = self.ui.tools_table.selectedItems()
+                    sel_rows = {t.row() for t in table_items}
+                    for row in sel_rows:
+                        tid = int(self.ui.tools_table.item(row, 3).text())
+                        sorted_tools.append(tid)
+                    if not sorted_tools:
+                        msg = _("There are no tools selected in the Tool Table.")
+                        self.app.inform.emit('[ERROR_NOTCL] %s' % msg)
+                        return 'fail'
+
+                    # check if the tools diameters are less then the safe tool diameter
+                    suitable_tools = []
+                    for tool in sorted_tools:
+                        tool_dia = float(self.ncc_tools[tool]['tooldia'])
+                        if tool_dia <= self.safe_tooldia:
+                            suitable_tools.append(tool_dia)
+
+                    if not suitable_tools:
+                        msg = _("Incomplete isolation. None of the selected tools could do a complete isolation.")
+                        self.app.inform.emit('[WARNING] %s' % msg)
+                    else:
+                        msg = _("At least one of the selected tools can do a complete isolation.")
+                        self.app.inform.emit('[success] %s' % msg)
+
+                    # reset the value to prepare for another isolation
+                    self.safe_tooldia = None
+                except Exception as ee:
+                    log.debug(str(ee))
+                    return
+
+        self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
+
+    def find_safe_tooldia_worker(self):
+        self.app.inform.emit(_("Checking tools for validity."))
         self.units = self.app.defaults['units'].upper()
 
         obj_name = self.ui.object_combo.currentText()
@@ -864,8 +982,8 @@ class NonCopperClear(AppTool, Gerber):
             self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
             return
 
-        def job_thread(app_obj, is_display):
-            with self.app.proc_container.new(_("Working...")):
+        def job_thread(app_obj):
+            with self.app.proc_container.new(_("Checking ...")):
                 try:
                     old_disp_number = 0
                     pol_nr = 0
@@ -933,46 +1051,16 @@ class NonCopperClear(AppTool, Gerber):
                     min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
                     self.safe_tooldia = min_dist_truncated
 
-                    if is_display:
-                        self.optimal_found_sig.emit(min_dist_truncated)
-
-                        app_obj.inform.emit('[success] %s: %s %s' %
-                                            (_("Optimal tool diameter found"), str(min_dist_truncated),
-                                             self.units.lower()))
-                    else:
-                        # find the selected tool ID's
-                        sorted_tools = []
-                        table_items = self.ui.tools_table.selectedItems()
-                        sel_rows = {t.row() for t in table_items}
-                        for row in sel_rows:
-                            tid = int(self.ui.tools_table.item(row, 3).text())
-                            sorted_tools.append(tid)
-                        if not sorted_tools:
-                            msg = _("There are no tools selected in the Tool Table.")
-                            self.app.inform.emit('[ERROR_NOTCL] %s' % msg)
-                            return 'fail'
-
-                        # check if the tools diameters are less then the safe tool diameter
-                        suitable_tools = []
-                        for tool in sorted_tools:
-                            tool_dia = float(self.ncc_tools[tool]['tooldia'])
-                            if tool_dia <= self.safe_tooldia:
-                                suitable_tools.append(tool_dia)
-
-                        if not suitable_tools:
-                            msg = _("Incomplete isolation. None of the selected tools could do a complete isolation.")
-                            self.app.inform.emit('[WARNING] %s' % msg)
-                        else:
-                            msg = _("At least one of the selected tools can do a complete isolation.")
-                            self.app.inform.emit('[success] %s' % msg)
+                    self.optimal_found_sig.emit(min_dist_truncated)
 
-                        # reset the value to prepare for another isolation
-                        self.safe_tooldia = None
+                    app_obj.inform.emit('[success] %s: %s %s' %
+                                        (_("Optimal tool diameter found"), str(min_dist_truncated),
+                                         self.units.lower()))
                 except Exception as ee:
                     log.debug(str(ee))
                     return
 
-        self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app, is_displayed]})
+        self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
 
     def on_tool_add(self, custom_dia=None):
         self.blockSignals(True)
@@ -1327,7 +1415,8 @@ class NonCopperClear(AppTool, Gerber):
             return
 
         if self.ui.valid_cb.get_value() is True:
-            self.find_safe_tooldia_worker(is_displayed=False)
+            # this is done in another Process
+            self.find_safe_tooldia_multiprocessing()
 
         # use the selected tools in the tool table; get diameters for isolation
         self.iso_dia_list = []

+ 5 - 5
app_Main.py

@@ -7107,17 +7107,17 @@ class App(QtCore.QObject):
 
         obj = self.collection.get_active()
         if type(obj) == GeometryObject:
-            self.on_file_exportdxf()
+            self.f_handlers.on_file_exportdxf()
         elif type(obj) == ExcellonObject:
-            self.on_file_saveexcellon()
+            self.f_handlers.on_file_saveexcellon()
         elif type(obj) == CNCJobObject:
             obj.on_exportgcode_button_click()
         elif type(obj) == GerberObject:
-            self.on_file_savegerber()
+            self.f_handlers.on_file_savegerber()
         elif type(obj) == ScriptObject:
-            self.on_file_savescript()
+            self.f_handlers.on_file_savescript()
         elif type(obj) == DocumentObject:
-            self.on_file_savedocument()
+            self.f_handlers.on_file_savedocument()
 
     def obj_move(self):
         """

BIN
locale/de/LC_MESSAGES/strings.mo


Разница между файлами не показана из-за своего большого размера
+ 158 - 158
locale/de/LC_MESSAGES/strings.po


BIN
locale/en/LC_MESSAGES/strings.mo


Разница между файлами не показана из-за своего большого размера
+ 158 - 158
locale/en/LC_MESSAGES/strings.po


BIN
locale/es/LC_MESSAGES/strings.mo


Разница между файлами не показана из-за своего большого размера
+ 158 - 158
locale/es/LC_MESSAGES/strings.po


BIN
locale/fr/LC_MESSAGES/strings.mo


Разница между файлами не показана из-за своего большого размера
+ 158 - 158
locale/fr/LC_MESSAGES/strings.po


BIN
locale/it/LC_MESSAGES/strings.mo


Разница между файлами не показана из-за своего большого размера
+ 158 - 158
locale/it/LC_MESSAGES/strings.po


BIN
locale/pt_BR/LC_MESSAGES/strings.mo


Разница между файлами не показана из-за своего большого размера
+ 158 - 158
locale/pt_BR/LC_MESSAGES/strings.po


BIN
locale/ro/LC_MESSAGES/strings.mo


Разница между файлами не показана из-за своего большого размера
+ 158 - 160
locale/ro/LC_MESSAGES/strings.po


BIN
locale/ru/LC_MESSAGES/strings.mo


Разница между файлами не показана из-за своего большого размера
+ 157 - 159
locale/ru/LC_MESSAGES/strings.po


BIN
locale/tr/LC_MESSAGES/strings.mo


Разница между файлами не показана из-за своего большого размера
+ 157 - 160
locale/tr/LC_MESSAGES/strings.po


Разница между файлами не показана из-за своего большого размера
+ 160 - 160
locale_template/strings.pot


Некоторые файлы не были показаны из-за большого количества измененных файлов