- structural changes in Preferences from David Robertson
This commit is contained in:
181
AppGUI/ColumnarFlowLayout.py
Normal file
181
AppGUI/ColumnarFlowLayout.py
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
# ##########################################################
|
||||||
|
# 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)
|
||||||
@@ -683,6 +683,100 @@ class NumericalEvalTupleEntry(FCEntry):
|
|||||||
self.setValidator(validator)
|
self.setValidator(validator)
|
||||||
|
|
||||||
|
|
||||||
|
class FCColorEntry(QtWidgets.QFrame):
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
self.entry = FCEntry()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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):
|
class FCSpinner(QtWidgets.QSpinBox):
|
||||||
|
|
||||||
returnPressed = QtCore.pyqtSignal()
|
returnPressed = QtCore.pyqtSignal()
|
||||||
|
|||||||
327
AppGUI/preferences/OptionUI.py
Normal file
327
AppGUI/preferences/OptionUI.py
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
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
|
||||||
@@ -1,4 +1,30 @@
|
|||||||
|
# ##########################################################
|
||||||
|
# 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 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):
|
class OptionsGroupUI(QtWidgets.QGroupBox):
|
||||||
@@ -6,7 +32,7 @@ class OptionsGroupUI(QtWidgets.QGroupBox):
|
|||||||
|
|
||||||
def __init__(self, title, parent=None):
|
def __init__(self, title, parent=None):
|
||||||
# QtGui.QGroupBox.__init__(self, title, parent=parent)
|
# QtGui.QGroupBox.__init__(self, title, parent=parent)
|
||||||
super(OptionsGroupUI, self).__init__()
|
super(OptionsGroupUI, self).__init__(title=title, parent=parent)
|
||||||
self.setStyleSheet("""
|
self.setStyleSheet("""
|
||||||
QGroupBox
|
QGroupBox
|
||||||
{
|
{
|
||||||
@@ -16,4 +42,36 @@ class OptionsGroupUI(QtWidgets.QGroupBox):
|
|||||||
""")
|
""")
|
||||||
|
|
||||||
self.layout = QtWidgets.QVBoxLayout()
|
self.layout = QtWidgets.QVBoxLayout()
|
||||||
self.setLayout(self.layout)
|
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
|
||||||
|
|||||||
41
AppGUI/preferences/PreferencesSectionUI.py
Normal file
41
AppGUI/preferences/PreferencesSectionUI.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
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
|
||||||
302
AppGUI/preferences/general/GeneralAppSettingsGroupUI.py
Normal file
302
AppGUI/preferences/general/GeneralAppSettingsGroupUI.py
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
|
||||||
|
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 FlatCAM.",
|
||||||
|
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"]
|
||||||
@@ -535,7 +535,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
|
|||||||
if self.app.defaults["global_open_style"] is False:
|
if self.app.defaults["global_open_style"] is False:
|
||||||
self.app.file_opened.emit("gcode", filename)
|
self.app.file_opened.emit("gcode", filename)
|
||||||
self.app.file_saved.emit("gcode", filename)
|
self.app.file_saved.emit("gcode", filename)
|
||||||
self.app.inform.emit('[success] %s: %s' % (_("Machine Code file saved to"), filename))
|
self.app.inform.emit('[success] %s: %s' % (_("File saved to"), filename))
|
||||||
|
|
||||||
def on_edit_code_click(self, *args):
|
def on_edit_code_click(self, *args):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ CHANGELOG for FlatCAM beta
|
|||||||
|
|
||||||
=================================================
|
=================================================
|
||||||
|
|
||||||
|
31.05.2020
|
||||||
|
|
||||||
|
- structural changes in Preferences from David Robertson
|
||||||
|
|
||||||
30.05.2020
|
30.05.2020
|
||||||
|
|
||||||
- made confirmation messages for the values that are modified not to be printed in the Shell
|
- made confirmation messages for the values that are modified not to be printed in the Shell
|
||||||
|
|||||||
Reference in New Issue
Block a user