- maintenance_1

This commit is contained in:
Marius 2020-06-02 18:24:44 +03:00
parent 7f06677791
commit 5abb7866d8
255 changed files with 152017 additions and 162289 deletions

View File

@ -1,181 +0,0 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File by: David Robertson (c) #
# Date: 5/2020 #
# License: MIT Licence #
# ##########################################################
import sys
from PyQt5.QtCore import QPoint, QRect, QSize, Qt
from PyQt5.QtWidgets import QLayout, QSizePolicy
import math
class ColumnarFlowLayout(QLayout):
def __init__(self, parent=None, margin=0, spacing=-1):
super().__init__(parent)
if parent is not None:
self.setContentsMargins(margin, margin, margin, margin)
self.setSpacing(spacing)
self.itemList = []
def __del__(self):
del_item = self.takeAt(0)
while del_item:
del_item = self.takeAt(0)
def addItem(self, item):
self.itemList.append(item)
def count(self):
return len(self.itemList)
def itemAt(self, index):
if 0 <= index < len(self.itemList):
return self.itemList[index]
return None
def takeAt(self, index):
if 0 <= index < len(self.itemList):
return self.itemList.pop(index)
return None
def expandingDirections(self):
return Qt.Orientations(Qt.Orientation(0))
def hasHeightForWidth(self):
return True
def heightForWidth(self, width):
height = self.doLayout(QRect(0, 0, width, 0), True)
return height
def setGeometry(self, rect):
super().setGeometry(rect)
self.doLayout(rect, False)
def sizeHint(self):
return self.minimumSize()
def minimumSize(self):
size = QSize()
for item in self.itemList:
size = size.expandedTo(item.minimumSize())
margin, _, _, _ = self.getContentsMargins()
size += QSize(2 * margin, 2 * margin)
return size
def doLayout(self, rect: QRect, testOnly: bool) -> int:
spacing = self.spacing()
x = rect.x()
y = rect.y()
# Determine width of widest item
widest = 0
for item in self.itemList:
widest = max(widest, item.sizeHint().width())
# Determine how many equal-width columns we can get, and how wide each one should be
column_count = math.floor(rect.width() / (widest + spacing))
column_count = min(column_count, len(self.itemList))
column_count = max(1, column_count)
column_width = math.floor((rect.width() - (column_count-1)*spacing - 1) / column_count)
# Get the heights for all of our items
item_heights = {}
for item in self.itemList:
height = item.heightForWidth(column_width) if item.hasHeightForWidth() else item.sizeHint().height()
item_heights[item] = height
# Prepare our column representation
column_contents = []
column_heights = []
for column_index in range(column_count):
column_contents.append([])
column_heights.append(0)
def add_to_column(column: int, item):
column_contents[column].append(item)
column_heights[column] += (item_heights[item] + spacing)
def shove_one(from_column: int) -> bool:
if len(column_contents[from_column]) >= 1:
item = column_contents[from_column].pop(0)
column_heights[from_column] -= (item_heights[item] + spacing)
add_to_column(from_column-1, item)
return True
return False
def shove_cascade_consider(from_column: int) -> bool:
changed_item = False
if len(column_contents[from_column]) > 1:
item = column_contents[from_column][0]
item_height = item_heights[item]
if column_heights[from_column-1] + item_height < max(column_heights):
changed_item = shove_one(from_column) or changed_item
if from_column+1 < column_count:
changed_item = shove_cascade_consider(from_column+1) or changed_item
return changed_item
def shove_cascade() -> bool:
if column_count < 2:
return False
changed_item = True
while changed_item:
changed_item = shove_cascade_consider(1)
return changed_item
def pick_best_shoving_position() -> int:
best_pos = 1
best_height = sys.maxsize
for column_idx in range(1, column_count):
if len(column_contents[column_idx]) == 0:
continue
item = column_contents[column_idx][0]
height_after_shove = column_heights[column_idx-1] + item_heights[item]
if height_after_shove < best_height:
best_height = height_after_shove
best_pos = column_idx
return best_pos
# Calculate the best layout
column_index = 0
for item in self.itemList:
item_height = item_heights[item]
if column_heights[column_index] != 0 and (column_heights[column_index] + item_height) > max(column_heights):
column_index += 1
if column_index >= column_count:
# Run out of room, need to shove more stuff in each column
if column_count >= 2:
changed = shove_cascade()
if not changed:
shoving_pos = pick_best_shoving_position()
shove_one(shoving_pos)
shove_cascade()
column_index = column_count-1
add_to_column(column_index, item)
shove_cascade()
# Set geometry according to the layout we have calculated
if not testOnly:
for column_index, items in enumerate(column_contents):
x = column_index * (column_width + spacing)
y = 0
for item in items:
height = item_heights[item]
item.setGeometry(QRect(x, y, column_width, height))
y += (height + spacing)
# Return the overall height
return max(column_heights)

View File

@ -1,327 +0,0 @@
from typing import Union, Sequence, List
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import RadioSet, FCCheckBox, FCButton, FCComboBox, FCEntry, FCSpinner, FCColorEntry, \
FCSliderWithSpinner, FCDoubleSpinner, FloatEntry, FCTextArea
import gettext
import AppTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
class OptionUI:
def __init__(self, option: str):
self.option = option
def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
"""
Adds the necessary widget to the grid, starting at the supplied row.
Returns the number of rows used (normally 1)
"""
raise NotImplementedError()
def get_field(self):
raise NotImplementedError()
class BasicOptionUI(OptionUI):
"""Abstract OptionUI that has a label on the left then some other widget on the right"""
def __init__(self, option: str, label_text: str, label_tooltip: Union[str, None] = None,
label_bold: bool = False, label_color: Union[str, None] = None):
super().__init__(option=option)
self.label_text = label_text
self.label_tooltip = label_tooltip
self.label_bold = label_bold
self.label_color = label_color
self.label_widget = self.build_label_widget()
self.entry_widget = self.build_entry_widget()
def build_label_widget(self) -> QtWidgets.QLabel:
fmt = "%s:"
if self.label_bold:
fmt = "<b>%s</b>" % fmt
if self.label_color:
fmt = "<span style=\"color:%s;\">%s</span>" % (self.label_color, fmt)
label_widget = QtWidgets.QLabel(fmt % _(self.label_text))
if self.label_tooltip is not None:
label_widget.setToolTip(_(self.label_tooltip))
return label_widget
def build_entry_widget(self) -> QtWidgets.QWidget:
raise NotImplementedError()
def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
grid.addWidget(self.label_widget, row, 0)
grid.addWidget(self.entry_widget, row, 1)
return 1
def get_field(self):
return self.entry_widget
class LineEntryOptionUI(BasicOptionUI):
def build_entry_widget(self) -> QtWidgets.QWidget:
return FCEntry()
# Not sure why this is needed over DoubleSpinnerOptionUI
class FloatEntryOptionUI(BasicOptionUI):
def build_entry_widget(self) -> QtWidgets.QWidget:
return FloatEntry()
class RadioSetOptionUI(BasicOptionUI):
def __init__(self, option: str, label_text: str, choices: list, orientation='horizontal', **kwargs):
self.choices = choices
self.orientation = orientation
super().__init__(option=option, label_text=label_text, **kwargs)
def build_entry_widget(self) -> QtWidgets.QWidget:
return RadioSet(choices=self.choices, orientation=self.orientation)
class TextAreaOptionUI(OptionUI):
def __init__(self, option: str, label_text: str, label_tooltip: str):
super().__init__(option=option)
self.label_text = label_text
self.label_tooltip = label_tooltip
self.label_widget = self.build_label_widget()
self.textarea_widget = self.build_textarea_widget()
def build_label_widget(self):
label = QtWidgets.QLabel("%s:" % _(self.label_text))
label.setToolTip(_(self.label_tooltip))
return label
def build_textarea_widget(self):
textarea = FCTextArea()
textarea.setPlaceholderText(_(self.label_tooltip))
qsettings = QSettings("Open Source", "FlatCAM")
if qsettings.contains("textbox_font_size"):
tb_fsize = qsettings.value('textbox_font_size', type=int)
else:
tb_fsize = 10
font = QtGui.QFont()
font.setPointSize(tb_fsize)
textarea.setFont(font)
return textarea
def get_field(self):
return self.textarea_widget
def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
grid.addWidget(self.label_widget, row, 0, 1, 3)
grid.addWidget(self.textarea_widget, row+1, 0, 1, 3)
return 2
class CheckboxOptionUI(OptionUI):
def __init__(self, option: str, label_text: str, label_tooltip: str):
super().__init__(option=option)
self.label_text = label_text
self.label_tooltip = label_tooltip
self.checkbox_widget = self.build_checkbox_widget()
def build_checkbox_widget(self):
checkbox = FCCheckBox('%s' % _(self.label_text))
checkbox.setToolTip(_(self.label_tooltip))
return checkbox
def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
grid.addWidget(self.checkbox_widget, row, 0, 1, 3)
return 1
def get_field(self):
return self.checkbox_widget
class ComboboxOptionUI(BasicOptionUI):
def __init__(self, option: str, label_text: str, choices: Sequence, **kwargs):
self.choices = choices
super().__init__(option=option, label_text=label_text, **kwargs)
def build_entry_widget(self):
combo = FCComboBox()
for choice in self.choices:
# don't translate the QCombo items as they are used in QSettings and identified by name
combo.addItem(choice)
return combo
class ColorOptionUI(BasicOptionUI):
def build_entry_widget(self) -> QtWidgets.QWidget:
entry = FCColorEntry()
return entry
class SliderWithSpinnerOptionUI(BasicOptionUI):
def __init__(self, option: str, label_text: str, min_value=0, max_value=100, step=1, **kwargs):
self.min_value = min_value
self.max_value = max_value
self.step = step
super().__init__(option=option, label_text=label_text, **kwargs)
def build_entry_widget(self) -> QtWidgets.QWidget:
entry = FCSliderWithSpinner(min=self.min_value, max=self.max_value, step=self.step)
return entry
class ColorAlphaSliderOptionUI(SliderWithSpinnerOptionUI):
def __init__(self, applies_to: List[str], group, label_text: str, **kwargs):
self.applies_to = applies_to
self.group = group
super().__init__(option="__color_alpha_slider", label_text=label_text, min_value=0, max_value=255, step=1,
**kwargs)
self.get_field().valueChanged.connect(self._on_alpha_change)
def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
for index, field in enumerate(self._get_target_fields()):
field.entry.textChanged.connect(lambda value, i=index: self._on_target_change(target_index=i))
return super().add_to_grid(grid, row)
def _get_target_fields(self):
return list(map(lambda n: self.group.option_dict()[n].get_field(), self.applies_to))
def _on_target_change(self, target_index: int):
field = self._get_target_fields()[target_index]
color = field.get_value()
alpha_part = color[7:]
if len(alpha_part) != 2:
return
alpha = int(alpha_part, 16)
if alpha < 0 or alpha > 255 or self.get_field().get_value() == alpha:
return
self.get_field().set_value(alpha)
def _on_alpha_change(self):
alpha = self.get_field().get_value()
for field in self._get_target_fields():
old_value = field.get_value()
new_value = self._modify_color_alpha(old_value, alpha=alpha)
field.set_value(new_value)
@staticmethod
def _modify_color_alpha(color: str, alpha: int):
color_without_alpha = color[:7]
if alpha > 255:
return color_without_alpha + "FF"
elif alpha < 0:
return color_without_alpha + "00"
else:
hexalpha = hex(alpha)[2:]
if len(hexalpha) == 1:
hexalpha = "0" + hexalpha
return color_without_alpha + hexalpha
class SpinnerOptionUI(BasicOptionUI):
def __init__(self, option: str, label_text: str, min_value: int, max_value: int, step: int = 1, **kwargs):
self.min_value = min_value
self.max_value = max_value
self.step = step
super().__init__(option=option, label_text=label_text, **kwargs)
def build_entry_widget(self) -> QtWidgets.QWidget:
entry = FCSpinner()
entry.set_range(self.min_value, self.max_value)
entry.set_step(self.step)
entry.setWrapping(True)
return entry
class DoubleSpinnerOptionUI(BasicOptionUI):
def __init__(self, option: str, label_text: str, step: float, decimals: int, min_value=None, max_value=None,
suffix=None, **kwargs):
self.min_value = min_value
self.max_value = max_value
self.step = step
self.suffix = suffix
self.decimals = decimals
super().__init__(option=option, label_text=label_text, **kwargs)
def build_entry_widget(self) -> QtWidgets.QWidget:
entry = FCDoubleSpinner(suffix=self.suffix)
entry.set_precision(self.decimals)
entry.setSingleStep(self.step)
if self.min_value is None:
self.min_value = entry.minimum()
else:
entry.setMinimum(self.min_value)
if self.max_value is None:
self.max_value = entry.maximum()
else:
entry.setMaximum(self.max_value)
return entry
class HeadingOptionUI(OptionUI):
def __init__(self, label_text: str, label_tooltip: Union[str, None] = None):
super().__init__(option="__heading")
self.label_text = label_text
self.label_tooltip = label_tooltip
def build_heading_widget(self):
heading = QtWidgets.QLabel('<b>%s</b>' % _(self.label_text))
heading.setToolTip(_(self.label_tooltip))
return heading
def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
grid.addWidget(self.build_heading_widget(), row, 0, 1, 2)
return 1
def get_field(self):
return None
class SeparatorOptionUI(OptionUI):
def __init__(self):
super().__init__(option="__separator")
@staticmethod
def build_separator_widget():
separator = QtWidgets.QFrame()
separator.setFrameShape(QtWidgets.QFrame.HLine)
separator.setFrameShadow(QtWidgets.QFrame.Sunken)
return separator
def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
grid.addWidget(self.build_separator_widget(), row, 0, 1, 2)
return 1
def get_field(self):
return None
class FullWidthButtonOptionUI(OptionUI):
def __init__(self, option: str, label_text: str, label_tooltip: Union[str, None]):
super().__init__(option=option)
self.label_text = label_text
self.label_tooltip = label_tooltip
self.button_widget = self.build_button_widget()
def build_button_widget(self):
button = FCButton(_(self.label_text))
if self.label_tooltip is not None:
button.setToolTip(_(self.label_tooltip))
return button
def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
grid.addWidget(self.button_widget, row, 0, 1, 3)
return 1
def get_field(self):
return self.button_widget

View File

@ -1,77 +0,0 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File by: David Robertson (c) #
# Date: 5/2020 #
# License: MIT Licence #
# ##########################################################
from typing import Dict
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSettings
import gettext
import AppTranslation as fcTranslate
import builtins
from AppGUI.preferences.OptionUI import OptionUI
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
settings = QSettings("Open Source", "FlatCAM")
if settings.contains("machinist"):
machinist_setting = settings.value('machinist', type=int)
else:
machinist_setting = 0
class OptionsGroupUI(QtWidgets.QGroupBox):
app = None
def __init__(self, title, parent=None):
# QtGui.QGroupBox.__init__(self, title, parent=parent)
super(OptionsGroupUI, self).__init__()
self.setStyleSheet("""
QGroupBox
{
font-size: 16px;
font-weight: bold;
}
""")
self.layout = QtWidgets.QVBoxLayout()
self.setLayout(self.layout)
def option_dict(self) -> Dict[str, OptionUI]:
# FIXME!
return {}
class OptionsGroupUI2(OptionsGroupUI):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.grid = QtWidgets.QGridLayout()
self.layout.addLayout(self.grid)
self.grid.setColumnStretch(0, 0)
self.grid.setColumnStretch(1, 1)
self.options = self.build_options()
row = 0
for option in self.options:
row += option.add_to_grid(grid=self.grid, row=row)
self.layout.addStretch()
def build_options(self) -> [OptionUI]:
return []
def option_dict(self) -> Dict[str, OptionUI]:
result = {}
for optionui in self.options:
result[optionui.option] = optionui
return result

View File

@ -1,41 +0,0 @@
from typing import Dict
from PyQt5 import QtWidgets, QtCore
from AppGUI.ColumnarFlowLayout import ColumnarFlowLayout
from AppGUI.preferences.OptionUI import OptionUI
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
class PreferencesSectionUI(QtWidgets.QWidget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.layout = ColumnarFlowLayout() # QtWidgets.QHBoxLayout()
self.setLayout(self.layout)
self.groups = self.build_groups()
for group in self.groups:
group.setMinimumWidth(250)
self.layout.addWidget(group)
def build_groups(self) -> [OptionsGroupUI]:
return []
def option_dict(self) -> Dict[str, OptionUI]:
result = {}
for group in self.groups:
groupoptions = group.option_dict()
result.update(groupoptions)
return result
def build_tab(self):
scroll_area = QtWidgets.QScrollArea()
scroll_area.setWidget(self)
scroll_area.setWidgetResizable(True)
return scroll_area
def get_tab_id(self) -> str:
raise NotImplementedError
def get_tab_label(self) -> str:
raise NotImplementedError

View File

@ -1,302 +0,0 @@
from PyQt5 import QtCore
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import OptionalInputSection
from AppGUI.preferences import settings
from AppGUI.preferences.OptionUI import *
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI2
import gettext
import AppTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
class GeneralAppSettingsGroupUI(OptionsGroupUI2):
def __init__(self, decimals=4, **kwargs):
self.decimals = decimals
self.pagesize = {}
self.pagesize.update(
{
'A0': (841, 1189),
'A1': (594, 841),
'A2': (420, 594),
'A3': (297, 420),
'A4': (210, 297),
'A5': (148, 210),
'A6': (105, 148),
'A7': (74, 105),
'A8': (52, 74),
'A9': (37, 52),
'A10': (26, 37),
'B0': (1000, 1414),
'B1': (707, 1000),
'B2': (500, 707),
'B3': (353, 500),
'B4': (250, 353),
'B5': (176, 250),
'B6': (125, 176),
'B7': (88, 125),
'B8': (62, 88),
'B9': (44, 62),
'B10': (31, 44),
'C0': (917, 1297),
'C1': (648, 917),
'C2': (458, 648),
'C3': (324, 458),
'C4': (229, 324),
'C5': (162, 229),
'C6': (114, 162),
'C7': (81, 114),
'C8': (57, 81),
'C9': (40, 57),
'C10': (28, 40),
# American paper sizes
'LETTER': (8.5, 11),
'LEGAL': (8.5, 14),
'ELEVENSEVENTEEN': (11, 17),
# From https://en.wikipedia.org/wiki/Paper_size
'JUNIOR_LEGAL': (5, 8),
'HALF_LETTER': (5.5, 8),
'GOV_LETTER': (8, 10.5),
'GOV_LEGAL': (8.5, 13),
'LEDGER': (17, 11),
}
)
super().__init__(**kwargs)
self.setTitle(str(_("App Settings")))
qsettings = QSettings("Open Source", "FlatCAM")
self.notebook_font_size_field = self.option_dict()["notebook_font_size"].get_field()
if qsettings.contains("notebook_font_size"):
self.notebook_font_size_field.set_value(qsettings.value('notebook_font_size', type=int))
else:
self.notebook_font_size_field.set_value(12)
self.axis_font_size_field = self.option_dict()["axis_font_size"].get_field()
if qsettings.contains("axis_font_size"):
self.axis_font_size_field.set_value(qsettings.value('axis_font_size', type=int))
else:
self.axis_font_size_field.set_value(8)
self.textbox_font_size_field = self.option_dict()["textbox_font_size"].get_field()
if qsettings.contains("textbox_font_size"):
self.textbox_font_size_field.set_value(settings.value('textbox_font_size', type=int))
else:
self.textbox_font_size_field.set_value(10)
self.workspace_enabled_field = self.option_dict()["global_workspace"].get_field()
self.workspace_type_field = self.option_dict()["global_workspaceT"].get_field()
self.workspace_type_label = self.option_dict()["global_workspaceT"].label_widget
self.workspace_orientation_field = self.option_dict()["global_workspace_orientation"].get_field()
self.workspace_orientation_label = self.option_dict()["global_workspace_orientation"].label_widget
self.wks = OptionalInputSection(self.workspace_enabled_field, [self.workspace_type_label, self.workspace_type_field, self.workspace_orientation_label, self.workspace_orientation_field])
self.mouse_cursor_color_enabled_field = self.option_dict()["global_cursor_color_enabled"].get_field()
self.mouse_cursor_color_field = self.option_dict()["global_cursor_color"].get_field()
self.mouse_cursor_color_label = self.option_dict()["global_cursor_color"].label_widget
self.mois = OptionalInputSection(self.mouse_cursor_color_enabled_field, [self.mouse_cursor_color_label, self.mouse_cursor_color_field])
self.mouse_cursor_color_enabled_field.stateChanged.connect(self.on_mouse_cursor_color_enable)
self.mouse_cursor_color_field.entry.editingFinished.connect(self.on_mouse_cursor_entry)
def build_options(self) -> [OptionUI]:
return [
HeadingOptionUI(label_text="Grid Settings", label_tooltip=None),
DoubleSpinnerOptionUI(
option="global_gridx",
label_text="X value",
label_tooltip="This is the Grid snap value on X axis.",
step=0.1,
decimals=self.decimals
),
DoubleSpinnerOptionUI(
option="global_gridy",
label_text='Y value',
label_tooltip="This is the Grid snap value on Y axis.",
step=0.1,
decimals=self.decimals
),
DoubleSpinnerOptionUI(
option="global_snap_max",
label_text="Snap Max",
label_tooltip="Max. magnet distance",
step=0.1,
decimals=self.decimals
),
SeparatorOptionUI(),
HeadingOptionUI(label_text="Workspace Settings", label_tooltip=None),
CheckboxOptionUI(
option="global_workspace",
label_text="Active",
label_tooltip="Draw a delimiting rectangle on canvas.\n"
"The purpose is to illustrate the limits for our work."
),
ComboboxOptionUI(
option="global_workspaceT",
label_text="Size",
label_tooltip="Select the type of rectangle to be used on canvas,\nas valid workspace.",
choices=list(self.pagesize.keys())
),
RadioSetOptionUI(
option="global_workspace_orientation",
label_text="Orientation",
label_tooltip="Can be:\n- Portrait\n- Landscape",
choices=[
{'label': _('Portrait'), 'value': 'p'},
{'label': _('Landscape'), 'value': 'l'},
]
),
# FIXME enabling OptionalInputSection ??
SeparatorOptionUI(),
HeadingOptionUI(label_text="Font Size", label_tooltip=None),
SpinnerOptionUI(
option="notebook_font_size",
label_text="Notebook",
label_tooltip="This sets the font size for the elements found in the Notebook.\n"
"The notebook is the collapsible area in the left side of the GUI,\n"
"and include the Project, Selected and Tool tabs.",
min_value=8, max_value=40, step=1
),
SpinnerOptionUI(
option="axis_font_size",
label_text="Axis",
label_tooltip="This sets the font size for canvas axis.",
min_value=8, max_value=40, step=1
),
SpinnerOptionUI(
option="textbox_font_size",
label_text="Textbox",
label_tooltip="This sets the font size for the Textbox GUI\n"
"elements that are used in the application.",
min_value=8, max_value=40, step=1
),
SeparatorOptionUI(),
HeadingOptionUI(label_text="Mouse Settings", label_tooltip=None),
RadioSetOptionUI(
option="global_cursor_type",
label_text="Cursor Shape",
label_tooltip="Choose a mouse cursor shape.\n"
"- Small -> with a customizable size.\n"
"- Big -> Infinite lines",
choices=[
{"label": _("Small"), "value": "small"},
{"label": _("Big"), "value": "big"}
]
),
SpinnerOptionUI(
option="global_cursor_size",
label_text="Cursor Size",
label_tooltip="Set the size of the mouse cursor, in pixels.",
min_value=10, max_value=70, step=1
),
SpinnerOptionUI(
option="global_cursor_width",
label_text="Cursor Width",
label_tooltip="Set the line width of the mouse cursor, in pixels.",
min_value=1, max_value=10, step=1
),
CheckboxOptionUI(
option="global_cursor_color_enabled",
label_text="Cursor Color",
label_tooltip="Check this box to color mouse cursor."
),
ColorOptionUI(
option="global_cursor_color",
label_text="Cursor Color",
label_tooltip="Set the color of the mouse cursor."
),
# FIXME enabling of cursor color
RadioSetOptionUI(
option="global_pan_button",
label_text="Pan Button",
label_tooltip="Select the mouse button to use for panning:\n"
"- MMB --> Middle Mouse Button\n"
"- RMB --> Right Mouse Button",
choices=[{'label': _('MMB'), 'value': '3'},
{'label': _('RMB'), 'value': '2'}]
),
RadioSetOptionUI(
option="global_mselect_key",
label_text="Multiple Selection",
label_tooltip="Select the key used for multiple selection.",
choices=[{'label': _('CTRL'), 'value': 'Control'},
{'label': _('SHIFT'), 'value': 'Shift'}]
),
SeparatorOptionUI(),
CheckboxOptionUI(
option="global_delete_confirmation",
label_text="Delete object confirmation",
label_tooltip="When checked the application will ask for user confirmation\n"
"whenever the Delete object(s) event is triggered, either by\n"
"menu shortcut or key shortcut."
),
CheckboxOptionUI(
option="global_open_style",
label_text='"Open" behavior',
label_tooltip="When checked the path for the last saved file is used when saving files,\n"
"and the path for the last opened file is used when opening files.\n\n"
"When unchecked the path for opening files is the one used last: either the\n"
"path for saving files or the path for opening files."
),
CheckboxOptionUI(
option="global_toggle_tooltips",
label_text="Enable ToolTips",
label_tooltip="Check this box if you want to have toolTips displayed\n"
"when hovering with mouse over items throughout the App."
),
CheckboxOptionUI(
option="global_machinist_setting",
label_text="Allow Machinist Unsafe Settings",
label_tooltip="If checked, some of the application settings will be allowed\n"
"to have values that are usually unsafe to use.\n"
"Like Z travel negative values or Z Cut positive values.\n"
"It will applied at the next application start.\n"
"<<WARNING>>: Don't change this unless you know what you are doing !!!"
),
SpinnerOptionUI(
option="global_bookmarks_limit",
label_text="Bookmarks limit",
label_tooltip="The maximum number of bookmarks that may be installed in the menu.\n"
"The number of bookmarks in the bookmark manager may be greater\n"
"but the menu will hold only so much.",
min_value=0, max_value=9999, step=1
),
ComboboxOptionUI(
option="global_activity_icon",
label_text="Activity Icon",
label_tooltip="Select the GIF that show activity when FlatCAM is active.",
choices=['Ball black', 'Ball green', 'Arrow green', 'Eclipse green']
)
]
def on_mouse_cursor_color_enable(self, val):
if val:
self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]
else:
theme_settings = QtCore.QSettings("Open Source", "FlatCAM")
if theme_settings.contains("theme"):
theme = theme_settings.value('theme', type=str)
else:
theme = 'white'
if theme == 'white':
self.app.cursor_color_3D = 'black'
else:
self.app.cursor_color_3D = 'gray'
def on_mouse_cursor_entry(self):
self.app.defaults['global_cursor_color'] = self.mouse_cursor_color_field.get_value()
self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]

View File

@ -1,100 +0,0 @@
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import FCDoubleSpinner, FCSpinner, RadioSet, FCCheckBox, FCComboBox
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
settings = QSettings("Open Source", "FlatCAM")
if settings.contains("machinist"):
machinist_setting = settings.value('machinist', type=int)
else:
machinist_setting = 0
class GerberOptPrefGroupUI(OptionsGroupUI):
def __init__(self, decimals=4, parent=None):
# OptionsGroupUI.__init__(self, "Gerber Options Preferences", parent=parent)
super(GerberOptPrefGroupUI, self).__init__(self, parent=parent)
self.decimals = decimals
self.setTitle(str(_("Gerber Options")))
# ## Clear non-copper regions
self.clearcopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Non-copper regions"))
self.clearcopper_label.setToolTip(
_("Create polygons covering the\n"
"areas without copper on the PCB.\n"
"Equivalent to the inverse of this\n"
"object. Can be used to remove all\n"
"copper from a specified region.")
)
self.layout.addWidget(self.clearcopper_label)
grid1 = QtWidgets.QGridLayout()
self.layout.addLayout(grid1)
# Margin
bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
bmlabel.setToolTip(
_("Specify the edge of the PCB\n"
"by drawing a box around all\n"
"objects with this minimum\n"
"distance.")
)
grid1.addWidget(bmlabel, 0, 0)
self.noncopper_margin_entry = FCDoubleSpinner()
self.noncopper_margin_entry.set_precision(self.decimals)
self.noncopper_margin_entry.setSingleStep(0.1)
self.noncopper_margin_entry.set_range(-9999, 9999)
grid1.addWidget(self.noncopper_margin_entry, 0, 1)
# Rounded corners
self.noncopper_rounded_cb = FCCheckBox(label=_("Rounded Geo"))
self.noncopper_rounded_cb.setToolTip(
_("Resulting geometry will have rounded corners.")
)
grid1.addWidget(self.noncopper_rounded_cb, 1, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid1.addWidget(separator_line, 2, 0, 1, 2)
# ## Bounding box
self.boundingbox_label = QtWidgets.QLabel('<b>%s:</b>' % _('Bounding Box'))
self.layout.addWidget(self.boundingbox_label)
grid2 = QtWidgets.QGridLayout()
self.layout.addLayout(grid2)
bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
bbmargin.setToolTip(
_("Distance of the edges of the box\n"
"to the nearest polygon.")
)
self.bbmargin_entry = FCDoubleSpinner()
self.bbmargin_entry.set_precision(self.decimals)
self.bbmargin_entry.setSingleStep(0.1)
self.bbmargin_entry.set_range(-9999, 9999)
grid2.addWidget(bbmargin, 0, 0)
grid2.addWidget(self.bbmargin_entry, 0, 1)
self.bbrounded_cb = FCCheckBox(label='%s' % _("Rounded Geo"))
self.bbrounded_cb.setToolTip(
_("If the bounding box is \n"
"to have rounded corners\n"
"their radius is equal to\n"
"the margin.")
)
grid2.addWidget(self.bbrounded_cb, 1, 0, 1, 2)
self.layout.addStretch()

View File

@ -1,81 +0,0 @@
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import FCDoubleSpinner
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
settings = QSettings("Open Source", "FlatCAM")
if settings.contains("machinist"):
machinist_setting = settings.value('machinist', type=int)
else:
machinist_setting = 0
class ToolsCornersPrefGroupUI(OptionsGroupUI):
def __init__(self, decimals=4, parent=None):
# OptionsGroupUI.__init__(self, "Calculators Tool Options", parent=parent)
super(ToolsCornersPrefGroupUI, self).__init__(self, parent=parent)
self.setTitle(str(_("Corner Markers Options")))
self.decimals = decimals
grid0 = QtWidgets.QGridLayout()
grid0.setColumnStretch(0, 0)
grid0.setColumnStretch(1, 1)
self.layout.addLayout(grid0)
self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
self.param_label.setToolTip(
_("Parameters used for this tool.")
)
grid0.addWidget(self.param_label, 0, 0, 1, 2)
# Thickness #
self.thick_label = QtWidgets.QLabel('%s:' % _("Thickness"))
self.thick_label.setToolTip(
_("The thickness of the line that makes the corner marker.")
)
self.thick_entry = FCDoubleSpinner()
self.thick_entry.set_range(0.0000, 9.9999)
self.thick_entry.set_precision(self.decimals)
self.thick_entry.setWrapping(True)
self.thick_entry.setSingleStep(10 ** -self.decimals)
grid0.addWidget(self.thick_label, 1, 0)
grid0.addWidget(self.thick_entry, 1, 1)
# Length #
self.l_label = QtWidgets.QLabel('%s:' % _("Length"))
self.l_label.setToolTip(
_("The length of the line that makes the corner marker.")
)
self.l_entry = FCDoubleSpinner()
self.l_entry.set_range(-9999.9999, 9999.9999)
self.l_entry.set_precision(self.decimals)
self.l_entry.setSingleStep(10 ** -self.decimals)
# Margin #
self.margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
self.margin_label.setToolTip(
_("Bounding box margin.")
)
self.margin_entry = FCDoubleSpinner()
self.margin_entry.set_range(-9999.9999, 9999.9999)
self.margin_entry.set_precision(self.decimals)
self.margin_entry.setSingleStep(0.1)
grid0.addWidget(self.margin_label, 2, 0)
grid0.addWidget(self.margin_entry, 2, 1)
grid0.addWidget(self.l_label, 4, 0)
grid0.addWidget(self.l_entry, 4, 1)
self.layout.addStretch()

View File

@ -1,320 +0,0 @@
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, NumericalEvalTupleEntry
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
settings = QSettings("Open Source", "FlatCAM")
if settings.contains("machinist"):
machinist_setting = settings.value('machinist', type=int)
else:
machinist_setting = 0
class ToolsISOPrefGroupUI(OptionsGroupUI):
def __init__(self, decimals=4, parent=None):
super(ToolsISOPrefGroupUI, self).__init__(self, parent=parent)
self.setTitle(str(_("Isolation Tool Options")))
self.decimals = decimals
# ## Clear non-copper regions
self.iso_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
self.iso_label.setToolTip(
_("Create a Geometry object with\n"
"toolpaths to cut around polygons.")
)
self.layout.addWidget(self.iso_label)
grid0 = QtWidgets.QGridLayout()
self.layout.addLayout(grid0)
# Tool Dias
isotdlabel = QtWidgets.QLabel('<b><font color="green">%s:</font></b>' % _('Tools Dia'))
isotdlabel.setToolTip(
_("Diameters of the tools, separated by comma.\n"
"The value of the diameter has to use the dot decimals separator.\n"
"Valid values: 0.3, 1.0")
)
self.tool_dia_entry = NumericalEvalTupleEntry(border_color='#0069A9')
self.tool_dia_entry.setPlaceholderText(_("Comma separated values"))
grid0.addWidget(isotdlabel, 0, 0)
grid0.addWidget(self.tool_dia_entry, 0, 1, 1, 2)
# Tool order Radio Button
self.order_label = QtWidgets.QLabel('%s:' % _('Tool order'))
self.order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n"
"'No' --> means that the used order is the one in the tool table\n"
"'Forward' --> means that the tools will be ordered from small to big\n"
"'Reverse' --> means that the tools will ordered from big to small\n\n"
"WARNING: using rest machining will automatically set the order\n"
"in reverse and disable this control."))
self.order_radio = RadioSet([{'label': _('No'), 'value': 'no'},
{'label': _('Forward'), 'value': 'fwd'},
{'label': _('Reverse'), 'value': 'rev'}])
grid0.addWidget(self.order_label, 1, 0)
grid0.addWidget(self.order_radio, 1, 1, 1, 2)
# Tool Type Radio Button
self.tool_type_label = QtWidgets.QLabel('%s:' % _('Tool Type'))
self.tool_type_label.setToolTip(
_("Default tool type:\n"
"- 'V-shape'\n"
"- Circular")
)
self.tool_type_radio = RadioSet([{'label': _('V-shape'), 'value': 'V'},
{'label': _('Circular'), 'value': 'C1'}])
self.tool_type_radio.setToolTip(
_("Default tool type:\n"
"- 'V-shape'\n"
"- Circular")
)
grid0.addWidget(self.tool_type_label, 2, 0)
grid0.addWidget(self.tool_type_radio, 2, 1, 1, 2)
# Tip Dia
self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
self.tipdialabel.setToolTip(
_("The tip diameter for V-Shape Tool"))
self.tipdia_entry = FCDoubleSpinner()
self.tipdia_entry.set_precision(self.decimals)
self.tipdia_entry.set_range(0, 1000)
self.tipdia_entry.setSingleStep(0.1)
grid0.addWidget(self.tipdialabel, 3, 0)
grid0.addWidget(self.tipdia_entry, 3, 1, 1, 2)
# Tip Angle
self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
self.tipanglelabel.setToolTip(
_("The tip angle for V-Shape Tool.\n"
"In degrees."))
self.tipangle_entry = FCDoubleSpinner()
self.tipangle_entry.set_precision(self.decimals)
self.tipangle_entry.set_range(1, 180)
self.tipangle_entry.setSingleStep(5)
self.tipangle_entry.setWrapping(True)
grid0.addWidget(self.tipanglelabel, 4, 0)
grid0.addWidget(self.tipangle_entry, 4, 1, 1, 2)
# Cut Z entry
cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
cutzlabel.setToolTip(
_("Depth of cut into material. Negative value.\n"
"In FlatCAM units.")
)
self.cutz_entry = FCDoubleSpinner()
self.cutz_entry.set_precision(self.decimals)
self.cutz_entry.set_range(-9999.9999, 0.0000)
self.cutz_entry.setSingleStep(0.1)
self.cutz_entry.setToolTip(
_("Depth of cut into material. Negative value.\n"
"In FlatCAM units.")
)
grid0.addWidget(cutzlabel, 5, 0)
grid0.addWidget(self.cutz_entry, 5, 1, 1, 2)
# New Diameter
self.newdialabel = QtWidgets.QLabel('%s:' % _('New Dia'))
self.newdialabel.setToolTip(
_("Diameter for the new tool to add in the Tool Table.\n"
"If the tool is V-shape type then this value is automatically\n"
"calculated from the other parameters.")
)
self.newdia_entry = FCDoubleSpinner()
self.newdia_entry.set_precision(self.decimals)
self.newdia_entry.set_range(0.0001, 9999.9999)
self.newdia_entry.setSingleStep(0.1)
grid0.addWidget(self.newdialabel, 6, 0)
grid0.addWidget(self.newdia_entry, 6, 1, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 7, 0, 1, 3)
# Passes
passlabel = QtWidgets.QLabel('%s:' % _('Passes'))
passlabel.setToolTip(
_("Width of the isolation gap in\n"
"number (integer) of tool widths.")
)
self.passes_entry = FCSpinner()
self.passes_entry.set_range(1, 999)
self.passes_entry.setObjectName("i_passes")
grid0.addWidget(passlabel, 8, 0)
grid0.addWidget(self.passes_entry, 8, 1, 1, 2)
# Overlap Entry
overlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
overlabel.setToolTip(
_("How much (percentage) of the tool width to overlap each tool pass.")
)
self.overlap_entry = FCDoubleSpinner(suffix='%')
self.overlap_entry.set_precision(self.decimals)
self.overlap_entry.setWrapping(True)
self.overlap_entry.set_range(0.0000, 99.9999)
self.overlap_entry.setSingleStep(0.1)
self.overlap_entry.setObjectName("i_overlap")
grid0.addWidget(overlabel, 9, 0)
grid0.addWidget(self.overlap_entry, 9, 1, 1, 2)
# Milling Type Radio Button
self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
self.milling_type_label.setToolTip(
_("Milling type when the selected tool is of type: 'iso_op':\n"
"- climb / best for precision milling and to reduce tool usage\n"
"- conventional / useful when there is no backlash compensation")
)
self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
{'label': _('Conventional'), 'value': 'cv'}])
self.milling_type_radio.setToolTip(
_("Milling type when the selected tool is of type: 'iso_op':\n"
"- climb / best for precision milling and to reduce tool usage\n"
"- conventional / useful when there is no backlash compensation")
)
grid0.addWidget(self.milling_type_label, 10, 0)
grid0.addWidget(self.milling_type_radio, 10, 1, 1, 2)
# Follow
self.follow_label = QtWidgets.QLabel('%s:' % _('Follow'))
self.follow_label.setToolTip(
_("Generate a 'Follow' geometry.\n"
"This means that it will cut through\n"
"the middle of the trace.")
)
self.follow_cb = FCCheckBox()
self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n"
"This means that it will cut through\n"
"the middle of the trace."))
self.follow_cb.setObjectName("i_follow")
grid0.addWidget(self.follow_label, 11, 0)
grid0.addWidget(self.follow_cb, 11, 1, 1, 2)
# Isolation Type
self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type'))
self.iso_type_label.setToolTip(
_("Choose how the isolation will be executed:\n"
"- 'Full' -> complete isolation of polygons\n"
"- 'Ext' -> will isolate only on the outside\n"
"- 'Int' -> will isolate only on the inside\n"
"'Exterior' isolation is almost always possible\n"
"(with the right tool) but 'Interior'\n"
"isolation can be done only when there is an opening\n"
"inside of the polygon (e.g polygon is a 'doughnut' shape).")
)
self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'},
{'label': _('Ext'), 'value': 'ext'},
{'label': _('Int'), 'value': 'int'}])
self.iso_type_radio.setObjectName("i_type")
grid0.addWidget(self.iso_type_label, 12, 0)
grid0.addWidget(self.iso_type_radio, 12, 1, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 13, 0, 1, 3)
# Rest machining CheckBox
self.rest_cb = FCCheckBox('%s' % _("Rest"))
self.rest_cb.setObjectName("i_rest_machining")
self.rest_cb.setToolTip(
_("If checked, use 'rest machining'.\n"
"Basically it will isolate outside PCB features,\n"
"using the biggest tool and continue with the next tools,\n"
"from bigger to smaller, to isolate the copper features that\n"
"could not be cleared by previous tool, until there is\n"
"no more copper features to isolate or there are no more tools.\n"
"If not checked, use the standard algorithm.")
)
grid0.addWidget(self.rest_cb, 17, 0)
# Combine All Passes
self.combine_passes_cb = FCCheckBox(label=_('Combine'))
self.combine_passes_cb.setToolTip(
_("Combine all passes into one object")
)
self.combine_passes_cb.setObjectName("i_combine")
grid0.addWidget(self.combine_passes_cb, 17, 1)
# Exception Areas
self.except_cb = FCCheckBox(label=_('Except'))
self.except_cb.setToolTip(_("When the isolation geometry is generated,\n"
"by checking this, the area of the object below\n"
"will be subtracted from the isolation geometry."))
self.except_cb.setObjectName("i_except")
grid0.addWidget(self.except_cb, 17, 2)
# Isolation Scope
self.select_label = QtWidgets.QLabel('%s:' % _("Selection"))
self.select_label.setToolTip(
_("Isolation scope. Choose what to isolate:\n"
"- 'All' -> Isolate all the polygons in the object\n"
"- 'Area Selection' -> Isolate polygons within a selection area.\n"
"- 'Polygon Selection' -> Isolate a selection of polygons.\n"
"- 'Reference Object' - will process the area specified by another object.")
)
self.select_combo = FCComboBox()
self.select_combo.addItems(
[_("All"), _("Area Selection"), _("Polygon Selection"), _("Reference Object")]
)
self.select_combo.setObjectName("i_selection")
grid0.addWidget(self.select_label, 20, 0)
grid0.addWidget(self.select_combo, 20, 1, 1, 2)
# Area Shape
self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
self.area_shape_label.setToolTip(
_("The kind of selection shape used for area selection.")
)
self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
{'label': _("Polygon"), 'value': 'polygon'}])
grid0.addWidget(self.area_shape_label, 21, 0)
grid0.addWidget(self.area_shape_radio, 21, 1, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 22, 0, 1, 3)
# ## Plotting type
self.plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'},
{"label": _("Progressive"), "value": "progressive"}])
plotting_label = QtWidgets.QLabel('%s:' % _("Plotting"))
plotting_label.setToolTip(
_("- 'Normal' - normal plotting, done at the end of the job\n"
"- 'Progressive' - each shape is plotted after it is generated")
)
grid0.addWidget(plotting_label, 23, 0)
grid0.addWidget(self.plotting_radio, 23, 1, 1, 2)
self.layout.addStretch()

View File

@ -1,394 +0,0 @@
# ###########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# http://flatcam.org #
# Author: Juan Pablo Caram (c) #
# Date: 2/5/2014 #
# MIT Licence #
# Modified by Marius Stanciu (2020) #
# ###########################################################
from PyQt5 import QtCore
from AppObjects.ObjectCollection import *
from AppObjects.FlatCAMCNCJob import CNCJobObject
from AppObjects.FlatCAMDocument import DocumentObject
from AppObjects.FlatCAMExcellon import ExcellonObject
from AppObjects.FlatCAMGeometry import GeometryObject
from AppObjects.FlatCAMGerber import GerberObject
from AppObjects.FlatCAMScript import ScriptObject
import time
import traceback
# FlatCAM Translation
import gettext
import AppTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
class AppObject(QtCore.QObject):
# Emitted by app_obj.new_object() and passes the new object as argument, plot flag.
# on_object_created() adds the object to the collection, plots on appropriate flag
# and emits app_obj.new_object_available.
object_created = QtCore.pyqtSignal(object, bool, bool)
# Emitted when a object has been changed (like scaled, mirrored)
object_changed = QtCore.pyqtSignal(object)
# Emitted after object has been plotted.
# Calls 'on_zoom_fit' method to fit object in scene view in main thread to prevent drawing glitches.
object_plotted = QtCore.pyqtSignal(object)
plots_updated = QtCore.pyqtSignal()
def __init__(self, app):
super(AppObject, self).__init__()
self.app = app
self.inform = app.inform
# signals that are emitted when object state changes
self.object_created.connect(self.on_object_created)
self.object_changed.connect(self.on_object_changed)
self.object_plotted.connect(self.on_object_plotted)
self.plots_updated.connect(self.app.on_plots_updated)
def new_object(self, kind, name, initialize, plot=True, autoselected=True):
"""
Creates a new specialized FlatCAMObj and attaches it to the application,
this is, updates the GUI accordingly, any other records and plots it.
This method is thread-safe.
Notes:
* If the name is in use, the self.collection will modify it
when appending it to the collection. There is no need to handle
name conflicts here.
:param kind: The kind of object to create. One of 'gerber', 'excellon', 'cncjob' and 'geometry'.
:type kind: str
:param name: Name for the object.
:type name: str
:param initialize: Function to run after creation of the object but before it is attached to the application.
The function is called with 2 parameters: the new object and the App instance.
:type initialize: function
:param plot: If to plot the resulting object
:param autoselected: if the resulting object is autoselected in the Project tab and therefore in the
self.collection
:return: None
:rtype: None
"""
log.debug("AppObject.new_object()")
obj_plot = plot
obj_autoselected = autoselected
t0 = time.time() # Debug
# ## Create object
classdict = {
"gerber": GerberObject,
"excellon": ExcellonObject,
"cncjob": CNCJobObject,
"geometry": GeometryObject,
"script": ScriptObject,
"document": DocumentObject
}
log.debug("Calling object constructor...")
# Object creation/instantiation
obj = classdict[kind](name)
obj.units = self.app.options["units"]
# IMPORTANT
# The key names in defaults and options dictionary's are not random:
# they have to have in name first the type of the object (geometry, excellon, cncjob and gerber) or how it's
# called here, the 'kind' followed by an underline. Above the App default values from self.defaults are
# copied to self.options. After that, below, depending on the type of
# object that is created, it will strip the name of the object and the underline (if the original key was
# let's say "excellon_toolchange", it will strip the excellon_) and to the obj.options the key will become
# "toolchange"
for option in self.app.options:
if option.find(kind + "_") == 0:
oname = option[len(kind) + 1:]
obj.options[oname] = self.app.options[option]
obj.isHovering = False
obj.notHovering = True
# Initialize as per user request
# User must take care to implement initialize
# in a thread-safe way as is is likely that we
# have been invoked in a separate thread.
t1 = time.time()
log.debug("%f seconds before initialize()." % (t1 - t0))
try:
return_value = initialize(obj, self.app)
except Exception as e:
msg = '[ERROR_NOTCL] %s' % _("An internal error has occurred. See shell.\n")
msg += _("Object ({kind}) failed because: {error} \n\n").format(kind=kind, error=str(e))
msg += traceback.format_exc()
self.app.inform.emit(msg)
return "fail"
t2 = time.time()
log.debug("%f seconds executing initialize()." % (t2 - t1))
if return_value == 'fail':
log.debug("Object (%s) parsing and/or geometry creation failed." % kind)
return "fail"
# Check units and convert if necessary
# This condition CAN be true because initialize() can change obj.units
if self.app.options["units"].upper() != obj.units.upper():
self.app.inform.emit('%s: %s' % (_("Converting units to "), self.app.options["units"]))
obj.convert_units(self.app.options["units"])
t3 = time.time()
log.debug("%f seconds converting units." % (t3 - t2))
# Create the bounding box for the object and then add the results to the obj.options
# But not for Scripts or for Documents
if kind != 'document' and kind != 'script':
try:
xmin, ymin, xmax, ymax = obj.bounds()
obj.options['xmin'] = xmin
obj.options['ymin'] = ymin
obj.options['xmax'] = xmax
obj.options['ymax'] = ymax
except Exception as e:
log.warning("AppObject.new_object() -> The object has no bounds properties. %s" % str(e))
return "fail"
try:
if kind == 'excellon':
obj.fill_color = self.app.defaults["excellon_plot_fill"]
obj.outline_color = self.app.defaults["excellon_plot_line"]
if kind == 'gerber':
obj.fill_color = self.app.defaults["gerber_plot_fill"]
obj.outline_color = self.app.defaults["gerber_plot_line"]
except Exception as e:
log.warning("AppObject.new_object() -> setting colors error. %s" % str(e))
# update the KeyWords list with the name of the file
self.app.myKeywords.append(obj.options['name'])
log.debug("Moving new object back to main thread.")
# Move the object to the main thread and let the app know that it is available.
obj.moveToThread(self.app.main_thread)
self.object_created.emit(obj, obj_plot, obj_autoselected)
return obj
def new_excellon_object(self):
"""
Creates a new, blank Excellon object.
:return: None
"""
self.new_object('excellon', 'new_exc', lambda x, y: None, plot=False)
def new_geometry_object(self):
"""
Creates a new, blank and single-tool Geometry object.
:return: None
"""
def initialize(obj, app):
obj.multitool = False
self.new_object('geometry', 'new_geo', initialize, plot=False)
def new_gerber_object(self):
"""
Creates a new, blank Gerber object.
:return: None
"""
def initialize(grb_obj, app):
grb_obj.multitool = False
grb_obj.source_file = []
grb_obj.multigeo = False
grb_obj.follow = False
grb_obj.apertures = {}
grb_obj.solid_geometry = []
try:
grb_obj.options['xmin'] = 0
grb_obj.options['ymin'] = 0
grb_obj.options['xmax'] = 0
grb_obj.options['ymax'] = 0
except KeyError:
pass
self.new_object('gerber', 'new_grb', initialize, plot=False)
def new_script_object(self):
"""
Creates a new, blank TCL Script object.
:return: None
"""
# commands_list = "# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, " \
# "AlignDrillGrid, Bbox, Bounds, ClearShell, CopperClear,\n" \
# "# Cncjob, Cutout, Delete, Drillcncjob, ExportDXF, ExportExcellon, ExportGcode,\n" \
# "# ExportGerber, ExportSVG, Exteriors, Follow, GeoCutout, GeoUnion, GetNames,\n" \
# "# GetSys, ImportSvg, Interiors, Isolate, JoinExcellon, JoinGeometry, " \
# "ListSys, MillDrills,\n" \
# "# MillSlots, Mirror, New, NewExcellon, NewGeometry, NewGerber, Nregions, " \
# "Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject,\n" \
# "# Options, Paint, Panelize, PlotAl, PlotObjects, SaveProject, " \
# "SaveSys, Scale, SetActive, SetSys, SetOrigin, Skew, SubtractPoly,\n" \
# "# SubtractRectangle, Version, WriteGCode\n"
new_source_file = '# %s\n' % _('CREATE A NEW FLATCAM TCL SCRIPT') + \
'# %s:\n' % _('TCL Tutorial is here') + \
'# https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html\n' + '\n\n' + \
'# %s:\n' % _("FlatCAM commands list")
new_source_file += '# %s\n\n' % _("Type >help< followed by Run Code for a list of FlatCAM Tcl Commands "
"(displayed in Tcl Shell).")
def initialize(obj, app):
obj.source_file = deepcopy(new_source_file)
outname = 'new_script'
self.new_object('script', outname, initialize, plot=False)
def new_document_object(self):
"""
Creates a new, blank Document object.
:return: None
"""
def initialize(obj, app):
obj.source_file = ""
self.new_object('document', 'new_document', initialize, plot=False)
def on_object_created(self, obj, plot, auto_select):
"""
Event callback for object creation.
It will add the new object to the collection. After that it will plot the object in a threaded way
:param obj: The newly created FlatCAM object.
:param plot: if the newly create object t obe plotted
:param auto_select: if the newly created object to be autoselected after creation
:return: None
"""
t0 = time.time() # DEBUG
log.debug("on_object_created()")
# The Collection might change the name if there is a collision
self.app.collection.append(obj)
# after adding the object to the collection always update the list of objects that are in the collection
self.app.all_objects_list = self.app.collection.get_list()
# self.app.inform.emit('[selected] %s created & selected: %s' %
# (str(obj.kind).capitalize(), str(obj.options['name'])))
if obj.kind == 'gerber':
self.app.inform.emit('[selected] {kind} {tx}: <span style="color:{color};">{name}</span>'.format(
kind=obj.kind.capitalize(),
color='green',
name=str(obj.options['name']), tx=_("created/selected"))
)
elif obj.kind == 'excellon':
self.app.inform.emit('[selected] {kind} {tx}: <span style="color:{color};">{name}</span>'.format(
kind=obj.kind.capitalize(),
color='brown',
name=str(obj.options['name']), tx=_("created/selected"))
)
elif obj.kind == 'cncjob':
self.app.inform.emit('[selected] {kind} {tx}: <span style="color:{color};">{name}</span>'.format(
kind=obj.kind.capitalize(),
color='blue',
name=str(obj.options['name']), tx=_("created/selected"))
)
elif obj.kind == 'geometry':
self.app.inform.emit('[selected] {kind} {tx}: <span style="color:{color};">{name}</span>'.format(
kind=obj.kind.capitalize(),
color='red',
name=str(obj.options['name']), tx=_("created/selected"))
)
elif obj.kind == 'script':
self.app.inform.emit('[selected] {kind} {tx}: <span style="color:{color};">{name}</span>'.format(
kind=obj.kind.capitalize(),
color='orange',
name=str(obj.options['name']), tx=_("created/selected"))
)
elif obj.kind == 'document':
self.app.inform.emit('[selected] {kind} {tx}: <span style="color:{color};">{name}</span>'.format(
kind=obj.kind.capitalize(),
color='darkCyan',
name=str(obj.options['name']), tx=_("created/selected"))
)
# update the SHELL auto-completer model with the name of the new object
self.app.shell._edit.set_model_data(self.app.myKeywords)
if auto_select:
# select the just opened object but deselect the previous ones
self.app.collection.set_all_inactive()
self.app.collection.set_active(obj.options["name"])
else:
self.app.collection.set_all_inactive()
# here it is done the object plotting
def task(t_obj):
with self.app.proc_container.new(_("Plotting")):
if t_obj.kind == 'cncjob':
t_obj.plot(kind=self.app.defaults["cncjob_plot_kind"])
else:
t_obj.plot()
t1 = time.time() # DEBUG
log.debug("%f seconds adding object and plotting." % (t1 - t0))
self.object_plotted.emit(t_obj)
# Send to worker
# self.worker.add_task(worker_task, [self])
if plot is True:
self.app.worker_task.emit({'fcn': task, 'params': [obj]})
def on_object_changed(self, obj):
"""
Called whenever the geometry of the object was changed in some way.
This require the update of it's bounding values so it can be the selected on canvas.
Update the bounding box data from obj.options
:param obj: the object that was changed
:return: None
"""
try:
xmin, ymin, xmax, ymax = obj.bounds()
except TypeError:
return
obj.options['xmin'] = xmin
obj.options['ymin'] = ymin
obj.options['xmax'] = xmax
obj.options['ymax'] = ymax
log.debug("Object changed, updating the bounding box data on self.options")
# delete the old selection shape
self.app.delete_selection_shape()
self.app.should_we_save = True
def on_object_plotted(self):
"""
Callback called whenever the plotted object needs to be fit into the viewport (canvas)
:return: None
"""
self.app.on_zoom_fit()

View File

@ -1,440 +0,0 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 5/17/2020 #
# MIT Licence #
# ##########################################################
from PyQt5 import QtWidgets, QtCore
from AppTool import AppTool
from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, FCButton
from shapely.geometry import MultiPolygon, LineString
from copy import deepcopy
import logging
import gettext
import AppTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class ToolCorners(AppTool):
toolName = _("Corner Markers Tool")
def __init__(self, app):
AppTool.__init__(self, app)
self.app = app
self.canvas = self.app.plotcanvas
self.decimals = self.app.decimals
self.units = ''
# ## Title
title_label = QtWidgets.QLabel("%s" % self.toolName)
title_label.setStyleSheet("""
QLabel
{
font-size: 16px;
font-weight: bold;
}
""")
self.layout.addWidget(title_label)
self.layout.addWidget(QtWidgets.QLabel(''))
# Gerber object #
self.object_label = QtWidgets.QLabel('<b>%s:</b>' % _("GERBER"))
self.object_label.setToolTip(
_("The Gerber object to which will be added corner markers.")
)
self.object_combo = FCComboBox()
self.object_combo.setModel(self.app.collection)
self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.object_combo.is_last = True
self.object_combo.obj_type = "Gerber"
self.layout.addWidget(self.object_label)
self.layout.addWidget(self.object_combo)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.layout.addWidget(separator_line)
self.points_label = QtWidgets.QLabel('<b>%s:</b>' % _('Locations'))
self.points_label.setToolTip(
_("Locations where to place corner markers.")
)
self.layout.addWidget(self.points_label)
# BOTTOM LEFT
self.bl_cb = FCCheckBox(_("Bottom Left"))
self.layout.addWidget(self.bl_cb)
# BOTTOM RIGHT
self.br_cb = FCCheckBox(_("Bottom Right"))
self.layout.addWidget(self.br_cb)
# TOP LEFT
self.tl_cb = FCCheckBox(_("Top Left"))
self.layout.addWidget(self.tl_cb)
# TOP RIGHT
self.tr_cb = FCCheckBox(_("Top Right"))
self.layout.addWidget(self.tr_cb)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.layout.addWidget(separator_line)
# Toggle ALL
self.toggle_all_cb = FCCheckBox(_("Toggle ALL"))
self.layout.addWidget(self.toggle_all_cb)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.layout.addWidget(separator_line)
# ## Grid Layout
grid_lay = QtWidgets.QGridLayout()
self.layout.addLayout(grid_lay)
grid_lay.setColumnStretch(0, 0)
grid_lay.setColumnStretch(1, 1)
self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
self.param_label.setToolTip(
_("Parameters used for this tool.")
)
grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
# Thickness #
self.thick_label = QtWidgets.QLabel('%s:' % _("Thickness"))
self.thick_label.setToolTip(
_("The thickness of the line that makes the corner marker.")
)
self.thick_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.thick_entry.set_range(0.0000, 9.9999)
self.thick_entry.set_precision(self.decimals)
self.thick_entry.setWrapping(True)
self.thick_entry.setSingleStep(10 ** -self.decimals)
grid_lay.addWidget(self.thick_label, 1, 0)
grid_lay.addWidget(self.thick_entry, 1, 1)
# Length #
self.l_label = QtWidgets.QLabel('%s:' % _("Length"))
self.l_label.setToolTip(
_("The length of the line that makes the corner marker.")
)
self.l_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.l_entry.set_range(-9999.9999, 9999.9999)
self.l_entry.set_precision(self.decimals)
self.l_entry.setSingleStep(10 ** -self.decimals)
grid_lay.addWidget(self.l_label, 2, 0)
grid_lay.addWidget(self.l_entry, 2, 1)
# Margin #
self.margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
self.margin_label.setToolTip(
_("Bounding box margin.")
)
self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.margin_entry.set_range(-9999.9999, 9999.9999)
self.margin_entry.set_precision(self.decimals)
self.margin_entry.setSingleStep(0.1)
grid_lay.addWidget(self.margin_label, 3, 0)
grid_lay.addWidget(self.margin_entry, 3, 1)
separator_line_2 = QtWidgets.QFrame()
separator_line_2.setFrameShape(QtWidgets.QFrame.HLine)
separator_line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
grid_lay.addWidget(separator_line_2, 4, 0, 1, 2)
# ## Insert Corner Marker
self.add_marker_button = FCButton(_("Add Marker"))
self.add_marker_button.setToolTip(
_("Will add corner markers to the selected Gerber file.")
)
self.add_marker_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
grid_lay.addWidget(self.add_marker_button, 11, 0, 1, 2)
self.layout.addStretch()
# ## Reset Tool
self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
self.reset_button.setToolTip(
_("Will reset the tool parameters.")
)
self.reset_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.layout.addWidget(self.reset_button)
# Objects involved in Copper thieving
self.grb_object = None
# store the flattened geometry here:
self.flat_geometry = []
# Tool properties
self.fid_dia = None
self.grb_steps_per_circle = self.app.defaults["gerber_circle_steps"]
# SIGNALS
self.add_marker_button.clicked.connect(self.add_markers)
self.toggle_all_cb.toggled.connect(self.on_toggle_all)
def run(self, toggle=True):
self.app.defaults.report_usage("ToolCorners()")
if toggle:
# if the splitter is hidden, display it, else hide it but only if the current widget is the same
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
AppTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, _("Corners Tool"))
def install(self, icon=None, separator=None, **kwargs):
AppTool.install(self, icon, separator, shortcut='Alt+M', **kwargs)
def set_tool_ui(self):
self.units = self.app.defaults['units']
self.thick_entry.set_value(self.app.defaults["tools_corners_thickness"])
self.l_entry.set_value(float(self.app.defaults["tools_corners_length"]))
self.margin_entry.set_value(float(self.app.defaults["tools_corners_margin"]))
self.toggle_all_cb.set_value(False)
def on_toggle_all(self, val):
self.bl_cb.set_value(val)
self.br_cb.set_value(val)
self.tl_cb.set_value(val)
self.tr_cb.set_value(val)
def add_markers(self):
self.app.call_source = "corners_tool"
tl_state = self.tl_cb.get_value()
tr_state = self.tr_cb.get_value()
bl_state = self.bl_cb.get_value()
br_state = self.br_cb.get_value()
# get the Gerber object on which the corner marker will be inserted
selection_index = self.object_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.object_combo.rootModelIndex())
try:
self.grb_object = model_index.internalPointer().obj
except Exception as e:
log.debug("ToolCorners.add_markers() --> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
return
xmin, ymin, xmax, ymax = self.grb_object.bounds()
points = {}
if tl_state:
points['tl'] = (xmin, ymax)
if tr_state:
points['tr'] = (xmax, ymax)
if bl_state:
points['bl'] = (xmin, ymin)
if br_state:
points['br'] = (xmax, ymin)
self.add_corners_geo(points, g_obj=self.grb_object)
self.grb_object.source_file = self.app.export_gerber(obj_name=self.grb_object.options['name'],
filename=None,
local_use=self.grb_object, use_thread=False)
self.on_exit()
def add_corners_geo(self, points_storage, g_obj):
"""
Add geometry to the solid_geometry of the copper Gerber object
:param points_storage: a dictionary holding the points where to add corners
:param g_obj: the Gerber object where to add the geometry
:return: None
"""
line_thickness = self.thick_entry.get_value()
line_length = self.l_entry.get_value()
margin = self.margin_entry.get_value()
geo_list = []
if not points_storage:
self.app.inform.emit("[ERROR_NOTCL] %s." % _("Please select at least a location"))
return
for key in points_storage:
if key == 'tl':
pt = points_storage[key]
x = pt[0] - margin - line_thickness / 2.0
y = pt[1] + margin + line_thickness / 2.0
line_geo_hor = LineString([
(x, y), (x + line_length, y)
])
line_geo_vert = LineString([
(x, y), (x, y - line_length)
])
geo_list.append(line_geo_hor)
geo_list.append(line_geo_vert)
if key == 'tr':
pt = points_storage[key]
x = pt[0] + margin + line_thickness / 2.0
y = pt[1] + margin + line_thickness / 2.0
line_geo_hor = LineString([
(x, y), (x - line_length, y)
])
line_geo_vert = LineString([
(x, y), (x, y - line_length)
])
geo_list.append(line_geo_hor)
geo_list.append(line_geo_vert)
if key == 'bl':
pt = points_storage[key]
x = pt[0] - margin - line_thickness / 2.0
y = pt[1] - margin - line_thickness / 2.0
line_geo_hor = LineString([
(x, y), (x + line_length, y)
])
line_geo_vert = LineString([
(x, y), (x, y + line_length)
])
geo_list.append(line_geo_hor)
geo_list.append(line_geo_vert)
if key == 'br':
pt = points_storage[key]
x = pt[0] + margin + line_thickness / 2.0
y = pt[1] - margin - line_thickness / 2.0
line_geo_hor = LineString([
(x, y), (x - line_length, y)
])
line_geo_vert = LineString([
(x, y), (x, y + line_length)
])
geo_list.append(line_geo_hor)
geo_list.append(line_geo_vert)
aperture_found = None
for ap_id, ap_val in g_obj.apertures.items():
if ap_val['type'] == 'C' and ap_val['size'] == line_thickness:
aperture_found = ap_id
break
geo_buff_list = []
if aperture_found:
for geo in geo_list:
geo_buff = geo.buffer(line_thickness / 2.0, resolution=self.grb_steps_per_circle, join_style=2)
geo_buff_list.append(geo_buff)
dict_el = {}
dict_el['follow'] = geo
dict_el['solid'] = geo_buff
g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
else:
ap_keys = list(g_obj.apertures.keys())
if ap_keys:
new_apid = str(int(max(ap_keys)) + 1)
else:
new_apid = '10'
g_obj.apertures[new_apid] = {}
g_obj.apertures[new_apid]['type'] = 'C'
g_obj.apertures[new_apid]['size'] = line_thickness
g_obj.apertures[new_apid]['geometry'] = []
for geo in geo_list:
geo_buff = geo.buffer(line_thickness / 2.0, resolution=self.grb_steps_per_circle, join_style=3)
geo_buff_list.append(geo_buff)
dict_el = {}
dict_el['follow'] = geo
dict_el['solid'] = geo_buff
g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
s_list = []
if g_obj.solid_geometry:
try:
for poly in g_obj.solid_geometry:
s_list.append(poly)
except TypeError:
s_list.append(g_obj.solid_geometry)
geo_buff_list = MultiPolygon(geo_buff_list)
geo_buff_list = geo_buff_list.buffer(0)
for poly in geo_buff_list:
s_list.append(poly)
g_obj.solid_geometry = MultiPolygon(s_list)
def replot(self, obj, run_thread=True):
def worker_task():
with self.app.proc_container.new('%s...' % _("Plotting")):
obj.plot()
if run_thread:
self.app.worker_task.emit({'fcn': worker_task, 'params': []})
else:
worker_task()
def on_exit(self):
# plot the object
try:
self.replot(obj=self.grb_object)
except (AttributeError, TypeError):
return
# update the bounding box values
try:
a, b, c, d = self.grb_object.bounds()
self.grb_object.options['xmin'] = a
self.grb_object.options['ymin'] = b
self.grb_object.options['xmax'] = c
self.grb_object.options['ymax'] = d
except Exception as e:
log.debug("ToolCorners.on_exit() copper_obj bounds error --> %s" % str(e))
# reset the variables
self.grb_object = None
self.app.call_source = "app"
self.app.inform.emit('[success] %s' % _("Corners Tool exit."))

View File

@ -1,455 +0,0 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 2/14/2020 #
# MIT Licence #
# ##########################################################
from PyQt5 import QtWidgets, QtCore
from AppTool import AppTool
from AppGUI.GUIElements import FCButton, FCDoubleSpinner, RadioSet, FCComboBox, NumericalEvalEntry, FCEntry
from shapely.ops import unary_union
from copy import deepcopy
import math
import logging
import gettext
import AppTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class ToolEtchCompensation(AppTool):
toolName = _("Etch Compensation Tool")
def __init__(self, app):
self.app = app
self.decimals = self.app.decimals
AppTool.__init__(self, app)
self.tools_frame = QtWidgets.QFrame()
self.tools_frame.setContentsMargins(0, 0, 0, 0)
self.layout.addWidget(self.tools_frame)
self.tools_box = QtWidgets.QVBoxLayout()
self.tools_box.setContentsMargins(0, 0, 0, 0)
self.tools_frame.setLayout(self.tools_box)
# Title
title_label = QtWidgets.QLabel("%s" % self.toolName)
title_label.setStyleSheet("""
QLabel
{
font-size: 16px;
font-weight: bold;
}
""")
self.tools_box.addWidget(title_label)
# Grid Layout
grid0 = QtWidgets.QGridLayout()
grid0.setColumnStretch(0, 0)
grid0.setColumnStretch(1, 1)
self.tools_box.addLayout(grid0)
grid0.addWidget(QtWidgets.QLabel(''), 0, 0, 1, 2)
# Target Gerber Object
self.gerber_combo = FCComboBox()
self.gerber_combo.setModel(self.app.collection)
self.gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.gerber_combo.is_last = True
self.gerber_combo.obj_type = "Gerber"
self.gerber_label = QtWidgets.QLabel('<b>%s:</b>' % _("GERBER"))
self.gerber_label.setToolTip(
_("Gerber object that will be inverted.")
)
grid0.addWidget(self.gerber_label, 1, 0, 1, 2)
grid0.addWidget(self.gerber_combo, 2, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 3, 0, 1, 2)
self.util_label = QtWidgets.QLabel("<b>%s:</b>" % _("Utilities"))
self.util_label.setToolTip('%s.' % _("Conversion utilities"))
grid0.addWidget(self.util_label, 4, 0, 1, 2)
# Oz to um conversion
self.oz_um_label = QtWidgets.QLabel('%s:' % _('Oz to Microns'))
self.oz_um_label.setToolTip(
_("Will convert from oz thickness to microns [um].\n"
"Can use formulas with operators: /, *, +, -, %, .\n"
"The real numbers use the dot decimals separator.")
)
grid0.addWidget(self.oz_um_label, 5, 0, 1, 2)
hlay_1 = QtWidgets.QHBoxLayout()
self.oz_entry = NumericalEvalEntry(border_color='#0069A9')
self.oz_entry.setPlaceholderText(_("Oz value"))
self.oz_to_um_entry = FCEntry()
self.oz_to_um_entry.setPlaceholderText(_("Microns value"))
self.oz_to_um_entry.setReadOnly(True)
hlay_1.addWidget(self.oz_entry)
hlay_1.addWidget(self.oz_to_um_entry)
grid0.addLayout(hlay_1, 6, 0, 1, 2)
# Mils to um conversion
self.mils_um_label = QtWidgets.QLabel('%s:' % _('Mils to Microns'))
self.mils_um_label.setToolTip(
_("Will convert from mils to microns [um].\n"
"Can use formulas with operators: /, *, +, -, %, .\n"
"The real numbers use the dot decimals separator.")
)
grid0.addWidget(self.mils_um_label, 7, 0, 1, 2)
hlay_2 = QtWidgets.QHBoxLayout()
self.mils_entry = NumericalEvalEntry(border_color='#0069A9')
self.mils_entry.setPlaceholderText(_("Mils value"))
self.mils_to_um_entry = FCEntry()
self.mils_to_um_entry.setPlaceholderText(_("Microns value"))
self.mils_to_um_entry.setReadOnly(True)
hlay_2.addWidget(self.mils_entry)
hlay_2.addWidget(self.mils_to_um_entry)
grid0.addLayout(hlay_2, 8, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 9, 0, 1, 2)
self.param_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
self.param_label.setToolTip('%s.' % _("Parameters for this tool"))
grid0.addWidget(self.param_label, 10, 0, 1, 2)
# Thickness
self.thick_label = QtWidgets.QLabel('%s:' % _('Copper Thickness'))
self.thick_label.setToolTip(
_("The thickness of the copper foil.\n"
"In microns [um].")
)
self.thick_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.thick_entry.set_precision(self.decimals)
self.thick_entry.set_range(0.0000, 9999.9999)
self.thick_entry.setObjectName(_("Thickness"))
grid0.addWidget(self.thick_label, 12, 0)
grid0.addWidget(self.thick_entry, 12, 1)
self.ratio_label = QtWidgets.QLabel('%s:' % _("Ratio"))
self.ratio_label.setToolTip(
_("The ratio of lateral etch versus depth etch.\n"
"Can be:\n"
"- custom -> the user will enter a custom value\n"
"- preselection -> value which depends on a selection of etchants")
)
self.ratio_radio = RadioSet([
{'label': _('Etch Factor'), 'value': 'factor'},
{'label': _('Etchants list'), 'value': 'etch_list'},
{'label': _('Manual offset'), 'value': 'manual'}
], orientation='vertical', stretch=False)
grid0.addWidget(self.ratio_label, 14, 0, 1, 2)
grid0.addWidget(self.ratio_radio, 16, 0, 1, 2)
# Etchants
self.etchants_label = QtWidgets.QLabel('%s:' % _('Etchants'))
self.etchants_label.setToolTip(
_("A list of etchants.")
)
self.etchants_combo = FCComboBox(callback=self.confirmation_message)
self.etchants_combo.setObjectName(_("Etchants"))
self.etchants_combo.addItems(["CuCl2", "Fe3Cl", _("Alkaline baths")])
grid0.addWidget(self.etchants_label, 18, 0)
grid0.addWidget(self.etchants_combo, 18, 1)
# Etch Factor
self.factor_label = QtWidgets.QLabel('%s:' % _('Etch factor'))
self.factor_label.setToolTip(
_("The ratio between depth etch and lateral etch .\n"
"Accepts real numbers and formulas using the operators: /,*,+,-,%")
)
self.factor_entry = NumericalEvalEntry(border_color='#0069A9')
self.factor_entry.setPlaceholderText(_("Real number or formula"))
self.factor_entry.setObjectName(_("Etch_factor"))
grid0.addWidget(self.factor_label, 19, 0)
grid0.addWidget(self.factor_entry, 19, 1)
# Manual Offset
self.offset_label = QtWidgets.QLabel('%s:' % _('Offset'))
self.offset_label.setToolTip(
_("Value with which to increase or decrease (buffer)\n"
"the copper features. In microns [um].")
)
self.offset_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.offset_entry.set_precision(self.decimals)
self.offset_entry.set_range(-9999.9999, 9999.9999)
self.offset_entry.setObjectName(_("Offset"))
grid0.addWidget(self.offset_label, 20, 0)
grid0.addWidget(self.offset_entry, 20, 1)
# Hide the Etchants and Etch factor
self.etchants_label.hide()
self.etchants_combo.hide()
self.factor_label.hide()
self.factor_entry.hide()
self.offset_label.hide()
self.offset_entry.hide()
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 22, 0, 1, 2)
self.compensate_btn = FCButton(_('Compensate'))
self.compensate_btn.setToolTip(
_("Will increase the copper features thickness to compensate the lateral etch.")
)
self.compensate_btn.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
grid0.addWidget(self.compensate_btn, 24, 0, 1, 2)
self.tools_box.addStretch()
# ## Reset Tool
self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
self.reset_button.setToolTip(
_("Will reset the tool parameters.")
)
self.reset_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.tools_box.addWidget(self.reset_button)
self.compensate_btn.clicked.connect(self.on_compensate)
self.reset_button.clicked.connect(self.set_tool_ui)
self.ratio_radio.activated_custom.connect(self.on_ratio_change)
self.oz_entry.textChanged.connect(self.on_oz_conversion)
self.mils_entry.textChanged.connect(self.on_mils_conversion)
def install(self, icon=None, separator=None, **kwargs):
AppTool.install(self, icon, separator, shortcut='', **kwargs)
def run(self, toggle=True):
self.app.defaults.report_usage("ToolEtchCompensation()")
log.debug("ToolEtchCompensation() is running ...")
if toggle:
# if the splitter is hidden, display it, else hide it but only if the current widget is the same
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
AppTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, _("Etch Compensation Tool"))
def set_tool_ui(self):
self.thick_entry.set_value(18.0)
self.ratio_radio.set_value('factor')
def on_ratio_change(self, val):
"""
Called on activated_custom signal of the RadioSet GUI element self.radio_ratio
:param val: 'c' or 'p': 'c' means custom factor and 'p' means preselected etchants
:type val: str
:return: None
:rtype:
"""
if val == 'factor':
self.etchants_label.hide()
self.etchants_combo.hide()
self.factor_label.show()
self.factor_entry.show()
self.offset_label.hide()
self.offset_entry.hide()
elif val == 'etch_list':
self.etchants_label.show()
self.etchants_combo.show()
self.factor_label.hide()
self.factor_entry.hide()
self.offset_label.hide()
self.offset_entry.hide()
else:
self.etchants_label.hide()
self.etchants_combo.hide()
self.factor_label.hide()
self.factor_entry.hide()
self.offset_label.show()
self.offset_entry.show()
def on_oz_conversion(self, txt):
try:
val = eval(txt)
# oz thickness to mils by multiplying with 1.37
# mils to microns by multiplying with 25.4
val *= 34.798
except Exception:
self.oz_to_um_entry.set_value('')
return
self.oz_to_um_entry.set_value(val, self.decimals)
def on_mils_conversion(self, txt):
try:
val = eval(txt)
val *= 25.4
except Exception:
self.mils_to_um_entry.set_value('')
return
self.mils_to_um_entry.set_value(val, self.decimals)
def on_compensate(self):
log.debug("ToolEtchCompensation.on_compensate()")
ratio_type = self.ratio_radio.get_value()
thickness = self.thick_entry.get_value() / 1000 # in microns
grb_circle_steps = int(self.app.defaults["gerber_circle_steps"])
obj_name = self.gerber_combo.currentText()
outname = obj_name + "_comp"
# Get source object.
try:
grb_obj = self.app.collection.get_by_name(obj_name)
except Exception as e:
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(obj_name)))
return "Could not retrieve object: %s with error: %s" % (obj_name, str(e))
if grb_obj is None:
if obj_name == '':
obj_name = 'None'
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
return
if ratio_type == 'factor':
etch_factor = 1 / self.factor_entry.get_value()
offset = thickness / etch_factor
elif ratio_type == 'etch_list':
etchant = self.etchants_combo.get_value()
if etchant == "CuCl2":
etch_factor = 0.33
else:
etch_factor = 0.25
offset = thickness / etch_factor
else:
offset = self.offset_entry.get_value() / 1000 # in microns
try:
__ = iter(grb_obj.solid_geometry)
except TypeError:
grb_obj.solid_geometry = list(grb_obj.solid_geometry)
new_solid_geometry = []
for poly in grb_obj.solid_geometry:
new_solid_geometry.append(poly.buffer(offset, int(grb_circle_steps)))
new_solid_geometry = unary_union(new_solid_geometry)
new_options = {}
for opt in grb_obj.options:
new_options[opt] = deepcopy(grb_obj.options[opt])
new_apertures = deepcopy(grb_obj.apertures)
# update the apertures attributes (keys in the apertures dict)
for ap in new_apertures:
type = new_apertures[ap]['type']
for k in new_apertures[ap]:
if type == 'R' or type == 'O':
if k == 'width' or k == 'height':
new_apertures[ap][k] += offset
else:
if k == 'size' or k == 'width' or k == 'height':
new_apertures[ap][k] += offset
if k == 'geometry':
for geo_el in new_apertures[ap][k]:
if 'solid' in geo_el:
geo_el['solid'] = geo_el['solid'].buffer(offset, int(grb_circle_steps))
# in case of 'R' or 'O' aperture type we need to update the aperture 'size' after
# the 'width' and 'height' keys were updated
for ap in new_apertures:
type = new_apertures[ap]['type']
for k in new_apertures[ap]:
if type == 'R' or type == 'O':
if k == 'size':
new_apertures[ap][k] = math.sqrt(
new_apertures[ap]['width'] ** 2 + new_apertures[ap]['height'] ** 2)
def init_func(new_obj, app_obj):
"""
Init a new object in FlatCAM Object collection
:param new_obj: New object
:type new_obj: ObjectCollection
:param app_obj: App
:type app_obj: App_Main.App
:return: None
:rtype:
"""
new_obj.options.update(new_options)
new_obj.options['name'] = outname
new_obj.fill_color = deepcopy(grb_obj.fill_color)
new_obj.outline_color = deepcopy(grb_obj.outline_color)
new_obj.apertures = deepcopy(new_apertures)
new_obj.solid_geometry = deepcopy(new_solid_geometry)
new_obj.source_file = self.app.export_gerber(obj_name=outname, filename=None,
local_use=new_obj, use_thread=False)
self.app.app_obj.new_object('gerber', outname, init_func)
def reset_fields(self):
self.gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
@staticmethod
def poly2rings(poly):
return [poly.exterior] + [interior for interior in poly.interiors]
# end of file

File diff suppressed because it is too large Load Diff

View File

@ -1,361 +0,0 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 4/23/2019 #
# MIT Licence #
# ##########################################################
from PyQt5 import QtWidgets, QtCore
from AppTool import AppTool
from AppParsers.ParsePDF import PdfParser, grace
from shapely.geometry import Point, MultiPolygon
from shapely.ops import unary_union
from copy import deepcopy
import zlib
import re
import time
import logging
import traceback
import gettext
import AppTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class ToolPDF(AppTool):
"""
Parse a PDF file.
Reference here: https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf
Return a list of geometries
"""
toolName = _("PDF Import Tool")
def __init__(self, app):
AppTool.__init__(self, app)
self.app = app
self.decimals = self.app.decimals
self.stream_re = re.compile(b'.*?FlateDecode.*?stream(.*?)endstream', re.S)
self.pdf_decompressed = {}
# key = file name and extension
# value is a dict to store the parsed content of the PDF
self.pdf_parsed = {}
# QTimer for periodic check
self.check_thread = QtCore.QTimer()
# Every time a parser is started we add a promise; every time a parser finished we remove a promise
# when empty we start the layer rendering
self.parsing_promises = []
self.parser = PdfParser(app=self.app)
def run(self, toggle=True):
self.app.defaults.report_usage("ToolPDF()")
self.set_tool_ui()
self.on_open_pdf_click()
def install(self, icon=None, separator=None, **kwargs):
AppTool.install(self, icon, separator, shortcut='Ctrl+Q', **kwargs)
def set_tool_ui(self):
pass
def on_open_pdf_click(self):
"""
File menu callback for opening an PDF file.
:return: None
"""
self.app.defaults.report_usage("ToolPDF.on_open_pdf_click()")
self.app.log.debug("ToolPDF.on_open_pdf_click()")
_filter_ = "Adobe PDF Files (*.pdf);;" \
"All Files (*.*)"
try:
filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open PDF"),
directory=self.app.get_last_folder(),
filter=_filter_)
except TypeError:
filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open PDF"), filter=_filter_)
if len(filenames) == 0:
self.app.inform.emit('[WARNING_NOTCL] %s.' % _("Open PDF cancelled"))
else:
# start the parsing timer with a period of 1 second
self.periodic_check(1000)
for filename in filenames:
if filename != '':
self.app.worker_task.emit({'fcn': self.open_pdf,
'params': [filename]})
def open_pdf(self, filename):
short_name = filename.split('/')[-1].split('\\')[-1]
self.parsing_promises.append(short_name)
self.pdf_parsed[short_name] = {}
self.pdf_parsed[short_name]['pdf'] = {}
self.pdf_parsed[short_name]['filename'] = filename
self.pdf_decompressed[short_name] = ''
if self.app.abort_flag:
# graceful abort requested by the user
raise grace
with self.app.proc_container.new(_("Parsing PDF file ...")):
with open(filename, "rb") as f:
pdf = f.read()
stream_nr = 0
for s in re.findall(self.stream_re, pdf):
if self.app.abort_flag:
# graceful abort requested by the user
raise grace
stream_nr += 1
log.debug("PDF STREAM: %d\n" % stream_nr)
s = s.strip(b'\r\n')
try:
self.pdf_decompressed[short_name] += (zlib.decompress(s).decode('UTF-8') + '\r\n')
except Exception as e:
self.app.inform.emit('[ERROR_NOTCL] %s: %s\n%s' % (_("Failed to open"), str(filename), str(e)))
log.debug("ToolPDF.open_pdf().obj_init() --> %s" % str(e))
return
self.pdf_parsed[short_name]['pdf'] = self.parser.parse_pdf(pdf_content=self.pdf_decompressed[short_name])
# we used it, now we delete it
self.pdf_decompressed[short_name] = ''
# removal from list is done in a multithreaded way therefore not always the removal can be done
# try to remove until it's done
try:
while True:
self.parsing_promises.remove(short_name)
time.sleep(0.1)
except Exception as e:
log.debug("ToolPDF.open_pdf() --> %s" % str(e))
self.app.inform.emit('[success] %s: %s' % (_("Opened"), str(filename)))
def layer_rendering_as_excellon(self, filename, ap_dict, layer_nr):
outname = filename.split('/')[-1].split('\\')[-1] + "_%s" % str(layer_nr)
# store the points here until reconstitution:
# keys are diameters and values are list of (x,y) coords
points = {}
def obj_init(exc_obj, app_obj):
clear_geo = [geo_el['clear'] for geo_el in ap_dict['0']['geometry']]
for geo in clear_geo:
xmin, ymin, xmax, ymax = geo.bounds
center = (((xmax - xmin) / 2) + xmin, ((ymax - ymin) / 2) + ymin)
# for drill bits, even in INCH, it's enough 3 decimals
correction_factor = 0.974
dia = (xmax - xmin) * correction_factor
dia = round(dia, 3)
if dia in points:
points[dia].append(center)
else:
points[dia] = [center]
sorted_dia = sorted(points.keys())
name_tool = 0
for dia in sorted_dia:
name_tool += 1
# create tools dictionary
spec = {"C": dia, 'solid_geometry': []}
exc_obj.tools[str(name_tool)] = spec
# create drill list of dictionaries
for dia_points in points:
if dia == dia_points:
for pt in points[dia_points]:
exc_obj.drills.append({'point': Point(pt), 'tool': str(name_tool)})
break
ret = exc_obj.create_geometry()
if ret == 'fail':
log.debug("Could not create geometry for Excellon object.")
return "fail"
for tool in exc_obj.tools:
if exc_obj.tools[tool]['solid_geometry']:
return
app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("No geometry found in file"), outname))
return "fail"
with self.app.proc_container.new(_("Rendering PDF layer #%d ...") % int(layer_nr)):
ret_val = self.app.app_obj.new_object("excellon", outname, obj_init, autoselected=False)
if ret_val == 'fail':
self.app.inform.emit('[ERROR_NOTCL] %s' % _('Open PDF file failed.'))
return
# Register recent file
self.app.file_opened.emit("excellon", filename)
# GUI feedback
self.app.inform.emit('[success] %s: %s' % (_("Rendered"), outname))
def layer_rendering_as_gerber(self, filename, ap_dict, layer_nr):
outname = filename.split('/')[-1].split('\\')[-1] + "_%s" % str(layer_nr)
def obj_init(grb_obj, app_obj):
grb_obj.apertures = ap_dict
poly_buff = []
follow_buf = []
for ap in grb_obj.apertures:
for k in grb_obj.apertures[ap]:
if k == 'geometry':
for geo_el in ap_dict[ap][k]:
if 'solid' in geo_el:
poly_buff.append(geo_el['solid'])
if 'follow' in geo_el:
follow_buf.append(geo_el['follow'])
poly_buff = unary_union(poly_buff)
if '0' in grb_obj.apertures:
global_clear_geo = []
if 'geometry' in grb_obj.apertures['0']:
for geo_el in ap_dict['0']['geometry']:
if 'clear' in geo_el:
global_clear_geo.append(geo_el['clear'])
if global_clear_geo:
solid = []
for apid in grb_obj.apertures:
if 'geometry' in grb_obj.apertures[apid]:
for elem in grb_obj.apertures[apid]['geometry']:
if 'solid' in elem:
solid_geo = deepcopy(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)
if solid_geo.is_empty:
solid_geo = elem['solid']
try:
for poly in solid_geo:
solid.append(poly)
except TypeError:
solid.append(solid_geo)
poly_buff = deepcopy(MultiPolygon(solid))
follow_buf = unary_union(follow_buf)
try:
poly_buff = poly_buff.buffer(0.0000001)
except ValueError:
pass
try:
poly_buff = poly_buff.buffer(-0.0000001)
except ValueError:
pass
grb_obj.solid_geometry = deepcopy(poly_buff)
grb_obj.follow_geometry = deepcopy(follow_buf)
with self.app.proc_container.new(_("Rendering PDF layer #%d ...") % int(layer_nr)):
ret = self.app.app_obj.new_object('gerber', outname, obj_init, autoselected=False)
if ret == 'fail':
self.app.inform.emit('[ERROR_NOTCL] %s' % _('Open PDF file failed.'))
return
# Register recent file
self.app.file_opened.emit('gerber', filename)
# GUI feedback
self.app.inform.emit('[success] %s: %s' % (_("Rendered"), outname))
def periodic_check(self, check_period):
"""
This function starts an QTimer and it will periodically check if parsing was done
: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("ToolPDF --> Periodic Check started.")
try:
self.check_thread.stop()
except TypeError:
pass
self.check_thread.setInterval(check_period)
try:
self.check_thread.timeout.disconnect(self.periodic_check_handler)
except (TypeError, AttributeError):
pass
self.check_thread.timeout.connect(self.periodic_check_handler)
self.check_thread.start(QtCore.QThread.HighPriority)
def periodic_check_handler(self):
"""
If the parsing worker finished then start multithreaded rendering
:return:
"""
# log.debug("checking parsing --> %s" % str(self.parsing_promises))
try:
if not self.parsing_promises:
self.check_thread.stop()
log.debug("PDF --> start rendering")
# parsing finished start the layer rendering
if self.pdf_parsed:
obj_to_delete = []
for object_name in self.pdf_parsed:
if self.app.abort_flag:
# graceful abort requested by the user
raise grace
filename = deepcopy(self.pdf_parsed[object_name]['filename'])
pdf_content = deepcopy(self.pdf_parsed[object_name]['pdf'])
obj_to_delete.append(object_name)
for k in pdf_content:
if self.app.abort_flag:
# graceful abort requested by the user
raise grace
ap_dict = pdf_content[k]
print(k, ap_dict)
if ap_dict:
layer_nr = k
if k == 0:
self.app.worker_task.emit({'fcn': self.layer_rendering_as_excellon,
'params': [filename, ap_dict, layer_nr]})
else:
self.app.worker_task.emit({'fcn': self.layer_rendering_as_gerber,
'params': [filename, ap_dict, layer_nr]})
# delete the object already processed so it will not be processed again for other objects
# that were opened at the same time; like in drag & drop on AppGUI
for obj_name in obj_to_delete:
if obj_name in self.pdf_parsed:
self.pdf_parsed.pop(obj_name)
log.debug("ToolPDF --> Periodic check finished.")
except Exception:
traceback.print_exc()

View File

@ -1,45 +0,0 @@
from AppTools.ToolCalculators import ToolCalculator
from AppTools.ToolCalibration import ToolCalibration
from AppTools.ToolDblSided import DblSidedTool
from AppTools.ToolExtractDrills import ToolExtractDrills
from AppTools.ToolAlignObjects import AlignObjects
from AppTools.ToolFilm import Film
from AppTools.ToolImage import ToolImage
from AppTools.ToolDistance import Distance
from AppTools.ToolDistanceMin import DistanceMin
from AppTools.ToolMove import ToolMove
from AppTools.ToolCutOut import CutOut
from AppTools.ToolNCC import NonCopperClear
from AppTools.ToolPaint import ToolPaint
from AppTools.ToolIsolation import ToolIsolation
from AppTools.ToolOptimal import ToolOptimal
from AppTools.ToolPanelize import Panelize
from AppTools.ToolPcbWizard import PcbWizard
from AppTools.ToolPDF import ToolPDF
from AppTools.ToolProperties import Properties
from AppTools.ToolQRCode import QRCode
from AppTools.ToolRulesCheck import RulesCheck
from AppTools.ToolCopperThieving import ToolCopperThieving
from AppTools.ToolFiducials import ToolFiducials
from AppTools.ToolShell import FCShell
from AppTools.ToolSolderPaste import SolderPaste
from AppTools.ToolSub import ToolSub
from AppTools.ToolTransform import ToolTransform
from AppTools.ToolPunchGerber import ToolPunchGerber
from AppTools.ToolInvertGerber import ToolInvertGerber
from AppTools.ToolCorners import ToolCorners
from AppTools.ToolEtchCompensation import ToolEtchCompensation

View File

@ -7,229 +7,6 @@ CHANGELOG for FlatCAM beta
=================================================
2.06.2020
- Tcl Shell - added a button to delete the content of the active line
- Tcl Command Isolate - fixed to work in the new configuration
- Tcl Command Follow - fixed to work in the new configuration
- Etch Compensation Tool - added a new etchant: alkaline baths
- fixed spacing in the status toolbar icons
- updated the translation files to the latest changes
- modified behavior of object comboboxes in Paint, NCC and CutOut Tools: now if an object is selected in Project Tab and is of the supported kind in the Tool, it will be auto-selected
- fixed some more strings
- updated the Google-translations for the German, Spanish, French
- updated the Romanian translation
- replaced the icon for the Editor in Toolbar (both for the normal icons and for icons in dark theme)
1.06.2020
- made the Distance Tool display the angle in values between 0 and 359.9999 degrees
- changed some strings
- fixed the warning that old preferences found even for new installation
- in Paint Tool fixed the message to select a polygon when using the Selection: Single Polygon being overwritten by the "Grid disabled" message
- more changes in strings throughout the app
- made some minor changes in the GUI of the FlatCAM Tools
- in Tools Database made sure that each new tool added has a unique name
- in AppTool made some methods to be class methods
- reverted the class methods in AppTool
- added a button for Transformations Tool in the lower side (common) of the Object UI
- some other UI changes
- after using Isolation Tool it will switch automatically to the Geometry UI
- in Preferences replaced some widgets with a new one that combine a Slider with a Spinner (from David Robertson)
- in Preferences replaced the widgets that sets colors with a compound one (from David Robertson)
- made Progressive plotting work in Isolation Tool
- fix an issue with progressive plotted shapes not being deleted on the end of the job
- some fixed due of recent changes and some strings changed
- added a validator for the FCColorEntry GUI element such that only the valid chars are accepted
- changed the status bar label to have an icon instead of text
- added a label in status bar that will toggle the Preferences tab
- made some changes such that that the label in status bar for toggling the Preferences Tab will be updated in various cases of closing the tab
- changed colors for the status bar labels and added some of the new icons in the gray version
- remade visibility as threaded - it seems that I can't really squeeze more performance from this
31.05.2020
- structural changes in Preferences from David Robertson
- made last filter selected for open file to be used next time when opening files (for Excellon, GCode and Gerber files, for now)
30.05.2020
- made confirmation messages for the values that are modified not to be printed in the Shell
- Isolation Tool: working on the Rest machining: almost there, perhaps I will use multiprocessing
- Isolation Tool: removed the tools that have empty geometry in case of rest machining
- Isolation Tool: solved some naming issues
- Isolation Tool: updated the tools dict with the common parameters value on isolating
- Fixed a recent change that made the edited Geometry objects in the Geometry Editor not to be plotted after saving changes
- modified the Tool Database such that when a tool shape is selected as 'V' any change in the Vdia or Vangle or CutZ parameters will update the tool diameter value
- In Tool Isolation made sure that the use of ESC key while some processes are active will disconnect the mouse events that may be connected, correctly
- optimized the Gerber UI
- added a Multi-color checkbox for the Geometry UI (will color differently tool geometry when the geometry is multitool)
- added a Multi-color checkbox for the Excellon UI (this way colors for each tool are easier to differentiate especially when the diameter is close)
- made the Shell Dock always show docked
- fixed NCC Tool behavior when selecting tools for Isolation operation
29.05.2020
- fixed the Tool Isolation when using the 'follow' parameter
- in Isolation Tool when the Rest machining is checked the combine parameter is set True automatically because the rest machining concept make sense only when all tools are used together
- some changes in the UI; added in the status bar an icon to control the Shell Dock
- clicking on the activity icon will replot all objects
- optimized UI in Tool Isolation
- overloaded the App inform signal to allow not printing to shell if a second bool parameter is given; modified some GUI messages to use this feature
- fixed the shell status label status on shell dock close from close button
- refactored some methods from App class and moved them to plotcanvas (plotcanvaslegacy) class
- added an label with icon in the status bar, clicking it will toggle (show status) of the X-Y axis on cavnas
- optimized the UI, added to status bar an icon to toggle the axis
- updated the Etch Compensation Tool by adding a new possibility to compensate the lateral etch (manual value)
- updated the Etch Compensation Tool such that the resulting Gerber object will have the apertures attributes ('size', 'width', 'height') updated to the changes
28.05.2020
- made the visibility change (when using the Spacebar key in Project Tab) to be not threaded and to use the enabled property of the ShapesCollection which should be faster
- updated the Tool Database class to have the Isolation Tool data
- Isolation Tool - made to work the adding of tools from database
- fixed some issues related to using the new Numerical... GUI elements
- fixed issues in the Tool Subtract
- remade Tool Subtract to use multiprocessing when processing geometry
- the resulting Gerber file from Tool Subtract has now the attribute source_file populated
27.05.2020
- working on Isolation Tool: made to work the Isolation with multiple tools without rest machining
26.05.2020
- working on Isolation Tool: made to work the tool parameters data to GUI and GUI to data
- Isolation Tool: reworked the GUI
- if there is a Gerber object selected then in Isolation Tool the Gerber object combobox will show that object name as current
- made the Project Tree items not editable by clicking on selected Tree items (the object rename can still be done in the Selected tab)
- working on Isolation Tool: added a Preferences section in Edit -> Preferences and updated their usage within the Isolation tool
- fixed milling drills not plotting the resulting Geometry object
- all tuple entries in the Preferences UI are now protected against letter entry
- all entries in the Preferences UI that have numerical entry are protected now against letters
- cleaned the Preferences UI in the Gerber area
- minor UI changes
25.05.2020
- updated the GUI fields for the Scale and Offset in the Object UI to allow only numeric values and operators in the list [/,*,+,-], spaces, dots and comma
- modified the Etch Compensation Tool and added conversion utilities from Oz thickenss and mils to microns
- added a Toggle All checkbox to Corner Markers Tool
- added an Icon to the MessageBox that asks for saving if the user try to close the app and there is some unsaved work
- changed and added some icons
- fixed the Shortcuts Tab to reflect the actual current shortcut keys
- started to work on moving the Isolation Routing from the Gerber Object UI to it's own tool
- created a new tool: Isolation Routing Tool: work in progress
- some fixes in NCC Tool
- added a dialog in Menu -> Help -> ReadMe?
24.05.2020
- changes some icons
- added a new GUI element which is a evaluated LineEdit that accepts only float numbers and /,*,+,-,% chars
- finished the Etch Compensation Tool
- fixed unreliable work of Gerber Editor and optimized the App.editor2object() method
- updated the Gerber parser such that it will parse correctly Gerber files that have only one solid polygon inside with multiple clear polygons (like those generated by the Invert Tool)
- fixed a small bug in the Geometry UI that made updating the storage from GUI not to work
- some small changes in Gerber Editor
23.05.2020
- fixed a issue when testing for Exclusion areas overlap over the Geometry object solid_geometry
22.05.2020
- fixed the algorithm for calculating closest points in the Exclusion areas
- added the Exclusion zones processing to Geometry GCode generation
21.05.2020
- added the Exclusion zones processing to Excellon GCode generation
- fixed a non frequent plotting problem for CNCJob objects made out of Excellon objects
19.05.2020
- updated the Italian language (translation incomplete)
- updated all the language strings to the latest changes; updated the POT file
- fixed a possible malfunction in Tool Punch Gerber
18.05.2020
- fixed the PDF Tool when importing as Gerber objects
- moved all the parsing out of the PDF Tool to a new file ParsePDF in the flatcamParsers folder
- trying to fix the pixmap load crash when running a FlatCAMScript
- made the workspace label in the status bar clickable and also added a status bar message on status toggle for workspace
- modified the GUI for Film and Panelize Tools
- moved some of the GUI related methods from FlatCAMApp.App to the flatcamGUI.MainGUI class
- moved Shortcuts Tab creation in it's own class
- renamed classes to have shorter names and grouped
- removed reference to postprocessors and replaced it with preprocessors
- more refactoring class names
- moved some of the methods from the App class to the ObjectCollection class
- moved all the new_object related methods in their own class AppObjects.AppObject
- more refactoring; solved some issues introduced by the refactoring
- solved a circular import
- updated the language translation files to the latest changes (no translation)
- working on a new Tool: Etch Compensation Tool -> installed the tool and created the GUI and class template
- moved more methods out of App_Main class
- added confirmation messages for toggle of HUD, Grid, Grid Snap, Axis
- added icon in status bar for HUD; clicking on it will toggle the HUD (heads up display)
- fixes due of recent changes
- fixed issue #417
17.05.2020
- added new FlatCAM Tool: Corner Markers Tool which will add line markers in the selected corners of the bounding box of the targeted Gerber object
- added a menu entry in Menu -> View for Toggle HUD
- solved the issue with the GUI in the Notebook being expanded too much in width due of the FCDoubleSpinner and FCSpinner sizeHint by setting the sizePolicy to Ignored value
- fixed the workspace being always A4
- added a label in the status bar to show if the workplace is active and what size it is
- now the Edit command (either from Menu Edit ->Edit Object) or through the shortcut key (E key) or project tab context menu works also for the CNCJob objects (will open a text Editor with the GCode)
- fixed the object collection methods that return a list of objects or names of objects such that they have a parameter now to allow adding to those lists (or not) for the objects of type Script or Document. Thus fixing some of the Tcl commands such Set Origin
- reverted the previous changes to object collection; it is better to create empty methods in FlatCAMScript and FlatCAMDocument objects
16.05.2020
- worked on the NCC Tool; added a new clear method named 'Combo' which will go through all methods until the clear is done
- added a Preferences parameter for font size used in HUD
13.05.2020
- updated the French translation strings, made by @micmac (Michel)
12.05.2020
- fixed recent issues introduced in Tcl command Drillcncjob
- updated the Cncjob to use the 'endxy' parameter which dictates the x,y position at the end of the job
- now the Tcl commands Drillcncjob and Cncjob can use the toolchangexy and endxy parameters with or without parenthesis (but no spaces allowed)
- modified the Tcl command Paint "single" parameter. Now it's value is a tuple with the x,y coordinates of the single polygon to be painted.
- the HUD display state is now persistent between app restarts
- updated the Distance Tool such that the right click of the mouse will cancel the tool unless it was a panning move
- modified the PlotCanvasLegacy to decide if there is a mouse drag based on the distance between the press event position and the release event position. If the distance is smaller than a delta distance then it is not a drag move.
11.05.2020
- removed the labels in status bar that display X,Y positions and replaced it with a HUD display on canvas (combo key SHIFT+H) will toggle the display of the HUD
- made the HUD work in Legacy2D mode
- fixed situation when the mouse cursor is outside of the canvas and no therefore returning None values
- remade the Snap Toolbar presence; now it is always active and situated in the Status Bar
- Snap Toolbar is now visible in Fullscreen
- in Fullscreen now the Notebook is available but it will be hidden on Fullscreen launch
- fixed some minor issues (in the HUD added a separating line, missing an icon in toolbars on first launch)
- made sure that the corner snap buttons are shown only in Editors
- changed the HUD color when using Dark theme
- fix issue in Legacy2D graphic mode where the snap function was not accessible when the PlotCanvasLegacy class was created
- modified the HUD in Legacy2D when using Dark Theme to use different colors
- modified how the graphic engine change act in Preferences: now only by clicking Apply(or Save) the change will happen. And there is also a message asking for confirmation
- re-added the position labels in the status bar; they will be useful if HUD is Off (Altium does the same :) so learn from the best)
- fixed the Tcl command Cncjob: there was a problem reported as issue #416. The command did not work due of the dpp parameter
- modified the Tcl command Cncjob such that if some of the parameters are not used then the default values will be used (set with set_sys)
- modified the Tcl command Drillcncjob to use the defaults when some of the parameters are not used
10.05.2020
- fixed the problem with using comma as decimal separator in Grid Snap fields
9.05.2020
- modified the GUI for Exclusion areas; now the shapes are displayed in a Table where they can be selected and deleted. Modification applied for Geometry Objects only (for now).
@ -285,7 +62,7 @@ CHANGELOG for FlatCAM beta
2.05.2020
- changed the icons for the grid snap in the status bar
- moved some of the methods from FlatCAMApp.App to flatcamGUI.MainGUI class
- moved some of the methods from FlatCAMApp.App to flatcamGUI.FlatCAMGUI class
- fixed bug in Gerber Editor in which the units conversion wasn't calculated correct
- fixed bug in Gerber Editor in which the QThread that is started on object edit was not stopped at clean up stage
- fixed bug in Gerber Editor that kept all the apertures (including the geometry) of a previously edited object that was not saved after edit
@ -1553,7 +1330,7 @@ CHANGELOG for FlatCAM beta
- optimized Rules Check Tool so it runs faster when doing Copper 2 Copper rule
- small GUI changes in Optimal Tool and in Film Tool
- some PEP8 corrections
- some code annotations to make it easier to navigate in the MainGUI.py
- some code annotations to make it easier to navigate in the FlatCAMGUI.py
- fixed exit FullScreen with Escape key
- added a new menu category in the MenuBar named 'Objects'. It will hold the objects found in the Project tab. Useful when working in FullScreen
- disabled a log.debug in ObjectColection.get_by_name()
@ -2992,7 +2769,7 @@ CHANGELOG for FlatCAM beta
- fix for issue #262: when doing Edit-> Save & Close Editor on a Geometry that is not generated through first entering into an Editor, the geometry disappear
- finished preparing for internationalization for the files: camlib and objectCollection
- fixed tools shortcuts not working anymore due of the new toggle parameter for the .run().
- finished preparing for internationalization for the files: FlatCAMEditor, MainGUI
- finished preparing for internationalization for the files: FlatCAMEditor, FlatCAMGUI
- finished preparing for internationalization for the files: FlatCAMObj, ObjectUI
- sorted the languages in the Preferences combobox
@ -3314,7 +3091,7 @@ CHANGELOG for FlatCAM beta
- fixed the name self-insert in save dialog file for GCode; added protection in case the save path is None
- fixed FlatCAM crash when trying to make drills GCode out of a file that have only slots.
- changed the messages for Units Conversion
- all key shortcuts work across the entire application; moved all the shortcuts definitions in MainGUI.keyPressEvent()
- all key shortcuts work across the entire application; moved all the shortcuts definitions in FlatCAMGUI.keyPressEvent()
- renamed the theme to layout because it is really a layout change
- added plot kind for CNC Job in the App Preferences
- combined the geocutout and cutout_any TCL commands - work in progress
@ -3835,7 +3612,7 @@ For now they are used only for Excellon objects who do have toolchange events
- fixed a reported bug generated by a typo for feedrate_z object in camlib.py. Because of that, the project could not be saved.
- fixed a G01 usage (should be G1) in Marlin preprocessor.
- changed the position of the Tool Dia entry in the Object UI and in MainGUI
- changed the position of the Tool Dia entry in the Object UI and in FlatCAMGUI
- fixed issues in the installer
30.10.2018
@ -4395,7 +4172,7 @@ still copper leftovers.
- modified generate_milling method which had issues from the Python3 port (it could not sort the tools due of dict to dict comparison no longer possible).
- modified the 'default' preprocessor in order to include a space between the value of Xcoord and the following Y
- made optional the using of threads for the milling command; by default it is OFF (False) because in the current configuration it creates issues when it is using threads
- modified the Panelize function and Tcl command Panelize. It was having issues due to multithreading (kept trying to modify a dictionary in redraw() method)and automatically selecting the last created object (feature introduced by me). I've added a parameter to the app_obj.new_object method, named autoselected (by default it is True) and in the panelize method I initialized it with False.
- modified the Panelize function and Tcl command Panelize. It was having issues due to multithreading (kept trying to modify a dictionary in redraw() method)and automatically selecting the last created object (feature introduced by me). I've added a parameter to the new_object method, named autoselected (by default it is True) and in the panelize method I initialized it with False.
By initializing the plot parameter with False for the temporary objects, I have increased dramatically the generation speed of the panel because now the temporary object are no longer ploted which consumed time.
- replaced log.warn() with log.warning() in camlib.py. Reason: deprecated
- fixed the issue that the "Defaults" button was having no effect when clicked and Options Combo was in Project Options

View File

@ -3,8 +3,8 @@ import os
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSettings, Qt
from App_Main import App
from AppGUI import VisPyPatches
from FlatCAMApp import App
from flatcamGUI import VisPyPatches
from multiprocessing import freeze_support
# import copyreg

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
from PyQt5 import QtGui, QtCore, QtWidgets
from AppGUI.GUIElements import FCTable, FCEntry, FCButton, FCFileSaveDialog
from flatcamGUI.GUIElements import FCTable, FCEntry, FCButton, FCFileSaveDialog
import sys
import webbrowser
@ -7,7 +7,7 @@ import webbrowser
from copy import deepcopy
from datetime import datetime
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -287,12 +287,12 @@ class BookmarkManager(QtWidgets.QWidget):
date = date.replace(' ', '_')
filter__ = "Text File (*.TXT);;All Files (*.*)"
filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Bookmarks"),
directory='{l_save}/{n}_{date}'.format(
filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export FlatCAM Bookmarks"),
directory='{l_save}/FlatCAM_{n}_{date}'.format(
l_save=str(self.app.get_last_save_folder()),
n=_("Bookmarks"),
date=date),
ext_filter=filter__)
filter=filter__)
filename = str(filename)
@ -334,7 +334,7 @@ class BookmarkManager(QtWidgets.QWidget):
self.app.log.debug("on_import_bookmarks()")
filter_ = "Text File (*.txt);;All Files (*.*)"
filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Import Bookmarks"), filter=filter_)
filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Import FlatCAM Bookmarks"), filter=filter_)
filename = str(filename)

View File

@ -12,18 +12,15 @@
# ##########################################################
from PyQt5 import QtCore
from shapely.geometry import Polygon, Point, LineString
from shapely.ops import unary_union
from shapely.geometry import Polygon, MultiPolygon
from AppGUI.VisPyVisuals import ShapeCollection
from AppTool import AppTool
from copy import deepcopy
from flatcamGUI.VisPyVisuals import ShapeCollection
from FlatCAMTool import FlatCAMTool
import numpy as np
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -32,9 +29,7 @@ if '_' not in builtins.__dict__:
class GracefulException(Exception):
"""
Graceful Exception raised when the user is requesting to cancel the current threaded task
"""
# Graceful Exception raised when the user is requesting to cancel the current threaded task
def __init__(self):
super().__init__()
@ -109,11 +104,8 @@ 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
:type hex_color: str
:param bright_factor: factor to change the color brightness [0 ... 1]
:type bright_factor: float
:return: Modified color
:rtype: str
:param bright_factor: factor to change the color brightness [0 ... 1]
:return: modified color
"""
if len(hex_color) != 7:
@ -138,9 +130,7 @@ def color_variant(hex_color, bright_factor=1):
class ExclusionAreas(QtCore.QObject):
"""
Functionality for adding Exclusion Areas for the Excellon and Geometry FlatCAM Objects
"""
e_shape_modified = QtCore.pyqtSignal()
def __init__(self, app):
@ -156,7 +146,7 @@ class ExclusionAreas(QtCore.QObject):
except AttributeError:
self.exclusion_shapes = None
else:
from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy
from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
self.exclusion_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name="exclusion")
# Event signals disconnect id holders
@ -177,8 +167,8 @@ class ExclusionAreas(QtCore.QObject):
{
"obj_type": string ("excellon" or "geometry") <- self.obj_type
"shape": Shapely polygon
"strategy": string ("over" or "around") <- self.strategy_button
"overz": float <- self.over_z_button
"strategy": string ("over" or "around") <- self.strategy
"overz": float <- self.over_z
}
'''
self.exclusion_areas_storage = []
@ -188,9 +178,9 @@ class ExclusionAreas(QtCore.QObject):
self.solid_geometry = []
self.obj_type = None
self.shape_type_button = None
self.over_z_button = None
self.strategy_button = None
self.shape_type = 'square' # TODO use the self.app.defaults when made general (not in Geo object Pref UI)
self.over_z = 0.1
self.strategy = None
self.cnc_button = None
def on_add_area_click(self, shape_button, overz_button, strategy_radio, cnc_button, solid_geo, obj_type):
@ -198,22 +188,21 @@ class ExclusionAreas(QtCore.QObject):
:param shape_button: a FCButton that has the value for the shape
:param overz_button: a FCDoubleSpinner that holds the Over Z value
:param strategy_radio: a RadioSet button with the strategy_button value
:param strategy_radio: a RadioSet button with the strategy value
:param cnc_button: a FCButton in Object UI that when clicked the CNCJob is created
We have a reference here so we can change the color signifying that exclusion areas are
available.
:param solid_geo: reference to the object solid geometry for which we add exclusion areas
:param obj_type: Type of FlatCAM object that called this method. String: "excellon" or "geometry"
:type obj_type: str
:return: None
:param obj_type: Type of FlatCAM object that called this method
:type obj_type: String: "excellon" or "geometry"
:return:
"""
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
self.app.call_source = 'geometry'
self.shape_type_button = shape_button
self.over_z_button = overz_button
self.strategy_button = strategy_radio
self.shape_type = shape_button.get_value()
self.over_z = overz_button.get_value()
self.strategy = strategy_radio.get_value()
self.cnc_button = cnc_button
self.solid_geometry = solid_geo
@ -234,14 +223,6 @@ class ExclusionAreas(QtCore.QObject):
# To be called after clicking on the plot.
def on_mouse_release(self, event):
"""
Called on mouse click release.
:param event: Mouse event
:type event:
:return: None
:rtype:
"""
if self.app.is_legacy is False:
event_pos = event.pos
# event_is_dragging = event.is_dragging
@ -259,11 +240,11 @@ class ExclusionAreas(QtCore.QObject):
x1, y1 = curr_pos[0], curr_pos[1]
# shape_type_button = self.ui.area_shape_radio.get_value()
# shape_type = self.ui.area_shape_radio.get_value()
# do clear area only for left mouse clicks
if event.button == 1:
if self.shape_type_button.get_value() == "square":
if self.shape_type == "square":
if self.first_click is False:
self.first_click = True
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the end point of the area."))
@ -287,14 +268,14 @@ class ExclusionAreas(QtCore.QObject):
# {
# "obj_type": string("excellon" or "geometry") < - self.obj_type
# "shape": Shapely polygon
# "strategy_button": string("over" or "around") < - self.strategy_button
# "overz": float < - self.over_z_button
# "strategy": string("over" or "around") < - self.strategy
# "overz": float < - self.over_z
# }
new_el = {
"obj_type": self.obj_type,
"shape": new_rectangle,
"strategy": self.strategy_button.get_value(),
"overz": self.over_z_button.get_value()
"strategy": self.strategy,
"overz": self.over_z
}
self.exclusion_areas_storage.append(new_el)
@ -306,7 +287,7 @@ class ExclusionAreas(QtCore.QObject):
face_color = "#FF7400BF"
# add a temporary shape on canvas
AppTool.draw_tool_selection_shape(
FlatCAMTool.draw_tool_selection_shape(
self, old_coords=(x0, y0), coords=(x1, y1),
color=color,
face_color=face_color,
@ -324,7 +305,7 @@ class ExclusionAreas(QtCore.QObject):
return ""
elif event.button == right_button and self.mouse_is_dragging is False:
shape_type = self.shape_type_button.get_value()
shape_type = self.shape_type
if shape_type == "square":
self.first_click = False
@ -341,23 +322,21 @@ class ExclusionAreas(QtCore.QObject):
# we need to add a Polygon and a Polygon can be made only from at least 3 points
if len(self.points) > 2:
AppTool.delete_moving_selection_shape(self)
FlatCAMTool.delete_moving_selection_shape(self)
pol = Polygon(self.points)
# do not add invalid polygons even if they are drawn by utility geometry
if pol.is_valid:
"""
{
"obj_type": string("excellon" or "geometry") < - self.obj_type
"shape": Shapely polygon
"strategy": string("over" or "around") < - self.strategy_button
"overz": float < - self.over_z_button
}
"""
# {
# "obj_type": string("excellon" or "geometry") < - self.obj_type
# "shape": Shapely polygon
# "strategy": string("over" or "around") < - self.strategy
# "overz": float < - self.over_z
# }
new_el = {
"obj_type": self.obj_type,
"shape": pol,
"strategy": self.strategy_button.get_value(),
"overz": self.over_z_button.get_value()
"strategy": self.strategy,
"overz": self.over_z
}
self.exclusion_areas_storage.append(new_el)
@ -368,7 +347,7 @@ class ExclusionAreas(QtCore.QObject):
color = "#098a8f"
face_color = "#FF7400BF"
AppTool.draw_selection_shape_polygon(
FlatCAMTool.draw_selection_shape_polygon(
self, points=self.points,
color=color,
face_color=face_color,
@ -380,7 +359,7 @@ class ExclusionAreas(QtCore.QObject):
self.poly_drawn = False
return
# AppTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
# FlatCAMTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
if self.app.is_legacy is False:
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
@ -403,11 +382,11 @@ class ExclusionAreas(QtCore.QObject):
if len(self.exclusion_areas_storage) == 0:
return
# since the exclusion areas should apply to all objects in the app collection, this check is limited to
# only the current object therefore it will not guarantee success
self.app.inform.emit("%s" % _("Exclusion areas added. Checking overlap with the object geometry ..."))
self.app.inform.emit(
"[success] %s" % _("Exclusion areas added. Checking overlap with the object geometry ..."))
for el in self.exclusion_areas_storage:
if el["shape"].intersects(unary_union(self.solid_geometry)):
if el["shape"].intersects(MultiPolygon(self.solid_geometry)):
self.on_clear_area_click()
self.app.inform.emit(
"[ERROR_NOTCL] %s" % _("Failed. Exclusion areas intersects the object geometry ..."))
@ -427,15 +406,10 @@ class ExclusionAreas(QtCore.QObject):
)
self.e_shape_modified.emit()
for k in self.exclusion_areas_storage:
print(k)
def area_disconnect(self):
"""
Will do the cleanup. Will disconnect the mouse events for the custom handlers in this class and initialize
certain class attributes.
:return: None
:rtype:
"""
if self.app.is_legacy is False:
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
@ -454,22 +428,15 @@ class ExclusionAreas(QtCore.QObject):
self.poly_drawn = False
self.exclusion_areas_storage = []
AppTool.delete_moving_selection_shape(self)
# AppTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
FlatCAMTool.delete_moving_selection_shape(self)
# FlatCAMTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
self.app.call_source = "app"
self.app.inform.emit("[WARNING_NOTCL] %s" % _("Cancelled. Area exclusion drawing was interrupted."))
# called on mouse move
def on_mouse_move(self, event):
"""
Called on mouse move
:param event: mouse event
:type event:
:return: None
:rtype:
"""
shape_type = self.shape_type_button.get_value()
shape_type = self.shape_type
if self.app.is_legacy is False:
event_pos = event.pos
@ -499,20 +466,15 @@ class ExclusionAreas(QtCore.QObject):
size=self.app.defaults["global_cursor_size"])
# update the positions on status bar
self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.4f" % (curr_pos[0], curr_pos[1]))
if self.cursor_pos is None:
self.cursor_pos = (0, 0)
self.app.dx = curr_pos[0] - float(self.cursor_pos[0])
self.app.dy = curr_pos[1] - float(self.cursor_pos[1])
self.app.ui.position_label.setText("&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.4f&nbsp;" % (curr_pos[0], curr_pos[1]))
# self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp; <b>Dy</b>: "
# "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
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, curr_pos[0], units, curr_pos[1], units)
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp; <b>Dy</b>: "
"%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
if self.obj_type == 'excellon':
color = "#FF7400"
@ -531,20 +493,14 @@ class ExclusionAreas(QtCore.QObject):
face_color=face_color,
coords=(curr_pos[0], curr_pos[1]))
else:
AppTool.delete_moving_selection_shape(self)
AppTool.draw_moving_selection_shape_poly(
FlatCAMTool.delete_moving_selection_shape(self)
FlatCAMTool.draw_moving_selection_shape_poly(
self, points=self.points,
color=color,
face_color=face_color,
data=(curr_pos[0], curr_pos[1]))
def on_clear_area_click(self):
"""
Slot for clicking the button for Deleting all the Exclusion areas.
:return: None
:rtype:
"""
self.clear_shapes()
# restore the default StyleSheet
@ -559,28 +515,21 @@ class ExclusionAreas(QtCore.QObject):
self.cnc_button.setToolTip('%s' % _("Generate the CNC Job object."))
def clear_shapes(self):
"""
Will delete all the Exclusion areas; will delete on canvas any possible selection box for the Exclusion areas.
:return: None
:rtype:
"""
self.exclusion_areas_storage.clear()
AppTool.delete_moving_selection_shape(self)
FlatCAMTool.delete_moving_selection_shape(self)
self.app.delete_selection_shape()
AppTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
self.app.inform.emit('%s' % _("All exclusion zones deleted."))
FlatCAMTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
self.app.inform.emit('[success] %s' % _("All exclusion zones deleted."))
def delete_sel_shapes(self, idxs):
"""
:param idxs: list of indexes in self.exclusion_areas_storage list to be deleted
:type idxs: list
:return: None
:param idxs: list of indexes in self.exclusion_areas_storage list to be deleted
:return:
"""
# delete all plotted shapes
AppTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
FlatCAMTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
# delete shapes
for idx in sorted(idxs, reverse=True):
@ -618,234 +567,4 @@ class ExclusionAreas(QtCore.QObject):
""")
self.cnc_button.setToolTip('%s' % _("Generate the CNC Job object."))
self.app.inform.emit('%s' % _("All exclusion zones deleted."))
def travel_coordinates(self, start_point, end_point, tooldia):
"""
WIll create a path the go around the exclusion areas on the shortest path when travelling (at a Z above the
material).
:param start_point: X,Y coordinates for the start point of the travel line
:type start_point: tuple
:param end_point: X,Y coordinates for the destination point of the travel line
:type end_point: tuple
:param tooldia: THe tool diameter used and which generates the travel lines
:type tooldia float
:return: A list of x,y tuples that describe the avoiding path
:rtype: list
"""
ret_list = []
# Travel lines: rapids. Should not pass through Exclusion areas
travel_line = LineString([start_point, end_point])
origin_point = Point(start_point)
buffered_storage = []
# add a little something to the half diameter, to make sure that we really don't enter in the exclusion zones
buffered_distance = (tooldia / 2.0) + (0.1 if self.app.defaults['units'] == 'MM' else 0.00393701)
for area in self.exclusion_areas_storage:
new_area = deepcopy(area)
new_area['shape'] = area['shape'].buffer(buffered_distance, join_style=2)
buffered_storage.append(new_area)
# sort the Exclusion areas from the closest to the start_point to the farthest
tmp = []
for area in buffered_storage:
dist = Point(start_point).distance(area['shape'])
tmp.append((dist, area))
tmp.sort(key=lambda k: k[0])
sorted_area_storage = [k[1] for k in tmp]
# process the ordered exclusion areas list
for area in sorted_area_storage:
outline = area['shape'].exterior
if travel_line.intersects(outline):
intersection_pts = travel_line.intersection(outline)
if isinstance(intersection_pts, Point):
# it's just a touch, continue
continue
entry_pt = nearest_point(origin_point, intersection_pts)
exit_pt = farthest_point(origin_point, intersection_pts)
if area['strategy'] == 'around':
full_vertex_points = [Point(x) for x in list(outline.coords)]
# the last coordinate in outline, a LinearRing, is the closing one
# therefore a duplicate of the first one; discard it
vertex_points = full_vertex_points[:-1]
# dist_from_entry = [(entry_pt.distance(vt), vertex_points.index(vt)) for vt in vertex_points]
# closest_point_entry = nsmallest(1, dist_from_entry, key=lambda x: x[0])
# start_idx = closest_point_entry[0][1]
#
# dist_from_exit = [(exit_pt.distance(vt), vertex_points.index(vt)) for vt in vertex_points]
# closest_point_exit = nsmallest(1, dist_from_exit, key=lambda x: x[0])
# end_idx = closest_point_exit[0][1]
# pts_line_entry = None
# pts_line_exit = None
# for i in range(len(full_vertex_points)):
# try:
# line = LineString(
# [
# (full_vertex_points[i].x, full_vertex_points[i].y),
# (full_vertex_points[i + 1].x, full_vertex_points[i + 1].y)
# ]
# )
# except IndexError:
# continue
#
# if entry_pt.within(line) or entry_pt.equals(Point(line.coords[0])) or \
# entry_pt.equals(Point(line.coords[1])):
# pts_line_entry = [Point(x) for x in line.coords]
#
# if exit_pt.within(line) or exit_pt.equals(Point(line.coords[0])) or \
# exit_pt.equals(Point(line.coords[1])):
# pts_line_exit = [Point(x) for x in line.coords]
#
# closest_point_entry = nearest_point(entry_pt, pts_line_entry)
# start_idx = vertex_points.index(closest_point_entry)
#
# closest_point_exit = nearest_point(exit_pt, pts_line_exit)
# end_idx = vertex_points.index(closest_point_exit)
# find all vertexes for which a line from start_point does not cross the Exclusion area polygon
# the same for end_point
# we don't need closest points for which the path leads to crosses of the Exclusion area
close_start_points = []
close_end_points = []
for i in range(len(vertex_points)):
try:
start_line = LineString(
[
start_point,
(vertex_points[i].x, vertex_points[i].y)
]
)
end_line = LineString(
[
end_point,
(vertex_points[i].x, vertex_points[i].y)
]
)
except IndexError:
continue
if not start_line.crosses(area['shape']):
close_start_points.append(vertex_points[i])
if not end_line.crosses(area['shape']):
close_end_points.append(vertex_points[i])
closest_point_entry = nearest_point(entry_pt, close_start_points)
closest_point_exit = nearest_point(exit_pt, close_end_points)
start_idx = vertex_points.index(closest_point_entry)
end_idx = vertex_points.index(closest_point_exit)
# calculate possible paths: one clockwise the other counterclockwise on the exterior of the
# exclusion area outline (Polygon.exterior)
vp_len = len(vertex_points)
if end_idx > start_idx:
path_1 = vertex_points[start_idx:(end_idx + 1)]
path_2 = [vertex_points[start_idx]]
idx = start_idx
for __ in range(vp_len):
idx = idx - 1 if idx > 0 else (vp_len - 1)
path_2.append(vertex_points[idx])
if idx == end_idx:
break
else:
path_1 = vertex_points[end_idx:(start_idx + 1)]
path_2 = [vertex_points[end_idx]]
idx = end_idx
for __ in range(vp_len):
idx = idx - 1 if idx > 0 else (vp_len - 1)
path_2.append(vertex_points[idx])
if idx == start_idx:
break
path_1.reverse()
path_2.reverse()
# choose the one with the lesser length
length_path_1 = 0
for i in range(len(path_1)):
try:
length_path_1 += path_1[i].distance(path_1[i + 1])
except IndexError:
pass
length_path_2 = 0
for i in range(len(path_2)):
try:
length_path_2 += path_2[i].distance(path_2[i + 1])
except IndexError:
pass
path = path_1 if length_path_1 < length_path_2 else path_2
# transform the list of Points into a list of Points coordinates
path_coords = [[None, (p.x, p.y)] for p in path]
ret_list += path_coords
else:
path_coords = [[float(area['overz']), (entry_pt.x, entry_pt.y)], [None, (exit_pt.x, exit_pt.y)]]
ret_list += path_coords
# create a new LineString to test again for possible other Exclusion zones
last_pt_in_path = path_coords[-1][1]
travel_line = LineString([last_pt_in_path, end_point])
ret_list.append([None, end_point])
return ret_list
def farthest_point(origin, points_list):
"""
Calculate the farthest Point in a list from another Point
:param origin: Reference Point
:type origin: Point
:param points_list: List of Points or a MultiPoint
:type points_list: list
:return: Farthest Point
:rtype: Point
"""
old_dist = 0
fartherst_pt = None
for pt in points_list:
dist = abs(origin.distance(pt))
if dist >= old_dist:
fartherst_pt = pt
old_dist = dist
return fartherst_pt
def nearest_point(origin, points_list):
"""
Calculate the nearest Point in a list from another Point
:param origin: Reference Point
:type origin: Point
:param points_list: List of Points or a MultiPoint
:type points_list: list
:return: Nearest Point
:rtype: Point
"""
old_dist = np.Inf
nearest_pt = None
for pt in points_list:
dist = abs(origin.distance(pt))
if dist <= old_dist:
nearest_pt = pt
old_dist = dist
return nearest_pt
self.app.inform.emit('[success] %s' % _("All exclusion zones deleted."))

View File

@ -1,5 +1,5 @@
from PyQt5 import QtGui, QtCore, QtWidgets
from AppGUI.GUIElements import FCTable, FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, \
from flatcamGUI.GUIElements import FCTable, FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, \
FCTree, RadioSet, FCFileSaveDialog
from camlib import to_dict
@ -8,10 +8,8 @@ import json
from copy import deepcopy
from datetime import datetime
import math
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -119,7 +117,7 @@ class ToolsDB(QtWidgets.QWidget):
)
self.buttons_box.addWidget(import_db_btn)
self.add_tool_from_db = FCButton(_("Transfer the Tool"))
self.add_tool_from_db = FCButton(_("Add Tool from Tools DB"))
self.add_tool_from_db.setToolTip(
_("Add a new tool in the Tools Table of the\n"
"active Geometry object after selecting a tool\n"
@ -315,7 +313,7 @@ class ToolsDB(QtWidgets.QWidget):
self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
return
self.app.inform.emit('[success] %s: %s' % (_("Loaded Tools DB from"), filename))
self.app.inform.emit('[success] %s: %s' % (_("Loaded FlatCAM Tools DB from"), filename))
self.build_db_ui()
@ -657,7 +655,7 @@ class ToolsDB(QtWidgets.QWidget):
l_save=str(self.app.get_last_save_folder()),
n=_("Tools_Database"),
date=date),
ext_filter=filter__)
filter=filter__)
filename = str(filename)
@ -726,7 +724,7 @@ class ToolsDB(QtWidgets.QWidget):
self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
return
self.app.inform.emit('[success] %s: %s' % (_("Loaded Tools DB from"), filename))
self.app.inform.emit('[success] %s: %s' % (_("Loaded FlatCAM Tools DB from"), filename))
self.build_db_ui()
self.callback_on_edited()
@ -1032,7 +1030,6 @@ class ToolsDB2(QtWidgets.QWidget):
self.advanced_box.setTitle(_("Advanced Geo Parameters"))
self.advanced_box.setFixedWidth(250)
# NCC TOOL BOX
self.ncc_box = QtWidgets.QGroupBox()
self.ncc_box.setStyleSheet("""
QGroupBox
@ -1045,7 +1042,6 @@ class ToolsDB2(QtWidgets.QWidget):
self.ncc_box.setTitle(_("NCC Parameters"))
self.ncc_box.setFixedWidth(250)
# PAINT TOOL BOX
self.paint_box = QtWidgets.QGroupBox()
self.paint_box.setStyleSheet("""
QGroupBox
@ -1058,24 +1054,10 @@ class ToolsDB2(QtWidgets.QWidget):
self.paint_box.setTitle(_("Paint Parameters"))
self.paint_box.setFixedWidth(250)
# ISOLATION TOOL BOX
self.iso_box = QtWidgets.QGroupBox()
self.iso_box.setStyleSheet("""
QGroupBox
{
font-size: 16px;
font-weight: bold;
}
""")
self.iso_vlay = QtWidgets.QVBoxLayout()
self.iso_box.setTitle(_("Isolation Parameters"))
self.iso_box.setFixedWidth(250)
self.basic_box.setLayout(self.basic_vlay)
self.advanced_box.setLayout(self.advanced_vlay)
self.ncc_box.setLayout(self.ncc_vlay)
self.paint_box.setLayout(self.paint_vlay)
self.iso_box.setLayout(self.iso_vlay)
geo_vlay = QtWidgets.QVBoxLayout()
geo_vlay.addWidget(self.basic_box)
@ -1085,7 +1067,6 @@ class ToolsDB2(QtWidgets.QWidget):
tools_vlay = QtWidgets.QVBoxLayout()
tools_vlay.addWidget(self.ncc_box)
tools_vlay.addWidget(self.paint_box)
tools_vlay.addWidget(self.iso_box)
tools_vlay.addStretch()
param_hlay.addLayout(geo_vlay)
@ -1497,7 +1478,7 @@ class ToolsDB2(QtWidgets.QWidget):
self.ncc_method_combo = FCComboBox()
self.ncc_method_combo.addItems(
[_("Standard"), _("Seed"), _("Lines"), _("Combo")]
[_("Standard"), _("Seed"), _("Lines")]
)
self.ncc_method_combo.setObjectName("gdb_n_method")
@ -1640,101 +1621,6 @@ class ToolsDB2(QtWidgets.QWidget):
self.grid3.addWidget(self.pathconnect_cb, 10, 0)
self.grid3.addWidget(self.paintcontour_cb, 10, 1)
# ###########################################################################
# ############### Paint UI form #############################################
# ###########################################################################
self.grid4 = QtWidgets.QGridLayout()
self.iso_vlay.addLayout(self.grid4)
self.grid4.setColumnStretch(0, 0)
self.grid4.setColumnStretch(1, 1)
self.iso_vlay.addStretch()
# Passes
passlabel = QtWidgets.QLabel('%s:' % _('Passes'))
passlabel.setToolTip(
_("Width of the isolation gap in\n"
"number (integer) of tool widths.")
)
self.passes_entry = FCSpinner()
self.passes_entry.set_range(1, 999)
self.passes_entry.setObjectName("gdb_i_passes")
self.grid4.addWidget(passlabel, 0, 0)
self.grid4.addWidget(self.passes_entry, 0, 1)
# Overlap Entry
overlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
overlabel.setToolTip(
_("How much (percentage) of the tool width to overlap each tool pass.")
)
self.iso_overlap_entry = FCDoubleSpinner(suffix='%')
self.iso_overlap_entry.set_precision(self.decimals)
self.iso_overlap_entry.setWrapping(True)
self.iso_overlap_entry.set_range(0.0000, 99.9999)
self.iso_overlap_entry.setSingleStep(0.1)
self.iso_overlap_entry.setObjectName("gdb_i_overlap")
self.grid4.addWidget(overlabel, 2, 0)
self.grid4.addWidget(self.iso_overlap_entry, 2, 1)
# Milling Type Radio Button
self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
self.milling_type_label.setToolTip(
_("Milling type when the selected tool is of type: 'iso_op':\n"
"- climb / best for precision milling and to reduce tool usage\n"
"- conventional / useful when there is no backlash compensation")
)
self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
{'label': _('Conventional'), 'value': 'cv'}])
self.milling_type_radio.setToolTip(
_("Milling type when the selected tool is of type: 'iso_op':\n"
"- climb / best for precision milling and to reduce tool usage\n"
"- conventional / useful when there is no backlash compensation")
)
self.milling_type_radio.setObjectName("gdb_i_milling_type")
self.grid4.addWidget(self.milling_type_label, 4, 0)
self.grid4.addWidget(self.milling_type_radio, 4, 1)
# Follow
self.follow_label = QtWidgets.QLabel('%s:' % _('Follow'))
self.follow_label.setToolTip(
_("Generate a 'Follow' geometry.\n"
"This means that it will cut through\n"
"the middle of the trace.")
)
self.follow_cb = FCCheckBox()
self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n"
"This means that it will cut through\n"
"the middle of the trace."))
self.follow_cb.setObjectName("gdb_i_follow")
self.grid4.addWidget(self.follow_label, 6, 0)
self.grid4.addWidget(self.follow_cb, 6, 1)
# Isolation Type
self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type'))
self.iso_type_label.setToolTip(
_("Choose how the isolation will be executed:\n"
"- 'Full' -> complete isolation of polygons\n"
"- 'Ext' -> will isolate only on the outside\n"
"- 'Int' -> will isolate only on the inside\n"
"'Exterior' isolation is almost always possible\n"
"(with the right tool) but 'Interior'\n"
"isolation can be done only when there is an opening\n"
"inside of the polygon (e.g polygon is a 'doughnut' shape).")
)
self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'},
{'label': _('Ext'), 'value': 'ext'},
{'label': _('Int'), 'value': 'int'}])
self.iso_type_radio.setObjectName("gdb_i_iso_type")
self.grid4.addWidget(self.iso_type_label, 8, 0)
self.grid4.addWidget(self.iso_type_radio, 8, 1)
# ####################################################################
# ####################################################################
# GUI for the lower part of the window
@ -1792,19 +1678,12 @@ class ToolsDB2(QtWidgets.QWidget):
)
self.buttons_box.addWidget(self.save_db_btn)
self.add_tool_from_db = FCButton(_("Transfer the Tool"))
self.add_tool_from_db = FCButton(_("Add Tool from Tools DB"))
self.add_tool_from_db.setToolTip(
_("Insert a new tool in the Tools Table of the\n"
"object/application tool after selecting a tool\n"
_("Add a new tool in the Tools Table of the\n"
"active Geometry object after selecting a tool\n"
"in the Tools Database.")
)
self.add_tool_from_db.setStyleSheet("""
QPushButton
{
font-weight: bold;
color: green;
}
""")
self.add_tool_from_db.hide()
self.cancel_tool_from_db = FCButton(_("Cancel"))
@ -1814,7 +1693,7 @@ class ToolsDB2(QtWidgets.QWidget):
tree_layout.addLayout(hlay)
hlay.addWidget(self.add_tool_from_db)
hlay.addWidget(self.cancel_tool_from_db)
# hlay.addStretch()
hlay.addStretch()
# ##############################################################################
# ##############################################################################
@ -1864,13 +1743,6 @@ class ToolsDB2(QtWidgets.QWidget):
"tools_paintmethod": self.paintmethod_combo,
"tools_pathconnect": self.pathconnect_cb,
"tools_paintcontour": self.paintcontour_cb,
# Isolation
"tools_iso_passes": self.passes_entry,
"tools_iso_overlap": self.iso_overlap_entry,
"tools_iso_milling_type": self.milling_type_radio,
"tools_iso_follow": self.follow_cb,
"tools_iso_isotype": self.iso_type_radio
}
self.name2option = {
@ -1915,13 +1787,6 @@ class ToolsDB2(QtWidgets.QWidget):
'gdb_p_method': "tools_paintmethod",
'gdb_p_connect': "tools_pathconnect",
'gdb_p_contour': "tools_paintcontour",
# Isolation
"gdb_i_passes": "tools_iso_passes",
"gdb_i_overlap": "tools_iso_overlap",
"gdb_i_milling_type": "tools_iso_milling_type",
"gdb_i_follow": "tools_iso_follow",
"gdb_i_iso_type": "tools_iso_isotype"
}
self.current_toolid = None
@ -2022,7 +1887,7 @@ class ToolsDB2(QtWidgets.QWidget):
self.blockSignals(False)
def setup_db_ui(self):
filename = self.app.data_path + '\geo_tools_db.FlatDB'
filename = self.app.data_path + '/geo_tools_db.FlatDB'
# load the database tools from the file
try:
@ -2041,7 +1906,7 @@ class ToolsDB2(QtWidgets.QWidget):
self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
return
self.app.inform.emit('[success] %s: %s' % (_("Loaded Tools DB from"), filename))
self.app.inform.emit('[success] %s: %s' % (_("Loaded FlatCAM Tools DB from"), filename))
self.build_db_ui()
@ -2074,23 +1939,21 @@ class ToolsDB2(QtWidgets.QWidget):
if self.db_tool_dict:
self.storage_to_form(self.db_tool_dict['1'])
# Enable AppGUI
# Enable GUI
self.basic_box.setEnabled(True)
self.advanced_box.setEnabled(True)
self.ncc_box.setEnabled(True)
self.paint_box.setEnabled(True)
self.iso_box.setEnabled(True)
self.tree_widget.setCurrentItem(self.tree_widget.topLevelItem(0))
# self.tree_widget.setFocus()
else:
# Disable AppGUI
# Disable GUI
self.basic_box.setEnabled(False)
self.advanced_box.setEnabled(False)
self.ncc_box.setEnabled(False)
self.paint_box.setEnabled(False)
self.iso_box.setEnabled(False)
else:
self.storage_to_form(self.db_tool_dict[str(self.current_toolid)])
@ -2143,27 +2006,10 @@ class ToolsDB2(QtWidgets.QWidget):
"tools_paintmethod": self.app.defaults["tools_paintmethod"],
"tools_pathconnect": self.app.defaults["tools_pathconnect"],
"tools_paintcontour": self.app.defaults["tools_paintcontour"],
# Isolation
"tools_iso_passes": int(self.app.defaults["tools_iso_passes"]),
"tools_iso_overlap": float(self.app.defaults["tools_iso_overlap"]),
"tools_iso_milling_type": self.app.defaults["tools_iso_milling_type"],
"tools_iso_follow": self.app.defaults["tools_iso_follow"],
"tools_iso_isotype": self.app.defaults["tools_iso_isotype"],
})
temp = []
for k, v in self.db_tool_dict.items():
if "new_tool_" in v['name']:
temp.append(float(v['name'].rpartition('_')[2]))
if temp:
new_name = "new_tool_%d" % int(max(temp) + 1)
else:
new_name = "new_tool_1"
dict_elem = {}
dict_elem['name'] = new_name
dict_elem['name'] = 'new_tool'
if type(self.app.defaults["geometry_cnctooldia"]) == float:
dict_elem['tooldia'] = self.app.defaults["geometry_cnctooldia"]
else:
@ -2271,7 +2117,7 @@ class ToolsDB2(QtWidgets.QWidget):
l_save=str(self.app.get_last_save_folder()),
n=_("Tools_Database"),
date=date),
ext_filter=filter__)
filter=filter__)
filename = str(filename)
@ -2340,7 +2186,7 @@ class ToolsDB2(QtWidgets.QWidget):
self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
return
self.app.inform.emit('[success] %s: %s' % (_("Loaded Tools DB from"), filename))
self.app.inform.emit('[success] %s: %s' % (_("Loaded FlatCAM Tools DB from"), filename))
self.build_db_ui()
self.update_storage()
@ -2372,18 +2218,6 @@ class ToolsDB2(QtWidgets.QWidget):
self.app.tools_db_changed_flag = False
self.on_save_tools_db()
def on_calculate_tooldia(self):
if self.shape_combo.get_value() == 'V':
tip_dia = float(self.vdia_entry.get_value())
half_tip_angle = float(self.vangle_entry.get_value()) / 2.0
cut_z = float(self.cutz_entry.get_value())
cut_z = -cut_z if cut_z < 0 else cut_z
# calculated tool diameter so the cut_z parameter is obeyed
tool_dia = tip_dia + (2 * cut_z * math.tan(math.radians(half_tip_angle)))
self.dia_entry.set_value(tool_dia)
def ui_connect(self):
# make sure that we don't make multiple connections to the widgets
self.ui_disconnect()
@ -2413,40 +2247,12 @@ class ToolsDB2(QtWidgets.QWidget):
if isinstance(wdg, FCSpinner) or isinstance(wdg, FCDoubleSpinner):
wdg.valueChanged.connect(self.update_storage)
# connect the calculate tooldia method to the controls
# if the tool shape is 'V' the tool dia will be calculated to obey Cut Z parameter
self.shape_combo.currentIndexChanged.connect(self.on_calculate_tooldia)
self.cutz_entry.valueChanged.connect(self.on_calculate_tooldia)
self.vdia_entry.valueChanged.connect(self.on_calculate_tooldia)
self.vangle_entry.valueChanged.connect(self.on_calculate_tooldia)
def ui_disconnect(self):
try:
self.name_entry.editingFinished.disconnect(self.update_tree_name)
except (TypeError, AttributeError):
pass
try:
self.shape_combo.currentIndexChanged.disconnect(self.on_calculate_tooldia)
except (TypeError, AttributeError):
pass
try:
self.cutz_entry.valueChanged.disconnect(self.on_calculate_tooldia)
except (TypeError, AttributeError):
pass
try:
self.vdia_entry.valueChanged.disconnect(self.on_calculate_tooldia)
except (TypeError, AttributeError):
pass
try:
self.vangle_entry.valueChanged.disconnect(self.on_calculate_tooldia)
except (TypeError, AttributeError):
pass
for key in self.form_fields:
wdg = self.form_fields[key]
@ -2592,18 +2398,6 @@ class ToolsDB2(QtWidgets.QWidget):
elif wdg_name == "gdb_p_contour":
self.db_tool_dict[tool_id]['data']['tools_paintcontour'] = val
# Isolation Tool
elif wdg_name == "gdb_i_passes":
self.db_tool_dict[tool_id]['data']['tools_iso_passes'] = val
elif wdg_name == "gdb_i_overlap":
self.db_tool_dict[tool_id]['data']['tools_iso_overlap'] = val
elif wdg_name == "gdb_i_milling_type":
self.db_tool_dict[tool_id]['data']['tools_iso_milling_type'] = val
elif wdg_name == "gdb_i_follow":
self.db_tool_dict[tool_id]['data']['tools_iso_follow'] = val
elif wdg_name == "gdb_i_iso_type":
self.db_tool_dict[tool_id]['data']['tools_iso_isotype'] = val
self.callback_app()
def on_tool_requested_from_app(self):

View File

@ -19,10 +19,10 @@ log = logging.getLogger('base')
preprocessors = {}
class ABCPreProcRegister(ABCMeta):
class ABCPostProcRegister(ABCMeta):
# handles preprocessors registration on instantiation
def __new__(cls, clsname, bases, attrs):
newclass = super(ABCPreProcRegister, cls).__new__(cls, clsname, bases, attrs)
newclass = super(ABCPostProcRegister, cls).__new__(cls, clsname, bases, attrs)
if object not in bases:
if newclass.__name__ in preprocessors:
log.warning('Preprocessor %s has been overriden' % newclass.__name__)
@ -30,7 +30,7 @@ class ABCPreProcRegister(ABCMeta):
return newclass
class PreProc(object, metaclass=ABCPreProcRegister):
class FlatCAMPostProc(object, metaclass=ABCPostProcRegister):
@abstractmethod
def start_code(self, p):
pass
@ -76,7 +76,7 @@ class PreProc(object, metaclass=ABCPreProcRegister):
pass
class AppPreProcTools(object, metaclass=ABCPreProcRegister):
class FlatCAMPostProc_Tools(object, metaclass=ABCPostProcRegister):
@abstractmethod
def start_code(self, p):
pass

View File

@ -6,12 +6,12 @@
# MIT Licence #
# ##########################################################
from AppGUI.GUIElements import FlatCAMActivityView
from flatcamGUI.FlatCAMGUI import FlatCAMActivityView
from PyQt5 import QtCore
import weakref
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')

View File

@ -6,12 +6,13 @@
# MIT Licence #
# ########################################################## ##
from PyQt5 import QtCore, QtWidgets
from PyQt5 import QtGui, QtCore, QtWidgets, QtWidgets
from PyQt5.QtCore import Qt
from shapely.geometry import Polygon, LineString
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -19,23 +20,22 @@ if '_' not in builtins.__dict__:
_ = gettext.gettext
class AppTool(QtWidgets.QWidget):
class FlatCAMTool(QtWidgets.QWidget):
toolName = "FlatCAM Generic Tool"
def __init__(self, app, parent=None):
"""
:param app: The application this tool will run in.
:type app: App_Main.App
:param parent: Qt Parent
:return: AppTool
:param app: The application this tool will run in.
:type app: App
:param parent: Qt Parent
:return: FlatCAMTool
"""
QtWidgets.QWidget.__init__(self, parent)
self.app = app
self.decimals = self.app.decimals
self.decimals = app.decimals
QtWidgets.QWidget.__init__(self, parent)
# self.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
self.layout = QtWidgets.QVBoxLayout()
@ -87,10 +87,10 @@ class AppTool(QtWidgets.QWidget):
if self.app.tool_tab_locked is True:
return
# Remove anything else in the AppGUI
# Remove anything else in the GUI
self.app.ui.tool_scroll_area.takeWidget()
# Put ourself in the AppGUI
# Put ourself in the GUI
self.app.ui.tool_scroll_area.setWidget(self)
# Switch notebook to tool page
@ -277,25 +277,22 @@ class AppTool(QtWidgets.QWidget):
def confirmation_message(self, accepted, minval, maxval):
if accepted is False:
self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
self.decimals,
minval,
self.decimals,
maxval), False)
self.app.inform.emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' %
(_("Edited value is out of range"), self.decimals, minval, self.decimals, maxval))
else:
self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
self.app.inform.emit('[success] %s' % _("Edited value is within limits."))
def confirmation_message_int(self, accepted, minval, maxval):
if accepted is False:
self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
(_("Edited value is out of range"), minval, maxval), False)
self.app.inform.emit('[WARNING_NOTCL] %s: [%d, %d]' %
(_("Edited value is out of range"), minval, maxval))
else:
self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
self.app.inform.emit('[success] %s' % _("Edited value is within limits."))
def sizeHint(self):
"""
I've overloaded this just in case I will need to make changes in the future to enforce dimensions
:return:
"""
default_hint_size = super(AppTool, self).sizeHint()
default_hint_size = super(FlatCAMTool, self).sizeHint()
return QtCore.QSize(default_hint_size.width(), default_hint_size.height())

View File

@ -106,8 +106,6 @@ def on_language_apply_click(app, restart=False):
(_("Are you sure do you want to change the current language to"), name.capitalize()))
msgbox.setWindowTitle(_("Apply Language ..."))
msgbox.setWindowIcon(QtGui.QIcon(resource_loc + '/language32.png'))
msgbox.setIcon(QtWidgets.QMessageBox.Question)
bt_yes = msgbox.addButton(_("Yes"), QtWidgets.QMessageBox.YesRole)
bt_no = msgbox.addButton(_("No"), QtWidgets.QMessageBox.NoRole)
@ -205,8 +203,6 @@ def restart_program(app, ask=None):
"Do you want to Save the project?"))
msgbox.setWindowTitle(_("Save changes"))
msgbox.setWindowIcon(QtGui.QIcon(resource_loc + '/save_as.png'))
msgbox.setIcon(QtWidgets.QMessageBox.Question)
bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)

View File

@ -1,5 +1,5 @@
from PyQt5 import QtCore
from AppWorker import Worker
from FlatCAMWorker import Worker
import multiprocessing

View File

@ -1,195 +0,0 @@
from PyQt5.QtGui import QPalette
from PyQt5 import QtCore, QtWidgets
import vispy.scene as scene
from vispy.scene.visuals import Rectangle, Text
from vispy.color import Color
import sys
class VisPyCanvas(scene.SceneCanvas):
def __init__(self, config=None):
super().__init__(config=config, keys=None)
self.unfreeze()
# Colors used by the Scene
theme_color = Color('#FFFFFF')
tick_color = Color('#000000')
back_color = str(QPalette().color(QPalette.Window).name())
# Central Widget Colors
self.central_widget.bgcolor = back_color
self.central_widget.border_color = back_color
self.grid_widget = self.central_widget.add_grid(margin=10)
self.grid_widget.spacing = 0
# TOP Padding
top_padding = self.grid_widget.add_widget(row=0, col=0, col_span=2)
top_padding.height_max = 0
# RIGHT Padding
right_padding = self.grid_widget.add_widget(row=0, col=2, row_span=2)
right_padding.width_max = 0
# X Axis
self.xaxis = scene.AxisWidget(
orientation='bottom', axis_color=tick_color, text_color=tick_color,
font_size=8, axis_width=1,
anchors=['center', 'bottom']
)
self.xaxis.height_max = 30
self.grid_widget.add_widget(self.xaxis, row=2, col=1)
# Y Axis
self.yaxis = scene.AxisWidget(
orientation='left', axis_color=tick_color, text_color=tick_color,
font_size=8, axis_width=1
)
self.yaxis.width_max = 55
self.grid_widget.add_widget(self.yaxis, row=1, col=0)
# View & Camera
self.view = self.grid_widget.add_view(row=1, col=1, border_color=tick_color,
bgcolor=theme_color)
self.view.camera = scene.PanZoomCamera(aspect=1, rect=(-25, -25, 150, 150))
self.xaxis.link_view(self.view)
self.yaxis.link_view(self.view)
self.grid = scene.GridLines(parent=self.view.scene, color='dimgray')
self.grid.set_gl_state(depth_test=False)
self.rect = Rectangle(center=(65,30), color=Color('#0000FF10'), border_color=Color('#0000FF10'),
width=120, height=50, radius=[5, 5, 5, 5], parent=self.view)
self.rect.set_gl_state(depth_test=False)
self.text = Text('', parent=self.view, color='black', pos=(5, 30), method='gpu', anchor_x='left')
self.text.font_size = 8
self.text.text = 'Coordinates:\nX: %s\nY: %s' % ('0.0000', '0.0000')
self.freeze()
# self.measure_fps()
class PlotCanvas(QtCore.QObject):
def __init__(self, container, my_app):
"""
The constructor configures the VisPy figure that
will contain all plots, creates the base axes and connects
events to the plotting area.
:param container: The parent container in which to draw plots.
:rtype: PlotCanvas
"""
super().__init__()
# VisPyCanvas instance
self.vispy_canvas = VisPyCanvas()
self.vispy_canvas.unfreeze()
self.my_app = my_app
# Parent container
self.container = container
# <VisPyCanvas>
self.vispy_canvas.create_native()
self.vispy_canvas.native.setParent(self.my_app.ui)
# <QtCore.QObject>
self.container.addWidget(self.vispy_canvas.native)
# add two Infinite Lines to act as markers for the X,Y axis
self.v_line = scene.visuals.InfiniteLine(
pos=0, color=(0.0, 0.0, 1.0, 0.3), vertical=True,
parent=self.vispy_canvas.view.scene)
self.h_line = scene.visuals.InfiniteLine(
pos=0, color=(0.00, 0.0, 1.0, 0.3), vertical=False,
parent=self.vispy_canvas.view.scene)
self.vispy_canvas.freeze()
def event_connect(self, event, callback):
getattr(self.vispy_canvas.events, event).connect(callback)
def event_disconnect(self, event, callback):
getattr(self.vispy_canvas.events, event).disconnect(callback)
def translate_coords(self, pos):
"""
Translate pixels to canvas units.
"""
tr = self.vispy_canvas.grid.get_transform('canvas', 'visual')
return tr.map(pos)
class MyGui(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("VisPy Test")
# add Menubar
self.menu = self.menuBar()
self.menufile = self.menu.addMenu("File")
self.menuedit = self.menu.addMenu("Edit")
self.menufhelp = self.menu.addMenu("Help")
# add a Toolbar
self.file_toolbar = QtWidgets.QToolBar("File Toolbar")
self.addToolBar(self.file_toolbar)
self.button = self.file_toolbar.addAction("Open")
# add Central Widget
self.c_widget = QtWidgets.QWidget()
self.central_layout = QtWidgets.QVBoxLayout()
self.c_widget.setLayout(self.central_layout)
self.setCentralWidget(self.c_widget)
# add InfoBar
# self.infobar = self.statusBar()
# self.position_label = QtWidgets.QLabel("Position: X: 0.0000\tY: 0.0000")
# self.infobar.addWidget(self.position_label)
class MyApp(QtCore.QObject):
def __init__(self):
super().__init__()
self.ui = MyGui()
self.plot = PlotCanvas(container=self.ui.central_layout, my_app=self)
self.ui.show()
self.plot.event_connect(event="mouse_move", callback=self.on_mouse_move)
def on_mouse_move(self, event):
cursor_pos = event.pos
pos_canvas = self.plot.translate_coords(cursor_pos)
# we don't need all the info in the tuple returned by the translate_coords()
# only first 2 elements
pos_canvas = [pos_canvas[0], pos_canvas[1]]
self.ui.position_label.setText("Position: X: %.4f\tY: %.4f" % (pos_canvas[0], pos_canvas[1]))
# pos_text = 'Coordinates: \nX: {:<7.4f}\nY: {:<7.4f}'.format(pos_canvas[0], pos_canvas[1])
pos_text = 'Coordinates: \nX: {:<.4f}\nY: {:<.4f}'.format(pos_canvas[0], pos_canvas[1])
self.plot.vispy_canvas.text.text = pos_text
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
m_app = MyApp()
sys.exit(app.exec_())

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 386 B

After

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 546 B

After

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 B

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 738 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 386 B

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 541 B

After

Width:  |  Height:  |  Size: 700 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 898 B

After

Width:  |  Height:  |  Size: 904 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 901 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 382 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 420 B

After

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 492 B

620
camlib.py

File diff suppressed because it is too large Load Diff

View File

@ -2,16 +2,16 @@ import os
import stat
import sys
from copy import deepcopy
from Common import LoudDict
from FlatCAMCommon import LoudDict
from camlib import to_dict, CNCjob, Geometry
import simplejson
import logging
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
from AppParsers.ParseExcellon import Excellon
from AppParsers.ParseGerber import Gerber
from flatcamParsers.ParseExcellon import Excellon
from flatcamParsers.ParseGerber import Gerber
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
@ -43,7 +43,6 @@ class FlatCAMDefaults:
# General
"global_graphic_engine": '3D',
"global_hud": True,
"global_app_level": 'b',
"global_portable": False,
"global_language": 'English',
@ -172,6 +171,12 @@ class FlatCAMDefaults:
"All Files (*.*)",
# Gerber Options
"gerber_isotooldia": 0.1,
"gerber_isopasses": 1,
"gerber_isooverlap": 10,
"gerber_milling_type": "cl",
"gerber_combine_passes": False,
"gerber_iso_scope": 'all',
"gerber_noncoppermargin": 0.1,
"gerber_noncopperrounded": False,
"gerber_bboxmargin": 0.1,
@ -182,6 +187,11 @@ class FlatCAMDefaults:
"gerber_aperture_scale_factor": 1.0,
"gerber_aperture_buffer_factor": 0.0,
"gerber_follow": False,
"gerber_tool_type": 'circular',
"gerber_vtipdia": 0.1,
"gerber_vtipangle": 30,
"gerber_vcutz": -0.05,
"gerber_iso_type": "full",
"gerber_buffering": "full",
"gerber_simplification": False,
"gerber_simp_tolerance": 0.0005,
@ -212,7 +222,6 @@ class FlatCAMDefaults:
# Excellon General
"excellon_plot": True,
"excellon_solid": True,
"excellon_multicolored": False,
"excellon_format_upper_in": 2,
"excellon_format_lower_in": 4,
"excellon_format_upper_mm": 3,
@ -301,7 +310,6 @@ class FlatCAMDefaults:
# Geometry General
"geometry_plot": True,
"geometry_multicolored": False,
"geometry_circle_steps": 64,
"geometry_cnctooldia": "2.4",
"geometry_plot_line": "#FF0000",
@ -382,28 +390,6 @@ class FlatCAMDefaults:
"cncjob_annotation_fontsize": 9,
"cncjob_annotation_fontcolor": '#990000',
# Isolation Routing Tool
"tools_iso_tooldia": "0.1",
"tools_iso_order": 'rev',
"tools_iso_tool_type": 'C1',
"tools_iso_tool_vtipdia": 0.1,
"tools_iso_tool_vtipangle": 30,
"tools_iso_tool_cutz": -0.05,
"tools_iso_newdia": 0.1,
"tools_iso_passes": 1,
"tools_iso_overlap": 10,
"tools_iso_milling_type": "cl",
"tools_iso_follow": False,
"tools_iso_isotype": "full",
"tools_iso_rest": False,
"tools_iso_combine_passes": False,
"tools_iso_isoexcept": False,
"tools_iso_selection": _("All"),
"tools_iso_area_shape": "square",
"tools_iso_plotting": 'normal',
# NCC Tool
"tools_ncctools": "1.0, 0.5",
"tools_nccorder": 'rev',
@ -418,13 +404,13 @@ class FlatCAMDefaults:
"tools_ncc_offset_value": 0.0000,
"tools_nccref": _('Itself'),
"tools_ncc_area_shape": "square",
"tools_ncc_plotting": 'normal',
"tools_nccmilling_type": 'cl',
"tools_ncctool_type": 'C1',
"tools_ncccutz": -0.05,
"tools_ncctipdia": 0.1,
"tools_ncctipangle": 30,
"tools_nccnewdia": 0.1,
"tools_ncc_plotting": 'normal',
# Cutout Tool
"tools_cutouttooldia": 2.4,
@ -443,7 +429,7 @@ class FlatCAMDefaults:
"tools_paintoverlap": 20,
"tools_paintmargin": 0.0,
"tools_paintmethod": _("Seed"),
"tools_selectmethod": _("All"),
"tools_selectmethod": _("All Polygons"),
"tools_paint_area_shape": "square",
"tools_pathconnect": True,
"tools_paintcontour": True,
@ -538,12 +524,6 @@ class FlatCAMDefaults:
# Distance Tool
"tools_dist_snap_center": False,
# Corner Markers Tool
"tools_corners_thickness": 0.1,
"tools_corners_length": 3.0,
"tools_corners_margin": 0.0,
# ########################################################################################################
# ################################ TOOLS 2 ###############################################################
# ########################################################################################################
@ -697,15 +677,13 @@ class FlatCAMDefaults:
}
@classmethod
def save_factory_defaults(cls, file_path: str, version: float):
def save_factory_defaults(cls, file_path: str):
"""Writes the factory defaults to a file at the given path, overwriting any existing file."""
# Delete any existing factory defaults file
if os.path.isfile(file_path):
os.chmod(file_path, stat.S_IRWXO | stat.S_IWRITE | stat.S_IWGRP)
os.remove(file_path)
cls.factory_defaults['version'] = version
try:
# recreate a new factory defaults file and save the factory defaults data into it
f_f_def_s = open(file_path, "w")
@ -786,8 +764,8 @@ class FlatCAMDefaults:
if defaults is None:
return
# Perform migration if necessary but only if the defaults dict is not empty
if self.__is_old_defaults(defaults) and defaults:
# Perform migration if necessary
if self.__is_old_defaults(defaults):
self.old_defaults_found = True
defaults = self.__migrate_old_defaults(defaults=defaults)
else:

View File

@ -9,9 +9,9 @@ from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import Qt, QSettings
from camlib import distance, arc, FlatCAMRTreeStorage
from AppGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, RadioSet, FCSpinner
from AppEditors.FlatCAMGeoEditor import FCShapeTool, DrawTool, DrawToolShape, DrawToolUtilityShape, FlatCAMGeoEditor
from AppParsers.ParseExcellon import Excellon
from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, RadioSet, FCSpinner
from flatcamEditors.FlatCAMGeoEditor import FCShapeTool, DrawTool, DrawToolShape, DrawToolUtilityShape, FlatCAMGeoEditor
from flatcamParsers.ParseExcellon import Excellon
from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon, Point
import shapely.affinity as affinity
@ -26,7 +26,7 @@ import logging
from copy import deepcopy
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -2123,7 +2123,7 @@ class FlatCAMExcEditor(QtCore.QObject):
else:
self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
else:
from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy
from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_exc_editor')
self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_exc_editor')
@ -2239,7 +2239,7 @@ class FlatCAMExcEditor(QtCore.QObject):
# store the status of the editor so the Delete at object level will not work until the edit is finished
self.editor_active = False
log.debug("Initialization of the Excellon Editor is finished ...")
log.debug("Initialization of the FlatCAM Excellon Editor is finished ...")
def pool_recreated(self, pool):
self.shapes.pool = pool
@ -2312,7 +2312,7 @@ class FlatCAMExcEditor(QtCore.QObject):
tool_dia = float('%.*f' % (self.decimals, v['C']))
self.tool2tooldia[int(k)] = tool_dia
# Init AppGUI
# Init GUI
self.addtool_entry.set_value(float(self.app.defaults['excellon_editor_newdia']))
self.drill_array_size_entry.set_value(int(self.app.defaults['excellon_editor_array_size']))
self.drill_axis_radio.set_value(self.app.defaults['excellon_editor_lin_dir'])
@ -2819,8 +2819,10 @@ class FlatCAMExcEditor(QtCore.QObject):
self.tool_shape.enabled = True
# self.app.app_cursor.enabled = True
self.app.ui.corner_snap_btn.setVisible(True)
self.app.ui.snap_max_dist_entry.setEnabled(True)
self.app.ui.corner_snap_btn.setEnabled(True)
self.app.ui.snap_magnet.setVisible(True)
self.app.ui.corner_snap_btn.setVisible(True)
self.app.ui.exc_editor_menu.setDisabled(False)
self.app.ui.exc_editor_menu.menuAction().setVisible(True)
@ -2830,11 +2832,12 @@ class FlatCAMExcEditor(QtCore.QObject):
self.app.ui.exc_edit_toolbar.setDisabled(False)
self.app.ui.exc_edit_toolbar.setVisible(True)
# self.app.ui.status_toolbar.setDisabled(False)
# self.app.ui.snap_toolbar.setDisabled(False)
# start with GRID toolbar activated
if self.app.ui.grid_snap_btn.isChecked() is False:
self.app.ui.grid_snap_btn.trigger()
self.app.ui.on_grid_snap_triggered(state=True)
self.app.ui.popmenu_disable.setVisible(False)
self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
@ -2866,8 +2869,30 @@ class FlatCAMExcEditor(QtCore.QObject):
self.clear()
self.app.ui.exc_edit_toolbar.setDisabled(True)
self.app.ui.corner_snap_btn.setVisible(False)
self.app.ui.snap_magnet.setVisible(False)
settings = QSettings("Open Source", "FlatCAM")
if settings.contains("layout"):
layout = settings.value('layout', type=str)
if layout == 'standard':
# self.app.ui.exc_edit_toolbar.setVisible(False)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
self.app.ui.snap_magnet.setVisible(False)
self.app.ui.corner_snap_btn.setVisible(False)
else:
# self.app.ui.exc_edit_toolbar.setVisible(True)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
self.app.ui.snap_magnet.setVisible(True)
self.app.ui.corner_snap_btn.setVisible(True)
else:
# self.app.ui.exc_edit_toolbar.setVisible(False)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
self.app.ui.snap_magnet.setVisible(False)
self.app.ui.corner_snap_btn.setVisible(False)
# set the Editor Toolbar visibility to what was before entering in the Editor
self.app.ui.exc_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
@ -3043,7 +3068,7 @@ class FlatCAMExcEditor(QtCore.QObject):
self.set_ui()
# now that we hava data, create the AppGUI interface and add it to the Tool Tab
# now that we hava data, create the GUI interface and add it to the Tool Tab
self.build_ui(first_run=True)
# we activate this after the initial build as we don't need to see the tool been populated
@ -3336,17 +3361,15 @@ class FlatCAMExcEditor(QtCore.QObject):
with self.app.proc_container.new(_("Creating Excellon.")):
try:
edited_obj = self.app.app_obj.new_object("excellon", outname, obj_init)
edited_obj = self.app.new_object("excellon", outname, obj_init)
edited_obj.source_file = self.app.export_excellon(obj_name=edited_obj.options['name'],
local_use=edited_obj,
filename=None,
use_thread=False)
except Exception as e:
self.deactivate()
log.error("Error on Edited object creation: %s" % str(e))
return
self.deactivate()
self.app.inform.emit('[success] %s' % _("Excellon editing finished."))
def on_tool_select(self, tool):
@ -3440,8 +3463,8 @@ class FlatCAMExcEditor(QtCore.QObject):
self.pos = (self.pos[0], self.pos[1])
if event.button == 1:
# self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp; <b>Dy</b>: "
# "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp; <b>Dy</b>: "
"%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
# Selection with left mouse button
if self.active_tool is not None and event.button == 1:
@ -3778,22 +3801,18 @@ class FlatCAMExcEditor(QtCore.QObject):
self.snap_x = x
self.snap_y = y
# update the position label in the infobar since the APP mouse event handlers are disconnected
self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.4f" % (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]
# # update the position label in the infobar since the APP mouse event handlers are disconnected
self.app.ui.position_label.setText("&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.4f&nbsp;" % (x, y))
# # 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&nbsp;&nbsp; <b>Dy</b>: "
# "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
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)
# 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&nbsp;&nbsp; <b>Dy</b>: "
"%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
# ## Utility geometry (animated)
self.update_utility_geometry(data=(x, y))
@ -4026,7 +4045,7 @@ class FlatCAMExcEditor(QtCore.QObject):
def select_tool(self, toolname):
"""
Selects a drawing tool. Impacts the object and AppGUI.
Selects a drawing tool. Impacts the object and GUI.
:param toolname: Name of the tool.
:return: None

View File

@ -15,10 +15,11 @@ from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import Qt, QSettings
from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStorage
from AppTool import AppTool
from AppGUI.GUIElements import OptionalInputSection, FCCheckBox, FCEntry, FCComboBox, FCTextAreaRich, \
FCDoubleSpinner, FCButton, FCInputDialog, FCTree
from AppParsers.ParseFont import *
from FlatCAMTool import FlatCAMTool
from flatcamGUI.ObjectUI import RadioSet
from flatcamGUI.GUIElements import OptionalInputSection, FCCheckBox, FCEntry, FCComboBox, FCTextAreaRich, \
FCTable, FCDoubleSpinner, FCButton, EvalEntry2, FCInputDialog, FCTree
from flatcamParsers.ParseFont import *
from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon
from shapely.ops import cascaded_union, unary_union, linemerge
@ -33,7 +34,7 @@ from rtree import index as rtindex
from copy import deepcopy
# from vispy.io import read_png
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -41,7 +42,7 @@ if '_' not in builtins.__dict__:
_ = gettext.gettext
class BufferSelectionTool(AppTool):
class BufferSelectionTool(FlatCAMTool):
"""
Simple input for buffer distance.
"""
@ -49,7 +50,7 @@ class BufferSelectionTool(AppTool):
toolName = "Buffer Selection"
def __init__(self, app, draw_app):
AppTool.__init__(self, app)
FlatCAMTool.__init__(self, app)
self.draw_app = draw_app
self.decimals = app.decimals
@ -117,12 +118,12 @@ class BufferSelectionTool(AppTool):
self.buffer_int_button.clicked.connect(self.on_buffer_int)
self.buffer_ext_button.clicked.connect(self.on_buffer_ext)
# Init AppGUI
# Init GUI
self.buffer_distance_entry.set_value(0.01)
def run(self):
self.app.defaults.report_usage("Geo Editor ToolBuffer()")
AppTool.run(self)
FlatCAMTool.run(self)
# if the splitter us hidden, display it
if self.app.ui.splitter.sizes()[0] == 0:
@ -186,7 +187,7 @@ class BufferSelectionTool(AppTool):
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
class TextInputTool(AppTool):
class TextInputTool(FlatCAMTool):
"""
Simple input for buffer distance.
"""
@ -194,7 +195,7 @@ class TextInputTool(AppTool):
toolName = "Text Input Tool"
def __init__(self, app):
AppTool.__init__(self, app)
FlatCAMTool.__init__(self, app)
self.app = app
self.text_path = []
@ -339,7 +340,7 @@ class TextInputTool(AppTool):
def run(self):
self.app.defaults.report_usage("Geo Editor TextInputTool()")
AppTool.run(self)
FlatCAMTool.run(self)
# if the splitter us hidden, display it
if self.app.ui.splitter.sizes()[0] == 0:
@ -404,7 +405,7 @@ class TextInputTool(AppTool):
self.app.ui.notebook.setTabText(2, _("Tool"))
class PaintOptionsTool(AppTool):
class PaintOptionsTool(FlatCAMTool):
"""
Inputs to specify how to paint the selected polygons.
"""
@ -412,7 +413,7 @@ class PaintOptionsTool(AppTool):
toolName = "Paint Tool"
def __init__(self, app, fcdraw):
AppTool.__init__(self, app)
FlatCAMTool.__init__(self, app)
self.app = app
self.fcdraw = fcdraw
@ -537,7 +538,7 @@ class PaintOptionsTool(AppTool):
def run(self):
self.app.defaults.report_usage("Geo Editor ToolPaint()")
AppTool.run(self)
FlatCAMTool.run(self)
# if the splitter us hidden, display it
if self.app.ui.splitter.sizes()[0] == 0:
@ -546,7 +547,7 @@ class PaintOptionsTool(AppTool):
self.app.ui.notebook.setTabText(2, _("Paint Tool"))
def set_tool_ui(self):
# Init AppGUI
# Init GUI
if self.app.defaults["tools_painttooldia"]:
self.painttooldia_entry.set_value(self.app.defaults["tools_painttooldia"])
else:
@ -598,7 +599,7 @@ class PaintOptionsTool(AppTool):
self.app.ui.splitter.setSizes([0, 1])
class TransformEditorTool(AppTool):
class TransformEditorTool(FlatCAMTool):
"""
Inputs to specify how to paint the selected polygons.
"""
@ -611,7 +612,7 @@ class TransformEditorTool(AppTool):
offsetName = _("Offset")
def __init__(self, app, draw_app):
AppTool.__init__(self, app)
FlatCAMTool.__init__(self, app)
self.app = app
self.draw_app = draw_app
@ -980,7 +981,7 @@ class TransformEditorTool(AppTool):
def run(self):
self.app.defaults.report_usage("Geo Editor Transform Tool()")
AppTool.run(self)
FlatCAMTool.run(self)
self.set_tool_ui()
# if the splitter us hidden, display it
@ -990,7 +991,7 @@ class TransformEditorTool(AppTool):
self.app.ui.notebook.setTabText(2, _("Transform Tool"))
def install(self, icon=None, separator=None, **kwargs):
AppTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
FlatCAMTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
def set_tool_ui(self):
# Initialize form
@ -3382,7 +3383,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.shapes = self.app.plotcanvas.new_shape_collection(layers=1)
self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
else:
from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy
from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_geo_editor')
self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_geo_editor')
@ -3466,32 +3467,22 @@ class FlatCAMGeoEditor(QtCore.QObject):
:return:
"""
try:
text_value = entry.text()
if ',' in text_value:
text_value = text_value.replace(',', '.')
self.options[opt] = float(text_value)
self.options[opt] = float(entry.text())
except Exception as e:
entry.set_value(self.app.defaults[opt])
log.debug("FlatCAMGeoEditor.__init__().entry2option() --> %s" % str(e))
return
def grid_changed(goption, gentry):
def gridx_changed(goption, gentry):
"""
:param goption: String. Can be either 'global_gridx' or 'global_gridy'
:param gentry: A GUI element which text value is read and used
:param goption: String. Can be either 'global_gridx' or 'global_gridy'
:param gentry: A GUI element which text value is read and used
:return:
"""
if goption not in ['global_gridx', 'global_gridy']:
return
entry2option(opt=goption, entry=gentry)
# if the grid link is checked copy the value in the GridX field to GridY
try:
text_value = gentry.text()
if ',' in text_value:
text_value = text_value.replace(',', '.')
val = float(text_value)
val = float(gentry.get_value())
except ValueError:
return
@ -3500,7 +3491,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.app.ui.grid_gap_x_entry.setValidator(QtGui.QDoubleValidator())
self.app.ui.grid_gap_x_entry.textChanged.connect(
lambda: grid_changed("global_gridx", self.app.ui.grid_gap_x_entry))
lambda: gridx_changed("global_gridx", self.app.ui.grid_gap_x_entry))
self.app.ui.grid_gap_y_entry.setValidator(QtGui.QDoubleValidator())
self.app.ui.grid_gap_y_entry.textChanged.connect(
@ -3551,7 +3542,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
# store the status of the editor so the Delete at object level will not work until the edit is finished
self.editor_active = False
log.debug("Initialization of the Geometry Editor is finished ...")
log.debug("Initialization of the FlatCAM Geometry Editor is finished ...")
def pool_recreated(self, pool):
self.shapes.pool = pool
@ -3568,14 +3559,14 @@ class FlatCAMGeoEditor(QtCore.QObject):
# Remove anything else in the GUI Selected Tab
self.app.ui.selected_scroll_area.takeWidget()
# Put ourselves in the AppGUI Selected Tab
# Put ourselves in the GUI Selected Tab
self.app.ui.selected_scroll_area.setWidget(self.geo_edit_widget)
# Switch notebook to Selected page
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
def build_ui(self):
"""
Build the AppGUI in the Selected Tab for this editor
Build the GUI in the Selected Tab for this editor
:return:
"""
@ -3653,8 +3644,10 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.tool_shape.enabled = True
self.app.app_cursor.enabled = True
self.app.ui.corner_snap_btn.setVisible(True)
self.app.ui.snap_max_dist_entry.setEnabled(True)
self.app.ui.corner_snap_btn.setEnabled(True)
self.app.ui.snap_magnet.setVisible(True)
self.app.ui.corner_snap_btn.setVisible(True)
self.app.ui.geo_editor_menu.setDisabled(False)
self.app.ui.geo_editor_menu.menuAction().setVisible(True)
@ -3665,7 +3658,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.app.ui.geo_edit_toolbar.setDisabled(False)
self.app.ui.geo_edit_toolbar.setVisible(True)
self.app.ui.status_toolbar.setDisabled(False)
self.app.ui.snap_toolbar.setDisabled(False)
self.app.ui.popmenu_disable.setVisible(False)
self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
@ -3682,7 +3675,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.item_selected.connect(self.on_geo_elem_selected)
# ## AppGUI Events
# ## GUI Events
self.tw.itemSelectionChanged.connect(self.on_tree_selection_change)
# self.tw.keyPressed.connect(self.app.ui.keyPressEvent)
# self.tw.customContextMenuRequested.connect(self.on_menu_request)
@ -3710,8 +3703,27 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.app.ui.geo_edit_toolbar.setDisabled(True)
settings = QSettings("Open Source", "FlatCAM")
self.app.ui.corner_snap_btn.setVisible(False)
self.app.ui.snap_magnet.setVisible(False)
if settings.contains("layout"):
layout = settings.value('layout', type=str)
if layout == 'standard':
# self.app.ui.geo_edit_toolbar.setVisible(False)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
self.app.ui.snap_magnet.setVisible(False)
self.app.ui.corner_snap_btn.setVisible(False)
else:
# self.app.ui.geo_edit_toolbar.setVisible(True)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
else:
# self.app.ui.geo_edit_toolbar.setVisible(False)
self.app.ui.snap_magnet.setVisible(False)
self.app.ui.corner_snap_btn.setVisible(False)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
# set the Editor Toolbar visibility to what was before entering in the Editor
self.app.ui.geo_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
@ -3745,7 +3757,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
pass
try:
# ## AppGUI Events
# ## GUI Events
self.tw.itemSelectionChanged.disconnect(self.on_tree_selection_change)
# self.tw.keyPressed.connect(self.app.ui.keyPressEvent)
# self.tw.customContextMenuRequested.connect(self.on_menu_request)
@ -4088,6 +4100,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.ui.on_grid_snap_triggered(state=True)
def on_buffer_tool(self):
buff_tool = BufferSelectionTool(self.app, self)
@ -4135,11 +4148,9 @@ class FlatCAMGeoEditor(QtCore.QObject):
# make sure that the cursor shape is enabled/disabled, too
if self.options['grid_snap'] is True:
self.app.inform[str, bool].emit(_("Grid Snap enabled."), False)
self.app.app_cursor.enabled = True
else:
self.app.app_cursor.enabled = False
self.app.inform[str, bool].emit(_("Grid Snap disabled."), False)
def on_canvas_click(self, event):
"""
@ -4162,8 +4173,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.pos = (self.pos[0], self.pos[1])
if event.button == 1:
# self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp; <b>Dy</b>: "
# "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp; <b>Dy</b>: "
"%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
modifiers = QtWidgets.QApplication.keyboardModifiers()
# If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard
@ -4250,23 +4261,18 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.snap_y = y
self.app.mouse = [x, y]
# update the position label in the infobar since the APP mouse event handlers are disconnected
self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.4f" % (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]
# # update the position label in the infobar since the APP mouse event handlers are disconnected
self.app.ui.position_label.setText("&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.4f&nbsp;" % (x, y))
#
# # 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&nbsp;&nbsp; <b>Dy</b>: "
# "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
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)
# 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&nbsp;&nbsp; <b>Dy</b>: "
"%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
if event.button == 1 and event_is_dragging and isinstance(self.active_tool, FCEraser):
pass
@ -4659,7 +4665,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
def select_tool(self, toolname):
"""
Selects a drawing tool. Impacts the object and AppGUI.
Selects a drawing tool. Impacts the object and GUI.
:param toolname: Name of the tool.
:return: None
@ -4744,8 +4750,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
Transfers the geometry tool shape buffer to the selected geometry
object. The geometry already in the object are removed.
:param fcgeometry: GeometryObject
:return: None
:param fcgeometry: GeometryObject
:return: None
"""
if self.multigeo_tool:
fcgeometry.tools[self.multigeo_tool]['solid_geometry'] = []
@ -4770,8 +4776,6 @@ class FlatCAMGeoEditor(QtCore.QObject):
new_geo = linemerge(new_geo)
fcgeometry.solid_geometry.append(new_geo)
self.deactivate()
def update_options(self, obj):
if self.paint_tooldia:
obj.options['cnctooldia'] = deepcopy(str(self.paint_tooldia))

View File

@ -14,13 +14,15 @@ import shapely.affinity as affinity
from vispy.geometry import Rect
import threading
import time
from copy import copy, deepcopy
import logging
from camlib import distance, arc, three_point_circle
from AppGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, FCSpinner, RadioSet, \
from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, FCSpinner, RadioSet, \
EvalEntry2, FCInputDialog, FCButton, OptionalInputSection, FCCheckBox
from AppTool import AppTool
from FlatCAMTool import FlatCAMTool
import numpy as np
from numpy.linalg import norm as numpy_norm
@ -30,7 +32,7 @@ import math
# import pngcanvas
import traceback
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -1084,6 +1086,15 @@ class FCRegion(FCShapeTool):
self.draw_app.app.inform.emit('[success] %s' % _("Done."))
def clean_up(self):
self.draw_app.selected = []
self.draw_app.apertures_table.clearSelection()
self.draw_app.plot_all()
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
def on_key(self, key):
# Jump to coords
if key == QtCore.Qt.Key_J or key == 'J':
@ -1149,36 +1160,16 @@ class FCRegion(FCShapeTool):
return msg
def clean_up(self):
self.draw_app.selected = []
self.draw_app.apertures_table.clearSelection()
self.draw_app.plot_all()
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
class FCTrack(FCShapeTool):
class FCTrack(FCRegion):
"""
Resulting type: Polygon
"""
def __init__(self, draw_app):
DrawTool.__init__(self, draw_app)
FCRegion.__init__(self, draw_app)
self.name = 'track'
self.draw_app = draw_app
self.steps_per_circle = self.draw_app.app.defaults["gerber_circle_steps"]
size_ap = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['size'])
self.buf_val = (size_ap / 2) if size_ap > 0 else 0.0000001
self.gridx_size = float(self.draw_app.app.ui.grid_gap_x_entry.get_value())
self.gridy_size = float(self.draw_app.app.ui.grid_gap_y_entry.get_value())
self.temp_points = []
self. final_click = False
try:
QtGui.QGuiApplication.restoreOverrideCursor()
except Exception as e:
@ -1192,23 +1183,52 @@ class FCTrack(FCShapeTool):
self.draw_app.app.inform.emit(_('Track Mode 1: 45 degrees ...'))
def make(self):
new_geo_el = {}
if len(self.temp_points) == 1:
new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val,
resolution=int(self.steps_per_circle / 4))
new_geo_el['follow'] = Point(self.temp_points)
else:
new_geo_el['solid'] = (LineString(self.temp_points).buffer(
self.buf_val, resolution=int(self.steps_per_circle / 4))).buffer(0)
new_geo_el['follow'] = LineString(self.temp_points)
self.geometry = DrawToolShape(new_geo_el)
self.draw_app.in_action = False
self.complete = True
self.draw_app.app.jump_signal.disconnect()
self.draw_app.app.inform.emit('[success] %s' % _("Done."))
def clean_up(self):
self.draw_app.selected = []
self.draw_app.apertures_table.clearSelection()
self.draw_app.plot_all()
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
def click(self, point):
self.draw_app.in_action = True
if not self.points:
try:
if point != self.points[-1]:
self.points.append(point)
except IndexError:
self.points.append(point)
elif point != self.points[-1]:
self.points.append(point)
else:
return
new_geo_el = {}
if len(self.temp_points) == 1:
new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle))
new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val,
resolution=int(self.steps_per_circle / 4))
new_geo_el['follow'] = Point(self.temp_points)
else:
new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle))
new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val,
resolution=int(self.steps_per_circle / 4))
new_geo_el['follow'] = LineString(self.temp_points)
self.draw_app.add_gerber_shape(DrawToolShape(new_geo_el),
@ -1221,25 +1241,23 @@ class FCTrack(FCShapeTool):
return ""
def update_grid_info(self):
self.gridx_size = float(self.draw_app.app.ui.grid_gap_x_entry.get_value())
self.gridy_size = float(self.draw_app.app.ui.grid_gap_y_entry.get_value())
def utility_geometry(self, data=None):
self.update_grid_info()
new_geo_el = {}
if not self.points:
new_geo_el['solid'] = Point(data).buffer(self.buf_val, int(self.steps_per_circle))
if len(self.points) == 0:
new_geo_el['solid'] = Point(data).buffer(self.buf_val,
resolution=int(self.steps_per_circle / 4))
return DrawToolUtilityShape(new_geo_el)
else:
elif len(self.points) > 0:
self.temp_points = [self.points[-1]]
old_x = self.points[-1][0]
old_y = self.points[-1][1]
x = data[0]
y = data[1]
self.temp_points = [self.points[-1]]
mx = abs(round((x - old_x) / self.gridx_size))
my = abs(round((y - old_y) / self.gridy_size))
@ -1287,30 +1305,14 @@ class FCTrack(FCShapeTool):
self.temp_points.append(data)
if len(self.temp_points) == 1:
new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle))
new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val,
resolution=int(self.steps_per_circle / 4))
return DrawToolUtilityShape(new_geo_el)
new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle))
new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val,
resolution=int(self.steps_per_circle / 4))
return DrawToolUtilityShape(new_geo_el)
def make(self):
new_geo_el = {}
if len(self.temp_points) == 1:
new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle))
new_geo_el['follow'] = Point(self.temp_points)
else:
new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle))
new_geo_el['solid'] = new_geo_el['solid'].buffer(0) # try to clean the geometry
new_geo_el['follow'] = LineString(self.temp_points)
self.geometry = DrawToolShape(new_geo_el)
self.draw_app.in_action = False
self.complete = True
self.draw_app.app.jump_signal.disconnect()
self.draw_app.app.inform.emit('[success] %s' % _("Done."))
def on_key(self, key):
if key == 'Backspace' or key == QtCore.Qt.Key_Backspace:
if len(self.points) > 0:
@ -1403,15 +1405,6 @@ class FCTrack(FCShapeTool):
return msg
def clean_up(self):
self.draw_app.selected = []
self.draw_app.apertures_table.clearSelection()
self.draw_app.plot_all()
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
class FCDisc(FCShapeTool):
"""
@ -2962,7 +2955,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
# this var will store the state of the toolbar before starting the editor
self.toolbar_old_state = False
# Init AppGUI
# Init GUI
self.apdim_lbl.hide()
self.apdim_entry.hide()
self.gerber_obj = None
@ -2974,7 +2967,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.tool_shape = self.canvas.new_shape_collection(layers=1)
self.ma_annotation = self.canvas.new_text_group()
else:
from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy
from flatcamGUI.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(
@ -3117,7 +3110,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.complete = True
self.set_ui()
log.debug("Initialization of the Gerber Editor is finished ...")
log.debug("Initialization of the FlatCAM Gerber Editor is finished ...")
def pool_recreated(self, pool):
self.shapes.pool = pool
@ -3146,7 +3139,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
tt_aperture = self.sorted_apcode[i]
self.tid2apcode[i + 1] = tt_aperture
# Init AppGUI
# Init GUI
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"])
@ -3435,7 +3428,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
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"))
self.app.inform.emit('[WARNING_NOTCL]%s' % _(" Select an aperture in Aperture Table"))
return
deleted_apcode_list = []
@ -3692,8 +3685,10 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.shapes.enabled = True
self.tool_shape.enabled = True
self.app.ui.corner_snap_btn.setVisible(True)
self.app.ui.snap_max_dist_entry.setEnabled(True)
self.app.ui.corner_snap_btn.setEnabled(True)
self.app.ui.snap_magnet.setVisible(True)
self.app.ui.corner_snap_btn.setVisible(True)
self.app.ui.grb_editor_menu.setDisabled(False)
self.app.ui.grb_editor_menu.menuAction().setVisible(True)
@ -3703,11 +3698,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.app.ui.grb_edit_toolbar.setDisabled(False)
self.app.ui.grb_edit_toolbar.setVisible(True)
# self.app.ui.status_toolbar.setDisabled(False)
# self.app.ui.snap_toolbar.setDisabled(False)
# start with GRID toolbar activated
if self.app.ui.grid_snap_btn.isChecked() is False:
self.app.ui.grid_snap_btn.trigger()
self.app.ui.on_grid_snap_triggered(state=True)
# adjust the visibility of some of the canvas context menu
self.app.ui.popmenu_edit.setVisible(False)
@ -3740,8 +3736,29 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.app.ui.grb_edit_toolbar.setDisabled(True)
settings = QSettings("Open Source", "FlatCAM")
self.app.ui.corner_snap_btn.setVisible(False)
self.app.ui.snap_magnet.setVisible(False)
if settings.contains("layout"):
layout = settings.value('layout', type=str)
if layout == 'standard':
# self.app.ui.exc_edit_toolbar.setVisible(False)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
self.app.ui.snap_magnet.setVisible(False)
self.app.ui.corner_snap_btn.setVisible(False)
else:
# self.app.ui.exc_edit_toolbar.setVisible(True)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
self.app.ui.snap_magnet.setVisible(True)
self.app.ui.corner_snap_btn.setVisible(True)
else:
# self.app.ui.exc_edit_toolbar.setVisible(False)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
self.app.ui.snap_magnet.setVisible(False)
self.app.ui.corner_snap_btn.setVisible(False)
# 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 \
@ -4193,7 +4210,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
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 AppGUI"))
self.app.inform.emit('[success] %s.' % _("Adding geometry finished. Preparing the GUI"))
self.set_ui()
self.build_ui(first_run=True)
self.plot_all()
@ -4228,7 +4245,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
new_grb_name = self.edited_obj_name + "_edit"
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)
@staticmethod
def update_options(obj):
@ -4250,10 +4266,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
"""
Creates a new Gerber object for the edited Gerber. Thread-safe.
: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
: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
:return: None
"""
@ -4345,27 +4360,26 @@ class FlatCAMGrbEditor(QtCore.QObject):
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 = '[ERROR] %s' % \
_("An internal error has occurred. See shell.\n")
msg += traceback.format_exc()
app_obj.inform.emit(msg)
raise
grb_obj.source_file = self.app.export_gerber(obj_name=out_name, filename=None,
local_use=grb_obj, use_thread=False)
with self.app.proc_container.new(_("Creating Gerber.")):
try:
self.app.app_obj.new_object("gerber", outname, obj_init)
self.app.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
self.app.inform.emit('[success] %s' % _("Done. Gerber editing finished."))
# make sure to clean the previous results
self.results = []
self.deactivate_grb_editor()
self.app.inform.emit('[success] %s' % _("Done. Gerber editing finished."))
def on_tool_select(self, tool):
"""
@ -4523,8 +4537,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.pos = (self.pos[0], self.pos[1])
if event.button == 1:
# self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp; <b>Dy</b>: "
# "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp; <b>Dy</b>: "
"%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
# Selection with left mouse button
if self.active_tool is not None:
@ -4536,7 +4550,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
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."))
self.app.inform.emit('[success] %s' %
_("Coordinates copied to clipboard."))
return
# Dispatch event to active_tool
@ -4547,7 +4562,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
if self.current_storage is not None:
self.on_grb_shape_complete(self.current_storage)
self.build_ui()
# MS: always return to the Select Tool if modifier key is not pressed
# else return to the current tool
key_modifier = QtWidgets.QApplication.keyboardModifiers()
@ -4555,7 +4569,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
modifier_to_use = Qt.ControlModifier
else:
modifier_to_use = Qt.ShiftModifier
# 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:
@ -4616,14 +4629,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
# 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()
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."))
self.app.inform.emit('[success] %s' %
_("Done."))
# MS: always return to the Select Tool if modifier key is not pressed
# else return to the current tool but not for FCTrack
@ -4763,23 +4774,18 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.app.mouse = [x, y]
# update the position label in the infobar since the APP mouse event handlers are disconnected
self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.4f" % (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]
# # update the position label in the infobar since the APP mouse event handlers are disconnected
self.app.ui.position_label.setText("&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.4f&nbsp;" % (x, y))
#
# # 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&nbsp;&nbsp; <b>Dy</b>: "
# "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
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)
# 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&nbsp;&nbsp; <b>Dy</b>: "
"%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
self.update_utility_geometry(data=(x, y))
@ -5026,7 +5032,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
def select_tool(self, toolname):
"""
Selects a drawing tool. Impacts the object and AppGUI.
Selects a drawing tool. Impacts the object and GUI.
:param toolname: Name of the tool.
:return: None
@ -5292,7 +5298,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
class TransformEditorTool(AppTool):
class TransformEditorTool(FlatCAMTool):
"""
Inputs to specify how to paint the selected polygons.
"""
@ -5305,7 +5311,7 @@ class TransformEditorTool(AppTool):
offsetName = _("Offset")
def __init__(self, app, draw_app):
AppTool.__init__(self, app)
FlatCAMTool.__init__(self, app)
self.app = app
self.draw_app = draw_app
@ -5691,13 +5697,13 @@ class TransformEditorTool(AppTool):
except AttributeError:
pass
AppTool.run(self)
FlatCAMTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, _("Transform Tool"))
def install(self, icon=None, separator=None, **kwargs):
AppTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
FlatCAMTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
def set_tool_ui(self):
# Initialize form

View File

@ -5,7 +5,7 @@
# MIT Licence #
# ##########################################################
from AppGUI.GUIElements import FCFileSaveDialog, FCEntry, FCTextAreaExtended, FCTextAreaLineNumber
from flatcamGUI.GUIElements import FCFileSaveDialog, FCEntry, FCTextAreaExtended, FCTextAreaLineNumber
from PyQt5 import QtPrintSupport, QtWidgets, QtCore, QtGui
from reportlab.platypus import SimpleDocTemplate, Paragraph
@ -15,7 +15,7 @@ from reportlab.lib.units import inch, mm
# from io import StringIO
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -214,10 +214,10 @@ class TextEditor(QtWidgets.QWidget):
filename = str(FCFileSaveDialog.get_saved_filename(
caption=_("Export Code ..."),
directory=self.app.defaults["global_last_folder"] + '/' + str(obj_name),
ext_filter=_filter_
filter=_filter_
)[0])
except TypeError:
filename = str(FCFileSaveDialog.get_saved_filename(caption=_("Export Code ..."), ext_filter=_filter_)[0])
filename = str(FCFileSaveDialog.get_saved_filename(caption=_("Export Code ..."), filter=_filter_)[0])
if filename == "":
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled."))

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@ import html
import sys
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
log = logging.getLogger('base')
@ -570,13 +570,9 @@ class FCEntry3(FCEntry):
class EvalEntry(QtWidgets.QLineEdit):
def __init__(self, border_color=None, parent=None):
def __init__(self, parent=None):
super(EvalEntry, self).__init__(parent)
self.readyToEdit = True
if border_color:
self.setStyleSheet("QLineEdit {border: 1px solid %s;}" % border_color)
self.editingFinished.connect(self.on_edit_finished)
def on_edit_finished(self):
@ -603,6 +599,7 @@ class EvalEntry(QtWidgets.QLineEdit):
def get_value(self):
raw = str(self.text()).strip(' ')
evaled = 0.0
try:
evaled = eval(raw)
except Exception as e:
@ -642,7 +639,7 @@ class EvalEntry2(QtWidgets.QLineEdit):
def get_value(self):
raw = str(self.text()).strip(' ')
evaled = 0.0
try:
evaled = eval(raw)
except Exception as e:
@ -659,132 +656,6 @@ class EvalEntry2(QtWidgets.QLineEdit):
return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
class NumericalEvalEntry(EvalEntry):
"""
Will evaluate the input and return a value. Accepts only float numbers and formulas using the operators: /,*,+,-,%
"""
def __init__(self, border_color=None):
super().__init__(border_color=border_color)
regex = QtCore.QRegExp("[0-9\/\*\+\-\%\.\s]*")
validator = QtGui.QRegExpValidator(regex, self)
self.setValidator(validator)
class NumericalEvalTupleEntry(FCEntry):
"""
Will evaluate the input and return a value. Accepts only float numbers and formulas using the operators: /,*,+,-,%
"""
def __init__(self, border_color=None):
super().__init__(border_color=border_color)
regex = QtCore.QRegExp("[0-9\/\*\+\-\%\.\s\,]*")
validator = QtGui.QRegExpValidator(regex, self)
self.setValidator(validator)
class FCColorEntry(QtWidgets.QFrame):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.entry = FCEntry()
regex = QtCore.QRegExp("[#A-Fa-f0-9]*")
validator = QtGui.QRegExpValidator(regex, self.entry)
self.entry.setValidator(validator)
self.button = QtWidgets.QPushButton()
self.button.setFixedSize(15, 15)
self.button.setStyleSheet("border-color: dimgray;")
self.layout = QtWidgets.QHBoxLayout()
self.layout.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.addWidget(self.entry)
self.layout.addWidget(self.button)
self.setLayout(self.layout)
self.entry.editingFinished.connect(self._sync_button_color)
self.button.clicked.connect(self._on_button_clicked)
self.editingFinished = self.entry.editingFinished
def get_value(self) -> str:
return self.entry.get_value()
def set_value(self, value: str):
self.entry.set_value(value)
self._sync_button_color()
def _sync_button_color(self):
value = self.get_value()
self.button.setStyleSheet("background-color:%s;" % self._extract_color(value))
def _on_button_clicked(self):
value = self.entry.get_value()
current_color = QtGui.QColor(self._extract_color(value))
color_dialog = QtWidgets.QColorDialog()
selected_color = color_dialog.getColor(initial=current_color, options=QtWidgets.QColorDialog.ShowAlphaChannel)
if selected_color.isValid() is False:
return
new_value = str(selected_color.name()) + self._extract_alpha(value)
self.set_value(new_value)
def _extract_color(self, value: str) -> str:
return value[:7]
def _extract_alpha(self, value: str) -> str:
return value[7:9]
class FCSliderWithSpinner(QtWidgets.QFrame):
def __init__(self, min=0, max=100, step=1, **kwargs):
super().__init__(**kwargs)
self.slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.slider.setMinimum(min)
self.slider.setMaximum(max)
self.slider.setSingleStep(step)
self.spinner = FCSpinner()
self.spinner.set_range(min, max)
self.spinner.set_step(step)
self.spinner.setMinimumWidth(70)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
self.spinner.setSizePolicy(sizePolicy)
self.layout = QtWidgets.QHBoxLayout()
self.layout.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.addWidget(self.slider)
self.layout.addWidget(self.spinner)
self.setLayout(self.layout)
self.slider.valueChanged.connect(self._on_slider)
self.spinner.valueChanged.connect(self._on_spinner)
self.valueChanged = self.spinner.valueChanged
def get_value(self) -> int:
return self.spinner.get_value()
def set_value(self, value: int):
self.spinner.set_value(value)
def _on_spinner(self):
spinner_value = self.spinner.value()
self.slider.setValue(spinner_value)
def _on_slider(self):
slider_value = self.slider.value()
self.spinner.set_value(slider_value)
class FCSpinner(QtWidgets.QSpinBox):
returnPressed = QtCore.pyqtSignal()
@ -813,8 +684,6 @@ class FCSpinner(QtWidgets.QSpinBox):
self.setAlignment(align_val)
self.prev_readyToEdit = True
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Preferred)
self.setSizePolicy(sizePolicy)
def eventFilter(self, object, event):
if event.type() == QtCore.QEvent.MouseButtonPress and self.prev_readyToEdit is True:
@ -947,8 +816,6 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox):
self.setAlignment(align_val)
self.prev_readyToEdit = True
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Preferred)
self.setSizePolicy(sizePolicy)
def on_edit_finished(self):
self.clearFocus()
@ -2231,24 +2098,6 @@ class FCDetachableTab2(FCDetachableTab):
def __init__(self, protect=None, protect_by_name=None, parent=None):
super(FCDetachableTab2, self).__init__(protect=protect, protect_by_name=protect_by_name, parent=parent)
try:
self.tabBar.onCloseTabSignal.disconnect()
except TypeError:
pass
self.tabBar.onCloseTabSignal.connect(self.on_closetab_middle_button)
def on_closetab_middle_button(self, current_index):
"""
:param current_index:
:return:
"""
# if tab is protected don't delete it
if self.tabBar.tabButton(current_index, QtWidgets.QTabBar.RightSide) is not None:
self.closeTab(current_index)
def closeTab(self, currentIndex):
"""
Slot connected to the tabCloseRequested signal
@ -2347,14 +2196,11 @@ class OptionalHideInputSection:
"""
Associates the a checkbox with a set of inputs.
:param cb: Checkbox that enables the optional inputs.
:type cb: QtWidgets.QCheckBox
:param optinputs: List of widgets that are optional.
:type optinputs: list
:param logic: When True the logic is normal, when False the logic is in reverse
It means that for logic=True, when the checkbox is checked the widgets are Enabled, and
for logic=False, when the checkbox is checked the widgets are Disabled
:type logic: bool
:param cb: Checkbox that enables the optional inputs.
:param optinputs: List of widgets that are optional.
:param logic: When True the logic is normal, when False the logic is in reverse
It means that for logic=True, when the checkbox is checked the widgets are Enabled, and
for logic=False, when the checkbox is checked the widgets are Disabled
:return:
"""
assert isinstance(cb, FCCheckBox), \
@ -2625,8 +2471,7 @@ class SpinBoxDelegate(QtWidgets.QItemDelegate):
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
@staticmethod
def setDecimals(spinbox, digits):
def setDecimals(self, spinbox, digits):
spinbox.setDecimals(digits)
@ -2674,7 +2519,7 @@ class DialogBoxRadio(QtWidgets.QDialog):
:param title: string with the window title
:param label: string with the message inside the dialog box
"""
super(DialogBoxRadio, self).__init__(parent=parent)
super(DialogBoxRadio, self).__init__()
if initial_text is None:
self.location = str((0, 0))
else:
@ -2917,12 +2762,9 @@ class MyCompleter(QCompleter):
insertText = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
QCompleter.__init__(self, parent=parent)
QCompleter.__init__(self)
self.setCompletionMode(QCompleter.PopupCompletion)
self.highlighted.connect(self.setHighlighted)
self.lastSelected = ''
# self.popup().installEventFilter(self)
# def eventFilter(self, obj, event):
@ -3078,9 +2920,9 @@ class FCFileSaveDialog(QtWidgets.QFileDialog):
super(FCFileSaveDialog, self).__init__(*args)
@staticmethod
def get_saved_filename(parent=None, caption='', directory='', ext_filter='', initialFilter=''):
def get_saved_filename(parent=None, caption='', directory='', filter='', initialFilter=''):
filename, _filter = QtWidgets.QFileDialog.getSaveFileName(parent=parent, caption=caption,
directory=directory, filter=ext_filter,
directory=directory, filter=filter,
initialFilter=initialFilter)
filename = str(filename)
@ -3096,225 +2938,6 @@ class FCFileSaveDialog(QtWidgets.QFileDialog):
return filename, _filter
class FCDock(QtWidgets.QDockWidget):
def __init__(self, *args, **kwargs):
super(FCDock, self).__init__(*args)
self.close_callback = kwargs["close_callback"] if "close_callback" in kwargs else None
def closeEvent(self, event: QtGui.QCloseEvent) -> None:
self.close_callback()
super().closeEvent(event)
def show(self) -> None:
if self.isFloating():
self.setFloating(False)
super().show()
class FlatCAMActivityView(QtWidgets.QWidget):
"""
This class create and control the activity icon displayed in the App status bar
"""
def __init__(self, app, parent=None):
super().__init__(parent=parent)
self.app = app
if self.app.defaults["global_activity_icon"] == "Ball green":
icon = self.app.resource_location + '/active_2_static.png'
movie = self.app.resource_location + "/active_2.gif"
elif self.app.defaults["global_activity_icon"] == "Ball black":
icon = self.app.resource_location + '/active_static.png'
movie = self.app.resource_location + "/active.gif"
elif self.app.defaults["global_activity_icon"] == "Arrow green":
icon = self.app.resource_location + '/active_3_static.png'
movie = self.app.resource_location + "/active_3.gif"
elif self.app.defaults["global_activity_icon"] == "Eclipse green":
icon = self.app.resource_location + '/active_4_static.png'
movie = self.app.resource_location + "/active_4.gif"
else:
icon = self.app.resource_location + '/active_static.png'
movie = self.app.resource_location + "/active.gif"
self.setMinimumWidth(200)
self.movie_path = movie
self.icon_path = icon
self.icon = FCLabel(self)
self.icon.setGeometry(0, 0, 16, 12)
self.movie = QtGui.QMovie(self.movie_path)
self.icon.setMovie(self.movie)
# self.movie.start()
layout = QtWidgets.QHBoxLayout()
layout.setContentsMargins(5, 0, 5, 0)
layout.setAlignment(QtCore.Qt.AlignLeft)
self.setLayout(layout)
layout.addWidget(self.icon)
self.text = QtWidgets.QLabel(self)
self.text.setText(_("Idle."))
self.icon.setPixmap(QtGui.QPixmap(self.icon_path))
layout.addWidget(self.text)
self.icon.clicked.connect(self.app.on_toolbar_replot)
def set_idle(self):
self.movie.stop()
self.text.setText(_("Idle."))
def set_busy(self, msg, no_movie=None):
if no_movie is not True:
self.icon.setMovie(self.movie)
self.movie.start()
self.text.setText(msg)
class FlatCAMInfoBar(QtWidgets.QWidget):
"""
This class create a place to display the App messages in the Status Bar
"""
def __init__(self, parent=None, app=None):
super(FlatCAMInfoBar, self).__init__(parent=parent)
self.app = app
self.icon = QtWidgets.QLabel(self)
self.icon.setGeometry(0, 0, 12, 12)
self.pmap = QtGui.QPixmap(self.app.resource_location + '/graylight12.png')
self.icon.setPixmap(self.pmap)
self.lock_pmaps = False
layout = QtWidgets.QHBoxLayout()
layout.setContentsMargins(5, 0, 5, 0)
self.setLayout(layout)
layout.addWidget(self.icon)
self.text = QtWidgets.QLabel(self)
self.text.setText(_("Application started ..."))
self.text.setToolTip(_("Hello!"))
layout.addWidget(self.text)
layout.addStretch()
def set_text_(self, text, color=None):
self.text.setText(text)
self.text.setToolTip(text)
if color:
self.text.setStyleSheet('color: %s' % str(color))
def set_status(self, text, level="info"):
level = str(level)
if self.lock_pmaps is not True:
self.pmap.fill()
if level == "ERROR" or level == "ERROR_NOTCL":
self.pmap = QtGui.QPixmap(self.app.resource_location + '/redlight12.png')
elif level.lower() == "success":
self.pmap = QtGui.QPixmap(self.app.resource_location + '/greenlight12.png')
elif level == "WARNING" or level == "WARNING_NOTCL":
self.pmap = QtGui.QPixmap(self.app.resource_location + '/yellowlight12.png')
elif level.lower() == "selected":
self.pmap = QtGui.QPixmap(self.app.resource_location + '/bluelight12.png')
else:
self.pmap = QtGui.QPixmap(self.app.resource_location + '/graylight12.png')
try:
self.set_text_(text)
self.icon.setPixmap(self.pmap)
except Exception as e:
log.debug("FlatCAMInfoBar.set_status() --> %s" % str(e))
class FlatCAMSystemTray(QtWidgets.QSystemTrayIcon):
"""
This class create the Sys Tray icon for the app
"""
def __init__(self, app, icon, headless=None, parent=None):
# QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
super().__init__(icon, parent=parent)
self.app = app
menu = QtWidgets.QMenu(parent)
menu_runscript = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/script14.png'),
'%s' % _('Run Script ...'), self)
menu_runscript.setToolTip(
_("Will run the opened Tcl Script thus\n"
"enabling the automation of certain\n"
"functions of FlatCAM.")
)
menu.addAction(menu_runscript)
menu.addSeparator()
if headless is None:
self.menu_open = menu.addMenu(QtGui.QIcon(self.app.resource_location + '/folder32_bis.png'), _('Open'))
# Open Project ...
menu_openproject = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/folder16.png'),
_('Open Project ...'), self)
self.menu_open.addAction(menu_openproject)
self.menu_open.addSeparator()
# Open Gerber ...
menu_opengerber = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/flatcam_icon24.png'),
_('Open &Gerber ...\tCtrl+G'), self)
self.menu_open.addAction(menu_opengerber)
# Open Excellon ...
menu_openexcellon = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/open_excellon32.png'),
_('Open &Excellon ...\tCtrl+E'), self)
self.menu_open.addAction(menu_openexcellon)
# Open G-Code ...
menu_opengcode = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/code.png'),
_('Open G-&Code ...'), self)
self.menu_open.addAction(menu_opengcode)
self.menu_open.addSeparator()
menu_openproject.triggered.connect(self.app.on_file_openproject)
menu_opengerber.triggered.connect(self.app.on_fileopengerber)
menu_openexcellon.triggered.connect(self.app.on_fileopenexcellon)
menu_opengcode.triggered.connect(self.app.on_fileopengcode)
exitAction = menu.addAction(_("Exit"))
exitAction.setIcon(QtGui.QIcon(self.app.resource_location + '/power16.png'))
self.setContextMenu(menu)
menu_runscript.triggered.connect(lambda: self.app.on_filerunscript(
silent=True if self.app.cmd_line_headless == 1 else False))
exitAction.triggered.connect(self.app.final_save)
def message_dialog(title, message, kind="info", parent=None):
"""
Builds and show a custom QMessageBox to be used in FlatCAM.
:param title: title of the QMessageBox
:param message: message to be displayed
:param kind: type of QMessageBox; will display a specific icon.
:param parent: parent
:return: None
"""
icon = {"info": QtWidgets.QMessageBox.Information,
"warning": QtWidgets.QMessageBox.Warning,
"error": QtWidgets.QMessageBox.Critical}[str(kind)]
dlg = QtWidgets.QMessageBox(icon, title, message, parent=parent)
dlg.setText(message)
dlg.exec_()
def rreplace(s, old, new, occurrence):
"""
Credits go here:

View File

@ -11,11 +11,11 @@
# Date: 3/10/2019 #
# ##########################################################
from AppGUI.GUIElements import *
from flatcamGUI.GUIElements import *
import sys
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -35,7 +35,7 @@ class ObjectUI(QtWidgets.QWidget):
put UI elements in ObjectUI.custom_box (QtWidgets.QLayout).
"""
def __init__(self, app, icon_file='assets/resources/flatcam_icon32.png', title=_('App Object'),
def __init__(self, app, icon_file='assets/resources/flatcam_icon32.png', title=_('FlatCAM Object'),
parent=None, common=True):
QtWidgets.QWidget.__init__(self, parent=parent)
@ -114,7 +114,7 @@ class ObjectUI(QtWidgets.QWidget):
self.common_grid.addWidget(self.transform_label, 2, 0, 1, 2)
# ### Scale ####
self.scale_entry = NumericalEvalEntry(border_color='#0069A9')
self.scale_entry = FCEntry()
self.scale_entry.set_value(1.0)
self.scale_entry.setToolTip(
_("Factor by which to multiply\n"
@ -132,7 +132,7 @@ class ObjectUI(QtWidgets.QWidget):
self.common_grid.addWidget(self.scale_button, 3, 1)
# ### Offset ####
self.offsetvector_entry = NumericalEvalTupleEntry(border_color='#0069A9')
self.offsetvector_entry = EvalEntry2()
self.offsetvector_entry.setText("(0.0, 0.0)")
self.offsetvector_entry.setToolTip(
_("Amount by which to move the object\n"
@ -149,30 +149,21 @@ class ObjectUI(QtWidgets.QWidget):
self.common_grid.addWidget(self.offsetvector_entry, 4, 0)
self.common_grid.addWidget(self.offset_button, 4, 1)
self.transformations_button = QtWidgets.QPushButton(_('Transformations'))
self.transformations_button.setToolTip(
_("Geometrical transformations of the current object.")
)
self.common_grid.addWidget(self.transformations_button, 5, 0, 1, 2)
layout.addStretch()
def confirmation_message(self, accepted, minval, maxval):
if accepted is False:
self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
self.decimals,
minval,
self.decimals,
maxval), False)
self.app.inform.emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' %
(_("Edited value is out of range"), self.decimals, minval, self.decimals, maxval))
else:
self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
self.app.inform.emit('[success] %s' % _("Edited value is within limits."))
def confirmation_message_int(self, accepted, minval, maxval):
if accepted is False:
self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
(_("Edited value is out of range"), minval, maxval), False)
self.app.inform.emit('[WARNING_NOTCL] %s: [%d, %d]' %
(_("Edited value is out of range"), minval, maxval))
else:
self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
self.app.inform.emit('[success] %s' % _("Edited value is within limits."))
class GerberObjectUI(ObjectUI):
@ -214,37 +205,27 @@ class GerberObjectUI(ObjectUI):
self.multicolored_cb.setMinimumWidth(55)
grid0.addWidget(self.multicolored_cb, 0, 2)
# Plot CB
self.plot_cb = FCCheckBox('%s' % _("Plot"))
# self.plot_cb.setLayoutDirection(QtCore.Qt.RightToLeft)
self.plot_cb.setToolTip(_("Plot (show) this object."))
grid0.addWidget(self.plot_cb, 1, 0, 1, 2)
# ## Object name
self.name_hlay = QtWidgets.QHBoxLayout()
grid0.addLayout(self.name_hlay, 1, 0, 1, 3)
self.custom_box.addLayout(self.name_hlay)
name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
self.name_entry = FCEntry()
self.name_entry.setFocusPolicy(QtCore.Qt.StrongFocus)
self.name_hlay.addWidget(name_label)
self.name_hlay.addWidget(self.name_entry)
# Plot CB
self.plot_lbl = FCLabel('%s:' % _("Plot"))
self.plot_lbl.setToolTip(_("Plot (show) this object."))
self.plot_cb = FCCheckBox()
grid0.addWidget(self.plot_lbl, 2, 0)
grid0.addWidget(self.plot_cb, 2, 1)
# generate follow
self.follow_cb = FCCheckBox('%s' % _("Follow"))
self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n"
"This means that it will cut through\n"
"the middle of the trace."))
self.follow_cb.setMinimumWidth(55)
grid0.addWidget(self.follow_cb, 2, 2)
hlay_plot = QtWidgets.QHBoxLayout()
self.custom_box.addLayout(hlay_plot)
# ### Gerber Apertures ####
self.apertures_table_label = QtWidgets.QLabel('%s:' % _('Apertures'))
self.apertures_table_label = QtWidgets.QLabel('<b>%s:</b>' % _('Apertures'))
self.apertures_table_label.setToolTip(
_("Apertures Table for the Gerber Object.")
)
@ -301,7 +282,254 @@ class GerberObjectUI(ObjectUI):
# start with apertures table hidden
self.apertures_table.setVisible(False)
# Buffer Geometry
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.custom_box.addWidget(separator_line)
# Isolation Routing
self.isolation_routing_label = QtWidgets.QLabel("<b>%s</b>" % _("Isolation Routing"))
self.isolation_routing_label.setToolTip(
_("Create a Geometry object with\n"
"toolpaths to cut outside polygons.")
)
self.custom_box.addWidget(self.isolation_routing_label)
# ###########################################
# ########## NEW GRID #######################
# ###########################################
grid1 = QtWidgets.QGridLayout()
self.custom_box.addLayout(grid1)
grid1.setColumnStretch(0, 0)
grid1.setColumnStretch(1, 1)
grid1.setColumnStretch(2, 1)
# Tool Type
self.tool_type_label = QtWidgets.QLabel('%s:' % _('Tool Type'))
self.tool_type_label.setToolTip(
_("Choose which tool to use for Gerber isolation:\n"
"'Circular' or 'V-shape'.\n"
"When the 'V-shape' is selected then the tool\n"
"diameter will depend on the chosen cut depth.")
)
self.tool_type_radio = RadioSet([{'label': _('Circular'), 'value': 'circular'},
{'label': _('V-Shape'), 'value': 'v'}])
grid1.addWidget(self.tool_type_label, 0, 0)
grid1.addWidget(self.tool_type_radio, 0, 1, 1, 2)
# Tip Dia
self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
self.tipdialabel.setToolTip(
_("The tip diameter for V-Shape Tool")
)
self.tipdia_spinner = FCDoubleSpinner(callback=self.confirmation_message)
self.tipdia_spinner.set_range(-99.9999, 99.9999)
self.tipdia_spinner.set_precision(self.decimals)
self.tipdia_spinner.setSingleStep(0.1)
self.tipdia_spinner.setWrapping(True)
grid1.addWidget(self.tipdialabel, 1, 0)
grid1.addWidget(self.tipdia_spinner, 1, 1, 1, 2)
# Tip Angle
self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
self.tipanglelabel.setToolTip(
_("The tip angle for V-Shape Tool.\n"
"In degree.")
)
self.tipangle_spinner = FCDoubleSpinner(callback=self.confirmation_message)
self.tipangle_spinner.set_range(1, 180)
self.tipangle_spinner.set_precision(self.decimals)
self.tipangle_spinner.setSingleStep(5)
self.tipangle_spinner.setWrapping(True)
grid1.addWidget(self.tipanglelabel, 2, 0)
grid1.addWidget(self.tipangle_spinner, 2, 1, 1, 2)
# Cut Z
self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
self.cutzlabel.setToolTip(
_("Cutting depth (negative)\n"
"below the copper surface.")
)
self.cutz_spinner = FCDoubleSpinner(callback=self.confirmation_message)
self.cutz_spinner.set_range(-9999.9999, 0.0000)
self.cutz_spinner.set_precision(self.decimals)
self.cutz_spinner.setSingleStep(0.1)
self.cutz_spinner.setWrapping(True)
grid1.addWidget(self.cutzlabel, 3, 0)
grid1.addWidget(self.cutz_spinner, 3, 1, 1, 2)
# Tool diameter
tdlabel = QtWidgets.QLabel('%s:' % _('Tool dia'))
tdlabel.setToolTip(
_("Diameter of the cutting tool.\n"
"If you want to have an isolation path\n"
"inside the actual shape of the Gerber\n"
"feature, use a negative value for\n"
"this parameter.")
)
tdlabel.setMinimumWidth(90)
self.iso_tool_dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.iso_tool_dia_entry.set_range(-9999.9999, 9999.9999)
self.iso_tool_dia_entry.set_precision(self.decimals)
self.iso_tool_dia_entry.setSingleStep(0.1)
grid1.addWidget(tdlabel, 4, 0)
grid1.addWidget(self.iso_tool_dia_entry, 4, 1, 1, 2)
# Number of Passes
passlabel = QtWidgets.QLabel('%s:' % _('# Passes'))
passlabel.setToolTip(
_("Width of the isolation gap in\n"
"number (integer) of tool widths.")
)
passlabel.setMinimumWidth(90)
self.iso_width_entry = FCSpinner(callback=self.confirmation_message_int)
self.iso_width_entry.set_range(1, 999)
grid1.addWidget(passlabel, 5, 0)
grid1.addWidget(self.iso_width_entry, 5, 1, 1, 2)
# Pass overlap
overlabel = QtWidgets.QLabel('%s:' % _('Pass overlap'))
overlabel.setToolTip(
_("How much (percentage) of the tool width to overlap each tool pass.")
)
overlabel.setMinimumWidth(90)
self.iso_overlap_entry = FCDoubleSpinner(suffix='%', callback=self.confirmation_message)
self.iso_overlap_entry.set_precision(self.decimals)
self.iso_overlap_entry.setWrapping(True)
self.iso_overlap_entry.set_range(0.0000, 99.9999)
self.iso_overlap_entry.setSingleStep(0.1)
grid1.addWidget(overlabel, 6, 0)
grid1.addWidget(self.iso_overlap_entry, 6, 1, 1, 2)
# Milling Type Radio Button
self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
self.milling_type_label.setToolTip(
_("Milling type:\n"
"- climb / best for precision milling and to reduce tool usage\n"
"- conventional / useful when there is no backlash compensation")
)
self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
{'label': _('Conventional'), 'value': 'cv'}])
grid1.addWidget(self.milling_type_label, 7, 0)
grid1.addWidget(self.milling_type_radio, 7, 1, 1, 2)
# combine all passes CB
self.combine_passes_cb = FCCheckBox(label=_('Combine'))
self.combine_passes_cb.setToolTip(
_("Combine all passes into one object")
)
# generate follow
self.follow_cb = FCCheckBox(label=_('"Follow"'))
self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n"
"This means that it will cut through\n"
"the middle of the trace."))
grid1.addWidget(self.combine_passes_cb, 8, 0)
# avoid an area from isolation
self.except_cb = FCCheckBox(label=_('Except'))
grid1.addWidget(self.follow_cb, 8, 1)
self.except_cb.setToolTip(_("When the isolation geometry is generated,\n"
"by checking this, the area of the object below\n"
"will be subtracted from the isolation geometry."))
grid1.addWidget(self.except_cb, 8, 2)
# ## Form Layout
form_layout = QtWidgets.QFormLayout()
grid1.addLayout(form_layout, 9, 0, 1, 3)
# ################################################
# ##### Type of object to be excepted ############
# ################################################
self.type_obj_combo = FCComboBox()
self.type_obj_combo.addItems([_("Gerber"), _("Geometry")])
# we get rid of item1 ("Excellon") as it is not suitable
# self.type_obj_combo.view().setRowHidden(1, True)
self.type_obj_combo.setItemIcon(0, QtGui.QIcon(self.resource_loc + "/flatcam_icon16.png"))
self.type_obj_combo.setItemIcon(1, QtGui.QIcon(self.resource_loc + "/geometry16.png"))
self.type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Obj Type"))
self.type_obj_combo_label.setToolTip(
_("Specify the type of object to be excepted from isolation.\n"
"It can be of type: Gerber or Geometry.\n"
"What is selected here will dictate the kind\n"
"of objects that will populate the 'Object' combobox.")
)
# self.type_obj_combo_label.setMinimumWidth(60)
form_layout.addRow(self.type_obj_combo_label, self.type_obj_combo)
# ################################################
# ##### The object to be excepted ################
# ################################################
self.obj_combo = FCComboBox()
self.obj_label = QtWidgets.QLabel('%s:' % _("Object"))
self.obj_label.setToolTip(_("Object whose area will be removed from isolation geometry."))
form_layout.addRow(self.obj_label, self.obj_combo)
# ---------------------------------------------- #
# --------- Isolation scope -------------------- #
# ---------------------------------------------- #
self.iso_scope_label = QtWidgets.QLabel('<b>%s:</b>' % _('Scope'))
self.iso_scope_label.setToolTip(
_("Isolation scope. Choose what to isolate:\n"
"- 'All' -> Isolate all the polygons in the object\n"
"- 'Selection' -> Isolate a selection of polygons.")
)
self.iso_scope_radio = RadioSet([{'label': _('All'), 'value': 'all'},
{'label': _('Selection'), 'value': 'single'}])
grid1.addWidget(self.iso_scope_label, 10, 0)
grid1.addWidget(self.iso_scope_radio, 10, 1, 1, 2)
# ---------------------------------------------- #
# --------- Isolation type -------------------- #
# ---------------------------------------------- #
self.iso_type_label = QtWidgets.QLabel('<b>%s:</b>' % _('Isolation Type'))
self.iso_type_label.setToolTip(
_("Choose how the isolation will be executed:\n"
"- 'Full' -> complete isolation of polygons\n"
"- 'Ext' -> will isolate only on the outside\n"
"- 'Int' -> will isolate only on the inside\n"
"'Exterior' isolation is almost always possible\n"
"(with the right tool) but 'Interior'\n"
"isolation can be done only when there is an opening\n"
"inside of the polygon (e.g polygon is a 'doughnut' shape).")
)
self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'},
{'label': _('Ext'), 'value': 'ext'},
{'label': _('Int'), 'value': 'int'}])
grid1.addWidget(self.iso_type_label, 11, 0)
grid1.addWidget(self.iso_type_radio, 11, 1, 1, 2)
self.generate_iso_button = QtWidgets.QPushButton("%s" % _("Generate Isolation Geometry"))
self.generate_iso_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.generate_iso_button.setToolTip(
_("Create a Geometry object with toolpaths to cut \n"
"isolation outside, inside or on both sides of the\n"
"object. For a Gerber object outside means outside\n"
"of the Gerber feature and inside means inside of\n"
"the Gerber feature, if possible at all. This means\n"
"that only if the Gerber feature has openings inside, they\n"
"will be isolated. If what is wanted is to cut isolation\n"
"inside the actual Gerber feature, use a negative tool\n"
"diameter above.")
)
grid1.addWidget(self.generate_iso_button, 12, 0, 1, 3)
self.create_buffer_button = QtWidgets.QPushButton(_('Buffer Solid Geometry'))
self.create_buffer_button.setToolTip(
_("This button is shown only when the Gerber file\n"
@ -309,12 +537,19 @@ class GerberObjectUI(ObjectUI):
"Clicking this will create the buffered geometry\n"
"required for isolation.")
)
self.custom_box.addWidget(self.create_buffer_button)
grid1.addWidget(self.create_buffer_button, 13, 0, 1, 3)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.custom_box.addWidget(separator_line)
self.ohis_iso = OptionalHideInputSection(
self.except_cb,
[self.type_obj_combo, self.type_obj_combo_label, self.obj_combo, self.obj_label],
logic=True
)
separator_line2 = QtWidgets.QFrame()
separator_line2.setFrameShape(QtWidgets.QFrame.HLine)
separator_line2.setFrameShadow(QtWidgets.QFrame.Sunken)
grid1.addWidget(separator_line2, 14, 0, 1, 3)
# grid1.addWidget(QtWidgets.QLabel(''), 15, 0)
# ###########################################
# ########## NEW GRID #######################
@ -328,21 +563,14 @@ class GerberObjectUI(ObjectUI):
self.tool_lbl = QtWidgets.QLabel('<b>%s</b>' % _("TOOLS"))
grid2.addWidget(self.tool_lbl, 0, 0, 1, 2)
# Isolation Tool - will create isolation paths around the copper features
self.iso_button = QtWidgets.QPushButton(_('Isolation Routing'))
self.iso_button.setToolTip(
_("Create a Geometry object with\n"
"toolpaths to cut around polygons.")
)
self.iso_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
grid2.addWidget(self.iso_button, 1, 0, 1, 2)
# ## Clear non-copper regions
self.clearcopper_label = QtWidgets.QLabel("%s" % _("Clear N-copper"))
self.clearcopper_label.setToolTip(
_("Create a Geometry object with\n"
"toolpaths to cut all non-copper regions.")
)
self.clearcopper_label.setMinimumWidth(90)
self.generate_ncc_button = QtWidgets.QPushButton(_('NCC Tool'))
self.generate_ncc_button.setToolTip(
_("Create the Geometry Object\n"
@ -354,9 +582,17 @@ class GerberObjectUI(ObjectUI):
font-weight: bold;
}
""")
grid2.addWidget(self.generate_ncc_button, 2, 0, 1, 2)
grid2.addWidget(self.clearcopper_label, 1, 0)
grid2.addWidget(self.generate_ncc_button, 1, 1)
# ## Board cutout
self.board_cutout_label = QtWidgets.QLabel("%s" % _("Board cutout"))
self.board_cutout_label.setToolTip(
_("Create toolpaths to cut around\n"
"the PCB and separate it from\n"
"the original board.")
)
self.generate_cutout_button = QtWidgets.QPushButton(_('Cutout Tool'))
self.generate_cutout_button.setToolTip(
_("Generate the geometry for\n"
@ -368,12 +604,13 @@ class GerberObjectUI(ObjectUI):
font-weight: bold;
}
""")
grid2.addWidget(self.generate_cutout_button, 3, 0, 1, 2)
grid2.addWidget(self.board_cutout_label, 2, 0)
grid2.addWidget(self.generate_cutout_button, 2, 1)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid2.addWidget(separator_line, 4, 0, 1, 2)
grid2.addWidget(separator_line, 3, 0, 1, 2)
# ## Non-copper regions
self.noncopper_label = QtWidgets.QLabel("<b>%s</b>" % _("Non-copper regions"))
@ -385,7 +622,7 @@ class GerberObjectUI(ObjectUI):
"copper from a specified region.")
)
grid2.addWidget(self.noncopper_label, 5, 0, 1, 2)
grid2.addWidget(self.noncopper_label, 4, 0, 1, 2)
# Margin
bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
@ -401,8 +638,8 @@ class GerberObjectUI(ObjectUI):
self.noncopper_margin_entry.set_precision(self.decimals)
self.noncopper_margin_entry.setSingleStep(0.1)
grid2.addWidget(bmlabel, 6, 0)
grid2.addWidget(self.noncopper_margin_entry, 6, 1)
grid2.addWidget(bmlabel, 5, 0)
grid2.addWidget(self.noncopper_margin_entry, 5, 1)
# Rounded corners
self.noncopper_rounded_cb = FCCheckBox(label=_("Rounded Geo"))
@ -412,13 +649,13 @@ class GerberObjectUI(ObjectUI):
self.noncopper_rounded_cb.setMinimumWidth(90)
self.generate_noncopper_button = QtWidgets.QPushButton(_('Generate Geo'))
grid2.addWidget(self.noncopper_rounded_cb, 7, 0)
grid2.addWidget(self.generate_noncopper_button, 7, 1)
grid2.addWidget(self.noncopper_rounded_cb, 6, 0)
grid2.addWidget(self.generate_noncopper_button, 6, 1)
separator_line1 = QtWidgets.QFrame()
separator_line1.setFrameShape(QtWidgets.QFrame.HLine)
separator_line1.setFrameShadow(QtWidgets.QFrame.Sunken)
grid2.addWidget(separator_line1, 8, 0, 1, 2)
grid2.addWidget(separator_line1, 7, 0, 1, 2)
# ## Bounding box
self.boundingbox_label = QtWidgets.QLabel('<b>%s</b>' % _('Bounding Box'))
@ -427,7 +664,7 @@ class GerberObjectUI(ObjectUI):
"Square shape.")
)
grid2.addWidget(self.boundingbox_label, 9, 0, 1, 2)
grid2.addWidget(self.boundingbox_label, 8, 0, 1, 2)
bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
bbmargin.setToolTip(
@ -440,8 +677,8 @@ class GerberObjectUI(ObjectUI):
self.bbmargin_entry.set_precision(self.decimals)
self.bbmargin_entry.setSingleStep(0.1)
grid2.addWidget(bbmargin, 10, 0)
grid2.addWidget(self.bbmargin_entry, 10, 1)
grid2.addWidget(bbmargin, 9, 0)
grid2.addWidget(self.bbmargin_entry, 9, 1)
self.bbrounded_cb = FCCheckBox(label=_("Rounded Geo"))
self.bbrounded_cb.setToolTip(
@ -456,8 +693,8 @@ class GerberObjectUI(ObjectUI):
self.generate_bb_button.setToolTip(
_("Generate the Geometry object.")
)
grid2.addWidget(self.bbrounded_cb, 11, 0)
grid2.addWidget(self.generate_bb_button, 11, 1)
grid2.addWidget(self.bbrounded_cb, 10, 0)
grid2.addWidget(self.generate_bb_button, 10, 1)
class ExcellonObjectUI(ObjectUI):
@ -486,38 +723,22 @@ class ExcellonObjectUI(ObjectUI):
parent=parent,
app=self.app)
# Plot options
grid_h = QtWidgets.QGridLayout()
grid_h.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.custom_box.addLayout(grid_h)
grid_h.setColumnStretch(0, 0)
grid_h.setColumnStretch(1, 1)
# ### Plot options ####
hlay_plot = QtWidgets.QHBoxLayout()
self.custom_box.addLayout(hlay_plot)
self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
self.plot_options_label.setMinimumWidth(90)
grid_h.addWidget(self.plot_options_label, 0, 0)
# Solid CB
self.solid_cb = FCCheckBox(label=_('Solid'))
self.solid_cb.setToolTip(
_("Solid circles.")
)
self.solid_cb.setMinimumWidth(50)
grid_h.addWidget(self.solid_cb, 0, 1)
# Multicolored CB
self.multicolored_cb = FCCheckBox(label=_('Multi-Color'))
self.multicolored_cb.setToolTip(
_("Draw polygons in different colors.")
)
self.multicolored_cb.setMinimumWidth(55)
grid_h.addWidget(self.multicolored_cb, 0, 2)
hlay_plot.addWidget(self.plot_options_label)
hlay_plot.addStretch()
hlay_plot.addWidget(self.solid_cb)
# ## Object name
self.name_hlay = QtWidgets.QHBoxLayout()
grid_h.addLayout(self.name_hlay, 1, 0, 1, 3)
self.custom_box.addLayout(self.name_hlay)
name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
self.name_entry = FCEntry()
self.name_entry.setFocusPolicy(QtCore.Qt.StrongFocus)
@ -1304,28 +1525,12 @@ class GeometryObjectUI(ObjectUI):
)
# Plot options
grid_header = QtWidgets.QGridLayout()
grid_header.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.custom_box.addLayout(grid_header)
grid_header.setColumnStretch(0, 0)
grid_header.setColumnStretch(1, 1)
self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
self.plot_options_label.setMinimumWidth(90)
grid_header.addWidget(self.plot_options_label, 0, 0)
# Multicolored CB
self.multicolored_cb = FCCheckBox(label=_('Multi-Color'))
self.multicolored_cb.setToolTip(
_("Draw polygons in different colors.")
)
self.multicolored_cb.setMinimumWidth(55)
grid_header.addWidget(self.multicolored_cb, 0, 2)
self.custom_box.addWidget(self.plot_options_label)
# ## Object name
self.name_hlay = QtWidgets.QHBoxLayout()
grid_header.addLayout(self.name_hlay, 1, 0, 1, 3)
self.custom_box.addLayout(self.name_hlay)
name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
self.name_entry = FCEntry()
@ -1489,24 +1694,19 @@ class GeometryObjectUI(ObjectUI):
grid1.addWidget(self.addtool_entry_lbl, 3, 0)
grid1.addWidget(self.addtool_entry, 3, 1)
bhlay = QtWidgets.QHBoxLayout()
self.addtool_btn = QtWidgets.QPushButton(_('Add'))
self.addtool_btn.setToolTip(
_("Add a new tool to the Tool Table\n"
"with the diameter specified above.")
"with the specified diameter.")
)
grid1.addWidget(self.addtool_btn, 4, 0, 1, 2)
self.addtool_from_db_btn = QtWidgets.QPushButton(_('Add from DB'))
self.addtool_from_db_btn.setToolTip(
_("Add a new tool to the Tool Table\n"
"from the Tool DataBase.")
)
bhlay.addWidget(self.addtool_btn)
bhlay.addWidget(self.addtool_from_db_btn)
grid1.addLayout(bhlay, 5, 0, 1, 2)
grid1.addWidget(self.addtool_from_db_btn, 7, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
@ -1554,10 +1754,10 @@ class GeometryObjectUI(ObjectUI):
# ################# GRID LAYOUT 3 ###############################
# #################################################################
self.grid3 = QtWidgets.QGridLayout()
self.grid3.setColumnStretch(0, 0)
self.grid3.setColumnStretch(1, 1)
self.geo_param_box.addLayout(self.grid3)
grid3 = QtWidgets.QGridLayout()
grid3.setColumnStretch(0, 0)
grid3.setColumnStretch(1, 1)
self.geo_param_box.addLayout(grid3)
# ### Tools Data ## ##
self.tool_data_label = QtWidgets.QLabel(
@ -1568,7 +1768,7 @@ class GeometryObjectUI(ObjectUI):
"Each tool store it's own set of such data."
)
)
self.grid3.addWidget(self.tool_data_label, 0, 0, 1, 2)
grid3.addWidget(self.tool_data_label, 0, 0, 1, 2)
# Tip Dia
self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
@ -1582,8 +1782,8 @@ class GeometryObjectUI(ObjectUI):
self.tipdia_entry.set_range(0.00001, 9999.9999)
self.tipdia_entry.setSingleStep(0.1)
self.grid3.addWidget(self.tipdialabel, 1, 0)
self.grid3.addWidget(self.tipdia_entry, 1, 1)
grid3.addWidget(self.tipdialabel, 1, 0)
grid3.addWidget(self.tipdia_entry, 1, 1)
# Tip Angle
self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
@ -1598,8 +1798,8 @@ class GeometryObjectUI(ObjectUI):
self.tipangle_entry.set_range(1.0, 180.0)
self.tipangle_entry.setSingleStep(1)
self.grid3.addWidget(self.tipanglelabel, 2, 0)
self.grid3.addWidget(self.tipangle_entry, 2, 1)
grid3.addWidget(self.tipanglelabel, 2, 0)
grid3.addWidget(self.tipangle_entry, 2, 1)
# Cut Z
self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
@ -1619,8 +1819,8 @@ class GeometryObjectUI(ObjectUI):
self.cutz_entry.setSingleStep(0.1)
self.grid3.addWidget(self.cutzlabel, 3, 0)
self.grid3.addWidget(self.cutz_entry, 3, 1)
grid3.addWidget(self.cutzlabel, 3, 0)
grid3.addWidget(self.cutz_entry, 3, 1)
# Multi-pass
self.mpass_cb = FCCheckBox('%s:' % _("Multi-Depth"))
@ -1645,8 +1845,8 @@ class GeometryObjectUI(ObjectUI):
)
self.ois_mpass_geo = OptionalInputSection(self.mpass_cb, [self.maxdepth_entry])
self.grid3.addWidget(self.mpass_cb, 4, 0)
self.grid3.addWidget(self.maxdepth_entry, 4, 1)
grid3.addWidget(self.mpass_cb, 4, 0)
grid3.addWidget(self.maxdepth_entry, 4, 1)
# Travel Z
self.travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
@ -1664,8 +1864,8 @@ class GeometryObjectUI(ObjectUI):
self.travelz_entry.setSingleStep(0.1)
self.grid3.addWidget(self.travelzlabel, 5, 0)
self.grid3.addWidget(self.travelz_entry, 5, 1)
grid3.addWidget(self.travelzlabel, 5, 0)
grid3.addWidget(self.travelz_entry, 5, 1)
# Feedrate X-Y
self.frlabel = QtWidgets.QLabel('%s:' % _('Feedrate X-Y'))
@ -1678,8 +1878,8 @@ class GeometryObjectUI(ObjectUI):
self.cncfeedrate_entry.set_range(0, 99999.9999)
self.cncfeedrate_entry.setSingleStep(0.1)
self.grid3.addWidget(self.frlabel, 10, 0)
self.grid3.addWidget(self.cncfeedrate_entry, 10, 1)
grid3.addWidget(self.frlabel, 10, 0)
grid3.addWidget(self.cncfeedrate_entry, 10, 1)
# Feedrate Z (Plunge)
self.frzlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z'))
@ -1693,8 +1893,8 @@ class GeometryObjectUI(ObjectUI):
self.feedrate_z_entry.set_range(0, 99999.9999)
self.feedrate_z_entry.setSingleStep(0.1)
self.grid3.addWidget(self.frzlabel, 11, 0)
self.grid3.addWidget(self.feedrate_z_entry, 11, 1)
grid3.addWidget(self.frzlabel, 11, 0)
grid3.addWidget(self.feedrate_z_entry, 11, 1)
# Feedrate rapids
self.fr_rapidlabel = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
@ -1710,8 +1910,8 @@ class GeometryObjectUI(ObjectUI):
self.feedrate_rapid_entry.set_range(0, 99999.9999)
self.feedrate_rapid_entry.setSingleStep(0.1)
self.grid3.addWidget(self.fr_rapidlabel, 12, 0)
self.grid3.addWidget(self.feedrate_rapid_entry, 12, 1)
grid3.addWidget(self.fr_rapidlabel, 12, 0)
grid3.addWidget(self.feedrate_rapid_entry, 12, 1)
# default values is to hide
self.fr_rapidlabel.hide()
self.feedrate_rapid_entry.hide()
@ -1736,8 +1936,8 @@ class GeometryObjectUI(ObjectUI):
"meet with last cut, we generate an\n"
"extended cut over the first cut section.")
)
self.grid3.addWidget(self.extracut_cb, 13, 0)
self.grid3.addWidget(self.e_cut_entry, 13, 1)
grid3.addWidget(self.extracut_cb, 13, 0)
grid3.addWidget(self.e_cut_entry, 13, 1)
# Spindlespeed
self.spindle_label = QtWidgets.QLabel('%s:' % _('Spindle speed'))
@ -1752,8 +1952,8 @@ class GeometryObjectUI(ObjectUI):
self.cncspindlespeed_entry.set_range(0, 1000000)
self.cncspindlespeed_entry.set_step(100)
self.grid3.addWidget(self.spindle_label, 14, 0)
self.grid3.addWidget(self.cncspindlespeed_entry, 14, 1)
grid3.addWidget(self.spindle_label, 14, 0)
grid3.addWidget(self.cncspindlespeed_entry, 14, 1)
# Dwell
self.dwell_cb = FCCheckBox('%s:' % _('Dwell'))
@ -1773,8 +1973,8 @@ class GeometryObjectUI(ObjectUI):
)
self.ois_dwell_geo = OptionalInputSection(self.dwell_cb, [self.dwelltime_entry])
self.grid3.addWidget(self.dwell_cb, 15, 0)
self.grid3.addWidget(self.dwelltime_entry, 15, 1)
grid3.addWidget(self.dwell_cb, 15, 0)
grid3.addWidget(self.dwelltime_entry, 15, 1)
# Probe depth
self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth"))
@ -1787,8 +1987,8 @@ class GeometryObjectUI(ObjectUI):
self.pdepth_entry.set_range(-9999.9999, 9999.9999)
self.pdepth_entry.setSingleStep(0.1)
self.grid3.addWidget(self.pdepth_label, 17, 0)
self.grid3.addWidget(self.pdepth_entry, 17, 1)
grid3.addWidget(self.pdepth_label, 17, 0)
grid3.addWidget(self.pdepth_entry, 17, 1)
self.pdepth_label.hide()
self.pdepth_entry.setVisible(False)
@ -1803,8 +2003,8 @@ class GeometryObjectUI(ObjectUI):
self.feedrate_probe_entry.set_range(0.0, 9999.9999)
self.feedrate_probe_entry.setSingleStep(0.1)
self.grid3.addWidget(self.feedrate_probe_label, 18, 0)
self.grid3.addWidget(self.feedrate_probe_entry, 18, 1)
grid3.addWidget(self.feedrate_probe_label, 18, 0)
grid3.addWidget(self.feedrate_probe_entry, 18, 1)
self.feedrate_probe_label.hide()
self.feedrate_probe_entry.setVisible(False)

View File

@ -8,21 +8,13 @@
from PyQt5 import QtCore
import logging
from AppGUI.VisPyCanvas import VisPyCanvas, Color
from AppGUI.VisPyVisuals import ShapeGroup, ShapeCollection, TextCollection, TextGroup, Cursor
from vispy.scene.visuals import InfiniteLine, Line, Rectangle, Text
import gettext
import AppTranslation as fcTranslate
import builtins
from flatcamGUI.VisPyCanvas import VisPyCanvas, Color
from flatcamGUI.VisPyVisuals import ShapeGroup, ShapeCollection, TextCollection, TextGroup, Cursor
from vispy.scene.visuals import InfiniteLine, Line
import numpy as np
from vispy.geometry import Rect
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
@ -62,12 +54,8 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
if theme == 'white':
self.line_color = (0.3, 0.0, 0.0, 1.0)
self.rect_hud_color = Color('#0000FF10')
self.text_hud_color = 'black'
else:
self.line_color = (0.4, 0.4, 0.4, 1.0)
self.rect_hud_color = Color('#80808040')
self.text_hud_color = 'white'
# workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node,
# which might decrease performance
@ -141,6 +129,11 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
self.h_line = InfiniteLine(pos=0, color=(0.70, 0.3, 0.3, 0.8), vertical=False,
parent=self.view.scene)
# draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area
# all CNC have a limited workspace
if self.fcapp.defaults['global_workspace'] is True:
self.draw_workspace(workspace_size=self.fcapp.defaults["global_workspaceT"])
self.line_parent = None
if self.fcapp.defaults["global_cursor_color_enabled"]:
c_color = Color(self.fcapp.defaults["global_cursor_color"]).rgba
@ -153,61 +146,13 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
self.cursor_h_line = InfiniteLine(pos=None, color=c_color, vertical=False,
parent=self.line_parent)
# font size
qsettings = QtCore.QSettings("Open Source", "FlatCAM")
if qsettings.contains("hud_font_size"):
fsize = qsettings.value('hud_font_size', type=int)
else:
fsize = 8
# units
units = self.fcapp.defaults["units"].lower()
# coordinates and anchors
height = fsize * 11 # 90. Constant 11 is something that works
width = height * 2 # width is double the height = it is something that works
center_x = (width / 2) + 5
center_y = (height / 2) + 5
# text
self.text_hud = Text('', color=self.text_hud_color, pos=(10, center_y), method='gpu', anchor_x='left',
parent=None)
self.text_hud.font_size = fsize
self.text_hud.text = 'Dx:\t%s [%s]\nDy:\t%s [%s]\n\nX: \t%s [%s]\nY: \t%s [%s]' % \
('0.0000', units, '0.0000', units, '0.0000', units, '0.0000', units)
# rectangle
self.rect_hud = Rectangle(center=(center_x, center_y), width=width, height=height, radius=[5, 5, 5, 5],
border_color=self.rect_hud_color, color=self.rect_hud_color, parent=None)
self.rect_hud.set_gl_state(depth_test=False)
# draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area
# all CNC have a limited workspace
if self.fcapp.defaults['global_workspace'] is True:
self.draw_workspace(workspace_size=self.fcapp.defaults["global_workspaceT"])
# HUD Display
self.hud_enabled = False
# enable the HUD if it is activated in FlatCAM Preferences
if self.fcapp.defaults['global_hud'] is True:
self.on_toggle_hud(state=True)
# Axis Display
self.axis_enabled = True
# enable Axis
self.on_toggle_axis(state=True)
# enable Grid lines
self.grid_lines_enabled = True
self.shape_collections = []
self.shape_collection = self.new_shape_collection()
self.fcapp.pool_recreated.connect(self.on_pool_recreated)
self.text_collection = self.new_text_collection()
# TODO: Should be setting to show/hide CNC job annotations (global or per object)
self.text_collection.enabled = True
self.c = None
@ -218,76 +163,6 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
self.graph_event_connect('mouse_wheel', self.on_mouse_scroll)
def on_toggle_axis(self, signal=None, state=None):
if state is None:
state = not self.axis_enabled
if state:
self.axis_enabled = True
self.v_line.parent = self.view.scene
self.h_line.parent = self.view.scene
self.fcapp.ui.axis_status_label.setStyleSheet("""
QLabel
{
color: black;
background-color: orange;
}
""")
self.fcapp.inform[str, bool].emit(_("Axis enabled."), False)
else:
self.axis_enabled = False
self.v_line.parent = None
self.h_line.parent = None
self.fcapp.ui.axis_status_label.setStyleSheet("")
self.fcapp.inform[str, bool].emit(_("Axis disabled."), False)
def on_toggle_hud(self, signal=None, state=None):
if state is None:
state = not self.hud_enabled
if state:
self.hud_enabled = True
self.rect_hud.parent = self.view
self.text_hud.parent = self.view
self.fcapp.defaults['global_hud'] = True
self.fcapp.ui.hud_label.setStyleSheet("""
QLabel
{
color: black;
background-color: mediumpurple;
}
""")
self.fcapp.inform[str, bool].emit(_("HUD enabled."), False)
else:
self.hud_enabled = False
self.rect_hud.parent = None
self.text_hud.parent = None
self.fcapp.defaults['global_hud'] = False
self.fcapp.ui.hud_label.setStyleSheet("")
self.fcapp.inform[str, bool].emit(_("HUD disabled."), False)
def on_toggle_grid_lines(self):
state = not self.grid_lines_enabled
if state:
self.grid_lines_enabled = True
self.grid.parent = self.view.scene
self.fcapp.inform[str, bool].emit(_("Grid enabled."), False)
else:
self.grid_lines_enabled = False
self.grid.parent = None
self.fcapp.inform[str, bool].emit(_("Grid disabled."), False)
# HACK: enabling/disabling the cursor seams to somehow update the shapes on screen
# - perhaps is a bug in VisPy implementation
if self.fcapp.grid_status():
self.fcapp.app_cursor.enabled = False
self.fcapp.app_cursor.enabled = True
else:
self.fcapp.app_cursor.enabled = True
self.fcapp.app_cursor.enabled = False
def draw_workspace(self, workspace_size):
"""
Draw a rectangular shape on canvas to specify our valid workspace.
@ -308,30 +183,17 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
a = np.array([(0, 0), (dims[0], 0), (dims[0], dims[1]), (0, dims[1])])
# if not self.workspace_line:
# self.workspace_line = Line(pos=np.array((a[0], a[1], a[2], a[3], a[0])), color=(0.70, 0.3, 0.3, 0.7),
# antialias=True, method='agg', parent=self.view.scene)
# else:
# self.workspace_line.parent = self.view.scene
self.workspace_line = Line(pos=np.array((a[0], a[1], a[2], a[3], a[0])), color=(0.70, 0.3, 0.3, 0.7),
antialias=True, method='agg', parent=self.view.scene)
self.fcapp.ui.wplace_label.set_value(workspace_size[:3])
self.fcapp.ui.wplace_label.setToolTip(workspace_size)
self.fcapp.ui.wplace_label.setStyleSheet("""
QLabel
{
color: black;
background-color: olivedrab;
}
""")
if not self.workspace_line:
self.workspace_line = Line(pos=np.array((a[0], a[1], a[2], a[3], a[0])), color=(0.70, 0.3, 0.3, 0.7),
antialias=True, method='agg', parent=self.view.scene)
else:
self.workspace_line.parent = self.view.scene
def delete_workspace(self):
try:
self.workspace_line.parent = None
except Exception:
pass
self.fcapp.ui.wplace_label.setStyleSheet("")
# redraw the workspace lines on the plot by re adding them to the parent view.scene
def restore_workspace(self):

View File

@ -19,10 +19,8 @@ from shapely.geometry import Polygon, LineString, LinearRing
from copy import deepcopy
import logging
import numpy as np
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
# Prevent conflict with Qt5 and above.
@ -31,13 +29,13 @@ mpl_use("Qt5Agg")
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.lines import Line2D
from matplotlib.offsetbox import AnchoredText
# from matplotlib.widgets import Cursor
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
@ -47,9 +45,9 @@ class CanvasCache(QtCore.QObject):
Case story #1:
1) No objects in the project.
2) Object is created (app_obj.new_object() emits object_created(obj)).
2) Object is created (new_object() emits object_created(obj)).
on_object_created() adds (i) object to collection and emits
(ii) app_obj.new_object_available() then calls (iii) object.plot()
(ii) new_object_available() then calls (iii) object.plot()
3) object.plot() creates axes if necessary on
app.collection.figure. Then plots on it.
4) Plots on a cache-size canvas (in background).
@ -115,7 +113,7 @@ class CanvasCache(QtCore.QObject):
# Continue to update the cache.
# def on_app_obj.new_object_available(self):
# def on_new_object_available(self):
#
# log.debug("A new object is available. Should plot it!")
@ -149,13 +147,9 @@ class PlotCanvasLegacy(QtCore.QObject):
if self.app.defaults['global_theme'] == 'white':
theme_color = '#FFFFFF'
tick_color = '#000000'
self.rect_hud_color = '#0000FF10'
self.text_hud_color = '#000000'
else:
theme_color = '#000000'
tick_color = '#FFFFFF'
self.rect_hud_color = '#80808040'
self.text_hud_color = '#FFFFFF'
# workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node,
# which might decrease performance
@ -301,163 +295,14 @@ class PlotCanvasLegacy(QtCore.QObject):
# signal is the mouse is dragging
self.is_dragging = False
self.mouse_press_pos = None
# signal if there is a doubleclick
self.is_dblclk = False
self.hud_enabled = False
self.text_hud = self.Thud(plotcanvas=self)
# enable Grid lines
self.grid_lines_enabled = True
# draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area
# all CNC have a limited workspace
if self.app.defaults['global_workspace'] is True:
self.draw_workspace(workspace_size=self.app.defaults["global_workspaceT"])
if self.app.defaults['global_hud'] is True:
self.on_toggle_hud(state=True)
# Axis Display
self.axis_enabled = True
# enable Axis
self.on_toggle_axis(state=True)
def on_toggle_axis(self, signal=None, state=None):
if state is None:
state = not self.axis_enabled
if state:
self.axis_enabled = True
if self.h_line not in self.axes.lines and self.v_line not in self.axes.lines:
self.h_line = self.axes.axhline(color=(0.70, 0.3, 0.3), linewidth=2)
self.v_line = self.axes.axvline(color=(0.70, 0.3, 0.3), linewidth=2)
self.app.ui.axis_status_label.setStyleSheet("""
QLabel
{
color: black;
background-color: orange;
}
""")
self.app.inform[str, bool].emit(_("Axis enabled."), False)
else:
self.axis_enabled = False
if self.h_line in self.axes.lines and self.v_line in self.axes.lines:
self.axes.lines.remove(self.h_line)
self.axes.lines.remove(self.v_line)
self.app.ui.axis_status_label.setStyleSheet("")
self.app.inform[str, bool].emit(_("Axis disabled."), False)
self.canvas.draw()
def on_toggle_hud(self, signal=None, state=None):
if state is None:
state = not self.hud_enabled
if state:
self.hud_enabled = True
self.text_hud.add_artist()
self.app.defaults['global_hud'] = True
self.app.ui.hud_label.setStyleSheet("""
QLabel
{
color: black;
background-color: mediumpurple;
}
""")
self.app.inform[str, bool].emit(_("HUD enabled."), False)
else:
self.hud_enabled = False
self.text_hud.remove_artist()
self.app.defaults['global_hud'] = False
self.app.ui.hud_label.setStyleSheet("")
self.app.inform[str, bool].emit(_("HUD disabled."), False)
self.canvas.draw()
class Thud(QtCore.QObject):
text_changed = QtCore.pyqtSignal(str)
def __init__(self, plotcanvas):
super().__init__()
self.p = plotcanvas
units = self.p.app.defaults['units']
self._text = 'Dx: %s [%s]\nDy: %s [%s]\n\nX: %s [%s]\nY: %s [%s]' % \
('0.0000', units, '0.0000', units, '0.0000', units, '0.0000', units)
# set font size
qsettings = QtCore.QSettings("Open Source", "FlatCAM")
if qsettings.contains("hud_font_size"):
# I multiply with 2.5 because this seems to be the difference between the value taken by the VisPy (3D)
# and Matplotlib (Legacy2D FlatCAM graphic engine)
fsize = int(qsettings.value('hud_font_size', type=int) * 2.5)
else:
fsize = 20
self.hud_holder = AnchoredText(self._text, prop=dict(size=fsize), frameon=True, loc='upper left')
self.hud_holder.patch.set_boxstyle("round,pad=0.,rounding_size=0.2")
fc_color = self.p.rect_hud_color[:-2]
fc_alpha = int(self.p.rect_hud_color[-2:], 16) / 255
text_color = self.p.text_hud_color
self.hud_holder.patch.set_facecolor(fc_color)
self.hud_holder.patch.set_alpha(fc_alpha)
self.hud_holder.patch.set_edgecolor((0, 0, 0, 0))
self. hud_holder.txt._text.set_color(color=text_color)
self.text_changed.connect(self.on_text_changed)
@property
def text(self):
return self._text
@text.setter
def text(self, val):
self.text_changed.emit(val)
self._text = val
def on_text_changed(self, txt):
try:
txt = txt.replace('\t', ' ')
self.hud_holder.txt.set_text(txt)
self.p.canvas.draw()
except Exception:
pass
def add_artist(self):
if self.hud_holder not in self.p.axes.artists:
self.p.axes.add_artist(self.hud_holder)
def remove_artist(self):
if self.hud_holder in self.p.axes.artists:
self.p.axes.artists.remove(self.hud_holder)
def on_toggle_grid_lines(self):
state = not self.grid_lines_enabled
if state:
self.grid_lines_enabled = True
self.axes.grid(True)
try:
self.canvas.draw()
except IndexError:
pass
self.app.inform[str, bool].emit(_("Grid enabled."), False)
else:
self.grid_lines_enabled = False
self.axes.grid(False)
try:
self.canvas.draw()
except IndexError:
pass
self.app.inform[str, bool].emit(_("Grid disabled."), False)
def draw_workspace(self, workspace_size):
"""
Draw a rectangular shape on canvas to specify our valid workspace.
@ -484,23 +329,12 @@ class PlotCanvasLegacy(QtCore.QObject):
self.axes.add_line(self.workspace_line)
self.canvas.draw()
self.app.ui.wplace_label.set_value(workspace_size[:3])
self.app.ui.wplace_label.setToolTip(workspace_size)
self.fcapp.ui.wplace_label.setStyleSheet("""
QLabel
{
color: black;
background-color: olivedrab;
}
""")
def delete_workspace(self):
try:
self.axes.lines.remove(self.workspace_line)
self.canvas.draw()
except Exception:
pass
self.fcapp.ui.wplace_label.setStyleSheet("")
def graph_event_connect(self, event_name, callback):
"""
@ -589,7 +423,7 @@ class PlotCanvasLegacy(QtCore.QObject):
if self.big_cursor is False:
try:
x, y = self.snap(x_pos, y_pos)
x, y = self.app.geo_editor.snap(x_pos, y_pos)
# Pointer (snapped)
# The size of the cursor is multiplied by 1.65 because that value made the cursor similar with the
@ -622,7 +456,7 @@ class PlotCanvasLegacy(QtCore.QObject):
pass
self.canvas.draw_idle()
self.canvas.blit(self.axes.bbox)
self.canvas.blit(self.axes.bbox)
def clear_cursor(self, state):
if state is True:
@ -947,7 +781,6 @@ class PlotCanvasLegacy(QtCore.QObject):
def on_mouse_press(self, event):
self.is_dragging = True
self.mouse_press_pos = (event.x, event.y)
# Check for middle mouse button press
if self.app.defaults["global_pan_button"] == '2':
@ -973,11 +806,7 @@ class PlotCanvasLegacy(QtCore.QObject):
def on_mouse_release(self, event):
mouse_release_pos = (event.x, event.y)
delta = 0.05
if abs(self.distance(self.mouse_press_pos, mouse_release_pos)) < delta:
self.is_dragging = False
self.is_dragging = False
# Check for middle mouse button release to complete pan procedure
# Check for middle mouse button press
@ -1029,7 +858,7 @@ class PlotCanvasLegacy(QtCore.QObject):
self.canvas.draw_idle()
# #### Temporary place-holder for cached update #####
# self.update_screen_request.emit([0, 0, 0, 0, 0])
self.update_screen_request.emit([0, 0, 0, 0, 0])
if self.app.defaults["global_cursor_color_enabled"] is True:
self.draw_cursor(x_pos=x, y_pos=y, color=self.app.cursor_color_3D)
@ -1081,59 +910,6 @@ class PlotCanvasLegacy(QtCore.QObject):
return width / xpx, height / ypx
def snap(self, x, y):
"""
Adjusts coordinates to snap settings.
:param x: Input coordinate X
:param y: Input coordinate Y
:return: Snapped (x, y)
"""
snap_x, snap_y = (x, y)
snap_distance = np.Inf
# ### Grid snap
if self.app.grid_status():
if self.app.defaults["global_gridx"] != 0:
try:
snap_x_ = round(x / float(self.app.defaults["global_gridx"])) * \
float(self.app.defaults["global_gridx"])
except TypeError:
snap_x_ = x
else:
snap_x_ = x
# If the Grid_gap_linked on Grid Toolbar is checked then the snap distance on GridY entry will be ignored
# and it will use the snap distance from GridX entry
if self.app.ui.grid_gap_link_cb.isChecked():
if self.app.defaults["global_gridx"] != 0:
try:
snap_y_ = round(y / float(self.app.defaults["global_gridx"])) * \
float(self.app.defaults["global_gridx"])
except TypeError:
snap_y_ = y
else:
snap_y_ = y
else:
if self.app.defaults["global_gridy"] != 0:
try:
snap_y_ = round(y / float(self.app.defaults["global_gridy"])) * \
float(self.app.defaults["global_gridy"])
except TypeError:
snap_y_ = y
else:
snap_y_ = y
nearest_grid_distance = self.distance((x, y), (snap_x_, snap_y_))
if nearest_grid_distance < snap_distance:
snap_x, snap_y = (snap_x_, snap_y_)
return snap_x, snap_y
@staticmethod
def distance(pt1, pt2):
return np.sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2)
class FakeCursor(QtCore.QObject):
"""

View File

@ -13,7 +13,6 @@ import numpy as np
import vispy.scene as scene
from vispy.scene.cameras.base_camera import BaseCamera
# from vispy.scene.widgets import Widget as VisPyWidget
from vispy.color import Color
import time

View File

@ -13,7 +13,7 @@ from vispy.color import Color
from shapely.geometry import Polygon, LineString, LinearRing
import threading
import numpy as np
from AppGUI.VisPyTesselators import GLUTess
from flatcamGUI.VisPyTesselators import GLUTess
class FlatCAMLineVisual(LineVisual):

View File

@ -0,0 +1,19 @@
from PyQt5 import QtWidgets
class OptionsGroupUI(QtWidgets.QGroupBox):
app = None
def __init__(self, title, parent=None):
# QtGui.QGroupBox.__init__(self, title, parent=parent)
super(OptionsGroupUI, self).__init__()
self.setStyleSheet("""
QGroupBox
{
font-size: 16px;
font-weight: bold;
}
""")
self.layout = QtWidgets.QVBoxLayout()
self.setLayout(self.layout)

View File

@ -5,7 +5,7 @@ from defaults import FlatCAMDefaults
import logging
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -29,7 +29,7 @@ class PreferencesUIManager:
:param defaults: a dictionary storage where all the application settings are stored
:param data_path: a path to the file where all the preferences are stored for persistence
:param ui: reference to the MainGUI class which constructs the UI
:param ui: reference to the FlatCAMGUI class which constructs the UI
:param inform: a pyqtSignal used to display information's in the StatusBar of the GUI
"""
@ -43,7 +43,7 @@ class PreferencesUIManager:
self.preferences_changed_flag = False
# when adding entries here read the comments in the method found below named:
# def app_obj.new_object(self, kind, name, initialize, active=True, fit=True, plot=True)
# def new_object(self, kind, name, initialize, active=True, fit=True, plot=True)
self.defaults_form_fields = {
# General App
"decimals_inch": self.ui.general_defaults_form.general_app_group.precision_inch_entry,
@ -123,10 +123,16 @@ class PreferencesUIManager:
"gerber_def_zeros": self.ui.gerber_defaults_form.gerber_gen_group.gerber_zeros_radio,
"gerber_clean_apertures": self.ui.gerber_defaults_form.gerber_gen_group.gerber_clean_cb,
"gerber_extra_buffering": self.ui.gerber_defaults_form.gerber_gen_group.gerber_extra_buffering,
"gerber_plot_fill": self.ui.gerber_defaults_form.gerber_gen_group.fill_color_entry,
"gerber_plot_line": self.ui.gerber_defaults_form.gerber_gen_group.line_color_entry,
"gerber_plot_fill": self.ui.gerber_defaults_form.gerber_gen_group.pf_color_entry,
"gerber_plot_line": self.ui.gerber_defaults_form.gerber_gen_group.pl_color_entry,
# Gerber Options
"gerber_isotooldia": self.ui.gerber_defaults_form.gerber_opt_group.iso_tool_dia_entry,
"gerber_isopasses": self.ui.gerber_defaults_form.gerber_opt_group.iso_width_entry,
"gerber_isooverlap": self.ui.gerber_defaults_form.gerber_opt_group.iso_overlap_entry,
"gerber_combine_passes": self.ui.gerber_defaults_form.gerber_opt_group.combine_passes_cb,
"gerber_iso_scope": self.ui.gerber_defaults_form.gerber_opt_group.iso_scope_radio,
"gerber_milling_type": self.ui.gerber_defaults_form.gerber_opt_group.milling_type_radio,
"gerber_noncoppermargin": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_margin_entry,
"gerber_noncopperrounded": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_rounded_cb,
"gerber_bboxmargin": self.ui.gerber_defaults_form.gerber_opt_group.bbmargin_entry,
@ -137,6 +143,12 @@ class PreferencesUIManager:
# "gerber_aperture_scale_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.scale_aperture_entry,
# "gerber_aperture_buffer_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffer_aperture_entry,
"gerber_follow": self.ui.gerber_defaults_form.gerber_adv_opt_group.follow_cb,
"gerber_tool_type": self.ui.gerber_defaults_form.gerber_adv_opt_group.tool_type_radio,
"gerber_vtipdia": self.ui.gerber_defaults_form.gerber_adv_opt_group.tipdia_spinner,
"gerber_vtipangle": self.ui.gerber_defaults_form.gerber_adv_opt_group.tipangle_spinner,
"gerber_vcutz": self.ui.gerber_defaults_form.gerber_adv_opt_group.cutz_spinner,
"gerber_iso_type": self.ui.gerber_defaults_form.gerber_adv_opt_group.iso_type_radio,
"gerber_buffering": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffering_radio,
"gerber_simplification": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplify_cb,
"gerber_simp_tolerance": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplification_tol_spinner,
@ -168,7 +180,6 @@ class PreferencesUIManager:
# Excellon General
"excellon_plot": self.ui.excellon_defaults_form.excellon_gen_group.plot_cb,
"excellon_solid": self.ui.excellon_defaults_form.excellon_gen_group.solid_cb,
"excellon_multicolored": self.ui.excellon_defaults_form.excellon_gen_group.multicolored_cb,
"excellon_format_upper_in":
self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_in_entry,
"excellon_format_lower_in":
@ -210,31 +221,31 @@ class PreferencesUIManager:
"excellon_gcode_type": self.ui.excellon_defaults_form.excellon_opt_group.excellon_gcode_type_radio,
# Excellon Advanced Options
"excellon_offset": self.ui.excellon_defaults_form.excellon_adv_opt_group.offset_entry,
"excellon_toolchangexy": self.ui.excellon_defaults_form.excellon_adv_opt_group.toolchangexy_entry,
"excellon_startz": self.ui.excellon_defaults_form.excellon_adv_opt_group.estartz_entry,
"excellon_feedrate_rapid": self.ui.excellon_defaults_form.excellon_adv_opt_group.feedrate_rapid_entry,
"excellon_z_pdepth": self.ui.excellon_defaults_form.excellon_adv_opt_group.pdepth_entry,
"excellon_feedrate_probe": self.ui.excellon_defaults_form.excellon_adv_opt_group.feedrate_probe_entry,
"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_retract": self.ui.excellon_defaults_form.excellon_adv_opt_group.fretract_cb,
"excellon_offset": self.ui.excellon_defaults_form.excellon_adv_opt_group.offset_entry,
"excellon_toolchangexy": self.ui.excellon_defaults_form.excellon_adv_opt_group.toolchangexy_entry,
"excellon_startz": self.ui.excellon_defaults_form.excellon_adv_opt_group.estartz_entry,
"excellon_feedrate_rapid": self.ui.excellon_defaults_form.excellon_adv_opt_group.feedrate_rapid_entry,
"excellon_z_pdepth": self.ui.excellon_defaults_form.excellon_adv_opt_group.pdepth_entry,
"excellon_feedrate_probe": self.ui.excellon_defaults_form.excellon_adv_opt_group.feedrate_probe_entry,
"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_retract": self.ui.excellon_defaults_form.excellon_adv_opt_group.fretract_cb,
# Excellon Export
"excellon_exp_units": self.ui.excellon_defaults_form.excellon_exp_group.excellon_units_radio,
"excellon_exp_format": self.ui.excellon_defaults_form.excellon_exp_group.format_radio,
"excellon_exp_integer": self.ui.excellon_defaults_form.excellon_exp_group.format_whole_entry,
"excellon_exp_decimals": self.ui.excellon_defaults_form.excellon_exp_group.format_dec_entry,
"excellon_exp_zeros": self.ui.excellon_defaults_form.excellon_exp_group.zeros_radio,
"excellon_exp_slot_type": self.ui.excellon_defaults_form.excellon_exp_group.slot_type_radio,
"excellon_exp_units": self.ui.excellon_defaults_form.excellon_exp_group.excellon_units_radio,
"excellon_exp_format": self.ui.excellon_defaults_form.excellon_exp_group.format_radio,
"excellon_exp_integer": self.ui.excellon_defaults_form.excellon_exp_group.format_whole_entry,
"excellon_exp_decimals": self.ui.excellon_defaults_form.excellon_exp_group.format_dec_entry,
"excellon_exp_zeros": self.ui.excellon_defaults_form.excellon_exp_group.zeros_radio,
"excellon_exp_slot_type": self.ui.excellon_defaults_form.excellon_exp_group.slot_type_radio,
# Excellon Editor
"excellon_editor_sel_limit": self.ui.excellon_defaults_form.excellon_editor_group.sel_limit_entry,
"excellon_editor_newdia": self.ui.excellon_defaults_form.excellon_editor_group.addtool_entry,
"excellon_editor_array_size": self.ui.excellon_defaults_form.excellon_editor_group.drill_array_size_entry,
"excellon_editor_lin_dir": self.ui.excellon_defaults_form.excellon_editor_group.drill_axis_radio,
"excellon_editor_lin_pitch": self.ui.excellon_defaults_form.excellon_editor_group.drill_pitch_entry,
"excellon_editor_lin_angle": self.ui.excellon_defaults_form.excellon_editor_group.drill_angle_entry,
"excellon_editor_sel_limit": self.ui.excellon_defaults_form.excellon_editor_group.sel_limit_entry,
"excellon_editor_newdia": self.ui.excellon_defaults_form.excellon_editor_group.addtool_entry,
"excellon_editor_array_size": self.ui.excellon_defaults_form.excellon_editor_group.drill_array_size_entry,
"excellon_editor_lin_dir": self.ui.excellon_defaults_form.excellon_editor_group.drill_axis_radio,
"excellon_editor_lin_pitch": self.ui.excellon_defaults_form.excellon_editor_group.drill_pitch_entry,
"excellon_editor_lin_angle": self.ui.excellon_defaults_form.excellon_editor_group.drill_angle_entry,
"excellon_editor_circ_dir": self.ui.excellon_defaults_form.excellon_editor_group.drill_circular_dir_radio,
"excellon_editor_circ_angle":
self.ui.excellon_defaults_form.excellon_editor_group.drill_circular_angle_entry,
@ -259,117 +270,94 @@ class PreferencesUIManager:
self.ui.excellon_defaults_form.excellon_editor_group.slot_array_circular_angle_entry,
# Geometry General
"geometry_plot": self.ui.geometry_defaults_form.geometry_gen_group.plot_cb,
"geometry_multicolored": self.ui.geometry_defaults_form.geometry_gen_group.multicolored_cb,
"geometry_circle_steps": self.ui.geometry_defaults_form.geometry_gen_group.circle_steps_entry,
"geometry_cnctooldia": self.ui.geometry_defaults_form.geometry_gen_group.cnctooldia_entry,
"geometry_plot_line": self.ui.geometry_defaults_form.geometry_gen_group.line_color_entry,
"geometry_plot": self.ui.geometry_defaults_form.geometry_gen_group.plot_cb,
"geometry_circle_steps": self.ui.geometry_defaults_form.geometry_gen_group.circle_steps_entry,
"geometry_cnctooldia": self.ui.geometry_defaults_form.geometry_gen_group.cnctooldia_entry,
"geometry_plot_line": self.ui.geometry_defaults_form.geometry_gen_group.line_color_entry,
# Geometry Options
"geometry_cutz": self.ui.geometry_defaults_form.geometry_opt_group.cutz_entry,
"geometry_travelz": self.ui.geometry_defaults_form.geometry_opt_group.travelz_entry,
"geometry_feedrate": self.ui.geometry_defaults_form.geometry_opt_group.cncfeedrate_entry,
"geometry_feedrate_z": self.ui.geometry_defaults_form.geometry_opt_group.feedrate_z_entry,
"geometry_spindlespeed": self.ui.geometry_defaults_form.geometry_opt_group.cncspindlespeed_entry,
"geometry_dwell": self.ui.geometry_defaults_form.geometry_opt_group.dwell_cb,
"geometry_dwelltime": self.ui.geometry_defaults_form.geometry_opt_group.dwelltime_entry,
"geometry_ppname_g": self.ui.geometry_defaults_form.geometry_opt_group.pp_geometry_name_cb,
"geometry_toolchange": self.ui.geometry_defaults_form.geometry_opt_group.toolchange_cb,
"geometry_toolchangez": self.ui.geometry_defaults_form.geometry_opt_group.toolchangez_entry,
"geometry_endz": self.ui.geometry_defaults_form.geometry_opt_group.endz_entry,
"geometry_endxy": self.ui.geometry_defaults_form.geometry_opt_group.endxy_entry,
"geometry_depthperpass": self.ui.geometry_defaults_form.geometry_opt_group.depthperpass_entry,
"geometry_multidepth": self.ui.geometry_defaults_form.geometry_opt_group.multidepth_cb,
"geometry_cutz": self.ui.geometry_defaults_form.geometry_opt_group.cutz_entry,
"geometry_travelz": self.ui.geometry_defaults_form.geometry_opt_group.travelz_entry,
"geometry_feedrate": self.ui.geometry_defaults_form.geometry_opt_group.cncfeedrate_entry,
"geometry_feedrate_z": self.ui.geometry_defaults_form.geometry_opt_group.feedrate_z_entry,
"geometry_spindlespeed": self.ui.geometry_defaults_form.geometry_opt_group.cncspindlespeed_entry,
"geometry_dwell": self.ui.geometry_defaults_form.geometry_opt_group.dwell_cb,
"geometry_dwelltime": self.ui.geometry_defaults_form.geometry_opt_group.dwelltime_entry,
"geometry_ppname_g": self.ui.geometry_defaults_form.geometry_opt_group.pp_geometry_name_cb,
"geometry_toolchange": self.ui.geometry_defaults_form.geometry_opt_group.toolchange_cb,
"geometry_toolchangez": self.ui.geometry_defaults_form.geometry_opt_group.toolchangez_entry,
"geometry_endz": self.ui.geometry_defaults_form.geometry_opt_group.endz_entry,
"geometry_endxy": self.ui.geometry_defaults_form.geometry_opt_group.endxy_entry,
"geometry_depthperpass": self.ui.geometry_defaults_form.geometry_opt_group.depthperpass_entry,
"geometry_multidepth": self.ui.geometry_defaults_form.geometry_opt_group.multidepth_cb,
# Geometry Advanced Options
"geometry_toolchangexy": self.ui.geometry_defaults_form.geometry_adv_opt_group.toolchangexy_entry,
"geometry_startz": self.ui.geometry_defaults_form.geometry_adv_opt_group.gstartz_entry,
"geometry_feedrate_rapid": self.ui.geometry_defaults_form.geometry_adv_opt_group.feedrate_rapid_entry,
"geometry_extracut": self.ui.geometry_defaults_form.geometry_adv_opt_group.extracut_cb,
"geometry_toolchangexy": self.ui.geometry_defaults_form.geometry_adv_opt_group.toolchangexy_entry,
"geometry_startz": self.ui.geometry_defaults_form.geometry_adv_opt_group.gstartz_entry,
"geometry_feedrate_rapid": self.ui.geometry_defaults_form.geometry_adv_opt_group.feedrate_rapid_entry,
"geometry_extracut": self.ui.geometry_defaults_form.geometry_adv_opt_group.extracut_cb,
"geometry_extracut_length": self.ui.geometry_defaults_form.geometry_adv_opt_group.e_cut_entry,
"geometry_z_pdepth": self.ui.geometry_defaults_form.geometry_adv_opt_group.pdepth_entry,
"geometry_feedrate_probe": self.ui.geometry_defaults_form.geometry_adv_opt_group.feedrate_probe_entry,
"geometry_spindledir": self.ui.geometry_defaults_form.geometry_adv_opt_group.spindledir_radio,
"geometry_f_plunge": self.ui.geometry_defaults_form.geometry_adv_opt_group.fplunge_cb,
"geometry_segx": self.ui.geometry_defaults_form.geometry_adv_opt_group.segx_entry,
"geometry_segy": self.ui.geometry_defaults_form.geometry_adv_opt_group.segy_entry,
"geometry_area_exclusion": self.ui.geometry_defaults_form.geometry_adv_opt_group.exclusion_cb,
"geometry_area_shape": self.ui.geometry_defaults_form.geometry_adv_opt_group.area_shape_radio,
"geometry_area_strategy": self.ui.geometry_defaults_form.geometry_adv_opt_group.strategy_radio,
"geometry_area_overz": self.ui.geometry_defaults_form.geometry_adv_opt_group.over_z_entry,
"geometry_z_pdepth": self.ui.geometry_defaults_form.geometry_adv_opt_group.pdepth_entry,
"geometry_feedrate_probe": self.ui.geometry_defaults_form.geometry_adv_opt_group.feedrate_probe_entry,
"geometry_spindledir": self.ui.geometry_defaults_form.geometry_adv_opt_group.spindledir_radio,
"geometry_f_plunge": self.ui.geometry_defaults_form.geometry_adv_opt_group.fplunge_cb,
"geometry_segx": self.ui.geometry_defaults_form.geometry_adv_opt_group.segx_entry,
"geometry_segy": self.ui.geometry_defaults_form.geometry_adv_opt_group.segy_entry,
"geometry_area_exclusion": self.ui.geometry_defaults_form.geometry_adv_opt_group.exclusion_cb,
"geometry_area_shape": self.ui.geometry_defaults_form.geometry_adv_opt_group.area_shape_radio,
"geometry_area_strategy": self.ui.geometry_defaults_form.geometry_adv_opt_group.strategy_radio,
"geometry_area_overz": self.ui.geometry_defaults_form.geometry_adv_opt_group.over_z_entry,
# Geometry Editor
"geometry_editor_sel_limit": self.ui.geometry_defaults_form.geometry_editor_group.sel_limit_entry,
"geometry_editor_milling_type": self.ui.geometry_defaults_form.geometry_editor_group.milling_type_radio,
"geometry_editor_sel_limit": self.ui.geometry_defaults_form.geometry_editor_group.sel_limit_entry,
"geometry_editor_milling_type": self.ui.geometry_defaults_form.geometry_editor_group.milling_type_radio,
# CNCJob General
"cncjob_plot": self.ui.cncjob_defaults_form.cncjob_gen_group.plot_cb,
"cncjob_plot_kind": self.ui.cncjob_defaults_form.cncjob_gen_group.cncplot_method_radio,
"cncjob_annotation": self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_cb,
"cncjob_plot": self.ui.cncjob_defaults_form.cncjob_gen_group.plot_cb,
"cncjob_plot_kind": self.ui.cncjob_defaults_form.cncjob_gen_group.cncplot_method_radio,
"cncjob_annotation": self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_cb,
"cncjob_tooldia": self.ui.cncjob_defaults_form.cncjob_gen_group.tooldia_entry,
"cncjob_coords_type": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_type_radio,
"cncjob_coords_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_dec_entry,
"cncjob_fr_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.fr_dec_entry,
"cncjob_steps_per_circle": self.ui.cncjob_defaults_form.cncjob_gen_group.steps_per_circle_entry,
"cncjob_line_ending": self.ui.cncjob_defaults_form.cncjob_gen_group.line_ending_cb,
"cncjob_plot_line": self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_entry,
"cncjob_plot_fill": self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_entry,
"cncjob_travel_line": self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_entry,
"cncjob_travel_fill": self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_entry,
"cncjob_tooldia": self.ui.cncjob_defaults_form.cncjob_gen_group.tooldia_entry,
"cncjob_coords_type": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_type_radio,
"cncjob_coords_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_dec_entry,
"cncjob_fr_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.fr_dec_entry,
"cncjob_steps_per_circle": self.ui.cncjob_defaults_form.cncjob_gen_group.steps_per_circle_entry,
"cncjob_line_ending": self.ui.cncjob_defaults_form.cncjob_gen_group.line_ending_cb,
"cncjob_plot_line": self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_entry,
"cncjob_plot_fill": self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_entry,
"cncjob_travel_line": self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_entry,
"cncjob_travel_fill": self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_entry,
# CNC Job Options
"cncjob_prepend": self.ui.cncjob_defaults_form.cncjob_opt_group.prepend_text,
"cncjob_append": self.ui.cncjob_defaults_form.cncjob_opt_group.append_text,
"cncjob_prepend": self.ui.cncjob_defaults_form.cncjob_opt_group.prepend_text,
"cncjob_append": self.ui.cncjob_defaults_form.cncjob_opt_group.append_text,
# CNC Job Advanced Options
"cncjob_toolchange_macro": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_text,
"cncjob_toolchange_macro_enable": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_cb,
"cncjob_annotation_fontsize": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontsize_sp,
"cncjob_toolchange_macro": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_text,
"cncjob_toolchange_macro_enable": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_cb,
"cncjob_annotation_fontsize": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontsize_sp,
"cncjob_annotation_fontcolor": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry,
# Isolation Routing Tool
"tools_iso_tooldia": self.ui.tools_defaults_form.tools_iso_group.tool_dia_entry,
"tools_iso_order": self.ui.tools_defaults_form.tools_iso_group.order_radio,
"tools_iso_tool_type": self.ui.tools_defaults_form.tools_iso_group.tool_type_radio,
"tools_iso_tool_vtipdia": self.ui.tools_defaults_form.tools_iso_group.tipdia_entry,
"tools_iso_tool_vtipangle": self.ui.tools_defaults_form.tools_iso_group.tipangle_entry,
"tools_iso_tool_cutz": self.ui.tools_defaults_form.tools_iso_group.cutz_entry,
"tools_iso_newdia": self.ui.tools_defaults_form.tools_iso_group.newdia_entry,
"tools_iso_passes": self.ui.tools_defaults_form.tools_iso_group.passes_entry,
"tools_iso_overlap": self.ui.tools_defaults_form.tools_iso_group.overlap_entry,
"tools_iso_milling_type": self.ui.tools_defaults_form.tools_iso_group.milling_type_radio,
"tools_iso_follow": self.ui.tools_defaults_form.tools_iso_group.follow_cb,
"tools_iso_isotype": self.ui.tools_defaults_form.tools_iso_group.iso_type_radio,
"tools_iso_rest": self.ui.tools_defaults_form.tools_iso_group.rest_cb,
"tools_iso_combine_passes": self.ui.tools_defaults_form.tools_iso_group.combine_passes_cb,
"tools_iso_isoexcept": self.ui.tools_defaults_form.tools_iso_group.except_cb,
"tools_iso_selection": self.ui.tools_defaults_form.tools_iso_group.select_combo,
"tools_iso_area_shape": self.ui.tools_defaults_form.tools_iso_group.area_shape_radio,
"tools_iso_plotting": self.ui.tools_defaults_form.tools_iso_group.plotting_radio,
# NCC Tool
"tools_ncctools": self.ui.tools_defaults_form.tools_ncc_group.ncc_tool_dia_entry,
"tools_nccorder": self.ui.tools_defaults_form.tools_ncc_group.ncc_order_radio,
"tools_nccoverlap": self.ui.tools_defaults_form.tools_ncc_group.ncc_overlap_entry,
"tools_nccmargin": self.ui.tools_defaults_form.tools_ncc_group.ncc_margin_entry,
"tools_nccmethod": self.ui.tools_defaults_form.tools_ncc_group.ncc_method_combo,
"tools_nccconnect": self.ui.tools_defaults_form.tools_ncc_group.ncc_connect_cb,
"tools_ncccontour": self.ui.tools_defaults_form.tools_ncc_group.ncc_contour_cb,
"tools_nccrest": self.ui.tools_defaults_form.tools_ncc_group.ncc_rest_cb,
"tools_ncc_offset_choice": self.ui.tools_defaults_form.tools_ncc_group.ncc_choice_offset_cb,
"tools_ncc_offset_value": self.ui.tools_defaults_form.tools_ncc_group.ncc_offset_spinner,
"tools_nccref": self.ui.tools_defaults_form.tools_ncc_group.select_combo,
"tools_ncc_area_shape": self.ui.tools_defaults_form.tools_ncc_group.area_shape_radio,
"tools_nccmilling_type": self.ui.tools_defaults_form.tools_ncc_group.milling_type_radio,
"tools_ncctool_type": self.ui.tools_defaults_form.tools_ncc_group.tool_type_radio,
"tools_ncccutz": self.ui.tools_defaults_form.tools_ncc_group.cutz_entry,
"tools_ncctipdia": self.ui.tools_defaults_form.tools_ncc_group.tipdia_entry,
"tools_ncctipangle": self.ui.tools_defaults_form.tools_ncc_group.tipangle_entry,
"tools_nccnewdia": self.ui.tools_defaults_form.tools_ncc_group.newdia_entry,
"tools_ncc_plotting": self.ui.tools_defaults_form.tools_ncc_group.plotting_radio,
"tools_ncctools": self.ui.tools_defaults_form.tools_ncc_group.ncc_tool_dia_entry,
"tools_nccorder": self.ui.tools_defaults_form.tools_ncc_group.ncc_order_radio,
"tools_nccoverlap": self.ui.tools_defaults_form.tools_ncc_group.ncc_overlap_entry,
"tools_nccmargin": self.ui.tools_defaults_form.tools_ncc_group.ncc_margin_entry,
"tools_nccmethod": self.ui.tools_defaults_form.tools_ncc_group.ncc_method_combo,
"tools_nccconnect": self.ui.tools_defaults_form.tools_ncc_group.ncc_connect_cb,
"tools_ncccontour": self.ui.tools_defaults_form.tools_ncc_group.ncc_contour_cb,
"tools_nccrest": self.ui.tools_defaults_form.tools_ncc_group.ncc_rest_cb,
"tools_ncc_offset_choice": self.ui.tools_defaults_form.tools_ncc_group.ncc_choice_offset_cb,
"tools_ncc_offset_value": self.ui.tools_defaults_form.tools_ncc_group.ncc_offset_spinner,
"tools_nccref": self.ui.tools_defaults_form.tools_ncc_group.select_combo,
"tools_ncc_area_shape": self.ui.tools_defaults_form.tools_ncc_group.area_shape_radio,
"tools_ncc_plotting": self.ui.tools_defaults_form.tools_ncc_group.ncc_plotting_radio,
"tools_nccmilling_type": self.ui.tools_defaults_form.tools_ncc_group.milling_type_radio,
"tools_ncctool_type": self.ui.tools_defaults_form.tools_ncc_group.tool_type_radio,
"tools_ncccutz": self.ui.tools_defaults_form.tools_ncc_group.cutz_entry,
"tools_ncctipdia": self.ui.tools_defaults_form.tools_ncc_group.tipdia_entry,
"tools_ncctipangle": self.ui.tools_defaults_form.tools_ncc_group.tipangle_entry,
"tools_nccnewdia": self.ui.tools_defaults_form.tools_ncc_group.newdia_entry,
# CutOut Tool
"tools_cutouttooldia": self.ui.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry,
@ -479,12 +467,6 @@ class PreferencesUIManager:
"tools_solderpaste_pp": self.ui.tools_defaults_form.tools_solderpaste_group.pp_combo,
"tools_sub_close_paths": self.ui.tools_defaults_form.tools_sub_group.close_paths_cb,
# Corner Markers Tool
"tools_corners_thickness": self.ui.tools_defaults_form.tools_corners_group.thick_entry,
"tools_corners_length": self.ui.tools_defaults_form.tools_corners_group.l_entry,
"tools_corners_margin": self.ui.tools_defaults_form.tools_corners_group.margin_entry,
# #######################################################################################################
# ########################################## TOOLS 2 ####################################################
# #######################################################################################################
@ -668,7 +650,7 @@ class PreferencesUIManager:
def show_preferences_gui(self):
"""
Called to initialize and show the Preferences AppGUI
Called to initialize and show the Preferences GUI
:return: None
"""
@ -737,7 +719,7 @@ class PreferencesUIManager:
self.ui.fa_scroll_area.setWidget(fa_form)
fa_form.show()
# Initialize the color box's color in Preferences -> Global -> Colors
# Initialize the color box's color in Preferences -> Global -> Colo
self.__init_color_pickers()
# Button handlers
@ -750,90 +732,167 @@ class PreferencesUIManager:
def __init_color_pickers(self):
# Init Gerber Plot Colors
self.ui.gerber_defaults_form.gerber_gen_group.fill_color_entry.set_value(self.defaults['gerber_plot_fill'])
self.ui.gerber_defaults_form.gerber_gen_group.line_color_entry.set_value(self.defaults['gerber_plot_line'])
self.ui.gerber_defaults_form.gerber_gen_group.pf_color_entry.set_value(self.defaults['gerber_plot_fill'])
self.ui.gerber_defaults_form.gerber_gen_group.pf_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['gerber_plot_fill'])[:7])
self.ui.gerber_defaults_form.gerber_gen_group.pf_color_alpha_spinner.set_value(
int(self.defaults['gerber_plot_fill'][7:9], 16))
self.ui.gerber_defaults_form.gerber_gen_group.pf_color_alpha_slider.setValue(
int(self.defaults['gerber_plot_fill'][7:9], 16))
self.ui.gerber_defaults_form.gerber_gen_group.gerber_alpha_entry.set_value(
int(self.defaults['gerber_plot_fill'][7:9], 16)) # alpha
self.ui.gerber_defaults_form.gerber_gen_group.pl_color_entry.set_value(self.defaults['gerber_plot_line'])
self.ui.gerber_defaults_form.gerber_gen_group.pl_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['gerber_plot_line'])[:7])
# Init Excellon Plot Colors
self.ui.excellon_defaults_form.excellon_gen_group.fill_color_entry.set_value(
self.defaults['excellon_plot_fill'])
self.ui.excellon_defaults_form.excellon_gen_group.fill_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['excellon_plot_fill'])[:7])
self.ui.excellon_defaults_form.excellon_gen_group.color_alpha_spinner.set_value(
int(self.defaults['excellon_plot_fill'][7:9], 16))
self.ui.excellon_defaults_form.excellon_gen_group.color_alpha_slider.setValue(
int(self.defaults['excellon_plot_fill'][7:9], 16))
self.ui.excellon_defaults_form.excellon_gen_group.line_color_entry.set_value(
self.defaults['excellon_plot_line'])
self.ui.excellon_defaults_form.excellon_gen_group.excellon_alpha_entry.set_value(
int(self.defaults['excellon_plot_fill'][7:9], 16))
self.ui.excellon_defaults_form.excellon_gen_group.line_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['excellon_plot_line'])[:7])
# Init Geometry Plot Colors
self.ui.geometry_defaults_form.geometry_gen_group.line_color_entry.set_value(
self.defaults['geometry_plot_line'])
self.ui.geometry_defaults_form.geometry_gen_group.line_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['geometry_plot_line'])[:7])
# Init CNCJob Travel Line Colors
self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_entry.set_value(
self.defaults['cncjob_travel_fill'])
self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['cncjob_travel_fill'])[:7])
self.ui.cncjob_defaults_form.cncjob_gen_group.tcolor_alpha_spinner.set_value(
int(self.defaults['cncjob_travel_fill'][7:9], 16))
self.ui.cncjob_defaults_form.cncjob_gen_group.tcolor_alpha_slider.setValue(
int(self.defaults['cncjob_travel_fill'][7:9], 16))
self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_entry.set_value(
self.defaults['cncjob_travel_line'])
self.ui.cncjob_defaults_form.cncjob_gen_group.cncjob_alpha_entry.set_value(
int(self.defaults['cncjob_travel_fill'][7:9], 16)) # alpha
self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['cncjob_travel_line'])[:7])
# Init CNCJob Plot Colors
self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_entry.set_value(
self.defaults['cncjob_plot_fill'])
self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['cncjob_plot_fill'])[:7])
self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_entry.set_value(
self.defaults['cncjob_plot_line'])
self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['cncjob_plot_line'])[:7])
# Init Left-Right Selection colors
self.ui.general_defaults_form.general_gui_group.sf_color_entry.set_value(self.defaults['global_sel_fill'])
self.ui.general_defaults_form.general_gui_group.sl_color_entry.set_value(self.defaults['global_sel_line'])
self.ui.general_defaults_form.general_gui_group.left_right_alpha_entry.set_value(
self.ui.general_defaults_form.general_gui_group.sf_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['global_sel_fill'])[:7])
self.ui.general_defaults_form.general_gui_group.sf_color_alpha_spinner.set_value(
int(self.defaults['global_sel_fill'][7:9], 16))
self.ui.general_defaults_form.general_gui_group.sf_color_alpha_slider.setValue(
int(self.defaults['global_sel_fill'][7:9], 16))
self.ui.general_defaults_form.general_gui_group.sl_color_entry.set_value(self.defaults['global_sel_line'])
self.ui.general_defaults_form.general_gui_group.sl_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['global_sel_line'])[:7])
# Init Right-Left Selection colors
self.ui.general_defaults_form.general_gui_group.alt_sf_color_entry.set_value(
self.defaults['global_alt_sel_fill'])
self.ui.general_defaults_form.general_gui_group.alt_sf_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['global_alt_sel_fill'])[:7])
self.ui.general_defaults_form.general_gui_group.alt_sf_color_alpha_spinner.set_value(
int(self.defaults['global_sel_fill'][7:9], 16))
self.ui.general_defaults_form.general_gui_group.alt_sf_color_alpha_slider.setValue(
int(self.defaults['global_sel_fill'][7:9], 16))
self.ui.general_defaults_form.general_gui_group.alt_sl_color_entry.set_value(
self.defaults['global_alt_sel_line'])
self.ui.general_defaults_form.general_gui_group.right_left_alpha_entry.set_value(
int(self.defaults['global_sel_fill'][7:9], 16))
self.ui.general_defaults_form.general_gui_group.alt_sl_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['global_alt_sel_line'])[:7])
# Init Draw color and Selection Draw Color
self.ui.general_defaults_form.general_gui_group.draw_color_entry.set_value(
self.defaults['global_draw_color'])
self.ui.general_defaults_form.general_gui_group.draw_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['global_draw_color'])[:7])
self.ui.general_defaults_form.general_gui_group.sel_draw_color_entry.set_value(
self.defaults['global_sel_draw_color'])
self.ui.general_defaults_form.general_gui_group.sel_draw_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['global_sel_draw_color'])[:7])
# Init Project Items color
self.ui.general_defaults_form.general_gui_group.proj_color_entry.set_value(
self.defaults['global_proj_item_color'])
self.ui.general_defaults_form.general_gui_group.proj_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['global_proj_item_color'])[:7])
# Init Project Disabled Items color
self.ui.general_defaults_form.general_gui_group.proj_color_dis_entry.set_value(
self.defaults['global_proj_item_dis_color'])
self.ui.general_defaults_form.general_gui_group.proj_color_dis_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['global_proj_item_dis_color'])[:7])
# Init Mouse Cursor color
# Init Project Disabled Items color
self.ui.general_defaults_form.general_app_set_group.mouse_cursor_entry.set_value(
self.defaults['global_cursor_color'])
self.ui.general_defaults_form.general_app_set_group.mouse_cursor_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['global_cursor_color'])[:7])
# Init the Annotation CNC Job color
self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry.set_value(
self.defaults['cncjob_annotation_fontcolor'])
self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['cncjob_annotation_fontcolor'])[:7])
# Init the Tool Film color
self.ui.tools_defaults_form.tools_film_group.film_color_entry.set_value(
self.defaults['tools_film_color'])
self.ui.tools_defaults_form.tools_film_group.film_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['tools_film_color'])[:7]
)
# Init the Tool QRCode colors
self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry.set_value(
self.defaults['tools_qrcode_fill_color'])
self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['tools_qrcode_fill_color'])[:7])
self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry.set_value(
self.defaults['tools_qrcode_back_color'])
self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_button.setStyleSheet(
"background-color:%s;"
"border-color: dimgray" % str(self.defaults['tools_qrcode_back_color'])[:7])
def on_save_button(self, save_to_file=True):
log.debug("on_save_button() --> Applying preferences to file.")
@ -862,17 +921,12 @@ class PreferencesUIManager:
theme = 'white'
should_restart = False
theme_new_val = self.ui.general_defaults_form.general_gui_group.theme_radio.get_value()
ge = self.defaults["global_graphic_engine"]
ge_val = self.ui.general_defaults_form.general_app_group.ge_radio.get_value()
if theme_new_val != theme or ge != ge_val:
val = self.ui.general_defaults_form.general_gui_group.theme_radio.get_value()
if val != theme:
msgbox = QtWidgets.QMessageBox()
msgbox.setText(_("Are you sure you want to continue?"))
msgbox.setWindowTitle(_("Application will restart"))
msgbox.setWindowTitle(_("Application restart"))
msgbox.setWindowIcon(QtGui.QIcon(self.ui.app.resource_location + '/warning.png'))
msgbox.setIcon(QtWidgets.QMessageBox.Question)
bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
msgbox.addButton(_('Cancel'), QtWidgets.QMessageBox.NoRole)
@ -881,22 +935,15 @@ class PreferencesUIManager:
msgbox.exec_()
response = msgbox.clickedButton()
if theme_new_val != theme:
if response == bt_yes:
theme_settings.setValue('theme', theme_new_val)
if response == bt_yes:
theme_settings.setValue('theme', val)
# This will write the setting to the platform specific storage.
del theme_settings
# This will write the setting to the platform specific storage.
del theme_settings
should_restart = True
else:
self.ui.general_defaults_form.general_gui_group.theme_radio.set_value(theme)
should_restart = True
else:
if response == bt_yes:
self.defaults["global_graphic_engine"] = ge_val
should_restart = True
else:
self.ui.general_defaults_form.general_app_group.ge_radio.set_value(ge)
self.ui.general_defaults_form.general_gui_group.theme_radio.set_value(theme)
if save_to_file or should_restart is True:
self.save_defaults(silent=False)
@ -920,10 +967,6 @@ class PreferencesUIManager:
tb_fsize = self.ui.general_defaults_form.general_app_set_group.textbox_font_size_spinner.get_value()
settgs.setValue('textbox_font_size', tb_fsize)
# save the HUD font size
hud_fsize = self.ui.general_defaults_form.general_app_set_group.hud_font_size_spinner.get_value()
settgs.setValue('hud_font_size', hud_fsize)
settgs.setValue(
'machinist',
1 if self.ui.general_defaults_form.general_app_set_group.machinist_cb.get_value() else 0
@ -948,14 +991,10 @@ class PreferencesUIManager:
self.preferences_changed_flag = False
self.ignore_tab_close_event = True
# restore stylesheet to default for the statusBar icon
self.ui.pref_status_label.setStyleSheet("")
try:
self.ui.general_defaults_form.general_app_group.units_radio.activated_custom.disconnect()
except (TypeError, AttributeError):
pass
self.defaults_write_form(source_dict=self.defaults.current_defaults)
self.ui.general_defaults_form.general_app_group.units_radio.activated_custom.connect(
lambda: self.ui.app.on_toggle_units(no_pref=False))
@ -996,7 +1035,6 @@ class PreferencesUIManager:
:return: None
"""
self.defaults.report_usage("save_defaults")
log.debug("App.PreferencesUIManager.save_defaults()")
if data_path is None:
data_path = self.data_path
@ -1033,7 +1071,7 @@ class PreferencesUIManager:
if self.ui.toolbarfile.isVisible():
tb_status += 1
if self.ui.toolbaredit.isVisible():
if self.ui.toolbargeo.isVisible():
tb_status += 2
if self.ui.toolbarview.isVisible():
@ -1051,7 +1089,7 @@ class PreferencesUIManager:
if self.ui.grb_edit_toolbar.isVisible():
tb_status += 64
if self.ui.status_toolbar.isVisible():
if self.ui.snap_toolbar.isVisible():
tb_status += 128
if self.ui.toolbarshell.isVisible():
@ -1080,9 +1118,6 @@ class PreferencesUIManager:
if self.ignore_tab_close_event:
return
# restore stylesheet to default for the statusBar icon
self.ui.pref_status_label.setStyleSheet("")
# disconnect
for idx in range(self.ui.pref_tab_area.count()):
for tb in self.ui.pref_tab_area.widget(idx).findChildren(QtCore.QObject):
@ -1118,7 +1153,6 @@ class PreferencesUIManager:
"Do you want to save the Preferences?"))
msgbox.setWindowTitle(_("Save Preferences"))
msgbox.setWindowIcon(QtGui.QIcon(self.ui.app.resource_location + '/save_as.png'))
msgbox.setIcon(QtWidgets.QMessageBox.Question)
bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)

View File

@ -1,6 +1,6 @@
from AppGUI.GUIElements import *
from flatcamGUI.GUIElements import *
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins

View File

@ -1,10 +1,10 @@
from PyQt5 import QtWidgets, QtGui
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtCore import QSettings, Qt
from AppGUI.GUIElements import FCTextArea, FCCheckBox, FCComboBox, FCSpinner, FCColorEntry
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.GUIElements import FCTextArea, FCCheckBox, FCComboBox, FCSpinner, FCEntry
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -158,16 +158,28 @@ class CNCJobAdvOptPrefGroupUI(OptionsGroupUI):
self.annotation_color_label.setToolTip(
_("Set the font color for the annotation texts.")
)
self.annotation_fontcolor_entry = FCColorEntry()
self.annotation_fontcolor_entry = FCEntry()
self.annotation_fontcolor_button = QtWidgets.QPushButton()
self.annotation_fontcolor_button.setFixedSize(15, 15)
self.form_box_child = QtWidgets.QHBoxLayout()
self.form_box_child.setContentsMargins(0, 0, 0, 0)
self.form_box_child.addWidget(self.annotation_fontcolor_entry)
self.form_box_child.addWidget(self.annotation_fontcolor_button, alignment=Qt.AlignRight)
self.form_box_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
color_widget = QtWidgets.QWidget()
color_widget.setLayout(self.form_box_child)
grid0.addWidget(self.annotation_color_label, 3, 0)
grid0.addWidget(self.annotation_fontcolor_entry, 3, 1)
grid0.addWidget(color_widget, 3, 1)
grid0.addWidget(QtWidgets.QLabel(''), 3, 2)
self.layout.addStretch()
self.tc_variable_combo.currentIndexChanged[str].connect(self.on_cnc_custom_parameters)
self.annotation_fontcolor_entry.editingFinished.connect(self.on_annotation_fontcolor_entry)
self.annotation_fontcolor_button.clicked.connect(self.on_annotation_fontcolor_button)
def on_cnc_custom_parameters(self, signal_text):
if signal_text == 'Parameters':
@ -177,3 +189,20 @@ class CNCJobAdvOptPrefGroupUI(OptionsGroupUI):
def on_annotation_fontcolor_entry(self):
self.app.defaults['cncjob_annotation_fontcolor'] = self.annotation_fontcolor_entry.get_value()
self.annotation_fontcolor_button.setStyleSheet(
"background-color:%s" % str(self.app.defaults['cncjob_annotation_fontcolor']))
def on_annotation_fontcolor_button(self):
current_color = QtGui.QColor(self.app.defaults['cncjob_annotation_fontcolor'])
c_dialog = QtWidgets.QColorDialog()
annotation_color = c_dialog.getColor(initial=current_color)
if annotation_color.isValid() is False:
return
self.annotation_fontcolor_button.setStyleSheet("background-color:%s" % str(annotation_color.name()))
new_val_sel = str(annotation_color.name())
self.annotation_fontcolor_entry.set_value(new_val_sel)
self.app.defaults['cncjob_annotation_fontcolor'] = new_val_sel

View File

@ -1,10 +1,10 @@
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import FCCheckBox, RadioSet, FCSpinner, FCDoubleSpinner, FCSliderWithSpinner, FCColorEntry
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.GUIElements import FCCheckBox, RadioSet, FCSpinner, FCDoubleSpinner, FCEntry
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -170,10 +170,17 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
self.tline_color_label.setToolTip(
_("Set the travel line color for plotted objects.")
)
self.tline_color_entry = FCColorEntry()
self.tline_color_entry = FCEntry()
self.tline_color_button = QtWidgets.QPushButton()
self.tline_color_button.setFixedSize(15, 15)
self.form_box_child_2 = QtWidgets.QHBoxLayout()
self.form_box_child_2.addWidget(self.tline_color_entry)
self.form_box_child_2.addWidget(self.tline_color_button)
self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid0.addWidget(self.tline_color_label, 14, 0)
grid0.addWidget(self.tline_color_entry, 14, 1)
grid0.addLayout(self.form_box_child_2, 14, 1)
# Plot Fill Color
self.tfill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
@ -182,20 +189,38 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
"First 6 digits are the color and the last 2\n"
"digits are for alpha (transparency) level.")
)
self.tfill_color_entry = FCColorEntry()
self.tfill_color_entry = FCEntry()
self.tfill_color_button = QtWidgets.QPushButton()
self.tfill_color_button.setFixedSize(15, 15)
self.form_box_child_1 = QtWidgets.QHBoxLayout()
self.form_box_child_1.addWidget(self.tfill_color_entry)
self.form_box_child_1.addWidget(self.tfill_color_button)
self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid0.addWidget(self.tfill_color_label, 15, 0)
grid0.addWidget(self.tfill_color_entry, 15, 1)
grid0.addLayout(self.form_box_child_1, 15, 1)
# Plot Fill Transparency Level
self.cncjob_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
self.cncjob_alpha_label.setToolTip(
self.alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
self.alpha_label.setToolTip(
_("Set the fill transparency for plotted objects.")
)
self.cncjob_alpha_entry = FCSliderWithSpinner(0, 255, 1)
self.tcolor_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.tcolor_alpha_slider.setMinimum(0)
self.tcolor_alpha_slider.setMaximum(255)
self.tcolor_alpha_slider.setSingleStep(1)
grid0.addWidget(self.cncjob_alpha_label, 16, 0)
grid0.addWidget(self.cncjob_alpha_entry, 16, 1)
self.tcolor_alpha_spinner = FCSpinner()
self.tcolor_alpha_spinner.setMinimumWidth(70)
self.tcolor_alpha_spinner.set_range(0, 255)
self.form_box_child_3 = QtWidgets.QHBoxLayout()
self.form_box_child_3.addWidget(self.tcolor_alpha_slider)
self.form_box_child_3.addWidget(self.tcolor_alpha_spinner)
grid0.addWidget(self.alpha_label, 16, 0)
grid0.addLayout(self.form_box_child_3, 16, 1)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
@ -203,7 +228,7 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
grid0.addWidget(separator_line, 17, 0, 1, 2)
# CNCJob Object Color
self.cnc_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Object Color'))
self.cnc_color_label = QtWidgets.QLabel('<b>%s</b>' % _('CNCJob Object Color'))
grid0.addWidget(self.cnc_color_label, 18, 0, 1, 2)
# Plot Line Color
@ -211,10 +236,17 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
self.line_color_label.setToolTip(
_("Set the color for plotted objects.")
)
self.line_color_entry = FCColorEntry()
self.line_color_entry = FCEntry()
self.line_color_button = QtWidgets.QPushButton()
self.line_color_button.setFixedSize(15, 15)
self.form_box_child_2 = QtWidgets.QHBoxLayout()
self.form_box_child_2.addWidget(self.line_color_entry)
self.form_box_child_2.addWidget(self.line_color_button)
self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid0.addWidget(self.line_color_label, 19, 0)
grid0.addWidget(self.line_color_entry, 19, 1)
grid0.addLayout(self.form_box_child_2, 19, 1)
# Plot Fill Color
self.fill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
@ -223,21 +255,32 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
"First 6 digits are the color and the last 2\n"
"digits are for alpha (transparency) level.")
)
self.fill_color_entry = FCColorEntry()
self.fill_color_entry = FCEntry()
self.fill_color_button = QtWidgets.QPushButton()
self.fill_color_button.setFixedSize(15, 15)
self.form_box_child_1 = QtWidgets.QHBoxLayout()
self.form_box_child_1.addWidget(self.fill_color_entry)
self.form_box_child_1.addWidget(self.fill_color_button)
self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid0.addWidget(self.fill_color_label, 20, 0)
grid0.addWidget(self.fill_color_entry, 20, 1)
grid0.addLayout(self.form_box_child_1, 20, 1)
self.layout.addStretch()
# Setting plot colors signals
self.tline_color_entry.editingFinished.connect(self.on_tline_color_entry)
self.tline_color_button.clicked.connect(self.on_tline_color_button)
self.tfill_color_entry.editingFinished.connect(self.on_tfill_color_entry)
self.cncjob_alpha_entry.valueChanged.connect(self.on_cncjob_alpha_changed) # alpha
self.tfill_color_button.clicked.connect(self.on_tfill_color_button)
self.tcolor_alpha_spinner.valueChanged.connect(self.on_tcolor_spinner)
self.tcolor_alpha_slider.valueChanged.connect(self.on_tcolor_slider)
self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
self.line_color_button.clicked.connect(self.on_line_color_button)
self.fill_color_entry.editingFinished.connect(self.on_fill_color_entry)
self.fill_color_button.clicked.connect(self.on_fill_color_button)
# ------------------------------------------------------
# Setting travel colors handlers
@ -245,12 +288,27 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
def on_tfill_color_entry(self):
self.app.defaults['cncjob_travel_fill'] = self.tfill_color_entry.get_value()[:7] + \
self.app.defaults['cncjob_travel_fill'][7:9]
self.tfill_color_button.setStyleSheet(
"background-color:%s" % str(self.app.defaults['cncjob_travel_fill'])[:7])
def on_tline_color_entry(self):
self.app.defaults['cncjob_travel_line'] = self.tline_color_entry.get_value()[:7] + \
self.app.defaults['cncjob_travel_line'][7:9]
def on_tfill_color_button(self):
current_color = QtGui.QColor(self.app.defaults['cncjob_travel_fill'][:7])
def on_cncjob_alpha_changed(self, spinner_value):
c_dialog = QtWidgets.QColorDialog()
plot_fill_color = c_dialog.getColor(initial=current_color)
if plot_fill_color.isValid() is False:
return
self.tfill_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
new_val = str(plot_fill_color.name()) + str(self.app.defaults['cncjob_travel_fill'][7:9])
self.tfill_color_entry.set_value(new_val)
self.app.defaults['cncjob_travel_fill'] = new_val
def on_tcolor_spinner(self):
spinner_value = self.tcolor_alpha_spinner.value()
self.tcolor_alpha_slider.setValue(spinner_value)
self.app.defaults['cncjob_travel_fill'] = \
self.app.defaults['cncjob_travel_fill'][:7] + \
(hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
@ -258,13 +316,74 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
self.app.defaults['cncjob_travel_line'][:7] + \
(hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
def on_tcolor_slider(self):
slider_value = self.tcolor_alpha_slider.value()
self.tcolor_alpha_spinner.setValue(slider_value)
def on_tline_color_entry(self):
self.app.defaults['cncjob_travel_line'] = self.tline_color_entry.get_value()[:7] + \
self.app.defaults['cncjob_travel_line'][7:9]
self.tline_color_button.setStyleSheet(
"background-color:%s" % str(self.app.defaults['cncjob_travel_line'])[:7])
def on_tline_color_button(self):
current_color = QtGui.QColor(self.app.defaults['cncjob_travel_line'][:7])
# print(current_color)
c_dialog = QtWidgets.QColorDialog()
plot_line_color = c_dialog.getColor(initial=current_color)
if plot_line_color.isValid() is False:
return
self.tline_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
new_val_line = str(plot_line_color.name()) + str(self.app.defaults['cncjob_travel_line'][7:9])
self.tline_color_entry.set_value(new_val_line)
self.app.defaults['cncjob_travel_line'] = new_val_line
# ------------------------------------------------------
# Setting plot colors handlers
# ------------------------------------------------------
def on_fill_color_entry(self):
self.app.defaults['cncjob_plot_fill'] = self.fill_color_entry.get_value()[:7] + \
self.app.defaults['cncjob_plot_fill'][7:9]
self.fill_color_button.setStyleSheet(
"background-color:%s" % str(self.app.defaults['cncjob_plot_fill'])[:7])
def on_fill_color_button(self):
current_color = QtGui.QColor(self.app.defaults['cncjob_plot_fill'][:7])
c_dialog = QtWidgets.QColorDialog()
plot_fill_color = c_dialog.getColor(initial=current_color)
if plot_fill_color.isValid() is False:
return
self.fill_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
new_val = str(plot_fill_color.name()) + str(self.app.defaults['cncjob_plot_fill'][7:9])
self.fill_color_entry.set_value(new_val)
self.app.defaults['cncjob_plot_fill'] = new_val
def on_line_color_entry(self):
self.app.defaults['cncjob_plot_line'] = self.line_color_entry.get_value()[:7] + \
self.app.defaults['cncjob_plot_line'][7:9]
self.line_color_button.setStyleSheet(
"background-color:%s" % str(self.app.defaults['cncjob_plot_line'])[:7])
def on_line_color_button(self):
current_color = QtGui.QColor(self.app.defaults['cncjob_plot_line'][:7])
# print(current_color)
c_dialog = QtWidgets.QColorDialog()
plot_line_color = c_dialog.getColor(initial=current_color)
if plot_line_color.isValid() is False:
return
self.line_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
new_val_line = str(plot_line_color.name()) + str(self.app.defaults['cncjob_plot_line'][7:9])
self.line_color_entry.set_value(new_val_line)
self.app.defaults['cncjob_plot_line'] = new_val_line

View File

@ -1,11 +1,11 @@
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import FCTextArea
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.GUIElements import FCTextArea
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')

View File

@ -1,8 +1,8 @@
from PyQt5 import QtWidgets
from AppGUI.preferences.cncjob.CNCJobAdvOptPrefGroupUI import CNCJobAdvOptPrefGroupUI
from AppGUI.preferences.cncjob.CNCJobOptPrefGroupUI import CNCJobOptPrefGroupUI
from AppGUI.preferences.cncjob.CNCJobGenPrefGroupUI import CNCJobGenPrefGroupUI
from flatcamGUI.preferences.cncjob.CNCJobAdvOptPrefGroupUI import CNCJobAdvOptPrefGroupUI
from flatcamGUI.preferences.cncjob.CNCJobOptPrefGroupUI import CNCJobOptPrefGroupUI
from flatcamGUI.preferences.cncjob.CNCJobGenPrefGroupUI import CNCJobGenPrefGroupUI
class CNCJobPreferencesUI(QtWidgets.QWidget):

View File

@ -1,10 +1,10 @@
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import FCDoubleSpinner, RadioSet, FCCheckBox, NumericalEvalTupleEntry, NumericalEvalEntry
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.GUIElements import FCDoubleSpinner, FCEntry, FloatEntry, RadioSet, FCCheckBox
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -60,7 +60,7 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI):
toolchange_xy_label.setToolTip(
_("Toolchange X,Y position.")
)
self.toolchangexy_entry = NumericalEvalTupleEntry(border_color='#0069A9')
self.toolchangexy_entry = FCEntry()
grid1.addWidget(toolchange_xy_label, 1, 0)
grid1.addWidget(self.toolchangexy_entry, 1, 1)
@ -71,7 +71,7 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI):
_("Height of the tool just after start.\n"
"Delete the value if you don't need this feature.")
)
self.estartz_entry = NumericalEvalEntry(border_color='#0069A9')
self.estartz_entry = FloatEntry()
grid1.addWidget(startzlabel, 2, 0)
grid1.addWidget(self.estartz_entry, 2, 1)

View File

@ -1,11 +1,11 @@
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')

View File

@ -1,10 +1,10 @@
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import RadioSet, FCSpinner
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.GUIElements import RadioSet, FCSpinner
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')

View File

@ -3,10 +3,10 @@ import platform
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import FCCheckBox, FCSpinner, RadioSet, FCEntry, FCSliderWithSpinner, FCColorEntry
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.GUIElements import FCCheckBox, FCSpinner, RadioSet, FCEntry
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -36,31 +36,22 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
grid1 = QtWidgets.QGridLayout()
self.layout.addLayout(grid1)
# Plot CB
self.plot_cb = FCCheckBox(label=_('Plot'))
self.plot_cb.setToolTip(
"Plot (show) this object."
)
grid1.addWidget(self.plot_cb, 0, 0)
# Solid CB
self.solid_cb = FCCheckBox(label=_('Solid'))
self.solid_cb.setToolTip(
"Plot as solid circles."
)
grid1.addWidget(self.solid_cb, 0, 1)
# Multicolored CB
self.multicolored_cb = FCCheckBox(label='%s' % _('M-Color'))
self.multicolored_cb.setToolTip(
_("Draw polygons in different colors.")
)
grid1.addWidget(self.multicolored_cb, 0, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid1.addWidget(separator_line, 1, 0, 1, 3)
grid1.addWidget(separator_line, 1, 0, 1, 2)
grid2 = QtWidgets.QGridLayout()
self.layout.addLayout(grid2)
@ -264,7 +255,7 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
grid2.addWidget(separator_line, 11, 0, 1, 2)
# Excellon Object Color
self.gerber_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Object Color'))
self.gerber_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Excellon Object Color'))
grid2.addWidget(self.gerber_color_label, 12, 0, 1, 2)
# Plot Line Color
@ -272,10 +263,17 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
self.line_color_label.setToolTip(
_("Set the line color for plotted objects.")
)
self.line_color_entry = FCColorEntry()
self.line_color_entry = FCEntry()
self.line_color_button = QtWidgets.QPushButton()
self.line_color_button.setFixedSize(15, 15)
self.form_box_child_2 = QtWidgets.QHBoxLayout()
self.form_box_child_2.addWidget(self.line_color_entry)
self.form_box_child_2.addWidget(self.line_color_button)
self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid2.addWidget(self.line_color_label, 13, 0)
grid2.addWidget(self.line_color_entry, 13, 1)
grid2.addLayout(self.form_box_child_2, 13, 1)
# Plot Fill Color
self.fill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
@ -284,20 +282,38 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
"First 6 digits are the color and the last 2\n"
"digits are for alpha (transparency) level.")
)
self.fill_color_entry = FCColorEntry()
self.fill_color_entry = FCEntry()
self.fill_color_button = QtWidgets.QPushButton()
self.fill_color_button.setFixedSize(15, 15)
self.form_box_child_1 = QtWidgets.QHBoxLayout()
self.form_box_child_1.addWidget(self.fill_color_entry)
self.form_box_child_1.addWidget(self.fill_color_button)
self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid2.addWidget(self.fill_color_label, 14, 0)
grid2.addWidget(self.fill_color_entry, 14, 1)
grid2.addLayout(self.form_box_child_1, 14, 1)
# Plot Fill Transparency Level
self.excellon_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
self.excellon_alpha_label.setToolTip(
self.alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
self.alpha_label.setToolTip(
_("Set the fill transparency for plotted objects.")
)
self.excellon_alpha_entry = FCSliderWithSpinner(0, 255, 1)
self.color_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.color_alpha_slider.setMinimum(0)
self.color_alpha_slider.setMaximum(255)
self.color_alpha_slider.setSingleStep(1)
grid2.addWidget(self.excellon_alpha_label, 15, 0)
grid2.addWidget(self.excellon_alpha_entry, 15, 1)
self.color_alpha_spinner = FCSpinner()
self.color_alpha_spinner.setMinimumWidth(70)
self.color_alpha_spinner.set_range(0, 255)
self.form_box_child_3 = QtWidgets.QHBoxLayout()
self.form_box_child_3.addWidget(self.color_alpha_slider)
self.form_box_child_3.addWidget(self.color_alpha_spinner)
grid2.addWidget(self.alpha_label, 15, 0)
grid2.addLayout(self.form_box_child_3, 15, 1)
self.layout.addStretch()
@ -317,18 +333,14 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
# Setting plot colors signals
self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
self.line_color_button.clicked.connect(self.on_line_color_button)
self.fill_color_entry.editingFinished.connect(self.on_fill_color_entry)
self.excellon_alpha_entry.valueChanged.connect(self.on_excellon_alpha_changed) # alpha
self.fill_color_button.clicked.connect(self.on_fill_color_button)
self.color_alpha_spinner.valueChanged.connect(self.on_color_spinner)
self.color_alpha_slider.valueChanged.connect(self.on_color_slider)
# Load the defaults values into the Excellon Format and Excellon Zeros fields
self.excellon_defaults_button.clicked.connect(self.on_excellon_defaults_button)
# Make sure that when the Excellon loading parameters are changed, the change is reflected in the
# Export Excellon parameters.
self.update_excellon_cb.stateChanged.connect(self.on_update_exc_export)
# call it once to make sure it is updated at startup
self.on_update_exc_export(state=self.app.defaults["excellon_update"])
def optimization_selection(self):
if self.excellon_optimization_radio.get_value() == 'M':
@ -342,12 +354,26 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
def on_fill_color_entry(self):
self.app.defaults['excellon_plot_fill'] = self.fill_color_entry.get_value()[:7] + \
self.app.defaults['excellon_plot_fill'][7:9]
self.fill_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['excellon_plot_fill'])[:7])
def on_line_color_entry(self):
self.app.defaults['excellon_plot_line'] = self.line_color_entry.get_value()[:7] + \
self.app.defaults['excellon_plot_line'][7:9]
def on_fill_color_button(self):
current_color = QtGui.QColor(self.app.defaults['excellon_plot_fill'][:7])
def on_excellon_alpha_changed(self, spinner_value):
c_dialog = QtWidgets.QColorDialog()
plot_fill_color = c_dialog.getColor(initial=current_color)
if plot_fill_color.isValid() is False:
return
self.fill_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
new_val = str(plot_fill_color.name()) + str(self.app.defaults['excellon_plot_fill'][7:9])
self.fill_color_entry.set_value(new_val)
self.app.defaults['excellon_plot_fill'] = new_val
def on_color_spinner(self):
spinner_value = self.color_alpha_spinner.value()
self.color_alpha_slider.setValue(spinner_value)
self.app.defaults['excellon_plot_fill'] = \
self.app.defaults['excellon_plot_fill'][:7] + \
(hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
@ -355,6 +381,31 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
self.app.defaults['excellon_plot_line'][:7] + \
(hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
def on_color_slider(self):
slider_value = self.color_alpha_slider.value()
self.color_alpha_spinner.setValue(slider_value)
def on_line_color_entry(self):
self.app.defaults['excellon_plot_line'] = self.line_color_entry.get_value()[:7] + \
self.app.defaults['excellon_plot_line'][7:9]
self.line_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['excellon_plot_line'])[:7])
def on_line_color_button(self):
current_color = QtGui.QColor(self.app.defaults['excellon_plot_line'][:7])
# print(current_color)
c_dialog = QtWidgets.QColorDialog()
plot_line_color = c_dialog.getColor(initial=current_color)
if plot_line_color.isValid() is False:
return
self.line_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
new_val_line = str(plot_line_color.name()) + str(self.app.defaults['excellon_plot_line'][7:9])
self.line_color_entry.set_value(new_val_line)
self.app.defaults['excellon_plot_line'] = new_val_line
def on_excellon_defaults_button(self):
self.app.preferencesUiManager.defaults_form_fields["excellon_format_lower_in"].set_value('4')
self.app.preferencesUiManager.defaults_form_fields["excellon_format_upper_in"].set_value('2')
@ -362,105 +413,3 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
self.app.preferencesUiManager.defaults_form_fields["excellon_format_upper_mm"].set_value('3')
self.app.preferencesUiManager.defaults_form_fields["excellon_zeros"].set_value('L')
self.app.preferencesUiManager.defaults_form_fields["excellon_units"].set_value('INCH')
def on_update_exc_export(self, state):
"""
This is handling the update of Excellon Export parameters based on the ones in the Excellon General but only
if the update_excellon_cb checkbox is checked
:param state: state of the checkbox whose signals is tied to his slot
:return:
"""
if state:
# first try to disconnect
try:
self.excellon_format_upper_in_entry.returnPressed.disconnect(self.on_excellon_format_changed)
except TypeError:
pass
try:
self.excellon_format_lower_in_entry.returnPressed.disconnect(self.on_excellon_format_changed)
except TypeError:
pass
try:
self.excellon_format_upper_mm_entry.returnPressed.disconnect(self.on_excellon_format_changed)
except TypeError:
pass
try:
self.excellon_format_lower_mm_entry.returnPressed.disconnect(self.on_excellon_format_changed)
except TypeError:
pass
try:
self.excellon_zeros_radio.activated_custom.disconnect(self.on_excellon_zeros_changed)
except TypeError:
pass
try:
self.excellon_units_radio.activated_custom.disconnect(self.on_excellon_zeros_changed)
except TypeError:
pass
# the connect them
self.excellon_format_upper_in_entry.returnPressed.connect(self.on_excellon_format_changed)
self.excellon_format_lower_in_entry.returnPressed.connect(self.on_excellon_format_changed)
self.excellon_format_upper_mm_entry.returnPressed.connect(self.on_excellon_format_changed)
self.excellon_format_lower_mm_entry.returnPressed.connect(self.on_excellon_format_changed)
self.excellon_zeros_radio.activated_custom.connect(self.on_excellon_zeros_changed)
self.excellon_units_radio.activated_custom.connect(self.on_excellon_units_changed)
else:
# disconnect the signals
try:
self.excellon_format_upper_in_entry.returnPressed.disconnect(self.on_excellon_format_changed)
except TypeError:
pass
try:
self.excellon_format_lower_in_entry.returnPressed.disconnect(self.on_excellon_format_changed)
except TypeError:
pass
try:
self.excellon_format_upper_mm_entry.returnPressed.disconnect(self.on_excellon_format_changed)
except TypeError:
pass
try:
self.excellon_format_lower_mm_entry.returnPressed.disconnect(self.on_excellon_format_changed)
except TypeError:
pass
try:
self.excellon_zeros_radio.activated_custom.disconnect(self.on_excellon_zeros_changed)
except TypeError:
pass
try:
self.excellon_units_radio.activated_custom.disconnect(self.on_excellon_zeros_changed)
except TypeError:
pass
def on_excellon_format_changed(self):
"""
Slot activated when the user changes the Excellon format values in Preferences -> Excellon -> Excellon General
:return: None
"""
if self.excellon_units_radio.get_value().upper() == 'METRIC':
self.app.ui.excellon_defaults_form.excellon_exp_group.format_whole_entry.set_value(
self.excellon_format_upper_mm_entry.get_value())
self.app.ui.excellon_defaults_form.excellon_exp_group.format_dec_entry.set_value(
self.excellon_format_lower_mm_entry.get_value())
else:
self.app.ui.excellon_defaults_form.excellon_exp_group.format_whole_entry.set_value(
self.excellon_format_upper_in_entry.get_value())
self.app.ui.excellon_defaults_form.excellon_exp_group.format_dec_entry.set_value(
self.excellon_format_lower_in_entry.get_value())
def on_excellon_zeros_changed(self, val):
"""
Slot activated when the user changes the Excellon zeros values in Preferences -> Excellon -> Excellon General
:return: None
"""
self.app.ui.excellon_defaults_form.excellon_exp_group.zeros_radio.set_value(val + 'Z')
def on_excellon_units_changed(self, val):
"""
Slot activated when the user changes the Excellon unit values in Preferences -> Excellon -> Excellon General
:return: None
"""
self.app.ui.excellon_defaults_form.excellon_exp_group.excellon_units_radio.set_value(val)
self.on_excellon_format_changed()

View File

@ -1,12 +1,12 @@
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt, QSettings
from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCEntry, FCSpinner, OptionalInputSection, \
FCComboBox, NumericalEvalTupleEntry
from AppGUI.preferences import machinist_setting
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCEntry, FCSpinner, OptionalInputSection, \
FCComboBox
from flatcamGUI.preferences import machinist_setting
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -198,7 +198,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
"If no value is entered then there is no move\n"
"on X,Y plane at the end of the job.")
)
self.endxy_entry = NumericalEvalTupleEntry(border_color='#0069A9')
self.endxy_entry = FCEntry()
grid2.addWidget(endmove_xy_label, 9, 0)
grid2.addWidget(self.endxy_entry, 9, 1)

View File

@ -1,14 +1,14 @@
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSettings
from AppGUI.preferences.excellon.ExcellonEditorPrefGroupUI import ExcellonEditorPrefGroupUI
from AppGUI.preferences.excellon.ExcellonExpPrefGroupUI import ExcellonExpPrefGroupUI
from AppGUI.preferences.excellon.ExcellonAdvOptPrefGroupUI import ExcellonAdvOptPrefGroupUI
from AppGUI.preferences.excellon.ExcellonOptPrefGroupUI import ExcellonOptPrefGroupUI
from AppGUI.preferences.excellon.ExcellonGenPrefGroupUI import ExcellonGenPrefGroupUI
from flatcamGUI.preferences.excellon.ExcellonEditorPrefGroupUI import ExcellonEditorPrefGroupUI
from flatcamGUI.preferences.excellon.ExcellonExpPrefGroupUI import ExcellonExpPrefGroupUI
from flatcamGUI.preferences.excellon.ExcellonAdvOptPrefGroupUI import ExcellonAdvOptPrefGroupUI
from flatcamGUI.preferences.excellon.ExcellonOptPrefGroupUI import ExcellonOptPrefGroupUI
from flatcamGUI.preferences.excellon.ExcellonGenPrefGroupUI import ExcellonGenPrefGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -31,7 +31,7 @@ class ExcellonPreferencesUI(QtWidgets.QWidget):
self.decimals = decimals
self.excellon_gen_group = ExcellonGenPrefGroupUI(decimals=self.decimals)
self.excellon_gen_group.setMinimumWidth(240)
self.excellon_gen_group.setMinimumWidth(220)
self.excellon_opt_group = ExcellonOptPrefGroupUI(decimals=self.decimals)
self.excellon_opt_group.setMinimumWidth(290)
self.excellon_exp_group = ExcellonExpPrefGroupUI(decimals=self.decimals)

View File

@ -1,13 +1,13 @@
from PyQt5 import QtCore, QtWidgets
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, RadioSet, OptionalInputSection, FCSpinner, \
FCColorEntry
from AppGUI.preferences import settings
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, RadioSet, OptionalInputSection, FCSpinner, \
FCEntry
from flatcamGUI.preferences import settings
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -177,6 +177,14 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
{'label': _('Landscape'), 'value': 'l'},
], stretch=False)
self.wks = OptionalInputSection(self.workspace_cb,
[
self.workspace_type_lbl,
self.wk_cb,
self.wk_orientation_label,
self.wk_orientation_radio
])
grid0.addWidget(self.wk_orientation_label, 8, 0)
grid0.addWidget(self.wk_orientation_radio, 8, 1)
@ -193,7 +201,7 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
self.notebook_font_size_label = QtWidgets.QLabel('%s:' % _('Notebook'))
self.notebook_font_size_label.setToolTip(
_("This sets the font size for the elements found in the Notebook.\n"
"The notebook is the collapsible area in the left side of the AppGUI,\n"
"The notebook is the collapsible area in the left side of the GUI,\n"
"and include the Project, Selected and Tool tabs.")
)
@ -232,8 +240,8 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
# TextBox Font Size
self.textbox_font_size_label = QtWidgets.QLabel('%s:' % _('Textbox'))
self.textbox_font_size_label.setToolTip(
_("This sets the font size for the Textbox AppGUI\n"
"elements that are used in the application.")
_("This sets the font size for the Textbox GUI\n"
"elements that are used in FlatCAM.")
)
self.textbox_font_size_spinner = FCSpinner()
@ -249,29 +257,10 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
grid0.addWidget(self.textbox_font_size_label, 13, 0)
grid0.addWidget(self.textbox_font_size_spinner, 13, 1)
# HUD Font Size
self.hud_font_size_label = QtWidgets.QLabel('%s:' % _('HUD'))
self.hud_font_size_label.setToolTip(
_("This sets the font size for the Heads Up Display.")
)
self.hud_font_size_spinner = FCSpinner()
self.hud_font_size_spinner.set_range(8, 40)
self.hud_font_size_spinner.setWrapping(True)
qsettings = QSettings("Open Source", "FlatCAM")
if qsettings.contains("hud_font_size"):
self.hud_font_size_spinner.set_value(settings.value('hud_font_size', type=int))
else:
self.hud_font_size_spinner.set_value(8)
grid0.addWidget(self.hud_font_size_label, 14, 0)
grid0.addWidget(self.hud_font_size_spinner, 14, 1)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 16, 0, 1, 2)
grid0.addWidget(separator_line, 14, 0, 1, 2)
# -----------------------------------------------------------
# -------------- MOUSE SETTINGS -----------------------------
@ -334,16 +323,24 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
self.mouse_color_label.setToolTip(
_("Set the color of the mouse cursor.")
)
self.mouse_cursor_entry = FCColorEntry()
self.mouse_cursor_entry = FCEntry()
self.mouse_cursor_button = QtWidgets.QPushButton()
self.mouse_cursor_button.setFixedSize(15, 15)
self.form_box_child_1 = QtWidgets.QHBoxLayout()
self.form_box_child_1.addWidget(self.mouse_cursor_entry)
self.form_box_child_1.addWidget(self.mouse_cursor_button)
self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid0.addWidget(self.mouse_color_label, 26, 0)
grid0.addWidget(self.mouse_cursor_entry, 26, 1)
grid0.addLayout(self.form_box_child_1, 26, 1)
self.mois = OptionalInputSection(
self.mouse_cursor_color_cb,
[
self.mouse_color_label,
self.mouse_cursor_entry
self.mouse_cursor_entry,
self.mouse_cursor_button
]
)
# Select mouse pan button
@ -443,7 +440,9 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
self.layout.addStretch()
self.mouse_cursor_color_cb.stateChanged.connect(self.on_mouse_cursor_color_enable)
self.mouse_cursor_entry.editingFinished.connect(self.on_mouse_cursor_entry)
self.mouse_cursor_button.clicked.connect(self.on_mouse_cursor_button)
def on_mouse_cursor_color_enable(self, val):
if val:
@ -462,4 +461,23 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
def on_mouse_cursor_entry(self):
self.app.defaults['global_cursor_color'] = self.mouse_cursor_entry.get_value()
self.mouse_cursor_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_cursor_color']))
self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]
def on_mouse_cursor_button(self):
current_color = QtGui.QColor(self.app.defaults['global_cursor_color'])
c_dialog = QtWidgets.QColorDialog()
proj_color = c_dialog.getColor(initial=current_color)
if proj_color.isValid() is False:
return
self.mouse_cursor_button.setStyleSheet("background-color:%s" % str(proj_color.name()))
new_val_sel = str(proj_color.name())
self.mouse_cursor_entry.set_value(new_val_sel)
self.app.defaults['global_cursor_color'] = new_val_sel
self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]

View File

@ -3,12 +3,12 @@ import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import RadioSet, FCSpinner, FCCheckBox, FCComboBox, FCButton, OptionalInputSection, \
from flatcamGUI.GUIElements import RadioSet, FCSpinner, FCCheckBox, FCComboBox, FCButton, OptionalInputSection, \
FCDoubleSpinner
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -380,11 +380,12 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
def on_toggle_shell_from_settings(self, state):
"""
Toggle shell ui: if is visible close it, if it is closed then open it
Toggle shell: if is visible close it, if it is closed then open it
:return: None
"""
self.app.defaults.report_usage("on_toggle_shell_from_settings()")
if state is True:
if not self.app.ui.shell_dock.isVisible():
self.app.ui.shell_dock.show()

View File

@ -1,11 +1,11 @@
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import QSettings, Qt
from AppGUI.GUIElements import RadioSet, FCCheckBox, FCComboBox, FCSliderWithSpinner, FCColorEntry
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.GUIElements import RadioSet, FCCheckBox, FCButton, FCComboBox, FCEntry, FCSpinner
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -35,7 +35,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
# Theme selection
self.theme_label = QtWidgets.QLabel('%s:' % _('Theme'))
self.theme_label.setToolTip(
_("Select a theme for the application.\n"
_("Select a theme for FlatCAM.\n"
"It will theme the plot area.")
)
@ -72,7 +72,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
# Layout selection
self.layout_label = QtWidgets.QLabel('%s:' % _('Layout'))
self.layout_label.setToolTip(
_("Select a layout for the application.\n"
_("Select an layout for FlatCAM.\n"
"It is applied immediately.")
)
self.layout_combo = FCComboBox()
@ -94,7 +94,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
# Style selection
self.style_label = QtWidgets.QLabel('%s:' % _('Style'))
self.style_label.setToolTip(
_("Select a style for the application.\n"
_("Select an style for FlatCAM.\n"
"It will be applied at the next app start.")
)
self.style_combo = FCComboBox()
@ -110,7 +110,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
# Enable High DPI Support
self.hdpi_cb = FCCheckBox('%s' % _('Activate HDPI Support'))
self.hdpi_cb.setToolTip(
_("Enable High DPI support for the application.\n"
_("Enable High DPI support for FlatCAM.\n"
"It will be applied at the next app start.")
)
@ -126,7 +126,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
# Enable Hover box
self.hover_cb = FCCheckBox('%s' % _('Display Hover Shape'))
self.hover_cb.setToolTip(
_("Enable display of a hover shape for the application objects.\n"
_("Enable display of a hover shape for FlatCAM objects.\n"
"It is displayed whenever the mouse cursor is hovering\n"
"over any kind of not-selected object.")
)
@ -135,7 +135,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
# Enable Selection box
self.selection_cb = FCCheckBox('%s' % _('Display Selection Shape'))
self.selection_cb.setToolTip(
_("Enable the display of a selection shape for the application objects.\n"
_("Enable the display of a selection shape for FlatCAM objects.\n"
"It is displayed whenever the mouse selects an object\n"
"either by clicking or dragging mouse from left to right or\n"
"right to left.")
@ -155,10 +155,17 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
self.sl_color_label.setToolTip(
_("Set the line color for the 'left to right' selection box.")
)
self.sl_color_entry = FCColorEntry()
self.sl_color_entry = FCEntry()
self.sl_color_button = QtWidgets.QPushButton()
self.sl_color_button.setFixedSize(15, 15)
self.form_box_child_4 = QtWidgets.QHBoxLayout()
self.form_box_child_4.addWidget(self.sl_color_entry)
self.form_box_child_4.addWidget(self.sl_color_button)
self.form_box_child_4.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid0.addWidget(self.sl_color_label, 16, 0)
grid0.addWidget(self.sl_color_entry, 16, 1)
grid0.addLayout(self.form_box_child_4, 16, 1)
self.sf_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
self.sf_color_label.setToolTip(
@ -167,20 +174,38 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
"First 6 digits are the color and the last 2\n"
"digits are for alpha (transparency) level.")
)
self.sf_color_entry = FCColorEntry()
self.sf_color_entry = FCEntry()
self.sf_color_button = QtWidgets.QPushButton()
self.sf_color_button.setFixedSize(15, 15)
self.form_box_child_5 = QtWidgets.QHBoxLayout()
self.form_box_child_5.addWidget(self.sf_color_entry)
self.form_box_child_5.addWidget(self.sf_color_button)
self.form_box_child_5.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid0.addWidget(self.sf_color_label, 17, 0)
grid0.addWidget(self.sf_color_entry, 17, 1)
grid0.addLayout(self.form_box_child_5, 17, 1)
# Plot Selection (left - right) Fill Transparency Level
self.left_right_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
self.left_right_alpha_label.setToolTip(
self.sf_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
self.sf_alpha_label.setToolTip(
_("Set the fill transparency for the 'left to right' selection box.")
)
self.left_right_alpha_entry = FCSliderWithSpinner(0, 255, 1)
self.sf_color_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.sf_color_alpha_slider.setMinimum(0)
self.sf_color_alpha_slider.setMaximum(255)
self.sf_color_alpha_slider.setSingleStep(1)
grid0.addWidget(self.left_right_alpha_label, 18, 0)
grid0.addWidget(self.left_right_alpha_entry, 18, 1)
self.sf_color_alpha_spinner = FCSpinner()
self.sf_color_alpha_spinner.setMinimumWidth(70)
self.sf_color_alpha_spinner.set_range(0, 255)
self.form_box_child_6 = QtWidgets.QHBoxLayout()
self.form_box_child_6.addWidget(self.sf_color_alpha_slider)
self.form_box_child_6.addWidget(self.sf_color_alpha_spinner)
grid0.addWidget(self.sf_alpha_label, 18, 0)
grid0.addLayout(self.form_box_child_6, 18, 1)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
@ -196,10 +221,17 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
self.alt_sl_color_label.setToolTip(
_("Set the line color for the 'right to left' selection box.")
)
self.alt_sl_color_entry = FCColorEntry()
self.alt_sl_color_entry = FCEntry()
self.alt_sl_color_button = QtWidgets.QPushButton()
self.alt_sl_color_button.setFixedSize(15, 15)
self.form_box_child_7 = QtWidgets.QHBoxLayout()
self.form_box_child_7.addWidget(self.alt_sl_color_entry)
self.form_box_child_7.addWidget(self.alt_sl_color_button)
self.form_box_child_7.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid0.addWidget(self.alt_sl_color_label, 21, 0)
grid0.addWidget(self.alt_sl_color_entry, 21, 1)
grid0.addLayout(self.form_box_child_7, 21, 1)
# Plot Selection (right - left) Fill Color
self.alt_sf_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
@ -209,20 +241,38 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
"First 6 digits are the color and the last 2\n"
"digits are for alpha (transparency) level.")
)
self.alt_sf_color_entry = FCColorEntry()
self.alt_sf_color_entry = FCEntry()
self.alt_sf_color_button = QtWidgets.QPushButton()
self.alt_sf_color_button.setFixedSize(15, 15)
self.form_box_child_8 = QtWidgets.QHBoxLayout()
self.form_box_child_8.addWidget(self.alt_sf_color_entry)
self.form_box_child_8.addWidget(self.alt_sf_color_button)
self.form_box_child_8.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid0.addWidget(self.alt_sf_color_label, 22, 0)
grid0.addWidget(self.alt_sf_color_entry, 22, 1)
grid0.addLayout(self.form_box_child_8, 22, 1)
# Plot Selection (right - left) Fill Transparency Level
self.right_left_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
self.right_left_alpha_label.setToolTip(
self.alt_sf_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
self.alt_sf_alpha_label.setToolTip(
_("Set the fill transparency for selection 'right to left' box.")
)
self.right_left_alpha_entry = FCSliderWithSpinner(0, 255, 1)
self.alt_sf_color_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.alt_sf_color_alpha_slider.setMinimum(0)
self.alt_sf_color_alpha_slider.setMaximum(255)
self.alt_sf_color_alpha_slider.setSingleStep(1)
grid0.addWidget(self.right_left_alpha_label, 23, 0)
grid0.addWidget(self.right_left_alpha_entry, 23, 1)
self.alt_sf_color_alpha_spinner = FCSpinner()
self.alt_sf_color_alpha_spinner.setMinimumWidth(70)
self.alt_sf_color_alpha_spinner.set_range(0, 255)
self.form_box_child_9 = QtWidgets.QHBoxLayout()
self.form_box_child_9.addWidget(self.alt_sf_color_alpha_slider)
self.form_box_child_9.addWidget(self.alt_sf_color_alpha_spinner)
grid0.addWidget(self.alt_sf_alpha_label, 23, 0)
grid0.addLayout(self.form_box_child_9, 23, 1)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
@ -241,20 +291,34 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
self.alt_sf_color_label.setToolTip(
_("Set the color for the shape.")
)
self.draw_color_entry = FCColorEntry()
self.draw_color_entry = FCEntry()
self.draw_color_button = QtWidgets.QPushButton()
self.draw_color_button.setFixedSize(15, 15)
self.form_box_child_10 = QtWidgets.QHBoxLayout()
self.form_box_child_10.addWidget(self.draw_color_entry)
self.form_box_child_10.addWidget(self.draw_color_button)
self.form_box_child_10.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid0.addWidget(self.draw_color_label, 26, 0)
grid0.addWidget(self.draw_color_entry, 26, 1)
grid0.addLayout(self.form_box_child_10, 26, 1)
# Editor Draw Selection Color
self.sel_draw_color_label = QtWidgets.QLabel('%s:' % _('Selection'))
self.sel_draw_color_label.setToolTip(
_("Set the color of the shape when selected.")
)
self.sel_draw_color_entry = FCColorEntry()
self.sel_draw_color_entry = FCEntry()
self.sel_draw_color_button = QtWidgets.QPushButton()
self.sel_draw_color_button.setFixedSize(15, 15)
self.form_box_child_11 = QtWidgets.QHBoxLayout()
self.form_box_child_11.addWidget(self.sel_draw_color_entry)
self.form_box_child_11.addWidget(self.sel_draw_color_button)
self.form_box_child_11.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid0.addWidget(self.sel_draw_color_label, 27, 0)
grid0.addWidget(self.sel_draw_color_entry, 27, 1)
grid0.addLayout(self.form_box_child_11, 27, 1)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
@ -273,20 +337,34 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
self.proj_color_label.setToolTip(
_("Set the color of the items in Project Tab Tree.")
)
self.proj_color_entry = FCColorEntry()
self.proj_color_entry = FCEntry()
self.proj_color_button = QtWidgets.QPushButton()
self.proj_color_button.setFixedSize(15, 15)
self.form_box_child_12 = QtWidgets.QHBoxLayout()
self.form_box_child_12.addWidget(self.proj_color_entry)
self.form_box_child_12.addWidget(self.proj_color_button)
self.form_box_child_12.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid0.addWidget(self.proj_color_label, 30, 0)
grid0.addWidget(self.proj_color_entry, 30, 1)
grid0.addLayout(self.form_box_child_12, 30, 1)
self.proj_color_dis_label = QtWidgets.QLabel('%s:' % _('Disabled'))
self.proj_color_dis_label.setToolTip(
_("Set the color of the items in Project Tab Tree,\n"
"for the case when the items are disabled.")
)
self.proj_color_dis_entry = FCColorEntry()
self.proj_color_dis_entry = FCEntry()
self.proj_color_dis_button = QtWidgets.QPushButton()
self.proj_color_dis_button.setFixedSize(15, 15)
self.form_box_child_13 = QtWidgets.QHBoxLayout()
self.form_box_child_13.addWidget(self.proj_color_dis_entry)
self.form_box_child_13.addWidget(self.proj_color_dis_button)
self.form_box_child_13.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid0.addWidget(self.proj_color_dis_label, 31, 0)
grid0.addWidget(self.proj_color_dis_entry, 31, 1)
grid0.addLayout(self.form_box_child_13, 31, 1)
# Project autohide CB
self.project_autohide_cb = FCCheckBox(label=_('Project AutoHide'))
@ -309,22 +387,32 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
# Setting selection (left - right) colors signals
self.sf_color_entry.editingFinished.connect(self.on_sf_color_entry)
self.sf_color_button.clicked.connect(self.on_sf_color_button)
self.sf_color_alpha_spinner.valueChanged.connect(self.on_sf_color_spinner)
self.sf_color_alpha_slider.valueChanged.connect(self.on_sf_color_slider)
self.sl_color_entry.editingFinished.connect(self.on_sl_color_entry)
self.left_right_alpha_entry.valueChanged.connect(self.on_left_right_alpha_changed) # alpha
self.sl_color_button.clicked.connect(self.on_sl_color_button)
# Setting selection (right - left) colors signals
self.alt_sf_color_entry.editingFinished.connect(self.on_alt_sf_color_entry)
self.alt_sf_color_button.clicked.connect(self.on_alt_sf_color_button)
self.alt_sf_color_alpha_spinner.valueChanged.connect(self.on_alt_sf_color_spinner)
self.alt_sf_color_alpha_slider.valueChanged.connect(self.on_alt_sf_color_slider)
self.alt_sl_color_entry.editingFinished.connect(self.on_alt_sl_color_entry)
self.right_left_alpha_entry.valueChanged.connect(self.on_right_left_alpha_changed) # alpha
self.alt_sl_color_button.clicked.connect(self.on_alt_sl_color_button)
# Setting Editor Draw colors signals
self.draw_color_entry.editingFinished.connect(self.on_draw_color_entry)
self.draw_color_button.clicked.connect(self.on_draw_color_button)
self.sel_draw_color_entry.editingFinished.connect(self.on_sel_draw_color_entry)
self.sel_draw_color_button.clicked.connect(self.on_sel_draw_color_button)
self.proj_color_entry.editingFinished.connect(self.on_proj_color_entry)
self.proj_color_button.clicked.connect(self.on_proj_color_button)
self.proj_color_dis_entry.editingFinished.connect(self.on_proj_color_dis_entry)
self.proj_color_dis_button.clicked.connect(self.on_proj_color_dis_button)
self.layout_combo.activated.connect(self.on_layout)
@ -349,64 +437,191 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
# Setting selection colors (left - right) handlers
def on_sf_color_entry(self):
self.app.defaults['global_sel_fill'] = self.app.defaults['global_sel_fill'][7:9]
self.sf_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_sel_fill'])[:7])
def on_sl_color_entry(self):
self.app.defaults['global_sel_line'] = self.sl_color_entry.get_value()[:7] + \
self.app.defaults['global_sel_line'][7:9]
def on_sf_color_button(self):
current_color = QtGui.QColor(self.app.defaults['global_sel_fill'][:7])
def on_left_right_alpha_changed(self, spinner_value):
"""
Change the alpha level for the color of the selection box when selection is done left to right.
Called on valueChanged of a FCSliderWithSpinner.
c_dialog = QtWidgets.QColorDialog()
plot_fill_color = c_dialog.getColor(initial=current_color)
:param spinner_value: passed value within [0, 255]
:type spinner_value: int
:return: None
:rtype:
"""
if plot_fill_color.isValid() is False:
return
self.sf_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
new_val = str(plot_fill_color.name()) + str(self.app.defaults['global_sel_fill'][7:9])
self.sf_color_entry.set_value(new_val)
self.app.defaults['global_sel_fill'] = new_val
def on_sf_color_spinner(self):
spinner_value = self.sf_color_alpha_spinner.value()
self.sf_color_alpha_slider.setValue(spinner_value)
self.app.defaults['global_sel_fill'] = self.app.defaults['global_sel_fill'][:7] + \
(hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
self.app.defaults['global_sel_line'] = self.app.defaults['global_sel_line'][:7] + \
(hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
def on_sf_color_slider(self):
slider_value = self.sf_color_alpha_slider.value()
self.sf_color_alpha_spinner.setValue(slider_value)
def on_sl_color_entry(self):
self.app.defaults['global_sel_line'] = self.sl_color_entry.get_value()[:7] + \
self.app.defaults['global_sel_line'][7:9]
self.sl_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_sel_line'])[:7])
def on_sl_color_button(self):
current_color = QtGui.QColor(self.app.defaults['global_sel_line'][:7])
c_dialog = QtWidgets.QColorDialog()
plot_line_color = c_dialog.getColor(initial=current_color)
if plot_line_color.isValid() is False:
return
self.sl_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
new_val_line = str(plot_line_color.name()) + str(self.app.defaults['global_sel_line'][7:9])
self.sl_color_entry.set_value(new_val_line)
self.app.defaults['global_sel_line'] = new_val_line
# Setting selection colors (right - left) handlers
def on_alt_sf_color_entry(self):
self.app.defaults['global_alt_sel_fill'] = self.alt_sf_color_entry.get_value()[:7] + \
self.app.defaults['global_alt_sel_fill'][7:9]
self.alt_sf_color_button.setStyleSheet(
"background-color:%s" % str(self.app.defaults['global_alt_sel_fill'])[:7]
)
def on_alt_sl_color_entry(self):
self.app.defaults['global_alt_sel_line'] = self.alt_sl_color_entry.get_value()[:7] + \
self.app.defaults['global_alt_sel_line'][7:9]
def on_alt_sf_color_button(self):
current_color = QtGui.QColor(self.app.defaults['global_alt_sel_fill'][:7])
def on_right_left_alpha_changed(self, spinner_value):
"""
Change the alpha level for the color of the selection box when selection is done right to left.
Called on valueChanged of a FCSliderWithSpinner.
c_dialog = QtWidgets.QColorDialog()
plot_fill_color = c_dialog.getColor(initial=current_color)
:param spinner_value: passed value within [0, 255]
:type spinner_value: int
:return: None
:rtype:
"""
if plot_fill_color.isValid() is False:
return
self.alt_sf_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
new_val = str(plot_fill_color.name()) + str(self.app.defaults['global_alt_sel_fill'][7:9])
self.alt_sf_color_entry.set_value(new_val)
self.app.defaults['global_alt_sel_fill'] = new_val
def on_alt_sf_color_spinner(self):
spinner_value = self.alt_sf_color_alpha_spinner.value()
self.alt_sf_color_alpha_slider.setValue(spinner_value)
self.app.defaults['global_alt_sel_fill'] = self.app.defaults['global_alt_sel_fill'][:7] + \
(hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
self.app.defaults['global_alt_sel_line'] = self.app.defaults['global_alt_sel_line'][:7] + \
(hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
def on_alt_sf_color_slider(self):
slider_value = self.alt_sf_color_alpha_slider.value()
self.alt_sf_color_alpha_spinner.setValue(slider_value)
def on_alt_sl_color_entry(self):
self.app.defaults['global_alt_sel_line'] = self.alt_sl_color_entry.get_value()[:7] + \
self.app.defaults['global_alt_sel_line'][7:9]
self.alt_sl_color_button.setStyleSheet(
"background-color:%s" % str(self.app.defaults['global_alt_sel_line'])[:7]
)
def on_alt_sl_color_button(self):
current_color = QtGui.QColor(self.app.defaults['global_alt_sel_line'][:7])
c_dialog = QtWidgets.QColorDialog()
plot_line_color = c_dialog.getColor(initial=current_color)
if plot_line_color.isValid() is False:
return
self.alt_sl_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
new_val_line = str(plot_line_color.name()) + str(self.app.defaults['global_alt_sel_line'][7:9])
self.alt_sl_color_entry.set_value(new_val_line)
self.app.defaults['global_alt_sel_line'] = new_val_line
# Setting Editor colors
def on_draw_color_entry(self):
self.app.defaults['global_draw_color'] = self.draw_color_entry.get_value()
self.draw_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_draw_color']))
def on_draw_color_button(self):
current_color = QtGui.QColor(self.app.defaults['global_draw_color'])
c_dialog = QtWidgets.QColorDialog()
draw_color = c_dialog.getColor(initial=current_color)
if draw_color.isValid() is False:
return
self.draw_color_button.setStyleSheet("background-color:%s" % str(draw_color.name()))
new_val = str(draw_color.name())
self.draw_color_entry.set_value(new_val)
self.app.defaults['global_draw_color'] = new_val
def on_sel_draw_color_entry(self):
self.app.defaults['global_sel_draw_color'] = self.sel_draw_color_entry.get_value()
self.sel_draw_color_button.setStyleSheet(
"background-color:%s" % str(self.app.defaults['global_sel_draw_color']))
def on_sel_draw_color_button(self):
current_color = QtGui.QColor(self.app.defaults['global_sel_draw_color'])
c_dialog = QtWidgets.QColorDialog()
sel_draw_color = c_dialog.getColor(initial=current_color)
if sel_draw_color.isValid() is False:
return
self.sel_draw_color_button.setStyleSheet("background-color:%s" % str(sel_draw_color.name()))
new_val_sel = str(sel_draw_color.name())
self.sel_draw_color_entry.set_value(new_val_sel)
self.app.defaults['global_sel_draw_color'] = new_val_sel
def on_proj_color_entry(self):
self.app.defaults['global_proj_item_color'] = self.proj_color_entry.get_value()
self.proj_color_button.setStyleSheet(
"background-color:%s" % str(self.app.defaults['global_proj_item_color']))
def on_proj_color_button(self):
current_color = QtGui.QColor(self.app.defaults['global_proj_item_color'])
c_dialog = QtWidgets.QColorDialog()
proj_color = c_dialog.getColor(initial=current_color)
if proj_color.isValid() is False:
return
self.proj_color_button.setStyleSheet("background-color:%s" % str(proj_color.name()))
new_val_sel = str(proj_color.name())
self.proj_color_entry.set_value(new_val_sel)
self.app.defaults['global_proj_item_color'] = new_val_sel
def on_proj_color_dis_entry(self):
self.app.defaults['global_proj_item_dis_color'] = self.proj_color_dis_entry.get_value()
self.proj_color_dis_button.setStyleSheet(
"background-color:%s" % str(self.app.defaults['global_proj_item_dis_color']))
def on_proj_color_dis_button(self):
current_color = QtGui.QColor(self.app.defaults['global_proj_item_dis_color'])
c_dialog = QtWidgets.QColorDialog()
proj_color = c_dialog.getColor(initial=current_color)
if proj_color.isValid() is False:
return
self.proj_color_dis_button.setStyleSheet("background-color:%s" % str(proj_color.name()))
new_val_sel = str(proj_color.name())
self.proj_color_dis_entry.set_value(new_val_sel)
self.app.defaults['global_proj_item_dis_color'] = new_val_sel
def on_layout(self, index=None, lay=None):
"""
@ -432,13 +647,14 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
# first remove the toolbars:
try:
self.app.ui.removeToolBar(self.app.ui.toolbarfile)
self.app.ui.removeToolBar(self.app.ui.toolbaredit)
self.app.ui.removeToolBar(self.app.ui.toolbargeo)
self.app.ui.removeToolBar(self.app.ui.toolbarview)
self.app.ui.removeToolBar(self.app.ui.toolbarshell)
self.app.ui.removeToolBar(self.app.ui.toolbartools)
self.app.ui.removeToolBar(self.app.ui.exc_edit_toolbar)
self.app.ui.removeToolBar(self.app.ui.geo_edit_toolbar)
self.app.ui.removeToolBar(self.app.ui.grb_edit_toolbar)
self.app.ui.removeToolBar(self.app.ui.snap_toolbar)
self.app.ui.removeToolBar(self.app.ui.toolbarshell)
except Exception:
pass
@ -449,9 +665,9 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
self.app.ui.toolbarfile.setObjectName('File_TB')
self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbarfile)
self.app.ui.toolbaredit = QtWidgets.QToolBar('Edit Toolbar')
self.app.ui.toolbaredit.setObjectName('Edit_TB')
self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbaredit)
self.app.ui.toolbargeo = QtWidgets.QToolBar('Edit Toolbar')
self.app.ui.toolbargeo.setObjectName('Edit_TB')
self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbargeo)
self.app.ui.toolbarshell = QtWidgets.QToolBar('Shell Toolbar')
self.app.ui.toolbarshell.setObjectName('Shell_TB')
@ -481,15 +697,22 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
self.app.ui.exc_edit_toolbar.setObjectName('ExcEditor_TB')
self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.exc_edit_toolbar)
self.app.ui.snap_toolbar = QtWidgets.QToolBar('Grid Toolbar')
self.app.ui.snap_toolbar.setObjectName('Snap_TB')
self.app.ui.snap_toolbar.setMaximumHeight(30)
self.app.ui.splitter_left.addWidget(self.app.ui.snap_toolbar)
self.app.ui.corner_snap_btn.setVisible(True)
self.app.ui.snap_magnet.setVisible(True)
else:
# ## TOOLBAR INSTALLATION # ##
self.app.ui.toolbarfile = QtWidgets.QToolBar('File Toolbar')
self.app.ui.toolbarfile.setObjectName('File_TB')
self.app.ui.addToolBar(self.app.ui.toolbarfile)
self.app.ui.toolbaredit = QtWidgets.QToolBar('Edit Toolbar')
self.app.ui.toolbaredit.setObjectName('Edit_TB')
self.app.ui.addToolBar(self.app.ui.toolbaredit)
self.app.ui.toolbargeo = QtWidgets.QToolBar('Edit Toolbar')
self.app.ui.toolbargeo.setObjectName('Edit_TB')
self.app.ui.addToolBar(self.app.ui.toolbargeo)
self.app.ui.toolbarview = QtWidgets.QToolBar('View Toolbar')
self.app.ui.toolbarview.setObjectName('View_TB')
@ -520,9 +743,18 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
self.app.ui.grb_edit_toolbar.setObjectName('GrbEditor_TB')
self.app.ui.addToolBar(self.app.ui.grb_edit_toolbar)
self.app.ui.snap_toolbar = QtWidgets.QToolBar('Grid Toolbar')
self.app.ui.snap_toolbar.setObjectName('Snap_TB')
# self.app.ui.snap_toolbar.setMaximumHeight(30)
self.app.ui.addToolBar(self.app.ui.snap_toolbar)
self.app.ui.corner_snap_btn.setVisible(False)
self.app.ui.snap_magnet.setVisible(False)
if current_layout == 'minimal':
self.app.ui.toolbarview.setVisible(False)
self.app.ui.toolbarshell.setVisible(False)
self.app.ui.snap_toolbar.setVisible(False)
self.app.ui.geo_edit_toolbar.setVisible(False)
self.app.ui.grb_edit_toolbar.setVisible(False)
self.app.ui.exc_edit_toolbar.setVisible(False)
@ -535,9 +767,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
self.app.connect_toolbar_signals()
self.app.ui.grid_snap_btn.setChecked(True)
self.app.ui.corner_snap_btn.setVisible(False)
self.app.ui.snap_magnet.setVisible(False)
self.app.ui.on_grid_snap_triggered(state=True)
self.app.ui.grid_gap_x_entry.setText(str(self.app.defaults["global_gridx"]))
self.app.ui.grid_gap_y_entry.setText(str(self.app.defaults["global_gridy"]))

View File

@ -1,12 +1,12 @@
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSettings
from AppGUI.preferences.general.GeneralAppPrefGroupUI import GeneralAppPrefGroupUI
from AppGUI.preferences.general.GeneralAPPSetGroupUI import GeneralAPPSetGroupUI
from AppGUI.preferences.general.GeneralGUIPrefGroupUI import GeneralGUIPrefGroupUI
from flatcamGUI.preferences.general.GeneralAppPrefGroupUI import GeneralAppPrefGroupUI
from flatcamGUI.preferences.general.GeneralAPPSetGroupUI import GeneralAPPSetGroupUI
from flatcamGUI.preferences.general.GeneralGUIPrefGroupUI import GeneralGUIPrefGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')

View File

@ -1,12 +1,11 @@
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCLabel, NumericalEvalTupleEntry, \
NumericalEvalEntry
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.GUIElements import FCEntry, FloatEntry, FCDoubleSpinner, FCCheckBox, RadioSet, FCLabel
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -47,9 +46,8 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
toolchange_xy_label.setToolTip(
_("Toolchange X,Y position.")
)
self.toolchangexy_entry = NumericalEvalTupleEntry(border_color='#0069A9')
grid1.addWidget(toolchange_xy_label, 1, 0)
self.toolchangexy_entry = FCEntry()
grid1.addWidget(self.toolchangexy_entry, 1, 1)
# Start move Z
@ -58,9 +56,8 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
_("Height of the tool just after starting the work.\n"
"Delete the value if you don't need this feature.")
)
self.gstartz_entry = NumericalEvalEntry(border_color='#0069A9')
grid1.addWidget(startzlabel, 2, 0)
self.gstartz_entry = FloatEntry()
grid1.addWidget(self.gstartz_entry, 2, 1)
# Feedrate rapids
@ -189,11 +186,6 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
grid1.addWidget(segy_label, 11, 0)
grid1.addWidget(self.segy_entry, 11, 1)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid1.addWidget(separator_line, 12, 0, 1, 2)
# -----------------------------
# --- Area Exclusion ----------
# -----------------------------
@ -203,10 +195,10 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
"Those parameters are available only for\n"
"Advanced App. Level.")
)
grid1.addWidget(self.adv_label, 13, 0, 1, 2)
grid1.addWidget(self.adv_label, 12, 0, 1, 2)
# Exclusion Area CB
self.exclusion_cb = FCCheckBox('%s' % _("Exclusion areas"))
self.exclusion_cb = FCCheckBox('%s:' % _("Exclusion areas"))
self.exclusion_cb.setToolTip(
_(
"Include exclusion areas.\n"
@ -214,7 +206,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
"is forbidden."
)
)
grid1.addWidget(self.exclusion_cb, 14, 0, 1, 2)
grid1.addWidget(self.exclusion_cb, 13, 0, 1, 2)
# Area Selection shape
self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
@ -225,8 +217,8 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
{'label': _("Polygon"), 'value': 'polygon'}])
grid1.addWidget(self.area_shape_label, 15, 0)
grid1.addWidget(self.area_shape_radio, 15, 1)
grid1.addWidget(self.area_shape_label, 14, 0)
grid1.addWidget(self.area_shape_radio, 14, 1)
# Chose Strategy
self.strategy_label = FCLabel('%s:' % _("Strategy"))
@ -237,8 +229,8 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
self.strategy_radio = RadioSet([{'label': _('Over'), 'value': 'over'},
{'label': _('Around'), 'value': 'around'}])
grid1.addWidget(self.strategy_label, 16, 0)
grid1.addWidget(self.strategy_radio, 16, 1)
grid1.addWidget(self.strategy_label, 15, 0)
grid1.addWidget(self.strategy_radio, 15, 1)
# Over Z
self.over_z_label = FCLabel('%s:' % _("Over Z"))

View File

@ -1,11 +1,11 @@
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import FCSpinner, RadioSet
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.GUIElements import FCSpinner, RadioSet
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')

View File

@ -1,11 +1,11 @@
from PyQt5 import QtWidgets
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import QSettings
from AppGUI.GUIElements import FCCheckBox, FCSpinner, FCEntry, FCColorEntry
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.GUIElements import FCCheckBox, FCSpinner, FCEntry
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -31,22 +31,12 @@ class GeometryGenPrefGroupUI(OptionsGroupUI):
self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
self.layout.addWidget(self.plot_options_label)
plot_hlay = QtWidgets.QHBoxLayout()
self.layout.addLayout(plot_hlay)
# Plot CB
self.plot_cb = FCCheckBox(label=_('Plot'))
self.plot_cb.setToolTip(
_("Plot (show) this object.")
)
plot_hlay.addWidget(self.plot_cb)
# Multicolored CB
self.multicolored_cb = FCCheckBox(label=_('M-Color'))
self.multicolored_cb.setToolTip(
_("Draw polygons in different colors.")
)
plot_hlay.addWidget(self.multicolored_cb)
self.layout.addWidget(self.plot_cb)
grid0 = QtWidgets.QGridLayout()
self.layout.addLayout(grid0)
@ -87,7 +77,7 @@ class GeometryGenPrefGroupUI(OptionsGroupUI):
grid0.addWidget(separator_line, 9, 0, 1, 2)
# Geometry Object Color
self.gerber_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Object Color'))
self.gerber_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Geometry Object Color'))
grid0.addWidget(self.gerber_color_label, 10, 0, 1, 2)
# Plot Line Color
@ -95,15 +85,39 @@ class GeometryGenPrefGroupUI(OptionsGroupUI):
self.line_color_label.setToolTip(
_("Set the line color for plotted objects.")
)
self.line_color_entry = FCColorEntry()
self.line_color_entry = FCEntry()
self.line_color_button = QtWidgets.QPushButton()
self.line_color_button.setFixedSize(15, 15)
self.form_box_child_2 = QtWidgets.QHBoxLayout()
self.form_box_child_2.addWidget(self.line_color_entry)
self.form_box_child_2.addWidget(self.line_color_button)
self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
grid0.addWidget(self.line_color_label, 11, 0)
grid0.addWidget(self.line_color_entry, 11, 1)
grid0.addLayout(self.form_box_child_2, 11, 1)
self.layout.addStretch()
# Setting plot colors signals
self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
self.line_color_button.clicked.connect(self.on_line_color_button)
def on_line_color_entry(self):
self.app.defaults['geometry_plot_line'] = self.line_color_entry.get_value()[:7] + 'FF'
self.line_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['geometry_plot_line'])[:7])
def on_line_color_button(self):
current_color = QtGui.QColor(self.app.defaults['geometry_plot_line'][:7])
# print(current_color)
c_dialog = QtWidgets.QColorDialog()
plot_line_color = c_dialog.getColor(initial=current_color)
if plot_line_color.isValid() is False:
return
self.line_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
new_val_line = str(plot_line_color.name()) + str(self.app.defaults['geometry_plot_line'][7:9])
self.line_color_entry.set_value(new_val_line)

View File

@ -1,13 +1,12 @@
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt, QSettings
from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCSpinner, FCComboBox, \
NumericalEvalTupleEntry
from AppGUI.preferences import machinist_setting
from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCEntry, FCSpinner, FCComboBox
from flatcamGUI.preferences import machinist_setting
from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
import gettext
import AppTranslation as fcTranslate
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -177,7 +176,7 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
"If no value is entered then there is no move\n"
"on X,Y plane at the end of the job.")
)
self.endxy_entry = NumericalEvalTupleEntry(border_color='#0069A9')
self.endxy_entry = FCEntry()
grid1.addWidget(endmove_xy_label, 7, 0)
grid1.addWidget(self.endxy_entry, 7, 1)

Some files were not shown because too many files have changed in this diff Show More