- 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
This commit is contained in:
Marius Stanciu 2020-01-01 16:13:33 +02:00 committed by Marius
parent c8955e0a00
commit 3475ae00cd
10 changed files with 116 additions and 80 deletions

View File

@ -20,7 +20,6 @@ import shutil
import stat import stat
from stat import S_IREAD, S_IRGRP, S_IROTH from stat import S_IREAD, S_IRGRP, S_IROTH
import subprocess
import ctypes import ctypes
# import tkinter as tk # import tkinter as tk
@ -39,7 +38,7 @@ import gc
from xml.dom.minidom import parseString as parse_xml_string from xml.dom.minidom import parseString as parse_xml_string
from multiprocessing.connection import Listener, Client from multiprocessing.connection import Listener, Client
from multiprocessing import Pool, cpu_count from multiprocessing import Pool
import socket import socket
from array import array from array import array
@ -241,6 +240,9 @@ class App(QtCore.QObject):
# signal emitted when jumping # signal emitted when jumping
jump_signal = pyqtSignal(tuple) jump_signal = pyqtSignal(tuple)
# close app signal
close_app_signal = pyqtSignal()
def __init__(self, user_defaults=True): def __init__(self, user_defaults=True):
""" """
Starts the application. 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_close_button.clicked.connect(self.on_pref_close_button)
self.ui.pref_defaults_button.clicked.connect(self.on_restore_defaults_preferences) 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 ########################### # ######################### GUI PREFERENCES SIGNALS ###########################
@ -2136,6 +2136,8 @@ class App(QtCore.QObject):
self.ui.grid_snap_btn.triggered.connect(self.on_grid_snap_triggered) 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 ############################################# # ########### FINISHED CONNECTING SIGNALS #############################################
# ##################################################################################### # #####################################################################################
@ -2695,20 +2697,24 @@ class App(QtCore.QObject):
sys.exit(2) sys.exit(2)
if self.cmd_line_shellfile: if self.cmd_line_shellfile:
try: if self.cmd_line_headless != 1:
if self.ui.shell_dock.isHidden(): if self.ui.shell_dock.isHidden():
self.ui.shell_dock.show() self.ui.shell_dock.show()
try:
with open(self.cmd_line_shellfile, "r") as myfile: with open(self.cmd_line_shellfile, "r") as myfile:
if show_splash: # if show_splash:
self.splash.showMessage('%s: %ssec\n%s' % ( # self.splash.showMessage('%s: %ssec\n%s' % (
_("Canvas initialization started.\n" # _("Canvas initialization started.\n"
"Canvas initialization finished in"), '%.2f' % self.used_time, # "Canvas initialization finished in"), '%.2f' % self.used_time,
_("Executing Tcl Script ...")), # _("Executing Tcl Script ...")),
alignment=Qt.AlignBottom | Qt.AlignLeft, # alignment=Qt.AlignBottom | Qt.AlignLeft,
color=QtGui.QColor("gray")) # color=QtGui.QColor("gray"))
cmd_line_shellfile_text = myfile.read() 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: except Exception as ext:
print("ERROR: ", ext) print("ERROR: ", ext)
sys.exit(2) sys.exit(2)
@ -3606,7 +3612,7 @@ class App(QtCore.QObject):
try: try:
if no_echo is False: 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)) result = self.tcl.eval(str(tcl_command_string))
if result != 'None' and no_echo is False: if result != 'None' and no_echo is False:
@ -3624,7 +3630,7 @@ class App(QtCore.QObject):
raise e raise e
finally: finally:
if no_echo is False: if no_echo is False:
self.shell.close_proccessing() self.shell.close_processing()
pass pass
return result return result
@ -3978,58 +3984,13 @@ class App(QtCore.QObject):
json.dump(defaults_from_file, f, default=to_dict, indent=2, sort_keys=True) json.dump(defaults_from_file, f, default=to_dict, indent=2, sort_keys=True)
f.close() f.close()
except Exception: 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 return
if self.defaults["global_open_style"] is False: if self.defaults["global_open_style"] is False:
self.file_opened.emit("preferences", filename) self.file_opened.emit("preferences", filename)
self.file_saved.emit("preferences", filename) self.file_saved.emit("preferences", filename)
self.inform.emit('[success] %s: %s' % (_("Exported preferences to"), 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): 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 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 self.defaults["global_toolbar_view"] = tb_status
# Save update options # Save update options
filename = data_path + "/current_defaults.FlatConfig"
try: 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) json.dump(defaults, f, default=to_dict, indent=2, sort_keys=True)
f.close() f.close()
except Exception as e: except Exception as e:
log.debug("App.save_defaults() --> %s" % str(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 return
if not silent: if not silent:
@ -5138,7 +5100,13 @@ class App(QtCore.QObject):
del stgs del stgs
log.debug("App.final_save() --> App UI state saved.") log.debug("App.final_save() --> App UI state saved.")
self.close_app_signal.emit()
def kill_app(self):
QtWidgets.qApp.quit() 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): def on_portable_checked(self, state):
""" """
@ -10043,8 +10011,7 @@ class App(QtCore.QObject):
if filename == "": if filename == "":
if silent is False: if silent is False:
self.inform.emit('[WARNING_NOTCL] %s' % self.inform.emit('[WARNING_NOTCL] %s' % _("Run TCL script cancelled."))
_("Run TCL script cancelled."))
else: else:
if self.cmd_line_headless != 1: if self.cmd_line_headless != 1:
if self.ui.shell_dock.isHidden(): if self.ui.shell_dock.isHidden():

View File

@ -7252,7 +7252,7 @@ class FlatCAMScript(FlatCAMObj):
# execute the actual Tcl command # execute the actual Tcl command
try: 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)) result = self.app.tcl.eval(str(new_command))
if result != 'None': if result != 'None':
@ -7270,7 +7270,7 @@ class FlatCAMScript(FlatCAMObj):
log.error("Exec command Exception: %s" % (result + '\n')) log.error("Exec command Exception: %s" % (result + '\n'))
self.app.shell.append_error('ERROR: ' + 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): def on_autocomplete_changed(self, state):
if state: if state:

View File

@ -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 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 - Buffer sub-tool in Transform Tool: added the possibility to apply a factor effectively scaling the aperture size thus the copper features sizes

View File

@ -2550,7 +2550,6 @@ class CNCjob(Geometry):
self.exc_cnc_tools[it[1]]['nr_slots'] = slot_no 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]]['offset_z'] = z_off
self.exc_cnc_tools[it[1]]['data'] = default_data self.exc_cnc_tools[it[1]]['data'] = default_data
self.exc_cnc_tools[it[1]]['solid_geometry'] = deepcopy(sol_geo) self.exc_cnc_tools[it[1]]['solid_geometry'] = deepcopy(sol_geo)
self.app.inform.emit(_("Creating a list of points to drill...")) self.app.inform.emit(_("Creating a list of points to drill..."))

View File

@ -18,6 +18,9 @@ from matplotlib.backend_bases import KeyEvent as mpl_key_event
import webbrowser import webbrowser
from copy import deepcopy from copy import deepcopy
from datetime import datetime from datetime import datetime
import subprocess
import os
import gettext import gettext
import FlatCAMTranslation as fcTranslate import FlatCAMTranslation as fcTranslate
import builtins import builtins
@ -2340,6 +2343,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.lock_toolbar(lock=lock_state) self.lock_toolbar(lock=lock_state)
self.lock_action.triggered[bool].connect(self.lock_toolbar) 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 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # %%%%%%%%%%%%%%%%% GUI Building FINISHED %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -2360,6 +2366,47 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
return False 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): def populate_toolbars(self):
""" """
Will populate the App Toolbars with theie actions Will populate the App Toolbars with theie actions

View File

@ -1007,7 +1007,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
if float('%.*f' % (self.decimals, tool_dia)) in tool_dias: if float('%.*f' % (self.decimals, tool_dia)) in tool_dias:
if muted is None: if muted is None:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Adding tool cancelled. Tool already in Tool Table.")) 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 return
else: else:
if muted is None: if muted is None:

View File

@ -468,7 +468,7 @@ class Properties(FlatCAMTool):
_("Depth of Cut"), _("Depth of Cut"),
'%.*f %s' % ( '%.*f %s' % (
self.decimals, self.decimals,
(obj.z_cut - obj.tool_offset[tool_dia]), (obj.z_cut - abs(obj.tool_offset[tool_dia])),
self.app.defaults['units'].lower() self.app.defaults['units'].lower()
) )
], ],

View File

@ -56,7 +56,7 @@ class TermWidget(QWidget):
self._history = [''] # current empty line self._history = [''] # current empty line
self._historyIndex = 0 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 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.setTextColor(Qt.white)
self._edit.setTextBackgroundColor(Qt.darkGreen) self._edit.setTextBackgroundColor(Qt.darkGreen)
if detail is None: if detail is None:
self._edit.setPlainText(_("...proccessing...")) self._edit.setPlainText(_("...processing..."))
else: else:
self._edit.setPlainText('%s [%s]' % (_("...proccessing..."), detail)) self._edit.setPlainText('%s [%s]' % (_("...processing..."), detail))
self._edit.setDisabled(True) self._edit.setDisabled(True)
self._edit.setFocus() self._edit.setFocus()
def close_proccessing(self): def close_processing(self):
""" """
Close processing and enable using shell commands again Close processing and enable using shell commands again
:return: :return:

View File

@ -258,8 +258,8 @@ class TclCommand(object):
:return: raise exception :return: raise exception
""" """
# becouse of signaling we cannot call error to TCL from here but when task # because of signaling we cannot call error to TCL from here but when task
# is finished also nonsignaled are handled here to better exception # is finished also non-signaled are handled here to better exception
# handling and displayed after command is finished # handling and displayed after command is finished
raise self.app.TclErrorException(text) raise self.app.TclErrorException(text)
@ -400,7 +400,7 @@ class TclCommandSignaled(TclCommand):
passed_timeout = self.app.defaults['global_background_timeout'] passed_timeout = self.app.defaults['global_background_timeout']
# set detail for processing, it will be there until next open or close # 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): def handle_finished(obj):
self.app.shell_command_finished.disconnect(handle_finished) self.app.shell_command_finished.disconnect(handle_finished)

View File

@ -48,7 +48,7 @@ class TclCommandDrillcncjob(TclCommandSignaled):
'args': collections.OrderedDict([ 'args': collections.OrderedDict([
('name', 'Name of the source object.'), ('name', 'Name of the source object.'),
('drilled_dias', ('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).'), ('drillz', 'Drill depth into material (example: -2.0).'),
('travelz', 'Travel distance above material (example: 2.0).'), ('travelz', 'Travel distance above material (example: 2.0).'),
('feedrate', 'Drilling feed rate.'), ('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' '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' ' -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): def execute(self, args, unnamed_args):
@ -186,6 +186,9 @@ class TclCommandDrillcncjob(TclCommandSignaled):
if bool(args['dwell']) and args['dwelltime']: if bool(args['dwell']) and args['dwelltime']:
job_obj.dwell = True job_obj.dwell = True
job_obj.dwelltime = float(args['dwelltime']) 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.spindlespeed = args["spindlespeed"] if "spindlespeed" in args else None
job_obj.pp_excellon_name = args["pp"] if "pp" in args and args["pp"] \ 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, job_obj.generate_from_excellon_by_tool(obj, tools, drillz=drillz, toolchangez=toolchangez,
endz=endz, endz=endz,
toolchange=toolchange, excellon_optimization_type=opt_type) 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.gcode_parse()
job_obj.create_geometry() job_obj.create_geometry()