From 5f909452d25a8ceb735ff198a4b19734dbfbacbb Mon Sep 17 00:00:00 2001 From: sliptonic Date: Wed, 17 Sep 2025 17:20:24 -0500 Subject: [PATCH 1/3] [CAM] change dialog to allow directly switching unit schema --- src/Mod/CAM/Path/Main/Gui/JobDlg.py | 159 +++++++++++++++++++++------- 1 file changed, 120 insertions(+), 39 deletions(-) diff --git a/src/Mod/CAM/Path/Main/Gui/JobDlg.py b/src/Mod/CAM/Path/Main/Gui/JobDlg.py index a53c96a107..73ad1a3732 100644 --- a/src/Mod/CAM/Path/Main/Gui/JobDlg.py +++ b/src/Mod/CAM/Path/Main/Gui/JobDlg.py @@ -81,47 +81,128 @@ class JobCreate: if FreeCAD.ActiveDocument.UnitSystem in minute_based_schemes: return - # NB: On macOS the header is ignored as per its UI guidelines. - header = translate("CAM_Job", "Warning: Incompatible Unit Schema") - info = translate( + # Create custom dialog with unit schema selection + dialog = QtGui.QDialog() + dialog.setWindowTitle(translate("CAM_Job", "Warning: Incompatible Unit Schema")) + dialog.setModal(True) + dialog.resize(500, 400) + + layout = QtGui.QVBoxLayout(dialog) + + # Warning message + warning_label = QtGui.QLabel() + warning_label.setText(translate( "CAM_Job", - ( - "This document uses an improper unit schema " - "which can result in dangerous situations and machine crashes!" - ), - ) - details = translate( + "This document uses an improper unit schema which can result in " + "dangerous situations and machine crashes!" + )) + warning_label.setWordWrap(True) + warning_label.setStyleSheet("color: red; font-weight: bold; margin-bottom: 10px;") + layout.addWidget(warning_label) + + # Current schema info + current_info = QtGui.QLabel() + current_info.setText(translate( "CAM_Job", - ( - "

This document's unit schema, '{}', " - "expresses velocity in values per second." - "\n" - "

Please change the unit schema in the document properties " - "to one that expresses feed rates per minute instead. " - "\n" - "For example: \n" - "

\n" - "\n" - "

Keeping the current unit schema can result in dangerous G-code errors. " - "For details please refer to the " - "Units section " - "of the CAM Workbench's wiki page." - ), - ).format(FreeCAD.ActiveDocument.UnitSystem, *minute_based_schemes[:2]) - msgbox = QtGui.QMessageBox(QtGui.QMessageBox.Warning, header, info) - msgbox.setInformativeText(details) - msgbox.addButton(translate("CAM_Job", "Ok"), QtGui.QMessageBox.AcceptRole) - dont_show_again_button = msgbox.addButton( - translate("CAM_Job", "Don't show this warning again"), - QtGui.QMessageBox.ActionRole, - ) - - msgbox.exec_() - if msgbox.clickedButton() == dont_show_again_button: - Path.Preferences.preferences().SetBool(Path.Preferences.WarningSuppressVelocity, True) + "Current unit schema '{}' expresses velocity in values per second." + ).format(FreeCAD.ActiveDocument.UnitSystem)) + current_info.setWordWrap(True) + layout.addWidget(current_info) + + # Recommendation + recommendation = QtGui.QLabel() + recommendation.setText(translate( + "CAM_Job", + "Please select a unit schema that expresses feed rates per minute instead:" + )) + recommendation.setWordWrap(True) + layout.addWidget(recommendation) + + # Unit schema selection + schema_group = QtGui.QGroupBox(translate("CAM_Job", "Recommended Unit Schemas")) + schema_layout = QtGui.QVBoxLayout(schema_group) + + self.schema_buttons = [] + for i, schema in enumerate(minute_based_schemes): + radio = QtGui.QRadioButton(schema) + if i == 0: # Select first (most preferred) by default + radio.setChecked(True) + self.schema_buttons.append(radio) + schema_layout.addWidget(radio) + + layout.addWidget(schema_group) + + # Additional info + info_label = QtGui.QLabel() + info_label.setText(translate( + "CAM_Job", + "Keeping the current unit schema can result in dangerous G-code errors. " + "For details please refer to the " + "Units section " + "of the CAM Workbench's wiki page." + )) + info_label.setWordWrap(True) + info_label.setOpenExternalLinks(True) + layout.addWidget(info_label) + + # Buttons + button_layout = QtGui.QHBoxLayout() + + change_button = QtGui.QPushButton(translate("CAM_Job", "Change Unit Schema")) + change_button.setDefault(True) + change_button.clicked.connect(lambda: self._applyUnitSchema(dialog)) + + keep_button = QtGui.QPushButton(translate("CAM_Job", "Keep Current Schema")) + keep_button.clicked.connect(dialog.reject) + + dont_show_button = QtGui.QPushButton(translate("CAM_Job", "Don't Show Again")) + dont_show_button.clicked.connect(lambda: self._suppressWarning(dialog)) + + button_layout.addWidget(change_button) + button_layout.addWidget(keep_button) + button_layout.addWidget(dont_show_button) + + layout.addLayout(button_layout) + + dialog.exec_() + + def _applyUnitSchema(self, dialog): + """Apply the selected unit schema to the document.""" + selected_schema = None + for button in self.schema_buttons: + if button.isChecked(): + selected_schema = button.text() + break + + if selected_schema: + try: + FreeCAD.ActiveDocument.UnitSystem = selected_schema + FreeCAD.ActiveDocument.recompute() + + # Show success message + QtGui.QMessageBox.information( + dialog, + translate("CAM_Job", "Unit Schema Changed"), + translate("CAM_Job", "Unit schema successfully changed to '{}'.").format(selected_schema) + ) + dialog.accept() + except Exception as e: + QtGui.QMessageBox.critical( + dialog, + translate("CAM_Job", "Error"), + translate("CAM_Job", "Failed to change unit schema: {}").format(str(e)) + ) + else: + QtGui.QMessageBox.warning( + dialog, + translate("CAM_Job", "No Selection"), + translate("CAM_Job", "Please select a unit schema.") + ) + + def _suppressWarning(self, dialog): + """Suppress future warnings and close dialog.""" + Path.Preferences.preferences().SetBool(Path.Preferences.WarningSuppressVelocity, True) + dialog.reject() def setupTitle(self, title): self.dialog.setWindowTitle(title) From b7153e12e01c5188488ccd682f07a6f05d102b07 Mon Sep 17 00:00:00 2001 From: sliptonic Date: Fri, 19 Sep 2025 14:14:08 -0500 Subject: [PATCH 2/3] [CAM] Robustly test schemas to find the ones that work. Robustly test current schema. Fixes #22549 --- src/Mod/CAM/Path/Main/Gui/JobDlg.py | 58 ++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/src/Mod/CAM/Path/Main/Gui/JobDlg.py b/src/Mod/CAM/Path/Main/Gui/JobDlg.py index 73ad1a3732..a2d3b6b398 100644 --- a/src/Mod/CAM/Path/Main/Gui/JobDlg.py +++ b/src/Mod/CAM/Path/Main/Gui/JobDlg.py @@ -71,16 +71,53 @@ class JobCreate: self.index = None self.model = None + def _getMinuteBasedSchemas(self): + """Dynamically discover which unit schemas support velocity in minutes.""" + internal_names = FreeCAD.Units.listSchemas() + minute_based_schemes = [] + + # Create a test velocity quantity + q = FreeCAD.Units.Quantity(1, FreeCAD.Units.Velocity) + + for i, key in enumerate(internal_names): + try: + label = FreeCAD.Units.listSchemas(i) + r = FreeCAD.Units.schemaTranslate(q, i) + if '/min' in r[2]: + minute_based_schemes.append({'id': i, 'label': label}) + except (IndexError, TypeError): + # Skip invalid schema indices + continue + + return minute_based_schemes + + def _currentSchemaUsesMinutes(self): + """Test if the current unit schema uses minutes for velocity.""" + try: + # Create a test velocity quantity + q = FreeCAD.Units.Quantity(1, FreeCAD.Units.Velocity) + + # Get current schema's representation of velocity + current_representation = q.getUserPreferred()[2] + + # Check if the current representation contains '/min' + return '/min' in current_representation + except Exception: + # If we can't determine, assume it doesn't use minutes + return False + def _warnUserIfNotUsingMinutes(self): # Warn user if current schema doesn't use minute for time in velocity if Path.Preferences.suppressVelocity(): return - # schemas in order of preference -- the first ones get proposed to the user - minute_based_schemes = list(map(FreeCAD.Units.listSchemas, [6, 3, 2])) - if FreeCAD.ActiveDocument.UnitSystem in minute_based_schemes: + # Test if current schema uses minutes for velocity + if self._currentSchemaUsesMinutes(): return + # Get all minute-based schemas for the dialog + minute_based_schemes = self._getMinuteBasedSchemas() + # Create custom dialog with unit schema selection dialog = QtGui.QDialog() dialog.setWindowTitle(translate("CAM_Job", "Warning: Incompatible Unit Schema")) @@ -124,7 +161,8 @@ class JobCreate: self.schema_buttons = [] for i, schema in enumerate(minute_based_schemes): - radio = QtGui.QRadioButton(schema) + radio = QtGui.QRadioButton(schema['label']) + radio.setProperty('schema_id', schema['id']) # Store the schema ID if i == 0: # Select first (most preferred) by default radio.setChecked(True) self.schema_buttons.append(radio) @@ -168,22 +206,24 @@ class JobCreate: def _applyUnitSchema(self, dialog): """Apply the selected unit schema to the document.""" - selected_schema = None + selected_schema_id = None + selected_schema_label = None for button in self.schema_buttons: if button.isChecked(): - selected_schema = button.text() + selected_schema_id = button.property('schema_id') + selected_schema_label = button.text() break - if selected_schema: + if selected_schema_id is not None: try: - FreeCAD.ActiveDocument.UnitSystem = selected_schema + FreeCAD.ActiveDocument.UnitSystem = selected_schema_id FreeCAD.ActiveDocument.recompute() # Show success message QtGui.QMessageBox.information( dialog, translate("CAM_Job", "Unit Schema Changed"), - translate("CAM_Job", "Unit schema successfully changed to '{}'.").format(selected_schema) + translate("CAM_Job", "Unit schema successfully changed to '{}'.").format(selected_schema_label) ) dialog.accept() except Exception as e: From dfb3ae2ef4100c5209c7ac10c68902fc5cad3bd1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 19 Sep 2025 19:16:55 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/Mod/CAM/Path/Main/Gui/JobDlg.py | 118 +++++++++++++++------------- 1 file changed, 64 insertions(+), 54 deletions(-) diff --git a/src/Mod/CAM/Path/Main/Gui/JobDlg.py b/src/Mod/CAM/Path/Main/Gui/JobDlg.py index a2d3b6b398..f22226a66c 100644 --- a/src/Mod/CAM/Path/Main/Gui/JobDlg.py +++ b/src/Mod/CAM/Path/Main/Gui/JobDlg.py @@ -75,20 +75,20 @@ class JobCreate: """Dynamically discover which unit schemas support velocity in minutes.""" internal_names = FreeCAD.Units.listSchemas() minute_based_schemes = [] - + # Create a test velocity quantity q = FreeCAD.Units.Quantity(1, FreeCAD.Units.Velocity) - + for i, key in enumerate(internal_names): try: label = FreeCAD.Units.listSchemas(i) r = FreeCAD.Units.schemaTranslate(q, i) - if '/min' in r[2]: - minute_based_schemes.append({'id': i, 'label': label}) + if "/min" in r[2]: + minute_based_schemes.append({"id": i, "label": label}) except (IndexError, TypeError): # Skip invalid schema indices continue - + return minute_based_schemes def _currentSchemaUsesMinutes(self): @@ -96,12 +96,12 @@ class JobCreate: try: # Create a test velocity quantity q = FreeCAD.Units.Quantity(1, FreeCAD.Units.Velocity) - + # Get current schema's representation of velocity - current_representation = q.getUserPreferred()[2] - + current_representation = q.getUserPreferred()[2] + # Check if the current representation contains '/min' - return '/min' in current_representation + return "/min" in current_representation except Exception: # If we can't determine, assume it doesn't use minutes return False @@ -123,122 +123,132 @@ class JobCreate: dialog.setWindowTitle(translate("CAM_Job", "Warning: Incompatible Unit Schema")) dialog.setModal(True) dialog.resize(500, 400) - + layout = QtGui.QVBoxLayout(dialog) - + # Warning message warning_label = QtGui.QLabel() - warning_label.setText(translate( - "CAM_Job", - "This document uses an improper unit schema which can result in " - "dangerous situations and machine crashes!" - )) + warning_label.setText( + translate( + "CAM_Job", + "This document uses an improper unit schema which can result in " + "dangerous situations and machine crashes!", + ) + ) warning_label.setWordWrap(True) warning_label.setStyleSheet("color: red; font-weight: bold; margin-bottom: 10px;") layout.addWidget(warning_label) - + # Current schema info current_info = QtGui.QLabel() - current_info.setText(translate( - "CAM_Job", - "Current unit schema '{}' expresses velocity in values per second." - ).format(FreeCAD.ActiveDocument.UnitSystem)) + current_info.setText( + translate( + "CAM_Job", + "Current unit schema '{}' expresses velocity in values per second.", + ).format(FreeCAD.ActiveDocument.UnitSystem) + ) current_info.setWordWrap(True) layout.addWidget(current_info) - + # Recommendation recommendation = QtGui.QLabel() - recommendation.setText(translate( - "CAM_Job", - "Please select a unit schema that expresses feed rates per minute instead:" - )) + recommendation.setText( + translate( + "CAM_Job", + "Please select a unit schema that expresses feed rates per minute instead:", + ) + ) recommendation.setWordWrap(True) layout.addWidget(recommendation) - + # Unit schema selection schema_group = QtGui.QGroupBox(translate("CAM_Job", "Recommended Unit Schemas")) schema_layout = QtGui.QVBoxLayout(schema_group) - + self.schema_buttons = [] for i, schema in enumerate(minute_based_schemes): - radio = QtGui.QRadioButton(schema['label']) - radio.setProperty('schema_id', schema['id']) # Store the schema ID + radio = QtGui.QRadioButton(schema["label"]) + radio.setProperty("schema_id", schema["id"]) # Store the schema ID if i == 0: # Select first (most preferred) by default radio.setChecked(True) self.schema_buttons.append(radio) schema_layout.addWidget(radio) - + layout.addWidget(schema_group) - + # Additional info info_label = QtGui.QLabel() - info_label.setText(translate( - "CAM_Job", - "Keeping the current unit schema can result in dangerous G-code errors. " - "For details please refer to the " - "Units section " - "of the CAM Workbench's wiki page." - )) + info_label.setText( + translate( + "CAM_Job", + "Keeping the current unit schema can result in dangerous G-code errors. " + "For details please refer to the " + "Units section " + "of the CAM Workbench's wiki page.", + ) + ) info_label.setWordWrap(True) info_label.setOpenExternalLinks(True) layout.addWidget(info_label) - + # Buttons button_layout = QtGui.QHBoxLayout() - + change_button = QtGui.QPushButton(translate("CAM_Job", "Change Unit Schema")) change_button.setDefault(True) change_button.clicked.connect(lambda: self._applyUnitSchema(dialog)) - + keep_button = QtGui.QPushButton(translate("CAM_Job", "Keep Current Schema")) keep_button.clicked.connect(dialog.reject) - + dont_show_button = QtGui.QPushButton(translate("CAM_Job", "Don't Show Again")) dont_show_button.clicked.connect(lambda: self._suppressWarning(dialog)) - + button_layout.addWidget(change_button) button_layout.addWidget(keep_button) button_layout.addWidget(dont_show_button) - + layout.addLayout(button_layout) - + dialog.exec_() - + def _applyUnitSchema(self, dialog): """Apply the selected unit schema to the document.""" selected_schema_id = None selected_schema_label = None for button in self.schema_buttons: if button.isChecked(): - selected_schema_id = button.property('schema_id') + selected_schema_id = button.property("schema_id") selected_schema_label = button.text() break - + if selected_schema_id is not None: try: FreeCAD.ActiveDocument.UnitSystem = selected_schema_id FreeCAD.ActiveDocument.recompute() - + # Show success message QtGui.QMessageBox.information( dialog, translate("CAM_Job", "Unit Schema Changed"), - translate("CAM_Job", "Unit schema successfully changed to '{}'.").format(selected_schema_label) + translate("CAM_Job", "Unit schema successfully changed to '{}'.").format( + selected_schema_label + ), ) dialog.accept() except Exception as e: QtGui.QMessageBox.critical( dialog, translate("CAM_Job", "Error"), - translate("CAM_Job", "Failed to change unit schema: {}").format(str(e)) + translate("CAM_Job", "Failed to change unit schema: {}").format(str(e)), ) else: QtGui.QMessageBox.warning( dialog, translate("CAM_Job", "No Selection"), - translate("CAM_Job", "Please select a unit schema.") + translate("CAM_Job", "Please select a unit schema."), ) - + def _suppressWarning(self, dialog): """Suppress future warnings and close dialog.""" Path.Preferences.preferences().SetBool(Path.Preferences.WarningSuppressVelocity, True)