From 3475ae00cd11cac04467e1ffd3f2bb2b6da791f1 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Wed, 1 Jan 2020 16:13:33 +0200 Subject: [PATCH] - fixed bug in NCC Tool: after trying to add a tool already in the Tool Table when trying to change the Tool Type the GUI does not change - final fix for app not quiting when running a script as argument, script that has the quit_flatcam Tcl command; fixed issue #360 - fixed issue #363. The Tcl command drillcncjob does not create tool cut, does not allow creation of gcode, it forces the usage of dwell and dwelltime parameters --- FlatCAMApp.py | 99 ++++++++++------------------ FlatCAMObj.py | 4 +- README.md | 7 ++ camlib.py | 1 - flatcamGUI/FlatCAMGUI.py | 47 +++++++++++++ flatcamTools/ToolNonCopperClear.py | 3 +- flatcamTools/ToolProperties.py | 2 +- flatcamTools/ToolShell.py | 8 +-- tclCommands/TclCommand.py | 6 +- tclCommands/TclCommandDrillcncjob.py | 19 +++++- 10 files changed, 116 insertions(+), 80 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 2675b776..08e1eb39 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -20,7 +20,6 @@ import shutil import stat from stat import S_IREAD, S_IRGRP, S_IROTH -import subprocess import ctypes # import tkinter as tk @@ -39,7 +38,7 @@ import gc from xml.dom.minidom import parseString as parse_xml_string from multiprocessing.connection import Listener, Client -from multiprocessing import Pool, cpu_count +from multiprocessing import Pool import socket from array import array @@ -241,6 +240,9 @@ class App(QtCore.QObject): # signal emitted when jumping jump_signal = pyqtSignal(tuple) + # close app signal + close_app_signal = pyqtSignal() + def __init__(self, user_defaults=True): """ Starts the application. @@ -2011,8 +2013,6 @@ class App(QtCore.QObject): self.ui.pref_close_button.clicked.connect(self.on_pref_close_button) self.ui.pref_defaults_button.clicked.connect(self.on_restore_defaults_preferences) - self.ui.pref_open_button.clicked.connect(self.on_preferences_open_folder) - self.ui.clear_btn.clicked.connect(self.on_gui_clear) # ############################################################################# # ######################### GUI PREFERENCES SIGNALS ########################### @@ -2136,6 +2136,8 @@ class App(QtCore.QObject): self.ui.grid_snap_btn.triggered.connect(self.on_grid_snap_triggered) + # signal to close the application + self.close_app_signal.connect(self.kill_app) # ##################################################################################### # ########### FINISHED CONNECTING SIGNALS ############################################# # ##################################################################################### @@ -2695,20 +2697,24 @@ class App(QtCore.QObject): sys.exit(2) if self.cmd_line_shellfile: - try: + if self.cmd_line_headless != 1: if self.ui.shell_dock.isHidden(): self.ui.shell_dock.show() - + try: with open(self.cmd_line_shellfile, "r") as myfile: - if show_splash: - self.splash.showMessage('%s: %ssec\n%s' % ( - _("Canvas initialization started.\n" - "Canvas initialization finished in"), '%.2f' % self.used_time, - _("Executing Tcl Script ...")), - alignment=Qt.AlignBottom | Qt.AlignLeft, - color=QtGui.QColor("gray")) + # if show_splash: + # self.splash.showMessage('%s: %ssec\n%s' % ( + # _("Canvas initialization started.\n" + # "Canvas initialization finished in"), '%.2f' % self.used_time, + # _("Executing Tcl Script ...")), + # alignment=Qt.AlignBottom | Qt.AlignLeft, + # color=QtGui.QColor("gray")) cmd_line_shellfile_text = myfile.read() - self.shell._sysShell.exec_command(cmd_line_shellfile_text) + if self.cmd_line_headless != 1: + self.shell._sysShell.exec_command(cmd_line_shellfile_text) + else: + self.shell._sysShell.exec_command(cmd_line_shellfile_text, no_echo=True) + except Exception as ext: print("ERROR: ", ext) sys.exit(2) @@ -3606,7 +3612,7 @@ class App(QtCore.QObject): try: if no_echo is False: - self.shell.open_proccessing() # Disables input box. + self.shell.open_processing() # Disables input box. result = self.tcl.eval(str(tcl_command_string)) if result != 'None' and no_echo is False: @@ -3624,7 +3630,7 @@ class App(QtCore.QObject): raise e finally: if no_echo is False: - self.shell.close_proccessing() + self.shell.close_processing() pass return result @@ -3978,58 +3984,13 @@ class App(QtCore.QObject): json.dump(defaults_from_file, f, default=to_dict, indent=2, sort_keys=True) f.close() except Exception: - self.inform.emit('[ERROR_NOTCL] %s' % _("Failed to write defaults to file.")) + self.inform.emit('[ERROR_NOTCL] %s %s' % (_("Failed to write defaults to file."), str(filename))) return if self.defaults["global_open_style"] is False: self.file_opened.emit("preferences", filename) self.file_saved.emit("preferences", filename) self.inform.emit('[success] %s: %s' % (_("Exported preferences to"), filename)) - def on_preferences_open_folder(self): - """ - Will open an Explorer window set to the folder path where the FlatCAM preferences files are usually saved. - - :return: None - """ - self.report_usage("on_preferences_open_folder()") - - if sys.platform == 'win32': - subprocess.Popen('explorer %s' % self.data_path) - elif sys.platform == 'darwin': - os.system('open "%s"' % self.data_path) - else: - subprocess.Popen(['xdg-open', self.data_path]) - self.inform.emit('[success] %s' % - _("FlatCAM Preferences Folder opened.")) - - def on_gui_clear(self): - theme_settings = QtCore.QSettings("Open Source", "FlatCAM") - theme_settings.setValue('theme', 'white') - - del theme_settings - - resource_loc = 'share' - - msgbox = QtWidgets.QMessageBox() - msgbox.setText(_("Are you sure you want to delete the GUI Settings? " - "\n") - ) - msgbox.setWindowTitle(_("Clear GUI Settings")) - msgbox.setWindowIcon(QtGui.QIcon(resource_loc + '/trash32.png')) - bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole) - bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole) - - msgbox.setDefaultButton(bt_no) - msgbox.exec_() - response = msgbox.clickedButton() - - if response == bt_yes: - settings = QSettings("Open Source", "FlatCAM") - for key in settings.allKeys(): - settings.remove(key) - # This will write the setting to the platform specific storage. - del settings - def save_geometry(self, x, y, width, height, notebook_width): """ Will save the application geometry and positions in the defaults discitionary to be restored at the next @@ -4981,13 +4942,14 @@ class App(QtCore.QObject): self.defaults["global_toolbar_view"] = tb_status # Save update options + filename = data_path + "/current_defaults.FlatConfig" try: - f = open(data_path + "/current_defaults.FlatConfig", "w") + f = open(filename, "w") json.dump(defaults, f, default=to_dict, indent=2, sort_keys=True) f.close() except Exception as e: log.debug("App.save_defaults() --> %s" % str(e)) - self.inform.emit('[ERROR_NOTCL] %s' % _("Failed to write defaults to file.")) + self.inform.emit('[ERROR_NOTCL] %s %s' % (_("Failed to write defaults to file."), str(filename))) return if not silent: @@ -5138,7 +5100,13 @@ class App(QtCore.QObject): del stgs log.debug("App.final_save() --> App UI state saved.") + self.close_app_signal.emit() + + def kill_app(self): QtWidgets.qApp.quit() + # When the main event loop is not started yet in which case the qApp.quit() will do nothing + # we use the following command + sys.exit(0) def on_portable_checked(self, state): """ @@ -10043,8 +10011,7 @@ class App(QtCore.QObject): if filename == "": if silent is False: - self.inform.emit('[WARNING_NOTCL] %s' % - _("Run TCL script cancelled.")) + self.inform.emit('[WARNING_NOTCL] %s' % _("Run TCL script cancelled.")) else: if self.cmd_line_headless != 1: if self.ui.shell_dock.isHidden(): diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 2458c62a..f28aa9ea 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -7252,7 +7252,7 @@ class FlatCAMScript(FlatCAMObj): # execute the actual Tcl command try: - self.app.shell.open_proccessing() # Disables input box. + self.app.shell.open_processing() # Disables input box. result = self.app.tcl.eval(str(new_command)) if result != 'None': @@ -7270,7 +7270,7 @@ class FlatCAMScript(FlatCAMObj): log.error("Exec command Exception: %s" % (result + '\n')) self.app.shell.append_error('ERROR: ' + result + '\n') - self.app.shell.close_proccessing() + self.app.shell.close_processing() def on_autocomplete_changed(self, state): if state: diff --git a/README.md b/README.md index 0d4e5943..b88766d1 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,13 @@ CAD program, and create G-Code for Isolation routing. ================================================= +1.01.2020 + +- fixed bug in NCC Tool: after trying to add a tool already in the Tool Table when trying to change the Tool Type the GUI does not change +- final fix for app not quiting when running a script as argument, script that has the quit_flatcam Tcl command; fixed issue #360 +- fixed issue #363. The Tcl command drillcncjob does not create tool cut, does not allow creation of gcode, it forces the usage of dwell and dwelltime parameters + + 30.12.2019 - Buffer sub-tool in Transform Tool: added the possibility to apply a factor effectively scaling the aperture size thus the copper features sizes diff --git a/camlib.py b/camlib.py index df328fed..fc3dbc3f 100644 --- a/camlib.py +++ b/camlib.py @@ -2550,7 +2550,6 @@ class CNCjob(Geometry): self.exc_cnc_tools[it[1]]['nr_slots'] = slot_no self.exc_cnc_tools[it[1]]['offset_z'] = z_off self.exc_cnc_tools[it[1]]['data'] = default_data - self.exc_cnc_tools[it[1]]['solid_geometry'] = deepcopy(sol_geo) self.app.inform.emit(_("Creating a list of points to drill...")) diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index e555bdb5..ade4b856 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -18,6 +18,9 @@ from matplotlib.backend_bases import KeyEvent as mpl_key_event import webbrowser from copy import deepcopy from datetime import datetime + +import subprocess +import os import gettext import FlatCAMTranslation as fcTranslate import builtins @@ -2340,6 +2343,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.lock_toolbar(lock=lock_state) self.lock_action.triggered[bool].connect(self.lock_toolbar) + self.pref_open_button.clicked.connect(self.on_preferences_open_folder) + self.clear_btn.clicked.connect(self.on_gui_clear) + # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # %%%%%%%%%%%%%%%%% GUI Building FINISHED %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2360,6 +2366,47 @@ class FlatCAMGUI(QtWidgets.QMainWindow): return False + def on_preferences_open_folder(self): + """ + Will open an Explorer window set to the folder path where the FlatCAM preferences files are usually saved. + + :return: None + """ + + if sys.platform == 'win32': + subprocess.Popen('explorer %s' % self.app.data_path) + elif sys.platform == 'darwin': + os.system('open "%s"' % self.app.data_path) + else: + subprocess.Popen(['xdg-open', self.app.data_path]) + self.app.inform.emit('[success] %s' % _("FlatCAM Preferences Folder opened.")) + + def on_gui_clear(self): + theme_settings = QtCore.QSettings("Open Source", "FlatCAM") + theme_settings.setValue('theme', 'white') + + del theme_settings + + resource_loc = self.app.resource_location + + msgbox = QtWidgets.QMessageBox() + msgbox.setText(_("Are you sure you want to delete the GUI Settings? \n")) + msgbox.setWindowTitle(_("Clear GUI Settings")) + msgbox.setWindowIcon(QtGui.QIcon(resource_loc + '/trash32.png')) + bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole) + bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole) + + msgbox.setDefaultButton(bt_no) + msgbox.exec_() + response = msgbox.clickedButton() + + if response == bt_yes: + settings = QSettings("Open Source", "FlatCAM") + for key in settings.allKeys(): + settings.remove(key) + # This will write the setting to the platform specific storage. + del settings + def populate_toolbars(self): """ Will populate the App Toolbars with theie actions diff --git a/flatcamTools/ToolNonCopperClear.py b/flatcamTools/ToolNonCopperClear.py index 4b3560cf..0a95370a 100644 --- a/flatcamTools/ToolNonCopperClear.py +++ b/flatcamTools/ToolNonCopperClear.py @@ -1007,7 +1007,8 @@ class NonCopperClear(FlatCAMTool, Gerber): if float('%.*f' % (self.decimals, tool_dia)) in tool_dias: if muted is None: self.app.inform.emit('[WARNING_NOTCL] %s' % _("Adding tool cancelled. Tool already in Tool Table.")) - self.tools_table.itemChanged.connect(self.on_tool_edit) + # self.tools_table.itemChanged.connect(self.on_tool_edit) + self.ui_connect() return else: if muted is None: diff --git a/flatcamTools/ToolProperties.py b/flatcamTools/ToolProperties.py index cdaef36c..03dfb755 100644 --- a/flatcamTools/ToolProperties.py +++ b/flatcamTools/ToolProperties.py @@ -468,7 +468,7 @@ class Properties(FlatCAMTool): _("Depth of Cut"), '%.*f %s' % ( self.decimals, - (obj.z_cut - obj.tool_offset[tool_dia]), + (obj.z_cut - abs(obj.tool_offset[tool_dia])), self.app.defaults['units'].lower() ) ], diff --git a/flatcamTools/ToolShell.py b/flatcamTools/ToolShell.py index 97e46f42..9c216bc7 100644 --- a/flatcamTools/ToolShell.py +++ b/flatcamTools/ToolShell.py @@ -56,7 +56,7 @@ class TermWidget(QWidget): self._history = [''] # current empty line self._historyIndex = 0 - def open_proccessing(self, detail=None): + def open_processing(self, detail=None): """ Open processing and disable using shell commands again until all commands are finished @@ -67,14 +67,14 @@ class TermWidget(QWidget): self._edit.setTextColor(Qt.white) self._edit.setTextBackgroundColor(Qt.darkGreen) if detail is None: - self._edit.setPlainText(_("...proccessing...")) + self._edit.setPlainText(_("...processing...")) else: - self._edit.setPlainText('%s [%s]' % (_("...proccessing..."), detail)) + self._edit.setPlainText('%s [%s]' % (_("...processing..."), detail)) self._edit.setDisabled(True) self._edit.setFocus() - def close_proccessing(self): + def close_processing(self): """ Close processing and enable using shell commands again :return: diff --git a/tclCommands/TclCommand.py b/tclCommands/TclCommand.py index e279fee2..294e0197 100644 --- a/tclCommands/TclCommand.py +++ b/tclCommands/TclCommand.py @@ -258,8 +258,8 @@ class TclCommand(object): :return: raise exception """ - # becouse of signaling we cannot call error to TCL from here but when task - # is finished also nonsignaled are handled here to better exception + # because of signaling we cannot call error to TCL from here but when task + # is finished also non-signaled are handled here to better exception # handling and displayed after command is finished raise self.app.TclErrorException(text) @@ -400,7 +400,7 @@ class TclCommandSignaled(TclCommand): passed_timeout = self.app.defaults['global_background_timeout'] # set detail for processing, it will be there until next open or close - self.app.shell.open_proccessing(self.get_current_command()) + self.app.shell.open_processing(self.get_current_command()) def handle_finished(obj): self.app.shell_command_finished.disconnect(handle_finished) diff --git a/tclCommands/TclCommandDrillcncjob.py b/tclCommands/TclCommandDrillcncjob.py index 2de0642a..511e2862 100644 --- a/tclCommands/TclCommandDrillcncjob.py +++ b/tclCommands/TclCommandDrillcncjob.py @@ -48,7 +48,7 @@ class TclCommandDrillcncjob(TclCommandSignaled): 'args': collections.OrderedDict([ ('name', 'Name of the source object.'), ('drilled_dias', - 'Comma separated tool diameters of the drills to be drilled (example: 0.6, 1.0 or 3.125).'), + 'Comma separated tool diameters of the drills to be drilled (example: 0.6,1.0 or 3.125). No space allowed'), ('drillz', 'Drill depth into material (example: -2.0).'), ('travelz', 'Travel distance above material (example: 2.0).'), ('feedrate', 'Drilling feed rate.'), @@ -74,7 +74,7 @@ class TclCommandDrillcncjob(TclCommandSignaled): ]), 'examples': ['drillcncjob test.TXT -drillz -1.5 -travelz 14 -feedrate 222 -feedrate_rapid 456 -spindlespeed 777' ' -toolchange True -toolchangez 33 -endz 22 -pp default\n' - 'Usage of -feedrate_rapid matter only when the posptocessor is using it, like -marlin-.'] + 'Usage of -feedrate_rapid matter only when the preprocessor is using it, like -marlin-.'] } def execute(self, args, unnamed_args): @@ -186,6 +186,9 @@ class TclCommandDrillcncjob(TclCommandSignaled): if bool(args['dwell']) and args['dwelltime']: job_obj.dwell = True job_obj.dwelltime = float(args['dwelltime']) + else: + job_obj.dwell = obj.options["dwell"] + job_obj.dwelltime = float(obj.options["dwelltime"]) job_obj.spindlespeed = args["spindlespeed"] if "spindlespeed" in args else None job_obj.pp_excellon_name = args["pp"] if "pp" in args and args["pp"] \ @@ -209,6 +212,18 @@ class TclCommandDrillcncjob(TclCommandSignaled): job_obj.generate_from_excellon_by_tool(obj, tools, drillz=drillz, toolchangez=toolchangez, endz=endz, toolchange=toolchange, excellon_optimization_type=opt_type) + + for t_item in job_obj.exc_cnc_tools: + job_obj.exc_cnc_tools[t_item]['data']['offset'] = \ + float(job_obj.exc_cnc_tools[t_item]['offset_z']) + float(drillz) + job_obj.exc_cnc_tools[t_item]['data']['ppname_e'] = obj.options['ppname_e'] + + # for now there is no tool offset support in this Tcl Command so we write the 0.0 value here + job_obj.tool_offset[t_item] = 0.0 + + print(job_obj.tool_offset) + job_obj.origin_kind = 'excellon' + job_obj.gcode_parse() job_obj.create_geometry()