- trying to make loading a project an easier task for the application

This commit is contained in:
Marius Stanciu
2022-03-27 22:18:22 +03:00
committed by Marius
parent eec366ce7f
commit c8620fd9d2
2 changed files with 156 additions and 138 deletions

View File

@@ -7,6 +7,10 @@ CHANGELOG for FlatCAM Evo beta
================================================= =================================================
27.03.2022
- trying to make loading a project an easier task for the application
24.03.2022 24.03.2022
- refactoring names for some classes - refactoring names for some classes

View File

@@ -226,21 +226,16 @@ class App(QtCore.QObject):
file_opened = QtCore.pyqtSignal(str, str) file_opened = QtCore.pyqtSignal(str, str)
# File type and filename # File type and filename
file_saved = QtCore.pyqtSignal(str, str) file_saved = QtCore.pyqtSignal(str, str)
# close app signal # close app signal
close_app_signal = pyqtSignal() close_app_signal = pyqtSignal()
# will perform the cleanup operation after a Graceful Exit # will perform the cleanup operation after a Graceful Exit
# usefull for the NCC Tool and Paint Tool where some progressive plotting might leave # usefull for the NCC Tool and Paint Tool where some progressive plotting might leave
# graphic residues behind # graphic residues behind
cleanup = pyqtSignal() cleanup = pyqtSignal()
# emitted when the new_project is created in a threaded way # emitted when the new_project is created in a threaded way
new_project_signal = pyqtSignal() new_project_signal = pyqtSignal()
# Percentage of progress # Percentage of progress
progress = QtCore.pyqtSignal(int) progress = QtCore.pyqtSignal(int)
# Emitted when a new object has been added or deleted from/to the collection # Emitted when a new object has been added or deleted from/to the collection
object_status_changed = QtCore.pyqtSignal(object, str, str) object_status_changed = QtCore.pyqtSignal(object, str, str)
@@ -248,31 +243,28 @@ class App(QtCore.QObject):
# Emmited when shell command is finished(one command only) # Emmited when shell command is finished(one command only)
shell_command_finished = QtCore.pyqtSignal(object) shell_command_finished = QtCore.pyqtSignal(object)
# Emitted when multiprocess pool has been recreated # Emitted when multiprocess pool has been recreated
pool_recreated = QtCore.pyqtSignal(object) pool_recreated = QtCore.pyqtSignal(object)
# Emitted when an unhandled exception happens # Emitted when an unhandled exception happens
# in the worker task. # in the worker task.
thread_exception = QtCore.pyqtSignal(object) thread_exception = QtCore.pyqtSignal(object)
# used to signal that there are arguments for the app # used to signal that there are arguments for the app
args_at_startup = QtCore.pyqtSignal(list) args_at_startup = QtCore.pyqtSignal(list)
# a reusable signal to replot a list of objects # a reusable signal to replot a list of objects
# should be disconnected after use so it can be reused # should be disconnected after use so it can be reused
replot_signal = pyqtSignal(list) replot_signal = pyqtSignal(list)
# signal emitted when jumping # signal emitted when jumping
jump_signal = pyqtSignal(tuple) jump_signal = pyqtSignal(tuple)
# signal emitted when jumping # signal emitted when jumping
locate_signal = pyqtSignal(tuple, str) locate_signal = pyqtSignal(tuple, str)
proj_selection_changed = pyqtSignal(object, object) proj_selection_changed = pyqtSignal(object, object)
# used by the AppScript object to process a script
# used by the FlatCAMScript object to process a script
run_script = pyqtSignal(str) run_script = pyqtSignal(str)
# used when loading a project and parsing the project file
restore_project = pyqtSignal(object, str, bool, bool, bool, bool)
# used when loading a project and restoring objects
restore_project_objects_sig = pyqtSignal(object, str, bool, bool)
def __init__(self, qapp, user_defaults=True): def __init__(self, qapp, user_defaults=True):
""" """
@@ -1411,6 +1403,9 @@ class App(QtCore.QObject):
# signals for displaying messages in the Tcl Shell are now connected in the ToolShell class # signals for displaying messages in the Tcl Shell are now connected in the ToolShell class
# loading an project
self.restore_project.connect(self.f_handlers.restore_project_handler)
self.restore_project_objects_sig.connect(self.f_handlers.restore_project_objects)
# signal to be called when the app is quiting # signal to be called when the app is quiting
self.app_quit.connect(self.quit_application, type=Qt.ConnectionType.QueuedConnection) self.app_quit.connect(self.quit_application, type=Qt.ConnectionType.QueuedConnection)
self.message.connect( self.message.connect(
@@ -1494,7 +1489,6 @@ class App(QtCore.QObject):
# ########################################################################################################### # ###########################################################################################################
# ####################################### FILE ASSOCIATIONS SIGNALS ######################################### # ####################################### FILE ASSOCIATIONS SIGNALS #########################################
# ########################################################################################################### # ###########################################################################################################
self.ui.util_pref_form.fa_excellon_group.restore_btn.clicked.connect( self.ui.util_pref_form.fa_excellon_group.restore_btn.clicked.connect(
lambda: self.restore_extensions(ext_type='excellon')) lambda: self.restore_extensions(ext_type='excellon'))
self.ui.util_pref_form.fa_gcode_group.restore_btn.clicked.connect( self.ui.util_pref_form.fa_gcode_group.restore_btn.clicked.connect(
@@ -2119,12 +2113,13 @@ class App(QtCore.QObject):
self.ui.menu_plugins_shell.triggered.connect(self.ui.toggle_shell_ui) self.ui.menu_plugins_shell.triggered.connect(self.ui.toggle_shell_ui)
# third install all of them # third install all of them
t0 = time.time()
try: try:
self.install_tools(init_tcl=init_tcl) self.install_tools(init_tcl=init_tcl)
except AttributeError: except AttributeError:
pass pass
self.log.debug("Tools are initialized.") self.log.debug("%s: %s" % ("Tools are initialized in", str(time.time() - t0)))
# def parse_system_fonts(self): # def parse_system_fonts(self):
# self.worker_task.emit({'fcn': self.f_parse.get_fonts_by_types, # self.worker_task.emit({'fcn': self.f_parse.get_fonts_by_types,
@@ -10245,7 +10240,7 @@ class MenuFileHandlers(QtCore.QObject):
self.log.debug("on_file_new_project()") self.log.debug("on_file_new_project()")
t0 = time.time() t_start_proj = time.time()
# close any editor that might be open # close any editor that might be open
if self.app.call_source != 'app': if self.app.call_source != 'app':
@@ -10281,7 +10276,7 @@ class MenuFileHandlers(QtCore.QObject):
# delete any selection shape on canvas # delete any selection shape on canvas
self.app.delete_selection_shape() self.app.delete_selection_shape()
# delete all FlatCAM objects # delete all App objects
if keep_scripts is True: if keep_scripts is True:
for prj_obj in self.app.collection.get_list(): for prj_obj in self.app.collection.get_list():
if prj_obj.kind != 'script': if prj_obj.kind != 'script':
@@ -10289,6 +10284,9 @@ class MenuFileHandlers(QtCore.QObject):
else: else:
self.app.collection.delete_all() self.app.collection.delete_all()
self.log.debug('%s: %s %s.' %
("Deleted all the application objects", str(time.time() - t_start_proj), _("seconds")))
# add in Selected tab an initial text that describe the flow of work in FlatCAm # add in Selected tab an initial text that describe the flow of work in FlatCAm
self.app.setup_default_properties_tab() self.app.setup_default_properties_tab()
@@ -10305,6 +10303,7 @@ class MenuFileHandlers(QtCore.QObject):
if use_thread is True: if use_thread is True:
self.app.new_project_signal.emit() self.app.new_project_signal.emit()
else: else:
t0 = time.time()
# Clear pool # Clear pool
self.app.clear_pool() self.app.clear_pool()
@@ -10313,6 +10312,8 @@ class MenuFileHandlers(QtCore.QObject):
self.app.init_tools(init_tcl=True) self.app.init_tools(init_tcl=True)
else: else:
self.app.init_tools(init_tcl=False) self.app.init_tools(init_tcl=False)
self.log.debug(
'%s: %s %s.' % ("Initiated the MP pool and plugins in: ", str(time.time() - t0), _("seconds")))
# tcl needs to be reinitialized, otherwise old shell variables etc remains # tcl needs to be reinitialized, otherwise old shell variables etc remains
# self.app.shell.init_tcl() # self.app.shell.init_tcl()
@@ -10336,7 +10337,7 @@ class MenuFileHandlers(QtCore.QObject):
# take the focus of the Notebook on Project Tab. # take the focus of the Notebook on Project Tab.
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab) self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
self.log.debug('%s: %s %s.' % (_("Project created in"), str(time.time() - t0), _("seconds"))) self.log.debug('%s: %s %s.' % (_("Project created in"), str(time.time() - t_start_proj), _("seconds")))
self.app.ui.set_ui_title(name=_("New Project - Not saved")) self.app.ui.set_ui_title(name=_("New Project - Not saved"))
self.inform.emit('[success] %s...' % _("New Project created")) self.inform.emit('[success] %s...' % _("New Project created"))
@@ -10348,6 +10349,7 @@ class MenuFileHandlers(QtCore.QObject):
:return: :return:
:rtype: :rtype:
""" """
t0 = time.time()
# Clear pool # Clear pool
self.log.debug("New Project: cleaning multiprocessing pool.") self.log.debug("New Project: cleaning multiprocessing pool.")
@@ -10356,6 +10358,7 @@ class MenuFileHandlers(QtCore.QObject):
# Init FlatCAMTools # Init FlatCAMTools
self.log.debug("New Project: initializing the Tools and Tcl Shell.") self.log.debug("New Project: initializing the Tools and Tcl Shell.")
self.app.init_tools(init_tcl=True) self.app.init_tools(init_tcl=True)
self.log.debug('%s: %s %s.' % ("Initiated the MP pool and plugins in: ", str(time.time() - t0), _("seconds")))
def on_filenewscript(self, silent=False): def on_filenewscript(self, silent=False):
""" """
@@ -11854,7 +11857,7 @@ class MenuFileHandlers(QtCore.QObject):
self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open config file"), filename)) self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open config file"), filename))
return return
def open_project(self, filename, run_from_arg=None, plot=True, cli=None, from_tcl=False): def open_project(self, filename, run_from_arg=False, plot=True, cli=False, from_tcl=False):
""" """
Loads a project from the specified file. Loads a project from the specified file.
@@ -11872,8 +11875,11 @@ class MenuFileHandlers(QtCore.QObject):
:param from_tcl: True if run from Tcl Sehll :param from_tcl: True if run from Tcl Sehll
:return: None :return: None
""" """
self.log.debug("Opening project: " + filename)
if not os.path.exists(filename): project_filename = filename
self.log.debug("Opening project: " + project_filename)
if not os.path.exists(project_filename):
self.inform.emit('[ERROR_NOTCL] %s' % _("File no longer available.")) self.inform.emit('[ERROR_NOTCL] %s' % _("File no longer available."))
return return
@@ -11893,22 +11899,24 @@ class MenuFileHandlers(QtCore.QObject):
alignment=Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignLeft, alignment=Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignLeft,
color=QtGui.QColor("lightgray")) color=QtGui.QColor("lightgray"))
def parse_worker(prj_filename):
with self.app.proc_container.new('%s' % _("Parsing...")):
# Open and parse an uncompressed Project file # Open and parse an uncompressed Project file
try: try:
f = open(filename, 'r') f = open(prj_filename, 'r')
except IOError: except IOError:
if from_tcl: if from_tcl:
name = filename.split('/')[-1].split('\\')[-1] name = prj_filename.split('/')[-1].split('\\')[-1]
filename = self.options['global_tcl_path'] + '/' + name prj_filename = os.path.join(self.options['global_tcl_path'], name)
try: try:
f = open(filename, 'r') f = open(prj_filename, 'r')
except IOError: except IOError:
self.log.error("Failed to open project file: %s" % filename) self.log.error("Failed to open project file: %s" % prj_filename)
self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open project file"), filename)) self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open project file"), prj_filename))
return return
else: else:
self.log.error("Failed to open project file: %s" % filename) self.log.error("Failed to open project file: %s" % prj_filename)
self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open project file"), filename)) self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open project file"), prj_filename))
return return
try: try:
@@ -11916,17 +11924,17 @@ class MenuFileHandlers(QtCore.QObject):
except Exception as e: except Exception as e:
self.log.debug( self.log.debug(
"Failed to parse project file, trying to see if it loads as an LZMA archive: %s because %s" % "Failed to parse project file, trying to see if it loads as an LZMA archive: %s because %s" %
(filename, str(e))) (prj_filename, str(e)))
f.close() f.close()
# Open and parse a compressed Project file # Open and parse a compressed Project file
try: try:
with lzma.open(filename) as f: with lzma.open(prj_filename) as f:
file_content = f.read().decode('utf-8') file_content = f.read().decode('utf-8')
d = json.loads(file_content, object_hook=dict2obj) d = json.loads(file_content, object_hook=dict2obj)
except Exception as e: except Exception as e:
self.log.error("Failed to open project file: %s with error: %s" % (filename, str(e))) self.log.error("Failed to open project file: %s with error: %s" % (prj_filename, str(e)))
self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open project file"), filename)) self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open project file"), prj_filename))
return return
# Check for older projects # Check for older projects
@@ -11965,6 +11973,11 @@ class MenuFileHandlers(QtCore.QObject):
self.app.log.error("Legacy Project. Loading not supported.") self.app.log.error("Legacy Project. Loading not supported.")
return return
self.app.restore_project.emit(d, prj_filename, run_from_arg, from_tcl, cli, plot)
self.app.worker_task.emit({'fcn': parse_worker, 'params': [project_filename]})
def restore_project_handler(self, proj_dict, filename, run_from_arg, from_tcl, cli, plot):
# Clear the current project # Clear the current project
# # NOT THREAD SAFE # ## # # NOT THREAD SAFE # ##
if run_from_arg is True: if run_from_arg is True:
@@ -11996,13 +12009,13 @@ class MenuFileHandlers(QtCore.QObject):
# self.app.defaults.update(self.app.options) # self.app.defaults.update(self.app.options)
# self.app.preferencesUiManager.save_defaults() # self.app.preferencesUiManager.save_defaults()
# Project options # Project options
self.app.options.update(d['options']) self.app.options.update(proj_dict['options'])
if response == bt_no: if response == bt_no:
pass pass
else: else:
# Load by default new options when not using GUI # Load by default new options when not using GUI
# Project options # Project options
self.app.options.update(d['options']) self.app.options.update(proj_dict['options'])
self.app.project_filename = filename self.app.project_filename = filename
@@ -12011,10 +12024,15 @@ class MenuFileHandlers(QtCore.QObject):
if cli is None: if cli is None:
self.app.set_screen_units(self.app.options["units"]) self.app.set_screen_units(self.app.options["units"])
self.app.restore_project_objects_sig.emit(proj_dict, filename, cli, plot)
def restore_project_objects(self, proj_dict, filename, cli, plot):
def worker_task():
with self.app.proc_container.new('%s' % _("Loading...")):
# Re create objects # Re create objects
self.log.debug(" **************** Started PROEJCT loading... **************** ") self.log.debug(" **************** Started PROEJCT loading... **************** ")
for obj in proj_dict['objs']:
for obj in d['objs']:
try: try:
msg = "Recreating from opened project an %s object: %s" % \ msg = "Recreating from opened project an %s object: %s" % \
(obj['kind'].capitalize(), obj['obj_options']['name']) (obj['kind'].capitalize(), obj['obj_options']['name'])
@@ -12025,9 +12043,6 @@ class MenuFileHandlers(QtCore.QObject):
self.app.log.debug(msg) self.app.log.debug(msg)
def obj_init(new_obj, app_inst): def obj_init(new_obj, app_inst):
def worker_task():
with app_inst.proc_container.new('%s...' % _("Opening")):
try: try:
new_obj.from_dict(obj) new_obj.from_dict(obj)
except Exception as erro: except Exception as erro:
@@ -12100,9 +12115,6 @@ class MenuFileHandlers(QtCore.QObject):
# CNCJob.set_ui() # CNCJob.set_ui()
new_obj.is_loaded_from_project = True new_obj.is_loaded_from_project = True
worker_task()
# app_inst.worker_task.emit({'fcn': worker_task, 'params': []})
# for some reason, setting ui_title does not work when this method is called from Tcl Shell # for some reason, setting ui_title does not work when this method is called from Tcl Shell
# it's because the TclCommand is run in another thread (it inherits TclCommandSignaled) # it's because the TclCommand is run in another thread (it inherits TclCommandSignaled)
try: try:
@@ -12138,6 +12150,8 @@ class MenuFileHandlers(QtCore.QObject):
self.log.debug(" **************** Finished PROJECT loading... **************** ") self.log.debug(" **************** Finished PROJECT loading... **************** ")
self.app.worker_task.emit({'fcn': worker_task, 'params': []})
def save_project(self, filename, quit_action=False, silent=False, from_tcl=False): def save_project(self, filename, quit_action=False, silent=False, from_tcl=False):
""" """
Saves the current project to the specified file. Saves the current project to the specified file.