- working on Gerber Editor - added the key shortcuts: wip

- made saving of the project file non-blocking and also while saving the project file, if the user tries again to close the app while project file is being saved, the app will close only after saving is complete (the project file size is non zero)
This commit is contained in:
Marius Stanciu 2019-04-04 00:47:08 +03:00
parent f14246ae6a
commit 8fc916b746
4 changed files with 230 additions and 19 deletions

View File

@ -11,6 +11,7 @@ import getopt
import random
import simplejson as json
import lzma
import threading
from stat import S_IREAD, S_IRGRP, S_IROTH
import subprocess
@ -112,12 +113,13 @@ class App(QtCore.QObject):
manual_url = "http://flatcam.org/manual/index.html"
video_url = "https://www.youtube.com/playlist?list=PLVvP2SYRpx-AQgNlfoxw93tXUXon7G94_"
should_we_quit = True
# this variable will hold the project status
# if True it will mean that the project was modified and not saved
should_we_save = False
# flag is True if saving action has been triggered
save_in_progress = False
##################
## Signals ##
##################
@ -127,6 +129,8 @@ class App(QtCore.QObject):
# * App.info() --> Print on the status bar
inform = QtCore.pyqtSignal(str)
app_quit = QtCore.pyqtSignal()
# General purpose background task
worker_task = QtCore.pyqtSignal(dict)
@ -1224,6 +1228,7 @@ class App(QtCore.QObject):
### Signal handling ###
## Custom signals
self.inform.connect(self.info)
self.app_quit.connect(self.quit_application)
self.message.connect(self.message_dialog)
self.progress.connect(self.set_progress_bar)
self.object_created.connect(self.on_object_created)
@ -3164,6 +3169,11 @@ class App(QtCore.QObject):
self.inform.emit(_("Factory defaults saved."))
def final_save(self):
if self.save_in_progress:
self.inform.emit(_("Application is saving the project. Please wait ..."))
return
if self.should_we_save and self.collection.get_list():
msgbox = QtWidgets.QMessageBox()
msgbox.setText(_("There are files/objects modified in FlatCAM. "
@ -3178,11 +3188,15 @@ class App(QtCore.QObject):
response = msgbox.exec_()
if response == QtWidgets.QMessageBox.Yes:
self.on_file_saveprojectas(thread=False)
self.on_file_saveprojectas(thread=True, quit=True)
elif response == QtWidgets.QMessageBox.No:
QtWidgets.qApp.quit()
elif response == QtWidgets.QMessageBox.Cancel:
self.should_we_quit = False
return
else:
QtWidgets.qApp.quit()
def quit_application(self):
self.save_defaults()
log.debug("App.final_save() --> App Defaults saved.")
@ -6218,7 +6232,7 @@ class App(QtCore.QObject):
self.should_we_save = False
def on_file_saveprojectas(self, make_copy=False, thread=True):
def on_file_saveprojectas(self, make_copy=False, thread=True, quit=False):
"""
Callback for menu item File->Save Project As... Opens a file
chooser and saves the project to the given file via
@ -6253,9 +6267,9 @@ class App(QtCore.QObject):
if thread is True:
self.worker_task.emit({'fcn': self.save_project,
'params': [filename]})
'params': [filename, quit]})
else:
self.save_project(filename)
self.save_project(filename, quit)
# self.save_project(filename)
self.file_opened.emit("project", filename)
@ -7861,7 +7875,7 @@ The normal flow when working in FlatCAM is the following:</span></p>
for obj in objects:
obj.on_generatecnc_button_click()
def save_project(self, filename):
def save_project(self, filename, quit=False):
"""
Saves the current project to the specified file.
@ -7871,6 +7885,8 @@ The normal flow when working in FlatCAM is the following:</span></p>
"""
self.log.debug("save_project()")
self.save_in_progress = True
with self.proc_container.new(_("Saving FlatCAM Project")) as proc:
## Capture the latest changes
# Current object
@ -7927,6 +7943,27 @@ The normal flow when working in FlatCAM is the following:</span></p>
else:
self.inform.emit(_("[ERROR_NOTCL] Failed to save project file: %s. Retry to save it.") % filename)
if quit:
t = threading.Thread(target=lambda: self.check_project_file_size(1, filename=filename))
t.start()
# using Alfe's answer from here:
# https://stackoverflow.com/questions/474528/what-is-the-best-way-to-repeatedly-execute-a-function-every-x-seconds-in-python
def check_project_file_size(self, delay, filename):
next_time = time.time() + delay
while True:
time.sleep(max(0, next_time - time.time()))
try:
statinfo = os.stat(filename)
if statinfo:
self.app_quit.emit()
except Exception:
traceback.print_exc()
# in production code you might want to have this instead of course:
# logger.exception("Problem while executing repetitive task.")
# skip tasks if we are behind schedule:
next_time += (time.time() - next_time) // delay * delay + delay
def on_options_app2project(self):
"""
Callback for Options->Transfer Options->App=>Project. Copies options

View File

@ -14,7 +14,9 @@ CAD program, and create G-Code for Isolation routing.
- fixed plotting in Gerber Editor
- working on GUI in Gerber Editor
- added a Gcode end_command: default is M02
- modified the calling of the editor2object() slot function
- modified the calling of the editor2object() slot function to fix an issue with updating geometry imported from SVG file, after edut
- working on Gerber Editor - added the key shortcuts: wip
- made saving of the project file non-blocking and also while saving the project file, if the user tries again to close the app while project file is being saved, the app will close only after saving is complete (the project file size is non zero)
31.03.2019

View File

@ -1365,7 +1365,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
else:
self.edited_obj_name += "_edit"
self.app.worker_task.emit({'fcn': self.new_edited_excellon,
self.app.worker_task.emit({'fcn': self.new_edited_gerber,
'params': [self.edited_obj_name]})
if self.gerber_obj.slots:
@ -1403,7 +1403,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
obj.options = {}
return True
def new_edited_excellon(self, outname):
def new_edited_gerber(self, outname):
"""
Creates a new Excellon object for the edited Excellon. Thread-safe.

View File

@ -2383,6 +2383,177 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
# Show Shortcut list
if key == 'F3':
self.app.on_shortcut_list()
elif self.app.call_source == 'grb_editor':
if modifiers == QtCore.Qt.ControlModifier:
# save (update) the current geometry and return to the App
if key == QtCore.Qt.Key_S or key == 'S':
self.app.editor2object()
return
# toggle the measurement tool
if key == QtCore.Qt.Key_M or key == 'M':
self.app.measurement_tool.run()
return
elif modifiers == QtCore.Qt.ShiftModifier:
pass
elif modifiers == QtCore.Qt.AltModifier:
pass
elif modifiers == QtCore.Qt.NoModifier:
# Abort the current action
if key == QtCore.Qt.Key_Escape or key == 'Escape':
# self.on_tool_select("select")
self.app.inform.emit(_("[WARNING_NOTCL] Cancelled."))
self.app.grb_editor.delete_utility_geometry()
self.app.grb_editor.replot()
# self.select_btn.setChecked(True)
# self.on_tool_select('select')
self.app.grb_editor.select_tool('select')
return
# Delete selected object if delete key event comes out of canvas
if key == 'Delete':
self.app.grb_editor.launched_from_shortcuts = True
if self.app.grb_editor.selected:
self.app.grb_editor.delete_selected()
self.app.grb_editor.replot()
else:
self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to delete."))
return
# Delete aperture in apertures table if delete key event comes from the Selected Tab
if key == QtCore.Qt.Key_Delete:
self.app.grb_editor.launched_from_shortcuts = True
self.app.grb_editor.on_tool_delete()
return
if key == QtCore.Qt.Key_Minus or key == '-':
self.app.grb_editor.launched_from_shortcuts = True
self.app.plotcanvas.zoom(1 / self.app.defaults['zoom_ratio'],
[self.app.grb_editor.snap_x, self.app.grb_editor.snap_y])
return
if key == QtCore.Qt.Key_Equal or key == '=':
self.app.grb_editor.launched_from_shortcuts = True
self.app.plotcanvas.zoom(self.app.defaults['zoom_ratio'],
[self.app.grb_editor.snap_x, self.app.grb_editor.snap_y])
return
# toggle display of Notebook area
if key == QtCore.Qt.Key_QuoteLeft or key == '`':
self.app.grb_editor.launched_from_shortcuts = True
self.app.on_toggle_notebook()
return
# Switch to Project Tab
if key == QtCore.Qt.Key_1 or key == '1':
self.app.grb_editor.launched_from_shortcuts = True
self.app.on_select_tab('project')
return
# Switch to Selected Tab
if key == QtCore.Qt.Key_2 or key == '2':
self.app.grb_editor.launched_from_shortcuts = True
self.app.on_select_tab('selected')
return
# Switch to Tool Tab
if key == QtCore.Qt.Key_3 or key == '3':
self.app.grb_editor.launched_from_shortcuts = True
self.app.on_select_tab('tool')
return
# Copy
if key == QtCore.Qt.Key_C or key == 'C':
self.app.grb_editor.launched_from_shortcuts = True
if self.app.grb_editor.selected:
self.app.inform.emit(_("Click on target point."))
self.app.ui.copy_aperture_btn.setChecked(True)
self.app.grb_editor.on_tool_select('aperture_copy')
self.app.grb_editor.active_tool.set_origin(
(self.app.grb_editor.snap_x, self.app.grb_editor.snap_y))
else:
self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to copy."))
return
# Add Aperture Tool
if key == QtCore.Qt.Key_A or key == 'A':
self.app.grb_editor.launched_from_shortcuts = True
self.app.inform.emit(_("Click on target point."))
self.app.ui.add_aperture_btn.setChecked(True)
self.app.grb_editor.x = self.app.mouse[0]
self.app.grb_editor.y = self.app.mouse[1]
self.app.grb_editor.select_tool('aperture_add')
return
# Grid Snap
if key == QtCore.Qt.Key_G or key == 'G':
self.app.grb_editor.launched_from_shortcuts = True
# make sure that the cursor shape is enabled/disabled, too
if self.app.grb_editor.options['grid_snap'] is True:
self.app.app_cursor.enabled = False
else:
self.app.app_cursor.enabled = True
self.app.ui.grid_snap_btn.trigger()
return
# Jump to coords
if key == QtCore.Qt.Key_J or key == 'J':
self.app.on_jump_to()
# Corner Snap
if key == QtCore.Qt.Key_K or key == 'K':
self.app.grb_editor.launched_from_shortcuts = True
self.app.ui.corner_snap_btn.trigger()
return
# Move
if key == QtCore.Qt.Key_M or key == 'M':
self.app.grb_editor.launched_from_shortcuts = True
if self.app.grb_editor.selected:
self.app.inform.emit(_("Click on target point."))
self.app.ui.move_aperture_btn.setChecked(True)
self.app.exc_editor.on_tool_select('aperture_move')
self.app.grb_editor.active_tool.set_origin(
(self.app.grb_editor.snap_x, self.app.grb_editor.snap_y))
else:
self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to move."))
return
# Resize Tool
if key == QtCore.Qt.Key_R or key == 'R':
self.app.grb_editor.launched_from_shortcuts = True
self.app.grb_editor.select_tool('drill_resize')
return
# Add Track
if key == QtCore.Qt.Key_T or key == 'T':
self.app.grb_editor.launched_from_shortcuts = True
## Current application units in Upper Case
self.units = self.general_defaults_group.general_app_group.units_radio.get_value().upper()
return
# Zoom Fit
if key == QtCore.Qt.Key_V or key == 'V':
self.app.grb_editor.launched_from_shortcuts = True
self.app.on_zoom_fit(None)
return
# Propagate to tool
response = None
if self.app.grb_editor.active_tool is not None:
response = self.app.grb_editor.active_tool.on_key(key=key)
if response is not None:
self.app.inform.emit(response)
# Show Shortcut list
if key == QtCore.Qt.Key_F3 or key == 'F3':
self.app.on_shortcut_list()
return
elif self.app.call_source == 'exc_editor':
if modifiers == QtCore.Qt.ControlModifier:
# save (update) the current geometry and return to the App
@ -2640,16 +2811,17 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
event.ignore()
def closeEvent(self, event):
grect = self.geometry()
if self.app.save_in_progress:
self.app.inform.emit(_("[WARNING_NOTCL] Application is saving the project. Please wait ..."))
else:
grect = self.geometry()
# self.splitter.sizes()[0] is actually the size of the "notebook"
if not self.isMaximized():
self.geom_update.emit(grect.x(), grect.y(), grect.width(), grect.height(), self.splitter.sizes()[0])
# self.splitter.sizes()[0] is actually the size of the "notebook"
if not self.isMaximized():
self.geom_update.emit(grect.x(), grect.y(), grect.width(), grect.height(), self.splitter.sizes()[0])
self.final_save.emit()
if self.app.should_we_quit is False:
event.ignore()
self.final_save.emit()
event.ignore()
class GeneralPreferencesUI(QtWidgets.QWidget):