|
|
@@ -392,7 +392,7 @@ class FCPad(FCShapeTool):
|
|
|
|
|
|
def clean_up(self):
|
|
|
self.draw_app.selected = []
|
|
|
- self.draw_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
self.draw_app.plot_all()
|
|
|
try:
|
|
|
self.draw_app.app.jump_signal.disconnect()
|
|
|
@@ -419,7 +419,7 @@ class FCPadArray(FCShapeTool):
|
|
|
self.complete = True
|
|
|
self.dont_execute = True
|
|
|
self.draw_app.in_action = False
|
|
|
- self.draw_app.array_frame.hide()
|
|
|
+ self.draw_app.ui.array_frame.hide()
|
|
|
self.draw_app.select_tool('select')
|
|
|
return
|
|
|
|
|
|
@@ -451,7 +451,7 @@ class FCPadArray(FCShapeTool):
|
|
|
except KeyError:
|
|
|
pass
|
|
|
|
|
|
- self.draw_app.array_frame.show()
|
|
|
+ self.draw_app.ui.array_frame.show()
|
|
|
|
|
|
self.selected_size = None
|
|
|
self.pad_axis = 'X'
|
|
|
@@ -512,15 +512,15 @@ class FCPadArray(FCShapeTool):
|
|
|
self.draw_app.select_tool('select')
|
|
|
return
|
|
|
|
|
|
- self.pad_axis = self.draw_app.pad_axis_radio.get_value()
|
|
|
- self.pad_direction = self.draw_app.pad_direction_radio.get_value()
|
|
|
- self.pad_array = self.draw_app.array_type_combo.get_value()
|
|
|
+ self.pad_axis = self.draw_app.ui.pad_axis_radio.get_value()
|
|
|
+ self.pad_direction = self.draw_app.ui.pad_direction_radio.get_value()
|
|
|
+ self.pad_array = self.draw_app.ui.array_type_combo.get_value()
|
|
|
try:
|
|
|
- self.pad_array_size = int(self.draw_app.pad_array_size_entry.get_value())
|
|
|
+ self.pad_array_size = int(self.draw_app.ui.pad_array_size_entry.get_value())
|
|
|
try:
|
|
|
- self.pad_pitch = float(self.draw_app.pad_pitch_entry.get_value())
|
|
|
- self.pad_linear_angle = float(self.draw_app.linear_angle_spinner.get_value())
|
|
|
- self.pad_angle = float(self.draw_app.pad_angle_entry.get_value())
|
|
|
+ self.pad_pitch = float(self.draw_app.ui.pad_pitch_entry.get_value())
|
|
|
+ self.pad_linear_angle = float(self.draw_app.ui.linear_angle_spinner.get_value())
|
|
|
+ self.pad_angle = float(self.draw_app.ui.pad_angle_entry.get_value())
|
|
|
except TypeError:
|
|
|
self.draw_app.app.inform.emit('[ERROR_NOTCL] %s' %
|
|
|
_("The value is not Float. Check for comma instead of dot separator."))
|
|
|
@@ -741,12 +741,12 @@ class FCPadArray(FCShapeTool):
|
|
|
self.complete = True
|
|
|
self.draw_app.app.inform.emit('[success] %s' % _("Done."))
|
|
|
self.draw_app.in_action = False
|
|
|
- self.draw_app.array_frame.hide()
|
|
|
+ self.draw_app.ui.array_frame.hide()
|
|
|
self.draw_app.app.jump_signal.disconnect()
|
|
|
|
|
|
def clean_up(self):
|
|
|
self.draw_app.selected = []
|
|
|
- self.draw_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
self.draw_app.plot_all()
|
|
|
try:
|
|
|
self.draw_app.app.jump_signal.disconnect()
|
|
|
@@ -860,7 +860,7 @@ class FCPoligonize(FCShapeTool):
|
|
|
|
|
|
def clean_up(self):
|
|
|
self.draw_app.selected = []
|
|
|
- self.draw_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
self.draw_app.plot_all()
|
|
|
|
|
|
|
|
|
@@ -1172,7 +1172,7 @@ class FCRegion(FCShapeTool):
|
|
|
|
|
|
def clean_up(self):
|
|
|
self.draw_app.selected = []
|
|
|
- self.draw_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
self.draw_app.plot_all()
|
|
|
try:
|
|
|
self.draw_app.app.jump_signal.disconnect()
|
|
|
@@ -1445,7 +1445,7 @@ class FCTrack(FCShapeTool):
|
|
|
|
|
|
def clean_up(self):
|
|
|
self.draw_app.selected = []
|
|
|
- self.draw_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
self.draw_app.plot_all()
|
|
|
try:
|
|
|
self.draw_app.app.jump_signal.disconnect()
|
|
|
@@ -1558,7 +1558,7 @@ class FCDisc(FCShapeTool):
|
|
|
|
|
|
def clean_up(self):
|
|
|
self.draw_app.selected = []
|
|
|
- self.draw_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
self.draw_app.plot_all()
|
|
|
try:
|
|
|
self.draw_app.app.jump_signal.disconnect()
|
|
|
@@ -1613,10 +1613,11 @@ class FCSemiDisc(FCShapeTool):
|
|
|
if '0' in self.draw_app.storage_dict:
|
|
|
self.storage_obj = self.draw_app.storage_dict['0']['geometry']
|
|
|
else:
|
|
|
- self.draw_app.storage_dict['0'] = {}
|
|
|
- self.draw_app.storage_dict['0']['type'] = 'C'
|
|
|
- self.draw_app.storage_dict['0']['size'] = 0.0
|
|
|
- self.draw_app.storage_dict['0']['geometry'] = []
|
|
|
+ self.draw_app.storage_dict['0'] = {
|
|
|
+ 'type': 'C',
|
|
|
+ 'size': 0.0,
|
|
|
+ 'geometry': []
|
|
|
+ }
|
|
|
self.storage_obj = self.draw_app.storage_dict['0']['geometry']
|
|
|
|
|
|
self.steps_per_circ = self.draw_app.app.defaults["gerber_circle_steps"]
|
|
|
@@ -1846,7 +1847,7 @@ class FCSemiDisc(FCShapeTool):
|
|
|
|
|
|
def clean_up(self):
|
|
|
self.draw_app.selected = []
|
|
|
- self.draw_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
self.draw_app.plot_all()
|
|
|
try:
|
|
|
self.draw_app.app.jump_signal.disconnect()
|
|
|
@@ -1872,16 +1873,16 @@ class FCScale(FCShapeTool):
|
|
|
|
|
|
def activate_scale(self):
|
|
|
self.draw_app.hide_tool('all')
|
|
|
- self.draw_app.scale_tool_frame.show()
|
|
|
+ self.draw_app.ui.scale_tool_frame.show()
|
|
|
|
|
|
try:
|
|
|
- self.draw_app.scale_button.clicked.disconnect()
|
|
|
+ self.draw_app.ui.scale_button.clicked.disconnect()
|
|
|
except (TypeError, AttributeError):
|
|
|
pass
|
|
|
- self.draw_app.scale_button.clicked.connect(self.on_scale_click)
|
|
|
+ self.draw_app.ui.scale_button.clicked.connect(self.on_scale_click)
|
|
|
|
|
|
def deactivate_scale(self):
|
|
|
- self.draw_app.scale_button.clicked.disconnect()
|
|
|
+ self.draw_app.ui.scale_button.clicked.disconnect()
|
|
|
self.complete = True
|
|
|
self.draw_app.select_tool("select")
|
|
|
self.draw_app.hide_tool(self.name)
|
|
|
@@ -1892,7 +1893,7 @@ class FCScale(FCShapeTool):
|
|
|
|
|
|
def clean_up(self):
|
|
|
self.draw_app.selected = []
|
|
|
- self.draw_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
self.draw_app.plot_all()
|
|
|
|
|
|
|
|
|
@@ -1914,16 +1915,16 @@ class FCBuffer(FCShapeTool):
|
|
|
|
|
|
def activate_buffer(self):
|
|
|
self.draw_app.hide_tool('all')
|
|
|
- self.draw_app.buffer_tool_frame.show()
|
|
|
+ self.draw_app.ui.buffer_tool_frame.show()
|
|
|
|
|
|
try:
|
|
|
- self.draw_app.buffer_button.clicked.disconnect()
|
|
|
+ self.draw_app.ui.buffer_button.clicked.disconnect()
|
|
|
except (TypeError, AttributeError):
|
|
|
pass
|
|
|
- self.draw_app.buffer_button.clicked.connect(self.on_buffer_click)
|
|
|
+ self.draw_app.ui.buffer_button.clicked.connect(self.on_buffer_click)
|
|
|
|
|
|
def deactivate_buffer(self):
|
|
|
- self.draw_app.buffer_button.clicked.disconnect()
|
|
|
+ self.draw_app.ui.buffer_button.clicked.disconnect()
|
|
|
self.complete = True
|
|
|
self.draw_app.select_tool("select")
|
|
|
self.draw_app.hide_tool(self.name)
|
|
|
@@ -1934,7 +1935,7 @@ class FCBuffer(FCShapeTool):
|
|
|
|
|
|
def clean_up(self):
|
|
|
self.draw_app.selected = []
|
|
|
- self.draw_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
self.draw_app.plot_all()
|
|
|
|
|
|
|
|
|
@@ -1955,31 +1956,31 @@ class FCMarkArea(FCShapeTool):
|
|
|
self.activate_markarea()
|
|
|
|
|
|
def activate_markarea(self):
|
|
|
- self.draw_app.ma_tool_frame.show()
|
|
|
+ self.draw_app.ui.ma_tool_frame.show()
|
|
|
|
|
|
# clear previous marking
|
|
|
- self.draw_app.ma_annotation.clear(update=True)
|
|
|
+ self.draw_app.ui.ma_annotation.clear(update=True)
|
|
|
|
|
|
try:
|
|
|
- self.draw_app.ma_threshold_button.clicked.disconnect()
|
|
|
+ self.draw_app.ui.ma_threshold_button.clicked.disconnect()
|
|
|
except (TypeError, AttributeError):
|
|
|
pass
|
|
|
- self.draw_app.ma_threshold_button.clicked.connect(self.on_markarea_click)
|
|
|
+ self.draw_app.ui.ma_threshold_button.clicked.connect(self.on_markarea_click)
|
|
|
|
|
|
try:
|
|
|
- self.draw_app.ma_delete_button.clicked.disconnect()
|
|
|
+ self.draw_app.ui.ma_delete_button.clicked.disconnect()
|
|
|
except TypeError:
|
|
|
pass
|
|
|
- self.draw_app.ma_delete_button.clicked.connect(self.on_markarea_delete)
|
|
|
+ self.draw_app.ui.ma_delete_button.clicked.connect(self.on_markarea_delete)
|
|
|
|
|
|
try:
|
|
|
- self.draw_app.ma_clear_button.clicked.disconnect()
|
|
|
+ self.draw_app.ui.ma_clear_button.clicked.disconnect()
|
|
|
except TypeError:
|
|
|
pass
|
|
|
- self.draw_app.ma_clear_button.clicked.connect(self.on_markarea_clear)
|
|
|
+ self.draw_app.ui.ma_clear_button.clicked.connect(self.on_markarea_clear)
|
|
|
|
|
|
def deactivate_markarea(self):
|
|
|
- self.draw_app.ma_threshold_button.clicked.disconnect()
|
|
|
+ self.draw_app.ui.ma_threshold_button.clicked.disconnect()
|
|
|
self.complete = True
|
|
|
self.draw_app.select_tool("select")
|
|
|
self.draw_app.hide_tool(self.name)
|
|
|
@@ -1997,7 +1998,7 @@ class FCMarkArea(FCShapeTool):
|
|
|
|
|
|
def clean_up(self):
|
|
|
self.draw_app.selected = []
|
|
|
- self.draw_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
self.draw_app.plot_all()
|
|
|
|
|
|
|
|
|
@@ -2027,10 +2028,10 @@ class FCApertureMove(FCShapeTool):
|
|
|
self.current_storage = None
|
|
|
self.geometry = []
|
|
|
|
|
|
- for index in self.draw_app.apertures_table.selectedIndexes():
|
|
|
+ for index in self.draw_app.ui.apertures_table.selectedIndexes():
|
|
|
row = index.row()
|
|
|
# on column 1 in tool tables we hold the aperture codes, and we retrieve them as strings
|
|
|
- aperture_on_row = self.draw_app.apertures_table.item(row, 1).text()
|
|
|
+ aperture_on_row = self.draw_app.ui.apertures_table.item(row, 1).text()
|
|
|
self.selected_apertures.append(aperture_on_row)
|
|
|
|
|
|
# Switch notebook to Properties page
|
|
|
@@ -2143,7 +2144,7 @@ class FCApertureMove(FCShapeTool):
|
|
|
|
|
|
def clean_up(self):
|
|
|
self.draw_app.selected = []
|
|
|
- self.draw_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
self.draw_app.plot_all()
|
|
|
|
|
|
try:
|
|
|
@@ -2242,10 +2243,10 @@ class FCEraser(FCShapeTool):
|
|
|
self.current_storage = None
|
|
|
self.geometry = []
|
|
|
|
|
|
- for index in self.draw_app.apertures_table.selectedIndexes():
|
|
|
+ for index in self.draw_app.ui.apertures_table.selectedIndexes():
|
|
|
row = index.row()
|
|
|
# on column 1 in tool tables we hold the aperture codes, and we retrieve them as strings
|
|
|
- aperture_on_row = self.draw_app.apertures_table.item(row, 1).text()
|
|
|
+ aperture_on_row = self.draw_app.ui.apertures_table.item(row, 1).text()
|
|
|
self.selected_apertures.append(aperture_on_row)
|
|
|
|
|
|
# Switch notebook to Properties page
|
|
|
@@ -2260,7 +2261,7 @@ class FCEraser(FCShapeTool):
|
|
|
|
|
|
def click(self, point):
|
|
|
if len(self.draw_app.get_selected()) == 0:
|
|
|
- self.draw_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
sel_aperture = set()
|
|
|
|
|
|
for storage in self.draw_app.storage_dict:
|
|
|
@@ -2277,19 +2278,19 @@ class FCEraser(FCShapeTool):
|
|
|
|
|
|
# select the aperture in the Apertures Table that is associated with the selected shape
|
|
|
try:
|
|
|
- self.draw_app.apertures_table.cellPressed.disconnect()
|
|
|
+ self.draw_app.ui.apertures_table.cellPressed.disconnect()
|
|
|
except Exception as e:
|
|
|
log.debug("AppGerberEditor.FCEraser.click_release() --> %s" % str(e))
|
|
|
|
|
|
- self.draw_app.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
|
|
|
+ self.draw_app.ui.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
|
|
|
for aper in sel_aperture:
|
|
|
- for row in range(self.draw_app.apertures_table.rowCount()):
|
|
|
- if str(aper) == self.draw_app.apertures_table.item(row, 1).text():
|
|
|
- self.draw_app.apertures_table.selectRow(row)
|
|
|
+ for row in range(self.draw_app.ui.apertures_table.rowCount()):
|
|
|
+ if str(aper) == self.draw_app.ui.apertures_table.item(row, 1).text():
|
|
|
+ self.draw_app.ui.apertures_table.selectRow(row)
|
|
|
self.draw_app.last_aperture_selected = aper
|
|
|
- self.draw_app.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
|
|
+ self.draw_app.ui.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
|
|
|
|
|
- self.draw_app.apertures_table.cellPressed.connect(self.draw_app.on_row_selected)
|
|
|
+ self.draw_app.ui.apertures_table.cellPressed.connect(self.draw_app.on_row_selected)
|
|
|
|
|
|
if len(self.draw_app.get_selected()) == 0:
|
|
|
return "Nothing to ersase."
|
|
|
@@ -2334,7 +2335,7 @@ class FCEraser(FCShapeTool):
|
|
|
|
|
|
def clean_up(self):
|
|
|
self.draw_app.selected = []
|
|
|
- self.draw_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
self.draw_app.plot_all()
|
|
|
try:
|
|
|
self.draw_app.app.jump_signal.disconnect()
|
|
|
@@ -2372,33 +2373,33 @@ class FCEraser(FCShapeTool):
|
|
|
|
|
|
|
|
|
class FCApertureSelect(DrawTool):
|
|
|
- def __init__(self, grb_editor_app):
|
|
|
- DrawTool.__init__(self, grb_editor_app)
|
|
|
+ def __init__(self, draw_app):
|
|
|
+ DrawTool.__init__(self, draw_app)
|
|
|
self.name = 'select'
|
|
|
self.origin = None
|
|
|
|
|
|
- self.grb_editor_app = grb_editor_app
|
|
|
- self.storage = self.grb_editor_app.storage_dict
|
|
|
- # self.selected = self.grb_editor_app.selected
|
|
|
+ self.draw_app = draw_app
|
|
|
+ self.storage = self.draw_app.storage_dict
|
|
|
+ # self.selected = self.draw_app.selected
|
|
|
|
|
|
# here we store all shapes that were selected
|
|
|
self.sel_storage = []
|
|
|
|
|
|
# since FCApertureSelect tool is activated whenever a tool is exited I place here the reinitialization of the
|
|
|
# bending modes using in FCRegion and FCTrack
|
|
|
- self.grb_editor_app.bend_mode = 1
|
|
|
+ self.draw_app.bend_mode = 1
|
|
|
|
|
|
# here store the selected apertures
|
|
|
self.sel_aperture = []
|
|
|
|
|
|
try:
|
|
|
- self.grb_editor_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
except Exception as e:
|
|
|
log.error("FlatCAMGerbEditor.FCApertureSelect.__init__() --> %s" % str(e))
|
|
|
|
|
|
- self.grb_editor_app.hide_tool('all')
|
|
|
- self.grb_editor_app.hide_tool('select')
|
|
|
- self.grb_editor_app.array_frame.hide()
|
|
|
+ self.draw_app.hide_tool('all')
|
|
|
+ self.draw_app.hide_tool('select')
|
|
|
+ self.draw_app.ui.array_frame.hide()
|
|
|
|
|
|
try:
|
|
|
QtGui.QGuiApplication.restoreOverrideCursor()
|
|
|
@@ -2406,16 +2407,16 @@ class FCApertureSelect(DrawTool):
|
|
|
log.debug("AppGerberEditor.FCApertureSelect --> %s" % str(e))
|
|
|
|
|
|
try:
|
|
|
- self.grb_editor_app.selection_triggered.disconnect()
|
|
|
+ self.draw_app.selection_triggered.disconnect()
|
|
|
except (TypeError, AttributeError):
|
|
|
pass
|
|
|
- self.grb_editor_app.selection_triggered.connect(self.selection_worker)
|
|
|
+ self.draw_app.selection_triggered.connect(self.selection_worker)
|
|
|
|
|
|
try:
|
|
|
- self.grb_editor_app.plot_object.disconnect()
|
|
|
+ self.draw_app.plot_object.disconnect()
|
|
|
except (TypeError, AttributeError):
|
|
|
pass
|
|
|
- self.grb_editor_app.plot_object.connect(self.clean_up)
|
|
|
+ self.draw_app.plot_object.connect(self.clean_up)
|
|
|
|
|
|
def set_origin(self, origin):
|
|
|
self.origin = origin
|
|
|
@@ -2430,13 +2431,13 @@ class FCApertureSelect(DrawTool):
|
|
|
else:
|
|
|
mod_key = None
|
|
|
|
|
|
- if mod_key == self.grb_editor_app.app.defaults["global_mselect_key"]:
|
|
|
+ if mod_key == self.draw_app.app.defaults["global_mselect_key"]:
|
|
|
pass
|
|
|
else:
|
|
|
- self.grb_editor_app.selected = []
|
|
|
+ self.draw_app.selected = []
|
|
|
|
|
|
def click_release(self, point):
|
|
|
- self.grb_editor_app.apertures_table.clearSelection()
|
|
|
+ self.draw_app.ui.apertures_table.clearSelection()
|
|
|
key_modifier = QtWidgets.QApplication.keyboardModifiers()
|
|
|
|
|
|
if key_modifier == QtCore.Qt.ShiftModifier:
|
|
|
@@ -2446,11 +2447,11 @@ class FCApertureSelect(DrawTool):
|
|
|
else:
|
|
|
mod_key = None
|
|
|
|
|
|
- if mod_key != self.grb_editor_app.app.defaults["global_mselect_key"]:
|
|
|
- self.grb_editor_app.selected.clear()
|
|
|
+ if mod_key != self.draw_app.app.defaults["global_mselect_key"]:
|
|
|
+ self.draw_app.selected.clear()
|
|
|
self.sel_aperture.clear()
|
|
|
|
|
|
- self.grb_editor_app.selection_triggered.emit(point)
|
|
|
+ self.draw_app.selection_triggered.emit(point)
|
|
|
|
|
|
def selection_worker(self, point):
|
|
|
def job_thread(editor_obj):
|
|
|
@@ -2471,15 +2472,15 @@ class FCApertureSelect(DrawTool):
|
|
|
if brake_flag is True:
|
|
|
break
|
|
|
|
|
|
- # #############################################################################################################
|
|
|
+ # ######################################################################################################
|
|
|
# select the aperture in the Apertures Table that is associated with the selected shape
|
|
|
- # #############################################################################################################
|
|
|
+ # ######################################################################################################
|
|
|
self.sel_aperture.clear()
|
|
|
- editor_obj.apertures_table.clearSelection()
|
|
|
+ editor_obj.ui.apertures_table.clearSelection()
|
|
|
|
|
|
# disconnect signal when clicking in the table
|
|
|
try:
|
|
|
- editor_obj.apertures_table.cellPressed.disconnect()
|
|
|
+ editor_obj.ui.apertures_table.cellPressed.disconnect()
|
|
|
except Exception as e:
|
|
|
log.debug("AppGerberEditor.FCApertureSelect.click_release() --> %s" % str(e))
|
|
|
|
|
|
@@ -2495,21 +2496,21 @@ class FCApertureSelect(DrawTool):
|
|
|
|
|
|
# actual row selection is done here
|
|
|
for aper in self.sel_aperture:
|
|
|
- for row in range(editor_obj.apertures_table.rowCount()):
|
|
|
- if str(aper) == editor_obj.apertures_table.item(row, 1).text():
|
|
|
- if not editor_obj.apertures_table.item(row, 0).isSelected():
|
|
|
- editor_obj.apertures_table.selectRow(row)
|
|
|
+ for row in range(editor_obj.ui.apertures_table.rowCount()):
|
|
|
+ if str(aper) == editor_obj.ui.apertures_table.item(row, 1).text():
|
|
|
+ if not editor_obj.ui.apertures_table.item(row, 0).isSelected():
|
|
|
+ editor_obj.ui.apertures_table.selectRow(row)
|
|
|
editor_obj.last_aperture_selected = aper
|
|
|
|
|
|
# reconnect signal when clicking in the table
|
|
|
- editor_obj.apertures_table.cellPressed.connect(editor_obj.on_row_selected)
|
|
|
+ editor_obj.ui.apertures_table.cellPressed.connect(editor_obj.on_row_selected)
|
|
|
|
|
|
editor_obj.plot_object.emit(None)
|
|
|
|
|
|
- self.grb_editor_app.app.worker_task.emit({'fcn': job_thread, 'params': [self.grb_editor_app]})
|
|
|
+ self.draw_app.app.worker_task.emit({'fcn': job_thread, 'params': [self.draw_app]})
|
|
|
|
|
|
def clean_up(self):
|
|
|
- self.grb_editor_app.plot_all()
|
|
|
+ self.draw_app.plot_all()
|
|
|
|
|
|
|
|
|
class FCTransform(FCShapeTool):
|
|
|
@@ -2553,2847 +2554,2865 @@ class AppGerberEditor(QtCore.QObject):
|
|
|
# Current application units in Upper Case
|
|
|
self.units = self.app.defaults['units'].upper()
|
|
|
|
|
|
- self.grb_edit_widget = QtWidgets.QWidget()
|
|
|
-
|
|
|
- layout = QtWidgets.QVBoxLayout()
|
|
|
- self.grb_edit_widget.setLayout(layout)
|
|
|
+ self.ui = AppGerberEditorUI(self.app)
|
|
|
|
|
|
- # Page Title box (spacing between children)
|
|
|
- self.title_box = QtWidgets.QHBoxLayout()
|
|
|
- layout.addLayout(self.title_box)
|
|
|
+ # Toolbar events and properties
|
|
|
+ self.tools_gerber = {}
|
|
|
|
|
|
- # Page Title icon
|
|
|
- pixmap = QtGui.QPixmap(self.app.resource_location + '/flatcam_icon32.png')
|
|
|
- self.icon = QtWidgets.QLabel()
|
|
|
- self.icon.setPixmap(pixmap)
|
|
|
- self.title_box.addWidget(self.icon, stretch=0)
|
|
|
+ # # ## Data
|
|
|
+ self.active_tool = None
|
|
|
|
|
|
- # Title label
|
|
|
- self.title_label = QtWidgets.QLabel("<font size=5><b>%s</b></font>" % _('Gerber Editor'))
|
|
|
- self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
|
|
|
- self.title_box.addWidget(self.title_label, stretch=1)
|
|
|
+ self.storage_dict = {}
|
|
|
+ self.current_storage = []
|
|
|
|
|
|
- # Object name
|
|
|
- self.name_box = QtWidgets.QHBoxLayout()
|
|
|
- layout.addLayout(self.name_box)
|
|
|
- name_label = QtWidgets.QLabel(_("Name:"))
|
|
|
- self.name_box.addWidget(name_label)
|
|
|
- self.name_entry = FCEntry()
|
|
|
- self.name_box.addWidget(self.name_entry)
|
|
|
+ self.sorted_apcode = []
|
|
|
|
|
|
- # Box for custom widgets
|
|
|
- # This gets populated in offspring implementations.
|
|
|
- self.custom_box = QtWidgets.QVBoxLayout()
|
|
|
- layout.addLayout(self.custom_box)
|
|
|
+ self.new_apertures = {}
|
|
|
+ self.new_aperture_macros = {}
|
|
|
|
|
|
- # #########################
|
|
|
- # ### Gerber Apertures ####
|
|
|
- # #########################
|
|
|
- self.apertures_table_label = QtWidgets.QLabel('<b>%s:</b>' % _('Apertures'))
|
|
|
- self.apertures_table_label.setToolTip(
|
|
|
- _("Apertures Table for the Gerber Object.")
|
|
|
- )
|
|
|
- self.custom_box.addWidget(self.apertures_table_label)
|
|
|
+ # store here the plot promises, if empty the delayed plot will be activated
|
|
|
+ self.grb_plot_promises = []
|
|
|
|
|
|
- self.apertures_table = FCTable()
|
|
|
- # delegate = SpinBoxDelegate(units=self.units)
|
|
|
- # self.apertures_table.setItemDelegateForColumn(1, delegate)
|
|
|
+ # dictionary to store the tool_row and aperture codes in Tool_table
|
|
|
+ # it will be updated everytime self.build_ui() is called
|
|
|
+ self.oldapcode_newapcode = {}
|
|
|
|
|
|
- self.custom_box.addWidget(self.apertures_table)
|
|
|
+ self.tid2apcode = {}
|
|
|
|
|
|
- self.apertures_table.setColumnCount(5)
|
|
|
- self.apertures_table.setHorizontalHeaderLabels(['#', _('Code'), _('Type'), _('Size'), _('Dim')])
|
|
|
- self.apertures_table.setSortingEnabled(False)
|
|
|
- self.apertures_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
|
|
+ # this will store the value for the last selected tool, for use after clicking on canvas when the selection
|
|
|
+ # is cleared but as a side effect also the selected tool is cleared
|
|
|
+ self.last_aperture_selected = None
|
|
|
+ self.utility = []
|
|
|
|
|
|
- self.apertures_table.horizontalHeaderItem(0).setToolTip(
|
|
|
- _("Index"))
|
|
|
- self.apertures_table.horizontalHeaderItem(1).setToolTip(
|
|
|
- _("Aperture Code"))
|
|
|
- self.apertures_table.horizontalHeaderItem(2).setToolTip(
|
|
|
- _("Type of aperture: circular, rectangle, macros etc"))
|
|
|
- self.apertures_table.horizontalHeaderItem(4).setToolTip(
|
|
|
- _("Aperture Size:"))
|
|
|
- self.apertures_table.horizontalHeaderItem(4).setToolTip(
|
|
|
- _("Aperture Dimensions:\n"
|
|
|
- " - (width, height) for R, O type.\n"
|
|
|
- " - (dia, nVertices) for P type"))
|
|
|
+ # this will store the polygons marked by mark are to be perhaps deleted
|
|
|
+ self.geo_to_delete = []
|
|
|
|
|
|
- self.empty_label = QtWidgets.QLabel('')
|
|
|
- self.custom_box.addWidget(self.empty_label)
|
|
|
+ # this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
|
|
|
+ self.launched_from_shortcuts = False
|
|
|
|
|
|
- # add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Apertures widgets
|
|
|
- # this way I can hide/show the frame
|
|
|
- self.apertures_frame = QtWidgets.QFrame()
|
|
|
- self.apertures_frame.setContentsMargins(0, 0, 0, 0)
|
|
|
- self.custom_box.addWidget(self.apertures_frame)
|
|
|
- self.apertures_box = QtWidgets.QVBoxLayout()
|
|
|
- self.apertures_box.setContentsMargins(0, 0, 0, 0)
|
|
|
- self.apertures_frame.setLayout(self.apertures_box)
|
|
|
+ # this var will store the state of the toolbar before starting the editor
|
|
|
+ self.toolbar_old_state = False
|
|
|
|
|
|
- # # ## Add/Delete an new Aperture ## ##
|
|
|
+ # #############################################################################################################
|
|
|
+ # ######################### Init appGUI #######################################################################
|
|
|
+ # #############################################################################################################
|
|
|
+ self.ui.apdim_lbl.hide()
|
|
|
+ self.ui.apdim_entry.hide()
|
|
|
|
|
|
- grid1 = QtWidgets.QGridLayout()
|
|
|
- self.apertures_box.addLayout(grid1)
|
|
|
- grid1.setColumnStretch(0, 0)
|
|
|
- grid1.setColumnStretch(1, 1)
|
|
|
+ self.gerber_obj = None
|
|
|
+ self.gerber_obj_options = {}
|
|
|
|
|
|
- apcode_lbl = QtWidgets.QLabel('%s:' % _('Aperture Code'))
|
|
|
- apcode_lbl.setToolTip(_("Code for the new aperture"))
|
|
|
- grid1.addWidget(apcode_lbl, 1, 0)
|
|
|
+ # VisPy Visuals
|
|
|
+ if self.app.is_legacy is False:
|
|
|
+ self.shapes = self.canvas.new_shape_collection(layers=1)
|
|
|
+ self.tool_shape = self.canvas.new_shape_collection(layers=1)
|
|
|
+ self.ma_annotation = self.canvas.new_text_group()
|
|
|
+ else:
|
|
|
+ from appGUI.PlotCanvasLegacy import ShapeCollectionLegacy
|
|
|
+ self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_grb_editor')
|
|
|
+ self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_grb_editor')
|
|
|
+ self.ma_annotation = ShapeCollectionLegacy(
|
|
|
+ obj=self,
|
|
|
+ app=self.app,
|
|
|
+ name='ma_anno_grb_editor',
|
|
|
+ annotation_job=True)
|
|
|
|
|
|
- self.apcode_entry = FCSpinner()
|
|
|
- self.apcode_entry.set_range(0, 999)
|
|
|
- self.apcode_entry.setWrapping(True)
|
|
|
+ # Event signals disconnect id holders
|
|
|
+ self.mp = None
|
|
|
+ self.mm = None
|
|
|
+ self.mr = None
|
|
|
|
|
|
- grid1.addWidget(self.apcode_entry, 1, 1)
|
|
|
+ # Remove from scene
|
|
|
+ self.shapes.enabled = False
|
|
|
+ self.tool_shape.enabled = False
|
|
|
|
|
|
- apsize_lbl = QtWidgets.QLabel('%s' % _('Aperture Size:'))
|
|
|
- apsize_lbl.setToolTip(
|
|
|
- _("Size for the new aperture.\n"
|
|
|
- "If aperture type is 'R' or 'O' then\n"
|
|
|
- "this value is automatically\n"
|
|
|
- "calculated as:\n"
|
|
|
- "sqrt(width**2 + height**2)")
|
|
|
- )
|
|
|
- grid1.addWidget(apsize_lbl, 2, 0)
|
|
|
+ # List of selected geometric elements.
|
|
|
+ self.selected = []
|
|
|
|
|
|
- self.apsize_entry = FCDoubleSpinner()
|
|
|
- self.apsize_entry.set_precision(self.decimals)
|
|
|
- self.apsize_entry.set_range(0.0, 9999)
|
|
|
+ self.key = None # Currently pressed key
|
|
|
+ self.modifiers = None
|
|
|
+ self.x = None # Current mouse cursor pos
|
|
|
+ self.y = None
|
|
|
+ # Current snapped mouse pos
|
|
|
+ self.snap_x = None
|
|
|
+ self.snap_y = None
|
|
|
+ self.pos = None
|
|
|
|
|
|
- grid1.addWidget(self.apsize_entry, 2, 1)
|
|
|
+ # used in FCRegion and FCTrack. Will store the bending mode
|
|
|
+ self.bend_mode = 1
|
|
|
|
|
|
- aptype_lbl = QtWidgets.QLabel('%s:' % _('Aperture Type'))
|
|
|
- aptype_lbl.setToolTip(
|
|
|
- _("Select the type of new aperture. Can be:\n"
|
|
|
- "C = circular\n"
|
|
|
- "R = rectangular\n"
|
|
|
- "O = oblong")
|
|
|
- )
|
|
|
- grid1.addWidget(aptype_lbl, 3, 0)
|
|
|
+ # signal that there is an action active like polygon or path
|
|
|
+ self.in_action = False
|
|
|
+ # this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
|
|
|
+ self.launched_from_shortcuts = False
|
|
|
|
|
|
- self.aptype_cb = FCComboBox()
|
|
|
- self.aptype_cb.addItems(['C', 'R', 'O'])
|
|
|
- grid1.addWidget(self.aptype_cb, 3, 1)
|
|
|
+ def_tol_val = float(self.app.defaults["global_tolerance"])
|
|
|
+ self.tolerance = def_tol_val if self.units == 'MM'else def_tol_val / 20
|
|
|
|
|
|
- self.apdim_lbl = QtWidgets.QLabel('%s:' % _('Aperture Dim'))
|
|
|
- self.apdim_lbl.setToolTip(
|
|
|
- _("Dimensions for the new aperture.\n"
|
|
|
- "Active only for rectangular apertures (type R).\n"
|
|
|
- "The format is (width, height)")
|
|
|
- )
|
|
|
- grid1.addWidget(self.apdim_lbl, 4, 0)
|
|
|
+ # options of this widget (AppGerberEditor class is a widget)
|
|
|
+ self.options = {
|
|
|
+ "global_gridx": 0.1,
|
|
|
+ "global_gridy": 0.1,
|
|
|
+ "snap_max": 0.05,
|
|
|
+ "grid_snap": True,
|
|
|
+ "corner_snap": False,
|
|
|
+ "grid_gap_link": True
|
|
|
+ }
|
|
|
+ # fill it with the application options (application preferences)
|
|
|
+ self.options.update(self.app.options)
|
|
|
|
|
|
- self.apdim_entry = EvalEntry2()
|
|
|
- grid1.addWidget(self.apdim_entry, 4, 1)
|
|
|
+ for option in self.options:
|
|
|
+ if option in self.app.options:
|
|
|
+ self.options[option] = self.app.options[option]
|
|
|
|
|
|
- apadd_del_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Add/Delete Aperture'))
|
|
|
- apadd_del_lbl.setToolTip(
|
|
|
- _("Add/Delete an aperture in the aperture table")
|
|
|
- )
|
|
|
- self.apertures_box.addWidget(apadd_del_lbl)
|
|
|
+ # flag to show if the object was modified
|
|
|
+ self.is_modified = False
|
|
|
+ self.edited_obj_name = ""
|
|
|
+ self.tool_row = 0
|
|
|
|
|
|
- hlay_ad = QtWidgets.QHBoxLayout()
|
|
|
- self.apertures_box.addLayout(hlay_ad)
|
|
|
+ # Multiprocessing pool
|
|
|
+ self.pool = self.app.pool
|
|
|
|
|
|
- self.addaperture_btn = QtWidgets.QPushButton(_('Add'))
|
|
|
- self.addaperture_btn.setToolTip(
|
|
|
- _("Add a new aperture to the aperture list.")
|
|
|
- )
|
|
|
+ # Multiprocessing results
|
|
|
+ self.results = []
|
|
|
|
|
|
- self.delaperture_btn = QtWidgets.QPushButton(_('Delete'))
|
|
|
- self.delaperture_btn.setToolTip(
|
|
|
- _("Delete a aperture in the aperture list")
|
|
|
- )
|
|
|
- hlay_ad.addWidget(self.addaperture_btn)
|
|
|
- hlay_ad.addWidget(self.delaperture_btn)
|
|
|
+ # A QTimer
|
|
|
+ self.plot_thread = None
|
|
|
|
|
|
- # ###################
|
|
|
- # ### BUFFER TOOL ###
|
|
|
- # ###################
|
|
|
- self.buffer_tool_frame = QtWidgets.QFrame()
|
|
|
- self.buffer_tool_frame.setContentsMargins(0, 0, 0, 0)
|
|
|
- self.custom_box.addWidget(self.buffer_tool_frame)
|
|
|
- self.buffer_tools_box = QtWidgets.QVBoxLayout()
|
|
|
- self.buffer_tools_box.setContentsMargins(0, 0, 0, 0)
|
|
|
- self.buffer_tool_frame.setLayout(self.buffer_tools_box)
|
|
|
- self.buffer_tool_frame.hide()
|
|
|
+ # a QThread for the edit process
|
|
|
+ self.thread = QtCore.QThread()
|
|
|
|
|
|
- # Title
|
|
|
- buf_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Buffer Aperture'))
|
|
|
- buf_title_lbl.setToolTip(
|
|
|
- _("Buffer a aperture in the aperture list")
|
|
|
- )
|
|
|
- self.buffer_tools_box.addWidget(buf_title_lbl)
|
|
|
+ # def entry2option(option, entry):
|
|
|
+ # self.options[option] = float(entry.text())
|
|
|
|
|
|
- # Form Layout
|
|
|
- buf_form_layout = QtWidgets.QFormLayout()
|
|
|
- self.buffer_tools_box.addLayout(buf_form_layout)
|
|
|
+ self.transform_tool = TransformEditorTool(self.app, self)
|
|
|
|
|
|
- # Buffer distance
|
|
|
- self.buffer_distance_entry = FCDoubleSpinner()
|
|
|
- self.buffer_distance_entry.set_precision(self.decimals)
|
|
|
- self.buffer_distance_entry.set_range(-10000.0000, 10000.0000)
|
|
|
-
|
|
|
- buf_form_layout.addRow('%s:' % _("Buffer distance"), self.buffer_distance_entry)
|
|
|
- self.buffer_corner_lbl = QtWidgets.QLabel('%s:' % _("Buffer corner"))
|
|
|
- self.buffer_corner_lbl.setToolTip(
|
|
|
- _("There are 3 types of corners:\n"
|
|
|
- " - 'Round': the corner is rounded.\n"
|
|
|
- " - 'Square': the corner is met in a sharp angle.\n"
|
|
|
- " - 'Beveled': the corner is a line that directly connects the features meeting in the corner")
|
|
|
- )
|
|
|
- self.buffer_corner_cb = FCComboBox()
|
|
|
- self.buffer_corner_cb.addItem(_("Round"))
|
|
|
- self.buffer_corner_cb.addItem(_("Square"))
|
|
|
- self.buffer_corner_cb.addItem(_("Beveled"))
|
|
|
- buf_form_layout.addRow(self.buffer_corner_lbl, self.buffer_corner_cb)
|
|
|
+ # #############################################################################################################
|
|
|
+ # ######################### Gerber Editor Signals #############################################################
|
|
|
+ # #############################################################################################################
|
|
|
+ self.app.pool_recreated.connect(self.pool_recreated)
|
|
|
+ self.mp_finished.connect(self.on_multiprocessing_finished)
|
|
|
|
|
|
- # Buttons
|
|
|
- hlay_buf = QtWidgets.QHBoxLayout()
|
|
|
- self.buffer_tools_box.addLayout(hlay_buf)
|
|
|
+ # connect the toolbar signals
|
|
|
+ self.connect_grb_toolbar_signals()
|
|
|
|
|
|
- self.buffer_button = QtWidgets.QPushButton(_("Buffer"))
|
|
|
- hlay_buf.addWidget(self.buffer_button)
|
|
|
+ self.app.ui.grb_add_pad_menuitem.triggered.connect(self.on_pad_add)
|
|
|
+ self.app.ui.grb_add_pad_array_menuitem.triggered.connect(self.on_pad_add_array)
|
|
|
|
|
|
- # ##################
|
|
|
- # ### SCALE TOOL ###
|
|
|
- # ##################
|
|
|
- self.scale_tool_frame = QtWidgets.QFrame()
|
|
|
- self.scale_tool_frame.setContentsMargins(0, 0, 0, 0)
|
|
|
- self.custom_box.addWidget(self.scale_tool_frame)
|
|
|
- self.scale_tools_box = QtWidgets.QVBoxLayout()
|
|
|
- self.scale_tools_box.setContentsMargins(0, 0, 0, 0)
|
|
|
- self.scale_tool_frame.setLayout(self.scale_tools_box)
|
|
|
- self.scale_tool_frame.hide()
|
|
|
+ self.app.ui.grb_add_track_menuitem.triggered.connect(self.on_track_add)
|
|
|
+ self.app.ui.grb_add_region_menuitem.triggered.connect(self.on_region_add)
|
|
|
|
|
|
- # Title
|
|
|
- scale_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Scale Aperture'))
|
|
|
- scale_title_lbl.setToolTip(
|
|
|
- _("Scale a aperture in the aperture list")
|
|
|
- )
|
|
|
- self.scale_tools_box.addWidget(scale_title_lbl)
|
|
|
+ self.app.ui.grb_convert_poly_menuitem.triggered.connect(self.on_poligonize)
|
|
|
+ self.app.ui.grb_add_semidisc_menuitem.triggered.connect(self.on_add_semidisc)
|
|
|
+ self.app.ui.grb_add_disc_menuitem.triggered.connect(self.on_disc_add)
|
|
|
+ self.app.ui.grb_add_buffer_menuitem.triggered.connect(self.on_buffer)
|
|
|
+ self.app.ui.grb_add_scale_menuitem.triggered.connect(self.on_scale)
|
|
|
+ self.app.ui.grb_add_eraser_menuitem.triggered.connect(self.on_eraser)
|
|
|
+ self.app.ui.grb_add_markarea_menuitem.triggered.connect(self.on_markarea)
|
|
|
|
|
|
- # Form Layout
|
|
|
- scale_form_layout = QtWidgets.QFormLayout()
|
|
|
- self.scale_tools_box.addLayout(scale_form_layout)
|
|
|
+ self.app.ui.grb_transform_menuitem.triggered.connect(self.transform_tool.run)
|
|
|
|
|
|
- self.scale_factor_lbl = QtWidgets.QLabel('%s:' % _("Scale factor"))
|
|
|
- self.scale_factor_lbl.setToolTip(
|
|
|
- _("The factor by which to scale the selected aperture.\n"
|
|
|
- "Values can be between 0.0000 and 999.9999")
|
|
|
- )
|
|
|
- self.scale_factor_entry = FCDoubleSpinner()
|
|
|
- self.scale_factor_entry.set_precision(self.decimals)
|
|
|
- self.scale_factor_entry.set_range(0.0000, 10000.0000)
|
|
|
+ self.app.ui.grb_copy_menuitem.triggered.connect(self.on_copy_button)
|
|
|
+ self.app.ui.grb_delete_menuitem.triggered.connect(self.on_delete_btn)
|
|
|
|
|
|
- scale_form_layout.addRow(self.scale_factor_lbl, self.scale_factor_entry)
|
|
|
+ self.app.ui.grb_move_menuitem.triggered.connect(self.on_move_button)
|
|
|
|
|
|
- # Buttons
|
|
|
- hlay_scale = QtWidgets.QHBoxLayout()
|
|
|
- self.scale_tools_box.addLayout(hlay_scale)
|
|
|
+ self.ui.buffer_button.clicked.connect(self.on_buffer)
|
|
|
+ self.ui.scale_button.clicked.connect(self.on_scale)
|
|
|
|
|
|
- self.scale_button = QtWidgets.QPushButton(_("Scale"))
|
|
|
- hlay_scale.addWidget(self.scale_button)
|
|
|
+ self.app.ui.aperture_delete_btn.triggered.connect(self.on_delete_btn)
|
|
|
+ self.ui.name_entry.returnPressed.connect(self.on_name_activate)
|
|
|
|
|
|
- # ######################
|
|
|
- # ### Mark Area TOOL ###
|
|
|
- # ######################
|
|
|
- self.ma_tool_frame = QtWidgets.QFrame()
|
|
|
- self.ma_tool_frame.setContentsMargins(0, 0, 0, 0)
|
|
|
- self.custom_box.addWidget(self.ma_tool_frame)
|
|
|
- self.ma_tools_box = QtWidgets.QVBoxLayout()
|
|
|
- self.ma_tools_box.setContentsMargins(0, 0, 0, 0)
|
|
|
- self.ma_tool_frame.setLayout(self.ma_tools_box)
|
|
|
- self.ma_tool_frame.hide()
|
|
|
+ self.ui.aptype_cb.currentIndexChanged[str].connect(self.on_aptype_changed)
|
|
|
|
|
|
- # Title
|
|
|
- ma_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Mark polygons'))
|
|
|
- ma_title_lbl.setToolTip(
|
|
|
- _("Mark the polygon areas.")
|
|
|
- )
|
|
|
- self.ma_tools_box.addWidget(ma_title_lbl)
|
|
|
+ self.ui.addaperture_btn.clicked.connect(self.on_aperture_add)
|
|
|
+ self.ui.apsize_entry.returnPressed.connect(self.on_aperture_add)
|
|
|
+ self.ui.apdim_entry.returnPressed.connect(self.on_aperture_add)
|
|
|
|
|
|
- # Form Layout
|
|
|
- ma_form_layout = QtWidgets.QFormLayout()
|
|
|
- self.ma_tools_box.addLayout(ma_form_layout)
|
|
|
+ self.ui.delaperture_btn.clicked.connect(self.on_aperture_delete)
|
|
|
+ self.ui.apertures_table.cellPressed.connect(self.on_row_selected)
|
|
|
|
|
|
- self.ma_upper_threshold_lbl = QtWidgets.QLabel('%s:' % _("Area UPPER threshold"))
|
|
|
- self.ma_upper_threshold_lbl.setToolTip(
|
|
|
- _("The threshold value, all areas less than this are marked.\n"
|
|
|
- "Can have a value between 0.0000 and 10000.0000")
|
|
|
- )
|
|
|
- self.ma_upper_threshold_entry = FCDoubleSpinner()
|
|
|
- self.ma_upper_threshold_entry.set_precision(self.decimals)
|
|
|
- self.ma_upper_threshold_entry.set_range(0, 10000)
|
|
|
+ self.ui.array_type_combo.currentIndexChanged.connect(self.on_array_type_combo)
|
|
|
+ self.ui.pad_axis_radio.activated_custom.connect(self.on_linear_angle_radio)
|
|
|
|
|
|
- self.ma_lower_threshold_lbl = QtWidgets.QLabel('%s:' % _("Area LOWER threshold"))
|
|
|
- self.ma_lower_threshold_lbl.setToolTip(
|
|
|
- _("The threshold value, all areas more than this are marked.\n"
|
|
|
- "Can have a value between 0.0000 and 10000.0000")
|
|
|
- )
|
|
|
- self.ma_lower_threshold_entry = FCDoubleSpinner()
|
|
|
- self.ma_lower_threshold_entry.set_precision(self.decimals)
|
|
|
- self.ma_lower_threshold_entry.set_range(0, 10000)
|
|
|
+ self.ui.exit_editor_button.clicked.connect(lambda: self.app.editor2object())
|
|
|
|
|
|
- ma_form_layout.addRow(self.ma_lower_threshold_lbl, self.ma_lower_threshold_entry)
|
|
|
- ma_form_layout.addRow(self.ma_upper_threshold_lbl, self.ma_upper_threshold_entry)
|
|
|
+ self.conversion_factor = 1
|
|
|
|
|
|
- # Buttons
|
|
|
- hlay_ma = QtWidgets.QHBoxLayout()
|
|
|
- self.ma_tools_box.addLayout(hlay_ma)
|
|
|
+ self.apertures_row = 0
|
|
|
|
|
|
- self.ma_threshold_button = QtWidgets.QPushButton(_("Mark"))
|
|
|
- self.ma_threshold_button.setToolTip(
|
|
|
- _("Mark the polygons that fit within limits.")
|
|
|
- )
|
|
|
- hlay_ma.addWidget(self.ma_threshold_button)
|
|
|
+ self.complete = True
|
|
|
|
|
|
- self.ma_delete_button = QtWidgets.QPushButton(_("Delete"))
|
|
|
- self.ma_delete_button.setToolTip(
|
|
|
- _("Delete all the marked polygons.")
|
|
|
- )
|
|
|
- hlay_ma.addWidget(self.ma_delete_button)
|
|
|
+ self.set_ui()
|
|
|
+ log.debug("Initialization of the Gerber Editor is finished ...")
|
|
|
|
|
|
- self.ma_clear_button = QtWidgets.QPushButton(_("Clear"))
|
|
|
- self.ma_clear_button.setToolTip(
|
|
|
- _("Clear all the markings.")
|
|
|
- )
|
|
|
- hlay_ma.addWidget(self.ma_clear_button)
|
|
|
+ def make_callback(self, the_tool):
|
|
|
+ def f():
|
|
|
+ self.on_tool_select(the_tool)
|
|
|
|
|
|
- # ######################
|
|
|
- # ### Add Pad Array ####
|
|
|
- # ######################
|
|
|
- # add a frame and inside add a vertical box layout. Inside this vbox layout I add
|
|
|
- # all the add Pad array widgets
|
|
|
- # this way I can hide/show the frame
|
|
|
- self.array_frame = QtWidgets.QFrame()
|
|
|
- self.array_frame.setContentsMargins(0, 0, 0, 0)
|
|
|
- self.custom_box.addWidget(self.array_frame)
|
|
|
- self.array_box = QtWidgets.QVBoxLayout()
|
|
|
- self.array_box.setContentsMargins(0, 0, 0, 0)
|
|
|
- self.array_frame.setLayout(self.array_box)
|
|
|
+ return f
|
|
|
|
|
|
- self.emptyarray_label = QtWidgets.QLabel('')
|
|
|
- self.array_box.addWidget(self.emptyarray_label)
|
|
|
+ def connect_grb_toolbar_signals(self):
|
|
|
+ self.tools_gerber.update({
|
|
|
+ "select": {"button": self.app.ui.grb_select_btn, "constructor": FCApertureSelect},
|
|
|
+ "pad": {"button": self.app.ui.grb_add_pad_btn, "constructor": FCPad},
|
|
|
+ "array": {"button": self.app.ui.add_pad_ar_btn, "constructor": FCPadArray},
|
|
|
+ "track": {"button": self.app.ui.grb_add_track_btn, "constructor": FCTrack},
|
|
|
+ "region": {"button": self.app.ui.grb_add_region_btn, "constructor": FCRegion},
|
|
|
+ "poligonize": {"button": self.app.ui.grb_convert_poly_btn, "constructor": FCPoligonize},
|
|
|
+ "semidisc": {"button": self.app.ui.grb_add_semidisc_btn, "constructor": FCSemiDisc},
|
|
|
+ "disc": {"button": self.app.ui.grb_add_disc_btn, "constructor": FCDisc},
|
|
|
+ "buffer": {"button": self.app.ui.aperture_buffer_btn, "constructor": FCBuffer},
|
|
|
+ "scale": {"button": self.app.ui.aperture_scale_btn, "constructor": FCScale},
|
|
|
+ "markarea": {"button": self.app.ui.aperture_markarea_btn, "constructor": FCMarkArea},
|
|
|
+ "eraser": {"button": self.app.ui.aperture_eraser_btn, "constructor": FCEraser},
|
|
|
+ "copy": {"button": self.app.ui.aperture_copy_btn, "constructor": FCApertureCopy},
|
|
|
+ "transform": {"button": self.app.ui.grb_transform_btn, "constructor": FCTransform},
|
|
|
+ "move": {"button": self.app.ui.aperture_move_btn, "constructor": FCApertureMove},
|
|
|
+ })
|
|
|
|
|
|
- self.padarray_label = QtWidgets.QLabel('<b>%s</b>' % _("Add Pad Array"))
|
|
|
- self.padarray_label.setToolTip(
|
|
|
- _("Add an array of pads (linear or circular array)")
|
|
|
- )
|
|
|
- self.array_box.addWidget(self.padarray_label)
|
|
|
+ for tool in self.tools_gerber:
|
|
|
+ self.tools_gerber[tool]["button"].triggered.connect(self.make_callback(tool)) # Events
|
|
|
+ self.tools_gerber[tool]["button"].setCheckable(True)
|
|
|
|
|
|
- self.array_type_combo = FCComboBox()
|
|
|
- self.array_type_combo.setToolTip(
|
|
|
- _("Select the type of pads array to create.\n"
|
|
|
- "It can be Linear X(Y) or Circular")
|
|
|
- )
|
|
|
- self.array_type_combo.addItem(_("Linear"))
|
|
|
- self.array_type_combo.addItem(_("Circular"))
|
|
|
+ def pool_recreated(self, pool):
|
|
|
+ self.shapes.pool = pool
|
|
|
+ self.tool_shape.pool = pool
|
|
|
+ self.pool = pool
|
|
|
|
|
|
- self.array_box.addWidget(self.array_type_combo)
|
|
|
+ def set_ui(self):
|
|
|
+ # updated units
|
|
|
+ self.units = self.app.defaults['units'].upper()
|
|
|
+ self.decimals = self.app.decimals
|
|
|
|
|
|
- self.array_form = QtWidgets.QFormLayout()
|
|
|
- self.array_box.addLayout(self.array_form)
|
|
|
+ self.oldapcode_newapcode.clear()
|
|
|
+ self.tid2apcode.clear()
|
|
|
|
|
|
- self.pad_array_size_label = QtWidgets.QLabel('%s:' % _('Nr of pads'))
|
|
|
- self.pad_array_size_label.setToolTip(
|
|
|
- _("Specify how many pads to be in the array.")
|
|
|
- )
|
|
|
- self.pad_array_size_label.setMinimumWidth(100)
|
|
|
+ # update the oldapcode_newapcode dict to make sure we have an updated state of the tool_table
|
|
|
+ for key in self.storage_dict:
|
|
|
+ self.oldapcode_newapcode[key] = key
|
|
|
|
|
|
- self.pad_array_size_entry = FCSpinner()
|
|
|
- self.pad_array_size_entry.set_range(1, 9999)
|
|
|
+ sort_temp = []
|
|
|
+ for aperture in self.oldapcode_newapcode:
|
|
|
+ sort_temp.append(int(aperture))
|
|
|
+ self.sorted_apcode = sorted(sort_temp)
|
|
|
|
|
|
- self.array_form.addRow(self.pad_array_size_label, self.pad_array_size_entry)
|
|
|
+ # populate self.intial_table_rows dict with the tool number as keys and aperture codes as values
|
|
|
+ for i in range(len(self.sorted_apcode)):
|
|
|
+ tt_aperture = self.sorted_apcode[i]
|
|
|
+ self.tid2apcode[i + 1] = tt_aperture
|
|
|
|
|
|
- self.array_linear_frame = QtWidgets.QFrame()
|
|
|
- self.array_linear_frame.setContentsMargins(0, 0, 0, 0)
|
|
|
- self.array_box.addWidget(self.array_linear_frame)
|
|
|
- self.linear_box = QtWidgets.QVBoxLayout()
|
|
|
- self.linear_box.setContentsMargins(0, 0, 0, 0)
|
|
|
- self.array_linear_frame.setLayout(self.linear_box)
|
|
|
+ # #############################################################################################################
|
|
|
+ # Init appGUI
|
|
|
+ # #############################################################################################################
|
|
|
+ self.ui.buffer_distance_entry.set_value(self.app.defaults["gerber_editor_buff_f"])
|
|
|
+ self.ui.scale_factor_entry.set_value(self.app.defaults["gerber_editor_scale_f"])
|
|
|
+ self.ui.ma_upper_threshold_entry.set_value(self.app.defaults["gerber_editor_ma_high"])
|
|
|
+ self.ui.ma_lower_threshold_entry.set_value(self.app.defaults["gerber_editor_ma_low"])
|
|
|
|
|
|
- self.linear_form = QtWidgets.QFormLayout()
|
|
|
- self.linear_box.addLayout(self.linear_form)
|
|
|
+ self.ui.apsize_entry.set_value(self.app.defaults["gerber_editor_newsize"])
|
|
|
+ self.ui.aptype_cb.set_value(self.app.defaults["gerber_editor_newtype"])
|
|
|
+ self.ui.apdim_entry.set_value(self.app.defaults["gerber_editor_newdim"])
|
|
|
|
|
|
- self.pad_axis_label = QtWidgets.QLabel('%s:' % _('Direction'))
|
|
|
- self.pad_axis_label.setToolTip(
|
|
|
- _("Direction on which the linear array is oriented:\n"
|
|
|
- "- 'X' - horizontal axis \n"
|
|
|
- "- 'Y' - vertical axis or \n"
|
|
|
- "- 'Angle' - a custom angle for the array inclination")
|
|
|
- )
|
|
|
- self.pad_axis_label.setMinimumWidth(100)
|
|
|
+ self.ui.pad_array_size_entry.set_value(int(self.app.defaults["gerber_editor_array_size"]))
|
|
|
+ # linear array
|
|
|
+ self.ui.pad_axis_radio.set_value(self.app.defaults["gerber_editor_lin_axis"])
|
|
|
+ self.ui.pad_pitch_entry.set_value(float(self.app.defaults["gerber_editor_lin_pitch"]))
|
|
|
+ self.ui.linear_angle_spinner.set_value(self.app.defaults["gerber_editor_lin_angle"])
|
|
|
+ # circular array
|
|
|
+ self.ui.pad_direction_radio.set_value(self.app.defaults["gerber_editor_circ_dir"])
|
|
|
+ self.ui.pad_angle_entry.set_value(float(self.app.defaults["gerber_editor_circ_angle"]))
|
|
|
|
|
|
- self.pad_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
|
|
|
- {'label': _('Y'), 'value': 'Y'},
|
|
|
- {'label': _('Angle'), 'value': 'A'}])
|
|
|
- self.pad_axis_radio.set_value('X')
|
|
|
- self.linear_form.addRow(self.pad_axis_label, self.pad_axis_radio)
|
|
|
+ def build_ui(self, first_run=None):
|
|
|
|
|
|
- self.pad_pitch_label = QtWidgets.QLabel('%s:' % _('Pitch'))
|
|
|
- self.pad_pitch_label.setToolTip(
|
|
|
- _("Pitch = Distance between elements of the array.")
|
|
|
- )
|
|
|
- self.pad_pitch_label.setMinimumWidth(100)
|
|
|
+ try:
|
|
|
+ # if connected, disconnect the signal from the slot on item_changed as it creates issues
|
|
|
+ self.ui.apertures_table.itemChanged.disconnect()
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
|
|
|
- self.pad_pitch_entry = FCDoubleSpinner()
|
|
|
- self.pad_pitch_entry.set_precision(self.decimals)
|
|
|
- self.pad_pitch_entry.set_range(0.0000, 10000.0000)
|
|
|
- self.pad_pitch_entry.setSingleStep(0.1)
|
|
|
-
|
|
|
- self.linear_form.addRow(self.pad_pitch_label, self.pad_pitch_entry)
|
|
|
+ try:
|
|
|
+ self.ui.apertures_table.cellPressed.disconnect()
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
|
|
|
- self.linear_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
|
|
|
- self.linear_angle_label.setToolTip(
|
|
|
- _("Angle at which the linear array is placed.\n"
|
|
|
- "The precision is of max 2 decimals.\n"
|
|
|
- "Min value is: -360.00 degrees.\n"
|
|
|
- "Max value is: 360.00 degrees.")
|
|
|
- )
|
|
|
- self.linear_angle_label.setMinimumWidth(100)
|
|
|
+ # updated units
|
|
|
+ self.units = self.app.defaults['units'].upper()
|
|
|
|
|
|
- self.linear_angle_spinner = FCDoubleSpinner()
|
|
|
- self.linear_angle_spinner.set_precision(self.decimals)
|
|
|
- self.linear_angle_spinner.setRange(-360.00, 360.00)
|
|
|
- self.linear_form.addRow(self.linear_angle_label, self.linear_angle_spinner)
|
|
|
+ # make a new name for the new Excellon object (the one with edited content)
|
|
|
+ self.edited_obj_name = self.gerber_obj.options['name']
|
|
|
+ self.ui.name_entry.set_value(self.edited_obj_name)
|
|
|
|
|
|
- self.array_circular_frame = QtWidgets.QFrame()
|
|
|
- self.array_circular_frame.setContentsMargins(0, 0, 0, 0)
|
|
|
- self.array_box.addWidget(self.array_circular_frame)
|
|
|
- self.circular_box = QtWidgets.QVBoxLayout()
|
|
|
- self.circular_box.setContentsMargins(0, 0, 0, 0)
|
|
|
- self.array_circular_frame.setLayout(self.circular_box)
|
|
|
+ self.apertures_row = 0
|
|
|
+ # aper_no = self.apertures_row + 1
|
|
|
|
|
|
- self.pad_direction_label = QtWidgets.QLabel('%s:' % _('Direction'))
|
|
|
- self.pad_direction_label.setToolTip(
|
|
|
- _("Direction for circular array.\n"
|
|
|
- "Can be CW = clockwise or CCW = counter clockwise.")
|
|
|
- )
|
|
|
- self.pad_direction_label.setMinimumWidth(100)
|
|
|
+ sort = []
|
|
|
+ for k, v in list(self.storage_dict.items()):
|
|
|
+ sort.append(int(k))
|
|
|
|
|
|
- self.circular_form = QtWidgets.QFormLayout()
|
|
|
- self.circular_box.addLayout(self.circular_form)
|
|
|
+ sorted_apertures = sorted(sort)
|
|
|
|
|
|
- self.pad_direction_radio = RadioSet([{'label': _('CW'), 'value': 'CW'},
|
|
|
- {'label': _('CCW'), 'value': 'CCW'}])
|
|
|
- self.pad_direction_radio.set_value('CW')
|
|
|
- self.circular_form.addRow(self.pad_direction_label, self.pad_direction_radio)
|
|
|
+ # sort = []
|
|
|
+ # for k, v in list(self.gerber_obj.aperture_macros.items()):
|
|
|
+ # sort.append(k)
|
|
|
+ # sorted_macros = sorted(sort)
|
|
|
|
|
|
- self.pad_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
|
|
|
- self.pad_angle_label.setToolTip(
|
|
|
- _("Angle at which each element in circular array is placed.")
|
|
|
- )
|
|
|
- self.pad_angle_label.setMinimumWidth(100)
|
|
|
+ # n = len(sorted_apertures) + len(sorted_macros)
|
|
|
+ n = len(sorted_apertures)
|
|
|
+ self.ui.apertures_table.setRowCount(n)
|
|
|
|
|
|
- self.pad_angle_entry = FCDoubleSpinner()
|
|
|
- self.pad_angle_entry.set_precision(self.decimals)
|
|
|
- self.pad_angle_entry.set_range(-360.00, 360.00)
|
|
|
- self.pad_angle_entry.setSingleStep(0.1)
|
|
|
+ for ap_code in sorted_apertures:
|
|
|
+ ap_code = str(ap_code)
|
|
|
|
|
|
- self.circular_form.addRow(self.pad_angle_label, self.pad_angle_entry)
|
|
|
+ ap_code_item = QtWidgets.QTableWidgetItem('%d' % int(self.apertures_row + 1))
|
|
|
+ ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
|
|
+ self.ui.apertures_table.setItem(self.apertures_row, 0, ap_code_item) # Tool name/id
|
|
|
|
|
|
- self.array_circular_frame.hide()
|
|
|
+ ap_code_item = QtWidgets.QTableWidgetItem(ap_code)
|
|
|
+ ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
|
|
|
|
|
- self.linear_angle_spinner.hide()
|
|
|
- self.linear_angle_label.hide()
|
|
|
+ ap_type_item = QtWidgets.QTableWidgetItem(str(self.storage_dict[ap_code]['type']))
|
|
|
+ ap_type_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
|
|
|
|
|
- self.array_frame.hide()
|
|
|
- self.custom_box.addStretch()
|
|
|
+ if str(self.storage_dict[ap_code]['type']) == 'R' or str(self.storage_dict[ap_code]['type']) == 'O':
|
|
|
+ ap_dim_item = QtWidgets.QTableWidgetItem(
|
|
|
+ '%.*f, %.*f' % (self.decimals, self.storage_dict[ap_code]['width'],
|
|
|
+ self.decimals, self.storage_dict[ap_code]['height']
|
|
|
+ )
|
|
|
+ )
|
|
|
+ ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
|
|
|
+ elif str(self.storage_dict[ap_code]['type']) == 'P':
|
|
|
+ ap_dim_item = QtWidgets.QTableWidgetItem(
|
|
|
+ '%.*f, %.*f' % (self.decimals, self.storage_dict[ap_code]['diam'],
|
|
|
+ self.decimals, self.storage_dict[ap_code]['nVertices'])
|
|
|
+ )
|
|
|
+ ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
|
|
|
+ else:
|
|
|
+ ap_dim_item = QtWidgets.QTableWidgetItem('')
|
|
|
+ ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
|
|
|
|
|
- layout.addStretch()
|
|
|
+ try:
|
|
|
+ if self.storage_dict[ap_code]['size'] is not None:
|
|
|
+ ap_size_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals,
|
|
|
+ float(self.storage_dict[ap_code]['size'])))
|
|
|
+ else:
|
|
|
+ ap_size_item = QtWidgets.QTableWidgetItem('')
|
|
|
+ except KeyError:
|
|
|
+ ap_size_item = QtWidgets.QTableWidgetItem('')
|
|
|
|
|
|
- # Editor
|
|
|
- self.exit_editor_button = QtWidgets.QPushButton(_('Exit Editor'))
|
|
|
- self.exit_editor_button.setIcon(QtGui.QIcon(self.app.resource_location + '/power16.png'))
|
|
|
- self.exit_editor_button.setToolTip(
|
|
|
- _("Exit from Editor.")
|
|
|
- )
|
|
|
- self.exit_editor_button.setStyleSheet("""
|
|
|
- QPushButton
|
|
|
- {
|
|
|
- font-weight: bold;
|
|
|
- }
|
|
|
- """)
|
|
|
- layout.addWidget(self.exit_editor_button)
|
|
|
+ if str(self.storage_dict[ap_code]['type']) == 'C':
|
|
|
+ ap_size_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
|
|
|
+ else:
|
|
|
+ ap_size_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
|
|
|
|
|
- self.exit_editor_button.clicked.connect(lambda: self.app.editor2object())
|
|
|
+ self.ui.apertures_table.setItem(self.apertures_row, 1, ap_code_item) # Aperture Code
|
|
|
+ self.ui.apertures_table.setItem(self.apertures_row, 2, ap_type_item) # Aperture Type
|
|
|
+ self.ui.apertures_table.setItem(self.apertures_row, 3, ap_size_item) # Aperture Size
|
|
|
+ self.ui.apertures_table.setItem(self.apertures_row, 4, ap_dim_item) # Aperture Dimensions
|
|
|
|
|
|
- # Toolbar events and properties
|
|
|
- self.tools_gerber = {}
|
|
|
+ self.apertures_row += 1
|
|
|
+ if first_run is True:
|
|
|
+ # set now the last aperture selected
|
|
|
+ self.last_aperture_selected = ap_code
|
|
|
|
|
|
- # # ## Data
|
|
|
- self.active_tool = None
|
|
|
+ # for ap_code in sorted_macros:
|
|
|
+ # ap_code = str(ap_code)
|
|
|
+ #
|
|
|
+ # ap_code_item = QtWidgets.QTableWidgetItem('%d' % int(self.apertures_row + 1))
|
|
|
+ # ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
|
|
+ # self.ui.apertures_table.setItem(self.apertures_row, 0, ap_code_item) # Tool name/id
|
|
|
+ #
|
|
|
+ # ap_code_item = QtWidgets.QTableWidgetItem(ap_code)
|
|
|
+ #
|
|
|
+ # ap_type_item = QtWidgets.QTableWidgetItem('AM')
|
|
|
+ # ap_type_item.setFlags(QtCore.Qt.ItemIsEnabled)
|
|
|
+ #
|
|
|
+ # self.ui.apertures_table.setItem(self.apertures_row, 1, ap_code_item) # Aperture Code
|
|
|
+ # self.ui.apertures_table.setItem(self.apertures_row, 2, ap_type_item) # Aperture Type
|
|
|
+ #
|
|
|
+ # self.apertures_row += 1
|
|
|
+ # if first_run is True:
|
|
|
+ # # set now the last aperture selected
|
|
|
+ # self.last_aperture_selected = ap_code
|
|
|
|
|
|
- self.storage_dict = {}
|
|
|
- self.current_storage = []
|
|
|
+ self.ui.apertures_table.selectColumn(0)
|
|
|
+ self.ui.apertures_table.resizeColumnsToContents()
|
|
|
+ self.ui.apertures_table.resizeRowsToContents()
|
|
|
|
|
|
- self.sorted_apcode = []
|
|
|
+ vertical_header = self.ui.apertures_table.verticalHeader()
|
|
|
+ # vertical_header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
|
|
|
+ vertical_header.hide()
|
|
|
+ self.ui.apertures_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
|
|
|
|
|
- self.new_apertures = {}
|
|
|
- self.new_aperture_macros = {}
|
|
|
+ horizontal_header = self.ui.apertures_table.horizontalHeader()
|
|
|
+ horizontal_header.setMinimumSectionSize(10)
|
|
|
+ horizontal_header.setDefaultSectionSize(70)
|
|
|
+ horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
|
|
|
+ horizontal_header.resizeSection(0, 27)
|
|
|
+ horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
|
|
|
+ horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
|
|
|
+ horizontal_header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents)
|
|
|
+ horizontal_header.setSectionResizeMode(4, QtWidgets.QHeaderView.Stretch)
|
|
|
|
|
|
- # store here the plot promises, if empty the delayed plot will be activated
|
|
|
- self.grb_plot_promises = []
|
|
|
+ self.ui.apertures_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
|
|
+ self.ui.apertures_table.setSortingEnabled(False)
|
|
|
+ self.ui.apertures_table.setMinimumHeight(self.ui.apertures_table.getHeight())
|
|
|
+ self.ui.apertures_table.setMaximumHeight(self.ui.apertures_table.getHeight())
|
|
|
|
|
|
- # dictionary to store the tool_row and aperture codes in Tool_table
|
|
|
- # it will be updated everytime self.build_ui() is called
|
|
|
- self.oldapcode_newapcode = {}
|
|
|
+ # make sure no rows are selected so the user have to click the correct row, meaning selecting the correct tool
|
|
|
+ self.ui.apertures_table.clearSelection()
|
|
|
|
|
|
- self.tid2apcode = {}
|
|
|
+ # Remove anything else in the GUI Properties Tab
|
|
|
+ self.app.ui.properties_scroll_area.takeWidget()
|
|
|
+ # Put ourselves in the GUI Properties Tab
|
|
|
+ self.app.ui.properties_scroll_area.setWidget(self.ui.grb_edit_widget)
|
|
|
+ # Switch notebook to Properties page
|
|
|
+ self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
|
|
|
|
|
|
- # this will store the value for the last selected tool, for use after clicking on canvas when the selection
|
|
|
- # is cleared but as a side effect also the selected tool is cleared
|
|
|
- self.last_aperture_selected = None
|
|
|
- self.utility = []
|
|
|
+ # we reactivate the signals after the after the tool adding as we don't need to see the tool been populated
|
|
|
+ self.ui.apertures_table.itemChanged.connect(self.on_tool_edit)
|
|
|
+ self.ui.apertures_table.cellPressed.connect(self.on_row_selected)
|
|
|
|
|
|
- # this will store the polygons marked by mark are to be perhaps deleted
|
|
|
- self.geo_to_delete = []
|
|
|
+ # for convenience set the next aperture code in the apcode field
|
|
|
+ try:
|
|
|
+ self.ui.apcode_entry.set_value(max(self.tid2apcode.values()) + 1)
|
|
|
+ except ValueError:
|
|
|
+ # this means that the edited object has no apertures so we start with 10 (Gerber specifications)
|
|
|
+ self.ui.apcode_entry.set_value(self.app.defaults["gerber_editor_newcode"])
|
|
|
|
|
|
- # this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
|
|
|
- self.launched_from_shortcuts = False
|
|
|
+ def on_aperture_add(self, apcode=None):
|
|
|
+ self.is_modified = True
|
|
|
+ if apcode:
|
|
|
+ ap_code = apcode
|
|
|
+ else:
|
|
|
+ try:
|
|
|
+ ap_code = str(self.ui.apcode_entry.get_value())
|
|
|
+ except ValueError:
|
|
|
+ self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
+ _("Aperture code value is missing or wrong format. Add it and retry."))
|
|
|
+ return
|
|
|
+ if ap_code == '':
|
|
|
+ self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
+ _("Aperture code value is missing or wrong format. Add it and retry."))
|
|
|
+ return
|
|
|
|
|
|
- # this var will store the state of the toolbar before starting the editor
|
|
|
- self.toolbar_old_state = False
|
|
|
+ if ap_code == '0':
|
|
|
+ if ap_code not in self.tid2apcode:
|
|
|
+ self.storage_dict[ap_code] = {}
|
|
|
+ self.storage_dict[ap_code]['type'] = 'REG'
|
|
|
+ size_val = 0
|
|
|
+ self.ui.apsize_entry.set_value(size_val)
|
|
|
+ self.storage_dict[ap_code]['size'] = size_val
|
|
|
|
|
|
- # Init appGUI
|
|
|
- self.apdim_lbl.hide()
|
|
|
- self.apdim_entry.hide()
|
|
|
- self.gerber_obj = None
|
|
|
- self.gerber_obj_options = {}
|
|
|
+ self.storage_dict[ap_code]['geometry'] = []
|
|
|
|
|
|
- # VisPy Visuals
|
|
|
- if self.app.is_legacy is False:
|
|
|
- self.shapes = self.canvas.new_shape_collection(layers=1)
|
|
|
- self.tool_shape = self.canvas.new_shape_collection(layers=1)
|
|
|
- self.ma_annotation = self.canvas.new_text_group()
|
|
|
+ # self.oldapcode_newapcode dict keeps the evidence on current aperture codes as keys and
|
|
|
+ # gets updated on values each time a aperture code is edited or added
|
|
|
+ self.oldapcode_newapcode[ap_code] = ap_code
|
|
|
else:
|
|
|
- from appGUI.PlotCanvasLegacy import ShapeCollectionLegacy
|
|
|
- self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_grb_editor')
|
|
|
- self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_grb_editor')
|
|
|
- self.ma_annotation = ShapeCollectionLegacy(
|
|
|
- obj=self,
|
|
|
- app=self.app,
|
|
|
- name='ma_anno_grb_editor',
|
|
|
- annotation_job=True)
|
|
|
+ if ap_code not in self.oldapcode_newapcode:
|
|
|
+ self.storage_dict[ap_code] = {}
|
|
|
|
|
|
- self.app.pool_recreated.connect(self.pool_recreated)
|
|
|
+ type_val = self.aptype_cb.currentText()
|
|
|
+ self.storage_dict[ap_code]['type'] = type_val
|
|
|
|
|
|
- # Event signals disconnect id holders
|
|
|
- self.mp = None
|
|
|
- self.mm = None
|
|
|
- self.mr = None
|
|
|
+ if type_val == 'R' or type_val == 'O':
|
|
|
+ try:
|
|
|
+ dims = self.ui.apdim_entry.get_value()
|
|
|
+ self.storage_dict[ap_code]['width'] = dims[0]
|
|
|
+ self.storage_dict[ap_code]['height'] = dims[1]
|
|
|
|
|
|
- # Remove from scene
|
|
|
- self.shapes.enabled = False
|
|
|
- self.tool_shape.enabled = False
|
|
|
+ size_val = np.sqrt((dims[0] ** 2) + (dims[1] ** 2))
|
|
|
+ self.ui.apsize_entry.set_value(size_val)
|
|
|
|
|
|
- # List of selected geometric elements.
|
|
|
- self.selected = []
|
|
|
+ except Exception as e:
|
|
|
+ log.error("AppGerberEditor.on_aperture_add() --> the R or O aperture dims has to be in a "
|
|
|
+ "tuple format (x,y)\nError: %s" % str(e))
|
|
|
+ self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
+ _("Aperture dimensions value is missing or wrong format. "
|
|
|
+ "Add it in format (width, height) and retry."))
|
|
|
+ return
|
|
|
+ else:
|
|
|
+ try:
|
|
|
+ size_val = float(self.ui.apsize_entry.get_value())
|
|
|
+ except ValueError:
|
|
|
+ # try to convert comma to decimal point. if it's still not working error message and return
|
|
|
+ try:
|
|
|
+ size_val = float(self.ui.apsize_entry.get_value().replace(',', '.'))
|
|
|
+ self.ui.apsize_entry.set_value(size_val)
|
|
|
+ except ValueError:
|
|
|
+ self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
+ _("Aperture size value is missing or wrong format. Add it and retry."))
|
|
|
+ return
|
|
|
+ self.storage_dict[ap_code]['size'] = size_val
|
|
|
|
|
|
- self.key = None # Currently pressed key
|
|
|
- self.modifiers = None
|
|
|
- self.x = None # Current mouse cursor pos
|
|
|
- self.y = None
|
|
|
- # Current snapped mouse pos
|
|
|
- self.snap_x = None
|
|
|
- self.snap_y = None
|
|
|
- self.pos = None
|
|
|
-
|
|
|
- # used in FCRegion and FCTrack. Will store the bending mode
|
|
|
- self.bend_mode = 1
|
|
|
-
|
|
|
- # signal that there is an action active like polygon or path
|
|
|
- self.in_action = False
|
|
|
- # this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
|
|
|
- self.launched_from_shortcuts = False
|
|
|
+ self.storage_dict[ap_code]['geometry'] = []
|
|
|
|
|
|
- def_tol_val = float(self.app.defaults["global_tolerance"])
|
|
|
- self.tolerance = def_tol_val if self.units == 'MM'else def_tol_val / 20
|
|
|
+ # self.oldapcode_newapcode dict keeps the evidence on current aperture codes as keys and gets updated on
|
|
|
+ # values each time a aperture code is edited or added
|
|
|
+ self.oldapcode_newapcode[ap_code] = ap_code
|
|
|
+ else:
|
|
|
+ self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
+ _("Aperture already in the aperture table."))
|
|
|
+ return
|
|
|
|
|
|
- self.options = {
|
|
|
- "global_gridx": 0.1,
|
|
|
- "global_gridy": 0.1,
|
|
|
- "snap_max": 0.05,
|
|
|
- "grid_snap": True,
|
|
|
- "corner_snap": False,
|
|
|
- "grid_gap_link": True
|
|
|
- }
|
|
|
- self.options.update(self.app.options)
|
|
|
+ # since we add a new tool, we update also the initial state of the tool_table through it's dictionary
|
|
|
+ # we add a new entry in the tid2apcode dict
|
|
|
+ self.tid2apcode[len(self.oldapcode_newapcode)] = int(ap_code)
|
|
|
|
|
|
- for option in self.options:
|
|
|
- if option in self.app.options:
|
|
|
- self.options[option] = self.app.options[option]
|
|
|
+ self.app.inform.emit('[success] %s: %s' % (_("Added new aperture with code"), str(ap_code)))
|
|
|
|
|
|
- # flag to show if the object was modified
|
|
|
- self.is_modified = False
|
|
|
- self.edited_obj_name = ""
|
|
|
- self.tool_row = 0
|
|
|
+ self.build_ui()
|
|
|
|
|
|
- # Multiprocessing pool
|
|
|
- self.pool = self.app.pool
|
|
|
+ self.last_aperture_selected = ap_code
|
|
|
|
|
|
- # Multiprocessing results
|
|
|
- self.results = []
|
|
|
+ # make a quick sort through the tid2apcode dict so we find which row to select
|
|
|
+ row_to_be_selected = None
|
|
|
+ for key in sorted(self.tid2apcode):
|
|
|
+ if self.tid2apcode[key] == int(ap_code):
|
|
|
+ row_to_be_selected = int(key) - 1
|
|
|
+ break
|
|
|
+ self.ui.apertures_table.selectRow(row_to_be_selected)
|
|
|
|
|
|
- # A QTimer
|
|
|
- self.plot_thread = None
|
|
|
+ def on_aperture_delete(self, ap_code=None):
|
|
|
+ """
|
|
|
+ Called for aperture deletion.
|
|
|
|
|
|
- # a QThread for the edit process
|
|
|
- self.thread = QtCore.QThread()
|
|
|
+ :param ap_code: An Aperture code; String
|
|
|
+ :return:
|
|
|
+ """
|
|
|
+ self.is_modified = True
|
|
|
|
|
|
- # def entry2option(option, entry):
|
|
|
- # self.options[option] = float(entry.text())
|
|
|
+ try:
|
|
|
+ if ap_code:
|
|
|
+ try:
|
|
|
+ deleted_apcode_list = [dd for dd in ap_code]
|
|
|
+ except TypeError:
|
|
|
+ deleted_apcode_list = [ap_code]
|
|
|
+ else:
|
|
|
+ # deleted_tool_dia = float(self.ui.apertures_table.item(self.ui.apertures_table.currentRow(), 1).text())
|
|
|
+ if len(self.ui.apertures_table.selectionModel().selectedRows()) == 0:
|
|
|
+ self.app.inform.emit('[WARNING_NOTCL] %s' % _(" Select an aperture in Aperture Table"))
|
|
|
+ return
|
|
|
|
|
|
- self.transform_tool = TransformEditorTool(self.app, self)
|
|
|
+ deleted_apcode_list = []
|
|
|
+ for index in self.ui.apertures_table.selectionModel().selectedRows():
|
|
|
+ row = index.row()
|
|
|
+ deleted_apcode_list.append(self.ui.apertures_table.item(row, 1).text())
|
|
|
+ except Exception as exc:
|
|
|
+ self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Select an aperture in Aperture Table -->", str(exc))))
|
|
|
+ return
|
|
|
|
|
|
- # #############################################################################################################
|
|
|
- # ######################### Gerber Editor Signals #############################################################
|
|
|
- # #############################################################################################################
|
|
|
+ if deleted_apcode_list:
|
|
|
+ for deleted_aperture in deleted_apcode_list:
|
|
|
+ # delete the storage used for that tool
|
|
|
+ self.storage_dict.pop(deleted_aperture, None)
|
|
|
|
|
|
- # connect the toolbar signals
|
|
|
- self.connect_grb_toolbar_signals()
|
|
|
+ for deleted_tool in list(self.tid2apcode.keys()):
|
|
|
+ if self.tid2apcode[deleted_tool] == deleted_aperture:
|
|
|
+ # delete the tool
|
|
|
+ self.tid2apcode.pop(deleted_tool, None)
|
|
|
|
|
|
- self.buffer_button.clicked.connect(self.on_buffer)
|
|
|
- self.scale_button.clicked.connect(self.on_scale)
|
|
|
+ self.oldapcode_newapcode.pop(deleted_aperture, None)
|
|
|
+ self.app.inform.emit('[success] %s: %s' % (_("Deleted aperture with code"), str(deleted_aperture)))
|
|
|
|
|
|
- self.app.ui.aperture_delete_btn.triggered.connect(self.on_delete_btn)
|
|
|
- self.name_entry.returnPressed.connect(self.on_name_activate)
|
|
|
+ self.plot_all()
|
|
|
+ self.build_ui()
|
|
|
|
|
|
- self.aptype_cb.currentIndexChanged[str].connect(self.on_aptype_changed)
|
|
|
+ # if last aperture selected was in the apertures deleted than make sure to select a
|
|
|
+ # 'new' last aperture selected because there are tools who depend on it.
|
|
|
+ # if there is no aperture left, then add a default one :)
|
|
|
+ if self.last_aperture_selected in deleted_apcode_list:
|
|
|
+ if self.ui.apertures_table.rowCount() == 0:
|
|
|
+ self.on_aperture_add('10')
|
|
|
+ self.last_aperture_selected = '10'
|
|
|
+ else:
|
|
|
+ self.last_aperture_selected = self.ui.apertures_table.item(0, 1).text()
|
|
|
|
|
|
- self.addaperture_btn.clicked.connect(self.on_aperture_add)
|
|
|
- self.apsize_entry.returnPressed.connect(self.on_aperture_add)
|
|
|
- self.apdim_entry.returnPressed.connect(self.on_aperture_add)
|
|
|
+ def on_tool_edit(self):
|
|
|
+ if self.ui.apertures_table.currentItem() is None:
|
|
|
+ return
|
|
|
|
|
|
- self.delaperture_btn.clicked.connect(self.on_aperture_delete)
|
|
|
- self.apertures_table.cellPressed.connect(self.on_row_selected)
|
|
|
+ # if connected, disconnect the signal from the slot on item_changed as it creates issues
|
|
|
+ self.ui.apertures_table.itemChanged.disconnect()
|
|
|
+ # self.ui.apertures_table.cellPressed.disconnect()
|
|
|
|
|
|
- self.app.ui.grb_add_pad_menuitem.triggered.connect(self.on_pad_add)
|
|
|
- self.app.ui.grb_add_pad_array_menuitem.triggered.connect(self.on_pad_add_array)
|
|
|
+ self.is_modified = True
|
|
|
+ val_edited = None
|
|
|
|
|
|
- self.app.ui.grb_add_track_menuitem.triggered.connect(self.on_track_add)
|
|
|
- self.app.ui.grb_add_region_menuitem.triggered.connect(self.on_region_add)
|
|
|
+ row_of_item_changed = self.ui.apertures_table.currentRow()
|
|
|
+ col_of_item_changed = self.ui.apertures_table.currentColumn()
|
|
|
|
|
|
- self.app.ui.grb_convert_poly_menuitem.triggered.connect(self.on_poligonize)
|
|
|
- self.app.ui.grb_add_semidisc_menuitem.triggered.connect(self.on_add_semidisc)
|
|
|
- self.app.ui.grb_add_disc_menuitem.triggered.connect(self.on_disc_add)
|
|
|
- self.app.ui.grb_add_buffer_menuitem.triggered.connect(self.on_buffer)
|
|
|
- self.app.ui.grb_add_scale_menuitem.triggered.connect(self.on_scale)
|
|
|
- self.app.ui.grb_add_eraser_menuitem.triggered.connect(self.on_eraser)
|
|
|
- self.app.ui.grb_add_markarea_menuitem.triggered.connect(self.on_markarea)
|
|
|
+ # rows start with 0, tools start with 1 so we adjust the value by 1
|
|
|
+ key_in_tid2apcode = row_of_item_changed + 1
|
|
|
+ ap_code_old = str(self.tid2apcode[key_in_tid2apcode])
|
|
|
|
|
|
- self.app.ui.grb_transform_menuitem.triggered.connect(self.transform_tool.run)
|
|
|
+ ap_code_new = self.ui.apertures_table.item(row_of_item_changed, 1).text()
|
|
|
|
|
|
- self.app.ui.grb_copy_menuitem.triggered.connect(self.on_copy_button)
|
|
|
- self.app.ui.grb_delete_menuitem.triggered.connect(self.on_delete_btn)
|
|
|
+ if col_of_item_changed == 1:
|
|
|
+ # we edited the Aperture Code column (int)
|
|
|
+ try:
|
|
|
+ val_edited = int(self.ui.apertures_table.currentItem().text())
|
|
|
+ except ValueError as e:
|
|
|
+ log.debug("AppGerberEditor.on_tool_edit() --> %s" % str(e))
|
|
|
+ # self.ui.apertures_table.setCurrentItem(None)
|
|
|
+ # we reactivate the signals after the after the tool editing
|
|
|
+ self.ui.apertures_table.itemChanged.connect(self.on_tool_edit)
|
|
|
+ return
|
|
|
+ elif col_of_item_changed == 3:
|
|
|
+ # we edited the Size column (float)
|
|
|
+ try:
|
|
|
+ val_edited = float(self.ui.apertures_table.currentItem().text())
|
|
|
+ except ValueError as e:
|
|
|
+ log.debug("AppGerberEditor.on_tool_edit() --> %s" % str(e))
|
|
|
+ # self.ui.apertures_table.setCurrentItem(None)
|
|
|
+ # we reactivate the signals after the after the tool editing
|
|
|
+ self.ui.apertures_table.itemChanged.connect(self.on_tool_edit)
|
|
|
+ return
|
|
|
+ elif col_of_item_changed == 4:
|
|
|
+ # we edit the Dimensions column (tuple)
|
|
|
+ try:
|
|
|
+ val_edited = [
|
|
|
+ float(x.strip()) for x in self.ui.apertures_table.currentItem().text().split(",") if x != ''
|
|
|
+ ]
|
|
|
+ except ValueError as e:
|
|
|
+ log.debug("AppGerberEditor.on_tool_edit() --> %s" % str(e))
|
|
|
+ # we reactivate the signals after the after the tool editing
|
|
|
+ self.ui.apertures_table.itemChanged.connect(self.on_tool_edit)
|
|
|
+ return
|
|
|
|
|
|
- self.app.ui.grb_move_menuitem.triggered.connect(self.on_move_button)
|
|
|
+ if len(val_edited) != 2:
|
|
|
+ self.app.inform.emit("[WARNING_NOTCL] %s" % _("Dimensions need two float values separated by comma."))
|
|
|
+ old_dims_txt = '%s, %s' % (str(self.storage_dict[ap_code_new]['width']),
|
|
|
+ str(self.storage_dict[ap_code_new]['height']))
|
|
|
|
|
|
- self.array_type_combo.currentIndexChanged.connect(self.on_array_type_combo)
|
|
|
- self.pad_axis_radio.activated_custom.connect(self.on_linear_angle_radio)
|
|
|
+ self.ui.apertures_table.currentItem().setText(old_dims_txt)
|
|
|
+ # we reactivate the signals after the after the tool editing
|
|
|
+ self.ui.apertures_table.itemChanged.connect(self.on_tool_edit)
|
|
|
+ return
|
|
|
+ else:
|
|
|
+ self.app.inform.emit("[success] %s" % _("Dimensions edited."))
|
|
|
|
|
|
- self.mp_finished.connect(self.on_multiprocessing_finished)
|
|
|
+ # In case we edited the Aperture Code therefore the val_edited holds a new Aperture Code
|
|
|
+ # TODO Edit of the Aperture Code is not active yet
|
|
|
+ if col_of_item_changed == 1:
|
|
|
+ # aperture code is not used so we create a new Aperture with the desired Aperture Code
|
|
|
+ if val_edited not in self.oldapcode_newapcode.values():
|
|
|
+ # update the dict that holds as keys old Aperture Codes and as values the new Aperture Codes
|
|
|
+ self.oldapcode_newapcode[ap_code_old] = val_edited
|
|
|
+ # update the dict that holds tool_no as key and tool_dia as value
|
|
|
+ self.tid2apcode[key_in_tid2apcode] = val_edited
|
|
|
|
|
|
- self.conversion_factor = 1
|
|
|
+ old_aperture_val = self.storage_dict.pop(ap_code_old)
|
|
|
+ self.storage_dict[val_edited] = old_aperture_val
|
|
|
|
|
|
- self.apertures_row = 0
|
|
|
+ else:
|
|
|
+ # aperture code is already in use so we move the pads from the prior tool to the new tool
|
|
|
+ # but only if they are of the same type
|
|
|
|
|
|
- self.complete = True
|
|
|
+ if self.storage_dict[ap_code_old]['type'] == self.storage_dict[ap_code_new]['type']:
|
|
|
+ # TODO I have to work here; if type == 'R' or 'O' have t otake care of all attributes ...
|
|
|
+ factor = val_edited / float(ap_code_old)
|
|
|
+ geometry = []
|
|
|
+ for geo_el in self.storage_dict[ap_code_old]:
|
|
|
+ geometric_data = geo_el.geo
|
|
|
+ new_geo_el = {}
|
|
|
+ if 'solid' in geometric_data:
|
|
|
+ new_geo_el['solid'] = deepcopy(affinity.scale(geometric_data['solid'],
|
|
|
+ xfact=factor, yfact=factor))
|
|
|
+ if 'follow' in geometric_data:
|
|
|
+ new_geo_el['follow'] = deepcopy(affinity.scale(geometric_data['follow'],
|
|
|
+ xfact=factor, yfact=factor))
|
|
|
+ if 'clear' in geometric_data:
|
|
|
+ new_geo_el['clear'] = deepcopy(affinity.scale(geometric_data['clear'],
|
|
|
+ xfact=factor, yfact=factor))
|
|
|
+ geometry.append(new_geo_el)
|
|
|
|
|
|
- self.set_ui()
|
|
|
- log.debug("Initialization of the Gerber Editor is finished ...")
|
|
|
+ self.add_gerber_shape(geometry, self.storage_dict[val_edited])
|
|
|
|
|
|
- def make_callback(self, the_tool):
|
|
|
- def f():
|
|
|
- self.on_tool_select(the_tool)
|
|
|
+ self.on_aperture_delete(apcode=ap_code_old)
|
|
|
|
|
|
- return f
|
|
|
+ # In case we edited the Size of the Aperture therefore the val_edited holds the new Aperture Size
|
|
|
+ # It will happen only for the Aperture Type == 'C' - I make sure of that in the self.build_ui()
|
|
|
+ elif col_of_item_changed == 3:
|
|
|
+ old_size = float(self.storage_dict[ap_code_old]['size'])
|
|
|
+ new_size = float(val_edited)
|
|
|
+ adjust_size = (new_size - old_size) / 2
|
|
|
+ geometry = []
|
|
|
+ for geo_el in self.storage_dict[ap_code_old]['geometry']:
|
|
|
+ g_data = geo_el.geo
|
|
|
+ new_geo_el = {}
|
|
|
+ if 'solid' in g_data:
|
|
|
+ if 'follow' in g_data:
|
|
|
+ if isinstance(g_data['follow'], Point):
|
|
|
+ new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(adjust_size))
|
|
|
+ else:
|
|
|
+ new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(adjust_size, join_style=2))
|
|
|
+ if 'follow' in g_data:
|
|
|
+ new_geo_el['follow'] = deepcopy(g_data['follow'])
|
|
|
+ if 'clear' in g_data:
|
|
|
+ new_geo_el['clear'] = deepcopy(g_data['clear'].buffer(adjust_size, join_style=2))
|
|
|
+ geometry.append(DrawToolShape(new_geo_el))
|
|
|
|
|
|
- def connect_grb_toolbar_signals(self):
|
|
|
- self.tools_gerber.update({
|
|
|
- "select": {"button": self.app.ui.grb_select_btn, "constructor": FCApertureSelect},
|
|
|
- "pad": {"button": self.app.ui.grb_add_pad_btn, "constructor": FCPad},
|
|
|
- "array": {"button": self.app.ui.add_pad_ar_btn, "constructor": FCPadArray},
|
|
|
- "track": {"button": self.app.ui.grb_add_track_btn, "constructor": FCTrack},
|
|
|
- "region": {"button": self.app.ui.grb_add_region_btn, "constructor": FCRegion},
|
|
|
- "poligonize": {"button": self.app.ui.grb_convert_poly_btn, "constructor": FCPoligonize},
|
|
|
- "semidisc": {"button": self.app.ui.grb_add_semidisc_btn, "constructor": FCSemiDisc},
|
|
|
- "disc": {"button": self.app.ui.grb_add_disc_btn, "constructor": FCDisc},
|
|
|
- "buffer": {"button": self.app.ui.aperture_buffer_btn, "constructor": FCBuffer},
|
|
|
- "scale": {"button": self.app.ui.aperture_scale_btn, "constructor": FCScale},
|
|
|
- "markarea": {"button": self.app.ui.aperture_markarea_btn, "constructor": FCMarkArea},
|
|
|
- "eraser": {"button": self.app.ui.aperture_eraser_btn, "constructor": FCEraser},
|
|
|
- "copy": {"button": self.app.ui.aperture_copy_btn, "constructor": FCApertureCopy},
|
|
|
- "transform": {"button": self.app.ui.grb_transform_btn, "constructor": FCTransform},
|
|
|
- "move": {"button": self.app.ui.aperture_move_btn, "constructor": FCApertureMove},
|
|
|
- })
|
|
|
+ self.storage_dict[ap_code_old]['geometry'].clear()
|
|
|
+ self.add_gerber_shape(geometry, self.storage_dict[ap_code_old]['geometry'])
|
|
|
+ # self.storage_dict[ap_code_old]['geometry'] = geometry
|
|
|
|
|
|
- for tool in self.tools_gerber:
|
|
|
- self.tools_gerber[tool]["button"].triggered.connect(self.make_callback(tool)) # Events
|
|
|
- self.tools_gerber[tool]["button"].setCheckable(True)
|
|
|
+ # In case we edited the Dims of the Aperture therefore the val_edited holds a list with the dimensions
|
|
|
+ # in the format [width, height]
|
|
|
+ # It will happen only for the Aperture Type in ['R', 'O'] - I make sure of that in the self.build_ui()
|
|
|
+ # and below
|
|
|
+ elif col_of_item_changed == 4:
|
|
|
+ if str(self.storage_dict[ap_code_old]['type']) == 'R' or str(self.storage_dict[ap_code_old]['type']) == 'O':
|
|
|
+ # use the biggest from them
|
|
|
+ buff_val_lines = max(val_edited)
|
|
|
+ new_width = val_edited[0]
|
|
|
+ new_height = val_edited[1]
|
|
|
|
|
|
- def pool_recreated(self, pool):
|
|
|
- self.shapes.pool = pool
|
|
|
- self.tool_shape.pool = pool
|
|
|
- self.pool = pool
|
|
|
+ geometry = []
|
|
|
+ for geo_el in self.storage_dict[ap_code_old]['geometry']:
|
|
|
+ g_data = geo_el.geo
|
|
|
+ new_geo_el = {}
|
|
|
+ if 'solid' in g_data:
|
|
|
+ if 'follow' in g_data:
|
|
|
+ if isinstance(g_data['follow'], Point):
|
|
|
+ x = g_data['follow'].x
|
|
|
+ y = g_data['follow'].y
|
|
|
+ minx = x - (new_width / 2)
|
|
|
+ miny = y - (new_height / 2)
|
|
|
+ maxx = x + (new_width / 2)
|
|
|
+ maxy = y + (new_height / 2)
|
|
|
+ geo = box(minx=minx, miny=miny, maxx=maxx, maxy=maxy)
|
|
|
+ new_geo_el['solid'] = deepcopy(geo)
|
|
|
+ else:
|
|
|
+ new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(buff_val_lines))
|
|
|
+ if 'follow' in g_data:
|
|
|
+ new_geo_el['follow'] = deepcopy(g_data['follow'])
|
|
|
+ if 'clear' in g_data:
|
|
|
+ if 'follow' in g_data:
|
|
|
+ if isinstance(g_data['follow'], Point):
|
|
|
+ x = g_data['follow'].x
|
|
|
+ y = g_data['follow'].y
|
|
|
+ minx = x - (new_width / 2)
|
|
|
+ miny = y - (new_height / 2)
|
|
|
+ maxx = x + (new_width / 2)
|
|
|
+ maxy = y + (new_height / 2)
|
|
|
+ geo = box(minx=minx, miny=miny, maxx=maxx, maxy=maxy)
|
|
|
+ new_geo_el['clear'] = deepcopy(geo)
|
|
|
+ else:
|
|
|
+ new_geo_el['clear'] = deepcopy(g_data['clear'].buffer(buff_val_lines, join_style=2))
|
|
|
+ geometry.append(DrawToolShape(new_geo_el))
|
|
|
|
|
|
- def set_ui(self):
|
|
|
- # updated units
|
|
|
- self.units = self.app.defaults['units'].upper()
|
|
|
- self.decimals = self.app.decimals
|
|
|
+ self.storage_dict[ap_code_old]['geometry'].clear()
|
|
|
+ self.add_gerber_shape(geometry, self.storage_dict[ap_code_old]['geometry'])
|
|
|
|
|
|
- self.oldapcode_newapcode.clear()
|
|
|
- self.tid2apcode.clear()
|
|
|
+ self.plot_all()
|
|
|
|
|
|
- # update the oldapcode_newapcode dict to make sure we have an updated state of the tool_table
|
|
|
- for key in self.storage_dict:
|
|
|
- self.oldapcode_newapcode[key] = key
|
|
|
+ # we reactivate the signals after the after the tool editing
|
|
|
+ self.ui.apertures_table.itemChanged.connect(self.on_tool_edit)
|
|
|
+ # self.ui.apertures_table.cellPressed.connect(self.on_row_selected)
|
|
|
|
|
|
- sort_temp = []
|
|
|
- for aperture in self.oldapcode_newapcode:
|
|
|
- sort_temp.append(int(aperture))
|
|
|
- self.sorted_apcode = sorted(sort_temp)
|
|
|
+ def on_name_activate(self):
|
|
|
+ self.edited_obj_name = self.ui.name_entry.get_value()
|
|
|
|
|
|
- # populate self.intial_table_rows dict with the tool number as keys and aperture codes as values
|
|
|
- for i in range(len(self.sorted_apcode)):
|
|
|
- tt_aperture = self.sorted_apcode[i]
|
|
|
- self.tid2apcode[i + 1] = tt_aperture
|
|
|
+ def on_aptype_changed(self, current_text):
|
|
|
+ # 'O' is letter O not zero.
|
|
|
+ if current_text == 'R' or current_text == 'O':
|
|
|
+ self.ui.apdim_lbl.show()
|
|
|
+ self.ui.apdim_entry.show()
|
|
|
+ self.ui.apsize_entry.setDisabled(True)
|
|
|
+ else:
|
|
|
+ self.ui.apdim_lbl.hide()
|
|
|
+ self.ui.apdim_entry.hide()
|
|
|
+ self.ui.apsize_entry.setDisabled(False)
|
|
|
|
|
|
- # Init appGUI
|
|
|
+ def activate_grb_editor(self):
|
|
|
+ # adjust the status of the menu entries related to the editor
|
|
|
+ self.app.ui.menueditedit.setDisabled(True)
|
|
|
+ self.app.ui.menueditok.setDisabled(False)
|
|
|
+ # adjust the visibility of some of the canvas context menu
|
|
|
+ self.app.ui.popmenu_edit.setVisible(False)
|
|
|
+ self.app.ui.popmenu_save.setVisible(True)
|
|
|
|
|
|
- self.buffer_distance_entry.set_value(self.app.defaults["gerber_editor_buff_f"])
|
|
|
- self.scale_factor_entry.set_value(self.app.defaults["gerber_editor_scale_f"])
|
|
|
- self.ma_upper_threshold_entry.set_value(self.app.defaults["gerber_editor_ma_high"])
|
|
|
- self.ma_lower_threshold_entry.set_value(self.app.defaults["gerber_editor_ma_low"])
|
|
|
+ self.connect_canvas_event_handlers()
|
|
|
|
|
|
- self.apsize_entry.set_value(self.app.defaults["gerber_editor_newsize"])
|
|
|
- self.aptype_cb.set_value(self.app.defaults["gerber_editor_newtype"])
|
|
|
- self.apdim_entry.set_value(self.app.defaults["gerber_editor_newdim"])
|
|
|
+ # init working objects
|
|
|
+ self.storage_dict = {}
|
|
|
+ self.current_storage = []
|
|
|
+ self.sorted_apcode = []
|
|
|
+ self.new_apertures = {}
|
|
|
+ self.new_aperture_macros = {}
|
|
|
+ self.grb_plot_promises = []
|
|
|
+ self.oldapcode_newapcode = {}
|
|
|
+ self.tid2apcode = {}
|
|
|
|
|
|
- self.pad_array_size_entry.set_value(int(self.app.defaults["gerber_editor_array_size"]))
|
|
|
- # linear array
|
|
|
- self.pad_axis_radio.set_value(self.app.defaults["gerber_editor_lin_axis"])
|
|
|
- self.pad_pitch_entry.set_value(float(self.app.defaults["gerber_editor_lin_pitch"]))
|
|
|
- self.linear_angle_spinner.set_value(self.app.defaults["gerber_editor_lin_angle"])
|
|
|
- # circular array
|
|
|
- self.pad_direction_radio.set_value(self.app.defaults["gerber_editor_circ_dir"])
|
|
|
- self.pad_angle_entry.set_value(float(self.app.defaults["gerber_editor_circ_angle"]))
|
|
|
+ self.shapes.enabled = True
|
|
|
+ self.tool_shape.enabled = True
|
|
|
|
|
|
- def build_ui(self, first_run=None):
|
|
|
+ self.app.ui.corner_snap_btn.setVisible(True)
|
|
|
+ self.app.ui.snap_magnet.setVisible(True)
|
|
|
|
|
|
- try:
|
|
|
- # if connected, disconnect the signal from the slot on item_changed as it creates issues
|
|
|
- self.apertures_table.itemChanged.disconnect()
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
+ self.app.ui.grb_editor_menu.setDisabled(False)
|
|
|
+ self.app.ui.grb_editor_menu.menuAction().setVisible(True)
|
|
|
|
|
|
- try:
|
|
|
- self.apertures_table.cellPressed.disconnect()
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
+ self.app.ui.update_obj_btn.setEnabled(True)
|
|
|
+ self.app.ui.grb_editor_cmenu.setEnabled(True)
|
|
|
|
|
|
- # updated units
|
|
|
- self.units = self.app.defaults['units'].upper()
|
|
|
+ self.app.ui.grb_edit_toolbar.setDisabled(False)
|
|
|
+ self.app.ui.grb_edit_toolbar.setVisible(True)
|
|
|
+ # self.app.ui.grid_toolbar.setDisabled(False)
|
|
|
|
|
|
- # make a new name for the new Excellon object (the one with edited content)
|
|
|
- self.edited_obj_name = self.gerber_obj.options['name']
|
|
|
- self.name_entry.set_value(self.edited_obj_name)
|
|
|
+ # start with GRID toolbar activated
|
|
|
+ if self.app.ui.grid_snap_btn.isChecked() is False:
|
|
|
+ self.app.ui.grid_snap_btn.trigger()
|
|
|
|
|
|
- self.apertures_row = 0
|
|
|
- # aper_no = self.apertures_row + 1
|
|
|
+ # adjust the visibility of some of the canvas context menu
|
|
|
+ self.app.ui.popmenu_edit.setVisible(False)
|
|
|
+ self.app.ui.popmenu_save.setVisible(True)
|
|
|
|
|
|
- sort = []
|
|
|
- for k, v in list(self.storage_dict.items()):
|
|
|
- sort.append(int(k))
|
|
|
+ self.app.ui.popmenu_disable.setVisible(False)
|
|
|
+ self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
|
|
|
+ self.app.ui.popmenu_properties.setVisible(False)
|
|
|
+ self.app.ui.grb_editor_cmenu.menuAction().setVisible(True)
|
|
|
|
|
|
- sorted_apertures = sorted(sort)
|
|
|
+ def deactivate_grb_editor(self):
|
|
|
+ try:
|
|
|
+ QtGui.QGuiApplication.restoreOverrideCursor()
|
|
|
+ except Exception as e:
|
|
|
+ log.debug("AppGerberEditor.deactivate_grb_editor() --> %s" % str(e))
|
|
|
|
|
|
- # sort = []
|
|
|
- # for k, v in list(self.gerber_obj.aperture_macros.items()):
|
|
|
- # sort.append(k)
|
|
|
- # sorted_macros = sorted(sort)
|
|
|
+ self.clear()
|
|
|
|
|
|
- # n = len(sorted_apertures) + len(sorted_macros)
|
|
|
- n = len(sorted_apertures)
|
|
|
- self.apertures_table.setRowCount(n)
|
|
|
+ # adjust the status of the menu entries related to the editor
|
|
|
+ self.app.ui.menueditedit.setDisabled(False)
|
|
|
+ self.app.ui.menueditok.setDisabled(True)
|
|
|
+ # adjust the visibility of some of the canvas context menu
|
|
|
+ self.app.ui.popmenu_edit.setVisible(True)
|
|
|
+ self.app.ui.popmenu_save.setVisible(False)
|
|
|
|
|
|
- for ap_code in sorted_apertures:
|
|
|
- ap_code = str(ap_code)
|
|
|
+ self.disconnect_canvas_event_handlers()
|
|
|
+ self.app.ui.grb_edit_toolbar.setDisabled(True)
|
|
|
|
|
|
- ap_code_item = QtWidgets.QTableWidgetItem('%d' % int(self.apertures_row + 1))
|
|
|
- ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
|
|
- self.apertures_table.setItem(self.apertures_row, 0, ap_code_item) # Tool name/id
|
|
|
+ self.app.ui.corner_snap_btn.setVisible(False)
|
|
|
+ self.app.ui.snap_magnet.setVisible(False)
|
|
|
|
|
|
- ap_code_item = QtWidgets.QTableWidgetItem(ap_code)
|
|
|
- ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
|
|
+ # set the Editor Toolbar visibility to what was before entering in the Editor
|
|
|
+ self.app.ui.grb_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
|
|
|
+ else self.app.ui.grb_edit_toolbar.setVisible(True)
|
|
|
|
|
|
- ap_type_item = QtWidgets.QTableWidgetItem(str(self.storage_dict[ap_code]['type']))
|
|
|
- ap_type_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
|
|
+ # Disable visuals
|
|
|
+ self.shapes.enabled = False
|
|
|
+ self.tool_shape.enabled = False
|
|
|
+ # self.app.app_cursor.enabled = False
|
|
|
|
|
|
- if str(self.storage_dict[ap_code]['type']) == 'R' or str(self.storage_dict[ap_code]['type']) == 'O':
|
|
|
- ap_dim_item = QtWidgets.QTableWidgetItem(
|
|
|
- '%.*f, %.*f' % (self.decimals, self.storage_dict[ap_code]['width'],
|
|
|
- self.decimals, self.storage_dict[ap_code]['height']
|
|
|
- )
|
|
|
- )
|
|
|
- ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
|
|
|
- elif str(self.storage_dict[ap_code]['type']) == 'P':
|
|
|
- ap_dim_item = QtWidgets.QTableWidgetItem(
|
|
|
- '%.*f, %.*f' % (self.decimals, self.storage_dict[ap_code]['diam'],
|
|
|
- self.decimals, self.storage_dict[ap_code]['nVertices'])
|
|
|
- )
|
|
|
- ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
|
|
|
- else:
|
|
|
- ap_dim_item = QtWidgets.QTableWidgetItem('')
|
|
|
- ap_dim_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
|
|
+ self.app.ui.grb_editor_menu.setDisabled(True)
|
|
|
+ self.app.ui.grb_editor_menu.menuAction().setVisible(False)
|
|
|
|
|
|
- try:
|
|
|
- if self.storage_dict[ap_code]['size'] is not None:
|
|
|
- ap_size_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals,
|
|
|
- float(self.storage_dict[ap_code]['size'])))
|
|
|
- else:
|
|
|
- ap_size_item = QtWidgets.QTableWidgetItem('')
|
|
|
- except KeyError:
|
|
|
- ap_size_item = QtWidgets.QTableWidgetItem('')
|
|
|
+ self.app.ui.update_obj_btn.setEnabled(False)
|
|
|
|
|
|
- if str(self.storage_dict[ap_code]['type']) == 'C':
|
|
|
- ap_size_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
|
|
|
- else:
|
|
|
- ap_size_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
|
|
+ # adjust the visibility of some of the canvas context menu
|
|
|
+ self.app.ui.popmenu_edit.setVisible(True)
|
|
|
+ self.app.ui.popmenu_save.setVisible(False)
|
|
|
|
|
|
- self.apertures_table.setItem(self.apertures_row, 1, ap_code_item) # Aperture Code
|
|
|
- self.apertures_table.setItem(self.apertures_row, 2, ap_type_item) # Aperture Type
|
|
|
- self.apertures_table.setItem(self.apertures_row, 3, ap_size_item) # Aperture Size
|
|
|
- self.apertures_table.setItem(self.apertures_row, 4, ap_dim_item) # Aperture Dimensions
|
|
|
+ self.app.ui.popmenu_disable.setVisible(True)
|
|
|
+ self.app.ui.cmenu_newmenu.menuAction().setVisible(True)
|
|
|
+ self.app.ui.popmenu_properties.setVisible(True)
|
|
|
+ self.app.ui.g_editor_cmenu.menuAction().setVisible(False)
|
|
|
+ self.app.ui.e_editor_cmenu.menuAction().setVisible(False)
|
|
|
+ self.app.ui.grb_editor_cmenu.menuAction().setVisible(False)
|
|
|
|
|
|
- self.apertures_row += 1
|
|
|
- if first_run is True:
|
|
|
- # set now the last aperture selected
|
|
|
- self.last_aperture_selected = ap_code
|
|
|
+ # Show original geometry
|
|
|
+ if self.gerber_obj:
|
|
|
+ self.gerber_obj.visible = True
|
|
|
|
|
|
- # for ap_code in sorted_macros:
|
|
|
- # ap_code = str(ap_code)
|
|
|
- #
|
|
|
- # ap_code_item = QtWidgets.QTableWidgetItem('%d' % int(self.apertures_row + 1))
|
|
|
- # ap_code_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
|
|
- # self.apertures_table.setItem(self.apertures_row, 0, ap_code_item) # Tool name/id
|
|
|
- #
|
|
|
- # ap_code_item = QtWidgets.QTableWidgetItem(ap_code)
|
|
|
- #
|
|
|
- # ap_type_item = QtWidgets.QTableWidgetItem('AM')
|
|
|
- # ap_type_item.setFlags(QtCore.Qt.ItemIsEnabled)
|
|
|
- #
|
|
|
- # self.apertures_table.setItem(self.apertures_row, 1, ap_code_item) # Aperture Code
|
|
|
- # self.apertures_table.setItem(self.apertures_row, 2, ap_type_item) # Aperture Type
|
|
|
- #
|
|
|
- # self.apertures_row += 1
|
|
|
- # if first_run is True:
|
|
|
- # # set now the last aperture selected
|
|
|
- # self.last_aperture_selected = ap_code
|
|
|
+ def connect_canvas_event_handlers(self):
|
|
|
+ # Canvas events
|
|
|
|
|
|
- self.apertures_table.selectColumn(0)
|
|
|
- self.apertures_table.resizeColumnsToContents()
|
|
|
- self.apertures_table.resizeRowsToContents()
|
|
|
+ # make sure that the shortcuts key and mouse events will no longer be linked to the methods from FlatCAMApp
|
|
|
+ # but those from AppGeoEditor
|
|
|
|
|
|
- vertical_header = self.apertures_table.verticalHeader()
|
|
|
- # vertical_header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
|
|
|
- vertical_header.hide()
|
|
|
- self.apertures_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
|
|
+ # first connect to new, then disconnect the old handlers
|
|
|
+ # don't ask why but if there is nothing connected I've seen issues
|
|
|
+ self.mp = self.canvas.graph_event_connect('mouse_press', self.on_canvas_click)
|
|
|
+ self.mm = self.canvas.graph_event_connect('mouse_move', self.on_canvas_move)
|
|
|
+ self.mr = self.canvas.graph_event_connect('mouse_release', self.on_grb_click_release)
|
|
|
|
|
|
- horizontal_header = self.apertures_table.horizontalHeader()
|
|
|
- horizontal_header.setMinimumSectionSize(10)
|
|
|
- horizontal_header.setDefaultSectionSize(70)
|
|
|
- horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
|
|
|
- horizontal_header.resizeSection(0, 27)
|
|
|
- horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
|
|
|
- horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
|
|
|
- horizontal_header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents)
|
|
|
- horizontal_header.setSectionResizeMode(4, QtWidgets.QHeaderView.Stretch)
|
|
|
+ if self.app.is_legacy is False:
|
|
|
+ self.canvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
|
|
|
+ self.canvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
|
|
|
+ self.canvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
|
|
|
+ self.canvas.graph_event_disconnect('mouse_double_click', self.app.on_mouse_double_click_over_plot)
|
|
|
+ else:
|
|
|
+ self.canvas.graph_event_disconnect(self.app.mp)
|
|
|
+ self.canvas.graph_event_disconnect(self.app.mm)
|
|
|
+ self.canvas.graph_event_disconnect(self.app.mr)
|
|
|
+ self.canvas.graph_event_disconnect(self.app.mdc)
|
|
|
|
|
|
- self.apertures_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
|
|
- self.apertures_table.setSortingEnabled(False)
|
|
|
- self.apertures_table.setMinimumHeight(self.apertures_table.getHeight())
|
|
|
- self.apertures_table.setMaximumHeight(self.apertures_table.getHeight())
|
|
|
+ self.app.collection.view.clicked.disconnect()
|
|
|
|
|
|
- # make sure no rows are selected so the user have to click the correct row, meaning selecting the correct tool
|
|
|
- self.apertures_table.clearSelection()
|
|
|
+ self.app.ui.popmenu_copy.triggered.disconnect()
|
|
|
+ self.app.ui.popmenu_delete.triggered.disconnect()
|
|
|
+ self.app.ui.popmenu_move.triggered.disconnect()
|
|
|
|
|
|
- # Remove anything else in the GUI Properties Tab
|
|
|
- self.app.ui.properties_scroll_area.takeWidget()
|
|
|
- # Put ourselves in the GUI Properties Tab
|
|
|
- self.app.ui.properties_scroll_area.setWidget(self.grb_edit_widget)
|
|
|
- # Switch notebook to Properties page
|
|
|
- self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
|
|
|
+ self.app.ui.popmenu_copy.triggered.connect(self.on_copy_button)
|
|
|
+ self.app.ui.popmenu_delete.triggered.connect(self.on_delete_btn)
|
|
|
+ self.app.ui.popmenu_move.triggered.connect(self.on_move_button)
|
|
|
|
|
|
- # we reactivate the signals after the after the tool adding as we don't need to see the tool been populated
|
|
|
- self.apertures_table.itemChanged.connect(self.on_tool_edit)
|
|
|
- self.apertures_table.cellPressed.connect(self.on_row_selected)
|
|
|
+ # Gerber Editor
|
|
|
+ self.app.ui.grb_draw_pad.triggered.connect(self.on_pad_add)
|
|
|
+ self.app.ui.grb_draw_pad_array.triggered.connect(self.on_pad_add_array)
|
|
|
+ self.app.ui.grb_draw_track.triggered.connect(self.on_track_add)
|
|
|
+ self.app.ui.grb_draw_region.triggered.connect(self.on_region_add)
|
|
|
|
|
|
- # for convenience set the next aperture code in the apcode field
|
|
|
- try:
|
|
|
- self.apcode_entry.set_value(max(self.tid2apcode.values()) + 1)
|
|
|
- except ValueError:
|
|
|
- # this means that the edited object has no apertures so we start with 10 (Gerber specifications)
|
|
|
- self.apcode_entry.set_value(self.app.defaults["gerber_editor_newcode"])
|
|
|
+ self.app.ui.grb_draw_poligonize.triggered.connect(self.on_poligonize)
|
|
|
+ self.app.ui.grb_draw_semidisc.triggered.connect(self.on_add_semidisc)
|
|
|
+ self.app.ui.grb_draw_disc.triggered.connect(self.on_disc_add)
|
|
|
+ self.app.ui.grb_draw_buffer.triggered.connect(lambda: self.select_tool("buffer"))
|
|
|
+ self.app.ui.grb_draw_scale.triggered.connect(lambda: self.select_tool("scale"))
|
|
|
+ self.app.ui.grb_draw_markarea.triggered.connect(lambda: self.select_tool("markarea"))
|
|
|
+ self.app.ui.grb_draw_eraser.triggered.connect(self.on_eraser)
|
|
|
+ self.app.ui.grb_draw_transformations.triggered.connect(self.on_transform)
|
|
|
|
|
|
- def on_aperture_add(self, apcode=None):
|
|
|
- self.is_modified = True
|
|
|
- if apcode:
|
|
|
- ap_code = apcode
|
|
|
+ def disconnect_canvas_event_handlers(self):
|
|
|
+
|
|
|
+ # we restore the key and mouse control to FlatCAMApp method
|
|
|
+ # first connect to new, then disconnect the old handlers
|
|
|
+ # don't ask why but if there is nothing connected I've seen issues
|
|
|
+ self.app.mp = self.canvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
|
|
|
+ self.app.mm = self.canvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
|
|
|
+ self.app.mr = self.canvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
|
|
|
+ self.app.mdc = self.canvas.graph_event_connect('mouse_double_click', self.app.on_mouse_double_click_over_plot)
|
|
|
+ self.app.collection.view.clicked.connect(self.app.collection.on_mouse_down)
|
|
|
+
|
|
|
+ if self.app.is_legacy is False:
|
|
|
+ self.canvas.graph_event_disconnect('mouse_press', self.on_canvas_click)
|
|
|
+ self.canvas.graph_event_disconnect('mouse_move', self.on_canvas_move)
|
|
|
+ self.canvas.graph_event_disconnect('mouse_release', self.on_grb_click_release)
|
|
|
else:
|
|
|
- try:
|
|
|
- ap_code = str(self.apcode_entry.get_value())
|
|
|
- except ValueError:
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
- _("Aperture code value is missing or wrong format. Add it and retry."))
|
|
|
- return
|
|
|
- if ap_code == '':
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
- _("Aperture code value is missing or wrong format. Add it and retry."))
|
|
|
- return
|
|
|
+ self.canvas.graph_event_disconnect(self.mp)
|
|
|
+ self.canvas.graph_event_disconnect(self.mm)
|
|
|
+ self.canvas.graph_event_disconnect(self.mr)
|
|
|
|
|
|
- if ap_code == '0':
|
|
|
- if ap_code not in self.tid2apcode:
|
|
|
- self.storage_dict[ap_code] = {}
|
|
|
- self.storage_dict[ap_code]['type'] = 'REG'
|
|
|
- size_val = 0
|
|
|
- self.apsize_entry.set_value(size_val)
|
|
|
- self.storage_dict[ap_code]['size'] = size_val
|
|
|
+ try:
|
|
|
+ self.app.ui.popmenu_copy.triggered.disconnect(self.on_copy_button)
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
|
|
|
- self.storage_dict[ap_code]['geometry'] = []
|
|
|
+ try:
|
|
|
+ self.app.ui.popmenu_delete.triggered.disconnect(self.on_delete_btn)
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
|
|
|
- # self.oldapcode_newapcode dict keeps the evidence on current aperture codes as keys and
|
|
|
- # gets updated on values each time a aperture code is edited or added
|
|
|
- self.oldapcode_newapcode[ap_code] = ap_code
|
|
|
- else:
|
|
|
- if ap_code not in self.oldapcode_newapcode:
|
|
|
- self.storage_dict[ap_code] = {}
|
|
|
+ try:
|
|
|
+ self.app.ui.popmenu_move.triggered.disconnect(self.on_move_button)
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
|
|
|
- type_val = self.aptype_cb.currentText()
|
|
|
- self.storage_dict[ap_code]['type'] = type_val
|
|
|
+ self.app.ui.popmenu_copy.triggered.connect(self.app.on_copy_command)
|
|
|
+ self.app.ui.popmenu_delete.triggered.connect(self.app.on_delete)
|
|
|
+ self.app.ui.popmenu_move.triggered.connect(self.app.obj_move)
|
|
|
|
|
|
- if type_val == 'R' or type_val == 'O':
|
|
|
- try:
|
|
|
- dims = self.apdim_entry.get_value()
|
|
|
- self.storage_dict[ap_code]['width'] = dims[0]
|
|
|
- self.storage_dict[ap_code]['height'] = dims[1]
|
|
|
+ # Gerber Editor
|
|
|
|
|
|
- size_val = np.sqrt((dims[0] ** 2) + (dims[1] ** 2))
|
|
|
- self.apsize_entry.set_value(size_val)
|
|
|
+ try:
|
|
|
+ self.app.ui.grb_draw_pad.triggered.disconnect(self.on_pad_add)
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
|
|
|
- except Exception as e:
|
|
|
- log.error("AppGerberEditor.on_aperture_add() --> the R or O aperture dims has to be in a "
|
|
|
- "tuple format (x,y)\nError: %s" % str(e))
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
- _("Aperture dimensions value is missing or wrong format. "
|
|
|
- "Add it in format (width, height) and retry."))
|
|
|
- return
|
|
|
- else:
|
|
|
- try:
|
|
|
- size_val = float(self.apsize_entry.get_value())
|
|
|
- except ValueError:
|
|
|
- # try to convert comma to decimal point. if it's still not working error message and return
|
|
|
- try:
|
|
|
- size_val = float(self.apsize_entry.get_value().replace(',', '.'))
|
|
|
- self.apsize_entry.set_value(size_val)
|
|
|
- except ValueError:
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
- _("Aperture size value is missing or wrong format. Add it and retry."))
|
|
|
- return
|
|
|
- self.storage_dict[ap_code]['size'] = size_val
|
|
|
+ try:
|
|
|
+ self.app.ui.grb_draw_pad_array.triggered.disconnect(self.on_pad_add_array)
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
|
|
|
- self.storage_dict[ap_code]['geometry'] = []
|
|
|
+ try:
|
|
|
+ self.app.ui.grb_draw_track.triggered.disconnect(self.on_track_add)
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
|
|
|
- # self.oldapcode_newapcode dict keeps the evidence on current aperture codes as keys and gets updated on
|
|
|
- # values each time a aperture code is edited or added
|
|
|
- self.oldapcode_newapcode[ap_code] = ap_code
|
|
|
- else:
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
- _("Aperture already in the aperture table."))
|
|
|
- return
|
|
|
+ try:
|
|
|
+ self.app.ui.grb_draw_region.triggered.disconnect(self.on_region_add)
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
|
|
|
- # since we add a new tool, we update also the initial state of the tool_table through it's dictionary
|
|
|
- # we add a new entry in the tid2apcode dict
|
|
|
- self.tid2apcode[len(self.oldapcode_newapcode)] = int(ap_code)
|
|
|
+ try:
|
|
|
+ self.app.ui.grb_draw_poligonize.triggered.disconnect(self.on_poligonize)
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
+ try:
|
|
|
+ self.app.ui.grb_draw_semidisc.triggered.diconnect(self.on_add_semidisc)
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
+ try:
|
|
|
+ self.app.ui.grb_draw_disc.triggered.disconnect(self.on_disc_add)
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
+ try:
|
|
|
+ self.app.ui.grb_draw_buffer.triggered.disconnect()
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
+ try:
|
|
|
+ self.app.ui.grb_draw_scale.triggered.disconnect()
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
+ try:
|
|
|
+ self.app.ui.grb_draw_markarea.triggered.disconnect()
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
+ try:
|
|
|
+ self.app.ui.grb_draw_eraser.triggered.disconnect(self.on_eraser)
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
+ try:
|
|
|
+ self.app.ui.grb_draw_transformations.triggered.disconnect(self.on_transform)
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
|
|
|
- self.app.inform.emit('[success] %s: %s' % (_("Added new aperture with code"), str(ap_code)))
|
|
|
+ try:
|
|
|
+ self.app.jump_signal.disconnect()
|
|
|
+ except (TypeError, AttributeError):
|
|
|
+ pass
|
|
|
|
|
|
- self.build_ui()
|
|
|
+ def clear(self):
|
|
|
+ self.thread.quit()
|
|
|
|
|
|
- self.last_aperture_selected = ap_code
|
|
|
+ self.active_tool = None
|
|
|
+ self.selected = []
|
|
|
+ self.storage_dict.clear()
|
|
|
+ self.results.clear()
|
|
|
|
|
|
- # make a quick sort through the tid2apcode dict so we find which row to select
|
|
|
- row_to_be_selected = None
|
|
|
- for key in sorted(self.tid2apcode):
|
|
|
- if self.tid2apcode[key] == int(ap_code):
|
|
|
- row_to_be_selected = int(key) - 1
|
|
|
- break
|
|
|
- self.apertures_table.selectRow(row_to_be_selected)
|
|
|
+ self.shapes.clear(update=True)
|
|
|
+ self.tool_shape.clear(update=True)
|
|
|
+ self.ma_annotation.clear(update=True)
|
|
|
|
|
|
- def on_aperture_delete(self, ap_code=None):
|
|
|
+ def edit_fcgerber(self, orig_grb_obj):
|
|
|
"""
|
|
|
- Called for aperture deletion.
|
|
|
+ Imports the geometry found in self.apertures from the given FlatCAM Gerber object
|
|
|
+ into the editor.
|
|
|
|
|
|
- :param ap_code: An Aperture code; String
|
|
|
- :return:
|
|
|
+ :param orig_grb_obj: ExcellonObject
|
|
|
+ :return: None
|
|
|
"""
|
|
|
- self.is_modified = True
|
|
|
-
|
|
|
- try:
|
|
|
- if ap_code:
|
|
|
- try:
|
|
|
- deleted_apcode_list = [dd for dd in ap_code]
|
|
|
- except TypeError:
|
|
|
- deleted_apcode_list = [ap_code]
|
|
|
- else:
|
|
|
- # deleted_tool_dia = float(self.apertures_table.item(self.apertures_table.currentRow(), 1).text())
|
|
|
- if len(self.apertures_table.selectionModel().selectedRows()) == 0:
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' % _(" Select an aperture in Aperture Table"))
|
|
|
- return
|
|
|
|
|
|
- deleted_apcode_list = []
|
|
|
- for index in self.apertures_table.selectionModel().selectedRows():
|
|
|
- row = index.row()
|
|
|
- deleted_apcode_list.append(self.apertures_table.item(row, 1).text())
|
|
|
- except Exception as exc:
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Select an aperture in Aperture Table -->", str(exc))))
|
|
|
- return
|
|
|
+ self.deactivate_grb_editor()
|
|
|
+ self.activate_grb_editor()
|
|
|
|
|
|
- if deleted_apcode_list:
|
|
|
- for deleted_aperture in deleted_apcode_list:
|
|
|
- # delete the storage used for that tool
|
|
|
- self.storage_dict.pop(deleted_aperture, None)
|
|
|
+ # reset the tool table
|
|
|
+ self.ui.apertures_table.clear()
|
|
|
|
|
|
- for deleted_tool in list(self.tid2apcode.keys()):
|
|
|
- if self.tid2apcode[deleted_tool] == deleted_aperture:
|
|
|
- # delete the tool
|
|
|
- self.tid2apcode.pop(deleted_tool, None)
|
|
|
+ self.ui.apertures_table.setHorizontalHeaderLabels(['#', _('Code'), _('Type'), _('Size'), _('Dim')])
|
|
|
+ self.last_aperture_selected = None
|
|
|
|
|
|
- self.oldapcode_newapcode.pop(deleted_aperture, None)
|
|
|
- self.app.inform.emit('[success] %s: %s' % (_("Deleted aperture with code"), str(deleted_aperture)))
|
|
|
+ # create a reference to the source object
|
|
|
+ self.gerber_obj = orig_grb_obj
|
|
|
+ self.gerber_obj_options = orig_grb_obj.options
|
|
|
|
|
|
- self.plot_all()
|
|
|
- self.build_ui()
|
|
|
+ file_units = self.gerber_obj.units if self.gerber_obj.units else 'IN'
|
|
|
+ app_units = self.app.defaults['units']
|
|
|
+ # self.conversion_factor = 25.4 if file_units == 'IN' else (1 / 25.4) if file_units != app_units else 1
|
|
|
|
|
|
- # if last aperture selected was in the apertures deleted than make sure to select a
|
|
|
- # 'new' last aperture selected because there are tools who depend on it.
|
|
|
- # if there is no aperture left, then add a default one :)
|
|
|
- if self.last_aperture_selected in deleted_apcode_list:
|
|
|
- if self.apertures_table.rowCount() == 0:
|
|
|
- self.on_aperture_add('10')
|
|
|
- self.last_aperture_selected = '10'
|
|
|
+ if file_units == app_units:
|
|
|
+ self.conversion_factor = 1
|
|
|
+ else:
|
|
|
+ if file_units == 'IN':
|
|
|
+ self.conversion_factor = 25.4
|
|
|
else:
|
|
|
- self.last_aperture_selected = self.apertures_table.item(0, 1).text()
|
|
|
+ self.conversion_factor = 0.0393700787401575
|
|
|
|
|
|
- def on_tool_edit(self):
|
|
|
- if self.apertures_table.currentItem() is None:
|
|
|
- return
|
|
|
+ # Hide original geometry
|
|
|
+ orig_grb_obj.visible = False
|
|
|
|
|
|
- # if connected, disconnect the signal from the slot on item_changed as it creates issues
|
|
|
- self.apertures_table.itemChanged.disconnect()
|
|
|
- # self.apertures_table.cellPressed.disconnect()
|
|
|
+ # Set selection tolerance
|
|
|
+ # DrawToolShape.tolerance = fc_excellon.drawing_tolerance * 10
|
|
|
|
|
|
- self.is_modified = True
|
|
|
- val_edited = None
|
|
|
-
|
|
|
- row_of_item_changed = self.apertures_table.currentRow()
|
|
|
- col_of_item_changed = self.apertures_table.currentColumn()
|
|
|
+ self.select_tool("select")
|
|
|
|
|
|
- # rows start with 0, tools start with 1 so we adjust the value by 1
|
|
|
- key_in_tid2apcode = row_of_item_changed + 1
|
|
|
- ap_code_old = str(self.tid2apcode[key_in_tid2apcode])
|
|
|
+ try:
|
|
|
+ # we activate this after the initial build as we don't need to see the tool been populated
|
|
|
+ self.ui.apertures_table.itemChanged.connect(self.on_tool_edit)
|
|
|
+ except Exception as e:
|
|
|
+ log.debug("AppGerberEditor.edit_fcgerber() --> %s" % str(e))
|
|
|
|
|
|
- ap_code_new = self.apertures_table.item(row_of_item_changed, 1).text()
|
|
|
+ # apply the conversion factor on the obj.apertures
|
|
|
+ conv_apertures = deepcopy(self.gerber_obj.apertures)
|
|
|
+ for apcode in self.gerber_obj.apertures:
|
|
|
+ for key in self.gerber_obj.apertures[apcode]:
|
|
|
+ if key == 'width':
|
|
|
+ conv_apertures[apcode]['width'] = self.gerber_obj.apertures[apcode]['width'] * \
|
|
|
+ self.conversion_factor
|
|
|
+ elif key == 'height':
|
|
|
+ conv_apertures[apcode]['height'] = self.gerber_obj.apertures[apcode]['height'] * \
|
|
|
+ self.conversion_factor
|
|
|
+ elif key == 'diam':
|
|
|
+ conv_apertures[apcode]['diam'] = self.gerber_obj.apertures[apcode]['diam'] * self.conversion_factor
|
|
|
+ elif key == 'size':
|
|
|
+ conv_apertures[apcode]['size'] = self.gerber_obj.apertures[apcode]['size'] * self.conversion_factor
|
|
|
+ else:
|
|
|
+ conv_apertures[apcode][key] = self.gerber_obj.apertures[apcode][key]
|
|
|
|
|
|
- if col_of_item_changed == 1:
|
|
|
- # we edited the Aperture Code column (int)
|
|
|
- try:
|
|
|
- val_edited = int(self.apertures_table.currentItem().text())
|
|
|
- except ValueError as e:
|
|
|
- log.debug("AppGerberEditor.on_tool_edit() --> %s" % str(e))
|
|
|
- # self.apertures_table.setCurrentItem(None)
|
|
|
- # we reactivate the signals after the after the tool editing
|
|
|
- self.apertures_table.itemChanged.connect(self.on_tool_edit)
|
|
|
- return
|
|
|
- elif col_of_item_changed == 3:
|
|
|
- # we edited the Size column (float)
|
|
|
- try:
|
|
|
- val_edited = float(self.apertures_table.currentItem().text())
|
|
|
- except ValueError as e:
|
|
|
- log.debug("AppGerberEditor.on_tool_edit() --> %s" % str(e))
|
|
|
- # self.apertures_table.setCurrentItem(None)
|
|
|
- # we reactivate the signals after the after the tool editing
|
|
|
- self.apertures_table.itemChanged.connect(self.on_tool_edit)
|
|
|
- return
|
|
|
- elif col_of_item_changed == 4:
|
|
|
- # we edit the Dimensions column (tuple)
|
|
|
- try:
|
|
|
- val_edited = [
|
|
|
- float(x.strip()) for x in self.apertures_table.currentItem().text().split(",") if x != ''
|
|
|
- ]
|
|
|
- except ValueError as e:
|
|
|
- log.debug("AppGerberEditor.on_tool_edit() --> %s" % str(e))
|
|
|
- # we reactivate the signals after the after the tool editing
|
|
|
- self.apertures_table.itemChanged.connect(self.on_tool_edit)
|
|
|
- return
|
|
|
+ self.gerber_obj.apertures = conv_apertures
|
|
|
+ self.gerber_obj.units = app_units
|
|
|
|
|
|
- if len(val_edited) != 2:
|
|
|
- self.app.inform.emit("[WARNING_NOTCL] %s" % _("Dimensions need two float values separated by comma."))
|
|
|
- old_dims_txt = '%s, %s' % (str(self.storage_dict[ap_code_new]['width']),
|
|
|
- str(self.storage_dict[ap_code_new]['height']))
|
|
|
+ # # and then add it to the storage elements (each storage elements is a member of a list
|
|
|
+ # def job_thread(aperture_id):
|
|
|
+ # with self.app.proc_container.new('%s: %s ...' %
|
|
|
+ # (_("Adding geometry for aperture"), str(aperture_id))):
|
|
|
+ # storage_elem = []
|
|
|
+ # self.storage_dict[aperture_id] = {}
|
|
|
+ #
|
|
|
+ # # add the Gerber geometry to editor storage
|
|
|
+ # for k, v in self.gerber_obj.apertures[aperture_id].items():
|
|
|
+ # try:
|
|
|
+ # if k == 'geometry':
|
|
|
+ # for geo_el in v:
|
|
|
+ # if geo_el:
|
|
|
+ # self.add_gerber_shape(DrawToolShape(geo_el), storage_elem)
|
|
|
+ # self.storage_dict[aperture_id][k] = storage_elem
|
|
|
+ # else:
|
|
|
+ # self.storage_dict[aperture_id][k] = self.gerber_obj.apertures[aperture_id][k]
|
|
|
+ # except Exception as e:
|
|
|
+ # log.debug("AppGerberEditor.edit_fcgerber().job_thread() --> %s" % str(e))
|
|
|
+ #
|
|
|
+ # # Check promises and clear if exists
|
|
|
+ # while True:
|
|
|
+ # try:
|
|
|
+ # self.grb_plot_promises.remove(aperture_id)
|
|
|
+ # time.sleep(0.5)
|
|
|
+ # except ValueError:
|
|
|
+ # break
|
|
|
+ #
|
|
|
+ # # we create a job work each aperture, job that work in a threaded way to store the geometry in local storage
|
|
|
+ # # as DrawToolShapes
|
|
|
+ # for ap_code in self.gerber_obj.apertures:
|
|
|
+ # self.grb_plot_promises.append(ap_code)
|
|
|
+ # self.app.worker_task.emit({'fcn': job_thread, 'params': [ap_code]})
|
|
|
+ #
|
|
|
+ # self.set_ui()
|
|
|
+ #
|
|
|
+ # # do the delayed plot only if there is something to plot (the gerber is not empty)
|
|
|
+ # try:
|
|
|
+ # if bool(self.gerber_obj.apertures):
|
|
|
+ # self.start_delayed_plot(check_period=1000)
|
|
|
+ # else:
|
|
|
+ # raise AttributeError
|
|
|
+ # except AttributeError:
|
|
|
+ # # now that we have data (empty data actually), create the GUI interface and add it to the Tool Tab
|
|
|
+ # self.build_ui(first_run=True)
|
|
|
+ # # and add the first aperture to have something to play with
|
|
|
+ # self.on_aperture_add('10')
|
|
|
|
|
|
- self.apertures_table.currentItem().setText(old_dims_txt)
|
|
|
- # we reactivate the signals after the after the tool editing
|
|
|
- self.apertures_table.itemChanged.connect(self.on_tool_edit)
|
|
|
- return
|
|
|
- else:
|
|
|
- self.app.inform.emit("[success] %s" % _("Dimensions edited."))
|
|
|
+ # self.app.worker_task.emit({'fcn': worker_job, 'params': [self]})
|
|
|
|
|
|
- # In case we edited the Aperture Code therefore the val_edited holds a new Aperture Code
|
|
|
- # TODO Edit of the Aperture Code is not active yet
|
|
|
- if col_of_item_changed == 1:
|
|
|
- # aperture code is not used so we create a new Aperture with the desired Aperture Code
|
|
|
- if val_edited not in self.oldapcode_newapcode.values():
|
|
|
- # update the dict that holds as keys old Aperture Codes and as values the new Aperture Codes
|
|
|
- self.oldapcode_newapcode[ap_code_old] = val_edited
|
|
|
- # update the dict that holds tool_no as key and tool_dia as value
|
|
|
- self.tid2apcode[key_in_tid2apcode] = val_edited
|
|
|
+ class Execute_Edit(QtCore.QObject):
|
|
|
|
|
|
- old_aperture_val = self.storage_dict.pop(ap_code_old)
|
|
|
- self.storage_dict[val_edited] = old_aperture_val
|
|
|
+ start = QtCore.pyqtSignal(str)
|
|
|
|
|
|
- else:
|
|
|
- # aperture code is already in use so we move the pads from the prior tool to the new tool
|
|
|
- # but only if they are of the same type
|
|
|
+ def __init__(self, app):
|
|
|
+ super(Execute_Edit, self).__init__()
|
|
|
+ self.app = app
|
|
|
+ self.start.connect(self.run)
|
|
|
|
|
|
- if self.storage_dict[ap_code_old]['type'] == self.storage_dict[ap_code_new]['type']:
|
|
|
- # TODO I have to work here; if type == 'R' or 'O' have t otake care of all attributes ...
|
|
|
- factor = val_edited / float(ap_code_old)
|
|
|
- geometry = []
|
|
|
- for geo_el in self.storage_dict[ap_code_old]:
|
|
|
- geometric_data = geo_el.geo
|
|
|
- new_geo_el = {}
|
|
|
- if 'solid' in geometric_data:
|
|
|
- new_geo_el['solid'] = deepcopy(affinity.scale(geometric_data['solid'],
|
|
|
- xfact=factor, yfact=factor))
|
|
|
- if 'follow' in geometric_data:
|
|
|
- new_geo_el['follow'] = deepcopy(affinity.scale(geometric_data['follow'],
|
|
|
- xfact=factor, yfact=factor))
|
|
|
- if 'clear' in geometric_data:
|
|
|
- new_geo_el['clear'] = deepcopy(affinity.scale(geometric_data['clear'],
|
|
|
- xfact=factor, yfact=factor))
|
|
|
- geometry.append(new_geo_el)
|
|
|
+ @staticmethod
|
|
|
+ def worker_job(app_obj):
|
|
|
+ with app_obj.app.proc_container.new('%s ...' % _("Loading")):
|
|
|
+ # ###############################################################
|
|
|
+ # APPLY CLEAR_GEOMETRY on the SOLID_GEOMETRY
|
|
|
+ # ###############################################################
|
|
|
|
|
|
- self.add_gerber_shape(geometry, self.storage_dict[val_edited])
|
|
|
+ # list of clear geos that are to be applied to the entire file
|
|
|
+ global_clear_geo = []
|
|
|
|
|
|
- self.on_aperture_delete(apcode=ap_code_old)
|
|
|
+ # create one big geometry made out of all 'negative' (clear) polygons
|
|
|
+ for aper_id in app_obj.gerber_obj.apertures:
|
|
|
+ # first check if we have any clear_geometry (LPC) and if yes added it to the global_clear_geo
|
|
|
+ if 'geometry' in app_obj.gerber_obj.apertures[aper_id]:
|
|
|
+ for elem in app_obj.gerber_obj.apertures[aper_id]['geometry']:
|
|
|
+ if 'clear' in elem:
|
|
|
+ global_clear_geo.append(elem['clear'])
|
|
|
+ log.warning("Found %d clear polygons." % len(global_clear_geo))
|
|
|
|
|
|
- # In case we edited the Size of the Aperture therefore the val_edited holds the new Aperture Size
|
|
|
- # It will happen only for the Aperture Type == 'C' - I make sure of that in the self.build_ui()
|
|
|
- elif col_of_item_changed == 3:
|
|
|
- old_size = float(self.storage_dict[ap_code_old]['size'])
|
|
|
- new_size = float(val_edited)
|
|
|
- adjust_size = (new_size - old_size) / 2
|
|
|
- geometry = []
|
|
|
- for geo_el in self.storage_dict[ap_code_old]['geometry']:
|
|
|
- g_data = geo_el.geo
|
|
|
- new_geo_el = {}
|
|
|
- if 'solid' in g_data:
|
|
|
- if 'follow' in g_data:
|
|
|
- if isinstance(g_data['follow'], Point):
|
|
|
- new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(adjust_size))
|
|
|
- else:
|
|
|
- new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(adjust_size, join_style=2))
|
|
|
- if 'follow' in g_data:
|
|
|
- new_geo_el['follow'] = deepcopy(g_data['follow'])
|
|
|
- if 'clear' in g_data:
|
|
|
- new_geo_el['clear'] = deepcopy(g_data['clear'].buffer(adjust_size, join_style=2))
|
|
|
- geometry.append(DrawToolShape(new_geo_el))
|
|
|
+ if global_clear_geo:
|
|
|
+ global_clear_geo = unary_union(global_clear_geo)
|
|
|
+ if isinstance(global_clear_geo, Polygon):
|
|
|
+ global_clear_geo = [global_clear_geo]
|
|
|
|
|
|
- self.storage_dict[ap_code_old]['geometry'].clear()
|
|
|
- self.add_gerber_shape(geometry, self.storage_dict[ap_code_old]['geometry'])
|
|
|
- # self.storage_dict[ap_code_old]['geometry'] = geometry
|
|
|
+ # we subtract the big "negative" (clear) geometry from each solid polygon but only the part of
|
|
|
+ # clear geometry that fits inside the solid. otherwise we may loose the solid
|
|
|
+ for ap_code in app_obj.gerber_obj.apertures:
|
|
|
+ temp_solid_geometry = []
|
|
|
+ if 'geometry' in app_obj.gerber_obj.apertures[ap_code]:
|
|
|
+ # for elem in self.gerber_obj.apertures[apcode]['geometry']:
|
|
|
+ # if 'solid' in elem:
|
|
|
+ # solid_geo = elem['solid']
|
|
|
+ # for clear_geo in global_clear_geo:
|
|
|
+ # # Make sure that the clear_geo is within the solid_geo otherwise we loose
|
|
|
+ # # the solid_geometry. We want for clear_geometry just to cut
|
|
|
+ # # into solid_geometry not to delete it
|
|
|
+ # if clear_geo.within(solid_geo):
|
|
|
+ # solid_geo = solid_geo.difference(clear_geo)
|
|
|
+ # try:
|
|
|
+ # for poly in solid_geo:
|
|
|
+ # new_elem = {}
|
|
|
+ #
|
|
|
+ # new_elem['solid'] = poly
|
|
|
+ # if 'clear' in elem:
|
|
|
+ # new_elem['clear'] = poly
|
|
|
+ # if 'follow' in elem:
|
|
|
+ # new_elem['follow'] = poly
|
|
|
+ # temp_elem.append(deepcopy(new_elem))
|
|
|
+ # except TypeError:
|
|
|
+ # new_elem = {}
|
|
|
+ # new_elem['solid'] = solid_geo
|
|
|
+ # if 'clear' in elem:
|
|
|
+ # new_elem['clear'] = solid_geo
|
|
|
+ # if 'follow' in elem:
|
|
|
+ # new_elem['follow'] = solid_geo
|
|
|
+ # temp_elem.append(deepcopy(new_elem))
|
|
|
+ for elem in app_obj.gerber_obj.apertures[ap_code]['geometry']:
|
|
|
+ new_elem = {}
|
|
|
+ if 'solid' in elem:
|
|
|
+ solid_geo = elem['solid']
|
|
|
+ if not global_clear_geo or global_clear_geo.is_empty:
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ for clear_geo in global_clear_geo:
|
|
|
+ # Make sure that the clear_geo is within the solid_geo otherwise we loose
|
|
|
+ # the solid_geometry. We want for clear_geometry just to cut into
|
|
|
+ # solid_geometry not to delete it
|
|
|
+ if clear_geo.within(solid_geo):
|
|
|
+ solid_geo = solid_geo.difference(clear_geo)
|
|
|
|
|
|
- # In case we edited the Dims of the Aperture therefore the val_edited holds a list with the dimensions
|
|
|
- # in the format [width, height]
|
|
|
- # It will happen only for the Aperture Type in ['R', 'O'] - I make sure of that in the self.build_ui()
|
|
|
- # and below
|
|
|
- elif col_of_item_changed == 4:
|
|
|
- if str(self.storage_dict[ap_code_old]['type']) == 'R' or str(self.storage_dict[ap_code_old]['type']) == 'O':
|
|
|
- # use the biggest from them
|
|
|
- buff_val_lines = max(val_edited)
|
|
|
- new_width = val_edited[0]
|
|
|
- new_height = val_edited[1]
|
|
|
+ new_elem['solid'] = solid_geo
|
|
|
+ if 'clear' in elem:
|
|
|
+ new_elem['clear'] = elem['clear']
|
|
|
+ if 'follow' in elem:
|
|
|
+ new_elem['follow'] = elem['follow']
|
|
|
+ temp_solid_geometry.append(deepcopy(new_elem))
|
|
|
|
|
|
- geometry = []
|
|
|
- for geo_el in self.storage_dict[ap_code_old]['geometry']:
|
|
|
- g_data = geo_el.geo
|
|
|
- new_geo_el = {}
|
|
|
- if 'solid' in g_data:
|
|
|
- if 'follow' in g_data:
|
|
|
- if isinstance(g_data['follow'], Point):
|
|
|
- x = g_data['follow'].x
|
|
|
- y = g_data['follow'].y
|
|
|
- minx = x - (new_width / 2)
|
|
|
- miny = y - (new_height / 2)
|
|
|
- maxx = x + (new_width / 2)
|
|
|
- maxy = y + (new_height / 2)
|
|
|
- geo = box(minx=minx, miny=miny, maxx=maxx, maxy=maxy)
|
|
|
- new_geo_el['solid'] = deepcopy(geo)
|
|
|
- else:
|
|
|
- new_geo_el['solid'] = deepcopy(g_data['solid'].buffer(buff_val_lines))
|
|
|
- if 'follow' in g_data:
|
|
|
- new_geo_el['follow'] = deepcopy(g_data['follow'])
|
|
|
- if 'clear' in g_data:
|
|
|
- if 'follow' in g_data:
|
|
|
- if isinstance(g_data['follow'], Point):
|
|
|
- x = g_data['follow'].x
|
|
|
- y = g_data['follow'].y
|
|
|
- minx = x - (new_width / 2)
|
|
|
- miny = y - (new_height / 2)
|
|
|
- maxx = x + (new_width / 2)
|
|
|
- maxy = y + (new_height / 2)
|
|
|
- geo = box(minx=minx, miny=miny, maxx=maxx, maxy=maxy)
|
|
|
- new_geo_el['clear'] = deepcopy(geo)
|
|
|
- else:
|
|
|
- new_geo_el['clear'] = deepcopy(g_data['clear'].buffer(buff_val_lines, join_style=2))
|
|
|
- geometry.append(DrawToolShape(new_geo_el))
|
|
|
+ app_obj.gerber_obj.apertures[ap_code]['geometry'] = deepcopy(temp_solid_geometry)
|
|
|
|
|
|
- self.storage_dict[ap_code_old]['geometry'].clear()
|
|
|
- self.add_gerber_shape(geometry, self.storage_dict[ap_code_old]['geometry'])
|
|
|
+ log.warning("Polygon difference done for %d apertures." % len(app_obj.gerber_obj.apertures))
|
|
|
|
|
|
- self.plot_all()
|
|
|
+ try:
|
|
|
+ # Loading the Geometry into Editor Storage
|
|
|
+ for ap_code, ap_dict in app_obj.gerber_obj.apertures.items():
|
|
|
+ app_obj.results.append(
|
|
|
+ app_obj.pool.apply_async(app_obj.add_apertures, args=(ap_code, ap_dict))
|
|
|
+ )
|
|
|
+ except Exception as ee:
|
|
|
+ log.debug(
|
|
|
+ "AppGerberEditor.edit_fcgerber.worker_job() Adding processes to pool --> %s" % str(ee))
|
|
|
+ traceback.print_exc()
|
|
|
|
|
|
- # we reactivate the signals after the after the tool editing
|
|
|
- self.apertures_table.itemChanged.connect(self.on_tool_edit)
|
|
|
- # self.apertures_table.cellPressed.connect(self.on_row_selected)
|
|
|
+ output = []
|
|
|
+ for p in app_obj.results:
|
|
|
+ output.append(p.get())
|
|
|
|
|
|
- def on_name_activate(self):
|
|
|
- self.edited_obj_name = self.name_entry.get_value()
|
|
|
+ for elem in output:
|
|
|
+ app_obj.storage_dict[elem[0]] = deepcopy(elem[1])
|
|
|
|
|
|
- def on_aptype_changed(self, current_text):
|
|
|
- # 'O' is letter O not zero.
|
|
|
- if current_text == 'R' or current_text == 'O':
|
|
|
- self.apdim_lbl.show()
|
|
|
- self.apdim_entry.show()
|
|
|
- self.apsize_entry.setDisabled(True)
|
|
|
- else:
|
|
|
- self.apdim_lbl.hide()
|
|
|
- self.apdim_entry.hide()
|
|
|
- self.apsize_entry.setDisabled(False)
|
|
|
+ app_obj.mp_finished.emit(output)
|
|
|
|
|
|
- def activate_grb_editor(self):
|
|
|
- # adjust the status of the menu entries related to the editor
|
|
|
- self.app.ui.menueditedit.setDisabled(True)
|
|
|
- self.app.ui.menueditok.setDisabled(False)
|
|
|
- # adjust the visibility of some of the canvas context menu
|
|
|
- self.app.ui.popmenu_edit.setVisible(False)
|
|
|
- self.app.ui.popmenu_save.setVisible(True)
|
|
|
+ def run(self):
|
|
|
+ self.worker_job(self.app)
|
|
|
|
|
|
- self.connect_canvas_event_handlers()
|
|
|
+ # self.thread.start(QtCore.QThread.NormalPriority)
|
|
|
|
|
|
- # init working objects
|
|
|
- self.storage_dict = {}
|
|
|
- self.current_storage = []
|
|
|
- self.sorted_apcode = []
|
|
|
- self.new_apertures = {}
|
|
|
- self.new_aperture_macros = {}
|
|
|
- self.grb_plot_promises = []
|
|
|
- self.oldapcode_newapcode = {}
|
|
|
- self.tid2apcode = {}
|
|
|
+ executable_edit = Execute_Edit(app=self)
|
|
|
+ # executable_edit.moveToThread(self.thread)
|
|
|
+ # executable_edit.start.emit("Started")
|
|
|
|
|
|
- self.shapes.enabled = True
|
|
|
- self.tool_shape.enabled = True
|
|
|
+ self.app.worker_task.emit({'fcn': executable_edit.run, 'params': []})
|
|
|
|
|
|
- self.app.ui.corner_snap_btn.setVisible(True)
|
|
|
- self.app.ui.snap_magnet.setVisible(True)
|
|
|
+ @staticmethod
|
|
|
+ def add_apertures(aperture_id, aperture_dict):
|
|
|
+ storage_elem = []
|
|
|
+ storage_dict = {}
|
|
|
|
|
|
- self.app.ui.grb_editor_menu.setDisabled(False)
|
|
|
- self.app.ui.grb_editor_menu.menuAction().setVisible(True)
|
|
|
+ for k, v in list(aperture_dict.items()):
|
|
|
+ try:
|
|
|
+ if k == 'geometry':
|
|
|
+ for geo_el in v:
|
|
|
+ if geo_el:
|
|
|
+ storage_elem.append(DrawToolShape(geo_el))
|
|
|
+ storage_dict[k] = storage_elem
|
|
|
+ else:
|
|
|
+ storage_dict[k] = aperture_dict[k]
|
|
|
+ except Exception as e:
|
|
|
+ log.debug("AppGerberEditor.edit_fcgerber().job_thread() --> %s" % str(e))
|
|
|
|
|
|
- self.app.ui.update_obj_btn.setEnabled(True)
|
|
|
- self.app.ui.grb_editor_cmenu.setEnabled(True)
|
|
|
+ return [aperture_id, storage_dict]
|
|
|
|
|
|
- self.app.ui.grb_edit_toolbar.setDisabled(False)
|
|
|
- self.app.ui.grb_edit_toolbar.setVisible(True)
|
|
|
- # self.app.ui.grid_toolbar.setDisabled(False)
|
|
|
+ def on_multiprocessing_finished(self):
|
|
|
+ self.app.proc_container.update_view_text(' %s' % _("Setting up the UI"))
|
|
|
+ self.app.inform.emit('[success] %s.' % _("Adding geometry finished. Preparing the GUI"))
|
|
|
+ self.set_ui()
|
|
|
+ self.build_ui(first_run=True)
|
|
|
+ self.plot_all()
|
|
|
|
|
|
- # start with GRID toolbar activated
|
|
|
- if self.app.ui.grid_snap_btn.isChecked() is False:
|
|
|
- self.app.ui.grid_snap_btn.trigger()
|
|
|
+ # HACK: enabling/disabling the cursor seams to somehow update the shapes making them more 'solid'
|
|
|
+ # - perhaps is a bug in VisPy implementation
|
|
|
+ self.app.app_cursor.enabled = False
|
|
|
+ self.app.app_cursor.enabled = True
|
|
|
+ self.app.inform.emit('[success] %s' % _("Finished loading the Gerber object into the editor."))
|
|
|
|
|
|
- # adjust the visibility of some of the canvas context menu
|
|
|
- self.app.ui.popmenu_edit.setVisible(False)
|
|
|
- self.app.ui.popmenu_save.setVisible(True)
|
|
|
+ def update_fcgerber(self):
|
|
|
+ """
|
|
|
+ Create a new Gerber object that contain the edited content of the source Gerber object
|
|
|
|
|
|
- self.app.ui.popmenu_disable.setVisible(False)
|
|
|
- self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
|
|
|
- self.app.ui.popmenu_properties.setVisible(False)
|
|
|
- self.app.ui.grb_editor_cmenu.menuAction().setVisible(True)
|
|
|
+ :return: None
|
|
|
+ """
|
|
|
+ new_grb_name = self.edited_obj_name
|
|
|
|
|
|
- def deactivate_grb_editor(self):
|
|
|
+ # if the 'delayed plot' malfunctioned stop the QTimer
|
|
|
try:
|
|
|
- QtGui.QGuiApplication.restoreOverrideCursor()
|
|
|
+ self.plot_thread.stop()
|
|
|
except Exception as e:
|
|
|
- log.debug("AppGerberEditor.deactivate_grb_editor() --> %s" % str(e))
|
|
|
+ log.debug("AppGerberEditor.update_fcgerber() --> %s" % str(e))
|
|
|
|
|
|
- self.clear()
|
|
|
+ if "_edit" in self.edited_obj_name:
|
|
|
+ try:
|
|
|
+ _id = int(self.edited_obj_name[-1]) + 1
|
|
|
+ new_grb_name = self.edited_obj_name[:-1] + str(_id)
|
|
|
+ except ValueError:
|
|
|
+ new_grb_name += "_1"
|
|
|
+ else:
|
|
|
+ new_grb_name = self.edited_obj_name + "_edit"
|
|
|
|
|
|
- # adjust the status of the menu entries related to the editor
|
|
|
- self.app.ui.menueditedit.setDisabled(False)
|
|
|
- self.app.ui.menueditok.setDisabled(True)
|
|
|
- # adjust the visibility of some of the canvas context menu
|
|
|
- self.app.ui.popmenu_edit.setVisible(True)
|
|
|
- self.app.ui.popmenu_save.setVisible(False)
|
|
|
+ self.app.worker_task.emit({'fcn': self.new_edited_gerber, 'params': [new_grb_name, self.storage_dict]})
|
|
|
+ # self.new_edited_gerber(new_grb_name, self.storage_dict)
|
|
|
|
|
|
- self.disconnect_canvas_event_handlers()
|
|
|
- self.app.ui.grb_edit_toolbar.setDisabled(True)
|
|
|
+ @staticmethod
|
|
|
+ def update_options(obj):
|
|
|
+ try:
|
|
|
+ if not obj.options:
|
|
|
+ obj.options = {'xmin': 0, 'ymin': 0, 'xmax': 0, 'ymax': 0}
|
|
|
+ return True
|
|
|
+ else:
|
|
|
+ return False
|
|
|
+ except AttributeError:
|
|
|
+ obj.options = {}
|
|
|
+ return True
|
|
|
|
|
|
- self.app.ui.corner_snap_btn.setVisible(False)
|
|
|
- self.app.ui.snap_magnet.setVisible(False)
|
|
|
+ def new_edited_gerber(self, outname, aperture_storage):
|
|
|
+ """
|
|
|
+ Creates a new Gerber object for the edited Gerber. Thread-safe.
|
|
|
|
|
|
- # set the Editor Toolbar visibility to what was before entering in the Editor
|
|
|
- self.app.ui.grb_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
|
|
|
- else self.app.ui.grb_edit_toolbar.setVisible(True)
|
|
|
+ :param outname: Name of the resulting object. None causes the name to be that of the file.
|
|
|
+ :type outname: str
|
|
|
+ :param aperture_storage: a dictionary that holds all the objects geometry
|
|
|
+ :type aperture_storage: dict
|
|
|
+ :return: None
|
|
|
+ """
|
|
|
|
|
|
- # Disable visuals
|
|
|
- self.shapes.enabled = False
|
|
|
- self.tool_shape.enabled = False
|
|
|
- # self.app.app_cursor.enabled = False
|
|
|
+ self.app.log.debug("Update the Gerber object with edited content. Source is: %s" %
|
|
|
+ self.gerber_obj.options['name'].upper())
|
|
|
|
|
|
- self.app.ui.grb_editor_menu.setDisabled(True)
|
|
|
- self.app.ui.grb_editor_menu.menuAction().setVisible(False)
|
|
|
+ out_name = outname
|
|
|
+ storage_dict = aperture_storage
|
|
|
|
|
|
- self.app.ui.update_obj_btn.setEnabled(False)
|
|
|
+ local_storage_dict = {}
|
|
|
+ for aperture in storage_dict:
|
|
|
+ if 'geometry' in storage_dict[aperture]:
|
|
|
+ # add aperture only if it has geometry
|
|
|
+ if len(storage_dict[aperture]['geometry']) > 0:
|
|
|
+ local_storage_dict[aperture] = deepcopy(storage_dict[aperture])
|
|
|
|
|
|
- # adjust the visibility of some of the canvas context menu
|
|
|
- self.app.ui.popmenu_edit.setVisible(True)
|
|
|
- self.app.ui.popmenu_save.setVisible(False)
|
|
|
+ # How the object should be initialized
|
|
|
+ def obj_init(grb_obj, app_obj):
|
|
|
|
|
|
- self.app.ui.popmenu_disable.setVisible(True)
|
|
|
- self.app.ui.cmenu_newmenu.menuAction().setVisible(True)
|
|
|
- self.app.ui.popmenu_properties.setVisible(True)
|
|
|
- self.app.ui.g_editor_cmenu.menuAction().setVisible(False)
|
|
|
- self.app.ui.e_editor_cmenu.menuAction().setVisible(False)
|
|
|
- self.app.ui.grb_editor_cmenu.menuAction().setVisible(False)
|
|
|
+ poly_buffer = []
|
|
|
+ follow_buffer = []
|
|
|
|
|
|
- # Show original geometry
|
|
|
- if self.gerber_obj:
|
|
|
- self.gerber_obj.visible = True
|
|
|
+ for storage_apcode, storage_val in local_storage_dict.items():
|
|
|
+ grb_obj.apertures[storage_apcode] = {}
|
|
|
|
|
|
- def connect_canvas_event_handlers(self):
|
|
|
- # Canvas events
|
|
|
+ for k, val in storage_val.items():
|
|
|
+ if k == 'geometry':
|
|
|
+ grb_obj.apertures[storage_apcode][k] = []
|
|
|
+ for geo_el in val:
|
|
|
+ geometric_data = geo_el.geo
|
|
|
+ new_geo_el = {}
|
|
|
+ if 'solid' in geometric_data:
|
|
|
+ new_geo_el['solid'] = geometric_data['solid']
|
|
|
+ poly_buffer.append(deepcopy(new_geo_el['solid']))
|
|
|
|
|
|
- # make sure that the shortcuts key and mouse events will no longer be linked to the methods from FlatCAMApp
|
|
|
- # but those from AppGeoEditor
|
|
|
+ if 'follow' in geometric_data:
|
|
|
+ # if isinstance(geometric_data['follow'], Polygon):
|
|
|
+ # buff_val = -(int(storage_val['size']) / 2)
|
|
|
+ # geo_f = (geometric_data['follow'].buffer(buff_val)).exterior
|
|
|
+ # new_geo_el['follow'] = geo_f
|
|
|
+ # else:
|
|
|
+ # new_geo_el['follow'] = geometric_data['follow']
|
|
|
+ new_geo_el['follow'] = geometric_data['follow']
|
|
|
+ follow_buffer.append(deepcopy(new_geo_el['follow']))
|
|
|
+ else:
|
|
|
+ if 'solid' in geometric_data:
|
|
|
+ geo_f = geometric_data['solid'].exterior
|
|
|
+ new_geo_el['follow'] = geo_f
|
|
|
+ follow_buffer.append(deepcopy(new_geo_el['follow']))
|
|
|
|
|
|
- # first connect to new, then disconnect the old handlers
|
|
|
- # don't ask why but if there is nothing connected I've seen issues
|
|
|
- self.mp = self.canvas.graph_event_connect('mouse_press', self.on_canvas_click)
|
|
|
- self.mm = self.canvas.graph_event_connect('mouse_move', self.on_canvas_move)
|
|
|
- self.mr = self.canvas.graph_event_connect('mouse_release', self.on_grb_click_release)
|
|
|
+ if 'clear' in geometric_data:
|
|
|
+ new_geo_el['clear'] = geometric_data['clear']
|
|
|
|
|
|
- if self.app.is_legacy is False:
|
|
|
- self.canvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
|
|
|
- self.canvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
|
|
|
- self.canvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
|
|
|
- self.canvas.graph_event_disconnect('mouse_double_click', self.app.on_mouse_double_click_over_plot)
|
|
|
- else:
|
|
|
- self.canvas.graph_event_disconnect(self.app.mp)
|
|
|
- self.canvas.graph_event_disconnect(self.app.mm)
|
|
|
- self.canvas.graph_event_disconnect(self.app.mr)
|
|
|
- self.canvas.graph_event_disconnect(self.app.mdc)
|
|
|
+ if new_geo_el:
|
|
|
+ grb_obj.apertures[storage_apcode][k].append(deepcopy(new_geo_el))
|
|
|
+ else:
|
|
|
+ grb_obj.apertures[storage_apcode][k] = val
|
|
|
|
|
|
- self.app.collection.view.clicked.disconnect()
|
|
|
+ grb_obj.aperture_macros = deepcopy(self.gerber_obj.aperture_macros)
|
|
|
|
|
|
- self.app.ui.popmenu_copy.triggered.disconnect()
|
|
|
- self.app.ui.popmenu_delete.triggered.disconnect()
|
|
|
- self.app.ui.popmenu_move.triggered.disconnect()
|
|
|
+ new_poly = MultiPolygon(poly_buffer)
|
|
|
+ new_poly = new_poly.buffer(0.00000001)
|
|
|
+ new_poly = new_poly.buffer(-0.00000001)
|
|
|
|
|
|
- self.app.ui.popmenu_copy.triggered.connect(self.on_copy_button)
|
|
|
- self.app.ui.popmenu_delete.triggered.connect(self.on_delete_btn)
|
|
|
- self.app.ui.popmenu_move.triggered.connect(self.on_move_button)
|
|
|
+ # for ad in grb_obj.apertures:
|
|
|
+ # print(ad, grb_obj.apertures[ad])
|
|
|
|
|
|
- # Gerber Editor
|
|
|
- self.app.ui.grb_draw_pad.triggered.connect(self.on_pad_add)
|
|
|
- self.app.ui.grb_draw_pad_array.triggered.connect(self.on_pad_add_array)
|
|
|
- self.app.ui.grb_draw_track.triggered.connect(self.on_track_add)
|
|
|
- self.app.ui.grb_draw_region.triggered.connect(self.on_region_add)
|
|
|
+ try:
|
|
|
+ __ = iter(new_poly)
|
|
|
+ except TypeError:
|
|
|
+ new_poly = [new_poly]
|
|
|
+
|
|
|
+ grb_obj.solid_geometry = deepcopy(new_poly)
|
|
|
+ grb_obj.follow_geometry = deepcopy(follow_buffer)
|
|
|
|
|
|
- self.app.ui.grb_draw_poligonize.triggered.connect(self.on_poligonize)
|
|
|
- self.app.ui.grb_draw_semidisc.triggered.connect(self.on_add_semidisc)
|
|
|
- self.app.ui.grb_draw_disc.triggered.connect(self.on_disc_add)
|
|
|
- self.app.ui.grb_draw_buffer.triggered.connect(lambda: self.select_tool("buffer"))
|
|
|
- self.app.ui.grb_draw_scale.triggered.connect(lambda: self.select_tool("scale"))
|
|
|
- self.app.ui.grb_draw_markarea.triggered.connect(lambda: self.select_tool("markarea"))
|
|
|
- self.app.ui.grb_draw_eraser.triggered.connect(self.on_eraser)
|
|
|
- self.app.ui.grb_draw_transformations.triggered.connect(self.on_transform)
|
|
|
+ for k, v in self.gerber_obj_options.items():
|
|
|
+ if k == 'name':
|
|
|
+ grb_obj.options[k] = out_name
|
|
|
+ else:
|
|
|
+ grb_obj.options[k] = deepcopy(v)
|
|
|
|
|
|
- def disconnect_canvas_event_handlers(self):
|
|
|
+ grb_obj.multigeo = False
|
|
|
+ grb_obj.follow = False
|
|
|
+ grb_obj.units = app_obj.defaults['units']
|
|
|
|
|
|
- # we restore the key and mouse control to FlatCAMApp method
|
|
|
- # first connect to new, then disconnect the old handlers
|
|
|
- # don't ask why but if there is nothing connected I've seen issues
|
|
|
- self.app.mp = self.canvas.graph_event_connect('mouse_press', self.app.on_mouse_click_over_plot)
|
|
|
- self.app.mm = self.canvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
|
|
|
- self.app.mr = self.canvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
|
|
|
- self.app.mdc = self.canvas.graph_event_connect('mouse_double_click', self.app.on_mouse_double_click_over_plot)
|
|
|
- self.app.collection.view.clicked.connect(self.app.collection.on_mouse_down)
|
|
|
+ try:
|
|
|
+ grb_obj.create_geometry()
|
|
|
+ except KeyError:
|
|
|
+ self.app.inform.emit('[ERROR_NOTCL] %s' %
|
|
|
+ _("There are no Aperture definitions in the file. Aborting Gerber creation."))
|
|
|
+ except Exception:
|
|
|
+ msg = '[ERROR] %s' % _("An internal error has occurred. See shell.\n")
|
|
|
+ msg += traceback.format_exc()
|
|
|
+ app_obj.inform.emit(msg)
|
|
|
+ raise
|
|
|
|
|
|
- if self.app.is_legacy is False:
|
|
|
- self.canvas.graph_event_disconnect('mouse_press', self.on_canvas_click)
|
|
|
- self.canvas.graph_event_disconnect('mouse_move', self.on_canvas_move)
|
|
|
- self.canvas.graph_event_disconnect('mouse_release', self.on_grb_click_release)
|
|
|
- else:
|
|
|
- self.canvas.graph_event_disconnect(self.mp)
|
|
|
- self.canvas.graph_event_disconnect(self.mm)
|
|
|
- self.canvas.graph_event_disconnect(self.mr)
|
|
|
+ grb_obj.source_file = self.app.f_handlers.export_gerber(obj_name=out_name, filename=None,
|
|
|
+ local_use=grb_obj, use_thread=False)
|
|
|
|
|
|
- try:
|
|
|
- self.app.ui.popmenu_copy.triggered.disconnect(self.on_copy_button)
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
+ with self.app.proc_container.new(_("Working ...")):
|
|
|
+ try:
|
|
|
+ self.app.app_obj.new_object("gerber", outname, obj_init)
|
|
|
+ except Exception as e:
|
|
|
+ log.error("Error on Edited object creation: %s" % str(e))
|
|
|
+ # make sure to clean the previous results
|
|
|
+ self.results = []
|
|
|
+ return
|
|
|
|
|
|
- try:
|
|
|
- self.app.ui.popmenu_delete.triggered.disconnect(self.on_delete_btn)
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
+ # make sure to clean the previous results
|
|
|
+ self.results = []
|
|
|
+ self.deactivate_grb_editor()
|
|
|
+ self.app.inform.emit('[success] %s' % _("Done."))
|
|
|
|
|
|
- try:
|
|
|
- self.app.ui.popmenu_move.triggered.disconnect(self.on_move_button)
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
+ def on_tool_select(self, tool):
|
|
|
+ """
|
|
|
+ Behavior of the toolbar. Tool initialization.
|
|
|
|
|
|
- self.app.ui.popmenu_copy.triggered.connect(self.app.on_copy_command)
|
|
|
- self.app.ui.popmenu_delete.triggered.connect(self.app.on_delete)
|
|
|
- self.app.ui.popmenu_move.triggered.connect(self.app.obj_move)
|
|
|
+ :rtype : None
|
|
|
+ """
|
|
|
+ current_tool = tool
|
|
|
|
|
|
- # Gerber Editor
|
|
|
+ self.app.log.debug("on_tool_select('%s')" % tool)
|
|
|
|
|
|
- try:
|
|
|
- self.app.ui.grb_draw_pad.triggered.disconnect(self.on_pad_add)
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
+ if self.last_aperture_selected is None and current_tool != 'select':
|
|
|
+ # self.draw_app.select_tool('select')
|
|
|
+ self.complete = True
|
|
|
+ current_tool = 'select'
|
|
|
+ self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. No aperture is selected"))
|
|
|
|
|
|
- try:
|
|
|
- self.app.ui.grb_draw_pad_array.triggered.disconnect(self.on_pad_add_array)
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
+ # This is to make the group behave as radio group
|
|
|
+ if current_tool in self.tools_gerber:
|
|
|
+ if self.tools_gerber[current_tool]["button"].isChecked():
|
|
|
+ self.app.log.debug("%s is checked." % current_tool)
|
|
|
+ for t in self.tools_gerber:
|
|
|
+ if t != current_tool:
|
|
|
+ self.tools_gerber[t]["button"].setChecked(False)
|
|
|
|
|
|
- try:
|
|
|
- self.app.ui.grb_draw_track.triggered.disconnect(self.on_track_add)
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
+ # this is where the Editor toolbar classes (button's) are instantiated
|
|
|
+ self.active_tool = self.tools_gerber[current_tool]["constructor"](self)
|
|
|
+ # self.app.inform.emit(self.active_tool.start_msg)
|
|
|
+ else:
|
|
|
+ self.app.log.debug("%s is NOT checked." % current_tool)
|
|
|
+ for t in self.tools_gerber:
|
|
|
+ self.tools_gerber[t]["button"].setChecked(False)
|
|
|
|
|
|
- try:
|
|
|
- self.app.ui.grb_draw_region.triggered.disconnect(self.on_region_add)
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
+ self.select_tool('select')
|
|
|
+ self.active_tool = FCApertureSelect(self)
|
|
|
|
|
|
- try:
|
|
|
- self.app.ui.grb_draw_poligonize.triggered.disconnect(self.on_poligonize)
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
- try:
|
|
|
- self.app.ui.grb_draw_semidisc.triggered.diconnect(self.on_add_semidisc)
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
- try:
|
|
|
- self.app.ui.grb_draw_disc.triggered.disconnect(self.on_disc_add)
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
- try:
|
|
|
- self.app.ui.grb_draw_buffer.triggered.disconnect()
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
- try:
|
|
|
- self.app.ui.grb_draw_scale.triggered.disconnect()
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
- try:
|
|
|
- self.app.ui.grb_draw_markarea.triggered.disconnect()
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
- try:
|
|
|
- self.app.ui.grb_draw_eraser.triggered.disconnect(self.on_eraser)
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
- try:
|
|
|
- self.app.ui.grb_draw_transformations.triggered.disconnect(self.on_transform)
|
|
|
- except (TypeError, AttributeError):
|
|
|
+ def on_row_selected(self, row, col):
|
|
|
+ # if col == 0:
|
|
|
+ key_modifier = QtWidgets.QApplication.keyboardModifiers()
|
|
|
+ if self.app.defaults["global_mselect_key"] == 'Control':
|
|
|
+ modifier_to_use = Qt.ControlModifier
|
|
|
+ else:
|
|
|
+ modifier_to_use = Qt.ShiftModifier
|
|
|
+
|
|
|
+ if key_modifier == modifier_to_use:
|
|
|
pass
|
|
|
+ else:
|
|
|
+ self.selected = []
|
|
|
|
|
|
try:
|
|
|
- self.app.jump_signal.disconnect()
|
|
|
- except (TypeError, AttributeError):
|
|
|
- pass
|
|
|
+ selected_ap_code = self.ui.apertures_table.item(row, 1).text()
|
|
|
+ self.last_aperture_selected = copy(selected_ap_code)
|
|
|
|
|
|
- def clear(self):
|
|
|
- self.thread.quit()
|
|
|
+ for obj in self.storage_dict[selected_ap_code]['geometry']:
|
|
|
+ self.selected.append(obj)
|
|
|
+ except Exception as e:
|
|
|
+ self.app.log.debug(str(e))
|
|
|
|
|
|
- self.active_tool = None
|
|
|
- self.selected = []
|
|
|
- self.storage_dict.clear()
|
|
|
- self.results.clear()
|
|
|
+ self.plot_all()
|
|
|
|
|
|
- self.shapes.clear(update=True)
|
|
|
- self.tool_shape.clear(update=True)
|
|
|
- self.ma_annotation.clear(update=True)
|
|
|
+ # def toolbar_tool_toggle(self, key):
|
|
|
+ # """
|
|
|
+ #
|
|
|
+ # :param key: key to update in self.options dictionary
|
|
|
+ # :return:
|
|
|
+ # """
|
|
|
+ # self.options[key] = self.sender().isChecked()
|
|
|
+ # return self.options[key]
|
|
|
|
|
|
- def edit_fcgerber(self, orig_grb_obj):
|
|
|
+ def on_grb_shape_complete(self, storage=None, specific_shape=None, no_plot=False):
|
|
|
"""
|
|
|
- Imports the geometry found in self.apertures from the given FlatCAM Gerber object
|
|
|
- into the editor.
|
|
|
|
|
|
- :param orig_grb_obj: ExcellonObject
|
|
|
- :return: None
|
|
|
+ :param storage: where to store the shape
|
|
|
+ :param specific_shape: optional, the shape to be stored
|
|
|
+ :param no_plot: use this if you want the added shape not plotted
|
|
|
+ :return:
|
|
|
"""
|
|
|
+ self.app.log.debug("on_grb_shape_complete()")
|
|
|
|
|
|
- self.deactivate_grb_editor()
|
|
|
- self.activate_grb_editor()
|
|
|
+ if specific_shape:
|
|
|
+ geo = specific_shape
|
|
|
+ else:
|
|
|
+ geo = deepcopy(self.active_tool.geometry)
|
|
|
+ if geo is None:
|
|
|
+ return
|
|
|
|
|
|
- # reset the tool table
|
|
|
- self.apertures_table.clear()
|
|
|
+ if storage is not None:
|
|
|
+ # Add shape
|
|
|
+ self.add_gerber_shape(geo, storage)
|
|
|
+ else:
|
|
|
+ stora = self.storage_dict[self.last_aperture_selected]['geometry']
|
|
|
+ self.add_gerber_shape(geo, storage=stora)
|
|
|
|
|
|
- self.apertures_table.setHorizontalHeaderLabels(['#', _('Code'), _('Type'), _('Size'), _('Dim')])
|
|
|
- self.last_aperture_selected = None
|
|
|
+ # Remove any utility shapes
|
|
|
+ self.delete_utility_geometry()
|
|
|
+ self.tool_shape.clear(update=True)
|
|
|
|
|
|
- # create a reference to the source object
|
|
|
- self.gerber_obj = orig_grb_obj
|
|
|
- self.gerber_obj_options = orig_grb_obj.options
|
|
|
+ if no_plot is False:
|
|
|
+ # Re-plot and reset tool.
|
|
|
+ self.plot_all()
|
|
|
|
|
|
- file_units = self.gerber_obj.units if self.gerber_obj.units else 'IN'
|
|
|
- app_units = self.app.defaults['units']
|
|
|
- # self.conversion_factor = 25.4 if file_units == 'IN' else (1 / 25.4) if file_units != app_units else 1
|
|
|
+ def add_gerber_shape(self, shape_element, storage):
|
|
|
+ """
|
|
|
+ Adds a shape to the shape storage.
|
|
|
|
|
|
- if file_units == app_units:
|
|
|
- self.conversion_factor = 1
|
|
|
- else:
|
|
|
- if file_units == 'IN':
|
|
|
- self.conversion_factor = 25.4
|
|
|
- else:
|
|
|
- self.conversion_factor = 0.0393700787401575
|
|
|
+ :param shape_element: Shape to be added.
|
|
|
+ :type shape_element: DrawToolShape or DrawToolUtilityShape Geometry is stored as a dict with keys: solid,
|
|
|
+ follow, clear, each value being a list of Shapely objects. The dict can have at least one of the mentioned keys
|
|
|
+ :param storage: Where to store the shape
|
|
|
+ :return: None
|
|
|
+ """
|
|
|
+ # List of DrawToolShape?
|
|
|
|
|
|
- # Hide original geometry
|
|
|
- orig_grb_obj.visible = False
|
|
|
+ if isinstance(shape_element, list):
|
|
|
+ for subshape in shape_element:
|
|
|
+ self.add_gerber_shape(subshape, storage)
|
|
|
+ return
|
|
|
|
|
|
- # Set selection tolerance
|
|
|
- # DrawToolShape.tolerance = fc_excellon.drawing_tolerance * 10
|
|
|
+ assert isinstance(shape_element, DrawToolShape), \
|
|
|
+ "Expected a DrawToolShape, got %s" % str(type(shape_element))
|
|
|
+
|
|
|
+ assert shape_element.geo is not None, \
|
|
|
+ "Shape object has empty geometry (None)"
|
|
|
|
|
|
- self.select_tool("select")
|
|
|
+ assert(isinstance(shape_element.geo, list) and len(shape_element.geo) > 0) or not \
|
|
|
+ isinstance(shape_element.geo, list), "Shape objects has empty geometry ([])"
|
|
|
|
|
|
- try:
|
|
|
- # we activate this after the initial build as we don't need to see the tool been populated
|
|
|
- self.apertures_table.itemChanged.connect(self.on_tool_edit)
|
|
|
- except Exception as e:
|
|
|
- log.debug("AppGerberEditor.edit_fcgerber() --> %s" % str(e))
|
|
|
+ if isinstance(shape_element, DrawToolUtilityShape):
|
|
|
+ self.utility.append(shape_element)
|
|
|
+ else:
|
|
|
+ storage.append(shape_element)
|
|
|
|
|
|
- # apply the conversion factor on the obj.apertures
|
|
|
- conv_apertures = deepcopy(self.gerber_obj.apertures)
|
|
|
- for apcode in self.gerber_obj.apertures:
|
|
|
- for key in self.gerber_obj.apertures[apcode]:
|
|
|
- if key == 'width':
|
|
|
- conv_apertures[apcode]['width'] = self.gerber_obj.apertures[apcode]['width'] * \
|
|
|
- self.conversion_factor
|
|
|
- elif key == 'height':
|
|
|
- conv_apertures[apcode]['height'] = self.gerber_obj.apertures[apcode]['height'] * \
|
|
|
- self.conversion_factor
|
|
|
- elif key == 'diam':
|
|
|
- conv_apertures[apcode]['diam'] = self.gerber_obj.apertures[apcode]['diam'] * self.conversion_factor
|
|
|
- elif key == 'size':
|
|
|
- conv_apertures[apcode]['size'] = self.gerber_obj.apertures[apcode]['size'] * self.conversion_factor
|
|
|
- else:
|
|
|
- conv_apertures[apcode][key] = self.gerber_obj.apertures[apcode][key]
|
|
|
+ def on_canvas_click(self, event):
|
|
|
+ """
|
|
|
+ event.x and .y have canvas coordinates
|
|
|
+ event.xdata and .ydata have plot coordinates
|
|
|
|
|
|
- self.gerber_obj.apertures = conv_apertures
|
|
|
- self.gerber_obj.units = app_units
|
|
|
+ :param event: Event object dispatched by VisPy
|
|
|
+ :return: None
|
|
|
+ """
|
|
|
+ if self.app.is_legacy is False:
|
|
|
+ event_pos = event.pos
|
|
|
+ # event_is_dragging = event.is_dragging
|
|
|
+ # right_button = 2
|
|
|
+ else:
|
|
|
+ event_pos = (event.xdata, event.ydata)
|
|
|
+ # event_is_dragging = self.app.plotcanvas.is_dragging
|
|
|
+ # right_button = 3
|
|
|
|
|
|
- # # and then add it to the storage elements (each storage elements is a member of a list
|
|
|
- # def job_thread(aperture_id):
|
|
|
- # with self.app.proc_container.new('%s: %s ...' %
|
|
|
- # (_("Adding geometry for aperture"), str(aperture_id))):
|
|
|
- # storage_elem = []
|
|
|
- # self.storage_dict[aperture_id] = {}
|
|
|
- #
|
|
|
- # # add the Gerber geometry to editor storage
|
|
|
- # for k, v in self.gerber_obj.apertures[aperture_id].items():
|
|
|
- # try:
|
|
|
- # if k == 'geometry':
|
|
|
- # for geo_el in v:
|
|
|
- # if geo_el:
|
|
|
- # self.add_gerber_shape(DrawToolShape(geo_el), storage_elem)
|
|
|
- # self.storage_dict[aperture_id][k] = storage_elem
|
|
|
- # else:
|
|
|
- # self.storage_dict[aperture_id][k] = self.gerber_obj.apertures[aperture_id][k]
|
|
|
- # except Exception as e:
|
|
|
- # log.debug("AppGerberEditor.edit_fcgerber().job_thread() --> %s" % str(e))
|
|
|
- #
|
|
|
- # # Check promises and clear if exists
|
|
|
- # while True:
|
|
|
- # try:
|
|
|
- # self.grb_plot_promises.remove(aperture_id)
|
|
|
- # time.sleep(0.5)
|
|
|
- # except ValueError:
|
|
|
- # break
|
|
|
- #
|
|
|
- # # we create a job work each aperture, job that work in a threaded way to store the geometry in local storage
|
|
|
- # # as DrawToolShapes
|
|
|
- # for ap_code in self.gerber_obj.apertures:
|
|
|
- # self.grb_plot_promises.append(ap_code)
|
|
|
- # self.app.worker_task.emit({'fcn': job_thread, 'params': [ap_code]})
|
|
|
- #
|
|
|
- # self.set_ui()
|
|
|
- #
|
|
|
- # # do the delayed plot only if there is something to plot (the gerber is not empty)
|
|
|
- # try:
|
|
|
- # if bool(self.gerber_obj.apertures):
|
|
|
- # self.start_delayed_plot(check_period=1000)
|
|
|
- # else:
|
|
|
- # raise AttributeError
|
|
|
- # except AttributeError:
|
|
|
- # # now that we have data (empty data actually), create the GUI interface and add it to the Tool Tab
|
|
|
- # self.build_ui(first_run=True)
|
|
|
- # # and add the first aperture to have something to play with
|
|
|
- # self.on_aperture_add('10')
|
|
|
+ self.pos = self.canvas.translate_coords(event_pos)
|
|
|
|
|
|
- # self.app.worker_task.emit({'fcn': worker_job, 'params': [self]})
|
|
|
+ if self.app.grid_status():
|
|
|
+ self.pos = self.app.geo_editor.snap(self.pos[0], self.pos[1])
|
|
|
+ else:
|
|
|
+ self.pos = (self.pos[0], self.pos[1])
|
|
|
|
|
|
- class Execute_Edit(QtCore.QObject):
|
|
|
+ if event.button == 1:
|
|
|
+ self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
|
|
+ "%.4f " % (0, 0))
|
|
|
|
|
|
- start = QtCore.pyqtSignal(str)
|
|
|
+ # Selection with left mouse button
|
|
|
+ if self.active_tool is not None:
|
|
|
+ modifiers = QtWidgets.QApplication.keyboardModifiers()
|
|
|
|
|
|
- def __init__(self, app):
|
|
|
- super(Execute_Edit, self).__init__()
|
|
|
- self.app = app
|
|
|
- self.start.connect(self.run)
|
|
|
+ # If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard
|
|
|
+ if modifiers == QtCore.Qt.ShiftModifier:
|
|
|
+ self.app.clipboard.setText(
|
|
|
+ self.app.defaults["global_point_clipboard_format"] %
|
|
|
+ (self.decimals, self.pos[0], self.decimals, self.pos[1])
|
|
|
+ )
|
|
|
+ self.app.inform.emit('[success] %s' % _("Coordinates copied to clipboard."))
|
|
|
+ return
|
|
|
|
|
|
- @staticmethod
|
|
|
- def worker_job(app_obj):
|
|
|
- with app_obj.app.proc_container.new('%s ...' % _("Loading")):
|
|
|
- # ###############################################################
|
|
|
- # APPLY CLEAR_GEOMETRY on the SOLID_GEOMETRY
|
|
|
- # ###############################################################
|
|
|
+ # Dispatch event to active_tool
|
|
|
+ self.active_tool.click(self.app.geo_editor.snap(self.pos[0], self.pos[1]))
|
|
|
|
|
|
- # list of clear geos that are to be applied to the entire file
|
|
|
- global_clear_geo = []
|
|
|
+ # If it is a shape generating tool
|
|
|
+ if isinstance(self.active_tool, FCShapeTool) and self.active_tool.complete:
|
|
|
+ if self.current_storage is not None:
|
|
|
+ self.on_grb_shape_complete(self.current_storage)
|
|
|
+ self.build_ui()
|
|
|
|
|
|
- # create one big geometry made out of all 'negative' (clear) polygons
|
|
|
- for aper_id in app_obj.gerber_obj.apertures:
|
|
|
- # first check if we have any clear_geometry (LPC) and if yes added it to the global_clear_geo
|
|
|
- if 'geometry' in app_obj.gerber_obj.apertures[aper_id]:
|
|
|
- for elem in app_obj.gerber_obj.apertures[aper_id]['geometry']:
|
|
|
- if 'clear' in elem:
|
|
|
- global_clear_geo.append(elem['clear'])
|
|
|
- log.warning("Found %d clear polygons." % len(global_clear_geo))
|
|
|
+ # MS: always return to the Select Tool if modifier key is not pressed
|
|
|
+ # else return to the current tool
|
|
|
+ key_modifier = QtWidgets.QApplication.keyboardModifiers()
|
|
|
+ if self.app.defaults["global_mselect_key"] == 'Control':
|
|
|
+ modifier_to_use = Qt.ControlModifier
|
|
|
+ else:
|
|
|
+ modifier_to_use = Qt.ShiftModifier
|
|
|
|
|
|
- if global_clear_geo:
|
|
|
- global_clear_geo = unary_union(global_clear_geo)
|
|
|
- if isinstance(global_clear_geo, Polygon):
|
|
|
- global_clear_geo = [global_clear_geo]
|
|
|
+ # if modifier key is pressed then we add to the selected list the current shape but if it's already
|
|
|
+ # in the selected list, we removed it. Therefore first click selects, second deselects.
|
|
|
+ if key_modifier == modifier_to_use:
|
|
|
+ self.select_tool(self.active_tool.name)
|
|
|
+ else:
|
|
|
+ # return to Select tool but not for FCPad
|
|
|
+ if isinstance(self.active_tool, FCPad):
|
|
|
+ self.select_tool(self.active_tool.name)
|
|
|
+ else:
|
|
|
+ self.select_tool("select")
|
|
|
+ return
|
|
|
|
|
|
- # we subtract the big "negative" (clear) geometry from each solid polygon but only the part of
|
|
|
- # clear geometry that fits inside the solid. otherwise we may loose the solid
|
|
|
- for ap_code in app_obj.gerber_obj.apertures:
|
|
|
- temp_solid_geometry = []
|
|
|
- if 'geometry' in app_obj.gerber_obj.apertures[ap_code]:
|
|
|
- # for elem in self.gerber_obj.apertures[apcode]['geometry']:
|
|
|
- # if 'solid' in elem:
|
|
|
- # solid_geo = elem['solid']
|
|
|
- # for clear_geo in global_clear_geo:
|
|
|
- # # Make sure that the clear_geo is within the solid_geo otherwise we loose
|
|
|
- # # the solid_geometry. We want for clear_geometry just to cut
|
|
|
- # # into solid_geometry not to delete it
|
|
|
- # if clear_geo.within(solid_geo):
|
|
|
- # solid_geo = solid_geo.difference(clear_geo)
|
|
|
- # try:
|
|
|
- # for poly in solid_geo:
|
|
|
- # new_elem = {}
|
|
|
- #
|
|
|
- # new_elem['solid'] = poly
|
|
|
- # if 'clear' in elem:
|
|
|
- # new_elem['clear'] = poly
|
|
|
- # if 'follow' in elem:
|
|
|
- # new_elem['follow'] = poly
|
|
|
- # temp_elem.append(deepcopy(new_elem))
|
|
|
- # except TypeError:
|
|
|
- # new_elem = {}
|
|
|
- # new_elem['solid'] = solid_geo
|
|
|
- # if 'clear' in elem:
|
|
|
- # new_elem['clear'] = solid_geo
|
|
|
- # if 'follow' in elem:
|
|
|
- # new_elem['follow'] = solid_geo
|
|
|
- # temp_elem.append(deepcopy(new_elem))
|
|
|
- for elem in app_obj.gerber_obj.apertures[ap_code]['geometry']:
|
|
|
- new_elem = {}
|
|
|
- if 'solid' in elem:
|
|
|
- solid_geo = elem['solid']
|
|
|
- if not global_clear_geo or global_clear_geo.is_empty:
|
|
|
- pass
|
|
|
- else:
|
|
|
- for clear_geo in global_clear_geo:
|
|
|
- # Make sure that the clear_geo is within the solid_geo otherwise we loose
|
|
|
- # the solid_geometry. We want for clear_geometry just to cut into
|
|
|
- # solid_geometry not to delete it
|
|
|
- if clear_geo.within(solid_geo):
|
|
|
- solid_geo = solid_geo.difference(clear_geo)
|
|
|
+ # if isinstance(self.active_tool, FCApertureSelect):
|
|
|
+ # self.plot_all()
|
|
|
+ else:
|
|
|
+ self.app.log.debug("No active tool to respond to click!")
|
|
|
|
|
|
- new_elem['solid'] = solid_geo
|
|
|
- if 'clear' in elem:
|
|
|
- new_elem['clear'] = elem['clear']
|
|
|
- if 'follow' in elem:
|
|
|
- new_elem['follow'] = elem['follow']
|
|
|
- temp_solid_geometry.append(deepcopy(new_elem))
|
|
|
+ def on_grb_click_release(self, event):
|
|
|
+ self.modifiers = QtWidgets.QApplication.keyboardModifiers()
|
|
|
+ if self.app.is_legacy is False:
|
|
|
+ event_pos = event.pos
|
|
|
+ # event_is_dragging = event.is_dragging
|
|
|
+ right_button = 2
|
|
|
+ else:
|
|
|
+ event_pos = (event.xdata, event.ydata)
|
|
|
+ # event_is_dragging = self.app.plotcanvas.is_dragging
|
|
|
+ right_button = 3
|
|
|
|
|
|
- app_obj.gerber_obj.apertures[ap_code]['geometry'] = deepcopy(temp_solid_geometry)
|
|
|
+ pos_canvas = self.canvas.translate_coords(event_pos)
|
|
|
+ if self.app.grid_status():
|
|
|
+ pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
|
|
|
+ else:
|
|
|
+ pos = (pos_canvas[0], pos_canvas[1])
|
|
|
|
|
|
- log.warning("Polygon difference done for %d apertures." % len(app_obj.gerber_obj.apertures))
|
|
|
+ # if the released mouse button was RMB then test if it was a panning motion or not, if not it was a context
|
|
|
+ # canvas menu
|
|
|
+ try:
|
|
|
+ if event.button == right_button: # right click
|
|
|
+ if self.app.ui.popMenu.mouse_is_panning is False:
|
|
|
+ if self.in_action is False:
|
|
|
+ try:
|
|
|
+ QtGui.QGuiApplication.restoreOverrideCursor()
|
|
|
+ except Exception as e:
|
|
|
+ log.debug("AppGerberEditor.on_grb_click_release() --> %s" % str(e))
|
|
|
|
|
|
- try:
|
|
|
- # Loading the Geometry into Editor Storage
|
|
|
- for ap_code, ap_dict in app_obj.gerber_obj.apertures.items():
|
|
|
- app_obj.results.append(
|
|
|
- app_obj.pool.apply_async(app_obj.add_apertures, args=(ap_code, ap_dict))
|
|
|
- )
|
|
|
- except Exception as ee:
|
|
|
- log.debug(
|
|
|
- "AppGerberEditor.edit_fcgerber.worker_job() Adding processes to pool --> %s" % str(ee))
|
|
|
- traceback.print_exc()
|
|
|
+ if self.active_tool.complete is False and not isinstance(self.active_tool, FCApertureSelect):
|
|
|
+ self.active_tool.complete = True
|
|
|
+ self.in_action = False
|
|
|
+ self.delete_utility_geometry()
|
|
|
+ self.app.inform.emit('[success] %s' %
|
|
|
+ _("Done."))
|
|
|
+ self.select_tool('select')
|
|
|
+ else:
|
|
|
+ self.app.cursor = QtGui.QCursor()
|
|
|
+ self.app.populate_cmenu_grids()
|
|
|
+ self.app.ui.popMenu.popup(self.app.cursor.pos())
|
|
|
+ else:
|
|
|
+ # if right click on canvas and the active tool need to be finished (like Path or Polygon)
|
|
|
+ # right mouse click will finish the action
|
|
|
+ if isinstance(self.active_tool, FCShapeTool):
|
|
|
+ if isinstance(self.active_tool, FCTrack):
|
|
|
+ self.active_tool.make()
|
|
|
+ else:
|
|
|
+ self.active_tool.click(self.app.geo_editor.snap(self.x, self.y))
|
|
|
+ self.active_tool.make()
|
|
|
+ if self.active_tool.complete:
|
|
|
+ self.on_grb_shape_complete()
|
|
|
+ self.app.inform.emit('[success] %s' % _("Done."))
|
|
|
|
|
|
- output = []
|
|
|
- for p in app_obj.results:
|
|
|
- output.append(p.get())
|
|
|
+ # MS: always return to the Select Tool if modifier key is not pressed
|
|
|
+ # else return to the current tool but not for FCTrack
|
|
|
|
|
|
- for elem in output:
|
|
|
- app_obj.storage_dict[elem[0]] = deepcopy(elem[1])
|
|
|
+ if isinstance(self.active_tool, FCTrack):
|
|
|
+ self.select_tool(self.active_tool.name)
|
|
|
+ else:
|
|
|
+ key_modifier = QtWidgets.QApplication.keyboardModifiers()
|
|
|
+ if (self.app.defaults["global_mselect_key"] == 'Control' and
|
|
|
+ key_modifier == Qt.ControlModifier) or \
|
|
|
+ (self.app.defaults["global_mselect_key"] == 'Shift' and
|
|
|
+ key_modifier == Qt.ShiftModifier):
|
|
|
|
|
|
- app_obj.mp_finished.emit(output)
|
|
|
+ self.select_tool(self.active_tool.name)
|
|
|
+ else:
|
|
|
+ self.select_tool("select")
|
|
|
+ except Exception as e:
|
|
|
+ log.warning("AppGerberEditor.on_grb_click_release() RMB click --> Error: %s" % str(e))
|
|
|
+ raise
|
|
|
|
|
|
- def run(self):
|
|
|
- self.worker_job(self.app)
|
|
|
+ # if the released mouse button was LMB then test if we had a right-to-left selection or a left-to-right
|
|
|
+ # selection and then select a type of selection ("enclosing" or "touching")
|
|
|
+ try:
|
|
|
+ if event.button == 1: # left click
|
|
|
+ if self.app.selection_type is not None:
|
|
|
+ self.draw_selection_area_handler(self.pos, pos, self.app.selection_type)
|
|
|
+ self.app.selection_type = None
|
|
|
|
|
|
- # self.thread.start(QtCore.QThread.NormalPriority)
|
|
|
+ elif isinstance(self.active_tool, FCApertureSelect):
|
|
|
+ self.active_tool.click_release((self.pos[0], self.pos[1]))
|
|
|
|
|
|
- executable_edit = Execute_Edit(app=self)
|
|
|
- # executable_edit.moveToThread(self.thread)
|
|
|
- # executable_edit.start.emit("Started")
|
|
|
+ # # if there are selected objects then plot them
|
|
|
+ # if self.selected:
|
|
|
+ # self.plot_all()
|
|
|
+ except Exception as e:
|
|
|
+ log.warning("AppGerberEditor.on_grb_click_release() LMB click --> Error: %s" % str(e))
|
|
|
+ raise
|
|
|
|
|
|
- self.app.worker_task.emit({'fcn': executable_edit.run, 'params': []})
|
|
|
+ def draw_selection_area_handler(self, start_pos, end_pos, sel_type):
|
|
|
+ """
|
|
|
+ :param start_pos: mouse position when the selection LMB click was done
|
|
|
+ :param end_pos: mouse position when the left mouse button is released
|
|
|
+ :param sel_type: if True it's a left to right selection (enclosure), if False it's a 'touch' selection
|
|
|
+ :return:
|
|
|
+ """
|
|
|
|
|
|
- @staticmethod
|
|
|
- def add_apertures(aperture_id, aperture_dict):
|
|
|
- storage_elem = []
|
|
|
- storage_dict = {}
|
|
|
+ poly_selection = Polygon([start_pos, (end_pos[0], start_pos[1]), end_pos, (start_pos[0], end_pos[1])])
|
|
|
+ sel_aperture = set()
|
|
|
+ self.ui.apertures_table.clearSelection()
|
|
|
|
|
|
- for k, v in list(aperture_dict.items()):
|
|
|
- try:
|
|
|
- if k == 'geometry':
|
|
|
- for geo_el in v:
|
|
|
- if geo_el:
|
|
|
- storage_elem.append(DrawToolShape(geo_el))
|
|
|
- storage_dict[k] = storage_elem
|
|
|
- else:
|
|
|
- storage_dict[k] = aperture_dict[k]
|
|
|
- except Exception as e:
|
|
|
- log.debug("AppGerberEditor.edit_fcgerber().job_thread() --> %s" % str(e))
|
|
|
+ self.app.delete_selection_shape()
|
|
|
+ for storage in self.storage_dict:
|
|
|
+ for obj in self.storage_dict[storage]['geometry']:
|
|
|
+ if 'solid' in obj.geo:
|
|
|
+ geometric_data = obj.geo['solid']
|
|
|
+ if (sel_type is True and poly_selection.contains(geometric_data)) or \
|
|
|
+ (sel_type is False and poly_selection.intersects(geometric_data)):
|
|
|
+ if self.key == self.app.defaults["global_mselect_key"]:
|
|
|
+ if obj in self.selected:
|
|
|
+ self.selected.remove(obj)
|
|
|
+ else:
|
|
|
+ # add the object to the selected shapes
|
|
|
+ self.selected.append(obj)
|
|
|
+ sel_aperture.add(storage)
|
|
|
+ else:
|
|
|
+ self.selected.append(obj)
|
|
|
+ sel_aperture.add(storage)
|
|
|
|
|
|
- return [aperture_id, storage_dict]
|
|
|
+ try:
|
|
|
+ self.ui.apertures_table.cellPressed.disconnect()
|
|
|
+ except Exception as e:
|
|
|
+ log.debug("AppGerberEditor.draw_selection_Area_handler() --> %s" % str(e))
|
|
|
+ # select the aperture code of the selected geometry, in the tool table
|
|
|
+ self.ui.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
|
|
|
+ for aper in sel_aperture:
|
|
|
+ for row_to_sel in range(self.ui.apertures_table.rowCount()):
|
|
|
+ if str(aper) == self.ui.apertures_table.item(row_to_sel, 1).text():
|
|
|
+ if row_to_sel not in set(index.row() for index in self.ui.apertures_table.selectedIndexes()):
|
|
|
+ self.ui.apertures_table.selectRow(row_to_sel)
|
|
|
+ self.last_aperture_selected = aper
|
|
|
+ self.ui.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
|
|
|
|
|
- def on_multiprocessing_finished(self):
|
|
|
- self.app.proc_container.update_view_text(' %s' % _("Setting up the UI"))
|
|
|
- self.app.inform.emit('[success] %s.' % _("Adding geometry finished. Preparing the GUI"))
|
|
|
- self.set_ui()
|
|
|
- self.build_ui(first_run=True)
|
|
|
+ self.ui.apertures_table.cellPressed.connect(self.on_row_selected)
|
|
|
self.plot_all()
|
|
|
|
|
|
- # HACK: enabling/disabling the cursor seams to somehow update the shapes making them more 'solid'
|
|
|
- # - perhaps is a bug in VisPy implementation
|
|
|
- self.app.app_cursor.enabled = False
|
|
|
- self.app.app_cursor.enabled = True
|
|
|
- self.app.inform.emit('[success] %s' % _("Finished loading the Gerber object into the editor."))
|
|
|
-
|
|
|
- def update_fcgerber(self):
|
|
|
+ def on_canvas_move(self, event):
|
|
|
"""
|
|
|
- Create a new Gerber object that contain the edited content of the source Gerber object
|
|
|
+ Called on 'mouse_move' event
|
|
|
+
|
|
|
+ event.pos have canvas screen coordinates
|
|
|
|
|
|
+ :param event: Event object dispatched by VisPy SceneCavas
|
|
|
:return: None
|
|
|
"""
|
|
|
- new_grb_name = self.edited_obj_name
|
|
|
+ if self.app.is_legacy is False:
|
|
|
+ event_pos = event.pos
|
|
|
+ event_is_dragging = event.is_dragging
|
|
|
+ right_button = 2
|
|
|
+ else:
|
|
|
+ event_pos = (event.xdata, event.ydata)
|
|
|
+ event_is_dragging = self.app.plotcanvas.is_dragging
|
|
|
+ right_button = 3
|
|
|
|
|
|
- # if the 'delayed plot' malfunctioned stop the QTimer
|
|
|
- try:
|
|
|
- self.plot_thread.stop()
|
|
|
- except Exception as e:
|
|
|
- log.debug("AppGerberEditor.update_fcgerber() --> %s" % str(e))
|
|
|
+ pos_canvas = self.canvas.translate_coords(event_pos)
|
|
|
+ event.xdata, event.ydata = pos_canvas[0], pos_canvas[1]
|
|
|
|
|
|
- if "_edit" in self.edited_obj_name:
|
|
|
- try:
|
|
|
- _id = int(self.edited_obj_name[-1]) + 1
|
|
|
- new_grb_name = self.edited_obj_name[:-1] + str(_id)
|
|
|
- except ValueError:
|
|
|
- new_grb_name += "_1"
|
|
|
- else:
|
|
|
- new_grb_name = self.edited_obj_name + "_edit"
|
|
|
+ self.x = event.xdata
|
|
|
+ self.y = event.ydata
|
|
|
|
|
|
- self.app.worker_task.emit({'fcn': self.new_edited_gerber, 'params': [new_grb_name, self.storage_dict]})
|
|
|
- # self.new_edited_gerber(new_grb_name, self.storage_dict)
|
|
|
+ self.app.ui.popMenu.mouse_is_panning = False
|
|
|
+
|
|
|
+ # if the RMB is clicked and mouse is moving over plot then 'panning_action' is True
|
|
|
+ if event.button == right_button and event_is_dragging == 1:
|
|
|
+ self.app.ui.popMenu.mouse_is_panning = True
|
|
|
+ return
|
|
|
|
|
|
- @staticmethod
|
|
|
- def update_options(obj):
|
|
|
try:
|
|
|
- if not obj.options:
|
|
|
- obj.options = {'xmin': 0, 'ymin': 0, 'xmax': 0, 'ymax': 0}
|
|
|
- return True
|
|
|
- else:
|
|
|
- return False
|
|
|
- except AttributeError:
|
|
|
- obj.options = {}
|
|
|
- return True
|
|
|
+ x = float(event.xdata)
|
|
|
+ y = float(event.ydata)
|
|
|
+ except TypeError:
|
|
|
+ return
|
|
|
|
|
|
- def new_edited_gerber(self, outname, aperture_storage):
|
|
|
- """
|
|
|
- Creates a new Gerber object for the edited Gerber. Thread-safe.
|
|
|
+ if self.active_tool is None:
|
|
|
+ return
|
|
|
|
|
|
- :param outname: Name of the resulting object. None causes the name to be that of the file.
|
|
|
- :type outname: str
|
|
|
- :param aperture_storage: a dictionary that holds all the objects geometry
|
|
|
- :type aperture_storage: dict
|
|
|
- :return: None
|
|
|
- """
|
|
|
+ # # ## Snap coordinates
|
|
|
+ if self.app.grid_status():
|
|
|
+ x, y = self.app.geo_editor.snap(x, y)
|
|
|
|
|
|
- self.app.log.debug("Update the Gerber object with edited content. Source is: %s" %
|
|
|
- self.gerber_obj.options['name'].upper())
|
|
|
+ # Update cursor
|
|
|
+ self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color=self.app.cursor_color_3D,
|
|
|
+ edge_width=self.app.defaults["global_cursor_width"],
|
|
|
+ size=self.app.defaults["global_cursor_size"])
|
|
|
|
|
|
- out_name = outname
|
|
|
- storage_dict = aperture_storage
|
|
|
+ self.snap_x = x
|
|
|
+ self.snap_y = y
|
|
|
|
|
|
- local_storage_dict = {}
|
|
|
- for aperture in storage_dict:
|
|
|
- if 'geometry' in storage_dict[aperture]:
|
|
|
- # add aperture only if it has geometry
|
|
|
- if len(storage_dict[aperture]['geometry']) > 0:
|
|
|
- local_storage_dict[aperture] = deepcopy(storage_dict[aperture])
|
|
|
+ self.app.mouse = [x, y]
|
|
|
|
|
|
- # How the object should be initialized
|
|
|
- def obj_init(grb_obj, app_obj):
|
|
|
+ if self.pos is None:
|
|
|
+ self.pos = (0, 0)
|
|
|
+ self.app.dx = x - self.pos[0]
|
|
|
+ self.app.dy = y - self.pos[1]
|
|
|
|
|
|
- poly_buffer = []
|
|
|
- follow_buffer = []
|
|
|
+ # # update the position label in the infobar since the APP mouse event handlers are disconnected
|
|
|
+ self.app.ui.position_label.setText(" <b>X</b>: %.4f "
|
|
|
+ "<b>Y</b>: %.4f " % (x, y))
|
|
|
|
|
|
- for storage_apcode, storage_val in local_storage_dict.items():
|
|
|
- grb_obj.apertures[storage_apcode] = {}
|
|
|
+ # update the reference position label in the infobar since the APP mouse event handlers are disconnected
|
|
|
+ self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
|
|
+ "%.4f " % (self.app.dx, self.app.dy))
|
|
|
|
|
|
- for k, val in storage_val.items():
|
|
|
- if k == 'geometry':
|
|
|
- grb_obj.apertures[storage_apcode][k] = []
|
|
|
- for geo_el in val:
|
|
|
- geometric_data = geo_el.geo
|
|
|
- new_geo_el = {}
|
|
|
- if 'solid' in geometric_data:
|
|
|
- new_geo_el['solid'] = geometric_data['solid']
|
|
|
- poly_buffer.append(deepcopy(new_geo_el['solid']))
|
|
|
+ units = self.app.defaults["units"].lower()
|
|
|
+ self.app.plotcanvas.text_hud.text = \
|
|
|
+ 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format(
|
|
|
+ self.app.dx, units, self.app.dy, units, x, units, y, units)
|
|
|
+
|
|
|
+ self.update_utility_geometry(data=(x, y))
|
|
|
+
|
|
|
+ # # ## Selection area on canvas section # ##
|
|
|
+ if event_is_dragging == 1 and event.button == 1:
|
|
|
+ # I make an exception for FCRegion and FCTrack because clicking and dragging while making regions can
|
|
|
+ # create strange issues like missing a point in a track/region
|
|
|
+ if isinstance(self.active_tool, FCRegion) or isinstance(self.active_tool, FCTrack):
|
|
|
+ pass
|
|
|
+ else:
|
|
|
+ dx = pos_canvas[0] - self.pos[0]
|
|
|
+ self.app.delete_selection_shape()
|
|
|
+ if dx < 0:
|
|
|
+ self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y),
|
|
|
+ color=self.app.defaults["global_alt_sel_line"],
|
|
|
+ face_color=self.app.defaults['global_alt_sel_fill'])
|
|
|
+ self.app.selection_type = False
|
|
|
+ else:
|
|
|
+ self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y))
|
|
|
+ self.app.selection_type = True
|
|
|
+ else:
|
|
|
+ self.app.selection_type = None
|
|
|
|
|
|
- if 'follow' in geometric_data:
|
|
|
- # if isinstance(geometric_data['follow'], Polygon):
|
|
|
- # buff_val = -(int(storage_val['size']) / 2)
|
|
|
- # geo_f = (geometric_data['follow'].buffer(buff_val)).exterior
|
|
|
- # new_geo_el['follow'] = geo_f
|
|
|
- # else:
|
|
|
- # new_geo_el['follow'] = geometric_data['follow']
|
|
|
- new_geo_el['follow'] = geometric_data['follow']
|
|
|
- follow_buffer.append(deepcopy(new_geo_el['follow']))
|
|
|
- else:
|
|
|
- if 'solid' in geometric_data:
|
|
|
- geo_f = geometric_data['solid'].exterior
|
|
|
- new_geo_el['follow'] = geo_f
|
|
|
- follow_buffer.append(deepcopy(new_geo_el['follow']))
|
|
|
+ def update_utility_geometry(self, data):
|
|
|
+ # # ## Utility geometry (animated)
|
|
|
+ geo = self.active_tool.utility_geometry(data=data)
|
|
|
|
|
|
- if 'clear' in geometric_data:
|
|
|
- new_geo_el['clear'] = geometric_data['clear']
|
|
|
+ if isinstance(geo, DrawToolShape) and geo.geo is not None:
|
|
|
+ # Remove any previous utility shape
|
|
|
+ self.tool_shape.clear(update=True)
|
|
|
+ self.draw_utility_geometry(geo=geo)
|
|
|
|
|
|
- if new_geo_el:
|
|
|
- grb_obj.apertures[storage_apcode][k].append(deepcopy(new_geo_el))
|
|
|
- else:
|
|
|
- grb_obj.apertures[storage_apcode][k] = val
|
|
|
+ def draw_utility_geometry(self, geo):
|
|
|
+ if type(geo.geo) == list:
|
|
|
+ for el in geo.geo:
|
|
|
+ geometric_data = el['solid']
|
|
|
+ # Add the new utility shape
|
|
|
+ self.tool_shape.add(
|
|
|
+ shape=geometric_data, color=(self.app.defaults["global_draw_color"] + '80'),
|
|
|
+ # face_color=self.app.defaults['global_alt_sel_fill'],
|
|
|
+ update=False, layer=0, tolerance=None
|
|
|
+ )
|
|
|
+ else:
|
|
|
+ geometric_data = geo.geo['solid']
|
|
|
+ # Add the new utility shape
|
|
|
+ self.tool_shape.add(
|
|
|
+ shape=geometric_data,
|
|
|
+ color=(self.app.defaults["global_draw_color"] + '80'),
|
|
|
+ # face_color=self.app.defaults['global_alt_sel_fill'],
|
|
|
+ update=False, layer=0, tolerance=None
|
|
|
+ )
|
|
|
|
|
|
- grb_obj.aperture_macros = deepcopy(self.gerber_obj.aperture_macros)
|
|
|
+ self.tool_shape.redraw()
|
|
|
|
|
|
- new_poly = MultiPolygon(poly_buffer)
|
|
|
- new_poly = new_poly.buffer(0.00000001)
|
|
|
- new_poly = new_poly.buffer(-0.00000001)
|
|
|
+ def plot_all(self):
|
|
|
+ """
|
|
|
+ Plots all shapes in the editor.
|
|
|
|
|
|
- # for ad in grb_obj.apertures:
|
|
|
- # print(ad, grb_obj.apertures[ad])
|
|
|
+ :return: None
|
|
|
+ :rtype: None
|
|
|
+ """
|
|
|
+ with self.app.proc_container.new('%s ...' % _("Plotting")):
|
|
|
+ self.shapes.clear(update=True)
|
|
|
|
|
|
- try:
|
|
|
- __ = iter(new_poly)
|
|
|
- except TypeError:
|
|
|
- new_poly = [new_poly]
|
|
|
+ for storage in self.storage_dict:
|
|
|
+ # fix for apertures with no geometry inside
|
|
|
+ if 'geometry' in self.storage_dict[storage]:
|
|
|
+ for elem in self.storage_dict[storage]['geometry']:
|
|
|
+ if 'solid' in elem.geo:
|
|
|
+ geometric_data = elem.geo['solid']
|
|
|
+ if geometric_data is None:
|
|
|
+ continue
|
|
|
|
|
|
- grb_obj.solid_geometry = deepcopy(new_poly)
|
|
|
- grb_obj.follow_geometry = deepcopy(follow_buffer)
|
|
|
+ if elem in self.selected:
|
|
|
+ self.plot_shape(geometry=geometric_data,
|
|
|
+ color=self.app.defaults['global_sel_draw_color'] + 'FF',
|
|
|
+ linewidth=2)
|
|
|
+ else:
|
|
|
+ self.plot_shape(geometry=geometric_data,
|
|
|
+ color=self.app.defaults['global_draw_color'] + 'FF')
|
|
|
|
|
|
- for k, v in self.gerber_obj_options.items():
|
|
|
- if k == 'name':
|
|
|
- grb_obj.options[k] = out_name
|
|
|
- else:
|
|
|
- grb_obj.options[k] = deepcopy(v)
|
|
|
+ if self.utility:
|
|
|
+ for elem in self.utility:
|
|
|
+ geometric_data = elem.geo['solid']
|
|
|
+ self.plot_shape(geometry=geometric_data, linewidth=1)
|
|
|
+ continue
|
|
|
|
|
|
- grb_obj.multigeo = False
|
|
|
- grb_obj.follow = False
|
|
|
- grb_obj.units = app_obj.defaults['units']
|
|
|
+ self.shapes.redraw()
|
|
|
|
|
|
- try:
|
|
|
- grb_obj.create_geometry()
|
|
|
- except KeyError:
|
|
|
- self.app.inform.emit('[ERROR_NOTCL] %s' %
|
|
|
- _("There are no Aperture definitions in the file. Aborting Gerber creation."))
|
|
|
- except Exception:
|
|
|
- msg = '[ERROR] %s' % _("An internal error has occurred. See shell.\n")
|
|
|
- msg += traceback.format_exc()
|
|
|
- app_obj.inform.emit(msg)
|
|
|
- raise
|
|
|
+ def plot_shape(self, geometry=None, color='#000000FF', linewidth=1):
|
|
|
+ """
|
|
|
+ Plots a geometric object or list of objects without rendering. Plotted objects
|
|
|
+ are returned as a list. This allows for efficient/animated rendering.
|
|
|
|
|
|
- grb_obj.source_file = self.app.f_handlers.export_gerber(obj_name=out_name, filename=None,
|
|
|
- local_use=grb_obj, use_thread=False)
|
|
|
+ :param geometry: Geometry to be plotted (Any Shapely.geom kind or list of such)
|
|
|
+ :param color: Shape color
|
|
|
+ :param linewidth: Width of lines in # of pixels.
|
|
|
+ :return: List of plotted elements.
|
|
|
+ """
|
|
|
|
|
|
- with self.app.proc_container.new(_("Working ...")):
|
|
|
- try:
|
|
|
- self.app.app_obj.new_object("gerber", outname, obj_init)
|
|
|
- except Exception as e:
|
|
|
- log.error("Error on Edited object creation: %s" % str(e))
|
|
|
- # make sure to clean the previous results
|
|
|
- self.results = []
|
|
|
+ if geometry is None:
|
|
|
+ geometry = self.active_tool.geometry
|
|
|
+
|
|
|
+ try:
|
|
|
+ self.shapes.add(shape=geometry.geo, color=color, face_color=color, layer=0, tolerance=self.tolerance)
|
|
|
+ except AttributeError:
|
|
|
+ if type(geometry) == Point:
|
|
|
return
|
|
|
+ if len(color) == 9:
|
|
|
+ color = color[:7] + 'AF'
|
|
|
+ self.shapes.add(shape=geometry, color=color, face_color=color, layer=0, tolerance=self.tolerance)
|
|
|
|
|
|
- # make sure to clean the previous results
|
|
|
- self.results = []
|
|
|
- self.deactivate_grb_editor()
|
|
|
- self.app.inform.emit('[success] %s' % _("Done."))
|
|
|
+ # def start_delayed_plot(self, check_period):
|
|
|
+ # """
|
|
|
+ # This function starts an QTImer and it will periodically check if all the workers finish the plotting functions
|
|
|
+ #
|
|
|
+ # :param check_period: time at which to check periodically if all plots finished to be plotted
|
|
|
+ # :return:
|
|
|
+ # """
|
|
|
+ #
|
|
|
+ # # self.plot_thread = threading.Thread(target=lambda: self.check_plot_finished(check_period))
|
|
|
+ # # self.plot_thread.start()
|
|
|
+ # log.debug("AppGerberEditor --> Delayed Plot started.")
|
|
|
+ # self.plot_thread = QtCore.QTimer()
|
|
|
+ # self.plot_thread.setInterval(check_period)
|
|
|
+ # self.plot_finished.connect(self.setup_ui_after_delayed_plot)
|
|
|
+ # self.plot_thread.timeout.connect(self.check_plot_finished)
|
|
|
+ # self.plot_thread.start()
|
|
|
+ #
|
|
|
+ # def check_plot_finished(self):
|
|
|
+ # """
|
|
|
+ # If all the promises made are finished then all the shapes are in shapes_storage and can be plotted safely and
|
|
|
+ # then the UI is rebuilt accordingly.
|
|
|
+ # :return:
|
|
|
+ # """
|
|
|
+ #
|
|
|
+ # try:
|
|
|
+ # if not self.grb_plot_promises:
|
|
|
+ # self.plot_thread.stop()
|
|
|
+ # self.plot_finished.emit()
|
|
|
+ # log.debug("AppGerberEditor --> delayed_plot finished")
|
|
|
+ # except Exception as e:
|
|
|
+ # traceback.print_exc()
|
|
|
+ #
|
|
|
+ # def setup_ui_after_delayed_plot(self):
|
|
|
+ # self.plot_finished.disconnect()
|
|
|
+ #
|
|
|
+ # # now that we have data, create the GUI interface and add it to the Tool Tab
|
|
|
+ # self.build_ui(first_run=True)
|
|
|
+ # self.plot_all()
|
|
|
+ #
|
|
|
+ # # HACK: enabling/disabling the cursor seams to somehow update the shapes making them more 'solid'
|
|
|
+ # # - perhaps is a bug in VisPy implementation
|
|
|
+ # self.app.app_cursor.enabled = False
|
|
|
+ # self.app.app_cursor.enabled = True
|
|
|
|
|
|
- def on_tool_select(self, tool):
|
|
|
+ def on_zoom_fit(self):
|
|
|
"""
|
|
|
- Behavior of the toolbar. Tool initialization.
|
|
|
+ Callback for zoom-fit request in Gerber Editor
|
|
|
|
|
|
- :rtype : None
|
|
|
+ :return: None
|
|
|
"""
|
|
|
- current_tool = tool
|
|
|
-
|
|
|
- self.app.log.debug("on_tool_select('%s')" % tool)
|
|
|
-
|
|
|
- if self.last_aperture_selected is None and current_tool != 'select':
|
|
|
- # self.draw_app.select_tool('select')
|
|
|
- self.complete = True
|
|
|
- current_tool = 'select'
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. No aperture is selected"))
|
|
|
+ log.debug("AppGerberEditor.on_zoom_fit()")
|
|
|
|
|
|
- # This is to make the group behave as radio group
|
|
|
- if current_tool in self.tools_gerber:
|
|
|
- if self.tools_gerber[current_tool]["button"].isChecked():
|
|
|
- self.app.log.debug("%s is checked." % current_tool)
|
|
|
- for t in self.tools_gerber:
|
|
|
- if t != current_tool:
|
|
|
- self.tools_gerber[t]["button"].setChecked(False)
|
|
|
+ # calculate all the geometry in the edited Gerber object
|
|
|
+ edit_geo = []
|
|
|
+ for ap_code in self.storage_dict:
|
|
|
+ for geo_el in self.storage_dict[ap_code]['geometry']:
|
|
|
+ actual_geo = geo_el.geo
|
|
|
+ if 'solid' in actual_geo:
|
|
|
+ edit_geo.append(actual_geo['solid'])
|
|
|
|
|
|
- # this is where the Editor toolbar classes (button's) are instantiated
|
|
|
- self.active_tool = self.tools_gerber[current_tool]["constructor"](self)
|
|
|
- # self.app.inform.emit(self.active_tool.start_msg)
|
|
|
- else:
|
|
|
- self.app.log.debug("%s is NOT checked." % current_tool)
|
|
|
- for t in self.tools_gerber:
|
|
|
- self.tools_gerber[t]["button"].setChecked(False)
|
|
|
+ all_geo = unary_union(edit_geo)
|
|
|
|
|
|
- self.select_tool('select')
|
|
|
- self.active_tool = FCApertureSelect(self)
|
|
|
+ # calculate the bounds values for the edited Gerber object
|
|
|
+ xmin, ymin, xmax, ymax = all_geo.bounds
|
|
|
|
|
|
- def on_row_selected(self, row, col):
|
|
|
- # if col == 0:
|
|
|
- key_modifier = QtWidgets.QApplication.keyboardModifiers()
|
|
|
- if self.app.defaults["global_mselect_key"] == 'Control':
|
|
|
- modifier_to_use = Qt.ControlModifier
|
|
|
+ if self.app.is_legacy is False:
|
|
|
+ new_rect = Rect(xmin, ymin, xmax, ymax)
|
|
|
+ self.app.plotcanvas.fit_view(rect=new_rect)
|
|
|
else:
|
|
|
- modifier_to_use = Qt.ShiftModifier
|
|
|
+ width = xmax - xmin
|
|
|
+ height = ymax - ymin
|
|
|
+ xmin -= 0.05 * width
|
|
|
+ xmax += 0.05 * width
|
|
|
+ ymin -= 0.05 * height
|
|
|
+ ymax += 0.05 * height
|
|
|
+ self.app.plotcanvas.adjust_axes(xmin, ymin, xmax, ymax)
|
|
|
|
|
|
- if key_modifier == modifier_to_use:
|
|
|
- pass
|
|
|
- else:
|
|
|
- self.selected = []
|
|
|
+ def get_selected(self):
|
|
|
+ """
|
|
|
+ Returns list of shapes that are selected in the editor.
|
|
|
+
|
|
|
+ :return: List of shapes.
|
|
|
+ """
|
|
|
+ # return [shape for shape in self.shape_buffer if shape["selected"]]
|
|
|
+ return self.selected
|
|
|
|
|
|
- try:
|
|
|
- selected_ap_code = self.apertures_table.item(row, 1).text()
|
|
|
- self.last_aperture_selected = copy(selected_ap_code)
|
|
|
+ def delete_selected(self):
|
|
|
+ temp_ref = [s for s in self.selected]
|
|
|
|
|
|
- for obj in self.storage_dict[selected_ap_code]['geometry']:
|
|
|
- self.selected.append(obj)
|
|
|
- except Exception as e:
|
|
|
- self.app.log.debug(str(e))
|
|
|
+ if len(temp_ref) == 0:
|
|
|
+ self.app.inform.emit('[ERROR_NOTCL] %s' %
|
|
|
+ _("Failed. No aperture geometry is selected."))
|
|
|
+ return
|
|
|
|
|
|
- self.plot_all()
|
|
|
+ for shape_sel in temp_ref:
|
|
|
+ self.delete_shape(shape_sel)
|
|
|
|
|
|
- # def toolbar_tool_toggle(self, key):
|
|
|
- # """
|
|
|
- #
|
|
|
- # :param key: key to update in self.options dictionary
|
|
|
- # :return:
|
|
|
- # """
|
|
|
- # self.options[key] = self.sender().isChecked()
|
|
|
- # return self.options[key]
|
|
|
+ self.selected = []
|
|
|
+ self.build_ui()
|
|
|
+ self.app.inform.emit('[success] %s' % _("Done."))
|
|
|
|
|
|
- def on_grb_shape_complete(self, storage=None, specific_shape=None, no_plot=False):
|
|
|
- """
|
|
|
+ def delete_shape(self, geo_el):
|
|
|
+ self.is_modified = True
|
|
|
|
|
|
- :param storage: where to store the shape
|
|
|
- :param specific_shape: optional, the shape to be stored
|
|
|
- :param no_plot: use this if you want the added shape not plotted
|
|
|
- :return:
|
|
|
- """
|
|
|
- self.app.log.debug("on_grb_shape_complete()")
|
|
|
+ if geo_el in self.utility:
|
|
|
+ self.utility.remove(geo_el)
|
|
|
+ return
|
|
|
|
|
|
- if specific_shape:
|
|
|
- geo = specific_shape
|
|
|
- else:
|
|
|
- geo = deepcopy(self.active_tool.geometry)
|
|
|
- if geo is None:
|
|
|
- return
|
|
|
+ for storage in self.storage_dict:
|
|
|
+ try:
|
|
|
+ if geo_el in self.storage_dict[storage]['geometry']:
|
|
|
+ self.storage_dict[storage]['geometry'].remove(geo_el)
|
|
|
+ except KeyError:
|
|
|
+ pass
|
|
|
+ if geo_el in self.selected:
|
|
|
+ self.selected.remove(geo_el) # TODO: Check performance
|
|
|
|
|
|
- if storage is not None:
|
|
|
- # Add shape
|
|
|
- self.add_gerber_shape(geo, storage)
|
|
|
- else:
|
|
|
- stora = self.storage_dict[self.last_aperture_selected]['geometry']
|
|
|
- self.add_gerber_shape(geo, storage=stora)
|
|
|
+ def delete_utility_geometry(self):
|
|
|
+ # for_deletion = [shape for shape in self.shape_buffer if shape.utility]
|
|
|
+ # for_deletion = [shape for shape in self.storage.get_objects() if shape.utility]
|
|
|
+ for_deletion = [geo_el for geo_el in self.utility]
|
|
|
+ for geo_el in for_deletion:
|
|
|
+ self.delete_shape(geo_el)
|
|
|
|
|
|
- # Remove any utility shapes
|
|
|
- self.delete_utility_geometry()
|
|
|
self.tool_shape.clear(update=True)
|
|
|
+ self.tool_shape.redraw()
|
|
|
|
|
|
- if no_plot is False:
|
|
|
- # Re-plot and reset tool.
|
|
|
- self.plot_all()
|
|
|
+ def on_delete_btn(self):
|
|
|
+ self.delete_selected()
|
|
|
+ self.plot_all()
|
|
|
|
|
|
- def add_gerber_shape(self, shape_element, storage):
|
|
|
+ def select_tool(self, toolname):
|
|
|
"""
|
|
|
- Adds a shape to the shape storage.
|
|
|
+ Selects a drawing tool. Impacts the object and appGUI.
|
|
|
|
|
|
- :param shape_element: Shape to be added.
|
|
|
- :type shape_element: DrawToolShape or DrawToolUtilityShape Geometry is stored as a dict with keys: solid,
|
|
|
- follow, clear, each value being a list of Shapely objects. The dict can have at least one of the mentioned keys
|
|
|
- :param storage: Where to store the shape
|
|
|
+ :param toolname: Name of the tool.
|
|
|
:return: None
|
|
|
"""
|
|
|
- # List of DrawToolShape?
|
|
|
+ self.tools_gerber[toolname]["button"].setChecked(True)
|
|
|
+ self.on_tool_select(toolname)
|
|
|
|
|
|
- if isinstance(shape_element, list):
|
|
|
- for subshape in shape_element:
|
|
|
- self.add_gerber_shape(subshape, storage)
|
|
|
- return
|
|
|
+ def set_selected(self, geo_el):
|
|
|
|
|
|
- assert isinstance(shape_element, DrawToolShape), \
|
|
|
- "Expected a DrawToolShape, got %s" % str(type(shape_element))
|
|
|
+ # Remove and add to the end.
|
|
|
+ if geo_el in self.selected:
|
|
|
+ self.selected.remove(geo_el)
|
|
|
|
|
|
- assert shape_element.geo is not None, \
|
|
|
- "Shape object has empty geometry (None)"
|
|
|
+ self.selected.append(geo_el)
|
|
|
|
|
|
- assert(isinstance(shape_element.geo, list) and len(shape_element.geo) > 0) or not \
|
|
|
- isinstance(shape_element.geo, list), "Shape objects has empty geometry ([])"
|
|
|
+ def set_unselected(self, geo_el):
|
|
|
+ if geo_el in self.selected:
|
|
|
+ self.selected.remove(geo_el)
|
|
|
|
|
|
- if isinstance(shape_element, DrawToolUtilityShape):
|
|
|
- self.utility.append(shape_element)
|
|
|
+ def on_array_type_combo(self):
|
|
|
+ if self.ui.array_type_combo.currentIndex() == 0:
|
|
|
+ self.ui.array_circular_frame.hide()
|
|
|
+ self.ui.array_linear_frame.show()
|
|
|
else:
|
|
|
- storage.append(shape_element)
|
|
|
-
|
|
|
- def on_canvas_click(self, event):
|
|
|
- """
|
|
|
- event.x and .y have canvas coordinates
|
|
|
- event.xdata and .ydata have plot coordinates
|
|
|
+ self.delete_utility_geometry()
|
|
|
+ self.ui.array_circular_frame.show()
|
|
|
+ self.ui.array_linear_frame.hide()
|
|
|
+ self.app.inform.emit(_("Click on the circular array Center position"))
|
|
|
|
|
|
- :param event: Event object dispatched by VisPy
|
|
|
- :return: None
|
|
|
- """
|
|
|
- if self.app.is_legacy is False:
|
|
|
- event_pos = event.pos
|
|
|
- # event_is_dragging = event.is_dragging
|
|
|
- # right_button = 2
|
|
|
+ def on_linear_angle_radio(self):
|
|
|
+ val = self.ui.pad_axis_radio.get_value()
|
|
|
+ if val == 'A':
|
|
|
+ self.ui.linear_angle_spinner.show()
|
|
|
+ self.ui.linear_angle_label.show()
|
|
|
else:
|
|
|
- event_pos = (event.xdata, event.ydata)
|
|
|
- # event_is_dragging = self.app.plotcanvas.is_dragging
|
|
|
- # right_button = 3
|
|
|
+ self.ui.linear_angle_spinner.hide()
|
|
|
+ self.ui.linear_angle_label.hide()
|
|
|
|
|
|
- self.pos = self.canvas.translate_coords(event_pos)
|
|
|
+ def on_copy_button(self):
|
|
|
+ self.select_tool('copy')
|
|
|
+ return
|
|
|
|
|
|
- if self.app.grid_status():
|
|
|
- self.pos = self.app.geo_editor.snap(self.pos[0], self.pos[1])
|
|
|
- else:
|
|
|
- self.pos = (self.pos[0], self.pos[1])
|
|
|
+ def on_move_button(self):
|
|
|
+ self.select_tool('move')
|
|
|
+ return
|
|
|
|
|
|
- if event.button == 1:
|
|
|
- self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
|
|
- "%.4f " % (0, 0))
|
|
|
+ def on_pad_add(self):
|
|
|
+ self.select_tool('pad')
|
|
|
|
|
|
- # Selection with left mouse button
|
|
|
- if self.active_tool is not None:
|
|
|
- modifiers = QtWidgets.QApplication.keyboardModifiers()
|
|
|
+ def on_pad_add_array(self):
|
|
|
+ self.select_tool('array')
|
|
|
|
|
|
- # If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard
|
|
|
- if modifiers == QtCore.Qt.ShiftModifier:
|
|
|
- self.app.clipboard.setText(
|
|
|
- self.app.defaults["global_point_clipboard_format"] %
|
|
|
- (self.decimals, self.pos[0], self.decimals, self.pos[1])
|
|
|
- )
|
|
|
- self.app.inform.emit('[success] %s' % _("Coordinates copied to clipboard."))
|
|
|
- return
|
|
|
+ def on_track_add(self):
|
|
|
+ self.select_tool('track')
|
|
|
|
|
|
- # Dispatch event to active_tool
|
|
|
- self.active_tool.click(self.app.geo_editor.snap(self.pos[0], self.pos[1]))
|
|
|
+ def on_region_add(self):
|
|
|
+ self.select_tool('region')
|
|
|
|
|
|
- # If it is a shape generating tool
|
|
|
- if isinstance(self.active_tool, FCShapeTool) and self.active_tool.complete:
|
|
|
- if self.current_storage is not None:
|
|
|
- self.on_grb_shape_complete(self.current_storage)
|
|
|
- self.build_ui()
|
|
|
+ def on_poligonize(self):
|
|
|
+ self.select_tool('poligonize')
|
|
|
|
|
|
- # MS: always return to the Select Tool if modifier key is not pressed
|
|
|
- # else return to the current tool
|
|
|
- key_modifier = QtWidgets.QApplication.keyboardModifiers()
|
|
|
- if self.app.defaults["global_mselect_key"] == 'Control':
|
|
|
- modifier_to_use = Qt.ControlModifier
|
|
|
- else:
|
|
|
- modifier_to_use = Qt.ShiftModifier
|
|
|
+ def on_disc_add(self):
|
|
|
+ self.select_tool('disc')
|
|
|
|
|
|
- # if modifier key is pressed then we add to the selected list the current shape but if it's already
|
|
|
- # in the selected list, we removed it. Therefore first click selects, second deselects.
|
|
|
- if key_modifier == modifier_to_use:
|
|
|
- self.select_tool(self.active_tool.name)
|
|
|
- else:
|
|
|
- # return to Select tool but not for FCPad
|
|
|
- if isinstance(self.active_tool, FCPad):
|
|
|
- self.select_tool(self.active_tool.name)
|
|
|
- else:
|
|
|
- self.select_tool("select")
|
|
|
- return
|
|
|
+ def on_add_semidisc(self):
|
|
|
+ self.select_tool('semidisc')
|
|
|
|
|
|
- # if isinstance(self.active_tool, FCApertureSelect):
|
|
|
- # self.plot_all()
|
|
|
- else:
|
|
|
- self.app.log.debug("No active tool to respond to click!")
|
|
|
+ def on_buffer(self):
|
|
|
+ buff_value = 0.01
|
|
|
+ log.debug("AppGerberEditor.on_buffer()")
|
|
|
|
|
|
- def on_grb_click_release(self, event):
|
|
|
- self.modifiers = QtWidgets.QApplication.keyboardModifiers()
|
|
|
- if self.app.is_legacy is False:
|
|
|
- event_pos = event.pos
|
|
|
- # event_is_dragging = event.is_dragging
|
|
|
- right_button = 2
|
|
|
- else:
|
|
|
- event_pos = (event.xdata, event.ydata)
|
|
|
- # event_is_dragging = self.app.plotcanvas.is_dragging
|
|
|
- right_button = 3
|
|
|
+ try:
|
|
|
+ buff_value = float(self.ui.buffer_distance_entry.get_value())
|
|
|
+ except ValueError:
|
|
|
+ # try to convert comma to decimal point. if it's still not working error message and return
|
|
|
+ try:
|
|
|
+ buff_value = float(self.ui.buffer_distance_entry.get_value().replace(',', '.'))
|
|
|
+ self.ui.buffer_distance_entry.set_value(buff_value)
|
|
|
+ except ValueError:
|
|
|
+ self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
+ _("Buffer distance value is missing or wrong format. Add it and retry."))
|
|
|
+ return
|
|
|
|
|
|
- pos_canvas = self.canvas.translate_coords(event_pos)
|
|
|
- if self.app.grid_status():
|
|
|
- pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
|
|
|
- else:
|
|
|
- pos = (pos_canvas[0], pos_canvas[1])
|
|
|
+ # the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
|
|
|
+ # I populated the combobox such that the index coincide with the join styles value (which is really an INT)
|
|
|
+ join_style = self.ui.buffer_corner_cb.currentIndex() + 1
|
|
|
|
|
|
- # if the released mouse button was RMB then test if it was a panning motion or not, if not it was a context
|
|
|
- # canvas menu
|
|
|
- try:
|
|
|
- if event.button == right_button: # right click
|
|
|
- if self.app.ui.popMenu.mouse_is_panning is False:
|
|
|
- if self.in_action is False:
|
|
|
- try:
|
|
|
- QtGui.QGuiApplication.restoreOverrideCursor()
|
|
|
- except Exception as e:
|
|
|
- log.debug("AppGerberEditor.on_grb_click_release() --> %s" % str(e))
|
|
|
+ def buffer_recursion(geom_el, selection):
|
|
|
+ if type(geom_el) == list:
|
|
|
+ geoms = []
|
|
|
+ for local_geom in geom_el:
|
|
|
+ geoms.append(buffer_recursion(local_geom, selection=selection))
|
|
|
+ return geoms
|
|
|
+ else:
|
|
|
+ if geom_el in selection:
|
|
|
+ geometric_data = geom_el.geo
|
|
|
+ buffered_geom_el = {}
|
|
|
+ if 'solid' in geometric_data:
|
|
|
+ buffered_geom_el['solid'] = geometric_data['solid'].buffer(buff_value, join_style=join_style)
|
|
|
+ if 'follow' in geometric_data:
|
|
|
+ buffered_geom_el['follow'] = geometric_data['follow'].buffer(buff_value, join_style=join_style)
|
|
|
+ if 'clear' in geometric_data:
|
|
|
+ buffered_geom_el['clear'] = geometric_data['clear'].buffer(buff_value, join_style=join_style)
|
|
|
+ return DrawToolShape(buffered_geom_el)
|
|
|
+ else:
|
|
|
+ return geom_el
|
|
|
+
|
|
|
+ if not self.ui.apertures_table.selectedItems():
|
|
|
+ self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
+ _("No aperture to buffer. Select at least one aperture and try again."))
|
|
|
+ return
|
|
|
|
|
|
- if self.active_tool.complete is False and not isinstance(self.active_tool, FCApertureSelect):
|
|
|
- self.active_tool.complete = True
|
|
|
- self.in_action = False
|
|
|
- self.delete_utility_geometry()
|
|
|
- self.app.inform.emit('[success] %s' %
|
|
|
- _("Done."))
|
|
|
- self.select_tool('select')
|
|
|
- else:
|
|
|
- self.app.cursor = QtGui.QCursor()
|
|
|
- self.app.populate_cmenu_grids()
|
|
|
- self.app.ui.popMenu.popup(self.app.cursor.pos())
|
|
|
- else:
|
|
|
- # if right click on canvas and the active tool need to be finished (like Path or Polygon)
|
|
|
- # right mouse click will finish the action
|
|
|
- if isinstance(self.active_tool, FCShapeTool):
|
|
|
- if isinstance(self.active_tool, FCTrack):
|
|
|
- self.active_tool.make()
|
|
|
- else:
|
|
|
- self.active_tool.click(self.app.geo_editor.snap(self.x, self.y))
|
|
|
- self.active_tool.make()
|
|
|
- if self.active_tool.complete:
|
|
|
- self.on_grb_shape_complete()
|
|
|
- self.app.inform.emit('[success] %s' % _("Done."))
|
|
|
+ for x in self.ui.apertures_table.selectedItems():
|
|
|
+ try:
|
|
|
+ apcode = self.ui.apertures_table.item(x.row(), 1).text()
|
|
|
|
|
|
- # MS: always return to the Select Tool if modifier key is not pressed
|
|
|
- # else return to the current tool but not for FCTrack
|
|
|
+ temp_storage = deepcopy(buffer_recursion(self.storage_dict[apcode]['geometry'], self.selected))
|
|
|
+ self.storage_dict[apcode]['geometry'] = []
|
|
|
+ self.storage_dict[apcode]['geometry'] = temp_storage
|
|
|
+ except Exception as e:
|
|
|
+ log.debug("AppGerberEditor.buffer() --> %s" % str(e))
|
|
|
+ self.app.inform.emit('[ERROR_NOTCL] %s\n%s' % (_("Failed."), str(traceback.print_exc())))
|
|
|
+ return
|
|
|
|
|
|
- if isinstance(self.active_tool, FCTrack):
|
|
|
- self.select_tool(self.active_tool.name)
|
|
|
- else:
|
|
|
- key_modifier = QtWidgets.QApplication.keyboardModifiers()
|
|
|
- if (self.app.defaults["global_mselect_key"] == 'Control' and
|
|
|
- key_modifier == Qt.ControlModifier) or \
|
|
|
- (self.app.defaults["global_mselect_key"] == 'Shift' and
|
|
|
- key_modifier == Qt.ShiftModifier):
|
|
|
+ self.plot_all()
|
|
|
+ self.app.inform.emit('[success] %s' % _("Done."))
|
|
|
|
|
|
- self.select_tool(self.active_tool.name)
|
|
|
- else:
|
|
|
- self.select_tool("select")
|
|
|
- except Exception as e:
|
|
|
- log.warning("AppGerberEditor.on_grb_click_release() RMB click --> Error: %s" % str(e))
|
|
|
- raise
|
|
|
+ def on_scale(self):
|
|
|
+ scale_factor = 1.0
|
|
|
+ log.debug("AppGerberEditor.on_scale()")
|
|
|
|
|
|
- # if the released mouse button was LMB then test if we had a right-to-left selection or a left-to-right
|
|
|
- # selection and then select a type of selection ("enclosing" or "touching")
|
|
|
try:
|
|
|
- if event.button == 1: # left click
|
|
|
- if self.app.selection_type is not None:
|
|
|
- self.draw_selection_area_handler(self.pos, pos, self.app.selection_type)
|
|
|
- self.app.selection_type = None
|
|
|
+ scale_factor = float(self.ui.scale_factor_entry.get_value())
|
|
|
+ except ValueError:
|
|
|
+ # try to convert comma to decimal point. if it's still not working error message and return
|
|
|
+ try:
|
|
|
+ scale_factor = float(self.ui.scale_factor_entry.get_value().replace(',', '.'))
|
|
|
+ self.ui.scale_factor_entry.set_value(scale_factor)
|
|
|
+ except ValueError:
|
|
|
+ self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
+ _("Scale factor value is missing or wrong format. Add it and retry."))
|
|
|
+ return
|
|
|
|
|
|
- elif isinstance(self.active_tool, FCApertureSelect):
|
|
|
- self.active_tool.click_release((self.pos[0], self.pos[1]))
|
|
|
+ def scale_recursion(geom_el, selection):
|
|
|
+ if type(geom_el) == list:
|
|
|
+ geoms = []
|
|
|
+ for local_geom in geom_el:
|
|
|
+ geoms.append(scale_recursion(local_geom, selection=selection))
|
|
|
+ return geoms
|
|
|
+ else:
|
|
|
+ if geom_el in selection:
|
|
|
+ geometric_data = geom_el.geo
|
|
|
+ scaled_geom_el = {}
|
|
|
+ if 'solid' in geometric_data:
|
|
|
+ scaled_geom_el['solid'] = affinity.scale(
|
|
|
+ geometric_data['solid'], scale_factor, scale_factor, origin='center'
|
|
|
+ )
|
|
|
+ if 'follow' in geometric_data:
|
|
|
+ scaled_geom_el['follow'] = affinity.scale(
|
|
|
+ geometric_data['follow'], scale_factor, scale_factor, origin='center'
|
|
|
+ )
|
|
|
+ if 'clear' in geometric_data:
|
|
|
+ scaled_geom_el['clear'] = affinity.scale(
|
|
|
+ geometric_data['clear'], scale_factor, scale_factor, origin='center'
|
|
|
+ )
|
|
|
|
|
|
- # # if there are selected objects then plot them
|
|
|
- # if self.selected:
|
|
|
- # self.plot_all()
|
|
|
- except Exception as e:
|
|
|
- log.warning("AppGerberEditor.on_grb_click_release() LMB click --> Error: %s" % str(e))
|
|
|
- raise
|
|
|
+ return DrawToolShape(scaled_geom_el)
|
|
|
+ else:
|
|
|
+ return geom_el
|
|
|
|
|
|
- def draw_selection_area_handler(self, start_pos, end_pos, sel_type):
|
|
|
- """
|
|
|
- :param start_pos: mouse position when the selection LMB click was done
|
|
|
- :param end_pos: mouse position when the left mouse button is released
|
|
|
- :param sel_type: if True it's a left to right selection (enclosure), if False it's a 'touch' selection
|
|
|
- :return:
|
|
|
- """
|
|
|
+ if not self.ui.apertures_table.selectedItems():
|
|
|
+ self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
+ _("No aperture to scale. Select at least one aperture and try again."))
|
|
|
+ return
|
|
|
|
|
|
- poly_selection = Polygon([start_pos, (end_pos[0], start_pos[1]), end_pos, (start_pos[0], end_pos[1])])
|
|
|
- sel_aperture = set()
|
|
|
- self.apertures_table.clearSelection()
|
|
|
+ for x in self.ui.apertures_table.selectedItems():
|
|
|
+ try:
|
|
|
+ apcode = self.ui.apertures_table.item(x.row(), 1).text()
|
|
|
|
|
|
- self.app.delete_selection_shape()
|
|
|
- for storage in self.storage_dict:
|
|
|
- for obj in self.storage_dict[storage]['geometry']:
|
|
|
- if 'solid' in obj.geo:
|
|
|
- geometric_data = obj.geo['solid']
|
|
|
- if (sel_type is True and poly_selection.contains(geometric_data)) or \
|
|
|
- (sel_type is False and poly_selection.intersects(geometric_data)):
|
|
|
- if self.key == self.app.defaults["global_mselect_key"]:
|
|
|
- if obj in self.selected:
|
|
|
- self.selected.remove(obj)
|
|
|
- else:
|
|
|
- # add the object to the selected shapes
|
|
|
- self.selected.append(obj)
|
|
|
- sel_aperture.add(storage)
|
|
|
- else:
|
|
|
- self.selected.append(obj)
|
|
|
- sel_aperture.add(storage)
|
|
|
+ temp_storage = deepcopy(scale_recursion(self.storage_dict[apcode]['geometry'], self.selected))
|
|
|
+ self.storage_dict[apcode]['geometry'] = []
|
|
|
+ self.storage_dict[apcode]['geometry'] = temp_storage
|
|
|
|
|
|
- try:
|
|
|
- self.apertures_table.cellPressed.disconnect()
|
|
|
- except Exception as e:
|
|
|
- log.debug("AppGerberEditor.draw_selection_Area_handler() --> %s" % str(e))
|
|
|
- # select the aperture code of the selected geometry, in the tool table
|
|
|
- self.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
|
|
|
- for aper in sel_aperture:
|
|
|
- for row_to_sel in range(self.apertures_table.rowCount()):
|
|
|
- if str(aper) == self.apertures_table.item(row_to_sel, 1).text():
|
|
|
- if row_to_sel not in set(index.row() for index in self.apertures_table.selectedIndexes()):
|
|
|
- self.apertures_table.selectRow(row_to_sel)
|
|
|
- self.last_aperture_selected = aper
|
|
|
- self.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
|
|
+ except Exception as e:
|
|
|
+ log.debug("AppGerberEditor.on_scale() --> %s" % str(e))
|
|
|
|
|
|
- self.apertures_table.cellPressed.connect(self.on_row_selected)
|
|
|
self.plot_all()
|
|
|
+ self.app.inform.emit('[success] %s' % _("Done."))
|
|
|
|
|
|
- def on_canvas_move(self, event):
|
|
|
- """
|
|
|
- Called on 'mouse_move' event
|
|
|
+ def on_markarea(self):
|
|
|
+ # clear previous marking
|
|
|
+ self.ma_annotation.clear(update=True)
|
|
|
|
|
|
- event.pos have canvas screen coordinates
|
|
|
+ self.units = self.app.defaults['units'].upper()
|
|
|
|
|
|
- :param event: Event object dispatched by VisPy SceneCavas
|
|
|
- :return: None
|
|
|
- """
|
|
|
- if self.app.is_legacy is False:
|
|
|
- event_pos = event.pos
|
|
|
- event_is_dragging = event.is_dragging
|
|
|
- right_button = 2
|
|
|
- else:
|
|
|
- event_pos = (event.xdata, event.ydata)
|
|
|
- event_is_dragging = self.app.plotcanvas.is_dragging
|
|
|
- right_button = 3
|
|
|
+ text = []
|
|
|
+ position = []
|
|
|
|
|
|
- pos_canvas = self.canvas.translate_coords(event_pos)
|
|
|
- event.xdata, event.ydata = pos_canvas[0], pos_canvas[1]
|
|
|
+ for apcode in self.storage_dict:
|
|
|
+ if 'geometry' in self.storage_dict[apcode]:
|
|
|
+ for geo_el in self.storage_dict[apcode]['geometry']:
|
|
|
+ if 'solid' in geo_el.geo:
|
|
|
+ area = geo_el.geo['solid'].area
|
|
|
+ try:
|
|
|
+ upper_threshold_val = self.ui.ma_upper_threshold_entry.get_value()
|
|
|
+ except Exception:
|
|
|
+ return
|
|
|
|
|
|
- self.x = event.xdata
|
|
|
- self.y = event.ydata
|
|
|
+ try:
|
|
|
+ lower_threshold_val = self.ui.ma_lower_threshold_entry.get_value()
|
|
|
+ except Exception:
|
|
|
+ lower_threshold_val = 0.0
|
|
|
|
|
|
- self.app.ui.popMenu.mouse_is_panning = False
|
|
|
+ if float(upper_threshold_val) > area > float(lower_threshold_val):
|
|
|
+ current_pos = geo_el.geo['solid'].exterior.coords[-1]
|
|
|
+ text_elem = '%.*f' % (self.decimals, area)
|
|
|
+ text.append(text_elem)
|
|
|
+ position.append(current_pos)
|
|
|
+ self.geo_to_delete.append(geo_el)
|
|
|
|
|
|
- # if the RMB is clicked and mouse is moving over plot then 'panning_action' is True
|
|
|
- if event.button == right_button and event_is_dragging == 1:
|
|
|
- self.app.ui.popMenu.mouse_is_panning = True
|
|
|
- return
|
|
|
+ if text:
|
|
|
+ self.ma_annotation.set(text=text, pos=position, visible=True,
|
|
|
+ font_size=self.app.defaults["cncjob_annotation_fontsize"],
|
|
|
+ color='#000000FF')
|
|
|
+ self.app.inform.emit('[success] %s' %
|
|
|
+ _("Polygons marked."))
|
|
|
+ else:
|
|
|
+ self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
+ _("No polygons were marked. None fit within the limits."))
|
|
|
|
|
|
- try:
|
|
|
- x = float(event.xdata)
|
|
|
- y = float(event.ydata)
|
|
|
- except TypeError:
|
|
|
- return
|
|
|
+ def delete_marked_polygons(self):
|
|
|
+ for shape_sel in self.geo_to_delete:
|
|
|
+ self.delete_shape(shape_sel)
|
|
|
|
|
|
- if self.active_tool is None:
|
|
|
- return
|
|
|
+ self.build_ui()
|
|
|
+ self.plot_all()
|
|
|
+ self.app.inform.emit('[success] %s' % _("Done."))
|
|
|
|
|
|
- # # ## Snap coordinates
|
|
|
- if self.app.grid_status():
|
|
|
- x, y = self.app.geo_editor.snap(x, y)
|
|
|
+ def on_eraser(self):
|
|
|
+ self.select_tool('eraser')
|
|
|
|
|
|
- # Update cursor
|
|
|
- self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color=self.app.cursor_color_3D,
|
|
|
- edge_width=self.app.defaults["global_cursor_width"],
|
|
|
- size=self.app.defaults["global_cursor_size"])
|
|
|
+ def on_transform(self):
|
|
|
+ if type(self.active_tool) == FCTransform:
|
|
|
+ self.select_tool('select')
|
|
|
+ else:
|
|
|
+ self.select_tool('transform')
|
|
|
|
|
|
- self.snap_x = x
|
|
|
- self.snap_y = y
|
|
|
+ def hide_tool(self, tool_name):
|
|
|
+ # self.app.ui.notebook.setTabText(2, _("Tools"))
|
|
|
+ try:
|
|
|
+ if tool_name == 'all':
|
|
|
+ self.ui.apertures_frame.hide()
|
|
|
+ if tool_name == 'select':
|
|
|
+ self.ui.apertures_frame.show()
|
|
|
+ if tool_name == 'buffer' or tool_name == 'all':
|
|
|
+ self.ui.buffer_tool_frame.hide()
|
|
|
+ if tool_name == 'scale' or tool_name == 'all':
|
|
|
+ self.ui.scale_tool_frame.hide()
|
|
|
+ if tool_name == 'markarea' or tool_name == 'all':
|
|
|
+ self.ui.ma_tool_frame.hide()
|
|
|
+ except Exception as e:
|
|
|
+ log.debug("AppGerberEditor.hide_tool() --> %s" % str(e))
|
|
|
+ self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
|
|
|
|
|
|
- self.app.mouse = [x, y]
|
|
|
|
|
|
- if self.pos is None:
|
|
|
- self.pos = (0, 0)
|
|
|
- self.app.dx = x - self.pos[0]
|
|
|
- self.app.dy = y - self.pos[1]
|
|
|
+class AppGerberEditorUI:
|
|
|
+ def __init__(self, app):
|
|
|
+ self.app = app
|
|
|
|
|
|
- # # update the position label in the infobar since the APP mouse event handlers are disconnected
|
|
|
- self.app.ui.position_label.setText(" <b>X</b>: %.4f "
|
|
|
- "<b>Y</b>: %.4f " % (x, y))
|
|
|
+ # Number of decimals used by tools in this class
|
|
|
+ self.decimals = self.app.decimals
|
|
|
|
|
|
- # update the reference position label in the infobar since the APP mouse event handlers are disconnected
|
|
|
- self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f <b>Dy</b>: "
|
|
|
- "%.4f " % (self.app.dx, self.app.dy))
|
|
|
+ # ## Current application units in Upper Case
|
|
|
+ self.units = self.app.defaults['units'].upper()
|
|
|
+
|
|
|
+ self.grb_edit_widget = QtWidgets.QWidget()
|
|
|
+
|
|
|
+ layout = QtWidgets.QVBoxLayout()
|
|
|
+ self.grb_edit_widget.setLayout(layout)
|
|
|
|
|
|
- units = self.app.defaults["units"].lower()
|
|
|
- self.app.plotcanvas.text_hud.text = \
|
|
|
- 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format(
|
|
|
- self.app.dx, units, self.app.dy, units, x, units, y, units)
|
|
|
+ # Page Title box (spacing between children)
|
|
|
+ self.title_box = QtWidgets.QHBoxLayout()
|
|
|
+ layout.addLayout(self.title_box)
|
|
|
|
|
|
- self.update_utility_geometry(data=(x, y))
|
|
|
+ # Page Title icon
|
|
|
+ pixmap = QtGui.QPixmap(self.app.resource_location + '/flatcam_icon32.png')
|
|
|
+ self.icon = QtWidgets.QLabel()
|
|
|
+ self.icon.setPixmap(pixmap)
|
|
|
+ self.title_box.addWidget(self.icon, stretch=0)
|
|
|
|
|
|
- # # ## Selection area on canvas section # ##
|
|
|
- if event_is_dragging == 1 and event.button == 1:
|
|
|
- # I make an exception for FCRegion and FCTrack because clicking and dragging while making regions can
|
|
|
- # create strange issues like missing a point in a track/region
|
|
|
- if isinstance(self.active_tool, FCRegion) or isinstance(self.active_tool, FCTrack):
|
|
|
- pass
|
|
|
- else:
|
|
|
- dx = pos_canvas[0] - self.pos[0]
|
|
|
- self.app.delete_selection_shape()
|
|
|
- if dx < 0:
|
|
|
- self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y),
|
|
|
- color=self.app.defaults["global_alt_sel_line"],
|
|
|
- face_color=self.app.defaults['global_alt_sel_fill'])
|
|
|
- self.app.selection_type = False
|
|
|
- else:
|
|
|
- self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y))
|
|
|
- self.app.selection_type = True
|
|
|
- else:
|
|
|
- self.app.selection_type = None
|
|
|
+ # Title label
|
|
|
+ self.title_label = QtWidgets.QLabel("<font size=5><b>%s</b></font>" % _('Gerber Editor'))
|
|
|
+ self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
|
|
|
+ self.title_box.addWidget(self.title_label, stretch=1)
|
|
|
|
|
|
- def update_utility_geometry(self, data):
|
|
|
- # # ## Utility geometry (animated)
|
|
|
- geo = self.active_tool.utility_geometry(data=data)
|
|
|
+ # Object name
|
|
|
+ self.name_box = QtWidgets.QHBoxLayout()
|
|
|
+ layout.addLayout(self.name_box)
|
|
|
+ name_label = QtWidgets.QLabel(_("Name:"))
|
|
|
+ self.name_box.addWidget(name_label)
|
|
|
+ self.name_entry = FCEntry()
|
|
|
+ self.name_box.addWidget(self.name_entry)
|
|
|
|
|
|
- if isinstance(geo, DrawToolShape) and geo.geo is not None:
|
|
|
- # Remove any previous utility shape
|
|
|
- self.tool_shape.clear(update=True)
|
|
|
- self.draw_utility_geometry(geo=geo)
|
|
|
+ # Box for custom widgets
|
|
|
+ # This gets populated in offspring implementations.
|
|
|
+ self.custom_box = QtWidgets.QVBoxLayout()
|
|
|
+ layout.addLayout(self.custom_box)
|
|
|
|
|
|
- def draw_utility_geometry(self, geo):
|
|
|
- if type(geo.geo) == list:
|
|
|
- for el in geo.geo:
|
|
|
- geometric_data = el['solid']
|
|
|
- # Add the new utility shape
|
|
|
- self.tool_shape.add(
|
|
|
- shape=geometric_data, color=(self.app.defaults["global_draw_color"] + '80'),
|
|
|
- # face_color=self.app.defaults['global_alt_sel_fill'],
|
|
|
- update=False, layer=0, tolerance=None
|
|
|
- )
|
|
|
- else:
|
|
|
- geometric_data = geo.geo['solid']
|
|
|
- # Add the new utility shape
|
|
|
- self.tool_shape.add(
|
|
|
- shape=geometric_data,
|
|
|
- color=(self.app.defaults["global_draw_color"] + '80'),
|
|
|
- # face_color=self.app.defaults['global_alt_sel_fill'],
|
|
|
- update=False, layer=0, tolerance=None
|
|
|
- )
|
|
|
+ # #########################
|
|
|
+ # ### Gerber Apertures ####
|
|
|
+ # #########################
|
|
|
+ self.apertures_table_label = QtWidgets.QLabel('<b>%s:</b>' % _('Apertures'))
|
|
|
+ self.apertures_table_label.setToolTip(
|
|
|
+ _("Apertures Table for the Gerber Object.")
|
|
|
+ )
|
|
|
+ self.custom_box.addWidget(self.apertures_table_label)
|
|
|
|
|
|
- self.tool_shape.redraw()
|
|
|
+ self.apertures_table = FCTable()
|
|
|
+ # delegate = SpinBoxDelegate(units=self.units)
|
|
|
+ # self.apertures_table.setItemDelegateForColumn(1, delegate)
|
|
|
|
|
|
- def plot_all(self):
|
|
|
- """
|
|
|
- Plots all shapes in the editor.
|
|
|
+ self.custom_box.addWidget(self.apertures_table)
|
|
|
|
|
|
- :return: None
|
|
|
- :rtype: None
|
|
|
- """
|
|
|
- with self.app.proc_container.new('%s ...' % _("Plotting")):
|
|
|
- self.shapes.clear(update=True)
|
|
|
+ self.apertures_table.setColumnCount(5)
|
|
|
+ self.apertures_table.setHorizontalHeaderLabels(['#', _('Code'), _('Type'), _('Size'), _('Dim')])
|
|
|
+ self.apertures_table.setSortingEnabled(False)
|
|
|
+ self.apertures_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
|
|
|
|
|
- for storage in self.storage_dict:
|
|
|
- # fix for apertures with no geometry inside
|
|
|
- if 'geometry' in self.storage_dict[storage]:
|
|
|
- for elem in self.storage_dict[storage]['geometry']:
|
|
|
- if 'solid' in elem.geo:
|
|
|
- geometric_data = elem.geo['solid']
|
|
|
- if geometric_data is None:
|
|
|
- continue
|
|
|
+ self.apertures_table.horizontalHeaderItem(0).setToolTip(
|
|
|
+ _("Index"))
|
|
|
+ self.apertures_table.horizontalHeaderItem(1).setToolTip(
|
|
|
+ _("Aperture Code"))
|
|
|
+ self.apertures_table.horizontalHeaderItem(2).setToolTip(
|
|
|
+ _("Type of aperture: circular, rectangle, macros etc"))
|
|
|
+ self.apertures_table.horizontalHeaderItem(4).setToolTip(
|
|
|
+ _("Aperture Size:"))
|
|
|
+ self.apertures_table.horizontalHeaderItem(4).setToolTip(
|
|
|
+ _("Aperture Dimensions:\n"
|
|
|
+ " - (width, height) for R, O type.\n"
|
|
|
+ " - (dia, nVertices) for P type"))
|
|
|
|
|
|
- if elem in self.selected:
|
|
|
- self.plot_shape(geometry=geometric_data,
|
|
|
- color=self.app.defaults['global_sel_draw_color'] + 'FF',
|
|
|
- linewidth=2)
|
|
|
- else:
|
|
|
- self.plot_shape(geometry=geometric_data,
|
|
|
- color=self.app.defaults['global_draw_color'] + 'FF')
|
|
|
+ self.empty_label = QtWidgets.QLabel('')
|
|
|
+ self.custom_box.addWidget(self.empty_label)
|
|
|
|
|
|
- if self.utility:
|
|
|
- for elem in self.utility:
|
|
|
- geometric_data = elem.geo['solid']
|
|
|
- self.plot_shape(geometry=geometric_data, linewidth=1)
|
|
|
- continue
|
|
|
+ # add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Apertures widgets
|
|
|
+ # this way I can hide/show the frame
|
|
|
+ self.apertures_frame = QtWidgets.QFrame()
|
|
|
+ self.apertures_frame.setContentsMargins(0, 0, 0, 0)
|
|
|
+ self.custom_box.addWidget(self.apertures_frame)
|
|
|
+ self.apertures_box = QtWidgets.QVBoxLayout()
|
|
|
+ self.apertures_box.setContentsMargins(0, 0, 0, 0)
|
|
|
+ self.apertures_frame.setLayout(self.apertures_box)
|
|
|
|
|
|
- self.shapes.redraw()
|
|
|
+ # # ## Add/Delete an new Aperture ## ##
|
|
|
|
|
|
- def plot_shape(self, geometry=None, color='#000000FF', linewidth=1):
|
|
|
- """
|
|
|
- Plots a geometric object or list of objects without rendering. Plotted objects
|
|
|
- are returned as a list. This allows for efficient/animated rendering.
|
|
|
+ grid1 = QtWidgets.QGridLayout()
|
|
|
+ self.apertures_box.addLayout(grid1)
|
|
|
+ grid1.setColumnStretch(0, 0)
|
|
|
+ grid1.setColumnStretch(1, 1)
|
|
|
|
|
|
- :param geometry: Geometry to be plotted (Any Shapely.geom kind or list of such)
|
|
|
- :param color: Shape color
|
|
|
- :param linewidth: Width of lines in # of pixels.
|
|
|
- :return: List of plotted elements.
|
|
|
- """
|
|
|
+ apcode_lbl = QtWidgets.QLabel('%s:' % _('Aperture Code'))
|
|
|
+ apcode_lbl.setToolTip(_("Code for the new aperture"))
|
|
|
+ grid1.addWidget(apcode_lbl, 1, 0)
|
|
|
|
|
|
- if geometry is None:
|
|
|
- geometry = self.active_tool.geometry
|
|
|
+ self.apcode_entry = FCSpinner()
|
|
|
+ self.apcode_entry.set_range(0, 999)
|
|
|
+ self.apcode_entry.setWrapping(True)
|
|
|
|
|
|
- try:
|
|
|
- self.shapes.add(shape=geometry.geo, color=color, face_color=color, layer=0, tolerance=self.tolerance)
|
|
|
- except AttributeError:
|
|
|
- if type(geometry) == Point:
|
|
|
- return
|
|
|
- if len(color) == 9:
|
|
|
- color = color[:7] + 'AF'
|
|
|
- self.shapes.add(shape=geometry, color=color, face_color=color, layer=0, tolerance=self.tolerance)
|
|
|
+ grid1.addWidget(self.apcode_entry, 1, 1)
|
|
|
|
|
|
- # def start_delayed_plot(self, check_period):
|
|
|
- # """
|
|
|
- # This function starts an QTImer and it will periodically check if all the workers finish the plotting functions
|
|
|
- #
|
|
|
- # :param check_period: time at which to check periodically if all plots finished to be plotted
|
|
|
- # :return:
|
|
|
- # """
|
|
|
- #
|
|
|
- # # self.plot_thread = threading.Thread(target=lambda: self.check_plot_finished(check_period))
|
|
|
- # # self.plot_thread.start()
|
|
|
- # log.debug("AppGerberEditor --> Delayed Plot started.")
|
|
|
- # self.plot_thread = QtCore.QTimer()
|
|
|
- # self.plot_thread.setInterval(check_period)
|
|
|
- # self.plot_finished.connect(self.setup_ui_after_delayed_plot)
|
|
|
- # self.plot_thread.timeout.connect(self.check_plot_finished)
|
|
|
- # self.plot_thread.start()
|
|
|
- #
|
|
|
- # def check_plot_finished(self):
|
|
|
- # """
|
|
|
- # If all the promises made are finished then all the shapes are in shapes_storage and can be plotted safely and
|
|
|
- # then the UI is rebuilt accordingly.
|
|
|
- # :return:
|
|
|
- # """
|
|
|
- #
|
|
|
- # try:
|
|
|
- # if not self.grb_plot_promises:
|
|
|
- # self.plot_thread.stop()
|
|
|
- # self.plot_finished.emit()
|
|
|
- # log.debug("AppGerberEditor --> delayed_plot finished")
|
|
|
- # except Exception as e:
|
|
|
- # traceback.print_exc()
|
|
|
- #
|
|
|
- # def setup_ui_after_delayed_plot(self):
|
|
|
- # self.plot_finished.disconnect()
|
|
|
- #
|
|
|
- # # now that we have data, create the GUI interface and add it to the Tool Tab
|
|
|
- # self.build_ui(first_run=True)
|
|
|
- # self.plot_all()
|
|
|
- #
|
|
|
- # # HACK: enabling/disabling the cursor seams to somehow update the shapes making them more 'solid'
|
|
|
- # # - perhaps is a bug in VisPy implementation
|
|
|
- # self.app.app_cursor.enabled = False
|
|
|
- # self.app.app_cursor.enabled = True
|
|
|
+ apsize_lbl = QtWidgets.QLabel('%s' % _('Aperture Size:'))
|
|
|
+ apsize_lbl.setToolTip(
|
|
|
+ _("Size for the new aperture.\n"
|
|
|
+ "If aperture type is 'R' or 'O' then\n"
|
|
|
+ "this value is automatically\n"
|
|
|
+ "calculated as:\n"
|
|
|
+ "sqrt(width**2 + height**2)")
|
|
|
+ )
|
|
|
+ grid1.addWidget(apsize_lbl, 2, 0)
|
|
|
|
|
|
- def on_zoom_fit(self):
|
|
|
- """
|
|
|
- Callback for zoom-fit request in Gerber Editor
|
|
|
+ self.apsize_entry = FCDoubleSpinner()
|
|
|
+ self.apsize_entry.set_precision(self.decimals)
|
|
|
+ self.apsize_entry.set_range(0.0, 9999)
|
|
|
|
|
|
- :return: None
|
|
|
- """
|
|
|
- log.debug("AppGerberEditor.on_zoom_fit()")
|
|
|
+ grid1.addWidget(self.apsize_entry, 2, 1)
|
|
|
|
|
|
- # calculate all the geometry in the edited Gerber object
|
|
|
- edit_geo = []
|
|
|
- for ap_code in self.storage_dict:
|
|
|
- for geo_el in self.storage_dict[ap_code]['geometry']:
|
|
|
- actual_geo = geo_el.geo
|
|
|
- if 'solid' in actual_geo:
|
|
|
- edit_geo.append(actual_geo['solid'])
|
|
|
+ aptype_lbl = QtWidgets.QLabel('%s:' % _('Aperture Type'))
|
|
|
+ aptype_lbl.setToolTip(
|
|
|
+ _("Select the type of new aperture. Can be:\n"
|
|
|
+ "C = circular\n"
|
|
|
+ "R = rectangular\n"
|
|
|
+ "O = oblong")
|
|
|
+ )
|
|
|
+ grid1.addWidget(aptype_lbl, 3, 0)
|
|
|
|
|
|
- all_geo = unary_union(edit_geo)
|
|
|
+ self.aptype_cb = FCComboBox()
|
|
|
+ self.aptype_cb.addItems(['C', 'R', 'O'])
|
|
|
+ grid1.addWidget(self.aptype_cb, 3, 1)
|
|
|
+
|
|
|
+ self.apdim_lbl = QtWidgets.QLabel('%s:' % _('Aperture Dim'))
|
|
|
+ self.apdim_lbl.setToolTip(
|
|
|
+ _("Dimensions for the new aperture.\n"
|
|
|
+ "Active only for rectangular apertures (type R).\n"
|
|
|
+ "The format is (width, height)")
|
|
|
+ )
|
|
|
+ grid1.addWidget(self.apdim_lbl, 4, 0)
|
|
|
|
|
|
- # calculate the bounds values for the edited Gerber object
|
|
|
- xmin, ymin, xmax, ymax = all_geo.bounds
|
|
|
+ self.apdim_entry = EvalEntry2()
|
|
|
+ grid1.addWidget(self.apdim_entry, 4, 1)
|
|
|
|
|
|
- if self.app.is_legacy is False:
|
|
|
- new_rect = Rect(xmin, ymin, xmax, ymax)
|
|
|
- self.app.plotcanvas.fit_view(rect=new_rect)
|
|
|
- else:
|
|
|
- width = xmax - xmin
|
|
|
- height = ymax - ymin
|
|
|
- xmin -= 0.05 * width
|
|
|
- xmax += 0.05 * width
|
|
|
- ymin -= 0.05 * height
|
|
|
- ymax += 0.05 * height
|
|
|
- self.app.plotcanvas.adjust_axes(xmin, ymin, xmax, ymax)
|
|
|
+ apadd_del_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Add/Delete Aperture'))
|
|
|
+ apadd_del_lbl.setToolTip(
|
|
|
+ _("Add/Delete an aperture in the aperture table")
|
|
|
+ )
|
|
|
+ self.apertures_box.addWidget(apadd_del_lbl)
|
|
|
|
|
|
- def get_selected(self):
|
|
|
- """
|
|
|
- Returns list of shapes that are selected in the editor.
|
|
|
+ hlay_ad = QtWidgets.QHBoxLayout()
|
|
|
+ self.apertures_box.addLayout(hlay_ad)
|
|
|
|
|
|
- :return: List of shapes.
|
|
|
- """
|
|
|
- # return [shape for shape in self.shape_buffer if shape["selected"]]
|
|
|
- return self.selected
|
|
|
+ self.addaperture_btn = QtWidgets.QPushButton(_('Add'))
|
|
|
+ self.addaperture_btn.setToolTip(
|
|
|
+ _("Add a new aperture to the aperture list.")
|
|
|
+ )
|
|
|
|
|
|
- def delete_selected(self):
|
|
|
- temp_ref = [s for s in self.selected]
|
|
|
+ self.delaperture_btn = QtWidgets.QPushButton(_('Delete'))
|
|
|
+ self.delaperture_btn.setToolTip(
|
|
|
+ _("Delete a aperture in the aperture list")
|
|
|
+ )
|
|
|
+ hlay_ad.addWidget(self.addaperture_btn)
|
|
|
+ hlay_ad.addWidget(self.delaperture_btn)
|
|
|
|
|
|
- if len(temp_ref) == 0:
|
|
|
- self.app.inform.emit('[ERROR_NOTCL] %s' %
|
|
|
- _("Failed. No aperture geometry is selected."))
|
|
|
- return
|
|
|
+ # ###################
|
|
|
+ # ### BUFFER TOOL ###
|
|
|
+ # ###################
|
|
|
+ self.buffer_tool_frame = QtWidgets.QFrame()
|
|
|
+ self.buffer_tool_frame.setContentsMargins(0, 0, 0, 0)
|
|
|
+ self.custom_box.addWidget(self.buffer_tool_frame)
|
|
|
+ self.buffer_tools_box = QtWidgets.QVBoxLayout()
|
|
|
+ self.buffer_tools_box.setContentsMargins(0, 0, 0, 0)
|
|
|
+ self.buffer_tool_frame.setLayout(self.buffer_tools_box)
|
|
|
+ self.buffer_tool_frame.hide()
|
|
|
|
|
|
- for shape_sel in temp_ref:
|
|
|
- self.delete_shape(shape_sel)
|
|
|
+ # Title
|
|
|
+ buf_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Buffer Aperture'))
|
|
|
+ buf_title_lbl.setToolTip(
|
|
|
+ _("Buffer a aperture in the aperture list")
|
|
|
+ )
|
|
|
+ self.buffer_tools_box.addWidget(buf_title_lbl)
|
|
|
|
|
|
- self.selected = []
|
|
|
- self.build_ui()
|
|
|
- self.app.inform.emit('[success] %s' % _("Done."))
|
|
|
+ # Form Layout
|
|
|
+ buf_form_layout = QtWidgets.QFormLayout()
|
|
|
+ self.buffer_tools_box.addLayout(buf_form_layout)
|
|
|
|
|
|
- def delete_shape(self, geo_el):
|
|
|
- self.is_modified = True
|
|
|
+ # Buffer distance
|
|
|
+ self.buffer_distance_entry = FCDoubleSpinner()
|
|
|
+ self.buffer_distance_entry.set_precision(self.decimals)
|
|
|
+ self.buffer_distance_entry.set_range(-10000.0000, 10000.0000)
|
|
|
|
|
|
- if geo_el in self.utility:
|
|
|
- self.utility.remove(geo_el)
|
|
|
- return
|
|
|
+ buf_form_layout.addRow('%s:' % _("Buffer distance"), self.buffer_distance_entry)
|
|
|
+ self.buffer_corner_lbl = QtWidgets.QLabel('%s:' % _("Buffer corner"))
|
|
|
+ self.buffer_corner_lbl.setToolTip(
|
|
|
+ _("There are 3 types of corners:\n"
|
|
|
+ " - 'Round': the corner is rounded.\n"
|
|
|
+ " - 'Square': the corner is met in a sharp angle.\n"
|
|
|
+ " - 'Beveled': the corner is a line that directly connects the features meeting in the corner")
|
|
|
+ )
|
|
|
+ self.buffer_corner_cb = FCComboBox()
|
|
|
+ self.buffer_corner_cb.addItem(_("Round"))
|
|
|
+ self.buffer_corner_cb.addItem(_("Square"))
|
|
|
+ self.buffer_corner_cb.addItem(_("Beveled"))
|
|
|
+ buf_form_layout.addRow(self.buffer_corner_lbl, self.buffer_corner_cb)
|
|
|
|
|
|
- for storage in self.storage_dict:
|
|
|
- try:
|
|
|
- if geo_el in self.storage_dict[storage]['geometry']:
|
|
|
- self.storage_dict[storage]['geometry'].remove(geo_el)
|
|
|
- except KeyError:
|
|
|
- pass
|
|
|
- if geo_el in self.selected:
|
|
|
- self.selected.remove(geo_el) # TODO: Check performance
|
|
|
+ # Buttons
|
|
|
+ hlay_buf = QtWidgets.QHBoxLayout()
|
|
|
+ self.buffer_tools_box.addLayout(hlay_buf)
|
|
|
|
|
|
- def delete_utility_geometry(self):
|
|
|
- # for_deletion = [shape for shape in self.shape_buffer if shape.utility]
|
|
|
- # for_deletion = [shape for shape in self.storage.get_objects() if shape.utility]
|
|
|
- for_deletion = [geo_el for geo_el in self.utility]
|
|
|
- for geo_el in for_deletion:
|
|
|
- self.delete_shape(geo_el)
|
|
|
+ self.buffer_button = QtWidgets.QPushButton(_("Buffer"))
|
|
|
+ hlay_buf.addWidget(self.buffer_button)
|
|
|
|
|
|
- self.tool_shape.clear(update=True)
|
|
|
- self.tool_shape.redraw()
|
|
|
+ # ##################
|
|
|
+ # ### SCALE TOOL ###
|
|
|
+ # ##################
|
|
|
+ self.scale_tool_frame = QtWidgets.QFrame()
|
|
|
+ self.scale_tool_frame.setContentsMargins(0, 0, 0, 0)
|
|
|
+ self.custom_box.addWidget(self.scale_tool_frame)
|
|
|
+ self.scale_tools_box = QtWidgets.QVBoxLayout()
|
|
|
+ self.scale_tools_box.setContentsMargins(0, 0, 0, 0)
|
|
|
+ self.scale_tool_frame.setLayout(self.scale_tools_box)
|
|
|
+ self.scale_tool_frame.hide()
|
|
|
|
|
|
- def on_delete_btn(self):
|
|
|
- self.delete_selected()
|
|
|
- self.plot_all()
|
|
|
+ # Title
|
|
|
+ scale_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Scale Aperture'))
|
|
|
+ scale_title_lbl.setToolTip(
|
|
|
+ _("Scale a aperture in the aperture list")
|
|
|
+ )
|
|
|
+ self.scale_tools_box.addWidget(scale_title_lbl)
|
|
|
|
|
|
- def select_tool(self, toolname):
|
|
|
- """
|
|
|
- Selects a drawing tool. Impacts the object and appGUI.
|
|
|
+ # Form Layout
|
|
|
+ scale_form_layout = QtWidgets.QFormLayout()
|
|
|
+ self.scale_tools_box.addLayout(scale_form_layout)
|
|
|
|
|
|
- :param toolname: Name of the tool.
|
|
|
- :return: None
|
|
|
- """
|
|
|
- self.tools_gerber[toolname]["button"].setChecked(True)
|
|
|
- self.on_tool_select(toolname)
|
|
|
+ self.scale_factor_lbl = QtWidgets.QLabel('%s:' % _("Scale factor"))
|
|
|
+ self.scale_factor_lbl.setToolTip(
|
|
|
+ _("The factor by which to scale the selected aperture.\n"
|
|
|
+ "Values can be between 0.0000 and 999.9999")
|
|
|
+ )
|
|
|
+ self.scale_factor_entry = FCDoubleSpinner()
|
|
|
+ self.scale_factor_entry.set_precision(self.decimals)
|
|
|
+ self.scale_factor_entry.set_range(0.0000, 10000.0000)
|
|
|
|
|
|
- def set_selected(self, geo_el):
|
|
|
+ scale_form_layout.addRow(self.scale_factor_lbl, self.scale_factor_entry)
|
|
|
|
|
|
- # Remove and add to the end.
|
|
|
- if geo_el in self.selected:
|
|
|
- self.selected.remove(geo_el)
|
|
|
+ # Buttons
|
|
|
+ hlay_scale = QtWidgets.QHBoxLayout()
|
|
|
+ self.scale_tools_box.addLayout(hlay_scale)
|
|
|
|
|
|
- self.selected.append(geo_el)
|
|
|
+ self.scale_button = QtWidgets.QPushButton(_("Scale"))
|
|
|
+ hlay_scale.addWidget(self.scale_button)
|
|
|
|
|
|
- def set_unselected(self, geo_el):
|
|
|
- if geo_el in self.selected:
|
|
|
- self.selected.remove(geo_el)
|
|
|
+ # ######################
|
|
|
+ # ### Mark Area TOOL ###
|
|
|
+ # ######################
|
|
|
+ self.ma_tool_frame = QtWidgets.QFrame()
|
|
|
+ self.ma_tool_frame.setContentsMargins(0, 0, 0, 0)
|
|
|
+ self.custom_box.addWidget(self.ma_tool_frame)
|
|
|
+ self.ma_tools_box = QtWidgets.QVBoxLayout()
|
|
|
+ self.ma_tools_box.setContentsMargins(0, 0, 0, 0)
|
|
|
+ self.ma_tool_frame.setLayout(self.ma_tools_box)
|
|
|
+ self.ma_tool_frame.hide()
|
|
|
|
|
|
- def on_array_type_combo(self):
|
|
|
- if self.array_type_combo.currentIndex() == 0:
|
|
|
- self.array_circular_frame.hide()
|
|
|
- self.array_linear_frame.show()
|
|
|
- else:
|
|
|
- self.delete_utility_geometry()
|
|
|
- self.array_circular_frame.show()
|
|
|
- self.array_linear_frame.hide()
|
|
|
- self.app.inform.emit(_("Click on the circular array Center position"))
|
|
|
+ # Title
|
|
|
+ ma_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Mark polygons'))
|
|
|
+ ma_title_lbl.setToolTip(
|
|
|
+ _("Mark the polygon areas.")
|
|
|
+ )
|
|
|
+ self.ma_tools_box.addWidget(ma_title_lbl)
|
|
|
|
|
|
- def on_linear_angle_radio(self):
|
|
|
- val = self.pad_axis_radio.get_value()
|
|
|
- if val == 'A':
|
|
|
- self.linear_angle_spinner.show()
|
|
|
- self.linear_angle_label.show()
|
|
|
- else:
|
|
|
- self.linear_angle_spinner.hide()
|
|
|
- self.linear_angle_label.hide()
|
|
|
+ # Form Layout
|
|
|
+ ma_form_layout = QtWidgets.QFormLayout()
|
|
|
+ self.ma_tools_box.addLayout(ma_form_layout)
|
|
|
|
|
|
- def on_copy_button(self):
|
|
|
- self.select_tool('copy')
|
|
|
- return
|
|
|
+ self.ma_upper_threshold_lbl = QtWidgets.QLabel('%s:' % _("Area UPPER threshold"))
|
|
|
+ self.ma_upper_threshold_lbl.setToolTip(
|
|
|
+ _("The threshold value, all areas less than this are marked.\n"
|
|
|
+ "Can have a value between 0.0000 and 10000.0000")
|
|
|
+ )
|
|
|
+ self.ma_upper_threshold_entry = FCDoubleSpinner()
|
|
|
+ self.ma_upper_threshold_entry.set_precision(self.decimals)
|
|
|
+ self.ma_upper_threshold_entry.set_range(0, 10000)
|
|
|
|
|
|
- def on_move_button(self):
|
|
|
- self.select_tool('move')
|
|
|
- return
|
|
|
+ self.ma_lower_threshold_lbl = QtWidgets.QLabel('%s:' % _("Area LOWER threshold"))
|
|
|
+ self.ma_lower_threshold_lbl.setToolTip(
|
|
|
+ _("The threshold value, all areas more than this are marked.\n"
|
|
|
+ "Can have a value between 0.0000 and 10000.0000")
|
|
|
+ )
|
|
|
+ self.ma_lower_threshold_entry = FCDoubleSpinner()
|
|
|
+ self.ma_lower_threshold_entry.set_precision(self.decimals)
|
|
|
+ self.ma_lower_threshold_entry.set_range(0, 10000)
|
|
|
|
|
|
- def on_pad_add(self):
|
|
|
- self.select_tool('pad')
|
|
|
+ ma_form_layout.addRow(self.ma_lower_threshold_lbl, self.ma_lower_threshold_entry)
|
|
|
+ ma_form_layout.addRow(self.ma_upper_threshold_lbl, self.ma_upper_threshold_entry)
|
|
|
|
|
|
- def on_pad_add_array(self):
|
|
|
- self.select_tool('array')
|
|
|
+ # Buttons
|
|
|
+ hlay_ma = QtWidgets.QHBoxLayout()
|
|
|
+ self.ma_tools_box.addLayout(hlay_ma)
|
|
|
|
|
|
- def on_track_add(self):
|
|
|
- self.select_tool('track')
|
|
|
+ self.ma_threshold_button = QtWidgets.QPushButton(_("Mark"))
|
|
|
+ self.ma_threshold_button.setToolTip(
|
|
|
+ _("Mark the polygons that fit within limits.")
|
|
|
+ )
|
|
|
+ hlay_ma.addWidget(self.ma_threshold_button)
|
|
|
|
|
|
- def on_region_add(self):
|
|
|
- self.select_tool('region')
|
|
|
+ self.ma_delete_button = QtWidgets.QPushButton(_("Delete"))
|
|
|
+ self.ma_delete_button.setToolTip(
|
|
|
+ _("Delete all the marked polygons.")
|
|
|
+ )
|
|
|
+ hlay_ma.addWidget(self.ma_delete_button)
|
|
|
|
|
|
- def on_poligonize(self):
|
|
|
- self.select_tool('poligonize')
|
|
|
+ self.ma_clear_button = QtWidgets.QPushButton(_("Clear"))
|
|
|
+ self.ma_clear_button.setToolTip(
|
|
|
+ _("Clear all the markings.")
|
|
|
+ )
|
|
|
+ hlay_ma.addWidget(self.ma_clear_button)
|
|
|
|
|
|
- def on_disc_add(self):
|
|
|
- self.select_tool('disc')
|
|
|
+ # ######################
|
|
|
+ # ### Add Pad Array ####
|
|
|
+ # ######################
|
|
|
+ # add a frame and inside add a vertical box layout. Inside this vbox layout I add
|
|
|
+ # all the add Pad array widgets
|
|
|
+ # this way I can hide/show the frame
|
|
|
+ self.array_frame = QtWidgets.QFrame()
|
|
|
+ self.array_frame.setContentsMargins(0, 0, 0, 0)
|
|
|
+ self.custom_box.addWidget(self.array_frame)
|
|
|
+ self.array_box = QtWidgets.QVBoxLayout()
|
|
|
+ self.array_box.setContentsMargins(0, 0, 0, 0)
|
|
|
+ self.array_frame.setLayout(self.array_box)
|
|
|
|
|
|
- def on_add_semidisc(self):
|
|
|
- self.select_tool('semidisc')
|
|
|
+ self.emptyarray_label = QtWidgets.QLabel('')
|
|
|
+ self.array_box.addWidget(self.emptyarray_label)
|
|
|
|
|
|
- def on_buffer(self):
|
|
|
- buff_value = 0.01
|
|
|
- log.debug("AppGerberEditor.on_buffer()")
|
|
|
+ self.padarray_label = QtWidgets.QLabel('<b>%s</b>' % _("Add Pad Array"))
|
|
|
+ self.padarray_label.setToolTip(
|
|
|
+ _("Add an array of pads (linear or circular array)")
|
|
|
+ )
|
|
|
+ self.array_box.addWidget(self.padarray_label)
|
|
|
|
|
|
- try:
|
|
|
- buff_value = float(self.buffer_distance_entry.get_value())
|
|
|
- except ValueError:
|
|
|
- # try to convert comma to decimal point. if it's still not working error message and return
|
|
|
- try:
|
|
|
- buff_value = float(self.buffer_distance_entry.get_value().replace(',', '.'))
|
|
|
- self.buffer_distance_entry.set_value(buff_value)
|
|
|
- except ValueError:
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
- _("Buffer distance value is missing or wrong format. Add it and retry."))
|
|
|
- return
|
|
|
- # the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
|
|
|
- # I populated the combobox such that the index coincide with the join styles value (which is really an INT)
|
|
|
- join_style = self.buffer_corner_cb.currentIndex() + 1
|
|
|
+ self.array_type_combo = FCComboBox()
|
|
|
+ self.array_type_combo.setToolTip(
|
|
|
+ _("Select the type of pads array to create.\n"
|
|
|
+ "It can be Linear X(Y) or Circular")
|
|
|
+ )
|
|
|
+ self.array_type_combo.addItem(_("Linear"))
|
|
|
+ self.array_type_combo.addItem(_("Circular"))
|
|
|
|
|
|
- def buffer_recursion(geom_el, selection):
|
|
|
- if type(geom_el) == list:
|
|
|
- geoms = []
|
|
|
- for local_geom in geom_el:
|
|
|
- geoms.append(buffer_recursion(local_geom, selection=selection))
|
|
|
- return geoms
|
|
|
- else:
|
|
|
- if geom_el in selection:
|
|
|
- geometric_data = geom_el.geo
|
|
|
- buffered_geom_el = {}
|
|
|
- if 'solid' in geometric_data:
|
|
|
- buffered_geom_el['solid'] = geometric_data['solid'].buffer(buff_value, join_style=join_style)
|
|
|
- if 'follow' in geometric_data:
|
|
|
- buffered_geom_el['follow'] = geometric_data['follow'].buffer(buff_value, join_style=join_style)
|
|
|
- if 'clear' in geometric_data:
|
|
|
- buffered_geom_el['clear'] = geometric_data['clear'].buffer(buff_value, join_style=join_style)
|
|
|
- return DrawToolShape(buffered_geom_el)
|
|
|
- else:
|
|
|
- return geom_el
|
|
|
+ self.array_box.addWidget(self.array_type_combo)
|
|
|
|
|
|
- if not self.apertures_table.selectedItems():
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
- _("No aperture to buffer. Select at least one aperture and try again."))
|
|
|
- return
|
|
|
+ self.array_form = QtWidgets.QFormLayout()
|
|
|
+ self.array_box.addLayout(self.array_form)
|
|
|
|
|
|
- for x in self.apertures_table.selectedItems():
|
|
|
- try:
|
|
|
- apcode = self.apertures_table.item(x.row(), 1).text()
|
|
|
+ self.pad_array_size_label = QtWidgets.QLabel('%s:' % _('Nr of pads'))
|
|
|
+ self.pad_array_size_label.setToolTip(
|
|
|
+ _("Specify how many pads to be in the array.")
|
|
|
+ )
|
|
|
+ self.pad_array_size_label.setMinimumWidth(100)
|
|
|
|
|
|
- temp_storage = deepcopy(buffer_recursion(self.storage_dict[apcode]['geometry'], self.selected))
|
|
|
- self.storage_dict[apcode]['geometry'] = []
|
|
|
- self.storage_dict[apcode]['geometry'] = temp_storage
|
|
|
- except Exception as e:
|
|
|
- log.debug("AppGerberEditor.buffer() --> %s" % str(e))
|
|
|
- self.app.inform.emit('[ERROR_NOTCL] %s\n%s' % (_("Failed."), str(traceback.print_exc())))
|
|
|
- return
|
|
|
+ self.pad_array_size_entry = FCSpinner()
|
|
|
+ self.pad_array_size_entry.set_range(1, 9999)
|
|
|
|
|
|
- self.plot_all()
|
|
|
- self.app.inform.emit('[success] %s' % _("Done."))
|
|
|
+ self.array_form.addRow(self.pad_array_size_label, self.pad_array_size_entry)
|
|
|
|
|
|
- def on_scale(self):
|
|
|
- scale_factor = 1.0
|
|
|
- log.debug("AppGerberEditor.on_scale()")
|
|
|
+ self.array_linear_frame = QtWidgets.QFrame()
|
|
|
+ self.array_linear_frame.setContentsMargins(0, 0, 0, 0)
|
|
|
+ self.array_box.addWidget(self.array_linear_frame)
|
|
|
+ self.linear_box = QtWidgets.QVBoxLayout()
|
|
|
+ self.linear_box.setContentsMargins(0, 0, 0, 0)
|
|
|
+ self.array_linear_frame.setLayout(self.linear_box)
|
|
|
|
|
|
- try:
|
|
|
- scale_factor = float(self.scale_factor_entry.get_value())
|
|
|
- except ValueError:
|
|
|
- # try to convert comma to decimal point. if it's still not working error message and return
|
|
|
- try:
|
|
|
- scale_factor = float(self.scale_factor_entry.get_value().replace(',', '.'))
|
|
|
- self.scale_factor_entry.set_value(scale_factor)
|
|
|
- except ValueError:
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
- _("Scale factor value is missing or wrong format. Add it and retry."))
|
|
|
- return
|
|
|
+ self.linear_form = QtWidgets.QFormLayout()
|
|
|
+ self.linear_box.addLayout(self.linear_form)
|
|
|
|
|
|
- def scale_recursion(geom_el, selection):
|
|
|
- if type(geom_el) == list:
|
|
|
- geoms = []
|
|
|
- for local_geom in geom_el:
|
|
|
- geoms.append(scale_recursion(local_geom, selection=selection))
|
|
|
- return geoms
|
|
|
- else:
|
|
|
- if geom_el in selection:
|
|
|
- geometric_data = geom_el.geo
|
|
|
- scaled_geom_el = {}
|
|
|
- if 'solid' in geometric_data:
|
|
|
- scaled_geom_el['solid'] = affinity.scale(
|
|
|
- geometric_data['solid'], scale_factor, scale_factor, origin='center'
|
|
|
- )
|
|
|
- if 'follow' in geometric_data:
|
|
|
- scaled_geom_el['follow'] = affinity.scale(
|
|
|
- geometric_data['follow'], scale_factor, scale_factor, origin='center'
|
|
|
- )
|
|
|
- if 'clear' in geometric_data:
|
|
|
- scaled_geom_el['clear'] = affinity.scale(
|
|
|
- geometric_data['clear'], scale_factor, scale_factor, origin='center'
|
|
|
- )
|
|
|
+ self.pad_axis_label = QtWidgets.QLabel('%s:' % _('Direction'))
|
|
|
+ self.pad_axis_label.setToolTip(
|
|
|
+ _("Direction on which the linear array is oriented:\n"
|
|
|
+ "- 'X' - horizontal axis \n"
|
|
|
+ "- 'Y' - vertical axis or \n"
|
|
|
+ "- 'Angle' - a custom angle for the array inclination")
|
|
|
+ )
|
|
|
+ self.pad_axis_label.setMinimumWidth(100)
|
|
|
|
|
|
- return DrawToolShape(scaled_geom_el)
|
|
|
- else:
|
|
|
- return geom_el
|
|
|
+ self.pad_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
|
|
|
+ {'label': _('Y'), 'value': 'Y'},
|
|
|
+ {'label': _('Angle'), 'value': 'A'}])
|
|
|
+ self.pad_axis_radio.set_value('X')
|
|
|
+ self.linear_form.addRow(self.pad_axis_label, self.pad_axis_radio)
|
|
|
|
|
|
- if not self.apertures_table.selectedItems():
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
- _("No aperture to scale. Select at least one aperture and try again."))
|
|
|
- return
|
|
|
+ self.pad_pitch_label = QtWidgets.QLabel('%s:' % _('Pitch'))
|
|
|
+ self.pad_pitch_label.setToolTip(
|
|
|
+ _("Pitch = Distance between elements of the array.")
|
|
|
+ )
|
|
|
+ self.pad_pitch_label.setMinimumWidth(100)
|
|
|
|
|
|
- for x in self.apertures_table.selectedItems():
|
|
|
- try:
|
|
|
- apcode = self.apertures_table.item(x.row(), 1).text()
|
|
|
+ self.pad_pitch_entry = FCDoubleSpinner()
|
|
|
+ self.pad_pitch_entry.set_precision(self.decimals)
|
|
|
+ self.pad_pitch_entry.set_range(0.0000, 10000.0000)
|
|
|
+ self.pad_pitch_entry.setSingleStep(0.1)
|
|
|
|
|
|
- temp_storage = deepcopy(scale_recursion(self.storage_dict[apcode]['geometry'], self.selected))
|
|
|
- self.storage_dict[apcode]['geometry'] = []
|
|
|
- self.storage_dict[apcode]['geometry'] = temp_storage
|
|
|
+ self.linear_form.addRow(self.pad_pitch_label, self.pad_pitch_entry)
|
|
|
|
|
|
- except Exception as e:
|
|
|
- log.debug("AppGerberEditor.on_scale() --> %s" % str(e))
|
|
|
+ self.linear_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
|
|
|
+ self.linear_angle_label.setToolTip(
|
|
|
+ _("Angle at which the linear array is placed.\n"
|
|
|
+ "The precision is of max 2 decimals.\n"
|
|
|
+ "Min value is: -360.00 degrees.\n"
|
|
|
+ "Max value is: 360.00 degrees.")
|
|
|
+ )
|
|
|
+ self.linear_angle_label.setMinimumWidth(100)
|
|
|
|
|
|
- self.plot_all()
|
|
|
- self.app.inform.emit('[success] %s' % _("Done."))
|
|
|
+ self.linear_angle_spinner = FCDoubleSpinner()
|
|
|
+ self.linear_angle_spinner.set_precision(self.decimals)
|
|
|
+ self.linear_angle_spinner.setRange(-360.00, 360.00)
|
|
|
+ self.linear_form.addRow(self.linear_angle_label, self.linear_angle_spinner)
|
|
|
|
|
|
- def on_markarea(self):
|
|
|
- # clear previous marking
|
|
|
- self.ma_annotation.clear(update=True)
|
|
|
+ self.array_circular_frame = QtWidgets.QFrame()
|
|
|
+ self.array_circular_frame.setContentsMargins(0, 0, 0, 0)
|
|
|
+ self.array_box.addWidget(self.array_circular_frame)
|
|
|
+ self.circular_box = QtWidgets.QVBoxLayout()
|
|
|
+ self.circular_box.setContentsMargins(0, 0, 0, 0)
|
|
|
+ self.array_circular_frame.setLayout(self.circular_box)
|
|
|
|
|
|
- self.units = self.app.defaults['units'].upper()
|
|
|
+ self.pad_direction_label = QtWidgets.QLabel('%s:' % _('Direction'))
|
|
|
+ self.pad_direction_label.setToolTip(
|
|
|
+ _("Direction for circular array.\n"
|
|
|
+ "Can be CW = clockwise or CCW = counter clockwise.")
|
|
|
+ )
|
|
|
+ self.pad_direction_label.setMinimumWidth(100)
|
|
|
|
|
|
- text = []
|
|
|
- position = []
|
|
|
+ self.circular_form = QtWidgets.QFormLayout()
|
|
|
+ self.circular_box.addLayout(self.circular_form)
|
|
|
|
|
|
- for apcode in self.storage_dict:
|
|
|
- if 'geometry' in self.storage_dict[apcode]:
|
|
|
- for geo_el in self.storage_dict[apcode]['geometry']:
|
|
|
- if 'solid' in geo_el.geo:
|
|
|
- area = geo_el.geo['solid'].area
|
|
|
- try:
|
|
|
- upper_threshold_val = self.ma_upper_threshold_entry.get_value()
|
|
|
- except Exception:
|
|
|
- return
|
|
|
+ self.pad_direction_radio = RadioSet([{'label': _('CW'), 'value': 'CW'},
|
|
|
+ {'label': _('CCW'), 'value': 'CCW'}])
|
|
|
+ self.pad_direction_radio.set_value('CW')
|
|
|
+ self.circular_form.addRow(self.pad_direction_label, self.pad_direction_radio)
|
|
|
|
|
|
- try:
|
|
|
- lower_threshold_val = self.ma_lower_threshold_entry.get_value()
|
|
|
- except Exception:
|
|
|
- lower_threshold_val = 0.0
|
|
|
+ self.pad_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
|
|
|
+ self.pad_angle_label.setToolTip(
|
|
|
+ _("Angle at which each element in circular array is placed.")
|
|
|
+ )
|
|
|
+ self.pad_angle_label.setMinimumWidth(100)
|
|
|
|
|
|
- if float(upper_threshold_val) > area > float(lower_threshold_val):
|
|
|
- current_pos = geo_el.geo['solid'].exterior.coords[-1]
|
|
|
- text_elem = '%.*f' % (self.decimals, area)
|
|
|
- text.append(text_elem)
|
|
|
- position.append(current_pos)
|
|
|
- self.geo_to_delete.append(geo_el)
|
|
|
+ self.pad_angle_entry = FCDoubleSpinner()
|
|
|
+ self.pad_angle_entry.set_precision(self.decimals)
|
|
|
+ self.pad_angle_entry.set_range(-360.00, 360.00)
|
|
|
+ self.pad_angle_entry.setSingleStep(0.1)
|
|
|
|
|
|
- if text:
|
|
|
- self.ma_annotation.set(text=text, pos=position, visible=True,
|
|
|
- font_size=self.app.defaults["cncjob_annotation_fontsize"],
|
|
|
- color='#000000FF')
|
|
|
- self.app.inform.emit('[success] %s' %
|
|
|
- _("Polygons marked."))
|
|
|
- else:
|
|
|
- self.app.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
- _("No polygons were marked. None fit within the limits."))
|
|
|
+ self.circular_form.addRow(self.pad_angle_label, self.pad_angle_entry)
|
|
|
|
|
|
- def delete_marked_polygons(self):
|
|
|
- for shape_sel in self.geo_to_delete:
|
|
|
- self.delete_shape(shape_sel)
|
|
|
+ self.array_circular_frame.hide()
|
|
|
|
|
|
- self.build_ui()
|
|
|
- self.plot_all()
|
|
|
- self.app.inform.emit('[success] %s' % _("Done."))
|
|
|
+ self.linear_angle_spinner.hide()
|
|
|
+ self.linear_angle_label.hide()
|
|
|
|
|
|
- def on_eraser(self):
|
|
|
- self.select_tool('eraser')
|
|
|
+ self.array_frame.hide()
|
|
|
+ self.custom_box.addStretch()
|
|
|
|
|
|
- def on_transform(self):
|
|
|
- if type(self.active_tool) == FCTransform:
|
|
|
- self.select_tool('select')
|
|
|
- else:
|
|
|
- self.select_tool('transform')
|
|
|
+ layout.addStretch()
|
|
|
|
|
|
- def hide_tool(self, tool_name):
|
|
|
- # self.app.ui.notebook.setTabText(2, _("Tools"))
|
|
|
- try:
|
|
|
- if tool_name == 'all':
|
|
|
- self.apertures_frame.hide()
|
|
|
- if tool_name == 'select':
|
|
|
- self.apertures_frame.show()
|
|
|
- if tool_name == 'buffer' or tool_name == 'all':
|
|
|
- self.buffer_tool_frame.hide()
|
|
|
- if tool_name == 'scale' or tool_name == 'all':
|
|
|
- self.scale_tool_frame.hide()
|
|
|
- if tool_name == 'markarea' or tool_name == 'all':
|
|
|
- self.ma_tool_frame.hide()
|
|
|
- except Exception as e:
|
|
|
- log.debug("AppGerberEditor.hide_tool() --> %s" % str(e))
|
|
|
- self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
|
|
|
+ # Editor
|
|
|
+ self.exit_editor_button = QtWidgets.QPushButton(_('Exit Editor'))
|
|
|
+ self.exit_editor_button.setIcon(QtGui.QIcon(self.app.resource_location + '/power16.png'))
|
|
|
+ self.exit_editor_button.setToolTip(
|
|
|
+ _("Exit from Editor.")
|
|
|
+ )
|
|
|
+ self.exit_editor_button.setStyleSheet("""
|
|
|
+ QPushButton
|
|
|
+ {
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+ """)
|
|
|
+ layout.addWidget(self.exit_editor_button)
|
|
|
|
|
|
|
|
|
class TransformEditorTool(AppTool):
|