- Tool Drilling - working on the UI
- Tool Drilling - added more tool parameters; laying the ground for adding "Drilling Slots" feature
This commit is contained in:
@@ -7,6 +7,11 @@ CHANGELOG for FlatCAM beta
|
|||||||
|
|
||||||
=================================================
|
=================================================
|
||||||
|
|
||||||
|
8.07.2020
|
||||||
|
|
||||||
|
- Tool Drilling - working on the UI
|
||||||
|
- Tool Drilling - added more tool parameters; laying the ground for adding "Drilling Slots" feature
|
||||||
|
|
||||||
7.07.2020
|
7.07.2020
|
||||||
|
|
||||||
- updated the Panelize Tool to save the source code for the panelized Excellon objects so it can be saved from the Save project tab context menu entry
|
- updated the Panelize Tool to save the source code for the panelized Excellon objects so it can be saved from the Save project tab context menu entry
|
||||||
|
|||||||
@@ -222,6 +222,9 @@ class PreferencesUIManager:
|
|||||||
"excellon_spindledir": self.ui.excellon_defaults_form.excellon_adv_opt_group.spindledir_radio,
|
"excellon_spindledir": self.ui.excellon_defaults_form.excellon_adv_opt_group.spindledir_radio,
|
||||||
"excellon_f_plunge": self.ui.excellon_defaults_form.excellon_adv_opt_group.fplunge_cb,
|
"excellon_f_plunge": self.ui.excellon_defaults_form.excellon_adv_opt_group.fplunge_cb,
|
||||||
"excellon_f_retract": self.ui.excellon_defaults_form.excellon_adv_opt_group.fretract_cb,
|
"excellon_f_retract": self.ui.excellon_defaults_form.excellon_adv_opt_group.fretract_cb,
|
||||||
|
"excellon_drill_slots": self.ui.excellon_defaults_form.excellon_adv_opt_group.drill_slots_cb,
|
||||||
|
"excellon_drill_overlap": self.ui.excellon_defaults_form.excellon_adv_opt_group.drill_overlap_entry,
|
||||||
|
"excellon_last_drill": self.ui.excellon_defaults_form.excellon_adv_opt_group.last_drill_cb,
|
||||||
|
|
||||||
# Excellon Export
|
# Excellon Export
|
||||||
"excellon_exp_units": self.ui.excellon_defaults_form.excellon_exp_group.excellon_units_radio,
|
"excellon_exp_units": self.ui.excellon_defaults_form.excellon_exp_group.excellon_units_radio,
|
||||||
|
|||||||
@@ -152,4 +152,42 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI):
|
|||||||
|
|
||||||
grid1.addWidget(self.fretract_cb, 8, 0, 1, 2)
|
grid1.addWidget(self.fretract_cb, 8, 0, 1, 2)
|
||||||
|
|
||||||
|
separator_line = QtWidgets.QFrame()
|
||||||
|
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||||
|
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||||
|
grid1.addWidget(separator_line, 9, 0, 1, 2)
|
||||||
|
|
||||||
|
# DRILL SLOTS LABEL
|
||||||
|
self.dslots_label = QtWidgets.QLabel('<b>%s:</b>' % _('Drilling Slots'))
|
||||||
|
grid1.addWidget(self.dslots_label, 10, 0, 1, 2)
|
||||||
|
|
||||||
|
# Drill slots
|
||||||
|
self.drill_slots_cb = FCCheckBox('%s' % _('Drill slots'))
|
||||||
|
self.drill_slots_cb.setToolTip(
|
||||||
|
_("If the selected tool has slots then they will be drilled.")
|
||||||
|
)
|
||||||
|
grid1.addWidget(self.drill_slots_cb, 11, 0, 1, 2)
|
||||||
|
|
||||||
|
# Drill Overlap
|
||||||
|
self.drill_overlap_label = QtWidgets.QLabel('%s:' % _('Overlap'))
|
||||||
|
self.drill_overlap_label.setToolTip(
|
||||||
|
_("How much (percentage) of the tool diameter to overlap previous drill hole.")
|
||||||
|
)
|
||||||
|
|
||||||
|
self.drill_overlap_entry = FCDoubleSpinner()
|
||||||
|
self.drill_overlap_entry.set_precision(self.decimals)
|
||||||
|
self.drill_overlap_entry.set_range(0.0, 9999.9999)
|
||||||
|
self.drill_overlap_entry.setSingleStep(0.1)
|
||||||
|
|
||||||
|
grid1.addWidget(self.drill_overlap_label, 12, 0)
|
||||||
|
grid1.addWidget(self.drill_overlap_entry, 12, 1)
|
||||||
|
|
||||||
|
# Last drill in slot
|
||||||
|
self.last_drill_cb = FCCheckBox('%s' % _('Last drill'))
|
||||||
|
self.last_drill_cb.setToolTip(
|
||||||
|
_("If the slot length is not completely covered by drill holes,\n"
|
||||||
|
"add a drill hole on the slot end point.")
|
||||||
|
)
|
||||||
|
grid1.addWidget(self.last_drill_cb, 14, 0, 1, 2)
|
||||||
|
|
||||||
self.layout.addStretch()
|
self.layout.addStretch()
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ class FlatCAMObj(QtCore.QObject):
|
|||||||
# View
|
# View
|
||||||
self.ui = None
|
self.ui = None
|
||||||
|
|
||||||
|
# set True by the collection.append() when the object load is complete
|
||||||
|
self.load_complete = None
|
||||||
|
|
||||||
self.options = LoudDict(name=name)
|
self.options = LoudDict(name=name)
|
||||||
self.options.set_change_callback(self.on_options_change)
|
self.options.set_change_callback(self.on_options_change)
|
||||||
|
|
||||||
|
|||||||
@@ -570,6 +570,8 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||||||
obj.options["name"] = name
|
obj.options["name"] = name
|
||||||
|
|
||||||
obj.set_ui(obj.ui_type(app=self.app))
|
obj.set_ui(obj.ui_type(app=self.app))
|
||||||
|
# a way to signal that the object was fully loaded
|
||||||
|
obj.load_complete = True
|
||||||
|
|
||||||
# Required before appending (Qt MVC)
|
# Required before appending (Qt MVC)
|
||||||
group = self.group_items[obj.kind]
|
group = self.group_items[obj.kind]
|
||||||
|
|||||||
@@ -138,7 +138,11 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
"dwell": self.t_ui.dwell_cb,
|
"dwell": self.t_ui.dwell_cb,
|
||||||
"dwelltime": self.t_ui.dwelltime_entry,
|
"dwelltime": self.t_ui.dwelltime_entry,
|
||||||
|
|
||||||
"offset": self.t_ui.offset_entry
|
"offset": self.t_ui.offset_entry,
|
||||||
|
|
||||||
|
"drill_slots": self.t_ui.drill_slots_cb,
|
||||||
|
"drill_overlap": self.t_ui.drill_overlap_entry,
|
||||||
|
"last_drill": self.t_ui.last_drill_cb
|
||||||
}
|
}
|
||||||
|
|
||||||
self.name2option = {
|
self.name2option = {
|
||||||
@@ -153,7 +157,11 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
"e_dwell": "dwell",
|
"e_dwell": "dwell",
|
||||||
"e_dwelltime": "dwelltime",
|
"e_dwelltime": "dwelltime",
|
||||||
|
|
||||||
"e_offset": "offset"
|
"e_offset": "offset",
|
||||||
|
|
||||||
|
"e_drill_slots": "drill_slots",
|
||||||
|
"e_drill_slots_overlap": "drill_overlap",
|
||||||
|
"e_drill_last_drill": "last_drill",
|
||||||
}
|
}
|
||||||
|
|
||||||
self.old_tool_dia = None
|
self.old_tool_dia = None
|
||||||
@@ -188,9 +196,9 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
|
|
||||||
AppTool.run(self)
|
AppTool.run(self)
|
||||||
|
|
||||||
self.on_object_changed()
|
|
||||||
self.set_tool_ui()
|
self.set_tool_ui()
|
||||||
self.build_ui()
|
self.on_object_changed()
|
||||||
|
# self.build_tool_ui()
|
||||||
|
|
||||||
# all the tools are selected by default
|
# all the tools are selected by default
|
||||||
self.t_ui.tools_table.selectAll()
|
self.t_ui.tools_table.selectAll()
|
||||||
@@ -338,6 +346,11 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
"f_plunge": self.app.defaults["excellon_f_plunge"],
|
"f_plunge": self.app.defaults["excellon_f_plunge"],
|
||||||
"f_retract": self.app.defaults["excellon_f_retract"],
|
"f_retract": self.app.defaults["excellon_f_retract"],
|
||||||
|
|
||||||
|
# Drill Slots
|
||||||
|
"drill_slots": self.app.defaults["excellon_drill_slots"],
|
||||||
|
"drill_overlap": self.app.defaults["excellon_drill_overlap"],
|
||||||
|
"last_drill": self.app.defaults["excellon_last_drill"],
|
||||||
|
|
||||||
"gcode": '',
|
"gcode": '',
|
||||||
"gcode_parsed": '',
|
"gcode_parsed": '',
|
||||||
"geometry": [],
|
"geometry": [],
|
||||||
@@ -362,10 +375,13 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
self.t_ui.tools_table.setMinimumHeight(self.t_ui.tools_table.getHeight())
|
self.t_ui.tools_table.setMinimumHeight(self.t_ui.tools_table.getHeight())
|
||||||
self.t_ui.tools_table.setMaximumHeight(self.t_ui.tools_table.getHeight())
|
self.t_ui.tools_table.setMaximumHeight(self.t_ui.tools_table.getHeight())
|
||||||
|
|
||||||
if self.excellon_obj:
|
|
||||||
# make sure to update the UI on init
|
# make sure to update the UI on init
|
||||||
|
try:
|
||||||
self.excellon_tools = self.excellon_obj.tools
|
self.excellon_tools = self.excellon_obj.tools
|
||||||
self.build_ui()
|
except AttributeError:
|
||||||
|
# no object loaded
|
||||||
|
pass
|
||||||
|
self.build_tool_ui()
|
||||||
|
|
||||||
# ########################################
|
# ########################################
|
||||||
# ########################################
|
# ########################################
|
||||||
@@ -394,6 +410,18 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
self.t_ui.over_z_entry.set_value(self.app.defaults["excellon_area_overz"])
|
self.t_ui.over_z_entry.set_value(self.app.defaults["excellon_area_overz"])
|
||||||
self.t_ui.area_shape_radio.set_value(self.app.defaults["excellon_area_shape"])
|
self.t_ui.area_shape_radio.set_value(self.app.defaults["excellon_area_shape"])
|
||||||
|
|
||||||
|
# Drill slots - part of the Advanced Excellon params
|
||||||
|
self.t_ui.drill_slots_cb.set_value(self.app.defaults["excellon_drill_slots"])
|
||||||
|
self.t_ui.drill_overlap_entry.set_value(self.app.defaults["excellon_drill_overlap"])
|
||||||
|
self.t_ui.last_drill_cb.set_value(self.app.defaults["excellon_last_drill"])
|
||||||
|
# if the app mode is Basic then disable this feature
|
||||||
|
if app_mode == 'b':
|
||||||
|
self.t_ui.drill_slots_cb.set_value(False)
|
||||||
|
self.t_ui.drill_slots_cb.hide()
|
||||||
|
self.t_ui.drill_overlap_label.hide()
|
||||||
|
self.t_ui.drill_overlap_entry.hide()
|
||||||
|
self.t_ui.last_drill_cb.hide()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.t_ui.object_combo.currentTextChanged.disconnect()
|
self.t_ui.object_combo.currentTextChanged.disconnect()
|
||||||
except (AttributeError, TypeError):
|
except (AttributeError, TypeError):
|
||||||
@@ -420,10 +448,10 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
self.excellon_tools = new_tools
|
self.excellon_tools = new_tools
|
||||||
|
|
||||||
# the tools table changed therefore we need to rebuild it
|
# the tools table changed therefore we need to rebuild it
|
||||||
QtCore.QTimer.singleShot(20, self.build_ui)
|
QtCore.QTimer.singleShot(20, self.build_tool_ui)
|
||||||
|
|
||||||
def build_ui(self):
|
def build_tool_ui(self):
|
||||||
log.debug("ToolDrilling.build_ui()")
|
log.debug("ToolDrilling.build_tool_ui()")
|
||||||
self.ui_disconnect()
|
self.ui_disconnect()
|
||||||
|
|
||||||
# order the tools by tool diameter if it's the case
|
# order the tools by tool diameter if it's the case
|
||||||
@@ -588,7 +616,7 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
self.t_ui.tools_table.setMaximumHeight(self.t_ui.tools_table.getHeight())
|
self.t_ui.tools_table.setMaximumHeight(self.t_ui.tools_table.getHeight())
|
||||||
|
|
||||||
# all the tools are selected by default
|
# all the tools are selected by default
|
||||||
# self.t_ui.tools_table.selectAll()
|
self.t_ui.tools_table.selectAll()
|
||||||
|
|
||||||
# Build Exclusion Areas section
|
# Build Exclusion Areas section
|
||||||
e_len = len(self.app.exc_areas.exclusion_areas_storage)
|
e_len = len(self.app.exc_areas.exclusion_areas_storage)
|
||||||
@@ -676,7 +704,7 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if self.excellon_obj is None:
|
if self.excellon_obj is None:
|
||||||
self.excellon_tools = []
|
self.excellon_tools = {}
|
||||||
self.t_ui.exc_param_frame.setDisabled(True)
|
self.t_ui.exc_param_frame.setDisabled(True)
|
||||||
self.set_tool_ui()
|
self.set_tool_ui()
|
||||||
else:
|
else:
|
||||||
@@ -685,7 +713,7 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
self.t_ui.exc_param_frame.setDisabled(False)
|
self.t_ui.exc_param_frame.setDisabled(False)
|
||||||
self.excellon_tools = self.excellon_obj.tools
|
self.excellon_tools = self.excellon_obj.tools
|
||||||
|
|
||||||
self.build_ui()
|
self.build_tool_ui()
|
||||||
|
|
||||||
sel_rows = set()
|
sel_rows = set()
|
||||||
table_items = self.t_ui.tools_table.selectedItems()
|
table_items = self.t_ui.tools_table.selectedItems()
|
||||||
@@ -709,7 +737,7 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
self.app.exc_areas.e_shape_modified.disconnect()
|
self.app.exc_areas.e_shape_modified.disconnect()
|
||||||
except (TypeError, AttributeError):
|
except (TypeError, AttributeError):
|
||||||
pass
|
pass
|
||||||
# then connect it to the current build_ui() method
|
# then connect it to the current build_tool_ui() method
|
||||||
self.app.exc_areas.e_shape_modified.connect(self.update_exclusion_table)
|
self.app.exc_areas.e_shape_modified.connect(self.update_exclusion_table)
|
||||||
|
|
||||||
# rows selected
|
# rows selected
|
||||||
@@ -977,7 +1005,7 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
|
|
||||||
def on_order_changed(self, order):
|
def on_order_changed(self, order):
|
||||||
if order != 'no':
|
if order != 'no':
|
||||||
self.build_ui()
|
self.build_tool_ui()
|
||||||
|
|
||||||
def on_tooltable_cellwidget_change(self):
|
def on_tooltable_cellwidget_change(self):
|
||||||
cw = self.sender()
|
cw = self.sender()
|
||||||
@@ -1441,7 +1469,7 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
def update_exclusion_table(self):
|
def update_exclusion_table(self):
|
||||||
self.exclusion_area_cb_is_checked = True if self.t_ui.exclusion_cb.isChecked() else False
|
self.exclusion_area_cb_is_checked = True if self.t_ui.exclusion_cb.isChecked() else False
|
||||||
|
|
||||||
self.build_ui()
|
self.build_tool_ui()
|
||||||
self.t_ui.exclusion_cb.set_value(self.exclusion_area_cb_is_checked)
|
self.t_ui.exclusion_cb.set_value(self.exclusion_area_cb_is_checked)
|
||||||
|
|
||||||
def on_strategy(self, val):
|
def on_strategy(self, val):
|
||||||
@@ -2144,7 +2172,7 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
|
|
||||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
# APPLY Offset only when using the appGUI, for TclCommand this will create an error
|
# APPLY Offset only when using the appGUI, for TclCommand this will create an error
|
||||||
# because the values for Z offset are created in build_ui()
|
# because the values for Z offset are created in build_tool_ui()
|
||||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
try:
|
try:
|
||||||
z_offset = float(tool_dict['offset']) * (-1)
|
z_offset = float(tool_dict['offset']) * (-1)
|
||||||
@@ -2373,7 +2401,7 @@ class ToolDrilling(AppTool, Excellon):
|
|||||||
|
|
||||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
# APPLY Offset only when using the appGUI, for TclCommand this will create an error
|
# APPLY Offset only when using the appGUI, for TclCommand this will create an error
|
||||||
# because the values for Z offset are created in build_ui()
|
# because the values for Z offset are created in build_tool_ui()
|
||||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
try:
|
try:
|
||||||
z_offset = float(self.exc_tools[one_tool]['data']['offset']) * (-1)
|
z_offset = float(self.exc_tools[one_tool]['data']['offset']) * (-1)
|
||||||
@@ -2879,6 +2907,48 @@ class DrillingUI:
|
|||||||
self.grid1.addWidget(self.tool_offset_label, 25, 0)
|
self.grid1.addWidget(self.tool_offset_label, 25, 0)
|
||||||
self.grid1.addWidget(self.offset_entry, 25, 1)
|
self.grid1.addWidget(self.offset_entry, 25, 1)
|
||||||
|
|
||||||
|
# Drill slots
|
||||||
|
self.drill_slots_cb = FCCheckBox('%s' % _('Drill slots'))
|
||||||
|
self.drill_slots_cb.setToolTip(
|
||||||
|
_("If the selected tool has slots then they will be drilled.")
|
||||||
|
)
|
||||||
|
self.drill_slots_cb.setObjectName("e_drill_slots")
|
||||||
|
self.grid1.addWidget(self.drill_slots_cb, 27, 0, 1, 2)
|
||||||
|
|
||||||
|
# Drill Overlap
|
||||||
|
self.drill_overlap_label = QtWidgets.QLabel('%s:' % _('Overlap'))
|
||||||
|
self.drill_overlap_label.setToolTip(
|
||||||
|
_("How much (percentage) of the tool diameter to overlap previous drill hole.")
|
||||||
|
)
|
||||||
|
|
||||||
|
self.drill_overlap_entry = FCDoubleSpinner(callback=self.confirmation_message)
|
||||||
|
self.drill_overlap_entry.set_precision(self.decimals)
|
||||||
|
self.drill_overlap_entry.set_range(0.0, 9999.9999)
|
||||||
|
self.drill_overlap_entry.setSingleStep(0.1)
|
||||||
|
|
||||||
|
self.drill_overlap_entry.setObjectName("e_drill_slots_overlap")
|
||||||
|
|
||||||
|
self.grid1.addWidget(self.drill_overlap_label, 28, 0)
|
||||||
|
self.grid1.addWidget(self.drill_overlap_entry, 28, 1)
|
||||||
|
|
||||||
|
# Last drill in slot
|
||||||
|
self.last_drill_cb = FCCheckBox('%s' % _('Last drill'))
|
||||||
|
self.last_drill_cb.setToolTip(
|
||||||
|
_("If the slot length is not completely covered by drill holes,\n"
|
||||||
|
"add a drill hole on the slot end point.")
|
||||||
|
)
|
||||||
|
self.last_drill_cb.setObjectName("e_drill_last_drill")
|
||||||
|
self.grid1.addWidget(self.last_drill_cb, 30, 0, 1, 2)
|
||||||
|
|
||||||
|
self.ois_drill_overlap = OptionalInputSection(
|
||||||
|
self.drill_slots_cb,
|
||||||
|
[
|
||||||
|
self.drill_overlap_label,
|
||||||
|
self.drill_overlap_entry,
|
||||||
|
self.last_drill_cb
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# #################################################################
|
# #################################################################
|
||||||
# ################# GRID LAYOUT 5 ###############################
|
# ################# GRID LAYOUT 5 ###############################
|
||||||
# #################################################################
|
# #################################################################
|
||||||
|
|||||||
@@ -3160,7 +3160,7 @@ class IsoUI:
|
|||||||
self.exc_obj_combo.setModel(self.app.collection)
|
self.exc_obj_combo.setModel(self.app.collection)
|
||||||
self.exc_obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
self.exc_obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
||||||
self.exc_obj_combo.is_last = True
|
self.exc_obj_combo.is_last = True
|
||||||
self.exc_obj_combo.obj_type = self.type_excobj_radio.get_value()
|
self.exc_obj_combo.obj_type = "gerber"
|
||||||
|
|
||||||
self.grid3.addWidget(self.exc_obj_combo, 29, 0, 1, 2)
|
self.grid3.addWidget(self.exc_obj_combo, 29, 0, 1, 2)
|
||||||
|
|
||||||
|
|||||||
@@ -287,6 +287,9 @@ class FlatCAMDefaults:
|
|||||||
"excellon_spindledir": 'CW',
|
"excellon_spindledir": 'CW',
|
||||||
"excellon_f_plunge": False,
|
"excellon_f_plunge": False,
|
||||||
"excellon_f_retract": False,
|
"excellon_f_retract": False,
|
||||||
|
"excellon_drill_slots": False,
|
||||||
|
"excellon_drill_overlap": 0.0,
|
||||||
|
"excellon_last_drill": True,
|
||||||
|
|
||||||
# Excellon Export
|
# Excellon Export
|
||||||
"excellon_exp_units": 'INCH',
|
"excellon_exp_units": 'INCH',
|
||||||
|
|||||||
Reference in New Issue
Block a user