diff --git a/.gitignore b/.gitignore index f957e1b3..30a8ae85 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ build/ .DS_Store .AppleDouble .LSOverride +.vscode diff --git a/Bookmark.py b/Bookmark.py index 796c1105..a3b6629d 100644 --- a/Bookmark.py +++ b/Bookmark.py @@ -75,7 +75,7 @@ class BookmarkManager(QtWidgets.QWidget): new_vlay = QtWidgets.QVBoxLayout() layout.addLayout(new_vlay) - new_title_lbl = FCLabel('%s' % _("New Bookmark")) + new_title_lbl = FCLabel('%s' % _("New Bookmark"), bold=True) new_vlay.addWidget(new_title_lbl) grid0 = GLay(v_spacing=5, h_spacing=3) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0b2b544..5e53c371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,23 @@ CHANGELOG for FlatCAM Evo beta ================================================= +20.04.2022 + +- in Solderpaste Plugin fixed the GCode generation; make sure that if no object is selected then the first Gerber object is autoselected +- in Solderpaste Plugin fixed the CNCJob plotting +- in Solderpaste Plugin added a new parameter 'Margin' which allows reducing how much solderpaste is added and therefore adding a space between the solderpaste and the pad boundary +- all CNCJob objects generated by the Solderpaste plugin now have the GCode saved as source_code which can be saved also from the CNCJob object context menu, and edited +- updated some custom widgets in the GUI elements such that the scrolling in the Preferences can be done without blocking on some of the widgets +- remade how the Preferences Tab is constructed such that now is made on demand for each section (tab) therefore making it faster to load (once a section is loaded - by clicking its tab - it will not be reloaded in the current session) +- a fix for the latest change in the Preferences Tab +- some changes in the Geometry Editor UI's and in some cases, fixes for the right-click close action + +19.04.2022 + +- fixed and prettified the 'Light' theme +- some more fixes for the 'Light' theme +- made sure that the 'default' theme gets the 'stronger' colors + 18.04.2022 - in Geometry Editor, in Copy Tool added the 2D copy-as-array feature therefore finishing this editor plugin upgrade @@ -14,6 +31,8 @@ CHANGELOG for FlatCAM Evo beta - replaced all the FCLabel widgets that have color HTML with the new FCLabel widget that uses parameters for 'color' and weight - minor changes - added a way to allow patching FCLabel widget colors for certain cases without having to pass them each instance +- some changes in the theme selection, added that the default situation is where no theme is applied +- some string changes 17.04.2022 @@ -7472,4 +7491,3 @@ Previously added features by Dennis - Groups in Project view. - Pan view by dragging in visualizer window with pressed MMB. - OpenGL-based visualizer. - diff --git a/appDatabase.py b/appDatabase.py index 8f21063a..0d487586 100644 --- a/appDatabase.py +++ b/appDatabase.py @@ -226,7 +226,7 @@ class ToolsDB2UI: self.grid_tool.addWidget(self.dia_entry, 1, 1) # Tool Tolerance - self.tol_label = FCLabel("%s:" % _("Diameter Tolerance")) + self.tol_label = FCLabel('%s:' % _("Diameter Tolerance"), bold=True) self.tol_label.setToolTip( _("Tool tolerance. This tool will be used if the desired tool diameter\n" "is within the tolerance specified here.") @@ -262,7 +262,7 @@ class ToolsDB2UI: self.grid_tool.addWidget(self.tol_max_entry, 6, 1) # Tool Object Type - self.tool_op_label = FCLabel('%s:' % _('Target')) + self.tool_op_label = FCLabel('%s:' % _('Target'), bold=True) self.tool_op_label.setToolTip( _("The kind of Application Tool where this tool is to be used.")) diff --git a/appEditors/AppExcEditor.py b/appEditors/AppExcEditor.py index 5d701f71..91bf4cf9 100644 --- a/appEditors/AppExcEditor.py +++ b/appEditors/AppExcEditor.py @@ -4054,7 +4054,7 @@ class AppExcEditorUI: self.name_box.addWidget(self.name_entry) # Tools Drills Table Title - self.tools_table_label = FCLabel("%s" % _('Tools Table')) + self.tools_table_label = FCLabel('%s' % _('Tools Table'), bold=True) self.tools_table_label.setToolTip( _("Tools in this Excellon object\n" "when are used for drilling.") @@ -4091,7 +4091,7 @@ class AppExcEditorUI: self.ui_vertical_lay.addWidget(separator_line) # Add a new Tool - self.addtool_label = FCLabel('%s' % _('Add/Delete Tool')) + self.addtool_label = FCLabel('%s' % _('Add/Delete Tool'), bold=True) self.addtool_label.setToolTip( _("Add/Delete a tool to the tool list\n" "for this Excellon object.") @@ -4157,7 +4157,7 @@ class AppExcEditorUI: self.resize_grid.setContentsMargins(0, 0, 0, 0) self.resize_frame.setLayout(self.resize_grid) - self.drillresize_label = FCLabel('%s' % _("Resize Tool")) + self.drillresize_label = FCLabel('%s' % _("Resize Tool"), bold=True) self.drillresize_label.setToolTip( _("Resize a drill or a selection of drills.") ) @@ -4212,7 +4212,7 @@ class AppExcEditorUI: self.array_frame.setLayout(self.array_grid) # Type of Drill Array - self.drill_array_label = FCLabel('%s' % _("Add Drill Array")) + self.drill_array_label = FCLabel('%s' % _("Add Drill Array"), bold=True) self.drill_array_label.setToolTip( _("Add an array of drills (linear or circular array)") ) @@ -4352,7 +4352,7 @@ class AppExcEditorUI: self.slot_frame.setLayout(self.slot_grid) # Slot Tile Label - self.slot_label = FCLabel('%s' % _("Slot Parameters")) + self.slot_label = FCLabel('%s' % _("Slot Parameters"), bold=True) self.slot_label.setToolTip( _("Parameters for adding a slot (hole with oval shape)\n" "either single or as an part of an array.") @@ -4424,7 +4424,7 @@ class AppExcEditorUI: self.slot_array_frame.setLayout(self.slot_array_grid) # Slot Array Title - self.slot_array_label = FCLabel('%s' % _("Slot Array Parameters")) + self.slot_array_label = FCLabel('%s' % _("Slot Array Parameters"), bold=True) self.slot_array_label.setToolTip( _("Parameters for the array of slots (linear or circular array)") ) diff --git a/appEditors/AppGeoEditor.py b/appEditors/AppGeoEditor.py index 8183c40d..cc6839f4 100644 --- a/appEditors/AppGeoEditor.py +++ b/appEditors/AppGeoEditor.py @@ -3003,6 +3003,9 @@ class FCPaint(FCShapeTool): self.origin = (0, 0) self.draw_app.paint_tool.run() + def clean_up(self): + pass + class FCTransform(FCShapeTool): def __init__(self, draw_app): @@ -3016,6 +3019,9 @@ class FCTransform(FCShapeTool): self.origin = (0, 0) self.draw_app.transform_tool.run() + def clean_up(self): + pass + # ############################################### # ################ Main Application ############# @@ -3096,7 +3102,7 @@ class AppGeoEditor(QtCore.QObject): self.tools_box.addLayout(self.grid_d) # Tool diameter - tooldia_lbl = FCLabel('%s:' % _("Tool dia")) + tooldia_lbl = FCLabel('%s' % _("Tool dia"), bold=True) tooldia_lbl.setToolTip( _("Edited tool diameter.") ) @@ -3108,7 +3114,7 @@ class AppGeoEditor(QtCore.QObject): self.grid_d.addWidget(tooldia_lbl, 0, 0) self.grid_d.addWidget(self.tooldia_entry, 0, 1) # Tree Widget Title - tw_label = FCLabel('%s:' % _("Geometry Table")) + tw_label = FCLabel('%s' % _("Geometry Table"), bold=True) tw_label.setToolTip( _("The list of geometry elements inside the edited object.") ) @@ -3148,35 +3154,35 @@ class AppGeoEditor(QtCore.QObject): grid0.addWidget(separator_line, 2, 0, 1, 3) # Parameters Title - param_title = FCLabel('%s:' % _("Parameters")) + param_title = FCLabel('%s' % _("Parameters"), bold=True) param_title.setToolTip( _("Geometry parameters.") ) grid0.addWidget(param_title, 4, 0, 1, 3) # Is Valid - is_valid_lbl = FCLabel('%s:' % _("Is Valid")) + is_valid_lbl = FCLabel('%s' % _("Is Valid"), bold=True) self.is_valid_entry = FCLabel('None') grid0.addWidget(is_valid_lbl, 10, 0) grid0.addWidget(self.is_valid_entry, 10, 1, 1, 2) # Is Empty - is_empty_lbl = FCLabel('%s:' % _("Is Empty")) + is_empty_lbl = FCLabel('%s' % _("Is Empty"), bold=True) self.is_empty_entry = FCLabel('None') grid0.addWidget(is_empty_lbl, 12, 0) grid0.addWidget(self.is_empty_entry, 12, 1, 1, 2) # Is Ring - is_ring_lbl = FCLabel('%s:' % _("Is Ring")) + is_ring_lbl = FCLabel('%s' % _("Is Ring"), bold=True) self.is_ring_entry = FCLabel('None') grid0.addWidget(is_ring_lbl, 14, 0) grid0.addWidget(self.is_ring_entry, 14, 1, 1, 2) # Is CCW - is_ccw_lbl = FCLabel('%s:' % _("Is CCW")) + is_ccw_lbl = FCLabel('%s' % _("Is CCW"), bold=True) self.is_ccw_entry = FCLabel('None') self.change_orientation_btn = FCButton(_("Change")) self.change_orientation_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/orientation32.png')) @@ -3189,14 +3195,14 @@ class AppGeoEditor(QtCore.QObject): grid0.addWidget(self.change_orientation_btn, 16, 2) # Is Simple - is_simple_lbl = FCLabel('%s:' % _("Is Simple")) + is_simple_lbl = FCLabel('%s' % _("Is Simple"), bold=True) self.is_simple_entry = FCLabel('None') grid0.addWidget(is_simple_lbl, 18, 0) grid0.addWidget(self.is_simple_entry, 18, 1, 1, 2) # Length - len_lbl = FCLabel('%s:' % _("Length")) + len_lbl = FCLabel('%s' % _("Length"), bold=True) len_lbl.setToolTip( _("The length of the geometry element.") ) @@ -3207,7 +3213,7 @@ class AppGeoEditor(QtCore.QObject): grid0.addWidget(self.geo_len_entry, 20, 1, 1, 2) # Coordinates - coords_lbl = FCLabel('%s:' % _("Coordinates")) + coords_lbl = FCLabel('%s' % _("Coordinates"), bold=True) coords_lbl.setToolTip( _("The coordinates of the selected geometry element.") ) @@ -3220,7 +3226,7 @@ class AppGeoEditor(QtCore.QObject): grid0.addWidget(self.geo_coords_entry, 24, 0, 1, 3) # Vertex Points Number - vertex_lbl = FCLabel('%s:' % _("Vertex Points")) + vertex_lbl = FCLabel('%s' % _("Vertex Points"), bold=True) vertex_lbl.setToolTip( _("The number of vertex points in the selected geometry element.") ) diff --git a/appEditors/AppGerberEditor.py b/appEditors/AppGerberEditor.py index 6613ecc5..90a9cd9e 100644 --- a/appEditors/AppGerberEditor.py +++ b/appEditors/AppGerberEditor.py @@ -6174,7 +6174,7 @@ class AppGerberEditorUI: # ############################################################################################################# # #################################### Gerber Apertures Table ################################################# # ############################################################################################################# - self.apertures_table_label = FCLabel('%s:' % _('Apertures')) + self.apertures_table_label = FCLabel('%s:' % _('Apertures'), bold=True) self.apertures_table_label.setToolTip( _("Apertures Table for the Gerber Object.") ) @@ -6225,7 +6225,7 @@ class AppGerberEditorUI: self.apertures_box.addLayout(grid1) # Title - apadd_del_lbl = FCLabel('%s:' % _('Add/Delete Aperture')) + apadd_del_lbl = FCLabel('%s:' % _('Add/Delete Aperture'), bold=True) apadd_del_lbl.setToolTip( _("Add/Delete an aperture in the aperture table") ) @@ -6334,7 +6334,7 @@ class AppGerberEditorUI: self.shape_grid.addWidget(separator_line, 2, 0, 1, 3) # Parameters Title - param_title = FCLabel('%s' % _("Parameters")) + param_title = FCLabel('%s' % _("Parameters"), bold=True) param_title.setToolTip( _("Geometry parameters.") ) @@ -6343,7 +6343,7 @@ class AppGerberEditorUI: p_grid = GLay(v_spacing=5, h_spacing=3, c_stretch=[0, 0, 0, 1, 0]) # Is Valid - valid_lbl = FCLabel('%s:' % _("Valid")) + valid_lbl = FCLabel('%s' % _("Valid"), bold=True) valid_lbl.setToolTip( _("Show if the selected polygon is valid.") ) @@ -6352,7 +6352,7 @@ class AppGerberEditorUI: p_grid.addWidget(self.is_valid_entry, 0, 1) # Area - area_lbl = FCLabel('%s:' % _("Area")) + area_lbl = FCLabel('%s' % _("Area"), bold=True) area_lbl.setToolTip( _("Show the area of the selected polygon.") ) @@ -6397,7 +6397,7 @@ class AppGerberEditorUI: self.shape_grid.addWidget(separator_line, 12, 0, 1, 3) # Simplification Title - simplif_lbl = FCLabel('%s:' % _("Simplification")) + simplif_lbl = FCLabel('%s' % _("Simplification"), bold=True) simplif_lbl.setToolTip( _("Simplify a geometry by reducing its vertex points number.") ) @@ -6445,7 +6445,7 @@ class AppGerberEditorUI: self.buffer_tool_frame.hide() # Title - buf_title_lbl = FCLabel('%s:' % _('Buffer Aperture')) + buf_title_lbl = FCLabel('%s:' % _('Buffer Aperture'), bold=True) buf_title_lbl.setToolTip( _("Buffer a aperture in the aperture list") ) @@ -6503,7 +6503,7 @@ class AppGerberEditorUI: self.scale_tool_frame.hide() # Title - scale_title_lbl = FCLabel('%s:' % _('Scale Aperture')) + scale_title_lbl = FCLabel('%s:' % _('Scale Aperture'), bold=True) scale_title_lbl.setToolTip( _("Scale a aperture in the aperture list") ) @@ -6552,7 +6552,7 @@ class AppGerberEditorUI: self.ma_tools_box.addWidget(separator_line) # Title - ma_title_lbl = FCLabel('%s:' % _('Mark polygons')) + ma_title_lbl = FCLabel('%s:' % _('Mark polygons'), bold=True) ma_title_lbl.setToolTip( _("Mark the polygon areas.") ) @@ -6632,7 +6632,7 @@ class AppGerberEditorUI: self.array_box.addLayout(array_grid) # Title - self.padarray_label = FCLabel('%s' % _("Add Pad Array")) + self.padarray_label = FCLabel('%s' % _("Add Pad Array"), bold=True) self.padarray_label.setToolTip( _("Add an array of pads (linear or circular array)") ) diff --git a/appEditors/geo_plugins/GeoCirclePlugin.py b/appEditors/geo_plugins/GeoCirclePlugin.py index 397b2451..bf45b5aa 100644 --- a/appEditors/geo_plugins/GeoCirclePlugin.py +++ b/appEditors/geo_plugins/GeoCirclePlugin.py @@ -187,48 +187,70 @@ class CircleEditorUI: self.circle_tool_box.addLayout(grid0) # Position - self.pos_lbl = FCLabel('%s' % _("Position")) + self.pos_lbl = FCLabel('%s' % _("Position"), color='red', bold=True) grid0.addWidget(self.pos_lbl, 0, 0, 1, 3) + # ############################################################################################################# + # Position Frame + # ############################################################################################################# + pos_frame = FCFrame() + grid0.addWidget(pos_frame, 2, 0, 1, 2) + + pos_grid = GLay(v_spacing=5, h_spacing=3) + pos_frame.setLayout(pos_grid) + # X Pos self.x_lbl = FCLabel('%s:' % _("X")) self.x_entry = FCDoubleSpinner() self.x_entry.set_precision(self.decimals) self.x_entry.set_range(-10000.0000, 10000.0000) - grid0.addWidget(self.x_lbl, 2, 0) - grid0.addWidget(self.x_entry, 2, 1, 1, 2) + pos_grid.addWidget(self.x_lbl, 0, 0) + pos_grid.addWidget(self.x_entry, 0, 1, 1, 2) # Y Pos self.y_lbl = FCLabel('%s:' % _("Y")) self.y_entry = FCDoubleSpinner() self.y_entry.set_precision(self.decimals) self.y_entry.set_range(-10000.0000, 10000.0000) - grid0.addWidget(self.y_lbl, 4, 0) - grid0.addWidget(self.y_entry, 4, 1, 1, 2) + pos_grid.addWidget(self.y_lbl, 2, 0) + pos_grid.addWidget(self.y_entry, 2, 1, 1, 2) + + # Radius + self.radius_lbl = FCLabel('%s' % _("Radius"), bold=True, color='blue') + grid0.addWidget(self.radius_lbl, 4, 0) + + # ############################################################################################################# + # Radius Frame + # ############################################################################################################# + rad_frame = FCFrame() + grid0.addWidget(rad_frame, 6, 0, 1, 2) + + rad_grid = GLay(v_spacing=5, h_spacing=3) + rad_frame.setLayout(rad_grid) # Radius X - self.radius_x_lbl = FCLabel('%s X:' % _("Radius")) + self.radius_x_lbl = FCLabel('%s:' % "X") self.radius_x_entry = FCDoubleSpinner() self.radius_x_entry.set_precision(self.decimals) self.radius_x_entry.set_range(0.0000, 10000.0000) - grid0.addWidget(self.radius_x_lbl, 6, 0) - grid0.addWidget(self.radius_x_entry, 6, 1) + rad_grid.addWidget(self.radius_x_lbl, 0, 0) + rad_grid.addWidget(self.radius_x_entry, 0, 1) # Radius Y - self.radius_y_lbl = FCLabel('%s Y:' % _("Radius")) + self.radius_y_lbl = FCLabel('%s:' % "Y") self.radius_y_entry = FCDoubleSpinner() self.radius_y_entry.set_precision(self.decimals) self.radius_y_entry.set_range(0.0000, 10000.0000) - grid0.addWidget(self.radius_y_lbl, 7, 0) - grid0.addWidget(self.radius_y_entry, 7, 1) + rad_grid.addWidget(self.radius_y_lbl, 1, 0) + rad_grid.addWidget(self.radius_y_entry, 1, 1) # Angle self.angle_lbl = FCLabel('%s:' % _("Angle")) self.angle_entry = FCDoubleSpinner() self.angle_entry.set_precision(self.decimals) self.angle_entry.set_range(0.0000, 360.0000) - grid0.addWidget(self.angle_lbl, 8, 0) - grid0.addWidget(self.angle_entry, 8, 1) + rad_grid.addWidget(self.angle_lbl, 2, 0) + rad_grid.addWidget(self.angle_entry, 2, 1) # Radius link self.radius_link_btn = QtWidgets.QToolButton() @@ -236,17 +258,19 @@ class CircleEditorUI: self.radius_link_btn.setSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Expanding) self.radius_link_btn.setCheckable(True) - grid0.addWidget(self.radius_link_btn, 6, 2, 3, 1) + rad_grid.addWidget(self.radius_link_btn, 0, 2, 3, 1) # Buttons self.add_button = FCButton(_("Add")) self.add_button.setIcon(QtGui.QIcon(self.app.resource_location + '/plus16.png')) grid0.addWidget(self.add_button, 18, 0, 1, 3) + GLay.set_common_column_size([grid0, pos_grid, rad_grid], 0) + self.layout.addStretch(1) # Note - self.note_lbl = FCLabel('%s' % _("Note")) + self.note_lbl = FCLabel('%s' % _("Note"), bold=True) self.layout.addWidget(self.note_lbl) self.note_description_lbl = FCLabel('%s' % _("Shift + click to select a shape for modification.")) self.layout.addWidget(self.note_description_lbl) @@ -256,14 +280,14 @@ class CircleEditorUI: def on_link_checked(self, checked): if checked: - self.radius_x_lbl.set_value('%s:' % _("Radius")) + self.radius_x_lbl.set_value('%s:' % _("Value")) self.radius_y_lbl.setDisabled(True) self.radius_y_entry.setDisabled(True) self.radius_y_entry.set_value(self.radius_x_entry.get_value()) self.angle_lbl.setDisabled(True) self.angle_entry.setDisabled(True) else: - self.radius_x_lbl.set_value('%s X:' % _("Radius")) + self.radius_x_lbl.set_value('%s:' % "X") self.radius_y_lbl.setDisabled(False) self.radius_y_entry.setDisabled(False) self.angle_lbl.setDisabled(False) diff --git a/appEditors/geo_plugins/GeoCopyPlugin.py b/appEditors/geo_plugins/GeoCopyPlugin.py index fb71cd2f..3f7aa75b 100644 --- a/appEditors/geo_plugins/GeoCopyPlugin.py +++ b/appEditors/geo_plugins/GeoCopyPlugin.py @@ -164,7 +164,7 @@ class CopyEditorUI: grid0.addWidget(separator_line, 4, 0, 1, 2) # Type of Array - self.mode_label = FCLabel('%s:' % _("Mode")) + self.mode_label = FCLabel('%s:' % _("Mode"), bold=True) self.mode_label.setToolTip( _("Single copy or special (array of copies)") ) @@ -461,15 +461,11 @@ class CopyEditorUI: minval, self.decimals, maxval), False) - else: - self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False) 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) - else: - self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False) def on_copy_mode(self, val): if val == 'n': @@ -496,7 +492,7 @@ class CopyEditorUI: self.rows.valueChanged.connect(self.on_rows_cols_value_changed) self.columns.valueChanged.connect(self.on_rows_cols_value_changed) - self.app.inform.emit(_("Click to place ...")) + self.app.inform.emit(_("Click on reference location ...")) else: if val == 'linear': self.array_circular_frame.hide() @@ -505,7 +501,7 @@ class CopyEditorUI: self.spacing_frame.hide() self.offset_frame.hide() - self.app.inform.emit(_("Click to place ...")) + self.app.inform.emit(_("Click on reference location ...")) else: # 'circular' self.array_circular_frame.show() self.array_linear_frame.hide() diff --git a/appEditors/geo_plugins/GeoRectanglePlugin.py b/appEditors/geo_plugins/GeoRectanglePlugin.py index 9acc15d6..6815767d 100644 --- a/appEditors/geo_plugins/GeoRectanglePlugin.py +++ b/appEditors/geo_plugins/GeoRectanglePlugin.py @@ -219,8 +219,20 @@ class RectangleEditorUI: grid0 = GLay(v_spacing=5, h_spacing=3) self.rect_tool_box.addLayout(grid0) + # Position + self.pos_lbl = FCLabel('%s' % _("Position"), bold=True, color='red') + grid0.addWidget(self.pos_lbl, 0, 0, 1, 2) + # ############################################################################################################# + # Position Frame + # ############################################################################################################# + pos_frame = FCFrame() + grid0.addWidget(pos_frame, 2, 0, 1, 2) + + pos_grid = GLay(v_spacing=5, h_spacing=3) + pos_frame.setLayout(pos_grid) + # Anchor - self.anchor_lbl = FCLabel('%s:' % _("Anchor")) + self.anchor_lbl = FCLabel('%s:' % _("Anchor"), bold=False) choices = [ {"label": _("T Left"), "value": "tl"}, {"label": _("T Right"), "value": "tr"}, @@ -229,31 +241,38 @@ class RectangleEditorUI: {"label": _("Center"), "value": "c"} ] self.anchor_radio = RadioSetCross(choices, compact=True) - grid0.addWidget(self.anchor_lbl, 0, 0) - grid0.addWidget(self.anchor_radio, 0, 1) - - # Position - self.pos_lbl = FCLabel('%s' % _("Position")) - grid0.addWidget(self.pos_lbl, 2, 0, 1, 2) + pos_grid.addWidget(self.anchor_lbl, 0, 0) + pos_grid.addWidget(self.anchor_radio, 0, 1) # X Pos self.x_lbl = FCLabel('%s:' % _("X")) self.x_entry = FCDoubleSpinner() self.x_entry.set_precision(self.decimals) self.x_entry.set_range(-10000.0000, 10000.0000) - grid0.addWidget(self.x_lbl, 4, 0) - grid0.addWidget(self.x_entry, 4, 1) + pos_grid.addWidget(self.x_lbl, 2, 0) + pos_grid.addWidget(self.x_entry, 2, 1) # Y Pos self.y_lbl = FCLabel('%s:' % _("Y")) self.y_entry = FCDoubleSpinner() self.y_entry.set_precision(self.decimals) self.y_entry.set_range(-10000.0000, 10000.0000) - grid0.addWidget(self.y_lbl, 6, 0) - grid0.addWidget(self.y_entry, 6, 1) + pos_grid.addWidget(self.y_lbl, 4, 0) + pos_grid.addWidget(self.y_entry, 4, 1) + + self.corner_lbl = FCLabel('%s' % _("Corner"), bold=True, color='green') + grid0.addWidget(self.corner_lbl, 4, 0, 1, 2) + # ############################################################################################################# + # Corner Frame + # ############################################################################################################# + cor_frame = FCFrame() + grid0.addWidget(cor_frame, 6, 0, 1, 2) + + cor_grid = GLay(v_spacing=5, h_spacing=3) + cor_frame.setLayout(cor_grid) # Corner Type - self.corner_lbl = FCLabel('%s:' % _("Corner")) + self.corner_lbl = FCLabel('%s:' % _("Type")) self.corner_lbl.setToolTip( _("There are 3 types of corners:\n" " - 'Round': the corners are rounded\n" @@ -265,42 +284,51 @@ class RectangleEditorUI: {'label': _('Square'), 'value': 's'}, {'label': _('Beveled'), 'value': 'b'}, ], orientation='vertical', compact=True) - grid0.addWidget(self.corner_lbl, 8, 0) - grid0.addWidget(self.corner_radio, 8, 1) + cor_grid.addWidget(self.corner_lbl, 0, 0) + cor_grid.addWidget(self.corner_radio, 0, 1) # Radius self.radius_lbl = FCLabel('%s:' % _("Radius")) self.radius_entry = FCDoubleSpinner() self.radius_entry.set_precision(self.decimals) self.radius_entry.set_range(0.0000, 10000.0000) - grid0.addWidget(self.radius_lbl, 10, 0) - grid0.addWidget(self.radius_entry, 10, 1) + cor_grid.addWidget(self.radius_lbl, 2, 0) + cor_grid.addWidget(self.radius_entry, 2, 1) # Size - self.size_lbl = FCLabel('%s' % _("Size")) - grid0.addWidget(self.size_lbl, 12, 0, 1, 2) + self.size_lbl = FCLabel('%s' % _("Size"), bold=True, color='indigo') + grid0.addWidget(self.size_lbl, 8, 0, 1, 2) + # ############################################################################################################# + # Size Frame + # ############################################################################################################# + size_frame = FCFrame() + grid0.addWidget(size_frame, 10, 0, 1, 2) + + size_grid = GLay(v_spacing=5, h_spacing=3) + size_frame.setLayout(size_grid) # Length self.length_lbl = FCLabel('%s:' % _("Length")) self.length_entry = NumericalEvalEntry() - grid0.addWidget(self.length_lbl, 14, 0) - grid0.addWidget(self.length_entry, 14, 1) + size_grid.addWidget(self.length_lbl, 0, 0) + size_grid.addWidget(self.length_entry, 0, 1) # Width self.width_lbl = FCLabel('%s:' % _("Width")) self.width_entry = NumericalEvalEntry() - grid0.addWidget(self.width_lbl, 16, 0) - grid0.addWidget(self.width_entry, 16, 1) + size_grid.addWidget(self.width_lbl, 2, 0) + size_grid.addWidget(self.width_entry, 2, 1) # Buttons self.add_button = FCButton(_("Add")) self.add_button.setIcon(QtGui.QIcon(self.app.resource_location + '/plus16.png')) grid0.addWidget(self.add_button, 18, 0, 1, 2) + GLay.set_common_column_size([grid0, pos_grid, cor_grid, size_grid], 0) self.layout.addStretch(1) # Note - self.note_lbl = FCLabel('%s' % _("Note")) + self.note_lbl = FCLabel('%s' % _("Note"), bold=True) self.layout.addWidget(self.note_lbl) self.note_description_lbl = FCLabel('%s' % _("Shift + click to select a shape for modification.")) self.layout.addWidget(self.note_description_lbl) diff --git a/appEditors/geo_plugins/GeoSimplificationPlugin.py b/appEditors/geo_plugins/GeoSimplificationPlugin.py index d350813e..cc89b1e1 100644 --- a/appEditors/geo_plugins/GeoSimplificationPlugin.py +++ b/appEditors/geo_plugins/GeoSimplificationPlugin.py @@ -196,43 +196,52 @@ class SimplificationEditorUI: self.simp_tools_box.addLayout(grid0) # Coordinates - coords_lbl = FCLabel('%s:' % _("Coordinates")) + coords_lbl = FCLabel('%s' % _("Coordinates"), bold=True, color='red') coords_lbl.setToolTip( _("The coordinates of the selected geometry element.") ) - grid0.addWidget(coords_lbl, 22, 0, 1, 3) + grid0.addWidget(coords_lbl, 0, 0, 1, 2) + + # ############################################################################################################# + # Coordinates Frame + # ############################################################################################################# + coors_frame = FCFrame() + grid0.addWidget(coors_frame, 2, 0, 1, 2) + + coords_grid = GLay(v_spacing=5, h_spacing=3) + coors_frame.setLayout(coords_grid) self.geo_coords_entry = FCTextEdit() self.geo_coords_entry.setPlaceholderText( _("The coordinates of the selected geometry element.") ) - grid0.addWidget(self.geo_coords_entry, 24, 0, 1, 3) + coords_grid.addWidget(self.geo_coords_entry, 0, 0, 1, 2) # Vertex Points Number - vertex_lbl = FCLabel('%s:' % _("Vertex Points")) + vertex_lbl = FCLabel('%s:' % _("Vertex Points"), bold=False) vertex_lbl.setToolTip( _("The number of vertex points in the selected geometry element.") ) self.geo_vertex_entry = FCEntry(decimals=self.decimals) self.geo_vertex_entry.setReadOnly(True) - grid0.addWidget(vertex_lbl, 26, 0) - grid0.addWidget(self.geo_vertex_entry, 26, 1, 1, 2) - - separator_line = QtWidgets.QFrame() - separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine) - separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) - grid0.addWidget(separator_line, 28, 0, 1, 3) + coords_grid.addWidget(vertex_lbl, 2, 0) + coords_grid.addWidget(self.geo_vertex_entry, 2, 1) # Simplification Title - simplif_lbl = FCLabel('%s:' % _("Simplification")) - simplif_lbl.setToolTip( - _("Simplify a geometry by reducing its vertex points number.") - ) - grid0.addWidget(simplif_lbl, 30, 0, 1, 3) + par_lbl = FCLabel('%s' % _("Parameters"), bold=True, color='blue') + grid0.addWidget(par_lbl, 4, 0, 1, 2) + # ############################################################################################################# + # Parameters Frame + # ############################################################################################################# + par_frame = FCFrame() + grid0.addWidget(par_frame, 6, 0, 1, 2) + + par_grid = GLay(v_spacing=5, h_spacing=3) + par_frame.setLayout(par_grid) # Simplification Tolerance - simplification_tol_lbl = FCLabel('%s:' % _("Tolerance")) + simplification_tol_lbl = FCLabel('%s' % _("Tolerance"), bold=True) simplification_tol_lbl.setToolTip( _("All points in the simplified object will be\n" "within the tolerance distance of the original geometry.") @@ -242,8 +251,8 @@ class SimplificationEditorUI: self.geo_tol_entry.setSingleStep(10 ** -self.decimals) self.geo_tol_entry.set_range(0.0000, 10000.0000) - grid0.addWidget(simplification_tol_lbl, 32, 0) - grid0.addWidget(self.geo_tol_entry, 32, 1, 1, 2) + par_grid.addWidget(simplification_tol_lbl, 0, 0) + par_grid.addWidget(self.geo_tol_entry, 0, 1) # Simplification button self.simplification_btn = FCButton(_("Simplify")) @@ -258,6 +267,7 @@ class SimplificationEditorUI: } """) - grid0.addWidget(self.simplification_btn, 34, 0, 1, 3) + self.layout.addWidget(self.simplification_btn) + GLay.set_common_column_size([grid0, coords_grid, par_grid], 0) self.layout.addStretch(1) diff --git a/appGUI/GUIElements.py b/appGUI/GUIElements.py index c7b01204..bf07c8e3 100644 --- a/appGUI/GUIElements.py +++ b/appGUI/GUIElements.py @@ -1045,12 +1045,25 @@ class FCColorEntry(QtWidgets.QFrame): return value[7:9] +class FCSlider(QtWidgets.QSlider): + + def __int__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) + + def wheelEvent(self, event: QtGui.QWheelEvent) -> None: + if self.hasFocus(): + super().wheelEvent(event) + else: + event.ignore() + + class FCSliderWithSpinner(QtWidgets.QFrame): def __init__(self, min=0, max=100, step=1, **kwargs): super().__init__(**kwargs) - self.slider = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal) + self.slider = FCSlider(QtCore.Qt.Orientation.Horizontal) self.slider.setMinimum(min) self.slider.setMaximum(max) self.slider.setSingleStep(step) @@ -1075,7 +1088,7 @@ class FCSliderWithSpinner(QtWidgets.QFrame): self.spinner.valueChanged.connect(self._on_spinner) self.valueChanged = self.spinner.valueChanged - self.slider.installEventFilter(self) + # self.slider.installEventFilter(self) def get_value(self) -> int: return self.spinner.get_value() @@ -1091,11 +1104,11 @@ class FCSliderWithSpinner(QtWidgets.QFrame): slider_value = self.slider.value() self.spinner.set_value(slider_value) - def eventFilter(self, object, event): - if event.type() == QtCore.QEvent.Type.Wheel: - if not self.slider.hasFocus(): - return True - return False + # def eventFilter(self, object, event): + # if event.type() == QtCore.QEvent.Type.Wheel: + # if not self.slider.hasFocus(): + # return True + # return False class FCSpinner(QtWidgets.QSpinBox): @@ -1127,6 +1140,7 @@ class FCSpinner(QtWidgets.QSpinBox): self.prev_readyToEdit = True self.menu = None + self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) if policy: sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Ignored, QtWidgets.QSizePolicy.Policy.Preferred) @@ -1151,10 +1165,17 @@ class FCSpinner(QtWidgets.QSpinBox): else: super().keyPressEvent(event) - def wheelEvent(self, *args, **kwargs): - # should work only there is a focus in the lineedit of the SpinBox - if self.readyToEdit is False: - super().wheelEvent(*args, **kwargs) + def wheelEvent(self, event: QtGui.QWheelEvent) -> None: + if self.hasFocus(): + super().wheelEvent(event) + else: + event.ignore() + # def wheelEvent(self, *args, **kwargs): + # # should work only there is a focus in the lineedit of the SpinBox + # if self.readyToEdit is False: + # super().wheelEvent(*args, **kwargs) + # return + # self.clearFocus() def on_edit_finished(self): self.clearFocus() @@ -1326,173 +1347,6 @@ class FCSpinner(QtWidgets.QSpinBox): # return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height()) -class FCDoubleSlider(QtWidgets.QSlider): - # frome here: https://stackoverflow.com/questions/42820380/use-float-for-qslider - - # create our our signal that we can connect to if necessary - doubleValueChanged = pyqtSignal(float) - - def __init__(self, decimals=3, orientation='horizontal', *args, **kargs): - if orientation == 'horizontal': - super(FCDoubleSlider, self).__init__(QtCore.Qt.Orientation.Horizontal, *args, **kargs) - else: - super(FCDoubleSlider, self).__init__(QtCore.Qt.Orientation.Vertical, *args, **kargs) - - self._multi = 10 ** decimals - - self.valueChanged.connect(self.emitDoubleValueChanged) - - def emitDoubleValueChanged(self): - value = float(super(FCDoubleSlider, self).value()) / self._multi - self.doubleValueChanged.emit(value) - - def value(self): - return float(super(FCDoubleSlider, self).value()) / self._multi - - def get_value(self): - return self.value() - - def setMinimum(self, value): - return super(FCDoubleSlider, self).setMinimum(int(value * self._multi)) - - def setMaximum(self, value): - return super(FCDoubleSlider, self).setMaximum(int(value * self._multi)) - - def setSingleStep(self, value): - return super(FCDoubleSlider, self).setSingleStep(int(value * self._multi)) - - def singleStep(self): - return float(super(FCDoubleSlider, self).singleStep()) / self._multi - - def set_value(self, value): - super(FCDoubleSlider, self).setValue(int(value * self._multi)) - - def set_precision(self, decimals): - self._multi = 10 ** decimals - - def set_range(self, min, max): - self.blockSignals(True) - self.setRange(int(min * self._multi), int(max * self._multi)) - self.blockSignals(False) - - -class FCSliderWithDoubleSpinner(QtWidgets.QFrame): - - def __init__(self, min=0, max=10000.0000, step=1, precision=4, orientation='horizontal', **kwargs): - super().__init__(**kwargs) - - self.slider = FCDoubleSlider(orientation=orientation) - self.slider.setMinimum(min) - self.slider.setMaximum(max) - self.slider.setSingleStep(step) - self.slider.set_range(min, max) - - self.spinner = FCDoubleSpinner() - self.spinner.set_range(min, max) - self.spinner.set_precision(precision) - - self.spinner.set_step(step) - self.spinner.setMinimumWidth(70) - - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, - QtWidgets.QSizePolicy.Policy.Preferred) - self.spinner.setSizePolicy(sizePolicy) - - self.layout = QtWidgets.QHBoxLayout() - self.layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter) - self.layout.setContentsMargins(0, 0, 0, 0) - self.layout.addWidget(self.slider) - self.layout.addWidget(self.spinner) - self.setLayout(self.layout) - - self.slider.doubleValueChanged.connect(self._on_slider) - self.spinner.valueChanged.connect(self._on_spinner) - - self.valueChanged = self.spinner.valueChanged - - def set_precision(self, prec): - self.spinner.set_precision(prec) - - def setSingleStep(self, step): - self.spinner.set_step(step) - - def set_range(self, min, max): - self.spinner.set_range(min, max) - self.slider.set_range(min, max) - - def set_minimum(self, min): - self.slider.setMinimum(min) - self.spinner.setMinimum(min) - - def set_maximum(self, max): - self.slider.setMaximum(max) - self.spinner.setMaximum(max) - - def get_value(self) -> float: - return self.spinner.get_value() - - def set_value(self, value: float): - self.spinner.set_value(value) - - def _on_spinner(self): - spinner_value = self.spinner.value() - self.slider.set_value(spinner_value) - - def _on_slider(self): - slider_value = self.slider.value() - self.spinner.set_value(slider_value) - - -class FCButtonWithDoubleSpinner(QtWidgets.QFrame): - - def __init__(self, min=0, max=100, step=1, decimals=4, button_text='', button_icon=None, callback=None, **kwargs): - super().__init__(**kwargs) - - self.button = QtWidgets.QToolButton() - if button_text != '': - self.button.setText(button_text) - if button_icon: - self.button.setIcon(button_icon) - - self.spinner = FCDoubleSpinner() - self.spinner.set_range(min, max) - self.spinner.set_step(step) - self.spinner.set_precision(decimals) - self.spinner.setMinimumWidth(70) - - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, - QtWidgets.QSizePolicy.Policy.Preferred) - self.spinner.setSizePolicy(sizePolicy) - - self.layout = QtWidgets.QHBoxLayout() - self.layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter) - self.layout.setContentsMargins(0, 0, 0, 0) - self.layout.addWidget(self.spinner) - self.layout.addWidget(self.button) - self.setLayout(self.layout) - - self.valueChanged = self.spinner.valueChanged - - self._callback = callback - self.button.clicked.connect(self._callback) - - def get_value(self) -> float: - return self.spinner.get_value() - - def set_value(self, value: float): - self.spinner.set_value(value) - - def set_callback(self, callback): - self._callback = callback - - def set_text(self, txt: str): - if txt: - self.button.setText(txt) - - def set_icon(self, icon: QtGui.QIcon): - self.button.setIcon(icon) - - class FCDoubleSpinner(QtWidgets.QDoubleSpinBox): returnPressed = QtCore.pyqtSignal() confirmation_signal = QtCore.pyqtSignal(bool, float, float) @@ -1507,6 +1361,7 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox): :param policy: by default the widget will not compact as much as possible on horizontal """ super(FCDoubleSpinner, self).__init__(parent) + self.cursor_pos = None self.readyToEdit = True self.editingFinished.connect(self.on_edit_finished) @@ -1536,6 +1391,7 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox): self.prev_readyToEdit = True self.menu = None + self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) if policy: sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Ignored, QtWidgets.QSizePolicy.Policy.Preferred) @@ -1565,10 +1421,15 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox): else: super().keyPressEvent(event) - def wheelEvent(self, *args, **kwargs): - # should work only there is a focus in the lineedit of the SpinBox - if self.readyToEdit is False: - super().wheelEvent(*args, **kwargs) + def wheelEvent(self, event: QtGui.QWheelEvent) -> None: + if self.hasFocus(): + super().wheelEvent(event) + else: + event.ignore() + # def wheelEvent(self, *args, **kwargs): + # # should work only there is a focus in the lineedit of the SpinBox + # if self.readyToEdit is False: + # super().wheelEvent(*args, **kwargs) def focusOutEvent(self, e): # don't focus out if the user requests an popup menu @@ -1747,6 +1608,189 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox): # return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height()) +class FCSliderWithDoubleSpinner(QtWidgets.QFrame): + + def __init__(self, min=0, max=10000.0000, step=1, precision=4, orientation='horizontal', **kwargs): + super().__init__(**kwargs) + + self.slider = FCDoubleSlider(orientation=orientation) + self.slider.setMinimum(min) + self.slider.setMaximum(max) + self.slider.setSingleStep(step) + self.slider.set_range(min, max) + + self.spinner = FCDoubleSpinner() + self.spinner.set_range(min, max) + self.spinner.set_precision(precision) + + self.spinner.set_step(step) + self.spinner.setMinimumWidth(70) + + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, + QtWidgets.QSizePolicy.Policy.Preferred) + self.spinner.setSizePolicy(sizePolicy) + + self.layout = QtWidgets.QHBoxLayout() + self.layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter) + self.layout.setContentsMargins(0, 0, 0, 0) + self.layout.addWidget(self.slider) + self.layout.addWidget(self.spinner) + self.setLayout(self.layout) + + self.slider.doubleValueChanged.connect(self._on_slider) + self.spinner.valueChanged.connect(self._on_spinner) + + self.valueChanged = self.spinner.valueChanged + + self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) + + def wheelEvent(self, event: QtGui.QWheelEvent) -> None: + if self.hasFocus(): + super().wheelEvent(event) + else: + event.ignore() + + def set_precision(self, prec): + self.spinner.set_precision(prec) + + def setSingleStep(self, step): + self.spinner.set_step(step) + + def set_range(self, min, max): + self.spinner.set_range(min, max) + self.slider.set_range(min, max) + + def set_minimum(self, min): + self.slider.setMinimum(min) + self.spinner.setMinimum(min) + + def set_maximum(self, max): + self.slider.setMaximum(max) + self.spinner.setMaximum(max) + + def get_value(self) -> float: + return self.spinner.get_value() + + def set_value(self, value: float): + self.spinner.set_value(value) + + def _on_spinner(self): + spinner_value = self.spinner.value() + self.slider.set_value(spinner_value) + + def _on_slider(self): + slider_value = self.slider.value() + self.spinner.set_value(slider_value) + + +class FCDoubleSlider(QtWidgets.QSlider): + # frome here: https://stackoverflow.com/questions/42820380/use-float-for-qslider + + # create our own signal that we can connect to if necessary + doubleValueChanged = pyqtSignal(float) + + def __init__(self, decimals=3, orientation='horizontal', *args, **kargs): + if orientation == 'horizontal': + super(FCDoubleSlider, self).__init__(QtCore.Qt.Orientation.Horizontal, *args, **kargs) + else: + super(FCDoubleSlider, self).__init__(QtCore.Qt.Orientation.Vertical, *args, **kargs) + + self._multi = 10 ** decimals + + self.valueChanged.connect(self.emitDoubleValueChanged) + + self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) + + def wheelEvent(self, event: QtGui.QWheelEvent) -> None: + if self.hasFocus(): + super().wheelEvent(event) + else: + event.ignore() + + def emitDoubleValueChanged(self): + value = float(super(FCDoubleSlider, self).value()) / self._multi + self.doubleValueChanged.emit(value) + + def value(self): + return float(super(FCDoubleSlider, self).value()) / self._multi + + def get_value(self): + return self.value() + + def setMinimum(self, value): + return super(FCDoubleSlider, self).setMinimum(int(value * self._multi)) + + def setMaximum(self, value): + return super(FCDoubleSlider, self).setMaximum(int(value * self._multi)) + + def setSingleStep(self, value): + return super(FCDoubleSlider, self).setSingleStep(int(value * self._multi)) + + def singleStep(self): + return float(super(FCDoubleSlider, self).singleStep()) / self._multi + + def set_value(self, value): + super(FCDoubleSlider, self).setValue(int(value * self._multi)) + + def set_precision(self, decimals): + self._multi = 10 ** decimals + + def set_range(self, min, max): + self.blockSignals(True) + self.setRange(int(min * self._multi), int(max * self._multi)) + self.blockSignals(False) + + +class FCButtonWithDoubleSpinner(QtWidgets.QFrame): + + def __init__(self, min=0, max=100, step=1, decimals=4, button_text='', button_icon=None, callback=None, **kwargs): + super().__init__(**kwargs) + + self.button = QtWidgets.QToolButton() + if button_text != '': + self.button.setText(button_text) + if button_icon: + self.button.setIcon(button_icon) + + self.spinner = FCDoubleSpinner() + self.spinner.set_range(min, max) + self.spinner.set_step(step) + self.spinner.set_precision(decimals) + self.spinner.setMinimumWidth(70) + + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, + QtWidgets.QSizePolicy.Policy.Preferred) + self.spinner.setSizePolicy(sizePolicy) + + self.layout = QtWidgets.QHBoxLayout() + self.layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter) + self.layout.setContentsMargins(0, 0, 0, 0) + self.layout.addWidget(self.spinner) + self.layout.addWidget(self.button) + self.setLayout(self.layout) + + self.valueChanged = self.spinner.valueChanged + + self._callback = callback + self.button.clicked.connect(self._callback) + + def get_value(self) -> float: + return self.spinner.get_value() + + def set_value(self, value: float): + self.spinner.set_value(value) + + def set_callback(self, callback): + self._callback = callback + + def set_text(self, txt: str): + if txt: + self.button.setText(txt) + + def set_icon(self, icon: QtGui.QIcon): + self.button.setIcon(icon) + + class FCCheckBox(QtWidgets.QCheckBox): def __init__(self, label='', parent=None): super(FCCheckBox, self).__init__(str(label), parent) @@ -2491,6 +2535,7 @@ class FCComboBox(QtWidgets.QComboBox): self._set_last = False self._obj_type = None + self.setFocusPolicy(Qt.FocusPolicy.StrongFocus) if policy is True: sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Ignored, QtWidgets.QSizePolicy.Policy.Preferred) @@ -2508,8 +2553,11 @@ class FCComboBox(QtWidgets.QComboBox): return True return False - def wheelEvent(self, *args, **kwargs): - pass + def wheelEvent(self, event: QtGui.QWheelEvent) -> None: + if self.hasFocus(): + super().wheelEvent(event) + else: + event.ignore() def get_value(self): return str(self.currentText()) diff --git a/appGUI/MainGUI.py b/appGUI/MainGUI.py index 56bd706b..8aa2d52f 100644 --- a/appGUI/MainGUI.py +++ b/appGUI/MainGUI.py @@ -41,6 +41,8 @@ import gettext import appTranslation as fcTranslate import builtins +import darkdetect + fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -52,8 +54,40 @@ class MainGUI(QtWidgets.QMainWindow): final_save = QtCore.pyqtSignal(name='saveBeforeExit') # screenChanged = QtCore.pyqtSignal(QtGui.QScreen, QtGui.QScreen) + # Mapping of colors used for text on Light theme to + # similar colors safe for use on Dark theme + # 'input_color': (light_color, dark_color), + theme_safe_colors = { + "blue": "#1F80FF", + "brown": "#CC9966", + "darkgreen": "#008015", + "darkorange": "darkorange", + "green": "#00CC22", + "indigo": "#9457EB", + "magenta": "magenta", + "orange": "orange", + "purple": "#B284BE", + "red": "salmon", + "teal": "teal", + "tomato": "tomato", + } + def theme_safe_color(self, color): - return color + """ + Some colors do not work well with light or dark backgrounds making them unreadable in the wrong + theme. For an approved color value this will return a similar color better suited for the current theme. + + :param color: color to be replaced + :return: similar color better suited for dark or light theme + """ + + if color in self.theme_safe_colors: + if self.app.options['global_theme'] in ['default', 'light']: + return color + else: + return self.theme_safe_colors[color] + else: + return color # https://www.w3.org/TR/SVG11/types.html#ColorKeywords def __init__(self, app): @@ -140,12 +174,12 @@ class MainGUI(QtWidgets.QMainWindow): # Open Gerber ... self.menufileopengerber = QtGui.QAction(QtGui.QIcon(self.app.resource_location + '/flatcam_icon24.png'), - '%s...\t%s' % (_('Open Gerber'), _('Ctrl+G')), self) + '%s...\t%s' % (_('Open Gerber'), _('Ctrl+G')), self) self.menufile_open.addAction(self.menufileopengerber) # Open Excellon ... self.menufileopenexcellon = QtGui.QAction(QtGui.QIcon(self.app.resource_location + '/open_excellon32.png'), - '%s...\t%s' % (_('Open Excellon'), _('Ctrl+E')), self) + '%s...\t%s' % (_('Open Excellon'), _('Ctrl+E')), self) self.menufile_open.addAction(self.menufileopenexcellon) # Open G-Code ... @@ -177,7 +211,7 @@ class MainGUI(QtWidgets.QMainWindow): # Save Project As ... self.menufilesaveprojectas = QtGui.QAction(QtGui.QIcon(self.app.resource_location + '/floppy16.png'), - '%s...\t%s' % (_('Save Project As'), _('Ctrl+Shift+S')), self) + '%s...\t%s' % (_('Save Project As'), _('Ctrl+Shift+S')), self) self.menufile_save.addAction(self.menufilesaveprojectas) # Save Project Copy ... @@ -196,9 +230,9 @@ class MainGUI(QtWidgets.QMainWindow): self.menufile_scripting.setToolTipsVisible(True) self.menufilenewscript = QtGui.QAction(QtGui.QIcon(self.app.resource_location + '/script_new16.png'), - '%s...\t%s' % (_('New Script'), ''), self) + '%s...\t%s' % (_('New Script'), ''), self) self.menufileopenscript = QtGui.QAction(QtGui.QIcon(self.app.resource_location + '/open_script32.png'), - '%s...\t%s' % (_('Open Script'), ''), self) + '%s...\t%s' % (_('Open Script'), ''), self) self.menufileopenscriptexample = QtGui.QAction( QtGui.QIcon(self.app.resource_location + '/open_script32.png'), '%s...\t%s' % (_('Open Example'), ''), self) @@ -715,7 +749,7 @@ class MainGUI(QtWidgets.QMainWindow): '%s\t%s' % (_("Copy Geom"), _('C'))) self.geo_delete_menuitem = self.geo_editor_menu.addAction( QtGui.QIcon(self.app.resource_location + '/deleteshape16.png'), - '%s\t%s' % (_("Delete Shape"), _('DEL')) + '%s\t%s' % (_("Delete"), _('DEL')) ) self.geo_editor_menu.addSeparator() self.geo_move_menuitem = self.geo_editor_menu.addAction( @@ -1263,7 +1297,7 @@ class MainGUI(QtWidgets.QMainWindow): QtGui.QIcon(self.app.resource_location + '/copy32.png'), _("Copy Shape(s)")) self.geo_delete_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/trash32.png'), _("Delete Shape")) + QtGui.QIcon(self.app.resource_location + '/trash32.png'), _("Delete")) self.geo_transform_btn = self.geo_edit_toolbar.addAction( QtGui.QIcon(self.app.resource_location + '/transform.png'), _("Transformations")) self.geo_edit_toolbar.addSeparator() @@ -2034,7 +2068,7 @@ class MainGUI(QtWidgets.QMainWindow): self.lock_action.triggered[bool].connect(self.lock_toolbar) self.pref_open_button.clicked.connect(self.on_preferences_open_folder) - self.clear_btn.clicked.connect(self.on_gui_clear) + self.clear_btn.clicked.connect(lambda: self.on_gui_clear()) self.wplace_label.clicked.connect(self.app.on_workspace_toggle) self.fcinfo.clicked.connect(self.toggle_shell_ui) @@ -2353,19 +2387,17 @@ class MainGUI(QtWidgets.QMainWindow): subprocess.Popen(['xdg-open', self.app.data_path]) self.app.inform.emit('[success] %s' % _("FlatCAM Preferences Folder opened.")) - def on_gui_clear(self, signal=None, forced_clear=False): + def on_gui_clear(self, forced_clear=False): """ Will clear the settings that are stored in QSettings. """ self.app.log.debug("Clearing the settings in QSettings. GUI settings cleared.") theme_settings = QtCore.QSettings("Open Source", "FlatCAM") - theme_settings.setValue('theme', 'white') + theme_settings.setValue('theme', 'light') del theme_settings - resource_loc = self.app.resource_location - response = None bt_yes = None if forced_clear is False: @@ -2633,7 +2665,7 @@ class MainGUI(QtWidgets.QMainWindow): self.geo_copy_btn = self.geo_edit_toolbar.addAction( QtGui.QIcon(self.app.resource_location + '/copy32.png'), _("Copy Objects")) self.geo_delete_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/trash32.png'), _("Delete Shape")) + QtGui.QIcon(self.app.resource_location + '/trash32.png'), _("Delete")) self.geo_transform_btn = self.geo_edit_toolbar.addAction( QtGui.QIcon(self.app.resource_location + '/transform.png'), _("Transformations")) @@ -3496,27 +3528,27 @@ class MainGUI(QtWidgets.QMainWindow): self.app.geo_editor.select_tool('select') return else: - if self.app.geo_editor.active_tool.name == 'path' and \ - self.app.geo_editor.active_tool.path_tool.length != 0.0: + if self.app.geo_editor.active_tool.name == 'path' \ + and self.app.geo_editor.active_tool.path_tool.length != 0.0: pass - elif self.app.geo_editor.active_tool.name == 'polygon' and \ - self.app.geo_editor.active_tool.polygon_tool.length != 0.0: + elif self.app.geo_editor.active_tool.name == 'polygon' \ + and self.app.geo_editor.active_tool.polygon_tool.length != 0.0: pass - elif self.app.geo_editor.active_tool.name == 'circle' and \ - self.app.geo_editor.active_tool.circle_tool.x != 0.0 and \ - self.app.geo_editor.active_tool.circle_tool.y != 0.0: + elif self.app.geo_editor.active_tool.name == 'circle' \ + and self.app.geo_editor.active_tool.circle_tool.x != 0.0 \ + and self.app.geo_editor.active_tool.circle_tool.y != 0.0: pass - elif self.app.geo_editor.active_tool.name == 'rectangle' and \ - self.app.geo_editor.active_tool.rect_tool.length != 0.0 and \ - self.app.geo_editor.active_tool.rect_tool.width != 0.0: + elif self.app.geo_editor.active_tool.name == 'rectangle' \ + and self.app.geo_editor.active_tool.rect_tool.length != 0.0 \ + and self.app.geo_editor.active_tool.rect_tool.width != 0.0: pass - elif self.app.geo_editor.active_tool.name == 'move' and \ - self.app.geo_editor.active_tool.move_tool.length != 0.0 and \ - self.app.geo_editor.active_tool.move_tool.width != 0.0: + elif self.app.geo_editor.active_tool.name == 'move' \ + and self.app.geo_editor.active_tool.move_tool.length != 0.0 \ + and self.app.geo_editor.active_tool.move_tool.width != 0.0: pass - elif self.app.geo_editor.active_tool.name == 'copy' and \ - self.app.geo_editor.active_tool.copy_tool.length != 0.0 and \ - self.app.geo_editor.active_tool.copy_tool.width != 0.0: + elif self.app.geo_editor.active_tool.name == 'copy' \ + and self.app.geo_editor.active_tool.copy_tool.length != 0.0 \ + and self.app.geo_editor.active_tool.copy_tool.width != 0.0: pass else: self.app.geo_editor.active_tool.click( @@ -3862,8 +3894,9 @@ class MainGUI(QtWidgets.QMainWindow): self.app.inform.emit(_("Click on target point.")) self.app.ui.aperture_copy_btn.setChecked(True) self.app.grb_editor.on_tool_select('copy') - self.app.grb_editor.active_tool.set_origin( - (self.app.grb_editor.snap_x, self.app.grb_editor.snap_y)) + if self.app.grb_editor.active_tool is not None: + self.app.grb_editor.active_tool.set_origin( + (self.app.grb_editor.snap_x, self.app.grb_editor.snap_y)) else: self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. Nothing selected.")) return @@ -3908,8 +3941,9 @@ class MainGUI(QtWidgets.QMainWindow): self.app.inform.emit(_("Click on target point.")) self.app.ui.aperture_move_btn.setChecked(True) self.app.grb_editor.on_tool_select('move') - self.app.grb_editor.active_tool.set_origin( - (self.app.grb_editor.snap_x, self.app.grb_editor.snap_y)) + if self.app.grb_editor.active_tool is not None: + self.app.grb_editor.active_tool.set_origin( + (self.app.grb_editor.snap_x, self.app.grb_editor.snap_y)) else: self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. Nothing selected.")) return @@ -4120,8 +4154,9 @@ class MainGUI(QtWidgets.QMainWindow): self.app.inform.emit(_("Click on target point.")) self.app.ui.copy_drill_btn.setChecked(True) self.app.exc_editor.on_tool_select('drill_copy') - self.app.exc_editor.active_tool.set_origin( - (self.app.exc_editor.snap_x, self.app.exc_editor.snap_y)) + if self.app.exc_editor.active_tool is not None: + self.app.exc_editor.active_tool.set_origin( + (self.app.exc_editor.snap_x, self.app.exc_editor.snap_y)) else: self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. Nothing selected.")) return @@ -4149,8 +4184,9 @@ class MainGUI(QtWidgets.QMainWindow): self.app.inform.emit(_("Click on target location ...")) self.app.ui.move_drill_btn.setChecked(True) self.app.exc_editor.on_tool_select('drill_move') - self.app.exc_editor.active_tool.set_origin( - (self.app.exc_editor.snap_x, self.app.exc_editor.snap_y)) + if self.app.exc_editor.active_tool is not None: + self.app.exc_editor.active_tool.set_origin( + (self.app.exc_editor.snap_x, self.app.exc_editor.snap_y)) else: self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. Nothing selected.")) return @@ -5211,7 +5247,7 @@ class ShortcutsTab(QtWidgets.QWidget): _('Space'), _("Rotate Geometry"), _('ENTER'), _("Finish drawing for certain tools"), _('Esc'), _("Abort and return to Select"), - _('Del'), _("Delete Shape") + _('Del'), _("Delete") ) # EXCELLON EDITOR SHORTCUT LIST diff --git a/appGUI/ObjectUI.py b/appGUI/ObjectUI.py index 143cac9c..a7b757eb 100644 --- a/appGUI/ObjectUI.py +++ b/appGUI/ObjectUI.py @@ -40,12 +40,12 @@ class ObjectUI(QtWidgets.QWidget): if theme_settings.contains("theme"): theme = theme_settings.value('theme', type=str) else: - theme = 'white' + theme = 'light' - if theme == 'white': + if theme == 'light': self.resource_loc = 'assets/resources' else: - self.resource_loc = 'assets/resources' + self.resource_loc = 'assets/resources/dark_resources' layout = QtWidgets.QVBoxLayout() self.setLayout(layout) @@ -197,7 +197,7 @@ class GerberObjectUI(ObjectUI): plot_grid.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter) gen_frame.setLayout(plot_grid) - self.plot_options_label = FCLabel("%s:" % _("Plot Options")) + self.plot_options_label = FCLabel('%s:' % _("Plot Options"), bold=True) plot_grid.addWidget(self.plot_options_label, 0, 0) @@ -219,7 +219,7 @@ class GerberObjectUI(ObjectUI): self.name_hlay = QtWidgets.QHBoxLayout() plot_grid.addLayout(self.name_hlay, 1, 0, 1, 3) - name_label = FCLabel("%s:" % _("Name")) + name_label = FCLabel('%s:' % _("Name"), bold=True) self.name_entry = FCEntry() self.name_entry.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) self.name_hlay.addWidget(name_label) @@ -482,7 +482,7 @@ class GerberObjectUI(ObjectUI): # Non-Copper Regions Frame # ############################################################################################################# # ## Non-copper regions - self.noncopper_label = FCLabel("%s" % _("Non-copper regions")) + self.noncopper_label = FCLabel('%s' % _("Non-copper regions"), bold=True) self.noncopper_label.setToolTip( _("Create polygons covering the\n" "areas without copper on the PCB.\n" @@ -530,7 +530,7 @@ class GerberObjectUI(ObjectUI): # Bounding Box Frame # ############################################################################################################# # ## Bounding box - self.boundingbox_label = FCLabel('%s' % _('Bounding Box')) + self.boundingbox_label = FCLabel('%s' % _('Bounding Box'), bold=True) self.boundingbox_label.setToolTip( _("Create a geometry surrounding the Gerber object.\n" "Square shape.") @@ -589,12 +589,12 @@ class ExcellonObjectUI(ObjectUI): if theme_settings.contains("theme"): theme = theme_settings.value('theme', type=str) else: - theme = 'white' + theme = 'light' - if theme == 'white': + if theme == 'light': self.resource_loc = 'assets/resources' else: - self.resource_loc = 'assets/resources' + self.resource_loc = 'assets/resources/dark_resources' ObjectUI.__init__(self, title=_('Excellon Object'), icon_file=self.resource_loc + '/drill32.png', @@ -617,7 +617,7 @@ class ExcellonObjectUI(ObjectUI): gen_frame.setLayout(plot_grid) # Plot options - self.plot_options_label = FCLabel("%s: " % _("Plot Options")) + self.plot_options_label = FCLabel('%s: ' % _("Plot Options"), bold=True) # Solid CB self.solid_cb = FCCheckBox(label=_('Solid')) @@ -638,7 +638,7 @@ class ExcellonObjectUI(ObjectUI): # ## Object name self.name_hlay = QtWidgets.QHBoxLayout() - name_label = FCLabel("%s: " % _("Name")) + name_label = FCLabel('%s: ' % _("Name"), bold=True) self.name_entry = FCEntry() self.name_entry.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) self.name_hlay.addWidget(name_label) @@ -844,7 +844,7 @@ class ExcellonObjectUI(ObjectUI): # ############################################################################################################# # Milling Drill Holes Frame # ############################################################################################################# - self.mill_hole_label = FCLabel('%s' % _('Milling Geometry')) + self.mill_hole_label = FCLabel('%s' % _('Milling Geometry'), bold=True) self.mill_hole_label.setToolTip( _("Create Geometry for milling holes.\n" "Select from the Tools Table above the hole dias to be\n" @@ -926,12 +926,12 @@ class GeometryObjectUI(ObjectUI): if theme_settings.contains("theme"): theme = theme_settings.value('theme', type=str) else: - theme = 'white' + theme = 'light' - if theme == 'white': + if theme == 'light': self.resource_loc = 'assets/resources' else: - self.resource_loc = 'assets/resources' + self.resource_loc = 'assets/resources/dark_resources' super(GeometryObjectUI, self).__init__( title=_('Geometry Object'), @@ -953,7 +953,7 @@ class GeometryObjectUI(ObjectUI): plot_grid.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter) gen_frame.setLayout(plot_grid) - self.plot_options_label = FCLabel("%s:" % _("Plot Options")) + self.plot_options_label = FCLabel('%s:' % _("Plot Options"), bold=True) self.plot_options_label.setMinimumWidth(90) plot_grid.addWidget(self.plot_options_label, 0, 0) @@ -970,7 +970,7 @@ class GeometryObjectUI(ObjectUI): self.name_hlay = QtWidgets.QHBoxLayout() plot_grid.addLayout(self.name_hlay, 2, 0, 1, 3) - name_label = FCLabel("%s:" % _("Name")) + name_label = FCLabel('%s:' % _("Name"), bold=True) self.name_entry = FCEntry() self.name_entry.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) self.name_hlay.addWidget(name_label) @@ -1033,7 +1033,7 @@ class GeometryObjectUI(ObjectUI): self.tt_frame.setLayout(tt_grid) # ### Tools #### - self.tools_table_label = FCLabel('%s:' % _('Tools Table')) + self.tools_table_label = FCLabel('%s:' % _('Tools Table'), bold=True) self.tools_table_label.setToolTip( _("Tools in this Geometry object used for cutting.\n" "The 'Offset' entry will set an offset for the cut.\n" @@ -1182,7 +1182,7 @@ class GeometryObjectUI(ObjectUI): # Simplification Frame # ############################################################################################################# # Simplification Title - simplif_lbl = FCLabel('%s:' % _("Simplification")) + simplif_lbl = FCLabel('%s' % _("Simplification"), bold=True) simplif_lbl.setToolTip( _("Simplify a geometry by reducing its vertex points number.") ) @@ -1267,12 +1267,12 @@ class CNCObjectUI(ObjectUI): if theme_settings.contains("theme"): theme = theme_settings.value('theme', type=str) else: - theme = 'white' + theme = 'light' - if theme == 'white': + if theme == 'light': self.resource_loc = 'assets/resources' else: - self.resource_loc = 'assets/resources' + self.resource_loc = 'assets/resources/dark_resources' ObjectUI.__init__(self, title=_('CNC Job Object'), icon_file=self.resource_loc + '/cnc32.png', parent=parent, @@ -1296,7 +1296,7 @@ class CNCObjectUI(ObjectUI): gen_frame.setLayout(grid0) # Plot Options - self.cncplot_method_label = FCLabel("%s: " % _("Plot Options")) + self.cncplot_method_label = FCLabel('%s: ' % _("Plot Options"), bold=True) self.cncplot_method_label.setToolTip( _( "This selects the kind of geometries on the canvas to plot.\n" @@ -1319,7 +1319,7 @@ class CNCObjectUI(ObjectUI): grid0.addLayout(self.name_hlay, 2, 0, 1, 3) # ## Object name - name_label = FCLabel("%s: " % _("Name")) + name_label = FCLabel('%s: ' % _("Name"), bold=True) self.name_entry = FCEntry() self.name_entry.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) @@ -1387,7 +1387,7 @@ class CNCObjectUI(ObjectUI): grid_par.addWidget(self.estimated_frame, 4, 0, 1, 3) # Travelled Distance - self.t_distance_label = FCLabel("%s:" % _("Travelled distance")) + self.t_distance_label = FCLabel('%s:' % _("Travelled distance"), bold=True) self.t_distance_label.setToolTip( _("This is the total travelled distance on X-Y plane.\n" "In current units.") @@ -1400,7 +1400,7 @@ class CNCObjectUI(ObjectUI): estimated_grid.addWidget(self.units_label, 0, 2) # Estimated Time - self.t_time_label = FCLabel("%s:" % _("Estimated time")) + self.t_time_label = FCLabel('%s:' % _("Estimated time"), bold=True) self.t_time_label.setToolTip( _("This is the estimated time to do the routing/drilling,\n" "without the time spent in ToolChange events.") @@ -1456,7 +1456,7 @@ class CNCObjectUI(ObjectUI): grid1.addLayout(hlay, 0, 0, 1, 2) # CNC Tools Table for plot - self.cnc_tools_table_label = FCLabel('%s' % _('CNC Tools Table')) + self.cnc_tools_table_label = FCLabel('%s' % _('CNC Tools Table'), bold=True) self.cnc_tools_table_label.setToolTip( _( "Tools in this CNCJob object used for cutting.\n" @@ -1585,12 +1585,12 @@ class ScriptObjectUI(ObjectUI): if theme_settings.contains("theme"): theme = theme_settings.value('theme', type=str) else: - theme = 'white' + theme = 'light' - if theme == 'white': + if theme == 'light': self.resource_loc = 'assets/resources' else: - self.resource_loc = 'assets/resources' + self.resource_loc = 'assets/resources/dark_resources' ObjectUI.__init__(self, title=_('Script Object'), icon_file=self.resource_loc + '/script_new24.png', @@ -1602,7 +1602,7 @@ class ScriptObjectUI(ObjectUI): self.name_hlay = QtWidgets.QHBoxLayout() self.custom_box.addLayout(self.name_hlay) - name_label = FCLabel("%s:" % _("Name")) + name_label = FCLabel('%s:' % _("Name"), bold=True) self.name_entry = FCEntry() self.name_entry.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) self.name_hlay.addWidget(name_label) @@ -1652,12 +1652,12 @@ class DocumentObjectUI(ObjectUI): if theme_settings.contains("theme"): theme = theme_settings.value('theme', type=str) else: - theme = 'white' + theme = 'light' - if theme == 'white': + if theme == 'light': self.resource_loc = 'assets/resources' else: - self.resource_loc = 'assets/resources' + self.resource_loc = 'assets/resources/dark_resources' ObjectUI.__init__(self, title=_('Document Object'), icon_file=self.resource_loc + '/notes16_1.png', @@ -1669,7 +1669,7 @@ class DocumentObjectUI(ObjectUI): self.name_hlay = QtWidgets.QHBoxLayout() self.custom_box.addLayout(self.name_hlay) - name_label = FCLabel("%s:" % _("Name")) + name_label = FCLabel('%s:' % _("Name"), bold=True) self.name_entry = FCEntry() self.name_entry.setFocusPolicy(QtCore.Qt.FocusPolicy.StrongFocus) self.name_hlay.addWidget(name_label) diff --git a/appGUI/PlotCanvas.py b/appGUI/PlotCanvas.py index 2253ce71..0d40cf9f 100644 --- a/appGUI/PlotCanvas.py +++ b/appGUI/PlotCanvas.py @@ -54,9 +54,14 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): if settings.contains("theme"): theme = settings.value('theme', type=str) else: - theme = 'white' + theme = 'default' - if theme == 'white': + if settings.contains("dark_canvas"): + dark_canvas = settings.value('dark_canvas', type=bool) + else: + dark_canvas = False + + if (theme == 'default' or theme == 'light') and not dark_canvas: self.line_color = (0.3, 0.0, 0.0, 1.0) # self.rect_hud_color = Color('#0000FF10') self.rect_hud_color = Color('#80808040') @@ -384,9 +389,14 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): if settings.contains("theme"): theme = settings.value('theme', type=str) else: - theme = 'white' + theme = 'default' - if theme == 'white': + if settings.contains("dark_canvas"): + dark_canvas = settings.value('dark_canvas', type=bool) + else: + dark_canvas = False + + if (theme == 'default' or theme == 'light') and not dark_canvas: color = 'dimgray' else: color = '#dededeff' @@ -712,7 +722,7 @@ class CursorBig(QtCore.QObject): # if 'edge_color' in kwargs: # color = kwargs['edge_color'] # else: - # if self.app.options['global_theme'] == 'white': + # if self.app.options['global_theme'] == 'light': # color = '#000000FF' # else: # color = '#FFFFFFFF' diff --git a/appGUI/PlotCanvas3d.py b/appGUI/PlotCanvas3d.py index 98051ece..748b459c 100644 --- a/appGUI/PlotCanvas3d.py +++ b/appGUI/PlotCanvas3d.py @@ -66,14 +66,19 @@ class PlotCanvas3d(QtCore.QObject, scene.SceneCanvas): if settings.contains("theme"): theme = settings.value('theme', type=str) else: - theme = 'white' + theme = 'default' + + if settings.contains("dark_canvas"): + dark_canvas = settings.value('dark_canvas', type=bool) + else: + dark_canvas = False if settings.contains("axis_font_size"): a_fsize = settings.value('axis_font_size', type=int) else: a_fsize = 8 - if theme == 'white': + if (theme == 'default' or theme == 'light') and not dark_canvas: theme_color = Color('#FFFFFF') tick_color = Color('#000000') back_color = str(QPalette().color(QPalette.ColorRole.Window).name()) @@ -131,7 +136,7 @@ class PlotCanvas3d(QtCore.QObject, scene.SceneCanvas): # self.xaxis.link_view(self.view) # self.yaxis.link_view(self.view) - # if theme == 'white': + # if theme == 'light': # self.grid = scene.GridLines(parent=self.view.scene, color='dimgray') # else: # self.grid = scene.GridLines(parent=self.view.scene, color='#dededeff') diff --git a/appGUI/PlotCanvasLegacy.py b/appGUI/PlotCanvasLegacy.py index 2b5609e8..17590476 100644 --- a/appGUI/PlotCanvasLegacy.py +++ b/appGUI/PlotCanvasLegacy.py @@ -82,7 +82,18 @@ class CanvasCache(QtCore.QObject): self.axes.set_xticks([]) self.axes.set_yticks([]) - if self.app.options['global_theme'] == 'white': + settings = QtCore.QSettings("Open Source", "FlatCAM") + if settings.contains("theme"): + theme = settings.value('theme', type=str) + else: + theme = 'default' + + if settings.contains("dark_canvas"): + dark_canvas = settings.value('dark_canvas', type=bool) + else: + dark_canvas = False + + if (theme == 'default' or theme == 'light') and not dark_canvas: self.axes.set_facecolor('#FFFFFF') else: self.axes.set_facecolor('#000000') @@ -154,7 +165,7 @@ class PlotCanvasLegacy(QtCore.QObject): self.app = app - if self.app.options['global_theme'] == 'white': + if self.app.options['global_theme'] in ['default', 'light']: theme_color = '#FFFFFF' tick_color = '#000000' self.rect_hud_color = '#0000FF10' @@ -446,7 +457,7 @@ class PlotCanvasLegacy(QtCore.QObject): super().__init__() self.p = plotcanvas - units = self.p.app.app_units + # units = self.p.app.app_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) self.on_update_text_hud() @@ -639,7 +650,7 @@ class PlotCanvasLegacy(QtCore.QObject): if self.app.options["global_cursor_color_enabled"]: color = self.app.options["global_cursor_color"] else: - if self.app.options['global_theme'] == 'white': + if self.app.options['global_theme'] == 'light': color = '#000000' else: color = '#FFFFFF' @@ -694,7 +705,7 @@ class PlotCanvasLegacy(QtCore.QObject): if color: color = color else: - if self.app.options['global_theme'] == 'white': + if self.app.options['global_theme'] == 'light': color = '#000000' else: color = '#FFFFFF' @@ -737,7 +748,7 @@ class PlotCanvasLegacy(QtCore.QObject): self.canvas.blit(self.axes.bbox) def clear_cursor(self, state): - if self.app.options['global_theme'] == 'white': + if self.app.options['global_theme'] == 'light': color = '#000000' else: color = '#FFFFFF' diff --git a/appGUI/VisPyCanvas.py b/appGUI/VisPyCanvas.py index 597980d3..35d86aa4 100644 --- a/appGUI/VisPyCanvas.py +++ b/appGUI/VisPyCanvas.py @@ -39,9 +39,14 @@ class VisPyCanvas(scene.SceneCanvas): if settings.contains("theme"): theme = settings.value('theme', type=str) else: - theme = 'white' + theme = 'default' - if theme == 'white': + if settings.contains("dark_canvas"): + dark_canvas = settings.value('dark_canvas', type=bool) + else: + dark_canvas = False + + if (theme == 'default' or theme == 'light') and not dark_canvas: theme_color = Color('#FFFFFF') tick_color = Color('#000000') back_color = str(QPalette().color(QPalette.ColorRole.Window).name()) @@ -97,10 +102,15 @@ class VisPyCanvas(scene.SceneCanvas): if settings.contains("theme"): theme = settings.value('theme', type=str) else: - theme = 'white' + theme = 'default' + + if settings.contains("dark_canvas"): + dark_canvas = settings.value('dark_canvas', type=bool) + else: + dark_canvas = False self.view = view - if theme == 'white': + if (theme == 'default' or theme == 'light') and not dark_canvas: self.grid = scene.GridLines(parent=self.view.scene, color='dimgray') else: self.grid = scene.GridLines(parent=self.view.scene, color='#dededeff') diff --git a/appGUI/preferences/PreferencesUIManager.py b/appGUI/preferences/PreferencesUIManager.py index a5acde02..0e295799 100644 --- a/appGUI/preferences/PreferencesUIManager.py +++ b/appGUI/preferences/PreferencesUIManager.py @@ -28,6 +28,16 @@ class PreferencesUIManager(QtCore.QObject): """ super(PreferencesUIManager, self).__init__() + self.general_displayed = False + self.gerber_displayed = False + self.excellon_displayed = False + self.geometry_displayed = False + self.cnc_displayed = False + self.engrave_displayed = False + self.plugins_displayed = False + self.plugins2_displayed = False + self.util_displayed = False + self.defaults = defaults self.data_path = data_path self.ui = ui @@ -73,8 +83,8 @@ class PreferencesUIManager(QtCore.QObject): "global_tpdf_rmargin": self.ui.general_pref_form.general_app_group.rmargin_entry, # General GUI Preferences - "global_theme": self.ui.general_pref_form.general_gui_group.theme_radio, - "global_gray_icons": self.ui.general_pref_form.general_gui_group.gray_icons_cb, + "global_appearance": self.ui.general_pref_form.general_gui_group.appearance_radio, + "global_dark_canvas": self.ui.general_pref_form.general_gui_group.dark_canvas_cb, "global_layout": self.ui.general_pref_form.general_gui_group.layout_combo, "global_hover_shape": self.ui.general_pref_form.general_gui_group.hover_cb, "global_selection_shape": self.ui.general_pref_form.general_gui_group.selection_cb, @@ -575,6 +585,7 @@ class PreferencesUIManager(QtCore.QObject): # SolderPaste Dispensing Tool "tools_solderpaste_tools": self.ui.plugin_pref_form.tools_solderpaste_group.nozzle_tool_dia_entry, "tools_solderpaste_new": self.ui.plugin_pref_form.tools_solderpaste_group.addtool_entry, + "tools_solderpaste_margin": self.ui.plugin_pref_form.tools_solderpaste_group.margin_entry, "tools_solderpaste_z_start": self.ui.plugin_pref_form.tools_solderpaste_group.z_start_entry, "tools_solderpaste_z_dispense": self.ui.plugin_pref_form.tools_solderpaste_group.z_dispense_entry, "tools_solderpaste_z_stop": self.ui.plugin_pref_form.tools_solderpaste_group.z_stop_entry, @@ -798,6 +809,25 @@ class PreferencesUIManager(QtCore.QObject): :return: None """ + self.init_preferences_gui() + + self.pref_connect() + + # Initialize the color box's color in Preferences -> Global -> Colors + self.__init_color_pickers() + + # log.debug("Finished Preferences GUI form initialization.") + + def init_preferences_gui(self): + self.general_displayed = False + self.gerber_displayed = False + self.excellon_displayed = False + self.geometry_displayed = False + self.cnc_displayed = False + self.engrave_displayed = False + self.plugins_displayed = False + self.plugins2_displayed = False + self.util_displayed = False gen_form = self.ui.general_pref_form try: @@ -807,72 +837,10 @@ class PreferencesUIManager(QtCore.QObject): self.ui.general_scroll_area.setWidget(gen_form) gen_form.show() - ger_form = self.ui.gerber_pref_form - try: - self.ui.gerber_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") - self.ui.gerber_scroll_area.setWidget(ger_form) - ger_form.show() + def pref_connect(self): + self.pref_disconnect() - exc_form = self.ui.excellon_pref_form - try: - self.ui.excellon_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") - self.ui.excellon_scroll_area.setWidget(exc_form) - exc_form.show() - - geo_form = self.ui.geo_pref_form - try: - self.ui.geometry_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") - self.ui.geometry_scroll_area.setWidget(geo_form) - geo_form.show() - - cnc_form = self.ui.cncjob_pref_form - try: - self.ui.cncjob_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") - self.ui.cncjob_scroll_area.setWidget(cnc_form) - cnc_form.show() - - plugins_engraving_form = self.ui.plugin_eng_pref_form - try: - self.ui.plugins_engraving_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") - self.ui.plugins_engraving_scroll_area.setWidget(plugins_engraving_form) - plugins_engraving_form.show() - - plugins_form = self.ui.plugin_pref_form - try: - self.ui.tools_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") - self.ui.tools_scroll_area.setWidget(plugins_form) - plugins_form.show() - - plugins2_form = self.ui.plugin2_pref_form - try: - self.ui.tools2_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") - self.ui.tools2_scroll_area.setWidget(plugins2_form) - plugins2_form.show() - - fa_form = self.ui.util_pref_form - try: - self.ui.fa_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") - self.ui.fa_scroll_area.setWidget(fa_form) - fa_form.show() - - # Initialize the color box's color in Preferences -> Global -> Colors - self.__init_color_pickers() + self.ui.pref_tab_area.tabBarClicked.connect(self.on_tab_clicked) # Button handlers self.ui.pref_save_button.clicked.connect(lambda: self.on_save_button(save_to_file=True)) @@ -880,9 +848,12 @@ class PreferencesUIManager(QtCore.QObject): self.ui.pref_close_button.clicked.connect(self.on_pref_close_button) self.ui.pref_defaults_button.clicked.connect(self.on_restore_defaults_preferences) - # log.debug("Finished Preferences GUI form initialization.") + def pref_disconnect(self): + try: + self.ui.pref_tab_area.tabBarClicked.disconnect() + except Exception: + pass - def clear_preferences_gui(self): # Disconnect Button handlers try: self.ui.pref_save_button.clicked.disconnect() @@ -904,50 +875,143 @@ class PreferencesUIManager(QtCore.QObject): except Exception: pass - try: - self.ui.general_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") + def clear_preferences_gui(self): + self.pref_disconnect() + # try: + # self.ui.general_scroll_area.takeWidget() + # except Exception: + # self.ui.app.log.debug("Nothing to remove") + # + # try: + # self.ui.gerber_scroll_area.takeWidget() + # except Exception: + # self.ui.app.log.debug("Nothing to remove") + # + # try: + # self.ui.excellon_scroll_area.takeWidget() + # except Exception: + # self.ui.app.log.debug("Nothing to remove") + # + # try: + # self.ui.geometry_scroll_area.takeWidget() + # except Exception: + # self.ui.app.log.debug("Nothing to remove") + # + # try: + # self.ui.cncjob_scroll_area.takeWidget() + # except Exception: + # self.ui.app.log.debug("Nothing to remove") + # + # try: + # self.ui.plugins_engraving_scroll_area.takeWidget() + # except Exception: + # self.ui.app.log.debug("Nothing to remove") + # + # try: + # self.ui.tools_scroll_area.takeWidget() + # except Exception: + # self.ui.app.log.debug("Nothing to remove") + # + # try: + # self.ui.tools2_scroll_area.takeWidget() + # except Exception: + # self.ui.app.log.debug("Nothing to remove") + # + # try: + # self.ui.fa_scroll_area.takeWidget() + # except Exception: + # self.ui.app.log.debug("Nothing to remove") - try: - self.ui.gerber_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") + def on_tab_clicked(self, idx): + if idx == 0 and self.general_displayed is False: + self.general_displayed = True + gen_form = self.ui.general_pref_form + try: + self.ui.general_scroll_area.takeWidget() + except Exception: + self.ui.app.log.debug("Nothing to remove") + self.ui.general_scroll_area.setWidget(gen_form) + gen_form.show() - try: - self.ui.excellon_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") + if idx == 1 and self.gerber_displayed is False: + self.gerber_displayed = True + ger_form = self.ui.gerber_pref_form + try: + self.ui.gerber_scroll_area.takeWidget() + except Exception: + self.ui.app.log.debug("Nothing to remove") + self.ui.gerber_scroll_area.setWidget(ger_form) + ger_form.show() - try: - self.ui.geometry_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") + if idx == 2 and self.excellon_displayed is False: + self.excellon_displayed = True + exc_form = self.ui.excellon_pref_form + try: + self.ui.excellon_scroll_area.takeWidget() + except Exception: + self.ui.app.log.debug("Nothing to remove") + self.ui.excellon_scroll_area.setWidget(exc_form) + exc_form.show() - try: - self.ui.cncjob_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") + if idx == 3 and self.geometry_displayed is False: + self.geometry_displayed = True + geo_form = self.ui.geo_pref_form + try: + self.ui.geometry_scroll_area.takeWidget() + except Exception: + self.ui.app.log.debug("Nothing to remove") + self.ui.geometry_scroll_area.setWidget(geo_form) + geo_form.show() - try: - self.ui.plugins_engraving_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") + if idx == 4 and self.cnc_displayed is False: + self.cnc_displayed = True + cnc_form = self.ui.cncjob_pref_form + try: + self.ui.cncjob_scroll_area.takeWidget() + except Exception: + self.ui.app.log.debug("Nothing to remove") + self.ui.cncjob_scroll_area.setWidget(cnc_form) + cnc_form.show() - try: - self.ui.tools_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") + if idx == 5 and self.engrave_displayed is False: + self.engrave_displayed = True + plugins_engraving_form = self.ui.plugin_eng_pref_form + try: + self.ui.plugins_engraving_scroll_area.takeWidget() + except Exception: + self.ui.app.log.debug("Nothing to remove") + self.ui.plugins_engraving_scroll_area.setWidget(plugins_engraving_form) + plugins_engraving_form.show() - try: - self.ui.tools2_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") + if idx == 6 and self.plugins_displayed is False: + self.plugins_displayed = True + plugins_form = self.ui.plugin_pref_form + try: + self.ui.tools_scroll_area.takeWidget() + except Exception: + self.ui.app.log.debug("Nothing to remove") + self.ui.tools_scroll_area.setWidget(plugins_form) + plugins_form.show() - try: - self.ui.fa_scroll_area.takeWidget() - except Exception: - self.ui.app.log.debug("Nothing to remove") + if idx == 7 and self.plugins2_displayed is False: + self.plugins2_displayed = True + plugins2_form = self.ui.plugin2_pref_form + try: + self.ui.tools2_scroll_area.takeWidget() + except Exception: + self.ui.app.log.debug("Nothing to remove") + self.ui.tools2_scroll_area.setWidget(plugins2_form) + plugins2_form.show() + + if idx == 8 and self.util_displayed is False: + self.util_displayed = True + fa_form = self.ui.util_pref_form + try: + self.ui.fa_scroll_area.takeWidget() + except Exception: + self.ui.app.log.debug("Nothing to remove") + self.ui.fa_scroll_area.setWidget(fa_form) + fa_form.show() def __init_color_pickers(self): # Init Gerber Plot Colors @@ -1064,20 +1128,26 @@ class PreferencesUIManager(QtCore.QObject): # make sure we update the self.current_defaults dict used to undo changes to self.defaults self.defaults.current_defaults.update(self.defaults) - # deal with theme change - theme_settings = QtCore.QSettings("Open Source", "FlatCAM") - if theme_settings.contains("theme"): - theme = theme_settings.value('theme', type=str) + # deal with appearance change + appearance_settings = QtCore.QSettings("Open Source", "FlatCAM") + if appearance_settings.contains("appearance"): + appearance = appearance_settings.value('appearance', type=str) else: - theme = 'white' + appearance = None + + if appearance_settings.contains("dark_canvas"): + dark_canvas = appearance_settings.value('dark_canvas', type=bool) + else: + dark_canvas = None should_restart = False - theme_new_val = self.ui.general_pref_form.general_gui_group.theme_radio.get_value() + appearance_new_val = self.ui.general_pref_form.general_gui_group.appearance_radio.get_value() + dark_canvas_new_val = self.ui.general_pref_form.general_gui_group.dark_canvas_cb.get_value() ge = self.defaults["global_graphic_engine"] ge_val = self.ui.general_pref_form.general_app_group.ge_radio.get_value() - if theme_new_val != theme or ge != ge_val: + if appearance_new_val != appearance or ge != ge_val or dark_canvas_new_val != dark_canvas: msgbox = FCMessageBox(parent=self.ui) title = _("Application will restart") txt = _("Are you sure you want to continue?") @@ -1094,17 +1164,24 @@ class PreferencesUIManager(QtCore.QObject): msgbox.exec() response = msgbox.clickedButton() - if theme_new_val != theme: + if appearance_new_val != appearance: if response == bt_yes: - theme_settings.setValue('theme', theme_new_val) - - # This will write the setting to the platform specific storage. - del theme_settings - + appearance_settings.setValue('appearance', appearance_new_val) should_restart = True else: - self.ui.general_pref_form.general_gui_group.theme_radio.set_value(theme) - else: + self.ui.general_pref_form.general_gui_group.appearance_radio.set_value(appearance) + + if dark_canvas_new_val != dark_canvas: + if response == bt_yes: + appearance_settings.setValue('dark_canvas', dark_canvas_new_val) + should_restart = True + else: + self.ui.general_pref_form.general_gui_group.dark_canvas_cb.set_value(dark_canvas) + + # This will write the setting to the platform specific storage. + del appearance_settings + + if ge != ge_val: if response == bt_yes: self.defaults["global_graphic_engine"] = ge_val should_restart = True diff --git a/appGUI/preferences/excellon/ExcellonOptPrefGroupUI.py b/appGUI/preferences/excellon/ExcellonOptPrefGroupUI.py index 3c0f5fdb..c62b2296 100644 --- a/appGUI/preferences/excellon/ExcellonOptPrefGroupUI.py +++ b/appGUI/preferences/excellon/ExcellonOptPrefGroupUI.py @@ -38,7 +38,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI): param_frame.setLayout(param_grid) # ### Milling Holes ## ## - self.mill_hole_label = FCLabel('%s' % _('Mill Holes')) + self.mill_hole_label = FCLabel('%s' % _('Mill Holes'), bold=True) self.mill_hole_label.setToolTip( _("Create Geometry for milling holes.") ) diff --git a/appGUI/preferences/general/GeneralAPPSetGroupUI.py b/appGUI/preferences/general/GeneralAPPSetGroupUI.py index 44fd77c4..b7d92edf 100644 --- a/appGUI/preferences/general/GeneralAPPSetGroupUI.py +++ b/appGUI/preferences/general/GeneralAPPSetGroupUI.py @@ -26,9 +26,9 @@ class GeneralAPPSetGroupUI(OptionsGroupUI): if theme_settings.contains("theme"): theme = theme_settings.value('theme', type=str) else: - theme = 'white' + theme = 'light' - if theme == 'white': + if theme == 'light': self.resource_loc = 'assets/resources' else: self.resource_loc = 'assets/resources' @@ -496,9 +496,14 @@ class GeneralAPPSetGroupUI(OptionsGroupUI): if theme_settings.contains("theme"): theme = theme_settings.value('theme', type=str) else: - theme = 'white' + theme = 'default' - if theme == 'white': + if theme_settings.contains("dark_canvas"): + dark_canvas = theme_settings.value('dark_canvas', type=bool) + else: + dark_canvas = False + + if (theme == 'default' or theme == 'light') and not dark_canvas: self.app.cursor_color_3D = 'black' else: self.app.cursor_color_3D = 'gray' @@ -509,4 +514,4 @@ class GeneralAPPSetGroupUI(OptionsGroupUI): def on_axis_color_entry(self): self.app.options['global_axis_color'] = self.axis_color_entry.get_value() - self.app.plotcanvas.apply_axis_color() \ No newline at end of file + self.app.plotcanvas.apply_axis_color() diff --git a/appGUI/preferences/general/GeneralAppPrefGroupUI.py b/appGUI/preferences/general/GeneralAppPrefGroupUI.py index ef32a068..a5346b49 100644 --- a/appGUI/preferences/general/GeneralAppPrefGroupUI.py +++ b/appGUI/preferences/general/GeneralAppPrefGroupUI.py @@ -89,7 +89,7 @@ class GeneralAppPrefGroupUI(OptionsGroupUI): grid1_frame.setLayout(grid1) # Graphic Engine for FlatCAM - self.ge_label = FCLabel('%s:' % _('Graphic Engine')) + self.ge_label = FCLabel('%s:' % _('Graphic Engine'), bold=True) self.ge_label.setToolTip(_("Choose what graphic engine to use in FlatCAM.\n" "Legacy(2D) -> reduced functionality, slow performance but enhanced compatibility.\n" "OpenGL(3D) -> full functionality, high performance\n" diff --git a/appGUI/preferences/general/GeneralAppSettingsGroupUI.py b/appGUI/preferences/general/GeneralAppSettingsGroupUI.py index 0bea646b..60bbc5fa 100644 --- a/appGUI/preferences/general/GeneralAppSettingsGroupUI.py +++ b/appGUI/preferences/general/GeneralAppSettingsGroupUI.py @@ -292,9 +292,14 @@ class GeneralAppSettingsGroupUI(OptionsGroupUI2): if theme_settings.contains("theme"): theme = theme_settings.value('theme', type=str) else: - theme = 'white' + theme = 'default' - if theme == 'white': + if theme_settings.contains("dark_canvas"): + dark_canvas = theme_settings.value('dark_canvas', type=bool) + else: + dark_canvas = False + + if (theme == 'default' or theme == 'light') and not dark_canvas: self.app.cursor_color_3D = 'black' else: self.app.cursor_color_3D = 'gray' diff --git a/appGUI/preferences/general/GeneralGUIPrefGroupUI.py b/appGUI/preferences/general/GeneralGUIPrefGroupUI.py index 9d1ba15a..e846d1e0 100644 --- a/appGUI/preferences/general/GeneralGUIPrefGroupUI.py +++ b/appGUI/preferences/general/GeneralGUIPrefGroupUI.py @@ -35,28 +35,36 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI): par_frame.setLayout(grid0) # Theme selection - self.theme_label = FCLabel('%s:' % _('Theme')) - self.theme_label.setToolTip( + self.appearance_label = FCLabel('%s' % _('Theme'), bold=True) + self.appearance_label.setToolTip( _("Select a theme for the application.\n" "It will theme the plot area.") ) - self.theme_radio = RadioSet([ - {"label": _("Light"), "value": "white"}, - {"label": _("Dark"), "value": "black"} + self.appearance_radio = RadioSet([ + {"label": _("Default"), "value": "default"}, + {"label": _("Auto"), "value": "auto"}, + {"label": _("Light"), "value": "light"}, + {"label": _("Dark"), "value": "dark"} ], compact=True) - - grid0.addWidget(self.theme_label, 0, 0) - grid0.addWidget(self.theme_radio, 0, 1) - - # Enable Gray Icons - self.gray_icons_cb = FCCheckBox('%s' % _('Use Gray Icons')) - self.gray_icons_cb.setToolTip( - _("Check this box to use a set of icons with\n" - "a lighter (gray) color. To be used when a\n" - "full dark theme is applied.") + self.appearance_radio.setToolTip( + _("The theme can be:\n" + "Default: Default theme\n" + "Auto: Matches mode from OS\n" + "Light: Light mode\n" + "Dark: Dark mode") ) - grid0.addWidget(self.gray_icons_cb, 2, 0, 1, 3) + + # Dark Canvas + self.dark_canvas_cb = FCCheckBox('%s' % _('Dark Canvas')) + self.dark_canvas_cb.setToolTip( + _("Check this box to force the use of dark canvas\n" + "even if a dark theme is not selected.") + ) + + grid0.addWidget(self.appearance_label, 0, 0, 1, 2) + grid0.addWidget(self.appearance_radio, 1, 0, 1, 3) + grid0.addWidget(self.dark_canvas_cb, 2, 0, 1, 3) # self.theme_button = FCButton(_("Apply Theme")) # self.theme_button.setToolTip( @@ -156,7 +164,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI): color_frame.setLayout(grid1) # Plot Selection (left - right) Color - self.sel_lr_label = FCLabel('%s' % _('Left-Right Selection Color')) + self.sel_lr_label = FCLabel('%s' % _('Left-Right Selection Color'), bold=True) grid1.addWidget(self.sel_lr_label, 0, 0, 1, 2) self.sl_color_label = FCLabel('%s:' % _('Outline')) @@ -196,7 +204,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI): grid1.addWidget(separator_line, 8, 0, 1, 2) # Plot Selection (left - right) Color - self.sel_rl_label = FCLabel('%s' % _('Right-Left Selection Color')) + self.sel_rl_label = FCLabel('%s' % _('Right-Left Selection Color'), bold=True) grid1.addWidget(self.sel_rl_label, 10, 0, 1, 2) # Plot Selection (right - left) Line Color @@ -241,7 +249,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI): # ----------------------- Editor Color ----------------------------- # ------------------------------------------------------------------ - self.editor_color_label = FCLabel('%s' % _('Editor Color')) + self.editor_color_label = FCLabel('%s' % _('Editor Color'), bold=True) grid1.addWidget(self.editor_color_label, 20, 0, 1, 2) # Editor Draw Color @@ -273,7 +281,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI): # ----------------------- Project Settings ----------------------------- # ------------------------------------------------------------------ # Light Theme - self.proj_settings_l_label = FCLabel('%s - %s' % (_('Project Items Color'), _("Light"))) + self.proj_settings_l_label = FCLabel('%s' % _("Light"), bold=True) grid1.addWidget(self.proj_settings_l_label, 28, 0, 1, 2) # Project Tab items color @@ -299,8 +307,13 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI): grid1.addWidget(self.proj_color_dis_l_label, 32, 0) grid1.addWidget(self.proj_color_dis_light_entry, 32, 1) + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + grid1.addWidget(separator_line, 33, 0, 1, 2) + # Dark Theme - self.proj_settings_d_label = FCLabel('%s - %s' % (_('Project Items Color'), _("Dark"))) + self.proj_settings_d_label = FCLabel('%s' % _("Dark"), bold=True) grid1.addWidget(self.proj_settings_d_label, 34, 0, 1, 2) # Project Tab items color diff --git a/appGUI/preferences/tools/Tools2CThievingPrefGroupUI.py b/appGUI/preferences/tools/Tools2CThievingPrefGroupUI.py index 4c85b0ea..c274284d 100644 --- a/appGUI/preferences/tools/Tools2CThievingPrefGroupUI.py +++ b/appGUI/preferences/tools/Tools2CThievingPrefGroupUI.py @@ -142,7 +142,7 @@ class Tools2CThievingPrefGroupUI(OptionsGroupUI): # ############################################################################################################# # DOTS Grid Parameters Frame # ############################################################################################################# - self.dots_label = FCLabel('%s:' % _("Dots Grid Parameters")) + self.dots_label = FCLabel('%s' % _("Dots Grid Parameters"), bold=True) self.layout.addWidget(self.dots_label) dots_frame = FCFrame() @@ -181,7 +181,7 @@ class Tools2CThievingPrefGroupUI(OptionsGroupUI): # ############################################################################################################# # Squares Grid Parameters Frame # ############################################################################################################# - self.squares_label = FCLabel('%s:' % _("Squares Grid Parameters")) + self.squares_label = FCLabel('%s' % _("Squares Grid Parameters"), bold=True) self.layout.addWidget(self.squares_label) square_frame = FCFrame() @@ -220,7 +220,7 @@ class Tools2CThievingPrefGroupUI(OptionsGroupUI): # ############################################################################################################# # Lines Grid Parameters Frame # ############################################################################################################# - self.lines_label = FCLabel('%s:' % _("Lines Grid Parameters")) + self.lines_label = FCLabel('%s' % _("Lines Grid Parameters"), bold=True) self.layout.addWidget(self.lines_label) line_frame = FCFrame() diff --git a/appGUI/preferences/tools/ToolsFilmPrefGroupUI.py b/appGUI/preferences/tools/ToolsFilmPrefGroupUI.py index 187a8158..ef75ae59 100644 --- a/appGUI/preferences/tools/ToolsFilmPrefGroupUI.py +++ b/appGUI/preferences/tools/ToolsFilmPrefGroupUI.py @@ -44,11 +44,6 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI): _("A value greater than 1 will compact the film\n" "while a value less than 1 will jolt it.") ) - self.film_scale_cb.setStyleSheet( - """ - QCheckBox {font-weight: bold; color: black} - """ - ) adj_grid.addWidget(self.film_scale_cb, 2, 0, 1, 2) # SCALE FRAME @@ -109,11 +104,6 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI): _("Positive values will skew to the right\n" "while negative values will skew to the left.") ) - self.film_skew_cb.setStyleSheet( - """ - QCheckBox {font-weight: bold; color: black} - """ - ) adj_grid.addWidget(self.film_skew_cb, 8, 0, 1, 2) # SKEW FRAME @@ -171,11 +161,6 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI): self.film_mirror_cb.setToolTip( _("Mirror the film geometry on the selected axis or on both.") ) - self.film_mirror_cb.setStyleSheet( - """ - QCheckBox {font-weight: bold; color: black} - """ - ) adj_grid.addWidget(self.film_mirror_cb, 12, 0, 1, 2) self.film_mirror_axis = RadioSet([{'label': _('X'), 'value': 'x'}, diff --git a/appGUI/preferences/tools/ToolsISOPrefGroupUI.py b/appGUI/preferences/tools/ToolsISOPrefGroupUI.py index c0fdd5a4..0a041e38 100644 --- a/appGUI/preferences/tools/ToolsISOPrefGroupUI.py +++ b/appGUI/preferences/tools/ToolsISOPrefGroupUI.py @@ -39,7 +39,7 @@ class ToolsISOPrefGroupUI(OptionsGroupUI): par_frame.setLayout(par_grid) # Tool Dias - isotdlabel = FCLabel('%s:' % _('Tools Dia')) + isotdlabel = FCLabel('%s:' % _('Tools Dia'), color='green', bold=True) isotdlabel.setToolTip( _("Diameters of the tools, separated by comma.\n" "The value of the diameter has to use the dot decimals separator.\n" diff --git a/appGUI/preferences/tools/ToolsMillPrefGroupUI.py b/appGUI/preferences/tools/ToolsMillPrefGroupUI.py index 65cd74ea..00793fb0 100644 --- a/appGUI/preferences/tools/ToolsMillPrefGroupUI.py +++ b/appGUI/preferences/tools/ToolsMillPrefGroupUI.py @@ -38,7 +38,7 @@ class ToolsMillPrefGroupUI(OptionsGroupUI): param_frame.setLayout(param_grid) # Tooldia - tdlabel = FCLabel('%s:' % _('Tools Dia')) + tdlabel = FCLabel('%s:' % _('Tools Dia'), color='green', bold=True) tdlabel.setToolTip( _("Diameters of the tools, separated by comma.\n" "The value of the diameter has to use the dot decimals separator.\n" diff --git a/appGUI/preferences/tools/ToolsNCCPrefGroupUI.py b/appGUI/preferences/tools/ToolsNCCPrefGroupUI.py index b2dddf3f..c547ebb5 100644 --- a/appGUI/preferences/tools/ToolsNCCPrefGroupUI.py +++ b/appGUI/preferences/tools/ToolsNCCPrefGroupUI.py @@ -40,7 +40,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): par_frame.setLayout(par_grid) # Tools Diameters - ncctdlabel = FCLabel('%s:' % _('Tools Dia')) + ncctdlabel = FCLabel('%s:' % _('Tools Dia'), color='green', bold=True) ncctdlabel.setToolTip( _("Diameters of the tools, separated by comma.\n" "The value of the diameter has to use the dot decimals separator.\n" diff --git a/appGUI/preferences/tools/ToolsPaintPrefGroupUI.py b/appGUI/preferences/tools/ToolsPaintPrefGroupUI.py index 85cbfc0a..789de600 100644 --- a/appGUI/preferences/tools/ToolsPaintPrefGroupUI.py +++ b/appGUI/preferences/tools/ToolsPaintPrefGroupUI.py @@ -42,7 +42,7 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI): par_frame.setLayout(param_grid) # Tool dia - ptdlabel = FCLabel('%s:' % _('Tools Dia')) + ptdlabel = FCLabel('%s:' % _('Tools Dia'), color='green', bold=True) ptdlabel.setToolTip( _("Diameters of the tools, separated by comma.\n" "The value of the diameter has to use the dot decimals separator.\n" diff --git a/appGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py b/appGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py index 0349b352..218c71d9 100644 --- a/appGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py +++ b/appGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py @@ -39,7 +39,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): param_frame.setLayout(param_grid) # Nozzle Tool Diameters - nozzletdlabel = FCLabel('%s:' % _('Tools Dia')) + nozzletdlabel = FCLabel('%s:' % _('Tools Dia'), color='green', bold=True) nozzletdlabel.setToolTip( _("Diameters of the tools, separated by comma.\n" "The value of the diameter has to use the dot decimals separator.\n" @@ -51,7 +51,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): param_grid.addWidget(self.nozzle_tool_dia_entry, 0, 1) # New Nozzle Tool Dia - self.addtool_entry_lbl = FCLabel('%s:' % _('New Nozzle Dia')) + self.addtool_entry_lbl = FCLabel('%s:' % _('New Nozzle Dia'), bold=True) self.addtool_entry_lbl.setToolTip( _("Diameter for the new tool to add in the Tool Table") ) @@ -60,8 +60,23 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): self.addtool_entry.set_range(0.0000001, 10000.0000) self.addtool_entry.setSingleStep(0.1) - param_grid.addWidget(self.addtool_entry_lbl, 1, 0) - param_grid.addWidget(self.addtool_entry, 1, 1) + param_grid.addWidget(self.addtool_entry_lbl, 2, 0) + param_grid.addWidget(self.addtool_entry, 2, 1) + + # Margin + self.margin_label = FCLabel('%s:' % _("Margin")) + self.margin_label.setToolTip('%s %s' % ( + _("Offset from the boundary."), + _("Fraction of tool diameter.") + ) + ) + self.margin_entry = FCDoubleSpinner(suffix='%') + self.margin_entry.set_range(-100.0000, 100.0000) + self.margin_entry.set_precision(self.decimals) + self.margin_entry.setSingleStep(0.1) + + param_grid.addWidget(self.margin_label, 4, 0) + param_grid.addWidget(self.margin_entry, 4, 1) # Z dispense start self.z_start_entry = FCDoubleSpinner() @@ -73,8 +88,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): self.z_start_label.setToolTip( _("The height (Z) when solder paste dispensing starts.") ) - param_grid.addWidget(self.z_start_label, 2, 0) - param_grid.addWidget(self.z_start_entry, 2, 1) + param_grid.addWidget(self.z_start_label, 6, 0) + param_grid.addWidget(self.z_start_entry, 6, 1) # Z dispense self.z_dispense_entry = FCDoubleSpinner() @@ -86,8 +101,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): self.z_dispense_label.setToolTip( _("The height (Z) when doing solder paste dispensing.") ) - param_grid.addWidget(self.z_dispense_label, 3, 0) - param_grid.addWidget(self.z_dispense_entry, 3, 1) + param_grid.addWidget(self.z_dispense_label, 8, 0) + param_grid.addWidget(self.z_dispense_entry, 8, 1) # Z dispense stop self.z_stop_entry = FCDoubleSpinner() @@ -99,8 +114,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): self.z_stop_label.setToolTip( _("The height (Z) when solder paste dispensing stops.") ) - param_grid.addWidget(self.z_stop_label, 4, 0) - param_grid.addWidget(self.z_stop_entry, 4, 1) + param_grid.addWidget(self.z_stop_label, 10, 0) + param_grid.addWidget(self.z_stop_entry, 101, 1) # Z travel self.z_travel_entry = FCDoubleSpinner() @@ -113,8 +128,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): _("The height (Z) for travel between pads\n" "(without dispensing solder paste).") ) - param_grid.addWidget(self.z_travel_label, 5, 0) - param_grid.addWidget(self.z_travel_entry, 5, 1) + param_grid.addWidget(self.z_travel_label, 12, 0) + param_grid.addWidget(self.z_travel_entry, 12, 1) # Z toolchange location self.z_toolchange_entry = FCDoubleSpinner() @@ -126,8 +141,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): self.z_toolchange_label.setToolTip( _("The height (Z) for tool (nozzle) change.") ) - param_grid.addWidget(self.z_toolchange_label, 6, 0) - param_grid.addWidget(self.z_toolchange_entry, 6, 1) + param_grid.addWidget(self.z_toolchange_label, 14, 0) + param_grid.addWidget(self.z_toolchange_entry, 14, 1) # X,Y Toolchange location self.xy_toolchange_entry = NumericalEvalTupleEntry(border_color='#0069A9') @@ -136,8 +151,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): _("The X,Y location for tool (nozzle) change.\n" "The format is (x, y) where x and y are real numbers.") ) - param_grid.addWidget(self.xy_toolchange_label, 7, 0) - param_grid.addWidget(self.xy_toolchange_entry, 7, 1) + param_grid.addWidget(self.xy_toolchange_label, 16, 0) + param_grid.addWidget(self.xy_toolchange_entry, 16, 1) # Feedrate X-Y self.frxy_entry = FCDoubleSpinner() @@ -149,8 +164,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): self.frxy_label.setToolTip( _("Feedrate (speed) while moving on the X-Y plane.") ) - param_grid.addWidget(self.frxy_label, 8, 0) - param_grid.addWidget(self.frxy_entry, 8, 1) + param_grid.addWidget(self.frxy_label, 18, 0) + param_grid.addWidget(self.frxy_entry, 18, 1) # Feedrate Z self.frz_entry = FCDoubleSpinner() @@ -163,8 +178,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): _("Feedrate (speed) while moving vertically\n" "(on Z plane).") ) - param_grid.addWidget(self.frz_label, 9, 0) - param_grid.addWidget(self.frz_entry, 9, 1) + param_grid.addWidget(self.frz_label, 20, 0) + param_grid.addWidget(self.frz_entry, 20, 1) # Feedrate Z Dispense self.frz_dispense_entry = FCDoubleSpinner() @@ -177,8 +192,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): _("Feedrate (speed) while moving up vertically\n" "to Dispense position (on Z plane).") ) - param_grid.addWidget(self.frz_dispense_label, 10, 0) - param_grid.addWidget(self.frz_dispense_entry, 10, 1) + param_grid.addWidget(self.frz_dispense_label, 22, 0) + param_grid.addWidget(self.frz_dispense_entry, 22, 1) # Spindle Speed Forward self.speedfwd_entry = FCSpinner() @@ -190,25 +205,25 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): _("The dispenser speed while pushing solder paste\n" "through the dispenser nozzle.") ) - param_grid.addWidget(self.speedfwd_label, 11, 0) - param_grid.addWidget(self.speedfwd_entry, 11, 1) + param_grid.addWidget(self.speedfwd_label, 24, 0) + param_grid.addWidget(self.speedfwd_entry, 24, 1) # Dwell Forward self.dwellfwd_entry = FCDoubleSpinner() self.dwellfwd_entry.set_precision(self.decimals) - self.dwellfwd_entry.set_range(0.0000001, 10000.0000) + self.dwellfwd_entry.set_range(0.0000, 10000.0000) self.dwellfwd_entry.setSingleStep(0.1) self.dwellfwd_label = FCLabel('%s:' % _("Dwell FWD")) self.dwellfwd_label.setToolTip( _("Pause after solder dispensing.") ) - param_grid.addWidget(self.dwellfwd_label, 12, 0) - param_grid.addWidget(self.dwellfwd_entry, 12, 1) + param_grid.addWidget(self.dwellfwd_label, 26, 0) + param_grid.addWidget(self.dwellfwd_entry, 26, 1) # Spindle Speed Reverse self.speedrev_entry = FCSpinner() - self.speedrev_entry.set_range(0, 999999) + self.speedrev_entry.set_range(0, 1000000) self.speedrev_entry.set_step(1000) self.speedrev_label = FCLabel('%s:' % _("Spindle Speed REV")) @@ -216,13 +231,13 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): _("The dispenser speed while retracting solder paste\n" "through the dispenser nozzle.") ) - param_grid.addWidget(self.speedrev_label, 13, 0) - param_grid.addWidget(self.speedrev_entry, 13, 1) + param_grid.addWidget(self.speedrev_label, 28, 0) + param_grid.addWidget(self.speedrev_entry, 28, 1) # Dwell Reverse self.dwellrev_entry = FCDoubleSpinner() self.dwellrev_entry.set_precision(self.decimals) - self.dwellrev_entry.set_range(0.0000001, 10000.0000) + self.dwellrev_entry.set_range(0.0000, 10000.0000) self.dwellrev_entry.setSingleStep(0.1) self.dwellrev_label = FCLabel('%s:' % _("Dwell REV")) @@ -230,8 +245,8 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): _("Pause after solder paste dispenser retracted,\n" "to allow pressure equilibrium.") ) - param_grid.addWidget(self.dwellrev_label, 14, 0) - param_grid.addWidget(self.dwellrev_entry, 14, 1) + param_grid.addWidget(self.dwellrev_label, 30, 0) + param_grid.addWidget(self.dwellrev_entry, 30, 1) # Preprocessors pp_label = FCLabel('%s:' % _('Preprocessor')) @@ -246,7 +261,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): for it in range(self.pp_combo.count()): self.pp_combo.setItemData(it, self.pp_combo.itemText(it), QtCore.Qt.ItemDataRole.ToolTipRole) - param_grid.addWidget(pp_label, 15, 0) - param_grid.addWidget(self.pp_combo, 15, 1) + param_grid.addWidget(pp_label, 32, 0) + param_grid.addWidget(self.pp_combo, 32, 1) self.layout.addStretch() diff --git a/appGUI/preferences/utilities/AutoCompletePrefGroupUI.py b/appGUI/preferences/utilities/AutoCompletePrefGroupUI.py index ee8f2e88..93139239 100644 --- a/appGUI/preferences/utilities/AutoCompletePrefGroupUI.py +++ b/appGUI/preferences/utilities/AutoCompletePrefGroupUI.py @@ -33,7 +33,7 @@ class AutoCompletePrefGroupUI(OptionsGroupUI): hlay0.addWidget(self.del_all_btn) # ## Gerber associations - self.grb_list_label = FCLabel("%s:" % _("Keywords list")) + self.grb_list_label = FCLabel('%s:' % _("Keywords list"), bold=True) self.grb_list_label.setToolTip( _("List of keywords used by\n" "the autocompleter in FlatCAM.\n" diff --git a/appGUI/preferences/utilities/FAExcPrefGroupUI.py b/appGUI/preferences/utilities/FAExcPrefGroupUI.py index 8e3c1c8e..68a6a5f7 100644 --- a/appGUI/preferences/utilities/FAExcPrefGroupUI.py +++ b/appGUI/preferences/utilities/FAExcPrefGroupUI.py @@ -43,7 +43,7 @@ class FAExcPrefGroupUI(OptionsGroupUI): self.vertical_lay.addLayout(hlay0) # # ## Excellon associations - list_label = FCLabel("%s:" % _("Extensions list")) + list_label = FCLabel('%s:' % _("Extensions list"), bold=True) list_label.setToolTip( _("List of file extensions to be\n" "associated with FlatCAM.") diff --git a/appGUI/preferences/utilities/FAGcoPrefGroupUI.py b/appGUI/preferences/utilities/FAGcoPrefGroupUI.py index 84697c77..9df84f31 100644 --- a/appGUI/preferences/utilities/FAGcoPrefGroupUI.py +++ b/appGUI/preferences/utilities/FAGcoPrefGroupUI.py @@ -34,7 +34,7 @@ class FAGcoPrefGroupUI(OptionsGroupUI): hlay0.addWidget(self.del_all_btn) # ## G-Code associations - self.gco_list_label = FCLabel("%s:" % _("Extensions list")) + self.gco_list_label = FCLabel('%s:' % _("Extensions list"), bold=True) self.gco_list_label.setToolTip( _("List of file extensions to be\n" "associated with FlatCAM.") diff --git a/appGUI/preferences/utilities/FAGrbPrefGroupUI.py b/appGUI/preferences/utilities/FAGrbPrefGroupUI.py index fe1dbf53..dcec8eca 100644 --- a/appGUI/preferences/utilities/FAGrbPrefGroupUI.py +++ b/appGUI/preferences/utilities/FAGrbPrefGroupUI.py @@ -33,7 +33,7 @@ class FAGrbPrefGroupUI(OptionsGroupUI): hlay0.addWidget(self.del_all_btn) # ## Gerber associations - self.grb_list_label = FCLabel("%s:" % _("Extensions list")) + self.grb_list_label = FCLabel('%s:' % _("Extensions list"), bold=True) self.grb_list_label.setToolTip( _("List of file extensions to be\n" "associated with FlatCAM.") diff --git a/appGUI/themes/__init__.py b/appGUI/themes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/appGUI/style_sheet.py b/appGUI/themes/dark_style_sheet.py similarity index 100% rename from appGUI/style_sheet.py rename to appGUI/themes/dark_style_sheet.py diff --git a/appGUI/themes/light_style_sheet.py b/appGUI/themes/light_style_sheet.py new file mode 100644 index 00000000..b0e87a96 --- /dev/null +++ b/appGUI/themes/light_style_sheet.py @@ -0,0 +1,1079 @@ +L_STYLE_SHEET = """ +* { + padding: 0px; + margin: 0px; + border: none; + border-style: none; + border-image: unset; + outline: none; +} +QToolBar * { + margin: 0px; + padding: 0px; +} +QWidget { + background: #f8f9fa; + color: #4d5157; + selection-background-color: #0081db; + selection-color: #f8f9fa; +} +QWidget:disabled { + color: #babdc2; + selection-background-color: #dadce0; + selection-color: #babdc2; +} +QWidget { + backward-icon: url(${path}/themes/light/svg/arrow_upward__icon-foreground__rotate-270.svg); + forward-icon: url(${path}/themes/light/svg/arrow_upward__icon-foreground__rotate-90.svg); + leftarrow-icon: url(${path}/themes/light/svg/arrow_upward__icon-foreground__rotate-270.svg); + rightarrow-icon: url(${path}/themes/light/svg/arrow_upward__icon-foreground__rotate-90.svg); + dialog-ok-icon: url(${path}/themes/light/svg/check__icon-foreground.svg); + dialog-cancel-icon: url(${path}/themes/light/svg/close__icon-foreground.svg); + dialog-yes-icon: url(${path}/themes/light/svg/check_circle__icon-foreground.svg); + dialog-no-icon: url(${path}/themes/light/svg/cancel__icon-foreground.svg); + dialog-apply-icon: url(${path}/themes/light/svg/check__icon-foreground.svg); + dialog-reset-icon: url(${path}/themes/light/svg/restart_alt__icon-foreground.svg); + dialog-save-icon: url(${path}/themes/light/svg/save__icon-foreground.svg); + dialog-discard-icon: url(${path}/themes/light/svg/delete__icon-foreground.svg); + dialog-close-icon: url(${path}/themes/light/svg/close__icon-foreground.svg); + dialog-open-icon: url(${path}/themes/light/svg/folder_open__icon-foreground.svg); + dialog-help-icon: url(${path}/themes/light/svg/help__icon-foreground.svg); + filedialog-parent-directory-icon: url(${path}/themes/light/svg/arrow_upward__icon-foreground.svg); + filedialog-new-directory-icon: url(${path}/themes/light/svg/create_new_folder__icon-foreground.svg); + titlebar-close-icon: url(${path}/themes/light/svg/close__icon-foreground.svg); + titlebar-normal-icon: url(${path}/themes/light/svg/flip_to_front__icon-foreground.svg); +} +QCommandLinkButton { + qproperty-icon: url(${path}/themes/light/svg/east__highlight.svg); +} +QMainWindow::separator { + width: 2px; + height: 4px; + background: #dadce0; +} +QMainWindow::separator:hover, +QMainWindow::separator:pressed { + background: #0081db; +} +QToolTip { + background: #ffffff; + color: #4d5157; + border: 1px solid #dadce0; +} +QSizeGrip { + width: 0; + height: 0; + image: none; +} +QStatusBar { + background: #dfe1e5; +} +QStatusBar::item { + border: none; +} +QStatusBar QWidget { + background: transparent; + padding: 0px; + border-radius: $radius{0px}; +} +QStatusBar > .QSizeGrip { + padding: 0; +} +QStatusBar QWidget:hover { + background: #d1d4da; +} +QStatusBar QWidget:pressed { + background: #c3c7ce; +} +QStatusBar QWidget:disabled { + background: #edeef0; +} +QStatusBar QWidget:checked { + background: #c3c7ce; +} +QCheckBox, +QRadioButton { + border-top: 2px solid transparent; + border-bottom: 2px solid transparent; +} +QCheckBox:!window, +QRadioButton:!window { + background: transparent; +} +QCheckBox:hover, +QRadioButton:hover { + border-bottom: 2px solid #0081db; +} +QGroupBox { + font-weight: bold; + border: 1px solid #dadce0; + margin-top: 8px; + padding: 2px 1px 1px 1px; + border-radius: $radius{4px}; +} +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top left; + left: 7px; + margin: 0 2px 0 3px; +} +QGroupBox:flat { + border-color: transparent; +} +QMenuBar { + background: #f8f9fa; + padding: 2px; + border-bottom: 1px solid #dadce0; +} +QMenuBar::item { + background: transparent; + padding: 4px; +} +QMenuBar::item:selected { + padding: 4px; + background: #dadce0; + border-radius: $radius{4px}; +} +QMenuBar::item:pressed { + padding: 4px; + margin-bottom: 0; + padding-bottom: 0; +} +QToolBar { + background: #f8f9fa; + padding: 0px; + font-weight: bold; + spacing: 1px; + margin: 0px; +} +QToolBar::handle:horizontal { + width: 10px; + image: url(${path}/themes/light/svg/drag_indicator_horizontal__icon-foreground.svg); +} +QToolBar::handle:vertical { + height: 20px; + image: url(${path}/themes/light/svg/drag_indicator_horizontal__icon-foreground__rotate-90.svg); +} +QToolBar::separator { + background: #dadce0; +} +QToolBar::separator:horizontal { + width: 2px; + margin: 0 6px; +} +QToolBar::separator:vertical { + height: 2px; + margin: 6px 0; +} +QToolBar > QToolButton { + background: transparent; + padding: 3px; + border-radius: $radius{4px}; +} +QToolBar > QToolButton:hover, +QToolBar > QToolButton::menu-button:hover { + background: #d7d7d7; +} +QToolBar > QToolButton:pressed, +QToolBar > QToolButton::menu-button:pressed { + background: #c4c4c4; +} +QToolBar > QToolButton:checked { + background: #c4c4c4; +} +QToolBar > QToolButton#qt_toolbar_ext_button { + image: url(${path}/themes/light/svg/double_arrow__icon-foreground.svg); + $env_patch{"os": "Windows", "value": "padding: 0; qproperty-icon: unset"}; +} +QToolBar > QToolButton#qt_toolbar_ext_button:disabled { + image: url(${path}/themes/light/svg/double_arrow__icon-foreground-disabled.svg); +} +QToolBar > QWidget { + background: transparent; +} +QMenu { + background: #ffffff; + padding: 8px 0; + border: 1px solid #dadce0; +} +QMenu::separator { + margin: 4px 0; + height: 1px; + background: #dadce0; +} +QMenu::item { + padding: 4px 28px; +} +QMenu::item:selected { + background: #dadce0; +} +QMenu::icon { + padding-left: 10px; + width: 14px; + height: 14px; +} +QMenu::right-arrow { + margin: 2px; + padding-left: 12px; + height: 20px; + width: 20px; + image: url(${path}/themes/light/svg/chevron_right__icon-foreground.svg); +} +QMenu::right-arrow:disabled { + image: url(${path}/themes/light/svg/chevron_right__icon-foreground-disabled.svg); +} +QScrollBar { + background: #edeff2; + $env_patch{"os": "Darwin", "value": "background: transparent"}; + border-radius: $radius{4px}; +} +QScrollBar:horizontal { + height: 14px; + $env_patch{"os": "Darwin", "value": "height: 7px;"}; +} +QScrollBar:vertical { + width: 14px; + $env_patch{"os": "Darwin", "value": "width: 7px;"}; +} +QScrollBar::handle { + background: rgba(155.000, 155.000, 157.000, 0.737); + border-radius: $radius{3px}; +} +QScrollBar::handle:hover { + background: rgba(117.000, 117.000, 119.000, 0.827); +} +QScrollBar::handle:pressed { + background: rgba(96.000, 96.000, 98.000, 0.933); +} +QScrollBar::handle:disabled { + background-color: #d6dbe2; +} +QScrollBar::handle:horizontal { + min-width: 8px; + margin: 4px 14px; + $env_patch{"os": "Darwin", "value": "margin: 0;"}; +} +QScrollBar::handle:horizontal:hover { + margin: 2px 14px; + $env_patch{"os": "Darwin", "value": "margin: 0;"}; +} +QScrollBar::handle:vertical { + min-height: 8px; + margin: 14px 4px; + $env_patch{"os": "Darwin", "value": "margin: 0;"}; +} +QScrollBar::handle:vertical:hover { + margin: 14px 2px; + $env_patch{"os": "Darwin", "value": "margin: 0;"}; +} +QScrollBar::sub-page, QScrollBar::add-page { + background: transparent; +} +QScrollBar::sub-line, +QScrollBar::add-line { + background: transparent; + width: 14px; + height: 14px; + margin: 2px; + subcontrol-origin: margin; + $env_patch{"os": "Darwin", "value": "width: 0; height: 0; margin: 0"}; +} +QScrollBar::sub-line:vertical { + subcontrol-position: top; +} +QScrollBar::add-line:vertical { + subcontrol-position: bottom; +} +QScrollBar::sub-line:horizontal { + subcontrol-position: left; +} +QScrollBar::add-line:horizontal { + subcontrol-position: right; +} +QScrollBar::up-arrow { + image: url(${path}/themes/light/svg/arrow_drop_up__scrollbar-handle.svg); +} +QScrollBar::right-arrow { + image: url(${path}/themes/light/svg/arrow_drop_up__scrollbar-handle__rotate-90.svg); +} +QScrollBar::down-arrow { + image: url(${path}/themes/light/svg/arrow_drop_up__scrollbar-handle__rotate-180.svg); +} +QScrollBar::left-arrow { + image: url(${path}/themes/light/svg/arrow_drop_up__scrollbar-handle__rotate-270.svg); +} +QScrollBar::up-arrow:hover { + image: url(${path}/themes/light/svg/arrow_drop_up__scrollbar-handle-pressed.svg); +} +QScrollBar::right-arrow:hover { + image: url(${path}/themes/light/svg/arrow_drop_up__scrollbar-handle-pressed__rotate-90.svg); +} +QScrollBar::down-arrow:hover { + image: url(${path}/themes/light/svg/arrow_drop_up__scrollbar-handle-pressed__rotate-180.svg); +} +QScrollBar::left-arrow:hover { + image: url(${path}/themes/light/svg/arrow_drop_up__scrollbar-handle-pressed__rotate-270.svg); +} +QScrollBar::up-arrow:disabled { + image: url(${path}/themes/light/svg/arrow_drop_up__scrollbar-disabled.svg); +} +QScrollBar::right-arrow:disabled { + image: url(${path}/themes/light/svg/arrow_drop_up__scrollbar-disabled__rotate-90.svg); +} +QScrollBar::down-arrow:disabled { + image: url(${path}/themes/light/svg/arrow_drop_up__scrollbar-disabled__rotate-180.svg); +} +QScrollBar::left-arrow:disabled { + image: url(${path}/themes/light/svg/arrow_drop_up__scrollbar-disabled__rotate-270.svg); +} +QProgressBar { + border: 1px solid #dadce0; + text-align: center; + color: #4d5157; + border-radius: $radius{4px}; +} +QProgressBar::chunk { + background: #0081db; + border-radius: $radius{3px}; +} +QProgressBar::chunk:disabled { + background: #dadce0; +} +QPushButton { + color: #0081db; + border: 1px solid #dadce0; + padding: 4px 8px; + border-radius: $radius{4px}; +} +QPushButton:!window { + background: transparent; +} +QPushButton:flat, +QPushButton:default { + border: none; + padding: 5px 9px; +} +QPushButton:default { + color: #f8f9fa; + background: #0081db; +} +QPushButton:hover, +QPushButton:flat:hover { + background: rgba(181.000, 202.000, 244.000, 0.333); +} +QPushButton:pressed, +QPushButton:flat:pressed, +QPushButton:checked:pressed, +QPushButton:flat:checked:pressed { + background: rgba(181.000, 202.000, 244.000, 0.933); +} +QPushButton:checked, +QPushButton:flat:checked { + background: rgba(181.000, 202.000, 244.000, 0.733); +} +QPushButton:default:hover { + background: #3781ea; +} +QPushButton:default:pressed { + background: #6ca1f0; +} +QPushButton:default:disabled { + background: #dadce0; +} +QDialogButtonBox QPushButton { + min-width: 65px; +} +QToolButton { + background: transparent; + padding: 5px; + spacing: 2px; + border-radius: $radius{2px}; +} +QToolButton:hover, +QToolButton::menu-button:hover { + background: rgba(181.000, 202.000, 244.000, 0.333); +} +QToolButton:pressed, +QToolButton:checked:pressed, +QToolButton::menu-button:pressed { + background: rgba(181.000, 202.000, 244.000, 0.933); +} +QToolButton:selected, +QToolButton:checked { + background: rgba(181.000, 202.000, 244.000, 0.733); +} +QToolButton::checked:disabled { + background: #dadce0; +} +QToolButton::menu-indicator { + height: 18px; + width: 18px; + top: 6px; + left: 3px; + image: url(${path}/themes/light/svg/expand_less__icon-foreground__rotate-180.svg); +} +QToolButton::menu-indicator:disabled { + image: url(${path}/themes/light/svg/expand_less__icon-foreground-disabled__rotate-180.svg); +} +QToolButton::menu-arrow { + image: unset; +} +QToolButton::menu-button { + subcontrol-origin: margin; + border: none; + width: 17px; + border-top-right-radius: $radius{4px}; + border-bottom-right-radius: $radius{4px}; + image: url(${path}/themes/light/svg/expand_less__icon-foreground__rotate-180.svg); +} +QToolButton::menu-button:disabled { + image: url(${path}/themes/light/svg/expand_less__icon-foreground-disabled__rotate-180.svg); +} +QToolButton[ +$env_patch{"version": "<6.0.0", "qt": "PySide2", "value": "popupMode=MenuButtonPopup"} +$env_patch{"version": "<6.0.0", "qt": "PyQt5", "value": "popupMode=\\\"1\\\""} +$env_patch{"version": ">=6.0.0", "value": "popupMode=MenuButtonPopup"} +] { + padding-right: 1px; + margin-right: 18px; + border-top-right-radius: $radius{0}; + border-bottom-right-radius: $radius{0}; +} +QComboBox { + border: 1px solid #dadce0; + min-height: 1.5em; + padding: 0 4px; + background: rgba(255.000, 255.000, 255.000, 0.000); + border-radius: $radius{4px}; +} +QComboBox:focus, +QComboBox:open { + border: 1px solid #0081db; +} +QComboBox::drop-down { + border: none; + padding-right: 4px; +} +QComboBox::down-arrow { + image: url(${path}/themes/light/svg/expand_less__icon-foreground__rotate-180.svg); +} +QComboBox::down-arrow:disabled { + image: url(${path}/themes/light/svg/expand_less__icon-foreground-disabled__rotate-180.svg); +} +QComboBox::item:selected { + border: none; + background: #4ca6e5; + color: #4d5157; +} +QComboBox QAbstractItemView { + background: #ffffff; + margin: 0; + border: 1px solid #dadce0; + selection-background-color: #4ca6e5; + selection-color: #4d5157; + padding: 2px; +} +QComboBox QAbstractItemView[ +$env_patch{"version": "<6.0.0", "value": "frameShape=\\\"0\\\""} +$env_patch{"version": ">=6.0.0", "value": "frameShape=NoFrame"} +] { + border-color: #dadce0; +} +QSlider { + padding: 2px 0; +} +QSlider::groove { + border-radius: $radius{2px}; +} +QSlider::groove:horizontal { + height: 4px; +} +QSlider::groove:vertical { + width: 4px; +} +QSlider::sub-page, QSlider::handle { + background: #0081db; +} +QSlider::sub-page:disabled, +QSlider::add-page:disabled, +QSlider::handle:disabled { + background: #dadce0; +} +QSlider::add-page { + background: #abc6f6; +} +QSlider::handle:hover { + background: #3781ea; +} +QSlider::handle:pressed { + background: #6ca1f0; +} +QSlider::handle:horizontal { + width: 16px; + height: 8px; + margin: -6px 0; + border-radius: 8px; +} +QSlider::handle:vertical { + width: 8px; + height: 16px; + margin: 0 -6px; + border-radius: 8px; +} +QTabWidget::pane { + border: 1px solid #dadce0; + border-radius: $radius{4px}; +} +QTabBar { + qproperty-drawBase: 0; +} +QTabBar::close-button:selected { + image: url(${path}/themes/light/svg/close__icon-foreground.svg); +} +QTabBar::close-button:!selected { + image: url(${path}/themes/light/svg/close__tabbar-button-inselected.svg) +} +QTabBar::close-button:disabled { + image: url(${path}/themes/light/svg/close__icon-foreground-disabled.svg); +} +QTabBar::close-button:hover { + background: #93b2ef; + border-radius: $radius{4px}; +} +QTabBar::close-button:hover:!selected { + background: #aec5f4; +} +QTabBar::tab { + padding: 3px; +} +QTabBar::tab:hover { + background: rgba(181.000, 202.000, 244.000, 0.333); +} +QTabBar::tab:selected { + color: #0081db; + background: rgba(181.000, 202.000, 244.000, 0.933); +} +QTabBar::tab:selected:disabled { + background: #dadce0; + color: #babdc2; +} +QTabBar::tab:top { + border-bottom: 2px solid #dadce0; + margin-left: 4px; + border-top-left-radius: $radius{2px}; + border-top-right-radius: $radius{2px}; +} +QTabBar::tab:top:selected { + border-bottom: 2px solid #0081db; +} +QTabBar::tab:top:hover { + border-color: #0081db; +} +QTabBar::tab:top:selected:disabled { + border-color: #dadce0; +} +QTabBar::tab:bottom { + border-top: 2px solid #dadce0; + margin-left: 4px; + border-bottom-left-radius: $radius{2px}; + border-bottom-right-radius: $radius{2px}; +} +QTabBar::tab:bottom:selected { + border-top: 2px solid #0081db; +} +QTabBar::tab:bottom:hover { + border-color: #0081db; +} +QTabBar::tab:bottom:selected:disabled { + border-color: #dadce0; +} +QTabBar::tab:left { + border-right: 2px solid #dadce0; + margin-top: 4px; + border-top-left-radius: $radius{2px}; + border-bottom-left-radius: $radius{2px}; +} +QTabBar::tab:left:selected { + border-right: 2px solid #0081db; +} +QTabBar::tab:left:hover { + border-color: #0081db; +} +QTabBar::tab:left:selected:disabled { + border-color: #dadce0; +} +QTabBar::tab:right { + border-left: 2px solid #dadce0; + margin-top: 4px; + border-top-right-radius: $radius{2px}; + border-bottom-right-radius: $radius{2px}; +} +QTabBar::tab:right:selected { + border-left: 2px solid #0081db; +} +QTabBar::tab:right:hover { + border-color: #0081db; +} +QTabBar::tab:right:selected:disabled { + border-color: #dadce0; +} +QDockWidget { + border: 1px solid #dadce0; + border-radius: $radius{4px}; +} +QDockWidget::title { + padding: 3px; + spacing: 4px; + background: #edeef0; +} +QDockWidget::close-button, +QDockWidget::float-button { + border-radius: $radius{2px}; +} +QDockWidget::close-button:hover, +QDockWidget::float-button:hover { + background: rgba(181.000, 202.000, 244.000, 0.333); +} +QDockWidget::close-button:pressed, +QDockWidget::float-button:pressed { + background: rgba(181.000, 202.000, 244.000, 0.933); +} +QFrame { + border: 1px solid #dadce0; + padding: 1px; + border-radius: $radius{4px}; +} +.QFrame { + padding: 0; +} +QFrame[ +$env_patch{"version": "<6.0.0", "qt": "PySide2", "value": "frameShape=NoFrame"} +$env_patch{"version": "<6.0.0", "qt": "PyQt5", "value": "frameShape=\\\"0\\\""} +$env_patch{"version": ">=6.0.0", "value": "frameShape=NoFrame"} +] { + border-color: transparent; + padding: 0; +} +.QFrame[ +$env_patch{"version": "<6.0.0", "qt": "PySide2", "value": "frameShape=NoFrame"} +$env_patch{"version": "<6.0.0", "qt": "PyQt5", "value": "frameShape=\\\"0\\\""} +$env_patch{"version": ">=6.0.0", "value": "frameShape=NoFrame"} +] { + border: none; +} +QFrame[ +$env_patch{"version": "<6.0.0", "qt": "PySide2", "value": "frameShape=Panel"} +$env_patch{"version": "<6.0.0", "qt": "PyQt5", "value": "frameShape=\\\"2\\\""} +$env_patch{"version": ">=6.0.0", "value": "frameShape=Panel"} +] { + border-color: #ffffff; + background: #ffffff; +} +QFrame[ +$env_patch{"version": "<6.0.0", "qt": "PySide2", "value": "frameShape=HLine"} +$env_patch{"version": "<6.0.0", "qt": "PyQt5", "value": "frameShape=\\\"4\\\""} +$env_patch{"version": ">=6.0.0", "value": "frameShape=HLine"} +] { + max-height: 2px; + border: none; + background: #dadce0; +} +QFrame[ +$env_patch{"version": "<6.0.0", "qt": "PySide2", "value": "frameShape=VLine"} +$env_patch{"version": "<6.0.0", "qt": "PyQt5", "value": "frameShape=\\\"5\\\""} +$env_patch{"version": ">=6.0.0", "value": "frameShape=VLine"} +] { + max-width: 2px; + border: none; + background: #dadce0; +} +QLCDNumber { + color: #4d5157; + min-width: 2em; + margin: 2px; +} +QLabel:!window, +QLCDNumber:!window { + background-color: transparent; +} +QToolBox:selected { + border: 2px solid #0081db; +} +QToolBox::tab { + background: #edeef0; + border-bottom: 2px solid #dadce0; + border-top-left-radius: $radius{4px}; + border-top-right-radius: $radius{4px}; +} +QToolBox::tab:selected { + border-bottom: 2px solid #0081db; +} +QToolBox::tab:selected:disabled { + border-bottom: 2px solid #dadce0; +} + +QAbstractScrollArea { + selection-background-color: #4ca6e5; + selection-color: #4d5157; + margin: 0px; + padding: 0px, 0px, 0px, 0px; +} +QAbstractScrollArea:disabled { + selection-background-color: #0081db; +} +QAbstractScrollArea::corner { + background: transparent; +} +QAbstractScrollArea > .QWidget { + background: transparent; +} +QAbstractScrollArea > .QWidget > .QWidget { + background: transparent; +} +QTextEdit, QPlainTextEdit { + background: #ffffff; +} +QTextEdit:focus, +QTextEdit:selected, +QPlainTextEdit:focus, +QPlainTextEdit:selected { + border: 1px solid #0081db; + selection-background-color: #a2d8ff; +} +QTextEdit:!focus, +QPlainTextEdit:!focus { + $env_patch{"version": ">=5.15.0", "value": "selection-background-color: #e4e6f2"}; +} +QTextEdit:!active, +QPlainTextEdit:!active { + $env_patch{"version": "<5.15.0", "value": "selection-background-color: #e4e6f2"}; +} +QAbstractItemView { + alternate-background-color: #e9ecef; +} +QAbstractItemView::item { + $env_patch{"version": ">=6.0.0", "value": "border-color: transparent"}; +} +QAbstractItemView:selected:!active, +QAbstractItemView:selected:!focus, +QAbstractItemView::item:selected:!active, +QTreeView::branch:selected:!active { + background: #e4e6f2; +} +QAbstractItemView::item:selected, +QTreeView::branch:selected { + background: #4ca6e5; + color: #4d5157; +} +QAbstractItemView::item:!selected:hover, +QTreeView::branch:!selected:hover { + background: #d3d3d3; +} +QAbstractItemView::item:selected:disabled { + color: #babdc2; +} +QAbstractItemView QLineEdit, +QAbstractItemView QAbstractSpinBox, +QAbstractItemView QComboBox, +QAbstractItemView QAbstractButton { + padding: 0; + margin: 0px; +} +QTreeView::branch { + border-image: url(${path}/themes/light/svg/vertical_line__guides-stroke-inactive.svg) 0; +} +QTreeView::branch:active { + border-image: url(${path}/themes/light/svg/vertical_line__icon-foreground.svg) 0; +} +QTreeView::branch:disabled { + border-image: url(${path}/themes/light/svg/vertical_line__icon-foreground-disabled.svg) 0; +} +QTreeView::branch:has-siblings:adjoins-item, +QTreeView::branch:!has-children:!has-siblings:adjoins-item { + border-image: unset; +} +QTreeView::branch:has-children:!has-siblings:closed, +QTreeView::branch:closed:has-children:has-siblings { + border-image: unset; + image: url(${path}/themes/light/svg/chevron_right__icon-foreground.svg); +} +QTreeView::branch:has-children:!has-siblings:closed:disabled, +QTreeView::branch:closed:has-children:has-siblings:disabled { + image: url(${path}/themes/light/svg/chevron_right__icon-foreground-disabled.svg); +} +QTreeView::branch:open:has-children:!has-siblings, +QTreeView::branch:open:has-children:has-siblings { + border-image: unset; + image: url(${path}/themes/light/svg/expand_less__icon-foreground__rotate-180.svg); +} +QTreeView::branch:open:has-children:!has-siblings:disabled, +QTreeView::branch:open:has-children:has-siblings:disabled { + image: url(${path}/themes/light/svg/expand_less__icon-foreground-disabled__rotate-180.svg); +} +QTableView { + gridline-color: #58595c; + background: #ffffff; +} +QTableView QTableCornerButton::section { + margin: 0 1px 1px 0; + background: #dadce0; + border-top-left-radius: $radius{2px}; +} +QTableView QTableCornerButton::section:pressed { + background: #4ca6e5; +} +QTableView > QHeaderView{ + background: #ffffff; +} +QHeaderView { + padding: 0; + margin: 0; + border: none; + border-radius: $radius{0}; +} +QHeaderView::section { + background: #dadce0; + text-align: left; + padding: 0 0px; + border: none; +} +QHeaderView::section:horizontal:on, +QHeaderView::section:vertical:on { + border-color: #0081db; +} +QHeaderView::section:horizontal:on:disabled, +QHeaderView::section:vertical:on:disabled { + color: #dadce0; + border-color: #dadce0; +} +QHeaderView::section:horizontal { + border-top: 2px solid transparent; + margin-right: 1px; +} +QHeaderView::section:vertical { + border-left: 2px solid transparent; + margin-bottom: 1px; +} +QHeaderView::section:last, +QHeaderView::section:only-one { + margin: 0; +} +QHeaderView::down-arrow { + margin: -2px -6px -6px -6px; + image: url(${path}/themes/light/svg/expand_less__icon-foreground__rotate-180.svg); +} +QHeaderView::down-arrow:disabled { + image: url(${path}/themes/light/svg/expand_less__icon-foreground-disabled__rotate-180.svg); +} +QHeaderView::up-arrow { + margin: -2px -6px -6px -6px; + image: url(${path}/themes/light/svg/expand_less__icon-foreground.svg); +} +QHeaderView::up-arrow:disabled { + image: url(${path}/themes/light/svg/expand_less__icon-foreground-disabled.svg); +} +QCalendarWidget { + border: none; +} +QCalendarWidget > .QWidget { + background: #ffffff; + border-bottom: 1px solid #dadce0; + border-radius: $radius{4px}; + border-bottom-left-radius: $radius{0}; + border-bottom-right-radius: $radius{0}; +} +QCalendarWidget > .QWidget > QWidget { + padding: 1px; +} +QCalendarWidget > .QWidget > QSpinBox { + margin-left: 1px; +} +QCalendarWidget > .QWidget > QSpinBox::up-button, +QCalendarWidget > .QWidget > QSpinBox::down-button { + margin: 1px 3px 1px 1px; +} +QCalendarWidget .QWidget > QToolButton { + border-radius: $radius{4px}; +} +QCalendarWidget > .QWidget > QToolButton::menu-indicator { + height: 14px; + width: 14px; + top: 5px; + left: 3px; +} +QCalendarWidget > QTableView { + margin: 0; + border: none; + border-radius: $radius{4px}; + border-top-left-radius: $radius{0}; + border-top-right-radius: $radius{0}; +} +QCalendarWidget > .QWidget > QToolButton#qt_calendar_prevmonth { + qproperty-icon: url(${path}/themes/light/svg/arrow_upward__icon-foreground__rotate-270.svg); +} +QCalendarWidget > .QWidget > QToolButton#qt_calendar_nextmonth { + qproperty-icon: url(${path}/themes/light/svg/arrow_upward__icon-foreground__rotate-90.svg); +} +QLineEdit, +QAbstractSpinBox { + border: 1px solid #dadce0; + padding: 3px 4px; + min-height: 1em; + background: rgba(255.000, 255.000, 255.000, 0.000); + border-radius: $radius{4px}; +} +QLineEdit:focus, +QAbstractSpinBox:focus { + border: 1px solid #0081db; +} +QAbstractSpinBox::up-button, +QAbstractSpinBox::down-button { + subcontrol-origin: border; + width: 12px; + height: 4px; + padding: 3px; + border-radius: $radius{4px}; +} +QAbstractSpinBox::up-button:hover, +QAbstractSpinBox::down-button:hover { + background: #e2eafb; +} +QAbstractSpinBox::up-button { + subcontrol-position: top right; + margin: 3px 3px 1px 1px; +} +QAbstractSpinBox::up-arrow { + image: url(${path}/themes/light/svg/arrow_drop_up__icon-foreground.svg); +} +QAbstractSpinBox::up-arrow:disabled { + image: url(${path}/themes/light/svg/arrow_drop_up__icon-foreground-disabled.svg); +} +QAbstractSpinBox::down-button { + subcontrol-position: bottom right; + margin: 1px 3px 3px 1px; +} +QAbstractSpinBox::down-arrow { + image: url(${path}/themes/light/svg/arrow_drop_up__icon-foreground__rotate-180.svg); +} +QAbstractSpinBox::down-arrow:disabled { + image: url(${path}/themes/light/svg/arrow_drop_up__icon-foreground-disabled__rotate-180.svg); +} +QDateTimeEdit::drop-down { + padding-right: 4px; + width: 16px; + image: url(${path}/themes/light/svg/calendar_today__icon-foreground.svg); +} +QDateTimeEdit::drop-down:disabled { + image: url(${path}/themes/light/svg/calendar_today__icon-foreground-disabled.svg); +} +QDateTimeEdit::down-arrow[calendarPopup=true] { + image: none; +} +QDateTimeEdit QCalendarWidget QAbstractItemView { + padding: -1px; + border: none; +} +QFileDialog > QFrame QAbstractItemView { + border: none; +} +QFileDialog > QFrame > QFrame QFrame QFrame { + border: none; + padding: 0; +} +QFontDialog QListView { + min-height: 60px; +} +QFontDialog QScrollBar:vertical { + margin: 0; +} +QComboBox::indicator:checked, +QMenu::indicator:checked { + width: 18px; + image: url(${path}/themes/light/svg/check__icon-foreground.svg); +} +QMenu::indicator { + width: 18px; + background: #c4c7cc; + margin-left: 3px; + border-radius: $radius{4px}; +} +QCheckBox, +QRadioButton { + spacing: 8px; +} +QGroupBox::title, +QAbstractItemView::item { + spacing: 6px; +} +QCheckBox::indicator, +QGroupBox::indicator, +QAbstractItemView::indicator, +QRadioButton::indicator { + height: 18px; + width: 18px; +} +QCheckBox::indicator, +QGroupBox::indicator, +QAbstractItemView::indicator { + image: url(${path}/themes/light/svg/check_box_outline_blank__icon-foreground.svg); +} +QCheckBox::indicator:unchecked:disabled, +QGroupBox::indicator:unchecked:disabled, +QAbstractItemView::indicator:unchecked:disabled { + image: url(${path}/themes/light/svg/check_box_outline_blank__icon-foreground-disabled.svg); +} +QCheckBox::indicator:checked, +QGroupBox::indicator:checked, +QAbstractItemView::indicator:checked { + image: url(${path}/themes/light/svg/check_box__highlight.svg); +} +QCheckBox::indicator:checked:disabled, +QGroupBox::indicator:checked:disabled, +QAbstractItemView::indicator:checked:disabled { + image: url(${path}/themes/light/svg/check_box__icon-foreground-disabled.svg); +} +QCheckBox::indicator:indeterminate, +QAbstractItemView::indicator:indeterminate { + image: url(${path}/themes/light/svg/indeterminate_check_box__highlight.svg); +} +QCheckBox::indicator:indeterminate:disabled, +QAbstractItemView::indicator:indeterminate:disabled { + image: url(${path}/themes/light/svg/indeterminate_check_box__icon-foreground-disabled.svg); +} +QRadioButton::indicator:unchecked { + image: url(${path}/themes/light/svg/radio_button_unchecked__icon-foreground.svg); +} +QRadioButton::indicator:unchecked:disabled { + image: url(${path}/themes/light/svg/radio_button_unchecked__icon-foreground-disabled.svg); +} +QRadioButton::indicator:checked { + image: url(${path}/themes/light/svg/radio_button_checked__highlight.svg); +} +QRadioButton::indicator:checked:disabled { + image: url(${path}/themes/light/svg/radio_button_checked__icon-foreground-disabled.svg); +} +QComboBox QAbstractItemView, +QStatusBar > QMenu, +QDateTimeEdit QCalendarWidget QAbstractItemView, +QDateTimeEdit QCalendarWidget .QWidget { + margin: 0; + border-radius: $radius{0}; + $env_patch{"version": "<6.0.0", "os": "Darwin", "value": "border-radius: $radius{4px}"}; +} +QMenu, +QStatusBar > QMenu { + $env_patch{"version": "<6.0.0", "os": "Darwin", "value": "border-radius: $radius{8px}"}; +} +PlotWidget { + padding: 0; +} +ParameterTree > .QWidget > .QWidget > .QWidget > QAbstractSpinBox::up-button, +ParameterTree > .QWidget > .QWidget > .QWidget > QAbstractSpinBox::down-button { + margin: 2px 3px 1px 1px; + padding: 2px; +} +ParameterTree > .QWidget > .QWidget > .QWidget > QComboBox{ + min-height: 1.2em; +} + +""" \ No newline at end of file diff --git a/appMain.py b/appMain.py index 37e74ba8..61ff699c 100644 --- a/appMain.py +++ b/appMain.py @@ -42,13 +42,14 @@ import tkinter as tk import qdarktheme import qdarktheme.themes.dark.stylesheet as qdarksheet +import qdarktheme.themes.light.stylesheet as qlightsheet # #################################################################################################################### # ################################### Imports part of FlatCAM ############################################# # #################################################################################################################### # Various -from appGUI import style_sheet +from appGUI.themes import dark_style_sheet, light_style_sheet from appCommon.Common import LoudDict from appCommon.Common import color_variant @@ -108,6 +109,8 @@ import gettext import appTranslation as fcTranslate import builtins +import darkdetect + if sys.platform == 'win32': import winreg @@ -610,6 +613,22 @@ class App(QtCore.QObject): # self.preferencesUiManager.show_preferences_gui() + # Set global_theme based on appearance + if self.options["global_appearance"] == 'auto': + if darkdetect.isDark(): + theme = 'dark' + else: + theme = 'light' + else: + if self.options["global_appearance"] == 'default': + theme = 'default' + elif self.options["global_appearance"] == 'dark': + theme = 'dark' + else: + theme = 'light' + + self.options["global_theme"] = theme + self.app_units = self.options["units"] self.default_units = self.defaults["units"] @@ -618,16 +637,15 @@ class App(QtCore.QObject): else: self.decimals = int(self.options['decimals_inch']) - if self.options["global_gray_icons"] is False: + if self.options["global_theme"] == 'default': self.resource_location = 'assets/resources' + elif self.options["global_theme"] == 'light': + self.resource_location = 'assets/resources' + qlightsheet.STYLE_SHEET = light_style_sheet.L_STYLE_SHEET + self.qapp.setStyleSheet(qdarktheme.load_stylesheet('light')) else: self.resource_location = 'assets/resources/dark_resources' - - # ############################################################################################################# - # ######################################### DARK THEME ######################################################## - # ############################################################################################################# - if self.options["global_gray_icons"] is True: - qdarksheet.STYLE_SHEET = style_sheet.D_STYLE_SHEET # patching so I can do my own changes to the theme + qdarksheet.STYLE_SHEET = dark_style_sheet.D_STYLE_SHEET self.qapp.setStyleSheet(qdarktheme.load_stylesheet()) # ########################################################################################################### @@ -1021,20 +1039,19 @@ class App(QtCore.QObject): self.FC_dark_blue = '#0000ffbf' theme_settings = QtCore.QSettings("Open Source", "FlatCAM") - if theme_settings.contains("theme"): - theme = theme_settings.value('theme', type=str) - else: - theme = 'white' + theme_settings.setValue("appearance", self.options["global_appearance"]) + theme_settings.setValue("theme", self.options["global_theme"]) + theme_settings.setValue("dark_canvas", self.options["global_dark_canvas"]) if self.options["global_cursor_color_enabled"]: self.cursor_color_3D = self.options["global_cursor_color"] else: - if theme == 'white': + if (theme == 'light' or theme == 'default') and not self.options["global_dark_canvas"]: self.cursor_color_3D = 'black' else: self.cursor_color_3D = 'gray' - # update the options dict with the setting in QSetting + # update the 'options' dict with the setting in QSetting self.options['global_theme'] = theme # ######################## @@ -8624,7 +8641,7 @@ class App(QtCore.QObject): root = d_properties_tw.invisibleRootItem() font = QtGui.QFont() font.setBold(True) - p_color = QtGui.QColor("#000000") if self.options['global_gray_icons'] is False else QtGui.QColor("#FFFFFF") + p_color = QtGui.QColor("#000000") if self.options['global_theme'] == 'light' else QtGui.QColor("#FFFFFF") # main Items categories general_cat = d_properties_tw.addParent(root, _('General'), expanded=True, color=p_color, font=font) @@ -9430,6 +9447,7 @@ class App(QtCore.QObject): return float('%.*f' % (dec_nr, float(val))) + class ArgsThread(QtCore.QObject): open_signal = pyqtSignal(list) start = pyqtSignal() diff --git a/appObjects/AppObjectTemplate.py b/appObjects/AppObjectTemplate.py index 192bb636..1e22e465 100644 --- a/appObjects/AppObjectTemplate.py +++ b/appObjects/AppObjectTemplate.py @@ -536,7 +536,7 @@ class FlatCAMObj(QtCore.QObject): font = QtGui.QFont() font.setBold(True) - p_color = QtGui.QColor("#000000") if self.app.options['global_gray_icons'] is False \ + p_color = QtGui.QColor("#000000") if self.app.options['global_theme'] == 'light' \ else QtGui.QColor("#FFFFFF") # main Items categories diff --git a/appObjects/CNCJobObject.py b/appObjects/CNCJobObject.py index 8cf9abb8..e795c3ef 100644 --- a/appObjects/CNCJobObject.py +++ b/appObjects/CNCJobObject.py @@ -1001,15 +1001,15 @@ class CNCJobObject(FlatCAMObj, CNCjob): if postamble == '': postamble = self.app.options["cncjob_append"] - try: - if self.special_group: - self.app.inform.emit('[WARNING_NOTCL] %s %s %s.' % - (_("This CNCJob object can't be processed because it is a"), - str(self.special_group), - _("CNCJob object"))) - return 'fail' - except AttributeError: - pass + # try: + # if self.special_group: + # self.app.inform.emit('[WARNING_NOTCL] %s %s %s.' % + # (_("This CNCJob object can't be processed because it is a"), + # str(self.special_group), + # _("CNCJob object"))) + # return 'fail' + # except AttributeError: + # pass # if this dict is not empty then the object is a Geometry object if self.obj_options['type'].lower() == 'geometry': @@ -1357,7 +1357,8 @@ class CNCJobObject(FlatCAMObj, CNCjob): self.plot2(tooldia=dia_plot, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind) self.shapes.redraw() - except (ObjectDeleted, AttributeError): + except (ObjectDeleted, AttributeError) as err: + self.app.log.debug("CNCJobObject.plot() --> %s" % str(err)) self.shapes.clear(update=True) if self.app.use_3d_engine: self.annotation.clear(update=True) diff --git a/appObjects/ObjectCollection.py b/appObjects/ObjectCollection.py index d8fa9660..742aefab 100644 --- a/appObjects/ObjectCollection.py +++ b/appObjects/ObjectCollection.py @@ -508,7 +508,7 @@ class ObjectCollection(QtCore.QAbstractItemModel): theme_settings = QtCore.QSettings("Open Source", "FlatCAM") theme = theme_settings.value('theme', type=str) - if theme in ['black', 'dark']: + if theme == 'dark': color = QColor(self.app.options['global_proj_item_color_dark'][:-2]) color_disabled = QColor(self.app.options['global_proj_item_dis_color_dark'][:-2]) else: diff --git a/appPlugins/ToolAlignObjects.py b/appPlugins/ToolAlignObjects.py index c8f1bfdb..d8e95ace 100644 --- a/appPlugins/ToolAlignObjects.py +++ b/appPlugins/ToolAlignObjects.py @@ -503,7 +503,7 @@ class AlignUI: par_frame.setLayout(grid2) # Alignment Type - self.a_type_lbl = FCLabel('%s:' % _("Alignment Type")) + self.a_type_lbl = FCLabel('%s:' % _("Alignment Type"), bold=True) self.a_type_lbl.setToolTip( _("The type of alignment can be:\n" "- Single Point -> it require a single point of sync, the action will be a translation\n" diff --git a/appPlugins/ToolCalibration.py b/appPlugins/ToolCalibration.py index a265562d..a897b58d 100644 --- a/appPlugins/ToolCalibration.py +++ b/appPlugins/ToolCalibration.py @@ -767,7 +767,7 @@ class CalibrationUI: grid_lay = GLay(v_spacing=5, h_spacing=3, c_stretch=[0, 1, 0]) self.layout.addLayout(grid_lay) - self.gcode_title_label = FCLabel('%s:' % _('Parameters')) + self.gcode_title_label = FCLabel('%s:' % _('Parameters'), bold=True) self.gcode_title_label.setToolTip( _("Parameters used when creating the GCode in this tool.") ) @@ -873,7 +873,7 @@ class CalibrationUI: grid_lay.addWidget(FCLabel(''), 9, 0, 1, 3) - step_1 = FCLabel('%s' % _("STEP 1: Acquire Calibration Points")) + step_1 = FCLabel('%s' % _("STEP 1: Acquire Calibration Points"), bold=True) step_1.setToolTip( _("Pick four points by clicking on canvas.\n" "Those four points should be in the four\n" @@ -881,7 +881,7 @@ class CalibrationUI: ) grid_lay.addWidget(step_1, 10, 0, 1, 3) - self.cal_source_lbl = FCLabel("%s:" % _("Source Type")) + self.cal_source_lbl = FCLabel('%s:' % _("Source Type"), bold=True) self.cal_source_lbl.setToolTip(_("The source of calibration points.\n" "It can be:\n" "- Object -> click a hole geo for Excellon or a pad for Gerber\n" @@ -918,7 +918,7 @@ class CalibrationUI: grid_lay.addWidget(self.object_label, 13, 0, 1, 3) grid_lay.addWidget(self.object_combo, 14, 0, 1, 3) - self.points_table_label = FCLabel('%s' % _('Calibration Points')) + self.points_table_label = FCLabel('%s' % _('Calibration Points'), bold=True) self.points_table_label.setToolTip( _("Contain the expected calibration points and the\n" "ones measured.") @@ -1090,7 +1090,7 @@ class CalibrationUI: grid_lay.addWidget(FCLabel(''), 19, 0) # STEP 2 # - step_2 = FCLabel('%s' % _("STEP 2: Verification GCode")) + step_2 = FCLabel('%s' % _("STEP 2: Verification GCode"), bold=True) step_2.setToolTip( _("Generate GCode file to locate and align the PCB by using\n" "the four points acquired above.\n" @@ -1129,7 +1129,7 @@ class CalibrationUI: grid_lay.addWidget(FCLabel(''), 23, 0, 1, 3) # STEP 3 # - step_3 = FCLabel('%s' % _("STEP 3: Adjustments")) + step_3 = FCLabel('%s' % _("STEP 3: Adjustments"), bold=True) step_3.setToolTip( _("Calculate Scale and Skew factors based on the differences (delta)\n" "found when checking the PCB pattern. The differences must be filled\n" @@ -1160,7 +1160,7 @@ class CalibrationUI: grid_lay.addWidget(FCLabel(''), 27, 0, 1, 3) # STEP 4 # - step_4 = FCLabel('%s' % _("STEP 4: Adjusted GCode")) + step_4 = FCLabel('%s' % _("STEP 4: Adjusted GCode"), bold=True) step_4.setToolTip( _("Generate verification GCode file adjusted with\n" "the factors above.") @@ -1241,7 +1241,7 @@ class CalibrationUI: """) grid_lay.addWidget(self.skew_button, 34, 0, 1, 3) - # final_factors_lbl = FCLabel('%s' % _("Final Factors")) + # final_factors_lbl = FCLabel('%s' % _("Final Factors"), bold=True) # final_factors_lbl.setToolTip( # _("Generate verification GCode file adjusted with\n" # "the factors above.") @@ -1323,7 +1323,7 @@ class CalibrationUI: grid_lay.addWidget(FCLabel(''), 44, 0, 1, 3) # STEP 5 # - step_5 = FCLabel('%s' % _("STEP 5: Calibrate FlatCAM Objects")) + step_5 = FCLabel('%s' % _("STEP 5: Calibrate FlatCAM Objects"), bold=True) step_5.setToolTip( _("Adjust the FlatCAM objects\n" "with the factors determined and verified above.") diff --git a/appPlugins/ToolCopperThieving.py b/appPlugins/ToolCopperThieving.py index debcf626..9d03c758 100644 --- a/appPlugins/ToolCopperThieving.py +++ b/appPlugins/ToolCopperThieving.py @@ -1449,7 +1449,7 @@ class ThievingUI: separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) dots_grid.addWidget(separator_line, 0, 0, 1, 2) - self.dots_label = FCLabel('%s:' % _("Dots Grid Parameters")) + self.dots_label = FCLabel('%s' % _("Dots Grid Parameters"), bold=True) dots_grid.addWidget(self.dots_label, 2, 0, 1, 2) # Dot diameter # @@ -1495,7 +1495,7 @@ class ThievingUI: separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) squares_grid.addWidget(separator_line, 0, 0, 1, 2) - self.squares_label = FCLabel('%s:' % _("Squares Grid Parameters")) + self.squares_label = FCLabel('%s' % _("Squares Grid Parameters"), bold=True) squares_grid.addWidget(self.squares_label, 2, 0, 1, 2) # Square Size # @@ -1541,7 +1541,7 @@ class ThievingUI: separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) lines_grid.addWidget(separator_line, 0, 0, 1, 2) - self.lines_label = FCLabel('%s:' % _("Lines Grid Parameters")) + self.lines_label = FCLabel('%s' % _("Lines Grid Parameters"), bold=True) lines_grid.addWidget(self.lines_label, 2, 0, 1, 2) # Line Size # diff --git a/appPlugins/ToolDblSided.py b/appPlugins/ToolDblSided.py index a4149570..8643adb6 100644 --- a/appPlugins/ToolDblSided.py +++ b/appPlugins/ToolDblSided.py @@ -884,7 +884,7 @@ class DsidedUI: grid_mirror.addWidget(separator_line, 3, 0, 1, 3) # ## Reference - self.axloc_label = FCLabel('%s:' % _("Reference")) + self.axloc_label = FCLabel('%s' % _("Reference"), bold=True) self.axloc_label.setToolTip( _("The coordinates used as reference for the mirror operation.\n" "Can be:\n" @@ -980,7 +980,7 @@ class DsidedUI: grid_snap_ref.setContentsMargins(0, 0, 0, 0) self.sr_frame.setLayout(grid_snap_ref) - self.exc_hole_lbl = FCLabel('%s:' % _("Excellon")) + self.exc_hole_lbl = FCLabel('%s' % _("Excellon"), bold=True) self.exc_hole_lbl.setToolTip( _("Object that holds holes that can be picked as reference for mirroring.") ) diff --git a/appPlugins/ToolDistance.py b/appPlugins/ToolDistance.py index 316ac0ea..a7f4ec28 100644 --- a/appPlugins/ToolDistance.py +++ b/appPlugins/ToolDistance.py @@ -764,15 +764,20 @@ class Distance(AppTool): if settings.contains("theme"): theme = settings.value('theme', type=str) else: - theme = 'white' + theme = 'default' + + if settings.contains("dark_canvas"): + dark_canvas = settings.value('dark_canvas', type=bool) + else: + dark_canvas = False if self.app.use_3d_engine: - if theme == 'white': + if (theme == 'default' or theme == 'light') and not dark_canvas: color = '#000000FF' else: color = '#FFFFFFFF' else: - if theme == 'white': + if (theme == 'default' or theme == 'light') and not dark_canvas: color = '#000000' else: color = '#FFFFFF' @@ -958,7 +963,7 @@ class DistanceUI: res_grid.addWidget(separator_line, 8, 0, 1, 3) # Distance - self.total_distance_label = FCLabel("%s:" % _('DISTANCE')) + self.total_distance_label = FCLabel('%s:' % _('DISTANCE'), bold=True) self.total_distance_label.setToolTip(_("This is the point to point Euclidian distance.")) self.total_distance_entry = FCEntry() diff --git a/appPlugins/ToolExtract.py b/appPlugins/ToolExtract.py index 9585784c..b8e6cbf4 100644 --- a/appPlugins/ToolExtract.py +++ b/appPlugins/ToolExtract.py @@ -1186,7 +1186,7 @@ class ExtractUI: self.ring_box.addLayout(ring_grid) # Annular Ring value - self.ring_label = FCLabel('%s' % _("Fixed Annular Ring")) + self.ring_label = FCLabel('%s' % _("Fixed Annular Ring"), bold=True) self.ring_label.setToolTip( _("The size of annular ring.\n" "The copper sliver between the hole exterior\n" @@ -1271,7 +1271,7 @@ class ExtractUI: self.fix_frame.setLayout(fixed_grid) # Fixed Diameter - self.fixed_label = FCLabel('%s' % _("Fixed Diameter")) + self.fixed_label = FCLabel('%s' % _("Fixed Diameter"), bold=True) fixed_grid.addWidget(self.fixed_label, 2, 0, 1, 2) # Diameter value @@ -1299,7 +1299,7 @@ class ExtractUI: self.prop_frame.setLayout(prop_grid) # Proportional Diameter - self.prop_label = FCLabel('%s' % _("Proportional Diameter")) + self.prop_label = FCLabel('%s' % _("Proportional Diameter"), bold=True) prop_grid.addWidget(self.prop_label, 0, 0, 1, 2) # Diameter value diff --git a/appPlugins/ToolFilm.py b/appPlugins/ToolFilm.py index fba56017..8efffa3f 100644 --- a/appPlugins/ToolFilm.py +++ b/appPlugins/ToolFilm.py @@ -1351,11 +1351,6 @@ class FilmUI: _("A value greater than 1 will compact the film\n" "while a value less than 1 will jolt it.") ) - self.film_scale_cb.setStyleSheet( - """ - QCheckBox {font-weight: bold; color: black} - """ - ) adj_grid.addWidget(self.film_scale_cb, 2, 0, 1, 2) # Scale Type @@ -1426,11 +1421,6 @@ class FilmUI: _("Positive values will skew to the right\n" "while negative values will skew to the left.") ) - self.film_skew_cb.setStyleSheet( - """ - QCheckBox {font-weight: bold; color: black} - """ - ) adj_grid.addWidget(self.film_skew_cb, 14, 0, 1, 2) # Skew Type @@ -1501,11 +1491,6 @@ class FilmUI: self.film_mirror_cb.setToolTip( _("Mirror the film geometry on the selected axis or on both.") ) - self.film_mirror_cb.setStyleSheet( - """ - QCheckBox {font-weight: bold; color: black} - """ - ) adj_grid.addWidget(self.film_mirror_cb, 26, 0, 1, 2) self.film_mirror_axis = RadioSet([{'label': _('X'), 'value': 'x'}, diff --git a/appPlugins/ToolImage.py b/appPlugins/ToolImage.py index 233b353b..18791c24 100644 --- a/appPlugins/ToolImage.py +++ b/appPlugins/ToolImage.py @@ -346,7 +346,7 @@ class ImageUI: # Type of image interpretation self.image_type = RadioSet([{'label': 'B/W', 'value': 'black'}, {'label': 'Color', 'value': 'color'}]) - self.image_type_label = FCLabel("%s:" % _('Image type')) + self.image_type_label = FCLabel('%s:' % _('Image type'), bold=True) self.image_type_label.setToolTip( _("Choose a method for the image interpretation.\n" "B/W means a black & white image. Color means a colored image.") diff --git a/appPlugins/ToolIsolation.py b/appPlugins/ToolIsolation.py index 8dbc70c5..acdc7e0e 100644 --- a/appPlugins/ToolIsolation.py +++ b/appPlugins/ToolIsolation.py @@ -3470,7 +3470,7 @@ class IsoUI: separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) new_tool_grid.addWidget(separator_line, 0, 0, 1, 3) - self.tool_sel_label = FCLabel('%s' % _('Add from DB')) + self.tool_sel_label = FCLabel('%s' % _('Add from DB'), bold=True) new_tool_grid.addWidget(self.tool_sel_label, 2, 0, 1, 3) # ### Tool Diameter #### diff --git a/appPlugins/ToolLevelling.py b/appPlugins/ToolLevelling.py index f99f5a3d..c8393041 100644 --- a/appPlugins/ToolLevelling.py +++ b/appPlugins/ToolLevelling.py @@ -1794,7 +1794,7 @@ class LevelUI: grid0 = GLay(v_spacing=5, h_spacing=3) self.al_box.addLayout(grid0) - self.al_title = FCLabel('%s' % _("Probe Points Table")) + self.al_title = FCLabel('%s' % _("Probe Points Table"), bold=True) self.al_title.setToolTip(_("Generate GCode that will obtain the height map")) self.show_al_table = FCCheckBox(_("Show")) @@ -1894,7 +1894,7 @@ class LevelUI: param_grid.addWidget(separator_line, 6, 0, 1, 2) # AUTOLEVELL MODE - al_mode_lbl = FCLabel('%s:' % _("Mode")) + al_mode_lbl = FCLabel('%s' % _("Mode"), bold=True) al_mode_lbl.setToolTip(_("Choose a mode for height map generation.\n" "- Manual: will pick a selection of probe points by clicking on canvas\n" "- Grid: will automatically generate a grid of probe points")) diff --git a/appPlugins/ToolMilling.py b/appPlugins/ToolMilling.py index 39ae0437..23f29e7a 100644 --- a/appPlugins/ToolMilling.py +++ b/appPlugins/ToolMilling.py @@ -4184,7 +4184,7 @@ class MillingUI: separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) new_tool_grid.addWidget(separator_line, 0, 0, 1, 2) - self.tool_sel_label = FCLabel('%s' % _("Add from DB")) + self.tool_sel_label = FCLabel('%s' % _("Add from DB"), bold=True) new_tool_grid.addWidget(self.tool_sel_label, 2, 0, 1, 2) self.addtool_entry_lbl = FCLabel('%s:' % _('Tool Dia')) diff --git a/appPlugins/ToolNCC.py b/appPlugins/ToolNCC.py index 982bba2d..66f44520 100644 --- a/appPlugins/ToolNCC.py +++ b/appPlugins/ToolNCC.py @@ -4193,7 +4193,7 @@ class NccUI: # ############################################################# # ############### Tool selection ############################## # ############################################################# - self.tool_sel_label = FCLabel('%s' % _('Add from DB')) + self.tool_sel_label = FCLabel('%s' % _('Add from DB'), bold=True) new_tool_grid.addWidget(self.tool_sel_label, 2, 0, 1, 3) # ### Tool Diameter #### diff --git a/appPlugins/ToolObjectDistance.py b/appPlugins/ToolObjectDistance.py index 3679a534..88d17eb4 100644 --- a/appPlugins/ToolObjectDistance.py +++ b/appPlugins/ToolObjectDistance.py @@ -570,7 +570,7 @@ class ObjectDistanceUI: res_grid.addWidget(separator_line, 6, 0, 1, 3) # Total Distance - self.total_distance_label = FCLabel("%s:" % _('DISTANCE')) + self.total_distance_label = FCLabel('%s:' % _('DISTANCE'), bold=True) self.total_distance_label.setToolTip(_("This is the point to point Euclidian distance.")) self.total_distance_entry = FCEntry() @@ -584,7 +584,7 @@ class ObjectDistanceUI: res_grid.addWidget(FCLabel("%s" % self.units), 8, 2) # Half Point - self.half_point_label = FCLabel("%s:" % _('Half Point')) + self.half_point_label = FCLabel('%s:' % _('Half Point'), bold=True) self.half_point_label.setToolTip(_("This is the middle point of the point to point Euclidean distance.")) self.half_point_entry = FCEntry() diff --git a/appPlugins/ToolOptimal.py b/appPlugins/ToolOptimal.py index 830dea85..60d29a5d 100644 --- a/appPlugins/ToolOptimal.py +++ b/appPlugins/ToolOptimal.py @@ -494,7 +494,7 @@ class OptimalUI: self.gerber_object_combo.is_last = True self.gerber_object_combo.obj_type = "Gerber" - self.gerber_object_label = FCLabel("%s:" % _("GERBER")) + self.gerber_object_label = FCLabel('%s:' % _("GERBER"), bold=True) self.gerber_object_label.setToolTip( "Gerber object for which to find the minimum distance between copper features." ) diff --git a/appPlugins/ToolPaint.py b/appPlugins/ToolPaint.py index 620e9d2a..03690b15 100644 --- a/appPlugins/ToolPaint.py +++ b/appPlugins/ToolPaint.py @@ -3041,7 +3041,7 @@ class PaintUI: "L = laser")) # Tool Order - self.order_label = FCLabel('%s:' % _('Tool order')) + self.order_label = FCLabel('%s:' % _('Tool order'), bold=True) self.order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n" "'Default' --> 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" @@ -3071,7 +3071,7 @@ class PaintUI: separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) new_tool_grid.addWidget(separator_line, 0, 0, 1, 2) - self.tool_sel_label = FCLabel('%s' % _('Add from DB')) + self.tool_sel_label = FCLabel('%s' % _('Add from DB'), bold=True) new_tool_grid.addWidget(self.tool_sel_label, 2, 0, 1, 2) # ### Tool Diameter #### diff --git a/appPlugins/ToolPanelize.py b/appPlugins/ToolPanelize.py index 6bb55144..158de065 100644 --- a/appPlugins/ToolPanelize.py +++ b/appPlugins/ToolPanelize.py @@ -1344,7 +1344,7 @@ class PanelizeUI: # Type of resulting Panel object self.panel_type_radio = RadioSet([{'label': _('Gerber'), 'value': 'gerber'}, {'label': _('Geo'), 'value': 'geometry'}]) - self.panel_type_label = FCLabel("%s:" % _("Panel Type")) + self.panel_type_label = FCLabel('%s:' % _("Panel Type"), bold=True) self.panel_type_label.setToolTip( _("Choose the type of object for the panel object:\n" "- Gerber\n" diff --git a/appPlugins/ToolPcbWizard.py b/appPlugins/ToolPcbWizard.py index d23cd9f1..0c2cab65 100644 --- a/appPlugins/ToolPcbWizard.py +++ b/appPlugins/ToolPcbWizard.py @@ -484,7 +484,7 @@ class WizardUI: # Units type self.units_radio = RadioSet([{'label': _('Inch'), 'value': 'INCH'}, {'label': _('mm'), 'value': 'METRIC'}]) - self.units_label = FCLabel("%s:" % _('Units')) + self.units_label = FCLabel('%s:' % _('Units'), bold=True) self.units_label.setToolTip( _("The type of units that the coordinates and tool\n" "diameters are using. Can be INCH or MM.") diff --git a/appPlugins/ToolPunchGerber.py b/appPlugins/ToolPunchGerber.py index 325bb021..1d04a24f 100644 --- a/appPlugins/ToolPunchGerber.py +++ b/appPlugins/ToolPunchGerber.py @@ -2173,7 +2173,7 @@ class PunchUI: separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) grid1.addWidget(separator_line, 2, 0, 1, 2) - self.exc_label = FCLabel('%s' % _("Excellon")) + self.exc_label = FCLabel('%s' % _("Excellon"), bold=True) self.exc_label.setToolTip( _("Remove the geometry of Excellon from the Gerber to create the holes in pads.") ) @@ -2188,7 +2188,7 @@ class PunchUI: grid1.addWidget(self.exc_combo, 6, 0, 1, 2) # Fixed Dia - self.fixed_label = FCLabel('%s' % _("Fixed Diameter")) + self.fixed_label = FCLabel('%s' % _("Fixed Diameter"), bold=True) grid1.addWidget(self.fixed_label, 8, 0, 1, 2) # Diameter value @@ -2216,7 +2216,7 @@ class PunchUI: self.ring_frame.setLayout(self.ring_box) # Annular Ring value - self.ring_label = FCLabel('%s' % _("Fixed Annular Ring")) + self.ring_label = FCLabel('%s' % _("Fixed Annular Ring"), bold=True) self.ring_label.setToolTip( _("The size of annular ring.\n" "The copper sliver between the hole exterior\n" @@ -2295,7 +2295,7 @@ class PunchUI: # ############################################################################################################# # Proportional value - self.prop_label = FCLabel('%s' % _("Proportional Diameter")) + self.prop_label = FCLabel('%s' % _("Proportional Diameter"), bold=True) grid1.addWidget(self.prop_label, 14, 0, 1, 2) # Diameter value diff --git a/appPlugins/ToolReport.py b/appPlugins/ToolReport.py index c8193269..1e93783e 100644 --- a/appPlugins/ToolReport.py +++ b/appPlugins/ToolReport.py @@ -158,7 +158,7 @@ class ObjectReport(AppTool): font = QtGui.QFont() font.setBold(True) - p_color = QtGui.QColor("#000000") if self.app.options['global_gray_icons'] is False \ + p_color = QtGui.QColor("#000000") if self.app.options['global_theme'] == 'light' \ else QtGui.QColor("#FFFFFF") # main Items categories diff --git a/appPlugins/ToolSolderPaste.py b/appPlugins/ToolSolderPaste.py index d95de190..1a7efd3e 100644 --- a/appPlugins/ToolSolderPaste.py +++ b/appPlugins/ToolSolderPaste.py @@ -154,6 +154,7 @@ class SolderPaste(AppTool): "tools_solderpaste_z_dispense": self.ui.z_dispense_entry, "tools_solderpaste_z_stop": self.ui.z_stop_entry, "tools_solderpaste_z_travel": self.ui.z_travel_entry, + "tools_solderpaste_margin": self.ui.margin_entry, "tools_solderpaste_z_toolchange": self.ui.z_toolchange_entry, "tools_solderpaste_xy_toolchange": self.ui.xy_toolchange_entry, "tools_solderpaste_frxy": self.ui.frxy_entry, @@ -217,6 +218,12 @@ class SolderPaste(AppTool): if obj and obj.kind == 'gerber': obj_name = obj.obj_options['name'] self.ui.obj_combo.set_value(obj_name) + else: + # select first Gerber object found + for o in self.app.collection.get_list(): + if o.kind == 'gerber': + obj_name = o.obj_options['name'] + self.ui.obj_combo.set_value(obj_name) def build_ui(self): """ @@ -451,6 +458,8 @@ class SolderPaste(AppTool): current_row = self.ui.tools_table.currentRow() uid = tooluid if tooluid else int(self.ui.tools_table.item(current_row, 2).text()) + if uid < 0: + return for key in self.form_fields: self.tooltable_tools[uid]['data'].update({ key: self.form_fields[key].get_value() @@ -718,6 +727,10 @@ class SolderPaste(AppTool): :param use_thread: use thread, True or False :return: a Geometry type object """ + + # this is a percentage of the tool diameter + tool_margin = self.ui.margin_entry.get_value() + proc = self.app.proc_container.new('%s...' % _("Working")) obj = work_object @@ -795,7 +808,7 @@ class SolderPaste(AppTool): tooluid = 1 for tool in sorted_tools: - offset = tool / 2 + offset = ((tool_margin * tool) * 0.01) + (tool / 2) for uid, vl in self.tooltable_tools.items(): if float('%.*f' % (self.decimals, float(vl['tooldia']))) == tool: tooluid = int(uid) @@ -817,7 +830,7 @@ class SolderPaste(AppTool): # so we do a hack: get first the exterior in a form of LinearRings and then convert back to Polygon # because intersection does not work on LinearRings for g in work_geo: - # for whatever reason intersection on LinearRings does not work so we convert back to Polygons + # for whatever reason intersection on LinearRings does not work, so we convert back to Polygons poly = Polygon(g) x_min, y_min, x_max, y_max = poly.bounds @@ -1002,13 +1015,13 @@ class SolderPaste(AppTool): app_obj.log.debug("GeometryObject.mtool_gen_cncjob() --> generate_from_geometry2() failed") return 'fail' else: - tool_cnc_dict['gcode'] = StringIO(res) + tool_cnc_dict['gcode'] = res total_gcode += res # ## PARSE GCODE # ## tool_cnc_dict['gcode_parsed'] = new_obj.gcode_parse(tool_data=tool_cnc_dict['data']) - # TODO this serve for bounding box creation only; should be optimized + # TODO this serve for bounding box creation only; should be optimized. Using recursive bounds()? tool_cnc_dict['solid_geometry'] = unary_union([geo['geom'] for geo in tool_cnc_dict['gcode_parsed']]) # tell gcode_parse from which point to start drawing the lines depending on what kind of @@ -1019,6 +1032,9 @@ class SolderPaste(AppTool): }) tool_cnc_dict.clear() + used_tools = list(obj.tools.keys()) + new_obj.used_tools = used_tools + new_obj.source_file = StringIO(total_gcode) if use_thread: @@ -1268,7 +1284,7 @@ class SolderUI: _("Tool Diameter. Its value\n" "is the width of the solder paste dispensed.")) - self.addtool_entry_lbl = FCLabel('%s:' % _('New Tool')) + self.addtool_entry_lbl = FCLabel('%s:' % _('New Tool'), bold=True) self.addtool_entry_lbl.setToolTip( _("Diameter for the new tool to add in the Tool Table") ) @@ -1330,6 +1346,21 @@ class SolderUI: param_grid.addWidget(self.z_travel_label, 0, 0) param_grid.addWidget(self.z_travel_entry, 0, 1) + # MARGIN + self.margin_label = FCLabel('%s:' % _("Margin")) + self.margin_label.setToolTip('%s %s' % ( + _("Offset from the boundary."), + _("Fraction of tool diameter.") + ) + ) + self.margin_entry = FCDoubleSpinner(suffix='%') + self.margin_entry.set_range(-100.0000, 100.0000) + self.margin_entry.set_precision(self.decimals) + self.margin_entry.setSingleStep(0.1) + + param_grid.addWidget(self.margin_label, 2, 0) + param_grid.addWidget(self.margin_entry, 2, 1) + # ############################################################################################################# # Dispense Frame # ############################################################################################################# diff --git a/appPlugins/ToolSub.py b/appPlugins/ToolSub.py index a6b42da3..f16de017 100644 --- a/appPlugins/ToolSub.py +++ b/appPlugins/ToolSub.py @@ -901,7 +901,7 @@ class SubUI: geo_grid = GLay(v_spacing=5, h_spacing=3) geo_frame.setLayout(geo_grid) - self.geo_title = FCLabel("%s" % _("GEOMETRY")) + self.geo_title = FCLabel('%s' % _("GEOMETRY"), bold=True) self.tools_box.addWidget(self.geo_title) # Target Geometry Object diff --git a/appTranslation.py b/appTranslation.py index 470ccaba..ed0964e9 100644 --- a/appTranslation.py +++ b/appTranslation.py @@ -93,12 +93,12 @@ def on_language_apply_click(app, restart=False): if theme_settings.contains("theme"): theme = theme_settings.value('theme', type=str) else: - theme = 'white' + theme = 'light' - if theme == 'white': + if theme == 'light': resource_loc = 'assets/resources' else: - resource_loc = 'assets/resources' + resource_loc = 'assets/resources/dark_resources' # do nothing if trying to apply the language that is the current language (already applied). settings = QSettings("Open Source", "FlatCAM") @@ -190,12 +190,12 @@ def restart_program(app, ask=None): if theme_settings.contains("theme"): theme = theme_settings.value('theme', type=str) else: - theme = 'white' + theme = 'light' - if theme == 'white': + if theme == 'light': resource_loc = 'assets/resources' else: - resource_loc = 'assets/resources' + resource_loc = 'assets/resources/dark_resources' # try to quit the Socket opened by ArgsThread class try: diff --git a/camlib.py b/camlib.py index 7ed420c6..c0307edd 100644 --- a/camlib.py +++ b/camlib.py @@ -7193,7 +7193,7 @@ class CNCjob(Geometry): return try: - if self.app.options['global_theme'] == 'white': + if self.app.options['global_theme'] == 'light': obj.annotation.set(text=text, pos=pos, visible=obj.obj_options['plot'], font_size=self.app.options["cncjob_annotation_fontsize"], color=self.app.options["cncjob_annotation_fontcolor"]) diff --git a/defaults.py b/defaults.py index 1bf2634e..8dc78ab6 100644 --- a/defaults.py +++ b/defaults.py @@ -112,8 +112,9 @@ class AppDefaults: "global_tpdf_rmargin": 20.0, # General GUI Preferences - "global_theme": 'white', - "global_gray_icons": False, + "global_appearance": 'default', + "global_dark_canvas": False, + "global_theme": 'default', "global_layout": "compact", "global_hover_shape": False, @@ -129,8 +130,8 @@ class AppDefaults: # Project Items colors "global_proj_item_color_light": '#000000FF', "global_proj_item_dis_color_light": '#b7b7cbFF', - "global_proj_item_color_dark": '#4385C8FF', - "global_proj_item_dis_color_dark": '#61616CFF', + "global_proj_item_color_dark": '#F2F2F2FF', + "global_proj_item_dis_color_dark": '#a6a6a6ff', "global_project_autohide": True, @@ -649,6 +650,7 @@ class AppDefaults: # SolderPaste Tool "tools_solderpaste_tools": "1.0, 0.3", "tools_solderpaste_new": 0.3, + "tools_solderpaste_margin": 0.0, "tools_solderpaste_z_start": 0.05, "tools_solderpaste_z_dispense": 0.1, "tools_solderpaste_z_stop": 0.05, diff --git a/requirements.txt b/requirements.txt index 37809140..be98b3e6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -43,4 +43,7 @@ vispy>=0.9.0 pyqtdarktheme gdal -rasterio \ No newline at end of file +rasterio + +# To detect OS dark mode +darkdetect