Преглед изворни кода

- added support for file associations with FlatCAM, for Windows

Marius Stanciu пре 6 година
родитељ
комит
9a8fa1ac4c
4 измењених фајлова са 273 додато и 30 уклоњено
  1. 135 28
      FlatCAMApp.py
  2. 4 0
      README.md
  3. 128 0
      flatcamGUI/FlatCAMGUI.py
  4. 6 2
      flatcamGUI/GUIElements.py

+ 135 - 28
FlatCAMApp.py

@@ -18,6 +18,8 @@ import shutil
 
 from stat import S_IREAD, S_IRGRP, S_IROTH
 import subprocess
+import ctypes
+import winreg
 
 import tkinter as tk
 from PyQt5 import QtPrintSupport
@@ -664,7 +666,13 @@ class App(QtCore.QObject):
             "tools_solderpaste_speedrev": self.ui.tools_defaults_form.tools_solderpaste_group.speedrev_entry,
             "tools_solderpaste_dwellrev": self.ui.tools_defaults_form.tools_solderpaste_group.dwellrev_entry,
             "tools_solderpaste_pp": self.ui.tools_defaults_form.tools_solderpaste_group.pp_combo,
-            "tools_sub_close_paths": self.ui.tools_defaults_form.tools_sub_group.close_paths_cb
+            "tools_sub_close_paths": self.ui.tools_defaults_form.tools_sub_group.close_paths_cb,
+
+            # file associations
+            "fa_excellon": self.ui.fa_defaults_form.fa_excellon_group.exc_list_text,
+            "fa_gcode": self.ui.fa_defaults_form.fa_gcode_group.gco_list_text,
+            # "fa_geometry": self.ui.fa_defaults_form.fa_geometry_group.close_paths_cb,
+            "fa_gerber": self.ui.fa_defaults_form.fa_gerber_group.grb_list_text,
 
         }
 
@@ -1033,7 +1041,15 @@ class App(QtCore.QObject):
             "tools_solderpaste_dwellrev": 1,
             "tools_solderpaste_pp": 'Paste_1',
 
-            "tools_sub_close_paths": True
+            "tools_sub_close_paths": True,
+
+            # file associations
+            "fa_excellon": ".drl, .xln, .drd, .tap, .exc, .ncd",
+            "fa_gcode": ".nc, .ncc, .tap, .gcode, .cnc, .ecs, .fnc, .dnc, .ncg, .gc, .fan, .fgc, .din, .xpi,"
+                        " .hnc, .h, .i, .ncp, .min, .gcd, .rol, .mpr, .ply, .out, .eia, .plt, .sbp, .mpf",
+            "fa_gerber": ".gbr, .ger, .gtl, .gbl, .gts, .gbs, .gtp, .gbp, .gto, .gbo, .gm1, .gml, .gm3, .gko, .cmp, "
+                         ".sol, .stc, .sts, .plc, .pls, .crc, .crs, .tsm, .bsm, .ly2, .ly15, .dim, .mil, .grb, .top, "
+                         ".bot, .smt, .smb, .sst, .ssb, .spt, .spb, .pho, .gdo, .art, .gbd",
         })
 
         # ##############################
@@ -1326,6 +1342,7 @@ class App(QtCore.QObject):
         self.geo_form = None
         self.cnc_form = None
         self.tools_form = None
+        self.fa_form = None
         self.on_options_combo_change(0)  # Will show the initial form
 
         # ################################
@@ -1767,7 +1784,6 @@ class App(QtCore.QObject):
                                   'version', 'write_gcode'
                                   ]
 
-
         self.ordinary_keywords = ['all', 'angle_x', 'angle_y', 'axis', 'axisoffset', 'box', 'center_x', 'center_y',
                                   'columns', 'combine', 'connect', 'contour', 'depthperpass', 'dia', 'dist', 'drillz',
                                   'endz', 'extracut', 'factor', 'False', 'false', 'feedrate', 'feedrate_rapid',
@@ -1967,7 +1983,6 @@ class App(QtCore.QObject):
             'unload', 'unset', 'update', 'uplevel', 'upvar', 'variable', 'vwait', 'while', 'yield', 'yieldto', 'zlib'
         ]
 
-
         self.myKeywords = self.tcl_commands_list + self.ordinary_keywords + self.tcl_keywords
 
         # ###########################################
@@ -2060,7 +2075,7 @@ class App(QtCore.QObject):
         # ################################################
         # ######### Variables for global usage ###########
         # ################################################
-
+        self.on_apply_associations()
         # coordinates for relative position display
         self.rel_point1 = (0, 0)
         self.rel_point2 = (0, 0)
@@ -2247,6 +2262,40 @@ class App(QtCore.QObject):
                 except Exception as e:
                     log.debug("Could not open FlatCAM Script file as App parameter due: %s" % str(e))
 
+            else:
+                exc_list = self.ui.fa_defaults_form.fa_excellon_group.exc_list_text.get_value().split(',')
+                for ext in exc_list:
+                    proc_ext = ext.replace(' ', '')
+                    if proc_ext.lower() in argument.lower():
+                        file_name = str(argument)
+                        if file_name == "":
+                            self.inform.emit(_("Open Script file failed."))
+                        else:
+                            self.on_fileopenexcellon(name=file_name)
+                            return
+
+                gco_list = self.ui.fa_defaults_form.fa_gcode_group.gco_list_text.get_value().split(',')
+                for ext in gco_list:
+                    proc_ext = ext.replace(' ', '')
+                    if proc_ext.lower() in argument.lower():
+                        file_name = str(argument)
+                        if file_name == "":
+                            self.inform.emit(_("Open Script file failed."))
+                        else:
+                            self.on_fileopengcode(name=file_name)
+                            return
+
+                grb_list = self.ui.fa_defaults_form.fa_gerber_group.grb_list_text.get_value().split(',')
+                for ext in grb_list:
+                    proc_ext = ext.replace(' ', '')
+                    if proc_ext.lower() in argument.lower():
+                        file_name = str(argument)
+                        if file_name == "":
+                            self.inform.emit(_("Open Script file failed."))
+                        else:
+                            self.on_fileopengerber(name=file_name)
+                            return
+
     def set_ui_title(self, name):
         self.ui.setWindowTitle('FlatCAM %s %s - %s    %s' %
                                (self.version,
@@ -3791,6 +3840,44 @@ class App(QtCore.QObject):
         else:
             self.ui.shell_dock.show()
 
+    def on_apply_associations(self):
+        new_reg_path = 'Software\\Classes\\'
+        # find if the current user is admin
+        try:
+            is_admin = os.getuid() == 0
+        except AttributeError:
+            is_admin = ctypes.windll.shell32.IsUserAnAdmin() == 1
+
+        if is_admin is True:
+            root_path = winreg.HKEY_CLASSES_ROOT
+        else:
+            root_path = winreg.HKEY_CURRENT_USER
+
+        # create the keys
+        def set_reg(name, root_path, new_reg_path, value):
+            try:
+                winreg.CreateKey(root_path, new_reg_path)
+                with winreg.OpenKey(root_path, new_reg_path, 0, winreg.KEY_WRITE) as registry_key:
+                    winreg.SetValueEx(registry_key, name, 0, winreg.REG_SZ, value)
+                return True
+            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')
+
+        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')
+
+        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')
+
     def on_edit_join(self, name=None):
         """
         Callback for Edit->Join. Joins the selected geometry objects into
@@ -4342,6 +4429,7 @@ class App(QtCore.QObject):
             self.geo_form = self.ui.geometry_defaults_form
             self.cnc_form = self.ui.cncjob_defaults_form
             self.tools_form = self.ui.tools_defaults_form
+            self.fa_form = self.ui.fa_defaults_form
         elif sel == 1:
             self.gen_form = self.ui.general_options_form
             self.ger_form = self.ui.gerber_options_form
@@ -4349,6 +4437,7 @@ class App(QtCore.QObject):
             self.geo_form = self.ui.geometry_options_form
             self.cnc_form = self.ui.cncjob_options_form
             self.tools_form = self.ui.tools_options_form
+            self.fa_form = self.ui.fa_options_form
         else:
             return
 
@@ -4394,6 +4483,13 @@ class App(QtCore.QObject):
         self.ui.tools_scroll_area.setWidget(self.tools_form)
         self.tools_form.show()
 
+        try:
+            self.ui.fa_scroll_area.takeWidget()
+        except:
+            self.log.debug("Nothing to remove")
+        self.ui.fa_scroll_area.setWidget(self.fa_form)
+        self.fa_form.show()
+
         self.log.debug("Finished GUI form initialization.")
 
         # self.options2form()
@@ -6622,7 +6718,7 @@ class App(QtCore.QObject):
         self.report_usage("obj_move()")
         self.move_tool.run(toggle=False)
 
-    def on_fileopengerber(self):
+    def on_fileopengerber(self, name=None):
         """
         File menu callback for opening a Gerber.
 
@@ -6643,13 +6739,17 @@ class App(QtCore.QObject):
                    "Mentor Files (*.pho *.gdo);;" \
                    "All Files (*.*)"
 
-        try:
-            filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open Gerber"),
-                                                                   directory=self.get_last_folder(), filter=_filter_)
-        except TypeError:
-            filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open Gerber"), filter=_filter_)
+        if name is None:
+            try:
+                filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open Gerber"),
+                                                                       directory=self.get_last_folder(),
+                                                                       filter=_filter_)
+            except TypeError:
+                filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open Gerber"), filter=_filter_)
 
-        filenames = [str(filename) for filename in filenames]
+            filenames = [str(filename) for filename in filenames]
+        else:
+            filenames = [name]
 
         if len(filenames) == 0:
             self.inform.emit(_("[WARNING_NOTCL] Open Gerber cancelled."))
@@ -6659,7 +6759,7 @@ class App(QtCore.QObject):
                     self.worker_task.emit({'fcn': self.open_gerber,
                                            'params': [filename]})
 
-    def on_fileopenexcellon(self):
+    def on_fileopenexcellon(self, name=None):
         """
         File menu callback for opening an Excellon file.
 
@@ -6671,14 +6771,16 @@ class App(QtCore.QObject):
 
         _filter_ = "Excellon Files (*.drl *.txt *.xln *.drd *.tap *.exc *.ncd);;" \
                    "All Files (*.*)"
-
-        try:
-            filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open Excellon"),
-                                                                   directory=self.get_last_folder(), filter=_filter_)
-        except TypeError:
-            filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open Excellon"), filter=_filter_)
-
-        filenames = [str(filename) for filename in filenames]
+        if name is None:
+            try:
+                filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open Excellon"),
+                                                                       directory=self.get_last_folder(),
+                                                                       filter=_filter_)
+            except TypeError:
+                filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open Excellon"), filter=_filter_)
+            filenames = [str(filename) for filename in filenames]
+        else:
+            filenames = [str(name)]
 
         if len(filenames) == 0:
             self.inform.emit(_("[WARNING_NOTCL] Open Excellon cancelled."))
@@ -6688,7 +6790,7 @@ class App(QtCore.QObject):
                     self.worker_task.emit({'fcn': self.open_excellon,
                                            'params': [filename]})
 
-    def on_fileopengcode(self):
+    def on_fileopengcode(self, name=None):
         """
         File menu call back for opening gcode.
 
@@ -6702,13 +6804,18 @@ class App(QtCore.QObject):
         _filter_ = "G-Code Files (*.txt *.nc *.ncc *.tap *.gcode *.cnc *.ecs *.fnc *.dnc *.ncg *.gc *.fan *.fgc" \
                    " *.din *.xpi *.hnc *.h *.i *.ncp *.min *.gcd *.rol *.mpr *.ply *.out *.eia *.plt *.sbp *.mpf);;" \
                    "All Files (*.*)"
-        try:
-            filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open G-Code"),
-                                                                   directory=self.get_last_folder(), filter=_filter_)
-        except TypeError:
-            filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open G-Code"), filter=_filter_)
 
-        filenames = [str(filename) for filename in filenames]
+        if name is None:
+            try:
+                filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open G-Code"),
+                                                                       directory=self.get_last_folder(),
+                                                                       filter=_filter_)
+            except TypeError:
+                filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open G-Code"), filter=_filter_)
+
+            filenames = [str(filename) for filename in filenames]
+        else:
+            filenames = [name]
 
         if len(filenames) == 0:
             self.inform.emit(_("[WARNING_NOTCL] Open G-Code cancelled."))

+ 4 - 0
README.md

@@ -9,6 +9,10 @@ CAD program, and create G-Code for Isolation routing.
 
 =================================================
 
+26.08.2019
+
+- added support for file associations with FlatCAM, for Windows
+
 25.08.2019
 
 - initial add of a new Tcl Command named CopperClear

+ 128 - 0
flatcamGUI/FlatCAMGUI.py

@@ -938,6 +938,16 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.tools_scroll_area = QtWidgets.QScrollArea()
         self.tools_tab_lay.addWidget(self.tools_scroll_area)
 
+        self.fa_tab = QtWidgets.QWidget()
+        self.fa_tab.setObjectName("fa_tab")
+        self.pref_tab_area.addTab(self.fa_tab, _("FILE ASSOCIATIONS"))
+        self.fa_tab_lay = QtWidgets.QVBoxLayout()
+        self.fa_tab_lay.setContentsMargins(2, 2, 2, 2)
+        self.fa_tab.setLayout(self.fa_tab_lay)
+
+        self.fa_scroll_area = QtWidgets.QScrollArea()
+        self.fa_tab_lay.addWidget(self.fa_scroll_area)
+
         self.pref_tab_bottom_layout = QtWidgets.QHBoxLayout()
         self.pref_tab_bottom_layout.setAlignment(QtCore.Qt.AlignVCenter)
         self.pref_tab_layout.addLayout(self.pref_tab_bottom_layout)
@@ -1848,6 +1858,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.geometry_defaults_form = GeometryPreferencesUI()
         self.cncjob_defaults_form = CNCJobPreferencesUI()
         self.tools_defaults_form = ToolsPreferencesUI()
+        self.fa_defaults_form = FAPreferencesUI()
 
         self.general_options_form = GeneralPreferencesUI()
         self.gerber_options_form = GerberPreferencesUI()
@@ -1855,6 +1866,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.geometry_options_form = GeometryPreferencesUI()
         self.cncjob_options_form = CNCJobPreferencesUI()
         self.tools_options_form = ToolsPreferencesUI()
+        self.fa_options_form = FAPreferencesUI()
 
         QtWidgets.qApp.installEventFilter(self)
 
@@ -3480,6 +3492,27 @@ class CNCJobPreferencesUI(QtWidgets.QWidget):
         self.layout.addStretch()
 
 
+class FAPreferencesUI(QtWidgets.QWidget):
+
+    def __init__(self, parent=None):
+        QtWidgets.QWidget.__init__(self, parent=parent)
+        self.layout = QtWidgets.QHBoxLayout()
+        self.setLayout(self.layout)
+
+        self.fa_excellon_group = FAExcPrefGroupUI()
+        self.fa_excellon_group.setMinimumWidth(260)
+        self.fa_gcode_group = FAGcoPrefGroupUI()
+        self.fa_gcode_group.setMinimumWidth(260)
+        self.fa_gerber_group = FAGrbPrefGroupUI()
+        self.fa_gerber_group.setMinimumWidth(260)
+
+        self.layout.addWidget(self.fa_excellon_group)
+        self.layout.addWidget(self.fa_gcode_group)
+        self.layout.addWidget(self.fa_gerber_group)
+
+        self.layout.addStretch()
+
+
 class OptionsGroupUI(QtWidgets.QGroupBox):
     def __init__(self, title, parent=None):
         # QtGui.QGroupBox.__init__(self, title, parent=parent)
@@ -7384,6 +7417,101 @@ class ToolsSubPrefGroupUI(OptionsGroupUI):
         self.layout.addStretch()
 
 
+class FAExcPrefGroupUI(OptionsGroupUI):
+    def __init__(self, parent=None):
+        # OptionsGroupUI.__init__(self, "Excellon File associations Preferences", parent=None)
+        super(FAExcPrefGroupUI, self).__init__(self)
+
+        self.setTitle(str(_("Excellon File associations")))
+
+        # ## Export G-Code
+        self.exc_list_label = QtWidgets.QLabel("<b>%s:</b>" % _("Extensions list"))
+        self.exc_list_label.setToolTip(
+            _("List of file extensions to be\n"
+              "associated with FlatCAM.")
+        )
+        self.layout.addWidget(self.exc_list_label)
+
+        self.exc_list_text = FCTextArea()
+        self.exc_list_text.sizeHint(custom_sizehint=150)
+        font = QtGui.QFont()
+        font.setPointSize(12)
+        self.exc_list_text.setFont(font)
+
+        self.layout.addWidget(self.exc_list_text)
+
+        self.exc_list_btn = FCButton("Apply")
+        self.exc_list_btn.setToolTip(_("Apply the file associations.\n"
+                                       "They will be active after next logon.\n"
+                                       "This work only in Windows."))
+        self.layout.addWidget(self.exc_list_btn)
+
+        # self.layout.addStretch()
+
+
+class FAGcoPrefGroupUI(OptionsGroupUI):
+    def __init__(self, parent=None):
+        # OptionsGroupUI.__init__(self, "Gcode File associations Preferences", parent=None)
+        super(FAGcoPrefGroupUI, self).__init__(self)
+
+        self.setTitle(str(_("GCode File associations")))
+
+        # ## Export G-Code
+        self.gco_list_label = QtWidgets.QLabel("<b>%s:</b>" % _("Extensions list"))
+        self.gco_list_label.setToolTip(
+            _("List of file extensions to be\n"
+              "associated with FlatCAM.")
+        )
+        self.layout.addWidget(self.gco_list_label)
+
+        self.gco_list_text = FCTextArea()
+        self.gco_list_text.sizeHint(custom_sizehint=150)
+        font = QtGui.QFont()
+        font.setPointSize(12)
+        self.gco_list_text.setFont(font)
+
+        self.layout.addWidget(self.gco_list_text)
+
+        self.gco_list_btn = FCButton("Apply")
+        self.gco_list_btn.setToolTip(_("Apply the file associations.\n"
+                                       "They will be active after next logon.\n"
+                                       "This work only in Windows."))
+        self.layout.addWidget(self.gco_list_btn)
+
+        # self.layout.addStretch()
+
+
+class FAGrbPrefGroupUI(OptionsGroupUI):
+    def __init__(self, parent=None):
+        # OptionsGroupUI.__init__(self, "Gerber File associations Preferences", parent=None)
+        super(FAGrbPrefGroupUI, self).__init__(self)
+
+        self.setTitle(str(_("Gerber File associations")))
+
+        # ## Export G-Code
+        self.grb_list_label = QtWidgets.QLabel("<b>%s:</b>" % _("Extensions list"))
+        self.grb_list_label.setToolTip(
+            _("List of file extensions to be\n"
+              "associated with FlatCAM.")
+        )
+        self.layout.addWidget(self.grb_list_label)
+
+        self.grb_list_text = FCTextArea()
+        self.grb_list_text.sizeHint(custom_sizehint=150)
+        self.layout.addWidget(self.grb_list_text)
+        font = QtGui.QFont()
+        font.setPointSize(12)
+        self.grb_list_text.setFont(font)
+
+        self.grb_list_btn = FCButton("Apply")
+        self.grb_list_btn.setToolTip(_("Apply the file associations.\n"
+                                       "They will be active after next logon.\n"
+                                       "This work only in Windows."))
+        self.layout.addWidget(self.grb_list_btn)
+
+        # self.layout.addStretch()
+
+
 class FlatCAMActivityView(QtWidgets.QWidget):
 
     def __init__(self, parent=None):

+ 6 - 2
flatcamGUI/GUIElements.py

@@ -531,9 +531,13 @@ class FCTextArea(QtWidgets.QPlainTextEdit):
     def get_value(self):
         return str(self.toPlainText())
 
-    def sizeHint(self):
+    def sizeHint(self, custom_sizehint=None):
         default_hint_size = super(FCTextArea, self).sizeHint()
-        return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
+
+        if custom_sizehint is None:
+            return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
+        else:
+            return QtCore.QSize(custom_sizehint, default_hint_size.height())
 
 
 class FCTextAreaRich(QtWidgets.QTextEdit):