diff --git a/FlatCAMApp.py b/FlatCAMApp.py index ce68ca28..4a41dbcd 100644 --- a/FlatCAMApp.py +++ b/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.")) diff --git a/README.md b/README.md index c3431a12..54296f3a 100644 --- a/README.md +++ b/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 diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index 505f36bf..31992b14 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/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("%s:" % _("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("%s:" % _("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("%s:" % _("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): diff --git a/flatcamGUI/GUIElements.py b/flatcamGUI/GUIElements.py index c2fc2525..38f94750 100644 --- a/flatcamGUI/GUIElements.py +++ b/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):