jpcgt/flatcam/Beta слито с Beta

This commit is contained in:
Camellan 2019-12-24 20:26:34 +04:00
commit 5784e4659e
28 changed files with 871 additions and 155 deletions

View File

@ -56,7 +56,7 @@ from flatcamGUI.PlotCanvas import *
from flatcamGUI.PlotCanvasLegacy import *
from flatcamGUI.FlatCAMGUI import *
from FlatCAMCommon import LoudDict, BookmarkManager, ToolsDB
from FlatCAMCommon import LoudDict, BookmarkManager, ToolsDB, color_variant
from FlatCAMPostProc import load_preprocessors
from flatcamEditors.FlatCAMGeoEditor import FlatCAMGeoEditor
@ -830,6 +830,8 @@ class App(QtCore.QObject):
"tools_transform_offset_y": 0.0,
"tools_transform_mirror_reference": False,
"tools_transform_mirror_point": (0, 0),
"tools_transform_buffer_dis": 0.0,
"tools_transform_buffer_corner": True,
# SolderPaste Tool
"tools_solderpaste_tools": "1.0, 0.3",
@ -1038,6 +1040,7 @@ class App(QtCore.QObject):
QtCore.QObject.__init__(self)
self.ui = FlatCAMGUI(self)
self.on_grid_snap_triggered(state=True)
theme_settings = QtCore.QSettings("Open Source", "FlatCAM")
if theme_settings.contains("theme"):
@ -1431,6 +1434,8 @@ class App(QtCore.QObject):
"tools_transform_offset_y": self.ui.tools_defaults_form.tools_transform_group.offy_entry,
"tools_transform_mirror_reference": self.ui.tools_defaults_form.tools_transform_group.mirror_reference_cb,
"tools_transform_mirror_point": self.ui.tools_defaults_form.tools_transform_group.flip_ref_entry,
"tools_transform_buffer_dis": self.ui.tools_defaults_form.tools_transform_group.buffer_entry,
"tools_transform_buffer_corner": self.ui.tools_defaults_form.tools_transform_group.buffer_rounded_cb,
# SolderPaste Dispensing Tool
"tools_solderpaste_tools": self.ui.tools_defaults_form.tools_solderpaste_group.nozzle_tool_dia_entry,
@ -1939,6 +1944,10 @@ class App(QtCore.QObject):
self.ui.popmenu_properties.triggered.connect(self.obj_properties)
# Project Context Menu -> Color Setting
for act in self.ui.menuprojectcolor.actions():
act.triggered.connect(self.on_set_color_action_triggered)
# Preferences Plot Area TAB
self.ui.pref_save_button.clicked.connect(lambda: self.on_save_button(save_to_file=True))
self.ui.pref_apply_button.clicked.connect(lambda: self.on_save_button(save_to_file=False))
@ -2023,7 +2032,7 @@ class App(QtCore.QObject):
self.ui.general_defaults_form.general_gui_group.proj_color_dis_button.clicked.connect(
self.on_proj_color_dis_button)
# ############################# workspace setting signals #####################
# ############################# Workspace Setting Signals #####################
self.ui.general_defaults_form.general_gui_group.wk_cb.currentIndexChanged.connect(self.on_workspace_modified)
self.ui.general_defaults_form.general_gui_group.wk_orientation_radio.activated_custom.connect(
self.on_workspace_modified
@ -2153,6 +2162,8 @@ class App(QtCore.QObject):
# signal emitted when a tab is closed in the Plot Area
self.ui.plot_tab_area.tab_closed_signal.connect(self.on_plot_area_tab_closed)
self.ui.grid_snap_btn.triggered.connect(self.on_grid_snap_triggered)
# #####################################################################################
# ########### FINISHED CONNECTING SIGNALS #############################################
# #####################################################################################
@ -4728,7 +4739,7 @@ class App(QtCore.QObject):
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 2, 3)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "German"), 3, 0)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Tr)"), 3, 1)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jens Karstedt, @detlefeckardt"), 3, 2)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Jens Karstedt, Detlef Eckardt"), 3, 2)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 3, 3)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Romanian"), 4, 0)
self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu"), 4, 1)
@ -6079,6 +6090,7 @@ class App(QtCore.QObject):
self.report_usage("on_toggle_grid()")
self.ui.grid_snap_btn.trigger()
self.on_grid_snap_triggered(state=True)
def on_toggle_grid_lines(self):
self.report_usage("on_toggle_grd_lines()")
@ -7019,6 +7031,8 @@ class App(QtCore.QObject):
self.connect_toolbar_signals()
self.ui.grid_snap_btn.setChecked(True)
self.on_grid_snap_triggered(state=True)
self.ui.grid_gap_x_entry.setText(str(self.defaults["global_gridx"]))
self.ui.grid_gap_y_entry.setText(str(self.defaults["global_gridy"]))
self.ui.snap_max_dist_entry.setText(str(self.defaults["global_snap_max"]))
@ -8437,16 +8451,16 @@ class App(QtCore.QObject):
def populate_cmenu_grids(self):
units = self.defaults['units'].lower()
for act in self.ui.cmenu_gridmenu.actions():
act.triggered.disconnect()
self.ui.cmenu_gridmenu.clear()
sorted_list = sorted(self.defaults["global_grid_context_menu"][str(units)])
grid_toggle = self.ui.cmenu_gridmenu.addAction(QtGui.QIcon(self.resource_location + '/grid32_menu.png'),
_("Grid On/Off"))
grid_toggle.setCheckable(True)
if self.grid_status() == True:
grid_toggle.setChecked(True)
else:
grid_toggle.setChecked(False)
grid_toggle.setChecked(True) if self.grid_status() else grid_toggle.setChecked(False)
self.ui.cmenu_gridmenu.addSeparator()
for grid in sorted_list:
@ -8842,12 +8856,14 @@ class App(QtCore.QObject):
# create the selection box around the selected object
if self.defaults['global_selection_shape'] is True:
self.draw_selection_shape(obj)
obj.selection_shape_drawn = True
self.collection.set_active(obj.options['name'])
else:
if poly_selection.intersects(poly_obj):
# create the selection box around the selected object
if self.defaults['global_selection_shape'] is True:
self.draw_selection_shape(obj)
obj.selection_shape_drawn = True
self.collection.set_active(obj.options['name'])
except Exception as e:
# the Exception here will happen if we try to select on screen and we have an newly (and empty)
@ -8894,20 +8910,26 @@ class App(QtCore.QObject):
# create the selection box around the selected object
if self.defaults['global_selection_shape'] is True:
self.draw_selection_shape(curr_sel_obj)
curr_sel_obj.selection_shape_drawn = True
elif self.collection.get_active().options['name'] not in objects_under_the_click_list:
elif curr_sel_obj.options['name'] not in objects_under_the_click_list:
self.on_objects_selection(False)
self.delete_selection_shape()
curr_sel_obj.selection_shape_drawn = False
self.collection.set_active(objects_under_the_click_list[0])
curr_sel_obj = self.collection.get_active()
# create the selection box around the selected object
if self.defaults['global_selection_shape'] is True:
self.draw_selection_shape(curr_sel_obj)
curr_sel_obj.selection_shape_drawn = True
self.selected_message(curr_sel_obj=curr_sel_obj)
elif curr_sel_obj.selection_shape_drawn is False:
if self.defaults['global_selection_shape'] is True:
self.draw_selection_shape(curr_sel_obj)
curr_sel_obj.selection_shape_drawn = True
else:
self.on_objects_selection(False)
self.delete_selection_shape()
@ -8922,6 +8944,7 @@ class App(QtCore.QObject):
# make active the first element of the overlapped objects list
if self.collection.get_active() is None:
self.collection.set_active(objects_under_the_click_list[0])
objects_under_the_click_list[0].selection_shape_drawn = True
name_sel_obj = self.collection.get_active().options['name']
# In case that there is a selected object but it is not in the overlapped object list
@ -8939,9 +8962,12 @@ class App(QtCore.QObject):
curr_sel_obj = self.collection.get_active()
# delete the possible selection box around a possible selected object
self.delete_selection_shape()
curr_sel_obj.selection_shape_drawn = False
# create the selection box around the selected object
if self.defaults['global_selection_shape'] is True:
self.draw_selection_shape(curr_sel_obj)
curr_sel_obj.selection_shape_drawn = True
self.selected_message(curr_sel_obj=curr_sel_obj)
@ -8951,6 +8977,9 @@ class App(QtCore.QObject):
# delete the possible selection box around a possible selected object
self.delete_selection_shape()
for o in self.collection.get_list():
o.selection_shape_drawn = False
# and as a convenience move the focus to the Project tab because Selected tab is now empty but
# only when working on App
if self.call_source == 'app':
@ -10442,7 +10471,8 @@ class App(QtCore.QObject):
mirror=None)
if obj.kind.lower() == 'gerber':
color = self.defaults["global_plot_fill"][:-2]
# color = self.defaults["global_plot_fill"][:-2]
color = obj.fill_color[:-2]
elif obj.kind.lower() == 'excellon':
color = '#C40000'
elif obj.kind.lower() == 'geometry':
@ -11501,26 +11531,28 @@ class App(QtCore.QObject):
App.log.debug(" **************** Started PROEJCT loading... **************** ")
for obj in d['objs']:
def obj_init(obj_inst, app_inst):
try:
def obj_init(obj_inst, app_inst):
obj_inst.from_dict(obj)
obj_inst.from_dict(obj)
App.log.debug("Recreating from opened project an %s object: %s" %
(obj['kind'].capitalize(), obj['options']['name']))
App.log.debug("Recreating from opened project an %s object: %s" %
(obj['kind'].capitalize(), obj['options']['name']))
# for some reason, setting ui_title does not work when this method is called from Tcl Shell
# it's because the TclCommand is run in another thread (it inherit TclCommandSignaled)
if cli is None:
self.set_ui_title(name="{} {}: {}".format(_("Loading Project ... restoring"),
obj['kind'].upper(),
obj['options']['name']
)
)
# for some reason, setting ui_title does not work when this method is called from Tcl Shell
# it's because the TclCommand is run in another thread (it inherit TclCommandSignaled)
if cli is None:
self.set_ui_title(name="{} {}: {}".format(_("Loading Project ... restoring"),
obj['kind'].upper(),
obj['options']['name']
)
)
self.new_object(obj['kind'], obj['options']['name'], obj_init, active=False, fit=False, plot=plot)
self.new_object(obj['kind'], obj['options']['name'], obj_init, active=False, fit=False, plot=plot)
except Exception as e:
print('App.open_project() --> ' + str(e))
self.inform.emit('[success] %s: %s' %
(_("Project loaded from"), filename))
self.inform.emit('[success] %s: %s' % (_("Project loaded from"), filename))
self.should_we_save = False
self.file_opened.emit("project", filename)
@ -12350,6 +12382,68 @@ class App(QtCore.QObject):
# Clear pool to free memory
self.clear_pool()
def on_set_color_action_triggered(self):
new_color = self.defaults['global_plot_fill']
act_name = self.sender().text().lower()
sel_obj_list = self.collection.get_selected()
if not sel_obj_list:
return
if act_name == 'red':
new_color = '#FF0000' + \
str(hex(self.ui.general_defaults_form.general_gui_group.pf_color_alpha_slider.value())[2:])
if act_name == 'blue':
new_color = '#0000FF' + \
str(hex(self.ui.general_defaults_form.general_gui_group.pf_color_alpha_slider.value())[2:])
if act_name == 'yellow':
new_color = '#FFDF00' + \
str(hex(self.ui.general_defaults_form.general_gui_group.pf_color_alpha_slider.value())[2:])
if act_name == 'green':
new_color = '#00FF00' + \
str(hex(self.ui.general_defaults_form.general_gui_group.pf_color_alpha_slider.value())[2:])
if act_name == 'purple':
new_color = '#FF00FF' + \
str(hex(self.ui.general_defaults_form.general_gui_group.pf_color_alpha_slider.value())[2:])
if act_name == 'brown':
new_color = '#A52A2A' + \
str(hex(self.ui.general_defaults_form.general_gui_group.pf_color_alpha_slider.value())[2:])
if act_name == 'custom':
new_color = QtGui.QColor(self.defaults['global_plot_fill'][:7])
c_dialog = QtWidgets.QColorDialog()
plot_fill_color = c_dialog.getColor(initial=new_color)
if plot_fill_color.isValid() is False:
return
new_color = str(plot_fill_color.name()) + \
str(hex(self.ui.general_defaults_form.general_gui_group.pf_color_alpha_slider.value())[2:])
new_line_color = color_variant(new_color[:7], 0.7)
for sel_obj in sel_obj_list:
if self.is_legacy is False:
sel_obj.fill_color = new_color
sel_obj.outline_color = new_line_color
sel_obj.shapes.redraw(
update_colors=(new_color, new_line_color)
)
else:
sel_obj.fill_color = new_color
sel_obj.outline_color = new_line_color
sel_obj.shapes.redraw(
update_colors=(new_color, new_line_color)
)
def on_grid_snap_triggered(self, state):
if state:
self.ui.snap_infobar_label.setPixmap(QtGui.QPixmap(self.resource_location + '/snap_filled_16.png'))
else:
self.ui.snap_infobar_label.setPixmap(QtGui.QPixmap(self.resource_location + '/snap_16.png'))
def generate_cnc_job(self, objects):
self.report_usage("generate_cnc_job()")

View File

@ -474,7 +474,7 @@ class ToolsDB(QtWidgets.QWidget):
self.on_tool_request = callback_on_tool_request
self.offset_item_options = ["Path", "In", "Out", "Custom"]
self.type_item_options = [_("Iso"), _("Rough"), _("Finish")]
self.type_item_options = ["Iso", "Rough", "Finish"]
self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
'''
@ -760,13 +760,16 @@ class ToolsDB(QtWidgets.QWidget):
self.table_widget.setRowCount(len(self.db_tool_dict))
nr_crt = 0
for toolid, dict_val in self.db_tool_dict.items():
row = nr_crt
nr_crt += 1
t_name = dict_val['name']
self.add_tool_table_line(row, name=t_name, widget=self.table_widget, tooldict=dict_val)
try:
self.add_tool_table_line(row, name=t_name, widget=self.table_widget, tooldict=dict_val)
except Exception as e:
self.app.log.debug("ToolDB.build_db_ui.add_tool_table_line() --> %s" % str(e))
vertical_header = self.table_widget.verticalHeader()
vertical_header.hide()
@ -920,7 +923,7 @@ class ToolsDB(QtWidgets.QWidget):
dwelltime_item = FCDoubleSpinner()
dwelltime_item.set_precision(self.decimals)
dwelltime_item.set_range(0.0, 9999.9999)
dwelltime_item.set_range(0.0000, 9999.9999)
dwelltime_item.set_value(float(data['dwelltime']))
widget.setCellWidget(row, 18, dwelltime_item)
@ -936,7 +939,7 @@ class ToolsDB(QtWidgets.QWidget):
ecut_length_item = FCDoubleSpinner()
ecut_length_item.set_precision(self.decimals)
ecut_length_item.set_range(0.0, 9999.9999)
ecut_length_item.set_range(0.0000, 9999.9999)
ecut_length_item.set_value(data['extracut_length'])
widget.setCellWidget(row, 21, ecut_length_item)
@ -977,11 +980,8 @@ class ToolsDB(QtWidgets.QWidget):
Add a tool in the DB Tool Table
:return: None
"""
new_toolid = len(self.db_tool_dict) + 1
dict_elem = dict()
default_data = dict()
default_data.update({
"cutz": float(self.app.defaults["geometry_cutz"]),
"multidepth": self.app.defaults["geometry_multidepth"],
@ -997,7 +997,7 @@ class ToolsDB(QtWidgets.QWidget):
"dwelltime": float(self.app.defaults["geometry_dwelltime"]),
"ppname_g": self.app.defaults["geometry_ppname_g"],
"extracut": self.app.defaults["geometry_extracut"],
"extracut_length": self.app.defaults["geometry_extracut_length"],
"extracut_length": float(self.app.defaults["geometry_extracut_length"]),
"toolchange": self.app.defaults["geometry_toolchange"],
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
"toolchangez": float(self.app.defaults["geometry_toolchangez"]),
@ -1005,20 +1005,17 @@ class ToolsDB(QtWidgets.QWidget):
"endz": float(self.app.defaults["geometry_endz"])
})
dict_elem = dict()
dict_elem['name'] = 'new_tool'
dict_elem['tooldia'] = self.app.defaults["geometry_cnctooldia"]
dict_elem['offset'] = 'Path'
dict_elem['offset_value'] = 0.0
dict_elem['type'] = _('Rough')
dict_elem['type'] = 'Rough'
dict_elem['tool_type'] = 'C1'
dict_elem['data'] = default_data
self.db_tool_dict.update(
{
new_toolid: deepcopy(dict_elem)
}
)
new_toolid = len(self.db_tool_dict) + 1
self.db_tool_dict[new_toolid] = deepcopy(dict_elem)
# add the new entry to the Tools DB table
self.build_db_ui()
@ -1253,59 +1250,59 @@ class ToolsDB(QtWidgets.QWidget):
new_toolid = row + 1
for col in range(self.table_widget.columnCount()):
column_header_text = self.table_widget.horizontalHeaderItem(col).text()
if column_header_text == 'Tool Name':
if column_header_text == _('Tool Name'):
dict_elem['name'] = self.table_widget.item(row, col).text()
elif column_header_text == 'Tool Dia':
elif column_header_text == _('Tool Dia'):
dict_elem['tooldia'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'Tool Offset':
elif column_header_text == _('Tool Offset'):
dict_elem['offset'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'Custom Offset':
elif column_header_text == _('Custom Offset'):
dict_elem['offset_value'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'Tool Type':
elif column_header_text == _('Tool Type'):
dict_elem['type'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'Tool Shape':
elif column_header_text == _('Tool Shape'):
dict_elem['tool_type'] = self.table_widget.cellWidget(row, col).get_value()
else:
if column_header_text == 'Cut Z':
if column_header_text == _('Cut Z'):
default_data['cutz'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'MultiDepth':
elif column_header_text == _('MultiDepth'):
default_data['multidepth'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'DPP':
elif column_header_text == _('DPP'):
default_data['depthperpass'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'V-Dia':
elif column_header_text == _('V-Dia'):
default_data['vtipdia'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'V-Angle':
elif column_header_text == _('V-Angle'):
default_data['vtipangle'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'Travel Z':
elif column_header_text == _('Travel Z'):
default_data['travelz'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'FR':
elif column_header_text == _('FR'):
default_data['feedrate'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'FR Z':
elif column_header_text == _('FR Z'):
default_data['feedrate_z'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'FR Rapids':
elif column_header_text == _('FR Rapids'):
default_data['feedrate_rapid'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'Spindle Speed':
elif column_header_text == _('Spindle Speed'):
default_data['spindlespeed'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'Dwell':
elif column_header_text == _('Dwell'):
default_data['dwell'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'Dwelltime':
elif column_header_text == _('Dwelltime'):
default_data['dwelltime'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'Preprocessor':
elif column_header_text == _('Preprocessor'):
default_data['ppname_g'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'ExtraCut':
elif column_header_text == _('ExtraCut'):
default_data['extracut'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == "E-Cut Length":
elif column_header_text == _("E-Cut Length"):
default_data['extracut_length'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'Toolchange':
elif column_header_text == _('Toolchange'):
default_data['toolchange'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'Toolchange XY':
elif column_header_text == _('Toolchange XY'):
default_data['toolchangexy'] = self.table_widget.item(row, col).text()
elif column_header_text == 'Toolchange Z':
elif column_header_text == _('Toolchange Z'):
default_data['toolchangez'] = self.table_widget.cellWidget(row, col).get_value()
elif column_header_text == 'Start Z':
elif column_header_text == _('Start Z'):
default_data['startz'] = float(self.table_widget.item(row, col).text()) \
if self.table_widget.item(row, col).text() is not '' else None
elif column_header_text == 'End Z':
elif column_header_text == _('End Z'):
default_data['endz'] = self.table_widget.cellWidget(row, col).get_value()
dict_elem['data'] = default_data
@ -1355,3 +1352,33 @@ class ToolsDB(QtWidgets.QWidget):
def closeEvent(self, QCloseEvent):
super().closeEvent(QCloseEvent)
def color_variant(hex_color, bright_factor=1):
"""
Takes a color in HEX format #FF00FF and produces a lighter or darker variant
:param hex_color: color to change
:param bright_factor: factor to change the color brightness [0 ... 1]
:return: modified color
"""
if len(hex_color) != 7:
print("Color is %s, but needs to be in #FF00FF format. Returning original color." % hex_color)
return hex_color
if bright_factor > 1.0:
bright_factor = 1.0
if bright_factor < 0.0:
bright_factor = 0.0
rgb_hex = [hex_color[x:x + 2] for x in [1, 3, 5]]
new_rgb = list()
for hex_value in rgb_hex:
# adjust each color channel and turn it into a INT suitable as argument for hex()
mod_color = round(int(hex_value, 16) * bright_factor)
# make sure that each color channel has two digits without the 0x prefix
mod_color_hex = str(hex(mod_color)[2:]).zfill(2)
new_rgb.append(mod_color_hex)
return "#" + "".join([i for i in new_rgb])

View File

@ -15,7 +15,9 @@ from shapely.geometry import Point, Polygon, MultiPolygon, MultiLineString, Line
from shapely.ops import cascaded_union
import shapely.affinity as affinity
from copy import deepcopy, copy
from copy import deepcopy
from copy import copy
from io import StringIO
import traceback
import inspect # TODO: For debugging only.
@ -30,6 +32,8 @@ from flatcamParsers.ParseGerber import Gerber
from camlib import Geometry, CNCjob
import FlatCAMApp
from flatcamGUI.VisPyVisuals import ShapeCollection
import tkinter as tk
import os, sys, itertools
import ezdxf
@ -104,6 +108,7 @@ class FlatCAMObj(QtCore.QObject):
if self.app.is_legacy is False:
self.shapes = self.app.plotcanvas.new_shape_group()
# self.shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, pool=self.app.pool, layers=2)
else:
self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name=name)
@ -123,6 +128,9 @@ class FlatCAMObj(QtCore.QObject):
self.isHovering = False
self.notHovering = True
# Flag to show if a selection shape is drawn
self.selection_shape_drawn = False
# self.units = 'IN'
self.units = self.app.defaults['units']
@ -591,7 +599,9 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
def __init__(self, name):
self.decimals = self.app.decimals
Gerber.__init__(self, steps_per_circle=int(self.app.defaults["gerber_circle_steps"]))
self.circle_steps = int(self.app.defaults["gerber_circle_steps"])
Gerber.__init__(self, steps_per_circle=self.circle_steps)
FlatCAMObj.__init__(self, name)
self.kind = "gerber"
@ -649,10 +659,13 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
self.units_found = self.app.defaults['units']
self.fill_color = self.app.defaults['global_plot_fill']
self.outline_color = self.app.defaults['global_plot_line']
# Attributes to be included in serialization
# Always append to it because it carries contents
# from predecessors.
self.ser_attrs += ['options', 'kind']
self.ser_attrs += ['options', 'kind', 'fill_color', 'outline_color']
def set_ui(self, ui):
"""
@ -736,6 +749,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
self.ui.aperture_table_visibility_cb.hide()
self.ui.milling_type_label.hide()
self.ui.milling_type_radio.hide()
self.ui.iso_type_label.hide()
self.ui.iso_type_radio.hide()
self.ui.follow_cb.hide()
@ -1665,12 +1679,12 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
if 'color' in kwargs:
color = kwargs['color']
else:
color = self.app.defaults['global_plot_line']
color = self.outline_color
if 'face_color' in kwargs:
face_color = kwargs['face_color']
else:
face_color = self.app.defaults['global_plot_fill']
face_color = self.fill_color
if 'visible' not in kwargs:
visible = self.options['plot']
@ -1740,7 +1754,10 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
for el in g:
self.add_shape(shape=el, color=random_color() if self.options['multicolored'] else 'black',
visible=visible)
self.shapes.redraw()
self.shapes.redraw(
# update_colors=(self.fill_color, self.outline_color),
# indexes=self.app.plotcanvas.shape_collection.data.keys()
)
except (ObjectDeleted, AttributeError):
self.shapes.clear(update=True)
except Exception as e:
@ -2184,6 +2201,10 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
Gerber.skew(self, angle_x=angle_x, angle_y=angle_y, point=point)
self.replotApertures.emit()
def buffer(self, distance, join):
Gerber.buffer(self, distance=distance, join=join)
self.replotApertures.emit()
def serialize(self):
return {
"options": self.options,
@ -2202,7 +2223,9 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
def __init__(self, name):
self.decimals = self.app.decimals
Excellon.__init__(self, geo_steps_per_circle=int(self.app.defaults["geometry_circle_steps"]))
self.circle_steps = int(self.app.defaults["geometry_circle_steps"])
Excellon.__init__(self, geo_steps_per_circle=self.circle_steps)
FlatCAMObj.__init__(self, name)
self.kind = "excellon"
@ -3530,8 +3553,11 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
def __init__(self, name):
self.decimals = self.app.decimals
self.circle_steps = int(self.app.defaults["geometry_circle_steps"])
FlatCAMObj.__init__(self, name)
Geometry.__init__(self, geo_steps_per_circle=int(self.app.defaults["geometry_circle_steps"]))
Geometry.__init__(self, geo_steps_per_circle=self.circle_steps)
self.kind = "geometry"
@ -3612,6 +3638,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
# this variable can be updated by the Object that generates the geometry
self.tool_type = 'C1'
# save here the old value for the Cut Z before it is changed by selecting a V-shape type tool in the tool table
self.old_cutz = self.app.defaults["geometry_cutz"]
# Attributes to be included in serialization
# Always append to it because it carries contents
# from predecessors.
@ -3760,6 +3789,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
self.ui.name_entry.set_value(self.options['name'])
self.ui_connect()
self.ui.e_cut_entry.setDisabled(False) if self.ui.extracut_cb.get_value() else \
self.ui.e_cut_entry.setDisabled(True)
def set_ui(self, ui):
FlatCAMObj.set_ui(self, ui)
@ -3847,15 +3879,18 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
if def_key == opt_key:
self.default_data[def_key] = deepcopy(opt_val)
try:
temp_tools = self.options["cnctooldia"].split(",")
tools_list = [
float(eval(dia)) for dia in temp_tools if dia != ''
]
except Exception as e:
log.error("At least one tool diameter needed. Verify in Edit -> Preferences -> Geometry General -> "
"Tool dia. %s" % str(e))
return
if type(self.options["cnctooldia"]) == float:
tools_list = [self.options["cnctooldia"]]
else:
try:
temp_tools = self.options["cnctooldia"].split(",")
tools_list = [
float(eval(dia)) for dia in temp_tools if dia != ''
]
except Exception as e:
log.error("FlatCAMGeometry.set_ui() -> At least one tool diameter needed. "
"Verify in Edit -> Preferences -> Geometry General -> Tool dia. %s" % str(e))
return
self.tooluid += 1
@ -3936,7 +3971,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
else:
self.ui.level.setText('<span style="color:red;"><b>%s</b></span>' % _('Advanced'))
self.ui.e_cut_entry.setDisabled(True)
self.ui.e_cut_entry.setDisabled(False) if self.app.defaults['geometry_extracut'] else \
self.ui.e_cut_entry.setDisabled(True)
self.ui.extracut_cb.toggled.connect(lambda state: self.ui.e_cut_entry.setDisabled(not state))
self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
self.ui.generate_cnc_button.clicked.connect(self.on_generatecnc_button_click)
@ -3949,6 +3986,10 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
self.ui.addtool_from_db_btn.clicked.connect(self.on_tool_add_from_db_clicked)
self.ui.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked)
self.ui.cutz_entry.returnPressed.connect(self.on_cut_z_changed)
def on_cut_z_changed(self):
self.old_cutz = self.ui.cutz_entry.get_value()
def set_tool_offset_visibility(self, current_row):
if current_row is None:
@ -4589,6 +4630,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
if cb_txt == 'V':
idx = self.ui.geo_tools_table.cellWidget(cw_row, 3).findText(_('Iso'))
self.ui.geo_tools_table.cellWidget(cw_row, 3).setCurrentIndex(idx)
else:
self.ui.cutz_entry.set_value(self.old_cutz)
self.ui_update_v_shape(tool_type_txt=self.ui.geo_tools_table.cellWidget(cw_row, 4).currentText())
def update_form(self, dict_storage):

View File

@ -321,6 +321,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
sel = len(self.view.selectedIndexes()) > 0
self.app.ui.menuprojectenable.setEnabled(sel)
self.app.ui.menuprojectdisable.setEnabled(sel)
self.app.ui.menuprojectcolor.setEnabled(sel)
self.app.ui.menuprojectviewsource.setEnabled(sel)
self.app.ui.menuprojectcopy.setEnabled(sel)
@ -334,8 +335,12 @@ class ObjectCollection(QtCore.QAbstractItemModel):
self.app.ui.menuprojectedit.setVisible(True)
self.app.ui.menuprojectsave.setVisible(True)
self.app.ui.menuprojectviewsource.setVisible(True)
self.app.ui.menuprojectcolor.setEnabled(False)
for obj in self.get_selected():
if type(obj) == FlatCAMGerber:
self.app.ui.menuprojectcolor.setEnabled(True)
if type(obj) != FlatCAMGeometry:
self.app.ui.menuprojectgeneratecnc.setVisible(False)
if type(obj) != FlatCAMGeometry and type(obj) != FlatCAMExcellon and type(obj) != FlatCAMGerber:

View File

@ -9,10 +9,39 @@ CAD program, and create G-Code for Isolation routing.
=================================================
23.12.2019
- some fixes in the Legacy(2D) graphic mode regarding the possibility of changing the color of the Gerber objects
- added a method to darken the outline color for Gerber objects when they have the color set
- when Printing as PDF Gerber objects now the rendered color is the print color
- speed up the plotting in OpenGL(3D) graphic mode
- spped up the color setting for Gerber object when using the OpenGL(3D) graphic mode
- setting color for Gerber objects work on a selection of Gerber objects
- ~~when the selection is changed in the Project Tree the selection shape on canvas is deleted~~
- if an object is selected on Project Tree and it does not have the selection shape drawn, first click on canvas over it will draw the selection shape
- in Tool Transform added a new feature named 'Buffer'. For Geometry and Gerber objects will create (and replace) a geometry at a distance from the original geometry and for Excellon will adjust the Tool diameters
- solved issue #355 - when the tool diameter field in the Edit → Preferences → Geometry → Geometry General → Tools → Tool dia is only one the app failed to read it
- solved issue #356 - in Tools DB can not be added more than one tool if a translation is active
22.12.2019
- added a new option for the Gerber objects: on the project context menu now can be chosen a color for the selected Gerber object
- fixed issue in Gerber UI where a label was not hidden when in Basic mode
- added the color parameters of the objects to the serializable attributes
- fixed Gerber object color set for Legacy(2D) graphic engine; glitch on the OpenGL(3D) graphic engine
- fixed the above mentioned glitch in the OpenGL(3D) graphic engine when an Gerber object has been set with a color
21.12.2019
- fixed a typo in Distance Tool
20.12.2019
- fixed a rare issue in the generation of non-copper-region geometry started from the Gerber Object UI (selected tab)
- Print function is now printing a PDF file for a selection of objects in the colors from canvas
- added an icon in the infobar that will show more clearly the status of the grid snapping
- in Geometry Object UI (selected tab) when a tool type is changed from no matter what to V-shape, the cut_z value is saved and when the tool type is changed back to something different than V-shape, this saved cut-z value is restored
- fixed re-cut length entry not staying disabled when the re-cut cb is not checked
19.12.2019

View File

@ -2118,6 +2118,69 @@ class Geometry(object):
# self.solid_geometry = affinity.skew(self.solid_geometry, angle_x, angle_y,
# origin=(px, py))
def buffer(self, distance, join):
"""
:param distance:
:param join:
:return:
"""
log.debug("camlib.Geometry.buffer()")
if distance == 0:
return
def buffer_geom(obj):
if type(obj) is list:
new_obj = []
for g in obj:
new_obj.append(buffer_geom(g))
return new_obj
else:
try:
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
return obj.buffer(distance, resolution=self.geo_steps_per_circle, join_style=join)
except AttributeError:
return obj
try:
if self.multigeo is True:
for tool in self.tools:
# variables to display the percentage of work done
self.geo_len = 0
try:
for __ in self.tools[tool]['solid_geometry']:
self.geo_len += 1
except TypeError:
self.geo_len = 1
self.old_disp_number = 0
self.el_count = 0
self.tools[tool]['solid_geometry'] = buffer_geom(self.tools[tool]['solid_geometry'])
# variables to display the percentage of work done
self.geo_len = 0
try:
for __ in self.solid_geometry:
self.geo_len += 1
except TypeError:
self.geo_len = 1
self.old_disp_number = 0
self.el_count = 0
self.solid_geometry = buffer_geom(self.solid_geometry)
self.app.inform.emit('[success] %s...' % _('Object was buffered'))
except AttributeError:
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed to buffer. No object selected"))
self.app.proc_container.new_text = ''
class AttrDict(dict):
def __init__(self, *args, **kwargs):

View File

@ -2744,6 +2744,7 @@ class FlatCAMExcEditor(QtCore.QObject):
# start with GRID toolbar activated
if self.app.ui.grid_snap_btn.isChecked() is False:
self.app.ui.grid_snap_btn.trigger()
self.app.on_grid_snap_triggered(state=True)
self.app.ui.popmenu_disable.setVisible(False)
self.app.ui.cmenu_newmenu.menuAction().setVisible(False)

View File

@ -3679,6 +3679,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
# start with GRID toolbar activated
if self.app.ui.grid_snap_btn.isChecked() is False:
self.app.ui.grid_snap_btn.trigger()
self.app.on_grid_snap_triggered(state=True)
def on_buffer_tool(self):
buff_tool = BufferSelectionTool(self.app, self)

View File

@ -3530,6 +3530,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
# start with GRID toolbar activated
if self.app.ui.grid_snap_btn.isChecked() is False:
self.app.ui.grid_snap_btn.trigger()
self.app.on_grid_snap_triggered(state=True)
# adjust the visibility of some of the canvas context menu
self.app.ui.popmenu_edit.setVisible(False)

View File

@ -634,11 +634,39 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
# ########################## Project Tab Context Menu # ##################
# ########################################################################
self.menuproject = QtWidgets.QMenu()
self.menuprojectenable = self.menuproject.addAction(
QtGui.QIcon(self.app.resource_location + '/replot32.png'), _('Enable Plot'))
self.menuprojectdisable = self.menuproject.addAction(
QtGui.QIcon(self.app.resource_location + '/clear_plot32.png'), _('Disable Plot'))
self.menuproject.addSeparator()
self.menuprojectcolor = self.menuproject.addMenu(
QtGui.QIcon(self.app.resource_location + '/set_color32.png'), _('Set Color'))
self.menuproject_red = self.menuprojectcolor.addAction(
QtGui.QIcon(self.app.resource_location + '/red32.png'), _('Red'))
self.menuproject_blue = self.menuprojectcolor.addAction(
QtGui.QIcon(self.app.resource_location + '/blue32.png'), _('Blue'))
self.menuproject_yellow = self.menuprojectcolor.addAction(
QtGui.QIcon(self.app.resource_location + '/yellow32.png'), _('Yellow'))
self.menuproject_green = self.menuprojectcolor.addAction(
QtGui.QIcon(self.app.resource_location + '/green32.png'), _('Green'))
self.menuproject_purple = self.menuprojectcolor.addAction(
QtGui.QIcon(self.app.resource_location + '/violet32.png'), _('Purple'))
self.menuproject_brown = self.menuprojectcolor.addAction(
QtGui.QIcon(self.app.resource_location + '/brown32.png'), _('Brown'))
self.menuproject_custom = self.menuprojectcolor.addAction(
QtGui.QIcon(self.app.resource_location + '/set_color32.png'), _('Custom'))
self.menuproject.addSeparator()
self.menuprojectgeneratecnc = self.menuproject.addAction(
QtGui.QIcon(self.app.resource_location + '/cnc32.png'), _('Generate CNC'))
self.menuprojectviewsource = self.menuproject.addAction(
@ -2149,6 +2177,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.fcinfo = FlatCAMInfoBar(app=self.app)
self.infobar.addWidget(self.fcinfo, stretch=1)
self.snap_infobar_label = QtWidgets.QLabel()
self.snap_infobar_label.setPixmap(QtGui.QPixmap(self.app.resource_location + '/snap_16.png'))
self.infobar.addWidget(self.snap_infobar_label)
self.rel_position_label = QtWidgets.QLabel(
"<b>Dx</b>: 0.0000&nbsp;&nbsp; <b>Dy</b>: 0.0000&nbsp;&nbsp;&nbsp;&nbsp;")
self.rel_position_label.setMinimumWidth(110)

View File

@ -1577,7 +1577,7 @@ class GeometryObjectUI(ObjectUI):
self.cncfeedrate_rapid_entry.hide()
# Cut over 1st point in path
self.extracut_cb = FCCheckBox('%s' % _('Re-cut'))
self.extracut_cb = FCCheckBox('%s:' % _('Re-cut'))
self.extracut_cb.setToolTip(
_("In order to remove possible\n"
"copper leftovers where first cut\n"
@ -1599,8 +1599,6 @@ class GeometryObjectUI(ObjectUI):
self.grid3.addWidget(self.extracut_cb, 13, 0)
self.grid3.addWidget(self.e_cut_entry, 13, 1)
self.ois_e_cut = OptionalInputSection(self.extracut_cb, [self.e_cut_entry])
# Spindlespeed
spdlabel = QtWidgets.QLabel('%s:' % _('Spindle speed'))
spdlabel.setToolTip(

View File

@ -957,9 +957,17 @@ class ShapeCollectionLegacy:
:param linewidth: the width of the line
:return:
"""
self._color = color[:-2] if color is not None else None
self._face_color = face_color[:-2] if face_color is not None else None
self._alpha = int(face_color[-2:], 16) / 255 if face_color is not None else 0.75
self._color = color if color is not None else "#006E20"
self._face_color = face_color if face_color is not None else "#BBF268"
if len(self._color) > 7:
self._color = self._color[:7]
if len(self._face_color) > 7:
self._face_color = self._face_color[:7]
# self._alpha = int(self._face_color[-2:], 16) / 255
self._alpha = 0.75
if alpha is not None:
self._alpha = alpha
@ -1033,7 +1041,7 @@ class ShapeCollectionLegacy:
if update is True:
self.redraw()
def redraw(self):
def redraw(self, update_colors=None):
"""
This draw the shapes in the shapes collection, on canvas
@ -1087,7 +1095,6 @@ class ShapeCollectionLegacy:
self.axes.plot(x, y, local_shapes[element]['color'],
linestyle='-',
linewidth=local_shapes[element]['linewidth'])
elif obj_type == 'gerber':
if self.obj.options["multicolored"]:
linespec = '-'
@ -1095,16 +1102,25 @@ class ShapeCollectionLegacy:
linespec = 'k-'
if self.obj.options["solid"]:
if update_colors:
gerber_fill_color = update_colors[0]
gerber_outline_color = update_colors[1]
else:
gerber_fill_color = local_shapes[element]['face_color']
gerber_outline_color = local_shapes[element]['color']
try:
patch = PolygonPatch(local_shapes[element]['shape'],
facecolor=local_shapes[element]['face_color'],
edgecolor=local_shapes[element]['color'],
facecolor=gerber_fill_color,
edgecolor=gerber_outline_color,
alpha=local_shapes[element]['alpha'],
zorder=2)
self.axes.add_patch(patch)
except AssertionError:
FlatCAMApp.App.log.warning("A geometry component was not a polygon:")
FlatCAMApp.App.log.warning(str(element))
except Exception as e:
FlatCAMApp.App.log.debug("PlotCanvasLegacy.ShepeCollectionLegacy.redraw() --> %s" % str(e))
else:
x, y = local_shapes[element]['shape'].exterior.xy
self.axes.plot(x, y, linespec)

View File

@ -5378,7 +5378,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
grid0.addWidget(self.skewy_label, 4, 0)
grid0.addWidget(self.skewy_entry, 4, 1)
# ## Scale factor on X axis
# ## Scale
scale_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Scale"))
grid0.addWidget(scale_title_lbl, 5, 0, 1, 2)
@ -5425,7 +5425,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
)
grid0.addWidget(self.reference_cb, 8, 1)
# ## Offset distance on X axis
# ## Offset
offset_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Offset"))
grid0.addWidget(offset_title_lbl, 9, 0, 1, 2)
@ -5454,6 +5454,10 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
grid0.addWidget(self.offy_label, 11, 0)
grid0.addWidget(self.offy_entry, 11, 1)
# ## Mirror
mirror_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Mirror"))
grid0.addWidget(mirror_title_lbl, 12, 0, 1, 2)
# ## Mirror (Flip) Reference Point
self.mirror_reference_cb = FCCheckBox('%s' % _("Mirror Reference"))
self.mirror_reference_cb.setToolTip(
@ -5466,9 +5470,9 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
"Then click Add button to insert coordinates.\n"
"Or enter the coords in format (x, y) in the\n"
"Point Entry field and click Flip on X(Y)"))
grid0.addWidget(self.mirror_reference_cb, 12, 0, 1, 2)
grid0.addWidget(self.mirror_reference_cb, 13, 0, 1, 2)
self.flip_ref_label = QtWidgets.QLabel('<b>%s</b>' % _("Mirror Reference point"))
self.flip_ref_label = QtWidgets.QLabel('%s' % _("Mirror Reference point"))
self.flip_ref_label.setToolTip(
_("Coordinates in format (x, y) used as reference for mirroring.\n"
"The 'x' in (x, y) will be used when using Flip on X and\n"
@ -5476,8 +5480,42 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
)
self.flip_ref_entry = EvalEntry2("(0, 0)")
grid0.addWidget(self.flip_ref_label, 13, 0, 1, 2)
grid0.addWidget(self.flip_ref_entry, 14, 0, 1, 2)
grid0.addWidget(self.flip_ref_label, 14, 0, 1, 2)
grid0.addWidget(self.flip_ref_entry, 15, 0, 1, 2)
# ## Buffer
buffer_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("Buffer"))
grid0.addWidget(buffer_title_lbl, 16, 0, 1, 2)
self.buffer_label = QtWidgets.QLabel('%s:' % _("Distance"))
self.buffer_label.setToolTip(
_("A positive value will create the effect of dilation,\n"
"while a negative value will create the effect of erosion.\n"
"Each geometry element of the object will be increased\n"
"or decreased with the 'distance'.")
)
self.buffer_entry = FCDoubleSpinner()
self.buffer_entry.set_precision(self.decimals)
self.buffer_entry.setSingleStep(0.1)
self.buffer_entry.setWrapping(True)
self.buffer_entry.set_range(-9999.9999, 9999.9999)
grid0.addWidget(self.buffer_label, 17, 0)
grid0.addWidget(self.buffer_entry, 17, 1)
self.buffer_rounded_cb = FCCheckBox()
self.buffer_rounded_cb.setText('%s' % _("Rounded"))
self.buffer_rounded_cb.setToolTip(
_("If checked then the buffer will surround the buffered shape,\n"
"every corner will be rounded.\n"
"If not checked then the buffer will follow the exact geometry\n"
"of the buffered shape.")
)
grid0.addWidget(self.buffer_rounded_cb, 18, 0, 1, 2)
grid0.addWidget(QtWidgets.QLabel(''), 19, 0, 1, 2)
self.layout.addStretch()

View File

@ -17,10 +17,9 @@ from flatcamGUI.VisPyTesselators import GLUTess
class FlatCAMLineVisual(LineVisual):
def __init__(self, pos=None, color=(0.5, 0.5, 0.5, 1), width=1, connect='strip',
method='gl', antialias=False):
LineVisual.__init__(self, pos=None, color=(0.5, 0.5, 0.5, 1), width=1, connect='strip',
method='gl', antialias=True)
def __init__(self, pos=None, color=(0.5, 0.5, 0.5, 1), width=1, connect='strip', method='gl', antialias=False):
LineVisual.__init__(self, pos=pos, color=color, width=width, connect=connect,
method=method, antialias=True)
def clear_data(self):
self._bounds = None
@ -46,44 +45,48 @@ def _update_shape_buffers(data, triangulation='glu'):
geo, color, face_color, tolerance = data['geometry'], data['color'], data['face_color'], data['tolerance']
if geo is not None and not geo.is_empty:
simple = geo.simplify(tolerance) if tolerance else geo # Simplified shape
pts = [] # Shape line points
tri_pts = [] # Mesh vertices
tri_tris = [] # Mesh faces
simplified_geo = geo.simplify(tolerance) if tolerance else geo # Simplified shape
pts = [] # Shape line points
tri_pts = [] # Mesh vertices
tri_tris = [] # Mesh faces
if type(geo) == LineString:
# Prepare lines
pts = _linestring_to_segments(list(simple.coords))
pts = _linestring_to_segments(list(simplified_geo.coords))
elif type(geo) == LinearRing:
# Prepare lines
pts = _linearring_to_segments(list(simple.coords))
pts = _linearring_to_segments(list(simplified_geo.coords))
elif type(geo) == Polygon:
# Prepare polygon faces
if face_color is not None:
if triangulation == 'glu':
gt = GLUTess()
tri_tris, tri_pts = gt.triangulate(simple)
tri_tris, tri_pts = gt.triangulate(simplified_geo)
else:
print("Triangulation type '%s' isn't implemented. Drawing only edges." % triangulation)
# Prepare polygon edges
if color is not None:
pts = _linearring_to_segments(list(simple.exterior.coords))
for ints in simple.interiors:
pts = _linearring_to_segments(list(simplified_geo.exterior.coords))
for ints in simplified_geo.interiors:
pts += _linearring_to_segments(list(ints.coords))
# Appending data for mesh
if len(tri_pts) > 0 and len(tri_tris) > 0:
mesh_tris += tri_tris
mesh_vertices += tri_pts
mesh_colors += [Color(face_color).rgba] * (len(tri_tris) // 3)
face_color_rgba = Color(face_color).rgba
# mesh_colors += [face_color_rgba] * (len(tri_tris) // 3)
mesh_colors += [face_color_rgba for __ in range(len(tri_tris) // 3)]
# Appending data for line
if len(pts) > 0:
line_pts += pts
line_colors += [Color(color).rgba] * len(pts)
colo_rgba = Color(color).rgba
# line_colors += [colo_rgba] * len(pts)
line_colors += [colo_rgba for __ in range(len(pts))]
# Store buffers
data['line_pts'] = line_pts
@ -158,11 +161,14 @@ class ShapeGroup(object):
if update:
self._collection.redraw([]) # Skip waiting results
def redraw(self):
def redraw(self, update_colors=None):
"""
Redraws shape collection
"""
self._collection.redraw(self._indexes)
if update_colors:
self._collection.redraw(self._indexes, update_colors=update_colors)
else:
self._collection.redraw(self._indexes)
@property
def visible(self):
@ -228,9 +234,9 @@ class ShapeCollectionVisual(CompoundVisual):
pass
m.set_gl_state(polygon_offset_fill=True, polygon_offset=(1, 1), cull_face=False)
for l in self._lines:
for lne in self._lines:
pass
l.set_gl_state(blend=True)
lne.set_gl_state(blend=True)
self.freeze()
@ -245,6 +251,8 @@ class ShapeCollectionVisual(CompoundVisual):
Line/edge color
:param face_color: str, tuple
Polygon face color
:param alpha: str
Polygon transparency
:param visible: bool
Shape visibility
:param update: bool
@ -271,11 +279,11 @@ class ShapeCollectionVisual(CompoundVisual):
# Add data to process pool if pool exists
try:
self.results[key] = self.pool.map_async(_update_shape_buffers, [self.data[key]])
except Exception as e:
except Exception:
self.data[key] = _update_shape_buffers(self.data[key])
if update:
self.redraw() # redraw() waits for pool process end
self.redraw() # redraw() waits for pool process end
return key
@ -309,6 +317,134 @@ class ShapeCollectionVisual(CompoundVisual):
if update:
self.__update()
def update_color(self, new_mesh_color=None, new_line_color=None, indexes=None):
if new_mesh_color is None and new_line_color is None:
return
if not self.data:
return
# if a new color is empty string then make it None so it will not be updated
# if a new color is valid then transform it here in a format palatable
mesh_color_rgba = None
line_color_rgba = None
if new_mesh_color:
if new_mesh_color != '':
mesh_color_rgba = Color(new_mesh_color).rgba
else:
new_mesh_color = None
if new_line_color:
if new_line_color != '':
line_color_rgba = Color(new_line_color).rgba
else:
new_line_color = None
mesh_colors = [[] for _ in range(0, len(self._meshes))] # Face colors
line_colors = [[] for _ in range(0, len(self._meshes))] # Line colors
line_pts = [[] for _ in range(0, len(self._lines))] # Vertices for line
# Lock sub-visuals updates
self.update_lock.acquire(True)
# Merge shapes buffers
if indexes is None:
for k, data in list(self.data.items()):
if data['visible'] and 'line_pts' in data:
if new_mesh_color and new_mesh_color != '':
dim_mesh_tris = (len(data['mesh_tris']) // 3)
if dim_mesh_tris != 0:
try:
mesh_colors[data['layer']] += [mesh_color_rgba] * dim_mesh_tris
self.data[k]['face_color'] = new_mesh_color
data['mesh_colors'] = [mesh_color_rgba for __ in range(len(data['mesh_colors']))]
except Exception as e:
print("VisPyVisuals.ShapeCollectionVisual.update_color(). "
"Create mesh colors --> Data error. %s" % str(e))
if new_line_color and new_line_color != '':
dim_line_pts = (len(data['line_pts']))
if dim_line_pts != 0:
try:
line_pts[data['layer']] += data['line_pts']
line_colors[data['layer']] += [line_color_rgba] * dim_line_pts
self.data[k]['color'] = new_line_color
data['line_colors'] = [mesh_color_rgba for __ in range(len(data['line_colors']))]
except Exception as e:
print("VisPyVisuals.ShapeCollectionVisual.update_color(). "
"Create line colors --> Data error. %s" % str(e))
else:
for k, data in list(self.data.items()):
if data['visible'] and 'line_pts' in data:
dim_mesh_tris = (len(data['mesh_tris']) // 3)
dim_line_pts = (len(data['line_pts']))
if k in indexes:
if new_mesh_color and new_mesh_color != '':
if dim_mesh_tris != 0:
try:
mesh_colors[data['layer']] += [mesh_color_rgba] * dim_mesh_tris
self.data[k]['face_color'] = new_mesh_color
data['mesh_colors'] = [mesh_color_rgba for __ in range(len(data['mesh_colors']))]
except Exception as e:
print("VisPyVisuals.ShapeCollectionVisual.update_color(). "
"Create mesh colors --> Data error. %s" % str(e))
if new_line_color and new_line_color != '':
if dim_line_pts != 0:
try:
line_pts[data['layer']] += data['line_pts']
line_colors[data['layer']] += [line_color_rgba] * dim_line_pts
self.data[k]['color'] = new_line_color
data['line_colors'] = [mesh_color_rgba for __ in range(len(data['line_colors']))]
except Exception as e:
print("VisPyVisuals.ShapeCollectionVisual.update_color(). "
"Create line colors --> Data error. %s" % str(e))
else:
if dim_mesh_tris != 0:
try:
mesh_colors[data['layer']] += [Color(data['face_color']).rgba] * dim_mesh_tris
except Exception as e:
print("VisPyVisuals.ShapeCollectionVisual.update_color(). "
"Create mesh colors --> Data error. %s" % str(e))
if dim_line_pts != 0:
try:
line_pts[data['layer']] += data['line_pts']
line_colors[data['layer']] += [Color(data['color']).rgba] * dim_line_pts
except Exception as e:
print("VisPyVisuals.ShapeCollectionVisual.update_color(). "
"Create line colors --> Data error. %s" % str(e))
# Updating meshes
if new_mesh_color and new_mesh_color != '':
for i, mesh in enumerate(self._meshes):
if mesh_colors[i]:
try:
mesh._meshdata.set_face_colors(colors=np.asarray(mesh_colors[i]))
mesh.mesh_data_changed()
except Exception as e:
print("VisPyVisuals.ShapeCollectionVisual.update_color(). "
"Apply mesh colors --> Data error. %s" % str(e))
# Updating lines
if new_line_color and new_line_color != '':
for i, line in enumerate(self._lines):
if len(line_pts[i]) > 0:
try:
line._color = np.asarray(line_colors[i])
line._changed['color'] = True
line.update()
except Exception as e:
print("VisPyVisuals.ShapeCollectionVisual.update_color(). "
"Apply line colors --> Data error. %s" % str(e))
else:
line.clear_data()
self.update_lock.release()
def __update(self):
"""
Merges internal buffers, sets data to visuals, redraws collection on scene
@ -328,20 +464,23 @@ class ShapeCollectionVisual(CompoundVisual):
try:
line_pts[data['layer']] += data['line_pts']
line_colors[data['layer']] += data['line_colors']
mesh_tris[data['layer']] += [x + len(mesh_vertices[data['layer']])
for x in data['mesh_tris']]
mesh_tris[data['layer']] += [x + len(mesh_vertices[data['layer']]) for x in data['mesh_tris']]
mesh_vertices[data['layer']] += data['mesh_vertices']
mesh_colors[data['layer']] += data['mesh_colors']
except Exception as e:
print("Data error", e)
print("VisPyVisuals.ShapeCollectionVisual._update() --> Data error. %s" % str(e))
# Updating meshes
for i, mesh in enumerate(self._meshes):
if len(mesh_vertices[i]) > 0:
set_state(polygon_offset_fill=False)
mesh.set_data(np.asarray(mesh_vertices[i]), np.asarray(mesh_tris[i], dtype=np.uint32)
.reshape((-1, 3)), face_colors=np.asarray(mesh_colors[i]))
faces_array = np.asarray(mesh_tris[i], dtype=np.uint32)
mesh.set_data(
vertices=np.asarray(mesh_vertices[i]),
faces=faces_array.reshape((-1, 3)),
face_colors=np.asarray(mesh_colors[i])
)
else:
mesh.set_data()
@ -350,17 +489,20 @@ class ShapeCollectionVisual(CompoundVisual):
# Updating lines
for i, line in enumerate(self._lines):
if len(line_pts[i]) > 0:
line.set_data(np.asarray(line_pts[i]), np.asarray(line_colors[i]), self._line_width, 'segments')
line.set_data(
pos=np.asarray(line_pts[i]),
color=np.asarray(line_colors[i]),
width=self._line_width,
connect='segments')
else:
line.clear_data()
line._bounds_changed()
self._bounds_changed()
self.update_lock.release()
def redraw(self, indexes=None):
def redraw(self, indexes=None, update_colors=None):
"""
Redraws collection
:param indexes: list
@ -369,19 +511,30 @@ class ShapeCollectionVisual(CompoundVisual):
# Only one thread can update data
self.results_lock.acquire(True)
for i in list(self.data.copy().keys()) if not indexes else indexes:
if i in list(self.results.copy().keys()):
for i in list(self.data.keys()) if not indexes else indexes:
if i in list(self.results.keys()):
try:
self.results[i].wait() # Wait for process results
if i in self.data:
self.data[i] = self.results[i].get()[0] # Store translated data
del self.results[i]
except Exception as e:
print(e, indexes)
print("VisPyVisuals.ShapeCollectionVisual.redraw() --> Data error = %s. Indexes = %s" %
(str(e), str(indexes)))
self.results_lock.release()
self.__update()
if update_colors is None:
self.__update()
else:
try:
self.update_color(
new_mesh_color=update_colors[0],
new_line_color=update_colors[1],
indexes=indexes
)
except Exception as e:
print("VisPyVisuals.ShapeCollectionVisual.redraw() --> Update colors error = %s." % str(e))
def lock_updates(self):
self.update_lock.acquire(True)
@ -489,7 +642,7 @@ class TextCollectionVisual(TextVisual):
self.lock.release()
# Prepare data for translation
self.data[key] = {'text': text, 'pos': pos, 'visible': visible,'font_size': font_size, 'color': color}
self.data[key] = {'text': text, 'pos': pos, 'visible': visible, 'font_size': font_size, 'color': color}
if update:
self.redraw()
@ -537,7 +690,7 @@ class TextCollectionVisual(TextVisual):
font_s = data['font_size']
color = data['color']
except Exception as e:
print("Data error", e)
print("VisPyVisuals.TextCollectionVisual._update() --> Data error. %s" % str(e))
# Updating text
if len(labels) > 0:

View File

@ -1457,4 +1457,35 @@ class Excellon(Geometry):
slot['start'] = affinity.rotate(slot['start'], angle, origin=(px, py))
self.create_geometry()
self.app.proc_container.new_text = ''
self.app.proc_container.new_text = ''
def buffer(self, distance, join):
"""
:param distance:
:param join:
:return:
"""
log.debug("flatcamParsers.ParseExcellon.Excellon.buffer()")
if distance == 0:
return
def buffer_geom(obj):
if type(obj) is list:
new_obj = []
for g in obj:
new_obj.append(buffer_geom(g))
return new_obj
else:
try:
return obj.buffer(distance, resolution=self.geo_steps_per_circle)
except AttributeError:
return obj
# buffer solid_geometry
for tool, tool_dict in list(self.tools.items()):
self.tools[tool]['solid_geometry'] = buffer_geom(tool_dict['solid_geometry'])
self.tools[tool]['C'] += distance
self.create_geometry()

View File

@ -2169,6 +2169,87 @@ class Gerber(Geometry):
_("Gerber Rotate done."))
self.app.proc_container.new_text = ''
def buffer(self, distance, join):
"""
:param distance:
:return:
"""
log.debug("parseGerber.Gerber.buffer()")
if distance == 0:
return
# variables to display the percentage of work done
self.geo_len = 0
try:
for __ in self.solid_geometry:
self.geo_len += 1
except TypeError:
self.geo_len = 1
self.old_disp_number = 0
self.el_count = 0
def buffer_geom(obj):
if type(obj) is list:
new_obj = []
for g in obj:
new_obj.append(buffer_geom(g))
return new_obj
else:
try:
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
return obj.buffer(distance, resolution=self.steps_per_circle, join_style=join)
except AttributeError:
return obj
self.solid_geometry = buffer_geom(self.solid_geometry)
# we need to buffer the geometry stored in the Gerber apertures, too
try:
for apid in self.apertures:
new_geometry = list()
if 'geometry' in self.apertures[apid]:
for geo_el in self.apertures[apid]['geometry']:
new_geo_el = dict()
if 'solid' in geo_el:
new_geo_el['solid'] = buffer_geom(geo_el['solid'])
if 'follow' in geo_el:
new_geo_el['follow'] = buffer_geom(geo_el['follow'])
if 'clear' in geo_el:
new_geo_el['clear'] = buffer_geom(geo_el['clear'])
new_geometry.append(new_geo_el)
self.apertures[apid]['geometry'] = deepcopy(new_geometry)
try:
if str(self.apertures[apid]['type']) == 'R' or str(self.apertures[apid]['type']) == 'O':
self.apertures[apid]['width'] += (distance * 2)
self.apertures[apid]['height'] += (distance * 2)
elif str(self.apertures[apid]['type']) == 'P':
self.apertures[apid]['diam'] += (distance * 2)
self.apertures[apid]['nVertices'] += (distance * 2)
except KeyError:
pass
try:
if self.apertures[apid]['size'] is not None:
self.apertures[apid]['size'] = float(self.apertures[apid]['size'] + (distance * 2))
except KeyError:
pass
except Exception as e:
log.debug('camlib.Gerber.buffer() Exception --> %s' % str(e))
return 'fail'
self.app.inform.emit('[success] %s' % _("Gerber Buffer done."))
self.app.proc_container.new_text = ''
def parse_gerber_number(strnumber, int_digits, frac_digits, zeros):
"""

View File

@ -349,7 +349,7 @@ class Distance(FlatCAMTool):
d = math.sqrt(dx ** 2 + dy ** 2)
self.stop_entry.set_value("(%.*f, %.*f)" % (self.decimals, pos[0], self.decimals, pos[1]))
self.app.inform.emit("{tx1}: {tx2} D(x) = {d_x} | D(y) = {d_y} | (tx3} = {d_z}".format(
self.app.inform.emit("{tx1}: {tx2} D(x) = {d_x} | D(y) = {d_y} | {tx3} = {d_z}".format(
tx1=_("MEASURING"),
tx2=_("Result"),
tx3=_("Distance"),

View File

@ -27,6 +27,7 @@ class ToolTransform(FlatCAMTool):
scaleName = _("Scale")
flipName = _("Mirror (Flip)")
offsetName = _("Offset")
bufferName = _("Buffer")
def __init__(self, app):
FlatCAMTool.__init__(self, app)
@ -255,11 +256,11 @@ class ToolTransform(FlatCAMTool):
grid0.addWidget(self.offy_entry, 14, 1)
grid0.addWidget(self.offy_button, 14, 2)
grid0.addWidget(QtWidgets.QLabel(''))
grid0.addWidget(QtWidgets.QLabel(''), 15, 0, 1, 3)
# ## Flip Title
flip_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.flipName)
self.transform_lay.addWidget(flip_title_label)
grid0.addWidget(flip_title_label, 16, 0, 1, 3)
self.flipx_button = FCButton()
self.flipx_button.set_value(_("Flip on X"))
@ -274,7 +275,7 @@ class ToolTransform(FlatCAMTool):
)
hlay0 = QtWidgets.QHBoxLayout()
self.transform_lay.addLayout(hlay0)
grid0.addLayout(hlay0, 17, 0, 1, 3)
hlay0.addWidget(self.flipx_button)
hlay0.addWidget(self.flipy_button)
@ -293,7 +294,7 @@ class ToolTransform(FlatCAMTool):
"Or enter the coords in format (x, y) in the\n"
"Point Entry field and click Flip on X(Y)"))
self.transform_lay.addWidget(self.flip_ref_cb)
grid0.addWidget(self.flip_ref_cb, 18, 0, 1, 3)
self.flip_ref_label = QtWidgets.QLabel('%s:' % _("Ref. Point"))
self.flip_ref_label.setToolTip(
@ -315,12 +316,60 @@ class ToolTransform(FlatCAMTool):
self.ois_flip = OptionalInputSection(self.flip_ref_cb, [self.flip_ref_entry, self.flip_ref_button], logic=True)
hlay1 = QtWidgets.QHBoxLayout()
self.transform_lay.addLayout(hlay1)
grid0.addLayout(hlay1, 19, 0, 1, 3)
hlay1.addWidget(self.flip_ref_label)
hlay1.addWidget(self.flip_ref_entry)
self.transform_lay.addWidget(self.flip_ref_button)
grid0.addWidget(self.flip_ref_button, 20, 0, 1, 3)
grid0.addWidget(QtWidgets.QLabel(''), 21, 0, 1, 3)
# ## Buffer Title
buffer_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.bufferName)
grid0.addWidget(buffer_title_label, 22, 0, 1, 3)
self.buffer_label = QtWidgets.QLabel('%s:' % _("Distance"))
self.buffer_label.setToolTip(
_("A positive value will create the effect of dilation,\n"
"while a negative value will create the effect of erosion.\n"
"Each geometry element of the object will be increased\n"
"or decreased with the 'distance'.")
)
self.buffer_entry = FCDoubleSpinner()
self.buffer_entry.set_precision(self.decimals)
self.buffer_entry.setSingleStep(0.1)
self.buffer_entry.setWrapping(True)
self.buffer_entry.set_range(-9999.9999, 9999.9999)
# self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.buffer_button = FCButton()
self.buffer_button.set_value(_("Buffer"))
self.buffer_button.setToolTip(
_("Create the buffer effect on each geometry,\n"
"element from the selected object.")
)
self.buffer_button.setMinimumWidth(90)
grid0.addWidget(self.buffer_label, 23, 0)
grid0.addWidget(self.buffer_entry, 23, 1)
grid0.addWidget(self.buffer_button, 23, 2)
self.buffer_rounded_cb = FCCheckBox()
self.buffer_rounded_cb.setText('%s' % _("Rounded"))
self.buffer_rounded_cb.setToolTip(
_("If checked then the buffer will surround the buffered shape,\n"
"every corner will be rounded.\n"
"If not checked then the buffer will follow the exact geometry\n"
"of the buffered shape.")
)
grid0.addWidget(self.buffer_rounded_cb, 24, 0, 1, 3)
grid0.addWidget(QtWidgets.QLabel(''), 25, 0, 1, 3)
self.transform_lay.addStretch()
# ## Signals
@ -334,14 +383,16 @@ class ToolTransform(FlatCAMTool):
self.flipx_button.clicked.connect(self.on_flipx)
self.flipy_button.clicked.connect(self.on_flipy)
self.flip_ref_button.clicked.connect(self.on_flip_add_coords)
self.buffer_button.clicked.connect(self.on_buffer)
self.rotate_entry.returnPressed.connect(self.on_rotate)
self.skewx_entry.returnPressed.connect(self.on_skewx)
self.skewy_entry.returnPressed.connect(self.on_skewy)
self.scalex_entry.returnPressed.connect(self.on_scalex)
self.scaley_entry.returnPressed.connect(self.on_scaley)
self.offx_entry.returnPressed.connect(self.on_offx)
self.offy_entry.returnPressed.connect(self.on_offy)
# self.rotate_entry.returnPressed.connect(self.on_rotate)
# self.skewx_entry.returnPressed.connect(self.on_skewx)
# self.skewy_entry.returnPressed.connect(self.on_skewy)
# self.scalex_entry.returnPressed.connect(self.on_scalex)
# self.scaley_entry.returnPressed.connect(self.on_scaley)
# self.offx_entry.returnPressed.connect(self.on_offx)
# self.offy_entry.returnPressed.connect(self.on_offy)
# self.buffer_entry.returnPressed.connect(self.on_buffer)
def run(self, toggle=True):
self.app.report_usage("ToolTransform()")
@ -430,6 +481,16 @@ class ToolTransform(FlatCAMTool):
else:
self.flip_ref_entry.set_value((0, 0))
if self.app.defaults["tools_transform_buffer_dis"]:
self.buffer_entry.set_value(self.app.defaults["tools_transform_buffer_dis"])
else:
self.buffer_entry.set_value(0.0)
if self.app.defaults["tools_transform_buffer_corner"]:
self.buffer_rounded_cb.set_value(self.app.defaults["tools_transform_buffer_corner"])
else:
self.buffer_rounded_cb.set_value(True)
def on_rotate(self):
value = float(self.rotate_entry.get_value())
if value == 0:
@ -511,8 +572,7 @@ class ToolTransform(FlatCAMTool):
def on_offx(self):
value = float(self.offx_entry.get_value())
if value == 0:
self.app.inform.emit('[WARNING_NOTCL] %s' %
_("Offset transformation can not be done for a value of 0."))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Offset transformation can not be done for a value of 0."))
return
axis = 'X'
@ -522,14 +582,20 @@ class ToolTransform(FlatCAMTool):
def on_offy(self):
value = float(self.offy_entry.get_value())
if value == 0:
self.app.inform.emit('[WARNING_NOTCL] %s' %
_("Offset transformation can not be done for a value of 0."))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Offset transformation can not be done for a value of 0."))
return
axis = 'Y'
self.app.worker_task.emit({'fcn': self.on_offset, 'params': [axis, value]})
return
def on_buffer(self):
value = self.buffer_entry.get_value()
join = 1 if self.buffer_rounded_cb.get_value() else 2
self.app.worker_task.emit({'fcn': self.on_buffer_action, 'params': [value, join]})
return
def on_rotate_action(self, num):
obj_list = self.app.collection.get_selected()
xminlist = []
@ -808,4 +874,40 @@ class ToolTransform(FlatCAMTool):
(_("Due of"), str(e), _("action was not executed.")))
return
def on_buffer_action(self, value, join):
obj_list = self.app.collection.get_selected()
if not obj_list:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object selected. Please Select an object to buffer!"))
return
else:
with self.app.proc_container.new(_("Applying Buffer")):
try:
for sel_obj in obj_list:
if isinstance(sel_obj, FlatCAMCNCjob):
self.app.inform.emit(_("CNCJob objects can't be buffered."))
elif sel_obj.kind.lower() == 'gerber':
sel_obj.buffer(value, join)
sel_obj.source_file = self.app.export_gerber(obj_name=sel_obj.options['name'],
filename=None, local_use=sel_obj,
use_thread=False)
elif sel_obj.kind.lower() == 'excellon':
sel_obj.buffer(value, join)
sel_obj.source_file = self.app.export_excellon(obj_name=sel_obj.options['name'],
filename=None, local_use=sel_obj,
use_thread=False)
elif sel_obj.kind.lower() == 'geometry':
sel_obj.buffer(value, join)
self.app.object_changed.emit(sel_obj)
sel_obj.plot()
self.app.inform.emit('[success] %s...' % _('Buffer done'))
except Exception as e:
self.app.log.debug("ToolTransform.on_buffer_action() --> %s" % str(e))
self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
(_("Due of"), str(e), _("action was not executed.")))
return
# end of file

BIN
share/blue32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

BIN
share/brown32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

BIN
share/green32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

BIN
share/red32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

BIN
share/set_color16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 B

BIN
share/set_color32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 B

BIN
share/snap_16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

BIN
share/snap_filled_16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

BIN
share/violet32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

BIN
share/yellow32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B